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.
Block names allows the person who invokes the method rewriter to specify which blocks are permitted.