A typical idiom for constructors is to do "yield self if block_given?",
which I prefer over hash based arguments. For example:
class Foo
attr_accessor :bar, :baz, :zap
def initialize
yield self if block_given?
end
end
foo = Foo.new do |f|
f.bar = "hello"
f.baz = 5
f.zap = "world"
end
Many thanks to Hal Fulton's "The Ruby Way" for teaching me this trick.

Regards,
Dan
···
-----Original Message-----
From: Stephan Mueller [mailto:d454d@web.de]
Sent: Saturday, August 06, 2005 12:01 PM
To: ruby-talk ML
Subject: polymorphism and/or named parameters: the ruby way?
Hello,
i am kind of a newbi, please forgive me if i am asking stale
questions.
I want to adjust the initialization of an object according to
the parameters given to new.
What's the advantage of this over:
foo = Foo.new
foo.bar = "hello"
foo.baz = 5
foo.zap = "world"
Is it method chaining? You want this thing to return foo? You
could just end this statement list with "foo", or you could use
"ensure":
begin
foo = Foo.new
ensure
foo.bar = "hello"
foo.baz = 5
foo.zap = "world"
end
This begin..ensure..end expression returns foo (the result
before ensure) not "world" as you might expect. Great for
doing post operations after you calculate a return value
(equivalent of C's i++ is another example).
···
--- "Berger, Daniel" <Daniel.Berger@qwest.com> wrote:
> -----Original Message-----
> From: Stephan Mueller [mailto:d454d@web.de]
> Sent: Saturday, August 06, 2005 12:01 PM
> To: ruby-talk ML
> Subject: polymorphism and/or named parameters: the ruby
way?
>
>
> Hello,
>
> i am kind of a newbi, please forgive me if i am asking
stale
> questions.
>
> I want to adjust the initialization of an object according
to
> the parameters given to new.
A typical idiom for constructors is to do "yield self if
block_given?",
which I prefer over hash based arguments. For example:
class Foo
attr_accessor :bar, :baz, :zap
def initialize
yield self if block_given?
end
end
foo = Foo.new do |f|
f.bar = "hello"
f.baz = 5
f.zap = "world"
end
____________________________________________________
Start your day with Yahoo! - make it your home page
http://www.yahoo.com/r/hs
Eric Mahurin wrote:
...
class Foo
attr_accessor :bar, :baz, :zap
def initialize
yield self if block_given?
end
end
foo = Foo.new do |f|
f.bar = "hello"
f.baz = 5
f.zap = "world"
end
What's the advantage of this over:
foo = Foo.new
foo.bar = "hello"
foo.baz = 5
foo.zap = "world"
One advantage is you can construct and configure an object without
assigning to a local variable. This can be useful if you are
constructing a bunch of objects in #map or in literal hashes or arrays
or (this is a bit far-fetched, but why not) parameter lists. And it is
nice not to add a symbol to the namespace. I particularly like the fact
that if you are doing several of these, possibly even in different
methods, you can use the same |f| parameter and cut and paste between
the assignments:
def make_foos
foo1 = Foo.new do |f|
f.bar = "BAR"
end
foo2 = Foo.new do |f|
f.bar = "ZAP"
end
end
That has a nice standardized look to me, especially when you are
populating many fields in several different methods.
Is it method chaining? You want this thing to return foo? You
could just end this statement list with "foo", or you could use
"ensure":
begin
foo = Foo.new
ensure
foo.bar = "hello"
foo.baz = 5
foo.zap = "world"
end
Danger Will Robinson. An exception may happen in Foo.new....
This begin..ensure..end expression returns foo (the result
before ensure) not "world" as you might expect. Great for
doing post operations after you calculate a return value
(equivalent of C's i++ is another example).
You can use an else clause to return a value from a begin..end block,
but only if there is at least one rescue clause (or else you get a warning).
p(
begin
a =
a << 1
a << 2
rescue
else
a
end
)
# ==> [1, 2]
···
--- "Berger, Daniel" <Daniel.Berger@qwest.com> wrote:
--
vjoel : Joel VanderWerf : path berkeley edu : 510 665 3407
Eric Mahurin wrote:
What's the advantage of this over:
foo = Foo.new
foo.bar = "hello"
foo.baz = 5
foo.zap = "world"
1. To avoid, having to call a configure method after configuration parameters have been set (or before certain instance methods are called),
2. to guarantee, that no Foo objects are created, that aren't configured correctly.
Consider this contrived example:
class Foo
FIELDS = %w[bar baz zap]
attr_accessor(*FIELDS)
def initialize
if block_given?
yield self
configure
else
raise ArgumentError, "configuration required!"
end
end
def configure
for f in FIELDS
__send__(f) or raise ArgumentError, "configuration for #{f} missing!"
end
@greeting = [ @bar ] * @baz * ', ' + " #@zap!"
end
def greet
puts @greeting
end
end
f = Foo.new do |f|
f.bar = "hello"
f.baz = 5
f.zap = "world"
end
f.greet
After the configuration block has been executed, f is guaranteed to be a correctly configured Foo instance. (At least no parameter has been forgotten, but further checks are possible as well.)
Perhaps you want to create and use an object without even assigning it to a variable:
Message do |mes|
# build message here...
mes.send
end
James Edward Gray II
···
On Aug 8, 2005, at 12:21 PM, Eric Mahurin wrote:
--- "Berger, Daniel" <Daniel.Berger@qwest.com> wrote:
A typical idiom for constructors is to do "yield self if
block_given?",
which I prefer over hash based arguments. For example:
class Foo
attr_accessor :bar, :baz, :zap
def initialize
yield self if block_given?
end
end
foo = Foo.new do |f|
f.bar = "hello"
f.baz = 5
f.zap = "world"
end
What's the advantage of this over:
foo = Foo.new
foo.bar = "hello"
foo.baz = 5
foo.zap = "world"
The exception issue bothers me too with this ensure trick.
But, this else doesn't really help. I was wanting something
that appplied some post operations after you determine the
return value. For example, the equivalent to the C expression
a[i++] is the following using the ensure "operator":
a[begin i ensure i+=1 end]
I've benchmarked various ways of doing this and this method is
the clear winner.
Functionally, I guess something like this would be most
flexible (no performance advantage like "ensure"):
class Object
def post
yield(self)
self
end
end
Then you could do these types of things:
a[i.post{i+=1}]
foo = Foo.new.post do |f|
f.bar = "hello"
f.baz = 5
f.zap = "world"
end
With this, no need for the "yield(self) if block_given?" in
initialize.
This very useful little method is a nice thing to have in your
bag-o-tricks.
···
--- Joel VanderWerf <vjoel@path.berkeley.edu> wrote:
Eric Mahurin wrote:
> --- "Berger, Daniel" <Daniel.Berger@qwest.com> wrote:
...
>>class Foo
>> attr_accessor :bar, :baz, :zap
>> def initialize
>> yield self if block_given?
>> end
>>end
>>
>>foo = Foo.new do |f|
>> f.bar = "hello"
>> f.baz = 5
>> f.zap = "world"
>>end
>
>
> What's the advantage of this over:
>
> foo = Foo.new
> foo.bar = "hello"
> foo.baz = 5
> foo.zap = "world"
One advantage is you can construct and configure an object
without
assigning to a local variable. This can be useful if you are
constructing a bunch of objects in #map or in literal hashes
or arrays
or (this is a bit far-fetched, but why not) parameter lists.
And it is
nice not to add a symbol to the namespace. I particularly
like the fact
that if you are doing several of these, possibly even in
different
methods, you can use the same |f| parameter and cut and paste
between
the assignments:
def make_foos
foo1 = Foo.new do |f|
f.bar = "BAR"
end
foo2 = Foo.new do |f|
f.bar = "ZAP"
end
end
That has a nice standardized look to me, especially when you
are
populating many fields in several different methods.
> Is it method chaining? You want this thing to return foo?
You
> could just end this statement list with "foo", or you could
use
> "ensure":
>
> begin
> foo = Foo.new
> ensure
> foo.bar = "hello"
> foo.baz = 5
> foo.zap = "world"
> end
Danger Will Robinson. An exception may happen in Foo.new....
> This begin..ensure..end expression returns foo (the result
> before ensure) not "world" as you might expect. Great for
> doing post operations after you calculate a return value
> (equivalent of C's i++ is another example).
You can use an else clause to return a value from a
begin..end block,
but only if there is at least one rescue clause (or else you
get a warning).
p(
begin
a =
a << 1
a << 2
rescue
else
a
end
)
# ==> [1, 2]
____________________________________________________
Start your day with Yahoo! - make it your home page
http://www.yahoo.com/r/hs
Yep, that makes more sense. It looks a lot more useful when
you do some operations after the yield. Kind of like the
reason the IO.open block form is useful - it does the close for
you.
···
--- Florian Frank <flori@nixe.ping.de> wrote:
Eric Mahurin wrote:
>What's the advantage of this over:
>
>foo = Foo.new
>foo.bar = "hello"
>foo.baz = 5
>foo.zap = "world"
>
>
1. To avoid, having to call a configure method after
configuration
parameters have been set (or before certain instance methods
are called),
2. to guarantee, that no Foo objects are created, that aren't
configured
correctly.
Consider this contrived example:
class Foo
FIELDS = %w[bar baz zap]
attr_accessor(*FIELDS)
def initialize
if block_given?
yield self
configure
else
raise ArgumentError, "configuration required!"
end
end
def configure
for f in FIELDS
__send__(f) or raise ArgumentError, "configuration for
#{f} missing!"
end
@greeting = [ @bar ] * @baz * ', ' + " #@zap!"
end
def greet
puts @greeting
end
end
f = Foo.new do |f|
f.bar = "hello"
f.baz = 5
f.zap = "world"
end
f.greet
After the configuration block has been executed, f is
guaranteed to be a
correctly configured Foo instance. (At least no parameter has
been
forgotten, but further checks are possible as well.)
____________________________________________________
Start your day with Yahoo! - make it your home page
http://www.yahoo.com/r/hs
You missed a .new, I believe.
I'd still say my Object#post (someone else called it "then")
would be a better thing to have instead of every initialize
doing "yield self if block_given?" at the end. If there was
something done after the yield (like checking or even
closing/destroying the object), then there'd be more use.
class Object
def post
yield(self)
self
end
end
Message.new.post do |mes|
# build message here...
mes.send
end
···
--- James Edward Gray II <james@grayproductions.net> wrote:
On Aug 8, 2005, at 12:21 PM, Eric Mahurin wrote:
> --- "Berger, Daniel" <Daniel.Berger@qwest.com> wrote:
>>
>> A typical idiom for constructors is to do "yield self if
>> block_given?",
>> which I prefer over hash based arguments. For example:
>>
>> class Foo
>> attr_accessor :bar, :baz, :zap
>> def initialize
>> yield self if block_given?
>> end
>> end
>>
>> foo = Foo.new do |f|
>> f.bar = "hello"
>> f.baz = 5
>> f.zap = "world"
>> end
>>
>
> What's the advantage of this over:
>
> foo = Foo.new
> foo.bar = "hello"
> foo.baz = 5
> foo.zap = "world"
Perhaps you want to create and use an object without even
assigning
it to a variable:
Message do |mes|
# build message here...
mes.send
end
____________________________________________________
Start your day with Yahoo! - make it your home page
http://www.yahoo.com/r/hs
Eric Mahurin wrote:
...
Functionally, I guess something like this would be most
flexible (no performance advantage like "ensure"):
class Object
def post
yield(self)
self
end
end
Then you could do these types of things:
a[i.post{i+=1}]
foo = Foo.new.post do |f|
f.bar = "hello"
f.baz = 5
f.zap = "world"
end
With this, no need for the "yield(self) if block_given?" in
initialize.
This very useful little method is a nice thing to have in your
bag-o-tricks.
Nice. you could even call it #then:
class Object
def then
yield(self)
self
end
end
#Then you could do these types of things:
a = (0..10).to_a
i = 3
p a[i.then{i+=1}]
p i
Foo = Struct.new :bar, :baz, :zap
foo = Foo.new.then do |f|
f.bar = "hello"
f.baz = 5
f.zap = "world"
end
p foo
__END__
output:
3
4
#<struct Foo bar="hello", baz=5, zap="world">
···
--
vjoel : Joel VanderWerf : path berkeley edu : 510 665 3407
Perhaps you want to create and use an object without even
assigning it to a variable:
Message do |mes|
# build message here...
mes.send
end
You missed a .new, I believe.
Oops, right you are.
I'd still say my Object#post (someone else called it "then")
would be a better thing to have instead of every initialize
doing "yield self if block_given?" at the end. If there was
something done after the yield (like checking or even
closing/destroying the object), then there'd be more use.
class Object
def post
yield(self)
self
end
end
Message.new.post do |mes|
# build message here...
mes.send
end
Hmm, I don't like that because it moves the management responsibility down to the caller, for no gain that I can see. But, of course, we are both free to create them however we like. 
James Edward Gray II
···
On Aug 18, 2005, at 10:36 AM, Eric Mahurin wrote:
This very useful little method is a nice thing to have in your
bag-o-tricks.
Nice. you could even call it #then:
class Object
def then
yield(self)
self
end
end
#Then you could do these types of things:
a = (0..10).to_a
i = 3
p a[i.then{i+=1}]
p i
[..snip..]
But be carefull:
i = 3
p i.then{i+=1}
p i
i = [3]
p i.then{i << 4}
p i
output:
3
4
[3, 4]
(perhaps this was obvious to all, except me)
cheers
Simon
Simon Kröger wrote:
This very useful little method is a nice thing to have in your
bag-o-tricks.
Nice. you could even call it #then:
class Object
def then
yield(self)
self
end
end
#Then you could do these types of things:
a = (0..10).to_a
i = 3
p a[i.then{i+=1}]
p i
[..snip..]
But be carefull:
i = 3
p i.then{i+=1}
p i
i = [3]
p i.then{i << 4}
p i
output:
3
4
[3, 4]
[3, 4]
(perhaps this was obvious to all, except me)
cheers
Simon
Good point. You really have to be especially clear about whether you are
modifying a variable binding or an object.
i = [3]
p i.then{i+=[4]} # ==> [3]
p i # ==> [3, 4]
···
--
vjoel : Joel VanderWerf : path berkeley edu : 510 665 3407
Joel VanderWerf wrote:
Simon Kröger wrote:
This very useful little method is a nice thing to have in your
bag-o-tricks.
Nice. you could even call it #then:
class Object
def then
yield(self)
self
end
end
#Then you could do these types of things:
a = (0..10).to_a
i = 3
p a[i.then{i+=1}]
p i
[..snip..]
But be carefull:
i = 3
p i.then{i+=1}
p i
i = [3]
p i.then{i << 4}
p i
output:
3
4
[3, 4]
(perhaps this was obvious to all, except me)
cheers
Simon
Good point. You really have to be especially clear about whether you are
modifying a variable binding or an object.
i = [3]
p i.then{i+=[4]} # ==> [3]
p i # ==> [3, 4]
woohoo,
i+=[4] creates a new array, right?!
this is - lets say - 'suboptimal' in terms of speed for the
99.9% of cases where you do not need the old array.
(ok, you should realy use <<, except if you use 'then' -
I would not like to teach someone this)
cheers
Simon
This gets us into the whole destructive vs. non-destructive
argument. << is the efficient destructive version and + is the
non-destructive create-a-new-object, less efficient, but more
flexible version. Non-destructive methods (that return a new
object) don't have the side-effect problem and allow method
chaining in a clean way. It is no different for this "then"
(or "post" as I had it originally) method.
···
--- Simon Kröger <SimonKroeger@gmx.de> wrote:
Joel VanderWerf wrote:
> Simon Kröger wrote:
>
>>>>This very useful little method is a nice thing to have in
your
>>>>bag-o-tricks.
>>>
>>>
>>>Nice. you could even call it #then:
>>>
>>>class Object
>>> def then
>>> yield(self)
>>> self
>>> end
>>>end
>>>
>>>#Then you could do these types of things:
>>>a = (0..10).to_a
>>>i = 3
>>>p a[i.then{i+=1}]
>>>p i
>>>
>>>[..snip..]
>>
>>
>>But be carefull:
>>
>>i = 3
>>p i.then{i+=1}
>>p i
>>
>>i = [3]
>>p i.then{i << 4}
>>p i
>>
>>
>>output:
>>3
>>4
>>[3, 4]
>>[3, 4]
>>
>>
>>(perhaps this was obvious to all, except me)
>>
>>cheers
>>
>>Simon
>
>
> Good point. You really have to be especially clear about
whether you are
> modifying a variable binding or an object.
>
> i = [3]
> p i.then{i+=[4]} # ==> [3]
> p i # ==> [3, 4]
>
woohoo,
i+=[4] creates a new array, right?!
this is - lets say - 'suboptimal' in terms of speed for the
99.9% of cases where you do not need the old array.
(ok, you should realy use <<, except if you use 'then' -
I would not like to teach someone this)
____________________________________________________
Start your day with Yahoo! - make it your home page
http://www.yahoo.com/r/hs