---
title: Squirrel Updates for Rails 2.1, now with Named Scopes!
teaser:
tags: news,web,rails,open source,squirrel
author: Jon Yurek
published_on: 2008-06-25
---

As a little follow up to the gotchas, I updated
[Squirrel](https://github.com/thoughtbot/squirrel). Its pagination is
compatible with the latest
[will_paginate](https://github.com/mislav/will_paginate) now.
However, more important is the fact that you can use Squirrel blocks to build
scopes:

    User.scoped{ name.not =~ "Jon%" }.ordered.find(:all)

It works just like `scoped` does normally, but it lets you give Squirrel-style
blocks. This lets you keep all your nice `named_scope` methods and it lets you
do all the crazy crap that Squirrel makes look so much nicer at the same time.

## A Refresher

For those of you who may not have seen Squirrel before, it's a plugin for
ActiveRecord that allows for a more Ruby-ish syntax when specifying queries.
It's especially handy for advanced searches and the like, where lots of joins
and conditionals are required. It makes those a lot more easy to read, and makes
absolutely sure that it uses the right names to refer to the right columns,
which isn't always as straightforward as you might think on a complex join.

Sadly, we can't actually use `named_scope` with a Squirrel block; it's already
using blocks for other things. However, we can do the next best thing, which is
creating class methods on our models. Since named_scopes proxy all their methods
back to where they were created, everything will work out fine.

Let's say we had an app that lets users put up postings for a length of time. We
can also tag those postings. We want to allow people to search for the postings,
so we whip up a Squirrel query like so:

```ruby
class Post
  named_scope :ordered, :order => "created_on DESC"
  named_scope :limit, lambda{|x| {:limit => x} }
  named_scope :after, lambda{|d| {
    :conditions => ["posts.created_on > ?", d]
  } }

  def self.search(params)
    scoped do
      any do
        title.contains? params[:keyword]
        body.contains?  params[:keyword]
      end

      expired == false unless params[:all] == "1"
      created_on > params[:timeframe].to_i.days.ago

      params[:tag].split(",").each do |tag|
        tags.name == tag.strip
      end
    end
  end
end
```

This lets us chain `search` alongside the `limit` and `ordered` scopes (using
some `params` that look like they came in a Rails request):

```ruby
Post.search({:all => "1",
             :tag => "one, two",
             :keyword => "Something",
             :timeframe => ""}).ordered.limit(5)
```

This performs the following query:

```sql
SELECT DISTINCT "posts".id FROM "posts"
  LEFT OUTER JOIN "tags" ON tags.post_id = posts.id
  WHERE ((posts.created_on > '2008-06-25 15::36'
        AND (posts.title LIKE '%Something%' OR posts.body LIKE '%Something%')
        AND (tags.name = 'one') AND (tags.name = 'two')))
  ORDER BY created_on DESC
  LIMIT 5
```

Now let's say those Posts have Comments (and both of those `belong_to :user`).
We can find all Posts that have Comments by their author like so:

```ruby
def Post.with_author_comments
  scoped { user.id == comments.user.id }
end
```

So to find all the Posts in the last week that have a comment by their Author,
we can say:

```ruby
Post.with_author_comments.after(7.days.ago)
```

Which gets us this lovely bit of <abbr title="Structured Query Language">SQL</abbr>:

```sql
SELECT
... 32 aliases snipped ...
FROM "posts"
  LEFT OUTER JOIN "comments" ON comments.post_id = posts.id
  LEFT OUTER JOIN "users" ON "users".id = "comments".user_id
  LEFT OUTER JOIN "users" users_posts ON "users_posts".id = "posts".user_id
WHERE ((posts.created_at > '2008-06-18 15:35:27')
      AND (((users_posts.id = users.id))))
```

And we can even find all the Users who have never made a Comment this way:

```ruby
def User.without_comments
  scoped { comments.id.nil? }
end
```

Each of these works exactly like any other named_scope and can be used in any
situation where a named_scope could also be used.

**Update**: After I hit publish on this, I noticed that the Squirrel-style named
scopes need to be specified first in the chain or they clobber what came before,
but otherwise work fine. Using blocks with scoped _not_ using the named methods
also works completely fine. I'll be looking into why this is and I'll have an
update soon. So, you can use them _almost_ exactly the same as regular named
scopes, anyway.

You can get the latest on [Squirrel's github
page](https://github.com/thoughtbot/squirrel).

Visit our [Open Source page](https://thoughtbot.com/open-source) to learn more about our team's contributions.
