Relax Dont Do it When You Want to Go to It

Jared Carroll

In apps I like to follow a pattern: each model has their own controller that handles the CRUD actions for that model. For example, the Blog model has the BlogsController and the User model has the UsersController. This is nice because if I need to know how to CRUD a particular model I know right where to look, in its corresponding controller.

Ok let’s go with this some more.

Say I have an Account and it needs to be authorized by someone before you can access it. Let’s model it:

class Account < ActiveRecord::Base
  has_one :authorization
end
class Authorization < ActiveRecord::Base
  belongs_to :account
  belongs_to :user
end
class User < ActiveRecord::Base
end

We could have modeled this as a boolean attribute authorized on the Account class but we also want to track what time an account was authorized (we can let Rails do this for us by using created_on) and which User authorized it. I like this because I think boolean flags feel dirty and can quickly get out of hand. I’d rather model states as objects, making my domain model richer. The phrase Oh we’ll just set a flag, is definitely one of my most hated in software development.

Anyway here’s our tables:

accounts (id, number, balance)
authorizations (id, account_id, user_id, created_on)

Now following the controller per model pattern we’d have :

  • AccountsController
  • AuthorizationsController
  • UsersController

Now I don’t like to consider performance but by creating an Authorization model we now require a join from accounts to authorizations just to determine if an Account has been authorized as opposed to just reading an attribute in the Account object.

Maybe we went too far but what if didn’t then, we’d have something like:

class AccountsController < ApplicationController
  def authorize
  end
  # all our CRUD actions
end

What is #authorize? That is not Creating, Reading, Updating or Deleting an Account object. I guess you could say “yeah it’s Updating just 1 attribute of an Account”. No, I don’t like it; I want my #edit action to edit the whole object. Like I said before, I want my controllers to handle only the CRUD actions for a particular model. It turned out we were missing a concept, an Authorization. Replacing that boolean with a model kept our CRUD only pattern alive. Authorizing an account became creating an Authorization:

class AuthorizationsController < ApplicationController
  def new
  end
  def create
  end
end

What is #authorize? That is not Creating, Reading, Updating or Deleting an Account object. I guess you could say yeah it’s Updating just 1 attribute of an Account. No, I don’t like it; I want my #edit action to edit the whole object. Like I said before, I want my controllers to handle only the CRUD actions for a particular model. It turned out we were missing a concept, an Authorization. Replacing that boolean with a model kept our CRUD only pattern alive. Authorizing an account became creating an Authorization:

class AuthorizationsController < ApplicationController

  def new
    @account = Account.find params[:id]
  end

  def create
    @account = Account.find params[:id]
     if @account.update_attributes params[:account]
        redirect_to :controller => 'accounts', :action => :show, :id => @account.id
     else
        render :action => :new
     end
  end

end

What if we relaxed the pattern a bit. Say, no longer can controllers only deal with their corresponding models.

For example, the BlogsController can now work with any model not just Blogs.

However, controllers can still only have CRUD actions.

Let’s also eliminate the Authorization model and just let Account have a boolean attribute named authorized, a integer called authorizer_id and a datetime called authorized_on.

Alright here we go.

class SessionsController < ApplicationController

  def new
  end

  def create
  end

end

The #new action would display something along the lines of a form saying ‘Do you want to authorize Account #A-1’? The form would then post to #create, using hidden fields containing a boolean value of true for the Account object’s authorized attribute, the current time for its authorized_on attribute, and the id of the currently logged in user for its authorizer_id attribute. The #update_attributes call in #create would update the Account objects authorized, authorizer_id and authorized_on attributes, thus authorizing the Account.

Here’s another common one.

When dealing with sessions don’t create a LoginController with say #index and #login actions, create a SessionsController who is #new and #create actions authenticate the given credentials.

class UsersController < ApplicationController

  def send_password_reminder
  end

end

Another one; typically apps that require a login end up with a ‘forgot password’ feature where a user gets emailed a link that allows them to reset their password. Usually this ends us in the UsersController like this:

class RemindersController < ApplicationController

  def create
  end

end

What is #send_password_reminder? It’s trash. And its certainly not CRUD. How about we do this:

class UsersController < ApplicationController

  def edit_password
  end

  def update_password
  end

  def edit
  end

  def update
  end

end

Ahh that’s better. Back to our CRUD. Now there’s no Reminder model but remember we relaxed the pattern by allowing controllers to deal with any model. And if someone says Hey I don’t think the password reminder form’s working right, we know right where to look, in the RemindersController because we’re creating a reminder; we don’t have to wade through the UsersController looking for some weird named action like #send_password_reminder.

One more.

Sometimes in apps, users like to be able to edit their profile, which would include things like first name, last name, address, etc. But it would not include password. They like to be able to edit/change their password separately. Typically this ends up like this:

class PasswordsController < ApplicationController

  def new
  end

  def create
  end

end

Now we have an #edit_password and #update_password in addition to our normal #edit and #update actions which deal with editing non-password user info. Once again, trash. How about this:

class PasswordsController < ApplicationController

  def new
  end

  def create
  end

end

There we go. We’re creating a new password for a given User. Back to our CRUD.

You see with this relaxed version of the controller per model pattern we still get to keep our nice CRUD actions, so every controller looks exactly the same i.e. they have all the same named actions. And we don’t have to create a whole bunch of models, which results in more tables, more joins, etc. This style is in line with the new REST stuff in Rails 1.2.

CRUD 4 Life.