May 8, 2013 - 3 comments

Tales on Rails: The Three Little Devs

Squeel: 3 Little Pigs

You know how the story goes: Once upon a time, there were three little devs. Smart and determined, the three developers set up a new Rails app.

The first little dev, the youngest and littlest, wrote a handful of ActiveRecord conditions in SQL.


[gist id="4bd6c8ea3c3e4be0ccdd"]

All was well, and the queries worked! So it was, and so it continued, for several minutes. But as the littlest dev carried on, innocently combining his scopes, he was interrupted by a big, bad bug. The bug snarled, "Little dev, little dev, just call it quits!"

The littlest dev was having none of this. "Not by the code in my github commits!"

"Then I'll huff, and I'll puff, and I'll raise an exception!" shouted the bug, who proceeded to do just that.

[gist id="5126e7b682aef787e628"]

The littlest dev fled in terror to the chat window of his brother, the second little dev, who was a little bit wiser and a little bit bigger (relatively speaking, of course). He nodded solemnly as his younger brother explained what had happened.

Putting on his bravest demeanor, he announced, "Let's refactor, and chase off that big, bad bug once and for all!"

They worked tirelessly for minutes upon minutes, using the squeel gem to replace their old SQL conditions with robust, readable Ruby code.

[gist id="45429d859f7d06bf69c0"]

Just as they finished adding in a new query, the big, bad bug arrived yet again, displaying an uncanny sense of plot and timing. "Little devs, little devs, just call it quits!"

"Not by the code in our github commits!" came the obvious response from the little devs.

"Then I'll huff, and I'll puff, and I'll raise an exception!" roared the bug. Sure enough, he huffed, and he puffed, and it turned out that the devs had made a typo in their new query, a direct result of too much code duplication.

[gist id="bdfa4f9cea58dbc6f3b1"]

The two little devs opened a group chat with their eldest brother, the third little dev, who was much wiser and much bigger (relatively speaking, of course). He nodded solemnly as his brothers explained what had happened. Confidence radiating from his monitor-tanned face, he spoke slowly and calmly, "Let's refactor, and chase off that big, bad bug once and for all."

The brothers worked tirelessly for at least a few seconds, using ActiveRecord::Relation's merge method to reduce the duplication of logic in their queries.

[gist id="d545f3737f6e34accc7f"]

The big, bad bug did not show up again, and the three little devs lived happily, for at least a little while.

I'm sorry—you don't think this sounds familiar? The version you know involves pigs building houses made of straw, sticks, and bricks? And a wolf with improbable lung capacity? This is no time for make-believe; I'm telling the story the way my father told it to me, and his father told it to him.

Like all good stories, this one has a moral: Use squeel and ActiveRecord::Relation#merge to reduce the complexity and duplication of your ActiveRecord queries. Who knows? Maybe you too can live happily, at least for a little while.

Published by: Andrew Horner in Developers
Tags: , , ,


May 8, 2013 at 4:46 pm

I might be missing something, but I don’t think the orders.uniq is necessary unless customers can share an order. Looking past that, I’d suggest this could still be simplified a bit:

^Rails 3 syntax. Rails 4 always requires a Proc is passed to scope.

Andrew Horner
May 8, 2013 at 5:38 pm

Thanks for the feedback, Mario! I may have written the scope in a slightly misleading way, but the call to uniq here passes through to the Customer relation, to prevent multiple results for a single Customer with several matching orders.

That is to say, we’re looking for “SELECT DISTINCT customers.* INNER JOIN orders ON…”.

I dislike the lambda syntax for complex scopes, so I tend to write all of my scopes as class methods for consistency, despite the extra whitespace introduced for simple cases. It’s a stylistic preference for me.

    May 8, 2013 at 5:48 pm

    Oh, right. Got it! 🙂

Leave a Reply

This site uses Akismet to reduce spam. Learn how your comment data is processed.