megas
megas

Reputation: 21791

Understanding delegate and scoped methods in Rails

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

Answers (3)

zetetic
zetetic

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

jemminger
jemminger

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

christianblais
christianblais

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

Related Questions