diff options
author | José Valim <jose.valim@gmail.com> | 2011-12-15 20:26:33 +0100 |
---|---|---|
committer | José Valim <jose.valim@gmail.com> | 2011-12-15 20:47:26 +0100 |
commit | 1e8b75181306ada87b1a4b05d1551348dd916f91 (patch) | |
tree | 6a2594022a82a3fabef5970ff112729d949ca04b /activerecord/lib | |
parent | 22bd21dc0fc0535f1de93d22569e8769c2e43949 (diff) | |
download | rails-1e8b75181306ada87b1a4b05d1551348dd916f91.tar.gz rails-1e8b75181306ada87b1a4b05d1551348dd916f91.tar.bz2 rails-1e8b75181306ada87b1a4b05d1551348dd916f91.zip |
Make with_scope public so we stop using send :bomb:
Diffstat (limited to 'activerecord/lib')
-rw-r--r-- | activerecord/lib/active_record/base.rb | 181 | ||||
-rw-r--r-- | activerecord/lib/active_record/relation.rb | 2 |
2 files changed, 92 insertions, 91 deletions
diff --git a/activerecord/lib/active_record/base.rb b/activerecord/lib/active_record/base.rb index 2d2909054d..db157f4422 100644 --- a/activerecord/lib/active_record/base.rb +++ b/activerecord/lib/active_record/base.rb @@ -1032,6 +1032,97 @@ module ActiveRecord #:nodoc: instance end + # with_scope lets you apply options to inner block incrementally. It takes a hash and the keys must be + # <tt>:find</tt> or <tt>:create</tt>. <tt>:find</tt> parameter is <tt>Relation</tt> while + # <tt>:create</tt> 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 + # <tt>where</tt>, <tt>includes</tt>, and <tt>joins</tt> operations in <tt>Relation</tt>, which are merged. + # + # <tt>joins</tt> 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 <tt>with_exclusive_scope</tt> 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+. + def with_scope(scope = {}, action = :merge, &block) + # If another Active Record class has been passed in, get its current scope + scope = scope.current_scope if !scope.is_a?(Relation) && scope.respond_to?(:current_scope) + + previous_scope = self.current_scope + + if scope.is_a?(Hash) + # Dup first and second level of hash (method and params). + scope = scope.dup + scope.each do |method, params| + scope[method] = params.dup unless params == true + end + + scope.assert_valid_keys([ :find, :create ]) + relation = construct_finder_arel(scope[:find] || {}) + relation.default_scoped = true unless action == :overwrite + + if previous_scope && previous_scope.create_with_value && scope[:create] + scope_for_create = if action == :merge + previous_scope.create_with_value.merge(scope[:create]) + else + scope[:create] + end + + relation = relation.create_with(scope_for_create) + else + scope_for_create = scope[:create] + scope_for_create ||= previous_scope.create_with_value if previous_scope + relation = relation.create_with(scope_for_create) if scope_for_create + end + + scope = relation + end + + scope = previous_scope.merge(scope) if previous_scope && action == :merge + + self.current_scope = scope + begin + yield + ensure + self.current_scope = previous_scope + end + end + private def relation #:nodoc: @@ -1159,96 +1250,6 @@ module ActiveRecord #:nodoc: end protected - # with_scope lets you apply options to inner block incrementally. It takes a hash and the keys must be - # <tt>:find</tt> or <tt>:create</tt>. <tt>:find</tt> parameter is <tt>Relation</tt> while - # <tt>:create</tt> 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 - # <tt>where</tt>, <tt>includes</tt>, and <tt>joins</tt> operations in <tt>Relation</tt>, which are merged. - # - # <tt>joins</tt> 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 <tt>with_exclusive_scope</tt> 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+. - def with_scope(scope = {}, action = :merge, &block) - # If another Active Record class has been passed in, get its current scope - scope = scope.current_scope if !scope.is_a?(Relation) && scope.respond_to?(:current_scope) - - previous_scope = self.current_scope - - if scope.is_a?(Hash) - # Dup first and second level of hash (method and params). - scope = scope.dup - scope.each do |method, params| - scope[method] = params.dup unless params == true - end - - scope.assert_valid_keys([ :find, :create ]) - relation = construct_finder_arel(scope[:find] || {}) - relation.default_scoped = true unless action == :overwrite - - if previous_scope && previous_scope.create_with_value && scope[:create] - scope_for_create = if action == :merge - previous_scope.create_with_value.merge(scope[:create]) - else - scope[:create] - end - - relation = relation.create_with(scope_for_create) - else - scope_for_create = scope[:create] - scope_for_create ||= previous_scope.create_with_value if previous_scope - relation = relation.create_with(scope_for_create) if scope_for_create - end - - scope = relation - end - - scope = previous_scope.merge(scope) if previous_scope && action == :merge - - self.current_scope = scope - begin - yield - ensure - self.current_scope = previous_scope - end - end # Works like with_scope, but discards any nested properties. def with_exclusive_scope(method_scoping = {}, &block) diff --git a/activerecord/lib/active_record/relation.rb b/activerecord/lib/active_record/relation.rb index a1aa764991..8aaab10b1e 100644 --- a/activerecord/lib/active_record/relation.rb +++ b/activerecord/lib/active_record/relation.rb @@ -251,7 +251,7 @@ module ActiveRecord # Please check unscoped if you want to remove all previous scopes (including # the default_scope) during the execution of a block. def scoping - @klass.send(:with_scope, self, :overwrite) { yield } + @klass.with_scope(self, :overwrite) { yield } end # Updates all records with details given if they match a set of conditions supplied, limits and order can |