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)
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