views:

141

answers:

3

I have a rails model class

class Model <  ActiveRecord::Base

  has_many :object_collection


  def add_object(object)
      object_collection.push object // works
      @object_collection.push object // does not work
      self.object_collection.push object // works
  end
end

I was wondering if someone can please explain to me why the @ does not work yet self does i thought these two meant the same

cheers

+4  A: 

They are not the same. Consider the following Ruby code:

class Person
  attr_accessor :employer
end
john = Person.new
john.employer = "ACME"
john.employer # equals "ACME"

The method attr_accessor conveniently generates an attribute reader and writer for you (employer= and employer). You can use these methods to read and write an attribute, which is stored in the instance variable @employer.

Now, we can rewrite the above to the following, which is functionally identical to the code above:

class Person
  def employer=(new_employer)
    @works_for = new_employer
  end
  def employer
    @works_for
  end
end
john = Person.new
john.employer = "ACME"
john.employer # equals "ACME"

Now, the instance variable @employer is no longer used. We chose to write the accessors manually, and have the freedom to pick a different name for the instance variable. In this particular example, the name of the instance variable is different than the name of the attribute accessors. There is nothing that prevents you from doing that.

This is similar to how ActiveRecord stores its attributes internally. They are not stored in instance variables of the same name, that is why your push call to @object_collection does not work.

As you may understand, attribute readers and writers offer a certain abstraction that can hide the implementation details from you. Reading and writing instance variables directly in subclasses is therefore generally considered bad practice.

molf
+1  A: 

@foo identifies an instance variable called @foo. foo identified a method called foo. By default, instance variables in Ruby are private. It means you cannot access the value of an instance variable unless you have some public method that exposes the value.

Those methods are called setters and getters. By convenction, setter and getter have the same name of the instance variable, but this is not a requirement.

class MyClass

  def initialize
    @foo
  end

  def foo=(value)
    @foo = foo
  end
  def foo
    @foo
  end

  def an_other_foo=(value)
    @foo = foo
  end
  def an_other_foo
    @foo
  end

end

Though methods and instance variables can have similar names, thery are different elements. If this topic is not clear to you, you probably need to stop playing with Rails and go back studying how Ruby works.

In your specific case, object_collection doesn't exist as an instance variable because it's an association method.

Simone Carletti
+1  A: 

They do not mean the same thing. One is an instance variable, the other is a method.

The @foo means "the value of the instance variable foo", where as self.foo means "the value of a call to the method foo on myself".

It is typical for a method foo= to set the @foo instance variable, so I can see how someone new to the language might be confused. I'd encourage you to pick up a book on the ruby language. There's one specifically for people who have done some rails but never learned ruby proper. You often can hack rails without understanding the language or what these statements mean, but you'll be far less productive than someone who spends the small amount of time it takes to learn the ruby language itself.

As a general rule, use the self.foo form whenever you can, as this is less sensitive to changes in the classes definition.

Jason Watkins