How to create a class in callers context?

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.

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

DMisener wrote:

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

···

--
Posted via http://www.ruby-forum.com/.

Drew Olson wrote:

DMisener wrote:

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:

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

···

--
Posted via http://www.ruby-forum.com/.