Scopes
Scope Definitions
NoBrainer allows named scopes to be defined on Models. Scopes are defined by declaring a class method on a model. Such method must return a criterion. Example:
class Model
def self.active
where(:active => true)
end
end
You can also use the scope
method which defines a class method with
whatever you pass as a block. Example:
class Model
scope(:active) { where(:active => true }
end
When a class method is found in a chain of criteria, the method is executed and the returned criterion is merged with the chain. Example with the previously declared scope:
Model.where(:email => /@gmail/).active.count
# Equivalent to
Model.where(:email => /@gmail/).where(:active => true).count
Scopes can accept arguments. Example:
class Model
scope(:in_category) { |category| where(:category => category) }
def self.created_before(time)
where(:created_at.lt => time)
end
end
Model.in_category('games').created_before(1.week.ago).count
Default Scopes
Default scopes are scopes that are merged in criteria when constructing a query.
Adding unscoped
in the criteria will disable the use of the default scope.
class Model
default_scope { where(:active => true) }
end
Model.count # returns only active models
Model.unscoped.count # returns all models
Note that many default scopes can be declared. All of them are applied, in the order of declaration. This can come in handy when using polymorphic models with different default scopes declared in parents and subclasses. Example:
class Parent
default_scope { order_by(:created_at) }
default_scope { where(:active => true) }
end
class Child < Parent
default_scope { where(:created_by => :admin) }
end
Default scopes are applied at the beginning of the chain when building a query.
The unscoped
keyword has no effect on has_many
associations have a custom :scope
defined.
Unlike the ActiveRecord behavior, where()
filters in default scopes are not overridable.
For example, consider a Model with a default scope of where(:active => true)
.
With ActiveRecord, Model.where(:active => false)
will yield all inactive
models, while NoBrainer will return nothing. This is because NoBrainer
evaluates
Model.where(:active => true).where(:active => false)
, which evaluates to
Model.where(:and => [:active => true, :active => false])
.
Default Scopes are sometimes ignored
NoBrainer will not use the default scopes in the following cases:
- When using a
dependent
option on an association, such as
has_many :stuff, :dependent => :destroy
, the Stuff model is used unscoped. For example, upon destroy, NoBrainer will perform aStuff.unscoped.destroy_all
. - When using
validates_uniqueness_of :some_field
, NoBrainer will not use the default scope to find any duplicates ofsome_field
. - When using
belongs_to
associations.