Class methods and instance variables in ActiveRecord::Base

I’m still learning my way around ruby and I seem to be confused about
class methods/variables versus instance methods/variables. My example
comes from Ruby on Rails, but I don’t think my confusion is rails-
specific.

In ActiveRecord::Base, there is a method columns. I initially thought
this was an instance method since it wasn’t declared as
ActiveRecord::Base.columns, which is the required syntax for class
methods. However someone explained to me that you can enclose several
method declarations in a class << self block which makes them all
class methods.

OK, so columns is a class method. But then in its source, it makes
reference to the instance variable @columns. In other OOP languages
that I’m familiar with, it’s forbidden to access instance variables
within a class method (if the class hasn’t been instantiated, then the
instance variables can’t have been initialized). I’ve investigated
the situation in ruby, and apparently it is this: it’s not illegal to
reference an instance variable from within a class method, but the
instance variable will be nil.

In the rails scaffolding, the code calls Modelname.columns. So it’s
calling the class method, and the class has not been instantiated.

Help de-confuse me! How does this class method get away with
referencing an instance variable and where exactly does this instance
variable get initialized? (I can’t find any occurrences of @columns
outside of other class methods, so it’s apparently not being
initialized anywhere in this class?)

Thanks in advance,
zig

zig wrote:

class methods.

OK, so columns is a class method. But then in its source, it makes
reference to the instance variable @columns. In other OOP languages
that I’m familiar with, it’s forbidden to access instance variables
within a class method (if the class hasn’t been instantiated, then the
instance variables can’t have been initialized). I’ve investigated
the situation in ruby, and apparently it is this: it’s not illegal to
reference an instance variable from within a class method, but the
instance variable will be nil.
Not quite. The difference between Ruby and other languages is that the
class itself is an object
. This means that when you refer to an
instance variable in a class method, you’re referring to an instance
variable of the class object itself, not the instance variable of any
instances of that class.

In the rails scaffolding, the code calls Modelname.columns. So it’s
calling the class method, and the class has not been instantiated.

Help de-confuse me! How does this class method get away with
referencing an instance variable and where exactly does this instance
variable get initialized? (I can’t find any occurrences of @columns
outside of other class methods, so it’s apparently not being
initialized anywhere in this class?)
In this case, it’s an instance variable on the Modelname class which, as
I rather confusingly explain above, is also an object - it’s an instance
of class Class.

Clear as mud? :slight_smile:

The usual Java thing of “this is a class, this thing is an instance of
that class” places classes themselves outside of the set of usable
objects in the language.

Ruby does not do this. Because a Ruby class is an instance of Class,
it’s as open to the programmer as anything else in Ruby.

(This model is adopted directly from Smalltalk.)

What this means is that classes in Ruby aren’t these ideal Platonic
objects that sit outside of the world the programmer interacts with.
They’re something you can rewrite in real time. There’s no real
difference between a class and an object.

Here’s the code you’re looking at. It’s inside a class << self block
which adds it to any class which inherits from ActiveRecord::Base.

  # Returns an array of column objects for the table associated

with this class.
def columns
unless @columns
@columns = connection.columns(table_name, “#{name} Columns”)
@columns.each {|column| column.primary = column.name ==
primary_key}
end
@columns
end

The answer to your question is that the class which inherits is opened
up, and this method is defined within it. Not the instances – the
instances are untouched. This opens up the Class object itself. Then,
obviously, it sets an instance variable. At that point, what it’s
doing is setting an instance variable of the Class object.

What’s actually happening here is pretty simple. You’re opening up
another object and assigning an instance variable to it. That’s all.
The other object just happens to be a Class object.

Alle sabato 24 marzo 2007, zig ha scritto:

class methods.
In the rails scaffolding, the code calls Modelname.columns. So it’s
calling the class method, and the class has not been instantiated.

Help de-confuse me! How does this class method get away with
referencing an instance variable and where exactly does this instance
variable get initialized? (I can’t find any occurrences of @columns
outside of other class methods, so it’s apparently not being
initialized anywhere in this class?)

Thanks in advance,
zig

I don’t know rails at all, so I may be completely wrong. Classes (in
this case
the class ActiveRecord::Base) are themselves instances of the class
Class
(try writing ActiveRecord::Base.is_a?(Class): it returns true), so they
can
have instance variables, too (here I’m not referring to instance
variables of
instances of the class, i.e of instances of ActiveRecord::Base, but to
instance variables of the class ActiveRecord::Base itself). Instance
variables of a class can be created whenever self is the class, usually
in
the definition of the class or in a class method. Instance variables of
the
class can used in class methods of that class, which actually are simply
singleton instance methods of the class.

To make this explaination a bit more clear, here’s a simple example
(comments
refer to the line of code above them:

class MyClass

Here, we’re creating an instance of class Class and storing it in a

constant

called MyClass

def method_1

Here, we’re defining an instance method, i.e a method which can be

called

on instances of class MyClass (for example, on the object returned by

MyClass.new

@var=1

Here, we create an instance variable. Since self is an instance of

MyClass, this is an instance variable of an instance of MyClass

puts “This is method_1”
end

@cls_var=2

Now, self is MyClass, so @cls_var is an instance variable of the

class

itself. It can’t be called by instances of MyClass

def MyClass.cls_method_1

Here, we’re defining a class variable, i.e a singleton method of

MyClass.

(Note that the syntax is the same used for defining a singleton

method in

other circumstances)

puts “This is cls_method_1”
puts “@cls_var is #{@cls_var}”

Here, self is MyClass, so we can access its instance variables

end

class << self

This idiom is used to access an object’s singleton class (in this

case,

MyClass’s singleton class. From now until the corresponding end,

MyClass will behave as an instance of the current class

def cls_method_2

Here we’re defining an instance method. Since the instance of the

current

class is MyClass, this is a class method for MyClass

puts “This is MyClass.cls_method_2”
puts “@cls_var is #{@cls_var}”

Here, we show again that we can access the instance variable of the

class
end

end

Here, we exit the singleton class

def method_2

We define a new instance method for MyClass (this will be called on

instances of MyClass

puts “This is method_2”
puts “@cls_var is #{@cls_var.inspect}”

Here we can’t access instance variables of MyClass, and we see it

(inspect is used to display nil instead of an empty string)

end

end

Some tests on the above:

MyClass.cls_method_1
=> This is MyClass.cls_method_1
@cls_var is 2

MyClass.cls_method_2
=> This is MyClass.cls_method_2
@cls_var is 2

MyClass.new.method_1
=> This is method_1

MyClass.new.method_2
=> This is method_2
@cls_var is nil

I hope this helps (and doesn’t create additional confusion). Sorry for
the
length of the post.

Stefano

zig wrote:

A class variable is NOT the same as an instance variable of the class
object. Have you tried “calling” @@var1 with A.var1? It doesn’t work.
The attr_reader and attr_writer methods create methods that operate on
@var1. There is no method “var1” that operates on @@var1.

On Mar 24, 3:56 pm, “Giles B.” [email protected] wrote:

The answer to your question is that the class which inherits is opened
up, and this method is defined within it. Not the instances – the
instances are untouched. This opens up the Class object itself. Then,
obviously, it sets an instance variable. At that point, what it’s
doing is setting an instance variable of the Class object.

OK, I think I’m getting there.

Is an instance variable of the class object the same thing as a class
variable?

For example, consider

class A
@@var1
end

and

class A
class << self
attr_reader :var1
attr_writer :var1
end
end

In both cases I call that variable with A.var1. Are they the same?

Alle domenica 25 marzo 2007, zig ha scritto:

So how about this?
@var1 = 2
to my 2?
In the second case, you have set @var1 to 2 in the context of the
singleton
class of A, not in the context of A, so it becomes an instance variable
of
the singleton class of A, not an instance variable of A. var1, instead
is a
class method of A, so inside its definition, self is A. Ruby will look
for an
instance variable called @var1 defined for A, it won’t find it and will
create a new one set to nil. To make the code work, you only need to
move the
line @var1=2 above the line class << self. This way, @var1 will become
an
instance variable of A and everyting will work.

Regarding the difference between class variable and class instance
variables:
they have nothing in common. They can be accessed by instances of the
class
(and even created in instance methods) and are shared between a class
and its
descendents. A class instance variable is private to the class, and
can’t be
accessed by the outside unless you provide suitable methods. In other
threads on this list, it was suggested that it’s best to avoid class
variables and use class instance variables or constants instead (I don’t
know
whether this is true or not, I’m only reporting).

I hope this helps

Stefano

On Mar 24, 6:52 pm, Timothy H. [email protected] wrote:

A class variable is NOT the same as an instance variable of the class
object. Have you tried “calling” @@var1 with A.var1? It doesn’t work.
The attr_reader and attr_writer methods create methods that operate on
@var1. There is no method “var1” that operates on @@var1.

You’re right, that method isn’t defined in the first case, I’ve just
tried it. It also fails because class variables are required to be
initialized, which I didn’t do.

So how about this?

class A
@@var1 = 2
def A.var1
@@var1
end
end

class A
class << self
@var1 = 2
def var1
@var1
end
end
end

I tried this in irb as well, and again, they’re not the same, but I
can’t figure out why not. This time the first declaration gives
A.var1 => 2, while the second gives A.var1 => nil. But what happened
to my 2?

OK, I think I’ve got it now. Class methods of the class have access
to class variables, class instance variables, but not instance
variables. Instance methods of the class have access to class
variables, instance variables, but not class instance variables.
Class variables are accessible to both, but neither class instance
variables nor instance variables are. Therefore class instance
variables are not the same thing as class variables.

Thanks Stefano, and all the others who replied!

On Mon, Mar 26, 2007 at 03:45:10AM +0900, zig wrote:

OK, I think I’ve got it now. Class methods of the class have access
to class variables, class instance variables, but not instance
variables. Instance methods of the class have access to class
variables, instance variables, but not class instance variables.
Class variables are accessible to both, but neither class instance
variables nor instance variables are. Therefore class instance
variables are not the same thing as class variables.

Also: class variables are also accessible to subclasses.

Class instance variables belong only to one class. For them, a subclass
is a
completely different class object.

Regards,

Brian.

On Mar 25, 11:42 am, “zig” [email protected] wrote:

Therefore class instance
variables are not the same thing as class variables.

One thing we can say is that class instance methods are the same
thing as class methods, right? In fact, the term “class instance
method” doesn’t exist as a distinct concept, precisely because they
are the same.

I think this must have been the source of my confusion about class
instance variables versus class variables.

On Mar 30, 2007, at 1:20 AM, zig wrote:

On Mar 25, 11:42 am, “zig” [email protected] wrote:

Therefore class instance
variables are not the same thing as class variables.

One thing we can say is that class instance methods are the same
thing as class methods, right? In fact, the term “class instance
method” doesn’t exist as a distinct concept, precisely because they
are the same.

I’m not sure that ‘class instance methods’ is a well-defined term.

I think what you are after is
instance methods of a singleton class of a class
are the same as
singleton methods of a class
are the same as
class singleton methods
are the same as
class methods

Via irb:

Array.singleton_methods
=> [“[]”]
Regexp.singleton_methods
=> [“escape”, “quote”, “last_match”, “compile”, “union”]
Time.singleton_methods
=> [“rfc822”, “gm”, “zone_offset”, “parse”, “iso8601”, “rfc2822”,
“utc”, “xmlschema”, “today”, “at”, “mktime”, “now”, “_load”,
“httpdate”, “local”, “times”]

But ‘Class instance methods’ is well-defined:

Class.instance_methods(false)
=> [“new”, “superclass”, “allocate”]

On Mar 25, 2007, at 2:45 PM, zig wrote:

OK, I think I’ve got it now. Class methods of the class have access
to class variables, class instance variables, but not instance
variables. Instance methods of the class have access to class
variables, instance variables, but not class instance variables.
Class variables are accessible to both, but neither class instance
variables nor instance variables are. Therefore class instance
variables are not the same thing as class variables.

It is also important to realize that class variables (’@@xyz’) are
resolved lexically. They are not resolved relative to ‘self’ but
instead relative to the surrounding lexical scope (top-level, file,
module blocks, class blocks).

This is very different than instance variables (’@xyz’) which are
always resolved relative to ‘self’.

class A
@@example = 1
puts @@example # 1, lexically associated with A

def self.example
@@example
end
end

puts @@example # error, lexically associated with top-level
# but not defined yet.

def A.top_level_example
@@example # top level @@example!!!
end

@@example = 2
puts A.top_level_example # 2, gets @@example relative to top-level
puts A.example # 1, gets @@example relative to A

Gary W.