Ruby code not in any method

General Ruby question: In Ruby, I often see code inside a class, but it is not part of the method. For example:

class DooDad attr_accessor :foo end 

or

 class Teacher < ActiveRecord::Base has_many :students end 

I think attr_accessor and has_many are methods called with arguments :foo or :students , respectively, right? If so, when similar instructions are followed. I tried this:

 class DooDad attr_accessor :foo puts "I happened!" @foo = 7 end 

This doesn't seem to be part of the new method:

 dd = DooDad.new dd.foo 

prints nil and never spits out any puts stuff

How exactly does it all work?

+8
ruby
source share
3 answers

Methods such as attr_accessor and has_many are often called "mock methods" because they look like ruby ​​keywords (mimic them), but in fact they, like you and others correctly pointed out, are method calls.

 dd = DooDad.new dd.foo 

prints nil and never spits out any puts things

How exactly does it all work?

When you are inside the class definition, the implicit recipient of all method calls and "variable definitions" is self , which is DooDad in your case.

So when you write

 class DooDad @foo = 1 end 

you actually define the instance variable on self , which turns out to be the class itself, since you are inside the definition of these classes. (and outside the definition of any other class, module, or method)

The attr_accessor method, on the other hand, generates metaprogramming access methods for an instance variable of objects that are created from the DooDad class .

Back to your example:

 class DooDad attr_accessor :foo puts "I happened!" @foo = 7 end 

Based on the foregoing, you should understand that you are dealing with two different @foo variables, one for instances of the DooDad class (i.e. DooDad.new ), the other (the one you created by writing @foo = 7 ) for the class itself DooDad!

When you call the new method on a class, you instantiate it.

 dd = DooDad.new #=> dd is now an object of class DooDad dd.foo #=> You just called the "getter" method for an instance variable foo of object dd, which was never defined before, that why it returning nil. 

The puts "I happened!" statement puts "I happened!" , like the other two, it is actually evaluated immediately after loading the class, but not when calling new on it. If you want you to describe the behavior (by doing things when calling new ), I suggest implementing the initialize() method for DooDad , which is called when calling new :

 class DooDad attr_accessor :foo def initialize() puts "I happened!" @foo = 7 end end dd = DooDad.new #=> outputs "I happened!" and sets dd.foo = 7 dd.foo #=> 7 

But why is @foo = 7 now setting the dd instance variable instead of DooDad ? When you define a method with the def keyword, you enter a new area (you pass the area shutter). self is no longer a class, but instead an instance of this class that you created with new , just like dd . Therefore, when you write @foo = 7 inside a method definition, you are talking about variables for an instance of the DooDad class, and not about the class itself.

This post is probably too long and may not even satisfy the quality of the answer, but I hope it was somewhat comprehensive.

+16
source share

Methods like has_many and attr_accessor are actually module or class methods. You are absolutely right in that they are ordinary methods called with arguments, like any other. When a method is called directly in the class (outside the method definition), it is called in the class itself.

Here are the docs for attr_accessor .

+7
source share

They are syntactic sugar embedded in the language, so this is not the same as your test. When you write (for example)

 class Foo attr_accessor :bar end 

This is actually a shortcut for ...

 class Foo def bar @bar end def bar=(value) @bar = value end end 
-2
source share

All Articles