Robby on Rails: Using model constants for project sanitythoughts.sort_by{|t| t[:topic]}.collect tag:www.robbyonrails.com,2005:TypoTypo2009-06-23T12:18:07-04:00Robby Russellurn:uuid:fc030737-c47e-4235-ae26-e5960f67d81b2009-06-23T01:39:00-04:002009-06-23T12:18:07-04:00Using model constants for project sanity<p>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.</p>
<p>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.</p>
<p>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 <em>long</em> ago.</p>
<p>Let’s take the following mode, <code>Division</code>. 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.</p>
<p>Prior to our change, we’d come across a lot of code like:</p>
<div class="typocode"><pre><code class="typocode_ruby "><span class="comment"># For all divisions except Canada, invoices are sent via email</span>
<span class="comment"># In Canada, invoices are sent via XML to a 3rd-party service</span>
<span class="keyword">def </span><span class="method">process_invoices_for</span><span class="punct">(</span><span class="ident">division</span><span class="punct">)</span>
<span class="keyword">if</span> <span class="ident">division</span><span class="punct">.</span><span class="ident">code</span> <span class="punct">==</span> <span class="punct">'</span><span class="string">XIUHR12</span><span class="punct">'</span>
<span class="comment"># trigger method to send invoices to 3rd party service</span>
<span class="comment"># ...</span>
<span class="keyword">else</span>
<span class="comment"># batch up invoices and send via email</span>
<span class="comment"># ...</span>
<span class="keyword">end</span>
<span class="keyword">end</span></code></pre></div>
<p>An alternative that we’d also find ourselves using was.</p>
<div class="typocode"><pre><code class="typocode_ruby "><span class="keyword">if</span> <span class="ident">division</span><span class="punct">.</span><span class="ident">name</span> <span class="punct">==</span> <span class="punct">'</span><span class="string">Canada</span><span class="punct">'</span></code></pre></div>
<p>Hell, I think I’ve even seen <code>if division.id == 2</code> somewhere in the code before. To be fair to ourselves, we did inherit this project a few years ago. ;-)</p>
<p>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.</p>
<p>So, we decided to implement the following change.</p>
<h3>Model Constants</h3>
<p>You might already use constants in your Ruby on Rails application. It’s not uncommon to add a few into <code>config/environment.rb</code> 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)</p>
<p>In our scenario, we decided to add the following constants to our <code>division</code> model.</p>
<div class="typocode"><pre><code class="typocode_ruby "><span class="keyword">class </span><span class="class">Division</span> <span class="punct"><</span> <span class="constant">ActiveRecord</span><span class="punct">::</span><span class="constant">Base</span>
<span class="constant">AFRICA</span> <span class="punct">=</span> <span class="constant">self</span><span class="punct">.</span><span class="ident">find_by_code</span><span class="punct">('</span><span class="string">XYU238</span><span class="punct">')</span>
<span class="constant">ASIA</span> <span class="punct">=</span> <span class="constant">self</span><span class="punct">.</span><span class="ident">find_by_code</span><span class="punct">('</span><span class="string">XIUHR73</span><span class="punct">')</span>
<span class="constant">AUSTRALIA</span> <span class="punct">=</span> <span class="constant">self</span><span class="punct">.</span><span class="ident">find_by_code</span><span class="punct">('</span><span class="string">XIUHR152</span><span class="punct">')</span>
<span class="constant">CANADA</span> <span class="punct">=</span> <span class="constant">self</span><span class="punct">.</span><span class="ident">find_by_code</span><span class="punct">('</span><span class="string">XIUHR12</span><span class="punct">')</span>
<span class="constant">USA</span> <span class="punct">=</span> <span class="constant">self</span><span class="punct">.</span><span class="ident">find_by_code</span><span class="punct">('</span><span class="string">XIUHR389</span><span class="punct">')</span>
<span class="comment"># etc..</span>
<span class="keyword">end</span></code></pre></div>
<p>What this will do is load up ech of these constants with the corresponding object. It’s basically the equivallent of us doing:</p>
<div class="typocode"><pre><code class="typocode_ruby "><span class="keyword">if</span> <span class="ident">division</span> <span class="punct">==</span> <span class="constant">Division</span><span class="punct">.</span><span class="ident">find_by_code</span><span class="punct">('</span><span class="string">XIUHR389</span><span class="punct">')</span></code></pre></div>
<p>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.”</p>
<div class="typocode"><pre><code class="typocode_ruby "><span class="keyword">if</span> <span class="ident">division</span> <span class="punct">==</span> <span class="constant">Division</span><span class="punct">::</span><span class="constant">CANADA</span>
<span class="comment"># ...</span>
<span class="keyword">end</span>
<span class="keyword">case</span> <span class="ident">division</span>
<span class="keyword">when</span> <span class="constant">Division</span><span class="punct">::</span><span class="constant">AFRICA</span>
<span class="comment">#</span>
<span class="keyword">when</span> <span class="constant">Division</span><span class="punct">::</span><span class="constant">AUSTRALIA</span>
<span class="comment"># ...</span>
<span class="keyword">end</span></code></pre></div>
<p>We are finding this to be <em>much</em> 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.</p>
<p>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.</p><p>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.</p>
<p>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.</p>
<p>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 <em>long</em> ago.</p>
<p>Let’s take the following mode, <code>Division</code>. 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.</p>
<p>Prior to our change, we’d come across a lot of code like:</p>
<div class="typocode"><pre><code class="typocode_ruby "><span class="comment"># For all divisions except Canada, invoices are sent via email</span>
<span class="comment"># In Canada, invoices are sent via XML to a 3rd-party service</span>
<span class="keyword">def </span><span class="method">process_invoices_for</span><span class="punct">(</span><span class="ident">division</span><span class="punct">)</span>
<span class="keyword">if</span> <span class="ident">division</span><span class="punct">.</span><span class="ident">code</span> <span class="punct">==</span> <span class="punct">'</span><span class="string">XIUHR12</span><span class="punct">'</span>
<span class="comment"># trigger method to send invoices to 3rd party service</span>
<span class="comment"># ...</span>
<span class="keyword">else</span>
<span class="comment"># batch up invoices and send via email</span>
<span class="comment"># ...</span>
<span class="keyword">end</span>
<span class="keyword">end</span></code></pre></div>
<p>An alternative that we’d also find ourselves using was.</p>
<div class="typocode"><pre><code class="typocode_ruby "><span class="keyword">if</span> <span class="ident">division</span><span class="punct">.</span><span class="ident">name</span> <span class="punct">==</span> <span class="punct">'</span><span class="string">Canada</span><span class="punct">'</span></code></pre></div>
<p>Hell, I think I’ve even seen <code>if division.id == 2</code> somewhere in the code before. To be fair to ourselves, we did inherit this project a few years ago. ;-)</p>
<p>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.</p>
<p>So, we decided to implement the following change.</p>
<h3>Model Constants</h3>
<p>You might already use constants in your Ruby on Rails application. It’s not uncommon to add a few into <code>config/environment.rb</code> 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)</p>
<p>In our scenario, we decided to add the following constants to our <code>division</code> model.</p>
<div class="typocode"><pre><code class="typocode_ruby "><span class="keyword">class </span><span class="class">Division</span> <span class="punct"><</span> <span class="constant">ActiveRecord</span><span class="punct">::</span><span class="constant">Base</span>
<span class="constant">AFRICA</span> <span class="punct">=</span> <span class="constant">self</span><span class="punct">.</span><span class="ident">find_by_code</span><span class="punct">('</span><span class="string">XYU238</span><span class="punct">')</span>
<span class="constant">ASIA</span> <span class="punct">=</span> <span class="constant">self</span><span class="punct">.</span><span class="ident">find_by_code</span><span class="punct">('</span><span class="string">XIUHR73</span><span class="punct">')</span>
<span class="constant">AUSTRALIA</span> <span class="punct">=</span> <span class="constant">self</span><span class="punct">.</span><span class="ident">find_by_code</span><span class="punct">('</span><span class="string">XIUHR152</span><span class="punct">')</span>
<span class="constant">CANADA</span> <span class="punct">=</span> <span class="constant">self</span><span class="punct">.</span><span class="ident">find_by_code</span><span class="punct">('</span><span class="string">XIUHR12</span><span class="punct">')</span>
<span class="constant">USA</span> <span class="punct">=</span> <span class="constant">self</span><span class="punct">.</span><span class="ident">find_by_code</span><span class="punct">('</span><span class="string">XIUHR389</span><span class="punct">')</span>
<span class="comment"># etc..</span>
<span class="keyword">end</span></code></pre></div>
<p>What this will do is load up ech of these constants with the corresponding object. It’s basically the equivallent of us doing:</p>
<div class="typocode"><pre><code class="typocode_ruby "><span class="keyword">if</span> <span class="ident">division</span> <span class="punct">==</span> <span class="constant">Division</span><span class="punct">.</span><span class="ident">find_by_code</span><span class="punct">('</span><span class="string">XIUHR389</span><span class="punct">')</span></code></pre></div>
<p>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.”</p>
<div class="typocode"><pre><code class="typocode_ruby "><span class="keyword">if</span> <span class="ident">division</span> <span class="punct">==</span> <span class="constant">Division</span><span class="punct">::</span><span class="constant">CANADA</span>
<span class="comment"># ...</span>
<span class="keyword">end</span>
<span class="keyword">case</span> <span class="ident">division</span>
<span class="keyword">when</span> <span class="constant">Division</span><span class="punct">::</span><span class="constant">AFRICA</span>
<span class="comment">#</span>
<span class="keyword">when</span> <span class="constant">Division</span><span class="punct">::</span><span class="constant">AUSTRALIA</span>
<span class="comment"># ...</span>
<span class="keyword">end</span></code></pre></div>
<p>We are finding this to be <em>much</em> 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.</p>
<p>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.</p>