diff options
author | Ryuta Kamizono <kamipo@gmail.com> | 2019-02-07 17:44:23 +0900 |
---|---|---|
committer | Ryuta Kamizono <kamipo@gmail.com> | 2019-02-07 21:04:01 +0900 |
commit | 22360534ac922c68fb0a28f584b48bc0f3633221 (patch) | |
tree | 634c8632646c2a4aa2560df46fad9f0a3e184ffb /activerecord/lib/active_record | |
parent | 2e018361c7c51e36d1d98bf770b7456d78dee68b (diff) | |
download | rails-22360534ac922c68fb0a28f584b48bc0f3633221.tar.gz rails-22360534ac922c68fb0a28f584b48bc0f3633221.tar.bz2 rails-22360534ac922c68fb0a28f584b48bc0f3633221.zip |
Fix `relation.create` to avoid leaking scope to initialization block and callbacks
`relation.create` populates scope attributes to new record by `scoping`,
it is necessary to assign the scope attributes to the record and to find
STI subclass from the scope attributes.
But the effect of `scoping` is class global, it was caused undesired
behavior that pollute all class level querying methods in initialization
block and callbacks (`after_initialize`, `before_validation`,
`before_save`, etc), which are user provided code.
To avoid the leaking scope issue, restore the original current scope
before initialization block and callbacks are invoked.
Fixes #9894.
Fixes #17577.
Closes #31526.
Diffstat (limited to 'activerecord/lib/active_record')
-rw-r--r-- | activerecord/lib/active_record/relation.rb | 17 |
1 files changed, 15 insertions, 2 deletions
diff --git a/activerecord/lib/active_record/relation.rb b/activerecord/lib/active_record/relation.rb index ab3d6f7222..32f0609798 100644 --- a/activerecord/lib/active_record/relation.rb +++ b/activerecord/lib/active_record/relation.rb @@ -67,6 +67,11 @@ module ActiveRecord # user = users.new { |user| user.name = 'Oscar' } # user.name # => Oscar def new(attributes = nil, &block) + current_scope = klass.current_scope(true) + block = -> record do + klass.current_scope = current_scope + yield record if block_given? + end scoping { klass.new(attributes, &block) } end @@ -92,7 +97,11 @@ module ActiveRecord # users.create(name: nil) # validation on name # # => #<User id: nil, name: nil, ...> def create(attributes = nil, &block) - scoping { klass.create(attributes, &block) } + if attributes.is_a?(Array) + attributes.collect { |attr| create(attr, &block) } + else + new(attributes, &block).tap(&:save) + end end # Similar to #create, but calls @@ -102,7 +111,11 @@ module ActiveRecord # Expects arguments in the same format as # {ActiveRecord::Base.create!}[rdoc-ref:Persistence::ClassMethods#create!]. def create!(attributes = nil, &block) - scoping { klass.create!(attributes, &block) } + if attributes.is_a?(Array) + attributes.collect { |attr| create!(attr, &block) } + else + new(attributes, &block).tap(&:save!) + end end def first_or_create(attributes = nil, &block) # :nodoc: |