1

I have a list of names and values I'm trying to read in and turn into classes so I'm using Class.new.

The end result I want is a number of classes that work as if defined like:

module MyMod
  class AA < Base
    def self.value
      value1
    end
  end

  class AB < Base
    def self.value
      value2
    end
  end

  ...
end

My current code looks like:

name = 'AA'
value = 'test'
MyMod.const_set name, Class.new(Base) do
  ???
end

Setting the name works great, but haven't figured out what I need in the block for get value in. Calling def doesn't work because the closure for value gets lost.

I have managed to get things working with:

temp = const_set name, Class.new(Base)
temp.define_singleton_method(:value) { value }

However, it seems like there should be a way to do it with the block of Class.new. Also, I'm really not sure define_singleton_method is actually putting the method in the right place. It works in my tests, but I'm not sure if the method is actually where I think it is or somewhere else up the call chain. I've tried various combinations of class_variable_set, attr_reader, class_eval, instance_eval, and others, but it got to a point where it was just guess and check. I think I still haven't quite wrapped my head around metaprogramming :-/

2
  • 1
    Can you provide some context why you want to do this? Maybe there's another way. I love metaprogramming (I'm working with Smalltalk), but generating classes on the fly (I tried that) was rarely the optimal solution. Commented Nov 23, 2012 at 13:09
  • @ewernli I have a short list of valid name:value pairs. I had created a class for them and kept the list of all the valid instances that were generated from reading a file. I didn't want arbitrary instance being created so had a lookup method on that list instead of an initializer. I was about to make another class that would encapsulate an "instance" of the first class storing a reference to it and some additional data. Based on that naming I realised making classes and having actual instances of them might be better. I think it models the domain well, but haven't considered performance. Commented Nov 23, 2012 at 20:23

1 Answer 1

4

if i correctly understood your question, this should work for you:

class Base
end

class AA < Base

  name = :Blah
  klass = self.const_set name, Class.new(Base)

  class << klass
    def value
      __method__
    end
  end

end

p AA::Blah.value
#=> :value

UPDATE: seems you want it defined in the block:

class Base
end

class AA < Base

  name = :Blah
  klass = Class.new(Base) do

    class << self
      def value
        __method__
      end
    end

  end
  self.const_set name, klass

end

p AA::Blah.value

you trying this:

const_set name, Class.new(Base) do
  ...
end

it does not work cause the block is referring to const_set rather than to Class.new

If you prefer define_singleton_method over class << self:

class Base
end

class AA < Base

  name = :Blah
  klass = Class.new(Base) do

    self.define_singleton_method :value do
      __method__
    end

  end
  self.const_set name, klass

end

And finally if you really want to define them at once, use brackets instead of do...end:

class Base
end

class AA < Base

  name = :Blah
  self.const_set name, Class.new(Base) {

    self.define_singleton_method :value do
      __method__
    end

  }

end

Here is a working demo

Sign up to request clarification or add additional context in comments.

2 Comments

The crux of the solution was "it does not work cause the block is referring to const_set rather than to Class.new". Thanks! Refactorings have rendered the rest obsolete, but they were useful examples.
Further refactorings have rendered the rest of this very useful so thank you again!

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.