How do I instantiate a class who's name is dynamic?

B

Ben Harper

I want to do the following, where 'somefile' is a dynamic value:
require 'somefile.rb'
'somefile.rb' will have a module inside it named "Magic_Module_somefile"
Inside that module there will be a class named "Magic_Class_somefile"
How do I new() that class ?
 
B

Ben Harper

Ben said:
I want to do the following, where 'somefile' is a dynamic value:
require 'somefile.rb'
'somefile.rb' will have a module inside it named "Magic_Module_somefile"
Inside that module there will be a class named "Magic_Class_somefile"
How do I new() that class ?

That is, without using eval( magic_etc + ".new" )?
 
D

dblack

Hi --

I want to do the following, where 'somefile' is a dynamic value:
require 'somefile.rb'
'somefile.rb' will have a module inside it named "Magic_Module_somefile"
Inside that module there will be a class named "Magic_Class_somefile"
How do I new() that class ?

You can get at it using const_get. Here's an irb demo:

irb(main):001:0> s = "somefile"
=> "somefile"
irb(main):002:0> module MM_somefile; class MC_somefile; end; end
=> nil
irb(main):003:0> Object.const_get("MM_#{s}").const_get("MC_#{s}").new
=> #<MM_somefile::MC_somefile:0xb7f85c34>

It's also possible to write a const_get variant that handles the ::
separator automatically:

class Object
def const_get_recursive(const)
const.split("::").inject(Object) {|c1,c2| c1.const_get(c2)}
end
end

const_get_recursive("MM_#{s}::MC::#{s}").new


David

--
David A. Black | (e-mail address removed)
Author of "Ruby for Rails" [1] | Ruby/Rails training & consultancy [3]
DABlog (DAB's Weblog) [2] | Co-director, Ruby Central, Inc. [4]
[1] http://www.manning.com/black | [3] http://www.rubypowerandlight.com
[2] http://dablog.rubypal.com | [4] http://www.rubycentral.org
 
M

Marcin Mielżyński

class Object
def const_get_recursive(const)
const.split("::").inject(Object) {|c1,c2| c1.const_get(c2)}
end
end

const_get_recursive("MM_#{s}::MC::#{s}").new


David

I know that eval is not save but it appears that it is much faster than
inject version

class A
class B
C = "foo"
end
end


n=10000
Benchmark.bm do |x|
x.report("eval") { n.times{ eval("A::B::C") } }
x.report("inject") { n.times{ Object.const_get_recursive("A::B::C")
} }
end

user system total real
eval 0.000000 0.000000 0.000000 ( 1.250000)
inject 0.000000 0.000000 0.000000 ( 2.656000)
Execution finished.


maybe eval does some caching or other kind of optimizations or maybe
because it's just c...?

lopex
 
K

Kalman Noel

Marcin Mielżyński:
I know that eval is not save but it appears that it is much faster than
inject version

class A
class B
C = "foo"
end
end

You may speed your method up a little by avoiding blocks with multiple
arguments, as following. Still eval is fastest, though, of course, nobody will
recommend it.

class Object
def const_get_eaching(const)
c = Object
const.split('::').each { |x| c = c.const_get x }
c
end
end


n=10000
Benchmark.bm do |x|
x.report("eval") { n.times{ eval("A::B::C") }}
x.report("inject") { n.times{ Object.const_get_recursive("A::B::C") }}
x.report("each") { n.times{ Object.const_get_eaching("A::B::C") }}

end

# Results:

user system total real
eval 0.100000 0.000000 0.100000 ( 0.101864)
inject 0.210000 0.040000 0.250000 ( 0.258335)
each 0.160000 0.030000 0.190000 ( 0.184249)

Kalman
 
E

Eero Saynatkari

Ben said:
I want to do the following, where 'somefile' is a dynamic value:
require 'somefile.rb'
'somefile.rb' will have a module inside it named "Magic_Module_somefile"
Inside that module there will be a class named "Magic_Class_somefile"
How do I new() that class ?

You want Module#const_get which must be called on the
enclosing class or module (split and inject is the
standard solution for cases where you have a qualified
class name in the ModuleName::ClassName format).
 
P

Patrick Spence

Marcin said:
I know that eval is not save but it appears that it is much faster than
inject version
<snip>
Just out of curosity, how is "eval()" not safe? I'm doing something very
similar to the OP and have adopted the Object.const_get() approach as
suggested by David. I'd like to get a better understanding of why this
is the preferred method.
 
F

Farrel Lifson

T24gMDgvMDMvMDcsIFBhdHJpY2sgU3BlbmNlIDxwYXRyaWNrQHBrc3BlbmNlLmNvbT4gd3JvdGU6
Cj4gTWFyY2luIE1pZWzFvHnFv3NraSB3cm90ZToKPiA+IGRibGFja0B3b2JibGluaS5uZXQgd3Jv
dGU6Cj4gPgo+ID4+IERhdmlkCj4gPj4KPiA+Cj4gPiBJIGtub3cgdGhhdCBldmFsIGlzIG5vdCBz
YXZlIGJ1dCBpdCBhcHBlYXJzIHRoYXQgaXQgaXMgbXVjaCBmYXN0ZXIgdGhhbgo+ID4gaW5qZWN0
IHZlcnNpb24KPiA8c25pcD4KPiBKdXN0IG91dCBvZiBjdXJvc2l0eSwgaG93IGlzICJldmFsKCki
IG5vdCBzYWZlPyBJJ20gZG9pbmcgc29tZXRoaW5nIHZlcnkKPiBzaW1pbGFyIHRvIHRoZSBPUCBh
bmQgaGF2ZSBhZG9wdGVkIHRoZSBPYmplY3QuY29uc3RfZ2V0KCkgYXBwcm9hY2ggYXMKPiBzdWdn
ZXN0ZWQgYnkgRGF2aWQuIEknZCBsaWtlIHRvIGdldCBhIGJldHRlciB1bmRlcnN0YW5kaW5nIG9m
IHdoeSB0aGlzCj4gaXMgdGhlIHByZWZlcnJlZCBtZXRob2QuCgpVbmxlc3MgeW91IGhhdmUgdGln
aHQgY29udHJvbCBvZiB5b3VyIHVzZXIgaW5wdXQgeW91IHJ1biB0aGUgcmlzawppbmplY3Rpb24g
YXR0YWNrcyB3aGVyZSB5b3UgbWlnaHQgIGV2YWwgInN5c3RlbSgncm0gLXJmIC8nKSIuCgpGYXJy
ZWwK
 
P

Patrick Spence

David said:
eval is not safe in any situation where there's any possibility that
you're executing text of unknown origin or suspicious composition. As
in:

command = gets.chomp
eval "system('#{command}')"

An extreme example, but you see the point :) When the input is not
suspicious, eval still often has a bit of a flavor of a brute-force
approach to doing things that there might be a more elegant way of
doing.

Thanks David and Farrel! Certainly, the Object#const_get() is a much
more elegant approach. Besides eval() smacks too much of the "&" macro
operator used in dBase and it's derivatives... yuck!
 

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,755
Messages
2,569,535
Members
45,007
Latest member
obedient dusk

Latest Threads

Top