<?xml version="1.0" encoding="utf-8"?>
<feed xmlns="http://www.w3.org/2005/Atom">

 <title>semanticart blog</title>
 <link href="http://blog.semanticart.com/atom.xml" rel="self"/>
 <link href="http://blog.semanticart.com/"/>
 <updated>2010-02-06T19:51:58-06:00</updated>
 <id>http://blog.semanticart.com/</id>
 <author>
   <name>Jefrey Chupp</name>
   <email>jeff@semanticart.com</email>
 </author>

 
 <entry>
   <title>How do you handle Page titles in Rails?  I use SmartMeta</title>
   <link href="http://blog.semanticart.com/2010/02/06/how-do-you-handle-page-titles-in-rails-i-use-smartmeta.html"/>
   <updated>2010-02-06T00:00:00-06:00</updated>
   <id>http://blog.semanticart.com/2010/02/06/how-do-you-handle-page-titles-in-rails-i-use-smartmeta</id>
   <content type="html">&lt;p&gt;How do you handle Page titles in Rails?  I&amp;#8217;ve tried a few different methods over the years.  I could never really settle on where I thought the data should go.  I&amp;#8217;ve set instance variables in controllers, I&amp;#8217;ve set instance variables in views, I&amp;#8217;ve used content_for in views&amp;#8230; etc.&lt;/p&gt;
&lt;p&gt;Once Rails implemented I18n support and starting using locale files, I finally felt like I&amp;#8217;d found a good place to put this data.  I started doing things like this:&lt;/p&gt;
&lt;pre&gt;
en:
  default:
    title: &quot;My default title&quot;
  users:
    index: &quot;My Awesome User index&quot;
    show: &quot;{{name}}'s page!&quot;
&lt;/pre&gt;
&lt;p&gt;And the nice thing is that since you&amp;#8217;re already putting all this in the locale files, internationalization is easy.  I&amp;#8217;d then use something in my views like:&lt;/p&gt;
&lt;div class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;erb&quot;&gt;&lt;span class=&quot;cp&quot;&gt;&amp;lt;%&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;content_for&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;ss&quot;&gt;:html_title&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;t&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&amp;#39;users.show&amp;#39;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;ss&quot;&gt;:name&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;vi&quot;&gt;@user&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;))&lt;/span&gt; &lt;span class=&quot;cp&quot;&gt;%&amp;gt;&lt;/span&gt;&lt;span class=&quot;x&quot;&gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;&lt;p&gt;and something like&lt;/p&gt;
&lt;div class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;erb&quot;&gt;&lt;span class=&quot;x&quot;&gt;&amp;lt;title&amp;gt;&lt;/span&gt;&lt;span class=&quot;cp&quot;&gt;&amp;lt;%=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;yield&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;ss&quot;&gt;:html_title&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;||&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;t&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&amp;#39;default.title&amp;#39;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;cp&quot;&gt;%&amp;gt;&lt;/span&gt;&lt;span class=&quot;x&quot;&gt;&amp;lt;/title&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;&lt;p&gt;in my layouts.&lt;/p&gt;
&lt;p&gt;It worked out pretty well, but the translations are dumb.  I ended up having to specify several cases in my locale file dependent upon how the controller actions were used.  If my action was nested, I might want my page title to reflect that.  The &lt;span class=&quot;caps&quot;&gt;YAML&lt;/span&gt; locale file didn&amp;#8217;t really allow for this easily so I used some conditional logic in my helpers.&lt;/p&gt;
&lt;p&gt;This approach worked but it wasn&amp;#8217;t pretty.  There was also a lot of redundancy.&lt;/p&gt;
&lt;h3&gt;SmartMeta&lt;/h3&gt;
&lt;p&gt;Now I&amp;#8217;m using &lt;a href=&quot;http://github.com/semanticart/smart-meta&quot;&gt;SmartMeta&lt;/a&gt;, a plugin that is just a simple extraction of my most recent pattern.  There are other plugins out there that assist in storing titles in &lt;span class=&quot;caps&quot;&gt;YAML&lt;/span&gt;, but with SmartMeta your translation entries can access your instance variables and your methods.&lt;/p&gt;
&lt;p&gt;As an example, you can do things like:&lt;/p&gt;
&lt;pre&gt;
en:
  users:
    show:
      title: &quot;{{@user.name}} has {{@user.hair.color}} hair&quot;
      description: &quot;{{@user.name}}'s page on my site&quot;
      keywords: &quot;user, {{@user.name}}, {{page_keywords}}, etc. etc.&quot;
&lt;/pre&gt;
&lt;p&gt;and&lt;/p&gt;
&lt;div class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;erb&quot;&gt;&lt;span class=&quot;x&quot;&gt;&amp;lt;title&amp;gt;&lt;/span&gt;&lt;span class=&quot;cp&quot;&gt;&amp;lt;%=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;yield&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;ss&quot;&gt;:html_title&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;||&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;smart_title&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;||&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;t&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&amp;#39;default.title&amp;#39;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;cp&quot;&gt;%&amp;gt;&lt;/span&gt;&lt;span class=&quot;x&quot;&gt;&amp;lt;/title&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;&lt;p&gt;I&amp;#8217;m making use of the @user variable.  You can send message after message &amp;#8211; in the title, we&amp;#8217;re going through the user association &amp;#8220;hair&amp;#8221; to get the color.  In keywords, I&amp;#8217;m using a page_keywords method which I can define in UsersHelper or ApplicationHelper.  I make use of such methods to handle logic considering nesting, etc.&lt;/p&gt;
&lt;p&gt;The benefits of this approach is that you can keep all your titles and other metadata in your locale files and out of your views without sacrificing the accessibility of your instance variables.&lt;/p&gt;
&lt;p&gt;This isn&amp;#8217;t a catch all solution and I&amp;#8217;ll still have to use content_for in places to specify alternate titles&amp;#8230; but it works well enough that I thought it would be worth sharing.&lt;/p&gt;
&lt;p&gt;Back to the question:  How do you handle page titles in Rails?&lt;/p&gt;</content>
 </entry>
 
 <entry>
   <title>Killing is_paranoid</title>
   <link href="http://blog.semanticart.com/2009/10/13/killing-is-paranoid.html"/>
   <updated>2009-10-13T00:00:00-05:00</updated>
   <id>http://blog.semanticart.com/2009/10/13/killing-is-paranoid</id>
   <content type="html">&lt;h2&gt;Intro&lt;/h2&gt;
&lt;p&gt;In short, I&amp;#8217;m killing &lt;a href=&quot;http://github.com/semanticart/is_paranoid&quot;&gt;is_paranoid&lt;/a&gt;.  It&amp;#8217;ll still exist as a github repo (mostly as an example of hack-ery and buffoon-ery) and you&amp;#8217;re still welcome to use it if you insist.  However, I&amp;#8217;m no longer going to support it or continue development on it.  I&amp;#8217;m sorry to anyone this affects.&lt;/p&gt;
&lt;h2&gt;Why&lt;/h2&gt;
&lt;p&gt;I created is_paranoid as an example of using &amp;#8220;default_scope&amp;#8221; to simulate acts_as_paranoid.  It was a proof-of-concept and a toy that caught on for whatever reason.  I liked it because the original version was ridiculously simple to grok and it was a fun example of the power of default_scope.&lt;/p&gt;
&lt;p&gt;But over time, I found out that default scope isn&amp;#8217;t as default as I had thought, and is_paranoid became a pile of hacks upon hacks to overcome unexpected behaviors in ActiveRecord and unexpected, complicated use-cases.  It became a pain and a mess.&lt;/p&gt;
&lt;h2&gt;A few examples of the train-wreck&lt;/h2&gt;
&lt;p&gt;About default_scope not being default-enough for is_paranoid:  Imagine you have a product model with a default scope that specifies that only products with in_stock = true should be selected (we don&amp;#8217;t want to show out-of-stock products by default).  Cool, that works on simple finds.  Now let&amp;#8217;s imagine we have categories which have many products.  Category.find(:first, :include =&amp;gt; :products) works, but it doesn&amp;#8217;t respect the default_scope on the eager-loading of products so you get out-of-stock items.   This applies to all relationships that are eager-loaded, not just has_many.  It also isn&amp;#8217;t respected on has_many :through relationships (eager-loaded or otherwise).  It also probably isn&amp;#8217;t respected in a few other places I can&amp;#8217;t recall offhand.&lt;/p&gt;
&lt;p&gt;Don&amp;#8217;t get me wrong, I&amp;#8217;m not claiming ActiveRecord or default_scope should be implemented differently or that I could do it better.  It just turns out that even though you can implement a rudimentary soft-deletion with default_scope I don&amp;#8217;t think you should do it.  The problem wasn&amp;#8217;t ActiveRecord, the problem was how I was using default_scope.  It just doesn&amp;#8217;t hold up long-term.&lt;/p&gt;
&lt;p&gt;Another issue is that once a scope is applied (default, named, otherwise), it isn&amp;#8217;t possible to easily subtract that scope.  is_paranoid used with_exclusive_scope to find soft-deleted items, but with_exclusive_scope makes things like Person.female.with_destroyed break since with_exclusive_scope undoes the female scope.  There&amp;#8217;s no obvious easy way to just pop out the is_paranoid scope and leave the other scopes.&lt;/p&gt;
&lt;p&gt;And you can overcome these things (and you can surely do it more cleanly than I did), but it will always be a hack because in the end you&amp;#8217;re trying to make ActiveRecord behave differently than it wants to in several different places.&lt;/p&gt;
&lt;h2&gt;Moving on&amp;#8230;&lt;/h2&gt;
&lt;p&gt;More than just is_paranoid becoming hacky, I&amp;#8217;m also subscribing to Rick Olson&amp;#8217;s reason for not using acts_as_paranoid anymore (even though he&amp;#8217;s the author).  This is a sentiment echoed by Ben Johnson in his post about &lt;a href=&quot;http://www.binarylogic.com/2009/10/06/discontinuing-resourcelogic/&quot;&gt;discontinuing Resourcelogic&lt;/a&gt;.  Simply put, you lose intent by relying on magic.   It makes a lot more sense to just be explicit about things using named_scopes or something similar.&lt;/p&gt;
&lt;p&gt;I&amp;#8217;m biting the hand that feeds me to an extent here since is_paranoid has been my most popular open source project.  But I&amp;#8217;d rather not be known than be known for something I don&amp;#8217;t believe in.&lt;/p&gt;
&lt;p&gt;Remember that just because you can do something doesn&amp;#8217;t mean you should.&lt;/p&gt;
&lt;p&gt;I wouldn&amp;#8217;t recommend using is_paranoid unless you want to fight with it.  And make sure you write a lot of tests :/&lt;/p&gt;
&lt;p&gt;I do want to thank the people who reported bugs and contributed solutions to is_paranoid.  I genuinely appreciate it.&lt;/p&gt;
&lt;p&gt;Well, now that I&amp;#8217;ve sufficiently bummed myself out, I&amp;#8217;m going to go back to hacking (the good kind, heh) on a fun little project that should be announced here in a day or two.  Ob-la-di, ob-la-da, right?&lt;/p&gt;




&lt;script type=&quot;text/javascript&quot;&gt;
	var disqus_url = &quot;http://blog.semanticart.com/killing_is_paranoid/&quot;; 
&lt;/script&gt;</content>
 </entry>
 
 <entry>
   <title>Using default_scope to recreate acts_as_paranoid</title>
   <link href="http://blog.semanticart.com/2009/03/22/using-default-scope-to-recreate-acts-as-paranoid.html"/>
   <updated>2009-03-22T00:00:00-05:00</updated>
   <id>http://blog.semanticart.com/2009/03/22/using-default-scope-to-recreate-acts-as-paranoid</id>
   <content type="html">&lt;h2&gt;Intro&lt;/h2&gt;
&lt;p&gt;Rails 2.3 gives us &amp;#8220;default_scope&amp;#8221; which &lt;a href=&quot;default_scope&quot;&gt;was described&lt;/a&gt; by Ryan Daigle as allowing you to &amp;#8220;specify default ordering, and other scopes, in edge rails directly in your ActiveRecord model.&amp;#8221;  Ryan&amp;#8217;s post gives some good examples of when you might want to use this (specifically on models where you always want them sorted in a specific manner).  In the comments of that post, Ryan Bates suggests that this might be useful for &amp;#8220;simulating destroying a model (like &lt;a href=&quot;http://github.com/technoweenie/acts_as_paranoid&quot;&gt;acts_as_paranoid&lt;/a&gt;).&amp;#8221;  Indeed this is possible and the idea of using scoping to create this is both present in acts_as_paranoid itself and also has &lt;a href=&quot;http://zargony.com/2007/08/13/scope_out-feature-default_scope&quot;&gt;been brought up before&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;In this article I&amp;#8217;m going to reinvent the wheel using default_scope to illustrate how powerful default_scope is and how trivial it makes this task.  You can keep track of the finished product in my &lt;a href=&quot;http://github.com/semanticart/is_paranoid&quot;&gt;is_paranoid&lt;/a&gt; gem.&lt;/p&gt;
&lt;h2&gt;Getting started&lt;/h2&gt;
&lt;p&gt;The syntax for declaring a default_scope is simple.  Here&amp;#8217;s what Ryan D. uses in his article:&lt;/p&gt;
&lt;div class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;ruby&quot;&gt;&lt;span class=&quot;k&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;Article&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&amp;lt;&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;ActiveRecord&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;no&quot;&gt;Base&lt;/span&gt;

  &lt;span class=&quot;n&quot;&gt;default_scope&lt;/span&gt; &lt;span class=&quot;ss&quot;&gt;:order&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&amp;#39;created_at DESC&amp;#39;&lt;/span&gt;

&lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;&lt;p&gt;To break this down, every time we use ActiveRecord to query our database, it will add the &amp;#8220;created_at &lt;span class=&quot;caps&quot;&gt;DESC&lt;/span&gt;&amp;#8221; order to our query.  Here&amp;#8217;s a sidebar word of warning about default_scope:  That&amp;#8217;s &lt;strong&gt;every&lt;/strong&gt; query, so you&amp;#8217;re incurring some unnecessary overhead if you haphazardly set default scopes when you don&amp;#8217;t really need them.  As a trivial example, the show action on our articles controller doesn&amp;#8217;t need to sort the articles by created_at since it should only be finding one article anyway.&lt;/p&gt;
&lt;p&gt;If you&amp;#8217;re unfamiliar with acts_as_paranoid, it allows &amp;#8220;you to hide and restore records without actually deleting them.&amp;#8221;  The plugin uses a timestamp field on your table to specify whether an item should count as deleted or not; if the deleted_at timestamp is nil, it is not deleted, if the timestamp is not nil, it is deleted.  The migration for articles with our deleted_at column looks like this:&lt;/p&gt;
&lt;div class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;ruby&quot;&gt;&lt;span class=&quot;k&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;CreateArticles&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&amp;lt;&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;ActiveRecord&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;no&quot;&gt;Migration&lt;/span&gt;

  &lt;span class=&quot;k&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;self&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;up&lt;/span&gt;

    &lt;span class=&quot;n&quot;&gt;create_table&lt;/span&gt; &lt;span class=&quot;ss&quot;&gt;:articles&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;do&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;|&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;t&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;|&lt;/span&gt;

      &lt;span class=&quot;n&quot;&gt;t&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;string&lt;/span&gt; &lt;span class=&quot;ss&quot;&gt;:name&lt;/span&gt;

      &lt;span class=&quot;n&quot;&gt;t&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;text&lt;/span&gt; &lt;span class=&quot;ss&quot;&gt;:body&lt;/span&gt;

      &lt;span class=&quot;n&quot;&gt;t&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;timestamp&lt;/span&gt; &lt;span class=&quot;ss&quot;&gt;:deleted_at&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;ss&quot;&gt;:default&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;kp&quot;&gt;nil&lt;/span&gt;

      &lt;span class=&quot;n&quot;&gt;t&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;timestamps&lt;/span&gt;

    &lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;

  &lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;



  &lt;span class=&quot;k&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;self&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;down&lt;/span&gt;

    &lt;span class=&quot;n&quot;&gt;drop_table&lt;/span&gt; &lt;span class=&quot;ss&quot;&gt;:articles&lt;/span&gt;

  &lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;

&lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;&lt;p&gt;And our default scope should look like:&lt;/p&gt;
&lt;div class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;ruby&quot;&gt;&lt;span class=&quot;k&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;Article&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&amp;lt;&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;ActiveRecord&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;no&quot;&gt;Base&lt;/span&gt;  

  &lt;span class=&quot;n&quot;&gt;default_scope&lt;/span&gt; &lt;span class=&quot;ss&quot;&gt;:conditions&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;ss&quot;&gt;:deleted_at&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;kp&quot;&gt;nil&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

&lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;&lt;p&gt;Now in script/console if you do Article.first, you can see the effects in your log:  &amp;quot;SELECT * &lt;span class=&quot;caps&quot;&gt;FROM&lt;/span&gt; &amp;#8220;articles&amp;#8221; &lt;span class=&quot;caps&quot;&gt;WHERE&lt;/span&gt; (&amp;#8220;articles&amp;#8221;.&amp;#8220;deleted_at&amp;#8221; IS &lt;span class=&quot;caps&quot;&gt;NULL&lt;/span&gt;) &lt;span class=&quot;caps&quot;&gt;LIMIT&lt;/span&gt; 1&amp;quot;&lt;/p&gt;
&lt;p&gt;We&amp;#8217;ll need to redefine destroy if we want it to mark an item deleted instead of actually deleting it.&lt;/p&gt;
&lt;div class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;ruby&quot;&gt;&lt;span class=&quot;k&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;Article&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&amp;lt;&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;ActiveRecord&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;no&quot;&gt;Base&lt;/span&gt;  

  &lt;span class=&quot;n&quot;&gt;default_scope&lt;/span&gt; &lt;span class=&quot;ss&quot;&gt;:conditions&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;ss&quot;&gt;:deleted_at&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;kp&quot;&gt;nil&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;



  &lt;span class=&quot;k&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;destroy&lt;/span&gt;

    &lt;span class=&quot;nb&quot;&gt;self&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;update_attribute&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;ss&quot;&gt;:deleted_at&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;Time&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;now&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;utc&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;

  &lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;

&lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;&lt;p&gt;Let&amp;#8217;s give it a spin:&lt;/p&gt;
&lt;div class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;ruby&quot;&gt;&lt;span class=&quot;o&quot;&gt;&amp;gt;&amp;gt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;a&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;Article&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;create&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;ss&quot;&gt;:name&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&amp;quot;Test Article&amp;quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;ss&quot;&gt;:body&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&amp;quot;some body&amp;quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;

&lt;span class=&quot;o&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;c1&quot;&gt;#&amp;lt;Article id: 3, name: &amp;quot;Test Article&amp;quot;, body: &amp;quot;some body&amp;quot;, deleted_at: nil, created_at: &amp;quot;2009-03-22 16:08:00&amp;quot;, updated_at: &amp;quot;2009-03-22 16:08:00&amp;quot;&amp;gt;&lt;/span&gt;

&lt;span class=&quot;o&quot;&gt;&amp;gt;&amp;gt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;a&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;destroy&lt;/span&gt;

&lt;span class=&quot;o&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;kp&quot;&gt;true&lt;/span&gt;

&lt;span class=&quot;o&quot;&gt;&amp;gt;&amp;gt;&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;Article&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;first&lt;/span&gt;

&lt;span class=&quot;o&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;kp&quot;&gt;nil&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;&lt;p&gt;But the log shows it actually just did &amp;quot;UPDATE &amp;#8220;articles&amp;#8221; &lt;span class=&quot;caps&quot;&gt;SET&lt;/span&gt; &amp;#8220;updated_at&amp;#8221; = &amp;#8216;2009-03-22 16:08:02&amp;#8217;, &amp;#8220;deleted_at&amp;#8221; = &amp;#8216;2009-03-22 16:08:02&amp;#8217; &lt;span class=&quot;caps&quot;&gt;WHERE&lt;/span&gt; &amp;#8220;id&amp;#8221; = 3&amp;quot; so our article is still there, we simply can&amp;#8217;t find it because of our default_scope.&lt;/p&gt;
&lt;p&gt;Sadly there&amp;#8217;s something we&amp;#8217;ve broken already, though.  Because we redefined destroy, we can&amp;#8217;t use our destroy callbacks, like before_destroy, anymore.  We can fix that by changing the way we implement destroy.  You should check out lib/is_paranoid.rb in &lt;a href=&quot;http://github.com/semanticart/is_paranoid&quot;&gt;the repo&lt;/a&gt; to see how this is implemented.  Let&amp;#8217;s move on.&lt;/p&gt;
&lt;p&gt;Now we need to add a method to help us find things that have been marked deleted.  To do that, we&amp;#8217;ll need to bypass our default_scope by using with_exclusive_scope.&lt;/p&gt;
&lt;div class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;ruby&quot;&gt;&lt;span class=&quot;k&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;self&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;find_with_destroyed&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;*&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;args&lt;/span&gt;

  &lt;span class=&quot;nb&quot;&gt;self&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;with_exclusive_scope&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;find&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;*&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;args&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

&lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;&lt;p&gt;Essentially this takes whatever arguments you provide as finder conditions (and/or order, includes, etc.) and passes them to the find method after first specifying that we should ignore the default_scope.  Now we can find our destroyed items as well as our non-destroyed items.&lt;/p&gt;
&lt;div class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;ruby&quot;&gt;&lt;span class=&quot;o&quot;&gt;&amp;gt;&amp;gt;&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;Article&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;find_with_destroyed&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;ss&quot;&gt;:first&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;

&lt;span class=&quot;o&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;c1&quot;&gt;#&amp;lt;Article id: 3, name: &amp;quot;Test Article&amp;quot;, body: &amp;quot;some body&amp;quot;, deleted_at: &amp;quot;2009-03-22 16:08:02&amp;quot;, created_at: &amp;quot;2009-03-22 16:08:00&amp;quot;, updated_at: &amp;quot;2009-03-22 16:08:02&amp;quot;&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;&lt;p&gt;There&amp;#8217;s still a few features to add, like count_with_destroyed and a restore method, but this should give you a good intro to the power of default_scope.  Both those features are added in the current version of &lt;a href=&quot;http://github.com/semanticart/is_paranoid&quot;&gt;is_paranoid&lt;/a&gt; and we&amp;#8217;re still sitting on less than 40 actual lines of code to accomplish the core functionality of acts_as_paranoid.&lt;/p&gt;
&lt;h2&gt;Why you might not want to use default_scope&lt;/h2&gt;
&lt;p&gt;There&amp;#8217;s the previously mentioned overhead incurred with applying order or conditions to every interaction ActiveRecord has with your database, but beyond that, some people in the community feel that overriding find methods can add unnecessary complexity to your code and make debugging more complicated.  In fact, Rick Olson, the author of acts_as_paranoid, &lt;a href=&quot;http://twitter.com/technoweenie/statuses/1241904419&quot;&gt;no longer uses &lt;span class=&quot;caps&quot;&gt;AAP&lt;/span&gt;&lt;/a&gt; in favor of hidden and visible named scopes on his models.&lt;/p&gt;
&lt;p&gt;Granted, it is more readily apparent that you&amp;#8217;re in a named scope since they&amp;#8217;re explicitly called, but if you have a good number of models that you want to be able to soft-delete or hide, then it seems that declaring those scopes on each model isn&amp;#8217;t very &lt;span class=&quot;caps&quot;&gt;DRY&lt;/span&gt;.  I&amp;#8217;d wager most people end up using a mixin to prevent repetition.  I don&amp;#8217;t feel that using default_scope in a manner such as this is far beyond simply DRYing things up.&lt;/p&gt;
&lt;p&gt;Besides, anyone should be able to easily grok the code in is_paranoid, as brief and simple as it is.  Ultimately it comes down to personal preference.  As always, makes sure you know your toolset.&lt;/p&gt;
&lt;h2&gt;In praise of ActiveRecord (rat-hole)&lt;/h2&gt;
&lt;p&gt;As a quick rat-hole, &lt;a href=&quot;http://api.rubyonrails.org/classes/ActiveRecord/Base.html&quot;&gt;ActiveRecord&lt;/a&gt; implements destroy and delete_all fantastically.  Basically Model.destroy and Model.destroy_all both internally call instance.destroy and Model.delete and instance.delete both internally call Model.destroy_all.  Because of this, you only have to redefine delete and destroy once for them to take effect across the other methods.  Awesome.&lt;/p&gt;
&lt;h2&gt;In closing&lt;/h2&gt;
&lt;p&gt;If you&amp;#8217;re looking for a light-weight acts_as_paranoid replacement and you&amp;#8217;re on Rails 2.3+ then give &lt;a href=&quot;http://github.com/semanticart/is_paranoid&quot;&gt;is_paranoid&lt;/a&gt; a look.  The readme should give a decent overview, but beyond that, check out the specs and read the source.&lt;/p&gt;
&lt;p&gt;I want to keep is_paranoid light-weight, but if anything important is missing or if you uncover any bugs, please let me know.&lt;/p&gt;













&lt;script type=&quot;text/javascript&quot;&gt;
	var disqus_url = &quot;http://blog.semanticart.com/using_default_scope_to_recreate_acts_as_paranoid&quot;;
&lt;/script&gt;</content>
 </entry>
 
 <entry>
   <title>Comparing Thinking Sphinx And Acts As Ferret For Full Text Indexing In Rails</title>
   <link href="http://blog.semanticart.com/2009/03/05/comparing-thinking-sphinx-and-acts-as-ferret-for-full-text-indexing-in-rails.html"/>
   <updated>2009-03-05T00:00:00-06:00</updated>
   <id>http://blog.semanticart.com/2009/03/05/comparing-thinking-sphinx-and-acts-as-ferret-for-full-text-indexing-in-rails</id>
   <content type="html">&lt;p&gt;This will by no means be an exhaustive list of differences between using Sphinx and using Ferret, but we&amp;#8217;ll look at a few major differences between the way these two search engines are implemented via &lt;a href=&quot;http://github.com/jkraemer/acts_as_ferret/commits/master&quot;&gt;acts_as_ferret&lt;/a&gt; (&lt;span class=&quot;caps&quot;&gt;AAF&lt;/span&gt;) and &lt;a href=&quot;http://github.com/freelancing-god/thinking-sphinx/tree/master&quot;&gt;Thinking Sphinx&lt;/a&gt; (TS).&lt;/p&gt;
&lt;h2&gt;Active Development&lt;/h2&gt;
&lt;p&gt;First thing is first, TS is in much more active development.  At the time of writing, TS last had a commit to the offical repo 13 days ago in comparison with three months ago for &lt;span class=&quot;caps&quot;&gt;AAF&lt;/span&gt;.  And it shows.  acts_as_ferret discussion these days is minimal online and most of the tutorials are rather old.  Meanwhile, Thinking Sphinx has a very active &lt;a href=&quot;http://groups.google.com/group/thinking-sphinx?pli=1&quot;&gt;google group&lt;/a&gt; and several more recent tutorials including a fairly recent &lt;a href=&quot;http://railscasts.com/episodes/120-thinking-sphinx&quot;&gt;Railscast&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;So Thinking Sphinx wins in terms of active development.&lt;/p&gt;
&lt;h2&gt;Speed, Reliability, and Resources&lt;/h2&gt;
&lt;p&gt;Another place TS blows &lt;span class=&quot;caps&quot;&gt;AAF&lt;/span&gt; out of the water is in speed and resource usage.  Sphinx uses kilobytes of memory where a ferret daemon will sit on megabytes, having to load your entire Rails app into memory.  For example, on my machine, the Sphinx daemon sat at 376 KB while my ferret process ate 57.69 MB.  Not kidding.&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;http://www.ruby-forum.com/topic/137629&quot;&gt;Ezra Zygmuntowicz said&lt;/a&gt;&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;Ferret is unstable in production. Segfaults, corrupted indexes&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;galore. We&amp;#8217;ve switched around 40 clients form ferret to sphinx and&lt;/p&gt;
&lt;p&gt;solved their problems this way. I will never use ferret again after&lt;/p&gt;
&lt;p&gt;all the problems I have seen it cause peoples production apps.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;Plus sphinx can reindex many many times faster then ferret and uses less cpu and memory as well.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;Anecdotally, that&amp;#8217;s my experience as well.  Thinking Sphinx can index my database in less than a minute, while acts_as_ferret can take up to 30 minutes or more.&lt;/p&gt;
&lt;h2&gt;The Level of Interaction with Rails&lt;/h2&gt;
&lt;p&gt;Simply put, acts_as_ferret obeys ActiveRecord, while Thinking Sphinx goes low-level.&lt;/p&gt;
&lt;p&gt;In his &lt;a href=&quot;http://peepcode.com/products/thinking-sphinx-pdf&quot;&gt;Thinking Sphinx Peepcode &lt;span class=&quot;caps&quot;&gt;PDF&lt;/span&gt;&lt;/a&gt;, Pat Allen writes &amp;#8220;For those familiar with Ferret, Sphinx is quite similar, except that Sphinx talks directly to database servers &amp;#8211; both MySQL and PostgreSQL &amp;#8211; to obtain the data to index.&amp;#8221;&lt;/p&gt;
&lt;p&gt;This is largely what gives Sphinx its speed advantage, but it also makes Thinking Sphinx dumb as far as your ActiveRecord models are concerned.&lt;/p&gt;
&lt;p&gt;For instance, this means that TS isn&amp;#8217;t aware of your acts_as_paranoid models until you add the deleted_at conditional to your define_index block.&lt;/p&gt;
&lt;div class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;ruby&quot;&gt;&lt;span class=&quot;n&quot;&gt;define_index&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;do&lt;/span&gt;

  &lt;span class=&quot;n&quot;&gt;indexes&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;ss&quot;&gt;:body&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;ss&quot;&gt;:title&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;]&lt;/span&gt;

  &lt;span class=&quot;n&quot;&gt;where&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&amp;#39;deleted_at is NULL&amp;#39;&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;]&lt;/span&gt;

&lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;&lt;p&gt;This also means that TS can&amp;#8217;t index computed values as easily.  In &lt;span class=&quot;caps&quot;&gt;AAF&lt;/span&gt; you can index methods on your object, so you could index a method like the following&lt;/p&gt;
&lt;div class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;ruby&quot;&gt;&lt;span class=&quot;k&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;ordinalized_names_of_children&lt;/span&gt;

  &lt;span class=&quot;n&quot;&gt;ordinalized_children&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;[]&lt;/span&gt;

  &lt;span class=&quot;nb&quot;&gt;self&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;children&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;sort_by&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;amp;&lt;/span&gt;&lt;span class=&quot;ss&quot;&gt;:birth_date&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;each_with_index&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;do&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;|&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;child&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;i&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;|&lt;/span&gt;

    &lt;span class=&quot;n&quot;&gt;ordinalized_children&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&amp;lt;&amp;lt;&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;child&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;first_name&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;i&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;to_ordinal&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;]&lt;/span&gt;

  &lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;

  &lt;span class=&quot;n&quot;&gt;ordinalized_children&lt;/span&gt;

&lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;&lt;p&gt;This is a silly example, but to accomplish the same with TS you need to use db-specific string transformations and add all your conditional logic to the query as well.  And you can easily imagine more complicated examples.  Where with &lt;span class=&quot;caps&quot;&gt;AAF&lt;/span&gt; you have the entire landscape of Ruby to use and abuse and you&amp;#8217;re instantly inheriting the constraints of ActiveRecord, with TS you&amp;#8217;re limited to what can be done solely on the database level.  Luckily this is usually enough.&lt;/p&gt;
&lt;h2&gt;Indexing Changes to Your Models&lt;/h2&gt;
&lt;p&gt;As far as ease of handling updates goes, acts_as_ferret has a big initial advantage.  From &lt;a href=&quot;http://www.railsenvy.com/2007/2/19/acts-as-ferret-tutorial&quot;&gt;Gregg Pollack&lt;/a&gt;&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;the index gets modified every time you add/edit/remove the ActiveRecord model it’s associated with. You never have to worry about doing this yourself, it happens automatically, so your search index is always 100% accurate. No rebuilding needed.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;With Thinking Sphinx, you need to specify something called &lt;a href=&quot;http://ts.freelancing-gods.com/usage.html&quot;&gt;delta indexes&lt;/a&gt; on the models you want to keep up-to-date for searches between index rebuilds.  This is a little more intrusive than AAF&amp;#8217;s approach since you also have to add a field to your table called &amp;#8220;delta&amp;#8221; to track what has updated&amp;#8230; but a single boolean field doesn&amp;#8217;t incur much overhead.  You&amp;#8217;ll still need to periodically rebuild your indexes regularly as the delta indexes can slow things down over time.&lt;/p&gt;
&lt;p&gt;In both &lt;span class=&quot;caps&quot;&gt;AAF&lt;/span&gt; and TS, deleted models are immediately removed from the index.&lt;/p&gt;
&lt;p&gt;To sum up the differences:&lt;/p&gt;
&lt;ul&gt;
	&lt;li&gt;&lt;span class=&quot;caps&quot;&gt;AAF&lt;/span&gt; requires less work on your part to keep an accurate running index, but the overhead involved in updating the index on each record update can be painfully slow over time.&lt;/li&gt;
&lt;/ul&gt;
&lt;ul&gt;
	&lt;li&gt;TS requires more work to specify delta indexes and requires you to add a column to your database, but provides you with options on how often incorporate the changes so that you&amp;#8217;re not waiting on a change to be indexed on every model save.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;From my experience, very few model updates need to be instantly available for search, and both approaches have their pros and cons.  Though it requires slightly more work on your part, I feel TS puts more control in your hands.&lt;/p&gt;
&lt;h2&gt;The Overall Winner&lt;/h2&gt;
&lt;p&gt;The winner here is obviously Thinking Sphinx.  You use less resources and get better speed, reliability, and the future looks a lot more sure for support.  Sure, you may have to get your hands a little dirtier with some &lt;span class=&quot;caps&quot;&gt;SQL&lt;/span&gt;, but the benefits more than make up for it.&lt;/p&gt;
&lt;p&gt;Also (and I get nothing out of this), you should buy the &lt;a href=&quot;http://peepcode.com/products/thinking-sphinx-pdf&quot;&gt;Peepcode &lt;span class=&quot;caps&quot;&gt;PDF&lt;/span&gt;&lt;/a&gt; as it will give you a huge head start on Thinking Sphinx.&lt;/p&gt;
&lt;h2&gt;An aside on Sphinx&amp;#8217;s Treating of Primary Keys&lt;/h2&gt;
&lt;p&gt;There&amp;#8217;s another thing Ferret can do that Sphinx cannot.  As &lt;a href=&quot;http://www.sphinxsearch.com/docs/current.html&quot;&gt;Section 3.5 of the Sphinx documentation&lt;/a&gt; states, &amp;#8220;&lt;span class=&quot;caps&quot;&gt;ALL&lt;/span&gt; &lt;span class=&quot;caps&quot;&gt;DOCUMENT&lt;/span&gt; &lt;span class=&quot;caps&quot;&gt;IDS&lt;/span&gt; &lt;span class=&quot;caps&quot;&gt;MUST&lt;/span&gt; BE &lt;span class=&quot;caps&quot;&gt;UNIQUE&lt;/span&gt; &lt;span class=&quot;caps&quot;&gt;UNSIGNED&lt;/span&gt; &lt;span class=&quot;caps&quot;&gt;NON&lt;/span&gt;-&lt;span class=&quot;caps&quot;&gt;ZERO&lt;/span&gt; &lt;span class=&quot;caps&quot;&gt;INTEGER&lt;/span&gt; &lt;span class=&quot;caps&quot;&gt;NUMBERS&lt;/span&gt; (32-&lt;span class=&quot;caps&quot;&gt;BIT&lt;/span&gt; OR 64-&lt;span class=&quot;caps&quot;&gt;BIT&lt;/span&gt;, &lt;span class=&quot;caps&quot;&gt;DEPENDING&lt;/span&gt; ON &lt;span class=&quot;caps&quot;&gt;BUILD&lt;/span&gt; &lt;span class=&quot;caps&quot;&gt;TIME&lt;/span&gt; &lt;span class=&quot;caps&quot;&gt;SETTINGS&lt;/span&gt;).&amp;#8221;  And Thinking Sphinx enforces this in its config file by performing math on your table&amp;#8217;s id column to help create unique sphinx index ids.&lt;/p&gt;
&lt;p&gt;99 times out of 100 this is fine since most tables just have auto-incremented integer ids anyway, but what if you have tables with ids of significant value?  That&amp;#8217;s the situation I found myself in when adopting Thinking Sphinx on &lt;a href=&quot;http://politics4all.com&quot;&gt;my current project&lt;/a&gt;.  We have a ton of external data coming in and much of that data already has a &lt;span class=&quot;caps&quot;&gt;GUID&lt;/span&gt; so we decided early on to use the &lt;span class=&quot;caps&quot;&gt;GUID&lt;/span&gt; as a primary key and foreign key as that would allow us to later recreate any table without having to worry about the foreign key integrity issues that can sometimes be a taxing side-effect of using auto-incremented ids.&lt;/p&gt;
&lt;p&gt;My first approach to overcoming this limitation was to add an auto-incremented column named &amp;#8220;id&amp;#8221; to the table and then make use of &lt;strong&gt;set_primary_key&lt;/strong&gt; in Rails.  Unfortunately, once you do that, Thinking Sphinx tries to call that primary key you specified.  So Thinking Sphinx had to be patched.  Essentially, I added a method &lt;strong&gt;set_sphinx_primary_key&lt;/strong&gt; to allow you to specify a primary key that TS should use regardless of what the ActiveRecord model specifies as its primary key.&lt;/p&gt;
&lt;p&gt;So in the example:&lt;/p&gt;
&lt;div class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;ruby&quot;&gt;&lt;span class=&quot;k&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;Robot&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&amp;lt;&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;ActiveRecord&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;no&quot;&gt;Base&lt;/span&gt;

  &lt;span class=&quot;c1&quot;&gt;# The key ActiveRecord will use on joins, map to id, etc.&lt;/span&gt;

  &lt;span class=&quot;c1&quot;&gt;# Setting the primary key isn&amp;#39;t necessary for set_sphinx_primary_key to work&lt;/span&gt;

  &lt;span class=&quot;n&quot;&gt;set_primary_key&lt;/span&gt; &lt;span class=&quot;ss&quot;&gt;:internal_id&lt;/span&gt;



  &lt;span class=&quot;c1&quot;&gt;# The key sphinx will use for indexing, must be a unique integer&lt;/span&gt;

  &lt;span class=&quot;n&quot;&gt;set_sphinx_primary_key&lt;/span&gt; &lt;span class=&quot;ss&quot;&gt;:alternate_primary_key&lt;/span&gt;



  &lt;span class=&quot;n&quot;&gt;define_index&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;do&lt;/span&gt;

    &lt;span class=&quot;n&quot;&gt;indexes&lt;/span&gt; &lt;span class=&quot;ss&quot;&gt;:name&lt;/span&gt;

  &lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;

&lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;&lt;p&gt;ActiveRecord will use the field internal_id on the &amp;#8220;robots&amp;#8221; table (the &lt;strong&gt;set_primary_key&lt;/strong&gt; could just as well be left out and ActiveRecord would use the default &amp;#8220;id&amp;#8221;).  But while ActiveRecord uses internal_id, Thinking Sphinx will instead use alternate_primary_key.  So our robots can internally use a &lt;span class=&quot;caps&quot;&gt;GUID&lt;/span&gt; string while Sphinx is still provided with the integer column it needs to index the robots.&lt;/p&gt;
&lt;p&gt;&lt;strike&gt;You can find these updates in &lt;a href=&quot;http://github.com/semanticart/thinking-sphinx/tree/alt_primary_key&quot;&gt;my github branch of Thinking Sphinx&lt;/a&gt;.  I have no idea if Pat will ever merge these into the main repo, as it is admittedly a niche need.  But if you find yourself in the situation I found myself in, it can really help you overcome this limitation of Sphinx.&lt;/strike&gt;&lt;/p&gt;
&lt;p&gt;Update: Due to popular (or at least some) demand, my changes are merged into the Thinking Sphinx master branch.  Enjoy.&lt;/p&gt;





















&lt;script type=&quot;text/javascript&quot;&gt;
	var disqus_url = &quot;http://blog.semanticart.com/comparing_thinking_sphinx_and_acts_as_ferret_for_full-text_indexing_in_rails&quot;; 
&lt;/script&gt;</content>
 </entry>
 
 <entry>
   <title>CsvMapper gets a little more magical</title>
   <link href="http://blog.semanticart.com/2009/02/06/csv-mapper-gets-a-little-more-magical.html"/>
   <updated>2009-02-06T00:00:00-06:00</updated>
   <id>http://blog.semanticart.com/2009/02/06/csv-mapper-gets-a-little-more-magical</id>
   <content type="html">&lt;p&gt;Have you checked out &lt;a href=&quot;http://github.com/pillowfactory/csv-mapper/tree/master&quot;&gt;CsvMapper&lt;/a&gt; by &lt;a href=&quot;http://github.com/pillowfactory&quot;&gt;Luke Pillow&lt;/a&gt;?  It makes some of the drudgery of &lt;span class=&quot;caps&quot;&gt;CSV&lt;/span&gt; parsing fade away.  It really is a fantastic library.  As an example from the doc:&lt;/p&gt;
&lt;p&gt;Given the csv&lt;/p&gt;


&lt;pre&gt;

First Name,Last Name,Age

John,Doe,27

Jane,Doe,26

Bat,Man,52

...etc...

&lt;/pre&gt;
&lt;p&gt;you can do&lt;/p&gt;
&lt;div class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;ruby&quot;&gt;&lt;span class=&quot;kp&quot;&gt;include&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;CsvMapper&lt;/span&gt;



&lt;span class=&quot;n&quot;&gt;results&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;import&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&amp;#39;/path/to/file.csv&amp;#39;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;do&lt;/span&gt;

  &lt;span class=&quot;n&quot;&gt;start_at_row&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;1&lt;/span&gt;

  &lt;span class=&quot;o&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;first_name&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;last_name&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;age&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;]&lt;/span&gt;

&lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;



&lt;span class=&quot;n&quot;&gt;results&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;first&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;first_name&lt;/span&gt;  &lt;span class=&quot;c1&quot;&gt;# John&lt;/span&gt;

&lt;span class=&quot;n&quot;&gt;results&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;first&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;last_name&lt;/span&gt;   &lt;span class=&quot;c1&quot;&gt;# Doe&lt;/span&gt;

&lt;span class=&quot;n&quot;&gt;results&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;first&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;age&lt;/span&gt;         &lt;span class=&quot;c1&quot;&gt;# 27&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;&lt;p&gt;Cool, right?  There&amp;#8217;s a lot of magic built in, but I felt there was an extra-mile left to go&amp;#8230;  By default CsvMapper requires you to specify the columns you want.  Most of the time you want to do this, but it doesn&amp;#8217;t really feel &lt;span class=&quot;caps&quot;&gt;DRY&lt;/span&gt; in cases where the column names are made redundant by matching those on the first line of the file.  And its a pain to reproduce column names when there are many, many fields.  So, presto:&lt;/p&gt;
&lt;div class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;ruby&quot;&gt;&lt;span class=&quot;kp&quot;&gt;include&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;CsvMapper&lt;/span&gt;



&lt;span class=&quot;n&quot;&gt;results&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;import&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&amp;#39;/path/to/file.csv&amp;#39;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;do&lt;/span&gt;

  &lt;span class=&quot;n&quot;&gt;read_attributes_from_file&lt;/span&gt;

&lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;



&lt;span class=&quot;n&quot;&gt;results&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;first&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;first_name&lt;/span&gt;  &lt;span class=&quot;c1&quot;&gt;# John&lt;/span&gt;

&lt;span class=&quot;n&quot;&gt;results&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;first&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;last_name&lt;/span&gt;   &lt;span class=&quot;c1&quot;&gt;# Doe&lt;/span&gt;

&lt;span class=&quot;n&quot;&gt;results&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;first&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;age&lt;/span&gt;         &lt;span class=&quot;c1&quot;&gt;# 27&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;&lt;p&gt;Sweet, you say, but what if my field names are something absurd and clunky?  Fine, say I, just specify an alias for them.&lt;/p&gt;
&lt;div class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;ruby&quot;&gt;&lt;span class=&quot;n&quot;&gt;results&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;import&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&amp;#39;/path/to/file.csv&amp;#39;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;do&lt;/span&gt;

  &lt;span class=&quot;n&quot;&gt;read_attributes_from_file&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&amp;#39;First Name&amp;#39;&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&amp;#39;what_my_friends_call_me&amp;#39;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;

&lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;



&lt;span class=&quot;n&quot;&gt;results&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;first&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;what_my_friends_call_me&lt;/span&gt;  &lt;span class=&quot;c1&quot;&gt;# John&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;&lt;p&gt;What about more realistic example with a bigger csv file?  OK, here&amp;#8217;s one from &lt;a href=&quot;http://subsidyscope.com/projects/bailout/tarp/&quot;&gt;Subsidyscope&lt;/a&gt;:&lt;/p&gt;
&lt;div class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;ruby&quot;&gt;&lt;span class=&quot;n&quot;&gt;results&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;import&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&amp;#39;subsidyscope-tarp.csv&amp;#39;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;){&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;read_attributes_from_file&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

&lt;span class=&quot;n&quot;&gt;results&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;first&lt;/span&gt;  &lt;span class=&quot;c1&quot;&gt;# &amp;lt;OpenStruct type_of_institution=&amp;quot;holding company&amp;quot;, city=&amp;quot;New York&amp;quot;, &lt;/span&gt;

               &lt;span class=&quot;c1&quot;&gt;# description=&amp;quot;Preferred Stock w/Warrants&amp;quot;, &lt;/span&gt;

               &lt;span class=&quot;c1&quot;&gt;# total_assets=&amp;quot;1856207282000.00&amp;quot;, date=&amp;quot;2008-10-28&amp;quot;, &lt;/span&gt;

               &lt;span class=&quot;c1&quot;&gt;# fdic_number=&amp;quot;1039502&amp;quot;, ots_number=nil, transaction_type=&amp;quot;Purchase&amp;quot;,&lt;/span&gt;

               &lt;span class=&quot;c1&quot;&gt;# price_paid=&amp;quot;25000000000.00&amp;quot;, state=&amp;quot;NY&amp;quot;, &lt;/span&gt;

               &lt;span class=&quot;c1&quot;&gt;# name=&amp;quot;JPMorgan Chase &amp;amp; Co.&amp;quot;, stock_symbol=&amp;quot;JPM&amp;quot;,&lt;/span&gt;

               &lt;span class=&quot;c1&quot;&gt;# pricing_mechanism=&amp;quot;Par&amp;quot;, regulator=&amp;quot;Federal Reserve&amp;quot;&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;&lt;p&gt;We didn&amp;#8217;t have to define a single attribute name; the csv file did it for us.&lt;/p&gt;
&lt;p&gt;Ok, I&amp;#8217;m sold, you say.  Where can I get this?  Try my &lt;a href=&quot;http://github.com/semanticart/csv-mapper/tree/master&quot;&gt;CsvMapper github fork&lt;/a&gt; until it maybe gets merged into the official branch.&lt;/p&gt;









&lt;script type=&quot;text/javascript&quot;&gt;
	var disqus_url = &quot;http://blog.semanticart.com/csv_mapper_gets_a_little_more_magical/&quot;; 
&lt;/script&gt;</content>
 </entry>
 
 <entry>
   <title>Ruby Idioms from Open Source, Volume One - Seinfeld</title>
   <link href="http://blog.semanticart.com/2008/11/29/ruby-idioms-from-open-source-volume-one.html"/>
   <updated>2008-11-29T00:00:00-06:00</updated>
   <id>http://blog.semanticart.com/2008/11/29/ruby-idioms-from-open-source-volume-one</id>
   <content type="html">&lt;p&gt;This is the first in what I hope to be a series of articles looking at different open source projects and pulling out various ruby idioms.&lt;/p&gt;
&lt;p&gt;We&amp;#8217;re looking at &lt;a href=&quot;http://github.com/technoweenie/seinfeld/tree/master&quot;&gt;Seinfeld&lt;/a&gt; which powers &lt;a href=&quot;http://calendaraboutnothing.com&quot;&gt;Calendar About Nothing&lt;/a&gt;.  If you haven&amp;#8217;t signed up yet, you should definitely do so.  It is a fantastic way of keeping yourself motivated and contributing.&lt;/p&gt;
&lt;h3&gt;String Substitution&lt;/h3&gt;
&lt;p&gt;You can find the first bit of interesting code at line 37&lt;sup class=&quot;footnote&quot;&gt;&lt;a href=&quot;#fn1&quot;&gt;1&lt;/a&gt;&lt;/sup&gt; of seinfeld_calendar.rb in the page_title method:&lt;/p&gt;
&lt;div class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;ruby&quot;&gt;&lt;span class=&quot;k&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;page_title&lt;/span&gt;

  &lt;span class=&quot;s2&quot;&gt;&amp;quot;%s&amp;#39;s Calendar&amp;quot;&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;%&lt;/span&gt; &lt;span class=&quot;vi&quot;&gt;@user&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;login&lt;/span&gt;

&lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;&lt;p&gt;Here we see &lt;a href=&quot;http://www.ruby-doc.org/core/classes/String.html#M000785&quot;&gt;String formatting&lt;/a&gt; substitution.  You could also do sprintf(&amp;#8220;%s&amp;#8217;s Calendar&amp;#8221;, @user.login), but why would you?  Use an array if you have more than one value for substitution.&lt;/p&gt;
&lt;h3&gt;Date Manipulation&lt;/h3&gt;
&lt;div class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;ruby&quot;&gt;&lt;span class=&quot;n&quot;&gt;now&lt;/span&gt;        &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;Date&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;new&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;params&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;ss&quot;&gt;:year&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;params&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;ss&quot;&gt;:month&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;

&lt;span class=&quot;n&quot;&gt;prev_month&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;now&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&amp;lt;&amp;lt;&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;1&lt;/span&gt;

&lt;span class=&quot;n&quot;&gt;next_month&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;now&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&amp;gt;&amp;gt;&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;1&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;&lt;p&gt;Around line 58 of seinfeld_calendar.rb, we see that for the calendar&amp;#8217;s next and previous month links to work properly, Seinfeld uses the &amp;lt;&amp;lt; and &amp;gt;&amp;gt; methods which &lt;a href=&quot;http://www.ruby-doc.org/stdlib/libdoc/date/rdoc/classes/Date.html#M000382&quot;&gt;are described&lt;/a&gt; to&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;Return a new Date object that is n months earlier/later than the current one.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;These methods were new to me, but they&amp;#8217;re easy enough to remember if you think of them as looking like fast-forward and rewind.&lt;/p&gt;
&lt;h3&gt;Determining if a string is empty&amp;#8230; wait, what?&lt;/h3&gt;
&lt;p&gt;Line 120 of /lib/seinfeld/models.rb gives us&lt;/p&gt;
&lt;div class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;ruby&quot;&gt;  &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;login_name&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;size&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;zero?&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;&lt;p&gt;Which looks nice, but one can&amp;#8217;t help but wonder why they didn&amp;#8217;t use login_name.empty? instead.  My benchmarks show .empty? to be trivially faster, but I may be missing something.  Any ideas?  My money is on it simply being the author&amp;#8217;s preferred idiom.  I think .size.zero? may be more readable, but I&amp;#8217;ll stick with .empty? for the near future.&lt;/p&gt;
&lt;p&gt;If you like learning by examining good examples of ruby code, I highly recommend David A. Black&amp;#8217;s &lt;a href=&quot;http://www.amazon.com/gp/product/1932394699?ie=UTF8&amp;amp;tag=adrfous-20&amp;amp;linkCode=as2&amp;amp;camp=1789&amp;amp;creative=9325&amp;amp;creativeASIN=1932394699&quot;&gt;Ruby for Rails: Ruby Techniques for Rails Developers&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;If you have any suggestions for a future project to look at, please let me know.&lt;/p&gt;
&lt;p class=&quot;footnote&quot; id=&quot;fn1&quot;&gt;&lt;sup&gt;1&lt;/sup&gt; Because projects are always changing these line numbers are just provided as-is at the time of writing.&lt;/p&gt;












&lt;script type=&quot;text/javascript&quot;&gt;
	var disqus_url = &quot;http://blog.semanticart.com/ruby_idioms_from_open_source_volume_one/&quot;; 
&lt;/script&gt;</content>
 </entry>
 
 <entry>
   <title>Driving Sinatra Without A Browser</title>
   <link href="http://blog.semanticart.com/2008/11/28/driving-sinatra-without-a-browser.html"/>
   <updated>2008-11-28T00:00:00-06:00</updated>
   <id>http://blog.semanticart.com/2008/11/28/driving-sinatra-without-a-browser</id>
   <content type="html">&lt;h3&gt;Intro&lt;/h3&gt;
&lt;p&gt;I&amp;#8217;ve been hacking on leethal&amp;#8217;s &lt;a href=&quot;http://github.com/leethal/stuff-site/tree/master&quot;&gt;stuff-site&lt;/a&gt; (the software that runs this blog) recently and adding a good number of features to &lt;a href=&quot;http://github.com/semanticart/stuff-site/tree/master&quot;&gt;my fork&lt;/a&gt;.  One item I recently added is pushing everything out to a static version of the site.  This allows you to run the app on any hosting and bypass &lt;a href=&quot;http://sinatra.rubyforge.org/&quot;&gt;Sinatra&lt;/a&gt; altogether on the host side.  To borrow the motto from merb, &amp;#8220;no code is faster than no code.&amp;#8221;  I&amp;#8217;m not using anything that has to be dynamic, so let&amp;#8217;s remove all the moving parts and just use the Sinatra part for development and testing.&lt;/p&gt;
&lt;p&gt;There are a plethora of ways to make static sites.  I didn&amp;#8217;t want to rely on outside resources like wget or curl and I wanted to avoid using any html parsing/spidering if possible.  Since this is a very simple app with only 4 types of pages (index, rss, article, topic), it made sense to borrow from whatever magic Sinatra&amp;#8217;s specs are using to make test requests and throw the task in the Rakefile.&lt;/p&gt;
&lt;h3&gt;Let&amp;#8217;s do this already&lt;/h3&gt;
&lt;p&gt;Sinatra&amp;#8217;s rspec suite provides get_it, post_it, etc. methods, so we first search for where these methods are defined and see if we can piggyback off of whatever process they use.  You can find them (as of this publication) on line 35 of &lt;a href=&quot;http://github.com/bmizerany/sinatra/tree/master/lib/sinatra/test/methods.rb#L35&quot;&gt;/lib/sinatra/test/methods.rb&lt;/a&gt; but the lines we really care about are 36 and 56&lt;/p&gt;
&lt;div class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;ruby&quot;&gt;&lt;span class=&quot;vi&quot;&gt;@request&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;Rack&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;no&quot;&gt;MockRequest&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;new&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;no&quot;&gt;Sinatra&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;build_application&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;

&lt;span class=&quot;c1&quot;&gt;# ...&lt;/span&gt;

&lt;span class=&quot;vi&quot;&gt;@response&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;vi&quot;&gt;@request&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;request&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;http_method&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;path&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;opts&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;&lt;p&gt;So let&amp;#8217;s try this&lt;sup class=&quot;footnote&quot;&gt;&lt;a href=&quot;#fn1&quot;&gt;1&lt;/a&gt;&lt;/sup&gt;&lt;/p&gt;
&lt;div class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;ruby&quot;&gt;&lt;span class=&quot;vi&quot;&gt;@request&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;Rack&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;no&quot;&gt;MockRequest&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;new&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;no&quot;&gt;Sinatra&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;application&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;

&lt;span class=&quot;nb&quot;&gt;puts&lt;/span&gt; &lt;span class=&quot;vi&quot;&gt;@request&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;request&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&amp;#39;get&amp;#39;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&amp;#39;/&amp;#39;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;inspect&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;&lt;p&gt;yields&lt;/p&gt;
&lt;pre&gt;

#&amp;lt;Rack::MockResponse:0x1031f28 @body=&quot;(...)&quot;, 

@errors=&quot;- - - [28/Nov/2008 12:29:02] \&quot;get / \&quot; 200 1511 0.0034\n&quot;,

@original_headers={&quot;Content-Type&quot;=&amp;gt;&quot;text/html&quot;},

@headers={&quot;Content-Type&quot;=&amp;gt;&quot;text/html&quot;}, @status=200&amp;gt;

&lt;/pre&gt;
&lt;p&gt;We really only care about the response body.  To dump out the index of our site, we just use&lt;/p&gt;
&lt;div class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;ruby&quot;&gt;&lt;span class=&quot;no&quot;&gt;File&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;open&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;no&quot;&gt;File&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;join&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;static_dir&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&amp;#39;index.html&amp;#39;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;),&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&amp;#39;w&amp;#39;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;do&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;|&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;f&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;|&lt;/span&gt;

  &lt;span class=&quot;n&quot;&gt;f&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;print&lt;/span&gt; &lt;span class=&quot;vi&quot;&gt;@request&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;request&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&amp;#39;get&amp;#39;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&amp;#39;/&amp;#39;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;body&lt;/span&gt;

&lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;&lt;p&gt;Then we continue with this process for each section of the site we want to make static.  Now uploading new content is as easy as running our rake task and then using rsync.&lt;/p&gt;
&lt;p&gt;Obviously you can set up your webserver to serve static content when it is available and then fall back on Sinatra when the static content isn&amp;#8217;t present.&lt;/p&gt;
&lt;p&gt;You can see the full code in &lt;a href=&quot;http://github.com/semanticart/stuff-site/tree/master/Rakefile&quot;&gt;the rake file of my repository&lt;/a&gt; or check out an &lt;a href=&quot;http://gist.github.com/30050&quot;&gt;archived gist version&lt;/a&gt;.&lt;/p&gt;
&lt;h3&gt;Nice, what&amp;#8217;s actually happening here?&lt;/h3&gt;
&lt;p&gt;Sinatra is a &lt;span class=&quot;caps&quot;&gt;DSL&lt;/span&gt; built upon &lt;a href=&quot;http://rack.rubyforge.org/&quot;&gt;Rack&lt;/a&gt;, so we simply leverage Rack&amp;#8217;s built-in methods to drive our web app without ever touching the browser.&lt;/p&gt;
&lt;p&gt;From &lt;a href=&quot;http://rack.rubyforge.org/doc/classes/Rack/MockRequest.html&quot;&gt;the documentation for Rack:MockRequest&lt;/a&gt;&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;Rack::MockRequest helps testing your Rack application without actually using &lt;span class=&quot;caps&quot;&gt;HTTP&lt;/span&gt;.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;Note that it is the &lt;span class=&quot;caps&quot;&gt;HTTP&lt;/span&gt; part of the request that is being mocked here.  Your app is receiving the request in the same way that it would if the request were triggered from the browser.&lt;/p&gt;
&lt;p class=&quot;footnote&quot; id=&quot;fn1&quot;&gt;&lt;sup&gt;1&lt;/sup&gt; : though the test/methods.rb isn&amp;#8217;t updated as of publication of this article, Sinatra.build_application is deprecated and it is now recommended you use Sinatra.application instead&lt;/p&gt;
















&lt;script type=&quot;text/javascript&quot;&gt;
	var disqus_url = &quot;http://blog.semanticart.com/driving_sinatra_without_a_browser/&quot;; 
&lt;/script&gt;</content>
 </entry>
 

</feed>

