Mostly, but not always, about developing software.

Friday, June 18, 2010

Specifications with ActiveRecord named scopes

The specification pattern is a useful technique for encapsulating the (potentially complex) logic for selecting or validating domain objects in small, composable classes or code blocks.

Previously on Java projects I'd used Hibernate's Criteria API to provide chaining across composed selection criteria (validation is the subject for a different post). For Ruby/ActiveRecord, there are a few projects that have implemented similar APIs, but none are especially mature. Handily, ActiveRecord's own named scopes provides a nice way to achieve what I needed (or at least the Rails 2 version of them, things have changed in Rails 3).

In particular, the undocumented scoped class method can be used to "disconnect" selection criteria from the model object and encapsulate them in Specification classes. A simple specification can be:

class IsEffectiveProductSpecification

def criteria
{ :conditions => ["effective_from <= :today", { :today => Date.today}] }
end

end

To chain specifications, a composite applies each composed specification's criteria against the model's scoped method:

class CompositeSpecification < Array

def all
self.inject(Product.scoped(nil)) do |combined_scope, specification|
combined_scope.scoped(specification.criteria)
end
end

Now in the model we can provide the selection interface that selects from one or more assembled specifications:

class Product

def self.select_satisfying(specification)
satisfying = specification.all
# apply more complex selection, such as selecting across different combination of model objects
end

end

I'd be interested in any Rails 3 (or other) approaches that might improve on this implementation.

Twitter Updates

    follow me on Twitter

    Followers