class methods and instance variables in ActiveRecord::Base

Z

zig

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
 
A

Alex Young

zig said:
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.
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? :)
 
S

Stefano Crocco

Alle sabato 24 marzo 2007, zig ha scritto:
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

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
 
G

Giles Bowkett

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.
 
Z

zig

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?
 
T

Timothy Hunter

zig said:
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?
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.
 
Z

zig

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?
 
S

Stefano Crocco

Alle domenica 25 marzo 2007, zig ha scritto:
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?

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
 
Z

zig

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!
 
B

Brian Candler

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.
 
G

Gary Wright

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 Wright
 
Z

zig

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.
 
G

Gary Wright

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:
=> ["new", "superclass", "allocate"]
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Members online

No members online now.

Forum statistics

Threads
473,755
Messages
2,569,536
Members
45,009
Latest member
GidgetGamb

Latest Threads

Top