Blocks rock
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.
Reader Comments
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:
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.
19 Jan 2007
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.
19 Jan 2007
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.
19 Jan 2007
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…
This lets me insert JS, css, or whatever into the head of my layout from views later on like so…
The JS gets inserted into the header where it should be.
19 Jan 2007
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
in a helper method. Something like
I have to say that I’m stumped. I don’t see how the helper method could do the yield.
20 Jan 2007
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!
20 Jan 2007
Michael, the way it works is that yielding
:header
simply returns the contents of the@contents_for_header
instance variable. Thus:is really the same as
Thus, if your
insert_headers
helper method simply returns the@contents_for_header
instance variable, you’ll get the same effect as yielding.20 Jan 2007
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.
20 Jan 2007
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?
20 Jan 2007
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
Providing a method so that they can write
sustains the abstraction.
20 Jan 2007
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:
Then, your tooltips helper becomes a trivial call to render:
Does that work for you, Michael?
20 Jan 2007
Thanks, that’s the right idea. I’m not using a partial, though, render :inline does it just fine.
20 Jan 2007
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.
21 Jan 2007
Why do so many bloggers get a hard-on over this? I was doing this with Clipper back in 90.
21 Jan 2007
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.
21 Jan 2007
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.
22 Jan 2007