tags:

views:

154

answers:

2

Man, I'm peeling the layers of the onion today, anyway here's the code

class MyClass
  def initialize(dynamic_methods)
    @arr = Array.new(dynamic_methods)
    @arr.each { |m|
      self.class.class_eval do
        define_method(m) do
          "<#{yield self if block_given?}>" 
        end
      end
    }
    end
end

tmp = MyClass.new ['method1', 'method2', 'method3']
tmp.method1 do |t|
  "here"
end

My problem is that I'm trying to access "here" within define_method(m) when the method is being executed, not when created. The current statement "<#{yield self if block_given?}>" doesn't give me that. And in case you are wondering, I have to keep this part of the code as is but I can make all the changes I want to MyClass.

tmp = MyClass.new ['method1', 'method2', 'method3']
tmp.method1 do |t|
  "here"
end

Can anyone help with the syntax? Thanks in advance for your help.

UPDATE: See below for my answer.

+1  A: 

Try replacing

define_method(m) do
  "<#{yield self if block_given?}>" 
end

with:

define_method(m) do |&block|
  "<#{block.call if block}>" 
end

This should work for 1.8.7 and up. You may also try to use module_eval:

self.class.module_eval %Q{
  def #{m}(&block)
    "<\#{block.call if block}>"
  end
}
Sergei Kozlov
It works with 1.8.7, but may not work with earlier versions.
Sergei Kozlov
Bob
Sergei Kozlov
Sorry that also caused an error, this time it's "undefined local variable or method `block'"
Bob
Correct, you'd need to escape the inner '#', so that it does not get evaluated straight away. Please, see my updated answer.
Sergei Kozlov
This is maddening, now it has this error "warning: multiple values for a block parameter (0 for 1)". Also thanks for the several times you've tried to help me so far.
Bob
I wouldn't be able to reproduce the warning, as I don't have 1.8.6. It must be rather buggy compared to 1.8.7, so, you may consider upgrading :)
Sergei Kozlov
Good idea except I'm concerned about 1.8.7 breaking my existing code, thanks for your gallant effort though, much appreciated.
Bob
A: 

With a lot of feedback from Sergei and some tinkering on my own, I manage to get it working

class MyClass
  def initialize(dynamic_methods)
    @arr = Array.new(dynamic_methods)
    @arr.each { |m|
      self.class.module_eval %Q{
        def #{m}(&block)
          yield(self) if block_given?
        end
      end
    }
    end
end

tmp = MyClass.new ['method1', 'method2', 'method3']
tmp.method1 do |t|
  "here"
end

As you can tell, there are some minor tweaks to Sergei's suggestions, so thanks for all your help Sergei.

Bob