Abstract resource
George Malamidis, June 30th, 2008A large portion of the internet is governed by HTTP and the World Wide Web in particular is designed based on the REST architectural style. It makes sense to design web applications or web based services in a way that respects and harnesses the web’s underlying architecture.
When it comes to developing web applications, Model-View-Controller (MVC) is one of the dominant architectural patterns current web frameworks are based on. MVC is not restricted to building web apps, on the contrary, its history can be traced back to 1979 and Smalltalk and has been originally applied to the development of applications which involved user interfaces.
The majority of Ruby web frameworks, especially the ones inspired by Rails, employ MVC and offer some sort of support for REST style application development, typically by defining resources which can be accessed through a URI and manipulated by making use of standard HTTP methods such as GET, PUT, POST, DELETE.
The above unveils an obvious similarity between the way HTTP resources can be manipulated – the four verbs can fundamentally constitute CRUD operations – and another common tier in web applications nowadays, databases.
Controllers in Merb, Rails or other similar Ruby, or not, web frameworks are a busy abstraction. A controller typically needs to dispatch to relevant actions, consolidate HTTP payloads, deal with sessions, sometimes caching, etc. These controllers are usually REST aware, meaning that they will by default map routed URI HTTP operations to a standard set of actions, namely index, show, create, edit, update, destroy.
If we focus on our application exposing strictly REST resource based interfaces, and assume that these resources directly map to the application’s database schema, we can relieve controllers from some of the associated strain by abstracting away the discussed common functionality.
module CrudTemplate
def resource
raise "You must define a resource"
end
def index
instance_variable_set(resource_sym_plural, resource.find(:all))
render
end
def show
assign_resource(resource.find(params[:id]))
render
end
alias edit show
alias delete show
def new
assign_resource(resource.new(resource_attrs))
render
end
def create
r = resource.new(resource_attrs)
assign_resource(r)
if r.save
on_create_success(r)
else
on_create_failure(r)
end
end
def on_create_success(r)
redirect(resource_sym)
end
alias on_update_success on_create_success
def on_create_failure(r)
assign_resource(r)
render(:new, :status => 400)
end
def update
r = resource.find(params[:id])
if r.update_attributes(resource_attrs)
on_update_success(r)
else
on_update_failure(r)
end
end
def on_update_failure(r)
assign_resource(r)
render(:edit)
end
def destroy
if resource.destroy(params[:id])
on_destroy_success(r)
else
on_destroy_failure(r)
end
redirect(resource_sym)
end
def self.included(controller)
controller.show_action(*shown_actions)
end
protected
def resource_attrs
{}
end
def self.shown_actions
[:index, :show, :create, :new, :edit, :update]
end
private
def assign_resource(r)
instance_variable_set(resource_sym, r)
end
def resource_sym
@resource_sym ||= :"@#{resource.name.underscore.split("/").last}"
end
def resource_sym_plural
@resource_sym_plural ||= :"@#{resource.name.underscore.split("/").last.pluralize}"
end
end
By doing so, we can write controllers that look something like the following.
class Reservations < Application
include CrudTemplate
def resource
Reservation
end
def on_create_success
flash[:notice] = "Thank you"
redirect("/")
end
protected
def self.shown_actions
[:new, :create]
end
def resource_attrs
params[:reservation].merge(session[:member])
end
end
Things are usually more complicated. The above model falls short for the majority of web applications I’ve worked on. Resources rarely are direct matches to database tables and there is usually good reason for them not to be. Applications involve complex business logic, spanning further from what a set CRUD operations is appropriate for. One might argue that business logic can be incorporated into Models (as in ORM classes), but I generally prefer to avoid keeping business logic near the persistence layer and opt for a database agnostic, rich domain tier.
This however doesn’t imply that controllers shouldn’t think in terms of resources. Controllers are close to the web, and the web works well with resources. It suffices for domain layer endpoints that intend to communicate with a controller to expose an interface the controller understands. If we define that interface so that it matches its database specific counterpart, we can achieve the best of both worlds.
Controllers can transparently operate on plain ruby components which include an AbstractResource module (interface) and choose to implement any of its methods, or directly on ORM models, such as ActiveRecord classes, where appropriate.
module AbstractResource
attr_reader :params
def initialize(params = {})
@params = params
end
def save
raise "Implement me"
end
def update_attributes(attrs = {})
raise "Implement me"
end
def valid?
raise "Implement me"
end
def errors
raise "Implement me"
end
module ClassMethods
def delete(id)
raise "Implement me"
end
def find(id)
raise "Implement me"
end
end
def self.included(target)
target.extend(ClassMethods)
end
end
P.S. Credit due to Carlos Villela whose observations have been the core and inspiration behind the ideas in this article.



July 1st, 2008 at 4:38 am
A really good example of this method done well is James Golick’s resource_controller. It does a lot the things you mention with some with very nice api and it’s quite flexible, too. http://jamesgolick.com/2007/10/19/introducing-resource_controller-focus-on-what-makes-your-controller-special
Of course, other people have done similar things, but Golick’s is the best I’ve seen.
September 12th, 2008 at 9:51 am
[...] of the domain, controllers are just plain unnecessary. If you’ve tried something like CrudTemplate, resources_controller or resource_controller, that’s the sort of thing I’m talking [...]