Use a scope by default on a Rails has_many relationship
activerecord, has-many, ruby-on-rails-3
Solution
In Rails 4, `Associations` have an optional `scope` parameter that accepts a lambda that is applied to the `Relation` (cf. the doc for ActiveRecord::Associations::ClassMethods)
class SolarSystem < ActiveRecord::Base
has_many :planets, -> { life_supporting }
end
class Planet < ActiveRecord::Base
scope :life_supporting, -> { where('distance_from_sun > ?', 5).order('diameter ASC') }
end
In Rails 3, the `where_values` workaround can sometimes be improved by using `where_values_hash` that handles better scopes where conditions are defined by multiple `where` or by a hash (not the case here).
has_many :planets, conditions: Planet.life_supporting.where_values_hash
Problem
Let's say I have the following classes ``` class SolarSystem < ActiveRecord::Base has_many :planets end class Planet < ActiveRecord::Base scope :life_supporting, where('distance_from_sun > ?', 5).order('diameter ASC') end ``` `Planet` has a scope `life_supporting` and `SolarSystem` `has_many :planets`. I would like to define my has_many relationship so that when I ask a `solar_system` for all associated `planets`, the `life_supporting` scope is automatically applied. Essentially, I would like `solar_system.planets == solar_system.planets.life_supporting`. Requirements I do not want to change `scope :life_supporting` in `Planet` to `default_scope where('distance_from_sun > ?', 5).order('diameter ASC')` I'd also like to prevent duplication by not having to add to `SolarSystem` `has_many :planets, :conditions => ['distance_from_sun > ?', 5], :order => 'diameter ASC'` Goal I'd like to have something like `has_many :planets, :with_scope => :life_supporting` Edit: Work Arounds As @phoet said, it may not be possible to achieve a default scope using ActiveRecord. However, I have found two potential work arounds. Both prevent duplication. The first one, while long, maintains obvious readability and transparency, and the second one is a helper type method who's output is explicit. ``` class SolarSystem < ActiveRecord::Base has_many :planets, :conditions => Planet.life_supporting.where_values, :order => Planet.life_supporting.order_values end class Planet < ActiveRecord::Base scope :life_supporting, where('distance_from_sun > ?', 5).order('diameter ASC') end ``` Another solution which is a lot cleaner is to simply add the following method to `SolarSystem` ``` def life_supporting_planets planets.life_supporting end ``` and to use `solar_system.life_supporting_planets` wherever you'd use `solar_system.planets`. Neither answers the question so I just put them here as work arounds should anyone else encounter this situation.