Calling methods from within a block

D

Daniel Schierbeck

Hi lads!

I'm looking to do something like this:

Doc.new do
foo do
bar { "value of doc -> foo -> bar" }
buz { "another text node" }
end

baz do
biz { "some text" }
end
end

So that the methods called (biz, bar, buz and baz) are called on the
same objects as the method to which they belong.

foo should be called on the Doc object, as should baz. bar and buz
should be called on an object returned by foo, etc. etc.

What I'm looking for is a way to avoid having to pass "self" to the
blocks all the time.


Daniel Schierbeck
 
J

James Edward Gray II

Hi lads!

I'm looking to do something like this:

Doc.new do
foo do
bar { "value of doc -> foo -> bar" }
buz { "another text node" }
end

baz do
biz { "some text" }
end
end

So that the methods called (biz, bar, buz and baz) are called on
the same objects as the method to which they belong.

foo should be called on the Doc object, as should baz. bar and buz
should be called on an object returned by foo, etc. etc.

What I'm looking for is a way to avoid having to pass "self" to the
blocks all the time.

Instead of yielding to the blocks, instance_eval() them. That will
set self up for the call.

James Edward Gray II
 
M

Malte Milatz

Daniel said:
I'm looking to do something like this:

Doc.new do
foo do
bar { "value of doc -> foo -> bar" }
buz { "another text node" }
end
end

The syntax you used could be achieved via instance_eval, which, however,
isn't the prefered way to do it. Another API trick (not such a nice one
either, however):

Doc.new do |x|
x.foo
end

Malte
 
D

David Holroyd

Hi lads!

I'm looking to do something like this:

Doc.new do
foo do
bar { "value of doc -> foo -> bar" }
buz { "another text node" }
end

baz do
biz { "some text" }
end
end

Something I've done which parallels that is to use an instance method
instead of a block, and to include the methods (of Doc, in your example)
from a module,

----8<----
class MyDocBuilder
include Doc

def build
foo do
bar { "value of doc -> foo -> bar" }
buz { "another text node" }
end

baz do
biz { "some text" }
end
end
end

MyDocBuilder.new.build
---->8----


dave
 
J

James Edward Gray II

The syntax you used could be achieved via instance_eval, which,
however,
isn't the prefered way to do it.

Hey, if it works, it works. instance_eval() is a great tool, though
there are some minuses to it. Mainly the following does not parse as
intended:

MyObject.new do
a_method_ending_in_equals_on_my_object = :whatever
end

Ruby will see that as a local variable assignment, not a method
call. You must add a self. prefix, or avoid methods ending in =s.

Also, you lose the context of where the call is made from. Instance
variables and method calls will all target the new self, not the
calling location.

These minuses mean instance_eval() isn't perfect in all cases, but if
you are aware of the gotchas you can use it to do some very clever
interface work, in my opinion.

In summary, preferred or not, I like it! ;)

James Edward Gray II
 
M

Malte Milatz

James said:
These minuses mean instance_eval() isn't perfect in all cases, but if you
are aware of the gotchas you can use it to do some very clever interface
work, in my opinion.

In summary, preferred or not, I like it! ;)

(One more gotcha/feature is that instance_eval doesn't prevent you from
calling private methods.)

Well, in my opinion it's nice when used on a literal block, however I think
it isn't great API design if the API user has to know about the fact that
the block will be evaluated using instance_eval. For example, if I happen to
read foreign code with something like this:

anObject.aMethod { anotherMethod }

Then I may be searching a long time for the definition of anotherMethod,
because I didn't have the idea that the block will be instance_eval'ed from
anObject.

However, readers of this be aware that I'm not really involved in Ruby
philosophy, so my opinion might not be relevant in any way.

Malte
 
J

James Edward Gray II

(One more gotcha/feature is that instance_eval doesn't prevent you
from
calling private methods.)

I actually consider that a feature. Sometimes I'll define some
private initialization only methods, then instance_eval() a block in
the constructor to provide access to them at the right time.

It's just another tool in the toolbox. Beware the limitations, but
don't be afraid to use it, I say!

James Edward Gray II
 
J

Jim Weirich

James Edward Gray II said:
Instead of yielding to the blocks, instance_eval() them. That will
set self up for the call.S

instance_eval will work, but be careful. It changes the values of self
within the block making the code inside the block act differently than
code outside the block. This is OK for small snippets of code, but I hav=
e
found it confusing when the block is larger and tends to contain arbitrar=
y
logic. It particularly confusing when you try to call methods that are
defined in the object containing block (for they get directed to a
different object by default).

See http://onestepback.org/index.cgi/Tech/Ruby/StayingSimple.rdoc for
details where I switched back from an instance_eval to a normal yield.


--=20
-- Jim Weirich (e-mail address removed) http://onestepback.org
 

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

No members online now.

Forum statistics

Threads
473,995
Messages
2,570,230
Members
46,817
Latest member
DicWeils

Latest Threads

Top