<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0"
	xmlns:content="http://purl.org/rss/1.0/modules/content/"
	xmlns:wfw="http://wellformedweb.org/CommentAPI/"
	xmlns:dc="http://purl.org/dc/elements/1.1/"
	xmlns:atom="http://www.w3.org/2005/Atom"
	xmlns:sy="http://purl.org/rss/1.0/modules/syndication/"
	xmlns:slash="http://purl.org/rss/1.0/modules/slash/"
	>

<channel>
	<title>McKinney Station &#187; pagination</title>
	<atom:link href="http://www.mckinneystation.com/categories/pagination/feed/" rel="self" type="application/rss+xml" />
	<link>http://www.mckinneystation.com</link>
	<description>Ruby on Rails web application development for Dallas/Fort Worth and all of North Texas.</description>
	<lastBuildDate>Wed, 02 Sep 2009 14:29:42 +0000</lastBuildDate>
	<generator>http://wordpress.org/?v=2.9</generator>
	<language>en</language>
	<sy:updatePeriod>hourly</sy:updatePeriod>
	<sy:updateFrequency>1</sy:updateFrequency>
			<item>
		<title>Adding an iPhone Interface to an Existing Rails Application</title>
		<link>http://www.mckinneystation.com/2009/02/20/adding-an-iphone-interface-to-an-existing-rails-application/</link>
		<comments>http://www.mckinneystation.com/2009/02/20/adding-an-iphone-interface-to-an-existing-rails-application/#comments</comments>
		<pubDate>Fri, 20 Feb 2009 17:21:20 +0000</pubDate>
		<dc:creator>Geoffrey</dc:creator>
				<category><![CDATA[AJAX]]></category>
		<category><![CDATA[CSS]]></category>
		<category><![CDATA[Entrepreneurial]]></category>
		<category><![CDATA[Rails]]></category>
		<category><![CDATA[Web Applications]]></category>
		<category><![CDATA[Web Development]]></category>
		<category><![CDATA[iPhone]]></category>
		<category><![CDATA[iUI]]></category>
		<category><![CDATA[pagination]]></category>

		<guid isPermaLink="false">http://www.mckinneystation.com/?p=95</guid>
		<description><![CDATA[I have been gradually adding new features to my StagingTracks.com website.  Really, it is a place where I can try out new things outside the office.  I have upgraded the UI to be a little cleaner by using the Blueprint CSS framework.  It was a easy way to normalize the CSS across [...]]]></description>
			<content:encoded><![CDATA[<p>I have been gradually adding new features to my <a href="/http://www.stagingtracks.com">StagingTracks.com</a> website.  Really, it is a place where I can try out new things outside the office.  I have upgraded the UI to be a little cleaner by using the <a href="http://www.blueprintcss.org">Blueprint <span class="caps">CSS</span> framework</a>.  It was a easy way to normalize the <span class="caps">CSS</span> across browsers and easily implement a column-based layout.  I also added <a href="http://twitter.com/stagingtracks">Twitter notifications</a> when new shops, clubs, and shows are added and reminders for upcoming shows each week.  Does the model railroading community really need all of this?  Probably not, but it helps me keep my skillz sharp.</p>
<h2>Finding Shops, Clubs, and Shows on your iPhone</h2>
<p><a href="http://www.stagingtracks.com/posts/getting_started">When I built StagingTracks a few years ago</a>, I did it because I was traveling and wanted to easily find the model railroading community wherever I was.  As it has grown over the past few years, so has technology.  While it was possible to navigate the StagingTracks website using a browser on the phone, it was not optimal.  Since this is my little sandbox for experimenting, I wanted to see how difficult it would be to add an optimized iPhone interface.</p>
<h2>Native app or Web app?</h2>
<p>I spend my daylight hours <a href="http://www.thinkrelevance.com">developing web applications for others</a>, so it made sense that I should reuse the infrastructure that I already had in place.  I didn&#8217;t want to learn iPhone <span class="caps">SDK</span> and all that is involved with that right now and I had recently come across the <a href="http://code.google.com/p/iui/">iUi javascript and css framework</a>.  iUI can give web applications a native iPhone application feel, so I just needed to see how to incorporate it into my &#8220;legacy&#8221; Rails application.</p>
<h2>Resources</h2>
<p>A quick Google search for iUI and Rails turned up Ben Smith&#8217;s excellent <a href="http://www.slashdotdash.net/2007/12/04/iphone-on-rails-creating-an-iphone-optimised-version-of-your-rails-site-using-iui-and-rails-2/">iPhone on Rails</a> article.</p>
<h3>iPhoney</h3>
<p>Reading through the article, I downloaded <a href="http://www.marketcircle.com/iphoney/">iPhoney</a> for quick testing without an iPhone.  Be sure to use the iPhone User Agent in the iPhoney menu.</p>
<h3>Local Subdomain for Testing</h3>
<p>I was going to serve the iPhone version from the subdomain iphone.stagingtracks.com, so I needed to setup something similar in my local development environment.  Fortunately, this was very easy with the <a href="http://github.com/bjeanes/ghost/tree/master">Ruby Ghost gem</a> found via Robby Russell&#8217;s <a href="http://www.robbyonrails.com/articles/2009/01/12/get-to-know-a-gem-ghost">Get to know a gem: Ghost</a>.</p>
<pre><code>sudo ghost add iphone.localhost.com</code></pre>
<p>We needed to add the .com so that the call to the request.subdomains will pick out the iphone portion.</p>
<h3>iUI Framework</h3>
<p>After downloading the iUI framework from the <a href="http://code.google.com/p/iui/">project site</a>, I moved everything into its rightful place.</p>
<pre>
<code>public
  - stylesheets
    - iui.css
  - javascripts
    - iui.js
  - images
    - iui
      - copy all of the .gif and .png files into here</code>
</pre>
<p>Because I moved the images into the <code>/images/iui</code> folder, I needed to update the image locations in the iUI css.  A quick find/replace and I was ready to go.</p>
<h2>Application changes</h2>
<p>I won&#8217;t go into all the details since <a href="http://www.slashdotdash.net/2007/12/04/iphone-on-rails-creating-an-iphone-optimised-version-of-your-rails-site-using-iui-and-rails-2/">Ben&#8217;s article</a> hit most of the high points.  Here are the few additional bits that I came across as I was adding my iPhone interface.</p>
<h3>Basic approach</h3>
<p>The basic approach to adding the iphone interface is to update the controller to render the iphone partial without the layout (since everything is <span class="caps">AJAX</span>) and then create an iphone template.</p>
<p>In posts_controller.rb change from:</p>
<pre><code>def show
  @post = Post.find(params[:id])
end  </code></pre>
<p>to</p>
<p><strong>UPDATE:</strong>: format.html should come before format.iphone.  For some reason it was working for browsers that were not IE.  Weird.</p>
<pre><code>def show
  @post = Post.find(params[:id])
  respond_to do |format|
    format.html
    format.iphone { render :layout =&gt; false }
  end
end  </code></pre>
<p>iphone template posts/show.iphone.erb:</p>
<pre><code>&lt;div class="panel" title="&lt;%= @post.title %&gt;" selected="true"&gt;
  &lt;h3&gt;&lt;%= @post.title %&gt;&lt;/h3&gt;
  &lt;%= render :partial =&gt; 'post.html.erb', :locals =&gt; {:post =&gt; @post} %&gt;
&lt;/div&gt;  </code></pre>
<h3>Search Button</h3>
<p>Since one of the more interesing features of StagingTracks is the ability to search for organizations near you, I wanted that to be prominent.  By adding a &#8220;button&#8221; link to the toolbar, it now shows up on every page.</p>
<p>In application.iphone.erb:</p>
<pre><code>&lt;div class="toolbar"&gt;
  &lt;h1 id="pageTitle"&gt;&lt;/h1&gt;
  <a id="backButton" class="button" href="#"></a>
  &lt;%= link_to "Search", search_path, :class =&gt; 'button' %&gt;
&lt;/div&gt;</code></pre>
<h3>Dynamically Growing Lists (a.k.a pagination)</h3>
<p>Since I already had paging in place for the blog posts, I wanted to be able to reuse that, if possible.  Turns out that was pretty easy to add as well.  I needed to separate the post_items into a separate partial so that I could return the next page of <code>&lt;li&gt;'s</code> to replace the &#8220;More news&#8230;&#8221; link (notice the target for the &#8220;More news&#8230;&#8221; link is &#8220;_replace&#8221;).</p>
<p>In posts/index.iphone.erb</p>
<pre><code>&lt;ul title="News" selected="true"&gt;
  &lt;%= render :partial =&gt; 'post_items', :locals =&gt; {:posts =&gt; @posts} %&gt;
&lt;/ul&gt;</code></pre>
<p>In posts/_post_items.iphone.erb</p>
<pre><code>&lt;% posts.each do |post| %&gt;
  &lt;li&gt;&lt;%= link_to post.title, post %&gt;&lt;/li&gt;
&lt;% end %&gt;
&lt;%= content_tag :li, link_to("More news...", posts_path(:page =&gt; posts.next_page), :target =&gt; "_replace") if posts.next_page %&gt;</code></pre>
<p>A quick change in the posts_controller.rb from:</p>
<pre><code>def index
  @posts = Post.latest.published.paginate :page =&gt; page, :order =&gt; 'published_at desc'
end</code></pre>
<p>to:</p>
<p><strong>UPDATE:</strong>: Same change to the ordering of format.html and  format.iphone.  </p>
<pre><code>def index
  @posts = Post.latest.published.paginate :page =&gt; page, :order =&gt; 'published_at desc'
  respond_to do |format|
    format.html
    format.iphone do
      if page == 1
          render :layout =&gt; false
        else
          render :layout =&gt; false, :partial =&gt; "post_items", :locals =&gt; {:posts =&gt; @posts}
      end
    end
  end
end</code></pre>
<h3>Styling Form Select Inputs</h3>
<p>My search form has a dropdown for choosing the country that you want to search.  By default, this did not look very nice.  Since it didn&#8217;t need a label, I just left it out in the form and added some additional <span class="caps">CSS</span>.</p>
<p>In search/index.iphone.erb</p>
<pre><code>&lt;% form_tag(search_path, :class =&gt; 'panel', :title =&gt; 'Search')  do %&gt;
  &lt;h2&gt;Find Local Shops, Clubs, and Shows&lt;/h2&gt;

  &lt;%= content_tag :p, flash[:error], :class =&gt; 'error' if flash[:error] %&gt;

  &lt;fieldset&gt;
    &lt;div class="row"&gt;
      &lt;%= country_select :search, :country, ['United States', 'Canada'], {} %&gt;
    &lt;/div&gt;  

    &lt;div class="row"&gt;
      &lt;label for='search_city'&gt;City&lt;/label&gt;
      &lt;input type="text" value="" name="search[city]" id="search_city"/&gt;
    &lt;/div&gt;

    &lt;div class="row"&gt;
      &lt;label for='search_state'&gt;State&lt;/label&gt;
      &lt;input type="text" value="" name="search[state]" id="search_state"/&gt;
    &lt;/div&gt;
  &lt;/fieldset&gt;
  &lt;%= link_to "Submit", "#", :class =&gt; 'whiteButton', :type =&gt; "submit" %&gt;
&lt;% end %&gt;</code></pre>
<p>And in my extra iphone.css (anything else that I needed to add to iui.css)</p>
<pre><code>.row &gt; select {
    box-sizing: border-box;
    -webkit-box-sizing: border-box;
    margin: 0;
    border: none;
    padding: 0;
    height: 42px;
    background: none;
    font-size: 16px;
    width: 100%;
}

.error {
  font-weight: bold;
  color: #8a1f11;
  margin-left: 14px;
}</code></pre>
<h2>Conclusion</h2>
<p>All told, I probably spent less than eight hours over a couple of nights adding a simple iPhone interface to my existing application.  I still want to look in to modifying the <span class="caps">CSS</span> more to have it look more like the regular StagingTracks website, but that can come later.  This was a fun little experiment.</p>
]]></content:encoded>
			<wfw:commentRss>http://www.mckinneystation.com/2009/02/20/adding-an-iphone-interface-to-an-existing-rails-application/feed/</wfw:commentRss>
		<slash:comments>3</slash:comments>
		</item>
		<item>
		<title>Pagination with acts_as_taggable_on_steroids, acts_as_ferret, and will_paginate</title>
		<link>http://www.mckinneystation.com/2007/08/20/pagination-with-acts_as_taggable_on_steroids-acts_as_ferret-and-will_paginate/</link>
		<comments>http://www.mckinneystation.com/2007/08/20/pagination-with-acts_as_taggable_on_steroids-acts_as_ferret-and-will_paginate/#comments</comments>
		<pubDate>Mon, 20 Aug 2007 20:40:18 +0000</pubDate>
		<dc:creator>Geoffrey</dc:creator>
				<category><![CDATA[Rails]]></category>
		<category><![CDATA[ferret]]></category>
		<category><![CDATA[pagination]]></category>
		<category><![CDATA[tagging]]></category>

		<guid isPermaLink="false">http://www.mckinneystation.com/2007/08/20/pagination-with-acts_as_taggable_on_steroids-acts_as_ferret-and-will_paginate/</guid>
		<description><![CDATA[UPDATE (6/6/08):  This is out of date for the latest Rails and will_paginate.
So I needed to paginate large collection of data in a new app I am working on.  will_paginate is a good drop in replacement for the the default rails paginator.  But I am also using acts_as_taggable_on_steroids (for tagging) and acts_as_ferret [...]]]></description>
			<content:encoded><![CDATA[<p>UPDATE (6/6/08):  This is out of date for the latest Rails and will_paginate.</p>
<p>So I needed to paginate large collection of data in a new app I am working on.  <a href="http://errtheblog.com/post/4791">will_paginate</a> is a good drop in replacement for the the default rails paginator.  But I am also using <a href="http://agilewebdevelopment.com/plugins/acts_as_taggable_on_steroids">acts_as_taggable_on_steroids</a> (for tagging) and <a href="http://projects.jkraemer.net/acts_as_ferret/wiki">acts_as_ferret</a> (for searching), so i needed special pagination for those scenarios.  </p>
<h2>acts_as_ferret</h2>
<p>A quick Google search led me to <a href="http://opensoul.org/2007/8/17/acts_as_ferret-will_paginate">this for paginating acts_as_ferret search results</a>.  I modified the offset calculation and ended up with this:</p>
<pre>
<code>module ActsAsFerret
  module ClassMethods
    def paginate_search(query, options = {})
      options, page, per_page = wp_parse_options!(options)
      offset = (page.to_i - 1) * per_page
      options.merge!(:offset =&gt; offset, :limit =&gt; per_page)
      result = result = find_by_contents(query, options)
      returning WillPaginate::Collection.new(page, per_page, result.total_hits) do |pager|
        pager.replace result
      end
    end
  end
end</code>
</pre>
<p>Drop that in a file lib/ferret_pagination.rb, require it in you environment.rb, and you can now do this in your controller:</p>
<pre>
<code>@entries = Entry.paginate_search params[:query],
                                       :page =&gt; params[:page],
                                       :per_page =&gt; 20</code>
</pre>
<h2>acts_as_taggable (on steroids)</h2>
<p>So with that out of the way, I was now ready to tackle paginating entries tagged with a certain tag.  Another quick google search turned up <a href="http://errtheblog.com/post/4791">some ideas in the will_paginate comments</a>.  I used <a href="http://blog.wolfman.com/articles/2007/07/30/paginating-acts_as_taggable-with-will_paginate">this one</a> as a starting point and this is what I ended up with:</p>
<pre>
<code>module ActiveRecord
  module Acts #:nodoc:
    module Taggable #:nodoc:
      module SingletonMethods
        # Return the number of time this class has been tagged with this tag
        def tagging_counts(tag)
          count_by_sql("select count(*) FROM tags, taggings WHERE " + sanitize_sql(['tags.name = ? AND tags.id = taggings.tag_id AND taggings.taggable_type = ?', tag, name]))
        end

        # paginate a call to find_tagged_with
        # tag is the tag to find
        # options is the option to use for pagination (:page, :per_page) and for find_tagged_with
        def paginate_by_tag(tag, options = {})
          options, page, per_page = wp_parse_options!(options)
          offset = (page.to_i - 1) * per_page
          options.merge!(:offset =&gt; offset, :limit =&gt; per_page.to_i)
          items = find_tagged_with(tag, options)
          count = tagging_counts(tag)
          returning WillPaginate::Collection.new(page, per_page, count) do |p|
            p.replace items
          end
        end
      end
    end
  end
end</code>
</pre>
<p>Again, drop that in a file lib/taggable_pagination.rb, require it in you environment.rb, and you can now do this in your controller:</p>
<pre>
<code>@entries = Entry.paginate_by_tag @tag.name,
                                     :order =&gt; 'entries.created_at DESC',
                                     :page =&gt; params[:page],
                                     :per_page =&gt; 20</code>
</pre>
<h2>Thanks</h2>
<p>Thanks to <a href="http://opensoul.org/">Brandon</a> for posting the ferret pagination code, <a href="http://blog.wolfman.com/">Jim</a> for the acts as taggable pagination code, and <a href="http://errtheblog.com/">PJ</a> for the will_paginate code.</p>
<p><strong>UPDATED:</strong> Corrected problem noted in comments</p>
]]></content:encoded>
			<wfw:commentRss>http://www.mckinneystation.com/2007/08/20/pagination-with-acts_as_taggable_on_steroids-acts_as_ferret-and-will_paginate/feed/</wfw:commentRss>
		<slash:comments>25</slash:comments>
		</item>
	</channel>
</rss>
