Hello and welcome to the restorm technical blog. It is here that we post about what is happening in our labs, what keeps us busy and why X doesn't work. Read more about us on the about page.
Tending to the blog and other crazy stuff
Let’s assume you want to call #capture(&block) somewhere out of view context. Like so (caution, haml ahead):
= form.radio_button_group_with_label 'How\'s the surf?' do |button|
= button.create 'IE 6'
= button.create 'IE 7'
= button.create 'A real Browser'
Let’s look at how we might do this:
def radio_button_group(label, field, &block)
builder = RadioButtonGroup.new(self, field)
label_tag = content_tag('label', label)
content_tag = ''
proc {
content_tag = capture(builder, &block)
}.bind(eval('self', block.binding)).call
return label_format(
label_tag,
content_tag
)
end
If you look beside the boilerplate that is for formatting and other irrelevant stuff (coder attitude forever!), you see a central snippet that has a bizarre beauty about its airs:
proc {
content_tag = capture(builder, &block)
}.bind(eval('self', block.binding)).call
We create a block using #proc, #bind something to it (as a new execution context) and then #call it. So far.. so good. This executes the block in the content of the view, which is needed for easy #capture access. Now what is the #eval(...) about? Well, turns out that Proc#bind() will only accept an object instance as an argument, no binding. Even though that would be logical, would it not?
So admire the thing in all its hideousness – and see the equivalence to
eval('self', block.binding).instance_eval do
# ...
end
Or, using facets
block.binding.self.instance_eval do
# ...
end
Let’s assume for a second that Proc#bind would take a binding as argument. How would that transform the piece?
proc do
# ...
end.bind(block.binding).call
A lot cleaner already. Here’s the piece of magic that allows you to do that:
class Proc
def bind_correct(self_or_binding)
if self_or_binding.kind_of? Binding
bind_incorrect eval('self', self_or_binding)
else
bind_incorrect self_or_binding
end
end
alias :bind_incorrect :bind
alias :bind :bind_correct
end
As always, have fun. The interested reader should take a look at the implementation of Proc#bind in active_support. Clever, that.
yours truly, kaspar