20
Posted February 11, 2011 by Spyros in Ruby on Rails
 
 

10 Ruby on Rails 3 Tips That Will Make You a Better Rails Programmer

helpful_tips_image
helpful_tips_image

If you already are a Rails programmer, or if you are just beginning to learn about rails in your computer programming classes, i’m pretty sure that you have already been dazzled by the things that Rails can do for you. However, all this convenience comes at a cost. That cost comes for the fact that you need to know how to work with the internal Rails conventions and traps. Since i’ve been programming in Rails for quite some months now, i would like to give you what i think are some very useful tips that will make your Rails life easier. On my previous post, i’ve described how Rails associations work, so you might want to take a look. Let’s now take a look at some important Rails techniques-tips.

1. A Relation is Different Than an Array of Records in Active Record

Take a look at the screenshot please (click on it for larger view) :

Notice that when we execute User.where(“name = ?”, ‘hthought’) and then get the class of object, we see that this one is a Relation. A Relation behaves like a pointer. It never actually executes the database query, unless asked by a specific operand. However, we can use it to get a pointer to a scope inside the database. This means that in this case :


the_users = User.where("name = ?", 'hthought')

The variable the_users is now a relation that describes a scope. This scope concerns all these users table rows that have the name ‘hthought’. Remember, at that point, no database select has been made. The actual database query will happen if we now execute :


the_users.first

This is exactly the same as the second User.where above. This one is actually an array of records now, or actual a single object, since we called first to retrieve the first record.

2. Use Ready Made Rails Plugins

We both know that we are using Rails in order to make our lives easier. We hate doing repetitive things with php, mySQL and the likes. That is why we decided to take on Rails and learn as much as possible, in order to fully harvest its power. Therefore, it would make no sense to reinvent the wheel for no reason. Rails has many people who are working behind the scenes to make it better. In that process, many of them create some very useful plugins, that make our lives a whole lot easier. There is no need to write your own authentication routine, RBAC, messaging system, or even a forum. Take a very close look on the hundred of Rails plugins first. A good starting point is http://agilewebdevelopment.com/, and as always, use a search engine for more :)

3. Understand What a Model, Controller, View, and Helper are

Rails heavily depends on the model view controller (MVC) design pattern. Every one of them has a specific job and jeopardizing them is never a good idea. A controller is responsible for handling Rails requests and passing data from a model to a view. You can think of the controller as a manager between the logic of your program and the actual view that a user actually sees. Generally, a controller should have very few code. It should just execute some functions and retrieve instance variables to be used directly in a view. Moreover, it is used to do redirects with redirect_to and the likes.

A model is where your actual business logic is. The body of your main functions should always lie inside a model that is responsible for handling that data. Since a model operates on data, it’s pretty sensible that a model actually represents a database table and the operations that can be done on that. Always make sure that your functions and core code is inside your models.

A view is where data is represented to the user. You should never (really, never) include logic inside your views. If you feel that you need to include some sort of code inside your views, chances are high that you will be executing more database queries than actually needed. If you feel that you can use a hash instead, do it; although a bit more code to write, it’s the superior choice.

Some people may believe that a helper is the way to elegantly include code in your views. However, that is not really the case. A helper is actually (or should be) sort of a formatting underlying task. For instance, suppose that you have a hash that contains the costs of 4 different products, that you need to present to your view. If you would like to present some of the prices in euros and some in dollars, you could use a helper that would create a string like “this costs 20 dollars” or a string like “this costs 18 euros”, based on the helper function input.

Always put code having this priority in your mind :

1. model

2. helper

3. controller

4. view

4. Yes, Nil and Empty are a Bit Weird in Rails. Hopefully, this section will make it a bit more clear for you.

One of the things that are a bit weird in Rails, is the the way it handles true, false, and nil values. One may think that a false is equal to a nil, like 0 is equal to false in languages like php. However, this is not the case with Rails. Let’s illustrate using rails console :


irb(main):013:0> false.nil?
=> false
irb(main):014:0> true.nil?
=> false
irb(main):015:0> 0.nil?
=> false
irb(main):016:0> [].nil?
=> false
irb(main):017:0> User.find_by_id(1).nil?
=> true
irb(main):018:0> User.find_by_name('hthought').nil?
=> false

This should now make more sense. In Rails, you check whether an object meets a certain condition, using the ? operand like : “1.nil?”. This would check whether 1 is a nil object (would return false, of course). In the examples above, you first notice that true, false, and 0 are not nil objects. Also, an empty hash is still not a nil object. However, trying to find a user that has an id of 1 returns nil if this user does not exist. On the other side, a user with the name ‘hthought’ does exist, and that is why we get a not nil value in the last example. Let’s now take a look at empty? as well :


irb(main):019:0> [].empty?
=> true
irb(main):020:0> "".empty?
=> true
irb(main):021:0> 1.empty?
NoMethodError: undefined method `empty?' for 1:Fixnum
 from (irb):21
irb(main):022:0> "hello".empty?
=> false
irb(main):023:0> "".blank?
=> true
irb(main):024:0> User.find_by_id(1).blank?
=> true

Checking for empty? is like checking whether an array object, like a hash or a string does not have anything in it. A nil object cannot be checked for empty? because it is not an array; that would give us an error. An empty hash or string returns true, as expected. 1.empty? is not correct, because 1 is not an array. Checking for [1].empty? though, is applicable.

Another interesting method is blank?. This one is actually the combination of checking for a nil object or an empty array. Thus, “”.blank? is like “”.empty? and User.find_by_id(1).blank? is like User.find_by_id(1).nil?. Hope this clears things for you.

5. Populate your Database using Fixtures

The folder test/fixtures holds a yml representation for each of your models. Use them to define static data for your database and then use a task to drop, create, migrate and seed your database.

6. When You Are Deeply Messed With Your Associations, It’s Probably Time To Use Scopes

A scope (or a named_scope in previous rails versions), is a way to create ready made relations, that can pretty much simplify some of your model code. Imagine a scenario when you have created a user inventory that contains lots of inventory items. Each inventory item is actually a game item and describes the inventory that holds it. Now, an item can be equipped or not and can be a weapon, gloves, a helmet or anything else. The question is, “What is an efficient way to get a user’s items (or a single item), if they are equipped or not ?

Scopes is a very elegant way to do that. First, you would go to the InventoryItem model and create a scope that describes whether an item is equipped :


scope :equipped, where(:is_equipped => 1).includes(:game_item)

The attribute inventory_item.is_equipped specifies whether an item is equipped and also includes the game_item model within the returned object. If we now execute something like user.inventory_items.equipped, we would receive an array with all the equipped user items. Let’s take it a step further. We just want to receive an equipped helmet, or an equipped weapon. Thus, we create a new scope :


scope :item, lambda { |item_type|
    joins(:game_item).
    where("game_items.item_type = ?", item_type ).
    limit(1)
}

We define a scope named item that is described by a lambda function. This one has an argument based on the item type and joins the game_item model in order to check for that value. We are now able to execute something like “user.equipped.item(‘Weapon’)” to get an equipped weapon. Pretty cool, isn’t it ? :)

7. If You Need Repetitive Non Model Functionality, Use Modules

There are times when we need to do the same things over and over, but in different modules. Moreover, the things we need to do are not really part of a particular model. This is most probably a good time to use a module. Modules are generally loaded from /lib. Be a bit cautious though. In Rails 3, the lib path does not autoload modules. You need to explicitly specify that in application.rb like :


# autoload lib modules
 config.autoload_paths += %W(#{config.root}/lib)

Creating a module is really easy. You can just create an rb file with the name of your module (the name is used to load the module), or create a separate folder and store your module there. Let’s say that you need to create a utilities.rb module (you can omit the class if you like of course) :


module Utilities

class Utils

#get a random number for min,max range
def self.rand_range(min,max)
min + rand(max-min)
end

end

end

From your other models, you just need to “include Utilities” and you can use these functions as if they were part of your model.  But what if we have our module in a folder like “message_modules/private_message.rb” ? You create the module the same way, but you include it like :


include MessageModules::PrivateMessage

Rails converts MessageModules::PrivateMessage to message_modules/private_messag, locates your code and is now ready to use it.

8. Avoid Scaffolding In Your First Projects

Who isn’t aware of the good old create a blog in Rails in 15 minutes video ? That is probably why some of us started to learn Rails in the first place. While this video is really good in showing how quickly things can be done in Rails, it does all these things by scaffolding. While scaffolding is a great way to do CRUD (create, read, update, delete) models, controllers and views really fast, it can confuse a newcomer quite a lot. The reason is that scaffolding creates a whole new premade environment for you. If you are not really comfortable with its internals, it can be a bit devastating. To effectively understand scaffolding, you already need to be a seasoned Rails programmer, know about routes, redirects and more. That is why i would never suggest that somebody begins a project with scaffolding. Better start by creating your models, controllers, views and helpers explicitly and avoid scaffolding for now.

9. Use Partials For Views That Are Used In Many Places

Partials are simply templates that can be included from your main views. A partial can be anywhere inside your views folder. I prefer to create a partials folder and have subfolders for some of my partials. You can even pass variables through partials. However, that is never a good idea, since you are including business logic in your views, if you do that. This could be an include :


<%= render :partial => "partials/attack_display", :locals => { :attack_type => 'monster' } %>

This renders the view that resides in partials/_attack_display.erb and passes an attack_type = ‘monster’ variable (for the sake of this example, but never do that). The code inside _attack_display.erb is now displayed in place of this include.

10. Create Your Routes From Scratch

It’s a very good idea to create your routes yourself. Eventually, you will need to learn how Rails routing works, so it’s better to learn is fast. It’s pretty easy anyway. Do not just use the standard global Rails route for every controller/action/:id but rather include your own ones, even better with scopes like :

scope :path => '/pvp', :controller => :pvp do
    match '/' => :index
    match 'pvp_attack/:id' => :pvp_attack
end

This defines that the pvp_controller is responsible for a call to domain/pvp. Then, for no specified action(/), the index action is called. If domain/pvp/pvp_attack/7361 is called, this is handled by the action pvp_attack of the pvp controller and a param[:id] = 7361 is passed to the action.


Spyros