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.
Scopes are custom queries that you define inside your Rails models with the scope method. Every scope takes two arguments: A name, which you use to call this scope in your code. A lambda, which implements the query.
Scopes are used to assign complex ActiveRecord queries into customized methods using Ruby on Rails. Inside your models, you can define a scope as a new method that returns a lambda function for calling queries you're probably used to using inside your controllers.
ActiveRecord::Base indicates that the ActiveRecord class or module has a static inner class called Base that you're extending. Edit: as Mike points out, in this case ActiveRecord is a module... ActiveRecord is defined as a module in Rails, github.com/rails/rails/tree/master/activerecord/lib/…
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
In Rails 5, the following code works fine...
class Order scope :paid, -> { where status: %w[paid refunded] } end class Store has_many :paid_orders, -> { paid }, class_name: 'Order' end
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With