When to use blocks

Viewed 2187

I love Ruby blocks! The idea behind them is just very very neat and convenient.

I have just looked back over my code from the past week or so, which is basically every single ruby function I ever have written, and I have noticed that not a single one of them returns a value! Instead of returning values, I always use a block to pass the data back!

I have even caught myself contemplating writing a little status class which would allow me to write code like :

something.do_stuff do |status|
  status.success do 
    # successful code
  end

  status.fail do
    # fail code
    puts status.error_message
  end
end

Am I using blocks too much? Is there a time to use blocks and a time to use return values?

Are there any gotchas to be aware of? Will my huge use of blocks come and bite me sometime?

3 Answers

The whole thing would be more readable as:

if something.do_stuff
  #successful code
else
  #unsuccessful code
end

or to use a common rails idiom:

if @user.save
  render :action=>:show
else
   @user.errors.each{|attr,msg| logger.info "#{attr} - #{msg}" }
   render :action=>:edit
end

IMHO, avoiding the return of a boolean value is overuse of code blocks.

A block makes sense if . . .

It allows code to use a resource without having to close that resource

 open("fname") do |f|
  # do stuff with the file
 end #don't have to worry about closing the file

The calling code would have to do non-trivial computation with the result

In this case, you avoid adding the return value to calling scope. This also often makes sense with multiple return values.

something.do_stuff do |res1, res2|
   if res1.foo? and res2.bar?
      foo(res1)
   elsif res2.bar?
      bar(res2)
   end
 end #didn't add res1/res2 to the calling scope

Code must be called both before and after the yield

You see this in some of the rails helpers:

 <% content_tag :div do  %>
     <%= content_tag :span "span content" %>
  <% end -%>

And of course iterators are a great use case, as they're (considered by ruby-ists to be) prettier than for loops or list comprehensions.

Certainly not an exhaustive list, but I recommend that you don't just use blocks because you can.

This is what functional programming people call "continuation-passing style". It's a valid technique, though there are cases where it will tend to complicate things more than it's worth. It might be worth to relook some of the places where you're using it and see if that's the case in your code. But there's nothing inherently wrong with it.

I like this style. It's actually very Ruby-like, and often you'll see projects restructure their code to use this format instead of something less readable.

Returning values makes sense where returning values makes sense. If you have an Article object, you want article.title to return the title. But for this particular example of callbacks, it's stellar style, and it's good that you know how to use them. I suspect that many new to Ruby will never figure out how to do it well.

Related