-
Notifications
You must be signed in to change notification settings - Fork 212
Common issues
This is a collection of issues arising from misconceptions about Squeel that frequently crop up on the issue tracker.
Like any other scopes, scopes that use the Squeel DSL must be wrapped in lambdas if you want them to be lazy evaluated. For instance:
# DO NOT USE!
scope :recent, where{created_at > 1.week.ago}
Won't work -- for long. The value of 1.week.ago
will be interpreted when the model is loaded, and never again. Instead, use a lambda:
scope :recent, lambda { where{created_at > 1.week.ago} }
or a class method:
def self.recent
where{created_at > 1.week.ago}
end
The Squeel DSL operates inside an instance_eval. This means that you won't have access to instance methods or instance variables from the calling class. You will, however, have access to local variables that exist inside the closure from which you call it.
So, if you intend to use, for instance, params[:id]
inside a Squeel block, you can do it in a few ways. First, you can assign the variable locally:
params_id = params[:id]
@article = Article.where{id == params_id}.first
Alternately, you can use the my
keyword, which grants you access to the instance from which the DSL was called:
@article = Article.where{id == my{params[:id]}}.first
Or, if you really like typing, you can give your block an arity, like so:
@article = Article.where{ |q|
q.id == params[:id]
}.first
Note that if given an arity, access to the DSL object (and thus creation of stubs, keypaths, etc) is via the block parameter.
This one was subject of some debate, but observing a few simple, common-sense guidelines gives you a way to refer to columns in the value-side of conditions that wasn't previously available.
For instance, here's a query that finds all children with the same name as their parent -- note the tilde (~
), which anchors the right-hand side of the comparison to the base model, instead of using the left-hand side's nesting:
Person.joins{children}.where{children.name == ~name}.to_sql
=> SELECT "people".* FROM "people"
INNER JOIN "people" "children_people" ON "children_people"."parent_id" = "people"."id"
WHERE "children_people"."name" = "people"."name""