Eric Hodel
12/3/2004 6:56:00 PM
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."
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