attr vs attr_accessor
One difference is that attr_accessor
and friends are clearer, and the optional boolean argument to attr
is now deprecated. Other than that, and the fact that attr
has no documentation, there's no real difference.
Difference between attr_accessor and attr_accessible
attr_accessor
is a Ruby method that makes a getter and a setter. attr_accessible
is a Rails method that allows you to pass in values to a mass assignment: new(attrs)
or update_attributes(attrs)
.
Here's a mass assignment:
Order.new({ :type => 'Corn', :quantity => 6 })
You can imagine that the order might also have a discount code, say :price_off
. If you don't tag :price_off
as attr_accessible
you stop malicious code from being able to do like so:
Order.new({ :type => 'Corn', :quantity => 6, :price_off => 30 })
Even if your form doesn't have a field for :price_off
, if it's in your model it's available by default. This means a crafted POST could still set it. Using attr_accessible
white lists those things that can be mass assigned.
Difference between @instance_variable and attr_accessor
An instance variable is not visible outside the object it is in; but when you create an attr_accessor
, it creates an instance variable and also makes it visible (and editable) outside the object.
Example with instance variable (not attr_accessor
)
class MyClass
def initialize
@greeting = "hello"
end
end
m = MyClass.new
m.greeting #results in the following error:
#NoMethodError: undefined method `greeting' for #<MyClass:0x007f9e5109c058 @greeting="hello">
Example using attr_accessor
:
class MyClass
attr_accessor :greeting
def initialize
@greeting = "hello"
end
end
m2 = MyClass.new
m2.greeting = "bonjour" # <-- set the @greeting variable from outside the object
m2.greeting #=> "bonjour" <-- didn't blow up as attr_accessor makes the variable accessible from outside the object
Hope that makes it clear.
Why use Ruby's attr_accessor, attr_reader and attr_writer?
You may use the different accessors to communicate your intent to someone reading your code, and make it easier to write classes which will work correctly no matter how their public API is called.
class Person
attr_accessor :age
...
end
Here, I can see that I may both read and write the age.
class Person
attr_reader :age
...
end
Here, I can see that I may only read the age. Imagine that it is set by the constructor of this class and after that remains constant. If there were a mutator (writer) for age and the class were written assuming that age, once set, does not change, then a bug could result from code calling that mutator.
But what is happening behind the scenes?
If you write:
attr_writer :age
That gets translated into:
def age=(value)
@age = value
end
If you write:
attr_reader :age
That gets translated into:
def age
@age
end
If you write:
attr_accessor :age
That gets translated into:
def age=(value)
@age = value
end
def age
@age
end
Knowing that, here's another way to think about it: If you did not have the attr_... helpers, and had to write the accessors yourself, would you write any more accessors than your class needed? For example, if age only needed to be read, would you also write a method allowing it to be written?
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.
Strange behave of ruby attr_accessor
Variable Hoisting effect is used in many languages. For Ruby it is described in the official documentation:
The local variable is created when the parser encounters the assignment, not when the assignment occurs
So, get_answer
method creates local variable b
regardless to value of c
. And assigns local variable b
to nil
while creating. Then get_answer
returns local variable b
which is always nil
.
Correct method:
def get_answer
c ? self.b = nil : b
end
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 attr_accessor vs. getter/setter benchmark: why is accessor faster?
Without deeply understanding the differences, I can at least say that attr_accessor
(and attr_reader
and attr_writer
) are implemented in C, as you can see by toggling the source on that page. Your methods will be implemented in Ruby, and Ruby methods have more call overhead than native C functions.
Here's an article explaining why Ruby method dispatch tends to be slow.
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
Related Topics
Create Array of N Items Based on Integer Value
How to Get a Stack Trace Object in Ruby
Class Method VS Constant in Ruby/Rails
Rspec: How to Stub an Instance Method Called by Constructor
Ruby - Determining Method Origins
How to Access (Devise) Current_User in a Rspec Feature Test
In Ruby What Does "=>" Mean and How Does It Work
Iconv Deprecation Warning with Ruby 1.9.3
Rails Migration Changing Column to Use Postgres Arrays
How to Switch to an Older Version of Rails
Rails How to Update a Column After Saving