Forum: Ruby how to create Class object with name determined at runtime

Announcement (2017-05-07): www.ruby-forum.com is now read-only since I unfortunately do not have the time to support and maintain the forum any more. Please see rubyonrails.org/community and ruby-lang.org/en/community for other Rails- und Ruby-related community platforms.
Bill R. (Guest)
on 2006-04-28 21:36
I hope someone can help with this - I am a Ruby newbie.

In my code I want to decide at runtime which type of object to create,
based on a variable holding the name of the class.

So I have classes Foo and Bar and a string todays_class.

If todays_class = "Foo"  I want to say my_obj = Foo.new
if todayS_class = "Bar" then my_obj = Bar.new

Except I've got a lot more than two classes and I'd prefer not to use an
if or case statement, so that I don't need to change the code if I add
another one to the list.

Any suggestions on a neat way to do this?

Thanks
James G. (Guest)
on 2006-04-28 21:47
(Received via mailing list)
On Apr 28, 2006, at 12:36 PM, Bill Roberts wrote:

> Except I've got a lot more than two classes and I'd prefer not to
> use an
> if or case statement, so that I don't need to change the code if I add
> another one to the list.
>
> Any suggestions on a neat way to do this?

 >> class Foo; end
=> nil
 >> class Bar; end
=> nil
 >> cls = "Foo"
=> "Foo"
 >> Object.const_get(cls)
=> Foo
 >> # Or, for nested classes...
?> class Foo
 >>   class Baz; end
 >> end
=> nil
 >> cls = "Foo::Baz"
=> "Foo::Baz"
 >> cls.split("::").inject(Object) do |parent, constant|
?>   parent.const_get(constant)
 >> end
=> Foo::Baz

Hope that helps.

James Edward G. II
Bill R. (Guest)
on 2006-04-28 21:52
James G. wrote:
>  >> Object.const_get(cls)
> => Foo
>
> Hope that helps.
>
> James Edward G. II

Yes it helps a lot! Thanks.
John J. (Guest)
on 2006-04-29 00:55
(Received via mailing list)
You can use eval() to do that, like so:
irb(main):001:0> a='String'
=> "String"
irb(main):002:0> b=eval("#{a}.new")
=> ""
irb(main):003:0> b.class
=> String
irb(main):004:0>

In the second irb line, the contents of the variable _a_ are substituted
in the string before the .new. It amounts to b=eval("String.new").

Regards,
  JJ

On Friday, April 28, 2006, at 02:55PM, Bill Roberts
<removed_email_address@domain.invalid> wrote:

>Except I've got a lot more than two classes and I'd prefer not to use an
>
>


---
Help everyone. If you can't do that, then at least be nice.
Bill Roberts (Guest)
on 2006-05-01 15:51
John J. wrote:
> You can use eval() to do that, like so:
> irb(main):001:0> a='String'
> => "String"
> irb(main):002:0> b=eval("#{a}.new")
> => ""
> irb(main):003:0> b.class
> => String
> irb(main):004:0>
>
> In the second irb line, the contents of the variable _a_ are substituted
> in the string before the .new. It amounts to b=eval("String.new").
>
> Regards,
>   JJ
>
>
John - thanks, also a neat solution.  Having had experience mainly in
static/compiled languages previously, I am just getting to grips with
Ruby's nice features for adapting itself as it runs.
polypus (Guest)
on 2006-05-02 02:13
Having had experience mainly in
> static/compiled languages previously, I am just getting to grips with
> Ruby's nice features for adapting itself as it runs.


you might want to check out Hal F.'s article 'An Exercise in
Metaprogramming with Ruby', where amongst other things he provides a
solution to your problem:

http://www.devsource.com/article2/0,1895,1928561,00.asp

_c
Gregory S. (Guest)
on 2006-05-03 20:57
(Received via mailing list)
On Mon, May 01, 2006 at 08:52:12PM +0900, Bill Roberts wrote:
} John J. wrote:
} > You can use eval() to do that, like so:
} > irb(main):001:0> a='String'
} > => "String"
} > irb(main):002:0> b=eval("#{a}.new")
} > => ""
} > irb(main):003:0> b.class
} > => String
} > irb(main):004:0>
} >
} > In the second irb line, the contents of the variable _a_ are
substituted
} > in the string before the .new. It amounts to b=eval("String.new").

Avoid using eval when possible.

irb(main):001:0> a='String'
=> "String"
irb(main):002:0> b=Object::const_get(a).new()
=> ""
irb(main):003:0> b.class
=> String
irb(main):004:0>

} > Regards,
} >   JJ
} >
} >
} John - thanks, also a neat solution.  Having had experience mainly in
} static/compiled languages previously, I am just getting to grips with
} Ruby's nice features for adapting itself as it runs.
--Greg
Benjohn B. (Guest)
on 2006-05-03 20:57
(Received via mailing list)
On 28 Apr 2006, at 18:36, Bill Roberts wrote:

> I hope someone can help with this - I am a Ruby newbie.
>
> In my code I want to decide at runtime which type of object to create,
> based on a variable holding the name of the class.

It (may be) worth noting that because classes are "first class
objects" in Ruby, you can reference the actual class's object with a
variable, if you wish:

class MyClass; end
c = MyClass
c.new

... sorry if I'm pointing out the very obvious.

Cheers,
	Benj
Francisco O. (Guest)
on 2006-05-03 21:07
(Received via mailing list)
On 5/1/06, Gregory S. <removed_email_address@domain.invalid> wrote:
>
> Avoid using eval when possible.
>

Hi Greg, would you mind telling me why? is this more expensive?
insecure?
I´m another newcomer to Ruby and this kind of tips are gold to me.

thanks!
Francisco
Wilson B. (Guest)
on 2006-05-03 21:07
(Received via mailing list)
On 5/3/06, Francisco O. <removed_email_address@domain.invalid> wrote:
> On 5/1/06, Gregory S. <removed_email_address@domain.invalid> wrote:
> >
> > Avoid using eval when possible.
> >
>
> Hi Greg, would you mind telling me why? is this more expensive? insecure?
> I´m another newcomer to Ruby and this kind of tips are gold to me.
>

Mostly speed, but many people (myself included) find it easier to read.

const_get is about 10 times faster than eval:
ruby eval_bench.rb
                user     system      total        real
eval:       1.140000   0.000000   1.140000 (  1.141000)
const_get:  0.157000   0.000000   0.157000 (  0.156000)

#eval_bench.rb
require 'benchmark'
class Foo;end
some_class = 'Foo'
Benchmark.bm(10) do |b|
  b.report("eval:") { 100000.times {eval("#{some_class}.new")} }
  b.report("const_get:") {
100000.times{Object.const_get(some_class).new} }
end
This topic is locked and can not be replied to.