J
John-Mason P. Shackelford
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
(e-mail address removed)
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
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
(e-mail address removed)
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