Skip to content

Instantly share code, notes, and snippets.

@heftig
Created October 17, 2011 22:00
Show Gist options
  • Save heftig/1293960 to your computer and use it in GitHub Desktop.
Save heftig/1293960 to your computer and use it in GitHub Desktop.
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
@heftig
Copy link
Author

heftig commented Oct 19, 2011

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.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment