rails/rails — pull request #8718 by jstirk (2013-01-03 11:48:49+00:00)
Title: Fix undefined method `to_i' introduced since 3.2.8

This commit fixes a bug introduced in 96a13fc7 which breaks behaviour of
integer fields in 3.2.8.

In 3.2.8, setting the value of an integer field to a non-integer (eg.
Array, Hash, etc.) would use the first value in the array :

# 3.2.8
p = Post.new
p.category_id = [ 1, 2 ]
p.category_id # => 1
p.category_id = { 1 => 2 }
p.category_id # => 1

In 3.2.9 and above, this will raise a NoMethodError :

# 3.2.9
p = Post.new
p.category_id = [ 1, 2 ]

NoMethodError: undefined method `to_i' for [1, 2]:Array

Whilst at first blush this appear to be sensible, it combines in bad
ways with scoping.

For example, it is common to use scopes to control access to data :

@collection = Posts.where(:category_id => [ 1,2 ])
@new_post = @collection.new

In 3.2.8, this would work as expected, creating a new Post object
(albeit with `@new_post.category_id = 1`). However, in 3.2.9 this will
cause the NoMethodError to be raised as above.

It is difficult to avoid triggering this error without descoping before
calling `.new`, breaking any apps running on 3.2.8 that rely on this
behaviour.

This patch deviates from 3.2.8 in that it does not retain the somewhat
spurious behaviour of setting the attribute to the first element or key.
Instead, it explicitly sets these invalid values to nil :

p = Post.new
p.category_id = [ 1, 2 ]
p.category_id # => nil

This also fixes the situation where a scope using an array will
"pollute" any newly instantiated records.

@new_post = @collection.new
@new_post.category_id # => nil

Finally, 3.2.8 exhibited a behaviour where setting an object to an
integer field caused it to be coerced to "1". This has not been
retained, as it is spurious and surprising in the same way that setting
Arrays and Hashes was :

c = Category.find(6)
p = Post.new

# 3.2.8
p.category_id = c
p.category_id # => 1

# This patch
p.category_id = c
p.category_id # => nil