modules, self class, help!

J

Joe Van Dyk

module JoeAccessors
class << self
def joe_accessor_method *args
args.each do |arg|
eval <<-src
def #{ arg }
@#{ arg }
end
def #{ arg }=3D value
@#{ arg } =3D value
end
src
end
end
end
end

class Joe
include JoeAccessors
joe_accessor_method :arg1, :arg2
end
test.rb:21: undefined method `joe_accessor_method' for Joe:Class (NoMetho=
dError)

*bangs head against desk*
 
B

Brian Mitchell

module JoeAccessors
class << self
def joe_accessor_method *args
args.each do |arg|
eval <<-src
def #{ arg }
@#{ arg }
end
def #{ arg }=3D value
@#{ arg } =3D value
end
src
end
end
end
end
=20
class Joe
include JoeAccessors
joe_accessor_method :arg1, :arg2
end
=20

module JoeAccessors
def joe_accessor_method *args
args.each do |arg|
eval <<-src
class << self
def #{ arg }
@#{ arg }
end
def #{ arg }=3D value
@#{ arg } =3D value
end
end
src
end
end
end

class Joe
extend JoeAccessors
joe_accessor_method :arg1, :arg2
end

This seems to be where the problem is. The module method does not
become a class method by include. The other thing to note is that I
moved the class << self so it targets your final object (instance of
Class in this case). Otherwise, you will find that it will not find
you method.

However, I am not sure what else you are trying to accomplish by this
code... as a guess, the following works for me:

Joe.arg1 =3D 42
Joe.arg1 #=3D> 42
hodError)
=20
*bangs head against desk*

Always wear a helmet when doing any meta-programming. :)

Brian.
 
A

Ara.T.Howard

module JoeAccessors
class << self
def joe_accessor_method *args
args.each do |arg|
eval <<-src
def #{ arg }
@#{ arg }
end
def #{ arg }= value
@#{ arg } = value
end
src
end
end
end
end

class Joe
include JoeAccessors
joe_accessor_method :arg1, :arg2
end


*bangs head against desk*

you're close - the issue is that including a module in another class/module
only inserts the __instance__ methods of the included module into the
class/module doing the reciever. so, altough you've defined a class level
method correctly it doesn't make it into your new class. there are a couple of
ways around that - you can do things like:

module M
def foo; 42; end
end
class C
extend M
end
p C::foo #=> 42

but that only works if ALL the methods in M should be class methods - though
this is often the case where one is defining accessor type stuff in a module.
another approach is do define class level methods in the other class on the
fly and let the instance methods take care of themselves:

module M
def self::included other
class << other
def foo; 42; end
end
end
def bar; 'forty-two'; end
end
class C
include M
end
p C::foo #=> 42
c = C::new
p c.bar #=> 'forty-two'

what you can't do, however, is to define a class method in a module and then
somehow 'get it back' to inject it into the including class at runtime (well
you could but it'd be very ugly). i've found this approach to be quite simple
and almost guaranteed to make sense to any developer that ever programmed an
object-oriented language instantly - even if they don't understand the
included method totally:

harp:~ > cat b.rb
module JoeAccessors

module ClassMethods
def joe_accessor_method *args
args.each do |arg|
module_eval <<-src
def #{ arg }
@#{ arg }
end
def #{ arg }= value
@#{ arg } = value
end
src
end
end
end

module InstanceMethods
# put em here
end

def self::included other
other.extend ClassMethods
other.module_eval{ include InstanceMethods }
end
end

class Joe
include JoeAccessors
joe_accessor_method :arg1, :arg2
end

joe = Joe::new
joe.arg1 = 42
p joe.arg1



harp:~ > ruby b.rb
42

it's just a tiny bit clunky - but it makes it totally clear which methods are
which without any doccumentation or understanding of 'class << self' etc.

(note that you have to use 'module_eval' here.)

hth.

-a
--
===============================================================================
| email :: ara [dot] t [dot] howard [at] noaa [dot] gov
| phone :: 303.497.6469
| Your life dwells amoung the causes of death
| Like a lamp standing in a strong breeze. --Nagarjuna
===============================================================================
 
J

Joe Van Dyk

On Tue, 16 Aug 2005, Joe Van Dyk wrote:
=20
=20
you're close - the issue is that including a module in another class/modu= le
only inserts the __instance__ methods of the included module into the
class/module doing the reciever. so, altough you've defined a class leve= l
method correctly it doesn't make it into your new class. =20

What's the rationale behind that?
 
A

Ara.T.Howard

What's the rationale behind that?

i'm not sure - but if it were true to include class methods by default it'd be
easy to do this:

harp:~ > cat a.rb
module M
module ClassMethods
def new
"can't create instances!"
end
def name
"can't get the name"
end
def ancestors
['ha', 'ha']
end
def is_a?
'nope'
end
end
module InstanceMethods
end
def self::included other
other.extend ClassMethods
other.module_eval{ include InstanceMethods }
end
end

class C
include M
end

p C::new
p C::name
p C::ancestors
p C::is_a?



harp:~ > ruby a.rb
"can't create instances!"
"can't get the name"
["ha", "ha"]
"nope"


if including modules added/supered class methods include would be almost
exactly like inheritence - some people think this is a good idea, some not. my
take is just that, in the normal case it's not what you want - but sometimes it
is.

cheers.

-a
--
===============================================================================
| email :: ara [dot] t [dot] howard [at] noaa [dot] gov
| phone :: 303.497.6469
| Your life dwells amoung the causes of death
| Like a lamp standing in a strong breeze. --Nagarjuna
===============================================================================
 
P

Pit Capitain

Ara.T.Howard said:
module JoeAccessors
module ClassMethods
...
end
module InstanceMethods
...
end
def self::included other
other.extend ClassMethods
other.module_eval{ include InstanceMethods }
end
end

(note that you have to use 'module_eval' here.)

Because Object#include is a private method, or are there other reasons?

Regards,
Pit
 
T

Trans

Ara.T.Howard said:
what you can't do, however, is to define a class method in a module and then
somehow 'get it back' to inject it into the including class at runtime (well
you could but it'd be very ugly). i've found this approach to be quite simple
and almost guaranteed to make sense to any developer that ever programmed an
object-oriented language instantly - even if they don't understand the
included method totally:

harp:~ > cat b.rb
module JoeAccessors

module ClassMethods
def joe_accessor_method *args
args.each do |arg|
module_eval <<-src
def #{ arg }
@#{ arg }
end
def #{ arg }= value
@#{ arg } = value
end
src
end
end
end

module InstanceMethods
# put em here
end

You can just put the instance methods where they normally go. No
InstanceMethods module needed.
def self::included other
other.extend ClassMethods
other.module_eval{ include InstanceMethods }
end

Then cahnge the above to:

extend ClassMethods

def self::included other
other.extend ClassMethods
end
it's just a tiny bit clunky - but it makes it totally clear which methods are
which without any doccumentation or understanding of 'class << self' etc.

A little less clunky.

T.
 

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
474,176
Messages
2,570,947
Members
47,501
Latest member
Ledmyplace

Latest Threads

Top