LRBlog

Logical Reality Design: Web Design and Software Development

Bypassing mass assignment for update_attributes

March 14, 2009

I'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

  1. Michael Hartl Said,

    What’s funny is that I recently added the same methods independently. You (though not the other readers of this blog) can verify this by doing a git pull and a git log on the private project we’ve been working on together; you’ll see that I added unsafe_update_attributes(!) on February 12, and the code is virtually identical to yours. (The only difference, apart from the method order, is a missing ‘save’ in your unsafe_update_attributes, which I think you need to add, since unsafe_update_attributes should change the attributes in the database as well as the ones in memory.)

  2. Evan Said,

    Oh, indeed – yours is the correct way, and I’ve modified the post.

    I had been lazy and not looked up update_attributes in the API, simply assuming that the difference between update_attributes and update_attributes! was the same as the difference between toggle and toggle! … i.e. that without the bang, it updates the attribs in memory but does not save the record.

    Instead, of course, it’s the same as the difference between save and save!; the difference is not whether it saves, but whether it returns an error if the record is invalid and the save fails.

    This is a case where a little more consistency from the API would be lovely… :-)

Add A Comment