diff options
-rw-r--r-- | activerecord/lib/active_record/attribute_methods/read.rb | 46 | ||||
-rw-r--r-- | activerecord/test/cases/base_test.rb | 2 |
2 files changed, 23 insertions, 25 deletions
diff --git a/activerecord/lib/active_record/attribute_methods/read.rb b/activerecord/lib/active_record/attribute_methods/read.rb index 245abeda65..ab2b7936f3 100644 --- a/activerecord/lib/active_record/attribute_methods/read.rb +++ b/activerecord/lib/active_record/attribute_methods/read.rb @@ -42,38 +42,34 @@ module ActiveRecord end protected - # Where possible, generate the method by evalling a string, as this will result in - # faster accesses because it avoids the block eval and then string eval incurred - # by the second branch. + # We want to generate the methods via module_eval rather than define_method, + # because define_method is slower on dispatch and uses more memory (because it + # creates a closure). # - # The second, slower, branch is necessary to support instances where the database - # returns columns with extra stuff in (like 'my_column(omg)'). + # But sometimes the database might return columns with characters that are not + # allowed in normal method names (like 'my_column(omg)'. So to work around this + # we first define with the __temp__ identifier, and then use alias method to + # 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) - if attr_name =~ ActiveModel::AttributeMethods::NAME_COMPILABLE_REGEXP - generated_attribute_methods.module_eval <<-STR, __FILE__, __LINE__ - def #{attr_name} - #{internal} - end - - def self.attribute_#{attr_name}(v, attributes, attributes_cache, attr_name) - #{external} - end - STR - else - generated_attribute_methods.module_eval do - define_method(attr_name) do - eval(internal) - end - - singleton_class.send(:define_method, "attribute_#{attr_name}") do |v, attributes, attributes_cache, attr_name| - eval(external) - end + generated_attribute_methods.module_eval <<-STR, __FILE__, __LINE__ + def __temp__ + #{internal} end - end + alias_method '#{attr_name}', :__temp__ + undef_method :__temp__ + STR + + generated_attribute_methods.singleton_class.module_eval <<-STR, __FILE__, __LINE__ + def __temp__(v, attributes, attributes_cache, attr_name) + #{external} + end + alias_method 'attribute_#{attr_name}', :__temp__ + undef_method :__temp__ + STR end private diff --git a/activerecord/test/cases/base_test.rb b/activerecord/test/cases/base_test.rb index b1a429c869..d846eb03aa 100644 --- a/activerecord/test/cases/base_test.rb +++ b/activerecord/test/cases/base_test.rb @@ -570,10 +570,12 @@ class BasicsTest < ActiveRecord::TestCase weird = Weird.create('a$b' => 'value') weird.reload assert_equal 'value', weird.send('a$b') + assert_equal 'value', weird.read_attribute('a$b') weird.update_column('a$b', 'value2') weird.reload assert_equal 'value2', weird.send('a$b') + assert_equal 'value2', weird.read_attribute('a$b') end def test_multiparameter_attributes_on_date |