ActiveRecord::Base#remove_attributes_protected_from_mass_assignment returns nil when both attr_protected and attr_accessible are used.
for example, with the table:
create_table :foos do |t|
t.column :bar, :integer
t.column :baz, :integer
end
and model
class Foo < ActiveRecord::Base
attr_protected :bar
attr_accessible :baz
end
anything that calls attributes=, which uses the result of remove_attributes_protected_from_mass_assignment, errors:
>> Foo.new(:bar => 0, :baz => 0)
NoMethodError: You have a nil object when you didn't expect it!
You might have expected an instance of Array.
The error occurred while evaluating nil.each
from ./vendor/rails/activerecord/lib/active_record/base.rb:1643:in `attributes='
from ./vendor/rails/activerecord/lib/active_record/base.rb:1482:in `initialize_without_callbacks'
from ./vendor/rails/activerecord/lib/active_record/callbacks.rb:225:in `initialize'
from (irb):13
the obvious patch updating the function:
def remove_attributes_protected_from_mass_assignment(attributes)
if self.class.accessible_attributes.nil? && self.class.protected_attributes.nil?
attributes.reject { |key, value| attributes_protected_by_default.include?(key.gsub(/\(.+/, "")) }
elsif self.class.protected_attributes.nil?
attributes.reject { |key, value| !self.class.accessible_attributes.include?(key.gsub(/\(.+/, "").intern) || attributes_protected_by_default.include?(key.gsub(/\(.+/, "")) }
elsif self.class.accessible_attributes.nil?
attributes.reject { |key, value| self.class.protected_attributes.include?(key.gsub(/\(.+/,"").intern) || attributes_protected_by_default.include?(key.gsub(/\(.+/, "")) }
else
attributes.reject { |key, value| self.class.protected_attributes.include?(key.gsub(/\(.+/,"").intern) || !self.class.accessible_attributes.include?(key.gsub(/\(.+/, "").intern) || attributes_protected_by_default.include?(key.gsub(/\(.+/, "")) }
end
end
with which for the above test case we get the expected
>> Foo.new(:bar => 0, :baz => 0)
=> #<Foo:0x24b1d44 @new_record=true, @attributes={"baz"=>0, "bar"=>nil}>