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

Nov 11, 2007 | 0 comments

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