Forum: Ruby class methods and instance variables in ActiveRecord::Base

Announcement (2017-05-07): www.ruby-forum.com is now read-only since I unfortunately do not have the time to support and maintain the forum any more. Please see rubyonrails.org/community and ruby-lang.org/en/community for other Rails- und Ruby-related community platforms.
Ce445e5f50b87873d82eefa318f86a18?d=identicon&s=25 zig (Guest)
on 2007-03-24 22:51
(Received via mailing list)
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
Ad7805c9fcc1f13efc6ed11251a6c4d2?d=identicon&s=25 Alex Young (regularfry)
on 2007-03-24 23:07
(Received via mailing list)
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?  :-)
C40020a47c6b625af6422b5b1302abaf?d=identicon&s=25 Stefano Crocco (crocco)
on 2007-03-24 23:29
(Received via mailing list)
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
Ce8b03e5750097942c58e12b46724312?d=identicon&s=25 Giles Bowkett (Guest)
on 2007-03-24 23:57
(Received via mailing list)
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.
Ce445e5f50b87873d82eefa318f86a18?d=identicon&s=25 zig (Guest)
on 2007-03-25 03:41
(Received via mailing list)
On Mar 24, 3:56 pm, "Giles Bowkett" <gil...@gmail.com> 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?
3afd3e5e05dc9310c89aa5762cc8dd1d?d=identicon&s=25 Timothy Hunter (Guest)
on 2007-03-25 03:53
(Received via mailing list)
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.
Ce445e5f50b87873d82eefa318f86a18?d=identicon&s=25 zig (Guest)
on 2007-03-25 04:16
(Received via mailing list)
On Mar 24, 6:52 pm, Timothy Hunter <TimHun...@nc.rr.com> 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?
C40020a47c6b625af6422b5b1302abaf?d=identicon&s=25 Stefano Crocco (crocco)
on 2007-03-25 09:55
(Received via mailing list)
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
Ce445e5f50b87873d82eefa318f86a18?d=identicon&s=25 zig (Guest)
on 2007-03-25 20:46
(Received via mailing list)
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!
753dcb78b3a3651127665da4bed3c782?d=identicon&s=25 Brian Candler (Guest)
on 2007-03-25 20:50
(Received via mailing list)
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.
E7559e558ececa67c40f452483b9ac8c?d=identicon&s=25 Gary Wright (Guest)
on 2007-03-26 02:17
(Received via mailing list)
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 Wright
Ce445e5f50b87873d82eefa318f86a18?d=identicon&s=25 zig (Guest)
on 2007-03-30 07:22
(Received via mailing list)
On Mar 25, 11:42 am, "zig" <ziggur...@gmail.com> 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.
E7559e558ececa67c40f452483b9ac8c?d=identicon&s=25 Gary Wright (Guest)
on 2007-03-30 08:14
(Received via mailing list)
On Mar 30, 2007, at 1:20 AM, zig wrote:

> On Mar 25, 11:42 am, "zig" <ziggur...@gmail.com> 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"]
This topic is locked and can not be replied to.