From bb44e5a80ac62f87a40122a213038a746f2bc289 Mon Sep 17 00:00:00 2001 From: Jon Leighton Date: Wed, 14 Dec 2011 14:49:02 +0000 Subject: [PATCH] Use a separate module for 'external' attribute methods. --- .../lib/active_record/attribute_methods.rb | 14 +++++++++++ .../attribute_methods/primary_key.rb | 6 ++--- .../active_record/attribute_methods/read.rb | 23 ++++++++----------- 3 files changed, 26 insertions(+), 17 deletions(-) diff --git a/activerecord/lib/active_record/attribute_methods.rb b/activerecord/lib/active_record/attribute_methods.rb index 3c9fafb1ca..47f625039b 100644 --- a/activerecord/lib/active_record/attribute_methods.rb +++ b/activerecord/lib/active_record/attribute_methods.rb @@ -33,6 +33,20 @@ def generated_attribute_methods @generated_attribute_methods ||= (base_class == self ? super : base_class.generated_attribute_methods) end + def generated_external_attribute_methods + @generated_external_attribute_methods ||= begin + if base_class == self + # We will define the methods as instance methods, but will call them as singleton + # methods. This allows us to use method_defined? to check if the method exists, + # which is fast and won't give any false positives from the ancestors (because + # there are no ancestors). + Module.new { extend self } + else + base_class.generated_external_attribute_methods + end + end + end + def undefine_attribute_methods if base_class == self super diff --git a/activerecord/lib/active_record/attribute_methods/primary_key.rb b/activerecord/lib/active_record/attribute_methods/primary_key.rb index 746682393e..e3c7344f59 100644 --- a/activerecord/lib/active_record/attribute_methods/primary_key.rb +++ b/activerecord/lib/active_record/attribute_methods/primary_key.rb @@ -30,10 +30,10 @@ def define_method_attribute(attr_name) if attr_name == primary_key && attr_name != 'id' generated_attribute_methods.send(:alias_method, :id, primary_key) - generated_attribute_methods.module_eval <<-CODE, __FILE__, __LINE__ - def self.attribute_id(v, attributes, attributes_cache, attr_name) + generated_external_attribute_methods.module_eval <<-CODE, __FILE__, __LINE__ + def id(v, attributes, attributes_cache, attr_name) attr_name = '#{primary_key}' - send(:'attribute_#{attr_name}', attributes[attr_name], attributes, attributes_cache, attr_name) + send(attr_name, attributes[attr_name], attributes, attributes_cache, attr_name) end CODE end diff --git a/activerecord/lib/active_record/attribute_methods/read.rb b/activerecord/lib/active_record/attribute_methods/read.rb index 25c998e857..0ca627638a 100644 --- a/activerecord/lib/active_record/attribute_methods/read.rb +++ b/activerecord/lib/active_record/attribute_methods/read.rb @@ -31,10 +31,8 @@ def cache_attribute?(attr_name) def undefine_attribute_methods if base_class == self - generated_attribute_methods.module_eval do - public_methods(false).each do |m| - singleton_class.send(:undef_method, m) if m.to_s =~ /^attribute_/ - end + generated_external_attribute_methods.module_eval do + instance_methods.each { |m| undef_method(m) } end end @@ -52,22 +50,20 @@ def undefine_attribute_methods # rename it to what we want. def define_method_attribute(attr_name) cast_code = attribute_cast_code(attr_name) - internal = internal_attribute_access_code(attr_name, cast_code) - external = external_attribute_access_code(attr_name, cast_code) generated_attribute_methods.module_eval <<-STR, __FILE__, __LINE__ + 1 def __temp__ - #{internal} + #{internal_attribute_access_code(attr_name, cast_code)} end alias_method '#{attr_name}', :__temp__ undef_method :__temp__ STR - generated_attribute_methods.singleton_class.module_eval <<-STR, __FILE__, __LINE__ + 1 + generated_external_attribute_methods.module_eval <<-STR, __FILE__, __LINE__ + 1 def __temp__(v, attributes, attributes_cache, attr_name) - #{external} + #{external_attribute_access_code(attr_name, cast_code)} end - alias_method 'attribute_#{attr_name}', :__temp__ + alias_method '#{attr_name}', :__temp__ undef_method :__temp__ STR end @@ -110,12 +106,11 @@ def attribute_cast_code(attr_name) # "2004-12-12" in a data column is cast to a date object, like Date.new(2004, 12, 12)). def read_attribute(attr_name) attr_name = attr_name.to_s - accessor = "attribute_#{attr_name}" - methods = self.class.generated_attribute_methods + methods = self.class.generated_external_attribute_methods - if methods.respond_to?(accessor) + if methods.method_defined?(attr_name) if @attributes.has_key?(attr_name) || attr_name == 'id' - methods.send(accessor, @attributes[attr_name], @attributes, @attributes_cache, attr_name) + methods.send(attr_name, @attributes[attr_name], @attributes, @attributes_cache, attr_name) end elsif !self.class.attribute_methods_generated? # If we haven't generated the caster methods yet, do that and -- GitLab