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
```bash
An alternative that we'd also find ourselves using was.
```ruby
if division.name == 'Canada'
```ruby
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.
```ruby
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
```ruby
What this will do is load up ech of these constants with the
corresponding object. It's basically the equivallent of us doing:
```ruby
if division == Division.find_by_code('XIUHR389')
```text
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."
```ruby
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.