<?xml version="1.0" encoding="utf-8"?>
<feed xml:lang="en-US" xmlns="http://www.w3.org/2005/Atom">
  <id>http://www.railsillustrated.com</id>
  <link type="text/html" href="http://www.railsillustrated.com" rel="alternate"/>
  <link type="application/atom+xml" href="http://www.railsillustrated.com/feed" rel="self"/>
  <title>Rails Illustrated</title>
  <subtitle>www.railsillustrated.com</subtitle>
  <updated>2012-09-02T19:47:49Z</updated>
  <entry>
    <id>http://www.railsillustrated.com/how-to-be-lazy-in-ruby-and-why-you-should.html</id>
    <link type="text/html" href="http://www.railsillustrated.com/how-to-be-lazy-in-ruby-and-why-you-should.html" rel="alternate"/>
    <updated>2012-09-02T19:47:49Z</updated>
    <title>How to be Lazy in Ruby and Why You Should Be</title>
    <summary type="html">&lt;p&gt;Ruby borrows many ideas from functional languages, allowing for readable, terse methods without side affects.  There are sometimes significant drawbacks to writing functional Ruby code, including inadvertent memory consumption.  Fortunately these drawbacks can be avoided by making use of Ruby's &lt;code&gt;Enumerator&lt;/code&gt; objects to implement lazy sequences.&lt;/p&gt;

&lt;p&gt;When writing ruby scripts for data munging, a common problem is to read a character separated data file into a Ruby hash.  If we have a data file with two columns, say &lt;code&gt;user_id&lt;/code&gt; and &lt;code&gt;group_id&lt;/code&gt; and we wish to read that into memory as hash with &lt;code&gt;user_id&lt;/code&gt; as the key and &lt;code&gt;group_id&lt;/code&gt; as the value we could use something like the following:&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;result = File.open("data") do |f|
  f.readlines.map{|l| l.strip.split(/,/)}.inject({}){|h,l| h.merge(l.first.to_i =&amp;gt; l.last.to_i)}
end
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;This implementation is functional but has one major drawback: &lt;code&gt;readlines&lt;/code&gt; will return an instance of &lt;code&gt;Array&lt;/code&gt; and so the entire file will be read into memory before constructing the hash.  If the file is of appreciable size this will quickly become a serious performance bottleneck.  One way to rectify this problem is to change the implementation to the following:&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;result = File.open("data") do |f|
  h = {}
  f.each do |l|
    l = l.strip.split(/,/)
    h[l.first.to_i] = l.last.to_i
  end
  h
end
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;While this no longer reads the entire data file in memory, addressing the memory concerns of the original implementation, it is certainly more verbose than the original implementation.  In particular, many might argue that the first implementation is the idiomatic Ruby implementation, and that the second implementation should be avoided unless there are known to be memory related performance problems.&lt;/p&gt;

&lt;h2&gt;The Lazy Way&lt;/h2&gt;

&lt;p&gt;There is a way to have the best of both worlds: a function style implementation that avoids loading the entire input file into memory at once by making use of Ruby's &lt;code&gt;Enumerator&lt;/code&gt; class.  An &lt;code&gt;Enumerator&lt;/code&gt; objects encapsulates the traversal of an enumerable, &lt;em&gt;without performing the complete traversal&lt;/em&gt;.  Thus, by using an &lt;code&gt;Enumerator&lt;/code&gt; we can express the loading of the file in a functional way, without loading the file all at once into an &lt;code&gt;Array&lt;/code&gt; at the very first step.&lt;/p&gt;

&lt;p&gt;It is easy to construct an &lt;code&gt;Enumerator&lt;/code&gt; for many of the methods defined in the &lt;code&gt;Enumerable&lt;/code&gt; module using the &lt;code&gt;enum_for&lt;/code&gt; method:&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;[:a, :b, :c].enum_for(:reject) # =&amp;gt; #&amp;lt;Enumerable::Enumerator:0x1011f5d40&amp;gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;In fact, the &lt;code&gt;File.each&lt;/code&gt; method will return an instance of &lt;code&gt;Enumerator&lt;/code&gt; if no block is provided:&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;File.open("data") {|f| f.each} # =&amp;gt; #&amp;lt;Enumerable::Enumerator:0x1011ca640&amp;gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;Knowing this we can re-write our original implementation:&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;result = File.open("data") do |f|
  f.each.map{|l| l.strip.split(/,/)}.inject({}){|h,l| h.merge(l.first.to_i =&amp;gt; l.last.to_i)}
end
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;Now &lt;code&gt;f.each&lt;/code&gt; will return an &lt;code&gt;Enumerator&lt;/code&gt;.  Assuming that &lt;code&gt;map&lt;/code&gt; will also return an &lt;code&gt;Enumerator&lt;/code&gt; we will have succeeded in constructing a lazy sequence from the input file.&lt;/p&gt;

&lt;p&gt;Unfortunately &lt;code&gt;map&lt;/code&gt; does not return an instance of &lt;code&gt;Enumerator&lt;/code&gt;, instead returning an instance of &lt;code&gt;Array&lt;/code&gt;.  This is considered a &lt;a href="http://redmine.ruby-lang.org/issues/show/708"&gt;bug&lt;/a&gt; in Ruby 1.9 (and in Ruby 1.8.7) and should hopefully fixed sometime soon.  As a work around we can monkey patch &lt;code&gt;Enumerable&lt;/code&gt; to fix the &lt;code&gt;map&lt;/code&gt; method:&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;module Enumerable
  class Enumerator

    def map(&amp;amp;block)
      self.class.new do |yielder|
        each do |e|
          yielder &amp;lt;&amp;lt; block.call(e)
        end
      end
    end

 end
end
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;Now &lt;code&gt;map&lt;/code&gt; will return a new &lt;code&gt;Enumerator&lt;/code&gt;, which will yield an enumeration of the result of calling the block provided to &lt;code&gt;map&lt;/code&gt; on the original sequence provided by the original &lt;code&gt;Enumerator&lt;/code&gt; instance.  Many other methods in the &lt;code&gt;Enumerable&lt;/code&gt; module can also be modified to return an instance of &lt;code&gt;Enumerator&lt;/code&gt;.  For implementation details see &lt;a href="http://redmine.ruby-lang.org/attachments/download/121"&gt;lazy_enum.rb&lt;/a&gt; as attached to the original &lt;a href="http://redmine.ruby-lang.org/issues/show/708"&gt;bug&lt;/a&gt; report.&lt;/p&gt;

&lt;p&gt;This code is Ruby 1.9 specific as &lt;code&gt;Enumerator.new&lt;/code&gt; doesn't take a block in Ruby 1.8.7.  We can use the &lt;a href="http://github.com/rubyworks/facets"&gt;Ruby facets gem&lt;/a&gt; to make &lt;code&gt;Enumerator&lt;/code&gt; in Ruby 1.8.7 behave like &lt;code&gt;Enumerator&lt;/code&gt; in Ruby 1.9.&lt;/p&gt;

&lt;h2&gt;Putting it all together&lt;/h2&gt;

&lt;p&gt;We can now write a completely lazy functional implementation of our original problem which works in Ruby 1.8.7:&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;require 'facets/enumerator'

module Enumerable
  class Enumerator

    def map(&amp;amp;block)
      self.class.new do |yielder|
        each do |e|
          yielder &amp;lt;&amp;lt; block.call(e)
        end
      end
    end

 end
end

result = File.open("data") do |f|
  f.each.map{|l| l.strip.split(/,/)}.inject({}){|h,l| h.merge(l.first.to_i =&amp;gt; l.last.to_i)}
end
&lt;/code&gt;&lt;/pre&gt;

&lt;h2&gt;Further reading&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="http://github.com/rdp/ruby_tutorials_core/wiki/enumerator"&gt;http://github.com/rdp/ruby_tutorials_core/wiki/enumerator&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="http://blog.grayproductions.net/articles/infinite_streams"&gt;http://blog.grayproductions.net/articles/infinite_streams&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="http://moonbase.rydia.net/software/lazy.rb/"&gt;http://moonbase.rydia.net/software/lazy.rb/&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="http://ruby-doc.org/stdlib/libdoc/enumerator/rdoc/index.html"&gt;http://ruby-doc.org/stdlib/libdoc/enumerator/rdoc/index.html&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="http://www.michaelharrison.ws/weblog/?p=163"&gt;http://www.michaelharrison.ws/weblog/?p=163&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="http://flori.github.com/lazylist/"&gt;http://flori.github.com/lazylist/&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

</summary>
    <content type="html">&lt;p&gt;Ruby borrows many ideas from functional languages, allowing for readable, terse methods without side affects.  There are sometimes significant drawbacks to writing functional Ruby code, including inadvertent memory consumption.  Fortunately these drawbacks can be avoided by making use of Ruby's &lt;code&gt;Enumerator&lt;/code&gt; objects to implement lazy sequences.&lt;/p&gt;

&lt;p&gt;When writing ruby scripts for data munging, a common problem is to read a character separated data file into a Ruby hash.  If we have a data file with two columns, say &lt;code&gt;user_id&lt;/code&gt; and &lt;code&gt;group_id&lt;/code&gt; and we wish to read that into memory as hash with &lt;code&gt;user_id&lt;/code&gt; as the key and &lt;code&gt;group_id&lt;/code&gt; as the value we could use something like the following:&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;result = File.open("data") do |f|
  f.readlines.map{|l| l.strip.split(/,/)}.inject({}){|h,l| h.merge(l.first.to_i =&amp;gt; l.last.to_i)}
end
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;This implementation is functional but has one major drawback: &lt;code&gt;readlines&lt;/code&gt; will return an instance of &lt;code&gt;Array&lt;/code&gt; and so the entire file will be read into memory before constructing the hash.  If the file is of appreciable size this will quickly become a serious performance bottleneck.  One way to rectify this problem is to change the implementation to the following:&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;result = File.open("data") do |f|
  h = {}
  f.each do |l|
    l = l.strip.split(/,/)
    h[l.first.to_i] = l.last.to_i
  end
  h
end
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;While this no longer reads the entire data file in memory, addressing the memory concerns of the original implementation, it is certainly more verbose than the original implementation.  In particular, many might argue that the first implementation is the idiomatic Ruby implementation, and that the second implementation should be avoided unless there are known to be memory related performance problems.&lt;/p&gt;

&lt;h2&gt;The Lazy Way&lt;/h2&gt;

&lt;p&gt;There is a way to have the best of both worlds: a function style implementation that avoids loading the entire input file into memory at once by making use of Ruby's &lt;code&gt;Enumerator&lt;/code&gt; class.  An &lt;code&gt;Enumerator&lt;/code&gt; objects encapsulates the traversal of an enumerable, &lt;em&gt;without performing the complete traversal&lt;/em&gt;.  Thus, by using an &lt;code&gt;Enumerator&lt;/code&gt; we can express the loading of the file in a functional way, without loading the file all at once into an &lt;code&gt;Array&lt;/code&gt; at the very first step.&lt;/p&gt;

&lt;p&gt;It is easy to construct an &lt;code&gt;Enumerator&lt;/code&gt; for many of the methods defined in the &lt;code&gt;Enumerable&lt;/code&gt; module using the &lt;code&gt;enum_for&lt;/code&gt; method:&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;[:a, :b, :c].enum_for(:reject) # =&amp;gt; #&amp;lt;Enumerable::Enumerator:0x1011f5d40&amp;gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;In fact, the &lt;code&gt;File.each&lt;/code&gt; method will return an instance of &lt;code&gt;Enumerator&lt;/code&gt; if no block is provided:&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;File.open("data") {|f| f.each} # =&amp;gt; #&amp;lt;Enumerable::Enumerator:0x1011ca640&amp;gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;Knowing this we can re-write our original implementation:&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;result = File.open("data") do |f|
  f.each.map{|l| l.strip.split(/,/)}.inject({}){|h,l| h.merge(l.first.to_i =&amp;gt; l.last.to_i)}
end
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;Now &lt;code&gt;f.each&lt;/code&gt; will return an &lt;code&gt;Enumerator&lt;/code&gt;.  Assuming that &lt;code&gt;map&lt;/code&gt; will also return an &lt;code&gt;Enumerator&lt;/code&gt; we will have succeeded in constructing a lazy sequence from the input file.&lt;/p&gt;

&lt;p&gt;Unfortunately &lt;code&gt;map&lt;/code&gt; does not return an instance of &lt;code&gt;Enumerator&lt;/code&gt;, instead returning an instance of &lt;code&gt;Array&lt;/code&gt;.  This is considered a &lt;a href="http://redmine.ruby-lang.org/issues/show/708"&gt;bug&lt;/a&gt; in Ruby 1.9 (and in Ruby 1.8.7) and should hopefully fixed sometime soon.  As a work around we can monkey patch &lt;code&gt;Enumerable&lt;/code&gt; to fix the &lt;code&gt;map&lt;/code&gt; method:&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;module Enumerable
  class Enumerator

    def map(&amp;amp;block)
      self.class.new do |yielder|
        each do |e|
          yielder &amp;lt;&amp;lt; block.call(e)
        end
      end
    end

 end
end
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;Now &lt;code&gt;map&lt;/code&gt; will return a new &lt;code&gt;Enumerator&lt;/code&gt;, which will yield an enumeration of the result of calling the block provided to &lt;code&gt;map&lt;/code&gt; on the original sequence provided by the original &lt;code&gt;Enumerator&lt;/code&gt; instance.  Many other methods in the &lt;code&gt;Enumerable&lt;/code&gt; module can also be modified to return an instance of &lt;code&gt;Enumerator&lt;/code&gt;.  For implementation details see &lt;a href="http://redmine.ruby-lang.org/attachments/download/121"&gt;lazy_enum.rb&lt;/a&gt; as attached to the original &lt;a href="http://redmine.ruby-lang.org/issues/show/708"&gt;bug&lt;/a&gt; report.&lt;/p&gt;

&lt;p&gt;This code is Ruby 1.9 specific as &lt;code&gt;Enumerator.new&lt;/code&gt; doesn't take a block in Ruby 1.8.7.  We can use the &lt;a href="http://github.com/rubyworks/facets"&gt;Ruby facets gem&lt;/a&gt; to make &lt;code&gt;Enumerator&lt;/code&gt; in Ruby 1.8.7 behave like &lt;code&gt;Enumerator&lt;/code&gt; in Ruby 1.9.&lt;/p&gt;

&lt;h2&gt;Putting it all together&lt;/h2&gt;

&lt;p&gt;We can now write a completely lazy functional implementation of our original problem which works in Ruby 1.8.7:&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;require 'facets/enumerator'

module Enumerable
  class Enumerator

    def map(&amp;amp;block)
      self.class.new do |yielder|
        each do |e|
          yielder &amp;lt;&amp;lt; block.call(e)
        end
      end
    end

 end
end

result = File.open("data") do |f|
  f.each.map{|l| l.strip.split(/,/)}.inject({}){|h,l| h.merge(l.first.to_i =&amp;gt; l.last.to_i)}
end
&lt;/code&gt;&lt;/pre&gt;

&lt;h2&gt;Further reading&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="http://github.com/rdp/ruby_tutorials_core/wiki/enumerator"&gt;http://github.com/rdp/ruby_tutorials_core/wiki/enumerator&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="http://blog.grayproductions.net/articles/infinite_streams"&gt;http://blog.grayproductions.net/articles/infinite_streams&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="http://moonbase.rydia.net/software/lazy.rb/"&gt;http://moonbase.rydia.net/software/lazy.rb/&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="http://ruby-doc.org/stdlib/libdoc/enumerator/rdoc/index.html"&gt;http://ruby-doc.org/stdlib/libdoc/enumerator/rdoc/index.html&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="http://www.michaelharrison.ws/weblog/?p=163"&gt;http://www.michaelharrison.ws/weblog/?p=163&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="http://flori.github.com/lazylist/"&gt;http://flori.github.com/lazylist/&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

</content>
    <author>
      <name>Erik Andrejko</name>
      <email>erik@railsillustrated.com</email>
    </author>
  </entry>
  <entry>
    <id>http://www.railsillustrated.com/ruby-deprecation-warnings-missing.html</id>
    <link type="text/html" href="http://www.railsillustrated.com/ruby-deprecation-warnings-missing.html" rel="alternate"/>
    <updated>2012-09-02T19:47:49Z</updated>
    <title>These Aren't the Deprecation Warnings You Are Looking For</title>
    <summary type="html">&lt;p&gt;Ruby prints some helpful deprecation warnings when using some deprecated features of the Ruby language.  However, you won't find these errors when they occur within a rake task run inside Rails.&lt;/p&gt;

&lt;h2&gt;Warnings Often Indicate Bugs&lt;/h2&gt;

&lt;p&gt;I prefer to &lt;a href="http://media.pragprog.com/titles/pad/CodeAndDebug.pdf"&gt;treat warnings as errors&lt;/a&gt;, as warnings often indicate the presence of a bug.  For example, in Ruby 1.8.7, the &lt;code&gt;id&lt;/code&gt; method is deprecated and calling &lt;code&gt;id&lt;/code&gt; on an &lt;code&gt;Object&lt;/code&gt; that doesn't define an &lt;code&gt;id&lt;/code&gt; method prints an error:&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;warning: Object#id will be deprecated; use Object#object_id
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;A warning like that is probably a good sign I am calling &lt;code&gt;id&lt;/code&gt; on the wrong &lt;code&gt;Object&lt;/code&gt;.  These deprecation warnings are, &lt;em&gt;surprisingly&lt;/em&gt;, explicitly turned off in Rails rake tasks in both Rails 2.3 and Rails 3.&lt;/p&gt;

&lt;h2&gt;Missing Warnings&lt;/h2&gt;

&lt;p&gt;A Rake task like the following will output nothing when run from &lt;code&gt;lib/tasks&lt;/code&gt;:&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;namespace :deprec do
  task :test do
    nil.type # =&amp;gt; no deprecation warning
    nil.id # =&amp;gt; no deprecation warning

    A = Class.new
    a = A.new 
    a.id # =&amp;gt;  no deprecation warning
    a.type # =&amp;gt;  no deprecation warning
  end
end
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;To add back the warnings, simply put &lt;code&gt;$VERBOSE = true&lt;/code&gt; at the top of the rake task:&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;$VERBOSE = true
namespace :deprec do
  task :test do
    nil.type # =&amp;gt; deprecation warning
    nil.id # =&amp;gt; deprecation warning

    A = Class.new
    a = A.new 
    a.id # =&amp;gt; deprecation warning
    a.type # =&amp;gt; deprecation warning
  end
end
&lt;/code&gt;&lt;/pre&gt;

&lt;h2&gt;A Long History&lt;/h2&gt;

&lt;p&gt;I'm not sure why silencing warnings is the default behavior for Rails rake tasks.  The &lt;code&gt;$VERBOSE = nil&lt;/code&gt; &lt;a href="http://github.com/rails/rails/blob/db045dbbf60b53dbe013ef25554fd013baf88134/railties/fresh_rakefile"&gt;seems to originate&lt;/a&gt; in the &lt;a href="http://github.com/rails/rails/commit/db045dbbf60b53dbe013ef25554fd013baf88134"&gt;very first commit&lt;/a&gt;.&lt;/p&gt;
</summary>
    <content type="html">&lt;p&gt;Ruby prints some helpful deprecation warnings when using some deprecated features of the Ruby language.  However, you won't find these errors when they occur within a rake task run inside Rails.&lt;/p&gt;

&lt;h2&gt;Warnings Often Indicate Bugs&lt;/h2&gt;

&lt;p&gt;I prefer to &lt;a href="http://media.pragprog.com/titles/pad/CodeAndDebug.pdf"&gt;treat warnings as errors&lt;/a&gt;, as warnings often indicate the presence of a bug.  For example, in Ruby 1.8.7, the &lt;code&gt;id&lt;/code&gt; method is deprecated and calling &lt;code&gt;id&lt;/code&gt; on an &lt;code&gt;Object&lt;/code&gt; that doesn't define an &lt;code&gt;id&lt;/code&gt; method prints an error:&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;warning: Object#id will be deprecated; use Object#object_id
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;A warning like that is probably a good sign I am calling &lt;code&gt;id&lt;/code&gt; on the wrong &lt;code&gt;Object&lt;/code&gt;.  These deprecation warnings are, &lt;em&gt;surprisingly&lt;/em&gt;, explicitly turned off in Rails rake tasks in both Rails 2.3 and Rails 3.&lt;/p&gt;

&lt;h2&gt;Missing Warnings&lt;/h2&gt;

&lt;p&gt;A Rake task like the following will output nothing when run from &lt;code&gt;lib/tasks&lt;/code&gt;:&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;namespace :deprec do
  task :test do
    nil.type # =&amp;gt; no deprecation warning
    nil.id # =&amp;gt; no deprecation warning

    A = Class.new
    a = A.new 
    a.id # =&amp;gt;  no deprecation warning
    a.type # =&amp;gt;  no deprecation warning
  end
end
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;To add back the warnings, simply put &lt;code&gt;$VERBOSE = true&lt;/code&gt; at the top of the rake task:&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;$VERBOSE = true
namespace :deprec do
  task :test do
    nil.type # =&amp;gt; deprecation warning
    nil.id # =&amp;gt; deprecation warning

    A = Class.new
    a = A.new 
    a.id # =&amp;gt; deprecation warning
    a.type # =&amp;gt; deprecation warning
  end
end
&lt;/code&gt;&lt;/pre&gt;

&lt;h2&gt;A Long History&lt;/h2&gt;

&lt;p&gt;I'm not sure why silencing warnings is the default behavior for Rails rake tasks.  The &lt;code&gt;$VERBOSE = nil&lt;/code&gt; &lt;a href="http://github.com/rails/rails/blob/db045dbbf60b53dbe013ef25554fd013baf88134/railties/fresh_rakefile"&gt;seems to originate&lt;/a&gt; in the &lt;a href="http://github.com/rails/rails/commit/db045dbbf60b53dbe013ef25554fd013baf88134"&gt;very first commit&lt;/a&gt;.&lt;/p&gt;
</content>
    <author>
      <name>Erik Andrejko</name>
      <email>erik@railsillustrated.com</email>
    </author>
  </entry>
  <entry>
    <id>http://www.railsillustrated.com/logger-tricks.html</id>
    <link type="text/html" href="http://www.railsillustrated.com/logger-tricks.html" rel="alternate"/>
    <updated>2012-09-02T19:47:49Z</updated>
    <title>Rails Logger Tricks</title>
    <summary type="html">&lt;p&gt;Here are a few quick tricks for using the Rails logger.&lt;/p&gt;

&lt;p&gt;Save disk space by rotating logs in the &lt;code&gt;config/environments/test.rb&lt;/code&gt; and &lt;code&gt;config/environments/development.rb&lt;/code&gt;&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;config.logger = Logger.new(Rails.root.join("log",Rails.env + ".log"),3,5*1024*1024)
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;which will rotate the log files every 5 megabytes and leave only the three most recent log files.  This will limit the total spaces used by the logs at 15 megabytes.&lt;/p&gt;

&lt;p&gt;To log to STDOUT while using the console use &lt;a href="http://rubyquicktips.tumblr.com/post/379756937/always-turn-on-activerecord-logging-in-the-console"&gt;this trick&lt;/a&gt;:&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;if $0 == "irb"
  config.logger = Logger.new(STDOUT)
else
  config.logger = Logger.new(Rails.root.join("log",Rails.env + ".log"),3,5*1024*1024)  
end
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;For more tips on Rails logging see this post: &lt;a href="http://maintainable.com/articles/rails_logging_tips"&gt;Rails Logging Tips&lt;/a&gt;.&lt;/p&gt;
</summary>
    <content type="html">&lt;p&gt;Here are a few quick tricks for using the Rails logger.&lt;/p&gt;

&lt;p&gt;Save disk space by rotating logs in the &lt;code&gt;config/environments/test.rb&lt;/code&gt; and &lt;code&gt;config/environments/development.rb&lt;/code&gt;&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;config.logger = Logger.new(Rails.root.join("log",Rails.env + ".log"),3,5*1024*1024)
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;which will rotate the log files every 5 megabytes and leave only the three most recent log files.  This will limit the total spaces used by the logs at 15 megabytes.&lt;/p&gt;

&lt;p&gt;To log to STDOUT while using the console use &lt;a href="http://rubyquicktips.tumblr.com/post/379756937/always-turn-on-activerecord-logging-in-the-console"&gt;this trick&lt;/a&gt;:&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;if $0 == "irb"
  config.logger = Logger.new(STDOUT)
else
  config.logger = Logger.new(Rails.root.join("log",Rails.env + ".log"),3,5*1024*1024)  
end
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;For more tips on Rails logging see this post: &lt;a href="http://maintainable.com/articles/rails_logging_tips"&gt;Rails Logging Tips&lt;/a&gt;.&lt;/p&gt;
</content>
    <author>
      <name>Erik Andrejko</name>
      <email>erik@railsillustrated.com</email>
    </author>
  </entry>
  <entry>
    <id>http://www.railsillustrated.com/getting-started-with-tokyo-on-osx.html</id>
    <link type="text/html" href="http://www.railsillustrated.com/getting-started-with-tokyo-on-osx.html" rel="alternate"/>
    <updated>2012-09-02T19:47:49Z</updated>
    <title>Getting Started with Tokyo Cabinet</title>
    <summary type="html">&lt;p&gt;&lt;img src="/posts/421422_21102890.jpg" alt="421422 21102890" /&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="http://tokyocabinet.sourceforge.net/"&gt;Tokyo Cabinet&lt;/a&gt; is an &lt;a href="http://gist.github.com/54371"&gt;extremely fast&lt;/a&gt; key-value store.  It's a lot like memcache: &lt;strong&gt;only better&lt;/strong&gt;.&lt;/p&gt;

&lt;h2&gt;Advantages over Memcache&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;Tokyo Cabinet can be run as a server (called &lt;a href="http://tokyocabinet.sourceforge.net/tyrantdoc/"&gt;Tokyo Tyrant&lt;/a&gt;) but it can also be used without running as a server.&lt;/li&gt;
&lt;li&gt;Tokyo Cabinet understands the memcache protocol, but also has its own protocol that has a smaller network footprint.&lt;/li&gt;
&lt;li&gt;Tokyo Cabinet is &lt;a href="http://tokyocabinet.sourceforge.net/rubydoc/"&gt;more flexible&lt;/a&gt; supporting different types of stores.&lt;/li&gt;
&lt;li&gt;Tokyo Cabinet stores can be easily persisted to disk.&lt;/li&gt;
&lt;/ul&gt;


&lt;h2&gt;Installation&lt;/h2&gt;

&lt;p&gt;First, you must install the Tokyo Cabinet libraries:&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;wget http://tokyocabinet.sourceforge.net/tokyocabinet-1.4.23.tar.gz
tar xvzf tokyocabinet-1.4.23.tar.gz
cd tokyocabinet-1.4.23
./configure
make
sudo make install
&lt;/code&gt;&lt;/pre&gt;

&lt;h3&gt;Ruby bindings installation&lt;/h3&gt;

&lt;p&gt;Next install the Ruby interface.  Unfortunately, Tokyo Cabinet is not currently bundled as a gem.  To install:&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;wget http://tokyocabinet.sourceforge.net/rubypkg/tokyocabinet-ruby-1.25.tar.gz
tar xvzf tokyocabinet-ruby-1.25.tar.gz
cd tokyocabinet-ruby-1.25/
ruby extconf.rb
make
&lt;/code&gt;&lt;/pre&gt;

&lt;h3&gt;Installation on Mac OS X Leopard&lt;/h3&gt;

&lt;p&gt;If, on Mac OS X Leopard, you get an error like this:&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;ld: warning in /usr/local/lib/libtokyocabinet.dylib, file is not of required architecture
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;Then replace the instructions above with:&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;wget http://tokyocabinet.sourceforge.net/rubypkg/tokyocabinet-ruby-1.25.tar.gz
tar xvzf tokyocabinet-ruby-1.25.tar.gz
cd tokyocabinet-ruby-1.25/
ARCHFLAGS="-arch i386" ruby extconf.rb
make clean
make
sudo make install
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;Now you should have the Ruby interface to the Tokyo Cabinet.&lt;/p&gt;

&lt;h2&gt;Using Tokyo Cabinet&lt;/h2&gt;

&lt;p&gt;To use Tokyo Cabinet within Ruby it is as simple as:&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;require 'tokyocabinet'
include TokyoCabinet

hdb = HDB::new
hdb.open("has-db.tch", HDB::OWRITER | HDB::OCREAT)
hdb.put("this-is-a-key","this-is-a-value")
puts hdb.get("this-is-a-key")
&lt;/code&gt;&lt;/pre&gt;

&lt;h2&gt;Further Reading&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;The &lt;a href="http://tokyocabinet.sourceforge.net/rubydoc/"&gt;RDocs for Tokyo Cabinet&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="http://tokyocabinet.sourceforge.net/"&gt;Tokyo Cabinet: a modern implementation of DBM&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="http://www.igvita.com/2009/02/13/tokyo-cabinet-beyond-key-value-store/"&gt;Tokyo Cabinet: Beyond Key-Value Store - igvita.com&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

</summary>
    <content type="html">&lt;p&gt;&lt;img src="/posts/421422_21102890.jpg" alt="421422 21102890" /&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="http://tokyocabinet.sourceforge.net/"&gt;Tokyo Cabinet&lt;/a&gt; is an &lt;a href="http://gist.github.com/54371"&gt;extremely fast&lt;/a&gt; key-value store.  It's a lot like memcache: &lt;strong&gt;only better&lt;/strong&gt;.&lt;/p&gt;

&lt;h2&gt;Advantages over Memcache&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;Tokyo Cabinet can be run as a server (called &lt;a href="http://tokyocabinet.sourceforge.net/tyrantdoc/"&gt;Tokyo Tyrant&lt;/a&gt;) but it can also be used without running as a server.&lt;/li&gt;
&lt;li&gt;Tokyo Cabinet understands the memcache protocol, but also has its own protocol that has a smaller network footprint.&lt;/li&gt;
&lt;li&gt;Tokyo Cabinet is &lt;a href="http://tokyocabinet.sourceforge.net/rubydoc/"&gt;more flexible&lt;/a&gt; supporting different types of stores.&lt;/li&gt;
&lt;li&gt;Tokyo Cabinet stores can be easily persisted to disk.&lt;/li&gt;
&lt;/ul&gt;


&lt;h2&gt;Installation&lt;/h2&gt;

&lt;p&gt;First, you must install the Tokyo Cabinet libraries:&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;wget http://tokyocabinet.sourceforge.net/tokyocabinet-1.4.23.tar.gz
tar xvzf tokyocabinet-1.4.23.tar.gz
cd tokyocabinet-1.4.23
./configure
make
sudo make install
&lt;/code&gt;&lt;/pre&gt;

&lt;h3&gt;Ruby bindings installation&lt;/h3&gt;

&lt;p&gt;Next install the Ruby interface.  Unfortunately, Tokyo Cabinet is not currently bundled as a gem.  To install:&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;wget http://tokyocabinet.sourceforge.net/rubypkg/tokyocabinet-ruby-1.25.tar.gz
tar xvzf tokyocabinet-ruby-1.25.tar.gz
cd tokyocabinet-ruby-1.25/
ruby extconf.rb
make
&lt;/code&gt;&lt;/pre&gt;

&lt;h3&gt;Installation on Mac OS X Leopard&lt;/h3&gt;

&lt;p&gt;If, on Mac OS X Leopard, you get an error like this:&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;ld: warning in /usr/local/lib/libtokyocabinet.dylib, file is not of required architecture
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;Then replace the instructions above with:&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;wget http://tokyocabinet.sourceforge.net/rubypkg/tokyocabinet-ruby-1.25.tar.gz
tar xvzf tokyocabinet-ruby-1.25.tar.gz
cd tokyocabinet-ruby-1.25/
ARCHFLAGS="-arch i386" ruby extconf.rb
make clean
make
sudo make install
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;Now you should have the Ruby interface to the Tokyo Cabinet.&lt;/p&gt;

&lt;h2&gt;Using Tokyo Cabinet&lt;/h2&gt;

&lt;p&gt;To use Tokyo Cabinet within Ruby it is as simple as:&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;require 'tokyocabinet'
include TokyoCabinet

hdb = HDB::new
hdb.open("has-db.tch", HDB::OWRITER | HDB::OCREAT)
hdb.put("this-is-a-key","this-is-a-value")
puts hdb.get("this-is-a-key")
&lt;/code&gt;&lt;/pre&gt;

&lt;h2&gt;Further Reading&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;The &lt;a href="http://tokyocabinet.sourceforge.net/rubydoc/"&gt;RDocs for Tokyo Cabinet&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="http://tokyocabinet.sourceforge.net/"&gt;Tokyo Cabinet: a modern implementation of DBM&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="http://www.igvita.com/2009/02/13/tokyo-cabinet-beyond-key-value-store/"&gt;Tokyo Cabinet: Beyond Key-Value Store - igvita.com&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

</content>
    <author>
      <name>Erik Andrejko</name>
      <email>erik@railsillustrated.com</email>
    </author>
  </entry>
  <entry>
    <id>http://www.railsillustrated.com/wonky-sessions-rails-2-3-passenger.html</id>
    <link type="text/html" href="http://www.railsillustrated.com/wonky-sessions-rails-2-3-passenger.html" rel="alternate"/>
    <updated>2012-09-02T19:47:49Z</updated>
    <title>Weird Session Issues in Rails 2.3 and Passenger</title>
    <summary type="html">&lt;p&gt;Are you having weird issues associated with sessions using Rails 2.3 and Passenger?  Perhaps you have problems like repeated &lt;code&gt;flash&lt;/code&gt; messages and the inability to logout by destroying a session variable.&lt;/p&gt;

&lt;p&gt;There is a simple fix: &lt;strong&gt;&lt;a href="http://blog.phusion.nl/2009/04/18/phusion-passenger-221-released/"&gt;upgrade passenger&lt;/a&gt;&lt;/strong&gt;.  This &lt;a href="http://rails_security.lighthouseapp.com/projects/15332/tickets/40-rails-edge-and-passenger"&gt;lighthouse ticket has some more details about session problems&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;To upgrade passenger:&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;sudo gem install passenger
passenger-install-apache2-module
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;After installing the new Passenger Apache module you will probably have to edit the &lt;code&gt;httpd.conf&lt;/code&gt; to include the new version of the Passenger module.&lt;/p&gt;

&lt;p&gt;After restarting Apache everything was working correctly.&lt;/p&gt;
</summary>
    <content type="html">&lt;p&gt;Are you having weird issues associated with sessions using Rails 2.3 and Passenger?  Perhaps you have problems like repeated &lt;code&gt;flash&lt;/code&gt; messages and the inability to logout by destroying a session variable.&lt;/p&gt;

&lt;p&gt;There is a simple fix: &lt;strong&gt;&lt;a href="http://blog.phusion.nl/2009/04/18/phusion-passenger-221-released/"&gt;upgrade passenger&lt;/a&gt;&lt;/strong&gt;.  This &lt;a href="http://rails_security.lighthouseapp.com/projects/15332/tickets/40-rails-edge-and-passenger"&gt;lighthouse ticket has some more details about session problems&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;To upgrade passenger:&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;sudo gem install passenger
passenger-install-apache2-module
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;After installing the new Passenger Apache module you will probably have to edit the &lt;code&gt;httpd.conf&lt;/code&gt; to include the new version of the Passenger module.&lt;/p&gt;

&lt;p&gt;After restarting Apache everything was working correctly.&lt;/p&gt;
</content>
    <author>
      <name>Erik Andrejko</name>
      <email>erik@railsillustrated.com</email>
    </author>
  </entry>
  <entry>
    <id>http://www.railsillustrated.com/diggbar.html</id>
    <link type="text/html" href="http://www.railsillustrated.com/diggbar.html" rel="alternate"/>
    <updated>2012-09-02T19:47:49Z</updated>
    <title>Is the Diggbar Impacting Your User's Experience?</title>
    <summary type="html">&lt;p&gt;&lt;img src="/posts/diggbar.jpg" alt="Diggbar" /&gt;&lt;/p&gt;

&lt;p&gt;The recently launched Diggbar from &lt;a href="http://digg.com"&gt;digg.com&lt;/a&gt; can wrap your site in a frame if the user visits a special URL on digg.com.  Your user's experience is negatively impacted:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;the URL in the address bar is 'digg.com'&lt;/li&gt;
&lt;li&gt;your site title could be changed&lt;/li&gt;
&lt;li&gt;content is moved down the page (leaving less 'above the fold')&lt;/li&gt;
&lt;li&gt;it adds visual noise to your site&lt;/li&gt;
&lt;li&gt;it changes your finely crafted visual design&lt;/li&gt;
&lt;li&gt;your site's personality is affected&lt;/li&gt;
&lt;/ul&gt;


&lt;p&gt;Many sites (like the &lt;a href="http://nytimes.com"&gt;NY Times&lt;/a&gt;) are, with good reason, &lt;a href="http://daringfireball.net/2009/04/how_to_block_the_diggbar"&gt;blocking this Diggbar&lt;/a&gt;.  There is a Rails plugin to do this for your site: &lt;a href="http://github.com/sandofsky/acts_against_douchebar/tree/master"&gt;acts_against_douchebar&lt;/a&gt;.&lt;/p&gt;
</summary>
    <content type="html">&lt;p&gt;&lt;img src="/posts/diggbar.jpg" alt="Diggbar" /&gt;&lt;/p&gt;

&lt;p&gt;The recently launched Diggbar from &lt;a href="http://digg.com"&gt;digg.com&lt;/a&gt; can wrap your site in a frame if the user visits a special URL on digg.com.  Your user's experience is negatively impacted:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;the URL in the address bar is 'digg.com'&lt;/li&gt;
&lt;li&gt;your site title could be changed&lt;/li&gt;
&lt;li&gt;content is moved down the page (leaving less 'above the fold')&lt;/li&gt;
&lt;li&gt;it adds visual noise to your site&lt;/li&gt;
&lt;li&gt;it changes your finely crafted visual design&lt;/li&gt;
&lt;li&gt;your site's personality is affected&lt;/li&gt;
&lt;/ul&gt;


&lt;p&gt;Many sites (like the &lt;a href="http://nytimes.com"&gt;NY Times&lt;/a&gt;) are, with good reason, &lt;a href="http://daringfireball.net/2009/04/how_to_block_the_diggbar"&gt;blocking this Diggbar&lt;/a&gt;.  There is a Rails plugin to do this for your site: &lt;a href="http://github.com/sandofsky/acts_against_douchebar/tree/master"&gt;acts_against_douchebar&lt;/a&gt;.&lt;/p&gt;
</content>
    <author>
      <name>Erik Andrejko</name>
      <email>erik@railsillustrated.com</email>
    </author>
  </entry>
  <entry>
    <id>http://www.railsillustrated.com/rails-documentation.html</id>
    <link type="text/html" href="http://www.railsillustrated.com/rails-documentation.html" rel="alternate"/>
    <updated>2012-09-02T19:47:48Z</updated>
    <title>Browsing the Rails Documentation online</title>
    <summary type="html">&lt;p&gt;&lt;img src="/posts/railsdocs.jpg" alt="Railsdocs" /&gt;&lt;/p&gt;

&lt;p&gt;There is a great new searchable version of the Rails documentation online at &lt;a href="http://voloko.ru/sdoc/rails/rdoc/"&gt;http://voloko.ru/sdoc/rails/rdoc/&lt;/a&gt;.  This version is very fast and can &lt;a href="http://voloko.ru/sdoc/rails/"&gt;even be downloaded&lt;/a&gt; for offline (airplane?) use.  What a nice usable search interface with embedded browsing capabilit.  I have used &lt;a href="http://www.railsbrain.com/"&gt;http://www.railsbrain.com/&lt;/a&gt; but I find the search experience to be suboptimal.  (Thanks to &lt;a href="http://www.railsinside.com/documentation/258-good-looking-searchable-rails-api-docs.html"&gt;Rails Inside&lt;/a&gt; for the link.)&lt;/p&gt;
</summary>
    <content type="html">&lt;p&gt;&lt;img src="/posts/railsdocs.jpg" alt="Railsdocs" /&gt;&lt;/p&gt;

&lt;p&gt;There is a great new searchable version of the Rails documentation online at &lt;a href="http://voloko.ru/sdoc/rails/rdoc/"&gt;http://voloko.ru/sdoc/rails/rdoc/&lt;/a&gt;.  This version is very fast and can &lt;a href="http://voloko.ru/sdoc/rails/"&gt;even be downloaded&lt;/a&gt; for offline (airplane?) use.  What a nice usable search interface with embedded browsing capabilit.  I have used &lt;a href="http://www.railsbrain.com/"&gt;http://www.railsbrain.com/&lt;/a&gt; but I find the search experience to be suboptimal.  (Thanks to &lt;a href="http://www.railsinside.com/documentation/258-good-looking-searchable-rails-api-docs.html"&gt;Rails Inside&lt;/a&gt; for the link.)&lt;/p&gt;
</content>
    <author>
      <name>Erik Andrejko</name>
      <email>erik@railsillustrated.com</email>
    </author>
  </entry>
  <entry>
    <id>http://www.railsillustrated.com/safari-tabs-in-title-bar.html</id>
    <link type="text/html" href="http://www.railsillustrated.com/safari-tabs-in-title-bar.html" rel="alternate"/>
    <updated>2012-09-02T19:47:48Z</updated>
    <title>Safari version 4 and tabs in the title bar</title>
    <summary type="html">&lt;p&gt;&lt;img src="/posts/safari-4.jpg" alt="Safari 4" /&gt;&lt;/p&gt;

&lt;p&gt;If you haven't already seen the beta version of Safari 4 you might be in for a little surprise when upgrading from Safari 3.  The tabs have moved into the title bar of the window, just like Google Chrome.&lt;/p&gt;

&lt;p&gt;I haven't decided if this is a good idea or not yet.  What I find the most interesting is that this has generated a lot of spirited discussion.  The most important conclusion from this whole ordeal is probably that sometimes making user experience decisions is very hard.  Fortunately, most user experience decisions are pretty easy.&lt;/p&gt;

&lt;h2&gt;Arguments in favor of tabs in the title bar&lt;/h2&gt;

&lt;p&gt;There are a couple of arguments that placing tabs in the title bar more closely matches the metaphor that a modern web browser uses.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="http://www.google.com/googlebooks/chrome/small_18.html"&gt;Google Chrome's Explanation&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="http://ignorethecode.net/blog/2009/02/24/hierarchies/"&gt;Matching Users' Mental Models&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="http://manton.org/2009/02/defending.html"&gt;Defending Safari 4 tabs&lt;/a&gt; with several good criticisms&lt;/li&gt;
&lt;/ul&gt;


&lt;h2&gt;Arguments against tabs in the title bar&lt;/h2&gt;

&lt;p&gt;There are those that find good reason not to put tabs in the title bar:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="http://www.tuaw.com/2009/02/26/tabs-in-the-title-bar-a-ui-design-trend-that-needs-to-go/"&gt;Tabs in the title bar: a UI design trend that needs to go&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="http://onebuttonmouse.com/ramblings/vagabond-tabs/"&gt;Vagabond Tabs&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;


&lt;h2&gt;Changing the tabs back&lt;/h2&gt;

&lt;p&gt;To change back the tab behavior to be like Safari 3 use:&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;$ defaults write com.apple.Safari DebugSafari4TabBarIsOnTop -bool NO
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;More &lt;a href="http://swedishcampground.com/safari-4-hidden-preferences"&gt;configuration options&lt;/a&gt; are also available.&lt;/p&gt;

&lt;h2&gt;Reloading is now harder&lt;/h2&gt;

&lt;p&gt;One complaint that I do have is that clicking the reload button is now much harder.  In Safari 4 the reload button is &lt;strong&gt;only 30%&lt;/strong&gt; of the size of the Safari 3 button.  This makes it much more difficult and &lt;a href="http://en.wikipedia.org/wiki/Fitts's_law"&gt;much slower to click&lt;/a&gt;.&lt;/p&gt;
</summary>
    <content type="html">&lt;p&gt;&lt;img src="/posts/safari-4.jpg" alt="Safari 4" /&gt;&lt;/p&gt;

&lt;p&gt;If you haven't already seen the beta version of Safari 4 you might be in for a little surprise when upgrading from Safari 3.  The tabs have moved into the title bar of the window, just like Google Chrome.&lt;/p&gt;

&lt;p&gt;I haven't decided if this is a good idea or not yet.  What I find the most interesting is that this has generated a lot of spirited discussion.  The most important conclusion from this whole ordeal is probably that sometimes making user experience decisions is very hard.  Fortunately, most user experience decisions are pretty easy.&lt;/p&gt;

&lt;h2&gt;Arguments in favor of tabs in the title bar&lt;/h2&gt;

&lt;p&gt;There are a couple of arguments that placing tabs in the title bar more closely matches the metaphor that a modern web browser uses.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="http://www.google.com/googlebooks/chrome/small_18.html"&gt;Google Chrome's Explanation&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="http://ignorethecode.net/blog/2009/02/24/hierarchies/"&gt;Matching Users' Mental Models&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="http://manton.org/2009/02/defending.html"&gt;Defending Safari 4 tabs&lt;/a&gt; with several good criticisms&lt;/li&gt;
&lt;/ul&gt;


&lt;h2&gt;Arguments against tabs in the title bar&lt;/h2&gt;

&lt;p&gt;There are those that find good reason not to put tabs in the title bar:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="http://www.tuaw.com/2009/02/26/tabs-in-the-title-bar-a-ui-design-trend-that-needs-to-go/"&gt;Tabs in the title bar: a UI design trend that needs to go&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="http://onebuttonmouse.com/ramblings/vagabond-tabs/"&gt;Vagabond Tabs&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;


&lt;h2&gt;Changing the tabs back&lt;/h2&gt;

&lt;p&gt;To change back the tab behavior to be like Safari 3 use:&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;$ defaults write com.apple.Safari DebugSafari4TabBarIsOnTop -bool NO
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;More &lt;a href="http://swedishcampground.com/safari-4-hidden-preferences"&gt;configuration options&lt;/a&gt; are also available.&lt;/p&gt;

&lt;h2&gt;Reloading is now harder&lt;/h2&gt;

&lt;p&gt;One complaint that I do have is that clicking the reload button is now much harder.  In Safari 4 the reload button is &lt;strong&gt;only 30%&lt;/strong&gt; of the size of the Safari 3 button.  This makes it much more difficult and &lt;a href="http://en.wikipedia.org/wiki/Fitts's_law"&gt;much slower to click&lt;/a&gt;.&lt;/p&gt;
</content>
    <author>
      <name>Erik Andrejko</name>
      <email>erik@railsillustrated.com</email>
    </author>
  </entry>
  <entry>
    <id>http://www.railsillustrated.com/simple-improvement-to-twitter.html</id>
    <link type="text/html" href="http://www.railsillustrated.com/simple-improvement-to-twitter.html" rel="alternate"/>
    <updated>2012-09-02T19:47:48Z</updated>
    <title>A Simple Improvement to Twitter</title>
    <summary type="html">&lt;p&gt;&lt;img src="/posts/twitter-0.jpg" alt="Twitter 0" /&gt;&lt;/p&gt;

&lt;p&gt;I'm sure that many have seen the Twitter user page.  I would guess most common use                         of this page is to:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;decide if to follow&lt;/strong&gt; a user, and then&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;to follow&lt;/strong&gt; that user&lt;/li&gt;
&lt;/ul&gt;


&lt;p&gt;If that is the case, why isn't the page optimized for these tasks?&lt;/p&gt;

&lt;h2&gt;What works well&lt;/h2&gt;

&lt;p&gt;In order to meet the first need: "decide if to follow a user" I think the page works well.&lt;/p&gt;

&lt;p&gt;The following are probably of most interest in deciding if to follow a user:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;number of followers&lt;/li&gt;
&lt;li&gt;number following&lt;/li&gt;
&lt;li&gt;bio&lt;/li&gt;
&lt;li&gt;recent tweets&lt;/li&gt;
&lt;/ul&gt;


&lt;p&gt;All of these items are shown in a prominent place in the page.  It is easy for a user to scan and find all of these items in a matter of a couple of seconds.&lt;/p&gt;

&lt;h2&gt;What can be improved&lt;/h2&gt;

&lt;p&gt;Now that you have decided to follow a user.  The next action to take is to click the 'Follow' button.
&lt;img src="/posts/twitter-1.jpg" alt="Twitter 1" /&gt;
Think back to the first time you clicked on the 'Follow' button.  Was the click successful?  How did you know?&lt;/p&gt;

&lt;p&gt;There are two problems with the interaction design of the 'Follow' button:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;the 'Follow' button does not change state&lt;/li&gt;
&lt;li&gt;the spinner is &lt;strong&gt;700 pixels away&lt;/strong&gt; from the button&lt;/li&gt;
&lt;/ul&gt;


&lt;h2&gt;A simple improvement&lt;/h2&gt;

&lt;p&gt;By changing a few lines of HTML and Javascript the design could be changed to:
&lt;img src="/posts/twitter-2.jpg" alt="Twitter 1" /&gt;&lt;/p&gt;

&lt;p&gt;In the redesign, after clicking 'Follow' the reaction is entirely in the visual field of the user and linked by proximity to the 'Follow' button.&lt;/p&gt;

&lt;p&gt;In this case:
- the 'Follow' button changes state and indicates this change visually
- the spinner is right next to the button&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Note&lt;/strong&gt;: It may be the case that the spinner is used for multiple actions on the same page.  If this is the case, place a spinner appropriately near each interface element that initiates an action.&lt;/p&gt;

&lt;h2&gt;Reasonable, but can be improved&lt;/h2&gt;

&lt;p&gt;This use of proximity is already used by Twitter to indicate that the follow action has been successful.&lt;/p&gt;

&lt;p&gt;&lt;img src="/posts/twitter-following.jpg" alt="Twitter Following" /&gt;&lt;/p&gt;

&lt;p&gt;I think the placement of the success message is appropriate, as is the change in state of the 'Follow' button.&lt;/p&gt;

&lt;p&gt;Now suppose that the user wants to stop following someone.  They will click the 'Following' button and see:
&lt;img src="/posts/twitter-unfollow.jpg" alt="Twitter Unfollow" /&gt;&lt;/p&gt;

&lt;p&gt;There are a few issues:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;to unfollow requires two clicks&lt;/li&gt;
&lt;li&gt;the unfollow process is not symmetric with the follow process&lt;/li&gt;
&lt;li&gt;the language is not consistent: click 'Follow' to follow, click the 'Following' to stop following&lt;/li&gt;
&lt;li&gt;unfollow is called 'remove'&lt;/li&gt;
&lt;/ul&gt;


&lt;p&gt;I suspect the design is like this to prevent a user from spuriously unfollowing someone.  In cases like these it is almost always better to &lt;strong&gt;replace confirmation actions with an undo&lt;/strong&gt;, i.e. &lt;a href="http://www.alistapart.com/articles/neveruseawarning/"&gt;Never Use a Waring When you Mean Undo&lt;/a&gt;.&lt;/p&gt;
</summary>
    <content type="html">&lt;p&gt;&lt;img src="/posts/twitter-0.jpg" alt="Twitter 0" /&gt;&lt;/p&gt;

&lt;p&gt;I'm sure that many have seen the Twitter user page.  I would guess most common use                         of this page is to:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;decide if to follow&lt;/strong&gt; a user, and then&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;to follow&lt;/strong&gt; that user&lt;/li&gt;
&lt;/ul&gt;


&lt;p&gt;If that is the case, why isn't the page optimized for these tasks?&lt;/p&gt;

&lt;h2&gt;What works well&lt;/h2&gt;

&lt;p&gt;In order to meet the first need: "decide if to follow a user" I think the page works well.&lt;/p&gt;

&lt;p&gt;The following are probably of most interest in deciding if to follow a user:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;number of followers&lt;/li&gt;
&lt;li&gt;number following&lt;/li&gt;
&lt;li&gt;bio&lt;/li&gt;
&lt;li&gt;recent tweets&lt;/li&gt;
&lt;/ul&gt;


&lt;p&gt;All of these items are shown in a prominent place in the page.  It is easy for a user to scan and find all of these items in a matter of a couple of seconds.&lt;/p&gt;

&lt;h2&gt;What can be improved&lt;/h2&gt;

&lt;p&gt;Now that you have decided to follow a user.  The next action to take is to click the 'Follow' button.
&lt;img src="/posts/twitter-1.jpg" alt="Twitter 1" /&gt;
Think back to the first time you clicked on the 'Follow' button.  Was the click successful?  How did you know?&lt;/p&gt;

&lt;p&gt;There are two problems with the interaction design of the 'Follow' button:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;the 'Follow' button does not change state&lt;/li&gt;
&lt;li&gt;the spinner is &lt;strong&gt;700 pixels away&lt;/strong&gt; from the button&lt;/li&gt;
&lt;/ul&gt;


&lt;h2&gt;A simple improvement&lt;/h2&gt;

&lt;p&gt;By changing a few lines of HTML and Javascript the design could be changed to:
&lt;img src="/posts/twitter-2.jpg" alt="Twitter 1" /&gt;&lt;/p&gt;

&lt;p&gt;In the redesign, after clicking 'Follow' the reaction is entirely in the visual field of the user and linked by proximity to the 'Follow' button.&lt;/p&gt;

&lt;p&gt;In this case:
- the 'Follow' button changes state and indicates this change visually
- the spinner is right next to the button&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Note&lt;/strong&gt;: It may be the case that the spinner is used for multiple actions on the same page.  If this is the case, place a spinner appropriately near each interface element that initiates an action.&lt;/p&gt;

&lt;h2&gt;Reasonable, but can be improved&lt;/h2&gt;

&lt;p&gt;This use of proximity is already used by Twitter to indicate that the follow action has been successful.&lt;/p&gt;

&lt;p&gt;&lt;img src="/posts/twitter-following.jpg" alt="Twitter Following" /&gt;&lt;/p&gt;

&lt;p&gt;I think the placement of the success message is appropriate, as is the change in state of the 'Follow' button.&lt;/p&gt;

&lt;p&gt;Now suppose that the user wants to stop following someone.  They will click the 'Following' button and see:
&lt;img src="/posts/twitter-unfollow.jpg" alt="Twitter Unfollow" /&gt;&lt;/p&gt;

&lt;p&gt;There are a few issues:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;to unfollow requires two clicks&lt;/li&gt;
&lt;li&gt;the unfollow process is not symmetric with the follow process&lt;/li&gt;
&lt;li&gt;the language is not consistent: click 'Follow' to follow, click the 'Following' to stop following&lt;/li&gt;
&lt;li&gt;unfollow is called 'remove'&lt;/li&gt;
&lt;/ul&gt;


&lt;p&gt;I suspect the design is like this to prevent a user from spuriously unfollowing someone.  In cases like these it is almost always better to &lt;strong&gt;replace confirmation actions with an undo&lt;/strong&gt;, i.e. &lt;a href="http://www.alistapart.com/articles/neveruseawarning/"&gt;Never Use a Waring When you Mean Undo&lt;/a&gt;.&lt;/p&gt;
</content>
    <author>
      <name>Erik Andrejko</name>
      <email>erik@railsillustrated.com</email>
    </author>
  </entry>
  <entry>
    <id>http://www.railsillustrated.com/interaction-09.html</id>
    <link type="text/html" href="http://www.railsillustrated.com/interaction-09.html" rel="alternate"/>
    <updated>2012-09-02T19:47:48Z</updated>
    <title>Interaction '09 Conference in Bullet Form</title>
    <summary type="html">&lt;p&gt;&lt;a href="http://www.lukew.com/"&gt;Luke Wroblewski&lt;/a&gt;, the writer of the book &lt;a href="http://www.lukew.com/resources/web_form_design.asp"&gt;Web Form Design: Filling in the Blanks&lt;/a&gt;, has a synopsis of some of the talks at this years &lt;a href="http://interaction09.ixda.org/"&gt;Interaction Conference&lt;/a&gt;. The talks include:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="http://www.lukew.com/ff/entry.asp?772"&gt;How to Change Complicated Stuff&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="http://www.lukew.com/ff/entry.asp?774"&gt;Designing the Viral App&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="http://www.lukew.com/ff/entry.asp?773"&gt;Mobile UX Patterns&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="http://www.lukew.com/ff/entry.asp?771"&gt;Carpe Diem&lt;/a&gt; (growing the profession of interaction design)&lt;/li&gt;
&lt;li&gt;&lt;a href="http://www.lukew.com/ff/entry.asp?770"&gt;Designing Natural Interfaces&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="http://www.lukew.com/ff/entry.asp?769"&gt;Irrational Behavior&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="http://www.lukew.com/ff/entry.asp?768"&gt;Fiona Raby&lt;/a&gt; (interaction between people and things)&lt;/li&gt;
&lt;li&gt;&lt;a href="http://www.lukew.com/ff/entry.asp?767"&gt;Coming Into Your Own&lt;/a&gt; (growing the discipline of interaction design)&lt;/li&gt;
&lt;li&gt;&lt;a href="http://www.lukew.com/ff/entry.asp?766"&gt;Experiencing Sustainability&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

</summary>
    <content type="html">&lt;p&gt;&lt;a href="http://www.lukew.com/"&gt;Luke Wroblewski&lt;/a&gt;, the writer of the book &lt;a href="http://www.lukew.com/resources/web_form_design.asp"&gt;Web Form Design: Filling in the Blanks&lt;/a&gt;, has a synopsis of some of the talks at this years &lt;a href="http://interaction09.ixda.org/"&gt;Interaction Conference&lt;/a&gt;. The talks include:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="http://www.lukew.com/ff/entry.asp?772"&gt;How to Change Complicated Stuff&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="http://www.lukew.com/ff/entry.asp?774"&gt;Designing the Viral App&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="http://www.lukew.com/ff/entry.asp?773"&gt;Mobile UX Patterns&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="http://www.lukew.com/ff/entry.asp?771"&gt;Carpe Diem&lt;/a&gt; (growing the profession of interaction design)&lt;/li&gt;
&lt;li&gt;&lt;a href="http://www.lukew.com/ff/entry.asp?770"&gt;Designing Natural Interfaces&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="http://www.lukew.com/ff/entry.asp?769"&gt;Irrational Behavior&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="http://www.lukew.com/ff/entry.asp?768"&gt;Fiona Raby&lt;/a&gt; (interaction between people and things)&lt;/li&gt;
&lt;li&gt;&lt;a href="http://www.lukew.com/ff/entry.asp?767"&gt;Coming Into Your Own&lt;/a&gt; (growing the discipline of interaction design)&lt;/li&gt;
&lt;li&gt;&lt;a href="http://www.lukew.com/ff/entry.asp?766"&gt;Experiencing Sustainability&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

</content>
    <author>
      <name>Erik Andrejko</name>
      <email>erik@railsillustrated.com</email>
    </author>
  </entry>
  <entry>
    <id>http://www.railsillustrated.com/more-speed-improvements-with-asset_packager.html</id>
    <link type="text/html" href="http://www.railsillustrated.com/more-speed-improvements-with-asset_packager.html" rel="alternate"/>
    <updated>2012-09-02T19:47:48Z</updated>
    <title>More Rails Speed with asset_packager Plugin</title>
    <summary type="html">&lt;p&gt;One of the &lt;a href="/Improving-Rails-Application-Speed.html"&gt;many strategies for speeding up your Rails application&lt;/a&gt; is to &lt;strong&gt;serve exactly one CSS file&lt;/strong&gt; and &lt;strong&gt;exactly one Javascript file&lt;/strong&gt;.  It is often more convenient to have multiple files in development and only package these files into one file in production.  It doesn't hurt to also minify these files when in production mode.  This is exactly what Scott Becker's great &lt;code&gt;asset_packager&lt;/code&gt; &lt;a href="http://synthesis.sbecker.net/pages/asset_packager"&gt;original plugin&lt;/a&gt; is for.&lt;/p&gt;

&lt;h2&gt;Asset Packager Plugin with YUI Compressor&lt;/h2&gt;

&lt;p&gt;The standard &lt;code&gt;asset_packager&lt;/code&gt; is a great plugin.  It has a couple of issues:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;It has a bug: it mangles CSS with multiline comments.&lt;/li&gt;
&lt;li&gt;It uses a sub optimal Javascript and CSS compressor.&lt;/li&gt;
&lt;/ul&gt;


&lt;h3&gt;YUI Compressor&lt;/h3&gt;

&lt;p&gt;The &lt;a href="http://developer.yahoo.com/yui/compressor/"&gt;Yahoo YUI Compressor&lt;/a&gt; is "100% safe" and does a great of job of reducing both Javascript and CSS file size.&lt;/p&gt;

&lt;blockquote&gt;&lt;p&gt;The goal of JavaScript and CSS minification is always to preserve the operational qualities of the code while reducing its overall byte footprint (both in raw terms and after gzipping, as most JavaScript and CSS served from production web servers is gzipped as part of the HTTP protocol). The YUI Compressor is JavaScript minifier designed to be 100% safe and yield a higher compression ratio than most other tools. Tests on the YUI Library have shown savings of over 20% compared to JSMin (becoming 10% after HTTP compression).&lt;/p&gt;&lt;/blockquote&gt;

&lt;p&gt;I have modified the &lt;code&gt;asset_packager&lt;/code&gt; plugin to use the YUI compressor, if available, for both CSS and Javascript minification.&lt;/p&gt;

&lt;h3&gt;Compression&lt;/h3&gt;

&lt;p&gt;The YUI compressor provides some serious savings:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;code&gt;prototype.js&lt;/code&gt; uncompressed: 54K&lt;/li&gt;
&lt;li&gt;&lt;code&gt;prototype.js&lt;/code&gt; compressed with &lt;code&gt;jsmin.rb&lt;/code&gt; Ruby compressor: 41K&lt;/li&gt;
&lt;li&gt;&lt;code&gt;prototype.js&lt;/code&gt; compressed with YUI compressor: 33K&lt;/li&gt;
&lt;/ul&gt;


&lt;h2&gt;Requirements&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;A java run time environment should be installed on the server.  If it is not available the asset_packager will still function but won't use the YUI compressor.&lt;/li&gt;
&lt;/ul&gt;


&lt;h2&gt;Installation&lt;/h2&gt;

&lt;p&gt;Until the official version includes the changes to support the YUI compressor you will have to use my fork.&lt;/p&gt;

&lt;p&gt;Install with&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;./script/plugin install git://github.com/eandrejko/asset_packager.git
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;Generate the &lt;code&gt;config/asset_packages.yml&lt;/code&gt; configuration file with&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;rake asset:packager:create_yml
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;Modify the configuration file to include the necessary Javascript and CSS files:&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;--- 
javascripts: 
- base: 
  - prototype
  - effects
  - dragdrop
  - controls
  - application
stylesheets: 
- base: 
  - reset
  - text
  - screen
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;In your layout file use:&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;&amp;lt;%= javascript_include_merged :base %&amp;gt;
&amp;lt;%= stylesheet_link_merged :base %&amp;gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;When your application is in the production environment all of the files will be automatically merged and compressed.&lt;/p&gt;

&lt;h2&gt;Check Javascript Files&lt;/h2&gt;

&lt;p&gt;To check your Javascript files for issues that might inhibit compression use the rake task&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;rake asset:packager:check
&lt;/code&gt;&lt;/pre&gt;

&lt;h2&gt;Use the Google version&lt;/h2&gt;

&lt;p&gt;Most browsers make at most two simultaneous connections to each web server.  If you place the following in your application layout file:&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;&amp;lt;script src="http://ajax.googleapis.com/ajax/libs/prototype/1.6.0.3/prototype.js" type="text/javascript" charset="utf-8"&amp;gt;&amp;lt;/script&amp;gt;
&amp;lt;%= javascript_include_merged :base %&amp;gt;
&amp;lt;%= stylesheet_link_merged :base %&amp;gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;The &lt;code&gt;prototype.js&lt;/code&gt; file will be loaded from Google and it may be loaded in parallel with your compressed Javascript files.  With any luck the user will already have it cached.  You will want to remove the &lt;code&gt;prototype&lt;/code&gt; from the  &lt;code&gt;config/asset_packages.yml&lt;/code&gt; file.&lt;/p&gt;
</summary>
    <content type="html">&lt;p&gt;One of the &lt;a href="/Improving-Rails-Application-Speed.html"&gt;many strategies for speeding up your Rails application&lt;/a&gt; is to &lt;strong&gt;serve exactly one CSS file&lt;/strong&gt; and &lt;strong&gt;exactly one Javascript file&lt;/strong&gt;.  It is often more convenient to have multiple files in development and only package these files into one file in production.  It doesn't hurt to also minify these files when in production mode.  This is exactly what Scott Becker's great &lt;code&gt;asset_packager&lt;/code&gt; &lt;a href="http://synthesis.sbecker.net/pages/asset_packager"&gt;original plugin&lt;/a&gt; is for.&lt;/p&gt;

&lt;h2&gt;Asset Packager Plugin with YUI Compressor&lt;/h2&gt;

&lt;p&gt;The standard &lt;code&gt;asset_packager&lt;/code&gt; is a great plugin.  It has a couple of issues:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;It has a bug: it mangles CSS with multiline comments.&lt;/li&gt;
&lt;li&gt;It uses a sub optimal Javascript and CSS compressor.&lt;/li&gt;
&lt;/ul&gt;


&lt;h3&gt;YUI Compressor&lt;/h3&gt;

&lt;p&gt;The &lt;a href="http://developer.yahoo.com/yui/compressor/"&gt;Yahoo YUI Compressor&lt;/a&gt; is "100% safe" and does a great of job of reducing both Javascript and CSS file size.&lt;/p&gt;

&lt;blockquote&gt;&lt;p&gt;The goal of JavaScript and CSS minification is always to preserve the operational qualities of the code while reducing its overall byte footprint (both in raw terms and after gzipping, as most JavaScript and CSS served from production web servers is gzipped as part of the HTTP protocol). The YUI Compressor is JavaScript minifier designed to be 100% safe and yield a higher compression ratio than most other tools. Tests on the YUI Library have shown savings of over 20% compared to JSMin (becoming 10% after HTTP compression).&lt;/p&gt;&lt;/blockquote&gt;

&lt;p&gt;I have modified the &lt;code&gt;asset_packager&lt;/code&gt; plugin to use the YUI compressor, if available, for both CSS and Javascript minification.&lt;/p&gt;

&lt;h3&gt;Compression&lt;/h3&gt;

&lt;p&gt;The YUI compressor provides some serious savings:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;code&gt;prototype.js&lt;/code&gt; uncompressed: 54K&lt;/li&gt;
&lt;li&gt;&lt;code&gt;prototype.js&lt;/code&gt; compressed with &lt;code&gt;jsmin.rb&lt;/code&gt; Ruby compressor: 41K&lt;/li&gt;
&lt;li&gt;&lt;code&gt;prototype.js&lt;/code&gt; compressed with YUI compressor: 33K&lt;/li&gt;
&lt;/ul&gt;


&lt;h2&gt;Requirements&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;A java run time environment should be installed on the server.  If it is not available the asset_packager will still function but won't use the YUI compressor.&lt;/li&gt;
&lt;/ul&gt;


&lt;h2&gt;Installation&lt;/h2&gt;

&lt;p&gt;Until the official version includes the changes to support the YUI compressor you will have to use my fork.&lt;/p&gt;

&lt;p&gt;Install with&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;./script/plugin install git://github.com/eandrejko/asset_packager.git
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;Generate the &lt;code&gt;config/asset_packages.yml&lt;/code&gt; configuration file with&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;rake asset:packager:create_yml
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;Modify the configuration file to include the necessary Javascript and CSS files:&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;--- 
javascripts: 
- base: 
  - prototype
  - effects
  - dragdrop
  - controls
  - application
stylesheets: 
- base: 
  - reset
  - text
  - screen
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;In your layout file use:&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;&amp;lt;%= javascript_include_merged :base %&amp;gt;
&amp;lt;%= stylesheet_link_merged :base %&amp;gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;When your application is in the production environment all of the files will be automatically merged and compressed.&lt;/p&gt;

&lt;h2&gt;Check Javascript Files&lt;/h2&gt;

&lt;p&gt;To check your Javascript files for issues that might inhibit compression use the rake task&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;rake asset:packager:check
&lt;/code&gt;&lt;/pre&gt;

&lt;h2&gt;Use the Google version&lt;/h2&gt;

&lt;p&gt;Most browsers make at most two simultaneous connections to each web server.  If you place the following in your application layout file:&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;&amp;lt;script src="http://ajax.googleapis.com/ajax/libs/prototype/1.6.0.3/prototype.js" type="text/javascript" charset="utf-8"&amp;gt;&amp;lt;/script&amp;gt;
&amp;lt;%= javascript_include_merged :base %&amp;gt;
&amp;lt;%= stylesheet_link_merged :base %&amp;gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;The &lt;code&gt;prototype.js&lt;/code&gt; file will be loaded from Google and it may be loaded in parallel with your compressed Javascript files.  With any luck the user will already have it cached.  You will want to remove the &lt;code&gt;prototype&lt;/code&gt; from the  &lt;code&gt;config/asset_packages.yml&lt;/code&gt; file.&lt;/p&gt;
</content>
    <author>
      <name>Erik Andrejko</name>
      <email>erik@railsillustrated.com</email>
    </author>
  </entry>
  <entry>
    <id>http://www.railsillustrated.com/class-based-logger-in-rails.html</id>
    <link type="text/html" href="http://www.railsillustrated.com/class-based-logger-in-rails.html" rel="alternate"/>
    <updated>2012-09-02T19:47:48Z</updated>
    <title>A Simple Class Based Logger in Rails</title>
    <summary type="html">&lt;p&gt;In a project I was working on there were several classes that were involved in a lot of background processing. In order to monitor the behavior of this background processing I wanted a separate log file for these classes.  The result: &lt;code&gt;class_logger&lt;/code&gt; is a simple plugin that creates a separated log file for an &lt;code&gt;ActiveRecord&lt;/code&gt; model.&lt;/p&gt;

&lt;h2&gt;Installation&lt;/h2&gt;

&lt;pre&gt;&lt;code&gt;./script/plugin install git://github.com/eandrejko/class_logger.git
&lt;/code&gt;&lt;/pre&gt;

&lt;h2&gt;Usage&lt;/h2&gt;

&lt;p&gt;In your model use&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;class Email &amp;lt; ActiveRecord::Base
    has_own_logger
end
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;which will by default create a log file &lt;code&gt;log/email.log&lt;/code&gt;.  To write to this log file use:&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;email = Email.create
email.log("sending")
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;or use the class method:&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;Email.class_logger("connecting to server")
&lt;/code&gt;&lt;/pre&gt;

&lt;h2&gt;Specifying the directory&lt;/h2&gt;

&lt;p&gt;If you want to keep the log file in another location specify the directory with:&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;class Email &amp;lt; ActiveRecord::Base
    has_own_logger :in =&amp;gt; 'log/emails'
end
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;which will create the log file in &lt;code&gt;log/emails/email.log&lt;/code&gt;.&lt;/p&gt;

&lt;h2&gt;Potential Applications&lt;/h2&gt;

&lt;p&gt;There are some advantages to having a separate log file.  You can log many more events without cluttering up your &lt;code&gt;production.log&lt;/code&gt;.  This is potentially helpful for&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;troubleshooting background processing in production&lt;/li&gt;
&lt;li&gt;recording and analyzing performance of long running processes in your application&lt;/li&gt;
&lt;/ul&gt;

</summary>
    <content type="html">&lt;p&gt;In a project I was working on there were several classes that were involved in a lot of background processing. In order to monitor the behavior of this background processing I wanted a separate log file for these classes.  The result: &lt;code&gt;class_logger&lt;/code&gt; is a simple plugin that creates a separated log file for an &lt;code&gt;ActiveRecord&lt;/code&gt; model.&lt;/p&gt;

&lt;h2&gt;Installation&lt;/h2&gt;

&lt;pre&gt;&lt;code&gt;./script/plugin install git://github.com/eandrejko/class_logger.git
&lt;/code&gt;&lt;/pre&gt;

&lt;h2&gt;Usage&lt;/h2&gt;

&lt;p&gt;In your model use&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;class Email &amp;lt; ActiveRecord::Base
    has_own_logger
end
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;which will by default create a log file &lt;code&gt;log/email.log&lt;/code&gt;.  To write to this log file use:&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;email = Email.create
email.log("sending")
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;or use the class method:&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;Email.class_logger("connecting to server")
&lt;/code&gt;&lt;/pre&gt;

&lt;h2&gt;Specifying the directory&lt;/h2&gt;

&lt;p&gt;If you want to keep the log file in another location specify the directory with:&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;class Email &amp;lt; ActiveRecord::Base
    has_own_logger :in =&amp;gt; 'log/emails'
end
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;which will create the log file in &lt;code&gt;log/emails/email.log&lt;/code&gt;.&lt;/p&gt;

&lt;h2&gt;Potential Applications&lt;/h2&gt;

&lt;p&gt;There are some advantages to having a separate log file.  You can log many more events without cluttering up your &lt;code&gt;production.log&lt;/code&gt;.  This is potentially helpful for&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;troubleshooting background processing in production&lt;/li&gt;
&lt;li&gt;recording and analyzing performance of long running processes in your application&lt;/li&gt;
&lt;/ul&gt;

</content>
    <author>
      <name>Erik Andrejko</name>
      <email>erik@railsillustrated.com</email>
    </author>
  </entry>
  <entry>
    <id>http://www.railsillustrated.com/screencast-hover-content-in-rails-prototype.html</id>
    <link type="text/html" href="http://www.railsillustrated.com/screencast-hover-content-in-rails-prototype.html" rel="alternate"/>
    <updated>2012-09-02T19:47:48Z</updated>
    <title>Screencast: Hover Content in Rails and Prototype  (Part 1)</title>
    <summary type="html">&lt;object width="530" height="400"&gt;&lt;param name="allowfullscreen" value="true" /&gt;&lt;param name="allowscriptaccess" value="always" /&gt;&lt;param name="movie" value="http://vimeo.com/moogaloop.swf?clip_id=2926137&amp;amp;server=vimeo.com&amp;amp;show_title=1&amp;amp;show_byline=1&amp;amp;show_portrait=0&amp;amp;color=00ADEF&amp;amp;fullscreen=1" /&gt;&lt;embed src="http://vimeo.com/moogaloop.swf?clip_id=2926137&amp;amp;server=vimeo.com&amp;amp;show_title=1&amp;amp;show_byline=1&amp;amp;show_portrait=0&amp;amp;color=00ADEF&amp;amp;fullscreen=1" type="application/x-shockwave-flash" allowfullscreen="true" allowscriptaccess="always" width="530" height="400"&gt;&lt;/embed&gt;&lt;/object&gt;


&lt;br /&gt;&lt;a href="http://vimeo.com/"&gt;Screencast: Hover Content in Rails and Prototype&lt;/a&gt; from &lt;a href="http://vimeo.com/user1001525"&gt;Erik Andrejko&lt;/a&gt; on &lt;a href="http://vimeo.com"&gt;Vimeo&lt;/a&gt;.


&lt;p&gt;It is often a good idea to show content when the user hovers the mouse above a link or an element in a page.  This technique has been &lt;a href="http://www.guardian.co.uk/technology/2007/feb/22/web20.internet"&gt;abused by some&lt;/a&gt;, but when used correctly it can enhance the user's experience.  This is particularly the case when you have a lot of information that may be relevant to the user's interests but is too much to display all at once.&lt;/p&gt;

&lt;h2&gt;Screencast&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;View a quicktime version: &lt;a href="/posts/screencast-hover-content.mov"&gt;full&lt;/a&gt;, &lt;a href="/posts/screencast-hover-content-iPhone.m4v"&gt;iPod&lt;/a&gt;.&lt;/li&gt;
&lt;li&gt;The &lt;a href="http://github.com/eandrejko/rails_illustrated_screencasts/"&gt;complete code&lt;/a&gt; can be downloaded from GitHub.&lt;/li&gt;
&lt;/ul&gt;


&lt;h2&gt;Required&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="http://www.danwebb.net/2006/9/3/low-pro-unobtrusive-scripting-for-prototype"&gt;LowPro&lt;/a&gt; for unobtrusive javascript for Prototype&lt;/li&gt;
&lt;/ul&gt;


&lt;h2&gt;Optional&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://addons.mozilla.org/en-US/firefox/addon/1843"&gt;Firebug&lt;/a&gt; for Javascript debugging&lt;/li&gt;
&lt;/ul&gt;


&lt;h2&gt;Basic Strategy&lt;/h2&gt;

&lt;p&gt;&lt;img src="/posts/hover-content-wrapper-div.jpg" alt="Hover Content Wrapper Div" /&gt;&lt;/p&gt;

&lt;p&gt;We want to place the content that appears when the mouse is hovered as near as possible to the element that generates the hover (the affordance).  We accomplish this by means of a wrapper &lt;code&gt;div&lt;/code&gt; around each row of contacts.&lt;/p&gt;

&lt;h2&gt;1. Controller&lt;/h2&gt;

&lt;p&gt;In the controller the contacts are grouped into groups of 4 so that they can be wrapped in a &lt;code&gt;div&lt;/code&gt;.&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;def index
  @contacts = Contact.find(:all, :order =&amp;gt; 'last_name asc, first_name asc')
  @contact_groups = @contacts.in_groups_of(4,false)
end

def show
  @contact = Contact.find(params[:id])
end
&lt;/code&gt;&lt;/pre&gt;

&lt;h2&gt;2. View&lt;/h2&gt;

&lt;p&gt;The trick is to wrap each each row of contacts with the wrapper &lt;code&gt;div&lt;/code&gt;.  This accomplished by the &lt;code&gt;contact_group&lt;/code&gt; partial.&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;&amp;lt;% content_for :scripts do %&amp;gt;
&amp;lt;%= javascript_include_tag 'hover_content' %&amp;gt;
&amp;lt;% end %&amp;gt;


&amp;lt;div id='contact-names'&amp;gt;
&amp;lt;%= render :partial =&amp;gt; 'contact_group', :collection =&amp;gt; @contact_groups %&amp;gt;
&amp;lt;/div&amp;gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;The &lt;code&gt;contact_group&lt;/code&gt; partial wraps the row in a &lt;code&gt;div&lt;/code&gt;.&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;&amp;lt;div class='contact-group'&amp;gt;
    &amp;lt;%= render :partial =&amp;gt; 'contact_name', :collection =&amp;gt; contact_group %&amp;gt;
&amp;lt;/div&amp;gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;h2&gt;3. Unobtrusive Javascript&lt;/h2&gt;

&lt;p&gt;The javascript is fairly straightforward.  When the &lt;code&gt;mouseover&lt;/code&gt; event is received, we load the content to display with an Ajax call.&lt;/p&gt;

&lt;h3&gt;Bubbling Event Problem&lt;/h3&gt;

&lt;p&gt;The &lt;code&gt;div&lt;/code&gt; that we watch for &lt;code&gt;mouseover&lt;/code&gt; events has child elements.  When any of those elements received a &lt;code&gt;mouseover&lt;/code&gt; event it will be sent to the parent elements.  This means that the &lt;code&gt;div&lt;/code&gt; will receive extraneous events.  We must ignore these extra events to provide smooth visual effects.&lt;/p&gt;

&lt;p&gt;The check for bubbling &lt;code&gt;mouseover&lt;/code&gt; events from child elements with Protoype:&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;// from http://groups.google.com/group/prototype-scriptaculous/browse_thread/thread/badf3974a0dd5ac6
function bubbledFromChild(element, event)  {
  var target = $(event).element();
  if (target === element) target = event.relatedTarget;
  return (target &amp;amp;&amp;amp; target.descendantOf(element));
}
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;This check is used on each function that handles either the &lt;code&gt;mouseover&lt;/code&gt; or &lt;code&gt;mouseout&lt;/code&gt; events.  The rest of the Javascript is fairly routine:&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;Event.addBehavior({
  ".contact-name:mouseover" : function(e){
    if(!bubbledFromChild(this,e)){
      $(this).setStyle({backgroundColor:"#f0f0f0"});
      var id = $(this).readAttribute("id").match(/[0-9]+$/)[0];
      var container = $(this).up(".contact-group");
      $$(".contact").each( function(e) {
        e.hide();
      });
      if($(container).down("#contact-"+id)){
        new Effect.Appear("contact-"+id, {queue: 'end', duration: 0.7})
      }else{
        if(Ajax.activeRequestCount == 0){
          var url = $(this).down("a").readAttribute("href");
          new Ajax.Request(url,{
            method: 'get',
            onSuccess: function(xhr){
              container.insert({bottom: xhr.responseText});
            }});
        }
      }      
    }
  },

  ".contact-name:mouseout" : function(e){
    if(!bubbledFromChild(this,e)){
      $$(".contact").each( function(e) {
        e.hide();
      });      
    }
  }
})
&lt;/code&gt;&lt;/pre&gt;

&lt;h2&gt;Optional Enhancements&lt;/h2&gt;

&lt;p&gt;Over the next couple of weeks we shall continue this screencast by enhancing this hover technique to improve usability.&lt;/p&gt;

&lt;h2&gt;Credits&lt;/h2&gt;

&lt;p&gt;Intro music thanks to &lt;a href="http://www.cwill1.com/"&gt;Courtney Williams&lt;/a&gt; via &lt;a href="http://www.podcastnyc.net/psm/podcast.html"&gt;Podcast NYC&lt;/a&gt;.&lt;/p&gt;
</summary>
    <content type="html">&lt;object width="530" height="400"&gt;&lt;param name="allowfullscreen" value="true" /&gt;&lt;param name="allowscriptaccess" value="always" /&gt;&lt;param name="movie" value="http://vimeo.com/moogaloop.swf?clip_id=2926137&amp;amp;server=vimeo.com&amp;amp;show_title=1&amp;amp;show_byline=1&amp;amp;show_portrait=0&amp;amp;color=00ADEF&amp;amp;fullscreen=1" /&gt;&lt;embed src="http://vimeo.com/moogaloop.swf?clip_id=2926137&amp;amp;server=vimeo.com&amp;amp;show_title=1&amp;amp;show_byline=1&amp;amp;show_portrait=0&amp;amp;color=00ADEF&amp;amp;fullscreen=1" type="application/x-shockwave-flash" allowfullscreen="true" allowscriptaccess="always" width="530" height="400"&gt;&lt;/embed&gt;&lt;/object&gt;


&lt;br /&gt;&lt;a href="http://vimeo.com/"&gt;Screencast: Hover Content in Rails and Prototype&lt;/a&gt; from &lt;a href="http://vimeo.com/user1001525"&gt;Erik Andrejko&lt;/a&gt; on &lt;a href="http://vimeo.com"&gt;Vimeo&lt;/a&gt;.


&lt;p&gt;It is often a good idea to show content when the user hovers the mouse above a link or an element in a page.  This technique has been &lt;a href="http://www.guardian.co.uk/technology/2007/feb/22/web20.internet"&gt;abused by some&lt;/a&gt;, but when used correctly it can enhance the user's experience.  This is particularly the case when you have a lot of information that may be relevant to the user's interests but is too much to display all at once.&lt;/p&gt;

&lt;h2&gt;Screencast&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;View a quicktime version: &lt;a href="/posts/screencast-hover-content.mov"&gt;full&lt;/a&gt;, &lt;a href="/posts/screencast-hover-content-iPhone.m4v"&gt;iPod&lt;/a&gt;.&lt;/li&gt;
&lt;li&gt;The &lt;a href="http://github.com/eandrejko/rails_illustrated_screencasts/"&gt;complete code&lt;/a&gt; can be downloaded from GitHub.&lt;/li&gt;
&lt;/ul&gt;


&lt;h2&gt;Required&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="http://www.danwebb.net/2006/9/3/low-pro-unobtrusive-scripting-for-prototype"&gt;LowPro&lt;/a&gt; for unobtrusive javascript for Prototype&lt;/li&gt;
&lt;/ul&gt;


&lt;h2&gt;Optional&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://addons.mozilla.org/en-US/firefox/addon/1843"&gt;Firebug&lt;/a&gt; for Javascript debugging&lt;/li&gt;
&lt;/ul&gt;


&lt;h2&gt;Basic Strategy&lt;/h2&gt;

&lt;p&gt;&lt;img src="/posts/hover-content-wrapper-div.jpg" alt="Hover Content Wrapper Div" /&gt;&lt;/p&gt;

&lt;p&gt;We want to place the content that appears when the mouse is hovered as near as possible to the element that generates the hover (the affordance).  We accomplish this by means of a wrapper &lt;code&gt;div&lt;/code&gt; around each row of contacts.&lt;/p&gt;

&lt;h2&gt;1. Controller&lt;/h2&gt;

&lt;p&gt;In the controller the contacts are grouped into groups of 4 so that they can be wrapped in a &lt;code&gt;div&lt;/code&gt;.&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;def index
  @contacts = Contact.find(:all, :order =&amp;gt; 'last_name asc, first_name asc')
  @contact_groups = @contacts.in_groups_of(4,false)
end

def show
  @contact = Contact.find(params[:id])
end
&lt;/code&gt;&lt;/pre&gt;

&lt;h2&gt;2. View&lt;/h2&gt;

&lt;p&gt;The trick is to wrap each each row of contacts with the wrapper &lt;code&gt;div&lt;/code&gt;.  This accomplished by the &lt;code&gt;contact_group&lt;/code&gt; partial.&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;&amp;lt;% content_for :scripts do %&amp;gt;
&amp;lt;%= javascript_include_tag 'hover_content' %&amp;gt;
&amp;lt;% end %&amp;gt;


&amp;lt;div id='contact-names'&amp;gt;
&amp;lt;%= render :partial =&amp;gt; 'contact_group', :collection =&amp;gt; @contact_groups %&amp;gt;
&amp;lt;/div&amp;gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;The &lt;code&gt;contact_group&lt;/code&gt; partial wraps the row in a &lt;code&gt;div&lt;/code&gt;.&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;&amp;lt;div class='contact-group'&amp;gt;
    &amp;lt;%= render :partial =&amp;gt; 'contact_name', :collection =&amp;gt; contact_group %&amp;gt;
&amp;lt;/div&amp;gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;h2&gt;3. Unobtrusive Javascript&lt;/h2&gt;

&lt;p&gt;The javascript is fairly straightforward.  When the &lt;code&gt;mouseover&lt;/code&gt; event is received, we load the content to display with an Ajax call.&lt;/p&gt;

&lt;h3&gt;Bubbling Event Problem&lt;/h3&gt;

&lt;p&gt;The &lt;code&gt;div&lt;/code&gt; that we watch for &lt;code&gt;mouseover&lt;/code&gt; events has child elements.  When any of those elements received a &lt;code&gt;mouseover&lt;/code&gt; event it will be sent to the parent elements.  This means that the &lt;code&gt;div&lt;/code&gt; will receive extraneous events.  We must ignore these extra events to provide smooth visual effects.&lt;/p&gt;

&lt;p&gt;The check for bubbling &lt;code&gt;mouseover&lt;/code&gt; events from child elements with Protoype:&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;// from http://groups.google.com/group/prototype-scriptaculous/browse_thread/thread/badf3974a0dd5ac6
function bubbledFromChild(element, event)  {
  var target = $(event).element();
  if (target === element) target = event.relatedTarget;
  return (target &amp;amp;&amp;amp; target.descendantOf(element));
}
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;This check is used on each function that handles either the &lt;code&gt;mouseover&lt;/code&gt; or &lt;code&gt;mouseout&lt;/code&gt; events.  The rest of the Javascript is fairly routine:&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;Event.addBehavior({
  ".contact-name:mouseover" : function(e){
    if(!bubbledFromChild(this,e)){
      $(this).setStyle({backgroundColor:"#f0f0f0"});
      var id = $(this).readAttribute("id").match(/[0-9]+$/)[0];
      var container = $(this).up(".contact-group");
      $$(".contact").each( function(e) {
        e.hide();
      });
      if($(container).down("#contact-"+id)){
        new Effect.Appear("contact-"+id, {queue: 'end', duration: 0.7})
      }else{
        if(Ajax.activeRequestCount == 0){
          var url = $(this).down("a").readAttribute("href");
          new Ajax.Request(url,{
            method: 'get',
            onSuccess: function(xhr){
              container.insert({bottom: xhr.responseText});
            }});
        }
      }      
    }
  },

  ".contact-name:mouseout" : function(e){
    if(!bubbledFromChild(this,e)){
      $$(".contact").each( function(e) {
        e.hide();
      });      
    }
  }
})
&lt;/code&gt;&lt;/pre&gt;

&lt;h2&gt;Optional Enhancements&lt;/h2&gt;

&lt;p&gt;Over the next couple of weeks we shall continue this screencast by enhancing this hover technique to improve usability.&lt;/p&gt;

&lt;h2&gt;Credits&lt;/h2&gt;

&lt;p&gt;Intro music thanks to &lt;a href="http://www.cwill1.com/"&gt;Courtney Williams&lt;/a&gt; via &lt;a href="http://www.podcastnyc.net/psm/podcast.html"&gt;Podcast NYC&lt;/a&gt;.&lt;/p&gt;
</content>
    <author>
      <name>Erik Andrejko</name>
      <email>erik@railsillustrated.com</email>
    </author>
  </entry>
  <entry>
    <id>http://www.railsillustrated.com/user-testing-exposes-hidden-assumptions.html</id>
    <link type="text/html" href="http://www.railsillustrated.com/user-testing-exposes-hidden-assumptions.html" rel="alternate"/>
    <updated>2012-09-02T19:47:48Z</updated>
    <title>User Testing Exposes Hidden Assumptions</title>
    <summary type="html">&lt;p&gt;Design is all about assumptions.  If you don't assume anything how can you get started?  Good design means getting those assumptions correct.  Often times these assumptions will be wrong, and many of those assumptions will be hidden: &lt;strong&gt;the designer doesn't realize the assumption is being made&lt;/strong&gt;.  This is not a problem if the assumption is correct, but becomes an unknown problem if the assumption is incorrect.&lt;/p&gt;

&lt;h2&gt;Identify Assumptions with User Testing&lt;/h2&gt;

&lt;p&gt;One of the best ways to expose those hidden assumptions is through user testing.
You can't fix something you don't know is broken.  That is what makes User Testing so powerful.&lt;/p&gt;

&lt;p&gt;To see an example of how user testing can expose hidden assumptions watch the screencast "&lt;a href="http://css-tricks.com/video-screencasts/51-a-lesson-learned-in-accessibility/"&gt;A Lesson Learned in Accessibility&lt;/a&gt;" at &lt;a href="http://css-tricks.com"&gt;CSS Tricks&lt;/a&gt;.  If you are like me you would have assumed the same things about how screen readers work and how visually impaired users will use your site.&lt;/p&gt;

&lt;h2&gt;Some Hidden Assumptions&lt;/h2&gt;

&lt;p&gt;Here are some assumptions that I have unknowingly made in the past.  Some of these are pretty obvious usability issues. Some of them are more subtle and were made very obvious during user testing.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;My users mostly use this version of Browser X.&lt;/li&gt;
&lt;li&gt;The input validation is sufficient.&lt;/li&gt;
&lt;li&gt;My users have flash installed.&lt;/li&gt;
&lt;li&gt;This javascript will run fast enough.&lt;/li&gt;
&lt;li&gt;My users have javascript enabled.&lt;/li&gt;
&lt;li&gt;The order of my html doesn't matter.&lt;/li&gt;
&lt;li&gt;My users will read this block of text.&lt;/li&gt;
&lt;li&gt;This functionality meets the needs of my users.&lt;/li&gt;
&lt;li&gt;My users have this purpose when visiting my site/using my product.&lt;/li&gt;
&lt;li&gt;This feature is important to my users.&lt;/li&gt;
&lt;li&gt;My visually impaired users will have javascript disabled.&lt;/li&gt;
&lt;li&gt;This interface element is clearly understandable.&lt;/li&gt;
&lt;li&gt;My users will scroll to the bottom of this page.&lt;/li&gt;
&lt;li&gt;The information architecture of this site is correct.&lt;/li&gt;
&lt;li&gt;My users will fill out these fields in this order&lt;/li&gt;
&lt;li&gt;The state of the application is clearly indicated.&lt;/li&gt;
&lt;li&gt;My users will navigate the page using a mouse.&lt;/li&gt;
&lt;li&gt;The application will be used generally in the way it was intended to be used.&lt;/li&gt;
&lt;li&gt;My users don't need this feature.&lt;/li&gt;
&lt;li&gt;This affordance is recognizable.&lt;/li&gt;
&lt;/ul&gt;


&lt;h2&gt;The Biggest Assumption of All&lt;/h2&gt;

&lt;p&gt;The biggest assumption is the &lt;strong&gt;meta-assumption: I know what I am assuming and am correct about those assumptions&lt;/strong&gt;.  No matter how many usability tests you do or how much experience you have there is always another assumption you won't realize you have made.  That is, until it becomes very obvious during user testing.&lt;/p&gt;

&lt;h2&gt;What are your hidden assumptions?&lt;/h2&gt;

&lt;p&gt;What assumptions have you made and how were they made obvious?  Please share your stories in the comments below.&lt;/p&gt;
</summary>
    <content type="html">&lt;p&gt;Design is all about assumptions.  If you don't assume anything how can you get started?  Good design means getting those assumptions correct.  Often times these assumptions will be wrong, and many of those assumptions will be hidden: &lt;strong&gt;the designer doesn't realize the assumption is being made&lt;/strong&gt;.  This is not a problem if the assumption is correct, but becomes an unknown problem if the assumption is incorrect.&lt;/p&gt;

&lt;h2&gt;Identify Assumptions with User Testing&lt;/h2&gt;

&lt;p&gt;One of the best ways to expose those hidden assumptions is through user testing.
You can't fix something you don't know is broken.  That is what makes User Testing so powerful.&lt;/p&gt;

&lt;p&gt;To see an example of how user testing can expose hidden assumptions watch the screencast "&lt;a href="http://css-tricks.com/video-screencasts/51-a-lesson-learned-in-accessibility/"&gt;A Lesson Learned in Accessibility&lt;/a&gt;" at &lt;a href="http://css-tricks.com"&gt;CSS Tricks&lt;/a&gt;.  If you are like me you would have assumed the same things about how screen readers work and how visually impaired users will use your site.&lt;/p&gt;

&lt;h2&gt;Some Hidden Assumptions&lt;/h2&gt;

&lt;p&gt;Here are some assumptions that I have unknowingly made in the past.  Some of these are pretty obvious usability issues. Some of them are more subtle and were made very obvious during user testing.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;My users mostly use this version of Browser X.&lt;/li&gt;
&lt;li&gt;The input validation is sufficient.&lt;/li&gt;
&lt;li&gt;My users have flash installed.&lt;/li&gt;
&lt;li&gt;This javascript will run fast enough.&lt;/li&gt;
&lt;li&gt;My users have javascript enabled.&lt;/li&gt;
&lt;li&gt;The order of my html doesn't matter.&lt;/li&gt;
&lt;li&gt;My users will read this block of text.&lt;/li&gt;
&lt;li&gt;This functionality meets the needs of my users.&lt;/li&gt;
&lt;li&gt;My users have this purpose when visiting my site/using my product.&lt;/li&gt;
&lt;li&gt;This feature is important to my users.&lt;/li&gt;
&lt;li&gt;My visually impaired users will have javascript disabled.&lt;/li&gt;
&lt;li&gt;This interface element is clearly understandable.&lt;/li&gt;
&lt;li&gt;My users will scroll to the bottom of this page.&lt;/li&gt;
&lt;li&gt;The information architecture of this site is correct.&lt;/li&gt;
&lt;li&gt;My users will fill out these fields in this order&lt;/li&gt;
&lt;li&gt;The state of the application is clearly indicated.&lt;/li&gt;
&lt;li&gt;My users will navigate the page using a mouse.&lt;/li&gt;
&lt;li&gt;The application will be used generally in the way it was intended to be used.&lt;/li&gt;
&lt;li&gt;My users don't need this feature.&lt;/li&gt;
&lt;li&gt;This affordance is recognizable.&lt;/li&gt;
&lt;/ul&gt;


&lt;h2&gt;The Biggest Assumption of All&lt;/h2&gt;

&lt;p&gt;The biggest assumption is the &lt;strong&gt;meta-assumption: I know what I am assuming and am correct about those assumptions&lt;/strong&gt;.  No matter how many usability tests you do or how much experience you have there is always another assumption you won't realize you have made.  That is, until it becomes very obvious during user testing.&lt;/p&gt;

&lt;h2&gt;What are your hidden assumptions?&lt;/h2&gt;

&lt;p&gt;What assumptions have you made and how were they made obvious?  Please share your stories in the comments below.&lt;/p&gt;
</content>
    <author>
      <name>Erik Andrejko</name>
      <email>erik@railsillustrated.com</email>
    </author>
  </entry>
  <entry>
    <id>http://www.railsillustrated.com/blazing-fast-sinatra-with-memcached.html</id>
    <link type="text/html" href="http://www.railsillustrated.com/blazing-fast-sinatra-with-memcached.html" rel="alternate"/>
    <updated>2012-09-02T19:47:48Z</updated>
    <title>Blazing Fast Speeds with Sinatra and Memcached</title>
    <summary type="html">&lt;p&gt;&lt;a href="http://sinatra.rubyforge.org/"&gt;Sinatra&lt;/a&gt; is a wonderful small Ruby web framework built on top of the &lt;a href="http://rack.rubyforge.org/"&gt;Rack interface&lt;/a&gt;.  Sinatra is very fast out of the box making it perfect for light weight tasks.  Sinatra does not come with support for memcached out of the box but adding support for memcached is fairly straightforward.&lt;/p&gt;

&lt;h2&gt;Requirements&lt;/h2&gt;

&lt;ol&gt;
&lt;li&gt;&lt;p&gt;Download the file &lt;a href="/posts/cache.rb"&gt;&lt;code&gt;cache.rb&lt;/code&gt;&lt;/a&gt;.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;You will need to have installed memcached and also the &lt;code&gt;memcache-client&lt;/code&gt; gem:&lt;/p&gt;&lt;/li&gt;
&lt;/ol&gt;


&lt;pre&gt;
&lt;code class='bash'&gt;sudo gem install memcache-client
&lt;/code&gt; 
&lt;/pre&gt;


&lt;h2&gt;Installation&lt;/h2&gt;

&lt;p&gt;At the top of your Sinatra application should be&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;require 'sinatra'    
require 'cache'    

CONFIG['memcached'] = 'localhost:11211'

# add caching to Sinatra
class Sinatra::Event
  include CacheableEvent
end
&lt;/code&gt;&lt;/pre&gt;

&lt;h2&gt;Usage&lt;/h2&gt;

&lt;p&gt;A standard Sinatra controller method looks like this:&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;get '/:name' do
    ...
    erb 'name'
end
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;To add caching change to:&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;get '/:name', :cache_key =&amp;gt; Name.cache_key do
    ...
    erb 'name'
end
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;You will want &lt;code&gt;Name.cache_key&lt;/code&gt; to return a string and to change whenever the content returned by &lt;code&gt;name.html.erb&lt;/code&gt; view will change.  You do not have to invalidate any caches directly.  Furthermore the computed &lt;code&gt;cache_key&lt;/code&gt; will automatically include details of the parameters of the request.&lt;/p&gt;

&lt;p&gt;If you want to cache anything else inside your application use:&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;result = Sinatra::Cache.cache(cache_key) do
    this_is_an_expensive_method
end
&lt;/code&gt;&lt;/pre&gt;

&lt;h2&gt;How it Works&lt;/h2&gt;

&lt;p&gt;There are essentially two modification that must be made. Override the way Sinatra stores blocks that make up the controller methods and also to override the way Sinatra calls those stored blocks.&lt;/p&gt;

&lt;h3&gt;Overriding the block storage&lt;/h3&gt;

&lt;p&gt;When you declare a controller method in your application file like&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;get '/:name' do
    ...
    erb 'name'
end
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;Sinatra stores the block provided and saves it to be executed when responding to a request.  The caching mechanism replaces this block with another block. This replacement block uses the caching mechanism.  The &lt;code&gt;cache_key&lt;/code&gt; and original block are saved by means of &lt;code&gt;Proc.new&lt;/code&gt; which creates a closure and allows the block to access these values at the time of execution.&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;def _invoke_with_caching(*args)
  if options[:cache_key]
    # replace the block with another block that can be cached
    def wrap_block(key,block)
      Proc.new do
        Sinatra::Cache.cache(key + "/" + params.to_a.join("/")) { instance_eval(&amp;amp;block) }
      end
    end
    @block = wrap_block(options[:cache_key], block)
  end
  _invoke_without_caching(*args)
end
&lt;/code&gt;&lt;/pre&gt;

&lt;h2&gt;How fast is it?&lt;/h2&gt;

&lt;p&gt;This depends on how long it takes do generate your &lt;code&gt;cache_key&lt;/code&gt;.  There is also additional overhead in accessing memcached.  In my case it usually takes about 15 milliseconds to respond from the cache.&lt;/p&gt;

&lt;h3&gt;It doesn't matter&lt;/h3&gt;

&lt;p&gt;However it doesn't matter that much.  You web application speed usually doesn't depend on how fast your application responds, especially when the response time is less than 150 milliseconds.  To understand why see &lt;a href="/Improving-Rails-Application-Speed.html"&gt;this explanation of where your application is really slow&lt;/a&gt;.&lt;/p&gt;
</summary>
    <content type="html">&lt;p&gt;&lt;a href="http://sinatra.rubyforge.org/"&gt;Sinatra&lt;/a&gt; is a wonderful small Ruby web framework built on top of the &lt;a href="http://rack.rubyforge.org/"&gt;Rack interface&lt;/a&gt;.  Sinatra is very fast out of the box making it perfect for light weight tasks.  Sinatra does not come with support for memcached out of the box but adding support for memcached is fairly straightforward.&lt;/p&gt;

&lt;h2&gt;Requirements&lt;/h2&gt;

&lt;ol&gt;
&lt;li&gt;&lt;p&gt;Download the file &lt;a href="/posts/cache.rb"&gt;&lt;code&gt;cache.rb&lt;/code&gt;&lt;/a&gt;.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;You will need to have installed memcached and also the &lt;code&gt;memcache-client&lt;/code&gt; gem:&lt;/p&gt;&lt;/li&gt;
&lt;/ol&gt;


&lt;pre&gt;
&lt;code class='bash'&gt;sudo gem install memcache-client
&lt;/code&gt; 
&lt;/pre&gt;


&lt;h2&gt;Installation&lt;/h2&gt;

&lt;p&gt;At the top of your Sinatra application should be&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;require 'sinatra'    
require 'cache'    

CONFIG['memcached'] = 'localhost:11211'

# add caching to Sinatra
class Sinatra::Event
  include CacheableEvent
end
&lt;/code&gt;&lt;/pre&gt;

&lt;h2&gt;Usage&lt;/h2&gt;

&lt;p&gt;A standard Sinatra controller method looks like this:&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;get '/:name' do
    ...
    erb 'name'
end
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;To add caching change to:&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;get '/:name', :cache_key =&amp;gt; Name.cache_key do
    ...
    erb 'name'
end
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;You will want &lt;code&gt;Name.cache_key&lt;/code&gt; to return a string and to change whenever the content returned by &lt;code&gt;name.html.erb&lt;/code&gt; view will change.  You do not have to invalidate any caches directly.  Furthermore the computed &lt;code&gt;cache_key&lt;/code&gt; will automatically include details of the parameters of the request.&lt;/p&gt;

&lt;p&gt;If you want to cache anything else inside your application use:&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;result = Sinatra::Cache.cache(cache_key) do
    this_is_an_expensive_method
end
&lt;/code&gt;&lt;/pre&gt;

&lt;h2&gt;How it Works&lt;/h2&gt;

&lt;p&gt;There are essentially two modification that must be made. Override the way Sinatra stores blocks that make up the controller methods and also to override the way Sinatra calls those stored blocks.&lt;/p&gt;

&lt;h3&gt;Overriding the block storage&lt;/h3&gt;

&lt;p&gt;When you declare a controller method in your application file like&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;get '/:name' do
    ...
    erb 'name'
end
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;Sinatra stores the block provided and saves it to be executed when responding to a request.  The caching mechanism replaces this block with another block. This replacement block uses the caching mechanism.  The &lt;code&gt;cache_key&lt;/code&gt; and original block are saved by means of &lt;code&gt;Proc.new&lt;/code&gt; which creates a closure and allows the block to access these values at the time of execution.&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;def _invoke_with_caching(*args)
  if options[:cache_key]
    # replace the block with another block that can be cached
    def wrap_block(key,block)
      Proc.new do
        Sinatra::Cache.cache(key + "/" + params.to_a.join("/")) { instance_eval(&amp;amp;block) }
      end
    end
    @block = wrap_block(options[:cache_key], block)
  end
  _invoke_without_caching(*args)
end
&lt;/code&gt;&lt;/pre&gt;

&lt;h2&gt;How fast is it?&lt;/h2&gt;

&lt;p&gt;This depends on how long it takes do generate your &lt;code&gt;cache_key&lt;/code&gt;.  There is also additional overhead in accessing memcached.  In my case it usually takes about 15 milliseconds to respond from the cache.&lt;/p&gt;

&lt;h3&gt;It doesn't matter&lt;/h3&gt;

&lt;p&gt;However it doesn't matter that much.  You web application speed usually doesn't depend on how fast your application responds, especially when the response time is less than 150 milliseconds.  To understand why see &lt;a href="/Improving-Rails-Application-Speed.html"&gt;this explanation of where your application is really slow&lt;/a&gt;.&lt;/p&gt;
</content>
    <author>
      <name>Erik Andrejko</name>
      <email>erik@railsillustrated.com</email>
    </author>
  </entry>
</feed>
