Fixing ActiveRecord::Base.all and making it friendlier to named_scope

Send to friend

Search feature of this blog is powered by following named_scope.

named_scope :search, lambda { |search_term|
   { :conditions => ["title LIKE :search_term or body LIKE :search_term", { :search_term => "%#{search_term}%"} ]  }
}

I want to search for all sinatra related articles.

>> Article.search('sinatra')
SELECT * FROM `articles` WHERE (title LIKE '%sinatra%' or body LIKE '%sinatra%') 

I want to search for all articles in published status.

>> Article.all(:conditions => {:status => 'published'})
SELECT * FROM `articles` WHERE (`articles`.`status` = 'published') 

Now I want to combine the above two requirements: search for ‘sinatra’ related articles which are in ‘published’ status. Should be easy, Right.

>> Article.all(:conditions => {:status => 'published'}).search('sinatra')
NoMethodError: undefined method `search' for #

Now try the same search as above in reverse order: put named_scope ‘search’ first and then apply ‘all’.

>> Article.search('sinatra').all(:conditions => {:status => 'published'})
SELECT * FROM `articles` WHERE (`articles`.`status` = 'published') AND (title LIKE '%sinatra%' or body LIKE '%sinatra%') 

This one worked. Why is that?

That’s because ActiveRecord::Base.all method is defined like this

def all(*args)
  find(:all, *args)
end

So the main problem is that all is not a named_scope. And since it is not a named_scope Article.all(:conditions => {:status => ‘published’}).search(‘sinatra’) failed.

Fix is to create a named_scope called all.

module ActiveRecord
   class Base
      named_scope :all, lambda { |args| args ||= {} }
   end
end

Now the order does not matter and you can use all any way you please.