<?xml version="1.0" encoding="UTF-8"?>
<?xml-stylesheet type="text/css" href="/stylesheets/rss.css"?>
<rss version="2.0" xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:trackback="http://madskills.com/public/xml/rss/module/trackback/">
  <channel>
    <title>Late to the Party: Category Rails</title>
    <link>http://cwilliams.textdriven.com/articles/category/rails</link>
    <language>en-us</language>
    <ttl>40</ttl>
    <description>Ruby. Rails. Stuff.</description>
    <item>
      <title>Aptana Studio 1.0 Released!</title>
      <description>&lt;p&gt;Wow it's been quite a while since I last posted. In the meantime I've been working hard on RDT and RadRails at Aptana and the job has been great. I'm very lucky to have found a way to work on the open source projects I love full-time.&lt;/p&gt;

&lt;p&gt;In that vein I'd like to announce that &lt;a href="http://www.aptana.com/blog/?p=200?diff=y"&gt;we've released the 1.0 of Aptana&lt;/a&gt;. This release is important for a number of reasons. First, we think the product is good-to-go for everyone. Second, we're announcing &lt;a href="http://www.aptana.com/products/studio_professional.php"&gt;a Pro version of the IDE&lt;/a&gt;. This version is for users who want to support the project so we can keep going, or who want the extra features and perks that come with a license: nightly build access, priority support, IE Javascript Debugger, SFTP/FTPS support and all sorts of other goodies. The support, nightly builds, SVN access also apply to the other components of what we're now calling Aptana Studio: &lt;a href="http://radrails.org/"&gt;RadRails&lt;/a&gt;, iPhone, PHP and AIR. So if you want to be on the bleeding edge of RadRails/RDT development you'll probably want to look into getting a license.&lt;/p&gt;

&lt;p&gt;Keep in mind that while we do offer a pro version for those who'd like to support us or the extra stuff, we are still shipping the same codebase (minus the commercial features) as &lt;a href="http://www.aptana.com/products/studio_community.php"&gt;an open source project under GPL&lt;/a&gt;. And we plan to remain an open-source company with an open source product.  Here's hoping that model will work for us!&lt;/p&gt;</description>
      <pubDate>Tue, 30 Oct 2007 12:41:00 +0000</pubDate>
      <guid isPermaLink="false">urn:uuid:494d8149-4863-4cca-bf1b-6c1d84350478</guid>
      <author>chris.a.williams@gmail.com (Chris)</author>
      <link>http://cwilliams.textdriven.com/articles/2007/10/30/aptana-studio-1-0-released</link>
      <category>Programming</category>
      <category>Personal</category>
      <category>Rails</category>
      <category>aptana</category>
      <category>RDT</category>
      <category>radrails</category>
      <category>Rails</category>
      <category>ide</category>
    </item>
    <item>
      <title>Aptana backs RDT, hires me</title>
      <description>&lt;p&gt;I'm proud to announce today that &lt;a href="http://www.aptana.com"&gt;Aptana&lt;/a&gt; has hired me to work full-time on &lt;a href="http://rubyeclipse.sourceforge.net"&gt;RDT&lt;/a&gt;, RadRails and integrating that work with their existing Aptana IDE which focuses on CSS, HTML and Javascript.&lt;/p&gt;

&lt;div style="background-color: white; text-align: center;"&gt;&lt;a href="http://www.aptana.com/rdt.html"&gt;&lt;img src="/images/aptana_radrails_rdt_ajax_rails_blue.gif" alt=""aptana-radrails-rdt ajax on rails/&gt;&lt;/a&gt;&lt;/div&gt;

&lt;p&gt;This announcement means that RDT will now have commercial backing (but will remain open-source and free!) and that you should see RDT and RadRails move forward at a much quicker pace than in the past.&lt;/p&gt;

&lt;p&gt;This is also great news for RadRails users and Rails developers in general as integrating the two will give you code completion, outlines, help, debugging and much more across the entire stack - from model to controller to the HTML, ruby code, CSS and Javascript that make up your views.&lt;/p&gt;

</description>
      <pubDate>Sat, 21 Apr 2007 16:41:00 +0000</pubDate>
      <guid isPermaLink="false">urn:uuid:c68f2859-5c20-4685-a61e-da26f1583f63</guid>
      <author>chris.a.williams@gmail.com (Chris)</author>
      <link>http://cwilliams.textdriven.com/articles/2007/04/21/aptana-backs-rdt-hires-me</link>
      <category>Programming</category>
      <category>Personal</category>
      <category>Web design</category>
      <category>Ruby</category>
      <category>Rails</category>
      <category>aptana</category>
      <category>ruby</category>
      <category>Rails</category>
      <category>commercial</category>
      <category>sponsor</category>
      <category>RDT</category>
      <category>radrails</category>
      <category>ide</category>
      <category>eclipse</category>
      <category>announcement</category>
    </item>
    <item>
      <title>Patterns in Ruby: Decorator revisited</title>
      <description>&lt;p&gt;A while back I wrote an article describing some possible ways to implement a 
&lt;a href="http://cwilliams.textdriven.com/articles/2006/10/27/patterns-in-ruby-template-method"&gt;Decorator pattern in Ruby&lt;/a&gt;. 
I've stumbled across several mentions of 
yet another idiom used so often in the Rails codebase that they've extracted 
it into the latest ActiveSupport. That idiom is 
&lt;a href="http://weblog.rubyonrails.org/2006/4/26/new-in-rails-module-alias_method_chain"&gt;&lt;code&gt;alias_method_chain&lt;/code&gt;&lt;/a&gt;,
and it's a good example of a decorator implementation.&lt;/p&gt;

&lt;p&gt;This idiom is a codified example of using the alias approach I briefly mentioned in the earlier article. In that 
article we aliased the original implementation with anew name, and set up a new implementation of our method 
(often delegating to the original) with the original's name. A quick example makes this clear:&lt;/p&gt;

&lt;div class="typocode"&gt;&lt;pre&gt;&lt;code class="typocode_ruby "&gt;&lt;span class="keyword"&gt;class &lt;/span&gt;&lt;span class="class"&gt;Window&lt;/span&gt;
  &lt;span class="keyword"&gt;def &lt;/span&gt;&lt;span class="method"&gt;draw&lt;/span&gt;
    &lt;span class="comment"&gt;# do some drawing here...&lt;/span&gt;
  &lt;span class="keyword"&gt;end&lt;/span&gt;

  &lt;span class="comment"&gt;# some code...&lt;/span&gt;

&lt;span class="comment"&gt;# creates a 'copy' of draw method, but gives it&lt;/span&gt;
&lt;span class="comment"&gt;# the name/selector 'original_draw'&lt;/span&gt;
  &lt;span class="keyword"&gt;alias&lt;/span&gt; &lt;span class="symbol"&gt;:original_draw&lt;/span&gt; &lt;span class="symbol"&gt;:draw&lt;/span&gt; 

  &lt;span class="keyword"&gt;def &lt;/span&gt;&lt;span class="method"&gt;draw&lt;/span&gt;
    &lt;span class="ident"&gt;draw_vertical_scrollbar&lt;/span&gt;
    &lt;span class="ident"&gt;original_draw&lt;/span&gt;
  &lt;span class="keyword"&gt;end&lt;/span&gt;
&lt;span class="keyword"&gt;end&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;h2&gt;&lt;code&gt;alias_method_chain&lt;/code&gt;&lt;/h2&gt;

&lt;p&gt;In &lt;a href="http://weblog.rubyonrails.org/2006/4/26/new-in-rails-module-alias_method_chain"&gt;Rails 1.2 (ActiveSupport specifically)&lt;/a&gt;, the Rails core team found many instances of this pattern 
and codified a new method on the class Module, &lt;code&gt;alias_method_chain&lt;/code&gt;. This class-level method encapsulates this 
pattern of wrapping existing methods with additional behavior.&lt;/p&gt;

&lt;p&gt;Here's a specific example, showing how they would wrap rendering with layouts:&lt;/p&gt;

&lt;div class="typocode"&gt;&lt;pre&gt;&lt;code class="typocode_ruby "&gt;&lt;span class="ident"&gt;alias_method&lt;/span&gt; &lt;span class="symbol"&gt;:render_with_no_layout&lt;/span&gt;&lt;span class="punct"&gt;,&lt;/span&gt; &lt;span class="symbol"&gt;:render&lt;/span&gt;
&lt;span class="ident"&gt;alias_method&lt;/span&gt; &lt;span class="symbol"&gt;:render&lt;/span&gt;&lt;span class="punct"&gt;,&lt;/span&gt; &lt;span class="symbol"&gt;:render_with_a_layout&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;In this small code snippet, they are creating a small chain of methods to wrap the existing render behavior. 
Now calls to render will be routed to &lt;code&gt;render_with_a_layout&lt;/code&gt; and then on to the original render implementation 
(which is now aliased to &lt;code&gt;render_with_no_layout&lt;/code&gt;). So they coded up &lt;code&gt;alias_method_chain&lt;/code&gt; which simply does 
the wrapping for them (using naming conventions):&lt;/p&gt;

&lt;div class="typocode"&gt;&lt;pre&gt;&lt;code class="typocode_ruby "&gt;&lt;span class="keyword"&gt;class &lt;/span&gt;&lt;span class="class"&gt;Module&lt;/span&gt;
  &lt;span class="comment"&gt;# Encapsulates the common pattern of:&lt;/span&gt;
  &lt;span class="comment"&gt;#&lt;/span&gt;
  &lt;span class="comment"&gt;#   alias_method :foo_without_feature, :foo&lt;/span&gt;
  &lt;span class="comment"&gt;#   alias_method :foo, :foo_with_feature&lt;/span&gt;
  &lt;span class="comment"&gt;#&lt;/span&gt;
  &lt;span class="comment"&gt;# With this, you simply do:&lt;/span&gt;
  &lt;span class="comment"&gt;#&lt;/span&gt;
  &lt;span class="comment"&gt;#   alias_method_chain :foo, :feature&lt;/span&gt;
  &lt;span class="comment"&gt;#&lt;/span&gt;
  &lt;span class="comment"&gt;# And both aliases are set up for you.&lt;/span&gt;
  &lt;span class="keyword"&gt;def &lt;/span&gt;&lt;span class="method"&gt;alias_method_chain&lt;/span&gt;&lt;span class="punct"&gt;(&lt;/span&gt;&lt;span class="ident"&gt;target&lt;/span&gt;&lt;span class="punct"&gt;,&lt;/span&gt; &lt;span class="ident"&gt;feature&lt;/span&gt;&lt;span class="punct"&gt;)&lt;/span&gt;
    &lt;span class="ident"&gt;alias_method&lt;/span&gt; &lt;span class="punct"&gt;&amp;quot;&lt;/span&gt;&lt;span class="string"&gt;&lt;span class="expr"&gt;#{target}&lt;/span&gt;_without_&lt;span class="expr"&gt;#{feature}&lt;/span&gt;&lt;/span&gt;&lt;span class="punct"&gt;&amp;quot;,&lt;/span&gt; &lt;span class="ident"&gt;target&lt;/span&gt;
    &lt;span class="ident"&gt;alias_method&lt;/span&gt; &lt;span class="ident"&gt;target&lt;/span&gt;&lt;span class="punct"&gt;,&lt;/span&gt; &lt;span class="punct"&gt;&amp;quot;&lt;/span&gt;&lt;span class="string"&gt;&lt;span class="expr"&gt;#{target}&lt;/span&gt;_with_&lt;span class="expr"&gt;#{feature}&lt;/span&gt;&lt;/span&gt;&lt;span class="punct"&gt;&amp;quot;&lt;/span&gt;
  &lt;span class="keyword"&gt;end&lt;/span&gt;
&lt;span class="keyword"&gt;end&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;Please note that if they were to replace their existing two calls to &lt;code&gt;alias_method&lt;/code&gt; above, they would need to tweak the naming a little
(the methods would become &lt;code&gt;render_without_layout&lt;/code&gt; and &lt;code&gt;render_with_layout&lt;/code&gt; as opposed to
&lt;code&gt;render_with_no_layout&lt;/code&gt; and &lt;code&gt;render_with_a_layout&lt;/code&gt; respectively).&lt;/p&gt;</description>
      <pubDate>Tue, 26 Dec 2006 18:38:00 +0000</pubDate>
      <guid isPermaLink="false">urn:uuid:42654a54-afbd-4e14-932e-8ef2ce31b561</guid>
      <author>chris.a.williams@gmail.com (Chris)</author>
      <link>http://cwilliams.textdriven.com/articles/2006/12/26/patterns-in-ruby-decorator-revisited</link>
      <category>Programming</category>
      <category>Ruby</category>
      <category>Rails</category>
      <category>decorator</category>
      <category>patterns</category>
      <category>ruby</category>
      <category>alias</category>
      <category>chain</category>
      <category>method</category>
    </item>
    <item>
      <title>Multi-stage deployments with Capistrano</title>
      <description>&lt;p&gt;I'm seeing a recurring theme in my feed reader today - multi-stage deployments with Capistrano.
It seems that those of us working in larger companies have all hit the same issue - we have some form of user acceptance, staging or other system which mirrors the production in configuration; and we want to use Capistrano to deploy to it as well as production. I know that my current company has a 5-stage process for pushing out releases (we're in financial services, and very paranoid. Whether they'd ever accept Capistrano is another issue).&lt;/p&gt;

&lt;p&gt;So here are my lazy pointers to the discussions:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;Ola Bini, who's been doing some great posts on ruby metaprogramming, &lt;a href="http://ola-bini.blogspot.com/2006/09/two-things-in-rails.html"&gt;calls out his need for a "production_test" environment&lt;/a&gt;.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;a href="http://www.redhillconsulting.com.au/blogs/simon/archives/000359.html"&gt;Simon Harris&lt;/a&gt; shares some snippets of deploying to his staged server running mongrel.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Jamis Buck, the creator of Capistrano, weighed in a while ago on &lt;a href="http://weblog.jamisbuck.org/2006/1/3/using-switchtower-with-multiple-deployment-stages"&gt;multiple deployment stages&lt;/a&gt;&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;em&gt;Update&lt;/em&gt;: Michael Buffington shares his Mongrel deployment for &lt;a href="http://www.michaelbuffington.com/articles/2006/09/11/mongrel-and-capistrano"&gt;multiple stages&lt;/a&gt;&lt;/p&gt;</description>
      <pubDate>Fri, 29 Sep 2006 12:56:00 +0000</pubDate>
      <guid isPermaLink="false">urn:uuid:4759d294-d891-4627-9c37-a0d2fd01721f</guid>
      <author>chris.a.williams@gmail.com (Chris)</author>
      <link>http://cwilliams.textdriven.com/articles/2006/09/29/multi-stage-deployments-with-capistrano</link>
      <category>Programming</category>
      <category>Ruby</category>
      <category>Rails</category>
      <category>Rails</category>
      <category>capistrano</category>
      <category>deployment</category>
      <category>production</category>
      <category>webdev</category>
      <category>programming</category>
      <category>ruby</category>
    </item>
    <item>
      <title>Prototype based Tag Autocompletion</title>
      <description>&lt;p&gt;I've been working on a website that uses tags internally. The &lt;a href="http://rubyforge.org/projects/taggable/"&gt;acts_as_taggable&lt;/a&gt; plugin has been great, but it only takes care of the ActiveRecord portion of tagging. One of the pieces of functionality I needed was to create an autocompleter for users to enter a list of tags on a record. Below is a pure copy-paste dump of my implementation. I'm not claiming it's good code or the right way top do things, but for those looking to use an autocompleter for a text field holding tags might find it useful. The implementation is close to that used by &lt;a href="http://del.icio.us"&gt;delicious&lt;/a&gt; in the tags field on posting new bookmarks.&lt;/p&gt;
&lt;p&gt;Here's an example usage:&lt;/p&gt;
&lt;div class="typocode"&gt;&lt;pre&gt;&lt;code class="typocode_html "&gt;&lt;notextile&gt;&amp;lt;label for=&amp;quot;tags&amp;quot;&amp;gt;Tags&amp;lt;/label&amp;gt;
&amp;lt;%= text_field_tag 'tags', @tags.join(' '), :size =&amp;gt; 80, :autocomplete =&amp;gt; &amp;quot;off&amp;quot; %&amp;gt;
&amp;lt;div class=&amp;quot;auto_complete&amp;quot; id=&amp;quot;tags_auto_complete&amp;quot;&amp;gt;&amp;lt;/div&amp;gt;&lt;/notextile&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;And the prototype-based class in javascript. You'll probably want to stick this in your application.js file if you're using &lt;a href="http://www.rubyonrails.org"&gt;Rails&lt;/a&gt;.&lt;/p&gt;

&lt;div class="typocode"&gt;&lt;pre&gt;&lt;code class="typocode_javascript "&gt;&lt;notextile&gt;String.prototype.trim = function(){ return this.replace(/^\s+|\s+$/g,'') }

Autocompleter.Tag = Class.create();
Autocompleter.Tag.prototype = Object.extend(new Autocompleter.Base(), {
  lastEdit: '',
  currentTag: {},
  initialize: function(element, update, array, options) {
    this.baseInitialize(element, update, options);
    this.options.array = array;
  },
  
  getCurrentTag: function() {
	if(this.element.value == this.lastEdit) return true // no edit
	if(this.element == '') return false
	this.currentTag = {}
	var tagArray=this.element.value.toLowerCase().split(' '), oldArray=this.lastEdit.toLowerCase().split(' '), currentTags = [], matched=false, t,o
	for (t in tagArray) {
		for (o in oldArray) {
			if(typeof oldArray[o] == 'undefined') { oldArray.splice(o,1); break }
			if(tagArray[t] == oldArray[o]) { matched = true; oldArray.splice(o,1); break; }
		}
		if(!matched) currentTags[currentTags.length] = t
		matched=false
	}
	// more than one word changed... abort
	if(currentTags.length &amp;gt; 1) {
	  // hideSuggestions(); TODO Fix this!
	  return false;
	}
	this.currentTag = { text:tagArray[currentTags[0]], index:currentTags[0] }
	return true
  },

  getUpdatedChoices: function() {
    this.updateChoices(this.options.selector(this));
  },
  
  onKeyPress: function(event) {
    if(this.active)
      switch(event.keyCode) {
       case Event.KEY_TAB:
       case Event.KEY_RETURN:
         this.selectEntry();
         Event.stop(event);
       case Event.KEY_ESC:
         this.hide();
         this.active = false;
         Event.stop(event);
         return;
       case Event.KEY_LEFT:
       case Event.KEY_RIGHT:
         return;
       case Event.KEY_UP:
         this.markPrevious();
         this.render();
         if(navigator.appVersion.indexOf('AppleWebKit')&amp;gt;0) Event.stop(event);
         return;
       case Event.KEY_DOWN:
         this.markNext();
         this.render();
         if(navigator.appVersion.indexOf('AppleWebKit')&amp;gt;0) Event.stop(event);
         return;
      }
     else 
       if(event.keyCode==Event.KEY_TAB || event.keyCode==Event.KEY_RETURN || 
         (navigator.appVersion.indexOf('AppleWebKit') &amp;gt; 0 &amp;amp;&amp;amp; event.keyCode == 0)) return;
    this.lastEdit = this.element.value;

    this.changed = true;
    this.hasFocus = true;

    if(this.observer) clearTimeout(this.observer);
      this.observer = 
        setTimeout(this.onObserverEvent.bind(this), this.options.frequency*1000);
  },
  
  updateElement: function(selectedElement) {
    if (this.options.updateElement) {
      this.options.updateElement(selectedElement);
      return;
    }
    var value = '';
    if (this.options.select) {
      var nodes = document.getElementsByClassName(this.options.select, selectedElement) || [];
      if(nodes.length&amp;gt;0) value = Element.collectTextNodes(nodes[0], this.options.select);
    } else
      value = Element.collectTextNodesIgnoreClass(selectedElement, 'informal');
    
    var lastTokenPos = this.findLastToken();
    if (lastTokenPos != -1) {
      var newValue = this.element.value.substr(0, lastTokenPos + 1);
      var whitespace = this.element.value.substr(lastTokenPos + 1).match(/^\s+/);
      if (whitespace)
        newValue += whitespace[0];
      this.element.value = newValue + value;
    } else {
      tagArray = this.element.value.trim().split(' ')
      if (!this.getCurrentTag()) return;  // We had a problem getting the current tag, just return?
      tagArray[this.currentTag.index] = value;
      this.element.value = tagArray.join(' ');
    }
    this.element.focus();
    
    if (this.options.afterUpdateElement)
      this.options.afterUpdateElement(this.element, selectedElement);
  },

  setOptions: function(options) {
    this.options = Object.extend({
      choices: 10,
      partialSearch: true,
      partialChars: 2,
      ignoreCase: true,
      fullSearch: false,
      selector: function(instance) {
          var ret       = []; // Beginning matches
        var partial   = []; // Inside matches
        var entry     = instance.getToken();
        
        entry = entry.trim().split(' ').pop()
        
        var count     = 0;

        for (var i = 0; i &amp;lt; instance.options.array.length &amp;amp;&amp;amp;  
          ret.length &amp;lt; instance.options.choices ; i++) { 

          var elem = instance.options.array[i];
          var foundPos = instance.options.ignoreCase ? 
            elem.toLowerCase().indexOf(entry.toLowerCase()) : 
            elem.indexOf(entry);

          while (foundPos != -1) {
            if (foundPos == 0 &amp;amp;&amp;amp; elem.length != entry.length) { 
              ret.push(&amp;quot;&amp;lt;li&amp;gt;&amp;lt;strong&amp;gt;&amp;quot; + elem.substr(0, entry.length) + &amp;quot;&amp;lt;/strong&amp;gt;&amp;quot; + 
                elem.substr(entry.length) + &amp;quot;&amp;lt;/li&amp;gt;&amp;quot;);
              break;
            } else if (entry.length &amp;gt;= instance.options.partialChars &amp;amp;&amp;amp; 
              instance.options.partialSearch &amp;amp;&amp;amp; foundPos != -1) {
              if (instance.options.fullSearch || /\s/.test(elem.substr(foundPos-1,1))) {
                partial.push(&amp;quot;&amp;lt;li&amp;gt;&amp;quot; + elem.substr(0, foundPos) + &amp;quot;&amp;lt;strong&amp;gt;&amp;quot; +
                  elem.substr(foundPos, entry.length) + &amp;quot;&amp;lt;/strong&amp;gt;&amp;quot; + elem.substr(
                  foundPos + entry.length) + &amp;quot;&amp;lt;/li&amp;gt;&amp;quot;);
                break;
              }
            }

            foundPos = instance.options.ignoreCase ? 
              elem.toLowerCase().indexOf(entry.toLowerCase(), foundPos + 1) : 
              elem.indexOf(entry, foundPos + 1);

          }
        }
        if (partial.length)
          ret = ret.concat(partial.slice(0, instance.options.choices - ret.length))
        return &amp;quot;&amp;lt;ul&amp;gt;&amp;quot; + ret.join('') + &amp;quot;&amp;lt;/ul&amp;gt;&amp;quot;;
      }    
    }, options || {});
  }
});
      &lt;/notextile&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;</description>
      <pubDate>Tue, 19 Sep 2006 15:12:00 +0000</pubDate>
      <guid isPermaLink="false">urn:uuid:2abe1246-129f-45b9-816b-7809b386bd93</guid>
      <author>chris.a.williams@gmail.com (Chris)</author>
      <link>http://cwilliams.textdriven.com/articles/2006/09/19/prototype-based-tag-autocompletion</link>
      <category>Programming</category>
      <category>Personal</category>
      <category>Web design</category>
      <category>Ruby</category>
      <category>Rails</category>
      <category>tags</category>
      <category>delicious</category>
      <category>ruby</category>
      <category>Rails</category>
      <category>autocomplete</category>
      <category>javascript</category>
      <category>prototype</category>
    </item>
    <item>
      <title>RailsConf and Rails 1.2</title>
      <description>&lt;p&gt;I meant to blog as the Rails Conference progressed, but the wireless network (and my own shame of my Wintel notebook) kept me from doing so. So here's my very quickly summarized notes on what I saw...&lt;/p&gt;

&lt;p&gt;The keynotes that stuck out most for me were Paul Graham's, Nathaniel Talbott's and DHH's. The first two were not technology focused, so much as people-oriented, and DHH's was very much about the future of Rails as he sees it.&lt;/p&gt;

&lt;h3&gt;Paul Graham's Keynote&lt;/h3&gt;

&lt;p&gt;&lt;em&gt;Update: Looks like Paul already has &lt;a href="http://www.paulgraham.com/marginal.html"&gt;posted this essay online&lt;/a&gt;.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;First I should mention that Paul Graham is even more entertaining in person than his essays, which is saying quite a bit.&lt;/p&gt;

&lt;p&gt;Paul Graham's keynote focused on technology and startups, particularly on the concept that being marginal/specialized is a great quality to pursue, and leads to most innovation. The talk was essentially about the fact that the conference attendees were highly marginalized in their technology choice - Ruby and Rails - but that being at the leading edge and risking yourself was the best way to get ahead. I guess the crux of it was that too many people play it safe in too many ways, not just about technology choices, but also in careers. The talk was really an extension of his series (and &lt;a href="http://ycombinator.com/"&gt;Y-Combinator&lt;/a&gt;), prodding us alpha geeks to go out and try our hand at becoming the next big startup.&lt;/p&gt;

&lt;h3&gt;Nathaniel Talbott's Keynote&lt;/h3&gt;

&lt;p&gt;&lt;a href="http://terralien.com/"&gt;Nathaniel&lt;/a&gt;'s speech was actually quite similar to Paul's. Their speaking styles and the analogies they drew were quite dissimilar, but their basic aim was the same: to urge us to strike out on our own and pursue our passion.&lt;/p&gt;

&lt;p&gt;Nathaniel drew analogies between homesteading in the 1800's to creating your own business in modern times. The technology we have now allows us to create services and business with minimal overhead and a very, very small team - similar to the way the Transcontinental Railroad gave way to east coast workers to strike out, stake some land and begin a new life.&lt;/p&gt;

&lt;p&gt;The analogy I most enjoyed, that was added from a suggestion when he gave a preparatory run through of his talk at a local user group, is the idea of "&lt;a href="http://barnraisr.com"&gt;barnraising&lt;/a&gt;" (yes that site got up over night after his talk). The basic premise being that we have a lot of smart rails geeks here, why not help start actually implementing one another's ideas? The idea isn't particularly unique, but the concept of the Rails community helping one another out in launching their businesses really hit home for a lot of attendees. I know I've personally got about 20 small Rails projects that I started quickly and scrapped just as quickly because I didn't have time, or needed encouragement. I'm really interested to see if this has sparked any groups of attendees into hacking up some quick projects (a la Railsday).&lt;/p&gt;

&lt;h3&gt;DHH's Keynote&lt;/h3&gt;

&lt;p&gt;David's keynote focused on a couple things: &lt;/p&gt;

&lt;p&gt;First, that Rails was successful because it often said "no" where other frameworks would say yes in terms of accepting functionality. So the calls to improve and extened various parts of Rails to help extend it into the corporate/enterprise world were alluring but if heeded too blindly or easily would lead to feature creep and bloat to the point that we'd begin to start looking like the frameworks we were leaving in the dust.&lt;/p&gt;

&lt;p&gt;Second, he talked about the immediate future for Rails, Rails 1.2. Rails is embracing REST. Really, really embracing REST. This means new routes like so:&lt;/p&gt;

&lt;div class="typocode"&gt;&lt;pre&gt;&lt;code class="typocode_ruby "&gt;&lt;span class="ident"&gt;map&lt;/span&gt;&lt;span class="punct"&gt;.&lt;/span&gt;&lt;span class="ident"&gt;resource&lt;/span&gt; &lt;span class="punct"&gt;'&lt;/span&gt;&lt;span class="string"&gt;/person&lt;/span&gt;&lt;span class="punct"&gt;'&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;Which would give RESTful URLs, which would automagically map to the conventional controller method names.&lt;/p&gt;

&lt;p&gt;Let's look at a controller in this scheme, with the URL mapped to the method it would invoke:&lt;/p&gt;

&lt;div class="typocode"&gt;&lt;pre&gt;&lt;code class="typocode_ruby "&gt;&lt;span class="keyword"&gt;class &lt;/span&gt;&lt;span class="class"&gt;PeopleController&lt;/span&gt; &lt;span class="punct"&gt;&amp;lt;&lt;/span&gt; &lt;span class="constant"&gt;ApplicationController&lt;/span&gt;
  &lt;span class="keyword"&gt;def &lt;/span&gt;&lt;span class="method"&gt;index&lt;/span&gt; &lt;span class="comment"&gt;# GET /people&lt;/span&gt;
  &lt;span class="keyword"&gt;end&lt;/span&gt;

  &lt;span class="keyword"&gt;def &lt;/span&gt;&lt;span class="method"&gt;create&lt;/span&gt;  &lt;span class="comment"&gt;# POST /people&lt;/span&gt;
  &lt;span class="keyword"&gt;end&lt;/span&gt;

  &lt;span class="keyword"&gt;def &lt;/span&gt;&lt;span class="method"&gt;edit&lt;/span&gt; &lt;span class="comment"&gt;# GET /people/1;edit&lt;/span&gt;
  &lt;span class="keyword"&gt;end&lt;/span&gt;

  &lt;span class="keyword"&gt;def &lt;/span&gt;&lt;span class="method"&gt;new&lt;/span&gt; &lt;span class="comment"&gt;# GET /people;new&lt;/span&gt;
  &lt;span class="keyword"&gt;end&lt;/span&gt;

  &lt;span class="keyword"&gt;def &lt;/span&gt;&lt;span class="method"&gt;show&lt;/span&gt;  &lt;span class="comment"&gt;# GET /people/1&lt;/span&gt;
  &lt;span class="keyword"&gt;end&lt;/span&gt;

  &lt;span class="keyword"&gt;def &lt;/span&gt;&lt;span class="method"&gt;destroy&lt;/span&gt;  &lt;span class="comment"&gt;# DELETE /people/1&lt;/span&gt;
  &lt;span class="keyword"&gt;end&lt;/span&gt;

  &lt;span class="keyword"&gt;def &lt;/span&gt;&lt;span class="method"&gt;update&lt;/span&gt;  &lt;span class="comment"&gt;# PUT /people/1&lt;/span&gt;
  &lt;span class="keyword"&gt;end&lt;/span&gt;
&lt;span class="keyword"&gt;end&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;We're moving beyond GET and POST in Rails 1.2 - it'll be done by doing some hacking in terms of setting an HTTP header with the HTTP method we want to actually invoke (since HTML doesn't actually support PUT and DELETE).&lt;/p&gt;

&lt;p&gt;Neat stuff, wqe're trying to move more towards convetniosn in our controller and views - and embracing the single URL approach of an URL as a resource and handling representation in different ways. There's also a new mime type idea, where we start using the URL's extension as the clue as to which format we'd want back. (We pushed to drop extensions long ago because .jsp, .php, or .asp didn't mean anything to the user - they were leaking the implementation language. DHH wants to re-introduce extensions as a means of letting the server know what format we want the representation in). Let's look at an example:&lt;/p&gt;

&lt;div class="typocode"&gt;&lt;pre&gt;&lt;code class="typocode_ruby "&gt;&lt;span class="keyword"&gt;class &lt;/span&gt;&lt;span class="class"&gt;PeopleController&lt;/span&gt; &lt;span class="punct"&gt;&amp;lt;&lt;/span&gt; &lt;span class="constant"&gt;ApplicationController&lt;/span&gt;

  &lt;span class="keyword"&gt;def &lt;/span&gt;&lt;span class="method"&gt;show&lt;/span&gt;
    &lt;span class="attribute"&gt;@person&lt;/span&gt; &lt;span class="punct"&gt;=&lt;/span&gt; &lt;span class="constant"&gt;Person&lt;/span&gt;&lt;span class="punct"&gt;.&lt;/span&gt;&lt;span class="ident"&gt;find&lt;/span&gt;&lt;span class="punct"&gt;(&lt;/span&gt;&lt;span class="ident"&gt;params&lt;/span&gt;&lt;span class="punct"&gt;[&lt;/span&gt;&lt;span class="symbol"&gt;:id&lt;/span&gt;&lt;span class="punct"&gt;])&lt;/span&gt;
    &lt;span class="ident"&gt;respond_to&lt;/span&gt; &lt;span class="keyword"&gt;do&lt;/span&gt; &lt;span class="punct"&gt;|&lt;/span&gt;&lt;span class="ident"&gt;wants&lt;/span&gt;&lt;span class="punct"&gt;|&lt;/span&gt;
      &lt;span class="ident"&gt;wants&lt;/span&gt;&lt;span class="punct"&gt;.&lt;/span&gt;&lt;span class="ident"&gt;html&lt;/span&gt; &lt;span class="comment"&gt;# renders 'show.rhtml' template&lt;/span&gt;
      &lt;span class="ident"&gt;wants&lt;/span&gt;&lt;span class="punct"&gt;.&lt;/span&gt;&lt;span class="ident"&gt;js&lt;/span&gt; &lt;span class="comment"&gt;# renders 'show.rjs' &lt;/span&gt;
      &lt;span class="ident"&gt;wants&lt;/span&gt;&lt;span class="punct"&gt;.&lt;/span&gt;&lt;span class="ident"&gt;xml&lt;/span&gt; &lt;span class="punct"&gt;{&lt;/span&gt; &lt;span class="ident"&gt;render&lt;/span&gt; &lt;span class="symbol"&gt;:xml&lt;/span&gt; &lt;span class="punct"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="attribute"&gt;@person&lt;/span&gt;&lt;span class="punct"&gt;.&lt;/span&gt;&lt;span class="ident"&gt;to_xml&lt;/span&gt; &lt;span class="punct"&gt;}&lt;/span&gt;
    &lt;span class="keyword"&gt;end&lt;/span&gt;
  &lt;span class="keyword"&gt;end&lt;/span&gt;

&lt;span class="keyword"&gt;end&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;Now we can add extensions to recognize using their mime-type and use that to determine what to return to the user.&lt;/p&gt;

&lt;p&gt;Building on all of this, David wants to create a new way of convention over configuration in web services: Enter ActiveResource. When we have REST APIs, everything gets pretty simple, we can start doing things across web services almost like ActiveRecord.&lt;/p&gt;

&lt;div class="typocode"&gt;&lt;pre&gt;&lt;code class="typocode_ruby "&gt;&lt;span class="constant"&gt;Person&lt;/span&gt; &lt;span class="punct"&gt;=&lt;/span&gt; &lt;span class="constant"&gt;ActiveResource&lt;/span&gt;&lt;span class="punct"&gt;::&lt;/span&gt;&lt;span class="constant"&gt;Struct&lt;/span&gt;&lt;span class="punct"&gt;.&lt;/span&gt;&lt;span class="ident"&gt;new&lt;/span&gt; &lt;span class="keyword"&gt;do&lt;/span&gt; &lt;span class="punct"&gt;|&lt;/span&gt;&lt;span class="ident"&gt;p&lt;/span&gt;&lt;span class="punct"&gt;|&lt;/span&gt; 
  &lt;span class="ident"&gt;p&lt;/span&gt;&lt;span class="punct"&gt;.&lt;/span&gt;&lt;span class="ident"&gt;uri&lt;/span&gt; &lt;span class="punct"&gt;&amp;quot;&lt;/span&gt;&lt;span class="string"&gt;http://www.example.com/people&lt;/span&gt;&lt;span class="punct"&gt;&amp;quot;&lt;/span&gt;   
  &lt;span class="ident"&gt;p&lt;/span&gt;&lt;span class="punct"&gt;.&lt;/span&gt;&lt;span class="ident"&gt;credentials&lt;/span&gt; &lt;span class="symbol"&gt;:name&lt;/span&gt; &lt;span class="punct"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="punct"&gt;&amp;quot;&lt;/span&gt;&lt;span class="string"&gt;dhh&lt;/span&gt;&lt;span class="punct"&gt;&amp;quot;,&lt;/span&gt; &lt;span class="symbol"&gt;:password&lt;/span&gt; &lt;span class="punct"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="punct"&gt;&amp;quot;&lt;/span&gt;&lt;span class="string"&gt;secret&lt;/span&gt;&lt;span class="punct"&gt;&amp;quot;&lt;/span&gt; 
&lt;span class="keyword"&gt;end&lt;/span&gt;

&lt;span class="comment"&gt;#Issues GET http://www.example.com/people/1&lt;/span&gt;
&lt;span class="ident"&gt;matz&lt;/span&gt; &lt;span class="punct"&gt;=&lt;/span&gt; &lt;span class="constant"&gt;Person&lt;/span&gt;&lt;span class="punct"&gt;.&lt;/span&gt;&lt;span class="ident"&gt;find&lt;/span&gt;&lt;span class="punct"&gt;(&lt;/span&gt;&lt;span class="number"&gt;1&lt;/span&gt;&lt;span class="punct"&gt;)&lt;/span&gt; 
&lt;span class="ident"&gt;matz&lt;/span&gt;&lt;span class="punct"&gt;.&lt;/span&gt;&lt;span class="ident"&gt;name&lt;/span&gt; &lt;span class="comment"&gt;# =&amp;gt; &amp;quot;Matz&amp;quot;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;No more SOAP, no XML-RPC, no nothing. Just grab the XML representation and translate it back into a Ruby object.&lt;/p&gt;

&lt;p&gt;The talk certainly worried a number of people, but the promise of this is incredible. We're getting nice simple web services, and we're getting URIs that really represent resources, and it's becoming very easy to do multiple representations quickly. Really exciting stuff here.&lt;/p&gt;</description>
      <pubDate>Mon, 26 Jun 2006 17:50:00 +0000</pubDate>
      <guid isPermaLink="false">urn:uuid:91e4e946-24bb-4891-bfcb-ffeafb909a88</guid>
      <author>chris.a.williams@gmail.com (Chris)</author>
      <link>http://cwilliams.textdriven.com/articles/2006/06/26/railsconf-and-rails-1-2</link>
      <category>Programming</category>
      <category>Personal</category>
      <category>Ruby</category>
      <category>Rails</category>
    </item>
    <item>
      <title>Switchtower changes</title>
      <description>&lt;p&gt;Just a note to people who may have run into the same problem: I had been running an old version of Switchtower (from back when it was announced but only available via SVN), and recently upgraded to using the gem. Here's the process of migrating for any of you who had been on the bleeding edge and want to get up to date now:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Remove the vendor/switchtower.rb and vendor/switchtower directory. (I'm assuming this is where you installed it - it should have been.)&lt;/li&gt;
&lt;li&gt;Remove scripts/switchtower&lt;/li&gt;
&lt;li&gt;Download and install the Switchtower gem&lt;/li&gt;
&lt;li&gt;Make a backup copy of config/deploy.rb just in case.&lt;/li&gt;
&lt;li&gt;Run 'switchtower --apply-to &lt;em&gt;your_rails_app_dir&lt;/em&gt;' - Do NOT overwrite your config/deploy.rb recipes! &lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;You should be ready to go. However, the first time I deployed I ran into some weirdness in my custom deploy task. I was referring to current_release to do some symlinking and chmodding. If your old switchtower labelled releases with an integer number and you refer to current_release in your customized tasks, you may run into the same problem. Switchtower was picking up one of my old release directories as the current_release (it was something like 355), I'm assuming because numerically it looks "more recent" than the release it just checked out (into the new timestamp format - something like 20051214XXXXX). I had to rename the old release directories so they wouldn't be picked up as "newer" _and I also modified my task to refer to release_path instead of current_release (since I was using it for the path anyhow).&lt;/p&gt;
</description>
      <pubDate>Mon, 19 Dec 2005 18:11:11 +0000</pubDate>
      <guid isPermaLink="false">urn:uuid:3b5ab3d4-c809-4247-95ae-85ff0b0ff4f8</guid>
      <author>chris.a.williams@gmail.com (Chris)</author>
      <link>http://cwilliams.textdriven.com/articles/2005/12/19/switchtower-changes</link>
      <category>Rails</category>
      <category>Programming</category>
      <category>Ruby</category>
    </item>
    <item>
      <title>First Rochester Rails / Ruby Group Meeting</title>
      <description>&lt;p&gt;This past Thursday saw the very first meeting for the &lt;a href="http://ror.jayunit.net/wiki/show/HomePage"&gt;Rochester Ruby and Rails group&lt;/a&gt;. We met at the &lt;a href="http://www.rit.edu/~gccis/"&gt;Golisano College of Computing and Information Sciences&lt;/a&gt; at &lt;abbr title="Rochester Institute of Technology"&gt;RIT&lt;/abbr&gt;, and had a significant RIT student and alumni contingent. As far as logistics go, it looks like we'll be meeting there regularly on the first and third Thursdays of each month starting up January 19th.&lt;/p&gt;
&lt;p&gt;The group seems to be more dominated by those interested in Rails, but I'm hoping as we go along they'll want to learn more about the underlying langauge - after all, to get some of the significant gains in productivity they'll have to exploit language features like blocks and meta-programming. Happily, the format we decided on devotes time to two interactive discussion/presentations per meeting - one Ruby-language focused and the other Rails focused. It was a bit of a strange night - the Rochester area had a nice coating of slick ice (though I won't call it an "ice storm" because of the true Ice Storm when I was a kid. We lost power for two weeks!) - and almost no one brought their laptops. I suppose we all figured it was just our first meeting so it would be a meet and greet.&lt;/p&gt;
&lt;p&gt;I'm also proud to say that the Rochester Ruby and Rails group has the entire &lt;a href="http://www.radrails.org"&gt;RadRails&lt;/a&gt; development team as members as well as myself from the RDT developers. So we have a nice showing of ruby related &lt;a href="http://www.eclipse.org"&gt;eclipse&lt;/a&gt; experts ;) &lt;/p&gt;</description>
      <pubDate>Mon, 19 Dec 2005 17:25:23 +0000</pubDate>
      <guid isPermaLink="false">urn:uuid:6e8dfb14-5203-4cd4-a978-f2c1035de8a2</guid>
      <author>chris.a.williams@gmail.com (Chris)</author>
      <link>http://cwilliams.textdriven.com/articles/2005/12/19/first-rochester-rails-ruby-group-meeting</link>
      <category>Rails</category>
      <category>Personal</category>
      <category>Programming</category>
      <category>Rochester</category>
      <category>Ruby</category>
    </item>
    <item>
      <title>Rails 1.0 Released</title>
      <description>&lt;p&gt;Hey! &lt;a href="http://weblog.rubyonrails.com/articles/2005/12/13/rails-1-0-party-like-its-one-oh-oh"&gt;Rails 1.0 is now out&lt;/a&gt;. Man, I really should get out from under my rock more often...&lt;/p&gt;</description>
      <pubDate>Wed, 14 Dec 2005 18:36:24 +0000</pubDate>
      <guid isPermaLink="false">urn:uuid:89980c30-076e-4221-ae13-bb78053e5bea</guid>
      <author>chris.a.williams@gmail.com (Chris)</author>
      <link>http://cwilliams.textdriven.com/articles/2005/12/14/rails-1-0-released</link>
      <category>Programming</category>
      <category>Rails</category>
      <category>Ruby</category>
    </item>
    <item>
      <title>New Rails templates for AJAX</title>
      <description>&lt;p&gt;Those of you who are doing some heavy AJAX with Rails should check out &lt;a href="http://www.codyfauser.com/articles/2005/11/20/rails-rjs-templates"&gt;Cody Fauser's post&lt;/a&gt; about the new RJS templates supported by Rails. This allows you to extract all the javascript actions that should take place (effects, insertions, changing HTML dynamically) into a single template file. The concept is a bit different for those used to current templates. The current notion for templates is to produce some XML or HTML to present to the user. These templates allow you to define methods in the controller (that are called by AJAX calls, like link_to_remote) which handle traversing your model, just like in normal actions, but instead of placing your javascript code inside the RHTML template, you place the intended AJAX effects into an RJS template with the same name as the AJAX method/action in the controller. This may sound rather complicated, but it is actually easier to handle complex interactions with AJAX. The current helper methods tended to limit you to updating a single element as a result of various callbacks in the lifecycle of the AJAX method invokation. These templates will make it easy to define all the intended AJAX effects and results into a single place. Please note that this is in Edge Rails - the trunk of the subversion repository for now, so those wanting to try this out will need to download the trunk.&lt;/p&gt;</description>
      <pubDate>Tue, 29 Nov 2005 19:11:21 +0000</pubDate>
      <guid isPermaLink="false">urn:uuid:3cfb905e-9f2a-4daf-ac09-30bd1900438c</guid>
      <author>chris.a.williams@gmail.com (Chris)</author>
      <link>http://cwilliams.textdriven.com/articles/2005/11/29/new-rails-templates-for-ajax</link>
      <category>Rails</category>
      <category>Programming</category>
      <category>Ruby</category>
      <category>Web design</category>
      <category>Rails</category>
      <category>ajax</category>
      <category>template</category>
      <category>rjs</category>
    </item>
  </channel>
</rss>
