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

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 ?

Ben H. wrote:

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” )?

Ben H. wrote:

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).

Hi –

On Sat, 23 Sep 2006, Ben H. wrote:

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

On Sat, 23 Sep 2006, Ben H. wrote:

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 ?

http://rubyforge.org/frs/?group_id=1024&release_id=6580
http://codeforpeople.com/lib/ruby/dynaload/dynaload-0.2.0/README

-a

Marcin MielżyÅ?ski:

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

Marcin Mielżyſski wrote:

[email protected] wrote:

David

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

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.

[email protected] wrote:

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

On 08/03/07, Patrick S. [email protected] wrote:

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.

Unless you have tight control of your user input you run the risk
injection attacks where you might eval “system(‘rm -rf /’)”.

Farrel

Hi –

On 3/8/07, Patrick S. [email protected] wrote:

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.

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 :slight_smile: 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.

David

David A. Black wrote:

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 :slight_smile: 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!