Re: Adding in class attribute with a Module


#1

Is it possible to add class attributes (cattr_accessor) using a module?
If so could you give an example?

There is no “Ruby Way” to do this b/c Ruby’s module methods aren’t
inherited by classes that include them. Though I’ve asked for a way
man times now!

It might seem easy to glaze by this, thinking nothing of
cattr_accessor, a seemingly esoteric method since it’s not included in
core Ruby, but you will find it in both Rail’s Active Support and Ruby
Facets. And in fact, you’ve brought up a nice example of how this play
out b/c cattr_accessor and it’s ilk really don’t do anything very
esoteric under the hood. I’m going to layout what cattr_accessor
essentially does* so all can see this for what it is and what happens
when you try to REUSE structures of this nature.

For a class, it is simply:

class C
def self.m ; @m ; end
def self.m=(x) ; @m=x ; end
def m ; self.class.m ; end
def m=(x) ; self.class.m=x ; end
end

Hence

C.m = 10
C.m #=> 10
c = C.new
c.m #=> 10
c.m = 20
c.m #=> 20
C.m #=> 20

While you can subclass C with this functionality readily, eg ‘class N
< C’ works as you’d expect, try MODULARIZING this functionality. The
cooresponding

module M
def self.m ; @m ; end
def self.m=(x) ; @m=x ; end
def m ; self.class.m ; end
def m=(x) ; self.class.m=x ; end
end

does not work at all. You can’t include it in a class, you can’t
extend a class with it, nor both --it completely bombs. Feel free to
maniputlate the code as well --shame as that is, while you can get
closer, nothing completely works --there’s alwasy something wrong.
Only ugly meta-programming hacks will give the sought behavior --and
even then their are corner-case complications.

T.

  • I’m using an instance var in this case rather than a class var for
    simplicity sake, but it makes no difference to the problem itself.

#2

-----BEGIN PGP SIGNED MESSAGE-----
Hash: SHA1

TRANS wrote:

core Ruby, but you will find it in both Rail’s Active Support and Ruby
def self.m=(x) ; @m=x ; end
c.m = 20
c.m #=> 20
C.m #=> 20

On a total side note, why do you have cattr_* methods affecting instance
methods as well ?

class C
cattr_accessor :m
end

C.m = 10
c = C.new
puts c.m

=> 10

I would expect it to work for “C.m” and “C.m=” but not for “c.m” I would
expect:

NoMethodError: undefined method m' for #<C:0xb7d3103c> from /home/zdennis/.irbrc:4:inmethod_missing’
from (irb):5
from :0

Perhaps I have wrong expectation of cattr_* . I am just asking at this
point though to better understand thought behind it.

Zach

-----BEGIN PGP SIGNATURE-----
Version: GnuPG v1.4.1 (GNU/Linux)
Comment: Using GnuPG with Thunderbird - http://enigmail.mozdev.org

iD8DBQFEOoKXMyx0fW1d8G0RAmiHAJ9KTcVR3wogDAWDEkvYEYtBJAJKFACcC/9T
Rxg1qQquejXwvmS6hH57aB0=
=Eq3D
-----END PGP SIGNATURE-----


#3

On Mon, Apr 10, 2006 at 11:58:43PM +0900, TRANS wrote:
} > Is it possible to add class attributes (cattr_accessor) using a
module?
} > If so could you give an example?
}
} There is no “Ruby Way” to do this b/c Ruby’s module methods aren’t
} inherited by classes that include them. Though I’ve asked for a way
} man times now!
[…]
} Only ugly meta-programming hacks will give the sought behavior --and
} even then their are corner-case complications.

Does this count as ugly meta-programming?

module M
def self.included(klass)
class << klass
attr_accessor :m
end
end
end

} T.
–Greg


#4

On 4/10/06, Gregory S. removed_email_address@domain.invalid wrote:

} Only ugly meta-programming hacks will give the sought behavior --and
end
It is much prettier than the following ugly meta-programming, but I have
no
clue how it works !!!
Could you explain please?
Thx
Robert

} T.

–Greg

class Module

            end
    end

end


Deux choses sont infinies : l’univers et la bêtise humaine ; en ce qui
concerne l’univers, je n’en ai pas acquis la certitude absolue.

  • Albert Einstein

#5

On Tue, Apr 11, 2006 at 02:19:30AM +0900, Robert D. wrote:
} On 4/10/06, Gregory S. removed_email_address@domain.invalid wrote:
} >
} > On Mon, Apr 10, 2006 at 11:58:43PM +0900, TRANS wrote:
} > } > Is it possible to add class attributes (cattr_accessor) using a
} > module?
} > } > If so could you give an example?
} > }
} > } There is no “Ruby Way” to do this b/c Ruby’s module methods aren’t
} > } inherited by classes that include them. Though I’ve asked for a
way
} > } man times now!
} > […]
} > } Only ugly meta-programming hacks will give the sought behavior
–and
} > } even then their are corner-case complications.
} >
} > Does this count as ugly meta-programming?
} >
} > module M
} > def self.included(klass)
} > class << klass
} > attr_accessor :m
} > end
} > end
} > end
}
} It is much prettier than the following ugly meta-programming, but I
have no
} clue how it works !!!
} Could you explain please?

The first thing to understand is that when you define a class, e.g.
class
Foo; … end, you are just setting a constant (Object::Foo, in this
case)
to a new instance of a Class. Just as you can add new methods to an
instance of any other kind of object with class << obj; … end, you can
add new methods to Foo. When a module is included, its self.included
method
is called with the Class instance it is being included into as an
argument.
I am defining self.included to add an attribute accessor to that Class
instance.

} Thx
} Robert
[…]
–Greg


#6

On 4/10/06, Gregory S. removed_email_address@domain.invalid wrote:

} > } inherited by classes that include them. Though I’ve asked for a way
} > attr_accessor :m
Foo; … end, you are just setting a constant (Object::Foo, in this case)
to a new instance of a Class. Just as you can add new methods to an
instance of any other kind of object with class << obj; … end, you can
add new methods to Foo. When a module is included, its self.includedmethod
is called with the Class instance it is being included into as an
argument.

I see thank you about the explanation of that hook.

I am defining self.included to add an attribute accessor to that Class

instance.

No of course not, because it is equivalent to

class <<self
attr_accessor
end

which does not work.

} Thx

} Robert
[…]
–Greg

Robert


Deux choses sont infinies : l’univers et la bêtise humaine ; en ce qui
concerne l’univers, je n’en ai pas acquis la certitude absolue.

  • Albert Einstein

#7

Does this count as ugly meta-programming?

module M
def self.included(klass)
class << klass
attr_accessor :m
end
end
end

Yep. That’s Ugly Hack #1. What wrong with Ugly Hack #1, you ask?

module N
include M
end

class C
include N
end

C.m
NoMethodError: undefined method `m’ for C:Class

T.


#8

On Tue, Apr 11, 2006 at 06:19:23AM +0900, TRANS wrote:
} > Does this count as ugly meta-programming?
} >
} > module M
} > def self.included(klass)
} > class << klass
} > attr_accessor :m
} > end
} > end
} > end
}
} Yep. That’s Ugly Hack #1. What wrong with Ugly Hack #1, you ask?
}
} module N
} include M
} end
}
} class C
} include N
} end
}
} C.m
} NoMethodError: undefined method `m’ for C:Class

Yep, that’s an issue. Well, you can do this:

module M
OnIncludeProc = lambda do |klass|
if klass.class == Module
def klass.included(k)
OnIncludeProc.call(k)
end
else
class << klass
attr_accessor :m
end
end
end

OnIncludeProc.call(self)

end

Yes, it’s uglier. Yes, it conflicts with other #included tricks with
modules
that include the module. It does work, though.

} T.
–Greg