Read my latest article: Ruby on Rails is like IKEA...whaa? (posted Wed, 02 Apr 2014 17:14:00 GMT)

Using model constants for project sanity

Posted by Tue, 23 Jun 2009 06:39:00 GMT

On one of our larger client projects (approx. 160 models and growing…) we have a specific model that we refer to quite a bit throughout our code. This model contains less than 10 records, but each of them sits on top of an insanely large and complex set of data. Each record refers to a each of their regions that our client does business in.

For example… we have, Australia, United Kingdom, Canada, United States, and so forth. Each of these regional divisions has their own company code, which are barely distinguishable from the next. They make sense to our client, but when we’re not interacting with those codes on a regular basis, we have to look constantly look them up again to make sure we’re dealing with the right record.

I wanted to share something that we did to make this easier for our team to work around these codes, which we should have thought of long ago.

Let’s take the following mode, Division. We only have about 10 records in our database, but have conditional code throughout the site that are dependent upon which divisions specific actions are being triggered within. Each division has various business logic that we have to maintain.

Prior to our change, we’d come across a lot of code like:

# For all divisions except Canada, invoices are sent via email
# In Canada, invoices are sent via XML to a 3rd-party service
def process_invoices_for(division)
  if division.code == 'XIUHR12'
    # trigger method to send invoices to 3rd party service
    # ...
  else
    # batch up invoices and send via email
    # ...
  end
end

An alternative that we’d also find ourselves using was.

if division.name == 'Canada'

Hell, I think I’ve even seen if division.id == 2 somewhere in the code before. To be fair to ourselves, we did inherit this project a few years ago. ;-)

Throughout the code base, you’ll find business rules like this. Our developers all agreed that this was far from friendly and/or efficient and worst of all, it was extremely error-prone. There have been a few incidents where we read the code wrong and/or got them confused with one another. We were lacking a convention that we could all begin to rely on and use.

So, we decided to implement the following change.

Model Constants

You might already use constants in your Ruby on Rails application. It’s not uncommon to add a few into config/environment.rb and call it a day, but you might also consider scoping them within your models. (makes it much easier for you to maintain them as well)

In our scenario, we decided to add the following constants to our division model.

class Division < ActiveRecord::Base
  AFRICA      = self.find_by_code('XYU238')
  ASIA        = self.find_by_code('XIUHR73')
  AUSTRALIA   = self.find_by_code('XIUHR152')
  CANADA      = self.find_by_code('XIUHR12')
  USA         = self.find_by_code('XIUHR389')
  # etc..
end

What this will do is load up ech of these constants with the corresponding object. It’s basically the equivallent of us doing:

if division == Division.find_by_code('XIUHR389')

But, with this approach, we can stop worrying about their codes and use the division names that we’re talking about with our clients. Our client usually approaches us with, “In Australia, we need to do X,Y,Z differently than we do in the other divisions due to new government regulations.”

if division == Division::CANADA
  # ...
end

case division
  when Division::AFRICA
    #
  when Division::AUSTRALIA
    # ...
end

We are finding this to be much easier to read and maintain. When we’re dealing with a lot of complex business logic in the application, little changes like this can make a big difference.

If you have any alternative solutions, we’d love to hear them. Until then, we’ve been quite pleased with this approach. Perhaps you’ll find some value in it as well.

Subscribe to my RSS feed Enjoying the content? Be sure to subscribe to my RSS feed.
comments powered by Disqus