Changing the context in which a block executes

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

"John-Mason P. Shackelford" <john-mason@shackelford.org> schrieb im
Newsbeitrag news:424B14FE.3020000@shackelford.org...

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

=> #<Foo:0x101c8928>

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

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

···

On 2005-03-30, John-Mason P. Shackelford <john-mason@shackelford.org> wrote: