Frequently rails applications need to process data that will not be stored in a database. How does one handle this sort of data?

An example of this might be a user filling out a search form, doing some sort of query. This quickly gets complicated to handle once you start searching by dates (which have to be casted from strings) or have complicated rules about which parameters are required.

This sort of data doesn’t really fit into Ruby on Rails models (which are usually concerned with persisting data). Additionally this data is often subject to business logic and doesn’t really seem to belong in the controller layer either.

The Rails answer to such things is ActiveModel, which provides some tools to get you started on building models that aren’t based off ActiveRecord::Base. Unfortunately, however, there are a number of great features, such as typecasting, that you can’t easily get this way.

Enter active_attr, a fantastic gem by Chris Griego, that helps you bootstrap your new non-persisted models and get into the good stuff. Chris’s gem provides validation, mass-assignment, and security features among others.

My favourite feature is typecasting. In a regular active-record model Rails can infer the appropriate types of your attributes from the database – not a possibility in a non-persisted model. With active_attr you can define the types of your attributes with ease, and get some of that Ruby magic going.

Here’s an example of a PersonQuery:

class PersonQuery
  include ActiveAttr::BasicModel
  include ActiveAttr::TypecastedAttributes
  include ActiveAttr::MassAssignment
  include ActiveModel::Validations

  attribute :full_name, type: String
  attribute :born_after, type: Date

  validates :full_name, presence: true, :length => { :minimum => 5 }
  validates :born_after, presence: true

  def execute
    Person.where(full_name: full_name).where("date_of_birth > ?", born_after) if valid?
  end
end

Lets have a look at how we might use this:

query  = PersonQuery.new(full_name: "John Doe", born_after: "12/12/1990")
# => #

query.born_after
# => Wed, 12 Dec 1990

query.execute
# => [#]

Notice in particular how the born_after attribute has been cast to a Date object.

In our controller we might have something like this:

def index
  @person_query = PersonQuery.new(params[:person_query])
  @people = @person_query.execute
end

Advantages of this technique include:

  1. Nice lean controllers and reusable code.
  2. Declarative validations, instead of messy inline checks.

As an added bonus, formtastic will play nicely with our PersonQuery objects, even displaying validation errors inline with inputs.

Happy hacking!