Greetings.

I'd like to define a method which accepts a block such callers can write:

some_method do
   op1(arg)
   op2(arg)
end

instead of:

some_method do |obj|
   obj.op1(arg)
   obj.op2(arg)
end

Since within a block self.class.name always returns Object (not the 
object of the method which takes the block) it is necessary to use 
instance_eval to expose methods available within the context of the 
block. This however, breaks encapsulation exposing an object's instance 
data and private methods to the block.

One approach to preventing the leak of private fields and methods to the 
block would be to use an adaptor which only routes selected calls to the 
class which defines methods for the block's use. The code below 
demonstrates this approach.

My question is, is this the best way to achieve this in Ruby?

Thanks,


John-Mason Shackelford

Software Developer
Pearson Educational Measurement

2510 North Dodge St.
Iowa City, IA 52245
ph. 319-354-9200x6214
john-mason.shackelford / pearson.com
http://www.pearson.com/


-- code ---

class MacroExec
   def self.with(klass, &block)
     lang = klass.new
     safe_lang = SafetyAdaptor.new(lang)
     safe_lang.instance_eval(&block)
   end
end


class SafetyAdaptor

   def initialize(lang_def)
     @macro_lang = lang_def
   end

   def method_missing(m, *args)
     if legal_methods.include?(m.to_s)
       @macro_lang.class.instance_method(m).bind(@macro_lang).call(*args)
     else
       @macro_lang.method_missing(m, *args)
     end
   end

   private
   def legal_methods
     @macro_lang.public_methods()
   end

end


class MacroLang

   def initialize;
     @private_data = true
   end

   def hello_world;
     puts 'Hello World'
   end

   def method_missing(m, *args)
     puts "Undefined term #{m}."
   end

   private
   def private_message
     puts 'Private!'
   end

end


MacroExec.with(MacroLang) do
   hello_world
   nosuch
   puts @private_data
   private_message
end