Bypassing mass assignment for update_attributes
March 14, 2009I've been following this excellent post by M. Hartl and this post by E. Chapweske banishing mass assignment from one of my Rails applications due to launch soon.
I'm following Chapweske's approach of blocking mass assignment by default in all models, by putting this line in an initializer:
ActiveRecord::Base.send(:attr_accessible, nil)
This had the expected side effect of breaking several zillion tests, because tests frequently use things like Model.build() and Model.create!() to generate on-demand fixtures during testing. Hartl has a great bit of code that creates unsafe_build() and unsafe_create() methods in ActiveRecord. You can use these methods instead of build() and create() to function as expected in your tests.
This works great, except that I also use the mass-assignment method update_attributes! in my tests and specs frequently, particularly when I want to spec the effect a change on one model has on an associated models' methods. So, I expanded on Hartl's helper code a bit, to give myself the necessary methods. In case it helps anyone else:
/lib/initializers/unsafe_build_and_create.rb
class ActiveRecord::Base
# Build and create records unsafely, bypassing attr_accessible.
# These methods are especially useful in tests and in the console.
def self.unsafe_build(attrs)
record = new
record.unsafe_attributes = attrs
record
end
def self.unsafe_create(attrs)
record = unsafe_build(attrs)
record.save
record
end
def self.unsafe_create!(attrs)
unsafe_build(attrs).save!
end
def unsafe_update_attributes!(attrs)
self.unsafe_attributes = attrs
self.save!
end
def unsafe_update_attributes(attrs)
self.unsafe_attributes = attrs
self.save
end
def unsafe_attributes=(attrs)
attrs.each do |k, v|
send("#{k}=", v)
end
end
end
