Stefano Crocco
3/24/2007 10:28:00 PM
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