Rails Best Practices, Tips and Tricks
Posted by kev Tue, 07 Feb 2006 01:32:00 GMT
Last week I accepted a job with Mingle.com. The work is exciting (all Rails) and the team is excellent.
Because Rails is a young framework, I thought it would be helpful to write up what I consider best practices when coding with it both for my new coworkers and the web at large. Here’s my current draft. Feel free to critique and comment. I’m very open to suggestions.
This is absolutely essential. Rails makes writing unit and functional tests incredibly easy and testing should be employed at all times. Positive and negative testing should be employed: the first to verify that the application does what it is supposed to when the proper variables are passed to the correct action and then second to verify that when incorrect variables are passed the prefered behavior occurs.
As a general rule, unit testing should test any validation in models as well as any added methods in those models.
For example, if I have a
User model which
validates_presence_of first_name and last_name fields as well as a method
fullname which combines the two, I might have test cases
test_fullname which would test that the model worked as it was intended to.
Functional testing is used to test controllers. As a rule, there should be atleast one testcase for each action and positive and negative testing should be employed. This means that if I have an action
create in my
PostsController then there should be
test_bad_create methods defined in my functional tests which do positive and negative testing. This does not mean there should be only two tests for each action. If there are exceptional cases beyond a simple good and bad, proper testing should cover those cases.
Agile Web Development With Rails Chapter 12
Migrations mean never having to say you’re sorry because you nuked the database. They allow for database agnostic schemas which means you can develop locally on SQLite and deploy on MySQL without a problem. They’re cleaner (and easier) than writing your own schemas custom and should be used whenever possible.
NEVER EVER EVER modify schema.rb. It is a reflection of the database. Migrations should be used to move this forward. If you don’t use migrations and instead modify schema.rb, things will break and people will be unhappy.
Note: You should always run svn update before generating a migration so you don’t have prefix collisions (two number 4s for example).
SQLite is an SQL engine which runs in a single file rather than a server. It is (in general) as fast or faster than MySQL and is excellent for development and testing. In fact, it is possible to run a database completely out of memory which speeds up tests significantly. With the advent of migrations, it makes sense to use SQLite for testing as there is no extra work to deploy on MySQL.
DRY: Don’t Repeat Yourself
The main idea of DRY is that if code which is repeated is extracted to a helper or function, you only have one place to look for (and edit) your code if and when something goes wrong. If you find that similar code is used in several places, you may want to look into extracting that code to a helper function or partial.
Refactoring by Martin Fowler
Don’t use abbreviations, especially in database column names. It should be immediately obvious what the column is for (or atleast what the name means) when looking at the name. These are not the days of C, we don’t need to conserve space with our variable names. Additionally, Rails error handling automatically knows how to “humanize” column names, so when you use a well described name you get to work less on outputting errors.
If names are too long, try to think of another word or phrase that means the same thing, but is immediately obvious.
Source Control (Subversion)
Some sort of source control should be used at all times. This allows for easy roll back if something goes wrong as well as the ability to refer back to old code if needed. Rails has certain files which should not be included in a source control repository, so please refer to the wiki page when preparing the initial import.
Several authentication modules have been written for Rails, but some are better at some things and some are better than others.
This should be the prefered authentication plugin. It is easily installed via
script/plugin, is easily extendible and can handle all issues of the other authentication modules. Additionally, the testing code is excellent which makes it easy to modify.
login_generator is the original login generator gem written by Tobias Luetke. The downside is that there is a bug which scrambles the password if you save an already existing user. It should not be used for this reason. It is replaced and superceded by acts_as_authenticated.
This was a first attempt at created a salted login generator which could reset passwords and do activation. It has since been extracted into login_engine. It is in general bloated and hard to modify. It should be avoided.
login_engine is the extraction of SaltedHashLoginGenerator, and as such, has the disadvantages that SaltedHash has. Additionally, it uses the Rails Engines system which is designed for drop in use. In general, applications need customization. If you need more than simple modification of an authentication system, this should be avoided.
Scaffolding can be a time saver or a crutch. When using scaffolding, make sure to understand exactly what the code is doing and why. Once that happens you can generally write the code faster without using scaffolding as there are almost always changes to each section that scaffolding provides.
Suggested Reading (in General)
The Pragmatic Programmer by Dave Thomas and Andy Hunt
Refactoring by Martin Fowler
Agile Web Development with Rails by Dave Thomas and David Heinemeier Hannson
Programming Ruby by Dave Thomas, with Chad Fowler and Andy Hunt