<?xml version="1.0" encoding="ISO-8859-1"?>
<?xml-stylesheet type="text/xsl" media="screen" href="/~d/styles/rss2full.xsl"?><?xml-stylesheet type="text/css" media="screen" href="http://feeds.feedburner.com/~d/styles/itemcontent.css"?><rss version="2.0">
    <channel>
        <title>semantic art</title>
        <link>http://feeds2.feedburner.com/semanticart-blog</link>
        <description />
        <language>en-us</language>
        <generator>Sinatra and ERB</generator>
    
        
        <atom10:link xmlns:atom10="http://www.w3.org/2005/Atom" rel="self" href="http://feeds.feedburner.com/semanticart-blog" type="application/rss+xml" /><atom10:link xmlns:atom10="http://www.w3.org/2005/Atom" rel="hub" href="http://pubsubhubbub.appspot.com" /><item>
            <title>Killing is_paranoid</title>
            <link>http://blog.semanticart.com/killing_is_paranoid</link>
            <description>&lt;h2&gt;Intro&lt;/h2&gt;
&lt;p&gt;In short, I&amp;#8217;m killing &lt;a href="http://github.com/semanticart/is_paranoid"&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="http://www.binarylogic.com/2009/10/06/discontinuing-resourcelogic/"&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;
</description>
            <pubDate>Tue, 13 Oct 2009 00:00:00 -0000</pubDate>
        </item>
        
        <item>
            <title>Using default_scope to recreate acts_as_paranoid</title>
            <link>http://blog.semanticart.com/using_default_scope_to_recreate_acts_as_paranoid</link>
            <description>&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="default_scope"&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="http://github.com/technoweenie/acts_as_paranoid"&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="http://zargony.com/2007/08/13/scope_out-feature-default_scope"&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="http://github.com/jchupp/is_paranoid"&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;pre&gt;&lt;code class="multiline_code"&gt;&lt;span class="r"&gt;class&lt;/span&gt; &lt;span class="cl"&gt;Article&lt;/span&gt; &amp;lt; &lt;span class="co"&gt;ActiveRecord&lt;/span&gt;::&lt;span class="co"&gt;Base&lt;/span&gt;
  default_scope &lt;span class="sy"&gt;:order&lt;/span&gt; =&amp;gt; &lt;span class="s"&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="k"&gt;created_at DESC&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;/span&gt;
&lt;span class="r"&gt;end&lt;/span&gt;&lt;/code&gt;&lt;/pre&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="caps"&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;pre&gt;&lt;code class="multiline_code"&gt;&lt;span class="r"&gt;class&lt;/span&gt; &lt;span class="cl"&gt;CreateArticles&lt;/span&gt; &amp;lt; &lt;span class="co"&gt;ActiveRecord&lt;/span&gt;::&lt;span class="co"&gt;Migration&lt;/span&gt;
  &lt;span class="r"&gt;def&lt;/span&gt; &lt;span class="pc"&gt;self&lt;/span&gt;.up
    create_table &lt;span class="sy"&gt;:articles&lt;/span&gt; &lt;span class="r"&gt;do&lt;/span&gt; |t|
      t.string &lt;span class="sy"&gt;:name&lt;/span&gt;
      t.text &lt;span class="sy"&gt;:body&lt;/span&gt;
      t.timestamp &lt;span class="sy"&gt;:deleted_at&lt;/span&gt;, &lt;span class="sy"&gt;:default&lt;/span&gt; =&amp;gt; &lt;span class="pc"&gt;nil&lt;/span&gt;
      t.timestamps
    &lt;span class="r"&gt;end&lt;/span&gt;
  &lt;span class="r"&gt;end&lt;/span&gt;

  &lt;span class="r"&gt;def&lt;/span&gt; &lt;span class="pc"&gt;self&lt;/span&gt;.down
    drop_table &lt;span class="sy"&gt;:articles&lt;/span&gt;
  &lt;span class="r"&gt;end&lt;/span&gt;
&lt;span class="r"&gt;end&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;And our default scope should look like:&lt;/p&gt;
&lt;pre&gt;&lt;code class="multiline_code"&gt;&lt;span class="r"&gt;class&lt;/span&gt; &lt;span class="cl"&gt;Article&lt;/span&gt; &amp;lt; &lt;span class="co"&gt;ActiveRecord&lt;/span&gt;::&lt;span class="co"&gt;Base&lt;/span&gt;  
  default_scope &lt;span class="sy"&gt;:conditions&lt;/span&gt; =&amp;gt; {&lt;span class="sy"&gt;:deleted_at&lt;/span&gt; =&amp;gt; &lt;span class="pc"&gt;nil&lt;/span&gt;}
&lt;span class="r"&gt;end&lt;/span&gt;&lt;/code&gt;&lt;/pre&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="caps"&gt;FROM&lt;/span&gt; &amp;#8220;articles&amp;#8221; &lt;span class="caps"&gt;WHERE&lt;/span&gt; (&amp;#8220;articles&amp;#8221;.&amp;#8220;deleted_at&amp;#8221; IS &lt;span class="caps"&gt;NULL&lt;/span&gt;) &lt;span class="caps"&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;pre&gt;&lt;code class="multiline_code"&gt;&lt;span class="r"&gt;class&lt;/span&gt; &lt;span class="cl"&gt;Article&lt;/span&gt; &amp;lt; &lt;span class="co"&gt;ActiveRecord&lt;/span&gt;::&lt;span class="co"&gt;Base&lt;/span&gt;  
  default_scope &lt;span class="sy"&gt;:conditions&lt;/span&gt; =&amp;gt; {&lt;span class="sy"&gt;:deleted_at&lt;/span&gt; =&amp;gt; &lt;span class="pc"&gt;nil&lt;/span&gt;}

  &lt;span class="r"&gt;def&lt;/span&gt; &lt;span class="fu"&gt;destroy&lt;/span&gt;
    &lt;span class="pc"&gt;self&lt;/span&gt;.update_attribute(&lt;span class="sy"&gt;:deleted_at&lt;/span&gt;, &lt;span class="co"&gt;Time&lt;/span&gt;.now.utc)
  &lt;span class="r"&gt;end&lt;/span&gt;
&lt;span class="r"&gt;end&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Let&amp;#8217;s give it a spin:&lt;/p&gt;
&lt;pre&gt;&lt;code class="multiline_code"&gt;&amp;gt;&amp;gt; a = &lt;span class="co"&gt;Article&lt;/span&gt;.create(&lt;span class="sy"&gt;:name&lt;/span&gt; =&amp;gt; &lt;span class="s"&gt;&lt;span class="dl"&gt;&amp;quot;&lt;/span&gt;&lt;span class="k"&gt;Test Article&lt;/span&gt;&lt;span class="dl"&gt;&amp;quot;&lt;/span&gt;&lt;/span&gt;, &lt;span class="sy"&gt;:body&lt;/span&gt; =&amp;gt; &lt;span class="s"&gt;&lt;span class="dl"&gt;&amp;quot;&lt;/span&gt;&lt;span class="k"&gt;some body&lt;/span&gt;&lt;span class="dl"&gt;&amp;quot;&lt;/span&gt;&lt;/span&gt;)
=&amp;gt; &lt;span class="c"&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;
&amp;gt;&amp;gt; a.destroy
=&amp;gt; &lt;span class="pc"&gt;true&lt;/span&gt;
&amp;gt;&amp;gt; &lt;span class="co"&gt;Article&lt;/span&gt;.first
=&amp;gt; &lt;span class="pc"&gt;nil&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;But the log shows it actually just did &amp;quot;UPDATE &amp;#8220;articles&amp;#8221; &lt;span class="caps"&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="caps"&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="http://github.com/jchupp/is_paranoid"&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;pre&gt;&lt;code class="multiline_code"&gt;&lt;span class="r"&gt;def&lt;/span&gt; &lt;span class="pc"&gt;self&lt;/span&gt;.find_with_destroyed *args
  &lt;span class="pc"&gt;self&lt;/span&gt;.with_exclusive_scope { find(*args) }
&lt;span class="r"&gt;end&lt;/span&gt;&lt;/code&gt;&lt;/pre&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;pre&gt;&lt;code class="multiline_code"&gt;&amp;gt;&amp;gt; &lt;span class="co"&gt;Article&lt;/span&gt;.find_with_destroyed(&lt;span class="sy"&gt;:first&lt;/span&gt;)
=&amp;gt; &lt;span class="c"&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;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="http://github.com/jchupp/is_paranoid"&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="http://twitter.com/technoweenie/statuses/1241904419"&gt;no longer uses &lt;span class="caps"&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="caps"&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="http://api.rubyonrails.org/classes/ActiveRecord/Base.html"&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="http://github.com/jchupp/is_paranoid"&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;p&gt;If you like this or the other articles, please consider subscribing to my &lt;a href="http://feeds2.feedburner.com/semanticart-blog"&gt;rss feed&lt;/a&gt;&lt;/p&gt;





</description>
            <pubDate>Sun, 22 Mar 2009 00:00:00 -0000</pubDate>
        </item>
        
        <item>
            <title>Comparing Thinking Sphinx And Acts As Ferret For Full-text Indexing In Rails</title>
            <link>http://blog.semanticart.com/comparing_thinking_sphinx_and_acts_as_ferret_for_full-text_indexing_in_rails</link>
            <description>&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="http://github.com/jkraemer/acts_as_ferret/commits/master"&gt;acts_as_ferret&lt;/a&gt; (&lt;span class="caps"&gt;AAF&lt;/span&gt;) and &lt;a href="http://github.com/freelancing-god/thinking-sphinx/tree/master"&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="caps"&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="http://groups.google.com/group/thinking-sphinx?pli=1"&gt;google group&lt;/a&gt; and several more recent tutorials including a fairly recent &lt;a href="http://railscasts.com/episodes/120-thinking-sphinx"&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="caps"&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="http://www.ruby-forum.com/topic/137629"&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;br /&gt;
galore. We&amp;#8217;ve switched around 40 clients form ferret to sphinx and&lt;br /&gt;
solved their problems this way. I will never use ferret again after&lt;br /&gt;
all the problems I have seen it cause peoples production apps.&lt;/p&gt;
&lt;/blockquote&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="http://peepcode.com/products/thinking-sphinx-pdf"&gt;Thinking Sphinx Peepcode &lt;span class="caps"&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 &#x2013; both MySQL and PostgreSQL &#x2013; 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;pre&gt;&lt;code class="multiline_code"&gt;define_index &lt;span class="r"&gt;do&lt;/span&gt;
  indexes [&lt;span class="sy"&gt;:body&lt;/span&gt;, &lt;span class="sy"&gt;:title&lt;/span&gt;]
  where [&lt;span class="s"&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="k"&gt;deleted_at is NULL&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;/span&gt;]
&lt;span class="r"&gt;end&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;This also means that TS can&amp;#8217;t index computed values as easily.  In &lt;span class="caps"&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;pre&gt;&lt;code class="multiline_code"&gt;&lt;span class="r"&gt;def&lt;/span&gt; &lt;span class="fu"&gt;ordinalized_names_of_children&lt;/span&gt;
  ordinalized_children = []
  &lt;span class="pc"&gt;self&lt;/span&gt;.children.sort_by(&amp;amp;&lt;span class="sy"&gt;:birth_date&lt;/span&gt;).each_with_index &lt;span class="r"&gt;do&lt;/span&gt; |child, i|
    ordinalized_children &amp;lt;&amp;lt; [child.first_name, i.to_ordinal]
  &lt;span class="r"&gt;end&lt;/span&gt;
  ordinalized_children
&lt;span class="r"&gt;end&lt;/span&gt;&lt;/code&gt;&lt;/pre&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="caps"&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="http://www.railsenvy.com/2007/2/19/acts-as-ferret-tutorial"&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&#x2019;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="http://ts.freelancing-gods.com/usage.html"&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="caps"&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="caps"&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;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="caps"&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="http://peepcode.com/products/thinking-sphinx-pdf"&gt;Peepcode &lt;span class="caps"&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="http://www.sphinxsearch.com/docs/current.html"&gt;Section 3.5 of the Sphinx documentation&lt;/a&gt; states, &amp;#8220;&lt;span class="caps"&gt;ALL&lt;/span&gt; &lt;span class="caps"&gt;DOCUMENT&lt;/span&gt; &lt;span class="caps"&gt;IDS&lt;/span&gt; &lt;span class="caps"&gt;MUST&lt;/span&gt; BE &lt;span class="caps"&gt;UNIQUE&lt;/span&gt; &lt;span class="caps"&gt;UNSIGNED&lt;/span&gt; &lt;span class="caps"&gt;NON&lt;/span&gt;-&lt;span class="caps"&gt;ZERO&lt;/span&gt; &lt;span class="caps"&gt;INTEGER&lt;/span&gt; &lt;span class="caps"&gt;NUMBERS&lt;/span&gt; (32-&lt;span class="caps"&gt;BIT&lt;/span&gt; OR 64-&lt;span class="caps"&gt;BIT&lt;/span&gt;, &lt;span class="caps"&gt;DEPENDING&lt;/span&gt; ON &lt;span class="caps"&gt;BUILD&lt;/span&gt; &lt;span class="caps"&gt;TIME&lt;/span&gt; &lt;span class="caps"&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="http://politics4all.com"&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="caps"&gt;GUID&lt;/span&gt; so we decided early on to use the &lt;span class="caps"&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;pre&gt;&lt;code class="multiline_code"&gt;&lt;span class="r"&gt;class&lt;/span&gt; &lt;span class="cl"&gt;Robot&lt;/span&gt; &amp;lt; &lt;span class="co"&gt;ActiveRecord&lt;/span&gt;::&lt;span class="co"&gt;Base&lt;/span&gt;
  &lt;span class="c"&gt;# The key ActiveRecord will use on joins, map to id, etc.&lt;/span&gt;
  &lt;span class="c"&gt;# Setting the primary key isn't necessary for set_sphinx_primary_key to work&lt;/span&gt;
  set_primary_key &lt;span class="sy"&gt;:internal_id&lt;/span&gt;

  &lt;span class="c"&gt;# The key sphinx will use for indexing, must be a unique integer&lt;/span&gt;
  set_sphinx_primary_key &lt;span class="sy"&gt;:alternate_primary_key&lt;/span&gt;

  define_index &lt;span class="r"&gt;do&lt;/span&gt;
    indexes &lt;span class="sy"&gt;:name&lt;/span&gt;
  &lt;span class="r"&gt;end&lt;/span&gt;
&lt;span class="r"&gt;end&lt;/span&gt;&lt;/code&gt;&lt;/pre&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="caps"&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;You can find these updates in &lt;a href="http://github.com/jchupp/thinking-sphinx/tree/alt_primary_key"&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;/p&gt;









</description>
            <pubDate>Thu, 05 Mar 2009 00:00:00 -0000</pubDate>
        </item>
        
        <item>
            <title>CsvMapper gets a little more magical</title>
            <link>http://blog.semanticart.com/csv_mapper_gets_a_little_more_magical</link>
            <description>&lt;p&gt;Have you checked out &lt;a href="http://github.com/pillowfactory/csv-mapper/tree/master"&gt;CsvMapper&lt;/a&gt; by &lt;a href="http://github.com/pillowfactory"&gt;Luke Pillow&lt;/a&gt;?  It makes some of the drudgery of &lt;span class="caps"&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;pre&gt;&lt;code class="multiline_code"&gt;include &lt;span class="co"&gt;CsvMapper&lt;/span&gt;

results = import(&lt;span class="s"&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="k"&gt;/path/to/file.csv&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;/span&gt;) &lt;span class="r"&gt;do&lt;/span&gt;
  start_at_row &lt;span class="i"&gt;1&lt;/span&gt;
  [first_name, last_name, age]
&lt;span class="r"&gt;end&lt;/span&gt;

results.first.first_name  &lt;span class="c"&gt;# John&lt;/span&gt;
results.first.last_name   &lt;span class="c"&gt;# Doe&lt;/span&gt;
results.first.age         &lt;span class="c"&gt;# 27&lt;/span&gt;&lt;/code&gt;&lt;/pre&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="caps"&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;pre&gt;&lt;code class="multiline_code"&gt;include &lt;span class="co"&gt;CsvMapper&lt;/span&gt;

results = import(&lt;span class="s"&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="k"&gt;/path/to/file.csv&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;/span&gt;) &lt;span class="r"&gt;do&lt;/span&gt;
  read_attributes_from_file
&lt;span class="r"&gt;end&lt;/span&gt;

results.first.first_name  &lt;span class="c"&gt;# John&lt;/span&gt;
results.first.last_name   &lt;span class="c"&gt;# Doe&lt;/span&gt;
results.first.age         &lt;span class="c"&gt;# 27&lt;/span&gt;&lt;/code&gt;&lt;/pre&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;pre&gt;&lt;code class="multiline_code"&gt;results = import(&lt;span class="s"&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="k"&gt;/path/to/file.csv&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;/span&gt;) &lt;span class="r"&gt;do&lt;/span&gt;
  read_attributes_from_file(&lt;span class="s"&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="k"&gt;First Name&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;/span&gt; =&amp;gt; &lt;span class="s"&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="k"&gt;what_my_friends_call_me&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;/span&gt;)
&lt;span class="r"&gt;end&lt;/span&gt;

results.first.what_my_friends_call_me  &lt;span class="c"&gt;# John&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;What about more realistic example with a bigger csv file?  OK, here&amp;#8217;s one from &lt;a href="http://subsidyscope.com/projects/bailout/tarp/"&gt;Subsidyscope&lt;/a&gt;:&lt;/p&gt;
&lt;pre&gt;&lt;code class="multiline_code"&gt;results = import(&lt;span class="s"&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="k"&gt;subsidyscope-tarp.csv&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;/span&gt;){read_attributes_from_file}
results.first  &lt;span class="c"&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="c"&gt;# description=&amp;quot;Preferred Stock w/Warrants&amp;quot;, &lt;/span&gt;
               &lt;span class="c"&gt;# total_assets=&amp;quot;1856207282000.00&amp;quot;, date=&amp;quot;2008-10-28&amp;quot;, &lt;/span&gt;
               &lt;span class="c"&gt;# fdic_number=&amp;quot;1039502&amp;quot;, ots_number=nil, transaction_type=&amp;quot;Purchase&amp;quot;,&lt;/span&gt;
               &lt;span class="c"&gt;# price_paid=&amp;quot;25000000000.00&amp;quot;, state=&amp;quot;NY&amp;quot;, &lt;/span&gt;
               &lt;span class="c"&gt;# name=&amp;quot;JPMorgan Chase &amp;amp; Co.&amp;quot;, stock_symbol=&amp;quot;JPM&amp;quot;,&lt;/span&gt;
               &lt;span class="c"&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;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="http://github.com/jchupp/csv-mapper/tree/master"&gt;CsvMapper github fork&lt;/a&gt; until it maybe gets merged into the official branch.&lt;/p&gt;
&lt;p&gt;If you like this or the other articles, please consider subscribing to my &lt;a href="http://feeds2.feedburner.com/semanticart-blog"&gt;rss feed&lt;/a&gt;&lt;/p&gt;



</description>
            <pubDate>Fri, 06 Feb 2009 00:00:00 -0000</pubDate>
        </item>
        
        <item>
            <title>Ruby Idioms from Open Source, Volume One - Seinfeld</title>
            <link>http://blog.semanticart.com/ruby_idioms_from_open_source_volume_one</link>
            <description>&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="http://github.com/technoweenie/seinfeld/tree/master"&gt;Seinfeld&lt;/a&gt; which powers &lt;a href="http://calendaraboutnothing.com"&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="footnote"&gt;&lt;a href="#fn1"&gt;1&lt;/a&gt;&lt;/sup&gt; of seinfeld_calendar.rb in the page_title method:&lt;br /&gt;
&lt;pre&gt;&lt;code class="multiline_code"&gt;&lt;span class="r"&gt;def&lt;/span&gt; &lt;span class="fu"&gt;page_title&lt;/span&gt;
  &lt;span class="s"&gt;&lt;span class="dl"&gt;&amp;quot;&lt;/span&gt;&lt;span class="k"&gt;%s's Calendar&lt;/span&gt;&lt;span class="dl"&gt;&amp;quot;&lt;/span&gt;&lt;/span&gt; % &lt;span class="iv"&gt;@user&lt;/span&gt;.login
&lt;span class="r"&gt;end&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;br /&gt;
Here we see &lt;a href="http://www.ruby-doc.org/core/classes/String.html#M000785"&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;pre&gt;&lt;code class="multiline_code"&gt;now        = &lt;span class="co"&gt;Date&lt;/span&gt;.new(params[&lt;span class="sy"&gt;:year&lt;/span&gt;], params[&lt;span class="sy"&gt;:month&lt;/span&gt;])
prev_month = now &amp;lt;&amp;lt; &lt;span class="i"&gt;1&lt;/span&gt;
next_month = now &amp;gt;&amp;gt; &lt;span class="i"&gt;1&lt;/span&gt;&lt;/code&gt;&lt;/pre&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="http://www.ruby-doc.org/stdlib/libdoc/date/rdoc/classes/Date.html#M000382"&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;pre&gt;&lt;code class="multiline_code"&gt;&lt;span class="r"&gt;return&lt;/span&gt; &lt;span class="r"&gt;if&lt;/span&gt; login_name.size.zero?&lt;/code&gt;&lt;/pre&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="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"&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="footnote" id="fn1"&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;




</description>
            <pubDate>Sat, 29 Nov 2008 00:00:00 -0000</pubDate>
        </item>
        
        <item>
            <title>Driving Sinatra Without A Browser</title>
            <link>http://blog.semanticart.com/driving_sinatra_without_a_browser</link>
            <description>&lt;h3&gt;Intro&lt;/h3&gt;
&lt;p&gt;I&amp;#8217;ve been hacking on leethal&amp;#8217;s &lt;a href="http://github.com/leethal/stuff-site/tree/master"&gt;stuff-site&lt;/a&gt; (the software that runs this blog) recently and adding a good number of features to &lt;a href="http://github.com/jchupp/stuff-site/tree/master"&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="http://sinatra.rubyforge.org/"&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="http://github.com/bmizerany/sinatra/tree/master/lib/sinatra/test/methods.rb#L35"&gt;/lib/sinatra/test/methods.rb&lt;/a&gt; but the lines we really care about are 36 and 56&lt;br /&gt;
&lt;pre&gt;&lt;code class="multiline_code"&gt;&lt;span class="iv"&gt;@request&lt;/span&gt; = &lt;span class="co"&gt;Rack&lt;/span&gt;::&lt;span class="co"&gt;MockRequest&lt;/span&gt;.new(&lt;span class="co"&gt;Sinatra&lt;/span&gt;.build_application)
&lt;span class="c"&gt;# ...&lt;/span&gt;
&lt;span class="iv"&gt;@response&lt;/span&gt; = &lt;span class="iv"&gt;@request&lt;/span&gt;.request(http_method, path, opts)&lt;/code&gt;&lt;/pre&gt;&lt;/p&gt;
&lt;p&gt;So let&amp;#8217;s try this&lt;sup class="footnote"&gt;&lt;a href="#fn1"&gt;1&lt;/a&gt;&lt;/sup&gt;&lt;br /&gt;
&lt;pre&gt;&lt;code class="multiline_code"&gt;&lt;span class="iv"&gt;@request&lt;/span&gt; = &lt;span class="co"&gt;Rack&lt;/span&gt;::&lt;span class="co"&gt;MockRequest&lt;/span&gt;.new(&lt;span class="co"&gt;Sinatra&lt;/span&gt;.application)
puts &lt;span class="iv"&gt;@request&lt;/span&gt;.request(&lt;span class="s"&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="k"&gt;get&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;/span&gt;, &lt;span class="s"&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="k"&gt;/&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;/span&gt;).inspect&lt;/code&gt;&lt;/pre&gt;&lt;/p&gt;
&lt;p&gt;yields&lt;br /&gt;
&lt;pre&gt;&lt;br /&gt;
#&amp;lt;Rack::MockResponse:0&amp;#215;1031f28 @body=&amp;#8220;(&amp;#8230;)&amp;#8221;, &lt;br /&gt;
@errors=&amp;#8220;- &amp;#8211; - [28/Nov/2008 12:29:02] \&amp;#8221;get / \&amp;quot; 200 1511 0.0034\n&amp;quot;,&lt;br /&gt;
@original_headers={&amp;quot;Content-Type&amp;quot;=&amp;gt;&amp;quot;text/html&amp;quot;},&lt;br /&gt;
@headers={&amp;quot;Content-Type&amp;quot;=&amp;gt;&amp;quot;text/html&amp;quot;}, @status=200&amp;gt;&lt;br /&gt;
&lt;/pre&gt;&lt;/p&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;pre&gt;&lt;code class="multiline_code"&gt;&lt;span class="co"&gt;File&lt;/span&gt;.open(&lt;span class="co"&gt;File&lt;/span&gt;.join(static_dir, &lt;span class="s"&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="k"&gt;index.html&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;/span&gt;), &lt;span class="s"&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="k"&gt;w&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;/span&gt;) &lt;span class="r"&gt;do&lt;/span&gt; |f|
  f.print &lt;span class="iv"&gt;@request&lt;/span&gt;.request(&lt;span class="s"&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="k"&gt;get&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;/span&gt;, &lt;span class="s"&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="k"&gt;/&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;/span&gt;).body
&lt;span class="r"&gt;end&lt;/span&gt;&lt;/code&gt;&lt;/pre&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="http://github.com/jchupp/stuff-site/tree/master/Rakefile"&gt;the rake file of my repository&lt;/a&gt; or check out an &lt;a href="http://gist.github.com/30050"&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="caps"&gt;DSL&lt;/span&gt; built upon &lt;a href="http://rack.rubyforge.org/"&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="http://rack.rubyforge.org/doc/classes/Rack/MockRequest.html"&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="caps"&gt;HTTP&lt;/span&gt;.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;Note that it is the &lt;span class="caps"&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="footnote" id="fn1"&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;






</description>
            <pubDate>Fri, 28 Nov 2008 00:00:00 -0000</pubDate>
        </item>
        
        <item>
            <title>Sinatra, Github, And More</title>
            <link>http://blog.semanticart.com/sinatra,_github,_and_more</link>
            <description>&lt;p&gt;Some random things&amp;#8230;  I&amp;#8217;m now six months into my job at &lt;a href="http://politics4all.com"&gt;Politics4All&lt;/a&gt; and it is pretty amazing how far we&amp;#8217;ve come as a company and how much my views on open-source and ruby have matured.&lt;/p&gt;
&lt;p&gt;&lt;a href="http://sinatra.rubyforge.org/"&gt;Sinatra&lt;/a&gt; is an awesome framework.  I&amp;#8217;ve written &lt;a href="http://github.com/jchupp/hoboken/tree"&gt;hoboken&lt;/a&gt; (a simple wiki) with it and now I&amp;#8217;m running this blog on it thanks to &lt;a href="http://august.lilleaas.net/"&gt;August Lilleaas&amp;#8217;&lt;/a&gt; &lt;a href="http://github.com/leethal/stuff-site/tree/master"&gt;stuff-site&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;There&amp;#8217;s something about &lt;a href="http://www.rubyonrails.org/"&gt;Rails&lt;/a&gt; where once you start using it you want to use it for things that it is overkill for.  You can use a chainsaw to cut the Thanksgiving turkey, but are you sure that&amp;#8217;s the wisest thing to do?  Sinatra stays out of the way and lets you do as little as you want to do.  All Sinatra is doing for me is pointing http requests to my methods.  There&amp;#8217;s no black magic or voodoo going on, it is just a small &lt;span class="caps"&gt;DSL&lt;/span&gt; on top of rack.  It does what it does, and lets me do what I want to do.  That&amp;#8217;s awesome.&lt;/p&gt;
&lt;p&gt;&lt;a href="http://github.com"&gt;GitHub&lt;/a&gt; has, for months, only existed for me as the place where my work repository was stored and a place where I could browse other people&amp;#8217;s repositories.  I&amp;#8217;ve dabbled in open source before, but when &lt;a href="http://calendaraboutnothing.com/"&gt;Calendar About Nothing&lt;/a&gt; showed up, something clicked for me.  I might not be able to turn out the next big thing, but there&amp;#8217;s surely something incremental I can do each day to make my hobby projects and the projects of others better.  &lt;a href="http://calendaraboutnothing.com/~jchupp"&gt;Sure enough&lt;/a&gt;, I&amp;#8217;m now thirteen days into a streak.  I&amp;#8217;ve worked on &lt;a href="http://github.com/jchupp/hoboken/tree"&gt;hoboken&lt;/a&gt; and a few api wrappers and made minor improvements to &lt;a href="http://github.com/jchupp/gnip-ruby/tree/master"&gt;gnip-ruby&lt;/a&gt; and the aforementioned &lt;a href="http://github.com/jchupp/stuff-site/tree/master"&gt;stuff-site&lt;/a&gt;.  You can &lt;a href="http://github.com/jchupp"&gt;follow me on github&lt;/a&gt; if you&amp;#8217;d like.&lt;/p&gt;
&lt;p&gt;So, &amp;#8220;thank you&amp;#8221; to August, the people behind sinatra, github, and Calendar About Nothing.&lt;/p&gt;
















</description>
            <pubDate>Tue, 25 Nov 2008 00:00:00 -0000</pubDate>
        </item>
        
        <item>
            <title>Review Of Envy Cast's Ruby On Rails 2.2 Screencast And Pdf</title>
            <link>http://blog.semanticart.com/review_of_envy_cast's_ruby_on_rails_2.2_screencast_and_pdf</link>
            <description>&lt;p&gt;
Recently I was given the opportunity to review the &lt;a href="http://envycasts.com/products/ruby-on-rails-22-screencast" title="Ruby on Rails 2.2 Envycast"&gt;Ruby on Rails Envycast of Rails 2.2&lt;/a&gt; from &lt;a href="http://envycasts.com/"&gt;Envy Casts&lt;/a&gt;.  I&amp;#8217;ve long been a fan of the &lt;a href="http://www.railsenvy.com/"&gt;Rails Envy podcast&lt;/a&gt;.  They give us solutions to Ruby and Rails problems as well as provoking deep thought about the Terminator.  They even mocked me once.  But most people will remember them as the jerks who encouraged everyone to use Ferret.
&lt;/p&gt;
&lt;p&gt;
If you&amp;#8217;re not familiar with the Rails Envy podcast, you should do yourself a favor and go listen to an episode first.  Don&amp;#8217;t worry, I&amp;#8217;ll wait here&amp;#8230;
&lt;/p&gt;
&lt;p&gt;
To try to write about Rails screencasts without mentioning Railscasts or Peepcode is impossible, so I won&amp;#8217;t try to act like the Rails Envy guys invented this space.  What do they bring to the table?  They&amp;#8217;re in front of the camera, for one thing, and despite the fact that neither Gregg nor Jason look like that beautiful girl you knew in high school turned exotic dancer, seeing them makes the their interactions and gags all that much more enjoyable.  Also, this video is a good bit longer than the Railscasts (44 minutes!) and this in-front-of-the-camera methodology keeps you from spacing out as I&amp;#8217;m prone to do watching a Peepcode screencast.
&lt;/p&gt;
&lt;p&gt;
The Ruby on Rails 2.2 Package is the second offering from Envy Casts.  It is available as a &lt;a href="http://envycasts.com/products/ruby-on-rails-22-screencast"&gt;9 dollar video&lt;/a&gt; or a &lt;a href="http://envycasts.com/products/ruby-on-rails-22-pdf"&gt;9 dollar &lt;span class="caps"&gt;PDF&lt;/span&gt;&lt;/a&gt;, or as a &lt;a href="http://envycasts.com/products/ruby-on-rails-22-package-deal"&gt;16 dollar combo&lt;/a&gt;.  Take a moment to write a rails app illustrating how much you save by buying the combo*.  Now scale it across 5 slices.  Now consider the silence of a pebble.
&lt;/p&gt;
&lt;p&gt;
Both items (surprise) cover the new features in Rails 2.2.  If you haven&amp;#8217;t watched the commit logs or kept up with Ryan Daigle&amp;#8217;s &lt;a href="%20http://ryandaigle.com/"&gt;What&amp;#8217;s new in Edge Rails&lt;/a&gt;, you&amp;#8217;ve missed some massive changes.  This &lt;span class="caps"&gt;PDF&lt;/span&gt; and screencast will bring you up to speed quickly.
&lt;/p&gt;
&lt;p&gt;
The screencast is conveniently broken into chapters (in the mov format, anyway), so, of course, is the &lt;span class="caps"&gt;PDF&lt;/span&gt;.  Each segment is filled with new features and each new feature is approached in a &amp;#8220;why would I care about this?&amp;#8221; manner that cuts through the fluff and lets you easily see how the change is relevant.
&lt;/p&gt;
&lt;p&gt;
The &lt;span class="caps"&gt;PDF&lt;/span&gt; covers the whole gambit of changes in Rails 2.2, while the video version covers a more interesting subset of the features.  The content quality of the &lt;span class="caps"&gt;PDF&lt;/span&gt; is on par with the PDF&amp;#8217;s you&amp;#8217;d get from PeepCode, but the visual quality is not nearly as polished.  Don&amp;#8217;t misunderstand, the content is perfectly readable, it just lacks any splash of color to break up the monotonous black and white.  Syntax highlighting and broken out sidebar boxes with key information might be useful in future Envy Cast PDFs.
&lt;/p&gt;
&lt;p&gt;
There are a few awkward moments where they&amp;#8217;re just left giggling at each other and some asides that go absolutely nowhere, but if you&amp;#8217;re an fan of the podcast, you know that&amp;#8217;s just part of the charm.  Besides, who wants to read a boring list of changes with examples?  Well, I do.  But I also like a summary format that gives me an overview with fancy special effects that appeal to my short attention span.  That&amp;#8217;s why the bundle of the &lt;span class="caps"&gt;PDF&lt;/span&gt; and the screencast is an excellent choice.  The grown up in me likes the dense and concise information in the &lt;span class="caps"&gt;PDF&lt;/span&gt;, but the kid in me likes the bleeped out profanity.
&lt;/p&gt;
&lt;p&gt;
Did I learn?  Yes.  Did I laugh?  Yes.  Would I recommend the screencast?  Yes, but that &amp;#8220;yes&amp;#8221; becomes &amp;#8220;Absolutely!&amp;#8221; if you are already a fan of the podcast.  The &lt;span class="caps"&gt;PDF&lt;/span&gt; is vanilla enough that regardless of your tastes in humor, you should make it a purchase.
&lt;/p&gt;
&lt;p&gt;
The Envy Cast guys didn&amp;#8217;t invent the screencast, but they&amp;#8217;ve made it enjoyable.  If you want to learn without feeling like you&amp;#8217;re working and become enlightened on Rails 2.2 rapidly, this is the best $16 you can spend.
&lt;/p&gt;
&lt;p&gt;
&lt;small&gt;
*Bonus points to anyone who uses BackgrounDRb for the heavy math.
&lt;/small&gt;
&lt;/p&gt;</description>
            <pubDate>Wed, 05 Nov 2008 00:00:00 -0000</pubDate>
        </item>
        
    </channel>
</rss>
