named_scope dependencies via returning anonymous scopes from class methods

Posted by Tim Connor Mon, 28 Jul 2008 19:42:00 GMT

Update: This doesn’t actually quite work, as it can override some scopes when composed. I haven’t quite figured out yet, if the technique is salvageable in some situations, or if it is always unsafe to use for chaining scopes.

I have not been able to find any info on ways to make rails named_scopes call each other, or otherwise handle dependencies. This can come up, for instance, if you have complex scopes that require joins. You might have some scopes that share the same needed set of joins to be valid, and you would like to spin that join out to a separate scope, so they could share it. Or it might be called from a has_many :through, where that join will already be valid, but alternatively called directly, where the qualified column name suddenly won’t make sense (‘other_table.column_name’ will be incorrrect in the unjoined query).

The best solution I have found is to use a class method that returns an anonymous scope. This is a contrived example that could be better handled through the has_many side of the relationship, but with sufficiently complex multi-level has_many :throughs you can easily come up with a situation where you want dependent named_scopes.


class Book
  named_scope :author_join, :joins => 'INNER JOIN authors ON books.author_id = authors.id, order => 'authors.last_name, authors.first_name'
  named_scope :published, :conditions => {:published => true}

  def by_author_country(country_code)
    Book.author_join.scoped(:conditions => ['authors.country_code = ?', country_code])
  end
end

#yes they are still composable
Book.by_author_country('EU').published

Just beware that currently scopes don’t merge joins, so you can only have one joins per set of composed scopes.