Rails Best Practices 1: Fat Model – Skinny Controller

Maintain a skinny controller is one of the most important things to increase the readability and testability of the code.
Let’s see some practices to move from the logic controller to the model.

1. Named Scope

In this first example we see how to use a named_scope to move the research methods in the model, simplifying the controller.

Suppose you have a controller where in the index method we extract the list of sedan cars and that of the station wagon.
The first implementation we can write is the following:

1
2
3
4
5
6
7
8

class CarsController < ApplicationController
def index
@sedan_cars = Car.find(:all,
:conditions => { :category => 'sedan' })
@wagon_cars = Car.find(:all,
:conditions => { :category => 'wagon'})
end
end

Now, defining two named_scope in the Car model we can see how easier is to write the same index method of the controller:

1
2
3
4
5
6
7
8
9
10
11

class Car < ActiveRecord::Base
named_scope :sedan, :conditions => { :category => 'sedan' }
named_scope :wagon, :conditions => { :category => 'wagon'}
end

class CarsController < ApplicationController
def index
@sedan_cars = Car.sedan
@wagon_cars = Car.wagon
end
end

The new version of the index method is also easier to test.

2. Model Association

In the second example I will show you how to use “model association” to simplify a create method.
Suppose we have a classic blog example where we create a new post associated to the current user.
The first implementation can be done is the following:

1
2
3
4
5
6
7

class PostsController < ApplicationController
def create
@post = Post.new(params[:post])
@post.user_id = current_user.id
@post.save
end
end

Defining an has_many association between users and post inside the model we can simplify the create method as follows:

1
2
3
4
5
6
7
8
9
10

class User < ActiveRecord::Base
has_many :posts
end

class PostsController < ApplicationController
def create
@post = current_user.posts.build(params[:post])
@post.save
end
end

3. Scope Access

Continuing the blog example, as we see in this third example we can simplify the edit method using a scope access.
In our method we want allow altering a post only if it belongs to the current user.
As usual, we can write an initial implementation:

1
2
3
4
5
6
7
8
9

class PostsController < ApplicationController
def edit
@post = Post.find(params[:id)
if @post.current_user != current_user
flash[:warning] = 'Access denied'
redirect_to posts_url
end
end
end

The optimization that can be done in this case is to simplify the method so if you edit a post that not belongs to the current user is raised an exception of type RecordNotFound.
Using the named scoped the entire method reduces to a single line of code:

1
2
3
4
5

class PostsController < ApplicationController
def edit
@post = current_user.posts.find(params[:id)
end
end

In the next post of this series devoted to the Rails Best Practices we will see other ways to simplify the logic controller and move more in the model.