How to create an object of a class you don't know yet

Hi, I'm just now getting into Ruby's OO-ness, and could use some advice.
I'm trying to create an object, but which specific class needs to be
determined at runtime. I figured out how to do this by creating a
string and executing it using the "eval" command, but I know there must
be an easier way. My first guess was to do something like #{answer}.new
but that didn't work.

Here's my code:

class Foo
  def method1
  end
end

class Bar
  def method2
  end
end

# Pretend this was determined at runtime
answer = "Foo"

# There must be a better way of doing this:

myobj = Object.new # Needs to exist in this scope
mystring = "myobj = #{answer}.new"

eval mystring

puts "I just created a #{myobj.class} object."

Any advice is appreciated. Thanks in advance!

Todd.

klass = answer.split('::').inject(Object) { |klass,const| klass.const_get const }

myobj = klass.new

In longer terms:

answer.split('::') # for Foo::Bar::Baz nested classes/modules

answer.split('::').inject(Object) do |klass, const| # namespaces start from Object
   klass.const_get const # #inject passes the value of this expression in
            # as the first arg to the block, so use that namespace
            # to find the next part of the namespace
end

klass = answer.split [...] # #inject returns the last result, which will
               # be a class, provided answer references a class

myobj = klass.new # instantiate an instance of the class

You can also do things like this:

KLASSES = { 'html' => HTMLWriter, 'pdf' => PDFWriter, 'plain-text' => TextWriter }

output = ARGV.shift

raise "invalid output type" unless KLASSES.include? output

writer = KLASSES[output].new

···

On 03 Dec 2004, at 10:44, Bradley, Todd wrote:

Hi, I'm just now getting into Ruby's OO-ness, and could use some advice.
I'm trying to create an object, but which specific class needs to be
determined at runtime. I figured out how to do this by creating a
string and executing it using the "eval" command, but I know there must
be an easier way. My first guess was to do something like #{answer}.new
but that didn't work.

Here's my code:

class Foo
  def method1
  end
end

class Bar
  def method2
  end
end

# Pretend this was determined at runtime
answer = "Foo"

# There must be a better way of doing this:

myobj = Object.new # Needs to exist in this scope
mystring = "myobj = #{answer}.new"

eval mystring

puts "I just created a #{myobj.class} object."

"new" is just a class method, and you can call class methods on classes that aren't bound at the time of interpretation. For example:

instance = MyClass.new

is the same as

a_class = MyClass
instance = a_class.new

So you can do things like:

irb(main):001:0> classes = [ String, Hash, Array ]
=> [String, Hash, Array]
irb(main):002:0> grab_bag = classes.collect { |a_class| a_class.new }
=> ["", {}, []]

···

On Dec 3, 2004, at 1:44 PM, Bradley, Todd wrote:

Hi, I'm just now getting into Ruby's OO-ness, and could use some advice.
I'm trying to create an object, but which specific class needs to be
determined at runtime. I figured out how to do this by creating a
string and executing it using the "eval" command, but I know there must
be an easier way. My first guess was to do something like #{answer}.new
but that didn't work.

Here's my code:

class Foo
  def method1
  end
end

class Bar
  def method2
  end
end

# Pretend this was determined at runtime
answer = "Foo"

# There must be a better way of doing this:

myobj = Object.new # Needs to exist in this scope
mystring = "myobj = #{answer}.new"

eval mystring

puts "I just created a #{myobj.class} object."

Any advice is appreciated. Thanks in advance!

Todd.

Francis Hwang
http://fhwang.net/

Bradley, Todd wrote:

Hi, I'm just now getting into Ruby's OO-ness, and could use some advice.
I'm trying to create an object, but which specific class needs to be
determined at runtime. I figured out how to do this by creating a
string and executing it using the "eval" command, but I know there must
be an easier way. My first guess was to do something like #{answer}.new
but that didn't work.

Here's my code:

class Foo
  def method1
  end
end

class Bar
  def method2
  end
end

# Pretend this was determined at runtime
answer = "Foo"

# There must be a better way of doing this:

myobj = Object.new # Needs to exist in this scope
mystring = "myobj = #{answer}.new"

eval mystring

puts "I just created a #{myobj.class} object."

You could do:

   myobj = eval("Foo.new")

or

   myobj = eval("Foo").new

or even

   myobj = Object.const_get( "Foo" ).new

I prefer the second option listed, since it is easier to pass arguments to constructor, and less verbose than the third option.

- Jamis

···

Any advice is appreciated. Thanks in advance!

Todd.

--
Jamis Buck
jgb3@email.byu.edu
http://www.jamisbuck.org/jamis

Bradley, Todd wrote:

Hi, I'm just now getting into Ruby's OO-ness, and could use some advice.
I'm trying to create an object, but which specific class needs to be
determined at runtime. I figured out how to do this by creating a
string and executing it using the "eval" command, but I know there must
be an easier way. My first guess was to do something like #{answer}.new
but that didn't work.

Here's my code:

class Foo
  def method1
  end
end

class Bar
  def method2
  end
end

# Pretend this was determined at runtime
answer = "Foo"

# There must be a better way of doing this:

myobj = Object.new # Needs to exist in this scope
mystring = "myobj = #{answer}.new"

eval mystring

puts "I just created a #{myobj.class} object."

Any advice is appreciated. Thanks in advance!

Todd.

class GMClassManager
   @@classHash = { 'CLASS_ROOM_DEFAULT' => GMRoom,
      'CLASS_ROOM_LOGIN' => GMLoginRoom,
   }

   def GMClassManager.getRoomObject( valueClassId )
     tmpClass = @@classHash[ valueClassId ]
     if not tmpClass == nil
       tmpClass.new
     else
       nil
     end
   end
end

This works for me

Daneel van Tonder wrote:

class GMClassManager
  @@classHash = { 'CLASS_ROOM_DEFAULT' => GMRoom,
          'CLASS_ROOM_LOGIN' => GMLoginRoom,
  }

Using a constant instead would be nicer, I think.