There's a few trivial but useful "extensions" to Ruby's standard
library that I find myself using in most of my projects. I'll share a
couple, and I'd really love to see what you're using, too.
Array folding is a functional idiom in which each item is evaluated in
some way and its result accumulated, returning the final value of the
accumulator. It's simple in Ruby:
class Array
def foldl(accum, &block)
each {|value| accum = yield(accum, value)}
return accum
end
def foldr(accum, &block)
reverse.foldl(accum, &block)
end
alias fold :foldl
end
The canonical example is summing values:
r = (1..100).to_a
p r.foldl(0) {|a,v| a += v}
=> 5050
p r.foldr(5050) {|a,v| a -= v}
=> 0
A more interesting and useful application involves boolean logic. For
example, the following snippet ensures all arguments passed to
FooSet#new are of type Foo:
class Foo; end
class FooSet
def initialize(*values)
raise TypeError unless
values.fold(true) {|a,v| a and v.is_a? Foo}
# ...
end
end
Another quickie I'm fond of assists in what Josh Bloch calls
``defensive copying'' in his book ``Effective Java.'' The idea is
that you should always return copies of mutable instance variables if
you aren't prepared to have users of your class modify those
variables.
For instance, if I have a class which contains an array, and I want to
make that array accessible from outside but not modifiable,
attr_reader won't be sufficient -- I need to call Array#dup and return
that. Once again, it's trivial in Ruby to define a helper to do just
that:
class Class
def attr_defender(*symbols)
symbols.each do |symbol|
class_eval "def #{symbol}; @#{symbol}.dup; end"
end
end
end
Here's how I might use it:
class Container
def initialize(*values)
@values = values
@length = values.length
end
attr_defender :values
attr_reader :length
end
Container#values returns a new object each time:
c = Container.new('foo', 'bar', 'baz')
p c.values.__id__
=> 402800548
p c.values.__id__
=> 402800512
So now I can be lazy and do whatever I want with the copy.
Please share your quickies!
Sam