Let me visualize that for you.
class Product < ActiveRecord::Base
end
Product.first.title
#=> "My sample product"
Nothing extraordinary here. Just a simple method call. Now take a look at the following example.
class Product < ActiveRecord::Base
def method_missing
end
end
Product.first.title
#=> nil
Product.first
Product.first.title
#=> "My sample product"
How is this possible? In some way they determine the end of the method chain and act upon that? At least thats my theory.
Can anyone explain this behavior?
You’re seeing an artifact of using
irbto investigate things.When you say this:
Your
method_missingwill be called to lazy-load thetitlemethod and you getnil.When you say this:
You’re effectively doing this:
The first Product instance will be loaded and then
irbwill callinspecton it and AR will add the accessor methods along the way. The result is that Product will now have atitlemethod. Hence, doing this:won’t call your
method_missingat all as there will be a realtitlemethod forProduct.first.titleto call.If you try again like this:
You’ll see two
nils.As far as chaining goes, ActiveRecord doesn’t really detect the end, it is just that some method calls naturally require real data from the database and some don’t.
If you call
where,order, or any of the other querying methods, you get an ActiveRecord::Relation instance back and you can chain more query methods and scopes on that relation object. For example,where(which ActiveRecord::Relation gets by including ActiveRecord::QueryMethods) looks like this:so it just makes a copy of the current query, adds a few things to the copy, and gives you the copy back.
If you call
first,last,to_a,all, any of the Enumerable methods (i.e. you calleach), … then you’re asking about specific instances and ActiveRecord will have to execute the query to realize the model instance(s) in question. For example,ActiveRecord::Relation#to_alooks like this:and
allis little more than a wrapper aroundto_a.ActiveRecord doesn’t really know where the end of the chain is, it just doesn’t load anything from the database until it has to so you tell it where the chain ends by saying go forth and retrieve me some data.