Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Set non-database attribute for rails model without `attr_accessor`

In PHP, I can set an attribute (that is not a column in database) to a model. E.g.(PHP code),

$user = new User;
$user->flag = true;

But in rails, when I set any attribute that doesn't exist in database, it will throw an error undefined method flag. There is attr_accessor method, but what will happen if I need about ten temp attributes?

like image 962
user3783161 Avatar asked Dec 22 '15 11:12

user3783161


4 Answers

but what will happen if I need about ten temp attributes?

#app/models/user.rb
class User < ActiveRecord::Base
   attr_accessor :flag, :other_attribute, :other_attribute2, :etc...
end

attr_accessor creates "virtual" attributes in Rails -- they don't exist in the database, but are present in the model.

As with attributes from the db, attr_accessor just creates a set of setter & getter (instance) methods in your class, which you can call & interact with when the class is initialized:

#app/models/user.rb
class User < ActiveRecord::Base
   attr_accessor :flag

   # getter
   def flag
     @flag
   end

   # setter
   def flag=(val)
     @flag = val
   end
end
like image 188
Richard Peck Avatar answered Oct 17 '22 13:10

Richard Peck


This is expected because it's how ActiveRecord works by design. If you need to set arbitrary attributes, then you have to use a different kind of objects.

For example, Ruby provides a library called OpenStruct that allows you to create objects where you can assign arbitrary key/values. You may want to use such library and then convert the object into a corresponding ActiveRecord instance only if/when you need to save to the database.

Don't try to model ActiveRecord to behave as you just described because it was simply not designed to behave in that way. That would be a cargo culting error from your current PHP knowledge.

like image 45
Simone Carletti Avatar answered Oct 17 '22 13:10

Simone Carletti


As the guys explained, attr_accessor is just a quick setter and getter.

We can set our Model attr_accessor on record initializing to be a Ruby#Hash for example using ActiveRecord#After_initilize method so we get more flexibility on temporarily storing values (idea credit to this answer).

Something like:

class User < ActiveRecord::Base

  attr_accessor :vars

  after_initialize do |user|
    self.vars = Hash.new
  end

end

Now you could do:

user = User.new

#set
user.vars['flag'] = true

#get
user.vars['flag']
#> true
like image 3
Nimir Avatar answered Oct 17 '22 11:10

Nimir


All that attr_accessor does is add getter and setter methods which use an instance variable, eg this

attr_accessor :flag

will add these methods:

def flag
  @flag
end

def flag=(val)
  @flag = val
end

You can write these methods yourself if you want, and have them do something more interesting than just storing the value in an instance var, if you want.

like image 2
Max Williams Avatar answered Oct 17 '22 12:10

Max Williams