changing the context in which a block executes

  • Thread starter John-Mason P. Shackelford
  • Start date
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
 
R

Robert Klemme

John-Mason P. Shackelford said:
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.

In Ruby you can *always* directly access an instance's state and thus
encapsulation is never really enforced:
class Foo
attr_accessor :bar
end => nil
f=Foo.new
=> # said:
f.bar = 10 => 10
f.instance_variables => ["@bar"]
f.instance_variable_get "@bar"
=> 10

So strictly speaking, you cannont prevent someone from accessing instance
state. There might be ways but they are usually far too complex to bother
to use them.

Kind regards

robert
 
C

Csaba Henk

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
[snip]

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

The block passed to some_method should be called by instance_eval.

class Foo
def printf(x)
45
end
def some_method &b
instance_eval &b
end
end

Foo.new.some_method { printf "a" } # => 45

Csaba
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Members online

Forum statistics

Threads
473,770
Messages
2,569,584
Members
45,075
Latest member
MakersCBDBloodSupport

Latest Threads

Top