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:
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.