tag:www.rhnh.net,2008:/data%20structuresData Structures - Xavier Shay's Blog2008-08-27T13:19:04ZEnkiXavier Shaynotreal@rhnh.nettag:www.rhnh.net,2008:Post/7842008-08-27T13:19:04Z2008-08-27T13:19:04ZYou don't need view logic in models<p><a href="http://jakescruggs.blogspot.com/2008/08/im-thinking-of-putting-view-logic-into.html">Jake Scruggs wrote about moving view logic into his models</a></p>
<p>It’s hard to tell without knowing the full dataset, but my approach to these sort of problems is to reduce the data down to the simplest possible form (usually a hash), and then use an algorithm to extract what I need.</p>
<p><a href="https://gist.github.com/7062/71ec7c0b47354cc3a06b39974f44c31ae9726a19">One commenter tried this</a> and I think it’s heading in the right direction. There is potentially quite a lot of duplication here – the repetition of the layouts and scripts. To ease this it can sometimes be easier to inverse the key/values, for a more concise representation. You could reduce this even further if there were sensible defaults (if 90% of cars used a two_column layout, for instance) – just replace the <code>raise</code> in the following code.</p><table class="CodeRay"><tr>
<td class="line_numbers" title="click to toggle" onclick="with (this.firstChild.style) { display = (display == '') ? 'none' : '' }"><pre>1<tt>
</tt>2<tt>
</tt>3<tt>
</tt>4<tt>
</tt>5<tt>
</tt>6<tt>
</tt>7<tt>
</tt>8<tt>
</tt>9<tt>
</tt><strong>10</strong><tt>
</tt>11<tt>
</tt>12<tt>
</tt>13<tt>
</tt>14<tt>
</tt>15<tt>
</tt>16<tt>
</tt>17<tt>
</tt>18<tt>
</tt>19<tt>
</tt><strong>20</strong><tt>
</tt>21<tt>
</tt>22<tt>
</tt>23<tt>
</tt>24<tt>
</tt>25<tt>
</tt>26<tt>
</tt>27<tt>
</tt>28<tt>
</tt>29<tt>
</tt><strong>30</strong><tt>
</tt></pre></td>
<td class="code"><pre ondblclick="with (this.style) { overflow = (overflow == 'auto' || overflow == '') ? 'visible' : 'auto' }"><span class="c"># See original post for context</span><tt>
</tt><span class="c"># Data</span><tt>
</tt>layouts = {<tt>
</tt> <span class="s"><span class="dl">'</span><span class="k">two_column</span><span class="dl">'</span></span> => [<span class="co">Toyota</span>, <span class="co">Saturn</span>],<tt>
</tt> <span class="s"><span class="dl">'</span><span class="k">three_column</span><span class="dl">'</span></span> => [<span class="co">Hyundai</span>],<tt>
</tt> <span class="s"><span class="dl">'</span><span class="k">ford</span><span class="dl">'</span></span> => [<span class="co">Ford</span>]<tt>
</tt>}<tt>
</tt><tt>
</tt>scripts => {<tt>
</tt> <span class="s"><span class="dl">'</span><span class="k">discount</span><span class="dl">'</span></span> => [<span class="co">Hyundai</span>, <span class="co">Ford</span>],<tt>
</tt> <span class="s"><span class="dl">'</span><span class="k">poll</span><span class="dl">'</span></span> => [<span class="co">Saturn</span>]<tt>
</tt>}<tt>
</tt><tt>
</tt><span class="c"># Algorithm</span><tt>
</tt>find_key = lambda {|hash, car| <tt>
</tt> (<tt>
</tt> hash.detect {|key, types| <tt>
</tt> types.any? {|type| car.is_a?(type)}<tt>
</tt> <span class="c"># types.include?(car.class) if you're not using inheritance</span><tt>
</tt> } || raise(<span class="s"><span class="dl">"</span><span class="k">No entry for car: </span><span class="il"><span class="idl">#{</span>car<span class="idl">}</span></span><span class="dl">"</span></span>)<tt>
</tt> ).first<tt>
</tt>}<tt>
</tt><tt>
</tt>layout = find_key[layouts, <span class="iv">@car</span>]<tt>
</tt>script = find_key[scripts, <span class="iv">@car</span>]<tt>
</tt><tt>
</tt><span class="iv">@stylesheets</span> += [<span class="s"><span class="dl">'</span><span class="k">layout</span><span class="dl">'</span></span>, <span class="s"><span class="dl">'</span><span class="k">theme</span><span class="dl">'</span></span>].collect {|suffix| <span class="s"><span class="dl">"</span><span class="il"><span class="idl">#{</span>layout<span class="idl">}</span></span><span class="k">_</span><span class="il"><span class="idl">#{</span>suffix<span class="idl">}</span></span><span class="k">.css</span><span class="dl">"</span></span> }<tt>
</tt><span class="iv">@scripts</span> += [<span class="s"><span class="dl">"</span><span class="il"><span class="idl">#{</span>script<span class="idl">}</span></span><span class="k">.js</span><span class="dl">"</span></span>]<tt>
</tt><tt>
</tt>render <span class="sy">:action</span> => find_view, <span class="sy">:layout</span> => layout<tt>
</tt></pre></td>
</tr></table>
<p>This is preferable to putting this data in your object hierarchy for all the normal reasons, especially since it keeps view logic where you expect to find it and doesn’t muddy up your models.</p>tag:www.rhnh.net,2008:Post/7792008-05-10T11:37:00Z2008-05-10T11:37:55ZHash trumps case<table class="CodeRay"><tr>
<td class="line_numbers" title="click to toggle" onclick="with (this.firstChild.style) { display = (display == '') ? 'none' : '' }"><pre>1<tt>
</tt>2<tt>
</tt>3<tt>
</tt>4<tt>
</tt>5<tt>
</tt>6<tt>
</tt>7<tt>
</tt>8<tt>
</tt>9<tt>
</tt><strong>10</strong><tt>
</tt>11<tt>
</tt>12<tt>
</tt>13<tt>
</tt>14<tt>
</tt>15<tt>
</tt>16<tt>
</tt>17<tt>
</tt></pre></td>
<td class="code"><pre ondblclick="with (this.style) { overflow = (overflow == 'auto' || overflow == '') ? 'visible' : 'auto' }"><span class="c"># Two equivalent functions</span><tt>
</tt><span class="r">def</span> <span class="fu">rgb</span>(color)<tt>
</tt> <span class="r">case</span> color<tt>
</tt> <span class="r">when</span> <span class="sy">:red</span> <span class="r">then</span> <span class="s"><span class="dl">'</span><span class="k">ff0000</span><span class="dl">'</span></span><tt>
</tt> <span class="r">when</span> <span class="sy">:green</span> <span class="r">then</span> <span class="s"><span class="dl">'</span><span class="k">00ff00</span><span class="dl">'</span></span><tt>
</tt> <span class="r">when</span> <span class="sy">:blue</span> <span class="r">then</span> <span class="s"><span class="dl">'</span><span class="k">0000ff</span><span class="dl">'</span></span><tt>
</tt> <span class="r">else</span> <span class="s"><span class="dl">'</span><span class="k">000000</span><span class="dl">'</span></span> <span class="c"># Default to black</span><tt>
</tt> <span class="r">end</span><tt>
</tt><span class="r">end</span><tt>
</tt><tt>
</tt><span class="r">def</span> <span class="fu">rgb2</span>(color)<tt>
</tt> {<tt>
</tt> <span class="sy">:red</span> => <span class="s"><span class="dl">'</span><span class="k">ff0000</span><span class="dl">'</span></span>,<tt>
</tt> <span class="sy">:green</span> => <span class="s"><span class="dl">'</span><span class="k">00ff00</span><span class="dl">'</span></span>,<tt>
</tt> <span class="sy">:blue</span> => <span class="s"><span class="dl">'</span><span class="k">0000ff</span><span class="dl">'</span></span><tt>
</tt> }[color] || <span class="s"><span class="dl">'</span><span class="k">000000</span><span class="dl">'</span></span><tt>
</tt><span class="r">end</span><tt>
</tt></pre></td>
</tr></table>
<p>Even though these functions are equivalent, the second carries more semantic weight – it maps a symbol directly to a color. The <code>case</code> sample makes no such guarantees since you can execute any arbitrary code in the <code>then</code> block. In addition, a hash is easier to work with – you can easily iterate over the keys, extract to another method if you need reuse, or query it for other properties (for example, 3 colors are available). It is also easier to read – both aesthetically and because it contains fewer tokens. In almost all circumstances I will prefer a hash over a case statement.</p>
<p><strong>Relationships in data are easier to comprehend and manipulate than relationships in code.</strong></p>