aboutsummaryrefslogtreecommitdiffstats
path: root/activerecord/lib
diff options
context:
space:
mode:
Diffstat (limited to 'activerecord/lib')
-rw-r--r--activerecord/lib/active_record/associations/collection_association.rb5
-rw-r--r--activerecord/lib/active_record/associations/collection_proxy.rb34
-rw-r--r--activerecord/lib/active_record/middleware/database_selector.rb22
-rw-r--r--activerecord/lib/active_record/middleware/database_selector/resolver.rb14
-rw-r--r--activerecord/lib/active_record/middleware/database_selector/resolver/session.rb2
-rw-r--r--activerecord/lib/active_record/railtie.rb2
-rw-r--r--activerecord/lib/active_record/railties/collection_cache_association_loading.rb2
-rw-r--r--activerecord/lib/active_record/relation.rb24
-rw-r--r--activerecord/lib/active_record/relation/delegation.rb2
-rw-r--r--activerecord/lib/active_record/relation/spawn_methods.rb2
-rw-r--r--activerecord/lib/active_record/scoping.rb19
-rw-r--r--activerecord/lib/active_record/scoping/default.rb9
12 files changed, 63 insertions, 74 deletions
diff --git a/activerecord/lib/active_record/associations/collection_association.rb b/activerecord/lib/active_record/associations/collection_association.rb
index c6941a2e76..b0c0beac0e 100644
--- a/activerecord/lib/active_record/associations/collection_association.rb
+++ b/activerecord/lib/active_record/associations/collection_association.rb
@@ -109,9 +109,8 @@ module ActiveRecord
end
end
- # Add +records+ to this association. Returns +self+ so method calls may
- # be chained. Since << flattens its argument list and inserts each record,
- # +push+ and +concat+ behave identically.
+ # Add +records+ to this association. Since +<<+ flattens its argument list
+ # and inserts each record, +push+ and +concat+ behave identically.
def concat(*records)
records = records.flatten
if owner.new_record?
diff --git a/activerecord/lib/active_record/associations/collection_proxy.rb b/activerecord/lib/active_record/associations/collection_proxy.rb
index 4fbbc713e4..78993cee8a 100644
--- a/activerecord/lib/active_record/associations/collection_proxy.rb
+++ b/activerecord/lib/active_record/associations/collection_proxy.rb
@@ -366,34 +366,6 @@ module ActiveRecord
@association.create!(attributes, &block)
end
- # Add one or more records to the collection by setting their foreign keys
- # to the association's primary key. Since #<< flattens its argument list and
- # inserts each record, +push+ and #concat behave identically. Returns +self+
- # so method calls may be chained.
- #
- # class Person < ActiveRecord::Base
- # has_many :pets
- # end
- #
- # person.pets.size # => 0
- # person.pets.concat(Pet.new(name: 'Fancy-Fancy'))
- # person.pets.concat(Pet.new(name: 'Spook'), Pet.new(name: 'Choo-Choo'))
- # person.pets.size # => 3
- #
- # person.id # => 1
- # person.pets
- # # => [
- # # #<Pet id: 1, name: "Fancy-Fancy", person_id: 1>,
- # # #<Pet id: 2, name: "Spook", person_id: 1>,
- # # #<Pet id: 3, name: "Choo-Choo", person_id: 1>
- # # ]
- #
- # person.pets.concat([Pet.new(name: 'Brain'), Pet.new(name: 'Benny')])
- # person.pets.size # => 5
- def concat(*records)
- @association.concat(*records)
- end
-
# Replaces this collection with +other_array+. This will perform a diff
# and delete/add only records that have changed.
#
@@ -1033,8 +1005,9 @@ module ActiveRecord
end
# Adds one or more +records+ to the collection by setting their foreign keys
- # to the association's primary key. Returns +self+, so several appends may be
- # chained together.
+ # to the association's primary key. Since +<<+ flattens its argument list and
+ # inserts each record, +push+ and +concat+ behave identically. Returns +self+
+ # so several appends may be chained together.
#
# class Person < ActiveRecord::Base
# has_many :pets
@@ -1057,6 +1030,7 @@ module ActiveRecord
end
alias_method :push, :<<
alias_method :append, :<<
+ alias_method :concat, :<<
def prepend(*args)
raise NoMethodError, "prepend on association is not defined. Please use <<, push or append"
diff --git a/activerecord/lib/active_record/middleware/database_selector.rb b/activerecord/lib/active_record/middleware/database_selector.rb
index 3ab50f5f6b..aad263695b 100644
--- a/activerecord/lib/active_record/middleware/database_selector.rb
+++ b/activerecord/lib/active_record/middleware/database_selector.rb
@@ -12,8 +12,8 @@ module ActiveRecord
#
# The resolver class defines when the application should switch (i.e. read
# from the primary if a write occurred less than 2 seconds ago) and an
- # operations class that sets a value that helps the resolver class decide
- # when to switch.
+ # resolver context class that sets a value that helps the resolver class
+ # decide when to switch.
#
# Rails default middleware uses the request's session to set a timestamp
# that informs the application when to read from a primary or read from a
@@ -24,7 +24,7 @@ module ActiveRecord
#
# config.active_record.database_selector = { delay: 2.seconds }
# config.active_record.database_resolver = ActiveRecord::Middleware::DatabaseSelector::Resolver
- # config.active_record.database_operations = ActiveRecord::Middleware::DatabaseSelector::Resolver::Session
+ # config.active_record.database_resolver_context = ActiveRecord::Middleware::DatabaseSelector::Resolver::Session
#
# New applications will include these lines commented out in the production.rb.
#
@@ -33,16 +33,16 @@ module ActiveRecord
#
# config.active_record.database_selector = { delay: 2.seconds }
# config.active_record.database_resolver = MyResolver
- # config.active_record.database_operations = MyResolver::MySession
+ # config.active_record.database_resolver_context = MyResolver::MySession
class DatabaseSelector
- def initialize(app, resolver_klass = Resolver, operations_klass = Resolver::Session, options = {})
+ def initialize(app, resolver_klass = Resolver, context_klass = Resolver::Session, options = {})
@app = app
@resolver_klass = resolver_klass
- @operations_klass = operations_klass
+ @context_klass = context_klass
@options = options
end
- attr_reader :resolver_klass, :operations_klass, :options
+ attr_reader :resolver_klass, :context_klass, :options
# Middleware that determines which database connection to use in a multiple
# database application.
@@ -57,13 +57,13 @@ module ActiveRecord
private
def select_database(request, &blk)
- operations = operations_klass.build(request)
- database_resolver = resolver_klass.call(operations, options)
+ context = context_klass.call(request)
+ resolver = resolver_klass.call(context, options)
if reading_request?(request)
- database_resolver.read(&blk)
+ resolver.read(&blk)
else
- database_resolver.write(&blk)
+ resolver.write(&blk)
end
end
diff --git a/activerecord/lib/active_record/middleware/database_selector/resolver.rb b/activerecord/lib/active_record/middleware/database_selector/resolver.rb
index a84c292714..80b8cd7cae 100644
--- a/activerecord/lib/active_record/middleware/database_selector/resolver.rb
+++ b/activerecord/lib/active_record/middleware/database_selector/resolver.rb
@@ -18,18 +18,18 @@ module ActiveRecord
class Resolver # :nodoc:
SEND_TO_REPLICA_DELAY = 2.seconds
- def self.call(resolver, options = {})
- new(resolver, options)
+ def self.call(context, options = {})
+ new(context, options)
end
- def initialize(resolver, options = {})
- @resolver = resolver
+ def initialize(context, options = {})
+ @context = context
@options = options
@delay = @options && @options[:delay] ? @options[:delay] : SEND_TO_REPLICA_DELAY
@instrumenter = ActiveSupport::Notifications.instrumenter
end
- attr_reader :resolver, :delay, :instrumenter
+ attr_reader :context, :delay, :instrumenter
def read(&blk)
if read_from_primary?
@@ -68,7 +68,7 @@ module ActiveRecord
instrumenter.instrument("database_selector.active_record.wrote_to_primary") do
yield
ensure
- resolver.update_last_write_timestamp
+ context.update_last_write_timestamp
end
end
end
@@ -82,7 +82,7 @@ module ActiveRecord
end
def time_since_last_write_ok?
- Time.now - resolver.last_write_timestamp >= send_to_replica_delay
+ Time.now - context.last_write_timestamp >= send_to_replica_delay
end
end
end
diff --git a/activerecord/lib/active_record/middleware/database_selector/resolver/session.rb b/activerecord/lib/active_record/middleware/database_selector/resolver/session.rb
index 33e0af5ee4..df7af054b7 100644
--- a/activerecord/lib/active_record/middleware/database_selector/resolver/session.rb
+++ b/activerecord/lib/active_record/middleware/database_selector/resolver/session.rb
@@ -10,7 +10,7 @@ module ActiveRecord
# The last_write is used to determine whether it's safe to read
# from the replica or the request needs to be sent to the primary.
class Session # :nodoc:
- def self.build(request)
+ def self.call(request)
new(request.session)
end
diff --git a/activerecord/lib/active_record/railtie.rb b/activerecord/lib/active_record/railtie.rb
index aac49a92b4..a1d7c893bf 100644
--- a/activerecord/lib/active_record/railtie.rb
+++ b/activerecord/lib/active_record/railtie.rb
@@ -91,7 +91,7 @@ module ActiveRecord
initializer "active_record.database_selector" do
if options = config.active_record.delete(:database_selector)
resolver = config.active_record.delete(:database_resolver)
- operations = config.active_record.delete(:database_operations)
+ operations = config.active_record.delete(:database_resolver_context)
config.app_middleware.use ActiveRecord::Middleware::DatabaseSelector, resolver, operations, options
end
end
diff --git a/activerecord/lib/active_record/railties/collection_cache_association_loading.rb b/activerecord/lib/active_record/railties/collection_cache_association_loading.rb
index dfaac4eefb..d57680aaaa 100644
--- a/activerecord/lib/active_record/railties/collection_cache_association_loading.rb
+++ b/activerecord/lib/active_record/railties/collection_cache_association_loading.rb
@@ -3,7 +3,7 @@
module ActiveRecord
module Railties # :nodoc:
module CollectionCacheAssociationLoading #:nodoc:
- def setup(context, options, block)
+ def setup(context, options, as, block)
@relation = relation_from_options(options)
super
diff --git a/activerecord/lib/active_record/relation.rb b/activerecord/lib/active_record/relation.rb
index a863227276..347d745d19 100644
--- a/activerecord/lib/active_record/relation.rb
+++ b/activerecord/lib/active_record/relation.rb
@@ -67,6 +67,7 @@ module ActiveRecord
# user = users.new { |user| user.name = 'Oscar' }
# user.name # => Oscar
def new(attributes = nil, &block)
+ block = klass.current_scope_restoring_block(&block)
scoping { klass.new(attributes, &block) }
end
@@ -92,7 +93,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 +107,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:
@@ -312,12 +321,12 @@ 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
- @delegate_to_klass ? yield : klass._scoping(self) { yield }
+ @delegate_to_klass && klass.current_scope(true) ? yield : _scoping(self) { yield }
end
def _exec_scope(*args, &block) # :nodoc:
@delegate_to_klass = true
- instance_exec(*args, &block) || self
+ _scoping(nil) { instance_exec(*args, &block) || self }
ensure
@delegate_to_klass = false
end
@@ -632,6 +641,13 @@ module ActiveRecord
end
private
+ def _scoping(scope)
+ previous, klass.current_scope = klass.current_scope(true), scope
+ yield
+ ensure
+ klass.current_scope = previous
+ end
+
def _substitute_values(values)
values.map do |name, value|
attr = arel_attribute(name)
diff --git a/activerecord/lib/active_record/relation/delegation.rb b/activerecord/lib/active_record/relation/delegation.rb
index 6e8a1fcad4..f7c3b3783f 100644
--- a/activerecord/lib/active_record/relation/delegation.rb
+++ b/activerecord/lib/active_record/relation/delegation.rb
@@ -132,7 +132,7 @@ module ActiveRecord
private
def respond_to_missing?(method, _)
- super || @klass.respond_to?(method) || arel.respond_to?(method)
+ super || @klass.respond_to?(method)
end
end
end
diff --git a/activerecord/lib/active_record/relation/spawn_methods.rb b/activerecord/lib/active_record/relation/spawn_methods.rb
index 7874c4c35a..8cf4fc469f 100644
--- a/activerecord/lib/active_record/relation/spawn_methods.rb
+++ b/activerecord/lib/active_record/relation/spawn_methods.rb
@@ -8,7 +8,7 @@ module ActiveRecord
module SpawnMethods
# This is overridden by Associations::CollectionProxy
def spawn #:nodoc:
- @delegate_to_klass ? klass.all : clone
+ @delegate_to_klass && klass.current_scope(true) ? klass.all : clone
end
# Merges in the conditions from <tt>other</tt>, if <tt>other</tt> is an ActiveRecord::Relation.
diff --git a/activerecord/lib/active_record/scoping.rb b/activerecord/lib/active_record/scoping.rb
index 9eba1254a4..1142a87d25 100644
--- a/activerecord/lib/active_record/scoping.rb
+++ b/activerecord/lib/active_record/scoping.rb
@@ -23,14 +23,21 @@ module ActiveRecord
current_scope
end
- private
- def current_scope(skip_inherited_scope = false)
- ScopeRegistry.value_for(:current_scope, self, skip_inherited_scope)
- end
+ def current_scope(skip_inherited_scope = false)
+ ScopeRegistry.value_for(:current_scope, self, skip_inherited_scope)
+ end
- def current_scope=(scope)
- ScopeRegistry.set_value_for(:current_scope, self, scope)
+ def current_scope=(scope)
+ ScopeRegistry.set_value_for(:current_scope, self, scope)
+ end
+
+ def current_scope_restoring_block(&block)
+ current_scope = self.current_scope(true)
+ -> *args do
+ self.current_scope = current_scope
+ yield(*args) if block_given?
end
+ end
end
def populate_with_current_scope_attributes # :nodoc:
diff --git a/activerecord/lib/active_record/scoping/default.rb b/activerecord/lib/active_record/scoping/default.rb
index 6caf9b3251..8c612df27a 100644
--- a/activerecord/lib/active_record/scoping/default.rb
+++ b/activerecord/lib/active_record/scoping/default.rb
@@ -31,14 +31,7 @@ module ActiveRecord
# Post.limit(10) # Fires "SELECT * FROM posts LIMIT 10"
# }
def unscoped
- block_given? ? _scoping(relation) { yield } : relation
- end
-
- def _scoping(relation) # :nodoc:
- previous, self.current_scope = current_scope(true), relation
- yield
- ensure
- self.current_scope = previous
+ block_given? ? relation.scoping { yield } : relation
end
# Are there attributes associated with this scope?