Squirrel - Natural Looking Queries for Rails
Posted by Jon Yurek
Sep 29
I’ve never liked how you query the database in ActiveRecord. Sure it works, but so does writing straight SQL. Neither seem very integrated into the framework. So after thinking about it, I figured I’d do it one better and make something a little more Rubyish looking. So I made Squirrel.
1 2 3 4 5 6 |
posts = Post.find do user.email =~ "%thoughtbot%" tags.name === %w( ruby rails ) created_on <=> [ 2.weeks.ago, 1.week.ago ] end |
I’ve seen various plugins for making queries nicer, but none looked like Ruby, because even in the best of them, they still used what I consider to be overly-awkward nested hashes to get relationships. Squirrel automatically gets the relationships and builds them as you use them. It supports all the normal associations, and it does it in a much friendlier way.
You reference associations by whatever name you gave to it in the has_many, belongs_to, etc. You can then access all the columns and relationships on that model. It is rather specific, though, and will raise errors if you misspell your relationships or don’t pluralize right.
And you simply reference columns by their normal names and use any of ==, ===, <=>, =~, >=, <=, >, and < on them pretty much like you’d expect to be able to (before you ask, yes, I cribbed the syntax from ez_where, since it makes sense). It handles nil values in == with a quick trip to IS NULL and it handles negation of conditions through the unary -.
It doesn’t yet do all the fancy stuff I’d like it to, like adding aggregation columns, limiting, or even OR joins and grouping, but it’s still much better looking than normal ActiveRecord::Base#find queries, I think. It does have some fancy stuff like order_by and placeholders, though. Here’s an example with both:
1 2 3 4 5 |
query = User.find do company.name = cname? order_by created_on end |
When you do that, instead of an array of results, it hands you back Squirrel’s Query object, which is the base of all its querying (imagine that). From there, you call find on it and pass a hash of the names of the placeholders you specified. No surprises there. (Also, you can get a query object for inspecting the SQL by passing :query to find, like so: query = User.find(:query) {id == 2})
1 2 3 4 5 6 7 8 9 10 |
users = query.find(:cname => "thoughtbot") </code></pre> Now, what's tricky is that you could pass any syntax-changing value to that and you'd get the right SQL in the executed query. <filter:code lang="ruby"> query.find :cname => "thoughtbot" # => company.name = "thoughtbot" query.find :cname => nil # => company.name IS NULL query.find :cname => ["Google", "37 Signals"] # => company.name IN ("Google", "37 Signals") |
So there you have it. I think it makes queries much more readable and easier to maintain.
You can obtain it with a simple piston init https://svn.thoughtbot.com/plugins/squirrel/trunk vendor/plugins/squirrel
(Updated to reflect correct URL)
Comments on this post
Sep 29
Zachary Holt said,
Neat! Will give it a whirl.Sep 30
Bart Braem said,
Very nice, seems like a solution for all my evil hardcoded queries... Do you have any idea about the performance penalty? Is any caching possible? BartOct 05
rick said,
I haven't used this, but I'd wager that the performance penalty is nothing compared to the act of actually querying the database.Oct 07
Jon Yurek said,
Squirrel builds SQL which ends up being put through find_by_sql, same as normal queries do in the end. I imagine that if those can be cached, there's nothing stopping these from being cached.Oct 25
Steve said,
Hi, I'm interested in your query tool. After downloading it, and seeing how it works, I have a question for you. What do you think about disabling the ActiveRecordHook? I can manually create a query object (and pass in the relevant activerecord instance myself). I'll play around with this myself, but please let me know if this seems like a reasonable approach. Possibly you might include in a future release some kind of global "#define" variable that turns on/off the activerecordhook method. I'd prefer to keep query more contained (for example if I wanted to have ez-where and squirrel both installed for testing).. You can reach me at the following address (de-munge): P U B LI C AT M I SU S E D OT O RG SteveSorry, comments are closed for this article.
© 2000 - 2009 by thoughtbot, inc.
written by a bushel of tiny robots
Come “ride the toad” on Hoptoad, the app error app.
Thunder Thimble: Brand monitoring for social media.
Widgetfinger: Simple content management for simple websites.
Tee-Bot, funny shirts your friends won't understand!
Umbrella Today: “It’s like totally the simplest weather report ever, Julie.”
Thoughtbot
thoughtbot is a technology consulting firm that provides web application development and design services. We focus on building modern systems, embracing good ideas and delivering elegant solutions.
Interested in learning Rails?
Sign up for our beginning or advanced training.
Archives