Defining a Class Accessor

I was playing around with an idea in another thread and ran into a surprise. Can anyone explain the following to me?

irb(main):015:0> class Module
irb(main):016:1> def cattr_accessor( *symbols )
irb(main):017:2> symbols.each do |sym|
irb(main):018:3* eval "def self.#{sym}( ) @@#{sym} end
irb(main):019:3" def self.#{sym}=( value ) @@#{sym} = value end"
irb(main):020:3> end
irb(main):021:2> end
irb(main):022:1> end
=> nil
irb(main):023:0> class Accessor
irb(main):024:1> cattr_accessor :one, :two
irb(main):025:1> def self.fetch_one( )
irb(main):026:2> @@one
irb(main):027:2> end
irb(main):028:1> end
=> nil
irb(main):029:0> Accessor.one = "James"
=> "James"
irb(main):030:0> Accessor.two = "Gray"
=> "Gray"
irb(main):031:0> Accessor.one
=> "James"
irb(main):032:0> Accessor.two
=> "Gray"
irb(main):033:0> Accessor.fetch_one
NameError: uninitialized class variable @@one in Accessor
         from (irb):26:in `fetch_one'
         from (irb):33
         from (null):0

Everything works as expected, save fetch_one(). Why does it not return "James"?

Thanks.

James Edward Gray II

Everything works as expected, save fetch_one(). Why does it not return "James"?

The problem is that the class variables are defined for Module, and not for the class Accessor:

class Module
   def cattr_accessor( *symbols )
     symbols.each do |sym|
       eval "def self.#{sym}() @@#{sym} end
             def self.#{sym}=(value) @@#{sym} = value end"
     end
   end
end

class A
   cattr_accessor :one
end

class B
   cattr_accessor :one
end

A.one = 1; p A.one # prints 1
B.one = 42; p A.one # prints 42!

martinus

I have been playing with this myself, your @@one is in Module not in
Accessor -- try changing your eval to class_eval:

class Module
  def cattr_accessor( *symbols )
    symbols.each do |sym|
        self.class_eval <<EVAL
          def self.#{sym}()
            @@#{sym}
          end

          def self.#{sym}=( value )
            @@#{sym} = value
          end
EVAL
    end
  end
end

···

On Wed, 30 Mar 2005 23:53:25 +0900, James Edward Gray II <james@grayproductions.net> wrote:

I was playing around with an idea in another thread and ran into a
surprise. Can anyone explain the following to me?

irb(main):015:0> class Module
irb(main):016:1> def cattr_accessor( *symbols )
irb(main):017:2> symbols.each do |sym|
irb(main):018:3* eval "def self.#{sym}( ) @@#{sym} end
irb(main):019:3" def self.#{sym}=( value ) @@#{sym} =
value end"
irb(main):020:3> end
irb(main):021:2> end
irb(main):022:1> end
=> nil
irb(main):023:0> class Accessor
irb(main):024:1> cattr_accessor :one, :two
irb(main):025:1> def self.fetch_one( )
irb(main):026:2> @@one
irb(main):027:2> end
irb(main):028:1> end
=> nil
irb(main):029:0> Accessor.one = "James"
=> "James"
irb(main):030:0> Accessor.two = "Gray"
=> "Gray"
irb(main):031:0> Accessor.one
=> "James"
irb(main):032:0> Accessor.two
=> "Gray"
irb(main):033:0> Accessor.fetch_one
NameError: uninitialized class variable @@one in Accessor
         from (irb):26:in `fetch_one'
         from (irb):33
         from (null):0

Everything works as expected, save fetch_one(). Why does it not return
"James"?

Thanks.

James Edward Gray II

James Edward Gray II wrote:

I was playing around with an idea in another thread and ran into a surprise. Can anyone explain the following to me?

irb(main):015:0> class Module
irb(main):016:1> def cattr_accessor( *symbols )
irb(main):017:2> symbols.each do |sym|
irb(main):018:3* eval "def self.#{sym}( ) @@#{sym} end
irb(main):019:3" def self.#{sym}=( value ) @@#{sym} = value end"
irb(main):020:3> end
irb(main):021:2> end
irb(main):022:1> end

Do you really need to use @@variables? From my experience they tend to cause trouble.

"James Edward Gray II" <james@grayproductions.net> schrieb im Newsbeitrag news:07549561ed849e497e65fdc423a92c88@grayproductions.net...

I was playing around with an idea in another thread and ran into a surprise. Can anyone explain the following to me?

irb(main):015:0> class Module
irb(main):016:1> def cattr_accessor( *symbols )
irb(main):017:2> symbols.each do |sym|
irb(main):018:3* eval "def self.#{sym}( ) @@#{sym} end
irb(main):019:3" def self.#{sym}=( value ) @@#{sym} = value end"
irb(main):020:3> end
irb(main):021:2> end
irb(main):022:1> end
=> nil
irb(main):023:0> class Accessor
irb(main):024:1> cattr_accessor :one, :two
irb(main):025:1> def self.fetch_one( )
irb(main):026:2> @@one
irb(main):027:2> end
irb(main):028:1> end
=> nil
irb(main):029:0> Accessor.one = "James"
=> "James"
irb(main):030:0> Accessor.two = "Gray"
=> "Gray"
irb(main):031:0> Accessor.one
=> "James"
irb(main):032:0> Accessor.two
=> "Gray"
irb(main):033:0> Accessor.fetch_one
NameError: uninitialized class variable @@one in Accessor
        from (irb):26:in `fetch_one'
        from (irb):33
        from (null):0

Everything works as expected, save fetch_one(). Why does it not return "James"?

I'm a bit stunned, too. Maybe this is a hint:

?> Accessor.class_variables
=>

Module.class_variables

=> ["@@one", "@@two"]

Although:

Accessor.class.ancestors

=> [Class, Module, Object, Kernel]

So, since Accessor is an instance of Class, both should be accessible IMO. Weired...

For me it's one more reason to never use class variables. They are simply too awkward.

Kind regards

    robert

PS: But I'm curios, too, why this does not work.

Makes sense. Thank you.

James Edward Gray II

···

On Mar 30, 2005, at 9:20 AM, Patrick Hurley wrote:

I have been playing with this myself, your @@one is in Module not in
Accessor -- try changing your eval to class_eval:

class Module
  def cattr_accessor( *symbols )
    symbols.each do |sym|
        self.class_eval <<EVAL
          def self.#{sym}()
            @@#{sym}
          end

          def self.#{sym}=( value )
            @@#{sym} = value
          end
EVAL
    end
  end
end

I'm a bit stunned, too. Maybe this is a hint:

?> Accessor.class_variables
=>

Module.class_variables

=> ["@@one", "@@two"]

Although:

Accessor.class.ancestors

=> [Class, Module, Object, Kernel]

Right, because I put the code in Module. It makes sense, when you let it sink in.

So, since Accessor is an instance of Class, both should be accessible IMO. Weired...

For me it's one more reason to never use class variables. They are simply too awkward.

Kind regards

   robert

PS: But I'm curios, too, why this does not work.

Just switching to class_eval(), to make sure the code is executed in the right context fixes the issue.

James

···

On Mar 30, 2005, at 9:44 AM, Robert Klemme wrote:

Could you explain why they would cause trouble?

Thanks,
Navin.

···

Florian Gross <flgr@ccan.de> wrote:

Do you really need to use @@variables? From my experience they tend to
cause trouble.

Not a problem, I feel like less of a ruby newbie today :slight_smile:

···

On Thu, 31 Mar 2005 02:13:59 +0900, James Edward Gray II <james@grayproductions.net> wrote:

Makes sense. Thank you.

Navindra Umanee wrote:

Do you really need to use @@variables? From my experience they tend to cause trouble.

Could you explain why they would cause trouble?

I thought the original post would answer that well enough already, but in my own Ruby experience I have so far being surprised when using them. Why should they be used when you can use far simpler constructs with clearer rules?

Generally they seem to be used most as a quick replacement for instance variables when something goes wrong without further questioning or actually finding the root of the problem. I've seen them used quite frequently in this case:

module StackMixIn
   def push(item)
     @items << item
   end

   def pop()
     @items.pop
   end

   # I'd like to initialize @items, but this will not work:
   @items =

   # If I just replace every '@items' with '@@items' it will magically
   # work. It's an obvious fix. It must be in Ruby exactly as a fix for
   # this kind of situation.
end

(And yes, I know that they are going to be simplified in Rite, but I'm not sure if that is going to make them completely non-surprising to most people...)

···

Florian Gross <flgr@ccan.de> wrote:

I thought the original post would answer that well enough already, but

Well, someone clarified what the problem was... he was assigning to
the class variables for Module instead of for the class itself. I
guess "self" is what is confusing.

in my own Ruby experience I have so far being surprised when using them.
Why should they be used when you can use far simpler constructs with
clearer rules?

Well, the idea is to have shared storage for all objects of a
particular class, right? Class variables fit exactly that
requirement. I'm not sure which simpler constructs you're referring
to in this context... but perhaps I'm still too much in the Java
mindset.

Cheers,
Navin.

···

Florian Gross <flgr@ccan.de> wrote:

Hi --

···

On Thu, 31 Mar 2005, Navindra Umanee wrote:

Florian Gross <flgr@ccan.de> wrote:

I thought the original post would answer that well enough already, but

Well, someone clarified what the problem was... he was assigning to
the class variables for Module instead of for the class itself. I
guess "self" is what is confusing.

in my own Ruby experience I have so far being surprised when using them.
Why should they be used when you can use far simpler constructs with
clearer rules?

Well, the idea is to have shared storage for all objects of a
particular class, right? Class variables fit exactly that
requirement.

As long as you add "... and possibly objects of another class" :slight_smile:
It's that inheritance thing, at least in large part, that causes
people to find class variables awkward.

David

--
David A. Black
dblack@wobblini.net

Navindra Umanee wrote:

Well, the idea is to have shared storage for all objects of a
particular class, right?

Which can be done like this, anyway:

class Foo
   class << self
     attr_accessor :space
   end

   def method()
     use self.class.space
   end
end

What I dislike about @@vars is that they behave differently depending on what context you are in -- method vs. class/module definition scope.