Consider a simple example, where we have 2 models, Article and Category.
class Article < ActiveRecord::Base
belongs_to :category
def self.search(title)
where(:title => title)
end
end
class Category < ActiveRecord::Base
has_many :articles
end
Now, on the rails console :
Article.all.search('test article')
As expected returns an error
NoMethodError: undefined method `search' for #<Array:0x9aa207c>
But when I do this
Category.first.articles.search('test article')
returns a set of records!
This prompts a check on the classes of
Article.all
and
Category.first.articles
both returning the Array class.
Obviously, Article's class method 'search' is being inducted in run time and prompting a 'correct' return of records when accessed through its association (Category) but behaving otherwise when accessed by the class itself (Article).
So, What's happening?
This is because when you perform .all
the query is actually executed, so the object returned will be a basic array. However, when you perform Category.first
, it will return a Category object, then articles
is actually making use of ActiveRecord::Reflection::AssociationReflection
and doing an extension of an Array. For example, in rails c
try:
Category.first.articles.ancestors
vs
Category.first.articles.all.ancestors #throws an error
The second one throws an error, because at this point the object is just a simple Array. The first, however, is made up of something like this:
Article(...), ActiveRecord::Base, ActiveRecord::Reflection, Object (and so on)
As another example, try this:
a = Category.first.articles; ObjectSpace.each_object(Class).select {|k| a < k }
#=> [Object, BasicObject, ActiveRecord::Base]
You can see that although it looks like an Array, it's inheriting those other classes.