Rails Best Practices - 2
In our previous article we have discussed how we can make the configurations correctly. Today we'll looking in to the best practices on how to code.
Keep your code MVC
Ok, how many times have you heard about MVC (Model View Controllers), this is one of the main buzz words in today’s web development world. But what does it really mean? When you say your app is designed in MVC pattern you should really mean it. In MVC you should
- Keep fat models
- Keep thin controllers
- and keep dumb views (yes.. really.. I'll explain why)
Fat models
Take all your program logic and implement it in models. Models should represent your class diagram. Another important point is models should not always inherit from ActiveRecord::Base, you might have just classes inside models directory.
Ex : User < ActiveRecord::Base, you may have a class called User
Thin controllers
Controllers are just facilitators. They will create a bridge between your models and views (your logical layer and your presentation layer). Other than doing some 'http' related process (as models can’t handle http functions) controllers shouldn't contain any business logic.
Ex: say you want to check a given user is an administrator or not you might have your controller like this
class UsersController < ApplicationController
def my_method
@user = User.find(params[:id])
if @user.user_type == “ADMIN”
#do something as administrator
else
#do something as normal user
end
end
end
But the problem here is, checking a user for an administrator is not a controller level function. Controller should only have a way of getting a given user is admin or not and do an operation upon that.
Right way of doing this would be
in your model you can add the administrator check method
class User < ActiveRecord::Base
def admin?
self.user_type == “ADMIN” ? true : false
end
end
and in your controller
class UsersController < ApplicationController
def my_method
@user = User.find(params[:id])
if @user.admin?
#do something as administrator
else
#do something as normal user
end
end
end
Dump views
Yes.. you heard it right... dump views. Keep your views just to represent data. Not to process data. Do not use 'find' commands as such inside your views.
In some practical situations you might want to do another database call inside you views. Take above example. Say you want to loop through the users and show their name and user type (not user type id)
But in your users table you have only the user_type_id column
One way of doing this is, in your view (after setting the relationships inside between models)
<% @users.each do |user| %>
<tr>
<td><%=h user.name %></td>
<td><%=h user.user_type.user_type_text %></td>
<td><%=h user.active %></td>
<td><%= link_to 'Show', user %></td>
<td><%= link_to 'Edit', edit_user_path(user) %></td>
<td><%= link_to 'Destroy', user, :confirm => 'Are you sure?', :method => :delete %></td>
</tr>
<% end %>
But, user.user_type.user_type_text means it does a database find from your view. Instead use a join and get all the details to your '@users' variable.
And for formatting and other functions make use of helpers.
Let’s start one by one and describe the important things you should know when creating an MVC application.
Models
Rule number 1, as I said before, keep the business logic in models.
scope is your friend
named_scope and default_scope are 2 cool features you can use inside your models. Let’s take named_scope first
named_scope – just think about it as you create a label for a find method. Next time when you want to execute the above find method, you simply call the label. What ?!.... Let me explain
Say you want to get all the administrator users. (give that user have a user_type column in your users table and administrator's user type is 1)
then you can write a named_scope
then your model would be
class User < ActiveRecord::Base
named_scope :admins, :conditions => "user_type=1"
end
and in your controller
@users = User.admins
isn’t it more readable than having something like '@users = User.find_all_by_user_type_id(1)'. And let me show you the really cool use of named_scope
say you have another named scope to get all the active users
class User < ActiveRecord::Base
named_scope :admins, :conditions => "user_type=1"
named_scope :active, :conditions => "active=1"
end
Or lets say you need to get all the active admins, so in your controller you can write
@users = User.active.admins
default_scope
default scope is also like named_scope, but the difference is, it will apply the given condition to all the select queries.
This is ideal when you have to do something across all the select queries. (something like order records)
default_scope :order => 'created_at DESC'
Ex:
class User < ActiveRecord::Base
default_scope :order => 'created_at DESC'
end
Create validations in your models
All the validation logic should be inside your models.
validates_presence_of :name
Create relationships
Define your relationships inside the model (I will not explain this in detail as there are handful of resources about this topic)
Use namespaces
Namespaces comes handy when it comes to organize code.
You may get all the company related models under a folder called company.
Then your model will be created as
class Company::Company
end

Controllers
Now let’s see what we can follow when it comes to controllers. As I mentioned earlier make them thin. And some key practices are
Avoid scaffolding
Scaffolding is a quick and dirty way of up and running your application with no time. Why I say dirty is that it makes lots of unnecessary code.
Just see the following example
def index
@users = User.active.admins
respond_to do |format|
format.html # index.html.erb
format.xml { render :xml => @users }
end
end
This method is defined to return the output in either html or xml. Its cool, but just think, sometimes you don't ever want to return the output in xml !. In a case like that all you need is
def index
@users = User.active.admins
end
Use helper methods
Think about the following scenario. You might want a way to get the user from the user name. Say you have a method called 'user_by_name(user_name)'
you can add this method to application controller (if this is a common method) and use inside your controllers. But shortly, you want function in one of your views. Now what to do... you may add this same method to 'applciation_helper'.
that’s where the controller helper methods comes to rescue. Once you define a controller method as a helper method you can access that method from both controller and view.
helper_method :my_method
Avoid layouts for each controller
Always try to keep 1-2 layouts throughout your application. You can define your controller layout at the top of your controller instead creating a layout for each view.
class UsersController < ApplicationController
layout 'common'
end
Views.
When it comes to views, other than what I mentioned earlier (dump views) mainly it’s about your html skills. Other than that one important point is try to use CDN (content delivery network) as much as possible.
Other than those main key points, there are so many and it’s up to you to explore things. Till we meet with another interesting article, happy programming.


by 
Post new comment