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 :
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 whos #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