When to use self in Model?
Asked Answered
C

4

85

Question: when do I need to use self in my models in Rails?

I have a set method in one of my models.

class SomeData < ActiveRecord::Base
  def set_active_flag(val)
    self.active_flag = val
    self.save!
  end
end

When I do this, everything works fine. However, when I do this:

class SomeData < ActiveRecord::Base
  def set_active_flag(val)
    active_flag = val
    save!
  end
end

The active_flag value doesn't change, rather it fails silently. Can someone explain?

I can't find any duplicates, but if someone finds one that's fine too.

Corm answered 29/5, 2012 at 19:6 Comment(1)
possible duplicate of When to use 'self' in RubyHomoio
P
80

When you're doing an action on the instance that's calling the method, you use self.

With this code

class SocialData < ActiveRecord::Base
  def set_active_flag(val)
    active_flag = val
    save!
  end
end

You are defining a brand new scoped local variable called active_flag, setting it to the passed in value, it's not associated with anything, so it's promptly thrown away when the method ends like it never existed.

self.active_flag = val

However tells the instance to modify its own attribute called active_flag, instead of a brand new variable. That's why it works.

Plowshare answered 29/5, 2012 at 19:9 Comment(5)
"When you're doing an action on the instance that's calling the method, you use self." That's not quite accurate. Calling a (non-setter) method on the current object works just fine without self. So does setting instance variables (directly) - in fact you can't do that using self (without reflection anyway).Homoio
How odd that it wouldn't reference the instance variable... another difference from Java. Thanks for the answer.Corm
@varatis: Since variables aren't declared in ruby, the only way to create a variable is to assign something to it. Java can tell whether foo = bar is assigning to a local variable or an instance variable, because it can just look whether a local variable with that name has been declared. Ruby can't do that because there are no declarations. You could say "call the setter method if one exists - otherwise create a local variable", but then you run into trouble with method_missing. Also note that self.foo = bar calls a method named foo=, it doesn't (directly) set an instance variable.Homoio
@Corm Also, instance variables are @var_name.Opia
could anyone explain whyFloury
A
78

This happens because of scoping. When you're inside of a method and you try to set a new variable like this:

class SomeData < ActiveRecord::Base
  def set_active_flag(val)
    active_flag = val
  end
end

You are creating a brand new variable that lives inside of set_active_flag. As soon as that's done executing, it goes away, not altering self.active_flag (the actual instance variable) in any way.

HOWEVER (this was a source of confusion for me): when you try to read an instance variable in ruby, like this:

class SomeData < ActiveRecord::Base
  def whats_my_active_flag
    puts active_flag
  end
end

You'll actually get self.active_flag (the actual instance variable) returned.


Here's why:

Ruby will do what it can to avoid returning nil.

  1. It initially asks "does active_flag exist within the scope of whats_my_active_flag?
  2. It searches and realizes the answer is "nope", so it jumps up one level, to the instance of SomeData
  3. It asks the same thing again: "does active_flag exist within this scope?
  4. The answer is "yup" and so it says "I got something for ya" and it returns that!

However, if you define active_flag within the whats_my_active_flag, and then ask for it, it goes through the steps again:

  1. It asks "does active_flag exist within the scope of whats_my_active_flag?
  2. The answer is "yup", so it returns that value

In either case, it won't change the value of self.active_flag unless you explicitly tell it to.

An easy way to describe this behavior is "it doesn't want to disappoint you" and return nil -- so it does its best to find whatever it can.

At the same time, "it doesn't want to mess up data that you didn't intend to change" so it doesn't alter the instance variable itself.

Hope this helps!

Aporia answered 23/12, 2013 at 6:31 Comment(3)
Great explanation. I've been searching for an hour trying to figure out why I need to use self for setting values but can jump to different methods and use what was seemingly a local variable for reads without using selfCresol
Great explanation! I was also confused by the difference when you read or write the variableAmygdalin
What's the purpose of doing this? Is this a bad language design?Thorite
O
2

It's to make sure you're using the setter method and not scoping a new variable. It's a Ruby and AR usage detail that often trips people up (the other being the (mis-)use of an instance variable).

Note there's already update_attributes! although I understand the desire to abstract.

There's also toggle!, which might be even nicer, depending on your interface to the flag.

Opia answered 29/5, 2012 at 19:10 Comment(2)
I might be having a dangling modifier problem here... are you saying that the fact that "foo = bar" creates a local variable is an AR implementation detail?Homoio
@Homoio That wouldn't really make any sense, but my edit wasn't clear.Opia
P
0

When use active_flag = val ruby thought your are define a local variable, the best way is self.active_flag = val, if you got it, hope you know that send(:active_flag=, val) will works too.

Pericarp answered 30/10, 2017 at 15:30 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.