Difference Between @Instance_Variable and Attr_Accessor

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.

attr_accessor vs attr_reader & instance variables

attr_accessor defines getter and setter.attr_reader defines only getter.

class Car
attr_reader :engine
def initialize(engine)
@engine = engine
end
end

Car.instance_methods(false) # => [:engine]

With the above code you defined only def engine; @engine ;end.

class Car
attr_accessor :engine
def initialize(engine)
self.engine = engine
end
end

Car.instance_methods(false) # => [:engine, :engine=]

With the above code you defined only def engine; @engine ;end and def engine=(engine) ;@engine = engine ;end.

What does it mean that `attr_accessor` / `attr_reader` create an instance variable?

That's a bug/misleading wording in documentation. The attr_reader/attr_accessor themselves don't create any variables. How can they? They work outside of class instance lifecycle. And even read access don't make the instance variables come to life. Only write access creates them.

class Foo
attr_accessor :bar
end

foo = Foo.new
foo.instance_variables # => []
foo.bar # try read ivar
foo.instance_variables # => [], nope, not yet
foo.bar = 2 # write ivar
foo.instance_variables # => [:@bar], there it is

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.

Attr_accessor on class variables

attr_accessor defines accessor methods for an instance. If you want class level auto-generated accessors you could use it on the metaclass

class Parent
@things = []

class << self
attr_accessor :things
end
end

Parent.things #=> []
Parent.things << :car
Parent.things #=> [:car]

but note that this creates a class level instance variable not a class variable. This is likely what you want anyway, as class variables behave differently than you might expect when dealing w/ inheritance. See "Class and Instance Variables In Ruby".

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?

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.

What's the difference between defining class instance variable in a class and a method. Why?

In your first case, the variable is neither a class variable(which should have started with @@, nor an instance variable. It is simply a local variable not available outside the current scope, not even within the instance methods.

The second is an instance variable.

class Document
attr_accessor :var1, :var2
@var1 = 1
puts @var1 + 2

def initialize
@var2 = 4
puts @var2**2
#puts @var1 + 6
end

end

1.9.2p0 :208 > class Document
1.9.2p0 :209?> attr_accessor :var1, :var2
1.9.2p0 :210?> @var1 = 1
1.9.2p0 :211?> puts @var1 + 2
1.9.2p0 :212?>
1.9.2p0 :213 > def initialize
1.9.2p0 :214?> @var2 = 4
1.9.2p0 :215?> puts @var2**2
1.9.2p0 :216?> #puts @var1 + 6
1.9.2p0 :217 > end
1.9.2p0 :218?>
1.9.2p0 :219 > end
3
=> nil
1.9.2p0 :220 > d = Document.new
16
=> #<Document:0x1a2f0c @var2=4>

The @var1 + 6 inside the instance method gives an error.



Related Topics



Leave a reply



Submit