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
.
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'
I'd like to have something like
has_many :planets, :with_scope => :life_supporting
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.
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