Make with_scope public so we stop using send 💣
This commit is contained in:
parent
22bd21dc0f
commit
1e8b751813
|
@ -29,6 +29,14 @@ class Exhibit < ActiveRecord::Base
|
|||
def look; attributes end
|
||||
def feel; look; user.name end
|
||||
|
||||
def self.with_name
|
||||
where("name IS NOT NULL")
|
||||
end
|
||||
|
||||
def self.with_notes
|
||||
where("notes IS NOT NULL")
|
||||
end
|
||||
|
||||
def self.look(exhibits) exhibits.each { |e| e.look } end
|
||||
def self.feel(exhibits) exhibits.each { |e| e.feel } end
|
||||
end
|
||||
|
@ -109,6 +117,10 @@ Benchmark.bm(46) do |x|
|
|||
TIMES.times { Exhibit.first.look }
|
||||
end
|
||||
|
||||
x.report 'Model.named_scope' do
|
||||
TIMES.times { Exhibit.limit(10).with_name.with_notes }
|
||||
end
|
||||
|
||||
x.report("Model.all limit(100) (x#{(TIMES / 10).ceil})") do
|
||||
(TIMES / 10).ceil.times { Exhibit.look Exhibit.limit(100) }
|
||||
end
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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
|
||||
|
|
Loading…
Reference in New Issue