I use this idiom from time to time:
x = expr.tap{|value| break expr-involving-value)}
It works well, but reading it requires the reader to understand a
relatively obscure part of ruby, namely the behavior of break during a
yield.
Here's my most recent case where the idiom useful: I've got a list of
model object ids, and I want produce an array of the model objects in
the order their ids appear in that list.
Obviously, this works:
id_list.map{|id| Model.find(id)}
Here's an approach that does a single query:
unsorted = Model.where(id: id_list)
objs_map = unsorted.reduce(Hash.new) {|h,o| h[o.id] = o; h}
sorted = id_list.map{|id| objs_map[id]}
Here's the same thing using tap/break, to make it more obvious that the
only thing I'm really interested in is that final value "sorted":
sorted = Model.where(id: id_list).tap do |unsorted|
break unsorted.reduce(Hash.new) {|h,o| h[o.id] = o; h}
end.tap do |objs_hash|
break id_list.map{|id| objs_hash[id]}
end
I find enough uses for this idiom that I'm thinking it'd be worth giving
it a name and its own Kernel method, rather than forcing the use of a
relatively narrowly understood language feature (behavior of break
during yield). "pipe" or "transform" may be a good name for it.
Definition would be almost identical to that of tap.
Kernel.module_eval do
def pipe
yield self
end
end
Then the example above turns into:
sorted = Model.where(id: id_list).pipe do |unsorted|
unsorted.reduce(Hash.new) {|h,o| h[o.id] = o; h}
end.pipe do |objs_map|
id_list.map{|id| objs_hash[id]}
end
Any thoughts?
···
--
Posted via http://www.ruby-forum.com/.