tag:www.rhnh.net,2008:/ruby Ruby - Xavier Shay's Blog 2011-11-29T04:24:34Z Enki Xavier Shay notreal@rhnh.net tag:www.rhnh.net,2008:Post/857 2011-11-29T04:39:00Z 2011-11-29T04:24:34Z DataMapper Retrospective <p>I introduced <a href="http://datamapper.org/">DataMapper</a> on my last two major projects. As those projects matured after I had left, they both migrated to a different <span class="caps">ORM</span>. That deserves a retrospective, I think. As I&#8217;ve left both projects, I don&#8217;t have the insider level of detail on the decision to abandon DataMapper, but developers from both projects kindly provided background for this blog post.</p> <h2>Project A</h2> <p>Web application and a batch processing component built on top of a legacy Oracle database.</p> <h3>Good</h3> <ul> <li>Field mappings, nice ruby names and able to ignore fields we didn&#8217;t care about.</li> </ul> <h3>Bad</h3> <ul> <li>Had to roll our own locking and time zone integration.</li> <li>Not great for batch processing (trying to write <span class="caps">SQL</span> through DM abstraction.)</li> </ul> <p>It turned out this project required a lot more batch processing than we anticipated, which DataMapper does not shine at. It was migrated to <a href="http://sequel.rubyforge.org/">Sequel</a> which provides a far better abstraction for working closer to <span class="caps">SQL</span>.</p> <h2>Project B</h2> <p>A fairly typical Rails 3 application. A couple of tens of thousands of lines of code.</p> <h3>Good</h3> <ul> <li>No migrations (pre-release).</li> <li>Foreign keys, composite primary keys.</li> <li>Auto-validations.</li> </ul> <h3>Bad</h3> <ul> <li>Auto-validations with nested attributes was uncharted territory (needed bug fixes).</li> <li>Performance on large object graphs was unusable for page rendering (close to two seconds for our home page, which admittedly had a stupid amount of stuff on it).</li> <li>Performance was suboptimal (though passable) on smaller pages.</li> <li>Tracing through what his happening across multiple gems (particularly around transactions) was tricky.</li> <li>The maintenance/interactions of all the various gems was problematic (e.g. gems X,Y work with 1.9.3 but Z doesn&#8217;t yet).</li> <li>Inability to easily &#8220;break the abstraction&#8221; when <span class="caps">SQL</span> was required.</li> </ul> <p>The performance issues were clear in our code base, but eluded much effort to reduce them down to smaller reproducible problems. The best quick win I found was ~15% by disabling assertions, but I suspect that given the large scope of the problem DataMapper is trying to solve there may not be any approachable way of tackling the issue (would love to be proven wrong!)</p> <p>We ran into obvious integration bugs (apologies for not having kept a concrete list), a symptom of a library not widely used. As a commiter on the project this wasn&#8217;t an issue, since they were easily fixed and moved past (the DataMapper code base is really nice to work on), but having a commiter on your team isn&#8217;t a tenable strategy.</p> <p>DataMapper takes an all-ruby-all-the-time approach, which means things get tricky when the abstraction leaks. Much of the <span class="caps">SQL</span> generation is hidden in private methods. Compare some code to create a composable full text search query:</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></pre></td> <td class="code"><pre ondblclick="with (this.style) { overflow = (overflow == 'auto' || overflow == '') ? 'visible' : 'auto' }"><span class="r">def</span> <span class="pc">self</span>.<span class="fu">search</span>(keywords, options = {})<tt> </tt> options = {<tt> </tt> <span class="ke">conditions</span>: [<span class="s"><span class="dl">&quot;</span><span class="k">true</span><span class="dl">&quot;</span></span>]<tt> </tt> }.merge(options)<tt> </tt><tt> </tt> current_query = query.merge(options)<tt> </tt><tt> </tt> a = repository.adapter<tt> </tt> columns_sql = a.send(<span class="sy">:columns_statement</span>, current_query.fields, <span class="pc">false</span>)<tt> </tt> conditions = a.send(<span class="sy">:conditions_statement</span>, current_query.conditions, <span class="pc">false</span>)<tt> </tt> order_sql = a.send(<span class="sy">:order_statement</span>, current_query.order, <span class="pc">false</span>)<tt> </tt> limit_sql = current_query.limit || <span class="i">50</span><tt> </tt> conditions_sql, conditions_values = *conditions<tt> </tt><tt> </tt> bind_values = [keywords] + conditions_values<tt> </tt><tt> </tt> find_by_sql([<span class="s"><span class="dl">&lt;&lt;-SQL</span></span>, *bind_values])<span class="s"><span class="k"><tt> </tt> SELECT </span><span class="il"><span class="idl">#{</span>columns_sql<span class="idl">}</span></span><span class="k">, ts_rank_cd(search_vector, query) AS rank<tt> </tt> FROM things<tt> </tt> CROSS JOIN plainto_tsquery(?) query<tt> </tt> WHERE </span><span class="il"><span class="idl">#{</span>conditions_sql<span class="idl">}</span></span><span class="k"> AND (query @@ search_vector)<tt> </tt> ORDER BY rank DESC, </span><span class="il"><span class="idl">#{</span>order_sql<span class="idl">}</span></span><span class="k"><tt> </tt> LIMIT </span><span class="il"><span class="idl">#{</span>limit_sql<span class="idl">}</span></span><span class="dl"><tt> </tt> SQL</span></span><tt> </tt><span class="r">end</span><tt> </tt></pre></td> </tr></table> <p>To the ActiveRecord equivalent (Sequel is similar):</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></pre></td> <td class="code"><pre ondblclick="with (this.style) { overflow = (overflow == 'auto' || overflow == '') ? 'visible' : 'auto' }"><span class="r">def</span> <span class="pc">self</span>.<span class="fu">search</span>(keywords)<tt> </tt> select(<span class="s"><span class="dl">&quot;</span><span class="k">things.*, ts_rank_cd(search_vector, query) AS rank</span><span class="dl">&quot;</span></span>)<tt> </tt> .joins(sanitize_sql_array([<span class="s"><span class="dl">&quot;</span><span class="k">CROSS JOIN plainto_tsquery(?) query</span><span class="dl">&quot;</span></span>, keywords]))<tt> </tt> .where(<span class="s"><span class="dl">&quot;</span><span class="k">query @@ search_vector</span><span class="dl">&quot;</span></span>)<tt> </tt> .order(<span class="s"><span class="dl">&quot;</span><span class="k">rank DESC</span><span class="dl">&quot;</span></span>)<tt> </tt><span class="r">end</span><tt> </tt></pre></td> </tr></table> <p>Switching to ActiveRecord took a week of all hands (~4) on deck, plus another week alongside other feature work to get it stable. From beginning to in production was two weeks. The end result was a drop in response time (the deploy is pretty blatant in the graph below), start up time, plus 3K less lines of code (a lot of custom code for dropping down to <span class="caps">SQL</span> was able to be removed).</p> <p><img src="http://a.yfrog.com/img739/7449/4h5.png" alt="" /></p> <h2>Do differently</h2> <p>Ultimately, DataMapper provides an abstraction that I just don&#8217;t need, and even if I did it hasn&#8217;t had its tires kicked sufficiently that a team can use it without having to delve down to the internals. The applications I find myself writing are about data, and the store in which that data lives is vitally important to the application. Abstracting away those details seems to be heading in the wrong direction for writing simple applications. As an intellectual achievement in its own right I really dig DataMapper, but it is too complicated a component to justify using inside other applications.</p> <p>Rich Hickey&#8217;s talk <a href="http://www.infoq.com/presentations/Simple-Made-Easy">Simple Made Easy</a> has been rattling around my head a lot.</p> <p>Nowadays I&#8217;m back to ActiveRecord for team conformance. It&#8217;s more work to keep on top of foreign keys and the like, but overall it does the job. It&#8217;s still too complicated, but has the non-trivial benefit of being used by <strong>lots</strong> of people. This is my responsible choice at the moment.</p> <p>On my own projects I first reach for Sequel. It supports all the nice database features I want to use, while providing a thin layer over <span class="caps">SQL</span>. In other words, I don&#8217;t have to worry about the abstraction leaking because the abstraction is still <span class="caps">SQL</span>, just expressed in ruby (which is a huge win for composeability that you don&#8217;t get with raw <span class="caps">SQL</span>). While it does have &#8220;<span class="caps">ORM</span>&#8221; features, it feels more like the most convenient way of accessing my database rather than an abstraction layer. It&#8217;s actively maintained and the only bug I have found was something that Rails broke, and a patch was already available. There are no open issues in the bug tracker. My experiences have been overwhelmingly positive. I haven&#8217;t built anything big enough with it yet to have confidence using it on a team project though.</p> <p>I still have a soft spot in my heart for DataMapper, I just don&#8217;t see anywhere for me to use it anymore.</p> tag:www.rhnh.net,2008:Post/856 2011-09-05T01:48:00Z 2011-09-05T01:48:38Z Exercises in style <p>Let us make a stack machine! It can add numbers! This may be a winding journey. Have some time and an <code>irb</code> up your sleeve. Maybe it is more of a meditation than a blog post? Onwards!</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></pre></td> <td class="code"><pre ondblclick="with (this.style) { overflow = (overflow == 'auto' || overflow == '') ? 'visible' : 'auto' }"><span class="r">def</span> <span class="fu">push_op</span>(value)<tt> </tt> lambda {|x| [value, x + [value]] }<tt> </tt><span class="r">end</span><tt> </tt><tt> </tt><span class="r">def</span> <span class="fu">add_op</span><tt> </tt> lambda {|x| [x[<span class="i">-1</span>] + x[<span class="i">-2</span>], x[<span class="i">0</span>..<span class="i">-3</span>]] }<tt> </tt><span class="r">end</span><tt> </tt><tt> </tt>[<tt> </tt> push_op(<span class="i">1</span>),<tt> </tt> push_op(<span class="i">2</span>),<tt> </tt> add_op<tt> </tt>].inject([<span class="pc">nil</span>, []]) {|(result, state), op|<tt> </tt> op[state]<tt> </tt>}<tt> </tt></pre></td> </tr></table> <p>Get it? Pushes 1, pushes 2, then the <code>add_op</code> pops them off the stack and makes 3. Not a lot of metadata in those lambdas though, and we can&#8217;t combine them in interesting way.</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>31<tt> </tt>32<tt> </tt>33<tt> </tt>34<tt> </tt>35<tt> </tt>36<tt> </tt>37<tt> </tt>38<tt> </tt>39<tt> </tt><strong>40</strong><tt> </tt>41<tt> </tt>42<tt> </tt></pre></td> <td class="code"><pre ondblclick="with (this.style) { overflow = (overflow == 'auto' || overflow == '') ? 'visible' : 'auto' }"><span class="r">class</span> <span class="cl">Operation</span> &lt; <span class="co">Struct</span>.new(<span class="sy">:block</span>)<tt> </tt> <span class="r">def</span> <span class="fu">+</span>(other)<tt> </tt> <span class="co">CompositeOperation</span>.new(<span class="pc">self</span>, other)<tt> </tt> <span class="r">end</span><tt> </tt><tt> </tt> <span class="r">def</span> <span class="fu">run</span>(state)<tt> </tt> <span class="iv">@block</span>.call(state)<tt> </tt> <span class="r">end</span><tt> </tt><span class="r">end</span><tt> </tt><tt> </tt><span class="r">class</span> <span class="cl">CompositeOperation</span> &lt; <span class="co">Operation</span><tt> </tt> <span class="r">def</span> <span class="fu">initialize</span>(a, b)<tt> </tt> <span class="iv">@a</span> = a<tt> </tt> <span class="iv">@b</span> = b<tt> </tt> <span class="r">super</span>(lambda {|x| <span class="iv">@b</span>.block[<span class="iv">@a</span>.block[x][<span class="i">1</span>]] })<tt> </tt> <span class="r">end</span><tt> </tt><tt> </tt> <span class="r">def</span> <span class="fu">desc</span><tt> </tt> <span class="iv">@a</span>.desc + <span class="s"><span class="dl">&quot;</span><span class="ch">\n</span><span class="dl">&quot;</span></span> + <span class="iv">@b</span>.desc<tt> </tt> <span class="r">end</span><tt> </tt><span class="r">end</span><tt> </tt><tt> </tt><span class="r">class</span> <span class="cl">PushOperation</span> &lt; <span class="co">Operation</span><tt> </tt> <span class="r">def</span> <span class="fu">initialize</span>(value)<tt> </tt> <span class="iv">@value</span> = value<tt> </tt> <span class="r">super</span>(lambda {|x| [value, x + [value]] })<tt> </tt> <span class="r">end</span><tt> </tt><tt> </tt> <span class="r">def</span> <span class="fu">desc</span><tt> </tt> <span class="s"><span class="dl">&quot;</span><span class="k">push </span><span class="il"><span class="idl">#{</span><span class="iv">@value</span><span class="idl">}</span></span><span class="dl">&quot;</span></span><tt> </tt> <span class="r">end</span><tt> </tt><span class="r">end</span><tt> </tt><tt> </tt><span class="r">class</span> <span class="cl">AddOperation</span> &lt; <span class="co">Operation</span><tt> </tt> <span class="r">def</span> <span class="fu">initialize</span><tt> </tt> <span class="r">super</span>(lambda {|x| [x[<span class="i">-1</span>] + x[<span class="i">-2</span>], x[<span class="i">0</span>..<span class="i">-3</span>]] })<tt> </tt> <span class="r">end</span><tt> </tt><tt> </tt> <span class="r">def</span> <span class="fu">desc</span><tt> </tt> <span class="s"><span class="dl">&quot;</span><span class="k">add top two digits on stack</span><span class="dl">&quot;</span></span><tt> </tt> <span class="r">end</span><tt> </tt><span class="r">end</span><tt> </tt></pre></td> </tr></table> <p>A lot more setup, but now we also get a description of operations!</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></pre></td> <td class="code"><pre ondblclick="with (this.style) { overflow = (overflow == 'auto' || overflow == '') ? 'visible' : 'auto' }"><span class="r">def</span> <span class="fu">tagged_push_op</span>(value)<tt> </tt> <span class="co">PushOperation</span>.new(value)<tt> </tt><span class="r">end</span><tt> </tt><tt> </tt><span class="r">def</span> <span class="fu">tagged_add_op</span><tt> </tt> <span class="co">AddOperation</span>.new<tt> </tt><span class="r">end</span><tt> </tt><tt> </tt>ops =<tt> </tt> tagged_push_op(<span class="i">1</span>) +<tt> </tt> tagged_push_op(<span class="i">2</span>) +<tt> </tt> tagged_add_op<tt> </tt><tt> </tt>puts ops.desc<tt> </tt>puts ops.run(start_state).inspect<tt> </tt></pre></td> </tr></table> <p>Ok you get that. What else can we do?</p> <p><em>&#8220;every monad [.] embodies a particular computational strategy. A &#8216;motto of computation,&#8217; if you will.&#8221;</em> &#8212; <a href="http://moonbase.rydia.net/mental/writings/programming/monads-in-ruby/02array">Mental Guy</a></p> <p>hmmm. What does it mean?</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></pre></td> <td class="code"><pre ondblclick="with (this.style) { overflow = (overflow == 'auto' || overflow == '') ? 'visible' : 'auto' }"><span class="r">class</span> <span class="cl">VerboseStackEvaluator</span> &lt; <span class="co">Struct</span>.new(<span class="sy">:stack</span>)<tt> </tt> attr_accessor <span class="sy">:result</span>, <span class="sy">:stack</span><tt> </tt><tt> </tt> <span class="r">def</span> <span class="fu">pass</span>(op)<tt> </tt> puts op.desc<tt> </tt> results = op.call(stack)<tt> </tt> <span class="pc">self</span>.class.new(results[<span class="i">1</span>]).tap <span class="r">do</span> |x|<tt> </tt> x.result = results[<span class="i">0</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="pc">self</span>.<span class="fu">identity</span><tt> </tt> new([])<tt> </tt> <span class="r">end</span><tt> </tt><span class="r">end</span><tt> </tt><tt> </tt>e = evaluator.identity.<tt> </tt> pass(tagged_push_op(<span class="i">1</span>)).<tt> </tt> pass(tagged_push_op(<span class="i">2</span>)).<tt> </tt> pass(tagged_add_op)<tt> </tt><tt> </tt>p [e.result, e.stack]<tt> </tt></pre></td> </tr></table> <p>Oh so now we have one structure (the <code>pass</code> stuff) that we can run through different evaluators. Let us make a recursive one!</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></pre></td> <td class="code"><pre ondblclick="with (this.style) { overflow = (overflow == 'auto' || overflow == '') ? 'visible' : 'auto' }"><span class="r">class</span> <span class="cl">RecursiveLazyStackEvaluator</span> &lt; <span class="co">Struct</span>.new(<span class="sy">:stack</span>)<tt> </tt> <span class="r">def</span> <span class="fu">pass</span>(op)<tt> </tt> <span class="pc">self</span>.class.new(lambda {<tt> </tt> op.call(stack)<tt> </tt> })<tt> </tt> <span class="r">end</span><tt> </tt><tt> </tt> <span class="r">def</span> <span class="pc">self</span>.<span class="fu">identity</span><tt> </tt> new(lambda { [<span class="pc">nil</span>, []] })<tt> </tt> <span class="r">end</span><tt> </tt><tt> </tt> <span class="r">def</span> <span class="fu">result</span>; evaled[<span class="i">0</span>]; <span class="r">end</span><tt> </tt> <span class="r">def</span> <span class="fu">stack</span>; evaled[<span class="i">1</span>]; <span class="r">end</span><tt> </tt><tt> </tt> private<tt> </tt><tt> </tt> <span class="r">def</span> <span class="fu">evaled</span><tt> </tt> <span class="iv">@evaled</span> ||= <span class="iv">@stack</span>.call<tt> </tt> <span class="r">end</span><tt> </tt><span class="r">end</span><tt> </tt></pre></td> </tr></table> <p>Do you see it is now lazy. Rather than evaluate each operation when <code>pass</code> is called, it saves them up until a result is requested. Look out! Haskell in your Ruby! Recursion might blow out our stack though. Let us isomorphically (I just learned this word) translate it to use iteration!</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></pre></td> <td class="code"><pre ondblclick="with (this.style) { overflow = (overflow == 'auto' || overflow == '') ? 'visible' : 'auto' }"><span class="r">class</span> <span class="cl">LazyStackEvaluator</span><tt> </tt> attr_accessor <span class="sy">:steps</span><tt> </tt><tt> </tt> <span class="r">def</span> <span class="fu">initialize</span>(stack, steps = [])<tt> </tt> <span class="iv">@stack</span> = stack<tt> </tt> <span class="iv">@steps</span> = steps<tt> </tt> <span class="r">end</span><tt> </tt><tt> </tt> <span class="r">def</span> <span class="fu">pass</span>(op)<tt> </tt> <span class="pc">self</span>.class.new(<span class="iv">@stack</span>, steps + [op])<tt> </tt> <span class="r">end</span><tt> </tt><tt> </tt> <span class="r">def</span> <span class="pc">self</span>.<span class="fu">identity</span><tt> </tt> new([])<tt> </tt> <span class="r">end</span><tt> </tt><tt> </tt> <span class="r">def</span> <span class="fu">result</span>; evaled[<span class="i">0</span>]; <span class="r">end</span><tt> </tt> <span class="r">def</span> <span class="fu">stack</span>; evaled[<span class="i">1</span>]; <span class="r">end</span><tt> </tt><tt> </tt> protected<tt> </tt><tt> </tt> <span class="r">def</span> <span class="fu">evaled</span><tt> </tt> <span class="iv">@evaled</span> ||= steps.inject([<span class="pc">nil</span>, <span class="iv">@stack</span>]) {|(r, s), op|<tt> </tt> op.call(s)<tt> </tt> }<tt> </tt> <span class="r">end</span><tt> </tt><span class="r">end</span><tt> </tt></pre></td> </tr></table> <p>Not too shabby. Let&#8217;s try something more useful. Given we only have one operation that pops the stack (add), and it only pops two numbers, if we have more than two numbers in a row they start becoming redundant. Let us optimize!</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></pre></td> <td class="code"><pre ondblclick="with (this.style) { overflow = (overflow == 'auto' || overflow == '') ? 'visible' : 'auto' }"><span class="r">class</span> <span class="cl">OptimizingEvaluator</span> &lt; <span class="co">LazyStackEvaluator</span><tt> </tt> <span class="r">def</span> <span class="fu">evaled</span><tt> </tt> <span class="iv">@evaled</span> ||= <span class="r">begin</span><tt> </tt> accumulator = []<tt> </tt> new_steps = []<tt> </tt> steps.each <span class="r">do</span> |step|<tt> </tt> accumulator &lt;&lt; step<tt> </tt> <span class="r">if</span> !step.is_a?(<span class="co">PushOperation</span>)<tt> </tt> new_steps += accumulator<tt> </tt> accumulator = []<tt> </tt> <span class="r">elsif</span> accumulator.length &gt; <span class="i">2</span><tt> </tt> accumulator = accumulator[<span class="i">1</span>..<span class="i">-1</span>]<tt> </tt> <span class="r">end</span><tt> </tt> <span class="r">end</span><tt> </tt> new_steps += accumulator<tt> </tt> new_steps.inject([<span class="pc">nil</span>, <span class="iv">@stack</span>]) {|(r, s), op|<tt> </tt> op.call(s)<tt> </tt> }<tt> </tt> <span class="r">end</span><tt> </tt> <span class="r">end</span><tt> </tt><span class="r">end</span><tt> </tt><tt> </tt>e = evaluator.identity.<tt> </tt> pass(tagged_push_op(<span class="i">1</span>)). <span class="c"># This won't get run!</span><tt> </tt> pass(tagged_push_op(<span class="i">1</span>)).<tt> </tt> pass(tagged_push_op(<span class="i">2</span>)).<tt> </tt> pass(tagged_add_op)<tt> </tt><tt> </tt>p [e.result, e.stack]<tt> </tt></pre></td> </tr></table> <p>Ok one more. This one is pretty useless for this problem, but perhaps it will inspire thought. Let us multithread!</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>31<tt> </tt>32<tt> </tt>33<tt> </tt>34<tt> </tt>35<tt> </tt>36<tt> </tt>37<tt> </tt>38<tt> </tt>39<tt> </tt><strong>40</strong><tt> </tt></pre></td> <td class="code"><pre ondblclick="with (this.style) { overflow = (overflow == 'auto' || overflow == '') ? 'visible' : 'auto' }"><span class="r">class</span> <span class="cl">ThreadingEvaluator</span> &lt; <span class="co">LazyStackEvaluator</span><tt> </tt> <span class="r">def</span> <span class="fu">evaled</span><tt> </tt> <span class="iv">@evaled</span> ||= <span class="r">begin</span><tt> </tt> accumulator = []<tt> </tt> workers = []<tt> </tt> steps.each <span class="r">do</span> |step|<tt> </tt> accumulator &lt;&lt; step<tt> </tt> <span class="r">if</span> step.is_a?(<span class="co">AddOperation</span>)<tt> </tt> workers &lt;&lt; spawn_thread(accumulator)<tt> </tt> accumulator = []<tt> </tt> <span class="r">end</span><tt> </tt> <span class="r">end</span><tt> </tt> workers &lt;&lt; spawn_thread(accumulator) <span class="r">unless</span> accumulator.empty?<tt> </tt> workers.each(&amp;<span class="sy">:join</span>)<tt> </tt><tt> </tt> workers.last[<span class="sy">:result</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">spawn_thread</span>(accumulator)<tt> </tt> <span class="co">Thread</span>.new <span class="r">do</span><tt> </tt> sleep rand / <span class="i">3</span><tt> </tt> <span class="co">Thread</span>.current[<span class="sy">:result</span>] = <span class="r">begin</span><tt> </tt> e = accumulator.inject(<span class="co">VerboseStackEvaluator</span>.identity) {|e, s| e.pass(s) }<tt> </tt> [e.result, e.stack]<tt> </tt> <span class="r">end</span><tt> </tt> <span class="r">end</span><tt> </tt> <span class="r">end</span><tt> </tt><span class="r">end</span><tt> </tt><tt> </tt>e = evaluator.identity.<tt> </tt> pass(tagged_push_op(<span class="i">1</span>)).<tt> </tt> pass(tagged_push_op(<span class="i">1</span>)).<tt> </tt> pass(tagged_push_op(<span class="i">2</span>)).<tt> </tt> pass(tagged_add_op).<tt> </tt> pass(tagged_push_op(<span class="i">3</span>)).<tt> </tt> pass(tagged_push_op(<span class="i">4</span>)).<tt> </tt> pass(tagged_add_op)<tt> </tt><tt> </tt>p [e.result, e.stack]<tt> </tt></pre></td> </tr></table> <p>Ok that is all. Here is an exercise for you: how would you allow the threading and optimizing evaluators to be combined?</p> tag:www.rhnh.net,2008:Post/850 2011-07-30T05:45:00Z 2011-09-03T22:46:34Z Interface Mocking <p><strong><span class="caps">UPDATE</span>:</strong> This is a gem now: <a href="https://github.com/xaviershay/rspec-fire">rspec-fire</a> The code in the gem is better than that presented here.</p> <p>Here is a screencast I put together in response to a recent Destroy All Software screencast on <a href="https://www.destroyallsoftware.com/screencasts/catalog/test-isolation-and-refactoring">test isolation and refactoring</a>, showing off an idea I&#8217;ve been tinkering around with for automatic validation of your implicit interfaces that you stub in tests.</p> <p><iframe src="http://player.vimeo.com/video/27079042?title=0&amp;byline=0&amp;portrait=0&amp;color=FFFACD" width="600" height="338" frameborder="0"></iframe><p><a href="http://vimeo.com/27079042">Interface Mocking screencast</a>.</p></p> <p>Here is the code for <code>InterfaceMocking</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>31<tt> </tt>32<tt> </tt>33<tt> </tt>34<tt> </tt>35<tt> </tt>36<tt> </tt>37<tt> </tt>38<tt> </tt>39<tt> </tt><strong>40</strong><tt> </tt>41<tt> </tt>42<tt> </tt>43<tt> </tt>44<tt> </tt>45<tt> </tt>46<tt> </tt>47<tt> </tt>48<tt> </tt>49<tt> </tt><strong>50</strong><tt> </tt>51<tt> </tt>52<tt> </tt>53<tt> </tt>54<tt> </tt>55<tt> </tt>56<tt> </tt>57<tt> </tt>58<tt> </tt>59<tt> </tt><strong>60</strong><tt> </tt>61<tt> </tt>62<tt> </tt>63<tt> </tt>64<tt> </tt>65<tt> </tt>66<tt> </tt>67<tt> </tt>68<tt> </tt>69<tt> </tt><strong>70</strong><tt> </tt>71<tt> </tt>72<tt> </tt>73<tt> </tt>74<tt> </tt>75<tt> </tt>76<tt> </tt>77<tt> </tt>78<tt> </tt>79<tt> </tt><strong>80</strong><tt> </tt>81<tt> </tt>82<tt> </tt>83<tt> </tt>84<tt> </tt>85<tt> </tt>86<tt> </tt>87<tt> </tt>88<tt> </tt>89<tt> </tt><strong>90</strong><tt> </tt>91<tt> </tt>92<tt> </tt>93<tt> </tt>94<tt> </tt>95<tt> </tt>96<tt> </tt>97<tt> </tt>98<tt> </tt>99<tt> </tt><strong>100</strong><tt> </tt>101<tt> </tt>102<tt> </tt></pre></td> <td class="code"><pre ondblclick="with (this.style) { overflow = (overflow == 'auto' || overflow == '') ? 'visible' : 'auto' }"><span class="r">module</span> <span class="cl">InterfaceMocking</span><tt> </tt><tt> </tt> <span class="c"># Returns a new interface double. This is equivalent to an RSpec double,</span><tt> </tt> <span class="c"># stub or, mock, except that if the class passed as the first parameter</span><tt> </tt> <span class="c"># is loaded it will raise if you try to set an expectation or stub on</span><tt> </tt> <span class="c"># a method that the class has not implemented.</span><tt> </tt> <span class="r">def</span> <span class="fu">interface_double</span>(stubbed_class, methods = {})<tt> </tt> <span class="co">InterfaceDouble</span>.new(stubbed_class, methods)<tt> </tt> <span class="r">end</span><tt> </tt><tt> </tt> <span class="r">module</span> <span class="cl">InterfaceDoubleMethods</span><tt> </tt><tt> </tt> include <span class="co">RSpec</span>::<span class="co">Matchers</span><tt> </tt><tt> </tt> <span class="r">def</span> <span class="fu">should_receive</span>(method_name)<tt> </tt> ensure_implemented(method_name)<tt> </tt> <span class="r">super</span><tt> </tt> <span class="r">end</span><tt> </tt><tt> </tt> <span class="r">def</span> <span class="fu">should_not_receive</span>(method_name)<tt> </tt> ensure_implemented(method_name)<tt> </tt> <span class="r">super</span><tt> </tt> <span class="r">end</span><tt> </tt><tt> </tt> <span class="r">def</span> <span class="fu">stub!</span>(method_name)<tt> </tt> ensure_implemented(method_name)<tt> </tt> <span class="r">super</span><tt> </tt> <span class="r">end</span><tt> </tt><tt> </tt> <span class="r">def</span> <span class="fu">ensure_implemented</span>(*method_names)<tt> </tt> <span class="r">if</span> recursive_const_defined?(<span class="co">Object</span>, <span class="iv">@__stubbed_class__</span>)<tt> </tt> recursive_const_get(<span class="co">Object</span>, <span class="iv">@__stubbed_class__</span>).<tt> </tt> should implement(method_names, <span class="iv">@__checked_methods__</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">recursive_const_get</span> object, name<tt> </tt> name.split(<span class="s"><span class="dl">'</span><span class="k">::</span><span class="dl">'</span></span>).inject(<span class="co">Object</span>) {|klass,name| klass.const_get name }<tt> </tt> <span class="r">end</span><tt> </tt><tt> </tt> <span class="r">def</span> <span class="fu">recursive_const_defined?</span> object, name<tt> </tt> !!name.split(<span class="s"><span class="dl">'</span><span class="k">::</span><span class="dl">'</span></span>).inject(<span class="co">Object</span>) {|klass,name|<tt> </tt> <span class="r">if</span> klass &amp;&amp; klass.const_defined?(name)<tt> </tt> klass.const_get name<tt> </tt> <span class="r">end</span><tt> </tt> }<tt> </tt> <span class="r">end</span><tt> </tt><tt> </tt> <span class="r">end</span><tt> </tt><tt> </tt> <span class="r">class</span> <span class="cl">InterfaceDouble</span> &lt; <span class="co">RSpec</span>::<span class="co">Mocks</span>::<span class="co">Mock</span><tt> </tt><tt> </tt> include <span class="co">InterfaceDoubleMethods</span><tt> </tt><tt> </tt> <span class="r">def</span> <span class="fu">initialize</span>(stubbed_class, *args)<tt> </tt> args &lt;&lt; {} <span class="r">unless</span> <span class="co">Hash</span> === args.last<tt> </tt><tt> </tt> <span class="iv">@__stubbed_class__</span> = stubbed_class<tt> </tt> <span class="iv">@__checked_methods__</span> = <span class="sy">:public_instance_methods</span><tt> </tt> ensure_implemented *args.last.keys<tt> </tt><tt> </tt> <span class="c"># __declared_as copied from rspec/mocks definition of `double`</span><tt> </tt> args.last[<span class="sy">:__declared_as</span>] = <span class="s"><span class="dl">'</span><span class="k">InterfaceDouble</span><span class="dl">'</span></span><tt> </tt> <span class="r">super</span>(stubbed_class, *args)<tt> </tt> <span class="r">end</span><tt> </tt><tt> </tt> <span class="r">end</span><tt> </tt><span class="r">end</span><tt> </tt><tt> </tt><span class="co">RSpec</span>::<span class="co">Matchers</span>.define <span class="sy">:implement</span> <span class="r">do</span> |expected_methods, checked_methods|<tt> </tt> match <span class="r">do</span> |stubbed_class|<tt> </tt> unimplemented_methods(<tt> </tt> stubbed_class,<tt> </tt> expected_methods,<tt> </tt> checked_methods<tt> </tt> ).empty?<tt> </tt> <span class="r">end</span><tt> </tt><tt> </tt> <span class="r">def</span> <span class="fu">unimplemented_methods</span>(stubbed_class, expected_methods, checked_methods)<tt> </tt> implemented_methods = stubbed_class.send(checked_methods)<tt> </tt> unimplemented_methods = expected_methods - implemented_methods<tt> </tt> <span class="r">end</span><tt> </tt><tt> </tt> failure_message_for_should <span class="r">do</span> |stubbed_class|<tt> </tt> <span class="s"><span class="dl">&quot;</span><span class="k">%s does not publicly implement:</span><span class="ch">\n</span><span class="k">%s</span><span class="dl">&quot;</span></span> % [<tt> </tt> stubbed_class,<tt> </tt> unimplemented_methods(<tt> </tt> stubbed_class,<tt> </tt> expected_methods,<tt> </tt> checked_methods<tt> </tt> ).sort.map {|x|<tt> </tt> <span class="s"><span class="dl">&quot;</span><span class="k"> </span><span class="il"><span class="idl">#{</span>x<span class="idl">}</span></span><span class="dl">&quot;</span></span><tt> </tt> }.join(<span class="s"><span class="dl">&quot;</span><span class="ch">\n</span><span class="dl">&quot;</span></span>)<tt> </tt> ]<tt> </tt> <span class="r">end</span><tt> </tt><span class="r">end</span><tt> </tt><tt> </tt><span class="co">RSpec</span>.configure <span class="r">do</span> |config|<tt> </tt><tt> </tt> config.include <span class="co">InterfaceMocking</span><tt> </tt><tt> </tt><span class="r">end</span><tt> </tt></pre></td> </tr></table> tag:www.rhnh.net,2008:Post/849 2011-07-29T05:55:00Z 2011-07-29T05:55:02Z Static Asset Caching on Heroku Cedar Stack <p>I recently moved this blog over to <a href="http://heroku.com">Heroku</a>, and in the process added in some proper <span class="caps">HTTP</span> caching headers. The dynamic pages use the build in <code>fresh_when</code> and <code>stale?</code> Rails helpers, combined with <code>Rack::Cache</code> and the free memcached plugin available on Heroku. That was all pretty straight forward, what was more difficult was configuring Heroku to serve all static assets (such as images and stylesheets) with a far-future <code>max-age</code> header so that they will be cached for eternity. What I&#8217;ve documented here is somewhat of a hack, and hopefully Heroku will provide a better way of doing this in the future.</p> <p>By default Heroku serves everything in <code>public</code> directly via nginx. This is a problem for us since we don&#8217;t get a chance to configure the caching headers. Instead, use the <code>Rack::StaticCache</code> middleware (provided in the <code>rack-contrib</code> gem) to serve static files, which by default adds far future max age cache control headers. This needs to be out of different directory to <code>public</code> since there is no way to disable the nginx serving. I renamed by <code>public</code> folder to <code>public_cached</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></pre></td> <td class="code"><pre ondblclick="with (this.style) { overflow = (overflow == 'auto' || overflow == '') ? 'visible' : 'auto' }"><span class="c"># config/application.rb</span><tt> </tt>config.middleware.use <span class="co">Rack</span>::<span class="co">StaticCache</span>, <tt> </tt> <span class="ke">urls</span>: <span class="s"><span class="dl">%w(</span><span class="k"><tt> </tt> /stylesheets<tt> </tt> /images<tt> </tt> /javascripts<tt> </tt> /robots.txt<tt> </tt> /favicon.ico<tt> </tt> </span><span class="dl">)</span></span>,<tt> </tt> <span class="ke">root</span>: <span class="s"><span class="dl">&quot;</span><span class="k">public_cached</span><span class="dl">&quot;</span></span><tt> </tt></pre></td> </tr></table> <p>I also disabled the built in Rails serving of static assets in development mode, so that it didn&#8217;t interfere:</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></pre></td> <td class="code"><pre ondblclick="with (this.style) { overflow = (overflow == 'auto' || overflow == '') ? 'visible' : 'auto' }"><span class="c"># config/environments/development.rb</span><tt> </tt>config.serve_static_assets = <span class="pc">false</span><tt> </tt></pre></td> </tr></table> <p>In the production config, I configured the <code>x_sendfile_header</code> option to be &#8220;X-Accel-Redirect&#8221;. It was &#8220;X-Sendfile&#8221; which is an apache directive, and was causing nginx to hang (Heroku would never actually serve the assets to the browser).</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></pre></td> <td class="code"><pre ondblclick="with (this.style) { overflow = (overflow == 'auto' || overflow == '') ? 'visible' : 'auto' }"><span class="c"># config/environments/production.rb</span><tt> </tt>config.action_dispatch.x_sendfile_header = <span class="s"><span class="dl">'</span><span class="k">X-Accel-Redirect</span><span class="dl">'</span></span><tt> </tt></pre></td> </tr></table> <p>A downside of this approach is that if you have a lot of static assets, they all have to hit the Rails stack in order to be served. If you only have one dyno (the free plan) then the initial load can be slower than it otherwise would be if nginx was serving them directly. As I mentioned in the introduction, hopefully Heroku will provide a nicer way to do this in the future.</p> tag:www.rhnh.net,2008:Post/847 2011-05-28T01:08:00Z 2011-05-28T01:08:12Z Speeding up Rails startup time <p>In which I provide easy instructions to try a new patch that drastically improves the start up time of Ruby applications, in the hope that with wide support it will be merged into the upcoming 1.9.3 release. Skip to the bottom for instructions, or keep reading for the narrative.</p> <p><strong><span class="caps">UPDATE</span>:</strong> If you have trouble installing, grab a recent copy of rvm: rvm get head.</p> <h2>Background</h2> <p>Recent releases of <span class="caps">MRI</span> Ruby have introduced some fairly major performance regressions when requiring files:</p> <p><img src="https://img.skitch.com/20110528-xigici83u5texbpnwnwntfrkuq.jpg" alt="" /></p> <p>For reference, our medium-sized Rails application requires around 2200 files &emdash; off the right-hand side of this graph. This is problematic. On 1.9.2 it takes 20s to start up, on 1.9.3 it takes 46s. Both are far too long.</p> <p>There are a few reasons for this, but the core of the problem is the basic algorithm which looks something like this:</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></pre></td> <td class="code"><pre ondblclick="with (this.style) { overflow = (overflow == 'auto' || overflow == '') ? 'visible' : 'auto' }"><span class="r">def</span> <span class="fu">require</span>(file)<tt> </tt> <span class="gv">$loaded</span>.each <span class="r">do</span> |x|<tt> </tt> <span class="r">return</span> <span class="pc">false</span> <span class="r">if</span> x == file<tt> </tt> <span class="r">end</span><tt> </tt> load(file)<tt> </tt> <span class="gv">$loaded</span> &lt;&lt; file<tt> </tt><span class="r">end</span><tt> </tt></pre></td> </tr></table> <p>That loop is no good, and gets worse the more files you have required. I have written a patch for 1.9.3 which changes this algorithm to:</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></pre></td> <td class="code"><pre ondblclick="with (this.style) { overflow = (overflow == 'auto' || overflow == '') ? 'visible' : 'auto' }"><span class="r">def</span> <span class="fu">require</span>(file)<tt> </tt> <span class="r">return</span> <span class="pc">false</span> <span class="r">if</span> <span class="gv">$loaded</span>[file] <tt> </tt> load(file)<tt> </tt> <span class="gv">$loaded</span>[file] = <span class="pc">true</span><tt> </tt><span class="r">end</span><tt> </tt></pre></td> </tr></table> <p>That gives you a performance curve that looks like this:</p> <p><img src="https://img.skitch.com/20110528-gtsgba1twaiwkd3frewen54ts.jpg" alt="" /></p> <p>Much nicer.</p> <p>That&#8217;s just a synthetic benchmark, but it works in the real world too. My <a href="http://theconversation.edu.au">main Rails application</a> now loads in a mite over 10s, down from 20s it was taking on 1.9.2. A blank Rails app loads in 1.1s, which is even faster than 1.8.7.</p> <p><img src="https://img.skitch.com/20110528-cu9nux6619fxruh5rq6ppywp7p.jpg" alt="" /></p> <h2>Getting the fix</h2> <p>Here is how you can try out my patch right now in just ten minutes using <span class="caps">RVM</span>.</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></pre></td> <td class="code"><pre ondblclick="with (this.style) { overflow = (overflow == 'auto' || overflow == '') ? 'visible' : 'auto' }"># First get a baseline measurement<tt> </tt>cd /your/rails/app<tt> </tt>time script/rails runner &quot;puts 1&quot;<tt> </tt><tt> </tt># Install a patched ruby<tt> </tt>curl https://gist.github.com/raw/996418/e2b346fbadeed458506fc69ca213ad96d1d08c3e/require-performance-fix-r31758.patch &gt; /tmp/require-performance-fix.patch<tt> </tt>rvm install ruby-head --patch /tmp/require-performance-fix.patch -n patched<tt> </tt># ... get a cup of tea, this took about 8 minutes on my MBP<tt> </tt><tt> </tt># Get a new measurement<tt> </tt>cd /your/rails/app<tt> </tt>rvm use ruby-head-patched<tt> </tt>gem install bundler --no-rdoc --no-ri<tt> </tt>bundle<tt> </tt>time script/rails runner &quot;puts 1&quot;<tt> </tt></pre></td> </tr></table> <h2>How you can help</h2> <p>I need a lot more eyeballs on this patch before it can be considered for merging into trunk. I would really appreciate any of the following:</p> <ul> <li>Try it out on your app and report timings in the comments.</li> <li><a href="https://github.com/ruby/ruby/pull/25">Code review the patch on this GitHub pull request</a> (it&#8217;s C code, but don&#8217;t let that scare you off).</li> <li>Try it on Windows.</li> <li>Report any bugs you find.</li> </ul> <h2>Next steps</h2> <p>I imagine there will be a bit more work to get this into Ruby 1.9.3, but after that this is just the first step of many to try and speed up the time Rails takes to start up. Bundler and RubyGems still spend a lot of time doing &#8230; something, which I want to investigate. I also want to port these changes over to JRuby which has similar issues (Rubinius isn&#8217;t quite as fast out of the gate, but does not degrade exponentially so would not benefit from this patch).</p> <p>Thank you for your time.</p> tag:www.rhnh.net,2008:Post/845 2011-04-18T00:48:42Z 2011-04-18T00:48:14Z New Column: Code Safari <p>I am writing a regular weekly column at the newly launched Sitepoint project <a href="http://rubysource.com">RubySource</a>. The column is named &#8220;Code Safari&#8221;, where I explore the jungle of ruby libraries and gems and figure out how they work. It&#8217;s an introductory series designed to not just explain how things operate, but show you the tools and techniques so that you can figure it out yourself.</p> <p>Three posts have already been published:</p> <ul> <li><a href="http://rubysource.com/understanding-concurrent-programming-with-ruby-goliath/">Understanding Concurrent Programming With Ruby’s Goliath</a>, in which I dig into the new Goliath web server to figure out how it uses the new 1.9 Fibers to work some magic.</li> <li><a href="http://rubysource.com/code-safari-configuring-capybara/">Configuring Capybara</a>, in which I investigate how Capybara implemented its configuration <span class="caps">DSL</span>, and then make one for myself.</li> <li><a href="http://rubysource.com/code-safari-twss-and-bayesian-classification-of-twitter-searches/"><span class="caps">TWSS</span> and Bayesian Classification of Twitter Searches</a>, in which I inspect the pipes of a beautiful piece of plumbing.</li> </ul> <p>The format is a bit different but I&#8217;m really happy with how it is working so far. Let me know what you think.</p> tag:www.rhnh.net,2008:Post/844 2011-02-25T04:30:52Z 2011-02-25T04:30:52Z PostgreSQL 9 and ruby full text search tricks <p>I have just released an introduction to PostgreSQL screencast, published through PeepCode. It is over an hour long and covers a large number of juicy topics:</p> <ul> <li>Setup full text search</li> <li>Optimize search with triggers and indexes</li> <li>Use Postgres with Ruby on Rails 3</li> <li>Optimize indexes by including only the rows that you need</li> <li>Use database standards for more reliable queries</li> <li>Write powerful reports in only a few lines of code</li> <li>Convert an existing MySQL application to use Postgres</li> </ul> <p>It&#8217;s a steal at only $12. You can <a href="http://peepcode.com/products/postgresql">buy it over at PeepCode</a>.</p> <p>In it, I introduce full text search in postgres, and use a trigger to keep a search vector up to date. I&#8217;m not going to cover that here, but the point I get to is:</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></pre></td> <td class="code"><pre ondblclick="with (this.style) { overflow = (overflow == 'auto' || overflow == '') ? 'visible' : 'auto' }"><span class="r">CREATE</span> <span class="r">TRIGGER</span> posts_search_vector_refresh <tt> </tt> <span class="r">BEFORE</span> <span class="r">INSERT</span> <span class="r">OR</span> <span class="r">UPDATE</span> <span class="r">ON</span> posts <tt> </tt>FOR EACH ROW EXECUTE PROCEDURE<tt> </tt> tsvector_update_trigger(search_vector, <span class="s"><span class="dl">'</span><span class="k">pg_catalog.english</span><span class="dl">'</span></span>, body, title);<tt> </tt></pre></td> </tr></table> <p>That is good for simple models, but what if you want to index child models as well? For instance, we want to include comment authors in the search index. I rolled up my sleeves an came up with this:</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></pre></td> <td class="code"><pre ondblclick="with (this.style) { overflow = (overflow == 'auto' || overflow == '') ? 'visible' : 'auto' }"><span class="r">CREATE</span> <span class="r">OR</span> <span class="r">REPLACE</span> FUNCTION search_trigger() RETURNS <span class="r">trigger</span> <span class="r">AS</span> <span class="er">$</span><span class="er">$</span><tt> </tt>DECLARE<tt> </tt> search <span class="pt">TEXT</span>;<tt> </tt> child_search <span class="pt">TEXT</span>;<tt> </tt><span class="r">begin</span><tt> </tt> <span class="r">SELECT</span> string_agg(author_name, <span class="s"><span class="dl">'</span><span class="k"> </span><span class="dl">'</span></span>) <span class="r">INTO</span> child_search<tt> </tt> <span class="r">FROM</span> comments<tt> </tt> <span class="r">WHERE</span> post_id = new.id;<tt> </tt><tt> </tt> search <span class="er">:</span>= <span class="s"><span class="dl">'</span><span class="dl">'</span></span>;<tt> </tt> search <span class="er">:</span>= search || <span class="s"><span class="dl">'</span><span class="k"> </span><span class="dl">'</span></span> || coalesce(new.title);<tt> </tt> search <span class="er">:</span>= search || <span class="s"><span class="dl">'</span><span class="k"> </span><span class="dl">'</span></span> || coalesce(new.body);<tt> </tt> search <span class="er">:</span>= search || <span class="s"><span class="dl">'</span><span class="k"> </span><span class="dl">'</span></span> child_search;<tt> </tt><tt> </tt> new.search_index <span class="er">:</span>= to_tsvector(search); <tt> </tt> return new;<tt> </tt><span class="r">end</span><tt> </tt><span class="er">$</span><span class="er">$</span> LANGUAGE plpgsql;<tt> </tt><tt> </tt><span class="r">CREATE</span> <span class="r">TRIGGER</span> posts_search_vector_refresh <tt> </tt> <span class="r">BEFORE</span> <span class="r">INSERT</span> <span class="r">OR</span> <span class="r">UPDATE</span> <span class="r">ON</span> posts<tt> </tt>FOR EACH ROW EXECUTE PROCEDURE<tt> </tt> search_trigger();<tt> </tt></pre></td> </tr></table> <p>Getting a bit ugly eh. It might be nice to move that logic back into ruby land, but we have the problem that we need to call a database function to convert our search document into the correct data-type. In this case, a quick work around is to store a <code>search_document</code> in a text field on the model, then use a trigger to only index that field into our <code>search_vector</code> field. The <code>search_document</code> field can then easily be set from your <span class="caps">ORM</span>.</p> <p>Of course, any self-respecting rubyist should hide all this complexity behind a neat interface. I have come up with one using DataMapper that automatically adds the required triggers and indexes via auto-migrations. You use it thusly:</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></pre></td> <td class="code"><pre ondblclick="with (this.style) { overflow = (overflow == 'auto' || overflow == '') ? 'visible' : 'auto' }"><span class="r">class</span> <span class="cl">Post</span><tt> </tt> include <span class="co">DataMapper</span>::<span class="co">Resource</span><tt> </tt> include <span class="co">Searchable</span><tt> </tt><tt> </tt> property <span class="sy">:id</span>, <span class="co">Serial</span><tt> </tt> property <span class="sy">:title</span>, <span class="co">String</span><tt> </tt> property <span class="sy">:body</span>, <span class="co">Text</span><tt> </tt><tt> </tt> searchable <span class="sy">:title</span>, <span class="sy">:body</span> <span class="c"># Provides Post.search('keyword')</span><tt> </tt><span class="r">end</span><tt> </tt></pre></td> </tr></table> <p>You can find the <a href="https://github.com/xaviershay/sandbox/blob/master/misc/searchable.rb">Searchable module code over on github</a>. In it you can also find a fugly proof-of-concept for a <span class="caps">DSL</span> that generates the above <span class="caps">SQL</span> for indexing child models using DataMapper&#8217;s rich property model. It worked, but I&#8217;m not using it in any production code so I can hardly recommend it. Maybe you want to have a play though.</p> tag:www.rhnh.net,2008:Post/843 2011-01-31T10:37:00Z 2011-01-31T10:37:27Z YAML Tutorial <p>Many years ago I wrote a <a href="http://rhnh.net/2006/06/25/yaml-tutorial">tutorial on using <span class="caps">YAML</span> in ruby</a>. It still sees the most google traffic of any post, by far. So people want to know about <span class="caps">YAML</span>? I&#8217;ll help them out.</p> <h3>What is <span class="caps">YAML</span>?</h3> <p><span class="caps">YAML</span> is a flexible, human readable file format that is ideal for storing object trees. <span class="caps">YAML</span> stands for &#8220;<span class="caps">YAML</span> Ain’t Markup Language&#8221;. It is easier to read (by humans) than <span class="caps">JSON</span>, and can contain richer meta data. It is far nicer than <span class="caps">XML</span>. There are libraries available for all mainstream languages including Ruby, Python, C++, Java, Perl, C#/.<span class="caps">NET</span>, Javascript, <span class="caps">PHP</span> and Haskell. It looks like this:</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></pre></td> <td class="code"><pre ondblclick="with (this.style) { overflow = (overflow == 'auto' || overflow == '') ? 'visible' : 'auto' }"><span class="head"><span class="head">---</span></span> <tt> </tt>- <span class="s">name: Xavier</span><tt> </tt> <span class="ke">country</span>: <span class="s">Australia</span><tt> </tt> <span class="ke">age</span>: <span class="s">24</span><tt> </tt>- <span class="s">name: Don</span><tt> </tt> <span class="ke">country</span>: <span class="s">US</span><tt> </tt></pre></td> </tr></table> <p>That is a simple array of hashes. You can nest any combination of these simple data structures however you like. Most parsers will also detect the 24 as an integer too. Quoting strings is optional, and was omitted in this example.</p> <p><span class="caps">YAML</span> allows you to add tags to your objects, which is extra meta-data that your application can use to deserialize portions into complex data structures. For instance, in ruby if you serialize a set object it looks like this:</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></pre></td> <td class="code"><pre ondblclick="with (this.style) { overflow = (overflow == 'auto' || overflow == '') ? 'visible' : 'auto' }"><span class="c"># Set.new([1,2]).to_yaml</span><tt> </tt><span class="head"><span class="head">---</span></span> <span class="ty">!ruby/object</span>:<span class="cl">Set</span> <tt> </tt><span class="ke">hash</span>: <tt> </tt> <span class="ke">1</span>: <span class="s">true</span><tt> </tt> <span class="ke">2</span>: <span class="s">true</span><tt> </tt></pre></td> </tr></table> <p>Notice that ruby has added the <code>ruby/object:Set</code> tag so that the correct object can be instantiated on deserialization, while maintaining a human readable rendition of a set. These tags can be anything you like, ruby just happens to use that particular format.</p> <p>You can remove duplication from <span class="caps">YAML</span> files by using anchors (&amp;) and aliases (*). You typically see this in configuration files, such as:</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></pre></td> <td class="code"><pre ondblclick="with (this.style) { overflow = (overflow == 'auto' || overflow == '') ? 'visible' : 'auto' }"><span class="ke">defaults</span>: <span class="v">&amp;defaults</span><tt> </tt> <span class="ke">adapter</span>: <span class="s">postgres</span><tt> </tt> <span class="ke">host</span>: <span class="s">localhost</span><tt> </tt><tt> </tt><span class="ke">development</span>:<tt> </tt> <span class="ke">database</span>: <span class="s">myapp_development</span><tt> </tt> <span class="cv">&lt;&lt;</span>: <span class="gv">*defaults</span><tt> </tt><tt> </tt><span class="ke">test</span>:<tt> </tt> <span class="ke">database</span>: <span class="s">myapp_test</span><tt> </tt> <span class="cv">&lt;&lt;</span>: <span class="gv">*defaults</span><tt> </tt></pre></td> </tr></table> <p><code>&amp;</code> sets up the name of the anchor (&#8220;defaults&#8221;), <code>&lt;&lt;</code> means &#8220;merge the given hash into the current one&#8221;, and <code>*</code> includes the named anchor (&#8220;defaults&#8221; again). The expanded version looks like this:</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></pre></td> <td class="code"><pre ondblclick="with (this.style) { overflow = (overflow == 'auto' || overflow == '') ? 'visible' : 'auto' }"><span class="ke">defaults</span>:<tt> </tt> <span class="ke">adapter</span>: <span class="s">postgres</span><tt> </tt> <span class="ke">host</span>: <span class="s">localhost</span><tt> </tt><tt> </tt><span class="ke">development</span>:<tt> </tt> <span class="ke">database</span>: <span class="s">myapp_development</span><tt> </tt> <span class="ke">adapter</span>: <span class="s">postgres</span><tt> </tt> <span class="ke">host</span>: <span class="s">localhost</span><tt> </tt><tt> </tt><span class="ke">test</span>:<tt> </tt> <span class="ke">database</span>: <span class="s">myapp_test</span><tt> </tt> <span class="ke">adapter</span>: <span class="s">postgres</span><tt> </tt> <span class="ke">host</span>: <span class="s">localhost</span><tt> </tt></pre></td> </tr></table> <p>Note that the defaults hash hangs around, even though it isn&#8217;t really required anymore.</p> <p><span class="caps">YAML</span> generators use this technique to correctly serialize repeated references to the same object, and even cyclic references. That&#8217;s pretty clever.</p> <h3>Flow style</h3> <p><span class="caps">YAML</span> has an alternate synax called &#8220;flow style&#8221;, that allows arrays and hashes to be written inline without having to rely on indentation, using square brackets and curly brackets respectively.</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></pre></td> <td class="code"><pre ondblclick="with (this.style) { overflow = (overflow == 'auto' || overflow == '') ? 'visible' : 'auto' }"><span class="head"><span class="head">---</span></span> <tt> </tt><span class="c"># Arrays</span><tt> </tt><span class="ke">colors</span>:<tt> </tt> - <span class="s">red</span><tt> </tt> - <span class="s">blue</span><tt> </tt><span class="c"># in flow style...</span><tt> </tt><span class="ke">colors</span>: <span class="s">[red, blue]</span><tt> </tt><tt> </tt><span class="c"># Hashes</span><tt> </tt>- <span class="s">name: Xavier</span><tt> </tt> <span class="ke">age</span>: <span class="s">24</span><tt> </tt><span class="c"># in flow style...</span><tt> </tt>- <span class="s">{name: Xavier, age: 24}</span><tt> </tt></pre></td> </tr></table> <p>This has the curious effect of making <span class="caps">YAML</span> a superset of <span class="caps">JSON</span>. A valid <span class="caps">JSON</span> document is also a valid <span class="caps">YAML</span> document.</p> <h3>Performance</h3> <p>Given YAML&#8217;s richness and human readability, you would expect it to be slower than native serialization or <span class="caps">JSON</span>. This would be correct. My <a href="https://github.com/xaviershay/sandbox/blob/master/misc/yaml-test.rb">brief testing</a> shows it is about an order of magnitude slower. For the typical configuration use-case, this is irrelevant, but worth keeping in mind if you are doing something crazy. Remember to run your own benchmarks that represent your specific need.</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></pre></td> <td class="code"><pre ondblclick="with (this.style) { overflow = (overflow == 'auto' || overflow == '') ? 'visible' : 'auto' }"> user system total real<tt> </tt>Marshal serialize 0.090000 0.000000 0.090000 ( 0.091822)<tt> </tt>Marshal deserialize 0.090000 0.000000 0.090000 ( 0.092186)<tt> </tt>JSON serialize 0.480000 0.010000 0.490000 ( 0.480291)<tt> </tt>JSON deserialize 0.130000 0.010000 0.140000 ( 0.134860)<tt> </tt>YAML serialize 2.040000 0.020000 2.060000 ( 2.065693)<tt> </tt>YAML deserialize 0.520000 0.010000 0.530000 ( 0.526048)<tt> </tt>Psych serialize 2.530000 0.030000 2.560000 ( 2.565116)<tt> </tt>Psych deserialize 1.510000 0.120000 1.630000 ( 1.622601)<tt> </tt></pre></td> </tr></table> <p>Curiously, the new <span class="caps">YAML</span> parser Psych included in ruby 1.9.2 appears significantly slower than the old one. Not sure what is going on there.</p> <h3>Reading <span class="caps">YAML</span> from a file with ruby</h3><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></pre></td> <td class="code"><pre ondblclick="with (this.style) { overflow = (overflow == 'auto' || overflow == '') ? 'visible' : 'auto' }">require <span class="s"><span class="dl">'</span><span class="k">yaml</span><span class="dl">'</span></span><tt> </tt><tt> </tt>parsed = <span class="r">begin</span><tt> </tt> <span class="co">YAML</span>.load(<span class="co">File</span>.open(<span class="s"><span class="dl">&quot;</span><span class="k">/tmp/test.yml</span><span class="dl">&quot;</span></span>))<tt> </tt><span class="r">rescue</span> <span class="co">ArgumentError</span> =&gt; e<tt> </tt> puts <span class="s"><span class="dl">&quot;</span><span class="k">Could not parse YAML: </span><span class="il"><span class="idl">#{</span>e.message<span class="idl">}</span></span><span class="dl">&quot;</span></span><tt> </tt><span class="r">end</span><tt> </tt></pre></td> </tr></table> <h3>Writing <span class="caps">YAML</span> to a file with ruby</h3><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></pre></td> <td class="code"><pre ondblclick="with (this.style) { overflow = (overflow == 'auto' || overflow == '') ? 'visible' : 'auto' }">require <span class="s"><span class="dl">'</span><span class="k">yaml</span><span class="dl">'</span></span><tt> </tt><tt> </tt>data = {<span class="s"><span class="dl">&quot;</span><span class="k">name</span><span class="dl">&quot;</span></span> =&gt; <span class="s"><span class="dl">&quot;</span><span class="k">Xavier</span><span class="dl">&quot;</span></span>}<tt> </tt><span class="co">File</span>.open(<span class="s"><span class="dl">&quot;</span><span class="k">path/to/output.yml</span><span class="dl">&quot;</span></span>, <span class="s"><span class="dl">&quot;</span><span class="k">w</span><span class="dl">&quot;</span></span>) {|f| f.write(data.to_yaml) }<tt> </tt></pre></td> </tr></table> <p>Anything else you&#8217;d like to know? Leave a comment.</p> tag:www.rhnh.net,2008:Post/842 2011-01-31T10:27:00Z 2011-01-31T10:29:15Z Psych YAML in ruby 1.9.2 with RVM and Snow Leopard OSX <p>Note that you must have libyaml installed <em>before</em> you compile ruby, so this probably means you&#8217;ll need to recompile your current version.</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></pre></td> <td class="code"><pre ondblclick="with (this.style) { overflow = (overflow == 'auto' || overflow == '') ? 'visible' : 'auto' }">sudo brew install libyaml<tt> </tt>rvm install ruby-1.9.2 --with-libyaml-dir=/usr/local<tt> </tt>ruby -rpsych -e 'puts Psych.load(&quot;win: true&quot;)'<tt> </tt></pre></td> </tr></table> tag:www.rhnh.net,2008:Post/841 2010-12-01T01:01:58Z 2010-12-01T01:01:58Z Ordering by a field in a join model with DataMapper <p>The public interface for datamapper 1.0.3 does not support ordering by a column in a joined model on a query. The core of datamapper does support this though, so we can use some hacks to make it work, as the following code demonstrates.</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>31<tt> </tt>32<tt> </tt>33<tt> </tt>34<tt> </tt>35<tt> </tt>36<tt> </tt>37<tt> </tt>38<tt> </tt>39<tt> </tt><strong>40</strong><tt> </tt>41<tt> </tt>42<tt> </tt>43<tt> </tt></pre></td> <td class="code"><pre ondblclick="with (this.style) { overflow = (overflow == 'auto' || overflow == '') ? 'visible' : 'auto' }">require <span class="s"><span class="dl">'</span><span class="k">rubygems</span><span class="dl">'</span></span><tt> </tt>require <span class="s"><span class="dl">'</span><span class="k">dm-core</span><span class="dl">'</span></span><tt> </tt>require <span class="s"><span class="dl">'</span><span class="k">dm-migrations</span><span class="dl">'</span></span><tt> </tt><tt> </tt><span class="co">DataMapper</span>::<span class="co">Logger</span>.new(<span class="gv">$stdout</span>, <span class="sy">:debug</span>)<tt> </tt><span class="co">DataMapper</span>.setup(<span class="sy">:default</span>, <span class="s"><span class="dl">'</span><span class="k">postgres://localhost/test</span><span class="dl">'</span></span>) <span class="c"># createdb test</span><tt> </tt><tt> </tt><span class="r">class</span> <span class="cl">User</span><tt> </tt> include <span class="co">DataMapper</span>::<span class="co">Resource</span><tt> </tt><tt> </tt> property <span class="sy">:id</span>, <span class="co">Serial</span><tt> </tt><tt> </tt> has <span class="i">1</span>, <span class="sy">:user_profile</span><tt> </tt><tt> </tt> <span class="r">def</span> <span class="pc">self</span>.ranked<tt> </tt> order = <span class="co">DataMapper</span>::<span class="co">Query</span>::<span class="co">Direction</span>.new(user_profile.ranking, <span class="sy">:desc</span>) <tt> </tt> query = all.query <span class="c"># Access a blank query object for us to manipulate</span><tt> </tt> query.instance_variable_set(<span class="s"><span class="dl">&quot;</span><span class="k">@order</span><span class="dl">&quot;</span></span>, [order])<tt> </tt><tt> </tt> <span class="c"># Force the user_profile model to be joined into the query</span><tt> </tt> query.instance_variable_set(<span class="s"><span class="dl">&quot;</span><span class="k">@links</span><span class="dl">&quot;</span></span>, [relationships[<span class="s"><span class="dl">'</span><span class="k">user_profile</span><span class="dl">'</span></span>].inverse])<tt> </tt><tt> </tt> all(query) <span class="c"># Create a new collection with the modified query</span><tt> </tt> <span class="r">end</span><tt> </tt><span class="r">end</span><tt> </tt><tt> </tt><span class="r">class</span> <span class="cl">UserProfile</span><tt> </tt> include <span class="co">DataMapper</span>::<span class="co">Resource</span><tt> </tt><tt> </tt> property <span class="sy">:user_id</span>, <span class="co">Integer</span>, <span class="sy">:key</span> =&gt; <span class="pc">true</span><tt> </tt> property <span class="sy">:ranking</span>, <span class="co">Integer</span>, <span class="sy">:default</span> =&gt; <span class="i">0</span><tt> </tt><tt> </tt> belongs_to <span class="sy">:user</span><tt> </tt><span class="r">end</span><tt> </tt><tt> </tt><span class="co">DataMapper</span>.finalize<tt> </tt><span class="co">DataMapper</span>.auto_migrate!<tt> </tt><tt> </tt><span class="co">User</span>.create(<span class="sy">:user_profile</span> =&gt; <span class="co">UserProfile</span>.new(<span class="sy">:ranking</span> =&gt; <span class="i">2</span>))<tt> </tt><span class="co">User</span>.create(<span class="sy">:user_profile</span> =&gt; <span class="co">UserProfile</span>.new(<span class="sy">:ranking</span> =&gt; <span class="i">5</span>))<tt> </tt><span class="co">User</span>.create(<span class="sy">:user_profile</span> =&gt; <span class="co">UserProfile</span>.new(<span class="sy">:ranking</span> =&gt; <span class="i">3</span>))<tt> </tt><tt> </tt>puts <span class="co">User</span>.ranked.map {|x| x.user_profile.ranking }.inspect<tt> </tt></pre></td> </tr></table> tag:www.rhnh.net,2008:Post/839 2010-10-13T09:43:01Z 2010-10-13T09:43:01Z Padrino, MongoHQ and Heroku <p>Next time I google for this I&#8217;ll find the answer waiting:</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></pre></td> <td class="code"><pre ondblclick="with (this.style) { overflow = (overflow == 'auto' || overflow == '') ? 'visible' : 'auto' }"><span class="c"># config/database.rb</span><tt> </tt><span class="r">if</span> <span class="co">ENV</span>[<span class="s"><span class="dl">'</span><span class="k">MONGOHQ_URL</span><span class="dl">'</span></span>]<tt> </tt> uri = <span class="co">URI</span>.parse(<span class="co">ENV</span>[<span class="s"><span class="dl">'</span><span class="k">MONGOHQ_URL</span><span class="dl">'</span></span>])<tt> </tt> <span class="co">MongoMapper</span>.connection = <span class="co">Mongo</span>::<span class="co">Connection</span>.from_uri(<span class="co">ENV</span>[<span class="s"><span class="dl">'</span><span class="k">MONGOHQ_URL</span><span class="dl">'</span></span>], <span class="sy">:logger</span> =&gt; logger)<tt> </tt> <span class="co">MongoMapper</span>.database = uri.path.gsub(<span class="rx"><span class="dl">/</span><span class="k">^</span><span class="ch">\/</span><span class="dl">/</span></span>, <span class="s"><span class="dl">'</span><span class="dl">'</span></span>)<tt> </tt><span class="r">else</span><tt> </tt> <span class="co">MongoMapper</span>.connection = <span class="co">Mongo</span>::<span class="co">Connection</span>.new(<span class="s"><span class="dl">'</span><span class="k">localhost</span><span class="dl">'</span></span>, <span class="pc">nil</span>, <span class="sy">:logger</span> =&gt; logger)<tt> </tt> <span class="co">MongoMapper</span>.database = <span class="s"><span class="dl">&quot;</span><span class="k">myapp_</span><span class="il"><span class="idl">#{</span><span class="co">Padrino</span>.env<span class="idl">}</span></span><span class="dl">&quot;</span></span><tt> </tt><span class="r">end</span><tt> </tt></pre></td> </tr></table> <p>Also I&#8217;ll write MongoDB here for google. Nicked from <a href="http://github.com/bratta/fikus/blob/master/config/database.rb">Fikus</a>.</p> tag:www.rhnh.net,2008:Post/838 2010-10-10T00:00:00Z 2010-10-10T04:25:57Z Rails 3, Ruby 1.9.2, Windows 2008, and SQL Server 2008 Tutorial <p>This took me a while to figure out, especially since I&#8217;m not so great with either windows or <span class="caps">SQL</span> server, but in the end the process isn&#8217;t so difficult.</p> <p><iframe src="http://player.vimeo.com/video/15701033?title=0&amp;byline=0&amp;portrait=0&amp;color=FFFACD" width="600" height="510" frameborder="0"></iframe><p><a href="http://vimeo.com/15701033">Rails 3, Ruby 1.9.2, Windows 2008, and <span class="caps">SQL</span> Server 2008 Screencast</a></p></p> <p>The steps covered in this screencast are:</p> <ol> <li>Create user</li> <li>Create database</li> <li>Give user permissions</li> <li>Create <span class="caps">DSN</span></li> <li>Install <a href="http://rubyinstaller.org/">ruby</a></li> <li>Install <a href="http://github.com/oneclick/rubyinstaller/wiki/Development-Kit">devkit</a> (Needed to complie native extensions for <span class="caps">ODBC</span>)</li> <li>Create a new rails app</li> <li>Add <code>activerecord-sqlserver-adapter</code> and <code>ruby-odbc</code> to Gemfile</li> <li>Customize <code>config/database.yml</code></li> </ol><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></pre></td> <td class="code"><pre ondblclick="with (this.style) { overflow = (overflow == 'auto' || overflow == '') ? 'visible' : 'auto' }"><span class="c"># config/database.yml</span><tt> </tt><span class="ke">development</span>:<tt> </tt> <span class="ke">adapter</span>: <span class="s">sqlserver</span><tt> </tt> <span class="ke">dsn</span>: <span class="s">testdsn_user</span><tt> </tt> <span class="ke">mode</span>: <span class="s">odbc</span><tt> </tt> <span class="ke">database</span>: <span class="s">test</span><tt> </tt> <span class="ke">username</span>: <span class="s">xavier</span><tt> </tt> <span class="ke">password</span>:<tt> </tt></pre></td> </tr></table> <p>Some errors you may encounter:</p> <p><strong>The specified module could not be found &#8211; odbc.so</strong> You have likely copied odbc.so from i386-msvcrt-ruby-odbc.zip. This is for 1.8.7, and does not work for 1.9. Remove the .so file, and install ruby-odbc as above.</p> <p><strong>The specified <span class="caps">DSN</span> contains an architecture mismatch between the Driver and the Application.</strong> Perhaps you have created a system <span class="caps">DSN</span>. Try creating a user <span class="caps">DSN</span> instead. I also found some suggestions that you need to <a href="http://social.answers.microsoft.com/Forums/en-US/addbuz/thread/a2c16c27-194f-452f-8447-5a70a4178a42">use a different version of the <span class="caps">ODBC</span> configuration panel</a>, but this wasn&#8217;t relevant for me.</p> tag:www.rhnh.net,2008:Post/837 2010-10-06T23:31:02Z 2010-10-06T23:31:02Z Transactional before all with RSpec and DataMapper <p>By default, <code>before(:all)</code> in rspec executes <em>outside</em> of any transaction, meaning that you can&#8217;t really use it for creating objects. Normally this should go in a <code>before(:each)</code>, but for a spec with simple creation and a large number of assertions this is terribly inefficient.</p> <p>Let&#8217;s fix it!</p> <p>This code assumes you are using DataMapper, and that your database supports some form of nested transactions (at the very least faking them with savepoints &#8211; see <a href="http://rhnh.net/2010/10/06/nested-transactions-in-postgres-with-datamapper">nested transactions in postgres with datamapper</a>). It wraps each before/after <code>:all</code> and <code>:each</code> in it&#8217;s own transaction.</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></pre></td> <td class="code"><pre ondblclick="with (this.style) { overflow = (overflow == 'auto' || overflow == '') ? 'visible' : 'auto' }"><span class="co">RSpec</span>.configure <span class="r">do</span> |config|<tt> </tt> [<span class="sy">:all</span>, <span class="sy">:each</span>].each <span class="r">do</span> |x|<tt> </tt> config.before(x) <span class="r">do</span><tt> </tt> repository(<span class="sy">:default</span>) <span class="r">do</span> |repository|<tt> </tt> transaction = <span class="co">DataMapper</span>::<span class="co">Transaction</span>.new(repository)<tt> </tt> transaction.begin<tt> </tt> repository.adapter.push_transaction(transaction)<tt> </tt> <span class="r">end</span><tt> </tt> <span class="r">end</span><tt> </tt><tt> </tt> config.after(x) <span class="r">do</span><tt> </tt> repository(<span class="sy">:default</span>).adapter.pop_transaction.rollback<tt> </tt> <span class="r">end</span><tt> </tt> <span class="r">end</span><tt> </tt><tt> </tt> config.include(<span class="co">RSpecExtensions</span>::<span class="co">Set</span>)<tt> </tt><span class="r">end</span><tt> </tt></pre></td> </tr></table> <p>See that <code>RSpecExtensions::Set</code> include? That&#8217;s a version of the lovely <code>let</code> helpers that works with before(:all) setup. Props to <a href="http://twitter.com/pcreux">pcreux</a> for this:</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>31<tt> </tt>32<tt> </tt>33<tt> </tt></pre></td> <td class="code"><pre ondblclick="with (this.style) { overflow = (overflow == 'auto' || overflow == '') ? 'visible' : 'auto' }"><span class="r">module</span> <span class="cl">RSpecExtensions</span><tt> </tt> <span class="r">module</span> <span class="cl">Set</span><tt> </tt><tt> </tt> <span class="r">module</span> <span class="cl">ClassMethods</span><tt> </tt> <span class="c"># Generates a method whose return value is memoized</span><tt> </tt> <span class="c"># in before(:all). Great for DB setup when combined with</span><tt> </tt> <span class="c"># transactional before alls.</span><tt> </tt> <span class="r">def</span> <span class="fu">set</span>(name, &amp;block)<tt> </tt> define_method(name) <span class="r">do</span><tt> </tt> __memoized[name] ||= instance_eval(&amp;block)<tt> </tt> <span class="r">end</span><tt> </tt> before(<span class="sy">:all</span>) { __send__(name) }<tt> </tt> before(<span class="sy">:each</span>) <span class="r">do</span><tt> </tt> __send__(name).tap <span class="r">do</span> |obj|<tt> </tt> obj.reload <span class="r">if</span> obj.respond_to?(<span class="sy">:reload</span>)<tt> </tt> <span class="r">end</span><tt> </tt> <span class="r">end</span><tt> </tt> <span class="r">end</span><tt> </tt> <span class="r">end</span><tt> </tt><tt> </tt> <span class="r">module</span> <span class="cl">InstanceMethods</span><tt> </tt> <span class="r">def</span> <span class="fu">__memoized</span> <span class="c"># :nodoc:</span><tt> </tt> <span class="iv">@__memoized</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="pc">self</span>.included(mod) <span class="c"># :nodoc:</span><tt> </tt> mod.extend <span class="co">ClassMethods</span><tt> </tt> mod.__send__ <span class="sy">:include</span>, <span class="co">InstanceMethods</span><tt> </tt> <span class="r">end</span><tt> </tt><tt> </tt> <span class="r">end</span><tt> </tt><span class="r">end</span><tt> </tt></pre></td> </tr></table> <p>Fast specs make me a happy man.</p> tag:www.rhnh.net,2008:Post/836 2010-10-06T04:34:00Z 2010-10-06T02:34:07Z Nested Transactions in Postgres with DataMapper <p>Hacks to get nested transactions support for Postgres in DataMapper. Not extensively tested, more a proof of concept. It re-opens the existing <code>Transaction</code> class to add a check for whether we need a nested transaction or not, and adds a new <code>NestedTransaction</code> transaction primitive that issues savepoint commands rather than begin/commit.</p> <p>I put this code in a Rails initializer.</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>31<tt> </tt>32<tt> </tt>33<tt> </tt>34<tt> </tt>35<tt> </tt>36<tt> </tt>37<tt> </tt>38<tt> </tt>39<tt> </tt><strong>40</strong><tt> </tt>41<tt> </tt>42<tt> </tt>43<tt> </tt>44<tt> </tt>45<tt> </tt>46<tt> </tt>47<tt> </tt>48<tt> </tt>49<tt> </tt><strong>50</strong><tt> </tt>51<tt> </tt>52<tt> </tt>53<tt> </tt>54<tt> </tt>55<tt> </tt>56<tt> </tt>57<tt> </tt>58<tt> </tt>59<tt> </tt><strong>60</strong><tt> </tt>61<tt> </tt>62<tt> </tt>63<tt> </tt>64<tt> </tt>65<tt> </tt>66<tt> </tt>67<tt> </tt>68<tt> </tt>69<tt> </tt><strong>70</strong><tt> </tt>71<tt> </tt>72<tt> </tt>73<tt> </tt>74<tt> </tt>75<tt> </tt>76<tt> </tt>77<tt> </tt>78<tt> </tt>79<tt> </tt><strong>80</strong><tt> </tt>81<tt> </tt>82<tt> </tt>83<tt> </tt>84<tt> </tt>85<tt> </tt>86<tt> </tt>87<tt> </tt>88<tt> </tt>89<tt> </tt><strong>90</strong><tt> </tt>91<tt> </tt>92<tt> </tt>93<tt> </tt>94<tt> </tt></pre></td> <td class="code"><pre ondblclick="with (this.style) { overflow = (overflow == 'auto' || overflow == '') ? 'visible' : 'auto' }"><span class="c"># Hacks to get nested transactions in Postgres</span><tt> </tt><span class="c"># Not extensively tested, more a proof of concept</span><tt> </tt><span class="c">#</span><tt> </tt><span class="c"># It re-opens the existing Transaction class to add a check for whether</span><tt> </tt><span class="c"># we need a nested transaction or not, and adds a new NestedTransaction</span><tt> </tt><span class="c"># transaction primitive that issues savepoint commands rather than begin/commit.</span><tt> </tt><tt> </tt><span class="r">module</span> <span class="cl">DataMapper</span><tt> </tt> <span class="r">module</span> <span class="cl">Resource</span><tt> </tt> <span class="r">def</span> <span class="fu">transaction</span>(&amp;block)<tt> </tt> <span class="pc">self</span>.class.transaction(&amp;block)<tt> </tt> <span class="r">end</span><tt> </tt> <span class="r">end</span><tt> </tt><tt> </tt> <span class="r">class</span> <span class="cl">Transaction</span><tt> </tt> <span class="c"># Overridden to allow nested transactions</span><tt> </tt> <span class="r">def</span> <span class="fu">connect_adapter</span>(adapter)<tt> </tt> <span class="r">if</span> <span class="iv">@transaction_primitives</span>.key?(adapter)<tt> </tt> raise <span class="s"><span class="dl">&quot;</span><span class="k">Already a primitive for adapter </span><span class="il"><span class="idl">#{</span>adapter<span class="idl">}</span></span><span class="dl">&quot;</span></span><tt> </tt> <span class="r">end</span><tt> </tt><tt> </tt> primitive = <span class="r">if</span> adapter.current_transaction<tt> </tt> adapter.nested_transaction_primitive<tt> </tt> <span class="r">else</span><tt> </tt> adapter.transaction_primitive<tt> </tt> <span class="r">end</span><tt> </tt><tt> </tt> <span class="iv">@transaction_primitives</span>[adapter] = validate_primitive(primitive)<tt> </tt> <span class="r">end</span><tt> </tt> <span class="r">end</span><tt> </tt><tt> </tt> <span class="r">module</span> <span class="cl">NestedTransactions</span><tt> </tt> <span class="r">def</span> <span class="fu">nested_transaction_primitive</span><tt> </tt> <span class="co">DataObjects</span>::<span class="co">NestedTransaction</span>.create_for_uri(normalized_uri, current_connection)<tt> </tt> <span class="r">end</span><tt> </tt> <span class="r">end</span><tt> </tt><tt> </tt> <span class="r">class</span> <span class="cl">NestedTransactionConfig</span> &lt; <span class="co">Rails</span>::<span class="co">Railtie</span><tt> </tt> config.after_initialize <span class="r">do</span><tt> </tt> repository.adapter.extend(<span class="co">DataMapper</span>::<span class="co">NestedTransactions</span>)<tt> </tt> <span class="r">end</span><tt> </tt> <span class="r">end</span><tt> </tt><span class="r">end</span><tt> </tt><tt> </tt><span class="r">module</span> <span class="cl">DataObjects</span><tt> </tt> <span class="r">class</span> <span class="cl">NestedTransaction</span> &lt; <span class="co">Transaction</span><tt> </tt><tt> </tt> <span class="c"># The host name. Note, this relies on the host name being configured</span><tt> </tt> <span class="c"># and resolvable using DNS</span><tt> </tt> <span class="co">HOST</span> = <span class="s"><span class="dl">&quot;</span><span class="il"><span class="idl">#{</span><span class="co">Socket</span>::gethostbyname(<span class="co">Socket</span>::gethostname)[<span class="i">0</span>]<span class="idl">}</span></span><span class="dl">&quot;</span></span> <span class="r">rescue</span> <span class="s"><span class="dl">&quot;</span><span class="k">localhost</span><span class="dl">&quot;</span></span><tt> </tt> <span class="cv">@@counter</span> = <span class="i">0</span><tt> </tt><tt> </tt> <span class="c"># The connection object for this transaction - must have already had</span><tt> </tt> <span class="c"># a transaction begun on it</span><tt> </tt> attr_reader <span class="sy">:connection</span><tt> </tt> <span class="c"># A unique ID for this transaction</span><tt> </tt> attr_reader <span class="sy">:id</span><tt> </tt><tt> </tt> <span class="r">def</span> <span class="pc">self</span>.create_for_uri(uri, connection)<tt> </tt> uri = uri.is_a?(<span class="co">String</span>) ? <span class="co">URI</span>::parse(uri) : uri<tt> </tt> <span class="co">DataObjects</span>::<span class="co">NestedTransaction</span>.new(uri, connection)<tt> </tt> <span class="r">end</span><tt> </tt><tt> </tt> <span class="c">#</span><tt> </tt> <span class="c"># Creates a NestedTransaction bound to an existing connection</span><tt> </tt> <span class="c">#</span><tt> </tt> <span class="r">def</span> <span class="fu">initialize</span>(uri, connection)<tt> </tt> <span class="iv">@connection</span> = connection<tt> </tt> <span class="iv">@id</span> = <span class="co">Digest</span>::<span class="co">SHA256</span>.hexdigest(<tt> </tt> <span class="s"><span class="dl">&quot;</span><span class="il"><span class="idl">#{</span><span class="co">HOST</span><span class="idl">}</span></span><span class="k">:</span><span class="il"><span class="idl">#{</span><span class="gv">$$</span><span class="idl">}</span></span><span class="k">:</span><span class="il"><span class="idl">#{</span><span class="co">Time</span>.now.to_f<span class="idl">}</span></span><span class="k">:nested:</span><span class="il"><span class="idl">#{</span><span class="cv">@@counter</span> += <span class="i">1</span><span class="idl">}</span></span><span class="dl">&quot;</span></span>)<tt> </tt> <span class="r">end</span><tt> </tt><tt> </tt> <span class="r">def</span> <span class="fu">close</span><tt> </tt> <span class="r">end</span><tt> </tt><tt> </tt> <span class="r">def</span> <span class="fu">begin</span><tt> </tt> run <span class="s"><span class="dl">%{</span><span class="k">SAVEPOINT &quot;</span><span class="il"><span class="idl">#{</span><span class="iv">@id</span><span class="idl">}</span></span><span class="k">&quot;</span><span class="dl">}</span></span><tt> </tt> <span class="r">end</span><tt> </tt><tt> </tt> <span class="r">def</span> <span class="fu">commit</span><tt> </tt> run <span class="s"><span class="dl">%{</span><span class="k">RELEASE SAVEPOINT &quot;</span><span class="il"><span class="idl">#{</span><span class="iv">@id</span><span class="idl">}</span></span><span class="k">&quot;</span><span class="dl">}</span></span><tt> </tt> <span class="r">end</span><tt> </tt><tt> </tt> <span class="r">def</span> <span class="fu">rollback</span><tt> </tt> run <span class="s"><span class="dl">%{</span><span class="k">ROLLBACK TO SAVEPOINT &quot;</span><span class="il"><span class="idl">#{</span><span class="iv">@id</span><span class="idl">}</span></span><span class="k">&quot;</span><span class="dl">}</span></span><tt> </tt> <span class="r">end</span><tt> </tt><tt> </tt> private<tt> </tt> <span class="r">def</span> <span class="fu">run</span>(cmd)<tt> </tt> connection.create_command(cmd).execute_non_query<tt> </tt> <span class="r">end</span><tt> </tt> <span class="r">end</span><tt> </tt><span class="r">end</span><tt> </tt><tt> </tt></pre></td> </tr></table> <p>I wrote code similar to this with <a href="http://twitter.com/hassox">hassox</a> while at <span class="caps">NZX</span>, big ups to those guys. I&#8217;m working on a proper patch, but haven&#8217;t quite figured out the internals enough. If you know how DataMapper works, please check out and comment on this <a href="http://gist.github.com/598001">sample patch for three dm gems</a>.</p> tag:www.rhnh.net,2008:Post/835 2010-09-23T09:28:41Z 2010-09-23T09:28:41Z Why I Rewrote Chronic <p>It seems like a pretty epic yak shave. If you want to parse natural language dates in ruby, you use Chronic. That&#8217;s just how it is. (There&#8217;s also Tickle for recurring dates, which is similar, but based on Chronic anyways.) It&#8217;s the standard, everyone uses it, so why oh why did I write my own version from scratch?</p> <p>Three reasons I can see.</p> <p><strong>Chronic is unmaintained.</strong> Check the network graph for Chronic. A more avid historian could turn this into an epic teledrama, but for now here&#8217;s the summary: The main repository hasn&#8217;t had a commit since late 2008. Evaryont made a valiant attempt to take the reins, but his stamina only lasted an extra year to August 2009. Since then numerous people have forked his efforts, mostly to add 1.9 support. These efforts are fragmented though. The inertia of such a large project with no clear leadership sees every man running for himself.</p> <p>Further, the new maintainers aren&#8217;t providing a rock solid base. From Evaryont&#8217;s <span class="caps">README</span>:<br /> <em>I decided on my own volition that the 40-some (as reported by Github) network should be merged together. I got it to run, but <strong>quite haphazardly</strong>. There are a lot of new features (<strong>mostly undocumented</strong> except the git logs) so be a little flexible in your language passed to Chronic.</em> [emphasis mine]</p> <p>This does not fill me with confidence.</p> <p><strong>Chronic has a large barrier to entry.</strong> Natural date parsing is a big challenge. In the original <span class="caps">README</span>, there are ~50 examples of formats it supports, and that is excluding all of the features added in forks in the last two years. The result is a large code base which is intimidating for a new comer, especially with no high level guidance as to how everything fits together. On a project of this size, &#8220;the documentation is in the specs&#8221; is insufficient. I know what it <strong>does</strong>, I need to know <strong>how</strong> it does it.</p> <p><strong>Chronic solves the wrong problem.</strong> I want an alternative to date pickers. As such, I don&#8217;t need time support, and I only need very simple day parsing. Chronic seems geared towards a calendar type application (&#8220;tomorrow at 6:45pm&#8221;), but also parses many expressions which simply are not useful in a real application either because they are obtuse <del>-</del> &#8220;7 hours before tomorrow at noon&#8221; <del>-</del> or just not how users think about dates <del>-</del> &#8220;3 months ago saturday at 5:00 pm&#8221;. (Note the last assertion is a totally unsubstantiated claim with no user research to support it.)</p> <p>Further, it is not hard to find simple examples that Chronic doesn&#8217;t support. Omitting a year is an easy one: 14 Sep, April 9.</p> <h2>So what to do?</h2> <p>Chronic needs a leader. Chronic neads a hero. One man to reunite the forks, document the code, and deliver it to the promised land.</p> <p>I am not that man.</p> <p>I sketched out the formats I actually needed to support for my application, looked at it and thought &#8220;really it can&#8217;t be that hard&#8221;. Natural date parsing is hard; parsing only the dates your application requires is easy. One hour later I had a gem that not only had 100% support for all of the Chronic features I had been using, but also covered some extra formats I wanted (&#8220;14 Sep&#8221;), and could also convert a date <em>back</em> into a human readable description. That&#8217;s less time than I had already sunk into trying to get Chronic working.</p> <p><a href="http://github.com/xaviershay/kronic">Introducing Kronic.</a></p> <p>Less than 100 lines of code, totally specced, totally solved my problem. Ultimately, I don&#8217;t want to deal with this problem, so I wanted the easiest solution. While patching Chronic would intuitively appear to be pragmatic, a quick spike in the other direction turned out to be worthwhile. Sometimes 80% just isn&#8217;t that hard.</p>