class methods and instance variables in ActiveRecord::Base

Discussion in 'Ruby' started by zig, Mar 24, 2007.

  1. zig

    zig Guest

    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
     
    zig, Mar 24, 2007
    #1
    1. Advertising

  2. zig

    Alex Young Guest

    zig wrote:
    > 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? :)

    --
    Alex
     
    Alex Young, Mar 24, 2007
    #2
    1. Advertising

  3. 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
     
    Stefano Crocco, Mar 24, 2007
    #3
  4. 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.

    --
    Giles Bowkett
    http://www.gilesgoatboy.org
    http://gilesbowkett.blogspot.com
    http://giles.tumblr.com/
     
    Giles Bowkett, Mar 24, 2007
    #4
  5. zig

    zig Guest

    On Mar 24, 3:56 pm, "Giles Bowkett" <> 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?
     
    zig, Mar 25, 2007
    #5
  6. zig wrote:
    > On Mar 24, 3:56 pm, "Giles Bowkett" <> 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?
    >
    >
    >

    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.
     
    Timothy Hunter, Mar 25, 2007
    #6
  7. zig

    zig Guest

    On Mar 24, 6:52 pm, Timothy Hunter <> 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?
     
    zig, Mar 25, 2007
    #7
  8. Alle domenica 25 marzo 2007, zig ha scritto:
    > On Mar 24, 6:52 pm, Timothy Hunter <> 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?


    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
     
    Stefano Crocco, Mar 25, 2007
    #8
  9. zig

    zig Guest

    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!
     
    zig, Mar 25, 2007
    #9
  10. 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.
     
    Brian Candler, Mar 25, 2007
    #10
  11. zig

    Gary Wright Guest

    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
     
    Gary Wright, Mar 26, 2007
    #11
  12. zig

    zig Guest

    On Mar 25, 11:42 am, "zig" <> 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.
     
    zig, Mar 30, 2007
    #12
  13. zig

    Gary Wright Guest

    On Mar 30, 2007, at 1:20 AM, zig wrote:

    > On Mar 25, 11:42 am, "zig" <> 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"]
     
    Gary Wright, Mar 30, 2007
    #13
    1. Advertising

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

It takes just 2 minutes to sign up (and it's free!). Just click the sign up button to choose a username and then you can ask your own questions on the forum.
Similar Threads
  1. Sridhar R
    Replies:
    14
    Views:
    1,407
    =?iso-8859-1?Q?Fran=E7ois?= Pinard
    Feb 10, 2004
  2. John M. Gabriele
    Replies:
    18
    Views:
    1,156
    Steven Bethard
    Feb 18, 2005
  3. Gerry Sutton
    Replies:
    1
    Views:
    542
    Peter Otten
    Apr 16, 2005
  4. Sandra-24
    Replies:
    18
    Views:
    1,868
    Steve Holden
    Apr 29, 2006
  5. Kenneth McDonald
    Replies:
    5
    Views:
    321
    Kenneth McDonald
    Sep 26, 2008
Loading...

Share This Page