diff options
-rw-r--r-- | activerecord/lib/active_record/attribute_methods/read.rb | 30 | ||||
-rw-r--r-- | activerecord/test/cases/attribute_methods_test.rb | 11 | ||||
-rw-r--r-- | activerecord/test/cases/finder_test.rb | 3 |
3 files changed, 37 insertions, 7 deletions
diff --git a/activerecord/lib/active_record/attribute_methods/read.rb b/activerecord/lib/active_record/attribute_methods/read.rb index dd3f4699f7..a248eb3a7b 100644 --- a/activerecord/lib/active_record/attribute_methods/read.rb +++ b/activerecord/lib/active_record/attribute_methods/read.rb @@ -43,7 +43,7 @@ module ActiveRecord end if attr_name == primary_key && attr_name != "id" - define_read_method(:id, attr_name, columns_hash[attr_name]) + define_read_method('id', attr_name, columns_hash[attr_name]) end end @@ -59,7 +59,9 @@ module ActiveRecord end # Define an attribute reader method. Cope with nil column. - def define_read_method(symbol, attr_name, column) + # method_name is the same as attr_name except when a non-standard primary key is used, + # we still define #id as an accessor for the key + def define_read_method(method_name, attr_name, column) cast_code = column.type_cast_code('v') access_code = "(v=@attributes['#{attr_name}']) && #{cast_code}" @@ -71,9 +73,25 @@ module ActiveRecord access_code = "@attributes_cache['#{attr_name}'] ||= (#{access_code})" end - generated_attribute_methods.module_eval do - define_method("_#{attr_name}") { eval(access_code) } - alias_method(attr_name, "_#{attr_name}") + # 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. + # + # The second, slower, branch is necessary to support instances where the database + # returns columns with extra stuff in (like 'my_column(omg)'). + if method_name =~ /^[a-zA-Z_]\w*[!?=]?$/ + generated_attribute_methods.module_eval <<-STR, __FILE__, __LINE__ + def _#{method_name} + #{access_code} + end + + alias #{method_name} _#{method_name} + STR + else + generated_attribute_methods.module_eval do + define_method("_#{method_name}") { eval(access_code) } + alias_method(method_name, "_#{method_name}") + end end end end @@ -82,7 +100,7 @@ module ActiveRecord # "2004-12-12" in a data column is cast to a date object, like Date.new(2004, 12, 12)). def read_attribute(attr_name) if respond_to? "_#{attr_name}" - send "_#{attr_name}" + send "_#{attr_name}" if @attributes.has_key?(attr_name.to_s) else _read_attribute attr_name end diff --git a/activerecord/test/cases/attribute_methods_test.rb b/activerecord/test/cases/attribute_methods_test.rb index 899166d129..5074ae50ab 100644 --- a/activerecord/test/cases/attribute_methods_test.rb +++ b/activerecord/test/cases/attribute_methods_test.rb @@ -10,6 +10,7 @@ require 'models/company' require 'models/category' require 'models/reply' require 'models/contact' +require 'models/keyboard' class AttributeMethodsTest < ActiveRecord::TestCase fixtures :topics, :developers, :companies, :computers @@ -89,6 +90,16 @@ class AttributeMethodsTest < ActiveRecord::TestCase assert !topic.respond_to?(:nothingness) end + def test_respond_to_with_custom_primary_key + keyboard = Keyboard.create + assert_not_nil keyboard.key_number + assert_equal keyboard.key_number, keyboard.id + assert keyboard.respond_to?('key_number') + assert keyboard.respond_to?('_key_number') + assert keyboard.respond_to?('id') + assert keyboard.respond_to?('_id') + end + # Syck calls respond_to? before actually calling initialize def test_respond_to_with_allocated_object topic = Topic.allocate diff --git a/activerecord/test/cases/finder_test.rb b/activerecord/test/cases/finder_test.rb index 3c242667eb..655437318f 100644 --- a/activerecord/test/cases/finder_test.rb +++ b/activerecord/test/cases/finder_test.rb @@ -247,9 +247,10 @@ class FinderTest < ActiveRecord::TestCase def test_find_only_some_columns topic = Topic.find(1, :select => "author_name") assert_raise(ActiveModel::MissingAttributeError) {topic.title} + assert_nil topic.read_attribute("title") assert_equal "David", topic.author_name assert !topic.attribute_present?("title") - #assert !topic.respond_to?("title") + assert !topic.attribute_present?(:title) assert topic.attribute_present?("author_name") assert_respond_to topic, "author_name" end |