Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

What is the value of self in a Rails model and why aren't obvious instance methods available?

I have a custom accessor method in my rails 3.1.6 app that assigns a value to an attribute, even if the value is not present.The my_attr attribute is a serialized Hash which should be merged with the given value unless a blank value is specified, in which case it will set the current value to a blank value. (There are added checks to make sure the values are what they should be, but are removed for brevity, as they are not part of my question.) My setter is defined as:

def my_attr=(new_val)
  cur_val = read_attribute(:my_attr)  #store current value

  #make sure we are working with a hash, and reset value if a blank value is given
  write_attribute(:my_attr, {}) if (new_val.nil? || new_val.blank? || cur_val.blank?)

  #merge value with new 
  if cur_val.blank?
    write_attribute(:my_attr, new_val)
  else
    write_attribute(:my_attr,cur_val.deep_merge(new_val))
  end
  read_attribute(:my_attr)
end

This code works well as-is, but not when I use self.write_attribute(). I then get the following error:

NoMethodError:
       private method `write_attribute' called for #<MyModel:0x00000004f10528>

My questions are thus: It seems more logical to have write_attribute available to an instance, so why is it only available to the class and not to the instance? Is there something lacking in my fundamental knowledge of self in Ruby or Rails (or both)?

like image 584
Skaman Sam Avatar asked Jul 12 '12 21:07

Skaman Sam


People also ask

Why do we use self in rails?

Because we don't have to use the class name for each method definition, making our code easier to change if we change the class. It also makes the code less noisy & better to read. That's why we do def self.

What does self mean in rails?

self is a reserved keyword in Ruby that always refers to the current object and classes are also objects, but the object self refers to frequently changes based on the situation or context. So if you're in an instance, self refers to the instance. If you're in a class, self refers to that class.

Why do we use self in Ruby?

self is a special variable that points to the object that "owns" the currently executing code. Ruby uses self everwhere: For instance variables: @myvar. For method and constant lookup.

What does self refer to in an instance method body?

the method self refers to the object it belongs to. Class definitions are objects too.


1 Answers

Inside of an instance method self is that instance. However, when you call a method with explicit receiver, then ruby's visibility control kicks in and forbids invocation of the private method.

class Foo
  def implicit
    self # => #<Foo:0x007fc019091060>
    private_method
  end

  def explicit
    self # => #<Foo:0x007fc019091060>
    self.private_method
  end

  private
  def private_method
    "bar"
  end
end

f = Foo.new
f.implicit # => "bar"
f.explicit # => 
# ~> -:9:in `explicit': private method `private_method' called for #<Foo:0x007fc019091060> (NoMethodError)
# ~>    from -:25:in `<main>'

If you want to call private methods, use implicit receiver or send.

self.send :private_method

Update

An excerpt from a ruby metaprogramming book.

What private Really Means

Now that you know about self, you can cast a new light over Ruby’s private keyword. Private methods are governed by a single simple rule: you cannot call a private method with an explicit receiver. In other words, every time you call a private method, it must be on the implicit receiver—self. Let’s see a corner case:

class C
  def public_method
    self.private_method 
  end
  private
  def private_method; end
end
C.new.public_method

⇒ NoMethodError: private method ‘private_method' called [...]

You can make this code work by removing the self keyword.

This contrived example shows that private methods come from two rules working together: first, you need an explicit receiver to call a method on an object that is not yourself, and second, private methods can be called only with an implicit receiver. Put these two rules together, and you’ll see that you can only call a private method on yourself. You can call this the “private rule.”

You could find Ruby’s private methods perplexing—especially if you come from Java or C#, where private behaves very differently. When you’re in doubt, just go back to the private rule, and everything will make sense. Can object x call a private method on object y if the two objects share the same class? The answer is no, because no matter which class you belong to, you still need an explicit receiver to call another object’s method. Can you call a private method that you inherited from a superclass? The answer is yes, because you don’t need an explicit receiver to call inherited methods on yourself.

like image 109
Sergio Tulentsev Avatar answered Nov 15 '22 07:11

Sergio Tulentsev