Recently I wrote a post about Rails scopes and lambdas in an attempt to clarify why it is so common to see the use of lambdas with Rails scopes. I now realise that I could have gone a bit further in my explanation.
A question that is sometimes asked is:
Why are lambdas used with Rails scopes? Why not procs or blocks?
To answer that question it is useful to first look at the implementation
of the Rails scope
method in
ActiveRecord::Scoping::Named::ClassMethods
.
The implementation is as follows:
def scope(name, body, &block)
unless body.respond_to?(:call)
raise ArgumentError, 'The scope body needs to be callable.'
end
if dangerous_class_method?(name)
raise ArgumentError, "You tried to define a scope named \"#{name}\" " \
"on the model \"#{self.name}\", but Active Record already defined " \
"a class method with the same name."
end
extension = Module.new(&block) if block
singleton_class.send(:define_method, name) do \|\*args\|
scope = all.scoping { body.call(\*args) }
scope = scope.extending(extension) if extension
scope || all
end
end
Now let’s consider this implementation in conjunction with the following scope:
class Article < ActiveRecord::Base
scope :created_before, ->(time) { where("created_at < ?", time) }
end
In this example, :created_before
is interpreted as
name
and ->(time) { where("created_at < ?", time)
}
as body
.
Notice that body
must be callable. So that rules out a
block, which is simply a syntactic structure. However, it does still
allow body
to be a lambda or a proc.
Whilst it is technically possible for a proc to be used in conjunction
with a Rails scope, a lambda is more useful because of the constraint
that it must, unlike a proc, have a specific arity. For example, in
the example above, Article.created_before
must be called
with one argument.
Hopefully, that explains why lambdas are used with Rails scopes.
Of course, you’ll notice that the implementation of the
scope
method uses metaprogramming via
:define_method
to create a class method that could have
been programmed directly.
Previous post: Rails scopes and lambdas
More recently: Reflecting on Ruby Conf AU 2016
© 2024 Keith Pitty, all rights reserved.