How to create a class in callers context??

D

DMisener

Here's a simple example of my predicament:

class Factory
def self.generate name,_binding
_name=name.to_s.capitalize
eval %[
class #{_name}
def to_s
"#{_name}"
end
end
],_binding
end
end

def Factory_generate what
Factory.generate what,binding
end

Factory_generate :Thing
puts Factory::Thing.new rescue puts "Can't
Factory::Thing.new" # warning
puts Thing .new rescue puts "Can't Thing.new"

class Universe
def initialize
Factory_generate :planet
puts Universe::planet.new rescue puts "Can't
Universe::planet.new" # warning
puts Planet .new rescue puts "Can't Planet.new"
end
end

Universe.new

puts Universe::planet.new rescue puts "Can't
Universe::planet.new" # warning
puts Planet .new rescue puts "Can't Planet.new"

---------------------------------------------
results in the following surprising output:

C:/Work/class_eval.rb:20: warning: toplevel constant Thing referenced
by Factory::Thing
Thing
Thing
C:/Work/class_eval.rb:26: warning: toplevel constant Planet referenced
by Universe::planet
Planet
Planet
C:/Work/class_eval.rb:33: warning: toplevel constant Planet referenced
by Universe::planet
Planet
Planet
----------------------------------------------
Questions:

1) What's the correct way to doing this?
2) I would have thought the non-qualified class references would have
generated exceptions.
3) What is the warning message trying to convey.
 
R

Rubén Medellín

1) What's the correct way to doing this?

Don't know the "correct" way, there is always more than one form to do
it. This is what I would do

class Class
def generate(name)
klass = Class.new
klass.class_eval <<-EVAL
def to_s
"I'm a #{name}"
end
EVAL
const_set(name, klass)
end
end

class Universe
generate :planet
puts Planet.new
puts Universe::planet.new
end

puts Planet.rescue "No Planet!"
puts Universe::planet.new

_______
I'm a Planet
I'm a Planet
No Planet!
I'm a Planet
_______

alternatively you could do

klass = Class.new do
def foo
#...
end
end

but then you can't pass a reference to the variable name. Don't know
if there's a way do do it, though.

If I wanted that to stick on a method, I would pass the binding as
well

class Factory
def self.generate(name, _binding)
eval <<-EVAL, _binding
class #{name}
def to_s
"Hello from #{name}"
end
end
EVAL
end
end
2) I would have thought the non-qualified class references would have
generated exceptions.

Very intriguing. I would have expected the same
 
D

Drew Olson

DMisener said:
1) What's the correct way to doing this?
2) I would have thought the non-qualified class references would have
generated exceptions.
3) What is the warning message trying to convey.

Here's my crack at this, not sure this is what you're trying to do:

def generate &name
eval("self",name.binding).class.class_eval <<-EOS
class #{name.call}
def to_s
name.call
end
end
EOS
end

class Universe
def initialize
generate{:planet}
puts Universe::planet
end
end

generate{:Thing}

Universe.new
puts Thing
 
D

Drew Olson

Drew said:
Here's my crack at this, not sure this is what you're trying to do:

Had a small typo, here's the fixed example:

def generate &name
eval("self",name.binding).class.class_eval <<-EOS
class #{name.call}
def to_s
"#{name.call}"
end
end
EOS
end

class Universe
def initialize
generate{:planet}
end
end

generate{:Thing}

Universe.new

puts Universe::planet.new
puts Thing.new
 

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

No members online now.

Forum statistics

Threads
473,968
Messages
2,570,154
Members
46,702
Latest member
LukasConde

Latest Threads

Top