<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom">
  <channel>
    <title>Notes: Josh McArthur</title>
    <description>I&apos;m Josh McArthur, a web developer specializing in Ruby on Rails and Phoenix. I write posts about technology and hiking.</description>
    <link>https://joshmcarthur.com/</link>
    <atom:link href="https://joshmcarthur.com/feed.xml" rel="self" type="application/rss+xml"/>
    <pubDate>Mon, 13 Apr 2026 21:18:19 +1200</pubDate>
    <lastBuildDate>Mon, 13 Apr 2026 21:18:19 +1200</lastBuildDate>
    <generator>Jekyll v3.10.0</generator>
    
      <item>
        <title>How to handle Cloudflare Challenge pages when making Turbo requests</title>
        <description>&lt;p&gt;&lt;a href=&quot;https://turbo.hotwired.dev/handbook/drive&quot;&gt;Turbo Drive&lt;/a&gt; intercepts link clicks and form submissions, fetching the next page with &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;fetch()&lt;/code&gt; and swapping the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;&amp;lt;body&amp;gt;&lt;/code&gt; in place rather than doing a full browser reload. That’s great for smoothness, but it means the browser never performs a real navigation — and some things depend on a real navigation to work.&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;https://developers.cloudflare.com/cloudflare-challenges/challenge-types/challenge-pages/&quot;&gt;Cloudflare challenge pages&lt;/a&gt; are one of those things. When Cloudflare decides a request needs to be challenged, it returns a special HTML page that runs JavaScript to verify the visitor. If Turbo swaps that HTML into the existing &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;&amp;lt;body&amp;gt;&lt;/code&gt; the challenge scripts fail to initialise and the user ends up stuck on a broken page.&lt;/p&gt;

&lt;p&gt;Cloudflare &lt;a href=&quot;https://developers.cloudflare.com/cloudflare-challenges/challenge-types/challenge-pages/detect-response/&quot;&gt;marks challenge responses&lt;/a&gt; with a &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Cf-Mitigated: challenge&lt;/code&gt; header. Turbo fires a &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;turbo:before-fetch-response&lt;/code&gt; event after a fetch completes but before it processes the body, which is the right place to catch this. Calling &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;event.preventDefault()&lt;/code&gt; stops the swap, then &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;window.location.assign()&lt;/code&gt; upgrades the visit to a real navigation:&lt;/p&gt;

&lt;div class=&quot;language-typescript highlighter-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;&apos;&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;turbo:before-fetch-response&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&apos;&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;event&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;Event&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;o&quot;&gt;!&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&apos;&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;detail&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&apos;&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;in&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;event&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;||&lt;/span&gt;
    &lt;span class=&quot;o&quot;&gt;!&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;event&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;detail&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;||&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;typeof&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;event&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;detail&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;!==&lt;/span&gt; &lt;span class=&quot;dl&quot;&gt;&apos;&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;object&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&apos;&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;kd&quot;&gt;const&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;fetchResponse&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;event&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;detail&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;as&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;na&quot;&gt;fetchResponse&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;header&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;name&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;kr&quot;&gt;string&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;kr&quot;&gt;string&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;|&lt;/span&gt; &lt;span class=&quot;kc&quot;&gt;null&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;nl&quot;&gt;location&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;URL&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;fetchResponse&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;header&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&apos;&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;cf-mitigated&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&apos;&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;&apos;&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;challenge&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&apos;&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;nx&quot;&gt;event&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;preventDefault&lt;/span&gt;&lt;span class=&quot;p&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;location&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;assign&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;fetchResponse&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;href&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;detail&lt;/code&gt; guard is there because TypeScript types the event as a plain &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Event&lt;/code&gt; — Turbo’s custom properties aren’t in the standard lib, so a cast is needed. The early return keeps the handler cheap for the overwhelming majority of requests that aren’t challenges.&lt;/p&gt;

&lt;p&gt;Once the challenge is solved, Cloudflare redirects back to the original URL, which Turbo handles as a normal navigation.&lt;/p&gt;
</description>
        <pubDate>Mon, 13 Apr 2026 00:00:00 +1200</pubDate>
        <link>https://joshmcarthur.com/til/2026/04/13/Handle-Cloudflare-Challenge-pages-when-making-Turbo-requests.html</link>
        <guid isPermaLink="true">https://joshmcarthur.com/til/2026/04/13/Handle-Cloudflare-Challenge-pages-when-making-Turbo-requests.html</guid>
        
        
        <category>TIL</category>
        
      </item>
    
      <item>
        <title>How to test new Github Actions workflows</title>
        <description>&lt;p&gt;I recently build out a data processing pipeline that uses Github Actions, and so
needed to add several new workflows. I couldn’t for the life of me figure out
how to test them. They were going to be initiated either as part of a pipeline
(using &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;workflow_call&lt;/code&gt;), or manually, using &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;workflow_dispatch&lt;/code&gt;. In most cases,
each workflow had inputs which needed to be provided.&lt;/p&gt;

&lt;p&gt;Github Actions shows workflows that can be called, but only once they’re on
&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;main&lt;/code&gt;. I didn’t really want to sit here committing directly to &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;main&lt;/code&gt; until
they worked the way I needed, so I came up with this flow which works well:&lt;/p&gt;

&lt;ol&gt;
  &lt;li&gt;Create the workflow files you need. Make sure they have the
&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;workflow_dispatch&lt;/code&gt; trigger &lt;em&gt;at least&lt;/em&gt; so they can be invoked&lt;/li&gt;
  &lt;li&gt;Put a minimal job into the workflow file - it doesn’t matter what the job
does, it just needs to be syntactically valid&lt;/li&gt;
  &lt;li&gt;Via a pull request, add these new workflows to &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;main&lt;/code&gt;- noting that they are
basically no-op workflows at the moment&lt;/li&gt;
  &lt;li&gt;Now that the workflows are on &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;main&lt;/code&gt;, they are available to be dispatched
from the Github Actions UI, &lt;em&gt;and&lt;/em&gt; you can now choose which branch to run the
workflow from.&lt;/li&gt;
  &lt;li&gt;Modify your workflow on a branch, committing as necessary and invoking the
workflow from that branch.&lt;/li&gt;
  &lt;li&gt;Once you have the workflow working the way you expect, you can open a pull
request to add the ‘implementation’ of the workflow back to ‘main’.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;This pattern still requires a pull request onto main, but only with no-op,
‘empty’ workflows. Once they are on &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;main&lt;/code&gt;, they can be actually implemented and
tested in a feature branch before merging the implementation back to &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;main&lt;/code&gt;.&lt;/p&gt;
</description>
        <pubDate>Mon, 29 Sep 2025 00:00:00 +1300</pubDate>
        <link>https://joshmcarthur.com/til/2025/09/29/How-to-test-new-Github-Actions-workflows.html</link>
        <guid isPermaLink="true">https://joshmcarthur.com/til/2025/09/29/How-to-test-new-Github-Actions-workflows.html</guid>
        
        
        <category>TIL</category>
        
      </item>
    
      <item>
        <title>Talk on Plain Language Law at the AI Engineers meetup in Wellington, NZ</title>
        <description>&lt;p&gt;Last night I had a great opportuntity to talk about &lt;a href=&quot;https://plainlanguagelaw.nz&quot;&gt;Plain Language Law&lt;/a&gt;, a site that I built using my own time and personal development time provided by &lt;a href=&quot;https://ackama.com&quot;&gt;Ackama&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Plain Language Law is a site that turns New Zealand legislation into plain language using &lt;abbr title=&quot;Large Language Models&quot;&gt;LLMs&lt;/abbr&gt;. This was the first meetup I’d presented at, so I really appreciated the invite, and the chance to do a bit of personal development with public speaking. I found I got a lot of value in conversations and questions, and I have some great ideas about ways I can build improvements into the data processing and QA pipeline.&lt;/p&gt;

&lt;p&gt;If this talk interests you, my slides can be found below:&lt;/p&gt;

&lt;iframe src=&quot;https://www.joshmcarthur.com/plain-language-talk/&quot; style=&quot;width: 1280px; height: 720px; max-width: 100%;&quot;&gt;&lt;/iframe&gt;
</description>
        <pubDate>Thu, 04 Sep 2025 00:00:00 +1200</pubDate>
        <link>https://joshmcarthur.com/2025/09/04/Talk-on-Plain-Language-Law-at-AI-Engineers-meetup.html</link>
        <guid isPermaLink="true">https://joshmcarthur.com/2025/09/04/Talk-on-Plain-Language-Law-at-AI-Engineers-meetup.html</guid>
        
        
      </item>
    
      <item>
        <title>Handy access to OS environment variables on Debian</title>
        <description>&lt;p&gt;This morning, while checking (yet again) how to &lt;a href=&quot;https://www.postgresql.org/download/linux/debian/&quot;&gt;install Postgres inside a Debian container&lt;/a&gt;, I stumbled across a file that I didn’t know existed - &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;/etc/os-release&lt;/code&gt; - this file contains a bunch of handy variables which can be sourced to chuck them into your environment as environment variables:&lt;/p&gt;

&lt;div class=&quot;language-sh highlighter-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;PRETTY_NAME&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;Debian GNU/Linux 12 (bookworm)&quot;&lt;/span&gt;
&lt;span class=&quot;nv&quot;&gt;NAME&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;Debian GNU/Linux&quot;&lt;/span&gt;
&lt;span class=&quot;nv&quot;&gt;VERSION_ID&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;12&quot;&lt;/span&gt;
&lt;span class=&quot;nv&quot;&gt;VERSION&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;12 (bookworm)&quot;&lt;/span&gt;
&lt;span class=&quot;nv&quot;&gt;VERSION_CODENAME&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;bookworm
&lt;span class=&quot;nv&quot;&gt;ID&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;debian
&lt;span class=&quot;nv&quot;&gt;HOME_URL&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;https://www.debian.org/&quot;&lt;/span&gt;
&lt;span class=&quot;nv&quot;&gt;SUPPORT_URL&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;https://www.debian.org/support&quot;&lt;/span&gt;
&lt;span class=&quot;nv&quot;&gt;BUG_REPORT_URL&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;https://bugs.debian.org/&quot;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;$VERSION_CODENAME&lt;/code&gt; is particularly handy for building apt repo release strings, but there’s a lot useful stuff in there!&lt;/p&gt;
</description>
        <pubDate>Thu, 04 Sep 2025 00:00:00 +1200</pubDate>
        <link>https://joshmcarthur.com/til/2025/09/04/Handy-access-to-OS-environment-variables-on-Debian.html</link>
        <guid isPermaLink="true">https://joshmcarthur.com/til/2025/09/04/Handy-access-to-OS-environment-variables-on-Debian.html</guid>
        
        
        <category>TIL</category>
        
      </item>
    
      <item>
        <title>How to set up Rails directories for MCP servers</title>
        <description>&lt;p&gt;I’ve recently started on a new Rails application which will define at least one
MCP servers, and potentially 2-3. To support this, I wanted to set up a new
namespace in my &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;app/&lt;/code&gt; folder to contain all the MCP ‘stuff’ - I also do the
same thing for GraphQL mutations, resolvers, queries etc.&lt;/p&gt;

&lt;p&gt;Zeitwerk has made autoloading and eager loading much easier in Rails, but this pattern is still something I always have to figure out - I want an &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;app/mcp&lt;/code&gt; folder, and I want all the constants in that tree to sit within the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;MCP&lt;/code&gt; module.&lt;/p&gt;

&lt;p&gt;So a folder structure like:&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;app/
├── mcp
│   ├── prompts
│   │   └── base_prompt.rb
│   ├── README.md
│   ├── resources
│   │   └── base_resource.rb
│   ├── servers
│   │   └── base_server.rb
│   └── tools
│       └── base_tool.rb
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Defines constants named like &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;MCP::Servers::BaseServer&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;The magic for this is to tell Rails’ autoloader about a new top level directory and the namespace to use:&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;# config/application.rb&lt;/span&gt;
&lt;span class=&quot;c1&quot;&gt;# Autoload and reload these paths in dev, eager load in production&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;autoloaders&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;main&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;push_dir&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;s2&quot;&gt;&quot;app/mcp&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;),&lt;/span&gt;
  &lt;span class=&quot;ss&quot;&gt;namespace: &lt;/span&gt;&lt;span class=&quot;no&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;With this in place, both autoloading, dev reloading and eager loading all work as expected:&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;test_helper&quot;&lt;/span&gt;

&lt;span class=&quot;k&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;MCPAutoloadTest&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&amp;lt;&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;ActiveSupport&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;no&quot;&gt;TestCase&lt;/span&gt;
  &lt;span class=&quot;nb&quot;&gt;test&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;MCP classes are autoloaded correctly&quot;&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;do&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;assert&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;MCP&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;no&quot;&gt;Servers&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;no&quot;&gt;BaseServe&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;assert&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;MCP&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;no&quot;&gt;Tools&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;no&quot;&gt;BaseTool&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;assert&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;MCP&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;no&quot;&gt;Prompts&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;no&quot;&gt;BasePrompt&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;assert&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;MCP&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;no&quot;&gt;Resources&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;no&quot;&gt;BaseResource&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;
</description>
        <pubDate>Thu, 28 Aug 2025 00:00:00 +1200</pubDate>
        <link>https://joshmcarthur.com/til/2025/08/28/Set-up-MCP-directories-for-autoloading-in-Rails.html</link>
        <guid isPermaLink="true">https://joshmcarthur.com/til/2025/08/28/Set-up-MCP-directories-for-autoloading-in-Rails.html</guid>
        
        
        <category>TIL</category>
        
      </item>
    
      <item>
        <title>Bikeshed #470: All about Queues</title>
        <description>&lt;p&gt;I listened to https://bikeshed.thoughtbot.com/470 in the weekend. There was a
fair amount of talking about judoscale, but there was also some interesting
stuff in there about measuring queue performance. A couple of key takeaways for
me:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;It seemed like a really good suggestion to me was to name queues by their SLA,
rather than their purpose - for example, instead of naming queues “emails”,
“exports”, “push_notifications” or whatever, go with when messages should be
handled within - Adam suggested starting with “5 seconds”, “5 minutes” and “5
hours”.&lt;/li&gt;
  &lt;li&gt;Having queues named for SLA gives you a really good metric for monitoring -
messages shouldn’t sit in any of those queues for longer than the time they’re
named for.&lt;/li&gt;
  &lt;li&gt;If you have the infra for it (e.g. Heroku was mentioned), you don’t have to
handle all queues with the same infra - an example they gave was that if you
have a big expensive job that takes loads of resources (memory) - put that job
on it’s own queue and then you can have a dedicated dyno or instance or
whatever that only works on jobs in that queue and is otherwise idle. The idea
being not to waste your expensive compute on trivial jobs&lt;/li&gt;
&lt;/ul&gt;
</description>
        <pubDate>Mon, 25 Aug 2025 00:00:00 +1200</pubDate>
        <link>https://joshmcarthur.com/2025/08/25/Bikeshed-470-All-about-Queues.html</link>
        <guid isPermaLink="true">https://joshmcarthur.com/2025/08/25/Bikeshed-470-All-about-Queues.html</guid>
        
        
      </item>
    
      <item>
        <title>Connecting to a locally Running app with hotwire native</title>
        <description>&lt;hr /&gt;

&lt;p&gt;title: Connecting to a locally-running app with Hotwire Native
category: TIL
–&lt;/p&gt;

&lt;p&gt;I’ve just started a new &lt;a href=&quot;https://native.hotwired.dev&quot;&gt;Hotwire Native&lt;/a&gt; application. The &lt;a href=&quot;https://native.hotwired.dev/android/getting-started&quot;&gt;Getting Started&lt;/a&gt; docs for Android use a deployed domain name, so while the demo app worked just fine, I had a bit of trouble figuring out how to connect to my locally-running web app from an Android emulator. In my case, it was a Rails app, but any other web framework would also have this problem.&lt;/p&gt;

&lt;p&gt;There were two useful things I found:&lt;/p&gt;

&lt;ol&gt;
  &lt;li&gt;Android emulators make your computer available at &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;10.0.2.2&lt;/code&gt;. This means that if you run your web app on port &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;3000&lt;/code&gt;, you can connect in a browser at &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;http://10.0.2.2:3000&lt;/code&gt;. Neat!&lt;/li&gt;
  &lt;li&gt;If you are connecting to insecure origins, you need to flag this in your &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;AndroidManifest.xml&lt;/code&gt; - specifically, you need to add &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;android:usesCleartextTraffic=&quot;true&quot;&lt;/code&gt; to the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;&amp;lt;application&amp;gt;&lt;/code&gt; tag.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;With this configuration in place, I was able to set up Hotwire Native to connect in debug builds to my locally-running web applicaiton, and I was off!&lt;/p&gt;
</description>
        <pubDate>Wed, 06 Aug 2025 00:00:00 +1200</pubDate>
        <link>https://joshmcarthur.com/2025/08/06/Connecting-to-a-locally-running-app-with-Hotwire-Native.html</link>
        <guid isPermaLink="true">https://joshmcarthur.com/2025/08/06/Connecting-to-a-locally-running-app-with-Hotwire-Native.html</guid>
        
        
      </item>
    
      <item>
        <title>A simple redirect to next pattern for Rails</title>
        <description>&lt;p&gt;In Rails controllers, it’s pretty common to redirect somewhere when an action is completed. Sometimes this location is unknown, and therefore straightforward. Other times I just want to redirect ‘back’ - wherever that might have been - &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;redirect_back(fallback_location: wherenver_path)&lt;/code&gt; is perfect for that.&lt;/p&gt;

&lt;p&gt;Occasionally though, I’ll want to determine the next path from the caller - often this will be for some sort of generic or shared action, that might need to go one of several places when it’s done. For that, I like to use this simple &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;redirect_to_next&lt;/code&gt; pattern:&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;module&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;RedirectToNext&lt;/span&gt;
  &lt;span class=&quot;kp&quot;&gt;extend&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;ActiveSupport&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;no&quot;&gt;Concern&lt;/span&gt;

  &lt;span class=&quot;k&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;redirect_to_next&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;fallback_location&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;return&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;redirect_to&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;fallback_location&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;k&quot;&gt;unless&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;next_path&lt;/span&gt;

    &lt;span class=&quot;n&quot;&gt;redirect_to&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;:next&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;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;next_path&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;kp&quot;&gt;nil&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;unless&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;:next&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;nf&quot;&gt;start_with?&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;/&quot;&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;:next&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;c1&quot;&gt;# app/controllers/application_controller.rb&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;ApplicationController&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&amp;lt;&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;ActionController&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;no&quot;&gt;Base&lt;/span&gt;
  &lt;span class=&quot;kp&quot;&gt;include&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;RedirectToNext&lt;/span&gt;

  &lt;span class=&quot;c1&quot;&gt;# in a controller&lt;/span&gt;
  &lt;span class=&quot;k&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;create&lt;/span&gt;
    &lt;span class=&quot;c1&quot;&gt;# ...&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;redirect_to_next&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;ss&quot;&gt;fallback_location: &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;whatever_path&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;ss&quot;&gt;notice: &lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;Created a thing&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;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;This pattern essentially follows the logic of &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;redirect_back&lt;/code&gt;, but sources the redirect location from a param called &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;next&lt;/code&gt;. The param should be a path (though Rails prevents other-host redirects now by default anyway). It splats other args so that anything that can be passed to &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;redirect_to&lt;/code&gt; is forwarded.&lt;/p&gt;

&lt;p&gt;The usage of this is nice and simple. For controllers that are POSTed to directly, I just provide the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;next&lt;/code&gt; param with the path to send to. For &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;new&lt;/code&gt; actions, I include the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;next&lt;/code&gt; param as a hidden field:&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;&lt;span class=&quot;cp&quot;&gt;&amp;lt;%=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;form_with&lt;/span&gt; &lt;span class=&quot;ss&quot;&gt;model: &lt;/span&gt;&lt;span class=&quot;vi&quot;&gt;@widget&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;cp&quot;&gt;%&amp;gt;&lt;/span&gt;
   &lt;span class=&quot;cp&quot;&gt;&amp;lt;%=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;hidden_field_tag&lt;/span&gt; &lt;span class=&quot;ss&quot;&gt;:next&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;whatever_path&lt;/span&gt; &lt;span class=&quot;cp&quot;&gt;%&amp;gt;&lt;/span&gt;
   &lt;span class=&quot;cp&quot;&gt;&amp;lt;%=&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;label&lt;/span&gt; &lt;span class=&quot;ss&quot;&gt;:name&lt;/span&gt; &lt;span class=&quot;cp&quot;&gt;%&amp;gt;&lt;/span&gt;
   &lt;span class=&quot;cp&quot;&gt;&amp;lt;%=&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;text_field&lt;/span&gt; &lt;span class=&quot;ss&quot;&gt;:name&lt;/span&gt; &lt;span class=&quot;cp&quot;&gt;%&amp;gt;&lt;/span&gt;
   &lt;span class=&quot;cp&quot;&gt;&amp;lt;%=&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;submit&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&amp;gt;&lt;/span&gt;
&lt;span class=&quot;o&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;sx&quot;&gt;% end &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;

</description>
        <pubDate>Thu, 24 Jul 2025 00:00:00 +1200</pubDate>
        <link>https://joshmcarthur.com/til/2025/07/24/A-simple-redirect-to-next-pattern-in-Rails.html</link>
        <guid isPermaLink="true">https://joshmcarthur.com/til/2025/07/24/A-simple-redirect-to-next-pattern-in-Rails.html</guid>
        
        
        <category>TIL</category>
        
      </item>
    
      <item>
        <title>How to run a command via AWS SSM with live output</title>
        <description>&lt;p&gt;This little technique is a great example of how simple *nix utilities can be
(mis)used to accomplish some interesting things:&lt;/p&gt;

&lt;div class=&quot;language-sh highlighter-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;(&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;echo&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;./deploy.sh&quot;&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;cat&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;exit&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;exit&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;)&lt;/span&gt; |  &lt;span class=&quot;se&quot;&gt;\&lt;/span&gt;
   aws ssm start-session &lt;span class=&quot;nt&quot;&gt;--target&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;$INSTANCE_ID&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;This:&lt;/p&gt;

&lt;ol&gt;
  &lt;li&gt;Echos the command into the shell session once started (&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;echo &quot;./deploy.sh&quot;&lt;/code&gt; -
obviously whatever command you want to run goes here)&lt;/li&gt;
  &lt;li&gt;Runs &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;cat&lt;/code&gt; to capture any output from that command until the command exits&lt;/li&gt;
  &lt;li&gt;&lt;em&gt;n&lt;/em&gt; &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;exit&lt;/code&gt; calls, where &lt;em&gt;n&lt;/em&gt; is the number of shell sessions you’ve got. I
have two, because SSM by default runs &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;sh&lt;/code&gt;, then I have a Linux profile set
up in SSM that runs &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;bash&lt;/code&gt;. This will drop you back where you started, in
your shell.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;This accomplishes the same result as using &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;send-command&lt;/code&gt; with the built-in
&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;AWS-RunShellScript&lt;/code&gt; document (which is what &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;start-session&lt;/code&gt; uses under the
hood), but shows output of the command in real-time, just like &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;ssh ... -c&lt;/code&gt;
would, without the need to wait for the SSM command to finish executing before
output is visible.&lt;/p&gt;
</description>
        <pubDate>Wed, 23 Jul 2025 00:00:00 +1200</pubDate>
        <link>https://joshmcarthur.com/til/2025/07/23/Run-a-command-via-SSM-with-live-output.html</link>
        <guid isPermaLink="true">https://joshmcarthur.com/til/2025/07/23/Run-a-command-via-SSM-with-live-output.html</guid>
        
        
        <category>TIL</category>
        
      </item>
    
      <item>
        <title>A Stimulus controller to auto-submit authentication codes</title>
        <description>&lt;p&gt;I think this is neat - very succinct. This controller expects an input that
calls &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;formatOnSubmit&lt;/code&gt; on input, which removes invalid chracters, strips the
input (e.g. of spaces), and then automatically submits it. Works great with
pasting, manually typing, and provides fast feedback to users.&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;import&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;Controller&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;from&lt;/span&gt; &lt;span class=&quot;dl&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;@hotwired/stimulus&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;export&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;default&lt;/span&gt; &lt;span class=&quot;kd&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;kd&quot;&gt;extends&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;Controller&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;kd&quot;&gt;static&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;targets&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;input&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;formatAndSubmit&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;event&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;input&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;event&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;target&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;value&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;input&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;nx&quot;&gt;replace&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;sr&quot;&gt;/&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\D&lt;/span&gt;&lt;span class=&quot;sr&quot;&gt;/g&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;c1&quot;&gt;// Remove non-digits&lt;/span&gt;

    &lt;span class=&quot;c1&quot;&gt;// Limit to 6 digits&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;value&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;o&quot;&gt;&amp;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;p&quot;&gt;{&lt;/span&gt;
      &lt;span class=&quot;nx&quot;&gt;value&lt;/span&gt; &lt;span class=&quot;o&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;nx&quot;&gt;slice&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;6&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;input&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;o&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;c1&quot;&gt;// Auto-submit when 6 digits are entered&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;value&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;o&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;p&quot;&gt;{&lt;/span&gt;
      &lt;span class=&quot;nx&quot;&gt;input&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;form&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;requestSubmit&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;I use this controller for things like email and 2FA codes, bound with a simple
bit of markup:&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;form&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;data-controller=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;verification-code&quot;&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;&amp;gt;&lt;/span&gt;
  &lt;span class=&quot;nt&quot;&gt;&amp;lt;input&lt;/span&gt;
    &lt;span class=&quot;na&quot;&gt;type=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;text&quot;&lt;/span&gt;
    &lt;span class=&quot;na&quot;&gt;pattern=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;[0-9]{6}&quot;&lt;/span&gt;
    &lt;span class=&quot;na&quot;&gt;maxlength=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;6&quot;&lt;/span&gt;
    &lt;span class=&quot;na&quot;&gt;autofocus&lt;/span&gt;
    &lt;span class=&quot;na&quot;&gt;required&lt;/span&gt;
    &lt;span class=&quot;na&quot;&gt;data-verification-code-target=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;input&quot;&lt;/span&gt;
    &lt;span class=&quot;na&quot;&gt;data-action=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;input-&amp;gt;verification-code#formatAndSubmit&quot;&lt;/span&gt;
  &lt;span class=&quot;nt&quot;&gt;/&amp;gt;&lt;/span&gt;
  &lt;span class=&quot;nt&quot;&gt;&amp;lt;button&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;type=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;submit&quot;&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;&amp;gt;&lt;/span&gt;Verify Code&lt;span class=&quot;nt&quot;&gt;&amp;lt;/button&amp;gt;&lt;/span&gt;
&lt;span class=&quot;nt&quot;&gt;&amp;lt;/form&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
</description>
        <pubDate>Tue, 22 Jul 2025 00:00:00 +1200</pubDate>
        <link>https://joshmcarthur.com/til/2025/07/22/Stimulus-controller-to-auto-submit-authentication-codes.html</link>
        <guid isPermaLink="true">https://joshmcarthur.com/til/2025/07/22/Stimulus-controller-to-auto-submit-authentication-codes.html</guid>
        
        
        <category>TIL</category>
        
      </item>
    
      <item>
        <title>Open-sourcing a reference implementation of native iOS Google sign-in flow with Hotwire</title>
        <description>&lt;p&gt;I’ve just open sourced a reference implementation of native iOS Google sign-in
using &lt;a href=&quot;https://native.hotwired.dev/&quot;&gt;Hotwire Native&lt;/a&gt;. I developed this flow when
building &lt;a href=&quot;https://virtualtrails.app&quot;&gt;Virtualtrails&lt;/a&gt;, where I wanted to support
both web and native authentication flows for Google Sign-in. Using Hotwire for
this worked great - when native support is available, it’s automatically used.
When not, it falls back to a web-based OAuth flow.&lt;/p&gt;

&lt;p&gt;https://github.com/joshmcarthur/hotwire-native-google-sign-in&lt;/p&gt;
</description>
        <pubDate>Tue, 22 Jul 2025 00:00:00 +1200</pubDate>
        <link>https://joshmcarthur.com/2025/07/22/Open-sourcing-native-iOS-Google-sign-in-flow-with-Hotwire.html</link>
        <guid isPermaLink="true">https://joshmcarthur.com/2025/07/22/Open-sourcing-native-iOS-Google-sign-in-flow-with-Hotwire.html</guid>
        
        
      </item>
    
      <item>
        <title>About Trove Project</title>
        <description>&lt;p&gt;I’ve been working this week on Trove
(&lt;a href=&quot;http://github.com/joshmcarthur/trove-project/&quot;&gt;Github&lt;/a&gt;,
&lt;a href=&quot;https://joshmcarthur.com/trove-project&quot;&gt;Docs&lt;/a&gt;). Trove is meant to be a
self-hosted system that can store arbitrary (but schema validated) JSON data,
files, and relationships between events. By events I mean things like:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;Bookmarks&lt;/li&gt;
  &lt;li&gt;Photos I like&lt;/li&gt;
  &lt;li&gt;Colours/patterns I’ve seen out and about that I like&lt;/li&gt;
  &lt;li&gt;Storypark updates (from daycare/school)&lt;/li&gt;
  &lt;li&gt;etc.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Basically all the digital miscellanery that I want to hang on to, but currently
exists as a google keep note, or read email that I don’t ever archive. I don’t
plan on trying to replicate services I already use like Google Photos, Google
Drive, iCloud, and the like, but rather might cherry-pick particular bits of
content from those systems to hang onto separately.&lt;/p&gt;

&lt;p&gt;I’ve just completed the core, which is basically an event bus with a plugin
system. Plugins (will be able to) register new schemas (event types), web/API
endpoints, or the ability to process specific event types. Processing like:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;Screenshotting a bookmarked website/extracting a MHTML archive&lt;/li&gt;
  &lt;li&gt;Label/OCR/face detection on images&lt;/li&gt;
  &lt;li&gt;Thumbnailing images&lt;/li&gt;
  &lt;li&gt;Generating colour palettes from a saved color&lt;/li&gt;
  &lt;li&gt;Extracting and saving attachments from an email&lt;/li&gt;
  &lt;li&gt;Extracting a note from an email&lt;/li&gt;
  &lt;li&gt;Visiting a link from an email and scraping the resulting page&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;This particular project is massively ambitious, but it’s also a good exercise in
trying to stay really focussed on a small core and try and architect it so that
I can achieve all of the above, and any other ideas I have, just using the
plugin system. I’m taking a lot of inspiration from HomeAssistant in being very
modular and trying to be very adaptable. I’m using Deno, which is an alternative
JS/TS runtime to Node, and I’m feeling really good about that. I’m doing my best
to not have any dependencies outside the Deno standard library, and automating
builds and releases. I want self-hosted data I put in to be safe and private to
me, but it’s also a fun and challenging constraint to think about designing
Trove to be able to be useful 10 years from now without a tonne of maintenance
intervention.&lt;/p&gt;

&lt;p&gt;I don’t have a stable release yet, but I will soon. Right now, the core works as
a library, but I’ll be setting it up as a precompiled CLI program and setting up
Github releases to distribute Mac OS/Windows/Linux binaries and Docker images.
The CLI program will accept a typescript configuration file which determines
what plugins to load, what configuration to pass to those plugins, and how to
store events, files, and event links (which can be a single storage, or an
individual storage for each).&lt;/p&gt;

&lt;p&gt;I also have plans to create an API (JSON and JSON-over websocket), and a web and
native frontend. I’ll be utilising plugins for each of these, and don’t expect
to need to extend the core. This means that if myself, or someone else does not
want or need a web frontend, or a native frontend, or an API, that plugin can
just be omitted from the configuration. I’m also expecting API and web core
plugins to allow other plugins to build on them, allowing for authentication,
middleware, and routing within plugins to create new capabilities without having
to extend the core.&lt;/p&gt;
</description>
        <pubDate>Fri, 04 Apr 2025 00:00:00 +1300</pubDate>
        <link>https://joshmcarthur.com/2025/04/04/About-Trove.html</link>
        <guid isPermaLink="true">https://joshmcarthur.com/2025/04/04/About-Trove.html</guid>
        
        
      </item>
    
      <item>
        <title>OpenNext on Cloudflare</title>
        <description>&lt;p&gt;I’m trying out OpenNext with Cloudflare. One kind of annoying thing already I’ve run into is that libraries that use cross-fetch end up running into errors, because OpenNext patches the deployment package enough that cross-fetch tries to use Node libraries, when actually it can just use native fetch.&lt;/p&gt;

&lt;p&gt;In the case I ran into, I could work around this by performing a native fetch, then processing the response. Alternatively, I can use patch-package to resolve cross-fetch’s fetch to the built-in version.&lt;/p&gt;

</description>
        <pubDate>Mon, 03 Mar 2025 00:00:00 +1300</pubDate>
        <link>https://joshmcarthur.com/til/2025/03/03/OpenNext-on-Cloudflare.html</link>
        <guid isPermaLink="true">https://joshmcarthur.com/til/2025/03/03/OpenNext-on-Cloudflare.html</guid>
        
        
        <category>TIL</category>
        
      </item>
    
      <item>
        <title>Hutt Forks/Eastern Hutt Hut</title>
        <description>&lt;p&gt;I tried to get to Eastern Hutt Hut on Friday. Didn’t quite make it, just because I’d committed to a half day rather than full day trip, but it seemed doable. I turned around &lt;a href=&quot;https://www.topomap.co.nz/NZTopoMap?v=2&amp;amp;ll=-41.037834,175.221848&amp;amp;z=15&quot;&gt;here&lt;/a&gt; with 2km to go, it took me 2 hours to get there after biking over from Kaitoke on the 4WD road - 1 hour biking, just over 1 hour walk/running. It felt like another 1-1.5 hours to the hut in the riverbed or game trails. Definitely no track, so it’s a case of bush bashing or rock hopping in the river.&lt;/p&gt;

&lt;p&gt;Easter Hutt Hut is for emergency use only, since it’s in or near the water catchment area - visitors are asked to clear the area in a day, rather than staying.&lt;/p&gt;

&lt;iframe width=&quot;1200&quot; height=&quot;960&quot; frameborder=&quot;0&quot; scrolling=&quot;no&quot; marginheight=&quot;0&quot; marginwidth=&quot;0&quot; src=&quot;https://www.topomap.co.nz/NZTopoMapEmbedded?v=2&amp;amp;ll=-41.031424,175.238414&amp;amp;z=15&amp;amp;pin=1&amp;amp;lbl=Turned%20here&quot;&gt;&lt;/iframe&gt;
&lt;p&gt;&lt;br /&gt;&lt;small&gt;&lt;a href=&quot;https://www.topomap.co.nz/NZTopoMap?v=2&amp;amp;ll=-41.031424,175.238414&amp;amp;z=15&amp;amp;pin=1&amp;amp;lbl=Turned%20here&quot; style=&quot;text-align:left&quot;&gt;View Larger Topographic Map&lt;/a&gt;&lt;/small&gt;&lt;/p&gt;
</description>
        <pubDate>Sun, 09 Feb 2025 00:00:00 +1300</pubDate>
        <link>https://joshmcarthur.com/trip-reports/2025/02/09/hutt-forkseastern-hutt-hut.html</link>
        <guid isPermaLink="true">https://joshmcarthur.com/trip-reports/2025/02/09/hutt-forkseastern-hutt-hut.html</guid>
        
        <category>trip-report</category>
        
        
        <category>trip-reports</category>
        
      </item>
    
      <item>
        <title>Restart rails quickly with bin/rails restart</title>
        <description>&lt;p&gt;Just a quick tip! If you’re like me, you start with just one or two tabs (or
tmux panes), and things get more and more disorganised as the day goes on. If
you need to restart your Rails server quickly, and don’t feel like hunting out
your Rail server terminal, no worries! Just running &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;bin/rails restart&lt;/code&gt; will
signal the server process, wherever it’s running, to restart itself. This can
also be used on a deployment server, to restart the server process without
actually knowing what the process is (though you should really be using proper
process/service manager on a server!).&lt;/p&gt;

&lt;p&gt;Technically, this also means that if you really don’t want your server visible
in your terminal, you &lt;em&gt;could&lt;/em&gt; also run the server daemonised (e.g. in the
background), and just restart it when you need.&lt;/p&gt;
</description>
        <pubDate>Thu, 16 Jan 2025 00:00:00 +1300</pubDate>
        <link>https://joshmcarthur.com/2025/01/16/bin-rails-restart.html</link>
        <guid isPermaLink="true">https://joshmcarthur.com/2025/01/16/bin-rails-restart.html</guid>
        
        
      </item>
    
      <item>
        <title>Check that an enumerable satisfies matchers with args in RSpec</title>
        <description>&lt;blockquote&gt;
  &lt;p&gt;Note: I discovered this API based on a suggestion from an AI editor assistant, just to disclaim that.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;When creating an RSpec test against an enumerable, &lt;a href=&quot;https://gem.wtf/rubocop-rspec&quot;&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;rubocop-rspec&lt;/code&gt;&lt;/a&gt; will enforce that the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;all&lt;/code&gt; matcher be used instead:&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;only returns odd numbers&quot;&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;do&lt;/span&gt;
  &lt;span class=&quot;n&quot;&gt;odd&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;mi&quot;&gt;3&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;n&quot;&gt;expect&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;odd&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;n&quot;&gt;be_odd&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;This is fine for a matcher that doesn’t require any args be passed to it, but what do we do in the case where we &lt;em&gt;do&lt;/em&gt; require args?&lt;/p&gt;

&lt;p&gt;Take this 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;it&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;shows the family member names on the page&quot;&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;do&lt;/span&gt;
  &lt;span class=&quot;n&quot;&gt;family_names&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;sx&quot;&gt;%w[Alice Bob Kenny]&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;family_names&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;n&quot;&gt;have_content&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;Now that fails, because, quite rightly, &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;has_content?&lt;/code&gt; expects an argument. So how do we pass an argument to the matcher? We can use &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;satisfy&lt;/code&gt;, a block matcher:&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;shows the family member names on the page&quot;&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;do&lt;/span&gt;
  &lt;span class=&quot;n&quot;&gt;family_names&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;sx&quot;&gt;%w[Alice Bob Kenny]&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;family_names&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;n&quot;&gt;satisfy&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;name&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;|&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;page&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;has_content?&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;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;Using matchers like this means that your test output will be most useful on failure. Instead of a &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;.each&lt;/code&gt; “failing fast” when it encounters an item that doesn’t pass the test,
RSpec will report on &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;all&lt;/code&gt; more comprehensively:&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;  expected [&quot;Alice&quot;, &quot;Bob&quot;, &quot;Kenny&quot;, &quot;Quentin&quot;] to all satisfy expression `has_content?(page_name)`

          object at index 3 failed to match:
             expected &quot;Quentin&quot; to satisfy expression `has_content?(page_name)`
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;There are lots of other checks you can do within &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;satisfy&lt;/code&gt;: https://rspec.info/features/3-12/rspec-expectations/built-in-matchers/satisfy/&lt;/p&gt;
</description>
        <pubDate>Wed, 15 Jan 2025 00:00:00 +1300</pubDate>
        <link>https://joshmcarthur.com/2025/01/15/Check-that-a-enumerable-satisfies-matchers-with-args-in-RSpec.html</link>
        <guid isPermaLink="true">https://joshmcarthur.com/2025/01/15/Check-that-a-enumerable-satisfies-matchers-with-args-in-RSpec.html</guid>
        
        
      </item>
    
      <item>
        <title>Detecting the origin of an ActiveRecord record&apos;s destruction</title>
        <description>&lt;p&gt;In a &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;before|after_destroy&lt;/code&gt; callback, you can use the
&lt;a href=&quot;https://api.rubyonrails.org/classes/ActiveRecord/AutosaveAssociation.html#method-i-destroyed_by_association&quot;&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;destroyed_by_association&lt;/code&gt;&lt;/a&gt;
method to access the association (therefore the parent record or parent record
type) that is causing this record to be destroyed. This is also a handy way to
tell the difference betwee&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt; a ‘direct’ destruction (&lt;/code&gt;widget.destroy&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;), and a
‘dependent’ destruction &lt;/code&gt;has_many :widgets, dependent: :destroy`).&lt;/p&gt;

&lt;p&gt;I had a use case for this recently with a &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;before_destroy&lt;/code&gt; callback intended to
prevent a record from being ‘orphaned’ - that is, a record that should always
have at least one of something, where destroying the record would cause it to
have none.&lt;/p&gt;

&lt;p&gt;I implemeneted a ‘validating’ callback:&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;before_destroy&lt;/span&gt; &lt;span class=&quot;ss&quot;&gt;:prevent_orphaned_record&lt;/span&gt;

&lt;span class=&quot;kp&quot;&gt;private&lt;/span&gt;

&lt;span class=&quot;k&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;prevent_orphaned_record&lt;/span&gt;
  &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;kp&quot;&gt;true&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;unless&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;parent&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;children&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;size&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;n&quot;&gt;errors&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;add&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;ss&quot;&gt;:base&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;ss&quot;&gt;:destroy_would_orphan_record&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;

  &lt;span class=&quot;kp&quot;&gt;throw&lt;/span&gt; &lt;span class=&quot;ss&quot;&gt;:abort&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 problem I have with this, is that when the parent record has an assocaition
with dependent destroy, we don’t actually want this validation to be triggered,
since, while we are orphaning the record, we are also about to destroy the
parent. An example of an association like this would be:&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;has_many&lt;/span&gt; &lt;span class=&quot;ss&quot;&gt;:children&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;ss&quot;&gt;dependent: :destroy&lt;/span&gt;
&lt;span class=&quot;c1&quot;&gt;# or&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;has_one&lt;/span&gt; &lt;span class=&quot;ss&quot;&gt;:child&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;ss&quot;&gt;dependent: :destroy&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;What we want to do, is guard the callback, so that if the &lt;em&gt;parent&lt;/em&gt; record is
being destroyed, we can allow the record to be orphaned, since it’s about to be
destroyed. I tried a few different variations, but eventually found
&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;destroyed_by_association&lt;/code&gt;. This method returns the &lt;em&gt;association&lt;/em&gt;, which allows
access to the parent record, as well as the parent record class.&lt;/p&gt;

&lt;p&gt;With this method, the guard clause can easily be implemented:&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;prevent_orphaned_record&lt;/span&gt;
  &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;kp&quot;&gt;true&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;destroyed_by_association&lt;/span&gt; &lt;span class=&quot;c1&quot;&gt;# Could also check the specific parent here, etc.&lt;/span&gt;
  &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;kp&quot;&gt;true&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;unless&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;parent&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;children&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;size&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;c1&quot;&gt;# etc&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;
</description>
        <pubDate>Tue, 14 Jan 2025 00:00:00 +1300</pubDate>
        <link>https://joshmcarthur.com/2025/01/14/Detect-in-a-destroy-callback-if-the-record-is-being-destroyed-by-association.html</link>
        <guid isPermaLink="true">https://joshmcarthur.com/2025/01/14/Detect-in-a-destroy-callback-if-the-record-is-being-destroyed-by-association.html</guid>
        
        
      </item>
    
      <item>
        <title>How to use assigns() in a Rails request spec without a dependency</title>
        <description>&lt;p&gt;Several years ago, the Rails core team &lt;a href=&quot;https://github.com/rails/rails/issues/18950&quot;&gt;advised&lt;/a&gt; that controller tests/specs were
being discouraged, in favour of request specs. The basis for this was that
controller tests had more visibility into the internals of the controller, and
were not purely testing the HTTP interaction as request specs were.&lt;/p&gt;

&lt;p&gt;This is valid, but one method I end up missing a lot from controller specs is &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;assigns&lt;/code&gt; - as in (&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;expect(assigns(:widget)).to be_published&lt;/code&gt;).
While the assertions available to controller tests are easily installable as a &lt;a href=&quot;https://github.com/rails/rails-controller-testing&quot;&gt;gem&lt;/a&gt;, getting the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;assigns&lt;/code&gt; method back is super simple.&lt;/p&gt;

&lt;p&gt;It’s pretty common to put support modules into place - for example, in &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;spec|test/support/request_spec_helpers.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;k&quot;&gt;module&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;RequestSpecHelpers&lt;/span&gt;
  &lt;span class=&quot;k&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;assigns&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;key&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;controller&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;view_assigns&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;key&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;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;In Test::Unit, this can just be included into your &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;ActionDispatch::IntegrationTest&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;WidgetRequestTest&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&amp;lt;&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;ActionDispatch&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;no&quot;&gt;IntegrationTest&lt;/span&gt;
  &lt;span class=&quot;kp&quot;&gt;include&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;RequestSpecHelpers&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;And in RSpec, it can be included with config:&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;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;include&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;RequestSpecHelpers&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;ss&quot;&gt;type: :request&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;
</description>
        <pubDate>Fri, 10 Jan 2025 00:00:00 +1300</pubDate>
        <link>https://joshmcarthur.com/2025/01/10/Use-assigns()-in-a-Rails-request-spec-without-adding-a-dependency-on-rails-controller-testing.html</link>
        <guid isPermaLink="true">https://joshmcarthur.com/2025/01/10/Use-assigns()-in-a-Rails-request-spec-without-adding-a-dependency-on-rails-controller-testing.html</guid>
        
        
      </item>
    
      <item>
        <title>Generating ActionMailer Previews inside a transaction</title>
        <description>&lt;p&gt;&lt;a href=&quot;https://guides.rubyonrails.org/action_mailer_basics.html#previewing-emails&quot;&gt;ActionMailer Previews&lt;/a&gt;
are a great way to provide some living documentation in the form of rendered
emails. They’re also great for testing.&lt;/p&gt;

&lt;p&gt;Often, in order to generate a preview, you might need to create a database
record - perhaps using fixtures, or a tool like
&lt;a href=&quot;https://github.com/thoughtbot/factory_bot&quot;&gt;factory_bot&lt;/a&gt;. When you do this, you
might be surprised to see these records then show up and remain in your
database. This is because previews are not generated inside a rollback
transaction, so once they are in your database, they stay there.&lt;/p&gt;

&lt;p&gt;To avoid this, I have a small patch to Rails which I apply using an initializer
to all the projects I work on that need previews:&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;module&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;RollbackingAfterPreview&lt;/span&gt;
  &lt;span class=&quot;k&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;preview&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;Base&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;transaction&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;do&lt;/span&gt;
      &lt;span class=&quot;k&quot;&gt;super&lt;/span&gt;
      &lt;span class=&quot;k&quot;&gt;raise&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;Rollback&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;no&quot;&gt;Rails&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;application&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;config&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;to_prepare&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;do&lt;/span&gt;
  &lt;span class=&quot;k&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;Rails::MailersController&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;prepend&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;RollbackingAfterPreview&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;It simply wraps the preview action of the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Rails::MailersController&lt;/code&gt; in a
transaction which is rolled back after the action executes. This means that any
database updates made in the process of generating the preview are rolled back,
and not committed to the database.&lt;/p&gt;

&lt;p&gt;I’ve been interested in committing this to Rails, as I think it’s a reasonable
default behaviour to have. Unfortunately, when I tried to dig into it, I found
that I couldn’t quite get my head around how to test the transactional part of
the patch, so it’s remained something that I just patch myself when I need it.
Maybe it’s useful to someone else.&lt;/p&gt;
</description>
        <pubDate>Thu, 09 Jan 2025 00:00:00 +1300</pubDate>
        <link>https://joshmcarthur.com/2025/01/09/Generate-ActionMailer-Previews-inside-a-transaction.html</link>
        <guid isPermaLink="true">https://joshmcarthur.com/2025/01/09/Generate-ActionMailer-Previews-inside-a-transaction.html</guid>
        
        
      </item>
    
      <item>
        <title>Asserting method calls with Minitest</title>
        <description>&lt;p&gt;Minitest has support for stubbing via
&lt;a href=&quot;https://www.rubydoc.info/gems/minitest/Minitest/Mock&quot;&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Minitest::Mock&lt;/code&gt;&lt;/a&gt;. This
is a good fit for providing fake results to a caller, but what about asserting
that a method is actually called?&lt;/p&gt;

&lt;p&gt;To do this, you can leverage the fact that if the stubbed value is callable, it
will be called. This means that you can provide a mock, then expect that the
mock will receive &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;call&lt;/code&gt; with the expected args (and return the return value
that you need):&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;perform_job&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;Minitest&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;no&quot;&gt;Mock&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;c1&quot;&gt;# expect(method name, return value, [args])&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;perform_job&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;expect&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;ss&quot;&gt;:call&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;kp&quot;&gt;nil&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;my_job_arg&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;])&lt;/span&gt;
&lt;span class=&quot;no&quot;&gt;SlowJob&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;stub&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;ss&quot;&gt;:perform_now&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;perform_job&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;method_that_triggers_slow_job&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;

&lt;span class=&quot;n&quot;&gt;perform_job&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;verify&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

</description>
        <pubDate>Sat, 05 Oct 2024 00:00:00 +1300</pubDate>
        <link>https://joshmcarthur.com/2024/10/05/Asserting-method-calls-with-Minitest.html</link>
        <guid isPermaLink="true">https://joshmcarthur.com/2024/10/05/Asserting-method-calls-with-Minitest.html</guid>
        
        
      </item>
    
      <item>
        <title>Converting geofiles to WKT using ogr2ogr</title>
        <description>&lt;p&gt;I &lt;em&gt;love&lt;/em&gt; gdal, particularly ogr2ogr. Like &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;ffmpeg&lt;/code&gt;, and &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;convert&lt;/code&gt;, it’s just one
of those convert anything to anything commands that is so handy to know about.&lt;/p&gt;

&lt;p&gt;ogr2ogr is part of the GDAL library, and is particularly suited to converting
files containing geospatial data from one source to another. I often use this
tool to convert shapefiles into GeoJSON for example, load data into Spatialite
or PostGIS, or use query and filter operations to reduce a result set into a
smaller file.&lt;/p&gt;

&lt;p&gt;I built, and operate, an application (web + iOS) called
&lt;a href=&quot;https://virtualtrails.app&quot;&gt;Virtualtrails&lt;/a&gt;. This application integrates with a
bunch of fitness platforms to track the distance travelled by walking, running,
or using a wheelchair, and then superimposes that distance onto a trail from New
Zealand and around the world. Along the way, points of interest are virtually
reached and unlocked, and so it’s a really fun way to both meet fitness
objectives and find out more about a trail.&lt;/p&gt;

&lt;p&gt;I create all the trail content myself, but often trail geospatial paths need to
get stitched together from a number of sources. Sometimes they are published in
geospatial formats already (typically a GPX or KML) file, other times I need to
trace the route myself to get the geospatial data. Unsurprisingly, with route
data coming from so many sources, &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;ogr2ogr&lt;/code&gt; regularly features in my route prep
process.&lt;/p&gt;

&lt;p&gt;Today I found a nice shortcut to prepare route data, which is to leverage an
option to &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;ogr2ogr&lt;/code&gt;’s CSV format export that exports data in well-known text
(WKT). WKT is particuarly useful to me because it’s a text format which
&lt;a href=&quot;https://gem.wtf/rgeo&quot;&gt;rgeo&lt;/a&gt; natively supports - I use rgeo to transform
geospatial data to and from Ruby objects, including database persistence in
native PostGIS types. Before I discovered this option, I used to have to
manually apply some find and replace operations in my editor to convert a format
that was &lt;em&gt;close&lt;/em&gt; to WKT like GeoJSON or KML to WKT.&lt;/p&gt;

&lt;p&gt;The option is passed using &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;-lco&lt;/code&gt;, which are format input flags. Here’s an
example:&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;ogr2ogr &lt;span class=&quot;nt&quot;&gt;-f&lt;/span&gt; CSV output.csv input[.geojson, .kml, .shp, etc] &lt;span class=&quot;nt&quot;&gt;-lco&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;GEOMETRY&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;AS_WKT
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;The output CSV will export one row per feature, with a header column of ‘WKT’.
If your input format features have attributes, these will also be included as
features.&lt;/p&gt;

&lt;pre&gt;&lt;code class=&quot;language-csv&quot;&gt;WKT
&quot;LINESTRING Z (174.235252414479 -41.0947374817005 0,174.234414785847 -41.0946484189218 0,174.2339359575 .... )&quot;
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;Because this process is part of ogr2ogr, it’s also no longer necessary for me to
convert from a complex format like a shapefile to a text format like GeoJSON -
because ogr2ogr is converting to CSV anyway, it will accept any input file it
supports (and it supports &lt;em&gt;loads&lt;/em&gt;) - this can include operations like filtering,
stripping or converting attributes, or reprojection, if I need.&lt;/p&gt;

&lt;p&gt;A handy find!&lt;/p&gt;
</description>
        <pubDate>Thu, 05 Sep 2024 00:00:00 +1200</pubDate>
        <link>https://joshmcarthur.com/2024/09/05/Convert-geofiles-to-WKT-using-ogr2ogr.html</link>
        <guid isPermaLink="true">https://joshmcarthur.com/2024/09/05/Convert-geofiles-to-WKT-using-ogr2ogr.html</guid>
        
        
      </item>
    
      <item>
        <title>Tapping targets with Stimulus</title>
        <description>&lt;p&gt;Normally I would use Bootstrap’s &lt;a href=&quot;https://getbootstrap.com/docs/5.3/helpers/stretched-link&quot;&gt;stretched
link&lt;/a&gt; behaviour to
implement functionality where clicking anywhere on a containing element triggers
a link element. This is useful for cards, menus, and other cases where a
‘primary’ link can benefit from a larger tap target.&lt;/p&gt;

&lt;p&gt;Bootstrap’s stretched link works by adding an &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;::after&lt;/code&gt; pseudo element to the
link element with the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;stretched-link&lt;/code&gt; class, then using absolute positioning to
position the pseudoelement across the container. The ‘container’ requires some
definition since it depends on the normal requirements for an
absolute-positioned container - Bootstrap’s &lt;a href=&quot;https://getbootstrap.com/docs/5.3/helpers/stretched-link&quot;&gt;docs on identifying the containing
block&lt;/a&gt; are useful
here.&lt;/p&gt;

&lt;p&gt;In my case, I couldn’t quite get this to work. I was implementing a card with a
dropdown menu of actions, of which one action was the primary one. I wanted to
support functionality to allow users to tap anywhere on the card to trigger the
primary action, unless the dropdown menu was triggered.&lt;/p&gt;

&lt;p&gt;To resolve this, I created what I thought would be a small Stimulus controller
to listen for click events on a containing element, and trigger a click on the
element:&lt;/p&gt;

&lt;div class=&quot;language-typescript highlighter-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;import&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;Controller&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;from&lt;/span&gt; &lt;span class=&quot;dl&quot;&gt;&apos;&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;@hotwired/stimulus&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&apos;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;

&lt;span class=&quot;k&quot;&gt;export&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;default&lt;/span&gt; &lt;span class=&quot;kd&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;TappableTargetController&lt;/span&gt; &lt;span class=&quot;kd&quot;&gt;extends&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;Controller&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;static&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;readonly&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;targets&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;&apos;&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;receiver&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&apos;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;];&lt;/span&gt;
  &lt;span class=&quot;k&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;readonly&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;receiverTarget&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;HTMLElement&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;nx&quot;&gt;connect&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;():&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;void&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;this&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;classList&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;dl&quot;&gt;&apos;&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;cursor-pointer&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&apos;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;this&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;addEventListener&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&apos;&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;click&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&apos;&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;evt&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;this&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;receiverTarget&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;click&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;This seemed to work great! The element had the correct cursor style, and when
clicked, triggered the primary action. Then I tried to open the dropdown menu to
check other actions - oops - this click was also captured, so the dropdown menu
was no longer expandable!&lt;/p&gt;

&lt;p&gt;I used Claude 3.5 to create a more complicated version to handle this case:&lt;/p&gt;

&lt;div class=&quot;language-typescript highlighter-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;import&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;Controller&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;from&lt;/span&gt; &lt;span class=&quot;dl&quot;&gt;&apos;&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;@hotwired/stimulus&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&apos;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;

&lt;span class=&quot;k&quot;&gt;export&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;default&lt;/span&gt; &lt;span class=&quot;kd&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;TappableTargetController&lt;/span&gt; &lt;span class=&quot;kd&quot;&gt;extends&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;Controller&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;static&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;readonly&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;targets&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;&apos;&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;receiver&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&apos;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;];&lt;/span&gt;
  &lt;span class=&quot;k&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;readonly&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;receiverTarget&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;HTMLElement&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;nx&quot;&gt;connect&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;():&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;void&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;this&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;classList&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;dl&quot;&gt;&apos;&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;cursor-pointer&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&apos;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;this&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;addEventListener&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&apos;&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;click&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&apos;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;handleClick&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;bind&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;this&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;private&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;handleClick&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;event&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;MouseEvent&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;):&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;void&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;target&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;event&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;target&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;as&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;HTMLElement&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;

    &lt;span class=&quot;c1&quot;&gt;// Check if the clicked element is the controller element itself&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;target&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;===&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;this&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;span class=&quot;k&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;receiverTarget&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;click&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;c1&quot;&gt;// Check if the clicked element is a button or anchor tag&lt;/span&gt;
      &lt;span class=&quot;kd&quot;&gt;const&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;isInteractableElement&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;target&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;matches&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&apos;&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;button, a, [role=&quot;button&quot;]&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&apos;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;

      &lt;span class=&quot;c1&quot;&gt;// If it&apos;s not an interactable element, trigger the receiver&apos;s click&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;isInteractableElement&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;this&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;receiverTarget&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;click&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;// If it is an interactable element, let the default behavior occur&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 nearly works, but in my case, the dropdown menu displays an overflow icon
using an SVG. The variant above seemed that it would work, but the event is
triggered on the SVG, not the button, so is not counted as a click on an
interactable element. I knew what I’d need to do - I’d need to consider the
target’s ancestors - but again, one of those fiddly things that I reached for
Claude for the next iteration:&lt;/p&gt;

&lt;div class=&quot;language-typescript highlighter-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;import&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;Controller&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;from&lt;/span&gt; &lt;span class=&quot;dl&quot;&gt;&apos;&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;@hotwired/stimulus&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&apos;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;

&lt;span class=&quot;k&quot;&gt;export&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;default&lt;/span&gt; &lt;span class=&quot;kd&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;TappableTargetController&lt;/span&gt; &lt;span class=&quot;kd&quot;&gt;extends&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;Controller&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;static&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;readonly&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;targets&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;&apos;&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;receiver&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&apos;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;];&lt;/span&gt;
  &lt;span class=&quot;k&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;readonly&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;receiverTarget&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;HTMLElement&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;nx&quot;&gt;connect&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;():&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;void&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;this&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;classList&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;dl&quot;&gt;&apos;&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;cursor-pointer&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&apos;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;this&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;addEventListener&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;
      &lt;span class=&quot;dl&quot;&gt;&apos;&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;click&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&apos;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
      &lt;span class=&quot;k&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;_handleClick&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;bind&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;as&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;EventListenerOrEventListenerObject&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;private&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;_handleClick&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;event&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;MouseEvent&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;):&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;void&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;target&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;event&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;target&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;as&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;HTMLElement&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;

    &lt;span class=&quot;c1&quot;&gt;// Check if the clicked element or any of its ancestors up to the controller element is interactable&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;k&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;_isOrHasInteractableAncestor&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nx&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;c1&quot;&gt;// Let the default behavior occur for interactable elements&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;// If we&apos;ve reached here, it means the click was not on an interactable element&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;receiverTarget&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;click&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;private&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;_isOrHasInteractableAncestor&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;HTMLElement&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;):&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;boolean&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;currentElement&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;HTMLElement&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;|&lt;/span&gt; &lt;span class=&quot;kc&quot;&gt;null&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;k&quot;&gt;while&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;currentElement&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;currentElement&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;!==&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;this&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;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;_isInteractableElement&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;currentElement&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;kc&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;span class=&quot;nx&quot;&gt;currentElement&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;currentElement&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;parentElement&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;kc&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;k&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;_isInteractableElement&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;HTMLElement&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;):&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;boolean&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;nx&quot;&gt;element&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;matches&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&apos;&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;button, a, [role=&quot;button&quot;], [tabindex=&quot;0&quot;]&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&apos;&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 version works, but doesn’t handle an important user experience
consideration - it should be possible to use a standard keyboard shortcut to
open the link in a new tab. This is usually done by holding the Command
(Control) key down while clicking the link. The final controller iteration
handles this case:&lt;/p&gt;

&lt;div class=&quot;language-typescript highlighter-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;import&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;Controller&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;from&lt;/span&gt; &lt;span class=&quot;dl&quot;&gt;&apos;&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;@hotwired/stimulus&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&apos;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;

&lt;span class=&quot;k&quot;&gt;export&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;default&lt;/span&gt; &lt;span class=&quot;kd&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;TappableTargetController&lt;/span&gt; &lt;span class=&quot;kd&quot;&gt;extends&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;Controller&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;static&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;targets&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;&apos;&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;receiver&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&apos;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;];&lt;/span&gt;
  &lt;span class=&quot;k&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;readonly&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;receiverTarget&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;HTMLElement&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;nx&quot;&gt;connect&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;():&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;void&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;this&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;classList&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;dl&quot;&gt;&apos;&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;cursor-pointer&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&apos;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;this&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;addEventListener&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;
      &lt;span class=&quot;dl&quot;&gt;&apos;&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;click&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&apos;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
      &lt;span class=&quot;k&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;_handleClick&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;bind&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;as&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;EventListenerOrEventListenerObject&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;private&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;_handleClick&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;event&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;MouseEvent&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;):&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;void&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;target&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;event&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;target&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;as&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;HTMLElement&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;

    &lt;span class=&quot;c1&quot;&gt;// Check if the clicked element or any of its ancestors up to the controller element is interactable&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;k&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;_isOrHasInteractableAncestor&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nx&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;c1&quot;&gt;// Let the default behavior occur for interactable elements&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;// Prevent the default action&lt;/span&gt;
    &lt;span class=&quot;nx&quot;&gt;event&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;preventDefault&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;();&lt;/span&gt;

    &lt;span class=&quot;c1&quot;&gt;// If Ctrl key (or Cmd key on Mac) is pressed, open in new tab&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;event&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;ctrlKey&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;||&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;event&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;metaKey&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;this&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;openInNewTab&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;c1&quot;&gt;// Otherwise, perform the regular click action&lt;/span&gt;
      &lt;span class=&quot;k&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;receiverTarget&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;click&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;private&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;_isOrHasInteractableAncestor&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;HTMLElement&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;):&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;boolean&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;currentElement&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;HTMLElement&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;|&lt;/span&gt; &lt;span class=&quot;kc&quot;&gt;null&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;k&quot;&gt;while&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;currentElement&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;currentElement&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;!==&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;this&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;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;_isInteractableElement&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;currentElement&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;kc&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;span class=&quot;nx&quot;&gt;currentElement&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;currentElement&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;parentElement&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;kc&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;k&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;_isInteractableElement&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;HTMLElement&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;):&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;boolean&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;nx&quot;&gt;element&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;matches&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&apos;&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;button, a, [role=&quot;button&quot;], [tabindex=&quot;0&quot;]&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&apos;&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;private&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;_openInNewTab&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;():&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;void&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;c1&quot;&gt;// Assuming receiverTarget is an anchor element&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;k&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;receiverTarget&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;instanceof&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;HTMLAnchorElement&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;window&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;open&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;receiverTarget&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;href&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;dl&quot;&gt;&apos;&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;_blank&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&apos;&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;c1&quot;&gt;// If it&apos;s not an anchor element, log a warning&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;warn&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;
        &lt;span class=&quot;dl&quot;&gt;&apos;&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;Receiver target is not an anchor element. Cannot open in new tab.&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&apos;&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;This version worked great - I could hold down Command to open links in a new
tab, and it worked exactly like a native link.&lt;/p&gt;

&lt;p&gt;That’s exactly what got me
thinking though - while I had emulated one user experience enhancement, there
were tonnes of others that users might be relying on, and it’s not really
practical for me to emulate every one of these enhancements.&lt;/p&gt;

&lt;p&gt;Ultimately, I decided not to use this code for this exact reason - I have too
many concerns about not supporting behaviour that users will expect to ‘just
work’, and might even harm the accessibility of links that I know will be
important to users.&lt;/p&gt;

&lt;p&gt;Instead, I reverted to using the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;stretched-link&lt;/code&gt; class, but changed it to be on
my dropdown menu toggle, instead of the primary action. I then gave the primary
action a bit more prominence in the dropdown menu. This introduces an extra
click to trigger the action, but also retains the accessibility and usability of
how this link can be accessed and interacted with - and that’s important to me.&lt;/p&gt;

&lt;p&gt;I still have an option to experiment with transparent (but still visible to
screen reader) links, so I have a fallback to support a primary action, but for
now, I’m ready to test triggering the dropdown, and will iterate on this if
required.&lt;/p&gt;
</description>
        <pubDate>Sat, 10 Aug 2024 00:00:00 +1200</pubDate>
        <link>https://joshmcarthur.com/2024/08/10/Tapping-targets-with-Stimulus.html</link>
        <guid isPermaLink="true">https://joshmcarthur.com/2024/08/10/Tapping-targets-with-Stimulus.html</guid>
        
        
      </item>
    
      <item>
        <title>iOS Action Sheet with Strada</title>
        <description>&lt;p&gt;&lt;a href=&quot;https://virtualtrails.app&quot;&gt;Virtualtrails&lt;/a&gt; uses &lt;a href=&quot;https://strada.hotwired.dev/&quot;&gt;Strada&lt;/a&gt; to bridge native iOS code to the web application, including notifications, native Apple + Google Signin and Apple Healthkit integration. I added a feature yesterday to replace a Bootstrap dropdown with a native iOS sheet, just because it’s a more intuitive experience for users.&lt;/p&gt;

&lt;div class=&quot;card-image&quot;&gt;
  &lt;video controls=&quot;&quot; src=&quot;/img/posts/sheet.mp4&quot;&gt;&lt;/video&gt;
&lt;/div&gt;

&lt;p&gt;Strada has two parts - the web side (which is in the form of a &lt;a href=&quot;https://stimulus.hotwired.dev/&quot;&gt;Stimulus&lt;/a&gt; controller with some guards built in so it only runs when Strada’s native side is available), and the iOS side (which is in the form of a ‘component’ which receives ‘messages’ from the web side, and can reply to them).&lt;/p&gt;

&lt;p&gt;The Strada Bridge Component is pretty straightforward:&lt;/p&gt;

&lt;div class=&quot;language-typescript highlighter-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;import&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;BridgeComponent&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;Message&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;from&lt;/span&gt; &lt;span class=&quot;dl&quot;&gt;&apos;&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;@hotwired/strada&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&apos;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;import&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;Turbo&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;from&lt;/span&gt; &lt;span class=&quot;dl&quot;&gt;&apos;&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;@hotwired/turbo-rails&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&apos;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;

&lt;span class=&quot;kr&quot;&gt;interface&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;PresentDropdownNavigationSheetLink&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;nl&quot;&gt;text&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;kr&quot;&gt;string&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;nl&quot;&gt;url&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;kr&quot;&gt;string&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;nl&quot;&gt;style&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;kr&quot;&gt;string&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

&lt;span class=&quot;kr&quot;&gt;interface&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;PresentDropdownNavigationSheetData&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;nl&quot;&gt;selectedLink&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;?:&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;PresentDropdownNavigationSheetLink&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;nl&quot;&gt;links&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;PresentDropdownNavigationSheetLink&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;kr&quot;&gt;interface&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;PresentDropdownNavigationSheetMessage&lt;/span&gt; &lt;span class=&quot;kd&quot;&gt;extends&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;Message&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;nl&quot;&gt;data&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;PresentDropdownNavigationSheetData&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;export&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;default&lt;/span&gt; &lt;span class=&quot;kd&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;DropdownNavigationSheetController&lt;/span&gt; &lt;span class=&quot;kd&quot;&gt;extends&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;BridgeComponent&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;static&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;component&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;dl&quot;&gt;&apos;&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;dropdown-navigation-sheet&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&apos;&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;static&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;values&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;links&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;Array&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;};&lt;/span&gt;
  &lt;span class=&quot;k&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;readonly&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;linksValue&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;PresentDropdownNavigationSheetData&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;kc&quot;&gt;undefined&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;nx&quot;&gt;present&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;Event&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;):&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;boolean&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;stopImmediatePropagation&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;();&lt;/span&gt;

    &lt;span class=&quot;k&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;send&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;
      &lt;span class=&quot;dl&quot;&gt;&apos;&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;present&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&apos;&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;links&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;linksValue&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;data&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;}:&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;PresentDropdownNavigationSheetMessage&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;nx&quot;&gt;data&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;selectedLink&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;Turbo&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;visit&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;data&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;selectedLink&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;url&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;span class=&quot;k&quot;&gt;return&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;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 the ‘present’ &lt;a href=&quot;https://stimulus.hotwired.dev/reference/actions&quot;&gt;action&lt;/a&gt; is triggered from the element, it sends a ‘present’ message to the iOS component, with the links to present in the action sheet. Each link has text, a URL (most of the time a path in my case), and a style (which maps to the &lt;a href=&quot;https://developer.apple.com/documentation/uikit/uialertaction/style&quot;&gt;iOS action sheet item styles&lt;/a&gt;).
iOS will respond with a message when the user selects an action, with the selectedLink property filled in. I then use &lt;a href=&quot;https://turbo.hotwired.dev/&quot;&gt;Turbo&lt;/a&gt; to ‘visit’ this URL  - this is essentially window.location, but with extra powers around other native integration (e.g. open a URL modally).&lt;/p&gt;

&lt;p&gt;The iOS code is super simple, it just responds to ‘present’ messages by opening an action sheet, and replying with the selected item:&lt;/p&gt;

&lt;div class=&quot;language-swift highlighter-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;import&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;SwiftUI&lt;/span&gt;
&lt;span class=&quot;kd&quot;&gt;import&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;Strada&lt;/span&gt;

&lt;span class=&quot;kd&quot;&gt;final&lt;/span&gt; &lt;span class=&quot;kd&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;DropdownNavigationSheetComponent&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;BridgeComponent&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;override&lt;/span&gt; &lt;span class=&quot;kd&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;var&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;kt&quot;&gt;String&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;dropdown-navigation-sheet&quot;&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

    &lt;span class=&quot;k&quot;&gt;override&lt;/span&gt; &lt;span class=&quot;kd&quot;&gt;func&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;onReceive&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;message&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;Message&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;guard&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;let&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;event&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;Event&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;rawValue&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;message&lt;/span&gt;&lt;span class=&quot;o&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;k&quot;&gt;else&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;k&quot;&gt;switch&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;k&quot;&gt;case&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;present&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;
               &lt;span class=&quot;nf&quot;&gt;handlePresentEvent&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;message&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;message&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;kd&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;viewController&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;UIViewController&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;delegate&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;destination&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;as?&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;UIViewController&lt;/span&gt;
   &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;


    &lt;span class=&quot;kd&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;kd&quot;&gt;func&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;handlePresentEvent&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;message&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;Message&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;let&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;alertController&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;UIAlertController&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;title&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;kc&quot;&gt;nil&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;message&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;kc&quot;&gt;nil&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;preferredStyle&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;actionSheet&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;guard&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;data&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;PresentMessageData&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;message&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;data&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;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;let&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;links&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;data&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;links&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;n&quot;&gt;index&lt;/span&gt;&lt;span class=&quot;p&quot;&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;k&quot;&gt;in&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;links&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;enumerated&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;let&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;action&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;UIAlertAction&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;title&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;link&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;text&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;style&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;link&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;actionStyle&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;weak&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;self&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;_&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;in&lt;/span&gt;
                &lt;span class=&quot;k&quot;&gt;let&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;newData&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;PresentMessageData&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;links&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;links&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;selectedLink&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;links&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&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;k&quot;&gt;self&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;!.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;reply&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;with&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;message&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;replacing&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;data&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;newData&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;alertController&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;addAction&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;action&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;let&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;cancelAction&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;UIAlertAction&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;title&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;Cancel&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;style&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;cancel&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;handler&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;kc&quot;&gt;nil&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;alertController&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;addAction&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;cancelAction&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;

        &lt;span class=&quot;n&quot;&gt;viewController&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;!.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;present&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;alertController&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;animated&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;nv&quot;&gt;completion&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;kc&quot;&gt;nil&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;


&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

&lt;span class=&quot;kd&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;kd&quot;&gt;extension&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;DropdownNavigationSheetComponent&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;kd&quot;&gt;enum&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;Event&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;String&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;case&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;kd&quot;&gt;struct&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;DropdownNavigationSheetLink&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;Encodable&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;Decodable&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;let&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;url&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;String&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;?&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;let&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;text&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;String&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;style&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;String&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;default&quot;&lt;/span&gt;

        &lt;span class=&quot;kd&quot;&gt;func&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;actionStyle&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;kt&quot;&gt;UIAlertAction&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;Style&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
            &lt;span class=&quot;k&quot;&gt;switch&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;self&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;style&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
            &lt;span class=&quot;k&quot;&gt;case&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;destructive&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;
                    &lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;destructive&lt;/span&gt;
            &lt;span class=&quot;k&quot;&gt;case&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;cancel&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;
                    &lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;cancel&lt;/span&gt;
            &lt;span class=&quot;k&quot;&gt;default&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;
                    &lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;default&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;kd&quot;&gt;struct&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;PresentMessageData&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;Encodable&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;Decodable&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;let&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;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;kt&quot;&gt;DropdownNavigationSheetLink&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;let&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;selectedLink&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;DropdownNavigationSheetLink&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;blockquote&gt;
  &lt;p&gt;I use some ! assertions in here to make sure things fall over when properties I expect to be defined are not. Honestly I could probably change these to be ? and it can just do nothing.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;The markup that invokes this is also pretty simple. I build the links collection in ERB which is pretty gross, but also contained to this menu partial, and I can quite easily extract it to an object. This is one of the times I kind of want to use viewcomponents, but I also then would be adding a dependency for this one thing. The reason I pass the links as a value is just to not encode anything in the Stimulus controller, since I’ll use different links based on user access and permissions, and in different places.&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;&lt;span class=&quot;cp&quot;&gt;&amp;lt;%&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;links&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;policy&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;progress_update&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;).&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;show?&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;text: &lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;Share&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;ss&quot;&gt;url: &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;share_path&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;ss&quot;&gt;style: &lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;default&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;kp&quot;&gt;nil&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;policy&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;progress_update&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;).&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;edit?&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;text: &lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;Edit&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;ss&quot;&gt;url:  &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;edit_path&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;ss&quot;&gt;style: &lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;default&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;kp&quot;&gt;nil&lt;/span&gt;
  &lt;span class=&quot;p&quot;&gt;].&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;compact&lt;/span&gt; &lt;span class=&quot;cp&quot;&gt;%&amp;gt;&lt;/span&gt;

  &lt;span class=&quot;cp&quot;&gt;&amp;lt;%&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;button_attrs&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;merge!&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;ss&quot;&gt;data: &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;ss&quot;&gt;controller: &lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;bridge--dropdown-navigation-sheet&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
    &lt;span class=&quot;ss&quot;&gt;action: &lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;bridge--dropdown-navigation-sheet#present&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
    &lt;span class=&quot;ss&quot;&gt;bridge__dropdown_navigation_sheet_links_value: &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;links&lt;/span&gt;
  &lt;span class=&quot;p&quot;&gt;})&lt;/span&gt; &lt;span class=&quot;cp&quot;&gt;%&amp;gt;&lt;/span&gt;

  # button_attrs then gets used to make a button, e.g.
  button_tag(&quot;More options&quot;, **button_attrs)
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

</description>
        <pubDate>Sat, 03 Aug 2024 00:00:00 +1200</pubDate>
        <link>https://joshmcarthur.com/2024/08/03/ios-action-sheet-with-strada.html</link>
        <guid isPermaLink="true">https://joshmcarthur.com/2024/08/03/ios-action-sheet-with-strada.html</guid>
        
        
      </item>
    
      <item>
        <title>Implementing an around callback with Minitest using Ruby</title>
        <description>&lt;p&gt;I use both RSpec and Minitest, but prefer Minitest. It’s not quite as
feature-rich as RSpec, but I find that I prefer the tests I write in Minitest -
they are more Ruby-ish, and aren’t as reliant on RSpec’s DSL to work.&lt;/p&gt;

&lt;p&gt;I don’t use &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;around&lt;/code&gt; callbacks in RSpec very often, but it is something I
recently found myself wanting to do in Minitest. Since I couldn’t find a
built-in way to achieve this, I fell back to just using Ruby - in this case,
proxying the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;test&lt;/code&gt; method.&lt;/p&gt;

&lt;p&gt;Just to be clear, in RSpec, an &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;around&lt;/code&gt; callback is passed the example as a
block arg. Typically, you’ll take an action &lt;em&gt;before&lt;/em&gt; running the example, then
run the example, then take an action &lt;em&gt;after&lt;/em&gt; running. This is often used for
setting a variable for the duration of a test, instrumentation, that kind of
thing:&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;describe&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;Thing&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;nb&quot;&gt;puts&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;Running before example&quot;&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;nb&quot;&gt;puts&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;Running after example&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;The thing with Minitest/Test::Unit is - it’s just Ruby. To achieve something
similar with Ruby, we can just wrap Minitest’s &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;test&lt;/code&gt; method to take the action
we want before and after calling the &lt;em&gt;actual&lt;/em&gt; test 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;class&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;ThingTest&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&amp;lt;&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;Minitest&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;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;wrapped_test&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;description&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;nb&quot;&gt;test&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;description&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;nb&quot;&gt;puts&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;Running before test&quot;&lt;/span&gt;
      &lt;span class=&quot;k&quot;&gt;yield&lt;/span&gt;
      &lt;span class=&quot;nb&quot;&gt;puts&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;Running after test&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;span class=&quot;n&quot;&gt;wrapped_test&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;does foo&quot;&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;do&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;assert_equals&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;1&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;Because this is just Ruby, &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;wrapped_test&lt;/code&gt; can be moved to a module, or a base
test class to be reused by other tests. It’s also entirely possible to create
multiple test wrappers, and because they’re declarative, they’ll have nice
descriptive names of &lt;em&gt;what&lt;/em&gt; they are wrapping. You can even wrap wrappers - it’s
Just Ruby!&lt;/p&gt;
</description>
        <pubDate>Sun, 23 Jun 2024 00:00:00 +1200</pubDate>
        <link>https://joshmcarthur.com/2024/06/23/Around-callback-with-Minitest-using-Ruby.html</link>
        <guid isPermaLink="true">https://joshmcarthur.com/2024/06/23/Around-callback-with-Minitest-using-Ruby.html</guid>
        
        
      </item>
    
      <item>
        <title>Auto-resizing images for .ico files using ImageMagick</title>
        <description>&lt;p&gt;There’s a bit of a mystery about &lt;a href=&quot;https://stackoverflow.com/questions/48956465/favicon-standard-2024-svg-ico-png-and-dimensions&quot;&gt;how relevant&lt;/a&gt; favicon.ico files are in 2024. Like it not though, they are the lowest common denominator, so they still have some use.&lt;/p&gt;

&lt;p&gt;Today I learned about a handy option that can be passed to ImageMagick that will automatically resize a source into a range
of common dimensions to be packed into an ico file.&lt;/p&gt;

&lt;p&gt;Given a source file, say &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;favicon.svg&lt;/code&gt;, which is, say, a vector image, the following command generates a favicon.ico:&lt;/p&gt;

&lt;div class=&quot;language-sh highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;convert &lt;span class=&quot;nt&quot;&gt;-density&lt;/span&gt; 256x256 &lt;span class=&quot;nt&quot;&gt;-background&lt;/span&gt; transparent favicon.svg &lt;span class=&quot;nt&quot;&gt;-define&lt;/span&gt; icon:auto-resize &lt;span class=&quot;nt&quot;&gt;-colors&lt;/span&gt; 256 favicon.ico
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Source: https://gist.github.com/azam/3b6995a29b9f079282f3&lt;/p&gt;

&lt;blockquote&gt;
  &lt;p&gt;This will also work with a non-vector image source, like a PNG, in which case I believe the density argument is not needed.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;The favicon by default resizes to 256, 192, 128, 96, 64, 48, 40, 32, 24 and 16px variants:&lt;/p&gt;

&lt;div class=&quot;language-sh highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;❯ identify favicon.ico
favicon.ico[0] PNG 256x256 256x256+0+0 8-bit sRGB 38282B 0.000u 0:00.002
favicon.ico[1] ICO 192x192 192x192+0+0 8-bit sRGB 0.000u 0:00.002
favicon.ico[2] ICO 128x128 128x128+0+0 8-bit sRGB 0.000u 0:00.001
favicon.ico[3] ICO 96x96 96x96+0+0 8-bit sRGB 0.000u 0:00.000
favicon.ico[4] ICO 64x64 64x64+0+0 8-bit sRGB 0.000u 0:00.000
favicon.ico[5] ICO 48x48 48x48+0+0 8-bit sRGB 0.000u 0:00.000
favicon.ico[6] ICO 40x40 40x40+0+0 8-bit sRGB 0.000u 0:00.000
favicon.ico[7] ICO 32x32 32x32+0+0 8-bit sRGB 0.000u 0:00.000
favicon.ico[8] ICO 24x24 24x24+0+0 8-bit sRGB 0.000u 0:00.000
favicon.ico[9] ICO 16x16 16x16+0+0 8-bit sRGB 337400B 0.000u 0:00.000
&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;-define&lt;/code&gt; option does accept arguments though, so you can resize to a different range or subset:&lt;/p&gt;

&lt;div class=&quot;language-sh highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;convert &lt;span class=&quot;nt&quot;&gt;-density&lt;/span&gt; 256x256 &lt;span class=&quot;nt&quot;&gt;-background&lt;/span&gt; transparent favicon.svg &lt;span class=&quot;nt&quot;&gt;-define&lt;/span&gt; icon:auto-resize&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;32,16 &lt;span class=&quot;nt&quot;&gt;-colors&lt;/span&gt; 256 favicon.ico

identify favicon.ico
▶ identify favicon.ico
favicon.ico[0] ICO 32x32 32x32+0+0 8-bit sRGB 0.000u 0:00.000
favicon.ico[1] ICO 16x16 16x16+0+0 8-bit sRGB 5430B 0.000u 0:00.000
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;This can be useful if you don’t need all the predefined sizes, since the resulting file will be much smaller.&lt;/p&gt;
</description>
        <pubDate>Wed, 19 Jun 2024 00:00:00 +1200</pubDate>
        <link>https://joshmcarthur.com/2024/06/19/Auto-resizing-images-for-.ico-files.html</link>
        <guid isPermaLink="true">https://joshmcarthur.com/2024/06/19/Auto-resizing-images-for-.ico-files.html</guid>
        
        
      </item>
    
      <item>
        <title>Generating responsive utility classes with Bootstrap</title>
        <description>&lt;p&gt;I’m a big fan of Bootstrap, I use it a lot. I’ve used it for many years, and
with the recent support for theming with CSS variables, and for their utility
API, I’ve found I can have the best of both worlds - a rich and capable library
of components, along with an extensive class-based approach for making tweaks to
elements and components.&lt;/p&gt;

&lt;p&gt;One of the neat things about the utility API is that it’s generated from a SASS
map. This means that the map can be manipulated before generation to affect
which utilities are generated, and how they are generated.&lt;/p&gt;

&lt;p&gt;There are &lt;em&gt;lots&lt;/em&gt; of things that can be done with this manipulation, but in this
case I’m going to talk about adding a single option to a utility: &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;responsive:
true&lt;/code&gt;. This will generate a variant of each utility class for each breakpoint
(which are themselves configurable).&lt;/p&gt;

&lt;p&gt;As an example, Bootstrap defines a range of text color utility classes -
&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;text-danger&lt;/code&gt;, &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;text-dark&lt;/code&gt;, &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;text-light&lt;/code&gt;, etc - all the colours defined in
&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;$utilities-text-colors&lt;/code&gt;, plus some options for &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;muted&lt;/code&gt;, &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;emphasis&lt;/code&gt;, etc. The
utility definition is in the utilities map:&lt;/p&gt;

&lt;div class=&quot;language-scss highlighter-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;$utilities&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;o&quot;&gt;...&lt;/span&gt;
  &lt;span class=&quot;s2&quot;&gt;&quot;color&quot;&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;
      &lt;span class=&quot;n&quot;&gt;property&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;color&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;,&lt;/span&gt;
      &lt;span class=&quot;n&quot;&gt;class&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;text&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;,&lt;/span&gt;
      &lt;span class=&quot;n&quot;&gt;local-vars&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;
        &lt;span class=&quot;s2&quot;&gt;&quot;text-opacity&quot;&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;1&lt;/span&gt;
      &lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;,&lt;/span&gt;
      &lt;span class=&quot;n&quot;&gt;values&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;map-merge&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;
        &lt;span class=&quot;nv&quot;&gt;$utilities-text-colors&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;,&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;
          &lt;span class=&quot;s2&quot;&gt;&quot;muted&quot;&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;var&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;si&quot;&gt;#{&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;$prefix&lt;/span&gt;&lt;span class=&quot;si&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;secondary-color&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;//&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;deprecated&lt;/span&gt;
          &lt;span class=&quot;s2&quot;&gt;&quot;black-50&quot;&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;rgba&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;$black&lt;/span&gt;&lt;span class=&quot;o&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;o&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;//&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;deprecated&lt;/span&gt;
          &lt;span class=&quot;s2&quot;&gt;&quot;white-50&quot;&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;rgba&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;$white&lt;/span&gt;&lt;span class=&quot;o&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;o&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;//&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;deprecated&lt;/span&gt;
          &lt;span class=&quot;s2&quot;&gt;&quot;body-secondary&quot;&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;var&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;si&quot;&gt;#{&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;$prefix&lt;/span&gt;&lt;span class=&quot;si&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;secondary-color&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;,&lt;/span&gt;
          &lt;span class=&quot;s2&quot;&gt;&quot;body-tertiary&quot;&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;var&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;si&quot;&gt;#{&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;$prefix&lt;/span&gt;&lt;span class=&quot;si&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;tertiary-color&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;,&lt;/span&gt;
          &lt;span class=&quot;s2&quot;&gt;&quot;body-emphasis&quot;&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;var&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;si&quot;&gt;#{&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;$prefix&lt;/span&gt;&lt;span class=&quot;si&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;emphasis-color&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;,&lt;/span&gt;
          &lt;span class=&quot;s2&quot;&gt;&quot;reset&quot;&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;inherit&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;span class=&quot;o&quot;&gt;,&lt;/span&gt;
    &lt;span class=&quot;o&quot;&gt;//&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;...&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;&lt;a href=&quot;https://github.com/twbs/bootstrap/blob/d2d4581790da2618d3fe063dafaa6205c73aabdd/scss/_utilities.scss#L576&quot;&gt;Source&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;You’ll notice that this map is missing the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;responsive&lt;/code&gt; option. This can be
added by manipulating the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;$utilities&lt;/code&gt; map. This needs to be done &lt;em&gt;after&lt;/em&gt;
importing &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;bootstrap/scss/utilities&lt;/code&gt; (so that the map is actually defined), but
&lt;em&gt;before&lt;/em&gt; importing &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;bootstrap/scss/utilities/api&lt;/code&gt; (so that the map changes are
reflected in the generated classes):&lt;/p&gt;

&lt;div class=&quot;language-scss highlighter-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;@import&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;bootstrap/scss/utilities&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;

&lt;span class=&quot;nv&quot;&gt;$utilities&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;map-merge&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;
  &lt;span class=&quot;nv&quot;&gt;$utilities&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;
    &lt;span class=&quot;s2&quot;&gt;&quot;color&quot;&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;map-merge&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;
      &lt;span class=&quot;nf&quot;&gt;map-get&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;$utilities&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;color&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;,&lt;/span&gt;
      &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;responsive&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;bp&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;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;@import&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;bootstrap/scss/utilities/api&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;With this option added, we can now apply normal responsive modifiers to our
&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;text-&lt;/code&gt; color classes. As an example: &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;.text-danger.text-lg-success&lt;/code&gt; will color
the element these classes is applied to to red (#dc3545 by default) up to large
screens (992px by default), then to green (#198754 by default).&lt;/p&gt;

&lt;p&gt;This is a somewhat convoluted example, but the takeway is more significant - in
the same way that the responsive option can be added, any other parts of the map
can be added, or removed, or changed - including class names, values, and other
options like &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;print: true&lt;/code&gt; (generates modifier classes for in print mode, for
example &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;d-print-none&lt;/code&gt; will hide an element with print media).&lt;/p&gt;
</description>
        <pubDate>Fri, 14 Jun 2024 00:00:00 +1200</pubDate>
        <link>https://joshmcarthur.com/2024/06/14/Generating-Bootstrap-responsive-utility-classes.html</link>
        <guid isPermaLink="true">https://joshmcarthur.com/2024/06/14/Generating-Bootstrap-responsive-utility-classes.html</guid>
        
        
      </item>
    
      <item>
        <title>How to rearrange menu bar icons in Mac OS</title>
        <description>&lt;p&gt;This is a tip I re-discover every couple of years, then forget until I find my
menu bar getting a bit cluttered up again.&lt;/p&gt;

&lt;p&gt;Holding down the Command key allows menu icons to be dragged backwards and
forwards along the menu bar to order them. This is available for all items other
than the control center and date/time, including system items like network,
bluetooth and sound.&lt;/p&gt;

</description>
        <pubDate>Mon, 10 Jun 2024 00:00:00 +1200</pubDate>
        <link>https://joshmcarthur.com/2024/06/10/Rearrange-menu-icons-in-Mac-OS.html</link>
        <guid isPermaLink="true">https://joshmcarthur.com/2024/06/10/Rearrange-menu-icons-in-Mac-OS.html</guid>
        
        
      </item>
    
      <item>
        <title>Logging the DOM state of an element with testing-library</title>
        <description>&lt;p&gt;I’m sure this is all over testing-library documentation, but I haven’t had to do
this before, so I think it’s worth a quick post about.&lt;/p&gt;

&lt;p&gt;If I’m writing a
&lt;a href=&quot;https://www.npmjs.com/package/testing-library&quot;&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;testing-library&lt;/code&gt;&lt;/a&gt; assertion,
every now and then the test won’t pass, because the element I expected to be
present, was not. Usually, when this happens, a snippet of the DOM will be
output, but will be truncated - sometimes the element that has not matched is in
the truncated version, and sometimes it is not.&lt;/p&gt;

&lt;p&gt;So that I can see the full DOM state, I’ve come across &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;screen.debug()&lt;/code&gt;. This
is, I suspect, what testing-library calls when a DOM matcher test fails, but it
has some useful options which can help debug a test.&lt;/p&gt;

&lt;p&gt;First of all, the element to log can be passed. This is useful if you have a
selector you can match, because it allows &lt;em&gt;just&lt;/em&gt; that element to be logged,
without any of the noise before, after, or around it:&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;screen&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;debug&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;screen&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;getByTestId&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;test-container&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;Obviously you can pass any DOM query function you want there, including if you
need to &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;await&lt;/code&gt; an element with &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;findBy*&lt;/code&gt;, or even direct DOM lookups with
&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;document.querySelector&lt;/code&gt; (though my eslint rules in this case don’t permit me to
do this).&lt;/p&gt;

&lt;p&gt;If the DOM contents are still too long to see the problem, you can also pass
options to &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;debug&lt;/code&gt; with the element - &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;maxLength&lt;/code&gt; is handy, since it lets you
override the length at which the contents will be truncated.&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;screen&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;debug&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;screen&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;getByTestId&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;test-container&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;maxLength&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;10000&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;Less useful for easy debugging, you can also pass extra options to dictate how the output should be rendered - for example, turning off syntax highlighting.&lt;/p&gt;
</description>
        <pubDate>Thu, 06 Jun 2024 00:00:00 +1200</pubDate>
        <link>https://joshmcarthur.com/2024/06/06/Logging-DOM-with-testing-library.html</link>
        <guid isPermaLink="true">https://joshmcarthur.com/2024/06/06/Logging-DOM-with-testing-library.html</guid>
        
        
      </item>
    
      <item>
        <title>Stretching interactive elements</title>
        <description>&lt;p&gt;&lt;a href=&quot;https://getbootstrap.com/docs/5.3/helpers/stretched-link/&quot;&gt;Stretched links&lt;/a&gt;
have been a feature of Bootstrap for some time, but it’s quite a useful
technique to understand, because the functionality it offers - expanding the tap
target of an element, works for &lt;em&gt;any&lt;/em&gt; element that can be interacted with with a
tap or mouse gesture, not just links. Some common examples include:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;Buttons&lt;/li&gt;
  &lt;li&gt;Inputs&lt;/li&gt;
  &lt;li&gt;Labels&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The way that stretch links work is that they define a &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;::after&lt;/code&gt; pseudo element,
and then use absolute positioning to have that pseudoelement fill it’s
relatively positioned container (and the relative positioning &lt;em&gt;is&lt;/em&gt; a requirement
for this technique), using the following CSS:&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;nc&quot;&gt;.stretched-link&lt;/span&gt;&lt;span class=&quot;nd&quot;&gt;::after&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
	&lt;span class=&quot;nl&quot;&gt;position&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;absolute&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
	&lt;span class=&quot;nl&quot;&gt;bottom&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;nl&quot;&gt;content&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&quot;&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
	&lt;span class=&quot;nl&quot;&gt;left&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;nl&quot;&gt;right&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;nl&quot;&gt;top&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;nl&quot;&gt;z-index&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;1&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 neat thing about using this technique is that whatever element the stretch
is applied to retains it’s normal position and style. Because the pseudo element
is part of that element though, any interaction bubbles - so clicks,
double-clicks, drags, hovers, etc, all propagate. This makes this technique
really useful for:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;Cards (where the card has an unsurprising primary action)&lt;/li&gt;
  &lt;li&gt;List items&lt;/li&gt;
  &lt;li&gt;Droppable file inputs (to make the drop target bigger)&lt;/li&gt;
&lt;/ul&gt;

</description>
        <pubDate>Wed, 05 Jun 2024 00:00:00 +1200</pubDate>
        <link>https://joshmcarthur.com/2024/06/05/Stretching-interactive-elements.html</link>
        <guid isPermaLink="true">https://joshmcarthur.com/2024/06/05/Stretching-interactive-elements.html</guid>
        
        
      </item>
    
      <item>
        <title>Passion projects</title>
        <description>&lt;p&gt;Throughout my career, I’ve always had at least one project on the go outside
work - call this a personal, learning, or passion project - it all amounts to
the same.&lt;/p&gt;

&lt;p&gt;I’ve got a lot of benefit out of these projects - sometimes, I approach them
deliberately to try out a new technique or tool. Sometimes, I just enjoy the
process of starting a new app from scratch. Sometimes, I’ve got an idea to
satisfy a problem I, or someone I know, is facing.&lt;/p&gt;

&lt;p&gt;This post is mostly dwelling on how often I see these sorts of projects being
undervalued - the point of these projects, in my mind, is that they don’t have
to succeed. They don’t even need to make sense, but they also need to have a bit
more thought and structure than a single-file code snippet or codepen. What they
provide is a kind of sandbox - both from the point of view of providing a space
to play around, but also from a stakes point of view - there’s no client getting
invoiced here, there’s no startup running out of cash unless this project turns
it around, or a board of directors to be presented to to secure ongoing funding.
That’s a really fun and relaxing environment to work in.&lt;/p&gt;

&lt;p&gt;This doesn’t mean that development or effort isn’t fast paced, but it doesn’t
&lt;em&gt;have&lt;/em&gt; to be. I’ve personally noticed a real difference in how I approach
projects, when I have space to think. This means that if I face a problem, or
something doesn’t work the way I expect - there’s no pressure. I can put that
problem down for a little while, even a few days or weeks. I can come back to it
from time to time, and try something out, see if it works or not. If something
really doesn’t work out the way I want it to, I can walk away completely, or I
can spend a couple of hours tidying it up enough to go on my
&lt;a href=&quot;https://github.com/joshmcarthur&quot;&gt;Github&lt;/a&gt;, MIT license it, and leave it there,
in case it’s useful to someone else. There’s no obigation to support it, keep it
alive, or up to date.&lt;/p&gt;

&lt;p&gt;This lack of urgency also helps pacing in other ways. I’ve found the working
patterns for my passion projects tend to be really different when you already
have professional and personal commitments. For me, this means that my ratio of
planning to doing is very weighted towards planning - I’m spending less time
actually at my desk, but much more time &lt;em&gt;not&lt;/em&gt; at my desk thinking about the next
move. I’ve found that this pattern of work has really helped me in my
professional softwave development job as well, since it’s taught me the value of
picking the right approach the first time, and how the right decision can
continue to pay dividends throughout the life of a project. Sometimes spending
90% of the time thinking through the best approach means that the 10% of
actually executing on that approach is more productive as if I’d spend 100% of
the time fumbling for a working solution.&lt;/p&gt;

&lt;p&gt;I’ve got a number of ‘completed’ projects that I personally use most days. I
find the end state for these projects which are largely feature-complete (or at
least feature complete for my needs) really interesting, because it turns out
that the process of taking care of these projects is also really different from
professional work. From my point of view, professional long term support tends
to be focussed on security  - patching packages, and upgrading frameworks,
libraries and languages as they reach end of life. Personal projects &lt;em&gt;can&lt;/em&gt; have
this pressure, but it depends on priorities. I see these types of projects being
more like taking care of a garden - sure, there will be a degree of chores, like
mowing the lawn or weeding, but there will also be a degree of small,
incremental tidy ups - pruning, and tidying up loose ends or rough patches
you’ve noticed and now can’t unsee. Every now and then, there will even be a
larger piece of work - maybe redoing part of the garden by creating a new
feature or undertaking a refactor of some core code.&lt;/p&gt;

&lt;p&gt;Regardless, I”ve got a lot of value out of creating projects with no particular
end goal in mind. It’s a completely different working situation than
professional work, and the lack of structure provides a great deal of space to
explore technical, architectural, planning and project management skill
development.&lt;/p&gt;

&lt;p&gt;To check out passion projects I’ve done before, check out my &lt;a href=&quot;https://github.com/joshmcarthur?tab=repositories&quot;&gt;repository
history&lt;/a&gt; on Github.&lt;/p&gt;
</description>
        <pubDate>Mon, 27 May 2024 00:00:00 +1200</pubDate>
        <link>https://joshmcarthur.com/2024/05/27/Passion-projects.html</link>
        <guid isPermaLink="true">https://joshmcarthur.com/2024/05/27/Passion-projects.html</guid>
        
        
      </item>
    
      <item>
        <title>Procs can be passed to Rails&apos; collection form helpers</title>
        <description>&lt;p&gt;Just a quick little way to reformat or otherwise transform a value to be used as the label
in a select, radio button, or checkbox collection group in a Rails form:&lt;/p&gt;

&lt;p&gt;Given I have an enum defined 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;User&lt;/span&gt;
  &lt;span class=&quot;n&quot;&gt;enum&lt;/span&gt; &lt;span class=&quot;ss&quot;&gt;:distance_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;ss&quot;&gt;mi: &lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;mi&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;ss&quot;&gt;km: &lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;km&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;And translations defined like so:&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;en&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;
  &lt;span class=&quot;na&quot;&gt;distance_units&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;
    &lt;span class=&quot;na&quot;&gt;km&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;Kilometer (Metric)&lt;/span&gt;
    &lt;span class=&quot;na&quot;&gt;mi&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;Mile (Imperial)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Then I can pass a short proc as the ‘label’ argument to a &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;collection_*&lt;/code&gt; form helper, which allows the value to be transformed:&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;&lt;span class=&quot;cp&quot;&gt;&amp;lt;%=&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;collection_radio_buttons&lt;/span&gt; &lt;span class=&quot;ss&quot;&gt;:distance_unit&lt;/span&gt;&lt;span class=&quot;p&quot;&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;nf&quot;&gt;distance_units&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;keys&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;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;val&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;t&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;distance_units.&lt;/span&gt;&lt;span class=&quot;si&quot;&gt;#{&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;val&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;cp&quot;&gt;%&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Note: This also works to transform the ID argument as well, however normally the model or object already has a method which can be passed by symbol to identify the value.&lt;/p&gt;
</description>
        <pubDate>Sat, 25 May 2024 00:00:00 +1200</pubDate>
        <link>https://joshmcarthur.com/2024/05/25/Pass-a-proc-to-collection_select,-collection_radio_buttons,-collection_check_boxes.html</link>
        <guid isPermaLink="true">https://joshmcarthur.com/2024/05/25/Pass-a-proc-to-collection_select,-collection_radio_buttons,-collection_check_boxes.html</guid>
        
        
      </item>
    
      <item>
        <title>Avoiding script injection when rendering ERB</title>
        <description>&lt;p&gt;Bad (but not obviously 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;data&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;greeting: &lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;Hello, world!&quot;&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;no&quot;&gt;ERB&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;s2&quot;&gt;&quot;
  &amp;lt;h1&amp;gt;Show greeting:&amp;lt;/h1&amp;gt;
  &amp;lt;p&amp;gt;&amp;lt;%= greeting %&amp;gt;&amp;lt;/p&amp;gt;
&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;).&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;result_with_hash&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;data&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;

&lt;span class=&quot;c1&quot;&gt;# =&amp;gt; &amp;lt;h1&amp;gt;Show greeting:&amp;lt;/h1&amp;gt; &amp;lt;p&amp;gt;Hello, world!&amp;lt;/p&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;The question is - what happens if &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;greeting&lt;/code&gt; is something different…like &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;&amp;lt;script&amp;gt;alert(&apos;greeting!&apos;)&amp;lt;/script&amp;gt;&lt;/code&gt;? Perhaps, if you’re used to Rails, not what you might expect:&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;data&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;greeting: &lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;&amp;lt;script&amp;gt;alert(&apos;greeting!&apos;)&amp;lt;/script&amp;gt;&quot;&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;no&quot;&gt;ERB&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;s2&quot;&gt;&quot;
  &amp;lt;h1&amp;gt;Show greeting:&amp;lt;/h1&amp;gt;
  &amp;lt;p&amp;gt;&amp;lt;%= greeting %&amp;gt;&amp;lt;/p&amp;gt;
&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;).&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;result_with_hash&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;data&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;

&lt;span class=&quot;c1&quot;&gt;# =&amp;gt; &amp;lt;h1&amp;gt;Show greeting:&amp;lt;/h1&amp;gt; &amp;lt;p&amp;gt;&amp;lt;script&amp;gt;alert(&apos;greeting!&apos;)&amp;lt;/script&amp;gt;&amp;lt;/p&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;ERB is a templating language, but the content it expects is agnostic - it could be HTML, script, CSS, text…anything. Because of this, it doesn’t perform any sanitisation or escaping for you.&lt;/p&gt;

&lt;p&gt;One option here is to wrap everything in methods like &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;CGI.escapeHTML&lt;/code&gt;, but in a Rails environment, it’s much better (and safer!) to instead rely on Rails’ existing rendering mechanisms!&lt;/p&gt;

&lt;p&gt;Good:&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;data&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;greeting: &lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;&amp;lt;script&amp;gt;alert(&apos;greeting!&apos;)&amp;lt;/script&amp;gt;&quot;&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;no&quot;&gt;ApplicationController&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;renderer&lt;/span&gt;&lt;span class=&quot;p&quot;&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;ss&quot;&gt;inline: &lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;
    &amp;lt;h1&amp;gt;Show greeting:&amp;lt;/h1&amp;gt;
    &amp;lt;p&amp;gt;&amp;lt;%= greeting %&amp;gt;&amp;lt;/p&amp;gt;
  &quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
  &lt;span class=&quot;ss&quot;&gt;locals: &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;data&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;c1&quot;&gt;# =&amp;gt; &amp;lt;h1&amp;gt;Show greeting:&amp;lt;/h1&amp;gt; &amp;lt;p&amp;gt;&amp;amp;lt;script&amp;amp;gt;alert(&amp;amp;#39;greeting!&amp;amp;#39;)&amp;amp;lt;/script&amp;amp;gt;&amp;lt;/p&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
</description>
        <pubDate>Thu, 23 May 2024 00:00:00 +1200</pubDate>
        <link>https://joshmcarthur.com/2024/05/23/Avoiding-script-injection-rendering-ERB.html</link>
        <guid isPermaLink="true">https://joshmcarthur.com/2024/05/23/Avoiding-script-injection-rendering-ERB.html</guid>
        
        
      </item>
    
      <item>
        <title>Streamlining daily blog posts</title>
        <description>&lt;p&gt;For quite a while, I managed to find something interesting to write up each work
day. Unfortunately, life got in the way, and as is easy to do, this habit
stopped.&lt;/p&gt;

&lt;p&gt;I’m hoping to get back into writing, and I’m trying to do this by making it
easier to write up a post without leaving my flow as much. To help with this, I
set up this shell script:&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;

blog-post&lt;span class=&quot;o&quot;&gt;()&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;c&quot;&gt;# Check if title argument is provided&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;nt&quot;&gt;-z&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;$1&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;p&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;then
        &lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;echo&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;Please provide a title for the blog post.&quot;&lt;/span&gt;
        &lt;span class=&quot;nb&quot;&gt;exit &lt;/span&gt;1
    &lt;span class=&quot;k&quot;&gt;fi&lt;/span&gt;

    &lt;span class=&quot;c&quot;&gt;# Save the title&lt;/span&gt;
    &lt;span class=&quot;nv&quot;&gt;title&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;$1&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;&lt;/span&gt;

    &lt;span class=&quot;c&quot;&gt;# Change directory to your blog directory&lt;/span&gt;
    &lt;span class=&quot;nb&quot;&gt;cd&lt;/span&gt; ~/Projects/github.com/joshmcarthur/joshmcarthur.github.com

    &lt;span class=&quot;c&quot;&gt;# Create a new blog post markdown file&lt;/span&gt;
    make post &lt;span class=&quot;nv&quot;&gt;title&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;$title&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;&lt;/span&gt;

    &lt;span class=&quot;c&quot;&gt;# Generate the file name&lt;/span&gt;
    &lt;span class=&quot;nv&quot;&gt;file_name&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;_posts/&lt;/span&gt;&lt;span class=&quot;si&quot;&gt;$(&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;date&lt;/span&gt; +&lt;span class=&quot;s2&quot;&gt;&quot;%Y-%m-%d&quot;&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;nv&quot;&gt;$title&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;.md&quot;&lt;/span&gt;

    &lt;span class=&quot;c&quot;&gt;# Open the new post in VSCode with --wait argument&lt;/span&gt;
    code &lt;span class=&quot;nt&quot;&gt;--wait&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;$file_name&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;&lt;/span&gt;

    &lt;span class=&quot;c&quot;&gt;# Commit and push changes after VSCode is closed&lt;/span&gt;
    commit_and_push &lt;span class=&quot;s2&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;$file_name&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;c&quot;&gt;# Function to commit and push changes to GitHub&lt;/span&gt;
commit_and_push&lt;span class=&quot;o&quot;&gt;()&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;nv&quot;&gt;file_name&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;$1&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;&lt;/span&gt;

    &lt;span class=&quot;c&quot;&gt;# Add all changes&lt;/span&gt;
    git add &lt;span class=&quot;s2&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;$file_name&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;&lt;/span&gt;

    &lt;span class=&quot;c&quot;&gt;# Extract the post title from the file name&lt;/span&gt;
    &lt;span class=&quot;nv&quot;&gt;post_title&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;si&quot;&gt;$(&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;basename&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;$file_name&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;&lt;/span&gt; | &lt;span class=&quot;nb&quot;&gt;sed&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&apos;s/^[0-9]*-//&apos;&lt;/span&gt; | &lt;span class=&quot;nb&quot;&gt;sed&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&apos;s/\.md$//&apos;&lt;/span&gt;&lt;span class=&quot;si&quot;&gt;)&lt;/span&gt;

    &lt;span class=&quot;c&quot;&gt;# Commit changes with post title and current date&lt;/span&gt;
    git commit &lt;span class=&quot;nt&quot;&gt;-m&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;Updating &lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;$post_title&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;nb&quot;&gt;date&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;c&quot;&gt;# Push to GitHub&lt;/span&gt;
    git push origin master
&lt;span class=&quot;o&quot;&gt;}&lt;/span&gt;

&lt;span class=&quot;c&quot;&gt;# Call create_post function with title argument passed from command line&lt;/span&gt;
blog-post &lt;span class=&quot;s2&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;nv&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;This script will switch to my blog, and create a new post file, then open VSCode
with &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;--wait&lt;/code&gt;, which means it waits for the editor to be closed before returning
to the calling script. Once this happens, it will commit and push the post
automatically. Any corrections or updates, I’m happy to do as a subsequent
commit.&lt;/p&gt;

&lt;p&gt;I’ve added this to my dotfiles bin/ folder, and chmod +x’d it so I can call it
from anywhere. It’s ready to go!&lt;/p&gt;
</description>
        <pubDate>Wed, 22 May 2024 00:00:00 +1200</pubDate>
        <link>https://joshmcarthur.com/2024/05/22/Streamlining-daily-posts.html</link>
        <guid isPermaLink="true">https://joshmcarthur.com/2024/05/22/Streamlining-daily-posts.html</guid>
        
        
      </item>
    
      <item>
        <title>Adding elevation to DOC track dataset</title>
        <description>&lt;p&gt;I don’t know if this is interesting to anyone, but on Friday and for an extra
hour or two in the weekend, I was playing around with calculating an elevation
profile for walking tracks in NZ.&lt;/p&gt;

&lt;p&gt;I started with a dataset from data.govt.nz from the Department of Conservation
(which for AU is responsible for walking tracks and facilities at a national
level - basically all the national parks, and big walks - local councils take
care of smaller parks, tracks etc). That dataset has just over 3,000 records,
including a track name, distance in meters, and a path comprised of X,Y points.&lt;/p&gt;

&lt;p&gt;For the elevation, I grabbed a dataset from LINZ (Land Information NZ), who
maintain topographical map coverage of NZ. The topomaps have lots of detail on
them, but I was interested in the contours. Contours are shapes that indicate
elevation on a map. Contour lines that are closer together indicate steeper
ground, and farther apart, a gentler climb. For the Topo50 map series in NZ, a
contour line indicates an altitude gain or loss of 20m. Every 100m, a contour
line is displayed as a bolder line with a label indicating the altitude. In the
dataset I downloaded from LINZ, contours are represented as polygons. This
dataset was reasonably small - 1.8gb compressed. I investigated a digital
elevation model, or DEM. This is basically a giant dataset of points, associated
with an altitude. Usually, this data is collected from air or spare using LiDAR,
which is basically bouncing a laser beam off the ground and measuring the
distance. In NZ, LiDAR coverage is still limited to around the main centres. I
am going to investigate this more, since there are a couple of 8-meter
resolution DEM datasets that I might be able to use - these are derived from the
contour lines anyway though, rather than something as precise as LiDAR.&lt;/p&gt;

&lt;p&gt;I set up a new PostGIS database, and imported the two datasets, which I had
downloaded as shapefiles, using GDAL, in particular, &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;ogr2ogr&lt;/code&gt;. GDAL is to
geospatial data what ImageMagick is to image data, and is great and converting
between formats. Once I had imported this data, I had two tables, &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;doc_tracks&lt;/code&gt;,
and &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;nz_contours_topo_150k&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;I then did a bit of googling, and immediately found a query that did almost
literally exactly what I wanted - took an existing line shape, and existing
elevation data, and matched one with the other to create a new line shape with
elevation (Z axis) added:&lt;/p&gt;

&lt;div class=&quot;language-sql highlighter-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;WITH&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;sequenced_points&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;AS&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;SELECT&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;description&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;status&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;length_over_ground&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;object_type&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;equipment&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
               &lt;span class=&quot;n&quot;&gt;mln_seq&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
               &lt;span class=&quot;n&quot;&gt;pts&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;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;AS&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;elem_seq&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
               &lt;span class=&quot;n&quot;&gt;pts&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;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;k&quot;&gt;AS&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;pnt_seq&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
               &lt;span class=&quot;n&quot;&gt;pts&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;geom&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;AS&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;geom&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;FROM&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;
            &lt;span class=&quot;k&quot;&gt;SELECT&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;descriptio&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;as&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;description&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;status&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;shape_leng&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;as&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;length_over_ground&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;object_typ&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;as&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;object_type&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;equipment&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
                   &lt;span class=&quot;n&quot;&gt;ROW_NUMBER&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;OVER&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;AS&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;mln_seq&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;ST_DumpPoints&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;wkb_geometry&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)).&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;*&lt;/span&gt;
            &lt;span class=&quot;k&quot;&gt;FROM&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;doc_tracks&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;AS&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;pts&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;

&lt;span class=&quot;k&quot;&gt;SELECT&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;DISTINCT&lt;/span&gt;
       &lt;span class=&quot;n&quot;&gt;description&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;status&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;length_over_ground&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;object_type&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;equipment&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
       &lt;span class=&quot;n&quot;&gt;ST_MakeLine&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;st_setsrid&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;ST_MakePoint&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;ST_X&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;sequenced_points&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;geom&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;),&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;ST_Y&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;sequenced_points&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;geom&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;),&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;q&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;elevation&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;),&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;2193&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;))&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;OVER&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;PARTITION&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;BY&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;sequenced_points&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;mln_seq&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;sequenced_points&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;elem_seq&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;ORDER&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;BY&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;sequenced_points&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;elem_seq&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;AS&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;geom&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;FROM&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;sequenced_points&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;JOIN&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;LATERAL&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;SELECT&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;elevation&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;FROM&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;nz_contours_topo_150k&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;ORDER&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;BY&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;wkb_geometry&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&amp;lt;-&amp;gt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;sequenced_points&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;geom&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;LIMIT&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;1&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;AS&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;q&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;ON&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;true&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 query isn’t &lt;em&gt;too&lt;/em&gt; hard to parse, but uses a few techniques that are unusual for my normal webapp CRUD queries. It uses a lateral join, common table expression, and partitioning. The original answer did not have a call to &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;ST_SetSRID&lt;/code&gt;, which I added, since otherwise the point is constructed without an SRID, which means any mapping software will not know how to display it. The SRID of both datasets I imported is 2193, which is the NZTM projection used throughout NZ. I could have reprojected either, or both datasets to ‘regular’ latitudes and longitudes, WGS84 or 4326, but this would distort the track paths, which I needed to keep quite precise.&lt;/p&gt;

&lt;p&gt;This query worked, but was incredibly slow. I also found it hard to run for just one track at a time, because in the CTE, the first thing it does is break the track path into individual points. So putting a LIMIT clause on that just returned part of a track.&lt;/p&gt;

&lt;p&gt;I stated looking at how to speed this up. The first thing I did, was add indices to the geospatial columns:&lt;/p&gt;

&lt;div class=&quot;language-sql highlighter-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;-- Create an index on the contour line polygon geometry&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;create&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;index&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;nz_contours_topo_150k_geo&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;on&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;nz_contours_topo_150k&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;using&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;gist&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;wkb_geometry&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;

&lt;span class=&quot;c1&quot;&gt;-- Create an index on the doc track geometry&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;create&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;index&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;doc_tracks_geo&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;on&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;doc_tracks&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;using&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;gist&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;wkb_geometry&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 didn’t really help. The index nz_contours_topo_150k also took a suspiciously short amount of time. Almost like it didn’t do anything at all…&lt;/p&gt;

&lt;p&gt;The next thing I did was move the preprocessing of the track lines into a new table. This meant my elevation-adding query got much simpler, since it didn’t need to partition any more:&lt;/p&gt;

&lt;div class=&quot;language-sql highlighter-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;create&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;table&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;doc_track_points&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;as&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;
 &lt;span class=&quot;k&quot;&gt;SELECT&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;tracks&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;descriptio&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;as&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;description&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
        	   &lt;span class=&quot;n&quot;&gt;tracks&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;object_typ&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;as&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;track_grade&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
               &lt;span class=&quot;n&quot;&gt;mln_seq&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
               &lt;span class=&quot;n&quot;&gt;tracks&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;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;AS&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;elem_seq&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
               &lt;span class=&quot;n&quot;&gt;tracks&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;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;k&quot;&gt;AS&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;pnt_seq&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
               &lt;span class=&quot;n&quot;&gt;tracks&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;geom&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;AS&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;geom&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;FROM&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;
            &lt;span class=&quot;k&quot;&gt;SELECT&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;descriptio&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;c1&quot;&gt;-- &amp;lt;some_other_column&amp;gt;,&lt;/span&gt;
                   &lt;span class=&quot;n&quot;&gt;object_typ&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
                   &lt;span class=&quot;n&quot;&gt;ROW_NUMBER&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;OVER&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;AS&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;mln_seq&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;ST_DumpPoints&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;wkb_geometry&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)).&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;*&lt;/span&gt;
            &lt;span class=&quot;k&quot;&gt;FROM&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;doc_tracks&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;AS&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;tracks&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;create&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;index&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;doc_track_points_seq_idx&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;on&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;doc_track_points&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;mln_seq&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;elem_seq&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;create&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;index&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;doc_track_points_geom_idx&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;on&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;doc_track_points&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;using&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;gist&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;geom&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;With the track points in their own table, I could filter the rows to just return points for a particular track:&lt;/p&gt;

&lt;div class=&quot;language-sql highlighter-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;SELECT&lt;/span&gt;
       &lt;span class=&quot;n&quot;&gt;dtp&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;description&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;dtp&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;track_grade&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
       &lt;span class=&quot;n&quot;&gt;st_setsrid&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;st_makepoint&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;st_x&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;dtp&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;geom&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;),&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;st_y&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;dtp&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;geom&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;),&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;q&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;elevation&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;),&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;2193&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;as&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;geom&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;from&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;doc_track_points&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;dtp&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;JOIN&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;LATERAL&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;SELECT&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;elevation&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;FROM&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;nz_contours_topo_150k&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;ORDER&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;BY&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;wkb_geometry&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&amp;lt;-&amp;gt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;dtp&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;geom&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;LIMIT&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;1&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;AS&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;q&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;ON&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;true&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;WHERE&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;dtp&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;description&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&apos;Upper Fenian/Fenian Gold/Adams Flat Trk&apos;&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 track has 167 points along it’s route - not a tonne. It took &lt;em&gt;6 minutes&lt;/em&gt; to add elevation to it. It wouldn’t have been impossible just to have this run and generate all the data, but I still felt it should be faster than this for that number of points. Because I was operating on one point at a time, and adding the elevation to it, I would still need to post-process that table’s data back into an actual line at some point as well.&lt;/p&gt;

&lt;p&gt;The next thing I did was start running EXPLAIN ANALYZE on the query. Prefixing a query with &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;EXPLAIN (ANALYZE, COSTS, VERBOSE, BUFFERS, FORMAT JSON) &lt;/code&gt; generates a JSON output that can be pasted into a visualisation tool like https://explain.depesz.com/ to visualise where the query is spending it’s time.&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/img/posts/adding-elevation-to-doc-tracks/explainanalyze.png&quot; alt=&quot;Explain analyze screenshot&quot; /&gt;&lt;/p&gt;

&lt;p&gt;Using this tool, I could see that even though the correct indexes were being used, nearly the entire query time was being spent matching contour polygons to track points. This isn’t terribly surprising, since this is where the actual geospatial calculation is happening - everything else is mostly just a straight operator-based query.&lt;/p&gt;

&lt;p&gt;Since indexes were already being used, I was a bit stuck here, but I had a suspicion - while there are well-established mathmatical formula for finding the nearest point on a polygon to a point (and in fact this is how Postgis is matching the polygon to the point internally), it’s still a potentially expensive calculation that needs to be done. I wondered whether the query performance would be improved if I “exploded” the contour lines out into their respective points, indexed these, and then matched on individual points, rather than a whole polygon. I particularly suspected that indexing these points would result in an efficiency gain, because PostGIS geospatial indexes are very much optimised for finding the nearest point to another point.&lt;/p&gt;

&lt;p&gt;So, I used &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;ST_DumpPoints&lt;/code&gt; as I had for getting the points from a track line, to dump out the points comprising a contour polygon, into a table:&lt;/p&gt;

&lt;div class=&quot;language-sql highlighter-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;create&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;table&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;contour_line_points&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;as&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;
	&lt;span class=&quot;k&quot;&gt;select&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;nctk&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;geom&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;as&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;wkb_geometry&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
		   &lt;span class=&quot;n&quot;&gt;nctk&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;elevation&lt;/span&gt;
	&lt;span class=&quot;k&quot;&gt;from&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;
		&lt;span class=&quot;k&quot;&gt;select&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;elevation&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;ST_DumpPoints&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;wkb_geometry&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)).&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;*&lt;/span&gt;
		&lt;span class=&quot;k&quot;&gt;from&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;nz_contours_topo_150k&lt;/span&gt;
	&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;as&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;nctk&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 table took a few minutes to generate - not bad.&lt;/p&gt;

&lt;p&gt;I then added an index to this table. This took a very long time - about 13 hours - but I wasn’t too worried. I had expected it to be slow, and essentially once the index was built, I wouldn’t have to rebuild it unless I change the underlying data. I probably could also tune some Postgres settings like &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;maintenance_work_mem&lt;/code&gt; to try and speed this up.&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;create index contour_line_points_geom on contour_line_points using gist (wkb_geometry);
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Now I ran the query for resolving the track points above, but swapped out the table name - &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;contour_line_points&lt;/code&gt; instead of &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;nz_contours_topo_150k&lt;/code&gt;. I could leave the operator the same, because the operator already knows how to handle different types of geospatial data - I’ve just switched it from matching a polygon to a point, to a point to a point.&lt;/p&gt;

&lt;p&gt;Now when I ran the same query for that single track with 167 points, it took around 250ms. A great improvement! It was such a good improvement that I rolled my query back to the original version. I preferred this version because it didn’t require me to pre-process the track lines into points then have to post-process them back into a line - I could just take in the 2D line, and return the 3D line with elevation added. I ran it, and off it went. It still took a couple of hours to run, so I prefixed the query with &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;CREATE TABLE doc_tracks_elevation AS&lt;/code&gt;. This meant the result of the query was written to a table so that I could quickly query data from the returned dataset (using &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;CREATE MATERIALIZED VIEW AS&lt;/code&gt; here would have been better, since I could refresh the view easily, but I didn’t think of it at the time). The query for generating this table I ended up with was:&lt;/p&gt;

&lt;div class=&quot;language-sql highlighter-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;create&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;table&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;doc_tracks_elevation&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;as&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;WITH&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;sequenced_points&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;AS&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;SELECT&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;description&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;status&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;length_over_ground&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;object_type&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;equipment&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
               &lt;span class=&quot;n&quot;&gt;mln_seq&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
               &lt;span class=&quot;n&quot;&gt;pts&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;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;AS&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;elem_seq&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
               &lt;span class=&quot;n&quot;&gt;pts&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;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;k&quot;&gt;AS&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;pnt_seq&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
               &lt;span class=&quot;n&quot;&gt;pts&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;geom&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;AS&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;geom&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;FROM&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;
            &lt;span class=&quot;k&quot;&gt;SELECT&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;descriptio&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;as&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;description&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;status&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;shape_leng&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;as&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;length_over_ground&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;object_typ&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;as&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;object_type&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;equipment&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
                   &lt;span class=&quot;n&quot;&gt;ROW_NUMBER&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;OVER&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;AS&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;mln_seq&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;ST_DumpPoints&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;wkb_geometry&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)).&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;*&lt;/span&gt;
            &lt;span class=&quot;k&quot;&gt;FROM&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;doc_tracks&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;AS&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;pts&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;

&lt;span class=&quot;k&quot;&gt;SELECT&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;DISTINCT&lt;/span&gt;
       &lt;span class=&quot;n&quot;&gt;description&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;status&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;length_over_ground&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;object_type&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;equipment&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
       &lt;span class=&quot;n&quot;&gt;ST_MakeLine&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;st_setsrid&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;ST_MakePoint&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;ST_X&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;sequenced_points&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;geom&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;),&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;ST_Y&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;sequenced_points&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;geom&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;),&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;q&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;elevation&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;),&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;2193&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;))&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;OVER&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;PARTITION&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;BY&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;sequenced_points&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;mln_seq&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;sequenced_points&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;elem_seq&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;ORDER&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;BY&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;sequenced_points&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;elem_seq&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;AS&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;geom&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;FROM&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;sequenced_points&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;JOIN&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;LATERAL&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;SELECT&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;elevation&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;FROM&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;contour_line_points&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;ORDER&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;BY&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;wkb_geometry&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&amp;lt;-&amp;gt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;sequenced_points&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;geom&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;LIMIT&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;1&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;AS&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;q&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;ON&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;true&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;img src=&quot;/img/posts/adding-elevation-to-doc-tracks/map.png&quot; alt=&quot;Query output&quot; /&gt;&lt;/p&gt;

&lt;p&gt;A little while later (about an hour), and I have 4,850 tracks with elevation. Why is the number higher than the original? Well, that’s a good question. I believe it is because some tracks, for whatever reason, have more than one line associated with them. This could be because of a river or tidal crossing, where DOC have represented the track as two segments rather than one continuous line. Because of the way the ST_DumpPoints and partitition works, it returns a distinct row per line, rather than a multiline. At the moment, this is fine for me to continue to start to visualise this data. In the future, I will post-process these line shapes back into a multiline by matching against the track description and/or objectid.&lt;/p&gt;

&lt;p&gt;Selecting a single track’s information is as fast as I would expect it to be, since I don’t need to recalculate each time I want the elevation of a track:&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;select * from doc_tracks_elevation where description=&apos;Upper Fenian/Fenian Gold/Adams Flat Trk&apos;;
=&amp;gt; Time: 20.296 ms
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Now that I have calculated the basic elevation data, I’ve got some work to do to start visualising this data. I’ve also identified some optimisations in my current process I’d like to put in place.&lt;/p&gt;

&lt;p&gt;For visualisation, I plan to generate a JSON file for each track, as well as a manifest that can be used for an autocomplete search. The idea would be that someone can search for a track, which then fetches the JSON containing the 3D track route. I would use the elevation points from this route to show a graph of the elevation over distance, as well as the route itself. I may experiment with web mapping libraries that support 3D, like Mapbox, to show the track against a 3D terrain layer. With the Z axis defined, I can also generate the actual travel distance for each track, rather than just distance over ground that comes from the route.&lt;/p&gt;

&lt;p&gt;For the data processing stage, I would like to switch to using a DEM - probably the 8m derived DEM from contours. While exploding the contour points has yielded sufficient results, it’s not very high resolution. As far as I know, &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;ST_DumpPoints&lt;/code&gt; just dumps the ‘corners’ of polygons, and not the intermediate points. That means that I might not be finding the correct elevation for a track point - it all depends on the orientation of nearby contours. Using a DEM with a 8m grid means that I can return more accurate elevation. I also would like to investigate which tracks have multiple lines, and ensure that I can post-process these back into their original form. I would also like to look at augmenting the track path data with some statistics - things like gain/drop, as well as the actual track distance as I mentioned above. Finally, to assist the visualiation, I would like to add marker points to the data every kilometer across the ground, and kilometer based on track distance. This would help to break the track path into segments to allow users to have a reference point for a particular area of climb or descent.&lt;/p&gt;

</description>
        <pubDate>Sun, 14 Aug 2022 00:00:00 +1200</pubDate>
        <link>https://joshmcarthur.com/projects/2022/08/14/adding-elevation-to-doc-track-dataset.html</link>
        <guid isPermaLink="true">https://joshmcarthur.com/projects/2022/08/14/adding-elevation-to-doc-track-dataset.html</guid>
        
        <category>gis</category>
        
        <category>open-data</category>
        
        
        <category>Projects</category>
        
      </item>
    
      <item>
        <title>Rails supports transforming aria hashes into dasherized attributes</title>
        <description>&lt;p&gt;TIL that aria- attributes get the same treatment in Rails tag helpers as
&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;data-&lt;/code&gt;. That is, you can do:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;link_to &quot;X&quot;, y_path, aria: { pressed: true }&lt;/code&gt; -&amp;gt; &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;aria-pressed=&quot;true&quot;&lt;/code&gt; OR&lt;/li&gt;
  &lt;li&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;link_to &quot;X&quot;, y_path, &quot;aria-pressed&quot; =&amp;gt; true&lt;/code&gt; -&amp;gt; &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;aria-pressed=&quot;true&quot;&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;I can’t find any multi-dash aria properties on MDN, but I imagine Rails will
follow the same formatting rules of turning underscores to dashes, e.g.&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;link_to &quot;X&quot;, y_path, aria: { active_status: &quot;madeup&quot; }&lt;/code&gt; -&amp;gt;
&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;aria-active-status=&quot;madeup&quot;&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;This is a handy thing to know, since this lets long tag definitions be nicely
wrapped and indented across multiple lines, as well as naturally grouping
related attributes.&lt;/p&gt;
</description>
        <pubDate>Thu, 09 Jun 2022 00:00:00 +1200</pubDate>
        <link>https://joshmcarthur.com/til/2022/06/09/rails-supports-transforming-aria-hashes-into-dasherized-attributes.html</link>
        <guid isPermaLink="true">https://joshmcarthur.com/til/2022/06/09/rails-supports-transforming-aria-hashes-into-dasherized-attributes.html</guid>
        
        <category>til</category>
        
        <category>rails</category>
        
        
        <category>TIL</category>
        
      </item>
    
      <item>
        <title>Create a trix editor with a hidden toolbar</title>
        <description>&lt;p&gt;&lt;a href=&quot;https://github.com/basecamp/trix&quot;&gt;Trix&lt;/a&gt; is fairly new to me, but now that it’s
closely integrated with Rails, it’s become by go-to for rich text editing.&lt;/p&gt;

&lt;p&gt;I’ve been working on a project where I wanted a text field with some basic rich
text capabilities, but I wanted the input to look like a normal textarea. This
would allow standard text entry, but allow users to use keyboard shortcuts to
specify bold, italic and underlined text.&lt;/p&gt;

&lt;p&gt;I came across &lt;a href=&quot;https://github.com/basecamp/trix/issues/28&quot;&gt;this issue&lt;/a&gt;, which
let me to a &lt;a href=&quot;https://github.com/basecamp/trix/blob/main/test/src/test_helpers/fixtures/editor_with_toolbar_and_input.jst.eco#L3&quot;&gt;test
fixture&lt;/a&gt;
which gave the pointer I needed to do this. If we create our own
&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;&amp;lt;trix-toolbar&amp;gt;&lt;/code&gt; element, &lt;em&gt;and hide it&lt;/em&gt;, then Trix will use this toolbar, but it
will remain hidden.&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;&amp;lt;trix-toolbar id=&quot;hidden_trix_toolbar&quot; style=&quot;display:none;&quot;&amp;gt;&amp;lt;/trix-toolbar&amp;gt;
&amp;lt;trix-editor toolbar=&quot;hidden_trix_toolbar&quot; input=&quot;text_input&quot;&amp;gt;&amp;lt;/trix-editor&amp;gt;
&amp;lt;input type=&quot;hidden&quot; id=&quot;text_input&quot; /&amp;gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

</description>
        <pubDate>Thu, 02 Jun 2022 00:00:00 +1200</pubDate>
        <link>https://joshmcarthur.com/til/2022/06/02/create-a-trix-editor-with-a-hidden-toolbar.html</link>
        <guid isPermaLink="true">https://joshmcarthur.com/til/2022/06/02/create-a-trix-editor-with-a-hidden-toolbar.html</guid>
        
        <category>til</category>
        
        <category>rails</category>
        
        <category>actiontext</category>
        
        <category>trix</category>
        
        
        <category>TIL</category>
        
      </item>
    
      <item>
        <title>Asserting a partial was rendered in an RSpec view spec</title>
        <description>&lt;p&gt;View specs are &lt;em&gt;great&lt;/em&gt;. While I always keep in mind they are a &lt;em&gt;unit&lt;/em&gt; test and
do not test the full integration of a feature, I find that they are fantastic
for asserting around all the edge cases of a feature - things like the expected
validation messages showing up, or buttons appearing or disappearing based on
different scenarios. I used to use system tests for this kind of thing, and find
that these get really slow and fragile over time, while view specs stay blazing
fast.&lt;/p&gt;

&lt;p&gt;Often, I’ll break a template up into smaller pieces. Rails has a mechanism for
this called &lt;em&gt;partials&lt;/em&gt;. I’ll often break up things that are either their own
“thing” (like a “card” display for an object, or “share links” for an object),
or things that have some logic wrapped around them that I want to keep contained
(for example, a button with a permission check wrapped around it).&lt;/p&gt;

&lt;p&gt;When I’m testing such templates with a view spec, I will often have a view spec
for the main template, and then another view spec for any more complex partials.
In this scenario, I want to assert in the main template that the partial was
rendered, but I don’t mind too much &lt;em&gt;what&lt;/em&gt; is rendered, since I have another
view spec specifically for that partial that checks that.&lt;/p&gt;

&lt;p&gt;In this case, I’ve found that RSpec spies are a great way to do this. Spies are
set up ahead of the test run, record interactions with themselves, and then
allow the calls that they received to be asserted against. RSpec spies also have
a handy method called &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;and_call_original&lt;/code&gt;. This records the call, but then
invokes the original method. I can use this to record that a particular call to
&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;render&lt;/code&gt; was received, but then still actually render the partial.&lt;/p&gt;

&lt;p&gt;Here’s an 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;nb&quot;&gt;require&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;spec_helper&quot;&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;widgets/show.html.erb&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;ss&quot;&gt;type: :view&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;renders the &apos;edit_button&apos; partial&quot;&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;do&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;assign&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;ss&quot;&gt;:widget&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;Widget&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;allow&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;view&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;receive&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;ss&quot;&gt;:render&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;).&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;and_call_original&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;render&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;view&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_received&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;ss&quot;&gt;:render&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;).&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;with&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;widgets/edit_button&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;ss&quot;&gt;widget: &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;widget&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 test catches if the correct partial is not rendered, or if unexpected
locals are passed to the partial, but doesn’t require me to assert in this spec
exactly &lt;em&gt;what&lt;/em&gt; the partial might render - I can put that in the partial spec.&lt;/p&gt;

&lt;p&gt;I’ve found this is quite a handy technique and helps to make it quick and easy
to test templates, since it allows the test to be super focussed on what the
&lt;em&gt;current&lt;/em&gt; template under test is doing without worrying about any conditions the
parent template might otherwise need to take into account. It also allows for
some nice dependency injection into the partial spec, since the locals can be
adjusted in the spec (for example, to pass a different ‘user’ local, or a
feature flag stub or something like that). Changing these settings just for one
partial in a wider template would not otherwise be possible without splitting up
the template into partials, and the template spec into partial specs.&lt;/p&gt;

&lt;p&gt;This technique is also a candidate for refactoring into a custom matcher for
view specs. I won’t cover that in this post, but since &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;allow(view).to receive(:render).and_call_original&lt;/code&gt; doesn’t actually effect how templates are
rendered, you could quite easily run this before &lt;em&gt;all&lt;/em&gt; view specs, and then
support a matcher like &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;expect(view).to have_rendered_template()&lt;/code&gt; - which is
very similar to what the
&lt;a href=&quot;https://github.com/rails/rails-controller-testing/blob/master/lib/rails/controller/testing/template_assertions.rb&quot;&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;rails-controller-testing&lt;/code&gt;&lt;/a&gt;
gem does - but in a view spec rather than a controller spec (though I
suspect this gem probably &lt;em&gt;would&lt;/em&gt; work with a view spec since a view spec
defines an anonymous controller under the hood).&lt;/p&gt;
</description>
        <pubDate>Mon, 02 May 2022 00:00:00 +1200</pubDate>
        <link>https://joshmcarthur.com/til/2022/05/02/asserting-a-partial-was-rendered-in-an-rspec-view-spec.html</link>
        <guid isPermaLink="true">https://joshmcarthur.com/til/2022/05/02/asserting-a-partial-was-rendered-in-an-rspec-view-spec.html</guid>
        
        <category>til</category>
        
        <category>rails</category>
        
        <category>testing</category>
        
        <category>rspec</category>
        
        
        <category>TIL</category>
        
      </item>
    
      <item>
        <title>CLI tool entr for running a command when a file changes</title>
        <description>&lt;p&gt;&lt;a href=&quot;http://eradman.com/entrproject/&quot;&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;entr&lt;/code&gt;&lt;/a&gt; is a tool I’ve been using a bunch recently - mostly for watching source files, and running tests when I change the source file. &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;entr&lt;/code&gt; can be piped one or more files, which means it’s really easy to use - just use &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;ls&lt;/code&gt;, &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;find&lt;/code&gt;, or any other tool that outputs filenames, and then pipe it to entr:&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;ls app/models/widget.rb | entr bundle exec rspec spec/models/widget_spec.rb
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&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;ls app/models/widget.rb app/presenters/widget_presenter.rb | entr bundle exec rspec spec/system/widgets/list_spec.rb
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&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;find . -name &quot;*.rb&quot; | entr rubocop
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;entr&lt;/code&gt; also has a handy shortcut, &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;/_&lt;/code&gt; to use a placeholder if you’re just watching a single file which cuts down on typing:&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;ls app/models/widget.rb | entr bundle exec rspec /_
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;There are plenty more options to check out in the &lt;a href=&quot;http://eradman.com/entrproject/entr.1.html&quot;&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;entr&lt;/code&gt; man page&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;entr&lt;/code&gt; is available in Linux distros via &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;yum&lt;/code&gt;, &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;apt&lt;/code&gt;, etc. On Mac OS, it’s available in Homebrew: &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;brew install entr&lt;/code&gt;.&lt;/p&gt;
</description>
        <pubDate>Thu, 17 Mar 2022 00:00:00 +1300</pubDate>
        <link>https://joshmcarthur.com/til/2022/03/17/mac-os-entr-for-running-a-command-when-a-file-changes.html</link>
        <guid isPermaLink="true">https://joshmcarthur.com/til/2022/03/17/mac-os-entr-for-running-a-command-when-a-file-changes.html</guid>
        
        <category>til</category>
        
        <category>technology</category>
        
        
        <category>TIL</category>
        
      </item>
    
      <item>
        <title>Assert window.location properties with Jest and jsdom</title>
        <description>&lt;p&gt;I have a React component that is part of a wider Ruby on Rails application. It fetches a record,
and if the record does not exist, it redirects to the server-rendered 404 page.&lt;/p&gt;

&lt;blockquote&gt;
  &lt;p&gt;Unfortunately, while the user will see the ‘not found’ page, the HTTP status code will be 200 OK
by default, because this page is static. Best practise would be to define a location block in your web server (Nginx, Apache, IIS etc) to respond with a 404 status when this static file is requested.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Having made this component, I’d like to add a Jest assertion that when the record is absent, the
component does not render but instead tells the window to change it’s location.&lt;/p&gt;

&lt;p&gt;Unfortunately, a naieve assertion does not work the way I expected:&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;it&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;navigates to /404 when the record is absent&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;nx&quot;&gt;render&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;MyComponent&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;record&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;kc&quot;&gt;undefined&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;sr&quot;&gt;/&amp;gt;&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;err&quot;&gt;;
&lt;/span&gt;  &lt;span class=&quot;nx&quot;&gt;expect&lt;/span&gt;&lt;span class=&quot;p&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;location&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;pathname&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;).&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;toBe&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;/404&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;p&gt;Jest tells us that we can’t use &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;window.location&lt;/code&gt; in our component, because jsdom can’t navigate:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Error: Not implemented: navigation (except hash changes)&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;That’s fair enough! Adding navigation would be a big increase in scope for jsdom, and I can’t blame anyone for not supporting this.&lt;/p&gt;

&lt;p&gt;The trick to getting this to work is to redefine the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;location&lt;/code&gt; property on the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;window&lt;/code&gt; object in our test, so that we are using and asserting against an object that we control:&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;it&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;navigates to /404 when the record is absent&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;c1&quot;&gt;// jsdom doesn&apos;t allow us to &apos;navigate&apos; from a component, because how would that work?&lt;/span&gt;
  &lt;span class=&quot;c1&quot;&gt;// Since we don&apos;t need to navigate, but just assert that navigation _would_ have occurred,&lt;/span&gt;
  &lt;span class=&quot;c1&quot;&gt;// we can replace window.location with a URL, which has a pathname property we can assert&lt;/span&gt;
  &lt;span class=&quot;c1&quot;&gt;// against&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;defineProperty&lt;/span&gt;&lt;span class=&quot;p&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;dl&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;location&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;value&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;nx&quot;&gt;URL&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;http://example.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;configurable&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;p&quot;&gt;});&lt;/span&gt;

  &lt;span class=&quot;nx&quot;&gt;render&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;MyComponent&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;record&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;kc&quot;&gt;undefined&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;sr&quot;&gt;/&amp;gt;&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;err&quot;&gt;;
&lt;/span&gt;  &lt;span class=&quot;nx&quot;&gt;expect&lt;/span&gt;&lt;span class=&quot;p&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;location&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;pathname&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;).&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;toBe&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;/404&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;p&gt;You might notice that we’re using a
&lt;a href=&quot;https://developer.mozilla.org/en-US/docs/Web/API/URL&quot;&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;URL&lt;/code&gt;&lt;/a&gt; object there. Why
is that? Well, it turns out that &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;window.location&lt;/code&gt; shares many properties with
&lt;a href=&quot;https://developer.mozilla.org/en-US/docs/Web/API/URL&quot;&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;URL&lt;/code&gt;&lt;/a&gt;. Not &lt;em&gt;all&lt;/em&gt;, and
that’s important to know - especially for things like events and functions like
&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;reload()&lt;/code&gt;, but if you’re trying to assert that a property of the location
changed, this is perfect. We replace the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;window.location&lt;/code&gt; property with a new
URL object, change a property of that URL within our component, then assert that
the property changed to the value we expect it to.&lt;/p&gt;

&lt;p&gt;You might also notice that we’re passing an option to &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;defineProperty&lt;/code&gt; -
&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;configurable: true&lt;/code&gt;. Adding this option means that other tests can change the
value of this property later on the test. It’s also important to note that we’re
&lt;em&gt;replacing&lt;/em&gt; &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;window.location&lt;/code&gt; from when this test runs onwards. To me, this is
OK, since &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;window.location&lt;/code&gt; raises errors in jsdom and doesn’t work the way we
expect anyway, but if you want to avoid side effects, you can assign the
original &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;window.location&lt;/code&gt; to a variable, then assign the property back at the
end of your test - or put all this in a &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;before&lt;/code&gt;/&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;after&lt;/code&gt; hook in your test file
or suite.&lt;/p&gt;

&lt;h5 id=&quot;references&quot;&gt;References&lt;/h5&gt;

&lt;ol&gt;
  &lt;li&gt;https://stackoverflow.com/questions/54021037/how-to-mock-window-location-href-with-jest-vuejs&lt;/li&gt;
  &lt;li&gt;https://www.benmvp.com/blog/mocking-window-location-methods-jest-jsdom/&lt;/li&gt;
  &lt;li&gt;https://remarkablemark.org/blog/2018/11/17/mock-window-location/&lt;/li&gt;
&lt;/ol&gt;
</description>
        <pubDate>Wed, 19 Jan 2022 00:00:00 +1300</pubDate>
        <link>https://joshmcarthur.com/til/2022/01/19/assert-windowlocation-properties-with-jest.html</link>
        <guid isPermaLink="true">https://joshmcarthur.com/til/2022/01/19/assert-windowlocation-properties-with-jest.html</guid>
        
        <category>til</category>
        
        <category>jest</category>
        
        <category>testing</category>
        
        <category>javascript</category>
        
        
        <category>TIL</category>
        
      </item>
    
      <item>
        <title>Mid King Biv Trip Report</title>
        <description>&lt;p&gt;This trip report is really more a cautionary tale of overconfidence and inexperience leading to a challenging Tararua overnight trip to &lt;a href=&quot;https://www.doc.govt.nz/parks-and-recreation/places-to-go/wellington-kapiti/places/tararua-forest-park/things-to-do/huts/mid-king-bivvy/&quot;&gt;Mid King Biv&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Started:&lt;/strong&gt; 11am from &lt;a href=&quot;https://www.topomap.co.nz/NZTopoMap?v=2&amp;amp;ll=-40.906734,175.479426&amp;amp;z=15&quot;&gt;Holdsworth Carpark&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Finished:&lt;/strong&gt; 6:30pm the next day at &lt;a href=&quot;https://www.topomap.co.nz/NZTopoMap?v=2&amp;amp;ll=-40.906734,175.479426&amp;amp;z=15&quot;&gt;Holdsworth Carpark&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Travel Times:&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Day 1:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;2 hours to &lt;a href=&quot;https://www.doc.govt.nz/parks-and-recreation/places-to-go/wellington-kapiti/places/tararua-forest-park/things-to-do/huts/atiwhakatu-hut/&quot;&gt;Atiwhakatu Hut&lt;/a&gt; (break)&lt;/li&gt;
  &lt;li&gt;1 hour from Atiwhakatu Hut to the &lt;a href=&quot;https://www.topomap.co.nz/NZTopoMap?v=2&amp;amp;ll=-40.851767,175.458356&amp;amp;z=15&quot;&gt;swingbridge over Atiwhakatu Stream&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;20 minutes to &lt;a href=&quot;https://www.topomap.co.nz/NZTopoMap?v=2&amp;amp;ll=-40.849495,175.46033&amp;amp;z=15&quot;&gt;Baldy turnoff&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;1 hr 10 minutes to bushline&lt;/li&gt;
  &lt;li&gt;40 minutes to &lt;a href=&quot;https://www.topomap.co.nz/NZTopoMap/nz43742/Baldy/Wellington&quot;&gt;Baldy summit&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;1 hour to &lt;a href=&quot;https://www.topomap.co.nz/NZTopoMap/nz43746/South-King/Wellington&quot;&gt;South King&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;2 hours to &lt;a href=&quot;https://www.doc.govt.nz/parks-and-recreation/places-to-go/wellington-kapiti/places/tararua-forest-park/things-to-do/huts/mid-king-bivvy/&quot;&gt;Mid King Biv&lt;/a&gt; (&amp;lt; 1 km)&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Total: 9 hours&lt;/p&gt;

&lt;p&gt;Day 2:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;~4 hours from &lt;a href=&quot;https://www.doc.govt.nz/parks-and-recreation/places-to-go/wellington-kapiti/places/tararua-forest-park/things-to-do/huts/mid-king-bivvy/&quot;&gt;Mid King Biv&lt;/a&gt; to &lt;a href=&quot;https://www.topomap.co.nz/NZTopoMap?v=2&amp;amp;ll=-40.833043,175.478068&amp;amp;z=15&quot;&gt;Barton Track (South Mitre Stream bridge)&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;~3.5 hours from Barton Track (South Mitre Stream bridge) to Atiwhakatu Hut&lt;/li&gt;
  &lt;li&gt;~2.5 hours from Atiwhakatu Hut to Holdsworth carpark&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Total: 10 hours&lt;/p&gt;

&lt;h4 id=&quot;day-1-holdsworth-carpark-to-mid-king-biv-via-baldy&quot;&gt;Day 1: Holdsworth carpark to Mid-King Biv via Baldy&lt;/h4&gt;

&lt;p&gt;This trip had been planned carefully to fit in around family commitments
betweeen Christmas and New Years, so having found a couple of days spare, me and
my tramping buddy for this trip were locked in and ready to go. The original
plan was to travel up to &lt;a href=&quot;https://www.doc.govt.nz/parks-and-recreation/places-to-go/wellington-kapiti/places/tararua-forest-park/things-to-do/huts/atiwhakatu-hut/&quot;&gt;Atiwhakatu Hut&lt;/a&gt; and either stay at &lt;a href=&quot;https://www.doc.govt.nz/parks-and-recreation/places-to-go/wellington-kapiti/places/tararua-forest-park/things-to-do/huts/jumbo-hut/&quot;&gt;Jumbo&lt;/a&gt; or carry on
past to &lt;a href=&quot;https://www.doc.govt.nz/parks-and-recreation/places-to-go/wellington-kapiti/places/tararua-forest-park/things-to-do/huts/mcgregor-bivvy/&quot;&gt;McGregor Biv&lt;/a&gt;, returning the next day over the &lt;a href=&quot;https://www.topomap.co.nz/NZTopoMap/nz43744/Broken-Axe-Pinnacles/Wellington&quot;&gt;Broken Axe Pinnacles&lt;/a&gt;, down &lt;a href=&quot;https://www.topomap.co.nz/NZTopoMap/nz43742/Baldy/Wellington&quot;&gt;Baldy&lt;/a&gt;, back to
Atiwhakatu, and out.&lt;/p&gt;

&lt;figure&gt;
&lt;img src=&quot;/img/posts/mid-king-biv/original-plan.jpg&quot; alt=&quot;Original trip plan&quot; /&gt;
&lt;figcaption&gt;The original plan, as scribbled over a topomap layer by me - we planned to travel this circuit left to right.&lt;/figcaption&gt;
&lt;/figure&gt;

&lt;p&gt;We met in Carterton at 9am, to sort out gear, supermarket shopping and
last-minute coffees and food. The weather forecast promised showers for the
first day, with a slight improvement the next. Facing a high liklihood of it
being a fairly wet and miserable day, we revised our plans, opting to flip the
route around to instead aim for Mid King Biv, stay overnight there, and try and
get all the way across the Pinnacles and past Jumbo to head out the next day.
Our plans were somewhat influenced also by very full hut bookings for Powell,
Jumbo, and Atiwhakatu, which meant that we were unlikely to have much luck
finding a bunk in any of these huts.&lt;/p&gt;

&lt;p&gt;By 10am, we had finally organised ourselves and hit the road to Holdsworth,
arriving at 10:45 and signing in on the intentions book at &lt;a href=&quot;https://www.topomap.co.nz/NZTopoMap/nz43725/Holdsworth-Lodge/Wellington&quot;&gt;Holdsworth Lodge&lt;/a&gt; by
11am. We made steady time into Atiwhakatu, arriving after 2 hours for a lunch
stop. The hut was looking suspiciously empty for the time of day considering it
was supposed to be fully booked, with just one group in, but we scoffed down
some wraps and leftover Christmas ham before continuing up the valley. From this
point on, this was new track for me. The weather was fairly grey, with drizzle
or showers most of the time.&lt;/p&gt;

&lt;p&gt;The track from Atiwhakatu Hut to &lt;a href=&quot;https://www.doc.govt.nz/parks-and-recreation/places-to-go/wellington-kapiti/places/tararua-forest-park/things-to-do/huts/mitre-flats-hut/&quot;&gt;Mitre Flats Hut&lt;/a&gt; is reasonably well travelled, but
obviously doesn’t receive the love and attention the Atiwhakatu Hut track
receives (the track between Holdsworth and Atiwhakatu Hut was significantly rebuilt a few years ago to make more
accessible for family groups). It sidles along beside Atiwhakatu Stream, dodging
up and down the bank a couple of times to avoid slips and bluffs. After an hour,
we reached the swingbridge over the stream.&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/img/posts/mid-king-biv/atiwhakatu-swingbridge.jpg&quot; alt=&quot;Swingbridge over Atiwhakatu
Stream&quot; /&gt;&lt;/p&gt;

&lt;p&gt;After crossing the stream, the climbing began! The travel so far had been wet,
but as flat as it ever gets in the Tararuas. From this point on, we had some
solid climing to do from about 550m to &lt;a href=&quot;https://www.topomap.co.nz/NZTopoMap/nz43742/Baldy/Wellington&quot;&gt;Baldy&lt;/a&gt; at 1325m, and then on to &lt;a href=&quot;https://www.topomap.co.nz/NZTopoMap/nz43746/South-King/Wellington&quot;&gt;South King&lt;/a&gt;
at 1531m. The climb up to the bushline at Baldy wasn’t too bad - pretty similar
to most other Tararua climbs - straight up the spur, with the contour lines on
the topomap determining whether it was clambering or just puffing up steadily
with the occasional breather. In this
time, we travelled up through Beech, then mossy mountain beech, before this to
thinned out and we arrived at the bushline just slightly after the posted time
of 1 hour. As we’d approached the bushline, we’d moved properly into the
cloud, so it was fairly steady drizzle and mist now. The posted time to Baldy at
the intersection we’d left behind near Atiwhakatu stream is only to the
bushline, and from here it was tops travel - no times, just distance to tick
off. Travelling up through the tussock and mist, it took us another 40 minutes
to reach the summit of Baldy - or at least a piece of DOC caution tape tacked to
a wooden batten, what I assumed to be the summit.&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/img/posts/mid-king-biv/baldy-summit.jpg&quot; alt=&quot;Summit of Baldy&quot; /&gt;&lt;/p&gt;

&lt;p&gt;Something that I noticed on this trip, particularly around the tops, is that the
route is generally not poled in this area. Previous tops travel I’ve done in
poor visibility around Tabletop/Kime Hut, and even Powell/Holdsworth has felt
much safer since either warratahs or blue plastic poles mark the route there,
and the poles are spaced so as to be reasonably visible even in poor conditions.
On this route, it was largely a case of following the worn trail and the topomap, with an
occasional cairn to let us know we were on the right trail. This generally
worked out for us, though it requires more attention than following poles, and
we did have a bit of trouble just after the Baldy summit where some erosion made
it hard to pick out the route. In clearer conditions, this would be easier since
the ridgeline would be visible, but in the mist it was quite hard to gauge
whether we were following the crest of the ridge and not dropping off.&lt;/p&gt;

&lt;figure&gt;
&lt;img src=&quot;/img/posts/mid-king-biv/baldy-erosion.jpg&quot; alt=&quot;Erosion between Baldy
and South King&quot; /&gt;
&lt;figcaption&gt;Erosion, large slips and bluffs between Baldy and South King provided good motivation for sticking to the top of the ridge.&lt;/figcaption&gt;
&lt;/figure&gt;

&lt;p&gt;With a couple of craggy bits to negotiate, it took us about 45 minutes to
reach the South King Tarn where the route from Baldy we were on joins the route
coming North from Jumbo via the Broken Axe Pinnacles, our planned route for the
following day. By this time it was after 5pm, so while we still had some day to
work with, we were in a bit more of a hurry to reach the biv and dry out a bit,
so turned to the right to carry on up to the top of South King proper, reaching
the top after another 15 minutes or so.&lt;/p&gt;

&lt;p&gt;It was here that we made our major navigational blunder, attempting to follow a
GPS track that I had found while researching for the trip. The source of my
research, a Wilderness magazine post seemed to indicate that the route to the
Biv dropped directly off South King to reach what looked on the topomap like a
spot where the bushline was slightly higher. This route was tempting, since it
saved us a bit more travel along the ridgeline and got us heading straight to
the hut, hopefully into the shelter of the beech forest faster than other
options. If we’d stopped for a little longer and made use of the cell reception
we’d had since halfway up Baldy, we would have found that while this source does
suggest a route off South King, several other sources instead recommend the spur
from Mid King itself, just a bit further north. Unfortunately, we made the
mistake of seeing the hut was only about a kilometer away, and went for it,
dropping off South King towards Mid King Biv.&lt;/p&gt;

&lt;p&gt;At first, travel was pretty easy. After a day of drizzle, the tussock was
slippery, with the occasional spiky &lt;a href=&quot;https://en.wikipedia.org/wiki/Aciphylla&quot;&gt;spaniard grass&lt;/a&gt; eliciting pained
exclamations, but we were losing the altitude we needed to reach the hut
quickly. We then started passing the occasional &lt;a href=&quot;https://en.wikipedia.org/wiki/Olearia_colensoi&quot;&gt;leatherwood shrub&lt;/a&gt;, which
gradually became less and less occasional, until we were having to pick a route
between them.&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;https://en.wikipedia.org/wiki/Olearia_colensoi&quot;&gt;Leatherwood&lt;/a&gt; has a particular reputation around the Tararuas as an extremely hardy, thick
leaved shrub, growing close to the ground, but becoming larger and larger closer
to the bushline, around 2-3m. It has a particularly annoying springiness to it -
it is difficult to break branches off leatherwood or even to push through, since
any resistance is pushed right back at you.&lt;/p&gt;

&lt;p&gt;The descent on the tussock had been steep, but manageable, but now we faced some
challenging decisions. The leatherwood was now all around us, and from what we
could see, was getting bigger and thicker, making it hard to see where we were
putting our feet. While the mist restricted our visibility, we could see on our
left hand side that the ground was dropping away sharply. In front of us, it was
looking like it might do the same, and we edged forward to see if we could see a
safe route down.&lt;/p&gt;

&lt;p&gt;At this point, we had the first of several decision points about whether to
carry on or climb back up to the main ridge and start again. Looking back, given
the fact we had any doubt about our route, climbing back up would have been the
correct move - but at the time, we were wet, and tired. The climb back up
through slippery tussock to meet a ridge where we &lt;em&gt;still&lt;/em&gt; wouldn’t definitely
know where to go was daunting. We chose to carry on, assuming we’d be able to
continue picking a route through. We looked on the map and reminded ourselves
that the hut was not far away, and that the green on the map surely represented
easy-travelled, sheltered beech forest.&lt;/p&gt;

&lt;p&gt;Having chosen to continue down the slope, we found a spot that was the best of a
bad lot. There really weren’t any safe options that we could see, and looking
back, this was a really bad call for us to make, but we found a spot that looked
like it could be clambered down, around 3 meters of maybe 75 degree slope, and
set off. I went first, and managed 2 footholds before I ran out halfway down. By
then of course, I was committed, and then second foothold sort of changed it’s
mind about being a foothold anyway - I slid down the rest of the slope on my
front, landing quite heavily. Fortunately for me, I landed flat on my feet - on
an angle, I think I probably would have been looking at least at a sprain.&lt;/p&gt;

&lt;p&gt;The only good thing about having done that drop is that with a different
perspective, I could look for better route down for us to rejoin
at the bottom of the slope. This meant that we didn’t both need to do as much of
a slide to reach the bottom.&lt;/p&gt;

&lt;p&gt;After this, the travel continued to become more and more challenging. We felt
increasingly more trapped in our situation now, feeling that it was unlikely
we’d get back up that slope. We continued to do our best to follow the topomap
towards the hut, but constantly had to stop to try and scout the best route
through the leatherwood, which had become much taller and larger as we’d dropped
down another contour line or two. This travelling was particulaly tiring. We
knew we were close to the hut, but not imminently so. The leatherwood grew very
densely, concealing drops and holes. In places, the we tried to go under or
through the leatherwood, causing cuts and scratches. In other places, the
leatherwood was so dense that we would try and crawl over the top of it. Despite
the topomap showing a single stream that led past the biv, after the wet day
we’d had, we continued coming across other small streams, any of which could
have been the one to follow. We crisscrossed the larger 2 of 3 streams several
times trying to find clearer ground, and eventually ended up dropping into the
largest stream for the final couple of hundred meters, splashing down the
stream, ducking under or clambering over the leatherwood which at times
completely tunneled in the stream, and a couple of times when the water was too
obstructed, climbing back into the leatherwood to work around the obstacle. We
reached the biv at 8pm, not long before dark, having approached it from the back
via the stream. Fortunately, this biv, like many ex-&lt;abbr title=&quot;New Zealand Forestry Service&quot;&gt;NZFS huts&lt;/abbr&gt;, is painted bright
orange.&lt;/p&gt;

&lt;p&gt;This part of the day was both incredibly challenging and humbling. It
took us over 2 hours to travel around a kilometer to the biv, and absolutely
pushed our endurance and fitness to the limit. I don’t feel that we were ever in
life-threatening danger (we carried a &lt;abbr title=&quot;Personal Locator Beacon&quot;&gt;PLB&lt;/abbr&gt; for that scenario), but we were certainly beginning to reach the point
where we would have had some difficult decisions to make around whether we
continued to try and reach the shelter of the biv, or whether we should shelter
where we were, and try again in the morning. The root cause of us ending
up in this situation was essentially a failure to properly research and
understand the best route to the hut, to make rushed decisions influenced by
desire to get to shelter and hot food, and to not understand the
characteristics of the Tararuas. What we thought was a higher bushline in green
on the map, was of course leatherwood and other mountain shrubs. Other routes,
such as the spur from Mid King, show clearer travel further down towards the
biv, but instead we headed right for the green on the map assuming it would be
sheltering forest, and ended up stuck in a basin of leatherwood as a result. We had several opportunities
where we could have turned around, returned to the ridge, and
probably found the &lt;em&gt;actual&lt;/em&gt; route down, but since we’d already had a long day,
as soon as we started dropping altitude, we felt committed to
continuing - sunk cost strikes again.&lt;/p&gt;

&lt;p&gt;We were able to use the 1 bar of cell reception at the biv to advise our
contacts that we had arrived safely, but tired. Having expended significant
energy getting to where we had, we discussed and agreed that to travel over the
Broken Axe Pinnacles with another long, exposed day before reaching Jumbo Hut
would be pushing our luck and stamina. We would instead drop down to the valley
near &lt;a href=&quot;https://www.doc.govt.nz/parks-and-recreation/places-to-go/wellington-kapiti/places/tararua-forest-park/things-to-do/huts/mitre-flats-hut/&quot;&gt;Mitre Flats Hut&lt;/a&gt;, and cross a saddle back to Atiwhakatu Hut to complete a
loop. We had a quick dehy meal, a hot drink and crawled into the 2-person
‘dogbox’ biv to get some sleep.&lt;/p&gt;

&lt;h4 id=&quot;day-2-mid-king-biv-to-holdsworth-carpark&quot;&gt;Day 2: Mid King Biv to Holdsworth Carpark&lt;/h4&gt;

&lt;p&gt;&lt;img src=&quot;/img/posts/mid-king-biv/mid-king-biv.jpg&quot; alt=&quot;Mid king biv in the light of the morning&quot; /&gt;&lt;/p&gt;

&lt;p&gt;In contrast to the misty and rainy day we’d had previously, the weather was
looking much better in the morning, with the sun shining through the trees,
giving everything a very gold and green look. We knew we had a long day ahead of
us - we were anticipating it taking 2 hours for us to drop to the swingbridge
near Mitre Flats Hut over the South Mitre stream, then 3 hours to get from there
to Atiwhakatu Hut, and another 2 hours or so out - 7 hours total. We had a quick breakfast and
coffee, and hit the track. I was cursing our navigation error from the previous
day as we followed a clear route marked with orange triangles for 10 minutes to
the bushline. From here, the triangles were replaced with pink tape, but the
route was still clearly obvious, and it looked as though if we’d dropped off the
spur from &lt;a href=&quot;https://www.topomap.co.nz/NZTopoMap/nz43749/Middle-King/Wellington&quot;&gt;Mid King&lt;/a&gt; instead of South King, we would have had a much easier time.&lt;/p&gt;

&lt;p&gt;As we reached the bushline, we looked up towards the Kings, and across towards
the Pinanncles, and had a quick checkin. Should we go over the ridge route to
Jumbo after all? In the end, we opted to continue dropping to Mitre Flats, for a
few reasons:&lt;/p&gt;

&lt;ol&gt;
  &lt;li&gt;We’d already been in touch with our contacts to advise we were going back
through the valleys. Changing our minds would require us to stop, plan out
our route a little, and get in touch with them again.&lt;/li&gt;
  &lt;li&gt;Even though it was quite sunny where we were, the cloud was still covering
the main ridge. Neither of us had travelled that route before, and I knew
that the Broken Axe Pinnacles in particular are narrow and can be a bit
technical, especially in poor visibility.&lt;/li&gt;
  &lt;li&gt;A good night’s sleep had restored us somewhat, but we were still feeling
pretty battered from the previous day. It seemed best given some poor
decisions the previous day to take the less risky option of travelling down
and out through the valley.&lt;/li&gt;
  &lt;li&gt;We considered seeing if we could arrange a pick up at &lt;a href=&quot;https://www.topomap.co.nz/NZTopoMap/nz43731/The-Pines/Wellington&quot;&gt;The Pines&lt;/a&gt;, where the
most direct route from Mitre Flats Hut emerges. This would require a bit of
coordination with our contacts in 1-2 bar cell coverage, and would still
require us to get to Mitre Flats and then out, which is signposted as 4
hours, plus another hours drive to reach Holdsworth carpark. We decided that
the overall travel time for this option meant that it wasn’t partciularly
compelling since we were tired, but not injured, and getting ourselves back
to the cars under our own power meant we could travel to our own schedule. If
either of us had been even mildly injured, we would have taken this option,
since it would have been the fastest way out.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;The track from Mid King to Mitre Flats isn’t on any maps, even the Topo50
series. The hut intentions book made it clear that it’s quite a well-travelled
route though, with many people coming up from Mitre Flats on a day trip. My
primary concern for this route was that we wouldn’t be able to find the track,
but it turned out to be fairly obvious where to go. We did miss the first few
meters and overshot, but seeing a cairn on the other side of some leatherwood,
were able to deploy our new scrub-bashing skills to join the track. The track is
taped, but very intermittently, so we had to pay attention to where we were
heading to make sure we were still following the track. This was easier at the
start, while the beech forest was still quite stunted, and the path through the
moss was quite obvious. As we got further down into the bush, things got more
complicated, with a lot of dead ends and spots where treefall had concealed the
line of the track. It was a relief to spot a piece of tape or a cairn every now
and then to let us know we hadn’t gotten badly lost.&lt;/p&gt;

&lt;p&gt;When we did lose the track, we had the advantage of being on a spur, meaning we
could traverse across the top of the spur until we found the most
promising-looking worn route, which we would follow until we found a cairn or
tape. The spur route effectively heads straight down the spur, dropping from
about 1100m to just under 500m by South Mitre Stream.&lt;/p&gt;

&lt;p&gt;The aim is to meet &lt;a href=&quot;https://www.topomap.co.nz/NZTopoMap?v=2&amp;amp;ll=-40.829411,175.473139&amp;amp;z=15&quot;&gt;the South Mitre Stream where Baldy Creek joins it&lt;/a&gt;. We mostly
managed this aside from a diversion where we left the spur too soon, and ended
up down at Baldy Creek about a kilometer from South Mitre Stream. We fossicked
around a bit and contemplated going down the stream, but in the end decided to
climb back up the 50 or 60 meters to rejoin the spur. After we eventually
reached the point where stream and creek intersected, we started heading down
beside the stream towards the swingbridge on the Barton Track. This travel
wasn’t too difficult since it was reasonably flat and open beside the stream,
but we didn’t find a particularly clear route at any point, and lost it
completely just 100m or so from the swingbridge. I suspect it climbed up to join
the Barton Track after the bridge, but since we could see the bridge, we dropped
into the stream and crossed the stream to avoid a small bluff the bridge was
built onto, and re-crossed the stream via the bridge.&lt;/p&gt;

&lt;p&gt;Overall, we expected this section of track to take us 2 hours, and it had taken
us 4 hours. Being used to front-country type travel, I think this is probably
the longest stretch of non-triangle-marked track I’ve done, and it was
surprisingly time-consuming and stressful to make sure that we were following
the spur and not dropping to one side or the other too early, and to regain the
track when we left it, which we did many times (usually only by 10 meters or
so). Probably a more experienced tramper wouldn’t find this route difficult,
since the bush is quite open and easy to travel through, but I hadn’t realised
how dependent I was on following from one marker to another. By the time we
joined the Barton Track and stopped for a break beside the bridge, I wasn’t
feeling great - we were well behind schedule on what we already knew was going
to be a fairly long 7 hour day, and we still had a lot of ground to cover.&lt;/p&gt;

&lt;p&gt;We had considered stopping in at Mitre Flats Hut for a break, which was only a
couple of hundred meters away in a clearing. Given we were “behind schedule”, we
skipped this option, and started trudging. One of the downsides of the Barton
Track which runs between Mitre Flats and Atiwhakatu Huts is that it goes up and
down - a lot. While we’d dropped on the spur down to 400m, we had to
immediately climb back up to 600m, and traverse approximately along this contour
(I say “approximately” because in reality the track was always going up and down
by 20m or so, never flat), before dropping 100m to cross a stream, then climbing
200m to meet the Baldy turnoff we’d left yesterday just short of 700m. Reaching
the Baldy turnoff, psycologically, was a big boost - it meant a lot to have
closed the loop and now be retracing our footsteps over ground we’d covered - we
knew what was coming, and more importantly, knew that Atiwhakatu Hut was only
about 1.25 hours from the turnoff. We made slow progress between the Mitre Flats
swingbridge and Baldy turnoff, about 2.5 hours (signposted time is 1.75 hours).
Largely this was due to being pretty tired, and needing a lot of mini-breaks and
breathers. After reaching the Baldy turnoff, we gained a little speed and
stumbled into Atiwhakatu Hut for lunch about 3:45pm.&lt;/p&gt;

&lt;figure&gt;
        &lt;img src=&quot;/img/posts/mid-king-biv/day-2.jpg&quot; alt=&quot;The only photo I took on Day 2 - Atiwhakatu Stream&quot; /&gt;
        &lt;figcaption&gt;Evidence of how tired I was from the previous day&apos;s challenges - in a 10 hour day, I took a single photo&lt;/figcaption&gt;
&lt;/figure&gt;

&lt;p&gt;At Atiwhakatu Hut, we took a little time for break, and treated ourselves to
the last of our dry clothes we’d been saving for the last stretch. I’d
considered a longer stop at this hut - perhaps an hour or two - to rest up a bit
before continuing, but after having a bit of food, decided to carry on and get
it done. We left just before 4:30, and immediately noticed the easier travel on
the well-graded and gravelled Atiwhakatu Track, which served as a relatively
gentle 2-and-a-bit hour cooldown out to the carpark, which we reached at 6:30pm,
just in time to get a message out before my 7pm due-out time I’d arranged
earlier in the day at the Baldy turnoff where we found a little cell coverage.
Having already split the gear up back to their respective owners while stopped
at Atiwhakatu, we were pretty relieved to each jump in our own cars and head
back home, after a humbling trip, and one that to me has served as a reminder
about how quickly risk factors can add up to quite a difficult situation and
trip.&lt;/p&gt;

&lt;figure&gt;
&lt;img src=&quot;/img/posts/mid-king-biv/route.jpg&quot; alt=&quot;Route diagram: Red - route we
should have taken - purple - approximate route we took, green - approximate
route to Mitre Flats for day 2&quot; /&gt;
&lt;figcaption&gt;The approximate routes we followed. &lt;strong&gt;Purple:&lt;/strong&gt;The route we followed off South King to Mid King Biv. &lt;strong&gt;Red:&lt;/strong&gt;A guess of the route we &lt;em&gt;should&lt;/em&gt; have taken from Mid King to Mid King Biv. &lt;strong&gt;Green:&lt;/strong&gt;The approximate route from Mid King Biv to meet Barton Track near Mitre Flats Hut, including approximately where we mistakently dropped off the spur to Baldy Creek.&lt;/figcaption&gt;
&lt;/figure&gt;

&lt;h4 id=&quot;retrospective&quot;&gt;Retrospective&lt;/h4&gt;

&lt;p&gt;In retrospect, there are a number of factors that I will consider in the future:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;The weather forecast was for showers through the day. This meant that we were
not going to stay dry. Given we were already looking at a trip that was going
to be reasonably challenging for our skill level, we could have considered an
alternative destination or route.&lt;/li&gt;
  &lt;li&gt;While Atiwhakatu Hut and Jumbo were both booked, we had camping stuff with us,
including a tent. We had mostly ruled out staying at Jumbo because of the
bookings, when actually we could have probably either camped or found a bunk,
and this trip would have still allowed us to venture onto the tops, but in a
less remote area more in-line with our skill level. The quietness of
Atiwhakatu Hut on the first day could have prompted us to change our trip
plans and head to Jumob, but we already felt committed to what we had decided.&lt;/li&gt;
  &lt;li&gt;When we reached South King, I had a couple of different webpages downloaded to
my phone to refer to, but I picked the first of these and stuck with it since
it included a route to follow on a topomap. As it turns out, this route wasn’t
the best way to go - I wonder if it was drawn from memory after the trip, or
if that group just didn’t mind travelling through the leatherwood as much as
we did. If I’d stopped and properly considered all of the information I had on hand
rather than rushing to reach the biv, there’s a high chance we would have
continued to Mid King and probably found a clearer route down.&lt;/li&gt;
  &lt;li&gt;Navigating our own way along an unmarked route through the bush takes much
longer than just following a track, even with similar distances. The fact that
this section took twice as long as we thought it would was disspiriting and
exhausting, and made the rest of the day much harder to get through. If we’d
been able to accurately forecast our travel time for the day, and given the
reasonably good weather conditions when we left the biv, we probably would
have chosen to go back up and along the ridge to Jumbo, or even back down
Baldy. We still would not have had good visibility, but tops travel time is
easier to assess, and we would have reached Atiwhakatu Hut after around 3.5
hours via Baldy, or Jumbo Hut after perhaps 4 or 5 hours.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Overall, I learned a lot from this trip about where my comfort level lies, but
also the endurance I have when necessary. We’ve not been put off future trips,
but have already agreed that given our skill and comfort levels, our next trip
in this area will be to head to Jumbo Hut and proceed out to the tops from
there with a hut at our backs, rather than overextending ourselves to reach a
remote shelter.&lt;/p&gt;
</description>
        <pubDate>Thu, 06 Jan 2022 00:00:00 +1300</pubDate>
        <link>https://joshmcarthur.com/tramping/2022/01/06/mid-king-biv-trip-report.html</link>
        <guid isPermaLink="true">https://joshmcarthur.com/tramping/2022/01/06/mid-king-biv-trip-report.html</guid>
        
        <category>tramping</category>
        
        <category>trip-reports</category>
        
        
        <category>Tramping</category>
        
      </item>
    
      <item>
        <title>Quickly access Ruby Mail object parts</title>
        <description>&lt;p&gt;Often, mailer specs will start with a simple assertion like
&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;expect(mail.body).to include &quot;Some text in the expected email here&quot;&lt;/code&gt;. This
works for a while, but quickly breaks down when an email grows more complex,
such as having special characters causing it to be encoded as 7bit or
QuotedPrintable, having attachments, or supporting different formats (like
plaintext &amp;amp; HTML).&lt;/p&gt;

&lt;p&gt;I’ve always tried to do various horrible things relating to traversing the parts
of the message object, and extracting the part that has a mimetype of
&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;text/html&lt;/code&gt;. I should have known that the &lt;a href=&quot;https://github.com/mikel/mail&quot;&gt;Mail&lt;/a&gt;
library would have a shortcut for this!&lt;/p&gt;

&lt;p&gt;Given a mail object, you can call
&lt;a href=&quot;https://www.rubydoc.info/github/mikel/mail/Mail%2FMessage:html_part&quot;&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;.html_part&lt;/code&gt;&lt;/a&gt;,
and
&lt;a href=&quot;https://www.rubydoc.info/github/mikel/mail/Mail%2FMessage:text_part&quot;&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;.text_part&lt;/code&gt;&lt;/a&gt;
to access the HTML and text parts of the message respectively. Under the hood,
these helpers use
&lt;a href=&quot;https://www.rubydoc.info/github/mikel/mail/Mail%2FMessage:find_first_mime_type&quot;&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;find_first_mime_type&lt;/code&gt;&lt;/a&gt;,
which means you can find any other format you may support by passing the
mimetype to this helper. Note - this helper filters out attachments - if you
want to check what attachments a message has, you should use
&lt;a href=&quot;https://www.rubydoc.info/github/mikel/mail/Mail%2FMessage:attachments&quot;&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;.attachments&lt;/code&gt;&lt;/a&gt;
for that.&lt;/p&gt;

&lt;p&gt;With these helpers, the test can handle all sorts of formats and attachments
while still supporting a nice stable assertion -
&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;expect(mail.html_part.decoded).to include &quot;Some text in the expected email
here&quot;&lt;/code&gt;.&lt;/p&gt;
</description>
        <pubDate>Sun, 12 Sep 2021 00:00:00 +1200</pubDate>
        <link>https://joshmcarthur.com/til/2021/09/12/quickly-access-mail-object-parts.html</link>
        <guid isPermaLink="true">https://joshmcarthur.com/til/2021/09/12/quickly-access-mail-object-parts.html</guid>
        
        <category>til</category>
        
        <category>rails</category>
        
        
        <category>TIL</category>
        
      </item>
    
      <item>
        <title>Access ActionView context in an RSpec test</title>
        <description>&lt;p&gt;I recently created a Presenter object to wrap around an &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;ActiveStorage::Blob&lt;/code&gt;
that I wanted to decorate with some presentation methods for things like a human
file size and content type.&lt;/p&gt;

&lt;p&gt;The initialisation signature for my presenter looks like 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;k&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;MyPresenter&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&amp;lt;&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;SimpleDelegator&lt;/span&gt;
  &lt;span class=&quot;k&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;initialize&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;obj&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;view_context&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;super&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;obj&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;vi&quot;&gt;@view_context&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;view_context&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;human_file_size&lt;/span&gt;
    &lt;span class=&quot;vi&quot;&gt;@view_context&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;number_to_human_size&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;byte_size&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;ss&quot;&gt;precision: &lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
  &lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;

  &lt;span class=&quot;c1&quot;&gt;# more presentation methods...&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 went to test this, and got a bit stuck! I needed to provide a view context to
this class that I could call view helpers on. I could pass in a double, but
would then have to stub out every single view helper that the presenter used.
This would mean my tests would be testing my stubs, not the actual behaviour. I
could use a &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;double.as_null_object&lt;/code&gt;, but that assumes that all the helpers are
OK to return &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;nil&lt;/code&gt; - that’s an &lt;em&gt;extremely&lt;/em&gt; uncommon thing for a view helper to
do!&lt;/p&gt;

&lt;p&gt;I stopped and thought for a minute - I know that there are several types of
RSpec tests where you can access a view context - particularly &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;controller&lt;/code&gt; and
&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;view&lt;/code&gt; specs. I decided to begin my investigation with view specs, since this
was most closely aligned with what I was trying to test.&lt;/p&gt;

&lt;p&gt;I started with the source of rspec-rails, particularly
&lt;a href=&quot;https://github.com/rspec/rspec-rails/blob/d2a9e0e1b18d7d0d95b98dfa6b31eadd8d1b3985/lib/rspec/rails/view_rendering.rb&quot;&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;lib/rspec/rails/view_rendering.rb&lt;/code&gt;&lt;/a&gt;.
It &lt;em&gt;sort of&lt;/em&gt; looked relevant, but it looked like it was using a lot of methods
coming from elsewhere - things like &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;controller&lt;/code&gt;, and &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;lookup_context&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Next, I looked to see where this module was used, and there I found
&lt;a href=&quot;https://github.com/rspec/rspec-rails/blob/d2a9e0e1b18d7d0d95b98dfa6b31eadd8d1b3985/lib/rspec/rails/example/view_example_group.rb&quot;&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;lib/rspec/rails/example/view_example_group.rb&lt;/code&gt;&lt;/a&gt;.
This looked even more relevant - I could see things like controllers, and
helpers being set up. Great! All of these were using a method named &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;view&lt;/code&gt;,
though, and while the method documentation for this looked relevant - “The
instance of &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;ActionView::Base&lt;/code&gt; that is used to render the template.” - the
actual method body just returned the value of &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;_view&lt;/code&gt; - and this wasn’t
instantiated inside this module.&lt;/p&gt;

&lt;p&gt;Looking at the top of this module, I could see an include from Rails itself -
&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;include ActionView::TestCase::Behavior&lt;/code&gt;. This was a real giveaway, because all
of the other top-level includes were for other modules from within rspec-rails.&lt;/p&gt;

&lt;p&gt;I switched over to the Rails repository, and headed into &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;lib/action_view&lt;/code&gt; to
find this class, and I found it at
&lt;a href=&quot;https://github.com/rails/rails/blob/90357af08048ef5076730505f6e7b14a81f33d0c/actionview/lib/action_view/test_case.rb&quot;&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;actionview/lib/action_view/test_case.rb&lt;/code&gt;&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Within this class, I found the
&lt;a href=&quot;https://github.com/rails/rails/blob/90357af08048ef5076730505f6e7b14a81f33d0c/actionview/lib/action_view/test_case.rb#L203&quot;&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;view&lt;/code&gt;&lt;/a&gt;
method. This method was aliased to &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;_view&lt;/code&gt;, which was what RSpec was using.&lt;/p&gt;

&lt;p&gt;In
&lt;a href=&quot;https://github.com/rails/rails/blob/90357af08048ef5076730505f6e7b14a81f33d0c/actionview/lib/action_view/test_case.rb#L104&quot;&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;setup_with_controller&lt;/code&gt;&lt;/a&gt;,
I could see that the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;@controller&lt;/code&gt; variable was set to an instance of
&lt;a href=&quot;https://github.com/rails/rails/blob/90357af08048ef5076730505f6e7b14a81f33d0c/actionview/lib/action_view/test_case.rb#L13&quot;&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;ActionView::TestCase::TestController&lt;/code&gt;&lt;/a&gt;.
Neat! This means that the set up of a view context is entirely contained to this
class, and I can do exactly what the view spec module is doing - include
&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;ActionView::TestCase::Behaviour&lt;/code&gt;, and then access the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;view&lt;/code&gt; method in my spec
to use the view context.&lt;/p&gt;

&lt;p&gt;As an 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;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;no&quot;&gt;MyPresenter&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;ss&quot;&gt;type: :presenter&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;do&lt;/span&gt;
  &lt;span class=&quot;kp&quot;&gt;include&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;TestCase&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;no&quot;&gt;Behavior&lt;/span&gt;

  &lt;span class=&quot;n&quot;&gt;describe&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;#file_size_human&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 expected value&quot;&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;do&lt;/span&gt;
      &lt;span class=&quot;nb&quot;&gt;format&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;new&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;ss&quot;&gt;byte_size: &lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;1500&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;described_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;nb&quot;&gt;format&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;view&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;).&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;file_size_human&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;s2&quot;&gt;&quot;1.5 KB&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;span class=&quot;k&quot;&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
</description>
        <pubDate>Thu, 02 Sep 2021 00:00:00 +1200</pubDate>
        <link>https://joshmcarthur.com/til/2021/09/02/access-actionview-context-in-an-rspec-test.html</link>
        <guid isPermaLink="true">https://joshmcarthur.com/til/2021/09/02/access-actionview-context-in-an-rspec-test.html</guid>
        
        <category>til</category>
        
        <category>rails</category>
        
        <category>testing</category>
        
        
        <category>TIL</category>
        
      </item>
    
      <item>
        <title>Access parent node with Capybara</title>
        <description>&lt;p&gt;Sometimes I’ll need to find an element in relation to another. An example of this is a link with a single icon element inside 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;a&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;href=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;/example&quot;&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;&amp;gt;&amp;lt;img&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;src=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;icon.png&quot;&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;alt=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;Example Page&quot;&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;/&amp;gt;&amp;lt;/a&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Because Capybara element finders can be chained (e.g. you can do &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;find().find().find()&lt;/code&gt; to traverse down elements, the ability of XPath to traverse the DOM tree can be used to easily find the parent node:&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;page&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;find&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;img[alt=&apos;Example Page&apos;]&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;).&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;find&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;ss&quot;&gt;:xpath&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;..&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;c1&quot;&gt;# =&amp;gt; Returns the &apos;a&apos; element&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Any XPath can be passed here, for example if you wanted to find a sibling of the parent or traverse further up the DOM tree. Handy!&lt;/p&gt;

&lt;p&gt;— &lt;a href=&quot;https://stackoverflow.com/a/6611237&quot;&gt;via Stackoverflow&lt;/a&gt;&lt;/p&gt;

</description>
        <pubDate>Wed, 02 Jun 2021 00:00:00 +1200</pubDate>
        <link>https://joshmcarthur.com/til/2021/06/02/access-parent-node-with-capybara.html</link>
        <guid isPermaLink="true">https://joshmcarthur.com/til/2021/06/02/access-parent-node-with-capybara.html</guid>
        
        <category>til</category>
        
        <category>rails</category>
        
        <category>capybara</category>
        
        <category>testing</category>
        
        
        <category>TIL</category>
        
      </item>
    
      <item>
        <title>ActiveRecord::Base.update actually calls model setter methods</title>
        <description>&lt;p&gt;When an update method like &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;@my_record.update(test_attr_1: &quot;foo&quot;, test_attr_2: &quot;bar&quot;)&lt;/code&gt; is called, my assumption was that those attributes immediately got passed to some ActiveRecord magic to update the record. Not true! Each attribute key calls the setter on the object - in this case, &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;@my_record.test_attr_1=(new_value)&lt;/code&gt; and &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;@my_record.test_attr_2=(new_value)&lt;/code&gt; would be called.&lt;/p&gt;

&lt;p&gt;This is neat because you can ‘redirect’ that attribute assignment to actually do something else, like set a different column. Here’s what an example that shows how to allow a boolean to be passed into an attribute called “enabled” that instead sets a “enabled_at” timestamp:&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;enabled&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;flag&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;nf&quot;&gt;enabled_at&lt;/span&gt; &lt;span class=&quot;o&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;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;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;kp&quot;&gt;nil&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;enabled?&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;enabled_at&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;present?&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;Usage:&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;# Updating&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;thing&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;Thing&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;last&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;thing&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;update&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;ss&quot;&gt;enabled: &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;c1&quot;&gt;# Sets enabled_at to Time.zone.now and saves&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;thing&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;enabled?&lt;/span&gt; &lt;span class=&quot;c1&quot;&gt;# =&amp;gt; true&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;thing&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;update&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;ss&quot;&gt;enabled: &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;# Sets enabled_at to nil and saves&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;thing&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;enabled?&lt;/span&gt; &lt;span class=&quot;c1&quot;&gt;# =&amp;gt; false&lt;/span&gt;

&lt;span class=&quot;c1&quot;&gt;# Instantiating&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;thing&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;Thing&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;enabled: &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;thing&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;enabled?&lt;/span&gt; &lt;span class=&quot;c1&quot;&gt;# =&amp;gt; false&lt;/span&gt;

&lt;span class=&quot;c1&quot;&gt;# Assign before save&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;thing&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;Thing&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;last&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;thing&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;assign_attributes&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;ss&quot;&gt;enabled: &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;c1&quot;&gt;# Enabled, but not saved yet!&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;thing&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;save!&lt;/span&gt; &lt;span class=&quot;c1&quot;&gt;# OK, saved now&lt;/span&gt;

&lt;span class=&quot;c1&quot;&gt;# And of course this works the same as any other model attribute&lt;/span&gt;
&lt;span class=&quot;c1&quot;&gt;# when it comes to form/template stuff:&lt;/span&gt;

&lt;span class=&quot;c1&quot;&gt;# In ThingController#create:&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;nf&quot;&gt;require&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;ss&quot;&gt;:thing&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;).&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;permit&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;:description&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;ss&quot;&gt;:enabled&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;

&lt;span class=&quot;c1&quot;&gt;# Template:&lt;/span&gt;
&lt;span class=&quot;o&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;sx&quot;&gt;%= f.check_box :enabled %&amp;gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;You could even do more than one assignment - for example, a &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;publish&lt;/code&gt; boolean might manage more than one timestamp:&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;publish&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;should_publish&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;should_publish&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;nf&quot;&gt;published_at&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;nb&quot;&gt;self&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;unpublished_at&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;kp&quot;&gt;nil&lt;/span&gt;
  &lt;span class=&quot;k&quot;&gt;else&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;nf&quot;&gt;published_at&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;kp&quot;&gt;nil&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;nf&quot;&gt;unpublished_at&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;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;In the scenario of publishing, this method is useful because if a record is unpublished, knowing &lt;em&gt;when&lt;/em&gt; this happened is relevant - just setting &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;published_at&lt;/code&gt; to &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;nil&lt;/code&gt; wouldn’t indicate this.&lt;/p&gt;

&lt;blockquote&gt;
  &lt;p&gt;Note: Anything more complex than this should be in its own class. A service object is probably what you are looking for.&lt;/p&gt;
&lt;/blockquote&gt;
</description>
        <pubDate>Sun, 16 May 2021 00:00:00 +1200</pubDate>
        <link>https://joshmcarthur.com/til/2021/05/16/activerecordbaseupdate-calls-model-setters.html</link>
        <guid isPermaLink="true">https://joshmcarthur.com/til/2021/05/16/activerecordbaseupdate-calls-model-setters.html</guid>
        
        <category>til</category>
        
        <category>rails</category>
        
        
        <category>TIL</category>
        
      </item>
    
      <item>
        <title>Handy Ruby shorthand for testing class ancestors</title>
        <description>&lt;p&gt;Today I was working on a method that had to take a string, and resolve it to a class. This method needed to perform some validation on the model class, to make sure it was a type we expected.&lt;/p&gt;

&lt;p&gt;My first iteration of this didn’t work - I expected that I could use &lt;a href=&quot;https://docs.ruby-lang.org/en/2.7.0/Object.html#method-i-kind_of-3F&quot;&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Object#kind_of?&lt;/code&gt;&lt;/a&gt;, however this only works for testing whether an &lt;em&gt;instance&lt;/em&gt; is a kind of class or module, rather than the class itself. This check looked like:&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;model&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;kind_of?&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;no&quot;&gt;ParentThing&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 next iteration did a direct superclass comparison:&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;model&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;superclass&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;==&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;ParentThing&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;This worked, but was more brittle than I wanted, since it would only work when &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;ParentThing&lt;/code&gt; was the &lt;em&gt;direct&lt;/em&gt; ancestor of the class.&lt;/p&gt;

&lt;p&gt;I checked in on my company Slack, where &lt;a href=&quot;https://eoinkelly.info/&quot;&gt;Eoin&lt;/a&gt; reminded me of the &lt;a href=&quot;https://docs.ruby-lang.org/en/2.7.0/Module.html#method-i-ancestors&quot;&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;ancestors&lt;/code&gt;&lt;/a&gt; method, which returned an array of the ancestors of the class or module the method was called on - essentially, the superclass chain. From this, I composed the test:&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;model&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;ancestors&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;include?&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;no&quot;&gt;ParentThing&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 worked, was flexible enough to allow &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;ParentThing&lt;/code&gt; to not be the direct ancestor, so I went to commit it, when Rubocop conveniently told me of a recommended replacement:&lt;/p&gt;

&lt;blockquote&gt;
  &lt;p&gt;&lt;em&gt;Performance/AncestorsInclude: Use &amp;lt;= instead of ancestors.include?&lt;/em&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;This led me to the &lt;a href=&quot;https://docs.rubocop.org/rubocop-performance/cops_performance.html&quot;&gt;Rubocop documentation&lt;/a&gt;, then to the &lt;a href=&quot;https://github.com/JuanitoFatas/fast-ruby#ancestorsinclude-vs--code&quot;&gt;Fast Ruby reference&lt;/a&gt;, indicating that not only does that shorthand exist, it is also faster. Neat!&lt;/p&gt;

&lt;p&gt;The &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;&amp;lt;=&lt;/code&gt; method, along with a few other handy-looking examples, are documented in the &lt;a href=&quot;https://docs.ruby-lang.org/en/2.7.0/Module.html#method-i-3C-3D&quot;&gt;Ruby documentation&lt;/a&gt;.&lt;/p&gt;

&lt;blockquote&gt;
  &lt;p&gt;&lt;strong&gt;mod &amp;lt;= other → true, false, or nil&lt;/strong&gt;&lt;/p&gt;

  &lt;p&gt;Returns true if mod is a subclass of other or is the same as other. Returns nil if there’s no relationship between the two. (Think of the relationship in terms of the class definition: “class A &amp;lt; B” implies “A &amp;lt; B”.)&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;My final check implementation became a lot simpler:&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;model&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&amp;lt;=&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;ParentThing&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

</description>
        <pubDate>Thu, 29 Apr 2021 00:00:00 +1200</pubDate>
        <link>https://joshmcarthur.com/til/2021/04/29/handy-ruby-shorthand-for-testing-class-ancestors.html</link>
        <guid isPermaLink="true">https://joshmcarthur.com/til/2021/04/29/handy-ruby-shorthand-for-testing-class-ancestors.html</guid>
        
        <category>til</category>
        
        <category>ruby</category>
        
        
        <category>TIL</category>
        
      </item>
    
      <item>
        <title>Go offline with Capybara, Selenium and Chromedriver</title>
        <description>&lt;blockquote&gt;
  &lt;p&gt;There’s some preamble here. &lt;a href=&quot;#solution&quot;&gt;Jump to solution&lt;/a&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Recently, I’ve been working on a client project that needs to function offline. This means that I have a subset of tests where I need to change the connectivity status of the browser driving the tests to simulate being offline to make assertions that the application is behaving correctly.&lt;/p&gt;

&lt;p&gt;I already knew that the devtools had the capability to simulate either being completely offline, or to impose latency and throughput on the connection to simulate poor connectivity and/or older connection standards like 2G.&lt;/p&gt;

&lt;p&gt;Because of this, I &lt;em&gt;suspected&lt;/em&gt; that there would be an API to change the network connection status, I just had to track it down. After a bit of a journey through Selenium’s different layers (and languages - often I’m stumble across what I’m looking for in a different language binding for Selenium like .NET or Python, and then have to go and find the equivalent bit of code in Ruby), I found the solution:&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;#&lt;/span&gt;
&lt;span class=&quot;c1&quot;&gt;# Sets network conditions&lt;/span&gt;
&lt;span class=&quot;c1&quot;&gt;#&lt;/span&gt;
&lt;span class=&quot;c1&quot;&gt;# @param [Hash] conditions&lt;/span&gt;
&lt;span class=&quot;c1&quot;&gt;# @option conditions [Integer] :latency&lt;/span&gt;
&lt;span class=&quot;c1&quot;&gt;# @option conditions [Integer] :throughput&lt;/span&gt;
&lt;span class=&quot;c1&quot;&gt;# @option conditions [Boolean] :offline&lt;/span&gt;
&lt;span class=&quot;c1&quot;&gt;#&lt;/span&gt;

&lt;span class=&quot;k&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;network_conditions&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;conditions&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
  &lt;span class=&quot;vi&quot;&gt;@bridge&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;network_conditions&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;conditions&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;— &lt;a href=&quot;https://github.com/SeleniumHQ/selenium/blob/6c701582f1724bd9d33a9017cb4189eb4e4053c9/rb/lib/selenium/webdriver/common/driver_extensions/has_network_conditions.rb#L23&quot;&gt;Source&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;This method calls the setter on &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;@bridge&lt;/code&gt;, which invokes the command through chromedriver to actually set the conditions:&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;# File &apos;selenium/webdriver/chrome/bridge.rb&apos;, line 45&lt;/span&gt;

&lt;span class=&quot;k&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;network_conditions&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;conditions&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
  &lt;span class=&quot;n&quot;&gt;execute&lt;/span&gt; &lt;span class=&quot;ss&quot;&gt;:set_network_conditions&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;ss&quot;&gt;network_conditions: &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;conditions&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;This method is defined as an ‘extension’ (basically, a module) on the Capybara page’s driver’s &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;browser&lt;/code&gt; (this is essentially a pointer to the running browser application). It functions as a setter method in Ruby, so we can just pass a hash to set the variables we want. It’s worth pointing out that I &lt;strong&gt;did&lt;/strong&gt; have to provide all of these hash keys - it doesn’t appear to be possible to provide just one.&lt;/p&gt;

&lt;p&gt;&lt;span id=&quot;solution&quot;&gt;&lt;/span&gt;&lt;/p&gt;

&lt;p&gt;Here’s how I set my test browser to offline:&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;page&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;driver&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;browser&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;network_conditions&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;offline: &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;latency: &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;ss&quot;&gt;throughput: &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;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;And online 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;page&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;driver&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;browser&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;network_conditions&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;offline: &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;ss&quot;&gt;latency: &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;ss&quot;&gt;throughput: &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;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;You could also adjust latency and throughput if you wished, so while the application was &lt;em&gt;technically&lt;/em&gt; online, it perhaps had very high latecy and very low throughput. This would be useful for testing some kind of ‘slow network connection’ message, for example.&lt;/p&gt;

</description>
        <pubDate>Thu, 29 Apr 2021 00:00:00 +1200</pubDate>
        <link>https://joshmcarthur.com/til/2021/04/29/go-offline-with-capybara-selenium-and-chromedriver.html</link>
        <guid isPermaLink="true">https://joshmcarthur.com/til/2021/04/29/go-offline-with-capybara-selenium-and-chromedriver.html</guid>
        
        <category>til</category>
        
        <category>testing</category>
        
        <category>ruby</category>
        
        
        <category>TIL</category>
        
      </item>
    
      <item>
        <title>Access browser console messages from a Capybara test</title>
        <description>&lt;p&gt;A very handy API I’ve stumbled across today:&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;page&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;driver&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;browser&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;manage&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;logs&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;get&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;ss&quot;&gt;:browser&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 returns an array of &lt;a href=&quot;https://github.com/SeleniumHQ/selenium/blob/93c9ec77403f1c379a8f38d7b499f4f483b40943/rb/lib/selenium/webdriver/common/log_entry.rb&quot;&gt;Selenium::LogEntry&lt;/a&gt; instances which have the expected properties - &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;level&lt;/code&gt;, &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;timestamp&lt;/code&gt;, and &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;message&lt;/code&gt;. You can use standard Enumerable methods to filter the log entries down to the information relevant to you.&lt;/p&gt;

&lt;p&gt;For example, all error messages:&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;page&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;driver&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;browser&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;manage&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;logs&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;get&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;ss&quot;&gt;:browser&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;|&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;le&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;|&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;le&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;level&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;==&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;SEVERE&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;ss&quot;&gt;:message&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;s2&quot;&gt;&quot;http://127.0.0.1:40539/madeup - Failed to load resource: net::ERR_CONNECTION_REFUSED&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;...&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
</description>
        <pubDate>Tue, 27 Apr 2021 00:00:00 +1200</pubDate>
        <link>https://joshmcarthur.com/til/2021/04/27/access-browser-console-messages-from-a-capybara-test.html</link>
        <guid isPermaLink="true">https://joshmcarthur.com/til/2021/04/27/access-browser-console-messages-from-a-capybara-test.html</guid>
        
        <category>til</category>
        
        <category>testing</category>
        
        <category>rails</category>
        
        <category>capybara</category>
        
        
        <category>TIL</category>
        
      </item>
    
      <item>
        <title>Use Foundation data attributes in a React functional component</title>
        <description>&lt;p&gt;I’ve been porting some previously server-rendered Rails templates to instead sit
within a React application. This particular application uses &lt;a href=&quot;https://get.foundation/sites&quot;&gt;Foundation
Sites&lt;/a&gt; for base styles, and uses some of
Foundation’s components which use Javascript to function - mostly basic stuff
like &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;data-dropdown&lt;/code&gt;, &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;data-accordion&lt;/code&gt;, &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;data-tabs&lt;/code&gt;. Foundation (&lt;em&gt;still&lt;/em&gt;) uses
jQuery for setting up these elements, and by default, initializes based on the
&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;ready&lt;/code&gt; event. This is at odds with React, since components are dynamically
added and removed from the DOM.&lt;/p&gt;

&lt;p&gt;To get this working, I first moved the markup that was in a template into a
functional component:&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;// app/frontend/components/Dropdown.jsx&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;import&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;React&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;from&lt;/span&gt; &lt;span class=&quot;dl&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;react&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;export&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;default&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;o&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;div&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;o&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;button&lt;/span&gt; &lt;span class=&quot;kd&quot;&gt;class&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;button&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&quot;&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;&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;button&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&quot;&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;data&lt;/span&gt;&lt;span class=&quot;o&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;dl&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;example-dropdown&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;gt;&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;Toggle&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;Dropdown&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;sr&quot;&gt;/button&lt;/span&gt;&lt;span class=&quot;err&quot;&gt;&amp;gt;
&lt;/span&gt;    &lt;span class=&quot;o&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;div&lt;/span&gt; &lt;span class=&quot;kd&quot;&gt;class&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;dropdown-pane&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&quot;&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;id&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-dropdown&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&quot;&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;data&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;-&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;dropdown&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;data&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;-&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;auto&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;-&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;focus&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;true&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;gt;&lt;/span&gt;
      &lt;span class=&quot;nx&quot;&gt;Example&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;dropdown&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;
    &lt;span class=&quot;o&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;sr&quot;&gt;/div&lt;/span&gt;&lt;span class=&quot;err&quot;&gt;&amp;gt;
&lt;/span&gt;  &lt;span class=&quot;o&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;sr&quot;&gt;/div&lt;/span&gt;&lt;span class=&quot;err&quot;&gt;&amp;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;&lt;a href=&quot;https://codesandbox.io/s/quirky-volhard-puw9q&quot;&gt;CodeSandbox&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The component renders, but the dropdown doesn’t work! This is because the
component has been mounted to the page &lt;em&gt;after&lt;/em&gt; Foundation has initialised.&lt;/p&gt;

&lt;p&gt;To resolve this, I used two React hooks
&lt;a href=&quot;https://reactjs.org/docs/hooks-reference.html#useref&quot;&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;useRef&lt;/code&gt;&lt;/a&gt; and
&lt;a href=&quot;https://reactjs.org/docs/hooks-reference.html#useeffect&quot;&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;useEffect&lt;/code&gt;&lt;/a&gt;. &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;useRef&lt;/code&gt;
is used to establish a reference to the DOM node, and &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;useEffect&lt;/code&gt; is used to run
a function when the component has rendered.&lt;/p&gt;

&lt;p&gt;Here’s how the hooks are used in the working component:&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;// app/frontend/components/Dropdown.jsx&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;import&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;React&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;useRef&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;useEffect&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;from&lt;/span&gt; &lt;span class=&quot;dl&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;react&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;import&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;jQuery&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;from&lt;/span&gt; &lt;span class=&quot;dl&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;jquery&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;import&lt;/span&gt; &lt;span class=&quot;dl&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;foundation-sites&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;export&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;default&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;nx&quot;&gt;foundationRef&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;useRef&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;kc&quot;&gt;null&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
  &lt;span class=&quot;nx&quot;&gt;useEffect&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;jQuery&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;foundationRef&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;current&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;).&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;foundation&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;foundationRef&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;o&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;div&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;ref&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;foundationRef&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;o&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;button&lt;/span&gt; &lt;span class=&quot;kd&quot;&gt;class&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;button&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&quot;&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;&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;button&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&quot;&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;data&lt;/span&gt;&lt;span class=&quot;o&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;dl&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;example-dropdown&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;gt;&lt;/span&gt;
        &lt;span class=&quot;nx&quot;&gt;Toggle&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;Dropdown&lt;/span&gt;
      &lt;span class=&quot;o&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;sr&quot;&gt;/button&lt;/span&gt;&lt;span class=&quot;err&quot;&gt;&amp;gt;
&lt;/span&gt;      &lt;span class=&quot;o&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;div&lt;/span&gt;
        &lt;span class=&quot;kd&quot;&gt;class&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;dropdown-pane&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&quot;&lt;/span&gt;
        &lt;span class=&quot;nx&quot;&gt;id&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-dropdown&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&quot;&lt;/span&gt;
        &lt;span class=&quot;nx&quot;&gt;data&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;-&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;dropdown&lt;/span&gt;
        &lt;span class=&quot;nx&quot;&gt;data&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;-&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;auto&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;-&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;focus&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;true&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&quot;&lt;/span&gt;
      &lt;span class=&quot;o&quot;&gt;&amp;gt;&lt;/span&gt;
        &lt;span class=&quot;nx&quot;&gt;Example&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;dropdown&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;
      &lt;span class=&quot;o&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;sr&quot;&gt;/div&lt;/span&gt;&lt;span class=&quot;err&quot;&gt;&amp;gt;
&lt;/span&gt;    &lt;span class=&quot;o&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;sr&quot;&gt;/div&lt;/span&gt;&lt;span class=&quot;err&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;&lt;a href=&quot;https://codesandbox.io/s/romantic-liskov-ggrv5&quot;&gt;CodeSandbox&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The first argument to &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;useRef&lt;/code&gt; is set to &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;null&lt;/code&gt;, as there is no additional value
to use until the component is mounted. &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;useEffect&lt;/code&gt; accepts the ref as a
dependency, as it only needs to render when the ref changes (e.g. when the
component is mounted), not every re-render.&lt;/p&gt;

&lt;hr /&gt;

&lt;p&gt;Foundation JS is getting pretty dated these days - it’s a heavy dependency, and
still relies on jQuery to work correctly. For projects already using the
framework though, it provides a useful mixture of styles and scripts, so it’s
worthwhile being able to continue to leverage these javascript-backed behaviours
in React components.&lt;/p&gt;
</description>
        <pubDate>Tue, 06 Apr 2021 00:00:00 +1200</pubDate>
        <link>https://joshmcarthur.com/til/2021/04/06/use-foundation-data-attributes-in-a-react-functional-component.html</link>
        <guid isPermaLink="true">https://joshmcarthur.com/til/2021/04/06/use-foundation-data-attributes-in-a-react-functional-component.html</guid>
        
        <category>til</category>
        
        <category>frontend</category>
        
        <category>technology</category>
        
        <category>react</category>
        
        
        <category>TIL</category>
        
      </item>
    
      <item>
        <title>What happens when I run &apos;rake db:migrate&apos; in Ruby on Rails?</title>
        <description>&lt;p&gt;&lt;strong&gt;This post is written stepping through the most recent stable branch of Rails
on Github: &lt;a href=&quot;https://github.com/rails/rails/tree/6-1-stable&quot;&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;6-1-stable&lt;/code&gt;&lt;/a&gt;. It
may not remain up to date.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Reading time: about 20 minutes&lt;/p&gt;

&lt;p&gt;&lt;small&gt;&lt;a href=&quot;#content-start&quot;&gt;Skip table of contents&lt;/a&gt;&lt;/small&gt;&lt;/p&gt;

&lt;ul id=&quot;markdown-toc&quot;&gt;
  &lt;li&gt;&lt;a href=&quot;#how-is-the-dbmigrate-rake-task-defined&quot; id=&quot;markdown-toc-how-is-the-dbmigrate-rake-task-defined&quot;&gt;How is the ‘db:migrate’ rake task defined?&lt;/a&gt;    &lt;ul&gt;
      &lt;li&gt;&lt;a href=&quot;#the-rails-gem-includes-activerecord-as-a-gem-dependency&quot; id=&quot;markdown-toc-the-rails-gem-includes-activerecord-as-a-gem-dependency&quot;&gt;The Rails gem includes ‘activerecord’ as a gem dependency:&lt;/a&gt;&lt;/li&gt;
      &lt;li&gt;&lt;a href=&quot;#the-activerecord-gem-adds-its-own-lib-directory-to-rubys-load-path&quot; id=&quot;markdown-toc-the-activerecord-gem-adds-its-own-lib-directory-to-rubys-load-path&quot;&gt;The activerecord gem adds it’s own ‘lib/` directory to Ruby’s load path:&lt;/a&gt;&lt;/li&gt;
      &lt;li&gt;&lt;a href=&quot;#the-activerecord-gem-railtie-file-is-require-d-by-rails&quot; id=&quot;markdown-toc-the-activerecord-gem-railtie-file-is-require-d-by-rails&quot;&gt;The ‘activerecord’ gem ‘railtie’ file is &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;require&lt;/code&gt;-d by Rails&lt;/a&gt;&lt;/li&gt;
      &lt;li&gt;&lt;a href=&quot;#activerecord-railtie-is-evaluated&quot; id=&quot;markdown-toc-activerecord-railtie-is-evaluated&quot;&gt;ActiveRecord Railtie is evaluated&lt;/a&gt;&lt;/li&gt;
      &lt;li&gt;&lt;a href=&quot;#activerecord-database-rake-tasks-are-defined&quot; id=&quot;markdown-toc-activerecord-database-rake-tasks-are-defined&quot;&gt;ActiveRecord Database Rake tasks are defined&lt;/a&gt;&lt;/li&gt;
    &lt;/ul&gt;
  &lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;#how-is-my-migration-run-when-i-run-bundle-exec-rake-dbmigrate&quot; id=&quot;markdown-toc-how-is-my-migration-run-when-i-run-bundle-exec-rake-dbmigrate&quot;&gt;How is my migration run when I run &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;bundle exec rake db:migrate&lt;/code&gt;?&lt;/a&gt;    &lt;ul&gt;
      &lt;li&gt;&lt;a href=&quot;#activerecordtasksdatabasetasksmigrate&quot; id=&quot;markdown-toc-activerecordtasksdatabasetasksmigrate&quot;&gt;ActiveRecord::Tasks::DatabaseTasks.migrate&lt;/a&gt;&lt;/li&gt;
      &lt;li&gt;&lt;a href=&quot;#activerecordbaseconnectionmigration_context&quot; id=&quot;markdown-toc-activerecordbaseconnectionmigration_context&quot;&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;ActiveRecordBase.connection.migration_context&lt;/code&gt;&lt;/a&gt;&lt;/li&gt;
      &lt;li&gt;&lt;a href=&quot;#activerecordmigrationcontext&quot; id=&quot;markdown-toc-activerecordmigrationcontext&quot;&gt;ActiveRecord::MigrationContext&lt;/a&gt;&lt;/li&gt;
      &lt;li&gt;&lt;a href=&quot;#activerecordmigrator&quot; id=&quot;markdown-toc-activerecordmigrator&quot;&gt;ActiveRecord::Migrator&lt;/a&gt;&lt;/li&gt;
      &lt;li&gt;&lt;a href=&quot;#your-migration-activerecordmigration&quot; id=&quot;markdown-toc-your-migration-activerecordmigration&quot;&gt;Your migration! (ActiveRecord::Migration)&lt;/a&gt;&lt;/li&gt;
      &lt;li&gt;&lt;a href=&quot;#updating-dbschemarb&quot; id=&quot;markdown-toc-updating-dbschemarb&quot;&gt;Updating db/schema.rb&lt;/a&gt;&lt;/li&gt;
    &lt;/ul&gt;
  &lt;/li&gt;
&lt;/ul&gt;

&lt;div id=&quot;content-start&quot;&gt;&lt;/div&gt;

&lt;h3 id=&quot;how-is-the-dbmigrate-rake-task-defined&quot;&gt;How is the ‘db:migrate’ rake task defined?&lt;/h3&gt;

&lt;h5 id=&quot;the-rails-gem-includes-activerecord-as-a-gem-dependency&quot;&gt;The Rails gem includes ‘activerecord’ as a gem dependency:&lt;/h5&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;s&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;add_dependency&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;activerecord&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;  &lt;span class=&quot;n&quot;&gt;version&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;— &lt;a href=&quot;https://github.com/rails/rails/blob/a215e47fb14af955071264b20818ca3834f477f2/rails.gemspec#L35&quot;&gt;rails/rails.gemspec&lt;/a&gt;&lt;/p&gt;

&lt;blockquote&gt;
  &lt;p&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;version&lt;/code&gt; is set by reading a file named ‘RAILS_VERSION’ in the ‘rails’ gem
directory. In &lt;a href=&quot;https://github.com/rails/rails/blob/6-1-stable/RAILS_VERSION&quot;&gt;this
branch&lt;/a&gt;, this is
6.1.3.1. This means that the core gem versions like ActiveRecord track the
Rails gem version exactly.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h5 id=&quot;the-activerecord-gem-adds-its-own-lib-directory-to-rubys-load-path&quot;&gt;The activerecord gem adds it’s own ‘lib/` directory to Ruby’s load path:&lt;/h5&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;s&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;require_path&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;lib&quot;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;— &lt;a href=&quot;https://github.com/rails/rails/blob/a215e47fb14af955071264b20818ca3834f477f2/activerecord/activerecord.gemspec#L21&quot;&gt;rails/activerecord/activerecord.gemspec&lt;/a&gt;&lt;/p&gt;

&lt;h5 id=&quot;the-activerecord-gem-railtie-file-is-require-d-by-rails&quot;&gt;The ‘activerecord’ gem ‘railtie’ file is &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;require&lt;/code&gt;-d by Rails&lt;/h5&gt;

&lt;p&gt;This can happen in two ways: either by &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;config/application.rb&lt;/code&gt; &lt;a href=&quot;https://github.com/rails/rails/blob/a215e47fb14af955071264b20818ca3834f477f2/railties/lib/rails/generators/rails/app/templates/config/application.rb.tt#L4&quot;&gt;requiring
‘rails/all’&lt;/a&gt;,
or by &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;config/application.rb&lt;/code&gt; specifically loading the Railtie:&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;active_record/railtie&quot;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;&amp;amp;mdash: &lt;a href=&quot;https://github.com/rails/rails/blob/a215e47fb14af955071264b20818ca3834f477f2/railties/lib/rails/generators/rails/app/templates/config/application.rb.tt#L10&quot;&gt;rails/railties/lib/rails/generators/app/templates/config/application.rb.tt&lt;/a&gt;)&lt;/p&gt;

&lt;h5 id=&quot;activerecord-railtie-is-evaluated&quot;&gt;ActiveRecord Railtie is evaluated&lt;/h5&gt;

&lt;p&gt;A ‘Railtie’ appears in most Rails-related gems. It’s like an initializer, but
for a library to set itself up within Rails, rather than within your
application. Railties are used to do things like &lt;a href=&quot;https://github.com/rails/rails/blob/a215e47fb14af955071264b20818ca3834f477f2/activerecord/lib/active_record/railtie.rb#L29&quot;&gt;set up default configuration&lt;/a&gt;,
&lt;a href=&quot;https://github.com/rails/rails/blob/a215e47fb14af955071264b20818ca3834f477f2/activerecord/lib/active_record/railtie.rb#L70&quot;&gt;declare
initiaizers&lt;/a&gt;,
and &lt;strong&gt;define Rake tasks&lt;/strong&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;rake_tasks&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;do&lt;/span&gt;
  &lt;span class=&quot;n&quot;&gt;namespace&lt;/span&gt; &lt;span class=&quot;ss&quot;&gt;:db&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;do&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;task&lt;/span&gt; &lt;span class=&quot;ss&quot;&gt;:load_config&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;do&lt;/span&gt;
      &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;defined?&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;no&quot;&gt;ENGINE_ROOT&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;engine&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;Rails&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;no&quot;&gt;Engine&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;find&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;no&quot;&gt;ENGINE_ROOT&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;engine&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;paths&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;db/migrate&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;].&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;existent&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;Tasks&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;no&quot;&gt;DatabaseTasks&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;migrations_paths&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;+=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;engine&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;paths&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;db/migrate&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;].&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;to_a&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;
      &lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;
  &lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;

  &lt;span class=&quot;nb&quot;&gt;load&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;active_record/railties/databases.rake&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;—
&lt;a href=&quot;https://github.com/rails/rails/blob/a215e47fb14af955071264b20818ca3834f477f2/activerecord/lib/active_record/railtie.rb#L38&quot;&gt;rails/activerecord/lib/active_record/railtie.rb&lt;/a&gt;&lt;/p&gt;

&lt;blockquote&gt;
  &lt;p&gt;See that &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;ENGINE_ROOT&lt;/code&gt; bit? That checks to see if ActiveRecord is being run
within an ‘engine’. A &lt;a href=&quot;https://guides.rubyonrails.org/engines.html&quot;&gt;Rails
engine&lt;/a&gt; is a bit like an
application-within-an-application, and &lt;em&gt;it can have it’s own migrations&lt;/em&gt;. That’s
what this code is doing - if it’s running in an engine, it checks to see if that
engine has a &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;db/migrate&lt;/code&gt; folder. If it does, it adds the engine’s migrations to
the list of all the places ActiveRecord’s database tasks (like &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;db:migrate&lt;/code&gt;)
should look for migration files.&lt;/p&gt;

  &lt;p&gt;For the rest of this guide, we’ll assume we’re not in an engine, so this block
doesn’t affect us. The &lt;strong&gt;load&lt;/strong&gt; line is the important bit.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h5 id=&quot;activerecord-database-rake-tasks-are-defined&quot;&gt;ActiveRecord Database Rake tasks are defined&lt;/h5&gt;

&lt;p&gt;‘databases.rake’ has a special ‘rake’ extension. ‘rake’ files are specially
evaluated in the context of the &lt;a href=&quot;https://ruby.github.io/rake/&quot;&gt;rake&lt;/a&gt; tool, that
defines such methods as ‘namespace’ (group tasks), ‘desc’ (describe what a task
does), and ‘task’ (describe a sequence of actions to take).&lt;/p&gt;

&lt;blockquote&gt;
  &lt;p&gt;I’m not actually sure what Rake does to automatically be able to load .rake
files with the &lt;abbr title=&quot;Domain Specific Language&quot;&gt;DSL&lt;/abbr&gt; methods
already loaded. Maybe another post sometime?&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Within this lengthy file (ActiveRecord makes use of a lot of Rake tasks to
manage databases), the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;db:migrate&lt;/code&gt; task is defined:&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;desc&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;Migrate the database (options: VERSION=x, VERBOSE=false, SCOPE=blog).&quot;&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;task&lt;/span&gt; &lt;span class=&quot;ss&quot;&gt;migrate: :load_config&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;do&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;—
&lt;a href=&quot;https://github.com/rails/rails/blob/a215e47fb14af955071264b20818ca3834f477f2/activerecord/lib/active_record/railties/databases.rake#L88&quot;&gt;rails/activerecord/lib/active_record/railties/databases.rake&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The first thing that will run here is the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;load_config&lt;/code&gt; task (that key: value
syntax is how Rake task
&lt;a href=&quot;https://ruby.github.io/rake/doc/rakefile_rdoc.html#label-Tasks+with+Prerequisites&quot;&gt;prerequisites&lt;/a&gt;
are defined).&lt;/p&gt;

&lt;p&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;load_config&lt;/code&gt; is pretty simple 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;task&lt;/span&gt; &lt;span class=&quot;ss&quot;&gt;load_config: :environment&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;do&lt;/span&gt;
  &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;ActiveRecord&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;no&quot;&gt;Base&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;configurations&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;empty?&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;Base&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;configurations&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&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;Tasks&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;no&quot;&gt;DatabaseTasks&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;database_configuration&lt;/span&gt;
  &lt;span class=&quot;k&quot;&gt;end&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;Migrator&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;migrations_paths&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&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;Tasks&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;no&quot;&gt;DatabaseTasks&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;migrations_paths&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;If there aren’t any configurations, it finds them
(&lt;a href=&quot;https://github.com/rails/rails/blob/a215e47fb14af955071264b20818ca3834f477f2/railties/lib/rails/application/configuration.rb#L270&quot;&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;ActiveRecord::Tasks::DatabaseTasks.database_configuration&lt;/code&gt;&lt;/a&gt;
is worth checking out - this is where your &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;database.yml&lt;/code&gt; file is loaded into
ActiveRecord/Rails!)&lt;/p&gt;

&lt;p&gt;It also passes along the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;migrations_paths&lt;/code&gt; that we saw earlier when engines
were being set up to the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;ActiveRecord::Migrator&lt;/code&gt;. This class will come up again
in the migration process, but for now, this is just setting up the migrator to
know where to load migrations for.&lt;/p&gt;

&lt;blockquote&gt;
  &lt;p&gt;Speculatively, the reason this is done is because it would be technically
possible to create more than one instance of a ‘migrator’, pointing at
different sets of migrations. Passing this configuration in decouples the
class that actually ‘runs’ the migrations from the class that controls the
registry of migration file locations.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;This task depends in turn on the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;environment&lt;/code&gt; task. This isn’t actually part of
ActiveRecord, but of
&lt;a href=&quot;https://github.com/rails/rails/blob/a215e47fb14af955071264b20818ca3834f477f2/railties/lib/rails/application.rb#L523&quot;&gt;Rails&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Basically, it “loads” the application, all of it’s dependencies, initializers,
etc. - all that slow stuff. No need to go into further detail here, other than
to note that without depending on this task, things like &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Rails.env&lt;/code&gt;,
&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Rails.root&lt;/code&gt;, and lots of other useful things aren’t available.&lt;/p&gt;

&lt;p&gt;And now back to the ‘migrate’ task:&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;desc&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;Migrate the database (options: VERSION=x, VERBOSE=false, SCOPE=blog).&quot;&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;task&lt;/span&gt; &lt;span class=&quot;ss&quot;&gt;migrate: :load_config&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;do&lt;/span&gt;
  &lt;span class=&quot;n&quot;&gt;original_db_config&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&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;Base&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;connection_db_config&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;Base&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;configurations&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;configs_for&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;ss&quot;&gt;env_name: &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;Tasks&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;no&quot;&gt;DatabaseTasks&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;env&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;).&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;each&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;do&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;|&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;db_config&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;|&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;Base&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;establish_connection&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;db_config&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&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;Tasks&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;no&quot;&gt;DatabaseTasks&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;migrate&lt;/span&gt;
  &lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;
  &lt;span class=&quot;n&quot;&gt;db_namespace&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;_dump&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;].&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;invoke&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;ensure&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;Base&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;establish_connection&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;original_db_config&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;Looks like some connection shenanigans going on there. The current database
connection config is stashed in a variable, and restored using an &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;ensure&lt;/code&gt; block
(this code will always run, even if an exception is raised). A list of
configurations is looked up matching &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;ActiveRecord::Tasks::DatabaseTasks.env&lt;/code&gt;,
and for each of this environments, &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;ActiveRecord::Tasks::DatabaseTasks.migrate&lt;/code&gt;
is run.&lt;/p&gt;

&lt;p&gt;I’ve never used this, but since I was curious,
&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;ActiveRecord::Tasks::DatabaseTasks&lt;/code&gt; is quite simple:&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;env&lt;/span&gt;
  &lt;span class=&quot;vi&quot;&gt;@env&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;env&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;—
&lt;a href=&quot;https://github.com/rails/rails/blob/a215e47fb14af955071264b20818ca3834f477f2/activerecord/lib/active_record/tasks/database_tasks.rb#L102&quot;&gt;rails/activerecord/lib/active_record/tasks/database_tasks.rb&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;In other words, unless someone or something sets it otherwise, the environment
that the migrations are run in will be the same as the rest of your application
(&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Rails.env&lt;/code&gt;, set from environment variable, command line option, etc). But
migrations &lt;em&gt;can&lt;/em&gt; be run in a different environment from the rest of your
application. This could be used if you had a different database user who had
additional privileges to change the database schema from the user that your
application normally runs with.&lt;/p&gt;

&lt;h3 id=&quot;how-is-my-migration-run-when-i-run-bundle-exec-rake-dbmigrate&quot;&gt;How is my migration run when I run &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;bundle exec rake db:migrate&lt;/code&gt;?&lt;/h3&gt;

&lt;h5 id=&quot;activerecordtasksdatabasetasksmigrate&quot;&gt;ActiveRecord::Tasks::DatabaseTasks.migrate&lt;/h5&gt;

&lt;p&gt;This method is the first sign of migrations actually being ‘run’:&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;migrate&lt;/span&gt;
  &lt;span class=&quot;n&quot;&gt;check_target_version&lt;/span&gt;

  &lt;span class=&quot;n&quot;&gt;scope&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;s2&quot;&gt;&quot;SCOPE&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt;
  &lt;span class=&quot;n&quot;&gt;verbose_was&lt;/span&gt;&lt;span class=&quot;p&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;nf&quot;&gt;verbose&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;nf&quot;&gt;verbose&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;verbose?&lt;/span&gt;

  &lt;span class=&quot;no&quot;&gt;Base&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;connection&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;migration_context&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;migrate&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;target_version&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;migration&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;|&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;scope&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;blank?&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;||&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;scope&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;==&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;migration&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;scope&lt;/span&gt;
  &lt;span class=&quot;k&quot;&gt;end&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;Base&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;clear_cache!&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;ensure&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;nf&quot;&gt;verbose&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;verbose_was&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;First, the target version is checked. Usually, this version isn’t actually
provided - in this case, it’s assumed the runner of the migrations wants the
“latest” version. It &lt;em&gt;is&lt;/em&gt; possible to pass an environment variable named
“VERSION” though - and when it &lt;em&gt;is&lt;/em&gt; provided, it must &lt;a href=&quot;https://github.com/rails/rails/blob/a215e47fb14af955071264b20818ca3834f477f2/activerecord/lib/active_record/tasks/database_tasks.rb#L261&quot;&gt;match the expected
format&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Next, the scope is grabbed out an environment variable called ‘SCOPE’, if
present, and so is the ‘verbose’ setting for migrations (unsurprisingly, using
an environment variabled named ‘VERBOSE’).&lt;/p&gt;

&lt;p&gt;There’s then a block of code that is the next step to run migrations - we’ll
loop back to that, but assuming the migrations all go smoothly, the ActiveRecord
cache is cleared with &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;ActiveRecord::Base.clear_cache!&lt;/code&gt;. &lt;a href=&quot;https://github.com/rails/rails/blob/a215e47fb14af955071264b20818ca3834f477f2/activerecord/lib/active_record/connection_handling.rb#L347&quot;&gt;This
method&lt;/a&gt;
&lt;a href=&quot;https://github.com/rails/rails/blob/a215e47fb14af955071264b20818ca3834f477f2/activerecord/lib/active_record/connection_adapters/schema_cache.rb#L132&quot;&gt;clears a bunch of instance
variables&lt;/a&gt;
that are used to save database schema lookups at runtime.&lt;/p&gt;

&lt;p&gt;If it looks like this method is just looking up a bunch of runtime configuration
from environment variables then calling something else - that’s because it is.
This block of code is simply decoupling environment-variable-based config from
the actual migration process. It’s clear from the code block above that the next
step lies in &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;ActiveRecord::Base.connection.migration_context.migrate&lt;/code&gt;.&lt;/p&gt;

&lt;h5 id=&quot;activerecordbaseconnectionmigration_context&quot;&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;ActiveRecordBase.connection.migration_context&lt;/code&gt;&lt;/h5&gt;

&lt;p&gt;This method is called on a connection - that’s something a bit different from
what’s been seen previously. Remember how in the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;db:migrate&lt;/code&gt; rake task
definition, connections were switched around based on the migration environment?
That’s where this connection comes into play. From here on in, the migration
code is going to need to know which database to connect to, and how - for this
reason, &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;migration_context&lt;/code&gt; is defined on &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;ActiveRecord::Base.connection&lt;/code&gt; (which
is an implementation of an
&lt;a href=&quot;https://github.com/rails/rails/blob/a215e47fb14af955071264b20818ca3834f477f2/activerecord/lib/active_record/connection_adapters/abstract_adapter.rb&quot;&gt;AbstractAdapter&lt;/a&gt;)&lt;/p&gt;
&lt;ul&gt;
  &lt;li&gt;it can call &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;self&lt;/code&gt;, and have an actual connection to the database.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;migration_context&lt;/code&gt; actually just builds another class - &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;MigrationContext&lt;/code&gt;. It
passes two arguments to the initialize method of this class:&lt;/p&gt;

&lt;ol&gt;
  &lt;li&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;migrations_paths&lt;/code&gt; - this has been seen before - it can be overridden in the
connection config, it looks like (I suspect you can specify
&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;migrations_paths&lt;/code&gt; in &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;config/database.yml&lt;/code&gt; for this to take effect), but
most of the time it will fall back to the
&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;ActiveRecord::Migrator.migrations_paths&lt;/code&gt; method that was set in the last
method.&lt;/li&gt;
  &lt;li&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;schema_migration&lt;/code&gt; - this has a &lt;a href=&quot;https://github.com/rails/rails/blob/a215e47fb14af955071264b20818ca3834f477f2/activerecord/lib/active_record/connection_adapters/abstract_adapter.rb#L139&quot;&gt;slightly complex
definition&lt;/a&gt;,
but most of the time ends up being either a &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;ActiveRecord::SchemaMigration&lt;/code&gt;
class or subclass.&lt;/li&gt;
&lt;/ol&gt;

&lt;blockquote&gt;
  &lt;p&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;ActiveRecord::SchemaMigration&lt;/code&gt; is an interesting piece in it’s own right.
It’s actually a model - a model that’s backed by a database table you &lt;em&gt;may&lt;/em&gt;
have seen before called ‘schema_migrations’. You also &lt;em&gt;may not&lt;/em&gt; have seen it
before, because most of the time it never needs to be touched. It holds the
‘state’ of which migrations have run. It has a &lt;a href=&quot;https://github.com/rails/rails/blob/a215e47fb14af955071264b20818ca3834f477f2/activerecord/lib/active_record/schema_migration.rb#L25&quot;&gt;very simple
schema&lt;/a&gt;&lt;/p&gt;
  &lt;ul&gt;
    &lt;li&gt;a single string timestamp column which is also the primary key.&lt;/li&gt;
  &lt;/ul&gt;
&lt;/blockquote&gt;

&lt;h5 id=&quot;activerecordmigrationcontext&quot;&gt;ActiveRecord::MigrationContext&lt;/h5&gt;

&lt;p&gt;This class looks complicated, but is mostly a state machine to control the
direction to migrate in. There are a bunch of helper methods to migrate the
database, back (arbitrarily or to a particular version), forward (arbitrarily or
to a particular version), one step backwards from the current version, or one
step forwards from the current version.&lt;/p&gt;

&lt;p&gt;Because we know this migration context is being called from &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;database_tasks.rb&lt;/code&gt;,
we’re only interested in the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;migrate&lt;/code&gt; 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;migrate&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;target_version&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;kp&quot;&gt;nil&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;case&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;when&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;target_version&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;nil?&lt;/span&gt;
      &lt;span class=&quot;n&quot;&gt;up&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;target_version&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;when&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;current_version&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;==&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;0&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;target_version&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;==&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;0&lt;/span&gt;
      &lt;span class=&quot;p&quot;&gt;[]&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;when&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;current_version&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&amp;gt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;target_version&lt;/span&gt;
      &lt;span class=&quot;n&quot;&gt;down&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;target_version&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;else&lt;/span&gt;
      &lt;span class=&quot;n&quot;&gt;up&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;target_version&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;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;—
&lt;a href=&quot;https://github.com/rails/rails/blob/a215e47fb14af955071264b20818ca3834f477f2/activerecord/lib/active_record/migration.rb#L1058&quot;&gt;rails/activerecord/lib/active_record/migration.rb&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;target_version&lt;/code&gt; came from that &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;VERSION&lt;/code&gt; environment variable, but most of the
time, if you’re running &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;bundle exec rake db:migrate&lt;/code&gt; in a &lt;em&gt;normal&lt;/em&gt; situation,
it’ll be &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;nil&lt;/code&gt;, so we call &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;up(nil)&lt;/code&gt;. If a specific version was passed, and it’s
less than the current migration version (which is looked up from that
‘schema_migrations’ table), then &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;down(target_version)&lt;/code&gt; is called.&lt;/p&gt;

&lt;p&gt;The &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;up&lt;/code&gt; method calls the next part of the chain, the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Migrator&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;def&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;up&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;target_version&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;kp&quot;&gt;nil&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
  &lt;span class=&quot;n&quot;&gt;selected_migrations&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;block_given?&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;migrations&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;|&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;m&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;|&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;yield&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;m&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
  &lt;span class=&quot;k&quot;&gt;else&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;migrations&lt;/span&gt;
  &lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;

  &lt;span class=&quot;no&quot;&gt;Migrator&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;:up&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;selected_migrations&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;schema_migration&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;target_version&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;).&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;migrate&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;—
&lt;a href=&quot;https://github.com/rails/rails/blob/a215e47fb14af955071264b20818ca3834f477f2/activerecord/lib/active_record/migration.rb#L1079&quot;&gt;rails/activerecord/lib/active_record/migration.rb&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;This method looks for migrations to run based on the block, if one is provided -
in &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;ActiveRecord::Tasks::DatabaseTasks.migrate&lt;/code&gt; one &lt;em&gt;is&lt;/em&gt; provided:&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;migrate&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;target_version&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;migration&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;|&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;scope&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;blank?&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;||&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;scope&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;==&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;migration&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;scope&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;In other words, if &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;ENV[&quot;SCOPE&quot;]&lt;/code&gt; was provided to the Rake task, only migrations
that match that scope will be run. Migration ‘scoping’ is based on the filename
of the migration, according to the regular expression stored in
&lt;a href=&quot;https://github.com/rails/rails/blob/a215e47fb14af955071264b20818ca3834f477f2/activerecord/lib/active_record/migration.rb#L574&quot;&gt;ActiveRecord::Migration::MigrationFilenameRegexp&lt;/a&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;/\A([0-9]+)_([_a-z0-9]*)\.?([_a-z0-9]*)?\.rb\z/
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;In other words, migrations are expected to be named with two or three parts:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;a version number (integer 0-9). This is usually a timestamp as seconds since
epoch, but can just be an incrementing number. It has to be present, otherwise
a &lt;a href=&quot;https://github.com/rails/rails/blob/58c3c62cad0b79ec1f977d827ccd3849cc7f99c1/activerecord/lib/active_record/migration.rb#L1131&quot;&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;IllegalMigrationNameError&lt;/code&gt; will be raised&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;the migration name - alphanumeric characters plus underscores. This is the
part you normally parse out yourself when scanning the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;db/migrations/&lt;/code&gt;
directory - something like &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;add_full_name_to_users&lt;/code&gt;.&lt;/li&gt;
  &lt;li&gt;the migration scope - after a full-stop character, but before “.rb”. The scope
is used to group migrations. Often, this feature is used by Rails engines to
identify the origin of a migration. By default, migrations are unscoped.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;This is all useful to know, but for the purposes of continuing our journey, most
of the time, scope is blank, so we run pass &lt;em&gt;all&lt;/em&gt; migrations along to the
&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Migrator&lt;/code&gt; class in our &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;up&lt;/code&gt; method.&lt;/p&gt;

&lt;h5 id=&quot;activerecordmigrator&quot;&gt;ActiveRecord::Migrator&lt;/h5&gt;

&lt;p&gt;This class acts as the ‘controller’ for a set of migrations. The primary
responsibility it has is to prepare a list of migrations to run, and to then
actually run these migrations (with some advisory lock decoration when this is
is supported).&lt;/p&gt;

&lt;p&gt;When the class is initialized, it sets up the schema migration database table (a
reminder - &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;ActiveRecord::SchemaMigration&lt;/code&gt; is an internal database table that is
used to store the version numbers of migrations that have been run), and the
&lt;a href=&quot;https://github.com/rails/rails/blob/58c3c62cad0b79ec1f977d827ccd3849cc7f99c1/activerecord/lib/active_record/internal_metadata.rb#L43&quot;&gt;internal metadata key-value table&lt;/a&gt; that ActiveRecord uses to store metadata, like the
environment that migrations were last run in.&lt;/p&gt;

&lt;p&gt;This class supports running a single migration (provided by ‘target version’ -
e.g. that &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;ENV[&apos;VERSION&apos;]&lt;/code&gt; environment variable again). It uses the &lt;a href=&quot;https://github.com/rails/rails/blob/58c3c62cad0b79ec1f977d827ccd3849cc7f99c1/activerecord/lib/active_record/migration.rb#L1241&quot;&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;run&lt;/code&gt;
method
for this&lt;/a&gt;
In the case of doing a
normal &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;bundle exec rake db:migrate&lt;/code&gt; though, it performs a different action,
called &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;migrate&lt;/code&gt; (unsurprisingly). Migrate wraps an advisory lock around running
the migrations. An &lt;a href=&quot;https://en.wikipedia.org/wiki/Lock_(computer_science)&quot;&gt;advisory
lock&lt;/a&gt; is a feature of
some of some database engines supported by Rails, which is just that - advisory.
The migrations check for an advisory lock, and if one exists in the database, a
&lt;a href=&quot;https://github.com/rails/rails/blob/58c3c62cad0b79ec1f977d827ccd3849cc7f99c1/activerecord/lib/active_record/migration.rb#L1399&quot;&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;ConcurrentMigrationError&lt;/code&gt; is
raised&lt;/a&gt;,
halting the migrations. If a lock doesn’t already exist, a new advisory lock is
created, preventing any other migrations being run until the advisory lock is
released.&lt;/p&gt;

&lt;p&gt;With the advisory lock issued, or if advisory locks are not enabled,
&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;migrate_without_advisory_lock&lt;/code&gt; is called, getting a step closer to running
migrations.&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;migrate_without_lock&lt;/span&gt;
  &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;invalid_target?&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;raise&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;UnknownMigrationVersionError&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;vi&quot;&gt;@target_version&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;result&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;runnable&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;each&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;method&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;ss&quot;&gt;:execute_migration_in_transaction&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;))&lt;/span&gt;
  &lt;span class=&quot;n&quot;&gt;record_environment&lt;/span&gt;
  &lt;span class=&quot;n&quot;&gt;result&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;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;invalid_target?&lt;/code&gt; only runs if an actual &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;target_version&lt;/code&gt; is provided - it
ensures that the version that &lt;em&gt;was&lt;/em&gt; provided &lt;a href=&quot;https://github.com/rails/rails/blob/58c3c62cad0b79ec1f977d827ccd3849cc7f99c1/activerecord/lib/active_record/migration.rb#L1319&quot;&gt;is not zero, and that a migration
actually exists that matches that
version&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;The actual ‘migration’ is pretty simple - it calls
‘execute_migration_in_transaction’ with each migration. The &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;runnable&lt;/code&gt; definition
is worth looking into, since it’s actually the thing that decides &lt;em&gt;what&lt;/em&gt; will be
run:&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;runnable&lt;/span&gt;
  &lt;span class=&quot;n&quot;&gt;runnable&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;migrations&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;start&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;..&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;finish&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;up?&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;runnable&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;reject&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;m&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;|&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;ran?&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;m&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;c1&quot;&gt;# skip the last migration if we&apos;re headed down, but not ALL the way down&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;runnable&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;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;target&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;runnable&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;find_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;m&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;|&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;ran?&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;m&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;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;migrations&lt;/code&gt; method is also relevant:&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;migrations&lt;/span&gt;
  &lt;span class=&quot;n&quot;&gt;down?&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;?&lt;/span&gt; &lt;span class=&quot;vi&quot;&gt;@migrations&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;reverse&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;vi&quot;&gt;@migrations&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;sort_by&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;:version&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;So, if we are migration ‘up’ (which we are, since we’re running &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;db:migrate&lt;/code&gt; and
not &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;db:rollback&lt;/code&gt;), migrations are ordered by their version (e.g. in ascending
order). Any migrations that have already been run are skipped - migrations have
been run if they exist in the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;schema_migrations&lt;/code&gt; table. &lt;a href=&quot;https://github.com/rails/rails/blob/58c3c62cad0b79ec1f977d827ccd3849cc7f99c1/activerecord/lib/active_record/migration.rb#L1347&quot;&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;start&lt;/code&gt;&lt;/a&gt;
is determined by the current migration version (again, this is the ‘maximum’
value number fom the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;schema_migrations&lt;/code&gt; table), or zero if there are no version
numbers in the table (this is the case, for example, with a just-created
database). &lt;a href=&quot;https://github.com/rails/rails/blob/58c3c62cad0b79ec1f977d827ccd3849cc7f99c1/activerecord/lib/active_record/migration.rb#L1343&quot;&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;finish&lt;/code&gt;&lt;/a&gt; is determined by either the index of the migration file if a
target version has been provided (which is not the case, if &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;rake
db:migrate&lt;/code&gt; is being run without any environment variable arguments), or the
last index of the migrations list (e.g. size - 1).&lt;/p&gt;

&lt;p&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;execute_migration_in_transaction&lt;/code&gt; is the final step in the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Migrator&lt;/code&gt; before
the actual migration is ‘called’ (we’ll get to what this means in a second):&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;execute_migration_in_transaction&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;migration&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
  &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;down?&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;!&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;migrated&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;include?&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;migration&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;version&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;to_i&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
  &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;up?&lt;/span&gt;   &lt;span class=&quot;o&quot;&gt;&amp;amp;&amp;amp;&lt;/span&gt;  &lt;span class=&quot;n&quot;&gt;migrated&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;include?&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;migration&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;version&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;to_i&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;

  &lt;span class=&quot;no&quot;&gt;Base&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;logger&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;info&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;Migrating to &lt;/span&gt;&lt;span class=&quot;si&quot;&gt;#{&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;migration&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&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;migration&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;version&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;if&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;Base&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;logger&lt;/span&gt;

  &lt;span class=&quot;n&quot;&gt;ddl_transaction&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;migration&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;migration&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;migrate&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;vi&quot;&gt;@direction&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;record_version_state_after_migrating&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;migration&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;version&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;rescue&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;e&lt;/span&gt;
  &lt;span class=&quot;n&quot;&gt;msg&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;+&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;An error has occurred, &quot;&lt;/span&gt;
  &lt;span class=&quot;n&quot;&gt;msg&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;this and &quot;&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;use_transaction?&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;migration&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
  &lt;span class=&quot;n&quot;&gt;msg&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&amp;lt;&amp;lt;&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;all later migrations canceled:&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\n\n&lt;/span&gt;&lt;span class=&quot;si&quot;&gt;#{&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;e&lt;/span&gt;&lt;span class=&quot;si&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;&lt;/span&gt;
  &lt;span class=&quot;k&quot;&gt;raise&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;StandardError&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;msg&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;e&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;backtrace&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 first two lines guard against the migration already being run. Next, the
migrator opens a database transaction (&lt;a href=&quot;https://github.com/rails/rails/blob/58c3c62cad0b79ec1f977d827ccd3849cc7f99c1/activerecord/lib/active_record/migration.rb#L1378&quot;&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;ddl_transaction&lt;/code&gt;&lt;/a&gt; calls
&lt;a href=&quot;https://github.com/rails/rails/blob/58c3c62cad0b79ec1f977d827ccd3849cc7f99c1/activerecord/lib/active_record/migration.rb#L1386&quot;&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;use_transaction?&lt;/code&gt;&lt;/a&gt;, which wraps the block in a transaction unless the migration
itself has signalled that it should not be run in a transaction (which is
necessary for &lt;em&gt;some&lt;/em&gt; specific database operations, and can be specified in a
migration using the &lt;a href=&quot;https://api.rubyonrails.org/classes/ActiveRecord/Migration.html#method-c-disable_ddl_transaction-21&quot;&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;disable_ddl_transaction!&lt;/code&gt;&lt;/a&gt; method, or if transactions are
disabled or unavailable for the database connection).&lt;/p&gt;

&lt;p&gt;After the transaction is begun, the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;migrate&lt;/code&gt; method is called on the migration,
then
&lt;a href=&quot;https://github.com/rails/rails/blob/58c3c62cad0b79ec1f977d827ccd3849cc7f99c1/activerecord/lib/active_record/migration.rb#L1359&quot;&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;record_version_state_after_migrating&lt;/code&gt;&lt;/a&gt;
is called, which updates the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;schema_migrations&lt;/code&gt; table. If anything goes wrong
during the migration, a descriptive error is raised. You might recognise this error message from your own migrations: “An error has occurred, this and
all later migrations canceled”.&lt;/p&gt;

&lt;h5 id=&quot;your-migration-activerecordmigration&quot;&gt;Your migration! (ActiveRecord::Migration)&lt;/h5&gt;

&lt;p&gt;At this point, the migrator has figured out which migrations need to be run, and
is running them one by one in a transaction. It’s now up to your migration.
Let’s say the migration you’ve written performs the following action:&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;CreateBlogs&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;create_table&lt;/span&gt; &lt;span class=&quot;ss&quot;&gt;:blogs&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;t&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;|&lt;/span&gt;
      &lt;span class=&quot;n&quot;&gt;t&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;string&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;unique: &lt;/span&gt;&lt;span class=&quot;kp&quot;&gt;true&lt;/span&gt;
      &lt;span class=&quot;n&quot;&gt;t&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;timestamps&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;blockquote&gt;
  &lt;p&gt;That square-bracket syntax is a bit unusual. It’s the way that Rails has
chosen to be able to support older migrations written for previous versions of
Rails. The &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;ActiveRecord::Migration&lt;/code&gt; class &lt;a href=&quot;https://github.com/rails/rails/blob/58c3c62cad0b79ec1f977d827ccd3849cc7f99c1/activerecord/lib/active_record/migration.rb#L566&quot;&gt;implements the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;self.[]&lt;/code&gt;
method&lt;/a&gt;,
which looks up the correct concrete migration class to use from the
&lt;a href=&quot;https://github.com/rails/rails/blob/58c3c62cad0b79ec1f977d827ccd3849cc7f99c1/activerecord/lib/active_record/migration/compatibility.rb&quot;&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;ActiveRecord::Migration::Compatibility&lt;/code&gt;
class&lt;/a&gt;.
This class contains a number of subclasses named after Rails major/minor
versions, where each subclass overrides the current (e.g. Rails 6.1) migration
methods to apply the previous versions’ default options. In this way,
migrations back to Rails 4.2 can be supported, even though ActiveRecord 6.1 is
installed. Neat!&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;We saw that the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;ActiveRecord::Migration::Migrator&lt;/code&gt; calls the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;migrate&lt;/code&gt; method
on the migration class. What does this look like?&lt;/p&gt;

&lt;p&gt;Turns out it’s pretty simple:&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;migrate&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;direction&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
  &lt;span class=&quot;n&quot;&gt;new&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;migrate&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;direction&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;—
&lt;a href=&quot;https://github.com/rails/rails/blob/58c3c62cad0b79ec1f977d827ccd3849cc7f99c1/activerecord/lib/active_record/migration.rb#L662&quot;&gt;rails/activerecord/lib/active_record/migration.rb&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;OK, so this creates a new instance of the migration class, and then calls
&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;migrate&lt;/code&gt; on &lt;em&gt;that&lt;/em&gt;. Here’s the instance 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;c1&quot;&gt;# Execute this migration in the named direction&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;migrate&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;direction&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
  &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;unless&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;respond_to?&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;direction&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;

  &lt;span class=&quot;k&quot;&gt;case&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;direction&lt;/span&gt;
  &lt;span class=&quot;k&quot;&gt;when&lt;/span&gt; &lt;span class=&quot;ss&quot;&gt;:up&lt;/span&gt;   &lt;span class=&quot;k&quot;&gt;then&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;announce&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;migrating&quot;&lt;/span&gt;
  &lt;span class=&quot;k&quot;&gt;when&lt;/span&gt; &lt;span class=&quot;ss&quot;&gt;:down&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;then&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;announce&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;reverting&quot;&lt;/span&gt;
  &lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;

  &lt;span class=&quot;n&quot;&gt;time&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;kp&quot;&gt;nil&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;Base&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;connection_pool&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;with_connection&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;conn&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;|&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;time&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&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;measure&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;do&lt;/span&gt;
      &lt;span class=&quot;n&quot;&gt;exec_migration&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;conn&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;direction&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;case&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;direction&lt;/span&gt;
  &lt;span class=&quot;k&quot;&gt;when&lt;/span&gt; &lt;span class=&quot;ss&quot;&gt;:up&lt;/span&gt;   &lt;span class=&quot;k&quot;&gt;then&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;announce&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;migrated (%.4fs)&quot;&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;%&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;time&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;real&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;write&lt;/span&gt;
  &lt;span class=&quot;k&quot;&gt;when&lt;/span&gt; &lt;span class=&quot;ss&quot;&gt;:down&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;then&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;announce&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;reverted (%.4fs)&quot;&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;%&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;time&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;real&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;write&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;So, if the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;direction&lt;/code&gt; is &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;:up&lt;/code&gt; (which it is for our purposes, since we’re
running &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;bundle exec rake db:migrate&lt;/code&gt;), it
&lt;a href=&quot;https://github.com/rails/rails/blob/58c3c62cad0b79ec1f977d827ccd3849cc7f99c1/activerecord/lib/active_record/migration.rb#L880&quot;&gt;announces&lt;/a&gt;
that the migration is beginning, then calls &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;exec_migration&lt;/code&gt; (the next step!)
using a connection checked out from the connection pool and wrapped in
&lt;a href=&quot;https://ruby-doc.org/stdlib-2.7.1/libdoc/benchmark/rdoc/Benchmark.html#method-c-measure&quot;&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Benchmark.measure&lt;/code&gt;&lt;/a&gt;
to measure the amount of time taken to run the block (i.e. the actual migration
operations). Once that’s done, the connection pool block completes, returning
the connection to the pool, then the total time taken to run the migration is
announced. With these announce methods, the migration will run, and the
migration output you are probably used to will be shown:&lt;/p&gt;

&lt;div class=&quot;language-sh highlighter-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;==&lt;/span&gt; 20210404082749 CreateBlogs: migrating &lt;span class=&quot;o&quot;&gt;======================================&lt;/span&gt;
&lt;span class=&quot;nt&quot;&gt;--&lt;/span&gt; create_table&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;:blogs&lt;span class=&quot;o&quot;&gt;)&lt;/span&gt;
   -&amp;gt; 0.0091s
&lt;span class=&quot;o&quot;&gt;==&lt;/span&gt; 20210404082749 CreateBlogs: migrated &lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;0.0092s&lt;span class=&quot;o&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=============================&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;So, &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;migrate&lt;/code&gt; instruments the actual migration operation, and provides some
informative messaging output. What about the actual migration? For that, we need
to jump into one more method - &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;exec_migration&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;def&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;exec_migration&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;conn&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;direction&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
  &lt;span class=&quot;vi&quot;&gt;@connection&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;conn&lt;/span&gt;
  &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;respond_to?&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;ss&quot;&gt;:change&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;direction&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;==&lt;/span&gt; &lt;span class=&quot;ss&quot;&gt;:down&lt;/span&gt;
      &lt;span class=&quot;n&quot;&gt;revert&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;change&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;else&lt;/span&gt;
      &lt;span class=&quot;n&quot;&gt;change&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;
  &lt;span class=&quot;k&quot;&gt;else&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;public_send&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;direction&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;ensure&lt;/span&gt;
  &lt;span class=&quot;vi&quot;&gt;@connection&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;kp&quot;&gt;nil&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;Some of these methods are maybe starting to look familiar, and may even match
what is in your migration! If the migration has a &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;change&lt;/code&gt; method, and the
direction is not &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;:down&lt;/code&gt;, then the change method of our migration is called,
using the passed-in connection from the connection pool. If the migration does
not implement &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;change&lt;/code&gt; (it &lt;em&gt;is&lt;/em&gt; optional after all, but if you don’t implement
&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;change&lt;/code&gt;, then you should ideally implement both &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;up&lt;/code&gt; and &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;down&lt;/code&gt; to make sure
your migration can run in both directions), then it calls either the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;up&lt;/code&gt; or
&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;down&lt;/code&gt; method, depending on the direction - this will be the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;up&lt;/code&gt; method in our
case, since we’re migrating and not rolling back.&lt;/p&gt;

&lt;p&gt;Now, whatever is in our own migration’s &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;change&lt;/code&gt; method will be run, altering
the database!&lt;/p&gt;

&lt;blockquote&gt;
  &lt;p&gt;Rolling back a change method wraps the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;change&lt;/code&gt; method in a block passed to
&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;revert&lt;/code&gt;. This method is called on the database connection, which reverses any
operations called on the connection within the block - &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;create_table&lt;/code&gt; becomes
&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;drop_table&lt;/code&gt;, &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;add_column&lt;/code&gt; becomes &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;remove_column&lt;/code&gt;, etc.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h5 id=&quot;updating-dbschemarb&quot;&gt;Updating db/schema.rb&lt;/h5&gt;

&lt;p&gt;If you’ve run a migration before, you’ll probably have noticed that running a
migration (or rolling back a migration) tends to also update &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;db/schema.rb&lt;/code&gt;.
Where does this change come from? To answer this question, we need to go all the
way back to the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;db:migrate&lt;/code&gt; Rake task definition, and take a closer look at the
last line:&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;desc&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;Migrate the database (options: VERSION=x, VERBOSE=false, SCOPE=blog).&quot;&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;task&lt;/span&gt; &lt;span class=&quot;ss&quot;&gt;migrate: :load_config&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;do&lt;/span&gt;
  &lt;span class=&quot;n&quot;&gt;original_db_config&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&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;Base&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;connection_db_config&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;Base&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;configurations&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;configs_for&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;ss&quot;&gt;env_name: &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;Tasks&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;no&quot;&gt;DatabaseTasks&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;env&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;).&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;each&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;do&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;|&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;db_config&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;|&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;Base&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;establish_connection&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;db_config&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&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;Tasks&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;no&quot;&gt;DatabaseTasks&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;migrate&lt;/span&gt;
  &lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;
  &lt;span class=&quot;n&quot;&gt;db_namespace&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;_dump&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;].&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;invoke&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;ensure&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;Base&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;establish_connection&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;original_db_config&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;So, migrations are run, and then another Rake task is called -
&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;db_namespace[&quot;_dump&quot;]&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;task&lt;/span&gt; &lt;span class=&quot;ss&quot;&gt;:_dump&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;do&lt;/span&gt;
  &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;ActiveRecord&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;no&quot;&gt;Base&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;dump_schema_after_migration&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;db_namespace&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;schema:dump&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;].&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;invoke&lt;/span&gt;
  &lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;
  &lt;span class=&quot;c1&quot;&gt;# Allow this task to be called as many times as required. An example is the&lt;/span&gt;
  &lt;span class=&quot;c1&quot;&gt;# migrate:redo task, which calls other two internally that depend on this one.&lt;/span&gt;
  &lt;span class=&quot;n&quot;&gt;db_namespace&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;_dump&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;].&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;reenable&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 is just a proxy for &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;rake db:schema:dump&lt;/code&gt; then - if
&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;dump_schema_after_migration&lt;/code&gt; is truthy, it calls the actual task, then
re-enables itself to allow it to be called again (by default, Rake tasks can
only be invoked once, hence why it must be re-enabled).&lt;/p&gt;

&lt;p&gt;The &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;rake db:schema:dump&lt;/code&gt; task looks very similar to the migration task:&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;task&lt;/span&gt; &lt;span class=&quot;ss&quot;&gt;dump: :load_config&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;do&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;Base&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;configurations&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;configs_for&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;ss&quot;&gt;env_name: &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;Tasks&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;no&quot;&gt;DatabaseTasks&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;env&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;).&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;each&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;do&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;|&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;db_config&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;|&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;Base&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;establish_connection&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;db_config&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&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;Tasks&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;no&quot;&gt;DatabaseTasks&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;dump_schema&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;db_config&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;db_namespace&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;schema:dump&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;].&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;reenable&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;It looks up which connection configurations should be used for database tasks,
establishes a connection, and calls &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;dump_schema&lt;/code&gt; on &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;DatabaseTasks&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Going all the way down the rabbithole of how the schema gets dumped is probably
another step-through in it’s own right. Let’s stop here by noting that
&lt;a href=&quot;https://github.com/rails/rails/blob/58c3c62cad0b79ec1f977d827ccd3849cc7f99c1/activerecord/lib/active_record/tasks/database_tasks.rb#L378&quot;&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;ActiveRecord::Tasks::DatabaseTasks.dump_schema&lt;/code&gt;&lt;/a&gt;
uses
&lt;a href=&quot;https://github.com/rails/rails/blob/58c3c62cad0b79ec1f977d827ccd3849cc7f99c1/activerecord/lib/active_record/schema_dumper.rb&quot;&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;ActiveRecord::SchemaDumper&lt;/code&gt;&lt;/a&gt;
to produce a formatted Ruby file containing the necessary ActiveRecord methods
to reconstruct the database.&lt;/p&gt;

&lt;blockquote&gt;
  &lt;p&gt;This is the case if the schema format is set to :ruby, which is the default.
The schema can also be stored as SQL, which allows the exact database schema
to be dumped to a file named db/structure.sql, at the expense of database engine compatibility (e.g. a dumped
PostgreSQL schema will only be restorable to databases using the PostgreSQL
database engine, not MySQL,
SQLite or others). When preparing structure.sql, a database engine-specific
tool is used to dump the schema, rather than anything in  ActiveRecord. For
PostgreSQL, &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;psql --no-owner --schema-only&lt;/code&gt; &lt;a href=&quot;https://github.com/rails/rails/blob/58c3c62cad0b79ec1f977d827ccd3849cc7f99c1/activerecord/lib/active_record/tasks/postgresql_database_tasks.rb#L49&quot;&gt;is
used&lt;/a&gt;.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;hr /&gt;

&lt;p&gt;Once our migration is run, and schema.rb is updated with the database schema
changes - that’s it! Our database is up to date, and ready for us to use the new
or modified data structure our migration has handled.&lt;/p&gt;

</description>
        <pubDate>Sat, 03 Apr 2021 00:00:00 +1300</pubDate>
        <link>https://joshmcarthur.com/step-through/2021/04/03/how-is-a-migration-run-in-ruby-on-rails.html</link>
        <guid isPermaLink="true">https://joshmcarthur.com/step-through/2021/04/03/how-is-a-migration-run-in-ruby-on-rails.html</guid>
        
        <category>technology</category>
        
        <category>rails</category>
        
        <category>activerecord</category>
        
        <category>step-through</category>
        
        
        <category>step-through</category>
        
      </item>
    
      <item>
        <title>Preview Devise mailers</title>
        <description>&lt;p&gt;I use ActionMailer::Previews whenever I am implementing a transactional email.
They’re a super handy way of working with email development, and I’ve even
enabled them in UAT environments before so clients and designers can check the
email easily.&lt;/p&gt;

&lt;p&gt;In many apps, nearly all the transactional email is actually sent by Devise, so
I wanted to set up a ActionMailer preview for the emails Devise sent. Someone on
StackOverflow have already &lt;a href=&quot;https://stackoverflow.com/a/25860079&quot;&gt;put together&lt;/a&gt;
the answer, but I made some small changes based on the specific application:&lt;/p&gt;

&lt;script src=&quot;https://gist.github.com/7fa1501eda3f454f4c095ea35506fc69.js&quot;&gt; &lt;/script&gt;

&lt;p&gt;There are a couple of notes I have:&lt;/p&gt;

&lt;ol&gt;
  &lt;li&gt;I had to comment out the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;confirmation_instructions&lt;/code&gt;, since I don’t have the
&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;:confirmable&lt;/code&gt; strategy active on this app. It fails with a non-specific
error message about being unable to find the route (e.g. it gets all the way
to rendering the email template before it fails).&lt;/li&gt;
  &lt;li&gt;There’s probably a way of introspecting the Devise strategies to figure out
dynamically which preview methods to generate, if you wanted to make this
reusable across different apps. You can see all the mailer methods available
for Devise in IRB with &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Devise::Mailer.methods - ApplicationMailer.methods&lt;/code&gt;,
or in pry with &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;show-source Devise::Mailer&lt;/code&gt;.&lt;/li&gt;
  &lt;li&gt;You can have as many preview methods per mailer method as you want - e.g. if
you had different content or links that were generated, you can have
different preview methods that build different data (e.g. if an admin user
had a different subject or email content, you could have one preview for the
admin user email, and one for the normal user email).&lt;/li&gt;
  &lt;li&gt;This class looks up the first user and uses that. I usually use
&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;FactoryBot.build_stubbed&lt;/code&gt; here, because I want a record that looks like it
is persisted, but I don’t actually need it in the database to render the
email.&lt;/li&gt;
  &lt;li&gt;You could use &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Devise.friendly_token&lt;/code&gt; instead of “faketoken” if you wanted to
have something that looked a bit more realistic.&lt;/li&gt;
&lt;/ol&gt;
</description>
        <pubDate>Wed, 10 Mar 2021 00:00:00 +1300</pubDate>
        <link>https://joshmcarthur.com/til/2021/03/10/preview-devise-mailers.html</link>
        <guid isPermaLink="true">https://joshmcarthur.com/til/2021/03/10/preview-devise-mailers.html</guid>
        
        <category>til</category>
        
        <category>rails</category>
        
        <category>devise</category>
        
        
        <category>TIL</category>
        
      </item>
    
      <item>
        <title>Docker: You must use Bundler 2 or greater with this lockfile</title>
        <description>&lt;p&gt;Ever seen the error “You must use Bundler 2 or greater with this lockfile.”
trying to build a Docker image from a Dockerfile that uses an older Ruby
version? Even though you are &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;gem install&lt;/code&gt;ing the correct version of Bundler?&lt;/p&gt;

&lt;p&gt;It turns out that the  Ruby Docker images set an environment variable called
&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;BUNDLER_VERSION&lt;/code&gt;, which Bundler will always try to use to install dependencies
when you run &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;bundle install&lt;/code&gt;, even if a newer version has been installed. Even
if you uninstall the old version, it will still not use the newer version. The
solution is to prefix your &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;RUN bundle install&lt;/code&gt; instruction with &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;unset
BUNDLER_VERSION&lt;/code&gt;. The full command that I am using to install the version of
Bundler my Gemfile.lock was bundled with and then install my dependencies is:&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;RUN&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;unset&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;BUNDLER_VERSION&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&amp;amp;&amp;amp;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;\&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;gem&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;install&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;bundler&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;-&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;v&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;$(grep -A 1 &quot;&lt;/span&gt;&lt;span class=&quot;no&quot;&gt;BUNDLED&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;WITH&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot; Gemfile.lock | tail -n
    1)&quot;&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&amp;amp;&amp;amp;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;\&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;echo&lt;/span&gt; &lt;span class=&quot;err&quot;&gt;$&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;bundle&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;--&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;version&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&amp;amp;&amp;amp;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;\&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;bundle&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;install&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Note: I know that this affects the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;ruby:2.5&lt;/code&gt; image (old, I know). While the
problem might not cause an error in newer versions, the &lt;em&gt;behaviour&lt;/em&gt; is the same&lt;/p&gt;
&lt;ul&gt;
  &lt;li&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;BUNDLER_VERSION&lt;/code&gt; is set, and the version specified by this environment
variable will be used in preference to any other installed version. For this
reason, it’s worth checking this, even on newer Ruby images, since dependencies
may be installed with a different version of Bundler than you were expecting.&lt;/li&gt;
&lt;/ul&gt;

&lt;hr /&gt;

&lt;p&gt;&lt;strong&gt;Follow-up&lt;/strong&gt;: this works when building the image, but not when using bundle
after that (it reverts to trying to use the old version). I tried a range of
things to get this environment variable to persist, but &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;export BUNDLER_VERSION&lt;/code&gt;
doesn’t persist in the container, and putting the environment variable export in
&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;~/.bashrc&lt;/code&gt; or &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;/etc/profile&lt;/code&gt; only gets run when a shell is run - not when a
standalone command (like &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;bundle exec rails s --binding=0.0.0.0&lt;/code&gt;) is run. I
ended up not being able to find a solution for this in the Dockerfile. Instead,
I added a &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;docker-entrypoint.sh&lt;/code&gt; script in the root of the project with the
following contents:&lt;/p&gt;

&lt;div class=&quot;language-sh highlighter-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;-Eeuo&lt;/span&gt; pipefail
&lt;span class=&quot;c&quot;&gt;# Use the version of Bundler specified in Gemfile.lock, not the one packaged&lt;/span&gt;
&lt;span class=&quot;c&quot;&gt;# with the original Docker image&lt;/span&gt;
&lt;span class=&quot;nb&quot;&gt;export &lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;BUNDLER_VERSION&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;si&quot;&gt;$(&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;grep&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;-A&lt;/span&gt; 1 &lt;span class=&quot;s2&quot;&gt;&quot;BUNDLED WITH&quot;&lt;/span&gt; Gemfile.lock | &lt;span class=&quot;nb&quot;&gt;tail&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;-n&lt;/span&gt; 1&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;exec&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;$@&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;And declaring this entrypoint in my Dockerfile: &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;ENTRYPOINT
[&quot;docker-entrypoint.sh&quot;]&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;This means that when my command is run, it will be passed to the entrypoint -
e.g. &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;docker-entrypoint.sh bundle exec rails s --binding=0.0.0.0&lt;/code&gt;, and will have
the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;BUNDLER_VERSION&lt;/code&gt; environment variable &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;export&lt;/code&gt;d from within that script.&lt;/p&gt;
</description>
        <pubDate>Wed, 27 Jan 2021 00:00:00 +1300</pubDate>
        <link>https://joshmcarthur.com/til/2021/01/27/docker-you-must-use-bundler-2-or-greater-with-this-lockfile.html</link>
        <guid isPermaLink="true">https://joshmcarthur.com/til/2021/01/27/docker-you-must-use-bundler-2-or-greater-with-this-lockfile.html</guid>
        
        <category>til</category>
        
        <category>technology</category>
        
        <category>ruby</category>
        
        <category>rails</category>
        
        <category>docker</category>
        
        
        <category>TIL</category>
        
      </item>
    
      <item>
        <title>Strip newlines from terminal output</title>
        <description>&lt;p&gt;Every so often I will need to take some kind of key file - like a PGP key, SSH, OpenSSL, that kind
of thing, and be able to paste it into a one-line text entry somewhere. Maybe a .env file, or shell
script, or some piece of infrastructure that just accepts a text field as the input (like CI
configuration).&lt;/p&gt;

&lt;p&gt;I’ve always had to google for how to remove newlines from such a file, and I’ve seen a range of
tricks using &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;sed&lt;/code&gt;, &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;awk&lt;/code&gt;, all of that sort of thing.&lt;/p&gt;

&lt;p&gt;I’ve just been able to find an easier way, which I’m documenting here, because I want to remember it
for next time.&lt;/p&gt;

&lt;p&gt;To remove newlines from a key-type file:&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;cat ~/.ssh/id_rsa.pub | tr -d &apos;\n&apos;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;cat&lt;/code&gt; - stream the file into the pipe&lt;/li&gt;
  &lt;li&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;tr&lt;/code&gt; - “ tr - translate or delete characters”&lt;/li&gt;
  &lt;li&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;-d&lt;/code&gt; - delete the character, don’t try and replace it&lt;/li&gt;
  &lt;li&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;&apos;\n&apos;&lt;/code&gt; - the character to delete (must be in single quotes to avoid your shell trying to interpret
it).&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Since I have &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;pbcopy&lt;/code&gt; available on my laptop, I can easily copy this to my clipboard:&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;cat ~/.ssh/id_rsa.pub | tr -d &apos;\n&apos; | pbcopy
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;(I’m on Linux, but have an &lt;a href=&quot;https://github.com/joshmcarthur/dotfiles/blob/88af7b25550380916d8f7186f1f18160c98ef69d/zsh/plugins/macbuntu/init.zsh#L1&quot;&gt;alias for
pbcopy&lt;/a&gt;
so it behaves the same way as it would on Mac OS).&lt;/p&gt;

&lt;p&gt;Or base 64 encode it first:&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;cat ~/.ssh/id_rsa | base64 | tr -d &apos;\n&apos; | pbcopy
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

</description>
        <pubDate>Sun, 24 May 2020 00:00:00 +1200</pubDate>
        <link>https://joshmcarthur.com/til/2020/05/24/strip-newlines-from-terminal-output.html</link>
        <guid isPermaLink="true">https://joshmcarthur.com/til/2020/05/24/strip-newlines-from-terminal-output.html</guid>
        
        <category>til</category>
        
        <category>technology</category>
        
        <category>shell</category>
        
        
        <category>TIL</category>
        
      </item>
    
      <item>
        <title>Inspect zip file contents</title>
        <description>&lt;p&gt;If you’ve got a large zipfile, or just a badly named one and you’d like to quickly take a peek at what’s inside it, 
there’s a handy flag that can be passed to the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;unzip&lt;/code&gt; utility (&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;apt install unzip&lt;/code&gt;):&lt;/p&gt;

&lt;p&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;unzip -vl path_to_zip.zip&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;This yields an output like:&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;root@224cb547fae0:/usr/src/app# unzip -vl spec/fixtures/example.zip
Archive:  spec/fixtures/example.zip
 Length   Method    Size  Cmpr    Date    Time   CRC-32   Name
--------  ------  ------- ---- ---------- ----- --------  ----
      12  Defl:N       14 -17% 2019-01-08 03:33 d0985364  Example File.txt
       0  Stored        0   0% 2019-01-08 03:33 00000000  __MACOSX/
     570  Defl:N      417  27% 2019-01-08 03:33 c696190f  __MACOSX/._Example File.txt
--------          -------  ---                            -------
     582              431  26%                            3 files
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;This shows the file size, the compression algorithm, and the mtime, all without bothering with a UI or extracting to a tempdir. Handy!&lt;/p&gt;
</description>
        <pubDate>Wed, 13 May 2020 00:00:00 +1200</pubDate>
        <link>https://joshmcarthur.com/til/2020/05/13/inspect-zip-file-contents.html</link>
        <guid isPermaLink="true">https://joshmcarthur.com/til/2020/05/13/inspect-zip-file-contents.html</guid>
        
        <category>til</category>
        
        <category>technology</category>
        
        
        <category>TIL</category>
        
      </item>
    
      <item>
        <title>See recipients of a GPG encrypted message</title>
        <description>&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;gpg --list-only $infile
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;e.g.&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;gpg --list-only test.txt.gpg
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;This command outputs recipient info, including recipient key ID and details if known:&lt;/p&gt;

&lt;p&gt;With an empty keyring (no public keys imported):&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;gpg: encrypted with RSA key, ID 70F28839
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;With a populated keyring (public key of recipient imported):&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;gpg: encrypted with 4096-bit RSA key, ID 70F28839, created 2018-05-29
      &quot;Josh McArthur &amp;lt;jo....@gmail.com&amp;gt;&quot;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
</description>
        <pubDate>Mon, 27 Apr 2020 00:00:00 +1200</pubDate>
        <link>https://joshmcarthur.com/2020/04/27/see-recipients-of-a-gpg-encrypted-message.html</link>
        <guid isPermaLink="true">https://joshmcarthur.com/2020/04/27/see-recipients-of-a-gpg-encrypted-message.html</guid>
        
        <category>til</category>
        
        <category>technology</category>
        
        
      </item>
    
      <item>
        <title>Cool utilities I&apos;ve discovered this weekend</title>
        <description>&lt;h3 id=&quot;bat&quot;&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;bat&lt;/code&gt;&lt;/h3&gt;

&lt;p&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;bat&lt;/code&gt; is &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;cat&lt;/code&gt; with syntax highlighting, but is still compatible with pipes, etc. Really neat. I like the filename header.&lt;/p&gt;

&lt;p&gt;Github: &lt;a href=&quot;https://github.com/sharkdp/bat&quot;&gt;https://github.com/sharkdp/bat&lt;/a&gt;&lt;/p&gt;

&lt;h3 id=&quot;jira&quot;&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;jira&lt;/code&gt;&lt;/h3&gt;

&lt;p&gt;A CLI for Jira. Maybe it’ll be faster than using the web interace???&lt;/p&gt;

&lt;p&gt;Regardless, it supports a fancy config loading system that looks like I can configure it as part of my &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;tmux&lt;/code&gt; setup
to pop me into the correct project for the directory I’m in.&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;https://github.com/go-jira/jira&quot;&gt;https://github.com/go-jira/jira&lt;/a&gt;&lt;/p&gt;

&lt;h3 id=&quot;entr&quot;&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;entr&lt;/code&gt;&lt;/h3&gt;

&lt;p&gt;A really nice tool in the UNIX philosphy of building simple, composible command line utilities.
&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;entr&lt;/code&gt; can be piped to to run a command when the stream on the left hand side changes. This is
similar functionality to &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;guard&lt;/code&gt;, &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;livereload&lt;/code&gt;, etc, but is useful precisely because it is generic.&lt;/p&gt;

&lt;p&gt;It also has a handy option for commands that don’t support signals or a modified stream &amp;amp;emdash; &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;-r&lt;/code&gt; will
cause &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;entr&lt;/code&gt; to terminate the process and start it again, rather than trying to ‘hot reload’.&lt;/p&gt;

&lt;p&gt;The website has heaps of examples with documentation on the additional options: &lt;a href=&quot;http://eradman.com/entrproject/&quot;&gt;http://eradman.com/entrproject/&lt;/a&gt;&lt;/p&gt;

&lt;h3 id=&quot;tldr-pages&quot;&gt;TLDR pages&lt;/h3&gt;

&lt;p&gt;TLDR pages aim to extend on &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;man&lt;/code&gt; pages with practical examples of common tasks. The website, https://tldr.sh/ is
usable as-is, but there are a bunch of command-line tools available. I’m trying to Node.js version, but there are 
various options depending on the runtimes you have installed.&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;https://tldr.sh/&quot;&gt;https://tldr.sh/&lt;/a&gt;&lt;/p&gt;
</description>
        <pubDate>Mon, 13 Apr 2020 00:00:00 +1200</pubDate>
        <link>https://joshmcarthur.com/2020/04/13/cool-utilities-ive-discovered-this-weekend.html</link>
        <guid isPermaLink="true">https://joshmcarthur.com/2020/04/13/cool-utilities-ive-discovered-this-weekend.html</guid>
        
        
      </item>
    
      <item>
        <title>UNIX find case insensitive</title>
        <description>&lt;p&gt;I use the &lt;a href=&quot;http://man7.org/linux/man-pages/man1/find.1.html&quot;&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;find&lt;/code&gt;&lt;/a&gt; all the time to
(unsurprisingly), &lt;em&gt;find&lt;/em&gt; files or directories I’m looking for. I prefer it to any UI built into my
OS (currently Ubuntu, but all OSes seem to have this kind of thing - Alfred/Spotlight/Start Menu
etc), because I usually know roughly where I’m looking, and I can be declarative about what I’m
looking for (e.g. &lt;em&gt;file&lt;/em&gt; that starts with &lt;em&gt;x&lt;/em&gt; and has the extension &lt;em&gt;.blah&lt;/em&gt;).&lt;/p&gt;

&lt;p&gt;Today I learned that the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;find&lt;/code&gt; option I use all the time, &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;-name&lt;/code&gt;, has a case-insensitive version.
I can’t believe I didn’t try and guess this earlier, but that option would be &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;-iname&lt;/code&gt;. This option
has identical behaviour to the standard, but doesn’t match on case. This means that wildcards like
&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;*&lt;/code&gt; can still be used, but the terms included in the search string can be in any case - upper,
lower, or mixed.&lt;/p&gt;

&lt;p&gt;I came across this because I was attempting to export my highlights from my Kindle. After some
research, I discovered that highlights are stored on a Kindle in &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;documents/my-clippings.txt&lt;/code&gt;.
Great! Except..I have many, many files in that folder. I first used &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;find . -type f -name
&apos;*clippings*&apos;&lt;/code&gt; to find it…that didnt’ match anything. I was then able to use &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;-iname &apos;*clipping*&apos;&lt;/code&gt; where I &lt;em&gt;did&lt;/em&gt; find it - named “My Clippings.txt”.&lt;/p&gt;

&lt;p&gt;Because I used the wildcard in my term, I also found an additional file I can now looking into - “My
Clippings.sdr”.&lt;/p&gt;

</description>
        <pubDate>Tue, 29 Oct 2019 00:00:00 +1300</pubDate>
        <link>https://joshmcarthur.com/til/2019/10/29/unix-find-case-insensitive.html</link>
        <guid isPermaLink="true">https://joshmcarthur.com/til/2019/10/29/unix-find-case-insensitive.html</guid>
        
        <category>til</category>
        
        <category>shell</category>
        
        
        <category>TIL</category>
        
      </item>
    
      <item>
        <title>My favourite HTTP client</title>
        <description>&lt;p&gt;It’s 2019. I’m writing code that needs to communicate with other APIs &lt;em&gt;all the time&lt;/em&gt;. The code I post below reflects a snapshot of my current template API client. 
If I need to commuicate with a third-party in Ruby, I’m using this class.&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;ostruct&quot;&lt;/span&gt;

&lt;span class=&quot;k&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;MyApiClient&lt;/span&gt;
  &lt;span class=&quot;k&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;Error&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&amp;lt;&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;StandardError&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;

  &lt;span class=&quot;k&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;initialize&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;configuration&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;kp&quot;&gt;nil&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;stubs&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;kp&quot;&gt;nil&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;vi&quot;&gt;@configuration&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;configuration&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;application&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;config_for&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;ss&quot;&gt;:my_api&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;vi&quot;&gt;@connection&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;Faraday&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;url: &lt;/span&gt;&lt;span class=&quot;vi&quot;&gt;@configuration&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;ss&quot;&gt;:host&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;],&lt;/span&gt; &lt;span class=&quot;ss&quot;&gt;headers: &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;headers&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;conn&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;|&lt;/span&gt;
      &lt;span class=&quot;n&quot;&gt;conn&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;request&lt;/span&gt;  &lt;span class=&quot;ss&quot;&gt;:json&lt;/span&gt;
      &lt;span class=&quot;n&quot;&gt;conn&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;response&lt;/span&gt; &lt;span class=&quot;ss&quot;&gt;:json&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;ss&quot;&gt;parser_options: &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;ss&quot;&gt;object_class: &lt;/span&gt;&lt;span class=&quot;no&quot;&gt;OpenStruct&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
      &lt;span class=&quot;n&quot;&gt;conn&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;response&lt;/span&gt; &lt;span class=&quot;ss&quot;&gt;:raise_error&lt;/span&gt;
      &lt;span class=&quot;n&quot;&gt;conn&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;response&lt;/span&gt; &lt;span class=&quot;ss&quot;&gt;:logger&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;logger&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;ss&quot;&gt;bodies: &lt;/span&gt;&lt;span class=&quot;kp&quot;&gt;true&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;vi&quot;&gt;@configuration&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;ss&quot;&gt;:debug&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt;

      &lt;span class=&quot;n&quot;&gt;stubs&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;?&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;conn&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;adapter&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;ss&quot;&gt;:test&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;stubs&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;conn&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;adapter&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;no&quot;&gt;Faraday&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;default_adapter&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;sx&quot;&gt;%i[get post patch put delete]&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;each&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;do&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;|&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;verb&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;|&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;define_method&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;ss&quot;&gt;:&quot;raw_&lt;/span&gt;&lt;span class=&quot;si&quot;&gt;#{&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;verb&lt;/span&gt;&lt;span class=&quot;si&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;ss&quot;&gt;&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;args&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;|&lt;/span&gt;
      &lt;span class=&quot;vi&quot;&gt;@connection&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;public_send&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;verb&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;rescue&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;Faraday&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;no&quot;&gt;Error&lt;/span&gt;
      &lt;span class=&quot;k&quot;&gt;raise&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;Error&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;

    &lt;span class=&quot;n&quot;&gt;define_method&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;verb&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;args&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;|&lt;/span&gt;
      &lt;span class=&quot;n&quot;&gt;public_send&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;raw_&lt;/span&gt;&lt;span class=&quot;si&quot;&gt;#{&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;verb&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;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;nf&quot;&gt;body&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;headers&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;ss&quot;&gt;user_agent: &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;user_agent&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;c1&quot;&gt;# ... other default headers here&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;user_agent&lt;/span&gt;
    &lt;span class=&quot;vi&quot;&gt;@configuration&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;:user_agent&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;My Application &lt;/span&gt;&lt;span class=&quot;si&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;application&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;config&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;version&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;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;Let’s deconstruct this.&lt;/p&gt;

&lt;ol&gt;
  &lt;li&gt;We require “ostruct” to get the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;OpenStruct&lt;/code&gt; class.
&lt;a href=&quot;https://ruby-doc.org/stdlib-2.5.1/libdoc/ostruct/rdoc/OpenStruct.html&quot;&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;OpenStruct&lt;/code&gt;&lt;/a&gt;
is a handy little class in the Ruby standard library that accepts a hash, and
allows attributes to be accessed either via hash keys (&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;my_object[:attribute]&lt;/code&gt;),
or via method calls (&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;my_object.attribute&lt;/code&gt;).&lt;/li&gt;
  &lt;li&gt;Make the class &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;MyApiClient&lt;/code&gt;. Note we’re not subclassing anything here. This class
uses a bit of stuff from Rails, but it’s all optional.&lt;/li&gt;
  &lt;li&gt;Define our own error class, a subclass of
&lt;a href=&quot;https://ruby-doc.org/core-2.5.0/StandardError.html&quot;&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;StandardError&lt;/code&gt;&lt;/a&gt;. We’ll use
this class further down to wrap all exceptions coming from our HTTP library.
Wrapping exceptions is beneficial since it allows consumers of this API client to
not need to know too much about how the request is being made - just that
something went wrong. If something goes wrong here, a &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;MyApiClient::Error&lt;/code&gt; will be
raised. The consumer can handle this with an error message or retry and/or inspect
the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;cause&lt;/code&gt; attribute of the error to access the specific error from the library
if more context is required.&lt;/li&gt;
  &lt;li&gt;Start our &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;initialize&lt;/code&gt; method. This method accepts two arguments - &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;configuration&lt;/code&gt;
and &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;stubs&lt;/code&gt;. We’ll get into what these do in the next couple of points.&lt;/li&gt;
  &lt;li&gt;Set up &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;@configuration&lt;/code&gt;. This defaults to whatever is passed in - the API client
just expects something that has hash-style accessors - so a &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Hash&lt;/code&gt; would do the
job here, but also an &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;ActiveRecord&lt;/code&gt; model, an &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;OpenStruct&lt;/code&gt;, or anything else that
implements &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;[](key)&lt;/code&gt;. If nothing is passed in, we fall back to getting Rails to
fetch our configuration using &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;config_for&lt;/code&gt;. I have &lt;a href=&quot;https://www.joshmcarthur.com/til/2018/03/07/rails-config_for.html&quot;&gt;blogged before about
&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;config_for&lt;/code&gt;&lt;/a&gt;,
so won’t go into detail - basically Rails looks for a YAML file in &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;config/YOUR
KEY.yml&lt;/code&gt; - so &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;config/my_api.yml&lt;/code&gt; in this case, parses it to a Hash, and then
grabs whatever values are under the key named by &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Rails.environment&lt;/code&gt; - so
&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;development&lt;/code&gt;, &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;test&lt;/code&gt;, &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;production&lt;/code&gt; etc. It’ll also run this file through ERB when
it reads it, so you can reference environment variables or any other config
service using &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;&amp;lt;%= %&amp;gt;&lt;/code&gt; tags.&lt;/li&gt;
  &lt;li&gt;Set up the base HTTP connection. This is an HTTP API &lt;em&gt;client&lt;/em&gt;, not an HTTP API
&lt;em&gt;library&lt;/em&gt;, so we want to lean on others’ hard work here. You can actually use any
HTTP library you’d like here - as you’ll see further down, all that our class really needs of &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;@connection&lt;/code&gt; are methods representing the HTTP verbs - &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;get&lt;/code&gt;, &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;post&lt;/code&gt;, etc. Some libraries even just have a &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;request(verb, ...args)&lt;/code&gt; method you could use instead. It really doesn’t matter too much as long as your helper methods we’re about to define know what to expect back from whatever library you’re using. In this case, I’m using the &lt;a href=&quot;https://github.com/lostisland/faraday&quot;&gt;Faraday gem&lt;/a&gt;, with &lt;a href=&quot;https://github.com/lostisland/faraday_middleware&quot;&gt;middleware&lt;/a&gt;. The set up I have with Faraday is useful, but not super specific to this client class, so I’ve talked more about it in the &lt;a href=&quot;#faraday&quot;&gt;Faraday&lt;/a&gt; section below.&lt;/li&gt;
  &lt;li&gt;Next we define some methods - two methods for each HTTP verb. The first method is
named &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;raw_#{verb}&lt;/code&gt; - &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;raw_get&lt;/code&gt;, &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;raw_post&lt;/code&gt;, and so on. The purpose of this
method is to take arguments for a request that the HTTP library is expecting, and
make a request. It should return the raw response object that the library returns.
In this method, we rescue errors bubbling up from our HTTP library - in this case,
&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Faraday::Error&lt;/code&gt;, and re-raise our own error. Calling &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;raise&lt;/code&gt; inside a &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;rescue&lt;/code&gt;
block with a new exception class like this will automatically assign the original
error to the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;cause&lt;/code&gt; attribute of the re-raised error. The second method we define
is just named after the verb - &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;get&lt;/code&gt;, &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;post&lt;/code&gt;, etc. This method is intended as a
friendlier version of the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;raw_request&lt;/code&gt; method, and the idea behind this one is to
provide a shortcut to just getting the response body data - 99% of the time, this
is what you want, and so long as your HTTP library can raise errors when it runs
into bad HTTP status codes (e.g. 400..600), you don’t need to worry about checking
the response status - just handling any errors. For the HTTP library used here,
&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;faraday&lt;/code&gt;, we have the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;raise_error&lt;/code&gt; middleware making sure that errors are raised
when something goes wrong, and the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;json&lt;/code&gt; response parser that will turn our
response body into an object.&lt;/li&gt;
  &lt;li&gt;We’re nearly done - just a couple of configuration methods to go. &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;headers&lt;/code&gt; should
return the default headers to be applied to &lt;em&gt;all&lt;/em&gt; requests. In this case, we just
add a &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;User-Agent&lt;/code&gt; header. It’s courtesy when consuming an API to make sure that
your requests are identifiable, and the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;User-Agent&lt;/code&gt; header is perfect for this.
You can put any other headers you’d like here in, such as &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Authorization&lt;/code&gt;,
&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;X-Api_Key&lt;/code&gt;, etc.&lt;/li&gt;
  &lt;li&gt;The &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;user_agent&lt;/code&gt; method just builds a UA string for us to use - here, we’re using
the common name of our application, and the version of our application. This will
return something like “My Application abc123”, which allows both the name and
release of our application to be identified, and if necessary filtered or rate
limited. Without adding a user-agent, the HTTP library will usually use it’s own
name as the user agent string, which means that all of your requests will be
indistingushable from all the other “faraday”, “HTTParty”, “curl”, “wget”, etc.
requests that others are making.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;And that’s it! Testing is also pretty simple. I usually prefer integration testing
something like this by mocking an HTTP request/response - usually the HTTP library
will support something like this. You can also test your dynamic method definitions
by calling them and asserting that the same method with expected args is called on
the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;@connection&lt;/code&gt;, with the default headers mixed in.&lt;/p&gt;

&lt;h2 id=&quot;faraday&quot;&gt;Faraday&lt;/h2&gt;

&lt;p&gt;You’ll notice above that Faraday makes up a fair amount of the meat of functionality
of this class. I wanted to break it into it’s own section because, as I mentioned
above, it doesn’t really matter what HTTP library is used, so long as it can be
passed data to make a request with, and passes some kind of response object back.
Examples of other HTTP libraries you might consider instead of Faraday are:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;excon&lt;/code&gt; - a bit more bare metal, but a much smaller dependency - this would be
suitable if you had a moderately complex HTTP endpoint to communicate with from a
gem where you maybe didn’t want to have a dependency as large as Faraday.&lt;/li&gt;
  &lt;li&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;HTTParty&lt;/code&gt; - quite a flexible library but sometimes a bit &lt;em&gt;too&lt;/em&gt; abstract for my
liking. It also has an annoying post-install message whenever &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;bundle install&lt;/code&gt; is
run - including if it’s depended on by another gem. The README for HTTParty has
it’s own example of how to make a super-slim API client class, so if you’re looking
for something specific to HTTParty, be sure to check that out.&lt;/li&gt;
  &lt;li&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Net::HTTP&lt;/code&gt;. Oh-so-tempting since it’s part of the Ruby standard library, but it
really is a low-level API. &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Net::HTTP&lt;/code&gt; may be worth considering if you are the
author of a gem and don’t want to add extra dependencies, but otherwise it’s
probably best to use a library to avoid code that is perhaps more verbose than it
needs to be.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Faraday is modelled on &lt;a href=&quot;https://rack.github.io/&quot;&gt;rack&lt;/a&gt;, which is the de-facto
interface between HTTP requests/responses, and your Ruby server. You could think of
Faraday as the ‘frontend’ version of Rack.&lt;/p&gt;

&lt;p&gt;It has a similar system of almost immediately bundling the request/response into an
‘env’ (environment) object, and then fulfilling the request and transforming the
response by applying a middleware pipeline to it.&lt;/p&gt;

&lt;p&gt;The actual out-of-the-box behaviour of Faraday is quite capable of making an HTTP or
HTTPS request, complete with params, headers, and all of the other stuff that makes
up the core of HTTP. To really unlock some neat behaviour though, it’s worth checking
out the middleware that can be applied to your Faraday connection object. Middleware
have been split out of the main Faraday project, so you only need to have that extra
gem dependency if you need it. The gem is called
&lt;a href=&quot;https://github.com/lostisland/faraday_middleware&quot;&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;faraday_middleware&lt;/code&gt;&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;The middleware I use in my API client is relatively small. I’ll step through each one
and describe what I use it for/what it does:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;conn.request :json&lt;/code&gt; - tells
&lt;a href=&quot;https://github.com/lostisland/faraday_middleware/blob/be685418ba8ef4c428f726e7b943b2fb64860ec5/lib/faraday_middleware/request/encode_json.rb&quot;&gt;FaradayMiddleware::EncodeJson&lt;/a&gt;
to automatically transform any params or request body I pass in to JSON. This means
that I can pass in a whole big hash (or in fact anything that responds to
&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;to_json&lt;/code&gt;), and Faraday will automatically transform it into JSON before sending
the request.&lt;/li&gt;
  &lt;li&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;conn.response :json, parser_options: { object_class: OpenStruct }&lt;/code&gt; - tells Faraday
to automatically transform the response body from a string, back into JSON
(obviously this requires the response actually be valid JSON!). I am passing some
special &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;parser_options&lt;/code&gt; here to tell JSON to decode using &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;OpenStruct&lt;/code&gt; as the
object class. Normally, &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;JSON.parse&lt;/code&gt; will return a Hash, which is &lt;em&gt;fine&lt;/em&gt;, but means
that all the attributes need to be accessed using &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;[]&lt;/code&gt; with String keys. Using
&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;OpenStruct&lt;/code&gt; as the object class means that (as I mentioned above), attributes can
be accessed using hash-key lookup syntax, or method syntax. I’ve also written a
blog post that describes this technique with a few examples at
https://www.joshmcarthur.com/til/2018/12/03/ruby-deserialize-json-to-an-openstruct.html.&lt;/li&gt;
  &lt;li&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;conn.response :raise_error&lt;/code&gt; - this middleware is actually &lt;a href=&quot;https://github.com/lostisland/faraday/blob/master/lib/faraday/response/raise_error.rb&quot;&gt;part of the main Faraday
project&lt;/a&gt;, so you don’t need the middleware gem for this one. It inspects the HTTP status code returned in the request, and will raise a variant of &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Faraday::Error&lt;/code&gt; if the request did not succeed. These variants can be specific for common statuses, like &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Faraday::BadRequestError&lt;/code&gt; or &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Faraday::ResourceNotFound&lt;/code&gt;, or a bit more generic, like &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Faraday::ClientError&lt;/code&gt; and &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Faraday::ServerError&lt;/code&gt;. The rresponse status, headers, and body are attached to the error for later inspection. In the API client, we’re using this middleware to make sure that &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Faraday::Errors&lt;/code&gt; are raised when a HTTP request fails - we’re then rescuing the error, wrapping it in our own error class, and re-raising it.&lt;/li&gt;
  &lt;li&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;conn.response :logger, Rails.logger, bodies: true if @configuration[:debug]&lt;/code&gt;. By
default, Faraday won’t really log much that is useful. This is by design, since
logging a full request/response takes up a number of log lines. This middleware is
conditionally added to the connection if the configuration we pass in (which,
remember, can either come from a hash passed to the API client, or by included in
our &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;config/api_client.yml&lt;/code&gt; as &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;debug: true&lt;/code&gt;) includes a &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;:debug&lt;/code&gt; key that is
truthy. If &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;debug&lt;/code&gt; mode is set, we direct the Faraday logging middleware at our
Rails.logger (you could direct it to it’s own log file or any other &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Logger&lt;/code&gt; if you
wanted to, but &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Rails.logger&lt;/code&gt; means that all our app logs go into a single stream),
and also tell it to log the request body with the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;bodies: true&lt;/code&gt; option. Without
this option, Faraday will only log the request URL and some minimal response info,
which isn’t as useful for debugging as seeing the full request/response.&lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;conn.adapter&lt;/code&gt; - set either to &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;:test&lt;/code&gt;, or &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Faraday.default_adapter&lt;/code&gt;. This setting
is conditional on whether &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;stubs&lt;/code&gt; have been passed in to the API client. If they
have, we assume that we’re testing the API, so we tell Faraday to use a fake
adapter named ‘test’. This adapter will look up a request in the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;stubs&lt;/code&gt; object
when the client is called, and if a stub exists that matches the request (matching
on path and/or params and/or headers), it will return the stubbed response. If
stubs have not been passed in, we’re operating in ‘real’ mode, and set the adapter
to &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Faraday.default_adapter&lt;/code&gt;. This defaults to &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;net/http&lt;/code&gt;, but there’s all sorts of
adapters you can use instead.&lt;/p&gt;

    &lt;p&gt;For more information on testing and using adapters with Faraday, you’ll find both the &lt;a href=&quot;https://github.com/lostisland/faraday/blob/993a483ec34d8d2c6005dbd0623ed41ba5029662/docs/adapters/testing.md&quot;&gt;testing
guide&lt;/a&gt;
and the &lt;a href=&quot;https://github.com/lostisland/faraday/blob/993a483ec34d8d2c6005dbd0623ed41ba5029662/docs/adapters/index.md&quot;&gt;adapters
guide&lt;/a&gt;
useful.&lt;/p&gt;
  &lt;/li&gt;
&lt;/ul&gt;

&lt;hr /&gt;

&lt;p&gt;That’s it. Hopefully this has been a useful and interesting deep dive into a nice
  understandable and configurable HTTP API client. If you’ve spotted any mistakes or
  points that need clarifying, please feel free to &lt;a href=&quot;https://github.com/joshmcarthur/joshmcarthur.github.com/pulls&quot;&gt;contribute a patch&lt;/a&gt; to my
  website repo!&lt;/p&gt;
</description>
        <pubDate>Thu, 17 Oct 2019 00:00:00 +1300</pubDate>
        <link>https://joshmcarthur.com/til/2019/10/17/my-favourite-http-client.html</link>
        <guid isPermaLink="true">https://joshmcarthur.com/til/2019/10/17/my-favourite-http-client.html</guid>
        
        <category>til</category>
        
        <category>technology</category>
        
        <category>rails</category>
        
        <category>ruby</category>
        
        
        <category>TIL</category>
        
      </item>
    
      <item>
        <title>See full Rails application logs on Heroku</title>
        <description>&lt;p&gt;I recently had an issue on an application that was deployed to Heroku. I could see from the frontend
that I was getting a 500 status code in response to my request, but when I took a look at the Heroku
logs (using &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;heroku logs&lt;/code&gt;), I saw a single line containing my request, but no other details:&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;2019-10-15T02:47:56.555551+00:00 heroku[router]: at=info method=POST path=&quot;/widgets&quot;
host=www.widgets.example.com request_id=c4819835-c2e6-480b-8bd3-4d70725f8bbf fwd=&quot;43.255.160.224&quot;
dyno=web.1 connect=0ms service=45ms status=500 bytes=1891 protocol=https
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;This is interesting, because Heroku definitely used to log whatever your Rails app spat out to
STDOUT, but it seems that by default they now suppress some messages - even if your application is
configured with a verbose log level, like my application was set to: &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;config.log_level = :debug&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;The solution to this problem came from &lt;a href=&quot;https://stackoverflow.com/a/22852185&quot;&gt;this StackOverflow
answer&lt;/a&gt; - Heroku has a separate &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;LOG_LEVEL&lt;/code&gt; environment
variable that can be set to control the verbosity of your process logs. A simple &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;heroku config:set
LOG_LEVEL=debug&lt;/code&gt;, and I was seeing the stacktrace in my log output:&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;2019-10-15T02:54:43.377469+00:00 app[web.1]: I, [2019-10-15T02:47:56.520678 #4]  INFO -- : [c4819835-c2e6-480b-8bd3-4d70725f8bbf] Started POST &quot;/widgets&quot; for 43.255.160.224 at 2019-10-15 02:47:56 +0000
2019-10-15T02:54:43.377471+00:00 app[web.1]: I, [2019-10-15T02:47:56.521691 #4]  INFO -- : [c4819835-c2e6-480b-8bd3-4d70725f8bbf] Processing by WidgetsController#create as JS
2019-10-15T02:54:43.377473+00:00 app[web.1]: I, [2019-10-15T02:47:56.521749 #4]  INFO -- : [c4819835-c2e6-480b-8bd3-4d70725f8bbf]   Parameters: {&quot;authenticity_token&quot;=&amp;gt;&quot;Psdasdas=&quot;, &quot;widget&quot;=&amp;gt;{&quot;name&quot;=&amp;gt;&quot;Test Widget&quot;}, &quot;commit&quot;=&amp;gt;&quot;Save&quot;}
2019-10-15T02:54:43.377475+00:00 app[web.1]: D, [2019-10-15T02:47:56.526155 #4] DEBUG -- : [c4819835-c2e6-480b-8bd3-4d70725f8bbf]   [1m[36mUser Load (1.2ms)[0m  [1m[34mSELECT &quot;users&quot;.* FROM &quot;users&quot; WHERE &quot;users&quot;.&quot;id&quot; = $1 ORDER BY &quot;users&quot;.&quot;id&quot; ASC LIMIT $2[0m  [[&quot;id&quot;, 1], [&quot;LIMIT&quot;, 1]]
2019-10-15T02:54:43.377478+00:00 app[web.1]: D, [2019-10-15T02:47:56.532972 #4] DEBUG -- : [c4819835-c2e6-480b-8bd3-4d70725f8bbf]   [1m[36mActiveStorage::Blob Load (1.3ms)[0m  [1m[34mSELECT &quot;active_storage_blobs&quot;.* FROM &quot;active_storage_blobs&quot; WHERE &quot;active_storage_blobs&quot;.&quot;id&quot; = $1 LIMIT $2[0m  [[&quot;id&quot;, 4], [&quot;LIMIT&quot;, 1]]
2019-10-15T02:54:43.377480+00:00 app[web.1]: D, [2019-10-15T02:47:56.548897 #4] DEBUG -- : [c4819835-c2e6-480b-8bd3-4d70725f8bbf]   [1m[35m (1.3ms)[0m  [1m[35mBEGIN[0m
2019-10-15T02:54:43.377482+00:00 app[web.1]: D, [2019-10-15T02:47:56.551282 #4] DEBUG -- : [c4819835-c2e6-480b-8bd3-4d70725f8bbf]   [1m[Widget Create (2.1ms)[0m  [1m[32mINSERT INTO &quot;widgets&quot; (&quot;name&quot;, &quot;updated_at&quot;, &quot;created_at&quot;, &quot;user_id&quot;) VALUES ($1, $2, $3, $4) RETURNING &quot;id&quot;[0m  [[&quot;name&quot;, &quot;Test Widget&quot;], [&quot;updated_at&quot;, &quot;2019-10-15 02:47:56.546980&quot;], [&quot;created_at&quot;, &quot;2019-10-15 02:47:56.546980&quot;], [&quot;user_id&quot;, 1]]
2019-10-15T02:54:43.377484+00:00 app[web.1]: D, [2019-10-15T02:47:56.552428 #4] DEBUG -- : [c4819835-c2e6-480b-8bd3-4d70725f8bbf]   [1m[35m (1.0ms)[0m  [1m[31mROLLBACK[0m
2019-10-15T02:54:43.377486+00:00 app[web.1]: I, [2019-10-15T02:47:56.552765 #4]  INFO -- : [c4819835-c2e6-480b-8bd3-4d70725f8bbf] Completed 500 Internal Server Error in 31ms (ActiveRecord: 11.7ms | Allocations: 5871)
2019-10-15T02:54:43.377489+00:00 app[web.1]: F, [2019-10-15T02:47:56.553564 #4] FATAL -- : [c4819835-c2e6-480b-8bd3-4d70725f8bbf]   
2019-10-15T02:54:43.377491+00:00 app[web.1]: [c4819835-c2e6-480b-8bd3-4d70725f8bbf] ActiveRecord::RecordNotUnique (PG::UniqueViolation: ERROR:  duplicate key value violates unique constraint &quot;widgets_pkey&quot;
2019-10-15T02:54:43.377494+00:00 app[web.1]: DETAIL:  Key (id)=(17) already exists.
2019-10-15T02:54:43.377496+00:00 app[web.1]: ):
2019-10-15T02:54:43.377498+00:00 app[web.1]: [c4819835-c2e6-480b-8bd3-4d70725f8bbf]   
2019-10-15T02:54:43.377500+00:00 app[web.1]: [c4819835-c2e6-480b-8bd3-4d70725f8bbf] app/controllers/widgets_controller.rb:21:in `create&apos;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Nice! Once you’ve got the error
tracked down and resolved, you can either remove this config value entirely, or reduce it to a less
verbose level, like &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;warn&lt;/code&gt;.&lt;/p&gt;
</description>
        <pubDate>Tue, 15 Oct 2019 00:00:00 +1300</pubDate>
        <link>https://joshmcarthur.com/til/2019/10/15/see-full-rails-application-logs-on-heroku.html</link>
        <guid isPermaLink="true">https://joshmcarthur.com/til/2019/10/15/see-full-rails-application-logs-on-heroku.html</guid>
        
        <category>til</category>
        
        <category>technology</category>
        
        
        <category>TIL</category>
        
      </item>
    
      <item>
        <title>Fancy Postgres indexes with ActiveRecord</title>
        <description>&lt;p&gt;Another day, another undocumented Rails feature!&lt;/p&gt;

&lt;p&gt;This time, it’s that &lt;a href=&quot;https://apidock.com/rails/ActiveRecord/ConnectionAdapters/SchemaStatements/add_index&quot;&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;ActiveRecord::Base.connection.add_index&lt;/code&gt;&lt;/a&gt; supports an undocumented option to pass a string argument as the value for ‘column’.&lt;/p&gt;

&lt;p&gt;This string is passed directly to the SQL statement, making it possible to use all sorts of fun things to lock down the constraint. It also means that you can make an index more declaratively if you’re not comfortable using the built-in options for key lengths or ordering.&lt;/p&gt;

&lt;p&gt;Here’s a real-life example of the String invocation, where I am making a case-insensitive unique index on folder titles that ignores leading and trailing whitespace:&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;add_index&lt;/span&gt; &lt;span class=&quot;ss&quot;&gt;:folders&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
          &lt;span class=&quot;s2&quot;&gt;&quot;user_id, TRIM(BOTH FROM LOWER(title))&quot;&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: :user_folders_title_unique_idx&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;I’m making a unique index on &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;&quot;folders&quot;.&quot;title&quot;&lt;/code&gt;, scoped to &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;&quot;folders&quot;.&quot;user_id&quot;&lt;/code&gt;. All standard so
far, &lt;em&gt;except&lt;/em&gt; for how the title is declared:&lt;/p&gt;

&lt;p&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;TRIM(BOTH FROM LOWER(title))&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;These are &lt;em&gt;Postgres functions&lt;/em&gt;, and will act on the value that is passsed to it. In this case,
&lt;a href=&quot;https://www.postgresql.org/docs/11/functions-string.html#id-1.5.8.9.5.2.2.12.1.1&quot;&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;TRIM&lt;/code&gt;&lt;/a&gt; will
strip whitespace from the string passed to it. I’m adding &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;BOTH&lt;/code&gt; so that both leading and trailing
whitespace is trimmed.
&lt;a href=&quot;https://www.postgresql.org/docs/11/functions-string.html#id-1.5.8.9.5.2.2.5.1.1&quot;&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;LOWER&lt;/code&gt;&lt;/a&gt;,
unsurprisingly, converts the string passed to lowercase.&lt;/p&gt;

&lt;p&gt;As far as I know, because this is passed directly to the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;CREATE INDEX&lt;/code&gt; call, any thing that could
be passed to a raw SQL index creation call can be provided to this argument of &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;add_index&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;I have also verified that the index call is correctly serialized in &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;schema.rb&lt;/code&gt;. In the case of the
above index, it is recorded as:&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;create_table&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;folders&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;ss&quot;&gt;force: :cascade&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;t&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;t&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;index&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;user_id, btrim(lower((title)::text))&quot;&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;user_folders_title_unique_idx&quot;&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;k&quot;&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Finally, something important to note: Rails can normally figure out what the index should be called
by sticking the table name and column names you want in your index together with underscores. Not so
in this case - because you are passing your own index statement, you &lt;em&gt;must&lt;/em&gt; provide the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;name: &lt;/code&gt;
option to &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;add_index&lt;/code&gt; like I have above, so that the index can be identified.&lt;/p&gt;

</description>
        <pubDate>Tue, 15 Oct 2019 00:00:00 +1300</pubDate>
        <link>https://joshmcarthur.com/til/2019/10/15/fancy-postgres-indexes-with-activerecord.html</link>
        <guid isPermaLink="true">https://joshmcarthur.com/til/2019/10/15/fancy-postgres-indexes-with-activerecord.html</guid>
        
        <category>til</category>
        
        <category>technology</category>
        
        <category>rails</category>
        
        <category>activerecord</category>
        
        
        <category>TIL</category>
        
      </item>
    
      <item>
        <title>Crossing technical and personal boundaries: New project nerves</title>
        <description>&lt;p&gt;A really interesting thought from Chris Toomey from Thoughtbot’s ‘The Bikeshed’ podcast, that
really elegantly clarifies my own thinking on imposter syndrome, feelings of inadequacy, and the
strive to deliver value to a broad range of contexts.&lt;/p&gt;

&lt;blockquote&gt;
  &lt;p&gt;The work we do is purposefully somewhat challenging. We’re coming into organistions to try and help
them, most often at sort of an inflection point - a point at which they’re struggling, either
technically or process-wise. So there’s &lt;em&gt;something&lt;/em&gt; we have to help with. It’s not always clear, or
it may be that people are pointing at one thing but meaning something else and so there’s always
some nerves.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;blockquote&gt;
  &lt;p&gt;Do I worry about not knowing the technical facets as much? The longer I do this, the longer I see
that code is much the same across different frameworks and languages. I’ve got better at learning, I
can do that more quickly, use existing knowledge. At the end of the day, the hard part, and the
thing that we bring into the situation is not so much about the code, but the conversations -
getting real pixels in front of users, and then mapping that back into the code. And THAT skillset
is the same everywhere, and I know I have that foundation and I lean into that all the harder at the
beginning of the project.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;(Paraphrased and left out a couple of less-relevant sentences for brevity that related to pairing at Thoughtbot)&lt;/p&gt;

&lt;p&gt;I really like the statements made here. I think I personally tend to concentrate too much on what is
being brought to the table in technical skill, and not as much in the value that exists just from
having an expert(s) guiding an organisation through something that they’re not necessarily
well-equpped to handle on theier own.&lt;/p&gt;
</description>
        <pubDate>Sat, 17 Aug 2019 00:00:00 +1200</pubDate>
        <link>https://joshmcarthur.com/til/2019/08/17/crossing-technical-and-personal-boundaries-new-project-nerves.html</link>
        <guid isPermaLink="true">https://joshmcarthur.com/til/2019/08/17/crossing-technical-and-personal-boundaries-new-project-nerves.html</guid>
        
        <category>til</category>
        
        <category>technology</category>
        
        
        <category>TIL</category>
        
      </item>
    
      <item>
        <title>Ubuntu laptop backup strategy</title>
        <description>&lt;p&gt;I recently upgraded my laptop from a 2013 Macbook Pro to a Lenovo Thinkpad T480. It’s great. I
switched around my Ctrl key to the Windows key (where the Command key would be on a Mac keyboard),
and everything has pretty much been perfect.&lt;/p&gt;

&lt;p&gt;One thing that I did need to rethink was my backup strategy, which, on my Macbook had been ad-hoc
TimeMachine. Coming from that, the world of backups on Linux was a little overwhelming. Many backup
tools are designed for, or at least documented for, continuously-running server environments, not a
laptop. Additionally, the backups will often be something like an automated rsync or scp, and not
have a particularly nice ‘set and forget’ experience.&lt;/p&gt;

&lt;p&gt;I did a bit of research, and found out that there probably wasn’t a UI tool for my shopping list:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;strong&gt;Diff backups:&lt;/strong&gt; I was coming from TimeMachine, so this was pretty important to me. I wanted a
backup to just be a diff of the last backup, not a full copy every time. This cuts down on the
time required for backing up, as well as upload size.&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;S3 backup:&lt;/strong&gt; I don’t run cloud servers myself - it’s just another thing to keep locked down and
secure. I already have some manual backups on S3, and I was keen to get my laptop backups headed
there as well. S3 has a bunch of backup-relevant tools available, such as encryption-at-rest,
versioning, access logging, and lifecycle rules. The lifecycle rules are particularly useful as
this allows me to not have to manage much in the way of backup retention within the actual backup
tool - I can just tell S3 to archive backups to Glacier after 6 months, and delete them a year
after that (or whatever). Also, S3 storage at the scale of ~200GB is peanuts. Maybe a dollar.&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;GPG Encryption:&lt;/strong&gt; The same as S3. I use GPG for a few things, and like it. I like how it’s tied
to an identity, not a specific password. I suspect the ‘right way’ (if such a thing exists) is to
generate a subkey tied to my main key and use this to encrypt my backups. I don’t really
understand subkeys, so I just made a whole new key for backups. This isn’t perfect, but does mean
that I can manage the key without worrying about what it’s doing to my main key.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Not much to ask for, I thought, but it proved kind of hard to find what I was looking for. In the
end, I did find it though - and the joke was on me, since I had seen &lt;em&gt;duplicity&lt;/em&gt; mentioned many
times early in my research and had discarded it because “it didn’t have a UI” :-(.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;duplicity&lt;/em&gt; is a really nice backup tool. This is largely because it’s not really a backup tool
itself, more of a tool that sticks a bunch of related tools together into a nice smooth pipeline.&lt;/p&gt;

&lt;p&gt;Without any extra installation, &lt;em&gt;duplicity&lt;/em&gt; could do all of the things on my shopping list. I even
got a neat bonus, where I could use the
&lt;a href=&quot;http://webcache.googleusercontent.com/search?q=cache:07-3ufYXKx4J:www.nongnu.org/duplicity//duplicity.1.html&amp;amp;hl=en&amp;amp;gl=nz&amp;amp;strip=1&amp;amp;vwsrc=0#sect18&quot;&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;multi://&lt;/code&gt;&lt;/a&gt;
backend to backup to a local hard drive initially, and then to my primary S3 backup destination.
This setup allows me to have a local, space constrained, not-very-reliable backup that is &lt;em&gt;very fast
to restore from&lt;/em&gt; - while still retaining my primary, reliable, cost-effective and lots-of-retention
data store on S3. I was happy with this. It’s not quite a 3-2-1 backup strategy - I have 2 copies of
my data, each on a different storage media, with 1 offsite. I could probably get 3 copies by
archiving to Glacier earlier, but my lifecycle rules are currently set up to initially keep backups
in S3 only.&lt;/p&gt;

&lt;p&gt;So, &lt;em&gt;duplicity&lt;/em&gt; could do all the things I wanted it to, but all via command line flags. Not very
maintainable, I thought. A quick Google search, and I found that at least one other person thought
the same!&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;https://0xacab.org/riseuplabs/backupninja&quot;&gt;&lt;em&gt;backupninja&lt;/em&gt;&lt;/a&gt; is a wrapper for Duplicity (and a bunch of other backup tools, but I’m just using
Duplicity). It supports defining a backup ‘pipeline’ in &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;/etc/backup.d&lt;/code&gt; to run a series of backups,
one after the other. It also supports scheduling backups and has a very usable curses-based
interface via the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;ninjahelper&lt;/code&gt; utility.&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/img/posts/ninjahelper.png&quot; alt=&quot;The `ninjahelper` interface&quot; /&gt;&lt;/p&gt;

&lt;p&gt;I used the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;ninjahelper&lt;/code&gt; utility to create a Duplicity backup configuration file in
&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;/etc/backup.d/90.dup&lt;/code&gt;, and then edited it extensively to achieve the backup behaviour I wanted. I
won’t post the full configuration file here, but here are some excerpts relating to the points I
wanted to achieve above:&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;# Backup every day at 12pm. I am usually at work with an idle laptop around this time. 
when = everyday at 12
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&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;# These are extra options that I have experimented with:
#   --s3-use-multiprocessing will use S3 multipart uploads concurrently, one upload processs
#     per core. This speeds up the upload to S3.
#   --s3-use-server-side-encryption will tell S3 to encrypt the uploaded diff files using the S3
#     encryption key. I don&apos;t use my own KMS key because I mostly trust GPG.
#   --allow-source-mismatch had to be used initially because I had some partial backups. I probably
#     don&apos;t need it anymore.
#   --progress was supposed to tell me the progress of the upload when I manually run a backup. It
#     does do this, but the output isn&apos;t particularly useful.
#   --gpg-options needed to be specified, because backupninja runs as root to access all the things
#     it needs to backup. Without this, GPG was trying to use /root/.gnupg, and I didn&apos;t want to
#     have my keys spread across multiple users.
options = --s3-use-multiprocessing --s3-use-server-side-encryption --allow-source-mismatch
--progress --gpg-options &quot;--homedir=~josh/.gnupg&quot;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;The GPG options are pretty simple. I just provided the encryption key and allowed this to be used
for both encryption and signing. I had hoped to keep the key password out of the config file, but didn’t
have much luck loading it from elsewhere.&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;[gpg]

# when set to yes, encryptkey variable must be set below; if you want to use
# two different keys for encryption and signing, you must also set the signkey
# variable below.
# default is no, for backwards compatibility with backupninja &amp;lt;= 0.5.
sign = yes

# ID of the GnuPG public key used for data encryption.
# if not set, symmetric encryption is used, and data signing is not possible.
encryptkey = [public key]

# ID of the GnuPG private key used for data signing.
# if not set, encryptkey will be used.
signkey =

## password used to unlock the encryption key
## NB: neither quote this, nor should it contain any quotes,
## an example setting would be:
## password = a_very_complicated_passphrase
password = [key]

## password used to unlock the signature key, used only if
## it differs from the encryption key
## NB: neither quote this, nor should it contain any quotes,
## an example setting would be:
## signpassword = a_very_complicated_passphrase
signpassword =
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;The multi destination stuff is pretty neat. The actual config file just had a provider defined, when
the provider is actually just a path to a JSON file that defines the providers:&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;desturl = multi:///home/josh/.dotfiles/backup/providers.json
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;The JSON file contains details on how duplicity should connect to both backends. Once again, I tried
to keep secrets out of this file, but didn’t have any luck providing them with any of the standard
ways of configuring AWS access key IDs and secrets:&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;[
	{
		&quot;description&quot;: &quot;Local backup. Not to be used for reliable backups, just for restore speed&quot;,
		&quot;url&quot;: &quot;file:///media/josh/DROPTANK/duplicity/flaps?mode=mirror&amp;amp;onabort=continue&quot;
	},
	{
		&quot;description&quot;: &quot;Primary datastore&quot;,
    &quot;url&quot;: &quot;s3+http://[bucket name]/duplicity/flaps?mode=mirror&amp;amp;onabort=fail&quot;,
    &quot;env&quot;: [
      { &quot;name&quot;: &quot;AWS_ACCESS_KEY_ID&quot;, &quot;value&quot;: &quot;[AWS access key ID]&quot; },
      { &quot;name&quot;: &quot;AWS_SECRET_ACCESS_KEY&quot;, &quot;value&quot;: &quot;[AWS secret access key]&quot; }
    ]
	}
]
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;The query params mentioned here do have special meaning as per the &lt;em&gt;duplicity&lt;/em&gt; man page:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;mode&lt;/code&gt; - mirror or stripe. I want to &lt;em&gt;mirror&lt;/em&gt; my backups - have a copy in each destination, not 
 striped across multiple destinations.&lt;/li&gt;
  &lt;li&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;onabort&lt;/code&gt; - this is set to ‘continue’ for the disk backup, because if it fails, it’s probably 
because the disk just isn’t plugged in (I’m probably not at my desk). For the S3 destination, 
this is set to &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;abort&lt;/code&gt;, because I want to know if &lt;em&gt;this&lt;/em&gt; backup fails.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;…and that’s all the configuration. With this approach, I have automated daily diff backups to two
different sources - one local, one remote, with two different storage medium. Technically once my
lifecycle rules kick in, I’ll actually have three sources and mediums since Glacier backups are
stored on a different media than normal S3 blobs (Bluray, I think). 
I use lifecycle rules on S3 to manage retention, which I  can basically configure to be indefinite 
if I want to, and the pricing is super reasonable, since the diff backups don’t take up too much
transfer bandwidth or storage space once the initial backup is complete. Both my local and remote
backups are encrypted with a mechanism that I can manage myself - I’m not trusting Dropbox or Google
Drive or any other cloud storage provider to keep my files secure - but, since I can, I’m taking
advantage of S3 encryption anyway.&lt;/p&gt;

&lt;p&gt;Hopefully this has been useful to somebody! I depended on a &lt;em&gt;lot&lt;/em&gt; of StackOverflow answers, blog
posts, man pages and other resources to put my configuration together - I certainly didn’t just
figure it out - but I think that this backup strategy may work well for others as well.&lt;/p&gt;

&lt;p&gt;I have a couple of ideas for next steps. Fairly imminent is a restore test, which I’ll probably aim
to conduct on an EC2 instance for the first restore just to make sure that everything goes smoothly.
If that goes well, I will snapshot my disk and try a restore onto my actual laptop.&lt;/p&gt;

&lt;p&gt;I would also like to look into S3 region replication. This is something I normally have set up for
website uploads in my deveopment job, but I need to price it out for backups and make sure that it
won’t blow things out too badly. If I get replication set up, then along with backups stored in S3
and Glacier, I’ll also have an additional level of redundancy, with backups stored in a completely
different region (probably Ireland or Frankfurt since I don’t like putting things is US regions and
don’t quite trust London either anymore). Singapore is too close to my physical location to feel
safe and my primary backups are already in Sydney.&lt;/p&gt;
</description>
        <pubDate>Mon, 05 Aug 2019 00:00:00 +1200</pubDate>
        <link>https://joshmcarthur.com/til/2019/08/05/ubuntu-laptop-backup-strategy.html</link>
        <guid isPermaLink="true">https://joshmcarthur.com/til/2019/08/05/ubuntu-laptop-backup-strategy.html</guid>
        
        <category>til</category>
        
        <category>technology</category>
        
        
        <category>TIL</category>
        
      </item>
    
      <item>
        <title>Upgrading Rails apps with rake app:update</title>
        <description>&lt;p&gt;It’s been awhile since I’ve done a Rails update. After this long working with the framework, 
I’ve learned that it’s much, much easier to keep things up to date with tools like &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;bundle-audit&lt;/code&gt;
so that things never get so bad that I’ve got a whole major version (or more!) to upgrade.&lt;/p&gt;

&lt;p&gt;Having said that, support legacy applications is just something that needs to be done sometimes, and
today I learned a little trick that I’ve somehow missed in the last 10 years: &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;rake app:update&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;The Rails Guides actually have this task &lt;a href=&quot;https://guides.rubyonrails.org/upgrading_ruby_on_rails.html#the-update-task&quot;&gt;documented pretty
clearly&lt;/a&gt; - section 1.4
in fact - so I’m not sure exactly how I missed this.&lt;/p&gt;

&lt;p&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;rake app:update&lt;/code&gt; isn’t magic, but it is helpful. It basically runs the same code path that &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;rails
new&lt;/code&gt; does when creating an application, but has the same conflict resolution process that normal
&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;rails generate&lt;/code&gt; processes do when it comes across two files where the newer version is not the same
as the older version:&lt;/p&gt;

&lt;div class=&quot;language-sh highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;Overwrite /usr/src/app/bin/rails? &lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;enter &lt;span class=&quot;s2&quot;&gt;&quot;h&quot;&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;for &lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;help&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;[&lt;/span&gt;Ynaqdhm] Y
       force  bin/rails
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;The options to resolve conflicts here are:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;Y - yes, overwrite&lt;/li&gt;
  &lt;li&gt;n - no, keep our version&lt;/li&gt;
  &lt;li&gt;a - apply this action to all following files (e.g. when you get to &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;bin/&lt;/code&gt; you can generally
overwrite all of those files).&lt;/li&gt;
  &lt;li&gt;q - Quit, stop here.&lt;/li&gt;
  &lt;li&gt;d - display a diff between the two versions.&lt;/li&gt;
  &lt;li&gt;h - display help, which is basically this list here.&lt;/li&gt;
  &lt;li&gt;m - open the conflict in a merge editor of your choice (e.g. VSCode, which can be &lt;a href=&quot;https://stackoverflow.com/a/44549734&quot;&gt;configured for
git to use for conflict resolution&lt;/a&gt;).&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Out of all of these options, I find &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;diff&lt;/code&gt; the most useful option, since it gives me an overview of
the changes. I currently manually patch changes that I want to include into the file in another tmux
pane, and then choose to not overwrite the file. My shell is not currently set up with a defined
&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;mergetool&lt;/code&gt; like VS Code, but I’m definitely going to give this a go next time, since it is
basically a more streamlined process for what I am already doing.&lt;/p&gt;

&lt;p&gt;One final note - &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;rake app:update&lt;/code&gt; is NOT bulletproof. It’s not intended to be. It’s just a
convenience. As with any change in your application’s code or dependencies, &lt;em&gt;a comprehensive test
suite is the most important feature&lt;/em&gt;. If you are going into any kind of change process, but
especially a framework update without at least some key behaviour tests, stop and add them. It’s
incredibly difficult otherwise to check after each change that your application remains functional.&lt;/p&gt;

</description>
        <pubDate>Thu, 25 Jul 2019 00:00:00 +1200</pubDate>
        <link>https://joshmcarthur.com/til/2019/07/25/upgrading-rails-apps-with-rake-appupdate.html</link>
        <guid isPermaLink="true">https://joshmcarthur.com/til/2019/07/25/upgrading-rails-apps-with-rake-appupdate.html</guid>
        
        <category>til</category>
        
        <category>technology</category>
        
        <category>rails</category>
        
        
        <category>TIL</category>
        
      </item>
    
      <item>
        <title>Broadcast receiver pattern</title>
        <description>&lt;p&gt;I have recently had the chance to get stuck back into some native Android development. One of the
things I really enjoy about working with the Android framework is that I get the chance to explore
new patterns, and patterns that I have used before for Ruby, but not yet in Java.&lt;/p&gt;

&lt;p&gt;One of these new patterns is the idea of a broadcast receiver. Broadcast receivers are a core part
of the Android framework, however receivers are triggered outside the cotext of an application
activity or fragment, so it can be a little unclear how to trigger something to happen in an
activity (such as updating a UI component) when an intent is received.&lt;/p&gt;

&lt;p&gt;The best example of this pattern is a code snippet. The following class is one I added to the NZSL
Dictionary Android application. It registers and receives intents from the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;DownloadManager&lt;/code&gt; system
component - specifically, when a download completes with a success or failure state
(&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;DownloadManager.ACTION_DOWNLOAD_COMPLETED&lt;/code&gt;), or when the notification that the system shows when
the download is completed is clicked (&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;DownloadManager.ACTION_NOTIFICATION_CLICKED&lt;/code&gt;).&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-java&quot; data-lang=&quot;java&quot;&gt;&lt;span class=&quot;kd&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;kd&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;DownloadReceiver&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;kd&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;DownloadCallback&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;callback&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;;&lt;/span&gt;

    &lt;span class=&quot;kd&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;BroadcastReceiver&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;receiver&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;BroadcastReceiver&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;nd&quot;&gt;@Override&lt;/span&gt;
        &lt;span class=&quot;kd&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;void&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;onReceive&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nc&quot;&gt;Context&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;context&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;Intent&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;intent&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;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;callback&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;==&lt;/span&gt; &lt;span class=&quot;kc&quot;&gt;null&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;;&lt;/span&gt;
            &lt;span class=&quot;nc&quot;&gt;String&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;action&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;intent&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;getAction&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;();&lt;/span&gt;

            &lt;span class=&quot;k&quot;&gt;switch&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;intent&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;getAction&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;k&quot;&gt;case&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;DownloadManager&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;ACTION_NOTIFICATION_CLICKED&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;:&lt;/span&gt;
                     &lt;span class=&quot;n&quot;&gt;callback&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;onDownloadNotificationClicked&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;();&lt;/span&gt;
                     &lt;span class=&quot;k&quot;&gt;break&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;;&lt;/span&gt;
                &lt;span class=&quot;k&quot;&gt;case&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;DownloadManager&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;ACTION_DOWNLOAD_COMPLETE&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;:&lt;/span&gt;
                     &lt;span class=&quot;n&quot;&gt;callback&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;onDownloadCompleted&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;();&lt;/span&gt;
                     &lt;span class=&quot;k&quot;&gt;break&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;o&quot;&gt;}&lt;/span&gt;
    &lt;span class=&quot;o&quot;&gt;};&lt;/span&gt;

    &lt;span class=&quot;kd&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;void&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;registerContext&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nc&quot;&gt;Context&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;context&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;DownloadCallback&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;callback&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;k&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;callback&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;callback&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;;&lt;/span&gt;
        &lt;span class=&quot;nc&quot;&gt;IntentFilter&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;filter&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;IntentFilter&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;();&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;filter&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;addAction&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nc&quot;&gt;DownloadManager&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;ACTION_NOTIFICATION_CLICKED&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;);&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;filter&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;addAction&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nc&quot;&gt;DownloadManager&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;ACTION_DOWNLOAD_COMPLETE&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;);&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;context&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;registerReceiver&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;receiver&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;filter&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;kd&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;void&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;unregisterContext&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nc&quot;&gt;Context&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;context&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;callback&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;kc&quot;&gt;null&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;;&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;context&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;unregisterReceiver&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;receiver&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;kd&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;kd&quot;&gt;interface&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;DownloadCallback&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;kt&quot;&gt;void&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;onDownloadCompleted&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;();&lt;/span&gt;
        &lt;span class=&quot;kt&quot;&gt;void&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;onDownloadNotificationClicked&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;o&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;p&gt;This class exposes two core methods - &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;registerContext&lt;/code&gt; and &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;unregisterContext&lt;/code&gt;. These methods are intended to be connected to the lifecycle methods of an activity or fragment - specifically, &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;onPause&lt;/code&gt; and &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;onResume&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;registerContext&lt;/code&gt; accepts two arguments - the context to use, and the callback class implementing the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;DownloadCallback&lt;/code&gt; interface. These arguments can &lt;em&gt;normally&lt;/em&gt; be set to the same value - the activity or fragment wanting to receive messages when a relevant download action occurs.&lt;/p&gt;

&lt;p&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;unregisterContext&lt;/code&gt; simply accepts a the single context to use to disable the broadcast receiver. No &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;AndroidManifest.xml&lt;/code&gt; changes are necessary, since the receiver is connected and disconnected as &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;registerContext&lt;/code&gt; and &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;unregisterContext&lt;/code&gt; are called.&lt;/p&gt;

&lt;p&gt;Within the broadcast receiver, it is also possible to do something else with the value, such as caching it in a class-private field so that &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;registerContext&lt;/code&gt; can return a value immediately to the subscriber without waiting for the broadcast intent to be triggered. An example of when I have found this useful is in a &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;NetworkManager&lt;/code&gt;, to detect whether there is an active internet connection or not.&lt;/p&gt;

&lt;hr /&gt;

&lt;p&gt;The main reason I like this pattern is that it abstracts away just about any kind of behaviour that may be received from a broadcast intent. This can be more than just system broadcasts, as other apps can also emit intents that can be listened for and intercepted.&lt;/p&gt;

&lt;p&gt;When an intent is received by a class following this pattern, it can be parsed, validated, and otherwise repackaged before being passed to the subscribing class via a consistent interface that is abstracted away from exactly &lt;em&gt;how&lt;/em&gt; the data was received. Nice!&lt;/p&gt;
</description>
        <pubDate>Tue, 23 Jul 2019 00:00:00 +1200</pubDate>
        <link>https://joshmcarthur.com/til/2019/07/23/broadcast-receiver-pattern.html</link>
        <guid isPermaLink="true">https://joshmcarthur.com/til/2019/07/23/broadcast-receiver-pattern.html</guid>
        
        <category>til</category>
        
        <category>technology</category>
        
        <category>android</category>
        
        
        <category>TIL</category>
        
      </item>
    
      <item>
        <title>Speeding up TimeMachine backups</title>
        <description>&lt;p&gt;TimeMachine is an incredibly smooth backup solution for Mac OS, but it can be a little slow,
especially for initial backups or incremental backups that have a large amount of changes (such as a
VM image being added to your filesystem or the typical I/O killer, many small files).&lt;/p&gt;

&lt;p&gt;I recently performed a TimeMachine backup to a brand new 1TB portable hard drive, and was alarmed at
the planned time to complete - 19 hours for 130GB. Here are some tweaks I found to speed things up:&lt;/p&gt;

&lt;h2 id=&quot;1-prevent-mac-from-sleeping&quot;&gt;1. Prevent Mac from sleeping&lt;/h2&gt;

&lt;p&gt;Left to it’s own devices, Mac OS will typically sleep after a period of inactivity. This can place
TimeMachine backups AND disks in a slower state. While the backup runs, it is worth adjusting Energy
Saver settings to never sleep the computer or display:&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/img/posts/slow-timemachine/energy-saver-1.png&quot; alt=&quot;Energy Saver - do not sleep&quot; /&gt;&lt;/p&gt;

&lt;p&gt;There is also a handy app called &lt;a href=&quot;http://lightheadsw.com/caffeine/&quot;&gt;Caffeine&lt;/a&gt; that allows toggling
on and off of sleep.&lt;/p&gt;

&lt;h2 id=&quot;2-disable-put-hard-disks-to-sleep-when-possible&quot;&gt;2. Disable “Put hard disks to sleep when possible”&lt;/h2&gt;

&lt;p&gt;Not all disks support this, but keeping both the portable drive and system drive active is obviously
beneficial to TimeMachine backup. This toggle can also be found in the Energy Saver preferences:&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/img/posts/slow-timemachine/energy-saver-2.png&quot; alt=&quot;Energy Saver - do not put hard disks to sleep&quot; /&gt;&lt;/p&gt;

&lt;h2 id=&quot;3-ensure-spotlight-is-not-indexing-the-timemachine-drive&quot;&gt;3. Ensure Spotlight is not indexing the TimeMachine drive&lt;/h2&gt;

&lt;p&gt;Spotlight is the indexing service built into Mac OS - by default, it crawls and indexes content on
your computer to use when you hit the ‘Search’ icon in your menu bar (or hit Cmd-Space). The problem
is that indexing is quite I/O intensive, so this can cause a real slowdown. On a newer disk, I
believe Spotlight may also begin indexing the Timemachine backup itself before the backup is
complete (there is a warning if you try and exclude the Timemachine backup disk once the initial
backup is done, but I did not see a warning when I excluded the Timemachine disk before the initial
backup).&lt;/p&gt;

&lt;p&gt;To exclude your Timemachine disk from indexing, go into System Preferences -&amp;gt; Spotlight -&amp;gt; Privacy,
click the Add (‘+’) button, and select your Timemachine disk from the sidebar in finder. You can
also drag and drop the drive into this list.&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/img/posts/slow-timemachine/spotlight-1.png&quot; alt=&quot;Spotlight - exclude Timemachine disk&quot; /&gt;&lt;/p&gt;

&lt;p&gt;Once you’ve made this change, you may want to restart your computer to make sure that both Spotlight
and TimeMachine recognise this change.&lt;/p&gt;

&lt;h2 id=&quot;4-temporarily-disable-low-priority-service-throttling&quot;&gt;4. Temporarily disable low priority service throttling&lt;/h2&gt;

&lt;p&gt;Mac OS has a concept of low-priority services. TimeMachine is lumped into this category, since in
normal operation backups should just run in the background without taking up more resources than
absolutely necessary. While you’re doing an initial backup though, you kind of &lt;em&gt;want&lt;/em&gt; TimeMachine to
have all the resources it needs to do it’s job a bit faster.&lt;/p&gt;

&lt;p&gt;Low priority throttling can be disabled using a sysctl flag via a Terminal. To get to a Terminal,
hit Cmd-Space, and type in ‘Terminal’ without quotes. The terminal will open - an empty screen with
a prompt. The command to run here is &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;sudo sysctl debug.lowpri_throttle_enabled=0&lt;/code&gt;. When you run
this command, you may be presented with a disclaimer-like warning and you will be prompted for your
computer password - type it in (note that the characters will not be shown), and hit enter to apply
the setting.&lt;/p&gt;

&lt;p&gt;In the same terminal window, you can now run &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;sudo sysctl debug.lowpri_throttle_enabled&lt;/code&gt; to see the
status of the throttling. It &lt;em&gt;should&lt;/em&gt; say &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;debug.lowpri_throttle_enabled: 0&lt;/code&gt;. If it does not, go
back and try adding a &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;-w&lt;/code&gt; to the command - &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;sudo sysctl -w debug.lowpri_throttle_enabled=0&lt;/code&gt;, as
some older versions of OS X seem to require this for the setting to stick.&lt;/p&gt;

&lt;figure&gt;
  &lt;img src=&quot;/img/posts/slow-timemachine/sysctl.gif&quot; alt=&quot;Sysctl demo&quot; /&gt;
  &lt;figcaption&gt;&lt;a href=&quot;http://recordit.co/EBVA4Er0ge&quot;&gt;HQ version&lt;/a&gt;&lt;/figcaption&gt;
&lt;/figure&gt;

&lt;p&gt;With this setting enabled, TimeMachine will no longer be throttled and will be free to use all the
system resources it can. You’ll most likely now see TimeMachine appear in the list of apps ‘Using
Significant Energy’ - this is great, as significant energy also equals significant system resources.&lt;/p&gt;

&lt;p&gt;Also note that this setting only lasts until your computer is restarted. To clear the setting, you
can either follow the instructions above, but set the setting to &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;1&lt;/code&gt; instead of &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;0&lt;/code&gt;, or you can
simply restart your computer.&lt;/p&gt;

&lt;hr /&gt;

&lt;p&gt;With these changes, my initial backup of 130GB completed in 8 hours overnight, instead of the
initial TimeMachine had planned, where after several hours it had backed up just 2GB. YMMV, but
the tips above should all contribute to a faster backup process. Don’t forget to switch these
settings back to their original values when your backup is complete, since the incremental
TimeMachine backups are fast and don’t use much resource.&lt;/p&gt;
</description>
        <pubDate>Thu, 20 Jun 2019 00:00:00 +1200</pubDate>
        <link>https://joshmcarthur.com/til/2019/06/20/speeding-up-timemachine-backups.html</link>
        <guid isPermaLink="true">https://joshmcarthur.com/til/2019/06/20/speeding-up-timemachine-backups.html</guid>
        
        <category>til</category>
        
        <category>technology</category>
        
        
        <category>TIL</category>
        
      </item>
    
      <item>
        <title>CURLing to a UNIX socket</title>
        <description>&lt;p&gt;In deployed environments, I often come across two Ruby application servers - Unicorn, and Puma. 
These servers are usually configured to listen on a UNIX socket file, usually located in the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;tmp/sockets&lt;/code&gt; directory of a Capistrano install, e.g. &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;/home/deploy/appname/shared/tmp/sockets/appname.sock&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;It’s quite often useful to be able to connect to a UNIX socket to see if the application server is behaving correctly, 
especially if the instance is not accessible via a public IP address. A little-known feature of the
&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;curl&lt;/code&gt; command line is that it accepts a &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;--unix-socket&lt;/code&gt; argument to send requests to. This allows
application servers and any other process that listens on a socket and supports HTTP to be tested
easily with a command like:&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-ruby&quot; data-lang=&quot;ruby&quot;&gt;&lt;span class=&quot;n&quot;&gt;curl&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;--&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;unix&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;-&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;socket&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;sr&quot;&gt;/home/&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;deploy&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;/&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;appname&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;/&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;shared&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;/&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;tmp&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;/&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;sockets&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;/&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;appname&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;sock&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;http&lt;/span&gt;&lt;span class=&quot;ss&quot;&gt;:/&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;/&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;localhost&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;/&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;my&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;-&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;application&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;-&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;route&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;-&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;goes&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;-&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;here&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;p&gt;All the usual features of cURL are available when the UNIX file is provided, such as making
POST/HEAD/OPTIONS/anything else requests, sending form data or files, and using cookies.&lt;/p&gt;

&lt;p&gt;I’ve found that this technique is very handy for diagnosing problems that lie between the web/proxy
server (usually Nginx), and the application itself.&lt;/p&gt;
</description>
        <pubDate>Thu, 13 Jun 2019 00:00:00 +1200</pubDate>
        <link>https://joshmcarthur.com/til/2019/06/13/curling-to-a-unix-socket.html</link>
        <guid isPermaLink="true">https://joshmcarthur.com/til/2019/06/13/curling-to-a-unix-socket.html</guid>
        
        <category>til</category>
        
        <category>technology</category>
        
        <category>server-admin</category>
        
        
        <category>TIL</category>
        
      </item>
    
      <item>
        <title>tmux system clipboard copy &amp; paste on Mac OS</title>
        <description>&lt;p&gt;I have recently begun using &lt;a href=&quot;https://en.wikipedia.org/wiki/Tmux&quot;&gt;tmux&lt;/a&gt; as part of my everyday
development process. It’s going great, but as somebody who uses a mixture of Mac OS applications
like VS Code as well as terminal applications like &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;git&lt;/code&gt;, &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;vim&lt;/code&gt;, &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;tail&lt;/code&gt;, &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;cat&lt;/code&gt;, etc., the lack of
system clipboard integration has really been hurting.&lt;/p&gt;

&lt;p&gt;I went out and did some research into how I can set tmux up to copy to the system clipboard, and
first found that support for piping copied content was added in Tmux 1.8 via the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;copy-pipe&lt;/code&gt;
command, and then found a &lt;a href=&quot;https://thoughtbot.com/blog/tmux-copy-paste-on-os-x-a-better-future&quot;&gt;blog post by
Thoughtbot&lt;/a&gt;  (of course!), that outlined how to set up
&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;copy-pipe&lt;/code&gt; to use the Mac OS clipboard shell integration command, &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;pbcopy&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Unfortunately, the Thoughtbot post was written in 2013, and since then, the syntax for defining
keybindings has changed somewhat - hence this post.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;This is the configuration that works for me on Tmux 2.8 that integrates Tmux copy operations with
the Mac OS system keyboard:&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;In &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;~/.tmux.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;# Vim mode
setw -g mode-keys vi

# Setup &apos;v&apos; to begin selection as in Vim
bind-key -T copy-mode-vi v send-keys -X begin-selection
bind-key -T copy-mode-vi y send-keys -X copy-pipe &quot;reattach-to-user-namespace pbcopy&quot;

# Update default binding of `Enter` to also use copy-pipe
unbind -T copy-mode-vi Enter
bind-key -T copy-mode-vi Enter send-keys -X copy-pipe &quot;reattach-to-user-namespace pbcopy&quot;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;The key changes are:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;-t vi-copy&lt;/code&gt; to &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;-T copy-mode-vi&lt;/code&gt;&lt;/li&gt;
  &lt;li&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;send-keys -X&lt;/code&gt; now needs to be added between the keyboard shortcut and the ‘command’.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;I am still trying to figure out how to integrate this copy operation with mouse support, however I
have verified that the vim keyboard shortcuts in tmux ‘visual mode’ now copy to the clipboard as I
expect.&lt;/p&gt;

</description>
        <pubDate>Tue, 04 Jun 2019 00:00:00 +1200</pubDate>
        <link>https://joshmcarthur.com/til/2019/06/04/tmux-system-clipboard-copy-paste-on-mac-os.html</link>
        <guid isPermaLink="true">https://joshmcarthur.com/til/2019/06/04/tmux-system-clipboard-copy-paste-on-mac-os.html</guid>
        
        <category>til</category>
        
        <category>technology</category>
        
        <category>shell</category>
        
        
        <category>TIL</category>
        
      </item>
    
      <item>
        <title>Pending block examples with RSpec</title>
        <description>&lt;p&gt;I have known about the different ways of &lt;a href=&quot;https://relishapp.com/rspec/rspec-core/v/2-0/docs/pending/pending-examples&quot;&gt;marking tests as pending&lt;/a&gt;
in RSpec for some time. When working on a feature for which there are already tests (e.g. cloning a
similar feature where shared examples or a common starting point for tests can be used), it can be
extremely convenient to temporarily mark as test as pending 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;describe&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;addition&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;adds 1 + 1&quot;&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;do&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;pending&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;#45: Implement addition&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;subject&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;mi&quot;&gt;2&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;Something that isn’t covered in the documentation is how to do this for several tests at once. You
&lt;em&gt;can&lt;/em&gt; accomplish this by changing the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;it&lt;/code&gt; methods to &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;xit&lt;/code&gt;, but this still requires each test to be
updated.&lt;/p&gt;

&lt;p&gt;While researching how to do this, I stumbled across an interesting
&lt;a href=&quot;https://github.com/rspec/rspec-core/issues/1208#issuecomment-30009148&quot;&gt;issue comment in
rspec-core&lt;/a&gt;, which mentioned
that:&lt;/p&gt;

&lt;blockquote&gt;
  &lt;p&gt;Currently pending ‘doc string’ is just an alias for it ‘doc string’, :pending =&amp;gt; true.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;This is interesting, because that means that &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;pending&lt;/code&gt; is implemented as an RSpec tag, and I know
that tags can be applied to more than just specific examples - they can also be applied to example
containers (&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;describe&lt;/code&gt;/&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;context&lt;/code&gt;/&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;feature&lt;/code&gt; etc)., and will merge their tags with each example.&lt;/p&gt;

&lt;p&gt;Sure enough, adding a &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;pending&lt;/code&gt; tag to an entire example block will cause all examples in that block
to be marked as pending. “Pending” means that the example will still be run, but the failure of the
example will not cause a test failure. What WILL cause a test failure is if the example runs and
&lt;em&gt;does not fail&lt;/em&gt; - this test fails because “pending” is meant to signal that the test SHOULD NOT pass&lt;/p&gt;
&lt;ul&gt;
  &lt;li&gt;if it does, this is an indicator that the pending tag can be removed from that example.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;If you would like to mark a block of tests as pending regardless of their underlying pass/fail
status, the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;skip&lt;/code&gt; tag can be used instead. This will not even attempt to run the examples,
resulting in a faster test run.&lt;/p&gt;

&lt;p&gt;Here’s a fuller example:&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;describe &quot;Arithmetic&quot; do
  it &quot;adds 1 + 1 and equals 3&quot; do
    expect(1 + 1).to eq 3
  end

  it &quot;adds 1 + 1 and equals 2&quot; do
    expect(1 + 1).to eq 2
  end
end
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;These examples can be marked as ‘skipped’ by adding the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;skip&lt;/code&gt; tag to the describe block:&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;describe &quot;Arithmetic&quot;, skip: true`
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Results in:&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;Arithmetic
  adds 1 + 1 and equals 3 (PENDING: No reason given)
  adds 1 + 1 and equals 2 (PENDING: No reason given)

Pending: (Failures listed here are expected and do not affect your suite&apos;s status)

  1) Arithmetic adds 1 + 1 and equals 3
     # No reason given
     # ./rspec.rb:9

  2) Arithmetic adds 1 + 1 and equals 2
     # No reason given
     # ./rspec.rb:12


Finished in 0.00226 seconds (files took 0.44679 seconds to load)
2 examples, 0 failures, 2 pending
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;These examples can also be marked as ‘pending’ by adding the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;pending&lt;/code&gt; flag to the describe block:&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;describe &quot;Arithmetic&quot;, pending: true
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Results in:&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;Arithmetic
  adds 1 + 1 and equals 3 (PENDING: No reason given)
  adds 1 + 1 and equals 2 (FAILED - 1)

Pending: (Failures listed here are expected and do not affect your suite&apos;s status)

  1) Arithmetic adds 1 + 1 and equals 3
     # No reason given
     Failure/Error: expect(1 + 1).to eq 3

       expected: 3
            got: 2

       (compared using ==)
     # ./rspec.rb:10:in `block (2 levels) in &amp;lt;top (required)&amp;gt;&apos;

Failures:

  1) Arithmetic adds 1 + 1 and equals 2 FIXED
     Expected pending &apos;No reason given&apos; to fail. No error was raised.
     # ./rspec.rb:12

Finished in 0.03692 seconds (files took 0.32464 seconds to load)
2 examples, 1 failure, 1 pending

Failed examples:

rspec ./rspec.rb:12 # Arithmetic adds 1 + 1 and equals 2
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Note the failed example, because Rspec in this case expected the test to fail, but it actually passed.&lt;/p&gt;

&lt;p&gt;In this case, we can just mark the individual failing test as pending:&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;it &quot;adds 1 + 1 and equals 3&quot;, pending: true
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Results in:&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;Arithmetic
  adds 1 + 1 and equals 3 (PENDING: No reason given)
  adds 1 + 1 and equals 2

Pending: (Failures listed here are expected and do not affect your suite&apos;s status)

  1) Arithmetic adds 1 + 1 and equals 3
     # No reason given
     Failure/Error: expect(1 + 1).to eq 3

       expected: 3
            got: 2

       (compared using ==)
     # ./rspec.rb:10:in `block (2 levels) in &amp;lt;top (required)&amp;gt;&apos;

Finished in 0.0288 seconds (files took 0.40468 seconds to load)
2 examples, 0 failures, 1 pending
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Finally, instead of passing &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;true&lt;/code&gt; or &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;false&lt;/code&gt; to &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;skip&lt;/code&gt; or &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;pending&lt;/code&gt; tags, a string explaining the reason for skipping or pending can be provided:&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;describe &quot;Arithmetic&quot;, skip: &quot;Blocked by ticket #45&quot;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;or&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;describe &quot;Arithmetic&quot;, pending: &quot;Blocked by ticket #45&quot;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Results in:&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;Arithmetic
  adds 1 + 1 and equals 3 (PENDING: Blocked by ticket #45)
  adds 1 + 1 and equals 2 (PENDING: Blocked by ticket #45)
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

</description>
        <pubDate>Mon, 27 May 2019 00:00:00 +1200</pubDate>
        <link>https://joshmcarthur.com/til/2019/05/27/pending-block-examples-with-rspec.html</link>
        <guid isPermaLink="true">https://joshmcarthur.com/til/2019/05/27/pending-block-examples-with-rspec.html</guid>
        
        <category>til</category>
        
        <category>techology</category>
        
        <category>rspec</category>
        
        
        <category>TIL</category>
        
      </item>
    
      <item>
        <title>https://v.redd.it video URLs</title>
        <description>&lt;p&gt;A couple of years ago, Reddit added support for video and gif hosting - basically in response to
competition from Imgur and Giphy taking visitors offsite.&lt;/p&gt;

&lt;p&gt;This service works well, but they have been to some trouble to obfuscate things and make it a little
hard to get to the raw video file - I guess to prevent hotlinking to try and direct visitors towards
the actual post instead (with accompanying advertisements). Right-clicking to save a video from a
post page won’t work either, since the video file isn’t referenced directly. Instead, they seem to
be fetching the video and audio streams as a &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Blob&lt;/code&gt;, and using that as the video source. Sneaky!&lt;/p&gt;

&lt;p&gt;If you are after that perfect cat video for your permanent archive though, you probably ARE after
the video file. Getting it isn’t hard, once the technique is known.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;TL;DR - this post details how to get each individual part of a video. If you just want the video,
&lt;a href=&quot;https://ytdl-org.github.io/youtube-dl/index.html&quot;&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;youtube-dl&lt;/code&gt;&lt;/a&gt; will do the job nicely.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;The first thing to know is that the transcoding process that v.redd.it videos go through splits the
video from the audio (if any). If a video &lt;em&gt;does&lt;/em&gt; have audio, you will consistently find it at
&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;https://v.redd.it/{id}/audio&lt;/code&gt;. If a video does not have audio, you’ll get a 403 error page that
looks suspiciously like that of an S3 bucket.&lt;/p&gt;

&lt;p&gt;The second thing to know is the presence of a very handy file - the DASH playlist. This file can
consistently be found at &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;https://v.redd.it/{id}/DASHPlaylist.mpd&lt;/code&gt;. The playlist is an XML file
listing all the ‘representations’ of a video. A representation is either an audio or video feed, and
in this way can support different video resolutions and AV formats. As I mentioned above,
Reddit-transcoded videos consistently have zero or one audio feeds in mp4a format, and a range of
different resolution videos feeds - 240p, 360p, 720p, etc.&lt;/p&gt;

&lt;p&gt;This XML file contains a number of &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;RepresentationElement&lt;/code&gt; tags, which each of these containing a
&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;BaseURL&lt;/code&gt;. The content of the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;BaseURL&lt;/code&gt; tag can be appended to the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;https://v.redd.it/{id}&lt;/code&gt; URL to
actually load the video stream, for example:&lt;/p&gt;

&lt;p&gt;From &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;https://v.redd.it/xcre0omf86j21/DASHPlaylist.mpd&lt;/code&gt;, we see that there are 5 video streams
available, and 1 audio stream. By looking out at the BaseURL tag (or automating this with something
like
&lt;a href=&quot;https://www.joshmcarthur.com/til/2018/06/19/extracting-xml-data-with-curl-and-xmllint.html&quot;&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;xmllint&lt;/code&gt;&lt;/a&gt;,
we derive the following video URLs:&lt;/p&gt;

&lt;ol&gt;
  &lt;li&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;https://v.redd.it/xcre0omf86j21/DASH_720&lt;/code&gt;&lt;/li&gt;
  &lt;li&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;https://v.redd.it/xcre0omf86j21/DASH_1080&lt;/code&gt;&lt;/li&gt;
  &lt;li&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;https://v.redd.it/xcre0omf86j21/DASH_480&lt;/code&gt;&lt;/li&gt;
  &lt;li&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;https://v.redd.it/xcre0omf86j21/DASH_360&lt;/code&gt;&lt;/li&gt;
  &lt;li&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;https://v.redd.it/xcre0omf86j21/DASH_240&lt;/code&gt;&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;And the audio URL:&lt;/p&gt;

&lt;ol&gt;
  &lt;li&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;https://v.redd.it/xcre0omf86j21/audio&lt;/code&gt;&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;And that’s all there is to it! As I said at the start of this post, if you’re just after a video,
&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;youtube-dl&lt;/code&gt; is the better solution. It’ll do all this fetching and parsing for you, and will even
select the highest-resolution video and re-mux the audio track back into the MP4 file.&lt;/p&gt;
</description>
        <pubDate>Mon, 20 May 2019 00:00:00 +1200</pubDate>
        <link>https://joshmcarthur.com/til/2019/05/20/httpsvreddit-video-urls.html</link>
        <guid isPermaLink="true">https://joshmcarthur.com/til/2019/05/20/httpsvreddit-video-urls.html</guid>
        
        <category>til</category>
        
        <category>technology</category>
        
        
        <category>TIL</category>
        
      </item>
    
      <item>
        <title>Prevent unnecessary slowdown in IRB sessions with slow method calls</title>
        <description>&lt;p&gt;One of the convenient things that any interactive Ruby sesion (IRB) does for you is to &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;inspect&lt;/code&gt;
the return value of the statement it has just evaluated.&lt;/p&gt;

&lt;p&gt;This is normally great, but if you have something that takes a LONG time to evaluate and you don’t
actually care about the result, it can cause the session to block completely unnecessarily.&lt;/p&gt;

&lt;p&gt;The best example of this is loading an &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;ActiveRecord::Relation&lt;/code&gt;. If you’ve worked with ActiveRecord
before, you’ll know that queries are lazily-executed - the actual SQL statement is not prepared and
executed until a method is called that requires the results.&lt;/p&gt;

&lt;p&gt;The gotcha here is that &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;inspect&lt;/code&gt;-ing an &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;ActiveRecord::Relation&lt;/code&gt; by default tries to echo out the
models or other values you have requested - even if you yourself haven’t actually requested them.
This can cause some pretty big delays in IRB or a Rails console if you have a large dataset your
relation is querying.&lt;/p&gt;

&lt;p&gt;To prevent this, if you need to grab a relation object (or anything else that takes a short amount
of time to build, but a long amount of time to actually load), just assign the relation to a
variable, and add on &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;; true&lt;/code&gt; to the end of your statement. When IRB evaluates this line, it will
assign the relation object to the variable, but will not need to &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;inspect&lt;/code&gt; it, because it’s not the
final result of the statement - &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;true&lt;/code&gt; is - and calling &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;inspect&lt;/code&gt; on a boolean just stringifies it,
which takes no time at all.&lt;/p&gt;

&lt;p&gt;Here’s an example. Let’s say we have billions of &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Widget&lt;/code&gt; rows in our database. Fetching all of them
takes a terrible worst case scenario of 2 minutes.&lt;/p&gt;

&lt;p&gt;Why run this:&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;&amp;gt; Widget.where.not(id: nil)
=&amp;gt; [&amp;lt;Widget #1&amp;gt;, &amp;lt;Widget #2&amp;gt;...]
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;in 2 minutes, when you can run:&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;&amp;gt; relation = Widget.where.not(id: nil); true
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;in no time at all!&lt;/p&gt;

</description>
        <pubDate>Tue, 14 May 2019 00:00:00 +1200</pubDate>
        <link>https://joshmcarthur.com/til/2019/05/14/prevent-unnecessary-slowdown-in-irb-sessions-with-slow-method-calls.html</link>
        <guid isPermaLink="true">https://joshmcarthur.com/til/2019/05/14/prevent-unnecessary-slowdown-in-irb-sessions-with-slow-method-calls.html</guid>
        
        <category>til</category>
        
        <category>technology</category>
        
        <category>rails</category>
        
        
        <category>TIL</category>
        
      </item>
    
      <item>
        <title>Trip Report: Days Bay Main Ridge, Wellington</title>
        <description>&lt;p&gt;&lt;strong&gt;Start time:&lt;/strong&gt; 11:20am from Days Bay Pavillion&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Finish time:&lt;/strong&gt; ~ 2:30pm at Cheviot Bay Road + 15 minutes to cycle back to Days Bay&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Time:&lt;/strong&gt; approximately 3 hours with a few breathers and a 15 minute stop for lunch&lt;/p&gt;

&lt;hr /&gt;

&lt;p&gt;An unexpected change in our weekend plans last weekend (mostly a sense of laziness that persisted
all through Saturday) let us to try out a new area of the East Harbour Regional Park that we have
not yet been into.&lt;/p&gt;

&lt;p&gt;Between Point Arthur (where the Pencarrow Head trail starts), and the Wainuiomata Hill Road lookout
runs the creatively named “Main Ridge”. This ridge sits between the East Harbour bays and
Wainuiomata/Coast Road, and consists of a mixture of mature beech forest and regenerating
manuka/native scrub.&lt;/p&gt;

&lt;p&gt;Since this route is not a loop, we began by dropping a bike at &lt;a href=&quot;https://goo.gl/maps/TNNUpY5gwNH2&quot;&gt;Cheviot
Road&lt;/a&gt;, inland from Point Howard. This would allow one of us to
cycle to the start point at the end of our walk to collect the car. After dropping the bike, we
continued around the bays to the Days Bay Pavillion, where we parked the car in the unrestricted car
park spaces along the beachfront (the Pavillion carpark is also free to use, but has a 4 hour
restriction).&lt;/p&gt;

&lt;p&gt;The Kereru Track begins behind and to the right of the duck pond that is next to the Pavillion,
following a small stream gently up the hill, with a residential street on the other side. The
Korimako Track also leaves from Days Bay and can be joined to from the start of the Kereru Track,
but is a rougher track and joins the ridge further along.&lt;/p&gt;

&lt;p&gt;The gentle gradient of the track steepens after passing a small dam after around 10 minutes, before
heading into more established beech forest and moving into some zig-zags up a spur.&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/img/posts/days-bay/spur.jpg&quot; alt=&quot;Zig-zags at the start of the spur&quot; /&gt;&lt;/p&gt;

&lt;p&gt;A further 10-20 minutes clambering up zigzags and steps will have you reaching a memorial bench with
a great view over the harbour. This is a great spot for a sit down and breather. Past the bench,
continue up the spur, which after the initial climb moves into more typical top-of-spur climing -
steep, short climbs with gradual climbing in between.&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/img/posts/days-bay/top-of-spur.jpg&quot; alt=&quot;Near the top of the ridge&quot; /&gt;&lt;/p&gt;

&lt;p&gt;The spur meets the ridge after another 20 minutes or so after the bench,  with a sign pointing the
way to the right along the ridge towards Eastbourne. Head left, which is sign posted as 15 minutes
to the mtop of the Korimako Track, and 45 minutes to the Ferry Road Track.&lt;/p&gt;

&lt;p&gt;The ridge travel is typical for the area - generally pretty gentle with a short climb and descent
every couple of hundred metres. I did manage to roll my ankle on one of these descents just for some
variety, but it was minor and after stretching it out we carried on.&lt;/p&gt;

&lt;p&gt;The Korimako and Ferry Road tracks are uneventful in their own right, but provide convenient
milestones to measure progress against the map. We found that we were sticking to a pace that was
around 2/3 of the signposted time, which is normal for us on Greater Wellington Regional Council
tracks.&lt;/p&gt;

&lt;p&gt;After the Ferry Road turnoff, the track turns to follow the ridge NNE with a couple more steep
climbs to meet the main Lowry intersection. At 373m, Mt Lowry isn’t a huge hill to climb, however
coming all the way up from sea level, it feels like quite an accomplishment. The trig itself isn’t
at the intersection - it looks like it is around 10 minutes down the Rata Ridge Track. We didn’t
bother finding the trig since we had already got to the viewpoint.&lt;/p&gt;

&lt;p&gt;From here on in, the track is shared use for mountain bikers and walkers. Mountain bikers can come
up the Rata Ridge Track, and head down the Main Ridge Track to meet up with the mountain bike park
on the Wainui hill road. We saw several cyclists climbing their way up the hill, but no one coming
down. I’m not sure if this is the predominant direction or not.&lt;/p&gt;

&lt;p&gt;The main ridge track continues along towards Lowry Bay, now with the edge of Wainuiomata quite close
on the right. There was a motorcross track which you might hear close by, however the track is
smooth and well-formed with shade provided by beech trees and manuka. If you are heading towards
Cheviot Road like we were, you continue along the ridge for what feels like ages, getting closer and
closer to the lookout (the road noise can be heard with the hill road running downhill below the
ridgeline). About 10 minutes before the lookout, a slightly wider firebreak is met, with tracks down
to Point Howard and Cheviot Road dropping off to the left. Follow this track, crossing the stile,
and take the extra 50 metres to go and check out the lookout point over the Hutt Valley.&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/img/posts/days-bay/hutt-valley-lookout.jpg&quot; alt=&quot;Hutt Valley Lookout&quot; /&gt;&lt;/p&gt;

&lt;p&gt;The track from here drops quite steeply down into a stream valley, so back into Nikau-style bush and
shade. The track is gravelled so is a good surface but might be a little slippery underfoot. The
track descends to come close to a stream running down the valley and turns to the left to gradually
descent the valley wall, meeting the stream 5 minutes before the road end.&lt;/p&gt;

&lt;p&gt;If you’ve dropped a bike here, the cycle back around to Days Bay is just under 5 kilometers and is
flat and smooth. There is shared footpath some of the way, but on the wrong side of the road (if you
are heading towards Days Bay), so expect to do a bit of crossing from side to side.&lt;/p&gt;

&lt;p&gt;Overall, this walk, like most in the East Harbour forest is nicer than might be expected considering
its proximity to the industrial part of the Hutt Valley and Wellington City itself. It is easy to
get to, being accessible by ferry from Wellington, bike, or car, and can fits nicely into a morning
or afternoon.&lt;/p&gt;

&lt;iframe width=&quot;100%&quot; height=&quot;480&quot; frameborder=&quot;0&quot; scrolling=&quot;no&quot; marginheight=&quot;0&quot; marginwidth=&quot;0&quot; src=&quot;http://www.topomap.co.nz/NZTopoMapEmbedded?v=2&amp;amp;ll=-41.271546,174.915247&amp;amp;z=15&quot;&gt;&lt;/iframe&gt;
&lt;p&gt;&lt;br /&gt;&lt;small&gt;&lt;a href=&quot;http://www.topomap.co.nz/NZTopoMap?v=2&amp;amp;ll=-41.271546,174.915247&amp;amp;z=15&quot; style=&quot;text-align:left&quot;&gt;View Larger Topographic Map&lt;/a&gt;&lt;/small&gt;&lt;/p&gt;
</description>
        <pubDate>Tue, 26 Mar 2019 00:00:00 +1300</pubDate>
        <link>https://joshmcarthur.com/tramping/2019/03/26/trip-report-days-bay-main-ridge-wellington.html</link>
        <guid isPermaLink="true">https://joshmcarthur.com/tramping/2019/03/26/trip-report-days-bay-main-ridge-wellington.html</guid>
        
        <category>tramping</category>
        
        <category>short-walks</category>
        
        
        <category>tramping</category>
        
      </item>
    
      <item>
        <title>Entering a REPL from a PDB Python debugger</title>
        <description>&lt;p&gt;Something I have grown accustomed to when debugging Ruby code is the ability to jump into an &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;irb&lt;/code&gt;
session right from a breakpoint. With &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;byebug&lt;/code&gt; this pretty much happens immediately. With &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;pry&lt;/code&gt;,
running &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;irb&lt;/code&gt; will have the desired effect.&lt;/p&gt;

&lt;p&gt;When I started getting to the point where I needed to debug Python code I was writing, I almost
immediately had to find out how to do the same thing with &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;pdb&lt;/code&gt; - I wasn’t getting anywhere with
&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;print()&lt;/code&gt; and the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;p&lt;/code&gt;, &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;pp&lt;/code&gt; commands built into &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;pdb&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;It turns out that the Python debugging module (&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;pdb&lt;/code&gt;) does have a built in command to drop to a repl
with the context of the breakpoint - it’s just a slightly differerent terminology to Ruby.&lt;/p&gt;

&lt;p&gt;To drop to a REPL from a PDB breakpoint, the &lt;strong&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;interact&lt;/code&gt;&lt;/strong&gt; command is what you’re after.&lt;/p&gt;

&lt;div class=&quot;language-python highlighter-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;hello_world&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;():&lt;/span&gt;
  &lt;span class=&quot;n&quot;&gt;name&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;you&quot;&lt;/span&gt;
  &lt;span class=&quot;n&quot;&gt;pdb&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;set_trace&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;
  &lt;span class=&quot;k&quot;&gt;print&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;sa&quot;&gt;f&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&apos;Hey &lt;/span&gt;&lt;span class=&quot;si&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;si&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&apos;&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;img src=&quot;http://g.recordit.co/UswyzFbQSX.gif&quot; alt=&quot;Screen recording demo&quot; /&gt;&lt;/p&gt;

</description>
        <pubDate>Fri, 22 Mar 2019 00:00:00 +1300</pubDate>
        <link>https://joshmcarthur.com/til/2019/03/22/entering-a-repl-from-a-pdb-python-debugger.html</link>
        <guid isPermaLink="true">https://joshmcarthur.com/til/2019/03/22/entering-a-repl-from-a-pdb-python-debugger.html</guid>
        
        <category>til</category>
        
        <category>technology</category>
        
        <category>python</category>
        
        
        <category>TIL</category>
        
      </item>
    
      <item>
        <title>Mocking Python requests library</title>
        <description>&lt;p&gt;One of the toughest part of writing tests is how to go about handling dependencies on 
external dependencies. Aside from databases, probably the most common type of external dependency is
a network request - usually HTTP (or HTTPS).&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;http://docs.python-requests.org/en/master/&quot;&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;requests&lt;/code&gt;&lt;/a&gt; is a very, very popular library for
performing HTTP requests in Python. It’s popular because of its easy to use API and simple approach
to performing requests that can otherwise prove to be quite complex with lots of edge cases. The
example from the homepage says it all really:&lt;/p&gt;

&lt;div class=&quot;language-python highlighter-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;&amp;gt;&amp;gt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;r&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;requests&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;get&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&apos;https://api.github.com/user&apos;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;auth&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;s&quot;&gt;&apos;user&apos;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&apos;pass&apos;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;))&lt;/span&gt;
&lt;span class=&quot;o&quot;&gt;&amp;gt;&amp;gt;&amp;gt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;r&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;status_code&lt;/span&gt;
&lt;span class=&quot;mi&quot;&gt;200&lt;/span&gt;
&lt;span class=&quot;o&quot;&gt;&amp;gt;&amp;gt;&amp;gt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;r&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;headers&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&apos;content-type&apos;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt;
&lt;span class=&quot;s&quot;&gt;&apos;application/json; charset=utf8&apos;&lt;/span&gt;
&lt;span class=&quot;o&quot;&gt;&amp;gt;&amp;gt;&amp;gt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;r&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;encoding&lt;/span&gt;
&lt;span class=&quot;s&quot;&gt;&apos;utf-8&apos;&lt;/span&gt;
&lt;span class=&quot;o&quot;&gt;&amp;gt;&amp;gt;&amp;gt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;r&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;text&lt;/span&gt;
&lt;span class=&quot;sa&quot;&gt;u&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&apos;{&quot;type&quot;:&quot;User&quot;...&apos;&lt;/span&gt;
&lt;span class=&quot;o&quot;&gt;&amp;gt;&amp;gt;&amp;gt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;r&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;json&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;sa&quot;&gt;u&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&apos;private_gists&apos;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;419&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;sa&quot;&gt;u&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&apos;total_private_repos&apos;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;77&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;So, writing the code to perform an HTTP request is nice and simple. How about testing? Well, it
really depends on your approach.&lt;/p&gt;

&lt;p&gt;One option is to just let the requests happen - maybe to a testing
or staging environment. This is obviously the easiest, but also requires such an environment to
exist, to be accessible from anywhere, and requires the requests to consistently return the same
results with no side effects.&lt;/p&gt;

&lt;p&gt;Another option is to create a fake endpoint that you hit instead. In
Ruby, I have done this with &lt;a href=&quot;http://sinatrarb.com/&quot;&gt;sinatra&lt;/a&gt; - and I guess in Python you would use
something like &lt;a href=&quot;http://flask.pocoo.org/&quot;&gt;flask&lt;/a&gt;. This can work well if the requests and responses
are quite complex (maybe lots of specific params, headers and bodies), but also introduces a server
taht needs to be maintained and kept up to date. There is a certain time penalty just to create such
a server as well, especially how to fake side effects. It’s easier than it sounds to accidentally
end up re-inventing the system that was being faked out in the first place!&lt;/p&gt;

&lt;p&gt;Finally, there is mocking. Mocking involves intercepting calls at the code layer, rather than the
network layer. This is useful because you don’t have the overhead of interacting with an external
service, but still have full control over the result - for example, you can mock out your network
request to see how your code reacts to all sorts of edge cases - network errors, partial responses,
SSL errors - the list goes on. There is what seems to be a popular library available specifically
for mocking calls to the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;requests&lt;/code&gt; library called, appropriately,
&lt;a href=&quot;https://github.com/getsentry/responses&quot;&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;responses&lt;/code&gt;&lt;/a&gt;. I would recommend this library if you’re
after wide-ranged or complex HTTP mocking. The remainder of this post, I’m going to be talking about
mocking HTTP requests using plain ol’ &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;@unittest.mock&lt;/code&gt; methods that are built right into Python.
This is really only suitable for simple requests, or a contained set of more complex requests, but
is a lot easier to reason with and maintain, and saves you adding a dependency to your application.&lt;/p&gt;

&lt;p&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;unittest.mock&lt;/code&gt; allows you to replace objects and classes that your code under test relies on. You
can assert that methods are called with arguments, add side effects to methods being called, and
change the value that a method will return. If you go back and read about some of the different
things that can go wrong with HTTP requests, you can maybe envisage how some of these mocking
capabilities can be used to test how your code reacts to some of these scenarios.&lt;/p&gt;

&lt;p&gt;The easiest way to mock out requests is to use the
&lt;a href=&quot;https://docs.python.org/3/library/unittest.mock.html#unittest.mock.patch&quot;&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;@patch&lt;/code&gt;&lt;/a&gt; decorator. This
decorator accepts the module and method to mock, and will build the mock for you, passing it as an
argument into your test.&lt;/p&gt;

&lt;p&gt;Heres an example:&lt;/p&gt;

&lt;div class=&quot;language-python highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;kn&quot;&gt;from&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;unittest.mock&lt;/span&gt; &lt;span class=&quot;kn&quot;&gt;import&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;patch&lt;/span&gt;
&lt;span class=&quot;kn&quot;&gt;from&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;unittest&lt;/span&gt; &lt;span class=&quot;kn&quot;&gt;import&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;TestCase&lt;/span&gt;

&lt;span class=&quot;k&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;ApiClientTest&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;TestCase&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;patch&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;requests.get&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
  &lt;span class=&quot;k&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;test_standard_request&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;bp&quot;&gt;self&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;requests_get&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;):&lt;/span&gt;
    &lt;span class=&quot;c1&quot;&gt;# ... test goes here
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;With the mock available, you can change whatever you like:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;You can change the status code return value: &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;requests_get.status_code.return_value = 200&lt;/code&gt;&lt;/li&gt;
  &lt;li&gt;You can introduce a side effect: &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;requests_get.side_effect = Exception(&quot;Request failure!&quot;)&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;As well as changing the behaviour of the mocked code, you can also add assertions that a method has
been called the way you expect. Using the example posted at the top of this post, this assertion
could be something like:&lt;/p&gt;

&lt;div class=&quot;language-python highlighter-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;ApiClientTest&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;TestCase&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;patch&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;requests.get&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
  &lt;span class=&quot;k&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;test_request&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;bp&quot;&gt;self&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;requests_get&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;):&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;requests_get&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;assert_called_with&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&apos;https://api.github.com/user&apos;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;auth&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;s&quot;&gt;&apos;user&apos;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&apos;pass&apos;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;))&lt;/span&gt;
    
    &lt;span class=&quot;c1&quot;&gt;# Call your code which performs a request here, e.g.:
&lt;/span&gt;    &lt;span class=&quot;n&quot;&gt;requests&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;get&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&apos;https://api.github.com/user&apos;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;auth&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;s&quot;&gt;&apos;user&apos;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&apos;pass&apos;&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;Quite often, after making a request, your code may have a dependency on the &lt;em&gt;response&lt;/em&gt;. This isn’t a
problem at all, but you might want to make a fully-fledged &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;requests.Response&lt;/code&gt; class, like so:&lt;/p&gt;

&lt;div class=&quot;language-python highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;kn&quot;&gt;from&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;requests&lt;/span&gt; &lt;span class=&quot;kn&quot;&gt;import&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Response&lt;/span&gt;
&lt;span class=&quot;kn&quot;&gt;from&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;requests.exceptions&lt;/span&gt; &lt;span class=&quot;kn&quot;&gt;import&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;HTTPError&lt;/span&gt;
&lt;span class=&quot;kn&quot;&gt;from&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;unittest.mocks&lt;/span&gt; &lt;span class=&quot;kn&quot;&gt;import&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Mock&lt;/span&gt;

&lt;span class=&quot;k&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;ApiClientTest&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;TestCase&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;patch&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;requests.get&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
  &lt;span class=&quot;k&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;test_request&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;bp&quot;&gt;self&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;requests_get&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;):&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;successful_response&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Mock&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Response&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;successful_response&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;status_code&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;406&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;requests_get&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;return_value&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;successful_response&lt;/span&gt;

    &lt;span class=&quot;bp&quot;&gt;self&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;assertEqual&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;requests&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;get&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;/&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;).&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;status_code&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;406&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;patch&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;requests.get&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
  &lt;span class=&quot;k&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;test_failed_request&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;bp&quot;&gt;self&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;requests_get&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;):&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;failed_response&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Mock&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Response&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;failed_response&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;raise_for_status&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;side_effect&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;HTTPError&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;requests_get&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;return_value&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;failed_response&lt;/span&gt;

    &lt;span class=&quot;bp&quot;&gt;self&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;assertRaises&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;HTTPError&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;requests&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;get&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&apos;/&apos;&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;These are simplistic examples. Obviously with full acccess to the response, your options are fairly
unlimited.&lt;/p&gt;

&lt;p&gt;I have used this approach a number of times now and it has worked very well for me. Without adding
complexity or an additional dependency, I am able to test both success and failure scenarios of
making HTTP requests without adding too much magic or indirection. It’s quite easy to see how the
requests are being mocked, and the return values and side effects that we can expect from these
requests.&lt;/p&gt;
</description>
        <pubDate>Wed, 20 Mar 2019 00:00:00 +1300</pubDate>
        <link>https://joshmcarthur.com/til/2019/03/20/mocking-python-requests-library.html</link>
        <guid isPermaLink="true">https://joshmcarthur.com/til/2019/03/20/mocking-python-requests-library.html</guid>
        
        <category>til</category>
        
        <category>technology</category>
        
        <category>python</category>
        
        
        <category>TIL</category>
        
      </item>
    
      <item>
        <title>Aliasing Django model property</title>
        <description>&lt;p&gt;Not a super common occurrence, but exposing a Django model property under an alternative name is
occasionally useful. I have used it before to assist in polymorphism, where several very similar
models needed to have the same attribute.&lt;/p&gt;

&lt;p&gt;Given a Django model like:&lt;/p&gt;

&lt;div class=&quot;language-python highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;kn&quot;&gt;from&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;django.db&lt;/span&gt; &lt;span class=&quot;kn&quot;&gt;import&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;models&lt;/span&gt;

&lt;span class=&quot;k&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;Widget&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;models&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Model&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;):&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;email&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;models&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;CharField&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;max_length&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;254&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 naievy try and alias the attribute by defining an accessor for it:&lt;/p&gt;

&lt;div class=&quot;language-python highlighter-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;# ...
&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;Widget&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;models&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Model&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;username&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;bp&quot;&gt;self&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;bp&quot;&gt;self&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;email&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;This appears to work - &lt;em&gt;except&lt;/em&gt; when we go to use it, we find that &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Widget().username&lt;/code&gt; isn’t a
property - it’s a callable method. This means that we can’t use it as an alias, since &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;username&lt;/code&gt;
would need to be accessed using &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;username()&lt;/code&gt;, while &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;email&lt;/code&gt; would be immediately accessible as
&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;email&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Fortunately, Python has a method decorator avilable called &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;@property&lt;/code&gt;. This decorator is a way of
signalling to the class that a particular method can be used to get or set an attribute on the
class. We can use the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;@property&lt;/code&gt; decorator in our &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Widget&lt;/code&gt; class to alias our attribute nicely now:&lt;/p&gt;

&lt;div class=&quot;language-python highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;kn&quot;&gt;from&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;django.db&lt;/span&gt; &lt;span class=&quot;kn&quot;&gt;import&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;models&lt;/span&gt;

&lt;span class=&quot;k&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;Widget&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;models&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Model&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;):&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;email&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;models&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;CharField&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;max_length&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;254&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;property&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;username&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;bp&quot;&gt;self&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;bp&quot;&gt;self&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;email&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;And we can access the email using either &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;widget.email&lt;/code&gt; OR &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;widget.username&lt;/code&gt;. Nice!&lt;/p&gt;

&lt;p&gt;If you wish this alias to be bound in both directions (getting the attribute as well as setting the
value), this is also possilble, you just need to define an additional method:&lt;/p&gt;

&lt;div class=&quot;language-python highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;
&lt;span class=&quot;kn&quot;&gt;from&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;djang.db&lt;/span&gt; &lt;span class=&quot;kn&quot;&gt;import&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;models&lt;/span&gt;

&lt;span class=&quot;k&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;Widget&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;models&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Model&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;):&lt;/span&gt;
  &lt;span class=&quot;n&quot;&gt;email&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;models&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;CharField&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;max_length&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;254&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;property&lt;/span&gt;
  &lt;span class=&quot;k&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;username&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;bp&quot;&gt;self&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;bp&quot;&gt;self&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;email&lt;/span&gt;

  &lt;span class=&quot;o&quot;&gt;@&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;username&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;setter&lt;/span&gt;
  &lt;span class=&quot;k&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;set_username&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;bp&quot;&gt;self&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;username&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;):&lt;/span&gt;
     &lt;span class=&quot;bp&quot;&gt;self&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;email&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;username&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

</description>
        <pubDate>Wed, 20 Mar 2019 00:00:00 +1300</pubDate>
        <link>https://joshmcarthur.com/til/2019/03/20/aliasing-django-model-property.html</link>
        <guid isPermaLink="true">https://joshmcarthur.com/til/2019/03/20/aliasing-django-model-property.html</guid>
        
        <category>til</category>
        
        <category>technology</category>
        
        <category>python</category>
        
        
        <category>TIL</category>
        
      </item>
    
      <item>
        <title>create-react-app Gitlab Page template</title>
        <description>&lt;p&gt;I regularly complete little side projects in React - normally convenience shortcuts or data visualisation projects.
My preferrred deployment platform for such static sites is Gitlab Pages, as it allows for flexible builds, and I can use custom SSL certificates so that I don’t need to terminate my SSL with Cloudflare.&lt;/p&gt;

&lt;p&gt;Here is the template I use to build my React apps for Gitlab:&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;image&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;node:lts&lt;/span&gt;

&lt;span class=&quot;na&quot;&gt;pages&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;
  &lt;span class=&quot;na&quot;&gt;stage&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;deploy&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;s&quot;&gt;npm install -g yarn&lt;/span&gt;
  &lt;span class=&quot;pi&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;yarn install&lt;/span&gt;
  &lt;span class=&quot;na&quot;&gt;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;s&quot;&gt;yarn build&lt;/span&gt;
  &lt;span class=&quot;pi&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;mv public public.old&lt;/span&gt;
  &lt;span class=&quot;pi&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;mv build public&lt;/span&gt;
  &lt;span class=&quot;na&quot;&gt;artifacts&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;
    &lt;span class=&quot;na&quot;&gt;paths&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;public&lt;/span&gt;
  &lt;span class=&quot;na&quot;&gt;only&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;master&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;It:&lt;/p&gt;

&lt;ol&gt;
  &lt;li&gt;Uses the latest LTS node image&lt;/li&gt;
  &lt;li&gt;Installs yarn, and then crete-react-app’s dependencies&lt;/li&gt;
  &lt;li&gt;Builds a production release of the create-react-app application (&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;yarn build&lt;/code&gt;)&lt;/li&gt;
  &lt;li&gt;Copies the old public folder out of the way, and then copies the ‘build’ folder produced by the create-react-app build step to ‘public’. This is necessary because Gitlab pages will only serve files from the ‘public’ folder.&lt;/li&gt;
  &lt;li&gt;Restrict this deployment to run only on pushes to the master branch. This lets me work on features, merge requests, and bugfixes in the relevant branches before merging to &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;master&lt;/code&gt; to release.&lt;/li&gt;
&lt;/ol&gt;
</description>
        <pubDate>Fri, 08 Mar 2019 00:00:00 +1300</pubDate>
        <link>https://joshmcarthur.com/til/2019/03/08/gitlab-pages-react-app-template.html</link>
        <guid isPermaLink="true">https://joshmcarthur.com/til/2019/03/08/gitlab-pages-react-app-template.html</guid>
        
        <category>til</category>
        
        <category>technology</category>
        
        
        <category>TIL</category>
        
      </item>
    
      <item>
        <title>GWRC Matangi Train Numbering System</title>
        <description>&lt;blockquote&gt;
  &lt;p&gt;Note: This numbering system is known as “TMS” (even though TMS stands for “Traffic Management System”) 
of which the numbering system is just a small, bespoke part.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;I travel on the Greater Wellington &lt;a href=&quot;https://www.metlink.org.nz&quot;&gt;Metlink&lt;/a&gt; transit system nearly every
weekday, and am often curious about what train I happen to be travelling on. I was recently checking
out the &lt;a href=&quot;https://en.wikipedia.org/wiki/New_Zealand_FP_class_electric_multiple_unit#Naming_and_classification&quot;&gt;Wikipedia
page&lt;/a&gt;
for the FP ‘Matangi’ units introduced in 2010, in particular how the numebering system worked. I had
always just assumed that the numbers following the classification ‘FP’ and ‘FT’ were somehow
sequential, so my curiousity was piqued to investigate the system further.&lt;/p&gt;

&lt;p&gt;To break down the number of a particular unit, it needs to be divided into 4 parts:&lt;/p&gt;

&lt;h4 id=&quot;1-classification&quot;&gt;1. Classification&lt;/h4&gt;

&lt;p&gt;This is a 2 or 3 character sequence. For the Matangi units, this is either ‘FT’ or ‘FP’ depending on
whether the car is powered or not (only a single car in the set is powered - the other is a
‘trailer’).&lt;/p&gt;

&lt;h4 id=&quot;2-series&quot;&gt;2. Series&lt;/h4&gt;

&lt;p&gt;The first number in the sequence following the classification is used to identify the series - 1000
series, 2000 series and so on.&lt;/p&gt;

&lt;p&gt;The initial series of the Matangi production units is 4000. A follow up 5000 series was introduced
in 2015-2016, with a few minor improvements such as LED lighting (being retrofitted to the 4000
series).&lt;/p&gt;

&lt;h4 id=&quot;3-train-number&quot;&gt;3. Train Number&lt;/h4&gt;

&lt;p&gt;The series and following two numbers makes up the train number - this is what would be typically
used to identity a single unit. According to the Series, the train number for Matangi units
consistently starts with either ‘4’ or ‘5’ (which is how the two generations may be differentiated).&lt;/p&gt;

&lt;h4 id=&quot;4-check-digit&quot;&gt;4. Check Digit&lt;/h4&gt;

&lt;p&gt;The final number is a check digit, used to verify the classification and train number. The check
digit was certainly the hardest to research, being referred to in nearly all places as just that -
“check digit”. I was fortunate to find the &lt;a href=&quot;https://nzrailphotos.co.nz/utilities/#tms-calc&quot;&gt;nzrailphotos.co.nz TMS Calculator
Utility&lt;/a&gt;, which I could take a peek at the code for
to see how it was calculated.&lt;/p&gt;

&lt;p&gt;The check digit is calculated as follows:&lt;/p&gt;

&lt;ol&gt;
  &lt;li&gt;Letters are converted to a numeric format. This means converting them to their character codes
(According to ASCII, A-Z is 65-90), and then subtracting 64 to zero-index them. To ensure the
placement is correct, they are added to the list of numbers to sum zero-padded - e.g. ‘8’ would
be added as ‘0’, ‘8’, and ‘20’ would be added as ‘2’, ‘0’.&lt;/li&gt;
  &lt;li&gt;Numbers that already fall between 0 and 9 (48-57 in ASCII) have 48 subtracted to zero-index them,
and are then added directly to the list to sum. Zero-padding isn’t a concern, since the number is
always between 0 and 9.&lt;/li&gt;
  &lt;li&gt;The ‘sum’ of the numbers is now calculated. Each number in the list above is squared, and then
multiplied by it’s position in the list (e.g. a list containing 0, 6, 0, 2 would work out being
`(0 * 0 + 36 * 1 + 0 * 2 + 4 * 3) = 48.&lt;/li&gt;
  &lt;li&gt;The remainder of the sum divided by 11 is calculated, and is ready to use as the check digit.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;The reason this calculation is so complex is because the check digit ensures that the numbers and
classification is correct, and also in the correct order. A train number with a different
classification or number will not have the same check digit, and a train number in an incorrect
format (too long/short), will also not have the same check digit.&lt;/p&gt;

&lt;p&gt;Since the explanation above is quite wordy, here are some examples:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;FT4247&lt;/code&gt;. Train number is &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;FT424&lt;/code&gt;, sum list is &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;0, 6, 2, 0, 0, 4, 2, 4&lt;/code&gt;, sum is 260, check digit
is 7. Number verifies.&lt;/li&gt;
  &lt;li&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;FP4247&lt;/code&gt;. Train number is &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;FP424&lt;/code&gt;, sum list is `0, 6, 1, 6, 4, 2, 4, sum is 326, check digit is
    &lt;ol&gt;
      &lt;li&gt;Number verifies.&lt;/li&gt;
    &lt;/ol&gt;
  &lt;/li&gt;
  &lt;li&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;FP5148&lt;/code&gt;. Train number is &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;FP514&lt;/code&gt;, sum list is &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;0, 6, 1, 6, 0, 5, 1, 4&lt;/code&gt;, sum is 389, check digit
is 8. Number verifies.&lt;/li&gt;
&lt;/ul&gt;

</description>
        <pubDate>Thu, 28 Feb 2019 00:00:00 +1300</pubDate>
        <link>https://joshmcarthur.com/til/2019/02/28/gwrc-matangi-train-numbering-system.html</link>
        <guid isPermaLink="true">https://joshmcarthur.com/til/2019/02/28/gwrc-matangi-train-numbering-system.html</guid>
        
        <category>til</category>
        
        <category>transit</category>
        
        
        <category>TIL</category>
        
      </item>
    
      <item>
        <title>Bash/Zsh shell expansion</title>
        <description>&lt;p&gt;Shell expansion is an incredibly handy shortcut. Previously to today, I’ve only ever used glob
(&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;*&lt;/code&gt;), which will expand to include all directories and files within the glob scope (e.g. &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;*&lt;/code&gt; would
list all files and directories in the current directory, &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;test/*&lt;/code&gt; would list all files and
directories in &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;test/&lt;/code&gt;).&lt;/p&gt;

&lt;p&gt;There are more though! Today I’m covering &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;{ range }&lt;/code&gt; and &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;{ list }&lt;/code&gt;, which are convient ways to
expand a known range or list of values. In most cases, it can be used to avoid a for loop, the exact
syntax for which I &lt;em&gt;always&lt;/em&gt; have to look up.&lt;/p&gt;

&lt;h4 id=&quot;range-expansion&quot;&gt;Range expansion&lt;/h4&gt;

&lt;p&gt;&lt;strong&gt;Syntax:&lt;/strong&gt; &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;{start value...end value}&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Example:&lt;/strong&gt; &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;{0..5}&lt;/code&gt;, &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;{5..10}&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;This expression expands numbers, and as far as I can tell, character ranges like &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;{a..z}&lt;/code&gt;. This is great
if you have directories that are incrementally numbered, and you want to target a subset of them.
This isn’t a magical directive that gets passed to shell scripts - it gets expanded before that and
passed to the script, which means that this expansion will work with &lt;em&gt;any&lt;/em&gt; script that accepts
multiple arguments - things like &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;echo&lt;/code&gt;, &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;cp&lt;/code&gt;, &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;mv&lt;/code&gt; and friends come to mind, but many scripts
accept a list of files to process somehow.&lt;/p&gt;

&lt;p&gt;Negative numbers do not appear to be supported, so the minimum start value is ‘0’ - it can of course
be a larger number than this. It’s worth pointing out that the range just passed each value to the
command - it won’t skip values that don’t map to a file or directory for example.&lt;/p&gt;

&lt;p&gt;Examples:&lt;/p&gt;

&lt;p&gt;Make 10 new directories:&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;&amp;gt; mkdir {0..10}
&amp;gt; ls
0	1	10	2	3	4	5	6	7	8	9
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Remove the first 5:&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;&amp;gt; rm {0..5}
&amp;gt; ls 
10	6	7	8	9
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Add 5 more, but start from 50:&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;&amp;gt; mkdir {50..55}
&amp;gt; ls
10	50	51	52	53	54	55	6	7	8	9
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h4 id=&quot;list-expansion&quot;&gt;List expansion&lt;/h4&gt;

&lt;p&gt;&lt;strong&gt;Syntax:&lt;/strong&gt; &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;{first value, second value, ..., last value}&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Examples:&lt;/strong&gt; &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;{cats,dogs,birds,cows}&lt;/code&gt;, &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;{1,2,3,5,8,13}&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;This expression expands to each value in the list. In other words, it’s not necessarily as handy as
the range expression for passing directly to a command, BUT it can be used as part of a wider
filename.&lt;/p&gt;

&lt;p&gt;As an example, let’s say that we have a directory structure like:&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;   |-livestock
   |---cows
   |---deer
   |---sheep
   |-pets
   |---birds
   |---cats
   |---dogs
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;You can use list expansion as part of a file path, for example to list all the animals:&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;&amp;gt; ls {livestock,pets}

livestock:
cows	deer	sheep

pets:
birds	cats	dogs

&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;To list all the animals whose name starts with ‘c’:&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;&amp;gt; ls {livestock,pets}/c*

livestock/cows:

pets/cats:
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

</description>
        <pubDate>Wed, 27 Feb 2019 00:00:00 +1300</pubDate>
        <link>https://joshmcarthur.com/til/2019/02/27/bashzsh-shell-expansion.html</link>
        <guid isPermaLink="true">https://joshmcarthur.com/til/2019/02/27/bashzsh-shell-expansion.html</guid>
        
        <category>til</category>
        
        <category>technology</category>
        
        <category>shell</category>
        
        
        <category>TIL</category>
        
      </item>
    
      <item>
        <title>Paste mode in VIM</title>
        <description>&lt;p&gt;A VIM mode I did not know about for quite some time during my VIM usage is “paste” mode. This mode
doesn’t do much, but is invaluable if you are moving code around, copy and pasting between files
outside vim.&lt;/p&gt;

&lt;p&gt;Paste mode can be activated using &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;set :paste&lt;/code&gt; and deactivated with &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;set :nopaste&lt;/code&gt;. This mode by
default simply deactivates autoindentation, which means that the existing format of the text being
pasted will be respected.&lt;/p&gt;

&lt;p&gt;This is a very simple and handy trick to keep in mind, as it saves auto-indent picking up the wrong
spacing and incorrectly applying “fixes” which often need to be manually fixed up.&lt;/p&gt;
</description>
        <pubDate>Sun, 24 Feb 2019 00:00:00 +1300</pubDate>
        <link>https://joshmcarthur.com/til/2019/02/24/paste-mode-in-vim.html</link>
        <guid isPermaLink="true">https://joshmcarthur.com/til/2019/02/24/paste-mode-in-vim.html</guid>
        
        <category>til</category>
        
        <category>technology</category>
        
        <category>tooling</category>
        
        
        <category>TIL</category>
        
      </item>
    
      <item>
        <title>Splitting large CSV files with awk</title>
        <description>&lt;p&gt;I recently &lt;a href=&quot;https://unix.stackexchange.com/a/297684&quot;&gt;learned&lt;/a&gt; of a useful technique when processing
large text files that have a consistent separator using awk.&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;https://en.wikipedia.org/wiki/AWK&quot;&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;awk&lt;/code&gt;&lt;/a&gt; is a text processing programming language dating all the
way back from 1977. While the format string passed to &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;awk&lt;/code&gt; technically represents a full
programming language, it is most typically used directly from the command line or from shell
scripts.&lt;/p&gt;

&lt;p&gt;The command to split a file is easily represented as:&lt;/p&gt;

&lt;div class=&quot;language-sh highlighter-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;awk&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;-F&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\,&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&apos;{print&amp;gt;$1}&apos;&lt;/span&gt; input.csv
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;This command contains a field separator specification (in this example it is comma, escaped just in
case - but it can be any character), and a very small Awk directive: &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;{print&amp;gt;$1}&lt;/code&gt;. This directive
takes the current line, and writes it to a file named after the first field. Unlike normal shell
programming, &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;&amp;gt;&lt;/code&gt; in &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;awk&lt;/code&gt; will &lt;em&gt;append&lt;/em&gt; to a file if it already exists, unlike the sh &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;&amp;gt;&lt;/code&gt; operator,
which typically overwrites the file. &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;$1&lt;/code&gt; simply represents the first column based on the field
separator. If you need to reference any other column, you can simply use an incremented placeholder -  &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;$2&lt;/code&gt;, &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;$3&lt;/code&gt; and so on.&lt;/p&gt;

</description>
        <pubDate>Tue, 19 Feb 2019 00:00:00 +1300</pubDate>
        <link>https://joshmcarthur.com/til/2019/02/19/splitting-large-csv-files-with-awk.html</link>
        <guid isPermaLink="true">https://joshmcarthur.com/til/2019/02/19/splitting-large-csv-files-with-awk.html</guid>
        
        <category>til</category>
        
        <category>technology</category>
        
        <category>shell</category>
        
        
        <category>TIL</category>
        
      </item>
    
      <item>
        <title>Relatively elegant ActiveRecord create callbacks</title>
        <description>&lt;p&gt;ActiveRecord callbacks are pretty popular in the Rails community, but they do have pitfalls.
Probably the most significant one is that the responsibilities of a model can creep quite a lot once
callbacks begin getting defined. Callbacks can also be hard to test, since they are triggered under
a range of conditions, and have multiple phases per condition (before/around/after initialize,
validate, create, etc).&lt;/p&gt;

&lt;p&gt;There is an interesting, little-known ability to pass a block to the
&lt;a href=&quot;https://apidock.com/rails/ActiveRecord/Persistence/ClassMethods/create&quot;&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;create&lt;/code&gt;&lt;/a&gt; method of an
ActiveRecord model. This can be an elegant way of passing a callback method, from something like a
service or form object to allow a model to set attributes on itself immediately before saving.&lt;/p&gt;

&lt;p&gt;Here’s an 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;c1&quot;&gt;# app/services/ticket_creator.rb&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;TicketCreator&lt;/span&gt;
  &lt;span class=&quot;k&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;create&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;summary&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:)&lt;/span&gt;
    &lt;span class=&quot;no&quot;&gt;Ticket&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;create!&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;ss&quot;&gt;summary: &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;summary&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;:assign_reference&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;In this example, our ticket creator service passes the attributes to &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;create&lt;/code&gt; as usual, but also
passes a proc reference to the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;assign_reference&lt;/code&gt; method of the model - in other words, this is a
shorthand, which could also be written as:&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;Ticket&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;create&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;attributes&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;ticket&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;|&lt;/span&gt;
  &lt;span class=&quot;n&quot;&gt;ticket&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;assign_reference&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;In this example, we would expect &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;assign_reference&lt;/code&gt; to be implemented inside the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Ticket&lt;/code&gt; model 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;c1&quot;&gt;# app/models/ticket.rb&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;Ticket&lt;/span&gt;
  &lt;span class=&quot;k&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;assign_reference&lt;/span&gt;
    &lt;span class=&quot;no&quot;&gt;TicketReference&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;generate!&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;Personally, I find this approach to be a lot cleaner than a normal callback, since the invocation of
the callback method isn’t linked directly into the model. It is invoked &lt;em&gt;on&lt;/em&gt; the model, but not &lt;em&gt;by&lt;/em&gt;
the model. In this case, it is called by a service object, but could just as easily be invoked from
a controller, mailer, or any other class really. It is also relatively easy to test, since we can
test the result of the service method being called, rather than the core &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;create&lt;/code&gt; method.&lt;/p&gt;

</description>
        <pubDate>Wed, 13 Feb 2019 00:00:00 +1300</pubDate>
        <link>https://joshmcarthur.com/til/2019/02/13/relatively-elegant-activerecord-create-callbacks.html</link>
        <guid isPermaLink="true">https://joshmcarthur.com/til/2019/02/13/relatively-elegant-activerecord-create-callbacks.html</guid>
        
        <category>til</category>
        
        <category>technology</category>
        
        <category>rails</category>
        
        
        <category>TIL</category>
        
      </item>
    
      <item>
        <title>Javascript based file downloads in IE9</title>
        <description>&lt;p&gt;Getting feature parity on Internet Explorer is never very fun, or easy. I recently worked on a
project where we needed to support inline downloads - specifically, a generated CSV string, as a 
file in all greenfields browsers as well as IE9-11.&lt;/p&gt;

&lt;p&gt;Fortunately, IE 10 and 11 support the &lt;a href=&quot;https://developer.mozilla.org/en/docs/Web/API/Blob&quot;&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Blob&lt;/code&gt; API&lt;/a&gt;, 
which make downloads pretty simple using the &lt;a href=&quot;https://developer.mozilla.org/en-US/docs/Web/API/Navigator/msSaveBlob&quot;&gt;‘msSaveBlob` non-standard
API&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;IE9 has no such support though - either for Blob building, or for saving said blobs. Fortunately,
there is a convenient workaround, using simple document body manipulation and the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;SaveAs&lt;/code&gt;
execCommand that can be sent to a frame.&lt;/p&gt;

&lt;p&gt;Here’s the snippet:&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;function&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;legacyBrowserSave&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;content&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;span class=&quot;kd&quot;&gt;const&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;frame&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;iframe&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;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;frame&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;

  &lt;span class=&quot;nx&quot;&gt;frame&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;contentWindow&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;open&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;text/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;dl&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;replace&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;frame&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;contentWindow&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;write&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;content&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
  &lt;span class=&quot;nx&quot;&gt;frame&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;contentWindow&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;close&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;();&lt;/span&gt;

  &lt;span class=&quot;nx&quot;&gt;frame&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;contentWindow&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;focus&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;();&lt;/span&gt;
  &lt;span class=&quot;nx&quot;&gt;frame&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;contentWindow&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;execCommand&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;SaveAs&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;kc&quot;&gt;true&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;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;removeChild&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;frame&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;kc&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;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;This snippet should be wrapped in a browser or feature detection script, since saving a blob is
supported in nearly every other browser.&lt;/p&gt;

&lt;p&gt;Just to step through this solution within the function body, line by line:&lt;/p&gt;

&lt;p&gt;On line 1, we create a new &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;&amp;lt;iframe&amp;gt;&lt;/code&gt; element.&lt;/p&gt;

&lt;p&gt;On line 2, we attach this new element to the document body. The iframe will not display as it has no
width or height.&lt;/p&gt;

&lt;p&gt;On line 4, we &lt;a href=&quot;https://developer.mozilla.org/en-US/docs/Web/API/Document/open&quot;&gt;“open”&lt;/a&gt; the document.
The second argument to this function, &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;&quot;replace&quot;&lt;/code&gt;, indicates that a new history item should not be
added (this isn’t an actual navigation after all).&lt;/p&gt;

&lt;p&gt;On line 5, we write the file content into the iframe, and then close the document on line 6. (This
&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;open&lt;/code&gt;, &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;write&lt;/code&gt;, &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;close&lt;/code&gt; operation is equivalent to doing &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;document.write&lt;/code&gt; except it resets the
existing content.)&lt;/p&gt;

&lt;p&gt;On line 8, we focus the window within the frame. This is necessary for the next operation on line 9,
when we pass an
&lt;a href=&quot;https://developer.mozilla.org/en-US/docs/Web/API/Document/execCommand&quot;&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;execCommand&lt;/code&gt;&lt;/a&gt; to the
frame’s window, with the argument of “SaveAs”. (&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;execCommand&lt;/code&gt; options are not standardized across
browsers. In this case, &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;SaveAs&lt;/code&gt; is specific to Internet Explorer). The &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;true&lt;/code&gt; argument provided
here is to indicate whether the default UI should be shown or not and has no effect - it’s just a
required arg so that we can pass the filename as the third argument.&lt;/p&gt;

&lt;p&gt;On line 10, we remove the frame from the document.body, since the user has now been presented with a
dialog to save the file, and the frame is no longer required.&lt;/p&gt;

&lt;p&gt;The &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;return false&lt;/code&gt; is just to prevent the event from bubbling, since we have handled it.&lt;/p&gt;

&lt;p&gt;Here’s a demo of this in action:&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;http://g.recordit.co/ja8gh29HcN.gif&quot; alt=&quot;IE9 download demo&quot; /&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;https://gist.github.com/joshmcarthur/114357808ff67a8729f3857ac18d3e6d&quot;&gt;Source Gist&lt;/a&gt;&lt;/p&gt;

</description>
        <pubDate>Wed, 13 Feb 2019 00:00:00 +1300</pubDate>
        <link>https://joshmcarthur.com/til/2019/02/13/javascript-based-file-downloads-in-ie9.html</link>
        <guid isPermaLink="true">https://joshmcarthur.com/til/2019/02/13/javascript-based-file-downloads-in-ie9.html</guid>
        
        <category>til</category>
        
        <category>technology</category>
        
        <category>javascript</category>
        
        
        <category>TIL</category>
        
      </item>
    
      <item>
        <title>Trip Report: East Holdsworth Loop</title>
        <description>&lt;p&gt;This trip report covers a fantastic day loop that I found to be a little under-documented when
researching - especially the East Holdsworth track, which has become one of my favourite Tararua
tracks.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Started:&lt;/strong&gt; 9:15am from Holdsworth Carpark&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Finished:&lt;/strong&gt; 4:50pm at Holdsworth Carpark&lt;/p&gt;

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

&lt;ul&gt;
  &lt;li&gt;1hr up Atiwhakatu Track&lt;/li&gt;
  &lt;li&gt;2.75hrs up East Holdsworth Track&lt;/li&gt;
  &lt;li&gt;20min to Mount Holdsworth Summit&lt;/li&gt;
  &lt;li&gt;30min from Summit to Powell Hut&lt;/li&gt;
  &lt;li&gt;2hrs from Powell Hut to Holdsworth Carpark&lt;/li&gt;
&lt;/ul&gt;

&lt;hr /&gt;

&lt;p&gt;We made a prompt, but not too early start from Wellington, heading over the Remutaka Hill Road and
straight to Holdsworth - not even a coffee stop. We arrived at the carpark, which was significantly
less busy than we had expected given the weather forecast, which was for “Fine, Light Winds” - the
Tararua Unicorn weather.&lt;/p&gt;

&lt;p&gt;We had organised our daypacks, lunches and snacks the night before, so we were able to get away
about quarter past 9, stopping briefly at Holdsworth Lodge to fill in our intentions. We crossed the
Atiwhakatu Stream and headed up towards the main intersection at the start of the Gentle Annie
track, taking our usual 10-15 minutes along the flat, wide track to get warmed up. We carried on
straight at the intersection, continuing through &lt;a href=&quot;https://www.joshmcarthur.com/tramping/2018/03/07/donnelly-flat-name-origin.html&quot;&gt;Donnelly
Flat&lt;/a&gt; and up the
Atiwhakatu Valley.&lt;/p&gt;

&lt;p&gt;The Atiwhakatu Track is popular with dog walkers and mountain runners as well as trampers, and we
met several people going both ways along this track. The track is well graded and maintained, and we
were able to stick to our normal pace for this track to meet the swingbridge back over the Atiwhakatu Stream after one hour. A brief wobble over the bridge with the dog (who can’t walk across due to the mesh and needs to be carried), and we reached the intersection with the East Holdsworth Track 2 minutes after the swingbridge.&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/img/posts/east-holdsworth/intersection-bottom.jpg&quot; alt=&quot;East Holdsworth Intersection&quot; /&gt;&lt;/p&gt;

&lt;p&gt;The East Holdsworth track is one we’ve passed several times on our way up or down the valley. We
had been looking for a good weather window to do a loop up this track and back down Holdsworth for
some time, as we were interested to see what kind of climb needs to be done to reach the
Holdsworth-Jumbo ridgeline in just a few hours.&lt;/p&gt;

&lt;p&gt;The East Holdsworth track &lt;a href=&quot;http://www.topomap.co.nz/NZTopoMap/trck129324/East-Holdsworth-Track/Wellington&quot; target=&quot;_blank&quot;&gt;[topomap]&lt;/a&gt; starts off surprisingly, suspiciously easy, travelling along flat terraces
with short sharp climbs between each level for about 45 minutes. Towards the end of this time, there is less and
less flat terrace, and more and more uphill, as the track steepens to climb up towards the bushline.
This section takes some time - around 45 minutes to an hour, as it climbs most steeply between 650m
and 1000m. After this, there is a slightly less steep section for around 30 minutes before reaching
the bushline at 1200m.&lt;/p&gt;

&lt;p&gt;We found that some careful monitoring of rest stops and breaks was needed - despite the relatively brief 
time period the main climb covers, hourly rest stops weren’t sufficient, and we made an 
early stop for lunch beneath the bushline at around 1100m. Next time we do this track, I would be
inclined to break the uphill climb into shorter sections, as I think we probably allowed ourselves
to use up more energy in the steepest part of the climb, which made things harder for us nearer to
the bushline.&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/img/posts/east-holdsworth/bushline.jpg&quot; alt=&quot;The Bushline Triangle&quot; /&gt;&lt;/p&gt;

&lt;p&gt;On a clear day, at the bushline you will be rewarded with sweeping views over the Wairarapa Plains,
along with views looking North towards Jumbo and Mitre. We found the remaining climb up through the
Tussock to the main Holdsworth-Jumbo ridgeline a lot easier, with a northwester coming down off the
ridge to cool us off and a better and better view of Holdsworth and Jumbo the further up we got. In
fact, with the wind being a little stronger than forecast, we stopped at a rocky platform around
1250m to put on thermal tops, just to keep the wind off.&lt;/p&gt;

&lt;p&gt;From the bushline to the ridge took us around 30 minutes, with more of a terraced climb up to the
Holdsworth-Jumbo circuit - gentle slopes followed by short/sharp climbs up the rocky tussock-covered
ridge. There’s a DoC sign at the intersection showing the way to Holdsworth/Powell, Jumbo, and back the 
way you came. It’s worth keeping in mind the possibility of using this track as an emergency exit
from the main circuit, as the sign at the intersection indicates that the return trip to the carpark
takes just 2.5 hours (personally I would expect that to take longer to get down the steep track, but
perhaps that time is intended to be a little lower than it actually is to persuade people who are on
the fence to follow the quickest route off the exposed ridge).&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/img/posts/east-holdsworth/top-intersection.jpg&quot; alt=&quot;East Holdsworth Top Intersection&quot; /&gt;&lt;/p&gt;

&lt;p&gt;Once the main track is reached, the going becomes easier - both due to being on top of the hill, and
because there is less tussock cover at the top, with most of the walking being along rock and
gravel. There are a couple of small knobs to sidle around along the ridge before the final climb up
to the trig at the top of Holdsworth. We found we made good progress in this section, reaching the
trig 20 minutes after leaving the East Holdsworth intersection.&lt;/p&gt;

&lt;p&gt;We stopped for 10 minutes or so at the trig for a quick peek around with the binoculars, spotting
the aircraft wreckage of an RNZAF De Havilland Devon that crashed in 1955, and was controversially
&lt;a href=&quot;http://www.stuff.co.nz/national/2032069/Historic-wreckage-removed-illegally&quot;&gt;stolen&lt;/a&gt; in 2009 and
&lt;a href=&quot;http://www.stuff.co.nz/dominion-post/news/3228249/Plane-parts-returned-to-Tararua-crash-site&quot;&gt;returned&lt;/a&gt;
in 2010. This was definitely the clearest day we’ve had at the summit of Holdsworth, and we could
see right out to Kapiti Island to the West, the Wairarapa Coastline to the East, and Hector,
Maungahuka, Aokaparangi and Kahiwiroa along the main range beside us. Directly ahead was Jumbo,
Angle Knob and Shingle Slip Knob, with McGregor and the Broken Axe Pinnacles in the background.
Fantastic views.&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/img/posts/east-holdsworth/holdsworth-views.jpg&quot; alt=&quot;Holdsworth views&quot; /&gt;&lt;/p&gt;

&lt;p&gt;From Holdsworth, we followed the ridgeline south towards the construction site at Powell Hut. The
rebuild of this hut is expected to completed at the end of April 2019, and it looks like they are
making good progress on the main building, with roofing going on and a larger deck under
construction. While the construction is ongoing, there is NO shelter or access to the hut, and also
NO water supply or toilets available to trampers.&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/img/posts/east-holdsworth/powell-hut-site.jpg&quot; alt=&quot;Powell Hut Construction Site&quot; /&gt;&lt;/p&gt;

&lt;p&gt;There is a rough diversion “track” in place that bypasses the construction site. This “track” is
temporary, and as such as fairly rooty and rough. There’s mud underfoot which makes for a bit of a
slip and slide, with the diversion taking around 10 minutes and emerging about 10 meters below the
altitude of the hut at the base of the final set of stairs. We heard a number of helicopters (or
maybe the same helicopter multiple times?) through the day in the direction of Powell, so suspect
they were also making the most of the nice weather.&lt;/p&gt;

&lt;p&gt;From here, we were following the Gentle Annie track that we’ve done several times before, and by
this point were mostly on autopilot and feeling pretty tired. We had a short break at Mountain House
to remove a layer (thermals) that were no longer needed, and refill our waterbottles that we had
been rationing since Mt Holdsworth. After Mountain House, it was a quick scramble back down the
track, reaching the main intersection with the Atiwhakatu Track around 4:30pm, and arriving back at
the car at 4:50pm.&lt;/p&gt;

&lt;p&gt;Overall, this loop did take a little longer than expected, but for a day trip that covers the range
of forest and terrain this particular loop does, I believe it’ unparalleled, in the Tararua Forest
Park at least. A large contributor to the variety on display on this loop is the East Holdsworth
Track. While we found it steep, it had a great variety of terrain, views and ecosystems on the climb
from river level to the tops. We were even fortunate enough to see a Kaka flying overhead and hear
another, most likely due to the extensive trapping program going on around Donnely Flat. This track
has definitely gone onto our favourites list, and can only assume that we struggled to find detailed trip 
reports covering this track previously due to the authors’ wish to keep it quiet for themselves!&lt;/p&gt;
</description>
        <pubDate>Sun, 10 Feb 2019 00:00:00 +1300</pubDate>
        <link>https://joshmcarthur.com/tramping/2019/02/10/trip-report-east-holdsworth-loop.html</link>
        <guid isPermaLink="true">https://joshmcarthur.com/tramping/2019/02/10/trip-report-east-holdsworth-loop.html</guid>
        
        <category>tramping</category>
        
        <category>trip-reports</category>
        
        
        <category>Tramping</category>
        
      </item>
    
      <item>
        <title>Internationalised ActionMailer Subjects</title>
        <description>&lt;p&gt;I’m a big fan of keeping strings out of my application code. It’s a pattern I first came across with
Android &lt;a href=&quot;https://developer.android.com/guide/topics/resources/string-resource&quot;&gt;string resources&lt;/a&gt;,
and since then I’ve been using tools just like the &lt;a href=&quot;https://github.com/ruby-i18n/i18n&quot;&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;i18n&lt;/code&gt;
gem&lt;/a&gt; to abstract content away from code.&lt;/p&gt;

&lt;p&gt;Something mentioned directly in the &lt;a href=&quot;https://guides.rubyonrails.org/i18n.html&quot;&gt;Rails I18n
guides&lt;/a&gt; that I really like to see used myself is the
built-in support for translating mailer method subjects. As the guides mention:&lt;/p&gt;

&lt;blockquote&gt;
  &lt;p&gt;If you don’t pass a subject to the mail method, Action Mailer will try to find it in your translations. 
The performed lookup will use the pattern &lt;mailer_scope&gt;.&lt;action_name&gt;.subject to construct the key.&lt;/action_name&gt;&lt;/mailer_scope&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;—  https://guides.rubyonrails.org/i18n.html#translations-for-action-mailer-e-mail-subjects&lt;/p&gt;

&lt;p&gt;This is a very handy trick, since email subjects are frequently the last remaining piece of content
that exists in mailers. Along with &lt;a href=&quot;https://www.joshmcarthur.com/til/2018/06/21/internationalised-views-with-rails.html&quot;&gt;translated
templates&lt;/a&gt;,
making mailers that support multiple languages (or even just the default locale language), is easy,
with all strings moved to I18n locale files and templates.&lt;/p&gt;

&lt;p&gt;By default, ActionMailer will use the mailer name and the action name as the I18n key if &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;mail&lt;/code&gt; is
called without a subject option.&lt;/p&gt;

&lt;p&gt;Here are some examples:&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;UsersMailer&lt;/span&gt;
  &lt;span class=&quot;k&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;password_reset&lt;/span&gt;
    &lt;span class=&quot;c1&quot;&gt;# Key is: users_mailer.password_reset.subject&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;mail&lt;/span&gt; &lt;span class=&quot;ss&quot;&gt;to: &lt;/span&gt;&lt;span class=&quot;vi&quot;&gt;@user&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;email&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;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;Admin::AlertsMailer&lt;/span&gt;
  &lt;span class=&quot;k&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;alert&lt;/span&gt;
    &lt;span class=&quot;c1&quot;&gt;# Key is admin/alerts_mailer.alert.subject&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;mail&lt;/span&gt; &lt;span class=&quot;ss&quot;&gt;to: &lt;/span&gt;&lt;span class=&quot;vi&quot;&gt;@administrator&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;email&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 would be the corresponding locale file for the above examples:&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;en&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;
  &lt;span class=&quot;na&quot;&gt;admin/alerts_mailer&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;
    &lt;span class=&quot;na&quot;&gt;alert&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;
      &lt;span class=&quot;na&quot;&gt;subject&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;New&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s&quot;&gt;Administrator&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s&quot;&gt;Alert&quot;&lt;/span&gt;
  &lt;span class=&quot;na&quot;&gt;users_mailer&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;
    &lt;span class=&quot;na&quot;&gt;password_reset&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;
      &lt;span class=&quot;na&quot;&gt;subject&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;New&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s&quot;&gt;Password&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s&quot;&gt;Reset&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s&quot;&gt;Request&quot;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;I feel that understanding I18n in Rails is a really undervalued skill. Frequently, understanding the
abstractions that I18n offer can lead to clean separation of content and logic, as well as inspiring
some abstractions in your own application. I look forward to continuing to learn more about what can
be done within the I18n framework.&lt;/p&gt;
</description>
        <pubDate>Mon, 04 Feb 2019 00:00:00 +1300</pubDate>
        <link>https://joshmcarthur.com/til/2019/02/04/internationalised-actionmailer-subjects.html</link>
        <guid isPermaLink="true">https://joshmcarthur.com/til/2019/02/04/internationalised-actionmailer-subjects.html</guid>
        
        <category>til</category>
        
        <category>technology</category>
        
        <category>rails</category>
        
        
        <category>TIL</category>
        
      </item>
    
      <item>
        <title>My understanding of CSS units</title>
        <description>&lt;p&gt;I was listening to &lt;a href=&quot;https://syntax.fm/show/107/hasty-treat-css-units&quot;&gt;Episode #107 of the Syntax Podcast&lt;/a&gt; this afternoon, which covered CSS units, and was inspired to actually stop and think about how I use units when writing CSS. I felt pretty reassured that generally my approach is in line with what Wes and Scott were discussing. Let’s go!&lt;/p&gt;

&lt;h3 id=&quot;rem&quot;&gt;rem&lt;/h3&gt;

&lt;p&gt;I use &lt;abbr title=&quot;Relative Em&quot;&gt;rem&lt;/abbr&gt; by default for most things. Usually this is margin, padding, and font sizing. rem sizing works well for me because I like to establish a baseline sizing at the document root. Usually this is 8px, but occasionally I’ll work with a 4px or 16px baseline. Once I’ve set this global base size, I can use rem to work in multiples of the baseline, keeping everything relative. This makes it simple to adjust the sizing later, since everything is relative to a common base value.&lt;/p&gt;

&lt;p&gt;There were a couple of things I learnt about rem from the podcast:&lt;/p&gt;

&lt;ol&gt;
  &lt;li&gt;A typical approach to sizing is to set the baseline to a percentage of the normal browser default font size of 16px. The normal number to see floating around the internet for this is 62.5%, which sets the baseline size to 10px. The reason a percentage is used is that this will scale up and down appropriately if the user has selected a larger or smaller font size.&lt;/li&gt;
  &lt;li&gt;It can be useful to set the baseline size to a base 10 value - for example, 10px (or the
percentage equivalent). The reason for this is is that it allows for easy conversion between
common pixel values to rem. For example, a 32px margin could be expressed as 3.2rem, while a font
size could be expressed as 1.6rem.&lt;/li&gt;
&lt;/ol&gt;

&lt;h3 id=&quot;pixels&quot;&gt;Pixels&lt;/h3&gt;

&lt;p&gt;I use pixels pretty much only for positioning (although an absolute positioning like this is always as a
last resort). I also tend to use pixels for defining a base border radius since, unless corners are
super rounded, the numbers involved tend to be small and difficult to reason with in rem. It was an
interesting observation about how pixels used to be &lt;em&gt;extremely&lt;/em&gt; widely used before they fell out of
favour - for no particular reason. Pixels continue to be used a lot in graphic design, and for a lot
of websites where relative baseline sizing is not required. For me, I find that using pixels for
measurements, even with aids such as variables, leads to brittle and hard to work with styles.&lt;/p&gt;

&lt;h3 id=&quot;other-units&quot;&gt;Other units&lt;/h3&gt;

&lt;p&gt;Just like Scott and Wes in the Syntax podcast, I don’t generally find a need for the many other
units available in CSS. I have always been curious to hear about use cases for slightly weird units
like centimeters and inches, and was interested to find that these units are generally useful for
print stylesheets - in particular specialist paper formats such as envelopes.&lt;/p&gt;

&lt;hr /&gt;

&lt;p&gt;Overall, I found listening to this particular episode gave me a great opportunity to reflect on the
opinions I hold regarding CSS units, and why I have decided on the approach I use.&lt;/p&gt;
</description>
        <pubDate>Sun, 03 Feb 2019 00:00:00 +1300</pubDate>
        <link>https://joshmcarthur.com/til/2019/02/03/my-understanding-of-css-units.html</link>
        <guid isPermaLink="true">https://joshmcarthur.com/til/2019/02/03/my-understanding-of-css-units.html</guid>
        
        <category>til</category>
        
        <category>css</category>
        
        
        <category>TIL</category>
        
      </item>
    
      <item>
        <title>Biking from Akatawara to Renata Hut (Attempt #2)</title>
        <description>&lt;p&gt;After &lt;a href=&quot;https://www.joshmcarthur.com/tramping/2017/07/25/akatarawa-summit-to-renata.html&quot;&gt;my failed 2017 attempt&lt;/a&gt; to reach 
&lt;a href=&quot;https://www.doc.govt.nz/parks-and-recreation/places-to-go/wellington-kapiti/places/tararua-forest-park/things-to-do/huts/renata-hut/&quot;&gt;Renata
Hut&lt;/a&gt;,
I finally mustered up the spare time and inclination to have another shot.&lt;/p&gt;

&lt;p&gt;The TL;DR of the last attempt is that due to significant tree fall on the track, it took me too long
to make it up to the Hut - I reached my cutoff and turned around.&lt;/p&gt;

&lt;p&gt;After that attempt, I reflected on how, because much of the track is four-wheel drive accesible, I
could possibly save some time by mountain biking as far up as I could before continuing up by foot.
That’s what I tried this time. It didn’t work out, which I’ll go into.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Start time:&lt;/strong&gt; 9:30am from Akatawara Hill Rd saddle&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Finish time:&lt;/strong&gt; ~ 3:30pm&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Time:&lt;/strong&gt; 6 hours - approximately 3 hours up, 1.5 hours down, 1.5 hour delay + breaks.&lt;/p&gt;

&lt;p&gt;I started from the saddle, which is just as dodgy looking as ever. Not-very-reassuringly, the
skeleton of a burnt out car which wasn’t there last time was right in the middle of the gravel patch
at the summit. I parked as out-of-sight as I could manage and got on my bike and set off up the
hill. I pretty quickly discovered that while the track is definitely bikable in terms of width, the
terrain and uphill slog was going to make things tough for me. This was 100% my bike fitness - I’m
pretty sure someone more capable on a bike would have no particular problems.&lt;/p&gt;

&lt;p&gt;I met a single tramper about 15 minutes up the hill, who was on his way out from Renata. It was good
to check in with someone, though he was pretty happy to find how close he was to getting out. The
track up is generally in good condition and has been cleared since I was last up. The 4WD track is
definitely 4WD - not just a gravel road, in places there are deep water-filled ditches to traverse,
and some impressively deep ruts.&lt;/p&gt;

&lt;p&gt;I found that in general I made good progress up the hill, although I had to jump off and walk a lot
more than I thought I would need to. This was mostly due to struggling to hold up enough momentum to
pick a stable path up through ruts, and on balance I found it was a lot less tiring to walk a few
meters and continue cycling rather than to battle up each tough bit. Another consideration if you’re
looking at cycling is also that the gravel is not that of a maintained road. It’s fine most of the
time, but there are frequently spots with larger lumps of rock - fist sized rocks took a lot more
effort to push over and tended to roll around a lot more.&lt;/p&gt;

&lt;p&gt;I was pleased to get to the large slip and washout where I turned around on my last attempt in
pretty good time, and as I carried on to Maymorn Junction, I could see I definitely made the right
call. While the slip isn’t THAT far from the Junction, it would have definitely pushed me off my
timing to carry on, let alone to go all the way to the Hut.  As you can see from the Topomap below,
I was about as far from the Junction as the Junction was from the Hut.&lt;/p&gt;

&lt;iframe width=&quot;640&quot; height=&quot;480&quot; frameborder=&quot;0&quot; scrolling=&quot;no&quot; marginheight=&quot;0&quot; marginwidth=&quot;0&quot; src=&quot;http://www.topomap.co.nz/NZTopoMapEmbedded?v=2&amp;amp;ll=-40.967164,175.147133&amp;amp;z=14&amp;amp;pin=1&quot;&gt;&lt;/iframe&gt;
&lt;p&gt;&lt;br /&gt;&lt;small&gt;&lt;a href=&quot;http://www.topomap.co.nz/NZTopoMap?v=2&amp;amp;ll=-40.967164,175.147133&amp;amp;z=14&amp;amp;pin=1&quot; style=&quot;text-align:left&quot;&gt;View Larger Topographic Map&lt;/a&gt;&lt;/small&gt;&lt;/p&gt;

&lt;p&gt;Anyway, I carried on. I was having no trouble with the track, which is 4WD track all the way to
Maymore Junction and on down to Waiotaru Hut. I reached a clearing just in time for lunch, and
stopped here with a great view of the main range.&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/img/posts/renata-2/junction.jpg&quot; alt=&quot;View of the main range&quot; /&gt;&lt;/p&gt;

&lt;p&gt;I was expecting to get to the junction about now, but figured that it must be coming up soon with
the road opening up. There was no signage, and I couldn’t see any triangles, so I carried on. A
short distance down the road, I saw some orange triangles, but there were on the wrong side to be to
Renata - I checked out the map, and noticed that there was a track coming in to Renata here. I
started to feel like I may have passed the Junction, but my GPS wasn’t putting me exactly where I
thought I was, and the topomap showed the tracks converging reasonably roughly, so I carried on. The
road starting heading downhill, and I heard some 4WD trucks coming up behind me. This put a little
pressure on since the road wasn’t so wide, and I wasn’t sure how fast they were coming up, so I
carried on further down the hill.&lt;/p&gt;

&lt;p&gt;By the time I figured out that I’d clearly missed the Junction, I was a decent distance down what I
now knew was the track to Waiotaru Hut.&lt;/p&gt;

&lt;iframe width=&quot;640&quot; height=&quot;480&quot; frameborder=&quot;0&quot; scrolling=&quot;no&quot; marginheight=&quot;0&quot; marginwidth=&quot;0&quot; src=&quot;http://www.topomap.co.nz/NZTopoMapEmbedded?v=2&amp;amp;ll=-40.9535,175.165765&amp;amp;z=15&amp;amp;pin=1&quot;&gt;&lt;/iframe&gt;
&lt;p&gt;&lt;br /&gt;&lt;small&gt;&lt;a href=&quot;http://www.topomap.co.nz/NZTopoMap?v=2&amp;amp;ll=-40.9535,175.165765&amp;amp;z=15&amp;amp;pin=1&quot; style=&quot;text-align:left&quot;&gt;View Larger Topographic Map&lt;/a&gt;&lt;/small&gt;&lt;/p&gt;

&lt;p&gt;I still wasn’t getting a good GPS fix, but determined where I was as I was beside a stream crossing.
I stopped here and let the group of three 4WD vehicles catch up and pass me before turning around to
head back up the hill. This took me around an hour to regain the junction. By this point, I was
feeling pretty short of energy, and hadn’t expected to have to get back to MORE uphill cycling
straight away, so I had to take it pretty slowly and walk a decent chunk of it. By the time I
reached the clearing where I stopped for lunch, and having not passed any other likely looking spots
I was more inclined to not carry on until I had confirmed where the track to Renata Hut actually was. 
By now, I was almost at my cutoff, both in terms of time and energy, and knew I wouldn’t be
able to make the Hut that day. After a bit of a hunt around, I found the stump of where a DoC
sign used to be. Just past that stump, I found an unmarked track heading up a small bank. At the
top of that bank, I could see an orange triangle about 50m away - solving the problem of the
missing track!&lt;/p&gt;

&lt;figure&gt;
  &lt;img alt=&quot;Renata Track&quot; src=&quot;/img/posts/renata-2/renata-track.jpg&quot; /&gt;
  &lt;figcaption&gt;Maymorn Junction with the track to Renata on the left beside the post.&lt;/figcaption&gt;
&lt;/figure&gt;

&lt;p&gt;Next time, I definitely know where the track is, and I also know that I can do a mixture of cycling
the easy bits and walking the hard bits to get up to the junction in good time and without using up
all my energy. It’s definitely one that I will try again, and hopefully it will be third time lucky.&lt;/p&gt;

&lt;p&gt;From the junction, I gratefully started downhill. The downhill trip was mostly quick, with some of
the undulations along the ridge providing some gradual uphill stretches to overcome. The rutted
parts of the track were a lot easier to roll down than struggle up, and even though I didn’t manage
to make it down without getting wet feet in the ditch crossings, by that stage I needed a cool down.&lt;/p&gt;

&lt;p&gt;Made it back to the car at around 3:30pm, and spent some time on the Summit stretching and resting
up, completely exhausted. Definitely the toughest cycle I’ve done in a day, but I really enjoy the
solitude and quietness of this part of the Tararua Forest Park.&lt;/p&gt;

</description>
        <pubDate>Sun, 03 Feb 2019 00:00:00 +1300</pubDate>
        <link>https://joshmcarthur.com/tramping/2019/02/03/biking-from-akatawara-to-renata-hut-attempt-2.html</link>
        <guid isPermaLink="true">https://joshmcarthur.com/tramping/2019/02/03/biking-from-akatawara-to-renata-hut-attempt-2.html</guid>
        
        <category>tramping</category>
        
        <category>outdoors</category>
        
        <category>trip-report</category>
        
        
        <category>tramping</category>
        
      </item>
    
      <item>
        <title>List filenames only with grep</title>
        <description>&lt;p&gt;Just a quick one - I use &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;grep&lt;/code&gt; all the time to find matches for text in files. Sometimes I’m not
really interested in exactly what matched, I just need a list of files that matched SOMEWHERE. This
morning I found that the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;-l&lt;/code&gt; option with this (or &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;--files-with-matches&lt;/code&gt;).&lt;/p&gt;

&lt;p&gt;From the man page:&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; -l, --files-with-matches
          Suppress  normal  output;  instead  print the name of each input
          file from which output would normally have  been  printed.   The
          scanning  will  stop  on  the  first match.  (-l is specified by
          POSIX.)
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Which turns the output from:&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;app/views/widgets/forms/_basics.html.haml:    = f.input :description, as: :text, input_html: { rows: 3, class: &quot;xxlarge tinymce&quot;, id: &quot;textarea-widget&quot; }
app/views/widgets/_edit_form_description.html.erb:&amp;lt;%= f.input :description, input_html: { class: &quot;tinymce xxlarge&quot; } %&amp;gt;
app/views/widgets/_edit_form_description.html.erb:&amp;lt;%= f.input :blurb, input_html: { class: &quot;tinymce xxlarge&quot; } %&amp;gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Into:&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;app/views/widgets/forms/_basics.html.haml
app/views/widgets/_edit_form_description.html.erb
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Handy!&lt;/p&gt;
</description>
        <pubDate>Sun, 09 Dec 2018 00:00:00 +1300</pubDate>
        <link>https://joshmcarthur.com/til/2018/12/09/list-filenames-only-with-grep.html</link>
        <guid isPermaLink="true">https://joshmcarthur.com/til/2018/12/09/list-filenames-only-with-grep.html</guid>
        
        <category>til</category>
        
        <category>technology</category>
        
        <category>shell</category>
        
        
        <category>TIL</category>
        
      </item>
    
      <item>
        <title>Ruby: Deserialize JSON to an OpenStruct</title>
        <description>&lt;p&gt;Deserializing JSON from API responses, files and the like is a pretty common task. Depending on the
complexity of the JSON structure, this can be a bit difficult to work with though, since the default
action of &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;JSON.parse&lt;/code&gt; is to deserialize to a string-key Hash. This means that after parsing the
JSON, there’s a lot of &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;fetch&lt;/code&gt;ing, &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;dig&lt;/code&gt;ing, and the like to get things working, and the code
doesn’t usually come out super readable.&lt;/p&gt;

&lt;p&gt;An alternative to this is based on an argument that I stumbled across this afternoon -
&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;object_class&lt;/code&gt;. This option &lt;em&gt;defaults&lt;/em&gt; to &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Hash&lt;/code&gt;, but can be set to anything you like. The best
example of this is what happens when &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;object_class&lt;/code&gt; is set to &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;OpenStruct&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;s1&quot;&gt;&apos;open-uri&apos;&lt;/span&gt;
&lt;span class=&quot;nb&quot;&gt;require&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&apos;openstruct&apos;&lt;/span&gt;
&lt;span class=&quot;nb&quot;&gt;require&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&apos;json&apos;&lt;/span&gt;

&lt;span class=&quot;n&quot;&gt;me&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;JSON&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;parse&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;open&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;https://api.github.com/users/joshmcarthur&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;).&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;read&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;ss&quot;&gt;object_class: &lt;/span&gt;&lt;span class=&quot;no&quot;&gt;OpenStruct&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;

&lt;span class=&quot;n&quot;&gt;me&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;login&lt;/span&gt;
&lt;span class=&quot;o&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;joshmcarthur&quot;&lt;/span&gt;

&lt;span class=&quot;n&quot;&gt;me&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;name&lt;/span&gt;
&lt;span class=&quot;o&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;Josh McArthur&quot;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;It even works with nested 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;repo_uri&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;https://api.github.com/repos/joshmcarthur/joshmcarthur.github.com&quot;&lt;/span&gt;

&lt;span class=&quot;n&quot;&gt;repo&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;JSON&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;parse&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;open&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;repo_uri&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;).&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;read&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;ss&quot;&gt;object_class: &lt;/span&gt;&lt;span class=&quot;no&quot;&gt;OpenStruct&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;

&lt;span class=&quot;n&quot;&gt;repo&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;owner&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;login&lt;/span&gt;
&lt;span class=&quot;o&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;joshmcarthur&quot;&lt;/span&gt;


&lt;span class=&quot;n&quot;&gt;repo&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;full_name&lt;/span&gt;
&lt;span class=&quot;o&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;joshmcarthur/joshmcarthur.github.com&quot;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;While the examples above are using &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;OpenStruct&lt;/code&gt;, it works equally well with a class of your own
creation - the only requirement is that your &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;initialize&lt;/code&gt; method accepts a Hash structure.&lt;/p&gt;

</description>
        <pubDate>Mon, 03 Dec 2018 00:00:00 +1300</pubDate>
        <link>https://joshmcarthur.com/til/2018/12/03/ruby-deserialize-json-to-an-openstruct.html</link>
        <guid isPermaLink="true">https://joshmcarthur.com/til/2018/12/03/ruby-deserialize-json-to-an-openstruct.html</guid>
        
        <category>til</category>
        
        <category>technology</category>
        
        <category>rails</category>
        
        
        <category>TIL</category>
        
      </item>
    
      <item>
        <title>Exercism Strain: Collaborative implementation and benchmarking</title>
        <description>&lt;p&gt;This post covers an incremental implementation of the Exercism &lt;a href=&quot;https://exercism.io/tracks/elixir/exercises/strain&quot;&gt;“strain”&lt;/a&gt; exercise
that I worked on collaboratively with one of my colleagues at &lt;a href=&quot;https://www.ackama.com&quot;&gt;Ackama&lt;/a&gt;,
Daniel Jauch.&lt;/p&gt;

&lt;p&gt;The Strain exercise brief is to implement &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;keep&lt;/code&gt; and &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;discard&lt;/code&gt; functions within an Elixir module
named &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Strain&lt;/code&gt;, without using the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Enum.filter&lt;/code&gt; module, for example:&lt;/p&gt;

&lt;div class=&quot;language-elixir highlighter-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;iex&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;gt;&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;Strain&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;keep&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;k&quot;&gt;fn&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;e&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;-&amp;gt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;e&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&amp;lt;&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;10&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;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;n&quot;&gt;iex&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;gt;&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;Strain&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;keep&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;k&quot;&gt;fn&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;e&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;-&amp;gt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;e&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&amp;lt;&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;3&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;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;n&quot;&gt;iex&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;gt;&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;Strain&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;discard&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;k&quot;&gt;fn&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;e&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;-&amp;gt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;e&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&amp;lt;&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;3&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;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;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h3 id=&quot;first-implementation-use-other-enum-methods-just-not-filter&quot;&gt;First implementation: Use other Enum methods, just not &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;filter&lt;/code&gt;&lt;/h3&gt;

&lt;p&gt;Our first stab at this was to assume that since we couldn’t use &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Enum/filter/2&lt;/code&gt;, there &lt;em&gt;were&lt;/em&gt; Enum
methods that could be used to get an equivalent result. Here is what we came up with:&lt;/p&gt;

&lt;div class=&quot;language-elixir highlighter-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;defmodule&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;Strain&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;do&lt;/span&gt;
  &lt;span class=&quot;k&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;keep_enum&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;list&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;fun&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;list&lt;/span&gt;
    &lt;span class=&quot;o&quot;&gt;|&amp;gt;&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;Enum&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;map&lt;/span&gt;&lt;span class=&quot;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;n&quot;&gt;fun&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;nv&quot;&gt;&amp;amp;1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;&amp;amp;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;no&quot;&gt;Enum&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;reject&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;n&quot;&gt;!&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;&amp;amp;1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;))&lt;/span&gt;
  &lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;This worked, with all tests passing, so we submitted that to Exercism. Yay, done!
But then we took a look at some of the community solutions, and realised that our initial foray into &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Enum&lt;/code&gt; was a
little misguided, since there was an Elixir feature we completely didn’t know about we could use instead.&lt;/p&gt;

&lt;h3 id=&quot;second-implementation-comprehensions&quot;&gt;Second implementation: Comprehensions&lt;/h3&gt;

&lt;p&gt;&lt;a href=&quot;https://elixir-lang.org/getting-started/comprehensions.html&quot;&gt;Elixir Comprehensions&lt;/a&gt; are a bit of
syntactic sugar over Enumerables that allow for some handy brevity when processing Enumerable data.
Comprehensions support a generator, and a filter. The generator is the first “argument” to the
comprehension and is required. The filter is an optional second argument, and can be used to exclude
non-matching values from the list. Is all this starting to sound exactly like how &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;keep&lt;/code&gt; and
&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;discard&lt;/code&gt; should work? We thought so as well!&lt;/p&gt;

&lt;div class=&quot;language-elixir highlighter-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;defmodule&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;Strain&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;do&lt;/span&gt;
  &lt;span class=&quot;k&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;keep&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;list&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;fun&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;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;for&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;x&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&amp;lt;-&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;list&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;fun&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;x&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;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;x&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;Much, much simpler, and even more readable in my opinion, once we understood what the two arguments
passed to the comprehension did. The implementation of &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;discard&lt;/code&gt; is exactly the same, just the
inverse result from the check function - &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;!fn.(x)&lt;/code&gt; instead of &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;fun.(x)&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;So, as an Exercism exercise, we were happy with this - from a 5 line function down to a single line.&lt;/p&gt;

&lt;p&gt;Then we started wondering - if &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Enum.filter&lt;/code&gt; is what we would normally use for this, and we have
come up with two different implementations of the exercise - what is the speed difference between
them? Only one way to find out!&lt;/p&gt;

&lt;h3 id=&quot;third-implementation-creating-a-mix-project&quot;&gt;Third implementation: Creating a mix project&lt;/h3&gt;

&lt;p&gt;To perform some benchmarking, we found a Hex package called
&lt;a href=&quot;https://hex.pm/packages/benchee&quot;&gt;benchee&lt;/a&gt; that looked like it would suit our needs nicely. Before
we could add this dependency though, we needed to package our current code into a Mix project!&lt;/p&gt;

&lt;p&gt;We started from the Exercism exercise folder:&lt;/p&gt;

&lt;div class=&quot;language-sh highlighter-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;ls&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;*&lt;/span&gt;
strain.exs
strain_test.exs
README.md
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;We made a new Mix project in this folder:&lt;/p&gt;

&lt;div class=&quot;language-sh highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;mix new strain_bench
&lt;span class=&quot;k&quot;&gt;*&lt;/span&gt; creating README.md
&lt;span class=&quot;k&quot;&gt;*&lt;/span&gt; creating .formatter.exs
&lt;span class=&quot;k&quot;&gt;*&lt;/span&gt; creating .gitignore
&lt;span class=&quot;k&quot;&gt;*&lt;/span&gt; creating mix.exs
&lt;span class=&quot;k&quot;&gt;*&lt;/span&gt; creating config
&lt;span class=&quot;k&quot;&gt;*&lt;/span&gt; creating config/config.exs
&lt;span class=&quot;k&quot;&gt;*&lt;/span&gt; creating lib
&lt;span class=&quot;k&quot;&gt;*&lt;/span&gt; creating lib/strain_bench.ex
&lt;span class=&quot;k&quot;&gt;*&lt;/span&gt; creating &lt;span class=&quot;nb&quot;&gt;test&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;*&lt;/span&gt; creating &lt;span class=&quot;nb&quot;&gt;test&lt;/span&gt;/test_helper.exs
&lt;span class=&quot;k&quot;&gt;*&lt;/span&gt; creating &lt;span class=&quot;nb&quot;&gt;test&lt;/span&gt;/strain_bench_test.exs

Your Mix project was created successfully.
You can use &lt;span class=&quot;s2&quot;&gt;&quot;mix&quot;&lt;/span&gt; to compile it, &lt;span class=&quot;nb&quot;&gt;test &lt;/span&gt;it, and more:

    &lt;span class=&quot;nb&quot;&gt;cd &lt;/span&gt;strain_bench
    mix &lt;span class=&quot;nb&quot;&gt;test

&lt;/span&gt;Run &lt;span class=&quot;s2&quot;&gt;&quot;mix help&quot;&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;for &lt;/span&gt;more commands.
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;And then we copied our exercise files into the project:&lt;/p&gt;

&lt;div class=&quot;language-sh highlighter-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;cp &lt;/span&gt;strain.exs lib/strain.exs
&lt;span class=&quot;nb&quot;&gt;cp &lt;/span&gt;strain_test.exs &lt;span class=&quot;nb&quot;&gt;test&lt;/span&gt;/strain_test.exs
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;We needed to make a few changes to our files:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;We needed to remove or comment out the first three lines of the test, since this contained some
Exercism-specific helper code that tried to load the main Elixir file from a particular location&lt;/li&gt;
  &lt;li&gt;We needed to rename our &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;strain.exs&lt;/code&gt; in the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;lib&lt;/code&gt; folder to &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;strain.ex&lt;/code&gt; so that Mix would compile
this file and it could be tested.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;With these two steps done, we could check our tests still passed:&lt;/p&gt;

&lt;div class=&quot;language-sh highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;mix &lt;span class=&quot;nb&quot;&gt;test&lt;/span&gt;
..............

Finished &lt;span class=&quot;k&quot;&gt;in &lt;/span&gt;0.1 seconds
1 doctest, 13 tests, 0 failures
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Great! Time to benchmark.&lt;/p&gt;

&lt;h3 id=&quot;final-implementation-benchmarking&quot;&gt;Final implementation: Benchmarking&lt;/h3&gt;

&lt;p&gt;First, we needed to add that dependency. Easily done - the &lt;a href=&quot;https://hex.pm/packages/benchee&quot;&gt;benchee page on
Hex.pm&lt;/a&gt; has a dependency block to add to our &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;mix.exs&lt;/code&gt;&lt;/p&gt;

&lt;div class=&quot;language-elixir highlighter-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;# mix.exs&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;defp&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;deps&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;do&lt;/span&gt;
  &lt;span class=&quot;p&quot;&gt;[{&lt;/span&gt;&lt;span class=&quot;ss&quot;&gt;:benchee&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;~&amp;gt; 0.9&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;ss&quot;&gt;only:&lt;/span&gt; &lt;span class=&quot;ss&quot;&gt;:dev&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 we download the dependencies: &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;mix deps.get&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;After that, we followed a &lt;a href=&quot;https://elixirschool.com/en/lessons/libraries/benchee/&quot;&gt;Elixir School guide on using
Benchee&lt;/a&gt;. First step - create our benchmark.&lt;/p&gt;

&lt;div class=&quot;language-elixir highlighter-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;# benchmark.exs&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;list&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;w&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;apple&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;zebra&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;banana&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;zombies&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;cherimoya&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;zelot&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;checker&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&amp;amp;&lt;/span&gt;&lt;span class=&quot;no&quot;&gt;String&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;starts_with?&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;&amp;amp;1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;z&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;

&lt;span class=&quot;no&quot;&gt;Benchee&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;run&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(%{&lt;/span&gt;
  &lt;span class=&quot;s2&quot;&gt;&quot;strain.comprehension&quot;&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;fn&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;-&amp;gt;&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;Strain&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;keep&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;list&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;checker&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;s2&quot;&gt;&quot;strain.enum&quot;&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;fn&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;-&amp;gt;&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;Strain&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;keep_enum&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;list&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;checker&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;s2&quot;&gt;&quot;enum.filter&quot;&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;fn&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;-&amp;gt;&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;Enum&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;filter&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;list&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;checker&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;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;We had three implementations we wished to test:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;strong&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;strain.comprehension&lt;/code&gt;&lt;/strong&gt; - our one line comprehension function.&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;strain.enum&lt;/code&gt;&lt;/strong&gt; - our Enum function (we renamed this to &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;keep_enum&lt;/code&gt; so we could benchmark it
from the same module)&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;enum.filter&lt;/code&gt;&lt;/strong&gt; - the normal &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;filter&lt;/code&gt; function built into the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Enum&lt;/code&gt; module.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;To run these benchmarks, we simply needed to &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;mix run benchmark.exs&lt;/code&gt;, which benchmarks each function
in the map a number of times and then reports results. During the benchmarking, we saw some warnings
explaining that our functions were a bit &lt;em&gt;too&lt;/em&gt; fast and our benchmarking results might have
deviation across runs. We “fixed” this by copy-pasting our list until it was larger, but we figured
the warning could mostly be ignored since we were really just interested in a rough measurement.
Here’s what we found.&lt;/p&gt;

&lt;h3 id=&quot;results-and-summary&quot;&gt;Results and Summary&lt;/h3&gt;

&lt;p&gt;Environment:&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;Operating System: macOS&quot;
CPU Information: Intel(R) Core(TM) i5-3230M CPU @ 2.60GHz
Number of Available Cores: 4
Available memory: 8 GB
Elixir 1.7.3
Erlang 21.0.6
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Test 1:&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;Comparison:
enum.filter                16.71 K
strain.comprehension       13.76 K - 1.21x slower
strain.enum                11.53 K - 1.45x slower
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Test 2:&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;Comparison:
enum.filter                16.03 K
strain.comprehension       10.77 K - 1.49x slower
strain.enum                 4.79 K - 3.34x slower
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Test 3:&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;Comparison:
enum.filter                15.70 K
strain.comprehension       13.43 K - 1.17x slower
strain.enum                 9.57 K - 1.64x slower
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Unsurprisingly, &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Enum.filter/2&lt;/code&gt; is fastest - this makes sense, since Enum functions are widely used
and I expect they have been heavily optimised.&lt;/p&gt;

&lt;p&gt;Also unsurprisingly, our Enum implementation of &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;keep&lt;/code&gt; was the slowest. We expected this, since we
were doing two complete iterations of the list - once for the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;map&lt;/code&gt;, and once for the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;reject&lt;/code&gt;. This
function also had a lot more deviation in speed over multiple benchmarking runs - probably just
because there’s more going on in that function to get delayed.&lt;/p&gt;

&lt;p&gt;Our comprehension sat very nicely, slightly slower than &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Enum.filter&lt;/code&gt;, but not by a huge margin - on
average, 1.3 times slower, but still consistently faster than &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;keep_enum&lt;/code&gt;. I would expect that the
benchmarking time on &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Enum.filter&lt;/code&gt; and &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;keep&lt;/code&gt; (with comprehension) would scale pretty linearly as
well, wheras the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;keep_enum&lt;/code&gt; implementation would be a bit more unpredictable due to it’s additional
complexity.&lt;/p&gt;

&lt;p&gt;Overall, we came to the conclusion that &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Enum.filter&lt;/code&gt; is the obvious winner when filtering a list.
Not terribly surprising to us! We did find that a list comprehension is still worth considering
though, since it’s an extremely capable way of performing map/reduce type operations in a succint
way. We also wrapped up the exercise with a real appreciation for how we maybe shouldn’t jump
straight into &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Enum&lt;/code&gt; every time just because it’s mentioned in the exercise scenario.&lt;/p&gt;

</description>
        <pubDate>Thu, 29 Nov 2018 00:00:00 +1300</pubDate>
        <link>https://joshmcarthur.com/til/2018/11/29/exercism-strain-collaborative-implementation-and-benchmarking.html</link>
        <guid isPermaLink="true">https://joshmcarthur.com/til/2018/11/29/exercism-strain-collaborative-implementation-and-benchmarking.html</guid>
        
        <category>til</category>
        
        <category>technology</category>
        
        <category>elixir</category>
        
        
        <category>TIL</category>
        
      </item>
    
      <item>
        <title>List memory usage per Unicorn worker</title>
        <description>&lt;p&gt;I have been using this snippet recently to show the amount of memory currently being consumed by
each Unicorn worker. This is a convenient way of checking:&lt;/p&gt;

&lt;ol&gt;
  &lt;li&gt;The expected number of workers is being run&lt;/li&gt;
  &lt;li&gt;The memory usage of workers is fairly uniform&lt;/li&gt;
  &lt;li&gt;The average memory usage of Unicorn workers (if you are considering adding or removing workers)&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Here’s the command:&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;ps &lt;span class=&quot;nt&quot;&gt;-e&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;-www&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;-o&lt;/span&gt; pid,rss,command | &lt;span class=&quot;nb&quot;&gt;grep&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;-e&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&apos;[u]nicorn_rails worker&apos;&lt;/span&gt; | &lt;span class=&quot;nb&quot;&gt;awk&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&apos;{printf &quot;%s %s\t%s mb\n&quot;,$3,$4,$2/1024}&apos;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;To break this down:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;ps -e -www -o pid,rss,command&lt;/code&gt; - list each process’s pid, memory usage in bytes, and the command&lt;/li&gt;
  &lt;li&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;grep -e &apos;[u]nicorn_rails worker&apos;&lt;/code&gt; - filter the command list to those than mention Unicorn workers&lt;/li&gt;
  &lt;li&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;awk&lt;/code&gt; - this just pretty-prints the list in the format ‘[command] [memory] mb`.&lt;/li&gt;
&lt;/ul&gt;

</description>
        <pubDate>Mon, 12 Nov 2018 00:00:00 +1300</pubDate>
        <link>https://joshmcarthur.com/til/2018/11/12/list-memory-usage-per-unicorn-worker.html</link>
        <guid isPermaLink="true">https://joshmcarthur.com/til/2018/11/12/list-memory-usage-per-unicorn-worker.html</guid>
        
        <category>til</category>
        
        <category>ops</category>
        
        <category>technology</category>
        
        <category>rails</category>
        
        
        <category>TIL</category>
        
      </item>
    
      <item>
        <title>My notes from Devops Days Wellington, Day Two</title>
        <description>&lt;blockquote&gt;
  &lt;p&gt;A little disclaimer: These notes are mostly for me to refer back to in the future, and are based
on quick handwritten notes I took at the conference. They shouldn’t be used as a general reference
since I may have misquoted, misremembered, or ventured an opinion that isn’t quite right.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;My DevOps ticket was funded by my employer, &lt;a href=&quot;https://ackama.com&quot;&gt;Ackama&lt;/a&gt;.&lt;/p&gt;

&lt;h2 id=&quot;devops-experiments-reflections-of-a-scaling-startup-alison-polton-simon&quot;&gt;DevOps Experiments: Reflections of a Scaling Startup: &lt;em&gt;Alison Polton-Simon&lt;/em&gt;&lt;/h2&gt;

&lt;p&gt;This talk was a great start to the day, covering an organisational change that many smaller
companies would emphasize with - offering support and ops resources to teams and customers in a
growing company without affecting development velocity.&lt;/p&gt;

&lt;p&gt;Alison’s talk started with an agenda, which I really liked, as it helped to frame the talk and the
direction it was going in. The next slide laid out her employer’s values, which I also really
appreciated being visible and upfront.&lt;/p&gt;

&lt;blockquote&gt;
  &lt;p&gt;&lt;strong&gt;Action Point:&lt;/strong&gt; The attribution against the key values slide was
&lt;a href=&quot;https://keyvalues.com&quot;&gt;keyvalues.com&lt;/a&gt; - look at this to see if Ackama’s written values can be
published publicly.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;One of the core question Alison suggested teams, and companies ask of themselves is “What
does reliability means to us?”.&lt;/p&gt;

&lt;blockquote&gt;
  &lt;p&gt;&lt;strong&gt;Action Point:&lt;/strong&gt; In my previous day’s notes, I identified an action point to identify and
document the strategy and purpose of operations at Ackama. I would extend this discussion and
documentation to include defining what reliability is, for Ackama and our customers.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;The remainder of the talk covered a journey through a number of team structures to try and alleviate
pressure from developers tasked to deliver features. I really emphasied with this points in this
part, as it’s something I’m actively experimenting with. It was useful to see where we are along
this track, and to also pick up some potential challenges and strategies for overcoming these in the
future.&lt;/p&gt;

&lt;p&gt;Alison described three team structures:&lt;/p&gt;

&lt;h3 id=&quot;tributes&quot;&gt;Tributes&lt;/h3&gt;

&lt;p&gt;This was the first team structure they tried out past their initial “everyone does ops, rotating
pager duty”, just-in-time approach. A tribute was a rotating single engineer whose full time job it
was to handle ops tasks, triaging support, supporting customers, and protecting the feature
development team. The tribute person rotated every now and then.&lt;/p&gt;

&lt;p&gt;Pros:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;More sustainable than a whole-team system&lt;/li&gt;
  &lt;li&gt;Fewer interruptions&lt;/li&gt;
  &lt;li&gt;Chance to build up context around support and ops tasks&lt;/li&gt;
  &lt;li&gt;Opportunities to undertake longer-term ops and support tasks&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Cons:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;Customer service &lt;em&gt;declined&lt;/em&gt;&lt;/li&gt;
  &lt;li&gt;Hard to hire for (needs to be able to do everything)&lt;/li&gt;
  &lt;li&gt;Hard to invest and grow the team - tribute always either fighting fires or trying to catch up with
support.&lt;/li&gt;
&lt;/ul&gt;

&lt;h3 id=&quot;collectives&quot;&gt;Collectives&lt;/h3&gt;

&lt;p&gt;The movement from tributes to collectives was based around one person not being scalable - from 
hiring, technical, support quality or human perspectives. This team was intended to support
specialisation of of particular team members, especially in regards to customer support and ops
work. The collective rotated quarterly, allowing knowledge to be accrued both in depth and breadth.&lt;/p&gt;

&lt;p&gt;Pros:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;Better focus for devs and support/ops people&lt;/li&gt;
  &lt;li&gt;Accrued platform support knowledge&lt;/li&gt;
  &lt;li&gt;Crosstraining and opportunities&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Cons:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;Development teams don’t support what they build&lt;/li&gt;
  &lt;li&gt;Juggling act to load balance tasks&lt;/li&gt;
  &lt;li&gt;Context switch cost both per-task and per-quarter&lt;/li&gt;
  &lt;li&gt;A quarter isn’t actually that long when you incorporate the time required to get up to speed and
handover.&lt;/li&gt;
&lt;/ul&gt;

&lt;h3 id=&quot;product-teams&quot;&gt;Product Teams&lt;/h3&gt;

&lt;p&gt;The current iteration of team structure sees developers with support capability, developers with ops
capability, developers and other cross-functional team members forming into product-focussed teams
with capability to work reasonably autonomously. These teams are designed to align with business
verticals rather than technical platforms (I imagine this means they could easily involve
marketing/sales/BA/other business roles in the teams with ease if this doesn’t happen already).
There is no ‘ops team’, since individual product teams own their own tools and systems. Each team
has KPIs and goals to meet against which success is measured.&lt;/p&gt;

&lt;blockquote&gt;
  &lt;p&gt;&lt;strong&gt;Action Point:&lt;/strong&gt; Currently I work as part of a team that exists across several work streams,
largely for resourcing convenience - it’s easier to allocate a project to a team rather than a
specific person. Consider what measurable KPIs could apply to such teams.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Since the customer/developer ops support tasks didn’t go away with the move to product teams, the
tribute model was retained, however this role was constrained to one tribute per team. This meant
that the scope of work the tribute is expected to complete is reduced, and constrained to projects
that the team already works with on a day-to-day basis (or at least has build experience of).&lt;/p&gt;

&lt;blockquote&gt;
  &lt;p&gt;&lt;strong&gt;Action Point:&lt;/strong&gt; Consider how a tribute system could work within the resourcing teams described
as above. How much of a cultural/process change would be required to support this?&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;There were some challenges outlined with product teams:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;Teams owned projects without necessarily holding responsibility for supporting projects. This was
the main motivation to the tribute being introduced as I understand it.&lt;/li&gt;
  &lt;li&gt;Cross-organisation consistency and knowledge sharing is hard&lt;/li&gt;
  &lt;li&gt;Need to prevent a return to the tribute system where one person gets lumped with all the
non-feature-dev tasks&lt;/li&gt;
  &lt;li&gt;Coordinating product teams takes more work than a single development team (this is for a startup
or other organisation that would normally have a single dev team).&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;While wrapping up, Alison also mentioned an engineering consititution, and it’s benefits in working
towards consistency, which I thought was a great idea. The engineering consitution is a collection
of basic principles which new hires agree to meet. The constitution is a living document hosted on
Github and is continually revised.&lt;/p&gt;

&lt;blockquote&gt;
  &lt;p&gt;&lt;strong&gt;Action Point:&lt;/strong&gt; Research existing engineering consitution documents and begin a process to
formalise such a set of principles to be published publicly. Most of these principles have already
been documented, they just need to be centralised.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2 id=&quot;lean-qa-theresa-neate&quot;&gt;Lean QA: &lt;em&gt;Theresa Neate&lt;/em&gt;&lt;/h2&gt;

&lt;p&gt;I found this talk really interesting with lots of potential for my human ops work with stewarding
and mentoring. It was one of a few talks this conference covering the need for testing and QA to be
baked into the entire software development, release and post-release support cycles.&lt;/p&gt;

&lt;p&gt;A recurring theme of this talk was T-Shaped people. I hadn’t come across this term before, but it
relates to people who have general skills in a range of areas (the wide top of the ‘T’), and
specialised skill in one precise area (the bar of the ‘T’).&lt;/p&gt;

&lt;blockquote&gt;
  &lt;p&gt;&lt;strong&gt;Action Point:&lt;/strong&gt; Consider how I could work with my mentees to figure out their ‘T’ and how they
can focus on general or specialist skills.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Theresa also redefined Quality Assurance as Quality Analysis, which I found to be a really good
reframing of how I see QA fitting into development processes. Ideally, they are involved throughout
the software development process, and should not be seen as gatekeepers with a pass/fail role -
instead, they should be seen as generalists with a specialisation in testing who can find potential
problems or quality issues in features, and provide assistance and advice to work with whoever
necessary to resolve these.&lt;/p&gt;

&lt;p&gt;Theresa’s role was described as “Developer advocate role”. She compared herself to Kelsey Hightower
(a well-known Kubernetes developer advocate at Google), but internal facing rather than external
facing. This role really reasonated with me, since my professional focus in the last few months has
been to introduce some consistency in the tools and processes used at my workplace to deliver
unsurprising, high-quality code.&lt;/p&gt;

&lt;blockquote&gt;
  &lt;p&gt;&lt;strong&gt;Action Point:&lt;/strong&gt; &lt;a href=&quot;https://www.oreilly.com/library/view/the-phoenix-project/9781457191350/&quot;&gt;“The Phoenix
Project”&lt;/a&gt; was recommended,
reminding me that I should circulate this novel yet again within Ackama.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;blockquote&gt;
  &lt;p&gt;&lt;strong&gt;Action Point:&lt;/strong&gt; Review a presentation from 2008 called &lt;a href=&quot;http://www.jedi.be/presentations/agile-infrastructure-agile-2008.pdf&quot;&gt;“Agile
Infrastructure”&lt;/a&gt; that was
mentioned several times.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2 id=&quot;a-devops-confessional-mrinal-mukherjee&quot;&gt;A Devops Confessional: &lt;em&gt;Mrinal Mukherjee&lt;/em&gt;&lt;/h2&gt;

&lt;p&gt;Not many notes from this one. I enjoyed the term “DevOops”, and the monkey slides.&lt;/p&gt;

&lt;p&gt;There were three devoops situtations that Mrinal identified:&lt;/p&gt;

&lt;h3 id=&quot;one-track-devops&quot;&gt;“One-track Devops”&lt;/h3&gt;

&lt;ul&gt;
  &lt;li&gt;Same process for each new devops project&lt;/li&gt;
  &lt;li&gt;Take care of the low hanging fruit, get short term wins&lt;/li&gt;
  &lt;li&gt;Automate infrastructure, cos it’s &lt;em&gt;cool&lt;/em&gt; and easily understood&lt;/li&gt;
  &lt;li&gt;Become bottleneck as the “devops person”&lt;/li&gt;
  &lt;li&gt;Become only person who can build and release the app&lt;/li&gt;
  &lt;li&gt;Discover that process and culture is causing issues, not just technology&lt;/li&gt;
  &lt;li&gt;Business doesn’t see results because only low hanging fruit is done, no actual painful problems.&lt;/li&gt;
&lt;/ul&gt;

&lt;h3 id=&quot;mvp-driven-devops&quot;&gt;“MVP-Driven DevOps”&lt;/h3&gt;

&lt;ul&gt;
  &lt;li&gt;Get stuff done fast&lt;/li&gt;
  &lt;li&gt;Start saying things like “We’ll fix it in the next sprint” (no future sprint planned)&lt;/li&gt;
  &lt;li&gt;Acrue technical debt&lt;/li&gt;
  &lt;li&gt;Make compromises with quality and security&lt;/li&gt;
  &lt;li&gt;Hard to maintain code&lt;/li&gt;
  &lt;li&gt;Slow to build new features because of tech debt, unplanned architecture and quality issues&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Can be somewhat avoided with:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;Peer review (of architecture, approach and consistency, not just bug spotting)&lt;/li&gt;
  &lt;li&gt;Minimum engineering standard (analogous to the “engineer constitution” described above)&lt;/li&gt;
&lt;/ul&gt;

&lt;h3 id=&quot;judgemental-devops&quot;&gt;“Judgemental DevOps”&lt;/h3&gt;

&lt;ul&gt;
  &lt;li&gt;Delays from other parties/teams&lt;/li&gt;
  &lt;li&gt;Not understanding the context of changes&lt;/li&gt;
  &lt;li&gt;Blame Driven Development - blame the other teams, other staff&lt;/li&gt;
  &lt;li&gt;Become stubborn and defensive&lt;/li&gt;
  &lt;li&gt;Results in conflict - even as minor as body language can be a giveaway&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Mitigate with:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;Empathy and shared context with other teams, processes and individuals&lt;/li&gt;
  &lt;li&gt;Understand goals, constraints and priorities&lt;/li&gt;
  &lt;li&gt;Collaborate on problems to create solutions&lt;/li&gt;
&lt;/ul&gt;

&lt;h2 id=&quot;kubernetesopenshift-open-session&quot;&gt;Kubernetes/OpenShift Open Session&lt;/h2&gt;

&lt;p&gt;A somewhat interesting session where a bunch of large kubernetes deployments talked about the tools
they used for logging, monitoring, secret management, configuration and networking. Some little
takeaways I got:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;~50% people in the room used Kubernetes&lt;/li&gt;
  &lt;li&gt;~2% used Kubernetes via Google&lt;/li&gt;
  &lt;li&gt;~2% self-hosted in public clouds (AWS, Azure, Google)&lt;/li&gt;
  &lt;li&gt;~2% V-Sphere&lt;/li&gt;
  &lt;li&gt;~5% OpenShift&lt;/li&gt;
  &lt;li&gt;~1% Rancher (possibly the rancher rep?)&lt;/li&gt;
  &lt;li&gt;Mostly people had a cluster size of 4-5, some with many more&lt;/li&gt;
  &lt;li&gt;Mostly people ran 10-20 nodes in a cluster&lt;/li&gt;
  &lt;li&gt;Storing state is normally the hardest bit (databases, static files)&lt;/li&gt;
  &lt;li&gt;It can be done, overall don’t run DB in containers unless it’s throwaway (e.g. integration
testing)&lt;/li&gt;
  &lt;li&gt;Majority of people using ECR as the container registry. Some using OpenShift’s internal registry.&lt;/li&gt;
  &lt;li&gt;Logging stacktraces as JSON blogs is good for busy logs since it’s all on one line&lt;/li&gt;
  &lt;li&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;kubectl top&lt;/code&gt; and &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;kubectl&lt;/code&gt; state metrics are good for monitoring&lt;/li&gt;
  &lt;li&gt;Ansible and ansible tools (ansible vault, etc) were far and away the most common thing used&lt;/li&gt;
&lt;/ul&gt;

&lt;h2 id=&quot;monoreposhared-libraries-open-session&quot;&gt;Monorepo/shared libraries Open Session&lt;/h2&gt;

&lt;p&gt;This session didn’t end up being super relevant to me, aside from a couple of little snippets around
where infrastructure vs orchestration vs app code should go.&lt;/p&gt;

&lt;h2 id=&quot;ignite-session-continuously-testing-govtnz-allan-geer-and-amanda-baker&quot;&gt;Ignite Session: Continuously Testing govt.nz: &lt;em&gt;Allan Geer and Amanda Baker&lt;/em&gt;&lt;/h2&gt;

&lt;p&gt;An interesting lighting talk covering the constraints of CWP/Silverstripe that I’ve noticed is
typical:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;Low motivation to regularly update&lt;/li&gt;
  &lt;li&gt;High risk/cost upgrades&lt;/li&gt;
  &lt;li&gt;Little to no testing&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Additionally, being in a government department, approaches to agile can be interesting. Their
flavour of agile was described as “micro-waterfall agile”. Sounds about right.&lt;/p&gt;

&lt;p&gt;Allan and Amanda introduced test coverage to govt.nz websites by building the creation of Gherkin
test cases into story writing sessions with the product owner, devs and testers. They created an
initial stocktake of features to describe existing systems and then prioritised to discove the most
important scenarios to test. They then made sure their test metrics were visible to the product team
to motivate further testing.&lt;/p&gt;

&lt;blockquote&gt;
  &lt;p&gt;&lt;strong&gt;Action Point:&lt;/strong&gt; Document strategy of stocktaking existing application features and prioritising
important revenue/customer generating features as part of legacy app discovery processes.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2 id=&quot;ignite-session-configuration-pipeline-ruling-the-one-ring-jules-clements&quot;&gt;Ignite Session: Configuration Pipeline: Ruling the One Ring: &lt;em&gt;Jules Clements&lt;/em&gt;&lt;/h2&gt;

&lt;p&gt;Another quick lightning talk about release processes and separating “deployment from
“orchestration”. I was also reminded of an additional requirement of a deployment pipeline that I
don’t often consider, which is to ensure that the output of a pipeline is a uniquelely identifiable
artifact that can be repeatedly deployed (i.e. deployed, rolled back, and deployed again). A Docker
image has these traits, and that’s probably the artifact that was intended, but it’s an additional
note to make sure that deployments using tools like Capistrano also share these traits (for example,
Capistrano deploys to release folders, and points to the “current” one using a symlink. It also
writes the hash of the git commit that was deployed into the REVISION file in each release.&lt;/p&gt;

&lt;blockquote&gt;
  &lt;p&gt;&lt;strong&gt;Action Point:&lt;/strong&gt; Come up with a good definition of orchestration I can get my head around&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h1 id=&quot;conference-takeaways&quot;&gt;Conference Takeaways&lt;/h1&gt;

&lt;p&gt;Overall, the conference was OK, but not great. The scale and type of work I do means that I need to
constantly translate many of the things I’m hearing in talks down to a much smaller scale, and then
decide whether they are problems I have, or if they are, if they are actionable. I think talks
covering smaller-scale devops would have been interesting. There were a couple of talks that were
this kind of thing, but they mostly covered scaling devops &lt;em&gt;teams&lt;/em&gt;, not devops &lt;em&gt;processes&lt;/em&gt; and
&lt;em&gt;technologies&lt;/em&gt;.&lt;/p&gt;

&lt;p&gt;My main high-level ideas to delve into more:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;Develop ops capabilities within each project team. Don’t have an “ops team”&lt;/li&gt;
  &lt;li&gt;Formally define ‘ops’ - it’s goals, mandates, and measurable KPIs&lt;/li&gt;
  &lt;li&gt;Define KPIs for a project that can be built into healthchecks and monitoring early on in the
project&lt;/li&gt;
  &lt;li&gt;Focus on value to customers, not MVP&lt;/li&gt;
  &lt;li&gt;Figure out how to continue testing post-release&lt;/li&gt;
  &lt;li&gt;Determine and document goals, constraints and priorities within a project charter&lt;/li&gt;
  &lt;li&gt;Publish organisational core values&lt;/li&gt;
  &lt;li&gt;Move away from tribute-style teams to product-ownership teams (and figure out what a “product” is
at a services company. Is it a client? A client’s application? A collection of similar
applications?)&lt;/li&gt;
  &lt;li&gt;Quality analysis, not Quality assurance&lt;/li&gt;
  &lt;li&gt;Document and publish engineer constitution&lt;/li&gt;
&lt;/ul&gt;

</description>
        <pubDate>Mon, 05 Nov 2018 00:00:00 +1300</pubDate>
        <link>https://joshmcarthur.com/2018/11/05/my-notes-from-devops-days-wellington-day-two.html</link>
        <guid isPermaLink="true">https://joshmcarthur.com/2018/11/05/my-notes-from-devops-days-wellington-day-two.html</guid>
        
        <category>technology</category>
        
        
      </item>
    
      <item>
        <title>My notes from Devops Days Wellington, Day One</title>
        <description>&lt;blockquote&gt;
  &lt;p&gt;A little disclaimer: These notes are mostly for me to refer back to in the future, and are based
on quick handwritten notes I took at the conference. They shouldn’t be used as a general reference
since I may have misquoted, misremembered, or ventured an opinion that isn’t quite right.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;My DevOps ticket was funded by my employer, &lt;a href=&quot;https://ackama.com&quot;&gt;Ackama&lt;/a&gt;.&lt;/p&gt;

&lt;h2 id=&quot;moving-from-ops-to-devops-centros-journey-to-the-promiseland-jeff-smith&quot;&gt;Moving from Ops to DevOps: Centro’s Journey to the Promiseland: &lt;em&gt;Jeff Smith&lt;/em&gt;&lt;/h2&gt;

&lt;p&gt;The most useful talk for me out of the whole day. Discussed the frequent conflict devops has with
both “dev” and “ops” (e.g. access vs security), and how many organisations attempt to merge two
teams with different context and motivation and instead end up with three teams.&lt;/p&gt;

&lt;p&gt;Took job offered as “Devops Manager” - condition of hire was to rename job title to “Production
Operations Director”, which makes much more sense.&lt;/p&gt;

&lt;p&gt;Align teams and tasks to centralize work. Good questions to explore team boundaries:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;What frustrates you?&lt;/li&gt;
  &lt;li&gt;What makes you come to work?&lt;/li&gt;
  &lt;li&gt;What makes you not want to come to work?&lt;/li&gt;
&lt;/ul&gt;

&lt;blockquote&gt;
  &lt;p&gt;&lt;strong&gt;Action Point:&lt;/strong&gt; I’m going to trial building these types of questions into my
stewarding/mentoring meeetings&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Jeff talked about how developers didn’t have access to set up alerts for the systems they created. I
have experienced similar pains on both sides of the dev/ops boundary.&lt;/p&gt;

&lt;blockquote&gt;
  &lt;p&gt;&lt;strong&gt;Action Point:&lt;/strong&gt; I’m going to consider how I can open up and make it easier for other developers
that I work with to understand and create customer-specific monitoring and alerts.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;He also talked about how making a dev environment destroyable and recreatable helped to remove some
frustration from both sides of devs/ops. I haven’t experienced this specific problem, but am going
to consider staging and UAT environments for some customers being destroyable/recreatable in this
way.&lt;/p&gt;

&lt;p&gt;I was prompted to consider what KPIs should be considered for particular sites, and to what extent
these should fit into monitoring and alerts.&lt;/p&gt;

&lt;p&gt;He also pointed out that infrastructure-as-code is not really “self documenting” to a sufficient
level, and advised also producing architecture diagrams and some documentation about what key
decisions were made and why.&lt;/p&gt;

&lt;blockquote&gt;
  &lt;p&gt;&lt;strong&gt;Action Point:&lt;/strong&gt; I am going to look at producing both AWS and regular architecture diagrams for
base Terraform configurations and publishing these internally.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;The most useful action point I got from the talk was to define the scope of the operations team.
What is it’s primary purpose, which should drive all the work it performs.&lt;/p&gt;

&lt;blockquote&gt;
  &lt;p&gt;&lt;strong&gt;Action Point:&lt;/strong&gt; Prepare draft team charter for the operations team outlining above.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;As a final takeaway, it was mentioned that for a larger ops team, keeping track of planned and
unplanned work is useful for understanding the health of the team. I think this is a fantastic idea
that applies to most project teams that have an element of BAU.&lt;/p&gt;

&lt;blockquote&gt;
  &lt;p&gt;&lt;strong&gt;Action Point:&lt;/strong&gt; I’m going to trial this reporting mechanism at the conclusion of my next
development sprint and report in the sprint review.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2 id=&quot;monitoring-that-cares-the-end-of-user-based-monitoring-françois-conil&quot;&gt;Monitoring that cares (the end of user based monitoring): &lt;em&gt;François Conil&lt;/em&gt;&lt;/h2&gt;

&lt;p&gt;This talk was all about monitoring and alerting in the context of the user. One interesting point
was the timezone difference between NZ and US makes it difficult to find times that “aren’t
important” for maintenance windows - e.g. a downtime window at 2am in NZ time would be the middle of
the day in the US. Not a concern for what I’m working on now, but valid for larger applications.&lt;/p&gt;

&lt;p&gt;They also noted that if everything is classed as critical, then nothing is, describing the concept
of an actionable alert as:&lt;/p&gt;

&lt;ol&gt;
  &lt;li&gt;Something is broken&lt;/li&gt;
  &lt;li&gt;Customer has noticed&lt;/li&gt;
  &lt;li&gt;You are the person who can fix it&lt;/li&gt;
  &lt;li&gt;You need to fix it immediately&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Unless all of these critieria are met, then the alert is not actionable and may as well not be
delivered.&lt;/p&gt;

&lt;p&gt;A recurring idea that I noted for a few talks was to design the KPIs of the application early on in
the development cycle, and monitor these as a primary metric, rather than more low-level metrics.
Monitoring customer journeys can reveal issues that standard CPU/memory/disk monitoring may not
detect.&lt;/p&gt;

&lt;h2 id=&quot;transforming-the-bank-pipelines-not-paperwork-mark-simposon-carlie-osbourne&quot;&gt;Transforming the Bank: pipelines not paperwork: &lt;em&gt;Mark Simposon, Carlie Osbourne&lt;/em&gt;&lt;/h2&gt;

&lt;p&gt;This talk was interesting, but was largely around a successful business process/change management
project. I always enjoy hearing about life in larger enterprises. Occasionally I get to hear about
some interesting technical challenges, more frequently about the overhead introduced around having
multiple levels of decision makers and stakeholders.&lt;/p&gt;

&lt;p&gt;An interesting note was that ANZ chose a project that was significant enough to the organistion to
prove the concept - internet banking in their case. They started with a 6 week development cycle,
then a 4 week release cycle that was frequently extended. Thye noted that the dev team had a sense
of disconnect with waiting so long before they would see their code in production. They also had a
high deploy cost, since each release would be significant - they staffed this at 25-30 people and
usually had 4-5 hours of downtime.&lt;/p&gt;

&lt;p&gt;Most of their change seems to be designed around a Jenkins pipeline that was designed to take their
code right through from a commit against master to production (running tests, UAT deploy, etc along
the way). This allowed them to drop to a weekly release instead of ~10 weeks.&lt;/p&gt;

&lt;p&gt;For testing, they cut their selenium tests and replaced with Cypress. I’ve done some brief looking
into Cypress, but I’d be curious to know how many Selenium tests they gave up to rewrite, and what
their coverage ended up being - seems like an odd cost/benefit tradeoff.&lt;/p&gt;

&lt;p&gt;The other refactoring they performed was to their change management process, getting stakeholders to
sign off on the release process, rather than each release, so they could re-use the signed off
release process each time without needing the overhead of getting sign off.&lt;/p&gt;

&lt;p&gt;Mark suggested playing down discussion of ‘MVP’, and instead focus on value to
the customer. Release small features often, listen to customer feedback, and interate.&lt;/p&gt;

&lt;blockquote&gt;
  &lt;p&gt;&lt;strong&gt;Action Point:&lt;/strong&gt; I’m interested to talk through this idea with some other developers and
scrum masters at Ackama, since I think this is a very important distinction in how I might frame
discussions around a feature or application.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;ANZ’s release process is now a 4-5 day cycle time to get code into production, followed by a 8 week
incremental release process for canary deploys.&lt;/p&gt;

&lt;h2 id=&quot;fighting-fires-with-devops-ryan-mccarvill&quot;&gt;Fighting fires with DevOps: &lt;em&gt;Ryan McCarvill&lt;/em&gt;&lt;/h2&gt;

&lt;p&gt;This talk was way different from how I thought it would be, but because of the structure of the
talk, a bunch of my notes were invalidated. The talk covered hardware/software packages deployed to
fire appliances for Fire and Emergency NZ, and how a capable but complicated system was compromised
down to a really interesting but minimalist solution due to unforeseen constraints (temperature in
this case).&lt;/p&gt;

&lt;p&gt;The initial hardware/software involved using a custom orchestrator to pull and run images from Azure
Container Registry onto a hardened Linux box on the appliance, paired with a multi-network comms
unit. A tablet and separate nav screen was used for the presentation of data. The box was
provisioned using Ansible, and both the Ansible scripts and docker image sources were controlled out
of git. This all worked in theory, but in practise the custom hardware boxes would overheat.&lt;/p&gt;

&lt;p&gt;With an impending deadline and all the hardware budget consumed, they ended up drastically
simplifying, moving a lot of the systems that were designed to run directly on the truck into the
cloud, and writing two small systems to run directly on the comms module, which was a locked-down
linux distribution that really just had Java available. One of these systems was a simple static
file server, which would serve off an external USB device, and the other was a search tool that
ended up being written in Rust after all other programming languages available fell over for one
reason or another.&lt;/p&gt;

&lt;p&gt;A super interesting talk anyway, and having done a similar project, I got a bunch of takeaways for
next time. I’m still super interested in how an Elixir &lt;a href=&quot;https://elixirschool.com/en/lessons/advanced/umbrella-projects/&quot;&gt;umbrella
project&lt;/a&gt; would function on a
low-resource hardware unit such as the one discussed in the talk. Especially since a tool like
&lt;a href=&quot;https://hex.pm/packages/distillery&quot;&gt;distillery&lt;/a&gt; may be able to package up the runtime and
application in such a way that it would just work.&lt;/p&gt;

&lt;p&gt;Overall, it was a super interesting story of reverting back to using nice simple, old style
technology to solve a new problem.&lt;/p&gt;

&lt;h2 id=&quot;open-space-session-devops-at-nz-scale&quot;&gt;Open Space Session: Devops at NZ scale&lt;/h2&gt;

&lt;p&gt;I attended this session, but it pretty quickly went off-topic without an external facilitator,
turning into a discussion about how devops functions in different teams, and even a whip-around of
job offerings. I didn’t speak up in this one as it took me the entire session to figure out the
nature of the discussion I wanted to have. I eventually figured out that I attended the session
hoping to have a discussion about “Devops at &lt;em&gt;small&lt;/em&gt; scale” (small == NZ really). I work for a
services company, so unlike our (generally) larger project company buddies, we’re usually just
setting off our customers on their journey, and doing so to the best of our abilities. Devops work
is inherently constrained and limited at this stage, so I was interested to get an answer here.&lt;/p&gt;

&lt;h2 id=&quot;open-space-session-devops-for-startups&quot;&gt;Open Space Session: Devops for startups&lt;/h2&gt;

&lt;p&gt;The above session led nicely into this one, since my colleagues and I had just come from the
previous session, and found the topic to be pretty much what we were after. The takeaways I got from
this discussion was to start small and with a consistent kind of approach in mind. It’s always
better to start with infrastructure-as-code rather than retrofitting this, and doesn’t take much
longer, if it takes longer at all. Generally it’s a good idea to dockerize applications from
scratch, but this does require balancing as to whether it will be maintained or is suitable for the
platform in question.&lt;/p&gt;

&lt;h2 id=&quot;a-trip-down-cicd-lane-everett-toews&quot;&gt;A Trip Down CI/CD Lane: &lt;em&gt;Everett Toews&lt;/em&gt;&lt;/h2&gt;

&lt;p&gt;This was just a five-minute talk but I still found it interesting. An overview of CI/CD platforms
and their pros and cons.&lt;/p&gt;

&lt;h2 id=&quot;creating-shared-contexts-jeff-smith&quot;&gt;Creating Shared Contexts: &lt;em&gt;Jeff Smith&lt;/em&gt;&lt;/h2&gt;

&lt;p&gt;Another 5 minute talk, with SO much packed into it. Jeff was back talking about establishing
contexts that are shared between teams, and the importance of making sure that context was being
communicated, not just a problem, or even worse, a pre-envisaged solution (that the other person may
think is the only option). Jeff encouraged thinking about problems, not solutions.&lt;/p&gt;

&lt;blockquote&gt;
  &lt;p&gt;&lt;strong&gt;Action Point:&lt;/strong&gt; Getting teams to agree on goals, constraints and priorities is super important
for teams to function (both within and without the team). I’m going to work to incorprate these
into future sales pipelines through to project charters and retrospectives.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2 id=&quot;2018-a-build-engineers-odeyssey-peter-sellars&quot;&gt;2018: A Build Engineer’s Odeyssey: &lt;em&gt;Peter Sellars&lt;/em&gt;&lt;/h2&gt;

&lt;p&gt;Another 5 minute overview of a bunch of different tools that a Node.js pipeline engineer might be
faced with coming into a role as a dedicated “devops person”. It was mostly material that had
already been covered/I’d already picked up (it was intended to be a high level overview, I think).
The main takeaway I got that was slightly unexpected was &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;npm ci&lt;/code&gt;, which is probably what I want to
be using within Rails and Phoenix projects when I just want my locked dependencies (e.g. during
Docker builds).&lt;/p&gt;

&lt;h2 id=&quot;testing-in-devops-for-engineeers-katrine-clokie&quot;&gt;Testing in DevOps for Engineeers: &lt;em&gt;Katrine Clokie&lt;/em&gt;&lt;/h2&gt;

&lt;p&gt;This talk was great, but by now I was getting pretty tired. I really wanted to concentrate since
I’ve been deep diving into testing recently, and it contained some really useful lessons around an
entire project team’s culture revolving around testing. Katrina intercepted really well with
François’s talk about monitoring, coming at the same idea from a testing perspective around driving
testing, development and ops processes based on customer journeys and application purpose rather
than just “standard metrics”. Something to consider for my projects:&lt;/p&gt;

&lt;blockquote&gt;
  &lt;p&gt;“What questions are you asking of your product that you know it’s good, it’s ready?”&lt;/p&gt;
&lt;/blockquote&gt;

&lt;blockquote&gt;
  &lt;p&gt;&lt;strong&gt;Action Point:&lt;/strong&gt; Pick a reference project and consider what healthchecks, KPIs, and possibly a
sample of intergration or system tests that could be run to continuously test application quality
after release.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;blockquote&gt;
  &lt;p&gt;&lt;strong&gt;Action Point:&lt;/strong&gt; Check out “A Practical Guide to Testing in DevOps” by Clokie.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;hr /&gt;

</description>
        <pubDate>Mon, 05 Nov 2018 00:00:00 +1300</pubDate>
        <link>https://joshmcarthur.com/2018/11/05/my-notes-from-devops-days-wellington-day-one.html</link>
        <guid isPermaLink="true">https://joshmcarthur.com/2018/11/05/my-notes-from-devops-days-wellington-day-one.html</guid>
        
        <category>technology</category>
        
        
      </item>
    
      <item>
        <title>Open all files as tabs in in vim</title>
        <description>&lt;p&gt;Just a quick trick I learned yesterday: I often glob files into &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;vim&lt;/code&gt; from my terminal to view them
quickly - for example, given the following files exist:&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;&amp;gt; ls 
file1.txt
file2.txt
file3.txt
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;When I run: &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;vim *.txt&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;Then I expect &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;file1.txt&lt;/code&gt;, the first file, to be opened&lt;/p&gt;

&lt;p&gt;When I run the command &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;:tab all&lt;/code&gt; within &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;vim&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;I then see a tab for each file I have opened:&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/img/posts/open-all-files-as-tabs-in-vim-1.png&quot; alt=&quot;Tabs in vim&quot; /&gt;&lt;/p&gt;

&lt;p&gt;This is a very nice shortcut that I expect to use often!&lt;/p&gt;
</description>
        <pubDate>Sat, 03 Nov 2018 00:00:00 +1300</pubDate>
        <link>https://joshmcarthur.com/til/2018/11/03/open-all-files-as-tabs-in-in-vim.html</link>
        <guid isPermaLink="true">https://joshmcarthur.com/til/2018/11/03/open-all-files-as-tabs-in-in-vim.html</guid>
        
        <category>til</category>
        
        <category>technology</category>
        
        <category>vim</category>
        
        <category>tools</category>
        
        
        <category>TIL</category>
        
      </item>
    
      <item>
        <title>RSpec&apos;s `have_attributes` matcher</title>
        <description>&lt;p&gt;I’ve been working on rewriting some legacy feature tests that have been unmaintained for several years as system
tests. One of the most convenient methods I have found while creating these tests is &lt;a href=&quot;https://relishapp.com/rspec/rspec-expectations/docs/built-in-matchers/have-attributes-matcher&quot;&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;have_attributes&lt;/code&gt;&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;The RSpec documentation I linked to above is a great overview of the matcher. I have found that it’s
useful to matching the result of a service object, processor or even a controller action when we
expect the resulting model, or models to have particular attributes set, but do not need to match on
the entire object.&lt;/p&gt;

&lt;p&gt;Here’s a simple example of &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;have_attributes&lt;/code&gt; in action:&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;WidgetCreator&lt;/span&gt; 
  &lt;span class=&quot;k&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;create&lt;/span&gt;
    &lt;span class=&quot;no&quot;&gt;Widget&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;create&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;ss&quot;&gt;status: :enqueued&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;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;no&quot;&gt;WidgetCreator&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;do&lt;/span&gt;
  &lt;span class=&quot;n&quot;&gt;describe&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;.create&quot;&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;do&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;subject&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;WidgetCreator&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;create&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

    &lt;span class=&quot;n&quot;&gt;it&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;creates the widget&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;subject&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;change&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;no&quot;&gt;Widget&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;ss&quot;&gt;:count&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;).&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;by&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; 
    &lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;

    &lt;span class=&quot;n&quot;&gt;it&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;marks the widget as enqueued&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;subject&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_attributes&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;ss&quot;&gt;status: :enqueued&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;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;have_attributes&lt;/code&gt; accepts a single attribute key-value pair, or multiple. If multiple attributes are
provided, then all keys and values must match. This matcher also seems to function against any other
object which exposes attributes using simple setter and getter methods - like &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;ActiveModel::Model&lt;/code&gt;,
&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Struct&lt;/code&gt; and &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;OpenStruct&lt;/code&gt;. This is based on the
&lt;a href=&quot;https://github.com/rspec/rspec-expectations/commit/6f975b08c996b1014654334229d5d4b020055690#diff-3d34526f651a63dfdd65fe70231f9d26R59&quot;&gt;implementation&lt;/a&gt;
using &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;__send__&lt;/code&gt; to read each attribute key from the actual object before comparing the value.&lt;/p&gt;
</description>
        <pubDate>Tue, 23 Oct 2018 00:00:00 +1300</pubDate>
        <link>https://joshmcarthur.com/til/2018/10/23/rspecs-have_attributes-matcher.html</link>
        <guid isPermaLink="true">https://joshmcarthur.com/til/2018/10/23/rspecs-have_attributes-matcher.html</guid>
        
        <category>til</category>
        
        <category>technology</category>
        
        <category>rails</category>
        
        
        <category>TIL</category>
        
      </item>
    
      <item>
        <title>Mitre Flats: Tararua Forest Park</title>
        <description>&lt;p&gt;This trip was a bit opportunistic, making the most of a fine weekend in late winter to start getting
back into the Forest Park before the official start of the summer season!&lt;/p&gt;

&lt;p&gt;Mitre Flats is one of the more popular spots to visit from the Wairarapa side of the Tararua range,
and has been on our to-do list for some time. Mitre Flats is a good location to start from if
heading towards one of the nearby high points:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;a href=&quot;http://www.topomap.co.nz/NZTopoMap?v=2&amp;amp;ll=-40.837028,175.44857&amp;amp;z=14&quot;&gt;Baldy&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;http://www.topomap.co.nz/NZTopoMap?v=2&amp;amp;ll=-40.805721,175.466423&amp;amp;z=14&quot;&gt;Mitre Peak&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;http://www.topomap.co.nz/NZTopoMap?v=2&amp;amp;ll=-40.826118,175.44548&amp;amp;z=14&quot;&gt;Three Kings&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;There’s also a tricky-looking loop track to do that could be done in a long weekend, heading into
Mitre, up and across the Broken Axe pinnacles (a narrow rocky ridge from the looks of it), and down
to Jumbo Hut, returning up the Atiwhakatu valley back to Mitre. Something for another day!&lt;/p&gt;

&lt;p&gt;The track to Mitre is known by some sources as the Barra Track, or just the Mitre Flats track. It’s
reached about 15 minutes from Masterton, past the Mt Holdsworth turn off and after skirting around
the outside of Masterton on the ring road that follows along the train tracks. The rural road turns
to gravel a few kms before the track start. The road seems to terminate around the start of the
track, near a fenced-off bridge over the Waingawa River. At the track start, there is a lay-by on
both sides of the road for parking, and a small shelter. No toilets or fresh water supplies here, so
you’ll be pretty quick to set off through the pedestrian gate next to the cattle grid and along the
farm road.&lt;/p&gt;

&lt;p&gt;A slightly different approach to the hills than we’re used to around Wellington, you’ll be
continuing along this farm road for the first 30-45 minutes before the park boundary is reached.
It’s not a particularly dull walk though, as the track follows alongside the river, which looks like
it has some great swimming holes. After a short time, you’ll reach the entrance to one of the farm
houses, where the track turns 90 degrees to head along the property boundary, before crossing farm
road beside a shearing shed and dropping off the terrace to cross a swampy bit and a small
bridge across a creek before arriving next to the river. About 10-15 minutes after this point the
bush edge is reached, with a short and steep climb back up to terrace level where there is a
boundary fence with a stile, and a pretty old and rotten looking sign noting that you’ve just
entered the Tararua Forest Park.&lt;/p&gt;

&lt;p&gt;From here on in, the track follows a pretty similar pattern until the swingbridge across the
Waingawa River is reached a couple of minutes before the hut. The track initially climbs until a
point approximately 100m above the river is reached. The track continues along this contour for the
next couple of hours - don’t make the mistake I made of assuming it’ll be cruisy though - there is
plenty of undulation going on, with a few rocky scrambles. The track is mostly well marked, except
for a couple of points around the halfway point where some small rock slips have concealed the
original track route. You won’t get lost here, but after going through some knee-high grass, the
track will appear to drop off a couple of metres to the left where you’ll then get to a slippy slope
to scramble up. Instead of going to the left here, go to the less-obvious right hand route which
will save this scramble.&lt;/p&gt;

&lt;p&gt;You’ll know that you’ve almost reached the swingbridge when the track begins descending - in
traditional Tararua fashion, this is straight down the hill to river level. The bridge is planked
rather than aluminum struts or mesh, so it’s quick and easy to cross. On the far side of the bridge
is the intersection with the track to the Atiwhakatu Valley, with the turn off to the track to the
summit of Mitre just after this intersection. The hut sits in a really nice clearing less than 5
minutes after the bridge.&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/img/posts/mitre-flats/bridge.jpg&quot; alt=&quot;Waingawa River Swingbridge&quot; /&gt;&lt;/p&gt;

&lt;p&gt;On this particular trip, we had chosen to camp (since we had the dog with us). I’d say it’s one of
the nicest, if not the nicest spots we’ve been to with this. The clearing was large enough for us to
comfortably share with a high school outdoor ed group of about 20 students, most of whom were also
camping. There’s some nice flat areas past the hut and just inside the bushline on the track to Cow
Creek. The hut itself is well maintained, I believe by the Masterton Tramping Club, and sleeps 20.
There is a supply of rainwater from the roof and a nice verandah area to enjoy the last of the sun
before it disappears (quite early, since the hut is deep in the valley!). Since we were just in for
the right, I had treated myself to carrying in an air couch to relax on and catch up on some
reading. A perfect spot, however it did get below zero overnight - cold enough to freeze water we
had outside the tent.&lt;/p&gt;

&lt;p&gt;Returning is much the same, however due to the undulation, the track does feel quite different going
the other way. The climb up from the swingbridge is mercifully brief, and after that it’s just a
matter of knocking off the distance until the farmland is reached. Meandering back along the farm
road in the sun was a nice way to wrap up the trip, while looking at the tempting pools in the river
below.&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/img/posts/mitre-flats/river.jpg&quot; alt=&quot;Swimming Hole&quot; /&gt;&lt;/p&gt;

&lt;p&gt;This was very much a recon trip for us, as we’ve had our eye on Mid King Biv, Broken Axe Pinnacles,
and the Cow Creek area for a while. I think we were probably hoping for a slightly easier trip than
we ended up with, but the great campsite more than made up for the slightly tougher walk!&lt;/p&gt;

&lt;p&gt;The DOC time for this track is described as 4 hours. It took us 3.5 moving quite slowly with lots of
breathers, but I think that because of the track surface and terrain that time is probably what most
people could expect to do it in. For more information on hut facilities and up to date alerts, see
the &lt;a href=&quot;https://www.doc.govt.nz/parks-and-recreation/places-to-go/wellington-kapiti/places/tararua-forest-park/things-to-do/huts/mitre-flats-hut/&quot;&gt;Mitre Flats Hut page on the DoC
website&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/img/posts/mitre-flats/start.jpg&quot; alt=&quot;Starting off along the Mitre Flats track&quot; /&gt;&lt;/p&gt;
</description>
        <pubDate>Wed, 03 Oct 2018 00:00:00 +1300</pubDate>
        <link>https://joshmcarthur.com/trip-reports/2018/10/03/mitre-flats-tararua-forest-park.html</link>
        <guid isPermaLink="true">https://joshmcarthur.com/trip-reports/2018/10/03/mitre-flats-tararua-forest-park.html</guid>
        
        <category>tramping</category>
        
        <category>trip-reports</category>
        
        
        <category>trip-reports</category>
        
      </item>
    
      <item>
        <title>Sending DHT22 readings from a Raspberry Pi Zero to MQTT</title>
        <description>&lt;p&gt;I spent a couple of hours last weekend getting a RaspberryPi Zero (W) up and running as a home
automation node, joining my existing fleet of MySensor Arduino nodes. The Raspberry Pi is being used
instead of an Arduino as it is eventually going to perform a number of functions along with climate
sensing - for example, bluetooth presence detection for room-based location.&lt;/p&gt;

&lt;p&gt;I found a number of resources documenting how to do parts of the system that I cobbled together, but
not an overview of the entire thing - that’s what this post, and
https://github.com/joshmcarthur/raspberrry-pi-dht22 is intended to be.&lt;/p&gt;

&lt;h2 id=&quot;1-hardware&quot;&gt;1. Hardware&lt;/h2&gt;

&lt;p&gt;I used a RaspberryPi 0 (W). The W means it has onboard wifi and bluetooth. I’ve had no problems with
connectivity and the hardware itself is looking super stable. I got the RaspberryPi boards of
Aliexpress, so there’s no telling if they’re official or not. They were around NZD$20 each from
memory.&lt;/p&gt;

&lt;p&gt;The DHT22 sensors I got in a 5 pack a few months ago to eventually replace the DHT11 sensors I have
on my Arduino nodes. Also from Aliexpress.&lt;/p&gt;

&lt;h2 id=&quot;2-os&quot;&gt;2. OS&lt;/h2&gt;

&lt;p&gt;I’m using &lt;a href=&quot;https://www.raspberrypi.org/downloads/raspbian/&quot;&gt;Raspbian Stretch Lite&lt;/a&gt; on the Raspberry
Pi board. I run all of the home automation stuff that I can in &lt;a href=&quot;https://www.docker.com/&quot;&gt;Docker&lt;/a&gt;
(which now has official support for ARM boards, the Raspberry Pi in particular), using
&lt;a href=&quot;https://docs.docker.com/compose/&quot;&gt;docker-compose&lt;/a&gt; as a lightweight container manager.&lt;/p&gt;

&lt;p&gt;While there is an overhead associated with running containers in Docker, I find that it is useful to
establish separation of concerns and boundaries between different tooling for different tasks. It’s
not necessary to use Docker for this, but my repo does include a working Dockerfile and
docker-compose.yml.&lt;/p&gt;

&lt;h2 id=&quot;3-wiring&quot;&gt;3. Wiring&lt;/h2&gt;

&lt;p&gt;The wiring for these sensors is pretty simple. See https://pinout.xyz for a pin layout of the
Raspberry Pi GPIO pins, but the wiring is simple - 3.3v from the bottom left corner of the GPIO goes
to the left hand pin, ground from the bottom right corner of the GPIO goes to the right hand pin,
and the data pin is connected to GPIO4. The data pin is the second from left (the remaining pin on
the DHT22 is unused for most use-cases), and GPIO is 4th from the left on the bottom row of the GPIO
pins.&lt;/p&gt;

&lt;p&gt;I found many forum posts that suggested that a resistor should be placed within this circuit,
however I found that I was able to get readings without one. GPIO4 in particular does have a pull-up
resistor that may have been sufficient to work with, so other GPIO pins may not work as well.&lt;/p&gt;

&lt;h2 id=&quot;4-the-python-script&quot;&gt;4. The Python script&lt;/h2&gt;

&lt;p&gt;I’m using &lt;a href=&quot;https://github.com/adafruit/Adafruit_Python_DHT&quot;&gt;Adafruit_Python_DHT&lt;/a&gt; to communicate with
the DHT22 sensor. This library is Python with a small C component for GPIO integration that
abstracts over the actual sensor reading. I initially tried to just use the &lt;a href=&quot;https://github.com/adafruit/Adafruit_Python_DHT/blob/master/examples/AdafruitDHT.py&quot;&gt;example
script&lt;/a&gt; built
into this library, but I found that having the output serialized for humans wasn’t particularly easy
to convert into an MQTT payload. Instead, I used the same API that the example script uses, but I
serialize the message into a JSON datastructure. Errors are logged to STDERR so that they can be
logged without accidentally piping error messages into MQTT.&lt;/p&gt;

&lt;h2 id=&quot;5-the-bash-script&quot;&gt;5. The bash script&lt;/h2&gt;

&lt;p&gt;The bash script glues everything together. It runs a &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;while true&lt;/code&gt; loop with a sleep-based delay that
calls the Python script, checks it succeeds, parses the JSON to extract the temperature and
humidity, and sends this data to MQTT. This script should work for most purposes, but this is also
where most of the ‘me-specific’ configuration lies.&lt;/p&gt;

&lt;h2 id=&quot;6-tying-it-all-together-docker-compose&quot;&gt;6. Tying it all together: docker-compose&lt;/h2&gt;

&lt;p&gt;The Docker image is Alpine linux based, and is just over 200mb when built (I haven’t optimised
this particularly). The image installs &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Adafruit_Python_DHT&lt;/code&gt; via a git clone, and then replaces
a shebang that is hardcoded to expect Python in a particular path that is mising in the Python base
image. It then adds the Python and bash scripts and sets up the polling script as the CMD (so this
will run by default if no other command is given).&lt;/p&gt;

&lt;p&gt;The docker-compose file mostly just mounts the current working directory in &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;/opt/dht22&lt;/code&gt; so that the
poll and python files can be revised easily. The docker-compose.yml file also instructs the
container to be restarted if it fails for any reason (&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;restart: always&lt;/code&gt;) - this will also cause the
container to be started on boot. For GPIO access, &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;privileged: true&lt;/code&gt; is the easiest way to allow the
container to communicate with the GPIO pins, although there are alternatives if you’re using your
Raspberry Pi in a secure environment. Finally, there are a couple of environment variables that
define the IP address of MQTT and the topic to publish to.&lt;/p&gt;

&lt;h2 id=&quot;7-result&quot;&gt;7. Result!&lt;/h2&gt;

&lt;p&gt;With a quick &lt;a href=&quot;https://www.home-assistant.io/components/sensor.mqtt/&quot;&gt;MQTT Sensor&lt;/a&gt; configuration in
HomeAssistant, the sensor shows up!&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/img/posts/homeassistant-mqtt-sensor.png&quot; alt=&quot;MQTT sensor in Home Assistant&quot; /&gt;&lt;/p&gt;

&lt;h2 id=&quot;resources&quot;&gt;Resources:&lt;/h2&gt;

&lt;p&gt;&lt;a href=&quot;https://github.com/joshmcarthur/raspberrry-pi-dht22&quot;&gt;Github Repository&lt;/a&gt;&lt;/p&gt;
</description>
        <pubDate>Sun, 30 Sep 2018 00:00:00 +1200</pubDate>
        <link>https://joshmcarthur.com/home-automation/2018/09/30/sending-dht22-readings-from-a-raspberry-pi-zero-to-mqtt.html</link>
        <guid isPermaLink="true">https://joshmcarthur.com/home-automation/2018/09/30/sending-dht22-readings-from-a-raspberry-pi-zero-to-mqtt.html</guid>
        
        
        <category>home-automation</category>
        
      </item>
    
      <item>
        <title>Automatic text wrapping in vim</title>
        <description>&lt;p&gt;This is something I use all the time. I don’t have it in my &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;~/.vimrc&lt;/code&gt; yet - partially because I
haven’t got around to it, partially because I sometimes want to use different line lenghts, and
partially because I don’t want to bikeshed myself trying to decide on what line length I want for
different file formats.&lt;/p&gt;

&lt;p&gt;Vim supports soft wrapping, which is something a bit different - it only affects the display of the
line, not the actual line length. Softwrap is great for scripting or programming, where a line break
in the wrong place might have a negative impact on your ability to actually compile and/or run the
file!&lt;/p&gt;

&lt;p&gt;Hard wrapping actually adds line break characters when the line exceeds a number of characters, and
is therefore great for git commit messages, emails, blog posts, letters, and any other contexts
where actual content is being written.&lt;/p&gt;

&lt;p&gt;To set a hard wrap at a particular character length, simply run &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;set tw=&lt;/code&gt; within a
vim window. Don’t forget to use the ‘:’ character to enter into the appropriate mode (I think this
is called ‘command mode’?) before running this. I usually use one of the following settings:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;set tw=80&lt;/code&gt;&lt;/li&gt;
  &lt;li&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;set tw=100&lt;/code&gt;&lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;set tw=120&lt;/code&gt;&lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;trending towards shorter lines for things like git commit messages, and longer lines for longform
content like a blog post. When the line character count reaches the wrap limit you have set, it
will automatically drop onto a new line, increasing the line count in the gutter on the left.&lt;/p&gt;

    &lt;p&gt;&lt;img src=&quot;http://g.recordit.co/DmJnHaaUtz.gif&quot; alt=&quot;tw demo&quot; /&gt;&lt;/p&gt;
  &lt;/li&gt;
&lt;/ul&gt;
</description>
        <pubDate>Wed, 19 Sep 2018 00:00:00 +1200</pubDate>
        <link>https://joshmcarthur.com/til/2018/09/19/automatic-text-wrapping-in-vim.html</link>
        <guid isPermaLink="true">https://joshmcarthur.com/til/2018/09/19/automatic-text-wrapping-in-vim.html</guid>
        
        <category>til</category>
        
        <category>vim</category>
        
        <category>tooling</category>
        
        
        <category>TIL</category>
        
      </item>
    
      <item>
        <title>An access token state store in Elixir</title>
        <description>&lt;p&gt;I’ve recently been working on a small project that uses OAuth for authentication, which is proving
to be a great approach for what I need it for. After I got the OAuth authentication layer working
through, I realised that I needed a place to store the hash of user data I receive back from the
OAuth provider, along with some kind of access token I can use to authenticate requests to my
GraphQL API. This post describes the state store I created to manage access tokens.&lt;/p&gt;

&lt;p&gt;Elixir has a built in module for background processes called GenServer. I actually managed to
implement most of what I needed simply based on the excellent documentation. The API for my module
wasn’t very complicated - I just needed to insert access tokens, destroy access tokens (for logout
and invalidation), and check whether a given token exists.&lt;/p&gt;

&lt;p&gt;The module ended up quite simple:&lt;/p&gt;

&lt;div class=&quot;language-elixir highlighter-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;defmodule&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;ElixirAuthTokenStoreExample&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;no&quot;&gt;Repo&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;do&lt;/span&gt;
  &lt;span class=&quot;kn&quot;&gt;use&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;GenServer&lt;/span&gt;
  &lt;span class=&quot;nv&quot;&gt;@name&lt;/span&gt; &lt;span class=&quot;bp&quot;&gt;__MODULE__&lt;/span&gt;

  &lt;span class=&quot;c1&quot;&gt;# Client&lt;/span&gt;

  &lt;span class=&quot;nv&quot;&gt;@doc&lt;/span&gt; &lt;span class=&quot;sd&quot;&gt;&quot;&quot;&quot;
  Start the token repo server

  ## Example

    iex&amp;gt; {:ok, pid} = ElixirAuthTokenStoreExample.Repo.start_link(name: :repo_start_link_doctest)
    iex&amp;gt; is_pid(pid)
    true
  &quot;&quot;&quot;&lt;/span&gt;
  &lt;span class=&quot;k&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;start_link&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;opts&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;do&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;opts&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;Keyword&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;put_new&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;opts&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;nv&quot;&gt;@name&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;store&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;GenServer&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;start_link&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;bp&quot;&gt;__MODULE__&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;store&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;opts&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;nv&quot;&gt;@doc&lt;/span&gt; &lt;span class=&quot;sd&quot;&gt;&quot;&quot;&quot;
  Insert a new token into the repo

  ## Example

    iex&amp;gt; {:ok, pid} = ElixirAuthTokenStoreExample.Repo.start_link(name: :repo_insert_doctest_1)
    iex&amp;gt; ElixirAuthTokenStoreExample.Repo.insert(pid, &quot;abc123&quot;)
    iex&amp;gt; :sys.get_state(pid)
    [&quot;abc123&quot;]
  &quot;&quot;&quot;&lt;/span&gt;
  &lt;span class=&quot;k&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;insert&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;pid&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;n&quot;&gt;token&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;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;GenServer&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;cast&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;pid&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;:insert&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;token&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;})&lt;/span&gt;

  &lt;span class=&quot;nv&quot;&gt;@doc&lt;/span&gt; &lt;span class=&quot;sd&quot;&gt;&quot;&quot;&quot;
  Remove a token from the repo

  ## Example

    iex&amp;gt; {:ok, pid} = ElixirAuthTokenStoreExample.Repo.start_link(name: :repo_destroy_doctest_1)
    iex&amp;gt; ElixirAuthTokenStoreExample.Repo.insert(pid, &quot;abc123&quot;)
    iex&amp;gt; ElixirAuthTokenStoreExample.Repo.destroy(pid, &quot;abc123&quot;)
    iex&amp;gt; :sys.get_state(pid)
    []

  &quot;&quot;&quot;&lt;/span&gt;
  &lt;span class=&quot;k&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;destroy&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;pid&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;n&quot;&gt;token&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;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;GenServer&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;cast&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;pid&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;:destroy&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;token&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;})&lt;/span&gt;

  &lt;span class=&quot;nv&quot;&gt;@doc&lt;/span&gt; &lt;span class=&quot;sd&quot;&gt;&quot;&quot;&quot;
  Check whether a given token exists in the store

  ## Example

    iex&amp;gt; {:ok, pid} = ElixirAuthTokenStoreExample.Repo.start_link(name: :repo_exists_doctest_1)
    iex&amp;gt; ElixirAuthTokenStoreExample.Repo.insert(pid, &quot;abc123&quot;)
    iex&amp;gt; ElixirAuthTokenStoreExample.Repo.exists?(pid, &quot;abc123&quot;)
    true

    iex&amp;gt; {:ok, pid} = ElixirAuthTokenStoreExample.Repo.start_link(name: :repo_exists_doctest_2)
    iex&amp;gt; ElixirAuthTokenStoreExample.Repo.insert(pid, &quot;abc123&quot;)
    iex&amp;gt; ElixirAuthTokenStoreExample.Repo.exists?(pid, &quot;abc1234&quot;)
    false

  &quot;&quot;&quot;&lt;/span&gt;
  &lt;span class=&quot;k&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;exists?&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;pid&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;n&quot;&gt;token&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;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;GenServer&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;call&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;pid&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;:exists&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;token&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;})&lt;/span&gt;

  &lt;span class=&quot;c1&quot;&gt;# Server&lt;/span&gt;

  &lt;span class=&quot;nv&quot;&gt;@impl&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;true&lt;/span&gt;
  &lt;span class=&quot;k&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;init&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;tokens&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;do&lt;/span&gt;
    &lt;span class=&quot;c1&quot;&gt;# This is the &apos;state&apos; of the server&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;n&quot;&gt;tokens&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;nv&quot;&gt;@impl&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;true&lt;/span&gt;
  &lt;span class=&quot;k&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;handle_cast&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;({&lt;/span&gt;&lt;span class=&quot;ss&quot;&gt;:insert&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;token&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;},&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;state&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;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;ss&quot;&gt;:noreply&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;token&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;|&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;state&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;n&quot;&gt;handle_cast&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;({&lt;/span&gt;&lt;span class=&quot;ss&quot;&gt;:destroy&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;token&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;},&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;state&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;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;ss&quot;&gt;:noreply&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;state&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;|&amp;gt;&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;List&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;delete&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;token&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;nv&quot;&gt;@impl&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;true&lt;/span&gt;
  &lt;span class=&quot;k&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;handle_call&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;({&lt;/span&gt;&lt;span class=&quot;ss&quot;&gt;:exists&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;token&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;},&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;_from&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;state&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;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;ss&quot;&gt;:reply&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;state&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;|&amp;gt;&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;Enum&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&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;token&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;),&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;state&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 genserver can be added to your Mix application supervisor, which will ensure that a a process
will always be running holding the state that is available application-wide. Because of the simple
API we have defined, interacting with the token store is quite simple:&lt;/p&gt;

&lt;div class=&quot;language-elixir highlighter-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;alias&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;ElixirAuthTokenStoreExample&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;no&quot;&gt;Repo&lt;/span&gt;

&lt;span class=&quot;no&quot;&gt;Repo&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;start_link&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;

&lt;span class=&quot;c1&quot;&gt;# Insert a token&lt;/span&gt;
&lt;span class=&quot;no&quot;&gt;Repo&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;insert&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;abc123&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;

&lt;span class=&quot;c1&quot;&gt;# Check it exists&lt;/span&gt;
&lt;span class=&quot;no&quot;&gt;Repo&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;exists?&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;abc123&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;

&lt;span class=&quot;c1&quot;&gt;# Destroy a token&lt;/span&gt;
&lt;span class=&quot;no&quot;&gt;Repo&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;destroy&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;abc123&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;I have placed this example code into a reference mix application, available at https://github.com/joshmcarthur/elixir-auth-token-store-example.&lt;/p&gt;

</description>
        <pubDate>Wed, 19 Sep 2018 00:00:00 +1200</pubDate>
        <link>https://joshmcarthur.com/til/2018/09/19/an-access-token-state-store-in-elixir.html</link>
        <guid isPermaLink="true">https://joshmcarthur.com/til/2018/09/19/an-access-token-state-store-in-elixir.html</guid>
        
        <category>til</category>
        
        <category>elixir</category>
        
        
        <category>TIL</category>
        
      </item>
    
      <item>
        <title>Generating error messages using ActiveModel::Errors</title>
        <description>&lt;p&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;ActiveModel::Errors&lt;/code&gt; are widely used in ActiveRecord and therefore Rails, and while custom messages
can be provided to each validation using the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;message&lt;/code&gt; key, it’s much more flexible to instead pass
a symbol as the message, which will cause the message to be looked up using the internationalisation
framework provided by the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;i18n&lt;/code&gt; gem that is a dependency of Rails. Here are some examples:&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;validates&lt;/span&gt; &lt;span class=&quot;ss&quot;&gt;:title&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;ss&quot;&gt;presence: &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;message: &lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;needs to be filled in&quot;&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;validates&lt;/span&gt; &lt;span class=&quot;ss&quot;&gt;:title&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;ss&quot;&gt;presence: &lt;/span&gt;&lt;span class=&quot;kp&quot;&gt;true&lt;/span&gt; &lt;span class=&quot;c1&quot;&gt;# Will use built-in validation message look up&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;validates&lt;/span&gt; &lt;span class=&quot;ss&quot;&gt;:title&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;ss&quot;&gt;presence: &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;message: :blank&lt;/span&gt; &lt;span class=&quot;c1&quot;&gt;# ^ The same effect as the above&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;validate&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;do&lt;/span&gt;
  &lt;span class=&quot;n&quot;&gt;errors&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;add&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;ss&quot;&gt;:title&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;ss&quot;&gt;:contains_today_i_learned&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;n&quot;&gt;title&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;includes?&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;TIL&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;Using an internationalised message is great for a number of reasons:&lt;/p&gt;

&lt;ol&gt;
  &lt;li&gt;It keeps strings out of your model&lt;/li&gt;
  &lt;li&gt;It allows for custom error messages to be constrained to a single location&lt;/li&gt;
  &lt;li&gt;(The obvious) - it allows error messages to be customized based on the current locale&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Once these custom errors are built into your model, you may want to add a test to assert that the
expected error messages are generated. You can assert against the string itself of course, however
if you run your tests in multiple locales, or just want to keep validation message strings out of
your model TEST as well as your model, the following method can come in handy.&lt;/p&gt;

&lt;p&gt;When you run validations, an instance of &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;ActiveModel::Errors&lt;/code&gt; is available as the return value of
the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;errors&lt;/code&gt; method on your model instance. This instance is normally used for accessing all the
validation errors on a model, but it also has a number of convenience functions - in particular, the
one we’re interested in is called &lt;strong&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;generate_message&lt;/code&gt;&lt;/strong&gt;. This method accepts an attribute, and the
custom error (the symbol passed to the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;message&lt;/code&gt; option of your validation, or a built-in validation
such as &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;presence&lt;/code&gt;, &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;inclusion&lt;/code&gt;, etc., and returns the string form of the message. It will respect
the current setting of &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;I18n.locale&lt;/code&gt; when looking up error strings.&lt;/p&gt;

&lt;p&gt;Here are some examples of looking up errors on a model (the instance of which is called &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;subject&lt;/code&gt; in
this 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;subject&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;errors&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;generate_message&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;ss&quot;&gt;:title&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;ss&quot;&gt;:blank&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; 
&lt;span class=&quot;c1&quot;&gt;# =&amp;gt; &quot;can&apos;t be blank&quot;&lt;/span&gt;

&lt;span class=&quot;n&quot;&gt;subject&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;errors&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;generate_message&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;ss&quot;&gt;:title&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;ss&quot;&gt;:contains_today_i_learned&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;c1&quot;&gt;# =&amp;gt; &quot;must include the category &apos;TIL&apos;&quot;&lt;/span&gt;

&lt;span class=&quot;no&quot;&gt;I18n&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;locale&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;mi-NZ&quot;&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;subject&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;errors&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;generate_message&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;ss&quot;&gt;:title&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;ss&quot;&gt;:contains_today_i_learned&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;c1&quot;&gt;# =&amp;gt; &quot;me whakauru i te tuhinga &apos;TIL&apos;&quot;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;For more information on the I18n translation format, and exactly how these above messages are
resolved, see the &lt;a href=&quot;https://guides.rubyonrails.org/i18n.html#error-message-scopes&quot;&gt;I18n Rails Guide section on error message
scopes&lt;/a&gt;.&lt;/p&gt;

</description>
        <pubDate>Mon, 10 Sep 2018 00:00:00 +1200</pubDate>
        <link>https://joshmcarthur.com/til/2018/09/10/generating-error-messages-using-activemodelerrors.html</link>
        <guid isPermaLink="true">https://joshmcarthur.com/til/2018/09/10/generating-error-messages-using-activemodelerrors.html</guid>
        
        <category>til</category>
        
        <category>rails</category>
        
        <category>technology</category>
        
        
        <category>TIL</category>
        
      </item>
    
      <item>
        <title>Configuring Cloudflare for a Heroku application</title>
        <description>&lt;p&gt;Cloudflare is a brilliant service, and I’ve been using it for personal use for a couple of years
now. In particular, its universal SSL, CDN, and always online capabilities have really come in
handy.&lt;/p&gt;

&lt;p&gt;I use Cloudflare for my personal domain, and frequently build little toy applications that I usually
host on Heroku free tier dynos. Because of how Heroku suggests that DNS
records are set up, there are a couple of tips that I wanted to write about today. Overall, this
post focusses on setting up a application hosted on Heroku on a subdomain. If you also want to have
your application available on the root record of your domain, I outline how to do this at the end of
the post.&lt;/p&gt;

&lt;p&gt;The thing to be aware of with Cloudflare is that it completely takes over your domain to offer
the capabilities it does. This means that your DNS records and managed and resolved by Cloudflare,
as well as caching, SSL, etc. The difference between configuring an app in Cloudflare and with a
“normal” DNS provider is that you do need to get all the settings correct for everything to work
together.&lt;/p&gt;

&lt;p&gt;The first thing to be aware of is where to direct your DNS records towards. If you follow Heroku’s
guide, they will instruct you to specify a CNAME record against the subdomain which you wish to
use, directed at “subdomain.your-domain.herokudns.com” (for example,
“www.joshmcarthur.com.herokudns.com”). This is to allow Heroku to provision and
serve an LetsEncrypt SSL certificate for the domain(s) you have configured. If you direct Cloudflare
towards this domain though, you’re likely to run into redirect loops, or even find that Heroku is
unable to find your app. For your app to resolve correctly, you should instead direct your subdomain
at a CNAME record of your normal .herokuapp.com domain - for example,
“www-joshmcarthur-com.herokuapp.com”.&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/img/posts/cloudflare-heroku-dns.png&quot; alt=&quot;Screenshot of DNS configuration in Cloudflare&quot; /&gt;&lt;/p&gt;

&lt;p&gt;Once your CNAME is correct, you will need to configure SSL for your application. At this point, if
your application is set up to force SSL in production (in Rails, this is &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;config.force_ssl = true&lt;/code&gt;,
in Phoenix this is &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;config :my_app, MyApp.Endpoint, force_ssl: true&lt;/code&gt;), then you might have a redirect loop going on. This is because the default with Cloudflare is to use ‘Flexible’ SSL. This means that the network connection is doing something like:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;“https://test.domain.com” (Cloudflare) -&amp;gt; “http://test-domain-com.herokuapp.com” (Heroku) -&amp;gt; “https://test.domain.com” (Redirect from app forcing SSL) - repeated over and over.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;In other words, flexible SSL will connect to your origin (Heroku), via HTTP. The protocol that your app will see in the headers will be “http”, so it will change the protocol to “https” and redirect - letting the same flow occur over and over again.&lt;/p&gt;

&lt;p&gt;To resolve this problem, simply change the SSL mode (under the “Crypto” tab in Cloudflare) to “Full” - or even “Full (Strict)” if you want to be extra good. The ‘Full’ SSL mode means that Cloudflare will connect to your origin via HTTPS instead of HTTP, and your application will not need to perform a redirect to a secure origin, since the request is already secure. The difference between ‘Full’ and ‘Full (Strict)’ is simply that ‘Full’ accepts any SSL certificate, even if it is self-signed, expired, or has some other problem. ‘Full (Strict)’ requires that the certificate is valid for the domain, is from a trusted issuer, and has not expired.&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/img/posts/cloudflare-heroku-ssl.png&quot; alt=&quot;Screenshot of SSL configuration in Cloudflare&quot; /&gt;&lt;/p&gt;

&lt;p&gt;With these two configurations in place, you should have your application functioning via SSL on your subdomain of choice. You can have a play around with different cache and perfomance settings if you wish, though I’ve found the defaults here to work well for me.&lt;/p&gt;

&lt;p&gt;If you just needed to configure a subdomain here, then you can stop reading - you’re done! If you would like your domain root to also serve your application, there’s just a couple more configurations you should put in place to get this working.&lt;/p&gt;

&lt;p&gt;The first is a DNS change specific to Cloudflare (well, specific-ish, several DNS providers support something similar)
called “CNAME flattening”. CNAME flattening means that you can specify a CNAME record on the root of your domain (this is not normally valid, normally an ‘A’ record is used, and ‘A’ records must be an IP address which Heroku does not support). When a client asks Cloudflare what the domain root record should resolve to, Cloudflare will resolve the domain configured as the CNAME to an IP address, and return this as an ‘A’ record. This allows for autoscaling services like Heroku, Cloudfront, and AWS ELB to be specified as the root record of a domain - all of these services use a full hostname to the endpoint, but autoscale servers in the background. With CNAME flattening, it will not matter what IP address these hostnames have, Cloudflare will figure it out and ‘follow’ them as they scale.&lt;/p&gt;

&lt;p&gt;For your root domain record, the easiest thing you can do is specify a CNAME of the subdomain you
configured earlier - for example setting the CNAME record of “joshmcarthur.com.” to
“www.joshmcarthur.com.”. Setting this value means that if you change where your subdomain points to,
your root record will follow that setting automatically. Once that setting is applied, you should be
able go to your domain, and see your app - still available via SSL. Neat!&lt;/p&gt;

&lt;p&gt;If you’re like me though, you really only want your app to be available on one particular hostname -
normally www.myapp.com. Luckily, Cloudflare supports “Page Rules” which you can use to redirect
requests - this means that you can put a Page Rule in place that will redirect any requests to your
root domain to your subdomain. This redirect will be marked with a 301 Permanent Redirect status,
which means that client browsers will cache and remember to just go straight to your subdomain next
time. To add a Page Rule, go to the “Page Rules” tab, and select the option to create a new rule.
The first thing to provide is the pattern that the URL must match for the rule. You’ll want to enter
&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;mydomain.com/*&lt;/code&gt;, which will match your domain without any subdomain, with any path. You can then
add one or more settings - we will just need one today, but you can add many settings to your rules
to change how different URLs will behave. After clicking on ‘Add Setting’, select a setting type of
‘Forwarding URL’, and select ‘301 - Permanent Redirect’ as the status code. The destination URL
should be your subdomain that you’d like to use, followed by ‘$1’ - the ‘$1’ will be replaced with
the wildcard value you specified in the pattern, which will be the path the client is going to. An
example of a destination URL would be “https://www.joshmcarthur.com/$1”.&lt;/p&gt;

&lt;p&gt;The complete rule definition should look something like:&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/img/posts/cloudflare-heroku-rule.png&quot; alt=&quot;URL forwarding rule definition&quot; /&gt;&lt;/p&gt;

&lt;p&gt;With these steps complete, you’ve achieved the following:&lt;/p&gt;

&lt;ol&gt;
  &lt;li&gt;Heroku app available on a subdomain - e.g. www.joshmcarthur.com -&amp;gt;
www-joshmcarthur-com.herokuapp.com&lt;/li&gt;
  &lt;li&gt;SSL available on both root and subdomain. SSL is forced by the Heroku app.&lt;/li&gt;
  &lt;li&gt;Root domain forwards to www subdomain - e.g. joshmcarthur.com -&amp;gt; 301 www.joshmcarthur.com.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Nice!&lt;/p&gt;
</description>
        <pubDate>Wed, 05 Sep 2018 00:00:00 +1200</pubDate>
        <link>https://joshmcarthur.com/til/2018/09/05/configuring-cloudflare-for-a-heroku-application.html</link>
        <guid isPermaLink="true">https://joshmcarthur.com/til/2018/09/05/configuring-cloudflare-for-a-heroku-application.html</guid>
        
        <category>til</category>
        
        <category>ops</category>
        
        
        <category>TIL</category>
        
      </item>
    
      <item>
        <title>Configuring S3 redirect rules</title>
        <description>&lt;p&gt;S3 supports &lt;a href=&quot;https://docs.aws.amazon.com/AmazonS3/latest/dev/how-to-page-redirect.html&quot;&gt;redirecting content in a number of ways&lt;/a&gt;. 
In most cases, providing an XML-formatted collection of redirect rules is a good way of centralising where content is going.
Using the XML format of redirect rules is also a neat way of extending an S3 bucket with additional
behaviour by redirecting to a Lambda function or similar dynamic generation endpoint.&lt;/p&gt;

&lt;p&gt;Redirection rules are specified in the following basic format:&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;RoutingRules&amp;gt;&lt;/span&gt;
  &lt;span class=&quot;nt&quot;&gt;&amp;lt;RoutingRule&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;nt&quot;&gt;&amp;lt;Condition&amp;gt;&lt;/span&gt;
      &lt;span class=&quot;nt&quot;&gt;&amp;lt;KeyPrefixEquals&amp;gt;&lt;/span&gt;KEY&lt;span class=&quot;nt&quot;&gt;&amp;lt;/KeyPrefixEquals&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;nt&quot;&gt;&amp;lt;/Condition&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;nt&quot;&gt;&amp;lt;Redirect&amp;gt;&lt;/span&gt;
      &lt;span class=&quot;nt&quot;&gt;&amp;lt;Protocol&amp;gt;&lt;/span&gt;PROTOCOL&lt;span class=&quot;nt&quot;&gt;&amp;lt;/Protocol&amp;gt;&lt;/span&gt;
      &lt;span class=&quot;nt&quot;&gt;&amp;lt;HostName&amp;gt;&lt;/span&gt;HOSTNAME&lt;span class=&quot;nt&quot;&gt;&amp;lt;/HostName&amp;gt;&lt;/span&gt;
      &lt;span class=&quot;c&quot;&gt;&amp;lt;!-- This can be left as an empty element to use the current key as the path --&amp;gt;&lt;/span&gt;
      &lt;span class=&quot;nt&quot;&gt;&amp;lt;ReplaceKeyWith&amp;gt;&lt;/span&gt;PATH&lt;span class=&quot;nt&quot;&gt;&amp;lt;/ReplaceKeyWith&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;nt&quot;&gt;&amp;lt;/Redirect&amp;gt;&lt;/span&gt;
  &lt;span class=&quot;nt&quot;&gt;&amp;lt;/RoutingRule&amp;gt;&lt;/span&gt;
&lt;span class=&quot;nt&quot;&gt;&amp;lt;/RoutingRules&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Within &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;&amp;lt;RoutingRules&amp;gt;&lt;/code&gt;, as many rules can be provided as you wish. There are also alternative
conditions available, such as &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;&amp;lt;HttpErrorCodeReturnedEquals&amp;gt;&lt;/code&gt;, which allows for a redirect if the
normal S3 response results in a particular HTTP status code - redirects of this kind can be used in
conjunction with an external application or FaaS platform to dynamically render or return content.
Examples of this I have seen include generating image thumbnails on the fly when they do not already
exist, and redirecting to a full-text search of a static site.&lt;/p&gt;

&lt;p&gt;If you have a large number of routing rules, you can order them such that the highest-priority matches 
will be matched and redirected before more general rules. This allows for quite precise control over
redirects.&lt;/p&gt;

&lt;p&gt;One big gotcha with redirect rules that I found was not really documented anywhere was that the
&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;&amp;lt;KeyPrefixEquals&amp;gt;&lt;/code&gt; must not begin with a leading slash. This seems a bit contrary to the path that
you are trying to match against, but is mostly in keeping with the fact that S3 keys cannot start
with a leading slash. Keep in mind that it is the &lt;em&gt;Key&lt;/em&gt; that S3 is matching against, not the web
path.&lt;/p&gt;

&lt;p&gt;Redirect rules cannot be used out of the box with Cloudfront, if you are using a Cloudfront
distribution to act as a CDN and SSL termination for your S3 website, however with a small
adjustment redirect rules work fine - when you configure the Cloudfront origin, simply provide the
full URL to the S3 website location, rather than selecting your S3 bucket from the typeahead
dropdown. This will cause Cloudfront to treat your S3 website as a “Custom” origin instead of an
“S3” origin, so it will forward the Location header back to the client.&lt;/p&gt;
</description>
        <pubDate>Tue, 04 Sep 2018 00:00:00 +1200</pubDate>
        <link>https://joshmcarthur.com/til/2018/09/04/configuring-s3-redirect-rules.html</link>
        <guid isPermaLink="true">https://joshmcarthur.com/til/2018/09/04/configuring-s3-redirect-rules.html</guid>
        
        <category>til</category>
        
        <category>ops</category>
        
        
        <category>TIL</category>
        
      </item>
    
      <item>
        <title>Test CORS headers with Rails in development</title>
        <description>&lt;p&gt;When testing CORS headers in Rails, an additional step is needed to check that the headers are working correctly.
This is because of a behaviour in Chrome specifically, where requests that are to the same origin do not perform
CORS validation - so it’s very difficult to tell whether CORS is working correctly or not. Chrome does not even appear
to send the ‘Origin’ header for same-origin requests, meaning that CORS headers never show up in the response.&lt;/p&gt;

&lt;p&gt;The way I found of testing this is to start an additional Rails server in development, and then set this as your asset
host. Using the asset host setting means that Rails will prefix URLS to your images, stylesheets, javascripts and other
assets with the asset host, rather than the current request host. Since the host and port are different, this is 
considered a different origin, and so triggers normal CORS behaviour.&lt;/p&gt;

&lt;p&gt;Configure asset host:&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;# config/environments/development.rb&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;application&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;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;asset_host&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;http://localhost:3001&quot;&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;p&gt;Starting the first Rails server:&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;bundle &lt;span class=&quot;nb&quot;&gt;exec &lt;/span&gt;rails s 
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Starting the second Rails server:&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;bundle &lt;span class=&quot;nb&quot;&gt;exec &lt;/span&gt;rails s &lt;span class=&quot;nt&quot;&gt;-p3001&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;--pid&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;tmp/pids/server1.pid
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Then visit http://localhost:3000. If you inspect the network requests, you should see some CORS warnings 
(unless you already have CORS configured correctly of course!).&lt;/p&gt;
</description>
        <pubDate>Mon, 03 Sep 2018 00:00:00 +1200</pubDate>
        <link>https://joshmcarthur.com/til/2018/09/03/test-cors-headers-with-rails-in-development.html</link>
        <guid isPermaLink="true">https://joshmcarthur.com/til/2018/09/03/test-cors-headers-with-rails-in-development.html</guid>
        
        <category>til</category>
        
        <category>rails</category>
        
        
        <category>TIL</category>
        
      </item>
    
      <item>
        <title>Pattern for testing ActiveRecord ideas in a standalone script</title>
        <description>&lt;p&gt;This code snippet is a reusable pattern for testing out a model/association structure
in ActiveRecord to see whether it’s worth proceeding with. The snippet covers installing
necessary dependencies, setting up an in-memory SQLite3 database, and loading a database
schema into this database.&lt;/p&gt;

&lt;p&gt;The example included in the pattern was prepared to demonstrate a has many through association
between users and other users that are followed.&lt;/p&gt;

&lt;script src=&quot;https://gist.github.com/fda80a4b138e393608d9ad43aa093be1.js&quot;&gt; &lt;/script&gt;

</description>
        <pubDate>Sun, 19 Aug 2018 00:00:00 +1200</pubDate>
        <link>https://joshmcarthur.com/til/2018/08/19/activerecord-test-script-snippet.html</link>
        <guid isPermaLink="true">https://joshmcarthur.com/til/2018/08/19/activerecord-test-script-snippet.html</guid>
        
        <category>til</category>
        
        <category>technology</category>
        
        <category>rails</category>
        
        
        <category>TIL</category>
        
      </item>
    
      <item>
        <title>Logging to multiple destinations using ActiveSupport 4+</title>
        <description>&lt;p&gt;Logging to multiple destinations is something I seem to have to do &lt;em&gt;all the time&lt;/em&gt;, and until now,
I’ve never really been able to find an approach I’m happy with. Almost always, I’m writing some kind
of developer-script, and I want to write messages to both the terminal (to let the dev know what is
going on), and to a log file (for future reference/audit). I don’t &lt;em&gt;normally&lt;/em&gt; want to use
&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Rails.logger&lt;/code&gt; for this - I prefer to have a brand new logger specifically for my output. Having
said that, I have verified that the approach described in this post does work with a standard Rails
logger (defined in &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Rails.logger&lt;/code&gt; or &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;config.logger&lt;/code&gt; depending on where you’re trying to work with
it).&lt;/p&gt;

&lt;blockquote&gt;
  &lt;p&gt;This approach relies on a method added to ActiveSupport version ~&amp;gt; 4.0. Versions of ActiveSupport
map directly to Rails versions (ActiveSupport is part of the &lt;a href=&quot;https://github.com/rails/rails/tree/master/activesupport&quot;&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;rails/rails&lt;/code&gt; repository after
all&lt;/a&gt;), so Rails 4+ will also work here.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;When working with logging in Ruby, the
&lt;a href=&quot;https://ruby-doc.org/stdlib-2.5.1/libdoc/logger/rdoc/Logger.html&quot;&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Logger&lt;/code&gt;&lt;/a&gt; class is normally the
one is used. This is completely reasonable, as it’s part of the standard library, and is almost
always sufficient. For those scenarios where something more is needed, &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;ActiveSupport::Logger&lt;/code&gt; is
available. The API for ActiveSupport’s Logger is broadly the same as Ruby’s Logger, with a few
bonuses, such as &lt;a href=&quot;https://api.rubyonrails.org/classes/ActiveSupport/TaggedLogging.html#method-i-tagged&quot;&gt;tagged
logging&lt;/a&gt; and
&lt;em&gt;the ability to extend loggers with other loggers&lt;/em&gt;!&lt;/p&gt;

&lt;p&gt;Extending any logger is possible using the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;extend&lt;/code&gt; and &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;broadcast&lt;/code&gt; method available on
&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;ActiveSupport::Logger&lt;/code&gt;. Unfortunately, I can’t link to documentation for either of these methods,
as they don’t seem to be documented.&lt;/p&gt;

&lt;p&gt;Here’s an example not using any Rails stuff, just ActiveSupport, as an 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;nb&quot;&gt;require&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;active_support/logger&quot;&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;console_logger&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;ActiveSupport&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;no&quot;&gt;Logger&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;STDOUT&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;file_logger&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;ActiveSupport&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;no&quot;&gt;Logger&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;s2&quot;&gt;&quot;my_log.log&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;combined_logger&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;console_logger&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;extend&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;no&quot;&gt;ActiveSupport&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;no&quot;&gt;Logger&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;broadcast&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;file_logger&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;))&lt;/span&gt;

&lt;span class=&quot;n&quot;&gt;combined_logger&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;debug&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;Debug level&quot;&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;combined_logger&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;info&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;Info level&quot;&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;combined_logger&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;error&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;Error level&quot;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;With the console output being:&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;Debug level
Info level
Error level
&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;mylog.log&lt;/code&gt; containing:&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;# Logfile created on 2018-08-16 15:24:10 +1200 by logger.rb/61378
Debug level
Info level
Error level
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Usage in Rails really isn’t that different, the only difference is that you will already have a 
logger set up to log &lt;em&gt;somewhere&lt;/em&gt; defined in &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Rails.logger&lt;/code&gt; (or &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;config.logger&lt;/code&gt; if you are modifying
a config file). You can just go ahead and extend that logger to add another logging destination:&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;# NOTE: The actual config file doesn&apos;t really matter here.&lt;/span&gt;
&lt;span class=&quot;c1&quot;&gt;# config/application.rb&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;extra_logger&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;ActiveSupport&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;no&quot;&gt;Logger&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;s2&quot;&gt;&quot;extra.log&quot;&lt;/span&gt;&lt;span class=&quot;p&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;logger&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;extend&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;no&quot;&gt;ActiveSupport&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;no&quot;&gt;Logger&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;broadcast&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;extra_logger&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;Multiple logging is a handy trick to keep in mind, as there’s a few places it can come in handy. As
mentioned at the start of this post, my usual use case is to log to a file and STDOUT at the same
time, but there’s all sorts of times you might want to post a log message multiple places. Maybe you
want logs to go to a third party service, as well as local log file, as well as STDOUT for good
measure? Sure!&lt;/p&gt;

</description>
        <pubDate>Thu, 16 Aug 2018 00:00:00 +1200</pubDate>
        <link>https://joshmcarthur.com/til/2018/08/16/logging-to-multiple-destinations-using-activesupport-4.html</link>
        <guid isPermaLink="true">https://joshmcarthur.com/til/2018/08/16/logging-to-multiple-destinations-using-activesupport-4.html</guid>
        
        <category>til</category>
        
        <category>technology</category>
        
        <category>rails</category>
        
        
        <category>TIL</category>
        
      </item>
    
      <item>
        <title>Calculating a SHA1 fingerprint for a file in Ruby</title>
        <description>&lt;p&gt;Fingerprinting a file isn’t something I need to do super often, but it is useful to detect duplicates of files, or
verify that the file has not been changed (this can be useful for more than auditing and security - for example, it’s particulalry
useful for caching, which is exactly why Rails’ asset pipeline calculates hash digests for asset files when they are precompiled).&lt;/p&gt;

&lt;p&gt;The manual approach to fingerprinting is to read the contents of the file (perform a buffered read for performance if you do this), but there’s
actually a nice 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;nb&quot;&gt;require&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;digest&quot;&lt;/span&gt;

&lt;span class=&quot;no&quot;&gt;Digest&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;no&quot;&gt;SHA1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;file&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;/path/to/file&quot;&lt;/span&gt;

&lt;span class=&quot;c1&quot;&gt;# Example:&lt;/span&gt;
&lt;span class=&quot;c1&quot;&gt;# fingerprint = Digest::SHA1.file Rails.root.join(&quot;public&quot;, &quot;my-file.txt&quot;).to_s&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

</description>
        <pubDate>Wed, 15 Aug 2018 00:00:00 +1200</pubDate>
        <link>https://joshmcarthur.com/til/2018/08/15/calculating-a-sha1-fingerprint-for-a-file-in-ruby.html</link>
        <guid isPermaLink="true">https://joshmcarthur.com/til/2018/08/15/calculating-a-sha1-fingerprint-for-a-file-in-ruby.html</guid>
        
        <category>til</category>
        
        <category>technology</category>
        
        <category>ruby</category>
        
        
        <category>TIL</category>
        
      </item>
    
      <item>
        <title>ActiveRecord: Mark all records of type as read only</title>
        <description>&lt;p&gt;Note: This post is created based on &lt;a href=&quot;https://stackoverflow.com/a/16923830&quot;&gt;this Stackoverflow Answer&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Occasionally, you may have a type of record that already exists in a database which you wish to read
from, but never modify. In these situations, it can be worth adding a little code to mark these
types of records as read-only. This will prevent &lt;em&gt;most&lt;/em&gt; ActiveRecord data modification methods to
raise an &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;ActiveRecord::ReadOnly&lt;/code&gt; exception if code attempts to create or update such a
record. Note that read only records do not appear to be protected from destruction!&lt;/p&gt;

&lt;p&gt;To mark an individual record as readonly, there is an &lt;a href=&quot;https://apidock.com/rails/ActiveRecord/Core/readonly%21&quot;&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;ActiveRecord::Base&lt;/code&gt;
method&lt;/a&gt; available to help with this. This
post concerns marking an entire type of model as readonly. Examples of when this may be necessary
include integrating with an external or third-party database, migrating content from one table to
another, and supporting shared or private data.&lt;/p&gt;

&lt;p&gt;There are two approaches I have seen for marking an entire type of record as readonly, affecting
every instance.&lt;/p&gt;

&lt;p&gt;The first is the one I prefer, as it doesn’t rely on ActiveRecord callbacks:&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;MyModel&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;c1&quot;&gt;##&lt;/span&gt;
  &lt;span class=&quot;c1&quot;&gt;# Mark all instances of this type of record as readonly.&lt;/span&gt;
  &lt;span class=&quot;c1&quot;&gt;# The logic for this method can be changed if more complicated logic is required.&lt;/span&gt;
  &lt;span class=&quot;k&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;readonly?&lt;/span&gt;
    &lt;span class=&quot;kp&quot;&gt;true&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;If you prefer a simpler implementation and don’t mind callbacks, the following implementation also
works:&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;MyModel&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;after_initialize&lt;/span&gt; &lt;span class=&quot;ss&quot;&gt;:readonly!&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;

</description>
        <pubDate>Wed, 15 Aug 2018 00:00:00 +1200</pubDate>
        <link>https://joshmcarthur.com/til/2018/08/15/activerecord-mark-all-records-of-type-as-read-only.html</link>
        <guid isPermaLink="true">https://joshmcarthur.com/til/2018/08/15/activerecord-mark-all-records-of-type-as-read-only.html</guid>
        
        <category>til</category>
        
        <category>rails</category>
        
        <category>technology</category>
        
        
        <category>TIL</category>
        
      </item>
    
      <item>
        <title>ActiveRecord&apos;s establish_connection</title>
        <description>&lt;p&gt;If you’ve ever wondered how your Rails ActiveRecord models end up magically translating some connection details (probably you have these in &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;config/database.yml&lt;/code&gt;) into data you can interact with, &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;establish_connection&lt;/code&gt; is a pretty significant part of that magic. In this post, I’m going to be laying out the different arguments this method accepts to determine how to connect to a database. Just so you know, this is largely a paraphrasing of &lt;a href=&quot;https://apidock.com/rails/ActiveRecord/ConnectionHandling/establish_connection&quot;&gt;the APIDock documentation&lt;/a&gt; to reinforce these arguments in my memory, so if you would prefer to directly read the documentation, go ahead.&lt;/p&gt;

&lt;h2 id=&quot;normal-operation&quot;&gt;Normal operation&lt;/h2&gt;

&lt;p&gt;Your standard Rails set up has your database connection settings listed per-environment in &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;config/database.yml&lt;/code&gt;. You probably at least have settings in this file for &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;development&lt;/code&gt; and &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;test&lt;/code&gt;, but may have other environments there as well. The normal definition of a model (e.g. what you get if your application models inherit from &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;ActiveRecord::Base&lt;/code&gt; and don’t change any settiings) is to call &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;establish_connection&lt;/code&gt;, passing a symbolized version of &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Rails.env&lt;/code&gt; - &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;:development&lt;/code&gt;, &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;:test&lt;/code&gt; and so on.&lt;/p&gt;

&lt;p&gt;When &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;establish_connection&lt;/code&gt; is passed a symbol, it will assume that the symbol is a “configuration”. A configuration in this context is a key of the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;ActiveRecord::Base.configurations&lt;/code&gt; hash, where the value contains the actual connection settings for your database. When Rails starts, it loads and parses your &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;config/database.yml&lt;/code&gt; YAML file into this hash, which is exactly how your Rails app knows how to connect to your development, test or even production database.&lt;/p&gt;

&lt;h2 id=&quot;passing-connection-information-via-a-hash&quot;&gt;Passing connection information via a hash&lt;/h2&gt;

&lt;p&gt;This argument is the lowest level form of argument that can be passed in to &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;establish_connection&lt;/code&gt;.
A hash is passed in, containing (at least) a key specifying the ““adapter”. Any additional options
are delegated to the particular adapter - MySQL, PostgreSQL, SQLite, etc - however, many of these options
are common across database engines - option names like “user”, “host”, “password” and “database”.
This is why, if you do ever change from one database to another, often the only key that needs to be
changed is “adapter”.&lt;/p&gt;

&lt;p&gt;This hash is accessed with indifferent access, which means that you can pass in hash keys as either
symbols or strings - either is acceptable. A situation where you might use this kind of argument to 
establish a database connection could be a scenario where you have dynamic connection details -
perhaps you have a multi tenanted application where customers can provide their own database to
use, for exmaple.&lt;/p&gt;

&lt;p&gt;An example of passing a hash to &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;establish_connection&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;ActiveRecord&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;no&quot;&gt;Base&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;establish_connection&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;
  &lt;span class=&quot;ss&quot;&gt;adapter: &lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;postgresql&quot;&lt;/span&gt;
  &lt;span class=&quot;ss&quot;&gt;database: &lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;myapp_development_1&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;h2 id=&quot;passing-connection-information-via-a-connection-uri-string&quot;&gt;Passing connection information via a connection URI (string)&lt;/h2&gt;

&lt;p&gt;Finally, a database URL can be passed to &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;establish_connection&lt;/code&gt;, which will detect the correct
adapter to use from the connection string, and parse other connection details, such as the host,
port, username, password and database to use from the URI. The default of &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;establish_connection&lt;/code&gt; is
to try and find a URI to a databse in an environment variable named &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;&quot;DATABASE_URL&quot;&lt;/code&gt;, falling back
to looking up a configuration matching the current Rails environment name (as described in ‘Normal
operation’).&lt;/p&gt;

&lt;p&gt;Passing a connection string to &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;establish_connection&lt;/code&gt; is quite a flexible way of establishing
connections to different databases, and can be used in a number of scenarios where you might
establish to different databases, either when the app starts, or during runtime.&lt;/p&gt;

&lt;p&gt;An example of passing a connection string to &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;establish_connection&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;ActiveRecord&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;no&quot;&gt;Base&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;establish_connection&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;postgres://postgres@localhost:5432/mydatabase&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;blockquote&gt;
  &lt;p&gt;Note: For actual applications, it would be more appropriate to store this connection 
string as configuration, or even as a secret, rather than hardcoding it into your script.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;hr /&gt;

&lt;p&gt;I wanted to blog about this particular ActiveRecord for a couple of reasons. First, it is actually a
method that is very widely used, but probably not very well known. Second, while I knew of the
existence of this method, and suspected some of what the method could do, I learned a lot while
having a read through the documentation and implementation of &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;establish_connection&lt;/code&gt;. It’s certainly
one worth remembering.&lt;/p&gt;
</description>
        <pubDate>Tue, 14 Aug 2018 00:00:00 +1200</pubDate>
        <link>https://joshmcarthur.com/til/2018/08/14/activerecords-establish_connection.html</link>
        <guid isPermaLink="true">https://joshmcarthur.com/til/2018/08/14/activerecords-establish_connection.html</guid>
        
        <category>til</category>
        
        <category>technology</category>
        
        
        <category>TIL</category>
        
      </item>
    
      <item>
        <title>Certbot renewals with Route53</title>
        <description>&lt;p&gt;This is a bit of documentation for the next time I run into this problem, however you
might find it useful.&lt;/p&gt;

&lt;p&gt;The &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;certbot&lt;/code&gt; and &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;certbot-auto&lt;/code&gt; commands support plugins, but do not come with many out of the
box. The installation method for plugins (certbot being written in Python), is &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;pip&lt;/code&gt;, however
certbot may or may not pick up plugins installed in this manner, depending on the environment.&lt;/p&gt;

&lt;p&gt;I had certbot installed in Mac OS for example, and it could not find the certbot-dns-route53 package
I installed with &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;pip&lt;/code&gt;. The &lt;a href=&quot;https://certbot.eff.org/docs/using.html#dns-plugins&quot;&gt;documentation&lt;/a&gt; suggests
that if you run into problems installing plugins, to run with Docker - however no such instructions for &lt;em&gt;how&lt;/em&gt;
to install these plugins in the official Docker images are included - &lt;a href=&quot;https://github.com/certbot/certbot/issues/4875&quot;&gt;An issue has been raised on github&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;I do prefer to run commands that have dependencies like these in Docker anyway, so these are the steps I followed
to address my specific problem. Note that at one point, I do hit this problem with rather a large hammer (mounting my entire &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;/Users&lt;/code&gt; directory as a volume) - if this upsets you, feel free to find another path forward.&lt;/p&gt;

&lt;p&gt;First, my environment:&lt;/p&gt;

&lt;ol&gt;
  &lt;li&gt;I have all of my projects in my home directory, namespaced by repository host, user or organisation name, then the repo name. In this case, I was renewing the cert for the &lt;a href=&quot;https://blog.pawfit.nz&quot;&gt;Pawfit Blog&lt;/a&gt;, so my code repository was located in: /Users/josh/Projects/gitlab.com/joshmcarthur/pawfit-blog.&lt;/li&gt;
  &lt;li&gt;I keep the letsencrypt file tree in this repo under “certs”, with things like private keys ignored (the repo is private but still). This just makes it easier to do renewals and see waht I did last time to fix certs). This file tree is what you would normally find under “/etc/letsencrypt”.&lt;/li&gt;
  &lt;li&gt;I have valid AWS credentials set up in my Mac OS home directory, inside the default “.aws” folder.&lt;/li&gt;
  &lt;li&gt;I have an expiring certificate inside the aforementioned “certs” directory.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;How I renewed the certificate:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;Start the docker container: &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;docker run --rm -it --entrypoint=sh -v /Users/josh:/Users/josh certbot/cerbot&lt;/code&gt;. This opens a shell
session inside the container. The &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;/Users/josh&lt;/code&gt; volume mount was required because certbot sets up relative symlinks
from the “live” certificate files to the “archive” folder. When Docker mounts the volume, these seem to be converted
to absolute symlinks.&lt;/li&gt;
  &lt;li&gt;Install the Route53 plugin: &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;pip install certbot-dns-route53&lt;/code&gt;. This &lt;em&gt;should&lt;/em&gt; work outside of docker, but may not
depending on how you have Python, Pip, and certbot installed (i.e. it didn’t work for me).&lt;/li&gt;
  &lt;li&gt;Symlink the AWS credentials folder from your host environment into the container’s home directory - this is so boto3
(which certbot-dns-route53 uses to connect to AWS) can resolve your AWS access keys. There are also a number of other
ways you can provide boto3 with AWS credentials, such as environment variables.&lt;/li&gt;
  &lt;li&gt;Change directory to the project directory: `cd
/Users/josh/Projects/gitlab.com/joshmcarthur/pawfit-blog”. See above for information about where I put code and
certs, as this might be different for you.&lt;/li&gt;
  &lt;li&gt;Run certbot! &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;certbot renew --config-dir=certs --work-dir=certs&lt;/code&gt;&lt;/li&gt;
  &lt;li&gt;You can now exit the container if you would like (by typing &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;exit&lt;/code&gt;), or use &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;cat&lt;/code&gt; to extract the information you need
from the new certs. I exited at this point, as I wanted to use the Mac OS-native &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;pbcopy&lt;/code&gt; command to pipe the output
of &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;cat&lt;/code&gt; to (e.g. &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;cat certs/live/blog.pawfit.nz/fullchain.pem | pbcopy&lt;/code&gt;).&lt;/li&gt;
&lt;/ul&gt;

</description>
        <pubDate>Sun, 12 Aug 2018 00:00:00 +1200</pubDate>
        <link>https://joshmcarthur.com/til/2018/08/12/certbot-renewals-with-route53.html</link>
        <guid isPermaLink="true">https://joshmcarthur.com/til/2018/08/12/certbot-renewals-with-route53.html</guid>
        
        <category>til</category>
        
        <category>technology</category>
        
        <category>ops</category>
        
        
        <category>TIL</category>
        
      </item>
    
      <item>
        <title>Internationalised views with Rails</title>
        <description>&lt;p&gt;Rails has great built in support for internationalisation. Generally, many people, including myself, end up focussing their efforts on internatising strings - for example, changing form labels, errors, and maybe some minor copy like headings, using &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;I18n.t&lt;/code&gt; (an alias for &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;translate&lt;/code&gt;), which looks up which string to return for the current locale (determined from &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;I18n.locale&lt;/code&gt;, which can be set from a cookie, geo lookup, param, or whatever).&lt;/p&gt;

&lt;p&gt;I’ve recently been working on an app that’s pretty well established in NZ, and is moving some operations to Australia. As part of this move, they have a bunch of content that needs to be worded slightly differently (and some behaviour in core models that has to work differently based on the locale, but that’s a story for another blog post). Something that often gets skipped over in internationalisation is the ability for Rails to automatically serve templates for the current locale.&lt;/p&gt;

&lt;p&gt;The &lt;a href=&quot;http://guides.rubyonrails.org/i18n.html#localized-views&quot;&gt;Rails Guides&lt;/a&gt; lay out localised views with the following description:&lt;/p&gt;

&lt;blockquote&gt;
  &lt;p&gt;Let’s say you have a BooksController in your application. Your index action renders content in app/views/books/index.html.erb template. When you put a localized variant of this template: index.es.html.erb in the same directory, Rails will render content in this template, when the locale is set to :es. When the locale is set to the default locale, the generic index.html.erb view will be used. (Future Rails versions may well bring this automagic localization to assets in public, etc.)&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;This is certainly true, but it’s worth exploring a little more. First of all, while the example uses Español as the locale (determined by the es locale code), any locale that your Rails app is aware of will work just the same. For my example, I have two locales within my application - “en-NZ” and “en-AU”. This means that I can keep my existing templates named as they are (e.g. “app/views/books/index.html.erb”), and provide a new template containing the copy for Australia in “app/views/books/index.en-AU.html.erb”). When the locale is set to the default of NZ, the default template will be used. When the locale is set to en-AU, the matching template will take precedence and will be used instead.&lt;/p&gt;

&lt;p&gt;The main thing to watch out for with localised views is the extent to which you are duplicating content. It’s quite simple to copy and paste a file, add the locale to the filename, and change a couple of words in the template - but this isn’t particularly maintainable if your template also has a bunch of content that does not require translation. To avoid this problem, carefully consider your template structure. If you have a section of a template that needs to be adjusted for each locale, consider refactoring that content into a partial. If the partial itself ends up particularly simple, you might then refactor to go back to a simple string lookup. Whichever approach you end up with, always try and reduce the content requiring per-locale translation to the smallest unit. This ensures that you have minimal duplication between views, while still providing all of the advantages.&lt;/p&gt;
</description>
        <pubDate>Thu, 21 Jun 2018 00:00:00 +1200</pubDate>
        <link>https://joshmcarthur.com/til/2018/06/21/internationalised-views-with-rails.html</link>
        <guid isPermaLink="true">https://joshmcarthur.com/til/2018/06/21/internationalised-views-with-rails.html</guid>
        
        <category>til</category>
        
        <category>technology</category>
        
        <category>rails</category>
        
        
        <category>TIL</category>
        
      </item>
    
      <item>
        <title>Running a command on a server directly from SSH</title>
        <description>&lt;p&gt;The SSH command accepts a very useful argument that I think is underused by many devops folks looking to run a quick command and get on with their day.&lt;/p&gt;

&lt;p&gt;The output of the synopsis of the SSH command man page (&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;man ssh&lt;/code&gt;) ends with:&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;[user@]hostname [command]
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;This &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;[command]&lt;/code&gt; is the useful bit - you can pop any command you like here, and SSH will connect and authenticate to the server, run the command (directing IO to your client as normal), and then exit. This makes it great for standalone tasks that can just…run, for example:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;ssh user@host df -h&lt;/code&gt; - to show the disk usage on a server&lt;/li&gt;
  &lt;li&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;ssh user@host ls -lah&lt;/code&gt; - to list the contents of the authenticating user’s home directory&lt;/li&gt;
  &lt;li&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;ssh user@host &quot;cd /u/app &amp;amp;&amp;amp; bundle exec rake db:migrate&quot;&lt;/code&gt; - to change into a Rails app working directory and run migrations&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Taking little shortcuts with common commands like this can seem kind of simple, but I’ve found that the time I save really adds up. You can also make use of the command argument for scripting - for example, to automate updates or provisioning.&lt;/p&gt;
</description>
        <pubDate>Wed, 20 Jun 2018 00:00:00 +1200</pubDate>
        <link>https://joshmcarthur.com/til/2018/06/20/running-a-command-on-a-server-directly-from-ssh.html</link>
        <guid isPermaLink="true">https://joshmcarthur.com/til/2018/06/20/running-a-command-on-a-server-directly-from-ssh.html</guid>
        
        <category>til</category>
        
        <category>technology</category>
        
        <category>ops</category>
        
        
        <category>TIL</category>
        
      </item>
    
      <item>
        <title>Extracting XML data with curl and xmllint</title>
        <description>&lt;p&gt;(I start off talking about &lt;em&gt;why&lt;/em&gt; I was doing this. You can &lt;a href=&quot;#tip&quot;&gt;jump to the tip&lt;/a&gt; if you want.)&lt;/p&gt;

&lt;p&gt;I use &lt;a href=&quot;https://www.home-assistant.io/&quot;&gt;HomeAssistant&lt;/a&gt; heavily at home for converging a range
of smart home data from sensors around my house, as well as from a range of open data sources.&lt;/p&gt;

&lt;p&gt;HomeAssistant has a REST sensor which is good for a known data source with a friendly format, but, because of the architecture of HomeAssistant, this sensor is not particularly flexible to use when the data is coming from a third-party in a format that isn’t readily usable. For more complex data consumption, a command line sensor may be appropriate. A command line sensor captures the output of running a command as the sensor value. The trick with this is to use &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;curl&lt;/code&gt; and some kind of data parsing routing to extract the data you actually want as your sensor value. That’s what I’m going over.&lt;/p&gt;

&lt;div id=&quot;tip&quot;&gt;&lt;/div&gt;

&lt;p&gt;I used two utilities for this - &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;curl&lt;/code&gt;, and &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;xmllint&lt;/code&gt;. &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;curl&lt;/code&gt; is preinstalled a lot these days, but if you don’t have it, it’s a simple &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;brew install curl&lt;/code&gt;/&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;apt-get install curl&lt;/code&gt;, etc. away. &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;xmllint&lt;/code&gt; is a little harder. It’s technically either part of, or strongly related to &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;libxml2&lt;/code&gt; (it seems to be a reference or demo of how to use libxml2?). I have it on Mac OS by having &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;libxml2&lt;/code&gt; installed using Homebrew, but on Ubuntu Server I had to &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;apt-get install libxml2-utils&lt;/code&gt;. YMMV, but I suspect that &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;-utils&lt;/code&gt; package is required in most Linux distributions. Once you &lt;em&gt;do&lt;/em&gt; have it though, you can take advantage of Unix pipes to pluck out the XML you need using XPath.&lt;/p&gt;

&lt;p&gt;Here’s an example that I’ll break down:&lt;/p&gt;

&lt;div class=&quot;language-sh 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;s2&quot;&gt;&quot;http://api.opendata.tld/example.xml&quot;&lt;/span&gt; |&lt;span class=&quot;se&quot;&gt;\&lt;/span&gt;
  xmllint &lt;span class=&quot;nt&quot;&gt;--xpath&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;//Measurements/Data/Reading[last()]/Value/text()&quot;&lt;/span&gt; -
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;First of all, &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;curl &quot;http://api.opendata.tld/example.xml&quot;&lt;/code&gt;. This requests the given URI, streaming the response to the pipe. For the sake of the further examples, let’s assume it returns XML data in the following format:&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;Measurements&amp;gt;&lt;/span&gt;
  &lt;span class=&quot;nt&quot;&gt;&amp;lt;Author&amp;gt;&lt;/span&gt;Data Publisher&lt;span class=&quot;nt&quot;&gt;&amp;lt;/Author&amp;gt;&lt;/span&gt;
  &lt;span class=&quot;nt&quot;&gt;&amp;lt;Data&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;DateFormat=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;Calendar&quot;&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;NumItems=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;1&quot;&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;nt&quot;&gt;&amp;lt;Reading&amp;gt;&lt;/span&gt;
      &lt;span class=&quot;nt&quot;&gt;&amp;lt;Time&amp;gt;&lt;/span&gt;2018-06-19T08:45:00&lt;span class=&quot;nt&quot;&gt;&amp;lt;/Time&amp;gt;&lt;/span&gt;
      &lt;span class=&quot;nt&quot;&gt;&amp;lt;Value&amp;gt;&lt;/span&gt;11.8&lt;span class=&quot;nt&quot;&gt;&amp;lt;/Value&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;nt&quot;&gt;&amp;lt;/Reading&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;nt&quot;&gt;&amp;lt;Reading&amp;gt;&lt;/span&gt;
      &lt;span class=&quot;nt&quot;&gt;&amp;lt;Time&amp;gt;&lt;/span&gt;2018-06-19T09:00:00&lt;span class=&quot;nt&quot;&gt;&amp;lt;/Time&amp;gt;&lt;/span&gt;
      &lt;span class=&quot;nt&quot;&gt;&amp;lt;Value&amp;gt;&lt;/span&gt;11.8&lt;span class=&quot;nt&quot;&gt;&amp;lt;/Value&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;nt&quot;&gt;&amp;lt;/Reading&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;nt&quot;&gt;&amp;lt;Reading&amp;gt;&lt;/span&gt;
      &lt;span class=&quot;nt&quot;&gt;&amp;lt;Time&amp;gt;&lt;/span&gt;2018-06-19T09:15:00&lt;span class=&quot;nt&quot;&gt;&amp;lt;/Time&amp;gt;&lt;/span&gt;
      &lt;span class=&quot;nt&quot;&gt;&amp;lt;Value&amp;gt;&lt;/span&gt;11.8&lt;span class=&quot;nt&quot;&gt;&amp;lt;/Value&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;nt&quot;&gt;&amp;lt;/Reading&amp;gt;&lt;/span&gt;
  &lt;span class=&quot;nt&quot;&gt;&amp;lt;/Data&amp;gt;&lt;/span&gt;
&lt;span class=&quot;nt&quot;&gt;&amp;lt;/Measurements&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;The second part of this command is the pipe to &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;xmllint&lt;/code&gt;. &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;xmllint&lt;/code&gt; is passed two key arguments. The first, &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;--xpath&lt;/code&gt;, contains an &lt;a href=&quot;https://developer.mozilla.org/en-US/docs/Web/XPath&quot;&gt;XPath expression&lt;/a&gt; for waht to select, and the trailing dash (&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;-&lt;/code&gt;) prompts &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;xmllint&lt;/code&gt; to accept XML from STDIN (the pipe from curl).&lt;/p&gt;

&lt;p&gt;Given a valid XPath expression, the result of this command will be exactly what is required - a value to assign to the sensor. In this case, the XPath expression will be interpreted as:&lt;/p&gt;

&lt;ol&gt;
  &lt;li&gt;Start at the root &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Measurements&lt;/code&gt; node&lt;/li&gt;
  &lt;li&gt;Traverse down the tree into &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Data&lt;/code&gt;&lt;/li&gt;
  &lt;li&gt;Select the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;last()&lt;/code&gt; &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Reading&lt;/code&gt; node&lt;/li&gt;
  &lt;li&gt;Traverse into the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Value&lt;/code&gt; node&lt;/li&gt;
  &lt;li&gt;Select the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;text()&lt;/code&gt; (inner text) of the node&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;In other words, the complete command:&lt;/p&gt;

&lt;div class=&quot;language-sh 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;s2&quot;&gt;&quot;http://api.opendata.tld/example.xml&quot;&lt;/span&gt; |&lt;span class=&quot;se&quot;&gt;\&lt;/span&gt;
  xmllint &lt;span class=&quot;nt&quot;&gt;--xpath&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;//Measurements/Data/Reading[last()]/Value/text()&quot;&lt;/span&gt; -
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Will return…..&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;11.8&lt;/code&gt;. Exactly what we need!&lt;/p&gt;

&lt;p&gt;We can now define our HomeAssistant sensor:&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;sensor&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;platform&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;command_line&lt;/span&gt;
    &lt;span class=&quot;na&quot;&gt;command&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;curl&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt; &lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\&quot;&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;http://api.opendata.tld/example.xml&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\&quot;&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s&quot;&gt;|&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s&quot;&gt;xmllint&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s&quot;&gt;--xpath&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt; &lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\&quot;&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;//Measurements/Data/Reading[last()]/Value/text()&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\&quot;&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s&quot;&gt;-&quot;&lt;/span&gt;
    &lt;span class=&quot;na&quot;&gt;unit_of_measurement&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;C&quot;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
</description>
        <pubDate>Tue, 19 Jun 2018 00:00:00 +1200</pubDate>
        <link>https://joshmcarthur.com/til/2018/06/19/extracting-xml-data-with-curl-and-xmllint.html</link>
        <guid isPermaLink="true">https://joshmcarthur.com/til/2018/06/19/extracting-xml-data-with-curl-and-xmllint.html</guid>
        
        <category>til</category>
        
        <category>technology</category>
        
        
        <category>TIL</category>
        
      </item>
    
      <item>
        <title>Migrate on push with Heroku releases</title>
        <description>&lt;p&gt;I use Heroku for most of my hosting, and also use it a lot in my work at &lt;a href=&quot;https://www.rabidtech.co.nz&quot;&gt;Rabid Technologies&lt;/a&gt;. Something I frequently run into is a migration not being run after a &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;git push heroku master&lt;/code&gt;, often causing an embarrasing application error page until someone can get to running &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;heroku run rake db:migrate&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Fortunately, Heroku supports &lt;a href=&quot;https://devcenter.heroku.com/articles/release-phase&quot;&gt;release commands&lt;/a&gt;. These commands are run after each successful deployment, and also delay the restart of the dyno(s). This allows for any post-code-update steps to be taken before the application is updated for users.&lt;/p&gt;

&lt;p&gt;Declaring release commands is very simple - a line simply needs to be added to the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Procfile&lt;/code&gt; stating which commands should be run upon release. For example, this is the line I use to run migrations:&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;# Procfile
# ...
# release: bundle exec rake db:migrate
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Adding this to your Procfile will change your post-receive hook output from Heroku slightly, as it will now output the status of the release command:&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;remote: -----&amp;gt; Launching...        
remote:  !     Release command declared: this new release will not be available until the command succeeds.        
remote:        Released v160        
remote:        https://myapp.herokuapp.com/ deployed to Heroku        
remote: 
remote: Verifying deploy... done.        
remote: Running release command....
remote: 
remote:    (0.6ms)  SELECT pg_try_advisory_lock(7931432920140052365)        
remote:    (1.2ms)  SELECT &quot;schema_migrations&quot;.&quot;version&quot; FROM &quot;schema_migrations&quot; ORDER BY &quot;schema_migrations&quot;.&quot;version&quot; ASC        
remote:   ActiveRecord::InternalMetadata Load (0.7ms)  SELECT  &quot;ar_internal_metadata&quot;.* FROM &quot;ar_internal_metadata&quot; WHERE &quot;ar_internal_metadata&quot;.&quot;key&quot; = $1 LIMIT $2  [[&quot;key&quot;, &quot;environment&quot;], [&quot;LIMIT&quot;, 1]]        
remote:    (0.7ms)  BEGIN        
remote:    (0.5ms)  COMMIT        
remote:    (0.9ms)  SELECT pg_advisory_unlock(7931432920140052365)        
remote: Waiting for release.... done. 
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;I find that this functionality is particularly useful for continuous deployment setup, as it only requires &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;git&lt;/code&gt; to be installed to perform a Heroku deployment AND automatically run migrations. Without this functionality, it is slightly harder, as the deploy needs to happen in two steps - one to do the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;git push&lt;/code&gt;, and then another to do the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;heroku run&lt;/code&gt;. This process also requires the Heroku CLI to be installed and authenticated, which isn’t always the easiest thing in CI environments.&lt;/p&gt;
</description>
        <pubDate>Thu, 31 May 2018 00:00:00 +1200</pubDate>
        <link>https://joshmcarthur.com/til/2018/05/31/migrate-on-push-with-heroku-releases.html</link>
        <guid isPermaLink="true">https://joshmcarthur.com/til/2018/05/31/migrate-on-push-with-heroku-releases.html</guid>
        
        <category>til</category>
        
        <category>technology</category>
        
        
        <category>TIL</category>
        
      </item>
    
      <item>
        <title>Simple Ruby API clients with HTTParty</title>
        <description>&lt;p&gt;I frequently write code that interacts with a third-party HTTP API. When I create this code, I find the best approach is to create a very thin abstraction layer over the API that can easily be stubbed out under test. If the data returned from the API is complex or requires further processing, I occasionally provide additional abstraction layers on top of the HTTP client, but frequently, just having a consistent API client is enough.&lt;/p&gt;

&lt;p&gt;In terms of creating this client, there are many, many options in Ruby, ranging from a relatively complicated approach by using the Ruby standard library &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;net/http&lt;/code&gt; module, up to a series of gems that either abstract this API or provide another HTTP interface altogether. An example of a gem that abstracts across the HTTP driver, and the one I’m going to be discussing today is &lt;a href=&quot;https://github.com/jnunemaker/httparty&quot;&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;httparty&lt;/code&gt;&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Despite having a &lt;a href=&quot;https://github.com/jnunemaker/httparty/blob/6888e5343aaac2dab3aac1afa05615cdec9587f5/httparty.gemspec#L21&quot;&gt;stupid post-install message that can’t (easily) be turned off&lt;/a&gt;, HTTParty provides a very easy way of defining an API client.&lt;/p&gt;

&lt;p&gt;Here’s an example of an API client I put together recently:&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;httparty&quot;&lt;/span&gt;

&lt;span class=&quot;k&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;MyAwesomeAPI&lt;/span&gt;
  &lt;span class=&quot;kp&quot;&gt;include&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;HTTParty&lt;/span&gt;
  &lt;span class=&quot;nb&quot;&gt;format&lt;/span&gt; &lt;span class=&quot;ss&quot;&gt;:json&lt;/span&gt;

  &lt;span class=&quot;k&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;initialize&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;username&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;api_key&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;vi&quot;&gt;@auth&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;username: &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;username&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;ss&quot;&gt;password: &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;api_key&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;nf&quot;&gt;class&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;base_uri&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;https://api.myawesomeapi.com&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;def&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;get&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;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;options&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;default_request_options&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;merge&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;nb&quot;&gt;self&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;class&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;get&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;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;kp&quot;&gt;private&lt;/span&gt;

  &lt;span class=&quot;k&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;default_request_options&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt; 
      &lt;span class=&quot;ss&quot;&gt;headers: &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;Accept&quot;&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;application/vnd.myawesomeapi.v3+json&quot;&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;},&lt;/span&gt; 
      &lt;span class=&quot;ss&quot;&gt;basic_auth: &lt;/span&gt;&lt;span class=&quot;vi&quot;&gt;@auth&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 API client:&lt;/p&gt;

&lt;ol&gt;
  &lt;li&gt;Will automatically serialize and deserialize requests using JSON encoding&lt;/li&gt;
  &lt;li&gt;Defines the base URI in a single place&lt;/li&gt;
  &lt;li&gt;Adds basic authentication to the request&lt;/li&gt;
  &lt;li&gt;Adds an outgoing header to each request defining the mimetype of content it will accept - in this case, a versioned custom type.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;In the case of this API, I only required &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;GET&lt;/code&gt; access to resources, so that’s all I’ve defined. It’s entirely possible to implement other HTTP methods in a very similar manner, or even to provide further abstractions, such as methods named after the API endpoint in question. As an example, if this API allowed for a listing of “widgets”, an &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;all_widgets&lt;/code&gt; method could be defined that returned an array of &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Widget&lt;/code&gt; instances created from the JSON response.&lt;/p&gt;

&lt;p&gt;HTTParty has &lt;a href=&quot;https://www.rubydoc.info/github/jnunemaker/httparty/HTTParty/ClassMethods&quot;&gt;many options&lt;/a&gt; for customizing how the request and response will be handled, but at the end of the day, what it provides most of all is a simple, abstract, class that allows for an API interface to be quickly defined that will Just Work.&lt;/p&gt;
</description>
        <pubDate>Tue, 29 May 2018 00:00:00 +1200</pubDate>
        <link>https://joshmcarthur.com/til/2018/05/29/simple-ruby-api-clients-with-httparty.html</link>
        <guid isPermaLink="true">https://joshmcarthur.com/til/2018/05/29/simple-ruby-api-clients-with-httparty.html</guid>
        
        <category>til</category>
        
        <category>technology</category>
        
        <category>rails</category>
        
        
        <category>TIL</category>
        
      </item>
    
      <item>
        <title>Git commit message templates</title>
        <description>&lt;p&gt;Writing high-quality git commit messages is one of the most responsible things you can do as a developer who cares to write considerate code that is well thought-out and easy to maintain. There’s a great template for git commit messages that’s floating around in a number of places. It’s referred to the “Contributing to a Project” chapter of the &lt;a href=&quot;https://git-scm.com/book/en/v2/Distributed-Git-Contributing-to-a-Project&quot;&gt;Git Book&lt;/a&gt; and originally credited to &lt;a href=&quot;https://tbaggery.com/2008/04/19/a-note-about-git-commit-messages.html&quot;&gt;Tim Pope&lt;/a&gt;. In short, a good template of a commit message is a short, one-liner summary of the code change, followed by the ‘body’ of the commit. The body of the commit may contain whichever links, bullet point or other emphasis required to communicate to the commit message reader the full context of the change.&lt;/p&gt;

&lt;p&gt;While this is a good day-to-day commit message template to follow, there are some situations which might require additional contextual prompts to result in all the necessary information being communicated. Examples of such scenarios include:&lt;/p&gt;

&lt;ol&gt;
  &lt;li&gt;A commit resolving a bug ticket, where consistently communicating the ticket number, the URL of the ticket, and the requester.&lt;/li&gt;
  &lt;li&gt;A commit layout out a particular architectural decision in a clear format, such as the model layed out in &lt;a href=&quot;https://www.infoq.com/articles/sustainable-architectural-design-decisions&quot;&gt;Sustainable Architectural Design Decisions&lt;/a&gt;.&lt;/li&gt;
  &lt;li&gt;A commit implementing an agile story, where the developer may want to lay out the role and motivation of the feature.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;While it’s entirely posible to write these git commits ad-hoc as required, an additional option I discovered today was the ability to customize which template file is loaded when committing. As laid out on the &lt;a href=&quot;https://git-scm.com/docs/git-commit&quot;&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;git-commit man page&lt;/code&gt;&lt;/a&gt;, this option is:&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;-t &amp;lt;file&amp;gt;
--template=&amp;lt;file&amp;gt;
  When editing the commit message, start the editor with the
  contents in the given file.The commit.template configuration
  variable is often used to give this option implicitly to the
  command. This mechanism can be used by projects that want to
  guide participants with some hints on what to write in the
  message in what order. If the user exits the editor without
  editing the message, the commit is aborted. This has no effect
  when a message is given by other means,
  e.g. with the -m or -F options.
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;The trick is to combine this option with the ability to set up &lt;a href=&quot;https://git-scm.com/book/en/v2/Git-Basics-Git-Aliases&quot;&gt;Git aliases&lt;/a&gt; to make it easy to start commits using a particular template, for example:&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;git config --global alias.bugfix-commit &apos;commit --template=~/dotfiles/git/templates/bugfix-commit.txt&apos;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Given a template that exists in that location, your &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;$EDITOR&lt;/code&gt; will open with the contents of the template prefilled. Any lines beginning with a pound &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;#&lt;/code&gt; can be used to provide instructions to the committer, and will be ignored from the final commit message.&lt;/p&gt;

&lt;p&gt;As a final example, this is how I have set up a template for committing at key decision points:&lt;/p&gt;

&lt;p&gt;My template in ~/.dotfiles/git/templates/commit-decision.txt:&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;# Write a useful commit message here:

# Now provide more information about why you&apos;ve made this
# decision:
In the context of [USE CASE],
facing [CONCERN]
we decided for [OPTION]
to achieve [QUALITY],
accepting [DOWNSIDES].
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;My git alias:&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;git config --global alias.commit-decision &apos;commit --template=~/.dotfiles/git/templates/commit-decision.txt&apos;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Usage:&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;http://g.recordit.co/JIqKkTuI8X.gif&quot; alt=&quot;Gif example&quot; /&gt;&lt;/p&gt;
</description>
        <pubDate>Mon, 28 May 2018 00:00:00 +1200</pubDate>
        <link>https://joshmcarthur.com/til/2018/05/28/git-commit-message-templates.html</link>
        <guid isPermaLink="true">https://joshmcarthur.com/til/2018/05/28/git-commit-message-templates.html</guid>
        
        <category>til</category>
        
        <category>technology</category>
        
        <category>git</category>
        
        
        <category>TIL</category>
        
      </item>
    
      <item>
        <title>URI.regexp in Ruby</title>
        <description>&lt;p&gt;Recognizing or validating URIs in a string or strings of text is a fairly common problem. 
Unfortunately, just as common is the range of regular expressions that exist to validate URIs
(or, generally, URLs). There are many, many StackOverflow answers and blog posts that lay out
a massive variant of expressions in function and quality, and it’s not the easist form to understand.&lt;/p&gt;

&lt;p&gt;Fortunately, the Ruby programming language comes with a very capable regexp for parsing URIs - it’s just
a matter of knowing where to look. Where to look, in this case, is the &lt;a href=&quot;https://docs.ruby-lang.org/en/2.4.0/URI.html#method-c-regexp&quot;&gt;URI module&lt;/a&gt;.  &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;URI.regexp&lt;/code&gt; will match most valid URIs, using &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;=~&lt;/code&gt;, &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;scan&lt;/code&gt;, &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;gsub&lt;/code&gt;, or any other string method that deals with regexp matching. Additionally, an array of schemes can be passed into the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;regexp&lt;/code&gt; method, which will restrict the returned regexp to match these schemes only. This regexp is particularly useful as the capture groups that are returned include the parsed components of the URI, allowing for very precise control of how the string is processed from there.&lt;/p&gt;

&lt;p&gt;Examples:&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;irb&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;main&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;):&lt;/span&gt;&lt;span class=&quot;mo&quot;&gt;001&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;o&quot;&gt;&amp;gt;&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;regexp&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;match&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;https://joshmcarthur.com/posts?page=2#top&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;).&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;to_a&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;https://joshmcarthur.com/posts?page=2#top&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;https&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;kp&quot;&gt;nil&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;kp&quot;&gt;nil&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;joshmcarthur.com&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;kp&quot;&gt;nil&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;kp&quot;&gt;nil&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;/posts&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;page=2&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;top&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;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;irb&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;main&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;):&lt;/span&gt;&lt;span class=&quot;mo&quot;&gt;00&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;9&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;o&quot;&gt;&amp;gt;&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;regexp&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;([&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;ftp&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;].&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;match&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;https://joshmcarthur.com/posts?page=2#top&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;).&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;to_a&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;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
</description>
        <pubDate>Wed, 23 May 2018 00:00:00 +1200</pubDate>
        <link>https://joshmcarthur.com/til/2018/05/23/uri-regexp-in-ruby.html</link>
        <guid isPermaLink="true">https://joshmcarthur.com/til/2018/05/23/uri-regexp-in-ruby.html</guid>
        
        <category>til</category>
        
        <category>technology</category>
        
        <category>rails</category>
        
        
        <category>TIL</category>
        
      </item>
    
      <item>
        <title>TIL: Begin/rescue/end assignment in Ruby</title>
        <description>&lt;p&gt;Today, while working on consuming a third-party API, I discovered that the result of a begin/rescue/end block in Ruby can assign to a variable, just like an &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;if&lt;/code&gt; statement can.&lt;/p&gt;

&lt;p&gt;This isn’t a technique I’d recommend for every single situation where it &lt;em&gt;could&lt;/em&gt; be used, but it is convenient for operations where a particular error scenario can result in a single return value.&lt;/p&gt;

&lt;p&gt;Here’s an example of reading a remote file and assigning it to an object. If the file operation fails (in this case, if the HTTP request fails with anything other than a 2xx or 3xx status), a fallback value is assigned instead:&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;ostruct&quot;&lt;/span&gt;
&lt;span class=&quot;nb&quot;&gt;require&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;open-uri&quot;&lt;/span&gt;

&lt;span class=&quot;n&quot;&gt;model&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;OpenStruct&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;model&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;title&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;begin&lt;/span&gt;
  &lt;span class=&quot;nb&quot;&gt;open&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;https://gist.githubusercontent.com/joshmcarthur/69cffbb395fc3781e23b56d9281f6f73/raw/7b31ea464799077cddd79cfe22105d7364497cdd/title.txt&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;rescue&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;OpenURI&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;no&quot;&gt;HTTPError&lt;/span&gt;
  &lt;span class=&quot;s2&quot;&gt;&quot;Default Title&quot;&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;

&lt;span class=&quot;nb&quot;&gt;puts&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;model&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;title&lt;/span&gt;
&lt;span class=&quot;c1&quot;&gt;# =&amp;gt; This is a title from a remote file&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

</description>
        <pubDate>Tue, 22 May 2018 00:00:00 +1200</pubDate>
        <link>https://joshmcarthur.com/til/2018/05/22/til-beginrescueend-assignment-in-ruby.html</link>
        <guid isPermaLink="true">https://joshmcarthur.com/til/2018/05/22/til-beginrescueend-assignment-in-ruby.html</guid>
        
        <category>til</category>
        
        <category>technology</category>
        
        <category>ruby</category>
        
        
        <category>TIL</category>
        
      </item>
    
      <item>
        <title>TIL how to fork a Heroku application</title>
        <description>&lt;p&gt;Tonight I had a need to create a duplicate of a running app on Heroku, and discovered &lt;a href=&quot;https://github.com/heroku/heroku-fork&quot;&gt;heroku-fork&lt;/a&gt;. This is a plugin which was extracted from the core Heroku CLI some time ago, but is easily installable using the command &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;heroku plugins:install heroku-fork&lt;/code&gt;. It is run via:&lt;/p&gt;

&lt;p&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;heroku fork --from [app name] --to [app name, can be new app]&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;The command adds functionality which can copy one app to another, including&lt;br /&gt;
code, addons, dyno configuration, and databases. In my case, I used it to quickly test some changes to a service worker against a clone of a production application before pushing these changes live. It no more than a couple of minutes to copy the application, and then I simply destroyed it when I was done.&lt;/p&gt;

&lt;p&gt;A super handy trick that I’ll have to keep in mind for future support work.&lt;/p&gt;
</description>
        <pubDate>Thu, 17 May 2018 00:00:00 +1200</pubDate>
        <link>https://joshmcarthur.com/til/2018/05/17/til-how-to-fork-a-heroku-application.html</link>
        <guid isPermaLink="true">https://joshmcarthur.com/til/2018/05/17/til-how-to-fork-a-heroku-application.html</guid>
        
        <category>til</category>
        
        <category>technology</category>
        
        <category>deployment</category>
        
        
        <category>TIL</category>
        
      </item>
    
      <item>
        <title>TIL how to italicize comments in VS Code</title>
        <description>&lt;p&gt;Continuing my transformation of my development environment to match Wes Bos’, today I discovered from a &lt;a href=&quot;https://stackoverflow.com/a/46278282&quot;&gt;Stack Overflow answer&lt;/a&gt; how to change how comments in any language are rendered in VS Code.&lt;/p&gt;

&lt;p&gt;It turns out that there is a setting to control it, it’s just deeply nested. The necessary snippet for user settings is:&lt;/p&gt;

&lt;div class=&quot;language-json highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;nl&quot;&gt;&quot;editor.tokenColorCustomizations&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;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
    &lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;textMateRules&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;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;w&quot;&gt;
            &lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;scope&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;span class=&quot;s2&quot;&gt;&quot;comment&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;span class=&quot;nl&quot;&gt;&quot;settings&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;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
                &lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;fontStyle&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;span class=&quot;s2&quot;&gt;&quot;italic&quot;&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;w&quot;&gt;
        &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;w&quot;&gt;
&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;(You can access user settings under “Code &amp;gt; Preferences &amp;gt; Settings” on Mac).&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/img/posts/vscode-italic-comments.png&quot; alt=&quot;Screenshot demo of italic comments&quot; /&gt;&lt;/p&gt;

&lt;p&gt;It turns out that VS Code is yet another editor to &lt;a href=&quot;https://code.visualstudio.com/docs/extensions/themes-snippets-colorizers&quot;&gt;support&lt;/a&gt; themes and colour highlighting rules in TextMate format. Once we’re into the correct setting context, it’s just a case of targeting the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;&quot;comment&quot;&lt;/code&gt; scope and instructing the editor to render comments in italic. This should work for a bunch of other style properties as well, if there are other adjustments you’d like to make.&lt;/p&gt;
</description>
        <pubDate>Tue, 15 May 2018 00:00:00 +1200</pubDate>
        <link>https://joshmcarthur.com/til/2018/05/15/til-how-to-italicize-comments-in-vs-code.html</link>
        <guid isPermaLink="true">https://joshmcarthur.com/til/2018/05/15/til-how-to-italicize-comments-in-vs-code.html</guid>
        
        <category>til</category>
        
        <category>techology</category>
        
        <category>tools</category>
        
        
        <category>TIL</category>
        
      </item>
    
      <item>
        <title>TIL about console.table</title>
        <description>&lt;p&gt;I’m working through Wes Bos’ &lt;a href=&quot;https://es6.io/&quot;&gt;ES6 for Everyone&lt;/a&gt;, and he’s dropped yet another useful tip for me to build into my day-to-day work - &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;console.table&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;console.table&lt;/code&gt; supports both objects and arrays, displaying data in a table format that is much
easier to read than simply logging the data:&lt;/p&gt;

&lt;p&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;console.table(user)&lt;/code&gt;
&lt;img src=&quot;/img/posts/console-table-object.png&quot; alt=&quot;Output of `console.table` with an object data structure&quot; /&gt;&lt;/p&gt;

&lt;p&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;console.table(repos)&lt;/code&gt;
&lt;img src=&quot;/img/posts/console-table-array.png&quot; alt=&quot;Output of `console.table` with an array data structure&quot; /&gt;&lt;/p&gt;

&lt;p&gt;To restrict which columns are shown for a collection of objects, a second argument can be passed to restrict which property names are shown:&lt;/p&gt;

&lt;p&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;console.table(repos, [&quot;name&quot;, &quot;description&quot;])&lt;/code&gt;
&lt;img src=&quot;/img/posts/console-table-array-columns.png&quot; alt=&quot;Output of `console.table` with an array data structure and restricted columns&quot; /&gt;&lt;/p&gt;

&lt;p&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;console.table&lt;/code&gt; &lt;a href=&quot;https://developer.mozilla.org/en-US/docs/Web/API/Console/table&quot;&gt;appears to be supported&lt;/a&gt; in all modern browsers, so it’s ready to go for any development workflow!&lt;/p&gt;
</description>
        <pubDate>Fri, 04 May 2018 00:00:00 +1200</pubDate>
        <link>https://joshmcarthur.com/til/2018/05/04/til-about-consoletable.html</link>
        <guid isPermaLink="true">https://joshmcarthur.com/til/2018/05/04/til-about-consoletable.html</guid>
        
        <category>til</category>
        
        <category>techology</category>
        
        <category>tooling</category>
        
        
        <category>TIL</category>
        
      </item>
    
      <item>
        <title>Rails console --sandbox</title>
        <description>&lt;p&gt;Occasionally, a particular bug or customer query will necessitate jumping into a Rails console connected to a live database, or a copy of the live database, that we wish to read from, but not change.&lt;/p&gt;

&lt;p&gt;I was reminded today of an option that can be passed to the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;rails console&lt;/code&gt; command, &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;-s&lt;/code&gt;, or &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;--sandbox&lt;/code&gt;. This wraps the entire console command in a database transaction that will be rolled back when the console is terminated.&lt;/p&gt;

&lt;p&gt;The implementation of the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;--sandbox&lt;/code&gt; option is actually very simple. When the console starts, &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;begin_transaction&lt;/code&gt; is called on the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;ActiveRecord::Base&lt;/code&gt; connection. A hook is set up to trap &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;at_exit&lt;/code&gt;, which will rollback any changes made within the sandbox. Here’s the implementation:&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;ActiveRecord&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;no&quot;&gt;Base&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;connection&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;begin_transaction&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;ss&quot;&gt;joinable: &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;nb&quot;&gt;at_exit&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;do&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;Base&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;connection&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;rollback_transaction&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;(&lt;a href=&quot;https://github.com/rails/rails/blob/45ddb7f1f94c2086488aaaac47a1092cf832f381/activerecord/lib/active_record/railties/console_sandbox.rb&quot;&gt;source&lt;/a&gt;)&lt;/p&gt;

&lt;p&gt;Effectively, this means that when a &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;rails console --sandbox&lt;/code&gt; command is run, records can be added, removed, changed (and anything else that is processed inside a &lt;a href=&quot;https://www.postgresql.org/docs/current/static/tutorial-transactions.html&quot;&gt;database transaction&lt;/a&gt; can be done), without it affecting any other part of the Rails application.&lt;/p&gt;
</description>
        <pubDate>Thu, 03 May 2018 00:00:00 +1200</pubDate>
        <link>https://joshmcarthur.com/til/2018/05/03/rails-console-sandbox.html</link>
        <guid isPermaLink="true">https://joshmcarthur.com/til/2018/05/03/rails-console-sandbox.html</guid>
        
        <category>til</category>
        
        <category>technology</category>
        
        <category>rails</category>
        
        
        <category>TIL</category>
        
      </item>
    
      <item>
        <title>What I learned from Command Line Power User</title>
        <description>&lt;p&gt;My tech education has been almost exclusively Wes Bos based at the moment - I’ve finished React for Beginners, am halfway through his ES6 course, have Redux queued up, and am keeping an eye on his progress with his advanced React course. I’m also a listener of the podcast he produces along with Scott Tolinski, &lt;a href=&quot;https://syntax.fm&quot;&gt;Syntax&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;As a short break from ES6, I powered through a slightly older course, &lt;a href=&quot;https://commandlinepoweruser.com/&quot;&gt;Command Line Power User&lt;/a&gt;. Because of how long I’ve been working in a shell, most of the lessons Wes had to share I had already learned the hard way - but I did pick up a couple of tips that I’m posting about today.&lt;/p&gt;

&lt;h3 id=&quot;1-ls--m&quot;&gt;1. &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;ls -m&lt;/code&gt;&lt;/h3&gt;

&lt;p&gt;This flag can be used to change the format of the directory listing from a column-based view to instead of be a comma-separated list of files and directories. I can see this being useful for passing into scripts in the future, or at least providing a list of directory contents without all the surrounding formatting and whitespace.&lt;/p&gt;

&lt;p&gt;This flag was shown as a demonstration of ZSH’s argument tab-completion, and it looks like there’s a bunch of other arguments I’ll have to check out in more depth.&lt;/p&gt;

&lt;h3 id=&quot;2-trash&quot;&gt;2. &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;trash&lt;/code&gt;&lt;/h3&gt;

&lt;p&gt;I think everyone’s had a little &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;rm&lt;/code&gt; whoopsie before. I know I have. One other tip that came up was having a command to send files and directories to a trash folder, rather than directly removing them. Seems obvious now, but I’d never thought of doing that. Wes even suggests aliasing &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;rm&lt;/code&gt; to a &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;trash&lt;/code&gt; command, which is a great idea.&lt;/p&gt;

&lt;p&gt;It looks as though there is an &lt;a href=&quot;https://www.npmjs.com/package/trash&quot;&gt;NPM package for a &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;trash&lt;/code&gt; command&lt;/a&gt;. I’m going to do a bit more investigation here, as, since I work exclusively on my Macbook, I might be able to find something more lightweight than a Node.js library.&lt;/p&gt;

&lt;h3 id=&quot;3-the-z-command&quot;&gt;3. The &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;z&lt;/code&gt; command&lt;/h3&gt;

&lt;p&gt;The &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;z&lt;/code&gt; command is a ZSH script that can be dropped in to provide “frecent” jumping to directories (frecent being “frequent” and/or “recent” directories). This looked interesting, and I might try it out sometime, but I’ve been using &lt;a href=&quot;https://github.com/wting/autojump&quot;&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;autojump&lt;/code&gt;&lt;/a&gt; for some time, and it seems to have a similar feature set - so I don’t really have a reason to switch, other than &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;z&lt;/code&gt; being written purely in ZSH scripting language (looks like autojump is Python).&lt;/p&gt;

&lt;h3 id=&quot;4-the-take-command&quot;&gt;4. The &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;take&lt;/code&gt; command&lt;/h3&gt;

&lt;p&gt;I’ve got to try and build this one into my muscle memory, because I would use it all the time.&lt;/p&gt;

&lt;p&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;take&lt;/code&gt; is a command built into &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;zsh.&lt;/code&gt;. It’s literally:&lt;/p&gt;

&lt;div class=&quot;language-sh highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;take &lt;span class=&quot;o&quot;&gt;()&lt;/span&gt; &lt;span class=&quot;o&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; &lt;span class=&quot;nv&quot;&gt;$1&lt;/span&gt;
	&lt;span class=&quot;nb&quot;&gt;cd&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;$1&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;p&gt;…in other words, make a directory (or directory tree), and immediately change into it. Fantastic!&lt;/p&gt;

&lt;h3 id=&quot;5-the-extract-plugin&quot;&gt;5. The &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;extract&lt;/code&gt; plugin&lt;/h3&gt;

&lt;p&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;extract&lt;/code&gt; is a plugin available as part of &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;oh-my-zsh&lt;/code&gt;. It simply acts as an abstraction over the many, many archiving formats available, delegating to the appropriate utility (tar, ar, zip, etc). I’ve run into frustration before trying to get the right set of utilities and arguments to extract an archive, so I’ve also added this to my oh-my-zsh plugins!&lt;/p&gt;

&lt;hr /&gt;

&lt;p&gt;Those were my takeaways, but the whole course was really interesting. I was pleased to see how well my current setup aligns with the productive setup demonstrated in the course - I’m also a ZSH user, though I only use a fraction of it’s features. I jumped on the &lt;a href=&quot;http://github.com/robbyrussell/oh-my-zsh&quot;&gt;oh-my-zsh&lt;/a&gt; bandwagon when it was released - mostly for the pretty themes of course, and eventually even ended up creating &lt;a href=&quot;https://github.com/joshmcarthur/dotfiles/tree/master/zsh&quot;&gt;my own small set of plugins and a theme&lt;/a&gt;. The only difference from the course content was that I have begun using antigen to control how much of oh-my-zsh is loaded when my &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;~/.zshrc&lt;/code&gt; is sourced into my shell, as I found the default oh-my-zsh install method was getting a little slow to initialize.&lt;/p&gt;
</description>
        <pubDate>Wed, 02 May 2018 00:00:00 +1200</pubDate>
        <link>https://joshmcarthur.com/til/2018/05/02/what-i-learned-from-command-line-power-user.html</link>
        <guid isPermaLink="true">https://joshmcarthur.com/til/2018/05/02/what-i-learned-from-command-line-power-user.html</guid>
        
        <category>til</category>
        
        <category>tools</category>
        
        <category>technology</category>
        
        
        <category>TIL</category>
        
      </item>
    
      <item>
        <title>TIL: Debugging Net::HTTP</title>
        <description>&lt;p&gt;Today I had to debug a &lt;a href=&quot;https://github.com/ambethia/recaptcha&quot;&gt;library&lt;/a&gt; to try and determine why a particular HTTP request was failing. The problem was, this particular library uses Net::HTTP, without &lt;a href=&quot;https://github.com/ambethia/recaptcha/blob/7ed15d060186c1465948aa2f17f338ba001027d8/lib/recaptcha.rb#L62&quot;&gt;any particular hooks&lt;/a&gt; to customise how the request will be executed.&lt;/p&gt;

&lt;p&gt;I discovered the following handy code snippet at &lt;a href=&quot;https://gist.github.com/ahoward/736721&quot;&gt;https://gist.github.com/ahoward/736721&lt;/a&gt;, which forces debug output to be on for any instance of Net::HTTP created. This snippet can be pasted into an initializer or even an IRB/pry debugging session to enable debug logging of Net::HTTP to STDERR:&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;BEGIN&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;s1&quot;&gt;&apos;net/http&apos;&lt;/span&gt;

  &lt;span class=&quot;no&quot;&gt;Net&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;no&quot;&gt;HTTP&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;module_eval&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;do&lt;/span&gt;
    &lt;span class=&quot;kp&quot;&gt;alias_method&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&apos;__initialize__&apos;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&apos;initialize&apos;&lt;/span&gt;

    &lt;span class=&quot;k&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;initialize&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;*&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;args&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;amp;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;block&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
      &lt;span class=&quot;n&quot;&gt;__initialize__&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;*&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;args&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&amp;amp;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;block&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;ensure&lt;/span&gt;
      &lt;span class=&quot;vi&quot;&gt;@debug_output&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;vg&quot;&gt;$stderr&lt;/span&gt; &lt;span class=&quot;c1&quot;&gt;### if ENV[&apos;HTTP_DEBUG&apos;]&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;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;I &lt;em&gt;wouldn’t&lt;/em&gt; recommend this code for any deployed environment, since it’s monkeypatching a pretty core Ruby class. It’s a great debugging tool though, if you’re not too sure how to get to a particular HTTP request.&lt;/p&gt;
</description>
        <pubDate>Wed, 02 May 2018 00:00:00 +1200</pubDate>
        <link>https://joshmcarthur.com/til/2018/05/02/til-debugging-nethttp.html</link>
        <guid isPermaLink="true">https://joshmcarthur.com/til/2018/05/02/til-debugging-nethttp.html</guid>
        
        <category>til</category>
        
        <category>technology</category>
        
        <category>ruby</category>
        
        <category>rails</category>
        
        
        <category>TIL</category>
        
      </item>
    
      <item>
        <title>TIL: Cherry picking without a commit</title>
        <description>&lt;p&gt;Cherry-picking git commits isn’t part of my day-to-day workflow, but 
I do need to do it occasionally. Generally, and in the case I ran into today, I had a range of commits that required cherry-picking from &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;master&lt;/code&gt; onto a long-lived feature branch.&lt;/p&gt;

&lt;p&gt;Normally cherry-picking in git will take each commit and “copy” it onto the current branch. This is generally fine, but in the case where you might be cherry-picking a larger range of commits, or you find that the message against the commit being cherry-picked isn’t adequately descriptive of the commit in the context of the branch, the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;-n&lt;/code&gt; flag can come in handy.&lt;/p&gt;

&lt;p&gt;The &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;-n&lt;/code&gt; flag is described in the manual:&lt;/p&gt;

&lt;blockquote&gt;
  &lt;p&gt;-n, –no-commit
          Usually the command automatically creates a sequence of commits. This flag applies the changes
          necessary to cherry-pick each named commit to your working tree and the index, without making any
          commit.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;In other words, using &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;git cherry-pick -n&lt;/code&gt; will cherry-pick a commit across into the branch, &lt;em&gt;without committing&lt;/em&gt;. This allows for the changes to be committed differently - maybe you want to ‘squash’ all of the changes into a single commit, or rephrase one or more of the commit messages - all of this is quite simple with a no commit cherry-pick, since while the changes are applied by git, none of the changes are recorded as actual commits - how the committing is done is entirely up to you.&lt;/p&gt;
</description>
        <pubDate>Mon, 23 Apr 2018 00:00:00 +1200</pubDate>
        <link>https://joshmcarthur.com/til/2018/04/23/til-cherry-picking-without-a-commit.html</link>
        <guid isPermaLink="true">https://joshmcarthur.com/til/2018/04/23/til-cherry-picking-without-a-commit.html</guid>
        
        <category>til</category>
        
        <category>technology</category>
        
        <category>git</category>
        
        
        <category>TIL</category>
        
      </item>
    
      <item>
        <title>HTML5 hidden attribute on option tags</title>
        <description>&lt;p&gt;This morning I learned about the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;hidden&lt;/code&gt; attribute that can be added 
to &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;&amp;lt;option&amp;gt;&lt;/code&gt; tags within a &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;&amp;lt;select&amp;gt;&lt;/code&gt; tag. The hidden attribute causes the option to simply not be shown in the list. Hidden elements will are also not selectable and will not return any selected value.&lt;/p&gt;

&lt;p&gt;The &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;hidden&lt;/code&gt; attribute works in &lt;a href=&quot;https://caniuse.com/#feat=hidden&quot;&gt;most browsers&lt;/a&gt;, with ~97.5% of browsers supported. For IE10 and below (depending on the browser breakdown of your application), an acceptable fallback would be to also add the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;disabled&lt;/code&gt; attribute to the option. The browser in this case would still show the option, but it would be greyed out and not selectable.&lt;/p&gt;

&lt;p&gt;As an example, this 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;nt&quot;&gt;&amp;lt;select&amp;gt;&lt;/span&gt;
  &lt;span class=&quot;nt&quot;&gt;&amp;lt;option&amp;gt;&lt;/span&gt;First option&lt;span class=&quot;nt&quot;&gt;&amp;lt;/option&amp;gt;&lt;/span&gt;
  &lt;span class=&quot;nt&quot;&gt;&amp;lt;option&amp;gt;&lt;/span&gt;Second option&lt;span class=&quot;nt&quot;&gt;&amp;lt;/option&amp;gt;&lt;/span&gt;
  &lt;span class=&quot;nt&quot;&gt;&amp;lt;option&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;hidden&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;disabled&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;&amp;gt;&lt;/span&gt;Third option&lt;span class=&quot;nt&quot;&gt;&amp;lt;/option&amp;gt;&lt;/span&gt;
&lt;span class=&quot;nt&quot;&gt;&amp;lt;/select&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Yields the following result:&lt;/p&gt;

&lt;p&gt;&lt;select&gt;
  &lt;option&gt;First option&lt;/option&gt;
  &lt;option&gt;Second option&lt;/option&gt;
  &lt;option hidden=&quot;&quot; disabled=&quot;&quot;&gt;Third option&lt;/option&gt;
&lt;/select&gt;&lt;/p&gt;

&lt;p&gt;And in IE10:&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/img/posts/select-hidden-attribute.png&quot; width=&quot;473&quot; alt=&quot;Demo of select `hidden` attribute in IE10&quot; /&gt;&lt;/p&gt;

</description>
        <pubDate>Sun, 15 Apr 2018 00:00:00 +1200</pubDate>
        <link>https://joshmcarthur.com/til/2018/04/15/html5-hidden-attribute-on-option-tags.html</link>
        <guid isPermaLink="true">https://joshmcarthur.com/til/2018/04/15/html5-hidden-attribute-on-option-tags.html</guid>
        
        <category>til</category>
        
        <category>technology</category>
        
        <category>html5</category>
        
        
        <category>TIL</category>
        
      </item>
    
      <item>
        <title>TIL: ActionMailer Preview</title>
        <description>&lt;p&gt;It’s been a while since I’ve worked with transactional email directly with Rails. This is because I’ve previously been working on some larger projects that have justified the use of an external mailing system like Mandrill, where email templates were used to generate email within that system, and sending an email from the Rails app was just an API call.&lt;/p&gt;

&lt;p&gt;Recently, I’ve been working on applications for smaller businesses, and that’s a problem I approach quite differently. Smaller businesses really need something that will work, and continue to work, with very little ongoing investment. Because of this, when I’ve got the need to send transactional email for a client like this, I go back to the bedrock - SMTP and ActionMailer.&lt;/p&gt;

&lt;p&gt;Because I haven’t worked with ActionMailer for some time, I seem to have missed a fantastic feature that I just learned about today. It’s even in the &lt;a href=&quot;http://guides.rubyonrails.org/action_mailer_basics.html#previewing-emails&quot;&gt;Guides&lt;/a&gt;, so I’m annoyed at myself for missing it!&lt;/p&gt;

&lt;p&gt;It turns out, since Rails 4.1, there is built-in support for rendering either an HTML or plain-text version of an ActionMailer template, right in a web browser. This allows for easy iterative development and testing, and since the preview follows exactly the same code paths that sending does (up to actual delivery of course!), you can see the final product in the browser - with inlined CSS, rewritten markup, and any other enhancements you’ve included.&lt;/p&gt;

&lt;p&gt;Writing a preview is very easy - so easy in fact that Rails will do it for you by default with &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;rails generate mailer&lt;/code&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;&lt;span class=&quot;o&quot;&gt;=&amp;gt;&lt;/span&gt; rails generate mailer user
      create  app/mailers/user_mailer.rb
      invoke  erb
      create    app/views/user_mailer
      create    app/views/layouts/mailer.text.erb
      create    app/views/layouts/mailer.html.erb
      invoke  rspec
      create    spec/mailers/user_spec.rb
      create    spec/mailers/previews/user_preview.rb
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;The preview class is very simple - one or more methods can be defined, with each method expected to invoke a corresponding mailer method. The preview class can take whatever steps it needs to to set up the data required to prepare a particular email, so long as the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Mail&lt;/code&gt; object returned from a mailer call is returned from the method.&lt;/p&gt;

&lt;p&gt;Here’s an example of a preview class:&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;UserMailerPreview&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&amp;lt;&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;ActionMailer&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;no&quot;&gt;Preview&lt;/span&gt;
  &lt;span class=&quot;k&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;confirmation&lt;/span&gt;
    &lt;span class=&quot;no&quot;&gt;UserMailer&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;confirmation&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;no&quot;&gt;FactoryBot&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;build&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;ss&quot;&gt;:user&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;))&lt;/span&gt;
  &lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;

  &lt;span class=&quot;k&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;password_reset&lt;/span&gt;
    &lt;span class=&quot;no&quot;&gt;UserMailer&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;password_reset&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;no&quot;&gt;FactoryBot&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;build&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;ss&quot;&gt;:user&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;Rails creates a simple navigation structure for previews - going to &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;http://localhost:3000/rails/mailers&lt;/code&gt; will serve an index of all the available preview classes, and then going to a particular preview name (in the example above, &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;http://localhost:3000/rails/mailers/user&lt;/code&gt;) will serve an index of all the mailer methods available for testing:&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/img/posts/actionmailer-preview-listing.png&quot; alt=&quot;Mailer preview index listing&quot; /&gt;&lt;/p&gt;

&lt;p&gt;Clicking on a particular mailer method will present the email within the web browser, showing the email metadata (recipient, subject, bcc etc) at the top of the page, with the HTML or plain text version selectable to show in the bottom. Updating the mailer template or mailer itself will be autoloaded as usual, so the mailer preview can just be refreshed to see the latest change.&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/img/posts/actionmailer-preview-display-html.png&quot; alt=&quot;Mailer preview display&quot; /&gt;
&lt;img src=&quot;/img/posts/actionmailer-preview-display-text.png&quot; alt=&quot;Mailer preview display&quot; /&gt;&lt;/p&gt;

&lt;p&gt;This is a great feature built into Rails, and certainly something I’ve tried to emulate before with tools such as Mailcatcher and letter_opener. Finding little pieces of functionality built into the framework like this is always great, and continues to encourage me to keep an eye on the changelog!&lt;/p&gt;

</description>
        <pubDate>Thu, 12 Apr 2018 00:00:00 +1200</pubDate>
        <link>https://joshmcarthur.com/til/2018/04/12/til-actionmailer-preview.html</link>
        <guid isPermaLink="true">https://joshmcarthur.com/til/2018/04/12/til-actionmailer-preview.html</guid>
        
        <category>til</category>
        
        <category>technology</category>
        
        <category>rails</category>
        
        
        <category>TIL</category>
        
      </item>
    
      <item>
        <title>TIL: toLocaleString() for currency formatting</title>
        <description>&lt;p&gt;Yesterday I learned about &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;toLocaleString&lt;/code&gt;, a Javascript function defined on the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Number&lt;/code&gt; prototype since ECMAScript 3. It has had a couple of iterations of arguments, but the version supported by most browsers provides a very convenient API for formatting currency in a user’s locale:&lt;/p&gt;

&lt;h2 id=&quot;examples&quot;&gt;Examples:&lt;/h2&gt;

&lt;blockquote&gt;
  &lt;p&gt;Note: Wherever I have passed &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;undefined&lt;/code&gt; as the first argument to &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;toLocaleString&lt;/code&gt;, this simply means that I am expressing no preference for a particular locale. In this case, the browser will use the system locale.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h4 id=&quot;format-an-amount-in-usd-using-the-users-locale-en-nz-for-me&quot;&gt;Format an amount in USD using the user’s locale (en-NZ for me)&lt;/h4&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;var&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;number&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;1234567&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;number&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;toLocaleString&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;p&quot;&gt;{&lt;/span&gt; 
    &lt;span class=&quot;na&quot;&gt;style&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;currency&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;currency&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;USD&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;c1&quot;&gt;// =&amp;gt; US$1,234.57&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h4 id=&quot;format-an-amount-in-usd-using-a-defined-locale&quot;&gt;Format an amount in USD using a defined locale&lt;/h4&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;var&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;number&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;mf&quot;&gt;1234.567&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;number&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;toLocaleString&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;en-US&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;style&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;currency&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;currency&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;USD&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;c1&quot;&gt;// =&amp;gt; $1,234.57&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h4 id=&quot;format-an-amount-in-nzd-using-a-defined-locale&quot;&gt;Format an amount in NZD using a defined locale&lt;/h4&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;var&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;number&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;mf&quot;&gt;1234.567&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;number&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;toLocaleString&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;en-US&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;style&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;currency&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;currency&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;NZD&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;c1&quot;&gt;// =&amp;gt; NZD$1,234.57&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h4 id=&quot;format-an-amount-in-nzd-using-european-formatting&quot;&gt;Format an amount in NZD using European formatting&lt;/h4&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;var&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;number&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;mf&quot;&gt;1234.567&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;number&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;toLocaleString&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;de-DE&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;style&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;currency&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;currency&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;NZD&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;c1&quot;&gt;// =&amp;gt; NZD$1.234,57&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h4 id=&quot;format-an-amount-in-pounds-rounded-to-a-whole-number&quot;&gt;Format an amount in Pounds, rounded to a whole number&lt;/h4&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;var&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;number&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;mf&quot;&gt;1234.567&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;number&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;toLocaleString&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;p&quot;&gt;{&lt;/span&gt; 
    &lt;span class=&quot;na&quot;&gt;style&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;currency&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;currency&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;GBP&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;minimumFractionDigits&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;na&quot;&gt;maximumFractionDigits&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;p&quot;&gt;);&lt;/span&gt;
&lt;span class=&quot;c1&quot;&gt;// =&amp;gt; £1,235&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h4 id=&quot;format-an-amount-in-nzd-using-the-default-locale-en-nz-for-me-rounding-to-a-whole-number-when-possible-and-otherwise-using-3dp&quot;&gt;Format an amount in NZD, using the default locale (en-NZ for me), rounding to a whole number when possible and otherwise using 3dp.&lt;/h4&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;var&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;roundNumber&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;mf&quot;&gt;1234.0&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;fractionalNumber&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;mf&quot;&gt;1234.5678&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;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;na&quot;&gt;style&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;currency&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;currency&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;NZD&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;minimumFractionDigits&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;na&quot;&gt;maximumFractionDigits&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;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;roundNumber&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;toLocaleString&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;nx&quot;&gt;options&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;))&lt;/span&gt;
&lt;span class=&quot;c1&quot;&gt;// =&amp;gt; $1,234&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;fractionalNumber&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;toLocaleString&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;nx&quot;&gt;options&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;));&lt;/span&gt;
&lt;span class=&quot;c1&quot;&gt;// =&amp;gt; $1,234.568&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h3 id=&quot;browser-support&quot;&gt;Browser support&lt;/h3&gt;

&lt;p&gt;According to the &lt;a href=&quot;https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Number/toLocaleString&quot;&gt;MDN documentation for &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;toLocaleString&lt;/code&gt;&lt;/a&gt;, browser support is good across the board for “Basic support” (this is formatting a number for a particular locale, but not in a particular style, e.g. “currency”). Support for &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;locales&lt;/code&gt; and &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;options&lt;/code&gt; is good for all mainstream browsers.&lt;/p&gt;

&lt;p&gt;While MDN’s compatibility chart indicates no support for &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;locales&lt;/code&gt; and &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;options&lt;/code&gt; on Android, my testing showed that it is in fact supported in Chrome running Android 6.0.1, and I suspect down to ~ Android 5 when the system webview was replaced by Chrome.&lt;/p&gt;

&lt;p&gt;For those looking to work in browsers that do not support non-Basic usage of &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;toLocaleString&lt;/code&gt;, the MDN page has feature detection functions that can be used to fallback to a different implementation. I created a fallback called &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;formatCurrency&lt;/code&gt;, which used the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;toLocaleString&lt;/code&gt; function directly if possible, falling back to concatenating the amount and currency.&lt;/p&gt;
</description>
        <pubDate>Wed, 11 Apr 2018 00:00:00 +1200</pubDate>
        <link>https://joshmcarthur.com/til/2018/04/11/til-tolocalestring.html</link>
        <guid isPermaLink="true">https://joshmcarthur.com/til/2018/04/11/til-tolocalestring.html</guid>
        
        <category>til</category>
        
        <category>technology</category>
        
        <category>javascript</category>
        
        
        <category>TIL</category>
        
      </item>
    
      <item>
        <title>Accessing selected element in Chrome DevTools</title>
        <description>&lt;p&gt;Chrome’s DevTools are something that I use for a decent portion of my day whenever I’m doing frontend work. I really appreciate how much work has gone into making for a great developer experience, and I’m always stumbing across new tricks to help make me more efficient.&lt;/p&gt;

&lt;p&gt;Just one of these tricks I picked up this morning from Wes Bos’ “React for Beginners” course in passing, and it’s been a bit of a revelation for me.&lt;/p&gt;

&lt;p&gt;When an element is selected in the Elements tab of the devtools, you may have noticed it displays something like this:&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;&amp;lt;div class=&quot;innerWrapper clearfix&quot;&amp;gt;…&amp;lt;/div&amp;gt; == $0
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;I’ve always seen the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;== $0&lt;/code&gt; and kind of skipped over it, assuming it’s some kind of notation that I’m just not familiar with.&lt;/p&gt;

&lt;p&gt;It turns out though, that this is Chrome communicating that the currently selected DOM element has been assigned to a variable named &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;$0&lt;/code&gt;. This variable can be used straight from the Console for looking up more detailed information, or even manipulating the DOM node in some way.&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/img/posts/devtools-select-element.gif&quot; alt=&quot;Gif of using the selected element in DevTools&quot; /&gt;&lt;/p&gt;

&lt;p&gt;Just a really nice trick that is going to make my life that much easier next time I need to drill down into an element.&lt;/p&gt;
</description>
        <pubDate>Tue, 10 Apr 2018 00:00:00 +1200</pubDate>
        <link>https://joshmcarthur.com/til/2018/04/10/accessing-selected-element-in-chrome-devtools.html</link>
        <guid isPermaLink="true">https://joshmcarthur.com/til/2018/04/10/accessing-selected-element-in-chrome-devtools.html</guid>
        
        <category>til</category>
        
        <category>technology</category>
        
        <category>tools</category>
        
        
        <category>TIL</category>
        
      </item>
    
      <item>
        <title>Accessing main_app routes from a Rails engine&apos;s templates</title>
        <description>&lt;p&gt;If you are working with a template that has been rendered by a Rails engine (the example I most commonly have is &lt;a href=&quot;https://github.com/refinery/refinerycms&quot;&gt;refinery&lt;/a&gt;, but any mounted Rails engine behaves the same way), then you will frequently run into a strange problem where route helpers (such as &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;widgets_path&lt;/code&gt;, &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;new_whatever_path&lt;/code&gt;, etc), result in an undefined method error, which can be quite frustrating to track down if you’ve not run into it before.&lt;/p&gt;

&lt;p&gt;This errors occurs because the view context that the template is being rendered in is the engine’s context, not the main application - long story short, the template is only looking at the engine’s routes for route helper methods, not the app’s routes.&lt;/p&gt;

&lt;p&gt;The trick to get this to work is to prefix your route helpers with &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;main_app&lt;/code&gt;. With the examples I gave just above, this means that the route helper method names would be &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;main_app.widgets_path&lt;/code&gt; and &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;main_app.new_whatever_path&lt;/code&gt;. This prompts Rails to look in the correct application namespace for the route helpers, which it can then resolve.&lt;/p&gt;
</description>
        <pubDate>Mon, 09 Apr 2018 00:00:00 +1200</pubDate>
        <link>https://joshmcarthur.com/til/2018/04/09/accessing-main_app-routes-from-a-rails-engines-templates.html</link>
        <guid isPermaLink="true">https://joshmcarthur.com/til/2018/04/09/accessing-main_app-routes-from-a-rails-engines-templates.html</guid>
        
        <category>til</category>
        
        <category>technology</category>
        
        <category>rails</category>
        
        
        <category>til</category>
        
      </item>
    
      <item>
        <title>Running Bitbucket Pipelines builds with docker compose</title>
        <description>&lt;p&gt;As a companion to [my post describing how to run tests with docker-compose on Gitlab CI], I converted the build steps this morning to work with &lt;a href=&quot;https://bitbucket.org/product/features/pipelines&quot;&gt;Bitbucket Pipelines&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Here’s the adapted &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;bitbucket-pipelines.yml&lt;/code&gt; file:&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;image&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;docker:stable&lt;/span&gt;

&lt;span class=&quot;na&quot;&gt;pipelines&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;
  &lt;span class=&quot;na&quot;&gt;default&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;step&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;
        &lt;span class=&quot;na&quot;&gt;services&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;docker&lt;/span&gt;
        &lt;span class=&quot;na&quot;&gt;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;s&quot;&gt;apk add --no-cache py-pip bash&lt;/span&gt; 
          &lt;span class=&quot;pi&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;pip install --no-cache-dir docker-compose&lt;/span&gt;
          &lt;span class=&quot;pi&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;docker-compose -v&lt;/span&gt;
          &lt;span class=&quot;pi&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;docker-compose run -e RAILS_ENV=test app bin/ci-setup&lt;/span&gt;
          &lt;span class=&quot;pi&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;docker-compose run -e RAILS_ENV=test app bin/ci-run&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Only minor differences from the Gitlab CI, mostly relating to the base image being used:&lt;/p&gt;

&lt;ol&gt;
  &lt;li&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;docker:stable&lt;/code&gt; is used as the base image. This is a nice small image for running DinD (Docker-in-Docker), and is (unsuprisingly) based on the latest stable release of Docker.&lt;/li&gt;
  &lt;li&gt;We declare that we need &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;docker&lt;/code&gt; to be running for this build.&lt;/li&gt;
  &lt;li&gt;Our script installs docker-compose using &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;pip&lt;/code&gt;, and then runs the CI commands.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;There is a small performance penalty for this method, since &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;docker-compose&lt;/code&gt; needs to build an image from scratch each time (unless your &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;docker-compose.yml&lt;/code&gt; declares a complete image rather than a build instruction), however I’ve found this time to be comparable to running in a traditional, non-Docker CI environment.&lt;/p&gt;

</description>
        <pubDate>Fri, 06 Apr 2018 00:00:00 +1200</pubDate>
        <link>https://joshmcarthur.com/til/2018/04/06/running-bitbucket-pipelines-builds-with-docker-compose.html</link>
        <guid isPermaLink="true">https://joshmcarthur.com/til/2018/04/06/running-bitbucket-pipelines-builds-with-docker-compose.html</guid>
        
        <category>til</category>
        
        <category>bitbucket</category>
        
        <category>ci</category>
        
        
        <category>TIL</category>
        
      </item>
    
      <item>
        <title>SASS Colour Functions</title>
        <description>&lt;p&gt;I’ve been implementing a style guide this week, and in particular have been trying to focus on creating clean, decoupled, and reusable components.&lt;/p&gt;

&lt;p&gt;Part of this effort has involved ensuring that all “global” settings that are represented in the style guide are contained in a single settings file. A perfect example of one of these settings is the colour palette.&lt;/p&gt;

&lt;p&gt;In order to try and avoid defining too many colours unnecessarily, I’ve been working a lot with SASS colour functions, and wanted to talk about those today.&lt;/p&gt;

&lt;h4 id=&quot;scale_color&quot;&gt;scale_color&lt;/h4&gt;

&lt;p&gt;&lt;a href=&quot;http://sass-lang.com/documentation/Sass/Script/Functions.html#scale_color-instance_method&quot;&gt;Documentation&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;This function is great for ‘tinting’ a colour. For example, if you have a palette colour defined, say &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;$green: #00ff00;&lt;/code&gt;, you can lighten it with &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;scale_color($green, $lightness: 15%)&lt;/code&gt; to make the colour 15% lighter. &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;scale_color&lt;/code&gt; has some minor details relating to how it changes the lightness in a relative sense - for a more absolute measurement, then there is &lt;a href=&quot;http://sass-lang.com/documentation/Sass/Script/Functions.html#adjust_color-instance_method&quot;&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;adjust_color&lt;/code&gt;&lt;/a&gt;.&lt;/p&gt;

&lt;h4 id=&quot;rgba&quot;&gt;rgba&lt;/h4&gt;

&lt;p&gt;&lt;a href=&quot;http://sass-lang.com/documentation/Sass/Script/Functions.html#rgba-instance_method&quot;&gt;Documentation&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;rgba&lt;/code&gt; is great for introducing an alpha value to a palette colour. I’ve been using this a lot for things like button states, where the background colour should be 85% opaque. It’s very simple to use, as it can accept either red, green, blue and alpha arguments, or a Sass “color” type and alpha arguments. A Sass color type can be basically any representation of a color - named colours (like “green”), and hex values (like “#00ff00”) are most common. Variables are also supported.&lt;/p&gt;

&lt;p&gt;As an example of introducing alpha to a colour value, given a color palette variable of &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;$green: #00ff00;&quot;&lt;/code&gt;, RGBA can be used to change the alpha of the color to 85% as follows: &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;rgba($green, 0.85)&lt;/code&gt;.&lt;/p&gt;

&lt;hr /&gt;

&lt;p&gt;These functions are incredibly useful for enabling stylesheets to stick to a small set of core variables, and simply adjust these, rather than adding variables for every possible variant of a colour. There are many, many more utility functions &lt;a href=&quot;http://sass-lang.com/documentation/Sass/Script/Functions.html#rgba-instance_method&quot;&gt;documented&lt;/a&gt; on the sass-lang site, and I’m going to be spending a lot more time investigating these more to further improve my componentized styles.&lt;/p&gt;

</description>
        <pubDate>Wed, 04 Apr 2018 00:00:00 +1200</pubDate>
        <link>https://joshmcarthur.com/til/2018/04/04/sass-colour-functions.html</link>
        <guid isPermaLink="true">https://joshmcarthur.com/til/2018/04/04/sass-colour-functions.html</guid>
        
        <category>til</category>
        
        <category>technology</category>
        
        <category>sass</category>
        
        
        <category>TIL</category>
        
      </item>
    
      <item>
        <title>Running Gitlab CI tests with docker compose</title>
        <description>&lt;p&gt;I’ve been working for some time to get &lt;a href=&quot;http://pawfit.nz&quot;&gt;Pawfit&lt;/a&gt; (my pet project, literally), continuously deploying. I’ve only recently added tests, so now that I have them, I want a green check each time I push.&lt;/p&gt;

&lt;p&gt;I run just about all my Rails apps now using Docker and docker-compose, almost always
using my own &lt;a href=&quot;https://github.com/joshmcarthur/Dockerfiles/tree/master/rails&quot;&gt;base Docker* files&lt;/a&gt;. I choose not to push these files to a repository like Docker Hub, because I don’t consider them usable on their own - their intention is to provide a starting point to grow from, rather than an out-of-the-box solution.&lt;/p&gt;

&lt;p&gt;Since this project is a small project and is running on a constrained budget (read: my own funds), I’ve constantly been looking for ways to optimise for cost. Along these lines, I’ve started pushing to Gitlab, mostly to make the most of their excellent CI product. I’ve been having quite a few problems getting my tests running though, as I want to use my own Dockerfile to have a consistent build - I don’t want to have my Dockerfile and then a bunch of duplicated set up steps in my &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;.gitlab-ci.yml&lt;/code&gt;. Gitlab supports Docker-in-Docker, which instantly puts it up there in terms of CI provider, but I still had a few issues. These issues were almost exclusively related to being unable to resolve linked container hostnames from my ‘app’ container - i.e. my ‘app’ container could be built and run, but could not see or communicate to port 5432 on my ‘db’ container.&lt;/p&gt;

&lt;p&gt;In the end, I gave up on a pure Docker solution to CI, and went with docker-compose, which is what I use for development. Whatever problems plain Docker commands were causing me, docker-compose seemed able to overcome, and I now have my builds running nicely. A future refactoring will be to make use of Gitlab’s built-in Docker image repository to keep track of app images each time I push, eventually moving to container based deployment.&lt;/p&gt;

&lt;p&gt;Anyway, here is the Gitlab CI YML file I ended up with:&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;test&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;
  &lt;span class=&quot;na&quot;&gt;stage&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;test&lt;/span&gt;
  &lt;span class=&quot;na&quot;&gt;image&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;docker:latest&lt;/span&gt;
  &lt;span class=&quot;na&quot;&gt;services&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;docker:dind&lt;/span&gt;
  &lt;span class=&quot;na&quot;&gt;variables&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;
    &lt;span class=&quot;na&quot;&gt;DOCKER_DRIVER&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;overlay2&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;s&quot;&gt;apk add --no-cache py-pip&lt;/span&gt;
    &lt;span class=&quot;pi&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;pip install docker-compose&lt;/span&gt;
  &lt;span class=&quot;na&quot;&gt;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;s&quot;&gt;docker-compose build&lt;/span&gt;
    &lt;span class=&quot;pi&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;docker-compose run -e RAILS_ENV=test app rake db:test:prepare test test:system&lt;/span&gt;
&lt;span class=&quot;na&quot;&gt;production&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;
  &lt;span class=&quot;na&quot;&gt;stage&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;deploy&lt;/span&gt;
  &lt;span class=&quot;na&quot;&gt;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;s&quot;&gt;gem install dpl&lt;/span&gt;
  &lt;span class=&quot;pi&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;dpl --provider=heroku --app=pawfit --api-key=$HEROKU_PRODUCTION_API_KEY&lt;/span&gt;
  &lt;span class=&quot;na&quot;&gt;only&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;master&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;What this does:&lt;/p&gt;

&lt;ol&gt;
  &lt;li&gt;Defines a test job, and production job. I can disregard the production job, as it just exists to auto-deploy my app to Heroku.&lt;/li&gt;
  &lt;li&gt;The test job uses the latest Docker as a base image, and tells Gitlab to use it’s “Docker-in-Docker” service (docker:dind). This sets up appropriate services and environment variables to be able to use Docker from within their Docker-based job runners.&lt;/li&gt;
  &lt;li&gt;Sets overlay2 as the Docker driver. I’m told this is faster, your job should work without this.&lt;/li&gt;
  &lt;li&gt;Before the job script runs, docker-compose is installed using &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;pip&lt;/code&gt;.&lt;/li&gt;
  &lt;li&gt;The script runs - this builds the image, then runs preparation for the tests, the unit tests, and the system tests (using headless Chrome).&lt;/li&gt;
&lt;/ol&gt;

</description>
        <pubDate>Mon, 02 Apr 2018 00:00:00 +1200</pubDate>
        <link>https://joshmcarthur.com/til/2018/04/02/running-gitlab-ci-tests-with-docker-compose.html</link>
        <guid isPermaLink="true">https://joshmcarthur.com/til/2018/04/02/running-gitlab-ci-tests-with-docker-compose.html</guid>
        
        <category>til</category>
        
        <category>technology</category>
        
        <category>gitlab</category>
        
        <category>ci</category>
        
        <category>docker</category>
        
        
        <category>TIL</category>
        
      </item>
    
      <item>
        <title>TIL: Mocking with Minitest in Ruby 2.0+</title>
        <description>&lt;p&gt;I normally work within an RSpec testing envionment, but I definitely enjoy working with more traditional Test::Unit tests when I get the chance.&lt;/p&gt;

&lt;p&gt;Today I discovered that Minitest, the testing toolkit that &lt;a href=&quot;http://ruby-doc.org/stdlib-2.0.0/libdoc/minitest/rdoc/MiniTest.html&quot;&gt;has been part of the Ruby standard library since 2.0&lt;/a&gt; has built-in support for mocks and stubs.&lt;/p&gt;

&lt;p&gt;To use this in a Test::Unit test, simply &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;require &quot;minitest/mocks&quot;&lt;/code&gt;.&lt;/p&gt;

&lt;h3 id=&quot;mocking&quot;&gt;Mocking&lt;/h3&gt;

&lt;p&gt;It looks like a mock object has to be specifically made. Minitest/Mocks does not include double support - you’ll need something like Mocha for that. The method signature on Minitest::Mock is: &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;expect(:method_name, return_value, [args])&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Here’s an 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;message_sender&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;Minitest&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;no&quot;&gt;Mock&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;message_sender&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;expect&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;send_message&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;p&quot;&gt;[{&lt;/span&gt;&lt;span class=&quot;ss&quot;&gt;email: &lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;test@example.com&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;ss&quot;&gt;message: &lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;Test&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;}])&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;result&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;message_sender&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;send_message&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;ss&quot;&gt;email: &lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;test@example.com&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;ss&quot;&gt;message: &lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;Test&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;assert&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;result&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;Message sends&quot;&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;message_sender&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;verify&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;A major difference from Rspec mocks to be aware of is that the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;verify&lt;/code&gt; method must be called on the mock object to actually check that the expected methods were returned.&lt;/p&gt;

&lt;h3 id=&quot;stubbing&quot;&gt;Stubbing&lt;/h3&gt;

&lt;p&gt;Minitest also includes basic support for stubs. The method signature for stubs is: &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;ObjectUnderTest.stub(:method_name, stub_value, &amp;amp;block)&lt;/code&gt;. &lt;strong&gt;Minitest does not have an unstub method&lt;/strong&gt; - instead, it is expected that the test code that requires the stub should go inside the block passed to &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;stub&lt;/code&gt;, and minitest will automatically unstub when the block completes.&lt;/p&gt;

&lt;p&gt;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;message_sender&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;MessageSender&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;message_sender&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;stub&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;ss&quot;&gt;:perform_deliveries?&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;k&quot;&gt;do&lt;/span&gt;
  &lt;span class=&quot;n&quot;&gt;message_sneder&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;send_message&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;ss&quot;&gt;email: &lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;test@example.com&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;ss&quot;&gt;message: &lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;Test&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;
</description>
        <pubDate>Wed, 28 Mar 2018 00:00:00 +1300</pubDate>
        <link>https://joshmcarthur.com/til/2018/03/28/til-mocking-with-minitest-in-ruby-20.html</link>
        <guid isPermaLink="true">https://joshmcarthur.com/til/2018/03/28/til-mocking-with-minitest-in-ruby-20.html</guid>
        
        <category>til</category>
        
        <category>technology</category>
        
        <category>rails</category>
        
        <category>minitest</category>
        
        
        <category>TIL</category>
        
      </item>
    
      <item>
        <title>TIL: let! and before order is important</title>
        <description>&lt;p&gt;I’ve just come across this issue. It’s minor, but it tripped me up!&lt;/p&gt;

&lt;p&gt;In RSpec, there are three methods I find myself using all the time:&lt;/p&gt;

&lt;ol&gt;
  &lt;li&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;before&lt;/code&gt; - this is equivalent to the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;setup&lt;/code&gt; method present in many assertion-based test frameworks, and runs before each test.&lt;/li&gt;
  &lt;li&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;let&lt;/code&gt; - this is a lazy-evaluated variable that will be made available to the context of each test - BUT it is undefined until called for the first time.&lt;/li&gt;
  &lt;li&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;let!&lt;/code&gt; - this is the “evaluate right away” version of &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;let&lt;/code&gt;. It will run before each example and already be available at that point.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Normally, in a feature or integration test that require data to be present, there will be some standard code:&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;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;My cool feature&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;ss&quot;&gt;type: :system&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;do&lt;/span&gt;
  &lt;span class=&quot;n&quot;&gt;let!&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;ss&quot;&gt;:widget&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;no&quot;&gt;FactoryBot&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;create&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;ss&quot;&gt;:widget&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;before&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;visit&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;/widgets&quot;&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

  &lt;span class=&quot;n&quot;&gt;it&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;shows the widget&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;page&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_content&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;widget&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;name&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 is normally the pattern that I follow. 
What I found is that, especially for &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;let!&lt;/code&gt; blocks where you want the data to be present
in the database &lt;em&gt;before&lt;/em&gt; the route is visited, the order of the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;let!&lt;/code&gt; and &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;before&lt;/code&gt; matters.&lt;/p&gt;

&lt;p&gt;In the below example the test fails, as the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;let!&lt;/code&gt; blocks actually run as they are evaluated, as in, after the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;before&lt;/code&gt; block:&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;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;My cool feature&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;ss&quot;&gt;type: :system&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;do&lt;/span&gt;
  &lt;span class=&quot;n&quot;&gt;before&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;visit&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;/widgets&quot;&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
  &lt;span class=&quot;n&quot;&gt;let!&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;ss&quot;&gt;:widget&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;no&quot;&gt;FactoryBot&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;create&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;ss&quot;&gt;:widget&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;it&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;shows the widget&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;page&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_content&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;widget&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;name&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;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Failure: Unable to to find content: &quot;My widget&quot;&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;There is an interesting &lt;a href=&quot;https://github.com/rspec/rspec-core/issues/2040&quot;&gt;issue&lt;/a&gt; lodged against rspec-core that has a bit more background as to why this happens, as well as some suggested workarounds. In my case, I just corrected the order of my &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;before&lt;/code&gt; and &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;let!&lt;/code&gt; calls to make sure the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;before&lt;/code&gt; block came after the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;let!&lt;/code&gt;s.&lt;/p&gt;

</description>
        <pubDate>Wed, 28 Mar 2018 00:00:00 +1300</pubDate>
        <link>https://joshmcarthur.com/til/2018/03/28/til-let-and-before-order-is-important.html</link>
        <guid isPermaLink="true">https://joshmcarthur.com/til/2018/03/28/til-let-and-before-order-is-important.html</guid>
        
        <category>til</category>
        
        <category>technology</category>
        
        <category>rails</category>
        
        <category>rspec</category>
        
        
        <category>TIL</category>
        
      </item>
    
      <item>
        <title>TIL: Nested layouts in Rails</title>
        <description>&lt;p&gt;Nested layouts in Rails aren’t a technique that I’ve had to use a lot in the past, but I’m finding myself using them quite a bit in my current work.&lt;/p&gt;

&lt;p&gt;Nesting layouts allows for some sharing of a common view structure. The best example of this that I have come across is in the app I am working on. 
This app is about 50/50 Refinery CMS and normal Rails app. The Refinery pages are of several ‘types’ of content, but are all presented within the same container structure. The normal Rails app views are presented in a higher-level layout that has a little less markup surrounding it.&lt;/p&gt;

&lt;p&gt;Nesting layouts is actually quite easy. It uses the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;content_for&lt;/code&gt; method to declare content for a particular named block, and then render the layout that you wish to use.&lt;/p&gt;

&lt;p&gt;Here’s an example:&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;# app/views/layouts/application.html.erb
&lt;span class=&quot;nt&quot;&gt;&amp;lt;html&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;title&amp;gt;&lt;/span&gt;My Rails App&lt;span class=&quot;nt&quot;&gt;&amp;lt;/title&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;c&quot;&gt;&amp;lt;!-- Head content --&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;nt&quot;&gt;&amp;lt;nav&amp;gt;&lt;/span&gt;
      &lt;span class=&quot;nt&quot;&gt;&amp;lt;h1&amp;gt;&lt;/span&gt;My navigation&lt;span class=&quot;nt&quot;&gt;&amp;lt;/h1&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;nt&quot;&gt;&amp;lt;/nav&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;cp&quot;&gt;&amp;lt;%=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;content_for?&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;ss&quot;&gt;:content&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;yield&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;ss&quot;&gt;:content&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;yield&lt;/span&gt; &lt;span class=&quot;cp&quot;&gt;%&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;nt&quot;&gt;&amp;lt;footer&amp;gt;&lt;/span&gt;
      &lt;span class=&quot;nt&quot;&gt;&amp;lt;small&amp;gt;&lt;/span&gt;Thanks for visiting&lt;span class=&quot;nt&quot;&gt;&amp;lt;/small&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;nt&quot;&gt;&amp;lt;/footer&amp;gt;&lt;/span&gt;
  &lt;span class=&quot;nt&quot;&gt;&amp;lt;/body&amp;gt;&lt;/span&gt;
&lt;span class=&quot;nt&quot;&gt;&amp;lt;/html&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;So, that’s the normal application layout. The only difference is that the normal &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;yield&lt;/code&gt; has a ternary operator wrapped around it to render content for &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;content&lt;/code&gt;, if it is set. This is important to let our nested layout provide content to the application layout.&lt;/p&gt;

&lt;p&gt;Our nested layout looks 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;# app/views/layouts/content_page.html.erb
&lt;span class=&quot;cp&quot;&gt;&amp;lt;%&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;content_for&lt;/span&gt; &lt;span class=&quot;ss&quot;&gt;:content&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;do&lt;/span&gt; &lt;span class=&quot;cp&quot;&gt;%&amp;gt;&lt;/span&gt;
  &lt;span class=&quot;nt&quot;&gt;&amp;lt;h2&amp;gt;&lt;/span&gt;This is a content page&lt;span class=&quot;nt&quot;&gt;&amp;lt;/h2&amp;gt;&lt;/span&gt;
  &lt;span class=&quot;cp&quot;&gt;&amp;lt;%=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;yield&lt;/span&gt; &lt;span class=&quot;cp&quot;&gt;%&amp;gt;&lt;/span&gt;
&lt;span class=&quot;cp&quot;&gt;&amp;lt;%&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;end&lt;/span&gt; &lt;span class=&quot;cp&quot;&gt;%&amp;gt;&lt;/span&gt;
&lt;span class=&quot;cp&quot;&gt;&amp;lt;%=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;render&lt;/span&gt; &lt;span class=&quot;ss&quot;&gt;template: &lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;layouts/application&quot;&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 template provides content for the block named &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;:content&lt;/code&gt;, and then renders the application layout. The app layout, seeing content in the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;:content&lt;/code&gt; block, renders this, rather than the implicit yield.&lt;/p&gt;

&lt;p&gt;Provided a controller declared the correct layout, 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;c1&quot;&gt;# app/controllers/pages_controller.rb&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;PagesController&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&amp;lt;&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;ApplicationController&lt;/span&gt;
  &lt;span class=&quot;n&quot;&gt;layout&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;content_page&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 a pages controller view will be rendered inside the content page layout, resulting in a compiled ERB template of:&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;&lt;span class=&quot;nt&quot;&gt;&amp;lt;html&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;title&amp;gt;&lt;/span&gt;My Rails App&lt;span class=&quot;nt&quot;&gt;&amp;lt;/title&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;c&quot;&gt;&amp;lt;!-- Head content --&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;nt&quot;&gt;&amp;lt;nav&amp;gt;&lt;/span&gt;
      &lt;span class=&quot;nt&quot;&gt;&amp;lt;h1&amp;gt;&lt;/span&gt;My navigation&lt;span class=&quot;nt&quot;&gt;&amp;lt;/h1&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;nt&quot;&gt;&amp;lt;/nav&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;nt&quot;&gt;&amp;lt;h2&amp;gt;&lt;/span&gt;This is a content page&lt;span class=&quot;nt&quot;&gt;&amp;lt;/h2&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;cp&quot;&gt;&amp;lt;%=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;yield&lt;/span&gt; &lt;span class=&quot;cp&quot;&gt;%&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;nt&quot;&gt;&amp;lt;footer&amp;gt;&lt;/span&gt;
      &lt;span class=&quot;nt&quot;&gt;&amp;lt;small&amp;gt;&lt;/span&gt;Thanks for visiting&lt;span class=&quot;nt&quot;&gt;&amp;lt;/small&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;nt&quot;&gt;&amp;lt;/footer&amp;gt;&lt;/span&gt;
  &lt;span class=&quot;nt&quot;&gt;&amp;lt;/body&amp;gt;&lt;/span&gt;
&lt;span class=&quot;nt&quot;&gt;&amp;lt;/html&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
</description>
        <pubDate>Mon, 26 Mar 2018 00:00:00 +1300</pubDate>
        <link>https://joshmcarthur.com/til/2018/03/26/til-nested-layouts-in-rails.html</link>
        <guid isPermaLink="true">https://joshmcarthur.com/til/2018/03/26/til-nested-layouts-in-rails.html</guid>
        
        <category>til</category>
        
        <category>rails</category>
        
        <category>technology</category>
        
        
        <category>TIL</category>
        
      </item>
    
      <item>
        <title>TIL: Handling browser dialogs with headless Chrome in Capybara</title>
        <description>&lt;p&gt;My normal Rails 5.x &lt;a href=&quot;https://github.com/joshmcarthur/Dockerfiles/blob/b3a5ec49505b76a89257aa458b196266f9926935/rails/Dockerfile#L11&quot;&gt;development stack&lt;/a&gt; now uses Chrome, running headlessly, powered by the following gems:&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;# Gemfile&lt;/span&gt;
&lt;span class=&quot;c1&quot;&gt;# ...&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;gem&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&apos;capybara&apos;&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;gem&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&apos;chromedriver-helper&apos;&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;gem&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&apos;capybara-selenium&apos;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Unlike using things like Poltergeist, the only external dependency you need to run these tests is Chrome itself. If you wish to reduce your gem dependencies, you can also remove &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;chromedriver-helper&lt;/code&gt;, but this will require that you get &lt;a href=&quot;https://sites.google.com/a/chromium.org/chromedriver/&quot;&gt;chromedriver&lt;/a&gt; set up.&lt;/p&gt;

&lt;p&gt;One of the behaviours I’ve recently wanted to test is interactions with browser dialogs. This is particularly common when using Rails’ UJS helpers, such as:&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;&lt;span class=&quot;cp&quot;&gt;&amp;lt;%=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;link_to&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;Cancel&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;#&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;ss&quot;&gt;data: &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;ss&quot;&gt;confirm: &lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;Are you sure?&quot;&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;}&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;And straight Javascript such as:&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;var&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;projectName&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;my-project&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&quot;&lt;/span&gt;
&lt;span class=&quot;kd&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;shouldCancel&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;prompt&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;Enter your project name to cancel&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;o&quot;&gt;===&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;projectName&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;Interacting with these kinds of browser interfaces can be hard from an abstraction such as Capybara. In fact, because each browser can present these dialogs slightly differently, the method used depends on the browser. The rest of this post assumes you’re using Chrome as your driver, but if you’re just after a summary, I found &lt;a href=&quot;https://gist.github.com/mikepack/5207962&quot;&gt;this Gist&lt;/a&gt; to be super useful.&lt;/p&gt;

&lt;h3 id=&quot;accepting-a-prompt---ie-promptare-you-sure&quot;&gt;Accepting a prompt - i.e. prompt(“Are you sure?”);&lt;/h3&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;confirm&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;page&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;driver&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;browser&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;switch_to&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;alert&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;assert_equal&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;confirm&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;text&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;Are you sure?
confirm.accept # You can also `confirm.dismiss` if you want to test the negative path
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h3 id=&quot;dismissing-a-messge---ie-alertreset-password-instructions-have-been-delivered&quot;&gt;Dismissing a messge - i.e. alert(“Reset password instructions have been delivered.”);&lt;/h3&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;alert&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;page&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;driver&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;browser&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;switch_to&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;alert&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;assert_equal&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;alert&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;text&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;Reset password instructions have been delivered.&quot;&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;alert&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;accept&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h3 id=&quot;inputting-data-to-a-prompt---ie-promptenter-your-project-name-to-cancel&quot;&gt;Inputting data to a prompt - i.e. prompt(“Enter your project name to cancel”)&lt;/h3&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;prompt&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;page&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;driver&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;browser&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;switch_to&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;alert&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;assert_equal&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;prompt&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;text&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;Enter your project name to cancel&quot;&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;prompt&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;send_keys&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;my-project&quot;&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;prompt&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;accept&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

</description>
        <pubDate>Mon, 26 Mar 2018 00:00:00 +1300</pubDate>
        <link>https://joshmcarthur.com/2018/03/26/til-handling-browser-dialogs-with-headless-chrome-in-capybara.html</link>
        <guid isPermaLink="true">https://joshmcarthur.com/2018/03/26/til-handling-browser-dialogs-with-headless-chrome-in-capybara.html</guid>
        
        <category>til</category>
        
        <category>technology</category>
        
        <category>rails</category>
        
        
      </item>
    
      <item>
        <title>TIL: SASS @at root</title>
        <description>&lt;p&gt;The &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;@at-root&lt;/code&gt; directive can be quite useful for creating SASS rules where you need 
to jump outside of a SASS scoping block back to the root level when your styles are compiled.&lt;/p&gt;

&lt;p&gt;In particular, this directive can be useful for generating &lt;a href=&quot;http://getbem.com/&quot;&gt;BEM&lt;/a&gt; styles, since the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;&amp;amp;&lt;/code&gt; abbreviation
continues to work for targeting the parent selector.&lt;/p&gt;

&lt;p&gt;Here’s an example of a component without &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;@at-root&lt;/code&gt;:&lt;/p&gt;

&lt;div class=&quot;language-scss highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;nc&quot;&gt;.button&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;k&quot;&gt;&amp;amp;&lt;/span&gt;&lt;span class=&quot;nc&quot;&gt;.collapse-to-icon&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;nl&quot;&gt;padding&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;1rem&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;&amp;amp;&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;__text&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;nl&quot;&gt;display&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;none&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;&amp;amp;&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;__icon&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;nl&quot;&gt;display&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;inline-block&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;Which is compiled to:&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;nc&quot;&gt;.button.collapse-to-icon&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;nl&quot;&gt;padding&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;1rem&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;.button.collapse-to-icon&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;.button.collapse-to-icon.collapse-to-icon__text&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;nl&quot;&gt;display&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;none&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;.button.collapse-to-icon&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;.button.collapse-to-icon.collapse-to-icon__icon&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;nl&quot;&gt;display&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;inline-block&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 problem with this CSS is that because of the parent nesting, unnecessary conditions
are prefixed onto the elements of the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;collapse-to-icon&lt;/code&gt; block.&lt;/p&gt;

&lt;p&gt;With &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;@at-root&lt;/code&gt;, something like this can be written:&lt;/p&gt;

&lt;div class=&quot;language-scss highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;nc&quot;&gt;.button&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;k&quot;&gt;&amp;amp;&lt;/span&gt;&lt;span class=&quot;nc&quot;&gt;.collapse-to-icon&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;nl&quot;&gt;padding&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;1rem&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;@at-root&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
      &lt;span class=&quot;k&quot;&gt;&amp;amp;&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;__text&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;nl&quot;&gt;display&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;none&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;&amp;amp;&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;__icon&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;nl&quot;&gt;display&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;inline-block&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;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 is compiled to:&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;nc&quot;&gt;.button.collapse-to-icon&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt; 
  &lt;span class=&quot;nl&quot;&gt;padding&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;1rem&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;.collapse-to-icon__text&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;nl&quot;&gt;display&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;none&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;.collapse-to-icon__icon&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{}&lt;/span&gt;
  &lt;span class=&quot;nt&quot;&gt;display&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;inline-block&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;err&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

</description>
        <pubDate>Wed, 21 Mar 2018 00:00:00 +1300</pubDate>
        <link>https://joshmcarthur.com/til/2018/03/21/til-sass-at-root.html</link>
        <guid isPermaLink="true">https://joshmcarthur.com/til/2018/03/21/til-sass-at-root.html</guid>
        
        <category>til</category>
        
        <category>sass</category>
        
        <category>technology</category>
        
        
        <category>til</category>
        
      </item>
    
      <item>
        <title>TIL: before(:all) callbacks run outside the test transaction</title>
        <description>&lt;p&gt;While working on a feature test today, I learned that any database data that is modified 
in a &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;before(:all)&lt;/code&gt; callback in an RSpec test is retained outside of the test run. This is documented quite clearly in the RSpec &lt;a href=&quot;https://relishapp.com/rspec/rspec-rails/docs/transactions&quot;&gt;documentation&lt;/a&gt;, I just haven’t had the opportunity to work with these types of callbacks regularly before.&lt;/p&gt;

&lt;p&gt;The test I ran into issues with looked like 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;nb&quot;&gt;require&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;rails_helper&quot;&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;MyFeature&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;ss&quot;&gt;type: :system&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;do&lt;/span&gt;
  &lt;span class=&quot;n&quot;&gt;before&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;vi&quot;&gt;@widgets&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;FactoryBot&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;create_list&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;ss&quot;&gt;:widget&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;k&quot;&gt;end&lt;/span&gt;

  &lt;span class=&quot;c1&quot;&gt;# ... tests&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 first time this test ran against an empty database, the test passed, no problems. The test also ran fine on CI, even after repeated attempts. If I tried to run the test multiple times locally though, the second run onwards failed with a uniquenss constraint error on widget name (which had a unique index).&lt;/p&gt;

&lt;p&gt;After reading the documentation, I found that while &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;before(:each)&lt;/code&gt; callbacks run within the ActiveRecord
transaction that wraps each RSpec example, and therefore rollback any data modifications after the test completes, &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;before(:all)&lt;/code&gt; does not. In the example of the test above, this meant that on the first test run, 5 widgets were being committed to my test database. The next test run, these widgets were already present, causing the uniqueness validation to be violated when widgets with identical names were created.&lt;/p&gt;

&lt;p&gt;Generally, the most appropriate solution with this type of test data is to move the data being created in a &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;before(:all)&lt;/code&gt; callback to a &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;before(:each)&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;n&quot;&gt;before&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;k&quot;&gt;do&lt;/span&gt;
  &lt;span class=&quot;vi&quot;&gt;@widgets&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;FactoryBot&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;create_list&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;ss&quot;&gt;:widget&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;k&quot;&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;If this is the entirity of your callback block, you may as well make this a &lt;a href=&quot;https://relishapp.com/rspec/rspec-core/v/3-4/docs/helper-methods/let-and-let&quot;&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;let&lt;/code&gt; block&lt;/a&gt; (but don’t forget to understand when to use &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;let&lt;/code&gt; vs &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;let!&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;let&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;ss&quot;&gt;:widgets&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;no&quot;&gt;FactoryBot&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;create_list&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;ss&quot;&gt;:widget&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;p&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;If you are doing more complex set up in your &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;before(:all)&lt;/code&gt; or cannot set up this data in &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;before(:each)&lt;/code&gt; callback, the before all callback can still be used, it just becomes important to use a corresponding &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;after(:all)&lt;/code&gt; callback to clean up the test data to prevent subsequent test failures:&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;before&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;vi&quot;&gt;@widgets&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;FactoryBot&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;create_list&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;ss&quot;&gt;:widget&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;k&quot;&gt;end&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;vi&quot;&gt;@widgets&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;destroy_all&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;

</description>
        <pubDate>Wed, 14 Mar 2018 00:00:00 +1300</pubDate>
        <link>https://joshmcarthur.com/til/2018/03/14/til-before-all-and-transactions.html</link>
        <guid isPermaLink="true">https://joshmcarthur.com/til/2018/03/14/til-before-all-and-transactions.html</guid>
        
        <category>til</category>
        
        <category>technology</category>
        
        <category>rails</category>
        
        <category>rspec</category>
        
        
        <category>til</category>
        
      </item>
    
      <item>
        <title>TIL: text underline position</title>
        <description>&lt;p&gt;TIL about the CSS property &lt;a href=&quot;https://developer.mozilla.org/en-US/docs/Web/CSS/text-underline-position&quot;&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;text-underline-position&lt;/code&gt;&lt;/a&gt;. This property is a useful progressive enhancement to achieve a particular look and feel for links, in particular for content with many descenders.&lt;/p&gt;

&lt;p&gt;Examples:&lt;/p&gt;

&lt;p style=&quot;text-decoration:underline;&quot;&gt;This is standard underlined text.&lt;/p&gt;
&lt;p style=&quot;text-decoration:underline; text-underline-position:under;&quot;&gt;This text has the underline position set to &quot;under&quot;.&lt;/p&gt;
&lt;p style=&quot;border-bottom:solid 1px #000;padding:0 2px;&quot;&gt;This text has a bottom border instead of an underline. Notice that the border overflows the text and doesn&apos;t work for multiline.&lt;/p&gt;

&lt;p&gt;Browser support is not so good for this feature, it being fully supported only in Chrome and Edge on desktop, and Edge on mobile, so it is important to consider this a progressive enhancement. A similar effect can be achived with a CSS &lt;a href=&quot;https://developer.mozilla.org/en-US/docs/Web/CSS/border-bottom&quot;&gt;bottom border&lt;/a&gt;, however this can make alignment of “underlined” and non-underlined text difficult.&lt;/p&gt;
</description>
        <pubDate>Tue, 13 Mar 2018 00:00:00 +1300</pubDate>
        <link>https://joshmcarthur.com/til/2018/03/13/til-text-underline-position.html</link>
        <guid isPermaLink="true">https://joshmcarthur.com/til/2018/03/13/til-text-underline-position.html</guid>
        
        <category>til</category>
        
        <category>technology</category>
        
        <category>css</category>
        
        
        <category>til</category>
        
      </item>
    
      <item>
        <title>TIL: The |= pipe equals CSS selector</title>
        <description>&lt;p&gt;Today I learned about a CSS selector I have not used a great deal before - pipe-equals. The &lt;a href=&quot;https://www.w3.org/TR/selectors/#attribute-representation&quot;&gt;spec&lt;/a&gt; describes this operator as:&lt;/p&gt;

&lt;blockquote&gt;
  &lt;p&gt;Represents an element with the att attribute, its value either being exactly “val” or beginning with “val” immediately followed by “-“&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;This selector is particularly useful for loose matching of &lt;a href=&quot;http://getbem.com/&quot;&gt;BEM&lt;/a&gt; CSS classes, and other dasherized class naming systems, for example:&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;o&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;class&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;|=&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&quot;icon&quot;&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;nl&quot;&gt;background&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;#656565&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;.icon--small&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;nl&quot;&gt;width&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;32px&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;nl&quot;&gt;height&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;32px&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;.icon--medium&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;nl&quot;&gt;width&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;64px&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;nl&quot;&gt;height&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;64px&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;.icon--large&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;nl&quot;&gt;width&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;128px&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;nl&quot;&gt;height&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;128px&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;For information:&lt;/p&gt;
&lt;ul&gt;
  &lt;li&gt;&lt;a href=&quot;https://www.w3.org/TR/selectors/#attribute-representation&quot;&gt;W3C CSS Selectors Level 4 Specification&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://developer.mozilla.org/en-US/docs/Web/CSS/Attribute_selectors&quot;&gt;Mozilla Developer Network: Attribute selectors&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

</description>
        <pubDate>Mon, 12 Mar 2018 00:00:00 +1300</pubDate>
        <link>https://joshmcarthur.com/til/2018/03/12/til-the-pipe-equals-css-selector.html</link>
        <guid isPermaLink="true">https://joshmcarthur.com/til/2018/03/12/til-the-pipe-equals-css-selector.html</guid>
        
        <category>til</category>
        
        <category>technology</category>
        
        
        <category>til</category>
        
      </item>
    
      <item>
        <title>Calibre Content Server</title>
        <description>&lt;p&gt;I’ve used &lt;a href=&quot;https://calibre-ebook.com/&quot;&gt;Calibre&lt;/a&gt; for years to manage my eBook collection. It’s a great piece of software that has
always worked really well for me.&lt;/p&gt;

&lt;p&gt;I discovered today that Calibre has the ability to start a “content server”. This provides an endpoint which serves information about the Calibre library, including
covers, titles, and descriptions. The content server can be started by clicking “Connect/Share” in the top menu bar, and then selecting “Start Content server”. This starts a server listening on all hosts on port 8080.&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/img/posts/calibre-content-server.png&quot; alt=&quot;Option to start content server&quot; /&gt;&lt;/p&gt;

&lt;p&gt;Under “Preferences” -&amp;gt; “Sharing over the net”, there are a slew of options allowing for some customization of how the content server works, including authentication mechanisms.&lt;/p&gt;

&lt;p&gt;Visiting http://localhost:8080 will allow a library to be selected and will show a nice grid view of all the books in the library, along with cover images, titles/descriptions, and options to download books in all the formats which have been generated for that particular title.&lt;/p&gt;

&lt;p&gt;I’m making use of this feature of Calibre to begin moving my library off my own laptop onto the Raspberry Pi which manages much of my home network infrastructure. In particular, the content server will allow me to create a shared library with the rest of my household.&lt;/p&gt;
</description>
        <pubDate>Fri, 09 Mar 2018 00:00:00 +1300</pubDate>
        <link>https://joshmcarthur.com/til/2018/03/09/calibre-content-server.html</link>
        <guid isPermaLink="true">https://joshmcarthur.com/til/2018/03/09/calibre-content-server.html</guid>
        
        <category>til</category>
        
        <category>tech</category>
        
        
        <category>TIL</category>
        
      </item>
    
      <item>
        <title>Rails: config_for</title>
        <description>&lt;p&gt;Sometimes an application can have pretty complex configuration for certain components.
There’s always temptation to put these settings straight in your service, but today I learned about a simple method built into Rails from 4.2 onwards that lets this config live outside of your Ruby classses: &lt;a href=&quot;https://apidock.com/rails/Rails/Application/config_for&quot;&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Rails.application.config_for&lt;/code&gt;&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;This method allows you to place your config in an environment-namespaced YAML file within your &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;config/&lt;/code&gt; folder, just like &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;database.yml&lt;/code&gt; or &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;secrets.yml&lt;/code&gt;. Here’s an example of a config file I created for Shopify:&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;c1&quot;&gt;# config/shopify.yml&lt;/span&gt;
&lt;span class=&quot;na&quot;&gt;development&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;
  &lt;span class=&quot;na&quot;&gt;shop_name&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&apos;&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;mystore-development&apos;&lt;/span&gt;
  &lt;span class=&quot;na&quot;&gt;api_key&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&apos;&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;abc123&apos;&lt;/span&gt;
  &lt;span class=&quot;na&quot;&gt;password&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&apos;&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;secret!&apos;&lt;/span&gt;
&lt;span class=&quot;na&quot;&gt;test&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;
  &lt;span class=&quot;na&quot;&gt;shop_name&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&apos;&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;mystore-test&apos;&lt;/span&gt;
  &lt;span class=&quot;na&quot;&gt;api_key&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&apos;&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;abc245&apos;&lt;/span&gt;
  &lt;span class=&quot;na&quot;&gt;password&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&apos;&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;secret!&apos;&lt;/span&gt;
&lt;span class=&quot;na&quot;&gt;production&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;
  &lt;span class=&quot;na&quot;&gt;shop_name&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&amp;lt;%= ENV[&apos;SHOPIFY_SHOP_NAME&apos;] %&amp;gt;&lt;/span&gt;
  &lt;span class=&quot;na&quot;&gt;api_key&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&amp;lt;%= ENV[&apos;SHOPIFY_API_KEY&apos;] %&amp;gt;&lt;/span&gt;
  &lt;span class=&quot;na&quot;&gt;password&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&amp;lt;%= ENV[&apos;SHOPIFY_PASSWORD&apos;] %&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;It can then be accessed within an initializer or even your service object:&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;ShopifyService&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;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;application&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;config_for&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;ss&quot;&gt;:shopify&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;With the example above, this would return a 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;shop_name: &lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;mystore-development&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
  &lt;span class=&quot;ss&quot;&gt;api_key: &lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;abc123&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
  &lt;span class=&quot;ss&quot;&gt;password: &lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;secret!&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;
</description>
        <pubDate>Wed, 07 Mar 2018 00:00:00 +1300</pubDate>
        <link>https://joshmcarthur.com/til/2018/03/07/rails-config_for.html</link>
        <guid isPermaLink="true">https://joshmcarthur.com/til/2018/03/07/rails-config_for.html</guid>
        
        <category>til</category>
        
        <category>rails</category>
        
        <category>tech</category>
        
        
        <category>TIL</category>
        
      </item>
    
      <item>
        <title>Pattern for configuring integration objects</title>
        <description>&lt;p&gt;I often struggle with the correct way to set any kind of configuration on a class, especially when it is a class intended to integrate with an external API that usually needs several configuration values.&lt;/p&gt;

&lt;p&gt;I initially implemented my own configuration pattern, before coming across &lt;a href=&quot;https://robots.thoughtbot.com/mygem-configure-block&quot;&gt;this great pattern&lt;/a&gt; by ThoughtBot that has been used for their open-source library Clearance:&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;MyApiIntegration&lt;/span&gt; 
  &lt;span class=&quot;k&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&amp;lt;&amp;lt;&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;self&lt;/span&gt;
    &lt;span class=&quot;nb&quot;&gt;attr_accessor&lt;/span&gt; &lt;span class=&quot;ss&quot;&gt;:configuration&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;configure&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;nf&quot;&gt;configuration&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;||=&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;Configuration&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;yield&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;configuration&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;class&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;Configuration&lt;/span&gt; 
    &lt;span class=&quot;nb&quot;&gt;attr_accessor&lt;/span&gt; &lt;span class=&quot;ss&quot;&gt;:api_key&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;ss&quot;&gt;:api_secret&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 exposes a very simple API for configuring the object:&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;MyApiIntegration&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;api_key&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;wow&quot;&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;api_secret&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;api&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;I found the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;attr_accessor&lt;/code&gt; on &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;class &amp;lt;&amp;lt; self&lt;/code&gt; particularly useful, as I had been running into issues with &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;class_attribute&lt;/code&gt;, class-level variables, and &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;cattr_accessor&lt;/code&gt; where I was losing configuration between test runs. The only issue I have with this code snippet is that the default configuration (if any) is not assigned until &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;configure&lt;/code&gt; is called. I am going to experiment with this pattern in my investment time to try and identify a way of having the default configuration used if &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;configure&lt;/code&gt; does not need to be called.&lt;/p&gt;

&lt;p&gt;Thanks ThoughtBot!&lt;/p&gt;
</description>
        <pubDate>Wed, 07 Mar 2018 00:00:00 +1300</pubDate>
        <link>https://joshmcarthur.com/til/2018/03/07/pattern-for-configuring-integration-objects.html</link>
        <guid isPermaLink="true">https://joshmcarthur.com/til/2018/03/07/pattern-for-configuring-integration-objects.html</guid>
        
        <category>til</category>
        
        <category>rails</category>
        
        <category>tech</category>
        
        
        <category>til</category>
        
      </item>
    
      <item>
        <title>A Brief History of Donnelly Flat</title>
        <description>&lt;p&gt;I frequently find myself tramping into the Tararua Forest Park from the Mount Holdsworth road-end, travelling through Donnelly Flat, a popular picnic spot around 10 minutes from the roadend. After considering the lack of signed history regarding the history of the Flat, and after being unable to find out any information from Archives, National Library, Papers Past or regional libraries, I got in touch with local DOC rangers. They have kindly provided me with a short history of the Mount Holdsworth area by Ben Iorns (1883-1977), a Masterton historian. This history has provided background information for this post.&lt;/p&gt;

&lt;hr /&gt;

&lt;p&gt;The Mount Holdsworth area is one of the oldest and most popular park accesses, largely due to the relative accessibility of the peak to the Masterton area, along with easy access up ridgelines to the summit. The first documented trip according to Iorns occurred in 1863, and by 1907 the Mount Holdsworth Committee was formed to cut a track and construct the first of three Mountain Houses, all located on the same site at Pig Flat.&lt;/p&gt;

&lt;p&gt;A particular feature noted by early visitors to the Holdsworth area was the profusion of the undisturbed native bush, especially in the Atiwhakatu valley around Donnelly Flat. Sawmillers expressed a keen interest in milling this bush, but fortunately faced determined opposition over many years which prevented milling from ever occurring inside the park boundaries.&lt;/p&gt;

&lt;blockquote&gt;
  &lt;p&gt;Sawmillers quite naturally coveted the tall clean trunks of those rimus, kahikatea, miro and beeches of 3 species, very generously offering to leave us the stumps and branches and a promise of a newer and more virile forest that would soon replace its “over mature” ancestors.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Donnelly Flat itself is a grassy clearing occupying a couple of acres. The clearing is natural, or at least not cleared by human occupation as far as it known. The flat has always been popular with picnickers and campers due to it’s sheltered position in the valley and proximity to Atiwhakatu Stream. The Flat was named for Thomas Donnelly, a prospector from Masterton who established a camp at the flat, being supplied weekly from the road-end. In April 1910, he failed to collect his resupply, kicking off an intensive search. A year later, his remains were found, with indications that he had injured himself in a fall and perished from exposure.&lt;/p&gt;

&lt;p&gt;Given this history, it’s understandable that the origin of the name is somewhat under documented, but I think it is still important to acknowledge the events and people attached to these special locations. I’d like to acknowledge the efforts of the Masterton office of the Department of Conservation for their speed and efforts in locating and passing on this fascinating history, and Ben Iorns for documenting his recollections of the Holdsworth area for future generations to learn from. I hope that others find the history of Donnelly Flat as interesting as I have.&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/img/posts/donnelly.jpg&quot; alt=&quot;Campsite at Donnelly Flat&quot; /&gt;&lt;/p&gt;
</description>
        <pubDate>Wed, 07 Mar 2018 00:00:00 +1300</pubDate>
        <link>https://joshmcarthur.com/tramping/2018/03/07/donnelly-flat-name-origin.html</link>
        <guid isPermaLink="true">https://joshmcarthur.com/tramping/2018/03/07/donnelly-flat-name-origin.html</guid>
        
        <category>tramping</category>
        
        <category>tararua</category>
        
        <category>history</category>
        
        
        <category>Tramping</category>
        
      </item>
    
      <item>
        <title>Trip Report: Akatawara Hill Road Summit to Renata (Attempt)</title>
        <description>&lt;p&gt;This post outlines a day trip from the Akatawara Hill Road saddle towards Renata Hut. After a couple of recent winter storms, I ran into a lot of tree fall that slowed things down sufficiently so that I ran into my time limit and turned back, however, it was a struggle to find information about the route, so I’m happy to document what I ran into up to about 3/4 of the way to &lt;a href=&quot;http://www.doc.govt.nz/parks-and-recreation/places-to-go/wellington-kapiti/places/tararua-forest-park/things-to-do/huts/renata-hut/&quot;&gt;Renata Hut&lt;/a&gt;.&lt;/p&gt;

&lt;h3 id=&quot;quick-facts&quot;&gt;Quick facts:&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;Start time:&lt;/strong&gt; 8:30am from Akatawara Hill Rd saddle&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Finish time:&lt;/strong&gt; ~ 2pm&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Walking time:&lt;/strong&gt; 5.5 hours - 3 up, 2.5 down&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Topomap:&lt;/strong&gt; BP32 and BP33, &lt;a href=&quot;http://www.topomap.co.nz/NZTopoMap?v=2&amp;amp;ll=-40.968219,175.150818&amp;amp;z=13&quot;&gt;here’s the frame on topomap.co.nz&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;This route uses the rarely used Akatawara Hill road entrance to the Tararua Forest Park (NOT the Ngatiawa Road entrance used to access Kapakapanui). The entrance is located directly across the road from the gravel dump spot at the summit of the Akatawara Hill Road. I’ve read of multiple break-ins at this spot, and passing a burnt out car on the way up (that was not there last time I passed!) is evidence enough that while it’s safe enough for day parking, it might not be the spot to leave a vehicle overnight. A pity, as the walk would make a great short overnight (an alternative overnight, albeit with a longer day, would be to park at Ngatiawa and walk over Kapakapanui and down the ridge to Renata).&lt;/p&gt;

&lt;p&gt;The route is entirely on 4WD track as far as Maymorn Juction, where the 4WD track continues to &lt;a href=&quot;http://www.doc.govt.nz/parks-and-recreation/places-to-go/wellington-kapiti/places/tararua-forest-park/things-to-do/huts/waiotauru-hut/&quot;&gt;Waiotauru Hut&lt;/a&gt;. I didn’t get up to the Junction, so my entire experience is on the 4WD. Note that I say “track”, &lt;em&gt;not&lt;/em&gt; “road” here - I was impressed that any vehicles can get up at all, but did see some tyre treads in the mud, so assume the track is still used. Because the track is 4WD, it is a relatively gentle gradient, and there is gravel along much of it.&lt;/p&gt;

&lt;p&gt;Starting at the saddle carpark, there is a steep rough section for a couple of hundred metres to start - I suspect this has intentionally been dug in to discourage anyone who doesn’t have an adequate vehicle from venturing up (I had optimistically hoped to get my all-wheel drive up the “road” (as it is marked on maps) some of the way to shorten the day, but this is impossible). After this, the track more or less levels, and winds its way up the hillside. I did this in winter, but judging from the size and location of the puddles, in the first 20 minutes of walking, expect to get wet feet. Most have a dry-feet route around them, but overall, your chances of keeping those feet dry are low - you might be better off just wading through.&lt;/p&gt;

&lt;p&gt;The 4WD track remains roughly the same all the way up. There are occasional rough patches, and it is clay, so parts are slippery, but it’s easy to move pretty quickly. There are a few places where the track forks - I think these have been added by 4WD-ers after some variation in the route and it’s obstacles - I just picked the option that looked the most used and it worked out fine - I think that all of the forks rejoined the main track in any case.&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/img/posts/renata-4wd-forks.jpg&quot; alt=&quot;Frequent forks in the track along the Waiotauru Road section&quot; /&gt;&lt;/p&gt;

&lt;p&gt;I was checking the topomap on my phone frequently and still managed to miss when the “road” turns into a normal track. For others’ reference, it is where the road goes around a wide bend, with a small patch of grass. While the track remains pretty much the same underfoot, it does close in a bit.&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/img/posts/renata-roadend.jpg&quot; alt=&quot;The &amp;quot;roadend&amp;quot;&quot; /&gt;&lt;/p&gt;

&lt;p&gt;The track continues - on the topomap it is shown as if the track goes directly along the ridgeline. In reality, it actually traverses on the eastern side and about 40-60 meters below the ridge. Along this section is where I encountered a lot of tree fall, all of it looking pretty recent. The entire route is unmarked with tape or triangles, so it’s just a case of sticking to the track and doing frequent map checks.&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/img/posts/renata-treefall.jpg&quot; alt=&quot;Treefall&quot; /&gt;&lt;/p&gt;

&lt;p&gt;It was all relatively small stuff, but because of this, some parts took a bit of work to fight through, over, or around. There weren’t any trunks thicker than about 20cm, so hopefully a kind 4WD club member or even DOC might make their way up with a chainsaw sometime. When I went up, the treefall was every 50 meters or so. Since I also had a relatively small dog to get through each bit as well, I found that it was taking me 2 or 3 minutes to negotiate each section. Probably without the dog it would be faster, but it’s still tough going.&lt;/p&gt;

&lt;p&gt;My turnaround point was a large slip in the saddle immediately before the &lt;a href=&quot;http://www.topomap.co.nz/NZTopoMap?v=2&amp;amp;ll=-40.968219,175.150818&amp;amp;z=15&quot;&gt;unnamed high point at an altitude of 762m on topomap BP33&lt;/a&gt;. While the DOC time to Renata Hut from the saddle was described as four hours, I had expected to make it up the hill a bit quicker, and couldn’t quite fit a full 8 hour day in. I had set my turnaround time to get to Maymorn Junction at least at 3 hours, knowing it was an additional 30-40 minutes to Renata Hut along the Junction.&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/img/posts/renata-slip.jpg&quot; alt=&quot;Slip where I turned around&quot; /&gt;&lt;/p&gt;

&lt;p&gt;I reached the slip bang on 3 hours. It was about as good a weather as I have ever had in the Tararuas, but I still eyed the cloud hovering around Kapakapanui with a bit of caution, and considered that I had to make my way back through all of the treefall, and made the correct decision to stick to my cutoff time. While it did take me less time to head downhill than uphill, I still spent a decent chunk of time maneuvering through downed trees, reaching the road again after about 2.5 hours. I estimate that it would have taken me at least an additional half an hour to reach Maymorn Junction, where the track from Kapakapanui meets this track, and the 4WD track drops off, and at least a half hour beyond that to reach the Hut.&lt;/p&gt;

&lt;p&gt;This trip was another great reconnaissance for future adventures. If there was slightly less dodgy looking car parking, this trip would be a great short overnight and I suspect would be a lot more popular. With the treefall, I’m not sure it’s a great day walk right now, as fighting through fall after fall becomes tiring quite quickly, especially when contemplating the return journey that same day - however, the forest is classic Tararuas, and the track is well graded and in good condition (aside from the trees!).&lt;/p&gt;
</description>
        <pubDate>Tue, 25 Jul 2017 00:00:00 +1200</pubDate>
        <link>https://joshmcarthur.com/tramping/2017/07/25/akatarawa-summit-to-renata.html</link>
        <guid isPermaLink="true">https://joshmcarthur.com/tramping/2017/07/25/akatarawa-summit-to-renata.html</guid>
        
        <category>tramping</category>
        
        <category>outdoors</category>
        
        <category>trip-report</category>
        
        
        <category>tramping</category>
        
      </item>
    
      <item>
        <title>Temporary PostgreSQL Database with Docker</title>
        <description>&lt;p&gt;Every now and then I need to restore an old database backup to grab some data out of. I use Postgres containers for
every app I work on, so I don’t really want to go polluting those. Here’s how I start a postgres container, restore a DB
into it, do what I need to do, then destroy it.&lt;/p&gt;

&lt;ol&gt;
  &lt;li&gt;Start the container: &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;docker run --rm -it -v `pwd`:/data postgres bash&lt;/code&gt;
    &lt;ul&gt;
      &lt;li&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;--rm&lt;/code&gt; means remove the container when it stops (we exit bash)&lt;/li&gt;
      &lt;li&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;-it&lt;/code&gt; means give me a interactive terminal (I need to type stuff and see output)&lt;/li&gt;
      &lt;li&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;-v&lt;/code&gt; means mount the directory I’m currently in to &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;/data&lt;/code&gt; - assuming the directory I’m in has a PG dump I want to use.&lt;/li&gt;
    &lt;/ul&gt;
  &lt;/li&gt;
  &lt;li&gt;Now in the container, we need to set up the database engine. The &lt;a href=&quot;https://hub.docker.com/_/postgres&quot;&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;postgres&lt;/code&gt;&lt;/a&gt; container has an entrypoint script that normally
does this for us, but we’ve told Docker to run &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;bash&lt;/code&gt; rather than this script.
    &lt;ul&gt;
      &lt;li&gt;Create the cluster: &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;pg_createcluster 9.5 main&lt;/code&gt; (replace 9.5 with whatever version of Postgres you have used)&lt;/li&gt;
      &lt;li&gt;Start Postgres: &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;service postgresql start&lt;/code&gt;&lt;/li&gt;
      &lt;li&gt;Create a user for ‘root’: &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;su - postgres -c &quot;createuser -s root&quot;&lt;/code&gt;&lt;/li&gt;
    &lt;/ul&gt;
  &lt;/li&gt;
  &lt;li&gt;Now we have a superuser Postgres user named “root”, we can do whatever we want, for example:
    &lt;ul&gt;
      &lt;li&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;createdb my_database&lt;/code&gt;&lt;/li&gt;
      &lt;li&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;cd /data&lt;/code&gt;&lt;/li&gt;
      &lt;li&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;psql my_database &amp;lt; my_database_backup.sql&lt;/code&gt;&lt;/li&gt;
      &lt;li&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;psql my_database&lt;/code&gt;&lt;/li&gt;
      &lt;li&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;SELECT count(*) from widgets&lt;/code&gt;&lt;/li&gt;
    &lt;/ul&gt;
  &lt;/li&gt;
  &lt;li&gt;When we’re done, we just need to exit bash. The container will stop and be removed by Docker.&lt;/li&gt;
&lt;/ol&gt;
</description>
        <pubDate>Wed, 11 Jan 2017 00:00:00 +1300</pubDate>
        <link>https://joshmcarthur.com/2017/01/11/temporary-postgresql-database-with-docker.html</link>
        <guid isPermaLink="true">https://joshmcarthur.com/2017/01/11/temporary-postgresql-database-with-docker.html</guid>
        
        <category>development</category>
        
        <category>docker</category>
        
        <category>postgres</category>
        
        
      </item>
    
      <item>
        <title>Totara Flats, Tararuas</title>
        <description>&lt;p&gt;A little while ago, I &lt;a href=&quot;/tramping/2015/06/19/powell-hut-tararuas.html&quot;&gt;blogged about the trip to Powell Hut&lt;/a&gt;. This post
decribes an alternative trip into the Tararuas, from the Waiohine road-end into Totara Flats Hut. I found this trip
much more challenging than the trip up the Gentle Annie track to Powell Hut, but at the same time a lot more
fulfiling.&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/img/posts/totara-flats-topomap.jpg&quot; alt=&quot;Topomap showing track to Totara Flats Hut&quot; /&gt;&lt;/p&gt;
&lt;blockquote&gt;
  &lt;p&gt;The track, marked in the thin red line &lt;a href=&quot;http://www.topomap.co.nz/NZTopoMap?v=2&amp;amp;ll=-40.959678,175.385056&amp;amp;z=14&quot;&gt;View Topomap in browser&lt;/a&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;We began the track from the Waiohine road-end. The road-end is reached by turning off just after the bridge over the Waiohine
River just outside Greytown, and then following a narrow but sealed country road. After crossing the railway tracks keep
an eye out for the turn off on your left - it’s just marked with an old yellow AA sign, and I missed the turn at first.&lt;/p&gt;

&lt;p&gt;The road soon turns into gravel but is a great drive up the gorge towards the picnic area. It’s similar to the Otaki Gorge
road, but not as long or windy. After heading through the picnic area, a turnaround bay is reached with a chain across the road.
In 2008 a &lt;a href=&quot;http://www.nzherald.co.nz/wairarapa-times-age/news/article.cfm?c_id=1503414&amp;amp;objectid=10975463&quot;&gt;large slip and washout&lt;/a&gt;
closed the road further up, and while repairs are going on the road is impassable. I’ve not heard either way about the
security at this carpark, but there were several cars parked there when we walked.&lt;/p&gt;

&lt;p&gt;Leaving your car at the turnaround area, you can cross the chain and walk up the road. You’ll reach the slip after about 5-10 minutes,
with the campground another 5-10 minutes after that.&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/img/posts/waiohine-gorge-slip.jpg&quot; alt=&quot;Crossing the slip on the Waiohine road&quot; /&gt;&lt;/p&gt;
&lt;blockquote&gt;
  &lt;p&gt;This was the slip &amp;amp; washout when we were there. Repairs have started so it may be easier to cross soon - but progress is slow.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;The campground has been a popular spot in the past, but cutting off road access gives it a strange abandoned air.
It’s no longer serviced but there is plenty of space for camping, and flush toilets here, if you are planning on
stopping overnight.&lt;/p&gt;

&lt;p&gt;To get to the tracks into the park, you’ll need to cross a large swing bridge. This bridge is pretty recent, but due to
it’s size and positioning in the narrow gorge you can expect some movement when the wind is blowing. It’s quite high up
above the river but a nice uphill slope allows nervous trampers to look uphill and focus on the end of the bridge.&lt;/p&gt;

&lt;p&gt;After crossing, there’s a intersection, with the left hand fork crossing over a ridge and dropping to the Tauherenikau River
and Cone Hut (apparently about 2-3 hours), and the right hand fork turning to follow the gorge up towards the flats. Taking the
right hand fork, the track condition soon deteriorates into large (semi-continuous) mud pools along the track route, interspersed
with large tree roots. The track condition was a slight shock to us after the well-maintained Gentle Annie Track, but seems more
typical of the classical tramping tracks in the Tararuas.&lt;/p&gt;

&lt;p&gt;The track carries on up the gorge, undulating along the river bank. Typically the track is routed over small bluffs
  so expect steep but short climbs and descents. There are several small streams and creeks along the gorge, but all major crossings
  are fitted with swing-bridges (The bridges don’t look terribly old and they are not on the topo map, so I imagine they were all done in one go).
  After about an hour, you’ll reach a nice Beech clearing just before Clem Creek. A good spot for a rest, and it’s looks like a popular
  camping spot judging from the number of old campfires. Clem Creek is about halfway along the narrow gorge section before
  opening out onto the flats so is also a good milestone to judge progress. The next hour continues much as the first -
  mud and roots.&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/img/posts/totara-flats-small-swingbridge.jpg&quot; alt=&quot;Totara Flats Swingbridge&quot; /&gt;&lt;/p&gt;
&lt;blockquote&gt;
  &lt;p&gt;One of the small swing-bridges over the smaller creeks going up the gorge&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;About halfway between Clem Creek and the flats you’ll reach an intersection with another track that heads over Cone Saddle
  and continues to Cone Hut to meet the left-hand fork of the track from the swing-bridge. This seems like a nice
  overnight trip that is on our to-do list, although I’ve read that the track continues to be muddy and rutted.&lt;/p&gt;

&lt;p&gt;Soon after the intersection is an impressively large old slip - there are two options for getting around it, both marked -
 either the river route or climbing up and around the slip. I recommend the river route unless the river is in flood - the
 slip is a lot larger than it looks from the intersection. Clamber down the river bank and then head upriver keeping an eye
 out for the orange triangles that mark the exit point for the diversion at the beginning of a short section of open
 grass. Keep an eye out from now on for sign of deer and pigs, both apparently common in this area. After the short
 bit of flats, the track climbs again steeply around some bluffs, and then drops back to the main body of flats.&lt;/p&gt;

&lt;p&gt;If you’re interested in checking out a historic hut, and don’t mind getting your boots wet, after joining the
 flats head off the track to the river. You should emerge within sight of a small rapid that diagonally crosses the
 river, with the end closest to you slightly further upstream. You can cross the river here in the right conditions and
 check out &lt;a href=&quot;http://www.doc.govt.nz/parks-and-recreation/places-to-go/wellington-kapiti/places/tararua-forest-park/things-to-do/huts/sayers-hut/&quot;&gt;Sayer’s Hut&lt;/a&gt;,
 an old Forest Service hut constructed of native timber with an open fire. Finding it from the river can be a little tough,
 but there are clear trails climbing the river bank after you cross the river that should get you within
 sight of the hut - it’s not directly visible from either bank of the river. We crossed over to the hut on the second day on
 the way back down the valley, but doing it on the first day probably makes more sense - less distance to travel in wet
 boots after crossing back over the river! The hut itself is in a lovely spot tucked into the bush back from the river, and this is also on our to-do list for an
 overnight trip. It sounds as though it doesn’t get frequent visitors - mostly locals and hunters.&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/img/posts/waiohine-river-crossing.jpg&quot; alt=&quot;Waiohine River Crossing Point&quot; /&gt;&lt;/p&gt;
&lt;blockquote&gt;
  &lt;p&gt;The river crossing point we used to reach Sayer’s Hut (a little wonky!)&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;From the crossing to Sayer’s the Totara Flats Hut is a quick walk up the valley through the grass. After the tough terrain
 up the gorge, the grass is a bit of a treat. Soon before reaching the hut, you’ll head back into some scrubby bush - the hut
 is 5 or 10 minutes on from this point.&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;http://www.doc.govt.nz/parks-and-recreation/places-to-go/wellington-kapiti/places/tararua-forest-park/things-to-do/huts/totara-flats-hut/&quot;&gt;Totara Flats Hut&lt;/a&gt;
 is a 26-bunk hut, so there is plenty of space. It’s often used as an alternative to Powell Hut, as the track that you have travelled
 on continues to the head of the valley and climbs to meet Gentle Annie track near Mountain House. The hut is a pretty standard design,
 with two bunk rooms and a common area with a mixed-fuel (wood/coal stove). There used to be gas cookers provided here, but DOC has
 removed them (though they’ve left the hut fee the same at 3 tickets ($15) per person - pah!). We had a slightly chilly night
 after the rain during the week, as there wasn’t much dry wood available. If you use some of the dry firewood from the wood shed,
 don’t forget to pop a bit more under cover to dry out for the next group! Also a hut to watch out for rats in - there were droppings
 everywhere when we were there - probably due to the quantity of food and little bits and pieces left behind by others. We initially
 had the hut to ourselves but were joined later in the evening by a couple of others who had diverted from Powell Hut due to
 ice higher up.&lt;/p&gt;

&lt;p&gt;The following day there are a couple of options. We had been hoping to do a long day, heading back down the valley to the track
 turnoff to Cone Hut, and then crossing over the saddle and back over the ridge back to the Waiohine Swing-bridge, but after
 seeing how long it took us to get up the valley we decided to head back the the way we came. On the way back we took our time a little
 bit more, and checked out a couple of geocaches on the way. It did take us about the same amount of time to get out as it did to
 reach the hut, so we must have been moving a little faster - perhaps because we weren’t trying to keep dry boots for as long as the
 first day!&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/img/posts/totara-flats-view.jpg&quot; alt=&quot;Totara Flats View&quot; /&gt;&lt;/p&gt;

&lt;p&gt;The whole trip up the valley took us pretty much bang on the DOC time of 4 hours. We normally do slightly under the DOC time, but
 the tough terrain certainly slowed us down - the roots require a lot of clambering and can be a bit slippery. This is also
 the trip on which we decided we needed a personal locator beacon - the country is beautiful, but has an air of inaccessibility.
 Despite all this, this is one of the best overnighter trips I’ve ever done. The closed off road, the steep and tough terrain, and the
 solitude in the valley (we saw 3 people in two days), makes for a fantastic escape. I highly recommend it!&lt;/p&gt;
</description>
        <pubDate>Fri, 31 Jul 2015 00:00:00 +1200</pubDate>
        <link>https://joshmcarthur.com/tramping/2015/07/31/totara-flats-tararuas.html</link>
        <guid isPermaLink="true">https://joshmcarthur.com/tramping/2015/07/31/totara-flats-tararuas.html</guid>
        
        <category>tramping</category>
        
        <category>outdoors</category>
        
        <category>tararua</category>
        
        
        <category>tramping</category>
        
      </item>
    
      <item>
        <title>Powell Hut, Tararuas</title>
        <description>&lt;p&gt;The Tararua ranges have a fearsome reputation in New Zealand. Frequently cloudy, snowy, rainy, misty, almost always windy, and very occasionally, sunny (though not to be trusted). Despite this bad rap, they are accessible, scenic, and have a broad range of trip options - from short dawdles through the bush to challenging alpine tramps over the tops.&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/img/posts/rocky_lookout_tararuas.jpg&quot; alt=&quot;View from Rocky Lookout, Tararua Forest Parkr&quot; /&gt;&lt;/p&gt;

&lt;p&gt;With Wellington so close by, these ranges offer so much. If you’re a Wellingtonian, there’s something there for you. Today, I’m going to be talking about a perfect intro trip - from the Holdsworth carpark near Masterton to Powell Hut and back. If you live anywhere within a 2 hour drive of this trip, it’s a must-do. It ascends gently from a river valley, travelling up through beech forest and subalpine bush and popping out above the bushline straight before the hut. In winter, you can expect to see snow around, but there shouldn’t be enough of it to worry too much.&lt;/p&gt;

&lt;p&gt;I’d consider the Holdswork carpark to be the main entry to the Tararuas, along with Otaki Forks on the Western side of the range. The carpark is just over 90 minutes drive from central Wellington along State Highway 2. The turn is between Carterton and Masterton (though closer to the latter), and is signposted ‘Mt Holdsworth’. There’s a small wood pulp processing plant (from memory, it’s something of the sort) next to the turn, so you can keep an eye out for the chimney stacks from that. Drive past the farm supplies place and the livestock sales yard and head along the rural road for about 15 kilometers until it narrows and then goes through the gate into the Holdsworth campsite. This campsite isn’t particular interesting as a destination in it’s own right, but can make a good staging post if you’re travelling from the city in the evening (Camping is DOC-standard $6 adult per night which will get you a grass spot of your choice, a cooking shelter and flush toilets).&lt;/p&gt;

&lt;p&gt;This carpark should be safe to leave your car (plenty of others do) - the ranger’s house is just up the driveway next to the carpark and while there’s campers around this should deter most - but take all precautions - don’t leave valuables in your car, make sure all the doors are locked etc. Don’t forget to make sure you’ve turned your headlights off!&lt;/p&gt;

&lt;p&gt;The track starts along a gravel road that goes to Holdsworth Lodge, a DOC managed accomodation facility that &lt;a href=&quot;http://www.doc.govt.nz/parks-and-recreation/places-to-go/wellington-kapiti/places/tararua-forest-park/things-to-do/lodges/holdsworth-lodge/&quot;&gt;offers group or sole-occupancy room options&lt;/a&gt;. There’s an intentions book on the left hand side as you face the lodge where you should write down your plans and when you’re expecting to be out - this will be the first thing that is checked if somebody has to come and find you. Once you’ve done that, the road carries on just past the lodge then becomes a narrower quad-bike-width track before reaching the bridge over the Atiwhakatu Stream.&lt;/p&gt;

&lt;p&gt;The track up towards Powell Hut is the aptly named “Gentle Annie” Track. For the first few minutes, it follows alongside the stream. The track is wide here with a good surface. Expect to see a few locals with walking their dogs (and kids!). After 10 minutes or so you’ll reach an intersection - the track continues straight ahead up the Atiwhakatu Valley, passing through the Donnelly Flat picnic area about 15 minutes after the intersection, eventually reaching &lt;a href=&quot;&quot;&gt;Atiwhakatu Hut&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;The Gentle Annie track narrows from the quad-bike sized track to more of a normal bush track - though the track surface remains excellent. It quickly enters a small valley, climbing up a spur towards the first stop, Rocky Lookout. The climb can be steeper in places, but there’s steps everywhere there should be, and plenty of places to pull over for a breather. Eventually you’ll reach the head of the valley and begin to traverse around the hillside, before joining another short spur for another bit of climbing. Eventually, you’ll emerge from the Beech forest, and if you look up the hill, you should see the railings of Rocky Lookout above you. This is a great spot with fantastic views. The rock the lookout is based around is fun to climb, and it’s a good chance to get your breath back, have some water and food. Expect the track from Holdsworth to Rocky Lookout to take between 30 and 40 minutes with one or two breather breaks on the way up. At Rocky Lookout, keep an eye out to the left for Powell Hut high up on the bushline.&lt;/p&gt;

&lt;p&gt;Fortunately, after Rocky Lookout there’s a bit more of a gentler climb up the last remaining bit of spur before reaching a saddle, where the track turns and begins to climb along the hill. It’s pretty easy going, with an easier gradient as it continues around the hill before meeting the start of a ridge where there is a key intersection and a couple of benches about 30 minutes after Rocky Lookout. Another good spot for a rest and some water! Going straight at this intersection descends down a valley to the Waiohine River and &lt;a href=&quot;&quot;&gt;Totara Flats Hut&lt;/a&gt;, which can be a good bad-weather alternative to Powell, but turn right to head towards Mountain House, the next landmark on the way to Powell Hut. The track climbs just a little more, before flattening off, crossing a couple of boardwalked sections, and then descending moderately to Mountain House. This section allegedly takes 20 minutes, but always seems to last much longer.&lt;/p&gt;

&lt;p&gt;Mountain House can be a good place for a quick stop, but no doubt you will have been having to look further and further up to be able to see Powell Hut and may have a slight dread of the hill that is to come - best to make it a quick stop and get stuck right into it. The track climbs up along the hill for a short period, before zigzagging a couple of times and then starting to climb up the left hand side of a spur that goes all the way up to the hut. The trick to this bit is to take breaks as needed - the DOC time to get up this section is generous and already allows for a few breaks. There’s a flat bit next to a couple of drainage holes that makes a good spot for a break, as it’s about halfway up the bush part of the section before the views emerge.&lt;/p&gt;

&lt;p&gt;Carrying on up the spur, you’ll eventually come to patchier sections of bush, and get into rockier terrain with steps on the steeper climbs to make things a bit easier. Watch out for ice at this level. There’s great views all the way up back down towards Holdsworth, down into the Waiohine Valley to Totara Flats, and out across the Wairarapa.&lt;/p&gt;

&lt;p&gt;It seems like there’s always just one more ridge to overcome, but eventually you’ll pop up to a surprisingly flat section, with the hut just a few metres along the hill.&lt;/p&gt;

&lt;p&gt;Powell Hut is a extremely popular hut, with bookings required in the summer months (Nov - Apr). In winter it seems to be variable, but with 28 bunks (plus plenty of floor space), it’s a pretty safe bet. There’s cookers installed with LPG tanks helicoptered in occasionally (making use of an &lt;a href=&quot;www.gw.govt.nz/Remote-gas-monitoring-system-for-Powell-Hut/&quot;&gt;interesting telecommunications system to send alerts&lt;/a&gt;), and a dual-fuel wood/coal burning stove for heating. This hut is a &lt;em&gt;Serviced&lt;/em&gt; hut, which means it requires 3 hut tickets per person, which translates to $15pp. Hut tickets can be bought from DOC offices and some outdoors stores, and go towards paying for hut maintenance and servicing. There doesn’t seem to be a great deal of good camping sites nearby the hut, but if need be there’s probably a couple of places to wedge a tent. There’s a great outlook from the hut over the whole of the Wairarapa, and the deck is quite nice if it’s not too cold and/or windy.&lt;/p&gt;

&lt;p&gt;Powell Hut was first built in 1939 by the Hutt Valley Tramping Club as a bit of a ski lodge - back in those days, a trip up to Ruapehu wasn’t quite as easy as it is today, and skiing in the Tararua’s was popular, both here and around &lt;a href=&quot;http://www.doc.govt.nz/parks-and-recreation/places-to-go/wellington-kapiti/places/tararua-forest-park/things-to-do/huts/field-hut-historic/&quot;&gt;Field Hut&lt;/a&gt; on the other side of the range. The hut was rebuilt as a Lockwood hut (as many NZ huts of that generate were, and are) in 1981, but burned down in 1999 (many huts burn down due to the fire being mishandled - for example hot ashes or embers escaping). In 2000 it was replaced by DOC and this is the iteration of the hut that stands today. This area is also the scene of &lt;a href=&quot;http://www.nzherald.co.nz/nz/news/article.cfm?c_id=1&amp;amp;objectid=161735&quot;&gt;an unsolved murder&lt;/a&gt; and even has &lt;a href=&quot;http://www.teara.govt.nz/en/photograph/12128/cedric-the-ghost&quot;&gt;a local ghost&lt;/a&gt;!&lt;/p&gt;

&lt;p&gt;If you’re staying the night at Powell (or have made good time on the climb up), you can continue up to the summit of Mt Holdsworth during summer, if the weather is reasonable. In winter there is snow and ice at this altitude and it is not safe to go all the way to the trig without the proper experience and equipment. From the hut, the track climbs steeply up to the ridge, and then clambers over, around, and through tussock before reaching a turn off to High Ridge (I believe this descents to Cone Ridge above the Waiohine). This is as far as I’ve ever been, but I believe this turnoff is about halfway to the trig.&lt;/p&gt;

&lt;p&gt;The descent is exactly the same as you came up (though there are steeper alternatives down from Mountain House to the Atiwhakatu Valley and out from there if you’d prefer a more difficult finish to the day). The steep bit down to Mountain House can be a bit tough on the knees and it’s worthwhile stopping once or twice on the way down this bit. After that it’s a nice doddle back down to Rocky Lookout, then the stream track back to the bridge, finally emerging at the carpark. Depending on how quick you are on the way down, the descent takes us about 2-2.5 hours, but will be longer if you stop on the way down as well.&lt;/p&gt;

&lt;p&gt;From the carpark, it’s time to head back out to the state highway. On the way home, we usually can’t resist stopping off at &lt;a href=&quot;http://www.thewhiteswan.co.nz/&quot;&gt;The White Swan&lt;/a&gt;, for a bit of a treat!&lt;/p&gt;
</description>
        <pubDate>Fri, 19 Jun 2015 00:00:00 +1200</pubDate>
        <link>https://joshmcarthur.com/tramping/2015/06/19/powell-hut-tararuas.html</link>
        <guid isPermaLink="true">https://joshmcarthur.com/tramping/2015/06/19/powell-hut-tararuas.html</guid>
        
        <category>tramping</category>
        
        <category>outdoors</category>
        
        <category>trip-report</category>
        
        
        <category>tramping</category>
        
      </item>
    
      <item>
        <title>Simple Ember CLI deploys</title>
        <description>&lt;blockquote&gt;
  &lt;p&gt;Note: I can’t vouch as to the best-practise compliance of this technique, but it’s worked well for me.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;One of the neat things about using a Javascript framework such as Ember.js is that you can host it just about
anywhere you like - it’s just static files after all. My first stop for hosting things like this is &lt;a href=&quot;https://pages.github.com&quot;&gt;Github Pages&lt;/a&gt; - it means the site is backed by Git (as it should be anyway), and your files are being served by Github’s &lt;a href=&quot;https://status.github.com&quot;&gt;pretty solid&lt;/a&gt; platform.&lt;/p&gt;

&lt;p&gt;If you’ve got a Github Pages site though, generally you are deploying to a branch named &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;gh-pages&lt;/code&gt;. The files you push to the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;gh-pages&lt;/code&gt; branch should also be the files you would normally place at the root location of your web server (i.e. there should be a file named &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;index.html&lt;/code&gt; that will be served by default. This becomes a bit tricky with git, as you need the files contained in one particular folder of your repository to be pushed to a different branch than the one your on.&lt;/p&gt;

&lt;p&gt;Based on a bit of research, I found that Git has a way of doing this, and it’s a pretty neat technique. The subcommand is called &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;subtree push&lt;/code&gt;, and it basically let’s you push a particular folder of your repository to a remote branch. In the context of Ember (Ember CLI in particular), this means pushing your &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;dist&lt;/code&gt; folder to the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;gh-pages&lt;/code&gt; branch, or:&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;git subtree push &lt;span class=&quot;nt&quot;&gt;--prefix&lt;/span&gt; dist origin gh-pages
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;It’s not quite as simple as that though! Applications generated with ember-cli (quite rightly in &lt;em&gt;most&lt;/em&gt; cases), add the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;dist&lt;/code&gt; folder to the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;.gitignore&lt;/code&gt; file, which will cause that subtree command to ignore files in that directory when pushing (i.e. it appears to succeed, but nothing is pushed). I haven’t yet found a good solution for this, so for now I’ve just stopped ignoring the dist folder. This invovles checking in changes every time I build, however it seems to be necessary for this deploy process to work.&lt;/p&gt;

&lt;p&gt;My workflow for making changes to an Ember CLI application now then is:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;Make the changes&lt;/li&gt;
  &lt;li&gt;Run the tests (if any, this is an area of Ember I need to catch up on)&lt;/li&gt;
  &lt;li&gt;Run &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;ember build production&lt;/code&gt; to update the contents of the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;dist&lt;/code&gt; folder&lt;/li&gt;
  &lt;li&gt;Run &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;git subtree push --prefix dist origin gh-pages&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Most of the time, I’ll put those last two commands into a shell script I can invoke, and call that &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;deploy.sh&lt;/code&gt; - but then - that’s it, that’s my deploy process. My changes go up in a couple of seconds and are live straight away. I can then check in my changes to &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;master&lt;/code&gt; as I normally would, and &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;master&lt;/code&gt; will continue to include a full copy of the Ember application that anyone can clone and hack away on.&lt;/p&gt;

&lt;h4 id=&quot;tips-tricks-and-notes&quot;&gt;Tips, Tricks and Notes&lt;/h4&gt;

&lt;ol&gt;
  &lt;li&gt;If you’ve got any files that you just want to go into the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;dist&lt;/code&gt; folder, put them in the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;public&lt;/code&gt; folder of your Ember CLI application. A perfect use case for this is the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;CNAME&lt;/code&gt; file that Github requires be in your &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;gh-pages&lt;/code&gt; branch if you wish to direct a custom domain name to your Github Pages site.&lt;/li&gt;
  &lt;li&gt;If you are not using a custom domain, and you’re pushing to a project repository, then your Github pages site will be available at: &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;https://[github username].github.io/[your repository name]&lt;/code&gt;. In order to get your assets, etc. loading correctly (because the site is in a subfolder), you need to add the following to your &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;config/environment.js&lt;/code&gt;:&lt;/li&gt;
&lt;/ol&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-javascript&quot; data-lang=&quot;javascript&quot;&gt;      &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;environment&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;===&lt;/span&gt; &lt;span class=&quot;dl&quot;&gt;&apos;&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;production&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&apos;&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;ENV&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;baseURL&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;dl&quot;&gt;&apos;&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;/name-of-your-repo&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&apos;&lt;/span&gt;
      &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
    &lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;ol&gt;
  &lt;li&gt;If you would prefer a more integrated approach, you might be interested in the &lt;a href=&quot;https://github.com/poetic/ember-cli-github-pages&quot;&gt;ember-cli-github-pages&lt;/a&gt; addon for Ember CLI. Personally, it’s not for me, as I prefer to put together my own git commands - I’m not so sure it’s an Ember addon should be taking care of the whole deployment like that.&lt;/li&gt;
&lt;/ol&gt;

</description>
        <pubDate>Sat, 25 Apr 2015 00:00:00 +1200</pubDate>
        <link>https://joshmcarthur.com/2015/04/25/simple-ember-cli-deploys.html</link>
        <guid isPermaLink="true">https://joshmcarthur.com/2015/04/25/simple-ember-cli-deploys.html</guid>
        
        
      </item>
    
      <item>
        <title>Walking the Waikaremoana</title>
        <description>&lt;p&gt;The Waikaremoana track is one of the Department of Conservation’s nine &lt;a href=&quot;http://www.doc.govt.nz/parks-and-recreation/tracks-and-walks/great-walks/&quot;&gt;great walks&lt;/a&gt;, and is located in the Te Urewara area of the North Island, inland from the East Cape of the North Island of New Zealand. Recently, Te Urewera was one of New Zealand’s many National Parks, however a recent Treaty of Waitangi settlement has made the park a seperate legal entity, managed by a board comprised of Tūhoe and crown stakeholders (&lt;a href=&quot;http://www.doc.govt.nz/parks-and-recreation/national-parks/te-urewera/features/tuhoe-claims-settlement/&quot;&gt;This page&lt;/a&gt; on the DOC website has more info).&lt;/p&gt;

&lt;p&gt;The Track is 46km long, and has both huts and campsites along the route (unlike many other great walks, which tend to have hut accomodation only). It is not a loop track, and so requires some form of transport to get between the two ends of the track - Onepoto and Hopuruahine Landings. It can be walked in either direction, but the most common direction is to start at Onepoto, the south end of the track, and walk through to finish at Hopuruahine.&lt;/p&gt;

&lt;p&gt;I had the pleasure to walk the Waikaremoana track recently, over the 2014/2015 summer break. The rest of this post covers my personal experience of the track, it’s accomodations, facilities, and transport options.&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/img/posts/waikaremoana/view.jpg&quot; alt=&quot;View from Bald Knob, Panekiri Ridgeline&quot; /&gt;&lt;/p&gt;

&lt;h2 id=&quot;the-track&quot;&gt;The Track&lt;/h2&gt;

&lt;p&gt;Interestingly, although it is most common to walk the track from South to North (that is, from Onepoto to Hopuruahine), the spaces on the track when I went to book made it necessary to go the opposite direction, starting at Hopuruahine and finishing at Onepoto. I found this direction much better. It avoids the tough climb on the first day of the tramp (in the usual direction), as the climb from the other side is much better track, and is on the second-to-last day, so packs are just that little bit lighter.&lt;/p&gt;

&lt;p&gt;The track condition is variable, depending on the track’s age. With the Urewera’s high rainfall, bogs and mud is pretty regular, with some creative workarounds on the edges of some bogs. Given the cost of doing the track (Either $14 or $32 depending on whether you are camping or staying in the huts), I would expect more work on maintaining the track though. During summer, there’s DOC staff on the track full-time, and while we saw a crew doing some weed-eating to keep the grass back around a couple of huts, there’s plenty of scope to add a bit of gravel on some of the boggier bits. To DOC’s credit, the section of track on the second-to-last day climbing to the Panekire bluff is fantastic (and looks very recent). 454 (that my partner counted) steps make the very steep ascent tiring, but over quickly. The old track can be seen beside, and at times underneath the staircases on this part of the track, and after seeing this I can appreciate the improvement.&lt;/p&gt;

&lt;p&gt;The track is clearly marked with DOC-standard orange triangles, with a reassuring larger triangle every now and then. With no other tracks in the close vicinity of the track, though, it’s largely a case of following the track - getting lost on this one would be pretty tough (which isn’t to say it doesn’t happen!). There was just one confusing section, on the last day just past the halfway point on the descent down the Panekire ridgeline, where what looked like a new benched track had been cut, but the orange triangles followed the old track for a few hundred meters. We followed the bench track, preferring a better surface and gradient to track markers, but this wasn’t necessarily the right move. I’m not sure why there isn’t a sign or markers on both routes here to provide guidance, but can advise that the two sections of track do re-join eventually. Expect to be without markers for 10 or 15 minutes, and carry on!&lt;/p&gt;

&lt;div class=&quot;image-box&quot;&gt;
	&lt;figure&gt;
		&lt;img alt=&quot;A bit of a mud bog on the Waikaremoana Track&quot; src=&quot;/img/posts/waikaremoana/track.jpg&quot; /&gt;
		&lt;figcaption&gt;A bit of a mud bog on the Waikaremoana Track&lt;/figcaption&gt;
	&lt;/figure&gt;
&lt;/div&gt;

&lt;h2 id=&quot;the-accomodation&quot;&gt;The Accomodation&lt;/h2&gt;

&lt;p&gt;There’s two options for accomodation on the track - huts, or campsites. Generally, the huts seem to be the more popular option, especially with families with younger children (sidenote: this track has the highest number of families with kids I’ve seen - but I still think Abel Tasman is the easier track to start with). The huts, though, are twice the price of the campsites, and you certainly shouldn’t expect any luxuries. During the tramp, I checked out most of the huts on the track, and they’re all of a similar standard. You can expect a DOC-standard vinyl mattress, a roof over your head, a wood-fired stove, and, if you’re staying in one of the cutting-edge huts, solar-powered LED lighting.&lt;/p&gt;

&lt;p&gt;Since I had a lightweight tramping tent, I went with the camping option, and was glad that I did. The campsites have shelters with water, a long-drop toilet and a cleared area of grass for tents. Each campsites have a maximum capacity of 15-20 tents, though some are smaller. I enjoyed the privacy and self-contained nature of the camping option, although I stayed in a hut the last night, at Panekire Hut (there’s no camping available there, I assume due to the fairly exposed location of the hut on the edge of a bluff, and the frequently adverse weather there.&lt;/p&gt;

&lt;div class=&quot;image-box&quot;&gt;
	&lt;figure&gt;
		&lt;img alt=&quot;Campsite on the first day&quot; src=&quot;/img/posts/waikaremoana/campsite.jpg&quot; /&gt;
		&lt;figcaption&gt;Typical campsite - on the first day&lt;/figcaption&gt;
	&lt;/figure&gt;
&lt;/div&gt;

&lt;p&gt;One important note is that neither the huts or campsites are provided with gas cookers. This is a key difference from some of the South Island great walks, where cookers are provided in the Summer season. I ran into one family starting the walk at Panekire, who only realized this after arriving at the hut after a 4 to 5 hour ascent up the ridgeline. Hopefully they were able to beg and borrow a cooker and gas for the remainer of their walk! At a pinch, you can cook on top of the wood-burner, but this’ll only work if you’re staying in huts, and will certainly add to your cooking time. Best to take along a small gas cooker and a couple of cannisters of gas.&lt;/p&gt;

&lt;p&gt;One aspect of the track accomodation that disappointed me was the communication and service from the rangers who are assigned to stay at the huts for a number of days. Previous great walks that I’ve done have had weather forecasts posted somewhere in each hut, along with any notes about upcoming sections of track. Waikaremoana huts do not do this, and this was unanticipated by me, as I had expected to be able to get updates on the weather in this way. I’m also used to rangers carefully checking hut and campsite bookings to make sure everyone is where they’re supposed to be, and this only happened for me at Panekire Hut. To be fair, this is the only hut I stayed at on the track, but the campsites all require booking plenty of time in advance as well. It was really disappointing to have paid for a space in the campsite, and then not have this enforced at all. I heard after I got off the track that this is fairly well known amongst backpackers especially, and quite a few people camp the track without a booking and just gamble on not being checked on. I feel like this is a dishonest way of doing the track, since the fees go into maintenance and upkeep of the facilities, but I also feel like it reflects a failure of DOC to enforce their own restrictions on usage of facilities. At a minimum, having a ranger visit all of the campsites that are easily accessible from their assigned hut should really be happening - it gives campers the opportunity to ask questions and find out about the track ahead, and is probably useful for search and rescue purposes if trampers are checking in with a ranger as they reach each campsite.&lt;/p&gt;

&lt;h2 id=&quot;start--end-accomodation&quot;&gt;Start &amp;amp; End Accomodation&lt;/h2&gt;

&lt;p&gt;DOC recommends the Waikaremoana Holiday Park for accomodation before and after the track - which is reasonable, since it is DOC that runs this park. Instead of the Waikaremoana Holiday Park though, I would recommend Big Bush. Big Bush Holiday Park is a few kms down the road from one end of the track at Onepoto Landing, and has campsites and backpackers. It also looked to have some cabins as well. Big Bush is the base of the water taxis and the predominant shuttle service in the area, and is also a lot closer to the start (or end!) of the track than Waikaremoana Holiday Park, which is right in the middle between the start and end.&lt;/p&gt;

&lt;p&gt;I got the impression that they weren’t particularly impressed with the DOC holiday park - apparently, Big Bush used to be &lt;em&gt;the&lt;/em&gt; Waikaremoana Holiday Park, and DOC renamed their park to match, forcing them to re-brand. Between that, and DOC recommending their park on all the track documentation, I believe that the DOC Waikaremoana Holiday Park gets the lion’s share of the visitors.&lt;/p&gt;

&lt;p&gt;Full disclosure though, I stayed at the DOC park. Why? Really, it never occurred to me as an option. I left the car parked at Big Bush while I did the track though, and I wish I’d stayed there. It was a pain to travel the 13km (20 minutes on gravel road) between Waikaremoana Holiday Park and Big Bush, especially after finishing the track.&lt;/p&gt;

&lt;p&gt;Even without this, I wouldn’t rate Waikaremoana Holiday Park. It’s typically extremely busy during the summer months, and seemed a little cramped in places. Tent site boundaries are strictly enforced, and as a tramper with an early start, it’s not great to be randomly placed next to a multi-family group, all with excitable young kids. So I think - stay at Big Bush. There’s tonnes more room for camping, seems that you can pop your tent wherever you want, and the water taxi van leaves right from where you’re staying!&lt;/p&gt;

&lt;h2 id=&quot;transport&quot;&gt;Transport&lt;/h2&gt;

&lt;p&gt;Transport arrangements depend on which end of the track you start from, and how you want to get there The most common option seems to be a water taxi from Big Bush (there’s theoretically other providers, but I saw no sign of them). Road shuttles are also available (either from Big Bush, or others), but the gravel roads in the area are downright unpleasant and should be avoided wherever possible. The water taxi is a pretty typical dual-engine type similar to the smaller water taxis used on the Abel Tasman track (they seat about 15). I opted to start from Big Bush, with a short ride in a van packed with people and packs to Onepoto Landing (one end of the track), and then a 20 minute water taxi across the lake to the opposite end of the track and Hopuruahine Landing. This costs $50pp, and included a pick-up at Onepoto Landing a few days later to return to Big Bush and save me from walking the extra 4km down the road to where the car was parked at the holiday park.&lt;/p&gt;

&lt;p&gt;One thing that I was unsure about before heading into the Ureweras was track parking. DOC holds no responsibility for vehicle break-ins or theft, which is fair enough - both ends of the track are remote and unmonitored, and there’s not really anything stopping thieves. Because of this, I parked at Big Bush Holiday Park for the duration of the track, so at least there’d be people around. I did see a few cars parked at the track end though, so it does happen. Really, it’s the same conditions as any other tramp - there’s no guarantee of security, but in many ways, the remoteness is in your favour. There’s plenty of parking space at Onepoto Landing, but there wasn’t much space at Hopuruahine. If you’re determined to park at the track end, you’re probably walking from Hopuruahine back towards your vehicle parked at Onepoto.&lt;/p&gt;

&lt;p&gt;Generally though, I had no problems with transport. It was smooth, quick, and pretty affordable considering that the $50pp fee covered two van rides, parking for the duration, and the boat ride itself.&lt;/p&gt;

&lt;h2 id=&quot;timing&quot;&gt;Timing&lt;/h2&gt;

&lt;p&gt;This is one of these tracks where you can spend as little or as long as you want doing it (DOC bookings permitting of course!). Three or four nights seemed to be the common number, and I did it in three, starting at Hopuruahine, staying at Waiharuru campsite, Waiopaoa campsite, and Panekire hut. The obvious effect in how long you’d like to spend doing the track is how long you’d like to spend each day walking. I ended up with a fairly short day the first day of about two hours, resulting in a lazy afternoon at the campsite, but meaning I had a long, seven-hour day the following day to reach Waiopaoa. The final two days were fairly consistent at about 3 hours each. Many families add an extra night to break that 7-hour day up, but conversely, quite a few people seem to do it in two nights with long walking days, and there were two or three people I ran into doing the whole track in a single day.&lt;/p&gt;

&lt;p&gt;I feel like the three night option is about right for most people, but it was restricting being required to choose the campsites we would stay at months in advance. Looking at a map, the track between Waiharuru and Waiopaoa looks like it follows the lake and would be pretty flat, but it had a couple of saddle crossings, rocks to clamber over, mud pools, and the terrain undulated right through the day, meaning that it was unexpectedly tiring. If I’d known how tough that day was going to be, I would have stayed at the campsite back from Waiopaoa, at Korokoro, and made up the time the following morning. I guess the lesson here is to consider not only the distance, but also the type of track you’ll be travelling. DOC times tend to be slightly imprecise due to the need to cater to many different walking paces, and I usually run shorter than the DOC time - I wasn’t expecting to take as long as the track guide said it would on the second day, and didn’t have a great time then.&lt;/p&gt;

&lt;h2 id=&quot;equipment&quot;&gt;Equipment&lt;/h2&gt;

&lt;p&gt;I almost always over-equip when I tramp. I’m a bit of an accessorizer, but that’s not uncommon on Great walks I think. I’m slowly coming round to more minimalist view though, which is why I’m writing this section.&lt;/p&gt;

&lt;p&gt;If you’re doing a great walk for the first time, this is probably your first serious multi-day tramp. The temptation is to throw everything in you might possibly need, and it’s surprisingly tough to resist (I know, I’ve been there). The trick is to focus on the essentials, and prepare to make-do for anything you might forget to bring.&lt;/p&gt;

&lt;p&gt;If you’re camping, a tent and a sleeping mat are pretty important. Remember you’re carrying the tent for hours, not just from your car to the ground. I split our two-person tent with my partner - she takes the poles and pegs, and I take the tent itself. This works out pretty evenly.&lt;/p&gt;

&lt;p&gt;Clothes are also important, but you don’t need as many as you think you might. I now take one set of walking clothes (light and breathable), one set of clothes to wear around the campsite (relatively warm and comfortable), and a few clothes designed for particular conditions (thermals and fleece for if it’s cold, rain jacket for rain, sun hat and glasses etc). The epiphany I had was that, after a couple of hours walking with a heavy pack, after the first day - everybody smells. There’s no point taking different clothes to wear each day for a 3 or 4 day walk - just make sure that when you’re going to finish up your day, you’ve got a less whiffy set of clothes to change into for closer quarters, such as a hut or tent, where your stench may be more noticible.&lt;/p&gt;

&lt;p&gt;Food is vital (for me, anyway). Try and take lightweight things that don’t need a bunch of ingredients to cook. On this tramp, I took these neat things called Backcountry Meals. They’re full “gourmet” meals that have been dehydrated, so they weigh less than 200 grams for a satchet that feeds two people. To cook, you boil water, add water to sachet, seal and wait for 10 minutes. Delightfully easy after a long day. Each sachet costs about $13-15 each though, so they’re not the cheapest option. Dehydrated food also has a kind of distinctive texture as well, which can get tiring after a few days. Next trip, I’m planning to do more soup &amp;amp; noodle type dishes, and might even splash out and take some veggies. Perhaps just one Backcountry Meal, as a treat, but I found it too much night after night.&lt;/p&gt;

&lt;p&gt;I just used my Android phone for photos on this trip, and it did mostly fine. I turned Airplane mode on and the battery lasted the whole trip (I turned the phone off about 7pm each night, but it was on  from about 8:30am each morning til then, so lasted well). Don’t take an SLR unless you’re going on a dedicated photography trip and are willing to take care of it constantly! I also took a cool little HD video camera that I have called a &lt;a href=&quot;http://polaroid.com/cube&quot;&gt;Polaroid Cube&lt;/a&gt; on this walk. I did way more videoing than I usually do, and I hope that it’ll eventually become my main image AND video capture tool when tramping, since it’s cheap and fairly resilient to damage, but I still don’t quite trust it to not lose anything yet, we’ll see.&lt;/p&gt;

&lt;p&gt;I do hope that this post hasn’t become to preachy. When I was researching my tramp on the Waikaremoana track, I was struck by the lack of guidance aside from the official DOC great walk guide, and I hope that this provides just a little useful information when anyone keen to get out there and knock this one off!&lt;/p&gt;

&lt;p&gt;Helpful information:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;strong&gt;&lt;a href=&quot;http://www.doc.govt.nz/Documents/parks-and-recreation/tracks-and-walks/east-coast-hawkes-bay/lake-waikaremoana-track.pdf&quot;&gt;Lake Waikaremoana Great Walk Guide (PDF)&lt;/a&gt;&lt;/strong&gt; - The official guide.&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;&lt;a href=&quot;http://www.doc.govt.nz/parks-and-recreation/tracks-and-walks/east-coast/te-urewera/lake-waikaremoana-great-walk/&quot;&gt;Lake Waikaremoana Walk Page on the DOC website&lt;/a&gt;&lt;/strong&gt; - A bit more information than fits in the guide, likely to be more up to date.&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;&lt;a href=&quot;http://www.lakewaikaremoana.co.nz/&quot;&gt;Big Bush Holiday Park&lt;/a&gt;&lt;/strong&gt; - accomodation, water taxis. Seemed like nice Kiwi people.&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;&lt;a href=&quot;http://www.waikaremoana.info/&quot;&gt;Waikaremoana Holiday Park&lt;/a&gt;&lt;/strong&gt; - the official DOC holiday park - central between track ends but can be busy and cramped.&lt;/li&gt;
&lt;/ul&gt;

</description>
        <pubDate>Mon, 05 Jan 2015 00:00:00 +1300</pubDate>
        <link>https://joshmcarthur.com/tramping/2015/01/05/walking-the-waikaremoana.html</link>
        <guid isPermaLink="true">https://joshmcarthur.com/tramping/2015/01/05/walking-the-waikaremoana.html</guid>
        
        <category>walking</category>
        
        <category>tramping</category>
        
        
        <category>tramping</category>
        
      </item>
    
      <item>
        <title>Multistep form validations with Rails and Wicked</title>
        <description>&lt;blockquote&gt;
  &lt;p&gt;Note: This blog post was originally posted at the &lt;a href=&quot;http://brains.rabid.co.nz/2014/09/29/rails-multistep-forms.html&quot;&gt;Rabid Brains blog&lt;/a&gt; (my employer’s blog).&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Multistep forms are the bane of the developer’s existence. No matter how you cut it, the fact that multiple request/response cycles are required to create a single resource goes against the grain of a whole bunch of acronyms representing fairly popular patterns and specifications (e.g. HTTP). Despite that, they’re a pretty well established usability pattern when you just have a tonne of information to collect and not much space to do it in, so it’s well worth keeping a method on hand to throw these types of forms together when it’s gotta be done.&lt;/p&gt;

&lt;!--break--&gt;

&lt;p&gt;In this post, I’m going to talk about how to create a multi-step form process in Ruby on Rails without throwing all that the framework can offer out the window. In particular, I’m going to discuss the structure the form controller can take, and how to perform incremental validations to provide useful feedback to users as they move through the fields. If you’d rather just look through the code, I’ve developed a demo application using the instructions in the blog post. You can check out &lt;a href=&quot;https://github.com/joshmcarthur/multistep-blog-demo&quot;&gt;the source code&lt;/a&gt;, or &lt;a href=&quot;https://jm-multistep-blog-demo.herokuapp.com/&quot;&gt;the application on Heroku&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;First of all, I suggest that you don’t set out and try and build the logic for managing the form steps yourself. This part in particular can end up quite messy in Rails, and is always consistent between applications, making it prime for extraction into a Rubygem. Luckily for us, a stable, maintained gem named &lt;a href=&quot;https://github.com/schneems/wicked&quot;&gt;wicked&lt;/a&gt; exists that includes this exact behaviour. Use the gem, read the README, and be glad that your controllers can remain free of the numerous helpers you would otherwise have to write to manage and move between steps.&lt;/p&gt;

&lt;p&gt;With Wicked installed, you’re all set to create your controller structure. The key thing to realise here is that if you are adding a multistep form, you are really creating a first-class resource in your application - at least, as far as your controllers are concerned. What I’m getting at here is that you musn’t try and shoehorn the controller actions you will be adding to your controller for your &lt;em&gt;model resource&lt;/em&gt; - the fact that there are multiple steps being presented, and different fieldsets being submitted, justifies an individual controller to contain all this logic.&lt;/p&gt;

&lt;p&gt;Let’s say the top-level resource is named &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Pet&lt;/code&gt;. We’ll go into the attributes of the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Pet&lt;/code&gt; model in just a minute, but for now, that tells us that our top-level resource controller should be named &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;PetsController&lt;/code&gt;, and be routed 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;c1&quot;&gt;# config.routes.rb&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;resources&lt;/span&gt; &lt;span class=&quot;ss&quot;&gt;:pets&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;ss&quot;&gt;only: &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;ss&quot;&gt;:new&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&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;:show&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;ss&quot;&gt;:index&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;root&lt;/span&gt; &lt;span class=&quot;ss&quot;&gt;to: &lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&apos;pets#index&apos;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Now we have a controller for managing our &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Pet&lt;/code&gt; resource, we now need to create a controller for managing our &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Pet&lt;/code&gt; resource &lt;em&gt;form&lt;/em&gt;. For clarity, this should probably be in it’s own namespace (otherwise, you’ll have a fairly generic-looking &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;FormStepsController&lt;/code&gt;). You can call this controller whatever you like, but something representative of what it does would be most useful - perhaps &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Pet::FormStepsController&lt;/code&gt;, &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Pet::StepsController&lt;/code&gt;, or something more high-level, such as &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Pet::BuildController&lt;/code&gt;. For the rest of this post, I’m going to use &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Pet::StepsController&lt;/code&gt;, but you can use what you like - just be sure to make appropriate replacements when necessary. Let’s create that controller now:&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;./bin/rails generate controller pet/steps_controller show update
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;You’ll notice that I’ve generated the controller with just two actions - &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;show&lt;/code&gt; and &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;update&lt;/code&gt;. This is actually all you need for this form - remember, Wicked is handling the step logic for us, and this controller is responsible for a form step, not the resource. This means that our actions map to &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;show&lt;/code&gt;-ing a form step, and &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;update&lt;/code&gt;-ing the record with the attributes for that step. Let’s add the routes for this new controller now - remember, it will be nested within our top-level resource controller:&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;# config/routes.rb&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;resources&lt;/span&gt; &lt;span class=&quot;ss&quot;&gt;:pets&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;ss&quot;&gt;only: &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;ss&quot;&gt;:new&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&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;:show&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;ss&quot;&gt;:index&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;resources&lt;/span&gt; &lt;span class=&quot;ss&quot;&gt;:steps&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;ss&quot;&gt;only: &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;ss&quot;&gt;:show&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;ss&quot;&gt;:update&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;],&lt;/span&gt; &lt;span class=&quot;ss&quot;&gt;controller: &lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&apos;pet/steps&apos;&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;

&lt;span class=&quot;n&quot;&gt;root&lt;/span&gt; &lt;span class=&quot;ss&quot;&gt;to: &lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&apos;pets#index&apos;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Notice how we can still use &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;resources&lt;/code&gt; with our new controller? Rails is going to cleverly pass through the form step in the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;:id&lt;/code&gt; parameter for this route, as this identifies &lt;em&gt;which&lt;/em&gt; step to &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;show&lt;/code&gt; or &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;update&lt;/code&gt;. So, the route to a form step with these routes will look like this:&lt;/p&gt;

&lt;p&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;/pets/1/steps/identity&lt;/code&gt; - where &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;1&lt;/code&gt; is the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;:pet_id&lt;/code&gt;, and &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;identity&lt;/code&gt; is the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;:id&lt;/code&gt;. Nice!&lt;/p&gt;

&lt;p&gt;Now that we’ve got our controller structure set up, let’s leave this area for now and go take a look at our &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Pet&lt;/code&gt; model - this is where we’ll set up our form steps, and tweak our validation to let us just check validity for a particular step.&lt;/p&gt;

&lt;hr /&gt;

&lt;p&gt;Let’s say (for the purposes of having a model complex enough to justify multiple steps), our &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Pet&lt;/code&gt; model has the following schema:&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;create_table&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;pets&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;ss&quot;&gt;force: &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;o&quot;&gt;|&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;t&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;|&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;t&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;string&lt;/span&gt;   &lt;span class=&quot;s2&quot;&gt;&quot;name&quot;&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;t&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;string&lt;/span&gt;   &lt;span class=&quot;s2&quot;&gt;&quot;colour&quot;&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;t&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;string&lt;/span&gt;   &lt;span class=&quot;s2&quot;&gt;&quot;owner_name&quot;&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;t&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;text&lt;/span&gt;     &lt;span class=&quot;s2&quot;&gt;&quot;identifying_characteristics&quot;&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;t&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;text&lt;/span&gt;     &lt;span class=&quot;s2&quot;&gt;&quot;special_instructions&quot;&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;t&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;datetime&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;created_at&quot;&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;t&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;datetime&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;updated_at&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;We’re going to have three form steps for this model - ‘identity’ (which will collect name, and owner_name), ‘characteristics’ (which will collect identifying_characteristics and colour), and ‘instructions’ (which will collect special_instructions). We’ll define a &lt;a href=&quot;http://apidock.com/rails/Class/cattr_accessor&quot;&gt;class-level accessor&lt;/a&gt; for holding our form steps:&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;# app/models/pet.rb&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;Pet&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;Base&lt;/span&gt;
  &lt;span class=&quot;n&quot;&gt;cattr_accessor&lt;/span&gt; &lt;span class=&quot;ss&quot;&gt;:form_steps&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;do&lt;/span&gt;
  	&lt;span class=&quot;sx&quot;&gt;%w(identity characteristics instructions)&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;Now we can access our form steps using the following method call: &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Pet.form_steps&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Next, let’s set up our validations. We’ll be requiring that all the fields be filled in, but only if you’re on the appropriate step. To check this, we need to implement a method that checks whether certain validations should be run based on what step we’re on. The validation check isn’t too simple - it’s not enough to just check the current step, because validations should still be run on previous steps, and if the current form step is nil. Let’s go ahead and implement that method now:&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;# app/models/pet.rb&lt;/span&gt;

&lt;span class=&quot;k&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;required_for_step?&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;step&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
  &lt;span class=&quot;c1&quot;&gt;# All fields are required if no form step is present&lt;/span&gt;
  &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;kp&quot;&gt;true&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;form_step&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;nil?&lt;/span&gt;

  &lt;span class=&quot;c1&quot;&gt;# All fields from previous steps are required if the&lt;/span&gt;
  &lt;span class=&quot;c1&quot;&gt;# step parameter appears before or we are on the current step&lt;/span&gt;
  &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;kp&quot;&gt;true&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;if&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;nf&quot;&gt;form_steps&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;index&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;step&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;o&quot;&gt;&amp;lt;=&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;nf&quot;&gt;form_steps&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;index&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;form_step&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;…hang on a minute - how do we know what form step we’re on at the moment? We don’t! Let’s add an &lt;a href=&quot;http://apidock.com/ruby/Module/attr_accessor&quot;&gt;instance-level accessor&lt;/a&gt; to our model to store the current form step:&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;# app/models/pet.rb&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;Pet&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;Base&lt;/span&gt;
 &lt;span class=&quot;c1&quot;&gt;# ...&lt;/span&gt;

 &lt;span class=&quot;nb&quot;&gt;attr_accessor&lt;/span&gt; &lt;span class=&quot;ss&quot;&gt;:form_step&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;p&gt;Great! Now we can detect whether we need to run validations for a set of attributes or not, based on which step we’re on. Conveniently, Rails allows us to pass in an &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;if&lt;/code&gt; option to each validation that determines whether it should be run or not. We could implement the validations for the first step like so then:&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;validates&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;:owner_name&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;ss&quot;&gt;presence: &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;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;n&quot;&gt;required_for_step?&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;ss&quot;&gt;:identity&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 go ahead and implement the validations for the other steps the exact same way - I’ll leave that as an exercise to the reader.&lt;/p&gt;

&lt;p&gt;Before we head back to our controller, I’m just going to make a very brief suggestion. If you’re building a multistep form yourself your validations are likely to be multiple lines for each step, and possibly even for each attribute. If you do find yourself having to do that, I would encourage you to look into the &lt;a href=&quot;http://apidock.com/rails/Object/with_options&quot;&gt;with_options helper&lt;/a&gt; - this will let you define your &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;if&lt;/code&gt; option in a single place, and then apply all the validations within the block. Here’s an example, showing each of the attributes for the first step on individual lines:&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;with_options&lt;/span&gt; &lt;span class=&quot;ss&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;n&quot;&gt;required_for_step?&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;ss&quot;&gt;:identity&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;step&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;|&lt;/span&gt;
  &lt;span class=&quot;n&quot;&gt;step&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;validates&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;presence: &lt;/span&gt;&lt;span class=&quot;kp&quot;&gt;true&lt;/span&gt;
  &lt;span class=&quot;n&quot;&gt;step&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;validates&lt;/span&gt; &lt;span class=&quot;ss&quot;&gt;:owner_name&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;ss&quot;&gt;presence: &lt;/span&gt;&lt;span class=&quot;kp&quot;&gt;true&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, it’s overkill for one-liner, simple validations, but it’s quite a nice syntax if you’ve got more complex rules.&lt;/p&gt;

&lt;p&gt;When we last looked at our controller, it had been generated with two actions - &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;show&lt;/code&gt; and &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;update&lt;/code&gt;. Now it’s time to add Wicked to our controller, and implement our actions.&lt;/p&gt;

&lt;p&gt;The first, and most obvious thing to do, is to include Wicked’s helpers into our controller - as per their README:&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;Pet::StepsController&lt;/span&gt;
  &lt;span class=&quot;kp&quot;&gt;include&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;Wicked&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;no&quot;&gt;Wizard&lt;/span&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;c1&quot;&gt;# TODO&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;update&lt;/span&gt;
    &lt;span class=&quot;c1&quot;&gt;# TODO&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;Once that’s done, we need to tell Wicked what our form steps are. Now, the README suggests putting this into the controller, but we’ve already gota collection of our form steps that we can access in our model. Let’s just use that!&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;Pet::StepsController&lt;/span&gt;
  &lt;span class=&quot;kp&quot;&gt;include&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;Wicked&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;no&quot;&gt;Wizard&lt;/span&gt;
  &lt;span class=&quot;n&quot;&gt;steps&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;*&lt;/span&gt;&lt;span class=&quot;no&quot;&gt;Pet&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;form_steps&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;p&gt;Now our controller is set up to use Wicked, and knows which form steps to use. There’s just one more thing we need to consider before we can test this out - how do we get into our wizard form? Remember, our routes rely on having a &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Pet&lt;/code&gt; ID already present, so we’re going to need to create our Pet instance before we enter the wizard form.&lt;/p&gt;

&lt;p&gt;The most appropriate place to implement this is back in our top-level controller. Remember, our wizard controller is responsible for showing and updating steps, but our top-level controller is still responsible for managing our &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Pet&lt;/code&gt; models. Semantically, this means that our &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;create&lt;/code&gt; action of our &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;PetsController&lt;/code&gt; should create the new &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Pet&lt;/code&gt; instance. This action will work a little differently from a normal &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;create&lt;/code&gt; action that you might be used to, as it doesn’t strictly need a &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;new&lt;/code&gt; action - we won’t be saving this &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Pet&lt;/code&gt; model with any data - just putting it in the database so that our &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;StepsController&lt;/code&gt; can access that.&lt;/p&gt;

&lt;p&gt;Your create action can be as simple as 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;k&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;PetsController&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&amp;lt;&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;ApplicationController&lt;/span&gt;
  &lt;span class=&quot;c1&quot;&gt;# ...&lt;/span&gt;

  &lt;span class=&quot;k&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;create&lt;/span&gt;
    &lt;span class=&quot;vi&quot;&gt;@pet&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;Pet&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;vi&quot;&gt;@pet&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;save&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;ss&quot;&gt;validate: &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;redirect_to&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;pet_step_path&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;vi&quot;&gt;@pet&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;Pet&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;steps&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;p&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;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;In other words, create a new pet with no data, and redirect to the first step.
If you would like a new action, you can of course add one - this could render a view that explains the form steps, or something similar to that. It’s not required for this controller to work correctly though - you can just point any links to create a new pet straight to the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;create&lt;/code&gt; action like so:&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;  &lt;span class=&quot;cp&quot;&gt;&amp;lt;%=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;link_to&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&apos;Record a Pet&apos;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;pets_path&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;ss&quot;&gt;method: :post&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;So, that will handle creating a new Pet for us, and will redirect to the start of the form. We haven’t implemented our &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;show&lt;/code&gt; action yet though - let’s do that now.&lt;/p&gt;

&lt;p&gt;Our show action needs to find the pet (so that we can use &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;form_for @pet&lt;/code&gt; in our templates), and then call a special Wicked method called &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;render_wizard&lt;/code&gt; - this will render a template with the same name of our step - for example, if we are on the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;identity&lt;/code&gt; step, this will render “app/views/pet/steps/identity.html.erb”. Here’s what the show action looks like:&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;Pet::StepsController&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&amp;lt;&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;ApplicationController&lt;/span&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;@pet&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;Pet&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;find&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;:pet_id&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;])&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;render_wizard&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;update&lt;/span&gt;
    &lt;span class=&quot;c1&quot;&gt;# TODO&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;Before we give this a test, let’s quickly implement the template for the first form step:&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;# app/views/pet/steps/identity.html.erb
&lt;span class=&quot;cp&quot;&gt;&amp;lt;%=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;form_for&lt;/span&gt; &lt;span class=&quot;vi&quot;&gt;@pet&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;ss&quot;&gt;method: :put&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;ss&quot;&gt;url: &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;wizard_path&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;cp&quot;&gt;%&amp;gt;&lt;/span&gt;
  &lt;span class=&quot;cp&quot;&gt;&amp;lt;%&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;if&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;object&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;errors&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;any?&lt;/span&gt; &lt;span class=&quot;cp&quot;&gt;%&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;nt&quot;&gt;&amp;lt;div&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;class=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;error_messages&quot;&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;&amp;gt;&lt;/span&gt;
      &lt;span class=&quot;cp&quot;&gt;&amp;lt;%&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;object&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;errors&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;full_messages&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;each&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;do&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;|&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;error&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;|&lt;/span&gt; &lt;span class=&quot;cp&quot;&gt;%&amp;gt;&lt;/span&gt;
        &lt;span class=&quot;nt&quot;&gt;&amp;lt;p&amp;gt;&lt;/span&gt;&lt;span class=&quot;cp&quot;&gt;&amp;lt;%=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;error&lt;/span&gt; &lt;span class=&quot;cp&quot;&gt;%&amp;gt;&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;&amp;lt;/p&amp;gt;&lt;/span&gt;
      &lt;span class=&quot;cp&quot;&gt;&amp;lt;%&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;end&lt;/span&gt; &lt;span class=&quot;cp&quot;&gt;%&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;nt&quot;&gt;&amp;lt;/div&amp;gt;&lt;/span&gt;
  &lt;span class=&quot;cp&quot;&gt;&amp;lt;%&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;end&lt;/span&gt; &lt;span class=&quot;cp&quot;&gt;%&amp;gt;&lt;/span&gt;

  &lt;span class=&quot;nt&quot;&gt;&amp;lt;fieldset&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;nt&quot;&gt;&amp;lt;legend&amp;gt;&lt;/span&gt;Pet Identity&lt;span class=&quot;nt&quot;&gt;&amp;lt;/legend&amp;gt;&lt;/span&gt;

    &lt;span class=&quot;nt&quot;&gt;&amp;lt;div&amp;gt;&lt;/span&gt;
      &lt;span class=&quot;cp&quot;&gt;&amp;lt;%=&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;label&lt;/span&gt; &lt;span class=&quot;ss&quot;&gt;:name&lt;/span&gt; &lt;span class=&quot;cp&quot;&gt;%&amp;gt;&lt;/span&gt;
      &lt;span class=&quot;cp&quot;&gt;&amp;lt;%=&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;text_field&lt;/span&gt; &lt;span class=&quot;ss&quot;&gt;:name&lt;/span&gt; &lt;span class=&quot;cp&quot;&gt;%&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;nt&quot;&gt;&amp;lt;/div&amp;gt;&lt;/span&gt;

    &lt;span class=&quot;nt&quot;&gt;&amp;lt;div&amp;gt;&lt;/span&gt;
      &lt;span class=&quot;cp&quot;&gt;&amp;lt;%=&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;label&lt;/span&gt; &lt;span class=&quot;ss&quot;&gt;:owner_name&lt;/span&gt; &lt;span class=&quot;cp&quot;&gt;%&amp;gt;&lt;/span&gt;
      &lt;span class=&quot;cp&quot;&gt;&amp;lt;%=&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;text_field&lt;/span&gt; &lt;span class=&quot;ss&quot;&gt;:owner_name&lt;/span&gt; &lt;span class=&quot;cp&quot;&gt;%&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;nt&quot;&gt;&amp;lt;/div&amp;gt;&lt;/span&gt;

    &lt;span class=&quot;nt&quot;&gt;&amp;lt;div&amp;gt;&lt;/span&gt;
      &lt;span class=&quot;cp&quot;&gt;&amp;lt;%=&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;submit&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&apos;Next Step&apos;&lt;/span&gt; &lt;span class=&quot;cp&quot;&gt;%&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;nt&quot;&gt;&amp;lt;/div&amp;gt;&lt;/span&gt;
  &lt;span class=&quot;nt&quot;&gt;&amp;lt;/fieldset&amp;gt;&lt;/span&gt;
&lt;span class=&quot;cp&quot;&gt;&amp;lt;%&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;end&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;If you visit your first form step now, you should see your identity form. If you see this - congratulations! If you don’t, go back and check that your routes are all correct - remember you need to access the wizard form using a Pet ID that exists in the database.&lt;/p&gt;

&lt;p&gt;We can view the form now, but if you try and hit save, you’ll notice you run into an error. This is because we have not yet implemented our &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;update&lt;/code&gt; action. This action is actually going to be very simple, because Wicked handily takes up all the heavy lifting of working out where we need to go next in our form. Let’s implement this, pretty much based off the Wicked README:&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;# app/controllers/pet/steps_controller.rb&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;Pet::StepsController&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&amp;lt;&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;ApplicationController&lt;/span&gt;
  &lt;span class=&quot;c1&quot;&gt;# ...&lt;/span&gt;

  &lt;span class=&quot;k&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;update&lt;/span&gt;
    &lt;span class=&quot;vi&quot;&gt;@pet&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;Pet&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;find&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;:pet_id&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;])&lt;/span&gt;
    &lt;span class=&quot;vi&quot;&gt;@pet&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;update&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;pet_params&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;step&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;))&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;render_wizard&lt;/span&gt; &lt;span class=&quot;vi&quot;&gt;@pet&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;We’re using &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;render_wizard&lt;/code&gt;, just like we did in the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;show&lt;/code&gt; action, but this time we’re passing it an object. In this case, Wicked will check the object - if it is valid, it will continue to the next step, but if it is invalid, it will not move onto the next step, but re-render the template for the current step (where errors will be shown as appropriate if you are rendering the error messages like the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;identity&lt;/code&gt; template above).&lt;/p&gt;

&lt;p&gt;This looks good, but you may have noticed that we’ve introduced another new method - this one’s to do with &lt;a href=&quot;http://edgeguides.rubyonrails.org/action_controller_overview.html#strong-parameters&quot;&gt;strong parameters&lt;/a&gt;. If you cast your mind back to when we were working on our model, you may also remember that our conditional validations rely on having the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;form_step&lt;/code&gt; set in the model to work correctly, and we’re not setting that anywhere here. Luckily, we can fix both of these problems by implementing the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;pet_params&lt;/code&gt; method!&lt;/p&gt;

&lt;p&gt;This method will use a “case/when” statement (I’ve found &lt;a href=&quot;http://www.skorks.com/2009/08/how-a-ruby-case-statement-works-and-what-you-can-do-with-it/&quot;&gt;Alan Skorkin’s blog post to be a good summary of case/when if you need a refresher&lt;/a&gt;) to return the different &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Pet&lt;/code&gt; attributes that can be updated based on the step that is passed in. It will also ‘mix-in’ the current form step to the parameters it returns so that the model knows which step it is on and can run validations accordingly.&lt;/p&gt;

&lt;p&gt;Here’s the implementation:&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;# app/controllers/pet/steps_controller.rb&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;Pet::StepsController&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&amp;lt;&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;ApplicationController&lt;/span&gt;
  &lt;span class=&quot;c1&quot;&gt;# ...&lt;/span&gt;

  &lt;span class=&quot;kp&quot;&gt;private&lt;/span&gt;

  &lt;span class=&quot;k&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;pet_params&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;step&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
  	&lt;span class=&quot;n&quot;&gt;permitted_attributes&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;case&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;step&lt;/span&gt;
  	  &lt;span class=&quot;k&quot;&gt;when&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;identity&quot;&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;:owner_name&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt;
  	  &lt;span class=&quot;k&quot;&gt;when&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;characteristics&quot;&lt;/span&gt;
  	    &lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;ss&quot;&gt;:colour&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;ss&quot;&gt;:identifying_characteristics&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt;
  	  &lt;span class=&quot;k&quot;&gt;when&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;instructions&quot;&lt;/span&gt;
  	    &lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;ss&quot;&gt;:special_instructions&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;params&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;require&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;ss&quot;&gt;:pet&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;).&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;permit&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;permitted_attributes&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;).&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;merge&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;ss&quot;&gt;form_step: &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;step&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;As an example, were this to be called with the first step, it would return: &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;{name: &apos;Tinkerbell&apos;, owner_name: &apos;Bob Jones&apos;, form_step: &apos;identity&apos;}&lt;/code&gt;. It will raise an error and return an appropriate status code if someone attempts to submit an attribute that is not allowed on the current step.&lt;/p&gt;

&lt;p&gt;We now have our model set up to perform validations for whichever step we are up to, and our controller set up with &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Wicked&lt;/code&gt; to manage working through the steps. Our form is not yet complete - we are still missing templates for the “characteristics” and “instructions” steps, but the implementation of those is left as an exercise - they will very much resemble the template for the “identity” step, just with different fields.&lt;/p&gt;

&lt;p&gt;In terms of where you go from here, there’s plenty of scope for improvement! There is some code in our steps controller that can be refactored to not be repeated, and you might want to split some of the common elements in your step templates into partials (for example, the submit button and/or the error rendering). You might also want to tweak when validation errors should/shouldn’t be run (as an example of this, I have implemented a variant of this form where validations are not run until the final step). You may also want to check out the following resources:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;Demo application developed using this blog post: &lt;a href=&quot;http://jm-multistep-blog-demo.herokuapp.com/&quot;&gt;App&lt;/a&gt;, &lt;a href=&quot;https://github.com/joshmcarthur/multistep-blog-demo&quot;&gt;Source&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;http://railscasts.com/episodes/346-wizard-forms-with-wicked&quot;&gt;Ryan Bates’ Railscast on Wicked&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://github.com/schneems/wicked&quot;&gt;Wicked README&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
</description>
        <pubDate>Tue, 23 Dec 2014 02:00:00 +1300</pubDate>
        <link>https://joshmcarthur.com/2014/12/23/rails-multistep-forms.html</link>
        <guid isPermaLink="true">https://joshmcarthur.com/2014/12/23/rails-multistep-forms.html</guid>
        
        
      </item>
    
      <item>
        <title>Coming or Going? The North American P-82</title>
        <description>&lt;p&gt;&lt;cite&gt;By United States Air Force [Public domain], &lt;a href=&quot;https://commons.wikimedia.org/wiki/File%3ANorth_American_XP-82_Twin_Mustang_44-83887.Color.jpg&quot;&gt;via Wikimedia Commons&lt;/a&gt;
&lt;/cite&gt;&lt;/p&gt;

&lt;p&gt;This aircraft is &lt;em&gt;literally&lt;/em&gt; two &lt;a href=&quot;https://en.wikipedia.org/wiki/North_American_P-51_Mustang&quot;&gt;P-51 Mustangs&lt;/a&gt; banged together.&lt;/p&gt;

&lt;p&gt;I entirely appreciate the thinking behind taking a very successful platform together and &lt;strong&gt;doubling the awesome&lt;/strong&gt;, but I can’t help imagining a number of entertaining scenarios in which a pilot in each fuselage attempts to wrest control of the ‘plane off the other.&lt;/p&gt;

&lt;p&gt;I’m also imagining a number of flights in which the pilots spend most of the cruising time in giggling fits as they make faces and thumb their noses at each other along with similar hijinks.&lt;/p&gt;

&lt;p&gt;There’s good reasons why this type of combination of two fuselages would be successful, but at first glance it’s such a seemingly-odd thing to do that I can’t help and be amused.&lt;/p&gt;

&lt;p&gt;For more technical information, see the &lt;a href=&quot;https://en.wikipedia.org/wiki/North_American_F-82_Twin_Mustang&quot;&gt;Wikipedia page&lt;/a&gt;.&lt;/p&gt;

&lt;hr /&gt;

&lt;p&gt;Sidenote: The first versions of these aircraft really &lt;em&gt;did&lt;/em&gt; fly with two pilots - but not at the same time! The pilots would alternate on long journeys to provide relief for the other. Later versions replaced one pilot position with space for a radar operator.&lt;/p&gt;
</description>
        <pubDate>Thu, 23 Oct 2014 00:00:00 +1300</pubDate>
        <link>https://joshmcarthur.com/2014/10/23/coming-or-going-the-north-american-xp-82.html</link>
        <guid isPermaLink="true">https://joshmcarthur.com/2014/10/23/coming-or-going-the-north-american-xp-82.html</guid>
        
        
      </item>
    
      <item>
        <title>IFTTT, Grabaseat, and You: Getting the flights you deserve.</title>
        <description>&lt;blockquote&gt;
  &lt;p&gt;This guide covers setting up &lt;a href=&quot;https://ifttt.com&quot;&gt;If This Then That&lt;/a&gt; to check for flights on &lt;a href=&quot;http://grabaseat.co.nz&quot;&gt;Air New Zealand’s Grab a Seat&lt;/a&gt; service. If you’re not at all interested in cheap NZ air travel, this might not be for you.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Grabaseat, Air NZ’s discount flights service, has got to be one of the hottest battlegrounds in New Zealand’s internet. Popular flights sell out in minutes, and it’s up to you to frantically refresh, hoping and hoping to see that exact flight you want, nay, &lt;em&gt;need&lt;/em&gt;.&lt;/p&gt;

&lt;p&gt;In this post I’m going to explain how to set up &lt;a href=&quot;https://ifttt.com&quot;&gt;If This Then That&lt;/a&gt; to do all this frantic checking for you, 24x7. If This Then That is awesome, and useful for so many things (the essence if these ‘things’ being: ‘when this happens, do that’), so I can’t possibly cover everything - instead, I’m going describe this particular trick.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Note that this seems like a long post, but it’s mostly just a lot of detail that you can skim over - in reality, this takes about 5 minutes to set up, and is totally worth it.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Right, let’s get into it. For the purposes of this exercise, let’s assume you live in Wellington, but are a traffic enthusiast keen to check out this ‘Southern Motorway’ you’ve heard your friends up in Auckland chat about. Traffic enthusiasts don’t make much money, so we’re after the cheapest flight possible, and we &lt;em&gt;know&lt;/em&gt; that traffic isn’t going to go anywhere while we look for a flight, so we’re in no hurry.&lt;/p&gt;

&lt;p&gt;The first thing to do is to &lt;a href=&quot;https://ifttt.com/join&quot;&gt;sign up for an If This Then That (IFTTT) account&lt;/a&gt; - it’s super simple, and will only take a moment - go and do that now. Once you’ve signed in, you’ll see a fun little landing page with pulsing buttons and things that looks a little like this:&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/img/posts/ifttt-grabaseat/landing.jpg&quot; alt=&quot;IFTTT Signed in page&quot; /&gt;&lt;/p&gt;

&lt;p&gt;You can explore all the pre-built recipes that are there - we’re going to jump right in and create our own. Hit the ‘Create’ link in the top bar.&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/img/posts/ifttt-grabaseat/create.jpg&quot; alt=&quot;IFTTT Create Link&quot; /&gt;&lt;/p&gt;

&lt;p&gt;First of all, we need to specify the ‘this’ part of ‘If THIS then THAT’ - click the bit that says ‘THIS’ to do that, which will prompt you to select something that the IFTTT team calls a ‘trigger channel’.&lt;/p&gt;

&lt;p&gt;A trigger channel can be lots of things, but you can think of it as the &lt;em&gt;data source&lt;/em&gt; - where the information we will need will be coming from. To find our flights, we’re going to use a handy little service that Air New Zealand makes available to us called an RSS Feed - you can check out the &lt;a href=&quot;http://en.wikipedia.org/wiki/RSS&quot;&gt;Wikipedia page&lt;/a&gt;, but it’s basically a list of links, usually sorted by date, that is easy for computers to understand.&lt;/p&gt;

&lt;p&gt;If you have a look in the list of trigger channels, you’ll see ‘Feed’ as an option, with a lovely (?) orange logo:&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/img/posts/ifttt-grabaseat/rss.jpg&quot; alt=&quot;IFTTT RSS Trigger Channel&quot; /&gt;&lt;/p&gt;

&lt;p&gt;If you click on that, the page will scroll down in a disorienting way until you reach a selection between two options - ‘New feed item’, which will act every time a new link is added to the RSS Feed, and ‘New feed item matches’, which will only act if that new link matches a search term.&lt;/p&gt;

&lt;p&gt;Hit the ‘New feed item matches’ - we don’t want to be alerted &lt;em&gt;everytime&lt;/em&gt; a new deal is posted on Grabseat, only when the ones we’re interested in become available.&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/img/posts/ifttt-grabaseat/rss-option.jpg&quot; alt=&quot;IFTTT RSS Option&quot; /&gt;&lt;/p&gt;

&lt;p&gt;After clicking on that bit, you’ll be taken to a form where you can specify what IFTTT should look for in new links. What you want to look for is the name of your journey, as Grabaseat shows it on their website - it’s usually &lt;strong&gt;City you live in&lt;/strong&gt; &lt;em&gt;to&lt;/em&gt; &lt;strong&gt;City you want to go to&lt;/strong&gt;. Watch out for special cases though - for example, Grabaseat shows flights to ‘Napier/Hastings’, not to ‘Napier’ and ‘Hastings’ - and the search requires that you be exact!&lt;/p&gt;

&lt;p&gt;With this example, we’re interested in flights from ‘Wellington’ to ‘Auckland’, so in the ‘Keyword or simple phrase’ box, we should type: &lt;strong&gt;Wellington to Auckland&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;The RSS Feed has a special address that shows the feed information, and it’s &lt;em&gt;not&lt;/em&gt; just ‘https://grabaseat.co.nz’ - it’s got an ‘/rss’ stuck on the end of that (so altogether that’s &lt;strong&gt;https://grabaseat.co.nz/rss&lt;/strong&gt;). Feel free to visit that page in a new tab if you’re curious to see what that looks like. If you’re wondering how I found that out, it’s listed down the bottom of the &lt;a href=&quot;http://grabaseat.co.nz&quot;&gt;Grabaseat website&lt;/a&gt; - it’s just not very obvious.&lt;/p&gt;

&lt;p&gt;With both of those things filled in, your form should look like this:&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/img/posts/ifttt-grabaseat/rss-form.jpg&quot; alt=&quot;IFTTT RSS Form&quot; /&gt;&lt;/p&gt;

&lt;p&gt;Now you can hit ‘Create Trigger’, and begin the ‘that’ part of ‘If THIS then THAT’, by hitting the word ‘That’.&lt;/p&gt;

&lt;p&gt;Now, an action channel needs to be selected. If the ‘this’ bit was the data &lt;em&gt;source&lt;/em&gt;, you can think of this as the data &lt;em&gt;destination&lt;/em&gt;. Similarly to the trigger channels, there’s a whole bunch of them, but in this case we just want to be emailed when there are matching results, so let’s select the ‘Email’ option (&lt;strong&gt;Note&lt;/strong&gt;: Gmail is also in that list - &lt;em&gt;don’t&lt;/em&gt; select that option, even if you have a gmail account - it’s something else, and not what we want in this case).&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/img/posts/ifttt-grabaseat/action-channel.jpg&quot; alt=&quot;IFTTT Action Channel&quot; /&gt;&lt;/p&gt;

&lt;p&gt;Once you’ve hit that, you get one option - to ‘Send an email’ - click on that to proceed.&lt;/p&gt;

&lt;p&gt;Now you get a form that lets you specify what text should be used when flights match your criteria and an email is sent to you. The defaults are fine, but if you’d like to add more text, that’s fine too - just type it into the fields. If you’d like to use fields from the RSS Feed, hit the ‘+’ icon next to each field to see what the options are.&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/img/posts/ifttt-grabaseat/email-form.jpg&quot; alt=&quot;IFTTT Email Form&quot; /&gt;&lt;/p&gt;

&lt;p&gt;Once you’re happy, hit the ‘Create Action’ button to proceed. You’ll be taken back to the ‘If This Then That’ screen, with the trigger and action you have set up shown. The description is just so you can find the ‘recipe’ again in IFTTT, so you can leave it as is, or change it if you wish.&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/img/posts/ifttt-grabaseat/confirm.jpg&quot; alt=&quot;IFTTT Confirm&quot; /&gt;&lt;/p&gt;

&lt;p&gt;If you’re happy, hit the ‘Create Recipe’ button to lock that in, and then that’s created! The RSS Feed will be checked every 15 minutes, and if any flights match, then an email will be sent to you right away with a link directly to Grabaseat. Easy as!&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/img/posts/ifttt-grabaseat/success.jpg&quot; alt=&quot;IFTTT Email&quot; /&gt;&lt;/p&gt;

&lt;p&gt;If you’d like to set up multiple alerts, that’s fine - just hit the ‘Create’ link again to go through the process - the only thing you need to change is your search term when you’re setting up the RSS Feed. As an example, I usually set up another alert for the return flight, so that I can match up both legs of the trip that I’d like to do.&lt;/p&gt;

&lt;p&gt;Happy flighting!&lt;/p&gt;

&lt;p&gt;— Josh&lt;/p&gt;
</description>
        <pubDate>Tue, 27 May 2014 00:00:00 +1200</pubDate>
        <link>https://joshmcarthur.com/2014/05/27/ifttt-grabaseat-and-you-getting-the-flights-you-deserve.html</link>
        <guid isPermaLink="true">https://joshmcarthur.com/2014/05/27/ifttt-grabaseat-and-you-getting-the-flights-you-deserve.html</guid>
        
        <category>guides</category>
        
        <category>ifttt</category>
        
        
      </item>
    
      <item>
        <title>Sshhh...Secrets in Rails 4.1</title>
        <description>&lt;p&gt;With the &lt;a href=&quot;https://rubygems.org/gems/rails&quot;&gt;recent release of Rails 4.1&lt;/a&gt;, has come a feature that I’ve really been looking forward to. That feature is automatic support for storing your application’s secrets in a YAML file.&lt;/p&gt;

&lt;p&gt;Whenever you have a repository of code, it’s a great idea to always work under the assumption that the code you write could be open-sourced at any time. Keeping this in mind helps to uphold coding standards, comment quality, and generally what goes into your codebase.&lt;/p&gt;

&lt;p&gt;Something I see a lot is files that have been committed with data in them that is specific to the project. Sometimes this data is an API key, or a password (this is &lt;em&gt;really&lt;/em&gt; bad), and sometime’s it’s just something that is configuration specific to a particular user that will make the project hard for others to use.&lt;/p&gt;

&lt;p&gt;The &lt;a href=&quot;http://12factor.net/config&quot;&gt;Twelve-Factor App’s rules on config&lt;/a&gt;, state that:&lt;/p&gt;

&lt;blockquote&gt;
  &lt;p&gt;Apps sometimes store config as constants in the code. This is a violation of twelve-factor, which requires strict separation of config from code. Config varies substantially across deploys, code does not.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;and:&lt;/p&gt;

&lt;blockquote&gt;
  &lt;p&gt;A litmus test for whether an app has all config correctly factored out of the code is whether the codebase could be made open source at any moment, without compromising any credentials.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Obviously then, just putting keys, passwords, or environment-specific information into a repository is undesirable.&lt;/p&gt;

&lt;p&gt;Previous to Rails 4.1, keeping this sort of information out of the codebase required undertaking some special steps. The most common option, popularized by Heroku’s implementation of config, is to store configuration in environment variables. To this end, the &lt;a href=&quot;https://github.com/bkeepers/dotenv&quot;&gt;dotenv&lt;/a&gt; gem was created, to centralize where these variables were stored (you could also &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;export&lt;/code&gt; them in your shell, but this is a little more difficult to maintain). Another option was to implement your own YAML file that was read in to your application using an initializer. This option is also good for more specific settings that aren’t secrets, but are configuration - for example, &lt;a href=&quot;https://gist.github.com/joshmcarthur/9884826&quot;&gt;my pattern for loading ActionMailer settings from a YAML file&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;The key factor here is, however secrets &lt;em&gt;used&lt;/em&gt; to be stored, almost all of the solutions suggested around the internet required using something more than just Rails. What the core team have done is to build in support for just one of these solutions, that is going to work for 90% of developers - reading a file named &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;config/secrets.yml&lt;/code&gt;, and automatically parsing, and namespacing to your Rails environment, the data in the file.&lt;/p&gt;

&lt;p&gt;Here’s an example:&lt;/p&gt;

&lt;p&gt;Let’s say I’ve got a recipe-book application named ‘Chef’, that has the ability to log in using Facebook using Oauth via the &lt;a href=&quot;http://rubygems.org/gems/omniauth&quot;&gt;Omniauth gem&lt;/a&gt;. In order to peform the authentication process, I need to provide Omniauth with my ‘app_id’, and ‘app secret’. Clearly, these two values are configuration, so we want to de-couple them from the application. They’re also pretty confidential, as anyone could use these two values to connect to Facebook as our application.&lt;/p&gt;

&lt;p&gt;With support for &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;secrets.yml&lt;/code&gt; in Rails 4.1, we can simply put these values straight into our configuration file:&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-yaml&quot; data-lang=&quot;yaml&quot;&gt;&lt;span class=&quot;c1&quot;&gt;# config/secrets.yml&lt;/span&gt;
&lt;span class=&quot;na&quot;&gt;development&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;
  &lt;span class=&quot;na&quot;&gt;facebook_app_id&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;123456&lt;/span&gt;
  &lt;span class=&quot;na&quot;&gt;facebook_app_secret&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;s3cret!!!&lt;/span&gt;
&lt;span class=&quot;na&quot;&gt;test&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;
  &lt;span class=&quot;na&quot;&gt;facebook_app_id&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;1234567&lt;/span&gt;
  &lt;span class=&quot;na&quot;&gt;facebook_app_secret&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;s3cret!!!&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;p&gt;and then access the values in our Omniauth setup:&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-ruby&quot; data-lang=&quot;ruby&quot;&gt;&lt;span class=&quot;c1&quot;&gt;# config/initializers/omniauth.rb&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;application&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;config&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;middleware&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;use&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;OmniAuth&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;no&quot;&gt;Builder&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;do&lt;/span&gt;
  &lt;span class=&quot;n&quot;&gt;provider&lt;/span&gt; &lt;span class=&quot;ss&quot;&gt;:facebook&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;application&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;secrets&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;facebook_app_id&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;application&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;secrets&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;facebook_app_secret&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;p&gt;By default, git will ignore and never commit the secrets file, so the file remains as a local copy only - it’s considered helpful when you have a file like this to copy it, and remove all sensitive data - naming convention is to suffix the file name with ‘.example’ - e.g. &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;config/secrets.yml.example&lt;/code&gt; - and commit this file. This gives other developers a template to work with - they can copy the file back to &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;config/secrets.yml&lt;/code&gt;, and fill in their own configuration.&lt;/p&gt;

&lt;p&gt;The only thing that I’ve spotted missing from the implementation of the parsing of the secrets file is that while the top-level keys of the YAML file can be accessed using dot-notation (as above, where I’ve gone &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;secrets.facebook_app_id&lt;/code&gt;), the file does not appear to be parsed in such a way that this dot-notation works for nested keys. I think this would be a great addition as a way to keep this file nice and tidy, if I could for example, have a YAML file that looks like this:&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-yaml&quot; data-lang=&quot;yaml&quot;&gt;&lt;span class=&quot;na&quot;&gt;development&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;
  &lt;span class=&quot;na&quot;&gt;facebook&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;
    &lt;span class=&quot;na&quot;&gt;app_id&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;12345&lt;/span&gt;
    &lt;span class=&quot;na&quot;&gt;app_secret&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;s3cret!!&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;p&gt;…and be able to access a secret using the code &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Rails.application.secrets.facebook.app_id&lt;/code&gt;. Hopefully support for nested keys will be coming in a later release!&lt;/p&gt;

&lt;p&gt;Rails’ support for secrets is a long time coming, and I’m really glad to see that they’re making things even easier for us developers. There’s a whole lot of other neat changes in 4.1, so to find our more about the new features (and some more information about the secrets support), check out the &lt;a href=&quot;http://edgeguides.rubyonrails.org/4_1_release_notes.html&quot;&gt;Rails 4.1 Release Notes&lt;/a&gt;!&lt;/p&gt;
</description>
        <pubDate>Thu, 10 Apr 2014 00:00:00 +1200</pubDate>
        <link>https://joshmcarthur.com/2014/04/10/sshhhsecrets-in-rails-41.html</link>
        <guid isPermaLink="true">https://joshmcarthur.com/2014/04/10/sshhhsecrets-in-rails-41.html</guid>
        
        
      </item>
    
      <item>
        <title>Keeping secrets in an Android Application</title>
        <description>&lt;p&gt;As a (mainly) Rails developer, I’m pretty accustomed to the need for secret keeping within a server-based application, and how it gets done (typically, &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;config.yml&lt;/code&gt;’s and/or &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;ENV&lt;/code&gt; variables). Getting into Android though, I’ve had the need to integrate with &lt;a href=&quot;https://parse.com&quot;&gt;Parse&lt;/a&gt; to retrieve some simple data that I don’t want to manage a data service for. Since I want to open source this application, it’s not really enough to follow Parse’s guides and just paste in my application IDs and keys - I need something more adaptable than that.&lt;/p&gt;

&lt;p&gt;After doing a bit of reading, I’ve had an epiphany about Android’s &lt;a href=&quot;http://developer.android.com/guide/topics/resources/string-resource.html&quot;&gt;neat support for Strings&lt;/a&gt; - that they’re not just for internationalization. My conclusion is that a separate Strings XML file, called something like &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;configuration.xml&lt;/code&gt;, and git-ignored, is perfect for storing secrets.&lt;/p&gt;

&lt;p&gt;The process for doing this is very simple. In any Android application, there will be a folder somewhere called ‘res’ (I say &lt;em&gt;somewhere&lt;/em&gt;, because Android Studio and Eclipse generate this folder in two separate places). Within the ‘res’ there is a ‘values’ folder, which normally contains a few XML files, typically containing dimensions, translations, and other generated files.&lt;/p&gt;

&lt;p&gt;To store secrets, simply create another XML file in ‘res/values’, called ‘configuration.xml’. The content of this file is pretty much based on what your existing ‘values.xml’ file will contain - except, instead of containing internationalization strings such as button and text field labels and content, it will contain whatever secret configuration you need. An example of this file is below:&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-xml&quot; data-lang=&quot;xml&quot;&gt;&lt;span class=&quot;cp&quot;&gt;&amp;lt;?xml version=&quot;1.0&quot; encoding=&quot;utf-8&quot;?&amp;gt;&lt;/span&gt;
&lt;span class=&quot;nt&quot;&gt;&amp;lt;resources&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;nt&quot;&gt;&amp;lt;string&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;name=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;parse_application_id&quot;&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;&amp;gt;&lt;/span&gt;xxxxxx&lt;span class=&quot;nt&quot;&gt;&amp;lt;/string&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;nt&quot;&gt;&amp;lt;string&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;name=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;parse_client_secret&quot;&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;&amp;gt;&lt;/span&gt;yyyyyy&lt;span class=&quot;nt&quot;&gt;&amp;lt;/string&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;nt&quot;&gt;&amp;lt;string&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;name=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;google_maps_api_key&quot;&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;&amp;gt;&lt;/span&gt;zzzzzz&lt;span class=&quot;nt&quot;&gt;&amp;lt;/string&amp;gt;&lt;/span&gt;
&lt;span class=&quot;nt&quot;&gt;&amp;lt;/resources&amp;gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;p&gt;Once these keys are in the file, Android will automatically merge it into your resources, where you can access them exactly as you would your normal strings. The only adaption you might need is when you literally need a string, rather than a string resource (which is an &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;int&lt;/code&gt; pointing to the resource) - for example, the initialization of the Parse library - you can simply use &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;getString()&lt;/code&gt; to get a string of your resource, like so:&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-java&quot; data-lang=&quot;java&quot;&gt;&lt;span class=&quot;nc&quot;&gt;Parse&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;initialize&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;,&lt;/span&gt; 
    &lt;span class=&quot;n&quot;&gt;getString&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;no&quot;&gt;R&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;string&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;parse_application_id&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;),&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;getString&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;no&quot;&gt;R&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;string&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;parse_client_secret&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;o&quot;&gt;);&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;p&gt;If you need your keys in another XML file (for example, your ‘AndroidManifest.xml’), you don’t need to worry about using &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;getString()&lt;/code&gt; - you can just use the XML notation for accessing project resources - for example:&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-xml&quot; data-lang=&quot;xml&quot;&gt;&lt;span class=&quot;nt&quot;&gt;&amp;lt;meta-data&lt;/span&gt;
    &lt;span class=&quot;na&quot;&gt;android:name=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;com.google.android.maps.v2.API_KEY&quot;&lt;/span&gt;
    &lt;span class=&quot;na&quot;&gt;android:value=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;@string/google_maps_api_key&quot;&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;/&amp;gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;p&gt;Since your secrets are now in an individual file, they’re simple to ignore in your source control system (for example, in Git, you would add this to the ‘.gitignore’ file in your repository):&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-sh&quot; data-lang=&quot;sh&quot;&gt;&lt;span class=&quot;nb&quot;&gt;echo&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;**/*/res/values/configuration.xml&quot;&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&amp;gt;&lt;/span&gt; .gitignore&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;p&gt;This process is not bulletproof. As resources, they are somewhat more vulnerable to decompilation of your application package, and so they are discoverable if somebody really wants to know them. This solution does, however, prevent your secrets just sitting in plaintext in source control waiting for someone to use, and also has the advantage of being simple to use, leveraging Android’s resource management system, and requiring no extra libraries.&lt;/p&gt;

&lt;h4 id=&quot;note&quot;&gt;Note:&lt;/h4&gt;

&lt;p&gt;I’ve seen encryption suggested around the place as an additional level of security combined with storing secrets in a strings file. The advice on that seems to be that it’s pointless, unless you can find a way to not have your encryption and decryption routine within your code, since the knowledge of &lt;em&gt;how&lt;/em&gt; your secrets were encrypted is generally enough to decrypt the secret.&lt;/p&gt;
</description>
        <pubDate>Sun, 16 Feb 2014 00:00:00 +1300</pubDate>
        <link>https://joshmcarthur.com/2014/02/16/keeping-secrets-in-an-android-application.html</link>
        <guid isPermaLink="true">https://joshmcarthur.com/2014/02/16/keeping-secrets-in-an-android-application.html</guid>
        
        
      </item>
    
      <item>
        <title>sam solomon social media buttons</title>
        <description>&lt;blockquote&gt;
  &lt;p&gt;I’m a minimalist. When it comes to web design, I value white space, typography and function. People come to your site for the writing, which is why a focus on these elements will almost always result in a good user experience. Adding non-essential elements to a page reduces signal and creates noise.
— &lt;cite&gt;&lt;a href=&quot;http://solomon.io/why-im-done-with-social-media-buttons/&quot;&gt;Sam Solomon&lt;/a&gt;&lt;/cite&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Very much inline with how I feel about design and UX in general.&lt;/p&gt;
</description>
        <pubDate>Fri, 14 Feb 2014 00:00:00 +1300</pubDate>
        <link>https://joshmcarthur.com/2014/02/14/sam-solomon-social-media-buttons.html</link>
        <guid isPermaLink="true">https://joshmcarthur.com/2014/02/14/sam-solomon-social-media-buttons.html</guid>
        
        
      </item>
    
      <item>
        <title>Source controlling my toolkit</title>
        <description>&lt;p&gt;This morning, I created a new repository on Github called &lt;a href=&quot;https://github.com/joshmcarthur/toolkit&quot;&gt;toolkit&lt;/a&gt;. I’m using it to keep track of the tools I use every day, in order to benefit others who are interested in how I work, and for my own benefit, to be able to easily refer to documentation, source code and links for tools that may be useful for a particular situation.&lt;/p&gt;

&lt;p&gt;I’ve tried to do this type of thing many times in the past - sometimes by writing a blog post, or forking or ‘starring’ a repository on Github. There are a few reason’s why I’ve started off with Git repository this time though:&lt;/p&gt;

&lt;ol&gt;
  &lt;li&gt;There is a history of the additions and changes that have been made to my toolkit over time.&lt;/li&gt;
  &lt;li&gt;Github automatically provides a nice, friendly layout to view items in my toolkit.&lt;/li&gt;
  &lt;li&gt;I can use Git submodules to track open source projects without needing to keep a full (and quickly outdated) copy of these projects in my toolkit.&lt;/li&gt;
  &lt;li&gt;It’s easy for others to view my toolkit, and even contribute to it (for example, by sending a pull request or lodging an issue), or even to fork it and adapt it to their own needs.&lt;/li&gt;
  &lt;li&gt;I can put absolutely any content in this repository - whatever works best for me - it could be an image, some markdown, a whole repository (as a submodule), or just a link - in all cases, Github tends to provide a nice friendly display for this content.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;My toolkit is, in many ways, performing a similar function to blog post or ‘liking’ or ‘starring’ something, but I think that the semantics are important. Writing a post about something is all well and good, but it tends to get outdated, and buried amongst all the other things that I might blog about - it’s too much work to trawl through a bunch of posts looking for one particular link. Similarly with starring something - just because I star it, it doesn’t mean I use it - it just indicates that I find the project interesting and might follow up on it. My toolkit ONLY contains things that I have already found useful, or know that I’ll need, and doesn’t include any other extraneous material to get in the way when I need to find something.&lt;/p&gt;

&lt;p&gt;I’ve made a start by adding a bunch of submodules to projects that I could think of off the top of my head, but I’m already coming up with ideas for more. If you like the concept, head over to Github and &lt;a href=&quot;https://github.com/joshmcarthur/toolkit.git&quot;&gt;check it out&lt;/a&gt; - and feel free to contribute your favourite tools for me to try out!&lt;/p&gt;

</description>
        <pubDate>Thu, 21 Nov 2013 00:00:00 +1300</pubDate>
        <link>https://joshmcarthur.com/2013/11/21/source-controlling-my-toolkit.html</link>
        <guid isPermaLink="true">https://joshmcarthur.com/2013/11/21/source-controlling-my-toolkit.html</guid>
        
        
      </item>
    
      <item>
        <title>Take advantage of Rails data attribute transformation to clean up your UJS</title>
        <description>&lt;p&gt;As a Rails developer, you’ll almost certainly need to do some unobtrusive Javascript in your applications - in fact, if you use Rails’ &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;jquery-ujs&lt;/code&gt; gem, it’s almost encouraged!&lt;/p&gt;

&lt;p&gt;Without writing any script yourself at all, you can have a form button that disables itself with a message while the form is submitted by simply constructing your button like so:&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-erb&quot; data-lang=&quot;erb&quot;&gt;&lt;span class=&quot;cp&quot;&gt;&amp;lt;%=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;submit_tag&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&apos;Submit&apos;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;ss&quot;&gt;disable_with: &lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&apos;Submitting...&apos;&lt;/span&gt; &lt;span class=&quot;cp&quot;&gt;%&amp;gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;p&gt;Which becomes:&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-html&quot; data-lang=&quot;html&quot;&gt;&lt;span class=&quot;nt&quot;&gt;&amp;lt;input&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;type=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;submit&quot;&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;value=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;Submit&quot;&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;data-disable-with=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;Submitting...&quot;&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;/&amp;gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;p&gt;With Rails 4, however, using the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;disable_with&lt;/code&gt; key directly has been deprecated - instead, Rails prefers that you place this attribute within a hash of &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;data&lt;/code&gt; - presumably to more closely resemble the HTML attributes - like so:&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-erb&quot; data-lang=&quot;erb&quot;&gt;&lt;span class=&quot;cp&quot;&gt;&amp;lt;%=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;submit_tag&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&apos;Submit&apos;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;ss&quot;&gt;data: &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;ss&quot;&gt;disable_with: &lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&apos;Submitting...&apos;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;cp&quot;&gt;%&amp;gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;p&gt;Interestingly though, I have noticed that &lt;em&gt;any&lt;/em&gt; hash keys placed within the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;data&lt;/code&gt; hash will be transformed into &lt;a href=&quot;http://html5doctor.com/html5-custom-data-attributes/&quot;&gt;HTML data attributes&lt;/a&gt; - and most importantly, you can continue to use symbols, not strings, as the hash keys will automatically have underscores replaced with dashes, so:&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-erb&quot; data-lang=&quot;erb&quot;&gt;&lt;span class=&quot;cp&quot;&gt;&amp;lt;%=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;submit_tag&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&apos;Submit&apos;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;ss&quot;&gt;data: &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;ss&quot;&gt;disable_with: &lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&apos;Submitting...&apos;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;ss&quot;&gt;collapse_element: &lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&apos;#share-modal&apos;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;cp&quot;&gt;%&amp;gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;p&gt;Becomes:&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-html&quot; data-lang=&quot;html&quot;&gt;&lt;span class=&quot;nt&quot;&gt;&amp;lt;input&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;type=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;submit&quot;&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;value=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;Submit&quot;&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;data-disable-with=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;Submitting...&quot;&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;data-collapse-element=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;#share-modal&quot;&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;/&amp;gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;p&gt;This is a really neat enhancement, and I’ve already found myself using it regularly. It makes it easier to stick with Rails helpers, even when binding to complex Javascript behaviours, and produces much neater template code.&lt;/p&gt;

</description>
        <pubDate>Wed, 30 Oct 2013 00:00:00 +1300</pubDate>
        <link>https://joshmcarthur.com/2013/10/30/take-advantage-of-rails-data-attribute-transformation-to-clean-up-your-ujs.html</link>
        <guid isPermaLink="true">https://joshmcarthur.com/2013/10/30/take-advantage-of-rails-data-attribute-transformation-to-clean-up-your-ujs.html</guid>
        
        
      </item>
    
      <item>
        <title>Finding records by ID with Ransack</title>
        <description>&lt;p&gt;&lt;a href=&quot;https://github.com/ernie/ransack&quot;&gt;Ransack&lt;/a&gt; is one of my go-to Rubygems, especially for admin applications. It provides a really simple interface to building up complex filtering and ‘searching’ of many records. Check out the README for more information about how it all works - this post covers a specific problem case of Ransack.&lt;/p&gt;

&lt;p&gt;In Rails, we are often subtely educated that the ID of the record doesn’t really matter to anyone - obviously it’s necessary, but for the most part, it just sits there, auto-incrementing. Every so often though, a use case comes along where the ID &lt;em&gt;does&lt;/em&gt; matter. When this happens, there’s a bit of a gotcha with Ransack, where searching will just not work.&lt;/p&gt;

&lt;p&gt;The reason it doesn’t work, is because the ID column is (usually) an &lt;strong&gt;integer&lt;/strong&gt; column, not a &lt;strong&gt;varchar&lt;/strong&gt; (string) - so many of the search predicates that Ransack applies to the column break (since you can’t do things like &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;ILIKE&lt;/code&gt; on an integer). When I ran into this problem, I thought I’d have to delve deep into how Ransack works to figure out a solution - but as it turns out, support for &lt;em&gt;manipulating&lt;/em&gt; column data is already built into Ransack, and there was a perfect solution described in &lt;a href=&quot;https://github.com/ernie/ransack/issues/224&quot;&gt;this Github issue&lt;/a&gt;:&lt;/p&gt;

&lt;script src=&quot;https://gist.github.com/7034614.js&quot;&gt; &lt;/script&gt;

&lt;p&gt;Essentially, Ransack, provides a &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;ransacker&lt;/code&gt; method in our ActiveRecord models which can be used for any transformations that are necessary for a particular column. In this case, we’re using some PostgreSQL functions - the inner function to convert the integer ID column to a string, and the outer function to remove whitespace from the string produced by the inner function (since that function pads the converted number with whitespace). This means that each of our ID column values will be automatically cast to a searchable string by PostgreSQL, via Ransack, allowing us to use the following predicates without any further work:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;id_cont&lt;/code&gt;&lt;/li&gt;
  &lt;li&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;id_eq&lt;/code&gt;&lt;/li&gt;
  &lt;li&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;id_start&lt;/code&gt;&lt;/li&gt;
  &lt;li&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;id_end&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h3 id=&quot;a-note-on-peformance&quot;&gt;A note on peformance&lt;/h3&gt;

&lt;p&gt;I should note that this solution may not be the best &lt;strong&gt;for your situation&lt;/strong&gt; - obviously, casting each column value every time a search is done takes more work than just querying the column’s value directly, and so you should consider alternatives. If a fast search is integral to your application, perhaps it would be a good idea to add an &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;id_string&lt;/code&gt; column to your database table that mirrors the ID column, but is a natively-searchable string that will not require casting. If you are after a quick and easy solution though, and search is more of a secondary function, then what I’ve laid out above should work perfectly for you!&lt;/p&gt;
</description>
        <pubDate>Tue, 29 Oct 2013 00:00:00 +1300</pubDate>
        <link>https://joshmcarthur.com/2013/10/29/code-snippet-18-oct-2013.html</link>
        <guid isPermaLink="true">https://joshmcarthur.com/2013/10/29/code-snippet-18-oct-2013.html</guid>
        
        
      </item>
    
      <item>
        <title>Parse Date/DateTime from date_select parameters</title>
        <description>&lt;p&gt;This Rails helper allows simple parsing of params that have originated from ActionView’s &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;date_select&lt;/code&gt; helper method into a &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Date&lt;/code&gt; or &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;DateTime&lt;/code&gt; object in a consistent and clean way. 
&lt;script src=&quot;https://gist.github.com/7146179.js&quot;&gt; &lt;/script&gt;&lt;/p&gt;
</description>
        <pubDate>Fri, 25 Oct 2013 00:00:00 +1300</pubDate>
        <link>https://joshmcarthur.com/2013/10/25/parse-datedatetime-from-date_select-parameters.html</link>
        <guid isPermaLink="true">https://joshmcarthur.com/2013/10/25/parse-datedatetime-from-date_select-parameters.html</guid>
        
        
      </item>
    
      <item>
        <title>How I built an open source product and SASS collaboratively in less than 48 hours</title>
        <description>&lt;p&gt;Last Friday, I headed off to &lt;a href=&quot;https://twitter.com/Railscamp_NZ&quot;&gt;Railscamp NZ&lt;/a&gt; with one guy from work, 8 cans of V, and a bag of carrots (my snack food of choice). Today it’s Wednesday, I’ve still got 8 cans of V, a few less carrots, one open-source application that I’m actually happy with (and &lt;a href=&quot;https://travis-ci.org/joshmcarthur/inquest&quot;&gt;Travis CI&lt;/a&gt; and &lt;a href=&quot;https://codeclimate.com/github/joshmcarthur/inquest&quot;&gt;CodeClimate&lt;/a&gt; say is pretty well put together), and one deployed-to-production, used-and-valued-by-a-company SASS based on said application.&lt;/p&gt;

&lt;h2 id=&quot;about-inquest&quot;&gt;About Inquest&lt;/h2&gt;

&lt;p&gt;Inquest is a bit like &lt;a href=&quot;http://stackoverflow.com&quot;&gt;StackOverflow&lt;/a&gt; - but it’s designed for those questions that aren’t for the public. Just observe in your job how often questions like these get asked:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;“How do I do x?”&lt;/li&gt;
  &lt;li&gt;“Why won’t x work?”&lt;/li&gt;
  &lt;li&gt;“Is this the best way to do x?”&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The idea behind Inquest is to move these questions into a area that focusses and preserves valuable and insightful questions and answers, reducing the need to constantly re-ask the same questions.&lt;/p&gt;

&lt;blockquote&gt;
  &lt;p&gt;If you think Inquest might be interesting to try out, you can either download the source code of the &lt;a href=&quot;https://github.com/joshmcarthur/inquest&quot;&gt;open source version&lt;/a&gt; on Github, or if you’re not too technical or want it to “just work”, &lt;a href=&quot;https://twitter.com/sudojosh&quot;&gt;Send me a Tweet or PM me on Twitter&lt;/a&gt; to find out more about an organization account.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2 id=&quot;about-railscamps&quot;&gt;About Railscamps&lt;/h2&gt;

&lt;p&gt;For those who don’t know about it, the general idea of a Railscamp is to disappear off somewhere into the wilderness with a bunch of other developers, and do whatever you feel like - whether that be socializing, running or attending presentations, or playing what seem to be constant and/or long-running games of [werewolf](http://en.wikipedia.org/wiki/Mafia_(party_game). It’s great fun, and a fantastic way to learn new things and expand your technical (or social) horizons.&lt;/p&gt;

&lt;h2 id=&quot;timing&quot;&gt;Timing&lt;/h2&gt;

&lt;p&gt;When I headed there on Friday, I already had the expectation that I was basically going to be heads-down coding an app all weekend. As it turns out, I more or less spent the first night and all of the next day just socializing and going to talks - I just couldn’t get into coding. I’d already had an idea for a private, host-your-own version of StackOverflow (based on many senior developers I’ve seen who are constantly barraged by the same questions about the same projects), so I had a bit of a plan in my head. Here was my timeline for the weekend:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;Friday - Sunday morning: initial build&lt;/li&gt;
  &lt;li&gt;Sunday morning - Sunday evening: finishing tests, and adding more features.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;In reality, by Saturday afternoon, I had serious doubts that I would even bother continuing on the application, let along finishing the initial build before the end of the weekend. I remember talking to my partner on the phone, when asked how it was all going: “Meh - the socializing is good, but I just can’t get into my project”. In the end, I would actually say that &lt;strong&gt;starting&lt;/strong&gt; on Sunday was the best possible motivation for me - it turned from building into a product into &lt;em&gt;what the hell, let’s just see how much we can get through in a day&lt;/em&gt;.&lt;/p&gt;

&lt;h2 id=&quot;collaboration&quot;&gt;Collaboration&lt;/h2&gt;

&lt;p&gt;One of the real benefits of a Railscamp is it tends to attract pretty damn competent developers, who have basically set aside the weekend to work on stuff. Having said that, this is a area where I really could have done better - after all, “&lt;em&gt;I’m working on an open-source host-your-own question and answer site, but it’s not going that well&lt;/em&gt;” isn’t the best pitch - to anyone. Despite that, I was lucky enough to already have one guy on board from work, and had various people helping out throughout Sunday, mostly on questions along the lines of “Am I doing this right?”. In the end, what became obvious to me was &lt;strong&gt;the value of everyone involved in building a product being in the same place - not just in the same room, but at the same table, in the same mindset, with the same vision&lt;/strong&gt;. In the case of Railscamp, this involved 3 or 4 of us sitting at a table, constantly communicating what we were working on, all with the same (albeit not very businessy) vision of &lt;em&gt;let’s just do what we can in a day&lt;/em&gt;.&lt;/p&gt;

&lt;h2 id=&quot;management&quot;&gt;Management&lt;/h2&gt;

&lt;p&gt;By Suday morning, it was pretty obvious that we were going to have to do some pretty intense work to get to the point we’re I’d be happy with what we’d done - it was going to need more than one person working constantly throughout the day to get everything done that I wanted to.&lt;/p&gt;

&lt;p&gt;To help keep track of everything, I borrowed a technique from &lt;a href=&quot;http://3months.com&quot;&gt;my employer&lt;/a&gt;, and set up a &lt;a href=&quot;http://en.wikipedia.org/wiki/Scrum_(development)&quot;&gt;scrum&lt;/a&gt; board, based on a tool that I use every day called &lt;a href=&quot;https://trello.com&quot;&gt;Trello&lt;/a&gt; (I suggest that you check this out if you don’t already use it!) here’s our board by the end of Sunday:&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/img/posts/inquest/board.jpg&quot; alt=&quot;Inquest &apos;Trello board&apos;&quot; /&gt;&lt;/p&gt;

&lt;p&gt;We stuck this up on the wall, and simply got everyone to update it as they went. No daily standup (obviously this wouldn’t have been much use to us!), just a real-time board that was a glance up away when anyone needed to see where a feature was at, or needed something new to work on. Because this board was “public”, we also got a few people wandering past and offering help or suggestions for features, which was a great way of passively brainstorming for ideas beyond those we had already identified as parts we wanted to build it.&lt;/p&gt;

&lt;p&gt;Another part of the management of the build was just the collaboaration. Because the board was right there, and so were us developers, we mostly relied on verbal announcements to know where things were at - and this worked really well. Just little things like being able to find out exactly where &lt;em&gt;feature x&lt;/em&gt; was at, what technnique was being used for &lt;em&gt;feature y&lt;/em&gt;, or whether &lt;em&gt;feature z&lt;/em&gt; had been merged fully into master yet, made the whole thing really easy to manage, and I’m really happy with how this part of it went. We also made extensive use of a &lt;a href=&quot;https://enterprise.github.com/&quot;&gt;Github Enterprise&lt;/a&gt; instance to manage the code quality and merging with pull requests, which meant that throughout the day, our master branch was (almost!) always working - even if we were building 4 new features at different points of completion in at the time.&lt;/p&gt;

&lt;h2 id=&quot;open-source-vs-product&quot;&gt;Open-Source vs Product&lt;/h2&gt;

&lt;p&gt;Something that I don’t get to do very much in my day-to-day job is develop SaaS products - so this seemed like a great opportunity. Despite that though, I’m very much in agreeement with Github’s ethos of &lt;a href=&quot;http://tom.preston-werner.com/2011/11/22/open-source-everything.html&quot;&gt;open-sourcing everything&lt;/a&gt;, and so the last thing I wanted to do was get all these people working on a cool little project that was just going to become another product.&lt;/p&gt;

&lt;p&gt;What I did instead was arrive at a middle ground - I would develop the core of the application fully-transparently, alongside all the other developers I could find who wanted to work on Inquest, and push this to Github as a ready-to-roll Rails project. Anyone who wanted to use it for anything can download it, install the dependencies, set up the database and use it. To satisfy my desire to work with a ‘product’ though, I have also made some further developments to make a hosted version of Inquest available to anyone who is either not comfortable setting up an open-source version of the application, or does not want to go to the bother of hosting and maintaining their own version (or both!).&lt;/p&gt;

&lt;p&gt;The ‘product’ version is exactly the same as the open-source version, but with support for multi-tenancy, and with all data secured and scoped to a company-specific subdomain (for example, http://3months.inquestapp.com). Right now, there’s no company sign-up available, while I get it to a point where I’d be comfortable charging for access - when I do get to that point though, I plan on charging just enough to cover my hosting costs, and hopefully my time to continue adding features to the open-source version of Inquest. I’ve seen this done before, and its worked quite well - helped by the fact that I have a full-time job that can quite easily cover my rent if people just end up preferring the open-source version.&lt;/p&gt;

&lt;p&gt;I’m really happy with how I’ve got these two versions working. The open-source version seems to me like a really good idea to help people try out the idea and the application, and will hopefully foster some pretty rapid completion of features (I know that at least a few of my workmates are already looking at building in a few things). The producticized version though will, over time, hopefully have some modest growth, to the point where I can fund my work and offer the best support I can on both versions of the application.&lt;/p&gt;

&lt;h2 id=&quot;outcomes&quot;&gt;Outcomes&lt;/h2&gt;

&lt;p&gt;I’m blown away with how much I got done - and really quite pleased with a few developers who stepped into unfamiliar territory to try and get this built. The application’s certainly not finished, but it’s also not spartan - everything that I really wanted to be there, is there, working, and being used. I’ve got a lot of improvements I still want to make, and I’m already taking advantage of the velocity I achieved during the last weekend to power through a few of the tougher features I wanted to start.&lt;/p&gt;

&lt;p&gt;Here’s the timeline of what I got done, when:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;strong&gt;Friday night:&lt;/strong&gt; ran &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;rails new&lt;/code&gt;, created application, promptly stopped development for the night and went and socialized.&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;Saturday night:&lt;/strong&gt; spent an hour doing the ‘Users can log in’ story. Promptly stopped development and went and socialized.&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;Sunday morning:&lt;/strong&gt; started development on the application in earnest with the ability to create questions. Designed log in pages.&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;Sunday lunchtime:&lt;/strong&gt; ability to create questions, answers, and mark answers as accepted.&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;Sunday evening:&lt;/strong&gt;  ability to vote on questions and answers, search for questions, and invite users.&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;Sunday night:&lt;/strong&gt; worked on commenting on questions and answers, polishing interface. Demonstrate working application deployed to camp server in front of all Railscamp attendees.&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;Monday morning:&lt;/strong&gt; travelled home, added multi-tenancy for hosted version (multiple organizations can have completely seperate data).&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;Monday early-afternoon:&lt;/strong&gt; purchased domain name, production server and set up production server running application. Done!&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;Tuesday morning:&lt;/strong&gt; set up organization account for my employed, was immediately used and continues to be used.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;And the overall outcomes of this whole project are:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;I have a bunch more knowledge in activity and event tracking, layout and design, rapid development &amp;amp; testing and presenting in front of a large group of people.&lt;/li&gt;
  &lt;li&gt;I ran a development team at a ridiculous pace, and managed the codebase to a point where there’s not really any really smelly code.&lt;/li&gt;
  &lt;li&gt;I pushed a complex Rails app as an open-source project on Github, and set it up with Travis CI to automatically run the application’s tests on multiple versions of Ruby, and CodeClimate to automatically analyse the code quality.&lt;/li&gt;
  &lt;li&gt;I augmented a project I had open-sourced, turned it into a product, and open-sourced it - in a day.&lt;/li&gt;
  &lt;li&gt;I had fun! And I still have fun!&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;I can’t really emphasize that last point enough - I went to Railscamp not to develop a kickass project, or product, but to learn new things, meet new people and overall, have fun. &lt;strong&gt;I’m really, really happy to say that a kickass product just happened to be a fringe benefit of having all that fun!&lt;/strong&gt;&lt;/p&gt;

&lt;blockquote&gt;
  &lt;p&gt;If you’d like to try out Inquest, feel free to &lt;a href=&quot;https://github.com/joshmcarthur/inquest&quot;&gt;check it out on Github&lt;/a&gt;. If you or the company you work at would like to try it out and see if it’s a good fit with you, feel free to &lt;a href=&quot;https://twitter.com/sudojosh&quot;&gt;get in touch with me&lt;/a&gt; - I’d love to hear from you. If you don’t think it’s for you - that’s fine as well! I’m constantly adding cool stuff to the open-source project though, so if you’re a developer, and you would like to support the project, please star it on github:&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;&lt;img src=&quot;/img/posts/inquest/github.jpg&quot; alt=&quot;Inquest on Github&quot; /&gt;&lt;/p&gt;

</description>
        <pubDate>Wed, 27 Mar 2013 00:00:00 +1300</pubDate>
        <link>https://joshmcarthur.com/2013/03/27/how-i-built-an-open-source-product-and-sass-collaboratively-in-less-than-48-hours.html</link>
        <guid isPermaLink="true">https://joshmcarthur.com/2013/03/27/how-i-built-an-open-source-product-and-sass-collaboratively-in-less-than-48-hours.html</guid>
        
        
      </item>
    
      <item>
        <title>The curses of proprietary technologies</title>
        <description>&lt;p&gt;As a web developer, I’ve got a pretty good pick of websites - I know what companies are prepared to stick with tried and tested technologies, and combine it with just enough new stuff to make browsing their site pleasant and easy to do.&lt;/p&gt;

&lt;p&gt;Every now and then though, I’m forced out of my comfort zone, and need to find information on something else, and when I do so, it’s not long before I come across some really nasty stuff. Sometimes, it’s just a hard to use website, when what I really need is specific product information - for example, &lt;a href=&quot;http://www.placemakers.co.nz/products/outdoor-and-leisure/bbqs.aspx&quot;&gt;trying to find out what BBQ’s Placemakers carry&lt;/a&gt; - when I don’t just want to be directed to enquire at the nearest store. The thing that really gets me though is when a retailer completely ignores the open and multi-platform/multi-device nature of the web, and instead chooses to use something that is closed, only works with special software installed, and drives customers away.&lt;/p&gt;

&lt;p&gt;Typically, the most common example you will see is Adobe Flash - a decade-old technology that even Adobe is scrambing to get killed off. The example I want to bring to light this evening however is even worse - it’s Silverlight, on a local jeweller’s website, &lt;a href=&quot;http://michaelhill.com&quot;&gt;Micahel Hill Jewellers&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Silverlight is a piece of software that Microsoft built to compete with Flash, back when Flash was really popular. It provides the ability to read files from the user, gather input from webcams, microphones and animate content. It also requires that it be installed on each computer that needs to use it, and does not work on &lt;a href=&quot;http://google.com/chrome&quot;&gt;Google Chrome&lt;/a&gt;, or very well on Mac at all (Indeed, when I DID install Silverlight, Firefox &lt;a href=&quot;https://addons.mozilla.org/en-US/firefox/blocked/p129&quot;&gt;blocked it, as it causes problems for the browser itself&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;This, generally, is a case of the wrong techology for the job. Michael Hill is a retail jeweller - that means that they want as many customers checking out their products as possible, both in stores and online. I imagine online purchasing must &lt;em&gt;seem&lt;/em&gt; tempting for them, but to me it seems like a dead-end - jewelerry purchases are typically expensive, and as such, it’s a better idea to focus on providing really good customer service support while you browse online (such as being able to ask dedicated support staff about specific products).&lt;/p&gt;

&lt;p&gt;By &lt;strong&gt;depending&lt;/strong&gt; on Silverlight, Michael Hill is making the wrong technology decision. Because they are using Silverlight, their (potential) customers can’t check prices or products on their iPhones, Android or any mobile device. They also can’t access the site from public or shared computers that they cannot install software onto, or do not wish to install software onto. In a way then, Michael Hill can claim the dubious honour of having one of the &lt;strong&gt;least accessible public websites available online&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;Certainly, the capabilities of the site do not indicate that any sort of framework such as Silverlight is necessary, but what is the alternative?&lt;/p&gt;
</description>
        <pubDate>Sat, 12 Jan 2013 00:00:00 +1300</pubDate>
        <link>https://joshmcarthur.com/2013/01/12/the-curses-of-proprietary-technologies.html</link>
        <guid isPermaLink="true">https://joshmcarthur.com/2013/01/12/the-curses-of-proprietary-technologies.html</guid>
        
        
      </item>
    
      <item>
        <title>Publishing with ActiveRecord: a guide to encapsulated code</title>
        <description>&lt;p&gt;It’s not at all uncommon to be asked by a client “So, I’d really like to be able to edit that bit of the page just there - can we do that?”. The answer, of course, is yes, but managing content has no end of complexities. Just one of these many is publishing - that is, controlling content, where it is visible, and to whom. In this blog post, I’m going to describe how I worked to move code out of several applications into a shared library, and the trials and tribulations I had ensuring this eventual &lt;a href=&quot;http://github.com/3months/has_publishing&quot;&gt;Rubygem&lt;/a&gt; worked with as many combinations of other Rubygems as possible.&lt;/p&gt;

&lt;h2 id=&quot;moving-code&quot;&gt;Moving Code&lt;/h2&gt;

&lt;p&gt;The first step to properly encapsulating code is to identify specific code functions that can be moved out of individual codebases. In the case of the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;has_publishing&lt;/code&gt; library, I identified the processes of publishing a record, creating a draft record, marking a record for future publishing and the querying of all the above states to be a common functionality that was required in a number of other sites. Once this functionality was identified, it was no big deal to copy-and-paste relevant bits of code into a new gem file structure, generated by the lovely &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;bundle gem&lt;/code&gt; command. Of course, because this code had been pulled in from a number of different locations, it was still very disorganized and messy. Fortunately, I had the next step:&lt;/p&gt;

&lt;h2 id=&quot;refactoring-code&quot;&gt;Refactoring Code&lt;/h2&gt;

&lt;p&gt;Once the code is gathered together in a central library, it’s all set to be tidied up, tested, and pushed out for the world to enjoy. For a gem that adds functionality to another application, the best way to rapidly prototype how things may fit togther is to set up a quick Rails app, and add the gem as a local dependency:&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-ruby&quot; data-lang=&quot;ruby&quot;&gt;&lt;span class=&quot;c1&quot;&gt;# Gemfile&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;gem&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&apos;has_publishing&apos;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;ss&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;&apos;../has_publishing&apos;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;p&gt;This way, any changes you make to the gem are instantly reflected in your testing harness. The other part of refactoring code is to ensure that the code gets covered by comprehensive unit tests.&lt;/p&gt;

&lt;p&gt;Refactoring code can take different forms, depending on the state of the code! Largely, you’ll be looking for duplicated or overly-complex code that can be merged into common behaviour, or any other code that you don’t feel is ready for the public to see.&lt;/p&gt;

&lt;h2 id=&quot;testing-code&quot;&gt;Testing Code&lt;/h2&gt;

&lt;p&gt;Testing code in a gem is always more tricky that testing within an application, because you don’t have an exact context to test against - just behaviour. Typically, you need to do a bit more set up to have an environment that provides just enough context to run your tests against.&lt;/p&gt;

&lt;p&gt;The easiest way to do this for this sort of project is to replicate the same sort of support that Rails provides for testing models against a database, in as little code as possible. Here’s how I set up a database-backed model for testing against in &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;has_publishing&lt;/code&gt;:&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-ruby&quot; data-lang=&quot;ruby&quot;&gt;&lt;span class=&quot;c1&quot;&gt;# Establish a database connection &lt;/span&gt;
&lt;span class=&quot;c1&quot;&gt;# to a local SQLite3 database&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;Base&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;establish_connection&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;
  &lt;span class=&quot;ss&quot;&gt;:adapter&lt;/span&gt;  &lt;span class=&quot;o&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&apos;sqlite3&apos;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
  &lt;span class=&quot;ss&quot;&gt;:database&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;nf&quot;&gt;join&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;dirname&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;&apos;has_publishing_test.db&apos;&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;# Create the data table for the model&lt;/span&gt;
&lt;span class=&quot;c1&quot;&gt;# I&apos;ll be testing against, and then&lt;/span&gt;
&lt;span class=&quot;c1&quot;&gt;# manually migrate the database to this&lt;/span&gt;
&lt;span class=&quot;c1&quot;&gt;# point &lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;CreateTestModels&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;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;up&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;if&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;Base&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;connection&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;table_exists?&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;test_models&quot;&lt;/span&gt;
      &lt;span class=&quot;n&quot;&gt;drop_table&lt;/span&gt; &lt;span class=&quot;ss&quot;&gt;:test_models&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;

    &lt;span class=&quot;n&quot;&gt;create_table&lt;/span&gt; &lt;span class=&quot;ss&quot;&gt;:test_models&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;t&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;|&lt;/span&gt;
      &lt;span class=&quot;n&quot;&gt;t&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;datetime&lt;/span&gt; &lt;span class=&quot;ss&quot;&gt;:published_at&lt;/span&gt;
      &lt;span class=&quot;n&quot;&gt;t&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;datetime&lt;/span&gt; &lt;span class=&quot;ss&quot;&gt;:embargoed_until&lt;/span&gt;
      &lt;span class=&quot;n&quot;&gt;t&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;string&lt;/span&gt; &lt;span class=&quot;ss&quot;&gt;:kind&lt;/span&gt;
      &lt;span class=&quot;n&quot;&gt;t&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;boolean&lt;/span&gt; &lt;span class=&quot;ss&quot;&gt;:dirty&lt;/span&gt;
      &lt;span class=&quot;n&quot;&gt;t&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;references&lt;/span&gt; &lt;span class=&quot;ss&quot;&gt;:published&lt;/span&gt;

      &lt;span class=&quot;n&quot;&gt;t&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;timestamps&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;nc&quot;&gt;self&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;down&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;drop_table&lt;/span&gt; &lt;span class=&quot;ss&quot;&gt;:test_models&lt;/span&gt;
  &lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;

&lt;span class=&quot;no&quot;&gt;CreateTestModels&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;migrate&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;ss&quot;&gt;:up&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;

&lt;span class=&quot;c1&quot;&gt;# Add the testing model, complete&lt;/span&gt;
&lt;span class=&quot;c1&quot;&gt;# with has_publishing&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;TestModel&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;Base&lt;/span&gt;
  &lt;span class=&quot;n&quot;&gt;has_publishing&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;p&gt;With this testing harness in place, it’s pretty simple to make assertions about how that model should behave with &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;has_publishing&lt;/code&gt; - it’s simply ActiveRecord. The reason I prefer not to take the approach of many Rails-exclusive gems and generate an entire dummy application to test with is that I feel like this approach is much more transparent, and just enough set up to get the tests passing, without getting in the way.&lt;/p&gt;

&lt;h2 id=&quot;bootstrapping-with-rails-engines&quot;&gt;Bootstrapping with Rails engines&lt;/h2&gt;

&lt;p&gt;Although this gem is framework-agnostic (only requiring ActiveRecord), I do want to make it easier for people using Rails to add publishing to their models. Because of this, I have added a &lt;a href=&quot;http://guides.rubyonrails.org/generators.html&quot;&gt;Rails generator&lt;/a&gt; to my gem. This generator can add the migration to add publishing attributes for any model with a single command, making it much easier to get the correct database schema set up quickly.&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;http://guides.rubyonrails.org/engines.html&quot;&gt;Rails engines&lt;/a&gt; are related, but not the same as generators. The easiest way to think of them is pluggable sub-applications - they may include extra functionality, change existing functionality or perform some special function, but they are typically just bundles of controllers, models and views, often bundled with generators to set all of these objects up within a Rails application.&lt;/p&gt;

&lt;p&gt;Adding generators to a gem is actually very easy to do, but it did have a couple of gotchas to ensure that Rails actually picks up the generator and makes it available for use.&lt;/p&gt;

&lt;h2 id=&quot;gem-compatiblity&quot;&gt;Gem Compatiblity&lt;/h2&gt;
</description>
        <pubDate>Mon, 17 Dec 2012 00:00:00 +1300</pubDate>
        <link>https://joshmcarthur.com/2012/12/17/publishing-with-activerecord-a-guide-to-encapsulated-code.html</link>
        <guid isPermaLink="true">https://joshmcarthur.com/2012/12/17/publishing-with-activerecord-a-guide-to-encapsulated-code.html</guid>
        
        
      </item>
    
      <item>
        <title>HTML5 Notifications</title>
        <description>&lt;p&gt;&lt;a href=&quot;https://github.com/joshmcarthur/on-the-spot&quot;&gt;On the Spot&lt;/a&gt; is a pet project that I tend to develop with bleeding-edge features in mind - something a bit more volatile than &lt;a href=&quot;http://github.com/joshmcarthur/Latter&quot;&gt;Latter&lt;/a&gt;, which is used heavily enough to justify a more cautious development process. In this post, I’m going to detail how I added HTML5 notifications to On the Spot, the upsides and downsides, and the Coffeescript class that does it all.&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/img/posts/html5_notifications.jpg&quot; alt=&quot;On the Spot displaying a notification&quot; /&gt;&lt;/p&gt;

&lt;p&gt;So, HTML5 Notifications are an interesting little API, in that they’re not a formal specification by &lt;a href=&quot;http://w3c.org&quot;&gt;W3C&lt;/a&gt; - in fact, this API was first implemented by the Chromium project, and it seems to be a feature that will continue on the fringe of modern browsers only (i.e. - this probably isn’t coming to Internet Explorer).&lt;/p&gt;

&lt;p&gt;They are quite powerful though - it’s a non-intrusive way of letting the user know something has happened, without reverting to an &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;alert()&lt;/code&gt;, or having to load a Javascript library, which will still only work if the page is currently visible.&lt;/p&gt;

&lt;p&gt;Adding the notifications though, it actually very simple - all the heavy lifting is done by the web browser - all that we need to do is:&lt;/p&gt;

&lt;h4 id=&quot;1-request-permission-to-display-notifications&quot;&gt;1. Request permission to display notifications&lt;/h4&gt;
&lt;h4 id=&quot;2-check-that-we-have-permission-to-display-notifications&quot;&gt;2. Check that we have permission to display notifications&lt;/h4&gt;
&lt;h4 id=&quot;3-set-up-the-notification&quot;&gt;3. Set up the notification&lt;/h4&gt;
&lt;h4 id=&quot;4-display-the-notitication&quot;&gt;4. Display the notitication&lt;/h4&gt;

&lt;p&gt;Permission needs to be requested, since you’ll be displaying notifications even if the user is not currently looking right at the page. We need to check that we have permission before we try and display anything to avoid errors, and then we can actually show the user the notification.&lt;/p&gt;

&lt;h2 id=&quot;my-implementation&quot;&gt;My Implementation&lt;/h2&gt;

&lt;p&gt;I was able to centralize the processing of Notifications in On the Spot by writing a Coffeescript class that handled the permissions (request &amp;amp; check), set up and display of notifications. This class is easy extensible later on if I need to add any new notifications as well. Here it is!&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-coffeescript&quot; data-lang=&quot;coffeescript&quot;&gt;&lt;span class=&quot;nx&quot;&gt;OnTheSpot&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;Notification&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;setup&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;-&amp;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;na&quot;&gt;notifications&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;na&quot;&gt;webkitNotifications&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;unless&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;na&quot;&gt;notifications&lt;/span&gt;
		&lt;span class=&quot;k&quot;&gt;if&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;na&quot;&gt;notifications&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&amp;amp;&amp;amp;&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;na&quot;&gt;notifications&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;checkPermission&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;mi&quot;&gt;0&lt;/span&gt;
			&lt;span class=&quot;nx&quot;&gt;OnTheSpot&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;Notification&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;supported&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;true&lt;/span&gt;
		&lt;span class=&quot;k&quot;&gt;else&lt;/span&gt;
			&lt;span class=&quot;nx&quot;&gt;OnTheSpot&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;Notification&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;supported&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;false&lt;/span&gt;

	&lt;span class=&quot;na&quot;&gt;requestAccess&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;-&amp;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;na&quot;&gt;notifications&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;requestPermission&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;();&lt;/span&gt;

	&lt;span class=&quot;na&quot;&gt;trackPlaying&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;track&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;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;unless&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;OnTheSpot&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;Notification&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;supported&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;==&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;true&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;track&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;na&quot;&gt;notifications&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;createNotification&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;
			&lt;span class=&quot;s&quot;&gt;&apos;/assets/apple-touch-icon-precomposed.png&apos;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; 
			&lt;span class=&quot;nx&quot;&gt;track&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;s&quot;&gt;&apos;On the Spot :: Track Playing&apos;&lt;/span&gt;
		&lt;span class=&quot;p&quot;&gt;).&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;show&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;/figure&gt;

&lt;p&gt;The flow of this class is:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;Add a document.ready event listener in jQuery, and call the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;requestAccess&lt;/code&gt; function, when a button is clicked (you must request access from a button or link click, otherwise the browser will not actually ask the user for permission)&lt;/li&gt;
  &lt;li&gt;Call &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;setup&lt;/code&gt; - this function performs some checks to calculate whether we’re able to use and display notifications.&lt;/li&gt;
  &lt;li&gt;To show a notification, just call &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;trackPlaying&lt;/code&gt; with the track data - the class will set up the notification and display it.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2 id=&quot;downsides&quot;&gt;Downsides&lt;/h2&gt;

&lt;p&gt;The main downside to notifications is browser support. Unless your users are running on Safari 6+, Chrome for Android, or Chrome, notifications won’t work for you. Firefox docs indicate that some support for notifications may be in the pipeline, but more likely this support is going to be in Mozilla’s efforts to support ‘desktop’ web apps. Internet Explorer, Opera and other mobile browsers have no support at all, with none planned.&lt;/p&gt;

&lt;p&gt;The other downside to the notifications is that they are (unsuprisingly, but still), only going to work when the page generating the notifications is open in a tab somewhere. Page focus isn’t necessary, but the generating page must be open. There might be a way to get around this by using &lt;a href=&quot;https://developer.mozilla.org/en-US/docs/DOM/Using_web_workers?redirectlocale=en-US&amp;amp;redirectslug=Using_web_workers&quot;&gt;Web workers&lt;/a&gt;, but I’ve got no concrete proof that this works.&lt;/p&gt;

&lt;h2 id=&quot;upsides&quot;&gt;Upsides&lt;/h2&gt;

&lt;p&gt;The main upside to notifications is that they reflect the native theme of the OS, are expected and anticipated behaviour from the user, and are generally easy to do, without compromising on look or feel.&lt;/p&gt;

&lt;p&gt;They are also easy to generate, and ‘float’ apart from the actual browser application. This means that you don’t need to have the browser visible on the screen to see notifications. They also work without the page being visible (which is required for all other notification techniques to work, which tend to style a div on the page to look and behave like a notification), and, on OS X, appear in the Notification Center, which is pretty neat - users can click on the notification to go back to the application.&lt;/p&gt;

&lt;hr /&gt;

&lt;p&gt;Overall, notifications are pretty cool, albeit with the proviso that they’re not used for anything important. They do require user opt-in to work, but once this permission is granted, they are simple and easy to use.&lt;/p&gt;
</description>
        <pubDate>Tue, 02 Oct 2012 00:00:00 +1300</pubDate>
        <link>https://joshmcarthur.com/2012/10/02/html5-notifications.html</link>
        <guid isPermaLink="true">https://joshmcarthur.com/2012/10/02/html5-notifications.html</guid>
        
        
      </item>
    
      <item>
        <title>Secret Keeping with Rails</title>
        <description>&lt;p&gt;Ever needed to store some secrets in Rails that you don’t want to share with the world? Yeah, same! In this post, I’m going to outline a really simple way to store your application’s secrets in a file called &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;config/secrets.yml&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/img/posts/secret.jpg&quot; alt=&quot;Secret image&quot; /&gt;&lt;/p&gt;

&lt;h4 id=&quot;rails-configuration-and-you&quot;&gt;Rails configuration and you&lt;/h4&gt;

&lt;p&gt;First of all, let’s discuss exactly how this is going to work. If you’ve even configured a Rails app before, you may have noticed that you’re setting your configuration on a &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;config&lt;/code&gt; object inside a block that looks a bit like this:&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-ruby&quot; data-lang=&quot;ruby&quot;&gt;&lt;span class=&quot;no&quot;&gt;MyRailsApp&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;no&quot;&gt;Application&lt;/span&gt;&lt;span class=&quot;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;compile_assets&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;kp&quot;&gt;false&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;p&gt;Something you might not know, though,is that these configuration settings are then available on an object called &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Rails.configuration&lt;/code&gt;. What we’re going to do is add an initializer that loads a secrets file, and then loads them into the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Rails.configuration&lt;/code&gt; object so that we can access them in our application.&lt;/p&gt;

&lt;p&gt;The advantage of this method versus something like &lt;a href=&quot;http://joshmcarthur.com/2012/08/31/process-configuration-management-with-foreman.html&quot;&gt;Foreman&lt;/a&gt; is that:&lt;/p&gt;

&lt;ol&gt;
  &lt;li&gt;This method is much closer to Rails conventions - for example, instead of using a dotfile, it uses a basic initializer&lt;/li&gt;
  &lt;li&gt;It doesn’t require running your application through any special interface like Foreman does&lt;/li&gt;
  &lt;li&gt;It just does one thing, and does it well - storing configuration.&lt;/li&gt;
&lt;/ol&gt;

&lt;h4 id=&quot;reading-the-config-file&quot;&gt;Reading the config file&lt;/h4&gt;

&lt;p&gt;As I’ve mentioned, we’ll be reading a file called &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;config/secrets.yml&lt;/code&gt;. Let’s get our application set up to load this file.&lt;/p&gt;

&lt;blockquote&gt;
  &lt;p&gt;NB: For this example, I’m assuming you’ve got a Rails 3.x application set up - you don’t need any models, controllers or anything else - we can test this from a &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;rails console&lt;/code&gt;.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;First of all, add the configuration file:&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-yaml&quot; data-lang=&quot;yaml&quot;&gt;&lt;span class=&quot;na&quot;&gt;development&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;
  &lt;span class=&quot;na&quot;&gt;facebook&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;
    &lt;span class=&quot;na&quot;&gt;app_key&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;123abc&lt;/span&gt;
    &lt;span class=&quot;na&quot;&gt;secret&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;123abcdef&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;p&gt;- as you can see, this secrets file just contains some credentials to use with Facebook OAuth - it can contain as many keys as you like, however.&lt;/p&gt;

&lt;p&gt;Next we need to add a gem to our &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Gemfile&lt;/code&gt;. This gem is called &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Hashie&lt;/code&gt;, and extends Ruby’s &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Hash&lt;/code&gt; object to do some cool stuff - in our case, we can pass it a hash, and it will give us method access to our secrets - in other words, we can do this:&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-ruby&quot; data-lang=&quot;ruby&quot;&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;secrets&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;facebook&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;app_key&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;p&gt;Instead of:&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-ruby&quot; data-lang=&quot;ruby&quot;&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;secrets&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&apos;facebook&apos;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;][&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&apos;app_key&apos;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;p&gt;As a bonus, if you’re using Omniauth and any Omniauth strategies, you’ve probably already installed &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Hashie&lt;/code&gt; as a dependency of one of these - we’re just doing to re-use it here, so let’s add it to our Gemfile and be specific:&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-ruby&quot; data-lang=&quot;ruby&quot;&gt;&lt;span class=&quot;c1&quot;&gt;# Gemfile&lt;/span&gt;
&lt;span class=&quot;c1&quot;&gt;# ...&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;gem&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&apos;hashie&apos;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;p&gt;Next, let’s add an initializer to load this file in &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;config/initializers/01_load_secrets&lt;/code&gt;. The “01” part is important; it tells Rails to load it before any other initializer runs (since we might need our secrets straight away in an initializer):&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-ruby&quot; data-lang=&quot;ruby&quot;&gt;&lt;span class=&quot;c1&quot;&gt;# config/initializers/01_load_secrets.rb&lt;/span&gt;
&lt;span class=&quot;no&quot;&gt;MyRailsApp&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;no&quot;&gt;Application&lt;/span&gt;&lt;span class=&quot;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;c1&quot;&gt;# Note: Hashie is a gem dependency&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;secrets&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;Hashie&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;no&quot;&gt;Mash&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;YAML&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;load_file&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;no&quot;&gt;Rails&lt;/span&gt;&lt;span class=&quot;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;&apos;config&apos;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&apos;secrets.yml&apos;&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;env&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;k&quot;&gt;end&lt;/span&gt; &lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;p&gt;I’ll just talk through what this is doing:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;First, we open a configuration block for our application -  this gives us access to a &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;config&lt;/code&gt; object we can set keys on&lt;/li&gt;
  &lt;li&gt;Next, we load the YAML file we created, but only pull in keys for the Rails environment we are running in - this let’s us have keys for &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;test&lt;/code&gt;, &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;development&lt;/code&gt;, etc. in a single file.&lt;/li&gt;
  &lt;li&gt;Finally, we wrap that YAML file load in a &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Hashie::Mash&lt;/code&gt;. &lt;a href=&quot;https://github.com/intridea/hashie&quot;&gt;Hashie&lt;/a&gt; is just a collection of Hash extensions that, in this case, gives turns our Hash (which is what &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;YAML.load_file&lt;/code&gt; returns) into an object with method access (so we can go &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;config.secrets.facebook.app_key&lt;/code&gt;)&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;That’s all we need to do to load the config in - as you can see, it’s actually a really simple operation, and use of Hashie means that we don’t need to mess up our code later down the track with lots of square brackets and hash keys.&lt;/p&gt;

&lt;h4 id=&quot;using-configuration&quot;&gt;Using configuration&lt;/h4&gt;

&lt;p&gt;Once we’ve got the file loaded, actually using the configuration is actually really easy - just access your top-secret secrets on &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Rails.configuration.secrets&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;For reference, here’s how you might set [Omniauth] to support logging in with Facebook using our new &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;secrets.yml&lt;/code&gt; file:&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-ruby&quot; data-lang=&quot;ruby&quot;&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;application&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;config&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;middleware&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;use&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;OmniAuth&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;no&quot;&gt;Builder&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;do&lt;/span&gt;
  &lt;span class=&quot;n&quot;&gt;provider&lt;/span&gt; &lt;span class=&quot;ss&quot;&gt;:facebook&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;secrets&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;facebook&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;app_key&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;secrets&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;facebook&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;secret&lt;/span&gt; 
&lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

</description>
        <pubDate>Sun, 30 Sep 2012 00:00:00 +1200</pubDate>
        <link>https://joshmcarthur.com/2012/09/30/secret-keeping-with-rails.html</link>
        <guid isPermaLink="true">https://joshmcarthur.com/2012/09/30/secret-keeping-with-rails.html</guid>
        
        
      </item>
    
      <item>
        <title>Redesigning for readability - a retrospective</title>
        <description>&lt;p&gt;I’ll be the first to admit that I’m not that great at design, especially typography and detailed elements. I’m just one of those people who can see what they want in their heads just fine, but something just goes…wrong…between the head and the page. In this post, I’m going to explain some of the lessons I learned while focussing on making this blog clearer, more consistent and more readable in general.&lt;/p&gt;

&lt;div class=&quot;image-box stack-1&quot;&gt;
	&lt;figure&gt;
		&lt;img src=&quot;/img/posts/old_blog.jpg&quot; alt=&quot;My old blog - something wasn&apos;t quite right&quot; /&gt;
		&lt;figcaption&gt;
			My old blog - something wasn&apos;t quite right
			&lt;a href=&quot;/img/posts/old_blog.jpg&quot; class=&quot;img-larger&quot;&gt;Open Full Size&lt;/a&gt;
		&lt;/figcaption&gt;
	&lt;/figure&gt;
&lt;/div&gt;

&lt;p&gt;My old blog wasn’t too bad - in fact, I’d had a few people profess to rather like it. The problem I had with it is that it felt like a cheap, low-quality knock-off of blog layouts like &lt;a href=&quot;http://zachholman.com/&quot;&gt;Zach Holman’s&lt;/a&gt;. While I’d made some efforts to make things a bit different and more readable, something wasn’t right about it - I just looked at it, and it didn’t look &lt;strong&gt;great&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;So, I decided to go back to grassroots - I’d start from a basic blog layout - this time, based on &lt;a href=&quot;http://tom.preston-werner.com/&quot;&gt;Tom Preston-Werner’s site&lt;/a&gt;, but rather than trying to replicate what was there, I’d go my own way. In the end, I ended up changing just about everything from that original layout, but in a good way - it feels like my own now.&lt;/p&gt;

&lt;h2 id=&quot;early-design-decisions&quot;&gt;Early design decisions&lt;/h2&gt;

&lt;p&gt;I do a lot of drawing, when I go to ‘design’ something. Really, I’m just trying to fool myself into thinking I’m some sort of ‘designer’, doing ‘wireframes’ - but I’m not, because my drawings are a pile of rubbish (see example). What I do end up doing though, is thinking as I doodle, and take notes across the page. By the end of the process, I have a pretty good idea of what I change.&lt;/p&gt;

&lt;div class=&quot;image-box stack-1&quot;&gt;
	&lt;figure&gt;
		&lt;img src=&quot;/img/posts/wireframing.jpg&quot; alt=&quot;My &apos;wireframing&apos; process&quot; /&gt;
		&lt;figcaption&gt;
			My &apos;wireframing&apos; process
			&lt;a href=&quot;/img/posts/wireframing.jpg&quot; class=&quot;img-larger&quot;&gt;Open Full Size&lt;/a&gt;
		&lt;/figcaption&gt;
	&lt;/figure&gt;
&lt;/div&gt;

&lt;p&gt;Some of the key things I decided were:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;The body font had to go - what I had hoped would be a lovely, slightly old-fashioned-but-still-somehow-modern look turned into something that looked just a tad messy. Along with the copious use of &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;sans-serif&lt;/code&gt; fonts elsewhere on the site, there was a lot of inconsistency here&lt;/li&gt;
  &lt;li&gt;My blog posts always end up long. I should add in more images to break up the content and make things easier to read&lt;/li&gt;
  &lt;li&gt;I didn’t like the listing of posts on the homepage - I felt that it was an unnecessary impediment to visitors seeing what &lt;em&gt;content&lt;/em&gt; I have for them.&lt;/li&gt;
  &lt;li&gt;I refer to &lt;a href=&quot;https://www.github.com&quot;&gt;Github&lt;/a&gt; projects a lot - I should be able to ‘feature’ them somehow for those readers who don’t know what I’m talking about.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2 id=&quot;typographical-inspiration&quot;&gt;Typographical Inspiration&lt;/h2&gt;

&lt;p&gt;As I’ve said, I liked the way that mojombo’s blog had a nice, slicked-back look to it, and decided that this was the sort of thing I wanted to pursue. I also decided that since consistency and readability was a priority for me, I would focus on trying to get post body copy looking the best.&lt;/p&gt;

&lt;p&gt;I had stumbled across Mark Boulton’s &lt;a href=&quot;http://www.markboulton.co.uk/journal/comments/five-simple-steps-to-better-typography&quot;&gt;blog&lt;/a&gt; a few days previously, and took some of his lessons on board from there - in particular, I took the &lt;a href=&quot;http://en.wikipedia.org/wiki/Measure_\(typography\)&quot;&gt;measure&lt;/a&gt; of my old blog, and found that the lines were a bit too long - between 80 and 90 characters. I also found that the line-spacing (Sorry, &lt;strong&gt;leading&lt;/strong&gt;), didn’t quite match the word-spacing.&lt;/p&gt;

&lt;p&gt;Once I had some ideas for basic typographic improvements, I then had a look around &lt;a href=&quot;http://www.google.com/webfonts&quot;&gt;Google Web Fonts&lt;/a&gt; to find some feature fonts for headings and important copy. I really liked the look of &lt;a href=&quot;http://tomafro.net/&quot;&gt;Tom Ward’s blog&lt;/a&gt;, which used a nice clean serif font for the headings that contrasted well with the sans-serif body copy. Eventually, I stumbled across &lt;a href=&quot;http://www.google.com/webfonts/specimen/Arvo&quot;&gt;Arvo&lt;/a&gt;, which fitted my needs perfectly.&lt;/p&gt;

&lt;p&gt;Since my blog is powered by &lt;a href=&quot;https://github.com/mojombo/jekyll&quot;&gt;Jekyll&lt;/a&gt;, I also took a look through the list of Jekyll sites that are maintained on the &lt;a href=&quot;https://github.com/mojombo/jekyll/wiki/Sites&quot;&gt;wiki&lt;/a&gt;. This had &lt;em&gt;some&lt;/em&gt; typographical and design stuff, but most of all, it allowed me to explore what might be possible to add to my posts and my homepage. It’s here I got the idea to add my ‘Featured Projects’ section to the bottom of some of my posts, and to show a ‘snippet’ of content for the latest 5 blog posts on the homepage, along with a list of all posts further down the page.&lt;/p&gt;

&lt;h2 id=&quot;implementation-and-experimentation&quot;&gt;Implementation and Experimentation&lt;/h2&gt;

&lt;p&gt;I used &lt;a href=&quot;http://lesscss.org/&quot;&gt;LESS&lt;/a&gt; for implementing the CSS for the new blog layout and typography. I’d originally begun using LESS with &lt;a href=&quot;http://twitter.github.com/bootstrap&quot;&gt;Twitter Bootstrap&lt;/a&gt; (which enhances ‘my’ design capabilities significantly).&lt;/p&gt;

&lt;p&gt;I set the base font-size on the body element, so that I could use relative font sizes from there on, and instructed the page to use that workhorse of fonts, &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;&apos;Arial&apos;, &apos;Helvetica&apos;, sans-serif&lt;/code&gt;. I did try a few other fonts, but in the end settled on Helvetica, largely due to:&lt;/p&gt;

&lt;ol&gt;
  &lt;li&gt;Standardization - it’s an expected and familiar font for readers&lt;/li&gt;
  &lt;li&gt;Adaptability - it’s suitable for a broad range of contexts and uses&lt;/li&gt;
  &lt;li&gt;Legibility - Helvetica/Arial remains legible at a range of sizes&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;To fix up the measure (which should be about 70 characters across), I used a smaller single-column layout. I used a width of 50%, which at a width of 1280px, yields an average measure of just over 70 characters. I prefered a fluid width for this column, so that it can scale a bit more gracefully between resolutions, and I added a media query or two to change the width to 90% once the resolution drops below &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;767px&lt;/code&gt;, so that at that size, I’m not wasting any page.&lt;/p&gt;

&lt;p&gt;After these few improvements, everything was looking way better. As is the way with these things though, discovering the best fit for all of these, along with some other minor tweaks, took the best part of a day.&lt;/p&gt;

&lt;h2 id=&quot;what-i-like&quot;&gt;What I Like&lt;/h2&gt;

&lt;p&gt;I do like the refactoring work I’ve done overall. I feel like everything is a bit more utilitarian and back-to-basics, but in a good, clean way. It still looks professional and makes it clear that I can wrangle CSS, without being too much for what it is.&lt;/p&gt;

&lt;p&gt;I’ve placed more images on a couple of my posts, and I also like how this is working. What was previously mostly drab posts (whose content is fascinating, I assure you!), has now been broken up a wee bit.&lt;/p&gt;

&lt;p&gt;I also think that the way I’m featuring Github projects is working well, and will hopefully help people out a bit if they’ve just scanned through a post and want to find out more about a specific project I’ve mentioned.&lt;/p&gt;

&lt;p&gt;The last thing I’m happy with is the homepage. I was a bit worried about achieving the right balance here, but I think that it’s about right. I now ‘feature’ the latest 5 posts on the homepage, with, hopefully, just enough content to get visitors interested in clicking through to the full article, but just below that I also include a full listing of all posts. This means that for an casual visitor, there’s info right there for them to find something interesting, and for the return visitor, there’s a way for them to refer back to a previous post without having to leave the homepage to dig through an archive.&lt;/p&gt;

&lt;h2 id=&quot;what-i-dont-like&quot;&gt;What I don’t like&lt;/h2&gt;

&lt;p&gt;There’s not actually too much - what I don’t like are things that I can fix in improvements later down the track.&lt;/p&gt;

&lt;p&gt;The main thing I’d like to have is captions underneath images. As I’ve mentioned in previous posts, I use Markdown for writing posts, and markdown happens to not support the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;&amp;lt;figure&amp;gt;&lt;/code&gt; or &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;&amp;lt;figcaption&amp;gt;&lt;/code&gt; tags, which are perfect for an image/caption scenario. I write this markup in HTML for some blog images, but I’d really like it to happen straight off a Markdown image tag. I should be able to fix this by adding some javascript to wrap each image tag in a figure element, with the caption being based on the image’s &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;title&lt;/code&gt; attribute.&lt;/p&gt;

&lt;p&gt;Another thing I’d like to do is properly attribute images and resources I use. A lot of the images that I use are in the public domain (usually out of &lt;a href=&quot;http://commons.wikimedia.org&quot;&gt;Wikimedia Commons&lt;/a&gt;), but some do require proper attribution, which I’m not currently giving. I’m still thinking about the proper way of doing this - I may be able to just add it to the image caption, once I’ve got my figure tags appearing properly, or I may do something like the ‘Featured Project’ boxes I’ve got at the bottom of posts that reference Github projects. Hm.&lt;/p&gt;

&lt;p&gt;Overall, I’m quite pleased with this. There’s certainly some improvements to be made, but I feel like I’ve built up something that is a bit less proprietary and based on previous work, and a bit more tailored to me. Along the way, I’ve even managed to learn a few things about typography, LESS, and advanced Jekyll functionality! Win!&lt;/p&gt;

&lt;hr /&gt;

&lt;p&gt;Got any comments about more improvements I could make? I welcome tweets or private messages on Twitter - &lt;a href=&quot;http://twitter.com/sudojosh&quot;&gt;@sudojosh&lt;/a&gt;&lt;/p&gt;
</description>
        <pubDate>Tue, 25 Sep 2012 00:00:00 +1200</pubDate>
        <link>https://joshmcarthur.com/2012/09/25/redesigning-for-readability-a-retrospective.html</link>
        <guid isPermaLink="true">https://joshmcarthur.com/2012/09/25/redesigning-for-readability-a-retrospective.html</guid>
        
        
      </item>
    
      <item>
        <title>Process configuration management with Foreman</title>
        <description>&lt;p&gt;Often in the Rails projects I build, I have a need to store configuration that I don’t really want to have in my code. The most obvious way to do this is to either store the configuration in a YAML file (e.g. &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;config/settings.yml&lt;/code&gt;), or in environment variables.&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;http://rubygems.org/gems/foreman&quot;&gt;Foreman&lt;/a&gt; is &lt;a href=&quot;http://heroku.com&quot;&gt;Heroku&lt;/a&gt;’s answer to these types of configuration challenges. It is used internally at Heroku to manage processes and their configuration.&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/img/posts/foreman.jpg&quot; alt=&quot;Child cannery workers sitting on bushelbaskets as they prepare beans under watchful eye of foreman. Source: LIFE Magazine&quot; /&gt;&lt;/p&gt;

&lt;h2 id=&quot;managing-configuration&quot;&gt;Managing Configuration&lt;/h2&gt;

&lt;p&gt;If you’ve ever worked with Heroku, you’ve probably seen the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;heroku config&lt;/code&gt; command. When you run this command on an active Heroku project, you see output like this:&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-bash&quot; data-lang=&quot;bash&quot;&gt;&lt;span class=&quot;o&quot;&gt;===&lt;/span&gt; latter Config Vars
DATABASE_URL:                 postgres://xxxxxxxxx:5432/db
GEM_PATH:                     vendor/bundle/ruby/1.9.1
HEROKU_POSTGRESQL_PURPLE_URL: postgres://xxxxxxxxx:5432/db
PGBACKUPS_URL:                https://backups.heroku.com/client
RACK_ENV:                     production
RAILS_ENV:                    production
SHARED_DATABASE_URL:          postgres://xxxxxxx&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;p&gt;This configuration is actually managed by Foreman, and is stored in a file called ‘.env’ in the root of the project. The .env file for the configuration above would look like this:&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-bash&quot; data-lang=&quot;bash&quot;&gt;&lt;span class=&quot;nv&quot;&gt;DATABASE_URL&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;postgres://xxxxxxxxx:5432/d3o4ndc7b0g39o
&lt;span class=&quot;nv&quot;&gt;GEM_PATH&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;vendor/bundle/ruby/1.9.1
&lt;span class=&quot;nv&quot;&gt;HEROKU_POSTGRESQL_PURPLE_URL&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;postgres://xxxxxxxxx:5432/db
&lt;span class=&quot;nv&quot;&gt;LANG&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;en_US.UTF-8
&lt;span class=&quot;nv&quot;&gt;PGBACKUPS_URL&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;https://backups.heroku.com/client
&lt;span class=&quot;nv&quot;&gt;RACK_ENV&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;production
&lt;span class=&quot;nv&quot;&gt;RAILS_ENV&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;production
&lt;span class=&quot;nv&quot;&gt;SHARED_DATABASE_URL&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;postgres://xxxxxxx&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;p&gt;The cool thing about Foreman is that you can use this .env file just as easily locally. All it does is reads the file when you run a command, and loads these variables into your shell as environment variables.&lt;/p&gt;

&lt;h2 id=&quot;managing-processes&quot;&gt;Managing Processes&lt;/h2&gt;

&lt;p&gt;The other functionality that Foreman provides is the ability to run and manage multiple processes. This is something that you may or may not have run into in Heroku, but it’s still pretty cool. Without Foreman, if you had a Rails server, a PubSub server (&lt;a href=&quot;http://faye.jcoglan.com/&quot;&gt;Faye&lt;/a&gt;, for example), and a daemon to run, you might have to do the following:&lt;/p&gt;

&lt;ol&gt;
  &lt;li&gt;New terminal tab, run &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;bundle exec rails s&lt;/code&gt;&lt;/li&gt;
  &lt;li&gt;New terminal tab, run &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;bundle exec rackup faye.ru&lt;/code&gt;&lt;/li&gt;
  &lt;li&gt;New terminal tab, run &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;bundle exec script/daemon start&lt;/code&gt;&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;…and then you need to keep an eye on each of these tabs.&lt;/p&gt;

&lt;p&gt;With Foreman, you create a file called a &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Procfile&lt;/code&gt;. This stands for ‘Process file’, and is a simple listing of the processes you would like to run. For example, &lt;a href=&quot;https://github.com/joshmcarthur/on-the-spot&quot;&gt;on-the-spot&lt;/a&gt;’s &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Procfile&lt;/code&gt; looks like this:&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-ruby&quot; data-lang=&quot;ruby&quot;&gt;&lt;span class=&quot;ss&quot;&gt;web: &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;bundle&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;exec&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;rails&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;s&lt;/span&gt; 
&lt;span class=&quot;ss&quot;&gt;pubsub: &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;bundle&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;exec&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;rackup&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;/&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;private_pub&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;ru&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;-&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;s&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;thin&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;-&lt;/span&gt;&lt;span class=&quot;no&quot;&gt;E&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;production&lt;/span&gt;
&lt;span class=&quot;ss&quot;&gt;daemon: &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;bundle&lt;/span&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;nf&quot;&gt;/&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;lib&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;/&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;daemons&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;/&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;spotify_controller_ctl&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;run&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;p&gt;When you run &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;foreman start&lt;/code&gt;, Foreman will boot up each of these processes, and output the combined logs for all in the window. When you stop Foreman (By hitting Ctrl-C), it will shutdown each of the processes before exiting. This process is much easier to handle than multiple terminal tabs.&lt;/p&gt;

&lt;h2 id=&quot;installing-foreman&quot;&gt;Installing Foreman&lt;/h2&gt;

&lt;p&gt;There’s two ways to install Foreman:&lt;/p&gt;

&lt;ol&gt;
  &lt;li&gt;&lt;strong&gt;Via the Heroku Toolbelt.&lt;/strong&gt; Heroku publishes a ‘Toolbelt’ - a collection of tools required to develop applications that run on the Heroku platform. I don’t use this method myself, as I prefer to have control over what’s installed and where, but you can install this from &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;https://toolbelt.heroku.com/&lt;/code&gt; if you’d like&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;As a gem.&lt;/strong&gt; You can just run &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;gem install foreman&lt;/code&gt; to get it installed - easy as that. If you’re using RVM, then you may want to follow my pattern - I normally place Foreman in my global gemset along with the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;heroku&lt;/code&gt; gem, so that it accessible to all my apps. If you’re just experimenting with Foreman though, you may want to place it in it’s own gemset.&lt;/li&gt;
&lt;/ol&gt;

&lt;h2 id=&quot;using-foreman&quot;&gt;Using Foreman&lt;/h2&gt;

&lt;p&gt;Foreman is actually pretty easy to use on a day-to-day basis - there’s really only two commands you need to run:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;foreman run [cmd]&lt;/code&gt; - You can run any command with Foreman, doing all the environment variable setup, etc. beforehand. If you &lt;em&gt;require&lt;/em&gt; any variables in your application’s setup routine, then you will need to run all your &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;rails *&lt;/code&gt; and &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;rake *&lt;/code&gt; commands with this, otherwise you can just run it when you need access to these variables.&lt;/li&gt;
  &lt;li&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;foreman start&lt;/code&gt; - Use this command to start all the processes listed in your &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Procfile&lt;/code&gt;, and just &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Ctrl-C&lt;/code&gt; when you want to stop them.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Easy as that really. There’s a bunch of other things you can do with Foreman (such as exporting launchd and init.d scripts to run your application automatically), but they’re not really something you’ll need to know to get started.&lt;/p&gt;

&lt;h2 id=&quot;debugging-ruby-with-foreman&quot;&gt;Debugging Ruby with Foreman&lt;/h2&gt;

&lt;p&gt;A common complaint I hear about Foreman is that it’s quite complex to debug application’s in. This is a consequence of the way that Foreman combines the running of all of the processes in your Procfile into a single output flow - debugger’s can’t easily make it through. I’ve found there’s a couple of ways to debug with Foreman though:&lt;/p&gt;

&lt;ol&gt;
  &lt;li&gt;&lt;strong&gt;Run a Rails debugging server with &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;foreman run&lt;/code&gt;.&lt;/strong&gt; Since &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;foreman run&lt;/code&gt; doesn’t do any of the fancy &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Procfile&lt;/code&gt; stuff, you can just run &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;foreman run rails s --debugger&lt;/code&gt; when you &lt;em&gt;do&lt;/em&gt; need to debug - it will behave just as it always has.&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;Use a debugging console directly in Foreman.&lt;/strong&gt; I’ve had mixed results with this, but if you do run with &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Foreman start&lt;/code&gt; and have debuggers in your code, I have noticed that these breakpoints do get hit - it’s just that Foreman doesn’t always output the prompt characters for the debugger. You can still type in &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;irb&lt;/code&gt; to drop into an IRB session, and everything will behave as it usually does from here - it just requires a keen eye to spot when you’ve landed on a debugger in Foreman (since there won’t be any output)&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;Run a remote instance of ruby-debug and connect that way.&lt;/strong&gt; Ruby’s debugging support includes the ability to run a debugging server from your application, that you can connect to from another terminal window or tab. To do this, you need to do a bit of setup in an initializer, but once it’s there, it’s pretty seamless:
    &lt;ul&gt;
      &lt;li&gt;In &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;config/environments/development.rb&lt;/code&gt;, add the following lines:&lt;/li&gt;
    &lt;/ul&gt;
  &lt;/li&gt;
&lt;/ol&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-ruby&quot; data-lang=&quot;ruby&quot;&gt;    &lt;span class=&quot;c1&quot;&gt;# Wait for remote connections from rdebug&lt;/span&gt;
    &lt;span class=&quot;no&quot;&gt;Debugger&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;wait_connection&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;c1&quot;&gt;# Listen for remote connections&lt;/span&gt;
	&lt;span class=&quot;no&quot;&gt;Debugger&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;start_remote&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;ul&gt;
  &lt;li&gt;In a new terminal window or tab, run the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;rdebug&lt;/code&gt; program to connect to your remote debugging instance:&lt;/li&gt;
&lt;/ul&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-bash&quot; data-lang=&quot;bash&quot;&gt; 	rdebug &lt;span class=&quot;nt&quot;&gt;-c&lt;/span&gt;
 	&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;ul&gt;
  &lt;li&gt;(More info &lt;a href=&quot;https://github.com/ddollar/foreman/issues/58&quot;&gt;on this Github issue&lt;/a&gt;)&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;I do prefer the first two options to the last, as I find that code changes seem unnecessary for this, and I normally aren’t needing to debug all the time. Largely though, this is up to personal preference, and I wanted to present all options.&lt;/p&gt;

&lt;h2 id=&quot;conclusion&quot;&gt;Conclusion&lt;/h2&gt;

&lt;p&gt;Foreman is a pretty awesome tool for development, especially for those apps that may have some complex configuration and process management needs. I don’t use it for all my applications (since I’m a strong believer in picking &lt;strong&gt;just enough&lt;/strong&gt; of the right tools for the job), but I do find it incredibly handy to extract all my configuration and processes into external files that I can easily update and generally DRY up my code.&lt;/p&gt;

&lt;blockquote&gt;
  &lt;p&gt;&lt;strong&gt;Information:&lt;/strong&gt;
This blog post is based on a lightning talk I delivered
at &lt;a href=&quot;http://3months.com&quot;&gt;3months&lt;/a&gt; for the dev team. 3months is an agile-based company based in Wellington, New Zealand. We have fun. Interested in working for us? &lt;a href=&quot;http://www.3months.com/working-for-us/&quot;&gt;Give us a shout!&lt;/a&gt;&lt;/p&gt;
&lt;/blockquote&gt;

</description>
        <pubDate>Fri, 31 Aug 2012 00:00:00 +1200</pubDate>
        <link>https://joshmcarthur.com/2012/08/31/process-configuration-management-with-foreman.html</link>
        <guid isPermaLink="true">https://joshmcarthur.com/2012/08/31/process-configuration-management-with-foreman.html</guid>
        
        
      </item>
    
      <item>
        <title>Building On-The-Spot: A Spotify play queue</title>
        <description>&lt;h2 id=&quot;about-the-application&quot;&gt;About the Application&lt;/h2&gt;

&lt;p&gt;The idea behind On the Spot was to allow everyone in the office at work to control the playlist, so that there was some semblence of fairness about what we listen to. Probably the simplest way of showing you what it is is to see some pictures, right?&lt;/p&gt;

&lt;div class=&quot;image-box stack-2&quot;&gt;
	&lt;figure&gt;
	    &lt;img src=&quot;/img/posts/building-on-the-spot/searching.jpg&quot; alt=&quot;Part 1: You can search for music&quot; /&gt;
	    &lt;figcaption&gt;
	    	Part 1: You can search for music
	    	&lt;a href=&quot;/img/posts/building-on-the-spot/searching.jpg&quot; class=&quot;img-larger&quot;&gt;
	    	&lt;i class=&quot;icon-external-link&quot;&gt;&lt;/i&gt;
	    	Open Full-Size
	    	&lt;/a&gt;
	    &lt;/figcaption&gt;
	&lt;/figure&gt;
	
	&lt;figure&gt;
	    &lt;img src=&quot;/img/posts/building-on-the-spot/queue.jpg&quot; alt=&quot;Part 2: You can see what&apos;s playing, and what&apos;s about to play&quot; /&gt;
	    &lt;figcaption&gt;
	    	Part 2: You can see what&apos;s playing, and what&apos;s about to play
	    	&lt;a href=&quot;/img/posts/building-on-the-spot/queue.jpg&quot; class=&quot;img-larger&quot;&gt;
	    	&lt;i class=&quot;icon-external-link&quot;&gt;&lt;/i&gt;
	    	Open Full-Size
	    	&lt;/a&gt;
	    &lt;/figcaption&gt;
	&lt;/figure&gt;

	&lt;br class=&quot;clearfix&quot; /&gt;
&lt;/div&gt;

&lt;p&gt;So, we basically have a two-page app - one page for finding music, another for playing it. This whole thing runs on a server in the office, and is DNS-d to http://spotify.office.3months.com, making it accessible to all staff.&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/img/posts/spotify.jpeg&quot; alt=&quot;Spotify&apos;s headquarters at Humlegårdsgatan&quot; /&gt;&lt;/p&gt;

&lt;h2 id=&quot;technology-choices&quot;&gt;Technology Choices&lt;/h2&gt;

&lt;p&gt;The technology choices for this app ended up being pretty clear cut, although as with all projects I do in Rails, I do sense that I could have found a lighter approach.&lt;/p&gt;

&lt;p&gt;In the end, the core technologies are:&lt;/p&gt;

&lt;ol&gt;
  &lt;li&gt;Rails 3.2&lt;/li&gt;
  &lt;li&gt;No database&lt;/li&gt;
  &lt;li&gt;Redis&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://github.com/Burgestrand/Hallon&quot;&gt;Hallon&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://github.com/mirasrael/daemons-rails&quot;&gt;Daemons Rails&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;Bootstrap for UI (customized)&lt;/li&gt;
  &lt;li&gt;Font Awesome for Icons&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;When I started the project, I considered a couple of projects that I thought might make the ‘real-time’ part of the application, mostly (Sinatra and &lt;a href=&quot;http://cramp.in/&quot;&gt;Cramp&lt;/a&gt;). I figured though, that as I wanted to add extra features, the drawbacks of these frameworks were going to get in the way - I opted to use Rails, accept the performance hit, in favor of not having to risk the framework getting in the way too much.&lt;/p&gt;

&lt;h2 id=&quot;searching&quot;&gt;Searching&lt;/h2&gt;

&lt;p&gt;Searching was the first feature I worked on - here’s a reminder of what a search looks like:&lt;/p&gt;

&lt;div class=&quot;image-box stack-1&quot;&gt;
	&lt;figure&gt;
	    &lt;img src=&quot;/img/posts/building-on-the-spot/searching_again.jpg&quot; alt=&quot;A reminder about searching&quot; /&gt;
	    &lt;figcaption&gt;
	    	A reminder about searching
	    	&lt;a href=&quot;/img/posts/building-on-the-spot/searching_again.jpg&quot; class=&quot;img-larger&quot;&gt;
	    	&lt;i class=&quot;icon-external-link&quot;&gt;&lt;/i&gt;
	    	Open Full-Size
	    	&lt;/a&gt;
	    &lt;/figcaption&gt;
	&lt;/figure&gt;
&lt;/div&gt;

&lt;p&gt;I actually tried two different ways of searching Spotify - the first was &lt;a href=&quot;https://github.com/philnash/meta-spotify&quot;&gt;Meta Spotify&lt;/a&gt;, and the second was &lt;a href=&quot;https://github.com/Burgestrand/Hallon&quot;&gt;Hallon&lt;/a&gt;. I ended up sticking with Hallon, as I was using this library to also play the files, however I did that Meta Spotify’s results seemed more accurate.&lt;/p&gt;

&lt;p&gt;The way I implemented the search was actually very simple - I have a &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;SearchController&lt;/code&gt;, with a &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;new&lt;/code&gt; action which looks a bit like this:&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-ruby&quot; data-lang=&quot;ruby&quot;&gt;  &lt;span class=&quot;k&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;new&lt;/span&gt;
    &lt;span class=&quot;vi&quot;&gt;@results&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;cache&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;s2&quot;&gt;&quot;search-for-&lt;/span&gt;&lt;span class=&quot;si&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;:q&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;s2&quot;&gt;&quot;&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;do&lt;/span&gt;
      &lt;span class=&quot;no&quot;&gt;Hallon&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;no&quot;&gt;Search&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;params&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;ss&quot;&gt;:q&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;],&lt;/span&gt; &lt;span class=&quot;ss&quot;&gt;:tracks&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;25&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;ss&quot;&gt;:tracks_offset&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&amp;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;load&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;tracks&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;to_a&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;track&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;o&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;track&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; 
          &lt;span class=&quot;ss&quot;&gt;:artist&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;track&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;artists&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;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; 
          &lt;span class=&quot;ss&quot;&gt;:album&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;track&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;album&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; 
          &lt;span class=&quot;ss&quot;&gt;:popularity&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;track&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;popularity&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; 
          &lt;span class=&quot;ss&quot;&gt;:uri&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;track&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;to_link&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;to_str&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;respond_to&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;nb&quot;&gt;format&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;|&lt;/span&gt;
      &lt;span class=&quot;nb&quot;&gt;format&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;json&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;render&lt;/span&gt; &lt;span class=&quot;ss&quot;&gt;:json&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;vi&quot;&gt;@results&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;/figure&gt;

&lt;p&gt;In human terms, I perform the search, restricting the number of results to 25 for performance, and then pull a subset of the information available to me out, and return the data as JSON.&lt;/p&gt;

&lt;p&gt;The other side of this is the Javascript - once again, quite a simple and common implementation of a typeahead search. The code for this is a bit more convoluted, so I’m going to post a shortened and annotated version of the main bit of it:&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-coffeescript&quot; data-lang=&quot;coffeescript&quot;&gt;&lt;span class=&quot;c1&quot;&gt;# This function takes results that are returned from an AJAX getJSON() call to the search action&lt;/span&gt;
&lt;span class=&quot;nx&quot;&gt;handleSearchResults&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;results&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;tracks&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;c1&quot;&gt;# We only want to add unique tracks to our array&lt;/span&gt;
	&lt;span class=&quot;k&quot;&gt;for&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;track&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;in&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;results&lt;/span&gt;
		&lt;span class=&quot;nx&quot;&gt;tracks&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;push&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;track&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;unless&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;na&quot;&gt;include&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;tracks&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;track&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;

	&lt;span class=&quot;c1&quot;&gt;# Now that we have the raw data, we iterate through the&lt;/span&gt;
	&lt;span class=&quot;c1&quot;&gt;# collection and build an HTML list item for&lt;/span&gt;
	&lt;span class=&quot;c1&quot;&gt;# each track&lt;/span&gt;
	&lt;span class=&quot;nx&quot;&gt;tracks&lt;/span&gt; &lt;span class=&quot;o&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;na&quot;&gt;map&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;tracks&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;track&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;$&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;&amp;lt;li&amp;gt;&amp;lt;/li&amp;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;append&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;s&quot;&gt;&apos;&amp;lt;span&amp;gt;&amp;lt;/span&amp;gt;&apos;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;).&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;addClass&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&apos;track&apos;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;).&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;text&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;track&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;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;append&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;s&quot;&gt;&apos;&amp;lt;span&amp;gt;&amp;lt;/span&amp;gt;&apos;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;).&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;addClass&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&apos;artist&apos;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;).&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;text&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot; - &lt;/span&gt;&lt;span class=&quot;si&quot;&gt;#{&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;track&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;artist&lt;/span&gt;&lt;span class=&quot;si&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;s&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;append&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;s&quot;&gt;&apos;&amp;lt;span&amp;gt;&amp;lt;/span&amp;gt;&apos;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;).&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;addClass&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&apos;album&apos;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;).&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;text&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot; (&lt;/span&gt;&lt;span class=&quot;si&quot;&gt;#{&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;track&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;album&lt;/span&gt;&lt;span class=&quot;si&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;s&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;data&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&apos;popularity&apos;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;track&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;popularity&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;data&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&apos;uri&apos;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;track&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;uri&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;prepend&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;s&quot;&gt;&apos;&amp;lt;i&amp;gt;&amp;lt;/i&amp;gt;&apos;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;).&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;addClass&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&apos;icon-music&apos;&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;append&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;s&quot;&gt;&quot;&amp;lt;button&amp;gt;&amp;lt;/button&amp;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;addClass&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&apos;btn btn-success play-btn&apos;&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;attr&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&apos;title&apos;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;Play: &lt;/span&gt;&lt;span class=&quot;si&quot;&gt;#{&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;track&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;si&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;s&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;append&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;s&quot;&gt;&apos;&amp;lt;i /&amp;gt;&apos;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;).&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;addClass&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&apos;icon-plus&apos;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)))&lt;/span&gt;


	&lt;span class=&quot;c1&quot;&gt;# We want to sort the collection by the &apos;popularity&apos;&lt;/span&gt;
	&lt;span class=&quot;c1&quot;&gt;# that Spotify records. This helps pull&lt;/span&gt;
	&lt;span class=&quot;c1&quot;&gt;# popular songs to the top, and push Karaoke/Knock off&lt;/span&gt;
	&lt;span class=&quot;c1&quot;&gt;# versions to the bottom&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;nx&quot;&gt;_&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;sortBy&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;tracks&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;item&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;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;1000&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;unless&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;na&quot;&gt;data&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&apos;popularity&apos;&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;na&quot;&gt;log&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;Track: &lt;/span&gt;&lt;span class=&quot;si&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;na&quot;&gt;find&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&apos;.track&apos;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;).&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;text&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;s&quot;&gt;, popularity: &lt;/span&gt;&lt;span class=&quot;si&quot;&gt;#{&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;100&lt;/span&gt; &lt;span class=&quot;o&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;na&quot;&gt;data&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&apos;popularity&apos;&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;s&quot;&gt;&quot;&lt;/span&gt;
		&lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;100&lt;/span&gt; &lt;span class=&quot;o&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;na&quot;&gt;data&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&apos;popularity&apos;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;

    &lt;span class=&quot;c1&quot;&gt;# Don&apos;t add anything if we have nothing in our collection&lt;/span&gt;
	&lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;if&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;na&quot;&gt;length&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;==&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;0&lt;/span&gt;
	
	&lt;span class=&quot;c1&quot;&gt;# Empty the last set of search results and add &lt;/span&gt;
	&lt;span class=&quot;c1&quot;&gt;# the new ones.&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;s&quot;&gt;&apos;#tracks&apos;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;).&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;empty&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;na&quot;&gt;each&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;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;o&quot;&gt;-&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;s&quot;&gt;&apos;#tracks&apos;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;).&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;append&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;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;p&gt;So, the combination of these two code blocks is enough to give us a typeahead search of tracks on Spotify. The search itself is trivialized by the Hallon library, and so the only real concern for us here is performance - each search takes about 500ms, and each key stroke performs a search.&lt;/p&gt;

&lt;h2 id=&quot;queueing-tracks&quot;&gt;Queueing Tracks&lt;/h2&gt;

&lt;p&gt;Once we have our search results, clicking on the ‘+’ icon next to the track queues it. The queue is a simple &lt;a href=&quot;http://redis.io&quot;&gt;Redis&lt;/a&gt; &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;list&lt;/code&gt;, which I push onto from the ‘left’ side, and pop items off from the ‘right’ side. This makes the controller action to queue a track as simple as this:&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-ruby&quot; data-lang=&quot;ruby&quot;&gt;  &lt;span class=&quot;k&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;create&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;position&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;vg&quot;&gt;$redis&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;lpush&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;tracks&quot;&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;:uri&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;flash&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;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;ss&quot;&gt;:notice&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;I18n&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;t&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&apos;queue.create.success&apos;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;ss&quot;&gt;:position&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;position&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;ordinalize&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;render&lt;/span&gt; &lt;span class=&quot;ss&quot;&gt;:status&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;ss&quot;&gt;:created&lt;/span&gt;
  &lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;p&gt;Queueing is one thing, but we still need a way to interact with this queue, and play tracks through Spotify. This is where the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;daemons-rails&lt;/code&gt; gem comes in!&lt;/p&gt;

&lt;h2 id=&quot;playing-tracks&quot;&gt;Playing Tracks&lt;/h2&gt;

&lt;p&gt;‘On the Spot’ has two components. The first, we’ve discussed - it’s the Rails application, that we use for searching and queueing tracks. The second is a &lt;a href=&quot;http://en.wikipedia.org/wiki/Daemon_(computing)&quot;&gt;daemon&lt;/a&gt;, which runs in a loop, grabbing the next track to play, and playing it using Hallon - here’s the main bit of this code:&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-ruby&quot; data-lang=&quot;ruby&quot;&gt;  &lt;span class=&quot;c1&quot;&gt;# Keep running while there are tracks to play&lt;/span&gt;
  &lt;span class=&quot;k&quot;&gt;while&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;uri&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;vg&quot;&gt;$redis&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;rpop&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;tracks&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
  
    &lt;span class=&quot;c1&quot;&gt;# Only continue if the URI looks like a spotify one&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;uri&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=~&lt;/span&gt; &lt;span class=&quot;sr&quot;&gt;/\Aspotify\:/&lt;/span&gt;


      &lt;span class=&quot;c1&quot;&gt;# Set the currently-playing track&lt;/span&gt;
      &lt;span class=&quot;vg&quot;&gt;$redis&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;set&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;currently_playing&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;uri&lt;/span&gt;

      &lt;span class=&quot;c1&quot;&gt;# Load the track&lt;/span&gt;
      &lt;span class=&quot;n&quot;&gt;track&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;Hallon&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;no&quot;&gt;Track&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;uri&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;).&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;load&lt;/span&gt;

      &lt;span class=&quot;k&quot;&gt;begin&lt;/span&gt;
        &lt;span class=&quot;c1&quot;&gt;# Pre-load the track&lt;/span&gt;
        &lt;span class=&quot;vg&quot;&gt;$player&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;load&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;track&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
        
        &lt;span class=&quot;c1&quot;&gt;# Play the track (blocks until complete)&lt;/span&gt;
        &lt;span class=&quot;vg&quot;&gt;$player&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;play!&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;track&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;vg&quot;&gt;$redis&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;del&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;currently_playing&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;span class=&quot;k&quot;&gt;end&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;p&gt;In a similar way to searching, much of the work is done for us. Really, all this code is doing is pulling a Spotify URI (pretty much an ID) from our Redis list (called “tracks”), storing it as the value of a Redis key (called “currently_playing”), so that we can display which track is playing, and then actually plays the track (using Hallon). Once the track has finished playing, we un-set that currently playing value.&lt;/p&gt;

&lt;h2 id=&quot;future-features&quot;&gt;Future Features&lt;/h2&gt;

&lt;p&gt;Really, all I’ve done so far, then, is built a web-interface around the functionality afforded by the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Hallon&lt;/code&gt; gem (which is itself a wrapper around Spotify’s API library, &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;libspotify&lt;/code&gt;). Despite this though, it does work exactly do design. The next thing that needs doing is the ability to control the player - right now, there is no way to pause or stop the player, except by actually logging into the Redis console and clearing the list by hand. I’d also like to add some more realtime capability using Faye or Pusher, and provide browser notifications of playing tracks.&lt;/p&gt;

&lt;h2 id=&quot;conclusion&quot;&gt;Conclusion&lt;/h2&gt;

&lt;p&gt;This has ended up a bit more long-winded than I meant it to be, but has hopefully touched on the process I’ve gone through to build this app - in total, I’ve spent around 12-15 hours getting this to its current state, including writing this blog post, and altogether, it’s working pretty well. In the future, I hope to write further about adding realtime notifications and features to this application, so stay tuned!&lt;/p&gt;

</description>
        <pubDate>Sun, 12 Aug 2012 00:00:00 +1200</pubDate>
        <link>https://joshmcarthur.com/2012/08/12/building-on-the-spot-a-spotify-play-queue.html</link>
        <guid isPermaLink="true">https://joshmcarthur.com/2012/08/12/building-on-the-spot-a-spotify-play-queue.html</guid>
        
        
      </item>
    
      <item>
        <title>VirtualBox Debians with additional adapters</title>
        <description>&lt;p&gt;I get stung by this one all the time, and I can never remember how I fix it. I’m noting it down here, in the hope that next time I’m googling for a fix I’ll stumble across my own blog post.&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/img/posts/virtualbox.jpeg&quot; alt=&quot; Robert Fairbanks and Robert Davis, both of IBM talking in computer room of North American Aviation. Magnetic tape drives and consoles and IBM computers are in the room.&quot; /&gt;&lt;/p&gt;

&lt;p&gt;Adding network adapters (or refreshing the MAC address) of a virtual machine running a Debian-based OS on VirtualBox causes a strange problem - the new interface is not listed in the guest after booting, and the network interface device is simply not present.&lt;/p&gt;

&lt;p&gt;The fix for this is very simple. Effectively, Debian ‘caches’ some interface information like the MAC address (which isn’t really supposed to change), in a file that gets loaded on boot. When the MAC address DOES change, Debian effectively ignores that interface.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;If you see errors that seem to be causing a missing network interface after adjusting Network adapter settings for the VM in VirtualBox, try the following fix:&lt;/strong&gt;&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-bash&quot; data-lang=&quot;bash&quot;&gt;&lt;span class=&quot;nb&quot;&gt;sudo rm&lt;/span&gt; /etc/udev/rules.d/70-persistent-net.rules&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;p&gt;This file gets recreated on boot, so it’s perfectly safe to delete - if you’re feeling cautious though, you can also just move it somewhere else. Once you’ve deleted it, just restart the VM, and you should have the correct number of networking interfaces ready to go!&lt;/p&gt;
</description>
        <pubDate>Tue, 31 Jul 2012 00:00:00 +1200</pubDate>
        <link>https://joshmcarthur.com/2012/07/31/virtualbox-debians-with-additional-adapters.html</link>
        <guid isPermaLink="true">https://joshmcarthur.com/2012/07/31/virtualbox-debians-with-additional-adapters.html</guid>
        
        
      </item>
    
      <item>
        <title>Whakamāori te Latter</title>
        <description>&lt;h3 id=&quot;introduction&quot;&gt;Introduction&lt;/h3&gt;

&lt;p&gt;I’ve spent the last week translating Latter (really badly) into Māori using Rails’ I18n support. Since this is the first time I’ve done a full site translation, I wanted to detail how I did this here.&lt;/p&gt;

&lt;p&gt;First, I’ll quickly explain how Rails I18n support works. Essentially, it’s a key-value store - you specify a ‘key’ which represents a bit of text like this:&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-ruby&quot; data-lang=&quot;ruby&quot;&gt;&lt;span class=&quot;no&quot;&gt;I18n&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;t&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&apos;player.create.success&apos;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;p&gt;The I18n module then takes this key, and looks up the ‘value’ that is stored in a &lt;a href=&quot;http://www.yaml.org/&quot;&gt;YAML&lt;/a&gt; file, that, for this string, might contain a snippet like this:&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-yaml&quot; data-lang=&quot;yaml&quot;&gt;&lt;span class=&quot;na&quot;&gt;player&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;
  &lt;span class=&quot;na&quot;&gt;create&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;
    &lt;span class=&quot;na&quot;&gt;success&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&apos;&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;Player&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s&quot;&gt;was&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s&quot;&gt;created&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s&quot;&gt;successfully&apos;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;p&gt;It then substitutes the &lt;em&gt;request&lt;/em&gt; for the string value for the current language, with the &lt;em&gt;string value&lt;/em&gt; in the translation file (in this case, ‘Player was created successfully’)&lt;/p&gt;

&lt;p&gt;The main advantage of I18n is centralization. Moving all the messages and small bits of text used all over the site makes it much easier to change content, try out different wording in certain areas, but most significantly, to offer support for multiple languages globally by changing just a couple of files.&lt;/p&gt;

&lt;h3 id=&quot;adding-te-reo-māori-to-latter&quot;&gt;Adding Te Reo Māori to Latter&lt;/h3&gt;

&lt;p&gt;&lt;img src=&quot;/img/posts/maori_flag.png&quot; alt=&quot;The Tino Rangatiratanga Flag of the Maori sovereignty movement&quot; /&gt;&lt;/p&gt;

&lt;p&gt;The process of adding Te Reo to Latter was pretty simple - just time consuming. Initally, the bulk of the work was just finding translatable strings of text, and adding English translations for them, so that I could later add support for Māori. This process goes much more smoothly when you know where to look:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;Flash messsages in the controller&lt;/li&gt;
  &lt;li&gt;Links and buttons&lt;/li&gt;
  &lt;li&gt;Model-based forms&lt;/li&gt;
  &lt;li&gt;Navigation and Layout&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The next challenging bit that I didn’t realize would be difficult is the organization of keys into a logical structure where particular strings are going to be easy to find and maintain. The strategy that I chose to stick with is the following:&lt;/p&gt;

&lt;ol&gt;
  &lt;li&gt;Resource - the name of the resource, for example, Player, Game, Score. I preferred singular forms of names, as these more closely relate to the source class or model&lt;/li&gt;
  &lt;li&gt;Action/Category - the controller action to which the translation belongs, or, if the string is for a specific area of the layout or a page, then an identifier for this idea&lt;/li&gt;
  &lt;li&gt;Identifier - a key that identifies the string uniquely.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;For example:&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-yaml&quot; data-lang=&quot;yaml&quot;&gt;&lt;span class=&quot;c1&quot;&gt;# Resource&lt;/span&gt;
&lt;span class=&quot;na&quot;&gt;player&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;
  &lt;span class=&quot;c1&quot;&gt;# Action&lt;/span&gt;
  &lt;span class=&quot;na&quot;&gt;update&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;
    &lt;span class=&quot;c1&quot;&gt;# Key&lt;/span&gt;
    &lt;span class=&quot;na&quot;&gt;success&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&apos;&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;Player&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s&quot;&gt;updated&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s&quot;&gt;successfully&apos;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;p&gt;By this point, I had the bulk of the application translated into English - that is, rather than having key messages, labels and actions specified in the HTML markup, I had moved it into &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;config/locales/en.yml&lt;/code&gt; - the file that Rails loads when the application starts, and where it looks for translations.&lt;/p&gt;

&lt;div class=&quot;image-box stack-2&quot;&gt;
	&lt;figure&gt;
	    &lt;img src=&quot;/img/posts/te-latter/latter_en.jpg&quot; alt=&quot;Current state of play: Latter with English translations&quot; /&gt;
	    &lt;figcaption&gt;
	    	The Current State of Play: Latter, translated to English
	    	&lt;a href=&quot;/img/posts/te-latter/latter_en.jpg&quot; class=&quot;img-larger&quot;&gt;
	    	&lt;i class=&quot;icon-external-link&quot;&gt;&lt;/i&gt;
	    	Open Full-Size
	    	&lt;/a&gt;
	    &lt;/figcaption&gt;
	&lt;/figure&gt;
	
	&lt;figure&gt;
    &lt;img src=&quot;/img/posts/te-latter/latter_mi.jpg&quot; alt=&quot;New state of play: Latter with Te Reo Māori translations&quot; /&gt;
    &lt;figcaption&gt;
    	New state of play: Latter with Te Reo Māori translations
	    	&lt;a href=&quot;/img/posts/te-latter/latter_en.jpg&quot; class=&quot;img-larger&quot;&gt;
	    	&lt;i class=&quot;icon-external-link&quot;&gt;&lt;/i&gt;
	    	Open Full-Size
	    	&lt;/a&gt;
    	&lt;/figcaption&gt;
    &lt;/figure&gt;
    &lt;br class=&quot;clearfix&quot; /&gt;
&lt;/div&gt;

&lt;p&gt;The next step, then is to add another language. This process in itself is actually very simple. Simply copy the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;en.yml&lt;/code&gt; file to &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;mi.yml&lt;/code&gt;, and begin translating!&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-bash&quot; data-lang=&quot;bash&quot;&gt;&lt;span class=&quot;nb&quot;&gt;cp &lt;/span&gt;config/locales/en.yml config/locales/mi.yml&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;p&gt;I found Māori quite a hard language to translate, for a few reasons. I had no prior experience with translating content, and a fairly weak grasp on Māori language constructs and syntax. Because of this, I wouldn’t be at all surprised to discover a range of minor errors in the translations, however I did find that having a central file to refer back to, as well as a fairly standard and simple vocabulary made this process much more simple.&lt;/p&gt;

&lt;p&gt;The translation process itself is very simple. Given a translation within &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;config/locales/en.yml&lt;/code&gt; like the following:&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-yaml&quot; data-lang=&quot;yaml&quot;&gt;&lt;span class=&quot;na&quot;&gt;en&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;
  &lt;span class=&quot;na&quot;&gt;game&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;
    &lt;span class=&quot;na&quot;&gt;index&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;
      &lt;span class=&quot;na&quot;&gt;heading&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;All Games&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;p&gt;then the Māori equaivalent in &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;config/locales/mi.yml&lt;/code&gt; would look like the following:&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-yaml&quot; data-lang=&quot;yaml&quot;&gt;&lt;span class=&quot;na&quot;&gt;mi&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;
  &lt;span class=&quot;na&quot;&gt;game&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;
    &lt;span class=&quot;na&quot;&gt;index&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;
      &lt;span class=&quot;na&quot;&gt;heading&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;Katoa Kēmu&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;p&gt;– the keys for the translation never change, just the content, typically with one file being added for each new language.&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;http://latter.3months.com&quot;&gt;Latter&lt;/a&gt; is the first Rails app that I’ve added full support for translations to. Māori was a really important language for me to add, as I feel that it’s a key part of the New Zealand identity and culture. Conveniently, it also tied in with Māori Language Week.&lt;/p&gt;

&lt;p&gt;Overall, the translation project took about a week’s worth of evenings and parts of the weekend - maybe about 15 hours all told, but now that the initial setup work is complete, adding additional languages is simply a matter of translating the strings already present. This process could have been sped up further, had Rails had build-in support for Māori - while the &lt;a href=&quot;https://github.com/svenfuchs/rails-i18n&quot;&gt;rails-i18n&lt;/a&gt; project has translations for many languages, the absence of Māori meant that I had to provide my own translations for some areas. This is an area where I see a key opportunity to add I18n translations for Māori to Rails, however I don’t feel that my Māori language skills are adequate enough to provide a quality contribution.&lt;/p&gt;

&lt;p&gt;Internationalization is an often overlooked part of most web sites and applications, especially in New Zealand, where Te Reo Māori is one of our three official languages. It’s worth noting that only a fraction of the non-government websites and applications I have seen that are intended for a New Zealand audience offer Māori as an alternative language, which is really something I’d like to see in the future.&lt;/p&gt;

&lt;p&gt;I hope that this post has explained the translation process clearly, and perhaps may even help drive more internationalization efforts with Rails in the future.&lt;/p&gt;

</description>
        <pubDate>Mon, 30 Jul 2012 00:00:00 +1200</pubDate>
        <link>https://joshmcarthur.com/2012/07/30/whakamori-te-latter.html</link>
        <guid isPermaLink="true">https://joshmcarthur.com/2012/07/30/whakamori-te-latter.html</guid>
        
        
      </item>
    
      <item>
        <title>Compiling with Homebrew without XCode</title>
        <description>&lt;p&gt;I’m a big fan of not installing XCode on new Macs. I just don’t think it’s necessary, unless you’re genuinely building OS X or iOS applications.&lt;/p&gt;

&lt;p&gt;Instead, I prefer to download the [Apple Command Line Tools] straight from &lt;a href=&quot;https://developer.apple.com/downloads/index.action?=command%20line%20tools&quot;&gt;Apple’s Developer Center&lt;/a&gt;, and install everything else I need to with &lt;a href=&quot;http://mxcl.github.com/homebrew/&quot;&gt;Homebrew&lt;/a&gt;. Here’s how to do that.&lt;/p&gt;

&lt;p&gt;First of all, download and install those command line tools. You’re going to need those first of all.&lt;/p&gt;

&lt;p&gt;Next, install Homebrew. There are instructions on the site, but the quick version is:&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-bash&quot; data-lang=&quot;bash&quot;&gt;/usr/bin/ruby &lt;span class=&quot;nt&quot;&gt;-e&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;si&quot;&gt;$(&lt;/span&gt;/usr/bin/curl &lt;span class=&quot;nt&quot;&gt;-fsSL&lt;/span&gt; https://raw.github.com/mxcl/homebrew/master/Library/Contributions/install_homebrew.rb&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;/figure&gt;

&lt;p&gt;Once homebrew install installed, you need to install another version of &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;gcc&lt;/code&gt; (A compiler). There is a version of this that already comes with OS X, but it’s pretty old.&lt;/p&gt;

&lt;p&gt;Enable the dupes homebrew package, and install an up-to-date version of GCC:&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-bash&quot; data-lang=&quot;bash&quot;&gt;brew tap homebrew/dupes
brew &lt;span class=&quot;nb&quot;&gt;install &lt;/span&gt;apple-gcc42&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;p&gt;Theoretically, you now have the compilers, utilities, and tools you need to set up a range of packages on your Mac. One last problem that I’ve run into installing packages such as &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;zsh&lt;/code&gt; and &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;node&lt;/code&gt;, however, are programs that are run from the compilers to try and find XCode - and fail when they find it is not installed. The error looks something like this:&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-bash&quot; data-lang=&quot;bash&quot;&gt;xcode-select: Error: No Xcode is selected. Use xcode-select &lt;span class=&quot;nt&quot;&gt;-switch&lt;/span&gt; &amp;lt;path-to-xcode&amp;gt;, or see the xcode-select manpage &lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;man xcode-select&lt;span class=&quot;o&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;for &lt;/span&gt;further information.&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;p&gt;The solution for this is to tell these tools that Xcode is ‘installed’ in &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;/usr/bin&lt;/code&gt; - where all the compilers are installed:&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-bash&quot; data-lang=&quot;bash&quot;&gt;&lt;span class=&quot;nb&quot;&gt;sudo &lt;/span&gt;xcode-select &lt;span class=&quot;nt&quot;&gt;-switch&lt;/span&gt; /usr/bin&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;p&gt;This stops xcode-select from erroring out and halting the compile process, by ensuring that it tries to find the compilers that it requires by looking in the directory where they are already installed.&lt;/p&gt;

&lt;p&gt;Once all these steps are completed, you should have a full set of developer tools installed - all you need to compile almost all of the many Homebrew recipes that are available.&lt;/p&gt;

&lt;hr /&gt;

&lt;p&gt;&lt;em&gt;Note: The original instructions for setting up command-line tools and gcc-4.2 come from: &lt;a href=&quot;http://robots.thoughtbot.com/post/27985816073/the-hitchhikers-guide-to-riding-a-mountain-lion&quot;&gt;http://robots.thoughtbot.com/post/27985816073/the-hitchhikers-guide-to-riding-a-mountain-lion&lt;/a&gt;. The &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;xcode-select&lt;/code&gt; fix comes from &lt;a href=&quot;https://github.com/mxcl/homebrew/issues/10245&quot;&gt;this issue report&lt;/a&gt; on the Homebrew repository&lt;/em&gt;&lt;/p&gt;

</description>
        <pubDate>Sun, 29 Jul 2012 00:00:00 +1200</pubDate>
        <link>https://joshmcarthur.com/2012/07/29/compiling-with-homebrew-without-xcode.html</link>
        <guid isPermaLink="true">https://joshmcarthur.com/2012/07/29/compiling-with-homebrew-without-xcode.html</guid>
        
        
      </item>
    
      <item>
        <title>Sublime Text 2: Do not reopen files</title>
        <description>&lt;p&gt;For quite some time now, I’ve been using &lt;a href=&quot;https://github.com/alloy/macvim&quot;&gt;alloy’s fork of Macvim&lt;/a&gt; as my primary editor, along with &lt;a href=&quot;https://github.com/carlhuda/janus&quot;&gt;janus&lt;/a&gt;, and it’s been working out really well.&lt;/p&gt;

&lt;p&gt;I’ve just started trying out Sublime Text 2 though, and it’s been pretty nice (although I still have reservations). Something I &lt;strong&gt;can’t stand&lt;/strong&gt; in a developer application though, is for an application to re-open all the files you had open last time the application was used. Sure enough, this is something that Sublime Text 2 does.&lt;/p&gt;

&lt;p&gt;To turn it off, you can change the following settings in your user preferences file. To open these preferences, open Sublime, click the application menu, and then go to ‘Preferences’ -&amp;gt; ‘Settings - User’, and add the following keys:&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-javascript&quot; data-lang=&quot;javascript&quot;&gt;&lt;span class=&quot;dl&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;hot_exit&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;kc&quot;&gt;false&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;remember_open_files&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;kc&quot;&gt;false&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;p&gt;If you haven’t already added any user settings, these two lines will need to be surrounded by curly braces - &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;{&lt;/code&gt; and &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;}&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;I don’t understand why any application needs to re-open files - if I close an application, that means I don’t need it anymore. If I needed it again, I would just leave it open, or suspend my computer and come back to it later.&lt;/p&gt;

&lt;p&gt;Hopefully these settings make things a bit easier on somebody else though - I couldn’t find this documented anywhere on the internet from a quick Google, so had to jot it down here.&lt;/p&gt;
</description>
        <pubDate>Thu, 19 Jul 2012 00:00:00 +1200</pubDate>
        <link>https://joshmcarthur.com/2012/07/19/sublime-text-2-do-not-reopen-files.html</link>
        <guid isPermaLink="true">https://joshmcarthur.com/2012/07/19/sublime-text-2-do-not-reopen-files.html</guid>
        
        
      </item>
    
      <item>
        <title>Document your code with Tomdoc</title>
        <description>&lt;p&gt;Documentation in Ruby on Rails apps tends to be somewhat of a hit-and-miss affair, particularly within non-product organizations. In this blog post, I’ll run through how &lt;a href=&quot;http://tomdoc.org&quot;&gt;Tomdoc&lt;/a&gt; has helped my code become clearer and easier to maintain, with very little overhead.&lt;/p&gt;

&lt;hr /&gt;

&lt;p&gt;First, a small explanation about Tomdoc. It is effectively a spec that dictates a way of writing inline documentation in comment blocks, in a similar way to ri and rdoc, but without some of the overhead. It was created by &lt;a href=&quot;http://tom.preston-werner.com/&quot;&gt;Tom Preston-Werner&lt;/a&gt; of &lt;a href=&quot;https://github.com&quot;&gt;Github&lt;/a&gt; fame, who felt that tools such as RDoc required too much verbosity and effect to properly document code.&lt;/p&gt;

&lt;p&gt;Tomdoc has been written specifically to be read by humans, unlike other documentation systems that are generally optimized for machine parsing and formatted display. Tomdoc makes the very sensible assumption that in most cases, the person reading the documentation will be looking at the code, and so removes the need for any special kind of markup.&lt;/p&gt;

&lt;p&gt;The &lt;a href=&quot;http://tomdoc.org&quot;&gt;Tomdoc&lt;/a&gt; spec has a detailed example of what a Tomdoc documentation section may look like, so I’ll skip over the big example, and instead drill down to how I use it to write more maintainable code.&lt;/p&gt;

&lt;p&gt;Effectively, I use Tomdoc the same way as I would an RSpec integration test - within my documentatation, I not only explain the API of the method, but also the context of how and where this method is used - for example:&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-ruby&quot; data-lang=&quot;ruby&quot;&gt;  &lt;span class=&quot;c1&quot;&gt;# Public - Record that this deal has been viewed by a user&lt;/span&gt;
  &lt;span class=&quot;c1&quot;&gt;#&lt;/span&gt;
  &lt;span class=&quot;c1&quot;&gt;# This method facilitates a given user having &apos;read&apos; and &apos;unread&apos;&lt;/span&gt;
  &lt;span class=&quot;c1&quot;&gt;# deals. When a deal is displayed to the user, it should call this method,&lt;/span&gt;
  &lt;span class=&quot;c1&quot;&gt;# which will add a DealView record for the card and user id, if one&lt;/span&gt;
  &lt;span class=&quot;c1&quot;&gt;# does not already exist.&lt;/span&gt;
  &lt;span class=&quot;c1&quot;&gt;#&lt;/span&gt;
  &lt;span class=&quot;c1&quot;&gt;# user - The user who viewed this deal&lt;/span&gt;
  &lt;span class=&quot;c1&quot;&gt;#&lt;/span&gt;
  &lt;span class=&quot;c1&quot;&gt;# Returns the found or created dealview&lt;/span&gt;
  &lt;span class=&quot;k&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;viewed_by!&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;no&quot;&gt;DealView&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;find_or_create_by_deal_id_and_user_id&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;ss&quot;&gt;:deal_id&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&amp;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;nf&quot;&gt;id&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;ss&quot;&gt;:user_id&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;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;id&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;/figure&gt;

&lt;p&gt;Within this method, I not only define the visibility (Public) of the method, and provide a short description of what the method does, but I also describe briefly where and  how the method is used. I then go on to define the parameters that the method accepts, and what the method returns.&lt;/p&gt;

&lt;p&gt;By commenting my code in this format, I achieve benefits in number of areas:&lt;/p&gt;

&lt;ol&gt;
  &lt;li&gt;It becomes easier for new developers to understand the relationships between different areas of code&lt;/li&gt;
  &lt;li&gt;The documentation forms a contract which future changes to the method should conform to&lt;/li&gt;
  &lt;li&gt;Writing the documentation forces me to think about the best way to code the method, and how it may need to interact with other areas of the application&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Overall, I’ve noticed that I’ve had a much better understanding of how components of applications I’ve been working on interact since I started documenting the code I write, and have been able to pass on this understanding to colleagues and others - that for me, indicates that this type of documentation works really well with an opinionated  framework like Rails, and a untyped language like Ruby, providing a flexible specification to build system knowledge without getting in the way or slowing things down.&lt;/p&gt;

&lt;hr /&gt;

&lt;blockquote&gt;
  &lt;p&gt;Just as a sidenote, I’ve also forked and made some minor improvements to an existing fork of tomdoc, that adds the ability to generate formatted documentation from a source tree. My change adds the ability to pass in a folder to parse documentation from, and adds nicer formatting and navigation controls. You can see my fork at &lt;a href=&quot;https://github.com/joshmcarthur/tomdoc&quot;&gt;https://github.com/joshmcarthur/tomdoc&lt;/a&gt;&lt;/p&gt;
&lt;/blockquote&gt;

</description>
        <pubDate>Mon, 25 Jun 2012 00:00:00 +1200</pubDate>
        <link>https://joshmcarthur.com/2012/06/25/document-your-code-with-tomdoc.html</link>
        <guid isPermaLink="true">https://joshmcarthur.com/2012/06/25/document-your-code-with-tomdoc.html</guid>
        
        
      </item>
    
      <item>
        <title>Import Trello cards from a CSV file</title>
        <description>&lt;p&gt;This morning I wrote a quick script that I found quite handy - it takes a CSV file, and adds cards to Trello from it’s contents. At &lt;a href=&quot;http://3months.com&quot;&gt;3months&lt;/a&gt;, we quite regularly get long spreadsheets with requirements, so this script saves us a lot of time when we need to import these lists into cards so we can estimate and expand on them. Now, with a bit of reformatting, replacing proper quotes with double quotes, etc., I can just import in a couple of minutes.&lt;/p&gt;

&lt;script src=&quot;https://gist.github.com/2839352.js?file=trello-cards-from-csv.rb&quot;&gt;&lt;/script&gt;

&lt;p&gt;I’ve documented the script pretty well, but basically it uses the ruby-trello gem along with Foreman (for managing quite a few ENV variables that get passed around) to use an existing Trello OAuth authorization to add cards to a board and label them (since you may want to apply a ‘from-spreadsheet’ label or something like that). It probably won’t handle special characters wonderfully, but the main thing is just to make sure that your CSV is a valid CSV file - escaped quotes, quoted columns etc.&lt;/p&gt;

&lt;p&gt;I hope somebody else may find this useful!&lt;/p&gt;
</description>
        <pubDate>Thu, 31 May 2012 00:00:00 +1200</pubDate>
        <link>https://joshmcarthur.com/2012/05/31/import-trello-cards-from-a-csv-file.html</link>
        <guid isPermaLink="true">https://joshmcarthur.com/2012/05/31/import-trello-cards-from-a-csv-file.html</guid>
        
        <category>code</category>
        
        <category>open source</category>
        
        
      </item>
    
      <item>
        <title>Jekyll Content Liquid Tags</title>
        <description>&lt;p&gt;For the last week after deploying my new blog to &lt;a href=&quot;http://joshmcarthur.com&quot;&gt;joshmcarthur.com&lt;/a&gt; instead of a ‘blog’ subdomain, and implementing a new design and back-to-basics blogging engine, I’ve noticed that my markdown wasn’t being parsed properly (/at all).&lt;/p&gt;

&lt;p&gt;Originally this was hard to spot - my posts had been migrated from Blogger, to Tumblr, out of Tumblr, transformed from HTML to Markdown, imported into an &lt;a href=&quot;http://www.octopres.org&quot;&gt;Octopress&lt;/a&gt; blog, and then transferred to this blog. After this process, most of them weren’t in very good shape anyway, but it was still hard to spot because my embedded &lt;a href=&quot;http://gist.github.com&quot;&gt;gists&lt;/a&gt; were still being rendered - in fact anywhere where I had used actual HTML markup was fine.&lt;/p&gt;

&lt;p&gt;After transferring markdown engines, a lot of Google and Stackoverflow searching and all-round frustration, I noticed that Jekyll mentioned two Liquid tags that could be used to interpolate content into a page:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;content&lt;/code&gt;, and&lt;/li&gt;
  &lt;li&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;page.content&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;I had considered these to be the same - one was just a namespaced one, like a convenience method. I was actually wrong about this, as they perform two slightly different functions:&lt;/p&gt;

&lt;p&gt;The &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;content&lt;/code&gt; tag outputs the contents of the file being built, and also processes the contents of that file - i.e. if it is a Markdown file, it parses the Markdown, if it is Textile, it parses the Textile, etc. etc. It also scans the output for other Liquid tags and does any further processing needed for these.&lt;/p&gt;

&lt;p&gt;The &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;page.content&lt;/code&gt; tag, on the other hand, just outputs whatever the content is. It’s basically just the first part of the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;content&lt;/code&gt; tag - the file reading bit, without any of the processing.&lt;/p&gt;

&lt;p&gt;I had been using &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;page.content&lt;/code&gt; in my layouts, as I liked the idea of the namespaced tag better - as soon as I changed this to use &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;content&lt;/code&gt; everything worked. I just wish it hadn’t taken me a week to sit down and figure that out, instead of blaming everything else but me!&lt;/p&gt;
</description>
        <pubDate>Fri, 18 May 2012 00:00:00 +1200</pubDate>
        <link>https://joshmcarthur.com/2012/05/18/jekyll-content-liquid-tags.html</link>
        <guid isPermaLink="true">https://joshmcarthur.com/2012/05/18/jekyll-content-liquid-tags.html</guid>
        
        
      </item>
    
      <item>
        <title>Bootstrap Tablesorter Styles</title>
        <description>&lt;p&gt;When &lt;a href=&quot;http://twitter.github.com/bootstrap&quot;&gt;Twitter Bootstrap&lt;/a&gt; first shipped, it came with a couple of handy integrations with external jQuery plugins. Once of the more popular of these integrations was styles that were compatible with the classes, elements and attributes added by &lt;a href=&quot;http://tablesorter.com/docs/&quot;&gt;jQuery Tablesorter&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Recently, however, these integrations have been removed from Bootstrap, as it was felt that they were distracting from the core of what the project was trying to achieve, and that as most plugins were themeable anyway, users of Bootstrap could just build a theme.&lt;/p&gt;

&lt;p&gt;This is all well and good, but sometimes it’s just a quick fix that is necessary. Here’s the CSS needed to provide Bootstrap look-and-feel in the table sorter plugin:&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-css&quot; data-lang=&quot;css&quot;&gt;&lt;span class=&quot;nt&quot;&gt;table&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;.header&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
	&lt;span class=&quot;nl&quot;&gt;cursor&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;pointer&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;table&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;.header&lt;/span&gt;&lt;span class=&quot;nd&quot;&gt;:after&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;nl&quot;&gt;content&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&quot;&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;nl&quot;&gt;float&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;right&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;nl&quot;&gt;margin-top&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;7px&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;nl&quot;&gt;border-width&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;m&quot;&gt;4px&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;4px&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;nl&quot;&gt;border-style&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;solid&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;nl&quot;&gt;border-color&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;#000000&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;transparent&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;nl&quot;&gt;visibility&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;hidden&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;table&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;.headerSortUp&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;table&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;.headerSortDown&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;nl&quot;&gt;background-color&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;#f7f7f9&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;nl&quot;&gt;text-shadow&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;m&quot;&gt;1px&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;1px&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;255&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;255&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;255&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;0.75&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;table&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;.header&lt;/span&gt;&lt;span class=&quot;nd&quot;&gt;:hover:after&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;nl&quot;&gt;visibility&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;visible&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;table&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;.headerSortDown&lt;/span&gt;&lt;span class=&quot;nd&quot;&gt;:after&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;table&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;.headerSortDown&lt;/span&gt;&lt;span class=&quot;nd&quot;&gt;:hover:after&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;nl&quot;&gt;visibility&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;visible&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;nl&quot;&gt;filter&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;alpha&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;opacity&lt;/span&gt;&lt;span class=&quot;err&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;m&quot;&gt;60&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
  &lt;span class=&quot;nl&quot;&gt;-moz-opacity&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;0.6&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;nl&quot;&gt;opacity&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;0.6&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;table&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;.headerSortUp&lt;/span&gt;&lt;span class=&quot;nd&quot;&gt;:after&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;nl&quot;&gt;border-bottom&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;none&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;nl&quot;&gt;border-left&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;4px&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;solid&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;transparent&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;nl&quot;&gt;border-right&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;4px&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;solid&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;transparent&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;nl&quot;&gt;border-top&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;4px&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;solid&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;#000000&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;nl&quot;&gt;visibility&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;visible&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;nl&quot;&gt;-webkit-box-shadow&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;none&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;nl&quot;&gt;-moz-box-shadow&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;none&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;nl&quot;&gt;box-shadow&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;none&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;nl&quot;&gt;filter&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;alpha&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;opacity&lt;/span&gt;&lt;span class=&quot;err&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;m&quot;&gt;60&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
  &lt;span class=&quot;nl&quot;&gt;-moz-opacity&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;0.6&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;nl&quot;&gt;opacity&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;0.6&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;/figure&gt;

&lt;p&gt;Add this stylesheet into your project to add the tablesorter integrations from Bootstrap 1, to Bootstrap 2. It doesn’t mess with the default table styles or anything else, but does make the tablesorter feel much more integrated.&lt;/p&gt;

&lt;p&gt;(The original discussion on this issue is from: &lt;a href=&quot;https://groups.google.com/forum/?fromgroups#!topic/twitter-bootstrap/NP8gnUEeUrY&quot;&gt;this Google Group thread&lt;/a&gt;)&lt;/p&gt;
</description>
        <pubDate>Mon, 14 May 2012 00:00:00 +1200</pubDate>
        <link>https://joshmcarthur.com/2012/05/14/bootstrap-tablesorter-styles.html</link>
        <guid isPermaLink="true">https://joshmcarthur.com/2012/05/14/bootstrap-tablesorter-styles.html</guid>
        
        
      </item>
    
      <item>
        <title>Git-browse for Quick Repo Viewing</title>
        <description>&lt;p&gt;Last night I quickly patched together a command called &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;git-browse&lt;/code&gt; - it’s a small but handy extension to &lt;a href=&quot;http://git-scm.com/&quot;&gt;git&lt;/a&gt; that looks at the remotes you have set up inside a repository, and opens up the first Github repository it finds - to give an example:&lt;/p&gt;

&lt;pre&gt;
-&amp;gt; git remote -v
origin git@github.com:joshmcarthur/WriteIdeally.git (fetch)
origin git@github.com:joshmcarthur/WriteIdeally.git (push)
heroku git@heroku.com:writeideally.git (fetch)
heroku git@heroku.com:writeideally.git (push)
-&amp;gt; git browse
(Opens https://github.com/joshmcarthur/WriteIdeally in default browser)
&lt;/pre&gt;
&lt;p&gt;l
The source is just below - at the moment, though it’s only compatible with OS X and Linux - Ubuntu that I know of, but I believe that RedHat won’t work without installing packages - see &lt;a href=&quot;http://stackoverflow.com/questions/5116473/linux-command-to-open-url-in-default-browser&quot;&gt;this StackOverflow thread&lt;/a&gt;&lt;/p&gt;

&lt;script src=&quot;https://gist.github.com/1926673.js?file=git-browse&quot;&gt;&lt;/script&gt;

&lt;p&gt;Installation is pretty basic - just download, make it executable by running &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;chmod +x git-browse&lt;/code&gt;, and then copy it somewhere that is referenced by your &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;$PATH&lt;/code&gt; variable (you can see what’s in this variable by running &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;echo $PATH&lt;/code&gt; in a Terminal). Once you’ve done this, you should be able to enter any repository with Github remotes, and run &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;git browse&lt;/code&gt; to have the repository open in your default browser.&lt;/p&gt;

&lt;p&gt;Extensions to this idea should be pretty easy - something I would be keen to see in a pull request, for example, would be to support opening arbitrary files - i.e. &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;git browse lib/writeideally/api.rb&lt;/code&gt; would open &lt;a href=&quot;https://github.com/joshmcarthur/WriteIdeally/blob/master/lib/writeideally/api.rb&quot;&gt;https://github.com/joshmcarthur/WriteIdeally/blob/master/lib/writeideally/api.rb&lt;/a&gt;. Having said that, my gsubbing is pretty gross, so that needs refactoring as well.&lt;/p&gt;

&lt;p&gt;In any case, enjoy!&lt;/p&gt;
</description>
        <pubDate>Tue, 28 Feb 2012 08:50:00 +1300</pubDate>
        <link>https://joshmcarthur.com/2012/02/28/git-browse-for-quick-repo-viewing.html</link>
        <guid isPermaLink="true">https://joshmcarthur.com/2012/02/28/git-browse-for-quick-repo-viewing.html</guid>
        
        
      </item>
    
      <item>
        <title>Treatme Lite: Part One - the Backend</title>
        <description>&lt;h2 id=&quot;the-backend&quot;&gt;The Backend&lt;/h2&gt;
&lt;blockquote&gt;
  &lt;p&gt;This article first is in a series of three blog posts documenting how I created &lt;a href=&quot;http://treatmelite.herokuapp.com&quot;&gt;TreatMe Lite&lt;/a&gt;, an HTMl5 web app using Zepto.js, Coffeescript, and a range of other frameworks and tools, all backed by an &lt;a href=&quot;http://expressjs.org&quot;&gt;Express JS&lt;/a&gt; web service. See my &lt;a href=&quot;/2012/02/06/treatme-lite-adventures-in-javascript/&quot;&gt;previous post&lt;/a&gt; for more information about the application.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Last post (the intro), I left off explaining why I set out the build the application, and the technologies I had selected to build each part. This post is going to detail the steps I took to create the web service using &lt;a href=&quot;http://nodejs.org&quot;&gt;NodeJS&lt;/a&gt; and &lt;a href=&quot;http://expressjs.org&quot;&gt;Express&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;First of all, I needed a way to serve the deal JSON. In actuality, this is something that I could have done just as easily directly from the front-end Javascript, however, I’d started off with a NodeJS application, and it seemed just as sensible to request the JSON from &lt;a href=&quot;http://treatme.co.nz&quot;&gt;TreatMe&lt;/a&gt; using Node’s &lt;a href=&quot;http://nodejs.org/docs/latest/api/http.html#http.get&quot;&gt;support for HTTP requests&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;To kick off the node app, I cloned a starter application from Github:&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;git clone git://github.com/twilson63/express-coffee.git treatmelite
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;This application gives us an easy base with support for &lt;a href=&quot;http://coffeescript.org&quot;&gt;Coffeescript&lt;/a&gt;, &lt;a href=&quot;http://jade-lang.com/&quot;&gt;Jade templating&lt;/a&gt; and a bunch of other CSS stuff, like the &lt;a href=&quot;http://getskeleton.com&quot;&gt;Skeleton grid&lt;/a&gt; (which I’m a big fan of).&lt;/p&gt;

&lt;p&gt;To satisfy the requirements for the front-end, my NodeJS application needed to perform two tasks:&lt;/p&gt;

&lt;h4 id=&quot;dealslatitudelongitude-retrieve-a-list-of-nearby-deals-from-treatme&quot;&gt;/deals/:latitude/:longitude: Retrieve a list of nearby deals from TreatMe&lt;/h4&gt;

&lt;p&gt;The code:&lt;/p&gt;

&lt;script src=&quot;https://gist.github.com/1766753.js?file=app.coffee&quot;&gt;&lt;/script&gt;

&lt;p&gt;Explanation:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;First, we make the HTTP request, passing a callback that will get a response object as an argument&lt;/li&gt;
  &lt;li&gt;We listen for certain events while we receive the data from the response - if we hit an error, we return an error message. We listen for when we receive some data, and append it to the string we want to return to the front-end, and when we reach the end of the data, we return this string.&lt;/li&gt;
&lt;/ul&gt;

&lt;h4 id=&quot;geocode-geocode-an-address-and-pass-back-the-information-we-need-to-display-to-the-user&quot;&gt;/geocode: Geocode an address, and pass back the information we need to display to the user&lt;/h4&gt;

&lt;p&gt;The Code:&lt;/p&gt;

&lt;script src=&quot;https://gist.github.com/1766776.js?file=app.coffee&quot;&gt;&lt;/script&gt;

&lt;p&gt;Explanation:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;We build the path to the Google Geocoder using either the latitude and longitude from the user’s current location (i.e. reverse geocoding), or the address (typically a city name like ‘Wellington, New Zealand’)&lt;/li&gt;
  &lt;li&gt;We follow a similar pattern to the Deals action, requesting a geocoding result, waiting for a response, and parsing the data we need out of that response. In this case, we build a JSON structure containing the geocoded address, it’s latitude, and it’s longitude.&lt;/li&gt;
&lt;/ul&gt;

&lt;blockquote&gt;
  &lt;p&gt;A note on Geocoding: We use &lt;a href=&quot;http:// code.google.com/apis/maps/documentation/geocoding/&quot;&gt;Google’s Geocoding service&lt;/a&gt; to retrieve the information we need from either the name of a location, or the latitude and longitude (either way, it fills in the information we need). Once again, this is something we ideally would do from the front-end, however, I ran into some cross domain issues, and so simplified matters by performing the request on the server&lt;/p&gt;
&lt;/blockquote&gt;

</description>
        <pubDate>Wed, 08 Feb 2012 21:07:00 +1300</pubDate>
        <link>https://joshmcarthur.com/nodejs/coffeescript/javascript/backend/2012/02/08/treatme-lite-part-one-the-backend.html</link>
        <guid isPermaLink="true">https://joshmcarthur.com/nodejs/coffeescript/javascript/backend/2012/02/08/treatme-lite-part-one-the-backend.html</guid>
        
        
        <category>nodejs</category>
        
        <category>coffeescript</category>
        
        <category>javascript</category>
        
        <category>backend</category>
        
      </item>
    
      <item>
        <title>Treatme Lite: Adventures in Javascript</title>
        <description>&lt;p&gt;For a long time, I’ve been looking to move into the &lt;a href=&quot;http://nodejs.org&quot;&gt;NodeJS&lt;/a&gt; trend that’s been taking the development community by storm. The problem is that coming from a Rails development background, and my inability to follow callbacks through more than 2 levels, has lead to my previous efforts to end in broken code and frustration.&lt;/p&gt;

&lt;p&gt;Happily, however, I took another look at &lt;a href=&quot;http://expressjs.com&quot;&gt;express&lt;/a&gt;, a Sinatra-like framework for NodeJS, and it clicked with me on a level that vanilla Node had not in the past. It supported middleware, was reasonably well-documented, and I was able to find &lt;a href=&quot;https://github.com/twilson63/express-coffee&quot;&gt;a handy project on Github&lt;/a&gt; which allowed me to start from a solid base of proven middleware and NodeJS modules to help me out (I typically now develop in &lt;a href=&quot;http://coffeescript.org&quot;&gt;Coffeescript&lt;/a&gt; for my personal projects - I enjoy the ability to code in something that takes my missing knowledge of how to write beautiful Javascript into account).&lt;/p&gt;

&lt;p&gt;Next, I needed something to build - ideally something I could become reasonably passionate about finishing, but that also would not consume more than a week or so of working in the evenings. Something I’ve been using recently is &lt;a href=&quot;http://treatme.co.nz/now&quot;&gt;TreatMe Now&lt;/a&gt; - a sort of customers-on-demand voucher system for retailers that is updated throughout the day with some really good deals - typically, these last only a couple of hours though, so it’s important to check quite often. Something else I had lamented is that, while there is an iPhone app available, there was no mobile web support, no support for other devices, and the normal web interface is chock full of data-heavy maps and unfriendly panels that are, frankly, downright annoying to use on a smartphone.&lt;/p&gt;

&lt;p&gt;Using Chrome’s web inspector, I was able to find a JSON feed of deals, and work out how to filter the results to a particular region. Given this datasource, I was ready to begin building out this experiment, using a number of resources I’ve wanted to try out in more detail - NodeJS, Express, more in-depth HTML5 things, responsive design, and an adaptive grid system (Normally I use &lt;a href=&quot;https://twitter.github.com/bootstrap&quot;&gt;Twitter Bootstrap&lt;/a&gt;, but I made a conscious effort to keep things light, using Skeleton’s adaptive grid system, and Zepto.js in place of jQuery). This blog post is going to detail how I went about building backend and fronted of the application, to the point where I’ve got &lt;a href=&quot;http://treatmelite.herokuapp.com&quot;&gt;The Final Product&lt;/a&gt;, an express-backed single-page application supporting offline access, local storage and a fully responsive design.&lt;/p&gt;

&lt;p&gt;Over the next three posts, I’m going to be documenting how I went about building a responsive and HTML5-based web application to serve TreatMe Deals, using NodeJS, Express, ZeptoJS,  Coffeescript and the Skeleton CSS framework, including difficulties I ran into, and how I solved them:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;Part One: The Backend - an examination of my basic web service written in &lt;a href=&quot;http://expressjs.org&quot;&gt;Express&lt;/a&gt; which serves a JSON datasource of deals, and makes calls to Google’s Geocoding service&lt;/li&gt;
  &lt;li&gt;Part Two: The Frontend - a look at the Javascript and CSS tools I used to develop the frontend of the application&lt;/li&gt;
  &lt;li&gt;Part Three: Responsiveness and Extras - some detail about how I added support for offline asset caching and local storage of deals, and made additional tweaks to make the application work fully offline.&lt;/li&gt;
&lt;/ul&gt;

</description>
        <pubDate>Mon, 06 Feb 2012 21:01:00 +1300</pubDate>
        <link>https://joshmcarthur.com/2012/02/06/treatme-lite-adventures-in-javascript.html</link>
        <guid isPermaLink="true">https://joshmcarthur.com/2012/02/06/treatme-lite-adventures-in-javascript.html</guid>
        
        
      </item>
    
      <item>
        <title>Upcoming: RateMyCourses</title>
        <description>&lt;p&gt;Tonight I’d like to talk about a project I’ve been working on in my spare time for the
last few months. I’m super excited to see it coming together, and it’s &lt;em&gt;nearly&lt;/em&gt; ready to
go live.&lt;/p&gt;

&lt;p&gt;The application is called &lt;strong&gt;RateMyCourses&lt;/strong&gt; - put simply, it’s a way for University students
to explore, rate, and comment on courses they’ve done to help out others. Inspired by
such projects as &lt;a href=&quot;http://www.fixmystreet.com&quot;&gt;Fix my Street&lt;/a&gt; and &lt;a href=&quot;http://www.fixmytransport.com&quot;&gt;Fix my Transport&lt;/a&gt;, &lt;strong&gt;RateMyCourses&lt;/strong&gt; aims to increase the transparency of quality courses, lecturers, and tertiary education institutes within New Zealand. Over the last few months I’ve been not only building up this application, but also generating the dataset of courses thats at the heart of the application from scratch, pulling course details from all major Universities within New Zealand, including:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;Auckland University&lt;/li&gt;
  &lt;li&gt;Massey University&lt;/li&gt;
  &lt;li&gt;Waikato University&lt;/li&gt;
  &lt;li&gt;Victoria University of Wellington&lt;/li&gt;
  &lt;li&gt;University of Canterbury&lt;/li&gt;
  &lt;li&gt;Lincoln University&lt;/li&gt;
  &lt;li&gt;Otago University&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;All up, I’ve got nearly 20,000 courses indexed, from these seven institutions. This is a huge opportunity for students to really engage with each other, with their Universities, and with new entrants. Over time, I’m hoping &lt;strong&gt;RateMyCourses&lt;/strong&gt; will become a valuable tool for course planning, as it uncovers the best courses, institutions, and educators in New Zealand (and, eventually, other countries).&lt;/p&gt;

&lt;p&gt;This is just a preliminary post while I polish up some areas, and make sure I’m really happy with things, but I welcome feedback and comments as I move into the launch and adoption phases. I’m planning to launch in the upcoming weeks, and spend the summer with a core group of users ironing out the major issues, before really pushing it at Victoria next year.&lt;/p&gt;
</description>
        <pubDate>Mon, 14 Nov 2011 20:16:00 +1300</pubDate>
        <link>https://joshmcarthur.com/2011/11/14/upcoming-ratemycourses.html</link>
        <guid isPermaLink="true">https://joshmcarthur.com/2011/11/14/upcoming-ratemycourses.html</guid>
        
        
      </item>
    
      <item>
        <title>Add jQuery to Any Page Really, Really Easily</title>
        <description>&lt;p&gt;When I’m working on a site, or analysing somebody else’s, I often wish that jQuery was loaded into that page - it’s an unbeatable tool for really digging into the site’s source to debug something or work out how something has been done.&lt;/p&gt;

&lt;p&gt;To help out with this, I’ve put together a quick bookmarket, &lt;a href=&quot;http://joshmcarthur.com/2011/10/20/a-fun-little-bookmarklet/&quot;&gt;similar to the more fun one I’ve done a while ago&lt;/a&gt;. It works extremely simply - it just creates a script element, adds jQuery 1.7.0 (served from Google), and appends it to the page body. As soon as the script has loaded, jQuery can be used from within Web Inspector, Firebug, or whatever other Javascript console you use.&lt;/p&gt;

&lt;p&gt;Here’s the bookmarklet:&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;javascript:var s=document.createElement(&apos;script&apos;);s.type=&apos;text/javascript&apos;;s.src=&apos;https://ajax.googleapis.com/ajax/libs/jquery/1.7.0/jquery.min.js&apos;;document.getElementsByTagName(&apos;body&apos;)[0].appendChild(s);&quot;&gt;Add jQuery&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;To add this to your browser, you can either: right-click on this link, select ‘Bookmark Link’, or just drag-and-drop onto your Bookmarks toolbar. Once it’s added, you can simply click on the bookmark on any page, and jQuery will be loaded into the page for you to use.&lt;/p&gt;

&lt;p&gt;Here’s the source:&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-javascript&quot; data-lang=&quot;javascript&quot;&gt;&lt;span class=&quot;c1&quot;&gt;// Create the script tag&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;createElement&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&apos;&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;script&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&apos;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;

&lt;span class=&quot;c1&quot;&gt;// Set the necessary attributes on the tag&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;type&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;text/javascript&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;s&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;https://ajax.googleapis.com/ajax/libs/jquery/1.7.0/jquery.min.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;c1&quot;&gt;// Add to the body tag (assume here we&apos;re dealing with HTML, and there IS a body tag)&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;&apos;&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;body&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&apos;&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;s&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

</description>
        <pubDate>Sat, 12 Nov 2011 21:27:00 +1300</pubDate>
        <link>https://joshmcarthur.com/howto,/nerdy/2011/11/12/add-jquery-to-any-page.html</link>
        <guid isPermaLink="true">https://joshmcarthur.com/howto,/nerdy/2011/11/12/add-jquery-to-any-page.html</guid>
        
        <category>javascript</category>
        
        <category>useful</category>
        
        <category>jquery</category>
        
        
        <category>howto,</category>
        
        <category>nerdy</category>
        
      </item>
    
      <item>
        <title>Generating &apos;Gem Install&apos; Commands From &apos;Gem List&apos;</title>
        <description>&lt;p&gt;Here at &lt;a href=&quot;http://3months.com&quot;&gt;3Months&lt;/a&gt;, we have a couple of monolithic projects that have been around for yonks - because of this, they don’t have bundler set up, and many of them have incomplete or out-of-date gem requirements. Yesterday I needed to get one of these projects set up locally for the first time, so I was kindly lent the output of &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;gem list&lt;/code&gt; with which to install the gems at the correct versions required.&lt;/p&gt;

&lt;p&gt;To make this problem a little easier in the future, I wrote a quick script to generate a massive shell command to install all the gems recorded in &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;gem list&lt;/code&gt;. Next time I, or someone else, needs to set up one of these types of project, I can run this script, and hand them the generated shell script. They can run this in their gemset of choice, and install all gems required - much easier!&lt;/p&gt;

&lt;p&gt;Here’s the script - alternatively, you can check it out the &lt;a href=&quot;https://gist.github.com/1335721&quot;&gt;Gist&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;c1&quot;&gt;#!/usr/bin/env ruby&lt;/span&gt;


&lt;span class=&quot;c1&quot;&gt;### gem_to_command.rb ###############################################################&lt;/span&gt;
&lt;span class=&quot;c1&quot;&gt;# Produce a command to install the gems you currently have installed (using gem list)&lt;/span&gt;
&lt;span class=&quot;c1&quot;&gt;# Makes a simple Shell script that can be run on Linux or Mac OS X&lt;/span&gt;
&lt;span class=&quot;c1&quot;&gt;#&lt;/span&gt;
&lt;span class=&quot;c1&quot;&gt;# Author: @sudojosh&lt;/span&gt;
&lt;span class=&quot;c1&quot;&gt;######################################################################################&lt;/span&gt;

&lt;span class=&quot;n&quot;&gt;gems&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;sb&quot;&gt;`gem list`&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;open&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;install_gems.sh&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;wb&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;script&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;|&lt;/span&gt;
  &lt;span class=&quot;n&quot;&gt;script&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;#!/usr/bin/env ruby&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\n&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;&lt;/span&gt;
  &lt;span class=&quot;n&quot;&gt;gems&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;gems&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;split&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;\n&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;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;gem&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;|&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;gem&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;gem&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;split&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&apos; &apos;&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;part&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;|&lt;/span&gt;
      &lt;span class=&quot;n&quot;&gt;part&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;gsub&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;sr&quot;&gt;/[^\w\.\-]/&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&apos;&apos;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;};&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;gem&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;gem&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;1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;].&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;min&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;gems&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;each&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;gem&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;|&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;script&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;gem install &lt;/span&gt;&lt;span class=&quot;si&quot;&gt;#{&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;gem&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;si&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt; -v=&lt;/span&gt;&lt;span class=&quot;si&quot;&gt;#{&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;gem&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;si&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;script&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; &amp;amp;&amp;amp; &quot;&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;unless&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;gem&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;n&quot;&gt;gems&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;last&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;p&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;
&lt;span class=&quot;nb&quot;&gt;puts&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;Wrote install_gems.sh&quot;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Here’s what it does:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;Get the output of &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;gem list&lt;/code&gt;&lt;/li&gt;
  &lt;li&gt;Open a .sh file for writing&lt;/li&gt;
  &lt;li&gt;Iterate through the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;gem list&lt;/code&gt; output, and parse from the file the name of the gem and the lowest version number (lowest is safer than highest, even though there &lt;em&gt;should&lt;/em&gt; only ever be one)&lt;/li&gt;
  &lt;li&gt;Assemble a &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;gem install&lt;/code&gt; command with the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;--no-rdoc --no-ri&lt;/code&gt; arguments, and write this to the file.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Enjoy!&lt;/p&gt;
</description>
        <pubDate>Thu, 03 Nov 2011 16:34:00 +1300</pubDate>
        <link>https://joshmcarthur.com/2011/11/03/generating-gem-install-commands-from-gem-list.html</link>
        <guid isPermaLink="true">https://joshmcarthur.com/2011/11/03/generating-gem-install-commands-from-gem-list.html</guid>
        
        <category>coding</category>
        
        <category>ruby</category>
        
        <category>scripts</category>
        
        <category>hacks</category>
        
        
      </item>
    
      <item>
        <title>A Fun Little Bookmarklet</title>
        <description>&lt;p&gt;I’m sure this has been done before, but after noticing &lt;a href=&quot;http://www.1-day.co.nz&quot;&gt;1-day’s&lt;/a&gt;  ‘Look Busy’ feature, I just had to write a bookmarklet to load this up on any site!&lt;/p&gt;

&lt;p&gt;If you just want to try it out, here’s the link &lt;a href=&quot;javascript:var busy=document.createElement(&apos;div&apos;);var body=document.getElementsByTagName(&apos;body&apos;)[0];var max_width_cache=body.getAttribute(&apos;max-width&apos;);body.style.maxWidth=&apos;100%&apos;;busy.setAttribute(&apos;id&apos;,&apos;lookbusy&apos;);busy.setAttribute(&apos;style&apos;,&apos;position: absolute; z-index: 1000; width: 100%; height: 100%; top: 0px; left: 0px; right: 0px; bottom: 0px; background: #FFF url(http://www.1-day.co.nz/images/2010_mission_critical_development_strategy.png) no-repeat 0 0;&apos;);var close=document.createElement(&apos;a&apos;);close.setAttribute(&apos;class&apos;,&apos;busy close&apos;);close.setAttribute(&apos;style&apos;,&apos;position: fixed; z-index: 1001; right: 10px; bottom: 10px; background-color: #FFF; color: #000; font-size:10px&apos;);close.setAttribute(&apos;href&apos;,&apos;#&apos;);close.innerText=&apos;OK, Clear&apos;;close.setAttribute(&apos;onclick&apos;,&amp;quot;javascript:body.removeChild(document.getElementById(&apos;lookbusy&apos;));body.style.maxWidth = &amp;quot; + max_width_cache);busy.appendChild(close);body.appendChild(busy)&quot;&gt;Look Busy Bookmarklet&lt;/a&gt; - either right-click on the link and ‘Copy URL’ and paste into a new bookmark, or drag to your bookmarks bar or folder. It should work in Chrome, Safari, Firefox and IE7-9&lt;/p&gt;

&lt;p&gt;Here’s the source code for anyone interested in seeing how this works:&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-javascript&quot; data-lang=&quot;javascript&quot;&gt;&lt;span class=&quot;c1&quot;&gt;// Create a new div tag to contain our image&lt;/span&gt;
&lt;span class=&quot;kd&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;busy&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;&apos;&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;div&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&apos;&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;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;getElementsByTagName&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&apos;&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;body&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&apos;&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;kd&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;max_width_cache&lt;/span&gt; &lt;span class=&quot;o&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;style&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;maxWidth&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;style&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;maxWidth&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;dl&quot;&gt;&apos;&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;100%&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&apos;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;nx&quot;&gt;busy&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;setAttribute&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&apos;&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;id&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&apos;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;dl&quot;&gt;&apos;&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;lookbusy&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&apos;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;

&lt;span class=&quot;c1&quot;&gt;// Add a background image, and position the div tag above all other content and make it fill the screen&lt;/span&gt;
&lt;span class=&quot;nx&quot;&gt;busy&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;setAttribute&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&apos;&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;style&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&apos;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;dl&quot;&gt;&apos;&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;position: absolute; z-index: 1000; width: 100%; height: 100%; top: 0px; left: 0px; right: 0px; bottom: 0px; background: #FFF url(http://www.1-day.co.nz/images/2010_mission_critical_development_strategy.png) no-repeat 0 0;&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&apos;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;

&lt;span class=&quot;c1&quot;&gt;// Add a inconspicuous link to close the image&lt;/span&gt;
&lt;span class=&quot;kd&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;close&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;&apos;&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;a&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&apos;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
&lt;span class=&quot;nx&quot;&gt;close&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;setAttribute&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&apos;&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;class&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&apos;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;dl&quot;&gt;&apos;&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;busy close&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&apos;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
&lt;span class=&quot;nx&quot;&gt;close&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;setAttribute&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&apos;&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;style&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&apos;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;dl&quot;&gt;&apos;&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;position: fixed; z-index: 1001; right: 10px; bottom: 10px; background-color: #FFF; color: #000; font-size:10px&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&apos;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
&lt;span class=&quot;nx&quot;&gt;close&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;setAttribute&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&apos;&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;href&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&apos;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;dl&quot;&gt;&apos;&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;#&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&apos;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
&lt;span class=&quot;nx&quot;&gt;close&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;innerText&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;OK, Clear&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;close&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;setAttribute&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&apos;&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;onclick&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&apos;&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;javascript:body.removeChild(document.getElementById(&apos;lookbusy&apos;));body.style.maxWidth = &lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&quot;&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;+&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;max_width_cache&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;

&lt;span class=&quot;c1&quot;&gt;// Add the close image to the look busy div tag&lt;/span&gt;
&lt;span class=&quot;nx&quot;&gt;busy&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;close&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;

&lt;span class=&quot;c1&quot;&gt;// Add the look busy div tag to the body of the document&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;busy&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;p&gt;Give it a go! It’s super handy for when you’re checking out &lt;a href=&quot;http://www.trademe.co.nz&quot;&gt;TradeMe&lt;/a&gt;, &lt;a href=&quot;http://failblog.org&quot;&gt;Failblog&lt;/a&gt;, or &lt;a href=&quot;http://images.google.com?q=wink&quot;&gt;anything similar&lt;/a&gt;!&lt;/p&gt;

</description>
        <pubDate>Thu, 20 Oct 2011 13:28:00 +1300</pubDate>
        <link>https://joshmcarthur.com/fun,nerdy/2011/10/20/a-fun-little-bookmarklet.html</link>
        <guid isPermaLink="true">https://joshmcarthur.com/fun,nerdy/2011/10/20/a-fun-little-bookmarklet.html</guid>
        
        <category>fun</category>
        
        <category>javascript</category>
        
        <category>experiments</category>
        
        
        <category>fun,nerdy</category>
        
      </item>
    
      <item>
        <title>Safely Start and Stop VirtualBox VMs with init.d</title>
        <description>&lt;p&gt;Recently I’ve rolled out a virtual machine host box to run headless VMs (headless means that there is no display, keyboard etc plugged into it), as these make great test and experiment machines for trying new things out. As part of this rollout, I mashed a couple of blogs and some documentation together and came up with this script:&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-bash&quot; data-lang=&quot;bash&quot;&gt;&lt;span class=&quot;c&quot;&gt;#!/bin/sh&lt;/span&gt;

&lt;span class=&quot;c&quot;&gt;# virtual_machines	Start and stop virtual machines when the host changes state&lt;/span&gt;
&lt;span class=&quot;c&quot;&gt;#&lt;/span&gt;
&lt;span class=&quot;c&quot;&gt;#&lt;/span&gt;
&lt;span class=&quot;nv&quot;&gt;VMUSER&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;administrator

&lt;span class=&quot;k&quot;&gt;case&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;$1&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;in
	&lt;/span&gt;start&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
		&lt;span class=&quot;nb&quot;&gt;echo&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;Starting VirtualBox VMs&quot;&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;nt&quot;&gt;-f&lt;/span&gt; /etc/virtualbox/machines_enabled &lt;span class=&quot;o&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;then
			&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;cat&lt;/span&gt; /etc/virtualbox/machines_enabled | &lt;span class=&quot;k&quot;&gt;while &lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;read &lt;/span&gt;VM&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;nb&quot;&gt;sudo&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;-H&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;-b&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;-u&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;$VMUSER&lt;/span&gt; /usr/bin/VBoxHeadless &lt;span class=&quot;nt&quot;&gt;-startvm&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;$VM&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;&lt;/span&gt;
			&lt;span class=&quot;k&quot;&gt;done
		fi&lt;/span&gt;
		&lt;span class=&quot;p&quot;&gt;;;&lt;/span&gt;
	stop&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
		&lt;span class=&quot;nb&quot;&gt;echo&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;Saving state of VirtualBox VM...&quot;&lt;/span&gt;
		&lt;span class=&quot;nb&quot;&gt;cat&lt;/span&gt; /etc/virtualbox/machines_enabled | &lt;span class=&quot;k&quot;&gt;while &lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;read &lt;/span&gt;VM&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;nb&quot;&gt;sudo&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;-H&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;-u&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;$VMUSER&lt;/span&gt; /usr/bin/VBoxManage controlvm &lt;span class=&quot;s2&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;$VM&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;&lt;/span&gt; savestate
		&lt;span class=&quot;k&quot;&gt;done&lt;/span&gt;
		&lt;span class=&quot;p&quot;&gt;;;&lt;/span&gt;
	&lt;span class=&quot;k&quot;&gt;*&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
		&lt;span class=&quot;nb&quot;&gt;echo&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;Usage: /etc/init.d/virtual_machines {start|stop}&quot;&lt;/span&gt;
		&lt;span class=&quot;nb&quot;&gt;exit &lt;/span&gt;1
		&lt;span class=&quot;p&quot;&gt;;;&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;esac&lt;/span&gt;

&lt;span class=&quot;nb&quot;&gt;exit &lt;/span&gt;0&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;p&gt;I wrote this because this isn’t something that is supported directly by VirtualBox, and it’s essential that on a headless server that these virtual machines be able to go up and down happily without harm and reliably. It supports start and stop explanations and uses savestate, rather than poweroff - essentially, as the host server goes down, it will ‘hibernate’ each VM, and then restore when the server starts back up again.&lt;/p&gt;

&lt;p&gt;Another feature that I built into this was the use of a file in which a list of VMs can be specified to start and stop - for example, if you only want this script to deal with a subset of the virtual machines you have set up on the host server.&lt;/p&gt;

&lt;p&gt;An example of this file might look like this:&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;winxp_development
win7_development
ubuntu_11_10_development
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;It works really well for me, and since it’s something I had trouble tracking down, hopefully I can make someone’s life a little easier with this solution.&lt;/p&gt;

</description>
        <pubDate>Wed, 19 Oct 2011 17:05:00 +1300</pubDate>
        <link>https://joshmcarthur.com/2011/10/19/init-dot-d-script-for-virtualbox-vms.html</link>
        <guid isPermaLink="true">https://joshmcarthur.com/2011/10/19/init-dot-d-script-for-virtualbox-vms.html</guid>
        
        <category>howto</category>
        
        <category>linux</category>
        
        <category>sysadmin</category>
        
        <category>virtualbox</category>
        
        
      </item>
    
      <item>
        <title>Action Mailer Interceptors</title>
        <description>&lt;p&gt;ActionMailer Interceptors are a great way to test the full stack of your mailing in Rails from the generation from data through to receiving the email in your client. They are similar to ActiveRecord’s &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;before_x&lt;/code&gt;-type callbacks, and let you change something about the message being sent right before it’s actually dispatched.&lt;/p&gt;

&lt;p&gt;The most common purpose I use these for is to redirect mail being sent from the application to either my Inbox, or some shared account (if it’s on a project where I’m not the only developer). This lets me make sure that the HTML in the message is displayed properly, and that the data gets injected into the content the way I expect it to be.&lt;/p&gt;

&lt;p&gt;Here’s a quick example of how to use a Mail interceptor for this purpose:&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-ruby&quot; data-lang=&quot;ruby&quot;&gt;&lt;span class=&quot;c1&quot;&gt;# lib/development_mail_interceptor.rb&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;DevelopmentMailInterceptor&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;delivering_email&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;mail&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;mail&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;subject&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;&amp;lt;&lt;/span&gt;&lt;span class=&quot;si&quot;&gt;#{&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;mail&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;si&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&amp;gt; &lt;/span&gt;&lt;span class=&quot;si&quot;&gt;#{&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;mail&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;subject&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;n&quot;&gt;mail&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;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;me@mywork.co.nz&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;span class=&quot;c1&quot;&gt;# config/initializers/mail_interceptors.rb&lt;/span&gt;
&lt;span class=&quot;nb&quot;&gt;require&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&apos;development_mail_interceptor&apos;&lt;/span&gt;
&lt;span class=&quot;no&quot;&gt;ActionMailer&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;no&quot;&gt;Base&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;register_interceptor&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;no&quot;&gt;DevelopmentMailInterceptor&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;Rails&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;env&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;development?&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;p&gt;…and give one of your mailers a go.&lt;/p&gt;

&lt;p&gt;This is a very simple example, of course, but there are a lot of uses for this type of thing - logging &amp;amp; auditing, checking against quotas, analysis, etc. etc.&lt;/p&gt;

&lt;p&gt;What really triggered this post though, was a odd problem I came across when trying to get this interceptor to work. An ActionMailer method can be triggered using one of two methods: either &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;deliver&lt;/code&gt; or &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;deliver!&lt;/code&gt; - the main difference between the two is that the second will throw exceptions if it cannot be sent, which is why I tend to prefer using it. Something to keep in mind though, is that using &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;deliver!&lt;/code&gt; will call any registered Mail Observers, but &lt;strong&gt;not Interceptors&lt;/strong&gt; - meaning that your mail will be sent unaltered.&lt;/p&gt;

&lt;p&gt;It really was a frustrating process to debug, but after looking at the &lt;a href=&quot;https://github.com/mikel/mail&quot;&gt;Mail gem&lt;/a&gt; source code (ActionMailer back-ends onto this gem for it’s mail setup and delivery processes), in particular the &lt;a href=&quot;https://github.com/mikel/mail/blob/master/lib/mail/message.rb#L227&quot;&gt;Message class&lt;/a&gt;, I noticed this crucial difference between the two. Resolving the issue is, of course, as simple as using &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;deliver&lt;/code&gt; - without the exclamation mark. After that, your Interceptors will be triggered just as they should be.&lt;/p&gt;
</description>
        <pubDate>Thu, 06 Oct 2011 16:51:00 +1300</pubDate>
        <link>https://joshmcarthur.com/howto/2011/10/06/action-mailer-interceptors.html</link>
        <guid isPermaLink="true">https://joshmcarthur.com/howto/2011/10/06/action-mailer-interceptors.html</guid>
        
        <category>ruby on rails</category>
        
        <category>programming</category>
        
        <category>actionmailer</category>
        
        
        <category>howto</category>
        
      </item>
    
      <item>
        <title>Overriding Action Caches</title>
        <description>&lt;p&gt;Recently, I have been working on a web application that is quite media rich, and is expected to run into quite a bit of traffic. I’ve been working on building an API for a front end system, using JSON to handle passing this data back and forth.&lt;/p&gt;

&lt;p&gt;Obviously with this amount of traffic, and the size of some of the JSON collections we were having to marshall and store via Rails, we were going to need some pretty intense caching to reduce the load on Rails, and our database server. We had to balance this need, however, with the requirement that data should be live, or close to live (i.e. around 5-10 minutes) - in particular, statistics, which are calculated on-demand for several responses.&lt;/p&gt;

&lt;p&gt;The strategy we chose to manage this balance was to use Rails’ provided &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;caches_action&lt;/code&gt; method to cache our JSON responses, building up a cache key from certain parameters, as well as some meta-data, for example, the user’s logged-in status. Because we were using memcached, we could use the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;:expires_in&lt;/code&gt; option to tell the memcached store to expire the cached value after x minutes.&lt;/p&gt;

&lt;p&gt;This approach worked for a while, but we found we had a pretty major problem - while the data was cached, it went alright, but as soon as the cache expired we were having loads of users hitting a response that took way to long to build (before we optimized queries, 30+ seconds). So, we needed another fix.&lt;/p&gt;

&lt;p&gt;To fix this problem, we tried out adding some cron tasks that used curl to ping the cached URLs, to try and preload the cache so that less users would be hitting the DB. This only partially fixed the problem though, so we identified a solution that would work a little better for us.&lt;/p&gt;

&lt;p&gt;What we wanted to do was to leave our existing caching in place - aside from the expiry, it was working fine, and we didn’t want to rework everything. With this in mind though, we needed a way to force a refresh of the data in the cache external from the controller. What we ended up implementing was a monkeypatch on Rails’ caches_action-related methods, that allows us to pass in an &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;:overwrite&lt;/code&gt; option - this can be a Proc, or just a boolean - basically, when the value of &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;:overwrite&lt;/code&gt; is true, Rails will bypass the cached value, grab the &lt;em&gt;new value&lt;/em&gt;, and load this into the cache - effectively refreshing the value without a user having to trigger the process.&lt;/p&gt;

&lt;p&gt;Here’s the monkeypatch code - have a scan through it, and I’ll explain it below:&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-ruby&quot; data-lang=&quot;ruby&quot;&gt;    &lt;span class=&quot;nb&quot;&gt;require&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&apos;set&apos;&lt;/span&gt;

    &lt;span class=&quot;k&quot;&gt;module&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;ActionController&lt;/span&gt; &lt;span class=&quot;c1&quot;&gt;#:nodoc:&lt;/span&gt;
      &lt;span class=&quot;k&quot;&gt;module&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;Caching&lt;/span&gt;

	    &lt;span class=&quot;k&quot;&gt;module&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;Actions&lt;/span&gt;
        &lt;span class=&quot;kp&quot;&gt;extend&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;ActiveSupport&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;no&quot;&gt;Concern&lt;/span&gt;

          &lt;span class=&quot;kp&quot;&gt;protected&lt;/span&gt;
          &lt;span class=&quot;k&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;ActionCacheFilter&lt;/span&gt; &lt;span class=&quot;c1&quot;&gt;#:nodoc:&lt;/span&gt;
            &lt;span class=&quot;k&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;initialize&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;options&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;vi&quot;&gt;@cache_path&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;vi&quot;&gt;@store_options&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;vi&quot;&gt;@cache_layout&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;values_at&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;ss&quot;&gt;:cache_path&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;ss&quot;&gt;:store_options&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;ss&quot;&gt;:layout&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
            &lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;

            &lt;span class=&quot;k&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;filter&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;controller&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
              &lt;span class=&quot;n&quot;&gt;path_options&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;vi&quot;&gt;@cache_path&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;respond_to?&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;ss&quot;&gt;:call&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
                &lt;span class=&quot;n&quot;&gt;controller&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;instance_exec&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;controller&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&amp;amp;&lt;/span&gt;&lt;span class=&quot;vi&quot;&gt;@cache_path&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;vi&quot;&gt;@cache_path&lt;/span&gt;
              &lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;

              &lt;span class=&quot;n&quot;&gt;cache_path&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;ActionCachePath&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;controller&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;path_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;overwrite&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;vi&quot;&gt;@overwrite&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;vi&quot;&gt;@store_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;:overwrite&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;kp&quot;&gt;nil&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
                &lt;span class=&quot;vi&quot;&gt;@overwrite&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;respond_to?&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;ss&quot;&gt;:call&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;controller&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;instance_exec&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;controller&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&amp;amp;&lt;/span&gt;&lt;span class=&quot;vi&quot;&gt;@overwrite&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;vi&quot;&gt;@overwrite&lt;/span&gt;
              &lt;span class=&quot;k&quot;&gt;else&lt;/span&gt;
                &lt;span class=&quot;kp&quot;&gt;false&lt;/span&gt;
              &lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;

              &lt;span class=&quot;n&quot;&gt;body&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;overwrite&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;?&lt;/span&gt; &lt;span class=&quot;kp&quot;&gt;nil&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;controller&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;read_fragment&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;cache_path&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;path&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;vi&quot;&gt;@store_options&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;n&quot;&gt;body&lt;/span&gt;
                &lt;span class=&quot;n&quot;&gt;controller&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;action_has_layout&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;kp&quot;&gt;false&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;unless&lt;/span&gt; &lt;span class=&quot;vi&quot;&gt;@cache_layout&lt;/span&gt;
                &lt;span class=&quot;k&quot;&gt;yield&lt;/span&gt;
                &lt;span class=&quot;n&quot;&gt;controller&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;action_has_layout&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;body&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;controller&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;_save_fragment&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;cache_path&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;path&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;vi&quot;&gt;@store_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;n&quot;&gt;body&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;controller&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;render_to_string&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;ss&quot;&gt;:text&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;body&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;ss&quot;&gt;:layout&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;k&quot;&gt;unless&lt;/span&gt; &lt;span class=&quot;vi&quot;&gt;@cache_layout&lt;/span&gt;
              &lt;span class=&quot;n&quot;&gt;controller&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;response_body&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;body&lt;/span&gt;
              &lt;span class=&quot;n&quot;&gt;controller&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;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;Mime&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;cache_path&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;extension&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;||&lt;/span&gt; &lt;span class=&quot;ss&quot;&gt;:html&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt;
            &lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;
          &lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;
      &lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;p&gt;By dropping this code into &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;config/initializers&lt;/code&gt;, this code gets patched into the ActionController::Caching::Actions::ActionCacheFilter class, and overrides the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;initalize&lt;/code&gt; and &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;filter&lt;/code&gt; methods to let us a) pass in an override option, and b) choose to refresh the cache if the override option is set.&lt;/p&gt;

&lt;p&gt;The filter method performs as normal until it has finished generating the cache path - at this point, it would normally return the cached response if it was there, and if it had not expired. Instead, my colleague &lt;a href=&quot;http://telos.co.nz&quot;&gt;James Moriaty&lt;/a&gt; replaced some code here:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;First, it retrieves the value of the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;:overwrite&lt;/code&gt; option passed in to the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;caches_action&lt;/code&gt; method from the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;@store_options&lt;/code&gt; hash - if it’s a Proc, it executes it here to get the value, otherwise assumes it’s a boolean variable.&lt;/li&gt;
  &lt;li&gt;If the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;:overwrite&lt;/code&gt; option has not been passed in, it returns false - i.e. don’t overwrite the cache.&lt;/li&gt;
  &lt;li&gt;If the overwrite value is true, it sets the body to nil, so that it will be re-built. Otherwise, it does the usual and returns the cached response from Memcache.&lt;/li&gt;
  &lt;li&gt;From here, it more or less goes back to the default class, rebuilding the response from the database.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;In our application’s case, we use this functionality by tweaking our cron jobs a little to pass in a particular parameter - we then added the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;:overwrite&lt;/code&gt; option to our &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;caches_action&lt;/code&gt; methods, with a Proc that returns true if this parameter equals the correct value.&lt;/p&gt;

&lt;p&gt;So far, this solution has worked fantastically - now, hardly any of our users hit the database - instead, they are heading to memcache to grab that response, while our background cron jobs rebuild the data that will get returned to them. Using a parameter for refreshing the cache also lets us easily refresh manually for testing or to check for a value.&lt;/p&gt;

&lt;p&gt;This solution is clean, simple and easy to implement. I suggest that if you are facing similar problems, that you give it a go - it’s really adaptable, and requires few changes if you are already using action caching. Full credit to James for thinking up and implementing this solution - I’m just documenting it.&lt;/p&gt;
</description>
        <pubDate>Fri, 30 Sep 2011 11:48:00 +1300</pubDate>
        <link>https://joshmcarthur.com/howto/2011/09/30/overriding-action-caches.html</link>
        <guid isPermaLink="true">https://joshmcarthur.com/howto/2011/09/30/overriding-action-caches.html</guid>
        
        <category>howto</category>
        
        <category>development</category>
        
        <category>rubyonrails</category>
        
        <category>caching</category>
        
        <category>optimization</category>
        
        
        <category>howto</category>
        
      </item>
    
      <item>
        <title>Howto: Database Backup and Restore</title>
        <description>&lt;p&gt;An inherent part of developing web applications is managing your datastores - typically, a relational database such as MySQL or PostgreSQL. Today, I’m going to quickly cover off how to backup and restore for both of these databases.&lt;/p&gt;

&lt;h2 id=&quot;what-youll-need&quot;&gt;What you’ll need&lt;/h2&gt;

&lt;ul&gt;
  &lt;li&gt;Either PostgreSQL or MySQL&lt;/li&gt;
  &lt;li&gt;Access to a database (preferably one with data in it)&lt;/li&gt;
&lt;/ul&gt;

&lt;h2 id=&quot;why-this-is-useful-to-know&quot;&gt;Why this is useful to know&lt;/h2&gt;

&lt;p&gt;Lots of interactions with databases have the potential to destroy or modify data (in a bad way). When using frameworks such as Ruby on Rails, it’s even easier to, say, accidently delete all of your Users (Horribly easy in DataMapper, unfortunately). It’s important that before you do anything with data that’s destructive, you have a backup of your database that you can restore from quickly and easily.&lt;/p&gt;

&lt;h2 id=&quot;postgresql&quot;&gt;PostgreSQL&lt;/h2&gt;

&lt;p&gt;Postgres databases are backed up using the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;pg_dump&lt;/code&gt; command - a command-line utility that comes packaged with the database server. Here’s the command:&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-sh&quot; data-lang=&quot;sh&quot;&gt;    pg_dump &lt;span class=&quot;nt&quot;&gt;--no-owner&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;-U&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;[&lt;/span&gt;username] &lt;span class=&quot;nt&quot;&gt;-W&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;[&lt;/span&gt;database_name] &lt;span class=&quot;o&quot;&gt;&amp;gt;&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;[&lt;/span&gt;file to dump to].sql.dump&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;p&gt;Let me explain these options and why I use them:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;--no-owner&lt;/code&gt;: By default, Postgres dumps the database with lots of SET OWNER TO statements. I like to take these out of the dump, as I’m not necessarily restoring the dump to exactly the same server with exactly the same users. Using –no-owner means that ownership statements will be excluded.&lt;/li&gt;
  &lt;li&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;-U [username]&lt;/code&gt;: This lets you pass in the database user name your web application usually uses to connect - using this is just good practise, as it ensures that what you’re dumping is exactly what the web application has.&lt;/li&gt;
  &lt;li&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;-W&lt;/code&gt;: This option, used in conjunction with the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;-U&lt;/code&gt; flag, prompts for the password when you run the command&lt;/li&gt;
  &lt;li&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;[database_name]&lt;/code&gt;: This is the name of the database that you want to dump&lt;/li&gt;
  &lt;li&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;[file to dump to]&lt;/code&gt;: This is the file that the SQL script that pg_dump produces will be piped into. I normally name this file with the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;.sql.dump&lt;/code&gt; extension, so that I can see it’s a SQL dump straight off.&lt;/li&gt;
&lt;/ul&gt;

&lt;h4 id=&quot;restoring-a-database-dump&quot;&gt;Restoring a database dump&lt;/h4&gt;

&lt;p&gt;Restoring a Postgresql dump is really easy, and involves using the standard &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;psql&lt;/code&gt; client to connect to the database and execute the SQL script in your dump file.&lt;/p&gt;

&lt;p&gt;First of all, make sure that you have created the database you want to load the data into. In this example, let’s say I’ve dumped from the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;facebook_production&lt;/code&gt; database to the file &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;facebook_production_23092011.sql.dump&lt;/code&gt;, and I want to restore into the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;facebook_development&lt;/code&gt; database so that I can test out some code against some production data. I want to connect to the database using psql as the web application user, and load the dump in:&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-sh&quot; data-lang=&quot;sh&quot;&gt;    psql &lt;span class=&quot;nt&quot;&gt;-U&lt;/span&gt; facebook &lt;span class=&quot;nt&quot;&gt;-W&lt;/span&gt; facebook_development &amp;lt; facebook_production_23092011.sql.dump&lt;span class=&quot;sb&quot;&gt;`&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;p&gt;Note how I am using the opposite of the less-than symbol I used above - this basically denotes the direction of the data - it’s coming &lt;em&gt;from&lt;/em&gt; the file, going &lt;em&gt;to&lt;/em&gt; the database.&lt;/p&gt;

&lt;p&gt;Upon running this commmand (with your own database, of course), you will first be prompted for your database user’s password, and will then see a bunch of SQL statements being executed. Once it’s completed, your database has been loaded successfully - you can jump in using psql if you’d like, and query around a bit.&lt;/p&gt;

&lt;h2 id=&quot;mysql&quot;&gt;MySQL&lt;/h2&gt;

&lt;p&gt;MySQL databases are backed up with the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;mysqldump&lt;/code&gt; program - one I’m not as familiar with as Postgres, but I know the basics, and largely that’s all you need with this type of thing. The main thing to keep in mind is that the process is the same as for PostgreSQL above - use the dump program to write the database out to a file (in the form of SQL statements), and then use the database client program &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;mysql&lt;/code&gt; in this case, to execute the commands in the file against the database being restored to. Here’s the command to dump a MySQL file to disk:&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-sh&quot; data-lang=&quot;sh&quot;&gt;    mysqldump &lt;span class=&quot;nt&quot;&gt;-U&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;[&lt;/span&gt;username] &lt;span class=&quot;nt&quot;&gt;-P&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;[&lt;/span&gt;database_name] &lt;span class=&quot;o&quot;&gt;&amp;gt;&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;[&lt;/span&gt;file to dump to].sql.dump&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;p&gt;The options are more or less as I’ve described above, except that &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;-P&lt;/code&gt; is substituted for &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;-W&lt;/code&gt;, and I’ve still stuck with the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;.sql.dump&lt;/code&gt; naming scheme.&lt;/p&gt;

&lt;h4 id=&quot;restoring-a-database-dump-1&quot;&gt;Restoring a database dump&lt;/h4&gt;

&lt;p&gt;This process is almost identical to the PostgreSQL restore process. Let’s stick with the same example format we already have - dumping from a database called &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;facebook_production&lt;/code&gt; to file &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;facebook_production_23092011.sql.dump&lt;/code&gt;, restoring into a database called &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;facebook_development&lt;/code&gt; - here’s the command we need for that:&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-sh&quot; data-lang=&quot;sh&quot;&gt;    mysql &lt;span class=&quot;nt&quot;&gt;-U&lt;/span&gt; facebook &lt;span class=&quot;nt&quot;&gt;-P&lt;/span&gt; facebook_development &amp;lt; facebook_production_23092011.sql.dump&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;p&gt;Once again, you’ll be prompted for your password, but for this one you won’t see the output of the SQL statements - it will take a couple of seconds, and then the program will exit. This is normal however - if you’d like to see the output of the batch load, you can add &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;-v -v -v&lt;/code&gt; before the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;&amp;lt;&lt;/code&gt; symbol to turn verbosity to level three, otherwise you can go ahead and jump into your database and make sure everything is there.&lt;/p&gt;

&lt;h2 id=&quot;tips&quot;&gt;Tips:&lt;/h2&gt;

&lt;ul&gt;
  &lt;li&gt;If you don’t have a database user account yet, on Unix and Linux you can use &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;sudo su postgres&lt;/code&gt; to login as the ‘postgres’ user - a root-like database user that gets created for you when Postgres is installed. MySQL has a root account, but it’s not a system user - you are normally prompted for a root username and password when you install MySQL. If you &lt;em&gt;are&lt;/em&gt; using the Postgres user, you don’t need to pass in a username or password in the above commands, but you need to remember that your restored databases will be owned by ‘postgres’, not you web application database user.&lt;/li&gt;
  &lt;li&gt;A handy place to put database dumps that you are planning to use right away is &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;/tmp&lt;/code&gt; (Only on Unix and Linux). This is a directly that is writeable by everyone, that will get ‘garbage collected’ periodically. This is especially handy if you are logging in as the ‘postgres’ user, as typically this user won’t have write permissions on many other directories&lt;/li&gt;
&lt;/ul&gt;

</description>
        <pubDate>Fri, 23 Sep 2011 13:26:00 +1200</pubDate>
        <link>https://joshmcarthur.com/howto/2011/09/23/howto-database-backup-and-restore.html</link>
        <guid isPermaLink="true">https://joshmcarthur.com/howto/2011/09/23/howto-database-backup-and-restore.html</guid>
        
        <category>howto</category>
        
        <category>development</category>
        
        <category>mysql</category>
        
        <category>postgresql</category>
        
        
        <category>howto</category>
        
      </item>
    
      <item>
        <title>Introducing Blog Broadcaster</title>
        <description>&lt;p&gt;I’ve just completed a Blog Broadcaster for this blog. It had a couple of interesting technical things, and I needed to test it properly, so here’s this post!&lt;/p&gt;

&lt;p&gt;I recently migrated this blog from &lt;a href=&quot;http://tumblr.com&quot;&gt;Tumblr&lt;/a&gt;, and while Tumblr was pretty awesome and easy to use, it didn’t have great support for blocks of code and preformatted comment, and, like &lt;a href=&quot;http://www.radiantcms.org&quot;&gt;Radiant CMS&lt;/a&gt;, it stored layouts, stylesheets, and all of that kind of thing in a database somewhere - it wasn’t in source control, and making changes to it was dangerous.&lt;/p&gt;

&lt;p&gt;One thing that I immediately missed from Tumblr, however, was it’s ability to post to Facebook and Twitter automatically whenever I published a post. As I don’t post that often, it’s really important to me that I market my blog as much as I can - like my Github profile, the content on my blog is a reflection of my knowledge and skill as a developer, and so I want to get that in front of people as much as possible - broadcasting to social networks is a great way of achieving that.&lt;/p&gt;

&lt;p&gt;With my blog on Github, I needed a way to broadcast new posts to these social networks. Github has a built-in post-receive hook to post to Twitter, but I really needed something more than that. Here’s my list of requirements:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;It should automatically post without me needing to do any special task&lt;/li&gt;
  &lt;li&gt;It should be conditional - i.e. it shouldn’t post &lt;em&gt;everytime&lt;/em&gt; I change something&lt;/li&gt;
  &lt;li&gt;It should support both Facebook and Twitter&lt;/li&gt;
  &lt;li&gt;It should be able to be triggered by a commit&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;What I ended up building was a Sinatra app whose sole purpose was to receive POST’ed commit information, parse it into a Facebook and Twitter post, and broadcast it. It’s hosted on Heroku, and gets triggered by a Github &lt;a href=&quot;http://help.github.com/post-receive-hooks/&quot;&gt;Post-Receive Hooks&lt;/a&gt;. I did run into a couple of problems along the way - the main one was sorting out being able to post Facebook updates without being logged in, or even needing to be involved at the process at all. This wasn’t too difficult, but I did need to get an access token that was long-lived, and have the ability to update this if necessary. I overcame this problem by adding some methods to my Sinatra app that will allow me to update the access token if it ever expires, or change the Facebook account used if necessary.&lt;/p&gt;

&lt;p&gt;The second problem I ran into wasn’t really a problem, but it was a challenge to try and think of a nice way of doing it. Basically I needed to store the Facebook access token somewhere so that I could use it when I needed it - but I didn’t have a database, and I didn’t particularly want to add one just for storing a single string. Since this is running on Heroku (Bamboo stack, not Cedar), I was also on a read-only filesystem, so couldn’t store it in a simple text file either. In this end, I chose to store the value in Memcache using the Heroku add-on. This still isn’t necessarily a good solution, as this storage method isn’t guaranteed to be persistent, however it should suit my needs - it doesn’t particularly matter if I lose the access token, the application will gracefully degrade and just post to Twitter until I log in to Facebook via the app again.&lt;/p&gt;

&lt;p&gt;So, I think I’ve come up with a good solution. It started off as a simple broadcaster for my blog, but I think it has a lot of potential for use with any open-source project that has social networking presence - I think it’s a much more elegant and flexible hook than that which Github provides, and I hope it get’s a bit of use.&lt;/p&gt;

</description>
        <pubDate>Wed, 14 Sep 2011 19:04:00 +1200</pubDate>
        <link>https://joshmcarthur.com/2011/09/14/introducing-blog-broadcaster.html</link>
        <guid isPermaLink="true">https://joshmcarthur.com/2011/09/14/introducing-blog-broadcaster.html</guid>
        
        <category>development</category>
        
        <category>sinatra</category>
        
        <category>github</category>
        
        <category>opensource</category>
        
        
      </item>
    
      <item>
        <title>Select Anything From Everything with Select</title>
        <description>&lt;p&gt;I was recently called upon to make a horrible select input for a Ruby on Rails project - essentially, there was this model, let’s call it a Snafu, and one Snafu could share an attachment to any number of models.&lt;/p&gt;

&lt;p&gt;This select was difficult because I couldn’t just have a selection of record ID’s - I would have to use both the model type &lt;strong&gt;as well as&lt;/strong&gt; the model ID in order to be able to track down these records when the form was submitted. Here’s how I did it:&lt;/p&gt;

&lt;p&gt;**Step 1: Rendering the select **
First of all, we need to render a selection box for the ‘New Snafu’ form. This selection box should be populated by a number of model collections, keyed by an identifier that specifies both the model name and the model ID. Time for a model method, and a helper!&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-ruby&quot; data-lang=&quot;ruby&quot;&gt;    &lt;span class=&quot;k&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;options_for_select_anything&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;selected&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;kp&quot;&gt;nil&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
     &lt;span class=&quot;c1&quot;&gt;#Make a hash of the things we want to select from&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;s1&quot;&gt;&apos;Foo&apos;&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;Foo&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;active&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;item_for_select_anything&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;),&lt;/span&gt;
      &lt;span class=&quot;s1&quot;&gt;&apos;Bar&apos;&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;Bar&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;active&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;item_for_select_anything&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;#Use a Rails helper to actually get the tags&lt;/span&gt;
     &lt;span class=&quot;n&quot;&gt;grouped_options_for_select&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;n&quot;&gt;selected&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;

    &lt;span class=&quot;k&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;item_for_select_anything&lt;/span&gt;
     &lt;span class=&quot;c1&quot;&gt;# Return an array of the record name and the identifier we want to use&lt;/span&gt;
     &lt;span class=&quot;c1&quot;&gt;# The record name in this case is the display value, while the identifier&lt;/span&gt;
     &lt;span class=&quot;c1&quot;&gt;# is the data value&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;o&quot;&gt;|&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;record&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;|&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;record&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;name&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;si&quot;&gt;#{&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;record&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;id&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;record&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;class&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&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;k&quot;&gt;end&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;p&gt;Now that we have these helpers, we have a grouped collection of options, in which each option tag within the select will look something like ‘&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;&amp;lt;option value=&quot;1-Foo&quot;&amp;gt;Foo #1&amp;lt;/option&amp;gt;&lt;/code&gt;’ - let’s render our select.&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-erb&quot; data-lang=&quot;erb&quot;&gt;	&lt;span class=&quot;cp&quot;&gt;&amp;lt;%&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;form_for&lt;/span&gt; &lt;span class=&quot;vi&quot;&gt;@snafu&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;form&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;|&lt;/span&gt; &lt;span class=&quot;cp&quot;&gt;%&amp;gt;&lt;/span&gt;
		&lt;span class=&quot;nt&quot;&gt;&amp;lt;p&amp;gt;&lt;/span&gt;
			&lt;span class=&quot;cp&quot;&gt;&amp;lt;%=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;form&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;label&lt;/span&gt; &lt;span class=&quot;ss&quot;&gt;:item_identifier&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&apos;Item&apos;&lt;/span&gt; &lt;span class=&quot;cp&quot;&gt;%&amp;gt;&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;&amp;lt;br&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;/&amp;gt;&lt;/span&gt;
			&lt;span class=&quot;cp&quot;&gt;&amp;lt;%=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;form&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;ss&quot;&gt;:item_identifier&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;options_for_select_anything&lt;/span&gt; &lt;span class=&quot;cp&quot;&gt;%&amp;gt;&lt;/span&gt;
		&lt;span class=&quot;nt&quot;&gt;&amp;lt;/p&amp;gt;&lt;/span&gt;
		&lt;span class=&quot;nt&quot;&gt;&amp;lt;p&amp;gt;&lt;/span&gt;
			&lt;span class=&quot;cp&quot;&gt;&amp;lt;%=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;form&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;submit&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&apos;Create&apos;&lt;/span&gt; &lt;span class=&quot;cp&quot;&gt;%&amp;gt;&lt;/span&gt;
		&lt;span class=&quot;nt&quot;&gt;&amp;lt;/p&amp;gt;&lt;/span&gt;
	&lt;span class=&quot;cp&quot;&gt;&amp;lt;%&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;end&lt;/span&gt; &lt;span class=&quot;cp&quot;&gt;%&amp;gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;p&gt;This is a basic form of course - your form will have all the other attributes you need, but the key thing to notice here is that we aren’t trying to load against our Polymorphic ‘item’ association within the form - instead, we’re just going to send through a ‘item_identifier’ parameter that we can use to &lt;em&gt;find&lt;/em&gt; the item in our model. Let’s take a look at the sections we need.&lt;/p&gt;

&lt;p&gt;First of all, we need to have the Polymorphic association in our model, if you haven’t already done this.&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-ruby&quot; data-lang=&quot;ruby&quot;&gt;	&lt;span class=&quot;c1&quot;&gt;# Snafu.rb&lt;/span&gt;

	&lt;span class=&quot;k&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;Snafu&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;Base&lt;/span&gt;
		&lt;span class=&quot;c1&quot;&gt;# Snip&lt;/span&gt;
		&lt;span class=&quot;n&quot;&gt;belongs_to&lt;/span&gt; &lt;span class=&quot;ss&quot;&gt;:item&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;ss&quot;&gt;:polymorphic&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;c1&quot;&gt;# Snip&lt;/span&gt;
	&lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;

	&lt;span class=&quot;c1&quot;&gt;# Migration&lt;/span&gt;
	&lt;span class=&quot;n&quot;&gt;add_column&lt;/span&gt; &lt;span class=&quot;ss&quot;&gt;:snafus&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;ss&quot;&gt;:item_id&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;ss&quot;&gt;:integer&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;ss&quot;&gt;:null&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;kp&quot;&gt;false&lt;/span&gt;
	&lt;span class=&quot;n&quot;&gt;add_column&lt;/span&gt; &lt;span class=&quot;ss&quot;&gt;:snafus&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;ss&quot;&gt;:item_type&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;ss&quot;&gt;:string&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;ss&quot;&gt;:null&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;kp&quot;&gt;false&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;p&gt;Next of all, we need to add an &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;attr_writer&lt;/code&gt; to our model - this will hold the ‘item_identifier’ from our form submission until we are ready to use it.&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-ruby&quot; data-lang=&quot;ruby&quot;&gt;	&lt;span class=&quot;c1&quot;&gt;# Snafu.rb&lt;/span&gt;

	&lt;span class=&quot;nb&quot;&gt;attr_writer&lt;/span&gt; &lt;span class=&quot;ss&quot;&gt;:item_identifier&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;p&gt;Finally, we need to add a &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;before_validation&lt;/code&gt; filter to associate the record identified by our item_identifier with our model instance:&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-ruby&quot; data-lang=&quot;ruby&quot;&gt;	&lt;span class=&quot;c1&quot;&gt;# Snafu.rb&lt;/span&gt;
	&lt;span class=&quot;k&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;Snafu&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;Base&lt;/span&gt;
		&lt;span class=&quot;c1&quot;&gt;# Snip&lt;/span&gt;
		&lt;span class=&quot;n&quot;&gt;belongs_to&lt;/span&gt; &lt;span class=&quot;ss&quot;&gt;:item&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;ss&quot;&gt;:polymorphic&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;n&quot;&gt;before_validation&lt;/span&gt; &lt;span class=&quot;ss&quot;&gt;:set_item&lt;/span&gt;

		&lt;span class=&quot;nb&quot;&gt;attr_writer&lt;/span&gt; &lt;span class=&quot;ss&quot;&gt;:item_identifier&lt;/span&gt;

		&lt;span class=&quot;c1&quot;&gt;# Snip&lt;/span&gt;

		&lt;span class=&quot;kp&quot;&gt;private&lt;/span&gt;

		&lt;span class=&quot;k&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;set_item&lt;/span&gt;
			&lt;span class=&quot;k&quot;&gt;if&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;nf&quot;&gt;item_identifier&lt;/span&gt;
			&lt;span class=&quot;nb&quot;&gt;id&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;model&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;nf&quot;&gt;item_identifier&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;split&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&apos;-&apos;&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;nf&quot;&gt;item&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;Kernel&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;const_get&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;model&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;:find&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;id&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;rescue&lt;/span&gt; &lt;span class=&quot;kp&quot;&gt;nil&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;/figure&gt;

&lt;p&gt;Let’s take a look at what we’ve done there - really, the meat of it is in our set_item method, that gets called before validations run (so that we will have a real item set before we may need to run validations on that item). First of all, set_item checks that we have set a item_identifier - if we haven’t we don’t want to run into Nil exceptions! Given an item_identifier is present, we want to split our identifier (Remember, this is in the format id-class name), into two parts. Finally, we do a little Ruby magic to get the class using Kernel.const_get, and then call &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;find&lt;/code&gt; on it with the ID that we want. If anything goes wrong with this bit (The class not existing, for example), then we just set item to nil.&lt;/p&gt;

&lt;p&gt;There we have it then - it’s a horrible situation, but I feel like it’s a pretty good approach. The logic is where it should be (models and helpers), the views and controllers feel clean, and it’s flexible to be reused pretty easily.&lt;/p&gt;

&lt;p&gt;As a final tweak, there’s one more change you may want to make - that is setting the selected item when we return to our form. If you take a look at the helper method we defined above, you’ll notice that we already support a selected option - we just need to pass this in. To do this, we want to add a method to our ‘Snafu’ class that will return a string of the item id and the item name concatenated with a dash - i.e., the format that our select box in the form is expecting. Go ahead and add the method now:&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-ruby&quot; data-lang=&quot;ruby&quot;&gt;	&lt;span class=&quot;c1&quot;&gt;# Snafu.rb&lt;/span&gt;

	&lt;span class=&quot;k&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;item_identifier&lt;/span&gt;
		&lt;span class=&quot;c1&quot;&gt;# Return the owner_identifier set using the attr_writer, if it exists&lt;/span&gt;
		&lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;vi&quot;&gt;@owner_identifier&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;vi&quot;&gt;@owner_identifier&lt;/span&gt;

		&lt;span class=&quot;c1&quot;&gt;# Otherwise, try and build it from the current item saved against the model&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;nf&quot;&gt;item&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;id&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;nf&quot;&gt;item&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;class&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;name&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;&apos;-&apos;&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;nb&quot;&gt;self&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;item&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;present?&lt;/span&gt;
	&lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;p&gt;With that method, can can just change our select input in the form to make use of this value:&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-erb&quot; data-lang=&quot;erb&quot;&gt;	# new.html.erb
	# Snip
	&lt;span class=&quot;cp&quot;&gt;&amp;lt;%=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;form&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;ss&quot;&gt;:item_identifier&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;options_for_select_anything&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;form&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;object&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;item_identifier&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;cp&quot;&gt;%&amp;gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;p&gt;There we are! Now when we show the form, the selected item will appear, just as we wanted.&lt;/p&gt;

</description>
        <pubDate>Fri, 09 Sep 2011 00:00:00 +1200</pubDate>
        <link>https://joshmcarthur.com/2011/09/09/select-anything-from-everything.html</link>
        <guid isPermaLink="true">https://joshmcarthur.com/2011/09/09/select-anything-from-everything.html</guid>
        
        
      </item>
    
      <item>
        <title>Capistrano rvm-shell: command not found error</title>
        <description>&lt;p&gt;I’ve just come across this problem, and I had to share it here - I can’t find
anywhere else on the the Internet where the solution is specifically stated -
it’s just alluded towards. If you are using RVM’s Capistrano integration, you
may come across a CommandNotFoundError to do with rvm-shell not being under /
usr/local/rvm/bin (Which is exactly where it should be). Upon searching the
internet, you will find that you have to upgrade RVM (you don’t), and that when
installed for a local user, RVM puts rvm-shell under ~/bin (But, you know, it’s
a system-wide install). The solution is really simple - rvm-shell is under /
usr/local/bin - use &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;set :rvm_bin_path, &quot;/usr/local/bin&quot;&lt;/code&gt; in your deploy
script, and you’re away. Clearly this is a bug with RVM putting things where it
shouldn’t, but that’s the way of things. And, if I sound a little short, it’s
because I had to all but reinstall RVM and break everything before realizing
this.&lt;/p&gt;

</description>
        <pubDate>Mon, 22 Aug 2011 00:00:00 +1200</pubDate>
        <link>https://joshmcarthur.com/2011/08/22/capistrano-rvm-shell-command-not-found-error.html</link>
        <guid isPermaLink="true">https://joshmcarthur.com/2011/08/22/capistrano-rvm-shell-command-not-found-error.html</guid>
        
        
      </item>
    
      <item>
        <title>Achievements on Coderwall</title>
        <description>&lt;p&gt;Well, it’s taken weeks for &lt;a href=&quot;http://coderwall.com&quot;&gt;Coderwall&lt;/a&gt; to finally get
it’s crawler to hit my &lt;a href=&quot;https://github.com/joshmcarthur&quot;&gt;Github Profile&lt;/a&gt;, but
I’ve finally &lt;a href=&quot;http://coderwall.com/joshmcarthur&quot;&gt;got more badges&lt;/a&gt;. I’ve been
moving some old PHP stuff of mine onto Github for people to use, so that’s
where I’ve gotten the PHP badge from. As part of my personal campaign to master
at least one other language apart from Ruby, I’ve also been working on some
Django tutorials (Django is a Python web framework), so have earned another
badge there. A happy accident was the walrus badge - I didn’t realize I had
projects up in so many different languages. Probably next on this list might be
the ‘Forked 20’ achievement for my Spree extension, [spree-import-products]
(https://github.com/joshmcarthur/spree-import-products) - I’m not so sure this
is a good thing, though - the forks-to-pull-requests ratio is too low on this
project - most likely indicating that I need to work more on making the
extension easier to use for my general audience - forks without pull requests
mean that people are taking their own copies to change them for their needs,
rather than to work on bugfixes and general improvements. We’ll have to see.
But… yay, more achievements! If you’ve got a Github account, I highly
recommend you check out Coderwall - the achievements are largely irrelevant to
any sort of reputation, but it’s a really nice way of finding interesting
Github members with interesting projects.&lt;/p&gt;

</description>
        <pubDate>Sat, 23 Jul 2011 00:00:00 +1200</pubDate>
        <link>https://joshmcarthur.com/2011/07/23/achievements-on-coderwall.html</link>
        <guid isPermaLink="true">https://joshmcarthur.com/2011/07/23/achievements-on-coderwall.html</guid>
        
        
      </item>
    
      <item>
        <title>Arbitrary Ordering in PostgreSQL when Rails + ENUM = No.</title>
        <description>&lt;p&gt;I’ve recently had to do a custom sort for a work project that has required a
sort on something that is not naturally sortable correctly (For example,
alphabetical or numerical sorting). While searching for a completely different
solution, I came across &lt;a href=&quot;http://stackoverflow.com/questions/1309624/
simulating-mysqls-order-by-field-in-postgresql&quot;&gt;this post&lt;/a&gt; that outlined a nice technique.
Basically, when you have a field in your Rails model with a predefined set of
possible values, you can use a CASE statement in PostgreSQL to perform the sort
in whichever order these values should appear. Here’s a sample of how this
could be achieved using Rails:&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-sql&quot; data-lang=&quot;sql&quot;&gt;&lt;span class=&quot;k&quot;&gt;Result&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;order&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;&quot;CASE &quot;&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;+&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;&quot;WHEN medal=&apos;gold&apos;
THEN 1 &quot;&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;+&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;&quot;WHEN medal=&apos;silver&apos; THEN 2 &quot;&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;+&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;&quot;WHEN medal=&apos;bronze&apos; THEN 3 &quot;&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;+&lt;/span&gt;
&lt;span class=&quot;nv&quot;&gt;&quot;ELSE 4 &quot;&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;nv&quot;&gt;&quot;END,name&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;p&gt;It’s messy of course - how you want to format the
SQL string is up to you, but it’s a great solution when you don’t have the
normal sorting capabilities of a ENUM datatype available to you.&lt;/p&gt;

</description>
        <pubDate>Thu, 14 Jul 2011 00:00:00 +1200</pubDate>
        <link>https://joshmcarthur.com/2011/07/14/arbitrary-ordering-in-postgresql-when-rails-enum-no.html</link>
        <guid isPermaLink="true">https://joshmcarthur.com/2011/07/14/arbitrary-ordering-in-postgresql-when-rails-enum-no.html</guid>
        
        
      </item>
    
      <item>
        <title>Quick: Clear Gemset</title>
        <description>&lt;p&gt;If you’re finding that you have to change something fairly significant in your
bundler dependencies, it’s usually a good idea to get rid of what you’ve got
loaded in an RVM gemset so that you don’t end up with different versions of
gems fighting with each other. To do this, simply run &lt;strong&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;rvm gemset empty&lt;/code&gt;&lt;/strong&gt; -
it’ll delete all the gems currently in your gemset, giving you a blank slate.&lt;/p&gt;

</description>
        <pubDate>Sun, 10 Jul 2011 00:00:00 +1200</pubDate>
        <link>https://joshmcarthur.com/2011/07/10/quick-clear-gemset.html</link>
        <guid isPermaLink="true">https://joshmcarthur.com/2011/07/10/quick-clear-gemset.html</guid>
        
        
      </item>
    
      <item>
        <title>The Dictionary of New Zealand Sign Language</title>
        <description>&lt;p&gt;&lt;a href=&quot;http://nzsl.vuw.ac.nz&quot;&gt;The Dictionary of New Zealand Sign Language&lt;/a&gt; went live
on Friday - it’s a project that I have worked on with &lt;a href=&quot;http://
www.danielsherson.com&quot;&gt;Daniel&lt;/a&gt;, Chris and &lt;a href=&quot;http://www.linkedin.com/in/
jamesarobertson&quot;&gt;James R&lt;/a&gt;, at 3Months. I attended a launch event at Victoria University
on the Friday, and it really was quite a humbling experience - this dictionary
has been the effort of a team of people for years and years - really, what
3Months has done has just been the tip of the iceberg. It really does seem like
this project has been rewarding - we’ve taken a comprehensive database of sign
information and images (Built with what seems to be a lot of time and effort by
&lt;a href=&quot;http://dave.moskovitz.co.nz/2011/06/24/the-online-dictionary-
of-new-zealand-sign-language/&quot;&gt;Dave Moskozitz&lt;/a&gt;), and given it a public presence - hopefully,
something that everyone can use - whether it’s to learn New Zealand Sign
Language, or just to learn how to finger spell names and words. I’m mostly
posting this in the hope that everyone who reads this goes to check out the
site, and to learn something of this language - give it a spin! &lt;a href=&quot;http://nzsl.vuw.ac.nz&quot;&gt;http://
nzsl.vuw.ac.nz&lt;/a&gt;&lt;/p&gt;

</description>
        <pubDate>Mon, 27 Jun 2011 00:00:00 +1200</pubDate>
        <link>https://joshmcarthur.com/2011/06/27/the-dictionary-of-new-zealand-sign-language.html</link>
        <guid isPermaLink="true">https://joshmcarthur.com/2011/06/27/the-dictionary-of-new-zealand-sign-language.html</guid>
        
        <category>work</category>
        
        
      </item>
    
      <item>
        <title>&apos;Password&apos; or &apos;Passphrase&apos;</title>
        <description>&lt;p&gt;So apparently pass phrases are the new ‘secure password’ - kinda the step you
get to when you finally accept that your users are going to use something like
‘password’ for their account password. The natural step here is to reinforce a
secure password strategy by requiring x numbers, x special characters and a
certain length - but I find this really annoying when I just want to get signed
up, and that means that other users do as well. Something I’ve just been
thinking about is the naming semantics of password field - labeling it
‘password’ immediately prompts users to think of an actual word - if they are
computer-savvy, then they might throw a symbol or number in, but most likely it
will still be based on an actual word. I wonder what would happen if you
labelled this field ‘Passphrase’ though? I think it is inevitable that many
users will recognize the pattern of the form rather than the labelling of the
fields and still enter their ‘password’, but just maybe there will be some
users who get the semantics of the label, and enter a sentence, instead of a
word. Even though there may not be special characters in that sentence, it’s
still just as, if not more secure from dictionary attacks - guessing one word
is pretty easy, but it’s much, much harder to guess a string of words, in the
correct order - especially if one or two of those words are obfuscated with
some special characters or numbers. Just a thought…. but interesting
nonetheless.&lt;/p&gt;

</description>
        <pubDate>Thu, 23 Jun 2011 00:00:00 +1200</pubDate>
        <link>https://joshmcarthur.com/2011/06/23/password-or-passphrase.html</link>
        <guid isPermaLink="true">https://joshmcarthur.com/2011/06/23/password-or-passphrase.html</guid>
        
        
      </item>
    
      <item>
        <title>Rails HABTM relationships on a non-standard connection</title>
        <description>&lt;p&gt;Recently, I’ve been implementing an admin interface for a system that I want to
make more secure than the main application. The way I’ve chosen to do this is
to run some models that relate solely to the admin application (Authentication
and Authorization in particular), on a different database - let’s call it
‘login’. This seems to be a reasonably common thing to do for security
purposes, and also for things like moving data from one database to another.
Once again though, I’ve been tempted into the potential nest of bugs that is
has_and_belongs_to_many - let me explain the schema first though: *
Administrators table - holds email, encrypted passwords and other data about
administrators * Roles table - holds the name of the role - used to authorizing
an administrator when performing an action. Each of these tables uses the line
&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;establish_connection &apos;login&apos;&lt;/code&gt; to connect to a different database than the
other models - this is the secure database that I want to leave purely for the
administrative application. So, given that I had an administrator that should
be given multiple roles, and obviously each role could have many
administrators, has_and_belongs_to_many seemed the obvious candidate - I didn’t
really want a model just for the association, and I would just need to write a
migration to create the join table. So, off, I went, and here’s what happened:
` ERROR: relation “administrators_roles” does not exist` i.e. - the
Administrator table exists, and the Role table exists, but the join table just
isn’t there. The first call for me was to take a look in the database, and make
sure that the table was there - which it was - and that migrations had
definitely run correctly and the schema was correct - which they were. After
much frustration, I found &lt;a href=&quot;http://groups.google.com/group/
rubyonrails-talk/browse_thread/thread/7644d9e5f5c6e44a/
69c8cce4c39eb571?show_docid=69c8cce4c39eb571&quot;&gt;this thread&lt;/a&gt; which described the problems I
had been having - and I was vaguely satisfied to see that the problem wasn’t
really my fault. It seems that in some versions of Rails, ActiveRecord’s
has_and_belongs_to_many_association class doesn’t respect the database
connections that the models are trying to use - instead, it uses the universal
database connection to try and look up the join table - so, what was going
wrong was that ActiveRecord was looking in the development environment’s
database, when it should have been looking in the login database.
Unfortunately, short of updating your version of ActiveRecord/Rails or patching
this class, it seems that there isn’t really any way of avoiding this problem -
you have to drop back to using has_many :through with a Model representing your
join table. I can, though, at least vouch that once you have done this, it does
work as expected, which, in the end, is what we want. It still feels a bit
hacky though….&lt;/p&gt;

</description>
        <pubDate>Tue, 21 Jun 2011 00:00:00 +1200</pubDate>
        <link>https://joshmcarthur.com/2011/06/21/rails-habtm-relationships-on-a-non-standard-connection.html</link>
        <guid isPermaLink="true">https://joshmcarthur.com/2011/06/21/rails-habtm-relationships-on-a-non-standard-connection.html</guid>
        
        <category>rails</category>
        
        <category>active_record</category>
        
        <category>habtm</category>
        
        <category>hints</category>
        
        
      </item>
    
      <item>
        <title>Quick: .rvmrc</title>
        <description>&lt;p&gt;This post is probably something more experienced RVM users will already know,
but I wanted to post this as it’s definitely my discovery of the week. When
throwing an .rvmrc file into a project, it’s a nice thing to do to write the
script correctly so that it will just work for other developers (As well as
telling you what gemset you’re using when you jump into the directory). In your
.rvmrc file, put something like this: &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;rvm use 1.9.2@gemset --create&lt;/code&gt; …this
will attempt to use that gemset (Printing out a nice message telling you it’s
using that one as it does so), and will create it if it doesn’t exist.&lt;/p&gt;

</description>
        <pubDate>Tue, 21 Jun 2011 00:00:00 +1200</pubDate>
        <link>https://joshmcarthur.com/2011/06/21/quick-rvmrc.html</link>
        <guid isPermaLink="true">https://joshmcarthur.com/2011/06/21/quick-rvmrc.html</guid>
        
        
      </item>
    
      <item>
        <title>Ubuntu: Quick ImageMagick Install</title>
        <description>&lt;p&gt;This is cross-posted from a tweet I posted a while back - I think it’s a nice
bit of advice, and I wanted to store it in a more persistent form&lt;/p&gt;

&lt;p&gt;Installing ImageMagick is one of the things that Rails developers need to do
reasonably often when provisioning new servers - basically, if you’re doing any
sort of image processing in your application (including the popularPaperclip
gem), ImageMagick is what you’ll be using.&lt;/p&gt;

&lt;p&gt;The problem is that there is a bit of a magical formula I have needed to use in
the past - if you just install ImageMagick, it will most likely not work, as it
needs to have support for different image formats you want to use compiled it
in right from the get go. Previously, I have just looked up the various
libraries I have needed (For PNG, JPEG, etc.), and then either found the
libraries in the Ubuntu package repositories or built them from source.Â 
A nice quick way of doing it though, is to use an ImageMagick meta-package in
the Ubuntu repositories named libmagick-9-dev - it is just a collection of
popular image format libraries, as well as a couple of additional utilities for
ImageMagick. You can install it on any Ubuntu system by running this command:
&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;sudo apt-get install libmagick-9-dev&lt;/code&gt;
ImageMagick itself still needs to be installed, of course. The best option here
is just to build from source - packages in the repositories are horribly out of
date, and I have found Paperclip, RMagick and Minimagick all require a fairly
recent version of ImageMagick.
Building from source sounds really intimidating, but it really isn’t - just
follow these steps:&lt;/p&gt;

&lt;ol&gt;
  &lt;li&gt;First of all, download a tarball of the ImageMagick source onto your computer:
wget ftp://ftp.imagemagick.org/pub/ImageMagick/ImageMagick.tar.gz&lt;/li&gt;
  &lt;li&gt;Next, extract the tarball:
tar -xvzf ImageMagick.tar.gz&lt;/li&gt;
  &lt;li&gt;Now configure the package - note especially the end of the output (There is a
lot of output) - it tells you which Image libraries you have installed - any
with ‘yes’ next to them it will happily format and convert - because you’ve
installed the package above, all the common formats should have a ‘yes’ next to
them, but it’s worth checking: &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;cd ImageMagick-[VERSION] (VERSION will be a series of numbers like &apos;6.7.0-8&apos;) &amp;amp;&amp;amp; ./configure&lt;/code&gt;
‘
Now all the hard work (By you) is done - you just need to compile the packages:&lt;/li&gt;
&lt;/ol&gt;

&lt;ul&gt;
  &lt;li&gt;make &amp;amp;&amp;amp; sudo make install&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;All done! - ImageMagick should be all installed. To check, run the command
&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;which identify&lt;/code&gt;, and check it returns a path to the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;identify&lt;/code&gt; command.
Note: If you have trouble compiling, make sure Ubunutu’s build tools are
installed: &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;sudo apt-get install build-essential&lt;/code&gt;&lt;/p&gt;

</description>
        <pubDate>Thu, 16 Jun 2011 00:00:00 +1200</pubDate>
        <link>https://joshmcarthur.com/2011/06/16/ubuntu-quick-imagemagick-install.html</link>
        <guid isPermaLink="true">https://joshmcarthur.com/2011/06/16/ubuntu-quick-imagemagick-install.html</guid>
        
        
      </item>
    
      <item>
        <title>Integration tests with Devise and RSpec</title>
        <description>&lt;p&gt;RSpec 2 has supported integration tests for a while now, and I’ve chosen to use
these for a project I’m working on at the moment instead of Cucumber (I don’t
feel that I need the verbosity andÂ English-like structure Cucumber provides
given that it a more complex process to write tests).
A bit of a problem I’ve come across recently is how to get a Devise user signed
in to your application in your tests, given that integration tests don’t really
give you any access to either the session or the controller (This rules out
manually setting ID’s in the session, or stubbing out current_user. As it turns
out, the implementation of it is pretty simple, but I did have to do a bit of
browsing and piece a few bits together to work out a nice way of doing it.
Here is the code you can use (You would normally place this within a before(:
each) filter in your routes spec (Which is what an integration test in RSpec is
called):&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-ruby&quot; data-lang=&quot;ruby&quot;&gt;&lt;span class=&quot;c1&quot;&gt;# At top of spec file, after require &apos;spec_helper&apos;&lt;/span&gt;
&lt;span class=&quot;kp&quot;&gt;include&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;Warden&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;Helpers&lt;/span&gt;

&lt;span class=&quot;c1&quot;&gt;# In a before(:each) block&lt;/span&gt;
&lt;span class=&quot;vi&quot;&gt;@user&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;Factory&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;create&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;ss&quot;&gt;:user&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;vi&quot;&gt;@user&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;confirm!&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;login_as&lt;/span&gt; &lt;span class=&quot;vi&quot;&gt;@user&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;ss&quot;&gt;:scope&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;ss&quot;&gt;:user&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;p&gt;Now, what is this doing? Well, first of all, you include some test helpers that
Warden (Which devise back-ends onto), provides. I tried out using Devise’s
Devise::TestHelpers here, but it looks like Devise haven’t really designed
their helpers with integration tests in mind - they didn’t really work.Â 
Within our before(:each) block, it now gets pretty simple. We create a User,
using Factory Girl (This part of the implementation doesn’t really matter, you
can use fixtures if you prefer, or even a plain old User.create.
Next, we confirm the user. This isn’t necessary if your users haven’t been
marked as :confirmable in your User model, but obviously our new user needs to
be confirmed and active in order to log in.
Last of all, we use a helper method Warden’s test helpers has provided us to
log in the user, which sets us up for any more requests we need to make.
Have fun speccing nicely with Devise/Warden and RSpec!&lt;/p&gt;

</description>
        <pubDate>Sat, 11 Jun 2011 00:00:00 +1200</pubDate>
        <link>https://joshmcarthur.com/2011/06/11/integration-tests-with-devise-and-rspec.html</link>
        <guid isPermaLink="true">https://joshmcarthur.com/2011/06/11/integration-tests-with-devise-and-rspec.html</guid>
        
        
      </item>
    
      <item>
        <title>Quick: Get random record efficiently in Rails</title>
        <description>&lt;p&gt;You could use SQL’s random function (RAND() or RANDOM() depending on database
engine) - but this isn’t database agnostic, so isn’t really very quick.
Instead you can use @nzkoz’s suggested method:
Widget.first(:offset =&amp;gt; Widget.count)
…. the count() method is fast, and the first() method will limit it to the
first result in the SQL as well.&lt;/p&gt;

</description>
        <pubDate>Tue, 07 Jun 2011 00:00:00 +1200</pubDate>
        <link>https://joshmcarthur.com/2011/06/07/quick-get-random-record-efficiently-in-rails.html</link>
        <guid isPermaLink="true">https://joshmcarthur.com/2011/06/07/quick-get-random-record-efficiently-in-rails.html</guid>
        
        
      </item>
    
      <item>
        <title>ImageMagick: Cropping then Resizing a PNG</title>
        <description>&lt;p&gt;I’ve been working on an image processor class for work, and recently ran into
this issue. I thought I would post it up here as normally I need to be quite
desperate before I start trawling through email mirrors - hopefully somebody
comes across this post first.
If you use ImageMagick (in particular, in conjunction with MiniMagick), then
you may come across this issue, and there is actually a quick fix to it. The
issue itself is as follows: my image processor retrieves an image from an
online source, but the image has a 1 or 2px border, and is thousands of pixels
wide - too wide for web use. I therefore wrote this class to first crop the
border off the image, and then resize it.
If you do actually do this though, there is an important step that needs to go
in-between the cropping and the resizing. If you won’t do this, you will
basically get a PNG layer that is offset from the image itself - i.e. some or
all of the Â actual image content is not visible. This happens because when the
image is cropped, the origin of the image changes. It needs to be reset back to
coordinates 0, 0 in order to not offset the layer itself when the image is
resized.
Here’s how to do it.Â 
For Ruby/Minimagick:
image.set(“page”, “#{image[‘width’]}x#{image[‘height’]}+0+0”)
For ImageMagick directly (i.e. in console):
convert [image sequence]: -set page [width]x[height]+0+0
Simple! The image should save correctly.&lt;/p&gt;

</description>
        <pubDate>Fri, 06 May 2011 00:00:00 +1200</pubDate>
        <link>https://joshmcarthur.com/2011/05/06/imagemagick-cropping-then-resizing-a-png.html</link>
        <guid isPermaLink="true">https://joshmcarthur.com/2011/05/06/imagemagick-cropping-then-resizing-a-png.html</guid>
        
        <category>mini_magick</category>
        
        <category>image_magick</category>
        
        <category>png</category>
        
        <category>image resizing</category>
        
        
      </item>
    
      <item>
        <title>Using setInterval to handle scroll() events</title>
        <description>&lt;p&gt;I’ve just added a nice unobtrusive scroll to top feature to my blog, and learnt
an interesting tip in the process I thought I would share, originating from one
of the many problems Twitter has had with it’s jQuery fanciness.
The scroll to top stuff isn’t overly complicated - just detect when the user
has scrolled x pixels down the screen, and then show a link to go back to the
top (Which can be animated if you want) - for a nice tutorial on this, check
out_this_tutorial.
The thing is that this technique uses the scroll event (bound to window, but
can be bound to basically anything with a scrollbar). The scroll event gets
fired any time the window is scrolled - but any time the scroll amount changes
at all. What this means is that the scroll event is fired any time scroll
position changes - so if you grab the scrollbar, and pull, the scroll event is
fired every time the bar moves, not when you release the mouse.
John_Resig_reports_that_Twitter_ran_into_some_problems_with_thisÂ - they had a
function bound directly to the scroll event being fired every time anyone
scrolled (at all!). Resig has also reported a nice solution though, which I’ll
pass on here. It’s actually quite simple. Instead of binding a complicated
function to the scroll event, you simply set a flag variable to let something
else know that the window has been scrolled. The second piece of the solution
is a function running via setInterval - that is, a function being called on a
scheduled basis. The first task of this function is to check this flag - if it
is set, it can perform any task (Such as showing a ‘Back to Top’ link, and set
the flag variable back to false.Â 
The end result of this solution is that you basically have a polling function,
rather than an event-driven one. This is actually a good thing though, when it
comes to this type of event. Instead of having a complex function called every
time the window is scrolled, a function is called every quarter second or so,
and only executes if the window has been scrolledÂ - much better!
I personally used an object variable, rather than just a flag - this object was
populated with the object that was scrolled on, and I then checked if this
object was populated in my polling function (rather than just is true), and
could then use the context it provided in this function. Since then, however, I
have realized that in my case, this object will alwaysbe the window object - so
I may as well have a cheaper variable assignment and just directly refer to the
object in my polling function.&lt;/p&gt;

</description>
        <pubDate>Tue, 12 Apr 2011 00:00:00 +1200</pubDate>
        <link>https://joshmcarthur.com/2011/04/12/using-setinterval-to-handle-scroll-events.html</link>
        <guid isPermaLink="true">https://joshmcarthur.com/2011/04/12/using-setinterval-to-handle-scroll-events.html</guid>
        
        
      </item>
    
      <item>
        <title>Accessing controller instance variables in model (Urgh?)</title>
        <description>&lt;p&gt;I can tell that this title alone will irritate a lot of developers out there.
It irritated me as well, until I figured out that sometimes, doing things the
‘wrong’ way is the best/only way. But let me get on with things.
Every now and then, you will come across a scenario where you need to access
something in the controller, from the model. This is so obviously non-MVC that
many will (should) immediately shy away from it, but let me suggest a scenario.
Let’s say you have users, who should be scoped to a particular subdomain. You
would place a default_scope on the User model (with a Proc, once an issue with
default_scope is resolved), that would automatically add conditions to any
database lookup calls to restrict the found users to those belonging to the
current subdomain. The tricky bit is, of course to get this subdomain. Because
it is a default scope, you don’t actually ‘call’ it - so that rules out passing
parameters. I’ve come across other suggestions as well, such as using
cattr_accessor (NO! It’s shared across requests), and storing a value in
Thread.current (Relying on the application using threads in the way you expect
them to = not safe). So far though, the best hint I have seen here, although
extremely anti-doing what I am suggesting, would seem to work.Â 
Basically, the jist of this post was that you could dynamically add methods to
ActiveRecord::Base using an around_filter to access sessions, cookies, params
and the request. Now, I agree that this might be a bit too extreme - if you
really need to access the entire session or cookie hash, for example, you
probably aredoing something wrong. Something that I think isÂ suitable though,
given an appropriate situation, is to use this technique to add an ‘accessor’
to ActiveRecord::Base that returns the value you need in your model - the
current subdomain for instance. This is still non-MVC, but is, I think, a far
more elegant solution that anything else I’ve seen suggested.&lt;/p&gt;

</description>
        <pubDate>Tue, 12 Apr 2011 00:00:00 +1200</pubDate>
        <link>https://joshmcarthur.com/2011/04/12/accessing-controller-instance-variables-in-model-urgh.html</link>
        <guid isPermaLink="true">https://joshmcarthur.com/2011/04/12/accessing-controller-instance-variables-in-model-urgh.html</guid>
        
        
      </item>
    
      <item>
        <title>I18n ALWAYS escapes dots in chained backends :-(</title>
        <description>&lt;p&gt;Just a quick tip I’ve come across while browsing through the comments on a
Railscast_I’ve_been_watching. It looks like the I18n gems that get
automatically installed with Rails 3 have a teensy bug.
When storing a translation to the backend (be it YAML, Redis, whatever), there
is an :escape option that can be passed to enable (or prevent) the key from
containing the dot character (.) (Among others, I’m sure). This seems to work
fine when dealing with a single backend, but unfortunately when you are using
chained backends (For example I am using Redis preferentially, but falling back
on YAML when necessary), the options hash that you would pass the :escape
option in is mistakenly initialized to an empty hash.
It’s a simple fix - there is already a closed pull request on the issue here.
It looks like the latest stable version is only 0.50 though, so the fix isn’t
going to filter through to the actual gem just yet. For now, the best option is
probably to class_eval into that particular section, or just change the file
yourself.&lt;/p&gt;

</description>
        <pubDate>Fri, 08 Apr 2011 00:00:00 +1200</pubDate>
        <link>https://joshmcarthur.com/2011/04/08/i18n-always-escapes-dots-in-chained-backends.html</link>
        <guid isPermaLink="true">https://joshmcarthur.com/2011/04/08/i18n-always-escapes-dots-in-chained-backends.html</guid>
        
        
      </item>
    
      <item>
        <title>Spree Hosted Gateway</title>
        <description>&lt;p&gt;Spree Hosted Gateway is my second ‘big’ extension - one of the ones that isn’t
just adding one or two bits of nice functionality, but actually and end-to-end
solution to add something that I think would be useful to a broad range of
Spree developers (My first was spree-import-products, see my post on it here).
Spree (for those of you unfamiliar with it), is an open-source eCommerce
framework written in Ruby on Rails, and has recently been upgraded to Rails 3,
which has enabled extensions to now be packaged as Rails engines, making them
far easier to create, implement and share. Out of the box, Spree is able to
provide much of what a basic online store requires, but something I have felt
is missing is support for alternative payment methods that are much more
rudimentary than those catered for by Railsdog’s_fork_of_ActiveShipping.Â 
Inspired by a project I was working on at 3Months, I decided to develop an
extension that provided a new type of Payment Method (Alongside Cheque and
Creditcard) - External (Or Hosted) Gateway - this type of payment method was
characterized in the following ways:
    * Redirection - the customer was required by the gateway to be redirected
      to an off-site form to make payment.
    * Tracing the payment and order: attributes required for payment (Amount,
      etc.) were gathered by the payment gateway from POSTed form data.
    * Communications: there was no type of complex back-and-forth between Spree
      and the gateway provider behind the scenes - information was transmitted
      to the gateway, and all Spree is able to do from that point is listen for
      a postback and collect any information from that.
These type of gateways are (unfortunately) quite prevalent, especially around
small-to-medium independent companies - they tend to be cheaper than more
widely-known solutions, at the expense of a more reliable and secure
transaction for the customer.
Spree Hosted Gateway, as I have mentioned, hooks into Spree in the form of
registering itself as a Payment Method that can be configured from the admin
interface. After seeing all theÂ arbitraryÂ information that I was required to
POST to the payment gateway I was testing against, I also made it easily
extendable, and used Spree’s preferences system to allow any number of key-
value pairs to be created (Which could be modified in the Spree Admin UI),
which are automatically posted to the payment gateway when I customer is
redirected (Unless the preference key is added to the exclusion list, of
course!).
I conjunction to the ‘transmit’ function of the extension, I have also
implemented a kind of ‘landing pad’ for postbacks from the Payment Gateway to
hit. When this happens, the extension is able to detect whether the transaction
was successful or not, and continue processing the order. If the customer has
made an payment that is not sufficient to cover the cost of the order they are
redirected back to the payment page, and if the transaction was not successful,
an error message is presented to them.
All in all, I’m happy with this extension - it was a great way to learn about
how Spree itself handles payment methods, different types of gateway and
payment validation and handling, and I’m hoping this extension will help out
some of the Spree developers out there working on smaller stores that don’t
need such a large (expensive) gateway.&lt;/p&gt;

</description>
        <pubDate>Thu, 10 Mar 2011 00:00:00 +1300</pubDate>
        <link>https://joshmcarthur.com/2011/03/10/spree-hosted-gateway.html</link>
        <guid isPermaLink="true">https://joshmcarthur.com/2011/03/10/spree-hosted-gateway.html</guid>
        
        
      </item>
    
      <item>
        <title>Running Rake tasks with Cron (RVM)</title>
        <description>&lt;p&gt;Recently I’ve had to deal with a strange problem with rake tasks being run
using Cron, a UNIX tool for running commands on a scheduled basis. The problem
was basically the server being slowly strangled of resources across a forty to
fifty minute time period, as each time the rake task was run it would leave an
entire bash process sitting there consuming resources.
I had assumed that this problem was relating to the rake task exiting in a non-
normal fashion - an exception maybe, or just not returning something that bash
was requiring to say ‘OK, I can close now’. As it turned out, the solution was
even more basic than that.
The server is question is a bit of a prototype, as it’s using a system-wide
installation of RVM to mange two very different sets of gems. To help
integration with Passenger, each project directory has an .rvmrc file that
automatically sets the correct Ruby version and Gemset when that directory is
entered. RVM has a neat security feature however, that prompts you to view and
approve the file before it is executed for the first time - and, you guessed
it, this is what was tripping up Cron.
What was happening, is that Cron was changing directory to the release path in
order to execute the rake task - but not getting as far as actually executing
the Rake task. Instead, Cron was being presented with a security warning from
RVM, which was very cleverly sitting there waiting for input to approve the
file - causing Bash to sit there… for ever.
Fortunately, I did find a solution - it is possible to turn this security
mechanism file off, to stop it from prompting for approval of the .rvmrc file,
by adding a configuration flag to the default rvmrc file in /etc/rvmrc - here’s
the configuration flag:
export rvm_trust_rvmrcs_flag=1
(It goes inside what should be an if block).
From now on, when Cron runs the rake tasks, it won’t be presented with this
prompt - approval will be assumed. No more server resources limits exceeded
alerts!&lt;/p&gt;

</description>
        <pubDate>Wed, 09 Mar 2011 00:00:00 +1300</pubDate>
        <link>https://joshmcarthur.com/2011/03/09/running-rake-tasks-with-cron-rvm.html</link>
        <guid isPermaLink="true">https://joshmcarthur.com/2011/03/09/running-rake-tasks-with-cron-rvm.html</guid>
        
        
      </item>
    
      <item>
        <title>OK, So I Was Wrong</title>
        <description>&lt;p&gt;OK,_So_I_Was_Wrong
So I was just flipping through my archives of blog posts, when I found this_one&lt;/p&gt;
&lt;ul&gt;
  &lt;li&gt;my first ever post to Tumblr, where I vowed to stop posting enormously long
essays on silly ideas I have to change the world.
Oops.
Sorry everyone!&lt;/li&gt;
&lt;/ul&gt;

</description>
        <pubDate>Mon, 07 Mar 2011 00:00:00 +1300</pubDate>
        <link>https://joshmcarthur.com/2011/03/07/ok-so-i-was-wrong.html</link>
        <guid isPermaLink="true">https://joshmcarthur.com/2011/03/07/ok-so-i-was-wrong.html</guid>
        
        
      </item>
    
      <item>
        <title>Dynamically Serving Inline Images with Rack</title>
        <description>&lt;p&gt;Following an insanely interesting presentation by Steve Souders at Webstock, I
began thinking about ways to simplify (i.e. automate) certain processes to
optimize performance of sites. Something that immediately jumped out at me from
early on in Steve’s workshop (So early, I was able to start coding in the
halfway break), was that inline images were an ideal candidate for something
that is relatively easy to do, but could have a significant effect on overall
page load speed.
The concept of inline images has been around for a while, and basically
involves embedding Base64-encoded image data within the image tag like so:&lt;/p&gt;
&lt;pre&gt;
&lt;img src=&quot;data:image/jpeg;base64,[data]&quot; /&gt;
&lt;/pre&gt;

&lt;p&gt;So, what impact does this have on performance vs. the normal technique?
Well, the normal technique involves several more requests - one per image, in
fact. As Steve pointed out in his workshop, server response time is only a
small part of the problem - the reality is that it doesn’t make a huge
difference how fast a piece of content is processed on the server - unless you
are doing something really wrong, of course. Where you can really hit
performance bottlenecks is actually in the request-response transaction -
waiting for data to be transmitted, received, processed, assembled, and
rendered on screen.
Using inline images can improve web performance when used appropriately, but as
with any other performance technique, it has it’s drawbacks. First of all,
because the images are actually embedded into the page, caching is affected -
browsers may cache images longer than static pages, and if your web server is
not set up correctly, dynamic pages may not be cached at all. Next, the
relative file size of the page is not equal to the page size without images
plus the combined images file size. The encoding process tends to result in
about a 30% increase in size.
Notice above I have said when used appropriatelyÂ - this means that, despite
the drawbacks above, it really can help web performance. The key to not
overusing this technique is to carefully consider the boundaries of what images
should be embedded - the best candidates are small to medium sized images that
do not generally change much - especially if there are quite a few of them on
the page and the server is slow - this means that your page load speed might be
slower, but there will also be no need to make several requests once the page
hasÂ loaded to draw in all of these assets. Large images, on the other hand,
should not generally be encoded. There is a fine balance between embedding
images into a page to save on requests, and overloading the page with embedded
images and ending up with a 2 or 3 megabyte page for the client to load. Large
images will shove up this page size, and in addition to this, they are usually
particular files that you want to apply a different caching policy to anyway.
In order to help out and generally feel good about my contributions to web
performance and open source, I have actually translated what I’ve written down
here into a piece of Rack middleware that creates inline images (Since I’ve
come across rack-pagespeedÂ since then, this middleware is probably
superseded), and embeds them into a page. This technique was nice because a) It
gave me a chance to learn how Rack middleware works with a practical project,
and b) because when myself or others wish to use it, all that needs to be done
is to drop the Gem into my Gemfile, run bundle install, and register the
middleware in my Application’s configuration - from there on, the middleware
will automatically parse responses from the Rails (Or any Rack-compatible
application, actually), and replace the &lt;img src=&quot;file.jpg&quot; /&gt; with encoded data.
Overall, this technique is not perfect - it’s definitely not something to be
overused, but with careful consideration and appropriate selection of images
it’s a good stepping stone to improved web performance.&lt;/p&gt;

</description>
        <pubDate>Fri, 04 Mar 2011 00:00:00 +1300</pubDate>
        <link>https://joshmcarthur.com/2011/03/04/dynamically-serving-inline-images-with-rack.html</link>
        <guid isPermaLink="true">https://joshmcarthur.com/2011/03/04/dynamically-serving-inline-images-with-rack.html</guid>
        
        
      </item>
    
      <item>
        <title>Creating a Scalable Academic Signup System</title>
        <description>&lt;p&gt;While working at 3Months as a developer, I am also completing a Bachelor of
Business Information Systems at Victoria University. One of the things that
rolls around twice a year and always causes students headaches is the signups
systemÂ - it’s one of the bragging points of the School of Information
Management at Victoria, as it was build as a project by a group of students
there (Hence the name ‘S-Cubed = SSS = SIM Signups System). In itself, it’s a
good application - it fulfills it’s brief of managing signups for tutorials and
workshops, and lecturers and students alike seem to have no huge issues using
is. The problem is though, is that its performance under load is crap - it
simply was not build for the load that it receives when tutorial signups for 2
courses of 700 people each opens at 9am, and each of those 1400 people trying
to get the exact tutorial they want are continually refereshing the page and so
exacerbating the server load even more.&lt;/p&gt;

&lt;p&gt;This server load doesn’t just extend to slow page load speed - since the site
has not been build with scalability in mind, there have been ongoing issues
with sessions expiring as people try and signup for a tutorial or workshop,
people stuck halfway through a transaction, and the server even goes down every
now and then.
What I am proposing is an open framework that takes the best aspects of this
type of system, and builds upon it techniques to optimize page load speed, and
increase scalability. It seems to me that Universities in general are lacking a
system that allows them to smoothly run signups (usually they are dependent on
modified forum software, paper signups, emails or proprietary in-house
systems), and so this isÂ definitelyÂ something that is marketable around the
world.
As I see it, here are the base requirements for such a system:
    * LDAP and OAuth Authentication so that students do not need to ‘register’
      to sign up for a tutorial
    * Accurate time-based releases of tutorials and workshops that do not
      require human intervention
    * Automatic server load monitoring with the ability to increase allocated
      resources if necessary (i.e. this application is an ideal candidate for
      Rackspace Cloud or Heroku)
    * Per-course and per-University analytics for better scheduling of tutorial
      and workshop release times and how students are accessing the system.
    * The ability for lecturers and course administrators to export and print
      class lists, tutorial lists, and workshops list with all student data or
      just a subset.
This project isÂ definitelyÂ going on theÂ work pileÂ for my personal projects&lt;/p&gt;
&lt;ul&gt;
  &lt;li&gt;it’s got a couple of interesting aspects to it which I haven’t approached
before (Such as LDAP from Rails), and generally seems to follow along with my
ethos of community-oriented ‘responsible web applications’.&lt;/li&gt;
&lt;/ul&gt;

</description>
        <pubDate>Fri, 04 Mar 2011 00:00:00 +1300</pubDate>
        <link>https://joshmcarthur.com/2011/03/04/creating-a-scalable-academic-signup-system.html</link>
        <guid isPermaLink="true">https://joshmcarthur.com/2011/03/04/creating-a-scalable-academic-signup-system.html</guid>
        
        
      </item>
    
      <item>
        <title>Radiant as a Service-delivery Platform</title>
        <description>&lt;p&gt;Tonight I’ve been playing round with Radiant - it looks really impressive as a
content-management system, and I’ve been thinking about ways I can change it
round to support vendors - that is, one person or organization offering Radiant
as a service to multiple clients - basically, it would be one big Radiant
application (to simplify administration and server maintenance), but would be
scoped (for administrative users) just to that organization - nothing would be
different from them, aside from the fact that the vendor can use one instance
of the application to create, modify and maintain many organizations’ Radiant
content management systems.Â 
My motivation for doing this is to try and create something I thought of a bit
earlier today - a service to help low-level, low-budget schools (Primary to
Intermediate) to move services and content online in a way that they can manage
themselves. Radiant seems to be an excellent option in this regard - supporting
diverse and flexible themes, and with plenty of extensions to offer additional
functionality for little work. What I want, though, is a sort of dashboard for
myself - I want to have a nice interface to have overall control over each
installation, and for me to administer the site myself if that is a client’s
preference. In addition, by using the method of scoping a ‘site’ to an
organization, some cool things are possible, like an online signup wizard that
would allow a client to set up a site without any initial involvement from me -
because it is just records being inserted into a database, and not an entire
application being created, this type of process becomes very easy.
It’s early days yet, of course, but something in the educational area is what
I’ve been wanting to tackle for a while now - let’s see how it goes.
For more information about Radiant CMS, seeÂ http://radiantcms.org/
For more information about what I am doing, follow my github repository:
Â https://github.com/joshmcarthur/radiant-for-vendor&lt;/p&gt;

</description>
        <pubDate>Thu, 23 Dec 2010 00:00:00 +1300</pubDate>
        <link>https://joshmcarthur.com/2010/12/23/radiant-as-a-service-delivery-platform.html</link>
        <guid isPermaLink="true">https://joshmcarthur.com/2010/12/23/radiant-as-a-service-delivery-platform.html</guid>
        
        
      </item>
    
      <item>
        <title>So I decided this evening to devote a couple of hours to giv...</title>
        <description>&lt;p&gt;So I decided this evening to devote a couple of hours to giving back to Spree,
after using the framework for a couple of work projects, and appreciating it
for it’s flexibility and extensibility.Â 
I’ve developed an extension for Spree called Import Products. The source is
based on a few bits of code that I have used for 2 projects now to import
products into a Spree store from a comma-seperated values file (.CSV).
It’s not perfect, but I’m happy with it as my first ever full contribution back
to the Ruby on Rails community - I hope I have done a good enough job that it
is useful to a couple of people.
If you want to check out the source code, it’s on github.
If you want to check out a description of the extension, check out the Spree
extension_page.&lt;/p&gt;

</description>
        <pubDate>Mon, 13 Dec 2010 00:00:00 +1300</pubDate>
        <link>https://joshmcarthur.com/2010/12/13/so-i-decided-this-evening-to-devote-a-couple-of-hours-to-giv.html</link>
        <guid isPermaLink="true">https://joshmcarthur.com/2010/12/13/so-i-decided-this-evening-to-devote-a-couple-of-hours-to-giv.html</guid>
        
        <category>development</category>
        
        <category>spree</category>
        
        <category>rails</category>
        
        
      </item>
    
      <item>
        <title>RVM, Ruby-Debug, and hours of frustration</title>
        <description>&lt;p&gt;Every now and then, you run into one of THOSE problems - inexplicable, and
something it seems no one else has solved before you. This afternoon, I hit one
of these. Basically, I had just installed RVM, Ruby_1.8.7 and Rails_2.3.8 -
everything was working great - I could run a server just fine, and use the
running site.
Eventually though, I found something broken. Easy enough to diagnose, I thought&lt;/p&gt;
&lt;ul&gt;
  &lt;li&gt;I’ll just start up a debugging server, and work out whats going on.
Wrong. Wrong, wrong, wrong.
What followed was a continuation of this error:
You need to install ruby-debug to run the server in debugging mode.
So I had a Google - lots of people have run into this problem before (here and
here specifically) - the problem is, most of these problems came down to a
missing library called libreadline - for them, installing the library corrected
all of their problems.
In the end, I stumbled across this_post, from a guy having issues with Ruby
after upgrading his Mac OS X - finally - he suggested something I hadn’t
already tried.
And, as it turns out - it worked! I have no idea why - but there we go.
If you are having a problem getting Ruby 1.8.7 to acknowledge ruby-debug is
there when running a debugging server, try this:
gem uninstall ruby-debug gem install ruby-debug linecache –no-ri –no-rdoc
script/server –debugger
… It might just work for you!&lt;/li&gt;
&lt;/ul&gt;

</description>
        <pubDate>Tue, 14 Sep 2010 00:00:00 +1200</pubDate>
        <link>https://joshmcarthur.com/2010/09/14/rvm-ruby-debug-and-hours-of-frustration.html</link>
        <guid isPermaLink="true">https://joshmcarthur.com/2010/09/14/rvm-ruby-debug-and-hours-of-frustration.html</guid>
        
        <category>rails</category>
        
        <category>rvm</category>
        
        <category>ruby-debug</category>
        
        <category>linecache</category>
        
        
      </item>
    
      <item>
        <title>Rubbish Domain Names</title>
        <description>&lt;p&gt;Finding appropriate domain names is a chore. There are so many rubbish parking
sites out there that just … shouldn’t be. I dare sayjoshmcarthur.me could be
considered one of them, since I could easily offload this onto Tumblr, but I
enjoy having the unlimited capabilities a hosted site allows.
I think that domains should be treated in a similar way to Patents when they
are registered. Patents require the applicant to justify to an overall
authority (the Intellectual_Property_Office in NZ), why they should be allowed
to own a particular patent.
If there was a justification scheme in place, it would not restrict the people
who need domains - they would simply have to specify why they need the domain.
What it would do is provide a decision framework on which to base decisions on
registering domains to domain agents.
Why tie up a perfectly good address? Leave them open, and truly make it first-
in, first-served, instead of the ‘richest wins’ model that exists at the
moment.&lt;/p&gt;

</description>
        <pubDate>Fri, 20 Aug 2010 00:00:00 +1200</pubDate>
        <link>https://joshmcarthur.com/2010/08/20/rubbish-domain-names.html</link>
        <guid isPermaLink="true">https://joshmcarthur.com/2010/08/20/rubbish-domain-names.html</guid>
        
        
      </item>
    
      <item>
        <title>Time Problems</title>
        <description>&lt;p&gt;Fixing time problems is probably one of the most satisfying things to fix
programmatically for me. It is one of those things where you may spend ages
stabbing in the dark with different time zones, formats, etc etc. - but there
it really is satisfying when you do get it right and what you see in your code
is what you see on your clock.
Weird, but true.&lt;/p&gt;

</description>
        <pubDate>Wed, 18 Aug 2010 00:00:00 +1200</pubDate>
        <link>https://joshmcarthur.com/2010/08/18/time-problems.html</link>
        <guid isPermaLink="true">https://joshmcarthur.com/2010/08/18/time-problems.html</guid>
        
        
      </item>
    
      <item>
        <title>Interesting Ideas</title>
        <description>&lt;p&gt;As part of my research for my last-minute essay rewrite, I happened to pick up
an article covering an interview with David_Heinemeier_Hansson, partner at
37signals.com and inventor of Ruby_on_Rails.
Hansson had some very interesting ideas, including the concept of reducing
working hours in favour of creative quality, and the value of constraints over
capital: “Excellent decisions come out of constraints…You must make
compromises and decisions to cut down and release something that’s simple and
easy to use”.
Hansson also had some interesting thoughts regarding the virtual workplace,
suggesting that employees are happier in an environment of their choice, and a
relaxed managerial style that reduces overhead and emphasises quality over
quantity.
Definitely somebody to keep an eye on!&lt;/p&gt;

</description>
        <pubDate>Wed, 11 Aug 2010 00:00:00 +1200</pubDate>
        <link>https://joshmcarthur.com/2010/08/11/interesting-ideas.html</link>
        <guid isPermaLink="true">https://joshmcarthur.com/2010/08/11/interesting-ideas.html</guid>
        
        
      </item>
    
      <item>
        <title>A handy tip</title>
        <description>&lt;p&gt;Just FYI…
See 2 posts ago - I am modifying this theme to make it work a bit nicer. Part
of this included wanting to change some images. I was trying to just link to
the images in CSS, but I found a much nicer way of doing it.
If you want to change your theme currently, you can often change a range of
colors. There is a technique that allows you to prompt the user to upload an
image file, rather than choosing a file:
First, set up the parameter that the settings drop down will look for by making
a meta tag in the document head that has it’s name set as “image:
YourTemplateImage”, and the content set as the link to the image that should be
used as default.
You can then use this image by referring to it in your CSS or HTML - for
example:
#content { background: #FFF url({image:YourTemplateImage}) no-repeat top left;
}
Very nice! Cheers to this_blog_post for the link.&lt;/p&gt;

</description>
        <pubDate>Tue, 10 Aug 2010 00:00:00 +1200</pubDate>
        <link>https://joshmcarthur.com/2010/08/10/a-handy-tip.html</link>
        <guid isPermaLink="true">https://joshmcarthur.com/2010/08/10/a-handy-tip.html</guid>
        
        
      </item>
    
      <item>
        <title>My new look...</title>
        <description>&lt;p&gt;Do you like my revamped theme? I think it’s totally cool to have this type of
timeline in a microblog like Tumblr - it makes things really easy to flip
through… don’t you want to keep pressing the arrows?
It’s this_theme, but I tweaked the CSS a bit and added some jQuery to bind
left/right scrolling to the arrow keys as well as the buttons.&lt;/p&gt;

</description>
        <pubDate>Mon, 09 Aug 2010 00:00:00 +1200</pubDate>
        <link>https://joshmcarthur.com/2010/08/09/my-new-look.html</link>
        <guid isPermaLink="true">https://joshmcarthur.com/2010/08/09/my-new-look.html</guid>
        
        
      </item>
    
      <item>
        <title>Here&apos;s how this works...</title>
        <description>&lt;p&gt;Right team, listen up.
See, I’m quite a lazy blogger - it’s just too much effort, and it never seems
to be shiny enough (Just check out my old_blog out for proof.
I’m going to make this easy for all of us - I’ll find cool stuff on the
Internet, or even in the Real_World - and I’ll share it here. It might be a
sentence, it might be a couple of paragraphs. No more pages-long blog posts for
me!&lt;/p&gt;

</description>
        <pubDate>Sun, 08 Aug 2010 00:00:00 +1200</pubDate>
        <link>https://joshmcarthur.com/2010/08/08/here-s-how-this-works.html</link>
        <guid isPermaLink="true">https://joshmcarthur.com/2010/08/08/here-s-how-this-works.html</guid>
        
        
      </item>
    
  </channel>
</rss>
