giant robots smashing into other giant robots

We are thoughtbot. We make web & mobile apps.

Tagged:

Comments (View)

Squirrel - Once More, with Feeling!

I admit, the first iteration of Squirrel wasn’t really as super fabulous as I thought it would be. Because of how I was using the blocks, you couldn’t do a simple thing like access params. And since half the fun of it was being able to make good looking, flexible queries, not being able to use params was a giant pain.

Now, however, I’ve managed to mix the clean, functional look of instance_eval with the husky utilitarianism of params to come up with a happier, shinier Squirrel:

users = User.find(:all) do
  blog.title == params[:blog_name]
end

And what’s that in the trees, gracefully swinging from vine to vine? It’s our friends any and all here to give us grouping blocks!

site = Site.find(:first) do
  any {
    domains.hostname == params[:hostname]
    domains.hostname == "www.#{params[:hostname]}"
  }
end

All the functions you’d expect to be able to use in your controller are available (well, as long as you actually are in your controller, anyway; squirrels aren’t miracle workers)—params, session, etc.

You can use the unary minus to negate any condition or block, and you can also use it to do a descending order_by

Post.find(:all) do
  -any {
    title =~ /^OMG/
    id == 4
  }
  order_by -created_on
end

This is a major rewrite from the original code and cleaned up things in a very good way. And what’s more, we’re using it in real code now, so I have a fire under my ass to keep it in fighting shape.

The SVN repo is https://svn.thoughtbot.com/plugins/squirrel/trunk for all of you itching to try it out.

Tagged:

Comments (View)

Squirrel - Natural Looking Queries for Rails

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.

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:

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})

users = query.find(:cname => "thoughtbot")

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.

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)