Ruby's syntax doesn't easily allow for passing multiple blocks to a method. This can sometimes make for awkward interfaces. Ruby's find()
iterator has this problem when you choose to pass a block for handling the "not found" condition.
MultiBlock solves this problem by allowing you to pass multiple blocks to a method by name. Here's the find()
example rewritten using MultiBlock:
require "multi_block"
module Enumerable
def multi_block_find(blocks)
find(blocks.if_not_found, &blocks.test)
end
multi_block :multi_block_find, :test, :if_not_found
end
enum = [1, 2, 3]
# Ruby's find()
p enum.find(&:even?)
p enum.find { |n| n > 3 }
begin
enum.find(-> { fail "Not found" }) { |n| n > 3 }
rescue => error
puts error.message
end
# using MultiBlock
begin
enum.multi_block_find do |blocks|
blocks.test { |n| n > 3 }
blocks.if_not_found { fail "Not found" }
end
rescue => error
puts error.message
end
As you can see above, you just call multi_block()
with the name of the method you want to take multiple blocks. You can optionally pass the block names to accept or just leave them out to allow for any block name. The blocks will be passed in a single argument added to the end of the arguments list. Blocks are accessed by calling a method named after the block.
You can call multi_block()
on instance methods, mix-in methods, top-level methods, and class/module methods through singleton_class()
:
module SomeModule
def self.some_module_method
# ...
end
singleton_class.multi_block :some_module_method
end
This code is in the Public Domain. You are free to use it in any way that you like.
Only complaint is that this requires you to define the method using some special metaprogramming. Also, a minor nit is that I'm not sure why you need @block_names when you have @blocks in MultiBlock.