Blocks rock

Posted by Jamis on January 19, 2007 @ 01:03 PM

Blocks are one of Ruby’s greatest assets. Every newcomer to Ruby is immediately introduced to their power when they learn how to do basic iteration, but there is a more powerful aspect to blocks than that. They are invaluable in refactoring code to remove duplication. Consider the classic example of the difference assertions proposed by the Projectionists. You want to know whether a particular bit of code increases the number of rows in a particular table or not. The brute-force approach is not very DRY at all:

1
2
3
4
5
6
7
8
9
10
11
12
13
def test_create_user
  count = User.count
  User.create(...)
  assert_equal count+1, User.count
end

def test_delete_user
  count = User.count
  users(:jamis).destroy
  assert_equal count-1, User.count
end

# ad nauseam...

By recognizing that the inner code is all that changes significantly, you can write a helper method that executes the outer code, and yields at the appropriate place(s):

1
2
3
4
5
6
7
8
9
10
11
12
13
14
# from http://project.ioni.st/post/218#post-218
def assert_difference(object, method, difference=1)
  initial_value = object.send(method)
  yield
  assert_equal initial_value + difference, object.send(method)
end

def test_create_user
  assert_difference(User, :count) { User.create(...) }
end

def test_delete_user
  assert_difference(User, :count, -1) { users(:jamis).destroy }
end

This can be handy in a variety of situations. I recently was working on a method that did a bunch of setup and teardown, and in the middle it invoked find.

1
2
3
4
5
6
def find_with_extra_stuff(*args)
  # a bunch of setup code here
  result = some_collection.find(*args)
  # a bunch of teardown code here
  return result
end

The problem was that I found myself wanting to do a variation on the find call, but with the exact same setup and teardown stuff. Rather than duplicate all the code, I refactored it into a separate method and used yield right there in the middle:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
def find_with_extra_stuff(*args)
  setup_with_extra_stuff { |collection| collection.find(*args) }
end

def setup_with_extra_stuff
  # a bunch of setup code here
  result = yield some_collection
  # a bunch of teardown code here
  return result
end

# this allows me to use that abstract method with any
# method of the collection I want:
setup_with_extra_stuff { |c| c.find_all_by_name("Joe") }

Blocks rock.

Posted in Tips & Tricks

Comments

Have something to add? Click here to leave a comment.

19 Jan 2007

1. Pat Maddox said...

Yeah, that’s an example of what Kent Beck calls the “execute around method” pattern in his smalltalk best practice patterns book.

When I first started with Ruby, I didn’t have any trouble understanding blocks, but I had a tough time identifying good places to use them in my own code. Once it clicks though, it’s pretty powerful.

To write code that uses blocks, you have to identify what parts you can do best, and what parts are best left to some client code. It helped for me to think of this code:

1
2
3
4
5
6
7

  def setup_with_extra_stuff(obj, method)
    # a bunch of setup code here
    result = obj.send(method, some_collection)
    # a bunch of teardown code here
    return result
  end

That example will probably be useful to some, and not useful to others. Since I of course had a lot more experience with methods than with blocks, it demonstrated to me how I might want to run some of my own code, and let someone else pass in their own code (in the form of an arbitrary method call) to execute. From there it’s an easy transition to blocks. It’s essentially the same concept at work, but with blocks it’s a lot cleaner.

My only gripe with Ruby is that you can’t pass multiple blocks to methods. You can do it by calling lambda and passing those objects in, but it doesn’t feel very clean.

2. Jamis said...

I really must read that book. Thanks for the reminder, Pat! And yah, I agree, it’d be lovely if Ruby allowed multiple blocks to be passed in. I’ve wanted that several times.

3. Jon Maddox said...

Excellent and poignant.

It was the assert_difference method that actually made blocks click for me. I’ve been loving them ever since. Hunting down repetitious code for places to use them.

4. Seth said...

Thanks for the write-up Jamis. That’s the clearest explanation I’ve seen of how blocks can be useful.

Not exactly block related, but my favorite use of yield goes something like this…

In all of my layouts I define this in the header tags…



<html>
  <head>
    ...js & css stuff here
    <%= yield :header %>
  </head>
</html>

This lets me insert JS, css, or whatever into the head of my layout from views later on like so…



<% content_for :header do %>
  <%= javascript_include_tag 'my_js' %>
<% end %>

The JS gets inserted into the header where it should be.

20 Jan 2007

5. Michael Schuerig said...

I’m going off on a tangent here. I’m currently doing something very similar to what Seth does, but I’d like to package the

<%= yield :header %>

in a helper method. Something like

<%= insert_headers %>

I have to say that I’m stumped. I don’t see how the helper method could do the yield.

6. Vinicius Teles said...

Jamis, I’ve found this kind of problem very often before, but as I used to program in Java, I had a hard time to solve it elegantly. Now, using Ruby, I know that blocks can be very helpful for that, but I’m still struggling with them. This example really clarifies. Thanks a lot!

7. Jamis said...

Michael, the way it works is that yielding :header simply returns the contents of the @contents_for_header instance variable. Thus:

1
<%= yield :header

is really the same as

1
<%= @contents_for_header %>

Thus, if your insert_headers helper method simply returns the @contents_for_header instance variable, you’ll get the same effect as yielding.

8. Michael Schuerig said...

Jamis, thanks for “approving” that solution. I’m still a bit hesitant as it accesses the contents in a way that is deprecated for public consumption. Anyway, it’s not going to break, I gather.

9. Jamis said...

Michael, actually, I cannot guarantee it will never break, because as you noted accessing instance variables is not recommended. However, if you can’t use the ‘yield’ interface for whatever reason, that might be your only option.

There may be a totally different way to tackle the issue, though. Why can’t you just do “yield :header”? Does your insert_headers helper do something more than just return the @content_for_header variable?

10. Michael Schuerig said...

Jamis, the helper does hardly anything, it only wraps a div around the content. It’s more a point about semantics. I’m using another helper to collect content for tooltips. This helper is implemented by means of content_for. However, users of my helper method don’t and shouldn’t have to know anything about its implementation. It would puncture the abstraction, if users had to insert the collected tooltip content with

<%= yield :tooltips %>

Providing a method so that they can write

<%= tooltips %>

sustains the abstraction.

11. Jamis said...

Ah, ok. In that case, here’s a way to work around the explicit use of instance variables. Create a partial (e.g., “shared/_tooltips.rhtml”), and put all your markup in there, including the call to yield:

1
2
3
<div id="tooltips" class="tooltips">
  <%= yield :tooltips %>
</div>

Then, your tooltips helper becomes a trivial call to render:

1
2
3
def tooltips
  render :partial => "shared/tooltips"
end

Does that work for you, Michael?

12. Michael Schuerig said...

Thanks, that’s the right idea. I’m not using a partial, though, render :inline does it just fine.

21 Jan 2007

13. Alan Francis said...

Spooky! I was just working on a bunhc of tests that checked the child count went up or down and I wrote a comment to myself to replace them with just this very pattern in the next session -)—A.

14. DanH said...

Why do so many bloggers get a hard-on over this? I was doing this with Clipper back in 90.

15. Jamis said...

DanH, sure, and Smalltalk was doing it decades earlier than that. It doesn’t mean that everyone knows what it is, or how to do it. It also doesn’t mean the technique is less useful, because it’s “old”.

If you feel the need to tout your superiority, please do it elsewhere. Thanks.

22 Jan 2007

16. Debasish Ghosh said...

Isn’t this what the OO guys refer to as the Template Method pattern ? This is wonderful how in a language with more powerful abstractions, some of the design patterns just disappear within the language.