From: Stefano Crocco Date: 2007-03-25T07:28:02+09:00 Subject: Re: class methods and instance variables in ActiveRecord::Base 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