Reputation: 21791
In active_record/base.rb, module ActiveRecord
you can see this code:
delegate :find, :first, :last, :all, :destroy, :destroy_all, :exists?, :delete,
:delete_all, :update, :update_all, :to => :scoped
Let's take first
method, so i assumed that first
method delegates to scoped
method and then scoped
should return the first record from database. But scoped
is just the anonymous scope
, how the current construction is doing its job?
At the same time, how dynamic methods work, like find_by_name
, find_all_by_name_and_colour
?
Thanks
Upvotes: 2
Views: 3809
Reputation: 47588
According to the documentation, delegate
:
Provides a delegate class method to easily expose contained objects’ methods as your own. Pass one or more methods (specified as symbols or strings) and the name of the target object via the :to option (also a symbol or string)
So this delegates the methods in the list to the scoped
method, which is defined in ActiveRecord::NamedScoped::ClassMethods, and which returns an anonymous scope.
As to why ActiveRecord does this, it is so we can continue to use familiar methods such as find
while behind the scenes AR is actually calling the fancy new Arel methods. For instance, when you do
Post.find(37)
What actually gets executed is:
Post.where(primary_key.eq(37))
Upvotes: 3
Reputation: 5173
First: "delegate" delegates to an object, not a method - so "scope" must be some object.
Without inspecting the source to verify and just going on my working knowledge of ActiveRecord, I'm going to assume that "scope" will be the current AR instance, unless it's being called on an association proxy.
Therefore:
User.first # "scope" will be User
User.posts.first # "scope" will be the Post collection proxy
@christianblais is correct on question #2, method_missing
is handling these calls. Furthermore, Rails actually defines the missing method on first call, so that subsequent calls to it do not incur the overhead of method_missing
Upvotes: 1
Reputation: 2458
I'll answer to your second question. find_by_name and find_all_by_what_you_want rely on ruby's precious method_missing. Whenever a method doesn't exist, the object calls method_missing, which you can overwrite. For example, I may want to overwrite method_missing, catch all non-existing method calls, check with some regex if they start/end/contain some keywords, etc.
In your example, I'll overwrite method_missing, check if it starts by find, and if yes, split on the 'and' keyword to get an array of they attributes with which I want to do my find.
Here is a pretty good example : http://technicalpickles.com/posts/using-method_missing-and-respond_to-to-create-dynamic-methods/
Upvotes: 1