Rails Best Practices - 2

by Sameera Gayan

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.

Previous Article

 

Share/Save
Your rating: None Average: 5 (1 vote)