usage of attr_accessor in Rails
attr_accessor can be used for values you don't want to store in the database directly and that will only exist for the life of the object (e.g. passwords).
attr_reader can be used as one of several alternatives to doing something like this:
def instance_value
"my value"
end
Where to use attr_accessor in Rails?
If by "in rails" you mean "activerecord models", then your guess is correct: transient attributes.
Here's an example (a bit contrived):
class User < ActiveRecord::Base
attr_accessor :is_admin
validate_presence_of :name, :shipping_address, :billing_address, unless: :is_admin
end
Users created via regular flow (signup page) will refuse to get saved unless you provide all of the info. But there's a simpler flow for creating admins:
User.create(email: 'chris@example.com', is_admin: true)
# you do this in rails console or rake task, for example
Naturally, you should not accept this "private" flag from html forms (strong_params method in your controller).
Thinking a bit more about this example: you possibly want to store this flag in the database (is user admin or not). But I hope this should illustrate the idea anyway.
Why is attr_accessor necessary in Rails?
attr_accessor
is a core feature of Ruby and is used to generate instance variables with getter and setter methods. Its use is never required in basic Ruby (it's a convenience).
In the case of ActiveRecord models, getters and setters are already generated by ActiveRecord for your data columns. attr_accessor
is not needed or desirable.
If you have additional instance data you don't need to persist (i.e. it's not a database column), you could then use attr_accessor
to save yourself a few lines of code.
The similarly-named attr_accessible
— which is frequently seen in Rails code and confused with attr_accessor
— is a deprecated method of controlling mass assignment within ActiveRecord models. Rails 4 doesn't support it out of the box; it has been replaced by Strong Parameters, which allows more granular control.
Rails: attr_accessor association
You never want to use attr_accessor
in Rails models.*
attr_accessor
is used in plain old ruby objects to generate accessor methods for instance variables. If you use attr_accessor
in a Rails model you're clobbering the setter and getter that it creates from reading the database schema.
The result is that the attribute will not be persisted when you save. This is because the attrbute is not stored in the attributes hash and is not marked as dirty. It also won't be included in any of the methods that use the attributes api such as #attributes
or #to_json
.
It will also break the association as well.
class UserSubscription < ApplicationRecord
belongs_to :subscription_plan
belongs_to :new_plan, class_name: 'SubscriptionPlan'
end
Using attr_accessor and attr_accessible on the same field
Thanks everyone for quick answers!
Your answers combined gave me the pieces I needed to understand this puzzle, I think.
(In a related problem, I was getting a lot of nil errors like "Object doesn’t support #inspect", and "undefined method ‘keys’ for nil:NilClass". I managed to solve it now, by removing the att_accessor field altogether.)
By experimenting with this particular case, this is what I've found out:
Actually, the :name field won't be persisted to the database.
user = User.new(:name=>"somename")
Will only set the attribute on the object, but not persist the :name column to the database. Like the following 'rails console' output shows:
> user
=> <User id: nil, created_at: nil, updated_at: nil>
> user.save
=> true
> user
=> <User id:1, created_at: 2011-01-19 12:37:21, updated_at: 2011-01-19 12:37:21>
I assume this is because *the setter made by attr_accessor will override ActiveRecord's setter* (which takes care of the database persistence). You can still retrieve the value from the :name field from the object though, like this:
> user.name
=> "somename"
So, in conclusion, I've learnt that using attr_accessor on fields might lead to them not being persisted to the database. And while I thought attr_accessible describes fields in the database that should be accessible from the outside, it doesn't seem to make a difference in this case.
What is attr_accessor in Ruby?
Let's say you have a class Person
.
class Person
end
person = Person.new
person.name # => no method error
Obviously we never defined method name
. Let's do that.
class Person
def name
@name # simply returning an instance variable @name
end
end
person = Person.new
person.name # => nil
person.name = "Dennis" # => no method error
Aha, we can read the name, but that doesn't mean we can assign the name. Those are two different methods. The former is called reader and latter is called writer. We didn't create the writer yet so let's do that.
class Person
def name
@name
end
def name=(str)
@name = str
end
end
person = Person.new
person.name = 'Dennis'
person.name # => "Dennis"
Awesome. Now we can write and read instance variable @name
using reader and writer methods. Except, this is done so frequently, why waste time writing these methods every time? We can do it easier.
class Person
attr_reader :name
attr_writer :name
end
Even this can get repetitive. When you want both reader and writer just use accessor!
class Person
attr_accessor :name
end
person = Person.new
person.name = "Dennis"
person.name # => "Dennis"
Works the same way! And guess what: the instance variable @name
in our person object will be set just like when we did it manually, so you can use it in other methods.
class Person
attr_accessor :name
def greeting
"Hello #{@name}"
end
end
person = Person.new
person.name = "Dennis"
person.greeting # => "Hello Dennis"
That's it. In order to understand how attr_reader
, attr_writer
, and attr_accessor
methods actually generate methods for you, read other answers, books, ruby docs.
Rails - attr_accessor :- read the attribute value instead of the function overriding it
attr_accessor
defines a instance variable and a method to access it (read/write).
So the easy way is to write:
def open_ledger_account
create_ledger_account!(opening_balance: @opening_balance)
end
The read_attribute
would only work if opening_balance
was an attribute in the database on Customer
.
ruby on rail - problem with attr_accessor in model
attr_accessor is a class level method. You are trying to use it on an instance level.
Try cattr_accessor
instead. Although you dont need an attr_accessor
orcattr_accessor
to access attributes of anActiveRecord::Base
model that are defined in the schema.
====> EDIT <==== Corrected Answer Below
Disregard the advice above. It's not accurate. Im leaving it for reference. The reason you are getting the undefined method: name
error is because you have set an attr_accessor
on a model that already has a getter set dynamically via ActiveRecord::Base
.
Setting attr_accessor
on an attribute of an ActiveRecord::Base
models DB column causes some sort of conflict. When you remove the attr_accessor
method and instead add whats supposed to be its exact equivalent...
def name
name
end
and then try to call that you get an infinitely recursive looping of your call because the .name
getter method we defined above is calling itself and not accessing the ActiveRecord getter method that actually pulls data from your DB.
Im guessing that somehow active_record model classes will just throw you an undefined method
error rather than make the recursive call that ends in a stack level too deep
error.
However since you arent getting a stack too deep
error my next guess is that the attr_accessor
you defined overrides the dynamically created ActiveRecord::Base
getter while rails is still attempting to access the overridden method ( which dosnt exist anymore ). Possibly resulting in the undefined method 'name'
error.
But...
When you add your own getter method this way... then it works...
def name
super
end
this is because with super
your class is just inheriting the behavior of its dynamically defined ActiveRecord::Base
getter method. You can test this by adding...
def name
super
'Foo'
end
And when you call @model.name
you'll see that Foo
is returned. Remove the 'Foo' from the method and what you have being returned is a super
call to the dynamically created ActiveRecord::Base
getter method.
Im open to edits and input on this answer. This is my logical conclusion from testing in my console and doing some reading.
When to use attr_accessor
Also, to answer your question further about when to use attr_accessor
...
You would use this when you want to create and store information that is business-logic as well as model related, and only lasts for the duration of the request/instance life cycle.
For example raw passwords ( although I cringe at anyone manipulating raw passwords )
or something like...
@stock.current_price
where @stock
is a ActiveRecord::Base
object but .current_price
is calculated or retrieved from an API upon instantiation of the @stock
object because it doesn't make sense to persist and retrieve from the DB an ever changing value such as the price of a stock at any given point in time.
Related Topics
What Are the Restrictions For Method Names in Ruby
How to Configure Webrick to Use Ssl in Rails
Custom Authentication Strategy For Devise
Access Variables Programmatically by Name in Ruby
&:Views_Count' in 'Post.Published.Collect(&:Views_Count)'
When Should I Use Struct Vs. Openstruct
Ruby/Ruby on Rails Memory Leak Detection
Why Isn't the Eigenclass Equivalent to Self.Class, When It Looks So Similar
How to Parse a Yaml File in Ruby
Create Two-Dimensional Arrays and Access Sub-Arrays in Ruby
Ruby Objects and Json Serialization (Without Rails)
Does Ruby Perform Tail Call Optimization
Rails Model Has_Many With Multiple Foreign_Keys
Ruby 1.9: How to Properly Upcase & Downcase Multibyte Strings
"Bin/Rails: No Such File or Directory" W/ Ruby 2 & Rails 4 on Heroku
How Does the Magic Comment ( # Encoding: Utf-8 ) in Ruby Work