<?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; Rails</title>
	<atom:link href="http://www.coffeepowered.net/category/rails/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>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>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>
		<item>
		<title>WillPaginate and custom paging.</title>
		<link>http://www.coffeepowered.net/2010/08/07/willpaginate-and-custom-paging/</link>
		<comments>http://www.coffeepowered.net/2010/08/07/willpaginate-and-custom-paging/#comments</comments>
		<pubDate>Sat, 07 Aug 2010 22:57:19 +0000</pubDate>
		<dc:creator>Chris Heald</dc:creator>
				<category><![CDATA[Rails]]></category>
		<category><![CDATA[Ruby]]></category>
		<category><![CDATA[will_paginate]]></category>

		<guid isPermaLink="false">http://www.coffeepowered.net/?p=300</guid>
		<description><![CDATA[will_paginate is the de facto Rails paging plugin, and with good reason &#8211; it&#8217;s solid, fast, and reliable. Everyone I know uses it, but a lot of people don&#8217;t use it to its full power. I recently discovered some very cool functionality it includes &#8211; the WillPaginate::Collection class can be used as a custom paginator [...]]]></description>
			<content:encoded><![CDATA[<p><a href="http://wiki.github.com/mislav/will_paginate/">will_paginate</a> is the de facto Rails paging plugin, and with good reason &#8211; it&#8217;s solid, fast, and reliable. Everyone I know uses it, but a lot of people don&#8217;t use it to its full power.</p>
<p>I recently discovered some <em>very</em> cool functionality it includes &#8211; the <code>WillPaginate::Collection</code> class can be used as a custom paginator for effectively any enumerable collection. It&#8217;s very simple, too. I recently used it to build pages of the most popular tags on posts in my database. My data store is MongoDB, and I&#8217;m fetching an array consisting of two-element arrays, <code>[tag, tag_count]</code>. To use will_paginate&#8217;s functionality with this, I just use the following:</p>
<pre class="brush: ruby; title: ; notranslate">
tags = Post.tag_counts(nil, {:sort =&gt; [&quot;value&quot;, &quot;descending&quot;]}) # Return an array of tag/count pairs. Custom function, so it can't leverage the finder on Post.
@topics = WillPaginate::Collection.create(current_page, 20, tags.length) do |pager|
	pager.replace(tags.slice(pager.offset, pager.offset + pager.per_page))
end
</pre>
<p><code>current_page</code> is a helper that derives the current page from the request parameters. The rest of it is self-explanitory. I can now use <code>@topics</code> in my page just as I&#8217;d use a paginated result set from the database.</p>
<pre class="brush: ruby; title: ; notranslate">
- @topics.each do |topic|
    # ...
=will_paginate @topics
</pre>
<p>Bam. Doesn&#8217;t get much easier than that. You can get exceptionally creative with it, too. Effectively, all you need to know is:</p>
<ul>
<li>WillPaginate::Collection#new takes 3 parameters: the current page, the per-page count, and optionally, the total number of entries.</li>
<li>The <code>pager</code> block variable exposes <code>offset</code> and <code>per_page</code> properties, prime for passing into a DB query or slicing an enumerable with</li>
<li>Call pager.replace(sub-array) with the current page&#8217;s set of elements.</li>
</ul>
<p>That&#8217;s literally all there is to it. Now you can have easy pagination on just about any collection you can conceive of. Let WillPaginate handle all the heavy lifting and such. If you&#8217;ve done enough pagination by hand, you&#8217;ll probably appreciate the easy beauty of this particular method.</p>
]]></content:encoded>
			<wfw:commentRss>http://www.coffeepowered.net/2010/08/07/willpaginate-and-custom-paging/feed/</wfw:commentRss>
		<slash:comments>1</slash:comments>
		</item>
		<item>
		<title>counter_cache for MongoMapper</title>
		<link>http://www.coffeepowered.net/2010/02/15/counter_cache-for-mongomapper/</link>
		<comments>http://www.coffeepowered.net/2010/02/15/counter_cache-for-mongomapper/#comments</comments>
		<pubDate>Tue, 16 Feb 2010 02:30:48 +0000</pubDate>
		<dc:creator>Chris Heald</dc:creator>
				<category><![CDATA[MongoDB]]></category>
		<category><![CDATA[Rails]]></category>
		<category><![CDATA[Ruby]]></category>

		<guid isPermaLink="false">http://www.coffeepowered.net/?p=229</guid>
		<description><![CDATA[I&#8217;ve started playing with MongoMapper, and it&#8217;s quite excellent, but it does suffer very much from being young. There are lots of pieces missing that veterans of ActiveRecord will take for granted. I&#8217;ve been working around or patching them, for the most part, but I felt that my solution to `:counter_cache` deserved a post. In [...]]]></description>
			<content:encoded><![CDATA[<p>I&#8217;ve started playing with <a href="http://github.com/jnunemaker/mongomapper">MongoMapper</a>, and it&#8217;s quite excellent, but it does suffer very much from being young. There are lots of pieces missing that veterans of ActiveRecord will take for granted. I&#8217;ve been working around or patching them, for the most part, but I felt that my solution to `:counter_cache` deserved a post.</p>
<p>In short, I didn&#8217;t want to hack around with the MongoMapper associations code, so I just implemented my own little ride-along version.</p>
<pre class="brush: ruby; title: ; notranslate">
module SecretProject
  module CounterCache
    module ClassMethods
      def counter_cache(field)
        class_eval &lt;&lt;-EOF
          after_create &quot;increment_counter_for_#{field}&quot;
          after_destroy &quot;decrement_counter_for_#{field}&quot;
        EOF
      end
    end

    module InstanceMethods
      def method_missing(method, *args)
        if matches = method.to_s.match(/^(in|de)crement_counter_for_(.*)$/) then
          dir = matches[1] == &quot;in&quot; ? 1 : -1
          parent_association = matches[2]
          if parent = self.send(parent_association) then
            name = &quot;#{self.class.to_s.tableize}_count&quot;
            if parent.respond_to?(name)
              parent.collection.update({:_id =&gt; parent._id}, {&quot;$inc&quot; =&gt; {name =&gt; dir}})
            end
          end
        else
          super
        end
      end
    end

    def self.included(receiver)
      receiver.extend         ClassMethods
      receiver.send :include, InstanceMethods
    end
  end
end
</pre>
<p>Throw that into your lib directory, load it with an initializer, and then you can use it something like so:</p>
<pre class="brush: ruby; title: ; notranslate">
class Foo
  include MongoMapper::Document
  include SecretProject::CounterCache

  belongs_to :user
  counter_cache :user  # Will cause a foos_count field on the owning user to be maintained when a Foo is created or deleted.
end
</pre>
<p>This&#8217;ll only increment a counter if you&#8217;ve defined one on your parent object, via <code>key :foos_count, Integer</code> or similar, just so that it doesn&#8217;t go around updating every model you might associate it with.</p>
<p>Yay.</p>
]]></content:encoded>
			<wfw:commentRss>http://www.coffeepowered.net/2010/02/15/counter_cache-for-mongomapper/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>Safe action caching with Memcached</title>
		<link>http://www.coffeepowered.net/2010/02/10/safe-action-caching-with-memcached/</link>
		<comments>http://www.coffeepowered.net/2010/02/10/safe-action-caching-with-memcached/#comments</comments>
		<pubDate>Thu, 11 Feb 2010 04:04:04 +0000</pubDate>
		<dc:creator>Chris Heald</dc:creator>
				<category><![CDATA[Rails]]></category>
		<category><![CDATA[Ruby]]></category>

		<guid isPermaLink="false">http://www.coffeepowered.net/?p=223</guid>
		<description><![CDATA[I&#8217;ve started using action caching more aggressively, to handle a large volume of not-signed-in search traffic. It composes a significant chunk of my site&#8217;s total traffic, but there&#8217;s no good reason to be recomputing full pages for all those long-tail hits. So, the obvious thing is to just implement a quick action cache. This all [...]]]></description>
			<content:encoded><![CDATA[<p>I&#8217;ve started using action caching more aggressively, to handle a large volume of not-signed-in search traffic. It composes a significant chunk of my site&#8217;s total traffic, but there&#8217;s no good reason to be recomputing full pages for all those long-tail hits. So, the obvious thing is to just implement a quick action cache.</p>
<pre class="brush: ruby; title: ; notranslate">
# Controller
caches_action :show, :unless =&gt; :user?, :expires_in =&gt; 24.hours
</pre>
<pre class="brush: ruby; title: ; notranslate">
# Sweeper
expire_action :controller =&gt; &quot;nodes&quot;, :action =&gt; &quot;show&quot;, :id =&gt; record.to_param
</pre>
<p>This all works dandy, but I generate pretty URLs, which means sometimes there are characters in the URL that Memcached doesn&#8217;t like. A few minutes after deploying my patch, I started getting IMs from my logger bot telling me things were unhappy.</p>
<pre class="brush: ruby; title: ; notranslate">
blippr. com: [#1265856785] ArgumentError: illegal character in key &quot;views/m.blippr.com/apps/346562-PicFo g.mobile&quot;
blippr. com: [#1265857710] ArgumentError: illegal character in key &quot;views/www.blippr.com/apps/336714-µTorrent  &quot;
blippr. com: [#1265857897] ArgumentError: illegal character in key &quot;views/www.blippr.com/apps/337076-ustre am&quot;
blippr. com: [#1265857924] ArgumentError: illegal character in key &quot;views/www.blippr.com/apps/336714-µTorrent  &quot;
</pre>
<p>That&#8217;s memcached complaining about the hash keys we&#8217;re giving to it. This just won&#8217;t do. We could just regex out &#8220;bad&#8221; characters, but that means potential collisions, and potentially leaves edge cases. Why not just hash it instead?</p>
<p>A quick monkey patch later:</p>
<pre class="brush: ruby; title: ; notranslate">
class ActionController::Caching::Actions::ActionCachePath
	def path
		@cached_path ||= Digest::SHA1.hexdigest(@path)
	end
end
</pre>
<p>And we&#8217;re all dandy. Now, rather than caching by path, the path is hashed, and the hash is used as the path key. Since hashes will always be hexadecimal characters, we know that it&#8217;ll never make memcached unhappy.</p>
<pre class="brush: ruby; title: ; notranslate">
Path is blippr.com/movies/6696-The-Silence-of-the-Lambs...
Cached fragment hit: views/9111cdefca4a52cb0e3a5ebac4f618127a30efd0 (1.1ms)
</pre>
<p>There is an argument for not using this technique if you&#8217;re using file-based caching, since it means your cached bits won&#8217;t be segregated into directories, but memcached doesn&#8217;t support expiry by regex anyhow, so there&#8217;s no good reason to not use it in this case.</p>
<p>Enjoy!</p>
]]></content:encoded>
			<wfw:commentRss>http://www.coffeepowered.net/2010/02/10/safe-action-caching-with-memcached/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
	</channel>
</rss>

