A Couple Of Tips For Ruby Code Blocks


Ruby’s code blocks (a.k.a “lambdas” or anonymous functions in .net) are powerful little tools that get used everywhere, and for good reason. But, every now and then I run into a little trick or issue related to them. For example:

Variable Scoping

Earlier today, I ran into a scoping issue related to ruby code blocks. Can you spot the error here?

def get_scored_value(key, code)

  @tests.each do |test|
    next unless test

    result = test[code]
    if result
      value = result.value
      break
    end

  end

  value = "N/A" unless value
  ScoredValue.new(value, key)
end

It wasn’t obvious to me, for about 30 minutes of banging my head into my keyboard… the `value` variable is scoped to the `do…end` code block because that’s the first place that it gets initialized. Since it was scoped to the code block, the `value = “N/A” unless value` line was always evaluating false and always setting value to “N/A”. This was an easy fix, of course. Just initialize the variable outside of the code block and the magic of closures will take over.

def get_scored_value(key, code)
  value = nil

  @tests.each do |test|
    next unless test

    result = test[code]
    if result
      value = result.value
      break
    end

  end

  value = "N/A" unless value
  ScoredValue.new(value, key)
end

I need to go back and re-read the Metaprogramming Ruby sections on scope gates and blocks. There’s obviously some stuff that I’ve not remembered since the last time I picked up that book.

Code Blocks vs Hashes

This is one that Hugo Bonacci mentioned on twitter a few days ago. He had an issue related to code blocks vs hashes:

Screen shot 2011 06 01 at 1 55 03 PM

The reason why this happens had never occurred to me before, but I have run into this problem in the same way he did on many occasions: code blocks and hashes can both use the { } curly brace to denote their beginning and end.

some_data = { :foo => "bar", :baz => "widget"}

[1..3].each { |i| puts i }

If you have a method that wants a hash as the parameter and you want to specify that hash in-line with the method call, the following will fail:

def something(foo)
  foo.each { |k, v| puts "#{k}: #{v}" }
end

something { :foo => "bar" }

Ruby will interpret this as a code block even though the developer intends it to be a hash, and it will crash:

SyntaxError: (irb):4: syntax error, unexpected tASSOC, expecting '}'
something { :foo => "bar" }

Fortunately, the solution is simple, again. You can either omit the curly braces or wrap the method call with parenthesis:

def something(foo)
  foo.each { |k, v| puts "#{k}: #{v}" }
end

something :foo => "bar"
something({:foo => "bar"})

I prefer to eliminate the curly braces, just to reduce the syntax noise of the method call.

I’m sure there are other little gotcha’s related to code blocks, as well. These are two that I’ve come across recently, though.

Git For Subversion Users: Article And Presentation