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

<channel>
	<title>Coffee Powered &#187; Ruby</title>
	<atom:link href="http://www.coffeepowered.net/category/ruby/feed/" rel="self" type="application/rss+xml" />
	<link>http://www.coffeepowered.net</link>
	<description>code and content</description>
	<lastBuildDate>Mon, 09 Jan 2012 18:32:06 +0000</lastBuildDate>
	<language>en</language>
	<sy:updatePeriod>hourly</sy:updatePeriod>
	<sy:updateFrequency>1</sy:updateFrequency>
	<generator>http://wordpress.org/?v=3.3</generator>
		<item>
		<title>Don&#8217;t use strip_tags.</title>
		<link>http://www.coffeepowered.net/2012/01/08/dont-use-strip_tags/</link>
		<comments>http://www.coffeepowered.net/2012/01/08/dont-use-strip_tags/#comments</comments>
		<pubDate>Sun, 08 Jan 2012 22:14:50 +0000</pubDate>
		<dc:creator>Chris Heald</dc:creator>
				<category><![CDATA[Performance]]></category>
		<category><![CDATA[Rails]]></category>
		<category><![CDATA[Ruby]]></category>

		<guid isPermaLink="false">http://www.coffeepowered.net/?p=503</guid>
		<description><![CDATA[I ran into a rather surprising little problem earlier this week that I felt bore documenting. It turns out that the &#8220;simple&#8221; Rails strip_tags helper is massive overkill when you just want to strip markup out of a document. It offers a lot of functionality, but it comes at a pretty ugly performance cost. Here&#8217;s [...]]]></description>
			<content:encoded><![CDATA[<p>I ran into a rather surprising little problem earlier this week that I felt bore documenting. It turns out that the &#8220;simple&#8221; Rails <a href="http://api.rubyonrails.org/classes/ActionView/Helpers/SanitizeHelper.html#method-i-strip_tags">strip_tags</a> helper is <i>massive</i> overkill when you just want to strip markup out of a document. It offers a lot of functionality, but it comes at a pretty ugly performance cost.</p>
<p>Here&#8217;s the call graph for <code>#strip_tags</code> (as profiled in an application I&#8217;m working on). As you can see, it tokenizes the entire string, and then iterates the tokens, likely pushing and popping sections onto and off of a stack as tags are opened and closed.</p>
<p><a href="http://www.coffeepowered.net/wp-content/uploads/2012/01/strip_tags_call_graph.png"><img src="http://www.coffeepowered.net/wp-content/uploads/2012/01/strip_tags_call_graph.png" alt="" title="strip_tags_call_graph" width="100%" class="aligncenter size-medium wp-image-504" /></a></p>
<p>This is a lot more than a quick little regex to strip out tags; it&#8217;s actually parsing the full HTML document. Fortunately, there are already tools to do that, and they have their slow parts written as C extensions. <a href="http://nokogiri.org/">Nokogiri</a> is my weapon of choice in this regard &#8211; it&#8217;s battle-tested and generally rocks at parsing markup, even when it&#8217;s poorly-formed.</p>
<p>So, let&#8217;s benchmark a &#8220;strip all the markup out of a string&#8221; use case with <code>#strip_tags</code> and nokogiri.</p>
<pre class="brush: ruby; title: ; notranslate">
require 'rubygems'
require 'action_view'
require 'nokogiri'

include ActionView::Helpers::SanitizeHelper

f = open(&quot;news&quot;).read

LOOPS = 1000
Benchmark.bmbm do |x|
  x.report(&quot;strip_tags&quot;) { LOOPS.times { strip_tags f }}
  x.report(&quot;nokogiri&quot;) { LOOPS.times { Nokogiri::HTML(f).text }}
end
</pre>
<p>The data file in this case is a snapshot of the current page of <a href="http://news.ycombinator.com/">Hacker News</a>. It&#8217;s a 23kb HTML file. Nothing too huge, but certainly not small, either. Let&#8217;s run it through the benchmark:</p>
<pre class="brush: bash; title: ; notranslate">
[chris@luna projects]$ ruby strip.rb
Rehearsal ----------------------------------------------
strip_tags  33.070000   0.010000  33.080000 ( 33.092638)
nokogiri     3.220000   0.020000   3.240000 (  3.241090)
------------------------------------ total: 36.320000sec

                 user     system      total        real
strip_tags  33.010000   0.010000  33.020000 ( 33.056551)
nokogiri     3.190000   0.000000   3.190000 (  3.200680)
</pre>
<p>Yikes. It&#8217;s not just slower, it&#8217;s ~10x slower.</p>
<p>Don&#8217;t use <code>strip_tags</code>. Also, profile your code. But just because it&#8217;s convenient doesn&#8217;t mean you should use it.</p>
]]></content:encoded>
			<wfw:commentRss>http://www.coffeepowered.net/2012/01/08/dont-use-strip_tags/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>Rails Cookie Sessions and PHP</title>
		<link>http://www.coffeepowered.net/2011/08/24/rails-cookie-sessions-and-php/</link>
		<comments>http://www.coffeepowered.net/2011/08/24/rails-cookie-sessions-and-php/#comments</comments>
		<pubDate>Wed, 24 Aug 2011 23:56:25 +0000</pubDate>
		<dc:creator>Chris Heald</dc:creator>
				<category><![CDATA[Rails]]></category>
		<category><![CDATA[Ruby]]></category>
		<category><![CDATA[PHP]]></category>
		<category><![CDATA[yaml]]></category>

		<guid isPermaLink="false">http://www.coffeepowered.net/?p=470</guid>
		<description><![CDATA[I recently found myself needing to share session data from my Rails app with a PHP app on the same domain. We use cookie sessions for a number of reasons, and while they work great, the data stored in them is stored in Ruby&#8217;s native Marshal format, which is not trivial to reimplement in PHP. [...]]]></description>
			<content:encoded><![CDATA[<p>I recently found myself needing to share session data from my Rails app with a PHP app on the same domain. We use cookie sessions for a number of reasons, and while they work great, the data stored in them is stored in Ruby&#8217;s native Marshal format, which is not trivial to reimplement in PHP. After trying to get the data unmarshaled for a bit, I had another idea &#8211; why not just change the storage format?</p>
<p>Fortunately, Ruby is deeply entangled with another more portable serialization format: YAML.</p>
<p>Rails manages its session cookies through the <a href="https://github.com/rails/rails/blob/2-3-stable/activesupport/lib/active_support/message_verifier.rb">MessageVerifier</a>. Easy enough &#8211; we can just write our own MessageVerifier that uses YAML rather than Marshal.</p>
<pre class="brush: ruby; title: ; notranslate">

module ActiveSupport
  class YamlMessageVerifier &lt; MessageVerifier
    def verify(signed_message)
      raise InvalidSignature if signed_message.blank?

      data, digest = signed_message.split(&quot;--&quot;)
      if data.present? &amp;&amp; digest.present? &amp;&amp; secure_compare(digest, generate_digest(data))
        str = ActiveSupport::Base64.decode64(data)
        if str[0..2] == '---'
          YAML::load str
        else # Handle old Marshal.dump'd session
          Marshal.load(str)
        end
      else
        raise InvalidSignature
      end
    end

    def generate(value)
      data = ActiveSupport::Base64.encode64s(YAML::dump value)
      &quot;#{data}--#{generate_digest(data)}&quot;
    end
  end
end
</pre>
<p>You&#8217;ll notice that verify() can accept a Marshaled session as well; this lets you transparently transition existing cookies to the new format without any kind of session breakage. Nice.</p>
<p>Now, to use the verifier, we monkeypatch CookieStore:</p>
<pre class="brush: ruby; title: ; notranslate">
module ActionController
  module Session
    class CookieStore
      def verifier_for(secret, digest)
        key = secret.respond_to?(:call) ? secret.call : secret
        ActiveSupport::YamlMessageVerifier.new(key, digest)
      end
    end
  end
end
</pre>
<p>Now, this will work&#8230;at least at first glance, until you try to use the flash. This is a particularly nasty little problem, and it stems from the fact that Ruby&#8217;s YAML implementation serializes Hash objects without their instance variables, and <a href="https://github.com/rails/rails/blob/2-3-stable/actionpack/lib/action_controller/flash.rb">FlashHash</a> inherits from Hash, and thus inherits its serialization/deserialization strategy. I worked for a while to monkeypatch those strategies, but I didn&#8217;t like the result, and it felt a little hacky. Instead, I just took advantage of the YAML load lifecycle to make sure the FlashHash initializes properly:</p>
<pre class="brush: ruby; title: ; notranslate">
module ActionController
  module Flash
    class FlashHash
      def update_with_initializer(h)
        @used ||= {}
        update_without_initializer(h)
      end
      alias_method_chain :update, :initializer
    end
  end
end
</pre>
<p>The core problem is that <code>YAML::load</code> calls <code>Hash#update</code>, and <code>FlashHash</code> presumes that the <code>@used</code> instance variable is present and initialized to an empty hash. To fix that, I just aliased in an initializer to make sure that variable is set.</p>
<p>Note that if you are storing other Hash subclasses with instance variables that rely on those variables being persisted across sessions, they will break. However, you should only be storing primitive/array/hash data in the session if possible. FlashHash is sort of a nasty violation of this principle.</p>
<p>At this point, your session should be serializing to and from YAML. We&#8217;ll want to read it from PHP, naturally. I&#8217;m using <a href="http://code.google.com/p/spyc/">SPYC</a> in the PHP project, which gets us Close Enough(TM). It doesn&#8217;t handle symbol keys, but we&#8217;ll handle those in the PHP itself.</p>
<h2>Reading from PHP</h2>
<p>Reading the data back out is surprisingly simple. We have to verify the authenticity of the data, of course, by checking the hash, but then you just base64 decode the data, load it with spyc, and perform some simple transformation to turn symbols into strings. If you wanted to make it even easier, you could monkeypatch the cookie store to call <code>#stringify_keys!</code> on your session hash before serializing it (and then call <code>#with_indifferent_access</code> on the hash when you deserialize it. Be aware of the speed impact of such a decision before you do it.)</p>
<pre class="brush: php; title: ; notranslate">
function explode_symbols($arr) {
  $result = array();
  foreach($arr as $key =&gt; $val) {
    if(is_numeric($key) &amp;&amp; $val[0] == &quot;:&quot;) {
      $bits = explode(&quot;:&quot;, $val, 3);
      $result[trim($bits[1])] = trim($bits[2]);
    } elseif (is_array($val)) {
      $result[$key] = explode_symbols($val);
    } else {
      $result[$key] = $val;
    }
  }
  return $result;
}

function deserialize_session($session_key, $secret) {
  list($session64, $hash) = explode(&quot;--&quot;, $_COOKIE[$session_key], 2);
  if(hash_hmac(&quot;SHA1&quot;, $session64, $secret) == $hash) {
    $session = base64_decode($session64);
    return explode_symbols(spyc_load($session));
  } else {
    throw new Exception(&quot;Invalid session signature&quot;);
  }
}

$rails_session = deserialize_session(&quot;your_session_cookie_name&quot;, $your_session_cookie_secret);
</pre>
<h2>Caveats</h2>
<ul>
<li>Be aware that YAML is slower than Marshal</li>
<li>Be aware that storing Hash subclasses in the session is likely going to Not Work.</li>
</ul>
<p>And that&#8217;s all there is to it. You can now share data between the two apps via the session cookie.</p>
]]></content:encoded>
			<wfw:commentRss>http://www.coffeepowered.net/2011/08/24/rails-cookie-sessions-and-php/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>MongoDB, count() and the big O</title>
		<link>http://www.coffeepowered.net/2011/07/17/mongodb-count-and-the-big-o/</link>
		<comments>http://www.coffeepowered.net/2011/07/17/mongodb-count-and-the-big-o/#comments</comments>
		<pubDate>Mon, 18 Jul 2011 00:04:55 +0000</pubDate>
		<dc:creator>Chris Heald</dc:creator>
				<category><![CDATA[MongoDB]]></category>
		<category><![CDATA[Performance]]></category>
		<category><![CDATA[Ruby]]></category>
		<category><![CDATA[mongomapper]]></category>
		<category><![CDATA[plucky]]></category>

		<guid isPermaLink="false">http://www.coffeepowered.net/?p=456</guid>
		<description><![CDATA[MongoDB, as I&#8217;ve mentioned before, is not without its warts. I&#8217;ve run into another, and it&#8217;s a nasty one. It turns out that if you perform count() on a query cursor that includes any conditions, even if those conditions are indexed, the operation takes O(n) time to run. In practice, I&#8217;ve found that this costs [...]]]></description>
			<content:encoded><![CDATA[<p>MongoDB, as I&#8217;ve mentioned before, is not without its warts. I&#8217;ve run into another, and it&#8217;s a nasty one. It turns out that if you perform <code>count()</code> on a query cursor that includes any conditions, even if those conditions are indexed, the operation takes <strong>O(n)</strong> time to run. </p>
<p>In practice, I&#8217;ve found that this costs about 1ms per 1000 records in your counted result set. This is <i>really</i> bad in concert with <a href="https://github.com/mislav/will_paginate">will_paginate</a>, which <a href="https://github.com/jnunemaker/plucky">Plucky</a> (which is used by <a href="http://mongomapper.com/">MongoMapper</a>) exposes an interface to. It naively takes your query, performs a <code>count()</code> on it, and then performs the query <em>again</em> with limiters to get the records for the current page. This is a standard and quickly-accepted way to do this sort of thing.</p>
<p>NewRelic is a great tool to help profile your applications, and in this case, it&#8217;s making the problem abundantly clear:</p>
<p><a href="http://www.coffeepowered.net/wp-content/uploads/2011/07/plucky.png"><img src="http://www.coffeepowered.net/wp-content/uploads/2011/07/plucky.png" alt="" title="NewRelic readout" width="100%" class="size-full wp-image-457" /></a></p>
<p>You see that purple? That&#8217;s how long it takes to run those <code>count()</code> operations. What a big fat pile of suck.</p>
<p>I don&#8217;t have a good solution to this yet, but in the meantime, I&#8217;ve mokneypatched Plucky to cache counts for large result sets. This means that my total counts for a large collection might desync over the course of an hour, but in my use cases, I only need ballpark numbers, so it works out well. This has a very noticeable effect on page times, effectively halving the amount of time I spend in the database for a given index page. Additionally, I can manually specify a count. So, for example, if I know a collection will have over 10k results, I can just pass 10k, and stop paginating after 10k results, drastically reducing my DB load at the expense of exposing older or long-tail content (which may be perfectly, appropriate, depending on the application context).</p>
<p>What I&#8217;m doing is caching any counts over some arbitrary limit (I chose 10k, at which point the counts would take ~10ms) for an hour via the Rails cache (memcached, in my case, leveraging the <code>expires_in</code> parameter). I brought the issue up in the #mongodb IRC channel, and the advice I was given was basically &#8220;cache your counts&#8221;, which is all well and good for simple data sets, but when I&#8217;m building pages per-user based on their preferences and myriad inputs (all indexed, mind you), it just doesn&#8217;t work, so I&#8217;ve resorted to this. It&#8217;s a hack, but it&#8217;s gotten my page times down substantially.</p>
<pre class="brush: ruby; title: ; notranslate">
module Plucky
  class Query
    BIG_RESULT_SET = 10000

    def paginate(opts={})
      page          = opts.delete(:page)
      limit         = opts.delete(:per_page) || per_page
      query         = clone.update(opts)
      cache_key     = &quot;count-cache-#{criteria.source.hash}&quot;
      total         = opts.delete(:total) || Rails.cache.read(cache_key)
      if total.nil?
        total       = query.count
        if total &gt; BIG_RESULT_SET
          Rails.cache.write(cache_key, total, :expires_in =&gt; 1.hour)
        end
      end
      paginator     = Pagination::Paginator.new(total, page, limit)
      query[:limit] = paginator.limit
      query[:skip]  = paginator.skip
      query.all.tap do |docs|
        docs.extend(Pagination::Decorator)
        docs.paginator(paginator)
      end
    end
  end
end</pre>
<p>I&#8217;m not entirely happy with this solution, and would love input on ways to fix it.</p>
]]></content:encoded>
			<wfw:commentRss>http://www.coffeepowered.net/2011/07/17/mongodb-count-and-the-big-o/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>Resque and Tests</title>
		<link>http://www.coffeepowered.net/2011/07/15/resque-and-tests/</link>
		<comments>http://www.coffeepowered.net/2011/07/15/resque-and-tests/#comments</comments>
		<pubDate>Sat, 16 Jul 2011 04:23:50 +0000</pubDate>
		<dc:creator>Chris Heald</dc:creator>
				<category><![CDATA[Rails]]></category>
		<category><![CDATA[Ruby]]></category>
		<category><![CDATA[redis]]></category>

		<guid isPermaLink="false">http://www.coffeepowered.net/?p=450</guid>
		<description><![CDATA[Resque is a bucket of awesome slathered in a delicious candy coating. It makes background job work really, *really* easy. I recently switched to it, and found that in the process of testing it, I was generating an awful lot of extra unfulfilled jobs in my queue, when the job was a side-effect of some [...]]]></description>
			<content:encoded><![CDATA[<p><a href="https://github.com/defunkt/resque">Resque</a> is a bucket of awesome slathered in a delicious candy coating. It makes background job work really, *really* easy. I recently switched to it, and found that in the process of testing it, I was generating an awful lot of extra unfulfilled jobs in my queue, when the job was a side-effect of some other test (rather than what was being tested explicitly).</p>
<p>I couldn&#8217;t find a quick and easy answer to this with some Googling, but it turns out that the answer is fortunately rather simple.<br />
<span id="more-450"></span><br />
In your <code>test_helper.rb</code>:</p>
<pre class="brush: ruby; title: ; notranslate">
 def setup
    Resque.redis.select 1
  end

  def teardown
    Resque.redis.keys(&quot;queue:*&quot;).each {|key| Resque.redis.del key }
  end
</pre>
<p>That&#8217;s all there is to it. The <code>setup</code> causes Resque to write to database #1 (#0 is default, and is what your development environment is likely using), and the <code>teardown</code> just deletes all your queues (which are really just lists of jobs to run). Test all you want and you won&#8217;t have to worry about tens of thousands of jobs junking up your redis DB.</p>
]]></content:encoded>
			<wfw:commentRss>http://www.coffeepowered.net/2011/07/15/resque-and-tests/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>Tarot for easier Rails configurations</title>
		<link>http://www.coffeepowered.net/2011/06/28/tarot-for-easier-rails-configurations/</link>
		<comments>http://www.coffeepowered.net/2011/06/28/tarot-for-easier-rails-configurations/#comments</comments>
		<pubDate>Tue, 28 Jun 2011 23:04:11 +0000</pubDate>
		<dc:creator>Chris Heald</dc:creator>
				<category><![CDATA[Rails]]></category>
		<category><![CDATA[Ruby]]></category>
		<category><![CDATA[tarot]]></category>

		<guid isPermaLink="false">http://www.coffeepowered.net/?p=432</guid>
		<description><![CDATA[Once upon a time, I wrote a quick-and-dirty Rails plugin for site configuration. Since then, I&#8217;ve continued to use variants on this pattern, and it&#8217;s evolved to the point that it deserved a revisit. After continually slimming down the code, I realized that even though it&#8217;s tiny, it&#8217;s danged useful to be able to just [...]]]></description>
			<content:encoded><![CDATA[<p>Once upon a time, I wrote a <a href="http://www.coffeepowered.net/2008/09/25/site_config-painless-config-variables-for-rails-projects/">quick-and-dirty Rails plugin for site configuration</a>. Since then, I&#8217;ve continued to use variants on this pattern, and it&#8217;s evolved to the point that it deserved a revisit.</p>
<p>After continually slimming down the code, I realized that even though it&#8217;s tiny, it&#8217;s <em>danged useful</em> to be able to just drop this into a Rails app and go. Thus, I&#8217;d like to present <a href="https://github.com/cheald/tarot">Tarot</a>, my Rails configuration solution.</p>
<p>Tarot&#8217;s current form is heavily inspired by the Rails I18n usage, and is very quick and easy to use in your app. The generator installs a sample yaml file at config/tarot.yml, as well as an initializer to bootstrap your configuration and provide a handy helper method for quick access to those config values.</p>
<p>Assuming you have a config file like so:</p>
<pre class="brush: yaml; title: ; notranslate">
---
base: &amp;base
  foo: bar
  nested:
    tree: value
  array:
    - value 1
    - value 2

development: &amp;development
  &lt;&lt;: *base

test: &amp;test
  &lt;&lt;: *base

production: &amp;production
  &lt;&lt;: *base
  foo: baz
</pre>
<p>You&#8217;ll notice that all the environments inherit from your base environment; this gives you an easy way to define common settings once, then override them per environment. Handy!</p>
<p>You could can access values by key, or by dot-delimited path:</p>
<pre class="brush: ruby; title: ; notranslate">
config('foo') =&gt; 'bar'
config('nested.tree') =&gt; value
</pre>
<p>Default values are similarly easy.</p>
<pre class="brush: ruby; title: ; notranslate">
config('foo.missing', 42) =&gt; 42
</pre>
<p>Finally, while Tarot will read your current application environment&#8217;s config, if you want to reach into another environment, that&#8217;s likewise easy:</p>
<pre class="brush: ruby; title: ; notranslate">
config('foo', nil, 'production') =&gt; 'baz'
</pre>
<p>As of 0.1.2, Tarot also supports method_missing invocation:</p>
<pre class="brush: ruby; title: ; notranslate">
Config = Tarot::Config.new('settings.yml', Rails.env)
Config.foo.bar.baz =&gt; &quot;bin&quot;
</pre>
<p>It also supports default values:</p>
<pre class="brush: ruby; title: ; notranslate"># Assuming foo has no subkey bar
Config.foo.bar(&quot;default&quot;) =&gt; &quot;default&quot;
</pre>
<p>But it&#8217;ll fail if you try to invoke method_missing on a non-leaf node</p>
<pre class="brush: ruby; title: ; notranslate">
# Assuming that there is no `blaze` tree
Config.blaze.blarg =&gt; NameError
</pre>
<p>That&#8217;s about all there is to it &#8212; config isn&#8217;t (or shouldn&#8217;t be) a hard problem, so there&#8217;s not a whole lot to it, but it should get you up and running with easily-configured Rails apps in seconds.</p>
]]></content:encoded>
			<wfw:commentRss>http://www.coffeepowered.net/2011/06/28/tarot-for-easier-rails-configurations/feed/</wfw:commentRss>
		<slash:comments>6</slash:comments>
		</item>
		<item>
		<title>Making HAML faster</title>
		<link>http://www.coffeepowered.net/2011/06/17/making-haml-faster/</link>
		<comments>http://www.coffeepowered.net/2011/06/17/making-haml-faster/#comments</comments>
		<pubDate>Fri, 17 Jun 2011 20:35:09 +0000</pubDate>
		<dc:creator>Chris Heald</dc:creator>
				<category><![CDATA[Performance]]></category>
		<category><![CDATA[Ruby]]></category>

		<guid isPermaLink="false">http://www.coffeepowered.net/?p=412</guid>
		<description><![CDATA[Haml&#8217;s among my favorite of the Rails technology stack. Clean, self-correcting templates that mean less typing and more doing for me. I love it. Unfortunately, there have been a number of performance regressions introduced into Haml recently, and that sucks, because Rails spends a lot of time building views, and I&#8217;d really like those numbers [...]]]></description>
			<content:encoded><![CDATA[<p>Haml&#8217;s among my favorite of the Rails technology stack. Clean, self-correcting templates that mean less typing and more doing for me. I love it.</p>
<p>Unfortunately, there have been a number of performance regressions introduced into Haml recently, and that sucks, because Rails spends a lot of time building views, and I&#8217;d really like those numbers to be smaller.</p>
<p>Over the past couple of weeks, I&#8217;ve been on-and-off profiling Haml and working on various performance patches. I mentioned one <a href="http://www.coffeepowered.net/2011/06/17/jruby-performance-exceptions-are-not-flow-control/">of them in my previous post</a> &#8211; avoiding exceptions as flow control. There are a couple more we need to watch out for, though.<br />
<span id="more-412"></span></p>
<h2>Problem #1: Lots and lots of extra string parsing</h2>
<p>There&#8217;s a utility method that lets Haml compare the current version of Ruby with some arbitrary string, to find out if certain features are supported. You can look at the implementation of <code>version_gt</code>, if you&#8217;d like, but it&#8217;s relatively complex, and we&#8217;re invoking it a <em>lot</em> in any given template. </p>
<p>In <code><a href="https://github.com/nex3/haml/blob/master/lib/haml/util.rb#L268">Haml::Util</a></code></p>
<pre class="brush: ruby; title: ; notranslate">
    def version_geq(v1, v2)
      version_gt(v1, v2) || !version_gt(v2, v1)
    end
</pre>
<p>Memoizing these values results in significantly less string parsing and much faster templates.</p>
<pre class="brush: ruby; title: ; notranslate">
    def version_geq(v1, v2)
      @@version_comparison_cache ||= {}
      k = &quot;#{v1}#{v2}&quot;
      return @@version_comparison_cache[k] unless @@version_comparison_cache[k].nil?
      @@version_comparison_cache[k] = ( version_gt(v1, v2) || !version_gt(v2, v1) )
    end
</pre>
<h2>Problem #2: Extraneous block creation</h2>
<p><code><a href="https://github.com/nex3/haml/blob/master/lib/haml/compiler.rb#L444">Haml::Compiler#compile</a></code> is what compiles your Haml soup down into HTML.  It also creates a bunch of extra closures &#8211; one for every leaf tag in your document.</p>
<pre class="brush: ruby; title: ; notranslate">
    def compile(node)
      parent, @node = @node, node
      block = proc {node.children.each {|c| compile c}}
      send(&quot;compile_#{node.type}&quot;, &amp;(block unless node.children.empty?))
    ensure
      @node = parent
    end
</pre>
<p>Let&#8217;s just change that so that the block is only created and passed if there are children to iterate:</p>
<pre class="brush: ruby; title: ; notranslate">
    def compile(node)
      parent, @node = @node, node
      if node.children.empty?
        send(&quot;compile_#{node.type}&quot;)
      else
        send(&quot;compile_#{node.type}&quot;,  &amp;proc {node.children.each {|c| compile c}} )
      end
    ensure
      @node = parent
    end
</pre>
<p>It&#8217;s worth noting that I tried a compacted single-line send, but it seems faster to just check <code>#empty?</code> than to conditionally create the block and pass <code>&#038;(block if block)</code>.</p>
<h2>Problem #3: Exceptions as flow control</h2>
<p><code>Haml::Helpers</code> has a couple of instances where it checks for the presence of <code>_hamlout</code> in a block binding by just eval&#8217;ing <code>_hamlout</code> and catching <code>NameError</code> to discover that it doesn&#8217;t exist. I&#8217;ve refactored that to use more idiomatic constructs.</p>
<pre class="brush: diff; title: ; notranslate">
@@ -337,7 +337,7 @@ MESSAGE
     # @yield [args] A block of Haml code that will be converted to a string
     # @yieldparam args [Array] `args`
     def capture_haml(*args, &amp;block)
-      buffer = eval('_hamlout', block.binding) rescue haml_buffer
+      buffer = eval('if defined? _hamlout then _hamlout else nil end', block.binding) || haml_buffer
       with_haml_buffer(buffer) do
         position = haml_buffer.buffer.length

...	...
@@ -540,10 +540,7 @@ MESSAGE
     # @param block [Proc] A Ruby block
     # @return [Boolean] Whether or not `block` is defined directly in a Haml template
     def block_is_haml?(block)
-      eval('_hamlout', block.binding)
-      true
-    rescue
-      false
+      eval('!!defined?(_hamlout)', block.binding)
     end
</pre>
<h2>Results</h2>
<p>To test, I have my branch in <code>./haml</code> and the current origin master in <code>./haml-upstream</code>. I&#8217;ve also got a 900-line Haml template with no inline ruby (just a html2haml converted webpage) that I&#8217;m parsing to test with.</p>
<p>To test, I just include the appropriate library and run the benchmark.</p>
<pre class="brush: ruby; title: ; notranslate">
require 'haml/lib/haml'
#require 'haml-upstream/lib/haml'
require 'benchmark'

TIMES = 100
source = open(&quot;formatted_email.haml&quot;).read

Benchmark.bmbm do |x|
    x.report(&quot;Render time&quot;) do
        TIMES.times do
            engine = Haml::Engine.new source
            engine.render :ugly =&gt; true
        end
    end
end
</pre>
<h3>REE-1.8.7-2011.03</h3>
<p>Upstream: </p>
<pre class="brush: bash; title: ; notranslate">
[chris@luna repos]$ ruby haml-bench.rb
Rehearsal -----------------------------------------------
Render time   7.010000   0.770000   7.780000 (  7.777076)
-------------------------------------- total: 7.780000sec

                  user     system      total        real
Render time   6.990000   0.710000   7.700000 (  7.721977)
</pre>
<p>And my branch:</p>
<pre class="brush: bash; title: ; notranslate">
[chris@luna repos]$ ruby haml-bench.rb
Rehearsal -----------------------------------------------
Render time   5.180000   0.460000   5.640000 (  5.703304)
-------------------------------------- total: 5.640000sec

                  user     system      total        real
Render time   5.170000   0.450000   5.620000 (  5.621875)
</pre>
<p><strong>Improvement: +27% speedup</strong></p>
<h3>JRuby 1.6.0</h3>
<p>Upstream:</p>
<pre class="brush: bash; title: ; notranslate">
[chris@luna repos]$ jruby --server haml-bench.rb
Rehearsal -----------------------------------------------
Render time  13.254000   0.000000  13.254000 ( 13.254000)
------------------------------------- total: 13.254000sec

                  user     system      total        real
Render time   6.183000   0.000000   6.183000 (  6.183000)
</pre>
<p>My branch:</p>
<pre class="brush: bash; title: ; notranslate">
[chris@luna repos]$ jruby --server haml-bench.rb
Rehearsal -----------------------------------------------
Render time  11.726000   0.000000  11.726000 ( 11.726000)
------------------------------------- total: 11.726000sec

                  user     system      total        real
Render time   4.856000   0.000000   4.856000 (  4.856000)
</pre>
<p><strong>Improvement: +21.5% speedup</strong></p>
<h3>Ruby MRI 1.9.2</h3>
<p>Upstream:</p>
<pre class="brush: bash; title: ; notranslate">
[chris@luna repos]$ ruby haml-bench.rb
Rehearsal -----------------------------------------------
Render time   6.990000   0.610000   7.600000 (  7.599568)
-------------------------------------- total: 7.600000sec

                  user     system      total        real
Render time   6.920000   0.660000   7.580000 (  7.577772)
</pre>
<p>My branch:</p>
<pre class="brush: bash; title: ; notranslate">
[chris@luna repos]$ ruby haml-bench.rb
Rehearsal -----------------------------------------------
Render time   5.110000   0.470000   5.580000 (  5.573496)
-------------------------------------- total: 5.580000sec

                  user     system      total        real
Render time   5.150000   0.440000   5.590000 (  5.577480)
</pre>
<p><strong>Improvement: +26.3% speedup</strong></p>
<h2>Final Words</h2>
<p>I&#8217;ve got an <a href="https://github.com/nex3/haml/pull/346">open pull request</a>, but it&#8217;s been ignored thus far. Make some noise and get this pulled into master, so we can make Rails apps everywhere faster!</p>
]]></content:encoded>
			<wfw:commentRss>http://www.coffeepowered.net/2011/06/17/making-haml-faster/feed/</wfw:commentRss>
		<slash:comments>9</slash:comments>
		</item>
		<item>
		<title>JRuby Performance: Exceptions are not flow control</title>
		<link>http://www.coffeepowered.net/2011/06/17/jruby-performance-exceptions-are-not-flow-control/</link>
		<comments>http://www.coffeepowered.net/2011/06/17/jruby-performance-exceptions-are-not-flow-control/#comments</comments>
		<pubDate>Fri, 17 Jun 2011 08:42:06 +0000</pubDate>
		<dc:creator>Chris Heald</dc:creator>
				<category><![CDATA[JRuby]]></category>
		<category><![CDATA[Performance]]></category>
		<category><![CDATA[Ruby]]></category>

		<guid isPermaLink="false">http://www.coffeepowered.net/?p=364</guid>
		<description><![CDATA[I started playing with JRuby tonight, and got my application up and running on it in under 10 minutes (kudos to the JRuby team!), but when I started measuring its performance, I was seriously unimpressed. This didn&#8217;t quite line up with what I&#8217;ve read of JRuby, so I decided to do a little digging. Exception [...]]]></description>
			<content:encoded><![CDATA[<p>I started playing with JRuby tonight, and got my application up and running on it in under 10 minutes (kudos to the JRuby team!), but when I started measuring its performance, I was seriously unimpressed. This didn&#8217;t quite line up with what I&#8217;ve read of JRuby, so I decided to do a little digging.<br />
<span id="more-364"></span></p>
<h2>Exception to the rule</h2>
<p>I hopped into #jruby and was fortunate enough to get to talk to <a href="http://twitter.com/#!/headius">headius</a> directly. He pointed me towards <a href="http://jira.codehaus.org/browse/JRUBY-5534?page=com.atlassian.jira.plugin.system.issuetabpanels%3Acomment-tabpanel#issue-tabs">problems with older versions of the i18n gem</a>, which got me thinking &#8211; perhaps there were other abuses of exceptions as flow control in my app. After using jconsole to measure my exceptions-per-request, I found that I was generating several hundred exceptions per request, which was imposing a significant slowdown on my requests. After a quick discussion, headius <a href="https://github.com/jruby/jruby/commit/041cc9c77a3ade4ac7c9130deaafa70ae2c2db79">committed a change to add logging switches for exceptions</a>. I was able to fire up my trinidad instance with logging switches:</p>
<pre class="brush: bash; title: ; notranslate">
jruby -Xlog.exceptions=true -Xlog.backtraces=true -Xlog.callers=true -S trinidad 2&gt;&amp;1 | grep &quot;Backtrace generated&quot; -A4
</pre>
<p>and I instantly had data at my disposal. The ugly areas became clear very quickly &#8211; <a href="https://github.com/jnicklas/carrierwave">CarrierWave</a>, <a href="http://mongomapper.com/">MongoMapper</a>, and surprisingly, <a href="http://haml-lang.com/">Haml</a> were my top offenders.</p>
<pre class="brush: bash; title: ; notranslate">
Haml:          117 exceptions
CarrierWave:   162 exceptions
MongoMapper:   114 exceptions
</pre>
<p>The stack traces themselves are easy to interpret, too:</p>
<pre class="brush: bash; title: ; notranslate">
Backtrace generated:
NameError: undefined local variable or method `_hamlout' for #&lt;ActionView::Base:0x1af81d1c&gt;
               eval at org/jruby/RubyKernel.java:1088
     block_is_haml? at /usr/local/rvm/gems/jruby-head/gems/haml-3.1.2/lib/haml/helpers.rb:543
  capture_with_haml at /usr/local/rvm/gems/jruby-head/gems/haml-3.1.2/lib/haml/helpers/action_view_mods.rb:90
1

This gave me an easy target:

1

What's happening here is the block is evaluated with just &lt;code&gt;_hamlout&lt;/code&gt;, which throws a NameError if the variable doesn't exist in the block's binding context, and then the proper boolean is returned. However, this is a perfect example of exceptions as flow control - the question is &quot;Does _hamlout exist in the block context?&quot;, and that's not best answered by a NameError. Ruby gives us &lt;code&gt;defined?&lt;/code&gt; to check that sort of thing trivially, and more importantly, idiomatically. So, I can just rewrite that helper as:

1

This is both far more correct, and far faster.

&lt;h2&gt;Lies, Damn Lies, and Benchmarks&lt;/h2&gt;

Let's try a quick benchmark to test the effects of each method:

1

And results:

1
[chris@luna repos]$ rvm use ree
Using /usr/local/rvm/gems/ree-1.8.7
[chris@luna repos]$ ruby test.rb
Rehearsal --------------------------------------------
Rescue     0.070000   0.010000   0.080000 (  0.082649)
defined?   0.020000   0.000000   0.020000 (  0.022991)
----------------------------------- total: 0.100000sec

               user     system      total        real
Rescue     0.080000   0.000000   0.080000 (  0.082292)
defined?   0.030000   0.000000   0.030000 (  0.022384)
</pre>
<p>Avoiding extraneous exceptions is clearly better on MRI (3.6x faster using <code>defined?</code> rather than <code>NameError</code>), but 100k exception raises only imposes an extra 0.6 seconds of runtime on the test. Probably not worth fretting over to too great a degree, at least until other optimizations have been made.</p>
<p>Let&#8217;s try JRuby:</p>
<pre class="brush: bash; title: ; notranslate">
[chris@luna repos]$ rvm use jruby-head
Using /usr/local/rvm/gems/jruby-head
[chris@luna repos]$ jruby --server --fast test.rb
Rehearsal --------------------------------------------
Rescue    11.145000   0.000000  11.145000 ( 11.097000)
defined?   0.599000   0.000000   0.599000 (  0.599000)
---------------------------------- total: 11.744000sec

               user     system      total        real
Rescue     9.758000   0.000000   9.758000 (  9.758000)
defined?   0.357000   0.000000   0.357000 (  0.357000)
</pre>
<p>Holy <em>crap</em>, now we&#8217;re in interesting territory. Using <code>defined?</code> rather than letting exception handling do our dirty work for us is <em>27.3x faster</em>. As Headius explains:</p>
<blockquote><p>
In JRuby 1.6, we moved to using JVM-level facilities for generating exception backtraces. These facilities are considerably more expensive than the artificial backtraces we had been maintaining before&#8230;not incredibly expensive, but not free or as cheap as they used to be by any means. If applications and libraries use exceptions for exceptional error cases, it&#8217;s not a big deal.
</p></blockquote>
<p>The message here is pretty clear &#8211; use exceptions for things that are exceptional, not for flow control of the normal program operation. It&#8217;s worth noting that in the benchmark, there isn&#8217;t a big stack trace to generate; in a Rails app, each exception is going to generate a stack trace scores of entries deep, further exacerbating the problem. This is a simple test, and a simple case, but it illustrates the point fairly clearly.</p>
<p>By using the new exception logging switch, I was able to, in about an hour, monkeypatch several of the libraries we were using to reduce my exceptions-per-request from 400 to 8. This had an extremely noticable impact on my page times, reducing my test action runtimes significantly. If you&#8217;d like to take a look at all the pieces I patched, <a href="http://pastie.org/private/tmvj7dlr2sosxceep9a">check this quick pastie</a>. I&#8217;m working on patches to submit to each of these projects; besides avoiding punching JRuby in the tenders, it also improves runtimes in MRI and REE Ruby VMs.</p>
<p>In all three libraries, exceptions were being used as flow control to answer &#8220;does this variable or method exist?&#8221;, and in all three cases, the libraries are doing it wrong; Ruby gives us <code>defined?</code> and <code>respond_to?</code>, and if you mean &#8220;Is this variable defined?&#8221;, why not ask &#8220;<code>defined? variable</code>&#8221; rather than &#8220;try to use this and hit the eject button if it doesn&#8217;t work&#8221;?</p>
<p>Exceptions are for things like &#8220;The remote service returned a bad response&#8221;, or &#8220;That string didn&#8217;t parse as valid JSON&#8221;, or &#8220;That file which you&#8217;re trying to parse as an image isn&#8217;t actually an image&#8221; &#8211; exceptional circumstances. Things that you don&#8217;t expect. If you&#8217;re using exceptions to answer a question, or as a poor-man&#8217;s GOTO, you may want to reevaluate how you&#8217;re using them.</p>
<h2>JRuby as an exceptions-as-flow-control detector</h2>
<p>It&#8217;s worth noting as a quick side item here that the commit to JRuby tonight makes JRuby a fantastic tool for locating libraries that use the exceptions-as-flow-control antipattern in your Rails apps. I tried a number of approaches to monkeypatching <code>Exception</code> before Headius committed the change, none of which worked very well, but once I built jruby-head, I had every exeception trace in my application laid out before me. You might consider taking a crack at this on your own apps &#8211; you might like what you find.</p>
<p>Getting running is as easy as:</p>
<pre class="brush: bash; title: ; notranslate">
rvm install jruby-head --branch jruby-1_6
rvm use jruby-head
gem install bundler trinidad
bundle install
jruby -Xlog.exceptions=true -Xlog.backtraces=true -Xlog.callers=true -S trinidad 2&gt;&amp;1 | grep &quot;Backtrace generated&quot; -A4
</pre>
<p>In a matter if minutes, you&#8217;ll have an abundance of stack traces to chew on at your leisure. Or, if you&#8217;re lucky, you won&#8217;t have much of anything at all. Good hunting!</p>
<h2>To sum up:</h2>
<p>This is flow control:</p>
<p><a href="http://www.coffeepowered.net/wp-content/uploads/2011/06/steering-wheel.jpg"><img src="http://www.coffeepowered.net/wp-content/uploads/2011/06/steering-wheel.jpg" alt="" title="steering-wheel" width="300" height="304" class="aligncenter size-full wp-image-378" /></a></p>
<p>This is exception handling:</p>
<p><a href="http://www.coffeepowered.net/wp-content/uploads/2011/06/ejection-seat-af-acesii.jpg"><img src="http://www.coffeepowered.net/wp-content/uploads/2011/06/ejection-seat-af-acesii.jpg" alt="" title="ejection-seat-af-acesii" width="300" height="201" class="aligncenter size-full wp-image-377" /></a></p>
<p>Make sure you&#8217;re pressing the right button.</p>
]]></content:encoded>
			<wfw:commentRss>http://www.coffeepowered.net/2011/06/17/jruby-performance-exceptions-are-not-flow-control/feed/</wfw:commentRss>
		<slash:comments>11</slash:comments>
		</item>
		<item>
		<title>Mongrations reloaded</title>
		<link>http://www.coffeepowered.net/2011/02/01/mongrations-reloaded/</link>
		<comments>http://www.coffeepowered.net/2011/02/01/mongrations-reloaded/#comments</comments>
		<pubDate>Tue, 01 Feb 2011 19:12:17 +0000</pubDate>
		<dc:creator>Chris Heald</dc:creator>
				<category><![CDATA[MongoDB]]></category>
		<category><![CDATA[Rails]]></category>
		<category><![CDATA[gem]]></category>
		<category><![CDATA[library]]></category>
		<category><![CDATA[mongrations]]></category>

		<guid isPermaLink="false">http://www.coffeepowered.net/?p=362</guid>
		<description><![CDATA[Users of MongoMapper may be familiar with mongrations, a Rails plugin to provide you with ActiveRecord-style migration tools for MongoDB. You don&#8217;t need them for schema changes, obviously, since MongoDB is schemaless, and you can define any changes you need to in your model. However, there are times that deploying a changeset will require some [...]]]></description>
			<content:encoded><![CDATA[<p>Users of <a href="https://github.com/jnunemaker/mongomapper">MongoMapper</a> may be familiar with mongrations, a Rails plugin to provide you with ActiveRecord-style migration tools for MongoDB. You don&#8217;t need them for schema changes, obviously, since MongoDB is schemaless, and you can define any changes you need to in your model. However, there are times that deploying a changeset will require some data change, or some maintenance stuff to be run. For that, mongrations is super helpful. Or was.</p>
<p>It&#8217;s been broken for some time now, and not really in much of a state to be used by anyone, but I needed it, so I fixed it up. You can get my source <a href="https://github.com/cheald/mongrations">on github</a>, but I&#8217;ve done one easier and made it a gem. Major changes are:</p>
<ul>
<li>Bad assumptions fixed. Actually works now.</li>
<li>Reorganized the whole thing and repackaged it as a gem. Doesn&#8217;t work as a Rails plugin anymore, but that&#8217;s okay. Just add it to to your environment or Gemfile and you&#8217;re good to go.</li>
<li>Added tests!</li>
<li>Fixed documentation, and normalized rake tasks names.</li>
</ul>
<p>To get it, you can just <code>gem install mongrations</code> and you&#8217;re off to the races.</p>
<p>This is primarily going to be useful for MongoMapper people, who are likely still on Rails 2.3.x. If you&#8217;re using mongoid, check out <a href="https://github.com/adacosta/mongoid_rails_migrations">mongoid_rails_migrations</a> instead.</p>
]]></content:encoded>
			<wfw:commentRss>http://www.coffeepowered.net/2011/02/01/mongrations-reloaded/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>Rails, Varnish, Cookie Sessions, and CSRF tokens</title>
		<link>http://www.coffeepowered.net/2011/01/29/rails-varnish-cookie-sessions-and-csrf-tokens/</link>
		<comments>http://www.coffeepowered.net/2011/01/29/rails-varnish-cookie-sessions-and-csrf-tokens/#comments</comments>
		<pubDate>Sat, 29 Jan 2011 08:33:29 +0000</pubDate>
		<dc:creator>Chris Heald</dc:creator>
				<category><![CDATA[Rails]]></category>
		<category><![CDATA[caching]]></category>
		<category><![CDATA[cookies]]></category>
		<category><![CDATA[csr]]></category>
		<category><![CDATA[varnish]]></category>

		<guid isPermaLink="false">http://www.coffeepowered.net/?p=352</guid>
		<description><![CDATA[I&#8217;ve recently been trying to figure out how to get Rails to place nicely with Varnish. It doesn&#8217;t do that very well. In a nutshell: Varnish is easy to use, if your app isn&#8217;t setting session cookies until you actually need them. The presence of a session cookie usually means that content shouldn&#8217;t be cacheable. [...]]]></description>
			<content:encoded><![CDATA[<p><img src="http://www.coffeepowered.net/wp-content/uploads/2011/01/126070445_82ca5f6f4c_m.jpg" align="right" style="margin: 0 0 15px 15px;" alt="Cookies! Delicious and performance-shattering." />I&#8217;ve recently been trying to figure out how to get Rails to place nicely with <a href="http://varnish-cache.org/">Varnish</a>. It doesn&#8217;t do that very well. In a nutshell:</p>
<ul>
<li>Varnish is easy to use, if your app isn&#8217;t setting session cookies until you actually need them. The presence of a session cookie usually means that content shouldn&#8217;t be cacheable.</li>
<li>Hitting any page with a form results in Rails generating a CSRF token and sticking it in the session, generating a session cookie and effectively locking the rest of the session out of being cacheable (even if it should be).</li>
<li>Just *breathing* the method <code>session</code> in your app initializes your session.</li>
<li>The Rails cookie session middleware assumes you always want to write a session cookie.</li>
</ul>
<p>Fortunately, since so much of this is in Rack middleware, we can fix its mistakes with a middleware of our own. In a nutshell, I&#8217;m going to:</p>
<ul>
<li>Avoid writing CSRF tokens until we actually need them</li>
<li>Check and see if we have an &#8220;empty&#8221; session (ie, no interesting data, just the session ID)</li>
<li>Prevent session cookies from being sent to the browser unless there&#8217;s actually useful data in them.</li>
</ul>
<p><span id="more-352"></span><br />
Let&#8217;s get started. There&#8217;s a lot to this, and it&#8217;s a delicate collection of hacks, but it works nicely.</p>
<h2>The care and feeding of CSRF tokens</h2>
<p>To get started, I disabled CSRF functionality based on user state. In your application_controller.rb:</p>
<pre class="brush: ruby; title: ; notranslate">
  protect_from_forgery :if =&gt; :user?
  skip_before_filter :verify_authenticity_token, :unless =&gt; :user?

protected

  def form_authenticity_token
    if user?
      session[:_csrf_token] ||= ActiveSupport::SecureRandom.base64(32)
    end
  end
</pre>
<p>In this case, <code>user?</code> is a method from my authentication framework that lets me check if I have an active session. The astute reader will note that this check performs the aforementioned breathing-on (and thereby initializing) the session, so it&#8217;s unfortunately not quite as simple as this. However, this will prevent authenticity checks for unauthenticated sessions. There&#8217;s no real point to them if you aren&#8217;t performing privileged operations anyhow, so we&#8217;ll just save the overhead.</p>
<h2>Stuffing your cookies back into the jar</h2>
<p>Next, we need to deal with the session cookie itself. We have two options when invalidating cookies &#8211; either strip them from the already-written headers, or just add another Set-Cookie line to instantly invalidate them. Since we&#8217;re dealing with Varnish, we want option 1 &#8211; ideally, we won&#8217;t be passing the Set-Cookie header, since Varnish (by default) won&#8217;t cache any response that attempts to set a cookie.</p>
<p>Session cookie management happens in <code>ActionController::Session::CookieStore</code>, and it&#8217;s really high up in the middleware stack. <code>rake middleware</code> will dump your stack &#8211; you&#8217;ll find it&#8217;s usually in position 2 or 3. So, in order to tweak it, we&#8217;ll need to inject a new middleware into your stack to mess with your cookie headers after the cookie handler itself blindly writes them out.</p>
<p>Scroll down if you want the code, but the gist of it is this:</p>
<ul>
<li>Check for a special &#8220;cookie.logout&#8221; environment parameter. If this is present, we&#8217;re going to just flat-out nuke the session cookie. More on this later.</li>
<li>Otherwise, check to see if the session has any interesting keys. If it doesn&#8217;t, remove it from the Set-Cookie header</li>
</ul>
<p>The code itself. Drop this in <code>lib/strip_empty_sessions.rb</code>.</p>
<pre class="brush: ruby; title: ; notranslate">
class StripEmptySessions
  ENV_SESSION_KEY = &quot;rack.session&quot;.freeze
  HTTP_SET_COOKIE = &quot;Set-Cookie&quot;.freeze
  BOGUS_KEYS = [:session_id, :_csrf_token]

  def initialize(app, options = {})
    @app = app
    @options = options
  end

  def call(env)
    status, headers, body = @app.call(env)

    session_data = env[ENV_SESSION_KEY]
    sc = headers[HTTP_SET_COOKIE]
    if env[&quot;cookie.logout&quot;]
      value = Hash.new
      value[:value] = &quot;x&quot;
      value[:expires] = Time.now - 1.year
      cookie = build_cookie(@options[:key], value.merge(@options))

      if sc.nil?
        headers[HTTP_SET_COOKIE] = cookie if env[&quot;cookie.logout&quot;]
      elsif sc.is_a? Array
        sc &lt;&lt; cookie if env[&quot;cookie.logout&quot;]
      elsif sc.is_a? String
        headers[HTTP_SET_COOKIE] &lt;&lt; &quot;\n#{cookie}&quot; if env[&quot;cookie.logout&quot;]
      end
    elsif (session_data.keys - BOGUS_KEYS).empty?
      if sc.is_a? Array
        sc.reject! {|c| c.match(/^\n?#{@options[:key]}=/)}
      elsif sc.is_a? String
        headers[HTTP_SET_COOKIE].gsub!( /(^|\n)#{@options[:key]}=.*?(\n|$)/, &quot;&quot; )
      end
    end

    [status, headers, body]
  end

  private

  # Copied from the cookie session middleware.
  def build_cookie(key, value)
    case value
    when Hash
      domain  = &quot;; domain=&quot;  + value[:domain] if value[:domain]
      path    = &quot;; path=&quot;    + value[:path]   if value[:path]
      # According to RFC 2109, we need dashes here.
      # N.B.: cgi.rb uses spaces...
      expires = &quot;; expires=&quot; + value[:expires].clone.gmtime.
        strftime(&quot;%a, %d-%b-%Y %H:%M:%S GMT&quot;) if value[:expires]
      secure = &quot;; secure&quot; if value[:secure]
      httponly = &quot;; HttpOnly&quot; if value[:httponly]
      value = value[:value]
    end
    value = [value] unless Array === value
    Rack::Utils.escape(key) + &quot;=&quot; +
      value.map { |v| Rack::Utils.escape(v) }.join(&quot;&amp;&quot;) +
      &quot;#{domain}#{path}#{expires}#{secure}#{httponly}&quot;
  end
end
</pre>
<p>Next, you&#8217;ll need to add this to your middleware stack. In your <code>environment.rb</code>:</p>
<pre class="brush: ruby; title: ; notranslate">
config.middleware.insert_before &quot;ActionController::Session::CookieStore&quot;, &quot;StripEmptySessions&quot;, :key =&gt; &quot;your_session_key&quot;, :path =&gt; &quot;/&quot;, :httponly =&gt; true
</pre>
<p>The <code>:key</code> and <code>:path</code> parameters should match your session cookie settings.</p>
<p>What this will do is let this middleware run on the way back up the stack, right after the session handler gets a crack at things. If there is nothing interesting in the session, it&#8217;ll remove that line from the Set-Cookie header, so if you aren&#8217;t setting any other cookies, the header should end up being empty and should get thrown away. If you triggered a logout, will invalidate the client cookies (rather than just writing a cookie with no data in it back to them).</p>
<p>To do that, you&#8217;ll need to modify your logout method:</p>
<pre class="brush: ruby; title: ; notranslate">
def logout
  request.env[&quot;cookie.logout&quot;] = true
end
</pre>
<p>That should be it. You should now:</p>
<ol>
<li>Not be setting session cookies for empty sessions</li>
<li>Not be setting CSRF tokens for anonymous sessions</li>
<li>Not be leaving &#8220;empty&#8221; session cookies laying around on client machines after a logout.</li>
</ol>
<p>The net result is that you should be cookieless for anonymous sessions, resulting in trivial caching with Varnish. This can vastly improve the performance of your site &#8211; especially if you&#8217;re catching high-traffic pages from web crawlers and the like with Varnish, so they never touch your Rails stack.</p>
<hr />
<p><i>Cookie image (C) <a href="http://www.flickr.com/photos/71217725@N00/126070445/sizes/z/">scubadive67</a>, used under Creative Commons license</i></p>
]]></content:encoded>
			<wfw:commentRss>http://www.coffeepowered.net/2011/01/29/rails-varnish-cookie-sessions-and-csrf-tokens/feed/</wfw:commentRss>
		<slash:comments>1</slash:comments>
		</item>
		<item>
		<title>Write-once read-only fixtures for Rails tests</title>
		<link>http://www.coffeepowered.net/2010/09/05/write-once-read-only-fixtures-for-rails-tests/</link>
		<comments>http://www.coffeepowered.net/2010/09/05/write-once-read-only-fixtures-for-rails-tests/#comments</comments>
		<pubDate>Sun, 05 Sep 2010 20:38:39 +0000</pubDate>
		<dc:creator>Chris Heald</dc:creator>
				<category><![CDATA[Rails]]></category>
		<category><![CDATA[Ruby]]></category>

		<guid isPermaLink="false">http://www.coffeepowered.net/?p=330</guid>
		<description><![CDATA[In the project I&#8217;m currently working on, I&#8217;m heavily using factory_girl to generate test data, rather than using the old Rails fixtures standby. However, I still have a set of read-only fixtures (which are used for testing read-only models against a legacy database). I&#8217;m using these in my tests, but since they are read only [...]]]></description>
			<content:encoded><![CDATA[<p>In the project I&#8217;m currently working on, I&#8217;m heavily using <a href="http://github.com/thoughtbot/factory_girl">factory_girl</a> to generate test data, rather than using the old Rails fixtures standby. However, I still have a set of read-only fixtures (which are used for testing read-only models against a legacy database). I&#8217;m using these in my tests, but since they are read only (like, seriously &#8211; the models are marked as by using <code>after_find</code> to call <code>readonly!</code>, ensuring that records will not be accidentally written), there&#8217;s no need to wipe and re-insert them per-test.</p>
<p>It&#8217;s not too hard to set up fixtures to be inserted once per test suite run &#8211;</p>
<p>In your test_helper.rb, above the <code>class ActiveSupport::TestCase</code> definition, add the following:</p>
<pre class="brush: ruby; title: ; notranslate">
Fixtures.reset_cache
fixtures_folder = File.join(RAILS_ROOT, 'test', 'fixtures')
fixtures = Dir[File.join(fixtures_folder, '*.yml')].map {|f| File.basename(f, '.yml') }
Fixtures.create_fixtures(fixtures_folder, fixtures)
Fixtures.reset_cache
</pre>
<p>Next, turn off transactional fixtures and comment out the fixtures macro:</p>
<pre class="brush: ruby; title: ; notranslate">
self.use_transactional_fixtures = false
# fixtures :all
</pre>
<p>That&#8217;s all there is to it. Your fixtures will be inserted into your test database once when test_helper is included for the first time, and then not again for the rest of the test suite run. This should speed your tests up substantially.</p>
]]></content:encoded>
			<wfw:commentRss>http://www.coffeepowered.net/2010/09/05/write-once-read-only-fixtures-for-rails-tests/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
	</channel>
</rss>

