Pass block instead of here document?

I have a situation where I want to send many messages, one after the
other, to single object. Instead of writing, say,

foo.eat burger, fries
foo.drink beer
foo.be_merry

I want to be able to write something like:

foo.perform { eat burger, fries; drink beer; be_merry }

I’m not much at meta-programming. The best I’ve been able to come up
with so far is:

#! /usr/bin/env ruby -w

class Foo
def report
defined_here = (methods - Object.methods).join(", ")
puts “#{self.class.name} defines: " + defined_here
end
def say(text)
puts %[#{inspect} says “#{text}”]
end
def eat(*food)
food = food.join(” and ")
say “Downing #{food}. Yum, yum, yum!”
end
def drink(beverage)
say “Chug, chug, chug”
end
def be_merry
say “What, me worry?”
end
end

module Kernel
def tell(obj, to_do)
obj.instance_eval(to_do)
end
end

tell Foo.new, <<TO_DO
report
eat ‘burger’, ‘fries’
drink ‘beer’
be_merry
TO_DO

Although this works, it offends my sense of programming esthetics.
I’d rather pass in a block. Is there a way to do that? Something like:

module Kernel def tell(obj, &to_do) # what goes here? end end

tell Foo.new do
report
eat ‘burger’, ‘fries’
drink ‘beer’
be_merry
end

Instead of a Kernel#tell method, a Foo#perform method would be OK:

Foo.new.perform { report; eat 'burger', 'fries'; drink 'beer'; be_merry }

Regards, Morton

On 10/25/06, Morton G. [email protected] wrote:

I have a situation where I want to send many messages, one after the
other, to single object. Instead of writing, say,

Foo.new.perform { report; eat 'burger', 'fries'; drink 'beer'; be_merry }

class Foo
def initialize( &block )
return unless block_given?
self.instance_eval &block
end

def eat( *args )
puts args.inspect
end

def drink( *args )
puts args.inspect
end
end

Foo.new do
eat ‘burger’, ‘fries’
drink ‘beer’
end

Is that what you are looking for? You can just as easily create a
process method (instead of using the initialize method) to do
something like this …

f = Foo.new
f.process do
eat ‘burger’, ‘fries’
drink ‘beer’
end

Blessings,
TwP

On Oct 25, 2006, at 3:39 PM, Tim P. wrote:

end
end

Blessings,
TwP

It’s the second way that I was looking for. Now that I know that
Object#instance_eval will accept a block, I can see how easy it is to
write Foo#perform

Thanks for your help.

Regards, Morton

On 10/25/06, Morton G. [email protected] wrote:

I have a situation where I want to send many messages, one after the
other, to single object. Instead of writing, say,

foo.eat burger, fries
foo.drink beer
foo.be_merry

I want to be able to write something like:

foo.perform { eat burger, fries; drink beer; be_merry }

Others have already answered your question, but the above syntax
reminds me of one thing in Smalltalk which I miss a bit in Ruby.

Smalltalk has very little syntax, but one piece is the use of
semicolons to do what Smalltalk calls message cascading. As in Ruby
each method in Smalltalk returns a value and if you write something
like:

obj msg1 msg2

This sends msg1 to obj, and then sends msg2 to the value returned by
obj msg1, much like the equivalent ruby syntax:

obj.msg1.msg2

But in Smalltalk if you write:

obj msg1;msg2

the object referenced by obj is sent msg1 and then msg2 in sequence,
regardless of the value returned by obj msg1.

Since Ruby doesn’ t have such syntax, it’s probably not missed much,
but since Smalltalk did it was pretty heavily used. Smalltalk also
had a method in Object called yourself which returned the receiver
which is commonly used as the last message in such a cascade on the
right hand side of an assignment.


Rick DeNatale

My blog on Ruby
http://talklikeaduck.denhaven2.com/

Rick DeNatale wrote:

But in Smalltalk if you write:

obj msg1;msg2

the object referenced by obj is sent msg1 and then msg2 in sequence,
regardless of the value returned by obj msg1.

Not quite the same, but this is something similar that’s been floating
around the ml for a while:

class Object
def then
yield(self)
self
end
end

hash = Hash.new.
then do |h| h[1] = 2 end.
then do |h| h[3] = 4 end

p hash

That’s much clunkier syntactically, but it is actually useful in one
case: when working with classes whose #new method doesn’t take a block
that allows further configuration of the new object. For example,
sockets:

def socket
@socket ||= UDPSocket.open.then do |s|
s.setsockopt(Socket::SOL_SOCKET, Socket::SO_BROADCAST, true)
s.connect(“192.168.255.255”, 1234)
end
end

It works particularly well with the ||= assignment operator.

Without the #then method you can’t use ||= and it’s just a little
clunkier:

def socket
unless @socket
@socket = UDPSocket.open
@socket.setsockopt(Socket::SOL_SOCKET, Socket::SO_BROADCAST, true)
@socket.connect(“255.255.255.255”, 1234)
end
@socket
end

Rick DeNatale wrote:

Not quite the same, but this is something similar that’s been floating
then do |h| h[1] = 2 end.
@socket ||= UDPSocket.open.then do |s|
s.setsockopt(Socket::SOL_SOCKET, Socket::SO_BROADCAST, true)
s.connect(“192.168.255.255”, 1234)
end
end

Of course this relies on UDPSocket::open returning the new socket
rather than something else.

UDPSocket.open is the same as UDPSocket.new, which returns the new
socket, as is standard for all #new methods.

Actually, UDPSocket does take a block, so you don’t need this
construct here. It’s for those cases (getting rarer and rarer as ruby
libs evolve) where someone defines an #initialize method that does not
take a block (or takes a block but does not yield the object to it).

It works particularly well with the ||= assignment operator.

Which in turn relies on the block evaluating to the socket as well.

Actually, no. The value of the block is discarded, and #then returns the
receiver. That’s the point of the #then method. It yields self and then
returns self.

It’s more of a variation on Smalltalk’s yourself than a cascade.

Nice nonetheless.

Thanks!

On 10/26/06, Joel VanderWerf [email protected] wrote:

around the ml for a while:
then do |h| h[3] = 4 end
s.connect(“192.168.255.255”, 1234)
end
end

Of course this relies on UDPSocket::open returning the new socket
rather than something else.

It works particularly well with the ||= assignment operator.

Which in turn relies on the block evaluating to the socket as well.

It’s more of a variation on Smalltalk’s yourself than a cascade.

Nice nonetheless.


Rick DeNatale

My blog on Ruby
http://talklikeaduck.denhaven2.com/

Rick DeNatale wrote:

construct here. It’s for those cases (getting rarer and rarer as ruby
libs evolve) where someone defines an #initialize method that does not
take a block (or takes a block but does not yield the object to it).

Or does something else with the block like

Hash::new

which captures a block if given and uses it to handle requests for
missing keys.

Right. I shouldn’t have suggested that all #initialize methods should
yield the object for further configuration, since there are plenty of
other legitimate uses for blocks, such as this one.

On 10/26/06, Joel VanderWerf [email protected] wrote:

Rick DeNatale wrote:

Of course this relies on UDPSocket::open returning the new socket
rather than something else.

UDPSocket.open is the same as UDPSocket.new, which returns the new
socket, as is standard for all #new methods.

Actually, UDPSocket does take a block, so you don’t need this
construct here. It’s for those cases (getting rarer and rarer as ruby
libs evolve) where someone defines an #initialize method that does not
take a block (or takes a block but does not yield the object to it).

Or does something else with the block like

Hash::new

which captures a block if given and uses it to handle requests for
missing keys.

It works particularly well with the ||= assignment operator.

Which in turn relies on the block evaluating to the socket as well.

Actually, no. The value of the block is discarded, and #then returns the
receiver. That’s the point of the #then method. It yields self and then
returns self.

Makes sense.


Rick DeNatale

My blog on Ruby
http://talklikeaduck.denhaven2.com/

This forum is not affiliated to the Ruby language, Ruby on Rails framework, nor any Ruby applications discussed here.

| Privacy Policy | Terms of Service | Remote Ruby Jobs