Permalink
Browse files

eliminate alias_method_chain from ActiveRecord

  • Loading branch information...
1 parent 636ffa1 commit d916c62cfc7c59ab6411407a05b946d3dd7535e9 @wycats wycats committed May 8, 2010
@@ -61,13 +61,15 @@ module ActiveRecord
autoload :Base
autoload :Callbacks
+ autoload :CounterCache
autoload :DynamicFinderMatch
autoload :DynamicScopeMatch
autoload :Migration
autoload :Migrator, 'active_record/migration'
autoload :NamedScope
autoload :NestedAttributes
autoload :Observer
+ autoload :Persistence
autoload :QueryCache
autoload :Reflection
autoload :Schema
@@ -253,6 +253,7 @@ def writer_method(name, class_name, mapping, allow_nil, converter)
raise ArgumentError, 'Converter must be a symbol denoting the converter method to call or a Proc to be invoked.'
end
end
+
mapping.each { |pair| self[pair.first] = part.send(pair.last) }
instance_variable_set("@#{name}", part.freeze)
end
@@ -1304,14 +1304,14 @@ def has_and_belongs_to_many(association_id, options = {}, &extension)
# Don't use a before_destroy callback since users' before_destroy
# callbacks will be executed after the association is wiped out.
- old_method = "destroy_without_habtm_shim_for_#{reflection.name}"
- class_eval <<-end_eval unless method_defined?(old_method)
- alias_method :#{old_method}, :destroy_without_callbacks # alias_method :destroy_without_habtm_shim_for_posts, :destroy_without_callbacks
- def destroy_without_callbacks # def destroy_without_callbacks
- #{reflection.name}.clear # posts.clear
- #{old_method} # destroy_without_habtm_shim_for_posts
- end # end
- end_eval
+ include Module.new {
+ class_eval <<-RUBY, __FILE__, __LINE__ + 1
+ def destroy # def destroy
+ super # super
+ #{reflection.name}.clear # posts.clear
+ end # end
+ RUBY
+ }
add_association_callbacks(reflection.name, options)
end
@@ -18,10 +18,19 @@ def define_attribute_methods
def instance_method_already_implemented?(method_name)
method_name = method_name.to_s
@_defined_class_methods ||= ancestors.first(ancestors.index(ActiveRecord::Base)).sum([]) { |m| m.public_instance_methods(false) | m.private_instance_methods(false) | m.protected_instance_methods(false) }.map {|m| m.to_s }.to_set
- @@_defined_activerecord_methods ||= (ActiveRecord::Base.public_instance_methods(false) | ActiveRecord::Base.private_instance_methods(false) | ActiveRecord::Base.protected_instance_methods(false)).map{|m| m.to_s }.to_set
+ @@_defined_activerecord_methods ||= defined_activerecord_methods
raise DangerousAttributeError, "#{method_name} is defined by ActiveRecord" if @@_defined_activerecord_methods.include?(method_name)
@_defined_class_methods.include?(method_name)
end
+
+ def defined_activerecord_methods
+ active_record = ActiveRecord::Base
+ super_klass = ActiveRecord::Base.superclass
+ methods = active_record.public_instance_methods - super_klass.public_instance_methods
+ methods += active_record.private_instance_methods - super_klass.private_instance_methods
+ methods += active_record.protected_instance_methods - super_klass.protected_instance_methods
+ methods.map {|m| m.to_s }.to_set
+ end
end
def method_missing(method_id, *args, &block)
@@ -5,91 +5,91 @@ module AttributeMethods
module Dirty
extend ActiveSupport::Concern
include ActiveModel::Dirty
+ include AttributeMethods::Write
included do
- alias_method_chain :save, :dirty
- alias_method_chain :save!, :dirty
- alias_method_chain :update, :dirty
- alias_method_chain :reload, :dirty
+ if self < Timestamp
+ raise "You cannot include Dirty after Timestamp"
+ end
superclass_delegating_accessor :partial_updates
self.partial_updates = true
end
# Attempts to +save+ the record and clears changed attributes if successful.
- def save_with_dirty(*args) #:nodoc:
- if status = save_without_dirty(*args)
+ def save(*) #:nodoc:
+ if status = super
@previously_changed = changes
@changed_attributes.clear
end
status
end
# Attempts to <tt>save!</tt> the record and clears changed attributes if successful.
- def save_with_dirty!(*args) #:nodoc:
- save_without_dirty!(*args).tap do
+ def save!(*) #:nodoc:
+ super.tap do
@previously_changed = changes
@changed_attributes.clear
end
end
# <tt>reload</tt> the record and clears changed attributes.
- def reload_with_dirty(*args) #:nodoc:
- reload_without_dirty(*args).tap do
+ def reload(*) #:nodoc:
+ super.tap do
@previously_changed.clear
@changed_attributes.clear
end
end
- private
- # Wrap write_attribute to remember original attribute value.
- def write_attribute(attr, value)
- attr = attr.to_s
+ private
+ # Wrap write_attribute to remember original attribute value.
+ def write_attribute(attr, value)
+ attr = attr.to_s
- # The attribute already has an unsaved change.
- if attribute_changed?(attr)
- old = @changed_attributes[attr]
- @changed_attributes.delete(attr) unless field_changed?(attr, old, value)
- else
- old = clone_attribute_value(:read_attribute, attr)
- # Save Time objects as TimeWithZone if time_zone_aware_attributes == true
- old = old.in_time_zone if clone_with_time_zone_conversion_attribute?(attr, old)
- @changed_attributes[attr] = old if field_changed?(attr, old, value)
- end
+ # The attribute already has an unsaved change.
+ if attribute_changed?(attr)
+ old = @changed_attributes[attr]
+ @changed_attributes.delete(attr) unless field_changed?(attr, old, value)
+ else
+ old = clone_attribute_value(:read_attribute, attr)
+ # Save Time objects as TimeWithZone if time_zone_aware_attributes == true
+ old = old.in_time_zone if clone_with_time_zone_conversion_attribute?(attr, old)
+ @changed_attributes[attr] = old if field_changed?(attr, old, value)
+ end
- # Carry on.
- super(attr, value)
+ # Carry on.
+ super(attr, value)
+ end
+
+ def update(*)
+ if partial_updates?
+ # Serialized attributes should always be written in case they've been
+ # changed in place.
+ super(changed | (attributes.keys & self.class.serialized_attributes.keys))
+ else
+ super
end
+ end
- def update_with_dirty
- if partial_updates?
- # Serialized attributes should always be written in case they've been
- # changed in place.
- update_without_dirty(changed | (attributes.keys & self.class.serialized_attributes.keys))
+ def field_changed?(attr, old, value)
+ if column = column_for_attribute(attr)
+ if column.number? && column.null && (old.nil? || old == 0) && value.blank?
+ # For nullable numeric columns, NULL gets stored in database for blank (i.e. '') values.
+ # Hence we don't record it as a change if the value changes from nil to ''.
+ # If an old value of 0 is set to '' we want this to get changed to nil as otherwise it'll
+ # be typecast back to 0 (''.to_i => 0)
+ value = nil
else
- update_without_dirty
+ value = column.type_cast(value)
end
end
- def field_changed?(attr, old, value)
- if column = column_for_attribute(attr)
- if column.number? && column.null && (old.nil? || old == 0) && value.blank?
- # For nullable numeric columns, NULL gets stored in database for blank (i.e. '') values.
- # Hence we don't record it as a change if the value changes from nil to ''.
- # If an old value of 0 is set to '' we want this to get changed to nil as otherwise it'll
- # be typecast back to 0 (''.to_i => 0)
- value = nil
- else
- value = column.type_cast(value)
- end
- end
-
- old != value
- end
+ old != value
+ end
- def clone_with_time_zone_conversion_attribute?(attr, old)
- old.class.name == "Time" && time_zone_aware_attributes && !skip_time_zone_conversion_for_attributes.include?(attr.to_sym)
- end
+ def clone_with_time_zone_conversion_attribute?(attr, old)
+ old.class.name == "Time" && time_zone_aware_attributes && !skip_time_zone_conversion_for_attributes.include?(attr.to_sym)
+ end
end
end
end
@@ -130,8 +130,6 @@ module AutosaveAssociation
ASSOCIATION_TYPES = %w{ has_one belongs_to has_many has_and_belongs_to_many }
included do
- alias_method_chain :reload, :autosave_associations
-
ASSOCIATION_TYPES.each do |type|
send("valid_keys_for_#{type}_association") << :autosave
end
@@ -196,9 +194,9 @@ def add_autosave_association_callbacks(reflection)
end
# Reloads the attributes of the object as usual and removes a mark for destruction.
- def reload_with_autosave_associations(options = nil)
+ def reload(options = nil)
@marked_for_destruction = false
- reload_without_autosave_associations(options)
+ super
end
# Marks this record to be destroyed as part of the parents save transaction.
Oops, something went wrong.

1 comment on commit d916c62

Contributor

iain commented on d916c62 May 9, 2010

gratz on your personal victory ;)

Please sign in to comment.