---
title: Life in the Fast Lane
teaser:
tags: news,web,open source,rails,pacecar
author: Matt Jankowski
published_on: 2008-10-14
---

You ever write a `named_scope` and think hey framework, I already wrote a schema
-- why are you bothering me with all this work?!

![''](http://images.thoughtbot.com/ui/2008-10-8-1955-chevrolet-pace-car-1.jpg)

What about those times when you're sitting behind the wheel of your ride and the
warning flag is up because Johnny gave Jimmy a little too hard of a bump around
turn number three and you are left wondering just how fast you should go?  I
mean sure you're worried about Jimmy being ok and all, that sure was quite the
spinout back there, but geez, this is the Quest For The Cup and your points
total is not going to grow itself!  Well the latter situation has had a pacecar
for a while - and now the first one does too.

## Pacecar

[Pacecar](http://github.com/thoughtbot/pacecar) is a plugin for rails
application which gives your ActiveRecord models a bunch of class methods based
(mostly) on database introspection.  It looks at column names and types to
generate `named_scope` methods at load time which can be used in your app and
combined with other `named_scope`s as well.

|                                                       |                  |                    |
| ----------------------------------------------------- | ---------------- | ------------------ |
|                                                       | Pacecar (Racing) | Pacecar (Ruby gem) |
| Communicates driving speed limit to automobile racers | Yes              | No                 |
| Provides `named_scope`s to `ActiveRecord` classes     | No               | Yes                |
| Does the boring work for you so you can have fun      | Yes              | Yes                |

## Scenario

Assume we have the following schema in a rails application...

```ruby
class CreateSchema < ActiveRecord::Migration
    def self.up
      create_table :users, :force => true do |t|
        t.boolean :admin, :default => false, :null => false
        t.datetime :approved_at
        t.datetime :rejected_at
        t.string :first_name
        t.string :last_name
        t.text :description
        t.timestamps
      end
      create_table :posts, :force => true do |t|
        t.string :owner_type
        t.integer :owner_id
        t.string :publication_state
        t.string :post_type
        t.timestamps
      end
      create_table :comments, :force => true do |t|
        t.integer :user_id
        t.text :description
        t.timestamps
      end
    end
  end
```

This is a fairly basic scenario - most applications are not this simple.  But,
this is enough to get the point across, and the benefits really only grow as you
add more tables and more models.  Here's how we might model this data (the
unfamiliar methods are coming from pacecar and will be explained shortly).

```ruby
class User < ActiveRecord::Base
  has_many :posts, :as => :owner
  has_many :comments
  scopes_ranking :comments
end

class Post < ActiveRecord::Base
  PUBLICATION_STATES = %w(Draft Submitted Rejected Accepted)
  TYPES = %w(Free Open Private Anonymous PostModern)
  belongs_to :owner, :polymorphic => true
  scopes_state :publication_state
  scopes_state :post_type, :with => TYPES
  scopes_polymorph :owner
end

class Comment < ActiveRecord::Base
  belongs_to :user
end
```

## Usage

Ok, so you have a schema and you have some models.  Now, what named_scope do you
need?  Here's what you get for free, when the pacecar plugin is installed...

```ruby
#Records where approved_at is not null, or where it is null…

User.approved_at_present
User.approved_at_missing

#Records where first_name is not null, or where it is null…

User.first_name_present
User.first_name_missing

#Records ordered by first_name (default to ‘asc’, can specify to override)…

User.by_first_name
User.by_first_name(:asc)
User.by_first_name(:desc)

#Records where an attribute matches a search term (column LIKE "%term%")…

User.first_name_matches('John')

#Records where an attribute starts or ends with a search term…

User.first_name_starts_with('A')
User.first_name_ends_with('a')

#Records where any non-state text or string column matches term…

User.search_for('test')

#Records where any of a list of columns match the term…

User.search_for 'test', :on => [:first_name, :last_name]

#Records where all of a list of columns match the term…

User.search_for 'test', :on => [:first_name, :last_name], :require => :all

#Records that are all admins or non-admins…

User.admin
User.not_admin

#Records approved before or after certain times…

User.approved_at_before(5.days.ago)
User.approved_at_after(4.weeks.ago)

#Records with approved_at in the past or future…

User.approved_at_in_past
User.approved_at_in_future

#Records with approved_at inside or outside of two times…

User.approved_at_inside(10.days.ago, 1.day.ago)
User.approved_at_outside(2.days.ago, 1.day.ago)

#Records with certain year, month or day…

User.approved_at_in_year(2000)
User.approved_at_in_month(01)
User.approved_at_in_day(01)

#Records with a duration (time delta between two columns) of,
#over or under a certain number of days…

User.with_duration_of(14, :approved_at, :rejected_at)
User.with_duration_over(14, :approved_at, :rejected_at)
User.with_duration_under(14, :approved_at, :rejected_at)

#First x records…

User.limited(10)
```

And by using those `#scopes_*` class methods in the models, we also get...

```ruby
#Records which have an owner_type of User…

Post.for_owner_type(User)

#Records with the most and least associated records…

User.maximum_comments
User.minimum_comments

#Records which are in a particular state, or not in a state…

Post.publication_state_draft
Post.post_type_not_open
```

## Installation

To get the latest pacecar straight from git as a rails plugin, you can do..

<kbd>ruby script/plugin install git://github.com/thoughtbot/pacecar.git</kbd>

Alternately you can use the gem version from github...

<kbd>
gem sources -a http://gems.github.com
<br />
sudo gem install thoughtbot-pacecar
</kbd>

..and then add a line like this to your config/environment.rb...

```ruby
config.gem 'thoughtbot-pacecar',
  :lib => 'pacecar',
  :version => '>= 1.0.0',
  :source => 'http://gems.github.com'
```

After installing pacecar with either of these methods you will be up and running
with all the for free methods, and you'll then be able to add in the class
method calls for enabling the Polymorph, State and Ranking use cases.

## Sample usage

Hey dev team, can you tell us how many users are active but dont have bios?

```ruby
User.bio_missing.active.count
```

How many events have we had that last longer than a week?

```ruby
Event.with_duration_over(7, :starts_at, :ends_at).count
```

Can I see the titles and authors of all the blog posts that we are scheduled to
post in the future?

```ruby
Post.published_at_in_future.collect { |p|
  "#{p.title} #{p.user.name}"
}
```

## Word of caution

Pacecar does a decent amount of method generation at load time - so if you're
opposed to having lots of methods you should be aware of that.  You should also
do an audit of the class methods you already have in place **before** installing
pacecar, and make sure you're not overwriting any of your own methods (named
scopes or otherwise).  Also, if you're philosophically opposed to method
generation ... this does that.

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