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