<?xml version="1.0" encoding="UTF-8"?>
<?xml-stylesheet type="text/xsl" media="screen" href="/~d/styles/atom10full.xsl"?><?xml-stylesheet type="text/css" media="screen" href="http://feeds.feedburner.com/~d/styles/itemcontent.css"?><feed xmlns="http://www.w3.org/2005/Atom" xmlns:feedburner="http://rssnamespace.org/feedburner/ext/1.0">

  <title>Wonderland</title>
  <subtitle>A Blog</subtitle>
  
  <link href="http://blog.leshill.org/" type="text/html" rel="alternate" />
  <updated>2009-10-11T12:00:56-04:00</updated>
  <author>
    <name>Les Hill</name>
    <email>blog@leshill.org</email>
  </author>
  <id>http://blog.leshill.org/</id>
  
  <link rel="self" href="http://feeds.feedburner.com/leshill" type="application/atom+xml" /><atom10:link xmlns:atom10="http://www.w3.org/2005/Atom" rel="hub" href="http://pubsubhubbub.appspot.com" /><entry>
    <title>The Simplest Thing That Could Possibly Work (With Gems)</title>
    <link rel="alternate" type="text/html" href="http://feedproxy.google.com/~r/leshill/~3/Csbpfvmis-0/the-simplest-thing-that-could-possibly-work-with-gems.html" />
    
    <id>tag:blog.leshill.org,2009-10-11:1255271197</id>
    
    <published>2009-10-11T10:26:37-04:00</published>
    <updated>2009-10-11T10:26:37-04:00</updated>
    <content type="html">&lt;p&gt;There has been some turmoil in the Ruby community over the &lt;a href="http://github.com"&gt;github&lt;/a&gt; decision to stop building gems.&lt;/p&gt;
&lt;p&gt;Since I have a forked gem that was previously hosted on github, I and my fellow users of the fork are directly affected.&lt;/p&gt;
&lt;p&gt;In my case, I am going to do the simplest thing that could possibly work.  Move my existing published gem under the same name&lt;sup class="footnote"&gt;&lt;a href="#fn1"&gt;1&lt;/a&gt;&lt;/sup&gt; (&lt;code&gt;leshill-will_paginate&lt;/code&gt;) over to &lt;a href="http://gemcutter.org"&gt;Gemcutter&lt;/a&gt;.  Why not follow along?&lt;/p&gt;
&lt;p&gt;Before getting started, I signed up with Gemcutter (go ahead, do so now).  Once you have confirmed your account, follow the very clear instructions and add your API key to &lt;code&gt;~/.gemrc&lt;/code&gt;.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;gemcutter_key: NOT_MY_REAL_API_KEY&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Gemcutter runs as a plugin to RubyGems, so lets install and configure it.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;sudo gem install gemcutter
gem tumble&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Great!  We are ready to start publishing. Gemcutter only accepts prebuilt gems, so we will have to build our gem locally first. Before doing that I will modify the &lt;code&gt;gemspec&lt;/code&gt; to make it clear that this is a fork by changing the name&lt;sup class="footnote"&gt;&lt;a href="#fn2"&gt;2&lt;/a&gt;&lt;/sup&gt; and a few other fields.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;Gem::Specification.new do |s|
  s.name    = 'leshill-will_paginate'
  s.version = '2.3.11'
  s.date    = '2009-10-11'
  s.summary = "Fork of the Most awesome pagination solution for Rails"
  s.description = "The will_paginate library provides a simple, yet powerful and extensible API for ActiveRecord pagination and rendering of pagination links in ActionView templates."
  s.authors  = ['Les Hill', 'Mislav Marohnić', 'PJ Hyett']
  s.email    = 'someone@example.com'
  s.homepage = 'http://github.com/leshill/will_paginate'&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Ok, now we can build it.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;gem build will_paginate.gemspec&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Ready?  Let&amp;#8217;s push it up to Gemcutter&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;gem push leshill-will_paginate-2.3.11.gem&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;We can now install my fork of &lt;a href="http://github.com/leshill/will_paginate"&gt;will_paginate&lt;/a&gt;:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;sudo gem install leshill-will_paginate&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Done!&lt;/p&gt;
&lt;p class="footnote" id="fn1"&gt;&lt;sup&gt;1&lt;/sup&gt; This is one source of heat (of many?) in the current &amp;#8216;What to do?&amp;#8217; gem discussion.  Who knew we were all unhappy with the github decision to publish gems as &lt;code&gt;user-gemname&lt;/code&gt;?&lt;/p&gt;
&lt;p class="footnote" id="fn2"&gt;&lt;sup&gt;2&lt;/sup&gt; github prepended the github user automatically, now we have to do so explicitly in the &lt;code&gt;gemspec&lt;/code&gt;.&lt;/p&gt;&lt;img src="http://feeds.feedburner.com/~r/leshill/~4/Csbpfvmis-0" height="1" width="1"/&gt;</content>
  <feedburner:origLink>http://blog.leshill.org/blog/2009/10/11/the-simplest-thing-that-could-possibly-work-with-gems.html</feedburner:origLink></entry>
  
  <entry>
    <title>and vs &amp;amp;&amp;amp; - Fight!</title>
    <link rel="alternate" type="text/html" href="http://feedproxy.google.com/~r/leshill/~3/ERUxkdYs6GM/and-v.html" />
    
    <id>tag:blog.leshill.org,2009-10-09:1255112554</id>
    
    <published>2009-10-09T14:22:34-04:00</published>
    <updated>2009-10-09T14:22:34-04:00</updated>
    <content type="html">&lt;p&gt;At &lt;a href="http://hashrocket.com"&gt;Hashrocket&lt;/a&gt; we are always expressing our views on code, both the good and the bad, about both our own code and code written by others. Recently, the issue of when to use the two flavors of Ruby logic operators: the punctation operators &lt;code&gt;&amp;amp;&amp;amp;&lt;/code&gt;, &lt;code&gt;||&lt;/code&gt;, &lt;code&gt;!&lt;/code&gt; or the english operators &lt;code&gt;and&lt;/code&gt;, &lt;code&gt;or&lt;/code&gt;, &lt;code&gt;not&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;Developers new to Ruby&lt;sup class="footnote"&gt;&lt;a href="#fn1"&gt;1&lt;/a&gt;&lt;/sup&gt; should be following the rule to only use the punctuation operators for logic:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;editable = user.admin? || item.editable?&lt;/code&gt;&lt;/pre&gt;
&lt;pre&gt;&lt;code&gt;if user.admin? &amp;amp;&amp;amp; item.editable?&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;and the english operators for flow control:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;render :action =&amp;gt; 'edit' and return if editable&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Once developers are proficient (see below for a quiz!) in Ruby, this rule should become a rule of thumb or just ignored.  For example, this reads better with &lt;code&gt;and&lt;/code&gt;:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;if user.admin? and item.editable?&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;So how do you know if you should be following the rule or just be aware of it? Answer this:&lt;/p&gt;
&lt;p&gt;What happens if we replace the &lt;code&gt;||&lt;/code&gt; with &lt;code&gt;or&lt;/code&gt; in the first example and why?&lt;sup class="footnote"&gt;&lt;a href="#fn2"&gt;2&lt;/a&gt;&lt;/sup&gt;&lt;/p&gt;
&lt;p class="footnote" id="fn1"&gt;&lt;sup&gt;1&lt;/sup&gt; Excluding those who are proficient in other languages and already recognize the issue.&lt;/p&gt;
&lt;p class="footnote" id="fn2"&gt;&lt;sup&gt;2&lt;/sup&gt; Did not know the answer?  Take a minute and learn &lt;a href="http://phrogz.net/ProgrammingRuby/language.html#table_18.4"&gt;Ruby&amp;#8217;s operator precedence&lt;/a&gt;&lt;/p&gt;&lt;img src="http://feeds.feedburner.com/~r/leshill/~4/ERUxkdYs6GM" height="1" width="1"/&gt;</content>
  <feedburner:origLink>http://blog.leshill.org/blog/2009/10/09/and-v.html</feedburner:origLink></entry>
  
  <entry>
    <title>Best Practice: No Chains in Controllers</title>
    <link rel="alternate" type="text/html" href="http://feedproxy.google.com/~r/leshill/~3/uvUy9MlraA8/best-practice-no-chains-in-controllers.html" />
    
    <id>tag:blog.leshill.org,2009-09-29:1254281940</id>
    
    <published>2009-09-29T23:39:00-04:00</published>
    <updated>2009-09-29T23:39:00-04:00</updated>
    <content type="html">&lt;p&gt;Dan Croak just posted &lt;a href="http://robots.thoughtbot.com/post/200254501/testing-named-scopes"&gt;testing named scopes&lt;/a&gt; which you should read right now.&lt;/p&gt;
&lt;p&gt;We also have been using the rule of thumb in the last paragraph: No Chains in Controllers.  Our experience is that not only do you immediately benefit from simpler tests, but we have also found that you get simpler and more expressive implementations.  If we do end up using a chain we usually encapsulate it in a method, keeping the tests and code clean.&lt;/p&gt;&lt;img src="http://feeds.feedburner.com/~r/leshill/~4/uvUy9MlraA8" height="1" width="1"/&gt;</content>
  <feedburner:origLink>http://blog.leshill.org/blog/2009/09/29/best-practice-no-chains-in-controllers.html</feedburner:origLink></entry>
  
  <entry>
    <title>Update for stub_chain for Mocha</title>
    <link rel="alternate" type="text/html" href="http://feedproxy.google.com/~r/leshill/~3/y-Y7MQlU6Zs/update-for-stub-chain-for-mocha.html" />
    
    <id>tag:blog.leshill.org,2009-08-05:1249495362</id>
    
    <published>2009-08-05T14:02:42-04:00</published>
    <updated>2009-08-05T14:02:42-04:00</updated>
    <content type="html">&lt;p&gt;Thanks to &lt;a href="http://technicalpickles.com"&gt;Josh Nichols&lt;/a&gt; for pointing out that the original code (and the RSpec mock code!) did not handle stubbing the same part of the chain multiple times, for example:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;stub_chain(:votes, :supporting, :count).returns(supporting_count)
stub_chain(:votes, :opposing, :count).returns(opposing_count)&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Here is the updated snippet that works when you stub the same part of the chain multiple times:&lt;/p&gt;
&lt;div class="CodeRay"&gt;
  &lt;div class="code"&gt;&lt;pre&gt;&lt;span class="no"&gt; 1&lt;/span&gt; &lt;span class="r"&gt;module&lt;/span&gt; &lt;span class="cl"&gt;StubChainMocha&lt;/span&gt;
&lt;span class="no"&gt; 2&lt;/span&gt;   &lt;span class="r"&gt;module&lt;/span&gt; &lt;span class="cl"&gt;Object&lt;/span&gt;
&lt;span class="no"&gt; 3&lt;/span&gt;     &lt;span class="r"&gt;def&lt;/span&gt; &lt;span class="fu"&gt;stub_chain&lt;/span&gt;(*methods)
&lt;span class="no"&gt; 4&lt;/span&gt;       &lt;span class="r"&gt;if&lt;/span&gt; methods.length &amp;gt; &lt;span class="i"&gt;1&lt;/span&gt;
&lt;span class="no"&gt; 5&lt;/span&gt;         next_in_chain = ::&lt;span class="co"&gt;Object&lt;/span&gt;.new
&lt;span class="no"&gt; 6&lt;/span&gt;         stubs(methods.shift).returns(next_in_chain)
&lt;span class="no"&gt; 7&lt;/span&gt;         next_in_chain.stub_chain(*methods)
&lt;span class="no"&gt; 8&lt;/span&gt;       &lt;span class="r"&gt;else&lt;/span&gt;
&lt;span class="no"&gt; 9&lt;/span&gt;         stubs(methods.shift)
&lt;span class="no"&gt;&lt;strong&gt;10&lt;/strong&gt;&lt;/span&gt;       &lt;span class="r"&gt;end&lt;/span&gt;
&lt;span class="no"&gt;11&lt;/span&gt;     &lt;span class="r"&gt;end&lt;/span&gt;
&lt;span class="no"&gt;12&lt;/span&gt;   &lt;span class="r"&gt;end&lt;/span&gt;
&lt;span class="no"&gt;13&lt;/span&gt; &lt;span class="r"&gt;end&lt;/span&gt;
&lt;span class="no"&gt;14&lt;/span&gt; 
&lt;span class="no"&gt;15&lt;/span&gt; &lt;span class="co"&gt;Object&lt;/span&gt;.send(&lt;span class="sy"&gt;:include&lt;/span&gt;, &lt;span class="co"&gt;StubChainMocha&lt;/span&gt;::&lt;span class="co"&gt;Object&lt;/span&gt;)&lt;/pre&gt;&lt;/div&gt;
&lt;/div&gt;
&lt;p&gt;Copy that into &lt;code&gt;spec/stub_chain_mocha.rb&lt;/code&gt; and then require it from &lt;code&gt;spec_helper.rb&lt;/code&gt;.&lt;/p&gt;&lt;img src="http://feeds.feedburner.com/~r/leshill/~4/y-Y7MQlU6Zs" height="1" width="1"/&gt;</content>
  <feedburner:origLink>http://blog.leshill.org/blog/2009/08/05/update-for-stub-chain-for-mocha.html</feedburner:origLink></entry>
  
  <entry>
    <title>Bouncy Bots!</title>
    <link rel="alternate" type="text/html" href="http://feedproxy.google.com/~r/leshill/~3/aEMmYmQWYg8/bouncy-bots.html" />
    
    <id>tag:blog.leshill.org,2009-07-01:1246457310</id>
    
    <published>2009-07-01T10:08:30-04:00</published>
    <updated>2009-07-01T10:08:30-04:00</updated>
    <content type="html">&lt;h3&gt;A simple negative captcha for Rails.&lt;/h3&gt;
&lt;p&gt;A negative captcha flips the normal captcha on its head, rather than asking humans identify themselves, we trick the bots into identifying themselves.  We do this by placing honey pots in a form that are invisible to a human, but visible to a bot.  When the bot submits the form, we look for the honey pot entries and discard the form submission if we find any.&lt;/p&gt;
&lt;p&gt;The source is MIT licensed and available on &lt;a href="http://github.com/leshill/bouncy_bots/tree/master"&gt;github&lt;/a&gt;.&lt;/p&gt;
&lt;h3&gt;Installing Bouncy Bots!&lt;/h3&gt;
&lt;p&gt;Bouncy Bots! is available as a gem from github.  Install the gem with the following command:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;% sudo gem install --source http://gems.github.com/ leshill-bouncy_bots&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;After you have the gem installed, add a &lt;code&gt;config.gem&lt;/code&gt; line to your &lt;code&gt;environment.rb&lt;/code&gt; file:&lt;/p&gt;
&lt;div class="CodeRay"&gt;
  &lt;div class="code"&gt;&lt;pre&gt;&lt;span class="no"&gt;1&lt;/span&gt; config.gem &lt;span class="s"&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="k"&gt;leshill-bouncy_bots&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;/span&gt;, &lt;span class="sy"&gt;:lib&lt;/span&gt; =&amp;gt; &lt;span class="s"&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="k"&gt;bouncy_bots&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;/span&gt;, &lt;span class="sy"&gt;:version&lt;/span&gt; =&amp;gt; &lt;span class="fl"&gt;0.1&lt;/span&gt;.&lt;span class="i"&gt;0&lt;/span&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;/div&gt;
&lt;h3&gt;Using Bouncy Bots!&lt;/h3&gt;
&lt;h4&gt;Controllers&lt;/h4&gt;
&lt;p&gt;Use the &lt;code&gt;bounce_bots&lt;/code&gt; macro in your controllers to detect and bounce bots.  The macro takes two parameters, the honey pot field name and the redirect path or url.  For example, to check for the field &lt;code&gt;:blog_url&lt;/code&gt; and redirect bots to the &lt;code&gt;pages_path&lt;/code&gt; :&lt;/p&gt;
&lt;div class="CodeRay"&gt;
  &lt;div class="code"&gt;&lt;pre&gt;&lt;span class="no"&gt;1&lt;/span&gt; bounce_bots &lt;span class="sy"&gt;:blog_url&lt;/span&gt;, &lt;span class="sy"&gt;:pages_path&lt;/span&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;/div&gt;
&lt;p&gt;You can also pass the standard controller filter options such as &lt;code&gt;:only&lt;/code&gt; or &lt;code&gt;:except&lt;/code&gt; :&lt;/p&gt;
&lt;div class="CodeRay"&gt;
  &lt;div class="code"&gt;&lt;pre&gt;&lt;span class="no"&gt;1&lt;/span&gt; bounce_bots &lt;span class="sy"&gt;:blog_url&lt;/span&gt;, &lt;span class="sy"&gt;:pages_path&lt;/span&gt;, &lt;span class="sy"&gt;:only&lt;/span&gt; =&amp;gt; [&lt;span class="sy"&gt;:create&lt;/span&gt;, &lt;span class="sy"&gt;:update&lt;/span&gt;]&lt;/pre&gt;&lt;/div&gt;
&lt;/div&gt;
&lt;h4&gt;Views&lt;/h4&gt;
&lt;p&gt;In your form views, add the honey pot field.  If you are using &lt;code&gt;form_tag&lt;/code&gt;, you can use any form element, for example (using &lt;a href="http://haml.hamptoncatlin.com/"&gt;haml&lt;/a&gt;):&lt;/p&gt;
&lt;div class="CodeRay"&gt;
  &lt;div class="code"&gt;&lt;pre&gt;&lt;span class="no"&gt;1&lt;/span&gt; = text_field_tag &lt;span class="sy"&gt;:blog_url&lt;/span&gt;, &lt;span class="pc"&gt;nil&lt;/span&gt;, &lt;span class="sy"&gt;:class&lt;/span&gt; =&amp;gt; &lt;span class="s"&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="k"&gt;long_required&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;/span&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;/div&gt;
&lt;p&gt;In your stylesheet, add a rule to &amp;#8216;hide&amp;#8217; the field:&lt;/p&gt;
&lt;div class="CodeRay"&gt;
  &lt;div class="code"&gt;&lt;pre&gt;&lt;span class="no"&gt;1&lt;/span&gt; &lt;span class="kw"&gt;input&lt;/span&gt;&lt;span class="cl"&gt;.long_required&lt;/span&gt; { &lt;span class="ke"&gt;display&lt;/span&gt;:&lt;span class="vl"&gt;none&lt;/span&gt;; }&lt;/pre&gt;&lt;/div&gt;
&lt;/div&gt;
&lt;p&gt;If you use &lt;code&gt;form_for&lt;/code&gt;, there are two helpers to simplify making the honey pot.&lt;/p&gt;
&lt;div class="CodeRay"&gt;
  &lt;div class="code"&gt;&lt;pre&gt;&lt;span class="no"&gt;1&lt;/span&gt; = f.bounce_label &lt;span class="s"&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="k"&gt;Blog Url&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;/span&gt;, &lt;span class="sy"&gt;:class&lt;/span&gt; =&amp;gt; &lt;span class="s"&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="k"&gt;long_required&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;/span&gt;
&lt;span class="no"&gt;2&lt;/span&gt; %br
&lt;span class="no"&gt;3&lt;/span&gt; = f.bounce_field &lt;span class="sy"&gt;:class&lt;/span&gt; =&amp;gt; &lt;span class="s"&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="k"&gt;long_required&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;/span&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;/div&gt;
&lt;p&gt;And that&amp;#8217;s it.&lt;/p&gt;&lt;img src="http://feeds.feedburner.com/~r/leshill/~4/aEMmYmQWYg8" height="1" width="1"/&gt;</content>
  <feedburner:origLink>http://blog.leshill.org/blog/2009/07/01/bouncy-bots.html</feedburner:origLink></entry>
  
  <entry>
    <title>Semantic List Markup with will_paginate</title>
    <link rel="alternate" type="text/html" href="http://feedproxy.google.com/~r/leshill/~3/qjB7wXYAFH4/semantic-list-markup-with-will-paginate.html" />
    
    <id>tag:blog.leshill.org,2009-06-27:1246106129</id>
    
    <published>2009-06-27T08:35:29-04:00</published>
    <updated>2009-06-27T08:35:29-04:00</updated>
    <content type="html">&lt;h3&gt;will_paginate using ul and li tags&lt;/h3&gt;
&lt;p&gt;&lt;a href="http://github.com/mislav/will_paginate/tree/master"&gt;will_paginate&lt;/a&gt; is the defacto standard plugin for the Rails community.  Almost every app will need pagination and &lt;strong&gt;will_paginate&lt;/strong&gt; does &lt;em&gt;almost&lt;/em&gt; everything you need.&lt;/p&gt;
&lt;p&gt;The one thing will_paginate does not do is generate &lt;em&gt;semantic&lt;/em&gt; pagination markup.  Semantic markup uses standard HTML tags to convey structure and meaning.  For example using an &lt;code&gt;h1&lt;/code&gt; tag for a heading rather than styling a &lt;code&gt;div&lt;/code&gt; tag to do the same.&lt;/p&gt;
&lt;p&gt;The default &lt;strong&gt;will_paginate&lt;/strong&gt; pagination is a series of anchors and spans strung together.  As many a designer has pointed out, that series is really a list of pagination controls.  If you are working with such a designer or you are writing semantic markup, try out my &lt;a href="http://github.com/leshill/will_paginate/tree/master"&gt;fork&lt;/a&gt; and enjoy &lt;strong&gt;will_paginate&lt;/strong&gt; with semantic pagination.&lt;/p&gt;
&lt;h3&gt;How to install&lt;/h3&gt;
&lt;p&gt;As of this moment, you can install it as a plugin.  I am considering releasing this as a gem; ideally the patch to &lt;strong&gt;will_paginate&lt;/strong&gt; would be accepted (see below).&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;%script/plugin install git://github.com/leshill/will_paginate.git&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;How to use&lt;/h3&gt;
&lt;p&gt;Use the &lt;code&gt;will_paginate&lt;/code&gt; view helper, passing the &lt;code&gt;:semantic =&amp;gt; true&lt;/code&gt; option.  If you do not want the enclosing &lt;code&gt;ul&lt;/code&gt; tag, pass &lt;code&gt;:container =&amp;gt; false&lt;/code&gt;.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;&amp;lt;%= will_paginate @pages, :semantic =&amp;gt; true %&amp;gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Which looks like:&lt;/p&gt;
&lt;div class="CodeRay"&gt;
  &lt;div class="code"&gt;&lt;pre&gt;&lt;span class="no"&gt; 1&lt;/span&gt; &lt;span class="ta"&gt;&amp;lt;ul&lt;/span&gt; &lt;span class="an"&gt;class&lt;/span&gt;=&lt;span class="s"&gt;&lt;span class="dl"&gt;&amp;quot;&lt;/span&gt;&lt;span class="k"&gt;pagination&lt;/span&gt;&lt;span class="dl"&gt;&amp;quot;&lt;/span&gt;&lt;/span&gt;&lt;span class="ta"&gt;&amp;gt;&lt;/span&gt;
&lt;span class="no"&gt; 2&lt;/span&gt;   &lt;span class="ta"&gt;&amp;lt;li&lt;/span&gt; &lt;span class="an"&gt;class&lt;/span&gt;=&lt;span class="s"&gt;&lt;span class="dl"&gt;&amp;quot;&lt;/span&gt;&lt;span class="k"&gt;disabled prev_page&lt;/span&gt;&lt;span class="dl"&gt;&amp;quot;&lt;/span&gt;&lt;/span&gt;&lt;span class="ta"&gt;&amp;gt;&lt;/span&gt;&lt;span class="en"&gt;&amp;amp;laquo;&lt;/span&gt; Previous&lt;span class="ta"&gt;&amp;lt;/li&amp;gt;&lt;/span&gt;
&lt;span class="no"&gt; 3&lt;/span&gt;   &lt;span class="ta"&gt;&amp;lt;li&lt;/span&gt; &lt;span class="an"&gt;class&lt;/span&gt;=&lt;span class="s"&gt;&lt;span class="dl"&gt;&amp;quot;&lt;/span&gt;&lt;span class="k"&gt;current&lt;/span&gt;&lt;span class="dl"&gt;&amp;quot;&lt;/span&gt;&lt;/span&gt;&lt;span class="ta"&gt;&amp;gt;&lt;/span&gt;1&lt;span class="ta"&gt;&amp;lt;/li&amp;gt;&lt;/span&gt;
&lt;span class="no"&gt; 4&lt;/span&gt;   &lt;span class="ta"&gt;&amp;lt;li&amp;gt;&lt;/span&gt;&lt;span class="ta"&gt;&amp;lt;a&lt;/span&gt; &lt;span class="an"&gt;href&lt;/span&gt;=&lt;span class="s"&gt;&lt;span class="dl"&gt;&amp;quot;&lt;/span&gt;&lt;span class="k"&gt;/pages?page=2&lt;/span&gt;&lt;span class="dl"&gt;&amp;quot;&lt;/span&gt;&lt;/span&gt; &lt;span class="an"&gt;rel&lt;/span&gt;=&lt;span class="s"&gt;&lt;span class="dl"&gt;&amp;quot;&lt;/span&gt;&lt;span class="k"&gt;next&lt;/span&gt;&lt;span class="dl"&gt;&amp;quot;&lt;/span&gt;&lt;/span&gt;&lt;span class="ta"&gt;&amp;gt;&lt;/span&gt;2&lt;span class="ta"&gt;&amp;lt;/a&amp;gt;&lt;/span&gt;&lt;span class="ta"&gt;&amp;lt;/li&amp;gt;&lt;/span&gt;
&lt;span class="no"&gt; 5&lt;/span&gt;   &lt;span class="ta"&gt;&amp;lt;li&amp;gt;&lt;/span&gt;&lt;span class="ta"&gt;&amp;lt;a&lt;/span&gt; &lt;span class="an"&gt;href&lt;/span&gt;=&lt;span class="s"&gt;&lt;span class="dl"&gt;&amp;quot;&lt;/span&gt;&lt;span class="k"&gt;/pages?page=3&lt;/span&gt;&lt;span class="dl"&gt;&amp;quot;&lt;/span&gt;&lt;/span&gt;&lt;span class="ta"&gt;&amp;gt;&lt;/span&gt;3&lt;span class="ta"&gt;&amp;lt;/a&amp;gt;&lt;/span&gt;&lt;span class="ta"&gt;&amp;lt;/li&amp;gt;&lt;/span&gt;
&lt;span class="no"&gt; 6&lt;/span&gt;   &lt;span class="ta"&gt;&amp;lt;li&amp;gt;&lt;/span&gt;&lt;span class="ta"&gt;&amp;lt;a&lt;/span&gt; &lt;span class="an"&gt;href&lt;/span&gt;=&lt;span class="s"&gt;&lt;span class="dl"&gt;&amp;quot;&lt;/span&gt;&lt;span class="k"&gt;/pages?page=4&lt;/span&gt;&lt;span class="dl"&gt;&amp;quot;&lt;/span&gt;&lt;/span&gt;&lt;span class="ta"&gt;&amp;gt;&lt;/span&gt;4&lt;span class="ta"&gt;&amp;lt;/a&amp;gt;&lt;/span&gt;&lt;span class="ta"&gt;&amp;lt;/li&amp;gt;&lt;/span&gt;
&lt;span class="no"&gt; 7&lt;/span&gt;   &lt;span class="ta"&gt;&amp;lt;li&amp;gt;&lt;/span&gt;&lt;span class="ta"&gt;&amp;lt;a&lt;/span&gt; &lt;span class="an"&gt;href&lt;/span&gt;=&lt;span class="s"&gt;&lt;span class="dl"&gt;&amp;quot;&lt;/span&gt;&lt;span class="k"&gt;/pages?page=5&lt;/span&gt;&lt;span class="dl"&gt;&amp;quot;&lt;/span&gt;&lt;/span&gt;&lt;span class="ta"&gt;&amp;gt;&lt;/span&gt;5&lt;span class="ta"&gt;&amp;lt;/a&amp;gt;&lt;/span&gt;&lt;span class="ta"&gt;&amp;lt;/li&amp;gt;&lt;/span&gt;
&lt;span class="no"&gt; 8&lt;/span&gt;   &lt;span class="ta"&gt;&amp;lt;li&amp;gt;&lt;/span&gt;&lt;span class="ta"&gt;&amp;lt;a&lt;/span&gt; &lt;span class="an"&gt;href&lt;/span&gt;=&lt;span class="s"&gt;&lt;span class="dl"&gt;&amp;quot;&lt;/span&gt;&lt;span class="k"&gt;/pages?page=6&lt;/span&gt;&lt;span class="dl"&gt;&amp;quot;&lt;/span&gt;&lt;/span&gt;&lt;span class="ta"&gt;&amp;gt;&lt;/span&gt;6&lt;span class="ta"&gt;&amp;lt;/a&amp;gt;&lt;/span&gt;&lt;span class="ta"&gt;&amp;lt;/li&amp;gt;&lt;/span&gt;
&lt;span class="no"&gt; 9&lt;/span&gt;   &lt;span class="ta"&gt;&amp;lt;li&amp;gt;&lt;/span&gt;&lt;span class="ta"&gt;&amp;lt;a&lt;/span&gt; &lt;span class="an"&gt;href&lt;/span&gt;=&lt;span class="s"&gt;&lt;span class="dl"&gt;&amp;quot;&lt;/span&gt;&lt;span class="k"&gt;/pages?page=7&lt;/span&gt;&lt;span class="dl"&gt;&amp;quot;&lt;/span&gt;&lt;/span&gt;&lt;span class="ta"&gt;&amp;gt;&lt;/span&gt;7&lt;span class="ta"&gt;&amp;lt;/a&amp;gt;&lt;/span&gt;&lt;span class="ta"&gt;&amp;lt;/li&amp;gt;&lt;/span&gt;
&lt;span class="no"&gt;&lt;strong&gt;10&lt;/strong&gt;&lt;/span&gt;   &lt;span class="ta"&gt;&amp;lt;li&amp;gt;&lt;/span&gt;&lt;span class="ta"&gt;&amp;lt;a&lt;/span&gt; &lt;span class="an"&gt;href&lt;/span&gt;=&lt;span class="s"&gt;&lt;span class="dl"&gt;&amp;quot;&lt;/span&gt;&lt;span class="k"&gt;/pages?page=8&lt;/span&gt;&lt;span class="dl"&gt;&amp;quot;&lt;/span&gt;&lt;/span&gt;&lt;span class="ta"&gt;&amp;gt;&lt;/span&gt;8&lt;span class="ta"&gt;&amp;lt;/a&amp;gt;&lt;/span&gt;&lt;span class="ta"&gt;&amp;lt;/li&amp;gt;&lt;/span&gt;
&lt;span class="no"&gt;11&lt;/span&gt;   &lt;span class="ta"&gt;&amp;lt;li&amp;gt;&lt;/span&gt;&lt;span class="ta"&gt;&amp;lt;a&lt;/span&gt; &lt;span class="an"&gt;href&lt;/span&gt;=&lt;span class="s"&gt;&lt;span class="dl"&gt;&amp;quot;&lt;/span&gt;&lt;span class="k"&gt;/pages?page=9&lt;/span&gt;&lt;span class="dl"&gt;&amp;quot;&lt;/span&gt;&lt;/span&gt;&lt;span class="ta"&gt;&amp;gt;&lt;/span&gt;9&lt;span class="ta"&gt;&amp;lt;/a&amp;gt;&lt;/span&gt;&lt;span class="ta"&gt;&amp;lt;/li&amp;gt;&lt;/span&gt;
&lt;span class="no"&gt;12&lt;/span&gt;   &lt;span class="ta"&gt;&amp;lt;li&lt;/span&gt; &lt;span class="an"&gt;class&lt;/span&gt;=&lt;span class="s"&gt;&lt;span class="dl"&gt;&amp;quot;&lt;/span&gt;&lt;span class="k"&gt;gap&lt;/span&gt;&lt;span class="dl"&gt;&amp;quot;&lt;/span&gt;&lt;/span&gt;&lt;span class="ta"&gt;&amp;gt;&lt;/span&gt;&lt;span class="en"&gt;&amp;amp;hellip;&lt;/span&gt;&lt;span class="ta"&gt;&amp;lt;/li&amp;gt;&lt;/span&gt;
&lt;span class="no"&gt;13&lt;/span&gt;   &lt;span class="ta"&gt;&amp;lt;li&amp;gt;&lt;/span&gt;&lt;span class="ta"&gt;&amp;lt;a&lt;/span&gt; &lt;span class="an"&gt;href&lt;/span&gt;=&lt;span class="s"&gt;&lt;span class="dl"&gt;&amp;quot;&lt;/span&gt;&lt;span class="k"&gt;/pages?page=33&lt;/span&gt;&lt;span class="dl"&gt;&amp;quot;&lt;/span&gt;&lt;/span&gt;&lt;span class="ta"&gt;&amp;gt;&lt;/span&gt;33&lt;span class="ta"&gt;&amp;lt;/a&amp;gt;&lt;/span&gt;&lt;span class="ta"&gt;&amp;lt;/li&amp;gt;&lt;/span&gt;
&lt;span class="no"&gt;14&lt;/span&gt;   &lt;span class="ta"&gt;&amp;lt;li&amp;gt;&lt;/span&gt;&lt;span class="ta"&gt;&amp;lt;a&lt;/span&gt; &lt;span class="an"&gt;href&lt;/span&gt;=&lt;span class="s"&gt;&lt;span class="dl"&gt;&amp;quot;&lt;/span&gt;&lt;span class="k"&gt;/pages?page=34&lt;/span&gt;&lt;span class="dl"&gt;&amp;quot;&lt;/span&gt;&lt;/span&gt;&lt;span class="ta"&gt;&amp;gt;&lt;/span&gt;34&lt;span class="ta"&gt;&amp;lt;/a&amp;gt;&lt;/span&gt;&lt;span class="ta"&gt;&amp;lt;/li&amp;gt;&lt;/span&gt;
&lt;span class="no"&gt;15&lt;/span&gt;   &lt;span class="ta"&gt;&amp;lt;li&lt;/span&gt; &lt;span class="an"&gt;class&lt;/span&gt;=&lt;span class="s"&gt;&lt;span class="dl"&gt;&amp;quot;&lt;/span&gt;&lt;span class="k"&gt;next_page&lt;/span&gt;&lt;span class="dl"&gt;&amp;quot;&lt;/span&gt;&lt;/span&gt;&lt;span class="ta"&gt;&amp;gt;&lt;/span&gt;&lt;span class="ta"&gt;&amp;lt;a&lt;/span&gt; &lt;span class="an"&gt;href&lt;/span&gt;=&lt;span class="s"&gt;&lt;span class="dl"&gt;&amp;quot;&lt;/span&gt;&lt;span class="k"&gt;/pages?page=2&lt;/span&gt;&lt;span class="dl"&gt;&amp;quot;&lt;/span&gt;&lt;/span&gt; &lt;span class="an"&gt;class&lt;/span&gt;=&lt;span class="s"&gt;&lt;span class="dl"&gt;&amp;quot;&lt;/span&gt;&lt;span class="k"&gt;next_page&lt;/span&gt;&lt;span class="dl"&gt;&amp;quot;&lt;/span&gt;&lt;/span&gt; &lt;span class="an"&gt;rel&lt;/span&gt;=&lt;span class="s"&gt;&lt;span class="dl"&gt;&amp;quot;&lt;/span&gt;&lt;span class="k"&gt;next&lt;/span&gt;&lt;span class="dl"&gt;&amp;quot;&lt;/span&gt;&lt;/span&gt;&lt;span class="ta"&gt;&amp;gt;&lt;/span&gt;Next &lt;span class="en"&gt;&amp;amp;raquo;&lt;/span&gt;&lt;span class="ta"&gt;&amp;lt;/a&amp;gt;&lt;/span&gt;&lt;span class="ta"&gt;&amp;lt;/li&amp;gt;&lt;/span&gt;
&lt;span class="no"&gt;16&lt;/span&gt; &lt;span class="ta"&gt;&amp;lt;/ul&amp;gt;&lt;/span&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;/div&gt;
&lt;h3&gt;Will this patch be accepted?&lt;/h3&gt;
&lt;p&gt;Signs point to &amp;#8216;uncertain&amp;#8217;.  Mislav has in the past indicated that he was not convinced that list support was necessary in the plugin since you can replace the renderer&lt;sup class="footnote"&gt;&lt;a href="#fn1"&gt;1&lt;/a&gt;&lt;/sup&gt;.  Since semantic markup is commonly used, I have sent a pull request.  In any event, the authors of &lt;strong&gt;will_paginate&lt;/strong&gt; have my thanks for creating a very useful plugin.&lt;/p&gt;
&lt;p class="footnote" id="fn1"&gt;&lt;sup&gt;1&lt;/sup&gt; So why the fork?  In order to support the containing &lt;code&gt;ul&lt;/code&gt; directly, the core of the helper had to be modified. See the change on &lt;a href="http://github.com/leshill/will_paginate/commit/6db9356185c9172d35d548000ab91d1bd8cbc7b2"&gt;github&lt;/a&gt; .&lt;/p&gt;&lt;img src="http://feeds.feedburner.com/~r/leshill/~4/qjB7wXYAFH4" height="1" width="1"/&gt;</content>
  <feedburner:origLink>http://blog.leshill.org/blog/2009/06/27/semantic-list-markup-with-will-paginate.html</feedburner:origLink></entry>
  
  <entry>
    <title>Default Options With Ruby</title>
    <link rel="alternate" type="text/html" href="http://feedproxy.google.com/~r/leshill/~3/_VeZietsKsc/default-options-with-ruby.html" />
    
    <id>tag:blog.leshill.org,2009-06-10:1244676887</id>
    
    <published>2009-06-10T19:34:47-04:00</published>
    <updated>2009-06-10T19:34:47-04:00</updated>
    <content type="html">&lt;h3&gt;Ruby and the argument collecting hash syntax&lt;/h3&gt;
&lt;p&gt;Unlike other languages, ruby does not support keyword parameters.  As an alternative, we use ruby&amp;#8217;s syntactic shorthand to pass a hash as the final argument to a method using the &lt;code&gt;key =&amp;gt; value&lt;/code&gt; syntax:&lt;/p&gt;
&lt;div class="CodeRay"&gt;
  &lt;div class="code"&gt;&lt;pre&gt;&lt;span class="no"&gt;1&lt;/span&gt; some_method(first_arg, &lt;span class="sy"&gt;:key1&lt;/span&gt; =&amp;gt; value1, &lt;span class="sy"&gt;:key2&lt;/span&gt; =&amp;gt; value2)&lt;/pre&gt;&lt;/div&gt;
&lt;/div&gt;
&lt;p&gt;&lt;code&gt;some_method&lt;/code&gt; will receive a hash as its second argument with the specified key/value pairs.  Unlike regular arguments, there is no way to provide default values for the individual key/value pairs (you can provide a default for the hash argument though):&lt;/p&gt;
&lt;div class="CodeRay"&gt;
  &lt;div class="code"&gt;&lt;pre&gt;&lt;span class="no"&gt;1&lt;/span&gt; &lt;span class="r"&gt;def&lt;/span&gt; &lt;span class="fu"&gt;some_method&lt;/span&gt;(thing, options = {})
&lt;span class="no"&gt;2&lt;/span&gt;   &lt;span class="c"&gt;#...&lt;/span&gt;
&lt;span class="no"&gt;3&lt;/span&gt; &lt;span class="r"&gt;end&lt;/span&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;/div&gt;
&lt;p&gt;There are a few ruby idioms for providing default options to an option hash, one of the most common is available in rails, using the core extension &lt;code&gt;Hash#reverse_merge&lt;/code&gt; from &lt;code&gt;ActiveSupport&lt;/code&gt;&lt;sup class="footnote"&gt;&lt;a href="#fn1"&gt;1&lt;/a&gt;&lt;/sup&gt;:&lt;/p&gt;
&lt;div class="CodeRay"&gt;
  &lt;div class="code"&gt;&lt;pre&gt;&lt;span class="no"&gt;1&lt;/span&gt; &lt;span class="r"&gt;def&lt;/span&gt; &lt;span class="fu"&gt;some_method&lt;/span&gt;(thing, options = {})
&lt;span class="no"&gt;2&lt;/span&gt;   options.reverse_merge!({&lt;span class="sy"&gt;:key1&lt;/span&gt; =&amp;gt; &lt;span class="s"&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="k"&gt;default&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;/span&gt;})
&lt;span class="no"&gt;3&lt;/span&gt;   &lt;span class="c"&gt;#...&lt;/span&gt;
&lt;span class="no"&gt;4&lt;/span&gt; &lt;span class="r"&gt;end&lt;/span&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;/div&gt;
&lt;p&gt;If you do not have access to &lt;code&gt;ActiveSupport&lt;/code&gt;, you can use ruby&amp;#8217;s &lt;code&gt;Hash#merge&lt;/code&gt;&lt;sup class="footnote"&gt;&lt;a href="#fn2"&gt;2&lt;/a&gt;&lt;/sup&gt; as an alternative:&lt;/p&gt;
&lt;div class="CodeRay"&gt;
  &lt;div class="code"&gt;&lt;pre&gt;&lt;span class="no"&gt;1&lt;/span&gt; &lt;span class="r"&gt;def&lt;/span&gt; &lt;span class="fu"&gt;some_method&lt;/span&gt;(thing, options = {})
&lt;span class="no"&gt;2&lt;/span&gt;   options = {&lt;span class="sy"&gt;:key1&lt;/span&gt; =&amp;gt; &lt;span class="s"&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="k"&gt;default&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;/span&gt;}.merge(options)
&lt;span class="no"&gt;3&lt;/span&gt;   &lt;span class="c"&gt;#...&lt;/span&gt;
&lt;span class="no"&gt;4&lt;/span&gt; &lt;span class="r"&gt;end&lt;/span&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;/div&gt;
&lt;p&gt;If that seems like too much code to be typing all the time, you can open up &lt;code&gt;Hash&lt;/code&gt; and add a method.  Careful!  This is simple enough to get wrong.  Here is some code&lt;sup class="footnote"&gt;&lt;a href="#fn3"&gt;3&lt;/a&gt;&lt;/sup&gt; from a very useful rails plugin:&lt;/p&gt;
&lt;div class="CodeRay"&gt;
  &lt;div class="code"&gt;&lt;pre&gt;&lt;span class="no"&gt;1&lt;/span&gt; &lt;span class="r"&gt;def&lt;/span&gt; &lt;span class="fu"&gt;default!&lt;/span&gt;(defaults = {})
&lt;span class="no"&gt;2&lt;/span&gt;   defaults.each &lt;span class="r"&gt;do&lt;/span&gt; |key, value|
&lt;span class="no"&gt;3&lt;/span&gt;     &lt;span class="pc"&gt;self&lt;/span&gt;[key] = value &lt;span class="r"&gt;if&lt;/span&gt; &lt;span class="pc"&gt;self&lt;/span&gt;[key].nil?
&lt;span class="no"&gt;4&lt;/span&gt;   &lt;span class="r"&gt;end&lt;/span&gt;
&lt;span class="no"&gt;5&lt;/span&gt;   &lt;span class="pc"&gt;self&lt;/span&gt;
&lt;span class="no"&gt;6&lt;/span&gt; &lt;span class="r"&gt;end&lt;/span&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;/div&gt;
&lt;p&gt;This code works until you want to set a default value to &lt;code&gt;nil&lt;/code&gt;.  This can be fixed by replacing the conditional on line three with &lt;code&gt;unless self.has_key?(key)&lt;/code&gt;.  Or you can simply rewrite this in terms of &lt;code&gt;Hash#merge&lt;/code&gt;&lt;sup class="footnote"&gt;&lt;a href="#fn4"&gt;4&lt;/a&gt;&lt;/sup&gt;:&lt;/p&gt;
&lt;div class="CodeRay"&gt;
  &lt;div class="code"&gt;&lt;pre&gt;&lt;span class="no"&gt;1&lt;/span&gt; &lt;span class="r"&gt;def&lt;/span&gt; &lt;span class="fu"&gt;default!&lt;/span&gt;(defaults = {})
&lt;span class="no"&gt;2&lt;/span&gt;   replace(defaults.merge(&lt;span class="pc"&gt;self&lt;/span&gt;))
&lt;span class="no"&gt;3&lt;/span&gt; &lt;span class="r"&gt;end&lt;/span&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;/div&gt;
&lt;p class="footnote" id="fn1"&gt;&lt;sup&gt;1&lt;/sup&gt; Which builds on ruby&amp;#8217;s &lt;code&gt;Hash#merge&lt;/code&gt;.&lt;/p&gt;
&lt;p class="footnote" id="fn2"&gt;&lt;sup&gt;2&lt;/sup&gt; Or merge-bang (&lt;code&gt;merge!&lt;/code&gt;).&lt;/p&gt;
&lt;p class="footnote" id="fn3"&gt;&lt;sup&gt;3&lt;/sup&gt; Slightly modified for readability.&lt;/p&gt;
&lt;p class="footnote" id="fn4"&gt;&lt;sup&gt;4&lt;/sup&gt; &lt;code&gt;ActiveSupport&lt;/code&gt; does something similar.&lt;/p&gt;&lt;img src="http://feeds.feedburner.com/~r/leshill/~4/_VeZietsKsc" height="1" width="1"/&gt;</content>
  <feedburner:origLink>http://blog.leshill.org/blog/2009/06/10/default-options-with-ruby.html</feedburner:origLink></entry>
  
  <entry>
    <title>Truncating HTML</title>
    <link rel="alternate" type="text/html" href="http://feedproxy.google.com/~r/leshill/~3/nZt48Haz_OM/truncating-html.html" />
    
    <id>tag:blog.leshill.org,2009-06-03:1244046220</id>
    
    <published>2009-06-03T12:23:40-04:00</published>
    <updated>2009-06-03T12:23:40-04:00</updated>
    <content type="html">&lt;p&gt;We recently needed to show a truncated version of existing HTML content.  Although there are several issues&lt;sup class="footnote"&gt;&lt;a href="#fn1"&gt;1&lt;/a&gt;&lt;/sup&gt; when dealing with HTML content, our specific concern was maintaining the integrity of the HTML.   Some quick googling led to a nice helper written by &lt;a href="http://henrik.nyh.se"&gt;Henrik Nyh&lt;/a&gt; last year.  We tweaked the original a bit to append the ellipsis within the tag at the truncation point and truncate at a word (or tag) boundary.  Here it is, enjoy.&lt;/p&gt;
&lt;div class="CodeRay"&gt;
  &lt;div class="code"&gt;&lt;pre&gt;&lt;span class="no"&gt; 1&lt;/span&gt; &lt;span class="c"&gt;# By Henrik Nyh &amp;lt;http://henrik.nyh.se&amp;gt; 2008-01-30.&lt;/span&gt;
&lt;span class="no"&gt; 2&lt;/span&gt; &lt;span class="c"&gt;# Free to modify and redistribute with credit.&lt;/span&gt;
&lt;span class="no"&gt; 3&lt;/span&gt; &lt;span class="c"&gt;# Word truncation and fixes by Les Hill &amp;lt;http://blog.leshill.org&amp;gt; 2009-06-02&lt;/span&gt;
&lt;span class="no"&gt; 4&lt;/span&gt; &lt;span class="c"&gt;#&lt;/span&gt;
&lt;span class="no"&gt; 5&lt;/span&gt; 
&lt;span class="no"&gt; 6&lt;/span&gt; require &lt;span class="s"&gt;&lt;span class="dl"&gt;&amp;quot;&lt;/span&gt;&lt;span class="k"&gt;rubygems&lt;/span&gt;&lt;span class="dl"&gt;&amp;quot;&lt;/span&gt;&lt;/span&gt;
&lt;span class="no"&gt; 7&lt;/span&gt; require &lt;span class="s"&gt;&lt;span class="dl"&gt;&amp;quot;&lt;/span&gt;&lt;span class="k"&gt;hpricot&lt;/span&gt;&lt;span class="dl"&gt;&amp;quot;&lt;/span&gt;&lt;/span&gt;
&lt;span class="no"&gt; 8&lt;/span&gt; 
&lt;span class="no"&gt; 9&lt;/span&gt; &lt;span class="r"&gt;module&lt;/span&gt; &lt;span class="cl"&gt;TextHelper&lt;/span&gt;
&lt;span class="no"&gt;&lt;strong&gt;10&lt;/strong&gt;&lt;/span&gt; 
&lt;span class="no"&gt;11&lt;/span&gt;   &lt;span class="c"&gt;# Like the Rails _truncate_ helper but doesn't break HTML tags or entities.&lt;/span&gt;
&lt;span class="no"&gt;12&lt;/span&gt;   &lt;span class="r"&gt;def&lt;/span&gt; &lt;span class="fu"&gt;truncate_html&lt;/span&gt;(text, max_length = &lt;span class="i"&gt;30&lt;/span&gt;, ellipsis = &lt;span class="s"&gt;&lt;span class="dl"&gt;&amp;quot;&lt;/span&gt;&lt;span class="k"&gt;...&lt;/span&gt;&lt;span class="dl"&gt;&amp;quot;&lt;/span&gt;&lt;/span&gt;)
&lt;span class="no"&gt;13&lt;/span&gt;     &lt;span class="r"&gt;return&lt;/span&gt; &lt;span class="r"&gt;if&lt;/span&gt; text.nil?
&lt;span class="no"&gt;14&lt;/span&gt;     doc = Hpricot(text.to_s)
&lt;span class="no"&gt;15&lt;/span&gt;     doc.inner_text.chars.length &amp;gt; max_length ? doc.truncate(max_length, ellipsis).inner_html : text.to_s
&lt;span class="no"&gt;16&lt;/span&gt;   &lt;span class="r"&gt;end&lt;/span&gt;
&lt;span class="no"&gt;17&lt;/span&gt; 
&lt;span class="no"&gt;18&lt;/span&gt;   &lt;span class="r"&gt;def&lt;/span&gt; &lt;span class="pc"&gt;self&lt;/span&gt;.truncate_at_space(text, max_length, ellipsis = &lt;span class="s"&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="k"&gt;...&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;/span&gt;)
&lt;span class="no"&gt;19&lt;/span&gt;     l = [max_length - ellipsis.length, &lt;span class="i"&gt;0&lt;/span&gt;].max
&lt;span class="no"&gt;&lt;strong&gt;20&lt;/strong&gt;&lt;/span&gt;     stop = text.rindex(&lt;span class="s"&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="k"&gt; &lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;/span&gt;, l) || &lt;span class="i"&gt;0&lt;/span&gt;
&lt;span class="no"&gt;21&lt;/span&gt;     (text.length &amp;gt; max_length ? text[&lt;span class="i"&gt;0&lt;/span&gt;...stop] + ellipsis : text).to_s
&lt;span class="no"&gt;22&lt;/span&gt;   &lt;span class="r"&gt;end&lt;/span&gt;
&lt;span class="no"&gt;23&lt;/span&gt; &lt;span class="r"&gt;end&lt;/span&gt;
&lt;span class="no"&gt;24&lt;/span&gt; 
&lt;span class="no"&gt;25&lt;/span&gt; &lt;span class="r"&gt;module&lt;/span&gt; &lt;span class="cl"&gt;HpricotTruncator&lt;/span&gt;
&lt;span class="no"&gt;26&lt;/span&gt;   &lt;span class="r"&gt;module&lt;/span&gt; &lt;span class="cl"&gt;NodeWithChildren&lt;/span&gt;
&lt;span class="no"&gt;27&lt;/span&gt;     &lt;span class="r"&gt;def&lt;/span&gt; &lt;span class="fu"&gt;truncate&lt;/span&gt;(max_length, ellipsis)
&lt;span class="no"&gt;28&lt;/span&gt;       &lt;span class="r"&gt;return&lt;/span&gt; &lt;span class="pc"&gt;self&lt;/span&gt; &lt;span class="r"&gt;if&lt;/span&gt; inner_text.chars.length &amp;lt;= max_length
&lt;span class="no"&gt;29&lt;/span&gt;       truncated_node = dup
&lt;span class="no"&gt;&lt;strong&gt;30&lt;/strong&gt;&lt;/span&gt;       truncated_node.name = name
&lt;span class="no"&gt;31&lt;/span&gt;       truncated_node.raw_attributes = raw_attributes
&lt;span class="no"&gt;32&lt;/span&gt;       truncated_node.children = []
&lt;span class="no"&gt;33&lt;/span&gt;       each_child &lt;span class="r"&gt;do&lt;/span&gt; |node|
&lt;span class="no"&gt;34&lt;/span&gt;         &lt;span class="r"&gt;break&lt;/span&gt; &lt;span class="r"&gt;if&lt;/span&gt; max_length &amp;lt;= &lt;span class="i"&gt;0&lt;/span&gt;
&lt;span class="no"&gt;35&lt;/span&gt;         node_length = node.inner_text.chars.length
&lt;span class="no"&gt;36&lt;/span&gt;         truncated_node.children &amp;lt;&amp;lt; node.truncate(max_length, ellipsis)
&lt;span class="no"&gt;37&lt;/span&gt;         max_length = max_length - node_length
&lt;span class="no"&gt;38&lt;/span&gt;       &lt;span class="r"&gt;end&lt;/span&gt;
&lt;span class="no"&gt;39&lt;/span&gt;       truncated_node
&lt;span class="no"&gt;&lt;strong&gt;40&lt;/strong&gt;&lt;/span&gt;     &lt;span class="r"&gt;end&lt;/span&gt;
&lt;span class="no"&gt;41&lt;/span&gt;   &lt;span class="r"&gt;end&lt;/span&gt;
&lt;span class="no"&gt;42&lt;/span&gt; 
&lt;span class="no"&gt;43&lt;/span&gt;   &lt;span class="r"&gt;module&lt;/span&gt; &lt;span class="cl"&gt;TextNode&lt;/span&gt;
&lt;span class="no"&gt;44&lt;/span&gt;     &lt;span class="r"&gt;def&lt;/span&gt; &lt;span class="fu"&gt;truncate&lt;/span&gt;(max_length, ellipsis)
&lt;span class="no"&gt;45&lt;/span&gt;       &lt;span class="pc"&gt;self&lt;/span&gt;.content = &lt;span class="co"&gt;TextHelper&lt;/span&gt;.truncate_at_space(content, max_length, ellipsis)
&lt;span class="no"&gt;46&lt;/span&gt;       &lt;span class="pc"&gt;self&lt;/span&gt;
&lt;span class="no"&gt;47&lt;/span&gt;     &lt;span class="r"&gt;end&lt;/span&gt;
&lt;span class="no"&gt;48&lt;/span&gt;   &lt;span class="r"&gt;end&lt;/span&gt;
&lt;span class="no"&gt;49&lt;/span&gt; 
&lt;span class="no"&gt;&lt;strong&gt;50&lt;/strong&gt;&lt;/span&gt;   &lt;span class="r"&gt;module&lt;/span&gt; &lt;span class="cl"&gt;IgnoredTag&lt;/span&gt;
&lt;span class="no"&gt;51&lt;/span&gt;     &lt;span class="r"&gt;def&lt;/span&gt; &lt;span class="fu"&gt;truncate&lt;/span&gt;(max_length, ellipsis)
&lt;span class="no"&gt;52&lt;/span&gt;       &lt;span class="pc"&gt;self&lt;/span&gt;
&lt;span class="no"&gt;53&lt;/span&gt;     &lt;span class="r"&gt;end&lt;/span&gt;
&lt;span class="no"&gt;54&lt;/span&gt;   &lt;span class="r"&gt;end&lt;/span&gt;
&lt;span class="no"&gt;55&lt;/span&gt; &lt;span class="r"&gt;end&lt;/span&gt;
&lt;span class="no"&gt;56&lt;/span&gt; 
&lt;span class="no"&gt;57&lt;/span&gt; &lt;span class="co"&gt;Hpricot&lt;/span&gt;::&lt;span class="co"&gt;Doc&lt;/span&gt;.send(&lt;span class="sy"&gt;:include&lt;/span&gt;,       &lt;span class="co"&gt;HpricotTruncator&lt;/span&gt;::&lt;span class="co"&gt;NodeWithChildren&lt;/span&gt;)
&lt;span class="no"&gt;58&lt;/span&gt; &lt;span class="co"&gt;Hpricot&lt;/span&gt;::&lt;span class="co"&gt;Elem&lt;/span&gt;.send(&lt;span class="sy"&gt;:include&lt;/span&gt;,      &lt;span class="co"&gt;HpricotTruncator&lt;/span&gt;::&lt;span class="co"&gt;NodeWithChildren&lt;/span&gt;)
&lt;span class="no"&gt;59&lt;/span&gt; &lt;span class="co"&gt;Hpricot&lt;/span&gt;::&lt;span class="co"&gt;Text&lt;/span&gt;.send(&lt;span class="sy"&gt;:include&lt;/span&gt;,      &lt;span class="co"&gt;HpricotTruncator&lt;/span&gt;::&lt;span class="co"&gt;TextNode&lt;/span&gt;)
&lt;span class="no"&gt;&lt;strong&gt;60&lt;/strong&gt;&lt;/span&gt; &lt;span class="co"&gt;Hpricot&lt;/span&gt;::&lt;span class="co"&gt;BogusETag&lt;/span&gt;.send(&lt;span class="sy"&gt;:include&lt;/span&gt;, &lt;span class="co"&gt;HpricotTruncator&lt;/span&gt;::&lt;span class="co"&gt;IgnoredTag&lt;/span&gt;)
&lt;span class="no"&gt;61&lt;/span&gt; &lt;span class="co"&gt;Hpricot&lt;/span&gt;::&lt;span class="co"&gt;Comment&lt;/span&gt;.send(&lt;span class="sy"&gt;:include&lt;/span&gt;,   &lt;span class="co"&gt;HpricotTruncator&lt;/span&gt;::&lt;span class="co"&gt;IgnoredTag&lt;/span&gt;)&lt;/pre&gt;&lt;/div&gt;
&lt;/div&gt;
&lt;p class="footnote" id="fn1"&gt;&lt;sup&gt;1&lt;/sup&gt; For example: preventing XSS attacks, maintaining coherent styling.&lt;/p&gt;&lt;img src="http://feeds.feedburner.com/~r/leshill/~4/nZt48Haz_OM" height="1" width="1"/&gt;</content>
  <feedburner:origLink>http://blog.leshill.org/blog/2009/06/03/truncating-html.html</feedburner:origLink></entry>
  
  <entry>
    <title>AWS S3 Bucket Copy</title>
    <link rel="alternate" type="text/html" href="http://feedproxy.google.com/~r/leshill/~3/s7b61VI_T1Y/aws-s3-bucket-copy.html" />
    
    <id>tag:blog.leshill.org,2009-05-28:1243539824</id>
    
    <published>2009-05-28T15:43:44-04:00</published>
    <updated>2009-05-28T15:43:44-04:00</updated>
    <content type="html">&lt;p&gt;We use &lt;a href="http://aws.amazon.com/s3/"&gt;S3&lt;/a&gt; for a lot of our projects using the &lt;a href="http://amazon.rubyforge.org/"&gt;aws-s3&lt;/a&gt; gem.  One thing that has continually plagued me is copying objects from one bucket to another.  Having rewritten this snippet of code twice already, I am placing it here for future reference.  Enjoy.&lt;/p&gt;
&lt;div class="CodeRay"&gt;
  &lt;div class="code"&gt;&lt;pre&gt;&lt;span class="no"&gt; 1&lt;/span&gt; &lt;span class="r"&gt;class&lt;/span&gt; &lt;span class="cl"&gt;S3Object&lt;/span&gt;
&lt;span class="no"&gt; 2&lt;/span&gt;   &lt;span class="r"&gt;def&lt;/span&gt; &lt;span class="pc"&gt;self&lt;/span&gt;.copy_across_buckets(src_bucket, src_key, dest_bucket, dest_key, options = {})
&lt;span class="no"&gt; 3&lt;/span&gt;     original = open(url_for(src_key, src_bucket))
&lt;span class="no"&gt; 4&lt;/span&gt;     default_options = {&lt;span class="sy"&gt;:content_type&lt;/span&gt; =&amp;gt; original.content_type}
&lt;span class="no"&gt; 5&lt;/span&gt;     store(dest_key, original, dest_bucket, default_options.merge(options))
&lt;span class="no"&gt; 6&lt;/span&gt;     acl(dest_key, dest_bucket, acl(src_key, src_bucket))
&lt;span class="no"&gt; 7&lt;/span&gt;   &lt;span class="r"&gt;end&lt;/span&gt;
&lt;span class="no"&gt; 8&lt;/span&gt; 
&lt;span class="no"&gt; 9&lt;/span&gt;   &lt;span class="r"&gt;def&lt;/span&gt; &lt;span class="fu"&gt;copy_to_bucket&lt;/span&gt;(dest_bucket, dest_key = &lt;span class="pc"&gt;nil&lt;/span&gt;, options = {})
&lt;span class="no"&gt;&lt;strong&gt;10&lt;/strong&gt;&lt;/span&gt;     &lt;span class="pc"&gt;self&lt;/span&gt;.class.copy_across_buckets(bucket.name, key, dest_bucket, dest_key ? dest_key : key, options)
&lt;span class="no"&gt;11&lt;/span&gt;   &lt;span class="r"&gt;end&lt;/span&gt;
&lt;span class="no"&gt;12&lt;/span&gt; &lt;span class="r"&gt;end&lt;/span&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;/div&gt;
&lt;img src="http://feeds.feedburner.com/~r/leshill/~4/s7b61VI_T1Y" height="1" width="1"/&gt;</content>
  <feedburner:origLink>http://blog.leshill.org/blog/2009/05/28/aws-s3-bucket-copy.html</feedburner:origLink></entry>
  
  <entry>
    <title>RSpec stub_chain for Mocha</title>
    <link rel="alternate" type="text/html" href="http://feedproxy.google.com/~r/leshill/~3/ueEHChZh3_M/rspec-stub-chain-for-mocha.html" />
    
    <id>tag:blog.leshill.org,2009-05-05:1241576025</id>
    
    <published>2009-05-05T22:13:45-04:00</published>
    <updated>2009-05-05T22:13:45-04:00</updated>
    <content type="html">&lt;p&gt;I am not at &lt;a href="http://en.oreilly.com/rails2009"&gt;RailsConf&lt;/a&gt; but I have been following the tweets of my coworkers, so I heard about &lt;a href="http://rspec.info"&gt;RSpec&lt;/a&gt;&amp;#8216;s new &lt;code&gt;stub_chain&lt;/code&gt; method.  Since I prefer &lt;a href="http://mocha.rubyforge.org"&gt;Mocha&lt;/a&gt;&amp;#8217;s syntax to that of the built-in RSpec stubbing framework, I created a monkey patch to add &lt;code&gt;stub_chain&lt;/code&gt; to Mocha:&lt;/p&gt;
&lt;div class="CodeRay"&gt;
  &lt;div class="code"&gt;&lt;pre&gt;&lt;span class="no"&gt; 1&lt;/span&gt; &lt;span class="r"&gt;module&lt;/span&gt; &lt;span class="cl"&gt;StubChainMocha&lt;/span&gt;
&lt;span class="no"&gt; 2&lt;/span&gt;   &lt;span class="r"&gt;module&lt;/span&gt; &lt;span class="cl"&gt;Object&lt;/span&gt;
&lt;span class="no"&gt; 3&lt;/span&gt;     &lt;span class="r"&gt;def&lt;/span&gt; &lt;span class="fu"&gt;stub_chain&lt;/span&gt;(*methods)
&lt;span class="no"&gt; 4&lt;/span&gt;       &lt;span class="r"&gt;while&lt;/span&gt; methods.length &amp;gt; &lt;span class="i"&gt;1&lt;/span&gt; &lt;span class="r"&gt;do&lt;/span&gt;
&lt;span class="no"&gt; 5&lt;/span&gt;         stubs(methods.shift).returns(&lt;span class="pc"&gt;self&lt;/span&gt;)
&lt;span class="no"&gt; 6&lt;/span&gt;       &lt;span class="r"&gt;end&lt;/span&gt;
&lt;span class="no"&gt; 7&lt;/span&gt;       stubs(methods.shift)
&lt;span class="no"&gt; 8&lt;/span&gt;     &lt;span class="r"&gt;end&lt;/span&gt;
&lt;span class="no"&gt; 9&lt;/span&gt;   &lt;span class="r"&gt;end&lt;/span&gt;
&lt;span class="no"&gt;&lt;strong&gt;10&lt;/strong&gt;&lt;/span&gt; &lt;span class="r"&gt;end&lt;/span&gt;
&lt;span class="no"&gt;11&lt;/span&gt; 
&lt;span class="no"&gt;12&lt;/span&gt; &lt;span class="co"&gt;Object&lt;/span&gt;.send(&lt;span class="sy"&gt;:include&lt;/span&gt;, &lt;span class="co"&gt;StubChainMocha&lt;/span&gt;::&lt;span class="co"&gt;Object&lt;/span&gt;)&lt;/pre&gt;&lt;/div&gt;
&lt;/div&gt;
&lt;p&gt;Copy that into &lt;code&gt;spec/stub_chain_mocha.rb&lt;/code&gt; and then require it from &lt;code&gt;spec_helper.rb&lt;/code&gt;.&lt;/p&gt;&lt;img src="http://feeds.feedburner.com/~r/leshill/~4/ueEHChZh3_M" height="1" width="1"/&gt;</content>
  <feedburner:origLink>http://blog.leshill.org/blog/2009/05/05/rspec-stub-chain-for-mocha.html</feedburner:origLink></entry>
  
</feed>
