24

I have a Rails 3 app which JSON encodes objects in order to store them in a Redis key/value store.

When I retrieve the objects, I'm trying to decode the JSON and instantiate them from the data like so:

def decode(json)
  self.new(ActiveSupport::JSON.decode(json)["#{self.name.downcase}"])
end

The problem is that doing this involves mass assignment which is disallowed (for good reason I'm told!) for attributes I haven't given attr_writer ability to.

Is there a way I can bypass the mass assignment protection just for this operation only?

David Tuite
  • 22,258
  • 25
  • 106
  • 176

3 Answers3

86

assign_attributes with without_protection: true seems less intrusive:

user = User.new
user.assign_attributes({ :name => 'Josh', :is_admin => true }, :without_protection => true)
user.name       # => "Josh"
user.is_admin?  # => true

@tovodeverett mentioned in the comment you can also use it with new, like this in 1 line

user = User.new({ :name => 'Josh', :is_admin => true }, :without_protection => true)
kizzx2
  • 18,775
  • 14
  • 76
  • 83
  • 7
    This should be the answer, IMO! – Jon Cairns Nov 29 '12 at 09:20
  • 4
    Also, it appears that one can pass `without_protection: true` in the initial call to `new` (i.e. `user = User.new({name: 'Josh', is_admin: true}, without_protection: true)` – tovodeverett May 01 '13 at 22:39
  • 3
    Also works when passed directly into the `create` action: `User.create({name: 'Josh', is_admin: true}, without_protection: true)` – Michael Jul 03 '13 at 15:21
7

EDIT: kizzx2's Answer is a much better solution.

Kind of a hack, but...

self.new do |n|
  n.send "attributes=", JSON.decode( json )["#{self.name.downcase}"], false
end

This invokes attributes= passing false for the guard_protected_attributes parameter which will skip any mass assignment checks.

Community
  • 1
  • 1
Paul Alexander
  • 31,970
  • 14
  • 96
  • 151
  • Thanks that worked. I had to fix two small typos though, the dot after "self" and the open curly brace for self.name.downcase. – David Tuite Apr 14 '11 at 07:37
4

You can create a user also in this way which is not doing the mass assignment.

User.create do |user|
  user.name = "Josh"
end

You may want to put this into a method.

new_user(name)
  User.create do |user|
    user.name = name
  end
end
Mika
  • 1,419
  • 18
  • 37