[lnkForumImage]
TotalShareware - Download Free Software

Confronta i prezzi di migliaia di prodotti.
Asp Forum
 Home | Login | Register | Search 


 

Forums >

comp.lang.ruby

changing the context in which a block executes

John-Mason P. Shackelford

3/30/2005 9:06:00 PM

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


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



2 Answers

Robert Klemme

3/31/2005 7:38:00 AM

0


"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

Csaba Henk

4/2/2005 1:45:00 PM

0

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