Permalink
Browse files

fix respond_to? for non selected column

fixes #4208

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.
  • Loading branch information...
1 parent 08c6df8 commit 66001f360661fefad89d9e271b9ff75a86b4b886 Neeraj Singh committed Apr 14, 2013
View
@@ -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*
@@ -163,8 +163,20 @@ def attribute_missing(match, *args, &block) # :nodoc:
# 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+.
@@ -185,6 +185,11 @@ def test_relation_merging_with_merged_joins
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

0 comments on commit 66001f3

Please sign in to comment.