aboutsummaryrefslogtreecommitdiffstats
path: root/activerecord/lib
diff options
context:
space:
mode:
Diffstat (limited to 'activerecord/lib')
-rw-r--r--activerecord/lib/active_record/associations/builder/belongs_to.rb3
-rw-r--r--activerecord/lib/active_record/attribute_methods.rb12
-rw-r--r--activerecord/lib/active_record/callbacks.rb8
-rw-r--r--activerecord/lib/active_record/connection_adapters/abstract/connection_pool.rb2
-rw-r--r--activerecord/lib/active_record/inheritance.rb20
-rw-r--r--activerecord/lib/active_record/persistence.rb5
-rw-r--r--activerecord/lib/active_record/relation.rb5
-rw-r--r--activerecord/lib/active_record/relation/delegation.rb2
-rw-r--r--activerecord/lib/active_record/touch_later.rb16
-rw-r--r--activerecord/lib/active_record/transactions.rb26
10 files changed, 69 insertions, 30 deletions
diff --git a/activerecord/lib/active_record/associations/builder/belongs_to.rb b/activerecord/lib/active_record/associations/builder/belongs_to.rb
index 81c535d962..f02d146e89 100644
--- a/activerecord/lib/active_record/associations/builder/belongs_to.rb
+++ b/activerecord/lib/active_record/associations/builder/belongs_to.rb
@@ -106,8 +106,7 @@ module ActiveRecord::Associations::Builder # :nodoc:
touch = reflection.options[:touch]
callback = lambda { |record|
- touch_method = touching_delayed_records? ? :touch : :touch_later
- BelongsTo.touch_record(record, foreign_key, n, touch, touch_method)
+ BelongsTo.touch_record(record, foreign_key, n, touch, belongs_to_touch_method)
}
model.after_save callback, if: :changed?
diff --git a/activerecord/lib/active_record/attribute_methods.rb b/activerecord/lib/active_record/attribute_methods.rb
index 4ae585d3f5..423a93964e 100644
--- a/activerecord/lib/active_record/attribute_methods.rb
+++ b/activerecord/lib/active_record/attribute_methods.rb
@@ -191,6 +191,18 @@ module ActiveRecord
end
end
+ # Returns true if the given attribute exists, otherwise false.
+ #
+ # class Person < ActiveRecord::Base
+ # end
+ #
+ # Person.has_attribute?('name') # => true
+ # Person.has_attribute?(:age) # => true
+ # Person.has_attribute?(:nothing) # => false
+ def has_attribute?(attr_name)
+ attribute_types.key?(attr_name.to_s)
+ end
+
# Returns the column object for the named attribute.
# Returns a +ActiveRecord::ConnectionAdapters::NullColumn+ if the
# named attribute does not exist.
diff --git a/activerecord/lib/active_record/callbacks.rb b/activerecord/lib/active_record/callbacks.rb
index cfd8cbda67..4058affec3 100644
--- a/activerecord/lib/active_record/callbacks.rb
+++ b/activerecord/lib/active_record/callbacks.rb
@@ -208,12 +208,12 @@ module ActiveRecord
#
# Sometimes the code needs that the callbacks execute in a specific order. For example, a +before_destroy+
# callback (+log_children+ in this case) should be executed before the children get destroyed by the
- # <tt>dependent: destroy</tt> option.
+ # <tt>dependent: :destroy</tt> option.
#
# Let's look at the code below:
#
# class Topic < ActiveRecord::Base
- # has_many :children, dependent: destroy
+ # has_many :children, dependent: :destroy
#
# before_destroy :log_children
#
@@ -228,7 +228,7 @@ module ActiveRecord
# You can use the +prepend+ option on the +before_destroy+ callback to avoid this.
#
# class Topic < ActiveRecord::Base
- # has_many :children, dependent: destroy
+ # has_many :children, dependent: :destroy
#
# before_destroy :log_children, prepend: true
#
@@ -238,7 +238,7 @@ module ActiveRecord
# end
# end
#
- # This way, the +before_destroy+ gets executed before the <tt>dependent: destroy</tt> is called, and the data is still available.
+ # This way, the +before_destroy+ gets executed before the <tt>dependent: :destroy</tt> is called, and the data is still available.
#
# == \Transactions
#
diff --git a/activerecord/lib/active_record/connection_adapters/abstract/connection_pool.rb b/activerecord/lib/active_record/connection_adapters/abstract/connection_pool.rb
index 486b7b6d25..ccd2899489 100644
--- a/activerecord/lib/active_record/connection_adapters/abstract/connection_pool.rb
+++ b/activerecord/lib/active_record/connection_adapters/abstract/connection_pool.rb
@@ -197,7 +197,7 @@ module ActiveRecord
elapsed = Time.now - t0
if elapsed >= timeout
- msg = 'could not obtain a database connection within %0.3f seconds (waited %0.3f seconds)' %
+ msg = 'could not obtain a connection from the pool within %0.3f seconds (waited %0.3f seconds); all pooled connections were in use' %
[timeout, elapsed]
raise ConnectionTimeoutError, msg
end
diff --git a/activerecord/lib/active_record/inheritance.rb b/activerecord/lib/active_record/inheritance.rb
index 8b719e0bcb..6259c4cd33 100644
--- a/activerecord/lib/active_record/inheritance.rb
+++ b/activerecord/lib/active_record/inheritance.rb
@@ -51,8 +51,8 @@ module ActiveRecord
end
attrs = args.first
- if subclass_from_attributes?(attrs)
- subclass = subclass_from_attributes(attrs)
+ if has_attribute?(inheritance_column)
+ subclass = subclass_from_attributes(attrs) || subclass_from_attributes(column_defaults)
end
if subclass && subclass != self
@@ -163,7 +163,7 @@ module ActiveRecord
end
def using_single_table_inheritance?(record)
- record[inheritance_column].present? && columns_hash.include?(inheritance_column)
+ record[inheritance_column].present? && has_attribute?(inheritance_column)
end
def find_sti_class(type_name)
@@ -195,18 +195,14 @@ module ActiveRecord
# Detect the subclass from the inheritance column of attrs. If the inheritance column value
# is not self or a valid subclass, raises ActiveRecord::SubclassNotFound
- # If this is a StrongParameters hash, and access to inheritance_column is not permitted,
- # this will ignore the inheritance column and return nil
- def subclass_from_attributes?(attrs)
- attribute_names.include?(inheritance_column) && (attrs.is_a?(Hash) || attrs.respond_to?(:permitted?))
- end
-
def subclass_from_attributes(attrs)
attrs = attrs.to_h if attrs.respond_to?(:permitted?)
- subclass_name = attrs.with_indifferent_access[inheritance_column]
+ if attrs.is_a?(Hash)
+ subclass_name = attrs.with_indifferent_access[inheritance_column]
- if subclass_name.present?
- find_sti_class(subclass_name)
+ if subclass_name.present?
+ find_sti_class(subclass_name)
+ end
end
end
end
diff --git a/activerecord/lib/active_record/persistence.rb b/activerecord/lib/active_record/persistence.rb
index 46c6d8c293..9e566031b8 100644
--- a/activerecord/lib/active_record/persistence.rb
+++ b/activerecord/lib/active_record/persistence.rb
@@ -298,6 +298,7 @@ module ActiveRecord
# * \Validations are skipped.
# * \Callbacks are skipped.
# * +updated_at+/+updated_on+ are not updated.
+ # * However, attributes are serialized with the same rules as ActiveRecord::Relation#update_all
#
# This method raises an ActiveRecord::ActiveRecordError when called on new
# objects, or when at least one of the attributes is marked as readonly.
@@ -566,5 +567,9 @@ module ActiveRecord
ensure
@_association_destroy_exception = nil
end
+
+ def belongs_to_touch_method
+ :touch
+ end
end
end
diff --git a/activerecord/lib/active_record/relation.rb b/activerecord/lib/active_record/relation.rb
index f100476374..2cf19c76c5 100644
--- a/activerecord/lib/active_record/relation.rb
+++ b/activerecord/lib/active_record/relation.rb
@@ -347,9 +347,8 @@ module ActiveRecord
# Updates all records in the current relation with details given. This method constructs a single SQL UPDATE
# statement and sends it straight to the database. It does not instantiate the involved models and it does not
- # trigger Active Record callbacks or validations. Values passed to #update_all will not go through
- # Active Record's type-casting behavior. It should receive only values that can be passed as-is to the SQL
- # database.
+ # trigger Active Record callbacks or validations. However, values passed to #update_all will still go through
+ # Active Record's normal type casting and serialization.
#
# ==== Parameters
#
diff --git a/activerecord/lib/active_record/relation/delegation.rb b/activerecord/lib/active_record/relation/delegation.rb
index b1333f110c..e4e5d63006 100644
--- a/activerecord/lib/active_record/relation/delegation.rb
+++ b/activerecord/lib/active_record/relation/delegation.rb
@@ -37,7 +37,7 @@ module ActiveRecord
# for each different klass, and the delegations are compiled into that subclass only.
delegate :to_xml, :to_yaml, :length, :collect, :map, :each, :all?, :include?, :to_ary, :join,
- :[], :&, :|, :+, :-, :sample, :reverse, :compact, to: :to_a
+ :[], :&, :|, :+, :-, :sample, :shuffle, :reverse, :compact, to: :to_a
delegate :table_name, :quoted_table_name, :primary_key, :quoted_primary_key,
:connection, :columns_hash, :to => :klass
diff --git a/activerecord/lib/active_record/touch_later.rb b/activerecord/lib/active_record/touch_later.rb
index 4352a0ffea..9a80a63e28 100644
--- a/activerecord/lib/active_record/touch_later.rb
+++ b/activerecord/lib/active_record/touch_later.rb
@@ -16,6 +16,13 @@ module ActiveRecord
surreptitiously_touch @_defer_touch_attrs
self.class.connection.add_transaction_record self
+
+ # touch the parents as we are not calling the after_save callbacks
+ self.class.reflect_on_all_associations(:belongs_to).each do |r|
+ if touch = r.options[:touch]
+ ActiveRecord::Associations::Builder::BelongsTo.touch_record(self, r.foreign_key, r.name, touch, :touch_later)
+ end
+ end
end
def touch(*names, time: nil) # :nodoc:
@@ -26,6 +33,7 @@ module ActiveRecord
end
private
+
def surreptitiously_touch(attrs)
attrs.each { |attr| write_attribute attr, @_touch_time }
clear_attribute_changes attrs
@@ -33,9 +41,8 @@ module ActiveRecord
def touch_deferred_attributes
if has_defer_touch_attrs? && persisted?
- @_touching_delayed_records = true
touch(*@_defer_touch_attrs, time: @_touch_time)
- @_touching_delayed_records, @_defer_touch_attrs, @_touch_time = nil, nil, nil
+ @_defer_touch_attrs, @_touch_time = nil, nil
end
end
@@ -43,8 +50,9 @@ module ActiveRecord
defined?(@_defer_touch_attrs) && @_defer_touch_attrs.present?
end
- def touching_delayed_records?
- defined?(@_touching_delayed_records) && @_touching_delayed_records
+ def belongs_to_touch_method
+ :touch_later
end
+
end
end
diff --git a/activerecord/lib/active_record/transactions.rb b/activerecord/lib/active_record/transactions.rb
index 8de82feae3..38ab1f3fc6 100644
--- a/activerecord/lib/active_record/transactions.rb
+++ b/activerecord/lib/active_record/transactions.rb
@@ -233,6 +233,24 @@ module ActiveRecord
set_callback(:commit, :after, *args, &block)
end
+ # Shortcut for +after_commit :hook, on: :create+.
+ def after_create_commit(*args, &block)
+ set_options_for_callbacks!(args, on: :create)
+ set_callback(:commit, :after, *args, &block)
+ end
+
+ # Shortcut for +after_commit :hook, on: :update+.
+ def after_update_commit(*args, &block)
+ set_options_for_callbacks!(args, on: :update)
+ set_callback(:commit, :after, *args, &block)
+ end
+
+ # Shortcut for +after_commit :hook, on: :destroy+.
+ def after_destroy_commit(*args, &block)
+ set_options_for_callbacks!(args, on: :destroy)
+ set_callback(:commit, :after, *args, &block)
+ end
+
# This callback is called after a create, update, or destroy are rolled back.
#
# Please check the documentation of #after_commit for options.
@@ -268,9 +286,11 @@ module ActiveRecord
private
- def set_options_for_callbacks!(args)
- options = args.last
- if options.is_a?(Hash) && options[:on]
+ def set_options_for_callbacks!(args, enforced_options = {})
+ options = args.extract_options!.merge!(enforced_options)
+ args << options
+
+ if options[:on]
fire_on = Array(options[:on])
assert_valid_transaction_action(fire_on)
options[:if] = Array(options[:if])