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
-- 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