Last active
August 29, 2015 14:12
-
-
Save ParthBarot-BoTreeConsulting/845276ac52d82db9e124 to your computer and use it in GitHub Desktop.
Controller refactoring
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
def users | |
per_page = Variable::default_pagination_value | |
@users = User.find(:all) | |
# First, check to see if there | |
# was an operation performed | |
if not params[:operation].nil? then | |
if (params[:operation] == "reset_password") then | |
user = User.find(params[:id]) | |
user.generate_password_reset_access_key | |
user.password_confirmation = user.password | |
user.email_confirmation = user.email | |
user.save! | |
flash[:notice] = user.first_name + " " + | |
user.last_name + "'s password has been reset." | |
end | |
if (params[:operation] == "delete_user") then | |
user = User.find(params[:id]) | |
user.item_status = ItemStatus.find_deleted() | |
user.password_confirmation = user.password | |
user.email_confirmation = user.email | |
user.save! | |
flash[:notice] = user.first_name + " " + | |
user.last_name + " has been deleted" | |
end | |
if (params[:operation] == "activate_user") then | |
user = User.find(params[:id]) | |
user.item_status = ItemStatus.find_active() | |
user.password_confirmation = user.password | |
user.email_confirmation = user.email | |
user.save! | |
flash[:notice] = user.first_name + " " + | |
user.last_name + " has been activated" | |
end | |
if (params[:operation] == "show_user") then | |
@user = User.find(params[:id]) | |
render :template => show_user | |
return true | |
end | |
end | |
user_order = 'username' | |
if not params[:user_sort_field].nil? then | |
user_order = params[:user_sort_field] | |
if !session[:user_sort_field].nil? && | |
user_order == session[:user_sort_field] then | |
user_order += " DESC" | |
end | |
session[:user_sort_field] = user_order | |
end | |
@user_pages, @users = paginate(:users, | |
:order => user_order, | |
:conditions => ['item_status_id <> ?', | |
ItemStatus.find_deleted().id], | |
:per_page => per_page) | |
end | |
This AdminController object has a users action. The users action expects an addi- | |
tional parameter, operation, which takes values that determine what functionality | |
occurs within the action. | |
Controller naming is very important, and the name of the controller in this case | |
may indicate a problem. In a RESTful structure, the controller is named for the | |
resource that is being operated on. An AdminController object, then, would be | |
expected to operate on Admins. This is not the case. | |
The following code is the remainder of the controller action, for the sake of clar- | |
ity and thoroughness | |
As you can see, this one action is using the operation parameter to provide the same | |
functionality that would normally be present within the index, show, and destroy | |
actions of a UsersController, as well as additional actions for resetting a user’s pass- | |
word and activating a user, with URLs that looked something like the following: | |
/admin/users?operation=reset_password?id=x | |
/admin/users?operation=delete_user?id=x | |
/admin/users?operation=activate_user?id=x | |
/admin/users?operation=show_user?id=x | |
/admin/users | |
Before we go any further in identifying the changes and solutions to this problem, | |
we need to note the importance of using an automated test suite when making large | |
refactorings like this. If this application didn’t have a test suite (it probably wouldn’t), | |
then it’s recommended that one be written. The most appropriate types of tests would | |
be integration tests using a tool such as Cucumber. These tests allow you to prevent | |
regressions because it should be possible to write the integration tests such that they | |
don’t fail if you haven’t broken anything. This is because integration tests operate on | |
the links that are clicked, the fields that are typed in, and so on, rather than on the | |
internal controller organization of the application. | |
When your integration tests have been addressed, you can refactor the monolithic | |
controller into one or more RESTful controllers. Fortunately, non-RESTful controller | |
actions are very often given the name that your controllers should have. Let’s start by | |
mapping out what the new URLs will be: | |
POST | |
DELETE | |
POST | |
GET | |
GET | |
/admin/users/:id/password | |
/admin/users/:id | |
/admin/users/:id/activation | |
/admin/users/:id | |
/admin/users | |
You need to rename AdminController to UsersController (or create a new one) | |
and create new PasswordsController and ActivationsController objects. Next, | |
you simply take the existing code from the if statements in the existing controller and | |
move it into the corresponding new controller actions: | |
class UsersController < ApplicationController | |
def index | |
per_page = Variable::default_pagination_value | |
user_order = 'username' | |
if not params[:user_sort_field].nil? then | |
user_order = params[:user_sort_field] | |
if !session[:user_sort_field].nil? && | |
user_order == session[:user_sort_field] then | |
user_order += " DESC" | |
end | |
session[:user_sort_field] = user_order | |
end | |
@user_pages, @users = paginate(:users, | |
:order => user_order, | |
:conditions => ['item_status_id <> ?', | |
ItemStatus.find_deleted().id], | |
:per_page => per_page) | |
end | |
def destroy | |
user = User.find(params[:id]) | |
user.item_status = ItemStatus.find_deleted() | |
user.password_confirmation = user.password | |
user.email_confirmation = user.email | |
user.save! | |
flash[:notice] = user.first_name + " " + | |
user.last_name + " has been deleted" | |
end | |
def show | |
@user = User.find(params[:id]) | |
render :template => show_user | |
end | |
end | |
class PasswordsController < ApplicationController | |
def create | |
user = User.find(params[:id]) | |
user.generate_password_reset_access_key | |
user.password_confirmation = user.password | |
user.email_confirmation = user.email | |
user.save! | |
flash[:notice] = user.first_name + " " + | |
user.last_name + "'s password has been reset." | |
end | |
end | |
class ActivationsController < ApplicationController | |
def create | |
user = User.find(params[:id]) | |
user.item_status = ItemStatus.find_active() | |
user.password_confirmation = user.password | |
user.email_confirmation = user.email | |
user.save! | |
flash[:notice] = user.first_name + " " + | |
user.last_name + " has been activated" | |
end | |
end | |
end | |
Now, with functionality organized into these new controllers, the routes for these | |
controllers would be as follows: | |
namespace :admin do | |
resources :users do | |
resource :passwords | |
resource :activations | |
end | |
end | |
As you review this code in more detail, you’re likely to see many other things that | |
could be improved in it. While fixing this example is fairly straightforward, it’s important | |
that you tackle only one issue at a time. You should refactor to a RESTful controller | |
first and then continue improving the code from there. Making a controller RESTful | |
often exposes better improvements and keeps things easier to organize. In addition, by | |
tackling one item at a time, you lessen the risk of getting lost or overwhelmed. |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment