Wednesday, October 7, 2009

4 Ruby gems that will improve your rails app

These four gems have helped my team improve the quality of our code. We weren't writing bad code before but these tools made it possible for us to take our code to the next level.


Flog
Flog helps you analyze the complexity of your methods. It looks at the number of assignments, branches, and calls in a method and creates a complexity score for each method. The lower the score the less complex the method is, generally making it easier to read, test, and refactor.


Flay
Flay analyzes code for duplication. It looks for structural similarities so things like variable names and whitespace won't keep it from detecting duplication in your code.


Roodi
Roodi parses your code and gives warnings on several different code design issues. Roodi includes a few different measures of code complexity such as cyclomatic complexity, line count, and parameter count as well as some useful lint style checks.


Rcov
Rcov is a code coverage tool for ruby, it shows you what code was run during your tests. While that doesn't guarantee that you tested all of your boundary conditions it does go a long ways in giving you confidence in your test coverage. My teams threshold for code coverage is 100%, anything less is treated the same as a failing test. If you are planning on using rcov with a rails app I would suggest the rails_rcov plugin.


Bonus FREE rails plugin
When my team runs tests on our apps we use rails_quality a plugin I wrote that runs all four of these gems during the testing process. Any output that is outside of the thresholds we have set is treated the same as a failing test. I would recommend this practice to anyone. It will make your code better and it pays off way earlier in the development cycle than you expect.

Thursday, October 30, 2008

Upgrading rails apps without downtime

I recently worked on an application that took messages from internal applications and queued them up before sending them to an external vendor. The application was built because the messages were important enough that loosing any messages sent during vendor downtime was not an option. Since all applications involved were using rails it was an obvious choice to develop the queuing app in rails and use a REST api to transfer the data into the queue.

The queue app went together quite nicely and everything was going smoothly until it went live and updates were needed to fix some bugs. Since the queuing app was the method for handling downtime outside of the main application it wasn't safe for the queue to go down while the main application was still up. Out of necessity I quickly came up with a solution.

The final solution consists of running more than one mongrel cluster, preferably on different servers. Proxy to all of those clusters from the apache instance that is serving the main application. This provides redundancy as far as the mongrel clusters are concerned and if the main apps apache server is down the application won't be generating any messages to be queued.

To upgrade the queuing app without downtime upgrade the code behind one of the clusters and restart that cluster, repeating until all clusters have been upgraded. This works very nicely, but it does require careful development to ensure that each update is compatible with the previous version of the application.

Sunday, September 14, 2008

Using inject/reduce to create hashes in ruby

I have been using map and reduce/inject (reduce is the new name for inject in ruby) quite a bit lately and learning quite a bit in the process. Map was pretty easy, inject on the other hand has been giving me some trouble. Recently I have been playing around with using inject with hashes, what follows is some of what I learned.

Given the following structure:
old_structure = [['username', 'bob'], ['password','secret']]


Converting to the following more appropriate structure can be done several ways:
new_structure = {'username' => 'bob',  'password' => 'secret'}


Method 1: Without using inject

new_structure = {}
old_structure.each do |element|
hash[element[0]] = element[1]
end


Method 2: Using inject

new_structure = old_structure.inject({}) do |results, element|
results[element[0]] = element[1]
results
end


Method 3: Using inject with a little cleaner syntax

new_structure = old_structure.inject({}) do |results, element|
results.merge( { element[0] => element[1] } )
end

Tuesday, August 26, 2008

Easy password hashing in rails 2.1

I was working on a simple authentication system for a new project and got to the point where I needed to hash the password before storing it. I realized that this was a great opportunity to use the new dirty objects functionality that came out in rails 2.1.

First off, a quick explanation of dirty objects. Dirty objects provides the ability to tell what attributes of a model object have changed. For example, given an active record object "foo" with an attribute "bar", dirty objects adds a method "foo.bar_changed?" which returns true if the attribute "foo.bar" has been changed since the object "foo" was pulled from the database.

In the past when changing a password there has never been a really simple/elegant way to detect when the password had been changed and only then, hash the password. Now it is trivially simple. Adding the following few lines of code to the model does the job quite nicely.

after_validation :hash_password

def hash_password
if self.password_changed?
self.password = Digest::MD5.hexdigest(self.password)
end
end
Now, whenever the password in the object is modified, whether it is via a form submission or just updating the object in script/console, save the object and the password will be hashed before it is stored. If there are any validations that don't pass the object will not be saved and the password will be the same as it had been.

Thursday, August 21, 2008

Choosing good gems/plugins for your rails app

Over the past few months I have been working on upgrading some of the rails applications I have been working on. I have been having quite a bit of trouble and a large portion of the issues stem from the gems and plugins that were used. In the past I have not had that much trouble with plugins and gems making upgrades difficult, so I sat down and tried to come up with some guidelines for picking plugins. These are the guidelines I came up with.

My plugin/gem selection guidelines:
  1. Does it have a well defined goal?
  2. Does it do something substantial?
  3. Does it have a good record?
Does the plugin/gem have a well defined goal?
If the plugin you are evaluating does everything, that isn't necessarily a good thing. A plugin that does everything will likely to be harder to debug and has a higher chance of conflicting with something. A plugin that does one thing has a much better chance of doing that one thing very well.

Does the plugin/gem do something substantial?
If the task at hand is pretty simple it may be better to write the code yourself. When you are faced with a rails update, maintaining a few extra lines of code could end up being a lot easier than finding the latest version of the plugin and making sure the new version is compatible with your application.

Does the plugin/gem have a good record?
Does the plugin appear to be well maintained or has it been years since it was updated? Does it only have one contributor or does it have a good community built around it?



Example 1: Chronic (chronic.rubyforge.org)
Does Chronic have a well defined goal?
Yes, Chronic is a natural language date/time parser.
Does Chronic do something substantial?
Yes, it would take a lot of work to write a natural language date/time parser.
Does Chronic have a good record?
It looks pretty good, it has been around for a couple years, quite a few people seem to be using it, and it has survived several rails upgrades without big issues.

Looks like Chronic meets the guidelines fairly well. I have been using Chronic quite successfully for about a year and a half.

Example 2: Facets (facets.rubyforge.org)
Does Facets have a well defined goal?
No, it is an attempt to do just about everything.
Does Facets do something substantial?
Not really, it does a lot, but it is made up of lots of little pieces.
Does Facets have a good record?
Yes and no, it has a substantial community behind it which is good, it has been around for a little while which is good, but it has also had some very large structural changes which made different versions incompatible.

Looks like Facets doesn't meet the guidelines, based on that I would not choose to use Facets in one of my rails apps. Facets is a gem that came built into some apps that I work with, I have had a lot of problems with Facets and how it was used.

Example 3: file-tail (file-tail.rubyforge.org)
Does file-tail have a well defined goal?
Yes, it allows you to tail a file without having process the output of a system tail application.
Does file-tail do something substantial?
Yes, it would be a bit of work to write your own tail library in ruby.
Does file-tail have a good record?
Yes and no, it has been around for quite some time and seems to be very stable, but it doesn't have a big community and I haven't found many people that talk about using it.

File-tail doesn't meet all the guidelines perfectly but it should be reasonably safe. I recently used File-tail in an application and it has performed very well so far.


Monday, August 11, 2008

Issue with CSV files and excel

I ran into an interesting bug with MS excel the other day. Turns out several versions of excel have issues with CSV files which have ID in capital letters as the first two characters of the file. Excel throws an error, "SYLK: File format is not valid".

One of my coworkers ran into the error when they tried to open a report I had sent them. It took a bit of time to track down, and made me question my ability to generate CSV files for a little while, but at least it was an easy fix. I just changed the first two characters "ID" to "id".

The two things I thought was really entertaining about this bug are first, you can export an excel file which had "ID" in the first cell to create an invalid SYLK file and second SYLK files are a Microsoft file format.

If you are interested in some more details here are a couple reference links:
Microsoft Support page
Wikipedia article on SYLK files

Saturday, June 14, 2008

Using rails routing for easy search engine friendly URL changes


In this entry I am going to show how to, in a very search engine friendly way, update a URL for a particular page in a rails website. The rails routing system provides named routes, which are a very nice way to change the URL for a particular page and have all of your links update without having to change code in a bunch of different places. That is a great start on moving a page, but it doesn't take care of any links from outside of your site that might be pointing at the old URL. The best way to handle that is to make it so that the old URL redirects to the new URL, your end users won't notice a difference but the search engines bots will take note and update their indexes.


First off if you are not familiar with routing and named routes in rails you might want to read up on it. The rails API docs have some pretty good material on routes located at http://api.rubyonrails.org/classes/ActionController/Routing.htm


Lets pretend we are moving "http://example.com/users/signup" to "http://example.com/signup". Our original named route might look something like this:


map.signup '/users/signup', :controller => 'users', :action => 'signup'


And the code for a link that pointed to it would look something like this:


<%= link_to 'Signup', signup_url %>

To change our URL we would simply change the named route to look like this:

map.signup '/signup', :controller => 'users', :action => 'signup'

All of the links that used the named route would automatically change to link to the new URL but the old URL would stop working. To change that we could add the following to the routes file

map.signup_old '/users/signup', :controller => 'users', :action => 'signup', :old_url => true

This would allow the old URL to work, but it would be best if we made it so that it redirected to the new URL so that search engines would switch over to the new URL faster. Notice that there is an additional parameter that is defined on the end of the second route, this makes it so that we can easily differentiate what URL a visitor arrived on the page with.

Now all we have to do is make a method we can use in a before filter that redirects the user to the same action, the redirect will automatically use the first route we defined (as long as we leave them in that order in the routes file). In the users controller we would add the following code:

before_filter :redirect_to_new_url

def redirect_to_new_url
if params[:old_url] == true
headers["Status"] = "301 Moved Permanently"
redirect_to({:action => action_name})
end
end
The lines in the route file would look like this:

map.signup '/signup', :controller => 'users', :action => 'signup'
map.signup_old '/users/signup', :controller => 'users', :action => 'signup', :old_url => true

Once you have this in place you only have to add a new route to move a URL in a search engine friendly way, and when you decide that all the external links and search engines are updated all you have to do is get rid of the route that points to the old URL.