aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--activerecord/CHANGELOG.md23
-rw-r--r--activerecord/lib/active_record/attribute_methods.rb14
-rw-r--r--activerecord/test/cases/relation_test.rb5
3 files changed, 41 insertions, 1 deletions
diff --git a/activerecord/CHANGELOG.md b/activerecord/CHANGELOG.md
index 278da322f2..e46ca3bd07 100644
--- a/activerecord/CHANGELOG.md
+++ b/activerecord/CHANGELOG.md
@@ -1,5 +1,28 @@
## Rails 4.0.0 (unreleased) ##
+* If a query selects only a few columns and gives custom names to
+ those columns then `respond_to?` was returning true for the non
+ selected columns. However calling those non selected columns
+ raises exception.
+
+ post = Post.select("'title' as post_title").first
+
+ In the above case when `post.body` is invoked then an exception is
+ raised since `body` attribute is not selected. Howevere `respond_to?`
+ did not behave correctly.
+
+ pos.respond_to?(:body) #=> true
+
+ Reason was that Active Record calls `super` to pass the call to
+ Active Model and all the columns are defined on Active Model.
+
+ Fix is to actually check if the data returned from the db contains
+ the data for column in question.
+
+ Fixes #4208.
+
+ *Neeraj Singh*
+
* Run `rake migrate:down` & `rake migrate:up` in transaction if database supports.
*Alexander Bondarev*
diff --git a/activerecord/lib/active_record/attribute_methods.rb b/activerecord/lib/active_record/attribute_methods.rb
index 22405c5d74..d0c51b77c2 100644
--- a/activerecord/lib/active_record/attribute_methods.rb
+++ b/activerecord/lib/active_record/attribute_methods.rb
@@ -163,8 +163,20 @@ module ActiveRecord
# person.respond_to('age?') # => true
# person.respond_to(:nothing) # => false
def respond_to?(name, include_private = false)
+ name = name.to_s
self.class.define_attribute_methods unless self.class.attribute_methods_generated?
- super
+ result = super
+
+ # If the result is false then it means this method is not supported by ActiveModel too
+ return false unless result
+
+ # If the result is true then check for the select case.
+ # For queries selecting a subset of columns, return false for unselected columns.
+ if @attributes.present? && self.class.column_names.include?(name)
+ return has_attribute?(name)
+ end
+
+ return true
end
# Returns +true+ if the given attribute is in the attributes hash, otherwise +false+.
diff --git a/activerecord/test/cases/relation_test.rb b/activerecord/test/cases/relation_test.rb
index 34ecdb3cc9..06723b9d7f 100644
--- a/activerecord/test/cases/relation_test.rb
+++ b/activerecord/test/cases/relation_test.rb
@@ -185,6 +185,11 @@ module ActiveRecord
assert_equal 3, authors(:david).posts.merge(posts_with_special_comments_with_ratings).count.length
end
+ def test_respond_to_for_non_selected_element
+ post = Post.select("'title' as post_title").first
+ assert !post.respond_to?(:body), "post should not respond_to?(:body) since invoking it raises exception"
+ end
+
end
class RelationMutationTest < ActiveSupport::TestCase