We follow Block, who goes into a cloak room and emerges with a change of fashion. A black hat for its dark magic, a dagger for its villianous theft of an instance’s binding, and a glimmering red ring for its metaprogramming fu.
Hey, would you look at that? Blocks actually look pretty good as unbound eigenmethods!
The Cloaker
Let’s say we have an HTML class for building HTML. Here’s the short dressing method:
class HTML
def cloaker &blk
(class << self; self; end).class_eval do
define_method :cloaker_, &blk
meth = instance_method( :cloaker_ )
remove_method :cloaker_
meth
end
end
end
Giving Parents to the Kids
You’re probably used to seeing code by now, in Rails and Builder and other meta-heavy libs, which passes a single class into a block for tooling with.
For example, in Builder, you’ll make an XML file like this:
xml = Builder::XmlMarkup.new :indent => 2
xml.person do |b|
b.name("Jim")
b.phone("555-1234")
end
As you get deeper in the XML file, you pass down each element to its kids using a block variable. In the above example: b.
Now, I ask, what is self in the above example? Well, it’s set to whatever instance is self in the encompassing scope.
Self Theft
Let’s use a cloak so our block can steal the soul of self.
class HTML
TAGS = [:html, :head, :title, :body, :h1, :h2, :h3, :div]
def initialize &blk; html &blk end
def method_missing tag, text = nil, &blk
raise NoMethodError, "No tag `#{tag}'" unless TAGS.include? tag
print "<#{tag}>#{text}"
if blk
cloaker(&blk).bind(self).call
end
print ""
end
end
Now when we attach the block, you’ll be able to run methods freely inside the block, as if you were inside a singleton instance method.
title = "My Love is Like a LaserJet Set at 300 DPI"
sub = "Poetry Selections from Kinko's Employees"
HTML.new do
head do
title title
end
body do
h1 title
h2 sub
div "Oh, Toner! How it doth trickle down the arms!"
end
end
The evil part is how the namespaces wash together. Check out that fifth line. The title method and title var cozy up just fine. A huge problem with this code is that methods in the block’s scope still take precedence. (Try adding: def title; end right before the first line.)
You can use self explicitly in those cases. Anyway, the full test script is here. (Hack inspired by override_method.)