Google Groups no longer supports new Usenet posts or subscriptions. Historical content remains viewable.
Dismiss

Dynamically generating classes?

2 views
Skip to first unread message

Jonas Galvez

unread,
Sep 27, 2005, 5:42:57 PM9/27/05
to
Hi,

In Python I can do this:

>>> def create_class(name):
.. import new
.. c = new.classobj(name, tuple([object]), {})
.. def __init__(self, value):
.. self.value = value
.. setattr(c, "__init__", new.instancemethod(__init__, None, c))
.. return c
..
>>> MyClass = create_class("MyClass")
>>>
>>> obj = MyClass(value=10)
>>> print obj.value
10

Is there anything similar in Ruby? Or do I need to use eval()?

--Jonas Galvez


Ara.T.Howard

unread,
Sep 27, 2005, 5:54:33 PM9/27/05
to
On Wed, 28 Sep 2005, Jonas Galvez wrote:

> Hi,
>
> In Python I can do this:
>
>>>> def create_class(name):

> ... import new
> ... c = new.classobj(name, tuple([object]), {})
> ... def __init__(self, value):
> ... self.value = value
> ... setattr(c, "__init__", new.instancemethod(__init__, None, c))
> ... return c
> ...


>>>> MyClass = create_class("MyClass")
>>>>
>>>> obj = MyClass(value=10)
>>>> print obj.value
> 10
>
> Is there anything similar in Ruby? Or do I need to use eval()?

much more easily:

harp:~ > cat a.rb
klass =
Class::new {
def foo
42
end
def bar
'forty-two'
end
}

k = klass::new
p k.foo

MyKlass = klass
k = MyKlass::new
p k.bar


harp:~ > ruby a.rb
42
"forty-two"

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
===============================================================================

Jonas Galvez

unread,
Sep 27, 2005, 5:56:25 PM9/27/05
to
Jonas Galvez wrote:
> Hi,
>
> In Python I can do this:
>
> >>> def create_class(name):
> ... import new
> ... c = new.classobj(name, tuple([object]), {})
> ... def __init__(self, value):
> ... self.value = value
> ... setattr(c, "__init__", new.instancemethod(__init__, None, c))
> ... return c
> ...

> >>> MyClass = create_class("MyClass")
> >>>
> >>> obj = MyClass(value=10)
> >>> print obj.value
> 10
>
> Is there anything similar in Ruby? Or do I need to use eval()?

Hmm, apparently:

MyClass = Class.new {
attr_accessor :value
def initialize(value)
self.value = value
end
}

o = MyClass.new(10)
puts o.value

But now I ask, how do I dynamically set its name ("MyClass") in the
global namespace?

--Jonas Galvez


Austin Ziegler

unread,
Sep 27, 2005, 5:58:54 PM9/27/05
to
On 9/27/05, Jonas Galvez <jonas...@gmail.com> wrote:
> In Python I can do this:
> >>> def create_class(name):
> ... import new
> ... c = new.classobj(name, tuple([object]), {})
> ... def __init__(self, value):
> ... self.value = value
> ... setattr(c, "__init__", new.instancemethod(__init__, None, c))
> ... return c
> ...

> >>> MyClass = create_class("MyClass")
> >>>
> >>> obj = MyClass(value=10)
> >>> print obj.value
> 10
>
> Is there anything similar in Ruby? Or do I need to use eval()?

def create_class(parent = nil)
if parent
klass = Class.new(parent)
else
klass = Class.new
end
klass.class_eval do
def initialize(value)
@value = value
end

attr_reader :value
end
klass
end

MyClass = create_class
obj = MyClass.new(10)
puts obj.value

The only difference is that the class's name is based on the first
constant it's given to. That's a bit of RubyMagic.

-austin
--
Austin Ziegler * halos...@gmail.com
* Alternate: aus...@halostatue.ca


Greg Millam

unread,
Sep 27, 2005, 6:01:32 PM9/27/05
to
On Wed, Sep 28, 2005 at 06:42:57AM +0900, Jonas Galvez wrote:
> In Python I can do this:
>
> >>> def create_class(name):
> ... import new
> ... c = new.classobj(name, tuple([object]), {})
> ... def __init__(self, value):
> ... self.value = value
> ... setattr(c, "__init__", new.instancemethod(__init__, None, c))
> ... return c
> ...

> >>> MyClass = create_class("MyClass")
> >>>
> >>> obj = MyClass(value=10)
> >>> print obj.value
> 10
>
> Is there anything similar in Ruby? Or do I need to use eval()?

MyClass = Class.new do
attr_accessor :value


def initialize(value)
@value = value
end

end

obj = MyClass.new(10)
puts obj.value

Cleaner, I think =).

If you want "MyClass" to be dynamically set:
Obj.const_set("MyClass",Class.new { ... })

Or for really simple classes, you can use 'struct':

require 'struct'
Struct.new('MyClass','value')


obj = MyClass.new(10)
puts obj.value

Hope that helps!

- Greg


Austin Ziegler

unread,
Sep 27, 2005, 6:01:54 PM9/27/05
to
On 9/27/05, Jonas Galvez <jonas...@gmail.com> wrote:
> Jonas Galvez wrote:
> > Is there anything similar in Ruby? Or do I need to use eval()?
> Hmm, apparently:
>
> MyClass = Class.new {
> attr_accessor :value
> def initialize(value)
> self.value = value
> end
> }
>
> o = MyClass.new(10)
> puts o.value
>
> But now I ask, how do I dynamically set its name ("MyClass") in the
> global namespace?

def create_class(name, parent = nil)


if parent
klass = Class.new(parent)
else
klass = Class.new
end
klass.class_eval do

def initialize(value)
@value = value
end

attr_reader :value
end

Object.const_set(name, klass)

klass
end

create_class('MyClass')
obj = MyClass.new(10)
puts obj.value


-a

Sean O'Halpin

unread,
Sep 27, 2005, 6:04:00 PM9/27/05
to
On 9/27/05, Jonas Galvez <jonas...@gmail.com> wrote:

> In Python I can do this:
>
> >>> def create_class(name):

> ... import new
> ... c = new.classobj(name, tuple([object]), {})
> ... def __init__(self, value):
> ... self.value = value

> ... setattr(c, "__init__", new.instancemethod(__init__, None, c))
> ... return c
> ...


> >>> MyClass = create_class("MyClass")
> >>>
> >>> obj = MyClass(value=10)
> >>> print obj.value
> 10
>
> Is there anything similar in Ruby? Or do I need to use eval()?

You can create an anonymous class then associate it with a named constant:

klass = Class.new do
attr_accessor :value


def initialize(value)
@value = value
end

end

Object.const_set('MyClass', klass)

c = MyClass.new(10)
p c
#=> #<MyClass:0x2871320 @value=10>

Regards,

Sean


Jonas Galvez

unread,
Sep 27, 2005, 6:17:39 PM9/27/05
to
Austin Ziegler wrote:
> Object.const_set(name, klass)

Thanks all. Here's what I'm trying to do exactly:

ERROR_CODES = YAML::load open('config/errorcodes.yml').read

for k, v in ERROR_CODES
Object.const_set(k, Class.new(Exception) {
def to_s
"{code: #{v['code']}, message: #{v['message']}}"
end
})
end

o = MyException.new # "MyExcetion" is defined in the YAML file
puts o

Unfortunately, this doesn't seem to work:

test.rb:8:in `to_s': undefined local variable or method `v' for
#<NotAFeed:0x2b6ca08> (NameError)
from test.rb:14:in `puts'
from test.rb:14

Here's the equivalent Python code (which works):

ERROR_CODES = syck.load(open('config/errorcodes.yml').read())

for k, v in ERROR_CODES.items():
nexc = new.classobj(k, (Exception,), {})
code, message = v
tostr = lambda self: "{code: %s, message: %s}" % code, message
setattr(nexc, "__str__", new.instancemethod(tostr, None, nexc))
globals()[k] = nexc

try:
raise MyException
except MyException, e:
print e # prints "{code: ..., message: ...}"


Thanks again.

--Jonas Galvez


Sean O'Halpin

unread,
Sep 27, 2005, 6:29:59 PM9/27/05
to
[snip]

> tostr = lambda self: "{code: %s, message: %s}" % code, message

I don't know Python's scoping rules, but to get the same effect in
Ruby you also need to use a block to capture the scope at the time of
definition - so use +define_method+ instead of +def+:

ERROR_CODES = {
:MyEx1 => {'code' => 1, 'message' => "Oops!"},
:MyEx2 => {'code' => 2, 'message' => "Whoops!"},
}

for k, v in ERROR_CODES
Object.const_set(k, Class.new(Exception) {

define_method :to_s do


"{code: #{v['code']}, message: #{v['message']}}"
end
})
end

o = MyEx1.new
p o

#=> #<MyEx1: {code: 2, message: Whoops!}>

HTH,

Sean


Devin Mullins

unread,
Sep 28, 2005, 12:15:15 AM9/28/05
to
Here's another way you might do it:

ERROR_CODES = {
:MyEx1 => {'code' => 1, 'message' => "Oops!"},
:MyEx2 => {'code' => 2, 'message' => "Whoops!"},
}

#StandardError'd be the canonical one to inherit.
#Exceptions outside of that tree are typically Ruby errors (bad syntax,
no memory, etc.).
class MyError < StandardError
def to_s
"{code: #{CODE}, message: #{MESSAGE}}"
end
end

for k, v in ERROR_CODES

Object.const_set(k, Class.new(MyError) {
CODE = v['code']
MESSAGE = v['message']
})
end

o = MyEx1.new
p o

__END__

#<MyEx1: {code: 2, message: Whoops!}>

I think it's a little cleaner, but that's partially taste.

Devin

Devin Mullins

unread,
Sep 28, 2005, 12:15:40 AM9/28/05
to
Austin Ziegler wrote:

>-a
>
>
A case of mistaken identity?

-devin

Robert Klemme

unread,
Sep 28, 2005, 3:33:47 AM9/28/05
to

Note though that this is completely superfluous if you know the name
beforehand:

09:31:38 [Oracle]: ruby -e 'MyClass = Class.new {}; p MyClass.name'
"MyClass"

09:32:48 [Oracle]: ruby -e 'c = Class.new {}; p c.name ; MyClass = c; p
MyClass.name'
""
"MyClass"

Assigning to a constant is sufficient.

Kind regards

robert


Jonathan Yurek

unread,
Sep 28, 2005, 10:45:02 AM9/28/05
to
Sean O'Halpin wrote:
> ERROR_CODES = {
> :MyEx1 => {'code' => 1, 'message' => "Oops!"},
> :MyEx2 => {'code' => 2, 'message' => "Whoops!"},
> }

[snip]

> #<MyEx1: {code: 2, message: Whoops!}>

Isn't that wrong, though? You instantiated MyExt1, but your code and
message are displaying what MyExt2 should be.

-- Jon


Sean O'Halpin

unread,
Sep 28, 2005, 11:53:31 AM9/28/05
to
On 9/28/05, Jonathan Yurek <jyu...@thoughtbot.com> wrote:
> [snip]
>
> > #<MyEx1: {code: 2, message: Whoops!}>
>
> Isn't that wrong, though? You instantiated MyExt1, but your code and
> message are displaying what MyExt2 should be.
>
> -- Jon
>
>

Hey - you're right! It's the for vs each scoping again! Ouch!

This works as expected:

ERROR_CODES = {
:MyEx1 => {'code' => 1, 'message' => "Oops!"},
:MyEx2 => {'code' => 2, 'message' => "Whoops!"},
}

ERROR_CODES.each do |k,v|


Object.const_set(k, Class.new(Exception) {
define_method :to_s do
"{code: #{v['code']}, message: #{v['message']}}"
end
})
end

p MyEx1.new
p MyEx2.new

__END__
#<MyEx1: {code: 1, message: Oops!}>
#<MyEx2: {code: 2, message: Whoops!}>

But best not to do it this way - see Devin Mullins' post for a better way.

Sean


Austin Ziegler

unread,
Sep 28, 2005, 11:55:29 AM9/28/05
to

No, both Ara and I will sign with -a from time to time ;)

-austin

0 new messages