Subtle Rails Backend Gotchas
-
Presence validation in the model without a schema constraint.
Presence is one of the most common ActiveRecord validations. In our Rails models, the syntax looks like this:validates_presence_of :attribute
or this:
validates :attribute, presence: true
It simply checks that the attribute is not nil or blank (whitespace). Having presence validated is great, because it frees us from having to nil check any methods that consume our validated attributes.
But what if a future contributor removes that presence validation? Any business logic we’ve written that depends on our validated attributes might now explode. We can avert a potential disaster by adding
null: false
to the migration, on any attribute that has presence validated:class CrazyCatLady < ActiveRecord::Base has_many :cats, inverse_of: :crazy_cat_lady end class Cat < ActiveRecord::Base belongs_to :crazy_cat_lady, inverse_of: :cats validates_presence_of :crazy_cat_lady end
Null: false
will cause a database error to be raised if nil is saved on that attribute. This simple step protects the integrity of our data by enforcing our intention on two independent levels of the Rails stack. -
Booleans without default values.
A boolean should virtually always have a default value. There are (very) occasional situations where our business logic will depend upon a boolean representing three states: unset, setfalse
, and settrue
. In the vast majority of applications, however, you won’t need to be sensitive to the unset case. To implement the typical situation of afalse
default value, we would adddefault: false
in our migrations:class CreateWidgets < ActiveRecord::Migration def change create_table :widgets do |t| t.string :name, null: false t.string :description t.boolean :flanged, default: false t.timestamps end end end
-
Incompletely tested migrations.
Here’s is an easy one to forget. You’ve just written a migration, and you’re about torake db:migrate
to effect the change to the database. But wait! How do you know that your migration will play nice if someone has to roll back (i.e., revert) your changes?Roll it back, then migrate it right back up!
rake db:migrate rake db:rollback rake db:migrate
If we don’t see any errors in the console, we then check our schema.rb and make sure everything looks right. Taking this extra step ensures that any migration we write will behave itself if it needs to be rolled back.</ul>