<?xml version="1.0" encoding="UTF-8"?>
<?xml-stylesheet href="http://feeds.feedburner.com/~d/styles/atom10full.xsl" type="text/xsl" media="screen"?><?xml-stylesheet href="http://feeds.feedburner.com/~d/styles/itemcontent.css" type="text/css" media="screen"?><feed xmlns="http://www.w3.org/2005/Atom" xmlns:openSearch="http://a9.com/-/spec/opensearchrss/1.0/" xmlns:feedburner="http://rssnamespace.org/feedburner/ext/1.0"><id>tag:blogger.com,1999:blog-3250677721251884719</id><updated>2008-08-21T00:19:24.740+01:00</updated><title type="text">The mysterious ways of Ruby</title><link rel="alternate" type="text/html" href="http://themysteriouswaysofruby.blogspot.com/" /><link rel="http://schemas.google.com/g/2005#feed" type="application/atom+xml" href="http://themysteriouswaysofruby.blogspot.com/feeds/posts/default" /><author><name>Matthias Georgi</name><uri>http://www.blogger.com/profile/16422189308202543523</uri><email>noreply@blogger.com</email></author><generator version="7.00" uri="http://www.blogger.com">Blogger</generator><openSearch:totalResults>6</openSearch:totalResults><openSearch:startIndex>1</openSearch:startIndex><openSearch:itemsPerPage>25</openSearch:itemsPerPage><link rel="self" href="http://feeds.feedburner.com/TheMysteriousWaysOfRuby" type="application/atom+xml" /><entry><id>tag:blogger.com,1999:blog-3250677721251884719.post-7218064578576722224</id><published>2007-05-10T13:37:00.000+01:00</published><updated>2007-05-10T14:12:09.005+01:00</updated><category scheme="http://www.blogger.com/atom/ns#" term="ruby" /><category scheme="http://www.blogger.com/atom/ns#" term="rails" /><category scheme="http://www.blogger.com/atom/ns#" term="search" /><title type="text">Google-like Search Results Helper</title><content type="html">&lt;p&gt;
Representing your search results in a user-friendly way is a common task among web developers. Google's approach is dead simple but really effective. The matching text is highlighted and shown with its context. This can be implemented in less than 20 lines of code which you can include into you helper:
&lt;/p&gt;

&lt;pre style='color:#000000;background:#ffffff;'&gt;&lt;span style='color:#800000; font-weight:bold; '&gt;def&lt;/span&gt; highlight_text&lt;span style='color:#808030; '&gt;(&lt;/span&gt;text, words&lt;span style='color:#808030; '&gt;)&lt;/span&gt;
  tokens &lt;span style='color:#808030; '&gt;=&lt;/span&gt; strip_tags&lt;span style='color:#808030; '&gt;(&lt;/span&gt;text&lt;span style='color:#808030; '&gt;)&lt;/span&gt;&lt;span style='color:#808030; '&gt;.&lt;/span&gt;&lt;span style='color:#400000; '&gt;split&lt;/span&gt;
  sections &lt;span style='color:#808030; '&gt;=&lt;/span&gt; &lt;span style='color:#808030; '&gt;[&lt;/span&gt;&lt;span style='color:#808030; '&gt;]&lt;/span&gt;
  words&lt;span style='color:#808030; '&gt;.&lt;/span&gt;&lt;span style='color:#800000; font-weight:bold; '&gt;each&lt;/span&gt; &lt;span style='color:#800000; font-weight:bold; '&gt;do&lt;/span&gt; |word|
    word &lt;span style='color:#808030; '&gt;=&lt;/span&gt; word&lt;span style='color:#808030; '&gt;.&lt;/span&gt;downcase
    tokens&lt;span style='color:#808030; '&gt;.&lt;/span&gt;each_with_index &lt;span style='color:#800000; font-weight:bold; '&gt;do&lt;/span&gt; |token, i|
&lt;span style='color:#800000; font-weight:bold; '&gt;      if&lt;/span&gt; token&lt;span style='color:#808030; '&gt;.&lt;/span&gt;downcase&lt;span style='color:#808030; '&gt;.&lt;/span&gt;include? word
        section &lt;span style='color:#808030; '&gt;=&lt;/span&gt; tokens&lt;span style='color:#808030; '&gt;[&lt;/span&gt;i&lt;span style='color:#808030; '&gt;-&lt;/span&gt;&lt;span style='color:#008c00; '&gt;10&lt;/span&gt;, &lt;span style='color:#008c00; '&gt;20&lt;/span&gt;&lt;span style='color:#808030; '&gt;]&lt;/span&gt;&lt;span style='color:#808030; '&gt;.&lt;/span&gt;join&lt;span style='color:#808030; '&gt;(&lt;/span&gt;&lt;span style='color:#0000e6; '&gt;' '&lt;/span&gt;&lt;span style='color:#808030; '&gt;)&lt;/span&gt;
        words&lt;span style='color:#808030; '&gt;.&lt;/span&gt;&lt;span style='color:#800000; font-weight:bold; '&gt;each&lt;/span&gt; &lt;span style='color:#800000; font-weight:bold; '&gt;do&lt;/span&gt; |word|
          section &lt;span style='color:#808030; '&gt;=&lt;/span&gt; highlight section, word
        &lt;span style='color:#800000; font-weight:bold; '&gt;end&lt;/span&gt;
        sections &amp;lt;&amp;lt; section &lt;span style='color:#808030; '&gt;+&lt;/span&gt; &lt;span style='color:#0000e6; '&gt;' ... '&lt;/span&gt;
        &lt;span style='color:#800000; font-weight:bold; '&gt;break&lt;/span&gt; &lt;span style='color:#800000; font-weight:bold; '&gt;if&lt;/span&gt; sections&lt;span style='color:#808030; '&gt;.&lt;/span&gt;size &gt; &lt;span style='color:#008c00; '&gt;3&lt;/span&gt;          
      &lt;span style='color:#800000; font-weight:bold; '&gt;end&lt;/span&gt;
    &lt;span style='color:#800000; font-weight:bold; '&gt;end&lt;/span&gt;
  &lt;span style='color:#800000; font-weight:bold; '&gt;end&lt;/span&gt;
  sections&lt;span style='color:#808030; '&gt;.&lt;/span&gt;join
&lt;span style='color:#800000; font-weight:bold; '&gt;end&lt;/span&gt;
&lt;/pre&gt;


&lt;p&gt;
First we remove the html tags of the text and split the text into tokens. Then we iterate over the search words and for each matching token we generate a section, which contains the highlighted word. This is done until we hav at least 4 sections, which are finally joined and ready to be included into the search result template.
&lt;/p&gt;</content><link rel="alternate" type="text/html" href="http://feeds.feedburner.com/~r/TheMysteriousWaysOfRuby/~3/115608669/google-like-search-results-helper.html" title="Google-like Search Results Helper" /><link rel="replies" type="text/html" href="http://www.blogger.com/comment.g?blogID=3250677721251884719&amp;postID=7218064578576722224" title="0 Comments" /><link rel="replies" type="application/atom+xml" href="http://themysteriouswaysofruby.blogspot.com/feeds/7218064578576722224/comments/default" title="Post Comments" /><link rel="self" type="application/atom+xml" href="http://themysteriouswaysofruby.blogspot.com/feeds/posts/default/7218064578576722224" /><link rel="edit" type="application/atom+xml" href="http://www.blogger.com/feeds/3250677721251884719/posts/default/7218064578576722224" /><author><name>Matthias Georgi</name><uri>http://www.blogger.com/profile/16422189308202543523</uri><email>noreply@blogger.com</email></author><feedburner:origLink>http://themysteriouswaysofruby.blogspot.com/2007/05/google-like-search-results-helper.html</feedburner:origLink></entry><entry><id>tag:blogger.com,1999:blog-3250677721251884719.post-7563992737660723733</id><published>2007-04-18T10:07:00.000+01:00</published><updated>2007-04-19T10:25:43.977+01:00</updated><title type="text">DRY Up Your Url Helpers</title><content type="html">&lt;p&gt;This tutorial shows you how to simplify url generation in combination with RESTful resources by extending the url_for helper. This approach will also work with nested routes and other helpers like form_tag and link_to.&lt;/p&gt;
&lt;p&gt;One of the concepts of REST is: each resource has its own unique URI. We will enhance the url_for helper to generate this unique URI for an arbitrary record.&lt;/p&gt;
&lt;p&gt;First we need a simple example. We have 2 models: User and Article. For our url generation to work we have to add following code to our models: &lt;/p&gt;&lt;pre style="BACKGROUND: rgb(255,255,255) 0% 50%; COLOR: rgb(0,0,0); moz-background-clip: -moz-initial; moz-background-origin: -moz-initial; moz-background-inline-policy: -moz-initial"&gt;&lt;span style="FONT-WEIGHT: bold; COLOR: rgb(128,0,0)"&gt;class&lt;/span&gt; User
  has_many :articles

&lt;span style="FONT-WEIGHT: bold; COLOR: rgb(128,0,0)"&gt;  def&lt;/span&gt; to_params
&lt;span style="COLOR: rgb(128,0,128)"&gt;    {&lt;/span&gt;:id &lt;span style="COLOR: rgb(128,128,48)"&gt;=&lt;/span&gt;&amp;gt; permalink&lt;span style="COLOR: rgb(128,0,128)"&gt;}&lt;/span&gt;
&lt;span style="FONT-WEIGHT: bold; COLOR: rgb(128,0,0)"&gt;  end&lt;/span&gt;
&lt;span style="FONT-WEIGHT: bold; COLOR: rgb(128,0,0)"&gt;end&lt;/span&gt;

&lt;span style="FONT-WEIGHT: bold; COLOR: rgb(128,0,0)"&gt;class&lt;/span&gt; Article
  belongs_to :user

&lt;span style="FONT-WEIGHT: bold; COLOR: rgb(128,0,0)"&gt;  def&lt;/span&gt; to_params
&lt;span style="COLOR: rgb(128,0,128)"&gt;    {&lt;/span&gt;:user_id &lt;span style="COLOR: rgb(128,128,48)"&gt;=&lt;/span&gt;&amp;gt; user&lt;span style="COLOR: rgb(128,128,48)"&gt;.&lt;/span&gt;permalink, :id &lt;span style="COLOR: rgb(128,128,48)"&gt;=&lt;/span&gt;&amp;gt; permalink&lt;span style="COLOR: rgb(128,0,128)"&gt;}&lt;/span&gt;
&lt;span style="FONT-WEIGHT: bold; COLOR: rgb(128,0,0)"&gt;  end&lt;/span&gt;
&lt;span style="FONT-WEIGHT: bold; COLOR: rgb(128,0,0)"&gt;end&lt;/span&gt;
&lt;/pre&gt;
&lt;p&gt;This is necessary for nested routes to play nicely with our url generation code. We are now able to find the parameters for each record to generate an unique URL. &lt;/p&gt;
&lt;p&gt;In a previous post I demonstrated the use of meaningful urls. We are going now the same way. &lt;/p&gt;
&lt;p&gt;Users are identified by an URL like:&lt;/p&gt;
&lt;p&gt;/users/matthias-georgi&lt;/p&gt;
&lt;p&gt;Each user may write articles, which are located at:&lt;/p&gt;
&lt;p&gt;/users/matthias-georgi/articles&lt;/p&gt;
&lt;p&gt;If I want to write a new article, I will use this URL:&lt;/p&gt;
&lt;p&gt;/users/matthias-georgi/articles/new&lt;/p&gt;
&lt;p&gt;Editing an existing article would end up on this URL:&lt;/p&gt;
&lt;p&gt;/users/matthias-georgi/articles/my-first-post;edit&lt;/p&gt;
&lt;p&gt;For nested resources to get working you define in config/routes.rb: &lt;/p&gt;&lt;pre style="BACKGROUND: rgb(255,255,255) 0% 50%; COLOR: rgb(0,0,0); moz-background-clip: -moz-initial; moz-background-origin: -moz-initial; moz-background-inline-policy: -moz-initial"&gt;map&lt;span style="COLOR: rgb(128,128,48)"&gt;.&lt;/span&gt;resources :users &lt;span style="FONT-WEIGHT: bold; COLOR: rgb(128,0,0)"&gt;do&lt;/span&gt; |user|
  user&lt;span style="COLOR: rgb(128,128,48)"&gt;.&lt;/span&gt;resources :articles
&lt;span style="FONT-WEIGHT: bold; COLOR: rgb(128,0,0)"&gt;end&lt;/span&gt;
&lt;/pre&gt;
&lt;p&gt;The traditional way to generate urls is to call the resource helpers:&lt;/p&gt;
&lt;p&gt;article_url(article.user, article)&lt;/p&gt;
&lt;p&gt;This is redundant, as the article already knows its user. &lt;/p&gt;
&lt;p&gt;Add following module into your lib folder and include the module in both your application controller and application helper. The most important bit is the url_for method. It will automatically generate the right url for your resource.&lt;/p&gt;&lt;pre style="BACKGROUND: rgb(255,255,255) 0% 50%; COLOR: rgb(0,0,0); moz-background-clip: -moz-initial; moz-background-origin: -moz-initial; moz-background-inline-policy: -moz-initial"&gt;&lt;span style="FONT-WEIGHT: bold; COLOR: rgb(128,0,0)"&gt;module&lt;/span&gt; ResourceHelper

&lt;span style="FONT-WEIGHT: bold; COLOR: rgb(128,0,0)"&gt;  def&lt;/span&gt; plural_class_name&lt;span style="COLOR: rgb(128,128,48)"&gt;(&lt;/span&gt;record&lt;span style="COLOR: rgb(128,128,48)"&gt;)&lt;/span&gt;
    singular_class_name(record)&lt;span style="COLOR: rgb(128,128,48)"&gt;.&lt;/span&gt;pluralize
&lt;span style="FONT-WEIGHT: bold; COLOR: rgb(128,0,0)"&gt;  end&lt;/span&gt;

&lt;span style="FONT-WEIGHT: bold; COLOR: rgb(128,0,0)"&gt;  def&lt;/span&gt; singular_class_name&lt;span style="COLOR: rgb(128,128,48)"&gt;(&lt;/span&gt;record&lt;span style="COLOR: rgb(128,128,48)"&gt;)&lt;/span&gt;
    record&lt;span style="COLOR: rgb(128,128,48)"&gt;.&lt;/span&gt;&lt;span style="FONT-WEIGHT: bold; COLOR: rgb(128,0,0)"&gt;class&lt;/span&gt;&lt;span style="COLOR: rgb(128,128,48)"&gt;.&lt;/span&gt;name&lt;span style="COLOR: rgb(128,128,48)"&gt;.&lt;/span&gt;underscore.tr(&lt;span style="COLOR: rgb(0,0,230)"&gt;'/'&lt;/span&gt;, &lt;span style="COLOR: rgb(0,0,230)"&gt;'_'&lt;/span&gt;)
&lt;span style="FONT-WEIGHT: bold; COLOR: rgb(128,0,0)"&gt;  end&lt;/span&gt;

&lt;span style="FONT-WEIGHT: bold; COLOR: rgb(128,0,0)"&gt;  def&lt;/span&gt; params_for&lt;span style="COLOR: rgb(128,128,48)"&gt;(&lt;/span&gt;record&lt;span style="COLOR: rgb(128,128,48)"&gt;)&lt;/span&gt;
&lt;span style="FONT-WEIGHT: bold; COLOR: rgb(128,0,0)"&gt;    if&lt;/span&gt; record&lt;span style="COLOR: rgb(128,128,48)"&gt;.&lt;/span&gt;respond_to?&lt;span style="COLOR: rgb(128,128,48)"&gt;(&lt;/span&gt;:to_params&lt;span style="COLOR: rgb(128,128,48)"&gt;)&lt;/span&gt;
      record&lt;span style="COLOR: rgb(128,128,48)"&gt;.&lt;/span&gt;to_params
&lt;span style="FONT-WEIGHT: bold; COLOR: rgb(128,0,0)"&gt;    else&lt;/span&gt;
     &lt;span style="COLOR: rgb(128,0,128)"&gt; {&lt;/span&gt;:id &lt;span style="COLOR: rgb(128,128,48)"&gt;=&lt;/span&gt;&amp;gt; record&lt;span style="COLOR: rgb(128,128,48)"&gt;.&lt;/span&gt;to_param&lt;span style="COLOR: rgb(128,0,128)"&gt;}&lt;/span&gt;
&lt;span style="FONT-WEIGHT: bold; COLOR: rgb(128,0,0)"&gt;    end&lt;/span&gt;
&lt;span style="FONT-WEIGHT: bold; COLOR: rgb(128,0,0)"&gt;  end&lt;/span&gt;

&lt;span style="FONT-WEIGHT: bold; COLOR: rgb(128,0,0)"&gt;  def&lt;/span&gt; collection_url&lt;span style="COLOR: rgb(128,128,48)"&gt;(&lt;/span&gt;collection, record, options&lt;span style="COLOR: rgb(128,128,48)"&gt;)&lt;/span&gt;
&lt;span style="FONT-WEIGHT: bold; COLOR: rgb(128,0,0)"&gt;    if&lt;/span&gt; record
      params &lt;span style="COLOR: rgb(128,128,48)"&gt;=&lt;/span&gt; params_for&lt;span style="COLOR: rgb(128,128,48)"&gt;(&lt;/span&gt;record&lt;span style="COLOR: rgb(128,128,48)"&gt;)&lt;/span&gt;
      params&lt;span style="COLOR: rgb(128,128,48)"&gt;[&lt;/span&gt;&lt;span style="COLOR: rgb(0,0,230)"&gt;"#{singular_class_name(record)}_id"&lt;/span&gt;&lt;span style="COLOR: rgb(128,128,48)"&gt;.&lt;/span&gt;to_sym&lt;span style="COLOR: rgb(128,128,48)"&gt;]&lt;/span&gt; &lt;span style="COLOR: rgb(128,128,48)"&gt;=&lt;/span&gt; params&lt;span style="COLOR: rgb(128,128,48)"&gt;.&lt;/span&gt;delete&lt;span style="COLOR: rgb(128,128,48)"&gt;(&lt;/span&gt;:id&lt;span style="COLOR: rgb(128,128,48)"&gt;)&lt;/span&gt;
      url_for options&lt;span style="COLOR: rgb(128,128,48)"&gt;.&lt;/span&gt;merge&lt;span style="COLOR: rgb(128,128,48)"&gt;(&lt;/span&gt;params&lt;span style="COLOR: rgb(128,128,48)"&gt;)&lt;/span&gt;&lt;span style="COLOR: rgb(128,128,48)"&gt;.&lt;/span&gt;merge&lt;span style="COLOR: rgb(128,128,48)"&gt;(&lt;/span&gt;:controller &lt;span style="COLOR: rgb(128,128,48)"&gt;=&lt;/span&gt;&amp;gt; collection&lt;span style="COLOR: rgb(128,128,48)"&gt;)&lt;/span&gt;
    &lt;span style="FONT-WEIGHT: bold; COLOR: rgb(128,0,0)"&gt;else&lt;/span&gt;
      url_for options&lt;span style="COLOR: rgb(128,128,48)"&gt;.&lt;/span&gt;merge&lt;span style="COLOR: rgb(128,128,48)"&gt;(&lt;/span&gt;:controller &lt;span style="COLOR: rgb(128,128,48)"&gt;=&lt;/span&gt;&amp;gt; collection&lt;span style="COLOR: rgb(128,128,48)"&gt;)&lt;/span&gt;
    &lt;span style="FONT-WEIGHT: bold; COLOR: rgb(128,0,0)"&gt;end&lt;/span&gt;
&lt;span style="FONT-WEIGHT: bold; COLOR: rgb(128,0,0)"&gt;  end&lt;/span&gt;

&lt;span style="FONT-WEIGHT: bold; COLOR: rgb(128,0,0)"&gt;  def&lt;/span&gt; member_url&lt;span style="COLOR: rgb(128,128,48)"&gt;(&lt;/span&gt;record, options&lt;span style="COLOR: rgb(128,128,48)"&gt;)&lt;/span&gt;
    url_for options&lt;span style="COLOR: rgb(128,128,48)"&gt;.&lt;/span&gt;merge&lt;span style="COLOR: rgb(128,128,48)"&gt;(&lt;/span&gt;params_for&lt;span style="COLOR: rgb(128,128,48)"&gt;(&lt;/span&gt;record&lt;span style="COLOR: rgb(128,128,48)"&gt;)&lt;/span&gt;&lt;span style="COLOR: rgb(128,128,48)"&gt;)&lt;/span&gt;&lt;span style="COLOR: rgb(128,128,48)"&gt;.&lt;/span&gt;merge&lt;span style="COLOR: rgb(128,128,48)"&gt;(&lt;/span&gt;:controller &lt;span style="COLOR: rgb(128,128,48)"&gt;=&lt;/span&gt;&amp;gt; plural_class_name&lt;span style="COLOR: rgb(128,128,48)"&gt;(&lt;/span&gt;record&lt;span style="COLOR: rgb(128,128,48)"&gt;)&lt;/span&gt;&lt;span style="COLOR: rgb(128,128,48)"&gt;)&lt;/span&gt;
&lt;span style="FONT-WEIGHT: bold; COLOR: rgb(128,0,0)"&gt;  end&lt;/span&gt;

&lt;span style="FONT-WEIGHT: bold; COLOR: rgb(128,0,0)"&gt;  def&lt;/span&gt; url_for&lt;span style="COLOR: rgb(128,128,48)"&gt;(&lt;/span&gt;&lt;span style="COLOR: rgb(128,128,48)"&gt;*&lt;/span&gt;args&lt;span style="COLOR: rgb(128,128,48)"&gt;)&lt;/span&gt;
&lt;span style="FONT-WEIGHT: bold; COLOR: rgb(128,0,0)"&gt;    if&lt;/span&gt; &lt;span style="COLOR: rgb(128,128,48)"&gt;[&lt;/span&gt;&lt;span style="COLOR: rgb(64,0,0)"&gt;String&lt;/span&gt;, Hash&lt;span style="COLOR: rgb(128,128,48)"&gt;]&lt;/span&gt;&lt;span style="COLOR: rgb(128,128,48)"&gt;.&lt;/span&gt;any? &lt;span style="COLOR: rgb(128,0,128)"&gt;{&lt;/span&gt;|type| args&lt;span style="COLOR: rgb(128,128,48)"&gt;.&lt;/span&gt;first&lt;span style="COLOR: rgb(128,128,48)"&gt;.&lt;/span&gt;is_a? type &lt;span style="COLOR: rgb(128,0,128)"&gt;}&lt;/span&gt;
      &lt;span style="FONT-WEIGHT: bold; COLOR: rgb(128,0,0)"&gt;super&lt;/span&gt;&lt;span style="COLOR: rgb(128,128,48)"&gt;(&lt;/span&gt;&lt;span style="COLOR: rgb(128,128,48)"&gt;*&lt;/span&gt;args&lt;span style="COLOR: rgb(128,128,48)"&gt;)&lt;/span&gt;
&lt;span style="FONT-WEIGHT: bold; COLOR: rgb(128,0,0)"&gt;    else&lt;/span&gt;
&lt;span style="FONT-WEIGHT: bold; COLOR: rgb(128,0,0)"&gt;      if&lt;/span&gt; args&lt;span style="COLOR: rgb(128,128,48)"&gt;[&lt;/span&gt;&lt;span style="COLOR: rgb(0,140,0)"&gt;0&lt;/span&gt;&lt;span style="COLOR: rgb(128,128,48)"&gt;]&lt;/span&gt;&lt;span style="COLOR: rgb(128,128,48)"&gt;.&lt;/span&gt;is_a?&lt;span style="COLOR: rgb(128,128,48)"&gt;(&lt;/span&gt;Symbol&lt;span style="COLOR: rgb(128,128,48)"&gt;)&lt;/span&gt;
        collection_url&lt;span style="COLOR: rgb(128,128,48)"&gt;(&lt;/span&gt;args&lt;span style="COLOR: rgb(128,128,48)"&gt;[&lt;/span&gt;&lt;span style="COLOR: rgb(0,140,0)"&gt;0&lt;/span&gt;&lt;span style="COLOR: rgb(128,128,48)"&gt;]&lt;/span&gt;, args&lt;span style="COLOR: rgb(128,128,48)"&gt;[&lt;/span&gt;&lt;span style="COLOR: rgb(0,140,0)"&gt;1&lt;/span&gt;&lt;span style="COLOR: rgb(128,128,48)"&gt;]&lt;/span&gt;, :action &lt;span style="COLOR: rgb(128,128,48)"&gt;=&lt;/span&gt;&amp;gt; &lt;span style="COLOR: rgb(0,0,230)"&gt;'index'&lt;/span&gt;&lt;span style="COLOR: rgb(128,128,48)"&gt;)&lt;/span&gt;
      &lt;span style="FONT-WEIGHT: bold; COLOR: rgb(128,0,0)"&gt;else&lt;/span&gt;
        member_url&lt;span style="COLOR: rgb(128,128,48)"&gt;(&lt;/span&gt;args&lt;span style="COLOR: rgb(128,128,48)"&gt;.&lt;/span&gt;first, :action &lt;span style="COLOR: rgb(128,128,48)"&gt;=&lt;/span&gt;&amp;gt; &lt;span style="COLOR: rgb(0,0,230)"&gt;'show'&lt;/span&gt;&lt;span style="COLOR: rgb(128,128,48)"&gt;)&lt;/span&gt;
      &lt;span style="FONT-WEIGHT: bold; COLOR: rgb(128,0,0)"&gt;end&lt;/span&gt;
&lt;span style="FONT-WEIGHT: bold; COLOR: rgb(128,0,0)"&gt;    end&lt;/span&gt;
&lt;span style="FONT-WEIGHT: bold; COLOR: rgb(128,0,0)"&gt;  end&lt;/span&gt;

&lt;span style="FONT-WEIGHT: bold; COLOR: rgb(128,0,0)"&gt;  def&lt;/span&gt; new_url_for&lt;span style="COLOR: rgb(128,128,48)"&gt;(&lt;/span&gt;collection, record&lt;span style="COLOR: rgb(128,128,48)"&gt;=&lt;/span&gt;&lt;span style="FONT-WEIGHT: bold; COLOR: rgb(128,0,0)"&gt;nil&lt;/span&gt;&lt;span style="COLOR: rgb(128,128,48)"&gt;)&lt;/span&gt;
    collection_url&lt;span style="COLOR: rgb(128,128,48)"&gt;(&lt;/span&gt;collection, record, :action &lt;span style="COLOR: rgb(128,128,48)"&gt;=&lt;/span&gt;&amp;gt; &lt;span style="COLOR: rgb(0,0,230)"&gt;'new'&lt;/span&gt;&lt;span style="COLOR: rgb(128,128,48)"&gt;)&lt;/span&gt;
&lt;span style="FONT-WEIGHT: bold; COLOR: rgb(128,0,0)"&gt;  end&lt;/span&gt;

&lt;span style="FONT-WEIGHT: bold; COLOR: rgb(128,0,0)"&gt;  def&lt;/span&gt; edit_url_for&lt;span style="COLOR: rgb(128,128,48)"&gt;(&lt;/span&gt;record&lt;span style="COLOR: rgb(128,128,48)"&gt;)&lt;/span&gt;
    member_url&lt;span style="COLOR: rgb(128,128,48)"&gt;(&lt;/span&gt;record, :action &lt;span style="COLOR: rgb(128,128,48)"&gt;=&lt;/span&gt;&amp;gt; &lt;span style="COLOR: rgb(0,0,230)"&gt;'edit'&lt;/span&gt;&lt;span style="COLOR: rgb(128,128,48)"&gt;)&lt;/span&gt;
&lt;span style="FONT-WEIGHT: bold; COLOR: rgb(128,0,0)"&gt;  end&lt;/span&gt;&lt;/pre&gt;&lt;pre style="BACKGROUND: rgb(255,255,255) 0% 50%; COLOR: rgb(0,0,0); moz-background-clip: -moz-initial; moz-background-origin: -moz-initial; moz-background-inline-policy: -moz-initial"&gt;&lt;pre style="BACKGROUND: rgb(255,255,255) 0% 50%; COLOR: rgb(0,0,0); moz-background-clip: -moz-initial; moz-background-origin: -moz-initial; moz-background-inline-policy: -moz-initial"&gt;&lt;span style="FONT-WEIGHT: bold; COLOR: rgb(128,0,0)"&gt;  def&lt;/span&gt; path_for&lt;span style="COLOR: rgb(128,128,48)"&gt;(&lt;/span&gt;&lt;span style="COLOR: rgb(128,128,48)"&gt;*&lt;/span&gt;args&lt;span style="COLOR: rgb(128,128,48)"&gt;)&lt;/span&gt;
&lt;span style="FONT-WEIGHT: bold; COLOR: rgb(128,0,0)"&gt;    if&lt;/span&gt; args&lt;span style="COLOR: rgb(128,128,48)"&gt;[&lt;/span&gt;&lt;span style="COLOR: rgb(0,140,0)"&gt;0&lt;/span&gt;&lt;span style="COLOR: rgb(128,128,48)"&gt;]&lt;/span&gt;&lt;span style="COLOR: rgb(128,128,48)"&gt;.&lt;/span&gt;is_a?&lt;span style="COLOR: rgb(128,128,48)"&gt;(&lt;/span&gt;Symbol&lt;span style="COLOR: rgb(128,128,48)"&gt;)&lt;/span&gt;
      collection_url&lt;span style="COLOR: rgb(128,128,48)"&gt;(&lt;/span&gt;args&lt;span style="COLOR: rgb(128,128,48)"&gt;[&lt;/span&gt;&lt;span style="COLOR: rgb(0,140,0)"&gt;0&lt;/span&gt;&lt;span style="COLOR: rgb(128,128,48)"&gt;]&lt;/span&gt;, args&lt;span style="COLOR: rgb(128,128,48)"&gt;[&lt;/span&gt;&lt;span style="COLOR: rgb(0,140,0)"&gt;1&lt;/span&gt;&lt;span style="COLOR: rgb(128,128,48)"&gt;]&lt;/span&gt;, :action &lt;span style="COLOR: rgb(128,128,48)"&gt;=&lt;/span&gt;&amp;gt; &lt;span style="COLOR: rgb(0,0,230)"&gt;'index'&lt;/span&gt;, :only_path &lt;span style="COLOR: rgb(128,128,48)"&gt;=&lt;/span&gt;&amp;gt; &lt;span style="FONT-WEIGHT: bold; COLOR: rgb(128,0,0)"&gt;true&lt;/span&gt;&lt;span style="COLOR: rgb(128,128,48)"&gt;)&lt;/span&gt;
    &lt;span style="FONT-WEIGHT: bold; COLOR: rgb(128,0,0)"&gt;else&lt;/span&gt;
      member_url&lt;span style="COLOR: rgb(128,128,48)"&gt;(&lt;/span&gt;args&lt;span style="COLOR: rgb(128,128,48)"&gt;.&lt;/span&gt;first, :action &lt;span style="COLOR: rgb(128,128,48)"&gt;=&lt;/span&gt;&amp;gt; &lt;span style="COLOR: rgb(0,0,230)"&gt;'show'&lt;/span&gt;, :only_path &lt;span style="COLOR: rgb(128,128,48)"&gt;=&lt;/span&gt;&amp;gt; &lt;span style="FONT-WEIGHT: bold; COLOR: rgb(128,0,0)"&gt;true&lt;/span&gt;&lt;span style="COLOR: rgb(128,128,48)"&gt;)&lt;/span&gt;
    &lt;span style="FONT-WEIGHT: bold; COLOR: rgb(128,0,0)"&gt;end&lt;/span&gt;
&lt;span style="FONT-WEIGHT: bold; COLOR: rgb(128,0,0)"&gt;  end&lt;/span&gt;

&lt;span style="FONT-WEIGHT: bold; COLOR: rgb(128,0,0)"&gt;  def&lt;/span&gt; new_path_for&lt;span style="COLOR: rgb(128,128,48)"&gt;(&lt;/span&gt;collection, record&lt;span style="COLOR: rgb(128,128,48)"&gt;=&lt;/span&gt;&lt;span style="FONT-WEIGHT: bold; COLOR: rgb(128,0,0)"&gt;nil&lt;/span&gt;&lt;span style="COLOR: rgb(128,128,48)"&gt;)&lt;/span&gt;
    collection_url&lt;span style="COLOR: rgb(128,128,48)"&gt;(&lt;/span&gt;collection, record, :action &lt;span style="COLOR: rgb(128,128,48)"&gt;=&lt;/span&gt;&amp;gt; &lt;span style="COLOR: rgb(0,0,230)"&gt;'new'&lt;/span&gt;, :only_path &lt;span style="COLOR: rgb(128,128,48)"&gt;=&lt;/span&gt;&amp;gt; &lt;span style="FONT-WEIGHT: bold; COLOR: rgb(128,0,0)"&gt;true&lt;/span&gt;&lt;span style="COLOR: rgb(128,128,48)"&gt;)&lt;/span&gt;
&lt;span style="FONT-WEIGHT: bold; COLOR: rgb(128,0,0)"&gt;  end&lt;/span&gt;

&lt;span style="FONT-WEIGHT: bold; COLOR: rgb(128,0,0)"&gt;  def&lt;/span&gt; edit_path_for&lt;span style="COLOR: rgb(128,128,48)"&gt;(&lt;/span&gt;record&lt;span style="COLOR: rgb(128,128,48)"&gt;)&lt;/span&gt;
    member_url&lt;span style="COLOR: rgb(128,128,48)"&gt;(&lt;/span&gt;record, :action &lt;span style="COLOR: rgb(128,128,48)"&gt;=&lt;/span&gt;&amp;gt; &lt;span style="COLOR: rgb(0,0,230)"&gt;'edit'&lt;/span&gt;, :only_path &lt;span style="COLOR: rgb(128,128,48)"&gt;=&lt;/span&gt;&amp;gt; &lt;span style="FONT-WEIGHT: bold; COLOR: rgb(128,0,0)"&gt;true&lt;/span&gt;&lt;span style="COLOR: rgb(128,128,48)"&gt;)&lt;/span&gt;
&lt;span style="FONT-WEIGHT: bold; COLOR: rgb(128,0,0)"&gt;  end&lt;/span&gt;
&lt;/pre&gt;&lt;span style="FONT-WEIGHT: bold; COLOR: rgb(128,0,0)"&gt;end&lt;/span&gt;
&lt;/pre&gt;
&lt;p&gt;So how can you use this stuff actually?&lt;/p&gt;
&lt;p&gt;It is pretty easy: just pass the record instead of the url hash and the unique url will be generated automatically.&lt;/p&gt;
&lt;p&gt;The url of a collection is treated differently. You have to pass the name of the collection, which is the controller name. For nested resources you have to pass additionally the record, the collection is belonging to.&lt;/p&gt;
&lt;p&gt;Some examples: &lt;/p&gt;&lt;pre style="BACKGROUND: rgb(255,255,255) 0% 50%; COLOR: rgb(0,0,0); moz-background-clip: -moz-initial; moz-background-origin: -moz-initial; moz-background-inline-policy: -moz-initial"&gt;new_path_for&lt;span style="COLOR: rgb(128,128,48)"&gt;(&lt;/span&gt;:users&lt;span style="COLOR: rgb(128,128,48)"&gt;)&lt;/span&gt;          &lt;span style="COLOR: rgb(105,105,105)"&gt;# =&amp;gt; '/users/new'&lt;/span&gt;
path_for&lt;span style="COLOR: rgb(128,128,48)"&gt;(&lt;/span&gt;user&lt;span style="COLOR: rgb(128,128,48)"&gt;)&lt;/span&gt;                &lt;span style="COLOR: rgb(105,105,105)"&gt;# =&amp;gt; '/users/harald'&lt;/span&gt;
edit_path_for&lt;span style="COLOR: rgb(128,128,48)"&gt;(&lt;/span&gt;user&lt;span style="COLOR: rgb(128,128,48)"&gt;)   &lt;/span&gt;        &lt;span style="COLOR: rgb(105,105,105)"&gt;# =&amp;gt; '/users/harald;edit'&lt;/span&gt;
path_for&lt;span style="COLOR: rgb(128,128,48)"&gt;(&lt;/span&gt;:articles, user&lt;span style="COLOR: rgb(128,128,48)"&gt;)&lt;/span&gt;     &lt;span style="COLOR: rgb(105,105,105)"&gt;# =&amp;gt; '/users/harald/articles'&lt;/span&gt;
new_path_for&lt;span style="COLOR: rgb(128,128,48)"&gt;(&lt;/span&gt;:articles, user&lt;span style="COLOR: rgb(128,128,48)"&gt;)&lt;/span&gt; &lt;span style="COLOR: rgb(105,105,105)"&gt;# =&amp;gt; '/users/harald/articles/new'&lt;/span&gt;
path_for&lt;span style="COLOR: rgb(128,128,48)"&gt;(&lt;/span&gt;article&lt;span style="COLOR: rgb(128,128,48)"&gt;)&lt;/span&gt;             &lt;span style="COLOR: rgb(105,105,105)"&gt;# =&amp;gt; '/users/harald/articles/article-1'&lt;/span&gt;
edit_path_for&lt;span style="COLOR: rgb(128,128,48)"&gt;(&lt;/span&gt;article&lt;span style="COLOR: rgb(128,128,48)"&gt;)&lt;/span&gt;        &lt;span style="COLOR: rgb(105,105,105)"&gt;# =&amp;gt; '/users/harald/articles/article-1;edit'&lt;/span&gt;
&lt;/pre&gt;&lt;pre style="BACKGROUND: rgb(255,255,255) 0% 50%; COLOR: rgb(0,0,0); moz-background-clip: -moz-initial; moz-background-origin: -moz-initial; moz-background-inline-policy: -moz-initial"&gt;# This works for helpers like url_for, form_tag or link_to. &lt;/pre&gt;&lt;pre style="BACKGROUND: rgb(255,255,255) 0% 50%; COLOR: rgb(0,0,0); moz-background-clip: -moz-initial; moz-background-origin: -moz-initial; moz-background-inline-policy: -moz-initial"&gt;link_to article&lt;span style="COLOR: rgb(128,128,48)"&gt;.&lt;/span&gt;title, article
form_tag :articles
form_tag article, :method &lt;span style="COLOR: rgb(128,128,48)"&gt;=&lt;/span&gt;&amp;gt; &lt;span style="COLOR: rgb(0,0,230)"&gt;'put'
&lt;/span&gt;&lt;/pre&gt;
&lt;p&gt;If anybody is interested I will release this stuff as plugin. I think other helpers could benefit as well as you can pass your records around and each helper may generate the appropriate url.&lt;/p&gt;</content><link rel="alternate" type="text/html" href="http://feeds.feedburner.com/~r/TheMysteriousWaysOfRuby/~3/110198611/dry-up-your-url-helpers.html" title="DRY Up Your Url Helpers" /><link rel="replies" type="text/html" href="http://www.blogger.com/comment.g?blogID=3250677721251884719&amp;postID=7563992737660723733" title="3 Comments" /><link rel="replies" type="application/atom+xml" href="http://themysteriouswaysofruby.blogspot.com/feeds/7563992737660723733/comments/default" title="Post Comments" /><link rel="self" type="application/atom+xml" href="http://themysteriouswaysofruby.blogspot.com/feeds/posts/default/7563992737660723733" /><link rel="edit" type="application/atom+xml" href="http://www.blogger.com/feeds/3250677721251884719/posts/default/7563992737660723733" /><author><name>Matthias Georgi</name><uri>http://www.blogger.com/profile/16422189308202543523</uri><email>noreply@blogger.com</email></author><feedburner:origLink>http://themysteriouswaysofruby.blogspot.com/2007/04/dry-up-your-url-helpers.html</feedburner:origLink></entry><entry><id>tag:blogger.com,1999:blog-3250677721251884719.post-8283404202880311190</id><published>2007-04-10T10:36:00.000+01:00</published><updated>2007-04-10T12:49:20.548+01:00</updated><title type="text">Pretty RESTful URLs in Rails</title><content type="html">Since Release 1.2 Rails knows to generate &lt;a href="http://en.wikipedia.org/wiki/Representational_State_Transfer"&gt;RESTful &lt;/a&gt;routes. Each resource is identified by an &lt;a href="http://en.wikipedia.org/wiki/Uniform_Resource_Identifier"&gt;URI&lt;/a&gt;, which looks like &lt;span style="font-family:courier new;"&gt;/users/123&lt;/span&gt; . It would be nice to have more readable URLs, which include the name of the user: &lt;span style="font-family:courier new;"&gt;/users/matthias-georgi.&lt;/span&gt; This is a short tutorial on making your urls pretty while retaining the REST approach.

Assuming that you already have an user model, we add following line to our config/routes.rb:
&lt;pre style="background: rgb(255, 255, 255) none repeat scroll 0% 50%; color: rgb(0, 0, 0); -moz-background-clip: -moz-initial; -moz-background-origin: -moz-initial; -moz-background-inline-policy: -moz-initial;"&gt;map&lt;span style="color: rgb(128, 128, 48);"&gt;.&lt;/span&gt;resources :users
&lt;/pre&gt;You may now run the scaffold_resource generator in case you don't have any UsersController yet.

In order to remember the permalink for each user, we store it in a column named permalink
in the users table. Before we save an user record, we have to infer a url-safe permalink for the user name. We do this by:
&lt;pre style="background: rgb(255, 255, 255) none repeat scroll 0% 50%; color: rgb(0, 0, 0); -moz-background-clip: -moz-initial; -moz-background-origin: -moz-initial; -moz-background-inline-policy: -moz-initial;"&gt;&lt;span style="color: rgb(128, 0, 0); font-weight: bold;"&gt;def&lt;/span&gt; before_save
&lt;span style="color: rgb(128, 0, 0); font-weight: bold;"&gt;  self&lt;/span&gt;&lt;span style="color: rgb(128, 128, 48);"&gt;.&lt;/span&gt;permalink &lt;span style="color: rgb(128, 128, 48);"&gt;=&lt;/span&gt; name&lt;span style="color: rgb(128, 128, 48);"&gt;.&lt;/span&gt;downcase&lt;span style="color: rgb(128, 128, 48);"&gt;.&lt;/span&gt;&lt;span style="color: rgb(64, 0, 0);"&gt;gsub&lt;/span&gt;&lt;span style="color: rgb(128, 128, 48);"&gt;(&lt;/span&gt;&lt;span style="color: rgb(128, 128, 48);"&gt;/&lt;/span&gt;&lt;span style="color: rgb(128, 128, 48);"&gt;[&lt;/span&gt;^a&lt;span style="color: rgb(128, 128, 48);"&gt;-&lt;/span&gt;z0&lt;span style="color: rgb(128, 128, 48);"&gt;-&lt;/span&gt;&lt;span style="color: rgb(0, 140, 0);"&gt;9&lt;/span&gt;&lt;span style="color: rgb(128, 128, 48);"&gt;]&lt;/span&gt;&lt;span style="color: rgb(128, 128, 48);"&gt;+&lt;/span&gt;&lt;span style="color: rgb(128, 128, 48);"&gt;/&lt;/span&gt;i, &lt;span style="color: rgb(0, 0, 230);"&gt;'-'&lt;/span&gt;&lt;span style="color: rgb(128, 128, 48);"&gt;)&lt;/span&gt; &lt;span style="color: rgb(128, 0, 0); font-weight: bold;"&gt;if&lt;/span&gt; permalink&lt;span style="color: rgb(128, 128, 48);"&gt;.&lt;/span&gt;blank?
&lt;span style="color: rgb(128, 0, 0); font-weight: bold;"&gt;end&lt;/span&gt;
&lt;/pre&gt;Each character, which is not an alphanumeric will be replaced by a dash. This is only done, if the permalink is not set already. So we have a way for users to set their permalink manually.

To avoid duplicate permalinks, we validate the uniqeness of the permalink:
&lt;pre style="background: rgb(255, 255, 255) none repeat scroll 0% 50%; color: rgb(0, 0, 0); -moz-background-clip: -moz-initial; -moz-background-origin: -moz-initial; -moz-background-inline-policy: -moz-initial;"&gt;validates_uniqeness_of :permalink&lt;/pre&gt;What happens now, if we browse to a user url like &lt;span style="font-family:courier new;"&gt;/users/matthias-georgi&lt;/span&gt; ?
Rails raises an exception, telling us that it cannot find an record with the id &lt;span style="font-family:courier new;"&gt;matthias-georgi&lt;/span&gt;. We'll fix now our controller to look for the permalink and not the id of the user. Just replace each call to &lt;span style="font-family:courier new;"&gt;User.find(params[:id])&lt;/span&gt; with &lt;span style="font-family:courier new;"&gt;User.find_by_permalink(params[:id]).&lt;/span&gt;

Also we have to ensure, that our user routes will be generated correctly. Therefore we overwrite the &lt;span style="font-family:courier new;"&gt;to_param&lt;/span&gt; method:
&lt;pre style="background: rgb(255, 255, 255) none repeat scroll 0% 50%; color: rgb(0, 0, 0); -moz-background-clip: -moz-initial; -moz-background-origin: -moz-initial; -moz-background-inline-policy: -moz-initial;"&gt;&lt;span style="color: rgb(128, 0, 0); font-weight: bold;"&gt;def&lt;/span&gt; to_param
 permalink
&lt;span style="color: rgb(128, 0, 0); font-weight: bold;"&gt;end&lt;/span&gt;
&lt;/pre&gt;The effect of this little change is, that &lt;span style="font-family:courier new;"&gt;user_url(a_user)&lt;/span&gt; generates the right url.

Nested resources and pretty urls can get tricky and  for now I won't touch this topic.  Remember  that changing the permalink may be problematic as links to the old url will get invalid. Nevertheless have fun experimenting with pretty urls and the new wonderful world of REST.</content><link rel="alternate" type="text/html" href="http://feeds.feedburner.com/~r/TheMysteriousWaysOfRuby/~3/107954668/pretty-restful-urls-in-rails.html" title="Pretty RESTful URLs in Rails" /><link rel="replies" type="text/html" href="http://www.blogger.com/comment.g?blogID=3250677721251884719&amp;postID=8283404202880311190" title="8 Comments" /><link rel="replies" type="application/atom+xml" href="http://themysteriouswaysofruby.blogspot.com/feeds/8283404202880311190/comments/default" title="Post Comments" /><link rel="self" type="application/atom+xml" href="http://themysteriouswaysofruby.blogspot.com/feeds/posts/default/8283404202880311190" /><link rel="edit" type="application/atom+xml" href="http://www.blogger.com/feeds/3250677721251884719/posts/default/8283404202880311190" /><author><name>Matthias Georgi</name><uri>http://www.blogger.com/profile/16422189308202543523</uri><email>noreply@blogger.com</email></author><feedburner:origLink>http://themysteriouswaysofruby.blogspot.com/2007/04/pretty-restful-urls-in-rails.html</feedburner:origLink></entry><entry><id>tag:blogger.com,1999:blog-3250677721251884719.post-2484003912726503198</id><published>2007-04-08T12:37:00.000+01:00</published><updated>2007-04-08T13:28:47.413+01:00</updated><category scheme="http://www.blogger.com/atom/ns#" term="ruby" /><category scheme="http://www.blogger.com/atom/ns#" term="flickr" /><category scheme="http://www.blogger.com/atom/ns#" term="rails" /><category scheme="http://www.blogger.com/atom/ns#" term="del.icio.us" /><category scheme="http://www.blogger.com/atom/ns#" term="sidebar" /><category scheme="http://www.blogger.com/atom/ns#" term="rss" /><title type="text">Building a del.ico.us and flickr sidebar in 5 minutes</title><content type="html">You need a del.icio.us sidebar which shows recent bookmarks or one of these nice flickr badges? This is really is easy as the &lt;a href="http://trac.typosphere.org/"&gt;Typo weblog&lt;/a&gt; already includes an flickr and del.icio.us aggregator. Download the files &lt;a href="http://trac.typosphere.org/browser/trunk/vendor/plugins/delicious_sidebar/lib/delicious.rb?format=raw"&gt;delicous.rb&lt;/a&gt; and &lt;a href="http://trac.typosphere.org/browser/trunk/vendor/plugins/flickr_sidebar/lib/flickr.rb?format=raw"&gt;flickr.rb&lt;/a&gt; and drop them into your rails lib folder.

Now add these two little helpers to your application_helper.rb:
&lt;pre style="background: rgb(255, 255, 255) none repeat scroll 0% 50%; color: rgb(0, 0, 0); -moz-background-clip: -moz-initial; -moz-background-origin: -moz-initial; -moz-background-inline-policy: -moz-initial;"&gt;&lt;span style="color: rgb(64, 0, 0);"&gt;require&lt;/span&gt; &lt;span style="color: rgb(0, 0, 230);"&gt;'delicious'&lt;/span&gt;
&lt;span style="color: rgb(64, 0, 0);"&gt;require&lt;/span&gt; &lt;span style="color: rgb(0, 0, 230);"&gt;'flickr'&lt;/span&gt;

&lt;span style="color: rgb(128, 0, 0); font-weight: bold;"&gt;def&lt;/span&gt; delicious&lt;span style="color: rgb(128, 128, 48);"&gt;(&lt;/span&gt;tag&lt;span style="color: rgb(128, 128, 48);"&gt;)&lt;/span&gt;
 Delicious&lt;span style="color: rgb(128, 128, 48);"&gt;.&lt;/span&gt;&lt;span style="color: rgb(128, 0, 0); font-weight: bold;"&gt;new&lt;/span&gt;&lt;span style="color: rgb(128, 128, 48);"&gt;(&lt;/span&gt;&lt;span style="color: rgb(0, 0, 230);"&gt;"http://del.icio.us/rss/tag/#{tag}"&lt;/span&gt;&lt;span style="color: rgb(128, 128, 48);"&gt;)&lt;/span&gt;
&lt;span style="color: rgb(128, 0, 0); font-weight: bold;"&gt;end&lt;/span&gt;

&lt;span style="color: rgb(128, 0, 0); font-weight: bold;"&gt;def&lt;/span&gt; flickr&lt;span style="color: rgb(128, 128, 48);"&gt;(&lt;/span&gt;tag&lt;span style="color: rgb(128, 128, 48);"&gt;)&lt;/span&gt;
 FlickrAggregation&lt;span style="color: rgb(128, 128, 48);"&gt;.&lt;/span&gt;&lt;span style="color: rgb(128, 0, 0); font-weight: bold;"&gt;new&lt;/span&gt;&lt;span style="color: rgb(128, 128, 48);"&gt;(&lt;/span&gt;&lt;span style="color: rgb(0, 0, 230);"&gt;"http://api.flickr.com/services/feeds/photos_public.gne?tags=#{tag}&amp;format=rss_200"&lt;/span&gt;&lt;span style="color: rgb(128, 128, 48);"&gt;)&lt;/span&gt;
&lt;span style="color: rgb(128, 0, 0); font-weight: bold;"&gt;end&lt;/span&gt;
&lt;/pre&gt;
We are now able to fetch the feeds for our desired tags with a simple method call. Next step is to render a list of links for our sidebar. I use &lt;a href="http://redhanded.hobix.com/inspect/markabyForRails.html"&gt;Markaby&lt;/a&gt; for rendering my Rails views. It is easy to type and has a clean syntax. So for rendering the del.icio.us sidebar, you need something like this:

&lt;pre style="background: rgb(255, 255, 255) none repeat scroll 0% 50%; color: rgb(0, 0, 0); -moz-background-clip: -moz-initial; -moz-background-origin: -moz-initial; -moz-background-inline-policy: -moz-initial;"&gt;ul &lt;span style="color: rgb(128, 0, 128);"&gt;{&lt;/span&gt;
 delicious&lt;span style="color: rgb(128, 128, 48);"&gt;(&lt;/span&gt;&lt;span style="color: rgb(0, 0, 230);"&gt;:ruby&lt;/span&gt;&lt;span style="color: rgb(128, 128, 48);"&gt;)&lt;/span&gt;&lt;span style="color: rgb(128, 128, 48);"&gt;.&lt;/span&gt;items&lt;span style="color: rgb(128, 128, 48);"&gt;[&lt;/span&gt;&lt;span style="color: rgb(0, 140, 0);"&gt;0&lt;/span&gt;,&lt;span style="color: rgb(0, 140, 0);"&gt;10&lt;/span&gt;&lt;span style="color: rgb(128, 128, 48);"&gt;]&lt;/span&gt;&lt;span style="color: rgb(128, 128, 48);"&gt;.&lt;/span&gt;&lt;span style="color: rgb(128, 0, 0); font-weight: bold;"&gt;each&lt;/span&gt; &lt;span style="color: rgb(128, 0, 128);"&gt;{&lt;/span&gt; |item|
   li &lt;span style="color: rgb(128, 0, 128);"&gt;{&lt;/span&gt; a&lt;span style="color: rgb(128, 128, 48);"&gt;(&lt;/span&gt;:href &lt;span style="color: rgb(128, 128, 48);"&gt;=&lt;/span&gt;&gt; item&lt;span style="color: rgb(128, 128, 48);"&gt;.&lt;/span&gt;link&lt;span style="color: rgb(128, 128, 48);"&gt;)&lt;/span&gt; &lt;span style="color: rgb(128, 0, 128);"&gt;{&lt;/span&gt; item&lt;span style="color: rgb(128, 128, 48);"&gt;.&lt;/span&gt;title &lt;span style="color: rgb(128, 0, 128);"&gt;}&lt;/span&gt; &lt;span style="color: rgb(128, 0, 128);"&gt;}&lt;/span&gt;
&lt;span style="color: rgb(128, 0, 128);"&gt; }&lt;/span&gt;
&lt;span style="color: rgb(128, 0, 128);"&gt;}&lt;/span&gt;
&lt;/pre&gt;
This is pretty self-explanatory. We take the first ten items of the del.icio.us feed and for each item we output a list element containing a link to the item.

The view for the flickr badge is similar:

&lt;pre style="background: rgb(255, 255, 255) none repeat scroll 0% 50%; color: rgb(0, 0, 0); -moz-background-clip: -moz-initial; -moz-background-origin: -moz-initial; -moz-background-inline-policy: -moz-initial;"&gt;ul &lt;span style="color: rgb(128, 0, 128);"&gt;{&lt;/span&gt;
 flickr&lt;span style="color: rgb(128, 128, 48);"&gt;(&lt;/span&gt;&lt;span style="color: rgb(0, 0, 230);"&gt;:ruby&lt;/span&gt;&lt;span style="color: rgb(128, 128, 48);"&gt;)&lt;/span&gt;&lt;span style="color: rgb(128, 128, 48);"&gt;.&lt;/span&gt;pics&lt;span style="color: rgb(128, 128, 48);"&gt;[&lt;/span&gt;&lt;span style="color: rgb(0, 140, 0);"&gt;0&lt;/span&gt;,&lt;span style="color: rgb(0, 140, 0);"&gt;10&lt;/span&gt;&lt;span style="color: rgb(128, 128, 48);"&gt;]&lt;/span&gt;&lt;span style="color: rgb(128, 128, 48);"&gt;.&lt;/span&gt;&lt;span style="color: rgb(128, 0, 0); font-weight: bold;"&gt;each&lt;/span&gt; &lt;span style="color: rgb(128, 0, 128);"&gt;{&lt;/span&gt; |pic|
   image_tag pic&lt;span style="color: rgb(128, 128, 48);"&gt;.&lt;/span&gt;square, :size &lt;span style="color: rgb(128, 128, 48);"&gt;=&lt;/span&gt;&gt; &lt;span style="color: rgb(0, 0, 230);"&gt;'48x48'&lt;/span&gt;
 &lt;span style="color: rgb(128, 0, 128);"&gt;}&lt;/span&gt;
&lt;span style="color: rgb(128, 0, 128);"&gt;}&lt;/span&gt;
&lt;/pre&gt;
We take the first ten items of the flickr feed and for each item we render an image tag which shows a square thumbnail of the size 48x48.

Congratulations, you have just written a del.icio.us and flickr sidebar in 5 minutes using only 18 lines of code. Now you can spend the rest of the day pimping up your sidebar with all kinds of feeds using one of these &lt;a href="http://trac.typosphere.org/browser/trunk/vendor/plugins"&gt;plugins&lt;/a&gt; of the typo weblog.</content><link rel="alternate" type="text/html" href="http://feeds.feedburner.com/~r/TheMysteriousWaysOfRuby/~3/107493748/building-delicous-and-flickr-sidebar-in.html" title="Building a del.ico.us and flickr sidebar in 5 minutes" /><link rel="replies" type="text/html" href="http://www.blogger.com/comment.g?blogID=3250677721251884719&amp;postID=2484003912726503198" title="0 Comments" /><link rel="replies" type="application/atom+xml" href="http://themysteriouswaysofruby.blogspot.com/feeds/2484003912726503198/comments/default" title="Post Comments" /><link rel="self" type="application/atom+xml" href="http://themysteriouswaysofruby.blogspot.com/feeds/posts/default/2484003912726503198" /><link rel="edit" type="application/atom+xml" href="http://www.blogger.com/feeds/3250677721251884719/posts/default/2484003912726503198" /><author><name>Matthias Georgi</name><uri>http://www.blogger.com/profile/16422189308202543523</uri><email>noreply@blogger.com</email></author><feedburner:origLink>http://themysteriouswaysofruby.blogspot.com/2007/04/building-delicous-and-flickr-sidebar-in.html</feedburner:origLink></entry><entry><id>tag:blogger.com,1999:blog-3250677721251884719.post-7833863330250237608</id><published>2007-04-04T14:32:00.000+01:00</published><updated>2007-04-04T19:45:05.704+01:00</updated><category scheme="http://www.blogger.com/atom/ns#" term="ruby" /><category scheme="http://www.blogger.com/atom/ns#" term="javascript" /><category scheme="http://www.blogger.com/atom/ns#" term="scriptaculous" /><category scheme="http://www.blogger.com/atom/ns#" term="rails" /><category scheme="http://www.blogger.com/atom/ns#" term="ajax" /><category scheme="http://www.blogger.com/atom/ns#" term="json" /><category scheme="http://www.blogger.com/atom/ns#" term="auto-completion" /><title type="text">Fast Auto-completion with Rails, Scriptaculous and JSON</title><content type="html">Inspired by the excellent &lt;a href="http://www.pragmaticprogrammer.com/titles/fr_rr/"&gt;Rails Recipes book&lt;/a&gt; , I created an improved Auto-completion helper, which uses JSON and AJAX instead of a script tag for loading the completions.

What we want to achieve is a search field, which pops up immediately, showing us a list of possible completions for our search word. Look at &lt;a href="http://labs.google.com/suggest/"&gt;Google Suggest&lt;/a&gt; to get an idea.

Rails already has an auto_complete_field, which sends an AJAX request for each keystroke. This approach is quite slow, but works in most cases, especially for large datasets auto_complete_field is the better choice.

Our idea, stolen from the Rails Recipes book is to fetch the array of possible completions only once. Each keystroke will trigger only a local lookup and need no further server interaction. Scriptaculous already has the right tool for this job: &lt;a href="http://wiki.script.aculo.us/scriptaculous/show/Autocompleter.Local"&gt;Autocompleter.Local&lt;/a&gt; . We will just pass a javascript array of possible completions to the constructor and we're done.

OK, let's start. First we need the CSS used by Autocompleter.Local, which styles the choices box:
&lt;pre style="background: rgb(255, 255, 255) none repeat scroll 0% 50%; color: rgb(0, 0, 0); -moz-background-clip: -moz-initial; -moz-background-origin: -moz-initial; -moz-background-inline-policy: -moz-initial;"&gt;&lt;span style="color: rgb(128, 0, 0); font-weight: bold;"&gt;div&lt;/span&gt;&lt;span style="color: rgb(128, 128, 48);"&gt;.&lt;/span&gt;auto_complete &lt;span style="color: rgb(128, 0, 128);"&gt;{&lt;/span&gt;
&lt;span style="color: rgb(187, 121, 119); font-weight: bold;"&gt;  width&lt;/span&gt;&lt;span style="color: rgb(128, 128, 48);"&gt;:&lt;/span&gt; &lt;span style="color: rgb(0, 140, 0);"&gt;350&lt;/span&gt;&lt;span style="color: rgb(0, 102, 0);"&gt;px&lt;/span&gt;&lt;span style="color: rgb(128, 0, 128);"&gt;;&lt;/span&gt;
&lt;span style="color: rgb(187, 121, 119); font-weight: bold;"&gt;  background&lt;/span&gt;&lt;span style="color: rgb(128, 128, 48);"&gt;:&lt;/span&gt; &lt;span style="color: rgb(0, 140, 0);"&gt;#&lt;/span&gt;&lt;span style="color: rgb(0, 128, 0);"&gt;fff&lt;/span&gt;&lt;span style="color: rgb(128, 0, 128);"&gt;;&lt;/span&gt;
&lt;span style="color: rgb(128, 0, 128);"&gt;}&lt;/span&gt;

&lt;span style="color: rgb(128, 0, 0); font-weight: bold;"&gt;div&lt;/span&gt;&lt;span style="color: rgb(128, 128, 48);"&gt;.&lt;/span&gt;auto_complete &lt;span style="color: rgb(128, 0, 0); font-weight: bold;"&gt;ul&lt;/span&gt; &lt;span style="color: rgb(128, 0, 128);"&gt;{&lt;/span&gt;
&lt;span style="color: rgb(187, 121, 119); font-weight: bold;"&gt;  border&lt;/span&gt;&lt;span style="color: rgb(128, 128, 48);"&gt;:&lt;/span&gt;&lt;span style="color: rgb(0, 140, 0);"&gt;1&lt;/span&gt;&lt;span style="color: rgb(0, 102, 0);"&gt;px&lt;/span&gt; &lt;span style="color: rgb(7, 71, 38);"&gt;solid&lt;/span&gt; &lt;span style="color: rgb(0, 140, 0);"&gt;#&lt;/span&gt;&lt;span style="color: rgb(0, 128, 0);"&gt;888&lt;/span&gt;&lt;span style="color: rgb(128, 0, 128);"&gt;;&lt;/span&gt;
&lt;span style="color: rgb(187, 121, 119); font-weight: bold;"&gt;  margin&lt;/span&gt;&lt;span style="color: rgb(128, 128, 48);"&gt;:&lt;/span&gt;&lt;span style="color: rgb(0, 140, 0);"&gt;0&lt;/span&gt;&lt;span style="color: rgb(128, 0, 128);"&gt;;&lt;/span&gt;
&lt;span style="color: rgb(187, 121, 119); font-weight: bold;"&gt;  padding&lt;/span&gt;&lt;span style="color: rgb(128, 128, 48);"&gt;:&lt;/span&gt;&lt;span style="color: rgb(0, 140, 0);"&gt;0&lt;/span&gt;&lt;span style="color: rgb(128, 0, 128);"&gt;;&lt;/span&gt;
&lt;span style="color: rgb(187, 121, 119); font-weight: bold;"&gt;  width&lt;/span&gt;&lt;span style="color: rgb(128, 128, 48);"&gt;:&lt;/span&gt;&lt;span style="color: rgb(0, 140, 0);"&gt;100&lt;/span&gt;&lt;span style="color: rgb(0, 102, 0);"&gt;%&lt;/span&gt;&lt;span style="color: rgb(128, 0, 128);"&gt;;&lt;/span&gt;
&lt;span style="color: rgb(187, 121, 119); font-weight: bold;"&gt;  list-style-type&lt;/span&gt;&lt;span style="color: rgb(128, 128, 48);"&gt;:&lt;/span&gt;&lt;span style="color: rgb(7, 71, 38);"&gt;none&lt;/span&gt;&lt;span style="color: rgb(128, 0, 128);"&gt;;&lt;/span&gt;
&lt;span style="color: rgb(128, 0, 128);"&gt;}&lt;/span&gt;

&lt;span style="color: rgb(128, 0, 0); font-weight: bold;"&gt;div&lt;/span&gt;&lt;span style="color: rgb(128, 128, 48);"&gt;.&lt;/span&gt;auto_complete &lt;span style="color: rgb(128, 0, 0); font-weight: bold;"&gt;ul&lt;/span&gt; &lt;span style="color: rgb(128, 0, 0); font-weight: bold;"&gt;li&lt;/span&gt; &lt;span style="color: rgb(128, 0, 128);"&gt;{&lt;/span&gt;
&lt;span style="color: rgb(187, 121, 119); font-weight: bold;"&gt;  margin&lt;/span&gt;&lt;span style="color: rgb(128, 128, 48);"&gt;:&lt;/span&gt;&lt;span style="color: rgb(0, 140, 0);"&gt;0&lt;/span&gt;&lt;span style="color: rgb(128, 0, 128);"&gt;;&lt;/span&gt;
&lt;span style="color: rgb(187, 121, 119); font-weight: bold;"&gt;  padding&lt;/span&gt;&lt;span style="color: rgb(128, 128, 48);"&gt;:&lt;/span&gt;&lt;span style="color: rgb(0, 140, 0);"&gt;3&lt;/span&gt;&lt;span style="color: rgb(0, 102, 0);"&gt;px&lt;/span&gt;&lt;span style="color: rgb(128, 0, 128);"&gt;;&lt;/span&gt;&lt;span style="color: rgb(128, 0, 128);"&gt;
}&lt;/span&gt;

&lt;span style="color: rgb(128, 0, 0); font-weight: bold;"&gt;div&lt;/span&gt;&lt;span style="color: rgb(128, 128, 48);"&gt;.&lt;/span&gt;auto_complete &lt;span style="color: rgb(128, 0, 0); font-weight: bold;"&gt;ul&lt;/span&gt; &lt;span style="color: rgb(128, 0, 0); font-weight: bold;"&gt;li&lt;/span&gt;&lt;span style="color: rgb(128, 128, 48);"&gt;.&lt;/span&gt;selected &lt;span style="color: rgb(128, 0, 128);"&gt;{&lt;/span&gt;
&lt;span style="color: rgb(187, 121, 119); font-weight: bold;"&gt;  background-color&lt;/span&gt;&lt;span style="color: rgb(128, 128, 48);"&gt;:&lt;/span&gt; &lt;span style="color: rgb(0, 140, 0);"&gt;#&lt;/span&gt;&lt;span style="color: rgb(0, 128, 0);"&gt;ffb&lt;/span&gt;&lt;span style="color: rgb(128, 0, 128);"&gt;;&lt;/span&gt;
&lt;span style="color: rgb(128, 0, 128);"&gt;}&lt;/span&gt;

&lt;span style="color: rgb(128, 0, 0); font-weight: bold;"&gt;div&lt;/span&gt;&lt;span style="color: rgb(128, 128, 48);"&gt;.&lt;/span&gt;auto_complete &lt;span style="color: rgb(128, 0, 0); font-weight: bold;"&gt;ul&lt;/span&gt; &lt;span style="color: rgb(128, 0, 0); font-weight: bold;"&gt;strong&lt;/span&gt;&lt;span style="color: rgb(128, 128, 48);"&gt;.&lt;/span&gt;highlight &lt;span style="color: rgb(128, 0, 128);"&gt;{&lt;/span&gt;
&lt;span style="color: rgb(187, 121, 119); font-weight: bold;"&gt;  color&lt;/span&gt;&lt;span style="color: rgb(128, 128, 48);"&gt;:&lt;/span&gt; &lt;span style="color: rgb(0, 140, 0);"&gt;#&lt;/span&gt;&lt;span style="color: rgb(0, 128, 0);"&gt;800&lt;/span&gt;&lt;span style="color: rgb(128, 0, 128);"&gt;;&lt;/span&gt;
&lt;span style="color: rgb(187, 121, 119); font-weight: bold;"&gt;  margin&lt;/span&gt;&lt;span style="color: rgb(128, 128, 48);"&gt;:&lt;/span&gt;&lt;span style="color: rgb(0, 140, 0);"&gt;0&lt;/span&gt;&lt;span style="color: rgb(128, 0, 128);"&gt;;&lt;/span&gt;
&lt;span style="color: rgb(187, 121, 119); font-weight: bold;"&gt;  padding&lt;/span&gt;&lt;span style="color: rgb(128, 128, 48);"&gt;:&lt;/span&gt;&lt;span style="color: rgb(0, 140, 0);"&gt;0&lt;/span&gt;&lt;span style="color: rgb(128, 0, 128);"&gt;;&lt;/span&gt;
&lt;span style="color: rgb(128, 0, 128);"&gt;}&lt;/span&gt;
&lt;/pre&gt;
Rails already has an controller macro for generating a auto completion action. We will create a similar macro, which will generate an action, which in turn generates the JSON response. Sounds complex, but the implementation is quite easy. Just add to your ApplicationController:

&lt;pre style="background: rgb(255, 255, 255) none repeat scroll 0% 50%; color: rgb(0, 0, 0); -moz-background-clip: -moz-initial; -moz-background-origin: -moz-initial; -moz-background-inline-policy: -moz-initial;"&gt;&lt;span style="color: rgb(128, 0, 0); font-weight: bold;"&gt;def&lt;/span&gt; self&lt;span style="color: rgb(128, 128, 48);"&gt;.&lt;/span&gt;fast_auto_complete_for&lt;span style="color: rgb(128, 128, 48);"&gt;(&lt;/span&gt;object, method, options &lt;span style="color: rgb(128, 128, 48);"&gt;=&lt;/span&gt; &lt;span style="color: rgb(128, 0, 128);"&gt;{&lt;/span&gt;&lt;span style="color: rgb(128, 0, 128);"&gt;}&lt;/span&gt;&lt;span style="color: rgb(128, 128, 48);"&gt;)&lt;/span&gt;
 define_method&lt;span style="color: rgb(128, 128, 48);"&gt;(&lt;/span&gt;&lt;span style="color: rgb(0, 0, 230);"&gt;"auto_complete_for_#{object}_#{method}"&lt;/span&gt;&lt;span style="color: rgb(128, 128, 48);"&gt;)&lt;/span&gt; &lt;span style="color: rgb(128, 0, 0); font-weight: bold;"&gt;do&lt;/span&gt;
   render :json &lt;span style="color: rgb(128, 128, 48);"&gt;=&lt;/span&gt;&gt; object&lt;span style="color: rgb(128, 128, 48);"&gt;.&lt;/span&gt;to_s&lt;span style="color: rgb(128, 128, 48);"&gt;.&lt;/span&gt;camelize&lt;span style="color: rgb(128, 128, 48);"&gt;.&lt;/span&gt;constantize&lt;span style="color: rgb(128, 128, 48);"&gt;.&lt;/span&gt;find&lt;span style="color: rgb(128, 128, 48);"&gt;(&lt;/span&gt;:all&lt;span style="color: rgb(128, 128, 48);"&gt;)&lt;/span&gt;&lt;span style="color: rgb(128, 128, 48);"&gt;.&lt;/span&gt;map&lt;span style="color: rgb(128, 128, 48);"&gt;(&lt;/span&gt;&amp;method&lt;span style="color: rgb(128, 128, 48);"&gt;)&lt;/span&gt;&lt;span style="color: rgb(128, 128, 48);"&gt;.&lt;/span&gt;to_json
&lt;span style="color: rgb(128, 0, 0); font-weight: bold;"&gt;  end&lt;/span&gt;
&lt;span style="color: rgb(128, 0, 0); font-weight: bold;"&gt;end&lt;/span&gt;
&lt;/pre&gt;


The response of the generated action will now contain a list of all values for the desired attribute. You can use it like in your controllers:

&lt;pre style="background: rgb(255, 255, 255) none repeat scroll 0% 50%; color: rgb(0, 0, 0); -moz-background-clip: -moz-initial; -moz-background-origin: -moz-initial; -moz-background-inline-policy: -moz-initial;"&gt;fast_auto_complete_for :sport, :name
&lt;/pre&gt;
Now let us get into the tricky part: the javascript macro helper. How will we get the completion list? Prototype includes the Ajax.Request class, which sends an Ajax Request to our generated action and fetches the array encoded as JSON. Furthermore we have to generate a div which will hold the popup list for our completion entries. Without going into detail, I'll just show you the code, which you can add to your ApplicationHelper:

&lt;pre style="background: rgb(255, 255, 255) none repeat scroll 0% 50%; color: rgb(0, 0, 0); -moz-background-clip: -moz-initial; -moz-background-origin: -moz-initial; -moz-background-inline-policy: -moz-initial;"&gt;&lt;span style="color: rgb(128, 0, 0); font-weight: bold;"&gt;def&lt;/span&gt; fast_auto_complete_field&lt;span style="color: rgb(128, 128, 48);"&gt;(&lt;/span&gt;field_id, options&lt;span style="color: rgb(128, 128, 48);"&gt;=&lt;/span&gt;&lt;span style="color: rgb(128, 0, 128);"&gt;{&lt;/span&gt;&lt;span style="color: rgb(128, 0, 128);"&gt;}&lt;/span&gt;&lt;span style="color: rgb(128, 128, 48);"&gt;)&lt;/span&gt;
 div_id &lt;span style="color: rgb(128, 128, 48);"&gt;=&lt;/span&gt; &lt;span style="color: rgb(0, 0, 230);"&gt;"#{field_id}_auto_complete"&lt;/span&gt;
 url &lt;span style="color: rgb(128, 128, 48);"&gt;=&lt;/span&gt; options&lt;span style="color: rgb(128, 128, 48);"&gt;.&lt;/span&gt;delete&lt;span style="color: rgb(128, 128, 48);"&gt;(&lt;/span&gt;:url&lt;span style="color: rgb(128, 128, 48);"&gt;)&lt;/span&gt; &lt;span style="color: rgb(128, 0, 0); font-weight: bold;"&gt;or&lt;/span&gt; &lt;span style="color: rgb(64, 0, 0);"&gt;raise&lt;/span&gt; &lt;span style="color: rgb(0, 0, 230);"&gt;"url required"&lt;/span&gt;
 options &lt;span style="color: rgb(128, 128, 48);"&gt;=&lt;/span&gt; options&lt;span style="color: rgb(128, 128, 48);"&gt;.&lt;/span&gt;merge&lt;span style="color: rgb(128, 128, 48);"&gt;(&lt;/span&gt;:tokens &lt;span style="color: rgb(128, 128, 48);"&gt;=&lt;/span&gt;&gt; &lt;span style="color: rgb(0, 0, 230);"&gt;','&lt;/span&gt;, :frequency &lt;span style="color: rgb(128, 128, 48);"&gt;=&lt;/span&gt;&gt; &lt;span style="color: rgb(0, 140, 0);"&gt;0&lt;/span&gt; &lt;span style="color: rgb(128, 128, 48);"&gt;)&lt;/span&gt;
 markaby &lt;span style="color: rgb(128, 0, 128);"&gt;{&lt;/span&gt;
   div&lt;span style="color: rgb(128, 128, 48);"&gt;.&lt;/span&gt;auto_complete&lt;span style="color: rgb(128, 128, 48);"&gt;(&lt;/span&gt;:id &lt;span style="color: rgb(128, 128, 48);"&gt;=&lt;/span&gt;&gt; div_id&lt;span style="color: rgb(128, 128, 48);"&gt;)&lt;/span&gt; &lt;span style="color: rgb(128, 0, 128);"&gt;{&lt;/span&gt;&lt;span style="color: rgb(128, 0, 128);"&gt;}&lt;/span&gt;
   javascript_tag &lt;&lt;&lt;span style="color: rgb(128, 128, 48);"&gt;-&lt;/span&gt;&lt;span style="color: rgb(128, 0, 0); font-weight: bold;"&gt;end&lt;/span&gt;
&lt;span style="color: rgb(128, 0, 0); font-weight: bold;"&gt;     new&lt;/span&gt; Ajax&lt;span style="color: rgb(128, 128, 48);"&gt;.&lt;/span&gt;Request&lt;span style="color: rgb(128, 128, 48);"&gt;(&lt;/span&gt;&lt;span style="color: rgb(0, 0, 230);"&gt;'#{url}'&lt;/span&gt;, &lt;span style="color: rgb(128, 0, 128);"&gt;{&lt;/span&gt;
       method: &lt;span style="color: rgb(0, 0, 230);"&gt;'get'&lt;/span&gt;,
       onSuccess: function&lt;span style="color: rgb(128, 128, 48);"&gt;(&lt;/span&gt;transport&lt;span style="color: rgb(128, 128, 48);"&gt;)&lt;/span&gt; &lt;span style="color: rgb(128, 0, 128);"&gt;{&lt;/span&gt;
       &lt;span style="color: rgb(128, 0, 0); font-weight: bold;"&gt;  new&lt;/span&gt; Autocompleter&lt;span style="color: rgb(128, 128, 48);"&gt;.&lt;/span&gt;Local&lt;span style="color: rgb(128, 128, 48);"&gt;(&lt;/span&gt;&lt;span style="color: rgb(0, 0, 230);"&gt;'#{field_id}'&lt;/span&gt;, &lt;span style="color: rgb(0, 0, 230);"&gt;'#{div_id}'&lt;/span&gt;, &lt;span style="color: rgb(64, 0, 0);"&gt;eval&lt;/span&gt;&lt;span style="color: rgb(128, 128, 48);"&gt;(&lt;/span&gt;transport&lt;span style="color: rgb(128, 128, 48);"&gt;.&lt;/span&gt;responseText&lt;span style="color: rgb(128, 128, 48);"&gt;)&lt;/span&gt;, &lt;span style="color: rgb(105, 105, 105);"&gt;#{options.to_json});          &lt;/span&gt;
       }
     }&lt;span style="color: rgb(128, 128, 48);"&gt;)&lt;/span&gt;;
&lt;span style="color: rgb(128, 0, 0); font-weight: bold;"&gt;    end&lt;/span&gt;
&lt;span style="color: rgb(128, 0, 128);"&gt;  }&lt;/span&gt;
&lt;span style="color: rgb(128, 0, 0); font-weight: bold;"&gt;end&lt;/span&gt;
&lt;/pre&gt;
Note, that I use my simple markaby helper, which I presented in my last post. It is just a little more convenient not to do all this string concatenation. Our helper needs the id for the text field we want to enhance. Based on this id the helper generates the div for presenting the completion entries. It is also required to pass the url of the json action, which is in our case /sports/auto_complete_for_sport_name.

Usage example:

&lt;pre style="background: rgb(255, 255, 255) none repeat scroll 0% 50%; color: rgb(0, 0, 0); -moz-background-clip: -moz-initial; -moz-background-origin: -moz-initial; -moz-background-inline-policy: -moz-initial;"&gt;form &lt;span style="color: rgb(128, 0, 128);"&gt;{&lt;/span&gt;
 input&lt;span style="color: rgb(128, 128, 48);"&gt;(&lt;/span&gt;:type &lt;span style="color: rgb(128, 128, 48);"&gt;=&lt;/span&gt;&gt; &lt;span style="color: rgb(0, 0, 230);"&gt;'text'&lt;/span&gt;, :name &lt;span style="color: rgb(128, 128, 48);"&gt;=&lt;/span&gt;&gt; &lt;span style="color: rgb(0, 0, 230);"&gt;'name'&lt;/span&gt;, :id &lt;span style="color: rgb(128, 128, 48);"&gt;=&lt;/span&gt;&gt; &lt;span style="color: rgb(0, 0, 230);"&gt;'sport_name'&lt;/span&gt;&lt;span style="color: rgb(128, 128, 48);"&gt;)&lt;/span&gt;
 input&lt;span style="color: rgb(128, 128, 48);"&gt;(&lt;/span&gt;:type &lt;span style="color: rgb(128, 128, 48);"&gt;=&lt;/span&gt;&gt; &lt;span style="color: rgb(0, 0, 230);"&gt;'submit'&lt;/span&gt;, :value &lt;span style="color: rgb(128, 128, 48);"&gt;=&lt;/span&gt;&gt; &lt;span style="color: rgb(0, 0, 230);"&gt;'Search'&lt;/span&gt;&lt;span style="color: rgb(128, 128, 48);"&gt;)&lt;/span&gt;
&lt;span style="color: rgb(128, 0, 128);"&gt;}&lt;/span&gt;

fast_auto_complete_field :sport_name, :url &lt;span style="color: rgb(128, 128, 48);"&gt;=&lt;/span&gt;&gt; &lt;span style="color: rgb(0, 0, 230);"&gt;'/sports/auto_complete_for_sport_name'

&lt;/span&gt;&lt;span style="color: rgb(0, 0, 230);"&gt;
&lt;/span&gt;&lt;/pre&gt;Well, that's it. Now you may enjoy snappy auto-completion and feel good about using bleeding edge technology like AJAX and JSON.

By the way, I'm really having a hard time formatting my code snippets here.  I use this &lt;a href="http://tohtml.com/ruby/"&gt;syntax-highlighter&lt;/a&gt; , which just works, as the styles are directly encoded into the span tags. Unfortunately every edit opertion at blogger.com swallows some whitespaces,
which is quite annoying. Maybe somebody knows a better way to do this?</content><link rel="alternate" type="text/html" href="http://feeds.feedburner.com/~r/TheMysteriousWaysOfRuby/~3/107135138/fast-autocompletion-with-rails.html" title="Fast Auto-completion with Rails, Scriptaculous and JSON" /><link rel="replies" type="text/html" href="http://www.blogger.com/comment.g?blogID=3250677721251884719&amp;postID=7833863330250237608" title="5 Comments" /><link rel="replies" type="application/atom+xml" href="http://themysteriouswaysofruby.blogspot.com/feeds/7833863330250237608/comments/default" title="Post Comments" /><link rel="self" type="application/atom+xml" href="http://themysteriouswaysofruby.blogspot.com/feeds/posts/default/7833863330250237608" /><link rel="edit" type="application/atom+xml" href="http://www.blogger.com/feeds/3250677721251884719/posts/default/7833863330250237608" /><author><name>Matthias Georgi</name><uri>http://www.blogger.com/profile/16422189308202543523</uri><email>noreply@blogger.com</email></author><feedburner:origLink>http://themysteriouswaysofruby.blogspot.com/2007/04/fast-autocompletion-with-rails.html</feedburner:origLink></entry><entry><id>tag:blogger.com,1999:blog-3250677721251884719.post-6667716891631358820</id><published>2007-04-02T10:42:00.000+01:00</published><updated>2007-04-02T15:08:02.795+01:00</updated><title type="text">Rendering markaby in your helpers</title><content type="html">Generating markup in your rails helpers is a general practice in rails and is used throughout all rails helpers. Normally you use content_tag to generate markup. But often you will encounter situations, where nested tags force you to write ugly helper code like the following helper method from the rails library:
&lt;pre style="background: rgb(255, 255, 255) none repeat scroll 0% 50%; color: rgb(0, 0, 0); -moz-background-clip: -moz-initial; -moz-background-origin: -moz-initial; -moz-background-inline-policy: -moz-initial;"&gt;&lt;span style="color: rgb(128, 0, 0); font-weight: bold;"&gt;def&lt;/span&gt; options_for_select&lt;span style="color: rgb(128, 128, 48);"&gt;(&lt;/span&gt;container, selected &lt;span style="color: rgb(128, 128, 48);"&gt;=&lt;/span&gt; &lt;span style="color: rgb(128, 0, 0); font-weight: bold;"&gt;nil&lt;/span&gt;&lt;span style="color: rgb(128, 128, 48);"&gt;)&lt;/span&gt;
 container &lt;span style="color: rgb(128, 128, 48);"&gt;=&lt;/span&gt; container&lt;span style="color: rgb(128, 128, 48);"&gt;.&lt;/span&gt;to_a &lt;span style="color: rgb(128, 0, 0); font-weight: bold;"&gt;if&lt;/span&gt; Hash &lt;span style="color: rgb(128, 128, 48);"&gt;=&lt;/span&gt;&lt;span style="color: rgb(128, 128, 48);"&gt;=&lt;/span&gt;&lt;span style="color: rgb(128, 128, 48);"&gt;=&lt;/span&gt; container

 options_for_select &lt;span style="color: rgb(128, 128, 48);"&gt;=&lt;/span&gt; container&lt;span style="color: rgb(128, 128, 48);"&gt;.&lt;/span&gt;inject&lt;span style="color: rgb(128, 128, 48);"&gt;(&lt;/span&gt;&lt;span style="color: rgb(128, 128, 48);"&gt;[&lt;/span&gt;&lt;span style="color: rgb(128, 128, 48);"&gt;]&lt;/span&gt;&lt;span style="color: rgb(128, 128, 48);"&gt;)&lt;/span&gt; &lt;span style="color: rgb(128, 0, 0); font-weight: bold;"&gt;do&lt;/span&gt; |options, element|
&lt;span style="color: rgb(128, 0, 0); font-weight: bold;"&gt;    if&lt;/span&gt; !element&lt;span style="color: rgb(128, 128, 48);"&gt;.&lt;/span&gt;is_a?&lt;span style="color: rgb(128, 128, 48);"&gt;(&lt;/span&gt;&lt;span style="color: rgb(64, 0, 0);"&gt;String&lt;/span&gt;&lt;span style="color: rgb(128, 128, 48);"&gt;)&lt;/span&gt; &lt;span style="color: rgb(128, 0, 0); font-weight: bold;"&gt;and&lt;/span&gt; element&lt;span style="color: rgb(128, 128, 48);"&gt;.&lt;/span&gt;respond_to?&lt;span style="color: rgb(128, 128, 48);"&gt;(&lt;/span&gt;:first&lt;span style="color: rgb(128, 128, 48);"&gt;)&lt;/span&gt; &lt;span style="color: rgb(128, 0, 0); font-weight: bold;"&gt;and&lt;/span&gt; element&lt;span style="color: rgb(128, 128, 48);"&gt;.&lt;/span&gt;respond_to?&lt;span style="color: rgb(128, 128, 48);"&gt;(&lt;/span&gt;:last&lt;span style="color: rgb(128, 128, 48);"&gt;)&lt;/span&gt;
     is_selected &lt;span style="color: rgb(128, 128, 48);"&gt;=&lt;/span&gt; &lt;span style="color: rgb(128, 128, 48);"&gt;(&lt;/span&gt; &lt;span style="color: rgb(128, 128, 48);"&gt;(&lt;/span&gt;selected&lt;span style="color: rgb(128, 128, 48);"&gt;.&lt;/span&gt;respond_to?&lt;span style="color: rgb(128, 128, 48);"&gt;(&lt;/span&gt;:include?&lt;span style="color: rgb(128, 128, 48);"&gt;)&lt;/span&gt; &amp;&amp;amp; !selected&lt;span style="color: rgb(128, 128, 48);"&gt;.&lt;/span&gt;is_a?&lt;span style="color: rgb(128, 128, 48);"&gt;(&lt;/span&gt;&lt;span style="color: rgb(64, 0, 0);"&gt;String&lt;/span&gt;&lt;span style="color: rgb(128, 128, 48);"&gt;)&lt;/span&gt; ? selected&lt;span style="color: rgb(128, 128, 48);"&gt;.&lt;/span&gt;include?&lt;span style="color: rgb(128, 128, 48);"&gt;(&lt;/span&gt;element&lt;span style="color: rgb(128, 128, 48);"&gt;.&lt;/span&gt;last&lt;span style="color: rgb(128, 128, 48);"&gt;)&lt;/span&gt; : element&lt;span style="color: rgb(128, 128, 48);"&gt;.&lt;/span&gt;last &lt;span style="color: rgb(128, 128, 48);"&gt;=&lt;/span&gt;&lt;span style="color: rgb(128, 128, 48);"&gt;=&lt;/span&gt; selected&lt;span style="color: rgb(128, 128, 48);"&gt;)&lt;/span&gt; &lt;span style="color: rgb(128, 128, 48);"&gt;)&lt;/span&gt;
&lt;span style="color: rgb(128, 0, 0); font-weight: bold;"&gt;      if&lt;/span&gt; is_selected
       options &amp;lt;&amp;lt; &lt;span style="color: rgb(0, 0, 230);"&gt;"&amp;lt;option value=\"&lt;/span&gt;&lt;span style="color: rgb(105, 105, 105);"&gt;#{html_escape(element.last.to_s)}\" selected=\"selected\"&gt;#{html_escape(element.first.to_s)}&amp;lt;/option&gt;"&lt;/span&gt;
     &lt;span style="color: rgb(128, 0, 0); font-weight: bold;"&gt;else&lt;/span&gt;
       options &amp;lt;&amp;lt; &lt;span style="color: rgb(0, 0, 230);"&gt;"&amp;lt;option value=\"&lt;/span&gt;&lt;span style="color: rgb(105, 105, 105);"&gt;#{html_escape(element.last.to_s)}\"&gt;#{html_escape(element.first.to_s)}&amp;lt;/option&gt;"&lt;/span&gt;
     &lt;span style="color: rgb(128, 0, 0); font-weight: bold;"&gt;end&lt;/span&gt;
 &lt;span style="color: rgb(128, 0, 0); font-weight: bold;"&gt;  else&lt;/span&gt;
     is_selected &lt;span style="color: rgb(128, 128, 48);"&gt;=&lt;/span&gt; &lt;span style="color: rgb(128, 128, 48);"&gt;(&lt;/span&gt; &lt;span style="color: rgb(128, 128, 48);"&gt;(&lt;/span&gt;selected&lt;span style="color: rgb(128, 128, 48);"&gt;.&lt;/span&gt;respond_to?&lt;span style="color: rgb(128, 128, 48);"&gt;(&lt;/span&gt;:include?&lt;span style="color: rgb(128, 128, 48);"&gt;)&lt;/span&gt; &amp;&amp;amp; !selected&lt;span style="color: rgb(128, 128, 48);"&gt;.&lt;/span&gt;is_a?&lt;span style="color: rgb(128, 128, 48);"&gt;(&lt;/span&gt;&lt;span style="color: rgb(64, 0, 0);"&gt;String&lt;/span&gt;&lt;span style="color: rgb(128, 128, 48);"&gt;)&lt;/span&gt; ? selected&lt;span style="color: rgb(128, 128, 48);"&gt;.&lt;/span&gt;include?&lt;span style="color: rgb(128, 128, 48);"&gt;(&lt;/span&gt;element&lt;span style="color: rgb(128, 128, 48);"&gt;)&lt;/span&gt; : element &lt;span style="color: rgb(128, 128, 48);"&gt;=&lt;/span&gt;&lt;span style="color: rgb(128, 128, 48);"&gt;=&lt;/span&gt; selected&lt;span style="color: rgb(128, 128, 48);"&gt;)&lt;/span&gt; &lt;span style="color: rgb(128, 128, 48);"&gt;)&lt;/span&gt;
     options &amp;lt;&amp;lt; &lt;span style="color: rgb(128, 128, 48);"&gt;(&lt;/span&gt;&lt;span style="color: rgb(128, 128, 48);"&gt;(&lt;/span&gt;is_selected&lt;span style="color: rgb(128, 128, 48);"&gt;)&lt;/span&gt; ? &lt;span style="color: rgb(0, 0, 230);"&gt;"&amp;lt;option value=\"&lt;/span&gt;&lt;span style="color: rgb(105, 105, 105);"&gt;#{html_escape(element.to_s)}\" selected=\"selected\"&gt;#{html_escape(element.to_s)}&amp;lt;/option&gt;" : "&amp;lt;option value=\"#{html_escape(element.to_s)}\"&gt;#{html_escape(element.to_s)}&amp;lt;/option&gt;")&lt;/span&gt;
   &lt;span style="color: rgb(128, 0, 0); font-weight: bold;"&gt;end&lt;/span&gt;
&lt;span style="color: rgb(128, 0, 0); font-weight: bold;"&gt;  end&lt;/span&gt;

 options_for_select&lt;span style="color: rgb(128, 128, 48);"&gt;.&lt;/span&gt;join&lt;span style="color: rgb(128, 128, 48);"&gt;(&lt;/span&gt;&lt;span style="color: rgb(0, 0, 230);"&gt;"\n"&lt;/span&gt;&lt;span style="color: rgb(128, 128, 48);"&gt;)&lt;/span&gt;
&lt;span style="color: rgb(128, 0, 0); font-weight: bold;"&gt;end&lt;/span&gt;
&lt;/pre&gt;

We will now rewrite this code with inline markaby. We need therefore the following helper method:

&lt;pre style="background: rgb(255, 255, 255) none repeat scroll 0% 50%; color: rgb(0, 0, 0); -moz-background-clip: -moz-initial; -moz-background-origin: -moz-initial; -moz-background-inline-policy: -moz-initial;"&gt;&lt;span style="color: rgb(128, 0, 0); font-weight: bold;"&gt;def&lt;/span&gt; markaby&lt;span style="color: rgb(128, 128, 48);"&gt;(&lt;/span&gt;&amp;&lt;span style="color: rgb(64, 0, 0);"&gt;proc&lt;/span&gt;&lt;span style="color: rgb(128, 128, 48);"&gt;)&lt;/span&gt;
 assigns &lt;span style="color: rgb(128, 128, 48);"&gt;=&lt;/span&gt; &lt;span style="color: rgb(128, 0, 128);"&gt;{&lt;/span&gt;&lt;span style="color: rgb(128, 0, 128);"&gt;}&lt;/span&gt;
 instance_variables&lt;span style="color: rgb(128, 128, 48);"&gt;.&lt;/span&gt;&lt;span style="color: rgb(128, 0, 0); font-weight: bold;"&gt;each&lt;/span&gt; &lt;span style="color: rgb(128, 0, 0); font-weight: bold;"&gt;do&lt;/span&gt; |name|
   assigns&lt;span style="color: rgb(128, 128, 48);"&gt;[&lt;/span&gt; name&lt;span style="color: rgb(128, 128, 48);"&gt;[&lt;/span&gt;&lt;span style="color: rgb(0, 140, 0);"&gt;1&lt;/span&gt;&lt;span style="color: rgb(128, 128, 48);"&gt;.&lt;/span&gt;&lt;span style="color: rgb(128, 128, 48);"&gt;.&lt;/span&gt;&lt;span style="color: rgb(128, 128, 48);"&gt;-&lt;/span&gt;&lt;span style="color: rgb(0, 140, 0);"&gt;1&lt;/span&gt;&lt;span style="color: rgb(128, 128, 48);"&gt;]&lt;/span&gt; &lt;span style="color: rgb(128, 128, 48);"&gt;]&lt;/span&gt; &lt;span style="color: rgb(128, 128, 48);"&gt;=&lt;/span&gt;  instance_variable_get&lt;span style="color: rgb(128, 128, 48);"&gt;(&lt;/span&gt;name&lt;span style="color: rgb(128, 128, 48);"&gt;)&lt;/span&gt;
&lt;span style="color: rgb(128, 0, 0); font-weight: bold;"&gt;  end&lt;/span&gt;
 Markaby::Builder&lt;span style="color: rgb(128, 128, 48);"&gt;.&lt;/span&gt;&lt;span style="color: rgb(128, 0, 0); font-weight: bold;"&gt;new&lt;/span&gt;&lt;span style="color: rgb(128, 128, 48);"&gt;(&lt;/span&gt;assigns, &lt;span style="color: rgb(128, 0, 0); font-weight: bold;"&gt;self&lt;/span&gt;&lt;span style="color: rgb(128, 128, 48);"&gt;)&lt;/span&gt;&lt;span style="color: rgb(128, 128, 48);"&gt;.&lt;/span&gt;capture&lt;span style="color: rgb(128, 128, 48);"&gt;(&lt;/span&gt;&amp;&lt;span style="color: rgb(64, 0, 0);"&gt;proc&lt;/span&gt;&lt;span style="color: rgb(128, 128, 48);"&gt;)&lt;/span&gt;
&lt;span style="color: rgb(128, 0, 0); font-weight: bold;"&gt;end&lt;/span&gt;
&lt;/pre&gt;

We need to collect the instance variables of the current template and pass a hash of instance variable names along with their values to the markaby builder. As second parameter we pass the current template, so that the builder can access other helper methods.

Ok, let's rewrite the options_for_select helper. The method takes an array of values which should be displayed as options. Alternatively you may pass an list of pairs like [['first',1],['second',2] or an Hash, which maps from option labels to their values.

One thing I did was to refactor the is_selected test into a lambda. It is cleaner to separate the test and probably more efficient. Inside the loop we are testing , if we have pairs or simple values and generate markup by sending the option method to the builder, which causes the markaby builder to generate an option tag. Tag attributes are defined with a hash, which we pass to the option method. A tag method takes an optional block, which defines the content of a tag, in our case simply the text of the option.

&lt;pre style="background: rgb(255, 255, 255) none repeat scroll 0% 50%; color: rgb(0, 0, 0); -moz-background-clip: -moz-initial; -moz-background-origin: -moz-initial; -moz-background-inline-policy: -moz-initial;"&gt;&lt;span style="color: rgb(128, 0, 0); font-weight: bold;"&gt;def&lt;/span&gt; options_for_select&lt;span style="color: rgb(128, 128, 48);"&gt;(&lt;/span&gt;container, selected &lt;span style="color: rgb(128, 128, 48);"&gt;=&lt;/span&gt; &lt;span style="color: rgb(128, 0, 0); font-weight: bold;"&gt;nil&lt;/span&gt;&lt;span style="color: rgb(128, 128, 48);"&gt;)&lt;/span&gt;
 container &lt;span style="color: rgb(128, 128, 48);"&gt;=&lt;/span&gt; container&lt;span style="color: rgb(128, 128, 48);"&gt;.&lt;/span&gt;to_a &lt;span style="color: rgb(128, 0, 0); font-weight: bold;"&gt;if&lt;/span&gt; Hash &lt;span style="color: rgb(128, 128, 48);"&gt;=&lt;/span&gt;&lt;span style="color: rgb(128, 128, 48);"&gt;=&lt;/span&gt;&lt;span style="color: rgb(128, 128, 48);"&gt;=&lt;/span&gt; container

&lt;span style="color: rgb(128, 0, 0); font-weight: bold;"&gt;  if&lt;/span&gt; selected&lt;span style="color: rgb(128, 128, 48);"&gt;.&lt;/span&gt;respond_to?&lt;span style="color: rgb(128, 128, 48);"&gt;(&lt;/span&gt;:include?&lt;span style="color: rgb(128, 128, 48);"&gt;)&lt;/span&gt; &lt;span style="color: rgb(128, 0, 0); font-weight: bold;"&gt;and&lt;/span&gt; !selected&lt;span style="color: rgb(128, 128, 48);"&gt;.&lt;/span&gt;is_a?&lt;span style="color: rgb(128, 128, 48);"&gt;(&lt;/span&gt;&lt;span style="color: rgb(64, 0, 0);"&gt;String&lt;/span&gt;&lt;span style="color: rgb(128, 128, 48);"&gt;)&lt;/span&gt;
   is_selected &lt;span style="color: rgb(128, 128, 48);"&gt;=&lt;/span&gt; &lt;span style="color: rgb(64, 0, 0);"&gt;lambda&lt;/span&gt; &lt;span style="color: rgb(128, 0, 128);"&gt;{&lt;/span&gt; |e| selected&lt;span style="color: rgb(128, 128, 48);"&gt;.&lt;/span&gt;include? e &lt;span style="color: rgb(128, 0, 128);"&gt;}&lt;/span&gt;
&lt;span style="color: rgb(128, 0, 0); font-weight: bold;"&gt;  else&lt;/span&gt;
   is_selected &lt;span style="color: rgb(128, 128, 48);"&gt;=&lt;/span&gt; &lt;span style="color: rgb(64, 0, 0);"&gt;lambda&lt;/span&gt; &lt;span style="color: rgb(128, 0, 128);"&gt;{&lt;/span&gt; |e| selected &lt;span style="color: rgb(128, 128, 48);"&gt;=&lt;/span&gt;&lt;span style="color: rgb(128, 128, 48);"&gt;=&lt;/span&gt; e &lt;span style="color: rgb(128, 0, 128);"&gt;}&lt;/span&gt;
&lt;span style="color: rgb(128, 0, 0); font-weight: bold;"&gt;  end&lt;/span&gt;

 is_pair &lt;span style="color: rgb(128, 128, 48);"&gt;=&lt;/span&gt; &lt;span style="color: rgb(64, 0, 0);"&gt;lambda&lt;/span&gt; &lt;span style="color: rgb(128, 0, 128);"&gt;{&lt;/span&gt;|e| !e&lt;span style="color: rgb(128, 128, 48);"&gt;.&lt;/span&gt;is_a?&lt;span style="color: rgb(128, 128, 48);"&gt;(&lt;/span&gt;&lt;span style="color: rgb(64, 0, 0);"&gt;String&lt;/span&gt;&lt;span style="color: rgb(128, 128, 48);"&gt;)&lt;/span&gt; &lt;span style="color: rgb(128, 0, 0); font-weight: bold;"&gt;and&lt;/span&gt; e&lt;span style="color: rgb(128, 128, 48);"&gt;.&lt;/span&gt;respond_to?&lt;span style="color: rgb(128, 128, 48);"&gt;(&lt;/span&gt;:first&lt;span style="color: rgb(128, 128, 48);"&gt;)&lt;/span&gt; &lt;span style="color: rgb(128, 0, 0); font-weight: bold;"&gt;and&lt;/span&gt; e&lt;span style="color: rgb(128, 128, 48);"&gt;.&lt;/span&gt;respond_to?&lt;span style="color: rgb(128, 128, 48);"&gt;(&lt;/span&gt;:last&lt;span style="color: rgb(128, 128, 48);"&gt;)&lt;/span&gt; &lt;span style="color: rgb(128, 0, 128);"&gt;}&lt;/span&gt;

 markaby &lt;span style="color: rgb(128, 0, 0); font-weight: bold;"&gt;do&lt;/span&gt;
   container&lt;span style="color: rgb(128, 128, 48);"&gt;.&lt;/span&gt;&lt;span style="color: rgb(128, 0, 0); font-weight: bold;"&gt;each&lt;/span&gt; &lt;span style="color: rgb(128, 0, 0); font-weight: bold;"&gt;do&lt;/span&gt; |element|
&lt;span style="color: rgb(128, 0, 0); font-weight: bold;"&gt;      if&lt;/span&gt; is_pair&lt;span style="color: rgb(128, 128, 48);"&gt;[&lt;/span&gt;element&lt;span style="color: rgb(128, 128, 48);"&gt;]&lt;/span&gt;
&lt;span style="color: rgb(128, 0, 0); font-weight: bold;"&gt;        if&lt;/span&gt; is_selected&lt;span style="color: rgb(128, 128, 48);"&gt;[&lt;/span&gt;element&lt;span style="color: rgb(128, 128, 48);"&gt;.&lt;/span&gt;last&lt;span style="color: rgb(128, 128, 48);"&gt;]&lt;/span&gt;
         option&lt;span style="color: rgb(128, 128, 48);"&gt;(&lt;/span&gt;:value &lt;span style="color: rgb(128, 128, 48);"&gt;=&lt;/span&gt;&gt; element&lt;span style="color: rgb(128, 128, 48);"&gt;.&lt;/span&gt;last, :selected &lt;span style="color: rgb(128, 128, 48);"&gt;=&lt;/span&gt;&gt; &lt;span style="color: rgb(0, 0, 230);"&gt;'selected'&lt;/span&gt;&lt;span style="color: rgb(128, 128, 48);"&gt;)&lt;/span&gt; &lt;span style="color: rgb(128, 0, 128);"&gt;{&lt;/span&gt; h element&lt;span style="color: rgb(128, 128, 48);"&gt;.&lt;/span&gt;first &lt;span style="color: rgb(128, 0, 128);"&gt;}&lt;/span&gt;
       &lt;span style="color: rgb(128, 0, 0); font-weight: bold;"&gt;else&lt;/span&gt;
         option&lt;span style="color: rgb(128, 128, 48);"&gt;(&lt;/span&gt;:value &lt;span style="color: rgb(128, 128, 48);"&gt;=&lt;/span&gt;&gt; element&lt;span style="color: rgb(128, 128, 48);"&gt;.&lt;/span&gt;last&lt;span style="color: rgb(128, 128, 48);"&gt;)&lt;/span&gt; &lt;span style="color: rgb(128, 0, 128);"&gt;{&lt;/span&gt; h element&lt;span style="color: rgb(128, 128, 48);"&gt;.&lt;/span&gt;first &lt;span style="color: rgb(128, 0, 128);"&gt;}&lt;/span&gt;
       &lt;span style="color: rgb(128, 0, 0); font-weight: bold;"&gt;end&lt;/span&gt;
     &lt;span style="color: rgb(128, 0, 0); font-weight: bold;"&gt;else&lt;/span&gt;
&lt;span style="color: rgb(128, 0, 0); font-weight: bold;"&gt;        if&lt;/span&gt; is_selected&lt;span style="color: rgb(128, 128, 48);"&gt;[&lt;/span&gt;element&lt;span style="color: rgb(128, 128, 48);"&gt;]&lt;/span&gt;
         option&lt;span style="color: rgb(128, 128, 48);"&gt;(&lt;/span&gt;:value &lt;span style="color: rgb(128, 128, 48);"&gt;=&lt;/span&gt;&gt; element, :selected &lt;span style="color: rgb(128, 128, 48);"&gt;=&lt;/span&gt;&gt; &lt;span style="color: rgb(0, 0, 230);"&gt;'selected'&lt;/span&gt;&lt;span style="color: rgb(128, 128, 48);"&gt;)&lt;/span&gt; &lt;span style="color: rgb(128, 0, 128);"&gt;{&lt;/span&gt; h element &lt;span style="color: rgb(128, 0, 128);"&gt;}&lt;/span&gt;
       &lt;span style="color: rgb(128, 0, 0); font-weight: bold;"&gt;else&lt;/span&gt;
         option&lt;span style="color: rgb(128, 128, 48);"&gt;(&lt;/span&gt;:value &lt;span style="color: rgb(128, 128, 48);"&gt;=&lt;/span&gt;&gt; element&lt;span style="color: rgb(128, 128, 48);"&gt;)&lt;/span&gt; &lt;span style="color: rgb(128, 0, 128);"&gt;{&lt;/span&gt; h element &lt;span style="color: rgb(128, 0, 128);"&gt;}&lt;/span&gt;
       &lt;span style="color: rgb(128, 0, 0); font-weight: bold;"&gt;end&lt;/span&gt;
     &lt;span style="color: rgb(128, 0, 0); font-weight: bold;"&gt;end&lt;/span&gt;
   &lt;span style="color: rgb(128, 0, 0); font-weight: bold;"&gt;end&lt;/span&gt;
&lt;span style="color: rgb(128, 0, 0); font-weight: bold;"&gt;  end&lt;/span&gt;
&lt;span style="color: rgb(128, 0, 0); font-weight: bold;"&gt;end&lt;/span&gt;
&lt;/pre&gt;
Our defined markaby method is even more useful, we can accept a block for our helper method and use it inside the markaby code:

&lt;pre style="background: rgb(255, 255, 255) none repeat scroll 0% 50%; color: rgb(0, 0, 0); -moz-background-clip: -moz-initial; -moz-background-origin: -moz-initial; -moz-background-inline-policy: -moz-initial;"&gt;&lt;span style="color: rgb(128, 0, 0); font-weight: bold;"&gt;def&lt;/span&gt; tasks&lt;span style="color: rgb(128, 128, 48);"&gt;(&lt;/span&gt;&amp;block&lt;span style="color: rgb(128, 128, 48);"&gt;)&lt;/span&gt;
 markaby &lt;span style="color: rgb(128, 0, 0); font-weight: bold;"&gt;do&lt;/span&gt;
   div&lt;span style="color: rgb(128, 128, 48);"&gt;.&lt;/span&gt;tasks &lt;span style="color: rgb(128, 0, 128);"&gt;{&lt;/span&gt;
     ul &lt;span style="color: rgb(128, 0, 128);"&gt;{&lt;/span&gt;
       markaby&lt;span style="color: rgb(128, 128, 48);"&gt;(&lt;/span&gt;&amp;amp;amp;block&lt;span style="color: rgb(128, 128, 48);"&gt;)&lt;/span&gt;
     &lt;span style="color: rgb(128, 0, 128);"&gt;}&lt;/span&gt;
   &lt;span style="color: rgb(128, 0, 128);"&gt;}&lt;/span&gt;
&lt;span style="color: rgb(128, 0, 0); font-weight: bold;"&gt;  end&lt;/span&gt;
&lt;span style="color: rgb(128, 0, 0); font-weight: bold;"&gt;end&lt;/span&gt;
&lt;/pre&gt;

If we have a common pattern like a list of tasks for many templates, we can generate the common code with the tasks method and put the actual tasks in the block:
&lt;pre style="background: rgb(255, 255, 255) none repeat scroll 0% 50%; color: rgb(0, 0, 0); -moz-background-clip: -moz-initial; -moz-background-origin: -moz-initial; -moz-background-inline-policy: -moz-initial;"&gt;tasks &lt;span style="color: rgb(128, 0, 128);"&gt;{&lt;/span&gt;
 task &lt;span style="color: rgb(0, 0, 230);"&gt;'Back to articles'&lt;/span&gt;&lt;span style="color: rgb(128, 128, 48);"&gt;.&lt;/span&gt;t, articles_url
 task :edit, @article
 task :versions, @article
&lt;span style="color: rgb(128, 0, 128);"&gt;}&lt;/span&gt;
&lt;/pre&gt;So, you can see, there is also a task helper, which is defined as follows:

&lt;pre style="background: rgb(255, 255, 255) none repeat scroll 0% 50%; color: rgb(0, 0, 0); -moz-background-clip: -moz-initial; -moz-background-origin: -moz-initial; -moz-background-inline-policy: -moz-initial;"&gt;&lt;span style="color: rgb(128, 0, 0); font-weight: bold;"&gt;def&lt;/span&gt; task&lt;span style="color: rgb(128, 128, 48);"&gt;(&lt;/span&gt;text, url_or_resource, html_options&lt;span style="color: rgb(128, 128, 48);"&gt;=&lt;/span&gt;&lt;span style="color: rgb(128, 0, 128);"&gt;{&lt;/span&gt;&lt;span style="color: rgb(128, 0, 128);"&gt;}&lt;/span&gt;&lt;span style="color: rgb(128, 128, 48);"&gt;)&lt;/span&gt;
&lt;span style="color: rgb(128, 0, 0); font-weight: bold;"&gt;  if&lt;/span&gt; text&lt;span style="color: rgb(128, 128, 48);"&gt;.&lt;/span&gt;is_a? Symbol
   task &lt;span style="color: rgb(0, 0, 230);"&gt;"#{text.to_s.humanize}"&lt;/span&gt;&lt;span style="color: rgb(128, 128, 48);"&gt;.&lt;/span&gt;t, &lt;span style="color: rgb(128, 0, 128);"&gt;{&lt;/span&gt;:action &lt;span style="color: rgb(128, 128, 48);"&gt;=&lt;/span&gt;&gt; text, :id &lt;span style="color: rgb(128, 128, 48);"&gt;=&lt;/span&gt;&gt; url_or_resource&lt;span style="color: rgb(128, 0, 128);"&gt;}&lt;/span&gt;, html_options
&lt;span style="color: rgb(128, 0, 0); font-weight: bold;"&gt;  else&lt;/span&gt;
   markaby &lt;span style="color: rgb(128, 0, 128);"&gt;{&lt;/span&gt; li &lt;span style="color: rgb(128, 0, 128);"&gt;{&lt;/span&gt; link_to text, url_or_resource, html_options &lt;span style="color: rgb(128, 0, 128);"&gt;}&lt;/span&gt; &lt;span style="color: rgb(128, 0, 128);"&gt;}&lt;/span&gt;
&lt;span style="color: rgb(128, 0, 0); font-weight: bold;"&gt;  end&lt;/span&gt;
&lt;span style="color: rgb(128, 0, 0); font-weight: bold;"&gt;end&lt;/span&gt;
&lt;/pre&gt;
If the link text is a symbol, we are going to infer the url from the action name which is the first parameter and the recource, which is the second parameter in this case. Otherwise we generate a list element and delegate the arguments to the link_to helper.

By using this simple abstraction, we have hidden the details of task links, which is really DRY. Instead of repeating the same pattern over and over again, we have a common place to decide, how the tasks should look like. Markaby makes it really easy to generate nested structures, as it takes advantage of ruby's block syntax.

In a future post I will discuss an advanced form builder, like the one already implemented in Rails, but this time implemented with the help of Markaby. It is even possible to separate form generation and form layouting by using a separate form renderer. So step by, if you want to delve into further Markaby Magic.</content><link rel="alternate" type="text/html" href="http://feeds.feedburner.com/~r/TheMysteriousWaysOfRuby/~3/107135139/rendering-markaby-in-your-helpers.html" title="Rendering markaby in your helpers" /><link rel="replies" type="text/html" href="http://www.blogger.com/comment.g?blogID=3250677721251884719&amp;postID=6667716891631358820" title="0 Comments" /><link rel="replies" type="application/atom+xml" href="http://themysteriouswaysofruby.blogspot.com/feeds/6667716891631358820/comments/default" title="Post Comments" /><link rel="self" type="application/atom+xml" href="http://themysteriouswaysofruby.blogspot.com/feeds/posts/default/6667716891631358820" /><link rel="edit" type="application/atom+xml" href="http://www.blogger.com/feeds/3250677721251884719/posts/default/6667716891631358820" /><author><name>Matthias Georgi</name><uri>http://www.blogger.com/profile/16422189308202543523</uri><email>noreply@blogger.com</email></author><feedburner:origLink>http://themysteriouswaysofruby.blogspot.com/2007/04/rendering-markaby-in-your-helpers.html</feedburner:origLink></entry></feed>
