<?xml version="1.0" encoding="utf-8"?>
<feed xmlns="http://www.w3.org/2005/Atom">
<title>Paul Mucur</title>
<link href="https://mudge.name/"/>
<link href="https://mudge.name/index.atom" rel="self"/>
<updated>2021-03-07T11:57:55+00:00</updated>
<id>https://mudge.name/</id>
<author>
  <name>Paul Mucur</name>
</author>
<entry>
  <title type="html">Weeknotes 71</title>
  <link href="https://mudge.name/2021/03/07/weeknotes-71/"/>
  <id>https://mudge.name/2021/03/07/weeknotes-71/</id>
  <published>2021-03-07T11:55:00+00:00</published>
  <updated>2021-03-07T11:55:00+00:00</updated>
  <summary type="html">Getting busted by Nintendo, who is fantastic when gymnastics is what you need, a greenfield Rails application and being spooked by Facebook.</summary>
  <content type="html">&lt;ul&gt;
  &lt;li&gt;
    &lt;p&gt;My &lt;a href=&quot;/2021/02/28/weeknotes-70/&quot;&gt;Joy-Con diagnosis&lt;/a&gt; came back from Nintendo: “liquid damage main board”. Turns out C dunking it in a glass of water might not have been the best for its delicate electronics.&lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;C surprised us one morning by saying “go go” when “&lt;a href=&quot;https://www.bbc.co.uk/cbeebies/shows/go-jetters&quot;&gt;Go Jetters&lt;/a&gt;” came on the TV during breakfast (after family favourite “&lt;a href=&quot;https://www.bbc.co.uk/cbeebies/shows/hey-duggee&quot;&gt;Hey Duggee&lt;/a&gt;” but before family least-liked “&lt;a href=&quot;https://www.bbc.co.uk/cbeebies/shows/peter-rabbit&quot;&gt;Peter Rabbit&lt;/a&gt;”).&lt;/p&gt;

    &lt;p&gt;E and I now sing the theme song on demand.&lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;I’ve been working on a brand new &lt;a href=&quot;https://rubyonrails.org&quot;&gt;Rails&lt;/a&gt; application at work and it has been a long time since I had to build something customer-facing from scratch.&lt;/p&gt;

    &lt;p&gt;What test frameworks should I use? Should I still use &lt;a href=&quot;https://cucumber.io&quot;&gt;Cucumber&lt;/a&gt;? What front-end framework should I be using? Should I &lt;a href=&quot;https://getbootstrap.com/docs/4.1/getting-started/theming/&quot;&gt;theme Bootstrap&lt;/a&gt;? What &lt;a href=&quot;https://github.com/rubocop/rubocop&quot;&gt;Rubocop&lt;/a&gt; configuration should I use? Should I use &lt;a href=&quot;https://github.com/testdouble/standard&quot;&gt;Standard&lt;/a&gt;?&lt;/p&gt;

    &lt;p&gt;In the past, I’ve tried to reduce the amount of code I had to write (e.g. perhaps unthinkingly in the name of “&lt;a href=&quot;https://en.wikipedia.org/wiki/Don%27t_repeat_yourself&quot;&gt;Don’t repeat yourself&lt;/a&gt;”), extracting as much as possible so I could whip up &lt;a href=&quot;https://guides.rubyonrails.org/routing.html#crud-verbs-and-actions&quot;&gt;resourceful controllers&lt;/a&gt; with the fewest lines of code. Now though, I’m less concerned about optimising for that initial writing and more concerned about making the code easier to read, change and &lt;a href=&quot;https://programmingisterrible.com/post/139222674273/how-to-write-disposable-code-in-large-systems&quot;&gt;delete&lt;/a&gt;.&lt;/p&gt;

    &lt;p&gt;The gamble I’m taking is that it might take a little longer to write at first but it’ll be much easier to understand and change in future.&lt;/p&gt;

    &lt;p&gt;Perhaps I’m getting more cynical about my ability to predict the &lt;a href=&quot;https://sandimetz.com/blog/2016/1/20/the-wrong-abstraction&quot;&gt;right abstraction&lt;/a&gt;. As &lt;a href=&quot;https://sandimetz.com&quot;&gt;Sandi Metz&lt;/a&gt; said:&lt;/p&gt;

    &lt;blockquote&gt;
      &lt;p&gt;Prefer duplication over the wrong abstraction.&lt;/p&gt;
    &lt;/blockquote&gt;

    &lt;p&gt;I’m reminded of &lt;a href=&quot;https://leanpub.com/wewut&quot;&gt;Jay Fields’ “Working Effectively with Unit Tests”&lt;/a&gt;:&lt;/p&gt;

    &lt;blockquote&gt;
      &lt;p&gt;[Test] code and production code is written, maintained, and reviewed in
drastically different ways. Production code collaborates to provide a
single running application, and it’s generally wise to avoid duplicating
concepts within that application. Tests do not, or at least should not
collaborate; it’s universally accepted that inter-test dependency is an
anti-pattern. If we think of tests as tiny, independent universes, then
code that appears in one test should not necessarily be considered
inadvisable duplication if it appears in another test as well.&lt;/p&gt;
    &lt;/blockquote&gt;

    &lt;p&gt;I wonder if it is also useful to think of separate parts of your application as “tiny, independent universes” too.&lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;I had to sign up for a Facebook account in order to fix a problem with Instagram embeds and, despite providing only my name and a work email address (which no one else would have in their contacts), Facebook suggested I add people I know in real life.&lt;/p&gt;

    &lt;p&gt;I was spooked until I realised &lt;a href=&quot;https://nakedsecurity.sophos.com/2019/12/23/facebook-will-stop-mining-contacts-with-your-2fa-number/&quot;&gt;Facebook use the phone number you give for 2-factor authentication to look you up in others’ contacts&lt;/a&gt;.&lt;/p&gt;
  &lt;/li&gt;
&lt;/ul&gt;
</content>
</entry><entry>
  <title type="html">Weeknotes 70</title>
  <link href="https://mudge.name/2021/02/28/weeknotes-70/"/>
  <id>https://mudge.name/2021/02/28/weeknotes-70/</id>
  <published>2021-02-28T14:37:00+00:00</published>
  <updated>2021-02-28T16:54:00+00:00</updated>
  <summary type="html">Fixing Joy-Con drift, a surprise encounter with my favourite type of duck, parsing Data URLs and a wardrobe malfunction.</summary>
  <content type="html">&lt;ul&gt;
  &lt;li&gt;
    &lt;p&gt;After having &lt;a href=&quot;/2020/01/26/weeknotes-13/&quot;&gt;a drifting left Joy-Con for over a year&lt;/a&gt;, I finally sent &lt;a href=&quot;https://www.nintendo.co.uk/Support/Nintendo-Switch/Troubleshooting/Joy-Con-Control-Sticks-Are-Not-Responding-or-Respond-Incorrectly-1908347.html&quot;&gt;it to Nintendo for repair&lt;/a&gt;.&lt;/p&gt;

    &lt;p&gt;With the Switch out of action, I signed up for a free trial of &lt;a href=&quot;https://www.apple.com/uk/apple-arcade/&quot;&gt;Apple Arcade&lt;/a&gt; and have so far downloaded “&lt;a href=&quot;https://whatthegolf.com&quot;&gt;What the Golf?&lt;/a&gt;, “&lt;a href=&quot;https://www.monsterexpedition.com&quot;&gt;A Monster’s Expedition (Through Puzzling Exhibitions)&lt;/a&gt;” and “&lt;a href=&quot;https://simogo.com/work/sayonara-wild-hearts/&quot;&gt;Sayonara Wild Hearts&lt;/a&gt;”.&lt;/p&gt;

    &lt;p&gt;I’ve only played “What the Golf?” so far and it is wonderful.&lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;My mum and I took C to a nearby park to feed the ducks (&lt;a href=&quot;https://www.theguardian.com/world/2019/oct/18/feed-the-ducks-bread-sign-sparks-heated-online-debate&quot;&gt;oats, to avoid controversy&lt;/a&gt;) and saw a raft of glorious &lt;a href=&quot;https://en.wikipedia.org/wiki/Mandarin_duck&quot;&gt;Mandarin ducks&lt;/a&gt;.&lt;/p&gt;

    &lt;p class=&quot;center&quot;&gt;&lt;img src=&quot;/i/mandarin-duck.jpg&quot; width=&quot;375&quot; height=&quot;375&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;

    &lt;p&gt;Along with the &lt;a href=&quot;https://www.rspb.org.uk/birds-and-wildlife/wildlife-guides/bird-a-z/red-kite/&quot;&gt;red kites&lt;/a&gt; we’ve seen in the garden, they certainly make a change from &lt;a href=&quot;https://en.wikipedia.org/wiki/Feral_parakeets_in_Great_Britain&quot;&gt;London’s feral parakeets&lt;/a&gt;.&lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;I had to parse a CSV file sent as a &lt;a href=&quot;https://developer.mozilla.org/en-US/docs/Web/HTTP/Basics_of_HTTP/Data_URIs&quot;&gt;Data URL&lt;/a&gt; this week and converting a &lt;a href=&quot;https://tools.ietf.org/html/rfc2397&quot;&gt;Request for Comments&lt;/a&gt; into a regular expression is a great joy in life:&lt;/p&gt;

    &lt;div class=&quot;language-ruby highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;no&quot;&gt;REGEXP&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;sr&quot;&gt;%r{
  data:
  (?&amp;lt;mediatype&amp;gt;
    (?&amp;lt;mimetype&amp;gt; .+? / .+? )?
    (?&amp;lt;parameters&amp;gt; ( ; .+? = .+? )* )
  )?
  (?&amp;lt;extension&amp;gt;;base64)?
  ,
  (?&amp;lt;data&amp;gt;.*)
}x&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;freeze&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;    &lt;/div&gt;

    &lt;p&gt;You can find a &lt;a href=&quot;https://gist.github.com/mudge/1f958ec848b6c31e287baa7a60a88064&quot;&gt;full Ruby implementation on GitHub&lt;/a&gt;.&lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;I learnt about &lt;a href=&quot;https://guides.rubyonrails.org/active_record_migrations.html#reverting-previous-migrations&quot;&gt;Active Record’s ability to rollback migrations using the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;revert&lt;/code&gt; method&lt;/a&gt; which came in very handy when amending a unique index:&lt;/p&gt;

    &lt;div class=&quot;language-ruby highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;nb&quot;&gt;require_relative&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;'20210211104247_add_unique_index_to_product_links'&lt;/span&gt;

&lt;span class=&quot;k&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;ChangeUniqueIndexOnProductLinks&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&amp;lt;&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;ActiveRecord&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;no&quot;&gt;Migration&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;mf&quot;&gt;6.1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt;
  &lt;span class=&quot;k&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;change&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;revert&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;AddUniqueIndexToProductLinks&lt;/span&gt;

    &lt;span class=&quot;n&quot;&gt;add_index&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;ss&quot;&gt;:product_links&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;ss&quot;&gt;:product_id&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;ss&quot;&gt;:reseller_id&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;ss&quot;&gt;:country_id&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;],&lt;/span&gt; &lt;span class=&quot;ss&quot;&gt;unique: &lt;/span&gt;&lt;span class=&quot;kp&quot;&gt;true&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;ss&quot;&gt;name: &lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;'by_product_reseller_country'&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
  &lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;    &lt;/div&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;An overgrown bush had begun to crowd the patio so my mum and I decided to cut it back. She disappeared into the garage and returned with a truly horrifying but &lt;a href=&quot;/2021/02/14/weeknotes-68/&quot;&gt;brilliantly effective single-purpose tool&lt;/a&gt;: a telescopic branch cutter.&lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;In the footsteps of &lt;a href=&quot;https://www.theverge.com/tldr/2017/5/30/15711662/netflix-house-of-cards-season-5-running-playlist&quot;&gt;other power couples&lt;/a&gt;, E and I went for our first run together yesterday. My legs now ache and, about 5 minutes into the run, my shorts nearly fell off.&lt;/p&gt;
  &lt;/li&gt;
&lt;/ul&gt;
</content>
</entry><entry>
  <title type="html">Weeknotes 69</title>
  <link href="https://mudge.name/2021/02/21/weeknotes-69/"/>
  <id>https://mudge.name/2021/02/21/weeknotes-69/</id>
  <published>2021-02-21T10:21:00+00:00</published>
  <updated>2021-02-21T14:22:00+00:00</updated>
  <summary type="html">A lot of hours writing YAML, pancake day, channelling Elon Musk and product names that maximise embarrassment in Homebase.</summary>
  <content type="html">&lt;ul&gt;
  &lt;li&gt;
    &lt;p&gt;I spent a long time &lt;a href=&quot;https://github.com/mudge/re2/pull/47&quot;&gt;rewriting re2’s CI configuration to test against Ruby versions 1.8 through to 3.0 and all six ABI versions&lt;/a&gt; of the &lt;a href=&quot;https://github.com/google/re2&quot;&gt;underlying Google re2 library&lt;/a&gt; using &lt;a href=&quot;https://github.com/features/actions&quot;&gt;GitHub Actions&lt;/a&gt;.&lt;/p&gt;

    &lt;p&gt;My &lt;a href=&quot;https://github.com/mudge/re2-test-action&quot;&gt;previous setup&lt;/a&gt;, based on using the &lt;a href=&quot;https://www.brightbox.com/docs/ruby/ubuntu/&quot;&gt;Brightbox Ruby packages for Ubuntu&lt;/a&gt; and the &lt;a href=&quot;http://rubinius-binaries-rubinius-com.s3-us-west-2.amazonaws.com/index.txt&quot;&gt;official Rubinius binary releases&lt;/a&gt;, &lt;a href=&quot;https://github.com/mudge/re2/runs/1927274557?check_suite_focus=true&quot;&gt;stopped working&lt;/a&gt; when I ran it for the first time in ten months.&lt;/p&gt;

    &lt;p&gt;Thankfully, &lt;a href=&quot;https://github.com/rspec/rspec-core&quot;&gt;RSpec’s &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;rspec-core&lt;/code&gt;&lt;/a&gt; &lt;a href=&quot;https://github.com/rspec/rspec-core/blob/main/.github/workflows/ci.yml&quot;&gt;also uses GitHub Actions to test against legacy Ruby versions&lt;/a&gt; and their &lt;a href=&quot;https://github.com/rspec/docker-ci&quot;&gt;custom Docker images&lt;/a&gt; were the basis for &lt;a href=&quot;https://github.com/mudge/re2-ci&quot;&gt;my own&lt;/a&gt;.&lt;/p&gt;

    &lt;p&gt;I hope that by switching to the &lt;a href=&quot;https://github.com/ruby/setup-ruby&quot;&gt;official Ruby &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;setup-ruby&lt;/code&gt; GitHub Action&lt;/a&gt;, it will prevent further &lt;a href=&quot;https://en.wikipedia.org/wiki/Software_rot&quot;&gt;bit rot&lt;/a&gt; but I suspect the &lt;a href=&quot;https://github.com/mudge/re2/blob/main/.github/workflows/tests.yml#L54&quot;&gt;necessary&lt;/a&gt; &lt;a href=&quot;https://github.com/mudge/re2-ci/blob/main/1.8/Dockerfile#L21&quot;&gt;workarounds&lt;/a&gt; for legacy Ruby versions will only get worse over time (see &lt;a href=&quot;https://jcs.org/2021/01/06/plaintext&quot;&gt;Joshua Stein’s “Plaintext HTTP in a Modern World”&lt;/a&gt;).&lt;/p&gt;

    &lt;p&gt;I hoped this work would mean I could add &lt;a href=&quot;https://github.com/oracle/truffleruby&quot;&gt;TruffleRuby&lt;/a&gt; as a supported version of Ruby but alas, &lt;a href=&quot;https://github.com/oracle/truffleruby/issues/2262&quot;&gt;it doesn’t seem to work with Ubuntu on GitHub Actions&lt;/a&gt;.&lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;We celebrated &lt;a href=&quot;https://en.wikipedia.org/wiki/Shrove_Tuesday&quot;&gt;Pancake Day&lt;/a&gt; which doubles as the anniversary of C first eating solid food. While he demonstrated what difference a year makes, we introduced the concept of savoury pancakes to my sceptical parents.&lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;I dropped the ball from my &lt;a href=&quot;https://www.kensington.com/en-gb/p/products/control/trackballs/expert-mouse-wired-trackball/&quot;&gt;trackball&lt;/a&gt; onto the sheet of smoked glass covering my desk, accidentally recreating the &lt;a href=&quot;https://youtu.be/LMWwImDX3ks&quot;&gt;Tesla Cybertruck bulletproof window demo&lt;/a&gt;.&lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;I enjoyed sending my mum to the hardware shop to buy a specific adhesive to fix a broken window blind: “&lt;a href=&quot;https://trade.evo-stik.co.uk/products/grab-adhesives/sticks-sht&quot;&gt;Sticks Like Sh*t&lt;/a&gt;”.&lt;/p&gt;
  &lt;/li&gt;
&lt;/ul&gt;
</content>
</entry><entry>
  <title type="html">Weeknotes 68</title>
  <link href="https://mudge.name/2021/02/14/weeknotes-68/"/>
  <id>https://mudge.name/2021/02/14/weeknotes-68/</id>
  <published>2021-02-14T13:35:00+00:00</published>
  <updated>2021-02-14T13:35:00+00:00</updated>
  <summary type="html">Hoarding tools, the trap of doing jobs and a new buzzword.</summary>
  <content type="html">&lt;ul&gt;
  &lt;li&gt;
    &lt;p&gt;The kitchen tap has been dripping for a week.&lt;/p&gt;

    &lt;p&gt;The replacement cartridge valves we ordered arrived quickly but the 14mm socket needed to remove the existing cartridge was delayed by a week.&lt;/p&gt;

    &lt;p&gt;Impatient and sick of the constant drip-drip-drip of the tap, my mum and E separately went in search of the missing part, returning home with various bits and pieces from local hardware shops.&lt;/p&gt;

    &lt;p&gt;We have ended up with:&lt;/p&gt;

    &lt;ul&gt;
      &lt;li&gt;A 14mm ½ inch drive socket.&lt;/li&gt;
      &lt;li&gt;A 14mm ¼ inch drive socket.&lt;/li&gt;
      &lt;li&gt;A set of hexagonal things I do not recognise but now think might be Allen keys for a socket set.&lt;/li&gt;
      &lt;li&gt;A whole new socket set that goes up to 14mm but has sockets too shallow to remove a cartridge valve.&lt;/li&gt;
      &lt;li&gt;A ½ inch drive ratchet handle.&lt;/li&gt;
    &lt;/ul&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;The tap is now fixed.&lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;&lt;a href=&quot;https://twitter.com/kentbeck/status/250733358307500032?lang=en&quot;&gt;Kent Beck famously said&lt;/a&gt;:&lt;/p&gt;

    &lt;blockquote&gt;
      &lt;p&gt;for each desired change, make the change easy (warning: this may be hard), then make the easy change&lt;/p&gt;
    &lt;/blockquote&gt;

    &lt;p&gt;I would add the corollary:&lt;/p&gt;

    &lt;blockquote&gt;
      &lt;p&gt;for each desired DIY job, find the tools to make the job easy (warning: this may be hard), then do the easy job.&lt;/p&gt;
    &lt;/blockquote&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;While hoarding the aforementioned tools, I was also attempting to complete multiple lengthy client questionnaires from our conveyancer, &lt;a href=&quot;https://www.gov.uk/applying-for-probate/apply-for-probate&quot;&gt;finish applying for probate&lt;/a&gt;, mend C’s bedroom door (which doesn’t fully close) and retrieve two table football balls from my dad’s home cinema subwoofer.&lt;/p&gt;

    &lt;p&gt;While completing the probate application with my mum, I realised I had fallen into a familiar trap: trying to be useful to those around by busying myself with odd jobs (both DIY and administrative).&lt;/p&gt;

    &lt;p&gt;In reality:&lt;/p&gt;

    &lt;ul&gt;
      &lt;li&gt;I was stressing myself out.&lt;/li&gt;
      &lt;li&gt;I was absent from the people I was trying (and failing) to help.&lt;/li&gt;
    &lt;/ul&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;I’ve given up on the table football balls in the subwoofer.&lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;&lt;a href=&quot;https://twitter.com/Stew/status/1358766649105649678&quot;&gt;Euan’s latest creation&lt;/a&gt; is absolutely wonderful.&lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;Having taken responsibility for some projects written using &lt;a href=&quot;https://www.gatsbyjs.com&quot;&gt;Gatsby&lt;/a&gt; at &lt;a href=&quot;https://www.ghostcassette.com&quot;&gt;work&lt;/a&gt;, you will now find me using the term “&lt;a href=&quot;https://jamstack.org&quot;&gt;jamstack&lt;/a&gt;” in emails.&lt;/p&gt;
  &lt;/li&gt;
&lt;/ul&gt;
</content>
</entry><entry>
  <title type="html">Weeknotes 67</title>
  <link href="https://mudge.name/2021/02/07/weeknotes-67/"/>
  <id>https://mudge.name/2021/02/07/weeknotes-67/</id>
  <published>2021-02-07T18:14:00+00:00</published>
  <updated>2021-02-07T18:14:00+00:00</updated>
  <summary type="html">Bidding our flat farewell, attempting to apply for probate and a new reason to tease me.</summary>
  <content type="html">&lt;ul&gt;
  &lt;li&gt;
    &lt;p&gt;Following &lt;a href=&quot;/2021/01/31/weeknotes-66/&quot;&gt;our decision&lt;/a&gt;, E and I signed terms with an estate agent, booked in a photographer and opened a file with a conveyancer. We &lt;a href=&quot;/2020/12/21/weeknotes-59-and-60/&quot;&gt;left our flat in a hurry&lt;/a&gt; so we decided to return to prepare it for sale and bid it fare well.&lt;/p&gt;

    &lt;p class=&quot;center&quot;&gt;&lt;img src=&quot;/i/flat.jpg&quot; width=&quot;375&quot; height=&quot;282&quot; /&gt;&lt;/p&gt;

    &lt;p&gt;With E as a temporary driver on my dad’s oversized car, we drove down and set to work removing all the clutter essential for living (particularly with a toddler) but unsightly to prospective buyers. E filled two suitcases with belongings as I pulled up weeds and removed an old planter full of dead flowers from our patio. It was a task that was literally years overdue.&lt;/p&gt;

    &lt;p&gt;I cannot unsee what I found on the underside of long-neglected bags of compost.&lt;/p&gt;

    &lt;p&gt;E ordered what may well be our last &lt;a href=&quot;https://yardsalepizza.com&quot;&gt;Yard Sale Pizza&lt;/a&gt; and I slept poorly, my mind racing with remaining jobs to do. By late morning, we’d done all we could, leaving a gooseneck kettle and a toaster on the kitchen worktop.&lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;I’ve been helping &lt;a href=&quot;https://www.moneysavingexpert.com/family/guide-to-probate/&quot;&gt;apply for probate&lt;/a&gt; and, my goodness, for the sake of your surviving relatives: please maintain a single, up-to-date list of all your assets. Every time we think we might be getting somewhere, a slip of undated paper is found that throws everything into doubt.&lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;During the latest attempt, I received a notification on my phone that my beverage had reached my “perfect temperature”. That’s right, I indulged in an &lt;a href=&quot;https://ember.com&quot;&gt;Ember mug&lt;/a&gt;.&lt;/p&gt;

    &lt;p&gt;This was mostly due to &lt;a href=&quot;https://allconsuming.show/listen/ember&quot;&gt;Noah Kalina and Adam Lisagor’s review&lt;/a&gt;. As you can tell from &lt;a href=&quot;https://youtu.be/8pPCMjqI_uw&quot;&gt;James Hoffmann’s comments&lt;/a&gt;, this is obviously an absurd luxury item (let’s not even mention &lt;a href=&quot;https://www.independent.co.uk/news/uk/politics/rishi-sunak-smart-mug-ember-coffee-week-minimum-wage-jobs-a9607341.html&quot;&gt;Rishi Sunak’s unfortunate photo op&lt;/a&gt;):&lt;/p&gt;

    &lt;blockquote&gt;
      &lt;p&gt;I’ll be honest, owning one of these will mean that your colleagues, friends and loved ones will mock you mercilessly for owning an incredibly expensive smart mug that holds coffee at a particular temperature of your choosing.&lt;/p&gt;

      &lt;p&gt;[…]&lt;/p&gt;

      &lt;p&gt;You should absolutely tease anyone who spent that much money on one of these… &lt;em&gt;but they’re also having a kind of a good time with it.&lt;/em&gt;&lt;/p&gt;
    &lt;/blockquote&gt;
  &lt;/li&gt;
&lt;/ul&gt;
</content>
</entry><entry>
  <title type="html">Weeknotes 66</title>
  <link href="https://mudge.name/2021/01/31/weeknotes-66/"/>
  <id>https://mudge.name/2021/01/31/weeknotes-66/</id>
  <published>2021-01-31T15:35:00+00:00</published>
  <updated>2021-02-01T16:37:54+00:00</updated>
  <summary type="html">Project managing life decisions and what even is pudding?</summary>
  <content type="html">&lt;ul&gt;
  &lt;li&gt;
    &lt;p&gt;My sister and I baked &lt;a href=&quot;https://thegreatbritishbakeoff.co.uk/recipes/all/liam-charles-mega-choccy-stack/&quot;&gt;Liam Charles’ “Mega-Choccy Stack”&lt;/a&gt; from the latest series of &lt;a href=&quot;https://www.channel4.com/programmes/junior-bake-off&quot;&gt;Junior Bake Off&lt;/a&gt;. We didn’t have any double cream so I used a sachet of &lt;a href=&quot;https://www.birdscustard.co.uk/#&quot;&gt;Bird’s&lt;/a&gt; Dream Topping instead.&lt;/p&gt;

    &lt;p class=&quot;center&quot;&gt;&lt;img src=&quot;/i/choccystack.jpg&quot; width=&quot;375&quot; height=&quot;337&quot; /&gt;&lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;I’ve created a &lt;a href=&quot;https://www.trello.com&quot;&gt;Trello&lt;/a&gt; board called “Leaving London”.&lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;I don’t know how to write about our decision to leave a city I have called home for 12 years.&lt;/p&gt;

    &lt;p&gt;&lt;a href=&quot;https://tomstu.art/weeknotes-30-on-pause&quot;&gt;Others have written about the peculiar feeling that your life is on pause&lt;/a&gt; but I hadn’t really thought about it until we started listing all the things we need to do to sell our flat.&lt;/p&gt;

    &lt;blockquote&gt;
      &lt;p&gt;At the time of writing my life is currently happening!&lt;/p&gt;
    &lt;/blockquote&gt;

    &lt;p&gt;Perhaps I haven’t really believed this. That I could live &lt;a href=&quot;/2021/01/17/weeknotes-64/&quot;&gt;five minutes at a time&lt;/a&gt; until “real life” could resume.&lt;/p&gt;

    &lt;p&gt;That, yes, we have outgrown our flat and, yes, want to be closer to family and, yes, we’re both working remotely and it seems to be going well. None of that ever connected with: right, let’s sell our flat, move our belongings and &lt;a href=&quot;https://www.royalmail.com/personal/receiving-mail/redirection&quot;&gt;redirect our mail&lt;/a&gt;.&lt;/p&gt;

    &lt;p&gt;I originally moved to London on little more than a whim so perhaps I shouldn’t be surprised that leaving would be unceremonious too.&lt;/p&gt;
  &lt;/li&gt;
&lt;/ul&gt;
</content>
</entry><entry>
  <title type="html">Weeknotes 65</title>
  <link href="https://mudge.name/2021/01/24/weeknotes-65/"/>
  <id>https://mudge.name/2021/01/24/weeknotes-65/</id>
  <published>2021-01-24T15:10:00+00:00</published>
  <updated>2021-01-24T15:10:00+00:00</updated>
  <summary type="html">Separating out weeknotes, scaling a Rails application, plugging a memory leak and a little boat, no bigger than your hand.</summary>
  <content type="html">&lt;ul&gt;
  &lt;li&gt;
    &lt;p&gt;Taking inspiration from &lt;a href=&quot;https://rowanmanning.com&quot;&gt;Rowan&lt;/a&gt; and &lt;a href=&quot;https://alicebartlett.co.uk/blog/programming-note&quot;&gt;Alice&lt;/a&gt;, I’ve separated my weeknotes (and &lt;a href=&quot;/2019/01/02/2018-yearnotes/&quot;&gt;solitary yearnotes&lt;/a&gt;) from my other, less frequent posts (e.g. “&lt;a href=&quot;/2019/11/12/using-a-raspberry-pi-for-time-machine/&quot;&gt;Using a Raspberry Pi for Time Machine&lt;/a&gt;”) on the &lt;a href=&quot;/&quot;&gt;homepage&lt;/a&gt;.&lt;/p&gt;

    &lt;p&gt;As well as the existing &lt;a href=&quot;/index.atom&quot;&gt;combined Atom feed&lt;/a&gt;, there are now separate feeds for &lt;a href=&quot;/posts.atom&quot;&gt;posts&lt;/a&gt; and &lt;a href=&quot;/weeknotes.atom&quot;&gt;weeknotes&lt;/a&gt; if you only care to subscribe to one.&lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;In preparation for a &lt;a href=&quot;https://www.raspberrypi.org/blog/raspberry-pi-silicon-pico-now-on-sale/&quot;&gt;launch&lt;/a&gt; and an expected burst of traffic this week, I worked on automatically scaling a &lt;a href=&quot;https://rubyonrails.org&quot;&gt;Rails&lt;/a&gt; application hosted on &lt;a href=&quot;https://www.heroku.com&quot;&gt;Heroku&lt;/a&gt;.&lt;/p&gt;

    &lt;p&gt;Heroku provide their &lt;a href=&quot;https://devcenter.heroku.com/articles/scaling#autoscaling&quot;&gt;own autoscaling&lt;/a&gt; but only for their &lt;a href=&quot;https://devcenter.heroku.com/articles/dyno-types&quot;&gt;“performance” tier of dynos&lt;/a&gt; which are 10 times the price of their standard ones.&lt;/p&gt;

    &lt;p&gt;Thankfully, they recommend third-party add-ons such as &lt;a href=&quot;https://railsautoscale.com&quot;&gt;Rails Autoscale&lt;/a&gt; if you’re not using performance dynos or if your app has variable response times (as most do). I hadn’t heard of Rails Autoscale before but &lt;a href=&quot;https://www.youtube.com/watch?v=hkZgpc3BD4E&quot;&gt;the demo&lt;/a&gt; was very compelling. Its use of &lt;a href=&quot;https://help.heroku.com/QB0BKTNJ/is-it-normal-for-requests-to-spend-a-long-time-in-request-queuing-as-reported-by-newrelic&quot;&gt;Heroku “request queueing” time&lt;/a&gt; rather than response time makes it especially useful when your app might have the occasional spike in response time without impacting its ability to serve requests to other clients.&lt;/p&gt;

    &lt;p&gt;&lt;a href=&quot;https://railsautoscale.com/how-many-dynos/&quot;&gt;Adam McCrea’s “How Many Heroku Dynos Do You Need, and Which Size—An Opinionated Guide”&lt;/a&gt; is a fantastic guide and led me to look into “&lt;a href=&quot;https://railsautoscale.com/how-many-dynos/#heroku-routing-and-in-dyno-concurrency&quot;&gt;in-dyno concurrency&lt;/a&gt;” with &lt;a href=&quot;https://devcenter.heroku.com/articles/deploying-rails-applications-with-the-puma-web-server#workers&quot;&gt;multiple Puma workers&lt;/a&gt; rather than relying solely on &lt;a href=&quot;https://devcenter.heroku.com/articles/deploying-rails-applications-with-the-puma-web-server#threads&quot;&gt;threads&lt;/a&gt;.&lt;/p&gt;

    &lt;p&gt;However, as soon as I bumped the number of workers up for my application, a slow memory leak became much more pronounced with my app peaking at 131.5% of its memory quota.&lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;Fortunately, I stumbled across &lt;a href=&quot;https://www.spacevatican.org/2019/5/4/debugging-a-memory-leak-in-a-rails-app/&quot;&gt;Frederick Cheung’s “Debugging a Memory Leak in a Rails App”&lt;/a&gt; which not only described how to find memory leaks in running Rails applications using &lt;a href=&quot;https://github.com/tmm1/rbtrace&quot;&gt;rbtrace&lt;/a&gt; and &lt;a href=&quot;https://samsaffron.com/archive/2015/03/31/debugging-memory-leaks-in-ruby&quot;&gt;Sam Saffron’s excellent guide&lt;/a&gt; but described a &lt;a href=&quot;https://github.com/rails/rails/issues/14301&quot;&gt;potential memory leak in Rails’ &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;prepend_view_path&lt;/code&gt;&lt;/a&gt;.&lt;/p&gt;

    &lt;p&gt;What did I find in a &lt;a href=&quot;http://api.rubyonrails.org/classes/AbstractController/Callbacks/ClassMethods.html#method-i-before_action&quot;&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;before_action&lt;/code&gt;&lt;/a&gt; in my Application Controller? Something like the following:&lt;/p&gt;

    &lt;div class=&quot;language-ruby highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;k&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;prepend_subdomain_specific_templates&lt;/span&gt;
  &lt;span class=&quot;n&quot;&gt;prepend_view_path&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;no&quot;&gt;Rails&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;root&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;join&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;'app'&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;'views'&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;request&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;subdomain&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;))&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;    &lt;/div&gt;

    &lt;p&gt;As Frederick points out, prepending a &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;String&lt;/code&gt; or &lt;a href=&quot;https://ruby-doc.org/stdlib-2.7.2/libdoc/pathname/rdoc/Pathname.html&quot;&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Pathname&lt;/code&gt;&lt;/a&gt; to the view path &lt;a href=&quot;https://github.com/rails/rails/blob/6-1-stable/actionview/lib/action_view/path_set.rb#L84&quot;&gt;causes Rails to initialize a new Action View &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Resolver&lt;/code&gt;&lt;/a&gt; for every request. This would be fine as Ruby should garbage collect the &lt;a href=&quot;http://api.rubyonrails.org/classes/ActionView/Resolver.html&quot;&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Resolver&lt;/code&gt;&lt;/a&gt;s when they are no longer used but Rails’ template caching means that each &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Resolver&lt;/code&gt; defines methods whose names are never collected, causing a slow but steady leak. If you’re calling &lt;a href=&quot;http://api.rubyonrails.org/classes/ActionView/ViewPaths.html#method-i-prepend_view_path&quot;&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;prepend_view_path&lt;/code&gt;&lt;/a&gt; on a particularly heavily trafficked route, this leak will become more pronounced.&lt;/p&gt;

    &lt;p&gt;The fix in my situation was to initialize a &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Resolver&lt;/code&gt; per subdomain once and then re-use the same &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Resolver&lt;/code&gt; whenever possible, e.g.&lt;/p&gt;

    &lt;div class=&quot;language-ruby highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;no&quot;&gt;SUBDOMAIN_SPECIFIC_TEMPLATES&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;s1&quot;&gt;'subdomain1'&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;ActionView&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;no&quot;&gt;OptimizedFileSystemResolver&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;new&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;'app/views/subdomain1'&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;),&lt;/span&gt;
  &lt;span class=&quot;s1&quot;&gt;'subdomain2'&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;ActionView&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;no&quot;&gt;OptimizedFileSystemResolver&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;new&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;'app/views/subdomain2'&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;),&lt;/span&gt;
  &lt;span class=&quot;s1&quot;&gt;'subdomain3'&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;ActionView&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;no&quot;&gt;OptimizedFileSystemResolver&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;new&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;'app/views/subdomain3'&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;freeze&lt;/span&gt;

&lt;span class=&quot;k&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;prepend_subdomain_specific_templates&lt;/span&gt;
  &lt;span class=&quot;n&quot;&gt;prepend_view_path&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;no&quot;&gt;SUBDOMAIN_SPECIFIC_TEMPLATES&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;fetch&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;request&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;subdomain&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;))&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;    &lt;/div&gt;

    &lt;p&gt;With the fix deployed, the resulting memory usage graph made me very happy indeed.&lt;/p&gt;

    &lt;p class=&quot;center&quot;&gt;&lt;img src=&quot;/i/leak.png&quot; width=&quot;500&quot; height=&quot;70&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;I’ve been rightly chastised for &lt;a href=&quot;/2020/08/23/weeknotes-43/&quot;&gt;using the trope of interpreting children’s TV shows through the eyes of a jaded adult&lt;/a&gt; before but I’m genuinely enjoying “&lt;a href=&quot;https://www.bbc.co.uk/cbeebies/shows/in-the-night-garden&quot;&gt;In the Night Garden…&lt;/a&gt;”.&lt;/p&gt;

    &lt;p&gt;Watching C’s excitement build as we watch &lt;a href=&quot;https://www.inthenightgarden.co.uk/about-the-show/igglepiggle&quot;&gt;Igglepiggle&lt;/a&gt; lie down in his boat, covering himself with its sail as we pan up from the rolling ocean to see the stars of the night sky bloom into flowers is a delight.&lt;/p&gt;

    &lt;p&gt;The &lt;a href=&quot;http://www.bbc.co.uk/pressoffice/pressreleases/stories/2007/03_march/19/cbeebies_garden.shtml&quot;&gt;creators’ intentions behind the series&lt;/a&gt; is interesting too:&lt;/p&gt;

    &lt;blockquote&gt;
      &lt;p&gt;“We became very aware of the anxiety surrounding the care of young children which manifested itself in all kind of directions – but the one big subject that came up again and again was bedtime. It’s the classic time for tension between children who want to stay up and parents who want them to go to bed.&lt;/p&gt;

      &lt;p&gt;“We wanted to explore the difference between being asleep and being awake from a child’s point of view: the difference between closing your eyes and pretending to be asleep and closing your eyes and sleeping.&lt;/p&gt;

      &lt;p&gt;“So this is a programme about calming things down whereas most children’s TV is about gee-ing everything up!”&lt;/p&gt;
    &lt;/blockquote&gt;
  &lt;/li&gt;
&lt;/ul&gt;
</content>
</entry><entry>
  <title type="html">Weeknotes 64</title>
  <link href="https://mudge.name/2021/01/17/weeknotes-64/"/>
  <id>https://mudge.name/2021/01/17/weeknotes-64/</id>
  <published>2021-01-17T11:29:00+00:00</published>
  <updated>2021-01-17T11:29:00+00:00</updated>
  <summary type="html">Playing old adventure games, extracting subdirectories from git repositories, programming robots, a photo book follow-up and more than five minutes ahead.</summary>
  <content type="html">&lt;ul&gt;
  &lt;li&gt;
    &lt;p&gt;After reading &lt;a href=&quot;https://www.dropbox.com/s/ojb9zaplszjcp63/FULL%20THROTTLE%20-for%20all.pdf?dl=0&quot;&gt;Duncan Jones’ script for “Full Throttle”&lt;/a&gt;, I replayed the &lt;a href=&quot;https://en.wikipedia.org/wiki/Full_Throttle_(1995_video_game)&quot;&gt;original 1995 LucasArts adventure game&lt;/a&gt; using &lt;a href=&quot;https://www.scummvm.org&quot;&gt;ScummVM&lt;/a&gt; having &lt;a href=&quot;https://wiki.scummvm.org/index.php?title=Datafiles#Full_Throttle&quot;&gt;copied the files from my original CD-ROMs&lt;/a&gt;.&lt;/p&gt;

    &lt;p&gt;I wanted to do the same with &lt;a href=&quot;https://en.wikipedia.org/wiki/Broken_Sword&quot;&gt;Broken Sword&lt;/a&gt; 1 and 2 but struggled to find a computer in the house with a disc drive. I ended up using my grandad’s abandoned &lt;a href=&quot;https://www.asus.com/uk/&quot;&gt;Asus&lt;/a&gt; laptop and &lt;a href=&quot;https://en.wikipedia.org/wiki/Server_Message_Block&quot;&gt;SMB&lt;/a&gt; file sharing to get the &lt;a href=&quot;https://wiki.scummvm.org/index.php?title=Datafiles#Broken_Sword:_The_Shadow_of_the_Templars&quot;&gt;files&lt;/a&gt; onto my Mac.&lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;I’ve been thoroughly enjoying &lt;a href=&quot;https://www.youtube.com/playlist?list=PLaDrN74SfdT7Ueqtwn_bXo1MuSWT0ji2w&quot;&gt;Brian David Gilbert’s “Unraveled” series for Polygon&lt;/a&gt; (now &lt;a href=&quot;https://www.polygon.com/videos/2020/12/28/22195388/pokemon-edibility-unraveled&quot;&gt;complete&lt;/a&gt;), e.g. “&lt;a href=&quot;https://youtu.be/Zb4CxTTFDfA&quot;&gt;Which Dark Souls boss is the best manager?&lt;/a&gt;”&lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;&lt;a href=&quot;https://twitter.com/KushalP/status/1346958399972499457&quot;&gt;Kushal’s tweet&lt;/a&gt; introduced me to the term “&lt;a href=&quot;https://en.wikipedia.org/wiki/Doomscrolling&quot;&gt;doomscrolling&lt;/a&gt;” and, perhaps it is the &lt;a href=&quot;https://en.wikipedia.org/wiki/Frequency_illusion&quot;&gt;Baader-Meinhof phenomenon&lt;/a&gt; but I find myself doing it a &lt;em&gt;lot&lt;/em&gt;.&lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;I needed to extract a custom WordPress theme from a subdirectory in a git repository while preserving its git history. I first tried using &lt;a href=&quot;https://git-scm.com/docs/git-filter-branch&quot;&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;git filter-branch&lt;/code&gt;&lt;/a&gt; but &lt;a href=&quot;https://patchwork.kernel.org/project/git/patch/20190904223239.571-3-newren@gmail.com/&quot;&gt;it told me&lt;/a&gt; to use &lt;a href=&quot;https://github.com/newren/git-filter-repo&quot;&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;git filter-repo&lt;/code&gt;&lt;/a&gt; instead.&lt;/p&gt;

    &lt;p&gt;I was able to extract the subdirectory as well as some top-level files and rewrite some hard-coded paths all with a single command:&lt;/p&gt;

    &lt;div class=&quot;language-console highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;gp&quot;&gt;$&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;git filter-repo &lt;span class=&quot;nt&quot;&gt;--path&lt;/span&gt; .stylelintrc &lt;span class=&quot;nt&quot;&gt;--path&lt;/span&gt; .stylelintignore &lt;span class=&quot;nt&quot;&gt;--path&lt;/span&gt; .phpcs.xml &lt;span class=&quot;nt&quot;&gt;--path&lt;/span&gt; package.json &lt;span class=&quot;nt&quot;&gt;--path&lt;/span&gt; package-lock.json &lt;span class=&quot;nt&quot;&gt;--path&lt;/span&gt; gulpfile.js &lt;span class=&quot;nt&quot;&gt;--path&lt;/span&gt; .browserslistrc &lt;span class=&quot;nt&quot;&gt;--path&lt;/span&gt; .eslintrc &lt;span class=&quot;nt&quot;&gt;--path&lt;/span&gt; .prettierrc &lt;span class=&quot;nt&quot;&gt;--path&lt;/span&gt; wp-content/themes/custom-theme/ &lt;span class=&quot;nt&quot;&gt;--path&lt;/span&gt; assets/ &lt;span class=&quot;nt&quot;&gt;--path-rename&lt;/span&gt; wp-content/themes/custom-theme/: &lt;span class=&quot;nt&quot;&gt;--path-rename&lt;/span&gt; assets/: &lt;span class=&quot;nt&quot;&gt;--replace-text&lt;/span&gt; ../replacements.txt
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;    &lt;/div&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;We got our very first robot vacuum cleaner and watching it navigate takes me back to 2004 and &lt;a href=&quot;https://github.com/mudge/lego_bulldozer&quot;&gt;my adventures programming LEGO MINDSTORMS&lt;/a&gt; with &lt;a href=&quot;http://bricxcc.sourceforge.net/nqc/&quot;&gt;Not Quite C&lt;/a&gt;.&lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;I finally managed to solve &lt;a href=&quot;/2021/01/10/weeknotes-63/&quot;&gt;my photo book and iCloud Photos syncing problems&lt;/a&gt;:&lt;/p&gt;

    &lt;ol&gt;
      &lt;li&gt;I restored my working (but outdated) photo library from a backup.&lt;/li&gt;
      &lt;li&gt;I &lt;a href=&quot;https://support.apple.com/en-gb/HT208243&quot;&gt;logged out of iCloud&lt;/a&gt; altogether on my Mac.&lt;/li&gt;
      &lt;li&gt;I &lt;a href=&quot;https://support.apple.com/en-gb/HT208682#macos&quot;&gt;logged back into iCloud&lt;/a&gt;.&lt;/li&gt;
      &lt;li&gt;I tried to enable iCloud Photos.&lt;/li&gt;
      &lt;li&gt;I encountered a &lt;a href=&quot;https://discussions.apple.com/docs/DOC-13744&quot;&gt;baffling error that “iCloud Photos is only available on macOS Standard, macOS Extended and APFS formatted volumes”&lt;/a&gt;.&lt;/li&gt;
      &lt;li&gt;I &lt;a href=&quot;https://support.apple.com/en-gb/HT201314&quot;&gt;restarted into macOS Recovery&lt;/a&gt;.&lt;/li&gt;
      &lt;li&gt;I ran &lt;a href=&quot;https://support.apple.com/en-gb/guide/disk-utility/dskutl1040/mac&quot;&gt;First Aid&lt;/a&gt; on my Big Sur volumes.&lt;/li&gt;
      &lt;li&gt;I restarted.&lt;/li&gt;
      &lt;li&gt;I successfully enabled iCloud Photos, accepting its warning that I might run out of storage and watched as it began to re-upload all ~37,000 photos and videos in my library.&lt;/li&gt;
      &lt;li&gt;Days passed.&lt;/li&gt;
      &lt;li&gt;The update completed!&lt;/li&gt;
    &lt;/ol&gt;

    &lt;p&gt;I now have my latest photos &lt;em&gt;and&lt;/em&gt; my Mimeo Photos project is intact.&lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;I’ve been reading &lt;a href=&quot;https://en.wikipedia.org/wiki/Northern_Lights_(novel)&quot;&gt;Philip Pullman’s “Northern Lights”&lt;/a&gt; (after enjoying the &lt;a href=&quot;https://www.bbc.co.uk/programmes/m000b1v2&quot;&gt;BBC TV adaptation&lt;/a&gt;) and this quote from Roger felt a bit close to home:&lt;/p&gt;

    &lt;blockquote&gt;
      &lt;p&gt;There en’t been nothing good more than about five minutes ahead. Like I can see now, this bath’s nice, and there’s a nice warm towel there, about five minutes away. And once I’m dry, maybe I’ll think of summing nice to eat, but no further ahead than that. And when I’ve eaten, maybe I’ll look forward to a kip in a comfortable bed. But after that, I dunno, Lyra. There’s been terrible things we seen, en’t there? And more a coming, more’n likely. So I think I’d rather not know what’s in the future. I’ll stick to the present.&lt;/p&gt;
    &lt;/blockquote&gt;
  &lt;/li&gt;
&lt;/ul&gt;
</content>
</entry><entry>
  <title type="html">Weeknotes 63</title>
  <link href="https://mudge.name/2021/01/10/weeknotes-63/"/>
  <id>https://mudge.name/2021/01/10/weeknotes-63/</id>
  <published>2021-01-10T15:16:00+00:00</published>
  <updated>2021-01-10T15:16:00+00:00</updated>
  <summary type="html">Arctic scenes, pointless defiance, networking problems and photo books.</summary>
  <content type="html">&lt;ul&gt;
  &lt;li&gt;
    &lt;p&gt;I haven’t seen snow like this in &lt;a href=&quot;/2019/01/02/2018-yearnotes/&quot;&gt;years&lt;/a&gt;.&lt;/p&gt;

    &lt;p class=&quot;center&quot;&gt;&lt;img src=&quot;/i/lane.jpg&quot; width=&quot;375&quot; height=&quot;282&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;I’m going to try to keep this quick as I want to try out my &lt;a href=&quot;/2021/01/03/weeknotes-62/&quot;&gt;new running shoes&lt;/a&gt; before C wakes up. The feeling of having eaten too much at every meal is becoming far too familiar.&lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;C has various place mats, posters and books that all feature &lt;a href=&quot;https://en.wikipedia.org/wiki/Cupcake&quot;&gt;cupcakes&lt;/a&gt; for the letter C. In an act of pointless defiance, I am insisting on calling them “buns” instead.&lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;My parents’ house is shaped like one long corridor so I bought them a &lt;a href=&quot;https://shop.bt.com/learnmore/bt-branded-products-and-services/bt-whole-home-wi-fi/&quot;&gt;BT Whole Home Wi-Fi&lt;/a&gt; system a few years ago. Now there are three of us working from home (not to mention my dad’s binge-watching of any Netflix show that has more than three seasons), we’re especially vulnerable to any problems with the internet.&lt;/p&gt;

    &lt;p&gt;The day after upgrading their router to the &lt;a href=&quot;https://shop.bt.com/products/bt-smart-hub-2-097683-DYNK.html&quot;&gt;hub&lt;/a&gt; BT insisted on sending them, we started to have problems. I spent that evening meticulously re-arranging the various nodes in the mesh network, trying to ensure a reading of “Excellent Connection” for each one (with mixed success).&lt;/p&gt;

    &lt;p&gt;The very next day at 8:30 am, all of our internet cut out unexpectedly.&lt;/p&gt;

    &lt;p&gt;C had pushed the router off a side table, turning the whole thing off.&lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;For Christmas, I put together and printed a photo album of C’s 2020 using &lt;a href=&quot;https://www.mimeophotos.com&quot;&gt;Mimeo Photos&lt;/a&gt; as &lt;a href=&quot;https://www.mimeophotos.com/blog/apple-picture-books&quot;&gt;they used to be the provider of Apple’s official photo books&lt;/a&gt;.&lt;/p&gt;

    &lt;p&gt;I’ve been very happy with the result but &lt;a href=&quot;/2020/12/28/weeknotes-61/&quot;&gt;during my upgrade to Big Sur&lt;/a&gt;, I downloaded my photo library afresh from &lt;a href=&quot;https://support.apple.com/en-gb/HT204264&quot;&gt;iCloud Photo Library&lt;/a&gt; after experiencing &lt;a href=&quot;https://daringfireball.net/2018/10/icloud_photo_library_start_over&quot;&gt;syncing problems&lt;/a&gt; with a restored backup. I clicked on my Mimeo project before all my original photos had finished downloading and it threw a &lt;a href=&quot;http://support.mimeophotos.com/en/articles/2965789-some-photos-are-missing-warning&quot;&gt;“Some Photos Are Missing” warning&lt;/a&gt;, immediately gutting the project of all its photos.&lt;/p&gt;

    &lt;p&gt;Thankfully, I still had a &lt;a href=&quot;https://www.shirt-pocket.com/SuperDuper/SuperDuperDescription.html&quot;&gt;backup&lt;/a&gt; of my entire photo library including the full project and, sure enough, &lt;a href=&quot;https://support.apple.com/en-us/HT204414&quot;&gt;loading that into Photos.app&lt;/a&gt; worked fine but there was seemingly no way to transfer that project over to my new library.&lt;/p&gt;

    &lt;p&gt;This led me to take the following steps:&lt;/p&gt;

    &lt;ol&gt;
      &lt;li&gt;Disable iCloud Photos.&lt;/li&gt;
      &lt;li&gt;Drag my entire photo library to the &lt;a href=&quot;https://www.imore.com/apple-ditches-trash-can-name-uk-macos-catalina-update&quot;&gt;Bin&lt;/a&gt;.&lt;/li&gt;
      &lt;li&gt;Restore my working (but now out-of-date) photo library from my backup.&lt;/li&gt;
      &lt;li&gt;Without opening my Mimeo project, delete the entire contents of my library.&lt;/li&gt;
      &lt;li&gt;Re-enable iCloud Photos so that my library is re-downloaded, hopefully fixing my project.&lt;/li&gt;
    &lt;/ol&gt;

    &lt;p&gt;Step 4 took over three hours to complete.&lt;/p&gt;

    &lt;p&gt;It didn’t work.&lt;/p&gt;
  &lt;/li&gt;
&lt;/ul&gt;
</content>
</entry><entry>
  <title type="html">Weeknotes 62</title>
  <link href="https://mudge.name/2021/01/03/weeknotes-62/"/>
  <id>https://mudge.name/2021/01/03/weeknotes-62/</id>
  <published>2021-01-03T16:55:00+00:00</published>
  <updated>2021-01-03T16:55:00+00:00</updated>
  <summary type="html">A snowman, this time last year, reconsidering exercise, dental instruments, optionally rotating eyes and colour schemes.</summary>
  <content type="html">&lt;ul&gt;
  &lt;li&gt;
    &lt;p&gt;I built a snowman.&lt;/p&gt;

    &lt;p class=&quot;center&quot;&gt;&lt;img src=&quot;/i/snowman.jpg&quot; width=&quot;375&quot; height=&quot;500&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;It’s been great to read &lt;a href=&quot;https://alicebartlett.co.uk/blog/yearnotes-2020&quot;&gt;people’s&lt;/a&gt; &lt;a href=&quot;https://tomstu.art/yearnotes-0-regular-reflection&quot;&gt;yearnotes&lt;/a&gt;.&lt;/p&gt;

    &lt;p&gt;&lt;a href=&quot;/2020/01/05/weeknotes-10/&quot;&gt;This time last year&lt;/a&gt; I was still on “parental leave” and anxious about finding a new client. By the beginning of March, I was ready to spend most of the year commuting over two hours a day to west London. In the end, I only spent one week doing that commute before everyone was working remotely. It meant I’ve been able to spend much more time with C than I might have hoped but I still can’t reconcile “C the baby” with “C the little boy who loves to climb stairs”.&lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;It’s the end of my second year working for &lt;a href=&quot;https://www.ghostcassette.com&quot;&gt;myself&lt;/a&gt; and I’ve been exceptionally lucky enough to work with &lt;a href=&quot;https://spyscape.com&quot; title=&quot;SPYSCAPE&quot;&gt;some&lt;/a&gt; &lt;a href=&quot;https://gofreerange.com&quot; title=&quot;Go Free Range&quot;&gt;great&lt;/a&gt; &lt;a href=&quot;https://www.raspberrypi.org&quot; title=&quot;Raspberry Pi&quot;&gt;clients&lt;/a&gt;.&lt;/p&gt;

    &lt;p&gt;I have even less of an idea than usual about what the future might hold so I’m extremely grateful to have the flexibility to work from home. It’s even more appealing now that I finally have a desk with a window after working in an unlit hallway for nine months.&lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;On top of an unusually sedentary year, Christmas and the &lt;a href=&quot;https://atp.fm/410&quot;&gt;Accidental Tech Podcast review of Apple Fitness+&lt;/a&gt; have pushed me further into reconsidering my exercise regime (which currently consists of nothing).&lt;/p&gt;

    &lt;p&gt;I’ve ordered &lt;a href=&quot;https://ringfitadventure.nintendo.com&quot;&gt;Ring Fit Adventure&lt;/a&gt; and a new pair of running shoes in an attempt to cajole me into some form of rigorous movement at least once a day.&lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;For a friend’s birthday on New Year’s Day, E and I took part in an online escape room over &lt;a href=&quot;https://jitsi.org&quot;&gt;Jitsi&lt;/a&gt;.&lt;/p&gt;

    &lt;p&gt;There were four of us playing and a single “games master” (no, not &lt;a href=&quot;https://en.wikipedia.org/wiki/GamesMaster&quot;&gt;that one&lt;/a&gt;) who acted as a sort of human text adventure game: they described the scene and responded to our commands to look at things, use objects, etc. As we slowly solved puzzles and revealed things in the scene, they presented an updating top-down view of the room.&lt;/p&gt;

    &lt;p&gt;It was a surprising amount of fun though it was difficult to coordinate our decision what to do next. At one point, we freed a character trapped in a cupboard and my friend instructed the games master to remove all of their teeth with dental instruments.&lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;While working with &lt;a href=&quot;https://create-react-app.dev&quot;&gt;Create React App&lt;/a&gt;, I noticed it uses the &lt;a href=&quot;https://developer.mozilla.org/en-US/docs/Web/CSS/@media/prefers-reduced-motion&quot;&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;prefers-reduced-motion&lt;/code&gt; CSS media feature&lt;/a&gt;.&lt;/p&gt;

    &lt;p&gt;Some may argue the &lt;a href=&quot;https://www.ghostcassette.com&quot;&gt;rotating eyes on my company logo&lt;/a&gt; are vital but I thought it best to &lt;a href=&quot;https://github.com/ghostcassette/ghostcassette.github.io/commit/7f92087068b02d9c425179934d0cdb0cd677b836&quot;&gt;disable them if someone has requested that non-essential motion is disabled&lt;/a&gt;.&lt;/p&gt;

    &lt;div class=&quot;language-css highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;k&quot;&gt;@media&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;prefers-reduced-motion&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;no-preference&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;nc&quot;&gt;.eye&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;nl&quot;&gt;animation&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;playing&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;23s&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;linear&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;infinite&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;    &lt;/div&gt;

    &lt;p&gt;This way, the eyes only rotate if you haven’t explicitly asked for reduced motion, i.e. if &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;prefers-reduced-motion&lt;/code&gt; is set to &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;reduce&lt;/code&gt;, the &lt;a href=&quot;https://developer.mozilla.org/en-US/docs/Web/CSS/animation&quot;&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;animation&lt;/code&gt; property&lt;/a&gt; isn’t set at all.&lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;As it is a new year and I &lt;a href=&quot;/2020/12/28/weeknotes-61/&quot;&gt;recently formatted my laptop&lt;/a&gt;, it’s time for a new Terminal colour scheme! Having used &lt;a href=&quot;https://ethanschoonover.com/solarized/&quot;&gt;Solarized&lt;/a&gt; and &lt;a href=&quot;https://www.nordtheme.com&quot;&gt;Nord&lt;/a&gt; in the past, &lt;a href=&quot;https://twitter.com/cassarani&quot;&gt;Leo&lt;/a&gt; introduced me to &lt;a href=&quot;http://chriskempson.com/projects/base16/&quot;&gt;Base16&lt;/a&gt;.&lt;/p&gt;

    &lt;p&gt;It took me a while to understand that I had to combine both &lt;a href=&quot;https://github.com/chriskempson/base16-shell&quot;&gt;Base16 Shell&lt;/a&gt; (to set my shell colours) and &lt;a href=&quot;https://github.com/chriskempson/base16-vim/&quot;&gt;Base16 Vim&lt;/a&gt; (to use those colours in Vim).&lt;/p&gt;

    &lt;p&gt;With both installed and configured, I could run the following in the shell and have both the Terminal and vim update:&lt;/p&gt;

    &lt;div class=&quot;language-console highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;gp&quot;&gt;$&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;base16_tomorrow
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;    &lt;/div&gt;

    &lt;p&gt;However, after having &lt;a href=&quot;/2019/12/01/weeknotes-5/&quot;&gt;pointlessly sped up my zsh startup time&lt;/a&gt; over a year ago, I am now avoiding &lt;a href=&quot;https://github.com/chriskempson/base16-shell/blob/master/profile_helper.sh&quot;&gt;Base16 Shell’s &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;profile_helper.sh&lt;/code&gt;&lt;/a&gt; and instead use the following in my &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;.zshrc&lt;/code&gt; (where &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;.base16_theme&lt;/code&gt; is a symlink to &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;~/.config/base16-shell/scripts/base16-tomorrow.sh&lt;/code&gt;):&lt;/p&gt;

    &lt;div class=&quot;language-zsh highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;nb&quot;&gt;source&lt;/span&gt; ~/.base16_theme
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;    &lt;/div&gt;

    &lt;p&gt;And the following in my &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;.vimrc&lt;/code&gt;:&lt;/p&gt;

    &lt;div class=&quot;language-vim highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;k&quot;&gt;let&lt;/span&gt; base16colorspace&lt;span class=&quot;p&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;m&quot;&gt;256&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;colorscheme&lt;/span&gt; base16&lt;span class=&quot;p&quot;&gt;-&lt;/span&gt;tomorrow
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;    &lt;/div&gt;

    &lt;p&gt;If I want to choose a different theme, I need only update the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;.base16_theme&lt;/code&gt; symlink and the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;colorscheme&lt;/code&gt; in my &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;.vimrc&lt;/code&gt;.&lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;Happy new year!&lt;/p&gt;
  &lt;/li&gt;
&lt;/ul&gt;
</content>
</entry><entry>
  <title type="html">Weeknotes 61</title>
  <link href="https://mudge.name/2020/12/28/weeknotes-61/"/>
  <id>https://mudge.name/2020/12/28/weeknotes-61/</id>
  <published>2020-12-28T13:33:00+00:00</published>
  <updated>2020-12-28T13:33:00+00:00</updated>
  <summary type="html">The annual cleansing ritual, first words, festive soldering, learning Go and a lot of Christmas specials.</summary>
  <content type="html">&lt;ul&gt;
  &lt;li&gt;
    &lt;p&gt;As 2020 draws to a close, I performed the annual cleansing ritual of formatting my laptop and upgrading to &lt;a href=&quot;https://www.apple.com/uk/macos/big-sur/&quot;&gt;macOS Big Sur&lt;/a&gt;.&lt;/p&gt;

    &lt;p&gt;Following &lt;a href=&quot;https://tomstu.art&quot;&gt;Tom&lt;/a&gt;’s recommendation, I bought a &lt;a href=&quot;https://www.samsung.com/uk/memory-storage/portable-ssd/portable-ssd-t5-500gb-blue-mu-pa500b-eu/&quot;&gt;Samsung T5 500GB portable SSD&lt;/a&gt; and used &lt;a href=&quot;https://www.shirt-pocket.com/SuperDuper/SuperDuperDescription.html&quot;&gt;SuperDuper!&lt;/a&gt; to take a copy of my entire hard drive. I then upgraded to Big Sur, &lt;a href=&quot;https://support.apple.com/en-gb/HT204904&quot;&gt;restarted my Mac into macOS Recovery&lt;/a&gt;, used Disk Utility to erase my internal hard drive and reinstall macOS from scratch.&lt;/p&gt;

    &lt;p&gt;While I don’t have an &lt;a href=&quot;https://github.com/tuzz/zz&quot;&gt;automation tool to my name&lt;/a&gt;, almost everything I need to set up my computer can be found in my &lt;a href=&quot;https://github.com/mudge/dotfiles&quot;&gt;dotfiles&lt;/a&gt; (aside from my private SSH and GPG keys). After copying over my music and photo libraries, I was back up and running within an hour.&lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;We enjoyed Christmas day with the family with its equal parts of rich food and busying myself with the construction of various toys given to C (e.g. a tricycle and a repair garage with 32 pieces of track).&lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;While we’ve been staying with my parents, C has started saying words. His first was “apple” and his vocabulary has now expanded to include “ball” and “dada” which, confusingly, is his attempt at “grandad”.&lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;C was particularly fond of a &lt;a href=&quot;https://www.heyduggee.com/products/hey-duggee-sound-puzzle/&quot;&gt;Hey Duggee wooden puzzle&lt;/a&gt; from my parents but managed to break it after dropping it only once.&lt;/p&gt;

    &lt;p&gt;From the outside, it seemed the circuit board attached to the inside of the puzzle had simply come loose but, as the entire unit was glued shut, I had to take a handsaw to it to investigate further.&lt;/p&gt;

    &lt;p&gt;After much sawing and clumsy prying with various screwdrivers, I managed to get inside to discover the integrated circuit that powered the device had detached as it was stuck in a blob of glue.&lt;/p&gt;

    &lt;p class=&quot;center&quot;&gt;&lt;img src=&quot;/i/ic.jpg&quot; width=&quot;375&quot; height=&quot;375&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;

    &lt;p&gt;After asking &lt;a href=&quot;https://twitter.com/cassarani&quot;&gt;Leo&lt;/a&gt; for advice, he encouraged me to look up surface-mount technology (SMT) soldering and give it a go. &lt;a href=&quot;/2020/12/21/weeknotes-59-and-60/&quot;&gt;Calling upon my father-in-law’s soldering iron once more&lt;/a&gt;, we gave it our best shot and got it (mostly) working! Only one of the characters (sorry, &lt;a href=&quot;https://www.heyduggee.com/characters/&quot;&gt;Tag&lt;/a&gt;) no longer speaks when you fit their piece in the puzzle but five out of six isn’t bad.&lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;Leo has also been teaching me &lt;a href=&quot;https://golang.org&quot;&gt;Go&lt;/a&gt; as I’ve been working on a custom HTTP &lt;a href=&quot;https://golang.org/pkg/net/http/httputil/#ReverseProxy&quot;&gt;reverse proxy&lt;/a&gt; to complete requests on behalf of a desktop application whose HTTP client has not been &lt;a href=&quot;https://curl.se/libcurl/c/CURLOPT_FOLLOWLOCATION.html&quot;&gt;configured to follow redirects&lt;/a&gt;.&lt;/p&gt;

    &lt;p&gt;Despite having done the excellent &lt;a href=&quot;https://tour.golang.org/welcome/1&quot;&gt;A Tour of Go&lt;/a&gt;, I find that new languages only really stick when I try to ship my own project.&lt;/p&gt;

    &lt;p&gt;Writing automated tests using the &lt;a href=&quot;https://golang.org/pkg/net/http/httptest/&quot;&gt;httptest package&lt;/a&gt; was great fun and Leo’s recommendation to &lt;a href=&quot;https://www.youtube.com/watch?t=376&amp;amp;v=8hQG7QlcLBk&amp;amp;feature=youtu.be&quot;&gt;watch Mitchell Hashimoto’s “Advanced Testing with Go”, focussing on subtests and table driven tests&lt;/a&gt; was invaluable.&lt;/p&gt;

    &lt;p&gt;It was also useful to learn about the &lt;a href=&quot;https://github.com/golang-standards/project-layout&quot;&gt;standard Go project layout&lt;/a&gt; even though my project is so small I’ve stuck everything in &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;main.go&lt;/code&gt;.&lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;I’ve been working my way through various Christmas specials: &lt;a href=&quot;https://www.bbc.co.uk/iplayer/episode/m000qf09/mortimer-whitehouse-gone-fishing-gone-christmas-fishing&quot;&gt;Mortimer &amp;amp; Whitehouse: Gone Christmas Fishing&lt;/a&gt;, &lt;a href=&quot;https://www.bbc.co.uk/iplayer/episode/m000qryp/motherland-christmas-special&quot;&gt;Motherland: Christmas Special&lt;/a&gt;, &lt;a href=&quot;https://www.bbc.co.uk/iplayer/episode/m000qqt4/the-great-british-sewing-bee-2020-specials-1-celebrity-christmas-special&quot;&gt;The Great British Sewing Bee: Celebrity Christmas Special&lt;/a&gt; and, of course, &lt;a href=&quot;https://www.channel4.com/programmes/the-great-british-bake-off-festive-specials/on-demand/70300-001&quot;&gt;The Great Christmas Bake Off 2020&lt;/a&gt;.&lt;/p&gt;

    &lt;p&gt;I’ve still got &lt;a href=&quot;https://www.bbc.co.uk/iplayer/episode/m000qqsy/the-repair-shop-at-christmas&quot;&gt;The Repair Shop at Christmas&lt;/a&gt; and the &lt;a href=&quot;https://www.bbc.co.uk/iplayer/episode/m000qq50/worzel-gummidge-saucy-nancy&quot;&gt;new episode of Worzel Gummidge&lt;/a&gt; to go.&lt;/p&gt;
  &lt;/li&gt;
&lt;/ul&gt;
</content>
</entry><entry>
  <title type="html">Weeknotes 59 and 60</title>
  <link href="https://mudge.name/2020/12/21/weeknotes-59-and-60/"/>
  <id>https://mudge.name/2020/12/21/weeknotes-59-and-60/</id>
  <published>2020-12-21T12:10:00+00:00</published>
  <updated>2020-12-21T12:10:00+00:00</updated>
  <summary type="html">Decamping to the north, two significant DIY projects and things learnt while continuing the migration to Active Storage.</summary>
  <content type="html">&lt;ul&gt;
  &lt;li&gt;
    &lt;p&gt;After &lt;a href=&quot;/2020/12/06/weeknotes-58/&quot;&gt;last week&lt;/a&gt;, we decided to decamp to my parents’ house for the foreseeable future.&lt;/p&gt;

    &lt;p&gt;The day after we made the decision, C came home from nursery with a cold and E caught it soon after. We waited for it to pass but, by Friday, our anxiety led to a last minute &lt;a href=&quot;https://www.gov.uk/get-coronavirus-test&quot;&gt;coronavirus test&lt;/a&gt; and a nervous two day wait for the result. Just as we were about to give up travelling that weekend, the text message arrived: a negative result.&lt;/p&gt;

    &lt;p&gt;Within seconds, E ordered our first (and perhaps only) &lt;a href=&quot;https://www.pret.co.uk/en-GB/christmas-menu&quot;&gt;Pret&lt;/a&gt; of 2020 while we loaded up the car. Complaining about the inexplicable popularity of cranberries in every festive sandwich, we drove north without stopping, listening to &lt;a href=&quot;https://www.adam-buxton.co.uk/podcasts/7-bfk9m-4l8kp-blcga-jwabs-blbb8-b9mjx-fj3gr-j4lyp-9jlhe-a4bhm-c67yy-szhgm-m8pcd-l376z-zjb32-wh9dr-za87b-c8l3j-byyhb-segba-hlsc6-5m648&quot;&gt;Adam Buxton interview Sir Paul McCartney&lt;/a&gt; along the way.&lt;/p&gt;

    &lt;p&gt;&lt;a href=&quot;https://www.gov.uk/guidance/tier-4-stay-at-home&quot;&gt;I’m so glad we did&lt;/a&gt;.&lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;With my parents available to look after C, I spent the day I usually look after him alone constructing a climbing frame.&lt;/p&gt;

    &lt;p class=&quot;center&quot;&gt;&lt;img src=&quot;/i/slide-under-construction.jpg&quot; width=&quot;375&quot; height=&quot;500&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;

    &lt;p&gt;The instructions said it would take two people four hours to complete. It took me around seven hours to screw, bolt, drill and hammer together the finished product.&lt;/p&gt;

    &lt;p class=&quot;center&quot;&gt;&lt;img src=&quot;/i/finished-slide.jpg&quot; width=&quot;375&quot; height=&quot;282&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;

    &lt;p&gt;It’s no &lt;a href=&quot;https://twitter.com/Stew/status/1340683776326856704&quot;&gt;1960s cocktail cabinet&lt;/a&gt; but it is the most complicated thing I have built.&lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;During C’s giddy exploration of his new surroundings, he threw my parents’ 23 year old telephone base station on their kitchen tiles. It immediately stopped working, rendering all of their handsets useless.&lt;/p&gt;

    &lt;p&gt;Upon inspection, we realised the station had landed on its own cable, severing two of four wires within.&lt;/p&gt;

    &lt;p&gt;After speaking with &lt;a href=&quot;/2020/02/23/weeknotes-17/&quot;&gt;my father-in-law&lt;/a&gt;, he lent me his soldering iron and some solder sleeves and I attempted to reattach each of the four wires.&lt;/p&gt;

    &lt;p class=&quot;center&quot;&gt;&lt;img src=&quot;/i/soldering.jpg&quot; width=&quot;375&quot; height=&quot;282&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;

    &lt;p&gt;I have never successfully repaired anything by soldering but this was my very first success!&lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;I used &lt;a href=&quot;https://www.rubydoc.info/gems/rake/12.0.0/Rake/MultiTask&quot;&gt;Rake’s &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;multitask&lt;/code&gt;&lt;/a&gt; for the first time:&lt;/p&gt;

    &lt;blockquote&gt;
      &lt;p&gt;Same as a regular task, but the immediate prerequisites are done in parallel using Ruby threads.&lt;/p&gt;
    &lt;/blockquote&gt;

    &lt;div class=&quot;language-ruby highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;n&quot;&gt;desc&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;'Run all migrations in parallel'&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;multitask&lt;/span&gt; &lt;span class=&quot;ss&quot;&gt;migrate: &lt;/span&gt;&lt;span class=&quot;sx&quot;&gt;%i[migrate_issues migrate_books migrate_magazines]&lt;/span&gt;

&lt;span class=&quot;n&quot;&gt;desc&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;'Migrate issues'&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;task&lt;/span&gt; &lt;span class=&quot;ss&quot;&gt;migrate_issues: :environment&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;do&lt;/span&gt;
  &lt;span class=&quot;c1&quot;&gt;# ...&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;

&lt;span class=&quot;c1&quot;&gt;# ...&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;    &lt;/div&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;My adventures with &lt;a href=&quot;https://guides.rubyonrails.org/active_storage_overview.html&quot;&gt;Rails’ Active Storage&lt;/a&gt; continue and the use of &lt;a href=&quot;https://guides.rubyonrails.org/routing.html#direct-routes&quot;&gt;direct routes&lt;/a&gt; stands out while reading &lt;a href=&quot;https://github.com/rails/rails/blob/6-1-stable/activestorage/config/routes.rb#L18-L24&quot;&gt;the source code&lt;/a&gt;. This allows you to create powerful routing helpers that take arbitrary objects (rather than relying on &lt;a href=&quot;https://guides.rubyonrails.org/routing.html#overriding-named-route-parameters&quot;&gt;overriding &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;ActiveRecord::Base#to_param&lt;/code&gt;&lt;/a&gt;):&lt;/p&gt;

    &lt;div class=&quot;language-ruby highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;n&quot;&gt;get&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;'/downloads/:signed_id/*filename'&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;'downloads#show'&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;ss&quot;&gt;as: :download_blob&lt;/span&gt;

&lt;span class=&quot;n&quot;&gt;direct&lt;/span&gt; &lt;span class=&quot;ss&quot;&gt;:download&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;do&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;|&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;object&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;options&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;|&lt;/span&gt;
  &lt;span class=&quot;n&quot;&gt;signed_id&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;object&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;some&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;custom&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;signed_id&lt;/span&gt;
  &lt;span class=&quot;n&quot;&gt;filename&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;object&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;another&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;custom&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;filename&lt;/span&gt;

  &lt;span class=&quot;n&quot;&gt;route_for&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;ss&quot;&gt;:download_blob&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;signed_id&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;filename&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;options&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;

&lt;span class=&quot;c1&quot;&gt;# So now you can call..&lt;/span&gt;

&lt;span class=&quot;n&quot;&gt;download_path&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;object&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;download_url&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;object&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;    &lt;/div&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;While I’ve since given up on &lt;a href=&quot;https://guides.rubyonrails.org/active_storage_overview.html#public-access&quot;&gt;Active Storage’s public access mode&lt;/a&gt; as it doesn’t let you set a &lt;a href=&quot;https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Content-Disposition&quot;&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Content-Disposition&lt;/code&gt;&lt;/a&gt; with &lt;a href=&quot;https://aws.amazon.com/s3/&quot;&gt;S3&lt;/a&gt;, trying to make it work led me to discover &lt;a href=&quot;https://docs.aws.amazon.com/sdk-for-ruby/v3/api/Aws/S3/Object.html#public_url-instance_method&quot;&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Aws::S3::Object#public_url&lt;/code&gt;&lt;/a&gt; doesn’t respect the &lt;a href=&quot;https://docs.aws.amazon.com/sdk-for-ruby/v3/api/Aws/S3/Client.html#initialize-instance_method&quot;&gt;AWS client’s &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;:use_accelerate_endpoint&lt;/code&gt;&lt;/a&gt; option to enable &lt;a href=&quot;https://docs.aws.amazon.com/AmazonS3/latest/dev/transfer-acceleration.html&quot;&gt;Amazon S3 Transfer Acceleration&lt;/a&gt;.&lt;/p&gt;

    &lt;p&gt;You can work around this by instead setting a custom &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;:endpoint&lt;/code&gt; in your client configuration:&lt;/p&gt;

    &lt;div class=&quot;language-yaml highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;na&quot;&gt;amazon&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;
  &lt;span class=&quot;na&quot;&gt;service&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;S3&lt;/span&gt;
  &lt;span class=&quot;na&quot;&gt;endpoint&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;https://s3-accelerate.amazonaws.com&quot;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;    &lt;/div&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;I also disappeared down a bit of a rabbit-hole trying to debug why an &lt;a href=&quot;https://guides.rubyonrails.org/active_storage_overview.html#transforming-images&quot;&gt;Active Storage Variant&lt;/a&gt; in my tests wasn’t being detected as &lt;a href=&quot;https://api.rubyonrails.org/classes/ActiveStorage/Variant.html#method-i-processed&quot;&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;processed&lt;/code&gt;&lt;/a&gt;.&lt;/p&gt;

    &lt;p&gt;After much use of &lt;a href=&quot;http://pry.github.io&quot;&gt;Pry&lt;/a&gt;, I discovered the bug was being caused by the fact Variant keys are generated by &lt;a href=&quot;https://api.rubyonrails.org/classes/ActiveSupport/MessageVerifier.html#method-i-generate&quot;&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;ActiveSupport::MessageVerifier#generate&lt;/code&gt;&lt;/a&gt; which uses &lt;a href=&quot;https://ruby-doc.org/core-2.7.2/Marshal.html#dump-method&quot;&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Marshal.dump&lt;/code&gt;&lt;/a&gt; to serialize a Variant’s transformations and, while my test variant seemed identical to the expected variant, there was a very subtle difference: the filename of the test variant was encoded with ASCII, not UTF-8:&lt;/p&gt;

    &lt;div class=&quot;language-ruby highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;o&quot;&gt;&amp;gt;&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;Marshal&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;dump&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;variant1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;variation&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;transformations&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;s2&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\x04\b&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\b&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\v&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;formatI&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\&quot;\b&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;png&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\x06&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\x06&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;ET:&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\x12&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;resize_to_fit[&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\a&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;0i&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\x01\x80&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\f&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;convertI&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\&quot;\b&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;jpg&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\x06&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;;&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\x06&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;T&quot;&lt;/span&gt;
&lt;span class=&quot;o&quot;&gt;&amp;gt;&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;Marshal&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;dump&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;variant2&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;variation&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;transformations&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;s2&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\x04\b&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\b&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\v&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;formatI&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\&quot;\b&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;png&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\x06&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\x06&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;EF:&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\x12&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;resize_to_fit[&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\a&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;0i&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\x01\x80&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\f&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;convertI&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\&quot;\b&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;jpg&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\x06&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;;&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\x06&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;T&quot;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;    &lt;/div&gt;

    &lt;p&gt;The difference can be hard to spot: it’s the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;\x06ET&lt;/code&gt; after &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;png&lt;/code&gt; instead of &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;\x06EF&lt;/code&gt;.&lt;/p&gt;

    &lt;p&gt;This stems from the &lt;a href=&quot;https://github.com/rails/rails/issues/25078&quot;&gt;encoding of &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Rails.root&lt;/code&gt; being ASCII&lt;/a&gt; (which is due to the underlying behaviour of &lt;a href=&quot;https://ruby-doc.org/stdlib-2.7.2/libdoc/pathname/rdoc/Pathname.html&quot;&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Pathname&lt;/code&gt;&lt;/a&gt;) and &lt;a href=&quot;https://www.rubydoc.info/github/brynary/rack-test/master/Rack/Test/UploadedFile&quot;&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Rack::Test::UploadedFile&lt;/code&gt;&lt;/a&gt; using that filename to determine the &lt;a href=&quot;https://www.rubydoc.info/github/brynary/rack-test/master/Rack/Test/UploadedFile#original_filename-instance_method&quot;&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;original_filename&lt;/code&gt;&lt;/a&gt;.&lt;/p&gt;

    &lt;p&gt;Passing an explicit &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;:original_filename&lt;/code&gt; fixes this:&lt;/p&gt;

    &lt;div class=&quot;language-ruby highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;no&quot;&gt;Rack&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;no&quot;&gt;Test&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;no&quot;&gt;UploadedFile&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;new&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;
  &lt;span class=&quot;no&quot;&gt;Rails&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;root&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;join&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;'spec/fixtures/image.png'&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;),&lt;/span&gt;
  &lt;span class=&quot;ss&quot;&gt;original_filename: &lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;'image.png'&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;    &lt;/div&gt;

    &lt;p&gt;I warned you it was a rabbit-hole.&lt;/p&gt;
  &lt;/li&gt;
&lt;/ul&gt;
</content>
</entry><entry>
  <title type="html">Weeknotes 58</title>
  <link href="https://mudge.name/2020/12/06/weeknotes-58/"/>
  <id>https://mudge.name/2020/12/06/weeknotes-58/</id>
  <published>2020-12-06T14:22:00+00:00</published>
  <updated>2020-12-06T14:22:00+00:00</updated>
  <summary type="html">Dealing with bad news, very specific tools for the job and migrating from Paperclip to Active Storage.</summary>
  <content type="html">&lt;ul&gt;
  &lt;li&gt;
    &lt;p&gt;While preparing some chocolate chip cookies, E asked, “there’s one left from the last batch, shall I &lt;a href=&quot;/2020/05/17/weeknotes-29/&quot;&gt;KitKat it&lt;/a&gt;?”&lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;I completed my first successful escape attempt from &lt;a href=&quot;https://www.supergiantgames.com/games/hades/&quot;&gt;Hades&lt;/a&gt; thanks to repeatedly watching &lt;a href=&quot;https://www.youtube.com/channel/UCSnGGntxMnAM6oDHML2qzaw&quot;&gt;Haelian’s videos&lt;/a&gt;, particularly the &lt;a href=&quot;https://www.youtube.com/watch?v=EbMkJBxIVaI&quot;&gt;top 6 easiest builds&lt;/a&gt;.&lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;After receiving some bad news, I went to make bread and discovered &lt;a href=&quot;/2020/05/03/weeknotes-27/&quot;&gt;our kitchen scales&lt;/a&gt; didn’t work. Sitting on a flat worktop with nothing on the pan, the reading jumped around of its own accord.&lt;/p&gt;

    &lt;p&gt;With a ratchet screwdriver in one hand and some &lt;a href=&quot;https://wd40.co.uk/specialist/fast-drying-contact-cleaner/&quot;&gt;WD-40® Specialist® Fast Drying Contact Cleaner&lt;/a&gt; in the other, I took the whole thing apart.&lt;/p&gt;

    &lt;p class=&quot;center&quot;&gt;&lt;img src=&quot;/i/scales.jpg&quot; width=&quot;375&quot; height=&quot;375&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;

    &lt;p&gt;Prying up the circuit board with a plectrum and discovering water pooled underneath the screen, I thought about my 92 year old grandfather who was the subject of the morning’s bad news. Taken ill in hospital, my occasionally overzealous sense of DIY comes entirely from him.&lt;/p&gt;

    &lt;p&gt;Always one to be fixing or building something throughout my childhood (or, on one occasion, inexplicably sawing the base off a beloved bin of my mother’s), he would invite me to help with whatever contraption he was working on. Even in his eighties, he drilled a hole in his walking stick so he could thread a loop through and secure it to his wrist, preventing it from ever falling from his grasp.&lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;After &lt;a href=&quot;/2020/10/11/weeknotes-50/&quot;&gt;rain broke our car gate again&lt;/a&gt;, I bought myself a four-way utility key which could lock and unlock the gate arms properly (rather than struggling with a pair of needle nose pliers). There’s something strangely satisfying about having a very specific tool for a job.&lt;/p&gt;

    &lt;p&gt;This might be why I own a spanner designed specifically for removing bath taps.&lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;I finally had justification to migrate from &lt;a href=&quot;https://thoughtbot.com/blog/closing-the-trombone&quot;&gt;Thoughtbot’s long-deprecated Paperclip&lt;/a&gt; to &lt;a href=&quot;https://guides.rubyonrails.org/active_storage_overview.html&quot;&gt;Rails’ Active Storage&lt;/a&gt; for storing uploads in a web application.&lt;/p&gt;

    &lt;p&gt;While Thoughtbot maintain a &lt;a href=&quot;https://github.com/thoughtbot/paperclip/blob/master/MIGRATING.md&quot;&gt;migration guide&lt;/a&gt;, I wanted to make sure I really understood the changes required before making them. Especially since I’m using &lt;a href=&quot;https://aws.amazon.com/s3/&quot;&gt;Amazon S3&lt;/a&gt; and &lt;a href=&quot;https://github.com/thoughtbot/paperclip/pull/2637&quot;&gt;Paperclip has a bug that can truncate your uploads when using S3&lt;/a&gt;.&lt;/p&gt;

    &lt;p&gt;While Paperclip concerns itself with processing uploads and storing them on disk or in a service like S3, it expects you to handle serving them by storing them somewhere publicly accessible (e.g. by &lt;a href=&quot;https://www.rubydoc.info/gems/paperclip/Paperclip/Storage/S3&quot;&gt;storing files with a &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;public-read&lt;/code&gt; ACL by default&lt;/a&gt;). As such, all my current uploads are stored in a public S3 bucket and I serve them via a &lt;a href=&quot;https://aws.amazon.com/cloudfront/&quot;&gt;CloudFront&lt;/a&gt; distribution.&lt;/p&gt;

    &lt;p&gt;This notion of both storage and serving are entwined: uploads are stored with relatively human-readable paths, e.g. &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;/system/books/full_pdfs/00/00/01/original/MyPDF.pdf&lt;/code&gt;, and are used to render any links, images, etc.&lt;/p&gt;

    &lt;p&gt;Looking at Active Storage, I was initially confused not to see any support for serving uploads via a CDN. When doing some test uploads to my existing S3 bucket, I was even more confused to see that a file like &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;MyPDF.pdf&lt;/code&gt; was uploaded to the top-level of the bucket with a &lt;a href=&quot;https://api.rubyonrails.org/classes/SecureRandom.html#method-c-base58&quot;&gt;base58&lt;/a&gt; filename like &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;77TMHrHJFvFDwodq8w7Ev2m7&lt;/code&gt;.&lt;/p&gt;

    &lt;p&gt;This is because I was still thinking of storage and serving as being one and the same. Diving into &lt;a href=&quot;https://guides.rubyonrails.org/active_storage_overview.html#linking-to-files&quot;&gt;how Active Storage handles linking to uploads&lt;/a&gt;, things started making a little more sense.&lt;/p&gt;

    &lt;p&gt;Instead of serving uploads using their “real” location in your storage service, e.g. a random key in an S3 bucket, Active Storage serves all uploads through its own controller with its own URLs. This way, the URL can contain the original filename, e.g. &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;/rails/blobs/lengthy-signed-id-here/MyPDF.pdf&lt;/code&gt; and generate short-lived, signed URLs to uploads in an otherwise private S3 bucket. What’s more, the &lt;a href=&quot;https://github.com/rails/rails/blob/6-0-stable/activestorage/app/controllers/active_storage/blobs_controller.rb&quot;&gt;implementation of this controller for Rails 6.0&lt;/a&gt; (and &lt;a href=&quot;https://github.com/rails/rails/blob/6-1-stable/activestorage/app/controllers/active_storage/blobs/redirect_controller.rb&quot;&gt;its replacement in the upcoming Rails 6.1&lt;/a&gt;) is fantastically simple, effectively boiling down to:&lt;/p&gt;

    &lt;div class=&quot;language-ruby highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;k&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;show&lt;/span&gt;
  &lt;span class=&quot;vi&quot;&gt;@blob&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;ActiveStorage&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;no&quot;&gt;Blob&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;find_signed!&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;params&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;ss&quot;&gt;:signed_id&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;])&lt;/span&gt;

  &lt;span class=&quot;n&quot;&gt;expires_in&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;ActiveStorage&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;service_urls_expire_in&lt;/span&gt;
  &lt;span class=&quot;n&quot;&gt;redirect_to&lt;/span&gt; &lt;span class=&quot;vi&quot;&gt;@blob&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;service_url&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;ss&quot;&gt;disposition: &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;params&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;ss&quot;&gt;:disposition&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;])&lt;/span&gt; &lt;span class=&quot;c1&quot;&gt;# or @blob.url(...) for Rails 6.1&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;    &lt;/div&gt;

    &lt;p&gt;The &lt;a href=&quot;https://guides.rubyonrails.org/active_storage_overview.html#linking-to-files&quot;&gt;official documentation explains this design decision&lt;/a&gt;:&lt;/p&gt;

    &lt;blockquote&gt;
      &lt;p&gt;Upon access, a redirect to the actual service endpoint is returned. This
indirection decouples the public URL from the actual one, and allows, for
example, mirroring attachments in different services for
high-availability.&lt;/p&gt;
    &lt;/blockquote&gt;

    &lt;p&gt;This decoupling means you rely on Rails to maintain your public URLs rather than using the underlying storage (as they rely on signed IDs to records in your database) but affords you the flexibility of moving storage services.&lt;/p&gt;

    &lt;p&gt;The difficult bit now is figuring out how to migrate all my existing Paperclip attachments to Active Storage without anyone noticing.&lt;/p&gt;

    &lt;p&gt;One useful thing I’ve discovered is that it is relatively easy to get a reference to a Paperclip attachment for a record even once you’ve replaced your call to &lt;a href=&quot;https://www.rubydoc.info/gems/paperclip/Paperclip%2FClassMethods:has_attached_file&quot;&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;has_attached_file&lt;/code&gt;&lt;/a&gt; with &lt;a href=&quot;https://api.rubyonrails.org/classes/ActiveStorage/Attached/Model.html#method-i-has_one_attached&quot;&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;has_one_attached&lt;/code&gt;&lt;/a&gt;:&lt;/p&gt;

    &lt;div class=&quot;language-ruby highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;k&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;Book&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&amp;lt;&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;ApplicationRecord&lt;/span&gt;
  &lt;span class=&quot;n&quot;&gt;has_one_attached&lt;/span&gt; &lt;span class=&quot;ss&quot;&gt;:full_pdf&lt;/span&gt; &lt;span class=&quot;c1&quot;&gt;# used to be has_attached_file :full_pdf&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;

&lt;span class=&quot;n&quot;&gt;book&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;Book&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;first&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;book&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;full_pdf&lt;/span&gt;
&lt;span class=&quot;c1&quot;&gt;# =&amp;gt; #&amp;lt;ActiveStorage::Attached::One:0xfeedface&amp;gt;&lt;/span&gt;

&lt;span class=&quot;n&quot;&gt;paperclip_attachment&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;Paperclip&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;no&quot;&gt;Attachment&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;new&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;ss&quot;&gt;:full_pdf&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;book&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;c1&quot;&gt;# =&amp;gt; #&amp;lt;Paperclip::Attachment:0xdecafbad&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;    &lt;/div&gt;

    &lt;p&gt;You can then convert from a Paperclip attachment to an Active Storage one
(albeit by downloading and re-uploading the original file) like so:&lt;/p&gt;

    &lt;div class=&quot;language-ruby highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;n&quot;&gt;book&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;full_pdf&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;attach&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;
  &lt;span class=&quot;ss&quot;&gt;io: &lt;/span&gt;&lt;span class=&quot;no&quot;&gt;URI&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;open&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;paperclip_attachment&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;url&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;),&lt;/span&gt;
  &lt;span class=&quot;ss&quot;&gt;filename: &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;paperclip_attachment&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;original_filename&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
  &lt;span class=&quot;ss&quot;&gt;content_type: &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;paperclip_attachment&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;content_type&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;    &lt;/div&gt;

    &lt;p&gt;My hope is that I can write a &lt;a href=&quot;https://guides.rubyonrails.org/v4.2/command_line.html#custom-rake-tasks&quot;&gt;custom Rake task&lt;/a&gt; that will copy all existing attachments to Active Storage before actually switching the application code over to using &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;has_one_attached&lt;/code&gt; so that the database and S3 bucket are fully populated before the switch, e.g. something like the following:&lt;/p&gt;

    &lt;div class=&quot;language-ruby highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;c1&quot;&gt;# The real implementation still uses Paperclip so override for the purposes of this task&lt;/span&gt;
&lt;span class=&quot;no&quot;&gt;Book&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;Class&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;new&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;no&quot;&gt;ApplicationRecord&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;has_one_attached&lt;/span&gt; &lt;span class=&quot;ss&quot;&gt;:full_pdf&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

&lt;span class=&quot;no&quot;&gt;Book&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;find_each&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;do&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;|&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;book&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;|&lt;/span&gt;
  &lt;span class=&quot;n&quot;&gt;paperclip_attachment&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;Paperclip&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;no&quot;&gt;Attachment&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;new&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;ss&quot;&gt;:full_pdf&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;book&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
  &lt;span class=&quot;k&quot;&gt;next&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;unless&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;paperclip_attachment&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;exists?&lt;/span&gt;

  &lt;span class=&quot;n&quot;&gt;book&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;full_pdf&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;attach&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;
    &lt;span class=&quot;ss&quot;&gt;io: &lt;/span&gt;&lt;span class=&quot;no&quot;&gt;URI&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;open&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;paperclip_attachment&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;url&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;),&lt;/span&gt;
    &lt;span class=&quot;ss&quot;&gt;filename: &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;paperclip_attachment&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;original_filename&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
    &lt;span class=&quot;ss&quot;&gt;content_type: &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;paperclip_attachment&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;content_type&lt;/span&gt;
  &lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;    &lt;/div&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;On a related note, I was wondering how easy it would be to run your own S3-compatible storage service given that &lt;a href=&quot;https://en.wikipedia.org/wiki/Amazon_S3#S3_API_and_competing_services&quot;&gt;quite a few competing services offer interoperability&lt;/a&gt;. This led me to &lt;a href=&quot;https://min.io&quot;&gt;MinIO&lt;/a&gt; which is an open source tool you can point at a location on disk and then &lt;a href=&quot;https://docs.min.io/docs/minio-quickstart-guide.html&quot;&gt;interact with it via the AWS CLI&lt;/a&gt;.&lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;Despite writing weeknotes for over a year, I only just discovered that Jekyll has  “Live Reload” and “Open URL” &lt;a href=&quot;https://jekyllrb.com/docs/configuration/options/#serve-command-options&quot;&gt;serve command options&lt;/a&gt;. No more will I manually open my browser to &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;localhost:4000&lt;/code&gt; or repeatedly mash ⌘R:&lt;/p&gt;

    &lt;div class=&quot;language-console highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;gp&quot;&gt;$&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;jekyll s &lt;span class=&quot;nt&quot;&gt;-lo&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;    &lt;/div&gt;
  &lt;/li&gt;
&lt;/ul&gt;
</content>
</entry><entry>
  <title type="html">Weeknotes 56 and 57</title>
  <link href="https://mudge.name/2020/11/28/weeknotes-56-and-57/"/>
  <id>https://mudge.name/2020/11/28/weeknotes-56-and-57/</id>
  <published>2020-11-28T13:47:00+00:00</published>
  <updated>2020-11-28T13:47:00+00:00</updated>
  <summary type="html">Cooking as a balm in troubled times, boil in the bag routine change, mistaken Twitter identity, meditating with DUPLO, sending cookies with the Fetch API, increasing errors by nearly 500,000%, trying to collect truly anonymous usage data and not thinking about a child's jaws.</summary>
  <content type="html">&lt;ul&gt;
  &lt;li&gt;
    &lt;p&gt;It has been a trying couple of weeks so we have been in need of uplifting television shows.&lt;/p&gt;

    &lt;p&gt;Our current rotation is “&lt;a href=&quot;https://www.netflix.com/gb/title/80036165&quot;&gt;Schitt’s Creek&lt;/a&gt;”, “&lt;a href=&quot;https://tv.apple.com/us/show/ted-lasso/umc.cmc.vtoh0mn0xn7t3c643xqonfzy&quot;&gt;Ted Lasso&lt;/a&gt;”, “&lt;a href=&quot;https://www.bbc.co.uk/programmes/m000mfhl&quot;&gt;Nadiya Bakes&lt;/a&gt;” and “&lt;a href=&quot;https://www.bbc.co.uk/programmes/m000pbhc&quot;&gt;Nigella’s Cook, Eat, Repeat&lt;/a&gt;”.&lt;/p&gt;

    &lt;p&gt;The latter two are a bit heavy-handed on the “&lt;a href=&quot;https://youtu.be/T72TopWbXJg&quot;&gt;everything’s alright, &lt;em&gt;everything&lt;/em&gt;’s alright, it’s OK, it’s &lt;em&gt;fine&lt;/em&gt;&lt;/a&gt;” but I don’t mind.&lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;In an attempt to break the &lt;a href=&quot;https://www.imdb.com/title/tt0107048/&quot;&gt;Groundhog Day&lt;/a&gt; of cooking at home and re-introduce some semblance of a date night, E and I ordered a &lt;a href=&quot;https://perilladining.slerp.com/order&quot;&gt;meal kit from Perilla&lt;/a&gt;. It met our hopes of providing a change from something we would typically make ourselves without being a chore to prepare: the main course came in two vacuum sealed bags and only needed boiling in water for 12 minutes.&lt;/p&gt;

    &lt;p&gt;We sat at our dining table for the first time in months and realised how much we needed a break from our daily routine.&lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;I got a &lt;a href=&quot;https://www.raspberrypi.org/products/raspberry-pi-400/&quot;&gt;Raspberry Pi 400&lt;/a&gt; and was pleasantly surprised to find a print edition of the &lt;a href=&quot;https://magpi.raspberrypi.org/books/beginners-guide-4th-ed&quot;&gt;Raspberry Pi Beginner’s Guide&lt;/a&gt; in the box. Leafing through it while booting the Pi 400 for the first time, I pictured myself at 8 years old opening this on Christmas Day.&lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;I’ve had &lt;a href=&quot;https://twitter.com/mudge&quot;&gt;my Twitter account&lt;/a&gt; for 14 years, initially only using it to send free group text messages. I am not &lt;a href=&quot;https://twitter.com/dotMudge&quot;&gt;the most famous “mudge”&lt;/a&gt; and am constantly mistaken for &lt;a href=&quot;https://en.wikipedia.org/wiki/Peiter_Zatko&quot;&gt;Peiter “Mudge” Zatko&lt;/a&gt;, so much so that I decided to &lt;a href=&quot;https://help.twitter.com/en/safety-and-security/public-and-protected-tweets&quot;&gt;“protect” my account&lt;/a&gt; a few weeks ago.&lt;/p&gt;

    &lt;p&gt;I did so in the nick of time as &lt;a href=&quot;https://uk.reuters.com/article/us-twitter-security/twitter-names-famed-hacker-mudge-as-head-of-security-idUSKBN27W2MB&quot;&gt;Zatko is the new Head of Security for Twitter&lt;/a&gt;.&lt;/p&gt;

    &lt;p&gt;Alas, now I’m receiving a whole new type of misdirected tweet: support and feature requests for Twitter itself.&lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;When looking after C, one of my favourite things to do is build him the most elaborate tower out of &lt;a href=&quot;https://www.lego.com/en-gb/themes/duplo&quot;&gt;DUPLO&lt;/a&gt; possible. I’ll incorporate &lt;a href=&quot;https://en.wikipedia.org/wiki/Bogie&quot;&gt;bogies&lt;/a&gt;, animal heads and even the occasional &lt;a href=&quot;https://www.lego.com/en-gb/product/submarine-adventure-10910&quot;&gt;submarine&lt;/a&gt;.&lt;/p&gt;

    &lt;p&gt;As I know he will eventually destroy it, it has become a sort of &lt;a href=&quot;https://en.wikipedia.org/wiki/Mandala&quot;&gt;mandala&lt;/a&gt;.&lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;In versions of &lt;a href=&quot;https://www.mozilla.org/en-GB/firefox/new/&quot;&gt;Mozilla Firefox&lt;/a&gt; prior to &lt;a href=&quot;https://www.mozilla.org/en-US/firefox/61.0/releasenotes/&quot;&gt;61.0&lt;/a&gt;, requests made with the &lt;a href=&quot;https://developer.mozilla.org/en-US/docs/Web/API/Fetch_API/Using_Fetch&quot;&gt;Fetch API&lt;/a&gt; won’t send cookies unless you specify the &lt;a href=&quot;https://developer.mozilla.org/en-US/docs/Web/API/WindowOrWorkerGlobalScope/fetch#Parameters&quot;&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;credentials&lt;/code&gt; option&lt;/a&gt;.&lt;/p&gt;

    &lt;p&gt;I ran into this when &lt;a href=&quot;https://guides.rubyonrails.org/security.html#csrf-countermeasures&quot;&gt;Rails’ cross-site request forgery countermeasures&lt;/a&gt; prevented my &lt;a href=&quot;https://stripe.com/en-gb/payments/checkout&quot;&gt;Stripe Checkout&lt;/a&gt; integration from working properly in Firefox 52. Switching to the new default value of &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;same-origin&lt;/code&gt; for &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;credentials&lt;/code&gt; meant the Rails session cookie &lt;em&gt;was&lt;/em&gt; correctly sent and the CSRF forgery protection would permit the request:&lt;/p&gt;

    &lt;div class=&quot;language-javascript highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;nx&quot;&gt;fetch&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;/some-endpoint&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;na&quot;&gt;method&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;dl&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;POST&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
  &lt;span class=&quot;na&quot;&gt;credentials&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;dl&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;same-origin&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
  &lt;span class=&quot;na&quot;&gt;headers&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;dl&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;Content-Type&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;dl&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;application/json&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
    &lt;span class=&quot;dl&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;X-CSRF-Token&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;csrfToken&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
  &lt;span class=&quot;p&quot;&gt;},&lt;/span&gt;
  &lt;span class=&quot;c1&quot;&gt;// ...&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;})&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;    &lt;/div&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;Concerned there were other bugs I had missed, I started using &lt;a href=&quot;https://reactjs.org/docs/strict-mode.html&quot;&gt;React’s Strict Mode&lt;/a&gt; to perform stricter checks in development. In particular, I wanted to see how my components would behave when &lt;a href=&quot;https://reactjs.org/docs/strict-mode.html#detecting-unexpected-side-effects&quot;&gt;intentionally double-rendered&lt;/a&gt;.&lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;I added &lt;a href=&quot;https://sentry.io/for/javascript/&quot;&gt;JavaScript error monitoring with Sentry&lt;/a&gt; which caused a 493,803% increase in “events” since the previous week when I was only &lt;a href=&quot;https://sentry.io/for/ruby/&quot;&gt;monitoring Ruby errors&lt;/a&gt;.&lt;/p&gt;

    &lt;p&gt;Specifically, I started to see a &lt;em&gt;lot&lt;/em&gt; of errors from a &lt;a href=&quot;https://github.com/github/fetch/&quot;&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;window.fetch&lt;/code&gt; polyfill&lt;/a&gt; bundled with the &lt;a href=&quot;http://shopify.github.io/buy-button-js/&quot;&gt;Shopify BuyButton.js&lt;/a&gt; but almost all of them came from a download page.&lt;/p&gt;

    &lt;p&gt;This page shows a simple “Thank you” message to the user and immediately redirects them to a PDF using a &lt;a href=&quot;https://developer.mozilla.org/en-US/docs/Web/HTML/Element/meta#attr-http-equiv&quot;&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;&amp;lt;meta http-equiv=&quot;refresh&quot;&amp;gt;&lt;/code&gt;&lt;/a&gt;. When trying to reproduce the error in &lt;a href=&quot;https://www.apple.com/uk/safari/&quot;&gt;Safari&lt;/a&gt;, I noticed a bunch of different errors all due to the fact the redirect effectively &lt;em&gt;cancels&lt;/em&gt; any JavaScript in progress.&lt;/p&gt;

    &lt;p&gt;To work around this, I copied &lt;a href=&quot;https://stackoverflow.com/questions/156686/how-to-start-automatic-download-of-a-file-in-internet-explorer&quot;&gt;a technique used by SourceForge and Audible&lt;/a&gt; and used an invisible &lt;a href=&quot;https://developer.mozilla.org/en-US/docs/Web/HTML/Element/iframe&quot;&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;&amp;lt;iframe&amp;gt;&lt;/code&gt;&lt;/a&gt; with a &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;src&lt;/code&gt; pointing to a file with a &lt;a href=&quot;https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Content-Disposition&quot;&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Content-Disposition&lt;/code&gt;&lt;/a&gt; of &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;attachment&lt;/code&gt;, e.g.&lt;/p&gt;

    &lt;div class=&quot;language-html highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;nt&quot;&gt;&amp;lt;iframe&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;src=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;https://example.com/some.pdf&quot;&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;class=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;d-none&quot;&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;&amp;gt;&amp;lt;/iframe&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;    &lt;/div&gt;

    &lt;p&gt;This immediately causes the browser to start downloading the PDF without cancelling any JavaScript running on the page.&lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;I’ve been working on a web service to record anonymous usage statistics of a desktop application. A key goal of this project is to avoid storing &lt;a href=&quot;https://ico.org.uk/for-organisations/guide-to-data-protection/guide-to-the-general-data-protection-regulation-gdpr/key-definitions/what-is-personal-data/&quot;&gt;personal data&lt;/a&gt; and exemplify &lt;a href=&quot;https://ico.org.uk/for-organisations/guide-to-data-protection/guide-to-the-general-data-protection-regulation-gdpr/principles/data-minimisation/&quot;&gt;data minimisation&lt;/a&gt;.&lt;/p&gt;

    &lt;p&gt;In looking for other applications that do a similar thing, I dug into the &lt;a href=&quot;https://docs.brew.sh/Analytics&quot;&gt;anonymous aggregate user behaviour analytics collected by Homebrew&lt;/a&gt; which &lt;a href=&quot;https://github.com/Homebrew/brew/blob/ae0332a0f6fcf4846ef032ba217fb00ef66ccc48/Library/Homebrew/utils/analytics.rb&quot;&gt;uses &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;curl&lt;/code&gt; to report events&lt;/a&gt; to the &lt;a href=&quot;https://developers.google.com/analytics/devguides/collection/protocol/v1&quot;&gt;Google Analytics Measurement Protocol&lt;/a&gt;.&lt;/p&gt;

    &lt;p&gt;Reporting events to a third party over HTTP opens a whole can of worms, particularly as IP addresses alone can be considered personal data. As our reporting needs are very simple, I decided to implement my own service using &lt;a href=&quot;https://rack.github.io&quot;&gt;Rack&lt;/a&gt; and &lt;a href=&quot;https://redis.io&quot;&gt;Redis&lt;/a&gt;.&lt;/p&gt;

    &lt;p&gt;Specifically, requests to the service are stored as daily counts in &lt;a href=&quot;https://redis.io/topics/data-types#sorted-sets&quot;&gt;Sorted Sets&lt;/a&gt; that expire after 90 days like so:&lt;/p&gt;

    &lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;ZADD choice:2020-11-28 INCR 1 &quot;Some choice&quot;
EXPIRE choice:2020-11-28 7776000
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;    &lt;/div&gt;

    &lt;p&gt;This way, we can see what the top ten most popular choices for a particular day are with &lt;a href=&quot;http://redis.io/commands/zrevrange&quot;&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;ZREVRANGE&lt;/code&gt;&lt;/a&gt; like so:&lt;/p&gt;

    &lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;ZREVRANGE choice:2020-11-28 0 10 WITHSCORES
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;    &lt;/div&gt;

    &lt;p&gt;If we want to look at choices over a longer time period, we use &lt;a href=&quot;http://redis.io/commands/zunionstore&quot;&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;ZUNIONSTORE&lt;/code&gt;&lt;/a&gt; to aggregate each of the dates in the range:&lt;/p&gt;

    &lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;ZUNIONSTORE choice:past_week 8 choice:2020-11-20 choice:2020-11-21 choice:2020-11-22 choice:2020-11-23 choice:2020-11-24 choice:2020-11-25 choice:2020-11-26 choice:2020-11-27
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;    &lt;/div&gt;

    &lt;p&gt;Then we use &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;ZREVRANGE&lt;/code&gt; again to get the results:&lt;/p&gt;

    &lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;ZREVRANGE choice:past_week 0 10 WITHSCORES
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;    &lt;/div&gt;

    &lt;p&gt;Despite best intentions, it is very easy to accidentally store personal data without realising it. For example, even if you don’t log requests to your service in your application code, Heroku will log the IP addresses of every request made to your app, &lt;a href=&quot;https://devcenter.heroku.com/articles/logging#log-history-limits&quot;&gt;retaining the most recent 1,500 lines of logs for one week&lt;/a&gt;.&lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;I enjoyed &lt;a href=&quot;https://rowanmanning.com/posts/yeehaw/&quot;&gt;Rowan Manning’s “Yeehaw!”&lt;/a&gt; about attempting side projects and tip #5 “When the Fun Stops, Stop” feels especially relevant when working alone. I’ve had several situations recently where I find myself down some blind alley of refactoring and have to force myself to &lt;a href=&quot;https://git-scm.com/docs/git-restore&quot;&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;git restore .&lt;/code&gt;&lt;/a&gt; and refocus my efforts.&lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;C is unhappily teething but please try not to think about how &lt;a href=&quot;http://memento.muttermuseum.org/detail/child-skull&quot;&gt;every child’s jaws are packed with teeth&lt;/a&gt;.&lt;/p&gt;
  &lt;/li&gt;
&lt;/ul&gt;
</content>
</entry><entry>
  <title type="html">Weeknotes 55</title>
  <link href="https://mudge.name/2020/11/15/weeknotes-55/"/>
  <id>https://mudge.name/2020/11/15/weeknotes-55/</id>
  <published>2020-11-15T10:52:00+00:00</published>
  <updated>2020-11-15T10:52:00+00:00</updated>
  <summary type="html">Integrating Shopify BuyButton.js with Turbolinks, the M1 SoC, testing signed Stripe webhooks and cavorting.</summary>
  <content type="html">&lt;ul&gt;
  &lt;li&gt;
    &lt;p&gt;This week, I’ve been trying to make &lt;a href=&quot;http://shopify.github.io/buy-button-js/&quot;&gt;Shopify BuyButton.js&lt;/a&gt; play nicely with &lt;a href=&quot;https://github.com/turbolinks/turbolinks&quot;&gt;Turbolinks&lt;/a&gt;.&lt;/p&gt;

    &lt;p&gt;Integration with Turbolinks is often tricky because &lt;a href=&quot;https://github.com/turbolinks/turbolinks#building-your-turbolinks-application&quot;&gt;it retains the state of &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;window&lt;/code&gt; and &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;document&lt;/code&gt; across page changes and any other JavaScript objects will stay in memory&lt;/a&gt;. In the case of BuyButton.js, this means any &lt;a href=&quot;https://github.com/Shopify/buy-button-js/blob/v2.1.7/src/ui.js#L29-L36&quot;&gt;UI state&lt;/a&gt; and &lt;a href=&quot;https://github.com/Shopify/buy-button-js/blob/v2.1.7/src/ui.js#L50-L53&quot;&gt;bound event listeners&lt;/a&gt; will persist as a user clicks around your site. This is further complicated by &lt;a href=&quot;https://github.com/turbolinks/turbolinks#understanding-caching&quot;&gt;Turbolinks’ caching&lt;/a&gt; which means &lt;a href=&quot;https://github.com/turbolinks/turbolinks#making-transformations-idempotent&quot;&gt;any transformations may be applied multiple times&lt;/a&gt;.&lt;/p&gt;

    &lt;p&gt;If you don’t take Turbolinks into consideration, you might see broken behaviour such as clicks on “Add to cart” not working, the entire UI failing to appear or multiple buy buttons appearing every time you use the browser’s back and forward buttons.&lt;/p&gt;

    &lt;p&gt;While I’m still working on this, I’ve had some success with the following strategy:&lt;/p&gt;

    &lt;ol&gt;
      &lt;li&gt;
        &lt;p&gt;&lt;a href=&quot;http://shopify.github.io/buy-button-js/#creating-a-shop-client&quot;&gt;Create a Shop client&lt;/a&gt; &lt;em&gt;once&lt;/em&gt; to be re-used across all pages.&lt;/p&gt;

        &lt;div class=&quot;language-javascript highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;kd&quot;&gt;const&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;client&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;ShopifyBuy&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;buildClient&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;({&lt;/span&gt;
  &lt;span class=&quot;na&quot;&gt;domain&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;dl&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;my-shop.myshopify.com&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
  &lt;span class=&quot;na&quot;&gt;storefrontAccessToken&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;dl&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;your-storefront-access-token&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;});&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;        &lt;/div&gt;
      &lt;/li&gt;
      &lt;li&gt;
        &lt;p&gt;&lt;a href=&quot;http://shopify.github.io/buy-button-js/#initializing-the-library&quot;&gt;Initialize the library&lt;/a&gt; &lt;em&gt;once&lt;/em&gt; to be re-used across all pages.&lt;/p&gt;

        &lt;div class=&quot;language-javascript highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;kd&quot;&gt;const&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;ui&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;ShopifyBuy&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;UI&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;init&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;client&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;        &lt;/div&gt;

        &lt;p&gt;This will &lt;a href=&quot;https://github.com/Shopify/buy-button-js/blob/v2.1.7/src/ui.js#L273-L281&quot;&gt;append a &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;&amp;lt;style&amp;gt;&lt;/code&gt; tag to the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;&amp;lt;head&amp;gt;&lt;/code&gt; of your &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;document&lt;/code&gt;&lt;/a&gt; and &lt;a href=&quot;https://github.com/Shopify/buy-button-js/blob/v2.1.7/src/ui.js#L273-L281&quot;&gt;add various event listeners to &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;document&lt;/code&gt;&lt;/a&gt;. As the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;&amp;lt;head&amp;gt;&lt;/code&gt; of your page will be &lt;a href=&quot;https://github.com/turbolinks/turbolinks#navigating-with-turbolinks&quot;&gt;merged when a user navigates between pages&lt;/a&gt; this will retain the styles and it is &lt;a href=&quot;https://github.com/turbolinks/turbolinks#observing-navigation-events&quot;&gt;best practice to only bind event listeners once on &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;document&lt;/code&gt;&lt;/a&gt;.&lt;/p&gt;
      &lt;/li&gt;
      &lt;li&gt;
        &lt;p&gt;&lt;del&gt;On &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;turbolinks:load&lt;/code&gt;, &lt;em&gt;reset the inner state of the library&lt;/em&gt; using &lt;a href=&quot;https://github.com/Shopify/buy-button-js/blob/v2.1.7/src/ui.js#L92-L105&quot;&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;destroyComponent&lt;/code&gt;&lt;/a&gt; as you can no longer guarantee the various DOM elements the UI will have injected into your &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;&amp;lt;body&amp;gt;&lt;/code&gt; are still present.&lt;/del&gt;&lt;/p&gt;

        &lt;div class=&quot;language-javascript highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;nx&quot;&gt;ui&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;destroyComponent&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;product&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
&lt;span class=&quot;nx&quot;&gt;ui&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;destroyComponent&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;cart&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
&lt;span class=&quot;nx&quot;&gt;ui&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;destroyComponent&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;collection&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
&lt;span class=&quot;nx&quot;&gt;ui&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;destroyComponent&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;productSet&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
&lt;span class=&quot;nx&quot;&gt;ui&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;destroyComponent&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;modal&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
&lt;span class=&quot;nx&quot;&gt;ui&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;destroyComponent&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;toggle&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;        &lt;/div&gt;

        &lt;p&gt;&lt;em&gt;Update:&lt;/em&gt; In writing this, I discovered &lt;a href=&quot;https://github.com/Shopify/buy-button-js/blob/v2.1.7/src/ui.js#L92-L105&quot;&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;destroyComponent&lt;/code&gt;&lt;/a&gt; doesn’t work if you don’t pass the ID of a component’s model. It uses &lt;a href=&quot;https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/splice&quot;&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Array.prototype.splice()&lt;/code&gt;&lt;/a&gt; to mutate an array while looping over it. This means the position of each component in the array is changing as the loop advances so some components will not be destroyed, e.g.&lt;/p&gt;

        &lt;div class=&quot;language-javascript highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;o&quot;&gt;&amp;gt;&lt;/span&gt; &lt;span class=&quot;kd&quot;&gt;const&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;foo&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;2&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;3&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;4&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;];&lt;/span&gt;
&lt;span class=&quot;o&quot;&gt;&amp;gt;&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;foo&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;forEach&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;((&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;e&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;index&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;foo&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;splice&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;index&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;));&lt;/span&gt;
&lt;span class=&quot;o&quot;&gt;&amp;gt;&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;foo&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;2&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;4&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;]&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;        &lt;/div&gt;

        &lt;p&gt;We can fix this by destroying and resetting the components ourselves:&lt;/p&gt;

        &lt;div class=&quot;language-javascript highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;nx&quot;&gt;ui&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;components&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;product&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;forEach&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;((&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;component&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;component&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;destroy&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;());&lt;/span&gt;
&lt;span class=&quot;nx&quot;&gt;ui&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;components&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;cart&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;forEach&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;((&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;component&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;component&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;destroy&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;());&lt;/span&gt;
&lt;span class=&quot;nx&quot;&gt;ui&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;components&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;collection&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;forEach&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;((&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;component&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;component&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;destroy&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;());&lt;/span&gt;
&lt;span class=&quot;nx&quot;&gt;ui&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;components&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;productSet&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;forEach&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;((&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;component&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;component&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;destroy&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;());&lt;/span&gt;
&lt;span class=&quot;nx&quot;&gt;ui&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;components&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;modal&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;forEach&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;((&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;component&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;component&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;destroy&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;());&lt;/span&gt;
&lt;span class=&quot;nx&quot;&gt;ui&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;components&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;toggle&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;forEach&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;((&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;component&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;component&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;destroy&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;());&lt;/span&gt;
&lt;span class=&quot;nx&quot;&gt;ui&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;components&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;product&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;[];&lt;/span&gt;
&lt;span class=&quot;nx&quot;&gt;ui&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;components&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;cart&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;[];&lt;/span&gt;
&lt;span class=&quot;nx&quot;&gt;ui&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;components&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;collection&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;[];&lt;/span&gt;
&lt;span class=&quot;nx&quot;&gt;ui&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;components&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;productSet&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;[];&lt;/span&gt;
&lt;span class=&quot;nx&quot;&gt;ui&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;components&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;modal&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;[];&lt;/span&gt;
&lt;span class=&quot;nx&quot;&gt;ui&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;components&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;toggle&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;[];&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;        &lt;/div&gt;

        &lt;p&gt;Or, more concisely:&lt;/p&gt;

        &lt;div class=&quot;language-javascript highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;nb&quot;&gt;Object&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;keys&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;ui&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;components&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;).&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;forEach&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;((&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;type&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;nx&quot;&gt;ui&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;components&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;type&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;].&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;forEach&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;((&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;component&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;component&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;destroy&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;());&lt;/span&gt;
  &lt;span class=&quot;nx&quot;&gt;ui&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;components&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;type&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;[];&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;});&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;        &lt;/div&gt;

        &lt;p&gt;We can do this when the &lt;a href=&quot;https://github.com/turbolinks/turbolinks#preparing-the-page-to-be-cached&quot;&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;turbolinks:before-cache&lt;/code&gt; event fires as recommended in the documentation&lt;/a&gt;:&lt;/p&gt;

        &lt;blockquote&gt;
          &lt;p&gt;You can use this event to reset forms, collapse expanded UI elements, or tear down any third-party widgets so the page is ready to be displayed again.&lt;/p&gt;
        &lt;/blockquote&gt;

        &lt;p&gt;This will ensure any DOM elements injected by BuyButton.js won’t be cached so you don’t end up with duplicate “Add to cart” buttons that don’t do anything when clicked.&lt;/p&gt;

        &lt;div class=&quot;language-javascript highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt; &lt;span class=&quot;nb&quot;&gt;document&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;addEventListener&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;turbolinks:before-cache&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;()&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
   &lt;span class=&quot;nb&quot;&gt;Object&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;keys&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;ui&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;components&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;).&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;forEach&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;((&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;type&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
     &lt;span class=&quot;nx&quot;&gt;ui&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;components&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;type&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;].&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;forEach&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;((&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;component&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;component&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;destroy&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;());&lt;/span&gt;
     &lt;span class=&quot;nx&quot;&gt;ui&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;components&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;type&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;[];&lt;/span&gt;
   &lt;span class=&quot;p&quot;&gt;});&lt;/span&gt;
 &lt;span class=&quot;p&quot;&gt;});&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;        &lt;/div&gt;
      &lt;/li&gt;
      &lt;li&gt;
        &lt;p&gt;Following that, &lt;a href=&quot;http://shopify.github.io/buy-button-js/#creating-a-component&quot;&gt;create your components&lt;/a&gt; as normal.&lt;/p&gt;

        &lt;div class=&quot;language-javascript highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;nx&quot;&gt;ui&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;createComponent&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;product&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;na&quot;&gt;handle&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;dl&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;some-product-handle&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
  &lt;span class=&quot;na&quot;&gt;node&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;element&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;});&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;        &lt;/div&gt;

        &lt;p&gt;To make it easy for Turbolinks to create the appropriate components based on the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;&amp;lt;body&amp;gt;&lt;/code&gt; contents for every &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;turbolinks:load&lt;/code&gt;, I embedded the necessary information in &lt;a href=&quot;https://developer.mozilla.org/en-US/docs/Learn/HTML/Howto/Use_data_attributes&quot;&gt;data attributes&lt;/a&gt;.&lt;/p&gt;

        &lt;div class=&quot;language-javascript highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;nb&quot;&gt;document&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;querySelectorAll&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;[data-shopify-product-handle]&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;).&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;forEach&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;((&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;element&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;kd&quot;&gt;const&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;dataset&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;shopifyProductHandle&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;element&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;kd&quot;&gt;const&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;temporaryWrapper&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;document&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;createElement&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;div&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;

  &lt;span class=&quot;nx&quot;&gt;element&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;appendChild&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;temporaryWrapper&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;

  &lt;span class=&quot;nx&quot;&gt;ui&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;createComponent&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;product&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;na&quot;&gt;handle&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;shopifyProductHandle&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
    &lt;span class=&quot;na&quot;&gt;node&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;temporaryWrapper&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
  &lt;span class=&quot;p&quot;&gt;});&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;});&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;        &lt;/div&gt;

        &lt;div class=&quot;language-html highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;nt&quot;&gt;&amp;lt;div&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;data-shopify-product-handle=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;some-product-handle&quot;&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;&amp;gt;&amp;lt;/div&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;        &lt;/div&gt;

        &lt;p&gt;I create a new &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;&amp;lt;div&amp;gt;&lt;/code&gt; for the component to use as calling &lt;a href=&quot;https://github.com/Shopify/buy-button-js/blob/ba89d7fa2f4944da7f388a9b6d632d764b376e5f/src/component.js#L197-L202&quot;&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;destroy&lt;/code&gt; will remove the node from the DOM&lt;/a&gt;.&lt;/p&gt;
      &lt;/li&gt;
    &lt;/ol&gt;

    &lt;p&gt;&lt;del&gt;I still need to iron out some issues with Turbolinks caching but I’m optimistic.&lt;/del&gt; &lt;em&gt;Update:&lt;/em&gt; with the update to use &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;turbolinks:before-cache&lt;/code&gt; above, all my caching issues are now resolved.&lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;On Tuesday, &lt;a href=&quot;https://www.apple.com/mac/m1/&quot;&gt;Apple announced the M1 system on a chip&lt;/a&gt;. While I’m not in a hurry to replace my &lt;a href=&quot;https://marco.org/2017/11/14/best-laptop-ever&quot;&gt;beloved&lt;/a&gt; refurbished 2015 15-inch MacBook Pro, the pandemic and &lt;a href=&quot;/2020/01/19/weeknotes-12/&quot;&gt;my musculoskeletal woes&lt;/a&gt; have made my working setup decidedly stationary. I’m seriously considering the &lt;a href=&quot;https://www.apple.com/uk/mac-mini/&quot;&gt;Mac mini&lt;/a&gt; for the first time.&lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;I’ve also been working on an integration with &lt;a href=&quot;https://stripe.com/&quot;&gt;Stripe&lt;/a&gt; and have been amazed how good their &lt;a href=&quot;https://stripe.com/docs/development&quot;&gt;developer tools&lt;/a&gt; are. From the ability to &lt;a href=&quot;https://stripe.com/docs/cli/listen&quot;&gt;test webhooks with their CLI&lt;/a&gt;, their &lt;a href=&quot;https://stripe.com/docs/development/checklist&quot;&gt;go-live checklist&lt;/a&gt; and &lt;a href=&quot;https://stripe.com/docs/testing&quot;&gt;extensive support for testing&lt;/a&gt;, it has been surprisingly pleasant to work with.&lt;/p&gt;

    &lt;p&gt;If you need to test handling their webhooks but are &lt;a href=&quot;https://stripe.com/docs/webhooks/signatures&quot;&gt;verifying their signatures&lt;/a&gt;, I found the following useful in my &lt;a href=&quot;https://relishapp.com/rspec/rspec-rails/docs/controller-specs&quot;&gt;controller specs&lt;/a&gt; (assuming you’re storing your signing secret in &lt;a href=&quot;https://guides.rubyonrails.org/configuring.html#custom-configuration&quot;&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Rails.configuration.stripe_webhook_signing_secret&lt;/code&gt;&lt;/a&gt;):&lt;/p&gt;

    &lt;div class=&quot;language-ruby highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;k&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;sign_stripe_webhook&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;payload&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
  &lt;span class=&quot;n&quot;&gt;timestamp&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;Time&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;zone&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;now&lt;/span&gt;
  &lt;span class=&quot;n&quot;&gt;signature&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;Stripe&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;no&quot;&gt;Webhook&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;no&quot;&gt;Signature&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;compute_signature&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;timestamp&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;payload&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;to_json&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
    &lt;span class=&quot;no&quot;&gt;Rails&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;configuration&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;stripe_webhook_signing_secret&lt;/span&gt;
  &lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;

  &lt;span class=&quot;n&quot;&gt;request&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;headers&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;'Stripe-Signature'&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;Stripe&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;no&quot;&gt;Webhook&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;no&quot;&gt;Signature&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;generate_header&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;timestamp&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;signature&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;    &lt;/div&gt;

    &lt;p&gt;Then I can use it like so:&lt;/p&gt;

    &lt;div class=&quot;language-ruby highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;n&quot;&gt;it&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;'returns a 200 OK with a valid Stripe event'&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;do&lt;/span&gt;
  &lt;span class=&quot;n&quot;&gt;event&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;s1&quot;&gt;'id'&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;'evt_1'&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
    &lt;span class=&quot;s1&quot;&gt;'object'&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;'event'&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
    &lt;span class=&quot;s1&quot;&gt;'type'&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;'checkout.session.completed'&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
    &lt;span class=&quot;s1&quot;&gt;'data'&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{}&lt;/span&gt;
  &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

  &lt;span class=&quot;n&quot;&gt;sign_stripe_webhook&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;event&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;

  &lt;span class=&quot;n&quot;&gt;post&lt;/span&gt; &lt;span class=&quot;ss&quot;&gt;:create&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;ss&quot;&gt;params: &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;event&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;ss&quot;&gt;as: :json&lt;/span&gt;

  &lt;span class=&quot;n&quot;&gt;expect&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;response&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;).&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;to&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;have_http_status&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;ss&quot;&gt;:ok&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;    &lt;/div&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;Following &lt;a href=&quot;/2020/11/08/weeknotes-54/&quot;&gt;last week’s adventures with &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;brew bundle&lt;/code&gt;&lt;/a&gt; and &lt;a href=&quot;https://medium.com/@scottm&quot;&gt;Scott&lt;/a&gt;’s tip about &lt;a href=&quot;https://medium.com/@scottm&quot;&gt;Homebrew Bundle&lt;/a&gt; being integrated with &lt;a href=&quot;https://github.com/mas-cli/mas&quot;&gt;mas&lt;/a&gt;, I committed my &lt;a href=&quot;https://github.com/mudge/dotfiles/blob/master/Brewfile&quot;&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Brewfile&lt;/code&gt; to my dotfiles&lt;/a&gt;.&lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;blockquote&gt;
      &lt;p&gt;The days of spring will surely bring the birds and bees cavorting.&lt;/p&gt;

      &lt;p&gt;But since I am a gentleman, I’d much rather be &lt;a href=&quot;https://youtu.be/Sx3ORAO1Y6s&quot;&gt;jorting&lt;/a&gt;.&lt;/p&gt;
    &lt;/blockquote&gt;
  &lt;/li&gt;
&lt;/ul&gt;
</content>
</entry><entry>
  <title type="html">Weeknotes 54</title>
  <link href="https://mudge.name/2020/11/08/weeknotes-54/"/>
  <id>https://mudge.name/2020/11/08/weeknotes-54/</id>
  <published>2020-11-08T14:12:00+00:00</published>
  <updated>2020-11-08T14:12:00+00:00</updated>
  <summary type="html">Handling numeric user input with React, cleaning up unused Homebrew dependencies, Command Line Tools woe, losing control of your eyes and comparing our son to icons in IKEA instruction manuals.</summary>
  <content type="html">&lt;ul&gt;
  &lt;li&gt;
    &lt;p&gt;I worked on two projects with &lt;a href=&quot;https://blog.chrislowis.co.uk&quot;&gt;Chris&lt;/a&gt; for &lt;a href=&quot;https://gofreerange.com&quot;&gt;Go Free Range&lt;/a&gt;, the first of which involved working with a &lt;a href=&quot;https://reactjs.org&quot;&gt;React&lt;/a&gt; form to record numbers given by the user. We already had some &lt;a href=&quot;https://developer.mozilla.org/en-US/docs/Web/HTML/Element/select&quot;&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;&amp;lt;select&amp;gt;&lt;/code&gt;&lt;/a&gt; elements to do this, e.g.&lt;/p&gt;

    &lt;div class=&quot;language-jsx highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;kd&quot;&gt;const&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;Form&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;()&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;kd&quot;&gt;const&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;unit&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;setUnit&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;useState&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;

  &lt;span class=&quot;kd&quot;&gt;const&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;handleUnitChange&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;({&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;target&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;value&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;}})&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;nx&quot;&gt;setUnit&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;parseFloat&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;value&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;));&lt;/span&gt;
  &lt;span class=&quot;p&quot;&gt;};&lt;/span&gt;

  &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;select&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;value&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;si&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;unit&lt;/span&gt;&lt;span class=&quot;si&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;onChange&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;si&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;handleUnitChange&lt;/span&gt;&lt;span class=&quot;si&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;gt;&lt;/span&gt;
      &lt;span class=&quot;p&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;option&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;value&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;1&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;gt;&lt;/span&gt;m&lt;span class=&quot;p&quot;&gt;&amp;lt;/&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;option&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;gt;&lt;/span&gt;
      &lt;span class=&quot;p&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;option&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;value&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;100&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;gt;&lt;/span&gt;cm&lt;span class=&quot;p&quot;&gt;&amp;lt;/&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;option&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;gt;&lt;/span&gt;
      &lt;span class=&quot;p&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;option&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;value&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;1000&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;gt;&lt;/span&gt;mm&lt;span class=&quot;p&quot;&gt;&amp;lt;/&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;option&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;&amp;lt;/&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;select&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;gt;&lt;/span&gt;
  &lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;};&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;    &lt;/div&gt;

    &lt;p&gt;This works fine. React is able to interpret our numeric value for &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;unit&lt;/code&gt; and select the appropriate &lt;a href=&quot;https://developer.mozilla.org/en-US/docs/Web/HTML/Element/option&quot;&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;&amp;lt;option&amp;gt;&lt;/code&gt;&lt;/a&gt; in our dropdown. When the user changes their selection, the corresponding value is passed to &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;handleUnitChange&lt;/code&gt; and stored as a floating point number. This way, we keep our &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;unit&lt;/code&gt; as a number throughout so it is ready to be passed to other functions without any coercion.&lt;/p&gt;

    &lt;p&gt;However, if we try to do the same thing with an &lt;a href=&quot;https://developer.mozilla.org/en-US/docs/Web/HTML/Element/input&quot;&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;&amp;lt;input type=&quot;text&quot;&amp;gt;&lt;/code&gt;&lt;/a&gt;, we quickly run into trouble:&lt;/p&gt;

    &lt;div class=&quot;language-jsx highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;kd&quot;&gt;const&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;Form&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;()&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;kd&quot;&gt;const&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;unit&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;setUnit&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;useState&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;

  &lt;span class=&quot;kd&quot;&gt;const&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;handleUnitChange&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;({&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;target&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;value&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;}})&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;nx&quot;&gt;setUnit&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;parseFloat&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;value&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;));&lt;/span&gt;
  &lt;span class=&quot;p&quot;&gt;};&lt;/span&gt;

  &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;input&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;type&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;text&quot;&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;value&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;si&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;unit&lt;/span&gt;&lt;span class=&quot;si&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;onChange&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;si&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;handleUnitChange&lt;/span&gt;&lt;span class=&quot;si&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;/&amp;gt;&lt;/span&gt;
  &lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;};&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;    &lt;/div&gt;

    &lt;p&gt;What happens when the user tries to empty the field (e.g. perhaps to enter a new value from scratch)? &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;handleUnitChange&lt;/code&gt; will fire with a value of an empty string:&lt;/p&gt;

    &lt;div class=&quot;language-javascript highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;o&quot;&gt;&amp;gt;&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;parseFloat&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&quot;&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;kc&quot;&gt;NaN&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;    &lt;/div&gt;

    &lt;p&gt;Perhaps then we try to avoid storing &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;NaN&lt;/code&gt; and instead set &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;unit&lt;/code&gt; to be &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;undefined&lt;/code&gt; or &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;null&lt;/code&gt; if it is &lt;a href=&quot;https://developer.mozilla.org/en-US/docs/Glossary/Falsy&quot;&gt;falsy&lt;/a&gt;?&lt;/p&gt;

    &lt;div class=&quot;language-javascript highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;kd&quot;&gt;const&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;handleUnitChange&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;({&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;target&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;value&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;}})&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;nx&quot;&gt;setUnit&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;value&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;?&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;parseFloat&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;value&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;kc&quot;&gt;undefined&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
  &lt;span class=&quot;c1&quot;&gt;// or setUnit(value ? parseFloat(value) : null);&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;    &lt;/div&gt;

    &lt;p&gt;If we do this, when the user clears the field, React will log a warning that we’re switching a component from being &lt;a href=&quot;https://reactjs.org/docs/forms.html#controlled-components&quot;&gt;controlled&lt;/a&gt; to being &lt;a href=&quot;https://reactjs.org/docs/uncontrolled-components.html&quot;&gt;uncontrolled&lt;/a&gt; because setting the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;value&lt;/code&gt; of a field to &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;undefined&lt;/code&gt; or &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;null&lt;/code&gt; has a special meaning: it tells React this field is now uncontrolled.&lt;/p&gt;

    &lt;p&gt;Given it is totally valid for the user to enter an empty value, we realised we needed to separate our concerns: use strings for the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;input&lt;/code&gt; value but convert the value to a number when we pass it elsewhere in our application.&lt;/p&gt;

    &lt;div class=&quot;language-jsx highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;kd&quot;&gt;const&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;Form&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;()&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;kd&quot;&gt;const&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;unit&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;setUnit&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;useState&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;

  &lt;span class=&quot;kd&quot;&gt;const&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;handleUnitChange&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;({&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;target&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;value&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;}})&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;nx&quot;&gt;setUnit&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;value&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
  &lt;span class=&quot;p&quot;&gt;};&lt;/span&gt;

  &lt;span class=&quot;nx&quot;&gt;doSomeCalculation&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;parseFloat&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;unit&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;));&lt;/span&gt;

  &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;input&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;type&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;text&quot;&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;value&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;si&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;unit&lt;/span&gt;&lt;span class=&quot;si&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;onChange&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;si&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;handleUnitChange&lt;/span&gt;&lt;span class=&quot;si&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;/&amp;gt;&lt;/span&gt;
  &lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;};&lt;/span&gt;

&lt;span class=&quot;kd&quot;&gt;const&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;doSomeCalculation&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;unit&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;Number&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;isNaN&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;unit&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;))&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

  &lt;span class=&quot;c1&quot;&gt;// ...&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;};&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;    &lt;/div&gt;

    &lt;p&gt;Using an &lt;a href=&quot;https://developer.mozilla.org/en-US/docs/Web/HTML/Element/input/number&quot;&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;&amp;lt;input type=&quot;number&quot;&amp;gt;&lt;/code&gt;&lt;/a&gt; helps a little with this as it prevents non-numeric inputs firing the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;onChange&lt;/code&gt; event but it seemed cleaner to deal with the problem in our logic than rely on browser behaviour.&lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;The second project Chris and I worked on involved a &lt;a href=&quot;https://www.python.org&quot;&gt;Python&lt;/a&gt; API written with &lt;a href=&quot;https://flask.palletsprojects.com/en/1.1.x/&quot;&gt;Flask&lt;/a&gt;. Getting things running locally was quite a pain with my attempts to use &lt;a href=&quot;https://pip.pypa.io/en/stable/&quot;&gt;pip&lt;/a&gt; to install &lt;a href=&quot;https://numpy.org&quot;&gt;NumPy&lt;/a&gt; ending in compilation errors. In the end, switching to the previous stable &lt;a href=&quot;https://www.python.org/downloads/release/python-386/&quot;&gt;Python release of 3.8&lt;/a&gt; (rather than the latest of &lt;a href=&quot;https://www.python.org/downloads/release/python-390/&quot;&gt;3.9&lt;/a&gt;) using &lt;a href=&quot;https://github.com/pyenv/pyenv&quot;&gt;pyenv&lt;/a&gt; solved my problems.&lt;/p&gt;

    &lt;p&gt;However, I was left with a bit of a mess of installed &lt;a href=&quot;https://brew.sh&quot;&gt;Homebrew&lt;/a&gt; formulae and wanted to clean things up. Thankfully, I found &lt;a href=&quot;https://superuser.com/a/1509213&quot;&gt;an answer on the macOS Super User&lt;/a&gt; with a great tip for removing unused Homebrew dependencies:&lt;/p&gt;

    &lt;div class=&quot;language-console highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;gp&quot;&gt;$&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;brew bundle dump
&lt;span class=&quot;gp&quot;&gt;$&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;brew bundle &lt;span class=&quot;nt&quot;&gt;--force&lt;/span&gt; cleanup
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;    &lt;/div&gt;

    &lt;p&gt;This will first dump all explicitly installed formulae and &lt;a href=&quot;https://github.com/Homebrew/homebrew-cask&quot;&gt;casks&lt;/a&gt; into a &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Brewfile&lt;/code&gt; (which you could then edit) and then uninstall anything no longer required.&lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;I ended up in a similar mess after installing &lt;a href=&quot;https://developer.apple.com/xcode/&quot;&gt;Xcode&lt;/a&gt; and &lt;a href=&quot;https://docs.brew.sh/Manpage#doctor-options&quot;&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;brew doctor&lt;/code&gt;&lt;/a&gt; reported that I had out-of-date Command Line Tools.&lt;/p&gt;

    &lt;p&gt;Attempting to use &lt;a href=&quot;https://developer.apple.com/library/archive/technotes/tn2339/_index.html#//apple_ref/doc/uid/DTS40014588-CH1-DOWNLOADING_COMMAND_LINE_TOOLS_IS_NOT_AVAILABLE_IN_XCODE_FOR_MACOS_10_9__HOW_CAN_I_INSTALL_THEM_ON_MY_MACHINE_&quot;&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;xcode-select --install&lt;/code&gt;&lt;/a&gt; failed with a strange error about the software update not being found and Software Update itself couldn’t install the update properly.&lt;/p&gt;

    &lt;p&gt;It turns out that Xcode &lt;em&gt;includes&lt;/em&gt; the Command Lines Tools itself even though I had a previous installation in &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;/Library/Developers/CommandLineTools&lt;/code&gt; installed via &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;xcode-select&lt;/code&gt;. I ended up completely removing both Xcode and the Command Line Tools and reinstalling the latter before Software Update could successfully pick up the right update.&lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;&lt;a href=&quot;https://www.urbanautomaton.com&quot;&gt;Simon&lt;/a&gt; wrote a truly excellent blog post about Rails autoloading with &lt;a href=&quot;https://github.com/fxn/zeitwerk&quot;&gt;Zeitwerk&lt;/a&gt; titled “&lt;a href=&quot;https://www.urbanautomaton.com/blog/2020/11/04/rails-autoloading-heaven/&quot;&gt;Rails autoloading — now it works, and how!&lt;/a&gt;”. This is a follow-up to his &lt;a href=&quot;https://www.urbanautomaton.com/blog/2013/08/27/rails-autoloading-hell/&quot;&gt;2013 post “Rails autoloading — how it works, and when it doesn’t”&lt;/a&gt; to which I often found myself referring.&lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;Following &lt;a href=&quot;/2020/11/01/weeknotes-53/&quot;&gt;last week’s need for earplugs&lt;/a&gt;, I lost some hearing in my left ear. I returned from a trip to the pharmacy with some &lt;a href=&quot;https://en.wikipedia.org/wiki/Hydrogen_peroxide_-_urea&quot;&gt;urea hydrogen peroxide&lt;/a&gt; and watched the &lt;a href=&quot;https://thegreatbritishbakeoff.co.uk&quot;&gt;Great British Bake Off&lt;/a&gt; sideways with the deeply unsettling sensation of fizzing in my ear canal.&lt;/p&gt;

    &lt;p&gt;I woke in the small hours of Thursday morning to find the room uncontrollably spinning around me. I staggered to the bathroom where I discovered I could no longer get my eyes to focus straight ahead without them constantly drifting to my right. With E’s help, I managed to speak to a doctor on the phone and go in to our local GP’s surgery to see someone in person that morning.&lt;/p&gt;

    &lt;p&gt;After waiting outside, the doctor greeted me at the door wrapped in plastic, said that they needed to “fire [their] gun” (a thermometer) at me and instructed me not to touch anything as we walked to a consultation room.&lt;/p&gt;

    &lt;p&gt;Thankfully, it’s a viral ear infection that’ll clear up by itself in a matter of days.&lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;Now that C is confident enough walking, he happily roams our flat looking for things to prod, pull or combine in some way. We’ve already lost three bowls to his curiosity and I’ve had to rewire one plug. It also means that I took the step of lowering the mattress in his cot now that he more closely resembles the IKEA cartoon for “toddler” than the one for “baby”.&lt;/p&gt;
  &lt;/li&gt;
&lt;/ul&gt;
</content>
</entry><entry>
  <title type="html">Weeknotes 53</title>
  <link href="https://mudge.name/2020/11/01/weeknotes-53/"/>
  <id>https://mudge.name/2020/11/01/weeknotes-53/</id>
  <published>2020-11-01T14:35:00+00:00</published>
  <updated>2020-11-01T14:35:00+00:00</updated>
  <summary type="html">An unwelcome return, low spirits and removing a special case.</summary>
  <content type="html">&lt;ul&gt;
  &lt;li&gt;
    &lt;p&gt;A year ago—one week before &lt;a href=&quot;/2020/09/06/weeknotes-45/&quot;&gt;C was born&lt;/a&gt;—wearing rubber gloves and a face mask, I pulled out all of our kitchen appliances and stuffed every crevice with steel wool. I covered the wool with electrical tape before carefully arranging boxes of mouse poison given to me by the council along the walls.&lt;/p&gt;

    &lt;p&gt;Last night, just as E and I were falling asleep, there was the sound of scratching mere metres from our heads. As soon as we moved to investigate, it stopped and I spent the rest of the night waiting to hear something else other than the pounding of my own heart.&lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;With that and the &lt;a href=&quot;https://www.gov.uk/guidance/new-national-restrictions-from-5-november&quot;&gt;new national restrictions in the UK from 5th November&lt;/a&gt;, my level of &lt;a href=&quot;https://github.com/computationclub/computationclub.github.io/wiki/Types-and-Programming-Languages-Chapter-13-References#pub&quot;&gt;“cheersy-cheers” spirit&lt;/a&gt; is at an all-time low.&lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;&lt;a href=&quot;https://youtu.be/wbfu39l0kxg&quot;&gt;Brian David Gilbert’s “Earn $20K EVERY MONTH by being your own boss”&lt;/a&gt; helped a little though.&lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;I’m working on a publishing platform that has a lot of special cases for a single publication. That publication is now being handled by another codebase so I have the opportunity to remove it and simplify a lot of the existing software. However, removing it cleanly without risking breaking other functionality has proven very difficult.&lt;/p&gt;

    &lt;p&gt;I tried a few different strategies: trying to remove all of the view-related code first then trying to come at it by removing a whole feature and all its related dependencies at a time. However, I kept coming unstuck: finding myself having deleted a lot of code but with far too many tests failing and not enough confidence I was on the right track so I’d &lt;a href=&quot;https://git-scm.com/docs/git-reset#Documentation/git-reset.txt---hard&quot;&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;git reset --hard&lt;/code&gt;&lt;/a&gt; and abandon the approach.&lt;/p&gt;

    &lt;p&gt;On Friday, I finally succeeded by being far more conservative in the amount I deleted at any one time. Rather than deleting an entire feature, I’d pick a small aspect of it (say, an admin-only view) and remove that, ensuring all the tests still passed before removing the next slice.&lt;/p&gt;

    &lt;p&gt;The result?&lt;/p&gt;

    &lt;blockquote&gt;
      &lt;p&gt;39 additions and 2,786 deletions.&lt;/p&gt;
    &lt;/blockquote&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;Time to find some ear plugs.&lt;/p&gt;
  &lt;/li&gt;
&lt;/ul&gt;
</content>
</entry><entry>
  <title type="html">Weeknotes 52</title>
  <link href="https://mudge.name/2020/10/29/weeknotes-52/"/>
  <id>https://mudge.name/2020/10/29/weeknotes-52/</id>
  <published>2020-10-29T10:32:00+00:00</published>
  <updated>2020-10-29T10:32:00+00:00</updated>
  <summary type="html">A year of weeknotes, inconsequential quests, kernel extensions, using Terraform to manage multiple AWS accounts at once, testing feature flags and the secret fantasy of a future self.</summary>
  <content type="html">&lt;ul&gt;
  &lt;li&gt;
    &lt;p&gt;It has been a year since my &lt;a href=&quot;/2019/11/04/weeknotes-1/&quot;&gt;first weeknotes&lt;/a&gt;.&lt;/p&gt;

    &lt;p&gt;I still haven’t finished that blog post on &lt;a href=&quot;https://reactjs.org/docs/hooks-intro.html&quot;&gt;React Hooks&lt;/a&gt;.&lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;Since reading &lt;a href=&quot;https://hotpodnews.com/the-problem-with-the-inconsequential-quest/&quot;&gt;Caroline Crampton’s “The Problem with the Inconsequential Quest”&lt;/a&gt;, I’ve found it hard to listen to recommended podcasts that fit the description, e.g. &lt;a href=&quot;https://underunderstood.com/podcast/episode/amazons-next-top-model/&quot;&gt;Underunderstood’s “Amazon’s Next Top Model”&lt;/a&gt;:&lt;/p&gt;

    &lt;blockquote&gt;
      &lt;p&gt;I’m sure readers will be very familiar with the form: a host chooses something that is of seemingly little importance and investigates it with a thoroughness and journalistic rigour that seems completely out of proportion to the original question. The resulting episode or series documents this journey in detail and ultimately reveals a conclusion that surprises and delights listeners.&lt;/p&gt;
    &lt;/blockquote&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;Instead of writing these notes on Sunday while C napped, I attempted to remap the extra buttons on &lt;a href=&quot;https://www.kensington.com/en-gb/p/products/control/trackballs/expert-mouse-wireless-trackball/&quot;&gt;my trackball&lt;/a&gt; to go back and forwards in my browser. I tried &lt;a href=&quot;https://www.usboverdrive.com&quot;&gt;USB Overdrive&lt;/a&gt;, &lt;a href=&quot;http://plentycom.jp/en/steermouse/&quot;&gt;SteerMouse&lt;/a&gt; and &lt;a href=&quot;https://sensible-side-buttons.archagon.net&quot;&gt;Sensible Side Buttons&lt;/a&gt; but balked at the need for &lt;a href=&quot;https://developer.apple.com/support/kernel-extensions/&quot;&gt;kernel extensions&lt;/a&gt; (and sadly Sensible Side Buttons doesn’t work because one of the buttons is interpreted as a middle-click).&lt;/p&gt;

    &lt;p&gt;I spent an hour trying to clean up the various bits of software I’d tried as &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;kextstat&lt;/code&gt; revealed USB Overdrive’s kernel extension lingered after uninstallation. In the end, I had to use &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;kextcache&lt;/code&gt; to clear “staged” extensions in &lt;a href=&quot;https://support.apple.com/en-gb/HT201314&quot;&gt;Recovery Mode&lt;/a&gt;.&lt;/p&gt;

    &lt;p&gt;I’ve actually made things worse than when I installed the &lt;a href=&quot;https://www.kensington.com/en-gb/software/trackballworks-customization-software/&quot;&gt;official Kensington TrackballWorks driver&lt;/a&gt;.&lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;I’ve been doing quite a lot of work with &lt;a href=&quot;https://aws.amazon.com&quot;&gt;Amazon Web Services&lt;/a&gt; for a client and was very grateful to have automated it with &lt;a href=&quot;https://www.terraform.io&quot;&gt;Hashicorp Terraform&lt;/a&gt; as I needed to quickly destroy and recreate &lt;a href=&quot;https://aws.amazon.com/s3/&quot;&gt;S3&lt;/a&gt; buckets connected to &lt;a href=&quot;https://aws.amazon.com/cloudfront/&quot;&gt;CloudFront&lt;/a&gt; distributions with &lt;a href=&quot;https://aws.amazon.com/certificate-manager/&quot;&gt;ACM&lt;/a&gt; certificates.&lt;/p&gt;

    &lt;p&gt;I’ve found it useful to manage both our production and staging environment using a single Terraform repository by leveraging &lt;a href=&quot;https://aws.amazon.com/organizations/&quot;&gt;AWS Organizations&lt;/a&gt; and the ability for an administrator in the root organization to temporarily assume the role of an administrator in any of the member accounts. By default, &lt;a href=&quot;https://docs.aws.amazon.com/organizations/latest/userguide/orgs_manage_accounts_access.html&quot;&gt;AWS will create a role called &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;OrganizationAccountAccessRole&lt;/code&gt;&lt;/a&gt; to do exactly this.&lt;/p&gt;

    &lt;p&gt;To manage &lt;a href=&quot;https://www.terraform.io/docs/configuration/resources.html&quot;&gt;Terraform resources&lt;/a&gt; in different AWS accounts we can then use an “&lt;a href=&quot;https://registry.terraform.io/providers/hashicorp/aws/latest/docs#assume-role&quot;&gt;Assume Role&lt;/a&gt;” block in an &lt;a href=&quot;https://registry.terraform.io/providers/hashicorp/aws/latest/docs#assume-role&quot;&gt;AWS provider&lt;/a&gt; like so:&lt;/p&gt;

    &lt;div class=&quot;language-terraform highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;k&quot;&gt;provider&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;aws&quot;&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;nx&quot;&gt;alias&lt;/span&gt;  &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;root&quot;&lt;/span&gt;
  &lt;span class=&quot;nx&quot;&gt;region&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;eu-west-1&quot;&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

&lt;span class=&quot;k&quot;&gt;provider&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;aws&quot;&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;nx&quot;&gt;alias&lt;/span&gt;  &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;production&quot;&lt;/span&gt;
  &lt;span class=&quot;nx&quot;&gt;region&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;eu-west-1&quot;&lt;/span&gt;
  &lt;span class=&quot;nx&quot;&gt;assume_role&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;nx&quot;&gt;role_arn&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;arn:aws:iam::PRODUCTION-ID:role/OrganizationAccountAccessRole&quot;&lt;/span&gt;
  &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

&lt;span class=&quot;k&quot;&gt;provider&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;aws&quot;&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;nx&quot;&gt;alias&lt;/span&gt;  &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;staging&quot;&lt;/span&gt;
  &lt;span class=&quot;nx&quot;&gt;region&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;eu-west-1&quot;&lt;/span&gt;
  &lt;span class=&quot;nx&quot;&gt;assume_role&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;nx&quot;&gt;role_arn&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;arn:aws:iam::STAGING-ID:role/OrganizationAccountAccessRole&quot;&lt;/span&gt;
  &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;    &lt;/div&gt;

    &lt;p&gt;Then you specify the appropriate provider alias in each resource, e.g.&lt;/p&gt;

    &lt;div class=&quot;language-terraform highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;k&quot;&gt;resource&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;aws_s3_bucket&quot;&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;staging&quot;&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;k&quot;&gt;provider&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;aws&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;staging&lt;/span&gt;
  &lt;span class=&quot;nx&quot;&gt;bucket&lt;/span&gt;   &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;bucket-staging&quot;&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

&lt;span class=&quot;k&quot;&gt;resource&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;aws_s3_bucket&quot;&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;production&quot;&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;k&quot;&gt;provider&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;aws&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;production&lt;/span&gt;
  &lt;span class=&quot;nx&quot;&gt;bucket&lt;/span&gt;   &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;bucket-production&quot;&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;    &lt;/div&gt;

    &lt;p&gt;You can use something like &lt;a href=&quot;https://github.com/99designs/aws-vault&quot;&gt;AWS Vault&lt;/a&gt; to run your Terraform commands as an administrator in your root account and it’ll assume the appropriate role for each resource automatically:&lt;/p&gt;

    &lt;div class=&quot;language-console highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;gp&quot;&gt;$&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;aws-vault &lt;span class=&quot;nb&quot;&gt;exec &lt;/span&gt;root-account-administrator &lt;span class=&quot;nt&quot;&gt;--&lt;/span&gt; terraform plan
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;    &lt;/div&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;I’ve been working on a new feature in a &lt;a href=&quot;https://rubyonrails.org&quot;&gt;Rails&lt;/a&gt; application and needed to hide it behind a &lt;a href=&quot;https://martinfowler.com/articles/feature-toggles.html&quot;&gt;feature flag&lt;/a&gt; so I wasn’t blocking other features that needed deploying. Now that I have two possible sets of behaviour in various parts of the application, I wanted to test them both with &lt;a href=&quot;https://rspec.info&quot;&gt;RSpec&lt;/a&gt;.&lt;/p&gt;

    &lt;p&gt;The feature flag lives in a bit of &lt;a href=&quot;https://guides.rubyonrails.org/configuring.html#custom-configuration&quot;&gt;custom Rails configuration&lt;/a&gt;, e.g.&lt;/p&gt;

    &lt;div class=&quot;language-ruby highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;no&quot;&gt;Rails&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;configuration&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;cool_new_feature&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;ENV&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;'COOL_NEW_FEATURE'&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;    &lt;/div&gt;

    &lt;p&gt;Then various parts of the application switch on the value of that flag:&lt;/p&gt;

    &lt;div class=&quot;language-ruby highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;Rails&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;configuration&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;cool_new_feature&lt;/span&gt;
  &lt;span class=&quot;c1&quot;&gt;# do something cool&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;else&lt;/span&gt;
  &lt;span class=&quot;c1&quot;&gt;# do something less cool&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;    &lt;/div&gt;

    &lt;p&gt;I wanted to change the value of that configuration before running some examples and reset it back to its original value after the test, e.g.&lt;/p&gt;

    &lt;div class=&quot;language-ruby highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;n&quot;&gt;context&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;'with the cool new feature enabled'&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;do&lt;/span&gt;
  &lt;span class=&quot;n&quot;&gt;around&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;do&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;|&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;example&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;|&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;original_value&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;Rails&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;configuration&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;cool_new_feature&lt;/span&gt;
    &lt;span class=&quot;no&quot;&gt;Rails&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;configuration&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;cool_new_feature&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;kp&quot;&gt;true&lt;/span&gt;

    &lt;span class=&quot;n&quot;&gt;example&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;run&lt;/span&gt;

    &lt;span class=&quot;no&quot;&gt;Rails&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;configuration&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;cool_new_feature&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;original_value&lt;/span&gt;
  &lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;

  &lt;span class=&quot;c1&quot;&gt;# Examples here...&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;    &lt;/div&gt;

    &lt;p&gt;However, adding this across several places in the test suite seemed rather noisy but we can leverage &lt;a href=&quot;https://relishapp.com/rspec/rspec-core/v/3-9/docs/metadata/user-defined-metadata&quot;&gt;user-defined metadata&lt;/a&gt; with &lt;a href=&quot;https://relishapp.com/rspec/rspec-core/v/3-9/docs/hooks/filters&quot;&gt;hook filtering&lt;/a&gt; to clean this up:&lt;/p&gt;

    &lt;div class=&quot;language-ruby highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;c1&quot;&gt;# in spec/rails_helper.rb&lt;/span&gt;
&lt;span class=&quot;no&quot;&gt;RSpec&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;configure&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;do&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;|&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;config&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;|&lt;/span&gt;
  &lt;span class=&quot;c1&quot;&gt;# ...&lt;/span&gt;

  &lt;span class=&quot;n&quot;&gt;config&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;around&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;ss&quot;&gt;:each&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;ss&quot;&gt;:cool_new_feature&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;do&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;|&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;example&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;|&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;original_value&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;Rails&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;configuration&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;cool_new_feature&lt;/span&gt;
    &lt;span class=&quot;no&quot;&gt;Rails&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;configuration&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;cool_new_feature&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;example&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;metadata&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;ss&quot;&gt;:cool_new_feature&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt;

    &lt;span class=&quot;n&quot;&gt;example&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;run&lt;/span&gt;

    &lt;span class=&quot;no&quot;&gt;Rails&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;configuration&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;cool_new_feature&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;original_value&lt;/span&gt;
  &lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;    &lt;/div&gt;

    &lt;p&gt;Then in our specs, we simply tag examples with the appropriate metadata:&lt;/p&gt;

    &lt;div class=&quot;language-ruby highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;n&quot;&gt;context&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;'with cool new feature enabled'&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;ss&quot;&gt;cool_new_feature: &lt;/span&gt;&lt;span class=&quot;kp&quot;&gt;true&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;do&lt;/span&gt;
  &lt;span class=&quot;c1&quot;&gt;# ...&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;

&lt;span class=&quot;n&quot;&gt;context&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;'with cool new feature disabled'&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;ss&quot;&gt;cool_new_feature: &lt;/span&gt;&lt;span class=&quot;kp&quot;&gt;false&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;do&lt;/span&gt;
  &lt;span class=&quot;c1&quot;&gt;# ...&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;    &lt;/div&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;I haven’t had any epiphanies after writing these notes every week for a year but I still think about the &lt;a href=&quot;/2020/02/10/weeknotes-15/&quot;&gt;Paul Murray quote I shared in weeknotes 15&lt;/a&gt;:&lt;/p&gt;

    &lt;blockquote&gt;
      &lt;p&gt;Although technology has the capability now to record entire lifetimes, meaning that every moment may be pulled from the foaming sea of oblivion to the dry land of perfect recall, the mythic power of the photograph nevertheless relates to the future, and not to the past. Every recording conceals the secret fantasy of a future self who will observe it; this future self is himself the simulacrum, the persona ficta. He exists beyond time, beyond action, beyond need; his only function is to witness the continuum of the past, as he might observe the steps that brought him to godhood. Through this fantasy, time is transformed from the condition of loss into a commodity that may be acquired and stockpiled; rather than disappear ceaselessly into the past, life accumulates, each moment becoming a unit of a total self that is the culmination of our experiences in a way that we—biological composites who profligately shed our cells, our memories and our possessions—can never be.&lt;/p&gt;
    &lt;/blockquote&gt;
  &lt;/li&gt;
&lt;/ul&gt;
</content>
</entry><entry>
  <title type="html">Weeknotes 51</title>
  <link href="https://mudge.name/2020/10/18/weeknotes-51/"/>
  <id>https://mudge.name/2020/10/18/weeknotes-51/</id>
  <published>2020-10-18T09:33:00+00:00</published>
  <updated>2020-10-18T09:33:00+00:00</updated>
  <summary type="html">Pizza crusts, London Computation Club wiki, what to do with an inherited codebase and collective disbelief.</summary>
  <content type="html">&lt;ul&gt;
  &lt;li&gt;
    &lt;p&gt;&lt;a href=&quot;/2020/09/27/weeknotes-48/&quot;&gt;Two weeks ago&lt;/a&gt; I wrote about choosing between two 12” pizzas and one 18” pizza by comparing their surface area but &lt;a href=&quot;https://tomstu.art&quot;&gt;Tom&lt;/a&gt; pointed out this only works if you care about sheer quantity of pizza.&lt;/p&gt;

    &lt;p&gt;If you care more about pizza crust (especially when &lt;a href=&quot;https://www.theguardian.com/global-development/2014/mar/02/pizza-hut-2880-calorie-monstrosity-worlds-burgeoning-food-crisis&quot;&gt;studded with burgers&lt;/a&gt;), you should instead compare the circumference:&lt;/p&gt;

    &lt;ul&gt;
      &lt;li&gt;“&lt;a href=&quot;https://www.wolframalpha.com/input/?i=Circumference+of+12+inch+diameter+circle&quot;&gt;circumference of 12 inch diameter circle&lt;/a&gt;” = 37.7 inches * 2 = 75.4 inches&lt;/li&gt;
      &lt;li&gt;“&lt;a href=&quot;https://www.wolframalpha.com/input/?i=Circumference+of+18+inch+diameter+circle&quot;&gt;circumference of 18 inch diameter circle&lt;/a&gt;” = 56.6 inches&lt;/li&gt;
    &lt;/ul&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;I added &lt;a href=&quot;https://github.com/computationclub/computationclub.github.io/wiki/iCE2Tetris&quot;&gt;a page about Leo’s astounding “iCE2Tetris” project&lt;/a&gt; to the &lt;a href=&quot;https://github.com/computationclub/computationclub.github.io/wiki&quot;&gt;London Computation Club wiki&lt;/a&gt; this week.&lt;/p&gt;

    &lt;p&gt;According to the git history, I have made 637 changes to the wiki since 2015, adding 8,228 lines and removing 1,911.&lt;/p&gt;

    &lt;p&gt;While I’d go &lt;a href=&quot;/2014/11/26/data-structures-as-functions/&quot; title=&quot;Data Structures as Functions, published in 2014&quot;&gt;years&lt;/a&gt; &lt;a href=&quot;/2016/03/26/the-difficulty-with-the-success-of-software-development-teams/&quot; title=&quot;What Is a Successful Software Development Team?, published in 2016&quot;&gt;between&lt;/a&gt; &lt;a href=&quot;/2019/01/02/cross-compiling-rust-for-a-raspberry-pi-on-travis-ci/&quot; title=&quot;Cross-Compiling Rust for a Raspberry Pi on Travis CI, published in 2019&quot;&gt;posts&lt;/a&gt; on this site, you could often find me writing up &lt;a href=&quot;https://github.com/computationclub/computationclub.github.io/wiki#meetings&quot;&gt;club meetings&lt;/a&gt; every few weeks.&lt;/p&gt;

    &lt;p&gt;The write-up I remember most is “&lt;a href=&quot;https://github.com/computationclub/computationclub.github.io/wiki/Types-and-Programming-Languages-Chapter-15-Subtyping-–-Part-1&quot;&gt;Types and Programming Languages Chapter 15: Subtyping&lt;/a&gt;” as I left the meeting defeated and burnt out, ready to give up on the club altogether but the act of writing it up clarified my understanding and restored my energy for returning to the club.&lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;I recently inherited a codebase and am the sole developer working on it. This means I’m in the rare position to make whatever change I see fit to make it easier to work on and so I decided to start with dependency updates.&lt;/p&gt;

    &lt;p&gt;Having seen it used before, I decided to &lt;a href=&quot;https://docs.github.com/en/free-pro-team@latest/github/administering-a-repository/enabling-and-disabling-version-updates&quot;&gt;set up GitHub Dependabot&lt;/a&gt;. While finding the right documentation was tricky given that &lt;a href=&quot;https://github.blog/2020-06-01-keep-all-your-packages-up-to-date-with-dependabot/&quot;&gt;Dependabot is being integrated into GitHub itself&lt;/a&gt;, once I had it set up I was pleasantly surprised how well it worked: raising PRs for upgrades, automatically rebasing them and even detecting when multiple dependencies need to be updated in tandem.&lt;/p&gt;

    &lt;p&gt;I have mixed feelings about updating dependencies now being an essential part of being a web developer but if you’re going to have to do it anyway, you might as well automate it.&lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;The other change I’d like to make is to the code style itself: the formatting, linting, etc. Running &lt;a href=&quot;https://github.com/rubocop-hq/rubocop&quot;&gt;RuboCop&lt;/a&gt; with the project’s current configuration produces over 1,000 violations so I needed some advice on how to approach this in a manageable way.&lt;/p&gt;

    &lt;p&gt;Thankfully, &lt;a href=&quot;https://youtu.be/Cd1aLLwTNKc&quot;&gt;Scott Matthewman’s “Calling in the cops”&lt;/a&gt; is an excellent talk about this exact topic: how to sensibly approach linting a legacy codebase.&lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;This led me to discover the &lt;a href=&quot;https://github.com/rubocop-hq/rubocop-rails/issues/195&quot;&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Rails/FilePath&lt;/code&gt; cop prefers using forward slashes in paths&lt;/a&gt; and share in the &lt;a href=&quot;https://twitter.com/tenderlove/status/842064491936280576&quot;&gt;collective disbelief that Ruby’s &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;File.join&lt;/code&gt; does not change separators based on platform&lt;/a&gt;.&lt;/p&gt;
  &lt;/li&gt;
&lt;/ul&gt;
</content>
</entry><entry>
  <title type="html">Weeknotes 50</title>
  <link href="https://mudge.name/2020/10/11/weeknotes-50/"/>
  <id>https://mudge.name/2020/10/11/weeknotes-50/</id>
  <published>2020-10-11T12:02:00+00:00</published>
  <updated>2020-10-11T12:02:00+00:00</updated>
  <summary type="html">Baby-proofing, Richard Osman’s role in my life, carrying pliers, zeitgeist marketing, ergonomic experiments and escaping from Hades.</summary>
  <content type="html">&lt;ul&gt;
  &lt;li&gt;
    &lt;p&gt;After realising our &lt;a href=&quot;/2020/06/07/weeknotes-32/&quot;&gt;DNS sinkhole&lt;/a&gt; was causing the &lt;a href=&quot;https://store.playstation.com/en-gb/product/EP4338-CUSA00122_00-IPLAYER0FULL0000&quot;&gt;BBC iPlayer app for the PlayStation 4&lt;/a&gt; to crash (presumably because some essential telemetry was being blocked), I switched it over to use &lt;a href=&quot;https://1.1.1.1/dns/&quot;&gt;Cloudflare’s 1.1.1.1&lt;/a&gt; and discovered two whole series of “&lt;a href=&quot;https://www.bbc.co.uk/cbeebies/shows/hey-duggee&quot;&gt;Hey Duggee&lt;/a&gt;” &lt;a href=&quot;https://www.netflix.com/gb/title/80989192&quot;&gt;not on Netflix&lt;/a&gt;. That’s my viewing for the foreseeable sorted.&lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;As C grows more confident with his toddling, his love of opening cupboards and drawers to explore their contents has also increased. I began securing certain cupboards with “child safety locks”: plastic straps secured at either end with adhesive tape.&lt;/p&gt;

    &lt;p&gt;Having placed a lock on our fridge the wrong way round, I followed the instructions to remove it: heat up the adhesive with a hair-dryer and peel it off. While that did the trick, it also melted the &lt;a href=&quot;https://en.wikipedia.org/wiki/Thermofoil&quot;&gt;thermofoil&lt;/a&gt; on our fridge door causing it to warp and peel away from the wood.&lt;/p&gt;

    &lt;p&gt;It turns out the lock is no match for C’s strength as he can easily pull the ruined door open.&lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;My mum refers to C’s teeth as “tooshy pegs”.&lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;Our evening schedule is dictated entirely by &lt;a href=&quot;https://en.wikipedia.org/wiki/Richard_Osman&quot;&gt;Richard Osman&lt;/a&gt;.&lt;/p&gt;

    &lt;p&gt;First, there’s “&lt;a href=&quot;https://www.bbc.co.uk/programmes/b00rhg2r&quot;&gt;Pointless&lt;/a&gt;” from 5:15 pm to 6 pm when C has his dinner. We switch over to BBC Two for “&lt;a href=&quot;https://www.bbc.co.uk/programmes/b094mjv0&quot;&gt;Richard Osman’s House of Games&lt;/a&gt;”, the end of which marks the beginning of C’s bedtime routine.&lt;/p&gt;

    &lt;p&gt;After a hard day, I long for the “Answer Smash” siren.&lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;Following &lt;a href=&quot;https://zerodivisionerror.neocities.org&quot;&gt;Maciej&lt;/a&gt;’s recommendation, I’ve been reading &lt;a href=&quot;https://en.wikipedia.org/wiki/The_Dark_Forest&quot;&gt;Cixin Liu’s “The Dark Forest”&lt;/a&gt;. &lt;a href=&quot;/2019/12/01/weeknotes-5/&quot;&gt;I read the previous book&lt;/a&gt; back in December last year but didn’t continue with &lt;a href=&quot;https://en.wikipedia.org/wiki/Remembrance_of_Earth%27s_Past&quot;&gt;the series&lt;/a&gt;. In looking for something new to read, I’m not sure why I overlooked this as I am thoroughly enjoying it so far.&lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;Our flat has an automatic car gate that stops working whenever it rains. As this week has been particularly wet, one of our neighbours discovered you can unlock the gate arms (so they can be opened and closed manually) by turning a small bolt.&lt;/p&gt;

    &lt;p&gt;You can now find a pair of needle-nose pliers in my coat pocket wherever I go.&lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;E and I watched “&lt;a href=&quot;https://www.imdb.com/title/tt7146812/&quot;&gt;Onward&lt;/a&gt;” last night. While she wasn’t especially moved, I cried the most I have in years.&lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;I enjoyed the first two episodes of &lt;a href=&quot;https://allconsuming.show&quot;&gt;Noah Kalina and Adam Lisagor’s “All Consuming” podcast&lt;/a&gt; reviewing new direct-to-consumer products commonly advertised on the likes of Instagram, etc.&lt;/p&gt;

    &lt;p&gt;The subject of &lt;a href=&quot;https://allconsuming.show/listen/nuggs&quot;&gt;their first episode&lt;/a&gt;, “the Tesla of chicken” &lt;a href=&quot;https://eatnuggs.com/releases&quot;&gt;NUGGS’ release notes&lt;/a&gt; are quite the read:&lt;/p&gt;

    &lt;blockquote&gt;
      &lt;ul&gt;
        &lt;li&gt;Enables a close to indistinguishable chicken-like substrate (utilizing soy and wheat proteins)&lt;/li&gt;
      &lt;/ul&gt;
    &lt;/blockquote&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;&lt;a href=&quot;https://www.bloomberg.com/opinion/articles/2020-09-07/welcome-to-your-bland-new-world-of-consumer-capitalism&quot;&gt;Ben Schott’s “Welcome to Your Bland New World”&lt;/a&gt; (via &lt;a href=&quot;https://alicebartlett.co.uk/blog/weaknotes-110&quot;&gt;Alice Bartlett&lt;/a&gt;) is another exploration of this “zeitgeist marketing”:&lt;/p&gt;

    &lt;blockquote&gt;
      &lt;p&gt;All startups seek to disrupt and disintermediate a smug status quo, or
originate and dominate an entirely new niche. But what makes a brand a
bland is duality: claiming simultaneously to be unique in product,
groundbreaking in purpose, and singular in delivery, while slavishly
obeying an identikit formula of business model, look and feel, and tone
of voice.&lt;/p&gt;
    &lt;/blockquote&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;In need of a fun game to pass the time, I read &lt;a href=&quot;https://www.eurogamer.net/articles/2020-09-17-hades-review-of-myth-and-mayhem&quot;&gt;Eurogamer.net’s review of “Hades”&lt;/a&gt; and bought it.&lt;/p&gt;

    &lt;p&gt;Having previously tried and failed to get into “&lt;a href=&quot;https://dead-cells.com&quot;&gt;Dead Cells&lt;/a&gt;”, I was nervous about another “&lt;a href=&quot;https://en.wikipedia.org/wiki/Roguelike&quot;&gt;roguelike&lt;/a&gt;” but something about it works for me. Perhaps the sense of progress with every failed run as the story and world unfolds breaks the monotony of dying over and over.&lt;/p&gt;

    &lt;p&gt;It has also inspired me to listen to &lt;a href=&quot;https://www.hadestown.com&quot;&gt;Anaïs Mitchell’s “Hadestown”&lt;/a&gt; again.&lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;While not going quite so far &lt;a href=&quot;https://blog.scottlogic.com/2020/10/09/ergo-rabbit-hole.html&quot;&gt;down the ergonomic keyboard rabbit hole as Steven Waterman&lt;/a&gt;, I did buy the wired version of &lt;a href=&quot;https://www.kensington.com/en-gb/p/products/control/trackball-products/expert-mouse-wireless-trackball/&quot;&gt;Kensington’s Expert Mouse Trackball&lt;/a&gt;. It is comically large with a 55 mm diameter trackball but if it &lt;a href=&quot;https://www.nytimes.com/wirecutter/reviews/best-trackballs/&quot;&gt;helps my ailing skeleton&lt;/a&gt;, I’m willing to try it.&lt;/p&gt;
  &lt;/li&gt;
&lt;/ul&gt;
</content>
</entry><entry>
  <title type="html">Weeknotes 49</title>
  <link href="https://mudge.name/2020/10/04/weeknotes-49/"/>
  <id>https://mudge.name/2020/10/04/weeknotes-49/</id>
  <published>2020-10-04T09:34:00+00:00</published>
  <updated>2020-10-04T09:34:00+00:00</updated>
  <summary type="html">Paracetamol suspensions, 90s music and final shaping.</summary>
  <content type="html">&lt;ul&gt;
  &lt;li&gt;
    &lt;p&gt;C had his &lt;a href=&quot;https://www.nhs.uk/conditions/vaccinations/NHS-vaccinations-and-when-to-have-them/&quot;&gt;1 year vaccinations&lt;/a&gt; which meant we had to &lt;a href=&quot;https://www.gov.uk/government/publications/menb-vaccine-and-paracetamol&quot;&gt;top him up with a cherry-flavoured paracetamol suspension&lt;/a&gt; to keep his fever down. At the same time, he was fighting off his first cold caught from nursery which made for a grumpy, ruddy-cheeked boy.&lt;/p&gt;

    &lt;p&gt;I caught C’s cold so my &lt;a href=&quot;https://www.lemsip.co.uk&quot;&gt;Lemsip&lt;/a&gt; intake has sky-rocketed.&lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;E and I began watching HBO’s “&lt;a href=&quot;https://www.hbo.com/watchmen&quot;&gt;Watchmen&lt;/a&gt;” TV series after &lt;a href=&quot;https://www.imdb.com/title/tt7049682/awards&quot;&gt;its success at the Emmy Awards&lt;/a&gt;. E has neither read the &lt;a href=&quot;https://en.wikipedia.org/wiki/Watchmen&quot;&gt;comic&lt;/a&gt; nor seen the &lt;a href=&quot;https://www.imdb.com/title/tt0409459/&quot;&gt;2009 Zack Snyder film&lt;/a&gt; so I attempted to summarise the key plot points.&lt;/p&gt;

    &lt;p&gt;It got tricky when I had to explain the squid.&lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;We celebrated our third wedding anniversary by exchanging gifts, making an extravagant dinner and indulging in various indie classics from the 90s including &lt;a href=&quot;https://en.wikipedia.org/wiki/Version_2.0&quot;&gt;Garbage’s “Version 2.0”&lt;/a&gt;, &lt;a href=&quot;https://en.wikipedia.org/wiki/Without_You_I%27m_Nothing_(Placebo_album)&quot;&gt;Placebo’s “Without You I’m Nothing”&lt;/a&gt; and &lt;a href=&quot;https://en.wikipedia.org/wiki/If_You%27re_Feeling_Sinister&quot;&gt;Belle and Sebastian’s “If You’re Feeling Sinister”&lt;/a&gt;.&lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;&lt;a href=&quot;https://blog.chrislowis.co.uk/2020/10/03/weeknotes.html&quot;&gt;I worked on a project with Chris&lt;/a&gt; all week, mostly pair-programming via &lt;a href=&quot;https://tuple.app&quot;&gt;Tuple&lt;/a&gt;. As my other project work is quite solitary, this was a welcome change. It was great to get a chance to work with Chris for the first time after years of being involved with &lt;a href=&quot;https://london.computation.club&quot;&gt;London Computation Club&lt;/a&gt; and &lt;a href=&quot;https://lrug.org&quot;&gt;London Ruby User Group&lt;/a&gt; together.&lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;Despite being put off Netflix’s “&lt;a href=&quot;https://thechefshow.com&quot;&gt;The Chef Show&lt;/a&gt;” by its first episode with Gwyneth Paltrow, I watched the episode with Chad Robertson and Chris Bianco at &lt;a href=&quot;https://tartinebakery.com&quot;&gt;Tartine Bakery&lt;/a&gt; and am warming to it a little. I assumed it was yet another entry in the genre of “&lt;a href=&quot;/2020/03/08/weeknotes-19/&quot;&gt;famous chef and their celebrity friends&lt;/a&gt;” but, in this recent episode, Favreau seems genuinely humble and keen to learn from others.&lt;/p&gt;

    &lt;p&gt;It also gave me a detailed look at Robertson’s final shaping technique for his country loaf recipe which prompted me to give it a try:&lt;/p&gt;

    &lt;p class=&quot;center&quot;&gt;&lt;img src=&quot;/i/final-shaping.gif&quot; width=&quot;270&quot; height=&quot;480&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;

    &lt;p&gt;And here’s the result:&lt;/p&gt;

    &lt;p class=&quot;center&quot;&gt;&lt;img src=&quot;/i/tartine-loaf.jpg&quot; width=&quot;375&quot; height=&quot;375&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;
  &lt;/li&gt;
&lt;/ul&gt;
</content>
</entry><entry>
  <title type="html">Weeknotes 48</title>
  <link href="https://mudge.name/2020/09/27/weeknotes-48/"/>
  <id>https://mudge.name/2020/09/27/weeknotes-48/</id>
  <published>2020-09-27T12:45:00+00:00</published>
  <updated>2020-09-27T12:45:00+00:00</updated>
  <summary type="html">Mentor envy, calculating the surface area of pizza, separation, adding HTTP headers and how to use your entire body to express dissatisfaction.</summary>
  <content type="html">&lt;ul&gt;
  &lt;li&gt;
    &lt;p&gt;I thoroughly enjoyed &lt;a href=&quot;https://www.annashipman.co.uk/jfdi/meeting-everyone.html&quot;&gt;Anna Shipman’s “Meeting everyone on a new team”&lt;/a&gt;:&lt;/p&gt;

    &lt;blockquote&gt;
      &lt;p&gt;When I joined the Financial Times as Technical Director for FT.com, I inherited a team of around 50 engineers. One of the first things I did was meet each of them for a one-to-one.&lt;/p&gt;
    &lt;/blockquote&gt;

    &lt;p&gt;Though it did make me envious of Anna’s mentors:&lt;/p&gt;

    &lt;blockquote&gt;
      &lt;p&gt;The idea was suggested to me by a mentor, who’d been advised to do it by his mentor, a &lt;em&gt;Rear Admiral&lt;/em&gt; [emphasis added], who said this was something you should do whenever you have a team of fewer than 150 people.&lt;/p&gt;
    &lt;/blockquote&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;Torn between sharing two takeaway 12 inch pizzas or a single 18 inch one, E and I turned to &lt;a href=&quot;https://www.wolframalpha.com&quot;&gt;Wolfram Alpha&lt;/a&gt;:&lt;/p&gt;

    &lt;ul&gt;
      &lt;li&gt;“&lt;a href=&quot;https://www.wolframalpha.com/input/?i=area+of+circle+with+diameter+12+inches&quot;&gt;area of circle with diameter 12 inches&lt;/a&gt;” = 36 square inches * 2 = 72 square inches&lt;/li&gt;
      &lt;li&gt;“&lt;a href=&quot;https://www.wolframalpha.com/input/?i=area+of+circle+with+diameter+18+inches&quot;&gt;area of circle with diameter 18 inches&lt;/a&gt;” = 81 square inches&lt;/li&gt;
    &lt;/ul&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;I dropped C off at nursery for the first time. Wearing a face mask, I had to slip on blue, disposable shoe covers while waiting for him to settle.&lt;/p&gt;

    &lt;p&gt;At a certain point, one of the key workers said to the other “let’s do the separation now” and it was time for me to leave.&lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;In order to test parts of a new project, I have to send an extra HTTP header with my requests. If you use Chrome or Firefox, &lt;a href=&quot;https://bewisse.com/modheader/&quot;&gt;ModHeader&lt;/a&gt; seems a popular choice for doing this but, as a Safari user, I needed an alternative.&lt;/p&gt;

    &lt;p&gt;Instead of using a browser extension, I settled on using &lt;a href=&quot;https://mitmproxy.org&quot;&gt;mitmproxy&lt;/a&gt;, a “free and open source interactive HTTPS proxy”. I use its “&lt;a href=&quot;https://mitmproxy.readthedocs.io/en/v2.0.2/features/setheaders.html&quot;&gt;Set Headers&lt;/a&gt;” feature to add my extra header to requests matching a particular domain, e.g. to add &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;X-Special-Header: header-value&lt;/code&gt; to any request to a &lt;a href=&quot;https://devcenter.heroku.com/articles/github-integration-review-apps&quot;&gt;Heroku review app&lt;/a&gt;:&lt;/p&gt;

    &lt;div class=&quot;language-console highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;gp&quot;&gt;$&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;mitmdump &lt;span class=&quot;nt&quot;&gt;--modify-headers&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;'/herokuapp.com/X-Special-Header/header-value'&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;    &lt;/div&gt;

    &lt;p&gt;This works regardless of browser and with tools such as &lt;a href=&quot;https://httpie.org&quot;&gt;HTTPie&lt;/a&gt;.&lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;“&lt;a href=&quot;https://thegreatbritishbakeoff.co.uk&quot;&gt;The Great British Bake Off&lt;/a&gt;” is back.&lt;/p&gt;

    &lt;p&gt;After &lt;a href=&quot;/2019/11/16/weeknotes-3/&quot;&gt;finding last year’s competition disappointing and upsetting&lt;/a&gt;, my faith was somewhat restored by the much more joyful “&lt;a href=&quot;https://www.channel4.com/programmes/junior-bake-off&quot;&gt;Junior Bake Off&lt;/a&gt;” and “&lt;a href=&quot;https://www.channel4.com/programmes/bake-off-the-professionals&quot;&gt;Bake Off: The Professionals&lt;/a&gt;”.&lt;/p&gt;

    &lt;p&gt;After the first episode and &lt;a href=&quot;https://youtu.be/JKhOFKCOH5M&quot;&gt;Matt Lucas’ surprisingly accurate impression of Boris Johnson&lt;/a&gt;, I’m cautiously optimistic.&lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;I’m a big fan of mint chocolate and have come to the conclusion the only mint chocolate worth buying is “&lt;a href=&quot;https://www.lindt.co.uk/lindt-excellence-dark-mint-intense-bar-100g-en-gb-03046920028752&quot;&gt;Lindt Excellence Dark Mint Intense&lt;/a&gt;”. Anything else just doesn’t cut it.&lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;C has been developing a special move he deploys whenever you prevent him doing something he wants (e.g. pulling things out of the fridge, grabbing bottles of gel for unclogging drains) where he’ll suddenly stiffen his entire body, arching backwards as hard as he can. We’ve tried explaining this usually results in an outcome no one desires: flying headfirst backwards.&lt;/p&gt;

    &lt;p&gt;We call it the “protest banana”.&lt;/p&gt;
  &lt;/li&gt;
&lt;/ul&gt;
</content>
</entry><entry>
  <title type="html">Weeknotes 47</title>
  <link href="https://mudge.name/2020/09/21/weeknotes-47/"/>
  <id>https://mudge.name/2020/09/21/weeknotes-47/</id>
  <published>2020-09-21T11:55:00+00:00</published>
  <updated>2020-09-21T11:55:00+00:00</updated>
  <summary type="html">Settling in, potentially diseased garlic and being at a loss for notes.</summary>
  <content type="html">&lt;ul&gt;
  &lt;li&gt;
    &lt;p&gt;It was C’s “settling in” week at nursery this week. E took him in for a few hours every day to get used to the unfamiliar surroundings and his key worker. This meant ironing &lt;a href=&quot;https://www.easy2name.com&quot;&gt;name labels&lt;/a&gt; into all of his clothes and that he was in someone else’s care for an hour or two for the first time.&lt;/p&gt;

    &lt;p&gt;I capitalised on this strange, guilty sense of freedom by going for a run for the first time in months. I didn’t get very far and spent a lot of the time trying to keep my running tights up. I also indulged in &lt;a href=&quot;https://www.netflix.com/title/80186863&quot; title=&quot;Umbrella Academy&quot;&gt;some&lt;/a&gt; &lt;a href=&quot;https://www.netflix.com/title/80097140&quot; title=&quot;Altered Carbon&quot;&gt;science fiction&lt;/a&gt;.&lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;&lt;a href=&quot;/2020/04/19/weeknotes-25/&quot;&gt;Five months ago&lt;/a&gt;, I planted a clove of garlic. Today, I harvested it!&lt;/p&gt;

    &lt;p class=&quot;center&quot;&gt;&lt;img src=&quot;/i/garlic.jpg&quot; width=&quot;360&quot; height=&quot;480&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;

    &lt;p&gt;It certainly &lt;em&gt;looks&lt;/em&gt; like a bulb of garlic but we now need to dry it out for a few days. I’m only slightly alarmed by the &lt;a href=&quot;https://www.rhs.org.uk/advice/grow-your-own/vegetables/garlic&quot;&gt;RHS Gardening website’s warning&lt;/a&gt; that growing garlic from a supermarket “may carry disease”.&lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;The official &lt;a href=&quot;https://github.blog/2020-09-17-github-cli-1-0-is-now-available/&quot;&gt;GitHub CLI 1.0 was released&lt;/a&gt;. I’ve long used &lt;a href=&quot;https://hub.github.com&quot;&gt;hub&lt;/a&gt; but &lt;a href=&quot;https://mislav.net/2020/01/github-cli/&quot;&gt;as its maintainer Mislav was hired by GitHub to build GitHub CLI&lt;/a&gt;, I’m going to try switching over.&lt;/p&gt;

    &lt;blockquote&gt;
      &lt;p&gt;The GitHub CLI that we are building is not exactly what I would have
chosen to create if I was the only person in charge of making it, but
this is A Good Thing. Before, I never really made an effort to understand
who the audience of hub was, but with an actual team we finally get to
explore that and hopefully build something that’s ambitious not in terms
of the number of features it offers or how much of the GitHub API it
covers, but in how well it helps people be productive with their daily
work.&lt;/p&gt;
    &lt;/blockquote&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;With an increasingly mobile C in nursery, E going back to work and me working with multiple clients for the first time, I’m at a bit of a loss for notes this week.&lt;/p&gt;
  &lt;/li&gt;
&lt;/ul&gt;
</content>
</entry><entry>
  <title type="html">Weeknotes 46</title>
  <link href="https://mudge.name/2020/09/13/weeknotes-46/"/>
  <id>https://mudge.name/2020/09/13/weeknotes-46/</id>
  <published>2020-09-13T10:23:00+00:00</published>
  <updated>2020-09-13T10:23:00+00:00</updated>
  <summary type="html">T'north, Netflix tips and tricks and coffee brewing theory.</summary>
  <content type="html">&lt;ul&gt;
  &lt;li&gt;
    &lt;p&gt;We’re back in London after two weeks up in t’north of England.&lt;/p&gt;

    &lt;p&gt;It had been six months since our families had properly seen C which is basically half his life so far. I got to rummage through old toys, uncovering an old &lt;a href=&quot;https://www.lego.com/en-gb/themes/duplo&quot;&gt;DUPLO&lt;/a&gt; aeroplane and train set of mine. We discovered a local &lt;a href=&quot;https://www.themilkchurn.com&quot;&gt;Yorkshire gelateria&lt;/a&gt; and revisited the &lt;a href=&quot;http://theangelssharebakery.com/&quot;&gt;bakery that provided bread for our wedding&lt;/a&gt;, giving C his first taste of a &lt;a href=&quot;http://theangelssharebakery.com/about/specialities/&quot;&gt;Yorkshire Curd tart&lt;/a&gt;.&lt;/p&gt;

    &lt;p&gt;It was the break we all needed before everything changes in the next few weeks as E returns to work and C starts nursery.&lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;Part of the ritual of leaving guest accommodation is logging out of one’s Netflix account. Depending on the device, this may involve &lt;a href=&quot;https://help.netflix.com/en/node/23876&quot;&gt;an arcane sequence of button presses&lt;/a&gt; to reveal a secret Settings screen:&lt;/p&gt;

    &lt;blockquote&gt;
      &lt;p&gt;From within the Netflix app, use the arrows on your remote to enter the following sequence: &lt;strong&gt;Up, Up, Down, Down, Left, Right, Left, Right, Up, Up, Up, Up.&lt;/strong&gt;&lt;/p&gt;
    &lt;/blockquote&gt;

    &lt;p&gt;It is easier to remember if you think of it as a &lt;a href=&quot;https://en.wikipedia.org/wiki/Konami_Code&quot;&gt;variation of the Konami Code&lt;/a&gt;.&lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;Speaking of Netflix: during the trip, we signed my dad up for his own account so he could stop filling my viewing history with endless shows related to the drug trade. I’ve been tempted to delete my profile and start again but am trying to &lt;a href=&quot;https://help.netflix.com/en/node/22205&quot;&gt;hide the things he has watched instead&lt;/a&gt;.&lt;/p&gt;

    &lt;p&gt;However, it can “take up to 24 hours for a hidden title to be removed from all your devices” and the interface seems remarkably flaky. I’m currently having to delete each and every episode of “Suits” and “Homeland” individually (I blame &lt;a href=&quot;https://en.wikipedia.org/wiki/Eventual_consistency&quot;&gt;eventual consistency&lt;/a&gt;).&lt;/p&gt;

    &lt;p&gt;Hopefully I’ll soon be rewarded for my meticulous clicking with fewer recommendations for documentaries about Pablo Escobar and El Chapo.&lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;One thing we’ve missed while being away was a &lt;a href=&quot;https://youtu.be/KxwcQ1dapw8&quot;&gt;damn fine cup of coffee&lt;/a&gt;. Leo recommended &lt;a href=&quot;https://youtu.be/AI4ynXzkSQo&quot;&gt;James Hoffmann’s “The Ultimate V60 Technique”&lt;/a&gt; which has immediately sent me down something of a “coffee brewing theory” rabbit-hole.&lt;/p&gt;
  &lt;/li&gt;
&lt;/ul&gt;
</content>
</entry><entry>
  <title type="html">Weeknotes 45</title>
  <link href="https://mudge.name/2020/09/06/weeknotes-45/"/>
  <id>https://mudge.name/2020/09/06/weeknotes-45/</id>
  <published>2020-09-06T11:02:00+00:00</published>
  <updated>2020-09-06T11:02:00+00:00</updated>
  <summary type="html">A birth story.</summary>
  <content type="html">&lt;ul&gt;
  &lt;li&gt;
    &lt;p&gt;“We’re going to pull the alarm now and a lot of people will come into the room but don’t worry.”&lt;/p&gt;

    &lt;p&gt;A year ago, I held an exhausted E’s hand as consultants, doctors and midwives gathered around C on a &lt;a href=&quot;https://www.draeger.com/en_uk/Products/Resuscitaire&quot;&gt;Resuscitaire&lt;/a&gt;. It’d been two days since E’s waters had broken and what little sleep we’d had was punctuated by my clumsy timing of contractions. It was a time of being offered choices, desperately trying to understand &lt;a href=&quot;https://www.cochranelibrary.com/cdsr/about-cdsr&quot;&gt;Cochrane systematic reviews&lt;/a&gt; and ultimately making decisions I was in no way equipped to make.&lt;/p&gt;

    &lt;p&gt;I listened to the mundane counting as they performed CPR, each new round of “1, 2, 3, 4” steadily filling me with dread.&lt;/p&gt;

    &lt;p&gt;Eventually, a mewling and a hubbub as C urinated on several members of staff. They began bickering among themselves as to what had happened to him with the most commanding person in the room repeatedly insisting “there was no meconium on the vocal cords”. As they finally beckoned me over to properly see C for the first time, the lead midwife jostled to show me a clear tube with something cloudy within, “see, look at the mucus!”&lt;/p&gt;

    &lt;p&gt;Bewildered, I did as I was told and followed as he was wheeled to the neonatal intensive care unit (the “NICU”). He was soon covered in dressings with teddy bears and the &lt;a href=&quot;https://www.3m.co.uk/3M/en_GB/company-uk/&quot;&gt;3M&lt;/a&gt; logo on them. I quickly began misspelling the word “cannula”.&lt;/p&gt;

    &lt;p&gt;As I watched him through the plastic, I turned to the nurses and, masking my panic, asked “erm, is it OK he’s pulling his breathing tubes out?”&lt;/p&gt;

    &lt;p&gt;I returned to the labour ward to find E eating a bowl of bran flakes.&lt;/p&gt;

    &lt;p&gt;Later, after being told parents shouldn’t be present for the procedure, E and I would sit in a small room off the NICU as C had a lumbar puncture. I stared at a noticeboard, trying not to think about what I’d read about “bacterial meningitis” the night before.&lt;/p&gt;

    &lt;p&gt;Eventually, we’d make it home after the most careful drive of my life. There’d be a trip to the children’s ambulatory care unit to deal with C’s cannula which we were told had “tissued”. We’d watch as two people struggled to fit another and I distracted myself by staring at some morning TV show about how a teenager had gone blind from a diet that consisted exclusively of chips.&lt;/p&gt;

    &lt;p&gt;There’d be a &lt;a href=&quot;https://www.nhs.uk/conditions/pregnancy-and-baby/baby-reviews/&quot;&gt;red book&lt;/a&gt; and a course of benzylpenicillin and gentamicin. I forget which one stings.&lt;/p&gt;

    &lt;p&gt;There’d be tearful debriefs with our community midwives. I’d grow resentful and then angry at how polarised the &lt;a href=&quot;https://www.nhs.uk&quot;&gt;NHS&lt;/a&gt; and &lt;a href=&quot;https://www.nct.org.uk&quot;&gt;NCT&lt;/a&gt; were and how that might have affected what happened. I’d reluctantly share our “birth story” with our NCT class and immediately regret it.&lt;/p&gt;

    &lt;p&gt;I’d think about sharing what had happened more publicly in an attempt to order my own thoughts and feelings but nothing would come of it.&lt;/p&gt;

    &lt;p&gt;Instead, after nine weeks, I’d &lt;a href=&quot;/2019/11/04/weeknotes-1/&quot;&gt;start writing weeknotes&lt;/a&gt;.&lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;Happy 1st birthday, C.&lt;/p&gt;

    &lt;p class=&quot;center&quot;&gt;&lt;img src=&quot;/i/c.jpg&quot; width=&quot;360&quot; height=&quot;480&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;
  &lt;/li&gt;
&lt;/ul&gt;
</content>
</entry><entry>
  <title type="html">Weeknotes 44</title>
  <link href="https://mudge.name/2020/08/30/weeknotes-44/"/>
  <id>https://mudge.name/2020/08/30/weeknotes-44/</id>
  <published>2020-08-30T09:39:00+00:00</published>
  <updated>2020-08-30T09:39:00+00:00</updated>
  <summary type="html">A young child's unsteady walk, deep cleaning and a holiday.</summary>
  <content type="html">&lt;ul&gt;
  &lt;li&gt;
    &lt;p&gt;In the past two weeks, C has begun insisting we hold his hands so he can toddle around. Having previously shown little interest in movement, this is quite a change of pace and has led to the discovery there is no greater fun in the world than pulling magnets off a radiator or mashing the buttons on a washing machine.&lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;This newfound mobility has meant I routinely find tiny, greasy hand-prints in new and exciting places. &lt;a href=&quot;https://atp.fm/381&quot;&gt;John Siracusa would not approve&lt;/a&gt; of C’s love of pressing his sticky palms against our TV screen.&lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;Now that I have been able to help with childcare in these last few weeks before E returns to work, she has begun a methodical deep clean of our flat starting with our kitchen (grubby hand-prints notwithstanding). It has been a great opportunity to &lt;a href=&quot;https://konmari.com&quot;&gt;KonMari&lt;/a&gt; the strange, useless things one accumulates over time, e.g. an expansive collection of reusable wine bottles that can only be filled at a shop that no longer offers refills.&lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;I write this looking out over a rolling Yorkshire dale.&lt;/p&gt;

    &lt;p&gt;We’re staying in a self-catering lodge which was apparently thoroughly disinfected and—as if to really shock-and-awe any virus—had some kind of antibacterial fog bomb detonated within.&lt;/p&gt;

    &lt;p&gt;This isn’t our first time here as this was our wedding venue nearly three years ago. Upon arrival, we were greeted by a member of staff who had looked after us back then. She told us they stopped doing weddings shortly after ours so we were one of the last.&lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;This is &lt;a href=&quot;https://youtu.be/UHzF5KnoN20&quot;&gt;the furthest we’ve travelled&lt;/a&gt; since the pandemic began. Halfway through the journey, we stopped at a motorway service station and enjoyed a picnic of cold pizza and peanut butter sandwiches in our car.&lt;/p&gt;
  &lt;/li&gt;
&lt;/ul&gt;
</content>
</entry><entry>
  <title type="html">Weeknotes 43</title>
  <link href="https://mudge.name/2020/08/23/weeknotes-43/"/>
  <id>https://mudge.name/2020/08/23/weeknotes-43/</id>
  <published>2020-08-23T15:23:00+00:00</published>
  <updated>2020-08-23T15:23:00+00:00</updated>
  <summary type="html">New experiences, starting over and Daddy Pig holding a mirror up to society.</summary>
  <content type="html">&lt;ul&gt;
  &lt;li&gt;
    &lt;p&gt;I met a former colleague at a local pub and we sat out front to catch up. After taking my order, a member of staff asked to scan my wrist with an infrared thermometer. This was an entirely new experience and I was quite giddy at having my faculties critically assessed by a beige handheld device.&lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;I finished my latest contract on Friday and, due to &lt;a href=&quot;/2020/08/16/weeknotes-42/&quot;&gt;last week’s preparation&lt;/a&gt;, I write these notes on a freshly formatted laptop.&lt;/p&gt;

    &lt;p&gt;There’s something cathartic about starting afresh, clearing out my old &lt;a href=&quot;https://support.1password.com/create-share-vaults/&quot;&gt;1Password vaults&lt;/a&gt; and asking whether each piece of software &lt;a href=&quot;https://konmari.com&quot;&gt;sparks joy&lt;/a&gt; or not.&lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;I went down a &lt;a href=&quot;https://github.com/Homebrew/homebrew-cask/pull/88039&quot;&gt;bit of a rabbit-hole&lt;/a&gt; installing &lt;a href=&quot;https://www.backblaze.com&quot;&gt;Backblaze&lt;/a&gt; via &lt;a href=&quot;https://brew.sh&quot;&gt;Homebrew&lt;/a&gt;.&lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;Now that I’ll be helping out with childcare more, I inevitably found myself watching &lt;a href=&quot;https://www.peppapig.co.uk&quot;&gt;Peppa Pig&lt;/a&gt; this morning and was reminded of &lt;a href=&quot;https://youtu.be/oZC790urhtw?t=136&quot;&gt;its damning satire of knowledge work&lt;/a&gt; at Daddy Pig’s office.&lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;&lt;a href=&quot;/2019/01/02/2018-yearnotes/&quot;&gt;Once more, I’m not sure what’s next&lt;/a&gt; but I’m looking forward to a break and to celebrating C’s first birthday.&lt;/p&gt;
  &lt;/li&gt;
&lt;/ul&gt;
</content>
</entry><entry>
  <title type="html">Weeknotes 42</title>
  <link href="https://mudge.name/2020/08/16/weeknotes-42/"/>
  <id>https://mudge.name/2020/08/16/weeknotes-42/</id>
  <published>2020-08-16T09:42:00+00:00</published>
  <updated>2020-08-16T09:42:00+00:00</updated>
  <summary type="html">A void that cannot be filled with ergonomic equipment, tech brain and decapitating cardboard dogs.</summary>
  <content type="html">&lt;ul&gt;
  &lt;li&gt;
    &lt;p&gt;I made no notes this week.&lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;&lt;a href=&quot;https://ahelwer.ca/post/2020-08-09-home-ergonomics/&quot;&gt;Andrew Helwer’s “Taking my home work setup seriously”&lt;/a&gt; is making me reconsider the suitability of my &lt;a href=&quot;https://www.apple.com/uk/shop/product/MJ2R2Z/A/magic-trackpad-2-silver&quot;&gt;Magic Trackpad&lt;/a&gt; especially given that I’ve developed some sort of callus between my palm and my wrist.&lt;/p&gt;

    &lt;p&gt;Then again:&lt;/p&gt;

    &lt;blockquote&gt;
      &lt;p&gt;Inside us all there is a void. People want to complete themselves and fill this void with spirituality, or hedonistic pursuits, or material things. If you’ll indulge a metaphor, this is not a void that can be filled - its nature is more akin to a black hole of the cosmic variety. Feeding it things - for example, expensive ergonomic equipment - will simply add to its mass and pull. Only if left alone might it slowly evaporate. You must learn to live with it.&lt;/p&gt;
    &lt;/blockquote&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;&lt;a href=&quot;https://pycnocline.substack.com/p/tech-brain&quot;&gt;stephanie’s “tech brain”&lt;/a&gt; provoked discussion among myself and some fellow software developers.&lt;/p&gt;

    &lt;blockquote&gt;
      &lt;p&gt;what is tech brain? there are lots of things to point to, but if i had to come up with a thesis it would be that tech brain is a sort of constant willful reductionism: an addiction to easy answers combined with a wholesale cultural resistance to any kind of complexity.&lt;/p&gt;
    &lt;/blockquote&gt;

    &lt;p&gt;&lt;a href=&quot;https://twitter.com/eightbitraptor/status/1293901151591702528&quot;&gt;Like&lt;/a&gt; &lt;a href=&quot;https://twitter.com/tomstuart/status/1293641281667764224&quot;&gt;others&lt;/a&gt;, I found myself nodding along but &lt;a href=&quot;https://zerodivisionerror.neocities.org&quot;&gt;Maciej&lt;/a&gt; asked an interesting question: how much of this is &lt;em&gt;Twitter&lt;/em&gt; brain rather than being specific to the tech industry?&lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;I’m currently running a full backup of my laptop with &lt;a href=&quot;https://www.shirt-pocket.com/SuperDuper/SuperDuperDescription.html&quot;&gt;SuperDuper!&lt;/a&gt;. Next week, in a ritual to mark moving on from a client I’ve been working with since March, I’ll wipe my computer clean and start afresh.&lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;C’s current favourite activity is to prop himself up on a bookshelf and systematically pull all of the books onto the floor.&lt;/p&gt;

    &lt;p&gt;Sometimes he wants to read them (or, in the case of certain pop-up books, attempt to decapitate as many cardboard dogs as possible) and sometimes he just wants to see &lt;del&gt;the world burn&lt;/del&gt; the shelf emptied.&lt;/p&gt;
  &lt;/li&gt;
&lt;/ul&gt;
</content>
</entry><entry>
  <title type="html">Weeknotes 41</title>
  <link href="https://mudge.name/2020/08/09/weeknotes-41/"/>
  <id>https://mudge.name/2020/08/09/weeknotes-41/</id>
  <published>2020-08-09T10:12:00+00:00</published>
  <updated>2020-08-09T10:12:00+00:00</updated>
  <summary type="html">Almost all the colours of the rainbow, what we wear on our feet, reminiscing with Radiohead, René Redzepi and technical tests.</summary>
  <content type="html">&lt;ul&gt;
  &lt;li&gt;
    &lt;p&gt;C has a set of “First Word” books that he often wants to read. They came in a little paper box so they can be kept together and each one is a different colour. Every time I put them away, I recite the mnemonic “&lt;a href=&quot;https://en.wikipedia.org/wiki/ROYGBIV&quot;&gt;Richard Of York Gave Battle In&lt;/a&gt;” for there are only six.&lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;As it has been unusually hot and humid, I had to forego &lt;a href=&quot;/2020/03/22/weeknotes-21/&quot;&gt;wearing socks&lt;/a&gt; and even resort to flip-flops on Friday. This marks my first slipper-free day working from home since March.&lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;As the word “flip-flop” is not quite onomatopoeic (or is that &lt;a href=&quot;https://en.wikipedia.org/wiki/Echomimetic&quot;&gt;echomimetic&lt;/a&gt;?) enough for this household, we like to use the much more accurate phrase “snicker-snackers”.&lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;Listening to &lt;a href=&quot;https://www.adam-buxton.co.uk/podcasts/7-bfk9m-4l8kp-blcga-jwabs-blbb8-b9mjx-r3ftc&quot;&gt;Adam Buxton interview Radiohead guitarist Ed O’Brien&lt;/a&gt; reminded me of my deep and perhaps-in-retrospect-unhealthy love of &lt;a href=&quot;https://www.radiohead.co.uk&quot;&gt;the band&lt;/a&gt; in my teens.&lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;Buxton and O’Brien’s discussion of &lt;a href=&quot;https://www.imdb.com/title/tt0816692/&quot;&gt;Christopher Nolan’s 2014 film “Interstellar”&lt;/a&gt; in relation to parenting reminded me of something I mentioned in &lt;a href=&quot;/2019/11/10/weeknotes-2/&quot;&gt;my second weeknotes&lt;/a&gt;:&lt;/p&gt;

    &lt;blockquote&gt;
      &lt;p&gt;It’s like the experience of being a parent is an exercise in relativity in a way because your sense of time is so different to theirs and it’s so cruel how fast it is for you as an adult and it’s so torturously slow for them.&lt;/p&gt;
    &lt;/blockquote&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;On a more joyous (but still poignant note), they also mentioned &lt;a href=&quot;https://youtu.be/6SFNW5F8K9Y&quot;&gt;Tom Petty, Jeff Lynne, Steve Winwood, Dhani Harrison and Prince’s performance of “While My Guitar Gently Weeps” during A Tribute to George Harrison in 2004&lt;/a&gt;. Where &lt;em&gt;does&lt;/em&gt; that guitar go?&lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;We &lt;a href=&quot;/2020/08/02/weeknotes-40/&quot;&gt;continue to watch “Parts Unknown”&lt;/a&gt; and, despite owning &lt;a href=&quot;https://www.mendo.nl/product/noma-guide-fermentation/&quot;&gt;one of his books&lt;/a&gt; and seeing him on countless other cookery shows, I found the episode about &lt;a href=&quot;https://noma.dk&quot;&gt;Noma&lt;/a&gt;’s &lt;a href=&quot;https://en.wikipedia.org/wiki/René_Redzepi&quot;&gt;René Redzepi&lt;/a&gt; fascinating.&lt;/p&gt;

    &lt;p&gt;It’s one thing to hear the phrase “&lt;a href=&quot;https://en.wikipedia.org/wiki/New_Danish_cuisine#New_Nordic_Cuisine&quot;&gt;New Nordic Cuisine&lt;/a&gt;” over and over but another to hear of Redzepi’s &lt;a href=&quot;https://en.wikipedia.org/wiki/René_Redzepi#Early_life_and_education&quot;&gt;childhood in Macedonia&lt;/a&gt; foraging with his father and watching chefs present their dishes for the scrutiny of their peers:&lt;/p&gt;

    &lt;blockquote&gt;
      &lt;p&gt;And that might well end up on the menu?&lt;/p&gt;
    &lt;/blockquote&gt;

    &lt;blockquote&gt;
      &lt;p&gt;No. This is not about putting things on the menu. No, I mean, if somebody makes a masterpiece, its &lt;em&gt;their&lt;/em&gt; masterpiece.&lt;/p&gt;
    &lt;/blockquote&gt;

    &lt;blockquote&gt;
      &lt;p&gt;&lt;em&gt;Really?&lt;/em&gt;&lt;/p&gt;
    &lt;/blockquote&gt;

    &lt;blockquote&gt;
      &lt;p&gt;Yeah, yeah, of course.&lt;/p&gt;
    &lt;/blockquote&gt;

    &lt;blockquote&gt;
      &lt;p&gt;Isn’t it your historical imperative as the chef to take his good work and innovation and put it on the menu and take credit for it as your own? I mean, that’s the way it’s been done for centuries.&lt;/p&gt;
    &lt;/blockquote&gt;

    &lt;blockquote&gt;
      &lt;p&gt;This is not the point here.&lt;/p&gt;
    &lt;/blockquote&gt;

    &lt;blockquote&gt;
      &lt;p&gt;The pursuit of enlightenment and knowledge is its own reward?&lt;/p&gt;
    &lt;/blockquote&gt;

    &lt;blockquote&gt;
      &lt;p&gt;To me, yes.&lt;/p&gt;
    &lt;/blockquote&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;I did a technical test this week for a potential client and, in doing so, thought about the difficulty of trying to condense your experience and philosophy of programming into the solution of a canned exercise.&lt;/p&gt;

    &lt;p&gt;There’s often an expectation from interviewers this is possible: that a candidate can concisely convey their approach and a lot of what you need to know about them with these exercises which are typically blind of context.&lt;/p&gt;

    &lt;p&gt;Last year, I was doing a lot of recruitment for a client and saw many solutions to the same problem. It’s a variation on one I dare say a lot of Ruby programmers in London have seen: a checkout with various discount rules.&lt;/p&gt;

    &lt;p&gt;I grew to love the problem after seeing so many people focus on different aspects of it. With each new submission, I got a clearer and clearer idea of what &lt;em&gt;I&lt;/em&gt; thought was interesting and what was unimportant. In the end, I was driven to write my own minimal solution: with nothing spare, focussed purely on my view of what the beating heart of the problem was.&lt;/p&gt;

    &lt;p&gt;In the end though, it was just my opinion. I believe code should be optimised for change (or &lt;a href=&quot;https://programmingisterrible.com/post/139222674273/write-code-that-is-easy-to-delete-not-easy-to&quot;&gt;deletion&lt;/a&gt;) but predicting exactly &lt;em&gt;which&lt;/em&gt; changes it needs to accommodate is extremely hard. In the case of a tech test which contains only business rules and no context, perhaps it borders on the impossible.&lt;/p&gt;
  &lt;/li&gt;
&lt;/ul&gt;
</content>
</entry><entry>
  <title type="html">Weeknotes 40</title>
  <link href="https://mudge.name/2020/08/02/weeknotes-40/"/>
  <id>https://mudge.name/2020/08/02/weeknotes-40/</id>
  <published>2020-08-02T10:20:00+00:00</published>
  <updated>2020-08-02T10:20:00+00:00</updated>
  <summary type="html">Not owning a microwave, uncanny turns of phrase, Parts Unknown and the therapeutic power of Taylor Swift.</summary>
  <content type="html">&lt;ul&gt;
  &lt;li&gt;
    &lt;p&gt;Following &lt;a href=&quot;/2020/07/19/weeknotes-38/&quot;&gt;the success of our trip north&lt;/a&gt;, we met up with my parents for the first time since lockdown began, having a picnic together so they could see their grandson in person after months of only FaceTime calls.&lt;/p&gt;

    &lt;p&gt;C enjoyed the extra attention and I got to tell my dad off for ignoring social distancing advice.&lt;/p&gt;

    &lt;p&gt;We came back from the trip with my parents’ old microwave. I’d tell you this is the first time I’ve had a microwave in 11 years but I fear it’s like people who are at pains to tell you they don’t even own a TV.&lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;On the radio one morning, someone described tourism as:&lt;/p&gt;

    &lt;blockquote&gt;
      &lt;p&gt;The industry of &lt;em&gt;human&lt;/em&gt; [emphasis added] happiness.&lt;/p&gt;
    &lt;/blockquote&gt;

    &lt;p&gt;Tough luck, other species.&lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;Continuing the theme of uncanny speech, we caught a TV interview with a politician talking about easing lockdown for our country’s beloved “sandwich bars”.&lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;E and I began watching “&lt;a href=&quot;https://en.wikipedia.org/wiki/Anthony_Bourdain:_Parts_Unknown&quot;&gt;Anthony Bourdain: Parts Unknown&lt;/a&gt;” after multiple recommendations.&lt;/p&gt;

    &lt;p&gt;We’ve only watched the first few episodes (originally aired in 2013) but I’m enjoying it so far. I expected a food tourism show with a larger-than-life host (not unlike &lt;a href=&quot;https://en.wikipedia.org/wiki/David_Chang&quot;&gt;David Chang&lt;/a&gt; in “&lt;a href=&quot;/2020/03/08/weeknotes-19/&quot;&gt;Ugly Delicious&lt;/a&gt;”) but I was pleasantly surprised that Bourdain doesn’t dominate the show, instead preferring to let others speak in places like Myanmar and Koreatown.&lt;/p&gt;

    &lt;p&gt;The amount of truffle and foie gras on display in Quebec was a bit much though.&lt;/p&gt;

    &lt;p&gt;Seeing him travel the world and listening to his narration, it’s hard not to think about &lt;a href=&quot;https://en.wikipedia.org/wiki/Anthony_Bourdain#Death&quot;&gt;his death in 2018&lt;/a&gt;.&lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;This week was dominated by some stressful decisions so I don’t have too much to share. I spent a lot of it agonising over details and going for walks around the block (mostly to the sound of &lt;a href=&quot;https://en.wikipedia.org/wiki/Folklore_(Taylor_Swift_album)&quot;&gt;Taylor Swift’s “Folklore”&lt;/a&gt;) in an attempt to clear my head.&lt;/p&gt;
  &lt;/li&gt;
&lt;/ul&gt;
</content>
</entry><entry>
  <title type="html">Weeknotes 39</title>
  <link href="https://mudge.name/2020/07/25/weeknotes-39/"/>
  <id>https://mudge.name/2020/07/25/weeknotes-39/</id>
  <published>2020-07-25T10:48:00+00:00</published>
  <updated>2020-07-25T10:48:00+00:00</updated>
  <summary type="html">Nurseries, unwinding with wooden blocks, things people say to you when you wear a baby carrier and a computation anniversary.</summary>
  <content type="html">&lt;ul&gt;
  &lt;li&gt;
    &lt;p&gt;Working from home with C in the house can be a strange experience. After a morning of calls about &lt;a href=&quot;https://webpack.js.org/guides/tree-shaking/&quot;&gt;tree shaking&lt;/a&gt;, &lt;a href=&quot;https://twitter.com/qrlitycontrol&quot;&gt;QR codes&lt;/a&gt; and &lt;a href=&quot;https://ethereum.stackexchange.com/questions/3/what-is-meant-by-the-term-gas&quot;&gt;gas fees&lt;/a&gt;, I’ll leave my desk and upon seeing him for the first time in a few hours, I’ll ask E, “whose baby is that?”&lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;As E will be going back to work soon, we’ve been trying to find a nursery for C to attend three days a week. We were lucky enough to get more than one place for him but hadn’t seen one of the options due to the lockdown. This week, we arranged to have a look around (wearing masks, of course) and, after much deliberation, made our decision and paid our deposit.&lt;/p&gt;

    &lt;p&gt;(It still doesn’t feel real.)&lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;After a stressful day, there’s no better way to unwind than by meticulously organising the wooden blocks in C’s walker.&lt;/p&gt;

    &lt;p class=&quot;center&quot;&gt;&lt;img src=&quot;/i/blocks.jpg&quot; width=&quot;375&quot; height=&quot;375&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;During one of my lunchtime walks with C, he was fascinated by a passing bin lorry. One of the dustmen noticed him watching and waved emphatically.&lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;During another stroll, a man crossing the street cooed at C in his baby carrier and gleefully called for all to hear, “look at his chubby cheeks!”&lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;On a call with a potential vendor this week, when discussing the user experience of a particular system, their representative said:&lt;/p&gt;

    &lt;blockquote&gt;
      &lt;p&gt;Sure, if you want to cater to Joe Schmoe… from Kokomo.&lt;/p&gt;
    &lt;/blockquote&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;It was the third anniversary of the &lt;a href=&quot;https://github.com/computationclub/computationclub.github.io/wiki/Bletchley-Park&quot;&gt;London Computation Club trip to Bletchley Park&lt;/a&gt;.&lt;/p&gt;

    &lt;p class=&quot;center&quot;&gt;&lt;img src=&quot;/i/bletchley.jpg&quot; width=&quot;512&quot; height=&quot;384&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;

    &lt;p&gt;Just look at us all: bright-eyed, bushy-tailed and sitting so close to one another.&lt;/p&gt;
  &lt;/li&gt;
&lt;/ul&gt;
</content>
</entry><entry>
  <title type="html">Weeknotes 38</title>
  <link href="https://mudge.name/2020/07/19/weeknotes-38/"/>
  <id>https://mudge.name/2020/07/19/weeknotes-38/</id>
  <published>2020-07-19T10:10:00+00:00</published>
  <updated>2020-07-19T10:10:00+00:00</updated>
  <summary type="html">Gold trees, some small sense of freedom, quartz glass platters and the villain in your history.</summary>
  <content type="html">&lt;ul&gt;
  &lt;li&gt;
    &lt;p&gt;After feeling &lt;a href=&quot;/2020/07/12/weeknotes-37/&quot;&gt;“out of sorts” last week&lt;/a&gt; due to the monotony of life during the pandemic, I decided to go for a walk with C every lunchtime this week. I get a change of scenery and he gets to babble at dog walkers.&lt;/p&gt;

    &lt;p&gt;Now that &lt;a href=&quot;https://alicebartlett.co.uk/blog/weaknotes-98&quot;&gt;Alice Bartlett’s “Week 98: Trees”&lt;/a&gt; has introduced me to &lt;a href=&quot;https://www.treetalk.co.uk&quot;&gt;TreeTalk&lt;/a&gt;, I might alter my route to investigate one of only two &lt;a href=&quot;https://www.rhs.org.uk/plants/14003/Prunus-lusitanica/Details&quot;&gt;Portuguese laurel trees&lt;/a&gt; in London.&lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;E and I decided to make our first trip outside London since lockdown began. We drove north for two hours and E’s family drove south for two hours and we all met for a picnic in the grounds of a country house.&lt;/p&gt;

    &lt;p&gt;As it seems unlikely we’ll be able to spend more time together soon, it felt good to exercise some small amount of freedom after feeling hopelessly trapped.&lt;/p&gt;

    &lt;p&gt;It has given us the confidence to get away for C’s first birthday and be closer to family so we won’t have to celebrate it purely virtually as we did for both &lt;a href=&quot;/2020/04/12/weeknotes-24/&quot;&gt;E’s&lt;/a&gt; and &lt;a href=&quot;/2020/05/24/weeknotes-30/&quot;&gt;my birthday&lt;/a&gt; this year.&lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;This week saw the &lt;a href=&quot;https://github.blog/2020-07-16-github-archive-program-the-journey-of-the-worlds-open-source-code-to-the-arctic/&quot;&gt;introduction of “Arctic Code Vault Badges” on GitHub&lt;/a&gt;, meaning open source software I wrote made it into the &lt;a href=&quot;https://archiveprogram.github.com&quot;&gt;GitHub Archive Program&lt;/a&gt;.&lt;/p&gt;

    &lt;p&gt;More specifically, it is stored on “&lt;a href=&quot;https://www.piql.com&quot;&gt;3,500-foot film reels&lt;/a&gt;” and has been written “into &lt;a href=&quot;https://www.microsoft.com/en-us/research/project/project-silica/&quot;&gt;quartz glass platters using a femtosecond laser&lt;/a&gt;” with the intent of being archived for “over 10,000 years”.&lt;/p&gt;

    &lt;p&gt;Let future historians judge the value of &lt;a href=&quot;https://github.com/rbenv/rbenv/commit/b7e19b4953f6f9c3377781342e517c78ebf27bce&quot;&gt;adding Solaris support to rbenv&lt;/a&gt; or &lt;a href=&quot;https://github.com/rails/rails/commit/58e21a4a0d4eefc395139e88c1f184b9eaf0b4c4&quot;&gt;passing blocks to &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;ActiveSupport::OrderedHash#merge&lt;/code&gt;&lt;/a&gt;.&lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;I’m currently avoiding tweeting my weeknotes as my mentions are full of &lt;a href=&quot;https://twitter.com/hexadecim8/status/1281224429863919619&quot;&gt;people mistaking me&lt;/a&gt; for &lt;a href=&quot;https://en.wikipedia.org/wiki/Peiter_Zatko&quot;&gt;Peiter Zatko&lt;/a&gt;.&lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;After adding &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;bamtech.sc.omtrdc.net&lt;/code&gt; to my &lt;a href=&quot;/2020/06/07/weeknotes-32/&quot;&gt;DNS sinkhole&lt;/a&gt;’s safelist, we watched &lt;a href=&quot;https://www.disneyplus.com/movies/hamilton/3uPmBHWlO6HJ&quot;&gt;“Hamilton” on Disney+&lt;/a&gt; having previously seen it &lt;a href=&quot;https://hamiltonmusical.com/london/home/&quot;&gt;on stage in London&lt;/a&gt; a year ago. It’s still &lt;a href=&quot;https://twitter.com/mudge/status/983827873063690240&quot;&gt;rather good&lt;/a&gt;.&lt;/p&gt;
  &lt;/li&gt;
&lt;/ul&gt;
</content>
</entry><entry>
  <title type="html">Weeknotes 37</title>
  <link href="https://mudge.name/2020/07/12/weeknotes-37/"/>
  <id>https://mudge.name/2020/07/12/weeknotes-37/</id>
  <published>2020-07-12T13:55:00+00:00</published>
  <updated>2020-07-12T13:55:00+00:00</updated>
  <summary type="html">How I discovered adventure games, saving your life, converting cups of sifted flour into moon masses and feeling out of sorts.</summary>
  <content type="html">&lt;ul&gt;
  &lt;li&gt;
    &lt;p&gt;The &lt;a href=&quot;https://twitter.com/DoubleFine&quot;&gt;Double Fine Productions&lt;/a&gt; remasters of classic &lt;a href=&quot;https://en.wikipedia.org/wiki/LucasArts&quot;&gt;LucasArts&lt;/a&gt;’ adventure games “&lt;a href=&quot;http://dott.doublefine.com&quot;&gt;Day of the Tentacle&lt;/a&gt;”, “&lt;a href=&quot;http://fullthrottle.doublefine.com&quot;&gt;Full Throttle&lt;/a&gt;” and “&lt;a href=&quot;http://grimremastered.com&quot;&gt;Grim Fandango&lt;/a&gt;” (as well as Double Fine’s own, more recent adventure game “&lt;a href=&quot;http://www.brokenagegame.com&quot;&gt;Broken Age&lt;/a&gt;”) are all &lt;a href=&quot;https://twitter.com/DoubleFine/status/1281618886744576003&quot;&gt;free on the Mac App Store this weekend&lt;/a&gt;.&lt;/p&gt;

    &lt;p&gt;When I was a child, my mum came back from &lt;a href=&quot;https://en.wikipedia.org/wiki/PC_World_(retailer)&quot;&gt;PC World&lt;/a&gt; with a copy of &lt;a href=&quot;https://en.wikipedia.org/wiki/The_Lion_King_(video_game)&quot;&gt;“The Lion King” video game&lt;/a&gt; on &lt;a href=&quot;https://en.wikipedia.org/wiki/Floppy_disk&quot;&gt;3½-inch floppy disks&lt;/a&gt;. Sadly, I had broken my disk drive when the sliding metal shutter from a diskette got stuck inside. My mum returned to the shop and swapped it for “&lt;a href=&quot;https://en.wikipedia.org/wiki/Day_of_the_Tentacle&quot;&gt;Day of the Tentacle&lt;/a&gt;” on &lt;a href=&quot;https://en.wikipedia.org/wiki/CD-ROM&quot;&gt;CD-ROM&lt;/a&gt; and my love of adventure games began.&lt;/p&gt;

    &lt;p&gt;In the words of the mechanics from one of my personal favourites:&lt;/p&gt;

    &lt;blockquote&gt;
      &lt;p&gt;We shoot you now like an arrow into the wind. May you pierce the heart of the wind itself, and drink the blood of flight.&lt;/p&gt;
    &lt;/blockquote&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;The &lt;a href=&quot;http://adam-buxton.co.uk/podcasts/ep126-joe-cornish&quot;&gt;latest episode of Adam Buxton’s podcast&lt;/a&gt; where he discusses the recent death of his mother with Joe Cornish is worth your time.&lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;Reading &lt;a href=&quot;https://web.dev/one-line-layouts/&quot;&gt;Una Kravets’ “Ten modern layouts in one line of CSS”&lt;/a&gt; drives home how woefully out-of-date my CSS knowledge is.&lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;I caught C as he toppled backwards in surprise while sat on his mat and, without pausing, E said: “tell your mum I saved your life.”&lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;&lt;a href=&quot;https://www.hillelwayne.com/post/frink/&quot;&gt;Hillel Wayne’s “The Frink Is Good, the Unit Is Evil”&lt;/a&gt; introduced me to &lt;a href=&quot;https://frinklang.org&quot;&gt;Frink&lt;/a&gt;, a “special purpose [language] for dealing with units”.&lt;/p&gt;

    &lt;p&gt;There’s a &lt;a href=&quot;https://futureboy.us/fsp/frink.fsp&quot;&gt;web-based interface to Frink&lt;/a&gt; available and it supports &lt;a href=&quot;https://frinklang.org/frinkdata/units.txt&quot;&gt;a dizzying number of units&lt;/a&gt;. As someone who often has to convert &lt;a href=&quot;https://twitter.com/adamliaw/status/1255608557027749888&quot;&gt;American recipe quantities&lt;/a&gt;, it will come in &lt;a href=&quot;https://futureboy.us/fsp/frink.fsp?fromVal=4+tbsp+butter&amp;amp;toVal=grams#calc&quot;&gt;very handy indeed&lt;/a&gt;.&lt;/p&gt;

    &lt;p&gt;Of course, it was named after &lt;a href=&quot;https://en.wikipedia.org/wiki/Professor_Frink&quot;&gt;Professor John Frink&lt;/a&gt;:&lt;/p&gt;

    &lt;blockquote&gt;
      &lt;p&gt;I predict that within 100 years, computers will be twice as powerful, ten thousand times larger, and so expensive that only the five richest kings of Europe will own them.&lt;/p&gt;
    &lt;/blockquote&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;After an intense week at work, I feel somewhat out of sorts and so decided to take a long, meandering walk and catch up on some &lt;a href=&quot;https://atp.fm&quot;&gt;podcasts&lt;/a&gt; while E took C to visit her aunt.&lt;/p&gt;

    &lt;p&gt;I made it as far as the local reservoir before a group of people gathered in my path made me turn back. Turns out I’m neither good at meandering nor staying outside the house for longer than half an hour.&lt;/p&gt;
  &lt;/li&gt;
&lt;/ul&gt;
</content>
</entry><entry>
  <title type="html">Weeknotes 36</title>
  <link href="https://mudge.name/2020/07/05/weeknotes-36/"/>
  <id>https://mudge.name/2020/07/05/weeknotes-36/</id>
  <published>2020-07-05T09:19:00+00:00</published>
  <updated>2020-07-05T09:19:00+00:00</updated>
  <summary type="html">Visiting other islands, comfort food TV, a market trader mystery, not understanding prescriptions and unexpected sobbing.</summary>
  <content type="html">&lt;ul&gt;
  &lt;li&gt;
    &lt;p&gt;&lt;a href=&quot;/2020/05/24/weeknotes-30/&quot;&gt;Since getting “Animal Crossing” over a month ago&lt;/a&gt;, I’ve gone through phases with it: at first, learning how to play then relentlessly &lt;a href=&quot;https://tomstu.art/weeknotes-15-what-a-ride&quot;&gt;plundering other islands for resources&lt;/a&gt; then &lt;a href=&quot;/2020/06/07/weeknotes-32/&quot;&gt;checking turnip prices twice a day and having a bad time on the Stalk Market&lt;/a&gt;. After feeling a little burnt out, I let go of all material concerns and simply fished to my heart’s content.&lt;/p&gt;

    &lt;p&gt;This week I visited some friends’ islands for the very first time and hosted &lt;a href=&quot;https://twitter.com/ShaneOnSwitch/status/1278770022081388544&quot;&gt;my first guest&lt;/a&gt;. This social aspect is entirely new to me and, having seen how beautiful others’ islands are, you will now find me happily planting imaginary flowers.&lt;/p&gt;

    &lt;p class=&quot;center&quot;&gt;&lt;a href=&quot;https://twitter.com/ShaneOnSwitch/status/1278770022081388544&quot;&gt;&lt;img src=&quot;/i/animalcrossing.jpg&quot; width=&quot;375&quot; height=&quot;211&quot; alt=&quot;&quot; /&gt;&lt;/a&gt;&lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;We had occasion to use our car for the first time in months and weren’t surprised when it failed to start. Thankfully, I’ve had to charge our car battery several times before and so painstakingly removed it from the car after reading &lt;a href=&quot;https://www.wikihow.com/Disconnect-a-Car-Battery&quot;&gt;the order in which to disconnect the two terminals&lt;/a&gt; at least six times (negative then positive and then the reverse order to reconnect).&lt;/p&gt;

    &lt;p&gt;There’s nothing like bringing a metal socket spanner toward a car battery terminal for focussing the mind.&lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;&lt;a href=&quot;/2020/06/28/weeknotes-35/&quot;&gt;Last week&lt;/a&gt; I credited &lt;a href=&quot;http://www.h-lame.com/&quot;&gt;Murray&lt;/a&gt; with describing modern web development as “putting strings into databases and getting strings out of databases” but &lt;a href=&quot;https://twitter.com/hlame/status/1277179500510543875&quot;&gt;it turns out&lt;/a&gt; he was paraphrasing &lt;a href=&quot;https://medium.com/@abscond/putting-strings-into-databases-and-then-taking-them-back-out-again-ce95c2ad2183&quot;&gt;James Darling&lt;/a&gt; who, &lt;a href=&quot;https://twitter.com/abscond/status/1277182825016557569&quot;&gt;in turn&lt;/a&gt;, was quoting &lt;a href=&quot;https://twitter.com/beng/status/1277575198338883584&quot;&gt;Ben Griffiths&lt;/a&gt;.&lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;There’s a new series of &lt;a href=&quot;https://www.netflix.com/title/80146601&quot;&gt;“Everybody Loves Raymond” creator Phil Rosenthal’s travel documentary “Somebody Feed Phil”&lt;/a&gt;. I find all food-based TV shows comforting but Rosenthal’s genuine, unbridled glee makes it especially optimistic viewing. I forgot how &lt;a href=&quot;https://twitter.com/PhilRosenthal/status/949471533994594304&quot;&gt;catchy the theme song is&lt;/a&gt;.&lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;Speaking of food TV, a recent episode of “&lt;a href=&quot;https://www.channel4.com/programmes/bake-off-the-professionals&quot;&gt;Bake Off: The Professionals&lt;/a&gt;” featured host &lt;a href=&quot;https://www.tomindeed.com&quot;&gt;Tom Allen&lt;/a&gt; posing as a market trader. When I lived in south-east London, I used to walk past a market stall selling fruit every day and the trader would bellow a single phrase:&lt;/p&gt;

    &lt;blockquote&gt;
      &lt;p&gt;Tray of bananas, sayp!&lt;/p&gt;
    &lt;/blockquote&gt;

    &lt;p&gt;For literal years, I could never decode what this last word was.&lt;/p&gt;

    &lt;p&gt;Finally, after nearly four years, I happened to hear the trader’s bark one more time:&lt;/p&gt;

    &lt;blockquote&gt;
      &lt;p&gt;Box of strawberries a pound, tray of bananas same!&lt;/p&gt;
    &lt;/blockquote&gt;

    &lt;p&gt;The mystery was solved.&lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;I decided to get a repeat prescription for my &lt;a href=&quot;/2020/03/29/weeknotes-22/&quot;&gt;occasional migraines&lt;/a&gt; and struggled to navigate the different websites and iOS apps required to talk to a doctor.&lt;/p&gt;

    &lt;ul&gt;
      &lt;li&gt;At first, I tried the &lt;a href=&quot;https://www.mygp.com&quot;&gt;myGP app&lt;/a&gt; which I’ve used to book appointments before but nothing was visible there.&lt;/li&gt;
      &lt;li&gt;I then tried to use the &lt;a href=&quot;https://www.patientaccess.com&quot;&gt;Patient Access&lt;/a&gt; website but still nothing was available.&lt;/li&gt;
      &lt;li&gt;I then followed instructions to set up the &lt;a href=&quot;https://www.nhs.uk/using-the-nhs/nhs-services/the-nhs-app/&quot;&gt;NHS app&lt;/a&gt; but, again, nothing.&lt;/li&gt;
      &lt;li&gt;I rang my local surgery and they told me I needed to use a &lt;em&gt;fourth&lt;/em&gt; service: &lt;a href=&quot;https://demo.webgp.com&quot;&gt;WebGP&lt;/a&gt;.&lt;/li&gt;
    &lt;/ul&gt;

    &lt;p&gt;After filling out a long questionnaire (and tweaking my answers so it didn’t tell me to immediately ring 999 for an ambulance), I got a call from a GP that same morning who happily created a repeat prescription for me.&lt;/p&gt;

    &lt;p&gt;However, it then became obvious I have no idea how repeat prescriptions work (a familiar fact given I was once shouted out over the phone by a receptionist for not understanding the difference between the &lt;em&gt;prescription&lt;/em&gt; and the medication itself). The doctor said they had created the prescription but what did that mean? Looking in the NHS app, I could see the prescription but there was a separate section for “orders”; did I need to order it? After finding no answers online, I went ahead and ordered it.&lt;/p&gt;

    &lt;p&gt;The next day, my order was marked as “rejected” in the NHS app and I was instructed to ring my GP. After a hilarious exchange as my local surgery attempted to verify my email address and I ran through the four separate addresses I used for each of their services, it turns out I &lt;em&gt;didn’t&lt;/em&gt; need to order anything: my prescription was waiting for me at the local pharmacy.&lt;/p&gt;

    &lt;p&gt;Utterly baffling.&lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;Whenever E and I are discussing a topic that requires some disclaimer (e.g. a relatively petty matter given the current pandemic), we’ll preface what we say with the single word “Rigby”, e.g.&lt;/p&gt;

    &lt;blockquote&gt;
      &lt;p&gt;Rigby, I wish there was more variety to our day.&lt;/p&gt;
    &lt;/blockquote&gt;

    &lt;p&gt;This &lt;a href=&quot;/2020/03/02/weeknotes-18/&quot;&gt;incomprehensive phrase&lt;/a&gt; comes from &lt;a href=&quot;https://youtu.be/IAGoLFCgNtY&quot;&gt;an episode of “Silicon Valley”&lt;/a&gt;:&lt;/p&gt;

    &lt;blockquote&gt;
      &lt;p&gt;“I know we keep saying this but even though Richard is a great guy—”&lt;/p&gt;

      &lt;p&gt;“And a brilliant coder.”&lt;/p&gt;

      &lt;p&gt;“Wonderful. Nevertheless, he is lacking in certain managerial capacities.”&lt;/p&gt;

      &lt;p&gt;“Totally fair.”&lt;/p&gt;

      &lt;p&gt;“It’s fair, right?”&lt;/p&gt;

      &lt;p&gt;“He’s still a great coder.”&lt;/p&gt;

      &lt;p&gt;“And an amazing human being!”&lt;/p&gt;

      &lt;p&gt;“OK, we have a lot of shit we want to say about him, do we have to keep prefacing it with all this nice guy stuff? I mean, if so, we’re going to be here all night. […] What if we use like a dictionary patch to compress all the nice guy stuff?”&lt;/p&gt;

      &lt;p&gt;“Like an acronym?”&lt;/p&gt;

      &lt;p&gt;“Exactly. Richard Is Great But, Y’know. RIGBY.”&lt;/p&gt;
    &lt;/blockquote&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;Despite being put off by the trailer, E and I watched “&lt;a href=&quot;https://www.netflix.com/title/80244088&quot;&gt;Eurovision Song Contest: The Story of Fire Saga”&lt;/a&gt; last night after hearing recommendations from several people.&lt;/p&gt;

    &lt;p&gt;It reduced us both to actual tears.&lt;/p&gt;
  &lt;/li&gt;
&lt;/ul&gt;
</content>
</entry><entry>
  <title type="html">Weeknotes 35</title>
  <link href="https://mudge.name/2020/06/28/weeknotes-35/"/>
  <id>https://mudge.name/2020/06/28/weeknotes-35/</id>
  <published>2020-06-28T09:40:00+00:00</published>
  <updated>2020-06-28T09:40:00+00:00</updated>
  <summary type="html">Pixels per inch, kicking up the 4d3d3d3, the importance of ventilation and genuine Computer Science.</summary>
  <content type="html">&lt;ul&gt;
  &lt;li&gt;
    &lt;p&gt;&lt;a href=&quot;https://tonsky.me/blog/monitors/&quot;&gt;Nikita Prokopov’s “Time to upgrade your monitor”&lt;/a&gt; finally convinced me to upgrade to &lt;a href=&quot;https://www.dell.com/en-uk/shop/dell-24-ultra-hd-4k-monitor-p2415q/apd/210-adzc/monitors-monitor-accessories&quot;&gt;a 4K display&lt;/a&gt; after over a decade of staring at less than &lt;a href=&quot;https://www.sven.de/dpi/&quot;&gt;100 pixels per inch&lt;/a&gt; on an old &lt;a href=&quot;https://www.benq.eu/en-eu/index.html&quot;&gt;BenQ&lt;/a&gt; monitor.&lt;/p&gt;

    &lt;p&gt;In the words of &lt;a href=&quot;https://alexspeller.com&quot;&gt;Alex&lt;/a&gt;, I’ve been &lt;a href=&quot;https://twitter.com/alexspeller/status/288661716710068224&quot;&gt;punching myself in the eyes with squares&lt;/a&gt; for too long.&lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;I was &lt;a href=&quot;https://twitter.com/siracusa/status/1274765016655372289&quot;&gt;reminded&lt;/a&gt; that “&lt;a href=&quot;https://youtu.be/MHWBEK8w_YY&quot;&gt;Celery Man&lt;/a&gt;” exists and it brought me great joy.&lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;For the past few weeks, my laptop has been increasingly struggling when running Zoom calls with multiple video streams, screen sharing, etc. with its performance dragging to a halt and the &lt;a href=&quot;https://twitter.com/Kolokodess/status/1275879372830973952&quot;&gt;fan noise approaching that of a jet engine&lt;/a&gt;.&lt;/p&gt;

    &lt;p&gt;Receiving the new monitor and seeing photos of &lt;a href=&quot;https://tomstu.art&quot;&gt;Tom&lt;/a&gt;’s setup inspired me to remove the protective plastic case from my computer and stand it on its end with the help of an old, wooden iPad stand. The roaring fan noise is now gone and my performance woes are no more.&lt;/p&gt;

    &lt;p&gt;In retrospect, it probably wasn’t a great idea covering up the few vents on the underside of my MacBook Pro for several years.&lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;A former colleague asked me for a copy of a presentation I had given at &lt;a href=&quot;https://www.altmetric.com&quot;&gt;Altmetric&lt;/a&gt; about using &lt;a href=&quot;https://en.wikipedia.org/wiki/Dijkstra%27s_algorithm&quot;&gt;Dijkstra’s algorithm&lt;/a&gt; to exhaustively map scholarly identifiers to one another (e.g. &lt;a href=&quot;https://www.doi.org&quot;&gt;DOI&lt;/a&gt;s to &lt;a href=&quot;https://pubmed.ncbi.nlm.nih.gov&quot;&gt;PubMed&lt;/a&gt; IDs to &lt;a href=&quot;http://handle.net&quot;&gt;Handle&lt;/a&gt;s to &lt;a href=&quot;https://en.wikipedia.org/wiki/International_Standard_Book_Number&quot;&gt;ISBNs&lt;/a&gt; to &lt;a href=&quot;https://arxiv.org/help/arxiv_identifier&quot;&gt;arXiv identifiers&lt;/a&gt;).&lt;/p&gt;

    &lt;p&gt;That project was so much fun to work on and led me to write &lt;a href=&quot;https://github.com/mudge/fibonacci_heap&quot;&gt;a Fibonnaci Heap implementation&lt;/a&gt;.&lt;/p&gt;

    &lt;p&gt;To paraphrase &lt;a href=&quot;http://www.h-lame.com&quot;&gt;Murray&lt;/a&gt;: so much of modern software development on the web is putting strings &lt;em&gt;in&lt;/em&gt; to databases and getting strings &lt;em&gt;out&lt;/em&gt; of databases so it is a rare treat to work on a problem that requires &lt;a href=&quot;https://en.wikipedia.org/wiki/Graph_(abstract_data_type)&quot;&gt;genuine Computer Science&lt;/a&gt; to solve.&lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;Apologies for the brevity of this week’s notes: I have started working on my &lt;a href=&quot;/2019/11/04/weeknotes-1/&quot;&gt;unfinished article about React Hooks&lt;/a&gt; again.&lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;I enjoyed my very first Fathers’ Day.&lt;/p&gt;
  &lt;/li&gt;
&lt;/ul&gt;
</content>
</entry><entry>
  <title type="html">Weeknotes 34</title>
  <link href="https://mudge.name/2020/06/21/weeknotes-34/"/>
  <id>https://mudge.name/2020/06/21/weeknotes-34/</id>
  <published>2020-06-21T10:55:00+00:00</published>
  <updated>2020-06-21T10:55:00+00:00</updated>
  <summary type="html">Shia LaBeouf, bad bakes, vertical rhythm, finding a stranger in the Alps and the streetlight effect.</summary>
  <content type="html">&lt;ul&gt;
  &lt;li&gt;
    &lt;p&gt;While making coffee with C in my arms one morning, he suddenly lunged for the &lt;a href=&quot;https://www.chemexcoffeemaker.com&quot;&gt;Chemex&lt;/a&gt;, spilling a slurry of hot water and coffee grounds as I shouted “no no no no no!” A stunned E declared she would now call me &lt;a href=&quot;https://youtu.be/8IXCK1EyP4s&quot;&gt;Shia LaBeouf&lt;/a&gt; but thankfully that has not happened.&lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;I can’t mention Shia LaBeouf without recommending &lt;a href=&quot;https://youtu.be/o0u4M6vppCI&quot;&gt;Rob Cantor’s song about the true story of an actual cannibal&lt;/a&gt;.&lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;I’ve been making sourdough bread since March 2012 &lt;a href=&quot;https://www.theguardian.com/us-news/2020/apr/19/coronavirus-stress-baking-sourdough-kneading-relax&quot;&gt;long before it became cool&lt;/a&gt;, even &lt;a href=&quot;https://tomstu.art/weeknotes-3-sweating-and-swearing&quot;&gt;encouraging others to get into it&lt;/a&gt; but my recent bakes have not been good.&lt;/p&gt;

    &lt;p&gt;My starter has been underactive and overactive, the bulk fermentation has been both too long and too short. More often than not, my bread is what &lt;a href=&quot;http://www.h-lame.com&quot;&gt;Murray&lt;/a&gt; would call “a tasty cowpat”.&lt;/p&gt;

    &lt;p&gt;As others got into baking, measuring their dough temperature with infrared thermometers and making their own blend of flour based on exacting protein levels, I was dismissive, believing only in some woolly notion of technique. Those bakers now produce consistently great loaves; each of &lt;a href=&quot;https://twitter.com/leocassarani&quot;&gt;Leo&lt;/a&gt;’s bakes is more beautiful than the last.&lt;/p&gt;

    &lt;p&gt;Perhaps I should get with the times and be less &lt;a href=&quot;https://oneforkonespoon.wordpress.com/2009/04/24/the-taste-of-your-hands/&quot;&gt;“son-mat”&lt;/a&gt; and more &lt;a href=&quot;https://en.wikipedia.org/wiki/Molecular_gastronomy&quot;&gt;molecular gastronomy&lt;/a&gt;.&lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;After correcting some typos in &lt;a href=&quot;/2020/06/14/weeknotes-33/&quot;&gt;last week’s notes&lt;/a&gt;, I enabled &lt;a href=&quot;http://vimdoc.sourceforge.net/htmldoc/spell.html&quot;&gt;Vim’s spell checking&lt;/a&gt;. The following bit of configuration now highlights any misspelt words in &lt;a href=&quot;https://daringfireball.net/projects/markdown/&quot;&gt;Markdown&lt;/a&gt; files:&lt;/p&gt;

    &lt;div class=&quot;language-vimscript highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;autocmd &lt;span class=&quot;nb&quot;&gt;BufRead&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;BufNewFile&lt;/span&gt; *&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;markdown&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;*&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;md &lt;span class=&quot;k&quot;&gt;setlocal&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;spell&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;spelllang&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;=&lt;/span&gt;en_gb
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;    &lt;/div&gt;

    &lt;p&gt;E tells me I should look into something called a “word processor”.&lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;I changed both this site and the &lt;a href=&quot;https://www.ghostcassette.com&quot;&gt;Ghost Cassette&lt;/a&gt; website to have a more consistent &lt;a href=&quot;https://zellwk.com/blog/why-vertical-rhythms/&quot;&gt;vertical rhythm&lt;/a&gt;. I found this little snippet of CSS to display a baseline grid extremely useful when trying to debug spacing issues:&lt;/p&gt;

    &lt;div class=&quot;language-css highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;nt&quot;&gt;body&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;nl&quot;&gt;background&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;linear-gradient&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;rgba&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;m&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;0.3&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;1px&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;transparent&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;1px&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
  &lt;span class=&quot;nl&quot;&gt;background-size&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;100%&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;24px&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;    &lt;/div&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;C’s latest discovery is clapping. I recommend everyone indulge in a round of applause every meal and at random times throughout the day.&lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;I’ve been listening to a lot of &lt;a href=&quot;https://phoebefuckingbridgers.com&quot;&gt;Phoebe Bridgers&lt;/a&gt; as her new album “Punisher” came out this week. I dearly hope her 2018 album “Stranger in the Alps” is named after the &lt;a href=&quot;https://youtu.be/LCcKBcZzGdA&quot;&gt;censored TV edit of “The Big Lebowski”&lt;/a&gt;.&lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;I’ve been thinking about &lt;a href=&quot;https://en.m.wikipedia.org/wiki/Streetlight_effect&quot;&gt;the streetlight effect&lt;/a&gt; and problem solving. There are &lt;a href=&quot;https://quoteinvestigator.com/2013/04/11/better-light/&quot;&gt;many versions&lt;/a&gt; of the story but here’s “&lt;a href=&quot;https://en.m.wikipedia.org/wiki/Nasreddin#Nasreddin's_ring&quot;&gt;Nasreddin’s ring&lt;/a&gt;”:&lt;/p&gt;

    &lt;blockquote&gt;
      &lt;p&gt;Mulla had lost his ring in the living room. He searched for it for a
while, but since he could not find it, he went out into the yard and
began to look there. His wife, who saw what he was doing, asked: “Mulla,
you lost your ring in the room, why are you looking for it in the yard?”
Mulla stroked his beard and said: “The room is too dark and I can’t see
very well. I came out to the courtyard to look for my ring because there
is much more light out here.”&lt;/p&gt;
    &lt;/blockquote&gt;

    &lt;p&gt;As a software developer, I wonder if there is a trap as you solve similar problems over and over again during the course of your career. Do you become more likely to take &lt;em&gt;any&lt;/em&gt; problem and turn it into a familiar one even if it’s not appropriate?&lt;/p&gt;
  &lt;/li&gt;
&lt;/ul&gt;
</content>
</entry><entry>
  <title type="html">Weeknotes 33</title>
  <link href="https://mudge.name/2020/06/14/weeknotes-33/"/>
  <id>https://mudge.name/2020/06/14/weeknotes-33/</id>
  <published>2020-06-14T11:03:54+00:00</published>
  <updated>2020-06-14T11:03:54+00:00</updated>
  <summary type="html">Generating human-friendly codes, keeping my cool running a Raspberry Pi and threatening my marriage.</summary>
  <content type="html">&lt;ul&gt;
  &lt;li&gt;
    &lt;p&gt;I’ve been researching how best to generate unique, human-friendly codes for a new project.&lt;/p&gt;

    &lt;p&gt;&lt;a href=&quot;https://medium.com/@scottm&quot;&gt;Scott&lt;/a&gt; recommended we take a look at &lt;a href=&quot;https://www.crockford.com/base32.html&quot;&gt;Douglas Crockford’s “Base 32” system&lt;/a&gt;:&lt;/p&gt;

    &lt;blockquote&gt;
      &lt;p&gt;The encoding scheme is required to&lt;/p&gt;

      &lt;ul&gt;
        &lt;li&gt;Be human readable and machine readable.&lt;/li&gt;
        &lt;li&gt;Be compact. Humans have difficulty in manipulating long strings of arbitrary symbols.&lt;/li&gt;
        &lt;li&gt;Be error resistant. Entering the symbols must not require keyboarding gymnastics.&lt;/li&gt;
        &lt;li&gt;Be pronounceable. Humans should be able to accurately transmit the symbols to other humans using a telephone.&lt;/li&gt;
      &lt;/ul&gt;
    &lt;/blockquote&gt;

    &lt;p&gt;Crockford excludes the letters I, L and O to avoid ambiguity with the numbers 1 and 0. Less intuitively, he also excludes the letter U as this helps avoid accidentally generating codes that contain English obscenities: a problem I would have missed entirely.&lt;/p&gt;

    &lt;p&gt;We looked into the implementation of &lt;a href=&quot;https://api.rubyonrails.org/classes/SecureRandom.html#method-c-base58&quot;&gt;Rails’ &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;SecureRandom.base58&lt;/code&gt;&lt;/a&gt;:&lt;/p&gt;

    &lt;div class=&quot;language-ruby highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;k&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;self&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;base58&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;n&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;16&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
  &lt;span class=&quot;no&quot;&gt;SecureRandom&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;random_bytes&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;n&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;).&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;unpack&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;C*&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;).&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;map&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;do&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;|&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;byte&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;|&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;idx&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;byte&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;%&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;64&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;idx&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;SecureRandom&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;random_number&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;58&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;idx&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&amp;gt;=&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;58&lt;/span&gt;
    &lt;span class=&quot;no&quot;&gt;BASE58_ALPHABET&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;idx&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt;
  &lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;join&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;    &lt;/div&gt;

    &lt;p&gt;I wondered why they didn’t simply &lt;a href=&quot;https://en.wikipedia.org/wiki/Modulo_operation&quot;&gt;modulo&lt;/a&gt; &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;byte&lt;/code&gt; with 58 like so:&lt;/p&gt;

    &lt;div class=&quot;language-ruby highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;k&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;self&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;base58&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;n&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;16&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
  &lt;span class=&quot;no&quot;&gt;SecureRandom&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;random_bytes&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;n&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;).&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;unpack&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;C*&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;).&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;map&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;do&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;|&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;byte&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;|&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;idx&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;byte&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;%&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;58&lt;/span&gt;
    &lt;span class=&quot;no&quot;&gt;BASE58_ALPHABET&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;idx&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt;
  &lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;join&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;    &lt;/div&gt;

    &lt;p&gt;Scott found the explanation in &lt;a href=&quot;https://github.com/rails/rails/pull/25734&quot;&gt;a four-year old pull request to simplify the implementation&lt;/a&gt;: &lt;a href=&quot;https://cmvandrevala.wordpress.com/2016/09/24/modulo-bias-when-generating-random-numbers/&quot;&gt;modulo bias&lt;/a&gt;. Naïvely simplifying the implementation would mean that the “random” choice would actually be biased towards picking some characters more than others.&lt;/p&gt;

    &lt;p&gt;Coincidentally, &lt;a href=&quot;https://github.com/rails/rails/pull/39511&quot;&gt;someone else suggested the same change again&lt;/a&gt; 12 days ago.&lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;After &lt;a href=&quot;/2020/06/07/weeknotes-32/&quot;&gt;last week’s big migration to Fargate&lt;/a&gt;, I wanted to double check the new &lt;a href=&quot;https://www.docker.com&quot;&gt;Docker&lt;/a&gt; version of our application wasn’t writing to disk (in order to remain &lt;a href=&quot;https://12factor.net/processes&quot;&gt;stateless&lt;/a&gt;).&lt;/p&gt;

    &lt;p&gt;The &lt;a href=&quot;https://docs.docker.com/engine/reference/commandline/diff/&quot;&gt;docker diff&lt;/a&gt; command handily told me exactly what a container was changing on its file system when running and revealed that &lt;a href=&quot;https://www.phusionpassenger.com/library/admin/nginx/log_file/#location-of-the-log-file&quot;&gt;Phusion Passenger was logging to a file when I didn’t expect it to&lt;/a&gt;.&lt;/p&gt;

    &lt;div class=&quot;language-console highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;gp&quot;&gt;$&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;docker diff 1fdfd1f54c1b
&lt;span class=&quot;go&quot;&gt;A /var/log/nginx/error.log
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;    &lt;/div&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;E has been binging the French spy drama “&lt;a href=&quot;https://www.imdb.com/title/tt4063800/&quot;&gt;The Bureau&lt;/a&gt;” but I can’t help but think of &lt;a href=&quot;https://youtu.be/WjjXTr9F77g&quot;&gt;The Day Today’s BBC drama of the same name&lt;/a&gt; every time I see her watching it:&lt;/p&gt;

    &lt;blockquote&gt;
      &lt;p&gt;This is supposed to be a high class &lt;em&gt;bureau de change&lt;/em&gt;, not some two-bit Punch and Judy show down on the seafront at Margate!&lt;/p&gt;
    &lt;/blockquote&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;&lt;a href=&quot;https://www.jeffgeerling.com/blog/2019/best-way-keep-your-cool-running-raspberry-pi-4&quot;&gt;Jeff Geerling’s “The best way to keep your cool running a Raspberry Pi 4”&lt;/a&gt; convinced me to buy the &lt;a href=&quot;https://thepihut.com/products/flirc-raspberry-pi-4-case&quot;&gt;FLIRC case from The Pi Hut&lt;/a&gt; and he’s right: my Pi runs much cooler than my previous setup with a &lt;a href=&quot;https://thepihut.com/products/xl-raspberry-pi-4-heatsink&quot;&gt;large heatsink&lt;/a&gt; inside &lt;a href=&quot;https://thepihut.com/products/raspberry-pi-4-case&quot;&gt;the official case&lt;/a&gt;:&lt;/p&gt;

    &lt;div class=&quot;language-console highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;gp&quot;&gt;mudge@raspberrypi:~ $&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;cat&lt;/span&gt; /sys/class/thermal/thermal_zone0/temp
&lt;span class=&quot;go&quot;&gt;36998
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;    &lt;/div&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;In a move that threatens my marriage, I have spent far too much time this weekend trying to generate a custom &lt;a href=&quot;https://www.raspbian.org&quot;&gt;Raspbian&lt;/a&gt; image that already has my &lt;a href=&quot;/2019/11/12/using-a-raspberry-pi-for-time-machine/&quot;&gt;Time Machine backups&lt;/a&gt;, &lt;a href=&quot;/2020/06/07/weeknotes-32/&quot;&gt;Unbound DNS sinkhole&lt;/a&gt; and &lt;a href=&quot;https://github.com/mikebrady/shairport-sync&quot;&gt;AirPlay receiver&lt;/a&gt; set up on it.&lt;/p&gt;

    &lt;p&gt;Thankfully, the &lt;a href=&quot;https://github.com/RPi-Distro/pi-gen&quot;&gt;tool used to create the raspberrypi.org images&lt;/a&gt; is open-source and you can find the results of my indulgent noodling in &lt;a href=&quot;https://github.com/mudge/pi-gen&quot;&gt;my fork of pi-gen&lt;/a&gt;.&lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;&lt;a href=&quot;/2020/06/07/weeknotes-32/&quot;&gt;Last week’s investment&lt;/a&gt; paid off.&lt;/p&gt;

    &lt;p class=&quot;center&quot;&gt;&lt;img src=&quot;/i/turnips.jpg&quot; width=&quot;375&quot; height=&quot;375&quot; alt=&quot;I sold a batch of my turnips for 803,400 Bells.&quot; /&gt;&lt;/p&gt;
  &lt;/li&gt;
&lt;/ul&gt;
</content>
</entry><entry>
  <title type="html">Weeknotes 32</title>
  <link href="https://mudge.name/2020/06/07/weeknotes-32/"/>
  <id>https://mudge.name/2020/06/07/weeknotes-32/</id>
  <published>2020-06-07T14:18:00+00:00</published>
  <updated>2020-06-07T14:18:00+00:00</updated>
  <summary type="html">Replacing a Pi-hole with Unbound and ISC DHCP, speeding up deployments and a reckless root vegetable investment.</summary>
  <content type="html">&lt;ul&gt;
  &lt;li&gt;
    &lt;p&gt;&lt;a href=&quot;https://blog.may.yt/2020/06/pdns-sinkhole/&quot;&gt;Thom May’s “Sinkholing with PowerDNS Recursor”&lt;/a&gt; made me wonder why &lt;a href=&quot;/2020/04/12/weeknotes-24/&quot;&gt;I run both a Pi-hole &lt;em&gt;and&lt;/em&gt; our own Unbound DNS resolver&lt;/a&gt;. Perhaps I could implement &lt;a href=&quot;https://firebog.net&quot;&gt;blocklists&lt;/a&gt; and &lt;a href=&quot;https://github.com/anudeepND/whitelist&quot;&gt;recommended exceptions&lt;/a&gt; using only &lt;a href=&quot;https://nlnetlabs.nl/documentation/unbound/unbound.conf/&quot;&gt;Unbound configuration&lt;/a&gt;?&lt;/p&gt;

    &lt;p&gt;Our Pi-hole currently serves two purposes:&lt;/p&gt;

    &lt;ol&gt;
      &lt;li&gt;It runs &lt;a href=&quot;https://discourse.pi-hole.net/t/how-do-i-use-pi-holes-built-in-dhcp-server-and-why-would-i-want-to/3026&quot;&gt;its built-in DHCP server&lt;/a&gt; so every device connecting to our network uses the Pi-hole’s DNS server rather than my ISP’s DNS servers (as &lt;a href=&quot;https://community.bt.com/t5/Archive-Staging/Manual-DNS-settings-please-for-home-hub-5/td-p/1226381&quot;&gt;this isn’t configurable on my router&lt;/a&gt;).&lt;/li&gt;
      &lt;li&gt;It runs its &lt;a href=&quot;https://docs.pi-hole.net/ftldns/blockingmode/&quot;&gt;own &lt;abbrev title=&quot;Faster Than Light&quot;&gt;FTL&lt;/abbrev&gt;DNS server to block access to domains&lt;/a&gt;.&lt;/li&gt;
    &lt;/ol&gt;

    &lt;p&gt;As Thom points out in his post, given only those two roles and the fact I’m already running &lt;a href=&quot;https://nlnetlabs.nl/projects/unbound/about/&quot;&gt;Unbound&lt;/a&gt;, the required tech stack for the Pi-hole (&lt;a href=&quot;https://www.php.net&quot;&gt;PHP&lt;/a&gt;, &lt;a href=&quot;https://www.lighttpd.net&quot;&gt;Lighttpd&lt;/a&gt;, &lt;a href=&quot;http://www.thekelleys.org.uk/dnsmasq/doc.html&quot;&gt;Dnsmasq&lt;/a&gt;, etc.) seems excessive.&lt;/p&gt;

    &lt;p&gt;I decided to replace the Pi-hole with only two pieces of software:&lt;/p&gt;

    &lt;ol&gt;
      &lt;li&gt;Use &lt;a href=&quot;https://www.isc.org/dhcp/&quot;&gt;ISC DHCP&lt;/a&gt; as a standalone DHCP server, replacing the Pi-hole’s Dnsmasq-based DHCP server.&lt;/li&gt;
      &lt;li&gt;Use the existing Unbound DNS server to block unwanted domains.&lt;/li&gt;
    &lt;/ol&gt;

    &lt;p&gt;&lt;a href=&quot;https://firebog.net&quot;&gt;The Firebog’s “The Big Blocklist Collection”&lt;/a&gt; contains a curated list of domains to block, all in the following format:&lt;/p&gt;

    &lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;0.0.0.0 some.bad.domain.here
0.0.0.0 another.bad.domain.here
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;    &lt;/div&gt;

    &lt;p&gt;In order to block these domains with Unbound, we can use the &lt;a href=&quot;https://nlnetlabs.nl/documentation/unbound/unbound.conf/#local-zone&quot;&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;local-zone&lt;/code&gt;&lt;/a&gt; and &lt;a href=&quot;https://nlnetlabs.nl/documentation/unbound/unbound.conf/#local-zone&quot;&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;local-data&lt;/code&gt;&lt;/a&gt; configuration options. The documentation for &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;local-zone&lt;/code&gt;’s &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;redirect&lt;/code&gt; type contains the following example:&lt;/p&gt;

    &lt;blockquote&gt;
      &lt;p&gt;It can be used to redirect a domain to return a different address record
to the end user, with &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;local-zone: &quot;example.com.&quot; redirect&lt;/code&gt; and
&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;local-data: &quot;example.com. A 127.0.0.1&quot;&lt;/code&gt; queries for www.example.com and
www.foo.example.com are redirected, so that users with web browsers
cannot access sites with suffix example.com.&lt;/p&gt;
    &lt;/blockquote&gt;

    &lt;p&gt;We’ll &lt;a href=&quot;https://docs.pi-hole.net/ftldns/blockingmode/#pi-holes-unspecified-ip-blocking-default&quot;&gt;copy Pi-hole’s unspecified IP blocking strategy&lt;/a&gt; and return &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;0.0.0.0&lt;/code&gt; for any A record lookups and &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;::&lt;/code&gt; for any AAAA record lookups:&lt;/p&gt;

    &lt;blockquote&gt;
      &lt;p&gt;Following &lt;a href=&quot;https://tools.ietf.org/html/rfc3513#section-2.5.2&quot;&gt;RFC 3513, Internet Protocol Version 6 (IPv6) Addressing
Architecture, section
2.5.2&lt;/a&gt;, the address
&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;0:0:0:0:0:0:0:0&lt;/code&gt; (or &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;::&lt;/code&gt; for short) is the unspecified address. It must
never be assigned to any node and indicates the absence of an address.
Following RFC1122, section 3.2, the address &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;0.0.0.0&lt;/code&gt; can be understood
as the IPv4 equivalent of &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;::&lt;/code&gt;.&lt;/p&gt;
    &lt;/blockquote&gt;

    &lt;p&gt;So to block the two example domains above, we would need the following Unbound configuration:&lt;/p&gt;

    &lt;div class=&quot;language-config highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;n&quot;&gt;local&lt;/span&gt;-&lt;span class=&quot;n&quot;&gt;zone&lt;/span&gt;: &lt;span class=&quot;s2&quot;&gt;&quot;some.bad.domain.here.&quot;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;redirect&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;local&lt;/span&gt;-&lt;span class=&quot;n&quot;&gt;data&lt;/span&gt;: &lt;span class=&quot;s2&quot;&gt;&quot;some.bad.domain.here. IN A 0.0.0.0&quot;&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;local&lt;/span&gt;-&lt;span class=&quot;n&quot;&gt;data&lt;/span&gt;: &lt;span class=&quot;s2&quot;&gt;&quot;some.bad.domain.here. IN AAAA ::&quot;&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;local&lt;/span&gt;-&lt;span class=&quot;n&quot;&gt;zone&lt;/span&gt;: &lt;span class=&quot;s2&quot;&gt;&quot;another.bad.domain.here.&quot;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;redirect&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;local&lt;/span&gt;-&lt;span class=&quot;n&quot;&gt;data&lt;/span&gt;: &lt;span class=&quot;s2&quot;&gt;&quot;another.bad.domain.here. IN A 0.0.0.0&quot;&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;local&lt;/span&gt;-&lt;span class=&quot;n&quot;&gt;data&lt;/span&gt;: &lt;span class=&quot;s2&quot;&gt;&quot;another.bad.domain.here. IN AAAA ::&quot;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;    &lt;/div&gt;

    &lt;p&gt;We can do this by stitching together some basic command-line tools:&lt;/p&gt;

    &lt;div class=&quot;language-bash highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;curl &lt;span class=&quot;nt&quot;&gt;-sSf&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;https://v.firebog.net/hosts/static/w3kbl.txt&quot;&lt;/span&gt; | &lt;span class=&quot;c&quot;&gt;# Download a blocklist&lt;/span&gt;
  &lt;span class=&quot;nb&quot;&gt;grep&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;'^0\.0\.0\.0'&lt;/span&gt; | &lt;span class=&quot;c&quot;&gt;# Filter out any comments, etc. that aren't rules&lt;/span&gt;
  &lt;span class=&quot;nb&quot;&gt;tr&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;-d&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;'\r'&lt;/span&gt; |         &lt;span class=&quot;c&quot;&gt;# Normalize line endings by removing carriage returns&lt;/span&gt;
  &lt;span class=&quot;nb&quot;&gt;sort&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;-u&lt;/span&gt; |            &lt;span class=&quot;c&quot;&gt;# Remove any duplicates&lt;/span&gt;
  &lt;span class=&quot;nb&quot;&gt;awk&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;'{print &quot;local-zone: \&quot;&quot;$2&quot;.\&quot; redirect\nlocal-data: \&quot;&quot;$2&quot;. IN A 0.0.0.0\&quot;\nlocal-data: \&quot;&quot;$2&quot;. IN AAAA ::\&quot;&quot;}'&lt;/span&gt; | &lt;span class=&quot;c&quot;&gt;# Convert to Unbound configuration&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;    &lt;/div&gt;

    &lt;p&gt;(You can find my &lt;a href=&quot;https://gist.github.com/mudge/eaff0a816e8ee49046e263b0a6e99fe7#file-blocklist-sh&quot;&gt;full script to download and re-format all ticked blocklists on GitHub&lt;/a&gt;.)&lt;/p&gt;

    &lt;p&gt;As the blocklists contain domains which break essential functionality when blocked, we want to explicitly allow any domains on &lt;a href=&quot;https://github.com/anudeepND/whitelist&quot;&gt;Anudeep’s list of exceptions&lt;/a&gt;.&lt;/p&gt;

    &lt;p&gt;To do this, we can use &lt;a href=&quot;https://nlnetlabs.nl/documentation/unbound/unbound.conf/#local-zone&quot;&gt;the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;always_transparent&lt;/code&gt; type of &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;local-zone&lt;/code&gt;&lt;/a&gt;:&lt;/p&gt;

    &lt;blockquote&gt;
      &lt;p&gt;Like transparent, but ignores local data and resolves normally.&lt;/p&gt;
    &lt;/blockquote&gt;

    &lt;p&gt;So, if we wanted to allow traffic to &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;some.bad.domain.here&lt;/code&gt; even though it is already in our configuration, we need to add the following to our Unbound configuration before our blocklist:&lt;/p&gt;

    &lt;div class=&quot;language-config highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;n&quot;&gt;local&lt;/span&gt;-&lt;span class=&quot;n&quot;&gt;zone&lt;/span&gt;: &lt;span class=&quot;s2&quot;&gt;&quot;some.bad.domain.here.&quot;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;always_transparent&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;    &lt;/div&gt;

    &lt;p&gt;This will ensure we ignore our &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;redirect&lt;/code&gt; to &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;local-data&lt;/code&gt; and look the domain up normally.&lt;/p&gt;

    &lt;p&gt;Again, we can do &lt;a href=&quot;https://gist.github.com/mudge/eaff0a816e8ee49046e263b0a6e99fe7#file-safelist-sh&quot;&gt;some basic scripting to convert the list into configuration for us&lt;/a&gt;:&lt;/p&gt;

    &lt;div class=&quot;language-bash highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;curl &lt;span class=&quot;nt&quot;&gt;-sSf&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;https://raw.githubusercontent.com/anudeepND/whitelist/master/domains/whitelist.txt&quot;&lt;/span&gt; |
  &lt;span class=&quot;nb&quot;&gt;sort&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;-u&lt;/span&gt; | &lt;span class=&quot;c&quot;&gt;# Remove any duplicates&lt;/span&gt;
  &lt;span class=&quot;nb&quot;&gt;awk&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;'{print &quot;local-zone: \&quot;&quot;$1&quot;.\&quot; always_transparent&quot;}'&lt;/span&gt; &lt;span class=&quot;c&quot;&gt;# Convert to Unbound configuration&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;    &lt;/div&gt;

    &lt;p&gt;If we save those two lists into separate configuration files called &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;01-safelist.conf&lt;/code&gt; and &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;02-blocklist.conf&lt;/code&gt; under &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;/etc/unbound/lists.d&lt;/code&gt;, we can &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;include&lt;/code&gt; them in our Unbound &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;server&lt;/code&gt; definition (e.g. under &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;/etc/unbound/unbound.conf.d&lt;/code&gt;):&lt;/p&gt;

    &lt;div class=&quot;language-config highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;n&quot;&gt;server&lt;/span&gt;:
  &lt;span class=&quot;c&quot;&gt;# ...other configuration here
&lt;/span&gt;
  &lt;span class=&quot;c&quot;&gt;# Include blocklist and safelist
&lt;/span&gt;  &lt;span class=&quot;n&quot;&gt;include&lt;/span&gt;: /&lt;span class=&quot;n&quot;&gt;etc&lt;/span&gt;/&lt;span class=&quot;n&quot;&gt;unbound&lt;/span&gt;/&lt;span class=&quot;n&quot;&gt;lists&lt;/span&gt;.&lt;span class=&quot;n&quot;&gt;d&lt;/span&gt;/*.&lt;span class=&quot;n&quot;&gt;conf&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;    &lt;/div&gt;

    &lt;p&gt;We can use &lt;a href=&quot;https://nlnetlabs.nl/documentation/unbound/unbound-checkconf/&quot;&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;unbound-checkconf&lt;/code&gt;&lt;/a&gt; to ensure we don’t have any syntax errors and &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;service unbound reload&lt;/code&gt; to load our new configuration if you’re using &lt;a href=&quot;https://nlnetlabs.nl/documentation/unbound/unbound.conf/#control-enable&quot;&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;control-enable&lt;/code&gt;&lt;/a&gt;, otherwise &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;service unbound restart&lt;/code&gt;.&lt;/p&gt;

    &lt;p&gt;We can test this by doing a lookup of a blocked domain with &lt;a href=&quot;https://en.wikipedia.org/wiki/Dig_(command)&quot;&gt;dig&lt;/a&gt;:&lt;/p&gt;

    &lt;div class=&quot;language-console highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;gp&quot;&gt;~&amp;gt;&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;dig @127.0.0.1 doubleclick.net +short
&lt;span class=&quot;go&quot;&gt;0.0.0.0
&lt;/span&gt;&lt;span class=&quot;gp&quot;&gt;~&amp;gt;&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;dig @127.0.0.1 doubleclick.net AAAA +short
&lt;span class=&quot;go&quot;&gt;::
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;    &lt;/div&gt;

    &lt;p&gt;And check that any exceptions still return results:&lt;/p&gt;

    &lt;div class=&quot;language-console highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;gp&quot;&gt;~&amp;gt;&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;dig @127.0.0.1 cdn.optimizely.com +short
&lt;span class=&quot;go&quot;&gt;cdn.o6.edgekey.net.
e5048.dsca.akamaiedge.net.
23.59.68.248
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;    &lt;/div&gt;

    &lt;p&gt;And with that, I could run &lt;a href=&quot;https://docs.pi-hole.net/core/pihole-command/#uninstall&quot;&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;pihole uninstall&lt;/code&gt;&lt;/a&gt;.&lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;I spent a good part of the week working with &lt;a href=&quot;https://www.scalefactory.com&quot;&gt;The Scale Factory&lt;/a&gt; to migrate a project to &lt;a href=&quot;https://aws.amazon.com/fargate/&quot;&gt;AWS Fargate&lt;/a&gt; using the &lt;a href=&quot;https://circleci.com/orbs/registry/orb/circleci/aws-ecr&quot;&gt;CircleCI Amazon Elastic Container Registry&lt;/a&gt; and &lt;a href=&quot;https://circleci.com/orbs/registry/orb/circleci/aws-ecs&quot;&gt;Elastic Container Service&lt;/a&gt; &lt;a href=&quot;https://circleci.com/docs/2.0/orb-intro/&quot;&gt;Orbs&lt;/a&gt;.&lt;/p&gt;

    &lt;p&gt;It all felt rather familiar given how similar Fargate is to &lt;a href=&quot;https://www.hashicorp.com/products/nomad/&quot;&gt;Hashicorp Nomad&lt;/a&gt; and my experience &lt;a href=&quot;https://technicallyshane.com/2020/01/14/week2.html&quot;&gt;kicking off a migration to Nomad at Altmetric&lt;/a&gt;.&lt;/p&gt;

    &lt;p&gt;&lt;a href=&quot;https://twitter.com/matt_macleod&quot;&gt;Matt&lt;/a&gt; wrote &lt;a href=&quot;https://github.com/PowerRhino/rocksteady&quot;&gt;Rocksteady&lt;/a&gt; to handle orchestrating &lt;a href=&quot;https://www.nomadproject.io/docs/job-specification/&quot;&gt;jobspec&lt;/a&gt; updates when deploying but thankfully, this is provided out-of-the-box by the &lt;a href=&quot;https://circleci.com/orbs/registry/orb/circleci/aws-ecs#jobs-deploy-service-update&quot;&gt;AWS ECS Orb’s &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;deploy-service-update&lt;/code&gt; job&lt;/a&gt;.&lt;/p&gt;

    &lt;p&gt;After all these changes, I’m hoping I will have cut down the total time to deploy (meaning from the start of our CI build to running our test suite, deploying to staging and then deploying to production) from over 34 minutes to 13 minutes.&lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;&lt;a href=&quot;https://turnipprophet.io?prices=106.90.85.82.78.73.70.66.63....&amp;amp;pattern=3&quot;&gt;After a bad week on the Stalk Market&lt;/a&gt;, I have now spent my entire life savings in bells on turnips.&lt;/p&gt;
  &lt;/li&gt;
&lt;/ul&gt;
</content>
</entry><entry>
  <title type="html">Weeknotes 31</title>
  <link href="https://mudge.name/2020/05/31/weeknotes-31/"/>
  <id>https://mudge.name/2020/05/31/weeknotes-31/</id>
  <published>2020-05-31T10:12:00+00:00</published>
  <updated>2020-05-31T10:12:00+00:00</updated>
  <summary type="html">Unexpected socialising; your mission, should you choose to accept it; computer means and a lifelong commitment.</summary>
  <content type="html">&lt;ul&gt;
  &lt;li&gt;
    &lt;p&gt;&lt;a href=&quot;https://www.polygon.com/2020/5/26/21270653/animal-crossing-new-horizons-turnips-daisy-mae-acnh-nintendo-switch-stress-prices&quot;&gt;The stress of buying turnips&lt;/a&gt; may have impacted the quality of &lt;a href=&quot;/2020/05/24/weeknotes-30/&quot;&gt;last week’s notes&lt;/a&gt;.&lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;After putting C to bed, E said “I wish we could just sit outside and have a glass of wine.” Thanks to the range of &lt;a href=&quot;/2020/03/08/weeknotes-19/&quot;&gt;our baby monitor&lt;/a&gt;, we did exactly that.&lt;/p&gt;

    &lt;p&gt;While setting up, our upstairs neighbour walked by and invited us to join them further up our driveway. We ended up chatting the evening away from socially distanced camping chairs, contactlessly sharing our last four After Eight mints and giving directions to lost Deliveroo riders.&lt;/p&gt;

    &lt;p&gt;I didn’t realise how much I miss socialising in person with people outside our household.&lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;I don’t know why but E and I have been making our way through the “Mission: Impossible” films.&lt;/p&gt;

    &lt;p&gt;So far, we’ve watched &lt;a href=&quot;https://www.imdb.com/title/tt0117060/&quot;&gt;Brian De Palma’s “Mission: Impossible”&lt;/a&gt;, &lt;a href=&quot;https://www.imdb.com/title/tt0120755/&quot;&gt;John Woo’s “Mission: Impossible II”&lt;/a&gt;, &lt;a href=&quot;https://www.imdb.com/title/tt0317919/&quot;&gt;J.J. Abrams’ “Mission: Impossible III”&lt;/a&gt; and &lt;a href=&quot;https://www.imdb.com/title/tt1229238/&quot;&gt;Brad Bird’s “Mission: Impossible - Ghost Protocol”&lt;/a&gt;.&lt;/p&gt;

    &lt;p&gt;The second one has not aged well.&lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;I made bread this weekend after a short break from baking.&lt;/p&gt;

    &lt;p class=&quot;center&quot;&gt;&lt;img src=&quot;/i/bread.jpg&quot; width=&quot;375&quot; height=&quot;375&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;

    &lt;p&gt;While our &lt;a href=&quot;/2020/03/15/weeknotes-20/&quot;&gt;large quantity of flour&lt;/a&gt; means I can’t deviate from &lt;a href=&quot;https://tartinebakery.com/stories/country-bread&quot;&gt;my usual recipe&lt;/a&gt;, &lt;a href=&quot;https://twitter.com/leocassarani&quot;&gt;Leo&lt;/a&gt; sharing a &lt;a href=&quot;https://www.reddit.com/r/Breadit/comments/glzuiz/heres_another_video_of_me_shaping_sourdough_i/?utm_source=share&amp;amp;utm_medium=web2x&quot;&gt;video of a baker shaping sourdough&lt;/a&gt; has inspired me to try different shaping techniques.&lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;I have a file called &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;budget.rb&lt;/code&gt; that contains all our household expenses and calculates how much money we spend every month. I often tinker with it and decided to switch to using the &lt;a href=&quot;https://en.wikipedia.org/wiki/Geometric_mean&quot;&gt;geometric mean&lt;/a&gt; instead of the typical &lt;a href=&quot;https://en.wikipedia.org/wiki/Arithmetic_mean&quot;&gt;arithmetic mean&lt;/a&gt; for some averages. (Despite E dismissing anything other than the arithmetic mean, &lt;a href=&quot;https://en.wikipedia.org/wiki/Median&quot;&gt;median&lt;/a&gt; and &lt;a href=&quot;https://en.wikipedia.org/wiki/Mode_(statistics)&quot;&gt;mode&lt;/a&gt; as a “computer mean”.)&lt;/p&gt;

    &lt;p&gt;This led me to write &lt;a href=&quot;https://gist.github.com/mudge/a653a1fb837e108a5501937c456f4412&quot;&gt;a Ruby refinement to add all three Pythagorean means to &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Enumerable&lt;/code&gt; objects&lt;/a&gt;, e.g.&lt;/p&gt;

    &lt;div class=&quot;language-ruby highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;n&quot;&gt;using&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;PythagoreanMeans&lt;/span&gt;

&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;2&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;3&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;4&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;5&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;].&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;arithmetic_mean&lt;/span&gt;
&lt;span class=&quot;c1&quot;&gt;# =&amp;gt; 3&lt;/span&gt;

&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;2&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;3&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;4&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;5&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;].&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;geometric_mean&lt;/span&gt;
&lt;span class=&quot;c1&quot;&gt;# =&amp;gt; ~2.61&lt;/span&gt;

&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;2&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;3&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;4&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;5&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;].&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;harmonic_mean&lt;/span&gt;
&lt;span class=&quot;c1&quot;&gt;# =&amp;gt; ~2.18&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;    &lt;/div&gt;

    &lt;p&gt;I’ve never written a &lt;a href=&quot;https://docs.ruby-lang.org/en/2.7.0/syntax/refinements_rdoc.html&quot;&gt;refinement&lt;/a&gt; before and immediately encountered &lt;a href=&quot;https://bugs.ruby-lang.org/issues/16852&quot;&gt;a bug in Ruby 2.7 meaning you can’t refine the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Enumerable&lt;/code&gt; module&lt;/a&gt;. Despite being four years old, &lt;a href=&quot;https://interblah.net/why-is-nobody-using-refinements&quot;&gt;James Adam’s “Why is nobody using Refinements?”&lt;/a&gt; is still as relevant as ever.&lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;C got his first teeth.&lt;/p&gt;

    &lt;p&gt;Despite months of asking ourselves “could he be teething?” whenever he was irrationally grumpy, it was still a surprise to see them. This means we have to brush his teeth twice daily. So begins a lifelong &lt;a href=&quot;https://natbuckley.co.uk/2019/06/02/weeknotes-20-the-wretched-task/&quot;&gt;wretched task&lt;/a&gt;.&lt;/p&gt;
  &lt;/li&gt;
&lt;/ul&gt;
</content>
</entry><entry>
  <title type="html">Weeknotes 30</title>
  <link href="https://mudge.name/2020/05/24/weeknotes-30/"/>
  <id>https://mudge.name/2020/05/24/weeknotes-30/</id>
  <published>2020-05-24T14:06:00+00:00</published>
  <updated>2020-05-24T14:06:00+00:00</updated>
  <summary type="html">A birthday, the island of Custard, more public keys and a new earworm.</summary>
  <content type="html">&lt;ul&gt;
  &lt;li&gt;
    &lt;p&gt;I successfully completed another full orbit around the sun. To celebrate, E, C and I went for our first “&lt;a href=&quot;https://www.gov.uk/government/publications/coronavirus-outbreak-faqs-what-you-can-and-cant-do/coronavirus-outbreak-faqs-what-you-can-and-cant-do&quot;&gt;stay alert&lt;/a&gt;” picnic in the park. While it was surprisingly busy for a weekday, it was great to be outside for the longest time since lockdown began.&lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;As an antidote to &lt;a href=&quot;/2020/05/03/weeknotes-27/&quot;&gt;the stress of Doom Eternal&lt;/a&gt;, &lt;a href=&quot;http://willhigo.com&quot;&gt;Will&lt;/a&gt; and &lt;a href=&quot;http://andrewlitt.co.uk&quot;&gt;Andrew&lt;/a&gt; gave me the gift of “&lt;a href=&quot;https://www.animal-crossing.com/new-horizons/&quot;&gt;Animal Crossing: New Horizons&lt;/a&gt;”. I don’t think I’ve played a game that requires waiting real days for progression before but the enforced slow pace is a welcome change. My island is called Custard and I now understand more of &lt;a href=&quot;https://twitter.com/tomstuart/status/1261942415553728512&quot;&gt;Tom’s tweets&lt;/a&gt;.&lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;As a birthday treat, E made &lt;a href=&quot;https://www.theguardian.com/lifeandstyle/wordofmouth/2017/mar/30/how-to-cook-the-perfect-miso-ramen&quot;&gt;Felicity Cloake’s perfect miso ramen&lt;/a&gt; and it was fantastic.&lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;Following &lt;a href=&quot;/2020/05/17/weeknotes-29/&quot;&gt;last week’s adventures with GPG&lt;/a&gt;, &lt;a href=&quot;https://www.instagram.com/_weszlem/&quot;&gt;Maciej&lt;/a&gt; recommended &lt;a href=&quot;https://github.com/drduh/YubiKey-Guide/blob/master/README.md#creating-keys&quot;&gt;drduh’s guide to using YubiKey for GPG and SSH&lt;/a&gt;. Even if you don’t have a &lt;a href=&quot;https://www.yubico.com/products/yubikey-hardware/&quot;&gt;YubiKey&lt;/a&gt;, it is a thorough guide to setting up &lt;a href=&quot;https://github.com/drduh/YubiKey-Guide#master-key&quot;&gt;a single GPG master key&lt;/a&gt; and single-purpose &lt;a href=&quot;https://github.com/drduh/YubiKey-Guide#sub-keys&quot;&gt;sub-keys&lt;/a&gt;.&lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;I went on a related adventure to &lt;a href=&quot;https://blog.g3rt.nl/upgrade-your-ssh-keys.html&quot;&gt;upgrade my SSH keys from a sole 2048-bit RSA key to a 4096-bit RSA key and a Ed25519 key&lt;/a&gt;.&lt;/p&gt;

    &lt;p&gt;&lt;a href=&quot;https://twitter.com/leocassarani&quot;&gt;Leo&lt;/a&gt; pointed out you can get the public SSH keys for any GitHub user by appending &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;.keys&lt;/code&gt; to their profile URL, e.g. &lt;a href=&quot;https://github.com/mudge.keys&quot;&gt;https://github.com/mudge.keys&lt;/a&gt;. You can also use &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;.gpg&lt;/code&gt; to get their public GPG keys, e.g. &lt;a href=&quot;https://github.com/mudge.gpg&quot;&gt;https://github.com/mudge.gpg&lt;/a&gt;.&lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;&lt;a href=&quot;https://technicallyshane.com&quot;&gt;Shane&lt;/a&gt; &lt;a href=&quot;https://twitter.com/shamess/status/1261981790794850305&quot;&gt;told me off&lt;/a&gt; for not simply tilting my head in order to better locate &lt;a href=&quot;/2020/05/17/weeknotes-29/&quot;&gt;last week’s infernal whining noise&lt;/a&gt;.&lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;We watched (or is that played?) Netflix’s interactive “&lt;a href=&quot;https://www.netflix.com/title/81131714&quot;&gt;Unbreakable Kimmy Schmidt: Kimmy vs. the Reverend&lt;/a&gt;” and enjoyed it more than I expected. I’m not sure we’ll go back to watch all the alternate paths we could have taken though.&lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;Despite being indifferent about the show, E will not stop singing &lt;a href=&quot;https://open.spotify.com/track/6tJFtthY0rI1x06qb8NjK0?si=E3pDqHKiT2Cap3lrdmYxCw&quot;&gt;The Mandalorian theme tune&lt;/a&gt;. It may well unseat &lt;a href=&quot;https://open.spotify.com/track/25lTenJPmSfwCRZi2hjCcB?si=DLXmTdOCSLuyQmf6Gq5zcQ&quot;&gt;the Outer Wilds theme&lt;/a&gt; and &lt;a href=&quot;https://open.spotify.com/track/05pN1ltuUivUgSzRlTXhtr?si=E2IjricSTwC9-OrDJ96QyA&quot;&gt;XCOM 2’s “Squad Loadout”&lt;/a&gt; music as the most annoying earworm in this house.&lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;&lt;a href=&quot;https://ftrain.substack.com/&quot;&gt;Paul Ford has started writing a newsletter called “i absolutely am going to bail on this in a month”&lt;/a&gt; (there’s an &lt;a href=&quot;https://ftrain.substack.com/feed/&quot;&gt;RSS feed&lt;/a&gt; too). As a fan of Ford’s &lt;a href=&quot;https://ftrain.com&quot;&gt;Ftrain.com&lt;/a&gt; back in the 2000s, this makes me happy.&lt;/p&gt;
  &lt;/li&gt;
&lt;/ul&gt;
</content>
</entry><entry>
  <title type="html">Weeknotes 29</title>
  <link href="https://mudge.name/2020/05/17/weeknotes-29/"/>
  <id>https://mudge.name/2020/05/17/weeknotes-29/</id>
  <published>2020-05-17T09:59:00+00:00</published>
  <updated>2020-05-17T09:59:00+00:00</updated>
  <summary type="html">An infernal whining, an update on EOL Ruby versions, the pit of success, a GPG rabbithole and teaching babies about quantum mechanics.</summary>
  <content type="html">&lt;ul&gt;
  &lt;li&gt;
    &lt;p&gt;After publishing &lt;a href=&quot;/2020/05/10/weeknotes-28/&quot;&gt;last week’s notes&lt;/a&gt;, E and I noticed a high pitched whining noise coming from our kitchen.&lt;/p&gt;

    &lt;p&gt;Unable to locate the source of the noise, I switched off the power to our oven, fridge and freezer but still the whining continued. I resorted to holding the cardboard tube from a toilet roll to one ear while plugging my other ear with a headphone, crawling on all fours trying to pinpoint this infernal whine.&lt;/p&gt;

    &lt;p&gt;With a growing headache, I climbed onto the kitchen counter and checked our fire alarm when suddenly I realised: the noise was coming from &lt;a href=&quot;https://uk.joiebaby.com/product/dreamer/&quot;&gt;C’s baby bouncer&lt;/a&gt;. He’d accidentally turned on one of its soothing sounds and the manufacturers had seemingly chosen the sound of a single, agitated mosquito to represent the rainforest.&lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;In my search, I did discover our fire alarm had a sticker on its underside that read “Replace by 2014”.&lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;&lt;a href=&quot;https://github.com/rubytogether/ecosystem/pull/284&quot;&gt;My changes to RubyGems.org stats were deployed&lt;/a&gt; so I can tell you on Friday, 15th May 2020, out of 54,933,639 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;gem install&lt;/code&gt; and &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;bundle install&lt;/code&gt;s:&lt;/p&gt;

    &lt;ul&gt;
      &lt;li&gt;19,634,055 (35.7%) were from Ruby 2.3 (&lt;a href=&quot;https://www.ruby-lang.org/en/downloads/branches/&quot;&gt;end-of-life since April 2019&lt;/a&gt;)&lt;/li&gt;
      &lt;li&gt;13,631,483 (24.8%) were from Ruby 2.5&lt;/li&gt;
      &lt;li&gt;8,370,929 (15.2%) were from Ruby 2.4 (&lt;a href=&quot;https://www.ruby-lang.org/en/downloads/branches/&quot;&gt;end-of-life since April 2020&lt;/a&gt;)&lt;/li&gt;
      &lt;li&gt;5,952,688 (10.8%) were from Ruby 2.6&lt;/li&gt;
      &lt;li&gt;3,882,306 (7.1%) were from Ruby 2.1 (&lt;a href=&quot;https://www.ruby-lang.org/en/downloads/branches/&quot;&gt;end-of-life since April 2017&lt;/a&gt;)&lt;/li&gt;
      &lt;li&gt;1,090,639 (2.0%) were from Ruby 1.9 (&lt;a href=&quot;https://www.ruby-lang.org/en/downloads/branches/&quot;&gt;end-of-life since February 2015&lt;/a&gt;)&lt;/li&gt;
      &lt;li&gt;782,778 (1.4%) were from Ruby 2.0 (&lt;a href=&quot;https://www.ruby-lang.org/en/downloads/branches/&quot;&gt;end-of-life since February 2016&lt;/a&gt;)&lt;/li&gt;
      &lt;li&gt;726,975 (1.3%) were from Ruby 2.2 (&lt;a href=&quot;https://www.ruby-lang.org/en/downloads/branches/&quot;&gt;end-of-life since April 2018&lt;/a&gt;)&lt;/li&gt;
      &lt;li&gt;720,261 (1.3%) were from Ruby 2.7&lt;/li&gt;
      &lt;li&gt;122,573 (0.2%) were from Ruby 1.8 (&lt;a href=&quot;https://www.ruby-lang.org/en/news/2014/07/01/eol-for-1-8-7-and-1-9-2/&quot;&gt;end-of-life since August 2014&lt;/a&gt;)&lt;/li&gt;
    &lt;/ul&gt;

    &lt;p&gt;This means that 34,610,255 (63%) of all requests were from versions of Ruby that are technically “end-of-life”.&lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;Following &lt;a href=&quot;https://engineering.fb.com/web/facebook-redesign/&quot;&gt;Facebook’s description of their newly rebuilt tech stack for Facebook.com&lt;/a&gt;, I enjoyed reading &lt;a href=&quot;https://macwright.org/2020/05/10/spa-fatigue.html&quot;&gt;Tom MacWright’s “Second-guessing the modern web”&lt;/a&gt;.&lt;/p&gt;

    &lt;p&gt;I was particularly drawn to the idea of the &lt;a href=&quot;https://blog.codinghorror.com/falling-into-the-pit-of-success/&quot;&gt;pit of success&lt;/a&gt;:&lt;/p&gt;

    &lt;blockquote&gt;
      &lt;p&gt;And it should be easy to do a good job.&lt;/p&gt;

      &lt;p&gt;Frameworks should lure people into the pit of success, where following the normal rules and using normal techniques is the winning approach.&lt;/p&gt;
    &lt;/blockquote&gt;

    &lt;p&gt;I’ve &lt;a href=&quot;/2019/12/08/weeknotes-6/&quot;&gt;mentioned my interest in the design concept of “affordance” before&lt;/a&gt; and my main consideration when designing software is what kind of behaviours does a decision encourage and discourage. “Pit of success” is a pithy way to communicate this idea: how can we construct a system such that the &lt;em&gt;easiest&lt;/em&gt; possible thing is also the &lt;em&gt;right&lt;/em&gt; thing to do?&lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;In one of our recent trips to &lt;a href=&quot;https://www.bbc.co.uk/cbeebies/shows/the-baby-club&quot;&gt;The Baby Club&lt;/a&gt;, the answer to the regular question of “what’s in the bag?” was “a bag”. The recursive nature of this answer reminded me of &lt;a href=&quot;https://www.foodandwine.com/news/solving-mystery-kit-kat-filling&quot;&gt;the filling inside KitKats&lt;/a&gt; and made me secretly wish the show would take a more surreal turn. I suppose I’ll just have to rewatch “&lt;a href=&quot;https://www.youtube.com/channel/UCZOnoLKzoBItcEk5OsES2TA&quot;&gt;Don’t Hug Me I’m Scared&lt;/a&gt;” instead.&lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;This week at work, after successfully launching a major new feature, we took a few days to clean up some &lt;a href=&quot;https://martinfowler.com/bliki/TechnicalDebt.html&quot;&gt;technical debt&lt;/a&gt; we’d accrued to meet our deadline. I took to calling it a “firebreak” week, having &lt;a href=&quot;https://insidegovuk.blog.gov.uk/2015/02/06/gov-uks-firebreak-why-and-how-we-spent-a-month-working-differently/&quot;&gt;stolen the term from GOV.UK&lt;/a&gt;.&lt;/p&gt;

    &lt;p&gt;Having overheard this, E wished me well “turning off the fire engine”.&lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;After seeing &lt;a href=&quot;https://twitter.com/siracusa/status/1258113504499904512&quot;&gt;John Siracusa recommend using &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;pmset&lt;/code&gt; to diagnose problems with a Mac that refuses to sleep&lt;/a&gt;, I used it myself when E’s laptop kept draining its battery with the lid closed.&lt;/p&gt;

    &lt;p&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;pmset -g&lt;/code&gt; and &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;pmset -g assertions&lt;/code&gt; revealed that &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;sharingd&lt;/code&gt; was preventing &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;UserIdleSystemSleep&lt;/code&gt; and, sure enough, toggling Screen Sharing (and a reboot) fixed the problem.&lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;I went down a bit of a &lt;a href=&quot;https://www.gnupg.org&quot;&gt;GPG&lt;/a&gt; rabbithole after discovering that my 12 year old &lt;a href=&quot;https://infra.apache.org/openpgp.html#generate-key&quot;&gt;1024 bit DSA key is threatened by weaknesses found in SHA-1&lt;/a&gt;. Thankfully, &lt;a href=&quot;https://infra.apache.org/key-transition.html&quot;&gt;The Apache Software Foundation’s “How to transition to a new PGP key”&lt;/a&gt; was an excellent guide to &lt;a href=&quot;https://infra.apache.org/openpgp.html#generate-key&quot;&gt;generating a new 4096 bit RSA key&lt;/a&gt; and &lt;a href=&quot;https://infra.apache.org/key-transition.html#single-keyring&quot;&gt;transitioning to using it&lt;/a&gt;.&lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;After reading many recommendations, we started watching “&lt;a href=&quot;https://disneyplusoriginals.disney.com/show/the-mandalorian&quot;&gt;The Mandalorian&lt;/a&gt;”. It’s fine.&lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;A recurring section of C’s &lt;a href=&quot;https://www.singandsign.co.uk&quot;&gt;Sing and Sign&lt;/a&gt; lessons is a cat named Jessie hidden in a box. I assume this is to teach children &lt;a href=&quot;https://en.wikipedia.org/wiki/Schrödinger%27s_cat&quot;&gt;the problem of the Copenhagen interpretation of quantum mechanics&lt;/a&gt;.&lt;/p&gt;
  &lt;/li&gt;
&lt;/ul&gt;
</content>
</entry><entry>
  <title type="html">Weeknotes 28</title>
  <link href="https://mudge.name/2020/05/10/weeknotes-28/"/>
  <id>https://mudge.name/2020/05/10/weeknotes-28/</id>
  <published>2020-05-10T10:16:00+00:00</published>
  <updated>2020-05-10T10:16:00+00:00</updated>
  <summary type="html">A new thought technology, supporting old Ruby versions, making the most of the long weekend and filling the hole left in my life by Keith Brymer Jones.</summary>
  <content type="html">&lt;ul&gt;
  &lt;li&gt;
    &lt;p&gt;Following &lt;a href=&quot;https://www.radicalcandor.com&quot;&gt;radical candor&lt;/a&gt; and &lt;a href=&quot;https://www.newyorker.com/sports/sporting-scene/world-cup-2018-radical-sensibleness-england-team-manager-gareth-southgate&quot;&gt;radical sensibleness&lt;/a&gt;, I propose a new &lt;a href=&quot;https://twitter.com/hotdogsladies/status/656676217408614400&quot;&gt;thought technology&lt;/a&gt;: radical competency.&lt;/p&gt;

    &lt;p&gt;It can be summarised in three steps:&lt;/p&gt;

    &lt;ol&gt;
      &lt;li&gt;Think about a thing.&lt;/li&gt;
      &lt;li&gt;Say you’ll do something related to step 1.&lt;/li&gt;
      &lt;li&gt;Do the thing you said you’d do in step 2.&lt;/li&gt;
    &lt;/ol&gt;

    &lt;p&gt;This is inspired by recent experience and by &lt;a href=&quot;https://www.instagram.com/_weszlem/&quot;&gt;Maciej&lt;/a&gt; sharing &lt;a href=&quot;https://chelseatroy.com/2019/12/18/reviewing-pull-requests/&quot;&gt;Chelsea Troy’s “Reviewing Pull Requests”&lt;/a&gt;. While I’m not sure I agree about pushing commits to someone else’s branch, I agree with the goal of saving time and frustration by placing more burden on the reviewer.&lt;/p&gt;

    &lt;p&gt;In any exchange, you should be trying to &lt;em&gt;reduce&lt;/em&gt; effort for the other party.&lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;E and I watched “&lt;a href=&quot;https://www.imdb.com/title/tt2527338/&quot;&gt;Star Wars: Episode IX - The Rise of Skywalker&lt;/a&gt;”. It was not good.&lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;Following &lt;a href=&quot;/2020/05/03/weeknotes-27/&quot;&gt;last week’s debugging adventures&lt;/a&gt;, I switched &lt;a href=&quot;https://github.com/mudge/re2&quot;&gt;re2&lt;/a&gt; from using &lt;a href=&quot;https://travis-ci.org&quot;&gt;Travis CI&lt;/a&gt; to &lt;a href=&quot;https://github.com/features/actions&quot;&gt;GitHub Actions&lt;/a&gt;.&lt;/p&gt;

    &lt;p&gt;Most of my effort went into writing a &lt;a href=&quot;https://github.com/mudge/re2-test-action&quot;&gt;new, custom GitHub Action called “re2 Test Action”&lt;/a&gt; which creates a consistent environment using &lt;a href=&quot;https://www.docker.com&quot;&gt;Docker&lt;/a&gt; for running the &lt;a href=&quot;https://github.com/mudge/re2/tree/master/spec&quot;&gt;re2 test suite&lt;/a&gt; against &lt;a href=&quot;https://github.com/mudge/re2/blob/master/.github/workflows/tests.yml#L13-L22&quot;&gt;different versions&lt;/a&gt; of Ruby and the &lt;a href=&quot;https://github.com/google/re2&quot;&gt;underlying re2 library&lt;/a&gt;.&lt;/p&gt;

    &lt;p&gt;It was a little tricky because the &lt;a href=&quot;https://github.com/actions/setup-ruby&quot;&gt;recommended way to setup Ruby versions with GitHub Actions&lt;/a&gt; only supports Ruby 2.4, 2.5, 2.6 and 2.7. However, the recently released &lt;a href=&quot;http://stats.rubygems.org&quot;&gt;RubyGems.org stats&lt;/a&gt; shows that the most popular Ruby version is Ruby 2.3 (and over 7% of people still use Ruby 2.1).&lt;/p&gt;

    &lt;p&gt;I’m &lt;a href=&quot;https://twitter.com/mudge/status/1259036292752576513&quot;&gt;attempting to get&lt;/a&gt; a &lt;a href=&quot;https://github.com/rubytogether/ecosystem/pull/284&quot;&gt;more detailed breakdown of older Ruby versions&lt;/a&gt; as I’d love to know how many people are still using long end-of-lifed versions.&lt;/p&gt;

    &lt;p&gt;People have often been surprised, confused and occasionally frustrated that I support Ruby 1.8 in my libraries such as re2, &lt;a href=&quot;https://github.com/fieldhand/fieldhand&quot;&gt;Fieldhand&lt;/a&gt; and &lt;a href=&quot;https://github.com/altmetric/embiggen&quot;&gt;Embiggen&lt;/a&gt;. Having wasted so much time in my career struggling to upgrade dependencies that have, in turn, caused other breakages, I strongly believe libraries should go out of their way to be as compatible and frictionless as possible for the user to use. I never want one of my libraries to cause you incidental issues because I made a change internally.&lt;/p&gt;

    &lt;p&gt;While &lt;a href=&quot;https://www.mikeperham.com/2016/02/09/kill-your-dependencies/&quot;&gt;Mike Perham called for us to “Kill Your Dependencies”&lt;/a&gt;, I agree more with &lt;a href=&quot;http://jcoglan.com&quot;&gt;James Coglan&lt;/a&gt; who talked about this in &lt;a href=&quot;https://whyarecomputers.com/1&quot;&gt;Why Are Computers’ “Episode 1: A Fairly Deep Yak Shave”&lt;/a&gt;:&lt;/p&gt;

    &lt;blockquote&gt;
      &lt;p&gt;The thing that most concerns me is stuff that gets changed because
someone thought that the new way is how it should’ve been done in the
first place and it’s obviously better, but it doesn’t give any real new
capabilities or power and doesn’t really fix any mistakes, and it breaks
their existing software.&lt;/p&gt;

      &lt;p&gt;To me the canonical example of that is the Ruby 1.9 hash syntax, which a
lot of people are like “oh, it’s obviously better”, but it doesn’t let
you write programs that you couldn’t write before, it doesn’t fix any
mistakes that anyone was making before, and it means that if someone uses
that syntax, that program now won’t run on an older thing. It’s purely an
aesthetic change. The aesthetics of code are important, and it’s
important to have stuff that’s readable, but if you have a thing that’s
already been shipped, making those tiny little fussy aesthetic changes to
it, to me, never seems really worth it.&lt;/p&gt;
    &lt;/blockquote&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;Never one to let a &lt;a href=&quot;https://www.gov.uk/bank-holidays&quot;&gt;bank holiday weekend&lt;/a&gt; go to waste, I spent a large portion of yesterday on administrivia mostly informed by &lt;a href=&quot;https://www.moneysavingexpert.com&quot;&gt;Martin Lewis’ “Money Saving Expert”&lt;/a&gt;.&lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;On a bittersweet note, we &lt;a href=&quot;/2020/05/03/weeknotes-27/&quot;&gt;finished “The Great Pottery Throw Down”&lt;/a&gt;. I am attempting to fill the hole it has left in my life by encouraging C to watch “&lt;a href=&quot;https://www.bbc.co.uk/cbeebies/shows/hey-duggee&quot;&gt;Hey Duggee&lt;/a&gt;” instead.&lt;/p&gt;
  &lt;/li&gt;
&lt;/ul&gt;
</content>
</entry><entry>
  <title type="html">Weeknotes 27</title>
  <link href="https://mudge.name/2020/05/03/weeknotes-27/"/>
  <id>https://mudge.name/2020/05/03/weeknotes-27/</id>
  <published>2020-05-03T09:40:00+00:00</published>
  <updated>2020-05-03T09:40:00+00:00</updated>
  <summary type="html">Getting emotional about cups and saucers, a home haircut, the only two good types of advert, new kitchen scales and a debugging adventure.</summary>
  <content type="html">&lt;ul&gt;
  &lt;li&gt;
    &lt;p&gt;E and I started watching series 3 of “&lt;a href=&quot;https://www.channel4.com/programmes/the-great-pottery-throw-down&quot;&gt;The Great Pottery Throw Down&lt;/a&gt;”. I was drawn in by the sight of expert judge &lt;a href=&quot;https://en.wikipedia.org/wiki/Keith_Brymer_Jones&quot;&gt;Keith Brymer Jones&lt;/a&gt; in dungarees throwing an amphora before asking contestants to do the same. We were hooked when we saw him moved to tears while judging a breakfast set:&lt;/p&gt;

    &lt;blockquote&gt;
      &lt;p&gt;That’s fantastic, mate. Fantastic!&lt;/p&gt;
    &lt;/blockquote&gt;

    &lt;p&gt;After writing about my disappointment with the Great British Bake Off in &lt;a href=&quot;/2019/11/16/weeknotes-3/&quot;&gt;my early weeknotes&lt;/a&gt;, this is the feel-good show I’ve been looking for.&lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;In &lt;a href=&quot;https://www.gov.uk/coronavirus&quot;&gt;Coronavirus lockdown&lt;/a&gt; news: E cut my hair with a beard trimmer. After watching a few YouTube videos and &lt;a href=&quot;https://travel.stackexchange.com/questions/14915/how-to-know-your-number-for-a-machine-haircut-in-a-foreign-country&quot;&gt;trying to convert UK haircut “grades” into millimetres&lt;/a&gt;, we went for 6 mm on the side, 12 mm on top and 9 mm in between.&lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;After &lt;a href=&quot;/2020/04/19/weeknotes-25/&quot;&gt;my adoption of Shortcuts a few weeks ago&lt;/a&gt;, &lt;a href=&quot;https://tomstu.art&quot;&gt;Tom&lt;/a&gt; asked why I didn’t use “&lt;a href=&quot;https://support.apple.com/en-gb/HT204389#ios&quot;&gt;Hey Siri&lt;/a&gt;” to activate my shortcuts instead of fumbling with my phone’s screen in the dark. I had tried but was frustrated pausing between saying “Hey Siri” and waiting for the telltale “hmm?”.&lt;/p&gt;

    &lt;p&gt;Tom told me you don’t need to pause at all: Siri will buffer everything you say after being activated. You will now find me occasionally saying “Hey Siri Bounce”, “Hey Siri Fold” and “Hey Siri &lt;a href=&quot;https://www.bakerybits.co.uk/resources/autolyse-what-why-how/&quot;&gt;Autolyse&lt;/a&gt;”.&lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;I haven’t been enjoying the increased number of emotionally manipulative Coronavirus-related advertisements from commercial companies and seeing &lt;a href=&quot;https://youtu.be/_e8XLnMiCOE&quot;&gt;Uber’s “Thank You For Not Riding”&lt;/a&gt; made me particularly cynical. As &lt;a href=&quot;https://youtu.be/vM3J9jDoaTA&quot;&gt;Sean Haney demonstrated, “Every Covid-19 Commercial is Exactly the Same”&lt;/a&gt;.&lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;There should only be two types of advert: the straightforward &lt;a href=&quot;https://youtu.be/lrMD_z_FnNk&quot;&gt;Cillit Bang&lt;/a&gt; approach or the utterly sublime &lt;a href=&quot;https://youtu.be/j4IFNKYmLa8&quot;&gt;Halo Top&lt;/a&gt;.&lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;Someone in this household accidentally cooked our kitchen scales, reducing them to molten plastic so we had to invest in a replacement. As &lt;a href=&quot;https://mrgan.tumblr.com/post/615569556634763264/nevens-pizza-dough/amp&quot;&gt;Neven Mrgan mentions in his pizza dough recipe&lt;/a&gt;, having gram-precision scales that have an extra long timeout is ideal for baking:&lt;/p&gt;

    &lt;blockquote&gt;
      &lt;p&gt;My favorite feature: extra long timeout (before it turns off) so I can forget to get the flour and run downstairs and hunt for it and when I come back, my measurement is still up on the screen.&lt;/p&gt;
    &lt;/blockquote&gt;

    &lt;p&gt;We got ourselves some &lt;a href=&quot;http://www.salterhousewares.co.uk/salter-pro-stainless-steel-digital-kitchen-scales.html&quot;&gt;Salter Pro Stainless Steel Digital Kitchen Scales&lt;/a&gt; as a replacement and, while it isn’t listed anywhere, they have a very generous timeout.&lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;&lt;a href=&quot;https://siguza.github.io/psychicpaper/&quot;&gt;Siguza’s write-up of their “Psychic Paper” security exploit for iOS&lt;/a&gt; is a great and extremely accessible read on how the incidental complexity of XML parsing resulted in a very serious bug.&lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;We finished &lt;a href=&quot;https://www.bbc.co.uk/iplayer/episode/p087gkct/devs-series-1-episode-1&quot;&gt;DEVS&lt;/a&gt; and, &lt;a href=&quot;https://tomstu.art/weeknotes-15-what-a-ride&quot;&gt;like Tom&lt;/a&gt;, I think it is well worth watching even if it wasn’t perfect.&lt;/p&gt;

    &lt;p&gt;It made me want to re-read &lt;a href=&quot;https://www.penguinrandomhouse.com/books/538034/exhalation-by-ted-chiang/&quot;&gt;Ted Chiang’s “Exhalation”&lt;/a&gt;, &lt;a href=&quot;https://www.penguinrandomhouse.com/books/538163/arrival-stories-of-your-life-mti-by-ted-chiang/&quot;&gt;“Arrival” (formerly published as “Stories of Your Life and Others”)&lt;/a&gt; and &lt;a href=&quot;https://www.gregegan.net/PERMUTATION/Permutation.html&quot;&gt;Greg Egan’s “Permutation City”&lt;/a&gt;.&lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;I investigated and fixed &lt;a href=&quot;https://github.com/mudge/re2/issues/43&quot;&gt;an issue related to re2&lt;/a&gt;, my most popular open source project, which led me on a journey to better understand &lt;a href=&quot;http://tldp.org/HOWTO/Program-Library-HOWTO/shared-libraries.html&quot;&gt;shared library names and how they communicate &lt;abbr title=&quot;Application binary interface&quot;&gt;ABI&lt;/abbr&gt; compatibility&lt;/a&gt;. This then led me to discover and &lt;a href=&quot;https://github.com/Homebrew/homebrew-core/pull/54136&quot;&gt;fix a bug in a Homebrew formula&lt;/a&gt; to resolve the original issue.&lt;/p&gt;

    &lt;p&gt;I do love the twists and turns of debugging.&lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;&lt;a href=&quot;/2020/04/26/weeknotes-26/&quot;&gt;The jury is in&lt;/a&gt;: Doom Eternal is far too stressful.&lt;/p&gt;
  &lt;/li&gt;
&lt;/ul&gt;
</content>
</entry><entry>
  <title type="html">Weeknotes 26</title>
  <link href="https://mudge.name/2020/04/26/weeknotes-26/"/>
  <id>https://mudge.name/2020/04/26/weeknotes-26/</id>
  <published>2020-04-26T11:00:00+00:00</published>
  <updated>2020-04-26T11:00:00+00:00</updated>
  <summary type="html">The importance of editing, unminifying JavaScript, inappropriate Zoom virtual backgrounds and a thirst for life.</summary>
  <content type="html">&lt;ul&gt;
  &lt;li&gt;
    &lt;p&gt;I waffle.&lt;/p&gt;

    &lt;p&gt;My first draft of that sentence was:&lt;/p&gt;

    &lt;blockquote&gt;
      &lt;p&gt;When writing, I have a tendency to waffle.&lt;/p&gt;
    &lt;/blockquote&gt;

    &lt;p&gt;Last week’s notes were a perfect example of this. When I first published them, E proofread and found so much to cut and simplify. So &lt;a href=&quot;https://github.com/mudge/mudge.github.com/commit/ccc989bfa7780bb1f5e684d799065cb2c82d92b4&quot;&gt;I did&lt;/a&gt;, &lt;a href=&quot;https://github.com/mudge/mudge.github.com/commit/95daf898f4063c6b918d2f6702da2c9f1a454971&quot;&gt;over&lt;/a&gt; and &lt;a href=&quot;https://github.com/mudge/mudge.github.com/commit/1c7eae2c11e473bdc13aad21f1f940bd3075f0e0&quot;&gt;over&lt;/a&gt; and &lt;a href=&quot;https://github.com/mudge/mudge.github.com/commit/bb91896504140b5b605030f276b8279379e540bd&quot;&gt;over&lt;/a&gt; and &lt;a href=&quot;https://github.com/mudge/mudge.github.com/commit/d05f3b4932e060b1794ef972d76065b684de6e88&quot;&gt;over again&lt;/a&gt;.&lt;/p&gt;

    &lt;p&gt;In the words of &lt;a href=&quot;https://quoteinvestigator.com/2012/04/28/shorter-letter/&quot;&gt;Blaise Pascal (not Mark Twain)&lt;/a&gt;:&lt;/p&gt;

    &lt;blockquote&gt;
      &lt;p&gt;I have made this longer than usual because I have not had time to make it shorter.&lt;/p&gt;
    &lt;/blockquote&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;I’m experimenting with writing these notes on my phone during the week. The main issue is how inept I am at editing text on iOS, especially when trying to place the insertion pointer to paste text. It turns out there are &lt;a href=&quot;https://support.apple.com/en-gb/guide/iphone/iph3c50f96e/13.0/ios/13.0&quot;&gt;three finger gestures for cut, copy and paste&lt;/a&gt; which make things much easier. I doubt I would have ever discovered them on my own.&lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;&lt;a href=&quot;https://twitter.com/chastell/status/1252337694320910336&quot;&gt;Piotr flagged that my unminified JavaScript from last week fits into a single TCP packet when gzipped&lt;/a&gt;. As &lt;a href=&quot;https://caniuse.com/#search=gzip&quot;&gt;gzip compression has been supported in browsers for the past 20 years&lt;/a&gt; and I’m using &lt;a href=&quot;https://support.cloudflare.com/hc/en-us/articles/200168396-What-will-Cloudflare-compress-&quot;&gt;Cloudflare to compress my JavaScript with Brotli&lt;/a&gt;, there really isn’t any need to minify it.&lt;/p&gt;

    &lt;p&gt;Still, I couldn’t resist playing &lt;a href=&quot;https://github.com/mudge/mudge.github.com/commit/dd318af9d8182f80538536d0077947e043275a97&quot;&gt;a little code golf&lt;/a&gt;.&lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;I &lt;a href=&quot;https://github.com/mudge/mudge.github.com/blob/51d0fd06ccf168428e1b51ad878db7031c50fb27/_includes/dotiw.test.js&quot;&gt;ported the original &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;distance_of_time_in_words&lt;/code&gt; test suite from Rails to JavaScript&lt;/a&gt; and used it as an excuse to &lt;a href=&quot;https://github.com/mudge/mudge.github.com/actions/runs/87789354&quot;&gt;set up my first workflow with GitHub Actions&lt;/a&gt;.&lt;/p&gt;

    &lt;p&gt;Running the test suite revealed &lt;a href=&quot;https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Date/getYear&quot;&gt;JavaScript’s &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Date.prototype.getYear()&lt;/code&gt; doesn’t return what I expected&lt;/a&gt;:&lt;/p&gt;

    &lt;blockquote&gt;
      &lt;p&gt;A number representing the year of the given date, according to local time, minus 1900.&lt;/p&gt;
    &lt;/blockquote&gt;

    &lt;p&gt;Sure enough:&lt;/p&gt;

    &lt;div class=&quot;language-javascript highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;o&quot;&gt;&amp;gt;&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;Date&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;2020-04-26&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)).&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;getYear&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;
&lt;span class=&quot;mi&quot;&gt;120&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;    &lt;/div&gt;

    &lt;p&gt;Thankfully, you can use &lt;a href=&quot;https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Date/getFullYear&quot;&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Date.prototype.getFullYear()&lt;/code&gt;&lt;/a&gt; instead:&lt;/p&gt;

    &lt;div class=&quot;language-javascript highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;o&quot;&gt;&amp;gt;&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;Date&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;2020-04-26&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)).&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;getFullYear&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;
&lt;span class=&quot;mi&quot;&gt;2020&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;    &lt;/div&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;I disappeared down a bit of a rabbit hole trying to add a &lt;a href=&quot;https://developer.mozilla.org/en-US/docs/Web/HTTP/CSP&quot;&gt;Content Security Policy&lt;/a&gt; to this site in pursuit of a perfect score from &lt;a href=&quot;https://observatory.mozilla.org/&quot;&gt;Mozilla Observatory&lt;/a&gt;.&lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;Following both &lt;a href=&quot;https://natbuckley.co.uk/2020/04/19/weeknotes-66-the-novelty-has-worn-off/&quot;&gt;Nat&lt;/a&gt; and &lt;a href=&quot;https://tomstu.art/weeknotes-15-what-a-ride&quot;&gt;Tom&lt;/a&gt; mentioning the &lt;a href=&quot;https://youtu.be/Mh4f9AYRCZY&quot;&gt;2017 BBC News interview with Professor Robert Kelly that was famously interrupted by his children&lt;/a&gt;, &lt;a href=&quot;https://twitter.com/leocassarani&quot;&gt;Leo&lt;/a&gt; told me about a colleague using the footage as a &lt;a href=&quot;https://support.zoom.us/hc/en-us/articles/210707503-Virtual-Background&quot;&gt;Zoom virtual background&lt;/a&gt;. This was too good an idea not to copy, so I downloaded &lt;a href=&quot;https://youtu.be/62a-1ZYcsV0&quot;&gt;a version of the video with Professor Kelly removed&lt;/a&gt; and set it as my background. Sadly, I had to quickly turn it off when the meeting I was hoping to brighten with &lt;a href=&quot;https://natbuckley.co.uk/images/girl.gif&quot;&gt;that entrance dance&lt;/a&gt; turned unexpectedly gloomy.&lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;On an impulse, I bought &lt;a href=&quot;https://bethesda.net/game/doom&quot;&gt;Doom Eternal&lt;/a&gt; from the &lt;a href=&quot;https://www.playstation.com/en-gb/explore/playstation-network/&quot;&gt;PlayStation Network&lt;/a&gt;. The jury is still out on whether blasting fiends from hell at an &lt;a href=&quot;https://youtu.be/I9ZsFT_eqXY&quot;&gt;intentionally unrelenting pace&lt;/a&gt; is an activity that can be considered “relaxing”.&lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;C is fascinated by anyone drinking. When he gets chance to drink water himself, he goes into a kind of joyful frenzy, finding every aspect of the experience wonderful. His enthusiasm for mundane things is an inspiration.&lt;/p&gt;
  &lt;/li&gt;
&lt;/ul&gt;
</content>
</entry><entry>
  <title type="html">Weeknotes 25</title>
  <link href="https://mudge.name/2020/04/19/weeknotes-25/"/>
  <id>https://mudge.name/2020/04/19/weeknotes-25/</id>
  <published>2020-04-19T13:28:00+00:00</published>
  <updated>2020-04-19T13:28:00+00:00</updated>
  <summary type="html">Bank holiday noodling, coffee paraphernalia, a use for Shortcuts and life advice from a Learn with Me Zebra walker.</summary>
  <content type="html">&lt;ul&gt;
  &lt;li&gt;
    &lt;p&gt;I spent part of the &lt;a href=&quot;https://www.gov.uk/bank-holidays&quot;&gt;bank holiday weekend&lt;/a&gt; adding a new section to every post’s byline showing how long it has been since it was published, e.g.&lt;/p&gt;

    &lt;blockquote&gt;
      &lt;p&gt;By Paul Mucur, &lt;strong&gt;7 days ago&lt;/strong&gt; on Sunday 12th April 2020&lt;/p&gt;
    &lt;/blockquote&gt;

    &lt;p&gt;As every post is a static page generated by &lt;a href=&quot;https://jekyllrb.com&quot;&gt;Jekyll&lt;/a&gt;, these new relative dates are calculated entirely in your browser so they are always up-to-date, regardless of when I last generated the site. As a bonus, they update every minute so it should still be accurate even if you leave a post open for a while.&lt;/p&gt;

    &lt;p&gt;I was too lazy to come up with my own algorithm for turning lengths of time into words so I decided to port &lt;a href=&quot;https://api.rubyonrails.org/classes/ActionView/Helpers/DateHelper.html#method-i-time_ago_in_words&quot;&gt;Rails’ &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;time_ago_in_words&lt;/code&gt;&lt;/a&gt; to JavaScript.&lt;/p&gt;

    &lt;p&gt;You can &lt;a href=&quot;https://github.com/mudge/mudge.github.com/blob/47092082ed7ef6c133f5c35070e4f96c3f523b74/j/1.js&quot;&gt;view the full, unminified source code on GitHub&lt;/a&gt; (or &lt;a href=&quot;https://github.com/mudge/mudge.github.com/blob/47092082ed7ef6c133f5c35070e4f96c3f523b74/j/1.m.js&quot;&gt;enjoy the minified version&lt;/a&gt;).&lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;Since &lt;a href=&quot;/2020/03/22/weeknotes-21/&quot;&gt;clearing out our planter a few weeks ago&lt;/a&gt;, the sight of a rectangle of barren soil with a single, surviving &lt;a href=&quot;https://en.wikipedia.org/wiki/Chard&quot;&gt;chard&lt;/a&gt; plant has not been a cheery one. To improve matters and following &lt;a href=&quot;https://twitter.com/matt_macleod&quot;&gt;Matt&lt;/a&gt;’s lead, I planted a clove of garlic in the hope it will grow.&lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;E and I watched &lt;a href=&quot;https://www.imdb.com/title/tt7014006/&quot;&gt;Bo Burnham’s “Eighth Grade”&lt;/a&gt; and loved it.&lt;/p&gt;

    &lt;p&gt;The movie doesn’t seem to be that well known despite receiving &lt;a href=&quot;https://en.wikipedia.org/wiki/Eighth_Grade_(film)#Critical_response&quot;&gt;a lot of critical praise&lt;/a&gt;.&lt;/p&gt;

    &lt;p&gt;As &lt;a href=&quot;https://twitter.com/MollyRingwald/status/1022250943298121728&quot;&gt;Molly Ringwald put it&lt;/a&gt;:&lt;/p&gt;

    &lt;blockquote&gt;
      &lt;p&gt;I just saw [Eighth Grade] and thought it was the best film about adolescence I’ve seen in a long time. Maybe ever.&lt;/p&gt;
    &lt;/blockquote&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;After watching &lt;a href=&quot;https://youtu.be/yssNu8Eynb8&quot;&gt;the chefs of Bon Appétit make their favourite coffee&lt;/a&gt;, E suggested we buy a coffee grinder.&lt;/p&gt;

    &lt;p&gt;Asking for recommendations led me to the &lt;a href=&quot;https://www.wilfa.co.uk/product/kitchen/black-aroma/&quot;&gt;Wilfa Svart Aroma Coffee Grinder&lt;/a&gt;. Seeing &lt;a href=&quot;http://www.timwendelboe.no&quot;&gt;Tim Wendelboe&lt;/a&gt;’s name &lt;a href=&quot;http://nordiccoffeeculture.com/a-tim-wendelboe-review-of-the-wilfa-svart-presisjon/&quot;&gt;in association with Wilfa&lt;/a&gt; made the decision easy given that E and I had visited his Espresso Bar in Oslo back in September 2015.&lt;/p&gt;

    &lt;p&gt;During our visit, E asked whether there were any pastries but was told they served no food as it might detract from the coffee drinking experience. While we disagree about the importance of pastries, I trust his judgement on coffee-making equipment.&lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;We ended up buying &lt;a href=&quot;https://shop.squaremilecoffee.com/products/wilfa-grinder&quot;&gt;the grinder from Square Mile Coffee Roasters&lt;/a&gt; along with a bag of beans.&lt;/p&gt;

    &lt;p&gt;The next day, I received the following email:&lt;/p&gt;

    &lt;blockquote&gt;
      &lt;p&gt;Dear Paul,&lt;/p&gt;

      &lt;p&gt;As you might know, we’ve recently launched our 25% GRINDATHOME discount code on all our grinders. We know, you just missed it, and to make up for it we would like to extend the discount code to your order by refunding the 25% on your recently purchased Wilfa grinder.&lt;/p&gt;
    &lt;/blockquote&gt;

    &lt;p&gt;In a week of extremely stressful emails, this was an unexpected and very welcome kindness.&lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;To celebrate the arrival of the grinder, I followed &lt;a href=&quot;https://bluebottlecoffee.com/preparation-guides/chemex&quot;&gt;Blue Bottle Coffee’s Chemex Brewing Guide&lt;/a&gt; and, reader, it was a glorious cup of coffee.&lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;Despite hearing about it on podcasts for a while, I hadn’t found a use for &lt;a href=&quot;https://support.apple.com/en-gb/HT208309&quot;&gt;iOS Shortcuts&lt;/a&gt;.&lt;/p&gt;

    &lt;p&gt;When C wakes up unexpectedly in the night, I get up to reassure him and set a timer for 10 minutes to see if he will put himself back to sleep. After struggling to do this in the dark for over a month, I realised this might be the perfect use case.&lt;/p&gt;

    &lt;p&gt;I created a new shortcut to set a 10 minute timer with a single button press but it is still awkward swiping over to the &lt;a href=&quot;https://support.apple.com/en-gb/HT207122&quot;&gt;widget in the Today View&lt;/a&gt;.&lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;E and I started watching &lt;a href=&quot;https://www.bbc.co.uk/programmes/p087gj19&quot;&gt;Alex Garland’s “DEVS” on BBC iPlayer&lt;/a&gt;. I’ll join &lt;a href=&quot;https://twitter.com/waxy/status/1250911854123200517&quot;&gt;others&lt;/a&gt; in highly recommending it.&lt;/p&gt;

    &lt;p&gt;While the title makes it sound like a comedy in the vein of “&lt;a href=&quot;https://www.channel4.com/programmes/the-it-crowd&quot;&gt;The IT Crowd&lt;/a&gt;” or “&lt;a href=&quot;https://www.hbo.com/silicon-valley&quot;&gt;Silicon Valley&lt;/a&gt;”, it is a drama more akin to Garland’s previous work: “&lt;a href=&quot;https://www.imdb.com/title/tt0470752&quot;&gt;Ex Machina&lt;/a&gt;” and “&lt;a href=&quot;https://www.imdb.com/title/tt2798920&quot;&gt;Annihilation&lt;/a&gt;”.&lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;It has been a trying week but as we retired for bed one evening, I accidentally kicked &lt;a href=&quot;https://www.fisher-price.com/en-gb/product/learn-with-me-zebra-walker-dlf00&quot;&gt;C’s walker&lt;/a&gt;. In its cheery voice, it said “keep going!”&lt;/p&gt;
  &lt;/li&gt;
&lt;/ul&gt;
</content>
</entry><entry>
  <title type="html">Weeknotes 24</title>
  <link href="https://mudge.name/2020/04/12/weeknotes-24/"/>
  <id>https://mudge.name/2020/04/12/weeknotes-24/</id>
  <published>2020-04-12T15:24:00+00:00</published>
  <updated>2020-04-12T15:24:00+00:00</updated>
  <summary type="html">Repairs, a new Raspberry Pi, an unforgettable ant update, overcomplicated cooking and an unusual birthday.</summary>
  <content type="html">&lt;ul&gt;
  &lt;li&gt;
    &lt;p&gt;In &lt;a href=&quot;https://therestartproject.org&quot; title=&quot;The Restart Project&quot;&gt;the spirit of repairing things&lt;/a&gt;, I took apart C’s broken toy arch, cursing to myself as I pried open the stubborn end caps with a palette knife. The telltale cracking sound of glue gave way to reveal critical bits of broken plastic, which I’ve now mended.&lt;/p&gt;

    &lt;p&gt;I was watching “&lt;a href=&quot;https://www.bbc.co.uk/iplayer/episode/m000h4n6/the-repair-shop-series-6-episode-4&quot;&gt;The Repair Shop&lt;/a&gt;” throughout, imagining myself as a regular &lt;a href=&quot;http://www.williamkirkrestoration.co.uk&quot;&gt;Will Kirk&lt;/a&gt;.&lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;&lt;a href=&quot;https://www.gyford.com/phil/writing/2020/04/05/weeknotes/#s2&quot;&gt;One of Phil Gyford’s weeknotes last week&lt;/a&gt; really made me rethink my own discomfort with “performative whimsy”. I won’t quote it here as it is worth reading it in its entirety.&lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;After over a year of running our &lt;a href=&quot;https://pi-hole.net&quot;&gt;Pi-hole&lt;/a&gt;, &lt;a href=&quot;https://github.com/mudge/homer&quot;&gt;my DNS over HTTPS proxy “Homer”&lt;/a&gt;, an &lt;a href=&quot;https://github.com/mikebrady/shairport-sync&quot;&gt;AirPlay audio player&lt;/a&gt; and &lt;a href=&quot;/2019/11/12/using-a-raspberry-pi-for-time-machine/&quot;&gt;using it for Time Machine backups&lt;/a&gt;, my &lt;a href=&quot;https://www.raspberrypi.org/products/raspberry-pi-1-model-b-plus/&quot;&gt;Raspberry Pi 1 Model B&lt;/a&gt; started failing under load.&lt;/p&gt;

    &lt;p&gt;I decided to treat myself to a brand new &lt;a href=&quot;https://www.raspberrypi.org/products/raspberry-pi-4-model-b/&quot;&gt;Raspberry Pi 4 Model B&lt;/a&gt;, going for a &lt;a href=&quot;https://thepihut.com/collections/raspberry-pi-kits-and-bundles/products/raspberry-pi-starter-kit&quot;&gt;starter kit from The Pi Hut&lt;/a&gt; and a &lt;a href=&quot;https://thepihut.com/products/xl-raspberry-pi-4-heatsink&quot;&gt;heatsink&lt;/a&gt; following &lt;a href=&quot;https://mobile.twitter.com/matt_macleod&quot;&gt;Matt&lt;/a&gt;’s recommendation to keep it from &lt;a href=&quot;https://www.raspberrypi.org/blog/thermal-testing-raspberry-pi-4/&quot;&gt;overheating and throttling&lt;/a&gt;.&lt;/p&gt;

    &lt;p&gt;Everything is now up and running again and I took the opportunity to replace Homer with &lt;a href=&quot;https://docs.pi-hole.net/guides/unbound/&quot;&gt;running our own Unbound DNS resolver&lt;/a&gt; after reading &lt;a href=&quot;https://en.wikipedia.org/wiki/DNS_over_HTTPS#Criticism&quot;&gt;criticism of DNS over HTTPS&lt;/a&gt;.&lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;I use &lt;a href=&quot;https://github.com/Homebrew/homebrew-cask&quot;&gt;Homebrew Cask&lt;/a&gt; to install software and noticed that running &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;brew cask upgrade&lt;/code&gt; rarely does anything. However, forcibly calling &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;upgrade&lt;/code&gt; on each installed Cask &lt;em&gt;does&lt;/em&gt;:&lt;/p&gt;

    &lt;div class=&quot;language-shell highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;nv&quot;&gt;$ &lt;/span&gt;brew cask list | xargs brew cask upgrade
&lt;span class=&quot;c&quot;&gt;# or, if you're running bash/zsh:&lt;/span&gt;
&lt;span class=&quot;nv&quot;&gt;$ &lt;/span&gt;brew cask upgrade &lt;span class=&quot;si&quot;&gt;$(&lt;/span&gt;brew cask list&lt;span class=&quot;si&quot;&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;    &lt;/div&gt;

    &lt;p&gt;This is because &lt;a href=&quot;https://github.com/Homebrew/homebrew-cask/blob/master/USAGE.md#updatingupgrading-casks&quot;&gt;Homebrew Cask ignores applications that update themselves when upgrading&lt;/a&gt; but you can override this with &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;--greedy&lt;/code&gt;:&lt;/p&gt;

    &lt;div class=&quot;language-shell highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;nv&quot;&gt;$ &lt;/span&gt;brew cask upgrade &lt;span class=&quot;nt&quot;&gt;--greedy&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;    &lt;/div&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;An update on our &lt;a href=&quot;/2020/04/05/weeknotes-23/&quot;&gt;ant situation&lt;/a&gt;: while washing the dishes, I spotted a sole ant going for some bait on our window sill. Within seconds, an entire procession appeared so I could finally trace back where they have been coming from.&lt;/p&gt;

    &lt;p&gt;I discovered they were coming from a broken bit of sealant around a kitchen window and promptly filled it up with more silicone. After checking outside and seeing that our wedding rose was doubling as an ant walkway, I returned to check on the window sill and what I saw next is not easily forgotten.&lt;/p&gt;

    &lt;p&gt;Previously unseen ants found their exit blocked and started &lt;em&gt;crawling out through the imperceptible gap between the glass windowpane and its frame&lt;/em&gt;.&lt;/p&gt;

    &lt;p&gt;I find that thought particularly enjoyable to turn over in your mind just before bed.&lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;It was E’s birthday and our first during the &lt;a href=&quot;https://www.gov.uk/government/publications/coronavirus-outbreak-faqs-what-you-can-and-cant-do/coronavirus-outbreak-faqs-what-you-can-and-cant-do&quot;&gt;Coronavirus outbreak&lt;/a&gt;.&lt;/p&gt;

    &lt;p&gt;Alongside a pack of &lt;a href=&quot;https://www.ocado.com/products/9-happy-birthday-balloons-341958011&quot;&gt;Ocado’s “Happy Birthday” balloons&lt;/a&gt;, I decided to make &lt;a href=&quot;https://www.theguardian.com/lifeandstyle/wordofmouth/2017/feb/02/how-to-cook-the-perfect-sticky-orange-cake&quot;&gt;Felicity Cloake’s perfect sticky orange cake&lt;/a&gt; (substituting Seville oranges with blood oranges) and a slap-up evening meal.&lt;/p&gt;

    &lt;p&gt;I decided to significantly complicate otherwise simple foods, e.g. instead of regular chips, I attempted &lt;a href=&quot;https://www.thefatduckgroup.com/hestons-triple-cooked-chips/&quot;&gt;Heston’s Triple Cooked chips&lt;/a&gt;, boiling and frying chipped potatoes the night before. While doing that, I was infusing white wine vinegar with tarragon and shallots for &lt;a href=&quot;https://thehawksmoor.com&quot;&gt;The Hawkmoor&lt;/a&gt;’s Stilton hollandaise, freeing time to simmer oranges for two hours in the morning while my sourdough bread baked in its Dutch oven.&lt;/p&gt;

    &lt;p&gt;Am I &lt;a href=&quot;https://www.gq.com/story/the-best-way-to-sous-vide-is-to-shut-up-about-it&quot;&gt;a Sous Vide Bro&lt;/a&gt;?&lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;&lt;a href=&quot;/2020/03/29/weeknotes-22/&quot;&gt;My new webcam&lt;/a&gt; arrived earlier than expected and we used it to host a birthday video conference in our living room. While I wasn’t sure how it would turn out (after all, people can’t have side conversations), it was surprisingly fun and it lifted E’s spirits to see friends in Aberdeen, Madrid and Dallas all in one place on an unusual birthday.&lt;/p&gt;
  &lt;/li&gt;
&lt;/ul&gt;
</content>
</entry><entry>
  <title type="html">Weeknotes 23</title>
  <link href="https://mudge.name/2020/04/05/weeknotes-23/"/>
  <id>https://mudge.name/2020/04/05/weeknotes-23/</id>
  <published>2020-04-05T00:00:00+00:00</published>
  <updated>2020-04-05T00:00:00+00:00</updated>
  <summary type="html">Reassuring TV shows, negotiating pavements, baby sign language, a home baking first and is Bear Grylls’ TV career over?</summary>
  <content type="html">&lt;ul&gt;
  &lt;li&gt;
    &lt;p&gt;I was somewhat amazed to see &lt;a href=&quot;https://www.channel4.com/programmes/jamie-keep-cooking-and-carry-on&quot;&gt;Jamie Oliver’s “Keep Cooking and Carry On”&lt;/a&gt;, a cookery show made with &lt;a href=&quot;https://www.gov.uk/coronavirus&quot;&gt;Coronavirus (COVID-19)&lt;/a&gt; in mind. As if there was any doubt how quickly he put this out, he even makes a joke about people &lt;a href=&quot;https://www.theguardian.com/news/2020/apr/03/off-our-trolleys-what-stockpiling-in-the-coronavirus-crisis-reveals-about-us&quot;&gt;stockpiling pasta&lt;/a&gt; in the first episode.&lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;Don’t worry, everyone: there is a &lt;a href=&quot;https://www.bbc.co.uk/programmes/m000gjn3&quot;&gt;new series of “The Repair Shop”&lt;/a&gt;. I suspect it was &lt;a href=&quot;https://youtu.be/T72TopWbXJg&quot;&gt;held in reserve for times of crisis&lt;/a&gt;.&lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;Thanks to meticulous planning around C’s bed time, E and I managed to watch &lt;a href=&quot;https://www.imdb.com/title/tt5083738/&quot;&gt;Yorgos Lanthimos’ “The Favourite”&lt;/a&gt; and loved it. &lt;a href=&quot;https://twitter.com/Stew/status/1088952413573259264&quot;&gt;Like Euan&lt;/a&gt;, I choose to believe &lt;a href=&quot;https://youtu.be/sM_fgqjKnUA&quot;&gt;the ballroom dance&lt;/a&gt; is real.&lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;I haven’t really been taking advantage of &lt;a href=&quot;https://www.gov.uk/government/publications/coronavirus-outbreak-faqs-what-you-can-and-cant-do/coronavirus-outbreak-faqs-what-you-can-and-cant-do#when-am-i-allowed-to-leave-the-house&quot;&gt;being able to leave the house for one form of exercise a day&lt;/a&gt; but did venture out for a stroll with C. While it was a nice change to get fresh air and focus on something more than a few metres from my face, I found the silent negotiation of pavement space with other pedestrians strangely stressful.&lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;&lt;a href=&quot;/2020/03/29/weeknotes-22/&quot;&gt;Last week’s ant situation&lt;/a&gt; led me to fill conspicuous gaps in our kitchen worktops with silicone and—&lt;a href=&quot;https://en.wikipedia.org/wiki/Knocking_on_wood&quot;&gt;touch faux marble&lt;/a&gt;—we haven’t seen any more curious insectoid invaders.&lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;My mum sent us a copy of “&lt;a href=&quot;http://www.childs-play.com/bookshop/9781904550044.html&quot;&gt;My First Signs&lt;/a&gt;”, a book of &lt;a href=&quot;https://en.wikipedia.org/wiki/British_Sign_Language&quot;&gt;British Sign Language&lt;/a&gt; (BSL) signs for children. While &lt;a href=&quot;https://en.wikipedia.org/wiki/Baby_sign_language#Pros_and_cons&quot;&gt;there is debate about the effects of baby sign language&lt;/a&gt;, my sister is profoundly deaf so I’m keen to teach C some sign language regardless.&lt;/p&gt;

    &lt;p&gt;Reading the signs in the book makes it clear the sign language I use with my sister has morphed from BSL into our own private language. After sending her photos of the book, she immediately proposed her own sign for “toilet” as she is not a fan of &lt;a href=&quot;https://youtu.be/rCeVPjIEAeo&quot;&gt;how it might be confused with a gesture of defiance&lt;/a&gt;.&lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;In a home baking first, E made a batch of 14 croissants over three days using a recipe from a &lt;a href=&quot;https://www.breadahead.com/courses&quot;&gt;Bread Ahead vienoisserie course&lt;/a&gt; she did three years ago.&lt;/p&gt;

    &lt;p class=&quot;center&quot;&gt;&lt;img src=&quot;/i/croissants.jpg&quot; width=&quot;375&quot; height=&quot;300&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;

    &lt;p&gt;We now start our day eating more than our daily recommended amount of butter.&lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;For the past few weeks, I have been seeing &lt;a href=&quot;https://twitter.com/BearGrylls/status/1238824362775588864&quot;&gt;bizarre advertisements on Instagram declaring “Bear Grylls [sic] TV Career is over!”&lt;/a&gt; because the “sources of his wealth became known”. Despite reporting these ads as scams to Instagram, they continue to flood in from various accounts with different images but always the same message.&lt;/p&gt;

    &lt;p&gt;I asked E what &lt;a href=&quot;https://en.wikipedia.org/wiki/Bear_Grylls&quot;&gt;his real name&lt;/a&gt; is and she replied “I don’t know; Michael Microwaves?”&lt;/p&gt;
  &lt;/li&gt;
&lt;/ul&gt;

</content>
</entry><entry>
  <title type="html">Weeknotes 22</title>
  <link href="https://mudge.name/2020/03/29/weeknotes-22/"/>
  <id>https://mudge.name/2020/03/29/weeknotes-22/</id>
  <published>2020-03-29T16:12:00+00:00</published>
  <updated>2020-03-29T16:12:00+00:00</updated>
  <summary type="html">Driveway picnics, “The Baby Club”, a new enemy at home and the etymology of DIY.</summary>
  <content type="html">&lt;ul&gt;
  &lt;li&gt;
    &lt;p&gt;Now we are &lt;a href=&quot;https://www.gov.uk/government/publications/full-guidance-on-staying-at-home-and-away-from-others&quot;&gt;staying at home&lt;/a&gt; and have little outside space of our own, we’re having to get creative when it comes to getting fresh air.&lt;/p&gt;

    &lt;p&gt;Our solution? Lunchtime driveway picnics!&lt;/p&gt;

    &lt;p&gt;Not only do we get to enjoy some sunshine while remaining on private property but we also get to use our rarely-seen &lt;a href=&quot;https://www.vonshef.com/vs_en/ash-picnic-backpack-for-4&quot;&gt;VonShef picnic backpack&lt;/a&gt;.&lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;Following on from &lt;a href=&quot;/2020/03/22/weeknotes-21/&quot;&gt;last week&lt;/a&gt;, I have been using &lt;a href=&quot;https://krisp.ai&quot;&gt;Krisp&lt;/a&gt; to mute background noise on calls and my colleagues report that it works well.&lt;/p&gt;

    &lt;p&gt;If you’re interested in trying it out, here’s &lt;a href=&quot;https://ref.krisp.ai/u/u140a0e434?utm_source=refprogram&amp;amp;utm_campaign=121761&amp;amp;locale=en-GB&quot;&gt;an invite from me&lt;/a&gt; that’ll give you an extra free month (and &lt;a href=&quot;https://help.krisp.ai/hc/en-us/articles/360012135419-What-is-the-Krisp-Referral-Program-&quot;&gt;I’ll get two back&lt;/a&gt; in return).&lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;After awkwardly wrangling my laptop lid a few too many times, I finally ordered a &lt;a href=&quot;https://www.logitech.com/en-gb/product/hd-pro-webcam-c920&quot;&gt;Logitech C920 HD PRO Webcam&lt;/a&gt; on several people’s recommendation. Sadly, it seems I am not the only person who had this idea and they’re &lt;a href=&quot;https://www.amazon.co.uk/Logitech-Calling-Recording-Microphones-Adjustable/dp/B006A2Q81M&quot;&gt;out of stock on Amazon&lt;/a&gt; until the second week of May.&lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;E has started doing yoga classes over &lt;a href=&quot;https://zoom.us&quot;&gt;Zoom&lt;/a&gt;. It is reassuring to hear that every class begins by negotiating volume levels and asking people to mute themselves.&lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;As we continue to seek ways to entertain C at home, E discovered &lt;a href=&quot;https://www.bbc.co.uk/cbeebies/shows/the-baby-club&quot;&gt;CBeebies’ “The Baby Club”&lt;/a&gt;. You can now find us occasionally singing its theme song or, its other earworm, “what’s in the bag?”&lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;&lt;a href=&quot;https://twitter.com/skillsmatter/status/1243183726101532674&quot;&gt;Skills Matter are back and have restored their videos&lt;/a&gt;. After &lt;a href=&quot;/2019/11/16/weeknotes-3/&quot;&gt;previously lamenting their disappearance&lt;/a&gt;, I fired up &lt;a href=&quot;https://ytdl-org.github.io/youtube-dl/&quot;&gt;youtube-dl&lt;/a&gt; as fast as I could to archive &lt;a href=&quot;https://skillsmatter.com/skillscasts/2435-lrug-puppet&quot;&gt;my&lt;/a&gt; &lt;a href=&quot;https://skillsmatter.com/skillscasts/6282-exploring-to_proc&quot;&gt;talks&lt;/a&gt;.&lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;I struggled with &lt;a href=&quot;https://www.nhs.uk/conditions/migraine/&quot;&gt;migraines&lt;/a&gt; at the start of the year but, thankfully, wearing glasses has helped a lot. Unfortunately, it also means I have developed some new, unconscious facial expressions. E is not a fan of these new tics and has taken to wordlessly scrunching up her own face as a gentle reminder.&lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;During the seven years in our flat, I have done battle with various unwanted house pests: first, moths and then mice. This week we met a new enemy: ants.&lt;/p&gt;

    &lt;p&gt;My hands are now lacerated and stinging with vinegar solution thanks to pulling out our kitchen appliances and spraying potential entry points.&lt;/p&gt;

    &lt;p&gt;My true nemesis is not the ants themselves but our cursed dishwasher, propped up on three—not four—flimsy plastic legs, ready to buckle at the slightest provocation. I spent more time yesterday trying to re-level the blasted thing than I did creating barriers out of washing up liquid.&lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;E and I caught the end of an episode of “&lt;a href=&quot;https://www.bbc.co.uk/iplayer/episode/b07236fx/the-100k-house-tricks-of-the-trade-series-2-4-skye-and-mattmary-and-luke&quot;&gt;The £100k House: Tricks of the Trade&lt;/a&gt;” and looked at each other as we contemplated Piers Taylor’s parting line:&lt;/p&gt;

    &lt;blockquote&gt;
      &lt;p&gt;But will DIY turn to DI-don’t?&lt;/p&gt;
    &lt;/blockquote&gt;
  &lt;/li&gt;
&lt;/ul&gt;
</content>
</entry><entry>
  <title type="html">Weeknotes 21</title>
  <link href="https://mudge.name/2020/03/22/weeknotes-21/"/>
  <id>https://mudge.name/2020/03/22/weeknotes-21/</id>
  <published>2020-03-22T00:00:00+00:00</published>
  <updated>2020-03-22T00:00:00+00:00</updated>
  <summary type="html">COVID-19, asynchronous working, surprise Smash Mouth fandom, flower arranging and being judged by book titles.</summary>
  <content type="html">&lt;ul&gt;
  &lt;li&gt;
    &lt;p&gt;I would typically euphemistically refer to “&lt;a href=&quot;/2020/03/15/weeknotes-20/&quot;&gt;current events&lt;/a&gt;” but let’s be plain: &lt;a href=&quot;https://en.wikipedia.org/wiki/Coronavirus_disease_2019&quot;&gt;&lt;abbrev title=&quot;Coronavirus disease 2019&quot;&gt;COVID-19&lt;/abbrev&gt;&lt;/a&gt;, the disease caused by the &lt;a href=&quot;https://en.wikipedia.org/wiki/Coronavirus&quot;&gt;coronavirus&lt;/a&gt; &lt;a href=&quot;https://en.wikipedia.org/wiki/Severe_acute_respiratory_syndrome_coronavirus_2&quot;&gt;&lt;abbrev title=&quot;severe acute respiratory syndrome coronavirus 2&quot;&gt;SARS-CoV-2&lt;/abbrev&gt;&lt;/a&gt; has &lt;a href=&quot;https://www.gov.uk/coronavirus&quot;&gt;dominated our lives&lt;/a&gt; this week.&lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;We’ve been rallying together with our neighbours and are sharing a precious online supermarket order. I intend to repay their kindness in bread made from my &lt;a href=&quot;/2020/03/15/weeknotes-20/&quot;&gt;fortuitous flour stockpile&lt;/a&gt;.&lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;With &lt;a href=&quot;https://www.gov.uk/government/publications/closure-of-educational-settings-information-for-parents-and-carers/closure-of-educational-settings-information-for-parents-and-carers&quot;&gt;UK schools now closed&lt;/a&gt;, more people need to juggle childcare with remote working and I really want to make this work in my current team. I’m hoping “&lt;a href=&quot;https://basecamp.com/guides/how-we-communicate&quot;&gt;The Basecamp Guide to Internal Communication&lt;/a&gt;” and &lt;a href=&quot;https://open.buffer.com/asynchronous-communication/&quot;&gt;Hailley Griffis’ “Asynchronous Communication and Why It Matters For Remote Work”&lt;/a&gt; will come in handy as we move to work more asynchronously.&lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;For those of you joining remote calls when you can’t really control the level of background noise (e.g. because your six-month old is &lt;em&gt;really&lt;/em&gt; enthusiastic about eating his porridge), &lt;a href=&quot;https://alexspeller.com&quot;&gt;Alex&lt;/a&gt; recommended &lt;a href=&quot;https://krisp.ai&quot;&gt;Krisp App&lt;/a&gt; to me. I haven’t tried it yet but might give it a go this week.&lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;Now that I am working from home for the foreseeable future, I was surprised to discover that the daily decision whether to wear socks or not has become much more significant.&lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;&lt;a href=&quot;https://apps.apple.com/us/app/netnewswire-rss-reader/id1480640210&quot;&gt;NetNewsWire, an RSS reader for iOS&lt;/a&gt; is now out. I admit I’m still struggling to get back into the habit of using an RSS reader versus mindlessly checking individual websites in turn.&lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;&lt;a href=&quot;https://engineering.shopify.com/blogs/engineering/refactoring-legacy-code-strangler-fig-pattern&quot;&gt;Adrianna Chang’s “Refactoring Legacy Code with the Strangler Fig Pattern”&lt;/a&gt; looks like &lt;a href=&quot;/2020/02/23/weeknotes-17/&quot;&gt;another&lt;/a&gt; great dive into tackling legacy code in a considered, incremental way.&lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;Happy Mother’s Day! Or is that &lt;a href=&quot;https://en.wikipedia.org/wiki/Mothering_Sunday&quot;&gt;Mothering Sunday&lt;/a&gt;?&lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;&lt;a href=&quot;https://cabel.com&quot;&gt;Cabel Sasser&lt;/a&gt; has been putting up &lt;a href=&quot;https://twitter.com/cabel/status/1240001079103778816&quot;&gt;Twitter polls for MIDI songs to play on his Roland Music Player&lt;/a&gt;. I was proud to contribute to &lt;a href=&quot;https://twitter.com/cabel/status/1241031821338406912&quot;&gt;Friday’s vote&lt;/a&gt;, resulting in &lt;a href=&quot;https://twitter.com/cabel/status/1241047282406940672&quot;&gt;Smash Mouth’s “All Star”&lt;/a&gt; and, as I enjoyed the tinny beats, E stunned me by singing along, revealing that she knew &lt;em&gt;all of the lyrics&lt;/em&gt;.&lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;Keeping the weekend spirit alive, yesterday I indulged in a little flower arranging, adding a little homemade flower food made from a squirt of bleach and a sprinkling of granulated sugar.&lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;To celebrate the &lt;a href=&quot;https://www.metoffice.gov.uk/weather/learn-about/weather/seasons/winter/when-does-winter-start&quot;&gt;end of winter&lt;/a&gt;, I removed the Sellotape from our patio door and stepped out onto our tiny spit of outside space. Our planter resembled something out of “&lt;a href=&quot;https://www.playstation.com/en-gb/games/the-last-of-us-remastered-ps4/&quot;&gt;The Last of Us&lt;/a&gt;” so I cleared it of dead salad plants and tilled the soil. It wasn’t quite &lt;a href=&quot;https://www.theguardian.com/environment/2019/jun/08/forest-bathing-japanese-practice-in-west-wellbeing&quot;&gt;forest bathing&lt;/a&gt; but was surprisingly restorative.&lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;I learnt that the strange outcrop of salt on the outside of our water softener is known as “salt creep” and tackled it with a combination of a sturdy brush and &lt;a href=&quot;/2020/01/26/weeknotes-13/&quot;&gt;one of my three trusty cans of WD-40&lt;/a&gt; (&lt;a href=&quot;https://www.wd40.com/products/silicone-lubricant/&quot;&gt;WD-40® Specialist® Water Resistant Silicone Lubricant&lt;/a&gt;, for those of you keeping track).&lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;One night, as I put my Kindle down, E asked me what I was reading and I replied “&lt;a href=&quot;https://www.hachettebookgroup.com/titles/adrian-tchaikovsky/children-of-ruin/9780316452540/&quot;&gt;Children of Ruin&lt;/a&gt;”. She scoffed, “of course; that’s a very Paul book title.”&lt;/p&gt;
  &lt;/li&gt;
&lt;/ul&gt;
</content>
</entry><entry>
  <title type="html">Weeknotes 20</title>
  <link href="https://mudge.name/2020/03/15/weeknotes-20/"/>
  <id>https://mudge.name/2020/03/15/weeknotes-20/</id>
  <published>2020-03-15T00:00:00+00:00</published>
  <updated>2020-03-15T00:00:00+00:00</updated>
  <summary type="html">Working from home, low sodium bread, workplace superpowers and yet another secure shoelace knot.</summary>
  <content type="html">&lt;ul&gt;
  &lt;li&gt;
    &lt;p&gt;Following &lt;a href=&quot;/2020/03/08/weeknotes-19/&quot;&gt;recent&lt;/a&gt; &lt;a href=&quot;https://www.nhs.uk/conditions/coronavirus-covid-19/&quot;&gt;events&lt;/a&gt;, I’ve been working from home all week.&lt;/p&gt;

    &lt;p&gt;Thankfully, I recently cobbled together a standing desk out of an old &lt;a href=&quot;https://www.benq.eu/en-eu/index.html&quot;&gt;BenQ&lt;/a&gt; monitor perched atop &lt;a href=&quot;https://www-cs-faculty.stanford.edu/~knuth/taocp.html&quot;&gt;Donald Knuth’s “The Art of Computer Programming”&lt;/a&gt; and &lt;a href=&quot;https://www.hachette.co.uk/titles/harold-mcgee/mcgee-on-food-and-cooking-an-encyclopedia-of-kitchen-science-history-and-culture/9780340831496/&quot;&gt;Harold McGee’s “McGee on Food &amp;amp; Cooking”&lt;/a&gt;. Combining that with &lt;a href=&quot;https://tuple.app&quot;&gt;Tuple&lt;/a&gt; for pair-programming and &lt;a href=&quot;https://miro.com&quot;&gt;Miro&lt;/a&gt; as a virtual whiteboard has made the switch from being co-located to being fully remote easier.&lt;/p&gt;

    &lt;p&gt;If you’re new to remote working, &lt;a href=&quot;https://blog.alicegoldfuss.com/work-in-the-time-of-corona/&quot;&gt;Alice Goldfuss’ “Work in the Time of Corona”&lt;/a&gt; has some good, practical advice about maintaining good mental health when working from home.&lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;Somewhat late to &lt;a href=&quot;https://www.rottentomatoes.com/m/hannah_gadsby_nanette&quot;&gt;the party&lt;/a&gt;, we finally watched—and were moved by—&lt;a href=&quot;https://www.netflix.com/title/80233611&quot;&gt;Hannah Gadsby’s “Nanette”&lt;/a&gt;.&lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;As someone who often uses &lt;a href=&quot;https://git-scm.com/docs/git-commit#Documentation/git-commit.txt---fixupltcommitgt&quot;&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;git commit --fixup&lt;/code&gt;&lt;/a&gt; to &lt;a href=&quot;https://blog.mocoso.co.uk/talks/2015/01/12/telling-stories-through-your-commits/&quot;&gt;tell stories through my commits&lt;/a&gt; before code review, &lt;a href=&quot;https://github.com/tummychow/git-absorb&quot;&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;git absorb&lt;/code&gt;&lt;/a&gt; could save me a lot of time (via &lt;a href=&quot;https://twitter.com/burntsushi5/status/1238115091868012545&quot;&gt;Andrew Gallant&lt;/a&gt;).&lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;I baked a loaf of bread with less salt than usual for C to try (although he’d have to eat 120g of my usual recipe to meet &lt;a href=&quot;https://www.nhs.uk/common-health-questions/childrens-health/how-much-salt-do-babies-and-children-need/&quot;&gt;his daily limit&lt;/a&gt;). Compared to his reaction to the kale and butternut squash on his plate, I’d say it was a rousing success.&lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;While I refuse to &lt;a href=&quot;https://www.theguardian.com/world/2020/mar/02/coronavirus-fears-see-stockpiling-of-food-and-medicines-across-uk&quot;&gt;stockpile&lt;/a&gt;, I do have 41 kg of bread flour in my kitchen from a spontaneous purchase back in January.&lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;&lt;a href=&quot;https://tomstu.art&quot;&gt;Tom&lt;/a&gt; and I were discussing the workplace superpower of “saying you’ll do a thing and then doing it” and he linked me to a &lt;a href=&quot;https://twitter.com/lucyprebblish/status/1236334970869813248&quot;&gt;Twitter thread by Lucy Prebble arguing competence is culturally undervalued&lt;/a&gt;.&lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;I started playing “&lt;a href=&quot;https://www.nintendo.com/games/detail/the-legend-of-zelda-links-awakening-switch/&quot;&gt;The Legend of Zelda: Link’s Awakening&lt;/a&gt;” and it is wonderful so far. Despite owning a &lt;a href=&quot;https://en.wikipedia.org/wiki/Super_Nintendo_Entertainment_System&quot;&gt;SNES&lt;/a&gt;, the first Zelda game I really played was 1998’s &lt;a href=&quot;https://www.nintendo.co.uk/Games/Nintendo-64/The-Legend-of-Zelda-Ocarina-of-Time-269536.html&quot;&gt;“The Legend of Zelda: Ocarina of Time”&lt;/a&gt; on a &lt;a href=&quot;https://en.wikipedia.org/wiki/Nintendo_64&quot;&gt;Nintendo 64&lt;/a&gt; so this is the first top-down perspective Zelda I’ve ever played.&lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;I put up a mirror in our bedroom today. Another day, another &lt;a href=&quot;https://www.fieggen.com/shoelace/secureknot.htm&quot;&gt;Ian’s secure shoelace knot&lt;/a&gt;.&lt;/p&gt;
  &lt;/li&gt;
&lt;/ul&gt;
</content>
</entry><entry>
  <title type="html">Weeknotes 19</title>
  <link href="https://mudge.name/2020/03/08/weeknotes-19/"/>
  <id>https://mudge.name/2020/03/08/weeknotes-19/</id>
  <published>2020-03-08T00:00:00+00:00</published>
  <updated>2020-03-08T00:00:00+00:00</updated>
  <summary type="html">The Missing Hit, hot lemon and honey, Ugly Delicious and a reassuringly dumb baby monitor.</summary>
  <content type="html">&lt;ul&gt;
  &lt;li&gt;
    &lt;p&gt;I followed &lt;a href=&quot;https://twitter.com/waxy/status/1235716731877961728&quot;&gt;Andy Baio’s recommendation&lt;/a&gt; and enjoyed listening to &lt;a href=&quot;https://gimletmedia.com/shows/reply-all/o2h8bx/158-the-case-of-the-missing-hit&quot;&gt;Reply All’s “The Case of the Missing Hit”&lt;/a&gt;. To avoid spoiling anything, I won’t say any more but it’s a good one.&lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;Despite the current climate of &lt;a href=&quot;https://twitter.com/getbentsaggy/status/1235229738504990720&quot;&gt;hand washing&lt;/a&gt; and sneezing into elbows, I’ve managed to catch a cough. &lt;a href=&quot;https://111.nhs.uk&quot;&gt;NHS 111&lt;/a&gt; reassures me it is nothing more and I have been consuming a record number of lemons in the form of hot drinks. The timing is unfortunate but at least I don’t need to worry about scurvy.&lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;&lt;a href=&quot;https://www.theportablebrain.com&quot;&gt;Jean&lt;/a&gt; reminded me that the second season of “&lt;a href=&quot;https://www.netflix.com/title/80170368&quot;&gt;Ugly Delicious&lt;/a&gt;” is now on Netflix.&lt;/p&gt;

    &lt;p&gt;I loved the first season for exploring the complicated notion of “authenticity” in food and its related sociopolitical baggage. While waiting for more, I tried to watch &lt;a href=&quot;https://www.netflix.com/title/81038022&quot;&gt;“Breakfast, Lunch &amp;amp; Dinner” (also starring David Chang)&lt;/a&gt; but the first episode—where Chang gets high with his celebrity friend Seth Rogen—put me off. Thankfully, the first episode of the new season centres on Chang as a nervous dad-to-be so I’m hooked once more.&lt;/p&gt;

    &lt;p&gt;From &lt;a href=&quot;https://www.instagram.com/p/B9NzY9KhpeW/&quot;&gt;Chang’s Instagram&lt;/a&gt;, I discovered there is &lt;a href=&quot;https://en.wikipedia.org/wiki/Zhuazhou&quot;&gt;ritual called “Zhuazhou”&lt;/a&gt; where a child picks an object that forecasts their future. I’m now planning to perform this ritual with C, placing before him a keyboard, a sword and a plastic dough scraper.&lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;We successfully completed the &lt;a href=&quot;/2020/03/02/weeknotes-18/&quot;&gt;2,000 year old problem of moving C into his own room&lt;/a&gt; and so were finally in the market for some sort of baby monitor. As a real &lt;a href=&quot;https://twitter.com/internetofshit&quot;&gt;Internet of Things sceptic&lt;/a&gt;, I was adamant that we buy no device with the word “Smart” in its name. Thankfully, we bought the decidedly dumb &lt;a href=&quot;https://shop.bt.com/products/bt-audio-baby-monitor-400-087429-CLWL.html&quot;&gt;BT Audio Baby Monitor 400&lt;/a&gt; and it is wonderful in its simplicity.&lt;/p&gt;
  &lt;/li&gt;
&lt;/ul&gt;
</content>
</entry><entry>
  <title type="html">Weeknotes 18</title>
  <link href="https://mudge.name/2020/03/02/weeknotes-18/"/>
  <id>https://mudge.name/2020/03/02/weeknotes-18/</id>
  <published>2020-03-02T00:00:00+00:00</published>
  <updated>2020-03-02T00:00:00+00:00</updated>
  <summary type="html">The Tube, dad chat, “Shape Up”, improving sleep with first-order logic and what it means when the clown is down.</summary>
  <content type="html">&lt;ul&gt;
  &lt;li&gt;
    &lt;p&gt;Thanks to all who helped with &lt;a href=&quot;/2020/02/10/weeknotes-15/&quot;&gt;my job search&lt;/a&gt;. I started work with a new client today, commuting on the Tube for the first time in eight years.&lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;Speaking of the Tube, I hate that all news coverage of Coronavirus features a photo of someone wearing a face mask on public transport even when the news isn’t about the UK.&lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;Every time I mention C to someone, I think of &lt;a href=&quot;https://twitter.com/Stew&quot;&gt;Euan&lt;/a&gt;’s mastery of undersharing. I remember first hearing he had a cat after working with him for &lt;em&gt;four&lt;/em&gt; years.&lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;Speaking of dad chat, I can’t wait to invest in a &lt;a href=&quot;https://youtu.be/LSYsmgZEKv0&quot;&gt;Father-Son Podcasting Microphone&lt;/a&gt;:&lt;/p&gt;

    &lt;blockquote&gt;
      &lt;p&gt;Well, it’s tough, you know? I mean, there’s no book you can read about being a dad.&lt;/p&gt;

      &lt;p&gt;Actually, there are several.&lt;/p&gt;
    &lt;/blockquote&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;I’ve always had a soft spot for macOS indie developers. I have been a customer of &lt;a href=&quot;https://www.obdev.at/products/launchbar/index.html&quot;&gt;LaunchBar&lt;/a&gt;, &lt;a href=&quot;https://ranchero.com/netnewswire/&quot;&gt;NetNewsWire&lt;/a&gt; (and its &lt;a href=&quot;https://inessential.com/2018/08/31/netnewswire_comes_home&quot;&gt;recent update&lt;/a&gt;), &lt;a href=&quot;https://c-command.com/dropdmg/&quot;&gt;DropDMG&lt;/a&gt;, &lt;a href=&quot;https://rogueamoeba.com/airfoil/mac/&quot;&gt;Airfoil&lt;/a&gt;, &lt;a href=&quot;https://www.shirt-pocket.com/SuperDuper/SuperDuperDescription.html&quot;&gt;SuperDuper!&lt;/a&gt;, &lt;a href=&quot;https://www.kangacode.com/interarchy/&quot;&gt;Interarchy&lt;/a&gt;, &lt;a href=&quot;https://www.omnigroup.com/omnigraffle&quot;&gt;OmniGraffle&lt;/a&gt;, &lt;a href=&quot;https://www.alsoft.com&quot;&gt;DiskWarrior&lt;/a&gt;, &lt;a href=&quot;https://www.barebones.com/products/bbedit/&quot;&gt;BBEdit&lt;/a&gt;, &lt;a href=&quot;https://panic.com/transmit/&quot;&gt;Transmit&lt;/a&gt;, &lt;a href=&quot;https://c-command.com/spamsieve/&quot;&gt;SpamSieve&lt;/a&gt; and &lt;a href=&quot;https://macromates.com&quot;&gt;TextMate&lt;/a&gt;. This is partly why I &lt;a href=&quot;/2020/02/16/weeknotes-16/&quot;&gt;enjoyed hearing about John Siracusa’s new apps&lt;/a&gt; and why I’m now trying &lt;a href=&quot;https://furbo.org&quot;&gt;Craig Hockenberry&lt;/a&gt;’s new notepad app—&lt;a href=&quot;https://tot.rocks&quot;&gt;Tot&lt;/a&gt;—after reading &lt;a href=&quot;https://daringfireball.net/2020/02/tot&quot;&gt;John Gruber’s summary of it&lt;/a&gt;.&lt;/p&gt;

    &lt;p&gt;I secretly wish I had an idea for an app of my own.&lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;I read &lt;a href=&quot;https://basecamp.com/shapeup&quot;&gt;Ryan Singer’s “Shape Up: Stop Running in Circles and Ship Work that Matters”&lt;/a&gt; and—despite unnecessarily dismissing existing agile practices in the &lt;a href=&quot;https://basecamp.com/shapeup/0.1-foreword&quot;&gt;foreword&lt;/a&gt;—found a lot I agreed with.&lt;/p&gt;

    &lt;p&gt;I’m increasingly convinced of the need for something like &lt;a href=&quot;https://basecamp.com/shapeup/1.1-chapter-02&quot;&gt;shaping&lt;/a&gt; before something is picked up by a development team. The struggle, as Singer writes, is getting the right level of detail: neither &lt;a href=&quot;https://basecamp.com/shapeup/1.1-chapter-02#words-are-too-abstract&quot;&gt;too abstract&lt;/a&gt; nor &lt;a href=&quot;https://basecamp.com/shapeup/1.1-chapter-02#wireframes-are-too-concrete&quot;&gt;too concrete&lt;/a&gt;.&lt;/p&gt;

    &lt;p&gt;I was especially intrigued by the idea of &lt;a href=&quot;https://basecamp.com/shapeup/1.2-chapter-03#setting-the-appetite&quot;&gt;appetite, not estimation&lt;/a&gt; as I’ve often found people unwilling to reveal truly how much they want a particular feature or project and drawing this out up front is extremely appealing.&lt;/p&gt;

    &lt;p&gt;The idea of &lt;a href=&quot;https://basecamp.com/shapeup/1.5-chapter-06&quot;&gt;pitch documents&lt;/a&gt; that can be reviewed asynchronously by members of the team sounds very similar to a Request For Comments (RFC) process (e.g. &lt;a href=&quot;https://github.com/rust-lang/rfcs&quot;&gt;RFCs for changes to Rust&lt;/a&gt;, &lt;a href=&quot;https://github.com/yarnpkg/rfcs&quot;&gt;Yarn&lt;/a&gt; and &lt;a href=&quot;https://github.com/emberjs/rfcs&quot;&gt;Ember&lt;/a&gt;; see also &lt;a href=&quot;https://blog.pragmaticengineer.com/scaling-engineering-teams-via-writing-things-down-rfcs/&quot;&gt;Gergely Orosz’ “Scaling Engineering Teams via Writing Things Down and Sharing - aka RFCs”&lt;/a&gt;): something I am a huge fan of if you’re looking to introduce both rigour and equitability when proposing significant changes to teams and systems.&lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;We took a trip to IKEA to get C a new cot and a Billy bookcase (one of which is sold &lt;a href=&quot;https://www.ikea.com/gb/en/p/billy-bookcase-white-00263850/&quot;&gt;every 5 seconds&lt;/a&gt;). I spent a lot of time in the kids’ section, fawning over the train sets, toy kitchen and its many, many accessories. Luckily for my wallet, C is still too young for them.&lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;As one of my many “jobs to complete before returning to work”, I lovingly re-seasoned our wooden chopping board following &lt;a href=&quot;https://www.seriouseats.com/2015/09/how-to-season-and-maintain-a-wood-cutting-board.html&quot;&gt;J. Kenji López-Alt’s advice&lt;/a&gt;. There is something therapeutic about rubbing a wooden board with coarse salt and half a lemon.&lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;The major thing E and I have been working on is getting C out of our bedroom and into his own room in the hope we can all improve our quality of sleep. Not wanting to confuse him with too much change at once, we first moved ourselves out and onto our sofa bed. When contemplating the next step, I was reminded of the &lt;a href=&quot;https://en.wikipedia.org/wiki/Wolf,_goat_and_cabbage_problem&quot;&gt;2,000 year old problem of the wolf, goat and cabbage&lt;/a&gt;.&lt;/p&gt;

    &lt;p&gt;In turn, this reminded me of a great &lt;a href=&quot;https://london.computation.club&quot;&gt;London Computation Club&lt;/a&gt; meeting back in 2015: &lt;a href=&quot;https://github.com/computationclub/computationclub.github.io/wiki/The-New-Turing-Omnibus-Chapter-58-Predicate-Calculus&quot;&gt;“The New Turing Omnibus, Chapter 58: Predicate Calculus”&lt;/a&gt; when &lt;a href=&quot;https://tomstu.art&quot;&gt;Tom&lt;/a&gt; showed us how to use &lt;a href=&quot;https://en.wikipedia.org/wiki/First-order_logic&quot;&gt;first-order logic&lt;/a&gt; to mechanically resolve the problem.&lt;/p&gt;

    &lt;p&gt;Bonus trivia: that was the same meeting that &lt;a href=&quot;https://github.com/computationclub/computationclub.github.io/wiki/The-New-Turing-Omnibus-Chapter-58-Predicate-Calculus#show--tell&quot;&gt;Chris announced his work on his programming language, Sentient&lt;/a&gt;.&lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;E and I have many phrases that would be incomprehensible to others but a recent favourite is the following emoji-only message:&lt;/p&gt;

    &lt;blockquote&gt;
      &lt;p&gt;🤡⬇️&lt;/p&gt;
    &lt;/blockquote&gt;

    &lt;p&gt;Of course, this translates to “the clown is down” which is a quote from &lt;a href=&quot;https://en.wikipedia.org/wiki/The_Parent_Rap&quot;&gt;The Simpsons’ “The Parent Rap”&lt;/a&gt; we’ve distorted to mean “our baby is asleep”.&lt;/p&gt;
  &lt;/li&gt;
&lt;/ul&gt;
</content>
</entry><entry>
  <title type="html">Weeknotes 17</title>
  <link href="https://mudge.name/2020/02/23/weeknotes-17/"/>
  <id>https://mudge.name/2020/02/23/weeknotes-17/</id>
  <published>2020-02-23T00:00:00+00:00</published>
  <updated>2020-02-23T00:00:00+00:00</updated>
  <summary type="html">A car park disaster, animated GIFs, not rewriting legacy systems and how the internet really works.</summary>
  <content type="html">&lt;ul&gt;
  &lt;li&gt;
    &lt;p&gt;My father-in-law was on &lt;a href=&quot;https://www.bbc.co.uk/programmes/m000fk4f&quot;&gt;The One Show&lt;/a&gt; discussing &lt;a href=&quot;https://www.newcivilengineer.com/archive/liverpool-car-park-fire-a-perfect-storm-02-02-2018/&quot;&gt;why a multi-storey car park in Liverpool burnt down, destroying 1,300 cars&lt;/a&gt;.&lt;/p&gt;

    &lt;p class=&quot;center&quot;&gt;&lt;a href=&quot;https://www.bbc.co.uk/programmes/m000fk4f&quot;&gt;&lt;img src=&quot;/i/hero.gif&quot; width=&quot;480&quot; height=&quot;270&quot; alt=&quot;&quot; /&gt;&lt;/a&gt;&lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;In an effort to be a better son-in-law, I extracted an animated GIF of his heroic introduction and, thanks to &lt;a href=&quot;https://engineering.giphy.com/how-to-make-gifs-with-ffmpeg/&quot;&gt;Collin Burger’s “How to make GIFs with FFMPEG”&lt;/a&gt;, ended up using this one-liner:&lt;/p&gt;

    &lt;div class=&quot;language-console highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;gp&quot;&gt;$&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;ffmpeg &lt;span class=&quot;nt&quot;&gt;-ss&lt;/span&gt; 1499.5 &lt;span class=&quot;nt&quot;&gt;-t&lt;/span&gt; 2.5 &lt;span class=&quot;nt&quot;&gt;-i&lt;/span&gt; oneshow.mp4 &lt;span class=&quot;nt&quot;&gt;-filter_complex&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;[0:v] fps=12,scale=480:-1,split [a][b];[a] palettegen [p];[b][p] paletteuse&quot;&lt;/span&gt; hero.gif
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;    &lt;/div&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;Following a recommendation from &lt;a href=&quot;https://twitter.com/leocassarani&quot;&gt;Leo&lt;/a&gt;, I listened to &lt;a href=&quot;https://oxide.computer/blog/on-the-metal-6-kenneth-finnegan/&quot;&gt;an episode of “On the Metal” interviewing Kenneth Finnegan about starting his own internet exchange&lt;/a&gt;. Despite working on the web for 20 years, it quickly became clear I had no idea how the internet really worked.&lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;I enjoyed &lt;a href=&quot;https://understandlegacycode.com/blog/avoid-rewriting-a-legacy-system-from-scratch-by-strangling-it/&quot;&gt;Nicolas Carlo’s “Avoid rewriting a legacy system from scratch, by strangling it”&lt;/a&gt; about the &lt;a href=&quot;https://martinfowler.com/bliki/StranglerFigApplication.html&quot;&gt;Strangler Fig Application pattern&lt;/a&gt;. It reminded me of the time I failed to migrate the main database in a legacy system from &lt;a href=&quot;https://www.mongodb.com&quot;&gt;MongoDB&lt;/a&gt; to &lt;a href=&quot;https://www.postgresql.org&quot;&gt;PostgreSQL&lt;/a&gt; while trying very hard &lt;a href=&quot;https://www.joelonsoftware.com/2000/04/06/things-you-should-never-do-part-i/&quot;&gt;not to rewrite it from scratch&lt;/a&gt;.&lt;/p&gt;

    &lt;p&gt;When we finally cancelled the project, I wrote a long explanation to the whole company (like an &lt;a href=&quot;https://qz.com/504661/why-etsy-engineers-send-company-wide-emails-confessing-mistakes-they-made/&quot;&gt;Etsy PSA&lt;/a&gt;) called “The State of Custard”. Sadly, having left the company, that is now locked inside someone else’s &lt;a href=&quot;https://basecamp.com&quot;&gt;Basecamp&lt;/a&gt; account.&lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;As part of my &lt;a href=&quot;/2020/02/10/weeknotes-15/&quot;&gt;job search&lt;/a&gt;, I have spent a lot of time editing my &lt;a href=&quot;https://www.linkedin.com/in/paulmucur/&quot;&gt;LinkedIn profile&lt;/a&gt;. In particular, I have changed my “headline” many times in an attempt to concisely explain what I do. At the time of writing it is “Consultant Technical/Engineering Lead”, shamelessly inspired by &lt;a href=&quot;https://www.linkedin.com/in/andrewmcdonough/&quot;&gt;Andrew McDonough&lt;/a&gt; but I’m sure I will continue to agonise about it.&lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;Having tempted fate by writing that our &lt;a href=&quot;/2020/02/16/weeknotes-16/&quot;&gt;white noise snafus were over last week&lt;/a&gt;, our Bluetooth speaker simply stopped charging. Sadly, no amount of &lt;a href=&quot;/2020/01/26/weeknotes-13/&quot;&gt;WD40&lt;/a&gt; could fix the problem.&lt;/p&gt;
  &lt;/li&gt;
&lt;/ul&gt;
</content>
</entry><entry>
  <title type="html">Weeknotes 16</title>
  <link href="https://mudge.name/2020/02/16/weeknotes-16/"/>
  <id>https://mudge.name/2020/02/16/weeknotes-16/</id>
  <published>2020-02-16T00:00:00+00:00</published>
  <updated>2020-02-16T00:00:00+00:00</updated>
  <summary type="html">Typing practice, an erosion of attention, puzzle solving and wishing we’d done everything differently.</summary>
  <content type="html">&lt;ul&gt;
  &lt;li&gt;
    &lt;p&gt;&lt;a href=&quot;/2020/02/10/weeknotes-15/&quot;&gt;The job search&lt;/a&gt; continues.&lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;I’m slowly getting used to &lt;a href=&quot;/2020/02/03/weeknotes-14/&quot;&gt;my split keyboard&lt;/a&gt; thanks to some deliberate &lt;a href=&quot;https://www.keybr.com&quot;&gt;typing practice&lt;/a&gt; recommended by &lt;a href=&quot;https://www.instagram.com/_weszlem/&quot;&gt;Maciej&lt;/a&gt;. I still managed to send an unfinished email instead of pressing the backspace key though.&lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;I was intrigued by &lt;a href=&quot;https://www.theguardian.com/lifeandstyle/2020/feb/07/how-to-stop-spread-of-fake-news-oliver-burkeman&quot;&gt;Oliver Burkeman’s “How to stop the spread of fake news? Pause for a moment”&lt;/a&gt; about &lt;a href=&quot;https://lindastone.net/qa/continuous-partial-attention/&quot;&gt;continuous partial attention&lt;/a&gt;:&lt;/p&gt;

    &lt;blockquote&gt;
      &lt;p&gt;We tend to think of distraction as an all-or-nothing affair: either you’re concentrating successfully on something, or else you’ve been distracted by Twitter or Netflix yet again. But this is more like an erosion of attention, consistent with at least nominally remaining focused on the task at hand.&lt;/p&gt;
    &lt;/blockquote&gt;

    &lt;p&gt;The article’s conclusion reminded me of &lt;a href=&quot;https://xkcd.com/481/&quot;&gt;the xkcd comic, “Listen to Yourself”&lt;/a&gt; (which &lt;a href=&quot;https://blog.xkcd.com/2008/10/08/youtube-audio-preview/&quot;&gt;was actually implemented&lt;/a&gt;).&lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;Shane’s mention that he was trying to write a solver for the &lt;a href=&quot;https://youtu.be/73Meh3NPno4&quot;&gt;“Snake Egg Puzzle”&lt;/a&gt; &lt;a href=&quot;http://technicallyshane.com/2020/02/12/week6.html&quot;&gt;in his weeknotes&lt;/a&gt; led me to recommend &lt;a href=&quot;https://sentient-lang.org&quot;&gt;Chris Patuzzo’s Sentient programming language&lt;/a&gt; for the task and mention the problem to Chris. A few days later and &lt;a href=&quot;https://github.com/tuzz/snake-egg-puzzle&quot;&gt;Chris successfully wrote a program to solve the puzzle and generate new ones&lt;/a&gt;.&lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;We’ve managed to drastically reduce the number of &lt;a href=&quot;/2020/01/26/weeknotes-13/&quot;&gt;white noise snafus&lt;/a&gt; by using &lt;a href=&quot;https://support.apple.com/en-gb/HT202612&quot;&gt;Guided Access&lt;/a&gt; on an old iPad. This way we can still enable and disable the susurration of a mild downpour but have the device ignore all accidental button presses. Thanks to &lt;a href=&quot;https://tomstu.art&quot;&gt;Tom&lt;/a&gt; for drawing my attention to this feature of iOS.&lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;I enjoyed &lt;a href=&quot;https://www.robinsloan.com/notes/home-cooked-app/&quot;&gt;Robin Sloan’s story of building a messaging app for his family, “An app can be a home-cooked meal”&lt;/a&gt;. For similar reasons, I’ve been enjoying hearing about &lt;a href=&quot;https://hypercritical.co/apps/&quot;&gt;John Siracusa’s new macOS apps written to meet his own needs&lt;/a&gt; on &lt;a href=&quot;https://atp.fm&quot;&gt;Accidental Tech Podcast&lt;/a&gt; (&lt;a href=&quot;https://atp.fm/episodes/360&quot;&gt;“Big Hole In the Middle”&lt;/a&gt; and &lt;a href=&quot;https://atp.fm/episodes/365&quot;&gt;“Day-One Cowboy”&lt;/a&gt;).&lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;On a slightly less optimistic note, I also enjoyed &lt;a href=&quot;https://usesthis.com/interviews/graydon.hoare/&quot;&gt;Daniel Bogan’s interview with the creator of the Rust programming language, Graydon Hoare&lt;/a&gt;:&lt;/p&gt;

    &lt;blockquote&gt;
      &lt;p&gt;Software-wise I really wish we’d done almost everything differently. The focus has been on eyeballs and engagement, not like “does it work” or “can it resist attack by a bad actor”. Almost no modern software works well or is safe in any serious sense. It’s always on fire. A sinking ship with everyone frantically bailing. Swiss cheese. Pick your metaphor. So my dream setup would involve “software that has any sort of reliability”. Which probably requires rewinding time to before the personal computing era and enacting liability legislation or something.&lt;/p&gt;
    &lt;/blockquote&gt;
  &lt;/li&gt;
&lt;/ul&gt;
</content>
</entry><entry>
  <title type="html">Weeknotes 15</title>
  <link href="https://mudge.name/2020/02/10/weeknotes-15/"/>
  <id>https://mudge.name/2020/02/10/weeknotes-15/</id>
  <published>2020-02-10T00:00:00+00:00</published>
  <updated>2020-02-10T00:00:00+00:00</updated>
  <summary type="html">Looking for work, laughter, stinging nostrils and writing weeknotes for the future self.</summary>
  <content type="html">&lt;ul&gt;
  &lt;li&gt;
    &lt;p&gt;After thoroughly enjoying my parental leave, I am now &lt;a href=&quot;https://www.ghostcassette.com&quot;&gt;looking for contract work&lt;/a&gt; so if you know anyone who could make use of a former CTO/Technical Lead with 14 years’ experience developing Ruby and JavaScript in teams, &lt;a href=&quot;mailto:paul@ghostcassette.com&quot;&gt;please get in touch&lt;/a&gt;.&lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;It took me a little while but I am loving “&lt;a href=&quot;https://www.mobiusdigitalgames.com/outer-wilds.html&quot;&gt;Outer Wilds&lt;/a&gt;” (and I am &lt;a href=&quot;https://www.polygon.com/2019/12/13/21011871/outer-wilds-goty-best-games-of-the-year&quot; title=&quot;Polygon's Game of the Year #1: Outer Wilds&quot;&gt;not the only one&lt;/a&gt;). I’m looking forward to watching &lt;a href=&quot;https://youtu.be/LbY0mBXKKT0&quot;&gt;Noclip’s “The Making of Outer Wilds - Documentary”&lt;/a&gt; when I’m done.&lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;I nearly nodded my head clean off my neck while reading &lt;a href=&quot;https://basecamp.com/about/policies/until-the-end-of-the-internet&quot;&gt;Basecamp’s “Until the End of the Internet”&lt;/a&gt; policy for their products.&lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;For that reason, I was very excited to &lt;a href=&quot;https://hey.com&quot;&gt;read the teaser for Basecamp’s new email service, “Hey”&lt;/a&gt; coming in April 2020.&lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;The &lt;a href=&quot;https://podcast.panic.com/#episode002&quot;&gt;second episode of the Panic Podcast, “Pantscast”&lt;/a&gt;, is funny for admittedly puerile reasons but it made me laugh all the same. I’m not sure how I missed the app when it was &lt;a href=&quot;https://daringfireball.net/linked/2009/03/09/pantscast&quot;&gt;first released&lt;/a&gt;.&lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;Speaking of laughter, let me join &lt;a href=&quot;https://www.theguardian.com/tv-and-radio/2020/feb/07/frankie-boyles-tour-of-scotland-review-bbc2&quot;&gt;many&lt;/a&gt; &lt;a href=&quot;https://tomstu.art/weeknotes-5-nothing-notable&quot;&gt;others&lt;/a&gt; by recommending &lt;a href=&quot;https://www.bbc.co.uk/iplayer/episode/m000f1vj/frankie-boyles-tour-of-scotland-series-1-1-aberdeen-to-oban&quot;&gt;“Frankie Boyle’s Tour of Scotland”&lt;/a&gt;. There are far too many lines to quote here but let’s just say I have a soft spot for his monologue about the beautiful &lt;a href=&quot;https://en.wikipedia.org/wiki/Glen_Coe&quot;&gt;Glen Coe&lt;/a&gt;.&lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;I didn’t take any notes this week as I spent a large part of it blowing my nose and stinging my nostrils with a mixture of steam and &lt;a href=&quot;https://www.olbas.co.uk/products/olbas-oil/&quot;&gt;Olbas Oil&lt;/a&gt;.&lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;Just as blogging about blogging or podcasting about podcasting is a real faux pas, thinking about writing this week’s notes reminded me of a passage from &lt;a href=&quot;https://www.penguin.co.uk/books/181/181041/the-mark-and-the-void/9780241953860.html&quot;&gt;Paul Murray’s “The Mark and the Void”&lt;/a&gt;:&lt;/p&gt;

    &lt;blockquote&gt;
      &lt;p&gt;Although technology has the capability now to record entire lifetimes, meaning that every moment may be pulled from the foaming sea of oblivion to the dry land of perfect recall, the mythic power of the photograph nevertheless relates to the future, and not to the past. Every recording conceals the secret fantasy of a future self who will observe it; this future self is himself the simulacrum, the &lt;em&gt;persona ficta&lt;/em&gt;. He exists beyond time, beyond action, beyond need; his only function is to witness the continuum of the past, as he might observe the steps that brought him to godhood. Through this fantasy, time is transformed from the condition of loss into a commodity that may be acquired and stockpiled; rather than disappear ceaselessly into the past, life accumulates, each moment becoming a unit of a total self that is the culmination of our experiences in a way that we—biological composites who profligately shed our cells, our memories and our possessions—can never be.&lt;/p&gt;
    &lt;/blockquote&gt;
  &lt;/li&gt;
&lt;/ul&gt;
</content>
</entry><entry>
  <title type="html">Weeknotes 14</title>
  <link href="https://mudge.name/2020/02/03/weeknotes-14/"/>
  <id>https://mudge.name/2020/02/03/weeknotes-14/</id>
  <published>2020-02-03T00:00:00+00:00</published>
  <updated>2020-02-03T00:00:00+00:00</updated>
  <summary type="html">The anaesthetic properties of cloves, saline rinses, split keyboards and yelling at clouds.</summary>
  <content type="html">&lt;ul&gt;
  &lt;li&gt;
    &lt;p&gt;Three days before attending a “&lt;a href=&quot;https://www.nhs.uk/start4life/weaning/&quot;&gt;starting solids&lt;/a&gt;” workshop at a local health centre, E made several batches of soup as I pressed a whole clove against the back of my mouth.&lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;After two days of alternating doses of paracetamol and ibuprofen as rapidly as enclosed leaflets allowed, my dentist confirmed I had &lt;a href=&quot;https://en.wikipedia.org/wiki/Pericoronitis&quot;&gt;pericoronitis&lt;/a&gt; (do not click unless you have a stomach for pink, glistening medical photography) and sent me away with a prescription for antibiotics.&lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;Brushing your teeth and doing a warm saline rinse every time you eat really makes one reconsider snacking. (Apologies to &lt;a href=&quot;https://natbuckley.co.uk&quot;&gt;Nat&lt;/a&gt; and &lt;a href=&quot;https://tomstu.art&quot;&gt;Tom&lt;/a&gt; for mentioning &lt;a href=&quot;https://natbuckley.co.uk/2019/06/02/weeknotes-20-the-wretched-task/&quot;&gt;the wretched task&lt;/a&gt;.)&lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;The elation felt when you can once again eat your morning porridge without first thoroughly mashing banana with a fork is exquisite.&lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;I am clumsily typing this on a &lt;a href=&quot;https://www.microsoft.com/accessories/en-gb/products/keyboards/sculpt-ergonomic-desktop/l5v-00006&quot;&gt;Microsoft Sculpt Ergonomic keyboard&lt;/a&gt;. The split keys make me realise the homegrown touch typing I’ve been practicing for the past two decades might not be &lt;em&gt;technically&lt;/em&gt; correct.&lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;I heartily second &lt;a href=&quot;https://tomstu.art/weeknotes-3-sweating-and-swearing&quot;&gt;Tom’s recommendation&lt;/a&gt; for “&lt;a href=&quot;https://www.imdb.com/title/tt1489887/&quot;&gt;Booksmart&lt;/a&gt;” especially as it is currently &lt;a href=&quot;https://www.amazon.co.uk/Booksmart-Kaitlyn-Dever/dp/B07V2SLM3K&quot;&gt;free for Prime Video subscribers in the UK&lt;/a&gt;.&lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;I took &lt;a href=&quot;https://youtu.be/l2tVGctt4Zo&quot;&gt;Ze Frank’s “Human Test For People Who Work In An Office”&lt;/a&gt;:&lt;/p&gt;

    &lt;blockquote&gt;
      &lt;p&gt;Has a co-worker ever said—in the nicest way—“you look tired” when, in fact, you weren’t tired at all but now you feel bad because you know you look like crap for some unknown reason and somehow your co-worker gets credit for caring?&lt;/p&gt;
    &lt;/blockquote&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;I thought of &lt;a href=&quot;https://www.buzzfeed.com/benrosen/how-to-snapchat-like-the-teens&quot;&gt;Ben Rosen being taught to use Snapchat by his 13-year-old sister&lt;/a&gt; as I posted my first &lt;a href=&quot;https://help.instagram.com/1660923094227526&quot;&gt;Instagram story&lt;/a&gt; despite having previously railed at them. I hadn’t understood that people’s responses would be both private and much more personal than a shallow “like”.&lt;/p&gt;

    &lt;p&gt;Maybe I’ve been &lt;a href=&quot;https://knowyourmeme.com/memes/old-man-yells-at-cloud&quot;&gt;yelling at clouds&lt;/a&gt; all along.&lt;/p&gt;
  &lt;/li&gt;
&lt;/ul&gt;
</content>
</entry><entry>
  <title type="html">Weeknotes 13</title>
  <link href="https://mudge.name/2020/01/26/weeknotes-13/"/>
  <id>https://mudge.name/2020/01/26/weeknotes-13/</id>
  <published>2020-01-26T00:00:00+00:00</published>
  <updated>2020-01-26T00:00:00+00:00</updated>
  <summary type="html">Posture, mouth guards, Guy Fieri’s “Trash Can Nachos” and hearing things in the noise.</summary>
  <content type="html">&lt;ul&gt;
  &lt;li&gt;
    &lt;p&gt;Shoulders back and down.&lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;E, C and I went to see “&lt;a href=&quot;https://www.imdb.com/title/tt5727208/&quot;&gt;Uncut Gems&lt;/a&gt;” at &lt;a href=&quot;https://www.barbican.org.uk/whats-on/series/parent-and-baby-screenings&quot;&gt;baby cinema&lt;/a&gt;. As the stress and tension in the film grew relentlessly, I thought of my last two trips to the dentist about the burgeoning “vertical cracks” on my teeth from &lt;a href=&quot;https://www.nhs.uk/conditions/teeth-grinding/&quot;&gt;grinding&lt;/a&gt;.&lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;For that reason, I found myself curled up in bed, a neoprene wrist support velcroed to one arm and a remouldable mouth guard separating my long-suffering molars. Not so dissimilar to Bart in “&lt;a href=&quot;https://en.wikipedia.org/wiki/The_Last_Temptation_of_Homer&quot;&gt;The Last Temptation of Homer&lt;/a&gt;”, laden with orthopaedic shoes, hair matted with medicated salve and voice hoarse with throat spray.&lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;A friend caught me describing a room as “roasty toasty” and successfully deduced I spend most of my time in conversation with a four-month old baby.&lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;Following &lt;a href=&quot;https://tomstu.art/weeknotes-2-the-good-half&quot;&gt;Tom Stuart’s recommendation&lt;/a&gt;, I started watching &lt;a href=&quot;https://www.bonappetit.com/video/series/reverse-engineering&quot;&gt;Chris Morocco’s “Reverse Engineering”&lt;/a&gt;. In it, Morocco attempts to recreate dishes in two days after only tasting them while blindfolded.&lt;/p&gt;

    &lt;p&gt;From &lt;a href=&quot;https://youtu.be/3eVMgUrEazA&quot;&gt;Gordon Ramsay’s Beef Wellington&lt;/a&gt; to &lt;a href=&quot;https://youtu.be/E3e20718ioY&quot;&gt;Guy Fieri’s “Trash Can Nachos”&lt;/a&gt;, he does consistently well despite never having made these dishes before. There’s a mastery in his ability to quickly recognise various components and techniques in dishes, e.g. a &lt;a href=&quot;https://en.wikipedia.org/wiki/Mornay_sauce&quot;&gt;Mornay sauce&lt;/a&gt; or a &lt;a href=&quot;https://en.wikipedia.org/wiki/Roux&quot;&gt;roux&lt;/a&gt;. Just don’t ask him to &lt;a href=&quot;https://youtu.be/zlG_iUEybgM&quot;&gt;identify a specific cheese&lt;/a&gt;.&lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;In an attempt to improve my posture, I’ve been doing yoga every morning under E’s tutelage. Naturally, I made the ritual my own by struggling to use Spotify to find appropriately soothing muzak on our aging iPad and ended up replacing our entire music setup with a &lt;a href=&quot;https://www.raspberrypi.org&quot;&gt;Raspberry Pi&lt;/a&gt; running &lt;a href=&quot;https://github.com/mikebrady/shairport-sync&quot;&gt;Shairport Sync&lt;/a&gt;. We can now stream any audio of our choice over &lt;a href=&quot;https://support.apple.com/en-gb/HT202809&quot;&gt;AirPlay&lt;/a&gt; and I’m no longer allowed to procrastinate.&lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;Speaking of which: shoulders back and down.&lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;Among our desperate attempts to improve C’s sleep, we use “&lt;a href=&quot;https://www.rainrainapp.com&quot;&gt;Rain Rain&lt;/a&gt;” and a small Bluetooth speaker to play the sound of running water throughout the night. When we’re not accidentally increasing the volume or fast-forwarding to the sound of a raging thunderstorm, I hear the strangest things in the white noise: snatches of a distant news broadcast, the wailing of some audacious post-rock and the hiss of Icelandic death metal on an AM radio.&lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;The self check-in at my GP was out of order this week. After speaking with the receptionist, I settled in the waiting room and looked up to see a display showing a “&lt;a href=&quot;https://www.microsoft.com/en-us/microsoft-365/windows/end-of-windows-7-support&quot;&gt;Your Windows 7 PC is out of support&lt;/a&gt;” screen.&lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;Watching &lt;a href=&quot;https://youtu.be/VPYhem4GRGY&quot;&gt;Claire Saffitz soak toast and popcorn in water to flavour jelly beans&lt;/a&gt; has inspired me to get back into making ice cream. I enjoyed making &lt;a href=&quot;https://www.telegraph.co.uk/foodanddrink/recipes/10957755/Momofukus-Cereal-Milk-ice-cream-recipe.html&quot;&gt;Christina Tosi’s “Cereal Milk ice cream”&lt;/a&gt; but now I’m wondering what else I could steep.&lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;People are never impressed when I talk about my collection of not one, not two but &lt;em&gt;three&lt;/em&gt; types of &lt;a href=&quot;https://www.wd40.com&quot;&gt;WD40&lt;/a&gt;. Only I will know the deep satisfaction of spraying &lt;a href=&quot;https://www.wd40.com/products/contact-cleaner/&quot;&gt;WD40 electrical contact cleaner&lt;/a&gt; into sticky game controllers.&lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;One more time: shoulders back and down.&lt;/p&gt;
  &lt;/li&gt;
&lt;/ul&gt;
</content>
</entry><entry>
  <title type="html">Weeknotes 12</title>
  <link href="https://mudge.name/2020/01/19/weeknotes-12/"/>
  <id>https://mudge.name/2020/01/19/weeknotes-12/</id>
  <published>2020-01-19T00:00:00+00:00</published>
  <updated>2020-01-19T00:00:00+00:00</updated>
  <summary type="html">Yearning for space, failing to make a video, ruining Christmas presents, smart meters and musculoskeletal woes.</summary>
  <content type="html">&lt;ul&gt;
  &lt;li&gt;
    &lt;p&gt;Since returning to London after our stay in a Glasgow tenement last week, I’ve yearned for the high ceilings and sheer &lt;em&gt;space&lt;/em&gt; we enjoyed there. You can often find me standing in each room of our relatively pokey flat, a look of concentration on my face as I desperately try to rearrange furniture in my mind to somehow give us a single morsel more space. So far, I’ve only managed to do this successfully in our bedroom; disappointingly, everywhere else requires the removal of at least one major piece of furniture.&lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;E asked me who &lt;a href=&quot;https://m.imdb.com/name/nm0147147/&quot;&gt;Henry Cavill&lt;/a&gt; plays in &lt;a href=&quot;https://www.netflix.com/title/80189685&quot;&gt;his new Netflix show&lt;/a&gt; and I replied, “the titular Witcher.”&lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;&lt;a href=&quot;https://youtu.be/N8jL_ub9gy4&quot;&gt;Tom Stuart’s video making Anna Jones’ cassoulet&lt;/a&gt; inspired me to make a similar video showing how I bake bread. Not having made a video since I was a teenager, I planned out the various parts I’d need to capture:&lt;/p&gt;

    &lt;ol&gt;
      &lt;li&gt;Making a starter&lt;/li&gt;
      &lt;li&gt;Making a leaven&lt;/li&gt;
      &lt;li&gt;Making the initial dough&lt;/li&gt;
      &lt;li&gt;Autolyse&lt;/li&gt;
      &lt;li&gt;Adding salt and water&lt;/li&gt;
      &lt;li&gt;Folding and bulk fermentation&lt;/li&gt;
      &lt;li&gt;Initial shaping&lt;/li&gt;
      &lt;li&gt;Final shaping&lt;/li&gt;
      &lt;li&gt;Proving overnight&lt;/li&gt;
      &lt;li&gt;Preparing the oven&lt;/li&gt;
      &lt;li&gt;Scoring&lt;/li&gt;
      &lt;li&gt;Baking in a Dutch oven&lt;/li&gt;
    &lt;/ol&gt;

    &lt;p&gt;On reflection, I think I might have to pick something simpler for my first foray into moving pictures.&lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;In an attempt to curb my tissue habit, E gave me a set of handkerchiefs for Christmas. I decided to clean them this morning and may have squirted bleach all over them before really thinking through my actions resulting in an accidental and not entirely pleasant tie-dye.&lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;I’ve been going back through &lt;a href=&quot;https://adam-buxton.co.uk/podcasts&quot;&gt;episodes of Adam Buxton’s podcast&lt;/a&gt; and especially enjoyed &lt;a href=&quot;https://adam-buxton.co.uk/podcasts/ep79-david-sedaris&quot;&gt;his interview with David Sedaris&lt;/a&gt;. It might even have convinced me to purchase my very first audiobook just so that I can hear more of Sedaris’ wonderful storytelling.&lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;We had our electricity meter replaced with a “smart” one which automatically reports readings to our supplier every month. It came with an “in-home display” but my creeping sense of paranoia has:&lt;/p&gt;

    &lt;ol&gt;
      &lt;li&gt;Precluded me from connecting it to our WiFi network&lt;/li&gt;
      &lt;li&gt;Encouraged me to consign the thing to a dark cupboard/possible future in the bin&lt;/li&gt;
    &lt;/ol&gt;

    &lt;p&gt;I suspect my reticence to have any form of “smart” device in our household is increasingly antiquated and quaint.&lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;In a surprise to no one but myself, using my laptop on our sofa seems to have given me an exciting new repetitive strain injury. Sadly, wearing a neoprene splint, moving the computer to our 70s dining table and perching atop a pile of sofa cushions has not improved matters. In fact, I’m writing this week’s notes on my phone thanks to GitHub’s web interface though even this is making one of my hands feel suspiciously cold. Time for a bit of a break from musculoskeletal stress perhaps.&lt;/p&gt;
  &lt;/li&gt;
&lt;/ul&gt;
</content>
</entry><entry>
  <title type="html">Weeknotes 11</title>
  <link href="https://mudge.name/2020/01/14/weeknotes-11/"/>
  <id>https://mudge.name/2020/01/14/weeknotes-11/</id>
  <published>2020-01-14T00:00:00+00:00</published>
  <updated>2020-01-14T00:00:00+00:00</updated>
  <summary type="html">Redesigning the site, swimming lessons, first cousins once removed and screwdrivering.</summary>
  <content type="html">&lt;ul&gt;
  &lt;li&gt;
    &lt;p&gt;I redesigned this site in response to feedback from a former colleague that the old inline lists made it look like the styling had failed to load (never what you want to hear).&lt;/p&gt;

    &lt;p&gt;It doesn’t look too wildly different but I wanted it to resemble &lt;a href=&quot;https://support.apple.com/en-gb/guide/iphone/iphdc30e3b86/ios&quot;&gt;Safari’s Reader Mode&lt;/a&gt; and look good on mobile devices so I’ve cranked both the font size and the contrast throughout.&lt;/p&gt;

    &lt;p&gt;I was most thrilled to replace the old &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;border&lt;/code&gt;-based link underlines so &lt;a href=&quot;https://css-tricks.com/styling-links-with-real-underlines/&quot;&gt;modern browsers can render better ones that respect descenders&lt;/a&gt;.&lt;/p&gt;

    &lt;p&gt;I’ve added an admittedly rather drab personal introduction to the &lt;a href=&quot;/&quot;&gt;home page&lt;/a&gt; after seeing much better introductions on &lt;a href=&quot;https://natbuckley.co.uk&quot;&gt;Nat Buckley&lt;/a&gt; and &lt;a href=&quot;https://alicebartlett.co.uk&quot;&gt;Alice Bartlett&lt;/a&gt;’s sites. I might tweak it some more but &lt;a href=&quot;https://en.wikipedia.org/wiki/Perfect_is_the_enemy_of_good&quot;&gt;perfect is the enemy of the good&lt;/a&gt;.&lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;I felt rather sheepish after reading &lt;a href=&quot;https://m.signalvnoise.com/the-last-tracker-was-just-removed-from-basecamp-com/&quot;&gt;Basecamp removed Google Analytics&lt;/a&gt; so I removed all analytics from this site too. To be honest, I never checked the results and it feels increasingly hypocritical to run a &lt;a href=&quot;https://pi-hole.net&quot;&gt;Pi-hole&lt;/a&gt; at home and yet thoughtlessly track visitors on my own sites.&lt;/p&gt;

    &lt;p&gt;For similar reasons, I’ve also ditched &lt;a href=&quot;https://disqus.com&quot;&gt;Disqus&lt;/a&gt; comments from all posts. I’d disabled it for new posts but it felt a good time to remove them altogether.&lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;C had his very first swimming lesson this week. It consisted mostly of E walking up and down the shallow end of the swimming pool singing nursery rhymes while pumping his fat little legs but did involve submerging him completely under water twice. My own father (who was a competitive swimmer in his youth) is particularly excited to hear C is taking to the water as he recently revealed how disappointed he was that I never truly mastered the front crawl or butterfly. Keep those mildly distressing revelations coming, people!&lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;I was overjoyed to see two people publish their first weeknotes: &lt;a href=&quot;https://tomstu.art/weeknotes-0-collectively-meaningless&quot;&gt;Tom Stuart&lt;/a&gt; and my former colleague &lt;a href=&quot;http://technicallyshane.com/2020/01/07/week1.html&quot;&gt;Shane&lt;/a&gt;. I continue my campaign to convince others to join in: perhaps &lt;em&gt;you’ll&lt;/em&gt; be next?&lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;We all went up to Glasgow over the weekend for E’s grandmother’s 94th birthday. This meant C met three &lt;a href=&quot;/2019/12/16/weeknotes-7/&quot;&gt;grand uncles&lt;/a&gt; and three first cousins once removed (you could argue a second cousin was also present but is still in development). One of his grand uncles described C as someone who “perceives life as an adventure, not a threat” which I thought was rather lovely.&lt;/p&gt;

    &lt;p&gt;I can never remember how cousins work (first? second? how many times removed?) so I am very grateful for &lt;a href=&quot;https://en.wikipedia.org/wiki/Cousin#/media/File:CousinTree.svg&quot;&gt;this excellent family tree diagram from Wikipedia&lt;/a&gt;.&lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;On the train up to Scotland, I enjoyed &lt;a href=&quot;https://adam-buxton.co.uk/podcasts/ep115-adam-joe&quot;&gt;Adam Buxton’s podcast with Joe Cornish&lt;/a&gt; where he mentioned Brian Eno’s concept of “screwdrivering”:&lt;/p&gt;

    &lt;blockquote&gt;
      &lt;p&gt;When I had Brian Eno on the podcast he was saying the impulse is always to &lt;em&gt;add&lt;/em&gt; when you’re making music or making anything creative. You always feel—he calls it “screwdrivering”—you always feel as if you should be putting more in and that’ll be good but actually what you should be doing is taking things out.&lt;/p&gt;
    &lt;/blockquote&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;Speaking of creative endeavours, a &lt;a href=&quot;http://willhigo.com&quot;&gt;good friend of mine&lt;/a&gt; is currently making his first full-length feature film and I keep thinking about the sheer enormity of such a project. Sadly, I won’t be reprising my beloved character of &lt;a href=&quot;https://vimeo.com/14098600&quot;&gt;policeman in the background&lt;/a&gt;.&lt;/p&gt;
  &lt;/li&gt;
&lt;/ul&gt;
</content>
</entry><entry>
  <title type="html">Weeknotes 10</title>
  <link href="https://mudge.name/2020/01/05/weeknotes-10/"/>
  <id>https://mudge.name/2020/01/05/weeknotes-10/</id>
  <published>2020-01-05T00:00:00+00:00</published>
  <updated>2020-01-05T00:00:00+00:00</updated>
  <summary type="html">Revisiting 2018’s yearnotes, sharing podcasts in the park, the rhythm of baby talk and why you wouldn’t be able to cut much with Apple’s scissors emoji.</summary>
  <content type="html">&lt;ul&gt;
  &lt;li&gt;
    &lt;p&gt;Happy New Year! And double digit weeknotes too!&lt;/p&gt;

    &lt;p&gt;It’s amazing to me that it has been over a year since I nervously posted my &lt;a href=&quot;/2019/01/02/2018-yearnotes/&quot;&gt;2018 Yearnotes&lt;/a&gt; having just left a company after eight years. At the time, I was deeply conflicted about my career and didn’t really know what to do. As I wrote then:&lt;/p&gt;

    &lt;blockquote&gt;
      &lt;p&gt;I’m honestly not sure what is next. After working continuously for a decade in London, I plan to take a break from keyboards and screens for a short while before seeing how I can best be of help to others, be that as a contractor or as a full-time employee.&lt;/p&gt;
    &lt;/blockquote&gt;

    &lt;p&gt;My “break from keyboards and screens” ended up being a month of not enjoying playing “&lt;a href=&quot;https://www.rockstargames.com/reddeadredemption2/&quot;&gt;Red Dead Redemption 2&lt;/a&gt;” and then, after interviewing for a few full-time roles, landing my first contract as a freelance software developer through a friend of a friend. It was only for two weeks but it took a mere three days to convince me to commit and set up my own limited company, &lt;a href=&quot;https://www.ghostcassette.com&quot;&gt;Ghost Cassette&lt;/a&gt;.&lt;/p&gt;

    &lt;p&gt;I was extremely fortunate and managed to work with two other companies before C’s turbulent arrival, getting the opportunity to work with technologies I had long wanted to use in production: e.g. &lt;a href=&quot;https://www.terraform.io&quot;&gt;HashiCorp’s Terraform&lt;/a&gt; and &lt;a href=&quot;https://reactjs.org&quot;&gt;Facebook’s React&lt;/a&gt;.&lt;/p&gt;

    &lt;p&gt;Conspicuously missing from my yearnotes is that we knew C was on his way but it was far too early to share with anyone. During E’s pregnancy, we referred to him as “Tujol” due to a vivid dream I had where I found myself in a hospital, watching &lt;a href=&quot;/2019/12/16/weeknotes-7/&quot;&gt;Turkish soap operas&lt;/a&gt; with a young boy of that name dressed in corduroy and with a small black felt triangle covering the end of his nose. It would later prove extremely difficult to stop affectionately referring to him as “Tuj” but it now seems a distant memory.&lt;/p&gt;

    &lt;p&gt;Against all odds, I did &lt;a href=&quot;/2019/11/04/weeknotes-1/&quot;&gt;manage to run again&lt;/a&gt; though.&lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;On New Year’s Eve, E and I continued our tradition of asking each other to reflect on our highlights and lowlights of the past year (and there was no shortage of either) before thinking about what we’d like to change in 2020.&lt;/p&gt;

    &lt;p&gt;While I had a few ideas, a common theme in my resolutions was &lt;a href=&quot;/2019/12/30/weeknotes-9/&quot;&gt;my inability to do nothing&lt;/a&gt; or, more specifically, how often I squander the present worrying about something else (e.g. DIY jobs to be done, being a better person, comparing insurance providers, etc).&lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;I’m a little &lt;a href=&quot;https://www.theguardian.com/tv-and-radio/2019/may/18/the-british-podcast-awards-review-george-the-poet&quot;&gt;late to the party&lt;/a&gt; but have been blown away by &lt;a href=&quot;https://www.georgethepoet.com&quot;&gt;George the Poet’s “Have You Heard George’s Podcast?”&lt;/a&gt;.&lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;Continuing my accidental &lt;a href=&quot;/2019/12/16/weeknotes-7/&quot;&gt;“things you can do with AirPods” column&lt;/a&gt;, E and I listened to “&lt;a href=&quot;https://www.bbc.co.uk/programmes/p07qtmfs&quot;&gt;A Grenfell Story&lt;/a&gt;” together while walking through the park thanks to &lt;a href=&quot;https://support.apple.com/en-us/HT210421&quot;&gt;iOS 13’s audio sharing&lt;/a&gt; which I’m sure made us look extremely antisocial. (I also can’t help but be reminded of the &lt;a href=&quot;https://www.newsweek.com/zune-should-go-beyond-squirting-107255&quot;&gt;Microsoft Zune’s awfully-named “squirting”&lt;/a&gt;.)&lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;On E’s mum’s recommendation, we listened to the director of the &lt;a href=&quot;https://www.cne.psychol.cam.ac.uk&quot;&gt;Centre for Neuroscience in Education&lt;/a&gt; at St. John’s College, Cambridge, &lt;a href=&quot;https://www.bbc.co.uk/programmes/m000cc0w&quot;&gt;Professor Usha Goswami discuss children’s language development on BBC Radio 4’s Woman’s Hour&lt;/a&gt;:&lt;/p&gt;

    &lt;blockquote&gt;
      &lt;p&gt;Rhythm is absolutely fundamental to the way the speech signal is structured. So when we’re talking, we’re creating a pressure wave in the air, it’s like a sound wave. It comes to the brain, the brain has its own intrinsic brain rhythms or brainwaves and effectively what happens is that, through automatic synchronisation processes, the brainwaves at different speeds align themselves with these energy changes in the speech signal that are also occurring at different speeds and that’s how you process the speech signal. It’s actually the most complex processing that your brain does, it’s more complex than vision.&lt;/p&gt;
    &lt;/blockquote&gt;

    &lt;p&gt;Her explanation of the importance of “baby talk” (characterised by the strong-weak rhythm of words such as “mummy”, “daddy”, “baby”, “doggy”, etc.) was fascinating.&lt;/p&gt;

    &lt;p&gt;As a result, we’re singing “&lt;a href=&quot;/2019/12/30/weeknotes-9/&quot;&gt;ye cannae shove your granny off a bus&lt;/a&gt;” with even &lt;em&gt;more&lt;/em&gt; gusto (if such a thing were possible) and I’m trying to break my habit of referring to C solely as “pud” (short for “pudding”, of course).&lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;As with &lt;a href=&quot;/2019/12/16/weeknotes-7/&quot;&gt;my love of knot-related web sites&lt;/a&gt;, I thoroughly enjoyed &lt;a href=&quot;https://wh0.github.io/2020/01/02/scissors.html&quot;&gt;a detailed analysis of which emoji scissors close&lt;/a&gt;. I file it alongside a &lt;a href=&quot;https://mobile.twitter.com/jelenawoehr/status/1191872816372600832?lang=e&quot;&gt;Twitter thread rating every horse emoji&lt;/a&gt; and &lt;a href=&quot;https://curlicuecal.tumblr.com/post/175362924100/an-entomologist-rates-ant-emojis&quot;&gt;an entomologist rating ant emojis&lt;/a&gt;.&lt;/p&gt;
  &lt;/li&gt;
&lt;/ul&gt;

</content>
</entry><entry>
  <title type="html">Weeknotes 9</title>
  <link href="https://mudge.name/2019/12/30/weeknotes-9/"/>
  <id>https://mudge.name/2019/12/30/weeknotes-9/</id>
  <published>2019-12-30T00:00:00+00:00</published>
  <updated>2019-12-30T00:00:00+00:00</updated>
  <summary type="html">Learning to do nothing, a deep, mirk widd and exactly which grandparent you can shove off a bus.</summary>
  <content type="html">&lt;ul&gt;
  &lt;li&gt;
    &lt;p&gt;This past week of relative inactivity is probably best summed up by &lt;a href=&quot;https://www.theguardian.com/lifeandstyle/2019/dec/27/learn-how-to-do-nothing&quot;&gt;Sam Delaney’s “‘No tricks. No mantras. I just want to learn how to do nothing’: my quest to stay still”&lt;/a&gt;:&lt;/p&gt;

    &lt;blockquote&gt;
      &lt;p&gt;“Doing nothing won’t be comfortable at first, however you try,” he says. “You must deliberately put the time aside to do nothing, and have the discipline to get through it, even when it is boring or creates anxiety. You have to sweat it out and gnash your teeth doing nothing for a while, until you learn to cope. The only person you have to live with 24/7 is yourself, so you might as well learn how to do it.”&lt;/p&gt;
    &lt;/blockquote&gt;

    &lt;p&gt;Though I did manage to &lt;a href=&quot;/2019/12/24/weeknotes-8/&quot;&gt;let go of de-duplicating my sister’s photographs&lt;/a&gt;.&lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;I was struck by the poetry of &lt;a href=&quot;https://www.theguardian.com/books/2019/dec/28/its-as-if-im-falling-from-a-50-storey-building-a-novelists-year-without-sleep&quot;&gt;Samantha Harvey’s “‘It’s as if I’m falling from a 50-storey building’: a year without sleep”&lt;/a&gt;:&lt;/p&gt;

    &lt;blockquote&gt;
      &lt;p&gt;Why is it – she’s thinking – that she has to sit there day after day listening to patients who refuse to take responsibility for their own wellbeing? People in Syria can sleep with bombs falling, why can’t you sleep on your king-size mattress with your winter-togged duvet and your kelp-scented hair on a fake-down pillow under a bomb-free sky? What pea disrupts your sleep, princess? A passing Audi? What paucity and fragility of spirit has left you relying on drugs to do that which is the natural inheritance of all animals everywhere and for ever?&lt;/p&gt;
    &lt;/blockquote&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;My &lt;a href=&quot;https://www.ghostcassette.com/function-composition-in-ruby/&quot;&gt;guide to function composition in Ruby&lt;/a&gt; made &lt;a href=&quot;https://rubyweekly.com/issues/481&quot;&gt;Ruby Inside’s “Top 6 Ruby Items of 2019”&lt;/a&gt; which was an unexpected and very pleasant early Christmas present.&lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;Speaking of gifts, we were very spoilt by others’ generosity including C receiving &lt;a href=&quot;http://blackandwhitepublishing.com/the-gruffalo-in-scots.html&quot;&gt;Julia Donaldson’s “The Gruffalo” translated into Scots by James Robertson&lt;/a&gt; so he can really embrace his heritage:&lt;/p&gt;

    &lt;blockquote&gt;
      &lt;p&gt;A moose took a dauner through the deep, mirk widd.&lt;br /&gt;
A tod saw the moose and the moose looked guid.&lt;/p&gt;
    &lt;/blockquote&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;I received a brand new pair of shoes and what was the first thing I did with them? Why re-lace them with &lt;a href=&quot;https://www.fieggen.com/shoelace/overunderlacing.htm&quot;&gt;Ian’s favourite “Over Under” lacing&lt;/a&gt;, of course!&lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;Having had a whale of a time playing it with colleagues earlier in the year, I finally managed to introduce &lt;a href=&quot;https://en.wikipedia.org/wiki/Codenames_(board_game)&quot;&gt;Codenames&lt;/a&gt; to another group of people—my in-laws—via &lt;a href=&quot;https://www.horsepaste.com&quot;&gt;a free online version&lt;/a&gt; and tried to dazzle them with my extremely obtuse code-words (with mixed success).&lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;There’s far too much Christmas television to cover but I was particularly pleased to see &lt;a href=&quot;https://www.imdb.com/name/nm0188871/&quot;&gt;Mackenzie Crook&lt;/a&gt;’s “&lt;a href=&quot;https://www.bbc.co.uk/programmes/m000csng&quot;&gt;Worzel Gummidge&lt;/a&gt;” &lt;a href=&quot;https://www.theguardian.com/tv-and-radio/2019/dec/26/worzel-gummidge-review-mackenzie-crook-hes-back-hes-hilarious-and-hes-wearing-a-cravat&quot;&gt;being well-received&lt;/a&gt; after how much we enjoyed his last series, “&lt;a href=&quot;https://www.bbc.co.uk/programmes/b06l51nr&quot;&gt;Detectorists&lt;/a&gt;”.&lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;I managed to put on an astonishing amount of weight over the festive period so I’ve already started thinking about having some extremely predictable New Year’s Resolutions. The amount of junk food we’ve returned home with (Jaffa Cakes, After Eight Mints, chocolate Santa Clauses, Oreos, a slab of Christmas cake, mince pies, a tube of Smarties, two bags of chocolate coins, a box of Cadbury “Festive Friends”, etc.) only makes that all the more &lt;em&gt;thrilling&lt;/em&gt;.&lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;Finally, thanks to another gift for C, we’ve taken to regularly singing the Scottish classic “&lt;a href=&quot;https://www.scotslanguage.com/articles/node/id/411&quot;&gt;Ye Canny Shove Yer Grannie&lt;/a&gt;” and he loves it. My mum (and therefore his ill-fated “daddy’s mammy” in the rhyme) is less keen.&lt;/p&gt;

    &lt;blockquote&gt;
      &lt;p&gt;Ye cannae shove yer granny off a bus (Push push!)&lt;br /&gt;
No, ye cannae shove yer granny off a bus (Push push!)&lt;br /&gt;
No, ye cannae shove yer granny&lt;br /&gt;
‘Cause she’s yer mammy’s mammy&lt;br /&gt;
Ye cannae shove yer granny off a bus&lt;/p&gt;
    &lt;/blockquote&gt;
  &lt;/li&gt;
&lt;/ul&gt;
</content>
</entry><entry>
  <title type="html">Weeknotes 8</title>
  <link href="https://mudge.name/2019/12/24/weeknotes-8/"/>
  <id>https://mudge.name/2019/12/24/weeknotes-8/</id>
  <published>2019-12-24T00:00:00+00:00</published>
  <updated>2019-12-24T00:00:00+00:00</updated>
  <summary type="html">Being extraordinarily lazy, cobbling together a homemade banneton and this year’s obligatory bit of family busywork.</summary>
  <content type="html">&lt;ul&gt;
  &lt;li&gt;
    &lt;p&gt;My constant low-level consumption of gingerbread and “&lt;a href=&quot;https://www.bbc.co.uk/programmes/b006v5kb&quot;&gt;Homes Under the Hammer&lt;/a&gt;” over the past seven days has made it feel like the most sedentary week in recent memory (and this is coming from someone who was living off freezer food with a newborn not too long ago). Despite my only exercise being moving between sofa and dining chair, it’s been remarkably difficult to find time to write this week’s notes so I’ll be brief!&lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;We spent the week at my in-laws and I wanted to repay their hospitality by whipping up a loaf of bread or two (as one does). This was yet another adventure in breadmaking away from all my various paraphernalia so I had to cobble together something in which to prove the loaves. Thankfully, a couple of bowls lined with a tea towel in a plastic shopping bag did the trick though you really do need to thoroughly dust the towels with some sort of rice or semolina flour. If not, you’re in for the nerve-wracking and delicate task of gingerly teasing apart cloth and cold dough.&lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;A home luxury I’ve been surprised to miss as I idly scroll through my phone on relatives’ WiFi is my &lt;a href=&quot;https://pi-hole.net&quot;&gt;Pi-hole&lt;/a&gt;: the amount of internet advertising it has been dutifully hiding from me (particularly in iOS apps) is quite alarming.&lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;We’re now at my family’s home and every year I fail to simply enjoy my time here as a guest and, instead, find some inane job with which to busy myself.&lt;/p&gt;

    &lt;p&gt;Previously, I’ve found myself precariously balancing on wooden beams in the attic, coordinating with my dad by barking questions down the hatch in order to painstaking identify and label various coaxial cables connected to the TV aerial.&lt;/p&gt;

    &lt;p&gt;This year, I have somehow found myself feeling responsible for ensuring all my sister’s photographs are stored in &lt;a href=&quot;https://www.apple.com/uk/icloud/&quot;&gt;iCloud Photo Library&lt;/a&gt; and—crucially—are free of duplicates.&lt;/p&gt;

    &lt;p&gt;Presumably because she used to be an avid &lt;a href=&quot;https://picasa.google.co.uk&quot;&gt;Picasa&lt;/a&gt; user, for some periods of time she has up to four copies of the exact same photo (sometimes in different sizes). I’m not sure how long this has been the case but I’ve been unable to let this rest and so, based on &lt;a href=&quot;/2019/11/13/scripting-photos-for-macos-with-javascript/&quot;&gt;my recent adventures scripting Photos for macOS&lt;/a&gt;, I’ve been tinkering with cleaning them up automatically.&lt;/p&gt;

    &lt;p&gt;I’ve got as far as the following script but, in my heart of hearts, I know I should just let it be and move on with the holidays.&lt;/p&gt;

    &lt;div class=&quot;language-javascript highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;kd&quot;&gt;const&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;Photos&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;Application&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;Photos&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;

&lt;span class=&quot;kd&quot;&gt;const&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;duplicatesAlbum&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;Photos&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;albums&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;().&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;byName&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;Duplicates&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
&lt;span class=&quot;kd&quot;&gt;const&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;seen&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;Map&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;();&lt;/span&gt;

&lt;span class=&quot;k&quot;&gt;for&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;kd&quot;&gt;const&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;photo&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;of&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;Photos&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;mediaItems&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;())&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;kd&quot;&gt;const&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;lat&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;lng&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;photo&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;location&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;();&lt;/span&gt;
  &lt;span class=&quot;kd&quot;&gt;const&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;date&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;photo&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;date&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;();&lt;/span&gt;
  &lt;span class=&quot;kd&quot;&gt;const&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;description&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;photo&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;description&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;();&lt;/span&gt;
  &lt;span class=&quot;kd&quot;&gt;const&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;fingerprint&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;`date:&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;${&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;date&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt; lat:&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;${&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;lat&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt; lng:&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;${&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;lng&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt; desc:&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;${&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;description&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;`&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;

  &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;!&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;lat&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;||&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;!&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;lng&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;||&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;!&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;date&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;continue&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

  &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;seen&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;has&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;fingerprint&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;))&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;nx&quot;&gt;seen&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;kd&quot;&gt;get&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;fingerprint&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;).&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;push&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;photo&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
  &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;else&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;nx&quot;&gt;seen&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;kd&quot;&gt;set&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;fingerprint&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;photo&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]);&lt;/span&gt;
  &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

&lt;span class=&quot;k&quot;&gt;for&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;kd&quot;&gt;let&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;duplicates&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;of&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;seen&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;kd&quot;&gt;const&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;biggest&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;...&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;rest&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;duplicates&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;sort&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;((&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;a&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;b&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;b&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;width&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;a&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;width&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;());&lt;/span&gt;

  &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;rest&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;length&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;nx&quot;&gt;Photos&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;add&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;rest&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;to&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;duplicatesAlbum&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;});&lt;/span&gt;
  &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;    &lt;/div&gt;

    &lt;p&gt;I’m hoping that tomorrow’s traditional morning sherry will render me blissfully free of caring any more about this particular bit of busywork.&lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;We finally managed to watch “&lt;a href=&quot;https://www.imdb.com/title/tt4154796/&quot;&gt;Avengers: Endgame&lt;/a&gt;” but it took around six sittings and a lot of rewinding. It turns out 3 hour 1 minute action epics are trickier to enjoy with a three-month old.&lt;/p&gt;
  &lt;/li&gt;
&lt;/ul&gt;
</content>
</entry><entry>
  <title type="html">Weeknotes 7</title>
  <link href="https://mudge.name/2019/12/16/weeknotes-7/"/>
  <id>https://mudge.name/2019/12/16/weeknotes-7/</id>
  <published>2019-12-16T00:00:00+00:00</published>
  <updated>2019-12-16T00:00:00+00:00</updated>
  <summary type="html">Great-grand relatives, the joy of tying knots and Turkish soap operas.</summary>
  <content type="html">&lt;ul&gt;
  &lt;li&gt;
    &lt;p&gt;We’re in Yorkshire spending time with our family in the run up to Christmas so please forgive the lateness of this week’s notes.&lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;I was very excited to consult “&lt;a href=&quot;https://www.animatedknots.com&quot;&gt;Animated Knots by Grog&lt;/a&gt;” to fix a new loop of string onto a duster (a lovely &lt;a href=&quot;https://www.animatedknots.com/bowline-knot&quot;&gt;bowline knot&lt;/a&gt; did the trick). I’d be remiss if I didn’t take this opportunity to mention my favourite web site: “&lt;a href=&quot;https://www.fieggen.com/shoelace/&quot;&gt;Ian’s Shoelace Site&lt;/a&gt;”. It embodies exactly what I love about the internet: a true labour of love that is narrow in scope but &lt;em&gt;extremely&lt;/em&gt; deep. I am yet to convert E (much to my annoyance as her laces come undone when we’re out walking) but I never leave home without tying a couple of trusty &lt;a href=&quot;https://www.fieggen.com/shoelace/secureknot.htm&quot;&gt;Ian’s secure shoelace knots&lt;/a&gt;.&lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;Travelling north meant our very first significant road trip with C so we kitted out the car with a backseat baby mirror and tentatively set off up the M1. While we were nervous about C getting upset along the way, I got to indulge in one of my great, guilty pleasures in life: spending time in motorway services. I don’t just mean fancy, peaceful ones like &lt;a href=&quot;https://www.tebayservices.com&quot;&gt;Tebay&lt;/a&gt; but ones with appalling men’s health warnings on toilet stall doors presumably targetted at long haul drivers. I’m not sure what it is about them but I find them curiously relaxing places to be; there’s something about their transitory, incidental nature that is strangely freeing.&lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;C met his great-grandfather and great-grand-aunt for the very first time who are 91 and 95 years his senior respectively (the latter worked at the &lt;a href=&quot;https://en.wikipedia.org/wiki/Avro#History&quot;&gt;Avro “shadow” factory in Leeds during the Second World War&lt;/a&gt; building &lt;a href=&quot;https://en.wikipedia.org/wiki/Avro_Lancaster&quot;&gt;Lancaster bombers&lt;/a&gt;). I’m mostly sharing this to use the term &lt;a href=&quot;https://blogs.ancestry.com/ancestry/2013/10/25/great-and-grand-aunts/&quot;&gt;grand-aunt instead of great-aunt&lt;/a&gt;.&lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;As we’re away from home in houses that actually have more than three rooms, we’ve been putting C down to sleep farther from us than ever before. We don’t have any sort of baby monitor but I’ve been using &lt;a href=&quot;https://support.apple.com/en-us/HT209082&quot;&gt;Live Listen with AirPods&lt;/a&gt; to turn my phone into a microphone and listen in. It has the added benefit of meaning I get to sit with a single earpiece in like some sort of cool dude.&lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;Continuing my streak of recommending &lt;a href=&quot;https://www.theguardian.com/profile/felicity-cloake&quot;&gt;Felicity Cloake&lt;/a&gt; recipes in the past &lt;a href=&quot;/2019/12/08/weeknotes-6/&quot;&gt;few&lt;/a&gt; &lt;a href=&quot;/2019/11/24/weeknotes-4/&quot;&gt;weeks&lt;/a&gt;: we made a large batch of her &lt;a href=&quot;https://www.theguardian.com/food/2019/dec/04/felicity-cloakes-mince-pie-masterclass-an-easy-step-by-step-recipe&quot;&gt;perfect mince pies&lt;/a&gt;. To be honest, I’m just excited whenever we get an excuse to use our increasingly dusty bottle of orange blossom water.&lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;I tried to make a loaf of bread with my sourdough starter (“Colin”) at my mum’s house but got a cracking migraine early in the process which meant a 40 minute &lt;a href=&quot;https://www.bakerybits.co.uk/resources/autolyse-what-why-how/&quot;&gt;autolyse&lt;/a&gt; unexpectedly became a &lt;em&gt;24 hour&lt;/em&gt; autolyse. Seeing as I had already used the ingredients, I decided to go ahead with the rest of the recipe and, while the dough was extremely difficult to handle, I was pleasantly surprised to end up with a decent loaf!&lt;/p&gt;

    &lt;p&gt;I’m increasingly inclined to agree with &lt;a href=&quot;https://interblah.net&quot;&gt;James Adam&lt;/a&gt;:&lt;/p&gt;

    &lt;blockquote&gt;
      &lt;p&gt;I bet if you just chuck all the ingredients in a bowl, stir it once, put it in an oven whose thermostat is triggered on and off by radioactive decay, you’ll get a normal loaf at the end of the process.&lt;/p&gt;
    &lt;/blockquote&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;Spending time with my dad means catching glimpses of the two hour long miniature epics that are &lt;a href=&quot;https://www.theguardian.com/tv-and-radio/2019/sep/13/turkish-tv-magnificent-century-dizi-taking-over-world&quot;&gt;Turkish soap operas, or “dizi”&lt;/a&gt;: I never watch enough to understand the plot but you can guarantee there’ll be some smouldering looks and some fantastic facial hair.&lt;/p&gt;
  &lt;/li&gt;
&lt;/ul&gt;
</content>
</entry><entry>
  <title type="html">Weeknotes 6</title>
  <link href="https://mudge.name/2019/12/08/weeknotes-6/"/>
  <id>https://mudge.name/2019/12/08/weeknotes-6/</id>
  <published>2019-12-08T00:00:00+00:00</published>
  <updated>2019-12-08T00:00:00+00:00</updated>
  <summary type="html">Christmas films, participating in democracy and things a true Englishman will never countenance.</summary>
  <content type="html">&lt;ul&gt;
  &lt;li&gt;
    &lt;p&gt;Following &lt;a href=&quot;/2019/12/01/weeknotes-5/&quot;&gt;last week&lt;/a&gt;’s beginnings of a cold for the newest member of our family, both E and I also succumbed, all three of us sniffling and coughing though none more pitifully than C. I took to using a large muslin square as a handkerchief which gave E great amusement every time I would unfurl it from a pocket while she relied on the restorative power of &lt;a href=&quot;https://www.theguardian.com/lifeandstyle/wordofmouth/2013/jan/02/how-to-make-the-perfect-hot-toddy&quot;&gt;Felicity Cloake’s perfect hot toddies&lt;/a&gt;.&lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;With little desire to leave the house while recuperating, we found ourselves drawn to the neverending stream of made-for-TV Christmas films shown during the day on &lt;a href=&quot;https://www.channel5.com&quot;&gt;Channel 5&lt;/a&gt;. An early highlight was 2017’s “&lt;a href=&quot;https://www.imdb.com/title/tt7030432/&quot;&gt;The Sweetest Christmas&lt;/a&gt;”, which featured the following classic line:&lt;/p&gt;

    &lt;blockquote&gt;
      &lt;p&gt;I have to tell him how I feel the best way I know how: &lt;em&gt;with gingerbread&lt;/em&gt;.&lt;/p&gt;
    &lt;/blockquote&gt;

    &lt;p&gt;A bonus mention has to go out to 2017’s “&lt;a href=&quot;https://www.imdb.com/title/tt7642558/&quot;&gt;Four Christmases and a Wedding&lt;/a&gt;” for its title.&lt;/p&gt;

    &lt;p&gt;All of this reminded me of &lt;a href=&quot;https://maximumfun.org/episodes/who-shot-ya/who-shot-ya-episode-68-christmas-extravaganza-love-actually-white-elephant-and-dan/&quot;&gt;Alonso Duralde’s “El Mero Mero De Navidad Christmas Minute” on “Who Shot Ya?”&lt;/a&gt; where the film critic would review as many &lt;a href=&quot;https://www.hallmarkchannel.com/christmas&quot;&gt;Hallmark Channel Christmas movies&lt;/a&gt; as possible in a minute.&lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;E baked a large batch of &lt;a href=&quot;https://www.theguardian.com/food/2019/nov/30/sweet-christmas-gift-recipes-helen-goh-hazelnut-brittle-rugelach-truffles-gingerbread&quot;&gt;Helen Goh’s black treacle gingerbread&lt;/a&gt; with the treacle left over from &lt;a href=&quot;/2019/11/24/weeknotes-4/&quot;&gt;our Christmas pudding adventures&lt;/a&gt; and, what’s more, even managed to use up &lt;a href=&quot;/2019/11/10/weeknotes-2/&quot;&gt;the blight on our household that is greaseproof paper&lt;/a&gt; to wrap them up as gifts. The combination of large quantities of biscuits and a daily ritual of tea in the afternoon is proving disastrous for my waistline.&lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;We &lt;a href=&quot;https://www.gov.uk/government/publications/apply-for-a-postal-vote&quot;&gt;voted by post&lt;/a&gt; for the very first time which I found rather exciting having only ever been to polling stations in the past. I was particularly intrigued by the design problem of explaining exactly how to fill out a postal ballot; not least because it involves two envelopes and &lt;em&gt;not&lt;/em&gt; tearing off a slip of paper. Perforated paper seems like a classic &lt;a href=&quot;https://en.wikipedia.org/wiki/Affordance#As_perceived_action_possibilities&quot;&gt;affordance&lt;/a&gt; and the temptation to tear it, even having read the instructions multiple times, was huge.&lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;I’ve been using &lt;a href=&quot;https://support.apple.com/en-us/HT204380&quot;&gt;FaceTime&lt;/a&gt; more and more so that distant family members can see C and have been startled when calls have been answered with unfamiliar faces present. It’s not quite &lt;a href=&quot;https://kottke.org/10/06/david-foster-wallace-on-iphone-4s-facetime&quot;&gt;David Foster Wallace’s predictions about video telephony from “Infinite Jest”&lt;/a&gt; but there’s something odd when you expect to see a loved one at the other end of a call and instead find yourself suddenly showcasing your child for a stranger.&lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;Continuing the theme of things for which I lack mental preparation: we visited two nurseries for C as there are waiting lists of up to a year in our area. I was a little caught in the headlights as we were given tours and heard about the various facilities on offer (even being reassured that any information technology used in the nursery was divorced from social media).&lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;Finally, our year-long nightmare of not opening a tiny cardboard door every day is over: it’s advent calendar season!&lt;/p&gt;

    &lt;p&gt;A former colleague of mine really made me question what is sacred when he took up happily consuming entire advent calendars as a snack in the run up to Christmas a few years ago. As I pleaded with him that such a thing was &lt;em&gt;just not done&lt;/em&gt;, I realised that I had become a character in &lt;a href=&quot;https://youtu.be/7lpN-3-SaHs&quot;&gt;a Mitchell and Webb Look sketch about starving Antarctic explorers&lt;/a&gt;:&lt;/p&gt;

    &lt;blockquote&gt;
      &lt;p&gt;Lieutenant, there are some things that a true Englishman will never countenance. That is a Christmas pudding and we are saving it for Christmas, is that understood?&lt;/p&gt;
    &lt;/blockquote&gt;
  &lt;/li&gt;
&lt;/ul&gt;
</content>
</entry><entry>
  <title type="html">Weeknotes 5</title>
  <link href="https://mudge.name/2019/12/01/weeknotes-5/"/>
  <id>https://mudge.name/2019/12/01/weeknotes-5/</id>
  <published>2019-12-01T00:00:00+00:00</published>
  <updated>2019-12-01T00:00:00+00:00</updated>
  <summary type="html">The unexpected joy of baby cinema, doing everything wrong and our daily multiplayer crossword routine.</summary>
  <content type="html">&lt;ul&gt;
  &lt;li&gt;
    &lt;p&gt;For a few years now, cookery shows on TV have been a guilty pleasure of mine: when your day has been a little too trying for the latest nail-biting thriller or existential drama, I find there’s no better way to unwind than to watch &lt;a href=&quot;https://www.jamieoliver.com&quot; title=&quot;Jamie Oliver&quot;&gt;Jamie&lt;/a&gt; “glug” olive oil or “click off” lettuce leaves. This week I indulged in &lt;a href=&quot;https://www.bbc.co.uk/programmes/m000b1qp&quot;&gt;Rick Stein’s new series “Secret France”&lt;/a&gt; which was &lt;a href=&quot;https://www.theguardian.com/tv-and-radio/2019/nov/23/rick-stein-secret-france&quot;&gt;expertly summed up by Joel Golby in his review for The Guardian&lt;/a&gt;:&lt;/p&gt;

    &lt;blockquote&gt;
      &lt;p&gt;Every episode of every TV show Rick Stein has ever made is essentially like watching your dad have a particularly joyful Christmas Day, rubbing his hands with glee over a long-aged sherry, sitting upright to eat a large plate of continental sausages, regaling you with tales of childhoods gone by as soon as someone rolls a braised rabbit out in front of him, saying he’s too full for pudding but eating it anyway.&lt;/p&gt;
    &lt;/blockquote&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;Having waited 12 years since &lt;a href=&quot;https://www.half-life.com/en/episode2&quot; title=&quot;Half-Life 2: Episode Two&quot;&gt;the last episode&lt;/a&gt;, watching &lt;a href=&quot;https://youtu.be/O2W0N3uKXmo&quot;&gt;the announcement trailer&lt;/a&gt; for “&lt;a href=&quot;https://www.half-life.com/en/alyx&quot;&gt;Half-Life: Alyx&lt;/a&gt;” was a strange experience having reached a kind of heavy-hearted acceptance I would never step foot in City 17 again.&lt;/p&gt;

    &lt;p&gt;I last built a PC 15 years ago, specifically to play “&lt;a href=&quot;https://www.half-life.com/en/halflife2&quot;&gt;Half-Life 2&lt;/a&gt;” and it is tempting to repeat that effort to meet &lt;a href=&quot;https://www.half-life.com/en/alyx/vr&quot;&gt;the new game’s lofty VR requirements&lt;/a&gt;. That said, the &lt;a href=&quot;https://store.steampowered.com/valveindex&quot;&gt;Valve Index VR Kit&lt;/a&gt; alone is over £900 so I might just have to rely on the kindness of any VR early adopters I know.&lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;I reluctantly attended my very first &lt;a href=&quot;https://www.barbican.org.uk/whats-on/series/parent-and-baby-screenings&quot;&gt;Parent and Baby Screening at the Barbican&lt;/a&gt; (or, as we like to call it, “baby cinema”) to see &lt;a href=&quot;https://www.netflix.com/gb/title/80223779&quot;&gt;Noah Baumbach’s “Marriage Story”&lt;/a&gt;. I was mostly dreading trying to keep C happy and quiet throughout the film’s two and a quarter hours of running time but, with E’s help, we actually got to enjoy it. It’s not exactly &lt;em&gt;light&lt;/em&gt; viewing for a married couple who have just had a child together but it’s well worth it.&lt;/p&gt;

    &lt;p&gt;Before the film started, I took C for a tactical change and was unexpectedly grilled by a member of staff when trying to re-enter the screen. I was slightly incredulous as I stood there coatless, holding C in my arms as I recited my row and seat number, imagining someone going to great lengths to smuggle their baby into a screening but then I thought of &lt;a href=&quot;https://fs.blog/2012/04/david-foster-wallace-this-is-water/&quot;&gt;David Foster Wallace’s “This Is Water”&lt;/a&gt;.&lt;/p&gt;

    &lt;blockquote&gt;
      &lt;p&gt;The point is that petty, frustrating crap like this is exactly where the work of choosing is gonna come in. Because the traffic jams and crowded aisles and long checkout lines give me time to think, and if I don’t make a conscious decision about how to think and what to pay attention to, I’m gonna be pissed and miserable every time I have to shop. Because my natural default setting is the certainty that situations like this are really all about me. About &lt;em&gt;my&lt;/em&gt; hungriness and &lt;em&gt;my&lt;/em&gt; fatigue and &lt;em&gt;my&lt;/em&gt; desire to just get home, and it’s going to seem for all the world like everybody else is just in my way. And who are all these people in my way? And look at how repulsive most of them are, and how stupid and cow-like and dead-eyed and nonhuman they seem in the checkout line, or at how annoying and rude it is that people are talking loudly on cell phones in the middle of the line. And look at how deeply and personally unfair this is.&lt;/p&gt;
    &lt;/blockquote&gt;

    &lt;p&gt;As Wallace warned, it’s difficult to catch yourself in a moment where you feel personally inconvenienced but it’s good to consider—if only for a second—maybe this isn’t all about &lt;em&gt;you&lt;/em&gt;.&lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;Having said that, it is spectacularly annoying how many different, inconsistent screens one has to find the opt-out link on (no, I don’t want an Amazon Prime trial; no, I don’t want to try Kindle Unlimited) when resetting an &lt;a href=&quot;https://www.amazon.com/kindle&quot;&gt;Amazon Kindle&lt;/a&gt; these days.&lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;In this week’s mostly pointless computer noodling corner: I was keen to get my &lt;a href=&quot;https://www.zsh.org&quot;&gt;zsh&lt;/a&gt; startup time down as much as possible and so used the “faster way to measure the total zsh startup time” from &lt;a href=&quot;https://esham.io/2018/02/zsh-profiling&quot;&gt;Benjamin Esham’s “How to profile your zsh startup time”&lt;/a&gt; to gradually whittle it down under 30 milliseconds.&lt;/p&gt;

    &lt;div class=&quot;language-console highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;gp&quot;&gt;$&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;time &lt;/span&gt;zsh &lt;span class=&quot;nt&quot;&gt;-i&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;-c&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;echo&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;zsh -i -c echo  0.01s user 0.01s system 89% cpu 0.028 total
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;    &lt;/div&gt;

    &lt;p&gt;You can find &lt;a href=&quot;https://github.com/mudge/dotfiles/blob/master/.zshrc&quot;&gt;my full &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;.zshrc&lt;/code&gt; for macOS Catalina on GitHub&lt;/a&gt; but the main trick is to avoid spawning subprocesses where possible. For me, this meant switching my prompt to use &lt;a href=&quot;http://zsh.sourceforge.net/Doc/Release/User-Contributions.html#Version-Control-Information&quot;&gt;the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;vcs_info&lt;/code&gt; function&lt;/a&gt; for showing git branch names and setting up my own alias for &lt;a href=&quot;https://hub.github.com&quot;&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;hub&lt;/code&gt;&lt;/a&gt; rather than using &lt;a href=&quot;https://hub.github.com/hub-alias.1.html&quot;&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;hub alias -s&lt;/code&gt;&lt;/a&gt;.&lt;/p&gt;

    &lt;p&gt;(I told you it was mostly pointless.)&lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;As it is Christmas shopping season, I was roped in to do some technical support as a family member was struggling with an online retailer’s checkout. Fortunately, after listening to &lt;a href=&quot;https://atp.fm/episodes/354&quot;&gt;John Siracusa describe his latest bout of family tech support on Accidental Tech Podcast&lt;/a&gt;, I &lt;a href=&quot;https://support.apple.com/en-gb/guide/messages/icht11883/mac&quot;&gt;used Messages on macOS’ little known “Ask to Share Screen” functionality&lt;/a&gt; to guide them through the process (and subsequently got angry that an email address ending in &lt;a href=&quot;https://en.wikipedia.org/wiki/.name&quot;&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;.name&lt;/code&gt;&lt;/a&gt; was considered invalid 18 years after the domain was first introduced).&lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;A member of our &lt;a href=&quot;https://www.nct.org.uk&quot;&gt;NCT group&lt;/a&gt; told us about &lt;a href=&quot;https://www.bbc.co.uk/programmes/b09tf362&quot;&gt;an excellent episode of Adam Buxton’s BBC Radio 4 show “You’re Doing It Wrong” about parenting&lt;/a&gt;:&lt;/p&gt;

    &lt;blockquote&gt;
      &lt;p&gt;In this programme, we’re taking a look at parenting and proving that no matter how hard you try, you’re probably going to screw it up and raise a psychopath who hates you.&lt;/p&gt;
    &lt;/blockquote&gt;

    &lt;p&gt;This quickly led to us listening to &lt;a href=&quot;https://www.bbc.co.uk/programmes/b09v3gwk&quot;&gt;all other episodes&lt;/a&gt; covering topics such as &lt;a href=&quot;https://www.bbc.co.uk/programmes/b09snrb7&quot;&gt;work&lt;/a&gt;, &lt;a href=&quot;https://www.bbc.co.uk/programmes/b09v6vt6&quot;&gt;diet&lt;/a&gt; and &lt;a href=&quot;https://www.bbc.co.uk/programmes/b09w0vhz&quot;&gt;the environment&lt;/a&gt;.&lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;I started reading &lt;a href=&quot;https://en.wikipedia.org/wiki/The_Three-Body_Problem_(novel)&quot;&gt;Cixin Liu’s award-winning “The Three-Body Problem”&lt;/a&gt; and was immediately struck by how much of what I read is based in USA or Western Europe and what a refreshing change it is to read something set elsewhere (in this case, China).&lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;I finally made a batch of &lt;a href=&quot;https://www.bonappetit.com/recipe/bas-best-chocolate-chip-cookies&quot;&gt;Bon Appétit’s Best Chocolate Chip Cookies&lt;/a&gt; following &lt;a href=&quot;https://chriszetter.com&quot;&gt;Chris Zetter&lt;/a&gt;’s recommendation. I was warned they would spread when baking but I was still taken aback by &lt;a href=&quot;/i/cookies.jpg&quot;&gt;how much&lt;/a&gt;! Luckily, they are delicious and even better the next day when they’ve firmed up a bit (eating them shortly after baking was basically like eating warm caramel-flavoured melted chocolate).&lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;Speaking of Chris, we have now formed a daily bedtime routine of using “&lt;a href=&quot;https://multicrosser.chriszetter.com&quot;&gt;Several People are Solving&lt;/a&gt;” to do &lt;a href=&quot;https://www.theguardian.com/crosswords/series/quick&quot;&gt;The Guardian quick crossword&lt;/a&gt; together. &lt;a href=&quot;https://chriszetter.com/blog/2018/12/02/multiplayer-crosswords/&quot;&gt;Chris wrote up how he made it in “Multiplayer Crosswords”&lt;/a&gt; and I highly recommend giving it a go with a fellow crossword enthusiast.&lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;C has his very first cold so I’ll be spending today mostly worrying about him and audibly aching at every pitiful sneeze and cough.&lt;/p&gt;
  &lt;/li&gt;
&lt;/ul&gt;
</content>
</entry><entry>
  <title type="html">Weeknotes 4</title>
  <link href="https://mudge.name/2019/11/24/weeknotes-4/"/>
  <id>https://mudge.name/2019/11/24/weeknotes-4/</id>
  <published>2019-11-24T00:00:00+00:00</published>
  <updated>2019-11-24T00:00:00+00:00</updated>
  <summary type="html">Struggling with structured data, Christmas puddings, the joy of repairing things and everything being a trade-off.</summary>
  <content type="html">&lt;ul&gt;
  &lt;li&gt;
    &lt;p&gt;I read &lt;a href=&quot;https://asthasr.github.io/posts/danger-of-simplicity/&quot;&gt;Blake Hyde’s “The Danger of ‘Simplicity’”&lt;/a&gt; this week and wished I had written it. I’ve wanted to write about everything being a trade-off for years (I even have a draft from 2014 but sadly it only contains a bulleted list of technology buzzwords) but this is a fantastic, concise piece on the difficulty of pursuing simplicity in software.&lt;/p&gt;

    &lt;blockquote&gt;
      &lt;p&gt;In other words: every decision made in order to simplify a program will cost something.&lt;/p&gt;
    &lt;/blockquote&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;I also enjoyed &lt;a href=&quot;https://getkiss.org/blog/20191004a&quot;&gt;Dylan Araps’ “I’ve gone to great lengths for this silence”&lt;/a&gt; as relentlessly optimising web page markup is also a bit of a guilty pleasure of mine. Dylan goes much further than I have but I thoroughly appreciated their efforts.&lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;On the topic of markup, I did &lt;em&gt;not&lt;/em&gt; have fun trying to switch to using &lt;a href=&quot;https://developers.google.com/search/docs/guides/sd-policies&quot;&gt;Google’s recommended structured data&lt;/a&gt; (based on &lt;a href=&quot;https://json-ld.org&quot;&gt;JSON-LD&lt;/a&gt;) for this site. The &lt;a href=&quot;https://search.google.com/structured-data/testing-tool&quot;&gt;Google Structured Data Testing Tool&lt;/a&gt; does not approve of these blog posts (marked up as &lt;a href=&quot;https://schema.org/BlogPosting&quot;&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;BlogPosting&lt;/code&gt;&lt;/a&gt;s) not having a &lt;a href=&quot;https://schema.org/publisher&quot;&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;publisher&lt;/code&gt;&lt;/a&gt; that is an &lt;a href=&quot;https://schema.org/Organization&quot;&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Organization&lt;/code&gt;&lt;/a&gt; with a mandatory &lt;a href=&quot;https://schema.org/logo&quot;&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;logo&lt;/code&gt;&lt;/a&gt;. Frustrated, I added only the markup that seemed to make sense but I risk the ire of Googlebot.&lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;E made our very first batch of Christmas puddings using &lt;a href=&quot;https://www.theguardian.com/lifeandstyle/wordofmouth/2016/nov/17/how-to-cook-the-perfect-christmas-pudding&quot;&gt;Felicity Cloake’s “perfect” recipe&lt;/a&gt; (prefixing search terms with “Felicity Cloake perfect” is the first thing I do before cooking or baking &lt;em&gt;anything&lt;/em&gt;) and we each took it in turns (including C) to stir the mixture. Unexpected bonus baked good: we used up the rest of the recipe’s Guinness in &lt;a href=&quot;https://www.nigella.com/recipes/chocolate-guinness-cake&quot;&gt;Nigella Lawson’s Chocolate Guinness Cake&lt;/a&gt; and let’s just say that our daily regimen of tea and cake in the afternoon isn’t doing wonders for my weight. I’d be remiss if I didn’t also mention my former colleague &lt;a href=&quot;https://github.com/mattmacleod/Recipes/blob/master/Desserts%20and%20puddings/Christmas%20pudding.md&quot;&gt;Matt MacLeod’s Christmas pudding recipe&lt;/a&gt; which features both &lt;a href=&quot;https://www.agbarr.co.uk/our-brands/irn-bru/&quot;&gt;IRN-BRU&lt;/a&gt; and &lt;a href=&quot;https://www.buckfast.com/&quot;&gt;Buckfast&lt;/a&gt; and is always in great demand.&lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;There’s a new series of &lt;a href=&quot;https://www.bbc.co.uk/programmes/b08l581p&quot;&gt;BBC’s “The Repair Shop”&lt;/a&gt; and it truly warms my heart. Having already been made a fan of &lt;a href=&quot;https://www.youtube.com/channel/UCMrMVIBtqFW6O0-MWq26gqw&quot;&gt;mechanical restoration videos&lt;/a&gt; following &lt;a href=&quot;https://twitter.com/urbanautomaton/status/1087089421424513025&quot;&gt;Simon Coffey’s recommendation&lt;/a&gt;, this takes the calming effect of watching a true expert methodically repair something and combines it with heartwrenching human emotion. E still teases me about being in floods of tears hearing &lt;a href=&quot;https://www.bbc.co.uk/programmes/m0004ppp&quot;&gt;the tale of “wheelie ted”&lt;/a&gt;.&lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;We began watching &lt;a href=&quot;https://tv.apple.com/us/show/the-morning-show/umc.cmc.25tn3v8ku4b39tr6ccgb8nl6m&quot;&gt;Apple TV+’s “The Morning Show”&lt;/a&gt; as E has a free trial for a year and I was overjoyed to be reminded of the existence of &lt;a href=&quot;https://www.benjaminclementine.com&quot;&gt;Benjamin Clementine’s wonderful music&lt;/a&gt; in &lt;a href=&quot;https://open.spotify.com/track/7aNwPr0nR3zocELBBe0Xzv?si=oNhmBpTpRLqbYEuB7aCb3Q&quot;&gt;its theme song, “Nemesis”&lt;/a&gt;.&lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;We’ve taken to singing nursery rhymes to C but I’m having to make alterations to &lt;a href=&quot;https://open.spotify.com/track/0EXodPnj0bgFFUg0o80s5S?si=PitqtcxiSWqkzyOn8du0TA&quot;&gt;the version of “The Wheels on the Bus” we found on Spotify&lt;/a&gt; as we’re not sure “the mummies on the bus go ‘chatter, chatter, chatter’” and “the daddies on the bus go ‘nod, nod, nod’” is a great depiction of our family life.&lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;I’m not exercising great restraint in sharing pictures, videos and stories of C with friends (have I told you he’s taking to shouting “goo”?) but a poignant moment in &lt;a href=&quot;https://www.bbc.co.uk/iplayer/episode/m00077cb/mortimer-whitehouse-gone-fishing-series-2-episode-1&quot;&gt;a recent episode of BBC’s “Mortimer &amp;amp; Whitehouse: Gone Fishing”&lt;/a&gt; as Bob talked about the loss of his mother gave me pause:&lt;/p&gt;

    &lt;blockquote&gt;
      &lt;p&gt;When my mum died, so I had zero parents, do you know the thing I really miss? And I miss it nearly every Sunday, because I used to phone her every Sunday, so it always passes through my mind even now on Sundays is… It was lovely having someone to tell your kids’ little achievements. Someone who &lt;em&gt;really&lt;/em&gt; was interested. No-one else cares less, do they, really? But you could phone up your mum and she loved hearing what they’d said or what they’d done. Now I’ve got no-one to tell, you know?&lt;/p&gt;
    &lt;/blockquote&gt;
  &lt;/li&gt;
&lt;/ul&gt;
</content>
</entry><entry>
  <title type="html">Weeknotes 3</title>
  <link href="https://mudge.name/2019/11/16/weeknotes-3/"/>
  <id>https://mudge.name/2019/11/16/weeknotes-3/</id>
  <published>2019-11-16T00:00:00+00:00</published>
  <updated>2019-11-16T00:00:00+00:00</updated>
  <summary type="html">Writing, solving logic puzzles and the Bake Off we deserve.</summary>
  <content type="html">&lt;ul&gt;
  &lt;li&gt;
    &lt;p&gt;While we initially recoiled at the adverts for the &lt;a href=&quot;https://www.channel4.com/programmes/junior-bake-off&quot;&gt;Junior Bake Off&lt;/a&gt;, after reading that &lt;a href=&quot;https://www.theguardian.com/tv-and-radio/2019/nov/11/junior-bake-off-the-kids-spinoff-thats-the-perfect-antidote-to-baking-brutality&quot;&gt;it is “the Bake Off we deserve” after this year’s disappointing series&lt;/a&gt;, we gave it a go and are now fully converted. Watching the contestants recover from baking disasters and help each other without hesitation is truly joyful in a way the regular series totally failed to be.&lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;After my &lt;a href=&quot;/2019/11/10/weeknotes-2.html&quot;&gt;computer noodling last week&lt;/a&gt;, I decided to write up in detail how to &lt;a href=&quot;/2019/11/12/using-a-raspberry-pi-for-time-machine/&quot;&gt;use a Raspberry Pi for Time Machine&lt;/a&gt; and how to &lt;a href=&quot;/2019/11/13/scripting-photos-for-macos-with-javascript/&quot;&gt;script Photos for macOS with JavaScript&lt;/a&gt;. It was good to write something in-depth again, especially for the first post as I wanted to ensure every step was both necessary and justified which was especially tricky given &lt;a href=&quot;/2019/11/12/using-a-raspberry-pi-for-time-machine/#configuring-avahi&quot;&gt;the rather opaque configuration necessary for Avahi&lt;/a&gt;.&lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;Long-time host of the &lt;a href=&quot;http://lrug.org&quot;&gt;London Ruby User Group&lt;/a&gt; &lt;a href=&quot;https://www.linkedin.com/pulse/skills-matter-appointed-administrators-wendy-devolder/&quot;&gt;Skills Matter recently went into administration&lt;/a&gt; and their recordings of previous talks have sadly been disappearing from the internet. Among them, the video of my March 2015 talk &lt;a href=&quot;/2014/11/26/data-structures-as-functions.html&quot;&gt;“Exploring &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;#to_proc&lt;/code&gt;”&lt;/a&gt; seems to have been been removed. Another day, another reason to think about the 21-year old &lt;a href=&quot;https://www.w3.org/Provider/Style/URI&quot;&gt;“Cool URIs don’t change”&lt;/a&gt;.&lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;Having said that, I changed the &lt;a href=&quot;https://jekyllrb.com/docs/permalinks/#built-in-formats&quot;&gt;format of permalinks&lt;/a&gt; on this site to drop the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;.html&lt;/code&gt; extension from the URLs. Thankfully, there’s a &lt;a href=&quot;https://github.com/jekyll/jekyll-redirect-from&quot;&gt;redirection plugin for Jekyll&lt;/a&gt; to keep old links working.&lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;&lt;a href=&quot;https://codon.com&quot;&gt;Tom Stuart&lt;/a&gt; shared the following &lt;a href=&quot;https://math.stackexchange.com/questions/2217248/which-answer-in-this-list-is-the-correct-answer-to-this-question&quot;&gt;logic puzzle&lt;/a&gt; on the &lt;a href=&quot;https://computationclub-slack.herokuapp.com/&quot;&gt;London Computation Club Slack&lt;/a&gt;:&lt;/p&gt;

    &lt;blockquote&gt;
      &lt;p&gt;Which answer in this list is the correct answer to this question?&lt;/p&gt;

      &lt;ol&gt;
        &lt;li&gt;All of the below.&lt;/li&gt;
        &lt;li&gt;None of the below.&lt;/li&gt;
        &lt;li&gt;All of the above.&lt;/li&gt;
        &lt;li&gt;One of the above.&lt;/li&gt;
        &lt;li&gt;None of the above.&lt;/li&gt;
        &lt;li&gt;None of the above.&lt;/li&gt;
      &lt;/ol&gt;
    &lt;/blockquote&gt;

    &lt;p&gt;I thought this would be an excellent opportunity to use &lt;a href=&quot;https://sentient-lang.org&quot;&gt;Chris Patuzzo’s Sentient programming language&lt;/a&gt; to solve it:&lt;/p&gt;

    &lt;div class=&quot;language-ruby highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;n&quot;&gt;bool&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;answer1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;answer2&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;answer3&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;answer4&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;answer5&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;answer6&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;

&lt;span class=&quot;c1&quot;&gt;# 1. All of the below.&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;invariant&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;answer1&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;==&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;answer2&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;answer3&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;answer4&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;answer5&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;answer6&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;].&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;all?&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;*&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;self&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;

&lt;span class=&quot;c1&quot;&gt;# 2. None of the below.&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;invariant&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;answer2&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;==&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;answer3&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;answer4&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;answer5&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;answer6&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;].&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;none?&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;*&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;self&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;

&lt;span class=&quot;c1&quot;&gt;# 3. All of the above.&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;invariant&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;answer3&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;==&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;answer1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;answer2&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;].&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;all?&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;*&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;self&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;

&lt;span class=&quot;c1&quot;&gt;# 4. One of the above.&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;invariant&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;answer4&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;==&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;answer1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;answer2&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;answer3&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;].&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;one?&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;*&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;self&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;

&lt;span class=&quot;c1&quot;&gt;# 5. None of the above.&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;invariant&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;answer5&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;==&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;answer1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;answer2&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;answer3&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;answer4&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;].&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;none?&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;*&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;self&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;

&lt;span class=&quot;c1&quot;&gt;# 6. None of the above.&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;invariant&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;answer6&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;==&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;answer1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;answer2&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;answer3&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;answer4&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;answer5&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;].&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;none?&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;*&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;self&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;

&lt;span class=&quot;n&quot;&gt;expose&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;answer1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;answer2&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;answer3&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;answer4&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;answer5&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;answer6&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;    &lt;/div&gt;

    &lt;p&gt;Which returned a single answer: number 5!&lt;/p&gt;

    &lt;div class=&quot;language-console highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;gp&quot;&gt;$&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;sentient &lt;span class=&quot;nt&quot;&gt;--number&lt;/span&gt; 0 answers.snt
&lt;span class=&quot;go&quot;&gt;{&quot;answer1&quot;:false,&quot;answer2&quot;:false,&quot;answer3&quot;:false,&quot;answer4&quot;:false,&quot;answer5&quot;:true,&quot;answer6&quot;:false}
{}
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;    &lt;/div&gt;

    &lt;p&gt;The story of Sentient is a great one and &lt;a href=&quot;https://whyarecomputers.com/4&quot;&gt;is told excellently by Chris and Tom in episode 4 of “Why Are Computers”&lt;/a&gt;.&lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;A blog post circulated this week &lt;a href=&quot;https://thesmartnik.com/solving-pazzles-wth-amb.html&quot;&gt;about solving puzzles with Amb&lt;/a&gt; so I thought I’d try using &lt;a href=&quot;https://github.com/chikamichi/amb&quot;&gt;an ambiguous function/operator implementation in Ruby&lt;/a&gt; to solve the logic puzzle as well:&lt;/p&gt;

    &lt;div class=&quot;language-ruby highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;nb&quot;&gt;require&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;'amb'&lt;/span&gt;

&lt;span class=&quot;no&quot;&gt;A&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;Class&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;kp&quot;&gt;include&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;Amb&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;}.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;new&lt;/span&gt;

&lt;span class=&quot;n&quot;&gt;answer1&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;A&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;choose&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;kp&quot;&gt;true&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;kp&quot;&gt;false&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;answer2&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;A&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;choose&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;kp&quot;&gt;true&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;kp&quot;&gt;false&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;answer3&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;A&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;choose&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;kp&quot;&gt;true&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;kp&quot;&gt;false&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;answer4&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;A&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;choose&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;kp&quot;&gt;true&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;kp&quot;&gt;false&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;answer5&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;A&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;choose&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;kp&quot;&gt;true&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;kp&quot;&gt;false&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;answer6&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;A&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;choose&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;kp&quot;&gt;true&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;kp&quot;&gt;false&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;

&lt;span class=&quot;c1&quot;&gt;# 1. All of the below.&lt;/span&gt;
&lt;span class=&quot;no&quot;&gt;A&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;assert&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;answer1&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;==&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;answer2&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;answer3&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;answer4&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;answer5&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;answer6&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;].&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;all?&lt;/span&gt;

&lt;span class=&quot;c1&quot;&gt;# 2. None of the below.&lt;/span&gt;
&lt;span class=&quot;no&quot;&gt;A&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;assert&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;answer2&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;==&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;answer3&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;answer4&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;answer5&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;answer6&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;].&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;none?&lt;/span&gt;

&lt;span class=&quot;c1&quot;&gt;# 3. All of the above.&lt;/span&gt;
&lt;span class=&quot;no&quot;&gt;A&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;assert&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;answer3&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;==&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;answer1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;answer2&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;].&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;all?&lt;/span&gt;

&lt;span class=&quot;c1&quot;&gt;# 4. One of the above.&lt;/span&gt;
&lt;span class=&quot;no&quot;&gt;A&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;assert&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;answer4&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;==&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;answer1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;answer2&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;answer3&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;].&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;one?&lt;/span&gt;

&lt;span class=&quot;c1&quot;&gt;# 5. None of the above.&lt;/span&gt;
&lt;span class=&quot;no&quot;&gt;A&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;assert&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;answer5&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;==&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;answer1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;answer2&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;answer3&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;answer4&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;].&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;none?&lt;/span&gt;

&lt;span class=&quot;c1&quot;&gt;# 6. None of the above.&lt;/span&gt;
&lt;span class=&quot;no&quot;&gt;A&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;assert&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;answer6&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;==&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;answer1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;answer2&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;answer3&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;answer4&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;answer5&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;].&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;none?&lt;/span&gt;

&lt;span class=&quot;nb&quot;&gt;puts&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;Answer 1: &lt;/span&gt;&lt;span class=&quot;si&quot;&gt;#{&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;answer1&lt;/span&gt;&lt;span class=&quot;si&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;&lt;/span&gt;
&lt;span class=&quot;nb&quot;&gt;puts&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;Answer 2: &lt;/span&gt;&lt;span class=&quot;si&quot;&gt;#{&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;answer2&lt;/span&gt;&lt;span class=&quot;si&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;&lt;/span&gt;
&lt;span class=&quot;nb&quot;&gt;puts&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;Answer 3: &lt;/span&gt;&lt;span class=&quot;si&quot;&gt;#{&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;answer3&lt;/span&gt;&lt;span class=&quot;si&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;&lt;/span&gt;
&lt;span class=&quot;nb&quot;&gt;puts&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;Answer 4: &lt;/span&gt;&lt;span class=&quot;si&quot;&gt;#{&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;answer4&lt;/span&gt;&lt;span class=&quot;si&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;&lt;/span&gt;
&lt;span class=&quot;nb&quot;&gt;puts&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;Answer 5: &lt;/span&gt;&lt;span class=&quot;si&quot;&gt;#{&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;answer5&lt;/span&gt;&lt;span class=&quot;si&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;&lt;/span&gt;
&lt;span class=&quot;nb&quot;&gt;puts&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;Answer 6: &lt;/span&gt;&lt;span class=&quot;si&quot;&gt;#{&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;answer6&lt;/span&gt;&lt;span class=&quot;si&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;    &lt;/div&gt;

    &lt;p&gt;And it returned the same answer!&lt;/p&gt;

    &lt;div class=&quot;language-console highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;gp&quot;&gt;$&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;ruby answers.rb
&lt;span class=&quot;go&quot;&gt;Answer 1: false
Answer 2: false
Answer 3: false
Answer 4: false
Answer 5: true
Answer 6: false
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;    &lt;/div&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;A friend bought C a lovely &lt;a href=&quot;https://uk.tomy.com/products/lamaze-freddie-firefly&quot;&gt;toy firefly&lt;/a&gt; which constantly reminds me of the butterfly from &lt;a href=&quot;https://youtu.be/sXOdn6vLCuU&quot;&gt;Don’t Hug Me I’m Scared 3&lt;/a&gt; (we’d even taken to calling it “Bertie the Butterfly”). Pesky bee.&lt;/p&gt;
  &lt;/li&gt;
&lt;/ul&gt;
</content>
</entry><entry>
  <title type="html">Scripting Photos for macOS with JavaScript</title>
  <link href="https://mudge.name/2019/11/13/scripting-photos-for-macos-with-javascript/"/>
  <id>https://mudge.name/2019/11/13/scripting-photos-for-macos-with-javascript/</id>
  <published>2019-11-13T00:00:00+00:00</published>
  <updated>2019-11-13T00:00:00+00:00</updated>
  <summary type="html">Automatically editing photo metadata for iCloud Photos using JavaScript for Automation.</summary>
  <content type="html">&lt;p&gt;I recently imported hundreds of home videos from the past 20 years into &lt;a href=&quot;https://www.apple.com/uk/macos/photos/&quot;&gt;Photos for macOS&lt;/a&gt; only to discover that none of the videos had the correct date and time. Thankfully, the correct date and time were encoded into each video’s filename, e.g. &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;CLIP-2003-08-02 21;40;28.MOV&lt;/code&gt; but I didn’t fancy updating them all by hand.&lt;/p&gt;

&lt;p&gt;As I use &lt;a href=&quot;https://support.apple.com/en-gb/HT204264&quot;&gt;iCloud Photos&lt;/a&gt; to back up and store all my videos and photos, I needed to find a way to update individual items’ metadata that would also register with iCloud. As editing the underlying files stored inside the rather opaque &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Photos Library.photoslibrary&lt;/code&gt; wasn’t guaranteed to propagate changes to other devices, I decided the safest approach was to use Photos for macOS directly and rely on the &lt;a href=&quot;https://developer.apple.com/library/archive/documentation/LanguagesUtilities/Conceptual/MacAutomationScriptingGuide/index.html&quot;&gt;JavaScript for Automation&lt;/a&gt; framework introduced in OS X 10.10. This gives us the ability to use JavaScript in place of the rather more arcane &lt;a href=&quot;https://developer.apple.com/library/archive/documentation/AppleScript/Conceptual/AppleScriptLangGuide/introduction/ASLR_intro.html#//apple_ref/doc/uid/TP40000983&quot;&gt;AppleScript&lt;/a&gt; to automate actions in macOS applications.&lt;/p&gt;

&lt;ol&gt;
  &lt;li&gt;&lt;a href=&quot;#running-scripts&quot;&gt;Running scripts&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;#using-the-photos-dictionary&quot;&gt;Using the Photos Dictionary&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;#the-final-script&quot;&gt;The final script&lt;/a&gt;&lt;/li&gt;
&lt;/ol&gt;

&lt;h2 id=&quot;running-scripts&quot;&gt;Running scripts&lt;/h2&gt;

&lt;p&gt;&lt;img src=&quot;/i/script-editor.png&quot; class=&quot;pull-right&quot; width=&quot;375&quot; height=&quot;379&quot; alt=&quot;&quot; /&gt; There are several ways to write these scripts but let’s start by using the Script Editor application found in &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;/Applications/Utilities&lt;/code&gt;, starting a new script and choosing “JavaScript” from the “Script language” dropdown.&lt;/p&gt;

&lt;p&gt;If we open up Photos and select a few items, we can run the following JavaScript in our Script Editor window to print out the selected item filenames:&lt;/p&gt;

&lt;div class=&quot;language-javascript highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;kd&quot;&gt;const&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;Photos&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;Application&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;Photos&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;

&lt;span class=&quot;k&quot;&gt;for&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;kd&quot;&gt;const&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;photo&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;of&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;Photos&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;selection&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;())&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;nx&quot;&gt;console&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;log&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;photo&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;filename&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;());&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Note we’re using relatively modern JavaScript features here from &lt;a href=&quot;http://es6-features.org/#Constants&quot;&gt;ECMAScript 6&lt;/a&gt; such as &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;const&lt;/code&gt; and &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;for...of&lt;/code&gt;, this is because &lt;a href=&quot;https://github.com/JXA-Cookbook/JXA-Cookbook/wiki/ES6-Features-in-JXA&quot;&gt;JavaScript for Automation uses the JavaScriptCore from the version of Safari bundled with macOS&lt;/a&gt; and I’m running macOS Catalina 10.15 and Safari 13.&lt;/p&gt;

&lt;p&gt;Running the script will print its output in the bottom pane of the Script Editor, hopefully listing the filenames of your selected items.&lt;/p&gt;

&lt;p&gt;Alternatively, if you’re more comfortable on the command line, we can start a REPL using &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;osascript&lt;/code&gt;:&lt;/p&gt;

&lt;div class=&quot;language-console highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;gp&quot;&gt;$&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;osascript &lt;span class=&quot;nt&quot;&gt;-il&lt;/span&gt; JavaScript
&lt;span class=&quot;gp&quot;&gt;&amp;gt;&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;gt;&lt;/span&gt; Application&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;Photos&quot;&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;)&lt;/span&gt;.selection&lt;span class=&quot;o&quot;&gt;()&lt;/span&gt;
&lt;span class=&quot;gp&quot;&gt;=&amp;gt;&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;o&quot;&gt;[&lt;/span&gt;Application&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;Photos&quot;&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;)&lt;/span&gt;.mediaItems.byId&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;16AE4D88-E094-466E-B726-74CE4E876DE5/L0/001&quot;&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;)&lt;/span&gt;, Application&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;Photos&quot;&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;)&lt;/span&gt;.mediaItems.byId&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;D11B78C8-82CD-472F-9039-DA148FEF8092/L0/001&quot;&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;)]&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h2 id=&quot;using-the-photos-dictionary&quot;&gt;Using the Photos Dictionary&lt;/h2&gt;

&lt;p&gt;&lt;img src=&quot;/i/photos-dictionary.png&quot; class=&quot;pull-right&quot; width=&quot;375&quot; height=&quot;324&quot; alt=&quot;&quot; /&gt; While it is all well and good printing filenames, what else can we do with our items? To find out, we can open the Photos “Dictionary” (AppleScript terminology for its API documentation) using Script Editor by going to &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;File &amp;gt; Open Dictionary...&lt;/code&gt; and choosing &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Photos.app&lt;/code&gt; from the list.&lt;/p&gt;

&lt;p&gt;You can then choose &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;JavaScript&lt;/code&gt; from the language dropdown and browse the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Photos Suite&lt;/code&gt; to see the various objects and properties available to you. It’s useful to know that the root object you’ll be using is the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Application&lt;/code&gt; which is described as follows:&lt;/p&gt;

&lt;blockquote&gt;
  &lt;p&gt;&lt;strong&gt;Application&lt;/strong&gt; &lt;em&gt;Object&lt;/em&gt; [see also Standard Suite] : The top level scripting object for Photos.&lt;/p&gt;

  &lt;p&gt;ELEMENTS&lt;br /&gt;
contains containers, albums, folders, mediaItems.&lt;/p&gt;

  &lt;p&gt;PROPERTIES&lt;br /&gt;
&lt;strong&gt;selection&lt;/strong&gt; (list of MediaItem, r/o) : The currently selected media items in the application&lt;br /&gt;
&lt;strong&gt;favoritesAlbum&lt;/strong&gt; (Album, r/o) : Favorited media items album.&lt;br /&gt;
&lt;strong&gt;slideshowRunning&lt;/strong&gt; (boolean, r/o) : Returns true if a slideshow is currently running.&lt;br /&gt;
&lt;strong&gt;recentlyDeletedAlbum&lt;/strong&gt; (Album, r/o) : The set of recently deleted media items&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;“Elements” here refer to properties on the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Application(&quot;Photos&quot;)&lt;/code&gt; object so you could print the filename of the first item in your whole library like so:&lt;/p&gt;

&lt;div class=&quot;language-javascript highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;nx&quot;&gt;console&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;log&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;Application&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;Photos&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;).&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;mediaItems&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;].&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;filename&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;());&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;However, while it appears that &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;mediaItems&lt;/code&gt; here is an array, it is actually a function:&lt;/p&gt;

&lt;div class=&quot;language-javascript highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;k&quot;&gt;typeof&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;Application&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;Photos&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;).&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;mediaItems&lt;/span&gt;
&lt;span class=&quot;o&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;dl&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;function&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&quot;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;It just happens that these “elements” functions allow you to access specific elements by their index but if you want a true JavaScript array, you’ll need to explicitly call the property as a function:&lt;/p&gt;

&lt;div class=&quot;language-javascript highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;c1&quot;&gt;// Missing out the () on albums causes an error&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;for&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;kd&quot;&gt;const&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;album&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;of&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;Application&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;Photos&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;).&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;albums&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;())&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;nx&quot;&gt;console&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;log&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;album&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;());&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;“Properties” are also functions: to read them, you need to call them. Note that, unlike “elements”, they do not support indexing and must always be called as functions:&lt;/p&gt;

&lt;div class=&quot;language-javascript highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;nx&quot;&gt;Application&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;Photos&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;).&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;selection&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;
&lt;span class=&quot;o&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;Application&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;Photos&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;).&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;mediaItems&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;byId&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;16AE4D88-E094-466E-B726-74CE4E876DE5/L0/001&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;),&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;Application&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;Photos&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;).&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;mediaItems&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;byId&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;D11B78C8-82CD-472F-9039-DA148FEF8092/L0/001&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)]&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Read-only properties are documented as &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;r/o&lt;/code&gt; (such as &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;selection&lt;/code&gt; above) but properties without this mean they can be updated.&lt;/p&gt;

&lt;p&gt;If we take a look at the documentation for a &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;MediaItem&lt;/code&gt;, we can see the two properties we’re interested in:&lt;/p&gt;

&lt;blockquote&gt;
  &lt;p&gt;&lt;strong&gt;MediaItem&lt;/strong&gt; &lt;em&gt;Object&lt;/em&gt; : A media item, such as a photo or video.&lt;/p&gt;

  &lt;p&gt;PROPERTIES&lt;br /&gt;
&lt;strong&gt;date&lt;/strong&gt; (date) : The date of the media item&lt;br /&gt;
&lt;strong&gt;filename&lt;/strong&gt; (text, r/o) : The name of the file on disk.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;In order to read the filename and date, we know to call them as functions:&lt;/p&gt;

&lt;div class=&quot;language-javascript highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;nx&quot;&gt;Application&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;Photos&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;).&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;selection&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()[&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;].&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;filename&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;
&lt;span class=&quot;o&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;dl&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;IMG_9295.HEIC&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&quot;&lt;/span&gt;

&lt;span class=&quot;nx&quot;&gt;Application&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;Photos&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;).&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;selection&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()[&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;].&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;date&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;
&lt;span class=&quot;o&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;Tue&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;Nov&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;12&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;2019&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;08&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;28&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;34&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;GMT&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;+&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;0000&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;GMT&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;However, to &lt;em&gt;set&lt;/em&gt; a property, we set it as we would any property in JavaScript (even though there is already a function with the same name):&lt;/p&gt;

&lt;div class=&quot;language-javascript highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;nx&quot;&gt;Application&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;Photos&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;).&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;selection&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()[&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;].&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;date&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;Date&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;2001&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;12&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;o&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;Mon&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;Jan&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;01&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;2001&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;12&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;00&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;00&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;GMT&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;+&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;0000&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;GMT&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h2 id=&quot;the-final-script&quot;&gt;The final script&lt;/h2&gt;

&lt;p&gt;With this and a little help from &lt;a href=&quot;https://developer.mozilla.org/en-US/docs/Web/JavaScript/Guide/Regular_Expressions&quot;&gt;JavaScript Regular Expressions&lt;/a&gt;, we’re now able to write a script to update my videos:&lt;/p&gt;

&lt;div class=&quot;language-javascript highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;c1&quot;&gt;// Regular expression to extract date and times from filenames&lt;/span&gt;
&lt;span class=&quot;kd&quot;&gt;const&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;pattern&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;sr&quot;&gt;/CLIP-&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;(\d{4})&lt;/span&gt;&lt;span class=&quot;sr&quot;&gt;-&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;(\d{2})&lt;/span&gt;&lt;span class=&quot;sr&quot;&gt;-&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;(\d{2})&lt;/span&gt;&lt;span class=&quot;sr&quot;&gt; &lt;/span&gt;&lt;span class=&quot;se&quot;&gt;(\d{2})&lt;/span&gt;&lt;span class=&quot;sr&quot;&gt;;&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;(\d{2})&lt;/span&gt;&lt;span class=&quot;sr&quot;&gt;;&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;(\d{2})\.&lt;/span&gt;&lt;span class=&quot;sr&quot;&gt;MOV/&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;

&lt;span class=&quot;c1&quot;&gt;// Pull out all the items from my current selection that match the above pattern&lt;/span&gt;
&lt;span class=&quot;kd&quot;&gt;const&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;clips&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;Application&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;Photos&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;).&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;selection&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;().&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;filter&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;item&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;pattern&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;test&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;item&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;filename&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()));&lt;/span&gt;

&lt;span class=&quot;c1&quot;&gt;// For every matching clip...&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;for&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;kd&quot;&gt;const&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;clip&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;of&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;clips&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;

  &lt;span class=&quot;c1&quot;&gt;// Take the current filename&lt;/span&gt;
  &lt;span class=&quot;kd&quot;&gt;const&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;filename&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;clip&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;filename&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;();&lt;/span&gt;

  &lt;span class=&quot;c1&quot;&gt;// Extract the date and time from the above filename&lt;/span&gt;
  &lt;span class=&quot;kd&quot;&gt;const&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;[,&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;year&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;month&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;day&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;hours&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;minutes&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;seconds&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;filename&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;match&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;pattern&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;

  &lt;span class=&quot;c1&quot;&gt;// Create a new Date object for the above (note months are 0-indexed in JavaScript)&lt;/span&gt;
  &lt;span class=&quot;kd&quot;&gt;const&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;newDate&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;Date&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;year&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;month&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;day&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;hours&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;minutes&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;seconds&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;

  &lt;span class=&quot;c1&quot;&gt;// Log the change we are about to make for posterity&lt;/span&gt;
  &lt;span class=&quot;nx&quot;&gt;console&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;log&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;`Setting &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;${&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;filename&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt; date to &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;${&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;newDate&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt; from &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;${&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;clip&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;date&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()}&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;`&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;

  &lt;span class=&quot;c1&quot;&gt;// Update the date, persisting the change to iCloud Photos&lt;/span&gt;
  &lt;span class=&quot;nx&quot;&gt;clip&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;date&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;newDate&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
</content>
</entry><entry>
  <title type="html">Using a Raspberry Pi for Time Machine</title>
  <link href="https://mudge.name/2019/11/12/using-a-raspberry-pi-for-time-machine/"/>
  <id>https://mudge.name/2019/11/12/using-a-raspberry-pi-for-time-machine/</id>
  <published>2019-11-12T00:00:00+00:00</published>
  <updated>2019-11-12T00:00:00+00:00</updated>
  <summary type="html">How to share a disk with Samba and Avahi on a Raspberry Pi for use with macOS backups.</summary>
  <content type="html">&lt;p&gt;&lt;img src=&quot;/i/timemachine.png&quot; class=&quot;pull-right&quot; width=&quot;360&quot; height=&quot;246&quot; alt=&quot;&quot; /&gt; Ever since &lt;a href=&quot;https://en.wikipedia.org/wiki/Mac_OS_X_Leopard&quot;&gt;Mac OS X Leopard&lt;/a&gt;, connecting an external storage device to your Mac will ask you if you want to use that storage to automatically back up all of your files with a feature called &lt;a href=&quot;https://support.apple.com/en-us/HT201250&quot;&gt;Time Machine&lt;/a&gt;. However, Time Machine can also be configured to &lt;a href=&quot;https://support.apple.com/en-gb/HT202784#nas&quot;&gt;back up to storage elsewhere on your network&lt;/a&gt; and not directly connected to your computer.&lt;/p&gt;

&lt;p&gt;We’re going to use a &lt;a href=&quot;https://www.raspberrypi.org&quot;&gt;Raspberry Pi&lt;/a&gt; (I used an old Model B) as a cheap alternative to a dedicated Network-attached Storage (NAS) device for use with Time Machine. I’m going to use an external hard drive connected via USB but the same principles should apply to any storage available to the Raspberry Pi.&lt;/p&gt;

&lt;ol&gt;
  &lt;li&gt;&lt;a href=&quot;#software-dependencies&quot;&gt;Software dependencies&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;#readying-our-storage&quot;&gt;Readying our storage&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;#configuring-samba&quot;&gt;Configuring Samba&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;#configuring-avahi&quot;&gt;Configuring Avahi&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;#accessing-your-backups-in-the-finder&quot;&gt;Accessing your backups in the Finder&lt;/a&gt;&lt;/li&gt;
&lt;/ol&gt;

&lt;h2 id=&quot;software-dependencies&quot;&gt;Software dependencies&lt;/h2&gt;

&lt;p&gt;First, ensure you’re using a recent version of &lt;a href=&quot;https://www.raspberrypi.org/downloads/raspbian/&quot;&gt;Raspbian&lt;/a&gt; so we can rely entirely on Debian packages already built for your Raspberry Pi and avoid compiling software ourselves (which can take quite some time on older models). If not, please see the &lt;a href=&quot;https://www.raspberrypi.org/documentation/installation/installing-images/README.md&quot;&gt;official documentation for installing a Raspberry Pi operating system image on an SD card&lt;/a&gt;. At the time of writing, I used the September 2019 version of Raspbian Buster with a kernel version of 4.19.&lt;/p&gt;

&lt;p&gt;With Raspbian up and running, let’s install the two packages we’re going to need: &lt;a href=&quot;https://www.samba.org&quot;&gt;Samba&lt;/a&gt; and the &lt;a href=&quot;https://www.avahi.org&quot;&gt;Avahi&lt;/a&gt; daemon.&lt;/p&gt;

&lt;div class=&quot;language-console highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;gp&quot;&gt;pi@raspberrypi:~ $&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;sudo &lt;/span&gt;apt &lt;span class=&quot;nb&quot;&gt;install &lt;/span&gt;samba avahi-daemon
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Samba is an open source implementation of the SMB file sharing protocol which is &lt;a href=&quot;https://developer.apple.com/library/archive/releasenotes/NetworkingInternetWeb/Time_Machine_SMB_Spec/index.html&quot;&gt;officially supported by Time Machine&lt;/a&gt; for backing up over a network. This is all we really need to back up to storage attached to our Raspberry Pi but we can make it slightly easier to set up on the macOS end by also using Avahi’s implementation of the mDNS/DNS-SD protocol (sometimes called &lt;a href=&quot;https://en.wikipedia.org/wiki/Bonjour_(software)&quot;&gt;“Bonjour”&lt;/a&gt;) to make our Time Machine share automatically discoverable on our network. This way we don’t need to manually connect to our Raspberry Pi from our Mac, it will automatically appear both in the Finder’s sidebar and in the Time Machine System Preferences.&lt;/p&gt;

&lt;p&gt;It’s important to have a recent version of Samba so that we can benefit from the latest support for macOS-specific extensions. At the time of writing, the versions I used were Samba 4.9.5-Debian and avahi-daemon 0.7.&lt;/p&gt;

&lt;p&gt;Note that we are &lt;em&gt;not&lt;/em&gt; using &lt;a href=&quot;http://netatalk.sourceforge.net&quot;&gt;Netatalk&lt;/a&gt; as we want to use SMB rather than AFP for file sharing as that seems to be the preferred file sharing protocol for network-attached storage in &lt;a href=&quot;https://support.apple.com/en-gb/HT202784#nas&quot;&gt;Apple’s current documentation&lt;/a&gt;.&lt;/p&gt;

&lt;h2 id=&quot;readying-our-storage&quot;&gt;Readying our storage&lt;/h2&gt;

&lt;p&gt;With our dependencies in place, let’s ready our storage. If you have this set up already, please feel free to &lt;a href=&quot;#configuring-samba&quot;&gt;skip ahead to Samba configuration&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;I use a cheap USB enclosure with a 500 GB hard disk inside and I want to use the whole disk for Time Machine. Connecting it to my Raspberry Pi makes it instantly discoverable with &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;blkid&lt;/code&gt; which will print attributes for all connected block devices:&lt;/p&gt;

&lt;div class=&quot;language-console highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;gp&quot;&gt;pi@raspberrypi:~ $&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;sudo &lt;/span&gt;blkid
&lt;span class=&quot;go&quot;&gt;/dev/mmcblk0p1: LABEL_FATBOOT=&quot;boot&quot; LABEL=&quot;boot&quot; UUID=&quot;3725-1C05&quot; TYPE=&quot;vfat&quot; PARTUUID=&quot;d565a392-01&quot;
/dev/mmcblk0p2: LABEL=&quot;rootfs&quot; UUID=&quot;fd695ef5-f047-44bd-b159-2a78c53af20a&quot; TYPE=&quot;ext4&quot; PARTUUID=&quot;d565a392-02&quot;
/dev/sda: UUID=&quot;e613b4f3-7fb8-463a-a65d-42a14148ea65&quot; TYPE=&quot;ext4&quot;
/dev/mmcblk0: PTUUID=&quot;d565a392&quot; PTTYPE=&quot;dos&quot;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;The key entry here is &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;/dev/sda&lt;/code&gt;: that’s my USB drive and its unique UUID I can use when configuring it.&lt;/p&gt;

&lt;p&gt;As the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;TYPE=&quot;ext4&quot;&lt;/code&gt; in the output reveals, I’ve already formatted it with the default Linux &lt;a href=&quot;https://en.wikipedia.org/wiki/Ext4&quot;&gt;ext4 journaling file system&lt;/a&gt; but let’s do it again for good measure:&lt;/p&gt;

&lt;div class=&quot;language-console highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;gp&quot;&gt;pi@raspberrypi:~ $&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;sudo &lt;/span&gt;mkfs &lt;span class=&quot;nt&quot;&gt;-t&lt;/span&gt; ext4 /dev/sda
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;We’re making a trade-off here: using ext4 means we won’t be able to plug our drive directly into a Mac and read and write to it but we benefit from the reliability of a &lt;a href=&quot;https://en.wikipedia.org/wiki/Journaling_file_system&quot;&gt;journaled file system&lt;/a&gt;. If we used Apple’s HFS+ instead, we would be able to plug our drive directly into a Mac but &lt;a href=&quot;https://en.wikipedia.org/wiki/HFS_Plus#Linux&quot;&gt;we would have to disable journaling&lt;/a&gt; thereby losing write safety. As the integrity of our backups is paramount and as long as we are happy doing both back ups and restores over our network, ext4 seems a good compromise between convenience and safety.&lt;/p&gt;

&lt;p&gt;Note this isn’t an encrypted file system so anyone can read anything written to it. We could use &lt;a href=&quot;https://en.wikipedia.org/wiki/Linux_Unified_Key_Setup&quot;&gt;Linux Unified Key Setup&lt;/a&gt; to avoid this but Time Machine will store any network backups in a &lt;a href=&quot;https://en.wikipedia.org/wiki/Sparse_image&quot;&gt;sparse image&lt;/a&gt; which can itself be encrypted. To simplify setup, let’s rely on Time Machine’s encryption to keep our backups secure and leave our underlying storage unencrypted.&lt;/p&gt;

&lt;p&gt;Now we want to mount this filesystem on our Raspberry Pi so that we can access it like any other directory. Let’s do this and ensure that the mount persists whenever the Raspberry Pi restarts by appending an entry to the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;fstab&lt;/code&gt;:&lt;/p&gt;

&lt;div class=&quot;language-console highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;gp&quot;&gt;pi@raspberrypi:~ $&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;echo&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;'UUID=e613b4f3-7fb8-463a-a65d-42a14148ea65	/media	ext4	noexec,nodev,noatime,nodiratime	0	0'&lt;/span&gt; | &lt;span class=&quot;nb&quot;&gt;sudo tee&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;-a&lt;/span&gt; /etc/fstab
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;This instructs our Raspberry Pi to mount the drive identified by the given UUID (which we got earlier when calling &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;blkid&lt;/code&gt;) at the directory &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;/media&lt;/code&gt; as an ext4 filesystem with a variety of flags:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;noexec&lt;/code&gt;: do not allow direct execution of any binaries on the mounted filesystem as it’s only going to be use for backup storage;&lt;/li&gt;
  &lt;li&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;nodev&lt;/code&gt;: do not interpret character or block special devices on the filesystem;&lt;/li&gt;
  &lt;li&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;noatime&lt;/code&gt;: do not store access times on this filesystem;&lt;/li&gt;
  &lt;li&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;nodiratime&lt;/code&gt;: do not store directory access times on this filesystem.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;(&lt;strong&gt;Update:&lt;/strong&gt; I previously recommended using the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;sync&lt;/code&gt; flag but &lt;a href=&quot;https://twitter.com/DriesOeyen/status/1341408326664523779&quot;&gt;Dries Oeyen pointed out this typically has a big impact on performance&lt;/a&gt; and the &lt;a href=&quot;https://www.kernel.org/doc/Documentation/filesystems/ext4.txt&quot;&gt;ext4 filesystem&lt;/a&gt; doesn’t support it, instead preferring the use of &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;commit=nrsec&lt;/code&gt; to specify a number of seconds after which data and metadata will be synchronised.)&lt;/p&gt;

&lt;p&gt;The two numbers at the end of the line instruct your Raspberry Pi to ignore this filesystem when using &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;dump&lt;/code&gt; and not to check it with &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;fsck&lt;/code&gt; when rebooting.&lt;/p&gt;

&lt;p&gt;I’m using &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;/media&lt;/code&gt; because I have no other storage attached to my Raspberry Pi but you could use any directory of your choice.&lt;/p&gt;

&lt;p&gt;Now we can actually mount the filesystem based on the definition we just added to our &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;fstab&lt;/code&gt;:&lt;/p&gt;

&lt;div class=&quot;language-console highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;gp&quot;&gt;pi@raspberrypi:~ $&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;sudo &lt;/span&gt;mount /media
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;This allows us to now read and write files on our freshly formatted USB drive.&lt;/p&gt;

&lt;p&gt;Let’s create a separate user for storing backups to match the one on my Mac:&lt;/p&gt;

&lt;div class=&quot;language-console highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;gp&quot;&gt;pi@raspberrypi:~ $&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;sudo &lt;/span&gt;adduser mudge
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Let’s store all our backups in a new directory and assign ownership of the entire disk to this new user:&lt;/p&gt;

&lt;div class=&quot;language-console highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;gp&quot;&gt;pi@raspberrypi:~ $&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;sudo mkdir&lt;/span&gt; /media/backups
&lt;span class=&quot;gp&quot;&gt;pi@raspberrypi:~ $&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;sudo chown&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;-R&lt;/span&gt; mudge: /media
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;As I’m using a spinning disk and Time Machine doesn’t run constantly, it’d be nice if we could put the disk in standby after 10 minutes of inactivity. To do this, we’ll need to install &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;hdparm&lt;/code&gt; to set a standby (spindown) timeout in 5 second increments, again identifying our disk by its UUID:&lt;/p&gt;

&lt;div class=&quot;language-console highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;gp&quot;&gt;pi@raspberrypi:~ $&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;sudo &lt;/span&gt;apt &lt;span class=&quot;nb&quot;&gt;install &lt;/span&gt;hdparm
&lt;span class=&quot;gp&quot;&gt;pi@raspberrypi:~ $&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;sudo &lt;/span&gt;hdparm &lt;span class=&quot;nt&quot;&gt;-S&lt;/span&gt; 120 /dev/disk/by-uuid/e613b4f3-7fb8-463a-a65d-42a14148ea65
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;We can make this permanent by adding the following stanza to &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;/etc/hdparm.conf&lt;/code&gt;:&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;/dev/disk/by-uuid/e613b4f3-7fb8-463a-a65d-42a14148ea65 {
	spindown_time = 120
}
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h2 id=&quot;configuring-samba&quot;&gt;Configuring Samba&lt;/h2&gt;

&lt;p&gt;Now that we have storage available to share with our Mac, let’s set it up with Samba so that it will work with Time Machine.&lt;/p&gt;

&lt;p&gt;To do this, we need to add a new share definition for our directory at &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;/media/backups&lt;/code&gt; owned by the user &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;mudge&lt;/code&gt; to our Samba configuration in &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;/etc/samba/smb.conf&lt;/code&gt;:&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;[backups]
    comment = Backups
    path = /media/backups
    valid users = mudge
    read only = no
    vfs objects = catia fruit streams_xattr
    fruit:time machine = yes
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;This creates a new share called &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;backups&lt;/code&gt; which can only be accessed by using the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;mudge&lt;/code&gt; username and password.&lt;/p&gt;

&lt;p&gt;The key configuration for Time Machine are the last two lines which enable &lt;a href=&quot;https://www.samba.org/samba/docs/current/man-html/vfs_fruit.8.html&quot;&gt;Samba’s enhanced macOS interoperability module&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;So that we can connect to the share as &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;mudge&lt;/code&gt;, we need to explicitly add that user to Samba’s password file and set a password with &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;smbpasswd&lt;/code&gt;:&lt;/p&gt;

&lt;div class=&quot;language-console highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;gp&quot;&gt;pi@raspberrypi:~ $&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;sudo &lt;/span&gt;smbpasswd &lt;span class=&quot;nt&quot;&gt;-a&lt;/span&gt; mudge
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;We can now use this username and password from macOS to connect to our &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;backups&lt;/code&gt; share.&lt;/p&gt;

&lt;p&gt;With these changes in place, we can now run &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;testparm&lt;/code&gt; to check our configuration is free of errors and reload our Samba configuration:&lt;/p&gt;

&lt;div class=&quot;language-console highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;gp&quot;&gt;pi@raspberrypi:~ $&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;sudo &lt;/span&gt;testparm &lt;span class=&quot;nt&quot;&gt;-s&lt;/span&gt;
&lt;span class=&quot;gp&quot;&gt;pi@raspberrypi:~ $&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;sudo &lt;/span&gt;service smbd reload
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;At this point, we could manually connect our Mac to the share by choosing &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Connect to Server...&lt;/code&gt; or pressing Command-K in the Finder, entering the IP address of our Raspberry Pi, e.g. &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;smb://192.168.1.104/backups&lt;/code&gt; and authenticating with our user credentials. This would then become available in the Time Machine Preference Pane as a backup destination when you choose “Select Disk…”.&lt;/p&gt;

&lt;p&gt;However, we can make this even easier by also setting up autodiscovery of our share with Avahi.&lt;/p&gt;

&lt;h2 id=&quot;configuring-avahi&quot;&gt;Configuring Avahi&lt;/h2&gt;

&lt;p&gt;&lt;img src=&quot;/i/sidebar.png&quot; class=&quot;pull-right&quot; width=&quot;375&quot; height=&quot;243&quot; alt=&quot;&quot; /&gt; Now that Samba is configured, let’s use Avahi to advertise it to Macs on our network. While Samba can do this itself if it is built with mDNS support, the version currently available for Raspbian does not have this functionality so we’ll have to do it ourselves based on the &lt;a href=&quot;https://github.com/samba-team/samba/blob/master/source3/smbd/avahi_register.c&quot;&gt;Samba source code&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;We’re going to have to add a new Avahi service to &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;/etc/avahi/services/samba.service&lt;/code&gt; which describes the two things &lt;a href=&quot;https://developer.apple.com/library/archive/releasenotes/NetworkingInternetWeb/Time_Machine_SMB_Spec/index.html#//apple_ref/doc/uid/TP40017496-CH1-SW1&quot;&gt;described in Apple’s documentation for advertising Time Machine availability through Bonjour&lt;/a&gt;:&lt;/p&gt;

&lt;ol&gt;
  &lt;li&gt;That we are running an SMB server on port 445;&lt;/li&gt;
  &lt;li&gt;That we have an SMB share point available for Time Machine.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;As a bonus, we’re also going to advertise our Raspberry Pi as a now-defunct &lt;a href=&quot;https://en.wikipedia.org/wiki/AirPort_Time_Capsule&quot;&gt;AirPort Time Capsule&lt;/a&gt; so that it appears as such in the Finder.&lt;/p&gt;

&lt;div class=&quot;language-xml highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;cp&quot;&gt;&amp;lt;?xml version=&quot;1.0&quot; standalone='no'?&amp;gt;&lt;/span&gt;&lt;span class=&quot;c&quot;&gt;&amp;lt;!--*-nxml-*--&amp;gt;&lt;/span&gt;
&lt;span class=&quot;cp&quot;&gt;&amp;lt;!DOCTYPE service-group SYSTEM &quot;avahi-service.dtd&quot;&amp;gt;&lt;/span&gt;
&lt;span class=&quot;nt&quot;&gt;&amp;lt;service-group&amp;gt;&lt;/span&gt;
  &lt;span class=&quot;nt&quot;&gt;&amp;lt;name&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;replace-wildcards=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;yes&quot;&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;&amp;gt;&lt;/span&gt;%h&lt;span class=&quot;nt&quot;&gt;&amp;lt;/name&amp;gt;&lt;/span&gt;
  &lt;span class=&quot;nt&quot;&gt;&amp;lt;service&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;nt&quot;&gt;&amp;lt;type&amp;gt;&lt;/span&gt;_smb._tcp&lt;span class=&quot;nt&quot;&gt;&amp;lt;/type&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;nt&quot;&gt;&amp;lt;port&amp;gt;&lt;/span&gt;445&lt;span class=&quot;nt&quot;&gt;&amp;lt;/port&amp;gt;&lt;/span&gt;
  &lt;span class=&quot;nt&quot;&gt;&amp;lt;/service&amp;gt;&lt;/span&gt;
  &lt;span class=&quot;nt&quot;&gt;&amp;lt;service&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;nt&quot;&gt;&amp;lt;type&amp;gt;&lt;/span&gt;_device-info._tcp&lt;span class=&quot;nt&quot;&gt;&amp;lt;/type&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;nt&quot;&gt;&amp;lt;port&amp;gt;&lt;/span&gt;9&lt;span class=&quot;nt&quot;&gt;&amp;lt;/port&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;nt&quot;&gt;&amp;lt;txt-record&amp;gt;&lt;/span&gt;model=TimeCapsule8,119&lt;span class=&quot;nt&quot;&gt;&amp;lt;/txt-record&amp;gt;&lt;/span&gt;
  &lt;span class=&quot;nt&quot;&gt;&amp;lt;/service&amp;gt;&lt;/span&gt;
  &lt;span class=&quot;nt&quot;&gt;&amp;lt;service&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;nt&quot;&gt;&amp;lt;type&amp;gt;&lt;/span&gt;_adisk._tcp&lt;span class=&quot;nt&quot;&gt;&amp;lt;/type&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;nt&quot;&gt;&amp;lt;port&amp;gt;&lt;/span&gt;9&lt;span class=&quot;nt&quot;&gt;&amp;lt;/port&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;nt&quot;&gt;&amp;lt;txt-record&amp;gt;&lt;/span&gt;dk0=adVN=backups,adVF=0x82&lt;span class=&quot;nt&quot;&gt;&amp;lt;/txt-record&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;nt&quot;&gt;&amp;lt;txt-record&amp;gt;&lt;/span&gt;sys=adVF=0x100&lt;span class=&quot;nt&quot;&gt;&amp;lt;/txt-record&amp;gt;&lt;/span&gt;
  &lt;span class=&quot;nt&quot;&gt;&amp;lt;/service&amp;gt;&lt;/span&gt;
&lt;span class=&quot;nt&quot;&gt;&amp;lt;/service-group&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Let’s take a look at each &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;service&lt;/code&gt; individually:&lt;/p&gt;

&lt;div class=&quot;language-xml highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;nt&quot;&gt;&amp;lt;service&amp;gt;&lt;/span&gt;
  &lt;span class=&quot;nt&quot;&gt;&amp;lt;type&amp;gt;&lt;/span&gt;_smb._tcp&lt;span class=&quot;nt&quot;&gt;&amp;lt;/type&amp;gt;&lt;/span&gt;
  &lt;span class=&quot;nt&quot;&gt;&amp;lt;port&amp;gt;&lt;/span&gt;445&lt;span class=&quot;nt&quot;&gt;&amp;lt;/port&amp;gt;&lt;/span&gt;
&lt;span class=&quot;nt&quot;&gt;&amp;lt;/service&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;This advertises our SMB server on port 445.&lt;/p&gt;

&lt;div class=&quot;language-xml highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;nt&quot;&gt;&amp;lt;service&amp;gt;&lt;/span&gt;
  &lt;span class=&quot;nt&quot;&gt;&amp;lt;type&amp;gt;&lt;/span&gt;_device-info._tcp&lt;span class=&quot;nt&quot;&gt;&amp;lt;/type&amp;gt;&lt;/span&gt;
  &lt;span class=&quot;nt&quot;&gt;&amp;lt;port&amp;gt;&lt;/span&gt;9&lt;span class=&quot;nt&quot;&gt;&amp;lt;/port&amp;gt;&lt;/span&gt;
  &lt;span class=&quot;nt&quot;&gt;&amp;lt;txt-record&amp;gt;&lt;/span&gt;model=TimeCapsule8,119&lt;span class=&quot;nt&quot;&gt;&amp;lt;/txt-record&amp;gt;&lt;/span&gt;
&lt;span class=&quot;nt&quot;&gt;&amp;lt;/service&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;This advertises our Raspberry Pi as an AirPort Time Capsule but you can use any device model name found in &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;/System/Library/CoreServices/CoreTypes.bundle/Contents/Info.plist&lt;/code&gt;, e.g. &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;MacBook&lt;/code&gt;, &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Xserve&lt;/code&gt;, &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Windows&lt;/code&gt;, etc.&lt;/p&gt;

&lt;p&gt;We use port 9 for both this and the next service as it is the port used by real Time Capsules and any data sent to it is &lt;a href=&quot;https://en.wikipedia.org/wiki/Discard_Protocol&quot;&gt;discarded by definition&lt;/a&gt;.&lt;/p&gt;

&lt;div class=&quot;language-xml highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;nt&quot;&gt;&amp;lt;service&amp;gt;&lt;/span&gt;
  &lt;span class=&quot;nt&quot;&gt;&amp;lt;type&amp;gt;&lt;/span&gt;_adisk._tcp&lt;span class=&quot;nt&quot;&gt;&amp;lt;/type&amp;gt;&lt;/span&gt;
  &lt;span class=&quot;nt&quot;&gt;&amp;lt;port&amp;gt;&lt;/span&gt;9&lt;span class=&quot;nt&quot;&gt;&amp;lt;/port&amp;gt;&lt;/span&gt;
  &lt;span class=&quot;nt&quot;&gt;&amp;lt;txt-record&amp;gt;&lt;/span&gt;dk0=adVN=backups,adVF=0x82&lt;span class=&quot;nt&quot;&gt;&amp;lt;/txt-record&amp;gt;&lt;/span&gt;
  &lt;span class=&quot;nt&quot;&gt;&amp;lt;txt-record&amp;gt;&lt;/span&gt;sys=adVF=0x100&lt;span class=&quot;nt&quot;&gt;&amp;lt;/txt-record&amp;gt;&lt;/span&gt;
&lt;span class=&quot;nt&quot;&gt;&amp;lt;/service&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;This is the crucial part that advertises our specific &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;backups&lt;/code&gt; share as a Time Machine-compatible “AirDisk” and it consists of two parts:&lt;/p&gt;

&lt;div class=&quot;language-xml highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;nt&quot;&gt;&amp;lt;txt-record&amp;gt;&lt;/span&gt;dk0=adVN=backups,adVF=0x82&lt;span class=&quot;nt&quot;&gt;&amp;lt;/txt-record&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Advertise a share named &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;backups&lt;/code&gt; (this &lt;em&gt;must&lt;/em&gt; match the name of your Samba share) which has the following two AirDisk volume flags set:&lt;/p&gt;

&lt;ol&gt;
  &lt;li&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;0x0002&lt;/code&gt;: SMB is supported on this volume;&lt;/li&gt;
  &lt;li&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;0x0080&lt;/code&gt;: Time Machine should allow this SMB volume as a backup destination.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Together, these make the single hex value string of &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;0x82&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;The second part sets system-wide &lt;a href=&quot;http://netatalk.sourceforge.net/wiki/index.php/Bonjour_record_adisk_adVF_values&quot;&gt;volume flags&lt;/a&gt; of &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;0x100&lt;/code&gt; so that connecting to our share prompts for a username and password:&lt;/p&gt;

&lt;div class=&quot;language-xml highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;nt&quot;&gt;&amp;lt;txt-record&amp;gt;&lt;/span&gt;sys=adVF=0x100&lt;span class=&quot;nt&quot;&gt;&amp;lt;/txt-record&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;With this service file in place, Avahi should automatically pick up the configuration change and your Raspberry Pi will now appear in the Finder and in the Time Machine Preference Pane when you choose “Select Disk…”.&lt;/p&gt;

&lt;p&gt;Ensure that you check “Encrypt backups” when selecting the disk and wait for your first backup to complete!&lt;/p&gt;

&lt;h2 id=&quot;accessing-your-backups-in-the-finder&quot;&gt;Accessing your backups in the Finder&lt;/h2&gt;

&lt;p&gt;&lt;img src=&quot;/i/finder-error.png&quot; class=&quot;pull-right&quot; width=&quot;365&quot; height=&quot;176&quot; alt=&quot;&quot; /&gt; Note that choosing your Raspberry Pi in the Finder will initially connect as a guest (as indicated by &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Connected as: Guest&lt;/code&gt; in the Finder window) and your &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;backups&lt;/code&gt; share will be visible there. However, attempting to open the share will fail with a puzzling error message stating that your share can’t be found. This is because you need to explicitly connect as the appropriate user (&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;mudge&lt;/code&gt;, in my case) by choosing &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Connect As...&lt;/code&gt; in the Finder.&lt;/p&gt;

&lt;p&gt;Once you do that, you should be able to see your backups stored as a sparse image with a &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;.backupbundle&lt;/code&gt; extension.&lt;/p&gt;

&lt;p&gt;If you have any trouble connecting (especially if you have changed your user credentials at all), it’s worth using the Keychain Access application in &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;/Applications/Utilities&lt;/code&gt; to check for any cached network passwords and delete old entries. You may also need to forcibly relaunch the Finder by holding down the Option key and right-clicking on its icon in the Dock and choosing the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Relaunch&lt;/code&gt; option at the bottom of the resulting menu.&lt;/p&gt;
</content>
</entry><entry>
  <title type="html">Weeknotes 2</title>
  <link href="https://mudge.name/2019/11/10/weeknotes-2/"/>
  <id>https://mudge.name/2019/11/10/weeknotes-2/</id>
  <published>2019-11-10T00:00:00+00:00</published>
  <updated>2019-11-10T00:00:00+00:00</updated>
  <summary type="html">Fixing dates on old videos in Photos, using a Raspberry Pi for Time Machine and the right tool for the job.</summary>
  <content type="html">&lt;p&gt;While I was conflicted about publishing &lt;a href=&quot;/2019/11/04/weeknotes-1.html&quot;&gt;my first weeknotes last week&lt;/a&gt; (were they too self-indulgent? are they interesting to anyone else?), I received some nice words of encouragement and so on with this week’s!&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;
    &lt;p&gt;A recurring lesson in the past few weeks has been the importance of the right tool for the job:&lt;/p&gt;

    &lt;ol&gt;
      &lt;li&gt;
        &lt;p&gt;Our kitchen tap began very occasionally dripping so I decided to replace its cartridges. However, despite my best efforts with a series of adjustable spanners and a hastily-purchased set of box spanners, all I managed to do was chew up the metal of the existing cartridge. Frustrated and close to admitting defeat, I remembered that I bought a set of socket and ratchet spanners to replace a car brakelight many years before and—lo and behold—that worked straight away and I was able to swap out the cartridges in mere minutes!&lt;/p&gt;
      &lt;/li&gt;
      &lt;li&gt;
        &lt;p&gt;While I was engaged in this exciting DIY adventure, E was having a customer service battle with our water company as we’d been sent a bill ten times our usual amount. Adamant we hadn’t suddenly taking to bathing around the clock, the company finally agreed to send out an engineer to investigate. Ever keen to see the workings of such things, I accompanied them to the street as they discovered they have been reading the wrong water meter for us for six years. In trying to identify our &lt;em&gt;real&lt;/em&gt; meter, they accidentally broke the stop cock on it, leaving us without any running water. A second engineer promptly came to fix it using a simple-looking tool known as a stop cock key and complained to me that if only they sent all engineers out with one, there would be no need for him to have visited. I nodded politely.&lt;/p&gt;
      &lt;/li&gt;
      &lt;li&gt;
        &lt;p&gt;As discussed &lt;a href=&quot;/2019/11/04/weeknotes-1.html&quot;&gt;last week&lt;/a&gt;, I’ve been baking some bread again and experimented with putting dough on baking parchment before lowering it into a Dutch oven. At least, I &lt;em&gt;thought&lt;/em&gt; I was using baking parchment but, thanks to swearing my head off while trying to pick apart an unholy fusion of paper and dough, I have since learnt that “greaseproof paper” and “greaseproof &lt;em&gt;and baking&lt;/em&gt; paper” are crucially different.&lt;/p&gt;
      &lt;/li&gt;
    &lt;/ol&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;Reading &lt;a href=&quot;https://vfoley.xyz/lesser-known-coding-fonts/&quot;&gt;Vincent Foley’s “Lesser Known Coding Fonts”&lt;/a&gt; sent me down a bit of a rabbithole trying to find the &lt;em&gt;perfect&lt;/em&gt; monospaced typeface, particularly after watching the recent episode of &lt;a href=&quot;https://www.netflix.com/gb/title/80057883&quot;&gt;“Abstract: The Art of Design”&lt;/a&gt; about &lt;a href=&quot;https://www.typography.com&quot;&gt;typeface design with Jonathan Hoefler&lt;/a&gt;. In the end, I abandoned this quest for what it was: an act of pure time-wasting.&lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;It has been five years since &lt;a href=&quot;https://www.youtube.com/watch?v=QrGrOK8oZG8&quot;&gt;Casper Kelly’s “Too Many Cooks”&lt;/a&gt;. I continue to pay tribute to it in my own small way by having &lt;a href=&quot;http://rushcoil.bandcamp.com/track/too-many-cooks-rush-coil-nes-cover&quot;&gt;Rush Coil’s NES cover of it&lt;/a&gt; as my ringtone.&lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;C having his 8-week &lt;a href=&quot;https://www.nhs.uk/conditions/vaccinations/&quot;&gt;vaccinations&lt;/a&gt; and talk of preventing fevers led me to &lt;a href=&quot;https://www.theguardian.com/lifeandstyle/2019/jun/04/why-parents-are-addicted-to-calpol&quot;&gt;Jenny Kleeman’s “Why parents are addicted to Calpol”&lt;/a&gt; so it was with an unearned sense of pride I marched into our local pharmacy and asked for whatever generic, liquid baby paracetamol suspension they had.&lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;It has been quite the week of computer noodling for me:&lt;/p&gt;

    &lt;ul&gt;
      &lt;li&gt;
        &lt;p&gt;As I manage my family’s email accounts, I decided to spend some time implementing &lt;a href=&quot;https://support.google.com/a/answer/2466580?hl=en&quot;&gt;Domain-based Message Authentication, Reporting &amp;amp; Conformance (DMARC)&lt;/a&gt; for my domains. As a side-effect, I now get a daily report of the times people are sending emails pretending to be from &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;mudge.name&lt;/code&gt;. These attempts &lt;em&gt;should&lt;/em&gt; now go straight to spam but it’s still a little disconcerting.&lt;/p&gt;
      &lt;/li&gt;
      &lt;li&gt;
        &lt;p&gt;Following &lt;a href=&quot;/2019/11/04/weeknotes-1.html&quot;&gt;last week&lt;/a&gt;’s adventures with backups, I decided to set up &lt;a href=&quot;https://support.apple.com/en-us/HT202784&quot;&gt;a network-based Time Machine backup&lt;/a&gt;.&lt;/p&gt;

        &lt;p&gt;The first step was to find a hard drive big enough to store backups and, in my search, I uncovered old videos of myself dating back to 1998. This led me to compress all these 21-year old videos using &lt;a href=&quot;https://support.apple.com/en-gb/guide/automator/welcome/mac&quot;&gt;Automator&lt;/a&gt; and then import them into &lt;a href=&quot;https://www.apple.com/uk/macos/photos/&quot;&gt;Photos&lt;/a&gt;. However, none of the videos had the correct date on them.&lt;/p&gt;

        &lt;p&gt;Thankfully, I noticed the videos &lt;em&gt;did&lt;/em&gt; contain their real date and time in their titles, e.g. &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;CLIP-2003-08-02 21;40;28.MOV&lt;/code&gt;. As there were over 700 of these videos, updating them manually didn’t seem a smart option so I decided to look into scripting Photos via the &lt;a href=&quot;https://developer.apple.com/library/archive/documentation/LanguagesUtilities/Conceptual/MacAutomationScriptingGuide/index.html#//apple_ref/doc/uid/TP40016239&quot;&gt;Mac Automation Scripting Guide&lt;/a&gt;. While it seemed like I might have to learn a whole new programming language, I vaguely recalled that you can now script macOS applications with JavaScript.&lt;/p&gt;

        &lt;p&gt;I wondered if I could use any modern JavaScript features and &lt;a href=&quot;http://umm.io&quot;&gt;Matt MacLeod&lt;/a&gt; pointed out &lt;a href=&quot;https://github.com/JXA-Cookbook/JXA-Cookbook/wiki/ES6-Features-in-JXA&quot;&gt;“ES6 Features in JXA”&lt;/a&gt; which explains that you can use any JavaScript features supported by the version of Safari bundled with your OS.&lt;/p&gt;

        &lt;p&gt;I’ve since written up &lt;a href=&quot;/2019/11/13/scripting-photos-for-macos-with-javascript.html&quot;&gt;how I scripted Photos with JavaScript to update all the videos in one go&lt;/a&gt;.&lt;/p&gt;
      &lt;/li&gt;
      &lt;li&gt;
        &lt;p&gt;Videos of a much softer-faced and squeakier-voiced me safely stored in iCloud, it was time to get that Time Machine set up on my &lt;a href=&quot;https://www.raspberrypi.org&quot;&gt;Raspberry Pi&lt;/a&gt;. This resulted in &lt;a href=&quot;/2019/11/12/using-a-raspberry-pi-for-time-machine.html&quot;&gt;a new post about how to use a Raspberry Pi for Time Machine&lt;/a&gt;.&lt;/p&gt;
      &lt;/li&gt;
    &lt;/ul&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;C is now ten weeks old and I’ve been thinking about something from &lt;a href=&quot;https://vimeo.com/41756240&quot;&gt;Michael Please‘s BAFTA award-winning short film, “The Eagleman Stag”&lt;/a&gt;: yesterday was less than a 12,500th of my life but, for C, it was over 177 times more.&lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;&lt;a href=&quot;https://codon.com&quot;&gt;Tom Stuart&lt;/a&gt; said I might regret the semicolons in last week’s notes so I hope you enjoy this week’s luxurious full stops instead.&lt;/p&gt;
  &lt;/li&gt;
&lt;/ul&gt;
</content>
</entry><entry>
  <title type="html">Weeknotes 1</title>
  <link href="https://mudge.name/2019/11/04/weeknotes-1/"/>
  <id>https://mudge.name/2019/11/04/weeknotes-1/</id>
  <published>2019-11-04T00:00:00+00:00</published>
  <updated>2019-11-04T00:00:00+00:00</updated>
  <summary type="html">Dadding, baking, TV and being a paranormal insurance investigator.</summary>
  <content type="html">&lt;p&gt;Following a trail already blazed by &lt;a href=&quot;https://natbuckley.co.uk/2019/01/20/weeknotes-1-tv-ads-and-business-models/&quot;&gt;Nat Buckley&lt;/a&gt;, &lt;a href=&quot;https://alicebartlett.co.uk/blog/weaknotes&quot;&gt;Alice Bartlett&lt;/a&gt; and &lt;a href=&quot;https://chriszetter.com/blog/2019/10/26/a-visit-to-kew-gardens-and-a-baking-failure/&quot;&gt;Chris Zetter&lt;/a&gt; and at the very real risk of over-sharing, here are my first weeknotes:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;
    &lt;p&gt;I thoroughly enjoyed playing &lt;a href=&quot;https://obradinn.com&quot;&gt;Lucas Pope’s multi-award-winning “Return of the Obra Dinn”&lt;/a&gt; which is now &lt;a href=&quot;https://www.nintendo.co.uk/Games/Nintendo-Switch-download-software/Return-of-the-Obra-Dinn-1633060.html&quot;&gt;available for the Nintendo Switch&lt;/a&gt;, a game I finished while walking C back to sleep at 6 am. From the use of music and text alone, you can tell this is from the creator of &lt;a href=&quot;https://papersplea.se&quot;&gt;Papers, Please&lt;/a&gt;;&lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;I keep wanting to talk to people about how fantastic “&lt;a href=&quot;https://www.hbo.com/succession&quot;&gt;Succession&lt;/a&gt;” is but so few people have heard of it, let alone seen it. I assume this is because it’s not on Netflix, Prime Video or the regular telly in the UK but I hope it gets the audience it deserves;&lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;On that note, I was simultaneously amused and dismayed to read that &lt;a href=&quot;https://www.vulture.com/2019/08/succession-connor-roy-hyperdecanting-wine.html&quot;&gt;Connor Roy’s “hyperdecanting” is a real thing coined by Nathan Myhrvold&lt;/a&gt;;&lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;While I can’t really make time for &lt;a href=&quot;https://london.computation.club&quot;&gt;London Computation Club&lt;/a&gt; any more, I have been thoroughly enjoying the &lt;a href=&quot;https://computationclub-slack.herokuapp.com/&quot;&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;#bread&lt;/code&gt; Slack channel&lt;/a&gt;, learning how to &lt;a href=&quot;https://www.seriouseats.com/2014/11/troubleshoot-bad-bread-messed-up-loaf.html&quot;&gt;troubleshoot bad bakes&lt;/a&gt; and experiment with &lt;a href=&quot;http://tartine-bread.blogspot.com/2014/01/guest-baker-chad-robertson.html&quot;&gt;long autolyse times, baking from cold and scoring with scissors&lt;/a&gt; (in fact, as I write this, I have some dough in bulk fermentation);&lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;Having C in my life has predictably altered my perspective so it’s been with some trepidation that I’ve been listening to &lt;a href=&quot;https://www.nickcave.com/ghosteen/&quot;&gt;Nick Cave and the Bad Seeds’ “Ghosteen”&lt;/a&gt; given &lt;a href=&quot;https://en.wikipedia.org/wiki/Ghosteen#Background&quot;&gt;its subject matter&lt;/a&gt; but it is rather wonderful;&lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;On a similarly morbid note, I’m considering re-reading &lt;a href=&quot;https://en.wikipedia.org/wiki/Lincoln_in_the_Bardo&quot;&gt;George Saunders’ “Lincoln in the Bardo”&lt;/a&gt; but I am having too much fun learning about &lt;a href=&quot;https://en.wikipedia.org/wiki/Hyperion_(Simmons_novel)&quot;&gt;the Shrike&lt;/a&gt; at the moment;&lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;As a lot of care for C in the first few weeks involved staring directly at my phone, I went through a bit of a panic after reading &lt;a href=&quot;https://www.theguardian.com/technology/2019/oct/12/stop-email-ping-pong-nine-ways-to-avoid-digital-distraction-nir-eyal&quot;&gt;Nir Eyal’s guide to avoiding digital distraction&lt;/a&gt; and followed quite a bit of advice on &lt;a href=&quot;https://medium.com/better-humans/how-to-set-up-your-iphone-for-productivity-focus-and-your-own-longevity-bb27a68cc3d8&quot;&gt;how to configure your iPhone to work for you, not against you&lt;/a&gt; especially around restricting notifications. I’m not entirely convinced it has helped though as I now compulsively check applications that &lt;em&gt;may&lt;/em&gt; notify me and moving applications onto secondary screens and into folders hasn’t really increased friction significantly;&lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;While I have very rarely opened my laptop in the past two months, I couldn’t resist the urge to format it and do a clean install of &lt;a href=&quot;https://www.apple.com/uk/macos/catalina/&quot;&gt;macOS Catalina&lt;/a&gt; (despite the growing &lt;a href=&quot;https://tidbits.com/2019/10/21/six-reasons-why-ios-13-and-catalina-are-so-buggy/&quot;&gt;criticism of Apple’s recent software quality&lt;/a&gt;). I decided to use it as a bit of a Disaster Recovery test to see how easily I could recover my files if my hard drive was lost or failed.&lt;/p&gt;

    &lt;p&gt;The most straightforward recovery were my photos and files stored in &lt;a href=&quot;https://www.apple.com/uk/icloud/&quot;&gt;iCloud&lt;/a&gt; and passwords in &lt;a href=&quot;https://1password.com&quot;&gt;our 1Password family account&lt;/a&gt; (though downloading over 15 years of photos took quite some time). Less successful was the restoration of my music library and many software projects from a full &lt;a href=&quot;https://www.backblaze.com&quot;&gt;Backblaze&lt;/a&gt; backup as I hadn’t realised that Backblaze does not backup file permissions (including whether a file is executable or not). &lt;a href=&quot;https://mjtsai.com/blog/2014/05/22/what-backblaze-doesnt-back-up/&quot;&gt;Michael Tsai wrote about Backblaze’s shortcomings in this area over five years ago&lt;/a&gt; and I ended up restoring those files from a &lt;a href=&quot;https://support.apple.com/en-us/HT201250&quot;&gt;Time Machine backup&lt;/a&gt; I’d done as a failsafe instead (I would have used &lt;a href=&quot;https://www.shirt-pocket.com/SuperDuper/SuperDuperDescription.html&quot;&gt;David Nanian’s SuperDuper!&lt;/a&gt; but it doesn’t support Catalina yet).&lt;/p&gt;

    &lt;p&gt;Following advice from others, I’ll probably relegate Backblaze for total disasters and introduce a &lt;a href=&quot;https://support.apple.com/en-us/HT202784&quot;&gt;network-based Time Machine backup&lt;/a&gt;, most likely &lt;a href=&quot;https://github.com/mr-bt/raspberrypi-timemachine&quot;&gt;connected to my Raspberry Pi&lt;/a&gt;;&lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;Thanks to &lt;a href=&quot;https://natbuckley.co.uk/2019/08/26/weeknotes-32-the-one-about-baking/&quot;&gt;Nat Buckley mentioning Claire Saffitz in their weeknotes&lt;/a&gt;, I binged quite a few episodes of &lt;a href=&quot;https://video.bonappetit.com/series/gourmet-makes&quot;&gt;Bon Appétit’s “Gourmet Makes”&lt;/a&gt; and ended up baking &lt;a href=&quot;https://doriegreenspan.com/recipe/world-peace-cookies-the-newest-version-from-dories-cookies-sneak-peek/&quot;&gt;Dorie Greenspan’s “World Peace Cookies”&lt;/a&gt; mentioned in the &lt;a href=&quot;https://video.bonappetit.com/watch/pastry-chef-attempts-to-make-gourmet-oreos?c=series&quot;&gt;Oreo episode&lt;/a&gt; and they were the best home-made cookies I’d ever made. &lt;a href=&quot;https://chriszetter.com&quot;&gt;Chris Zetter&lt;/a&gt; recommended &lt;a href=&quot;https://www.bonappetit.com/recipe/bas-best-chocolate-chip-cookies&quot;&gt;Kate Davis’ “Brown Butter and Toffee Chocolate Chip Cookies” recipe&lt;/a&gt; so I might have to give that a go soon;&lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;I’ve spent an unhealthy amount of time reading and talking about &lt;a href=&quot;https://www.theguardian.com/tv-and-radio/2019/oct/30/great-british-bake-off-is-broken-heres-how-to-fix-it&quot;&gt;why the latest series of the Great British Bake Off went wrong&lt;/a&gt; but I was overjoyed to discover &lt;a href=&quot;https://www.earwolf.com/episode/how-did-you-win-great-british-bake-off-with-nancy-birtwhistle-winner-of-great-british-bake-off-series-5/&quot;&gt;a recent episode of “Getting Curious with Jonathan Van Ness” about series 5 winner, Nancy Birtwhistle&lt;/a&gt;;&lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;I have a very long, unfinished blog post about &lt;a href=&quot;https://reactjs.org/docs/hooks-intro.html&quot;&gt;React Hooks&lt;/a&gt; and &lt;a href=&quot;https://gist.github.com/mudge/eb9178a4b6d595ffde8f9cb31744afcf&quot;&gt;my attempts to implement debouncing with them&lt;/a&gt; that I really should finish and publish on &lt;a href=&quot;https://www.ghostcassette.com&quot;&gt;my consultancy website&lt;/a&gt; (especially since &lt;a href=&quot;https://tuzz.tech/blog/react-commentary-sidebar-2&quot;&gt;Chris Patuzzo mentioned it in his latest blog post&lt;/a&gt;) but it’ll have to wait for another day;&lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;Reader, you won’t believe me but &lt;a href=&quot;/2019/01/02/2018-yearnotes.html&quot;&gt;I ran&lt;/a&gt; for the first time since I got married and, what’s more, it was my first time around the park we’ve lived next to for over six years;&lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;I have now been a dad for nine weeks.&lt;/p&gt;
  &lt;/li&gt;
&lt;/ul&gt;
</content>
</entry><entry>
  <title type="html">2018 Yearnotes</title>
  <link href="https://mudge.name/2019/01/02/2018-yearnotes/"/>
  <id>https://mudge.name/2019/01/02/2018-yearnotes/</id>
  <published>2019-01-02T15:36:00+00:00</published>
  <updated>2019-01-02T15:36:00+00:00</updated>
  <summary type="html">Reflecting on the past year, both personally and professionally.</summary>
  <content type="html">&lt;p&gt;Last year, inspired by &lt;a href=&quot;http://ntlk.net/2017/12/29/2017-yearnotes/&quot;&gt;Nat Buckley’s “2017 Yearnotes”&lt;/a&gt; and &lt;a href=&quot;https://jvns.ca/blog/2017/12/31/2017--year-in-review/&quot;&gt;Julia Evans’ “2017: Year in review”&lt;/a&gt;, I wrote my own “2017 Yearnotes”. Despite the fact that year saw me getting &lt;a href=&quot;https://www.instagram.com/p/BRlk68ngORO/?taken-by=mudgemeister&quot;&gt;engaged at the top of Goat Fell&lt;/a&gt; on the &lt;a href=&quot;https://en.wikipedia.org/wiki/Isle_of_Arran&quot;&gt;Isle of Arran&lt;/a&gt; and later &lt;a href=&quot;https://twitter.com/mudge/status/914594665315356672&quot;&gt;married&lt;/a&gt; (not before completing the &lt;a href=&quot;https://en.wiktionary.org/wiki/yak_shaving&quot;&gt;frankly colossal act of yak shaving&lt;/a&gt; of &lt;a href=&quot;https://github.com/mudge/engine&quot;&gt;writing my own PHP web framework&lt;/a&gt;), I never published the piece. Reading it now, it’s somewhat strange to read my reflections on a lack of separation between my career and my identity and that my two hopes for 2018 were as follows:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;Figure out a healthier work-life balance;&lt;/li&gt;
  &lt;li&gt;Start running again.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;I flat-out failed at the latter and, as for the former, well, you could say I made some progress in that area but not in the way I could have predicted.&lt;/p&gt;

&lt;p&gt;And so, some reflections on 2018.&lt;/p&gt;

&lt;h2 id=&quot;travelling&quot;&gt;Travelling&lt;/h2&gt;

&lt;p&gt;&lt;img src=&quot;/i/tromso.jpg&quot; class=&quot;pull-right&quot; width=&quot;360&quot; height=&quot;270&quot; alt=&quot;&quot; /&gt; In February, I travelled to &lt;a href=&quot;https://en.wikipedia.org/wiki/Tromsø&quot;&gt;Tromsø, Norway&lt;/a&gt; for a week-long holiday exploring the breathtaking &lt;a href=&quot;https://en.wikipedia.org/wiki/Lofoten&quot;&gt;Lofoten islands&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;It was the first time I’ve ever been within the &lt;a href=&quot;https://en.wikipedia.org/wiki/Arctic_Circle&quot;&gt;Arctic Circle&lt;/a&gt; and the combination of natural beauty and outside temperatures of -13 °C made for a heady mix of awe and fearful respect. In retrospect, our desire to take it all in and reach &lt;a href=&quot;https://en.wikipedia.org/wiki/Å,_Moskenes&quot;&gt;Å&lt;/a&gt; within seven days meant we spent too much time on the road but I did get my first exposure to &lt;a href=&quot;https://en.wikipedia.org/wiki/Lane_departure_warning_system&quot;&gt;lane assist technology&lt;/a&gt; which was frankly terrifying on icy roads.&lt;/p&gt;

&lt;p&gt;One of the intentions of the trip was to break our daily habit of being glued to our phones and to break our dependence on constant stimulation from the internet. As with the rest of the year, I admit I wasn’t wholly successful in this endeavour though the place itself was wonderful.&lt;/p&gt;

&lt;p&gt;Not long after returning from Norway, we travelled to Cheshire for the first of several weddings this year (the rest would later take us to Liverpool, Shoreditch and Durham) which—in a change from last year—are all-the-more enjoyable when you’re not planning your own.&lt;/p&gt;

&lt;p&gt;2018 saw two weekend breaks: the first to sunny &lt;a href=&quot;https://en.wikipedia.org/wiki/Margate&quot;&gt;Margate&lt;/a&gt; and the second, my very first weekend break abroad when we went to &lt;a href=&quot;https://en.wikipedia.org/wiki/Porto&quot;&gt;Porto, Portugal&lt;/a&gt;. The latter gave us 48 hours to spelunk in port caves and seek out &lt;a href=&quot;https://en.wikipedia.org/wiki/Pastel_de_nata&quot;&gt;pastéis de nata&lt;/a&gt; in very good company.&lt;/p&gt;

&lt;p&gt;In further attempts to “get away from it all”, we journeyed up to Scotland several times, visiting family in Aberdeen and Glasgow and, in search of true respite, &lt;a href=&quot;https://www.instagram.com/p/BotuE8vAnrs/&quot;&gt;retreating to remote parts of Skye&lt;/a&gt; (where I ended up spending hours playing &lt;a href=&quot;http://hollowknight.com&quot;&gt;Hollow Knight&lt;/a&gt; on the Nintendo Switch).&lt;/p&gt;

&lt;p&gt;You may have noticed a recurring theme in the aforementioned journeys: attempts to dramatically switch context, to shake off daily routine and to find some peace outside of daily life in London.&lt;/p&gt;

&lt;h2 id=&quot;open-source&quot;&gt;Open source&lt;/h2&gt;

&lt;p&gt;I began the year by open sourcing the aforementioned PHP web framework I wrote &lt;a href=&quot;https://twitter.com/mudge/status/908383877927985152&quot;&gt;in order to get married&lt;/a&gt;: &lt;a href=&quot;https://github.com/mudge/engine&quot;&gt;Engine&lt;/a&gt;.&lt;/p&gt;

&lt;div class=&quot;language-console highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;gp&quot;&gt;$&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;composer create-project mudge/engine-skeleton:dev-master my-project
&lt;span class=&quot;gp&quot;&gt;$&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;cd &lt;/span&gt;my-project
&lt;span class=&quot;gp&quot;&gt;$&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;php &lt;span class=&quot;nt&quot;&gt;-S&lt;/span&gt; localhost:8080 &lt;span class=&quot;nt&quot;&gt;-t&lt;/span&gt; public
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;The core idea of Engine is that modelling a web application as a pure function that takes in a request and produces a response as an output (i.e. &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;f(request) = response&lt;/code&gt;) no longer feels accurate. Perhaps it is better to think of a web application as something that takes &lt;em&gt;both&lt;/em&gt; a request and a response as input and achieves its desired goal by using the response for side-effects (think of the response as a sort of open socket). This way, you can easily send your response in bits or stream responses indefinitely.&lt;/p&gt;

&lt;p&gt;It was also a great excuse to take advantage of &lt;a href=&quot;http://php.net/releases/7_1_0.php&quot;&gt;PHP 7.1’s type system&lt;/a&gt; and baffle people who haven’t seen any recent PHP. For example, here is some real source code from our wedding site:&lt;/p&gt;

&lt;div class=&quot;language-php highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;cp&quot;&gt;&amp;lt;?php&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;declare&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;strict_types&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;

&lt;span class=&quot;kn&quot;&gt;namespace&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;Wedding&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;

&lt;span class=&quot;k&quot;&gt;final&lt;/span&gt; &lt;span class=&quot;kd&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;TravelController&lt;/span&gt; &lt;span class=&quot;kd&quot;&gt;extends&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;PrivateController&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;function&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;index&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;():&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;void&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;nv&quot;&gt;$this&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;-&amp;gt;&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;requireUser&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;();&lt;/span&gt;

        &lt;span class=&quot;nv&quot;&gt;$this&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;-&amp;gt;&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;render&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;
            &lt;span class=&quot;s1&quot;&gt;'travel.html'&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
            &lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;'lodges'&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;$this&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;-&amp;gt;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;user&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;-&amp;gt;&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;isStayingInLodges&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()]&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;In February, I followed this up with a failed experiment to implement &lt;a href=&quot;https://github.com/mudge/readable&quot;&gt;a &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Readable&lt;/code&gt; module in Ruby&lt;/a&gt; that would allow users to create &lt;a href=&quot;http://ruby-doc.org/core/IO.html&quot;&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;IO&lt;/code&gt;&lt;/a&gt; objects from arbitrary sources much like &lt;a href=&quot;http://ruby-doc.org/core/Enumerator.html&quot;&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Enumerator&lt;/code&gt;&lt;/a&gt; allows users to create lazy &lt;a href=&quot;http://ruby-doc.org/core/Enumerable.html&quot;&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Enumerable&lt;/code&gt;&lt;/a&gt; objects:&lt;/p&gt;

&lt;div class=&quot;language-ruby highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;n&quot;&gt;readable&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;Readable&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;do&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;|&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;yielder&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;|&lt;/span&gt;
  &lt;span class=&quot;n&quot;&gt;yielder&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&amp;lt;&amp;lt;&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\u&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;001F&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\x8B\b\u&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;0000[&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\u&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;0017&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\x88&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;Z&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\u&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;0000&quot;&lt;/span&gt;
  &lt;span class=&quot;n&quot;&gt;yielder&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&amp;lt;&amp;lt;&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\u&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;0003&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\xF3&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;H&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\xCD\xC9\xC9\xD7&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\xCF&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;/&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\xCA&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;I&quot;&lt;/span&gt;
  &lt;span class=&quot;n&quot;&gt;yielder&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&amp;lt;&amp;lt;&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\u&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;0001&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\u&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;0000)^&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\u&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;0014&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\xFC\v\u&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;0000&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\u&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;0000&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\u&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;0000&quot;&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;

&lt;span class=&quot;n&quot;&gt;gz_reader&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;Zlib&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;no&quot;&gt;GzipReader&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;new&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;readable&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;no&quot;&gt;CSV&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;gz_reader&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;).&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;gets&lt;/span&gt;
&lt;span class=&quot;c1&quot;&gt;#=&amp;gt; ['Hello', 'world']&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Sadly, this &lt;a href=&quot;https://github.com/mudge/readable#why&quot;&gt;didn’t fully work due to the sprawling surface area of the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;IO&lt;/code&gt; module&lt;/a&gt; (though, later in the year, &lt;a href=&quot;https://github.com/dkandalov/coroutines-explained&quot;&gt;Dmitry Kandalov’s “Coroutines explained”&lt;/a&gt; made me want to try this again with &lt;a href=&quot;https://ruby-doc.org/core/Fiber.html&quot;&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Fiber&lt;/code&gt;&lt;/a&gt;s).&lt;/p&gt;

&lt;p&gt;In July, thanks to the help of &lt;a href=&quot;http://london.computation.club&quot;&gt;London Computation Club&lt;/a&gt;, a particularly thorny &lt;a href=&quot;https://en.wikipedia.org/wiki/Graph_theory&quot;&gt;Graph theory&lt;/a&gt; problem at work led me to implement &lt;a href=&quot;https://en.wikipedia.org/wiki/Dijkstra%27s_algorithm&quot;&gt;Dijkstra’s algorithm&lt;/a&gt; in Ruby.&lt;/p&gt;

&lt;p&gt;While the initial implementation using arrays worked, it could take an hour to run and so I looked into implementing a &lt;a href=&quot;https://en.wikipedia.org/wiki/Priority_queue&quot;&gt;priority queue data structure&lt;/a&gt; to speed things up. In a surprising twist, I discovered an ideal algorithm not online but in a printed textbook gathering dust on my shelf: &lt;a href=&quot;https://mitpress.mit.edu/books/introduction-algorithms-third-edition&quot;&gt;Cormen, Leiserson, Rivest and Stein’s “Introduction to Algorithms, Third Edition”&lt;/a&gt;. Implementing their description of Fibonacci Heaps was difficult but ultimately rewarding when I published a new RubyGem imaginatively titled &lt;a href=&quot;https://github.com/mudge/fibonacci_heap&quot;&gt;Fibonacci Heap&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;The data structure allows you to insert nodes with a given priority and efficiently &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;pop&lt;/code&gt; the highest priority node from the heap even if that priority has been modified after insertion:&lt;/p&gt;

&lt;div class=&quot;language-ruby highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;n&quot;&gt;heap&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;FibonacciHeap&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;no&quot;&gt;Heap&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;new&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;foo&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;FibonacciHeap&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;no&quot;&gt;Node&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;new&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;'foo'&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;bar&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;FibonacciHeap&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;no&quot;&gt;Node&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;new&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;'bar'&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;baz&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;FibonacciHeap&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;no&quot;&gt;Node&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;new&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;2&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;'baz'&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;heap&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;insert&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;foo&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;heap&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;insert&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;bar&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;heap&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;insert&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;baz&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;heap&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;pop&lt;/span&gt;
&lt;span class=&quot;c1&quot;&gt;#=&amp;gt; #&amp;lt;FibonacciHeap::Node key=0 value=&quot;bar&quot;&amp;gt;&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;heap&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;decrease_key&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;baz&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;heap&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;pop&lt;/span&gt;
&lt;span class=&quot;c1&quot;&gt;#=&amp;gt; #&amp;lt;FibonacciHeap::Node key=0 value=&quot;baz&quot;&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Replacing my array-based implementation of Dijkstra’s algorithm with a Fibonacci Heap-based one sped up our build times to mere &lt;em&gt;seconds&lt;/em&gt; and was well worth it.&lt;/p&gt;

&lt;p&gt;That &lt;em&gt;would&lt;/em&gt; have been my open source highlight of the year until something else happened on Christmas Day: &lt;a href=&quot;https://www.ruby-lang.org/en/news/2018/12/25/ruby-2-6-0-released/&quot;&gt;Ruby 2.6 was released&lt;/a&gt; containing &lt;a href=&quot;https://bugs.ruby-lang.org/issues/6284&quot;&gt;a certain new feature&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;That feature was the ability to compose &lt;a href=&quot;https://ruby-doc.org/core/Proc.html&quot;&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Proc&lt;/code&gt;&lt;/a&gt; objects using &lt;a href=&quot;https://ruby-doc.org/core-2.6/Proc.html#method-i-3C-3C&quot;&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;&amp;lt;&amp;lt;&lt;/code&gt;&lt;/a&gt; and &lt;a href=&quot;https://ruby-doc.org/core-2.6/Proc.html#method-i-3E-3E&quot;&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;&amp;gt;&amp;gt;&lt;/code&gt;&lt;/a&gt;. While I have dabbled in functional programming before, this feature is of particular interest to me as it is based on &lt;a href=&quot;https://bugs.ruby-lang.org/issues/6284#note-26&quot;&gt;a series of patches I submitted in 2015&lt;/a&gt;. This is my first code contribution to Ruby to be accepted and, as such, has to be my open source highlight of the year.&lt;/p&gt;

&lt;h2 id=&quot;public-speaking&quot;&gt;Public speaking&lt;/h2&gt;

&lt;p&gt;I only did one talk this year: &lt;a href=&quot;https://speakerdeck.com/mudge/hyperloglog-in-15-minutes&quot;&gt;“HyperLogLog in 15 minutes”&lt;/a&gt; at the &lt;a href=&quot;https://www.meetup.com/London-Ruby-Meetup/&quot;&gt;Drover Ruby Meetup in November&lt;/a&gt;. Huge thanks to those who listened to me &lt;a href=&quot;https://github.com/computationclub/computationclub.github.io/wiki/HyperLogLog-in-15-minutes&quot;&gt;rehearse this presentation&lt;/a&gt; (some people more than once), the feedback I received massively improved everything from the slides to my delivery.&lt;/p&gt;

&lt;p&gt;I’m considering expanding this into a longer talk for the new year so that I can reduce the amount of hand-waving and dig into the mathematics a bit more but I’m still undecided.&lt;/p&gt;

&lt;h2 id=&quot;work&quot;&gt;Work&lt;/h2&gt;

&lt;p&gt;&lt;a href=&quot;https://www.instagram.com/p/Bmd7oNYAOVy/&quot;&gt;&lt;img src=&quot;/i/altmetric.jpg&quot; class=&quot;pull-right&quot; width=&quot;360&quot; height=&quot;360&quot; alt=&quot;&quot; /&gt;&lt;/a&gt; The elephant in this post is that in August of this year, after 5 years, I gave notice of my resignation as CTO of &lt;a href=&quot;https://www.altmetric.com&quot;&gt;Altmetric&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;The year leading up to that point had seen some great successes from the team: the rewrite of the company’s oldest data source, the &lt;a href=&quot;https://www.altmetric.com/press/press-releases/patent-data-in-altmetric-highlights-the-commercialization-of-research/&quot;&gt;addition of over 8 million patents&lt;/a&gt;, a &lt;a href=&quot;https://www.altmetric.com/press/press-releases/altmetric-releases-explorer-highlights-and-enhanced-user-experience/&quot;&gt;huge user interface and technology overhaul to the flagship product&lt;/a&gt;, the &lt;a href=&quot;https://www.altmetric.com/press/press-releases/altmetric-adds-dimensions-citation-data-to-highlight-academic-impact-of-scholarly-work/&quot;&gt;integration of scholarly citations for the full database&lt;/a&gt; and some especially tricky performance work on the company’s &lt;a href=&quot;https://en.wikipedia.org/wiki/Extract,_transform,_load&quot;&gt;Extract, transform, load processes&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Ultimately though, my struggle to find a sustainable place for myself within the company (as evidenced by my feelings in the previous year and by my attempts to take a break and even start &lt;a href=&quot;https://www.headspace.com&quot;&gt;meditating&lt;/a&gt;) was in vain. Considering how blissful I felt during my honeymoon at the end of 2017, it was shocking to me how much my work permeated my every waking thought and how quickly I fell into the same trap of my own doing.&lt;/p&gt;

&lt;p&gt;At my lowest ebb, I booked a long Bank Holiday weekend off at the last minute and asked many people in my field for advice, many of them from &lt;a href=&quot;http://london.computation.club&quot;&gt;London Computation Club&lt;/a&gt;. I am hugely thankful to all those who kindly took the time to speak with me and help me navigate a very difficult decision.&lt;/p&gt;

&lt;p&gt;I worked the remaining four months of the year, handing over to &lt;a href=&quot;https://www.altmetric.com/about-us/people/&quot;&gt;my amazing colleagues&lt;/a&gt;, confident in their ability to learn from my mistakes and lead the team to even greater success.&lt;/p&gt;

&lt;p&gt;I hope to write more about my time at Altmetric in the future as I learnt an incredible amount trying to build a software development team there over five and a half years and I would do it all over again in a heartbeat.&lt;/p&gt;

&lt;h2 id=&quot;2019&quot;&gt;2019&lt;/h2&gt;

&lt;p&gt;I’m honestly not sure what is next. After working continuously for a decade in London, I plan to take a break from keyboards and screens for a short while before seeing how I can best be of help to others, be that as a contractor or as a full-time employee.&lt;/p&gt;

&lt;p&gt;Despite having already published &lt;a href=&quot;https://github.com/mudge/homer&quot;&gt;a new open source project this year&lt;/a&gt;, I hope to continue seeking a better balance between work and life and, as with last year, get back into running.&lt;/p&gt;
</content>
</entry><entry>
  <title type="html">Cross-Compiling Rust for a Raspberry Pi on Travis CI</title>
  <link href="https://mudge.name/2019/01/02/cross-compiling-rust-for-a-raspberry-pi-on-travis-ci/"/>
  <id>https://mudge.name/2019/01/02/cross-compiling-rust-for-a-raspberry-pi-on-travis-ci/</id>
  <published>2019-01-02T00:00:00+00:00</published>
  <updated>2019-01-02T00:00:00+00:00</updated>
  <summary type="html">How to compile binaries from a Rust project that will run on a Raspberry Pi using Travis CI.</summary>
  <content type="html">&lt;p&gt;I recently wrote a &lt;a href=&quot;https://github.com/mudge/homer&quot;&gt;small program in Rust&lt;/a&gt; that I wanted to compile for an old &lt;a href=&quot;https://www.raspberrypi.org&quot;&gt;Raspberry Pi&lt;/a&gt; Model B but running &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;cargo build --release&lt;/code&gt; on the Pi itself took several hours to complete. Researching online, I found &lt;a href=&quot;https://github.com/japaric/rust-cross&quot;&gt;Jorge Aparicio’s guide to Rust cross compilation&lt;/a&gt; and their &lt;a href=&quot;https://github.com/japaric/trust&quot;&gt;Travis CI and AppVeyor template&lt;/a&gt; extremely useful but wasn’t entirely successful in compiling a binary I could use directly on my device. After many experiments and many failed builds, I finally have a build pipeline that will compile a Rust binary for the Raspberry Pi, Linux and macOS in minutes using &lt;a href=&quot;https://travis-ci.org&quot;&gt;Travis CI&lt;/a&gt;.&lt;/p&gt;

&lt;h2 id=&quot;cross-compiling-manually&quot;&gt;Cross-compiling manually&lt;/h2&gt;

&lt;p&gt;The first thing we need to understand is how we can compile a Rust program on one architecture but target another and, most importantly, understand the current limitations of cross-compilation with Rust.&lt;/p&gt;

&lt;p&gt;Unlike &lt;a href=&quot;https://golangcookbook.com/chapters/running/cross-compiling/&quot;&gt;Go which only requires setting two environment variables to cross-compile to &lt;em&gt;any&lt;/em&gt; architecture&lt;/a&gt;, cross-compiling Rust requires us to have a copy of the standard library for the target platform (e.g. in the case of the Raspberry Pi, the standard library needs to be compiled for &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;arm-unknown-linux-gnueabihf&lt;/code&gt;) and for us to have an appropriate platform-specific linker for compilation. Thankfully, &lt;a href=&quot;https://github.com/rust-lang/rustup.rs#cross-compilation&quot;&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;rustup&lt;/code&gt; allows us to easily add standard libraries for other platforms&lt;/a&gt; but we’ll need to source the appropriate linker ourselves.&lt;/p&gt;

&lt;p&gt;Finding a platform-specific linker is what restricts us: if there is no toolchain for the target platform available on your build platform then we can’t cross-compile. For example, we can’t easily compile a binary for the Raspberry Pi from macOS because the &lt;a href=&quot;https://github.com/raspberrypi/tools&quot;&gt;Raspberry Pi toolchain&lt;/a&gt; doesn’t run on macOS.&lt;/p&gt;

&lt;p&gt;Luckily for us, it &lt;em&gt;does&lt;/em&gt; run on 64-bit Linux so we can use Travis CI to cross-compile as long as we download the Raspberry Pi toolchain and instruct Rust to use its compiler to do the linking. Let’s see how this would work if we ran it ourselves on the command-line.&lt;/p&gt;

&lt;p&gt;Let’s assume we’re running this inside our Rust project on a 64-bit Linux machine and we have a recent &lt;a href=&quot;https://rustup.rs&quot;&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;rustup&lt;/code&gt;&lt;/a&gt; installed. We’ll start by first downloading the standard library for our target platform: Linux running on an ARMv6 CPU with hardware floating-point support.&lt;/p&gt;

&lt;div class=&quot;language-console highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;gp&quot;&gt;$&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;rustup target add arm-unknown-linux-gnueabihf
&lt;span class=&quot;go&quot;&gt;info: downloading component 'rust-std' for 'arm-unknown-linux-gnueabihf'
 52.5 MiB /  52.5 MiB (100 %)  26.5 MiB/s ETA:   0 s
info: installing component 'rust-std' for 'arm-unknown-linux-gnueabihf'
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Then let’s download the Raspberry Pi toolchain from GitHub (only fetching the latest commit) into &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;/tmp&lt;/code&gt; and add the necessary binaries to our path:&lt;/p&gt;

&lt;div class=&quot;language-console highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;gp&quot;&gt;$&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;git clone &lt;span class=&quot;nt&quot;&gt;--depth&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;1 https://github.com/raspberrypi/tools.git /tmp/tools
&lt;span class=&quot;gp&quot;&gt;$&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;export &lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;PATH&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;/tmp/tools/arm-bcm2708/arm-linux-gnueabihf/bin:&lt;span class=&quot;nv&quot;&gt;$PATH&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;We now need to tell Rust where to find the linker for the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;arm-unknown-linux-gnueabihf&lt;/code&gt; target and we can do this in one of two ways:&lt;/p&gt;

&lt;ol&gt;
  &lt;li&gt;Add configuration to &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;~/.cargo/config&lt;/code&gt;&lt;/li&gt;
  &lt;li&gt;Set an environment variable&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;For the first approach, we would add the following to &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;~/.cargo/config&lt;/code&gt; (creating it if it does not already exist):&lt;/p&gt;

&lt;div class=&quot;language-toml highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;nn&quot;&gt;[target.arm-unknown-linux-gnueabihf]&lt;/span&gt;
&lt;span class=&quot;py&quot;&gt;linker&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;arm-linux-gnueabihf-gcc&quot;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;For the second, we need to export an environment variable for the specific target:&lt;/p&gt;

&lt;div class=&quot;language-console highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;gp&quot;&gt;$&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;export &lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;CARGO_TARGET_ARM_UNKNOWN_LINUX_GNUEABIHF_LINKER&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;arm-linux-gnueabihf-gcc
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;With either of those in place, we can now attempt to compile our project for the target platform with &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;cargo&lt;/code&gt;:&lt;/p&gt;

&lt;div class=&quot;language-console highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;gp&quot;&gt;$&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;cargo build &lt;span class=&quot;nt&quot;&gt;--target&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;arm-unknown-linux-gnueabihf &lt;span class=&quot;nt&quot;&gt;--release&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;If you’re lucky, this will work and produce a binary in &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;target/arm-unknown-linux-gnueabihf/release/&lt;/code&gt; that you can run on your target platform. However, I ran into issues trying to compile parts of the &lt;a href=&quot;https://github.com/sfackler/rust-openssl&quot;&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;openssl&lt;/code&gt; crate&lt;/a&gt; as it was attempting to find OpenSSL headers for the target platform on my system but they aren’t binary compatible.&lt;/p&gt;

&lt;p&gt;Thankfully, we can solve this by taking advantage of the &lt;a href=&quot;https://docs.rs/openssl/0.10.16/openssl/#vendored&quot;&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;openssl&lt;/code&gt; crate’s &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;vendored&lt;/code&gt; Cargo feature&lt;/a&gt; which will compile and statically link its &lt;em&gt;own&lt;/em&gt; copy of OpenSSL meaning that it will be compiled for the appropriate platform. To do this, we need to add the following dependency to our &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Cargo.toml&lt;/code&gt;:&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;openssl = { version = &quot;0.10&quot;, features = [&quot;vendored&quot;] }
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Hopefully you will now have a cross-compiled binary you can run to your heart’s content.&lt;/p&gt;

&lt;h2 id=&quot;running-on-travis-ci&quot;&gt;Running on Travis CI&lt;/h2&gt;

&lt;p&gt;So how can we automate this to run on Travis CI? We can make use of &lt;a href=&quot;https://docs.travis-ci.com/user/build-matrix/&quot;&gt;Travis CI’s Build Matrix&lt;/a&gt; functionality to specify multiple target platforms and run a Bash script before deployment to prepare assets for &lt;a href=&quot;https://docs.travis-ci.com/user/deployment/releases/&quot;&gt;deployment to GitHub Releases&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Let’s start with only one target platform to keep things simple.&lt;/p&gt;

&lt;p&gt;First, you’ll need to &lt;a href=&quot;https://docs.travis-ci.com/user/deployment/releases/#authenticating-with-an-oauth-token&quot;&gt;generate an encrypted GitHub OAuth token to use for the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;deploy&lt;/code&gt; configuration&lt;/a&gt;. The easiest way to do this is to use the &lt;a href=&quot;https://github.com/travis-ci/travis.rb#installation&quot;&gt;Travis CLI&lt;/a&gt;:&lt;/p&gt;

&lt;div class=&quot;language-console highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;gp&quot;&gt;$&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;gem &lt;span class=&quot;nb&quot;&gt;install &lt;/span&gt;travis
&lt;span class=&quot;gp&quot;&gt;$&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;travis init
&lt;span class=&quot;gp&quot;&gt;$&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;travis setup releases
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;With that set up, add the following to your &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;.travis.yml&lt;/code&gt; (leaving your generated &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;deploy&lt;/code&gt; API key and replacing &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;mybinary&lt;/code&gt; with your desired name):&lt;/p&gt;

&lt;div class=&quot;language-yaml highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;na&quot;&gt;dist&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;trusty&lt;/span&gt;
&lt;span class=&quot;na&quot;&gt;language&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;rust&lt;/span&gt;
&lt;span class=&quot;na&quot;&gt;cache&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;cargo&lt;/span&gt;
&lt;span class=&quot;na&quot;&gt;env&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;TARGET=arm-unknown-linux-gnueabihf CARGO_TARGET_ARM_UNKNOWN_LINUX_GNUEABIHF_LINKER=arm-linux-gnueabihf-gcc&lt;/span&gt;
&lt;span class=&quot;na&quot;&gt;before_deploy&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;ci/before_deploy.sh&lt;/span&gt;
&lt;span class=&quot;na&quot;&gt;deploy&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;
  &lt;span class=&quot;na&quot;&gt;file&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;mybinary-$TRAVIS_TAG-$TARGET.tar.gz&lt;/span&gt;
  &lt;span class=&quot;na&quot;&gt;on&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;
    &lt;span class=&quot;na&quot;&gt;tags&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;true&lt;/span&gt;
  &lt;span class=&quot;na&quot;&gt;provider&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;releases&lt;/span&gt;
  &lt;span class=&quot;na&quot;&gt;skip_cleanup&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;true&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;We can now write our &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;ci/before_deploy.sh&lt;/code&gt; script to perform the steps we did earlier (you’ll need to alter the last line to compress the appropriate binary with your desired name as specified in your &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;.travis.yml&lt;/code&gt; above):&lt;/p&gt;

&lt;div class=&quot;language-bash highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;c&quot;&gt;#!/bin/bash&lt;/span&gt;
&lt;span class=&quot;nb&quot;&gt;set&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;-euo&lt;/span&gt; pipefail

&lt;span class=&quot;c&quot;&gt;# Install the Rust stdlib for the current target&lt;/span&gt;
rustup target add &lt;span class=&quot;nv&quot;&gt;$TARGET&lt;/span&gt;

&lt;span class=&quot;c&quot;&gt;# Download the Raspberry Pi cross-compilation toolchain if needed&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;[&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;$TARGET&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;arm-unknown-linux-gnueabihf&quot;&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;]&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;then
  &lt;/span&gt;git clone &lt;span class=&quot;nt&quot;&gt;--depth&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;1 https://github.com/raspberrypi/tools.git /tmp/tools
  &lt;span class=&quot;nb&quot;&gt;export &lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;PATH&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;/tmp/tools/arm-bcm2708/arm-linux-gnueabihf/bin:&lt;span class=&quot;nv&quot;&gt;$PATH&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;fi&lt;/span&gt;

&lt;span class=&quot;c&quot;&gt;# Compile the binary for the current target&lt;/span&gt;
cargo build &lt;span class=&quot;nt&quot;&gt;--target&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;$TARGET&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;--release&lt;/span&gt;

&lt;span class=&quot;c&quot;&gt;# Package up the release binary&lt;/span&gt;
&lt;span class=&quot;nb&quot;&gt;tar&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;-C&lt;/span&gt; target/&lt;span class=&quot;nv&quot;&gt;$TARGET&lt;/span&gt;/release &lt;span class=&quot;nt&quot;&gt;-czf&lt;/span&gt; mybinary-&lt;span class=&quot;nv&quot;&gt;$TRAVIS_TAG&lt;/span&gt;-&lt;span class=&quot;nv&quot;&gt;$TARGET&lt;/span&gt;.tar.gz mybinary
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Make this file executable:&lt;/p&gt;

&lt;div class=&quot;language-console highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;gp&quot;&gt;$&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;chmod &lt;/span&gt;a+x ci/before_deploy.sh
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;You should now be able to build this on Travis CI when pushing a tag and see the release archive pushed automatically to your project.&lt;/p&gt;

&lt;p&gt;To add another target platform, e.g. 64-bit Linux, alter your &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;.travis.yml&lt;/code&gt; by replacing the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;env&lt;/code&gt; key with the following:&lt;/p&gt;

&lt;div class=&quot;language-yaml highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;na&quot;&gt;matrix&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;
  &lt;span class=&quot;na&quot;&gt;include&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;
    &lt;span class=&quot;pi&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;env&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;TARGET=arm-unknown-linux-gnueabihf CARGO_TARGET_ARM_UNKNOWN_LINUX_GNUEABIHF_LINKER=arm-linux-gnueabihf-gcc&lt;/span&gt;
    &lt;span class=&quot;pi&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;env&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;TARGET=x86_64-unknown-linux-gnu&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;This will run two builds, one with the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;TARGET&lt;/code&gt; set to &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;arm-unknown-linux-gnueabihf&lt;/code&gt; (and with the Cargo linker configuration for that platform) and one with the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;TARGET&lt;/code&gt; set to &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;x86_64-unknown-linux-gnu&lt;/code&gt; (as that’s the native platform for our build, there’s no need to specify a different linker).&lt;/p&gt;

&lt;p&gt;For a fully worked example building for the Raspberry Pi, Linux and macOS, see &lt;a href=&quot;https://github.com/mudge/homer&quot;&gt;my lightweight DNS-over-HTTPS proxy, Homer&lt;/a&gt;.&lt;/p&gt;
</content>
</entry><entry>
  <title type="html">What Is a &amp;ldquo;Successful&amp;rdquo; Software Development Team?</title>
  <link href="https://mudge.name/2016/03/26/the-difficulty-with-the-success-of-software-development-teams/"/>
  <id>https://mudge.name/2016/03/26/the-difficulty-with-the-success-of-software-development-teams/</id>
  <published>2016-03-26T00:00:00+00:00</published>
  <updated>2016-03-26T00:00:00+00:00</updated>
  <summary type="html">Thoughts on the difficulty of gauging and improving the success of development teams following Scale Summit 2016.</summary>
  <content type="html">&lt;p&gt;Last Friday, I attended &lt;a href=&quot;http://www.scalesummit.org/&quot;&gt;Scale Summit&lt;/a&gt; 2016, my fourth time at the conference formerly known as &lt;a href=&quot;http://www.scalecamp.org.uk/&quot;&gt;Scale Camp&lt;/a&gt;. I make no secret that it is &lt;a href=&quot;/2014/03/22/a-summit-for-scaling.html&quot;&gt;one of my favourite conferences&lt;/a&gt; with its &lt;a href=&quot;https://en.wikipedia.org/wiki/Open_Space_Technology&quot;&gt;open space technology format&lt;/a&gt;, the high calibre of attendees and its use of &lt;a href=&quot;https://www.chathamhouse.org/about/chatham-house-rule&quot;&gt;Chatham House Rule&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;I spent the morning participating in two sessions:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;“Breaking down barriers between teams”&lt;/li&gt;
  &lt;li&gt;“Code sharing in large organisations”&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;a href=&quot;https://gist.github.com/d6y/c063ccadfea3a6800ffd&quot;&gt;Richard Dallaway wrote up his notes&lt;/a&gt; from the former and both &lt;a href=&quot;http://www.annashipman.co.uk/jfdi/code-sharing.html&quot;&gt;Anna Shipman&lt;/a&gt; and &lt;a href=&quot;https://github.com/bazbremner/scalesummit-2016-notes/blob/master/code_sharing.org&quot;&gt;Barrie Bremner&lt;/a&gt; have shared their thoughts on the latter.&lt;/p&gt;

&lt;p&gt;As I’ve spent the past three years attempting to grow a software development company, there was a common theme between these two sessions that fascinated me:&lt;/p&gt;

&lt;blockquote&gt;
  &lt;p&gt;How do you measure the success of a software development team and, in turn, improve it?&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;The first session began with a discussion of how to encourage collaboration between teams (e.g. between different teams of developers, operations, product owners, etc.), break down organisational &lt;a href=&quot;http://www.grammarphobia.com/blog/2013/04/silo.html&quot;&gt;silos&lt;/a&gt; and consolidate tooling across an organisation.&lt;/p&gt;

&lt;p&gt;The second session focussed on promoting code re-use within a large organisation, with the aim of reducing the amount of duplicated software built by separate teams.&lt;/p&gt;

&lt;p&gt;Software development is rich with ideas and processes aiming to optimise efficiency and deliver better results, but has &lt;a href=&quot;http://pragdave.me/blog/2014/03/04/time-to-kill-agile/&quot;&gt;a somewhat spotty history of applying these practices&lt;/a&gt;. In such an environment, there is an ever-present risk of &lt;a href=&quot;https://en.wikipedia.org/wiki/Cargo_cult_programming#Cargo_cult_software_engineering&quot;&gt;cargo culting&lt;/a&gt;:&lt;/p&gt;

&lt;blockquote&gt;
  &lt;p&gt;McConnell describes software development organizations that attempt to emulate more successful development houses, […] by slavishly following a &lt;a href=&quot;https://en.wikipedia.org/wiki/Software_development_process&quot; title=&quot;Software development process&quot;&gt;software development process&lt;/a&gt; without understanding the reasoning behind it.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;With this in mind, in both sessions my first question was similar:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;Why &lt;em&gt;should&lt;/em&gt; we break down the barriers between teams?&lt;/li&gt;
  &lt;li&gt;Why &lt;em&gt;should&lt;/em&gt; we share code in a large organisation?&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Without a clear motivation for using it, how can anyone be truly convinced of a practice? A team forced to collaborate without incentive to do so beyond “our managers told us to” seems unlikely to truly benefit. What’s more, without clear reasoning, it’s hard—if not impossible—to measure the success of any endeavour. Sure, the teams are talking more but why is that a good thing?&lt;/p&gt;

&lt;p&gt;The push for increased collaboration between developers and operations staff lead to the &lt;a href=&quot;https://en.wikipedia.org/wiki/DevOps&quot;&gt;DevOps movement&lt;/a&gt;, which aimed to spread knowledge of and responsibility for the deployment software and its on-going maintenance. The pattern of developers “&lt;a href=&quot;http://c2.com/cgi/wiki?ThrownOverTheWall&quot;&gt;throwing code over the wall&lt;/a&gt;” to operations staff, who were suddenly responsible for its performance and uptime, contributed to antagonism between teams and increased both the risk and cost of failure.&lt;/p&gt;

&lt;p&gt;Breaking down the barriers between these teams had some key goals in mind:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;Reduce the risk of failures when deploying software&lt;/li&gt;
  &lt;li&gt;Reduce the time to recovery when failures do occur&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;We could similarly evaluate improving communication between product owners and developers, e.g.&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;Reduce the amount of unnecessary software written &lt;a href=&quot;https://en.wikipedia.org/wiki/Tree_swing_cartoon&quot;&gt;due to misunderstanding&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;Reduce the cost of software and ship higher value features in a more timely fashion, by sharing the complexity and risk of the backlog&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;However, choosing different evaluation criteria can have an unpredictable—and even damaging—effect:&lt;/p&gt;

&lt;blockquote&gt;
  &lt;p&gt;Some of the managers decided that it would be a good idea to track the progress of each individual engineer in terms of the amount of code that they wrote from week to week. They devised a form that each engineer was required to submit every Friday, which included a field for the number of lines of code that were written that week.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;— Andy Hertzfeld, “&lt;a href=&quot;http://www.folklore.org/StoryView.py?story=Negative_2000_Lines_Of_Code.txt&quot;&gt;-2000 Lines Of Code&lt;/a&gt;”&lt;/p&gt;

&lt;p&gt;Incentivising developers to write &lt;em&gt;more&lt;/em&gt; code can accidentally reward those who write verbose software (often at the expense of maintainability). There are even stories of &lt;a href=&quot;http://thedailywtf.com/articles/The-Defect-Black-Market&quot;&gt;how introducing a bounty for fixing bugs created a black market&lt;/a&gt; within a company.&lt;/p&gt;

&lt;p&gt;Not all incentives have such obvious (even if only in hindsight) side-effects. Discussion in the first session about consolidating tooling (specifically, continuous integration solutions) brought up a more difficult scenario: management want everyone to use the same tool in order to save paying for (and maintaining) multiple solutions to the same problem. What is the cost of forcing everyone to use the same tool? Does this accidentally reduce the &lt;a href=&quot;https://labs.spotify.com/2014/03/27/spotify-engineering-culture-part-1/&quot;&gt;autonomy&lt;/a&gt; of a team, coupling them to others in a way that might impede their progress? What if the teams’ requirements differ in a way that you missed?&lt;/p&gt;

&lt;p&gt;The benefits of code reuse should also be subject to the same scrutiny: as &lt;a href=&quot;http://www.annashipman.co.uk/jfdi/code-sharing.html&quot;&gt;Anna Shipman writes&lt;/a&gt; (emphasis my own):&lt;/p&gt;

&lt;blockquote&gt;
  &lt;p&gt;The problem we are trying to solve is to reduce duplication of &lt;strong&gt;work&lt;/strong&gt;, rather than specifically duplication of &lt;strong&gt;code&lt;/strong&gt;. More generally, we do not want teams to waste time reinventing the wheel. We do not necessarily want “the best tool for the job”, we want the most cost-effective tool, and that might be copying someone else’s code, or the team solving the same, or similar, problem in a different way.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Perhaps using &lt;a href=&quot;http://martinfowler.com/articles/microservices.html&quot;&gt;services&lt;/a&gt; would be a better way to share common functionality than reusing code itself?&lt;/p&gt;

&lt;p&gt;Ultimately, these different attempts to improve teams were best described as a struggle between focussing on &lt;strong&gt;outputs&lt;/strong&gt; and &lt;strong&gt;means&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;The two are often confused: no one encouraging collaboration between teams does so without an implicit expectation that their mutual output will improve. No one attempting to consolidate tooling does so thinking that it will have a deleterious effect on users.&lt;/p&gt;

&lt;p&gt;Without attempting to understand &lt;em&gt;both&lt;/em&gt; output and means, we are at risk of jeopardising, not improving, our chances of success.&lt;/p&gt;

</content>
</entry><entry>
  <title type="html">Data Structures as Functions (or, Implementing &lt;code&gt;Set#to_proc&lt;/code&gt; and &lt;code&gt;Hash#to_proc&lt;/code&gt; in Ruby)</title>
  <link href="https://mudge.name/2014/11/26/data-structures-as-functions/"/>
  <id>https://mudge.name/2014/11/26/data-structures-as-functions/</id>
  <published>2014-11-26T00:00:00+00:00</published>
  <updated>2014-11-26T00:00:00+00:00</updated>
  <summary type="html">Experimenting with using hashes and sets as functions in Ruby.</summary>
  <content type="html">&lt;p&gt;&lt;a class=&quot;pull-right&quot; href=&quot;//skillsmatter.com/skillscasts/6282-exploring-to_proc&quot;&gt;&lt;img src=&quot;/i/to_proc.png&quot; alt=&quot;View the full presentation on the Skills Matter web site&quot; width=&quot;400&quot; height=&quot;250&quot; /&gt;&lt;/a&gt; &lt;em&gt;At the &lt;a href=&quot;http://lrug.org/meetings/2015/march/&quot;&gt;March 2015 meeting of the London Ruby
User Group&lt;/a&gt;, I delivered a presentation
based on this blog post with some supplemental material. I’ve since tweaked
some of the examples to closer match the ones in the final talk but the
content should largely be the same.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;Reading &lt;a href=&quot;http://thepugautomatic.com/2014/11/array-to-proc-for-hash-access/&quot;&gt;Henrik Nyh’s “&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Array#to_proc&lt;/code&gt; for hash
access”&lt;/a&gt;
made me think about a similar concept in &lt;a href=&quot;http://clojure.org/&quot;&gt;Clojure&lt;/a&gt;:
data structures being used as functions.&lt;/p&gt;

&lt;h2 id=&quot;functions&quot;&gt;Functions&lt;/h2&gt;

&lt;p&gt;First of all, let’s be clear what we mean by “functions”.
The definition of a &lt;a href=&quot;http://en.wikipedia.org/wiki/Function_(mathematics)&quot;&gt;function in mathematics&lt;/a&gt; is as follows:&lt;/p&gt;

&lt;blockquote&gt;
  &lt;p&gt;A &lt;b&gt;function&lt;/b&gt; is a relation between a set of inputs and a set of
permissible outputs with the property that each input is related to exactly
one output.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;So a function &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;f&lt;/code&gt; can be considered as a way of mapping from some input (its
“&lt;a href=&quot;http://en.wikipedia.org/wiki/Domain_of_a_function&quot;&gt;domain&lt;/a&gt;”) to some output (its “&lt;a href=&quot;http://en.wikipedia.org/wiki/Codomain_(mathematics)&quot;&gt;codomain&lt;/a&gt;”), e.g.&lt;/p&gt;

&lt;div class=&quot;language-ruby highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;n&quot;&gt;f&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;c1&quot;&gt;# =&amp;gt; 2&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Crucially, whenever the function is called with a specific input, it should
&lt;em&gt;always&lt;/em&gt; produce the same output. This means &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;f&lt;/code&gt; in the following example
would &lt;em&gt;not&lt;/em&gt; be a function because it produces two different outputs given the
same input:&lt;/p&gt;

&lt;div class=&quot;language-ruby highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;n&quot;&gt;f&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;c1&quot;&gt;# =&amp;gt; 2&lt;/span&gt;

&lt;span class=&quot;n&quot;&gt;f&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;c1&quot;&gt;# =&amp;gt; 3&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Whole programming languages are built out of this concept (it’s called
&lt;a href=&quot;http://en.wikipedia.org/wiki/Functional_programming&quot;&gt;&lt;em&gt;functional&lt;/em&gt; programming&lt;/a&gt; for a reason) but they also appear in languages
that aren’t purely functional, such as Ruby.&lt;/p&gt;

&lt;p&gt;A common place you might find them is when using methods from
&lt;a href=&quot;http://ruby-doc.org/core-2.1.5/Enumerable.html&quot;&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Enumerable&lt;/code&gt;&lt;/a&gt; such as
&lt;a href=&quot;http://ruby-doc.org/core-2.1.5/Enumerable.html#method-i-all-3F&quot;&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;all?&lt;/code&gt;&lt;/a&gt;
and &lt;a href=&quot;http://www.ruby-doc.org/core-2.1.5/Array.html#method-i-map&quot;&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;map&lt;/code&gt;&lt;/a&gt;:&lt;/p&gt;

&lt;div class=&quot;language-ruby highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;..&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;10&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;).&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;all?&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;|&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;x&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;|&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;x&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&amp;gt;&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;4&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;c1&quot;&gt;# =&amp;gt; false&lt;/span&gt;

&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;..&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;10&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;).&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;map&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;|&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;x&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;|&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;x&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;*&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;2&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;c1&quot;&gt;# =&amp;gt; [2, 4, 6, 8, 10, 12, 14, 16, 18, 20]&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;In both of the above cases, the &lt;a href=&quot;http://ruby-doc.com/docs/ProgrammingRuby/html/tut_containers.html#S2&quot;&gt;blocks&lt;/a&gt; passed to &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;all?&lt;/code&gt; and &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;map&lt;/code&gt; are
actually simple functions.&lt;/p&gt;

&lt;div class=&quot;language-ruby highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;nb&quot;&gt;proc&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;|&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;x&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;|&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;x&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&amp;gt;&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;4&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;No matter how many times you call this function with the number 0, it will
&lt;em&gt;always&lt;/em&gt; return false.&lt;/p&gt;

&lt;div class=&quot;language-ruby highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;nb&quot;&gt;proc&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;|&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;x&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;|&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;x&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;*&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;2&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Similarly, no matter how many times you call this function with the number 1,
it will &lt;em&gt;always&lt;/em&gt; return 2.&lt;/p&gt;

&lt;p&gt;With this in mind, let’s take a look at functions in Clojure.&lt;/p&gt;

&lt;h2 id=&quot;using-sets-as-functions&quot;&gt;Using sets as functions&lt;/h2&gt;

&lt;p&gt;In Clojure (as in many languages, including Ruby), there are data structures
for &lt;a href=&quot;http://clojure.org/data_structures#Data%20Structures-Sets&quot;&gt;sets&lt;/a&gt; and &lt;a href=&quot;http://clojure.org/data_structures#Data%20Structures-Maps%20(IPersistentMap)&quot;&gt;maps&lt;/a&gt; (not to be confused with the
&lt;a href=&quot;https://clojuredocs.org/clojure.core/map&quot;&gt;function of the same name&lt;/a&gt; but something akin to a &lt;a href=&quot;http://www.ruby-doc.org/core-2.1.5/Hash.html&quot;&gt;Hash&lt;/a&gt;
in Ruby or a &lt;a href=&quot;https://docs.python.org/2/tutorial/datastructures.html#dictionaries&quot;&gt;dictionary&lt;/a&gt; in Python) but, unlike other
languages, these structures can be used in places where you would expect a
function.&lt;/p&gt;

&lt;p&gt;For example, Clojure’s &lt;a href=&quot;https://clojuredocs.org/clojure.core/filter&quot;&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;filter&lt;/code&gt;&lt;/a&gt; takes two arguments: a
function &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;pred&lt;/code&gt; and a collection &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;coll&lt;/code&gt;. As you might expect from its name, it
goes through &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;coll&lt;/code&gt; and only returns elements that return true when fed into
&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;pred&lt;/code&gt;. We can use this to filter even numbers from a range using
&lt;a href=&quot;https://clojuredocs.org/clojure.core/even_q&quot;&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;even?&lt;/code&gt;&lt;/a&gt;:&lt;/p&gt;

&lt;div class=&quot;language-clojure highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;filter&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;even?&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;range&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;30&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;))&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
&lt;/span&gt;&lt;span class=&quot;c1&quot;&gt;;; =&amp;gt; (2 4 6 8 10 12 14 16 18 20 22 24 26 28)&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Of course, &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;even?&lt;/code&gt; can be used by itself too:&lt;/p&gt;

&lt;div class=&quot;language-clojure highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;even?&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;5&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
&lt;/span&gt;&lt;span class=&quot;c1&quot;&gt;;; =&amp;gt; false&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Nothing too surprising there but what if we used a set instead of an existing
function?&lt;/p&gt;

&lt;p&gt;Let’s define a set of numbers we like:&lt;/p&gt;

&lt;div class=&quot;language-clojure highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;def&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;perfect-numbers&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;o&quot;&gt;#&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;6&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;28&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;496&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;})&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Now let’s filter our range again but using the set instead of &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;even?&lt;/code&gt;:&lt;/p&gt;

&lt;div class=&quot;language-clojure highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;filter&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;perfect-numbers&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;range&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;30&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;))&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
&lt;/span&gt;&lt;span class=&quot;c1&quot;&gt;;; =&amp;gt; (6 28)&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;To understand what happened here, let’s just try using the set as a function
as we did &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;even?&lt;/code&gt;:&lt;/p&gt;

&lt;div class=&quot;language-clojure highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;perfect-numbers&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
&lt;/span&gt;&lt;span class=&quot;c1&quot;&gt;;; =&amp;gt; nil&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;

&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;perfect-numbers&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;6&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
&lt;/span&gt;&lt;span class=&quot;c1&quot;&gt;;; =&amp;gt; 6&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;It turns out that sets can be used as functions of their members: when given
some input, they will return that input if (and only if) it is a member of the
set; if it isn’t, it returns &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;nil&lt;/code&gt;. This behaviour makes for a nifty shortcut
when testing whether a value is in some whitelist:&lt;/p&gt;

&lt;div class=&quot;language-clojure highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;def&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;valid-attributes&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;o&quot;&gt;#&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;no&quot;&gt;:name&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;no&quot;&gt;:age&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;})&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;

&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;defn&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;foo&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;attribute&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
  &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;when&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;valid-attributes&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;attribute&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
    &lt;/span&gt;&lt;span class=&quot;c1&quot;&gt;;; insert money-making business logic here&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
    &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;println&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;PROFIT&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)))&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;The way this actually works in Clojure is that sets implement the &lt;a href=&quot;https://github.com/clojure/clojure/blob/master/src/jvm/clojure/lang/IFn.java&quot;&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;IFn&lt;/code&gt;
interface&lt;/a&gt; meaning that they have an &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;invoke&lt;/code&gt; method that can be used to
call them as if they were ordinary functions. There’s a very similar &lt;a href=&quot;http://php.net/manual/en/language.oop5.magic.php#object.invoke&quot;&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;__invoke&lt;/code&gt;
magic method&lt;/a&gt; in &lt;a href=&quot;http://php.net/&quot;&gt;PHP&lt;/a&gt; that allows objects to be called as functions:&lt;/p&gt;

&lt;div class=&quot;language-php highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;cp&quot;&gt;&amp;lt;?php&lt;/span&gt;
&lt;span class=&quot;kd&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;Monkey&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;function&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;__invoke&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;$name&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;Ook, &lt;/span&gt;&lt;span class=&quot;si&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;$name&lt;/span&gt;&lt;span class=&quot;si&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;!&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

&lt;span class=&quot;nv&quot;&gt;$monkey&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;Monkey&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;nv&quot;&gt;$monkey&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;'Bob'&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
&lt;span class=&quot;c1&quot;&gt;// =&amp;gt; 'Ook, Bob!'&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;We can achieve similar behaviour in Ruby by implementing &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Set#to_proc&lt;/code&gt; (in the same
way Henrik implemented &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;to_proc&lt;/code&gt; on &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Array&lt;/code&gt;):&lt;/p&gt;

&lt;div class=&quot;language-ruby highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;k&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;Set&lt;/span&gt;
  &lt;span class=&quot;k&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;to_proc&lt;/span&gt;
    &lt;span class=&quot;nb&quot;&gt;proc&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;|&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;element&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;|&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;element&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;member?&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;element&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
  &lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;This means we can do the same sort of filtering using
&lt;a href=&quot;http://www.ruby-doc.org/core-2.1.5/Array.html#method-i-select&quot;&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;select&lt;/code&gt;&lt;/a&gt;:&lt;/p&gt;

&lt;div class=&quot;language-ruby highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;nb&quot;&gt;require&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;set&quot;&lt;/span&gt;

&lt;span class=&quot;n&quot;&gt;perfect_numbers&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;Set&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;6&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;28&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;496&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt;

&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;..&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;30&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;).&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;select&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;amp;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;perfect_numbers&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;c1&quot;&gt;# =&amp;gt; [6, 28]&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;If you’re unfamiliar with the syntax above, let’s take a quick detour (if this
is old news to you, feel free to &lt;a href=&quot;#calling-procs&quot;&gt;skip ahead&lt;/a&gt;).&lt;/p&gt;

&lt;h2 id=&quot;whats-with-the-ampersand&quot;&gt;What’s with the ampersand?&lt;/h2&gt;

&lt;p&gt;In Ruby, there are methods (such as &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;select&lt;/code&gt; above) that take &lt;a href=&quot;http://ruby-doc.com/docs/ProgrammingRuby/html/tut_containers.html#S2&quot;&gt;blocks&lt;/a&gt;. A
good example of this is &lt;a href=&quot;http://www.ruby-doc.org/core-2.1.5/Array.html#method-i-map&quot;&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;map&lt;/code&gt;&lt;/a&gt; which evaluates a given block for
each item in a collection and returns a new collection with the results:&lt;/p&gt;

&lt;div class=&quot;language-ruby highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;..&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;30&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;).&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;map&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;|&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;x&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;|&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;x&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;to_s&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;c1&quot;&gt;# =&amp;gt; [&quot;1&quot;, &quot;2&quot;, &quot;3&quot;, ..., &quot;30&quot;]&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Wherever you can pass a block in Ruby, you can also pass a
&lt;a href=&quot;http://www.ruby-doc.org/core-2.1.5/Proc.html&quot;&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Proc&lt;/code&gt;&lt;/a&gt; using an ampersand like so:&lt;/p&gt;

&lt;div class=&quot;language-ruby highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;n&quot;&gt;stringify&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;proc&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;|&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;x&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;|&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;x&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;to_s&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;..&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;30&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;).&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;map&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;amp;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;stringify&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;c1&quot;&gt;# =&amp;gt; [&quot;1&quot;, &quot;2&quot;, &quot;3&quot;, ..., &quot;30&quot;]&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Note that &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;stringify&lt;/code&gt; is already a &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Proc&lt;/code&gt; here but if it wasn’t, Ruby would
internally call &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;to_proc&lt;/code&gt; on it in an attempt to coerce it to the right type.&lt;/p&gt;

&lt;p&gt;As mapping over a collection and calling a method on each element is quite a
common task and armed with the knowledge of Ruby’s internal use of &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;to_proc&lt;/code&gt;,
a technique emerged of using symbols as a shortcut:&lt;/p&gt;

&lt;div class=&quot;language-ruby highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;..&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;30&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;).&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;map&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;amp;&lt;/span&gt;&lt;span class=&quot;ss&quot;&gt;:to_s&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;c1&quot;&gt;# =&amp;gt; [&quot;1&quot;, &quot;2&quot;, &quot;3&quot;, ..., &quot;30&quot;]&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;This works by implementing &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Symbol#to_proc&lt;/code&gt; with something like so:&lt;/p&gt;

&lt;div class=&quot;language-ruby highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;k&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;Symbol&lt;/span&gt;
  &lt;span class=&quot;k&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;to_proc&lt;/span&gt;
    &lt;span class=&quot;nb&quot;&gt;proc&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;|&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;x&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;|&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;x&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;send&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;self&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
  &lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;(The &lt;a href=&quot;https://github.com/ruby/ruby/blob/25bab786cb416aa491ff62e6d9b6ba196251bfc6/string.c#L8631-L8669&quot;&gt;real implementation&lt;/a&gt; actually features a cache to reduce some of the
performance overhead of this approach.)&lt;/p&gt;

&lt;p&gt;You can think of this as expanding like so:&lt;/p&gt;

&lt;div class=&quot;language-ruby highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;..&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;30&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;).&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;map&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;amp;&lt;/span&gt;&lt;span class=&quot;ss&quot;&gt;:to_s&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;..&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;30&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;).&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;map&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;amp;&lt;/span&gt;&lt;span class=&quot;ss&quot;&gt;:to_s&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;to_proc&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;..&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;30&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;).&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;map&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;amp;&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;proc&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;|&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;x&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;|&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;x&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;send&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;ss&quot;&gt;:to_s&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;})&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;..&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;30&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;).&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;map&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;|&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;x&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;|&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;x&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;send&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;ss&quot;&gt;:to_s&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;..&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;30&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;).&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;map&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;|&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;x&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;|&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;x&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;to_s&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h2 id=&quot;calling-procs&quot;&gt;Calling Procs&lt;/h2&gt;

&lt;p&gt;With &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;to_proc&lt;/code&gt; implemented on sets, can we use them as we did in Clojure?&lt;/p&gt;

&lt;div class=&quot;language-ruby highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;n&quot;&gt;perfect_numbers&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;call&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;c1&quot;&gt;# NoMethodError: undefined method `call' for #&amp;lt;Set: {6, 28, 496}&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Sadly not, as sets themselves are not &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Proc&lt;/code&gt;s. Perhaps we could use the
ampersand trick?&lt;/p&gt;

&lt;div class=&quot;language-ruby highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;o&quot;&gt;&amp;amp;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;perfect_numbers&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;call&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;c1&quot;&gt;# SyntaxError: unexpected &amp;amp;, expecting end-of-input&lt;/span&gt;
&lt;span class=&quot;c1&quot;&gt;# &amp;amp;perfect_numbers.call(1)&lt;/span&gt;
&lt;span class=&quot;c1&quot;&gt;#  ^&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;No luck there either. In fact, we’d have to do the following:&lt;/p&gt;

&lt;div class=&quot;language-ruby highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;n&quot;&gt;perfect_numbers&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;to_proc&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;call&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;c1&quot;&gt;# =&amp;gt; nil&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;This is where our efforts diverge from Clojure: simply implementing &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;to_proc&lt;/code&gt;
doesn’t mean that sets are now functions. You could maybe make them more
&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Proc&lt;/code&gt;-like with something like the following but there is no equivalent to
Clojure’s &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;IFn&lt;/code&gt; or PHP’s &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;__invoke&lt;/code&gt; in Ruby:&lt;/p&gt;

&lt;div class=&quot;language-ruby highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;k&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;Object&lt;/span&gt;
  &lt;span class=&quot;k&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;call&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;*&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;args&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;to_proc&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;call&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;*&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;args&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
  &lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;

&lt;span class=&quot;n&quot;&gt;perfect_numbers&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;call&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;3&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;c1&quot;&gt;# =&amp;gt; 6&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h2 id=&quot;using-hashes-as-functions&quot;&gt;Using hashes as functions&lt;/h2&gt;

&lt;p&gt;A slightly more interesting case is that of maps in Clojure:&lt;/p&gt;

&lt;div class=&quot;language-clojure highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;p&quot;&gt;({&lt;/span&gt;&lt;span class=&quot;no&quot;&gt;:a&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;2&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;no&quot;&gt;:b&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;3&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;no&quot;&gt;:a&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
&lt;/span&gt;&lt;span class=&quot;c1&quot;&gt;;; =&amp;gt; 2&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;

&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;({&lt;/span&gt;&lt;span class=&quot;no&quot;&gt;:a&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;2&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;no&quot;&gt;:b&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;3&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;no&quot;&gt;:c&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
&lt;/span&gt;&lt;span class=&quot;c1&quot;&gt;;; =&amp;gt; nil&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Like the behaviour of sets, maps are functions of their keys: given the input
of the key, they return the corresponding value (or &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;nil&lt;/code&gt; if there is no such
entry). This fits quite nicely with our original mathematical functions: the
keys being the &lt;a href=&quot;http://en.wikipedia.org/wiki/Domain_of_a_function&quot;&gt;domain&lt;/a&gt; and the values being the &lt;a href=&quot;http://en.wikipedia.org/wiki/Codomain_(mathematics)&quot;&gt;codomain&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;As for how this might be useful, &lt;a href=&quot;http://blog.jayfields.com/2010/08/clojure-using-sets-and-maps-as.html&quot;&gt;Jay Fields wrote up an interesting use
case&lt;/a&gt; when comparing and filtering two maps. It also gives us an
alternate way to access hash keys as in Henrik’s original post.&lt;/p&gt;

&lt;p&gt;First, let’s implement &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Hash#to_proc&lt;/code&gt; to mirror Clojure’s behaviour:&lt;/p&gt;

&lt;div class=&quot;language-ruby highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;k&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;Hash&lt;/span&gt;
  &lt;span class=&quot;k&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;to_proc&lt;/span&gt;
    &lt;span class=&quot;nb&quot;&gt;method&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;ss&quot;&gt;:[]&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;).&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;to_proc&lt;/span&gt;
  &lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Here we simply take &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Hash&lt;/code&gt;’s existing &lt;a href=&quot;http://www.ruby-doc.org/core-2.1.5/Hash.html#method-i-5B-5D&quot;&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;[]&lt;/code&gt;&lt;/a&gt; method for accessing values and
convert it to a &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Proc&lt;/code&gt;. Now we can use it like so:&lt;/p&gt;

&lt;div class=&quot;language-ruby highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;sx&quot;&gt;%w(a b c d)&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;map&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;amp;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;a&quot;&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;b&quot;&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;2&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;c&quot;&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;3&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;})&lt;/span&gt;
&lt;span class=&quot;c1&quot;&gt;# =&amp;gt; [1, 2, 3, nil]&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Or, to use a more realistic example:&lt;/p&gt;

&lt;div class=&quot;language-ruby highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;n&quot;&gt;person&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;ss&quot;&gt;name: &lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;Robert Paulson&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;ss&quot;&gt;age: &lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;43&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

&lt;span class=&quot;nb&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;age&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;ss&quot;&gt;:name&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;ss&quot;&gt;:age&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;].&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;map&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;amp;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;person&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;c1&quot;&gt;# =&amp;gt; [&quot;Robert Paulson&quot;, 43]&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;In this second example, it’s almost the reverse of Henrik’s &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Array#to_proc&lt;/code&gt;
behaviour where the hash is passed into a function created from the keys:&lt;/p&gt;

&lt;div class=&quot;language-ruby highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;p&quot;&gt;[{&lt;/span&gt; &lt;span class=&quot;ss&quot;&gt;name: &lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;A&quot;&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;},&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;ss&quot;&gt;name: &lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;B&quot;&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;}].&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;map&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;amp;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;ss&quot;&gt;:name&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;])&lt;/span&gt;
&lt;span class=&quot;c1&quot;&gt;# =&amp;gt; [&quot;A&quot;, &quot;B&quot;]&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;In our case, it is the key that is passed into a function created from the
hash:&lt;/p&gt;

&lt;div class=&quot;language-ruby highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;ss&quot;&gt;:name&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;].&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;map&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;amp;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;ss&quot;&gt;name: &lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;A&quot;&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;})&lt;/span&gt;
&lt;span class=&quot;c1&quot;&gt;# =&amp;gt; &quot;A&quot;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h2 id=&quot;other-data-structures-as-functions&quot;&gt;Other data structures as functions&lt;/h2&gt;

&lt;p&gt;Maps and sets are not the only data structures to implement the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;IFn&lt;/code&gt;
interface in Clojure.&lt;/p&gt;

&lt;p&gt;There are vectors:&lt;/p&gt;

&lt;div class=&quot;language-clojure highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;p&quot;&gt;([&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;2&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;3&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
&lt;/span&gt;&lt;span class=&quot;c1&quot;&gt;;; =&amp;gt; 1&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Which would be equivalent to the following &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Array#to_proc&lt;/code&gt;:&lt;/p&gt;

&lt;div class=&quot;language-ruby highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;k&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;Array&lt;/span&gt;
  &lt;span class=&quot;k&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;to_proc&lt;/span&gt;
    &lt;span class=&quot;nb&quot;&gt;method&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;ss&quot;&gt;:[]&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;).&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;to_proc&lt;/span&gt;
  &lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;

&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;..&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;3&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;).&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;map&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;amp;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;A&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;B&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;C&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;D&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;E&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;])&lt;/span&gt;
&lt;span class=&quot;c1&quot;&gt;# =&amp;gt; [&quot;B&quot;, &quot;C&quot;, &quot;D&quot;]&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;There are also keywords which are similar to Ruby’s symbols:&lt;/p&gt;

&lt;div class=&quot;language-clojure highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;no&quot;&gt;:name&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;no&quot;&gt;:name&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;Robert Paulson&quot;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;no&quot;&gt;:age&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;42&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;})&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
&lt;/span&gt;&lt;span class=&quot;c1&quot;&gt;;; =&amp;gt; &quot;Robert Paulson&quot;&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;To implement this in Ruby, we’d have to override the now standard
&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Symbol#to_proc&lt;/code&gt; like so:&lt;/p&gt;

&lt;div class=&quot;language-ruby highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;k&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;Symbol&lt;/span&gt;
  &lt;span class=&quot;k&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;to_proc&lt;/span&gt;
    &lt;span class=&quot;nb&quot;&gt;proc&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;|&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;coll&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;|&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;coll&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;self&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
  &lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;

&lt;span class=&quot;p&quot;&gt;[{&lt;/span&gt; &lt;span class=&quot;ss&quot;&gt;name: &lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;Robert Paulson&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;ss&quot;&gt;age: &lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;42&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;}].&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;map&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;amp;&lt;/span&gt;&lt;span class=&quot;ss&quot;&gt;:name&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;c1&quot;&gt;# =&amp;gt; [&quot;Robert Paulson&quot;]&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Of course, this would mean you can no longer use it for method access.&lt;/p&gt;

&lt;p&gt;In conclusion, we shouldn’t be afraid to experiment with concepts from other
languages. After all, as &lt;a href=&quot;http://blog.fogus.me/2011/08/14/perlis-languages/&quot;&gt;Alan Perlis
said&lt;/a&gt;:&lt;/p&gt;

&lt;blockquote&gt;
  &lt;p&gt;A language that doesn’t affect the way you think about programming is not
worth knowing.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2 id=&quot;further-reading&quot;&gt;Further reading&lt;/h2&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;a href=&quot;http://raganwald.com/&quot;&gt;Reginald Braithwaite&lt;/a&gt; talked about
&lt;a href=&quot;http://raganwald.com/2007/10/stringtoproc.html&quot;&gt;a possible &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;String#to_proc&lt;/code&gt;&lt;/a&gt; and
&lt;a href=&quot;http://raganwald.com/2007/11/fun-with-symboltoproc.html&quot;&gt;using arguments with &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Symbol#to_proc&lt;/code&gt;&lt;/a&gt; back in
2007;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;http://ohthatjames.github.io/&quot;&gt;James Hunt&lt;/a&gt; has experimented with
&lt;a href=&quot;https://gist.github.com/ohthatjames/60a0b219443aeeb6cb41&quot;&gt;reducing the amount&lt;/a&gt; and
&lt;a href=&quot;https://gist.github.com/ohthatjames/de7a516ac13d0da2147e&quot;&gt;avoiding monkey-patching altogether to achieve the same effects&lt;/a&gt;;&lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;&lt;a href=&quot;http://samhuri.net/posts/2007/05/enumerable-pluck-and-string-to_proc-for-ruby/&quot;&gt;Sami Samhuri wrote about using &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;String#to_proc&lt;/code&gt; and &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Enumerable#to_proc&lt;/code&gt; to
achieve a universal &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Enumerable#pluck&lt;/code&gt; back in
2007&lt;/a&gt;.&lt;/p&gt;

  &lt;/li&gt;
&lt;/ul&gt;

</content>
</entry><entry>
  <title type="html">A Summit for Scaling</title>
  <link href="https://mudge.name/2014/03/22/a-summit-for-scaling/"/>
  <id>https://mudge.name/2014/03/22/a-summit-for-scaling/</id>
  <published>2014-03-22T00:00:00+00:00</published>
  <updated>2014-03-22T00:00:00+00:00</updated>
  <summary type="html">A summary of my time at 2014&amp;rsquo;s Scale Summit: a performance and scalability unconference.
</summary>
  <content type="html">&lt;p&gt;&lt;img src=&quot;/i/scalesummit.jpg&quot; width=&quot;200&quot; height=&quot;200&quot; alt=&quot;&quot; class=&quot;pull-left&quot; /&gt;Yesterday, I attended &lt;a href=&quot;http://www.scalesummit.org&quot;&gt;Scale Summit&lt;/a&gt;: “a performance and scalability
unconference” using the &lt;a href=&quot;http://en.wikipedia.org/wiki/Open_Space_Technology&quot;&gt;Open Space Technology&lt;/a&gt; format. Scale Summit is the
successor to &lt;a href=&quot;http://www.scalecamp.org.uk&quot;&gt;Scale Camp&lt;/a&gt;, an event organised by &lt;a href=&quot;https://twitter.com/bruntonspall&quot;&gt;Michæl
Brunton-Spall&lt;/a&gt; and previously held at &lt;a href=&quot;http://www.theguardian.com&quot;&gt;The Guardian&lt;/a&gt;
offices. Having attended Scale Camp in late 2012 and considering it to be one
of the most rewarding experiences of my career, it was without hesitation that
I signed up for its follow-up.&lt;/p&gt;

&lt;p&gt;The quality of Scale Camp and Scale Summit is almost entirely dependent on
the attendees: the unconference format and &lt;a href=&quot;http://www.chathamhouse.org/about-us/chathamhouserule&quot;&gt;Chatham House Rule&lt;/a&gt; mean that
people are free to run open discussions and &lt;a href=&quot;http://en.wikipedia.org/wiki/Fishbowl_(conversation)&quot;&gt;Fishbowls&lt;/a&gt; and speak a
little more freely than they might elsewhere. For example, my highlight of
2012’s Scale Camp was discussing &lt;a href=&quot;http://en.wikipedia.org/wiki/Continuous_delivery&quot;&gt;continuous delivery&lt;/a&gt; with people I would
consider experts and even &lt;em&gt;pioneers&lt;/em&gt; of that practice.&lt;/p&gt;

&lt;p&gt;As Scale Summit is multi-track, I could not participate in all sessions but
thankfully other attendees such as &lt;a href=&quot;https://gist.github.com/philandstuff/9684513&quot;&gt;Philip Potter&lt;/a&gt;, &lt;a href=&quot;http://words.volant.is/articles/notes-scale-summit/&quot;&gt;Matthew
Cottingham&lt;/a&gt; and &lt;a href=&quot;http://www.cryptocracy.com/blog/2014/03/23/scale-summit-2014/&quot;&gt;Zac Stevens&lt;/a&gt; have also written up their notes.&lt;/p&gt;

&lt;h2 id=&quot;real-time-monitoring&quot;&gt;Real-Time Monitoring&lt;/h2&gt;

&lt;p&gt;My first session of the day was an open discussion on real-time monitoring and
the collection (and retention) of large amounts of metrics.&lt;/p&gt;

&lt;p&gt;There was a general consensus that everyone is using &lt;a href=&quot;http://graphite.wikidot.com&quot;&gt;Graphite&lt;/a&gt; though
&lt;a href=&quot;http://grafana.org&quot;&gt;Grafana&lt;/a&gt; piqued interest as an alternative dashboard and &lt;a href=&quot;https://github.com/jssjr/carbonate&quot;&gt;Carbonate&lt;/a&gt;
suggested for rebalancing clusters.&lt;/p&gt;

&lt;p&gt;There seemed to be a lot of people using &lt;a href=&quot;http://sensuapp.org&quot;&gt;Sensu&lt;/a&gt; for monitoring (as well as
the usual crowd of &lt;a href=&quot;http://www.zenoss.com&quot;&gt;Zenoss&lt;/a&gt;, &lt;a href=&quot;http://ganglia.sourceforge.net&quot;&gt;Ganglia&lt;/a&gt; and &lt;a href=&quot;http://www.nagios.org&quot;&gt;Nagios&lt;/a&gt; users) though some
(including myself) have been experimenting with &lt;a href=&quot;http://riemann.io&quot;&gt;Riemann&lt;/a&gt;, particularly for
its ability to alert on event expiry. We currently use this at my day job to
detect when an integration with an external service has started misbehaving as
we can reasonably expect new data in a certain time period (e.g. a tweet every
10
minutes). Upon discussing this, it occurred to me that I could use this expiry
technique a lot more than I currently do as a very simple way of flagging
anomalies.&lt;/p&gt;

&lt;p&gt;While discussing the resolution of metrics (e.g. every second, every 5
seconds, etc.), an interesting question was asked:&lt;/p&gt;

&lt;blockquote&gt;
  &lt;p&gt;When investigating an outage, has anyone ever had &lt;em&gt;too much&lt;/em&gt; data?&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;It seems that collecting the right data is still difficult to predict so the
sensible approach seems to be &lt;a href=&quot;http://codeascraft.com/2011/02/15/measure-anything-measure-everything/&quot;&gt;measuring everything&lt;/a&gt;. The
cost of doing so (particularly those wanting high resolution or retention of
data) can be high with one individual’s monitoring server having “more cores
than my entire [server] farm”.&lt;/p&gt;

&lt;p&gt;Aside from &lt;a href=&quot;http://collectd.org&quot;&gt;collectd&lt;/a&gt;, there was some discussion around using &lt;a href=&quot;https://github.com/BrightcoveOS/Diamond&quot;&gt;Diamond&lt;/a&gt; to
collect metrics largely due to it being written in &lt;a href=&quot;https://www.python.org&quot;&gt;Python&lt;/a&gt; for companies
already using the programming language for their applications.&lt;/p&gt;

&lt;h2 id=&quot;microservices-in-the-real-world&quot;&gt;Microservices in the Real World&lt;/h2&gt;

&lt;p&gt;During the day’s introductions, quite a few people mentioned &lt;a href=&quot;http://martinfowler.com/articles/microservices.html&quot;&gt;Microservices&lt;/a&gt;
which I had heard little about so I decided to take part in an extremely
fast-paced Fishbowl discussion on the topic.&lt;/p&gt;

&lt;p&gt;There was some disagreement on the exact definition of a microservice and some
took issue with Martin Fowler’s insistence on HTTP as a transport or purely
using lines of code as a defining factor. The loose definition seemed to be
modelling applications as a collection of simple services responsible
for a single piece of functionality (e.g. translating place names). In this
regard, it seems remarkably similar to &lt;a href=&quot;http://en.wikipedia.org/wiki/Service-oriented_architecture&quot;&gt;service-oriented architecture&lt;/a&gt;
(SOA) but with an emphasis on smaller services.&lt;/p&gt;

&lt;p&gt;Many benefits were cited in terms of reducing complexity, affording diversity
of technologies and the ability for teams of one or two people to hold entire
services in their head (something that becomes unfeasible over time for
monolithic systems).&lt;/p&gt;

&lt;p&gt;The importance of consistent practice was said to be paramount: consistent
documentation, clear ownership and predictable interfaces were all discussed.
One intriguing practice mentioned was that of having a standardised endpoint
that revealed the owner and current health of a system (with some parallels
being drawn to a &lt;a href=&quot;http://humanstxt.org&quot;&gt;humans.txt&lt;/a&gt;). Even if ownership and responsibility for a
service is unclear, the size and simplicity of such systems might make them
easier to maintain.&lt;/p&gt;

&lt;p&gt;While the benefits seemed appealing, I was more than a little concerned about
the effects this would have on teams and technology organisations as a whole.
One problem I have seen repeatedly across companies is that of information
siloes: where team members become segregated and shared knowledge of systems
is largely absent. I fear this model of many small services would encourage
siloes but the difficult question is whether this is a bad thing: perhaps it
is better for small teams to move fast at the cost of shared understanding? If
the benefits are to believed then services might be small and simple enough to
alleviate the dreaded &lt;a href=&quot;http://en.wikipedia.org/wiki/Bus_factor&quot;&gt;Bus factor&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;As an extreme example of this culture, the practice of allowing &lt;em&gt;competing&lt;/em&gt;
services was touched upon: where teams can freely develop another version of a
service without collaboration and let client applications decide which they
prefer. The implications this environment would have on teams (where people
could simply supplant your work and openly duplicate effort)
makes me more than a little apprehensive.&lt;/p&gt;

&lt;p&gt;I remain sceptical but it has encouraged me to consider pursuing SOA more
aggressively particularly considering success stories such as
&lt;a href=&quot;http://devblog.songkick.com/2012/07/27/service-oriented-songkick/&quot;&gt;Songkick’s&lt;/a&gt;.&lt;/p&gt;

&lt;h2 id=&quot;visibility-of-infrastructure&quot;&gt;Visibility of Infrastructure&lt;/h2&gt;

&lt;p&gt;This discussion began with the problem of increasing awareness of
infrastructure within an organisation to help reduce the divide between
developers and operations but meandered around general visibility of
infrastructure for various stakeholders.&lt;/p&gt;

&lt;p&gt;It was agreed that the problem with documentation is that it is almost always
out of date and there is little incentive to keep it updated. After some
suggestions of automatically deriving architectural diagrams from
infrastructure (using firewall rules to infer traffic between services or the
likes of &lt;a href=&quot;http://twitter.github.io/zipkin/&quot;&gt;Zipkin&lt;/a&gt;), it was clear that human intervention is required. It was
suggested that diagramming the high level, logical flow of a system might last
longer than any including exact implementation details (the ultimate,
eternally correct diagram being a single circle with the word “System” in it).&lt;/p&gt;

&lt;p&gt;After a slightly alarming diversion into the world of &lt;a href=&quot;http://en.wikipedia.org/wiki/Promise_theory&quot;&gt;Promise Theory&lt;/a&gt;,
there was a discussion about tools for describing the relationships
between services beyond what is currently offered by the likes of
&lt;a href=&quot;http://puppetlabs.com&quot;&gt;Puppet&lt;/a&gt; and &lt;a href=&quot;http://www.getchef.com&quot;&gt;Chef&lt;/a&gt; with &lt;a href=&quot;https://juju.ubuntu.com&quot;&gt;Ubuntu Juju&lt;/a&gt; suggested as a possible option.&lt;/p&gt;

&lt;p&gt;When discussing the problem of service discovery, one architectural practice
that caught my attention was that of running &lt;a href=&quot;http://haproxy.1wt.eu&quot;&gt;HAProxy&lt;/a&gt; on &lt;em&gt;all&lt;/em&gt; servers and
having applications talk to &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;localhost&lt;/code&gt; with the load balancer configuration
determining where services currently reside. A few people seemed to be using
this pattern (and some cited &lt;a href=&quot;https://github.com/airbnb/synapse&quot;&gt;Airbnb’s Synapse&lt;/a&gt;) though the
difficulty of debugging network traffic and managing health checks coming from
every server were mentioned as downsides.&lt;/p&gt;

&lt;h2 id=&quot;surviving-a-ddos-attack&quot;&gt;Surviving a &lt;abbr title=&quot;Distributed Denial of Service&quot;&gt;DDoS&lt;/abbr&gt; Attack&lt;/h2&gt;

&lt;p&gt;The discussion of this topic was obviously a little more sensitive than most
but the general recommendations were to use a content delivery network such as
&lt;a href=&quot;http://www.fastly.com&quot;&gt;Fastly&lt;/a&gt; to immediate drop all attacks below &lt;a href=&quot;http://en.wikipedia.org/wiki/Application_layer&quot;&gt;Layer 7&lt;/a&gt; and use services
such as &lt;a href=&quot;http://www.prolexic.com&quot;&gt;Prolexic&lt;/a&gt; and intrusion detection systems such as &lt;a href=&quot;http://www.snort.org&quot;&gt;Snort&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Ultimately, the most sophisticated attacks are extremely difficult to
distinguish from normal user behaviour though these are fortunately rare with
most attacks being easy to detect. That said, there seemed to be no fully
automated way to deal with an attack and human intervention is currently
required.&lt;/p&gt;

&lt;h2 id=&quot;linux-containers&quot;&gt;Linux Containers&lt;/h2&gt;

&lt;p&gt;The last and most popular session I attended was that on Linux containers with
&lt;a href=&quot;https://www.docker.io&quot;&gt;Docker&lt;/a&gt; being the impetus but &lt;a href=&quot;https://linuxcontainers.org&quot;&gt;LXC&lt;/a&gt; and &lt;a href=&quot;http://en.wikipedia.org/wiki/Solaris_Containers&quot;&gt;Solaris Zones&lt;/a&gt; also being
discussed.&lt;/p&gt;

&lt;p&gt;There was a lot of interest in the promise of containers as a way of providing
isolation with less of a performance penalty than fully virtual machines but
startlingly few people were using them in production. A few people were using
them during continuous integration and to build packages for multiple
operating systems extremely quickly but most people were still
experimenting.&lt;/p&gt;

&lt;p&gt;The current best practice seemed to be using a container to run a single
process (e.g. one container for &lt;a href=&quot;http://nginx.org&quot;&gt;nginx&lt;/a&gt; and one for &lt;a href=&quot;http://php-fpm.org&quot;&gt;php-fpm&lt;/a&gt;) though it
wasn’t explored what benefits this gives.&lt;/p&gt;

&lt;p&gt;One appealing use of containers seemed to be encouraging the &lt;a href=&quot;http://martinfowler.com/bliki/ImmutableServer.html&quot;&gt;Immutable
Server&lt;/a&gt; pattern where persistent data is separated from application servers
and configuration management is eschewed in favour of disposable instances:
e.g. if you want to upgrade a package, simply build a new container with the
change, spin it up and kill the old instances (this reminded me of &lt;a href=&quot;http://www.holovaty.com/writing/aws-notes/&quot;&gt;Adrian
Holovaty’s move to AWS from Heroku&lt;/a&gt;).&lt;/p&gt;

&lt;p&gt;There was a brief discussion on service discovery in the face of many
containers with some success using &lt;a href=&quot;https://github.com/dotcloud/hipache&quot;&gt;Hipache&lt;/a&gt; instead of using the
HAProxy/Synapse approach mentioned in the earlier “Visibility of
Infrastructure” session. For further production use, &lt;a href=&quot;http://mesos.apache.org&quot;&gt;Mesos&lt;/a&gt; and
&lt;a href=&quot;https://github.com/mesosphere/marathon&quot;&gt;Marathon&lt;/a&gt; seemed to be the technologies to watch.&lt;/p&gt;

&lt;p&gt;In general, everyone seemed quite excited about the potential of containers
but it is still very young with little tooling available. At the
very least, it made me want to join the cabal of people toying with Docker in
my spare time.&lt;/p&gt;

&lt;h2 id=&quot;conclusions&quot;&gt;Conclusions&lt;/h2&gt;

&lt;p&gt;In general, there was a strong theme of small, composable services (perhaps in
containers) and a particularly impressive demonstration of deploying an
application server in minutes to something resembling a private &lt;a href=&quot;https://www.heroku.com&quot;&gt;Heroku&lt;/a&gt; by
using &lt;a href=&quot;https://github.com/progrium/dokku&quot;&gt;Dokku&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;As with the last Scale Camp, it did feel like absolutely
everyone is using &lt;a href=&quot;http://aws.amazon.com&quot;&gt;AWS&lt;/a&gt; though there were some, like myself, with a mix of
hosting providers. Amongst those not entirely in the cloud, there was some
fondness for &lt;a href=&quot;https://www.openstack.org&quot;&gt;OpenStack&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Besides the aforementioned renewed vigor to pursue SOA, I was convinced by a
discussion between sessions to make better use of DNS as a means of
service discovery: e.g. instead of specifying the location of your MySQL
cluster by managing configuration files with Chef, simply hardcode the
location as &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;mysql.service&lt;/code&gt; and then use an internal DNS server to resolve
that as needed (even allowing for test environments to resolve to a different
location). This separation of service location from application configuration
seems obvious but I have long coupled such things in the past and used
configuration management to work around it.&lt;/p&gt;

&lt;p&gt;If you are at all interested in any of the above or just want to sit in a room
and hear tales of scaling woe, I heartily recommend attending the next Scale
Summit.&lt;/p&gt;

</content>
</entry><entry>
  <title type="html">Implementing if in Ruby</title>
  <link href="https://mudge.name/2012/07/09/implementing-if-in-ruby/"/>
  <id>https://mudge.name/2012/07/09/implementing-if-in-ruby/</id>
  <published>2012-07-09T00:00:00+00:00</published>
  <updated>2012-07-09T00:00:00+00:00</updated>
  <summary type="html">Using multiple blocks in Ruby to implement Smalltalk-style control flow.</summary>
  <content type="html">&lt;p&gt;It was with great interest that I watched &lt;a href=&quot;http://pupeno.com/&quot;&gt;J. Pablo Fernández&lt;/a&gt;’s presentation at the &lt;a href=&quot;http://lrug.org/meetings/2012/06/18/july-2012-meeting/&quot;&gt;London Ruby User Group&lt;/a&gt; tonight entitled “&lt;a href=&quot;http://skillsmatter.com/podcast/ajax-ria/what-ruby-cant-do&quot;&gt;What Ruby Can’t Do&lt;/a&gt;”. Concerned of being myopic about the language that happens to provide my income, I am keen to see what we can unabashedly steal from other languages (see &lt;a href=&quot;http://www.confreaks.com/videos/551-scotlandruby2011-testing-outside-of-the-ruby-world&quot;&gt;Joseph Wilk’s excellent “Testing Outside of the Ruby World” for a similar sentiment&lt;/a&gt;). Pablo offered two alternatives to Ruby – Smalltalk and Lisp – and then demonstrated their relative power by implementing his own &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;if&lt;/code&gt; condition in both.&lt;/p&gt;

&lt;p&gt;While the Lisp example demonstrated homoiconicity and the power of macros, the Smalltalk example was (barring syntactic oddities) much closer to home for Rubyists.&lt;/p&gt;

&lt;p&gt;To summarise: Smalltalk eschews keywords for control structures such as &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;if&lt;/code&gt; in favour of message sends (to use the Smalltalk parlance for method calls). This means that conditional logic is just another method on an object (not entirely dissimilar to Ruby’s infamous &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;5.times { ... }&lt;/code&gt; example):&lt;/p&gt;

&lt;div class=&quot;language-smalltalk highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;nv&quot;&gt;somePredicate&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;ifTrue:&lt;/span&gt;  &lt;span class=&quot;p&quot;&gt;[&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;Transcript&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;show:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;'I am true'&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;  &lt;span class=&quot;p&quot;&gt;]&lt;/span&gt;
              &lt;span class=&quot;nb&quot;&gt;ifFalse:&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;[&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;Transcript&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;show:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;'I am false'&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;].&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;The implementation of these messages is simple: define a method that takes two arguments, one block to call when true (e.g. &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;trueBlock&lt;/code&gt;) and one block to call when false (e.g. &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;falseBlock&lt;/code&gt;). Objects that can be considered “truthy” evaluate the first block and those that are “falsey” evaluate the second:&lt;/p&gt;

&lt;div class=&quot;language-smalltalk highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;nf&quot;&gt;ifTrue:&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;trueBlock&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;ifFalse:&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;falseBlock&lt;/span&gt;
  &lt;span class=&quot;c&quot;&gt;&quot;Implementation for truthy objects.&quot;&lt;/span&gt;
  &lt;span class=&quot;o&quot;&gt;^&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;trueBlock&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;value&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;div class=&quot;language-smalltalk highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;nf&quot;&gt;ifTrue:&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;trueBlock&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;ifFalse:&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;falseBlock&lt;/span&gt;
  &lt;span class=&quot;c&quot;&gt;&quot;Implementation for falsey objects.&quot;&lt;/span&gt;
  &lt;span class=&quot;o&quot;&gt;^&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;falseBlock&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;value&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Pablo bemoaned that such a construction cannot be done in Ruby easily without chaining &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Proc&lt;/code&gt; objects and &lt;a href=&quot;https://twitter.com/mortice/status/222393465663787008&quot;&gt;Tom Stuart summarised the problem on Twitter&lt;/a&gt;: “Spoiler alert: Smalltalk lets you pass multiple blocks to a method naturally, Ruby doesn’t.” However, &lt;a href=&quot;https://twitter.com/mudge/status/222398046825234432&quot;&gt;I tweeted&lt;/a&gt; that this might not be true from Ruby 1.9 onwards.&lt;/p&gt;

&lt;p&gt;To mimic the Smalltalk example, the following syntax is actually valid Ruby:&lt;/p&gt;

&lt;div class=&quot;language-ruby highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;n&quot;&gt;some_predicate&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;-&amp;gt;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;It's true!&quot;&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;},&lt;/span&gt; &lt;span class=&quot;ss&quot;&gt;else: &lt;/span&gt;&lt;span class=&quot;o&quot;&gt;-&amp;gt;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;It's false!&quot;&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;While it might at first seem odd, it’s actually equivalent to the following:&lt;/p&gt;

&lt;div class=&quot;language-ruby highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;n&quot;&gt;some_predicate&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;if&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;lambda&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;It's true!&quot;&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;},&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;ss&quot;&gt;:else&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;lambda&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;It's false!&quot;&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;})&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;To implement this functionality, we can take advantage of Ruby’s notoriously open classes:&lt;/p&gt;

&lt;div class=&quot;language-ruby highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;k&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;BasicObject&lt;/span&gt;
  &lt;span class=&quot;k&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;if&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;if_true&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;options&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{})&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;if_true&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;call&lt;/span&gt;
  &lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;

&lt;span class=&quot;k&quot;&gt;module&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;Falsey&lt;/span&gt;
  &lt;span class=&quot;k&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;if&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;if_true&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;options&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{})&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;if_false&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;options&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;fetch&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;ss&quot;&gt;:else&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;-&amp;gt;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;{})&lt;/span&gt;

    &lt;span class=&quot;n&quot;&gt;if_false&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;call&lt;/span&gt;
  &lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;

&lt;span class=&quot;k&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;NilClass&lt;/span&gt;
  &lt;span class=&quot;kp&quot;&gt;include&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;Falsey&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;

&lt;span class=&quot;k&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;FalseClass&lt;/span&gt;
  &lt;span class=&quot;kp&quot;&gt;include&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;Falsey&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Note that I default the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;if_false&lt;/code&gt; case to a no-op to avoid using &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;if&lt;/code&gt; internally (and therefore sidestep some furore).&lt;/p&gt;

&lt;p&gt;(Those of you wishing to draw ire from your colleagues are welcome to download and use
the above code as a &lt;a href=&quot;http://rubygems.org/gems/if&quot;&gt;gem&lt;/a&gt;.)&lt;/p&gt;

&lt;p&gt;Practically speaking, there is likely to be a performance penalty in avoiding Ruby’s intrinsic optimisations for the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;if&lt;/code&gt; keyword. However, it implements functionality in typical Ruby that was previously reserved as a “special” language feature and, at the same time, demonstrates achieving conditional logic purely via polymorphism.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;&lt;em&gt;Update&lt;/em&gt;&lt;/strong&gt;&lt;em&gt;: Yehuda Katz beat me to this by two years in his 2009 post &lt;a href=&quot;http://yehudakatz.com/2009/10/04/emulating-smalltalks-conditionals-in-ruby/&quot;&gt;“Emulating
Smalltalk’s Conditionals in
Ruby”&lt;/a&gt;. He uses separate, chainable &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;if_true&lt;/code&gt; and &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;if_false&lt;/code&gt; methods rather than multiple blocks to mirror Smalltalk’s own &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;ifTrue&lt;/code&gt; and &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;ifFalse&lt;/code&gt; messages giving you code like so:&lt;/em&gt;&lt;/p&gt;

&lt;div class=&quot;language-ruby highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;n&quot;&gt;some_predicate&lt;/span&gt;
  &lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;if_true&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;puts&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;true&quot;&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
  &lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;if_false&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;puts&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;false&quot;&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

</content>
</entry><entry>
  <title type="html">Testing Vim Plugins on Travis CI with RSpec and Vimrunner</title>
  <link href="https://mudge.name/2012/04/18/testing-vim-plugins-on-travis-ci-with-rspec-and-vimrunner/"/>
  <id>https://mudge.name/2012/04/18/testing-vim-plugins-on-travis-ci-with-rspec-and-vimrunner/</id>
  <published>2012-04-18T00:00:00+00:00</published>
  <updated>2012-04-18T00:00:00+00:00</updated>
  <summary type="html">How to continuously integrate and test-drive Vim plugin development with the Vimrunner gem.
</summary>
  <content type="html">&lt;p&gt;I have been using &lt;a href=&quot;http://www.vim.org/&quot;&gt;Vim&lt;/a&gt; as my primary text editor for several years but it
was only recently that I decided to try and write &lt;a href=&quot;https://github.com/mudge/runspec.vim&quot;&gt;my own Vim
plugin&lt;/a&gt;. My aim was simple: to create a function that would run
the tests for whatever file I was working on. If the file was a test itself
then simply run it but if it wasn’t, try to find the corresponding test and
run that.&lt;/p&gt;

&lt;p&gt;Having heard Vim script (sometimes referred to as VimL) is a rather esoteric
language, I pored through the &lt;a href=&quot;http://vimdoc.sourceforge.net/htmldoc/usr_41.html&quot;&gt;official Vim manual section&lt;/a&gt; and &lt;a href=&quot;http://learnvimscriptthehardway.stevelosh.com/&quot;&gt;Steve
Losh’s “Learn Vimscript the Hard Way”&lt;/a&gt; and got stuck in.&lt;/p&gt;

&lt;p&gt;The code was simple at first but, as it progressed, I found myself programming
and then firing up Vim to check whether everything worked as expected. At the
recent &lt;a href=&quot;http://skillsmatter.com/podcast/ajax-ria/lrug-tdd-fishbowl&quot;&gt;London Ruby User Group “TDD Fishbowl” debate&lt;/a&gt;, this
method of verifying code correctness was derisively referred to as
“Refresh-Driven Development”. Mindful of this bad practice and having grown
tired of manually inspecting functions in Vim, I decided to look into
solutions for not only automating the testing of Vim script but also
techniques for test &lt;em&gt;driving&lt;/em&gt; development.&lt;/p&gt;

&lt;p&gt;My initial searches resulted in a &lt;a href=&quot;http://stackoverflow.com/questions/3029882/tools-for-testing-vim-plugins&quot;&gt;Stack Overflow post about Vim script unit
test frameworks&lt;/a&gt; but none of the solutions appeared
particularly popular or actively maintained. However, by adding “driving” to
my search terms, I came across &lt;a href=&quot;http://andrewradev.com/2011/11/15/driving-vim-with-ruby-and-cucumber/&quot;&gt;Andrew Radev’s “Driving Vim with Ruby and
Cucumber”&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;I recommend you read Andrew’s full post but the summary is that he wrote a
gem called &lt;a href=&quot;https://github.com/AndrewRadev/vimrunner&quot;&gt;Vimrunner&lt;/a&gt; which uses &lt;a href=&quot;http://vimdoc.sourceforge.net/htmldoc/remote.html&quot;&gt;Vim’s client-server
functionality&lt;/a&gt; to drive Vim from &lt;a href=&quot;http://www.ruby-lang.org/en/&quot;&gt;Ruby&lt;/a&gt;. Using this gem, he
was able to create &lt;a href=&quot;https://github.com/AndrewRadev/cucumber-vimscript&quot;&gt;an extension to Cucumber&lt;/a&gt; to write
acceptance tests for his Vim plugins.&lt;/p&gt;

&lt;p&gt;This was almost exactly what I was looking for except that I prefer to use
&lt;a href=&quot;https://www.relishapp.com/rspec&quot;&gt;RSpec&lt;/a&gt; for small libraries rather than &lt;a href=&quot;http://cukes.info/&quot;&gt;Cucumber&lt;/a&gt;. Luckily, the &lt;a href=&quot;https://github.com/AndrewRadev/cucumber-vimscript/blob/master/lib/cucumber/vimscript.rb&quot;&gt;core of
cucumber-vimscript&lt;/a&gt; is very simple, so I decided to
explore using only Vimrunner and RSpec to test-drive my plugin.&lt;/p&gt;

&lt;p&gt;To begin with, I had the following directory layout with the standard files:&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;.
├── plugin
│   └── runspec.vim
├── autoload
│   └── runspec.vim
└── doc
    └── runspec.txt
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;In order to get up and running with these Ruby testing frameworks, I added a
&lt;a href=&quot;http://gembundler.com/man/gemfile.5.html&quot;&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Gemfile&lt;/code&gt;&lt;/a&gt; with the following contents:&lt;/p&gt;

&lt;div class=&quot;language-ruby highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;n&quot;&gt;source&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;'https://rubygems.org'&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;gem&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;'vimrunner'&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;'~&amp;gt; 0.3.1'&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;gem&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;'rspec'&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;     &lt;span class=&quot;s1&quot;&gt;'~&amp;gt; 3.1.0'&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;The actual process of testing the plugin is as follows:&lt;/p&gt;

&lt;ol&gt;
  &lt;li&gt;Start a Vim server by invoking Vim with an explicit
&lt;a href=&quot;http://vimdoc.sourceforge.net/htmldoc/remote.html#--servername&quot;&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;servername&lt;/code&gt;&lt;/a&gt;;&lt;/li&gt;
  &lt;li&gt;Load our plugin under test into the server (viz. put it in Vim’s &lt;a href=&quot;http://vimdoc.sourceforge.net/htmldoc/options.html#'runtimepath'&quot;&gt;runtime
path&lt;/a&gt; and &lt;a href=&quot;http://vimdoc.sourceforge.net/htmldoc/repeat.html#:runtime&quot;&gt;execute the main script&lt;/a&gt;);&lt;/li&gt;
  &lt;li&gt;Start sending commands to our Vim server using &lt;a href=&quot;http://vimdoc.sourceforge.net/htmldoc/remote.html#--remote-expr&quot;&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;remote-expr&lt;/code&gt;&lt;/a&gt;
and check the results for correctness;&lt;/li&gt;
  &lt;li&gt;Quit the Vim server.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;If we’re writing a spec called &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;spec/runspec.vim_spec.rb&lt;/code&gt; then steps 1, 2
and 4 are straightforward thanks to Vimrunner:&lt;/p&gt;

&lt;div class=&quot;language-ruby highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;nb&quot;&gt;require&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;'vimrunner'&lt;/span&gt;

&lt;span class=&quot;n&quot;&gt;vim&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;Vimrunner&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;start&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;vim&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;add_plugin&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;no&quot;&gt;File&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;expand_path&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;'../..'&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;kp&quot;&gt;__FILE__&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;),&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;'plugin/runspec.vim'&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;

&lt;span class=&quot;no&quot;&gt;RSpec&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;describe&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;runspec.vim&quot;&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;do&lt;/span&gt;
  &lt;span class=&quot;n&quot;&gt;after&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;ss&quot;&gt;:all&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;do&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;vim&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;kill&lt;/span&gt;
  &lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;As you can see, we require the gem and then start a new Vim server with
&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;start&lt;/code&gt;. We add our plugin to the server using the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;add_plugin&lt;/code&gt; helper
method and pass the directory (given relative to our current test file) and
the relative path of the main plugin script. We also add an &lt;a href=&quot;https://www.relishapp.com/rspec/rspec-core/v/2-9/docs/hooks/before-and-after-hooks&quot;&gt;RSpec &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;after&lt;/code&gt;
hook&lt;/a&gt; to shut down the server once we are done.&lt;/p&gt;

&lt;p&gt;With this in place, we can start writing some basic tests.&lt;/p&gt;

&lt;p&gt;As the point of my plugin is to run tests by executing a shell command with
&lt;a href=&quot;http://vimdoc.sourceforge.net/htmldoc/various.html#:!&quot;&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;:!&lt;/code&gt;&lt;/a&gt;, it could prove difficult to exhaustively test. However, instead of focussing on
the actual execution, I can test the parts of the plugin leading up to that
final stage. In particular, let’s look at the part of the plugin that tries to
find the most appropriate spec for the currently open file.&lt;/p&gt;

&lt;p&gt;Before I started writing tests, this function was private and accessible only
to the plugin thereby making it difficult if not impossible to test. By
switching to using Vim’s &lt;a href=&quot;http://vimdoc.sourceforge.net/htmldoc/eval.html#autoload&quot;&gt;autoload&lt;/a&gt; functionality, I could now access (and
&lt;em&gt;test&lt;/em&gt;) the function from outside the plugin but without worrying too much
about namespace clashes.&lt;/p&gt;

&lt;p&gt;Here’s the first (admittedly simple) spec:&lt;/p&gt;

&lt;div class=&quot;language-ruby highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;nb&quot;&gt;require&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;'vimrunner'&lt;/span&gt;

&lt;span class=&quot;n&quot;&gt;vim&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;Vimrunner&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;start&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;vim&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;add_plugin&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;no&quot;&gt;File&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;expand_path&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;'../..'&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;kp&quot;&gt;__FILE__&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;),&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;'plugin/runspec.vim'&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;

&lt;span class=&quot;no&quot;&gt;RSpec&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;describe&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;runspec.vim&quot;&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;do&lt;/span&gt;
  &lt;span class=&quot;n&quot;&gt;after&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;ss&quot;&gt;:all&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;do&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;vim&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;kill&lt;/span&gt;
  &lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;

  &lt;span class=&quot;n&quot;&gt;describe&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;#SpecPath&quot;&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;do&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;it&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;returns the current file if it ends in _spec.rb&quot;&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;do&lt;/span&gt;
      &lt;span class=&quot;n&quot;&gt;expect&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;vim&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;command&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;'echo runspec#SpecPath(&quot;bar/foo_spec.rb&quot;)'&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)).&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;to&lt;/span&gt;
          &lt;span class=&quot;n&quot;&gt;eq&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;bar/foo_spec.rb&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;
  &lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Running the spec produced the following, rather thrilling output:&lt;/p&gt;

&lt;div class=&quot;language-console highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;gp&quot;&gt;$&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;rspec spec/runspec.vim_spec.rb
&lt;span class=&quot;c&quot;&gt;.
&lt;/span&gt;&lt;span class=&quot;go&quot;&gt;
Finished in 0.49066 seconds
1 example, 0 failures
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;As it ran, I saw an instance of Vim fire up and then be shut down as the test
suite completed. More importantly, all my specified behaviour was checked
without my having to interfere.&lt;/p&gt;

&lt;p&gt;This was a great start but is only really good if you are testing functions
that have no side-effects. However, one part of my plugin relies on checking
the file system to determine its behaviour: namely, it reads a &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Gemfile.lock&lt;/code&gt; to
determine whether to use &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;rspec&lt;/code&gt; or not. How could we test this sort of
functionality?&lt;/p&gt;

&lt;p&gt;Well, we can simply write out a dummy &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Gemfile.lock&lt;/code&gt; during our test and see
if the function works correctly:&lt;/p&gt;

&lt;div class=&quot;language-ruby highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;no&quot;&gt;File&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;open&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;Gemfile.lock&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;w&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;do&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;|&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;f&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;|&lt;/span&gt;
  &lt;span class=&quot;n&quot;&gt;f&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;puts&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;lt;&amp;lt;-&lt;/span&gt;&lt;span class=&quot;no&quot;&gt;EOF&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;sh&quot;&gt;
GEM
  remote: https://rubygems.org/
  specs:
    diff-lcs (1.1.3)
    rake (0.9.2.2)
    rspec (2.9.0)
      rspec-core (~&amp;gt; 2.9.0)
      rspec-expectations (~&amp;gt; 2.9.0)
      rspec-mocks (~&amp;gt; 2.9.0)
    rspec-core (2.9.0)
    rspec-expectations (2.9.1)
      diff-lcs (~&amp;gt; 1.1.3)
    rspec-mocks (2.9.0)

PLATFORMS
  ruby

DEPENDENCIES
  rake
  rspec
&lt;/span&gt;&lt;span class=&quot;no&quot;&gt;  EOF&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;However, this file won’t be removed at the end of your test runs and, what’s
worse, it could potentially interfere with other tests.&lt;/p&gt;

&lt;p&gt;To fix this, we can use Ruby’s &lt;a href=&quot;http://www.ruby-doc.org/stdlib-1.9.3/libdoc/tmpdir/rdoc/Dir.html#method-c-mktmpdir&quot;&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Dir.mktmpdir&lt;/code&gt;&lt;/a&gt; and &lt;a href=&quot;http://www.ruby-doc.org/core-1.9.3/Dir.html#method-c-chdir&quot;&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Dir.chdir&lt;/code&gt;&lt;/a&gt;
in an &lt;a href=&quot;https://www.relishapp.com/rspec/rspec-core/v/2-9/docs/hooks/around-hooks&quot;&gt;RSpec &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;around&lt;/code&gt; hook&lt;/a&gt; to create a temporary directory and &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;cd&lt;/code&gt;
into it for every single test. As we are re-using the same instance of Vim for
each test, we’ll also have to &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;cd&lt;/code&gt; the server into each new directory:&lt;/p&gt;

&lt;div class=&quot;language-ruby highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;c1&quot;&gt;# At the top of your spec:&lt;/span&gt;
&lt;span class=&quot;nb&quot;&gt;require&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;tmpdir&quot;&lt;/span&gt;

&lt;span class=&quot;c1&quot;&gt;# In your example group:&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;around&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;do&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;|&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;example&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;|&lt;/span&gt;
  &lt;span class=&quot;no&quot;&gt;Dir&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;mktmpdir&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;do&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;|&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;dir&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;|&lt;/span&gt;
    &lt;span class=&quot;no&quot;&gt;Dir&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;chdir&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;dir&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;do&lt;/span&gt;
      &lt;span class=&quot;n&quot;&gt;vim&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;command&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;cd &lt;/span&gt;&lt;span class=&quot;si&quot;&gt;#{&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;dir&lt;/span&gt;&lt;span class=&quot;si&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
      &lt;span class=&quot;n&quot;&gt;example&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;call&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;
  &lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;By using the block form of both &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;mktmpdir&lt;/code&gt; and &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;chdir&lt;/code&gt;, Ruby will destroy the
temporary directory and restore the current working directory after every run.&lt;/p&gt;

&lt;p&gt;This made it possible for me to write specs like the following without
worrying about the disk being littered with test files:&lt;/p&gt;

&lt;div class=&quot;language-ruby highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;n&quot;&gt;it&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;finds a test with the most similar name&quot;&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;do&lt;/span&gt;
  &lt;span class=&quot;no&quot;&gt;FileUtils&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;mkdir_p&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;test/unit&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
  &lt;span class=&quot;no&quot;&gt;FileUtils&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;touch&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;test/unit/user_test.rb&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;

  &lt;span class=&quot;n&quot;&gt;expect&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;vim&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;command&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;'echo runspec#SpecPath(&quot;app/models/user.rb&quot;)'&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)).&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;to&lt;/span&gt;
      &lt;span class=&quot;n&quot;&gt;eq&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;test/unit/user_test.rb&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;At this point, there was quite a bit of set up in the spec file so I decided
to move it out into a separate &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;spec_helper.rb&lt;/code&gt; to keep the actual tests
concise:&lt;/p&gt;

&lt;div class=&quot;language-ruby highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;nb&quot;&gt;require&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;'tmpdir'&lt;/span&gt;
&lt;span class=&quot;nb&quot;&gt;require&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;'vimrunner'&lt;/span&gt;

&lt;span class=&quot;no&quot;&gt;RSpec&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;configure&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;do&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;|&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;config&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;|&lt;/span&gt;
  &lt;span class=&quot;n&quot;&gt;config&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;around&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;do&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;|&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;example&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;|&lt;/span&gt;
    &lt;span class=&quot;no&quot;&gt;Dir&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;mktmpdir&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;do&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;|&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;dir&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;|&lt;/span&gt;
      &lt;span class=&quot;no&quot;&gt;Dir&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;chdir&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;dir&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;do&lt;/span&gt;
        &lt;span class=&quot;no&quot;&gt;VIM&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;command&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;cd &lt;/span&gt;&lt;span class=&quot;si&quot;&gt;#{&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;dir&lt;/span&gt;&lt;span class=&quot;si&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;example&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;call&lt;/span&gt;
      &lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;
  &lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;

  &lt;span class=&quot;n&quot;&gt;config&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;before&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;ss&quot;&gt;:suite&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;do&lt;/span&gt;
    &lt;span class=&quot;no&quot;&gt;VIM&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;Vimrunner&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;start&lt;/span&gt;
    &lt;span class=&quot;no&quot;&gt;VIM&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;add_plugin&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;no&quot;&gt;File&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;expand_path&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;'../..'&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;kp&quot;&gt;__FILE__&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;),&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;'plugin/runspec.vim'&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
  &lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;

  &lt;span class=&quot;n&quot;&gt;config&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;after&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;ss&quot;&gt;:suite&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;do&lt;/span&gt;
    &lt;span class=&quot;no&quot;&gt;VIM&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;kill&lt;/span&gt;
  &lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Note the use of a &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;VIM&lt;/code&gt; constant in &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;before(:suite)&lt;/code&gt; instead of a simple local
variable so that the server is available to all specs and the changing of
&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;after(:all)&lt;/code&gt; to &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;after(:suite)&lt;/code&gt; so that the Vim server is shut down once the
&lt;em&gt;entire&lt;/em&gt; test suite is finished and not just those in the current example
group.&lt;/p&gt;

&lt;p&gt;So now we have a fully automated test suite that we can run locally; what
next? How about continuously integrating that test suite to ensure that no
commit breaks the build? How about using &lt;a href=&quot;http://travis-ci.org/&quot;&gt;Travis CI&lt;/a&gt; for that purpose?&lt;/p&gt;

&lt;p&gt;If you are unfamiliar with Travis, have a look at their &lt;a href=&quot;http://about.travis-ci.org/docs/user/getting-started/&quot;&gt;Getting started&lt;/a&gt;
guide which will lead you through creating an account, hooking it up to your
&lt;a href=&quot;https://github.com/&quot;&gt;GitHub&lt;/a&gt; repositories and running your first build.&lt;/p&gt;

&lt;p&gt;In this case, we’re going to use their &lt;a href=&quot;http://about.travis-ci.org/docs/user/languages/ruby/&quot;&gt;Ruby support&lt;/a&gt; to run our test suite.
In order to do that, we need a default &lt;a href=&quot;http://rake.rubyforge.org/&quot;&gt;Rake&lt;/a&gt; task that will run the tests.&lt;/p&gt;

&lt;p&gt;Firstly, we need to add Rake as a dependency to our existing &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Gemfile&lt;/code&gt;:&lt;/p&gt;

&lt;div class=&quot;language-ruby highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;n&quot;&gt;gem&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;'rake'&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;'~&amp;gt; 10.3.2'&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Then we need to create a &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Rakefile&lt;/code&gt; and a task to run the tests. Luckily,
RSpec ships with &lt;a href=&quot;https://www.relishapp.com/rspec/rspec-core/v/2-9/docs/command-line/rake-task&quot;&gt;such a task&lt;/a&gt; by default which we can use
and define as the default like so:&lt;/p&gt;

&lt;div class=&quot;language-ruby highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;nb&quot;&gt;require&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;'rspec/core/rake_task'&lt;/span&gt;

&lt;span class=&quot;no&quot;&gt;RSpec&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;no&quot;&gt;Core&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;no&quot;&gt;RakeTask&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;new&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;ss&quot;&gt;:spec&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;

&lt;span class=&quot;n&quot;&gt;task&lt;/span&gt; &lt;span class=&quot;ss&quot;&gt;:default&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;ss&quot;&gt;:spec&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;(Note that &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;RSpec::Core::RakeTask.new&lt;/code&gt; will name the task &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;:spec&lt;/code&gt; by default
but I’m being explicit here for clarity.)&lt;/p&gt;

&lt;p&gt;You should now be able to run your suite like so:&lt;/p&gt;

&lt;div class=&quot;language-console highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;gp&quot;&gt;$&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;rake
&lt;span class=&quot;c&quot;&gt;..............
&lt;/span&gt;&lt;span class=&quot;go&quot;&gt;
Finished in 4.85 seconds
14 examples, 0 failures
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;All that is left is to configure Travis to do that too. Simply create a file
named &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;.travis.yml&lt;/code&gt; with the following contents:&lt;/p&gt;

&lt;div class=&quot;language-yaml highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;na&quot;&gt;language&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;ruby&lt;/span&gt;
&lt;span class=&quot;na&quot;&gt;rvm&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;
  &lt;span class=&quot;pi&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;2.1.3&lt;/span&gt;
&lt;span class=&quot;na&quot;&gt;before_install&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;sudo apt-get install vim-gtk&lt;/span&gt;
&lt;span class=&quot;na&quot;&gt;before_script&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;
  &lt;span class=&quot;pi&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;export&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s&quot;&gt;DISPLAY=:99.0&quot;&lt;/span&gt;
  &lt;span class=&quot;pi&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;sh&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s&quot;&gt;-e&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s&quot;&gt;/etc/init.d/xvfb&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s&quot;&gt;start&quot;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;This will inform Travis to use the latest version of Ruby (as of writing) and
to install a version of Vim with the necessary client-server functionality we
need for testing. We also need to start a &lt;a href=&quot;http://en.wikipedia.org/wiki/Xvfb&quot;&gt;X virtual framebuffer&lt;/a&gt; so
that we can create a Vim server successfully which is what the two
&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;before_script&lt;/code&gt; options are doing (see &lt;a href=&quot;http://about.travis-ci.org/docs/user/gui-and-headless-browsers/&quot;&gt;GUI &amp;amp; Headless browser testing&lt;/a&gt;
for more information).&lt;/p&gt;

&lt;p&gt;With that in place, the next time you push to GitHub, it should trigger a
build on Travis: see &lt;a href=&quot;http://travis-ci.org/#!/mudge/runspec.vim/builds/1148554&quot;&gt;Build #8 of runspec.vim&lt;/a&gt; for an example.&lt;/p&gt;

&lt;p&gt;To see a fully worked example (and my finished plugin), feel free to browse
the &lt;a href=&quot;https://github.com/mudge/runspec.vim&quot;&gt;source code of runspec.vim&lt;/a&gt; and look at the &lt;a href=&quot;http://travis-ci.org/mudge/runspec.vim&quot;&gt;build history
on Travis CI&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Update:&lt;/em&gt; Updated for &lt;a href=&quot;http://myronmars.to/n/dev-blog/2014/05/notable-changes-in-rspec-3&quot;&gt;RSpec 3&lt;/a&gt;.&lt;/p&gt;

</content>
</entry><entry>
  <title type="html">Naming Dynamically Created iframes</title>
  <link href="https://mudge.name/2012/01/29/naming-dynamically-created-iframes/"/>
  <id>https://mudge.name/2012/01/29/naming-dynamically-created-iframes/</id>
  <published>2012-01-29T00:00:00+00:00</published>
  <updated>2012-01-29T00:00:00+00:00</updated>
  <summary type="html">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=&quot;https://developer.mozilla.org/en/HTML/Element/iframe&quot;&gt;inline frames (aka &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&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=&quot;http://www.w3.org/TR/html4/present/frames.html#h-16.5&quot;&gt;W3C specification for the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;IFRAME&lt;/code&gt; element&lt;/a&gt; states the following document type definition:&lt;/p&gt;

&lt;div class=&quot;language-html highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;nt&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;err&quot;&gt;!&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;ELEMENT&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;IFRAME&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;err&quot;&gt;(%&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;flow&lt;/span&gt;&lt;span class=&quot;err&quot;&gt;;)&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;*&lt;/span&gt;         &lt;span class=&quot;na&quot;&gt;--&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;inline&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;subwindow&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;--&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;&amp;gt;&lt;/span&gt;
&lt;span class=&quot;nt&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;err&quot;&gt;!&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;ATTLIST&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;IFRAME&lt;/span&gt;
  &lt;span class=&quot;err&quot;&gt;%&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;coreattrs&lt;/span&gt;&lt;span class=&quot;err&quot;&gt;;&lt;/span&gt;                          &lt;span class=&quot;na&quot;&gt;--&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;id&lt;/span&gt;&lt;span class=&quot;err&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;class&lt;/span&gt;&lt;span class=&quot;err&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;style&lt;/span&gt;&lt;span class=&quot;err&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;title&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;--&lt;/span&gt;
  &lt;span class=&quot;na&quot;&gt;longdesc&lt;/span&gt;    &lt;span class=&quot;err&quot;&gt;%&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;URI&lt;/span&gt;&lt;span class=&quot;err&quot;&gt;;&lt;/span&gt;          &lt;span class=&quot;na&quot;&gt;#IMPLIED&lt;/span&gt;  &lt;span class=&quot;na&quot;&gt;--&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;link&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;to&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;long&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;description&lt;/span&gt;
                                          &lt;span class=&quot;err&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;complements&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;title&lt;/span&gt;&lt;span class=&quot;err&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;--&lt;/span&gt;
  &lt;span class=&quot;na&quot;&gt;name&lt;/span&gt;        &lt;span class=&quot;na&quot;&gt;CDATA&lt;/span&gt;          &lt;span class=&quot;na&quot;&gt;#IMPLIED&lt;/span&gt;  &lt;span class=&quot;na&quot;&gt;--&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;name&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;of&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;frame&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;for&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;targetting&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;--&lt;/span&gt;
  &lt;span class=&quot;na&quot;&gt;src&lt;/span&gt;         &lt;span class=&quot;err&quot;&gt;%&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;URI&lt;/span&gt;&lt;span class=&quot;err&quot;&gt;;&lt;/span&gt;          &lt;span class=&quot;na&quot;&gt;#IMPLIED&lt;/span&gt;  &lt;span class=&quot;na&quot;&gt;--&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;source&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;of&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;frame&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;content&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;--&lt;/span&gt;
  &lt;span class=&quot;na&quot;&gt;frameborder&lt;/span&gt; &lt;span class=&quot;err&quot;&gt;(1|0)&lt;/span&gt;          &lt;span class=&quot;err&quot;&gt;1&lt;/span&gt;         &lt;span class=&quot;na&quot;&gt;--&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;request&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;frame&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;borders&lt;/span&gt;&lt;span class=&quot;err&quot;&gt;?&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;--&lt;/span&gt;
  &lt;span class=&quot;na&quot;&gt;marginwidth&lt;/span&gt; &lt;span class=&quot;err&quot;&gt;%&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;Pixels&lt;/span&gt;&lt;span class=&quot;err&quot;&gt;;&lt;/span&gt;       &lt;span class=&quot;na&quot;&gt;#IMPLIED&lt;/span&gt;  &lt;span class=&quot;na&quot;&gt;--&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;margin&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;widths&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;in&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;pixels&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;--&lt;/span&gt;
  &lt;span class=&quot;na&quot;&gt;marginheight&lt;/span&gt; &lt;span class=&quot;err&quot;&gt;%&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;Pixels&lt;/span&gt;&lt;span class=&quot;err&quot;&gt;;&lt;/span&gt;      &lt;span class=&quot;na&quot;&gt;#IMPLIED&lt;/span&gt;  &lt;span class=&quot;na&quot;&gt;--&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;margin&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;height&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;in&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;pixels&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;--&lt;/span&gt;
  &lt;span class=&quot;na&quot;&gt;scrolling&lt;/span&gt;   &lt;span class=&quot;err&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;yes&lt;/span&gt;&lt;span class=&quot;err&quot;&gt;|&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;no&lt;/span&gt;&lt;span class=&quot;err&quot;&gt;|&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;auto&lt;/span&gt;&lt;span class=&quot;err&quot;&gt;)&lt;/span&gt;  &lt;span class=&quot;na&quot;&gt;auto&lt;/span&gt;      &lt;span class=&quot;na&quot;&gt;--&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;scrollbar&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;or&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;none&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;--&lt;/span&gt;
  &lt;span class=&quot;na&quot;&gt;align&lt;/span&gt;       &lt;span class=&quot;err&quot;&gt;%&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;IAlign&lt;/span&gt;&lt;span class=&quot;err&quot;&gt;;&lt;/span&gt;       &lt;span class=&quot;na&quot;&gt;#IMPLIED&lt;/span&gt;  &lt;span class=&quot;na&quot;&gt;--&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;vertical&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;or&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;horizontal&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;alignment&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;--&lt;/span&gt;
  &lt;span class=&quot;na&quot;&gt;height&lt;/span&gt;      &lt;span class=&quot;err&quot;&gt;%&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;Length&lt;/span&gt;&lt;span class=&quot;err&quot;&gt;;&lt;/span&gt;       &lt;span class=&quot;na&quot;&gt;#IMPLIED&lt;/span&gt;  &lt;span class=&quot;na&quot;&gt;--&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;frame&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;height&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;--&lt;/span&gt;
  &lt;span class=&quot;na&quot;&gt;width&lt;/span&gt;       &lt;span class=&quot;err&quot;&gt;%&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;Length&lt;/span&gt;&lt;span class=&quot;err&quot;&gt;;&lt;/span&gt;       &lt;span class=&quot;na&quot;&gt;#IMPLIED&lt;/span&gt;  &lt;span class=&quot;na&quot;&gt;--&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;frame&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;width&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;--&lt;/span&gt;
  &lt;span class=&quot;nt&quot;&gt;&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;The key thing I’m interested in here is the &lt;a href=&quot;http://www.w3.org/TR/html4/present/frames.html#adef-name-IFRAME&quot;&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;name&lt;/code&gt; attribute&lt;/a&gt;. Our particular application allows &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&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 class=&quot;language-plaintext highlighter-rouge&quot;&gt;iframe&lt;/code&gt;s send along their &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;name&lt;/code&gt; (accessed via &lt;a href=&quot;https://developer.mozilla.org/en/DOM/window.name&quot;&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;window.name&lt;/code&gt;&lt;/a&gt;).&lt;/p&gt;

&lt;p&gt;If you have an &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;iframe&lt;/code&gt; like the following in your web page:&lt;/p&gt;

&lt;div class=&quot;language-html highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;nt&quot;&gt;&amp;lt;iframe&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;frameborder=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;0&quot;&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;name=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;bob&quot;&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;src=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;/some-page&quot;&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;&amp;gt;&amp;lt;/iframe&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;And &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;/some-page&lt;/code&gt; contains the following HTML:&lt;/p&gt;

&lt;div class=&quot;language-html highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;cp&quot;&gt;&amp;lt;!DOCTYPE html&amp;gt;&lt;/span&gt;
&lt;span class=&quot;nt&quot;&gt;&amp;lt;title&amp;gt;&lt;/span&gt;Test&lt;span class=&quot;nt&quot;&gt;&amp;lt;/title&amp;gt;&lt;/span&gt;
&lt;span class=&quot;nt&quot;&gt;&amp;lt;script&amp;gt;&lt;/span&gt;
&lt;span class=&quot;nb&quot;&gt;document&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;write&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;'&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;My name is &lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;'&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;+&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;window&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
&lt;span class=&quot;nt&quot;&gt;&amp;lt;/script&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Then the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;iframe&lt;/code&gt; should output the following:&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;My name is bob
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Here’s that in action:&lt;/p&gt;

&lt;iframe frameborder=&quot;0&quot; name=&quot;bob&quot; src=&quot;/code/iframe-test.html&quot;&gt;&lt;/iframe&gt;

&lt;p&gt;However, if you are dynamically creating &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;iframe&lt;/code&gt;s with JavaScript (perhaps to avoid &lt;a href=&quot;https://developer.mozilla.org/En/Working_with_BFCache&quot;&gt;BFCache&lt;/a&gt; bugs) then you may run into some issues where &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;window.name&lt;/code&gt; is &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;undefined&lt;/code&gt; in Internet Explorer 6 and 7.&lt;/p&gt;

&lt;p&gt;Specifically, if you create an &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;iframe&lt;/code&gt; and set its name like so:&lt;/p&gt;

&lt;div class=&quot;language-html highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;nt&quot;&gt;&amp;lt;script&amp;gt;&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;kd&quot;&gt;function&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;()&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;kd&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;iframe&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;document&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;createElement&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;'&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;iframe&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;'&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
    &lt;span class=&quot;nx&quot;&gt;iframe&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;src&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;dl&quot;&gt;'&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;/some-page&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;'&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;nx&quot;&gt;iframe&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;name&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;dl&quot;&gt;'&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;bob&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;'&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;nb&quot;&gt;document&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;body&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;appendChild&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;iframe&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}());&lt;/span&gt;
&lt;span class=&quot;nt&quot;&gt;&amp;lt;/script&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Then &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;/some-page&lt;/code&gt; will report the following:&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;My name is
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Again, a live demonstration (note that this will only be broken in Internet Explorer):&lt;/p&gt;

&lt;div id=&quot;test-1&quot;&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=&quot;http://jquery.com/&quot;&gt;jQuery&lt;/a&gt;:&lt;/p&gt;

&lt;div class=&quot;language-html highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;nt&quot;&gt;&amp;lt;script&amp;gt;&lt;/span&gt;
&lt;span class=&quot;nx&quot;&gt;$&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;kd&quot;&gt;function&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;()&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;nx&quot;&gt;$&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;'&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&amp;lt;iframe/&amp;gt;&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;'&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;na&quot;&gt;src&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;dl&quot;&gt;'&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;/some-page&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;'&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
    &lt;span class=&quot;na&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;dl&quot;&gt;'&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;bob&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;'&lt;/span&gt;
  &lt;span class=&quot;p&quot;&gt;}).&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;appendTo&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;document&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;body&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;});&lt;/span&gt;
&lt;span class=&quot;nt&quot;&gt;&amp;lt;/script&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;The issue arises because the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;name&lt;/code&gt; of an &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;iframe&lt;/code&gt; cannot be changed in Internet Explorer, much like the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;type&lt;/code&gt; attribute on &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;input&lt;/code&gt; (there is a &lt;a href=&quot;http://api.jquery.com/jQuery/#creating-new-elements&quot;&gt;caveat in the jQuery documentation about the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&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 class=&quot;language-plaintext highlighter-rouge&quot;&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=&quot;language-html highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;nt&quot;&gt;&amp;lt;script&amp;gt;&lt;/span&gt;
&lt;span class=&quot;nx&quot;&gt;$&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;kd&quot;&gt;function&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;()&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;nx&quot;&gt;$&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;'&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&amp;lt;iframe name=&quot;bob&quot;&amp;gt;&amp;lt;/iframe&amp;gt;&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;'&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;).&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;attr&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;({&lt;/span&gt;
    &lt;span class=&quot;na&quot;&gt;src&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;dl&quot;&gt;'&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;/some-page&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;'&lt;/span&gt;
  &lt;span class=&quot;p&quot;&gt;}).&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;appendTo&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;document&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;body&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;});&lt;/span&gt;
&lt;span class=&quot;nt&quot;&gt;&amp;lt;/script&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;With pure JavaScript it is slightly trickier as you can no longer use &lt;a href=&quot;https://developer.mozilla.org/en/DOM/document.createElement&quot;&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;document.createElement&lt;/code&gt;&lt;/a&gt;. However, you can create a temporary container element (say, a &lt;a href=&quot;https://developer.mozilla.org/en/HTML/Element/div&quot;&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;div&lt;/code&gt;&lt;/a&gt;), and then modify that element’s &lt;a href=&quot;https://developer.mozilla.org/en/DOM/element.innerHTML&quot;&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;innerHTML&lt;/code&gt;&lt;/a&gt; to create your &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;iframe&lt;/code&gt; with its &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;name&lt;/code&gt; from the beginning. You can then take your newly created &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;iframe&lt;/code&gt; and insert that directly into the DOM (without ever inserting your temporary &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;div&lt;/code&gt;) like so:&lt;/p&gt;

&lt;div class=&quot;language-html highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;nt&quot;&gt;&amp;lt;script&amp;gt;&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;kd&quot;&gt;function&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;()&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;kd&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;temp&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;document&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;createElement&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;'&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;div&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;'&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
  &lt;span class=&quot;nx&quot;&gt;temp&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;innerHTML&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;dl&quot;&gt;'&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&amp;lt;iframe name=&quot;bob&quot; src=&quot;/some-page&quot;&amp;gt;&amp;lt;/iframe&amp;gt;&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;'&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;nb&quot;&gt;document&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;body&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;appendChild&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;temp&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;firstChild&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}());&lt;/span&gt;
&lt;span class=&quot;nt&quot;&gt;&amp;lt;/script&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&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;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;My name is bob
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;And a live demonstration:&lt;/p&gt;

&lt;div id=&quot;test-2&quot;&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 type="html">Managing Web Application Servers with Puppet</title>
  <link href="https://mudge.name/2011/08/11/managing-web-application-servers-with-puppet/"/>
  <id>https://mudge.name/2011/08/11/managing-web-application-servers-with-puppet/</id>
  <published>2011-08-11T00:00:00+00:00</published>
  <updated>2011-08-11T00:00:00+00:00</updated>
  <summary type="html">A transcript of my August 2011 LRUG presentation regarding configuration management and Puppet.</summary>
  <content type="html">&lt;p&gt;&lt;a class=&quot;pull-right&quot; href=&quot;//skillsmatter.com/podcast/home/lrug-puppet/&quot;&gt;&lt;img src=&quot;/i/screengrab.png&quot; alt=&quot;View the full presentation on the Skillsmatter web site&quot; width=&quot;239&quot; height=&quot;179&quot; /&gt;&lt;/a&gt; &lt;em&gt;At the &lt;a href=&quot;http://lrug.org/meetings/2011/07/18/august-2011-meeting/&quot;&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=&quot;http://skillsmatter.com/podcast/home/lrug-puppet/&quot;&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;h2 id=&quot;table-of-contents&quot;&gt;Table of Contents&lt;/h2&gt;

&lt;ol&gt;
  &lt;li&gt;&lt;a href=&quot;#introduction&quot;&gt;Introduction&lt;/a&gt; (who I am, what I am going to talk about);&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;#the-old-way&quot;&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=&quot;#enter-configuration-management&quot;&gt;Enter Configuration Management&lt;/a&gt; (introduction to configuration management and Puppet);&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;#the-better-way&quot;&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=&quot;#testing&quot;&gt;Testing&lt;/a&gt; (a few words on testing configuration);&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;#wrap-up&quot;&gt;Wrap-Up&lt;/a&gt; (closing remarks).&lt;/li&gt;
&lt;/ol&gt;

&lt;h2 id=&quot;introduction&quot;&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=&quot;http://www.puppetlabs.com/&quot;&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=&quot;http://www.nature.com/&quot;&gt;Nature Publishing Group&lt;/a&gt;: they publish &lt;a href=&quot;http://www.nature.com/nature/&quot;&gt;Nature&lt;/a&gt; which is a scientific journal that has been going for &lt;a href=&quot;http://en.wikipedia.org/wiki/Nature_(journal)&quot;&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=&quot;http://www.rubyonrails.org/&quot;&gt;Rails&lt;/a&gt; apps (with a couple of &lt;a href=&quot;http://www.sinatrarb.com/&quot;&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=&quot;http://www.linode.com/&quot;&gt;Linode&lt;/a&gt; or &lt;a href=&quot;http://www.slicehost.com/&quot;&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=&quot;http://www.heroku.com/&quot;&gt;Heroku&lt;/a&gt;);&lt;/li&gt;
  &lt;li&gt;You’ve heard of Puppet and/or &lt;a href=&quot;http://www.opscode.com/chef/&quot;&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 class=&quot;language-plaintext highlighter-rouge&quot;&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=&quot;https://github.com/capistrano/capistrano/wiki/&quot;&gt;Capistrano&lt;/a&gt; to get our application code on the servers).&lt;/li&gt;
&lt;/ul&gt;

&lt;h2 id=&quot;the-old-way&quot;&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=&quot;http://en.wikipedia.org/wiki/Master_Control_Program_(Tron)&quot;&gt;mcp&lt;/a&gt;” and it’s a normal, no-frills Rails application.&lt;/p&gt;

&lt;div class=&quot;language-console highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;gp&quot;&gt;$&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;ls &lt;/span&gt;mcp
&lt;span class=&quot;go&quot;&gt;Capfile      config       log
Gemfile      config.ru    public
Gemfile.lock db           script
README       doc          spec
Rakefile     features     tmp
app          lib          vendor
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&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=&quot;language-console highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;gp&quot;&gt;$&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;bundle &lt;span class=&quot;nb&quot;&gt;exec &lt;/span&gt;rspec spec
&lt;span class=&quot;c&quot;&gt;................
&lt;/span&gt;&lt;span class=&quot;go&quot;&gt;
Finished in 0.00816 seconds
Many, many examples, 0 failures
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&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 class=&quot;language-plaintext highlighter-rouge&quot;&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=&quot;language-console highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;gp&quot;&gt;$&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;ssh mudge@server1
&lt;span class=&quot;go&quot;&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;/div&gt;

&lt;p&gt;We like to separate our applications by user so – as this is an application called &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;mcp&lt;/code&gt; – we’ll use an &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&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 class=&quot;language-plaintext highlighter-rouge&quot;&gt;adduser&lt;/code&gt; on this machine…&lt;/p&gt;

&lt;div class=&quot;language-console highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;gp&quot;&gt;$&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;/usr/sbin/adduser &lt;span class=&quot;nt&quot;&gt;-h&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;adduser [--home DIR] [--shell SHELL]
[--no-create-home] [--uid ID] [--firstuid ID]
[--lastuid ID] [--gecos GECOS]
[--ingroup GROUP | --gid ID]
[--disabled-password] [--disabled-login]
[--encrypt-home] USER
   Add a normal user
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;It looks like this machine’s &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;adduser&lt;/code&gt; will create home directories for us by default (some operating systems require a &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;-m&lt;/code&gt; flag to do that). Let’s drop into a &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&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=&quot;language-console highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;gp&quot;&gt;$&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;sudo&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;-i&lt;/span&gt;
&lt;span class=&quot;gp&quot;&gt;#&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;adduser mcp
&lt;span class=&quot;go&quot;&gt;Adding user `mcp' ...
Adding new group `mcp' (1002) ...
Adding new user `mcp' (1001) with group `mcp' ...
Creating home directory `/home/mcp' ...
Copying files from `/etc/skel' ...
Enter new UNIX password: 
Retype new UNIX password: 
&lt;/span&gt;&lt;span class=&quot;c&quot;&gt;...
&lt;/span&gt;&lt;span class=&quot;go&quot;&gt;Is the information correct? [Y/n] 
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&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 class=&quot;language-plaintext highlighter-rouge&quot;&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 class=&quot;language-plaintext highlighter-rouge&quot;&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=&quot;language-console highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;gp&quot;&gt;#&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;mkdir&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;-m&lt;/span&gt; 700 ~mcp/.ssh
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Then we’re going to add our key to the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;authorized_keys&lt;/code&gt; file and, as I am running as &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;root&lt;/code&gt; and not &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;mcp&lt;/code&gt;, we need to make sure that we &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;chown&lt;/code&gt; everything to the right user so they can access it.&lt;/p&gt;

&lt;div class=&quot;language-console highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;gp&quot;&gt;#&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;vi ~mcp/.ssh/authorized_keys
&lt;span class=&quot;gp&quot;&gt;#&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;chmod &lt;/span&gt;600 ~mcp/.ssh/authorized_keys
&lt;span class=&quot;gp&quot;&gt;#&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;chown&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;-R&lt;/span&gt; mcp: ~mcp/.ssh
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;That’s now done and only the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&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 class=&quot;language-plaintext highlighter-rouge&quot;&gt;releases&lt;/code&gt; directory that will contain timestamped versions of your application code;&lt;/li&gt;
  &lt;li&gt;A &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;shared&lt;/code&gt; directory that persists across deploys making it ideal for storing configuration like your &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&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 class=&quot;language-plaintext highlighter-rouge&quot;&gt;current&lt;/code&gt; which points to the currently active version of your code within the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&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 class=&quot;language-plaintext highlighter-rouge&quot;&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 class=&quot;language-plaintext highlighter-rouge&quot;&gt;mcp&lt;/code&gt; user’s home directory…&lt;/p&gt;

&lt;div class=&quot;language-console highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;gp&quot;&gt;#&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;mkdir&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;-p&lt;/span&gt; ~mcp/apps/mcp/shared/config
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;As we’re doing everything as &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;root&lt;/code&gt;, we just need to make sure everything is owned by the right user…&lt;/p&gt;

&lt;div class=&quot;language-console highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;gp&quot;&gt;#&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;chown&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;-R&lt;/span&gt; mcp: ~mcp/apps
&lt;/code&gt;&lt;/pre&gt;&lt;/div&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 class=&quot;language-plaintext highlighter-rouge&quot;&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=&quot;language-console highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;gp&quot;&gt;#&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;cd&lt;/span&gt; ~mcp/apps/mcp/shared/config
&lt;span class=&quot;gp&quot;&gt;#&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;vi database.yml
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Again, make sure that the user is correct.&lt;/p&gt;

&lt;div class=&quot;language-console highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;gp&quot;&gt;#&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;chown &lt;/span&gt;mcp: database.yml
&lt;/code&gt;&lt;/pre&gt;&lt;/div&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=&quot;http://rvm.beginrescueend.com/&quot;&gt;RVM&lt;/a&gt; to manage Ruby for us &lt;em&gt;[NB: this predates the release of &lt;a href=&quot;http://www.rubyinside.com/rbenv-a-simple-new-ruby-version-management-tool-5302.html&quot;&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 class=&quot;language-plaintext highlighter-rouge&quot;&gt;apt-get&lt;/code&gt; to install the required packages…&lt;/p&gt;

&lt;div class=&quot;language-console highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;gp&quot;&gt;#&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;apt-get &lt;span class=&quot;nb&quot;&gt;install &lt;/span&gt;curl git-core subversion
&lt;span class=&quot;go&quot;&gt;Reading package lists... Done
Building dependency tree
Reading state information... Done
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&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=&quot;language-console highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;gp&quot;&gt;#&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;cd&lt;/span&gt; /root
&lt;span class=&quot;gp&quot;&gt;#&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;curl &lt;span class=&quot;nt&quot;&gt;-s&lt;/span&gt; &lt;span class=&quot;se&quot;&gt;\&lt;/span&gt;
&lt;span class=&quot;o&quot;&gt;&amp;gt;&lt;/span&gt; https://rvm.beginrescueend.com/install/rvm &lt;span class=&quot;se&quot;&gt;\&lt;/span&gt;
&lt;span class=&quot;o&quot;&gt;&amp;gt;&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;-o&lt;/span&gt; rvm-installer
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Making it executable…&lt;/p&gt;

&lt;div class=&quot;language-console highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;gp&quot;&gt;#&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;chmod&lt;/span&gt; +x rvm-installer
&lt;/code&gt;&lt;/pre&gt;&lt;/div&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=&quot;language-console highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;gp&quot;&gt;#&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;./rvm-installer &lt;span class=&quot;nt&quot;&gt;--version&lt;/span&gt; 1.6.32
&lt;span class=&quot;go&quot;&gt;Installation of RVM to /usr/local/rvm is
complete.
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;As we are running as &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;root&lt;/code&gt;, everything will be installed to &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&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 class=&quot;language-plaintext highlighter-rouge&quot;&gt;rvm notes&lt;/code&gt; on your machine)…&lt;/p&gt;

&lt;div class=&quot;language-console highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;gp&quot;&gt;#&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;apt-get &lt;span class=&quot;nb&quot;&gt;install &lt;/span&gt;build-essential bison &lt;span class=&quot;se&quot;&gt;\&lt;/span&gt;
&lt;span class=&quot;o&quot;&gt;&amp;gt;&lt;/span&gt; openssl libreadline6 libreadline6-dev &lt;span class=&quot;se&quot;&gt;\&lt;/span&gt;
&lt;span class=&quot;o&quot;&gt;&amp;gt;&lt;/span&gt; zlib1g zlib1g-dev libssl-dev libyaml-dev &lt;span class=&quot;se&quot;&gt;\&lt;/span&gt;
&lt;span class=&quot;o&quot;&gt;&amp;gt;&lt;/span&gt; libsqlite3-0 libsqlite3-dev sqlite3 &lt;span class=&quot;se&quot;&gt;\&lt;/span&gt;
&lt;span class=&quot;o&quot;&gt;&amp;gt;&lt;/span&gt; libxml2-dev libxslt-dev autoconf &lt;span class=&quot;se&quot;&gt;\&lt;/span&gt;
&lt;span class=&quot;o&quot;&gt;&amp;gt;&lt;/span&gt; libc6-dev ncurses-dev libcurl4-openssl-dev
&lt;span class=&quot;go&quot;&gt;Reading package lists... Done
Building dependency tree
Reading state information... Done
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&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=&quot;language-console highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;gp&quot;&gt;#&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;rvm &lt;span class=&quot;nb&quot;&gt;install &lt;/span&gt;1.9.2-p290
&lt;span class=&quot;go&quot;&gt;Installing Ruby from source to:
/usr/local/rvm/rubies/ruby-1.9.2-p290, this
may take a while depending on your cpu(s)...
&lt;/span&gt;&lt;span class=&quot;gp&quot;&gt;Install of ruby-1.9.2-p290 - #&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;complete&lt;/span&gt; 
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;The final piece of the puzzle is our web server: we like to use &lt;a href=&quot;http://nginx.org/&quot;&gt;nginx&lt;/a&gt; and &lt;a href=&quot;http://www.modrails.com/&quot;&gt;Passenger&lt;/a&gt; to serve our applications so let’s install and configure them. First we’ll need to &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;source&lt;/code&gt; RVM in order to switch environments…&lt;/p&gt;

&lt;div class=&quot;language-console highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;gp&quot;&gt;#&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;source&lt;/span&gt; /usr/local/rvm/scripts/rvm
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Now we can choose Ruby 1.9.2…&lt;/p&gt;

&lt;div class=&quot;language-console highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;gp&quot;&gt;#&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;rvm use 1.9.2-p290
&lt;span class=&quot;go&quot;&gt;Using /usr/local/rvm/gems/ruby-1.9.2-p290
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&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=&quot;language-console highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;gp&quot;&gt;#&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;gem &lt;span class=&quot;nb&quot;&gt;install &lt;/span&gt;passenger &lt;span class=&quot;nt&quot;&gt;-v3&lt;/span&gt;.0.8
&lt;span class=&quot;go&quot;&gt;Fetching: fastthread-1.0.7.gem (100%)
Building native extensions.  This could
take a while...
Fetching: daemon_controller-0.2.6.gem (100%)
Fetching: rack-1.3.2.gem (100%)
&lt;/span&gt;&lt;span class=&quot;c&quot;&gt;...
&lt;/span&gt;&lt;span class=&quot;go&quot;&gt;Successfully installed passenger-3.0.8
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Then we can use the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;passenger-install-nginx-module&lt;/code&gt; command to actually compile an nginx for us.&lt;/p&gt;

&lt;div class=&quot;language-console highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;gp&quot;&gt;#&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;passenger-install-nginx-module
&lt;span class=&quot;go&quot;&gt;Welcome to the Phusion Passenger Nginx
module installer, v3.0.8.
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Then after following the installation wizard, you will have a complete, compiled nginx in &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&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 class=&quot;language-plaintext highlighter-rouge&quot;&gt;sites_available&lt;/code&gt;, with all possible application configuration;&lt;/li&gt;
  &lt;li&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;sites_enabled&lt;/code&gt;, with only enabled applications.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The main &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;nginx.conf&lt;/code&gt; is then instructed to include any configuration found in &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;sites_enabled&lt;/code&gt; and that is merely a collection of symbolic links to configuration in &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;sites_available&lt;/code&gt;.&lt;/p&gt;

&lt;div class=&quot;language-console highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;gp&quot;&gt;#&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;cd&lt;/span&gt; /opt/nginx/conf
&lt;span class=&quot;gp&quot;&gt;#&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;mkdir &lt;/span&gt;sites_available sites_enabled
&lt;span class=&quot;gp&quot;&gt;#&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;vi nginx.conf
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Now we just need to put our application configuration in &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;sites_available&lt;/code&gt; and link to it from &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&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=&quot;language-console highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;gp&quot;&gt;#&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;cd &lt;/span&gt;sites_available
&lt;span class=&quot;gp&quot;&gt;#&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;vi mcp.conf
&lt;span class=&quot;gp&quot;&gt;#&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;ln&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;-s&lt;/span&gt; &lt;span class=&quot;se&quot;&gt;\&lt;/span&gt;
&lt;span class=&quot;o&quot;&gt;&amp;gt;&lt;/span&gt; /opt/nginx/conf/sites_available/mcp.conf &lt;span class=&quot;se&quot;&gt;\&lt;/span&gt;
&lt;span class=&quot;o&quot;&gt;&amp;gt;&lt;/span&gt; /opt/nginx/conf/sites_enabled/mcp.conf
&lt;/code&gt;&lt;/pre&gt;&lt;/div&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 class=&quot;language-plaintext highlighter-rouge&quot;&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=&quot;language-console highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;gp&quot;&gt;#&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;vi /etc/init.d/nginx
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Make it executable…&lt;/p&gt;

&lt;div class=&quot;language-console highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;gp&quot;&gt;#&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;chmod&lt;/span&gt; +x /etc/init.d/nginx
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;As we are using Debian, we need to use &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;update-rc.d&lt;/code&gt; to start nginx up on boot…&lt;/p&gt;

&lt;div class=&quot;language-console highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;gp&quot;&gt;#&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;update-rc.d &lt;span class=&quot;nt&quot;&gt;-f&lt;/span&gt; nginx defaults
&lt;/code&gt;&lt;/pre&gt;&lt;/div&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=&quot;language-console highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;gp&quot;&gt;#&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;/etc/init.d/nginx start
&lt;span class=&quot;go&quot;&gt;Starting nginx: nginx
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&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 class=&quot;language-plaintext highlighter-rouge&quot;&gt;deploy:setup&lt;/code&gt; to create the directories we need…&lt;/p&gt;

&lt;div class=&quot;language-console highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;gp&quot;&gt;$&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;cap deploy:setup
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Do a quick &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;check&lt;/code&gt; to make sure everything is OK…&lt;/p&gt;

&lt;div class=&quot;language-console highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;gp&quot;&gt;$&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;cap deploy:check
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;And then do our big first &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;deploy&lt;/code&gt; (hoping that everything will work first time).&lt;/p&gt;

&lt;div class=&quot;language-console highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;gp&quot;&gt;$&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;cap deploy:cold
&lt;/code&gt;&lt;/pre&gt;&lt;/div&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 class=&quot;language-plaintext highlighter-rouge&quot;&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 class=&quot;language-plaintext highlighter-rouge&quot;&gt;adduser&lt;/code&gt; as it differs from platform to platform. When I installed the dependencies for RVM, I used &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;apt-get&lt;/code&gt;; when I wanted to start nginx up on boot, I used &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&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 class=&quot;language-plaintext highlighter-rouge&quot;&gt;yum&lt;/code&gt; and &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;chkconfig&lt;/code&gt; instead? More importantly, why do I have to care what the exact flags to pass to &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&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 class=&quot;language-plaintext highlighter-rouge&quot;&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=&quot;enter-configuration-management&quot;&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=&quot;http://cukes.info/&quot;&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=&quot;http://skillsmatter.com/podcast/home/chef-vagrant&quot;&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=&quot;language-ruby highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;n&quot;&gt;user&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;'henry'&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;
  &lt;span class=&quot;k&quot;&gt;ensure&lt;/span&gt;     &lt;span class=&quot;o&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;present&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
  &lt;span class=&quot;n&quot;&gt;uid&lt;/span&gt;        &lt;span class=&quot;o&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;'507'&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
  &lt;span class=&quot;n&quot;&gt;gid&lt;/span&gt;        &lt;span class=&quot;o&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;'staff'&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
  &lt;span class=&quot;n&quot;&gt;shell&lt;/span&gt;      &lt;span class=&quot;o&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;'/bin/zsh'&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
  &lt;span class=&quot;n&quot;&gt;home&lt;/span&gt;       &lt;span class=&quot;o&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;'/home/henry'&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
  &lt;span class=&quot;n&quot;&gt;managehome&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;kp&quot;&gt;true&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/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=&quot;http://puppetlabs.com/blog/ruby-dsl/&quot;&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 class=&quot;language-plaintext highlighter-rouge&quot;&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 class=&quot;language-plaintext highlighter-rouge&quot;&gt;present&lt;/code&gt; to &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&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 class=&quot;language-plaintext highlighter-rouge&quot;&gt;henry.pp&lt;/code&gt; and then run the following command…&lt;/p&gt;

&lt;div class=&quot;language-console highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;gp&quot;&gt;$&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;sudo &lt;/span&gt;puppet apply henry.pp
&lt;/code&gt;&lt;/pre&gt;&lt;/div&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=&quot;language-console highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;go&quot;&gt;notice:
/Stage[main]//User[henry]/ensure: created
notice: Finished catalog run in 0.25 seconds
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;We can verify that everything has run successfully by having a look in &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;/etc/passwd&lt;/code&gt;…&lt;/p&gt;

&lt;div class=&quot;language-console highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;gp&quot;&gt;$&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;grep &lt;/span&gt;henry /etc/passwd
&lt;span class=&quot;go&quot;&gt;henry:x:507:50::/home/henry:/bin/zsh
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&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=&quot;language-console highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;gp&quot;&gt;$&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;sudo &lt;/span&gt;chsh henry
&lt;span class=&quot;go&quot;&gt;Changing shell for henry.
New shell [/bin/zsh]: /bin/bash
Shell changed.
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&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=&quot;language-console highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;gp&quot;&gt;$&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;sudo &lt;/span&gt;puppet apply henry.pp
&lt;span class=&quot;go&quot;&gt;notice:
/Stage[main]//User[henry]/shell:
shell changed '/bin/bash' to '/bin/zsh'
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&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 class=&quot;language-plaintext highlighter-rouge&quot;&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=&quot;language-ruby highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;n&quot;&gt;package&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;'openssh-server'&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;
  &lt;span class=&quot;k&quot;&gt;ensure&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;installed&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;A &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;package&lt;/code&gt; resource is simply a software package that you might install via &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;apt-get&lt;/code&gt; on Debian, &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;yum&lt;/code&gt; on RedHat, etc.&lt;/p&gt;

&lt;div class=&quot;language-ruby highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;n&quot;&gt;file&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;'/etc/sudoers'&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;
  &lt;span class=&quot;k&quot;&gt;ensure&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;present&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;The &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&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 class=&quot;language-plaintext highlighter-rouge&quot;&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=&quot;language-ruby highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;n&quot;&gt;service&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;'sshd'&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;
  &lt;span class=&quot;k&quot;&gt;ensure&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;running&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;The &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;service&lt;/code&gt; resource describes long running processes like those you would manage with an &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;init.d&lt;/code&gt; script; in this example, it just makes sure the process labelled &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;sshd&lt;/code&gt; is running.&lt;/p&gt;

&lt;p&gt;I’ve shown examples using &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&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=&quot;the-better-way&quot;&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 class=&quot;language-plaintext highlighter-rouge&quot;&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 class=&quot;language-plaintext highlighter-rouge&quot;&gt;mcp&lt;/code&gt; and then a user named &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&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=&quot;language-ruby highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;n&quot;&gt;group&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;'mcp'&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;
  &lt;span class=&quot;k&quot;&gt;ensure&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;present&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

&lt;span class=&quot;n&quot;&gt;user&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;'mcp'&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;
  &lt;span class=&quot;k&quot;&gt;ensure&lt;/span&gt;     &lt;span class=&quot;o&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;present&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
  &lt;span class=&quot;n&quot;&gt;gid&lt;/span&gt;        &lt;span class=&quot;o&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;'mcp'&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
  &lt;span class=&quot;n&quot;&gt;home&lt;/span&gt;       &lt;span class=&quot;o&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;'/home/mcp'&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
  &lt;span class=&quot;n&quot;&gt;managehome&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;kp&quot;&gt;true&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/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 class=&quot;language-plaintext highlighter-rouge&quot;&gt;group&lt;/code&gt;:&lt;/p&gt;

&lt;div class=&quot;language-ruby highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;n&quot;&gt;group&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;'mcp'&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;
  &lt;span class=&quot;k&quot;&gt;ensure&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;present&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;But if you then use title case such as &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Group&lt;/code&gt;:&lt;/p&gt;

&lt;div class=&quot;language-ruby highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;no&quot;&gt;Group&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;'mcp'&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&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=&quot;language-ruby highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;n&quot;&gt;user&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;'mcp'&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;
  &lt;span class=&quot;k&quot;&gt;ensure&lt;/span&gt;  &lt;span class=&quot;o&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;present&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
  &lt;span class=&quot;nb&quot;&gt;require&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;Group&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;'mcp'&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;],&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;This explictly states that the user &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;mcp&lt;/code&gt; will not be checked until the &lt;em&gt;group&lt;/em&gt; &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&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=&quot;language-ruby highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;n&quot;&gt;user&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;'mcp'&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;
  &lt;span class=&quot;k&quot;&gt;ensure&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;present&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
  &lt;span class=&quot;nb&quot;&gt;require&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;Group&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;'mcp'&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;],&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

&lt;span class=&quot;n&quot;&gt;group&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;'mcp'&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;
  &lt;span class=&quot;k&quot;&gt;ensure&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;present&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
  &lt;span class=&quot;nb&quot;&gt;require&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;User&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;'mcp'&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;],&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Puppet will actually spot the issue when you attempt to run it:&lt;/p&gt;

&lt;div class=&quot;language-console highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;go&quot;&gt;err: Could not apply complete catalog:
Found dependency cycles in the following
relationships:
&lt;/span&gt;&lt;span class=&quot;gp&quot;&gt;User[mcp] =&amp;gt;&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;Group[mcp], Group[mcp] &lt;span class=&quot;o&quot;&gt;=&amp;gt;&lt;/span&gt; User[mcp]&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;try using the '--graph' option and open the '.dot'
files in OmniGraffle or GraphViz
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;This is because Puppet builds a &lt;a href=&quot;http://en.wikipedia.org/wiki/Directed_acyclic_graph&quot;&gt;directed acyclic graph&lt;/a&gt; of your resources and can actually output the whole thing as a &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&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 class=&quot;language-plaintext highlighter-rouge&quot;&gt;.ssh&lt;/code&gt; folder, add our key to &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&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 class=&quot;language-plaintext highlighter-rouge&quot;&gt;ssh_authorized_key&lt;/code&gt; resource type:&lt;/p&gt;

&lt;div class=&quot;language-ruby highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;n&quot;&gt;ssh_authorized_key&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;'mcp-mudge'&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;
  &lt;span class=&quot;k&quot;&gt;ensure&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;present&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
  &lt;span class=&quot;n&quot;&gt;key&lt;/span&gt;    &lt;span class=&quot;o&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;'AAAAB3NzaC1yc2EAAAAB...'&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
  &lt;span class=&quot;n&quot;&gt;type&lt;/span&gt;   &lt;span class=&quot;o&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;dsa&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
  &lt;span class=&quot;n&quot;&gt;user&lt;/span&gt;   &lt;span class=&quot;o&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;'mcp'&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;As for the directory structure: we created &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;apps&lt;/code&gt;, &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;apps/mcp&lt;/code&gt;, &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;apps/mcp/shared&lt;/code&gt; and &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;apps/mcp/shared/config&lt;/code&gt; so let’s do the same using the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;file&lt;/code&gt; resource:&lt;/p&gt;

&lt;div class=&quot;language-ruby highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;n&quot;&gt;file&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;s1&quot;&gt;'/home/mcp/apps'&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;ensure&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;directory&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;owner&lt;/span&gt;  &lt;span class=&quot;o&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;'mcp'&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;group&lt;/span&gt;  &lt;span class=&quot;o&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;'mcp'&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;

  &lt;span class=&quot;s1&quot;&gt;'/home/mcp/apps/mcp'&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;ensure&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;directory&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;owner&lt;/span&gt;  &lt;span class=&quot;o&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;'mcp'&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;group&lt;/span&gt;  &lt;span class=&quot;o&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;'mcp'&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;

  &lt;span class=&quot;s1&quot;&gt;'/home/mcp/apps/mcp/shared'&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;ensure&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;directory&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;owner&lt;/span&gt;  &lt;span class=&quot;o&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;'mcp'&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;group&lt;/span&gt;  &lt;span class=&quot;o&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;'mcp'&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;

  &lt;span class=&quot;s1&quot;&gt;'/home/mcp/apps/mcp/shared/config'&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;ensure&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;directory&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;owner&lt;/span&gt;  &lt;span class=&quot;o&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;'mcp'&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;group&lt;/span&gt;  &lt;span class=&quot;o&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;'mcp'&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;This makes sure that each path is a directory owned by &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&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=&quot;language-ruby highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;n&quot;&gt;file&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;'/home/mcp/apps/mcp/shared/config/database.yml'&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;
  &lt;span class=&quot;k&quot;&gt;ensure&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;present&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
  &lt;span class=&quot;n&quot;&gt;owner&lt;/span&gt;  &lt;span class=&quot;o&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;'mcp'&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
  &lt;span class=&quot;n&quot;&gt;group&lt;/span&gt;  &lt;span class=&quot;o&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;'mcp'&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
  &lt;span class=&quot;n&quot;&gt;source&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;'puppet:///modules/mcp/database.yml'&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;The first part is clear enough but the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&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 class=&quot;language-plaintext highlighter-rouge&quot;&gt;database.yml&lt;/code&gt; to hand.&lt;/p&gt;

&lt;p&gt;However, the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&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=&quot;language-ruby highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;n&quot;&gt;file&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;'/some/config.yml'&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;
  &lt;span class=&quot;n&quot;&gt;source&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;
    &lt;span class=&quot;s1&quot;&gt;'puppet:///confidential/config.yml'&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
    &lt;span class=&quot;s1&quot;&gt;'puppet:///modules/mcp/config.yml'&lt;/span&gt;
  &lt;span class=&quot;p&quot;&gt;],&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;When Puppet attempts to create this file, it will check each path in the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;source&lt;/code&gt; list until it finds one it can access. In this way, you can have a &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&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 class=&quot;language-plaintext highlighter-rouge&quot;&gt;source&lt;/code&gt; paths like so:&lt;/p&gt;

&lt;div class=&quot;language-ruby highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;n&quot;&gt;file&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;'/some/config.yml'&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;
  &lt;span class=&quot;n&quot;&gt;source&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;
    &lt;span class=&quot;s2&quot;&gt;&quot;puppet:///confidential/config.yml.$hostname&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
    &lt;span class=&quot;s2&quot;&gt;&quot;puppet:///confidential/config.yml.$tier&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
    &lt;span class=&quot;s1&quot;&gt;'puppet:///modules/mcp/config.yml'&lt;/span&gt;
  &lt;span class=&quot;p&quot;&gt;],&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;As &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;$hostname&lt;/code&gt; will be the hostname of your server (e.g. &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;webserver1&lt;/code&gt;) then you can have a file such as &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&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 class=&quot;language-plaintext highlighter-rouge&quot;&gt;config.yml.$tier&lt;/code&gt; and &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;$tier&lt;/code&gt; can be set as follows:&lt;/p&gt;

&lt;div class=&quot;language-ruby highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;vg&quot;&gt;$tier&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;'test'&lt;/span&gt;
&lt;span class=&quot;c1&quot;&gt;# =&amp;gt; &quot;puppet:///confidential/config.yml.test&quot;&lt;/span&gt;

&lt;span class=&quot;vg&quot;&gt;$tier&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;'staging'&lt;/span&gt;
&lt;span class=&quot;c1&quot;&gt;# =&amp;gt; &quot;puppet:///confidential/config.yml.staging&quot;&lt;/span&gt;

&lt;span class=&quot;vg&quot;&gt;$tier&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;'live'&lt;/span&gt;
&lt;span class=&quot;c1&quot;&gt;# =&amp;gt; &quot;puppet:///confidential/config.yml.live&quot;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&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 class=&quot;language-plaintext highlighter-rouge&quot;&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=&quot;language-ruby highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;vg&quot;&gt;$db_username&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;'bob'&lt;/span&gt;
&lt;span class=&quot;vg&quot;&gt;$db_password&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;'letmein'&lt;/span&gt;

&lt;span class=&quot;n&quot;&gt;file&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;'/some/database.yml'&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;
  &lt;span class=&quot;n&quot;&gt;content&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;template&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;'mcp/database.yml.erb'&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;),&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/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 class=&quot;language-plaintext highlighter-rouge&quot;&gt;source&lt;/code&gt; parameter, Puppet supports specifying the exact contents of a file as a string with &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;content&lt;/code&gt; and, if you use the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;template&lt;/code&gt; function, you can actually process an entire &lt;a href=&quot;http://www.ruby-doc.org/stdlib/libdoc/erb/rdoc/&quot;&gt;ERB&lt;/a&gt; template just like you might be used to in Rails. In this case, our &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;database.yml.erb&lt;/code&gt; might look like this:&lt;/p&gt;

&lt;div class=&quot;language-erb highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;production:
  adapter: mysql
  username: &lt;span class=&quot;cp&quot;&gt;&amp;lt;%=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;db_username&lt;/span&gt; &lt;span class=&quot;cp&quot;&gt;%&amp;gt;&lt;/span&gt;
  password: &lt;span class=&quot;cp&quot;&gt;&amp;lt;%=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;db_password&lt;/span&gt; &lt;span class=&quot;cp&quot;&gt;%&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;This means that I can be free to change the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&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=&quot;http://www.devco.net/archives/2009/08/31/complex_data_and_puppet.php&quot;&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;extlookup&lt;/code&gt;&lt;/a&gt;, &lt;a href=&quot;http://docs.puppetlabs.com/guides/external_nodes.html&quot;&gt;External Node Classifiers&lt;/a&gt;, etc.) and Chef has a built-in concept called &lt;a href=&quot;http://wiki.opscode.com/display/chef/Encrypted+Data+Bags&quot;&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=&quot;http://projects.puppetlabs.com/projects/hiera&quot;&gt;Hiera&lt;/a&gt; along with an encrypted backend such as &lt;a href=&quot;https://github.com/crayfishx/hiera-gpg&quot;&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=&quot;http://rubygems.org/gems/hiera&quot;&gt;Hiera&lt;/a&gt; and &lt;a href=&quot;http://rubygems.org/gems/hiera-puppet&quot;&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 class=&quot;language-plaintext highlighter-rouge&quot;&gt;package&lt;/code&gt; to do so:&lt;/p&gt;

&lt;div class=&quot;language-ruby highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;n&quot;&gt;package&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;'rvm-dependencies'&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;
  &lt;span class=&quot;k&quot;&gt;ensure&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;installed&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
  &lt;span class=&quot;nb&quot;&gt;name&lt;/span&gt;   &lt;span class=&quot;o&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;
    &lt;span class=&quot;s1&quot;&gt;'curl'&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
    &lt;span class=&quot;s1&quot;&gt;'git-core'&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
    &lt;span class=&quot;s1&quot;&gt;'subversion'&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
    &lt;span class=&quot;s1&quot;&gt;'build-essential'&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
    &lt;span class=&quot;o&quot;&gt;...&lt;/span&gt;
  &lt;span class=&quot;p&quot;&gt;],&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/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 class=&quot;language-plaintext highlighter-rouge&quot;&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=&quot;language-ruby highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;n&quot;&gt;file&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;'/root/rvm-installer'&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;
  &lt;span class=&quot;k&quot;&gt;ensure&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;present&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
  &lt;span class=&quot;n&quot;&gt;owner&lt;/span&gt;  &lt;span class=&quot;o&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;'root'&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
  &lt;span class=&quot;n&quot;&gt;group&lt;/span&gt;  &lt;span class=&quot;o&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;'root'&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
  &lt;span class=&quot;n&quot;&gt;mode&lt;/span&gt;   &lt;span class=&quot;o&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;'0700'&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
  &lt;span class=&quot;n&quot;&gt;source&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;'puppet:///modules/mcp/rvm'&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Now we need to run the installer and, for that, we can use the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&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=&quot;language-ruby highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;nb&quot;&gt;exec&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;'install-rvm'&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;
  &lt;span class=&quot;n&quot;&gt;command&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;'/root/rvm-installer --version 1.6.32'&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
  &lt;span class=&quot;n&quot;&gt;cwd&lt;/span&gt;     &lt;span class=&quot;o&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;'/root'&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
  &lt;span class=&quot;k&quot;&gt;unless&lt;/span&gt;  &lt;span class=&quot;o&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;'grep 1.6.32 /usr/local/rvm/VERSION'&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
  &lt;span class=&quot;n&quot;&gt;path&lt;/span&gt;    &lt;span class=&quot;o&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;'/usr/bin:/usr/sbin:/bin:/sbin'&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
  &lt;span class=&quot;nb&quot;&gt;require&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;Package&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;'rvm-dependencies'&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;],&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;You can see the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;command&lt;/code&gt; to run and the directory to run it from in &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&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 class=&quot;language-plaintext highlighter-rouge&quot;&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 class=&quot;language-plaintext highlighter-rouge&quot;&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 class=&quot;language-plaintext highlighter-rouge&quot;&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 class=&quot;language-plaintext highlighter-rouge&quot;&gt;rvm install ruby&lt;/code&gt; in much the same way:&lt;/p&gt;

&lt;div class=&quot;language-ruby highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;nb&quot;&gt;exec&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;'rvm install ruby-1.9.2-p290'&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;
  &lt;span class=&quot;n&quot;&gt;creates&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;'/usr/local/rvm/rubies/ruby-1.9.2-p290'&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
  &lt;span class=&quot;n&quot;&gt;timeout&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;1800&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
  &lt;span class=&quot;n&quot;&gt;path&lt;/span&gt;    &lt;span class=&quot;o&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;'/usr/local/rvm/bin:/usr/bin:/usr/sbin:/bin:/sbin'&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
  &lt;span class=&quot;nb&quot;&gt;require&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;File&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;'install-rvm'&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;],&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Instead of using &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;unless&lt;/code&gt; which runs any arbitrary command and checks its exit code, we can use &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;creates&lt;/code&gt; which checks for the presence of a file. This resource will therefore only be installed if &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&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 class=&quot;language-plaintext highlighter-rouge&quot;&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 class=&quot;language-plaintext highlighter-rouge&quot;&gt;exec&lt;/code&gt; resource.&lt;/p&gt;

&lt;div class=&quot;language-ruby highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;nb&quot;&gt;exec&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;'install-passenger-3.0.8'&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;
  &lt;span class=&quot;n&quot;&gt;command&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;'rvm-shell ruby-1.9.2-p290 -c &quot;gem install passenger -v3.0.8&quot;'&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
  &lt;span class=&quot;k&quot;&gt;unless&lt;/span&gt;  &lt;span class=&quot;o&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;'rvm-shell ruby-1.9.2-p290 -c &quot;gem list passenger -v3.0.8 -i&quot;'&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
  &lt;span class=&quot;n&quot;&gt;path&lt;/span&gt;    &lt;span class=&quot;o&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;'/usr/local/rvm/bin:/usr/bin:/usr/sbin:/bin:/sbin'&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
  &lt;span class=&quot;n&quot;&gt;timeout&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;1800&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
  &lt;span class=&quot;nb&quot;&gt;require&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;Exec&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;'rvm install ruby-1.9.2-p290'&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;],&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/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 class=&quot;language-plaintext highlighter-rouge&quot;&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=&quot;language-ruby highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;nb&quot;&gt;exec&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;'install-nginx'&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;
  &lt;span class=&quot;n&quot;&gt;command&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;'rvm-shell ruby-1.9.2-p290 -c &quot;passenger-install-nginx-module...&quot;'&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
  &lt;span class=&quot;n&quot;&gt;creates&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;'...agents/nginx/PassengerHelperAgent'&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
  &lt;span class=&quot;n&quot;&gt;timeout&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;1800&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
  &lt;span class=&quot;n&quot;&gt;path&lt;/span&gt;    &lt;span class=&quot;o&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;'/usr/local/rvm/bin:/usr/bin:/usr/sbin:/bin:/sbin'&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
  &lt;span class=&quot;nb&quot;&gt;require&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;Exec&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;'install-passenger-3.0.8'&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;],&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;From our experience, we know that when Passenger and nginx compile successfully, it results in &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&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=&quot;language-console highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;gp&quot;&gt;$&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;rvm-shell ruby-1.9.2-p290 &lt;span class=&quot;nt&quot;&gt;-c&lt;/span&gt; &lt;span class=&quot;se&quot;&gt;\&lt;/span&gt;
&lt;span class=&quot;o&quot;&gt;&amp;gt;&lt;/span&gt;  &lt;span class=&quot;s2&quot;&gt;&quot;passenger-install-nginx-module --auto &lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;
&amp;gt;  --auto-download &lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;
&amp;gt;  --prefix=/opt/nginx&quot;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&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 class=&quot;language-plaintext highlighter-rouge&quot;&gt;file&lt;/code&gt; resource:&lt;/p&gt;

&lt;div class=&quot;language-ruby highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;n&quot;&gt;file&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;s1&quot;&gt;'/opt/nginx/conf/sites_available/mcp.conf'&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;ensure&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;present&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;owner&lt;/span&gt;  &lt;span class=&quot;o&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;'root'&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;group&lt;/span&gt;  &lt;span class=&quot;o&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;'root'&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;source&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;'puppet:///modules/mcp/mcp.conf'&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;

  &lt;span class=&quot;s1&quot;&gt;'/opt/nginx/conf/sites_enabled/mcp.conf'&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;ensure&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;link&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;owner&lt;/span&gt;  &lt;span class=&quot;o&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;'root'&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;group&lt;/span&gt;  &lt;span class=&quot;o&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;'root'&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;target&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;'/opt/nginx/conf/sites_available/mcp.conf'&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/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 class=&quot;language-plaintext highlighter-rouge&quot;&gt;link&lt;/code&gt; to &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;absent&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Then we’ve got our &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;init.d&lt;/code&gt; script which we just copy:&lt;/p&gt;

&lt;div class=&quot;language-ruby highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;n&quot;&gt;file&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;'/etc/init.d/nginx'&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;
  &lt;span class=&quot;k&quot;&gt;ensure&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;present&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
  &lt;span class=&quot;n&quot;&gt;owner&lt;/span&gt;  &lt;span class=&quot;o&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;'root'&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
  &lt;span class=&quot;n&quot;&gt;group&lt;/span&gt;  &lt;span class=&quot;o&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;'root'&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
  &lt;span class=&quot;n&quot;&gt;mode&lt;/span&gt;   &lt;span class=&quot;o&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;'0755'&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
  &lt;span class=&quot;n&quot;&gt;source&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;'puppet:///modules/mcp/nginx'&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
  &lt;span class=&quot;nb&quot;&gt;require&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;Exec&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;'install-nginx'&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;];&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Finally, we used &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;update-rc.d&lt;/code&gt; to register our service previously but we can now use the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;service&lt;/code&gt; resource type to do that for us:&lt;/p&gt;

&lt;div class=&quot;language-ruby highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;n&quot;&gt;service&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;'nginx'&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;
  &lt;span class=&quot;k&quot;&gt;ensure&lt;/span&gt;     &lt;span class=&quot;o&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;running&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
  &lt;span class=&quot;n&quot;&gt;enable&lt;/span&gt;     &lt;span class=&quot;o&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;kp&quot;&gt;true&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
  &lt;span class=&quot;n&quot;&gt;hasrestart&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;kp&quot;&gt;true&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
  &lt;span class=&quot;n&quot;&gt;hasstatus&lt;/span&gt;  &lt;span class=&quot;o&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;kp&quot;&gt;true&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
  &lt;span class=&quot;n&quot;&gt;subscribe&lt;/span&gt;  &lt;span class=&quot;o&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;File&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;'/opt/nginx/conf/nginx.conf'&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;],&lt;/span&gt;
  &lt;span class=&quot;nb&quot;&gt;require&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;
    &lt;span class=&quot;no&quot;&gt;File&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;'/opt/nginx/conf/nginx.conf'&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;],&lt;/span&gt;
    &lt;span class=&quot;no&quot;&gt;File&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;'/etc/init.d/nginx'&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt;
  &lt;span class=&quot;p&quot;&gt;],&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/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 class=&quot;language-plaintext highlighter-rouge&quot;&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 class=&quot;language-plaintext highlighter-rouge&quot;&gt;init.d&lt;/code&gt; scripts. You can also see a &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&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=&quot;testing&quot;&gt;Testing&lt;/h2&gt;

&lt;p&gt;Puppet is obviously very powerful and, as with anything that runs as &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&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 class=&quot;language-plaintext highlighter-rouge&quot;&gt;--noop&lt;/code&gt; flag you can use with &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;puppet apply&lt;/code&gt; that will effectively do a dry-run of your changes. If you are using the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&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 class=&quot;language-plaintext highlighter-rouge&quot;&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=&quot;http://vagrantup.com/&quot;&gt;Vagrant&lt;/a&gt;. If you’re not familiar with it, Vagrant is a RubyGem for managing virtual machines using &lt;a href=&quot;http://www.virtualbox.org/&quot;&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 class=&quot;language-plaintext highlighter-rouge&quot;&gt;Vagrantfile&lt;/code&gt; to point it at your modules and manifests:&lt;/p&gt;

&lt;div class=&quot;language-ruby highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;n&quot;&gt;config&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;vm&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;provision&lt;/span&gt; &lt;span class=&quot;ss&quot;&gt;:puppet&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;do&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;|&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;puppet&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;|&lt;/span&gt;
  &lt;span class=&quot;n&quot;&gt;puppet&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;module_path&lt;/span&gt;    &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;modules&quot;&lt;/span&gt;
  &lt;span class=&quot;n&quot;&gt;puppet&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;manifests_path&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;manifests&quot;&lt;/span&gt;
  &lt;span class=&quot;n&quot;&gt;puppet&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;manifest_file&lt;/span&gt;  &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;mcp.pp&quot;&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Then it is simply a case of bringing up your virtual machine:&lt;/p&gt;

&lt;div class=&quot;language-console highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;gp&quot;&gt;$&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;bundle &lt;span class=&quot;nb&quot;&gt;exec &lt;/span&gt;vagrant up
&lt;span class=&quot;go&quot;&gt;[default] Box natty was not found. Fetching box...
[default] Downloading with Vagrant::Downloaders::HTTP...
[default] Downloading box: http://mudge.name/downloads...
[default] Extracting box...
[default] Verifying box...
[default] Cleaning up downloaded box...
[default] Importing base box 'natty'...
[default] Matching MAC address for NAT networking...
[default] Clearing any previously set forwarded ports...
[default] Forwarding ports...
&lt;/span&gt;&lt;span class=&quot;gp&quot;&gt;[default] -- ssh: 22 =&amp;gt;&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;2222 &lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;adapter 1&lt;span class=&quot;o&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;[default] Creating shared folders metadata...
[default] Running any VM customizations...
[default] Booting VM...
[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;/div&gt;

&lt;p&gt;The first time you run it, Vagrant will inspect your &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&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=&quot;language-console highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;gp&quot;&gt;$&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;bundle &lt;span class=&quot;nb&quot;&gt;exec &lt;/span&gt;vagrant provision
&lt;span class=&quot;go&quot;&gt;[default] Running provisioner: Vagrant::Provisioners::Puppet...
[default] Running Puppet with mcp.pp...
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&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 class=&quot;language-plaintext highlighter-rouge&quot;&gt;Gemfile&lt;/code&gt;, &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Vagrantfile&lt;/code&gt; and all the modules and manifests required. Simply get it from &lt;a href=&quot;https://github.com/mudge/managing_web_application_servers_with_puppet/tree/master/example&quot;&gt;GitHub&lt;/a&gt;, run &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;bundle&lt;/code&gt; and then &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&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 class=&quot;language-plaintext highlighter-rouge&quot;&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=&quot;https://github.com/nistude/cucumber-puppet&quot;&gt;Cucumber-Puppet&lt;/a&gt;. That allows you to write Cucumber features for your infrastructure:&lt;/p&gt;

&lt;div class=&quot;language-gherkin highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;kd&quot;&gt;Feature&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; General catalog policy
  In order to ensure a host's catalog
  As a manifest developer
  I want all catalogs to obey some general rules

  &lt;span class=&quot;kn&quot;&gt;Scenario Outline&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; Compile and verify catalog
    &lt;span class=&quot;nf&quot;&gt;Given &lt;/span&gt;a node specified by &lt;span class=&quot;s&quot;&gt;&quot;features/yaml/eg.yml&quot;&lt;/span&gt;
    &lt;span class=&quot;nf&quot;&gt;When &lt;/span&gt;I compile its catalog
    &lt;span class=&quot;nf&quot;&gt;Then &lt;/span&gt;compilation should succeed
    &lt;span class=&quot;nf&quot;&gt;And &lt;/span&gt;all resource dependencies should resolve

    &lt;span class=&quot;nn&quot;&gt;Examples&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;
      &lt;span class=&quot;p&quot;&gt;|&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;hostname&lt;/span&gt;  &lt;span class=&quot;p&quot;&gt;|&lt;/span&gt;
      &lt;span class=&quot;p&quot;&gt;|&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;localhost&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;|&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&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=&quot;http://puppetlabs.com/blog/testing-modules-in-the-puppet-forge/&quot;&gt;an extensive post on testing Puppet on the Puppet Labs blog&lt;/a&gt;.]&lt;/em&gt;&lt;/p&gt;

&lt;h2 id=&quot;wrap-up&quot;&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=&quot;http://docs.puppetlabs.com/learning/&quot;&gt;“Learning Puppet”&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;You can follow me on Twitter, &lt;a href=&quot;http://twitter.com/mudge&quot;&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=&quot;http://github.com/mudge&quot;&gt;github.com/mudge&lt;/a&gt; is where you’ll find the source code to this presentation, it’s called &lt;a href=&quot;http://github.com/mudge/managing_web_application_servers_with_puppet&quot;&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&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=&quot;https://github.com/mudge/managing_web_application_servers_with_puppet/tree/master/example&quot;&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&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 type="html">Combining and Compressing Disqus&amp;rsquo; and Google Analytics&amp;rsquo; JavaScript</title>
  <link href="https://mudge.name/2011/01/29/combining-and-compressing-disqus-and-google-analytics-javascript/"/>
  <id>https://mudge.name/2011/01/29/combining-and-compressing-disqus-and-google-analytics-javascript/</id>
  <published>2011-01-29T00:00:00+00:00</published>
  <updated>2011-01-29T00:00:00+00:00</updated>
  <summary type="html">A step-by-step refactoring of Disqus&amp;rsquo; and Google Analytics&amp;rsquo; JavaScript.</summary>
  <content type="html">&lt;p&gt;In setting up this &lt;a href=&quot;http://jekyllrb.com&quot;&gt;Jekyll&lt;/a&gt;-powered blog, I had cause to dust off my &lt;a href=&quot;http://www.google.com/analytics/&quot;&gt;Google
Analytics&lt;/a&gt; account and finally take &lt;a href=&quot;http://disqus.com&quot;&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=&quot;language-html highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;nt&quot;&gt;&amp;lt;script &lt;/span&gt;&lt;span class=&quot;na&quot;&gt;type=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;text/javascript&quot;&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;&amp;gt;&lt;/span&gt;
  &lt;span class=&quot;kd&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;_gaq&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;_gaq&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;||&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;[];&lt;/span&gt;
  &lt;span class=&quot;nx&quot;&gt;_gaq&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;push&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;([&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;'&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;_setAccount&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;'&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;dl&quot;&gt;'&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;UA-XXXXX-X&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;'&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]);&lt;/span&gt;
  &lt;span class=&quot;nx&quot;&gt;_gaq&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;push&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;([&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;'&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;_trackPageview&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;'&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]);&lt;/span&gt;

  &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;kd&quot;&gt;function&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;kd&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;ga&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;document&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;createElement&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;'&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;script&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;'&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
    &lt;span class=&quot;nx&quot;&gt;ga&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;type&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;dl&quot;&gt;'&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;text/javascript&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;'&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;nx&quot;&gt;ga&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;async&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;kc&quot;&gt;true&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;nx&quot;&gt;ga&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;src&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;'&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;https:&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;'&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;==&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;document&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;location&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;protocol&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;?&lt;/span&gt; &lt;span class=&quot;dl&quot;&gt;'&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;https://ssl&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;'&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;dl&quot;&gt;'&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;http://www&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;'&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;+&lt;/span&gt;
      &lt;span class=&quot;dl&quot;&gt;'&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;.google-analytics.com/ga.js&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;'&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;kd&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;s&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;document&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;getElementsByTagName&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;'&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;script&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;'&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)[&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;];&lt;/span&gt;
    &lt;span class=&quot;nx&quot;&gt;s&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;parentNode&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;insertBefore&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;ga&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;s&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
  &lt;span class=&quot;p&quot;&gt;})();&lt;/span&gt;
&lt;span class=&quot;nt&quot;&gt;&amp;lt;/script&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&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=&quot;language-html highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;nt&quot;&gt;&amp;lt;script &lt;/span&gt;&lt;span class=&quot;na&quot;&gt;type=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;text/javascript&quot;&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;&amp;gt;&lt;/span&gt;
  &lt;span class=&quot;kd&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;disqus_shortname&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;dl&quot;&gt;'&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;example&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;'&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;c1&quot;&gt;// var disqus_identifier = 'unique_dynamic_id_1234';&lt;/span&gt;
  &lt;span class=&quot;c1&quot;&gt;// var disqus_url = 'http://example.com/permalink-to-page.html';&lt;/span&gt;

  &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;kd&quot;&gt;function&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;kd&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;dsq&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;document&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;createElement&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;'&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;script&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;'&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
    &lt;span class=&quot;nx&quot;&gt;dsq&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;type&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;dl&quot;&gt;'&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;text/javascript&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;'&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;nx&quot;&gt;dsq&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;async&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;kc&quot;&gt;true&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;nx&quot;&gt;dsq&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;src&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;dl&quot;&gt;'&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;http://&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;'&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;+&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;disqus_shortname&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;+&lt;/span&gt; &lt;span class=&quot;dl&quot;&gt;'&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;.disqus.com/embed.js&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;'&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;document&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;getElementsByTagName&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;'&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;head&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;'&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)[&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;||&lt;/span&gt;
      &lt;span class=&quot;nb&quot;&gt;document&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;getElementsByTagName&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;'&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;body&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;'&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)[&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]).&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;appendChild&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;dsq&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
  &lt;span class=&quot;p&quot;&gt;})();&lt;/span&gt;
&lt;span class=&quot;nt&quot;&gt;&amp;lt;/script&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&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=&quot;http://news.ycombinator.com/item?id=2157082&quot;&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 class=&quot;language-plaintext highlighter-rouge&quot;&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=&quot;http://code.google.com/apis/analytics/docs/tracking/asyncTracking.html#Installation&quot;&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=&quot;http://mir.aculo.us/dom-monster/&quot;&gt;Amy Hoy and Thomas Fuchs’ “DOM Monster”&lt;/a&gt; shows that
the number of &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&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=&quot;language-html highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;nt&quot;&gt;&amp;lt;script &lt;/span&gt;&lt;span class=&quot;na&quot;&gt;type=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;text/javascript&quot;&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;&amp;gt;&lt;/span&gt;
  &lt;span class=&quot;kd&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;_gaq&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;_gaq&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;||&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;[];&lt;/span&gt;
  &lt;span class=&quot;nx&quot;&gt;_gaq&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;push&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;([&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;'&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;_setAccount&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;'&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;dl&quot;&gt;'&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;UA-XXXXX-X&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;'&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]);&lt;/span&gt;
  &lt;span class=&quot;nx&quot;&gt;_gaq&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;push&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;([&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;'&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;_trackPageview&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;'&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]);&lt;/span&gt;

  &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;kd&quot;&gt;function&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;kd&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;ga&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;document&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;createElement&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;'&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;script&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;'&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
    &lt;span class=&quot;nx&quot;&gt;ga&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;type&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;dl&quot;&gt;'&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;text/javascript&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;'&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;nx&quot;&gt;ga&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;async&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;kc&quot;&gt;true&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;nx&quot;&gt;ga&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;src&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;'&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;https:&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;'&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;==&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;document&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;location&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;protocol&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;?&lt;/span&gt; &lt;span class=&quot;dl&quot;&gt;'&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;https://ssl&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;'&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;dl&quot;&gt;'&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;http://www&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;'&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;+&lt;/span&gt;
      &lt;span class=&quot;dl&quot;&gt;'&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;.google-analytics.com/ga.js&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;'&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;kd&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;s&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;document&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;getElementsByTagName&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;'&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;script&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;'&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)[&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;];&lt;/span&gt;
    &lt;span class=&quot;nx&quot;&gt;s&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;parentNode&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;insertBefore&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;ga&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;s&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
  &lt;span class=&quot;p&quot;&gt;})();&lt;/span&gt;

  &lt;span class=&quot;kd&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;disqus_shortname&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;dl&quot;&gt;'&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;example&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;'&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;kd&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;disqus_identifier&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;dl&quot;&gt;'&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;unique_dynamic_id_1234&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;'&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;kd&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;disqus_url&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;dl&quot;&gt;'&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;http://example.com/permalink-to-page.html&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;'&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;

  &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;kd&quot;&gt;function&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;kd&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;dsq&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;document&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;createElement&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;'&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;script&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;'&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
    &lt;span class=&quot;nx&quot;&gt;dsq&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;type&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;dl&quot;&gt;'&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;text/javascript&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;'&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;nx&quot;&gt;dsq&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;async&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;kc&quot;&gt;true&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;nx&quot;&gt;dsq&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;src&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;dl&quot;&gt;'&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;http://&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;'&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;+&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;disqus_shortname&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;+&lt;/span&gt; &lt;span class=&quot;dl&quot;&gt;'&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;.disqus.com/embed.js&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;'&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;document&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;getElementsByTagName&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;'&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;head&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;'&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)[&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;||&lt;/span&gt;
      &lt;span class=&quot;nb&quot;&gt;document&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;getElementsByTagName&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;'&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;body&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;'&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)[&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]).&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;appendChild&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;dsq&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
  &lt;span class=&quot;p&quot;&gt;})();&lt;/span&gt;
&lt;span class=&quot;nt&quot;&gt;&amp;lt;/script&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&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=&quot;http://dev.w3.org/html5/spec/syntax.html#optional-tags&quot;&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=&quot;http://closure-compiler.appspot.com/&quot;&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 class=&quot;language-plaintext highlighter-rouge&quot;&gt;script&lt;/code&gt; tag itself: it turns out that the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;type&lt;/code&gt;
attribute is optional and is &lt;a href=&quot;http://dev.w3.org/html5/spec/scripting-1.html#attr-script-type&quot;&gt;specified as having a default value of
&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&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=&quot;http://news.ycombinator.com/item?id=2157118&quot;&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 class=&quot;language-plaintext highlighter-rouge&quot;&gt;script&lt;/code&gt; tags
and specifying the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;type&lt;/code&gt; attribute so let’s remove those as well:&lt;/p&gt;

&lt;div class=&quot;language-html highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;nt&quot;&gt;&amp;lt;script&amp;gt;&lt;/span&gt;
  &lt;span class=&quot;kd&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;_gaq&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;_gaq&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;||&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;[];&lt;/span&gt;
  &lt;span class=&quot;nx&quot;&gt;_gaq&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;push&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;([&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;'&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;_setAccount&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;'&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;dl&quot;&gt;'&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;UA-XXXXX-X&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;'&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]);&lt;/span&gt;
  &lt;span class=&quot;nx&quot;&gt;_gaq&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;push&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;([&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;'&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;_trackPageview&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;'&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]);&lt;/span&gt;

  &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;kd&quot;&gt;function&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;kd&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;ga&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;document&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;createElement&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;'&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;script&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;'&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
    &lt;span class=&quot;nx&quot;&gt;ga&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;async&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;kc&quot;&gt;true&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;nx&quot;&gt;ga&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;src&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;'&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;https:&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;'&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;==&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;document&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;location&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;protocol&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;?&lt;/span&gt; &lt;span class=&quot;dl&quot;&gt;'&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;https://ssl&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;'&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;dl&quot;&gt;'&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;http://www&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;'&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;+&lt;/span&gt;
      &lt;span class=&quot;dl&quot;&gt;'&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;.google-analytics.com/ga.js&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;'&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;kd&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;s&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;document&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;getElementsByTagName&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;'&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;script&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;'&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)[&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;];&lt;/span&gt;
    &lt;span class=&quot;nx&quot;&gt;s&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;parentNode&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;insertBefore&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;ga&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;s&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
  &lt;span class=&quot;p&quot;&gt;})();&lt;/span&gt;

  &lt;span class=&quot;kd&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;disqus_shortname&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;dl&quot;&gt;'&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;example&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;'&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;kd&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;disqus_identifier&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;dl&quot;&gt;'&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;unique_dynamic_id_1234&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;'&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;kd&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;disqus_url&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;dl&quot;&gt;'&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;http://example.com/permalink-to-page.html&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;'&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;

  &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;kd&quot;&gt;function&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;kd&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;dsq&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;document&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;createElement&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;'&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;script&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;'&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
    &lt;span class=&quot;nx&quot;&gt;dsq&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;async&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;kc&quot;&gt;true&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;nx&quot;&gt;dsq&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;src&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;dl&quot;&gt;'&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;http://&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;'&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;+&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;disqus_shortname&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;+&lt;/span&gt; &lt;span class=&quot;dl&quot;&gt;'&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;.disqus.com/embed.js&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;'&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;document&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;getElementsByTagName&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;'&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;head&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;'&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)[&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;||&lt;/span&gt;
      &lt;span class=&quot;nb&quot;&gt;document&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;getElementsByTagName&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;'&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;body&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;'&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)[&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]).&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;appendChild&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;dsq&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
  &lt;span class=&quot;p&quot;&gt;})();&lt;/span&gt;
&lt;span class=&quot;nt&quot;&gt;&amp;lt;/script&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Another thing that has caught my eye is the odd set up of the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&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 class=&quot;language-plaintext highlighter-rouge&quot;&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=&quot;http://code.google.com/apis/analytics/docs/gaJS/gaJSApi_gaq.html#_gaq.push&quot;&gt;documentation for &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&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=&quot;http://diveintomark.org&quot;&gt;dive into mark&lt;/a&gt;”, we can simplify
this quite drastically:&lt;/p&gt;

&lt;div class=&quot;language-html highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;nt&quot;&gt;&amp;lt;script&amp;gt;&lt;/span&gt;
  &lt;span class=&quot;kd&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;_gaq&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;[[&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;'&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;_setAccount&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;'&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;dl&quot;&gt;'&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;UA-XXXXX-X&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;'&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;],&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;'&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;_trackPageview&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;'&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]];&lt;/span&gt;
  &lt;span class=&quot;c1&quot;&gt;// ... omitted for brevity ...&lt;/span&gt;
&lt;span class=&quot;nt&quot;&gt;&amp;lt;/script&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&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=&quot;https://developer.mozilla.org/en/JavaScript/Reference/Statements/var&quot;&gt;JavaScript’s syntax to declare multiple variables
at once&lt;/a&gt;:&lt;/p&gt;

&lt;div class=&quot;language-html highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;nt&quot;&gt;&amp;lt;script&amp;gt;&lt;/span&gt;
  &lt;span class=&quot;kd&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;_gaq&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;[[&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;'&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;_setAccount&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;'&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;dl&quot;&gt;'&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;UA-XXXXX-X&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;'&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;],&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;'&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;_trackPageview&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;'&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]],&lt;/span&gt;
      &lt;span class=&quot;nx&quot;&gt;disqus_shortname&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;dl&quot;&gt;'&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;example&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;'&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
      &lt;span class=&quot;nx&quot;&gt;disqus_identifier&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;dl&quot;&gt;'&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;unique_dynamic_id_1234&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;'&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
      &lt;span class=&quot;nx&quot;&gt;disqus_url&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;dl&quot;&gt;'&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;http://example.com/permalink-to-page.html&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;'&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;c1&quot;&gt;// ... omitted for brevity ...&lt;/span&gt;
&lt;span class=&quot;nt&quot;&gt;&amp;lt;/script&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&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 class=&quot;language-plaintext highlighter-rouge&quot;&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 class=&quot;language-plaintext highlighter-rouge&quot;&gt;script&lt;/code&gt; element with &lt;a href=&quot;https://developer.mozilla.org/en/DOM/document.createElement&quot;&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&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=&quot;http://dev.w3.org/html5/spec/scripting-1.html#attr-script-async&quot;&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;async&lt;/code&gt;&lt;/a&gt; attribute;&lt;/li&gt;
  &lt;li&gt;Set the &lt;a href=&quot;http://dev.w3.org/html5/spec/scripting-1.html#attr-script-src&quot;&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&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 class=&quot;language-plaintext highlighter-rouge&quot;&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=&quot;language-html highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;nt&quot;&gt;&amp;lt;script&amp;gt;&lt;/span&gt;
  &lt;span class=&quot;kd&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;_gaq&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;[[&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;'&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;_setAccount&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;'&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;dl&quot;&gt;'&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;UA-XXXXX-X&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;'&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;],&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;'&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;_trackPageview&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;'&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]],&lt;/span&gt;
      &lt;span class=&quot;nx&quot;&gt;disqus_shortname&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;dl&quot;&gt;'&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;example&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;'&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
      &lt;span class=&quot;nx&quot;&gt;disqus_identifier&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;dl&quot;&gt;'&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;unique_dynamic_id_1234&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;'&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
      &lt;span class=&quot;nx&quot;&gt;disqus_url&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;dl&quot;&gt;'&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;http://example.com/permalink-to-page.html&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;'&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;

  &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;kd&quot;&gt;function&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;kd&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;ga&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;document&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;createElement&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;'&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;script&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;'&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;),&lt;/span&gt;
        &lt;span class=&quot;nx&quot;&gt;dsq&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;document&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;createElement&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;'&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;script&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;'&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
    &lt;span class=&quot;nx&quot;&gt;ga&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;async&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;dsq&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;async&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;kc&quot;&gt;true&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;nx&quot;&gt;ga&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;src&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;'&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;https:&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;'&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;==&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;document&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;location&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;protocol&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;?&lt;/span&gt; &lt;span class=&quot;dl&quot;&gt;'&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;https://ssl&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;'&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;dl&quot;&gt;'&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;http://www&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;'&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;+&lt;/span&gt;
      &lt;span class=&quot;dl&quot;&gt;'&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;.google-analytics.com/ga.js&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;'&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;nx&quot;&gt;dsq&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;src&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;dl&quot;&gt;'&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;http://&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;'&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;+&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;disqus_shortname&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;+&lt;/span&gt; &lt;span class=&quot;dl&quot;&gt;'&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;.disqus.com/embed.js&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;'&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;kd&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;s&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;document&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;getElementsByTagName&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;'&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;script&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;'&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)[&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;];&lt;/span&gt;
    &lt;span class=&quot;nx&quot;&gt;s&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;parentNode&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;insertBefore&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;ga&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;s&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;document&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;getElementsByTagName&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;'&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;head&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;'&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)[&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;||&lt;/span&gt;
      &lt;span class=&quot;nb&quot;&gt;document&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;getElementsByTagName&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;'&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;body&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;'&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)[&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]).&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;appendChild&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;dsq&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
  &lt;span class=&quot;p&quot;&gt;})();&lt;/span&gt;
&lt;span class=&quot;nt&quot;&gt;&amp;lt;/script&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;We’ve stated that both snippets insert their newly-created &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;script&lt;/code&gt; elements into
the &lt;a href=&quot;https://developer.mozilla.org/en/DOM&quot;&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=&quot;http://code.google.com/speed/page-speed/docs/payload.html#DeferLoadingJS&quot;&gt;Google’s Performance Best Practices&lt;/a&gt;, let’s
append the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;script&lt;/code&gt; elements to the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;body&lt;/code&gt; tag by using &lt;a href=&quot;https://developer.mozilla.org/en/DOM/document.body&quot;&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;document.body&lt;/code&gt;&lt;/a&gt;
and &lt;a href=&quot;https://developer.mozilla.org/En/DOM/Node.appendChild&quot;&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;appendChild&lt;/code&gt;&lt;/a&gt;:&lt;/p&gt;

&lt;div class=&quot;language-html highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;nt&quot;&gt;&amp;lt;script&amp;gt;&lt;/span&gt;
  &lt;span class=&quot;kd&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;_gaq&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;[[&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;'&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;_setAccount&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;'&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;dl&quot;&gt;'&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;UA-XXXXX-X&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;'&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;],&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;'&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;_trackPageview&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;'&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]],&lt;/span&gt;
      &lt;span class=&quot;nx&quot;&gt;disqus_shortname&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;dl&quot;&gt;'&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;example&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;'&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
      &lt;span class=&quot;nx&quot;&gt;disqus_identifier&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;dl&quot;&gt;'&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;unique_dynamic_id_1234&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;'&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
      &lt;span class=&quot;nx&quot;&gt;disqus_url&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;dl&quot;&gt;'&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;http://example.com/permalink-to-page.html&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;'&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;

  &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;kd&quot;&gt;function&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;kd&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;ga&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;document&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;createElement&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;'&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;script&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;'&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;),&lt;/span&gt;
        &lt;span class=&quot;nx&quot;&gt;dsq&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;document&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;createElement&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;'&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;script&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;'&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
    &lt;span class=&quot;nx&quot;&gt;ga&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;async&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;dsq&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;async&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;kc&quot;&gt;true&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;nx&quot;&gt;ga&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;src&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;'&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;https:&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;'&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;==&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;document&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;location&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;protocol&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;?&lt;/span&gt; &lt;span class=&quot;dl&quot;&gt;'&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;https://ssl&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;'&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;dl&quot;&gt;'&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;http://www&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;'&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;+&lt;/span&gt;
      &lt;span class=&quot;dl&quot;&gt;'&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;.google-analytics.com/ga.js&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;'&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;nx&quot;&gt;dsq&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;src&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;dl&quot;&gt;'&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;http://&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;'&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;+&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;disqus_shortname&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;+&lt;/span&gt; &lt;span class=&quot;dl&quot;&gt;'&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;.disqus.com/embed.js&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;'&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;nb&quot;&gt;document&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;body&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;appendChild&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;ga&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
    &lt;span class=&quot;nb&quot;&gt;document&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;body&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;appendChild&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;dsq&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
  &lt;span class=&quot;p&quot;&gt;})();&lt;/span&gt;
&lt;span class=&quot;nt&quot;&gt;&amp;lt;/script&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&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=&quot;http://en.wikipedia.org/wiki/Don't_repeat_yourself&quot;&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 class=&quot;language-plaintext highlighter-rouge&quot;&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 class=&quot;language-plaintext highlighter-rouge&quot;&gt;disqus_shortname&lt;/code&gt; by
explicitly setting the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;src&lt;/code&gt; to your own Disqus URL such as
&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;http://example.disqus.com/embed.js&lt;/code&gt;;&lt;/li&gt;
  &lt;li&gt;Instead of calling &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&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=&quot;http://www.jslint.com&quot;&gt;JSLint&lt;/a&gt; reveals that invoking a function defined inside
parentheses (e.g. &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;(function() { ... })()&lt;/code&gt;) should be done &lt;em&gt;inside&lt;/em&gt; the
parentheses instead of outside (e.g. &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&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=&quot;language-html highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;nt&quot;&gt;&amp;lt;script&amp;gt;&lt;/span&gt;
  &lt;span class=&quot;kd&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;_gaq&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;[[&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;'&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;_setAccount&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;'&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;dl&quot;&gt;'&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;UA-XXXXX-X&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;'&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;],&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;'&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;_trackPageview&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;'&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]],&lt;/span&gt;
      &lt;span class=&quot;nx&quot;&gt;disqus_shortname&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;dl&quot;&gt;'&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;example&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;'&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
      &lt;span class=&quot;nx&quot;&gt;disqus_identifier&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;dl&quot;&gt;'&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;unique_dynamic_id_1234&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;'&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
      &lt;span class=&quot;nx&quot;&gt;disqus_url&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;dl&quot;&gt;'&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;http://example.com/permalink-to-page.html&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;'&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;

  &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;kd&quot;&gt;function&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;kd&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;ga&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;document&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;createElement&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;'&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;script&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;'&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;),&lt;/span&gt;
        &lt;span class=&quot;nx&quot;&gt;dsq&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;document&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;createElement&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;'&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;script&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;'&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;),&lt;/span&gt;
        &lt;span class=&quot;nx&quot;&gt;body&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;document&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;body&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;nx&quot;&gt;ga&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;async&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;dsq&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;async&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;kc&quot;&gt;true&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;nx&quot;&gt;ga&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;src&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;dl&quot;&gt;'&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;http://www.google-analytics.com/ga.js&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;'&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;nx&quot;&gt;dsq&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;src&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;dl&quot;&gt;'&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;http://example.disqus.com/embed.js&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;'&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;nx&quot;&gt;body&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;appendChild&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;ga&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
    &lt;span class=&quot;nx&quot;&gt;body&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;appendChild&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;dsq&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
  &lt;span class=&quot;p&quot;&gt;}());&lt;/span&gt;
&lt;span class=&quot;nt&quot;&gt;&amp;lt;/script&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;At this point you can now turn to more brutal compressors such as &lt;a href=&quot;http://developer.yahoo.com/yui/compressor/&quot;&gt;YUI
Compressor&lt;/a&gt; or the aforementioned &lt;a href=&quot;http://closure-compiler.appspot.com/&quot;&gt;Closure Compiler&lt;/a&gt; which (after some
tweaks to keep &lt;a href=&quot;http://www.jslint.com&quot;&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=&quot;language-html highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;nt&quot;&gt;&amp;lt;script&amp;gt;&lt;/span&gt;
&lt;span class=&quot;kd&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;_gaq&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[[&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;_setAccount&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;UA-XXXXX-X&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;],[&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;_trackPageview&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]],&lt;/span&gt;
&lt;span class=&quot;nx&quot;&gt;disqus_shortname&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;example&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;disqus_identifier&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;unique_dynamic_id_1234&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
&lt;span class=&quot;nx&quot;&gt;disqus_url&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;http://example.com/permalink-to-page.html&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;(&lt;/span&gt;&lt;span class=&quot;kd&quot;&gt;function&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(){&lt;/span&gt;
&lt;span class=&quot;kd&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;a&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;document&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;createElement&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;script&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;),&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;b&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;document&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;createElement&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;script&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;),&lt;/span&gt;
&lt;span class=&quot;nx&quot;&gt;c&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;document&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;body&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;a&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;async&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;b&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;async&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;kc&quot;&gt;true&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;a&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;src&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;http://www.google-analytics.com/ga.js&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;nx&quot;&gt;b&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;src&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;http://example.disqus.com/embed.js&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;c&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;appendChild&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;a&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;c&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;appendChild&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;b&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);}());&lt;/span&gt;
&lt;span class=&quot;nt&quot;&gt;&amp;lt;/script&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&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=&quot;http://www.jslint.com&quot;&gt;JSLint&lt;/a&gt;, feel free to post your own attempts in
the comments:&lt;/p&gt;

&lt;div class=&quot;language-html highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;nt&quot;&gt;&amp;lt;script&amp;gt;&lt;/span&gt;
&lt;span class=&quot;kd&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;_gaq&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[[&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;_setAccount&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;UA-XXXXX-X&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;],[&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;_trackPageview&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]],&lt;/span&gt;
&lt;span class=&quot;nx&quot;&gt;disqus_shortname&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;example&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;disqus_identifier&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;unique_dynamic_id_1234&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
&lt;span class=&quot;nx&quot;&gt;disqus_url&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;http://example.com/permalink-to-page.html&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;(&lt;/span&gt;&lt;span class=&quot;kd&quot;&gt;function&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(){&lt;/span&gt;
&lt;span class=&quot;kd&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;a&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;document&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;b&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;a&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;createElement&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;script&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;),&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;c&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;a&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;body&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;d&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;nx&quot;&gt;b&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;async&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;kc&quot;&gt;true&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;d&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;b&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;cloneNode&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;kc&quot;&gt;false&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;b&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;src&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;http://www.google-analytics.com/ga.js&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;nx&quot;&gt;d&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;src&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;http://example.disqus.com/embed.js&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;c&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;appendChild&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;b&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;c&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;appendChild&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;d&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);}());&lt;/span&gt;
&lt;span class=&quot;nt&quot;&gt;&amp;lt;/script&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&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=&quot;http://www.jslint.com&quot;&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 class=&quot;language-plaintext highlighter-rouge&quot;&gt;_gaq&lt;/code&gt;,
&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;disqus_identifier&lt;/code&gt;, &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;disqus_shortname&lt;/code&gt; &lt;del&gt;and &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;disqus_url&lt;/code&gt;&lt;/del&gt;
(&lt;em&gt;Update:&lt;/em&gt; Disqus software engineer &lt;a href=&quot;http://anton.kovalyov.net/&quot;&gt;Anton Kovalyov&lt;/a&gt; says in the comments
that &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;disqus_url&lt;/code&gt; is not required if &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&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=&quot;http://www.steveklabnik.com&quot;&gt;Steve Klabnik&lt;/a&gt;, &lt;a href=&quot;http://anton.kovalyov.net/&quot;&gt;Anton Kovalyov&lt;/a&gt; and
&lt;a href=&quot;http://www.moddular.org&quot;&gt;Andrew Walker&lt;/a&gt;, here is a solution that consists of only 345 characters:&lt;/p&gt;

&lt;div class=&quot;language-html highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;nt&quot;&gt;&amp;lt;script&amp;gt;&lt;/span&gt;
&lt;span class=&quot;kd&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;_gaq&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[[&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;_setAccount&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;UA-XXXXX-X&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;],[&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;_trackPageview&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]],&lt;/span&gt;
&lt;span class=&quot;nx&quot;&gt;disqus_shortname&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;example&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;disqus_identifier&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;unique_dynamic_id_1234&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;kd&quot;&gt;function&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;a&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;b&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;){&lt;/span&gt;
&lt;span class=&quot;kd&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;c&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;a&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;createElement&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;script&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;),&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;d&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;a&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;body&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;e&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;nx&quot;&gt;c&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;async&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;b&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;e&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;c&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;cloneNode&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;b&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;c&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;src&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;//www.google-analytics.com/ga.js&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;nx&quot;&gt;e&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;src&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;//example.disqus.com/embed.js&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;d&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;appendChild&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;c&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;d&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;appendChild&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;e&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}(&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;document&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;kc&quot;&gt;true&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;));&lt;/span&gt;
&lt;span class=&quot;nt&quot;&gt;&amp;lt;/script&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;If you want to follow &lt;a href=&quot;http://code.google.com/apis/analytics/docs/tracking/asyncTracking.html#Installation&quot;&gt;Google’s recommendation of inserting their
snippet at the bottom of the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;head&lt;/code&gt; element&lt;/a&gt; (and continue inserting Disqus’ snippet at the bottom of
the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;body&lt;/code&gt;) then you will have to sacrifice the single, unified &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;script&lt;/code&gt; for two
separate ones:&lt;/p&gt;

&lt;div class=&quot;language-html highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;nt&quot;&gt;&amp;lt;head&amp;gt;&lt;/span&gt;
  &lt;span class=&quot;c&quot;&gt;&amp;lt;!-- Usual HTML head elements here... --&amp;gt;&lt;/span&gt;
  &lt;span class=&quot;nt&quot;&gt;&amp;lt;script&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;kd&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;_gaq&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[[&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;_setAccount&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;UA-XXXXX-X&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;],[&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;_trackPageview&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]],&lt;/span&gt;
    &lt;span class=&quot;nx&quot;&gt;disqus_shortname&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;example&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;disqus_identifier&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;unique_dynamic_id_1234&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;kd&quot;&gt;function&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;a&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;b&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;){&lt;/span&gt;&lt;span class=&quot;kd&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;c&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;document&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;d&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;c&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;createElement&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;script&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
    &lt;span class=&quot;nx&quot;&gt;d&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;async&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;kc&quot;&gt;true&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;d&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;src&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;b&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;nx&quot;&gt;c&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;documentElement&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;firstChild&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;appendChild&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;d&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);}&lt;/span&gt;
    &lt;span class=&quot;nx&quot;&gt;a&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;//www.google-analytics.com/ga.js&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
  &lt;span class=&quot;nt&quot;&gt;&amp;lt;/script&amp;gt;&lt;/span&gt;
&lt;span class=&quot;nt&quot;&gt;&amp;lt;/head&amp;gt;&lt;/span&gt;
&lt;span class=&quot;nt&quot;&gt;&amp;lt;body&amp;gt;&lt;/span&gt;
  &lt;span class=&quot;c&quot;&gt;&amp;lt;!-- Page content here... --&amp;gt;&lt;/span&gt;
  &lt;span class=&quot;nt&quot;&gt;&amp;lt;script&amp;gt;&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;a&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;//example.disqus.com/embed.js&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;&amp;lt;/script&amp;gt;&lt;/span&gt;
&lt;span class=&quot;nt&quot;&gt;&amp;lt;/body&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&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 class=&quot;language-plaintext highlighter-rouge&quot;&gt;a&lt;/code&gt; which will create a new &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;script&lt;/code&gt; element and append it to
the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&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 class=&quot;language-plaintext highlighter-rouge&quot;&gt;script&lt;/code&gt;
and population of the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;head&lt;/code&gt; element won’t win you any favours with the &lt;a href=&quot;http://mir.aculo.us/dom-monster/&quot;&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 class=&quot;language-plaintext highlighter-rouge&quot;&gt;script&lt;/code&gt;
elements created by synchronously requiring the Disqus JavaScript yourself at
the bottom of the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;body&lt;/code&gt;:&lt;/p&gt;

&lt;div class=&quot;language-html highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;nt&quot;&gt;&amp;lt;head&amp;gt;&lt;/span&gt;
  &lt;span class=&quot;c&quot;&gt;&amp;lt;!-- Usual HTML head elements here... --&amp;gt;&lt;/span&gt;
  &lt;span class=&quot;nt&quot;&gt;&amp;lt;script&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;kd&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;_gaq&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[[&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;_setAccount&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;UA-XXXXX-X&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;],[&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;_trackPageview&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]],&lt;/span&gt;
    &lt;span class=&quot;nx&quot;&gt;disqus_shortname&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;example&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;disqus_identifier&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;unique_dynamic_id_1234&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;kd&quot;&gt;function&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;a&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;){&lt;/span&gt;&lt;span class=&quot;kd&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;b&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;a&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;createElement&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;script&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;b&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;async&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;kc&quot;&gt;true&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;nx&quot;&gt;b&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;src&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;//www.google-analytics.com/ga.js&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;nx&quot;&gt;a&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;documentElement&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;firstChild&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;appendChild&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;b&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);}(&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;document&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;));&lt;/span&gt;
  &lt;span class=&quot;nt&quot;&gt;&amp;lt;/script&amp;gt;&lt;/span&gt;
&lt;span class=&quot;nt&quot;&gt;&amp;lt;/head&amp;gt;&lt;/span&gt;
&lt;span class=&quot;nt&quot;&gt;&amp;lt;body&amp;gt;&lt;/span&gt;
  &lt;span class=&quot;c&quot;&gt;&amp;lt;!-- Page content here... --&amp;gt;&lt;/span&gt;
  &lt;span class=&quot;nt&quot;&gt;&amp;lt;script &lt;/span&gt;&lt;span class=&quot;na&quot;&gt;src=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;//example.disqus.com/embed.js&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;async&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;&amp;gt;&amp;lt;/script&amp;gt;&lt;/span&gt;
&lt;span class=&quot;nt&quot;&gt;&amp;lt;/body&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

</content>
</entry><entry>
  <title type="html">Passing Blocks in Ruby Without &lt;code&gt;&amp;amp;block&lt;/code&gt;</title>
  <link href="https://mudge.name/2011/01/26/passing-blocks-in-ruby-without-block/"/>
  <id>https://mudge.name/2011/01/26/passing-blocks-in-ruby-without-block/</id>
  <published>2011-01-26T00:00:00+00:00</published>
  <updated>2011-01-26T00:00:00+00:00</updated>
  <summary type="html">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=&quot;http://ruby-doc.org/docs/ProgrammingRuby/html/tut_containers.html&quot;&gt;blocks&lt;/a&gt; in a method in Ruby:
the first is to use the &lt;a href=&quot;http://ruby-doc.org/docs/keywords/1.9/files/keywords_rb.html#M000042&quot;&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;yield&lt;/code&gt;&lt;/a&gt; keyword like so:&lt;/p&gt;

&lt;div class=&quot;language-ruby highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;k&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;speak&lt;/span&gt;
  &lt;span class=&quot;nb&quot;&gt;puts&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;yield&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;

&lt;span class=&quot;n&quot;&gt;speak&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;Hello&quot;&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;c1&quot;&gt;# Hello&lt;/span&gt;
&lt;span class=&quot;c1&quot;&gt;#  =&amp;gt; nil&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&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=&quot;http://www.ruby-doc.org/core/classes/Proc.html&quot;&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&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=&quot;http://www.ruby-doc.org/core/classes/Proc.html#M000548&quot;&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;call&lt;/code&gt;&lt;/a&gt; method like so:&lt;/p&gt;

&lt;div class=&quot;language-ruby highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;k&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;speak&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;amp;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;block&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
  &lt;span class=&quot;nb&quot;&gt;puts&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;block&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;call&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;

&lt;span class=&quot;n&quot;&gt;speak&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;Hello&quot;&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;c1&quot;&gt;# Hello&lt;/span&gt;
&lt;span class=&quot;c1&quot;&gt;#  =&amp;gt; nil&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;The problem with the second approach is that instantiating a new &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Proc&lt;/code&gt; object incurs
a surprisingly heavy performance penalty as detailed by &lt;a href=&quot;http://tenderlovemaking.com&quot;&gt;Aaron Patterson&lt;/a&gt; in his
excellent RubyConf X presentation, “&lt;a href=&quot;http://confreaks.net/videos/427-rubyconf2010-zomg-why-is-this-code-so-slow&quot;&gt;ZOMG WHY IS THIS CODE SO SLOW?&lt;/a&gt;”
(beginning around the 30 minute mark or from &lt;a href=&quot;http://www.slideshare.net/tenderlove/zomg-why-is-this-code-so-slow/181&quot;&gt;slide 181&lt;/a&gt;).&lt;/p&gt;

&lt;p&gt;This can easily be verified with the following benchmark, &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;block_benchmark.rb&lt;/code&gt;:&lt;/p&gt;

&lt;div class=&quot;language-ruby highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;nb&quot;&gt;require&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;benchmark&quot;&lt;/span&gt;

&lt;span class=&quot;k&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;speak_with_block&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;amp;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;block&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
  &lt;span class=&quot;n&quot;&gt;block&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;call&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;

&lt;span class=&quot;k&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;speak_with_yield&lt;/span&gt;
  &lt;span class=&quot;k&quot;&gt;yield&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;

&lt;span class=&quot;n&quot;&gt;n&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;1_000_000&lt;/span&gt;
&lt;span class=&quot;no&quot;&gt;Benchmark&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;bmbm&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;do&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;|&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;x&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;|&lt;/span&gt;
  &lt;span class=&quot;n&quot;&gt;x&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;report&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;&amp;amp;block&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;do&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;n&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;times&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;speak_with_block&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;ook&quot;&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
  &lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;
  &lt;span class=&quot;n&quot;&gt;x&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;report&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;yield&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;do&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;n&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;times&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;speak_with_yield&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;ook&quot;&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
  &lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/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;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&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;/div&gt;&lt;/div&gt;

&lt;p&gt;So it is clearly preferable to choose &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;yield&lt;/code&gt; over &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&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 class=&quot;language-plaintext highlighter-rouge&quot;&gt;tell_ape&lt;/code&gt; which delegates to
another, more generic method named &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;tell&lt;/code&gt;. This sort of pattern is commonly done
using &lt;a href=&quot;http://ruby-doc.org/docs/ProgrammingRuby/html/ref_c_object.html#Object.method_missing&quot;&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;method_missing&lt;/code&gt;&lt;/a&gt; but I’ll keep the methods explicit for
simplicity:&lt;/p&gt;

&lt;div class=&quot;language-ruby highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;k&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;Monkey&lt;/span&gt;

  &lt;span class=&quot;c1&quot;&gt;# Monkey.tell_ape { &quot;ook!&quot; }&lt;/span&gt;
  &lt;span class=&quot;c1&quot;&gt;# ape: ook!&lt;/span&gt;
  &lt;span class=&quot;c1&quot;&gt;#  =&amp;gt; nil&lt;/span&gt;
  &lt;span class=&quot;k&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;self&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;tell_ape&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;amp;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;block&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;tell&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;ape&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&amp;amp;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;block&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
  &lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;

  &lt;span class=&quot;k&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;self&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;tell&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&amp;amp;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;block&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;nb&quot;&gt;puts&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;si&quot;&gt;#{&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;si&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;: &lt;/span&gt;&lt;span class=&quot;si&quot;&gt;#{&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;block&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;call&lt;/span&gt;&lt;span class=&quot;si&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;&lt;/span&gt;
  &lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Such a thing is not possible with the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;yield&lt;/code&gt; keyword:&lt;/p&gt;

&lt;div class=&quot;language-ruby highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;k&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;Monkey&lt;/span&gt;

  &lt;span class=&quot;c1&quot;&gt;# Monkey.tell_ape { &quot;ook!&quot; }&lt;/span&gt;
  &lt;span class=&quot;c1&quot;&gt;# ArgumentError: wrong number of arguments (2 for 1)&lt;/span&gt;
  &lt;span class=&quot;k&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;self&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;tell_ape&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;tell&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;ape&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;yield&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
  &lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;

  &lt;span class=&quot;k&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;self&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;tell&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;nb&quot;&gt;puts&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;si&quot;&gt;#{&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;si&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;: &lt;/span&gt;&lt;span class=&quot;si&quot;&gt;#{&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;yield&lt;/span&gt;&lt;span class=&quot;si&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;&lt;/span&gt;
  &lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Neither does it work by using an ampersand:&lt;/p&gt;

&lt;div class=&quot;language-ruby highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;k&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;Monkey&lt;/span&gt;

  &lt;span class=&quot;c1&quot;&gt;# Monkey.tell_ape { &quot;ook!&quot; }&lt;/span&gt;
  &lt;span class=&quot;c1&quot;&gt;# TypeError: wrong argument type String (expected Proc)&lt;/span&gt;
  &lt;span class=&quot;k&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;self&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;tell_ape&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;tell&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;ape&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&amp;amp;&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;yield&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
  &lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;

  &lt;span class=&quot;k&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;self&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;tell&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;nb&quot;&gt;puts&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;si&quot;&gt;#{&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;si&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;: &lt;/span&gt;&lt;span class=&quot;si&quot;&gt;#{&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;yield&lt;/span&gt;&lt;span class=&quot;si&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;&lt;/span&gt;
  &lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;However, there is a way to only create a &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Proc&lt;/code&gt; object when needed and that is
to use the little known behaviour of &lt;a href=&quot;http://www.ruby-doc.org/core/classes/Proc.html#M000547&quot;&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&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 class=&quot;language-plaintext highlighter-rouge&quot;&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 class=&quot;language-plaintext highlighter-rouge&quot;&gt;Proc&lt;/code&gt; containing the block given to its surrounding method.&lt;/p&gt;

&lt;div class=&quot;language-ruby highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;k&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;speak&lt;/span&gt;
  &lt;span class=&quot;nb&quot;&gt;puts&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;Proc&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;new&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;call&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;

&lt;span class=&quot;n&quot;&gt;speak&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;Hello&quot;&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;c1&quot;&gt;# Hello&lt;/span&gt;
&lt;span class=&quot;c1&quot;&gt;#  =&amp;gt; nil&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&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 class=&quot;language-plaintext highlighter-rouge&quot;&gt;&amp;amp;block&lt;/code&gt; parameter:&lt;/p&gt;

&lt;div class=&quot;language-ruby highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;k&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;Monkey&lt;/span&gt;

  &lt;span class=&quot;c1&quot;&gt;# Monkey.tell_ape { &quot;ook!&quot; }&lt;/span&gt;
  &lt;span class=&quot;c1&quot;&gt;# ape: ook!&lt;/span&gt;
  &lt;span class=&quot;c1&quot;&gt;#  =&amp;gt; nil&lt;/span&gt;
  &lt;span class=&quot;k&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;self&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;tell_ape&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;tell&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;ape&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&amp;amp;&lt;/span&gt;&lt;span class=&quot;no&quot;&gt;Proc&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;new&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
  &lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;

  &lt;span class=&quot;k&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;self&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;tell&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;nb&quot;&gt;puts&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;si&quot;&gt;#{&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;si&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;: &lt;/span&gt;&lt;span class=&quot;si&quot;&gt;#{&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;yield&lt;/span&gt;&lt;span class=&quot;si&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;&lt;/span&gt;
  &lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Of course, if you do use &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Proc.new&lt;/code&gt; then you lose the performance benefit of using
only &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;yield&lt;/code&gt; (as &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Proc&lt;/code&gt; objects are being created as with &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&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 class=&quot;language-plaintext highlighter-rouge&quot;&gt;proc_new_benchmark.rb&lt;/code&gt;:&lt;/p&gt;

&lt;div class=&quot;language-ruby highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;nb&quot;&gt;require&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;benchmark&quot;&lt;/span&gt;

&lt;span class=&quot;k&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;sometimes_block&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;flag&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&amp;amp;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;block&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
  &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;flag&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;block&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;block&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;call&lt;/span&gt;
  &lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;

&lt;span class=&quot;k&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;sometimes_proc_new&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;flag&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
  &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;flag&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;block_given?&lt;/span&gt;
    &lt;span class=&quot;no&quot;&gt;Proc&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;new&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;call&lt;/span&gt;
  &lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;

&lt;span class=&quot;n&quot;&gt;n&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;1_000_000&lt;/span&gt;
&lt;span class=&quot;no&quot;&gt;Benchmark&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;bmbm&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;do&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;|&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;x&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;|&lt;/span&gt;
  &lt;span class=&quot;n&quot;&gt;x&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;report&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;&amp;amp;block&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;do&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;n&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;times&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;do&lt;/span&gt;
      &lt;span class=&quot;n&quot;&gt;sometimes_block&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;kp&quot;&gt;false&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;won't get used&quot;&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;
  &lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;
  &lt;span class=&quot;n&quot;&gt;x&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;report&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;Proc.new&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;do&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;n&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;times&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;do&lt;/span&gt;
      &lt;span class=&quot;n&quot;&gt;sometimes_proc_new&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;kp&quot;&gt;false&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;won't get used&quot;&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;
  &lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Which makes the following rather significant difference:&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&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;/div&gt;&lt;/div&gt;

&lt;p&gt;The key here is that using &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;&amp;amp;block&lt;/code&gt; will &lt;em&gt;always&lt;/em&gt; create a new &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Proc&lt;/code&gt; object,
even if we don’t make use of it. By using &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&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 class=&quot;language-plaintext highlighter-rouge&quot;&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 class=&quot;language-plaintext highlighter-rouge&quot;&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>
