Getting started

Squeel.configure do |config|
# To load hash extensions (to allow for AND (&), OR (|), and NOT (-) against
# hashes of conditions)
config.load_core_extensions :hash
# To load symbol extensions (for a subset of the old MetaWhere functionality,
# via ARel predicate methods on Symbols: :name.matches, etc)
# config.load_core_extensions :symbol
# To load both hash and symbol extensions
# config.load_core_extensions :hash, :symbol
end

The Squeel Query DSL

Squeel enhances the normal ActiveRecord query methods by enabling them to accept
blocks. Inside a block, the Squeel query DSL can be used. Note the use of curly braces
in these examples instead of parentheses. {} denotes a Squeel DSL query.

Stubs and keypaths are the two primary building blocks used in a Squeel DSL query, so we'll
start by taking a look at them. Most of the other examples that follow will be based on
this "symbol-less" block syntax.

An important gotcha, before we begin: The Squeel DSL works its magic using instance_eval.
If you've been working with Ruby for a while, you'll know immediately that this means that
inside a Squeel DSL block, self isn't the same thing that it is outside the block.

This carries with it an important implication: Instance variables and instance methods
inside the block won't refer to your object's variables/methods.

Don't worry, Squeel's got you covered. Use one of the following methods to get access
to your object's methods and variables:

Assign the variable locally before the DSL block, and access it as you would
normally.

Supply an arity to the DSL block, as in Person.where{|q| q.name == @my_name}
Downside: You'll need to prefix stubs, keypaths, and functions (explained below)
with the DSL object.

Stubs

Stubs are, for most intents and purposes, just like Symbols in a normal call to
Relation#where (note the need for doubling up on the curly braces here, the first ones
start the block, the second are the hash braces):

You normally wouldn't bother using the DSL in this case, as a simple hash would
suffice. However, stubs serve as a building block for keypaths, and keypaths are
very handy.

KeyPaths

A Squeel keypath is essentially a more concise and readable alternative to a
deeply nested hash. For instance, in standard ActiveRecord, you might join several
associations like this to perform a query:

There are two obvious but important differences between these two code samples, and
both of them have to do with context.

To read code with SQL interpolation, the structure of the SQL query must
first be considered, then we must cross-reference the values to be substituted
with their placeholders. This carries with it a small but perceptible (and
annoying!) context shift during which we stop thinking about the comparison being
performed, and instead play "count the arguments", or, in the case of
named/hash interpolations, "find the word". The Squeel syntax places
both sides of each comparison in proximity to one another, allowing us to
focus on what our code is doing.

In the first example, we're starting off with Ruby, switching context to SQL,
and then back to Ruby, and while we spend time in SQL-land, we're stuck with
SQL syntax, whether or not it's the best way to express what we're trying to do.
With Squeel, we're writing Ruby from start to finish. And with Ruby syntax comes
flexibility to express the query in the way we see fit.

Predicate aliases

That last bit is important. We can mix and match predicate methods with operators
and take advantage of Ruby's operator precedence or parenthetical grouping to make
our intentions more clear, on the first read-through. And if we don't like the
way that the existing predications read, we can create our own aliases in a Squeel
configure block:

And while we're on the topic of helping you make your code more expressive...

Compound conditions

Let's say you want to check if a Person has a name like one of several possibilities.

names = ['Ernie%', 'Joe%', 'Mary%']
Person.where('name LIKE ? OR name LIKE ? OR name LIKE ?', *names)

But you're smart, and you know that you might want to check more or less than
3 names, so you make your query flexible:

Person.where((['name LIKE ?'] * names.size).join(' OR '), *names)

Yeah... that's readable, all right. How about:

Person.where{name.like_any names}
# => SELECT "people".* FROM "people"
WHERE (("people"."name" LIKE 'Ernie%' OR "people"."name" LIKE 'Joe%' OR "people"."name" LIKE 'Mary%'))

I'm not sure about you, but I much prefer the latter. In short, you can add _any or
_all to any predicate method, and it would do what you expect, when given an array of
possibilities to compare against.

Sifters

Sifters are like little snippets of conditions that take parameters. Let's say that you
have a model called Article, and you often want to query for articles that contain a
string in the title or body. So you write a scope:

Joins

Squeel adds a couple of enhancements to joins. First, keypaths can be used as shorthand for
nested association joins. Second, you can specify join types (inner and outer), and a class
in the case of a polymorphic belongs_to relationship.

You can refer to these associations when constructing other parts of your query, and
they'll be automatically mapped to the proper table or table alias This is most noticeable
when using self-referential associations:

SQL Operators

You can use the standard mathematical operators (+, -, *, /) inside the Squeel DSL to
specify operators in the resulting SQL, or the op method to specify another
custom operator, such as the standard SQL concatenation operator, ||: