<?xml version="1.0" encoding="UTF-8"?>
<?xml-stylesheet type="text/xsl" media="screen" href="/~d/styles/atom10full.xsl"?><?xml-stylesheet type="text/css" media="screen" href="http://feeds.feedburner.com/~d/styles/itemcontent.css"?><feed xmlns="http://www.w3.org/2005/Atom">
<title>Paul Mucur</title>
<link href="http://mudge.github.com/" />

<updated>2012-01-29T09:28:44-08:00</updated>
<id>http://mudge.github.com/</id>
<author>
  <name>Paul Mucur</name>
</author>

<atom10:link xmlns:atom10="http://www.w3.org/2005/Atom" rel="self" type="application/atom+xml" href="http://feeds.feedburner.com/paul-mucur" /><feedburner:info xmlns:feedburner="http://rssnamespace.org/feedburner/ext/1.0" uri="paul-mucur" /><atom10:link xmlns:atom10="http://www.w3.org/2005/Atom" rel="hub" href="http://pubsubhubbub.appspot.com/" /><entry>
  <title>Naming Dynamically Created iframes</title>
  <link href="/2012/01/29/naming-dynamically-created-iframes.html" />
  <id>http://mudge.github.com/2012/01/29/naming-dynamically-created-iframes.html</id>
  <updated>2012-01-29T00:00:00-08:00</updated>
  <summary>Working around issues with setting iframe names in Internet Explorer.</summary>
  <content type="html">&lt;p&gt;For the past year, I have been working on a web application that relies heavily on &lt;a href="https://developer.mozilla.org/en/HTML/Element/iframe"&gt;inline frames (aka &lt;code&gt;iframe&lt;/code&gt;s)&lt;/a&gt;. During the course of development, we have run into some interesting issues and I thought I would record some of these for posterity.&lt;/p&gt;

&lt;p&gt;The &lt;a href="http://www.w3.org/TR/html4/present/frames.html#h-16.5"&gt;W3C specification for the &lt;code&gt;IFRAME&lt;/code&gt; element&lt;/a&gt; states the following document type definition:&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre&gt;&lt;code class="html"&gt;&lt;span class="cp"&gt;&amp;lt;!ELEMENT IFRAME - - (%flow;)*         -- inline subwindow --&amp;gt;&lt;/span&gt;
&lt;span class="cp"&gt;&amp;lt;!ATTLIST IFRAME&lt;/span&gt;
&lt;span class="cp"&gt;  %coreattrs;                          -- id, class, style, title --&lt;/span&gt;
&lt;span class="cp"&gt;  longdesc    %URI;          #IMPLIED  -- link to long description&lt;/span&gt;
&lt;span class="cp"&gt;                                          (complements title) --&lt;/span&gt;
&lt;span class="cp"&gt;  name        CDATA          #IMPLIED  -- name of frame for targetting --&lt;/span&gt;
&lt;span class="cp"&gt;  src         %URI;          #IMPLIED  -- source of frame content --&lt;/span&gt;
&lt;span class="cp"&gt;  frameborder (1|0)          1         -- request frame borders? --&lt;/span&gt;
&lt;span class="cp"&gt;  marginwidth %Pixels;       #IMPLIED  -- margin widths in pixels --&lt;/span&gt;
&lt;span class="cp"&gt;  marginheight %Pixels;      #IMPLIED  -- margin height in pixels --&lt;/span&gt;
&lt;span class="cp"&gt;  scrolling   (yes|no|auto)  auto      -- scrollbar or none --&lt;/span&gt;
&lt;span class="cp"&gt;  align       %IAlign;       #IMPLIED  -- vertical or horizontal alignment --&lt;/span&gt;
&lt;span class="cp"&gt;  height      %Length;       #IMPLIED  -- frame height --&lt;/span&gt;
&lt;span class="cp"&gt;  width       %Length;       #IMPLIED  -- frame width --&lt;/span&gt;
&lt;span class="cp"&gt;  &amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;


&lt;p&gt;The key thing I'm interested in here is the &lt;a href="http://www.w3.org/TR/html4/present/frames.html#adef-name-IFRAME"&gt;&lt;code&gt;name&lt;/code&gt; attribute&lt;/a&gt;. Our particular application allows &lt;code&gt;iframe&lt;/code&gt;s to fire events which are then caught by the parent window. In order to identify the source of these events, the &lt;code&gt;iframe&lt;/code&gt;s send along their &lt;code&gt;name&lt;/code&gt; (accessed via &lt;a href="https://developer.mozilla.org/en/DOM/window.name"&gt;&lt;code&gt;window.name&lt;/code&gt;&lt;/a&gt;).&lt;/p&gt;

&lt;p&gt;If you have an &lt;code&gt;iframe&lt;/code&gt; like the following in your web page:&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre&gt;&lt;code class="html"&gt;&lt;span class="nt"&gt;&amp;lt;iframe&lt;/span&gt; &lt;span class="na"&gt;frameborder=&lt;/span&gt;&lt;span class="s"&gt;&amp;quot;0&amp;quot;&lt;/span&gt; &lt;span class="na"&gt;name=&lt;/span&gt;&lt;span class="s"&gt;&amp;quot;bob&amp;quot;&lt;/span&gt; &lt;span class="na"&gt;src=&lt;/span&gt;&lt;span class="s"&gt;&amp;quot;/some-page&amp;quot;&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&amp;lt;/iframe&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;


&lt;p&gt;And &lt;code&gt;/some-page&lt;/code&gt; contains the following HTML:&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre&gt;&lt;code class="html"&gt;&lt;span class="cp"&gt;&amp;lt;!DOCTYPE html&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;title&amp;gt;&lt;/span&gt;Test&lt;span class="nt"&gt;&amp;lt;/title&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;script&amp;gt;&lt;/span&gt;
&lt;span class="nb"&gt;document&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;write&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;My name is &amp;#39;&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="nb"&gt;window&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;name&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/script&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;


&lt;p&gt;Then the &lt;code&gt;iframe&lt;/code&gt; should output the following:&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;My name is bob
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;Here's that in action:&lt;/p&gt;

&lt;iframe frameborder=0 name=bob src=/code/iframe-test.html&gt;&lt;/iframe&gt;


&lt;p&gt;However, if you are dynamically creating &lt;code&gt;iframe&lt;/code&gt;s with JavaScript (perhaps to avoid &lt;a href="https://developer.mozilla.org/En/Working_with_BFCache"&gt;BFCache&lt;/a&gt; bugs) then you may run into some issues where &lt;code&gt;window.name&lt;/code&gt; is &lt;code&gt;undefined&lt;/code&gt; in Internet Explorer 6 and 7.&lt;/p&gt;

&lt;p&gt;Specifically, if you create an &lt;code&gt;iframe&lt;/code&gt; and set its name like so:&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre&gt;&lt;code class="html"&gt;&lt;span class="nt"&gt;&amp;lt;script&amp;gt;&lt;/span&gt;
&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="kd"&gt;var&lt;/span&gt; &lt;span class="nx"&gt;iframe&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;document&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;createElement&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;iframe&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="nx"&gt;iframe&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;src&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39;/some-page&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="nx"&gt;iframe&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;name&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39;bob&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="nb"&gt;document&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;body&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;appendChild&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;iframe&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;}());&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/script&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;


&lt;p&gt;Then &lt;code&gt;/some-page&lt;/code&gt; will report the following:&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;My name is
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;Again, a live demonstration (note that this will only be broken in Internet Explorer):&lt;/p&gt;

&lt;div id="test-1"&gt;&lt;/div&gt;


&lt;script&gt;
(function () {
  var t = document.getElementById('test-1'),
      i = document.createElement('iframe');
  i.src = '/code/iframe-test.html';
  i.name = 'bob';
  i.frameBorder = 0;
  t.appendChild(i);
}());
&lt;/script&gt;


&lt;p&gt;The same will occur if you use &lt;a href="http://jquery.com/"&gt;jQuery&lt;/a&gt;:&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre&gt;&lt;code class="html"&gt;&lt;span class="nt"&gt;&amp;lt;script&amp;gt;&lt;/span&gt;
&lt;span class="nx"&gt;$&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;$&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;&amp;lt;iframe/&amp;gt;&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nx"&gt;src&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39;/some-page&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="nx"&gt;name&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39;bob&amp;#39;&lt;/span&gt;
  &lt;span class="p"&gt;}).&lt;/span&gt;&lt;span class="nx"&gt;appendTo&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;document&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;body&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/script&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;


&lt;p&gt;The issue arises because the &lt;code&gt;name&lt;/code&gt; of an &lt;code&gt;iframe&lt;/code&gt; cannot be changed in Internet Explorer, much like the &lt;code&gt;type&lt;/code&gt; attribute on &lt;code&gt;input&lt;/code&gt; (there is a &lt;a href="http://api.jquery.com/jQuery/#creating-new-elements"&gt;caveat in the jQuery documentation about the &lt;code&gt;type&lt;/code&gt; issue when creating elements&lt;/a&gt;).&lt;/p&gt;

&lt;p&gt;This means that you must set the &lt;code&gt;name&lt;/code&gt; attribute on &lt;em&gt;creation&lt;/em&gt; of the element, not afterwards.&lt;/p&gt;

&lt;p&gt;With jQuery, it's a case of doing the following:&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre&gt;&lt;code class="html"&gt;&lt;span class="nt"&gt;&amp;lt;script&amp;gt;&lt;/span&gt;
&lt;span class="nx"&gt;$&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;$&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;&amp;lt;iframe name=&amp;quot;bob&amp;quot;&amp;gt;&amp;lt;/iframe&amp;gt;&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nx"&gt;attr&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
    &lt;span class="nx"&gt;src&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39;/some-page&amp;#39;&lt;/span&gt;
  &lt;span class="p"&gt;}).&lt;/span&gt;&lt;span class="nx"&gt;appendTo&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;document&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;body&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/script&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;


&lt;p&gt;With pure JavaScript it is slightly trickier as you can no longer use &lt;a href="https://developer.mozilla.org/en/DOM/document.createElement"&gt;&lt;code&gt;document.createElement&lt;/code&gt;&lt;/a&gt;. However, you can create a temporary container element (say, a &lt;a href="https://developer.mozilla.org/en/HTML/Element/div"&gt;&lt;code&gt;div&lt;/code&gt;&lt;/a&gt;), and then modify that element's &lt;a href="https://developer.mozilla.org/en/DOM/element.innerHTML"&gt;&lt;code&gt;innerHTML&lt;/code&gt;&lt;/a&gt; to create your &lt;code&gt;iframe&lt;/code&gt; with its &lt;code&gt;name&lt;/code&gt; from the beginning. You can then take your newly created &lt;code&gt;iframe&lt;/code&gt; and insert that directly into the DOM (without ever inserting your temporary &lt;code&gt;div&lt;/code&gt;) like so:&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre&gt;&lt;code class="html"&gt;&lt;span class="nt"&gt;&amp;lt;script&amp;gt;&lt;/span&gt;
&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;var&lt;/span&gt; &lt;span class="nx"&gt;temp&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;document&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;createElement&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;div&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="nx"&gt;temp&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;innerHTML&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39;&amp;lt;iframe name=&amp;quot;bob&amp;quot; src=&amp;quot;/some-page&amp;quot;&amp;gt;&amp;lt;/iframe&amp;gt;&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nb"&gt;document&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;body&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;appendChild&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;temp&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;firstChild&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;}());&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/script&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;


&lt;p&gt;(This is actually what jQuery does internally in the prior example.)&lt;/p&gt;

&lt;p&gt;In this way, all browsers should now correctly report:&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;My name is bob
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;And a live demonstration:&lt;/p&gt;

&lt;div id="test-2"&gt;&lt;/div&gt;


&lt;script&gt;
(function () {
  var t = document.getElementById('test-2'),
      temp = document.createElement('div');
  temp.innerHTML = '&lt;iframe frameborder=0 name=bob src=/code/iframe-test.html&gt;&lt;/iframe&gt;';
  t.appendChild(temp.firstChild);
}());
&lt;/script&gt;



</content>
</entry>

<entry>
  <title>Managing Web Application Servers with Puppet</title>
  <link href="/2011/08/11/managing-web-application-servers-with-puppet.html" />
  <id>http://mudge.github.com/2011/08/11/managing-web-application-servers-with-puppet.html</id>
  <updated>2011-08-11T00:00:00-07:00</updated>
  <summary>A transcript of my August 2011 LRUG presentation regarding configuration management and Puppet.</summary>
  <content type="html">&lt;p&gt;&lt;em&gt;At the &lt;a href="http://lrug.org/meetings/2011/07/18/august-2011-meeting/"&gt;August 2011 meeting of the London Ruby User Group&lt;/a&gt;, I delivered a presentation titled "Managing Web Application Servers with Puppet". You can watch a video of &lt;a href="http://skillsmatter.com/podcast/home/lrug-puppet/"&gt;the whole presentation and the Q &amp;amp; A session on the Skills Matter web site&lt;/a&gt; but for those of you who prefer to read, here is an edited transcript:&lt;/em&gt;&lt;/p&gt;

&lt;p class="centre"&gt;&lt;a href=//skillsmatter.com/podcast/home/lrug-puppet/&gt;&lt;img src=/i/screengrab.png alt="View the full presentation on the Skillsmatter web site"&gt;&lt;/a&gt;&lt;/p&gt;


&lt;h2&gt;Table of Contents&lt;/h2&gt;

&lt;ol&gt;
&lt;li&gt;&lt;a href="#introduction"&gt;Introduction&lt;/a&gt; (who I am, what I am going to talk about);&lt;/li&gt;
&lt;li&gt;&lt;a href="#the-old-way"&gt;The Old Way&lt;/a&gt; (manualling setting up a sample Rails application and some problems);&lt;/li&gt;
&lt;li&gt;&lt;a href="#enter-configuration-management"&gt;Enter Configuration Management&lt;/a&gt; (introduction to configuration management and Puppet);&lt;/li&gt;
&lt;li&gt;&lt;a href="#the-better-way"&gt;The Better Way&lt;/a&gt; (setting up the Rails application from earlier but entirely with Puppet);&lt;/li&gt;
&lt;li&gt;&lt;a href="#testing"&gt;Testing&lt;/a&gt; (a few words on testing configuration);&lt;/li&gt;
&lt;li&gt;&lt;a href="#wrap-up"&gt;Wrap-Up&lt;/a&gt; (closing remarks).&lt;/li&gt;
&lt;/ol&gt;


&lt;h2 id="introduction"&gt;Introduction&lt;/h2&gt;


&lt;p&gt;My name's Paul Mucur and I'm going to be talking about managing web application servers with &lt;a href="http://www.puppetlabs.com/"&gt;Puppet&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;First of all, who am I? I've been a Ruby developer since late 2006 and I currently work for the &lt;a href="http://www.nature.com/"&gt;Nature Publishing Group&lt;/a&gt;: they publish &lt;a href="http://www.nature.com/nature/"&gt;Nature&lt;/a&gt; which is a scientific journal that has been going for &lt;a href="http://en.wikipedia.org/wiki/Nature_(journal)"&gt;around 140 years&lt;/a&gt;. However, I don't work on the journal side; I work for their Web Applications team building mainly &lt;a href="http://www.rubyonrails.org/"&gt;Rails&lt;/a&gt; apps (with a couple of &lt;a href="http://www.sinatrarb.com/"&gt;Sinatra&lt;/a&gt; bits and pieces) for the scientific community. It's quite a large organisation and the actual system administrators are not only not in the office but they're actually in a totally different &lt;em&gt;time zone&lt;/em&gt; and I want to talk a bit about how we try to mitigate problems with regards to having that amount of separation.&lt;/p&gt;

&lt;p&gt;Who is this talk for? Really, it's for developers not unlike myself (unsurprisingly):&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;You've deployed an application (maybe a personal project that you've put on &lt;a href="http://www.linode.com/"&gt;Linode&lt;/a&gt; or &lt;a href="http://www.slicehost.com/"&gt;Slicehost&lt;/a&gt;);&lt;/li&gt;
&lt;li&gt;You still have to deploy to servers (i.e. you're not lucky enough to be on &lt;a href="http://www.heroku.com/"&gt;Heroku&lt;/a&gt;);&lt;/li&gt;
&lt;li&gt;You've heard of Puppet and/or &lt;a href="http://www.opscode.com/chef/"&gt;Chef&lt;/a&gt; and have maybe had a play around but want to know more;&lt;/li&gt;
&lt;li&gt;You're open to the idea that there is a better way to set up servers than just running a load of commands as &lt;code&gt;root&lt;/code&gt;.&lt;/li&gt;
&lt;/ul&gt;


&lt;p&gt;I'm going to be taking the example of a simple Rails application -- just because it touches on quite a lot of things sysadmin-wise and hopefully you're all familiar with the standard set up -- and we're going to do the following:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Run through how we would set up the application normally;&lt;/li&gt;
&lt;li&gt;Some problems with doing it like that;&lt;/li&gt;
&lt;li&gt;Offer an alternative using configuration management (and specifically Puppet) as a better way of doing things.&lt;/li&gt;
&lt;/ul&gt;


&lt;p&gt;What I'm &lt;em&gt;not&lt;/em&gt; going to talk about is:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;The nitty-gritty of how to set up Puppet (you can install it from a package manager but we're just going to take it as read that you have it set up);&lt;/li&gt;
&lt;li&gt;Using Puppet as a replacement for your normal deployment workflow (we're still going to be using &lt;a href="https://github.com/capistrano/capistrano/wiki/"&gt;Capistrano&lt;/a&gt; to get our application code on the servers).&lt;/li&gt;
&lt;/ul&gt;


&lt;h2 id="the-old-way"&gt;The Old Way&lt;/h2&gt;


&lt;p&gt;Let's introduce our Rails application. Because I'm a geek and because it will fit neatly on slides, it's just going to be called "&lt;a href="http://en.wikipedia.org/wiki/Master_Control_Program_(Tron)"&gt;mcp&lt;/a&gt;" and it's a normal, no-frills Rails application.&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre&gt;&lt;code class="console"&gt;&lt;span class="gp"&gt;$&lt;/span&gt; ls mcp
&lt;span class="go"&gt;Capfile      config       log&lt;/span&gt;
&lt;span class="go"&gt;Gemfile      config.ru    public&lt;/span&gt;
&lt;span class="go"&gt;Gemfile.lock db           script&lt;/span&gt;
&lt;span class="go"&gt;README       doc          spec&lt;/span&gt;
&lt;span class="go"&gt;Rakefile     features     tmp&lt;/span&gt;
&lt;span class="go"&gt;app          lib          vendor&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;


&lt;p&gt;It has a complete test suite, has been set up with Capistrano but has not been deployed yet. First, let's make sure all the tests pass...&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre&gt;&lt;code class="console"&gt;&lt;span class="gp"&gt;$&lt;/span&gt; bundle &lt;span class="nb"&gt;exec &lt;/span&gt;rspec spec
&lt;span class="go"&gt;................&lt;/span&gt;

&lt;span class="go"&gt;Finished in 0.00816 seconds&lt;/span&gt;
&lt;span class="go"&gt;Many, many examples, 0 failures&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;


&lt;p&gt;Everything seems to work so let's get a server prepared for a first time deploy.&lt;/p&gt;

&lt;p&gt;We're going to make some assumptions:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;You've already got a server (you don't have to worry about buying it);&lt;/li&gt;
&lt;li&gt;You can already log into the server via SSH;&lt;/li&gt;
&lt;li&gt;You have permission to run commands as &lt;code&gt;root&lt;/code&gt; as you are responsible for setting things up.&lt;/li&gt;
&lt;/ul&gt;


&lt;p&gt;Let's log into the server...&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre&gt;&lt;code class="console"&gt;&lt;span class="gp"&gt;$&lt;/span&gt; ssh mudge@server1
&lt;span class="go"&gt;Last login: Mon Aug  1 21:08:06 2011 from 123-45-67-89.&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;


&lt;p&gt;We like to separate our applications by user so -- as this is an application called &lt;code&gt;mcp&lt;/code&gt; -- we'll use an &lt;code&gt;mcp&lt;/code&gt; user and we will actually keep the application code inside the user's home folder.&lt;/p&gt;

&lt;p&gt;So the first thing we need to do is create a user but I can't quite remember what flags we need to pass to &lt;code&gt;adduser&lt;/code&gt; on this machine...&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre&gt;&lt;code class="console"&gt;&lt;span class="gp"&gt;$&lt;/span&gt; /usr/sbin/adduser -h
&lt;span class="go"&gt;adduser [--home DIR] [--shell SHELL]&lt;/span&gt;
&lt;span class="go"&gt;[--no-create-home] [--uid ID] [--firstuid ID]&lt;/span&gt;
&lt;span class="go"&gt;[--lastuid ID] [--gecos GECOS]&lt;/span&gt;
&lt;span class="go"&gt;[--ingroup GROUP | --gid ID]&lt;/span&gt;
&lt;span class="go"&gt;[--disabled-password] [--disabled-login]&lt;/span&gt;
&lt;span class="go"&gt;[--encrypt-home] USER&lt;/span&gt;
&lt;span class="go"&gt;   Add a normal user&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;


&lt;p&gt;It looks like this machine's &lt;code&gt;adduser&lt;/code&gt; will create home directories for us by default (some operating systems require a &lt;code&gt;-m&lt;/code&gt; flag to do that). Let's drop into a &lt;code&gt;root&lt;/code&gt; prompt (just to make the slides a bit easier) and add our new user.&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre&gt;&lt;code class="console"&gt;&lt;span class="gp"&gt;$&lt;/span&gt; sudo -i
&lt;span class="gp"&gt;#&lt;/span&gt; adduser mcp
&lt;span class="go"&gt;Adding user `mcp&amp;#39; ...&lt;/span&gt;
&lt;span class="go"&gt;Adding new group `mcp&amp;#39; (1002) ...&lt;/span&gt;
&lt;span class="go"&gt;Adding new user `mcp&amp;#39; (1001) with group `mcp&amp;#39; ...&lt;/span&gt;
&lt;span class="go"&gt;Creating home directory `/home/mcp&amp;#39; ...&lt;/span&gt;
&lt;span class="go"&gt;Copying files from `/etc/skel&amp;#39; ...&lt;/span&gt;
&lt;span class="go"&gt;Enter new UNIX password: &lt;/span&gt;
&lt;span class="go"&gt;Retype new UNIX password: &lt;/span&gt;
&lt;span class="go"&gt;...&lt;/span&gt;
&lt;span class="go"&gt;Is the information correct? [Y/n] &lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;


&lt;p&gt;After filling out a little information, we now have our user and, from the output, it looks like we got a group named after the user created as well.&lt;/p&gt;

&lt;p&gt;As we're going to be deploying our application with Capistrano and we don't want to be passing around passwords, we now want to set up SSH keys. If you're not familiar with the concept, you can use SSH keys to log into a user account without entering a password by putting your public key in a file called &lt;code&gt;authorized_keys&lt;/code&gt; in the user's home directory. There are a few tricky things to be aware of though particularly regarding permissions of the files involved in this process.
The first thing we need to do is create a &lt;code&gt;.ssh&lt;/code&gt; directory but do it in a way that means only the owner can access it...&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre&gt;&lt;code class="console"&gt;&lt;span class="gp"&gt;#&lt;/span&gt; mkdir -m 700 ~mcp/.ssh
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;


&lt;p&gt;Then we're going to add our key to the &lt;code&gt;authorized_keys&lt;/code&gt; file and, as I am running as &lt;code&gt;root&lt;/code&gt; and not &lt;code&gt;mcp&lt;/code&gt;, we need to make sure that we &lt;code&gt;chown&lt;/code&gt; everything to the right user so they can access it.&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre&gt;&lt;code class="console"&gt;&lt;span class="gp"&gt;#&lt;/span&gt; vi ~mcp/.ssh/authorized_keys
&lt;span class="gp"&gt;#&lt;/span&gt; chmod 600 ~mcp/.ssh/authorized_keys
&lt;span class="gp"&gt;#&lt;/span&gt; chown -R mcp: ~mcp/.ssh
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;


&lt;p&gt;That's now done and only the &lt;code&gt;mcp&lt;/code&gt; user can access our list of public keys.&lt;/p&gt;

&lt;p&gt;We want to have a particular directory structure for our deployed application and we're going to copy the one used by Capistrano:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;A &lt;code&gt;releases&lt;/code&gt; directory that will contain timestamped versions of your application code;&lt;/li&gt;
&lt;li&gt;A &lt;code&gt;shared&lt;/code&gt; directory that persists across deploys making it ideal for storing configuration like your &lt;code&gt;database.yml&lt;/code&gt;.&lt;/li&gt;
&lt;/ul&gt;


&lt;p&gt;The final part (which will be managed by Capistrano itself) is a symbolic link called &lt;code&gt;current&lt;/code&gt; which points to the currently active version of your code within the &lt;code&gt;releases&lt;/code&gt; directory.&lt;/p&gt;

&lt;p&gt;While Capistrano will create those directories for us, we want to take advantage of the &lt;code&gt;shared&lt;/code&gt; directory now so that we can put our configuration in place before we deploy. Let's create our directory structure within the &lt;code&gt;mcp&lt;/code&gt; user's home directory...&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre&gt;&lt;code class="console"&gt;&lt;span class="gp"&gt;#&lt;/span&gt; mkdir -p ~mcp/apps/mcp/shared/config
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;


&lt;p&gt;As we're doing everything as &lt;code&gt;root&lt;/code&gt;, we just need to make sure everything is owned by the right user...&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre&gt;&lt;code class="console"&gt;&lt;span class="gp"&gt;#&lt;/span&gt; chown -R mcp: ~mcp/apps
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;


&lt;p&gt;As a developer in a large organisation, ideally I'm not supposed to see live usernames and passwords for things like the database so let's say, at this point, we hand over to a system administrator who &lt;em&gt;does&lt;/em&gt; have authority to handle those details. Perhaps we could supply them with a &lt;code&gt;database.yml&lt;/code&gt; with "FILL IN THE BLANKS HERE" for the username and password and then guide them to put the file in the right place. Once they have done that then we can make use of the file in our deploys without having to store it on development machines.&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre&gt;&lt;code class="console"&gt;&lt;span class="gp"&gt;#&lt;/span&gt; &lt;span class="nb"&gt;cd&lt;/span&gt; ~mcp/apps/mcp/shared/config
&lt;span class="gp"&gt;#&lt;/span&gt; vi database.yml
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;


&lt;p&gt;Again, make sure that the user is correct.&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre&gt;&lt;code class="console"&gt;&lt;span class="gp"&gt;#&lt;/span&gt; chown mcp: database.yml
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;


&lt;p&gt;To run our application, we're going to need to install Ruby. As we might have several applications using different versions, let's install &lt;a href="http://rvm.beginrescueend.com/"&gt;RVM&lt;/a&gt; to manage Ruby for us &lt;em&gt;[NB: this predates the release of &lt;a href="http://www.rubyinside.com/rbenv-a-simple-new-ruby-version-management-tool-5302.html"&gt;rbenv and the ensuing controversy&lt;/a&gt;]&lt;/em&gt;.&lt;/p&gt;

&lt;p&gt;Firstly, we'll need to install RVM's dependencies; as we are using Debian, we'll need to use &lt;code&gt;apt-get&lt;/code&gt; to install the required packages...&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre&gt;&lt;code class="console"&gt;&lt;span class="gp"&gt;#&lt;/span&gt; apt-get install curl git-core subversion
&lt;span class="go"&gt;Reading package lists... Done&lt;/span&gt;
&lt;span class="go"&gt;Building dependency tree&lt;/span&gt;
&lt;span class="go"&gt;Reading state information... Done&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;


&lt;p&gt;You can install RVM using a one-liner but, as we are on a production server, we want to be a bit pickier about the version we are using. Luckily, RVM offers an alternative way to install by downloading a single script...&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre&gt;&lt;code class="console"&gt;&lt;span class="gp"&gt;#&lt;/span&gt; &lt;span class="nb"&gt;cd&lt;/span&gt; /root
&lt;span class="gp"&gt;#&lt;/span&gt; curl -s &lt;span class="se"&gt;\&lt;/span&gt;
&lt;span class="gp"&gt;&amp;gt;&lt;/span&gt; https://rvm.beginrescueend.com/install/rvm &lt;span class="se"&gt;\&lt;/span&gt;
&lt;span class="gp"&gt;&amp;gt;&lt;/span&gt; -o rvm-installer
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;


&lt;p&gt;Making it executable...&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre&gt;&lt;code class="console"&gt;&lt;span class="gp"&gt;#&lt;/span&gt; chmod +x rvm-installer
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;


&lt;p&gt;And then using that to install a known good version, let's say 1.6.32...&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre&gt;&lt;code class="console"&gt;&lt;span class="gp"&gt;#&lt;/span&gt; ./rvm-installer --version 1.6.32
&lt;span class="go"&gt;Installation of RVM to /usr/local/rvm is&lt;/span&gt;
&lt;span class="go"&gt;complete.&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;


&lt;p&gt;As we are running as &lt;code&gt;root&lt;/code&gt;, everything will be installed to &lt;code&gt;/usr/local&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;In order to actually compile and install versions of Ruby, there are a load of other dependencies we need (the list of which you can get by running &lt;code&gt;rvm notes&lt;/code&gt; on your machine)...&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre&gt;&lt;code class="console"&gt;&lt;span class="gp"&gt;#&lt;/span&gt; apt-get install build-essential bison &lt;span class="se"&gt;\&lt;/span&gt;
&lt;span class="gp"&gt;&amp;gt;&lt;/span&gt; openssl libreadline6 libreadline6-dev &lt;span class="se"&gt;\&lt;/span&gt;
&lt;span class="gp"&gt;&amp;gt;&lt;/span&gt; zlib1g zlib1g-dev libssl-dev libyaml-dev &lt;span class="se"&gt;\&lt;/span&gt;
&lt;span class="gp"&gt;&amp;gt;&lt;/span&gt; libsqlite3-0 libsqlite3-dev sqlite3 &lt;span class="se"&gt;\&lt;/span&gt;
&lt;span class="gp"&gt;&amp;gt;&lt;/span&gt; libxml2-dev libxslt-dev autoconf &lt;span class="se"&gt;\&lt;/span&gt;
&lt;span class="gp"&gt;&amp;gt;&lt;/span&gt; libc6-dev ncurses-dev libcurl4-openssl-dev
&lt;span class="go"&gt;Reading package lists... Done&lt;/span&gt;
&lt;span class="go"&gt;Building dependency tree&lt;/span&gt;
&lt;span class="go"&gt;Reading state information... Done&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;


&lt;p&gt;Now that we've got all those, we can finally install our chosen version of Ruby. We are lucky enough to be using the latest version of 1.9.2 so let's go ahead and use RVM to install that...&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre&gt;&lt;code class="console"&gt;&lt;span class="gp"&gt;#&lt;/span&gt; rvm install 1.9.2-p290
&lt;span class="go"&gt;Installing Ruby from source to:&lt;/span&gt;
&lt;span class="go"&gt;/usr/local/rvm/rubies/ruby-1.9.2-p290, this&lt;/span&gt;
&lt;span class="go"&gt;may take a while depending on your cpu(s)...&lt;/span&gt;
&lt;span class="go"&gt;Install of ruby-1.9.2-p290 - #complete &lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;


&lt;p&gt;The final piece of the puzzle is our web server: we like to use &lt;a href="http://nginx.org/"&gt;nginx&lt;/a&gt; and &lt;a href="http://www.modrails.com/"&gt;Passenger&lt;/a&gt; to serve our applications so let's install and configure them. First we'll need to &lt;code&gt;source&lt;/code&gt; RVM in order to switch environments...&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre&gt;&lt;code class="console"&gt;&lt;span class="gp"&gt;#&lt;/span&gt; &lt;span class="nb"&gt;source&lt;/span&gt; /usr/local/rvm/scripts/rvm
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;


&lt;p&gt;Now we can choose Ruby 1.9.2...&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre&gt;&lt;code class="console"&gt;&lt;span class="gp"&gt;#&lt;/span&gt; rvm use 1.9.2-p290
&lt;span class="go"&gt;Using /usr/local/rvm/gems/ruby-1.9.2-p290&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;


&lt;p&gt;And then we're going to install a specific version of Passenger...&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre&gt;&lt;code class="console"&gt;&lt;span class="gp"&gt;#&lt;/span&gt; gem install passenger -v3.0.8
&lt;span class="go"&gt;Fetching: fastthread-1.0.7.gem (100%)&lt;/span&gt;
&lt;span class="go"&gt;Building native extensions.  This could&lt;/span&gt;
&lt;span class="go"&gt;take a while...&lt;/span&gt;
&lt;span class="go"&gt;Fetching: daemon_controller-0.2.6.gem (100%)&lt;/span&gt;
&lt;span class="go"&gt;Fetching: rack-1.3.2.gem (100%)&lt;/span&gt;
&lt;span class="go"&gt;...&lt;/span&gt;
&lt;span class="go"&gt;Successfully installed passenger-3.0.8&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;


&lt;p&gt;Then we can use the &lt;code&gt;passenger-install-nginx-module&lt;/code&gt; command to actually compile an nginx for us.&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre&gt;&lt;code class="console"&gt;&lt;span class="gp"&gt;#&lt;/span&gt; passenger-install-nginx-module
&lt;span class="go"&gt;Welcome to the Phusion Passenger Nginx&lt;/span&gt;
&lt;span class="go"&gt;module installer, v3.0.8.&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;


&lt;p&gt;Then after following the installation wizard, you will have a complete, compiled nginx in &lt;code&gt;/opt/nginx&lt;/code&gt;. All we need to do now is configure it for our application.&lt;/p&gt;

&lt;p&gt;By default, there is only one monolithic nginx configuration file but we're going to break that down so we can manage an application's configuration separately. In order to do that, we're going to use a pattern that I originally saw in the Debian packaging of Apache by having two directories:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;code&gt;sites_available&lt;/code&gt;, with all possible application configuration;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;sites_enabled&lt;/code&gt;, with only enabled applications.&lt;/li&gt;
&lt;/ul&gt;


&lt;p&gt;The main &lt;code&gt;nginx.conf&lt;/code&gt; is then instructed to include any configuration found in &lt;code&gt;sites_enabled&lt;/code&gt; and that is merely a collection of symbolic links to configuration in &lt;code&gt;sites_available&lt;/code&gt;.&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre&gt;&lt;code class="console"&gt;&lt;span class="gp"&gt;#&lt;/span&gt; &lt;span class="nb"&gt;cd&lt;/span&gt; /opt/nginx/conf
&lt;span class="gp"&gt;#&lt;/span&gt; mkdir sites_available sites_enabled
&lt;span class="gp"&gt;#&lt;/span&gt; vi nginx.conf
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;


&lt;p&gt;Now we just need to put our application configuration in &lt;code&gt;sites_available&lt;/code&gt; and link to it from &lt;code&gt;sites_enabled&lt;/code&gt; (this way you can easily disable applications without having to delete all their configuration).&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre&gt;&lt;code class="console"&gt;&lt;span class="gp"&gt;#&lt;/span&gt; &lt;span class="nb"&gt;cd &lt;/span&gt;sites_available
&lt;span class="gp"&gt;#&lt;/span&gt; vi mcp.conf
&lt;span class="gp"&gt;#&lt;/span&gt; ln -s &lt;span class="se"&gt;\&lt;/span&gt;
&lt;span class="gp"&gt;&amp;gt;&lt;/span&gt; /opt/nginx/conf/sites_available/mcp.conf &lt;span class="se"&gt;\&lt;/span&gt;
&lt;span class="gp"&gt;&amp;gt;&lt;/span&gt; /opt/nginx/conf/sites_enabled/mcp.conf
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;


&lt;p&gt;As we installed nginx via Passenger, we will also need to make sure that we can control the web server and start it up on boot by creating an &lt;code&gt;init.d&lt;/code&gt; script. Let's say we already have a stock one that we use and put that into place...&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre&gt;&lt;code class="console"&gt;&lt;span class="gp"&gt;#&lt;/span&gt; vi /etc/init.d/nginx
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;


&lt;p&gt;Make it executable...&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre&gt;&lt;code class="console"&gt;&lt;span class="gp"&gt;#&lt;/span&gt; chmod +x /etc/init.d/nginx
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;


&lt;p&gt;As we are using Debian, we need to use &lt;code&gt;update-rc.d&lt;/code&gt; to start nginx up on boot...&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre&gt;&lt;code class="console"&gt;&lt;span class="gp"&gt;#&lt;/span&gt; update-rc.d -f nginx defaults
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;


&lt;p&gt;And then finally we can start up nginx ourselves for the first time.&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre&gt;&lt;code class="console"&gt;&lt;span class="gp"&gt;#&lt;/span&gt; /etc/init.d/nginx start
&lt;span class="go"&gt;Starting nginx: nginx&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;


&lt;p&gt;Our server is now totally set up and ready to go.&lt;/p&gt;

&lt;p&gt;It's just a case of going back to our deployment tool of choice (say, Capistrano) and performing the usual first-time deploy procedure. So we do a &lt;code&gt;deploy:setup&lt;/code&gt; to create the directories we need...&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre&gt;&lt;code class="console"&gt;&lt;span class="gp"&gt;$&lt;/span&gt; cap deploy:setup
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;


&lt;p&gt;Do a quick &lt;code&gt;check&lt;/code&gt; to make sure everything is OK...&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre&gt;&lt;code class="console"&gt;&lt;span class="gp"&gt;$&lt;/span&gt; cap deploy:check
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;


&lt;p&gt;And then do our big first &lt;code&gt;deploy&lt;/code&gt; (hoping that everything will work first time).&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre&gt;&lt;code class="console"&gt;&lt;span class="gp"&gt;$&lt;/span&gt; cap deploy:cold
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;


&lt;p&gt;You might not think that's too bad if you've done it many times before and it's become second nature to you but there are a few big problems with setting up servers in this way.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;It's not easily repeatable&lt;/strong&gt;: what if I want to run through the same process on a second server? What if a colleague of mine wants to run through the same process? Perhaps you only have one server but something has gone wrong and you need to re-do the process from scratch, how can you easily do that? Without writing down this procedure in some way, you have no canonical resource that describes how the server was set up and there is little audit trail for you to follow.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;It's not consistent&lt;/strong&gt;: I just ran a series of commands and was content with their output but, let's say an hour has passed, how confident can I be that the system is still in the state that I left it? What if someone else has logged in and accidentally deleted that &lt;code&gt;authorized_keys&lt;/code&gt; file or perhaps subtly changed permissions somewhere critical? There isn't really a good way for me to be sure about things until they start breaking which is less than ideal.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;It's not portable&lt;/strong&gt;: earlier, I had to check to see what flags I needed to pass to &lt;code&gt;adduser&lt;/code&gt; as it differs from platform to platform. When I installed the dependencies for RVM, I used &lt;code&gt;apt-get&lt;/code&gt;; when I wanted to start nginx up on boot, I used &lt;code&gt;update-rc.d&lt;/code&gt; all because I knew I was using Debian. What if I wanted to run the same process on a RedHat or CentOS machine where I'd have to use &lt;code&gt;yum&lt;/code&gt; and &lt;code&gt;chkconfig&lt;/code&gt; instead? More importantly, why do I have to care what the exact flags to pass to &lt;code&gt;adduser&lt;/code&gt; are?&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;


&lt;p&gt;The other big problem is that all of this was done assuming that I could both SSH into a live server and also run commands as &lt;code&gt;root&lt;/code&gt;; in a large organisation, that is extremely unlikely. I definitely do not have such authority and neither should I have so the real way this work would have to be done is to be described laboriously in a JIRA ticket. Perhaps I would write a list of instructions in English with a little justification for each command.&lt;/p&gt;

&lt;p&gt;That brings me to the most important weakness of all: &lt;strong&gt;people make mistakes&lt;/strong&gt;. If I end up writing a script in English describing the commands to run, what happens if a step gets missed out? What if my explanation is ambiguous and there is a misunderstanding about the order in which commands need to be run? You can get into a sticky situation quite quickly and not realise until much further down the process and it might not be obvious or trivial to rectify.&lt;/p&gt;

&lt;p&gt;Perhaps there is a better way to do this. I would argue that there is and it's called configuration management.&lt;/p&gt;

&lt;h2 id="enter-configuration-management"&gt;Enter Configuration Management&lt;/h2&gt;


&lt;p&gt;Firstly, what does the term mean? It's quite a dry title and obviously refers to the management of configuration but what is important to grasp is the oft-cited idea of "infrastructure as code". Some people describe &lt;a href="http://cukes.info/"&gt;Cucumber&lt;/a&gt; as "executable documentation" in that it is both human-readable and executable by machine to verify your business requirements; I would argue that "infrastructure as code" is a similar idea. Imagine if you could describe your infrastructure not in an ambiguous language like English but a formal grammar with something like Chef or Puppet: it would both describe the state of your servers in an unambiguous way but could also be executed to ensure consistency.&lt;/p&gt;

&lt;p&gt;How many times have you had to debug a problem with a system administrator by asking them to check the permissions or content of certain files? What if you could just state all these assumptions up front and in code?&lt;/p&gt;

&lt;p&gt;There are two main solutions for configuration management in the Ruby community (obviously there are more but these are the two most popular at the moment): Opcode's Chef and Puppet Labs' Puppet.&lt;/p&gt;

&lt;p&gt;I'm going to be talking about and sharing examples written with Puppet because that's what I use in my day job. When I joined the company, there was already some existing Puppet infrastructure so it made sense to leverage that but I did actually start learning Chef before I found that out. I want to make it clear that it doesn't matter which tool you use; give both a go and see which you prefer but the key thing is to embrace configuration management in some form. Don't let the nuances of one put you off the concept as a whole. You will see Puppet examples from me but &lt;a href="http://skillsmatter.com/podcast/home/chef-vagrant"&gt;Gareth Rushgrove will be showing examples with Chef&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Let's start with a little primer; if you've never seen Puppet before, let's do some simple examples to show you how you need to start thinking about things.&lt;/p&gt;

&lt;p&gt;Firstly, Puppet revolves around the idea of resources and this is one such resource...&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre&gt;&lt;code class="ruby"&gt;&lt;span class="n"&gt;user&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39;henry&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
  &lt;span class="k"&gt;ensure&lt;/span&gt;     &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;present&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="n"&gt;uid&lt;/span&gt;        &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39;507&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="n"&gt;gid&lt;/span&gt;        &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39;staff&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="n"&gt;shell&lt;/span&gt;      &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39;/bin/zsh&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="n"&gt;home&lt;/span&gt;       &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39;/home/henry&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="n"&gt;managehome&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="kp"&gt;true&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;


&lt;p&gt;This is written in the Puppet language which looks a little like Ruby but &lt;em&gt;isn't&lt;/em&gt; Ruby. (There is a &lt;a href="http://puppetlabs.com/blog/ruby-dsl/"&gt;Ruby DSL for Puppet&lt;/a&gt; but I'm only going to be using the original Puppet language.)&lt;/p&gt;

&lt;p&gt;This resource is describing a user called Henry: it's ensuring that he exists, that he's got a certain user ID, that his primary group is "staff", that his preferred shell is ZSH, that his home directory is in the standard place and that we will create it for him if it doesn't already exist. Notice I'm not saying, "you must run &lt;code&gt;adduser&lt;/code&gt;"; I'm simply saying "there's a user with these properties". By describing the user at this level, you can make one change, say, from &lt;code&gt;present&lt;/code&gt; to &lt;code&gt;absent&lt;/code&gt;, to remove his account from the system. By delegating the responsibility of &lt;em&gt;how&lt;/em&gt; to actually achieve these things to Puppet, you are free to keep your configuration quite sparse.&lt;/p&gt;

&lt;p&gt;How would you actually use this? Let's take that definition, save it in a file called &lt;code&gt;henry.pp&lt;/code&gt; and then run the following command...&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre&gt;&lt;code class="console"&gt;&lt;span class="gp"&gt;$&lt;/span&gt; sudo puppet apply henry.pp
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;


&lt;p&gt;What Puppet's going to do is look at your system, check for a user named Henry and, as he doesn't yet exist, create him.&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre&gt;&lt;code class="console"&gt;&lt;span class="go"&gt;notice:&lt;/span&gt;
&lt;span class="go"&gt;/Stage[main]//User[henry]/ensure: created&lt;/span&gt;
&lt;span class="go"&gt;notice: Finished catalog run in 0.25 seconds&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;


&lt;p&gt;We can verify that everything has run successfully by having a look in &lt;code&gt;/etc/passwd&lt;/code&gt;...&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre&gt;&lt;code class="console"&gt;&lt;span class="gp"&gt;$&lt;/span&gt; grep henry /etc/passwd
&lt;span class="go"&gt;henry:x:507:50::/home/henry:/bin/zsh&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;


&lt;p&gt;As we can see, the user has been created and with the properties we specified.&lt;/p&gt;

&lt;p&gt;If someone now logs into the server and decides that Henry should be using Bash instead of ZSH...&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre&gt;&lt;code class="console"&gt;&lt;span class="gp"&gt;$&lt;/span&gt; sudo chsh henry
&lt;span class="go"&gt;Changing shell for henry.&lt;/span&gt;
&lt;span class="go"&gt;New shell [/bin/zsh]: /bin/bash&lt;/span&gt;
&lt;span class="go"&gt;Shell changed.&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;


&lt;p&gt;Then the next time you run Puppet it's actually going to look at the system and spot the discrepancy between our specification and the state of the system. Most importantly, it will attempt to resolve this issue itself...&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre&gt;&lt;code class="console"&gt;&lt;span class="gp"&gt;$&lt;/span&gt; sudo puppet apply henry.pp
&lt;span class="go"&gt;notice:&lt;/span&gt;
&lt;span class="go"&gt;/Stage[main]//User[henry]/shell:&lt;/span&gt;
&lt;span class="go"&gt;shell changed &amp;#39;/bin/bash&amp;#39; to &amp;#39;/bin/zsh&amp;#39;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;


&lt;p&gt;Puppet is clever enough to just to make the appropriate changes to bring the system in-line with our specification. Notice that we didn't tell it to explicitly run &lt;code&gt;chsh&lt;/code&gt; and that it didn't need to delete and recreate the user. This is the idea of consistency that was missing from the manual approach and also shows how powerful Puppet can be in enforcing system state.&lt;/p&gt;

&lt;p&gt;As well as specifying users, Puppet has many different types of resource. The official documentation refers to three in particular as the "Trifecta":&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre&gt;&lt;code class="ruby"&gt;&lt;span class="n"&gt;package&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39;openssh-server&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
  &lt;span class="k"&gt;ensure&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;installed&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;


&lt;p&gt;A &lt;code&gt;package&lt;/code&gt; resource is simply a software package that you might install via &lt;code&gt;apt-get&lt;/code&gt; on Debian, &lt;code&gt;yum&lt;/code&gt; on RedHat, etc.&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre&gt;&lt;code class="ruby"&gt;&lt;span class="n"&gt;file&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39;/etc/sudoers&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
  &lt;span class="k"&gt;ensure&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;present&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;


&lt;p&gt;The &lt;code&gt;file&lt;/code&gt; resource allows you to perform operations on the file system; this example simply ensures that a file at &lt;code&gt;/etc/sudoers&lt;/code&gt; exists and will create one if not. This is actually a very powerful resource type and we will see more of it later.&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre&gt;&lt;code class="ruby"&gt;&lt;span class="n"&gt;service&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39;sshd&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
  &lt;span class="k"&gt;ensure&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;running&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;


&lt;p&gt;The &lt;code&gt;service&lt;/code&gt; resource describes long running processes like those you would manage with an &lt;code&gt;init.d&lt;/code&gt; script; in this example, it just makes sure the process labelled &lt;code&gt;sshd&lt;/code&gt; is running.&lt;/p&gt;

&lt;p&gt;I've shown examples using &lt;code&gt;puppet apply&lt;/code&gt; but, in real life, you would probably use a different approach altogether: by having a separate Puppet Master server and having your nodes all run the Puppet Agent, you would actually store your configuration on the Master and watch it be applied every 30 minutes by default on all nodes. This is how you can be reasonably confident of your systems' consistency but to keep things simple, we're going to leave that out for now.&lt;/p&gt;

&lt;h2 id="the-better-way"&gt;The Better Way&lt;/h2&gt;


&lt;p&gt;So let's go back to our Rails application and go through its set up again but this time using Puppet.&lt;/p&gt;

&lt;p&gt;Last time, I ran &lt;code&gt;adduser&lt;/code&gt; and a group was created simultaneously but it's good to be more explicit about it. So let's state there is a group named &lt;code&gt;mcp&lt;/code&gt; and then a user named &lt;code&gt;mcp&lt;/code&gt; with that as its primary group and let's manage their home directory as well...&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre&gt;&lt;code class="ruby"&gt;&lt;span class="n"&gt;group&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39;mcp&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
  &lt;span class="k"&gt;ensure&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;present&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="n"&gt;user&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39;mcp&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
  &lt;span class="k"&gt;ensure&lt;/span&gt;     &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;present&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="n"&gt;gid&lt;/span&gt;        &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39;mcp&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="n"&gt;home&lt;/span&gt;       &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39;/home/mcp&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="n"&gt;managehome&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="kp"&gt;true&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;


&lt;p&gt;One thing that is interesting to note here is that the user refers to the group we created above it. You might think that is due to Puppet executing things sequentially but that's not true: Puppet does not use the order of definitions to derive dependencies. Instead, when you write resources, you refer to them with a lowercase resource type such as &lt;code&gt;group&lt;/code&gt;:&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre&gt;&lt;code class="ruby"&gt;&lt;span class="n"&gt;group&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39;mcp&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
  &lt;span class="k"&gt;ensure&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;present&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;


&lt;p&gt;But if you then use title case such as &lt;code&gt;Group&lt;/code&gt;:&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre&gt;&lt;code class="ruby"&gt;&lt;span class="no"&gt;Group&lt;/span&gt;&lt;span class="o"&gt;[&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;mcp&amp;#39;&lt;/span&gt;&lt;span class="o"&gt;]&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;


&lt;p&gt;You are actually creating a reference to your resource which can then be used in other definitions like so:&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre&gt;&lt;code class="ruby"&gt;&lt;span class="n"&gt;user&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39;mcp&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
  &lt;span class="k"&gt;ensure&lt;/span&gt;  &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;present&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="nb"&gt;require&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="no"&gt;Group&lt;/span&gt;&lt;span class="o"&gt;[&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;mcp&amp;#39;&lt;/span&gt;&lt;span class="o"&gt;]&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;


&lt;p&gt;This explictly states that the user &lt;code&gt;mcp&lt;/code&gt; will not be checked until the &lt;em&gt;group&lt;/em&gt; &lt;code&gt;mcp&lt;/code&gt; has been enforced. The reason we didn't do that earlier is because Puppet will &lt;em&gt;autorequire&lt;/em&gt; a lot of obvious things (you can check this in the documentation) but it never hurts to be explicit with dependencies.&lt;/p&gt;

&lt;p&gt;If you accidentally set up resources that depend on each other like so:&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre&gt;&lt;code class="ruby"&gt;&lt;span class="n"&gt;user&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39;mcp&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
  &lt;span class="k"&gt;ensure&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;present&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="nb"&gt;require&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="no"&gt;Group&lt;/span&gt;&lt;span class="o"&gt;[&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;mcp&amp;#39;&lt;/span&gt;&lt;span class="o"&gt;]&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="n"&gt;group&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39;mcp&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
  &lt;span class="k"&gt;ensure&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;present&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="nb"&gt;require&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="no"&gt;User&lt;/span&gt;&lt;span class="o"&gt;[&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;mcp&amp;#39;&lt;/span&gt;&lt;span class="o"&gt;]&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;


&lt;p&gt;Puppet will actually spot the issue when you attempt to run it:&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre&gt;&lt;code class="console"&gt;&lt;span class="go"&gt;err: Could not apply complete catalog:&lt;/span&gt;
&lt;span class="go"&gt;Found dependency cycles in the following&lt;/span&gt;
&lt;span class="go"&gt;relationships:&lt;/span&gt;
&lt;span class="go"&gt;User[mcp] =&amp;gt; Group[mcp], Group[mcp] =&amp;gt; User[mcp];&lt;/span&gt;
&lt;span class="go"&gt;try using the &amp;#39;--graph&amp;#39; option and open the &amp;#39;.dot&amp;#39;&lt;/span&gt;
&lt;span class="go"&gt;files in OmniGraffle or GraphViz&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;


&lt;p&gt;This is because Puppet builds a &lt;a href="http://en.wikipedia.org/wiki/Directed_acyclic_graph"&gt;directed acyclic graph&lt;/a&gt; of your resources and can actually output the whole thing as a &lt;code&gt;.dot&lt;/code&gt; file as you can see in the output above.&lt;/p&gt;

&lt;p&gt;Now that we have user and group, we need to set up our SSH keys. Previously, we had to create the &lt;code&gt;.ssh&lt;/code&gt; folder, add our key to &lt;code&gt;authorized_keys&lt;/code&gt; and make sure that the ownership and permissions were set correctly. With Puppet, you can forget all that and simply use the built-in &lt;code&gt;ssh_authorized_key&lt;/code&gt; resource type:&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre&gt;&lt;code class="ruby"&gt;&lt;span class="n"&gt;ssh_authorized_key&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39;mcp-mudge&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
  &lt;span class="k"&gt;ensure&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;present&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="n"&gt;key&lt;/span&gt;    &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39;AAAAB3NzaC1yc2EAAAAB...&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="n"&gt;type&lt;/span&gt;   &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;dsa&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="n"&gt;user&lt;/span&gt;   &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39;mcp&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;


&lt;p&gt;As for the directory structure: we created &lt;code&gt;apps&lt;/code&gt;, &lt;code&gt;apps/mcp&lt;/code&gt;, &lt;code&gt;apps/mcp/shared&lt;/code&gt; and &lt;code&gt;apps/mcp/shared/config&lt;/code&gt; so let's do the same using the &lt;code&gt;file&lt;/code&gt; resource:&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre&gt;&lt;code class="ruby"&gt;&lt;span class="n"&gt;file&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="s1"&gt;&amp;#39;/home/mcp/apps&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="k"&gt;ensure&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;directory&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;owner&lt;/span&gt;  &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39;mcp&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;group&lt;/span&gt;  &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39;mcp&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

  &lt;span class="s1"&gt;&amp;#39;/home/mcp/apps/mcp&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="k"&gt;ensure&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;directory&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;owner&lt;/span&gt;  &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39;mcp&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;group&lt;/span&gt;  &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39;mcp&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

  &lt;span class="s1"&gt;&amp;#39;/home/mcp/apps/mcp/shared&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="k"&gt;ensure&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;directory&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;owner&lt;/span&gt;  &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39;mcp&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;group&lt;/span&gt;  &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39;mcp&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

  &lt;span class="s1"&gt;&amp;#39;/home/mcp/apps/mcp/shared/config&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="k"&gt;ensure&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;directory&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;owner&lt;/span&gt;  &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39;mcp&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;group&lt;/span&gt;  &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39;mcp&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;


&lt;p&gt;This makes sure that each path is a directory owned by &lt;code&gt;mcp&lt;/code&gt;; you can make this more concise by listing multiple paths at once but I've kept each definition discrete here for clarity.&lt;/p&gt;

&lt;p&gt;The next step is slightly more interesting: the management of sensitive configuration files. Last time we had someone trusted put the file in place for us; we can recreate that process again:&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre&gt;&lt;code class="ruby"&gt;&lt;span class="n"&gt;file&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39;/home/mcp/apps/mcp/shared/config/database.yml&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
  &lt;span class="k"&gt;ensure&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;present&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="n"&gt;owner&lt;/span&gt;  &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39;mcp&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="n"&gt;group&lt;/span&gt;  &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39;mcp&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="n"&gt;source&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39;puppet:///modules/mcp/database.yml&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;


&lt;p&gt;The first part is clear enough but the &lt;code&gt;source&lt;/code&gt; parameter is the interesting one. Without going into too much detail, as well as having manifests written in the Puppet language, you can also include other artefacts such as whole configuration files that will be copied into place. However, this example is no good as it assumes that I have the live &lt;code&gt;database.yml&lt;/code&gt; to hand.&lt;/p&gt;

&lt;p&gt;However, the &lt;code&gt;source&lt;/code&gt; option is really quite powerful and allows you specify not just one but many paths for a particular file:&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre&gt;&lt;code class="ruby"&gt;&lt;span class="n"&gt;file&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39;/some/config.yml&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
  &lt;span class="n"&gt;source&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="o"&gt;[&lt;/span&gt;
    &lt;span class="s1"&gt;&amp;#39;puppet:///confidential/config.yml&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="s1"&gt;&amp;#39;puppet:///modules/mcp/config.yml&amp;#39;&lt;/span&gt;
  &lt;span class="o"&gt;]&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;


&lt;p&gt;When Puppet attempts to create this file, it will check each path in the &lt;code&gt;source&lt;/code&gt; list until it finds one it can access. In this way, you can have a &lt;code&gt;confidential&lt;/code&gt; file server that can only be accessed from the live servers: when you run this manifest there, it will simply fetch that version but when you run it anywhere else, it will fall through the second version.&lt;/p&gt;

&lt;p&gt;However, this still isn't quite right as it assumes that you only have two types of servers: live and not. What if you have tiers such as staging or test? What you can actually do is use variable interpolation in the &lt;code&gt;source&lt;/code&gt; paths like so:&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre&gt;&lt;code class="ruby"&gt;&lt;span class="n"&gt;file&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39;/some/config.yml&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
  &lt;span class="n"&gt;source&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="o"&gt;[&lt;/span&gt;
    &lt;span class="s2"&gt;&amp;quot;puppet:///confidential/config.yml.$hostname&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="s2"&gt;&amp;quot;puppet:///confidential/config.yml.$tier&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="s1"&gt;&amp;#39;puppet:///modules/mcp/config.yml&amp;#39;&lt;/span&gt;
  &lt;span class="o"&gt;]&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;


&lt;p&gt;As &lt;code&gt;$hostname&lt;/code&gt; will be the hostname of your server (e.g. &lt;code&gt;webserver1&lt;/code&gt;) then you can have a file such as &lt;code&gt;config.yml.webserver1&lt;/code&gt; and that will be used on that machine; if no such file exists, it will fall through to &lt;code&gt;config.yml.$tier&lt;/code&gt; and &lt;code&gt;$tier&lt;/code&gt; can be set as follows:&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre&gt;&lt;code class="ruby"&gt;&lt;span class="vg"&gt;$tier&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39;test&amp;#39;&lt;/span&gt;
&lt;span class="c1"&gt;# =&amp;gt; &amp;quot;puppet:///confidential/config.yml.test&amp;quot;&lt;/span&gt;

&lt;span class="vg"&gt;$tier&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39;staging&amp;#39;&lt;/span&gt;
&lt;span class="c1"&gt;# =&amp;gt; &amp;quot;puppet:///confidential/config.yml.staging&amp;quot;&lt;/span&gt;

&lt;span class="vg"&gt;$tier&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39;live&amp;#39;&lt;/span&gt;
&lt;span class="c1"&gt;# =&amp;gt; &amp;quot;puppet:///confidential/config.yml.live&amp;quot;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;


&lt;p&gt;By simply setting a variable per tier, you can have several different configuration files but the same resource definition. This is the pattern that we are currently using at my job but there are still problems with it. The main being that you have redundant copies of configuration files when really the only thing that needs to be change between environments are usernames and passwords. When you are managing things like Java &lt;code&gt;.properties&lt;/code&gt; files, it can get tiresome to make sure that any changes are rolled out to all versions of a particular file.&lt;/p&gt;

&lt;p&gt;A better approach might be to extract the username and password out into variables...&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre&gt;&lt;code class="ruby"&gt;&lt;span class="vg"&gt;$db_username&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39;bob&amp;#39;&lt;/span&gt;
&lt;span class="vg"&gt;$db_password&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39;letmein&amp;#39;&lt;/span&gt;

&lt;span class="n"&gt;file&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39;/some/database.yml&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
  &lt;span class="n"&gt;content&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;template&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;mcp/database.yml.erb&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;


&lt;p&gt;If you can then keep those variable declarations in a file only on the Puppet Master then you can make use of them in a template. Luckily, as well as the &lt;code&gt;source&lt;/code&gt; parameter, Puppet supports specifying the exact contents of a file as a string with &lt;code&gt;content&lt;/code&gt; and, if you use the &lt;code&gt;template&lt;/code&gt; function, you can actually process an entire &lt;a href="http://www.ruby-doc.org/stdlib/libdoc/erb/rdoc/"&gt;ERB&lt;/a&gt; template just like you might be used to in Rails. In this case, our &lt;code&gt;database.yml.erb&lt;/code&gt; might look like this:&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre&gt;&lt;code class="erb"&gt;&lt;span class="x"&gt;production:&lt;/span&gt;
&lt;span class="x"&gt;  adapter: mysql&lt;/span&gt;
&lt;span class="x"&gt;  username: &lt;/span&gt;&lt;span class="cp"&gt;&amp;lt;%=&lt;/span&gt; &lt;span class="n"&gt;db_username&lt;/span&gt; &lt;span class="cp"&gt;%&amp;gt;&lt;/span&gt;&lt;span class="x"&gt;&lt;/span&gt;
&lt;span class="x"&gt;  password: &lt;/span&gt;&lt;span class="cp"&gt;&amp;lt;%=&lt;/span&gt; &lt;span class="n"&gt;db_password&lt;/span&gt; &lt;span class="cp"&gt;%&amp;gt;&lt;/span&gt;&lt;span class="x"&gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;


&lt;p&gt;This means that I can be free to change the &lt;code&gt;adapter&lt;/code&gt; in one place without worrying about the other versions of this file.&lt;/p&gt;

&lt;p&gt;There seems to be a lot of different recommendations for how to approach this management of sensitive configuration (&lt;a href="http://www.devco.net/archives/2009/08/31/complex_data_and_puppet.php"&gt;&lt;code&gt;extlookup&lt;/code&gt;&lt;/a&gt;, &lt;a href="http://docs.puppetlabs.com/guides/external_nodes.html"&gt;External Node Classifiers&lt;/a&gt;, etc.) and Chef has a built-in concept called &lt;a href="http://wiki.opscode.com/display/chef/Encrypted+Data+Bags"&gt;Encrypted Data Bags&lt;/a&gt; for this purpose but I would be keen to see how other people are doing this so please feel free to comment below. &lt;em&gt;[NB: It seems the consensus is to use &lt;a href="http://projects.puppetlabs.com/projects/hiera"&gt;Hiera&lt;/a&gt; along with an encrypted backend such as &lt;a href="https://github.com/crayfishx/hiera-gpg"&gt;hiera-gpg&lt;/a&gt;. Hiera is actually being made part of Puppet itself but for now you can use it by installing the &lt;a href="http://rubygems.org/gems/hiera"&gt;Hiera&lt;/a&gt; and &lt;a href="http://rubygems.org/gems/hiera-puppet"&gt;Hiera-Puppet&lt;/a&gt; gems.]&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;Let's install RVM and Ruby. First, we need all the dependencies and we can just use &lt;code&gt;package&lt;/code&gt; to do so:&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre&gt;&lt;code class="ruby"&gt;&lt;span class="n"&gt;package&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39;rvm-dependencies&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
  &lt;span class="k"&gt;ensure&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;installed&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="nb"&gt;name&lt;/span&gt;   &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="o"&gt;[&lt;/span&gt;
    &lt;span class="s1"&gt;&amp;#39;curl&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="s1"&gt;&amp;#39;git-core&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="s1"&gt;&amp;#39;subversion&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="s1"&gt;&amp;#39;build-essential&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;.&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;
  &lt;span class="o"&gt;]&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;


&lt;p&gt;The way that we installed RVM was very sequential: we ran a list of specific commands in a particular order. When writing manifests for Puppet, it's best not to think in terms of sequence but in terms of state and, most importantly, in a way that is idempotent.&lt;/p&gt;

&lt;p&gt;Firstly, we need the RVM installer; as it is just a single script and to save fetching it from the internet, let's include it with our modules and use the &lt;code&gt;source&lt;/code&gt; parameter we saw earlier to copy it into place with the right permissions and ownership...&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre&gt;&lt;code class="ruby"&gt;&lt;span class="n"&gt;file&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39;/root/rvm-installer&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
  &lt;span class="k"&gt;ensure&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;present&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="n"&gt;owner&lt;/span&gt;  &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39;root&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="n"&gt;group&lt;/span&gt;  &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39;root&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="n"&gt;mode&lt;/span&gt;   &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39;0700&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="n"&gt;source&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39;puppet:///modules/mcp/rvm&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;


&lt;p&gt;Now we need to run the installer and, for that, we can use the &lt;code&gt;exec&lt;/code&gt; resource which allows you to run any arbitrary command. This is discouraged when an alternative is available but we have no other choice in this situation...&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre&gt;&lt;code class="ruby"&gt;&lt;span class="nb"&gt;exec&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39;install-rvm&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
  &lt;span class="n"&gt;command&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39;/root/rvm-installer --version 1.6.32&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="n"&gt;cwd&lt;/span&gt;     &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39;/root&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="k"&gt;unless&lt;/span&gt;  &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39;grep 1.6.32 /usr/local/rvm/VERSION&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="n"&gt;path&lt;/span&gt;    &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39;/usr/bin:/usr/sbin:/bin:/sbin&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="nb"&gt;require&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="no"&gt;Package&lt;/span&gt;&lt;span class="o"&gt;[&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;rvm-dependencies&amp;#39;&lt;/span&gt;&lt;span class="o"&gt;]&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;


&lt;p&gt;You can see the &lt;code&gt;command&lt;/code&gt; to run and the directory to run it from in &lt;code&gt;cwd&lt;/code&gt; but don't forget that I said that Puppet will keep running your resources every 30 minutes and, by default, this would continually re-install RVM. This is what I meant when I said that resources must be idempotent: you must be able to run them multiple times without side effect. In order to stop this from happening, we can use the &lt;code&gt;unless&lt;/code&gt; parameter to define a command that, should it return successfully, means that this resource doesn't need to be executed. The command that we are using is &lt;code&gt;grep&lt;/code&gt; and we are checking to see that our particular version of RVM is installed; in this way, when RVM isn't installed at all, the command will fail and therefore the installer will run and if the version is out of date, it will also run, thereby seamlessly upgrading your installation. Also note the &lt;code&gt;require&lt;/code&gt; dependency which ensures that RVM is not installed before its dependencies are satisfied.&lt;/p&gt;

&lt;p&gt;Now we can do &lt;code&gt;rvm install ruby&lt;/code&gt; in much the same way:&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre&gt;&lt;code class="ruby"&gt;&lt;span class="nb"&gt;exec&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39;rvm install ruby-1.9.2-p290&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
  &lt;span class="n"&gt;creates&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39;/usr/local/rvm/rubies/ruby-1.9.2-p290&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="n"&gt;timeout&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="mi"&gt;1800&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="n"&gt;path&lt;/span&gt;    &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39;/usr/local/rvm/bin:/usr/bin:/usr/sbin:/bin:/sbin&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="nb"&gt;require&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="no"&gt;File&lt;/span&gt;&lt;span class="o"&gt;[&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;install-rvm&amp;#39;&lt;/span&gt;&lt;span class="o"&gt;]&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;


&lt;p&gt;Instead of using &lt;code&gt;unless&lt;/code&gt; which runs any arbitrary command and checks its exit code, we can use &lt;code&gt;creates&lt;/code&gt; which checks for the presence of a file. This resource will therefore only be installed if &lt;code&gt;/usr/local/rvm/rubies/ruby-1.9.2-p290&lt;/code&gt; doesn't already exist. As compiling Ruby can take some time, we also bump up the default &lt;code&gt;timeout&lt;/code&gt; so Puppet doesn't assume something has gone wrong.&lt;/p&gt;

&lt;p&gt;Next is the installation of nginx and Passenger for which we will also use the &lt;code&gt;exec&lt;/code&gt; resource.&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre&gt;&lt;code class="ruby"&gt;&lt;span class="nb"&gt;exec&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39;install-passenger-3.0.8&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
  &lt;span class="n"&gt;command&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39;rvm-shell ruby-1.9.2-p290 -c &amp;quot;gem install passenger -v3.0.8&amp;quot;&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="k"&gt;unless&lt;/span&gt;  &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39;rvm-shell ruby-1.9.2-p290 -c &amp;quot;gem list passenger -v3.0.8 -i&amp;quot;&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="n"&gt;path&lt;/span&gt;    &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39;/usr/local/rvm/bin:/usr/bin:/usr/sbin:/bin:/sbin&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="n"&gt;timeout&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="mi"&gt;1800&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="nb"&gt;require&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="no"&gt;Exec&lt;/span&gt;&lt;span class="o"&gt;[&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;rvm install ruby-1.9.2-p290&amp;#39;&lt;/span&gt;&lt;span class="o"&gt;]&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;


&lt;p&gt;We need to always think about the two commands: one to run and one to check whether it has already been run. Here we are simply installing a gem but not if it is already installed (&lt;code&gt;gem list -i&lt;/code&gt; just being something built into RubyGems).&lt;/p&gt;

&lt;p&gt;Nginx is much the same:&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre&gt;&lt;code class="ruby"&gt;&lt;span class="nb"&gt;exec&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39;install-nginx&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
  &lt;span class="n"&gt;command&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39;rvm-shell ruby-1.9.2-p290 -c &amp;quot;passenger-install-nginx-module...&amp;quot;&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="n"&gt;creates&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39;...agents/nginx/PassengerHelperAgent&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="n"&gt;timeout&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="mi"&gt;1800&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="n"&gt;path&lt;/span&gt;    &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39;/usr/local/rvm/bin:/usr/bin:/usr/sbin:/bin:/sbin&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="nb"&gt;require&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="no"&gt;Exec&lt;/span&gt;&lt;span class="o"&gt;[&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;install-passenger-3.0.8&amp;#39;&lt;/span&gt;&lt;span class="o"&gt;]&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;


&lt;p&gt;From our experience, we know that when Passenger and nginx compile successfully, it results in &lt;code&gt;PassengerHelperAgent&lt;/code&gt; being present in a certain directory so we can use this to detect whether nginx has been installed or not. I've truncated some of the paths to fit on the slides but the top command simply has flags to perform a headless install of Passenger and looks like so:&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre&gt;&lt;code class="console"&gt;&lt;span class="gp"&gt;$&lt;/span&gt; rvm-shell ruby-1.9.2-p290 -c &lt;span class="se"&gt;\&lt;/span&gt;
&lt;span class="gp"&gt;&amp;gt;&lt;/span&gt;  &lt;span class="s2"&gt;&amp;quot;passenger-install-nginx-module --auto \&lt;/span&gt;
&lt;span class="gp"&gt;&amp;gt;&lt;/span&gt;&lt;span class="s2"&gt;  --auto-download \&lt;/span&gt;
&lt;span class="gp"&gt;&amp;gt;&lt;/span&gt;&lt;span class="s2"&gt;  --prefix=/opt/nginx&amp;quot;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;


&lt;p&gt;This is well-documented on the Passenger web site and just means that it will not prompt for user input when installing.&lt;/p&gt;

&lt;p&gt;Finally, our nginx configuration uses the &lt;code&gt;file&lt;/code&gt; resource:&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre&gt;&lt;code class="ruby"&gt;&lt;span class="n"&gt;file&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="s1"&gt;&amp;#39;/opt/nginx/conf/sites_available/mcp.conf&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="k"&gt;ensure&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;present&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;owner&lt;/span&gt;  &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39;root&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;group&lt;/span&gt;  &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39;root&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;source&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39;puppet:///modules/mcp/mcp.conf&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

  &lt;span class="s1"&gt;&amp;#39;/opt/nginx/conf/sites_enabled/mcp.conf&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="k"&gt;ensure&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;link&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;owner&lt;/span&gt;  &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39;root&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;group&lt;/span&gt;  &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39;root&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;target&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39;/opt/nginx/conf/sites_available/mcp.conf&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;


&lt;p&gt;This just demonstrates that you can also manage symbolic links with Puppet as well and we could easily disable the site by changing &lt;code&gt;link&lt;/code&gt; to &lt;code&gt;absent&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Then we've got our &lt;code&gt;init.d&lt;/code&gt; script which we just copy:&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre&gt;&lt;code class="ruby"&gt;&lt;span class="n"&gt;file&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39;/etc/init.d/nginx&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
  &lt;span class="k"&gt;ensure&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;present&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="n"&gt;owner&lt;/span&gt;  &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39;root&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="n"&gt;group&lt;/span&gt;  &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39;root&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="n"&gt;mode&lt;/span&gt;   &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39;0755&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="n"&gt;source&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39;puppet:///modules/mcp/nginx&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="nb"&gt;require&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="no"&gt;Exec&lt;/span&gt;&lt;span class="o"&gt;[&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;install-nginx&amp;#39;&lt;/span&gt;&lt;span class="o"&gt;]&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;


&lt;p&gt;Finally, we used &lt;code&gt;update-rc.d&lt;/code&gt; to register our service previously but we can now use the &lt;code&gt;service&lt;/code&gt; resource type to do that for us:&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre&gt;&lt;code class="ruby"&gt;&lt;span class="n"&gt;service&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39;nginx&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
  &lt;span class="k"&gt;ensure&lt;/span&gt;     &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;running&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="n"&gt;enable&lt;/span&gt;     &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="kp"&gt;true&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="n"&gt;hasrestart&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="kp"&gt;true&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="n"&gt;hasstatus&lt;/span&gt;  &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="kp"&gt;true&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="n"&gt;subscribe&lt;/span&gt;  &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="no"&gt;File&lt;/span&gt;&lt;span class="o"&gt;[&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;/opt/nginx/conf/nginx.conf&amp;#39;&lt;/span&gt;&lt;span class="o"&gt;]&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="nb"&gt;require&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="o"&gt;[&lt;/span&gt;
    &lt;span class="no"&gt;File&lt;/span&gt;&lt;span class="o"&gt;[&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;/opt/nginx/conf/nginx.conf&amp;#39;&lt;/span&gt;&lt;span class="o"&gt;]&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="no"&gt;File&lt;/span&gt;&lt;span class="o"&gt;[&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;/etc/init.d/nginx&amp;#39;&lt;/span&gt;&lt;span class="o"&gt;]&lt;/span&gt;
  &lt;span class="o"&gt;]&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;


&lt;p&gt;This is quite powerful as it describes the functionality of the service (whether it supports restarting natively, whether it can report its own status, etc.) and also sets up a special type of dependency in the &lt;code&gt;subscribe&lt;/code&gt; parameter. By subscribing to the nginx configuration file, this service will automatically reload when any change to the configuration is made. This means that the web server can be tweaked and dependent services seamlessly reloaded without any need to run &lt;code&gt;init.d&lt;/code&gt; scripts. You can also see a &lt;code&gt;require&lt;/code&gt; dependency with more than one resource showing that you can have multiple dependencies at once.&lt;/p&gt;

&lt;p&gt;Now that is complete, you might be thinking that it is a lot of work and maybe you only have one server, so what's the point? I would simply ask if there was no part of that you have ever done twice; have you never had to create users or use the same directory structure more than once? If you have any sort of "best practices" for your server setup, would they not benefit from being documented in some way? What if that documentation took the form of a Puppet manifest or a Chef cookbook? If you have that then you have your one canonical source of information about your infrastructure, you can correct any mistakes in one place and watch it roll out across all your nodes and you can subject your infrastructure to normal code workflows: versioning, code review, etc. &lt;em&gt;[NB: versioning is something I neglected to mention during my presentation but it is important to mention now.]&lt;/em&gt;&lt;/p&gt;

&lt;h2 id="testing"&gt;Testing&lt;/h2&gt;


&lt;p&gt;Puppet is obviously very powerful and, as with anything that runs as &lt;code&gt;root&lt;/code&gt;, the possibility for damage is great. Therefore, it is important to be able to test your Puppet manifests before they are applied to live servers.&lt;/p&gt;

&lt;p&gt;Puppet ships with a &lt;code&gt;--noop&lt;/code&gt; flag you can use with &lt;code&gt;puppet apply&lt;/code&gt; that will effectively do a dry-run of your changes. If you are using the &lt;code&gt;file&lt;/code&gt; resource, for example, it will show you a diff of the changes it would have made without actually modifying the original files. While this can be useful as a sanity check, it falls down when doing anything slightly more involved as it is not really possible for Puppet to predict what state your system will be in after installing new software or running an arbitrary &lt;code&gt;exec&lt;/code&gt; resource.&lt;/p&gt;

&lt;p&gt;A much more thorough way to test your configuration is to make use of &lt;a href="http://vagrantup.com/"&gt;Vagrant&lt;/a&gt;. If you're not familiar with it, Vagrant is a RubyGem for managing virtual machines using &lt;a href="http://www.virtualbox.org/"&gt;Oracle's VirtualBox&lt;/a&gt;. Using this, you can effectively have one of your live servers set up as a virtual machine running on your development machine and then run your manifests against it. In this way you can see what effect your changes would have on a running system.&lt;/p&gt;

&lt;p&gt;To make use of Puppet, you modify a file named &lt;code&gt;Vagrantfile&lt;/code&gt; to point it at your modules and manifests:&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre&gt;&lt;code class="ruby"&gt;&lt;span class="n"&gt;config&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;vm&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;provision&lt;/span&gt; &lt;span class="ss"&gt;:puppet&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt;&lt;span class="n"&gt;puppet&lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt;
  &lt;span class="n"&gt;puppet&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;module_path&lt;/span&gt;    &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;&amp;quot;modules&amp;quot;&lt;/span&gt;
  &lt;span class="n"&gt;puppet&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;manifests_path&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;&amp;quot;manifests&amp;quot;&lt;/span&gt;
  &lt;span class="n"&gt;puppet&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;manifest_file&lt;/span&gt;  &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;&amp;quot;mcp.pp&amp;quot;&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;


&lt;p&gt;Then it is simply a case of bringing up your virtual machine:&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre&gt;&lt;code class="console"&gt;&lt;span class="gp"&gt;$&lt;/span&gt; bundle &lt;span class="nb"&gt;exec &lt;/span&gt;vagrant up
&lt;span class="go"&gt;[default] Box natty was not found. Fetching box...&lt;/span&gt;
&lt;span class="go"&gt;[default] Downloading with Vagrant::Downloaders::HTTP...&lt;/span&gt;
&lt;span class="go"&gt;[default] Downloading box: http://mudge.name/downloads...&lt;/span&gt;
&lt;span class="go"&gt;[default] Extracting box...&lt;/span&gt;
&lt;span class="go"&gt;[default] Verifying box...&lt;/span&gt;
&lt;span class="go"&gt;[default] Cleaning up downloaded box...&lt;/span&gt;
&lt;span class="go"&gt;[default] Importing base box &amp;#39;natty&amp;#39;...&lt;/span&gt;
&lt;span class="go"&gt;[default] Matching MAC address for NAT networking...&lt;/span&gt;
&lt;span class="go"&gt;[default] Clearing any previously set forwarded ports...&lt;/span&gt;
&lt;span class="go"&gt;[default] Forwarding ports...&lt;/span&gt;
&lt;span class="go"&gt;[default] -- ssh: 22 =&amp;gt; 2222 (adapter 1)&lt;/span&gt;
&lt;span class="go"&gt;[default] Creating shared folders metadata...&lt;/span&gt;
&lt;span class="go"&gt;[default] Running any VM customizations...&lt;/span&gt;
&lt;span class="go"&gt;[default] Booting VM...&lt;/span&gt;
&lt;span class="go"&gt;[default] Waiting for VM to boot. This can take a few minutes.&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;


&lt;p&gt;The first time you run it, Vagrant will inspect your &lt;code&gt;Vagrantfile&lt;/code&gt; to download a base box (a blank slate virtual machine, e.g. a freshly installed Ubuntu server) and then provision it with Puppet. Then you can tweak your manifests and run the following command to do another Puppet run:&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre&gt;&lt;code class="console"&gt;&lt;span class="gp"&gt;$&lt;/span&gt; bundle &lt;span class="nb"&gt;exec &lt;/span&gt;vagrant provision
&lt;span class="go"&gt;[default] Running provisioner: Vagrant::Provisioners::Puppet...&lt;/span&gt;
&lt;span class="go"&gt;[default] Running Puppet with mcp.pp...&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;


&lt;p&gt;All of the examples in this presentation are actually available as a sample project with a &lt;code&gt;Gemfile&lt;/code&gt;, &lt;code&gt;Vagrantfile&lt;/code&gt; and all the modules and manifests required. Simply get it from &lt;a href="https://github.com/mudge/managing_web_application_servers_with_puppet/tree/master/example"&gt;GitHub&lt;/a&gt;, run &lt;code&gt;bundle&lt;/code&gt; and then &lt;code&gt;bundle exec vagrant up&lt;/code&gt; to download a base box I'm hosting on Dropbox and prepare a virtual machine for our &lt;code&gt;mcp&lt;/code&gt; application including the user, SSH key (though it's just a fake key so you won't be able to log in with it), RVM, Ruby, nginx and Passenger as a demonstration of Puppet's power.&lt;/p&gt;

&lt;p&gt;While that is extremely useful, it is just another method of visual inspection; what might be better is to actual test-drive your Puppet manifests with a tool like &lt;a href="https://github.com/nistude/cucumber-puppet"&gt;Cucumber-Puppet&lt;/a&gt;. That allows you to write Cucumber features for your infrastructure:&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre&gt;&lt;code class="gherkin"&gt;&lt;span class="k"&gt;Feature:&lt;/span&gt;&lt;span class="nf"&gt; General catalog policy&lt;/span&gt;
&lt;span class="nf"&gt;  In order to ensure a host&amp;#39;s catalog&lt;/span&gt;
&lt;span class="nf"&gt;  As a manifest developer&lt;/span&gt;
&lt;span class="nf"&gt;  I want all catalogs to obey some general rules&lt;/span&gt;

&lt;span class="nf"&gt;  &lt;/span&gt;&lt;span class="k"&gt;Scenario Outline:&lt;/span&gt;&lt;span class="nf"&gt; Compile and verify catalog&lt;/span&gt;
&lt;span class="k"&gt;    Given &lt;/span&gt;&lt;span class="nf"&gt;a node specified by &amp;quot;&lt;/span&gt;&lt;span class="s"&gt;features/yaml/eg.yml&lt;/span&gt;&lt;span class="nf"&gt;&amp;quot;&lt;/span&gt;
&lt;span class="nf"&gt;    &lt;/span&gt;&lt;span class="k"&gt;When &lt;/span&gt;&lt;span class="nf"&gt;I compile its catalog&lt;/span&gt;
&lt;span class="nf"&gt;    &lt;/span&gt;&lt;span class="k"&gt;Then &lt;/span&gt;&lt;span class="nf"&gt;compilation should succeed&lt;/span&gt;
&lt;span class="nf"&gt;    &lt;/span&gt;&lt;span class="k"&gt;And &lt;/span&gt;&lt;span class="nf"&gt;all resource dependencies should resolve&lt;/span&gt;

&lt;span class="nf"&gt;    &lt;/span&gt;&lt;span class="k"&gt;Examples:&lt;/span&gt;
&lt;span class="k"&gt;      |&lt;/span&gt;&lt;span class="nv"&gt; hostname&lt;/span&gt;&lt;span class="k"&gt;  |&lt;/span&gt;&lt;span class="nf"&gt;&lt;/span&gt;
&lt;span class="k"&gt;      |&lt;/span&gt;&lt;span class="s"&gt; localhost&lt;/span&gt;&lt;span class="k"&gt; |&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;


&lt;p&gt;This is just a sample feature from their web site and I admit that I haven't used this yet but it is definitely something I want to look into more closely. There are some exciting tools emerging for Chef regarding testing with minitest and my hope is that Puppet will also benefit from the enthusiasm in this area. &lt;em&gt;[NB: shortly after my talk, there was &lt;a href="http://puppetlabs.com/blog/testing-modules-in-the-puppet-forge/"&gt;an extensive post on testing Puppet on the Puppet Labs blog&lt;/a&gt;.]&lt;/em&gt;&lt;/p&gt;

&lt;h2 id="wrap-up"&gt;Wrap-Up&lt;/h2&gt;


&lt;p&gt;You might be thinking that, as a developer, it is not really your job to worry about the maintenance of web servers but I would argue that being a developer doesn't stop once the specs pass on your machine: your job is to deliver your software all the way to the customer and therefore successfully onto live servers. There is this gathering movement of "devops" trying to bridge the gap between operations and development and I think it is an obvious idea to collaborate more: no one ever really benefited from operating entirely in a silo. It's a movement that has been picking up speed within our organisation and has definitely improved things from the dark days of throwing code and instructions over the wall and waiting for a ticket to come back with a minor misunderstanding or a simple missed step.&lt;/p&gt;

&lt;p&gt;If you want to know more, Puppet Labs recently revamped all of their documentation so it is well worth reading &lt;a href="http://docs.puppetlabs.com/learning/"&gt;"Learning Puppet"&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;You can follow me on Twitter, &lt;a href="http://twitter.com/mudge"&gt;@mudge&lt;/a&gt;: it's mostly pictures of coffee and cakes but occasionally some technical stuff.&lt;/p&gt;

&lt;p&gt;&lt;a href="http://github.com/mudge"&gt;github.com/mudge&lt;/a&gt; is where you'll find the source code to this presentation, it's called &lt;a href="http://github.com/mudge/managing_web_application_servers_with_puppet"&gt;&lt;code&gt;managing_web_application_servers_with_puppet&lt;/code&gt;&lt;/a&gt; and it's just a ShowOff presentation with the &lt;a href="https://github.com/mudge/managing_web_application_servers_with_puppet/tree/master/example"&gt;&lt;code&gt;example&lt;/code&gt; directory&lt;/a&gt; I mentioned earlier.&lt;/p&gt;

&lt;p&gt;Thanks for listening.&lt;/p&gt;
</content>
</entry>

<entry>
  <title>Combining and Compressing Disqus' and Google Analytics' JavaScript</title>
  <link href="/2011/01/29/combining-and-compressing-disqus-and-google-analytics-javascript.html" />
  <id>http://mudge.github.com/2011/01/29/combining-and-compressing-disqus-and-google-analytics-javascript.html</id>
  <updated>2011-01-29T00:00:00-08:00</updated>
  <summary>A step-by-step refactoring of Disqus' and Google Analytics' JavaScript.</summary>
  <content type="html">&lt;p&gt;In setting up this &lt;a href="http://jekyllrb.com"&gt;Jekyll&lt;/a&gt;-powered blog, I had cause to dust off my &lt;a href="http://www.google.com/analytics/"&gt;Google
Analytics&lt;/a&gt; account and finally take &lt;a href="http://disqus.com"&gt;Disqus&lt;/a&gt; for a spin. Both services
provide small snippets of JavaScript for inclusion in your web pages; Google
Analytics' looks much like this (with line breaks added for readability):&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre&gt;&lt;code class="html"&gt;&lt;span class="nt"&gt;&amp;lt;script &lt;/span&gt;&lt;span class="na"&gt;type=&lt;/span&gt;&lt;span class="s"&gt;&amp;quot;text/javascript&amp;quot;&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
  &lt;span class="kd"&gt;var&lt;/span&gt; &lt;span class="nx"&gt;_gaq&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;_gaq&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="p"&gt;[];&lt;/span&gt;
  &lt;span class="nx"&gt;_gaq&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;push&lt;/span&gt;&lt;span class="p"&gt;([&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;_setAccount&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39;UA-XXXXX-X&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;]);&lt;/span&gt;
  &lt;span class="nx"&gt;_gaq&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;push&lt;/span&gt;&lt;span class="p"&gt;([&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;_trackPageview&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;]);&lt;/span&gt;

  &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kd"&gt;function&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="kd"&gt;var&lt;/span&gt; &lt;span class="nx"&gt;ga&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;document&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;createElement&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;script&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="nx"&gt;ga&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;type&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39;text/javascript&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="nx"&gt;ga&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;async&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="nx"&gt;ga&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;src&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;https:&amp;#39;&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="nb"&gt;document&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;location&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;protocol&lt;/span&gt; &lt;span class="o"&gt;?&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39;https://ssl&amp;#39;&lt;/span&gt; &lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39;http://www&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt;
      &lt;span class="s1"&gt;&amp;#39;.google-analytics.com/ga.js&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="kd"&gt;var&lt;/span&gt; &lt;span class="nx"&gt;s&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;document&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;getElementsByTagName&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;script&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;)[&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;];&lt;/span&gt;
    &lt;span class="nx"&gt;s&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;parentNode&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;insertBefore&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;ga&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;s&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="p"&gt;})();&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/script&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;


&lt;p&gt;Disqus' is as follows (with line breaks added and extraneous comments
removed):&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre&gt;&lt;code class="html"&gt;&lt;span class="nt"&gt;&amp;lt;script &lt;/span&gt;&lt;span class="na"&gt;type=&lt;/span&gt;&lt;span class="s"&gt;&amp;quot;text/javascript&amp;quot;&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
  &lt;span class="kd"&gt;var&lt;/span&gt; &lt;span class="nx"&gt;disqus_shortname&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39;example&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="c1"&gt;// var disqus_identifier = &amp;#39;unique_dynamic_id_1234&amp;#39;;&lt;/span&gt;
  &lt;span class="c1"&gt;// var disqus_url = &amp;#39;http://example.com/permalink-to-page.html&amp;#39;;&lt;/span&gt;

  &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kd"&gt;function&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="kd"&gt;var&lt;/span&gt; &lt;span class="nx"&gt;dsq&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;document&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;createElement&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;script&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="nx"&gt;dsq&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;type&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39;text/javascript&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="nx"&gt;dsq&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;async&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="nx"&gt;dsq&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;src&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39;http://&amp;#39;&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="nx"&gt;disqus_shortname&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39;.disqus.com/embed.js&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;document&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;getElementsByTagName&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;head&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;)[&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt;
      &lt;span class="nb"&gt;document&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;getElementsByTagName&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;body&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;)[&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;]).&lt;/span&gt;&lt;span class="nx"&gt;appendChild&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;dsq&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="p"&gt;})();&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/script&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;


&lt;p&gt;Both seem terse enough &lt;del&gt;and are meant for inclusion at the bottom
of your web page&lt;/del&gt; (&lt;em&gt;Update:&lt;/em&gt; &lt;a href="http://news.ycombinator.com/item?id=2157082"&gt;enomar on Hacker News pointed out that
Google's asynchronous tracking code is ideally meant to be included at the end
of the &lt;code&gt;head&lt;/code&gt; element&lt;/a&gt; while Disqus' JavaScript is meant for inclusion
wherever you want your comments to appear but both can be placed at the bottom
of your page, see "&lt;a href="http://code.google.com/apis/analytics/docs/tracking/asyncTracking.html#Installation"&gt;Getting Started with the Asynchronous Snippet&lt;/a&gt;" and the end
of this post for more information).&lt;/p&gt;

&lt;p&gt;A quick check with &lt;a href="http://mir.aculo.us/dom-monster/"&gt;Amy Hoy and Thomas Fuchs' "DOM Monster"&lt;/a&gt; shows that
the number of &lt;code&gt;script&lt;/code&gt; tags should ideally be kept to a minimum so let's combine
the two into one (and let's specify those recommended Disqus variables while we're
at it):&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre&gt;&lt;code class="html"&gt;&lt;span class="nt"&gt;&amp;lt;script &lt;/span&gt;&lt;span class="na"&gt;type=&lt;/span&gt;&lt;span class="s"&gt;&amp;quot;text/javascript&amp;quot;&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
  &lt;span class="kd"&gt;var&lt;/span&gt; &lt;span class="nx"&gt;_gaq&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;_gaq&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="p"&gt;[];&lt;/span&gt;
  &lt;span class="nx"&gt;_gaq&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;push&lt;/span&gt;&lt;span class="p"&gt;([&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;_setAccount&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39;UA-XXXXX-X&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;]);&lt;/span&gt;
  &lt;span class="nx"&gt;_gaq&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;push&lt;/span&gt;&lt;span class="p"&gt;([&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;_trackPageview&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;]);&lt;/span&gt;

  &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kd"&gt;function&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="kd"&gt;var&lt;/span&gt; &lt;span class="nx"&gt;ga&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;document&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;createElement&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;script&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="nx"&gt;ga&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;type&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39;text/javascript&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="nx"&gt;ga&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;async&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="nx"&gt;ga&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;src&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;https:&amp;#39;&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="nb"&gt;document&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;location&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;protocol&lt;/span&gt; &lt;span class="o"&gt;?&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39;https://ssl&amp;#39;&lt;/span&gt; &lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39;http://www&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt;
      &lt;span class="s1"&gt;&amp;#39;.google-analytics.com/ga.js&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="kd"&gt;var&lt;/span&gt; &lt;span class="nx"&gt;s&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;document&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;getElementsByTagName&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;script&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;)[&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;];&lt;/span&gt;
    &lt;span class="nx"&gt;s&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;parentNode&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;insertBefore&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;ga&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;s&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="p"&gt;})();&lt;/span&gt;

  &lt;span class="kd"&gt;var&lt;/span&gt; &lt;span class="nx"&gt;disqus_shortname&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39;example&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="kd"&gt;var&lt;/span&gt; &lt;span class="nx"&gt;disqus_identifier&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39;unique_dynamic_id_1234&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="kd"&gt;var&lt;/span&gt; &lt;span class="nx"&gt;disqus_url&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39;http://example.com/permalink-to-page.html&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

  &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kd"&gt;function&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="kd"&gt;var&lt;/span&gt; &lt;span class="nx"&gt;dsq&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;document&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;createElement&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;script&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="nx"&gt;dsq&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;type&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39;text/javascript&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="nx"&gt;dsq&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;async&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="nx"&gt;dsq&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;src&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39;http://&amp;#39;&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="nx"&gt;disqus_shortname&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39;.disqus.com/embed.js&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;document&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;getElementsByTagName&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;head&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;)[&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt;
      &lt;span class="nb"&gt;document&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;getElementsByTagName&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;body&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;)[&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;]).&lt;/span&gt;&lt;span class="nx"&gt;appendChild&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;dsq&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="p"&gt;})();&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/script&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;


&lt;p&gt;We could stop at this point but I am something of a sucker for compressing web pages
and JavaScript down to their bare minimum (I have been known to pore through the
&lt;a href="http://dev.w3.org/html5/spec/syntax.html#optional-tags"&gt;HTML5 specification's "optional tags" section&lt;/a&gt; and butcher
my markup appropriately).&lt;/p&gt;

&lt;p&gt;We might decide to put the whole thing into something like
Google's &lt;a href="http://closure-compiler.appspot.com/"&gt;Closure Compiler&lt;/a&gt; to optimise the code for us but let's take it more
slowly and start by looking at the &lt;code&gt;script&lt;/code&gt; tag itself: it turns out that the &lt;code&gt;type&lt;/code&gt;
attribute is optional and is &lt;a href="http://dev.w3.org/html5/spec/scripting-1.html#attr-script-type"&gt;specified as having a default value of
&lt;code&gt;text/javascript&lt;/code&gt;&lt;/a&gt; so we can immediately lose that
(&lt;em&gt;Update:&lt;/em&gt; &lt;a href="http://news.ycombinator.com/item?id=2157118"&gt;this is only true if you're using HTML5&lt;/a&gt;). It
turns out that the actual JavaScript itself is also creating &lt;code&gt;script&lt;/code&gt; tags
and specifying the &lt;code&gt;type&lt;/code&gt; attribute so let's remove those as well:&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre&gt;&lt;code class="html"&gt;&lt;span class="nt"&gt;&amp;lt;script&amp;gt;&lt;/span&gt;
  &lt;span class="kd"&gt;var&lt;/span&gt; &lt;span class="nx"&gt;_gaq&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;_gaq&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="p"&gt;[];&lt;/span&gt;
  &lt;span class="nx"&gt;_gaq&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;push&lt;/span&gt;&lt;span class="p"&gt;([&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;_setAccount&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39;UA-XXXXX-X&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;]);&lt;/span&gt;
  &lt;span class="nx"&gt;_gaq&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;push&lt;/span&gt;&lt;span class="p"&gt;([&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;_trackPageview&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;]);&lt;/span&gt;

  &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kd"&gt;function&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="kd"&gt;var&lt;/span&gt; &lt;span class="nx"&gt;ga&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;document&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;createElement&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;script&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="nx"&gt;ga&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;async&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="nx"&gt;ga&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;src&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;https:&amp;#39;&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="nb"&gt;document&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;location&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;protocol&lt;/span&gt; &lt;span class="o"&gt;?&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39;https://ssl&amp;#39;&lt;/span&gt; &lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39;http://www&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt;
      &lt;span class="s1"&gt;&amp;#39;.google-analytics.com/ga.js&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="kd"&gt;var&lt;/span&gt; &lt;span class="nx"&gt;s&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;document&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;getElementsByTagName&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;script&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;)[&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;];&lt;/span&gt;
    &lt;span class="nx"&gt;s&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;parentNode&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;insertBefore&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;ga&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;s&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="p"&gt;})();&lt;/span&gt;

  &lt;span class="kd"&gt;var&lt;/span&gt; &lt;span class="nx"&gt;disqus_shortname&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39;example&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="kd"&gt;var&lt;/span&gt; &lt;span class="nx"&gt;disqus_identifier&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39;unique_dynamic_id_1234&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="kd"&gt;var&lt;/span&gt; &lt;span class="nx"&gt;disqus_url&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39;http://example.com/permalink-to-page.html&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

  &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kd"&gt;function&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="kd"&gt;var&lt;/span&gt; &lt;span class="nx"&gt;dsq&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;document&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;createElement&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;script&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="nx"&gt;dsq&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;async&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="nx"&gt;dsq&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;src&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39;http://&amp;#39;&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="nx"&gt;disqus_shortname&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39;.disqus.com/embed.js&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;document&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;getElementsByTagName&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;head&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;)[&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt;
      &lt;span class="nb"&gt;document&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;getElementsByTagName&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;body&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;)[&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;]).&lt;/span&gt;&lt;span class="nx"&gt;appendChild&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;dsq&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="p"&gt;})();&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/script&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;


&lt;p&gt;Another thing that has caught my eye is the odd set up of the &lt;code&gt;_gaq&lt;/code&gt; variable:
it checks to see if it has already been defined (which it has not in my case),
assigns an empty array and then pushes two arrays onto it. This is due to the fact
that &lt;code&gt;_gaq&lt;/code&gt; can be modified after Google's tracking code has loaded to use more
advanced tracking features (see the &lt;a href="http://code.google.com/apis/analytics/docs/gaJS/gaJSApi_gaq.html#_gaq.push"&gt;documentation for &lt;code&gt;_gaq.push&lt;/code&gt;&lt;/a&gt; for
more information) but seeing as I am only doing basic analytics and taking a cue from
Mark Pilgrim's code from "&lt;a href="http://diveintomark.org"&gt;dive into mark&lt;/a&gt;", we can simplify
this quite drastically:&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre&gt;&lt;code class="html"&gt;&lt;span class="nt"&gt;&amp;lt;script&amp;gt;&lt;/span&gt;
  &lt;span class="kd"&gt;var&lt;/span&gt; &lt;span class="nx"&gt;_gaq&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[[&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;_setAccount&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39;UA-XXXXX-X&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;_trackPageview&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;]];&lt;/span&gt;
  &lt;span class="c1"&gt;// ... omitted for brevity ...&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/script&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;


&lt;p&gt;While we are looking at variable declarations, another easy optimisation we
can make is to take advantage of &lt;a href="https://developer.mozilla.org/en/JavaScript/Reference/Statements/var"&gt;JavaScript's syntax to declare multiple variables
at once&lt;/a&gt;:&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre&gt;&lt;code class="html"&gt;&lt;span class="nt"&gt;&amp;lt;script&amp;gt;&lt;/span&gt;
  &lt;span class="kd"&gt;var&lt;/span&gt; &lt;span class="nx"&gt;_gaq&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[[&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;_setAccount&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39;UA-XXXXX-X&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;_trackPageview&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;]],&lt;/span&gt;
      &lt;span class="nx"&gt;disqus_shortname&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39;example&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="nx"&gt;disqus_identifier&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39;unique_dynamic_id_1234&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="nx"&gt;disqus_url&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39;http://example.com/permalink-to-page.html&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="c1"&gt;// ... omitted for brevity ...&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/script&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;


&lt;p&gt;With those low-hanging fruit out of the way, we need to consider what both snippets
are actually &lt;em&gt;doing&lt;/em&gt;. They are, in fact, very similar: they are both creating
&lt;code&gt;script&lt;/code&gt; tags set to load some external JavaScript on your page in the following way:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Create a new &lt;code&gt;script&lt;/code&gt; element with &lt;a href="https://developer.mozilla.org/en/DOM/document.createElement"&gt;&lt;code&gt;document.createElement&lt;/code&gt;&lt;/a&gt;;&lt;/li&gt;
&lt;li&gt;Inform the script that it is to load asynchronously by setting the
&lt;a href="http://dev.w3.org/html5/spec/scripting-1.html#attr-script-async"&gt;&lt;code&gt;async&lt;/code&gt;&lt;/a&gt; attribute;&lt;/li&gt;
&lt;li&gt;Set the &lt;a href="http://dev.w3.org/html5/spec/scripting-1.html#attr-script-src"&gt;&lt;code&gt;src&lt;/code&gt;&lt;/a&gt; of the element thereby identifying the location of the external
script;&lt;/li&gt;
&lt;li&gt;Insert the finished element into the web page.&lt;/li&gt;
&lt;/ol&gt;


&lt;p&gt;With this knowledge, we can start to remove some repetition in the code. Firstly,
we can streamline the creation of the two &lt;code&gt;script&lt;/code&gt; tags by setting them up
simultaneously (and moving them into the same closure while we're at it):&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre&gt;&lt;code class="html"&gt;&lt;span class="nt"&gt;&amp;lt;script&amp;gt;&lt;/span&gt;
  &lt;span class="kd"&gt;var&lt;/span&gt; &lt;span class="nx"&gt;_gaq&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[[&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;_setAccount&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39;UA-XXXXX-X&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;_trackPageview&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;]],&lt;/span&gt;
      &lt;span class="nx"&gt;disqus_shortname&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39;example&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="nx"&gt;disqus_identifier&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39;unique_dynamic_id_1234&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="nx"&gt;disqus_url&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39;http://example.com/permalink-to-page.html&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

  &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kd"&gt;function&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="kd"&gt;var&lt;/span&gt; &lt;span class="nx"&gt;ga&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;document&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;createElement&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;script&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
        &lt;span class="nx"&gt;dsq&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;document&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;createElement&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;script&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="nx"&gt;ga&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;async&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;dsq&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;async&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="nx"&gt;ga&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;src&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;https:&amp;#39;&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="nb"&gt;document&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;location&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;protocol&lt;/span&gt; &lt;span class="o"&gt;?&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39;https://ssl&amp;#39;&lt;/span&gt; &lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39;http://www&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt;
      &lt;span class="s1"&gt;&amp;#39;.google-analytics.com/ga.js&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="nx"&gt;dsq&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;src&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39;http://&amp;#39;&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="nx"&gt;disqus_shortname&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39;.disqus.com/embed.js&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="kd"&gt;var&lt;/span&gt; &lt;span class="nx"&gt;s&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;document&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;getElementsByTagName&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;script&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;)[&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;];&lt;/span&gt;
    &lt;span class="nx"&gt;s&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;parentNode&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;insertBefore&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;ga&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;s&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;document&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;getElementsByTagName&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;head&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;)[&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt;
      &lt;span class="nb"&gt;document&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;getElementsByTagName&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;body&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;)[&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;]).&lt;/span&gt;&lt;span class="nx"&gt;appendChild&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;dsq&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="p"&gt;})();&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/script&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;


&lt;p&gt;We've stated that both snippets insert their newly-created &lt;code&gt;script&lt;/code&gt; elements into
the &lt;a href="https://developer.mozilla.org/en/DOM"&gt;DOM&lt;/a&gt; but they are currently doing it in different ways. Let's change this
and, in the spirit of following &lt;a href="http://code.google.com/speed/page-speed/docs/payload.html#DeferLoadingJS"&gt;Google's Performance Best Practices&lt;/a&gt;, let's
append the &lt;code&gt;script&lt;/code&gt; elements to the &lt;code&gt;body&lt;/code&gt; tag by using &lt;a href="https://developer.mozilla.org/en/DOM/document.body"&gt;&lt;code&gt;document.body&lt;/code&gt;&lt;/a&gt;
and &lt;a href="https://developer.mozilla.org/En/DOM/Node.appendChild"&gt;&lt;code&gt;appendChild&lt;/code&gt;&lt;/a&gt;:&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre&gt;&lt;code class="html"&gt;&lt;span class="nt"&gt;&amp;lt;script&amp;gt;&lt;/span&gt;
  &lt;span class="kd"&gt;var&lt;/span&gt; &lt;span class="nx"&gt;_gaq&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[[&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;_setAccount&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39;UA-XXXXX-X&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;_trackPageview&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;]],&lt;/span&gt;
      &lt;span class="nx"&gt;disqus_shortname&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39;example&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="nx"&gt;disqus_identifier&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39;unique_dynamic_id_1234&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="nx"&gt;disqus_url&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39;http://example.com/permalink-to-page.html&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

  &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kd"&gt;function&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="kd"&gt;var&lt;/span&gt; &lt;span class="nx"&gt;ga&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;document&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;createElement&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;script&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
        &lt;span class="nx"&gt;dsq&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;document&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;createElement&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;script&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="nx"&gt;ga&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;async&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;dsq&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;async&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="nx"&gt;ga&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;src&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;https:&amp;#39;&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="nb"&gt;document&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;location&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;protocol&lt;/span&gt; &lt;span class="o"&gt;?&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39;https://ssl&amp;#39;&lt;/span&gt; &lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39;http://www&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt;
      &lt;span class="s1"&gt;&amp;#39;.google-analytics.com/ga.js&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="nx"&gt;dsq&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;src&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39;http://&amp;#39;&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="nx"&gt;disqus_shortname&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39;.disqus.com/embed.js&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="nb"&gt;document&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;body&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;appendChild&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;ga&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="nb"&gt;document&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;body&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;appendChild&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;dsq&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="p"&gt;})();&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/script&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;


&lt;p&gt;We're now in much better shape than when we started and a lot closer to following
the &lt;a href="http://en.wikipedia.org/wiki/Don't_repeat_yourself"&gt;Don't Repeat Yourself (DRY) principle&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;If you want to take this even further then there are a few other changes that you
might consider making:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;If your site is only served via HTTP and not HTTPS then you can lose the
&lt;code&gt;'https:' == document.location.protocol&lt;/code&gt; check;&lt;/li&gt;
&lt;li&gt;You can remove the string concatenation that uses the &lt;code&gt;disqus_shortname&lt;/code&gt; by
explicitly setting the &lt;code&gt;src&lt;/code&gt; to your own Disqus URL such as
&lt;code&gt;http://example.disqus.com/embed.js&lt;/code&gt;;&lt;/li&gt;
&lt;li&gt;Instead of calling &lt;code&gt;document.body&lt;/code&gt; twice, you can cache it once in a variable
which you can then reuse.&lt;/li&gt;
&lt;li&gt;A quick check with &lt;a href="http://www.jslint.com"&gt;JSLint&lt;/a&gt; reveals that invoking a function defined inside
parentheses (e.g. &lt;code&gt;(function() { ... })()&lt;/code&gt;) should be done &lt;em&gt;inside&lt;/em&gt; the
parentheses instead of outside (e.g. &lt;code&gt;(function() { ... }())&lt;/code&gt;).&lt;/li&gt;
&lt;/ol&gt;


&lt;p&gt;If you decide to take these (admittedly more drastic) steps then you might end up
with something like the following:&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre&gt;&lt;code class="html"&gt;&lt;span class="nt"&gt;&amp;lt;script&amp;gt;&lt;/span&gt;
  &lt;span class="kd"&gt;var&lt;/span&gt; &lt;span class="nx"&gt;_gaq&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[[&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;_setAccount&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39;UA-XXXXX-X&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;_trackPageview&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;]],&lt;/span&gt;
      &lt;span class="nx"&gt;disqus_shortname&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39;example&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="nx"&gt;disqus_identifier&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39;unique_dynamic_id_1234&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="nx"&gt;disqus_url&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39;http://example.com/permalink-to-page.html&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

  &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kd"&gt;function&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="kd"&gt;var&lt;/span&gt; &lt;span class="nx"&gt;ga&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;document&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;createElement&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;script&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
        &lt;span class="nx"&gt;dsq&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;document&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;createElement&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;script&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
        &lt;span class="nx"&gt;body&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;document&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;body&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="nx"&gt;ga&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;async&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;dsq&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;async&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="nx"&gt;ga&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;src&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39;http://www.google-analytics.com/ga.js&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="nx"&gt;dsq&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;src&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39;http://example.disqus.com/embed.js&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="nx"&gt;body&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;appendChild&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;ga&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="nx"&gt;body&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;appendChild&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;dsq&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="p"&gt;}());&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/script&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;


&lt;p&gt;At this point you can now turn to more brutal compressors such as &lt;a href="http://developer.yahoo.com/yui/compressor/"&gt;YUI
Compressor&lt;/a&gt; or the aforementioned &lt;a href="http://closure-compiler.appspot.com/"&gt;Closure Compiler&lt;/a&gt; which (after some
tweaks to keep &lt;a href="http://www.jslint.com"&gt;JSLint&lt;/a&gt; happy) will result in a single snippet like
so (line breaks added for some semblance of readability):&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre&gt;&lt;code class="html"&gt;&lt;span class="nt"&gt;&amp;lt;script&amp;gt;&lt;/span&gt;
&lt;span class="kd"&gt;var&lt;/span&gt; &lt;span class="nx"&gt;_gaq&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;[[&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;_setAccount&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;UA-XXXXX-X&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;],[&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;_trackPageview&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;]],&lt;/span&gt;
&lt;span class="nx"&gt;disqus_shortname&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;example&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="nx"&gt;disqus_identifier&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;unique_dynamic_id_1234&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="nx"&gt;disqus_url&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;http://example.com/permalink-to-page.html&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;;(&lt;/span&gt;&lt;span class="kd"&gt;function&lt;/span&gt;&lt;span class="p"&gt;(){&lt;/span&gt;
&lt;span class="kd"&gt;var&lt;/span&gt; &lt;span class="nx"&gt;a&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="nb"&gt;document&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;createElement&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;script&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;&lt;span class="nx"&gt;b&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="nb"&gt;document&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;createElement&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;script&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
&lt;span class="nx"&gt;c&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="nb"&gt;document&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;body&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;&lt;span class="nx"&gt;a&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;async&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="nx"&gt;b&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;async&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;&lt;span class="nx"&gt;a&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;src&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;http://www.google-analytics.com/ga.js&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="nx"&gt;b&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;src&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;http://example.disqus.com/embed.js&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;&lt;span class="nx"&gt;c&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;appendChild&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;a&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;&lt;span class="nx"&gt;c&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;appendChild&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;b&lt;/span&gt;&lt;span class="p"&gt;);}());&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/script&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;


&lt;p&gt;Of course, there's no reason for it to end there. I've got it down to 412
characters while still passing &lt;a href="http://www.jslint.com"&gt;JSLint&lt;/a&gt;, feel free to post your own attempts in
the comments:&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre&gt;&lt;code class="html"&gt;&lt;span class="nt"&gt;&amp;lt;script&amp;gt;&lt;/span&gt;
&lt;span class="kd"&gt;var&lt;/span&gt; &lt;span class="nx"&gt;_gaq&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;[[&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;_setAccount&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;UA-XXXXX-X&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;],[&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;_trackPageview&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;]],&lt;/span&gt;
&lt;span class="nx"&gt;disqus_shortname&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;example&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="nx"&gt;disqus_identifier&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;unique_dynamic_id_1234&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="nx"&gt;disqus_url&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;http://example.com/permalink-to-page.html&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;;(&lt;/span&gt;&lt;span class="kd"&gt;function&lt;/span&gt;&lt;span class="p"&gt;(){&lt;/span&gt;
&lt;span class="kd"&gt;var&lt;/span&gt; &lt;span class="nx"&gt;a&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="nb"&gt;document&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="nx"&gt;b&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="nx"&gt;a&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;createElement&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;script&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;&lt;span class="nx"&gt;c&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="nx"&gt;a&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;body&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="nx"&gt;d&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="nx"&gt;b&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;async&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;&lt;span class="nx"&gt;d&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="nx"&gt;b&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;cloneNode&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kc"&gt;false&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;&lt;span class="nx"&gt;b&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;src&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;http://www.google-analytics.com/ga.js&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="nx"&gt;d&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;src&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;http://example.disqus.com/embed.js&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;&lt;span class="nx"&gt;c&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;appendChild&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;b&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;&lt;span class="nx"&gt;c&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;appendChild&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;d&lt;/span&gt;&lt;span class="p"&gt;);}());&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/script&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;


&lt;p&gt;There are only two rules:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Your solution must not produce any errors from &lt;a href="http://www.jslint.com"&gt;JSLint&lt;/a&gt; with "Assume a browser"
ticked;&lt;/li&gt;
&lt;li&gt;The only global variables that should be declared are &lt;code&gt;_gaq&lt;/code&gt;,
&lt;code&gt;disqus_identifier&lt;/code&gt;, &lt;code&gt;disqus_shortname&lt;/code&gt; &lt;del&gt;and &lt;code&gt;disqus_url&lt;/code&gt;&lt;/del&gt;
(&lt;em&gt;Update:&lt;/em&gt; Disqus software engineer &lt;a href="http://anton.kovalyov.net/"&gt;Anton Kovalyov&lt;/a&gt; says in the comments
that &lt;code&gt;disqus_url&lt;/code&gt; is not required if &lt;code&gt;disqus_identifier&lt;/code&gt; is set so feel free
to leave that one out).&lt;/li&gt;
&lt;/ol&gt;


&lt;p&gt;&lt;em&gt;Update:&lt;/em&gt; Based on feedback from &lt;a href="http://www.steveklabnik.com"&gt;Steve Klabnik&lt;/a&gt;, &lt;a href="http://anton.kovalyov.net/"&gt;Anton Kovalyov&lt;/a&gt; and
&lt;a href="http://www.moddular.org"&gt;Andrew Walker&lt;/a&gt;, here is a solution that consists of only 345 characters:&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre&gt;&lt;code class="html"&gt;&lt;span class="nt"&gt;&amp;lt;script&amp;gt;&lt;/span&gt;
&lt;span class="kd"&gt;var&lt;/span&gt; &lt;span class="nx"&gt;_gaq&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;[[&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;_setAccount&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;UA-XXXXX-X&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;],[&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;_trackPageview&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;]],&lt;/span&gt;
&lt;span class="nx"&gt;disqus_shortname&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;example&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="nx"&gt;disqus_identifier&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;unique_dynamic_id_1234&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kd"&gt;function&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;a&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="nx"&gt;b&lt;/span&gt;&lt;span class="p"&gt;){&lt;/span&gt;
&lt;span class="kd"&gt;var&lt;/span&gt; &lt;span class="nx"&gt;c&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="nx"&gt;a&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;createElement&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;script&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;&lt;span class="nx"&gt;d&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="nx"&gt;a&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;body&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="nx"&gt;e&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="nx"&gt;c&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;async&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="nx"&gt;b&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;&lt;span class="nx"&gt;e&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="nx"&gt;c&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;cloneNode&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;b&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;&lt;span class="nx"&gt;c&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;src&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;//www.google-analytics.com/ga.js&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="nx"&gt;e&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;src&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;//example.disqus.com/embed.js&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;&lt;span class="nx"&gt;d&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;appendChild&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;c&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;&lt;span class="nx"&gt;d&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;appendChild&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;e&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;}(&lt;/span&gt;&lt;span class="nb"&gt;document&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/script&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;


&lt;p&gt;If you want to follow &lt;a href="http://code.google.com/apis/analytics/docs/tracking/asyncTracking.html#Installation"&gt;Google's recommendation of inserting their
snippet at the bottom of the &lt;code&gt;head&lt;/code&gt; element&lt;/a&gt; (and continue inserting Disqus' snippet at the bottom of
the &lt;code&gt;body&lt;/code&gt;) then you will have to sacrifice the single, unified &lt;code&gt;script&lt;/code&gt; for two
separate ones:&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre&gt;&lt;code class="html"&gt;&lt;span class="nt"&gt;&amp;lt;head&amp;gt;&lt;/span&gt;
  &lt;span class="c"&gt;&amp;lt;!-- Usual HTML head elements here... --&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;script&amp;gt;&lt;/span&gt;
    &lt;span class="kd"&gt;var&lt;/span&gt; &lt;span class="nx"&gt;_gaq&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;[[&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;_setAccount&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;UA-XXXXX-X&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;],[&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;_trackPageview&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;]],&lt;/span&gt;
    &lt;span class="nx"&gt;disqus_shortname&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;example&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="nx"&gt;disqus_identifier&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;unique_dynamic_id_1234&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nx"&gt;a&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;b&lt;/span&gt;&lt;span class="p"&gt;){&lt;/span&gt;&lt;span class="kd"&gt;var&lt;/span&gt; &lt;span class="nx"&gt;c&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="nb"&gt;document&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="nx"&gt;d&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="nx"&gt;c&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;createElement&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;script&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="nx"&gt;d&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;async&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;&lt;span class="nx"&gt;d&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;src&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="nx"&gt;b&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="nx"&gt;c&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;documentElement&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;firstChild&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;appendChild&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;d&lt;/span&gt;&lt;span class="p"&gt;);}&lt;/span&gt;
    &lt;span class="nx"&gt;a&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;//www.google-analytics.com/ga.js&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;/script&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/head&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;body&amp;gt;&lt;/span&gt;
  &lt;span class="c"&gt;&amp;lt;!-- Page content here... --&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;script&amp;gt;&lt;/span&gt;&lt;span class="nx"&gt;a&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;//example.disqus.com/embed.js&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;&lt;span class="nt"&gt;&amp;lt;/script&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/body&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;


&lt;p&gt;This version consists of a total of 325 characters but introduces a new global
function named &lt;code&gt;a&lt;/code&gt; which will create a new &lt;code&gt;script&lt;/code&gt; element and append it to
the &lt;code&gt;head&lt;/code&gt; element of the page. The benefit of this approach is that it will
immediately start loading the Google Analytics tracking code which might
otherwise be delayed (particularly on large web pages) but the extra &lt;code&gt;script&lt;/code&gt;
and population of the &lt;code&gt;head&lt;/code&gt; element won't win you any favours with the &lt;a href="http://mir.aculo.us/dom-monster/"&gt;DOM
Monster&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Update #2:&lt;/em&gt; I have since taken to another approach that reduces the number of &lt;code&gt;script&lt;/code&gt;
elements created by synchronously requiring the Disqus JavaScript yourself at
the bottom of the &lt;code&gt;body&lt;/code&gt;:&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre&gt;&lt;code class="html"&gt;&lt;span class="nt"&gt;&amp;lt;head&amp;gt;&lt;/span&gt;
  &lt;span class="c"&gt;&amp;lt;!-- Usual HTML head elements here... --&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;script&amp;gt;&lt;/span&gt;
    &lt;span class="kd"&gt;var&lt;/span&gt; &lt;span class="nx"&gt;_gaq&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;[[&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;_setAccount&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;UA-XXXXX-X&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;],[&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;_trackPageview&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;]],&lt;/span&gt;
    &lt;span class="nx"&gt;disqus_shortname&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;example&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="nx"&gt;disqus_identifier&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;unique_dynamic_id_1234&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kd"&gt;function&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;a&lt;/span&gt;&lt;span class="p"&gt;){&lt;/span&gt;&lt;span class="kd"&gt;var&lt;/span&gt; &lt;span class="nx"&gt;b&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="nx"&gt;a&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;createElement&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;script&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;&lt;span class="nx"&gt;b&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;async&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="nx"&gt;b&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;src&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;//www.google-analytics.com/ga.js&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="nx"&gt;a&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;documentElement&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;firstChild&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;appendChild&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;b&lt;/span&gt;&lt;span class="p"&gt;);}(&lt;/span&gt;&lt;span class="nb"&gt;document&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;/script&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/head&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;body&amp;gt;&lt;/span&gt;
  &lt;span class="c"&gt;&amp;lt;!-- Page content here... --&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;script &lt;/span&gt;&lt;span class="na"&gt;src=&lt;/span&gt;&lt;span class="s"&gt;//example.disqus.com/embed.js&lt;/span&gt; &lt;span class="na"&gt;async&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&amp;lt;/script&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/body&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;



</content>
</entry>

<entry>
  <title>Passing Blocks in Ruby Without &amp;block</title>
  <link href="/2011/01/26/passing-blocks-in-ruby-without-block.html" />
  <id>http://mudge.github.com/2011/01/26/passing-blocks-in-ruby-without-block.html</id>
  <updated>2011-01-26T00:00:00-08:00</updated>
  <summary>Using yield and Proc.new to avoid unnecessary Proc object creation in Ruby.</summary>
  <content type="html">&lt;p&gt;There are two main ways to receive &lt;a href="http://ruby-doc.org/docs/ProgrammingRuby/html/tut_containers.html"&gt;blocks&lt;/a&gt; in a method in Ruby:
the first is to use the &lt;a href="http://ruby-doc.org/docs/keywords/1.9/files/keywords_rb.html#M000042"&gt;&lt;code&gt;yield&lt;/code&gt;&lt;/a&gt; keyword like so:&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre&gt;&lt;code class="ruby"&gt;&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;speak&lt;/span&gt;
  &lt;span class="nb"&gt;puts&lt;/span&gt; &lt;span class="k"&gt;yield&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;

&lt;span class="n"&gt;speak&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="s2"&gt;&amp;quot;Hello&amp;quot;&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="c1"&gt;# Hello&lt;/span&gt;
&lt;span class="c1"&gt;#  =&amp;gt; nil&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;


&lt;p&gt;The other is to prefix the last argument in a method signature with an
ampersand which will then create a &lt;a href="http://www.ruby-doc.org/core/classes/Proc.html"&gt;&lt;code&gt;Proc&lt;/code&gt;&lt;/a&gt; object from any block passed
in. This object can then be executed with the &lt;a href="http://www.ruby-doc.org/core/classes/Proc.html#M000548"&gt;&lt;code&gt;call&lt;/code&gt;&lt;/a&gt; method like so:&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre&gt;&lt;code class="ruby"&gt;&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;speak&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="n"&gt;block&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="nb"&gt;puts&lt;/span&gt; &lt;span class="n"&gt;block&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;call&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;

&lt;span class="n"&gt;speak&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="s2"&gt;&amp;quot;Hello&amp;quot;&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="c1"&gt;# Hello&lt;/span&gt;
&lt;span class="c1"&gt;#  =&amp;gt; nil&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;


&lt;p&gt;The problem with the second approach is that instantiating a new &lt;code&gt;Proc&lt;/code&gt; object incurs
a surprisingly heavy performance penalty as detailed by &lt;a href="http://tenderlovemaking.com"&gt;Aaron Patterson&lt;/a&gt; in his
excellent RubyConf X presentation, "&lt;a href="http://confreaks.net/videos/427-rubyconf2010-zomg-why-is-this-code-so-slow"&gt;ZOMG WHY IS THIS CODE SO SLOW?&lt;/a&gt;"
(beginning around the 30 minute mark or from &lt;a href="http://www.slideshare.net/tenderlove/zomg-why-is-this-code-so-slow/181"&gt;slide 181&lt;/a&gt;).&lt;/p&gt;

&lt;p&gt;This can easily be verified with the following benchmark, &lt;code&gt;block_benchmark.rb&lt;/code&gt;:&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre&gt;&lt;code class="ruby"&gt;&lt;span class="nb"&gt;require&lt;/span&gt; &lt;span class="s2"&gt;&amp;quot;benchmark&amp;quot;&lt;/span&gt;

&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;speak_with_block&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="n"&gt;block&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="n"&gt;block&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;call&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;

&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;speak_with_yield&lt;/span&gt;
  &lt;span class="k"&gt;yield&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;

&lt;span class="n"&gt;n&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;1_000_000&lt;/span&gt;
&lt;span class="no"&gt;Benchmark&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;bmbm&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt;&lt;span class="n"&gt;x&lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt;
  &lt;span class="n"&gt;x&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;report&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;&amp;amp;block&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
    &lt;span class="n"&gt;n&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;times&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="n"&gt;speak_with_block&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="s2"&gt;&amp;quot;ook&amp;quot;&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="k"&gt;end&lt;/span&gt;
  &lt;span class="n"&gt;x&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;report&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;yield&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
    &lt;span class="n"&gt;n&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;times&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="n"&gt;speak_with_yield&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="s2"&gt;&amp;quot;ook&amp;quot;&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="k"&gt;end&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;


&lt;p&gt;The results of this on my own machine are as follows (the numbers themselves aren't
as important as their difference):&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;$ ruby block_benchmark.rb 
Rehearsal ------------------------------------------
&amp;amp;block   1.410000   0.020000   1.430000 (  1.430050)
yield    0.290000   0.000000   0.290000 (  0.291750)
--------------------------------- total: 1.720000sec

             user     system      total        real
&amp;amp;block   1.420000   0.030000   1.450000 (  1.452686)
yield    0.290000   0.000000   0.290000 (  0.292179)
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;So it is clearly preferable to choose &lt;code&gt;yield&lt;/code&gt; over &lt;code&gt;&amp;amp;block&lt;/code&gt; but what if you need to
pass a block to another method?&lt;/p&gt;

&lt;p&gt;For example, here is a class that implements a method &lt;code&gt;tell_ape&lt;/code&gt; which delegates to
another, more generic method named &lt;code&gt;tell&lt;/code&gt;. This sort of pattern is commonly done
using &lt;a href="http://ruby-doc.org/docs/ProgrammingRuby/html/ref_c_object.html#Object.method_missing"&gt;&lt;code&gt;method_missing&lt;/code&gt;&lt;/a&gt; but I'll keep the methods explicit for
simplicity:&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre&gt;&lt;code class="ruby"&gt;&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;Monkey&lt;/span&gt;

  &lt;span class="c1"&gt;# Monkey.tell_ape { &amp;quot;ook!&amp;quot; }&lt;/span&gt;
  &lt;span class="c1"&gt;# ape: ook!&lt;/span&gt;
  &lt;span class="c1"&gt;#  =&amp;gt; nil&lt;/span&gt;
  &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nc"&gt;self&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;tell_ape&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="n"&gt;block&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;tell&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;ape&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="n"&gt;block&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="k"&gt;end&lt;/span&gt;

  &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nc"&gt;self&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;tell&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;name&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="n"&gt;block&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="nb"&gt;puts&lt;/span&gt; &lt;span class="s2"&gt;&amp;quot;&lt;/span&gt;&lt;span class="si"&gt;#{&lt;/span&gt;&lt;span class="nb"&gt;name&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;: &lt;/span&gt;&lt;span class="si"&gt;#{&lt;/span&gt;&lt;span class="n"&gt;block&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;call&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;&lt;/span&gt;
  &lt;span class="k"&gt;end&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;


&lt;p&gt;Such a thing is not possible with the &lt;code&gt;yield&lt;/code&gt; keyword:&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre&gt;&lt;code class="ruby"&gt;&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;Monkey&lt;/span&gt;

  &lt;span class="c1"&gt;# Monkey.tell_ape { &amp;quot;ook!&amp;quot; }&lt;/span&gt;
  &lt;span class="c1"&gt;# ArgumentError: wrong number of arguments (2 for 1)&lt;/span&gt;
  &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nc"&gt;self&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;tell_ape&lt;/span&gt;
    &lt;span class="n"&gt;tell&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;ape&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="k"&gt;yield&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="k"&gt;end&lt;/span&gt;

  &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nc"&gt;self&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;tell&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;name&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="nb"&gt;puts&lt;/span&gt; &lt;span class="s2"&gt;&amp;quot;&lt;/span&gt;&lt;span class="si"&gt;#{&lt;/span&gt;&lt;span class="nb"&gt;name&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;: &lt;/span&gt;&lt;span class="si"&gt;#{&lt;/span&gt;&lt;span class="k"&gt;yield&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;&lt;/span&gt;
  &lt;span class="k"&gt;end&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;


&lt;p&gt;Neither does it work by using an ampersand:&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre&gt;&lt;code class="ruby"&gt;&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;Monkey&lt;/span&gt;

  &lt;span class="c1"&gt;# Monkey.tell_ape { &amp;quot;ook!&amp;quot; }&lt;/span&gt;
  &lt;span class="c1"&gt;# TypeError: wrong argument type String (expected Proc)&lt;/span&gt;
  &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nc"&gt;self&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;tell_ape&lt;/span&gt;
    &lt;span class="n"&gt;tell&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;ape&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="k"&gt;yield&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="k"&gt;end&lt;/span&gt;

  &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nc"&gt;self&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;tell&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;name&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="nb"&gt;puts&lt;/span&gt; &lt;span class="s2"&gt;&amp;quot;&lt;/span&gt;&lt;span class="si"&gt;#{&lt;/span&gt;&lt;span class="nb"&gt;name&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;: &lt;/span&gt;&lt;span class="si"&gt;#{&lt;/span&gt;&lt;span class="k"&gt;yield&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;&lt;/span&gt;
  &lt;span class="k"&gt;end&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;


&lt;p&gt;However, there is a way to only create a &lt;code&gt;Proc&lt;/code&gt; object when needed and that is
to use the little known behaviour of &lt;a href="http://www.ruby-doc.org/core/classes/Proc.html#M000547"&gt;&lt;code&gt;Proc.new&lt;/code&gt;&lt;/a&gt; as explained in
Aaron Patterson's aforementioned presentation.&lt;/p&gt;

&lt;p&gt;If &lt;code&gt;Proc.new&lt;/code&gt; is called from inside a method without any arguments of its own,
it will return a new &lt;code&gt;Proc&lt;/code&gt; containing the block given to its surrounding method.&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre&gt;&lt;code class="ruby"&gt;&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;speak&lt;/span&gt;
  &lt;span class="nb"&gt;puts&lt;/span&gt; &lt;span class="no"&gt;Proc&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;new&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;call&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;

&lt;span class="n"&gt;speak&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="s2"&gt;&amp;quot;Hello&amp;quot;&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="c1"&gt;# Hello&lt;/span&gt;
&lt;span class="c1"&gt;#  =&amp;gt; nil&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;


&lt;p&gt;This means that it is now possible to pass a block between methods without using the
&lt;code&gt;&amp;amp;block&lt;/code&gt; parameter:&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre&gt;&lt;code class="ruby"&gt;&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;Monkey&lt;/span&gt;

  &lt;span class="c1"&gt;# Monkey.tell_ape { &amp;quot;ook!&amp;quot; }&lt;/span&gt;
  &lt;span class="c1"&gt;# ape: ook!&lt;/span&gt;
  &lt;span class="c1"&gt;#  =&amp;gt; nil&lt;/span&gt;
  &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nc"&gt;self&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;tell_ape&lt;/span&gt;
    &lt;span class="n"&gt;tell&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;ape&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="no"&gt;Proc&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;new&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="k"&gt;end&lt;/span&gt;

  &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nc"&gt;self&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;tell&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;name&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="nb"&gt;puts&lt;/span&gt; &lt;span class="s2"&gt;&amp;quot;&lt;/span&gt;&lt;span class="si"&gt;#{&lt;/span&gt;&lt;span class="nb"&gt;name&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;: &lt;/span&gt;&lt;span class="si"&gt;#{&lt;/span&gt;&lt;span class="k"&gt;yield&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;&lt;/span&gt;
  &lt;span class="k"&gt;end&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;


&lt;p&gt;Of course, if you do use &lt;code&gt;Proc.new&lt;/code&gt; then you lose the performance benefit of using
only &lt;code&gt;yield&lt;/code&gt; (as &lt;code&gt;Proc&lt;/code&gt; objects are being created as with &lt;code&gt;&amp;amp;block&lt;/code&gt;) but it does
avoid unnecessary creation of Proc objects when you don't need them. This can be
demonstrated with the following benchmark, &lt;code&gt;proc_new_benchmark.rb&lt;/code&gt;:&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre&gt;&lt;code class="ruby"&gt;&lt;span class="nb"&gt;require&lt;/span&gt; &lt;span class="s2"&gt;&amp;quot;benchmark&amp;quot;&lt;/span&gt;

&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;sometimes_block&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;flag&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="n"&gt;block&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;flag&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="n"&gt;block&lt;/span&gt;
    &lt;span class="n"&gt;block&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;call&lt;/span&gt;
  &lt;span class="k"&gt;end&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;

&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;sometimes_proc_new&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;flag&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;flag&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="nb"&gt;block_given?&lt;/span&gt;
    &lt;span class="no"&gt;Proc&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;new&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;call&lt;/span&gt;
  &lt;span class="k"&gt;end&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;

&lt;span class="n"&gt;n&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;1_000_000&lt;/span&gt;
&lt;span class="no"&gt;Benchmark&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;bmbm&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt;&lt;span class="n"&gt;x&lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt;
  &lt;span class="n"&gt;x&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;report&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;&amp;amp;block&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
    &lt;span class="n"&gt;n&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;times&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
      &lt;span class="n"&gt;sometimes_block&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kp"&gt;false&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="s2"&gt;&amp;quot;won&amp;#39;t get used&amp;quot;&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="k"&gt;end&lt;/span&gt;
  &lt;span class="k"&gt;end&lt;/span&gt;
  &lt;span class="n"&gt;x&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;report&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;Proc.new&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
    &lt;span class="n"&gt;n&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;times&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
      &lt;span class="n"&gt;sometimes_proc_new&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kp"&gt;false&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="s2"&gt;&amp;quot;won&amp;#39;t get used&amp;quot;&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="k"&gt;end&lt;/span&gt;
  &lt;span class="k"&gt;end&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;


&lt;p&gt;Which makes the following rather significant difference:&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;$ ruby code/proc_new_benchmark.rb 
Rehearsal --------------------------------------------
&amp;amp;block     1.080000   0.160000   1.240000 (  1.237644)
Proc.new   0.160000   0.000000   0.160000 (  0.156077)
----------------------------------- total: 1.400000sec

               user     system      total        real
&amp;amp;block     1.090000   0.080000   1.170000 (  1.178771)
Proc.new   0.160000   0.000000   0.160000 (  0.155053)
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;The key here is that using &lt;code&gt;&amp;amp;block&lt;/code&gt; will &lt;em&gt;always&lt;/em&gt; create a new &lt;code&gt;Proc&lt;/code&gt; object,
even if we don't make use of it. By using &lt;code&gt;Proc.new&lt;/code&gt; only when we actually
need it, we can avoid the cost of this object instantiation entirely.&lt;/p&gt;

&lt;p&gt;That said, there is a potential trade-off here between performance and
readability: it is clear from the &lt;code&gt;sometimes_block&lt;/code&gt; method signature that it
takes a block and therefore will presumably do something with it; the same cannot
be said for the more efficient &lt;code&gt;sometimes_proc_new&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;In the end, it comes down to your specific requirements but it is still a useful
language feature to know.&lt;/p&gt;
</content>
</entry>

</feed>

