with_scope lets you apply options to inner block incrementally. It takes a hash and the keys must be :find or :create. :find parameter is Relation while :create parameters are an attributes hash.
class Article < ActiveRecord::Base def self.create_with_scope with_scope(:find => where(:blog_id => 1), :create => { :blog_id => 1 }) do find(1) # => SELECT * from articles WHERE blog_id = 1 AND id = 1 a = create(1) a.blog_id # => 1 end end end
In nested scopings, all previous parameters are overwritten by the innermost rule, with the exception of where, includes, and joins operations in Relation, which are merged.
joins operations are uniqued so multiple scopes can join in the same table without table aliasing problems. If you need to join multiple tables, but still want one of the tables to be uniqued, use the array of strings format for your joins.
class Article < ActiveRecord::Base def self.find_with_scope with_scope(:find => where(:blog_id => 1).limit(1), :create => { :blog_id => 1 }) do with_scope(:find => limit(10)) do all # => SELECT * from articles WHERE blog_id = 1 LIMIT 10 end with_scope(:find => where(:author_id => 3)) do all # => SELECT * from articles WHERE blog_id = 1 AND author_id = 3 LIMIT 1 end end end end
You can ignore any previous scopings by using the with_exclusive_scope method.
class Article < ActiveRecord::Base def self.find_with_exclusive_scope with_scope(:find => where(:blog_id => 1).limit(1)) do with_exclusive_scope(:find => limit(10)) do all # => SELECT * from articles LIMIT 10 end end end end
Note: the :find scope also has effect on update and deletion methods, like update_all and delete_all.
# File lib/active_record/scoping.rb, line 60 60: def with_scope(scope = {}, action = :merge, &block) 61: # If another Active Record class has been passed in, get its current scope 62: scope = scope.current_scope if !scope.is_a?(Relation) && scope.respond_to?(:current_scope) 63: 64: previous_scope = self.current_scope 65: 66: if scope.is_a?(Hash) 67: # Dup first and second level of hash (method and params). 68: scope = scope.dup 69: scope.each do |method, params| 70: scope[method] = params.dup unless params == true 71: end 72: 73: scope.assert_valid_keys([ :find, :create ]) 74: relation = construct_finder_arel(scope[:find] || {}) 75: relation.default_scoped = true unless action == :overwrite 76: 77: if previous_scope && previous_scope.create_with_value && scope[:create] 78: scope_for_create = if action == :merge 79: previous_scope.create_with_value.merge(scope[:create]) 80: else 81: scope[:create] 82: end 83: 84: relation = relation.create_with(scope_for_create) 85: else 86: scope_for_create = scope[:create] 87: scope_for_create ||= previous_scope.create_with_value if previous_scope 88: relation = relation.create_with(scope_for_create) if scope_for_create 89: end 90: 91: scope = relation 92: end 93: 94: scope = previous_scope.merge(scope) if previous_scope && action == :merge 95: 96: self.current_scope = scope 97: begin 98: yield 99: ensure 100: self.current_scope = previous_scope 101: end 102: end
Works like with_scope, but discards any nested properties.
# File lib/active_record/scoping.rb, line 107 107: def with_exclusive_scope(method_scoping = {}, &block) 108: if method_scoping.values.any? { |e| e.is_a?(ActiveRecord::Relation) } 109: raise ArgumentError, New finder API can not be used with_exclusive_scope. You can either call unscoped to get an anonymous scope not bound to the default_scope: User.unscoped.where(:active => true) Or call unscoped with a block: User.unscoped do User.where(:active => true).all end 110: end 111: with_scope(method_scoping, :overwrite, &block) 112: end
# File lib/active_record/scoping.rb, line 135 135: def construct_finder_arel(options = {}, scope = nil) 136: relation = options.is_a?(Hash) ? unscoped.apply_finder_options(options) : options 137: relation = scope.merge(relation) if scope 138: relation 139: end
Disabled; run with --debug to generate this.
Generated with the Darkfish Rdoc Generator 1.1.6.