11

Assuming a Rails Model with persistent / non-persistent attributes, what is the best practice regarding referencing them? If you look at code publicly available, different patterns are used.

For instance, if you have an association from one model to another. What is the difference between using self.association_name and @association_name?. What is the preferable way?

Same as with non-persistent attributes defined with attr_accessor :attr in Models. You can reference them with both approaches, self.attr and @attr. What is the preferable way?

Scholle
  • 1,521
  • 2
  • 23
  • 44

2 Answers2

13

self.x/self.x=y are always method calls.

(self.x is just sugar for self.__send__(:x) and self.x = y is really just sugar for self.__send__(:x=, y))

@x, on the other hand, only refers to an instance variable.

Using @x will not work with AR associations as AR only defines x/x= (which are methods) for its magical operation. (AR essentially just "captures" intent access through these methods and routes through its own internal data structures which are unrelated to any similar-named instance variables.)

attr_accessor allows "accessing both ways" because and only because it uses the same-named instance variable as it's backing (it has to store the value somewhere). Consider that attr_accessor :x is equivalent to:

def x; @x; end
def x= (y); @x = y; end

Happy coding.

  • Regarding associations: Do I actually have to use self.* to reference them? What is the downside of skipping self.* in this context? – Scholle Jun 25 '11 at 02:22
  • 2
    @Scholle You must use `self.x = y` otherwise Ruby would think you wanted to assign the evaluation of `y` into the `x` local variable (which it will happily do, creating it if needed). However, assuming there is no variable `x` in scope, then `self.x` and `x` will both invoke the `x` method. Some people prefer to omit `self` in this case (I would). The big issue is *to be consistent*. –  Jun 25 '11 at 02:25
6

Most of the time, you would prefer to use attr instead of @attr. It's because the method attr often set the attributes if it doesn't exists.

For example this model order:

model Order
  have_many :items

  def total
    @total ||= items.collect(&:price).sum
  end

  def taxes
    @taxes ||= total * 0.10
  end
end

This model works vell, if I had used @total instead of total in the method taxes, the calcul will failed if I didn't call method total on this object before.

As @pst point out, @association_name does not work for association, you HAVE TO use the method association_name.

Also self.attr should be used only to set an attribute (calling self.attr=) when reading the value you are better to use attr, see this article : When to used self Rails model

Adrien Coquio
  • 4,870
  • 2
  • 24
  • 37
  • In the article you posted it says "never use local variables that are the same name as attributes". This basically means you have to check your code each time after you have introduced a new attribute whether you have a local variable with the same name? For instance, you have two methods def start_time ... end (getter) and def start_time ... end (setter). According to the article, it is bad coding to use a local variable called start_time (e.g. to compute a time)? – Scholle Jun 25 '11 at 12:18
  • The exact sens of this sentence is if you have an attribute, for example `@total`, you should not use a variable named `total` in a method of this class, this is because when you are using `total` as a variable it's confusing between using the variable `total` and call the accessor of `@total` (if existing). So you are right, you should not use a variable called `start_time` if you have a method `start_time` in your class but you may want to use the attributes `@start_time` to store the value **if needed** – Adrien Coquio Jun 25 '11 at 12:40