tag:www.rhnh.net,2008:/data%20structures Data Structures - Xavier Shay's Blog 2008-08-27T13:19:04Z Enki Xavier Shay notreal@rhnh.net tag:www.rhnh.net,2008:Post/784 2008-08-27T13:19:04Z 2008-08-27T13:19:04Z You 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&#8217;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&#8217;s heading in the right direction. There is potentially quite a lot of duplication here &#8211; 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) &#8211; 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> =&gt; [<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> =&gt; [<span class="co">Hyundai</span>],<tt> </tt> <span class="s"><span class="dl">'</span><span class="k">ford</span><span class="dl">'</span></span> =&gt; [<span class="co">Ford</span>]<tt> </tt>}<tt> </tt><tt> </tt>scripts =&gt; {<tt> </tt> <span class="s"><span class="dl">'</span><span class="k">discount</span><span class="dl">'</span></span> =&gt; [<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> =&gt; [<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">&quot;</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">&quot;</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">&quot;</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">&quot;</span></span> }<tt> </tt><span class="iv">@scripts</span> += [<span class="s"><span class="dl">&quot;</span><span class="il"><span class="idl">#{</span>script<span class="idl">}</span></span><span class="k">.js</span><span class="dl">&quot;</span></span>]<tt> </tt><tt> </tt>render <span class="sy">:action</span> =&gt; find_view, <span class="sy">:layout</span> =&gt; 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&#8217;t muddy up your models.</p> tag:www.rhnh.net,2008:Post/779 2008-05-10T11:37:00Z 2008-05-10T11:37:55Z Hash 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> =&gt; <span class="s"><span class="dl">'</span><span class="k">ff0000</span><span class="dl">'</span></span>,<tt> </tt> <span class="sy">:green</span> =&gt; <span class="s"><span class="dl">'</span><span class="k">00ff00</span><span class="dl">'</span></span>,<tt> </tt> <span class="sy">:blue</span> =&gt; <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 &#8211; 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 &#8211; 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 &#8211; 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>