Created
October 17, 2011 22:00
-
-
Save heftig/1293960 to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
module MultiBlock | |
class NoBlockError < StandardError; end | |
class Catcher < BasicObject | |
def initialize(&blk) | |
@self = blk.binding.eval "self" | |
@blocks = {} | |
@mode = :catch | |
instance_eval &blk | |
@mode = :call | |
end | |
def method_missing(sym, *args, &blk) | |
if @mode == :catch | |
@blocks[sym] = blk | |
else | |
@blocks.include? sym or | |
::Kernel.raise NoBlockError.new "undefined block '#{sym}'" | |
@self.instance_exec *args, &@blocks[sym] | |
end | |
end | |
def to_proc | |
::Proc.new { self } | |
end | |
end | |
private | |
def multi_block(meth) | |
old_meth = instance_method meth | |
define_method meth do |*args, &blk| | |
old_meth.bind(self).call *args, &Catcher.new(&blk) | |
end | |
end | |
def multi_block_singleton(meth) | |
old_meth = method meth | |
define_singleton_method meth do |*args, &blk| | |
old_meth.call *args, &Catcher.new(&blk) | |
end | |
end | |
end | |
if $0 == __FILE__ | |
# Can be used in a variety of ways: | |
class Foobar | |
extend MultiBlock | |
def self.yield_singleton | |
yield.foo "1" | |
yield.bar "2" | |
end | |
multi_block_singleton :yield_singleton | |
def yield_normal | |
yield.foo "3" | |
yield.bar "4" | |
end | |
multi_block :yield_normal | |
def unwrapped(&blk) | |
cb = MultiBlock::Catcher.new &blk | |
cb.foo "5" | |
cb.bar "6" | |
end | |
def with_cb_arg(cb) | |
cb.foo "7" | |
cb.bar "8" | |
end | |
end | |
Foobar.yield_singleton do | |
foo { |foo| puts "Singleton foo: #{foo}" } | |
bar { |bar| puts "Singleton bar: #{bar}" } | |
end | |
fb = Foobar.new | |
fb.yield_normal do | |
foo { |foo| puts "Normal foo: #{foo}" } | |
bar { |bar| puts "Normal bar: #{bar}" } | |
end | |
fb.unwrapped do | |
foo { |foo| puts "Unwrapped foo: #{foo}" } | |
bar { |bar| puts "Unwrapped bar: #{bar}" } | |
end | |
callbacks = MultiBlock::Catcher.new do | |
foo { |foo| puts "Callback foo: #{foo}" } | |
bar { |bar| puts "Callback bar: #{bar}" } | |
end | |
fb.with_cb_arg(callbacks) | |
### :o | |
stub = MultiBlock::Catcher.new do | |
hello { |x| puts "Hello #{x}!" } | |
end | |
stub.hello "World" | |
end |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Code to pass multiple blocks to a Ruby method. Someone linked me to the CodeBrawl.com contest, but too late to enter. :-(
Hacked this together in about 2 hours. Became surprisingly flexible. Blocks passed this way should act just like a regular block. Putting other code into the outer block doesn't make sense at the moment, but maybe I'll find a use for that.