Building complex ActiveRecord queries with tap23 Aug 2015
I recently ran across the need to build a complex ActiveRecord query which called for conditional joins and lookups based on certain parameters.
My options were to either write a raw SQL query which would be hard to maintain or end up doing in-memory operations after a certain point which would be detrimental to performance. So I decided to take a different approach which would let me cleanly write the query while avoiding those pitfalls.
The best way to demonstrate the technique is by showing you an example. So say we wanted to list all the upcoming music gigs in a city and optionally search for a specific venue/band and paginate the results using a scope like this:
To implement a scope like that, we’ll need to make use of something like
tap which is built into Ruby 1.9+. It’s basically a helper for method chaining. Let’s look at how
tap is implemented at it’s source:
That basically translates to this in Ruby:
So let’s make use of that to implement the scope like this:
But looks like there is a problem. None of the conditional queries inside the
tap block seem to be taking effect. Taking a second look at how
tap is implemented, it makes sense.
tap passes it’s object into the given block and after the block finishes, it returns the object. But in our case, what we need is the return value of the block itself not the object.
So let’s make that change and introduce a separate method which can do exactly that. We’ll call it
let and add it to Ruby’s Object class (open classes FTW!):
Now we can implement the upcoming scope on the Gig model something like this:
Now I know this particular example can be written in many different ways and may not make the best case for making use of a
let method. But in more complicated cases this isn’t possible which is where
let would come in handy.
Anyway I’d love to hear about what other techniques are out there for building complex AR queries. Share your thoughts in the comments.