<?xml version="1.0" encoding="UTF-8"?>
<?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 xmlns:content="http://purl.org/rss/1.0/modules/content/" xmlns:wfw="http://wellformedweb.org/CommentAPI/" xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:sy="http://purl.org/rss/1.0/modules/syndication/" xmlns:slash="http://purl.org/rss/1.0/modules/slash/" version="2.0">

<channel>
	<title>Future Adapter</title>
	
	<link>http://futureadapter.com</link>
	<description>Cool Tech</description>
	<lastBuildDate>Fri, 13 Nov 2009 14:37:22 +0000</lastBuildDate>
	<generator>http://wordpress.org/?v=2.8.6</generator>
	<language>en</language>
	<sy:updatePeriod>hourly</sy:updatePeriod>
	<sy:updateFrequency>1</sy:updateFrequency>
			<atom10:link xmlns:atom10="http://www.w3.org/2005/Atom" rel="self" type="application/rss+xml" href="http://feeds.feedburner.com/futureadapter" /><feedburner:info xmlns:feedburner="http://rssnamespace.org/feedburner/ext/1.0" uri="futureadapter" /><atom10:link xmlns:atom10="http://www.w3.org/2005/Atom" rel="hub" href="http://pubsubhubbub.appspot.com/" /><item>
		<title>authlogic plugin error/fix</title>
		<link>http://futureadapter.com/2009/11/13/authlogic-plugin-errorfix/</link>
		<comments>http://futureadapter.com/2009/11/13/authlogic-plugin-errorfix/#comments</comments>
		<pubDate>Fri, 13 Nov 2009 14:37:22 +0000</pubDate>
		<dc:creator>mattenat</dc:creator>
				<category><![CDATA[Uncategorized]]></category>
		<category><![CDATA[authlogic]]></category>
		<category><![CDATA[authlogic_facebook_connect]]></category>
		<category><![CDATA[capistrano]]></category>
		<category><![CDATA[gems]]></category>
		<category><![CDATA[rails]]></category>
		<category><![CDATA[rubyonrails]]></category>

		<guid isPermaLink="false">http://futureadapter.com/?p=293</guid>
		<description><![CDATA[I&#8217;ve seen this error lots and lots of times dealing with authlogic plugins:
rake aborted!
undefined method `add_acts_as_authentic_module' for ActiveRecord::Base:Class
And I&#8217;ve searched on the web for answers and there&#8217;s not a really good source as to why it happens or what can be done to fix it.  Yesterday, I put an end to the error in authlogic_facebook_connect [...]]]></description>
			<content:encoded><![CDATA[<p>I&#8217;ve seen this error lots and lots of times dealing with <a href="http://github.com/binarylogic/authlogic/" target="_blank">authlogic</a> plugins:</p>
<p><code>rake aborted!<br />
undefined method `add_acts_as_authentic_module' for ActiveRecord::Base:Class</code></p>
<p>And I&#8217;ve searched on the web for answers and there&#8217;s not a really good source as to why it happens or what can be done to fix it.  Yesterday, I put <a href="http://github.com/kalasjocke/authlogic_facebook_connect/commit/b6202f982820d6f7dd9cf818b2e8e5e532625264" target="_blank">an end to the error</a> in <a href="http://github.com/kalasjocke/authlogic_facebook_connect/" target="_blank">authlogic_facebook_connect</a> (great plugin, BTW) and wanted to post here so the next schlub who comes along googling for a solution will find it.</p>
<p>The conversational explanation is that if you&#8217;re attempting to use rake to install, build, unpack or otherwise effect gems, its possible you&#8217;ll have issues with the plugins that depend on those not-yet-working gems.  The exact use-case for me is that I have authlogic installed as a gem on my local box.  I have authlogic_facebook_connect installed as a plugin.  I setup <a href="http://github.com/jamis/capistrano/" target="_blank">capistrano</a> to install the gems on every update of the code to the server (to pickup new gem dependencies in environment.rb).  When the code is deployed to the server and the <em>rake gems:install</em> task is run, authlogic_facebook_connect would cause an error because of its dependence on the yet-to-be-installed authlogic, preventing the installation of authlogic.  The fix is to simply guard the integration code with a check to make sure authlogic is installed.  That way, there will be no errors while installing authlogic and when the server is started immediately after the gems:install, authlogic_facebook_connect will integrate correctly (upon writing this, perhaps a warning would be in order instead of silent ignorance).</p>
<p>The code:</p>
<pre><span class="keyword">if</span> <span class="type">ActiveRecord</span>::<span class="type">Base</span>.respond_to?(<span class="constant">:add_acts_as_authentic_module</span>)
  <span class="type">ActiveRecord</span>::<span class="type">Base</span>.send(<span class="constant">:include</span>, <span class="type">AuthlogicFacebookConnect</span>::<span class="type">ActsAsAuthentic</span>)
  <span class="type">Authlogic</span>::<span class="type">Session</span>::<span class="type">Base</span>.send(<span class="constant">:include</span>, <span class="type">AuthlogicFacebookConnect</span>::<span class="type">Session</span>)
  <span class="type">ActionController</span>::<span class="type">Base</span>.helper <span class="type">AuthlogicFacebookConnect</span>::<span class="type">Helper</span>
<span class="keyword">end</span></pre>
<p>I imagine the exact same fix would work for <a href="http://github.com/search?langOverride=&amp;language=&amp;q=authlogic&amp;repo=&amp;start_value=1&amp;type=Repositories&amp;x=0&amp;y=0" target="_blank">all the various authlogic plugins</a>.</p>
<p>Thanks goes out to the developers of authlogic, authlogic_facebook_connect, and all the other authlogic plugin developers.  You guys have built a really great-to-use, modular, extendable authentication system that I enjoy very much.</p>
<div class="feedflare">
<a href="http://feeds.feedburner.com/~ff/futureadapter?a=4sanTmGIR3w:eE4Q59X2rdU:yIl2AUoC8zA"><img src="http://feeds.feedburner.com/~ff/futureadapter?d=yIl2AUoC8zA" border="0"></img></a> <a href="http://feeds.feedburner.com/~ff/futureadapter?a=4sanTmGIR3w:eE4Q59X2rdU:F7zBnMyn0Lo"><img src="http://feeds.feedburner.com/~ff/futureadapter?i=4sanTmGIR3w:eE4Q59X2rdU:F7zBnMyn0Lo" border="0"></img></a> <a href="http://feeds.feedburner.com/~ff/futureadapter?a=4sanTmGIR3w:eE4Q59X2rdU:V_sGLiPBpWU"><img src="http://feeds.feedburner.com/~ff/futureadapter?i=4sanTmGIR3w:eE4Q59X2rdU:V_sGLiPBpWU" border="0"></img></a> <a href="http://feeds.feedburner.com/~ff/futureadapter?a=4sanTmGIR3w:eE4Q59X2rdU:qj6IDK7rITs"><img src="http://feeds.feedburner.com/~ff/futureadapter?d=qj6IDK7rITs" border="0"></img></a> <a href="http://feeds.feedburner.com/~ff/futureadapter?a=4sanTmGIR3w:eE4Q59X2rdU:l6gmwiTKsz0"><img src="http://feeds.feedburner.com/~ff/futureadapter?d=l6gmwiTKsz0" border="0"></img></a> <a href="http://feeds.feedburner.com/~ff/futureadapter?a=4sanTmGIR3w:eE4Q59X2rdU:gIN9vFwOqvQ"><img src="http://feeds.feedburner.com/~ff/futureadapter?i=4sanTmGIR3w:eE4Q59X2rdU:gIN9vFwOqvQ" border="0"></img></a> <a href="http://feeds.feedburner.com/~ff/futureadapter?a=4sanTmGIR3w:eE4Q59X2rdU:TzevzKxY174"><img src="http://feeds.feedburner.com/~ff/futureadapter?d=TzevzKxY174" border="0"></img></a>
</div><img src="http://feeds.feedburner.com/~r/futureadapter/~4/4sanTmGIR3w" height="1" width="1"/>]]></content:encoded>
			<wfw:commentRss>http://futureadapter.com/2009/11/13/authlogic-plugin-errorfix/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>prototype-based image preloader</title>
		<link>http://futureadapter.com/2009/10/05/prototype-based-image-preloader/</link>
		<comments>http://futureadapter.com/2009/10/05/prototype-based-image-preloader/#comments</comments>
		<pubDate>Mon, 05 Oct 2009 13:38:58 +0000</pubDate>
		<dc:creator>mattenat</dc:creator>
				<category><![CDATA[Uncategorized]]></category>
		<category><![CDATA[ajax]]></category>
		<category><![CDATA[images]]></category>
		<category><![CDATA[javascript]]></category>
		<category><![CDATA[optimization]]></category>
		<category><![CDATA[prototype]]></category>

		<guid isPermaLink="false">http://futureadapter.com/?p=269</guid>
		<description><![CDATA[Preloading images is often a final optimization step when writing a javascript heavy web app.  I think its generally because its seen as a pita, so it keep getting pushed as the site is developed until you get to the very end, and something has to be re-engineered to make it look right.  [...]]]></description>
			<content:encoded><![CDATA[<p>Preloading images is often a final optimization step when writing a javascript heavy web app.  I think its generally because its seen as a pita, so it keep getting pushed as the site is developed until you get to the very end, and something has to be re-engineered to make it look right.  I think that&#8217;s a shame, because a simple preloader is pretty easy to write and use and can really improve the look &amp; feel of a website.  Here&#8217;s mine:</p>
<pre>
<span class="keyword">var</span> <span class="variable-name">Preloader</span> = {
    <span class="function-name">load</span>: <span class="keyword">function</span>() {
        <span class="keyword">var</span> <span class="variable-name">args</span> = $A(<span class="constant">arguments</span>);
        <span class="keyword">var</span> <span class="variable-name">callback</span> = Object.isFunction(args.last()) ? args.pop() : Prototype.emptyFunction;
        <span class="keyword">var</span> <span class="variable-name">urls</span> = Object.isArray(args[0]) ? $A(args[0]) : args;
        <span class="keyword">var</span> <span class="variable-name">loaded</span> = 0;
        <span class="keyword">var</span> <span class="variable-name">images</span> = $A();

        <span class="keyword">var</span> <span class="variable-name">onload</span> = <span class="keyword">function</span>() {
            <span class="keyword">if</span> (++loaded == urls.length) {
                callback();

                <span class="comment-delimiter">// </span><span class="comment">cleanup
</span>                images.each(<span class="keyword">function</span>(<span class="variable-name">i</span>) { <span class="keyword">delete</span> i });
                images = callback = urls = <span class="constant">null</span>;
            }
        };

        urls.each(<span class="keyword">function</span>(<span class="variable-name">url</span>) {
            <span class="keyword">var</span> <span class="variable-name">image</span> = <span class="keyword">new</span> <span class="type">Image</span>();
            image.onload = image.onerror = onload;
            image.src = url;
            images.push(image);
        });
    }
};
</pre>
<p>To use you load it up with url&#8217;s like:</p>
<pre>
<span class="comment-delimiter">// </span><span class="comment">to load a single image
</span>Preloader.load(<span class="string">'http://futureadapter.com/wp-content/uploads/2008/08/fa_blog3-1.jpg'</span>);

<span class="comment-delimiter">// </span><span class="comment">to load a bunch of images
</span>Preloader.load(<span class="string">'http://futureadapter.com/wp-content/uploads/2008/08/fa_blog3-1.jpg'</span>, <span class="string">'http://futureadapter.com/another_image.jpg'</span>);

<span class="comment-delimiter">// </span><span class="comment">or, alternately, if it works better for loading a bunch of images
</span>Preloader.load([<span class="string">'http://futureadapter.com/wp-content/uploads/2008/08/fa_blog3-1.jpg'</span>, <span class="string">'http://futureadapter.com/another_image.jpg'</span>]);
</pre>
<p>And the final argument can be a callback which executes after all the images have been loaded.  You&#8217;d commonly use this for something like:</p>
<pre>
<span class="comment-delimiter">// </span><span class="comment">show an ajax spinner in your front end over the updating area
</span>$(<span class="string">'target'</span>).startWaiting();

<span class="keyword">new</span> <span class="type">Ajax.Request</span>(<span class="string">'/images'</span>, {
    method: <span class="string">'get'</span>,
    <span class="function-name">onSuccess</span>: <span class="keyword">function</span>(<span class="variable-name">response</span>) {
        <span class="comment-delimiter">// </span><span class="comment">grab the image data from the back end
</span>        <span class="keyword">var</span> <span class="variable-name">images</span> = response.responseJSON[<span class="string">'images'</span>];
        <span class="keyword">var</span> <span class="variable-name">urls</span> = images.invoke(<span class="string">'public_filename'</span>);

        <span class="comment-delimiter">// </span><span class="comment">load up the images
</span>        Preloader.load(urls, <span class="keyword">function</span>() {
            <span class="comment-delimiter">// </span><span class="comment">when all the images have been loaded, replace the
</span>            <span class="comment-delimiter">// </span><span class="comment">existing ones with the new page (for instance)
</span>            $$(<span class="string">'#target .image'</span>).each(<span class="keyword">function</span>(<span class="variable-name">image</span>, <span class="variable-name">index</span>) {
                image.writeAttribute(<span class="string">'src'</span>, urls[index]);
            });

            <span class="comment-delimiter">// </span><span class="comment">and reveal the updated area with the new images
</span>            $(<span class="string">'target'</span>).stopWaiting();
        });
    }
});
</pre>
<p>Have fun!</p>
<div class="feedflare">
<a href="http://feeds.feedburner.com/~ff/futureadapter?a=DvciV5cf-Qw:9sK2Uv80-WM:yIl2AUoC8zA"><img src="http://feeds.feedburner.com/~ff/futureadapter?d=yIl2AUoC8zA" border="0"></img></a> <a href="http://feeds.feedburner.com/~ff/futureadapter?a=DvciV5cf-Qw:9sK2Uv80-WM:F7zBnMyn0Lo"><img src="http://feeds.feedburner.com/~ff/futureadapter?i=DvciV5cf-Qw:9sK2Uv80-WM:F7zBnMyn0Lo" border="0"></img></a> <a href="http://feeds.feedburner.com/~ff/futureadapter?a=DvciV5cf-Qw:9sK2Uv80-WM:V_sGLiPBpWU"><img src="http://feeds.feedburner.com/~ff/futureadapter?i=DvciV5cf-Qw:9sK2Uv80-WM:V_sGLiPBpWU" border="0"></img></a> <a href="http://feeds.feedburner.com/~ff/futureadapter?a=DvciV5cf-Qw:9sK2Uv80-WM:qj6IDK7rITs"><img src="http://feeds.feedburner.com/~ff/futureadapter?d=qj6IDK7rITs" border="0"></img></a> <a href="http://feeds.feedburner.com/~ff/futureadapter?a=DvciV5cf-Qw:9sK2Uv80-WM:l6gmwiTKsz0"><img src="http://feeds.feedburner.com/~ff/futureadapter?d=l6gmwiTKsz0" border="0"></img></a> <a href="http://feeds.feedburner.com/~ff/futureadapter?a=DvciV5cf-Qw:9sK2Uv80-WM:gIN9vFwOqvQ"><img src="http://feeds.feedburner.com/~ff/futureadapter?i=DvciV5cf-Qw:9sK2Uv80-WM:gIN9vFwOqvQ" border="0"></img></a> <a href="http://feeds.feedburner.com/~ff/futureadapter?a=DvciV5cf-Qw:9sK2Uv80-WM:TzevzKxY174"><img src="http://feeds.feedburner.com/~ff/futureadapter?d=TzevzKxY174" border="0"></img></a>
</div><img src="http://feeds.feedburner.com/~r/futureadapter/~4/DvciV5cf-Qw" height="1" width="1"/>]]></content:encoded>
			<wfw:commentRss>http://futureadapter.com/2009/10/05/prototype-based-image-preloader/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>counter_cache as an optimization step</title>
		<link>http://futureadapter.com/2009/10/01/counter_cache-as-an-optimization-step/</link>
		<comments>http://futureadapter.com/2009/10/01/counter_cache-as-an-optimization-step/#comments</comments>
		<pubDate>Thu, 01 Oct 2009 15:06:49 +0000</pubDate>
		<dc:creator>mattenat</dc:creator>
				<category><![CDATA[Uncategorized]]></category>
		<category><![CDATA[activerecord]]></category>
		<category><![CDATA[counter_cache]]></category>
		<category><![CDATA[magic]]></category>
		<category><![CDATA[optimization]]></category>
		<category><![CDATA[performance]]></category>
		<category><![CDATA[rails]]></category>

		<guid isPermaLink="false">http://futureadapter.com/?p=257</guid>
		<description><![CDATA[I had a debate with a programmer friend of mine once about optimization.  I argued that once you get past adding indexes to a relational database, every attempt at increasing its speed or scaling is an act of denormalization.  Whether its double storing information in the database in such a way that it [...]]]></description>
			<content:encoded><![CDATA[<p>I had a debate with a programmer friend of mine once about optimization.  I argued that once you get past adding indexes to a relational database, every attempt at increasing its speed or scaling is an act of denormalization.  Whether its double storing information in the database in such a way that it can be read more efficiently by different queries or its adding in a cacheing layer—be it key-value-esque or page/fragment caching your view layer—it&#8217;s all essentially doing the same thing: making your DBA&#8217;s head explode.</p>
<p>Anyways, Rails has a great mechanism for doing exactly this, and its called <a href="http://railsapi.com/doc/rails-v2.3.4/classes/ActiveRecord/Associations/ClassMethods.html#M004176" target="_blank">counter_cache</a>.  Its an attribute on a has_many/belongs_to association (it&#8217;s actually on the belongs_to) which keeps track of the number of <em>belonging to</em> objects in a count column on the <em>having many</em> table.</p>
<p>In the application I&#8217;m working on there are categories, tags, and images.  Tags and images have a many to many relationship; you can tag an image multiple times and a tag can be re-used on multiple images.  Tags also belong to categories.  When trying to improve the load times of a page, I was immediately struck by the code counting the images for a catogory:</p>
<pre><span class="keyword">class</span> <span class="type">Category</span> &lt; <span class="type">ActiveRecord</span>::<span class="type">Base</span>
  has_many <span class="constant">:tags</span>

  <span class="keyword">def</span> <span class="function-name">image_count</span>
    tags.inject(0) { |tag, memo| memo + tag.images.count }
  <span class="keyword">end</span>
<span class="keyword">end</span></pre>
<p>To find the count for each category, we have to instantiate every single tag object associated with the category and run a separate SQL count for each to grab the images.  And this could be worse depending on what else ActiveRecord decides to load with your tags (maybe you have a default_scope that includes images or an after_find callback).  Worst case, we&#8217;re running 20 SQL queries if there are 20 tags &#8230; and if there are 20 categories, with 20 tags, all on the same page &#8230;</p>
<p>To fix the problem I had to start with the tags and images themselves.  The way they were defined was:</p>
<pre><span class="keyword">class</span> <span class="type">Tag</span> &lt; <span class="type">ActiveRecord</span>::<span class="type">Base</span>
  has_and_belongs_to_many <span class="constant">:images</span>
<span class="keyword">end</span>

<span class="keyword">class</span> <span class="type">Image</span> &lt; <span class="type">ActiveRecord</span>::<span class="type">Base</span>
  has_and_belongs_to_many <span class="constant">:tags</span>
<span class="keyword">end</span></pre>
<p>but I wanted to take advantage of the counter_cache.  Without changing the join table at all, I simply added a model and changed the assocations:</p>
<pre><span class="keyword">class</span> <span class="type">ImagesTag</span> &lt; <span class="type">ActiveRecord</span>::<span class="type">Base</span>
  belongs_to <span class="constant">:image</span>, <span class="constant">:counter_cache</span> =&gt; <span class="variable-name">true</span>
  belongs_to <span class="constant">:tag</span>, <span class="constant">:counter_cache</span> =&gt; <span class="variable-name">true</span>
<span class="keyword">end</span>

<span class="keyword">class</span> <span class="type">Tag</span> &lt; <span class="type">ActiveRecord</span>::<span class="type">Base</span>
  has_many <span class="constant">:images_tags, :dependent =&gt; :destroy</span>
  has_many <span class="constant">:images</span>, <span class="constant">:through</span> =&gt; <span class="constant">:images_tags</span>
<span class="keyword">end</span>

<span class="keyword">class</span> <span class="type">Image</span> &lt; <span class="type">ActiveRecord</span>::<span class="type">Base</span>
  has_many <span class="constant">:images_tags, :dependent =&gt; :destroy</span>
  has_many <span class="constant">:tags</span>, <span class="constant">:through</span> =&gt; <span class="constant">:images_tags</span>
<span class="keyword">end</span></pre>
<p>and with the following migration:</p>
<pre><span class="keyword">class</span> <span class="type">AddCounterCachesToImagesAndTags</span> &lt; <span class="type">ActiveRecord</span>::<span class="type">Migration</span>
  <span class="keyword">def</span> <span class="function-name">self.up</span>
    add_column <span class="constant">:images</span>, <span class="constant">:tag_count</span>, <span class="constant">:integer</span>, <span class="constant">:default</span> =&gt; 0
    add_column <span class="constant">:tags</span>, <span class="constant">:image_count</span>, <span class="constant">:integer</span>, <span class="constant">:default</span> =&gt; 0

    <span class="comment-delimiter"># </span><span class="comment">this is necessary because in production w/ cache_classes on the
</span>    <span class="comment-delimiter"># </span><span class="comment">definition of the Image and Tag objects won't reload to reflect
</span>    <span class="comment-delimiter"># </span><span class="comment">the new columns unless you do it explicitly
</span>    <span class="type">Image</span>.reset_column_information
    <span class="type">Tag</span>.reset_column_information

    <span class="type">Image</span>.all.each <span class="keyword">do</span> |image|
      <span class="type">Image</span>.update_counters image.id, <span class="constant">:tag_count</span> =&gt; image.tags.count
    <span class="keyword">end</span>

    <span class="type">Tag</span>.all.each <span class="keyword">do</span> |tag|
      <span class="type">Tag</span>.update_counters tag.id, <span class="constant">:image_count</span> =&gt; tag.images.count
    <span class="keyword">end</span>

    add_column <span class="constant">:images_tags</span>, <span class="constant">:id</span>, <span class="constant">:integer</span> <span class="keyword">unless</span> columns.detect { |column| column.name == <span class="string">'id'</span> }
  <span class="keyword">end</span>

  <span class="keyword">def</span> <span class="function-name">self.down</span>
    remove_column <span class="constant">:images</span>, <span class="constant">:tag_count</span>
    remove_column <span class="constant">:tags</span>, <span class="constant">:image_count</span>
    remove_column <span class="constant">:images_tags</span>, <span class="constant">:id</span> <span class="keyword">if</span> columns.detect { |column| column.name == <span class="string">'id'</span> }
  <span class="keyword">end</span>
<span class="keyword">end</span></pre>
<p>I now had denormalized image_count and tag_count columns in my database available as attributes on objects (which I definitely needed in my view anyways).</p>
<p>To take advantage of this data for categories, I rewrote the image_count method:</p>
<pre><span class="keyword">class</span> <span class="type">Category</span> &lt; <span class="type">ActiveRecord</span>::<span class="type">Base</span>
  has_many <span class="constant">:tags</span>

  <span class="keyword">def</span> <span class="function-name">image_count</span>
    tags.sum(<span class="constant">:image_count</span>)
  <span class="keyword">end</span>
<span class="keyword">end</span></pre>
<p>Now no matter how many tags belong to a category and regardless of any extra loading a tag might do, there are no extra objects instantiated by the single SQL query to find the number of images belonging to a category.</p>
<p>Rails does all the work to keep these count columns in sync without any extra effort on my part.  It tries (hard enough in most cases) to keep the overhead as low as possible using SQL without going through ActiveRecord, which is nice because you don&#8217;t have to put that SQL straight into your model.</p>
<p>There&#8217;s lots of built-in magic in Rails and unless you happen to be on top of new code being pushed into Edge (and there are <a href="http://ryandaigle.com/tags/Edge%20Rails" target="_blank">great</a> <a href="http://weblog.rubyonrails.org/edge" target="_blank">blogs</a> to help!), you can miss it and never even know it exists.  This isn&#8217;t new voodoo, its been around since at least Rails 2.0, perhaps earlier.  If it slid past your radar, this is a bit of magic that&#8217;s easy to use and easy to add onto an already working application as an optimization step.  Just don&#8217;t tell the DBA.</p>
<p>Hat-tip to the <a href="http://railscasts.com/" target="_blank">railscasts</a> guys for <a href="http://railscasts.com/episodes/23-counter-cache-column" target="_blank">the migration syntax</a>.  You should also check out their <a href="http://railscasts.com/episodes/23-counter-cache-column" target="_blank">counter cache column screencast</a> for more info.</p>
<p><strong>UPDATE: </strong>I made <a href="http://futureadapter.com/2009/10/01/counter_cache-as-an-optimization-step/comment-page-1/#comment-54" target="_self">all the changes suggested by Rain</a> in the comments.</p>
<div class="feedflare">
<a href="http://feeds.feedburner.com/~ff/futureadapter?a=iA-bWm6rsLY:o5wivqpXTLI:yIl2AUoC8zA"><img src="http://feeds.feedburner.com/~ff/futureadapter?d=yIl2AUoC8zA" border="0"></img></a> <a href="http://feeds.feedburner.com/~ff/futureadapter?a=iA-bWm6rsLY:o5wivqpXTLI:F7zBnMyn0Lo"><img src="http://feeds.feedburner.com/~ff/futureadapter?i=iA-bWm6rsLY:o5wivqpXTLI:F7zBnMyn0Lo" border="0"></img></a> <a href="http://feeds.feedburner.com/~ff/futureadapter?a=iA-bWm6rsLY:o5wivqpXTLI:V_sGLiPBpWU"><img src="http://feeds.feedburner.com/~ff/futureadapter?i=iA-bWm6rsLY:o5wivqpXTLI:V_sGLiPBpWU" border="0"></img></a> <a href="http://feeds.feedburner.com/~ff/futureadapter?a=iA-bWm6rsLY:o5wivqpXTLI:qj6IDK7rITs"><img src="http://feeds.feedburner.com/~ff/futureadapter?d=qj6IDK7rITs" border="0"></img></a> <a href="http://feeds.feedburner.com/~ff/futureadapter?a=iA-bWm6rsLY:o5wivqpXTLI:l6gmwiTKsz0"><img src="http://feeds.feedburner.com/~ff/futureadapter?d=l6gmwiTKsz0" border="0"></img></a> <a href="http://feeds.feedburner.com/~ff/futureadapter?a=iA-bWm6rsLY:o5wivqpXTLI:gIN9vFwOqvQ"><img src="http://feeds.feedburner.com/~ff/futureadapter?i=iA-bWm6rsLY:o5wivqpXTLI:gIN9vFwOqvQ" border="0"></img></a> <a href="http://feeds.feedburner.com/~ff/futureadapter?a=iA-bWm6rsLY:o5wivqpXTLI:TzevzKxY174"><img src="http://feeds.feedburner.com/~ff/futureadapter?d=TzevzKxY174" border="0"></img></a>
</div><img src="http://feeds.feedburner.com/~r/futureadapter/~4/iA-bWm6rsLY" height="1" width="1"/>]]></content:encoded>
			<wfw:commentRss>http://futureadapter.com/2009/10/01/counter_cache-as-an-optimization-step/feed/</wfw:commentRss>
		<slash:comments>3</slash:comments>
		</item>
		<item>
		<title>find_by_param for permalinks</title>
		<link>http://futureadapter.com/2009/08/28/find_by_param-for-permalinks/</link>
		<comments>http://futureadapter.com/2009/08/28/find_by_param-for-permalinks/#comments</comments>
		<pubDate>Fri, 28 Aug 2009 22:54:53 +0000</pubDate>
		<dc:creator>mattenat</dc:creator>
				<category><![CDATA[Uncategorized]]></category>
		<category><![CDATA[find_by_param]]></category>
		<category><![CDATA[permalinks]]></category>
		<category><![CDATA[plugin]]></category>
		<category><![CDATA[props]]></category>
		<category><![CDATA[rails]]></category>
		<category><![CDATA[rubyonrails]]></category>

		<guid isPermaLink="false">http://futureadapter.com/?p=247</guid>
		<description><![CDATA[A nice, clear, declarative means of doing something I&#8217;ve been writing code to do for years &#8230; find_by_param
From the README:

Find_by_param helps you dealing with permalinks and finding objects by our permalink value
class Post &#60; ActiveRecord:Base
&#160;&#160;make_permalink :with =&#62; :title
end
now you can do Post.find_by_param(...)
If you have a permalink-column find_by_param saves the permalink there and uses that otherwise it [...]]]></description>
			<content:encoded><![CDATA[<p>A nice, clear, declarative means of doing something I&#8217;ve been writing code to do for years &#8230; <a href="http://github.com/bumi/find_by_param/tree" target="_blank">find_by_param</a></p>
<p>From the README:</p>
<blockquote><p><code><br />
Find_by_param helps you dealing with permalinks and finding objects by our permalink value</code></p>
<p><code>class Post &lt; ActiveRecord:Base<br />
&nbsp;&nbsp;make_permalink :with =&gt; :title<br />
end</code></p>
<p><code>now you can do Post.find_by_param(...)</p>
<p></code><code>If you have a permalink-column find_by_param saves the permalink there and uses that otherwise it just uses the provided<br />
attribute.<br />
</code></p></blockquote>
<p>I love small and simple plugins.  Thanks to <a href="http://github.com/bumi" target="_blank">Michael Bumann</a>.  Great job!</p>
<div class="feedflare">
<a href="http://feeds.feedburner.com/~ff/futureadapter?a=-bwmDZqNQ54:yxVM5OmVOdo:yIl2AUoC8zA"><img src="http://feeds.feedburner.com/~ff/futureadapter?d=yIl2AUoC8zA" border="0"></img></a> <a href="http://feeds.feedburner.com/~ff/futureadapter?a=-bwmDZqNQ54:yxVM5OmVOdo:F7zBnMyn0Lo"><img src="http://feeds.feedburner.com/~ff/futureadapter?i=-bwmDZqNQ54:yxVM5OmVOdo:F7zBnMyn0Lo" border="0"></img></a> <a href="http://feeds.feedburner.com/~ff/futureadapter?a=-bwmDZqNQ54:yxVM5OmVOdo:V_sGLiPBpWU"><img src="http://feeds.feedburner.com/~ff/futureadapter?i=-bwmDZqNQ54:yxVM5OmVOdo:V_sGLiPBpWU" border="0"></img></a> <a href="http://feeds.feedburner.com/~ff/futureadapter?a=-bwmDZqNQ54:yxVM5OmVOdo:qj6IDK7rITs"><img src="http://feeds.feedburner.com/~ff/futureadapter?d=qj6IDK7rITs" border="0"></img></a> <a href="http://feeds.feedburner.com/~ff/futureadapter?a=-bwmDZqNQ54:yxVM5OmVOdo:l6gmwiTKsz0"><img src="http://feeds.feedburner.com/~ff/futureadapter?d=l6gmwiTKsz0" border="0"></img></a> <a href="http://feeds.feedburner.com/~ff/futureadapter?a=-bwmDZqNQ54:yxVM5OmVOdo:gIN9vFwOqvQ"><img src="http://feeds.feedburner.com/~ff/futureadapter?i=-bwmDZqNQ54:yxVM5OmVOdo:gIN9vFwOqvQ" border="0"></img></a> <a href="http://feeds.feedburner.com/~ff/futureadapter?a=-bwmDZqNQ54:yxVM5OmVOdo:TzevzKxY174"><img src="http://feeds.feedburner.com/~ff/futureadapter?d=TzevzKxY174" border="0"></img></a>
</div><img src="http://feeds.feedburner.com/~r/futureadapter/~4/-bwmDZqNQ54" height="1" width="1"/>]]></content:encoded>
			<wfw:commentRss>http://futureadapter.com/2009/08/28/find_by_param-for-permalinks/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>Handling ajax-y file uploads</title>
		<link>http://futureadapter.com/2009/08/18/handling-ajax-y-file-uploads/</link>
		<comments>http://futureadapter.com/2009/08/18/handling-ajax-y-file-uploads/#comments</comments>
		<pubDate>Tue, 18 Aug 2009 16:01:31 +0000</pubDate>
		<dc:creator>mattenat</dc:creator>
				<category><![CDATA[Uncategorized]]></category>
		<category><![CDATA[ajax]]></category>
		<category><![CDATA[file uploads]]></category>
		<category><![CDATA[forms]]></category>
		<category><![CDATA[rails]]></category>
		<category><![CDATA[responds_to_parent]]></category>
		<category><![CDATA[rubyonrails]]></category>

		<guid isPermaLink="false">http://futureadapter.com/?p=210</guid>
		<description><![CDATA[I&#8217;m working on a site for a client where a user needs to upload a logo for their newly created widget.  The client wants the logo to be visible during the widget creation process (so the user can make sure the logo they&#8217;re uploading is correct).  A user can add multiple widgets all at the [...]]]></description>
			<content:encoded><![CDATA[<p>I&#8217;m working on a site for a client where a user needs to upload a logo for their newly created widget.  The client wants the logo to be visible during the widget creation process (so the user can make sure the logo they&#8217;re uploading is correct).  A user can add multiple widgets all at the same time.</p>
<p>I discovered a slightly different strategy for implementing this functionality and as its pretty common, something I&#8217;ve done before and I&#8217;m kinda pleased by the new strategy, I thought I&#8217;d share.</p>
<p><strong>responds_to_parent</strong></p>
<p>A lot of posts exist on the web about how to do file uploads via ajax.  Short answer is, you can&#8217;t.  There are lots of work-arounds to make it look like that&#8217;s exactly what you&#8217;re doing, though.  I&#8217;ve been using <a href="http://github.com/markcatley/responds_to_parent/tree/master" target="_blank">responds_to_parent</a> for years now.  It&#8217;s pretty rad.  Excerpted from its readme:</p>
<blockquote><p>Adds responds_to_parent to your controller to respond to the parent document of your page.  Make Ajaxy file uploads by posting the form to a hidden iframe, and respond with RJS to the parent window.</p></blockquote>
<p>Basically what happens is that you create a hidden iframe on the page which is the target of a form submission with a file upload.  responds_to_parent executes the javascript returned to the browser via the form submission (and going into the hidden iframe) in the context of the iframe containing page.  Typically, this bit of javascript is where you update the old logo with the newly uploaded one.</p>
<p><strong>links to other articles w/ explanations</strong></p>
<p>I don&#8217;t want to re-cover a lot of previously covered ground, so here&#8217;s a collection of links explaining the basics.  The piece I want to concentrate on, is a good way of handling the problem of page flow and separate forms which cannot be embedded, which is beyond the scope of these links.</p>
<ul>
<li><a href="http://khamsouk.souvanlasy.com/2007/5/1/ajax-file-uploads-in-rails-using-attachment_fu-and-responds_to_parent" target="_blank">http://khamsouk.souvanlasy.com/2007/5/1/ajax-file-uploads-in-rails-using-attachment_fu-and-responds_to_parent</a></li>
<li><a href="http://kpumuk.info/ruby-on-rails/in-place-file-upload-with-ruby-on-rails/" target="_blank">http://kpumuk.info/ruby-on-rails/in-place-file-upload-with-ruby-on-rails/</a></li>
</ul>
<p>Incidentally, here&#8217;s another solution using jQuery for the hacky part and not responds_to_parent.</p>
<ul>
<li><a href="http://blog.edgecase.com/2009/6/15/ajax-file-uploads-made-easy-screencast" target="_blank">http://blog.edgecase.com/2009/6/15/ajax-file-uploads-made-easy-screencast</a></li>
</ul>
<p><strong>two schemes</strong></p>
<p>Ok, so we have our controllers and models and responds_to_parent all setup and ready to go, but now we have a problem.  In every page design I&#8217;ve seen that demands this functionality, the file upload&#8211;in this case the logo attaching&#8211;is done in the middle of a form with all sorts of other values that need to be filled out.  This makes sense from a UI point of view, but isn&#8217;t possible in a HTML form sense.  Because you can&#8217;t have a form inside another form (what would the submit button be submitting?), you can&#8217;t simply put your file upload form where it belongs on the page.</p>
<p>In the past my solution has been to put the file upload form where it belongs page-flow wise and then instead of putting all the other form values inside a form, they&#8217;re simply unattached inputs.  Then I create a form at the bottom of the page inside a hidden div which contains all the fields that I want to submit.  I name the visible, unattached inputs by a standard scheme and then I bind to the click event of the submit button such that all of values are copied from the visible, unattached inputs to the invisible real form, which is what gets submitted.</p>
<p>Here&#8217;s some example javascript which explains the theory:</p>
<pre>CopyValuesForm = Class.create({
    FIELDS = $w(<span class="string">'first_name last_name street_address city state zip'</span>),

    <span class="function-name">initialize</span>: <span class="keyword">function</span>() {
        <span class="keyword">this</span>.targetForm = $(<span class="string">'target-form'</span>);

        $(<span class="string">'fake-submit'</span>).observe(<span class="string">'click'</span>,
                                 <span class="keyword">this</span>.submit.bindAsEventListener(<span class="keyword">this</span>));
    },

    <span class="function-name">submit</span>: <span class="keyword">function</span>(<span class="variable-name">event</span>) {
        event.stop();

        <span class="keyword">this</span>.FIELDS.each(<span class="keyword">function</span>(<span class="variable-name">field</span>) {
            <span class="keyword">this</span>.realField(field).setValue = $F(<span class="keyword">this</span>.fakeField(field));
        }.bind(<span class="keyword">this</span>));

        <span class="keyword">this</span>.targetForm.submit();
    },

    <span class="function-name">fakeField</span>: <span class="keyword">function</span>(<span class="variable-name">field</span>) {
        <span class="keyword">return</span> $(<span class="string">'fake_'</span> + field);
    },

    <span class="function-name">realField</span>: <span class="keyword">function</span>(<span class="variable-name">field</span>) {
        <span class="keyword">return</span> $(<span class="string">'widgets['</span> + field <span class="string">']'</span>);
    }
});</pre>
<p>This works and isn&#8217;t a bad solution.  Its kinda annoying that you have to copy lots and lots of values instead of copying just the file-to-be-uploaded value, but file upload values can&#8217;t be copied for security reasons via javascript.  It also stinks because your unattached inputs don&#8217;t play nicely with all of Rails built-in form wizardry like hooking in the error message into the fields.  You can overcome all of this, but its with more javascript, some of which has to be customized per-field.</p>
<p>But there&#8217;s another solution which obviates the need for copying values and allows you to use built-in Rails form magic to its fullest.</p>
<p><strong>moving the display instead of the form</strong></p>
<p>In this scheme we move the file upload form to the bottom of the page and keep the normal form intact.  We use javascript to change where the file upload form is displayed in the page instead of copying the values.</p>
<p>First the javascript:</p>
<pre><span class="keyword">var</span> <span class="variable-name">ContainingObject</span> = Class.create({
    <span class="function-name">removeWidget</span>: <span class="keyword">function</span>(<span class="variable-name">event</span>) {
        event.stop();
        <span class="keyword">var</span> <span class="variable-name">widget_container</span> = event.element().up(<span class="string">'.widget'</span>);
        <span class="keyword">var</span> <span class="variable-name">widget_id</span> = widget_container.id;

        <span class="keyword">new</span> Ajax.Request(<span class="string">'/containing_objects/1/widgets/2'</span>, {
            method: <span class="string">'delete'</span>,
            <span class="function-name">onSuccess</span>: <span class="keyword">function</span>(<span class="variable-name">transport</span>) {
                <span class="comment">// we no longer contain this widget in the ui, so get rid of it
</span>                <span class="keyword">this</span>.widgets.unset(widget_id).remove();
                <span class="comment">// reposition the remaining forms
</span>                <span class="keyword">this</span>.widgets.values().invoke(<span class="string">'positionUploadForm'</span>);
            }.bind(<span class="keyword">this</span>)
        });
    }
});

<span class="keyword">var</span> <span class="variable-name">Widget</span> = Class.create({
    <span class="function-name">initialize</span>: <span class="keyword">function</span>(<span class="variable-name">container</span>) {
        <span class="keyword">this</span>.container = container;
        <span class="keyword">this</span>.dom_id = container.id;
        <span class="keyword">this</span>.positionUploadForm();
    },

    <span class="function-name">remove</span>: <span class="keyword">function</span>() {
        <span class="keyword">this</span>.uploadForm().remove();
        <span class="keyword">this</span>.container.remove();
    },

    <span class="function-name">uploadForm</span>: <span class="keyword">function</span>() {
        <span class="keyword">return</span> $(<span class="string">'widget-form-'</span> + <span class="keyword">this</span>.dom_id);
    },

    <span class="function-name">positionUploadForm</span>: <span class="keyword">function</span>() {
        <span class="keyword">var</span> <span class="variable-name">form</span> = <span class="keyword">this</span>.uploadForm();
        form.absolutize();
        form.clonePosition(<span class="keyword">this</span>.container.down(<span class="string">'.form-goes-here'</span>));
    }
});</pre>
<p>There&#8217;s of course elided logic there for adding widgets, creating widget objects, etc.  The important pieces to note are that the form is positioned as per the container (.form-goes-here) on initialization and also on removal of any other widget.  It occurs to me writing this, that it would be an excellent application for an event-based setup whereby a widget:remove event is fired from the Widget object (in the remove method) upon its ContainingObject.  All Widgets could observe their ContainingObject for this event.  Anyways &#8230; the html/css is pretty rote.  Simply put a div in the normal flow of the form:</p>
<pre>&lt;<span class="function-name">div</span> <span class="variable-name">class</span>=<span class="string">".form-goes-here"</span>&gt;&lt;/<span class="function-name">div</span>&gt;</pre>
<p>and make sure you specify that its large enough for the form which will be cloned on top of it in your css:</p>
<pre>.form-goes-here {
        <span class="type">width</span>: 200px;
        <span class="type">height</span>: 100px;
}</pre>
<p><strong>cute work with content_for, yield</strong></p>
<p>You&#8217;d want to define the file upload form in the same partial that contains the rest of your form fields (to be shared between the new and edit views) even though you actually want it to spit out into html at the bottom of the page.  That&#8217;s exactly what content_for was designed to do.</p>
<pre><span class="mumamo-border"><span class="mumamo-background-chunk-submode">&lt;% content_for :upload_form do -%&gt;</span></span>
&lt;<span class="function-name">div</span> <span class="variable-name">class</span>=<span class="string">"logo-form-container"</span>&gt;
  <span class="mumamo-border"><span class="mumamo-background-chunk-submode">&lt;% form_for :logo, :url =&gt; logos_path, :html =&gt; { :multipart =&gt; true, :target =&gt; 'upload-frame', :id =&gt; "widget-form-#{dom_id(widget)}" } do |f| -%&gt;</span></span>
  &lt;<span class="function-name">p</span>&gt;
  <span class="mumamo-border"><span class="mumamo-background-chunk-submode">&lt;%= f.label :uploaded_data, 'Logo file' %&gt;</span></span>&lt;<span class="function-name">br</span>/&gt;
  <span class="mumamo-border"><span class="mumamo-background-chunk-submode">&lt;%= f.file_field :uploaded_data %&gt;</span></span>
  &lt;/<span class="function-name">p</span>&gt;
  &lt;<span class="function-name">p</span>&gt;
  <span class="mumamo-border"><span class="mumamo-background-chunk-submode">&lt;%= f.submit 'Upload image' %&gt;</span></span>
  &lt;/<span class="function-name">p</span>&gt;
<span class="mumamo-border"><span class="mumamo-background-chunk-submode">&lt;% end -%&gt;</span></span>
&lt;/<span class="function-name">div</span>&gt;
<span class="mumamo-border"><span class="mumamo-background-chunk-submode">&lt;% end -%&gt;</span></span></pre>
<p>and then in your new/edit.html.erb files:</p>
<pre><span class="mumamo-border"><span class="mumamo-background-chunk-submode">&lt;%= yield :upload_form %&gt;</span></span>
&lt;<span class="function-name">iframe</span> <span class="variable-name">id</span>=<span class="string">"upload-frame"</span> <span class="variable-name">name</span>=<span class="string">"upload-frame"</span> <span class="variable-name">border</span>=<span class="string">"0"</span> <span class="variable-name">width</span>=<span class="string">"0"</span> <span class="variable-name">height</span>=<span class="string">"0"</span> <span class="mumamo-border"><span class="mumamo-background-chunk-submode">style="display:none;"</span></span>&gt;&lt;/<span class="function-name">iframe</span>&gt;</pre>
<p>I also discovered this somewhat cute way of getting the file upload form into the page even when there&#8217;s some ajax to add another form.  The use-case here is that your page uses Ajax to add another widget and bring along all the form fields (including the logo) which need to be submitted.  In the new.js.rjs:</p>
<pre>page.insert_html <span class="constant">:bottom</span>, <span class="string">"widgets-container"</span>, <span class="constant">:partial</span> =&gt; <span class="variable-name">@widget</span>
page.insert_html <span class="constant">:before</span>, <span class="string">"upload-frame"</span>, <span class="keyword">yield</span>(<span class="constant">:upload_form</span>)</pre>
<p><strong>summing it up</strong></p>
<p><strong></strong>So we know there can&#8217;t be embedded forms and we know that we have to do some shenanigans for Ajax looking file uploads.  Instead of copying the values from all of the fields in one form into a hidden form, we can position the upload form on the page so it appears to flow normally without actually being embedded.  This is simpler and allows us to rely more on the built-in Rails form support and error messages.</p>
<p>Thanks to <a href="http://github.com/markcatley" target="_blank">Mark Catley</a> for the <a href="http://github.com/markcatley/responds_to_parent/tree/master" target="_blank">great plugin</a> I&#8217;ve used so many times over the years.</p>
<div class="feedflare">
<a href="http://feeds.feedburner.com/~ff/futureadapter?a=fT2bLiooId4:JLdJluWlELw:yIl2AUoC8zA"><img src="http://feeds.feedburner.com/~ff/futureadapter?d=yIl2AUoC8zA" border="0"></img></a> <a href="http://feeds.feedburner.com/~ff/futureadapter?a=fT2bLiooId4:JLdJluWlELw:F7zBnMyn0Lo"><img src="http://feeds.feedburner.com/~ff/futureadapter?i=fT2bLiooId4:JLdJluWlELw:F7zBnMyn0Lo" border="0"></img></a> <a href="http://feeds.feedburner.com/~ff/futureadapter?a=fT2bLiooId4:JLdJluWlELw:V_sGLiPBpWU"><img src="http://feeds.feedburner.com/~ff/futureadapter?i=fT2bLiooId4:JLdJluWlELw:V_sGLiPBpWU" border="0"></img></a> <a href="http://feeds.feedburner.com/~ff/futureadapter?a=fT2bLiooId4:JLdJluWlELw:qj6IDK7rITs"><img src="http://feeds.feedburner.com/~ff/futureadapter?d=qj6IDK7rITs" border="0"></img></a> <a href="http://feeds.feedburner.com/~ff/futureadapter?a=fT2bLiooId4:JLdJluWlELw:l6gmwiTKsz0"><img src="http://feeds.feedburner.com/~ff/futureadapter?d=l6gmwiTKsz0" border="0"></img></a> <a href="http://feeds.feedburner.com/~ff/futureadapter?a=fT2bLiooId4:JLdJluWlELw:gIN9vFwOqvQ"><img src="http://feeds.feedburner.com/~ff/futureadapter?i=fT2bLiooId4:JLdJluWlELw:gIN9vFwOqvQ" border="0"></img></a> <a href="http://feeds.feedburner.com/~ff/futureadapter?a=fT2bLiooId4:JLdJluWlELw:TzevzKxY174"><img src="http://feeds.feedburner.com/~ff/futureadapter?d=TzevzKxY174" border="0"></img></a>
</div><img src="http://feeds.feedburner.com/~r/futureadapter/~4/fT2bLiooId4" height="1" width="1"/>]]></content:encoded>
			<wfw:commentRss>http://futureadapter.com/2009/08/18/handling-ajax-y-file-uploads/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>authlogic_oauth gotcha</title>
		<link>http://futureadapter.com/2009/07/30/authlogic_oauth-gotcha/</link>
		<comments>http://futureadapter.com/2009/07/30/authlogic_oauth-gotcha/#comments</comments>
		<pubDate>Thu, 30 Jul 2009 16:08:57 +0000</pubDate>
		<dc:creator>mattenat</dc:creator>
				<category><![CDATA[Uncategorized]]></category>
		<category><![CDATA[authentication]]></category>
		<category><![CDATA[authlogic]]></category>
		<category><![CDATA[authlogic_oauth]]></category>
		<category><![CDATA[oauth]]></category>
		<category><![CDATA[rails]]></category>
		<category><![CDATA[rubyonrails]]></category>
		<category><![CDATA[twitter]]></category>

		<guid isPermaLink="false">http://futureadapter.com/?p=194</guid>
		<description><![CDATA[I don&#8217;t want to imply by the title this is anyone&#8217;s fault but my own.  But since it took me two days to figure out, I thought I&#8217;d share.
I&#8217;m using the excellent authentication package, authlogic, for my current project.  I want to allow normal registrations through my site and also registrations using Twitter credentials.  In [...]]]></description>
			<content:encoded><![CDATA[<p>I don&#8217;t want to imply by the title this is anyone&#8217;s fault but my own.  But since it took me two days to figure out, I thought I&#8217;d share.</p>
<p>I&#8217;m using the excellent authentication package, <a href="http://github.com/binarylogic/authlogic/tree/master" target="_blank">authlogic</a>, for my current project.  I want to allow normal registrations through my site and also registrations using Twitter credentials.  In an incremental way, I first implemented normal registrations.  I followed the <a href="http://rdoc.info/projects/binarylogic/authlogic" target="_blank">authlogic documentation</a> and liberally copied &amp; pasted from the <a href="http://github.com/binarylogic/authlogic_example/tree/master" target="_blank">authlogic_example</a> source tree.  One quick note here, authlogic is very well documented!</p>
<p>With no problems, registrations on my site worked.  Then I moved on to oauth/Twitter registrations.  Once again I read the <a href="http://rdoc.info/projects/jrallison/authlogic_oauth" target="_blank">documentation</a> and followed the <a href="http://github.com/jrallison/authlogic_example/tree/with-oauth" target="_blank">example code</a>.  Once again, it was excellently documented.  This time, though, I fell into a two-day hole of frustration.</p>
<p>This was the error, cause of consternation, and some of the backtrace:</p>
<pre>Processing UsersController#create (for 74.98.231.174 at 2009-07-29 15:05:11) [POST]
  Parameters: {"action"=&gt;"create", "controller"=&gt;"users", "oauth_token"=&gt;"NX6WgsvgPiyIJWnHdibE4l23vYnrJo4XztZdzn24mM", "oauth_verifier"=&gt;"S0EUR49GOYRtZ9IFCpXMBHCh4n3eedcnka8sdd7vdM"}
  User Columns (2.3ms)   SHOW FIELDS FROM `users`
  User Load (0.4ms)   SELECT * FROM `users` WHERE (`users`.`persistence_token` = '...') LIMIT 1
  User Load (0.5ms)   SELECT * FROM `users` WHERE (`users`.`oauth_token` = '14934375-oOOgpW5hXiONW6NM4yD9PHlkhAJyHmbVyeLwUz5R4') LIMIT 1
  SQL (0.2ms)   BEGIN
  SQL (0.2ms)   ROLLBACK

<span class="function-name">OAuth</span>::Unauthorized (401 Unauthorized):
  oauth (0.3.5) [v] lib/oauth/consumer.rb:197:in `token_request'
  oauth (0.3.5) [v] lib/oauth/tokens/request_token.rb:18:in `get_access_token'
<span class="underline">  </span><span class="underline"><span class="compilation-error">vendor/gems/authlogic-oauth-1.0.7/lib/authlogic_oauth/oauth_process.rb</span></span><span class="underline">:</span><span class="underline"><span class="compilation-line-number">42</span></span><span class="underline">:</span>in `generate_access_token'
<span class="underline">  </span><span class="underline"><span class="compilation-error">vendor/gems/authlogic-oauth-1.0.7/lib/authlogic_oauth/acts_as_authentic.rb</span></span><span class="underline">:</span><span class="underline"><span class="compilation-line-number">93</span></span><span class="underline">:</span>in `authenticate_with_oauth'
<span class="underline">  </span><span class="underline"><span class="compilation-error">vendor/gems/authlogic-oauth-1.0.7/lib/authlogic_oauth/oauth_process.rb</span></span><span class="underline">:</span><span class="underline"><span class="compilation-line-number">12</span></span><span class="underline">:</span>in `validate_by_oauth'
<span class="underline">  </span><span class="underline"><span class="compilation-error">vendor/gems/authlogic-oauth-1.0.7/lib/authlogic_oauth/acts_as_authentic.rb</span></span><span class="underline">:</span><span class="underline"><span class="compilation-line-number">67</span></span><span class="underline">:</span>in `save'
<span class="underline">  </span><span class="underline"><span class="compilation-error">app/controllers/users_controller.rb</span></span><span class="underline">:</span><span class="underline"><span class="compilation-line-number">20</span></span><span class="underline">:</span>in `create'
<span class="underline">  </span><span class="underline"><span class="compilation-error">vendor/gems/authlogic-oauth-1.0.7/lib/oauth_callback_filter.rb</span></span><span class="underline">:</span><span class="underline"><span class="compilation-line-number">10</span></span><span class="underline">:</span>in `call'</pre>
<p>Unauthorized was quite frustrating as my error because Twitter, to the eye, was certainly authorizing my request.  Also, when the process was tested on the command line via oauth directly, I had no issues.</p>
<p>It turns out the problem was in a before_filter which verified that there was no user in the session when creating a new user (as was suggested by the authlogic_example code for a totally sensible reason).  What I believe was happening as a result of this was that the callback return from Twitter including an oauth_token and oauth_verifier was then being redirected again because of the user in the session whereby another call was attempted of Twitter using the oauth_token that was a param.  Anyways, its not terribly important except to say that it completely broke with an error that didn&#8217;t help me decipher the problem.</p>
<p>When I changed the actions for which my before_filter applied, the problem was solved.</p>
<pre><span class="keyword">class</span> <span class="type">UsersController</span> &lt; <span class="type">ApplicationController</span>
  <span class="comment-delimiter"># </span><span class="comment">authlogic_example: THIS ONE BREAKS OAUTH
</span><span class="comment-delimiter">#   </span><span class="comment">before_filter :require_no_user, :only =&gt; [:new, :create]
</span>  <span class="comment-delimiter"># </span><span class="comment">authlogic_example (oauth branch): THIS ONE WORKS
</span>  before_filter <span class="constant">:require_no_user</span>, <span class="constant">:only</span> =&gt; [<span class="constant">:new</span>]</pre>
<p>Hopefully my two day of flailing helps someone else out in the same position.  I hate when this happens.</p>
<p>Thanks again to the authors of <a href="http://github.com/binarylogic/authlogic/tree/master" target="_blank">authlogic</a> and <a href="http://github.com/jrallison/authlogic_oauth/tree/master" target="_blank">authlogic_oauth</a> for writing some great code and documentation.  I really want to stress that this was my fault, not theirs.  Hopefully this lesson will make it into the documentaiton.</p>
<div class="feedflare">
<a href="http://feeds.feedburner.com/~ff/futureadapter?a=2G9SEIfTyJI:86tfyyZb1_Q:yIl2AUoC8zA"><img src="http://feeds.feedburner.com/~ff/futureadapter?d=yIl2AUoC8zA" border="0"></img></a> <a href="http://feeds.feedburner.com/~ff/futureadapter?a=2G9SEIfTyJI:86tfyyZb1_Q:F7zBnMyn0Lo"><img src="http://feeds.feedburner.com/~ff/futureadapter?i=2G9SEIfTyJI:86tfyyZb1_Q:F7zBnMyn0Lo" border="0"></img></a> <a href="http://feeds.feedburner.com/~ff/futureadapter?a=2G9SEIfTyJI:86tfyyZb1_Q:V_sGLiPBpWU"><img src="http://feeds.feedburner.com/~ff/futureadapter?i=2G9SEIfTyJI:86tfyyZb1_Q:V_sGLiPBpWU" border="0"></img></a> <a href="http://feeds.feedburner.com/~ff/futureadapter?a=2G9SEIfTyJI:86tfyyZb1_Q:qj6IDK7rITs"><img src="http://feeds.feedburner.com/~ff/futureadapter?d=qj6IDK7rITs" border="0"></img></a> <a href="http://feeds.feedburner.com/~ff/futureadapter?a=2G9SEIfTyJI:86tfyyZb1_Q:l6gmwiTKsz0"><img src="http://feeds.feedburner.com/~ff/futureadapter?d=l6gmwiTKsz0" border="0"></img></a> <a href="http://feeds.feedburner.com/~ff/futureadapter?a=2G9SEIfTyJI:86tfyyZb1_Q:gIN9vFwOqvQ"><img src="http://feeds.feedburner.com/~ff/futureadapter?i=2G9SEIfTyJI:86tfyyZb1_Q:gIN9vFwOqvQ" border="0"></img></a> <a href="http://feeds.feedburner.com/~ff/futureadapter?a=2G9SEIfTyJI:86tfyyZb1_Q:TzevzKxY174"><img src="http://feeds.feedburner.com/~ff/futureadapter?d=TzevzKxY174" border="0"></img></a>
</div><img src="http://feeds.feedburner.com/~r/futureadapter/~4/2G9SEIfTyJI" height="1" width="1"/>]]></content:encoded>
			<wfw:commentRss>http://futureadapter.com/2009/07/30/authlogic_oauth-gotcha/feed/</wfw:commentRss>
		<slash:comments>2</slash:comments>
		</item>
		<item>
		<title>Rails 2.3.1+, Passenger, and Rack 1.0</title>
		<link>http://futureadapter.com/2009/07/23/rails-2-3-1-passenger-and-rack-1-0/</link>
		<comments>http://futureadapter.com/2009/07/23/rails-2-3-1-passenger-and-rack-1-0/#comments</comments>
		<pubDate>Thu, 23 Jul 2009 18:50:45 +0000</pubDate>
		<dc:creator>mattenat</dc:creator>
				<category><![CDATA[Uncategorized]]></category>
		<category><![CDATA[dreamhost]]></category>
		<category><![CDATA[passenger]]></category>
		<category><![CDATA[phusion passenger]]></category>
		<category><![CDATA[rack]]></category>
		<category><![CDATA[rails]]></category>
		<category><![CDATA[rubyonrails]]></category>

		<guid isPermaLink="false">http://futureadapter.com/?p=182</guid>
		<description><![CDATA[Just an FYI for those of you running the latest stable Rails.
Rails 2.3.1+ depends on Rack 1.0.  In 2.3.1, it only uses the vendor&#8217;d version of Rack 1.0 (which is located under action_controller/vendor).  In 2.3.2, it can use a vendor&#8217;d version of Rack 1.0 if the gem isn&#8217;t installed in your system.  Rails 2.3.3 depends [...]]]></description>
			<content:encoded><![CDATA[<p>Just an FYI for those of you running the latest stable Rails.</p>
<p>Rails 2.3.1+ depends on Rack 1.0.  In 2.3.1, it only uses the vendor&#8217;d version of Rack 1.0 (which is located under action_controller/vendor).  In 2.3.2, it can use a vendor&#8217;d version of Rack 1.0 if the gem isn&#8217;t installed in your system.  Rails 2.3.3 depends on the Rack 1.0 gem and no longer contains vendor&#8217;d Rack.  I haven&#8217;t found any reasons for why this has changed between point releases.</p>
<p>Either way, Rack 1.0 needs for the rack.input to be seekable.  It calls rewind on said input.  Passenger &lt; 2.2.2 passed a UnixSocket as rack.input, which is not seekable.  This will produce the following error in your Rails app:</p>
<p><code>/!\ FAILSAFE /!\  Thu Jul 23 10:57:14 -0700 2009<br />
Status: 500 Internal Server Error<br />
undefined method `rewind' for #&lt;UNIXSocket:0x2add0f3f45a0&gt;<br />
</code></p>
<p>So, to repeat.  If you&#8217;re running Rails in 2.3.1+, you need Rack 1.0, which needs Passenger 2.2.2+.  There have been various efforts by all sorts of software including Facebooker to work around this problem, but those seem to have sometimes caused more issues.</p>
<p>Incidentally, Dreamhost currently has Passenger 2.1.2 installed.  *sigh*</p>
<p>References:</p>
<ul>
<li><a href="http://blog.phusion.nl/2009/04/26/phusion-passenger-222-released/" target="_blank">http://blog.phusion.nl/2009/04/26/phusion-passenger-222-released/</a></li>
<li><a href="http://code.google.com/p/phusion-passenger/issues/detail?id=220" target="_blank">http://code.google.com/p/phusion-passenger/issues/detail?id=220</a></li>
<li><a href="http://groups.google.com/group/cloudkit/browse_thread/thread/f07b21320d0f6fe8" target="_blank">http://groups.google.com/group/cloudkit/browse_thread/thread/f07b21320d0f6fe8</a></li>
<li><a href="http://groups.google.com/group/rack-devel/browse_thread/thread/4324acc264d9d6ed" target="_blank">http://groups.google.com/group/rack-devel/browse_thread/thread/4324acc264d9d6ed</a></li>
<li><a href="http://github.com/FooBarWidget/passenger/blob/423a88a1c5180f0209e91f14c05e7bde82818928/lib/phusion_passenger/rack/request_handler.rb#L70" target="_blank">http://github.com/FooBarWidget/passenger/blob/423a88a1c5180f0209e91f14c05e7bde82818928/lib/phusion_passenger/rack/request_handler.rb#L70</a></li>
<li><a href="http://github.com/chneukirchen/rack/blob/7a55c98a30deea7fe198958d7c2770b087aa94b9/lib/rack/request.rb#L144" target="_blank">http://github.com/chneukirchen/rack/blob/7a55c98a30deea7fe198958d7c2770b087aa94b9/lib/rack/request.rb#L144</a></li>
</ul>
<p>Oh, and I also learned that if there&#8217;s a particular commit in <a href="http://github.com" target="_blank">Github</a> that you&#8217;d like to apply to your own source tree (say, for instance, the patch that removes vendor&#8217;d rack 1.0 from Rails which you&#8217;d like to reverse), you can simply add .patch onto the end of the URL and you&#8217;ll get the relevant patchfile.  Github rocks!</p>
<p><strong>UPDATE: </strong>Dreamhost has upgraded their versions of Passenger and Rack, and Rails 2.3.3 is now working.  Thanks Dreamhost!</p>
<div class="feedflare">
<a href="http://feeds.feedburner.com/~ff/futureadapter?a=p47rksdPQq4:rZP9ZIFl2gU:yIl2AUoC8zA"><img src="http://feeds.feedburner.com/~ff/futureadapter?d=yIl2AUoC8zA" border="0"></img></a> <a href="http://feeds.feedburner.com/~ff/futureadapter?a=p47rksdPQq4:rZP9ZIFl2gU:F7zBnMyn0Lo"><img src="http://feeds.feedburner.com/~ff/futureadapter?i=p47rksdPQq4:rZP9ZIFl2gU:F7zBnMyn0Lo" border="0"></img></a> <a href="http://feeds.feedburner.com/~ff/futureadapter?a=p47rksdPQq4:rZP9ZIFl2gU:V_sGLiPBpWU"><img src="http://feeds.feedburner.com/~ff/futureadapter?i=p47rksdPQq4:rZP9ZIFl2gU:V_sGLiPBpWU" border="0"></img></a> <a href="http://feeds.feedburner.com/~ff/futureadapter?a=p47rksdPQq4:rZP9ZIFl2gU:qj6IDK7rITs"><img src="http://feeds.feedburner.com/~ff/futureadapter?d=qj6IDK7rITs" border="0"></img></a> <a href="http://feeds.feedburner.com/~ff/futureadapter?a=p47rksdPQq4:rZP9ZIFl2gU:l6gmwiTKsz0"><img src="http://feeds.feedburner.com/~ff/futureadapter?d=l6gmwiTKsz0" border="0"></img></a> <a href="http://feeds.feedburner.com/~ff/futureadapter?a=p47rksdPQq4:rZP9ZIFl2gU:gIN9vFwOqvQ"><img src="http://feeds.feedburner.com/~ff/futureadapter?i=p47rksdPQq4:rZP9ZIFl2gU:gIN9vFwOqvQ" border="0"></img></a> <a href="http://feeds.feedburner.com/~ff/futureadapter?a=p47rksdPQq4:rZP9ZIFl2gU:TzevzKxY174"><img src="http://feeds.feedburner.com/~ff/futureadapter?d=TzevzKxY174" border="0"></img></a>
</div><img src="http://feeds.feedburner.com/~r/futureadapter/~4/p47rksdPQq4" height="1" width="1"/>]]></content:encoded>
			<wfw:commentRss>http://futureadapter.com/2009/07/23/rails-2-3-1-passenger-and-rack-1-0/feed/</wfw:commentRss>
		<slash:comments>5</slash:comments>
		</item>
		<item>
		<title>Rails running in script/console?</title>
		<link>http://futureadapter.com/2009/07/16/rails-running-in-scriptconsole/</link>
		<comments>http://futureadapter.com/2009/07/16/rails-running-in-scriptconsole/#comments</comments>
		<pubDate>Thu, 16 Jul 2009 20:42:46 +0000</pubDate>
		<dc:creator>mattenat</dc:creator>
				<category><![CDATA[Uncategorized]]></category>
		<category><![CDATA[console]]></category>
		<category><![CDATA[rails]]></category>
		<category><![CDATA[rubyonrails]]></category>
		<category><![CDATA[script/console]]></category>

		<guid isPermaLink="false">http://futureadapter.com/?p=173</guid>
		<description><![CDATA[If you ever wanted to know if your application is running normally or in console mode, here&#8217;s the trick:
def in_console?
  Object.const_defined?(:IRB)
end
This would break if you require&#8217;d IRB in your Rails application normally, though I can&#8217;t possibly imagine why you would.
]]></description>
			<content:encoded><![CDATA[<p>If you ever wanted to know if your application is running normally or in console mode, here&#8217;s the trick:</p>
<pre><span class="keyword">def</span> <span class="function-name">in_console?</span>
  <span class="type">Object</span>.const_defined?(:<span class="type">IRB</span>)
<span class="keyword">end</span></pre>
<p>This would break if you require&#8217;d IRB in your Rails application normally, though I can&#8217;t possibly imagine why you would.</p>
<div class="feedflare">
<a href="http://feeds.feedburner.com/~ff/futureadapter?a=9-NA2-aNieU:WXIR5HAsaJs:yIl2AUoC8zA"><img src="http://feeds.feedburner.com/~ff/futureadapter?d=yIl2AUoC8zA" border="0"></img></a> <a href="http://feeds.feedburner.com/~ff/futureadapter?a=9-NA2-aNieU:WXIR5HAsaJs:F7zBnMyn0Lo"><img src="http://feeds.feedburner.com/~ff/futureadapter?i=9-NA2-aNieU:WXIR5HAsaJs:F7zBnMyn0Lo" border="0"></img></a> <a href="http://feeds.feedburner.com/~ff/futureadapter?a=9-NA2-aNieU:WXIR5HAsaJs:V_sGLiPBpWU"><img src="http://feeds.feedburner.com/~ff/futureadapter?i=9-NA2-aNieU:WXIR5HAsaJs:V_sGLiPBpWU" border="0"></img></a> <a href="http://feeds.feedburner.com/~ff/futureadapter?a=9-NA2-aNieU:WXIR5HAsaJs:qj6IDK7rITs"><img src="http://feeds.feedburner.com/~ff/futureadapter?d=qj6IDK7rITs" border="0"></img></a> <a href="http://feeds.feedburner.com/~ff/futureadapter?a=9-NA2-aNieU:WXIR5HAsaJs:l6gmwiTKsz0"><img src="http://feeds.feedburner.com/~ff/futureadapter?d=l6gmwiTKsz0" border="0"></img></a> <a href="http://feeds.feedburner.com/~ff/futureadapter?a=9-NA2-aNieU:WXIR5HAsaJs:gIN9vFwOqvQ"><img src="http://feeds.feedburner.com/~ff/futureadapter?i=9-NA2-aNieU:WXIR5HAsaJs:gIN9vFwOqvQ" border="0"></img></a> <a href="http://feeds.feedburner.com/~ff/futureadapter?a=9-NA2-aNieU:WXIR5HAsaJs:TzevzKxY174"><img src="http://feeds.feedburner.com/~ff/futureadapter?d=TzevzKxY174" border="0"></img></a>
</div><img src="http://feeds.feedburner.com/~r/futureadapter/~4/9-NA2-aNieU" height="1" width="1"/>]]></content:encoded>
			<wfw:commentRss>http://futureadapter.com/2009/07/16/rails-running-in-scriptconsole/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>deploy:web:disable and Dreamhost</title>
		<link>http://futureadapter.com/2009/07/03/deploywebdisable-and-dreamhost/</link>
		<comments>http://futureadapter.com/2009/07/03/deploywebdisable-and-dreamhost/#comments</comments>
		<pubDate>Fri, 03 Jul 2009 13:00:41 +0000</pubDate>
		<dc:creator>mattenat</dc:creator>
				<category><![CDATA[Uncategorized]]></category>
		<category><![CDATA[deploy:web:disable]]></category>
		<category><![CDATA[dreamhost]]></category>
		<category><![CDATA[htaccess]]></category>
		<category><![CDATA[maintenance]]></category>
		<category><![CDATA[passenger]]></category>
		<category><![CDATA[rails]]></category>

		<guid isPermaLink="false">http://futureadapter.com/?p=123</guid>
		<description><![CDATA[Following on the heels of Twitter on your maintenance page, the other piece of custom setup I needed for this solution to work was making the site swallow all incoming requests while disabled in order to show the maintenance page.  In the past on sites using Nginx+thin/mongrel the setup has been in the nginx configuration [...]]]></description>
			<content:encoded><![CDATA[<p>Following on the heels of <a href="http://futureadapter.com/2009/07/01/twitter-on-your-maintenance-page/">Twitter on your maintenance page</a>, the other piece of custom setup I needed for this solution to work was making the site swallow all incoming requests while disabled in order to show the maintenance page.  In the past on sites using Nginx+thin/mongrel the setup has been in the nginx configuration code.  At <a href="http://www.dreamhost.com" target="_blank">Dreamhost</a>, where I have my Rails projects (and this site), the setup uses Apache+passenger.  And you don&#8217;t have access to the virtual host section of the Apache setup to do the configuration there.</p>
<p><strong>Use .htaccess</strong></p>
<p>The solution was to use .htaccess.  Put it in your public directory.  Check it into source control.  When you deploy your app, the configuration will go with it.</p>
<p><strong>Make a special rule for images, if you want &#8216;em</strong></p>
<p>My custom maintenace page links to a couple of images (the logo and a background gradient).  The rest of the page I can encapsulate in the html iteself (css and javascript), so there&#8217;s no need for special rules about what Apache will choose to server and what it won&#8217;t.  But to avoid Apache serving up the maintenance.html page for all your images, you need a special rule.</p>
<p>Below, the complete .htaccess:</p>
<pre>RewriteEngine On
RewriteCond %{DOCUMENT_ROOT}/system/maintenance.html -f
RewriteCond %{SCRIPT_FILENAME} !maintenance.html
RewriteCond %{REQUEST_URI} !^/images/
RewriteRule ^.*$ /system/maintenance.html [L]</pre>
<p><strong>Useful additional links</strong></p>
<ul>
<li><a href="http://wiki.dreamhost.com/index.php/Making_stats_accessible_with_htaccess">Making stats accessible with htaccess &#8211; DreamHost</a></li>
<li><a href="http://wiki.dreamhost.com/index.php/Making_stats_accessible_with_htaccess"></a><a href="http://groups.google.com/group/capistrano/browse_thread/thread/8adcf27c38573522">.htaccess, passenger, apache &#8211; Capistrano | Google Groups</a></li>
</ul>
<div class="feedflare">
<a href="http://feeds.feedburner.com/~ff/futureadapter?a=V4exycKNjAU:Rrb2jMdlDLs:yIl2AUoC8zA"><img src="http://feeds.feedburner.com/~ff/futureadapter?d=yIl2AUoC8zA" border="0"></img></a> <a href="http://feeds.feedburner.com/~ff/futureadapter?a=V4exycKNjAU:Rrb2jMdlDLs:F7zBnMyn0Lo"><img src="http://feeds.feedburner.com/~ff/futureadapter?i=V4exycKNjAU:Rrb2jMdlDLs:F7zBnMyn0Lo" border="0"></img></a> <a href="http://feeds.feedburner.com/~ff/futureadapter?a=V4exycKNjAU:Rrb2jMdlDLs:V_sGLiPBpWU"><img src="http://feeds.feedburner.com/~ff/futureadapter?i=V4exycKNjAU:Rrb2jMdlDLs:V_sGLiPBpWU" border="0"></img></a> <a href="http://feeds.feedburner.com/~ff/futureadapter?a=V4exycKNjAU:Rrb2jMdlDLs:qj6IDK7rITs"><img src="http://feeds.feedburner.com/~ff/futureadapter?d=qj6IDK7rITs" border="0"></img></a> <a href="http://feeds.feedburner.com/~ff/futureadapter?a=V4exycKNjAU:Rrb2jMdlDLs:l6gmwiTKsz0"><img src="http://feeds.feedburner.com/~ff/futureadapter?d=l6gmwiTKsz0" border="0"></img></a> <a href="http://feeds.feedburner.com/~ff/futureadapter?a=V4exycKNjAU:Rrb2jMdlDLs:gIN9vFwOqvQ"><img src="http://feeds.feedburner.com/~ff/futureadapter?i=V4exycKNjAU:Rrb2jMdlDLs:gIN9vFwOqvQ" border="0"></img></a> <a href="http://feeds.feedburner.com/~ff/futureadapter?a=V4exycKNjAU:Rrb2jMdlDLs:TzevzKxY174"><img src="http://feeds.feedburner.com/~ff/futureadapter?d=TzevzKxY174" border="0"></img></a>
</div><img src="http://feeds.feedburner.com/~r/futureadapter/~4/V4exycKNjAU" height="1" width="1"/>]]></content:encoded>
			<wfw:commentRss>http://futureadapter.com/2009/07/03/deploywebdisable-and-dreamhost/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>Twitter on your maintenance page</title>
		<link>http://futureadapter.com/2009/07/01/twitter-on-your-maintenance-page/</link>
		<comments>http://futureadapter.com/2009/07/01/twitter-on-your-maintenance-page/#comments</comments>
		<pubDate>Wed, 01 Jul 2009 12:16:24 +0000</pubDate>
		<dc:creator>mattenat</dc:creator>
				<category><![CDATA[Uncategorized]]></category>
		<category><![CDATA[capistrano]]></category>
		<category><![CDATA[deploy:web:disable]]></category>
		<category><![CDATA[deploys]]></category>
		<category><![CDATA[maintenance]]></category>
		<category><![CDATA[rails]]></category>
		<category><![CDATA[twitter]]></category>

		<guid isPermaLink="false">http://futureadapter.com/?p=121</guid>
		<description><![CDATA[I recently started a new project.  Because the domain was purchased, one of the first acts of business was a, &#8220;Coming Soon,&#8221; page.  I decided the maintenance/deploy:web:disable page was the perfect solution.  I could put it up in production and leave it off in staging without special code shenanigans.  It would be re-usable for the [...]]]></description>
			<content:encoded><![CDATA[<p>I recently started a new project.  Because the domain was purchased, one of the first acts of business was a, &#8220;Coming Soon,&#8221; page.  I decided the maintenance/deploy:web:disable page was the perfect solution.  I could put it up in production and leave it off in staging without special code shenanigans.  It would be re-usable for the future if the site ever needed maintenance.  All-in-all, simple and useful.</p>
<p><strong>How to setup a custom maintenance page</strong></p>
<p><strong><span style="font-weight: normal;">This is totally cribbed from the capistrano default task for deploy:web:disable.  My only change is the location of the maintenance template.</span></strong></p>
<p><strong><span style="font-weight: normal;">In config/deploy.rb:</span></strong></p>
<pre>namespace <span class="constant">:deploy</span> <span class="keyword">do</span>
  namespace <span class="constant">:web</span> <span class="keyword">do</span>
    desc <span class="string">"Put the `we're working on it' message up using config/templates/maintenance.html.erb"</span>
    task <span class="constant">:disable</span>, <span class="constant">:roles</span> =&gt; <span class="constant">:web</span>, <span class="constant">:except</span> =&gt; { <span class="constant">:no_release</span> =&gt; <span class="variable-name">true</span> } <span class="keyword">do</span>
      require <span class="string">'erb'</span>
      on_rollback { run <span class="string">"rm </span><span class="variable-name">#{shared_path}</span><span class="string">/system/maintenance.html"</span> }

      template = <span class="type">File</span>.read(<span class="type">File</span>.join(<span class="type">File</span>.dirname(__FILE__), <span class="string">"templates"</span>, <span class="string">"maintenance.html.erb"</span>))
      result = <span class="type">ERB</span>.new(template).result(binding)

      put result, <span class="string">"</span><span class="variable-name">#{shared_path}</span><span class="string">/system/maintenance.html"</span>, <span class="constant">:mode</span> =&gt; 0644
    <span class="keyword">end</span>
  <span class="keyword">end</span>
<span class="keyword">end</span></pre>
<p><strong>Simple how-to-include twitter</strong></p>
<p>The interesting twist is putting—instead of stock ,&#8221;Coming soon,&#8221; or, &#8220;We&#8217;ll be right back&#8221;—a twitter message right into the maintenance page.  The last tweet from the account for the site is automatically included.</p>
<p>The javascript, which doesn&#8217;t depend on any outside libraries, for updating a portion of the page with the most recent tweet:</p>
<pre><span class="function-name">&lt;</span><span class="html-helper-tag">head</span><span class="function-name">&gt;</span>
  <span class="function-name">&lt;</span><span class="html-helper-tag">script type=</span><span class="string">"text/javascript"</span><span class="function-name">&gt;</span>
    function tweetCallback<span class="function-name">(</span>data<span class="function-name">)</span> <span class="function-name">{</span>
        var tweet = data<span class="function-name">[</span>0<span class="function-name">]</span>;
        if <span class="function-name">(</span>text = tweet<span class="function-name">[</span><span class="string">"text"</span><span class="function-name">])</span>
            document.getElementById<span class="function-name">(</span>'tweet'<span class="function-name">)</span>.innerHTML = ' : ' + text;
    <span class="function-name">}</span>
  <span class="function-name">&lt;</span><span class="html-helper-tag">/script</span><span class="function-name">&gt;</span>
<span class="function-name">&lt;</span><span class="html-helper-tag">/head</span><span class="function-name">&gt;</span></pre>
<p>At the bottom of the maintenance.html.erb body:</p>
<pre><span class="function-name">&lt;</span><span class="html-helper-tag">script type=</span><span class="string">"text/javascript"</span><span class="html-helper-tag"> src=</span><span class="string">"http://twitter.com/statuses/user_timeline/TWITTER_USERNAME_HERE.json?callback=tweetCallback&amp;count=1"</span><span class="function-name">&gt;&lt;</span><span class="html-helper-tag">/script</span><span class="function-name">&gt;</span></pre>
<p><strong>Why its a good idea</strong></p>
<p>I&#8217;m really happy with including the last twitter message for the text.  Firstly, the client can change the page whenever they want without contacting me.  Also, it doesn&#8217;t involve any application code, which is important since this is also the page the user will see when the site is down.  It promotes the twitter account for the site and at the same time is a totally sensible place to update any possible status messages should they arise.</p>
<div class="feedflare">
<a href="http://feeds.feedburner.com/~ff/futureadapter?a=lFxSxmqeKHA:kP6ObpjUhMY:yIl2AUoC8zA"><img src="http://feeds.feedburner.com/~ff/futureadapter?d=yIl2AUoC8zA" border="0"></img></a> <a href="http://feeds.feedburner.com/~ff/futureadapter?a=lFxSxmqeKHA:kP6ObpjUhMY:F7zBnMyn0Lo"><img src="http://feeds.feedburner.com/~ff/futureadapter?i=lFxSxmqeKHA:kP6ObpjUhMY:F7zBnMyn0Lo" border="0"></img></a> <a href="http://feeds.feedburner.com/~ff/futureadapter?a=lFxSxmqeKHA:kP6ObpjUhMY:V_sGLiPBpWU"><img src="http://feeds.feedburner.com/~ff/futureadapter?i=lFxSxmqeKHA:kP6ObpjUhMY:V_sGLiPBpWU" border="0"></img></a> <a href="http://feeds.feedburner.com/~ff/futureadapter?a=lFxSxmqeKHA:kP6ObpjUhMY:qj6IDK7rITs"><img src="http://feeds.feedburner.com/~ff/futureadapter?d=qj6IDK7rITs" border="0"></img></a> <a href="http://feeds.feedburner.com/~ff/futureadapter?a=lFxSxmqeKHA:kP6ObpjUhMY:l6gmwiTKsz0"><img src="http://feeds.feedburner.com/~ff/futureadapter?d=l6gmwiTKsz0" border="0"></img></a> <a href="http://feeds.feedburner.com/~ff/futureadapter?a=lFxSxmqeKHA:kP6ObpjUhMY:gIN9vFwOqvQ"><img src="http://feeds.feedburner.com/~ff/futureadapter?i=lFxSxmqeKHA:kP6ObpjUhMY:gIN9vFwOqvQ" border="0"></img></a> <a href="http://feeds.feedburner.com/~ff/futureadapter?a=lFxSxmqeKHA:kP6ObpjUhMY:TzevzKxY174"><img src="http://feeds.feedburner.com/~ff/futureadapter?d=TzevzKxY174" border="0"></img></a>
</div><img src="http://feeds.feedburner.com/~r/futureadapter/~4/lFxSxmqeKHA" height="1" width="1"/>]]></content:encoded>
			<wfw:commentRss>http://futureadapter.com/2009/07/01/twitter-on-your-maintenance-page/feed/</wfw:commentRss>
		<slash:comments>1</slash:comments>
		</item>
	</channel>
</rss>
