mirror of
https://github.com/rails/rails.git
synced 2022-11-09 12:12:34 -05:00
with_scope is protected. Closes #8524.
git-svn-id: http://svn-commit.rubyonrails.org/rails/trunk@6909 5ecf4fe2-1ee6-0310-87b1-e25e094e27de
This commit is contained in:
parent
962b12fb9f
commit
1edd21bb02
6 changed files with 111 additions and 92 deletions
|
@ -1,5 +1,7 @@
|
|||
*SVN*
|
||||
|
||||
* with_scope is protected. #8524 [Josh Peek]
|
||||
|
||||
* Quickref for association methods. #7723 [marclove, Mindsweeper]
|
||||
|
||||
* Calculations: return nil average instead of 0 when there are no rows to average. #8298 [davidw]
|
||||
|
|
|
@ -85,14 +85,14 @@ module ActiveRecord
|
|||
end
|
||||
|
||||
def create(attrs = {})
|
||||
record = @reflection.klass.with_scope(:create => construct_scope[:create]) { @reflection.klass.create(attrs) }
|
||||
record = @reflection.klass.send(:with_scope, :create => construct_scope[:create]) { @reflection.klass.create(attrs) }
|
||||
@target ||= [] unless loaded?
|
||||
@target << record
|
||||
record
|
||||
end
|
||||
|
||||
def create!(attrs = {})
|
||||
record = @reflection.klass.with_scope(:create => construct_scope[:create]) { @reflection.klass.create!(attrs) }
|
||||
record = @reflection.klass.send(:with_scope, :create => construct_scope[:create]) { @reflection.klass.create!(attrs) }
|
||||
@target ||= [] unless loaded?
|
||||
@target << record
|
||||
record
|
||||
|
@ -161,7 +161,7 @@ module ActiveRecord
|
|||
if @target.respond_to?(method) || (!@reflection.klass.respond_to?(method) && Class.respond_to?(method))
|
||||
super
|
||||
else
|
||||
@reflection.klass.with_scope(construct_scope) { @reflection.klass.send(method, *args, &block) }
|
||||
@reflection.klass.send(:with_scope, construct_scope) { @reflection.klass.send(method, *args, &block) }
|
||||
end
|
||||
end
|
||||
|
||||
|
|
|
@ -57,7 +57,7 @@ module ActiveRecord
|
|||
raise_on_type_mismatch(associate)
|
||||
raise ActiveRecord::HasManyThroughCantAssociateNewRecords.new(@owner, through) unless associate.respond_to?(:new_record?) && !associate.new_record?
|
||||
|
||||
@owner.send(@reflection.through_reflection.name).proxy_target << klass.with_scope(:create => construct_join_attributes(associate)) { klass.create! }
|
||||
@owner.send(@reflection.through_reflection.name).proxy_target << klass.send(:with_scope, :create => construct_join_attributes(associate)) { klass.create! }
|
||||
@target << associate if loaded?
|
||||
end
|
||||
end
|
||||
|
@ -91,7 +91,7 @@ module ActiveRecord
|
|||
|
||||
def create!(attrs = nil)
|
||||
@reflection.klass.transaction do
|
||||
self << @reflection.klass.with_scope(:create => attrs) { @reflection.klass.create! }
|
||||
self << @reflection.klass.send(:with_scope, :create => attrs) { @reflection.klass.create! }
|
||||
end
|
||||
end
|
||||
|
||||
|
@ -105,7 +105,7 @@ module ActiveRecord
|
|||
if @target.respond_to?(method) || (!@reflection.klass.respond_to?(method) && Class.respond_to?(method))
|
||||
super
|
||||
else
|
||||
@reflection.klass.with_scope(construct_scope) { @reflection.klass.send(method, *args, &block) }
|
||||
@reflection.klass.send(:with_scope, construct_scope) { @reflection.klass.send(method, *args, &block) }
|
||||
end
|
||||
end
|
||||
|
||||
|
|
|
@ -80,7 +80,7 @@ module ActiveRecord
|
|||
# instance. Otherwise, if the target has not previously been loaded
|
||||
# elsewhere, the instance we create will get orphaned.
|
||||
load_target if replace_existing
|
||||
record = @reflection.klass.with_scope(:create => construct_scope[:create]) { yield @reflection.klass }
|
||||
record = @reflection.klass.send(:with_scope, :create => construct_scope[:create]) { yield @reflection.klass }
|
||||
|
||||
if replace_existing
|
||||
replace(record, true)
|
||||
|
|
|
@ -947,91 +947,6 @@ module ActiveRecord #:nodoc:
|
|||
logger.level = old_logger_level if logger
|
||||
end
|
||||
|
||||
# Scope parameters to method calls within the block. Takes a hash of method_name => parameters hash.
|
||||
# method_name may be :find or :create. :find parameters may include the <tt>:conditions</tt>, <tt>:joins</tt>,
|
||||
# <tt>:include</tt>, <tt>:offset</tt>, <tt>:limit</tt>, and <tt>:readonly</tt> options. :create parameters are an attributes hash.
|
||||
#
|
||||
# Article.with_scope(:find => { :conditions => "blog_id = 1" }, :create => { :blog_id => 1 }) do
|
||||
# Article.find(1) # => SELECT * from articles WHERE blog_id = 1 AND id = 1
|
||||
# a = Article.create(1)
|
||||
# a.blog_id # => 1
|
||||
# end
|
||||
#
|
||||
# In nested scopings, all previous parameters are overwritten by inner rule
|
||||
# except :conditions in :find, that are merged as hash.
|
||||
#
|
||||
# Article.with_scope(:find => { :conditions => "blog_id = 1", :limit => 1 }, :create => { :blog_id => 1 }) do
|
||||
# Article.with_scope(:find => { :limit => 10})
|
||||
# Article.find(:all) # => SELECT * from articles WHERE blog_id = 1 LIMIT 10
|
||||
# end
|
||||
# Article.with_scope(:find => { :conditions => "author_id = 3" })
|
||||
# Article.find(:all) # => SELECT * from articles WHERE blog_id = 1 AND author_id = 3 LIMIT 1
|
||||
# end
|
||||
# end
|
||||
#
|
||||
# You can ignore any previous scopings by using <tt>with_exclusive_scope</tt> method.
|
||||
#
|
||||
# Article.with_scope(:find => { :conditions => "blog_id = 1", :limit => 1 }) do
|
||||
# Article.with_exclusive_scope(:find => { :limit => 10 })
|
||||
# Article.find(:all) # => SELECT * from articles LIMIT 10
|
||||
# end
|
||||
# end
|
||||
def with_scope(method_scoping = {}, action = :merge, &block)
|
||||
method_scoping = method_scoping.method_scoping if method_scoping.respond_to?(:method_scoping)
|
||||
|
||||
# Dup first and second level of hash (method and params).
|
||||
method_scoping = method_scoping.inject({}) do |hash, (method, params)|
|
||||
hash[method] = (params == true) ? params : params.dup
|
||||
hash
|
||||
end
|
||||
|
||||
method_scoping.assert_valid_keys([ :find, :create ])
|
||||
|
||||
if f = method_scoping[:find]
|
||||
f.assert_valid_keys([ :conditions, :joins, :select, :include, :from, :offset, :limit, :order, :readonly, :lock ])
|
||||
set_readonly_option! f
|
||||
end
|
||||
|
||||
# Merge scopings
|
||||
if action == :merge && current_scoped_methods
|
||||
method_scoping = current_scoped_methods.inject(method_scoping) do |hash, (method, params)|
|
||||
case hash[method]
|
||||
when Hash
|
||||
if method == :find
|
||||
(hash[method].keys + params.keys).uniq.each do |key|
|
||||
merge = hash[method][key] && params[key] # merge if both scopes have the same key
|
||||
if key == :conditions && merge
|
||||
hash[method][key] = [params[key], hash[method][key]].collect{ |sql| "( %s )" % sanitize_sql(sql) }.join(" AND ")
|
||||
elsif key == :include && merge
|
||||
hash[method][key] = merge_includes(hash[method][key], params[key]).uniq
|
||||
else
|
||||
hash[method][key] = hash[method][key] || params[key]
|
||||
end
|
||||
end
|
||||
else
|
||||
hash[method] = params.merge(hash[method])
|
||||
end
|
||||
else
|
||||
hash[method] = params
|
||||
end
|
||||
hash
|
||||
end
|
||||
end
|
||||
|
||||
self.scoped_methods << method_scoping
|
||||
|
||||
begin
|
||||
yield
|
||||
ensure
|
||||
self.scoped_methods.pop
|
||||
end
|
||||
end
|
||||
|
||||
# Works like with_scope, but discards any nested properties.
|
||||
def with_exclusive_scope(method_scoping = {}, &block)
|
||||
with_scope(method_scoping, :overwrite, &block)
|
||||
end
|
||||
|
||||
# Overwrite the default class equality method to provide support for association proxies.
|
||||
def ===(object)
|
||||
object.is_a?(self)
|
||||
|
@ -1409,6 +1324,103 @@ module ActiveRecord #:nodoc:
|
|||
end
|
||||
|
||||
protected
|
||||
# Scope parameters to method calls within the block. Takes a hash of method_name => parameters hash.
|
||||
# method_name may be :find or :create. :find parameters may include the <tt>:conditions</tt>, <tt>:joins</tt>,
|
||||
# <tt>:include</tt>, <tt>:offset</tt>, <tt>:limit</tt>, and <tt>:readonly</tt> options. :create parameters are an attributes hash.
|
||||
#
|
||||
# class Article < ActiveRecord::Base
|
||||
# def self.create_with_scope
|
||||
# with_scope(:find => { :conditions => "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 inner rule
|
||||
# except :conditions in :find, that are merged as hash.
|
||||
#
|
||||
# class Article < ActiveRecord::Base
|
||||
# def self.find_with_scope
|
||||
# with_scope(:find => { :conditions => "blog_id = 1", :limit => 1 }, :create => { :blog_id => 1 }) do
|
||||
# with_scope(:find => { :limit => 10})
|
||||
# find(:all) # => SELECT * from articles WHERE blog_id = 1 LIMIT 10
|
||||
# end
|
||||
# with_scope(:find => { :conditions => "author_id = 3" })
|
||||
# find(: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 <tt>with_exclusive_scope</tt> method.
|
||||
#
|
||||
# class Article < ActiveRecord::Base
|
||||
# def self.find_with_exclusive_scope
|
||||
# with_scope(:find => { :conditions => "blog_id = 1", :limit => 1 }) do
|
||||
# with_exclusive_scope(:find => { :limit => 10 })
|
||||
# find(:all) # => SELECT * from articles LIMIT 10
|
||||
# end
|
||||
# end
|
||||
# end
|
||||
# end
|
||||
def with_scope(method_scoping = {}, action = :merge, &block)
|
||||
method_scoping = method_scoping.method_scoping if method_scoping.respond_to?(:method_scoping)
|
||||
|
||||
# Dup first and second level of hash (method and params).
|
||||
method_scoping = method_scoping.inject({}) do |hash, (method, params)|
|
||||
hash[method] = (params == true) ? params : params.dup
|
||||
hash
|
||||
end
|
||||
|
||||
method_scoping.assert_valid_keys([ :find, :create ])
|
||||
|
||||
if f = method_scoping[:find]
|
||||
f.assert_valid_keys([ :conditions, :joins, :select, :include, :from, :offset, :limit, :order, :readonly, :lock ])
|
||||
set_readonly_option! f
|
||||
end
|
||||
|
||||
# Merge scopings
|
||||
if action == :merge && current_scoped_methods
|
||||
method_scoping = current_scoped_methods.inject(method_scoping) do |hash, (method, params)|
|
||||
case hash[method]
|
||||
when Hash
|
||||
if method == :find
|
||||
(hash[method].keys + params.keys).uniq.each do |key|
|
||||
merge = hash[method][key] && params[key] # merge if both scopes have the same key
|
||||
if key == :conditions && merge
|
||||
hash[method][key] = [params[key], hash[method][key]].collect{ |sql| "( %s )" % sanitize_sql(sql) }.join(" AND ")
|
||||
elsif key == :include && merge
|
||||
hash[method][key] = merge_includes(hash[method][key], params[key]).uniq
|
||||
else
|
||||
hash[method][key] = hash[method][key] || params[key]
|
||||
end
|
||||
end
|
||||
else
|
||||
hash[method] = params.merge(hash[method])
|
||||
end
|
||||
else
|
||||
hash[method] = params
|
||||
end
|
||||
hash
|
||||
end
|
||||
end
|
||||
|
||||
self.scoped_methods << method_scoping
|
||||
|
||||
begin
|
||||
yield
|
||||
ensure
|
||||
self.scoped_methods.pop
|
||||
end
|
||||
end
|
||||
|
||||
# Works like with_scope, but discards any nested properties.
|
||||
def with_exclusive_scope(method_scoping = {}, &block)
|
||||
with_scope(method_scoping, :overwrite, &block)
|
||||
end
|
||||
|
||||
def subclasses #:nodoc:
|
||||
@@subclasses[self] ||= []
|
||||
@@subclasses[self] + extra = @@subclasses[self].inject([]) {|list, subclass| list + subclass.subclasses }
|
||||
|
|
|
@ -75,5 +75,10 @@ ActiveRecord::Base.connection.class.class_eval do
|
|||
end
|
||||
end
|
||||
|
||||
# Make with_scope public for tests
|
||||
class << ActiveRecord::Base
|
||||
public :with_scope, :with_exclusive_scope
|
||||
end
|
||||
|
||||
#ActiveRecord::Base.logger = Logger.new(STDOUT)
|
||||
#ActiveRecord::Base.colorize_logging = false
|
||||
|
|
Loading…
Reference in a new issue