aboutsummaryrefslogtreecommitdiffstats
path: root/activerecord/lib/active_record
diff options
context:
space:
mode:
Diffstat (limited to 'activerecord/lib/active_record')
-rwxr-xr-xactiverecord/lib/active_record/associations.rb53
-rwxr-xr-xactiverecord/lib/active_record/base.rb11
-rw-r--r--activerecord/lib/active_record/callbacks.rb255
-rw-r--r--activerecord/lib/active_record/observer.rb20
-rw-r--r--activerecord/lib/active_record/validations.rb35
5 files changed, 185 insertions, 189 deletions
diff --git a/activerecord/lib/active_record/associations.rb b/activerecord/lib/active_record/associations.rb
index 02dfb7b400..72061a1b31 100755
--- a/activerecord/lib/active_record/associations.rb
+++ b/activerecord/lib/active_record/associations.rb
@@ -1491,24 +1491,43 @@ module ActiveRecord
end
before_destroy method_name
when :delete_all
- module_eval %Q{
- before_destroy do |record| # before_destroy do |record|
- delete_all_has_many_dependencies(record, # delete_all_has_many_dependencies(record,
- "#{reflection.name}", # "posts",
- #{reflection.class_name}, # Post,
- %@#{dependent_conditions}@) # %@...@) # this is a string literal like %(...)
- end # end
- }
+ # before_destroy do |record|
+ # self.class.send(:delete_all_has_many_dependencies,
+ # record,
+ # "posts",
+ # Post,
+ # %@...@) # this is a string literal like %(...)
+ # end
+ # end
+ module_eval <<-CALLBACK
+ before_destroy do |record|
+ self.class.send(:delete_all_has_many_dependencies,
+ record,
+ "#{reflection.name}",
+ #{reflection.class_name},
+ %@#{dependent_conditions}@)
+ end
+ CALLBACK
when :nullify
- module_eval %Q{
- before_destroy do |record| # before_destroy do |record|
- nullify_has_many_dependencies(record, # nullify_has_many_dependencies(record,
- "#{reflection.name}", # "posts",
- #{reflection.class_name}, # Post,
- "#{reflection.primary_key_name}", # "user_id",
- %@#{dependent_conditions}@) # %@...@) # this is a string literal like %(...)
- end # end
- }
+ # before_destroy do |record|
+ # self.class.send(:nullify_has_many_dependencies,
+ # record,
+ # "posts",
+ # Post,
+ # "user_id",
+ # %@...@) # this is a string literal like %(...)
+ # end
+ # end
+ module_eval <<-CALLBACK
+ before_destroy do |record|
+ self.class.send(:nullify_has_many_dependencies,
+ record,
+ "#{reflection.name}",
+ #{reflection.class_name},
+ "#{reflection.primary_key_name}",
+ %@#{dependent_conditions}@)
+ end
+ CALLBACK
else
raise ArgumentError, "The :dependent option expects either :destroy, :delete_all, or :nullify (#{reflection.options[:dependent].inspect})"
end
diff --git a/activerecord/lib/active_record/base.rb b/activerecord/lib/active_record/base.rb
index 72742cb57c..afa4185c60 100755
--- a/activerecord/lib/active_record/base.rb
+++ b/activerecord/lib/active_record/base.rb
@@ -1679,13 +1679,8 @@ module ActiveRecord #:nodoc:
object.instance_variable_set("@attributes", record)
object.instance_variable_set("@attributes_cache", Hash.new)
- if object.respond_to_without_attributes?(:after_find)
- object.send(:callback, :after_find)
- end
-
- if object.respond_to_without_attributes?(:after_initialize)
- object.send(:callback, :after_initialize)
- end
+ object.send(:_run_find_callbacks)
+ object.send(:_run_initialize_callbacks)
object
end
@@ -2438,7 +2433,7 @@ module ActiveRecord #:nodoc:
self.attributes = attributes unless attributes.nil?
self.class.send(:scope, :create).each { |att,value| self.send("#{att}=", value) } if self.class.send(:scoped?, :create)
result = yield self if block_given?
- callback(:after_initialize) if respond_to_without_attributes?(:after_initialize)
+ _run_initialize_callbacks
result
end
diff --git a/activerecord/lib/active_record/callbacks.rb b/activerecord/lib/active_record/callbacks.rb
index dd509b6c6a..361c7b2ef4 100644
--- a/activerecord/lib/active_record/callbacks.rb
+++ b/activerecord/lib/active_record/callbacks.rb
@@ -10,16 +10,14 @@ module ActiveRecord
# * (-) <tt>save</tt>
# * (-) <tt>valid</tt>
# * (1) <tt>before_validation</tt>
- # * (2) <tt>before_validation_on_create</tt>
# * (-) <tt>validate</tt>
# * (-) <tt>validate_on_create</tt>
- # * (3) <tt>after_validation</tt>
- # * (4) <tt>after_validation_on_create</tt>
- # * (5) <tt>before_save</tt>
- # * (6) <tt>before_create</tt>
+ # * (2) <tt>after_validation</tt>
+ # * (3) <tt>before_save</tt>
+ # * (4) <tt>before_create</tt>
# * (-) <tt>create</tt>
- # * (7) <tt>after_create</tt>
- # * (8) <tt>after_save</tt>
+ # * (5) <tt>after_create</tt>
+ # * (6) <tt>after_save</tt>
#
# That's a total of eight callbacks, which gives you immense power to react and prepare for each state in the
# Active Record lifecycle. The sequence for calling <tt>Base#save</tt> for an existing record is similar, except that each
@@ -212,162 +210,161 @@ module ActiveRecord
# instead of quietly returning +false+.
module Callbacks
extend ActiveSupport::Concern
+ include ActiveSupport::NewCallbacks
- CALLBACKS = %w(
- after_find after_initialize before_save after_save before_create after_create before_update after_update before_validation
- after_validation before_validation_on_create after_validation_on_create before_validation_on_update
- after_validation_on_update before_destroy after_destroy
- )
+ CALLBACKS = [
+ :after_initialize, :after_find, :before_validation, :after_validation,
+ :before_save, :after_save, :before_create, :after_create, :before_update,
+ :after_update, :before_destroy, :after_destroy
+ ]
included do
- extend Observable
-
[:create_or_update, :valid?, :create, :update, :destroy].each do |method|
alias_method_chain method, :callbacks
end
- include ActiveSupport::Callbacks
- define_callbacks(*CALLBACKS)
+ define_callbacks :initialize, :find, :save, :create, :update, :destroy, :validation, "result == false"
end
- # Is called when the object was instantiated by one of the finders, like <tt>Base.find</tt>.
- #def after_find() end
-
- # Is called after the object has been instantiated by a call to <tt>Base.new</tt>.
- #def after_initialize() end
-
- # Is called _before_ <tt>Base.save</tt> (regardless of whether it's a +create+ or +update+ save).
- def before_save() end
+ module ClassMethods
+ def after_initialize(*args, &block)
+ options = args.extract_options!
+ options[:prepend] = true
+ set_callback(:initialize, :after, *(args << options), &block)
+ end
- # Is called _after_ <tt>Base.save</tt> (regardless of whether it's a +create+ or +update+ save).
- # Note that this callback is still wrapped in the transaction around +save+. For example, if you
- # invoke an external indexer at this point it won't see the changes in the database.
- #
- # class Contact < ActiveRecord::Base
- # after_save { logger.info( 'New contact saved!' ) }
- # end
- def after_save() end
- def create_or_update_with_callbacks #:nodoc:
- return false if callback(:before_save) == false
- if result = create_or_update_without_callbacks
- callback(:after_save)
+ def after_find(*args, &block)
+ options = args.extract_options!
+ options[:prepend] = true
+ set_callback(:find, :after, *(args << options), &block)
end
- result
- end
- private :create_or_update_with_callbacks
- # Is called _before_ <tt>Base.save</tt> on new objects that haven't been saved yet (no record exists).
- def before_create() end
+ def before_save(*args, &block)
+ set_callback(:save, :before, *args, &block)
+ end
- # Is called _after_ <tt>Base.save</tt> on new objects that haven't been saved yet (no record exists).
- # Note that this callback is still wrapped in the transaction around +save+. For example, if you
- # invoke an external indexer at this point it won't see the changes in the database.
- #
- # class Contact < ActiveRecord::Base
- # after_create { |record| logger.info( "Contact #{record.id} was created." ) }
- # end
- def after_create() end
- def create_with_callbacks #:nodoc:
- return false if callback(:before_create) == false
- result = create_without_callbacks
- callback(:after_create)
- result
- end
- private :create_with_callbacks
+ def around_save(*args, &block)
+ set_callback(:save, :around, *args, &block)
+ end
- # Is called _before_ <tt>Base.save</tt> on existing objects that have a record.
- #
- # class Contact < ActiveRecord::Base
- # before_update { |record| logger.info( "Contact #{record.id} is about to be updated." ) }
- # end
- def before_update() end
+ def after_save(*args, &block)
+ options = args.extract_options!
+ options[:prepend] = true
+ options[:if] = Array(options[:if]) << "!halted && value != false"
+ set_callback(:save, :after, *(args << options), &block)
+ end
- # Is called _after_ <tt>Base.save</tt> on existing objects that have a record.
- # Note that this callback is still wrapped in the transaction around +save+. For example, if you
- # invoke an external indexer at this point it won't see the changes in the database.
- #
- # class Contact < ActiveRecord::Base
- # after_update { |record| logger.info( "Contact #{record.id} was updated." ) }
- # end
- def after_update() end
+ def before_create(*args, &block)
+ set_callback(:create, :before, *args, &block)
+ end
- def update_with_callbacks(*args) #:nodoc:
- return false if callback(:before_update) == false
- result = update_without_callbacks(*args)
- callback(:after_update)
- result
- end
- private :update_with_callbacks
+ def around_create(*args, &block)
+ set_callback(:create, :around, *args, &block)
+ end
- # Is called _before_ <tt>Validations.validate</tt> (which is part of the <tt>Base.save</tt> call).
- def before_validation() end
+ def after_create(*args, &block)
+ options = args.extract_options!
+ options[:prepend] = true
+ options[:if] = Array(options[:if]) << "!halted && value != false"
+ set_callback(:create, :after, *(args << options), &block)
+ end
- # Is called _after_ <tt>Validations.validate</tt> (which is part of the <tt>Base.save</tt> call).
- def after_validation() end
+ def before_update(*args, &block)
+ set_callback(:update, :before, *args, &block)
+ end
- # Is called _before_ <tt>Validations.validate</tt> (which is part of the <tt>Base.save</tt> call) on new objects
- # that haven't been saved yet (no record exists).
- def before_validation_on_create() end
+ def around_update(*args, &block)
+ set_callback(:update, :around, *args, &block)
+ end
- # Is called _after_ <tt>Validations.validate</tt> (which is part of the <tt>Base.save</tt> call) on new objects
- # that haven't been saved yet (no record exists).
- def after_validation_on_create() end
+ def after_update(*args, &block)
+ options = args.extract_options!
+ options[:prepend] = true
+ options[:if] = Array(options[:if]) << "!halted && value != false"
+ set_callback(:update, :after, *(args << options), &block)
+ end
- # Is called _before_ <tt>Validations.validate</tt> (which is part of the <tt>Base.save</tt> call) on
- # existing objects that have a record.
- def before_validation_on_update() end
+ def before_destroy(*args, &block)
+ set_callback(:destroy, :before, *args, &block)
+ end
- # Is called _after_ <tt>Validations.validate</tt> (which is part of the <tt>Base.save</tt> call) on
- # existing objects that have a record.
- def after_validation_on_update() end
+ def around_destroy(*args, &block)
+ set_callback(:destroy, :around, *args, &block)
+ end
- def valid_with_callbacks? #:nodoc:
- return false if callback(:before_validation) == false
- if new_record? then result = callback(:before_validation_on_create) else result = callback(:before_validation_on_update) end
- return false if false == result
+ def after_destroy(*args, &block)
+ options = args.extract_options!
+ options[:prepend] = true
+ options[:if] = Array(options[:if]) << "!halted && value != false"
+ set_callback(:destroy, :after, *(args << options), &block)
+ end
- result = valid_without_callbacks?
+ def before_validation(*args, &block)
+ options = args.extract_options!
+ if options[:on]
+ options[:if] = Array(options[:if])
+ options[:if] << "@_on_validate == :#{options[:on]}"
+ end
+ set_callback(:validation, :before, *(args << options), &block)
+ end
- callback(:after_validation)
- if new_record? then callback(:after_validation_on_create) else callback(:after_validation_on_update) end
+ def after_validation(*args, &block)
+ options = args.extract_options!
+ options[:if] = Array(options[:if])
+ options[:if] << "!halted"
+ options[:if] << "@_on_validate == :#{options[:on]}" if options[:on]
+ options[:prepend] = true
+ set_callback(:validation, :after, *(args << options), &block)
+ end
- return result
+ def method_added(meth)
+ super
+ if CALLBACKS.include?(meth.to_sym)
+ ActiveSupport::Deprecation.warn("Base##{meth} has been deprecated, please use Base.#{meth} :method instead", caller[0,1])
+ send(meth.to_sym, meth.to_sym)
+ end
+ end
end
- # Is called _before_ <tt>Base.destroy</tt>.
- #
- # Note: If you need to _destroy_ or _nullify_ associated records first,
- # use the <tt>:dependent</tt> option on your associations.
- #
- # class Contact < ActiveRecord::Base
- # after_destroy { |record| logger.info( "Contact #{record.id} is about to be destroyed." ) }
- # end
- def before_destroy() end
+ def create_or_update_with_callbacks #:nodoc:
+ _run_save_callbacks do
+ create_or_update_without_callbacks
+ end
+ end
+ private :create_or_update_with_callbacks
- # Is called _after_ <tt>Base.destroy</tt> (and all the attributes have been frozen).
- #
- # class Contact < ActiveRecord::Base
- # after_destroy { |record| logger.info( "Contact #{record.id} was destroyed." ) }
- # end
- def after_destroy() end
- def destroy_with_callbacks #:nodoc:
- return false if callback(:before_destroy) == false
- result = destroy_without_callbacks
- callback(:after_destroy)
- result
+ def create_with_callbacks #:nodoc:
+ _run_create_callbacks do
+ create_without_callbacks
+ end
end
+ private :create_with_callbacks
- private
- def callback(method)
- result = run_callbacks(method) { |result, object| false == result }
+ def update_with_callbacks(*args) #:nodoc:
+ _run_update_callbacks do
+ update_without_callbacks(*args)
+ end
+ end
+ private :update_with_callbacks
- if result != false && respond_to_without_attributes?(method)
- result = send(method)
- end
+ def valid_with_callbacks? #:nodoc:
+ @_on_validate = new_record? ? :create : :update
+ _run_validation_callbacks do
+ valid_without_callbacks?
+ end
+ end
- notify_observers(method)
+ def destroy_with_callbacks #:nodoc:
+ _run_destroy_callbacks do
+ destroy_without_callbacks
+ end
+ end
- return result
+ def deprecated_callback_method(symbol) #:nodoc:
+ if respond_to?(symbol)
+ ActiveSupport::Deprecation.warn("Base##{symbol} has been deprecated, please use Base.#{symbol} :method instead")
+ send(symbol)
end
+ end
end
end
diff --git a/activerecord/lib/active_record/observer.rb b/activerecord/lib/active_record/observer.rb
index a34ff4a47a..4e05b819b5 100644
--- a/activerecord/lib/active_record/observer.rb
+++ b/activerecord/lib/active_record/observer.rb
@@ -1,6 +1,3 @@
-require 'singleton'
-require 'set'
-
module ActiveRecord
# Observer classes respond to lifecycle callbacks to implement trigger-like
# behavior outside the original class. This is a great way to reduce the
@@ -88,11 +85,17 @@ module ActiveRecord
# singletons and that call instantiates and registers them.
#
class Observer < ActiveModel::Observer
+ extlib_inheritable_accessor(:observed_methods){ [] }
+
def initialize
super
observed_subclasses.each { |klass| add_observer!(klass) }
end
+ def self.method_added(method)
+ observed_methods << method if ActiveRecord::Callbacks::CALLBACKS.include?(method.to_sym)
+ end
+
protected
def observed_subclasses
observed_classes.sum([]) { |klass| klass.send(:subclasses) }
@@ -100,8 +103,15 @@ module ActiveRecord
def add_observer!(klass)
super
- if respond_to?(:after_find) && !klass.method_defined?(:after_find)
- klass.class_eval 'def after_find() end'
+
+ # Check if a notifier callback was already added to the given class. If
+ # it was not, add it.
+ self.observed_methods.each do |method|
+ callback = :"_notify_observers_for_#{method}"
+ if (klass.instance_methods & [callback, callback.to_s]).empty?
+ klass.class_eval "def #{callback}; notify_observers(:#{method}); end"
+ klass.send(method, callback)
+ end
end
end
end
diff --git a/activerecord/lib/active_record/validations.rb b/activerecord/lib/active_record/validations.rb
index 5fc41cf054..ab79b520a2 100644
--- a/activerecord/lib/active_record/validations.rb
+++ b/activerecord/lib/active_record/validations.rb
@@ -110,8 +110,6 @@ module ActiveRecord
included do
alias_method_chain :save, :validation
alias_method_chain :save!, :validation
-
- define_callbacks :validate_on_create, :validate_on_update
end
module ClassMethods
@@ -127,17 +125,6 @@ module ActiveRecord
object
end
end
-
- def validation_method(on)
- case on
- when :create
- :validate_on_create
- when :update
- :validate_on_update
- else
- :validate
- end
- end
end
module InstanceMethods
@@ -165,27 +152,15 @@ module ActiveRecord
def valid?
errors.clear
- run_callbacks(:validate)
+ @_on_validate = new_record? ? :create : :update
+ _run_validate_callbacks
- if respond_to?(:validate)
- ActiveSupport::Deprecation.warn("Base#validate has been deprecated, please use Base.validate :method instead")
- validate
- end
+ deprecated_callback_method(:validate)
if new_record?
- run_callbacks(:validate_on_create)
-
- if respond_to?(:validate_on_create)
- ActiveSupport::Deprecation.warn("Base#validate_on_create has been deprecated, please use Base.validate_on_create :method instead")
- validate_on_create
- end
+ deprecated_callback_method(:validate_on_create)
else
- run_callbacks(:validate_on_update)
-
- if respond_to?(:validate_on_update)
- ActiveSupport::Deprecation.warn("Base#validate_on_update has been deprecated, please use Base.validate_on_update :method instead")
- validate_on_update
- end
+ deprecated_callback_method(:validate_on_update)
end
errors.empty?