Defining a Class Accessor

  • Thread starter James Edward Gray II
  • Start date
J

James Edward Gray II

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 :eek:ne, :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
 
M

Martin Ankerl

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

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 :eek:ne
end

class B
cattr_accessor :eek:ne
end


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



martinus
 
P

Patrick Hurley

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 :eek:ne, :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

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
 
F

Florian Gross

James said:
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.
 
R

Robert Klemme

James Edward Gray II said:
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 :eek:ne, :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
=> []=> ["@@one", "@@two"]

Although:=> [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.
 
J

James Edward Gray II

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

Makes sense. Thank you.

James Edward Gray II
 
J

James Edward Gray II

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

?> Accessor.class_variables
=> []=> ["@@one", "@@two"]

Although:=> [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
 
N

Navindra Umanee

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

Could you explain why they would cause trouble?

Thanks,
Navin.
 
F

Florian Gross

Navindra said:
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...)
 
N

Navindra Umanee

Florian Gross said:
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.
 
D

David A. Black

Hi --

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.


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" :)
It's that inheritance thing, at least in large part, that causes
people to find class variables awkward.


David
 
F

Florian Gross

Navindra said:
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.
 

Ask a Question

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

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Members online

Forum statistics

Threads
473,982
Messages
2,570,189
Members
46,734
Latest member
manin

Latest Threads

Top