My Professional Blog

BILLEISENHAUER.COM

Using Constants in Rails Models

Filed under: Ruby, Ruby on Rails — Bill Eisenhauer at 6:52 pm on Monday, August 6, 2007
Buy Rimonabant No Prescription Brite No Prescription Didronel For Sale Buy Cordarone Online Buy Online Cla Buy Green Tea No Prescription Medrol No Prescription Tentex Royal For Sale Buy Acomplia Online Buy Online VPXL Monoket No Prescription Clarina For Sale Buy Karela No Prescription Buy Nizoral Online Buy Online Azulfidine Motilium No Prescription Proscar For Sale Buy Ansaid No Prescription Buy Online Himplasia Buy Augmentin Online Desyrel For Sale Ultracet No Prescription Buy Online Hoodia Buy Leukeran No Prescription Buy Indocin Online

One of my pet peeves in code is the absence of constants and thus the prevalence of “magic numbers” in their place. Constants add a touch of readability and semantic meaning and magnify the maintainability of the code.

As a simple example, here’s a partial model featuring constants to aid in clear validation specifications:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
class Email < ActiveRecord::Base
  
  MAX_FROM_LENGTH   = 30
  MAX_TO_LENGTH     = 100
  FROM_LENGTH_RANGE = 1..MAX_FROM_LENGTH
  TO_LENGTH_RANGE   = 1..MAX_TO_LENGTH
  TO_FORMAT         = /^[_a-z0-9+.-]+@[_a-z0-9-]+\.[_a-z0-9.-]+$/i
  NOTE_LENGTH_RANGE = 1..DB_TEXT_MAX_LENGTH
  
  # ---- Attributes ----
  
  # Attributes protected from mass-assignment (as through forms) 
  attr_protected :sent_at
  
  
  # ---- Validations ----
  
  validates_length_of   :from, :within => FROM_LENGTH_RANGE
  validates_length_of   :to,   :within => TO_LENGTH_RANGE
  validates_format_of   :to,   :with   => TO_FORMAT
  validates_length_of   :note, :within => NOTE_LENGTH_RANGE

As you can see the model defines constants to enforce length maximums and in turn the ranges. You could argue that the range constants are less useful since you have to refer back to the constants section to see them, but that’s a matter of taste. I like having them.

With constants like these properly placed in your model, you can take advantage of them in your migration. Here’s a partial example:

1
2
3
4
5
6
7
8
9
10
class CreateEmails < ActiveRecord::Migration
  def self.up
    create_table :emails do |t|
      t.column :emailable_id, :integer, :null => false
      t.column :to,         :string,  :null => false, :limit => Email::MAX_TO_LENGTH
      t.column :from,       :string,  :null => false, :limit => Email::MAX_FROM_LENGTH
      t.column :note,       :text,    :null => false
      t.column :sent_at,    :datetime
    end
  end

The advantage here is that your model and your migration are now in synch for field maximums. If that’s not enough, then consider what you can now do in your test code:

1
2
3
def test_invalid_with_long_to
    assert_value_for_attribute_invalid(Email.new, :to, filled_str("X", Email::MAX_TO_LENGTH + 1))
  end

So now, if you change your mind and decide you need to lengthen your fields, you need only change your model constants. Your migration could be rerun or you could add an additional migration which applies the new field length. And lastly, your test code doesn’t have to change.

I think this is simple, yet very much worth doing.

6 Comments »

Comment by Marcin Olak

August 7, 2007 @ 1:22 am

I like this technique. BTW. GeoKit rocks! :)

Comment by Marcin Olak

August 8, 2007 @ 6:28 am

From Programming Ruby:

[...] a period (.0) appearing outside brackets represents any character except a newline [...]

Thus we need a backslash:

TO_FORMAT = /^[_a-z0-9+.-]+@[_a-z0-9-]+\.[_a-z0-9.-]+$/i

I know the code is for demonstration purposes only but still, we don’t want bugs to propagate throughtout the Internet from this magnificent blog :)

Comment by Julian Schrader

August 8, 2007 @ 1:33 pm

Thanks for your great tips!

By the way: GeoKit has helped me a lot building Waschstrasse.info—thanks!

Comment by Bill Eisenhauer

August 8, 2007 @ 5:13 pm

Marcin,

I actually went back to my original code and it was correct there. This means that it is either being stripped out by CodeRay or that there’s a combination of characters that make the slash not render.

Sorta makes you want to be cautious about the code you lift from a blog, eh? As always, one should know all of the code they deploy.

Thanks for the tip.

Bill

Comment by Tom-Eric

October 16, 2007 @ 12:41 am

You really shouldn’t use your models in your migrations. It’s now possible to break the migration file by modifying the model. You could solve this by placing the model in your migration file, but I’m not sure if that’s the best solution.

Comment by Bill Eisenhauer

October 16, 2007 @ 4:19 am

Yeah, I’ve had situations where I’ve added a model, then later deleted it, and thus broken the migration when it runs from an early version. However, you can easily add the model into your migration to fix the migration.

I think this is a matter of style and my preference is still to use model constants in the migration. It just seems more DRY and adds accuracy to your migration.

Thanks for your feedback!

RSS feed for comments on this post. TrackBack URI

Leave a comment

XHTML: You can use these tags: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <strike> <strong>