Wednesday, July 12, 2006

Introduction to Bindings in Ruby

When it comes to programming languages, software developers really are zealots--everyone has their favorites. Like Paul Graham says about himself, you shouldn't just listen to a person just because they have notoriety. But I think his article about how some programming languages are more powerful than others is convincing (can't find the link right now).

What I liked about Ruby over Java or C++ most are the different types of programming ideas that I've had to wrap my head around. Just yesterday, I was looking at Ruby's ERB (Embedded RuBy) library. It executes ruby embedded in text (plain text or html). Most commonly, Rails uses ERB as a way to translate view templates into html to be displayed to the browser.

I had the problem of trying to render templates outside of controllers, which was why I was looking at ERB. I couldn't get the ERB to read the variables I had defined in the function. I mean...that's the way it worked in the controllers!

@title = "The web and all that jazz"
template = "<html><body><%= @title %></body></html>"
erb = ERB.new(template)
html = erb.result

It didn't take. It ends up that you'd have to pass ERB the current binding as well, because it assumes TOPLEVEL_BINDING. I think ERB uses eval() heavily, which in turn uses binding.

html = erb.result(binding)

binding() is a kernel method that returns the binding--essentially the scope which code is being executed. Not only that, binding() returns the binding as an object, so you can store it for future use, and even pass it around. That means that you can request the scope of a block of code (the variables that are available to it) at that time in execution, and use it at another time, when that scope might have already expired.

So in the erb example, result() was assuming scope at the very top level of the application, rather than the local scope. And that's why it couldn't find @title.

I only found one extensive article on binding. It has great examples on binding.

Being able to pass around bindings seems odd to me. It's like being able to pass scope around, and I wonder if it violates encapsulation...or maybe it's ok to violate it once in a while? But then again, I remembered that closures and blocks in ruby also have bindings. The scope in which a block is define is remembered, and can be passed around; and it's part of their power. So every time you do an array each() and pass it a block, you're passing around bindings as well. It's so much easier than messing around with function pointers.

3 comments:

  1. Anonymous1:06 AM

    I just get this error message:

    undefined method `results' for ActionView::TemplateHandlers::ERB:0x4bb9e60>

    can't post my code sorry:
    Your HTML cannot be accepted: Tag is not allowed:
    smart!!

    ReplyDelete
  2. Anonymous8:50 AM

    The method is called 'result', not 'results'.

    ReplyDelete