<?xml version="1.0" encoding="utf-8"?>
<feed xmlns="http://www.w3.org/2005/Atom">
 
 <title>Mike Ferrier</title>
 <link href="http://mikeferrier.com/atom.xml" rel="self"/>
 <link href="http://mikeferrier.com/"/>
 <updated>2012-05-19T12:47:54-04:00</updated>
 <id>http://mikeferrier.com/</id>
 <author>
   <name>Mike Ferrier</name>
   <email>mike@mikeferrier.com</email>
 </author>

 
 <entry>
   <title>Rescuing multiple exception types in Ruby and binding to local variable</title>
   <link href="http://mikeferrier.com/2012/05/19/rescuing-multiple-exception-types-in-ruby-and-binding-to-local-variable/"/>
   <updated>2012-05-19T00:00:00-04:00</updated>
   <id>http://mikeferrier.com/2012/05/19/rescuing-multiple-exception-types-in-ruby-and-binding-to-local-variable</id>
   <content type="html">&lt;p&gt;Took me a few minutes to figure this out and wasn't easy to Google, so hopefully this helps someone out.&lt;/p&gt;

&lt;p&gt;Rescuing multiple exceptions in one &lt;code&gt;rescue&lt;/code&gt; clause is pretty intuitive:&lt;/p&gt;

&lt;div class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;ruby&quot;&gt;&lt;span class=&quot;k&quot;&gt;begin&lt;/span&gt;
  &lt;span class=&quot;n&quot;&gt;i&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;rand&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;2&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
  &lt;span class=&quot;n&quot;&gt;i&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;==&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;0&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;?&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;[]&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;+&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&amp;#39;&amp;#39;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;foo&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;rescue&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;TypeError&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;NameError&lt;/span&gt;
  &lt;span class=&quot;nb&quot;&gt;puts&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&amp;quot;oops&amp;quot;&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;


&lt;p&gt;I wanted to also bind the exception, whatever it is, to a local variable. To do that for a single exception is like:&lt;/p&gt;

&lt;div class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;ruby&quot;&gt;&lt;span class=&quot;k&quot;&gt;begin&lt;/span&gt;
  &lt;span class=&quot;o&quot;&gt;[]&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;+&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&amp;#39;&amp;#39;&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;rescue&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;TypeError&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;e&lt;/span&gt;
  &lt;span class=&quot;nb&quot;&gt;puts&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&amp;quot;oops: &lt;/span&gt;&lt;span class=&quot;si&quot;&gt;#{&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;e&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;message&lt;/span&gt;&lt;span class=&quot;si&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&amp;quot;&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;


&lt;p&gt;To combine the two, list the exceptions and then name the local variable with the last type in the list:&lt;/p&gt;

&lt;div class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;ruby&quot;&gt;&lt;span class=&quot;k&quot;&gt;begin&lt;/span&gt;
  &lt;span class=&quot;n&quot;&gt;i&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;rand&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;2&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
  &lt;span class=&quot;n&quot;&gt;i&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;==&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;0&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;?&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;[]&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;+&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&amp;#39;&amp;#39;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;foo&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;rescue&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;TypeError&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;NameError&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;e&lt;/span&gt;
  &lt;span class=&quot;nb&quot;&gt;puts&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&amp;quot;oops: &lt;/span&gt;&lt;span class=&quot;si&quot;&gt;#{&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;e&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;message&lt;/span&gt;&lt;span class=&quot;si&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&amp;quot;&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;



</content>
 </entry>
 
 <entry>
   <title>Using Google Latitude to map your travel</title>
   <link href="http://mikeferrier.com/2012/01/14/using-google-latitude-to-map-your-travel/"/>
   <updated>2012-01-14T00:00:00-05:00</updated>
   <id>http://mikeferrier.com/2012/01/14/using-google-latitude-to-map-your-travel</id>
   <content type="html">&lt;p&gt;&lt;a href=&quot;http://melissakaita.com&quot;&gt;Melissa&lt;/a&gt; and I just got back from our &lt;a href=&quot;http://melissakaita.com/blog/?p=1936&quot;&gt;mini-rtw&lt;/a&gt; trip to Asia, during which we had &lt;a href=&quot;http://iphonetrip.com&quot;&gt;data roaming on our phones&lt;/a&gt;. I kept the &lt;a href=&quot;https://www.google.com/latitude/&quot;&gt;Google Latitude&lt;/a&gt; app open in the background, which pings your location to Google every so often.&lt;/p&gt;

&lt;p&gt;When I got back and took a look at the map, the results were pretty cool. Some highlights:&lt;/p&gt;

&lt;p&gt;&lt;a href='/images/tokyo.png '&gt;&lt;img class='thumb' src='/images/tokyo.png ' /&gt; &lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Here's us going all over the place in Tokyo, with our home base in Shibuya. You can see the &lt;a href=&quot;http://en.wikipedia.org/wiki/Yamanote_line&quot;&gt;Yamanote Line&lt;/a&gt; loop mapped out clearly. You can also see our trip to the Imperial Palace in Chiyoda for the Emperor's New Year's address.&lt;/p&gt;

&lt;p&gt;&lt;a href='/images/hk-overview.png '&gt;&lt;img class='thumb' src='/images/hk-overview.png ' /&gt; &lt;/a&gt;&lt;/p&gt;

&lt;p&gt;During our Hong Kong stop we took the &lt;a href=&quot;http://www.youtube.com/watch?v=xgszOhSUdhQ&quot;&gt;Turbo Jetfoil&lt;/a&gt; to Macau -- you can see the ferry's path above.&lt;/p&gt;

&lt;p&gt;&lt;a href='/images/hk.png '&gt;&lt;img class='thumb' src='/images/hk.png ' /&gt; &lt;/a&gt;&lt;/p&gt;

&lt;p&gt;In Hong Kong we stayed in Aberdeen, which is on the south side of Hong Kong island. Every day we would take a taxi up to the north side which would cost about $60 HKD, or around $8 CAD. As you can see, sometimes the cab took the toll tunnel, other times the winding mountain roads. Also apparently we hung out in Wan Chai and Causeway Bay a lot.&lt;/p&gt;

&lt;p&gt;Here's the entire trip if you'd like to play around with it:&lt;/p&gt;

&lt;iframe width=&quot;700&quot; height=&quot;450&quot; frameborder=&quot;0&quot; scrolling=&quot;no&quot; marginheight=&quot;0&quot; marginwidth=&quot;0&quot; src=&quot;http://maps.google.com/maps/ms?msa=0&amp;amp;msid=213895207054466519835.0004b67e73af2dc97e672&amp;amp;ie=UTF8&amp;amp;t=w&amp;amp;vpsrc=6&amp;amp;source=embed&amp;amp;ll=36.11416,30.389066&amp;amp;spn=27.877052,220.003433&amp;amp;output=embed&quot;&gt;&lt;/iframe&gt;


&lt;br /&gt;&lt;small&gt;View &lt;a href=&quot;http://maps.google.com/maps/ms?msa=0&amp;amp;msid=213895207054466519835.0004b67e73af2dc97e672&amp;amp;ie=UTF8&amp;amp;t=w&amp;amp;vpsrc=6&amp;amp;source=embed&amp;amp;ll=36.11416,30.389066&amp;amp;spn=27.877052,220.003433&quot; style=&quot;color:#0000FF;text-align:left&quot;&gt;Asia Trip 2011&lt;/a&gt; in a larger map&lt;/small&gt;

</content>
 </entry>
 
 <entry>
   <title>Fake Cookie Store for Rails Unit Tests</title>
   <link href="http://mikeferrier.com/2011/11/25/fake-cookie-store-for-rails-unit-tests/"/>
   <updated>2011-11-25T00:00:00-05:00</updated>
   <id>http://mikeferrier.com/2011/11/25/fake-cookie-store-for-rails-unit-tests</id>
   <content type="html">&lt;p&gt;While writing tests for an AB testing library, I needed to simulate a Rails cookie store, but I didn't want to do any controller requests. Without doing a controller request, you aren't given any cookie store to test with, so I whipped up this fake cookie store that seems to work pretty well.&lt;/p&gt;

&lt;div class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;ruby&quot;&gt;&lt;span class=&quot;k&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;FakeCookieStore&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&amp;lt;&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;Hash&lt;/span&gt;
  &lt;span class=&quot;c1&quot;&gt;# allow for things like cookies.permanent.signed etc&lt;/span&gt;
  &lt;span class=&quot;k&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;method_missing&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;*&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;args&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&amp;amp;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;block&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;self&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;

  &lt;span class=&quot;k&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;[]=&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;key&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;value&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;c1&quot;&gt;# simulate cookies[key] = {:value =&amp;gt; &amp;#39;foo&amp;#39;}&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;value&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;is_a?&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;no&quot;&gt;Hash&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;v&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;value&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;ss&quot;&gt;:value&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
      &lt;span class=&quot;k&quot;&gt;super&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;key&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;v&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;else&lt;/span&gt;
      &lt;span class=&quot;k&quot;&gt;super&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;
  &lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;



</content>
 </entry>
 
 <entry>
   <title>Granting access to a single S3 bucket using Amazon IAM</title>
   <link href="http://mikeferrier.com/2011/10/27/granting-access-to-a-single-s3-bucket-using-amazon-iam/"/>
   <updated>2011-10-27T00:00:00-04:00</updated>
   <id>http://mikeferrier.com/2011/10/27/granting-access-to-a-single-s3-bucket-using-amazon-iam</id>
   <content type="html">&lt;p&gt;If you've ever used Amazon's &lt;a href=&quot;http://console.aws.amazon.com&quot;&gt;AWS console&lt;/a&gt; then you probably know that though sometimes it can be clunky, it has a ton of functionality for interacting with the various AWS serivces. So when I needed to give one of my coworkers at &lt;a href=&quot;http://4ormat.com&quot;&gt;4ormat&lt;/a&gt; access to one of our S3 buckets, I immediately investigated the laziest option: figuring out how they could login to the S3 console and use that to manage the bucket.&lt;/p&gt;

&lt;p&gt;The S3 console is pretty great. Uploading, downloading, creating folders, managing permissions, even copying and pasting buckets between files is a snap. If I could figure this out, I would save myself all the work of setting up &lt;a href=&quot;http://www.s3fox.net/&quot;&gt;S3Fox&lt;/a&gt; or even worse, writing an interface from scratch.&lt;/p&gt;

&lt;p&gt;After some trial and error, success! I've written a quick guideline on how to do this below.&lt;/p&gt;

&lt;h4&gt;1. Login to the IAM AWS console&lt;/h4&gt;


&lt;p&gt;Login &lt;a href=&quot;http://aws.amazon.com/console/&quot;&gt;here&lt;/a&gt; as the owner of the AWS account. Click the IAM tab.&lt;/p&gt;

&lt;h4&gt;2. Create an account alias&lt;/h4&gt;


&lt;p&gt;This step is optional, but it gives you a nice login URL for your users. Add an account alias in the &lt;em&gt;AWS Account Alias&lt;/em&gt; section of the IAM console. Then, your login URL will be &lt;em&gt;youralias&lt;/em&gt;.signin.aws.amazon.com.&lt;/p&gt;

&lt;p&gt;If you don't do this, your login page URL will be a bunch of random numbers.&lt;/p&gt;

&lt;h4&gt;3. Create a new group or a new user&lt;/h4&gt;


&lt;p&gt;With IAM you can create a group that has certain permissions, and then assign users to that group. Or, you can just create users piecemeal, but then you can't reuse permissions.&lt;/p&gt;

&lt;p&gt;If you want a group, create it first. Then create a user and assign it to that group.&lt;/p&gt;

&lt;h4&gt;4. Set a password for the new user&lt;/h4&gt;


&lt;p&gt;Click the new user you've created and then click the &lt;strong&gt;Security Credentials&lt;/strong&gt; tab. On that page, you can click &lt;strong&gt;Manage Password&lt;/strong&gt; to add a password for your user. Without a password, the user won't be able to login to the AWS console.&lt;/p&gt;

&lt;p&gt;Make sure your user knows to use the login page from step #2 in order to login &amp;mdash; they can't use the regular AWS login page.&lt;/p&gt;

&lt;p&gt;You'll notice your user also has a AWS access key created: API clients using this key will have the same permissions as the user would in the AWS console.&lt;/p&gt;

&lt;h4&gt;5. Add permissions for your user&lt;/h4&gt;


&lt;p&gt;Permissions are added either on the group the user is in, or if you decided not to create a group, the user account itself.&lt;/p&gt;

&lt;p&gt;Click the user or group, then click the &lt;strong&gt;Permissions&lt;/strong&gt; tab. Here you can see which permissions policies are currently attached to the group or user. Click the &lt;strong&gt;Attach Policy&lt;/strong&gt; button. You'll get a pop-up where you can Manage User Permissions. Here you can select a prerolled policy, use the Policy Generator, or just paste in a custom policy.&lt;/p&gt;

&lt;p&gt;There are two permissions that need to be added in order for your user to be able to login, see the bucket list in the S3 console, and manage the one bucket you've assigned.&lt;/p&gt;

&lt;p&gt;To manage the bucket, you need to grant the &lt;code&gt;s3:*&lt;/code&gt; action for the bucket you designate. AWS policies designate resources by their &lt;strong&gt;Amazon Resource Name&lt;/strong&gt;, or ARN and for S3 buckets, they look like: &lt;code&gt;arn:aws:s3:::bucket-name-here&lt;/code&gt;. So to grant your user full access to your bucket, you'd paste the policy:&lt;/p&gt;

&lt;div class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;js&quot;&gt;&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;s2&quot;&gt;&amp;quot;Statement&amp;quot;&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
      &lt;span class=&quot;s2&quot;&gt;&amp;quot;Action&amp;quot;&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&amp;quot;s3:*&amp;quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
      &lt;span class=&quot;s2&quot;&gt;&amp;quot;Effect&amp;quot;&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&amp;quot;Allow&amp;quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
      &lt;span class=&quot;s2&quot;&gt;&amp;quot;Resource&amp;quot;&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;
        &lt;span class=&quot;s2&quot;&gt;&amp;quot;arn:aws:s3:::4ormat-knowledge-base&amp;quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
        &lt;span class=&quot;s2&quot;&gt;&amp;quot;arn:aws:s3:::4ormat-knowledge-base/*&amp;quot;&lt;/span&gt;
      &lt;span class=&quot;p&quot;&gt;]&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
  &lt;span class=&quot;p&quot;&gt;]&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;


&lt;p&gt;Now, you would think that this would be enough to enable the user to use the S3 console to manage the bucket, but you'd be wrong. Turns out the user needs one more permission to do the initial listing of the buckets in order to be able to select a bucket, and its called &lt;code&gt;s3:ListAllMyBuckets&lt;/code&gt;. You need to add that permissions too, and it looks like this:&lt;/p&gt;

&lt;div class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;js&quot;&gt;&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;s2&quot;&gt;&amp;quot;Statement&amp;quot;&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
      &lt;span class=&quot;s2&quot;&gt;&amp;quot;Effect&amp;quot;&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&amp;quot;Allow&amp;quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
      &lt;span class=&quot;s2&quot;&gt;&amp;quot;Action&amp;quot;&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&amp;quot;s3:ListAllMyBuckets&amp;quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
      &lt;span class=&quot;s2&quot;&gt;&amp;quot;Resource&amp;quot;&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&amp;quot;arn:aws:s3:::*&amp;quot;&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
  &lt;span class=&quot;p&quot;&gt;]&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;




&lt;h4&gt; 6. Done! &lt;/h4&gt;


&lt;p&gt;You're done. Give the user their credentials and the login page, and then bask in the glory of laziness.&lt;/p&gt;
</content>
 </entry>
 
 <entry>
   <title>Vintages Values October 2011, Part Deux</title>
   <link href="http://mikeferrier.com/2011/10/23/vintages-values-october-2011--part-deux/"/>
   <updated>2011-10-23T00:00:00-04:00</updated>
   <id>http://mikeferrier.com/2011/10/23/vintages-values-october-2011--part-deux</id>
   <content type="html">&lt;p&gt;As promised in the &lt;a href=&quot;/2011/10/01/vintages-values-october-2011/&quot;&gt;previous post&lt;/a&gt;, I wrote a little widget so you can weight the different ratings sources that the LCBO uses. The table will re-sort when you change a weighting.&lt;/p&gt;

&lt;p&gt;Value is still calculated as &lt;strong&gt;critic rating points per dollar per millilitre&lt;/strong&gt;, but then that value is multiplied by the source weighting.&lt;/p&gt;

&lt;p&gt;Any more suggestions, let me know in the comments.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Thanks to &lt;a href=&quot;http://twitter.com/modernmod&quot;&gt;@modernmod&lt;/a&gt; for the idea.&lt;/em&gt;&lt;/p&gt;

&lt;div id=&quot;winelist-controls-container&quot;&gt;
&lt;/div&gt;




&lt;div id=&quot;winelist-table-container&quot;&gt;
&lt;/div&gt;




&lt;script id=&quot;winelist-controls&quot; type=&quot;text/x-handlebars-template&quot;&gt;
  &lt;ul&gt;
    {{#sources}}
    &lt;li class=&quot;source&quot; data-key=&quot;{{key}}&quot;&gt;
      &lt;div class=&quot;label&quot;&gt;&lt;a name=&quot;{{name}}&quot; href=&quot;#&quot;&gt;{{key}}&lt;/a&gt;&lt;/div&gt;
      &lt;div class=&quot;slider&quot;&gt;&lt;/div&gt;
      &lt;div class=&quot;value&quot;&gt;1.0&lt;/div&gt;
    &lt;/li&gt;
    {{/sources}}
  &lt;/ul class=&quot;winelist-controls&quot;&gt;
  &lt;div class=&quot;buttons&quot;&gt;
    &lt;button id=&quot;reset&quot;&gt;Reset&lt;/button&gt;
    &lt;button id=&quot;save&quot;&gt;Save&lt;/button&gt;
  &lt;/div&gt;
&lt;/script&gt;




&lt;script id=&quot;winelist-table&quot; type=&quot;text/x-handlebars-template&quot;&gt;
  &lt;table class=&quot;winelist&quot;&gt;
    &lt;thead&gt;&lt;tr&gt;
      &lt;th&gt;product&lt;/th&gt;
      &lt;th&gt;size(ml)&lt;/th&gt;
      &lt;th&gt;sku#&lt;/th&gt;
      &lt;th&gt;price&lt;/th&gt;
      &lt;th&gt;inventory&lt;/th&gt;
      &lt;th&gt;critic score&lt;/th&gt;
      &lt;th&gt;score&lt;/th&gt;
      &lt;th&gt;value&lt;/th&gt;
    &lt;/tr&gt;&lt;/thead&gt;
    &lt;tbody&gt;
      {{#products}}
        &lt;tr class=&quot;product&quot;&gt;
          &lt;td&gt;{{name}}&lt;/td&gt;
          &lt;td class=&quot;size&quot;&gt;{{size}}&lt;/td&gt;
          &lt;td&gt;{{sku}}&lt;/td&gt;
          &lt;td class=&quot;price&quot;&gt;$&lt;span&gt;{{money price}}&lt;/span&gt;&lt;/td&gt;
          &lt;td&gt;{{inventory}}&lt;/td&gt;
          &lt;td&gt;{{raw_score}}&lt;/td&gt;
          &lt;td class=&quot;score&quot; data-source=&quot;{{score_source}}&quot;&gt;{{absolute_score}}&lt;/td&gt;
          &lt;td class=&quot;value&quot;&gt;0.0&lt;/td&gt;
        &lt;/tr&gt;
      {{/products}}
    &lt;/tbody&gt;
  &lt;/table&gt;
&lt;/script&gt;

</content>
 </entry>
 
 <entry>
   <title>Vintages Values October 2011</title>
   <link href="http://mikeferrier.com/2011/10/01/vintages-values-october-2011/"/>
   <updated>2011-10-01T00:00:00-04:00</updated>
   <id>http://mikeferrier.com/2011/10/01/vintages-values-october-2011</id>
   <content type="html">&lt;p&gt;&lt;a href='/images/winecat.jpg '&gt;&lt;img class='thumb' src='/images/winecat.jpg ' /&gt; &lt;/a&gt;&lt;/p&gt;

&lt;p&gt;It's that time again. Vintages' October releases are here, and so is my faithful scraper script: bringing you the best values in the latest batch of Vintages releases.&lt;/p&gt;

&lt;p&gt;As with the &lt;a href=&quot;/2011/04/28/vintages-fancy-wine-deals-may-2011/&quot;&gt;previous post&lt;/a&gt;, value is calculated as &lt;strong&gt;critic rating points per dollar per millilitre&lt;/strong&gt;. I'm working on a little something that will let you, faithful reader, set weightings per critic.&lt;/p&gt;

&lt;p&gt;I should have something interested hooked up for next month. Watch for that!&lt;/p&gt;

&lt;p&gt;Without further ado...&lt;/p&gt;

&lt;p&gt;&lt;iframe width='850' height='500' frameborder='0' src='https://docs.google.com/spreadsheet/pub?hl=en_US&amp;hl=en_US&amp;key=0AuOu6QgPoWMwdHJKb190dDdaSktuQzNmTEhLelhuOGc&amp;single=true&amp;gid=1&amp;output=html'&gt;&lt;/iframe&gt;&lt;/p&gt;

</content>
 </entry>
 
 <entry>
   <title>Compiling EEE for Rubyscript2Exe on Mac OSX</title>
   <link href="http://mikeferrier.com/2011/05/29/compiling-eee-for-rubyscript2exe-on-mac-osx/"/>
   <updated>2011-05-29T00:00:00-04:00</updated>
   <id>http://mikeferrier.com/2011/05/29/compiling-eee-for-rubyscript2exe-on-mac-osx</id>
   <content type="html">&lt;p&gt;If you're having problems compiling EEE for Rubyscript2Exe or AllInOneRuby on OSX, this post will explain how to modify the source to compile properly.&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;http://www.erikveen.dds.nl/rubyscript2exe/#2.2.0&quot;&gt;Rubyscript2Exe&lt;/a&gt; is a framework to &quot;transform your Ruby application into a standalone, compressed Windows, Linux or Mac OS X (Darwin) executable.&quot; To do so, it depends on a little Pascal program called &lt;em&gt;Environment Embedding Executable&lt;/em&gt;, or EEE. It doesn't look like this stuff has been updated in a while, because I had problems compiling it on my OSX system. It seems that the EEE source code is not compatible with the latest &lt;a href=&quot;http://www.freepascal.org/&quot;&gt;FreePascal&lt;/a&gt; compiler.&lt;/p&gt;

&lt;p&gt;After &lt;a href=&quot;http://www.freepascal.org/fpcmac.html&quot;&gt;downloading&lt;/a&gt; and installing the latest &lt;strong&gt;fpc&lt;/strong&gt;, compiling barfs out some errors:&lt;/p&gt;

&lt;div class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;sh&quot;&gt;&lt;span class=&quot;nv&quot;&gt;$ &lt;/span&gt;fpc -Xs -B eee.pas
Free Pascal Compiler version 2.4.4 &lt;span class=&quot;o&quot;&gt;[&lt;/span&gt;2011/05/01&lt;span class=&quot;o&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;for &lt;/span&gt;i386
Copyright &lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;c&lt;span class=&quot;o&quot;&gt;)&lt;/span&gt; 1993-2010 by Florian Klaempfl
Target OS: Darwin &lt;span class=&quot;k&quot;&gt;for &lt;/span&gt;i386
Compiling eee.pas
eee.pas&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;151,49&lt;span class=&quot;o&quot;&gt;)&lt;/span&gt; Error: Parameters cannot contain &lt;span class=&quot;nb&quot;&gt;local type &lt;/span&gt;definitions. Use a separate &lt;span class=&quot;nb&quot;&gt;type &lt;/span&gt;definition in a &lt;span class=&quot;nb&quot;&gt;type &lt;/span&gt;block.
eee.pas&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;172,29&lt;span class=&quot;o&quot;&gt;)&lt;/span&gt; Error: Parameters cannot contain &lt;span class=&quot;nb&quot;&gt;local type &lt;/span&gt;definitions. Use a separate &lt;span class=&quot;nb&quot;&gt;type &lt;/span&gt;definition in a &lt;span class=&quot;nb&quot;&gt;type &lt;/span&gt;block.
eee.pas&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;204,43&lt;span class=&quot;o&quot;&gt;)&lt;/span&gt; Error: Parameters cannot contain &lt;span class=&quot;nb&quot;&gt;local type &lt;/span&gt;definitions. Use a separate &lt;span class=&quot;nb&quot;&gt;type &lt;/span&gt;definition in a &lt;span class=&quot;nb&quot;&gt;type &lt;/span&gt;block.
eee.pas&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;395,52&lt;span class=&quot;o&quot;&gt;)&lt;/span&gt; Error: Parameters cannot contain &lt;span class=&quot;nb&quot;&gt;local type &lt;/span&gt;definitions. Use a separate &lt;span class=&quot;nb&quot;&gt;type &lt;/span&gt;definition in a &lt;span class=&quot;nb&quot;&gt;type &lt;/span&gt;block.
eee.pas&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;395,70&lt;span class=&quot;o&quot;&gt;)&lt;/span&gt; Error: Parameters cannot contain &lt;span class=&quot;nb&quot;&gt;local type &lt;/span&gt;definitions. Use a separate &lt;span class=&quot;nb&quot;&gt;type &lt;/span&gt;definition in a &lt;span class=&quot;nb&quot;&gt;type &lt;/span&gt;block.
eee.pas&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;395,90&lt;span class=&quot;o&quot;&gt;)&lt;/span&gt; Error: Parameters cannot contain &lt;span class=&quot;nb&quot;&gt;local type &lt;/span&gt;definitions. Use a separate &lt;span class=&quot;nb&quot;&gt;type &lt;/span&gt;definition in a &lt;span class=&quot;nb&quot;&gt;type &lt;/span&gt;block.
eee.pas&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;421,52&lt;span class=&quot;o&quot;&gt;)&lt;/span&gt; Error: Parameters cannot contain &lt;span class=&quot;nb&quot;&gt;local type &lt;/span&gt;definitions. Use a separate &lt;span class=&quot;nb&quot;&gt;type &lt;/span&gt;definition in a &lt;span class=&quot;nb&quot;&gt;type &lt;/span&gt;block.
eee.pas&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;421,70&lt;span class=&quot;o&quot;&gt;)&lt;/span&gt; Error: Parameters cannot contain &lt;span class=&quot;nb&quot;&gt;local type &lt;/span&gt;definitions. Use a separate &lt;span class=&quot;nb&quot;&gt;type &lt;/span&gt;definition in a &lt;span class=&quot;nb&quot;&gt;type &lt;/span&gt;block.
eee.pas&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;421,90&lt;span class=&quot;o&quot;&gt;)&lt;/span&gt; Error: Parameters cannot contain &lt;span class=&quot;nb&quot;&gt;local type &lt;/span&gt;definitions. Use a separate &lt;span class=&quot;nb&quot;&gt;type &lt;/span&gt;definition in a &lt;span class=&quot;nb&quot;&gt;type &lt;/span&gt;block.
eee.pas&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;444,52&lt;span class=&quot;o&quot;&gt;)&lt;/span&gt; Error: Parameters cannot contain &lt;span class=&quot;nb&quot;&gt;local type &lt;/span&gt;definitions. Use a separate &lt;span class=&quot;nb&quot;&gt;type &lt;/span&gt;definition in a &lt;span class=&quot;nb&quot;&gt;type &lt;/span&gt;block.
eee.pas&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;444,70&lt;span class=&quot;o&quot;&gt;)&lt;/span&gt; Error: Parameters cannot contain &lt;span class=&quot;nb&quot;&gt;local type &lt;/span&gt;definitions. Use a separate &lt;span class=&quot;nb&quot;&gt;type &lt;/span&gt;definition in a &lt;span class=&quot;nb&quot;&gt;type &lt;/span&gt;block.
eee.pas&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;444,90&lt;span class=&quot;o&quot;&gt;)&lt;/span&gt; Error: Parameters cannot contain &lt;span class=&quot;nb&quot;&gt;local type &lt;/span&gt;definitions. Use a separate &lt;span class=&quot;nb&quot;&gt;type &lt;/span&gt;definition in a &lt;span class=&quot;nb&quot;&gt;type &lt;/span&gt;block.
eee.pas&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;489,52&lt;span class=&quot;o&quot;&gt;)&lt;/span&gt; Error: Parameters cannot contain &lt;span class=&quot;nb&quot;&gt;local type &lt;/span&gt;definitions. Use a separate &lt;span class=&quot;nb&quot;&gt;type &lt;/span&gt;definition in a &lt;span class=&quot;nb&quot;&gt;type &lt;/span&gt;block.
eee.pas&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;489,70&lt;span class=&quot;o&quot;&gt;)&lt;/span&gt; Error: Parameters cannot contain &lt;span class=&quot;nb&quot;&gt;local type &lt;/span&gt;definitions. Use a separate &lt;span class=&quot;nb&quot;&gt;type &lt;/span&gt;definition in a &lt;span class=&quot;nb&quot;&gt;type &lt;/span&gt;block.
eee.pas&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;489,90&lt;span class=&quot;o&quot;&gt;)&lt;/span&gt; Error: Parameters cannot contain &lt;span class=&quot;nb&quot;&gt;local type &lt;/span&gt;definitions. Use a separate &lt;span class=&quot;nb&quot;&gt;type &lt;/span&gt;definition in a &lt;span class=&quot;nb&quot;&gt;type &lt;/span&gt;block.
eee.pas&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;512,52&lt;span class=&quot;o&quot;&gt;)&lt;/span&gt; Error: Parameters cannot contain &lt;span class=&quot;nb&quot;&gt;local type &lt;/span&gt;definitions. Use a separate &lt;span class=&quot;nb&quot;&gt;type &lt;/span&gt;definition in a &lt;span class=&quot;nb&quot;&gt;type &lt;/span&gt;block.
eee.pas&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;512,70&lt;span class=&quot;o&quot;&gt;)&lt;/span&gt; Error: Parameters cannot contain &lt;span class=&quot;nb&quot;&gt;local type &lt;/span&gt;definitions. Use a separate &lt;span class=&quot;nb&quot;&gt;type &lt;/span&gt;definition in a &lt;span class=&quot;nb&quot;&gt;type &lt;/span&gt;block.
eee.pas&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;512,90&lt;span class=&quot;o&quot;&gt;)&lt;/span&gt; Error: Parameters cannot contain &lt;span class=&quot;nb&quot;&gt;local type &lt;/span&gt;definitions. Use a separate &lt;span class=&quot;nb&quot;&gt;type &lt;/span&gt;definition in a &lt;span class=&quot;nb&quot;&gt;type &lt;/span&gt;block.
eee.pas&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;535,52&lt;span class=&quot;o&quot;&gt;)&lt;/span&gt; Error: Parameters cannot contain &lt;span class=&quot;nb&quot;&gt;local type &lt;/span&gt;definitions. Use a separate &lt;span class=&quot;nb&quot;&gt;type &lt;/span&gt;definition in a &lt;span class=&quot;nb&quot;&gt;type &lt;/span&gt;block.
eee.pas&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;535,70&lt;span class=&quot;o&quot;&gt;)&lt;/span&gt; Error: Parameters cannot contain &lt;span class=&quot;nb&quot;&gt;local type &lt;/span&gt;definitions. Use a separate &lt;span class=&quot;nb&quot;&gt;type &lt;/span&gt;definition in a &lt;span class=&quot;nb&quot;&gt;type &lt;/span&gt;block.
eee.pas&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;535,90&lt;span class=&quot;o&quot;&gt;)&lt;/span&gt; Error: Parameters cannot contain &lt;span class=&quot;nb&quot;&gt;local type &lt;/span&gt;definitions. Use a separate &lt;span class=&quot;nb&quot;&gt;type &lt;/span&gt;definition in a &lt;span class=&quot;nb&quot;&gt;type &lt;/span&gt;block.
eee.pas&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;1164&lt;span class=&quot;o&quot;&gt;)&lt;/span&gt; Fatal: There were 21 errors compiling module, stopping
Fatal: Compilation aborted
Error: /usr/local/bin/ppc386 returned an error exitcode &lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;normal &lt;span class=&quot;k&quot;&gt;if &lt;/span&gt;you did not specify a &lt;span class=&quot;nb&quot;&gt;source &lt;/span&gt;file to be compiled&lt;span class=&quot;o&quot;&gt;)&lt;/span&gt;

&lt;span class=&quot;nv&quot;&gt;$ &lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;


&lt;p&gt;Turns out fpc has a problem with local type definitions in function parameters, and it considers static-length &lt;code&gt;string&lt;/code&gt; identifiers, such as &lt;code&gt;string[255]&lt;/code&gt;, to be type definitions. Apparently this was not so when eee.pas was first written, because the function definitions are full of &lt;code&gt;string[1]&lt;/code&gt;s and &lt;code&gt;string[255]&lt;/code&gt;s.&lt;/p&gt;

&lt;p&gt;To fix, we simply have to define both of those types on their own, and then replace all references to these types with the name of the defined types. Here's how I did it.&lt;/p&gt;

&lt;p&gt;First I added the types, one for  &lt;code&gt;string[1]&lt;/code&gt; and one for  &lt;code&gt;string[255]&lt;/code&gt;:&lt;/p&gt;

&lt;div class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;pascal&quot;&gt;&lt;span class=&quot;k&quot;&gt;type&lt;/span&gt;
  &lt;span class=&quot;n&quot;&gt;string255&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;string&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;255&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;;&lt;/span&gt;

&lt;span class=&quot;k&quot;&gt;type&lt;/span&gt;
  &lt;span class=&quot;n&quot;&gt;string1&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;string&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;


&lt;p&gt;Then I replaced all occurences of string[1] with string1, and string[255] with string255, e.g.:&lt;/p&gt;

&lt;div class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;pascal&quot;&gt;&lt;span class=&quot;k&quot;&gt;type&lt;/span&gt;

  &lt;span class=&quot;n&quot;&gt;header&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;  &lt;span class=&quot;k&quot;&gt;record&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;klasse&lt;/span&gt;  &lt;span class=&quot;o&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;string1&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;tekst&lt;/span&gt;    &lt;span class=&quot;o&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;string255&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;datalength&lt;/span&gt;  &lt;span class=&quot;o&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;longint&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;


&lt;p&gt;Once these changes are made, compiling is no problem:&lt;/p&gt;

&lt;div class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;sh&quot;&gt;&lt;span class=&quot;nv&quot;&gt;$ &lt;/span&gt;fpc -Xs -B eee_fixed.pas
Free Pascal Compiler version 2.4.4 &lt;span class=&quot;o&quot;&gt;[&lt;/span&gt;2011/05/01&lt;span class=&quot;o&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;for &lt;/span&gt;i386
Copyright &lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;c&lt;span class=&quot;o&quot;&gt;)&lt;/span&gt; 1993-2010 by Florian Klaempfl
Target OS: Darwin &lt;span class=&quot;k&quot;&gt;for &lt;/span&gt;i386
Compiling eee_fixed.pas
Assembling &lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;pipe&lt;span class=&quot;o&quot;&gt;)&lt;/span&gt; eee_fixed.s
Linking eee_fixed
1169 lines compiled, 0.4 sec

&lt;span class=&quot;nv&quot;&gt;$ &lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;


&lt;p&gt;You can download eee.pas with these changes made &lt;a href=&quot;/junk/eee_fixed.pas&quot;&gt;here&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;My pascal is pretty rusty, so if anyone has any pointers on improving this code, please let me know in the comments.&lt;/p&gt;
</content>
 </entry>
 
 <entry>
   <title>My beautiful dark twisted reverse-proxy LRU cache</title>
   <link href="http://mikeferrier.com/2011/05/14/my-beautiful-dark-twisted-reverse-proxy-LRU-cache/"/>
   <updated>2011-05-14T00:00:00-04:00</updated>
   <id>http://mikeferrier.com/2011/05/14/my-beautiful-dark-twisted-reverse-proxy-LRU-cache</id>
   <content type="html">&lt;p&gt;Reverse-proxy caching is generally one of the low-hanging fruit of scaling a site. If your reverse proxy is nginx, then you've probably seen the modules &lt;a href=&quot;http://wiki.nginx.org/HttpMemcachedModule&quot;&gt;HttpMemcachedModule&lt;/a&gt; and &lt;a href=&quot;http://wiki.nginx.org/HttpRedis&quot;&gt;HttpRedis&lt;/a&gt;, both of which are pretty good at fetching from memcached or redis based on a simple key.&lt;/p&gt;

&lt;p&gt;The config would look a little something like this:&lt;/p&gt;

&lt;div class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;nginx&quot;&gt;&lt;span class=&quot;k&quot;&gt;server&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;kn&quot;&gt;location&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;/&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;kn&quot;&gt;set&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;$memcached_key&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;$uri&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;kn&quot;&gt;memcached_pass&lt;/span&gt;     &lt;span class=&quot;s&quot;&gt;redis_server:11211&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;kn&quot;&gt;default_type&lt;/span&gt;       &lt;span class=&quot;s&quot;&gt;text/html&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;kn&quot;&gt;error_page&lt;/span&gt;         &lt;span class=&quot;mi&quot;&gt;404&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;@fallback&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
 
  &lt;span class=&quot;kn&quot;&gt;location&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;@fallback&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;kn&quot;&gt;proxy_pass&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;backend&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;


&lt;p&gt;But what if you want to do something a bit more complex? Say you wanted to do &lt;a href=&quot;http://blog.leetsoft.com/2007/5/22/the-secret-to-memcached&quot;&gt;namespaced caching&lt;/a&gt;, where there are two cache operations per request: one to fetch the version number of the requested resource, and a second to fetch the actual cached data from a key which interpolates the value of the first operation. Since HttpRedis only allows you to fetch values from redis based on a key computed in your nginx script, this isn't possible.&lt;/p&gt;

&lt;p&gt;Enter the nginx modules &lt;a href=&quot;https://github.com/agentzh/redis2-nginx-module&quot;&gt;Redis2&lt;/a&gt; and &lt;a href=&quot;https://github.com/chaoslawful/lua-nginx-module&quot;&gt;Lua&lt;/a&gt;. The former allows you to make any call you like to redis, as opposed to HttpRedis which only allows the plain old &lt;code&gt;GET&lt;/code&gt; command. The latter allows you do embed &lt;a href=&quot;http://www.lua.org/&quot;&gt;Lua&lt;/a&gt; scripts in your nginx config, effectively giving nginx a bigger brain and allowing you to do some pretty fancy stuff.&lt;/p&gt;

&lt;p&gt;In this post we'll set nginx and redis up to serve as a reverse-proxy &lt;a href=&quot;http://en.wikipedia.org/wiki/Cache_algorithms#Least_Recently_Used&quot;&gt;LRU cache&lt;/a&gt;. We recently started using this setup on &lt;a href=&quot;http://4ormat.com&quot;&gt;4ormat&lt;/a&gt; and it's sped up our site considerably and offloaded the hits to the application server by around 91%!&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;&lt;a href=&quot;#p1&quot;&gt;Install lua and redis.parser&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;#p2&quot;&gt;Build nginx with module support&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;#p3&quot;&gt;Redis structure&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;#p4&quot;&gt;Configure nginx&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;#p5&quot;&gt;Cache script&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;#p6&quot;&gt;Configure Redis&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;#p7&quot;&gt;Configure your app&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;#p8&quot;&gt;Conclusion and Benchmark&lt;/a&gt;&lt;/li&gt;
&lt;/ol&gt;


&lt;p&gt;&lt;a name=&quot;p1&quot;/&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h4&gt;1. Install lua and redis.parser &lt;/h4&gt;


&lt;p&gt;In order to build the nginx lua module, you'll need lua installed on your system. We also need the &lt;code&gt;redis.parser&lt;/code&gt; library in order to easily parse raw redis responses.&lt;/p&gt;

&lt;p&gt;On most systems, lua is already installed or is easily installed with your local package manager.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;OSX:&lt;/strong&gt;&lt;/p&gt;

&lt;div class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;sh&quot;&gt;brew install lua
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;


&lt;p&gt;&lt;strong&gt;Ubuntu:&lt;/strong&gt;&lt;/p&gt;

&lt;div class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;sh&quot;&gt;sudo apt-get install lua
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;


&lt;p&gt;&lt;strong&gt;Gentoo:&lt;/strong&gt;&lt;/p&gt;

&lt;div class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;sh&quot;&gt;sudo emerge lua
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;


&lt;p&gt;Once that's done, you just need the &lt;code&gt;redis.parser&lt;/code&gt; library:&lt;/p&gt;

&lt;div class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;sh&quot;&gt;&lt;span class=&quot;nv&quot;&gt;$ &lt;/span&gt;curl https://github.com/agentzh/lua-redis-parser/tarball/v0.04 -s -L -o lua-redis-parser.tar.gz

&lt;span class=&quot;nv&quot;&gt;$ &lt;/span&gt;tar zxvf lua-redis-parser.tar.gz
...untar output...

&lt;span class=&quot;nv&quot;&gt;$ &lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;cd &lt;/span&gt;agentzh-lua-redis-parser-ceffe35
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;


&lt;p&gt;On Linux, you can just type &lt;code&gt;make&lt;/code&gt; to build, but on OSX I found you have to do it by hand:&lt;/p&gt;

&lt;div class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;sh&quot;&gt;&lt;span class=&quot;c&quot;&gt;# linux:&lt;/span&gt;
&lt;span class=&quot;nv&quot;&gt;$ &lt;/span&gt;make
&lt;span class=&quot;c&quot;&gt;# osx:&lt;/span&gt;
&lt;span class=&quot;nv&quot;&gt;$ &lt;/span&gt;gcc -I/usr/local/Cellar/lua/include/ -O2 -fPIC -Wall -Werror -o parser.lo -c redis-parser.c
&lt;span class=&quot;nv&quot;&gt;$ &lt;/span&gt;gcc -o parser.so -bundle -undefined dynamic_lookup -fomit-frame-pointer parser.lo
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;


&lt;p&gt;Then, install the library:&lt;/p&gt;

&lt;div class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;sh&quot;&gt;&lt;span class=&quot;nv&quot;&gt;$ &lt;/span&gt;sudo make &lt;span class=&quot;nv&quot;&gt;INSTALL_PATH&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;/usr/local/lib/lua/5.1/ install
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;


&lt;p&gt;&lt;a name=&quot;p2&quot;/&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h4&gt;2. Build nginx with module support&lt;/h4&gt;


&lt;p&gt;Nginx does not support dynamic module loading, so in order to build new functionality into nginx you need to recompile it with the appropriate modules.&lt;/p&gt;

&lt;p&gt;Here's how I do it:&lt;/p&gt;

&lt;div class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;sh&quot;&gt;&lt;span class=&quot;nv&quot;&gt;$ &lt;/span&gt;curl http://nginx.org/download/nginx-1.0.0.tar.gz -s -O
&lt;span class=&quot;nv&quot;&gt;$ &lt;/span&gt;curl http://mdounin.ru/hg/ngx_http_upstream_keepalive/archive/tip.tar.gz -s -o ngx_http_upstream_keepalive.tar.gz
&lt;span class=&quot;nv&quot;&gt;$ &lt;/span&gt;curl https://github.com/chaoslawful/lua-nginx-module/tarball/v0.1.6rc5 -s -L -o lua-nginx-module.tar.gz
&lt;span class=&quot;nv&quot;&gt;$ &lt;/span&gt;curl https://github.com/agentzh/set-misc-nginx-module/tarball/v0.21rc3 -s -L -o &lt;span class=&quot;nb&quot;&gt;set&lt;/span&gt;-misc-nginx-module.tar.gz
&lt;span class=&quot;nv&quot;&gt;$ &lt;/span&gt;curl https://github.com/simpl/ngx_devel_kit/tarball/v0.2.17rc2 -s -L -o ngx_devel_kit.tar.gz
&lt;span class=&quot;nv&quot;&gt;$ &lt;/span&gt;curl https://github.com/agentzh/redis2-nginx-module/zipball/v0.06 -s -L -o redis2-nginx-module.tar.gz
&lt;span class=&quot;nv&quot;&gt;$ &lt;/span&gt;&lt;span class=&quot;k&quot;&gt;for &lt;/span&gt;f in *.gz; &lt;span class=&quot;k&quot;&gt;do &lt;/span&gt;tar xzvf &lt;span class=&quot;nv&quot;&gt;$f&lt;/span&gt;; rm -f &lt;span class=&quot;nv&quot;&gt;$f&lt;/span&gt;; &lt;span class=&quot;k&quot;&gt;done&lt;/span&gt;
...untar output...

&lt;span class=&quot;nv&quot;&gt;$ &lt;/span&gt;ls -1
agentzh-redis2-nginx-module-62f5b6a
agentzh-set-misc-nginx-module-4b0512a
chaoslawful-lua-nginx-module-0e0b0fc
nginx-1.0.0
ngx_http_upstream_keepalive-c6396fef9295
simpl-ngx_devel_kit-bc97eea

&lt;span class=&quot;nv&quot;&gt;$ &lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;cd &lt;/span&gt;nginx-1.0.0
&lt;span class=&quot;nv&quot;&gt;$ &lt;/span&gt;./configure &lt;span class=&quot;se&quot;&gt;\&lt;/span&gt;
&amp;gt;     --prefix&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;/opt &lt;span class=&quot;se&quot;&gt;\&lt;/span&gt;
&amp;gt;     --conf-path&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;/etc/nginx/nginx.conf &lt;span class=&quot;se&quot;&gt;\&lt;/span&gt;
&amp;gt;     --add-module&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;../agentzh-redis2-nginx-module-62f5b6a &lt;span class=&quot;se&quot;&gt;\&lt;/span&gt;
&amp;gt;     --add-module&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;../agentzh-set-misc-nginx-module-4b0512a &lt;span class=&quot;se&quot;&gt;\&lt;/span&gt;
&amp;gt;     --add-module&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;../chaoslawful-lua-nginx-module-0e0b0fc &lt;span class=&quot;se&quot;&gt;\&lt;/span&gt;
&amp;gt;     --add-module&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;../ngx_http_upstream_keepalive-c6396fef9295 &lt;span class=&quot;se&quot;&gt;\&lt;/span&gt;
&amp;gt;     --add-module&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;../simpl-ngx_devel_kit-bc97eea &lt;span class=&quot;se&quot;&gt;\&lt;/span&gt;
&amp;gt;     --add-module&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;/usr/local/rvm/gems/ruby-1.8.7-p330@default/gems/passenger-3.0.6/ext/nginx

...configure output...

Configuration summary
  + using system PCRE library
  + OpenSSL library is not used
  + md5: using system crypto library
  + sha1: using system crypto library
  + using system zlib library

  nginx path prefix: &lt;span class=&quot;s2&quot;&gt;&amp;quot;/opt&amp;quot;&lt;/span&gt;
  nginx binary file: &lt;span class=&quot;s2&quot;&gt;&amp;quot;/opt/sbin/nginx&amp;quot;&lt;/span&gt;
  nginx configuration prefix: &lt;span class=&quot;s2&quot;&gt;&amp;quot;/etc/nginx&amp;quot;&lt;/span&gt;
  nginx configuration file: &lt;span class=&quot;s2&quot;&gt;&amp;quot;/etc/nginx/nginx.conf&amp;quot;&lt;/span&gt;
  nginx pid file: &lt;span class=&quot;s2&quot;&gt;&amp;quot;/opt/logs/nginx.pid&amp;quot;&lt;/span&gt;
  nginx error log file: &lt;span class=&quot;s2&quot;&gt;&amp;quot;/opt/logs/error.log&amp;quot;&lt;/span&gt;
  nginx http access log file: &lt;span class=&quot;s2&quot;&gt;&amp;quot;/opt/logs/access.log&amp;quot;&lt;/span&gt;
  nginx http client request body temporary files: &lt;span class=&quot;s2&quot;&gt;&amp;quot;client_body_temp&amp;quot;&lt;/span&gt;
  nginx http proxy temporary files: &lt;span class=&quot;s2&quot;&gt;&amp;quot;proxy_temp&amp;quot;&lt;/span&gt;
  nginx http fastcgi temporary files: &lt;span class=&quot;s2&quot;&gt;&amp;quot;fastcgi_temp&amp;quot;&lt;/span&gt;
  nginx http uwsgi temporary files: &lt;span class=&quot;s2&quot;&gt;&amp;quot;uwsgi_temp&amp;quot;&lt;/span&gt;
  nginx http scgi temporary files: &lt;span class=&quot;s2&quot;&gt;&amp;quot;scgi_temp&amp;quot;&lt;/span&gt;

&lt;span class=&quot;nv&quot;&gt;$ &lt;/span&gt;make
...make output...

&lt;span class=&quot;nv&quot;&gt;$ &lt;/span&gt;make install
...install output...
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;


&lt;p&gt;A lot of these modules were written by the very awesome &lt;a href=&quot;https://github.com/agentzh&quot;&gt;agentzh&lt;/a&gt;, so a big thanks to him. He's working on something very exciting, a &lt;a href=&quot;https://github.com/agentzh/ngx_openresty&quot;&gt;full fledged web application server in nginx and lua&lt;/a&gt;! Will definitely be keeping my eye on that project.&lt;/p&gt;

&lt;p&gt;You'll notice I add the &lt;a href=&quot;http://www.modrails.com/&quot;&gt;Phusion Passenger&lt;/a&gt; module in with the path to my gem. You can omit that if you're not using Passenger.&lt;/p&gt;

&lt;p&gt;&lt;a name=&quot;p3&quot;/&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h4&gt;3. Redis structure&lt;/h4&gt;


&lt;p&gt;This is a good time to figure out where things are going to live in redis. We have two bits of information we need to store:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Resource versions, which will be numbers that we increment when something changes in the app in order to invalidate the cache&lt;/li&gt;
&lt;li&gt;Cached content, which will be written by the app after a cacheable request is complete, so that future requests can be served from cache&lt;/li&gt;
&lt;/ol&gt;


&lt;p&gt;For this post, I'm storing the resource version numbers in &quot;resource_versions&quot;, which will be a hash keyed by the request URI. The cached content itself will live in the root of the redis database, keyed by the request URI plus &quot;:version=n&quot;, where &lt;em&gt;n&lt;/em&gt; is the resource version. Like this:&lt;/p&gt;

&lt;div class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;javascript&quot;&gt;&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;s2&quot;&gt;&amp;quot;resource_versions&amp;quot;&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;s2&quot;&gt;&amp;quot;http://myproject.dev/&amp;quot;&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
    &lt;span class=&quot;s2&quot;&gt;&amp;quot;http://myproject.dev/foos&amp;quot;&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;2&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
    &lt;span class=&quot;s2&quot;&gt;&amp;quot;http://myproject.dev/foo/123&amp;quot;&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;3&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
  &lt;span class=&quot;p&quot;&gt;},&lt;/span&gt;
  &lt;span class=&quot;s2&quot;&gt;&amp;quot;http://myproject.dev/:version=1&amp;quot;&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&amp;quot;&amp;lt;html&amp;gt;...&amp;lt;/html&amp;gt;&amp;quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
  &lt;span class=&quot;s2&quot;&gt;&amp;quot;http://myproject.dev/foos:version=2&amp;quot;&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&amp;quot;&amp;lt;html&amp;gt;...&amp;lt;/html&amp;gt;&amp;quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
  &lt;span class=&quot;s2&quot;&gt;&amp;quot;http://myproject.dev/foo/123:version=3&amp;quot;&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&amp;quot;&amp;lt;html&amp;gt;...&amp;lt;/html&amp;gt;&amp;quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;


&lt;p&gt;Why not also store the cached content in a hash? The redis &lt;a href=&quot;http://antirez.com/post/redis-as-LRU-cache.html&quot;&gt;LRU eviction policy&lt;/a&gt; evicts keys from the root of the hash when the memory limit is reached; if there were only two keys, then either all our cached content would be evicted, or all our resource versions. Storing the cached content in the root ensures that it will be a least recently used bit of HTML that gets evicted.&lt;/p&gt;

&lt;p&gt;&lt;a name=&quot;p4&quot;/&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h4&gt;4. Configure nginx&lt;/h4&gt;


&lt;p&gt;First thing you need to do is an an &lt;code&gt;upstream&lt;/code&gt; block in your &lt;code&gt;nginx.conf&lt;/code&gt; pointing to your redis server:&lt;/p&gt;

&lt;div class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;nginx&quot;&gt;&lt;span class=&quot;c1&quot;&gt;# keepalive connection pool to a single redis running on localhost&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;upstream&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;redis&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;   
  &lt;span class=&quot;kn&quot;&gt;server&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;localhost&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;6379&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;

  &lt;span class=&quot;c1&quot;&gt;# a pool with at most 1024 connections&lt;/span&gt;
  &lt;span class=&quot;c1&quot;&gt;# and do not distinguish the servers:&lt;/span&gt;
  &lt;span class=&quot;kn&quot;&gt;keepalive&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;1024&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;single&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;


&lt;p&gt;Keepalive connections are more efficient than creating a new connection to redis every time; this is enabled by the the &lt;code&gt;ngx_http_upstream_keepalive&lt;/code&gt; module compiled into nginx.&lt;/p&gt;

&lt;p&gt;Nginx has a powerful internal system called &lt;a href=&quot;http://www.evanmiller.org/nginx-modules-guide-advanced.html#subrequests&quot;&gt;subrequests&lt;/a&gt;. It basically allows complex request rerouting internally to nginx that remains transparent to both the client and application. In this nginx config, internal locations are used to pass off requests further down the chain as necessary.&lt;/p&gt;

&lt;div class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;nginx&quot;&gt;&lt;span class=&quot;k&quot;&gt;server&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;kn&quot;&gt;listen&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;80&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;kn&quot;&gt;server_name&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;myproject.dev&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;kn&quot;&gt;root&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;/Users/mferrier/dev/myproject/public&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
  
  &lt;span class=&quot;kn&quot;&gt;location&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;/&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;c1&quot;&gt;# try to serve files directly from root, otherwise pass to @cache&lt;/span&gt;
    &lt;span class=&quot;kn&quot;&gt;try_files&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;$uri&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;@cache&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

  &lt;span class=&quot;kn&quot;&gt;location&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;@cache&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;kn&quot;&gt;internal&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;kn&quot;&gt;default_type&lt;/span&gt;   &lt;span class=&quot;s&quot;&gt;text/html&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;

    &lt;span class=&quot;kn&quot;&gt;set&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;$full_uri&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;$scheme://$host$request_uri&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;

    &lt;span class=&quot;kn&quot;&gt;content_by_lua_file&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&amp;#39;/Users/mferrier/dev/myproject/config/nginx.cache.lua&amp;#39;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;

    &lt;span class=&quot;kn&quot;&gt;error_page&lt;/span&gt;     &lt;span class=&quot;mi&quot;&gt;404&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;@app&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt; 
  &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

  &lt;span class=&quot;kn&quot;&gt;location&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;@app&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;kn&quot;&gt;internal&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;kn&quot;&gt;passenger_enabled&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;on&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;


&lt;p&gt;First we check for the existence of the requested file in the filesystem. If it doesn't exist, we pass control to &lt;code&gt;@cache&lt;/code&gt;. &lt;code&gt;@cache&lt;/code&gt; uses the &lt;code&gt;content_by_lua_file&lt;/code&gt; directive from the &lt;strong&gt;Lua&lt;/strong&gt; nginx module to specify that the content for this request should be generated by an external lua script. If that script returns a 404, then we pass control down to &lt;code&gt;@app&lt;/code&gt;. &lt;code&gt;@app&lt;/code&gt; is our application, in this case a Rails app handled by Passenger.&lt;/p&gt;

&lt;p&gt;The equals sign &lt;code&gt;=&lt;/code&gt; after the error code specifies to ultimately use the response code returned by &lt;code&gt;@app&lt;/code&gt; rather than the 404 returned by &lt;code&gt;@cache&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;We also need some internal locations to simplify the querying of redis. Our structure will require &lt;code&gt;GET&lt;/code&gt; requests for the cached content in the root of the database, and &lt;code&gt;HGET&lt;/code&gt; requests for the resource versions stored at &lt;code&gt;resource_versions&lt;/code&gt;.&lt;/p&gt;

&lt;div class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;nginx&quot;&gt;&lt;span class=&quot;c1&quot;&gt;# requires the &amp;quot;key&amp;quot; argument&lt;/span&gt;
&lt;span class=&quot;c1&quot;&gt;# http://redis.io/commands/get&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;location&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;/redis_get&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;kn&quot;&gt;internal&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;kn&quot;&gt;set_unescape_uri&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;$key&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;$arg_key&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;kn&quot;&gt;redis2_query&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;get&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;$key&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;

  &lt;span class=&quot;kn&quot;&gt;redis2_connect_timeout&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;200ms&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;kn&quot;&gt;redis2_send_timeout&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;200ms&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;kn&quot;&gt;redis2_read_timeout&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;200ms&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;kn&quot;&gt;redis2_pass&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;redis&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
  
  &lt;span class=&quot;kn&quot;&gt;error_page&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;500&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;501&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;502&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;503&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;504&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;505&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;@empty_string&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

&lt;span class=&quot;c1&quot;&gt;# requires the &amp;quot;hash_key and &amp;quot;key&amp;quot; argument&lt;/span&gt;
&lt;span class=&quot;c1&quot;&gt;# http://redis.io/commands/hget&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;location&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;/redis_hget&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;kn&quot;&gt;internal&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;kn&quot;&gt;set_unescape_uri&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;$key&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;$arg_key&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;kn&quot;&gt;redis2_query&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;hget&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;$arg_hash_key&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;$key&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;

  &lt;span class=&quot;kn&quot;&gt;redis2_connect_timeout&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;200ms&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;kn&quot;&gt;redis2_send_timeout&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;200ms&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;kn&quot;&gt;redis2_read_timeout&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;200ms&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;kn&quot;&gt;redis2_pass&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;redis&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
  
  &lt;span class=&quot;kn&quot;&gt;error_page&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;500&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;501&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;502&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;503&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;504&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;505&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;@empty_string&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

&lt;span class=&quot;c1&quot;&gt;# returns an empty string&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;location&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;@empty_string&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;kn&quot;&gt;internal&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;kn&quot;&gt;content_by_lua&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&amp;#39;ngx.print(&amp;quot;&amp;quot;)&amp;#39;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;


&lt;p&gt;Both &lt;code&gt;/redis_get&lt;/code&gt; and &lt;code&gt;/redis_hget&lt;/code&gt; are meant to be used in an &lt;code&gt;ngx_lua&lt;/code&gt; block with &lt;code&gt;ngx.location.capture&lt;/code&gt; and &lt;code&gt;redis.parser.parse_reply&lt;/code&gt; (more on that in the next step.)&lt;/p&gt;

&lt;p&gt;You'll also notice some sensible timeouts, and we pass control of any errors to the &lt;code&gt;@empty_string&lt;/code&gt; internal location, defined last. This location serves as a rescue, and returns an empty string back to &lt;code&gt;@cache&lt;/code&gt;, which treats an empty string as a cache miss. So if the redis server goes away or takes too long, it ends up being a cache miss rather than a 500 on the request.&lt;/p&gt;

&lt;p&gt;All that is needed now is the lua script which will check for cached content in redis, and either return that content or return a 404 if it wasn't found.&lt;/p&gt;

&lt;p&gt;&lt;a name=&quot;p5&quot;/&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h4&gt;5. Cache script&lt;/h4&gt;


&lt;p&gt;In the previous step, we specified that the content for the &lt;code&gt;@cache&lt;/code&gt; internal location was to be handled by the script &lt;code&gt;nginx.cache.lua&lt;/code&gt;. Every incoming request which isn't a request for a static file will be handled by this script, and it will either return some cached content, or pass the request along to &lt;code&gt;@app&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;This script needs to:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Determine if the request is cacheable based on the request method and request path, and return HTTP_NOT_FOUND if it isn't&lt;/li&gt;
&lt;li&gt;Try to grab the current version of the requested resource from redis, and return HTTP_NOT_FOUND if it doesn't exist&lt;/li&gt;
&lt;li&gt;Construct the cached content key for the requested resource from the result of the previous step&lt;/li&gt;
&lt;li&gt;Try to grab the cached content from redis using the cached content key from the previous step, and return HTTP_NOT_FOUND if no cached content is found Otherwise, we have a cache hit and can return the cached content&lt;/li&gt;
&lt;/ol&gt;


&lt;p&gt;Remember, a 404 response code in the &lt;code&gt;@cache&lt;/code&gt; location causes control to be passed to &lt;code&gt;@app&lt;/code&gt;.&lt;/p&gt;

&lt;div class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;lua&quot;&gt;&lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;ngx&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;var&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;request_method&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;~=&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&amp;quot;&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;GET&amp;quot;&lt;/span&gt; &lt;span class=&quot;ow&quot;&gt;and&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;ngx&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;var&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;request_method&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;~=&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&amp;quot;&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;HEAD&amp;quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;then&lt;/span&gt;
  &lt;span class=&quot;n&quot;&gt;ngx&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;log&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;ngx&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;NOTICE&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&amp;quot;&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;@cache: skipping uncacheable request method: &amp;quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;ngx&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;var&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;request_method&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
  &lt;span class=&quot;n&quot;&gt;ngx&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;exit&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;ngx&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;HTTP_NOT_FOUND&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;

&lt;span class=&quot;c1&quot;&gt;-- lua patterns, not regexes&lt;/span&gt;
&lt;span class=&quot;kd&quot;&gt;local&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;cacheable_resource_matchers&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;s2&quot;&gt;&amp;quot;&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;^/$&amp;quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
  &lt;span class=&quot;s2&quot;&gt;&amp;quot;&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;^/foo&amp;quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

&lt;span class=&quot;c1&quot;&gt;-- whether the request is cacheable&lt;/span&gt;
&lt;span class=&quot;kd&quot;&gt;local&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;cacheable&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;kc&quot;&gt;false&lt;/span&gt;

&lt;span class=&quot;k&quot;&gt;for&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;i&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;matcher&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;in&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;ipairs&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;cacheable_resource_matchers&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;do&lt;/span&gt;
  &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;string.find&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;ngx&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;var&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;uri&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;matcher&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;))&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;then&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;ngx&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;log&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;ngx&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;NOTICE&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&amp;quot;&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;@cache: cacheable request found: &amp;quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;ngx&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;var&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;host&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;ngx&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;var&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;uri&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;cacheable&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;kc&quot;&gt;true&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;break&lt;/span&gt;
  &lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;

&lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;cacheable&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;==&lt;/span&gt; &lt;span class=&quot;kc&quot;&gt;false&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;then&lt;/span&gt;
  &lt;span class=&quot;n&quot;&gt;ngx&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;log&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;ngx&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;NOTICE&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&amp;quot;&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;@cache: skipping uncacheable request: &amp;quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;ngx&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;var&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;host&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;ngx&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;var&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;uri&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
  &lt;span class=&quot;n&quot;&gt;ngx&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;exit&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;ngx&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;HTTP_NOT_FOUND&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;

&lt;span class=&quot;c1&quot;&gt;-- parser object that will receive redis responses and parse them into lua objects&lt;/span&gt;
&lt;span class=&quot;kd&quot;&gt;local&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;parser&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;require&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&amp;quot;&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;redis.parser&amp;quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;

&lt;span class=&quot;c1&quot;&gt;-- key in redis that stores the resource version numbers&lt;/span&gt;
&lt;span class=&quot;kd&quot;&gt;local&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;version_hash_key&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&amp;quot;&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;resource_versions&amp;quot;&lt;/span&gt;

&lt;span class=&quot;c1&quot;&gt;-- full uri of the request, used to construct the cache key&lt;/span&gt;
&lt;span class=&quot;kd&quot;&gt;local&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;full_uri&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;ngx&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;var&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;scheme&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;..&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&amp;quot;&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;://&amp;quot;&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;..&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;ngx&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;var&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;host&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;..&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;ngx&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;var&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;request_uri&lt;/span&gt;

&lt;span class=&quot;c1&quot;&gt;-- key under which the current version of this resource is stored&lt;/span&gt;
&lt;span class=&quot;kd&quot;&gt;local&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;version_key&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;ngx&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;var&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;request_uri&lt;/span&gt;
&lt;span class=&quot;kd&quot;&gt;local&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;response_body&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;ngx&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;location&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;capture&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&amp;quot;&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;/redis_hget&amp;quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; 
    &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;args&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;hash_key&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;version_hash_key&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;key&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;version_key&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;}}).&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;body&lt;/span&gt;
&lt;span class=&quot;kd&quot;&gt;local&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;res&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;typ&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;parser&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;parse_reply&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;response_body&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;

&lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;typ&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;==&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;parser&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;BULK_REPLY&lt;/span&gt; &lt;span class=&quot;ow&quot;&gt;and&lt;/span&gt; &lt;span class=&quot;ow&quot;&gt;not&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;res&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;==&lt;/span&gt; &lt;span class=&quot;kc&quot;&gt;nil&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;ow&quot;&gt;and&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;#&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;res&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&amp;gt;&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;))&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;then&lt;/span&gt;
  &lt;span class=&quot;n&quot;&gt;ngx&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;log&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;ngx&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;NOTICE&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&amp;quot;&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;@cache: cache HIT on version key &amp;quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;version_key&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&amp;quot;&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;, value: &amp;quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;res&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;

  &lt;span class=&quot;kd&quot;&gt;local&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;version_value&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;tonumber&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;res&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
  &lt;span class=&quot;kd&quot;&gt;local&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;cache_key&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;string.format&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&amp;quot;&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;%s:version=%s&amp;quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;full_uri&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;version_value&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
  &lt;span class=&quot;kd&quot;&gt;local&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;response_body&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;ngx&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;location&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;capture&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&amp;quot;&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;/redis_get&amp;quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; 
      &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;args&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;key&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;cache_key&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;}}).&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;body&lt;/span&gt;
  &lt;span class=&quot;kd&quot;&gt;local&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;res&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;typ&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;parser&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;parse_reply&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;response_body&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;

  &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;typ&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;==&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;parser&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;BULK_REPLY&lt;/span&gt; &lt;span class=&quot;ow&quot;&gt;and&lt;/span&gt; &lt;span class=&quot;ow&quot;&gt;not&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;res&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;==&lt;/span&gt; &lt;span class=&quot;kc&quot;&gt;nil&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;ow&quot;&gt;and&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;#&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;res&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&amp;gt;&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;))&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;then&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;ngx&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;log&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;ngx&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;NOTICE&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&amp;quot;&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;@cache: cache HIT on cache key: &amp;quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;cache_key&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&amp;quot;&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;, content length: &amp;quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;#&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;res&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;ngx&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;print&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;res&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;ngx&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;exit&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;ngx&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;OK&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
  &lt;span class=&quot;k&quot;&gt;else&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;ngx&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;log&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;ngx&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;NOTICE&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&amp;quot;&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;@cache: cache MISS on cache key: &amp;quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;cache_key&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
  &lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;else&lt;/span&gt;
  &lt;span class=&quot;n&quot;&gt;ngx&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;log&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;ngx&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;NOTICE&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&amp;quot;&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;@cache: cache MISS on version key: &amp;quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;version_key&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;

&lt;span class=&quot;n&quot;&gt;ngx&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;exit&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;ngx&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;HTTP_NOT_FOUND&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;


&lt;p&gt;In this script we do &lt;strong&gt;opt-in&lt;/strong&gt; caching: only the locations we specify in &lt;code&gt;cacheable_resource_matchers&lt;/code&gt; are eligible to be cached. The opposite of this would be &lt;strong&gt;opt-out&lt;/strong&gt; caching, where everything is cached unless we specify it shouldn't be. I find &lt;strong&gt;opt-in&lt;/strong&gt; caching to be safer, because it's generally worse to cache something that shouldn't be rather than &lt;em&gt;not&lt;/em&gt; cache something that should be.&lt;/p&gt;

&lt;p&gt;The cacheable resource matchers are &lt;a href=&quot;http://www.lua.org/pil/20.2.html&quot;&gt;Lua Patterns&lt;/a&gt;, which are kind of like regular expressions, but a bit less powerful: you can't use the logical OR operator &quot;|&quot;. If you need full blown regular expressions, there are lua regex libraries you can install.&lt;/p&gt;

&lt;p&gt;One of the great things about the lua nginx module is that code is automatically cached between requests. That means you don't have to worry about the &lt;code&gt;require&lt;/code&gt; statement in the lua script; it will only ever happen once.&lt;/p&gt;

&lt;p&gt;&lt;a name=&quot;p6&quot;/&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h4&gt;6. Configure Redis&lt;/h4&gt;


&lt;p&gt;Adding these two lines to your redis config will ensure that your memory usage never exceeds the limit you set:&lt;/p&gt;

&lt;div class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;sh&quot;&gt;maxmemory 64mb
maxmemory-policy allkeys-lru
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;


&lt;p&gt;&lt;a name=&quot;p7&quot;/&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h4&gt;7. Configure your app&lt;/h4&gt;


&lt;p&gt;All that remains to be done at this point is to set up your app so that it will do two things:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Write back the result of a cacheable request to redis&lt;/li&gt;
&lt;li&gt;Increment the resource version when a resource is updated&lt;/li&gt;
&lt;/ol&gt;


&lt;p&gt;Most of this should be customized to your particular app, but here's the basic framework I use in Rails.&lt;/p&gt;

&lt;p&gt;First we need to set up redis and the around_filter in &lt;code&gt;ApplicationController&lt;/code&gt;:&lt;/p&gt;

&lt;p&gt;&lt;code&gt;config/initializers/redis.rb&lt;/code&gt;&lt;/p&gt;

&lt;div class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;ruby&quot;&gt;&lt;span class=&quot;no&quot;&gt;REDIS_CONFIG&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;YAML&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;load_file&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;no&quot;&gt;Rails&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;root&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;join&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&amp;#39;config&amp;#39;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&amp;#39;redis.yml&amp;#39;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;))&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;no&quot;&gt;Rails&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;env&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;].&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;with_indifferent_access&lt;/span&gt;
&lt;span class=&quot;vg&quot;&gt;$REDIS&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;Redis&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;REDIS_CONFIG&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;


&lt;p&gt;&lt;code&gt;app/controllers/application_controller.rb&lt;/code&gt;:&lt;/p&gt;

&lt;div class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;ruby&quot;&gt;&lt;span class=&quot;k&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;ApplicationController&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&amp;lt;&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;ActionController&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;no&quot;&gt;Base&lt;/span&gt;
  &lt;span class=&quot;n&quot;&gt;around_filter&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;Cache&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;


&lt;p&gt;&lt;code&gt;app/controllers/cache.rb&lt;/code&gt;:&lt;/p&gt;

&lt;div class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;ruby&quot;&gt;&lt;span class=&quot;k&quot;&gt;module&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;Cache&lt;/span&gt;
  &lt;span class=&quot;no&quot;&gt;CACHEABLE_RESOURCE_MATCHERS&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;[&lt;/span&gt;
    &lt;span class=&quot;s2&quot;&gt;&amp;quot;^/$&amp;quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; 
    &lt;span class=&quot;s2&quot;&gt;&amp;quot;^/foo&amp;quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
  &lt;span class=&quot;o&quot;&gt;]&lt;/span&gt;
  
  &lt;span class=&quot;no&quot;&gt;VERSION_HASH_KEY&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&amp;quot;resource_versions&amp;quot;&lt;/span&gt;
  &lt;span class=&quot;no&quot;&gt;CACHE_KEY_FORMAT&lt;/span&gt;   &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&amp;quot;%s:version=%s&amp;quot;&lt;/span&gt;
  
  &lt;span class=&quot;k&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;request_cacheable?&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;request&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;kp&quot;&gt;false&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;unless&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;request&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;method&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;==&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&amp;quot;GET&amp;quot;&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;||&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;request&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;method&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;==&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&amp;quot;HEAD&amp;quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;no&quot;&gt;CACHEABLE_RESOURCE_MATCHERS&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;any?&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;do&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;|&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;pattern&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;|&lt;/span&gt;
      &lt;span class=&quot;n&quot;&gt;request&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;path&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=~&lt;/span&gt; &lt;span class=&quot;sr&quot;&gt;/&lt;/span&gt;&lt;span class=&quot;si&quot;&gt;#{&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;pattern&lt;/span&gt;&lt;span class=&quot;si&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;sr&quot;&gt;/&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;
  &lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;
  
  &lt;span class=&quot;k&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;response_cacheable?&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;response&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;response&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;status&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;==&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;200&lt;/span&gt;
  &lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;
  
  &lt;span class=&quot;k&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;version_key_for_request&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;request&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;s2&quot;&gt;&amp;quot;&lt;/span&gt;&lt;span class=&quot;si&quot;&gt;#{&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;request&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;scheme&lt;/span&gt;&lt;span class=&quot;si&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;://&lt;/span&gt;&lt;span class=&quot;si&quot;&gt;#{&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;request&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;host&lt;/span&gt;&lt;span class=&quot;si&quot;&gt;}#{&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;request&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;fullpath&lt;/span&gt;&lt;span class=&quot;si&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&amp;quot;&lt;/span&gt;
  &lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;
  
  &lt;span class=&quot;k&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;version_value_for_request&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;request&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;query_safely&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;do&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;|&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;cache&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;|&lt;/span&gt;
      &lt;span class=&quot;n&quot;&gt;cache&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;hget&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;no&quot;&gt;VERSION_HASH_KEY&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;version_key_for_request&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;request&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;))&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;
  &lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;
  
  &lt;span class=&quot;k&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;cache_key_for_request&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;request&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;version&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;full_url&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&amp;quot;&lt;/span&gt;&lt;span class=&quot;si&quot;&gt;#{&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;request&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;scheme&lt;/span&gt;&lt;span class=&quot;si&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;://&lt;/span&gt;&lt;span class=&quot;si&quot;&gt;#{&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;request&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;host&lt;/span&gt;&lt;span class=&quot;si&quot;&gt;}#{&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;request&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;fullpath&lt;/span&gt;&lt;span class=&quot;si&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&amp;quot;&lt;/span&gt;
    &lt;span class=&quot;no&quot;&gt;CACHE_KEY_FORMAT&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;%&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;full_url&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;version&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;]&lt;/span&gt;
  &lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;
  
  &lt;span class=&quot;k&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;cache_response&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;request&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;response&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;version_value&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;query_safely&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;do&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;|&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;cache&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;|&lt;/span&gt;
      &lt;span class=&quot;n&quot;&gt;cache&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;multi&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;do&lt;/span&gt;
        &lt;span class=&quot;c1&quot;&gt;# if version value is nil, ensure it hasn&amp;#39;t been set since the beginning&lt;/span&gt;
        &lt;span class=&quot;c1&quot;&gt;# of this request so that we don&amp;#39;t clobber it&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;version_value&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;||=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;version_value_for_request&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;request&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;version_value&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;nil?&lt;/span&gt;
          &lt;span class=&quot;n&quot;&gt;version_key&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;version_key_for_request&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;request&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
          &lt;span class=&quot;n&quot;&gt;log&lt;/span&gt; &lt;span class=&quot;sx&quot;&gt;%{initializing version key &amp;quot;&lt;/span&gt;&lt;span class=&quot;si&quot;&gt;#{&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;version_key&lt;/span&gt;&lt;span class=&quot;si&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;sx&quot;&gt;&amp;quot; with value &amp;quot;0&amp;quot;}&lt;/span&gt;
          &lt;span class=&quot;n&quot;&gt;cache&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;hset&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;no&quot;&gt;VERSION_HASH_KEY&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;version_key&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
          &lt;span class=&quot;n&quot;&gt;version_value&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;0&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;

        &lt;span class=&quot;n&quot;&gt;cache_key&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;cache_key_for_request&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;request&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;version_value&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;log&lt;/span&gt; &lt;span class=&quot;sx&quot;&gt;%{writing cache: key = &amp;quot;&lt;/span&gt;&lt;span class=&quot;si&quot;&gt;#{&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;cache_key&lt;/span&gt;&lt;span class=&quot;si&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;sx&quot;&gt;&amp;quot;, content length &lt;/span&gt;&lt;span class=&quot;si&quot;&gt;#{&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;response&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;body&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;length&lt;/span&gt;&lt;span class=&quot;si&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;sx&quot;&gt;)}&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;cache&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;set&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;cache_key&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;response&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;body&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
      &lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;
  &lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;
  
  
  &lt;span class=&quot;k&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;filter&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;controller&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;request_cacheable?&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;controller&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;request&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
      &lt;span class=&quot;n&quot;&gt;version_value&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;version_value_for_request&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;controller&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;request&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
      &lt;span class=&quot;k&quot;&gt;yield&lt;/span&gt;
      &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;response_cacheable?&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;controller&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;response&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;cache_response&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;controller&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;request&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;controller&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;response&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;version_value&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
      &lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;else&lt;/span&gt;
      &lt;span class=&quot;k&quot;&gt;yield&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;
  &lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;
  
  &lt;span class=&quot;k&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;invalidate_cache_for_url&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;url&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;query_safely&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;do&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;|&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;cache&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;|&lt;/span&gt;
      &lt;span class=&quot;n&quot;&gt;cache&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;hincrby&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;no&quot;&gt;VERSION_HASH_KEY&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;url&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;
  &lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;
  
  &lt;span class=&quot;k&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;log&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;msg&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;level&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;ss&quot;&gt;:info&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;no&quot;&gt;Rails&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;logger&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;send&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;level&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;sx&quot;&gt;%{[CACHE] &lt;/span&gt;&lt;span class=&quot;si&quot;&gt;#{&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;msg&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;to_s&lt;/span&gt;&lt;span class=&quot;si&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;sx&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
  &lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;
  
  &lt;span class=&quot;c1&quot;&gt;# yields the redis client, but catches connection errors. use this when you &lt;/span&gt;
  &lt;span class=&quot;c1&quot;&gt;# don&amp;#39;t mind if your cache operation fails silently.&lt;/span&gt;
  &lt;span class=&quot;k&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;query_safely&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;begin&lt;/span&gt;
      &lt;span class=&quot;k&quot;&gt;yield&lt;/span&gt; &lt;span class=&quot;vg&quot;&gt;$REDIS&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;rescue&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;Errno&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;no&quot;&gt;ECONNREFUSED&lt;/span&gt;
      &lt;span class=&quot;n&quot;&gt;log&lt;/span&gt; &lt;span class=&quot;sx&quot;&gt;%{ERROR: Connection refused while connecting to redis! Discarding query silently.}&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;ss&quot;&gt;:warn&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;rescue&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;Errno&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;no&quot;&gt;EAGAIN&lt;/span&gt;
      &lt;span class=&quot;n&quot;&gt;log&lt;/span&gt; &lt;span class=&quot;sx&quot;&gt;%{ERROR: Connection timeout while querying redis! Discarding query silently.}&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;ss&quot;&gt;:warn&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;
  &lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;


&lt;p&gt;It has been said that there are only two hard problems in computer science: naming things and cache invalidation. Implementing effective cache invalidation for your particular application is left as an exercise for the reader, but as an example let's say you have a Foo model whose current state affects the paths &lt;code&gt;/foos&lt;/code&gt; and &lt;code&gt;/foo/:id&lt;/code&gt;. You could write an observer like this:&lt;/p&gt;

&lt;p&gt;&lt;code&gt;app/models/foo_observer.rb&lt;/code&gt;:&lt;/p&gt;

&lt;div class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;ruby&quot;&gt;&lt;span class=&quot;k&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;FooObserver&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&amp;lt;&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;ActiveRecord&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;no&quot;&gt;Observer&lt;/span&gt;
  &lt;span class=&quot;k&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;after_save&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;record&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;unless&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;record&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;changed?&lt;/span&gt;

    &lt;span class=&quot;n&quot;&gt;afftected_urls&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;record&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;each&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;do&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;|&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;u&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;|&lt;/span&gt;
      &lt;span class=&quot;no&quot;&gt;Cache&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;invalidate_cache_for_url&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;u&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;
  &lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;
  
  &lt;span class=&quot;k&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;after_destroy&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;record&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;afftected_urls&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;record&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;each&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;do&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;|&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;u&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;|&lt;/span&gt;
      &lt;span class=&quot;no&quot;&gt;Cache&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;invalidate_cache_for_url&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;u&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;
  &lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;
  
  &lt;span class=&quot;k&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;affected_urls&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;record&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;o&quot;&gt;[&lt;/span&gt;
      &lt;span class=&quot;s2&quot;&gt;&amp;quot;http://myproject.com/foos&amp;quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
      &lt;span class=&quot;s2&quot;&gt;&amp;quot;http://myproject.com/foo/&lt;/span&gt;&lt;span class=&quot;si&quot;&gt;#{&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;record&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;to_param&lt;/span&gt;&lt;span class=&quot;si&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&amp;quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
    &lt;span class=&quot;o&quot;&gt;]&lt;/span&gt;
  &lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;


&lt;p&gt;&lt;code&gt;config/application.rb&lt;/code&gt;&lt;/p&gt;

&lt;div class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;ruby&quot;&gt;&lt;span class=&quot;k&quot;&gt;module&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;MyProject&lt;/span&gt;
  &lt;span class=&quot;k&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;Application&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&amp;lt;&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;Rails&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;no&quot;&gt;Application&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;config&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;active_record&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;observers&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;ss&quot;&gt;:foo_observer&lt;/span&gt;
  &lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;


&lt;p&gt;&lt;a name=&quot;p8&quot;/&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h4&gt;8. Conclusion and Benchmark&lt;/h4&gt;


&lt;p&gt;By serving content from nginx, you can easily see &lt;strong&gt;1000%&lt;/strong&gt; improvement in requests per second served. From a baseline Rails app served on a High CPU Medium instance on EC2 that serves around 100 requests per second, here's a benchmark once the caching is added:&lt;/p&gt;

&lt;div class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;sh&quot;&gt;&lt;span class=&quot;nv&quot;&gt;$ &lt;/span&gt;ab -n 1000 -c 100 http://testserver/
This is ApacheBench, Version 2.3 &amp;lt;&lt;span class=&quot;nv&quot;&gt;$Revision&lt;/span&gt;: 655654 &lt;span class=&quot;nv&quot;&gt;$&amp;gt;&lt;/span&gt;
Copyright 1996 Adam Twiss, Zeus Technology Ltd, http://www.zeustech.net/
Licensed to The Apache Software Foundation, http://www.apache.org/

Benchmarking ocadportfolio.com &lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;be patient&lt;span class=&quot;o&quot;&gt;)&lt;/span&gt;
Completed 100 requests
Completed 200 requests
Completed 300 requests
Completed 400 requests
Completed 500 requests
Completed 600 requests
Completed 700 requests
Completed 800 requests
Completed 900 requests
Completed 1000 requests
Finished 1000 requests


Server Software:        nginx/0.8.54
Server Hostname:        testserver
Server Port:            80

Document Path:          /features
Document Length:        8357 bytes

Concurrency Level:      100
Time taken &lt;span class=&quot;k&quot;&gt;for &lt;/span&gt;tests:   0.970 seconds
Complete requests:      1000
Failed requests:        0
Write errors:           0
Total transferred:      8540096 bytes
HTML transferred:       8394800 bytes
Requests per second:    1031.12 &lt;span class=&quot;o&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;c&quot;&gt;#/sec] (mean)&lt;/span&gt;
Time per request:       96.982 &lt;span class=&quot;o&quot;&gt;[&lt;/span&gt;ms&lt;span class=&quot;o&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;mean&lt;span class=&quot;o&quot;&gt;)&lt;/span&gt;
Time per request:       0.970 &lt;span class=&quot;o&quot;&gt;[&lt;/span&gt;ms&lt;span class=&quot;o&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;mean, across all concurrent requests&lt;span class=&quot;o&quot;&gt;)&lt;/span&gt;
Transfer rate:          8599.47 &lt;span class=&quot;o&quot;&gt;[&lt;/span&gt;Kbytes/sec&lt;span class=&quot;o&quot;&gt;]&lt;/span&gt; received

Connection Times &lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;ms&lt;span class=&quot;o&quot;&gt;)&lt;/span&gt;
              min  mean&lt;span class=&quot;o&quot;&gt;[&lt;/span&gt;+/-sd&lt;span class=&quot;o&quot;&gt;]&lt;/span&gt; median   max
Connect:        9   11   2.5     10      41
Processing:    22   83  32.2     84     263
Waiting:       11   73  32.4     74     254
Total:         32   94  32.6     95     274

Percentage of the requests served within a certain &lt;span class=&quot;nb&quot;&gt;time&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;ms&lt;span class=&quot;o&quot;&gt;)&lt;/span&gt;
  50%     95
  66%    117
  75%    119
  80%    121
  90%    129
  95%    142
  98%    163
  99%    168
 100%    274 &lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;longest request&lt;span class=&quot;o&quot;&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;


&lt;p&gt;Over 1000 requests per second, with a 96ms mean request time, and &lt;em&gt;less than 1ms mean time across all concurrent requests&lt;/em&gt;. This setup also has the nice side effect of being able to serve cached content even if &lt;em&gt;the application is completely down&lt;/em&gt;. Not too shabby!&lt;/p&gt;

&lt;p&gt;It's also worth noting that recently &lt;a href=&quot;http://antirez.com&quot;&gt;Salvatore Sanfilippo&lt;/a&gt;, author of redis, added experimental &lt;a href=&quot;http://antirez.com/post/an-update-on-redis-and-lua.html&quot;&gt;lua scripting&lt;/a&gt; to a side branch of redis. This seems to have gotten a lot of positive feedback from the community, and it should find its way into the master branch pretty soon. This basically adds the same embedded lua scripting support to redis, which means the same sorts of things implemented in nginx in this post could be achieved on the server side of redis without any special nginx modules.&lt;/p&gt;

&lt;p&gt;Code for this post can be found &lt;a href=&quot;/junk/caching_code.tar.gz&quot;&gt;here&lt;/a&gt;. If you have any questions, feel free to post in the comments.&lt;/p&gt;

&lt;p&gt;If you enjoyed this post, feel free to &lt;a href=&quot;http://news.ycombinator.com/item?id=2558733&quot;&gt;upvote on hackernews&lt;/a&gt;.&lt;/p&gt;
</content>
 </entry>
 
 <entry>
   <title>Blogging with Jekyll, Haml, Sass, and Jammit</title>
   <link href="http://mikeferrier.com/2011/04/29/blogging-with-jekyll-haml-sass-and-jammit/"/>
   <updated>2011-04-29T00:00:00-04:00</updated>
   <id>http://mikeferrier.com/2011/04/29/blogging-with-jekyll-haml-sass-and-jammit</id>
   <content type="html">&lt;p&gt;It seems anyone who uses &lt;a href=&quot;https://github.com/mojombo/jekyll&quot;&gt;Jekyll&lt;/a&gt; to run their blog ends up blogging about the particular way they've set it up. So here's mine.&lt;/p&gt;

&lt;p&gt;I came to Jekyll from Wordpress, which I found easy to set up but way too hard to change when something wasn't quite right. The great thing about Jekyll is that it doesn't generate anything on the fly &amp;mdash; all HTML is generated at once, meaning your web server only has to serve raw files. Less moving parts, less things that can break.&lt;/p&gt;

&lt;h4&gt;Haml/Sass for both templates and layouts&lt;/h4&gt;


&lt;p&gt;First thing I noticed was a lack of &lt;a href=&quot;http://haml-lang.com/&quot;&gt;Haml&lt;/a&gt; and &lt;a href=&quot;http://sass-lang.com/&quot;&gt;Sass&lt;/a&gt; support, which simply wouldn't do. Once you've had a taste of concise HTML/CSS, you never want to go back. While Jekyll doesn't support either out of the gate, a quick Googling revealed a very simple Haml/Sass converter script, which I added in &lt;code&gt;_plugins/haml_converter.rb&lt;/code&gt;:&lt;/p&gt;

&lt;div class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;ruby&quot;&gt;&lt;span class=&quot;k&quot;&gt;module&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;Jekyll&lt;/span&gt;
  &lt;span class=&quot;nb&quot;&gt;require&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&amp;#39;haml&amp;#39;&lt;/span&gt;
  &lt;span class=&quot;k&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;HamlConverter&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&amp;lt;&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;Converter&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;safe&lt;/span&gt; &lt;span class=&quot;kp&quot;&gt;true&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;priority&lt;/span&gt; &lt;span class=&quot;ss&quot;&gt;:low&lt;/span&gt;

    &lt;span class=&quot;k&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;matches&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;ext&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
      &lt;span class=&quot;n&quot;&gt;ext&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=~&lt;/span&gt; &lt;span class=&quot;sr&quot;&gt;/haml/i&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;

    &lt;span class=&quot;k&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;output_ext&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;ext&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
      &lt;span class=&quot;s2&quot;&gt;&amp;quot;.html&amp;quot;&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;

    &lt;span class=&quot;k&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;convert&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;content&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
      &lt;span class=&quot;n&quot;&gt;engine&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;Haml&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;no&quot;&gt;Engine&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;new&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;content&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
      &lt;span class=&quot;n&quot;&gt;engine&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;render&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;
  &lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;

  &lt;span class=&quot;nb&quot;&gt;require&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&amp;#39;sass&amp;#39;&lt;/span&gt;
  &lt;span class=&quot;k&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;SassConverter&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&amp;lt;&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;Converter&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;safe&lt;/span&gt; &lt;span class=&quot;kp&quot;&gt;true&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;priority&lt;/span&gt; &lt;span class=&quot;ss&quot;&gt;:low&lt;/span&gt;

     &lt;span class=&quot;k&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;matches&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;ext&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
      &lt;span class=&quot;n&quot;&gt;ext&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=~&lt;/span&gt; &lt;span class=&quot;sr&quot;&gt;/sass/i&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;

    &lt;span class=&quot;k&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;output_ext&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;ext&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
      &lt;span class=&quot;s2&quot;&gt;&amp;quot;.css&amp;quot;&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;

    &lt;span class=&quot;k&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;convert&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;content&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
      &lt;span class=&quot;n&quot;&gt;engine&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;Sass&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;no&quot;&gt;Engine&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;new&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;content&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
      &lt;span class=&quot;n&quot;&gt;engine&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;render&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;
  &lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;


&lt;p&gt;So far, so good. One problem though, Jekyll doesn't evaluate layouts through markup converters the same way it does with posts, so my layouts will have to be in plain HTML. &lt;em&gt;Or will they?&lt;/em&gt; I stuck my haml layouts in &lt;code&gt;_layouts/haml&lt;/code&gt;, and added a rake task, also lifted from Google, to Haml-ize any layouts:&lt;/p&gt;

&lt;div class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;ruby&quot;&gt;&lt;span class=&quot;n&quot;&gt;desc&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&amp;quot;Parse haml layouts&amp;quot;&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;task&lt;/span&gt; &lt;span class=&quot;ss&quot;&gt;:parse_haml&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;do&lt;/span&gt;
  &lt;span class=&quot;nb&quot;&gt;print&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&amp;quot;Parsing Haml layouts...&amp;quot;&lt;/span&gt;
  &lt;span class=&quot;nb&quot;&gt;system&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;sx&quot;&gt;%{&lt;/span&gt;
&lt;span class=&quot;sx&quot;&gt;    cd _layouts/haml &amp;amp;&amp;amp; &lt;/span&gt;
&lt;span class=&quot;sx&quot;&gt;    for f in *.haml; do [ -e $f ] &amp;amp;&amp;amp; haml $f ../${f%.haml}.html; done&lt;/span&gt;
&lt;span class=&quot;sx&quot;&gt;  }&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
  &lt;span class=&quot;nb&quot;&gt;puts&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&amp;quot;done.&amp;quot;&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;


&lt;p&gt;Add a pair of rake tasks to run &lt;code&gt;jekyll&lt;/code&gt; one-time or in preview mode:&lt;/p&gt;

&lt;div class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;ruby&quot;&gt;&lt;span class=&quot;n&quot;&gt;desc&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&amp;quot;Launch preview environment&amp;quot;&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;task&lt;/span&gt; &lt;span class=&quot;ss&quot;&gt;:preview&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;do&lt;/span&gt;
  &lt;span class=&quot;no&quot;&gt;Rake&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;no&quot;&gt;Task&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&amp;quot;parse_haml&amp;quot;&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;].&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;invoke&lt;/span&gt;
  &lt;span class=&quot;nb&quot;&gt;system&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&amp;quot;jekyll --auto --server&amp;quot;&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;

&lt;span class=&quot;n&quot;&gt;desc&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&amp;quot;Build site&amp;quot;&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;task&lt;/span&gt; &lt;span class=&quot;ss&quot;&gt;:build&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;do&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;|&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;task&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;args&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;|&lt;/span&gt;
  &lt;span class=&quot;no&quot;&gt;Rake&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;no&quot;&gt;Task&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&amp;quot;parse_haml&amp;quot;&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;].&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;invoke&lt;/span&gt;
  &lt;span class=&quot;nb&quot;&gt;system&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&amp;quot;jekyll&amp;quot;&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;




&lt;h4&gt;Minify assets with Jammit&lt;/h4&gt;


&lt;p&gt;&lt;a href=&quot;http://documentcloud.github.com/jammit/&quot;&gt;Jammit&lt;/a&gt; is an &quot;&lt;strong&gt;industrial strength asset packaging library for Rails&lt;/strong&gt;&quot; which combines groups of js scripts and css files into single asset files, then runs those assets through minifiers which shrink them and hopefully makes your pages load a bit faster. Jekyll is not Rails, but you can get Jammit working without too much trouble.&lt;/p&gt;

&lt;p&gt;First off, you need a YAML config file to configure Jammit. I put mine in &lt;code&gt;_assets.yml&lt;/code&gt; with the following contents:&lt;/p&gt;

&lt;div class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;yaml&quot;&gt;&lt;span class=&quot;l-Scalar-Plain&quot;&gt;javascript_compressor&lt;/span&gt;&lt;span class=&quot;p-Indicator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;l-Scalar-Plain&quot;&gt;closure&lt;/span&gt;

&lt;span class=&quot;l-Scalar-Plain&quot;&gt;javascripts&lt;/span&gt;&lt;span class=&quot;p-Indicator&quot;&gt;:&lt;/span&gt;
  &lt;span class=&quot;l-Scalar-Plain&quot;&gt;application&lt;/span&gt;&lt;span class=&quot;p-Indicator&quot;&gt;:&lt;/span&gt;
    &lt;span class=&quot;p-Indicator&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;l-Scalar-Plain&quot;&gt;_site/js/jquery-1.5.2.js&lt;/span&gt;
    &lt;span class=&quot;p-Indicator&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;l-Scalar-Plain&quot;&gt;_site/js/jquery.cookie.js&lt;/span&gt;
    &lt;span class=&quot;p-Indicator&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;l-Scalar-Plain&quot;&gt;_site/js/jquery.twitter.js&lt;/span&gt;
    &lt;span class=&quot;p-Indicator&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;l-Scalar-Plain&quot;&gt;_site/js/app.js&lt;/span&gt;

&lt;span class=&quot;l-Scalar-Plain&quot;&gt;stylesheets&lt;/span&gt;&lt;span class=&quot;p-Indicator&quot;&gt;:&lt;/span&gt;
  &lt;span class=&quot;l-Scalar-Plain&quot;&gt;screen&lt;/span&gt;&lt;span class=&quot;p-Indicator&quot;&gt;:&lt;/span&gt;
    &lt;span class=&quot;p-Indicator&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;l-Scalar-Plain&quot;&gt;_site/css/screen.css&lt;/span&gt;
    &lt;span class=&quot;p-Indicator&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;l-Scalar-Plain&quot;&gt;_site/css/syntax.css&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;


&lt;p&gt;Now when I run &lt;code&gt;jammit -o assets -c _assets.yml&lt;/code&gt;, I get nice streamlined javascript and css assets. However, one of the things Jammit does with Rails is add in helper methods for including the groups of assets into your template files. To emulate this, I had to add some special tags to Jekyll.&lt;/p&gt;

&lt;p&gt;Jekyll uses &lt;a href=&quot;http://www.liquidmarkup.org/&quot;&gt;Liquid&lt;/a&gt; for templating, and adding new tags in Liquid is easy. &lt;code&gt;_plugins/asset_tag.rb&lt;/code&gt;:&lt;/p&gt;

&lt;div class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;ruby&quot;&gt;&lt;span class=&quot;k&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;AssetTag&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&amp;lt;&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;Liquid&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;no&quot;&gt;Tag&lt;/span&gt;
  &lt;span class=&quot;no&quot;&gt;CONFIG&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;YAML&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;load_file&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&amp;quot;_assets.yml&amp;quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
  
  &lt;span class=&quot;k&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;initialize&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;tag_name&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;kind&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;tokens&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;super&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;tag_name&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;tokens&lt;/span&gt;
    &lt;span class=&quot;vi&quot;&gt;@name&lt;/span&gt;   &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;to_s&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;strip&lt;/span&gt;
    &lt;span class=&quot;vi&quot;&gt;@kind&lt;/span&gt;   &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;kind&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;to_s&lt;/span&gt;
  &lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;

  &lt;span class=&quot;k&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;render&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;context&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;Jekyll&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;no&quot;&gt;ENV&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;==&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&amp;#39;production&amp;#39;&lt;/span&gt;
      &lt;span class=&quot;n&quot;&gt;markup&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&amp;quot;/assets/&lt;/span&gt;&lt;span class=&quot;si&quot;&gt;#{&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;name_with_ext&lt;/span&gt;&lt;span class=&quot;si&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&amp;quot;&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;else&lt;/span&gt;
      &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;assets_for_name&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;map&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;do&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;|&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;asset&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;|&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;markup&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&amp;quot;&lt;/span&gt;&lt;span class=&quot;si&quot;&gt;#{&lt;/span&gt;&lt;span class=&quot;vi&quot;&gt;@path&lt;/span&gt;&lt;span class=&quot;si&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;/&lt;/span&gt;&lt;span class=&quot;si&quot;&gt;#{&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;asset&lt;/span&gt;&lt;span class=&quot;si&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&amp;quot;&lt;/span&gt;
      &lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;join&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&amp;quot;&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\n&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&amp;quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;
  &lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;
  
  &lt;span class=&quot;k&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;name_with_ext&lt;/span&gt;
    &lt;span class=&quot;s2&quot;&gt;&amp;quot;&lt;/span&gt;&lt;span class=&quot;si&quot;&gt;#{&lt;/span&gt;&lt;span class=&quot;vi&quot;&gt;@name&lt;/span&gt;&lt;span class=&quot;si&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;si&quot;&gt;#{&lt;/span&gt;&lt;span class=&quot;vi&quot;&gt;@ext&lt;/span&gt;&lt;span class=&quot;si&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&amp;quot;&lt;/span&gt;
  &lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;
  
  &lt;span class=&quot;k&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;assets_for_name&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;CONFIG&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;vi&quot;&gt;@asset_type&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;].&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;include?&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;vi&quot;&gt;@name&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
      &lt;span class=&quot;no&quot;&gt;CONFIG&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;vi&quot;&gt;@asset_type&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;][&lt;/span&gt;&lt;span class=&quot;vi&quot;&gt;@name&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;].&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;map&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;do&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;|&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;asset&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;|&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;asset&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;gsub&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;sr&quot;&gt;/_site\/(css|js)\//&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&amp;#39;&amp;#39;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
      &lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;else&lt;/span&gt;
      &lt;span class=&quot;n&quot;&gt;name_with_ext&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;
  &lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;



&lt;span class=&quot;k&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;IncludeJsTag&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&amp;lt;&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;AssetTag&lt;/span&gt;
  &lt;span class=&quot;k&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;initialize&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;tag_name&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;tokens&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;vi&quot;&gt;@path&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&amp;#39;/js&amp;#39;&lt;/span&gt;
    &lt;span class=&quot;vi&quot;&gt;@ext&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&amp;#39;js&amp;#39;&lt;/span&gt;
    &lt;span class=&quot;vi&quot;&gt;@asset_type&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&amp;#39;javascripts&amp;#39;&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;super&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;tag_name&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&amp;#39;js&amp;#39;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;tokens&lt;/span&gt;
  &lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;
  
  &lt;span class=&quot;k&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;markup&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;src&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;sx&quot;&gt;%{&amp;lt;script src=&amp;#39;&lt;/span&gt;&lt;span class=&quot;si&quot;&gt;#{&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;src&lt;/span&gt;&lt;span class=&quot;si&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;sx&quot;&gt;&amp;#39; type=&amp;#39;text/javascript&amp;#39;&amp;gt;&amp;lt;/script&amp;gt;}&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;to_s&lt;/span&gt;
  &lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;  
&lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;

&lt;span class=&quot;no&quot;&gt;Liquid&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;no&quot;&gt;Template&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;register_tag&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&amp;#39;include_js&amp;#39;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;IncludeJsTag&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;



&lt;span class=&quot;k&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;IncludeCssTag&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&amp;lt;&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;AssetTag&lt;/span&gt;
  &lt;span class=&quot;k&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;initialize&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;tag_name&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;tokens&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;vi&quot;&gt;@path&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&amp;#39;/css&amp;#39;&lt;/span&gt;
    &lt;span class=&quot;vi&quot;&gt;@ext&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&amp;#39;css&amp;#39;&lt;/span&gt;
    &lt;span class=&quot;vi&quot;&gt;@asset_type&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&amp;#39;stylesheets&amp;#39;&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;super&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;tag_name&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&amp;#39;css&amp;#39;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;tokens&lt;/span&gt;
  &lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;

  &lt;span class=&quot;k&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;markup&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;src&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;sx&quot;&gt;%{&amp;lt;link href=&amp;#39;&lt;/span&gt;&lt;span class=&quot;si&quot;&gt;#{&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;src&lt;/span&gt;&lt;span class=&quot;si&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;sx&quot;&gt;&amp;#39; media=&amp;#39;screen&amp;#39; rel=&amp;#39;stylesheet&amp;#39; type=&amp;#39;text/css&amp;#39; /&amp;gt;}&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;to_s&lt;/span&gt;
  &lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;

&lt;span class=&quot;no&quot;&gt;Liquid&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;no&quot;&gt;Template&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;register_tag&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&amp;#39;include_css&amp;#39;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;IncludeCssTag&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;


&lt;p&gt;And then in my layout: I can use &lt;code&gt;{% include_css screen %}&lt;/code&gt; and &lt;code&gt;{% include_js application %}&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;You may have noticed &lt;code&gt;Jekyll::ENV == 'production'&lt;/code&gt; in there. I don't want to use my minified assets while I'm writing posts because if there's a javascript or css problem, it's much harder to debug minified code. So I introduce the concept of a Jekyll environment variable in &lt;code&gt;_plugin/env.rb&lt;/code&gt;, where I simply check for the variable in the shell environment:&lt;/p&gt;

&lt;div class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;ruby&quot;&gt;&lt;span class=&quot;no&quot;&gt;Jekyll&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;no&quot;&gt;ENV&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;no&quot;&gt;ENV&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&amp;#39;JEKYLL_ENV&amp;#39;&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;||&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&amp;#39;development&amp;#39;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;


&lt;p&gt;And then we bring it all together in a rake task, to be run when I'm ready to push my latest changes up to my live blog:&lt;/p&gt;

&lt;div class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;ruby&quot;&gt;&lt;span class=&quot;n&quot;&gt;desc&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&amp;quot;Package app for production&amp;quot;&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;task&lt;/span&gt; &lt;span class=&quot;ss&quot;&gt;:package&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;do&lt;/span&gt;
  &lt;span class=&quot;no&quot;&gt;ENV&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&amp;#39;JEKYLL_ENV&amp;#39;&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&amp;#39;production&amp;#39;&lt;/span&gt;
  
  &lt;span class=&quot;no&quot;&gt;Rake&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;no&quot;&gt;Task&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&amp;quot;build&amp;quot;&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;].&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;invoke&lt;/span&gt;

  &lt;span class=&quot;nb&quot;&gt;print&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&amp;quot;Compressing assets...&amp;quot;&lt;/span&gt;
  &lt;span class=&quot;nb&quot;&gt;system&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&amp;quot;jammit -o assets -c _assets.yml&amp;quot;&lt;/span&gt;
  &lt;span class=&quot;nb&quot;&gt;puts&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&amp;quot;done.&amp;quot;&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;


&lt;p&gt;Lastly, I have a rake task to deploy:&lt;/p&gt;

&lt;div class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;ruby&quot;&gt;&lt;span class=&quot;n&quot;&gt;desc&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&amp;quot;Deploy latest code in _site to production&amp;quot;&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;task&lt;/span&gt; &lt;span class=&quot;ss&quot;&gt;:deploy&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;do&lt;/span&gt;
  &lt;span class=&quot;nb&quot;&gt;system&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;sx&quot;&gt;%{&lt;/span&gt;
&lt;span class=&quot;sx&quot;&gt;    rsync -avz --delete _site/ deploy@mikeferrier.com:/srv/www/mikeferrier.com/&lt;/span&gt;
&lt;span class=&quot;sx&quot;&gt;  }&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;


&lt;p&gt;So my post-writing routine basically looks like this:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Start up jekyll with &lt;code&gt;rake preview&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;Write a post&lt;/li&gt;
&lt;li&gt;When I'm satified, run &lt;code&gt;rake package deploy&lt;/code&gt;&lt;/li&gt;
&lt;/ol&gt;


&lt;p&gt;Nice and simple. So far I'm loving the ease and flexibility, and not missing Wordpress one bit. Maybe one day I'll be able to convince &lt;a href=&quot;http://melissakaita.com&quot;&gt;Melissa&lt;/a&gt; to switch too (not bloody likely.)&lt;/p&gt;
</content>
 </entry>
 
 <entry>
   <title>Vintages Fancy Wine Deals May 2011</title>
   <link href="http://mikeferrier.com/2011/04/28/vintages-fancy-wine-deals-may-2011/"/>
   <updated>2011-04-28T00:00:00-04:00</updated>
   <id>http://mikeferrier.com/2011/04/28/vintages-fancy-wine-deals-may-2011</id>
   <content type="html">&lt;p&gt;&lt;a href='/images/vcc-2011-05.jpg '&gt;&lt;img class='thumb' src='/images/vcc-2011-05.jpg ' /&gt; &lt;/a&gt;&lt;/p&gt;

&lt;p&gt;I really don't know much about wine, except that I like drinking it. To help people like me, &lt;a href=&quot;http://vintages.com&quot;&gt;Vintages&lt;/a&gt; includes the ratings of the &lt;a href=&quot;http://www.erobertparker.com/&quot;&gt;various&lt;/a&gt; &lt;a href=&quot;http://www.winespectator.com/&quot;&gt;wine&lt;/a&gt; &lt;a href=&quot;http://www.wineaccess.com/&quot;&gt;critics&lt;/a&gt; in their &lt;a href=&quot;http://www.vintages.com/classics/cc_main.shtml&quot;&gt;upcoming release list&lt;/a&gt;, which, though clearly subjective, are somewhat of a good indicator of the highs and lows of the list. When I'm looking for the catalog's best values, I like to compare price vs. rating.&lt;/p&gt;

&lt;p&gt;So for anyone else in Ontario who enjoys the value of a highly rated wine for cheap, here's the output of a script I wrote to pull down the latest VCC releases, calculate the value of each (as defined as rating points per dollar per millilitre), and sort descending by value. Enjoy!&lt;/p&gt;

&lt;p&gt;&lt;iframe width='850' height='500' frameborder='0' src='https://spreadsheets.google.com/spreadsheet/pub?hl=en&amp;hl=en&amp;key=0AuOu6QgPoWMwdDVXQ2VlTlNfZ0RueDIwb2ltaWwwVGc&amp;single=true&amp;gid=0&amp;output=html&amp;widget=true'&gt;&lt;/iframe&gt;&lt;/p&gt;


&lt;p&gt;Incidentally, my most knowledgeable wine buddy &lt;a href=&quot;http://twitter.com/modernmod&quot;&gt;Duarte&lt;/a&gt; hinted that some critic points are more valuable than others. Next release I'll see if I can weight certain critic points higher than others. Snobbery!&lt;/p&gt;

&lt;p&gt;Also, for general LCBO searching, skip the official website and go right to &lt;a href=&quot;http://heycarsten.com/&quot;&gt;Carsten Nielsen&lt;/a&gt;'s &lt;a href=&quot;http://lcbosearch.com&quot;&gt;lcbosearch.com&lt;/a&gt; (based on his awesome &lt;a href=&quot;http://lcboapi.com&quot;&gt;lcboapi.com&lt;/a&gt;). It's about 1000% better looking and easier to use.&lt;/p&gt;
</content>
 </entry>
 
 <entry>
   <title>Sailing to Norway</title>
   <link href="http://mikeferrier.com/2011/04/24/sailing-to-norway/"/>
   <updated>2011-04-24T00:00:00-04:00</updated>
   <id>http://mikeferrier.com/2011/04/24/sailing-to-norway</id>
   <content type="html">&lt;p&gt;Gmail tried to auto-translate a test email I sent myself:&lt;/p&gt;

&lt;p&gt;&lt;a href='/images/gib.png '&gt;&lt;img class='thumb' src='/images/gib.png ' /&gt; &lt;/a&gt;&lt;/p&gt;

&lt;p&gt;I always suspected Norwegian was a fake language.&lt;/p&gt;
</content>
 </entry>
 
 <entry>
   <title>Using Pow for local development</title>
   <link href="http://mikeferrier.com/2011/04/22/using-pow-for-local-development/"/>
   <updated>2011-04-22T00:00:00-04:00</updated>
   <id>http://mikeferrier.com/2011/04/22/using-pow-for-local-development</id>
   <content type="html">&lt;p&gt;Shortly after my post on &lt;a href=&quot;/2011/04/04/setting-up-wildcard-dns-on-localhost-domains-on-osx/&quot;&gt;setting up wildcard DNS on localhost domains on OSX&lt;/a&gt;, there was a much-needed breakthrough in the realm of ruby localhost development. &lt;a href=&quot;http://twitter.com/sstephenson&quot;&gt;Sam Stephenson&lt;/a&gt; introduced &lt;a href=&quot;http://pow.cx/&quot;&gt;Pow&lt;/a&gt;, which aims to simplify the running of multiple development apps on localhost.&lt;/p&gt;

&lt;p&gt;Rather than manually run standalone webservers (ugh), or add entries to an &lt;strong&gt;nginx+passenger&lt;/strong&gt; config file (less ugh), with Pow you can simply symlink your myproject directory into &lt;code&gt;~/.pow&lt;/code&gt; and then hit &lt;code&gt;http://myproject.dev&lt;/code&gt; and things will &lt;em&gt;just work&lt;/em&gt;.&lt;/p&gt;

&lt;p&gt;How's this work? Well, Pow is a &lt;a href=&quot;http://nodejs.org&quot;&gt;node&lt;/a&gt; app running on localhost which has two functions:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Serving rack apps&lt;/li&gt;
&lt;li&gt;Serving DNS requests&lt;/li&gt;
&lt;/ol&gt;


&lt;p&gt;When you symlink a project directory into &lt;code&gt;~/.pow&lt;/code&gt;, you're telling Pow that a Rack app lives there. Pow listens on &lt;code&gt;localhost:80&lt;/code&gt; for http requests, and based on the hostname requested, serves the appropriate Rack app.&lt;/p&gt;

&lt;p&gt;Pow uses an interesting trick to get the &lt;code&gt;*.dev&lt;/code&gt; URLs resolving to localhost: on installation it adds the file &lt;code&gt;/etc/resolver/dev&lt;/code&gt; which on OSX acts as a DNS resolution config for the &lt;code&gt;.dev&lt;/code&gt; domain. It instructs your computer to use the namserver at &lt;code&gt;127.0.0.1:20560&lt;/code&gt; to resolve any domain with the TLD of &lt;code&gt;.dev&lt;/code&gt;. The Pow server binds to that port and serves DNS for &lt;code&gt;.dev&lt;/code&gt; domain requests &amp;mdash; it resolves all hostnames to &lt;code&gt;127.0.0.1&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Pow also supports &lt;a href=&quot;http://rvm.beginrescueend.com/&quot;&gt;RVM&lt;/a&gt; and &lt;a href=&quot;http://gembundler.com/&quot;&gt;Bundler&lt;/a&gt;, so you can simultaneously run projects with different ruby versions and gemsets, a feat not currently possible with &lt;strong&gt;nginx+passenger&lt;/strong&gt; without adding upstream proxying to standalone Passenger instances.&lt;/p&gt;

&lt;p&gt;Overall a very welcome contribution to the ruby developer community. I'll be sticking with &lt;strong&gt;nginx+passenger&lt;/strong&gt; for now as I require &lt;a href=&quot;https://github.com/agentzh/set-misc-nginx-module&quot;&gt;some&lt;/a&gt; &lt;a href=&quot;https://github.com/vkholodkov/nginx-upload-module&quot;&gt;special&lt;/a&gt; &lt;a href=&quot;https://github.com/chaoslawful/lua-nginx-module&quot;&gt;nginx&lt;/a&gt; &lt;a href=&quot;https://github.com/agentzh/redis2-nginx-module&quot;&gt;modules&lt;/a&gt; for a few projects, but the next time a designer needs to get up and running on a project, it'll be much easier to get this done. I might even be able to do it without having to take over the keyboard.&lt;/p&gt;
</content>
 </entry>
 
 <entry>
   <title>Setting up a VPN for secure networking while traveling</title>
   <link href="http://mikeferrier.com/2011/04/12/setting-up-a-vpn-for-secure-networking-while-traveling/"/>
   <updated>2011-04-12T00:00:00-04:00</updated>
   <id>http://mikeferrier.com/2011/04/12/setting-up-a-vpn-for-secure-networking-while-traveling</id>
   <content type="html">&lt;p&gt;My &lt;a href=&quot;http://melissakaita.com&quot;&gt;wife&lt;/a&gt; and I tend to do a lot of traveling, both on &lt;a href=&quot;http://formandmethod.com&quot;&gt;business&lt;/a&gt; and &lt;a href=&quot;http://www.flyertalk.com/forum/miles-points-1/&quot;&gt;for pleasure&lt;/a&gt;, and if you've ever spent time in airports, you know that internet access tends to come in the WEP flavour, usually with no password.&lt;/p&gt;

&lt;p&gt;When &lt;a href=&quot;http://en.wikipedia.org/wiki/Firesheep&quot;&gt;Firesheep&lt;/a&gt; was released last year, it demonstrated how easy it is for those listening in on a wireless network to steal session cookies for &lt;a href=&quot;http://facebook.com&quot;&gt;popular&lt;/a&gt; &lt;a href=&quot;http://twitter.com&quot;&gt;sites&lt;/a&gt;, allowing them to login as the victim.&lt;/p&gt;

&lt;p&gt;One way to protect yourself is to run all your traffic through a VPN while using an insecure network. I set mine up on my Linode using their instuctions &quot;&lt;a href=&quot;http://library.linode.com/networking/openvpn/ubuntu-10.04-lucid&quot;&gt;Secure Communications with OpenVPN&lt;/a&gt;&quot;, especially the part about &quot;&lt;a href=&quot;http://library.linode.com/networking/openvpn/ubuntu-10.04-lucid#tunnel_all_connections_through_the_vpn&quot;&gt;tunnelling all connections&lt;/a&gt;&quot;.&lt;/p&gt;

&lt;p&gt;Once you've got that set up, you just need to install a VPN client; on OSX, I recommend &lt;a href=&quot;http://code.google.com/p/tunnelblick/&quot;&gt;Tunnelblick&lt;/a&gt;. It sits up in your menu bar, and connecting is as simple as choosing a dropdown option. Once connected, all your internet traffic is seamlessly routed through the VPN.&lt;/p&gt;

&lt;p&gt;Since Firesheep was released, almost every site targeted has switched to enforcing &lt;strong&gt;https&lt;/strong&gt; on all connections, but I still appreciate this extra layer of security while laptopping on the road. As an extra added bonus, it also allows me to access geo-restricted US sites like Hulu, but you didn't hear that from me.&lt;/p&gt;
</content>
 </entry>
 
 <entry>
   <title>Setting up wildcard DNS on localhost domains on OSX</title>
   <link href="http://mikeferrier.com/2011/04/04/setting-up-wildcard-dns-on-localhost-domains-on-osx/"/>
   <updated>2011-04-04T00:00:00-04:00</updated>
   <id>http://mikeferrier.com/2011/04/04/setting-up-wildcard-dns-on-localhost-domains-on-osx</id>
   <content type="html">&lt;p&gt;Like many other Rails developers, I've made the switch to using &lt;a href=&quot;http://www.modrails.com/install.html&quot;&gt;nginx+passenger&lt;/a&gt; for my local development on OSX. It's super easy to compile and install, especially if you use
&lt;a href=&quot;https://github.com/mxcl/homebrew&quot;&gt;brew&lt;/a&gt; and having your nginx process serving all your development sites is a snap to configure.&lt;/p&gt;

&lt;p&gt;The workflow for a new project generally goes like this: add your project directory, give that directory its own server{} block in &lt;code&gt;nginx.conf&lt;/code&gt;, and then picking a local (i.e. fake) domain and pointing that domain at &lt;code&gt;127.0.0.1&lt;/code&gt;, or &lt;code&gt;localhost&lt;/code&gt;. For instance, if I were to start projectx in &lt;code&gt;~/dev/projectx&lt;/code&gt;, I would then have to add the appropriate server{} block in &lt;code&gt;nginx.conf&lt;/code&gt;, and then add &lt;code&gt;projectx.local&lt;/code&gt; to my &lt;code&gt;/etc/hosts&lt;/code&gt; file. When I hit &lt;code&gt;http://projectx.local&lt;/code&gt; in my browser, it'll resolve to localhost and hit my local nginx, serving me my development site.&lt;/p&gt;

&lt;p&gt;The one place where this kind of fails is when you need wildcard subdomains. That is to say, when you need *.projectx.local to point to localhost.&lt;/p&gt;

&lt;p&gt;While working on 
&lt;a href=&quot;http://4ormat.com&quot;&gt;4ormat&lt;/a&gt;, we do a lot of testing on our local machines, and since we give users their own subdomains on 4ormat.com, it comes in handy to be able to map *.4ormat.local in our web browsers to the localhost. While nginx supports wildcard hostnames in its configuration file, /etc/hosts does not.&lt;/p&gt;

&lt;p&gt;So what's the answer? Set up your own local nameserver. It's easier than you might think. OSX Snow Leopard ships with &lt;a href=&quot;http://www.isc.org/software/bind&quot;&gt;ISC Bind&lt;/a&gt; (the de facto standard for serving DNS) and it's pretty easy to get going. Here's how to do it.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;(big thanks to &lt;a href=&quot;http://geoffhankerson.com/node/108&quot;&gt;Geoff Hankerson's post&lt;/a&gt; which helped kickstart this process)&lt;/em&gt;&lt;/p&gt;

&lt;h4&gt;1. Generate an rndc key&lt;/h4&gt;


&lt;p&gt;&lt;code&gt;rndc&lt;/code&gt; is a utility that allows you to admin the bind server remotely, and by default the Bind config looks for a key, so start by generating one:&lt;/p&gt;

&lt;div class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;console&quot;&gt;&lt;span class=&quot;gp&quot;&gt;$&lt;/span&gt; sudo rndc-confgen -a -c /etc/rndc.key
&lt;span class=&quot;go&quot;&gt;wrote key file &amp;quot;/etc/rndc.key&amp;quot;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;




&lt;h4&gt;2. Add your .local top level domain as a zone&lt;/h4&gt;


&lt;p&gt;Edit
&lt;code&gt;/etc/named.conf&lt;/code&gt; and add the following block, called a &quot;zone&quot;:&lt;/p&gt;

&lt;div class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;text&quot;&gt;zone &amp;quot;local&amp;quot; IN {
type master;
file &amp;quot;local.zone&amp;quot;;
allow-update { none; };
};
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;


&lt;p&gt;Add it on its own lines right after one of the existing &quot;zone&quot; blocks.&lt;/p&gt;

&lt;h4&gt;3. Add the .local zone file&lt;/h4&gt;


&lt;p&gt;The line &lt;code&gt;file &quot;local.zone&quot;&lt;/code&gt; in &lt;code&gt;named.conf&lt;/code&gt; sets the name of the file where bind will look for information on that zone. Those zone files live in &lt;code&gt;/var/named&lt;/code&gt; so next we add the file &lt;code&gt;/var/named/local.zone&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;Here's what mine looks like:&lt;/p&gt;

&lt;div class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;text&quot;&gt;$TTL  60
$ORIGIN local.
@      1D IN SOA  localhost. root.localhost. (
          45    ; serial (d. adams)
          3H    ; refresh
          15M    ; retry
          1W    ; expiry
          1D )    ; minimum

      1D IN NS  localhost.
      1D IN A  127.0.0.1

*.local. 60 IN A 127.0.0.1
4ormat.local. 60 IN A 127.0.0.1
*.4ormat.local. 60 IN A 127.0.0.1
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;


&lt;p&gt;In a nutshell, this config says:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;The authority on &lt;code&gt;.local&lt;/code&gt; domains is &lt;code&gt;localhost&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;The domains &lt;code&gt;anything.local&lt;/code&gt; and &lt;code&gt;anything.4ormat.local&lt;/code&gt; resolve to &lt;code&gt;127.0.0.1&lt;/code&gt;&lt;/li&gt;
&lt;/ol&gt;


&lt;p&gt;The important line is the one that starts &lt;code&gt;*.4ormat.local&lt;/code&gt;. That allows us to use wildcard domains that will all resolve to &lt;code&gt;localhost&lt;/code&gt; which is something we can't do with &lt;code&gt;/etc/hosts&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;You'll want to replace &lt;code&gt;4ormat&lt;/code&gt; with the domain of your choice, and &lt;code&gt;local&lt;/code&gt; with the suffix you want.&lt;/p&gt;

&lt;p&gt;By the way, the trailing dots are no typo &amp;mdash; they're &lt;a href=&quot;http://en.wikipedia.org/wiki/Fully_qualified_domain_name&quot;&gt;&lt;strong&gt;Fully Qualified Domain Names&lt;/strong&gt;&lt;/a&gt;. It's a good practice when dealing with DNS settings to use &lt;strong&gt;FQDN&lt;/strong&gt;s, as they remove any ambiguity that might arise when referencing domain names.&lt;/p&gt;

&lt;h4&gt;4. Check your config and zone files&lt;/h4&gt;


&lt;p&gt;Bind comes with some handy utilities for checking your config and zone files.&lt;/p&gt;

&lt;div class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;console&quot;&gt;&lt;span class=&quot;gp&quot;&gt;$&lt;/span&gt; sudo named-checkconf /etc/named.conf
&lt;span class=&quot;gp&quot;&gt;$&lt;/span&gt; sudo named-checkzone &lt;span class=&quot;nb&quot;&gt;local&lt;/span&gt; /var/named/local.zone
&lt;span class=&quot;go&quot;&gt;zone local/IN: loaded serial 45&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;OK&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;


&lt;p&gt;The first command will have no output if your config is set up properly. The second should say &quot;OK&quot;.&lt;/p&gt;

&lt;h4&gt;5. Start bind&lt;/h4&gt;


&lt;p&gt;An easy way to start up bind is to add it to the Launch Daemons of the root user:&lt;/p&gt;

&lt;div class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;console&quot;&gt;&lt;span class=&quot;go&quot;&gt;sudo launchctl load -w /System/Library/LaunchDaemons/org.isc.named.plist&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;


&lt;p&gt;After running this command, launchd should have started up named automatically:&lt;/p&gt;

&lt;div class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;console&quot;&gt;&lt;span class=&quot;gp&quot;&gt;$&lt;/span&gt; ps aux | grep named
&lt;span class=&quot;go&quot;&gt;root 2467 0.0 0.2 2441740 8380 ?? Ss 6:43pm 0:05.48 /usr/sbin/named -f&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;


&lt;p&gt;As an added bonus, this command makes sure &lt;code&gt;named&lt;/code&gt; is automatically started on boot, too.&lt;/p&gt;

&lt;h4&gt;6. Verify things are working&lt;/h4&gt;


&lt;p&gt;&lt;code&gt;dig&lt;/code&gt; is the swiss army knife of DNS tools, and is distributed with Bind, so it's already around on OSX. Asking localhost to resolve a few domains will verify things are working:&lt;/p&gt;

&lt;div class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;console&quot;&gt;&lt;span class=&quot;gp&quot;&gt;$&lt;/span&gt; dig @localhost foo.local

&lt;span class=&quot;go&quot;&gt;; &amp;lt;&amp;lt;&amp;gt;&amp;gt; DiG 9.6.0-APPLE-P2 &amp;lt;&amp;lt;&amp;gt;&amp;gt; @localhost foo.local&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;; (3 servers found)&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;;; global options: +cmd&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;;; Got answer:&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;;; -&amp;gt;&amp;gt;HEADER&amp;lt;&amp;lt;- opcode: QUERY, status: NOERROR, id: 53309&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;;; flags: qr aa rd ra; QUERY: 1, ANSWER: 1, AUTHORITY: 1, ADDITIONAL: 1&lt;/span&gt;

&lt;span class=&quot;go&quot;&gt;;; QUESTION SECTION:&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;;foo.local. IN  A&lt;/span&gt;

&lt;span class=&quot;go&quot;&gt;;; ANSWER SECTION:&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;foo.local.    60  IN  A  127.0.0.1&lt;/span&gt;

&lt;span class=&quot;go&quot;&gt;;; AUTHORITY SECTION:&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;local.   86400  IN  NS  localhost.&lt;/span&gt;

&lt;span class=&quot;go&quot;&gt;;; ADDITIONAL SECTION:&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;localhost.    86400  IN  A  127.0.0.1&lt;/span&gt;

&lt;span class=&quot;go&quot;&gt;;; Query time: 0 msec&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;;; SERVER: 127.0.0.1#53(127.0.0.1)&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;;; WHEN: Mon Apr  4 17:13:51 2011&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;;; MSG SIZE  rcvd: 82&lt;/span&gt;


&lt;span class=&quot;gp&quot;&gt;$&lt;/span&gt; dig @localhost 4ormat.local

&lt;span class=&quot;go&quot;&gt;; &amp;lt;&amp;lt;&amp;gt;&amp;gt; DiG 9.6.0-APPLE-P2 &amp;lt;&amp;lt;&amp;gt;&amp;gt; @localhost 4ormat.local&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;; (3 servers found)&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;;; global options: +cmd&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;;; Got answer:&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;;; -&amp;gt;&amp;gt;HEADER&amp;lt;&amp;lt;- opcode: QUERY, status: NOERROR, id: 1431&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;;; flags: qr aa rd ra; QUERY: 1, ANSWER: 1, AUTHORITY: 1, ADDITIONAL: 1&lt;/span&gt;

&lt;span class=&quot;go&quot;&gt;;; QUESTION SECTION:&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;;4ormat.local.      IN  A&lt;/span&gt;

&lt;span class=&quot;go&quot;&gt;;; ANSWER SECTION:&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;4ormat.local.    60  IN  A  127.0.0.1&lt;/span&gt;

&lt;span class=&quot;go&quot;&gt;;; AUTHORITY SECTION:&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;local.      86400  IN  NS  localhost.&lt;/span&gt;

&lt;span class=&quot;go&quot;&gt;;; ADDITIONAL SECTION:&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;localhost.    86400  IN  A  127.0.0.1&lt;/span&gt;

&lt;span class=&quot;go&quot;&gt;;; Query time: 0 msec&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;;; SERVER: 127.0.0.1#53(127.0.0.1)&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;;; WHEN: Mon Apr  4 17:13:54 2011&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;;; MSG SIZE  rcvd: 85&lt;/span&gt;


&lt;span class=&quot;gp&quot;&gt;$&lt;/span&gt; dig @localhost foo.4ormat.local

&lt;span class=&quot;go&quot;&gt;; &amp;lt;&amp;lt;&amp;gt;&amp;gt; DiG 9.6.0-APPLE-P2 &amp;lt;&amp;lt;&amp;gt;&amp;gt; @localhost foo.4ormat.local&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;; (3 servers found)&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;;; global options: +cmd&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;;; Got answer:&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;;; -&amp;gt;&amp;gt;HEADER&amp;lt;&amp;lt;- opcode: QUERY, status: NOERROR, id: 43030&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;;; flags: qr aa rd ra; QUERY: 1, ANSWER: 1, AUTHORITY: 1, ADDITIONAL: 1&lt;/span&gt;

&lt;span class=&quot;go&quot;&gt;;; QUESTION SECTION:&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;;foo.4ormat.local.    IN  A&lt;/span&gt;

&lt;span class=&quot;go&quot;&gt;;; ANSWER SECTION:&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;foo.4ormat.local.  60  IN  A  127.0.0.1&lt;/span&gt;

&lt;span class=&quot;go&quot;&gt;;; AUTHORITY SECTION:&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;local.      86400  IN  NS  localhost.&lt;/span&gt;

&lt;span class=&quot;go&quot;&gt;;; ADDITIONAL SECTION:&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;localhost.    86400  IN  A  127.0.0.1&lt;/span&gt;

&lt;span class=&quot;go&quot;&gt;;; Query time: 0 msec&lt;/span&gt;

&lt;span class=&quot;go&quot;&gt;;; SERVER: 127.0.0.1#53(127.0.0.1)&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;;; WHEN: Mon Apr  4 17:13:57 2011&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;;; MSG SIZE  rcvd: 89&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;


&lt;p&gt;The important part of each of those three queries is the IN A response in the ANSWER SECTION. In each, we see that the domains resolve to 127.0.0.1, which is exactly what we want.&lt;/p&gt;

&lt;h4&gt;7. Add 127.0.0.1 to your list of DNS servers&lt;/h4&gt;


&lt;p&gt;Open &lt;strong&gt;System Preferences&lt;/strong&gt; and open the &lt;strong&gt;Network&lt;/strong&gt; pane. On the left side, select the network adapter you use (most likely your wireless adapter), and then click &lt;strong&gt;Advanced&lt;/strong&gt;. Under the &lt;strong&gt;DNS&lt;/strong&gt; tab, click the &lt;strong&gt;+&lt;/strong&gt; button under the &lt;strong&gt;DNS Servers&lt;/strong&gt; list, and add &lt;strong&gt;127.0.0.1&lt;/strong&gt;. Click &lt;strong&gt;OK&lt;/strong&gt;, then click &lt;strong&gt;Apply&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;That's it, you're done.&lt;/p&gt;

&lt;h4&gt;Reloading Bind and flushing DNS&lt;/h4&gt;


&lt;p&gt;If you ever change anything in your config or zone files, you can signal Bind to reload its config using:&lt;/p&gt;

&lt;div class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;text&quot;&gt;sudo rndc -p 54 reload
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;


&lt;p&gt;It's also a good practice to increment the &lt;code&gt;serial&lt;/code&gt; field in your zone file any time anything changes. This lets any DNS client know to invalidate its DNS cache for that zone. So in our example, our &lt;code&gt;serial&lt;/code&gt; is set to &lt;code&gt;45&lt;/code&gt;. So if you made any change, you'd change it to &lt;code&gt;46&lt;/code&gt; before saving the file and reloading Bind.&lt;/p&gt;

&lt;p&gt;Some people also reported that they needed to flush their local DNS cache after doing this (I didn't.) If you feel you need to, you can by issuing the command:&lt;/p&gt;

&lt;div class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;text&quot;&gt;dscacheutil -flushcache
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;



</content>
 </entry>
 
 
</feed>
