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


Post Footer automatically generated by Add Post Footer Plugin for wordpress.

About Derick Bailey

Derick Bailey is an entrepreneur, problem solver (and creator? :P ), software developer, screecaster, writer, blogger, speaker and technology leader in central Texas (north of Austin). He runs SignalLeaf.com - the amazingly awesome podcast audio hosting service that everyone should be using, and WatchMeCode.net where he throws down the JavaScript gauntlets to get you up to speed. He has been a professional software developer since the late 90's, and has been writing code since the late 80's. Find me on twitter: @derickbailey, @mutedsolutions, @backbonejsclass Find me on the web: SignalLeaf, WatchMeCode, Kendo UI blog, MarionetteJS, My Github profile, On Google+.
This entry was posted in Ruby. Bookmark the permalink. Follow any comments here with the RSS feed for this post.
  • http://twitter.com/vcr2 Victor Rodrigues

    the new ruby 1.9 hash syntax also helps cleaning this: something foo: “bar” 

    • http://mutedsolutions.com Derick Bailey

      i think that just changes up the key / value relational syntax. it doesn’t change whether or not you are using { } braces

  • http://twitter.com/GavinStark Gavin Stark

    That method could also be rewritten to avoid the need for dealing with local variables and closures.  I believe this approach would work:


    def get_scored_value(key, code)

    # this will either return the first test that exists and has a code, or nil if none qualify 

    found_test = @tests.detect { |test| test && test[code] } 

    # if we found a test and it has a value, use it, or use "NA" 

    value = (found_test && found_test.value) || "NA" 

    ScoredValue.new(value, key)

    end

    I often have to remind myself to go back and look at http://www.ruby-doc.org/core/classes/Enumerable.html when I have a "search for some value" or iteration block that seems more complex than need be.

    • http://mutedsolutions.com Derick Bailey

      nice! i didn’t know about the .detect method. i’ll change my method implementation and see if that works for me. 

    • http://mutedsolutions.com Derick Bailey

      ended up needing more than your suggestion… the .detect method return the object that was detected… in this case, the |test| itself. what i need is a child of the test object, a test result. here’s what i ended up with:

            found_results = @tests.map{ |t| t.results }.flatten.detect { |result|
              result && result.code == code
            }

      it’s using the detect method and avoids the closure… it just took a bit more via mapping the tests into a flat list of results that i could detect.

    • http://mwilden.blogspot.com Mark Wilden

      Ruby is weird sometimes:

      foo = “N/A” unless value
      value = “N/A” unless value

      The first line fails with an exception, but the second one runs just fine. When you’re assigning to value, you can test it for nil first. But if you’re not assigning to it, you can’t.

      • http://twitter.com/GavinStark Gavin Stark

        This is because while the expressions are evaluated in the unless clause first, I believe the interpreter processes left to right and creates the local variable for the *potential* assignment during the parsing phase.  so when    value = “N/A” unless value is parsed at the point where “value=” is read, the local variable exists so that when the statement is *evaluated* it is present, and nil.  In the case of foo = “N/A” unless value, the value variable is never created.

        I remember a discussion about this a while back and I’m sure the Rubinius, JRuby or MRI could do a WAY better job than I at explaining this. There is a fair chance I got part of the subtlety wrong.

        • http://mwilden.blogspot.com Mark Wilden

          The important thing, I guess, is that whatever the reasoning behind it, it’s useful behavior. That’s Ruby for you.