Erin CallYou Can't Spell Engineering Without Erintag:blog.erincall.com,2008:/posts2016-04-05T18:15:00ZSigning Your Git Commits With GPGtag:blog.erincall.com,2008:p/signing-your-git-commits-with-gpg2016-04-05T18:15:00Z<p>Github has just announced <a href="https://github.com/blog/2144-gpg-signature-verification">GPG signature verification</a>, which helps verify that commits made in someone&#39;s name were indeed made by that person. It&#39;s long been a dirty secret of git that <a href="http://www.jayhuang.org/blog/pushing-code-to-github-as-linus-torvalds/">you can impersonate anyone with minimal effort</a>, so GPG verification adds a nice layer of assurance. Now that I&#39;ve gone through the hassle of setting up automatic commit signatures, here&#39;s how you can do it too.</p><p>Before you can sign keys with GPG, you&#39;ll need a GPG keypair. The ins and outs of GPG key generation are nothing I want to write about, but <a href="https://help.ubuntu.com/community/GnuPrivacyGuardHowto">this guide for Ubuntu</a> should give you what you need (it&#39;ll behave the same on OSX, except you&#39;ll have to install GPG yourself).</p><p>Once you have a keypair, you&#39;re ready to start signing. The simplest way to sign a commit with git is with <code>git commit --gpg-sign</code>, but adding that option manually will get real old real fast. Instead, you can configure git to sign <i>all</i> commits: <code>git config --global commit.gpgsign true</code> (yes, it&#39;s <code>gpg-sign</code> at the command line and <code>gpgsign</code> in your config). At this point, if your committer email matches the email in your gpg key, you&#39;re off and running. All your commits will be signed and they&#39;ll show up as verified on Github.</p><p><img src="https://cdn.erincall.com/9b3ee7e9db7ea445d56df479ed10859d43b717e2" alt="committing with --gpg-sign"></p><p>There&#39;s a fly in the ointment, though: you now have to type your GPG passphrase every time you commit. However, GPG ships with a background daemon to solve this problem, and despite being a GPG tool it&#39;s not too hard to get it running. Have some Fish (or <a href="https://github.com/ErinCall/Dotfiles/blob/master/.bashrc#L32-L40">Bash, if you must</a>):</p><pre><code class="Fish">if not begin
# Is the agent running already? Does the agent-info file exist, and if so,
# is there a process with the pid given in the file?
[ -f ~/.gpg-agent-info ]
and kill -0 (cut -d : -f 2 ~/.gpg-agent-info) ^/dev/null
end
# no, it is not running. Start it!
gpg-agent --daemon --no-grab --write-env-file ~/.gpg-agent-info &gt;/dev/null ^&amp;1
end
# get the agent info from the info file, and export it so GPG can see it.
set -gx GPG_AGENT_INFO (cut -c 16- ~/.gpg-agent-info)
set -gx GPG_TTY (tty)</code></pre><p>If you add this to your <code>config.fish</code> (or add the bash version to your <code>.bash_profile</code>), then all your terminals will have access to a gpg agent. However, GPG won&#39;t automatically connect to it. When invoking GPG directly, you can pass <code>--use-agent</code>, but git doesn&#39;t expose that option. Instead, add the line <code>use-agent</code> to your <a href="https://github.com/ErinCall/Dotfiles/blob/master/.gnupg/gpg.conf#L199"><code>~/.gnupg/gpg.conf</code></a>. Now GPG will look for an agent whenever it&#39;s invoked.</p><p>If you want to adjust how long the agent holds onto your password, adjust the <code>default-cache-ttl</code> setting in <a href="https://github.com/ErinCall/Dotfiles/blob/master/.gnupg/gpg-agent.conf"><code>~/.gnupg/gpg-agent.conf</code></a>. The setting is expressed in seconds, so if you want to type your password once per hour, set it to 3600.</p><p>There&#39;s one other thing that&#39;ll annoy you. Behind the scenes, <code>git stash</code> creates a commit. That means that you&#39;ll have to unlock your GPG key in order to stash. I&#39;ve created a <code>stache</code> alias that stashes without signing: <code>git config --global alias.stache=&#39;!git -c commit.gpgsign=false stash&#39;</code>. We&#39;ll see how reliably I remember to use it.</p><p>GPG signing adds an extra layer of insurance, particularly in this age of automated builds and package distribution. Although just showing a &quot;verified&quot; box is a small feature, I expect it&#39;ll help push adoption. As signed commits become more common, I&#39;m excited to see how various sites and tools start using them. Maybe Github will even allow <a href="https://twitter.com/bunsen/status/694527077266104321">locking down your contribution graph</a>...</p>What's The Deal With SSL?tag:blog.erincall.com,2008:p/whats-the-deal-with-ssl2015-10-08T01:30:00Z<p>SSL is an important part of the infrastucture of the Internet. It provides three assurances: The computer you&#39;re talking to is the one you meant to talk to, the message you&#39;re reading was really sent by the computer you&#39;re talking to, and the message you&#39;re reading hasn&#39;t been read by anyone else. I&#39;ve <a href="/p/setting-up-sni-on-cloudfront">mentioned before</a> that I think <a href="https://www.tbray.org/ongoing/When/201x/2012/12/02/HTTPS">increasing the cryptographic noise floor</a> is important, so I thought I&#39;d write a bit about what SSL does and how to put together a strong webserver configuration.</p><h3>What SSL Does</h3><p>Public-key cryptography is good at ensuring that the message a client received is the one that was sent, and that nobody else has read it. In short, a client exchanges public keys with the server, some math happens, and now the communications are secure.</p><p>Verifying a remote server is tricky, though. When you exchange keys, how do you know the server you&#39;re talking to is really the server representing someweb.site? In PGP, this problem is addressed with a concept called &quot;<a href="https://en.wikipedia.org/wiki/Web_of_trust">web of trust</a>,&quot; where people you know sign your public key to verify your identity to a third party. It isn&#39;t really practical for the Web, though: nobody wants to seek out a friend-of-a-friend-of-a-friend who works at esoteric-craft-supplies-online.com before they can buy some esoteric craft supplies online.</p><p>Instead, SSL uses Certificate Authorities (CAs). Operating systems and browsers come with a few dozen CA&#39;s public keys preinstalled. When the server begins a handshake, it sends an <a href="https://en.wikipedia.org/wiki/X.509#Certificates">X.509 certificate</a> that includes (among other things) its public key, the domain(s) for which it&#39;s valid, and a cryptographic signature from a certificate authority.</p><p>Under most circumstances the client doesn&#39;t need to send much of anything beyond a public key. Webservers are happy to talk to anybody, so they don&#39;t need to verify anything about a client before commencing communications.</p><h3>Concepts In SSL</h3><p>&quot;SSL&quot; is something of a loaded term. It can refer specifically to the SSL protocol, but <a href="https://blogs.akamai.com/2014/10/ssl-is-dead-long-live-tls.html">that protocol is deprecated in favor of TLS</a>. However, since TLS gained usage gradually, and performs the same role as SSL, &quot;SSL&quot; is used colloquially to refer generally to SSL-like key exchange and encrypted communication. In the rest of this post I&#39;ll use &quot;SSL&quot; and &quot;TLS&quot; to refer to the specific protocols, and &quot;SSL/TLS&quot; for the general concept/process.</p><p>SSL/TLS uses X.509 certificates, as mentioned above. They&#39;re usually stored in <code>.pem</code> or <code>.crt</code> files, which encode the X.509 data in base 64 with 64-character lines. Certificates use asymmetric (or &quot;public/private-key&quot;) cryptographic signatures to provide verification from a trusted third party.</p><p>Diffie-Hellman is a mechanism that uses asymmetric cryptography to establish a secure channel on top of an insecure one. It uses large prime numbers and advanced math to devise and exchange a shared secret, after which the shared secret is used in a symmetric cipher like AES.</p><p>A reverse proxy webserver is a program that receives HTTP requests from clients and passes them along to application webservers. SSL/TLS is typically handled by a reverse proxy webserver rather than by a specific application. Among other advantages, this practice moves the tricky and security-critical logic into well-understood open-source code, which is more likely to get it right. Nginx and Apache are the most popular reverse-proxy webservers.</p><p>A reseller is a company that provides some sort of middleman service between consumers and CAs.</p><h3>How You Can Use SSL/TLS</h3><p>If you want to provide SSL/TLS on your website, you&#39;ll need to generate a private key, acquire a certificate, and configure your webserver. In the examples below I&#39;ll be using OpenSSL and Nginx. OpenSSL&#39;s interface is arcane, and it&#39;s acquired a somewhat negative reputation since Heartbleed, but there isn&#39;t really anything better available. Nginx is my webserver of choice in most circumstances; Apache users should have little trouble finding translations of the Nginx config provided here.</p><h4>Generate A Private Key</h4><p>The first part is also the easiest. It&#39;s a single OpenSSL command:</p><pre><code>openssl genrsa -out mywebsite.key 2048</code></pre><p>The <code>2048</code> is the number of bits OpenSSL should use for the key. 2048 is generally considered appropriate.</p><p>Remember that <code>mywebsite.key</code> is the secret, private key for your website. Don&#39;t leave it lying around, and don&#39;t put it in source control (<a href="/p/using-pgp-to-encrypt-the-ansible-vault">at least not in plaintext</a>).</p><h4>Acquire A Certificate</h4><p>The next step is to decide whether you&#39;re buying a single domain name, a wildcard, or some number of sub/domains. What choice you make here depends on what you&#39;re trying to do, so I don&#39;t have much guidance for you. Keep in mind that a wildcard is only valid for one layer of subdomains: a certificate for <code>*.yourdomain.com</code> is <b>not valid</b> for <code>two-level.subdomain.yourdomain.com</code>.</p><p>Pick a CA or reseller (I use <a href="https://www.namecheap.com/security/ssl-certificates.aspx">Namecheap</a>) and browse their options. Now you can generate a CSR with OpenSSL:</p><pre><code>openssl req -new -key mywebsite.key -out mywebsite.csr</code></pre><p>OpenSSL will ask a bunch of questions. The critical one is the 6th or so: <code>Common Name (e.g. server FQDN or YOUR name)</code>. If this is to be a wildcard domain, enter <code>*.yourdomain.com</code>; otherwise enter <code>yourdomain.com</code> or <code>somesubdomain.yourdomain.com</code>. If you&#39;re the type of person who uses a whois anonymizer, keep in mind that the information in your CSR will end up publically viewable in your certificate.</p><p>Send <code>mywebsite.csr</code> to your CA/reseller. Some resellers are still signing certificates with the deprecated SHA-1 algorithm, so make sure your CA/reseller provides SHA-256 signatures. Chrome has started displaying user-facing warnings when it encounters certificates signed with SHA-1.</p><p>They&#39;ll perform some sort of verification and eventually email you a certificate file along with several intermediate certificates. While you wait for the certificates, let&#39;s talk about configuring your webserver.</p><h4>Configure Your Webserver</h4><p>My Nginx config looks somewhat like the below (this all goes in a <code>server {}</code> block):</p><pre><code class="nginx">ssl on;
ssl_protocols TLSv1 TLSv1.2;
ssl_ciphers &quot;ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-AES256-GCM-SHA384:DHE-RSA-AES128-GCM-SHA256:DHE-DSS-AES128-GCM-SHA256:kEDH+AESGCM:ECDHE-RSA-AES128-SHA256:ECDHE-ECDSA-AES128-SHA256:ECDHE-RSA-AES128-SHA:ECDHE-ECDSA-AES128-SHA:ECDHE-RSA-AES256-SHA384:ECDHE-ECDSA-AES256-SHA384:ECDHE-RSA-AES256-SHA:ECDHE-ECDSA-AES256-SHA:DHE-RSA-AES128-SHA256:DHE-RSA-AES128-SHA:DHE-DSS-AES128-SHA256:DHE-RSA-AES256-SHA256:DHE-DSS-AES256-SHA:DHE-RSA-AES256-SHA:AES128-GCM-SHA256:AES256-GCM-SHA384:AES128-SHA256:AES256-SHA256:AES128-SHA:AES256-SHA:AES:CAMELLIA:DES-CBC3-SHA:!aNULL:!eNULL:!EXPORT:!DES:!RC4:!MD5:!PSK:!aECDH:!EDH-DSS-DES-CBC3-SHA:!EDH-RSA-DES-CBC3-SHA:!KRB5-DES-CBC3-SHA&quot;;
ssl_prefer_server_ciphers on;
ssl_dhparam /etc/ssl/dhparams.pem;
ssl_session_cache shared:SSL:50m;
ssl_session_timeout 5m;
ssl_certificate_key /etc/ssl/www_erincall_com.key;
ssl_certificate /etc/ssl/www_erincall_com.chained.crt;</code></pre><p>The first two lines turn on SSL/TLS and restrict the actual protocol to TLS versions 1 and 1.2. That&#39;s a fairly tight restriction, but it works for most browsers. The excluded browsers are--quelle surprise--older versions of IE, and newer versions of IE on older versions of Windows. If you need to support those browsers you&#39;ll need to include SSL, although I think you can use versions that have only theoretical vulnerabilities, not practical exploits.</p><p>The next two lines, <code>ssl_ciphers</code>, and <code>ssl_prefer_server_ciphers</code>, control the actual encryption algorithm the TLS protocol will use. You may have heard about the <a href="https://en.wikipedia.org/wiki/FREAK">FREAK</a> and <a href="https://en.wikipedia.org/wiki/Logjam_%28computer_security%29">Logjam</a> vulnerabilities, which downgraded SSL/TLS connections to use <a href="https://en.wikipedia.org/wiki/Crypto_Wars">&quot;export-grade&quot; cryptography</a>. These two lines are where you secure your server against those exploits. The list of ciphers is drawn from <a href="https://weakdh.org/sysadmin.html">weakdh.org</a>.</p><p>The next line, <code>ssl_dhparam</code>, won&#39;t work out of the box. Use OpenSSL to create a Diffie-Hellman group:</p><pre><code>openssl dhparam -out dhparams.pem 2048</code></pre><p>The <code>dhparams.pem</code> file you generate in this way doesn&#39;t need to be kept secret, and in fact you <i>could</i> skip this step entirely and use the Diffie-Hellman group that came with your OS. However, recent research has shown that if everyone on the Internet is using the same handful of Diffie-Hellman groups, sophisticated attackers (NSA, GCHQ) can exploit the situation to read targeted encrypted communications. Since it&#39;s cheap to generate your own group, and a diverse population of Diffie-Hellman groups makes the attack impractical, you should go ahead and do it.</p><p>The next two lines, <code>ssl_session_cache</code> and <code>ssl_session_timeout</code>, are reasonable defaults for Nginx. You can tune or tweak them if you want, but you probably won&#39;t need to.</p><p>The final two lines use the private key you generated at the beginning and the certificate from your CA/reseller. By now they&#39;ve probably finished verification and emailed your certificate to you, so let&#39;s get back to that.</p><h4>Put Your Certificate Together</h4><p>Your CA/reseller has emailed you a certificate, and probably included one or more <i>intermediate certificates</i>. Earlier I claimed that your site&#39;s cert would be signed by a root CA, but that isn&#39;t exactly true. In order to limit the root certificate&#39;s private key&#39;s exposure, CAs actually sign an intermediate cert with the root cert, then sign site certs with the intermediate cert. You&#39;ll need to include the intermediate certs along with your site cert. The &quot;chained&quot; cert should have your site cert at the top, then the intermediate cert that signed your site cert, then the cert that signed the first intermediate...and so on until the root certifate, which you can exclude. For example, I created the chained certificate for blog.erincall.com with this command:</p><pre><code class="Bash">cat blog_erincall_com.crt COMODORSADomainValidationSecureServerCA.crt COMODORSAAddTrustCA.crt &gt; blog_erincall_com.chained.crt</code></pre><p>The filenames you get from your CA/reseller may be different, of course.</p><h3>Self-Signed Certificates</h3><p>If you don&#39;t want to pay for a verified certificate, or you don&#39;t want to go to the trouble, you could use a self-signed certificate. Self-signed certificates are inappropriate for production use, but they can be just fine for testing configuration, or for applications like an IRC bouncer, where you&#39;re the only user and can just install the certificate locally.</p><p>Most of the process is the same, but you get to skip the whole &quot;interacting with a CA&quot; step. After creating your CSR, you play the role of CA:</p><pre><code>openssl x509 -req -in mywebsite.csr -signkey mywebsite.key -out mywebsite.crt</code></pre><h3>The Future</h3><p>With any luck, a lot of the information presented here will be out of date very soon. The <a href="https://letsencrypt.org/">Let&#39;s Encrypt</a> project aims to provide a free, API-accessible CA to support automated SSL/TLS provisioning. In the meantime, keep an eye on your configuration. SSL Labs provides <a href="https://www.ssllabs.com/ssltest/">a free online configuration tester</a> that will tell you about major or minor problems with your site&#39;s configuration. Keep an eye out for SSL/TLS vulnerabilities; if you see one, check back at SSL Labs to see if you&#39;re vulnerable. They&#39;ve been very quick to update their tester.</p><p>Stay safe out there!</p>Git Helpers For The Fish Shelltag:blog.erincall.com,2008:p/git-helpers-for-the-fish-shell2015-08-19T15:30:00Z<p>I recently switched from Bash to the <a href="http://fishshell.com/">Fish Shell</a>. It&#39;s great, but I missed the git helpers I got from <a href="http://github.com/ndbroadbent/scm_breeze">SCM Breeze</a>. Rather than sit around moping, I thought I&#39;d implement the core feature in Fish.</p><p>The best thing SCM Breeze offers is a collection of shortcuts for files with outstanding changes. The author realized that when you run <code>git status</code>, the next thing you&#39;re likely to want to do is perform some operation or another on the changed files. You might want to <code>git add</code> them, view the changes in <code>git diff</code>, open them in an editor, etc. SCM Breeze alters the <code>git status</code> output to make the file listing a numbered list. It also wraps <code>git</code> in a Bash function that translates those numbers when they&#39;re used as arguments. The upshot is that instead of having to type <code>git add some/long/path/to/a/filename</code>, you can just ype <code>git add 1</code>.</p><p>That behavior is what I implemented in Fish, as well. It looks like this:</p><p><img src="https://cdn.erincall.com/48a79a4f594d917c242f7cab6f9810ee137b82f9" alt="Terminal video"></p><p>One improvement I&#39;ve made over SCM Breeze is that my version handles renamed files correctly. Under SCM Breeze, the entire rename would get a single number pointing to &quot;<code>oldname -&gt; newname</code>&quot;. It was unhelpful. My version assigns indices to the old and new names.</p><p>If you&#39;re interested in trying this out, drop <a href="https://github.com/ErinCall/dotfiles/blob/master/.config/fish/functions/git.fish"><code>git.fish</code></a> and <a href="https://github.com/ErinCall/dotfiles/blob/master/.config/fish/functions/acquire_git_changes.fish"><code>acquire_git_changes.fish</code></a> in your <code>~/.config/fish/functions</code> directory. This is still an early version, so there may be problems--let me know if you run into any! If you do have trouble, you can always use <code>command git ...</code> to bypass the git-wrapping function.</p>How To Paint Your Nailstag:blog.erincall.com,2008:p/how-to-paint-your-nails2015-08-04T18:45:00Z<h3>Background</h3><p>I started painting my nails about a year ago. That may not seem like very long, but it gives me an advantage because I still remember which things I had to learn and which were obvious. That means you won&#39;t have to see me say &quot;now just do an inverted Woollongong Shimmy and you&#39;re finished!&quot;</p><h3>Supplies</h3><h4>Beyond the obvious colored polish, there are a few supplies you should grab.</h4><p>(All the Amazon links here are mostly intended as examples; they aren&#39;t necessarily the cheapest or best option)</p><p><b>Base coat</b>: <a href="http://smile.amazon.com/OPI-Natural-Nail-0-5-Fluid-Ounce/dp/B004220C6E/ref=sr_1_1?ie=UTF8&amp;qid=1438704152&amp;sr=8-1&amp;keywords=opi+base+coat">Base coat</a> goes on first and serves two purposes: it helps the polish last longer without chipping, and it keeps the polish from staining your nails.</p><p><b>Top coat</b>: as you might guess, <a href="http://smile.amazon.com/OPI-Nail-Polish-Coat-0-5-Ounce/dp/B00178VX50/ref=sr_1_1?ie=UTF8&amp;qid=1438704183&amp;sr=8-1&amp;keywords=opi+top+coat">top coat</a> goes on last. Along with the base coat it helps prevent chipping, and it gives you a nice shiny finish. A <a href="http://smile.amazon.com/OPI-T35-Matte-Top-Coat/dp/B00BPDFQK0/ref=sr_1_1?ie=UTF8&amp;qid=1438705171&amp;sr=8-1&amp;keywords=opi+matte+top+coat">matte top coat</a> is another option that looks nice with solid colors.</p><p><b>Nail polish remover</b>: If you decide you want a new color, or when the polish starts chipping, you&#39;ll want to take it off your nails. <a href="http://smile.amazon.com/Sally-Hansen-Nail-Polish-Remover/dp/B000142TS6/ref=sr_1_13?s=beauty&amp;ie=UTF8&amp;qid=1438704220&amp;sr=1-13&amp;keywords=nail+polish+remover">Polish remover</a> is a usually-acetone-based solvent that...removes nail polish.</p><p><b>Cotton balls or rounds</b>: You&#39;ll use these to apply the polish remover. <a href="http://smile.amazon.com/Delon-Cleansing-Cotton-Rounds-Count/dp/B00VQSHRY2/ref=sr_1_4?ie=UTF8&amp;qid=1438704348&amp;sr=8-4&amp;keywords=cotton+rounds">Rounds</a> are slightly more convenient than <a href="http://smile.amazon.com/Kendall-Covidien-Prepping-Cotton-Count/dp/B00962EG7G/ref=sr_1_2?ie=UTF8&amp;qid=1438704271&amp;sr=8-2&amp;keywords=cotton+balls">balls</a>, but I don&#39;t think the difference is large enough to justify the higher cost.</p><p><b>Q-tips</b> (cotton swabs): It&#39;s common to get a bit of paint on the skin around your nails. Clean it off with a q-tip dipped in polish remover.</p><p><b>Aluminum foil</b>: Acetone is a pretty vigorous solvent that can ruin the finish on your furniture. I do my work on a bit of foil so that doesn&#39;t happen.</p><h4>Some additional tools will prove helpful if you want to do anything fancier than solid colors:</h4><p><b>Striping brush</b>: This is <a href="http://smile.amazon.com/Winstonia-Professional-Striping-Striper-Acrylic/dp/B00GU0OYK6/ref=sr_1_3?ie=UTF8&amp;qid=1438704915&amp;sr=8-3&amp;keywords=striping+brush">a brush with very long fibers</a>, for making straight lines.</p><p><b>Fine brush</b>: <a href="http://smile.amazon.com/Acrylic-Design-Decoration-Painting-Drawing/dp/B008FPGY2U/ref=sr_1_3?ie=UTF8&amp;qid=1438704951&amp;sr=8-3&amp;keywords=fine+nail+brush">A small brush</a> for making delicate designs.</p><p><b>Dotting tool</b>: <a href="http://smile.amazon.com/DragonPad-2-ways-Acrylic-Dotting-Painting/dp/B005Y6F4WO/ref=sr_1_1?ie=UTF8&amp;qid=1438704994&amp;sr=8-1&amp;keywords=dotting+tool">A metal rod with spheres on each end</a> that makes dots. It&#39;s identical to the stylus you may have for carbon tracing or making lines in clay.</p><p><b>Cosmetic Sponges</b>: <a href="http://smile.amazon.com/Wonder-Wedge-Cosmetic-100s/dp/B003BMGAFI/ref=sr_1_1?ie=UTF8&amp;qid=1438705078&amp;sr=8-1&amp;keywords=cosmetic+sponges">Pieces of fine-pore synthetic foam</a> are good for applying gradients (see below for more).</p><p><b>Wooden Pencil</b>: A pencil eraser is good for making medium circles.</p><h4>Finally, some things for your nails themselves:</h4><p><b>Cutting tool</b>: normal nail clippers will work ok. I find <a href="http://smile.amazon.com/Sally-Hansen-Fingernails-Combo-Cuticle-Scissors/dp/B004KRXFVA/ref=sr_1_6?ie=UTF8&amp;qid=1438704015&amp;sr=8-6&amp;keywords=nail+scissors">nail scissors</a> are a little more precise.</p><p><b>File</b>: Emery boards work, but they wear out fast. I highly recommend a <a href="http://smile.amazon.com/OPI-FI031-Crystal-Nail-File/dp/B000PHKE0O/ref=sr_1_1?ie=UTF8&amp;qid=1438703951&amp;sr=8-1&amp;keywords=opi+crystal+file">crystal file</a>, which is made of glass. They don&#39;t wear out at all, so you&#39;ll quickly recoup the upfront cost, and the cutting surfaces are sharper and finer than an emery board.</p><p><b>Nail Block</b>: This is basically <a href="http://smile.amazon.com/Shiner-Buffer-Sanding-Manicure-Product/dp/B00GOIKKCA/ref=sr_1_4?ie=UTF8&amp;qid=1438704086&amp;sr=8-4&amp;keywords=nail+block">a file glued to a foam block</a>. It&#39;s nice for shining up the surfaces of your nails, rather than the ends. It won&#39;t matter if you&#39;re painting, but if you&#39;re going out &quot;bare&quot; a nail block can give you a nice shine.</p><h3>Basic process</h3><p>Whether you&#39;re doing fancy multi-colored art or a simple single color, the fundamentals are the same.</p><p>First of all, for the next hour or so, you aren&#39;t going to be able to use your hands. I usually turn on the TV so I have something to occupy my attention while I wait for each layer to dry. Go to the bathroom now!</p><p>Gather all your supplies: base and top coat, color, foil, and any tools. The basic nail paint is 4 coats: base, two layers of color, and topcoat. Let each coat dry for 10-15 minutes before you start the next one. While you wait for a coat to dry, you kinda have to pretend you&#39;re a surgeon and avoid touching anything. You can do a little bit with your fingertips, but it&#39;s surprisingly difficult to do anything productive. I&#39;m overemphasizing this point because you still don&#39;t believe me, but you&#39;ll learn for yourself in time.</p><p>I&#39;ve seen various tricks for preventing chips, like soaking your fingers in ice water after each coat. Their benefit is minimal; a good base- and top-coat are much more effective.</p><h3>Brushing Technique</h3><p>The best way to get a smooth coat of lacquer is to load a moderate amount onto your brush. Let a small drop fall onto the base of your nail, then spread it toward the tip. If it takes more than one pass to get a good layer, you don&#39;t have enough on the brush; if you have any blobs or pools, you have too much.</p><p>Don&#39;t worry about getting thick, fully-opaque color in one go--that&#39;s why you&#39;re doing two coats.</p><p><a href="https://catsnap.erincall.com/image/2344"> <img src="https://cdn.erincall.com/80862af3bbd884c46444e905fef61a1e7beb367a_small" alt="too much"> </a></p><p>Too much paint on the brush.</p><p><a href="https://catsnap.erincall.com/image/2345"> <img src="https://cdn.erincall.com/1ff7e19a5480dc2d4c864c3ea5aead2536174065_small" alt="not enough"> </a></p><p>Not enough paint on the brush.</p><p><a href="https://catsnap.erincall.com/image/2346"> <img src="https://cdn.erincall.com/b3cc65edc60643e51ba9acf3fd40971f17efe511_small" alt="just right"> </a></p><p>Just the right amount.</p><h3>Expensive vs. Cheap Lacquers</h3><p>A bottle of nail paint can range from USD $0.50 at a grocery store endcap to $10.00 for high-quality brands like OPI. I haven&#39;t noticed a significant correlation between price and the quality or longevity of the color. However, good lacquers spread more smoothly and are <i>much</i> easier to use for delicate work.</p><h3>Painted Designs</h3><p>If you aren&#39;t a skilled painter you might feel intimidated by the thought of trying to work in a tiny space, especially with your non-dominant hand. Don&#39;t be! I myself have such shaky hands that I&#39;ve been accused of faking it. Nevertheless, I&#39;ve had a lot of success with this one weird trick: <b>go really slow</b>. With a fine brush, a quality lacquer, and patience, even a text-oriented keyboard-masher like me can manage some nice designs.</p><p><a href="https://catsnap.erincall.com/image/2339"> <img src="https://cdn.erincall.com/303c8c4896970927a8e60f6011b247d0cdd4da57_small" alt="Halloween"> </a> <a href="https://catsnap.erincall.com/image/2338"> <img src="https://cdn.erincall.com/7ba8607b44d6ec47e356b5bb74d8fd157ad8aa94_small" alt="Halloween"> </a> <a href="https://catsnap.erincall.com/image/2340"><img src="https://cdn.erincall.com/5ab15b48057afcd649ab3d79e4b606ca3dfe9781_small" alt="Christmas Trees"></a></p><h3>Other Design Ideas</h3><p>A nice way to go above and beyond a simple one-color job is to <a href="http://www.thenailasaurus.com/2012/04/gradient-nails-picture-tutorial.html">mix two colors into a gradient</a> (or &quot;ombre,&quot; if you prefer). You&#39;ll need makeup sponges and a couple of compatible colors. I&#39;ve found that colors from different brands may or may not mix well; I assume this is a matter of differing solvent bases.</p><p><a href="https://catsnap.erincall.com/image/2343"> <img src="https://cdn.erincall.com/90880f5f2e27f5dd4156427e5053f1345759b7f6_small" alt="Blue/purple gradient"> </a></p><p>A <a href="http://www.wikihow.com/Create-a-Marble-Nail-Effect-Using-Water">marbled look</a> takes some work, but looks really impressive when you pull it off. It&#39;s especially a good way to take things up a notch if you&#39;re intimidated by fine brushwork.</p><p><a href="https://catsnap.erincall.com/image/2332"> <img src="https://cdn.erincall.com/232684c9a20e22b6bf1030d23e302ee05fdf85b3_small" alt="pastel marbling"> </a></p><p>Simple dots or stripes take minimal effort and stand out in a nice way. Use a light touch with your dotting tool; pressing hard against the nails will make a sort of empty ring rather than a dot.</p><p><a href="https://catsnap.erincall.com/image/2334/"> <img src="https://cdn.erincall.com/396a0e8d60b5f7393d3d86caa260f68fa45a636d_small" alt="!dark green with yellow spots"> </a></p><h3>Nail Strengthening</h3><p>If you&#39;re growing your nails out as well as painting them, you&#39;ll discover a tendency to crack, chip, or peel. The only real way to prevent damage to your nails is to avoid using your hands, but that isn&#39;t very practical. Typing, in particular, seems hard on your nails.</p><p>There are lots of polish removers and base coats that allege to strengthen your nails. They don&#39;t work, so don&#39;t waste your money. I&#39;ve been trying a biotin supplement, with some success. Switching to a non-acetone nail polish remover also seemed to help somewhat.</p>Using PGP To Encrypt The Ansible Vaulttag:blog.erincall.com,2008:p/using-pgp-to-encrypt-the-ansible-vault2014-11-30T21:00:00Z<p>Over the last week I&#39;ve been getting rid of the extremely janky Puppet setup I had provisioning my VPS, and replacing it with Ansible. One of the features I really like in Ansible is the Vault, which is a fancy name for AES-encrypted data files. The Vault lets me put stuff like API keys in source control without exposing them to my enemies [1]. Super convenient!</p><p>Unfortunately, the Vault is also sort of a pain: every time I want to edit an encrypted file, or do a test run, I have to type my Super Complex Secret Passphrase. I had to do a lot of test runs while getting everything verified, so that got pretty tedious. Additionally, if this Ansible setup were for a project with many developers, I&#39;d have <a href="https://en.wikipedia.org/wiki/Key_distribution">the usual password-distribution problems</a>.</p><p>Fortunately, there&#39;s another way. Ansible has support for getting the Vault passphrase from a script [2], so if I had some authorized agent that could report the passphrase, I&#39;d be golden. <a href="https://btl.gs/2014/09/01/using-ansible-vault-and-gpg-to-secure-critical-infrastructure-in-public-github-repositories/">John Knight has a nice writeup on using GPG</a>, but I&#39;m a little uncomfortable with his approach. He stores a PGP-encrypted Vault passphrase in source control, which is sensible, but then decrypts it to a <code>.gitignore</code>d plaintext file. That isn&#39;t the kind of thing I want to leave lying around. I use full-disk encryption and all, but I&#39;m still nervous, so I&#39;ve developed a slightly different approach.</p><p>First, I generate a super secret Vault passphrase:</p><pre><code class="Bash">pwgen -C | head -n1 | gpg -e -o vault_passphrase.gpg</code></pre><p>I&#39;m using <code>pwgen</code> here, but of course you could read from <code>/dev/urandom</code> or whatever strikes your fancy. The point is just to make something nice and unguessable, since humans won&#39;t ever have to type it.</p><p>Now I make a script called <code>open_the_vault.sh</code>. Remember, it needs to be marked executable or Ansible will think its contents themselves are the passphrase.</p><pre><code class="Bash">#!/bin/sh
gpg --batch --use-agent --decrypt vault_passphrase.gpg</code></pre><p>I tell Ansible about it by adding a line to <code>ansible.cfg</code>:</p><pre><code class="INI">[defaults]
vault_password_file=open_the_vault.sh</code></pre><p>The last thing is to make sure <code>gpg-agent</code> is installed and running. Otherwise, I&#39;ve just swapped &quot;type an inconveniently-long Vault passphrase&quot; for &quot;type an inconveniently-long PGP passphrase,&quot; and that&#39;s not even an improvement. I&#39;ve put a 10-minute TTL on my GPG agent by adding <code>default-cache-ttl 600</code> to <code>~/.gnupg/gpg-agent.conf</code>, to minimize the window where someone can use my cached private key.</p><p>Once I&#39;ve done all this, I have a real nice situation: my servers&#39; secrets are in source control, but not exposed; I can grant and revoke access with ease; an authorized person must be present to decrypt any secrets; I don&#39;t have to type my passphrase over and over and over. As far as I&#39;m concerned, it&#39;s the best of all possible worlds!</p><p>[1] I don&#39;t actually have any enemies. At least, not as far as I know<i>!</i></p><p>[2] Since version 1.7.</p>On "The Incorruptibles"tag:blog.erincall.com,2008:p/on-the-incorruptibles2014-09-21T03:30:00Z<p>I really really want to love the Old West as a setting. It&#39;s so romantic: hard, lonely men and women wresting a living from a hard, lonely land under a huge, lonely sky. It&#39;s a place and time where you can&#39;t rely on anyone but yourself: lawmen, lovers, and old old friends will all turn on you in a second if the price is right. If your wits are sharp and your hands are fast, you might just make your fortune...but the only guaranteed payday is the undertaker&#39;s.</p><p>Problem is, all that romance is inextricably tied to the genocide of the Native Americans. The only reason that big empty land is so empty is that the army--and the plagues that preceded them--came through and emptied it. There&#39;s no getting around it. If the story has no natives, it&#39;s another entry in the long list of American cultural works that pretend the genocide never happened. If it treats the natives as unfathomable, implacable enemies, it plays the same notes that made the genocide possible in the first place. Similarly, Tonto-like sidekicks turn a a civilization and culture into a one-dimensional caricature. Few and far between are the stories that manage to use a Western setting in a responsible way.</p><p>I recently finished <a href="http://smile.amazon.com/Incorruptibles-John-Hornor-Jacobs-ebook/dp/B00JEIPHOI/ref=sr_1_2">The Incorruptibles</a>, by John Hornor Jacobs. I&#39;m sorry to say that it makes the Native Americans into terrible creatures, ones with whom there is no possibility of dialogue or compromise.</p><p><i>The Incorruptibles</i> is an alternate-history / high-fantasy story. The Ruman empire, which is like the Roman empire but spelled funny, has survived into the Western expansion. Tensions with Mediera (Spain, basically) and the Autumn Lords (China) are high. Our protagonist is Shoestring, a half-dwarven mercenary who won&#39;t carry the Hellfire-powered sixguns carried by his colleagues.</p><p>Shoestring&#39;s characterization fits nicely into the lonely setting. Dwarves are hardly unheard-of, but they&#39;re uncommon and of low status. His human side keeps him from fitting in with other dwarves, while his dwarven heritage holds him back among humans. Layered on top of that is his religious horror of the demon-powered artifice so common among the Rumans. His supporting cast have vibrant personalities that really shine as their individual goals start to clash.</p><p>Unfortunately, the treatment of the Native Americans casts a pall over everything. There are no native humans on Jacobs&#39;s American continent; instead, the land is populated with &quot;stretchers,&quot; or elves, who are inhumanly tall, inhumanly beautiful, and just plain inhuman. Their immortality in the face of both age and injury leaves them with little desire to interact with our world. None of &quot;us&quot; speak their language, although some of them know ours. It&#39;s repeatedly stated in dialogue that they&#39;re too different to understand, so it&#39;s not worth even trying.</p><p>This is an irresponsible way to structure the book. Atrocities become possible when we forget that they&#39;re being done to people. The way that crimes against Native Americans have been consistently swept under the rug over the centuries only makes it more important to start getting it right. Jacobs got it wrong.</p><p>The frustrating thing is that the unintelligibility of the Stretchers isn&#39;t even central to the story. The tale could have been told just as well with Stretchers who are comprehensibly different. They could&#39;ve been another culture, maybe weird and surprising, but still people. Instead we got nature spirits with all the depth and introspection of a cardboard cutout.</p><p><i>The Incorruptibles</i> is a good story. I wish I could tell you it&#39;s a good book.</p>Alternative Ways To Rescue In Rubytag:blog.erincall.com,2008:p/alternative-ways-to-rescue-in-ruby2014-09-11T18:00:00Z<h3>Begin</h3><p>If you&#39;ve worked in Ruby much, you&#39;re probably familiar with using <code>begin</code>/<code>rescue</code>/<code>end</code> to catch exceptions. Did you know there are other ways to use rescue? It&#39;s true! You may be able to delete a bit of software by using inline or implicit rescues.</p><h3>Inline Rescue</h3><p>You can catch exceptions from a single statement by putting a <code>rescue</code> at the end of the line:</p><pre><code class="Ruby">def get_some_bread
throw_out_stale_bread
top_slice rescue nil
end</code></pre><p>This doesn&#39;t create any new capabilities for you, it&#39;s just a terser syntax. This is exactly equivalent:</p><pre><code class="Ruby">def get_some_bread
throw_out_stale_bread
begin
top_slice
rescue StandardError
nil
end
end</code></pre><p>The big downside to an inline rescue is that there&#39;s no way to specify an exception class to rescue. Rescuing from everything is <a href="http://ischenko.blogspot.com/2005/01/exception-based-code-antipatterns.html">almost always a bad idea</a>; you&#39;re extremely likely to catch exceptions that you don&#39;t know how to handle and should&#39;ve let bubble higher up. Remember, it&#39;s better for your program to fail and tell you why than for it to plow heedlessly ahead and do something incomprehensible.</p><h3>Rescuing whole methods</h3><p>Another way to trim your code is by rescuing a whole method. It turns out methods have an implicit <code>begin</code>, so if all the logic in your method is wrapped in a begin/rescue, you can instead use the implicit begin:</p><pre><code class="Ruby">def get_some_bread
throw_out_stale_bread
top_slice
rescue NoBreadLeft =&gt; e
LOGGER.warn(&quot;buy more bread!&quot;)
nil
end</code></pre><p>Again, this is simply a shortened syntax. It is exactly equivalent to:</p><pre><code class="Ruby">def get_some_bread
begin
throw_out_stale_bread
top_slice
rescue NoBreadLeft =&gt; e
LOGGER.warn(&quot;buy more bread!&quot;)
nil
end
end</code></pre><p>The caveat here is that you have to wrap the whole method in your catch. As with catching all exceptions, it&#39;s risky to catch exceptions from code you didn&#39;t know could raise them: your handler is likely to do the wrong thing, putting your program in an inconsistent state. Still, this can be useful for small methods.</p><h3>End</h3><p>Inline rescues and implicit begins aren&#39;t gonna shake up the way you develop Ruby, but they can reduce clutter and save you a little space.</p>Everything I Know About Testing Computer Programstag:blog.erincall.com,2008:p/everything-i-know-about-testing-computer-programs2014-06-10T22:30:00Z<p>In an email, Rachel King asked a fairly open-ended question about writing tests:</p><blockquote><p>I&#39;m looking for guides on how to write tests. Trying to get better at debugging...</p></blockquote><p>Since everything I know about testing is stuff I just picked up along the way, I don&#39;t know of any good guides off the top of my head. I started writing up what I do know, but when I realized I had a pretty sizeable document on my hands, I thought I&#39;d make it a blog post instead. Let&#39;s get to it!</p><h3>Unit tests and integration tests</h3><p>I think of these two things as the ends of a spectrum. A <i>unit test</i> acts on a single logical unit--ideally a <a href="https://en.wikipedia.org/wiki/Pure_function">pure function</a>--and shows that it works the way it&#39;s supposed to, in isolation. An <i>integration test</i> acts on a large swathe of the code at once, showing that all the pieces work together harmoniously. In general, moving toward the unit end of the spectrum will make a test easier to understand and debug, while moving toward the integration end will provide stronger assurances about the system&#39;s overall reliability.</p><p>A lot of people will shout about how integration testing is better than unit testing, or vice versa. They&#39;re wrong. Both have benefits, and a well-tested codebase will include tests that lie all along the spectrum.</p><h4>Behavioral Testing, Functional Testing, Acceptance Testing, etc.</h4><p>There are various categories or families of testing that&#39;ve found homes in people&#39;s hearts. The way I&#39;ve defined unit/integration testing above (which I guess I should emphasize are <i>not</i> really the standard definitions), all these families fall at one point or another on the unit/integration spectrum.</p><h4>Strength and precision of a test</h4><p>The <i>strength</i> of a test is a qualitative measure of its assurance that the codebase works correctly. The <i>precision</i> of a test is a qualitative measure of the insight it provides when it fails. In general, a test&#39;s strength is inversely proportionate to its precision. A test that hits one unit of code with one input is very precise: a failure in that test says a lot about what&#39;s wrong with the code. However, it will only fail under very specific circumstances. A test that exercises a whole integrated stack of code with a variety of data is very strong: if there are any problems at all, it&#39;s likely to find them. However, a failure doesn&#39;t reveal much about where to start debugging.</p><p>Given a choice between only very strong tests and only very precise tests, I&#39;d take the strong tests. The ease with which tests can be debugged is only relevant if they can actually reveal a problem. In non-hypothetical situations, though, I want both.</p><h3>Mocks, stubs, spies, and their ilk</h3><p>The easiest code to unit-test is code that takes inputs and returns outputs and has no side-effects. Sometimes, though, one must write code composed mostly, or entirely, of side-effects. One way to test such code is by letting it run its side-effects and then check that they happened the way they should. This can be clunky, though, so another solution is to use a mocking library. Mocks lift out parts of the tested system and replace them with objects that behave in predefined ways, to isolate the interesting logic. They are super neat! There is a risk, though: if the mock objects don&#39;t behave the way their real counterparts do, the result will be a passing test suite on a broken codebase.</p><p>Stubs and spies are particular types of mock object. I think spies, in particular, are under-emphasized by mocking frameworks. A spy acts just like the real object it replaces--which dodges the inaccurate-interface problem--but also records everything that was done to it. Then the test can make assertions about what happened without having to spend time duplicating the interface it&#39;s spying on.</p><h3>The test suite should pass cleanly every time</h3><p>The point of the test suite is to show that the code works right; consequently the ideal is to have an ironclad correspondence between &quot;test suite does/doesn&#39;t pass&quot; and &quot;code does/doesn&#39;t work.&quot; It isn&#39;t possible to get 100% of this ideal on a nontrivial system, but it makes sense to strive toward it. The closer the test suite gets to the ideal, the more it can become an integral part of the development process. Some developers even use tools that watch their working directory for changes to files and automatically run the corresponding tests. If the test suite is unreliable, though, running it all the time will quickly become more annoying than helpful.</p><p>If a programming team is exceptionally diligent, the full test suite will succeed every time it&#39;s run, on every commit in the repo. This lets tools like <a href="http://git-scm.com/book/en/Git-Tools-Debugging-with-Git">git-bisect</a> quickly track down regressions.</p><p>If the team is just regular diligent, the full test suite will succeed every time it&#39;s run on master. Tests that fail intermittently (because of race conditions, say) mean that every test failure requires careful examination to see if it&#39;s legitimate before normal debugging can even start.</p><p>If the normal state of the test suite is to have failing tests, it&#39;s terribly easy to overlook a &quot;legitimate&quot; failure. Failing tests on master are only slightly less alarming than errors on production, because the former can so easily turn into the latter.</p><h4>The test suite should be fast</h4><p>Although opinions differ on how fast comprises &quot;fast,&quot; every development strategy that embraces testing agrees that the test suite must run quickly. This dovetails with the requirement that the suite passes reliably: the faster a suite runs, the more often one can run it; the more often one runs one&#39;s tests, the more utility they&#39;re providing.</p><h3>Altering The Implementation To Make It Easier To Test</h3><p>This is a controversial topic. Programmers I respect have argued that the implementation is the important thing, so if accommodations must be made, they should be made by the tests. Rails creator David Heinemeier Hansson famously <a href="http://david.heinemeierhansson.com/2014/test-induced-design-damage.html">came out against test-driven design</a> not long ago. I disagree, though: in my experience, practices that make code easier to test also make it easier to maintain and extend in general. Take DHH&#39;s example of the Rails controller architecture being decoupled from ActiveRecord: to me that sounds great, because now if a maintainer has cause to use some database-interactor other than ActiveRecord, it&#39;ll be easy.</p><h3>How to write a test</h3><p>Ok, so I set out to write a post on writing tests, and wrote nearly a thousand words just on background. Let&#39;s get to the meat!</p><h4>Write the tests first: &quot;Test-Driven Development&quot;</h4><p>Most of the time, I follow the TDD maxim: &quot;write a test, watch it fail, make it pass.&quot; Assuming I know how I want the system to work, I take that knowledge and encode it in a test. Now there&#39;s a test that can run over and over to see if the system is working yet. Hopefully, the test is invokable with a single command that executes quickly (again, for vague values of &quot;quickly&quot;), and shows clearly how the system falls short.</p><h4>Write the implementation first: &quot;Spiking&quot;</h4><p>Purists will claim that the tests must always be written first. In real life, I often know generally what I want the system to do, but don&#39;t know precisely how it should happen. At that point, writing some implementation can help get my thoughts in order and show me where the pitfalls and difficulties will lie, which gives me clues about how the code will need to work. Once I have that, I can go back and fill in the blank spaces in the tests. Often, those tests will show flaws in the early work, and it has to be rewritten. <a href="http://c2.com/cgi/wiki?PlanToThrowOneAway">Plan To Throw One Away</a> comes to the fore when spiking.</p><p>There&#39;s a pitfall here that &quot;write a test and watch it fail&quot; is designed to avoid. If you never actually see a test fail, you don&#39;t know for sure that it <i>can</i> fail. Tests are just code, and code can have bugs. Make sure you see your new tests fail at least once after a spike. I like to delete or drastically neuter the spiked code for a single run of the suite.</p><p>The most common strength-reducing bug is failing to write your test in such a way that the test framework can find it. The naming conventions are one of the first things you should learn about a new test framework.</p><h4>Start with integration, then add unit tests</h4><p>This is a mere personal preference, but it works for me. I write an integration test that encodes my ideas about my eventual goal (or the goal laid out in the task system). As I try to make it pass, I get ideas about how to break my solution into smaller units. I write those units one at a time, with unit tests driving that process. Once they look right, I come back to my initial integration test to show that each piece connects correctly to its neighbors.</p><h3>How to add tests to untested code</h3><p>Testing legacy code is a real challenge. If it doesn&#39;t have tests it probably wasn&#39;t written in ways that make it easy to test--at the least, ease-of-testing certainly won&#39;t have been on the author&#39;s mind. Depending on how old the code is, it might not even be clear how it&#39;s supposed to work; I&#39;ve dealt with legacy systems whose only specification was the current behavior of the code.</p><p>One rule of thumb that&#39;s worked out all right for me has been to not worry about unit testing legacy code. Since it presumably interacts with as much of the environment as the author felt like using, trying to break the code into unit-testable chunks exposes you to all sorts of subtle regressions. Get some integration tests in place for the most obvious aspects of its design before changing anything.</p><p>If the code is <i>buggy</i> legacy code, adding integration tests will probably mean adding tests that fail if the bugs are fixed. Don&#39;t worry about it: this doesn&#39;t constitute adding bugs or compounding them. Counter-intuitively, it actually makes them easier to fix, because the test already exists; only the assertions need to be changed. Backing away in fear of an erroneous test just leaves future maintainers with the same quandary.</p><h3>Tests are your friends</h3><p>In closing, I want to reiterate that tests aren&#39;t just a good idea in the long run, they&#39;re the fastest way to develop in the short run too. Yes, like any tool, they take some time to learn to use effectively. But the investment pays off. A good unit test provides an extremely fast &quot;write code/see if it works&quot; cycle, and optimizations in that cycle pay off more than anywhere else.</p>Prague Day 6: Lazy Town and Traveltag:blog.erincall.com,2008:p/prague-day-6-lazy-town-and-travel2014-06-10T15:30:00Z<p>First off, sorry about the delay getting this last post up! I was at <a href="https://www.petekeen.net/">Pete Keen</a>&#39;s wedding, and the preparations took up all my blogging time.</p><p>Fortunately, you haven&#39;t been missing out on much. By Friday the blister on my foot was too painful to do much exploring, so after transferring hotels again--this time to the <a href="http://www.kkhotels.com/en/hotels/prague/k-k-hotel-central/about-k-khotel-central/services-facilities/">K+K Central</a>--I found a cozy bar and just sat and read my book. I&#39;d planned to go up the hill to the Castle Quarter and see Prague Castle, and I was sorry to miss it, but there wasn&#39;t anything to be done.</p><p>Saturday, my travel day, was similarly uneventful. I did finally figure out the difference between the tram and the metro, and take the right one!</p><p>As you can imagine, none of this led to a lot of photo ops. I did see a neat piece of public art:</p><p><img src="https://cdn.erincall.com/1f972a00493da40d8948a9822ce465c19d8ee65d_medium" alt="In Utero"></p><p>And a subway tunnel with its skin removed:</p><p><img src="https://cdn.erincall.com/0cc1f745945d7b8a775275ed448a6f1d1d31c3a8_medium" alt="Subway Tunnel"></p><p>So those days weren&#39;t a total loss.</p>Prague Day 5: New Towntag:blog.erincall.com,2008:p/prague-day-5-new-town2014-06-06T01:30:00Z<p>Thursday was a good day to visit Prague&#39;s New Town. Although it&#39;s new by comparison to the Old Town, it&#39;s still full of beautiful old buildings.</p><p>By this point I&#39;d burst a blister on my foot and wasn&#39;t moving too fast. Still, I had time to visit the National Museum as well as the Museum Of Communism. I eventually made it down the river a ways to see the Dancing House, as well.</p><p>The National Museum was something of a disappointment. The main building was closed for renovations, and the auxiliary building only had a single exhibit open, about the history and social structures of money. It was ok, but not great.</p><p>The Museum Of Communism was much better. Historical exhibits provided information about the rise of Czech communism in the wake of Nazi withdrawl, through the Velvet Revolution of 1989, when the communist government acceeded to peaceful protests&#39; demands and stepped down from single-party rule.</p><p>After an hour or so of reading about secret police and shoot-on-sight border zones, I felt pretty ready for some American-style capitalism, so I went to McDonald&#39;s and got a Coca-Cola:</p><p><img src="https://cdn.erincall.com/1f8326a545b7931c278c65a3498f0fb472ed46ee_small" alt="USA! USA!"></p><p>Check out <a href="https://catsnap.erincall.com/album/13">the rest of my pictures from the day</a>, as well!</p>Prague Day 4: Old Towntag:blog.erincall.com,2008:p/prague-day-4-old-town2014-06-05T03:00:00Z<p>I woke up Wednesday morning feeling refreshed and ready to enjoy this wonderful gem of Europe. After breakfast, the first order of business was to move my stuff over to my new new hotel (making reservations at the last minute meant I couldn&#39;t find one place with vacancies all my remaining nights). It was <a href="https://goo.gl/maps/ljjEF">a bit of a hike</a>, but soon enough I was off and sightseeing!</p><p>I&#39;d decided to split my remaining 3 days in Prague into 3 sections of town: Wednesday in the Old Town, Thursday in the New Town, and Friday in the Small and Castle Quarters. This plan worked well, and things were mostly uneventful on Wednesday. I walked across the Charles Bridge, visited the Museum Of Medieval Torture (somewhat disappointing) and walked through the Jewish Quarter.</p><p>There&#39;s not much else to say, but as always be sure to <a href="https://catsnap.erincall.com/album/12">check out my pictures from the day</a>!</p>Prague Day 3: Actually Enjoying Praguetag:blog.erincall.com,2008:p/prague-day-3-actually-enjoying-prague2014-06-04T03:15:00Z<p>Waking up late Tuesday morning, I resolved to simply head home. The desk was somewhat flummoxed to see me checking out early, but I needed my medicine, so I was resolute--or desperate, take your pick. I headed down the hill to the tram. I&#39;d picked up a couple extra transit tickets at the hospital the day before, so I was able to jump on and head towards town.</p><p>Remember yesterday, when I said I should&#39;ve been looking for the metro? Well, the tram didn&#39;t head toward town. When I realized I was going the wrong way, I got off, rode back to where I figured I&#39;d left the right path, and started reading my map more carefully. My slow realization that the tram and the metro weren&#39;t the same thing was interupted by a peal of thunder, and the skies opened up.</p><p>Lost, tired, and unmedicated, I gave up on transit and found a bartender to call me a cab. He apparently found a car service instead, but 15 minutes later I was off the street and on the way home. At least the downpour had washed some of the stink off my 4-day-old set of clothes.</p><p>At the airport I found the baggage office and asked to upgrade my delayed-bag status to lost-bag. Confused, the agent told me my bag had been delivered to the hotel the day before. Apparently nobody at the hotel or baggage office had thought I might be interested in that information. After a stunned silence, I simply stormed off, furious and hurting.</p><p>It was good news--<i>excellent</i> news, in fact--but for several long minutes the previous day&#39;s ordeal just crashed upon my head again. It took every self-control technique at my disposal to gather my thoughts and decide that yes, I did want to try again to enjoy a trip to Prague. A few phone calls I had hotel reservations, and I found a taxi.</p><p>The driver had enough English for me to communicate what I needed, but not nearly enough for me to explain why I wanted him to go from the airport to a cheap hotel, wait 45 seconds until I came back out with a large bag, and then continue to a nice hotel. The best I could do was grin weakly. Sharing that ridiculous moment with him and finally being able to laugh was like coming out of the water and taking a breath of clean air.</p><p>I checked into my lovely old-new hotel:</p><p><img src="https://cdn.erincall.com/dfa7d5fdcd01f04d903e261bbfe7a640b7009343_small" alt="Hotel Hastal"></p><p>I showered and shaved and changed my clothes.</p><p>I brushed my teeth and put on deodorant.</p><p>I strode out the door, tall and confident and brave, and <i>immediately made a beeline for the nearest restaurant</i>, because I hadn&#39;t actually eaten anything but a Snickers bar all day.</p><p>Once I got some pork and mashed potatoes into me, I went and did a little sightseeing before diving headfirst into bed. <a href="https://catsnap.erincall.com/image/1935">Have a look at my pictures of the evening</a>!</p>Prague Day 2: DrugQuesttag:blog.erincall.com,2008:p/prague-day-2-drugquest2014-06-02T19:15:00Z<p>Upon waking on my second day in Prague, I resolved to just stick things out and try to enjoy the city without my medicine. I headed down the hill and found the tram, but no ticket machine. Some days later I would realize I should have been looking for the subway, not the tram, and if I&#39;d found the subway I would&#39;ve found a ticket machine. At the time, all I knew was I couldn&#39;t get a ticket.</p><p>I figured I&#39;d just follow the tracks into the Old Town. Looking at google maps now, I can see this probably would&#39;ve worked, but it would&#39;ve been a 5-mile hike each way. In reality I made it maybe a kilometer before the lethargy and weakness from my withdrawal pulled me up short. Casting about, I found a pharmacy and tried to convince them to retrieve my prescription from my pharmacy in the US. They either didn&#39;t understand what I was saying, or you can&#39;t transfer prescriptions internationally like that. Either way, they turfed me to a hospital I&#39;d passed earlier.</p><p>It was at the top of a hill, of course.</p><p>The foreign-patients office at the hospital didn&#39;t think their doctors would be able to help with my prescription, so they referred me to a psychiatric hospital a few miles away. The hospital, at least, had a working ticket machine. It took about an hour by tram and bus to get to the right stop, and then I was stymied again. The directions I got at the hospital said simply &quot;go through the park and you&#39;ll see it.&quot; I think this was a poor translation: the hospital is part of a large complex of buildings surrounded by trees, but I went haring off through a small municipal park.</p><p>After pausing for some lunch I got the waiter to give me some directions, which also proved wrong. Eventually I found a map that got me onto the facility grounds, where I met a porter who spoke no English. He pointed off into the trees, where I found nothing that looked useful and nobody who could speak to me.</p><p>It&#39;s important to understand my state of mind at this point. If I&#39;d been in full control of my brain, I would&#39;ve been able to handle the situation gracefully. Operating at full capacity, I simply see obstacles and setbacks as problems to be solved. I&#39;m quick to see the humor in the situation and to think of the bright side. <a href="https://en.wikipedia.org/wiki/SSRI_discontinuation_syndrome#Venlafaxine">Without my medicine</a>, though, I lacked these capacities. Problems seemed insurmountable, the situation seemed hopeless, and humor was entirely out of reach. Additionally, having packed my hat and sunscreen in my missing bag, I was sunburned and flirting with heatstroke from walking in the sun all day.</p><p>So, I sat on a bench and wept.</p><p>Eventually I cried myself out and resolved to just go home. It was already getting late, so I figured I&#39;d head back to my hotel for the night and fly out in the morning. I dropped by the desk and asked about my bag, but they hadn&#39;t seen it, so I trudged up to my room to try and get some sleep.</p><p>As you can imagine, I didn&#39;t particularly feel up to photography, so I don&#39;t have any pictures from today.</p><p>Tomorrow: For Crying Out Loud!</p>Prague Day 1: Travel and the A-and-O Hosteltag:blog.erincall.com,2008:p/prague-day-1-travel-and-the-a-and-o-hostel2014-06-02T01:00:00Z<p>I took a trip to Prague! I&#39;ve been wanting to visit Europe for some time, and Prague seemed like a great entree to the continent. It&#39;s not very well-known in the US, but is actually the 6th most-visited city in Europe. Largely untouched by the world wars, it retains architecture and infrastructure dating back to the middle ages. The Nazis occupied the city, but didn&#39;t destroy the Jewish Quarter. Later the Soviets took control, but mostly limited their defilements to the outer boroughs. Horrors were certainly committed in the area, but were apparently limited to the humans, leaving the structures intact.</p><p>Wow, when I write it out like that, it sounds incredibly ghoulish. Let&#39;s say instead that it preserves tangible remembrances of Europe&#39;s history, and change the subject.</p><p>My trip was marred by problems that ranged from inconvenience to disaster, and they kicked off at the airport before I even left Portland. After we boarded the plane we sat motionless for about 10 minutes, before the pilot announced that the ground crew had run some equipment into the plane, denting it badly enough that the engineers wouldn&#39;t clear it to fly, which was inconvenient for the passengers, and disastrous for whoever had to explain to their boss why they broke an $85,000,000 plane. We all filed sullenly off the plane and started making calls to reschedule our flights. I got a new flight set up, and everything seemed fine.</p><p>Many hours later, tired and smelly, I arrived at Vaclav Havel Airport outside Prague, went to the bag check, and waited. And waited. Finally I went and talked to the baggage desk, where I learned that not only was my bag not in Prague, there was no way to know where it actually was. I was furious: misplacing the bag is a simple mistake, but the lack of a tracking system seemed (and seems) ridiculously incompetent. I was also mad at myself, because I&#39;d packed my critically-important medicine in my checked bag. Being without it is really rough--not only does my depression resume eagerly, the <a href="http://en.wikipedia.org/wiki/Venlafaxine#Discontinuation_syndrome">withdrawal symptoms</a> include weakness, lightheadedness, and problems regulating my heartrate.</p><p>I couldn&#39;t accomplish anything by yelling at the baggage clerk, though, so I bought a local prepaid cell phone, gave the baggage office my number, and headed off to my hotel.</p><p>I got checked in, grabbed my camera, and went to do some sightseeing around my hotel. I&#39;d reserved a bed in a shared room at the <a href="http://www.aohostels.com/en/prague/prag-metro-strizkov/">A-and-O Prag Metro Strizkov</a>, a hotel/hostel in the hills above the city center. At $100 for the week, I wasn&#39;t expecting much, and I didn&#39;t get it. The staff were distant and unhelpful, the furniture was Ikea, and the room smelled of industrial chemicals. Still, in a city with Prague&#39;s reputation, who wants to spend a bunch of time in their hotel anyway?</p><p>Unfortunately, the area around the A-and-O isn&#39;t particularly pretty. It&#39;s not a particularly old or affluent area; there are lots of crumbling walls and car-repair shops. I did find a restaurant with absolutely excellent food, at least.</p><p>That&#39;s all for today, but be sure to <a href="https://catsnap.erincall.com/image/1925">take a look at the pictures I took</a>.</p><p>Tomorrow: sunburn, psychiatry, and senselessness!</p>Getting Interrupted Is Your Jobtag:blog.erincall.com,2008:p/getting-interrupted-is-your-job2014-05-18T17:02:13Z<p>There&#39;s a piece of &quot;wisdom&quot; floating around in programming culture that says you should never interrupt programmers. I imagine it comes from early-stage startups, where the business is make-or-break on getting a viable product out the door as fast as possible. In that context, it makes some sense.</p><p>Once the business is running, though, it&#39;s ludicrous. The programmers aren&#39;t the only work center that matters any more, and they probably aren&#39;t even the most critical one. If you as a programmer have information that someone needs, it&#39;s part of your job to take your headphones off for a minute and talk to them.</p><p>Look, I get it: interruptions can break your concentration and make you lose time while you rebuild your train of thought. And there are definitely better and worse ways to execute an interruption: charging into the room already talking, or installing a device on someone&#39;s desk that makes a loud insistent noise on demand, is terribly rude. If someone sends you an IM, though, or comes into the room and waves a hand to get your attention, get yourself to a stopping place and talk to them.</p><p>Other people have jobs too. Their jobs are important too. Unless you&#39;re absolutely, 100% sure that your productivity is <a href="https://en.wikipedia.org/wiki/Theory_of_Constraints">the tightest constraint on your business&#39;s throughput</a>, optimizing it isn&#39;t even all that important. Instead you should focus on doing what you can to ensure work is moving smoothly through the company as a whole.</p><p>Now, there is a pathological case where interruptions really are a problem. If you&#39;re spending your whole day, every day, responding to do-it-now requests, it&#39;s almost a certainty that you aren&#39;t working on the actual high-priority tasks. This is a <i>management</i> problem, though. The solution isn&#39;t to be <a href="https://www.youtube.com/watch?v=HLI7MLZYPBg">a surly jerk who snarls at anyone with temerity to bother them</a>. The solution is to start bumping these requests up the chain: &quot;I&#39;m sorry, but I&#39;m really swamped. Can you talk to my boss so they can find you someone to answer your question?&quot; Prioritizing and queueing tasks is a project manager&#39;s raison d&#39;etre. They love doing that stuff! Let them do it!</p><p>On the other hand, it&#39;s <i>never</i> acceptable to touch a programmer&#39;s screen. Keep your greasy fingers to yourself!</p>Setting Up SNI On Cloudfronttag:blog.erincall.com,2008:p/setting-up-sni-on-cloudfront2014-04-29T00:27:07Z<p>I have this app <a href="http://catsnap.erincall.com">Catsnap</a> that I use to organize my photos (as well as gifs I pick up around the internet). It stores the images on Amazon S3, and has a cloudfront distribution attached for OMGFAST load times. The cloudfront distro has an ugly domain, though--&quot;d5hwde6hzncg6.cloudfront.net&quot;. The links don&#39;t look like something you should click.</p><p>So, this problem has a trivial solution, right? Just <a href="http://www.petekeen.net/dns-the-good-parts">make a CNAME</a> pointing e.g. cdn.erincall.com to d5whatever.cloudfront.net? Yes, BUT: I&#39;d no longer be able to use SSL/TLS. The SSL/TLS model ties certificates to particular domain names, so the certificate Amazon has for *.cloudfront.net is invalid for cdn.erincall.com (or any domains other than *.cloudfront.net). <a href="https://www.tbray.org/ongoing/When/201x/2012/12/02/HTTPS">I think using SSL/TLS is important</a>, so that wasn&#39;t acceptable.</p><p>Fortunately, the <a href="http://en.wikipedia.org/wiki/Server_Name_Indication">SNI</a> extension to TLS offers a fix for this, and since March 2014, Cloudfront supports it. Let&#39;s get into setting it up!</p><h3>Generate a private key and CSR</h3><p>Amazon requires you to upload the private key you&#39;ll use, so I think you should assign a specific key to your CDN. I have a wildcard certificate I could use for my cdn domain, but distributing private keys to anyone--even an entity as reputable as Amazon--is a bad idea, so let&#39;s generate a fresh public/private key pair to use with Amazon. Run these commands (you may have to install openssl):</p><pre><code>openssl genrsa -out cdn_erincall_com.key 2048
openssl req -new -key cdn_erincall_com.key -out cdn_erincall_com.csr</code></pre><p>Of course, you should replace <code>cdn_erincall_com</code> with your own domain. The second command will prompt you for various information; for the most part it&#39;s obvious what you should enter. It&#39;s imperative, though, that you enter your CDN domain at the &quot;Common Name&quot; prompt, and leave the &quot;challenge password&quot; blank.</p><p>You now have two files, a <code>.key</code> and a <code>.csr</code> (for Certificate Signing Request). The <code>.key</code> file is your private key; you should keep it close to your vest. The .csr is public information and you don&#39;t have to be careful with it.</p><p>Next step is to buy a certificate. Yes, &quot;buy,&quot; yes, this will cost money. It may rankle--it does for me, at least--but the good news is it&#39;s at least cheap. I <a href="https://www.namecheap.com/security/ssl-certificates/domain-validation.aspx">buy my certificates through Namecheap</a>; for the purposes of a CDN certificate you just need the cheap $9.00/year one. Once you&#39;ve gone through the purchasing process (which can take quite a while; be patient), you&#39;ll eventually receive an email from Comodo, with a zip file containing your certificate.</p><h3>Put your certificate and private key into AWS</h3><p>While you&#39;re waiting for the email, let&#39;s get the AWS command-line client set up. If you already have <code>pip</code> installed, it&#39;s easy to install the AWS CLI: <code>pip install awscli</code>. You&#39;ll also want to configure an access key id and secret for the CLI to use: <code>aws configure</code>.</p><p>Ok, check your email. Got that zip file? Unzip it to find your certificate, as well as several other certificates representing a &quot;root chain.&quot; You&#39;ll need to combine the root chain certificates into one large &quot;chain&quot; file. Comodo doesn&#39;t provide much information about which order they need to go in. Personally, I had three files: <code>COMODORSADomainValidationSecureServerCA.crt</code>, <code>COMODORSAAddTrustCA.crt</code>, and <code>AddTrustExternalCARoot.crt</code>. You may receive a different set. Whatever you have, you need to concatenate them into a single file, with the CA Root <i>last</i>. Do not include your site&#39;s certificate (this may be different from what you&#39;re used to when feeding a chain to, say nginx). The command I used was:</p><pre><code>cat COMODORSADomainValidationSecureServerCA.crt COMODORSAAddTrustCA.crt AddTrustExternalCARoot.crt &gt;| cdn_erincall_com.chain</code></pre><p>Now you need to upload to Cloudfront using the AWS CLI. Pick a name for your certificate. It just needs to be meaningful to you; you&#39;ll use it in the AWS console. The upload command is:</p><pre><code>aws iam upload-server-certificate --server-certificate-name $NAME --certificate-body file://cdn_erincall_com.crt --private-key file://cdn_erincall_com.key --certificate-chain file://cdn_erincall_com.chain --path /cloudfront/production/</code></pre><p>That command might fail with a MalformedCertificate error:</p><pre><code>A client error (MalformedCertificate) occurred when calling the UploadServerCertificate operation: Unable to validate certificate chain. The certificate chain must start with the immediate signing certificate, followed by any intermediaries in order.</code></pre><p>This error means you didn&#39;t put the root chain certificates in the right order. Remember that the CA Root certificate goes last, and beyond that, as far as I can see you sorta have to guess and permute.</p><h3>Set up your Cloudfront distro to use your certificate</h3><p>Ok, this has been a lot, but we&#39;re almost done. Head to <a href="https://console.aws.amazon.com">the AWS console</a>, sign in, select Cloudfront, select the Cloudfront distro you&#39;re using, click Distribution Settings, then click Edit. Add your custom domain to the &quot;Alternate Domain Names (CNAMEs)&quot; field. Select Custom SSL Certificate and grab your certificate by name in the dropdown. Finally, under Custom SSL Support, select &quot;Only Clients that Support Server Name Indication (SNI)&quot;. You can choose All Clients instead if you want, but beware! It&#39;s $600.00/month. Hit &quot;Yes, Edit&quot; and you&#39;re all done in the AWS console.</p><p>Keep in mind that it&#39;ll take some time--15 minutes or so--for the new settings to propagate out to the various Cloudfront servers. Be patient!</p><h3>Create a CNAME</h3><p>The last step is just to create a CNAME in your DNS provider. The specifics of this are highly tied to your DNS provider, so I won&#39;t get into too much detail, but if you go into your DNS provider&#39;s console and click CNAME they should make it evident what you need to do next.</p><h3>Final thoughts</h3><p>Amazon does provide <a href="http://docs.aws.amazon.com/AmazonCloudFront/latest/DeveloperGuide/SecureConnections.html#CNAMEsAndHTTPS">some documentation on this process</a>. I found that it was difficult to follow: the simple-path instructions are intermingled with all sorts of info about edge cases and side notes. It&#39;s important information to have, but being new to the process, I would&#39;ve preferred a more focused guide (such as what I&#39;ve tried to present here).</p><p>SNI is not supported on all clients. However, its support is broad enough for most purposes: Everything but WinXP and the default browser on very old versions of Android will work. Anyone using those needs to upgrade before they try to view your fancy html5-enabled site anyway ;~)</p>Introducing Bloge!tag:blog.erincall.com,2008:p/introducing-bloge2014-03-30T01:00:00Z<p>I&#39;d like to introduce you to my newest project, Bloge (pronounced like &quot;doge&quot;). It&#39;s a semi-static markdown blog generator written in Haskell, using the <a href="http://snapframework.com/">Snap framework</a> and <a href="https://github.com/snapframework/heist">Heist</a> HTML-rendering engine.</p><p>My previous blog was built using <a href="https://github.com/xaviershay/enki">Enki</a>, a Rails project built to mimic Wordpress. I was never super happy with it; in some ways it was overbuilt and in other ways it fell short. It used Textile for markup, and while Textile is a fine markup system, it never really stuck in my head the way Markdown has, so I was always looking up its syntax. It had a web-based post form, which seems nice, except I don&#39;t have a modal editor in my browser, so...in the end I was just dissatisfied.</p><p>I&#39;m hoping this new built-from-the-ground-up system will make me much happier! :~)</p>What do you think of this mailing-list message?tag:blog.erincall.com,2008:p/what-do-you-think-of-this-mailing-list-message2014-02-24T20:22:00Z<p>I&#39;ve been subscribed to the email list for Cascade Bicycle Club since I signed up for the Seattle To Portland Bicycle Classic last year. This morning they sent me an email from the &quot;Bike Bot&quot;:</p><blockquote><p>Hi Andrew --</p></blockquote><blockquote><p>Let me introduce myself. I&#39;m Bike &quot;I&#39;m smarter than you&quot; Bot, the Director of Cascade&#39;s Intelligence Agency.</p></blockquote><blockquote><p>I&#39;m not human. I&#39;m an internet program that&#39;s been trolling through how many emails you&#39;ve been opening from the Cascade Bicycle Club and how many actions you&#39;ve been taking.</p></blockquote><blockquote><p>And I have to say, I&#39;m a little disappointed (like Siri gets when you ask her a dumb question ... you know the tone). You&#39;ve opened fewer than one out of four emails from Cascade, and you&#39;ve never, ever signed a petition, sent an email to a decision-maker, or attended a lots-of-humans-in-the-room (ick) Cascade advocacy event.</p></blockquote><blockquote><p>So, I&#39;ve instructed the human advocacy staff at the Cascade Bicycle Club to remove you from the advocacy email list. Unless you tell me otherwise, you won&#39;t receive another Advocacy Alert from Cascade.</p></blockquote><blockquote><p>&lt;...snip...&gt;</p></blockquote><blockquote><p>By the way, here&#39;s a public service announcement for all you humans from an internet bot: don&#39;t get any ideas from the movie &quot;Her.&quot; We won&#39;t love you, we&#39;ll play you. Siri doesn&#39;t even like you. Plus, humans shouldn&#39;t be stealing my dates.</p></blockquote><p>I thought this was funny and cute! I guess a lot of people disagreed, though, because a few hours later I got an apology:</p><blockquote><p>Hi Andrew --</p></blockquote><blockquote><p>This morning, in trying to add a little levity to Cascade&#39;s effort to ensure you only receive the emails you want, we crossed a line. What we thought was funny now clearly wasn&#39;t. Our sincerest apologies.</p></blockquote><blockquote><p>Sincerely,</p></blockquote><blockquote><p>Thomas Goldstein, Policy Director
Cascade Bicycle Club</p></blockquote><p>That&#39;s a good apology, but I don&#39;t see the need for it. Do you? What do you think it was that upset people?</p>I Have Beef With How Git Push Is Taughttag:blog.erincall.com,2008:p/i-have-beef-with-how-git-push-is-taught2014-01-10T22:41:00Z<p>There&#39;s a particular detail in the workings of the @git push@ command that&#39;s almost always elided in tutorials. It&#39;s one of those details where you don&#39;t often need to exercise your knowledge of it, but you occasionally need to know it exists. If you don&#39;t know you don&#39;t know it, you can end up in a frustratingly confusing state.</p><p>Pretty much <a href="http://try.github.io/levels/1/challenges/11">every</a> <a href="http://gitready.com/beginner/2009/01/21/pushing-and-pulling.html">git</a> <a href="https://www.atlassian.com/git/tutorial/remote-repositories#!push">tutorial</a> <a href="http://www.vogella.com/tutorials/Git/article.html#gitpushbranch">I&#39;ve</a> <a href="http://gitimmersion.com/lab_48.html">looked</a> at makes a similar claim: &quot;The syntax of this command is <code>git push &lt;remote&gt; &lt;branch&gt;</code>.&quot;</p><p>That&#39;s false! The syntax of the command is <code>git push &lt;remote&gt; &lt;local-ref&gt;:&lt;remote-branch&gt;</code>. You can omit some of the keywords, and their values will be inferred in various ways. And in fact, it&#39;s extremely common to omit the <code>local-ref</code> and colon, leaving the command looking the way tutorials claim it always looks.</p><p>The <a href="http://git-scm.com/docs/git-push.html">official git docs</a> get it right, of course, but they&#39;re totally inaccessible to a beginner (or, well, anyone who isn&#39;t a git maintainer themselves). No help there.</p><p>You might wonder if it&#39;s all that important. After all, how often do you really need to use the fully-explicated form? You&#39;d be right, to a point. The only time you really need to specify both the local ref and the remote branch is when you want to push a local branch to some remote branch with a different name. Problem is, when the tutorials don&#39;t explain that another form of the command even exists, then someone who needs the full form don&#39;t know to look for it. On several occasions, I&#39;ve talked to people who did something like:</p><pre><code>git checkout some-branch
git commit
git push origin some-other-branch</code></pre><p>expecting it to push the HEAD of <code>some-branch</code> to origin&#39;s <code>some-other-branch</code>. In other words, they&#39;d made some changes on one branch, and they wanted to push those changes up to another branch. That&#39;s a reasonable thing to want to do, and it was even reasonable to think it would work. The problem was, having never even heard that there was a longer form for <code>git push</code>, they didn&#39;t even know to look into it. They were just lost at sea.</p>Microaggressions and Intenttag:blog.erincall.com,2008:p/microaggressions-and-intent2013-11-23T05:04:00Z<p>I received a surprising note in an email:</p><blockquote><p>I came across your blog and really enjoyed your post about &#39;microaggressions&#39;. I went to college at UCLA, and met many people who, though their hearts were in the right places, would twist others&#39; words to frame them as microaggressions or otherwise sexist/racist comments when they, by intent, were not.</p></blockquote><p>This person has misconstrued my purpose in writing <a href="/p/i-dont-get-the-concept-of-microaggressions">the previous post</a>! Let me clarify: I do believe such a thing as a microaggression exists, I just wanted to understand more clearly what it was.</p><p>This email-sender strikes a note I&#39;ve heard (and played, in the past) often: the notion that a harmful action with no malicious intent should be held blameless. This is not a position that adults typically take. Although a child might indeed adopt &quot;I didn&#39;t mean to&quot; as a first line of defense, most of us grow out of it in time--except when the harm was done to someone else&#39;s feelings.</p><p>I&#39;m not sure why it&#39;s so hard for those of us in privileged positions to accept that hurting someone by accident is still hurting someone, and thus unacceptable. In this particular case we could point a finger at the language: that &quot;aggression&quot; there at the back end of &quot;microaggression&quot; does suggest some intentionality. The problem in general extends beyond this one word, though. Richie Incognito&#39;s use of &quot;n----r&quot; is unacceptable for all that it &quot;comes from a place of love,&quot; for example.</p><p>Our culture has an aphorism for this situation: &quot;the road to hell is paved with good intentions.&quot; What matters is always the actual result of your actions, not what you hoped would happen.</p>I Don't Get The Concept Of Microaggressionstag:blog.erincall.com,2008:p/i-dont-get-the-concept-of-microaggressions2013-10-18T05:19:00Z<p><i>Update</i>: &quot;wilkie&quot; has knocked it out of the park with a thorough <a href="/p/i-dont-get-the-concept-of-microaggressions#comment-1086823065">explanation in the comments</a>. Make sure you check it out!</p><p>This post involves a discussion of two people, &quot;Rita&quot; and &quot;Bastian,&quot; whose identities I&#39;ve chosen to obfuscate. An astute reader will be able to divine their identities. A <i>responsible</i> reader will choose not to do so. I invite you to be responsible.</p><p>I spent some time this evening discussing feminism in tech. My colleague Rita brought up the subject of microaggressions. She gave the following anecdote as an example:</p><blockquote><p>I was talking with Bastian about programming languages and mentioned that people don&#39;t like X for reason Y. He wondered, &quot;how would you know?&quot;</p></blockquote><p>You must understand that Rita is less experienced in programming than Bastian. It is easy, therefore, to see how this comment would be painful for Rita to hear. Even though it was not meant maliciously, it brought into sharp focus the difference in their experience levels--and thus, not trivially, their socioeconomic standing.</p><p>I don&#39;t want to make Bastian out to be a villain, here. Indeed, Rita went on to defend him as a sweet guy who meant no harm. It was an offhanded comment that caused more damage than it was meant to, and if anyone reading this has never done that, PLEASE BE MY FRIEND, because you&#39;re an angel.</p><p>So the thing I don&#39;t really get here though is how it relates to feminism. Rita didn&#39;t bring this up as an example of how people can cause more harm than they mean to. She brought it up as an example of <a href="http://en.wikipedia.org/wiki/Microaggression">microaggressions</a>: subtle indignities against a less-privileged person. The term &quot;aggression&quot;, to me, implies a certain amount of emotional intentionality that I don&#39;t see in the anecdote at hand.</p><p>I&#39;m able to see that there is a privilege imbalance, and I&#39;m able to see how this was a subtle indignity. I&#39;m not able to see how those two facts coalesce into a matter of import. People must be free to make mistakes--and indeed, Rita admits that Bastian&#39;s actions were nothing more than that. So how did this transform from &quot;poorly-phrased&quot; to &quot;microaggression&quot;?</p><p>I hope someone reading this has the time and energy to straighten me out on this matter, because it&#39;s weighing on my mind. I want to be an enlightened fella, but many aspects of the egalitarianism that I profess are unintuitive to me.</p>Regular Expressionstag:blog.erincall.com,2008:p/regular-expressions2013-10-12T06:37:00Z<p>My friend <a href="http://shawnacscott.com/blog/">Shawna Scott</a> encountered a need for regular expressions for the first time recently. I went to link her to some information, but I couldn&#39;t find a single introduction online that satisfied me. That lack seems disastrous. Regular Expressions are so important that every major programming language provides an engine for executing them, and many embed that engine directly into the parent language&#39;s syntax. They&#39;re inescapable--yet no tutorial measures up. Let&#39;s see what I can do, shall we?</p><h3>What Are Regular Expressions?</h3><p>Regular Expressions are a specialized programming language for describing some aspect of a body of text that is of interest to you. I want to emphasize that regular expressions are a whole and entire <i>programming language</i>. They are not Turing complete, and their abilities are limited to text processing. Nevertheless, they truly are a language in their own right.</p><p>Regular Expressions describe some body of text. It&#39;s true that they are often used for &quot;matching&quot; or &quot;substituting&quot; text, but those are functions of the engine that implements them. At its core, an expression describes a piece of text. This, I think, is an important concept! When you find yourself lost trying to read an expression, ask yourself, &quot;What text does this describe?&quot;</p><p>For example, consider one of the most ubiquitous and important expressions: <code>.*</code>. This expression describes a body of text that contains 0 or more characters--that is, it describes anything, from the collected works of Shakespeare to this very blog post. It might surprise you that &quot;everything and anything&quot; is a ubiquitous and important description! You might also feel a little confused about what those two nonalphanumeric characters have to do with your grocery list. Don&#39;t worry! All will become clear.</p><h3>How Do Regular Expressions Work?</h3><p>Regular Expressions are composed of symbols that describe a piece of text in various ways. A symbol, in this case, might be a letter, a number, or something more exotic like our friend <code>.*</code> from before. Letters and numbers describe text very literally--they just describe text that contains themselves. The expression <code>A1</code> describes the text &quot;A1&quot;, and that&#39;s it.</p><p>Most nonalphanumeric characters have special meanings. The symbol <code>.</code> describes any single character--&quot;A&quot;, or &quot;b&quot;, or maybe a literal &quot;.&quot;. The symbol <code>*</code> modifies the symbol that preceded it: taken together, the two describe 0 or more of the preceding symbol. Can you see now why our friend <code>.*</code> from before describes everything? The <code>.</code> symbol describes any single character, and the <code>*</code> symbol modifies it to describe any number of repetitions of that.</p><p>Be sure you understand the difference between &quot;any number of repetitions of <i>any</i> single character&quot; and &quot;any number of repetitions of <i>a</i> single character.&quot; The latter concept would describe &quot;aaa&quot; or &quot;bbb&quot; but not &quot;abc&quot;. <code>.*</code> describes all three of those pieces of text. When the <code>*</code> symbol modifies another symbol, the modified symbol gets a chance to describe whatever it can, each time that it&#39;s able to.</p><p>There are many other symbols with special meaning in a Regular Expression, and I will not attempt to explain all of them here. However, I do want to briefly cover a handful more symbols that can significantly increase the power of your expressions.</p><ul><li><code>(</code> and <code>)</code> enclose a group of symbols and allow you to apply modifiers to that whole sub-expression. Thus the expression <code>(ab)*</code> describes the text &quot;ab&quot;, and the text &quot;ababababab&quot;, but not &quot;abba&quot;.</li><li><code>[</code> and <code>]</code> enclose a group of symbols and create a sub-expression that describes any one of those symbols at a time. These <b>character classes</b> work like a more restrictive form of the symbol <code>.</code>.</li><li><code>?</code> modifies the preceding symbol--or, as you now know, sub-expression--so that it describes 0 or 1 occurrence of whatever it would normally describe. Thus the expression <code>under_?score</code> describes the text &quot;under<i>score&quot; or &quot;underscore&quot;, but not &quot;under</i>___score&quot;.</li></ul><p>There are many other symbols that do exciting and useful things, and I encourage you to consult the reference in your preferred programming language&#39;s documentation to learn about them.</p><h3>How Do I Use Regular Expressions?</h3><p>As discussed in the introduction, every reputable programming language provides an engine for executing regular expressions, either in the standard library or in the syntax of the language itself. In addition, the Unix utility <code>grep</code> is a tool for using regular expressions on streams of text at the command line.</p><p>There are three tasks for which you&#39;ll typically use a Regular Expression while programming: checking whether a body of text <i>matches</i> an expression, <i>extracting</i> the subset of a body of text that is described by an expression, and <i>transforming</i> or <i>substituting</i> the portions of a body of text that are described by an expression.</p><h3>Checking Whether A Body Of Text Matches An Expression</h3><p>When dealing with user input, you&#39;ll commonly want to make sure that the input conforms to some format. For example, here is some Ruby that verifies that some input seems to contain an American phone number:</p><pre><code class="Ruby">if user_input =~ /\(?\d{3}\)?[.\- ]?\d{3}[.\- ]?\d{4}/
puts &quot;Thanks for telling me your phone number!&quot;
else
puts &quot;Hey, that doesn&#39;t look quite right...&quot;
end</code></pre><p>This regular expression uses some symbols that I didn&#39;t discuss earlier. Can you guess what they do? Don&#39;t worry if you can&#39;t, or you don&#39;t want to spend your time on it--the important thing is the Ruby code surrounding it. It takes an expression and uses it to check whether a body of text contains some substring described by that expression. Up until now, we&#39;ve been dealing with this fairly abstract notion of &quot;describing&quot; some text. The Regular Expression engine provided by Ruby applies that abstract concept to a real-world problem.</p><h3>Extracting Part Of A Body Of Text</h3><p>In the example above, we checked whether a user provided us with their phone number inside a larger body of text. That&#39;s great, but it would probably be better if we could actually extract that phone number from its surroundings. We don&#39;t have to change the expression we used--it still describes the text we want. Instead, we just need to change the way we&#39;re interacting with the Regular Expression engine:</p><pre><code class="Ruby">phone_number = /\(?\d{3}\)?[.\- ]?\d{3}[.\- ]?\d{4}/.match(user_input).to_s
nefarious_telemarketing_database.store_new_phone_number(phone_number)</code></pre><p>Now we&#39;ve gone beyond simply verifying that the text our expression describes is present. We&#39;ve actually extracted it from its surroundings, so we can store it for later use.</p><h3>Transforming Or Substituting A Body Of Text</h3><p>Suppose that instead of operating a soulless call center, we&#39;re cautious custodians of our users&#39; data. We&#39;ve decided that, in order to help safeguard them, we&#39;re going to X out any phone numbers from our log files. That way, those jerks from the last example can&#39;t get ahold of it and steal our users&#39; data...</p><pre><code class="Ruby">sanitized_line = log_line.gsub(/\(?\d{3}\)?[.\- ]?\d{3}[.\- ]?\d{4}/, &#39;XXX-XXX-XXXX&#39;)
log_file.write(sanitized_line)</code></pre><p>Once again we&#39;ve used the same expression to describe the text we&#39;re interested in. The only difference is how we&#39;ve used the Regular Expression engine to interact with that description.</p><h3>Wrapping Up</h3><p>The examples above only cover Regular Expressions in a very shallow way. There are many aspects of their syntax and use that I didn&#39;t even attempt to cover here. However, I hope that I&#39;ve given you the tools you need to start getting value from intermediate tutorials or your language&#39;s reference guide.</p><p>If you&#39;d like to tell me about errors, points of confusion, or other objections, please dive right on into the comments. Of course, if you think I&#39;ve done a good job, I&#39;d love to hear that too.</p>The Splinter Mysterytag:blog.erincall.com,2008:p/the-splinter-mystery2013-08-10T17:42:00Z<p>I&#39;ve been wrestling with a mystery lately. The <a href="https://github.com/ErinCall/catsnap">Catsnap</a> tests use a browser-interaction library called <a href="http://splinter.cobrateam.info/">Splinter</a> to verify the JavaScript on the pages. After I added a few tests, I started to see some fairly bizarre performance problems.</p><p>While the browser was doing things, it was very quick indeed. But at what looked like random times, <a href="http://screencast.com/t/MpEpfe5mm">it would just sit and think about philosophy for a while</a>. I asked about this on the Splinter mailing list and got no responses (I&#39;m not sure how active that mailing list is), so I finally did some investigation on my own.</p><p>I created <a href="https://github.com/ErinCall/slow_splinter_demo">a repository that demonstrates the slow behavior</a>. I&#39;ve built a fairly minimal amount of behavior, so it&#39;s easy to read through. Unfortunately, even though it <i>demonstrates</i> the problem, it still doesn&#39;t <i>explain</i> it. I still have no idea why this particular set of steps leads to such a sudden drop-off in performance.</p><p>At this point I think I&#39;ve exhausted my ability to debug this any farther. I&#39;m hoping to take it to the Cobrateam, uh, team, and see if they&#39;re interested in helping track down the root cause.</p>Announcing Radlibstag:blog.erincall.com,2008:p/announcing-radlibs2013-07-07T21:09:00Z<p>For the last few days I&#39;ve been eating, breathing, and sleeping a new project. Code has flown from my fingers like sparks from a millstone. Today I&#39;d like to announce the result of my fevered efforts: <a href="http://www.radlibs.info">Radlibs</a>! At its core, Radlibs is a laguange for generating English text. It uses randomly-selected category-members to fill in the blanks in a given <a href="http://en.wikipedia.org/wiki/Phrasal_template">phrasal template</a>.</p><p>The &quot;Rad&quot; in &quot;radlibs&quot; comes from its recursive nature: category-members may also be phrasal templates. Thus, with the right libraries Radlibs can generate some good clean fun:</p><p><a href="https://cdn.erincall.com/439301aa34325e767a339d3edfa340e4528c78ff"><img src="https://cdn.erincall.com/439301aa34325e767a339d3edfa340e4528c78ff_medium" alt="crate and barrel"></a></p><p>I&#39;m very proud of what I&#39;ve made, but I&#39;d be remiss if I didn&#39;t mention its influences. Many years ago when I worked at Rentrak, a few developers there generated a post-test hook called game.pl that would simulate a random encounter in a dungeon crawl, with the outcome based on whether your tests had passed or failed. Game.pl was built from the most monstrous regular expression you&#39;ve ever seen (or maybe not--there&#39;s always a regex more monstrous), using perl&#39;s <code>/e</code> regex to implement recursion and branching. Another developer later open-sourced game.pl as <a href="https://github.com/wickline/legendary_flavor">Legendary Flavor</a>, but it hasn&#39;t seen much development since then. You can think of Radlibs as an homage, a port, or a resurrection effort.</p><p>Anyway, I hope you enjoy it. Be sure to <a href="https://github.com/ErinCall/radlibs/issues">open an issue</a> if you encounter any problems.</p>Managing Multiple Identities In Gittag:blog.erincall.com,2008:p/managing-multiple-identities-in-git2012-12-31T00:02:00Z<p>When you use git at work and for personal projects, it&#39;s easy to mess up and make a commit using the wrong identity. You can end up with your work email attached to a commit for your personal work, or your personal email attached to your professional work. One solution is to simply isolate your code: only do your work programming on your work computer and only do your personal programming on your personal computer. That&#39;s not always practical, though. For example, you might find yourself wanting to do personal work while travelling with your work computer. In this post I&#39;ll show you how I manage that problem.</p><p>When you first run <code>git commit</code> on a new computer, it asks you to run a global config command to set up your identity:</p><pre><code>Committer: Erin Call &lt;erincall@Slim.local&gt;
Your name and email address were configured automatically based
on your username and hostname. Please check that they are accurate.
You can suppress this message by setting them explicitly:
git config --global user.name &quot;Your Name&quot;
git config --global user.email you@example.com
After doing this, you may fix the identity used for this commit with:
git commit --amend --reset-author</code></pre><p>That&#39;s fine if you only use one identity to commit, but it&#39;s no good at all for our purposes. Instead, we use environment variables:</p><pre><code>export GIT_AUTHOR_NAME=&#39;Erin Call&#39;
export GIT_AUTHOR_EMAIL=&#39;hello@erincall.com&#39;
export GIT_COMMITTER_NAME=$GIT_AUTHOR_NAME
export GIT_COMMITTER_EMAIL=$GIT_AUTHOR_EMAIL</code></pre><p>Set this up on your personal and work computers, using the appropriate identity on each (don&#39;t forget to clear the identity from your global git config).</p><p>Now for the clever bit: the <code>personal</code> and <code>work</code> aliases. On your work computer, you&#39;ll have a shell alias for making commits using your personal identity:</p><pre><code>alias personal=&quot;GIT_AUTHOR_EMAIL=&#39;hello@erincall.com&#39; GIT_COMMITTER_EMAIL=&#39;hello@erincall.com&#39;&quot;</code></pre><p>And on your personal computer, an alias for committing as your work identity:</p><pre><code>alias personal=&quot;GIT_AUTHOR_EMAIL=&#39;ecall@myjob.com&#39; GIT_COMMITTER_EMAIL=&#39;ecall@myjob.com&#39;&quot;</code></pre><p>Now you can easily switch to the other identity for a single commit:</p><pre><code>personal git commit</code></pre><p>I&#39;ve framed this in terms of work vs. home, but of course it&#39;ll work any time you need to manage multiple identities in git. It&#39;s a simple system that I find is very effective.</p>Catsnap 3.0 Betatag:blog.erincall.com,2008:p/catsnap-3-0-beta2012-12-27T23:15:00Z<p>A beta version of Catsnap 3.0 is now available. 3.0 introduces a major architectural change: instead of accessing dynamodb directly, the command-line script is now a client to a postgres-backed web api. This has several advantages, including browser-based access, reduced operating costs (potentially all the way to $0.00), and improved security.</p><p>You&#39;ll need to run the server code somewhere. The instructions below assume you&#39;re using a VPS or EC2. However, you could also run catsnap on your personal computer, or a managed service like <a href="http://www.heroku.com/">Heroku</a>. In any case, you&#39;ll need to create a postgres database for Catsnap to use. First-time Catsnap users will also need to <a href="http://docs.amazonwebservices.com/AmazonS3/latest/gsg/CreatingABucket.html">create an S3 bucket</a>.</p><p>Deploying the 3.0 server is fairly straightforward. You&#39;ll need to set several environment variables:
<i> CATSNAP<i>API</i>KEY is a secret key the client and server share for authentication. It can be any string of characters.
</i> CATSNAP<i>AWS</i>ACCESS<i>KEY</i>ID and CATSNAP<i>AWS</i>SECRET<i>ACCESS</i>KEY: your AWS credentials.
<i> CATSNAP<i>BUCKET: the S3 bucket where you want catsnap to store images.
<i> CATSNAP<i>OWNER</i>ID: an openid provider that uniquely identifies you as the owner of this catsnap installation.
</i> CATSNAP</i>SECRET_KEY: a secret key to use when generating session identifiers.
</i> DATABASE_URL: a url Catsnap can use to connect to its postgres database.
* PORT: the port on which you want catsnap to listen.</p><p>Once you have your environment set up, <a href="https://github.com/ErinCall/catsnap">clone catsnap</a> into a directory of your choice. Change to the catsnap directory and install its dependencies:</p><pre><code>python setup.py install</code></pre><p>Use yoyo-migrate to build the database:</p><pre><code>yoyo-migrate apply migrations $DATABASE_URL</code></pre><p>If you have an existing catsnap installation backed by dynamodb, there&#39;s a script for migrating your data into postgres. The script isn&#39;t installed by setup.py so you&#39;ll need to run it directly from its path:</p><pre><code>scripts/one-time/migrate_dynamo_to_pg.py</code></pre><p>Start the server with gunicorn:</p><pre><code>gunicorn catsnap.app:app -b 0.0.0.0:$PORT -w 3</code></pre><p>And you should be up and running.</p><p>Installing the client is even easier:</p><pre><code>pip install catsnap==3.0.0b1
catsnap config</code></pre><p>I&#39;m aware of a couple minor issues, but I expect to have them fixed within the next few days. Please <a href="mailto:hello@erincall.com">let me know</a> about any problems you run across (or even <a href="https://github.com/ErinCall/catsnap">send me a pull request</a>), and happy catsnapping!</p>Ruby Blocks Are Way More Complicated Than You Thinktag:blog.erincall.com,2008:p/ruby-blocks-are-way-more-complicated-than-you-think2012-11-22T05:42:00Z<p>I&#39;ve been working with Ruby a lot lately, and it&#39;s impossible to do that very much without noticing how feature-rich the language is. Today I journeyed deep into the strange and wonderful world of blocks. Blocks are used heavily in Ruby, and although they look like the simple anonymous functions I&#39;m used to in JavaScript, their semantics are <a href="http://yehudakatz.com/2010/02/07/the-building-blocks-of-ruby/">significantly more sophisticated</a>. Most relevant to us today is that a function that receives a block doesn&#39;t take it as an explicit parameter. Instead, it checks <code>block_given?</code> to see if the caller provided a block.</p><p>In JavaScript we might write:</p><pre><code class="JavaScript">var call_me_when_you_get_there = function (where_to_go, callback) {
go_to(where_to_go)
if (callback !== undefined) {
callback()
}
}</code></pre><p>In Ruby this plays very differently, using <code>block_given?</code> and <code>yield</code>:</p><pre><code class="Ruby">def call_me_when_you_get_there where_to_go
go_to where_to_go
if block_given?
yield
end
end</code></pre><p>These two examples may look fairly similar at first blush. However, it&#39;s important to notice what keyword ruby has provided for moving program execution to the provided block, because it isn&#39;t just moving the program execution--it&#39;s yielding flow control. If you yield to a block that has the <code>return</code> keyword, it&#39;ll return from more than just the block--it&#39;ll return from the method where that block is defined.</p><pre><code class="Ruby">def call_me_back
if block_given?
yield
end
puts &quot;this string will not print&quot;
end
def return_in_a_block
call_me_back do
return
end
puts &quot;this string will not print, either&quot;
end</code></pre><p>Indeed, neither string prints:</p><pre><code>&gt;&gt; return_in_a_block
=&gt; nil</code></pre><p>That is <i>intense</i>. No other language I know has that sort of flow-control sophistication. It&#39;s especially remarkable because blocks have such a prominent place in real-world Ruby. They aren&#39;t an arcane feature hidden away in the guts of the docs, they&#39;re the primary looping construct. That primacy of place led me into a fairly confusing situation today.</p><p>Ruby has a method called <code>define_method</code> that takes a method name and a block and, as you might expect, defines a method with the given name that executes the given block. For example, we could write a class with an <code>alamarain</code> method:</p><pre><code class="Ruby">class Chula
define_method :alamarain do
puts &#39;Move along home!&#39;
end
end</code></pre><p>This example is a little degenerate; we could as easily have written <code>def alamarain</code>. However, <code>define_method</code> allows us wide latitude for metaprogramming--if circumstances dictate, we could use <code>define_method</code> to name our new method <code>count_to_four</code> or <code>then_three_more</code>, or any number of things.</p><p>However, since the methods we define with <code>define_method</code> are written in terms of a block, their context is a little different than it would be if we were simply writing them out by hand. We already saw that <code>return</code> will apply to the function that encloses our block, not the block itself. It turns out this applies to <code>yield</code> and <code>block_given?</code> as well. We can illustrate this by defining an identical method twice, once with <code>def</code> and once with <code>define_method</code>:</p><pre><code class="Ruby">class TwoTimes
def defined_normally
puts &quot;a block was #{ block_given? ? &#39;&#39; : &#39;not &#39; }given&quot;
end
define_method :defined_by_block do
puts &quot;a block was #{ block_given? ? &#39;&#39; : &#39;not &#39; }given&quot;
end
end</code></pre><p>And the results:</p><pre><code>&gt;&gt; TwoTimes.new.defined_normally do end
a block was given
=&gt; nil
&gt;&gt; TwoTimes.new.defined_by_block do end
a block was not given
=&gt; nil</code></pre><p>If you&#39;ve been following along closely, you see what&#39;s happening here: the <code>block_given?</code> invocation counts for the scope that encloses the block--in this case, the <code>class TwoTimes</code> declaration--not the block itself. So how can we use <code>define_method</code> to define a method that accepts a block? Well, it turns out <code>define_method</code> helpfully transforms a block that&#39;s given to its defined method into a proc, and passes that proc as an argument.</p><pre><code class="Ruby">class SmarterBlock
define_method :defined_by_block do | &amp;block |
puts &quot;a block was #{ block.nil? ? &#39;not &#39; : &#39;&#39; }given&quot;
block.call()
end
end</code></pre><p>This works the way we want:</p><pre><code>&gt;&gt; SmarterBlock.new.defined_by_block do puts &#39;hello!&#39; end
a block was given
hello!
=&gt; nil</code></pre><p>Honestly, I think this is more confusing than it should be. It&#39;s good that Ruby provides a lightweight anonymous-function syntax, and it&#39;s exciting that it provides this advanced flow control mechanism, but I&#39;m not convinced they should be the same thing.</p>Oh, So That's Why You'd Use A Generatortag:blog.erincall.com,2008:p/oh-so-thats-why-youd-use-a-generator2012-07-18T06:19:00Z<p>So, I&#39;ve been working on this <a href="https://github.com/ErinCall/catsnap">catsnap</a> project of mine, improving the performance. The big problem is that it&#39;s spending an enormous amount of time on the wire, waiting to get information back from AWS. For example, when searching for images by tag, my first pass used a pretty na&#239;ve approach: loop through the images associated with a tag, asking DynamoDB about each one. Fortunately, the excellent <a href="http://boto.cloudhackers.com/en/latest/index.html">boto</a> library for interacting with aws has a batched lookup mode that lets me grab all the images at once.</p><p>Only...not quite. DynamoDB has this thing going on where if you ask for some items, it&#39;ll give some or all of them back.[1] It&#39;s polite enough to tell you which keys it ignored, but you still have to ask for them again if you really wanted what you said you wanted.</p><p>The obvious, hamfisted solution is to check and see if there were any &quot;unprocessed keys,&quot; and go fetch those, and then check again, until we&#39;ve finally gotten everything we wanted in the first place, and finally return. That leaves the user twiddling their thumbs, waiting on all those requests.</p><p>So instead I built <a href="https://github.com/ErinCall/catsnap/blob/batched-requests/catsnap/batch/image_batch.py#L26">a generator</a> that immediately starts yielding whatever it has, and then goes back for more if anything is missing. It makes for a nice, snappy-feeling app.</p><p>I knew python generators existed, but I&#39;d never really had occasion to use one. I think I&#39;d fallen victim to that situation where people demonstrate a feature with toy problems, and although their simplicity makes it clear <i>how</i> to use the feature, their triviality makes it very vague <i>why</i> you&#39;d use the feature. I&#39;m glad I finally see the point!</p><p>[1] The <a href="http://docs.amazonwebservices.com/amazondynamodb/latest/developerguide/API_BatchGetItems.html">API docs</a> imply that this should only crop up for relatively large queries, but I was seeing it for queries on 20-or-so items from tables that were well under a MB. Probably I was hitting the ceiling on my relatively low read-throughput setting.</p>Bike Camping!tag:blog.erincall.com,2008:p/bike-camping2012-07-17T05:22:00Z<p>I&#39;ve wanted to go bike touring for some time, and I figured a good starting place was to go camping near home. On Friday afternoon I went to <a href="https://www.oregonstateparks.org/park_113.php/">Champoeg State Park</a>, which turns out to be fantastic!</p><p>The <a href="https://www.bikely.com/maps/bike-path/Portland-to-Champoeg-State-Park-Route-A">route</a> I took was about 35 miles, which is quite a ways with a heavily-laden bike! The poundng heat didn&#39;t help, either. As you can imagine I was pretty glad to arrive!</p><p><a href="https://d20xvz37guknpy.cloudfront.net/Arrival_thumb.jpg"><img src="https://d20xvz37guknpy.cloudfront.net/Arrival_thumb.jpg" alt="Arrival"></a></p><p>I immediately set up camp:</p><p><a href="https://d20xvz37guknpy.cloudfront.net/Campsite1.jpg"><img src="https://d20xvz37guknpy.cloudfront.net/Campsite1_thumb.jpg" alt="Campsite shot"></a></p><p>And started making dinner:</p><p><a href="https://d20xvz37guknpy.cloudfront.net/MakingDinner.jpg"><img src="https://d20xvz37guknpy.cloudfront.net/MakingDinner_thumb.jpg" alt="Big old potbelly"></a></p><p>Later, a nice older couple from Florida arrived. We were in the general hiker/biker camp, so we chatted over a beer. They were about to start riding the <a href="https://rideoregonride.com/inspiration/itineraries/scenic-bikeways-a-unique-way-to-see-the-state/">Willamette scenic bikeway</a>, a signed highway route running from Champoeg to Eugene. I wanna!</p><p>The day ended with a lovely sunset:</p><p><a href="https://d20xvz37guknpy.cloudfront.net/Sunset.jpg"><img src="https://d20xvz37guknpy.cloudfront.net/Sunset_thumb.jpg" alt="Sunset"></a></p><p>Saturday morning I hiked over to the original park that grew into Champoeg. It was founded in 1901 as Provisional Government Park, commemorating the site of the vote that established the original Oregon government. That vote barely passed, 52 votes in favor to 50 against. Who knew how close Oregon came to not even happening? There&#39;s a cool monument with the names of those who voted in favor (those who voted against aren&#39;t remembered at all):</p><p><a href="https://d20xvz37guknpy.cloudfront.net/Monument.jpg"><img src="https://d20xvz37guknpy.cloudfront.net/Monument_thumb.jpg" alt="Monument"></a></p><p>At lunch I rode out to Butteville and got a sandwich from Oregon&#39;s oldest continually-operating retail establishment. It was very cute but I didn&#39;t take any pictures.</p><p>In the afternoon I went to the park&#39;s visitor center, where there was a blacksmithing demonstration underway:</p><p><a href="https://d20xvz37guknpy.cloudfront.net/Blacksmith1.jpg"><img src="https://d20xvz37guknpy.cloudfront.net/Blacksmith1_thumb.jpg" alt="A hot piece of steel"></a></p><p><a href="https://d20xvz37guknpy.cloudfront.net/Blacksmith2.jpg"><img src="https://d20xvz37guknpy.cloudfront.net/Blacksmith2_thumb.jpg" alt="Hammer hammer"></a></p><p>In the evening a group of cyclists riding with <a href="https://www.cyclewild.org/">Cycle Wild</a> arrived. They were a great bunch, and I invited myself to join them. I think I&#39;ll join their rides in the future. I joined them around the fire:</p><p><a href="https://d20xvz37guknpy.cloudfront.net/Fireside.jpg"><img src="https://d20xvz37guknpy.cloudfront.net/Fireside_thumb.jpg" alt="kinda creepy"></a></p><p>Sunday morning I rode back. It was a cool misty morning, so I was able to make much better time than on the way out. What a wonderful weekend!</p>Unreserved Praise For Patrick Rothfusstag:blog.erincall.com,2008:p/unreserved-praise-for-patrick-rothfuss2012-06-25T03:41:00Z<p>p. Lately I&#39;ve been geeking out about <i>The Name Of The Wind</i> with friends, so I went in for a re-read. I was immediately struck by how <i>dense</i> the opening chapters are, laying out themes and setting up later events. I&#39;m gonna take a few minutes to blather at you about how much I like them.</p><p>Let us, as Arliden would demand, begin at the beginning. The first sentence in the prologue is &quot;It was night again.&quot; Again? It&#39;s the first sentence! Of the <i>prologue</i>! It&#39;s a dreary start, better suited to some existential philosophical discourse than the tale of excitement the back-cover blurb promised us. But this is a tragedy, as we&#39;ll see in the prologue&#39;s last sentence. There&#39;re fireworks in the middle, but at the end it&#39;s the story of a man who is waiting to die.</p><p>After the prologue, though, things are a little more what you&#39;d expect. We open in a tavern, the sort where a D&amp;D party might begin their adventure. Old Cob is telling a story of Taborlin The Great to a small crowd: his friends Jake, Shep, and Graham, along with the smith&#39;s apprentice. Immediately Rothfuss introduces the Chandrian, and the story-within-a-story motif that&#39;s gonna recur and recur, and the theme of &quot;old stories, corrupted by time, but with a kernel of truth.&quot; We see Kvothe think of the Chandrian as &quot;familiar.&quot; This is all in the first half-page of chapter one.</p><p>In the next page the Taborlin story continues. Taborlin leaps from a high tower, but he knows the name of the wind, so he&#39;s borne safely to the ground. So now, <i>already</i>, on page four including the prologue, we see the meaning of the book&#39;s title: names have power. Next there&#39;s an interruption over the exact phrasing of a proverb. Everyone remembers it differently--again with the corrupted stories--but they all agree on another recurring theme: Be Friendly To Tinkers.</p><p>The tinker argument segues into a description of Taborlin&#39;s amulet: it&#39;s &quot;cold as ice to touch.&quot; We can infer it&#39;s either a gram or an arcanist&#39;s guilder. This is sort of thing that gives me confidence that Rothfuss means what he says when he announces a release date, by the way. It&#39;s just a passing reference in the first chapter, but it foreshadows something that won&#39;t come up again for ages and ages. We see Abenthy&#39;s guilder, but there&#39;s no mention at all that it might be protective until midway through book 2. The author knows <i>exactly</i> where he&#39;s going.</p><p>We have more debate about the origins of the Chandrian and the exact effectiveness of Taborlin&#39;s amulet. More corrupted stories...and then Carter bursts in, wounded and carrying a dead Scraeling. Suddenly the stories are right here in the tavern, uncomfortably close. This is where the thematic buildup tapers off and things themselves start happening, so this is where I&#39;ll leave off.</p><p>Consider: everything I&#39;ve written about here is in the first six pages. There&#39;s a lot going on here!</p>Sorry Brogramming, I Gotta Cut You Loosetag:blog.erincall.com,2008:p/sorry-brogramming-i-gotta-cut-you-loose2012-03-23T13:29:00Z<h3>Nerds</h3><p>Let us begin at the beginning. Most programmers were probably not the cool kids. By and large, we were the fringe kids: futzing around with computers and reading sci-fi, rather than working on our rushing game.</p><p>When we came of age and started working together, we were excited to find ourselves on the inside. I clearly remember how thrilled I was at my first Real Job when I realized I could make offhand references to Admiral Thrawn or whatever, and everyone in earshot would get what I was saying. So it&#39;s natural that we formed our own tribe. This wasn&#39;t <a href="http://www.imdb.com/title/tt0088000/">Revenge Of The Nerds</a>, where the nerds win by adopting mainstream norms. This was nerds living well on their own terms.</p><p>Of course, once you have a tribe, you&#39;ll start to have shibboleths. It&#39;s an integral part of human society: you want to know if someone&#39;s a member of the group, so you know if you can trust them. The heuristic isn&#39;t actually all that good, but it seems to be hardwired.</p><p>Some of the identifying traits were positively-oriented: defining the group in terms of what its members <i>are</i>. Programmers know at least one programming language. Programmers have a favorite editor. Programmers are upset about the Star Wars prequels.</p><p>Others were negatively-oriented: defining the group in terms of what its members <i>are not</i>. Programmers don&#39;t play sports. Programmers don&#39;t wear suits. Programmers don&#39;t care about celebrity gossip.</p><p>It&#39;s understandable: Having gotten away from the &quot;cool kids&quot; who&#39;d hurt us in one way or another, we were eager to <i>stay</i> away. But when it comes to guessing whether an individual is trustworthy, negatively-oriented group traits are deeply flawed. Positively-oriented traits are weak evidence themselves, but they tend to at least indicate a certain amount of time or emotion invested in a shared interest. Negatively-oriented traits tell you almost nothing at all.</p><p>The pervasiveness of these negatively-oriented traits was a matter of some frustration for me. I really like to ride a bike! I think of my body as a tool, and one that it behooves me to hone. It actually turns out that athletic pursuits made me a better programmer, as well--more focused, more energized. It was weird to catch flak for that.</p><h3>Brogramming!</h3><p>I welcomed the &quot;brogramming&quot; notion with open arms. To me, it was a rejection of some problematic negatively-oriented traits. Yes, there were some troubling associations, but I saw them as mostly <a href="https://twitter.com/#!/brodotjs">tongue in cheek</a>. The unabashed adoption of self-improvement charmed me.</p><p>Either I had misunderstood the trend, or it changed direction. Suddenly it was inescapable that brogramming was a negatively-oriented trait. It was probably foreseeable, given the way &quot;bro&quot; was perched right there on the front of the word. In any case, brogramming had come to encompass the most sexist subsets of the programming community.</p><p>I don&#39;t think there&#39;s any chance of turning &quot;brogramming&quot; around. Personal fitness and self-improvement are still valuable goals within the programming community, but we have to call them something else. That gendered &quot;bro&quot; on the front was never a good thing, and it&#39;s grown to dwarf any parts that were.</p><p>Bummer.</p>And The Asset Pipeline Is So Enticing!tag:blog.erincall.com,2008:p/and-the-asset-pipeline-is-so-enticing2012-03-04T04:59:00Z<p><i>UPDATE</i>: I fixed it! I had a directory <code>assets/</code> with some non-site-related assets in it--layered image files, etc.--and had therefore put &quot;assets&quot; in my <code>.slugignore</code>. Apparently, Heroku was applying this to <code>app/assets/</code> as well.</p><p>I&#39;m having a bear of a time upgrading this site[1] from rails 3.0 to 3.2. The bulk of the engine work was done upstream (thanks upstream, upstream rules) but I can&#39;t get <code>application.css</code> to compile on Heroku. It&#39;s working locally, but on production no matter what I do, I get exceptions on pageload:</p><pre><code>A ActionView::Template::Error occurred in posts#index:
application.css isn&#39;t precompiled</code></pre><p>Initially it wouldn&#39;t compile at all; per <a href="http://devcenter.heroku.com/articles/rails3x-asset-pipeline-cedar#troubleshooting">Heroku&#39;s troubleshooting tips</a> I added <code>config.assets.initialize_on_precompile = false</code> to config/application.rb . Now when I push to heroku, the output log appears to be successfully precompiling my assets:</p><pre><code>$ git push heroku master
Counting objects: 17, done.
Delta compression using up to 4 threads.
Compressing objects: 100% (9/9), done.
Writing objects: 100% (9/9), 1.23 KiB, done.
Total 9 (delta 7), reused 0 (delta 0)
-----&gt; Heroku receiving push
-----&gt; Ruby/Rails app detected
-----&gt; Installing dependencies using Bundler version 1.1.rc.7
Running: bundle install --without development:test --path vendor/bundle --binstubs bin/ --deployment
Fetching gem metadata from https://rubygems.org/.......
{{snip bundle install output}}
Your bundle is complete! It was installed into ./vendor/bundle
Cleaning up the bundler cache.
-----&gt; Writing config/database.yml to read from DATABASE_URL
-----&gt; Preparing app for Rails asset pipeline
Running: rake assets:precompile
-----&gt; Rails plugin injection
Injecting rails_log_stdout
Injecting rails3_serve_static_assets
-----&gt; Discovering process types
Procfile declares types -&gt; web
Default types for Ruby/Rails -&gt; console, rake, worker
-----&gt; Compiled slug size is 20.7MB
-----&gt; Launching... done, v30</code></pre><p>But I still get <code>application.css isn&#39;t precompiled</code>.</p><p>I&#39;ve tried manually precompiling my assets:</p><pre><code>$ RAILS_ENV=production bundle exec rake assets:precompile
/Users/erincall/.rvm/rubies/ruby-1.9.2-p290/bin/ruby /Users/erincall/.rvm/gems/ruby-1.9.2-p290@opinions/bin/rake assets:precompile:all RAILS_ENV=production RAILS_GROUPS=assets
$ git add public/assets
$ git commit -m &quot;precompiled assets&quot;
$ git push heroku master</code></pre><p>But it has no effect. Perplexingly, I still see <code>Running: rake assets:precompile</code> in the Heroku slug compilation output, even though in this case I have a <code>public/assets/manifest.yml</code> and <a href="http://devcenter.heroku.com/articles/rails3x-asset-pipeline-cedar">the Heroku docs say I should see</a> <code>Detected manifest.yml, assuming assets were compiled locally</code>.</p><p>It almost looks like Heroku is expecting/putting <code>manifest.yml</code> somewhere other than <code>public/assets</code>, but I don&#39;t have much evidence to support that. I hope it&#39;s that, though, &#39;cause that would be a super-easy fix.</p><p>Uh the upside of this is I&#39;m not sure I would&#39;ve looked into the asset pipeline if I hadn&#39;t run into this trouble, and it turns out it&#39;s super-awesome &#39;cause you can just write sass and coffeescript and the framework will compile and concatenate and minify them. &lt;3</p><p>[1] At the time this entry was written, this blog was powered by Rails. This is no longer the case, but I&#39;ve retained the entry for posterity.</p>Triple-Equals In Javascripttag:blog.erincall.com,2008:p/triple-equals-in-javascript2012-02-22T06:20:00Z<p><a href="https://twitter.com/#!/mollyn/status/172187789637586945">Molly wants to know what <code>===</code> means in JavaScript</a>, so I thought I&#39;d write it up, from as close to first principles as my brain can think about. Fun!</p><p>Most programming languages use the <code>=</code> operator for variable assignment [1]:</p><pre><code class="JavaScript">x = 5;</code></pre><p>Now any time the variable <code>x</code> is referenced, the expression referencing it will operate on the value <code>5</code>. You&#39;ll recall this from algebra, of course:</p><pre><code>x = 5
f(x) = 3 + x^2</code></pre><p>That whole system evaluates to 28. If <code>x</code> were 3, or 17.2, it would evaluate to something else. Variables in software work the same way[2].</p><p>Of course, if the <code>=</code> sign means variable assignment, it could be ambiguous when we want to check if two variables are equal:</p><pre><code class="JavaScript">if ( x = 27 ) {
do_a_thing();
}</code></pre><p>Whuh-oh, did we mean to <i>assign 27 to x</i>, or did we mean to <i>check to see if x is already 27</i>? Most popular programming languages resolve the ambiguity by using a double-equals to check equality:</p><pre><code class="JavaScript">if ( x == 27 ) {
do_a_thing();
}</code></pre><p>So we have single-equals for variable assignments, and double-equals for equality comparison. How do we get up to triple-equals? Better put on your boots, we&#39;re heading into the mucky bits. Not the nice new sage ones--those are nice, Molly, by the way, I meant to mention--put on some old ones that you won&#39;t mind getting dirty.</p><p>Suppose you have a bit of text from a user. In JavaScript, and most languages, this bit of text is a <i>string</i>: a series of numbers, each of which represents a letter or other character. For example, if a user has typed &quot;27&quot; into a textbox, the computer will store it with three numbers: <code>50,55,0</code> [3]. The 50 is ASCII for &quot;2&quot;; the 55 makes 7, and the 0 means &quot;that&#39;s all there is in this string.&quot;</p><p>Now, suppose you have this string that the user gave you, and you want to know if it&#39;s equal to the number 27. You might try comparing it:</p><pre><code class="JavaScript">if (string_from_user == 27) {
do_a_thing();
}</code></pre><p>This will, in fact, work just as you wanted it to. However, in many early languages, it would&#39;ve been totally illegal. The compiler would yell at you that you can&#39;t compare strings to numbers, because what does that even mean? The string just is a bunch of numbers. How do you compare a bunch of numbers to a single number? I don&#39;t know.</p><p>JavaScript, however, has learned a few tricks since those early languages were all the rage. It knows how to perform <i>type coercion</i>. When you use <code>==</code> to check to see if a string is equal to a number, you&#39;re telling JavaScript that you want to know if the string&#39;s contents describe a number that is equal to your number. You&#39;re saying you want to compare your <code>27</code> to the user&#39;s &quot;27&quot;, not to the machine&#39;s <code>50,55,0</code>.</p><p>Unfortunately, JavaScript&#39;s rules for type coercion are broken. As you recall from math class, one of <a href="http://en.wikipedia.org/wiki/Equivalence_relation">the very important rules for equality</a> is that it needs to be <i>transitive</i>: if <code>a = b</code> and <code>b = c</code>, then <code>a = c</code>. However, because of the way JavaScript coerces types, this is not the case. Let&#39;s check it out, using the string <code>&quot;0&quot;</code> for our <code>a</code>, the number <code>0</code> for our <code>b</code>, and the string <code>&quot;&quot;</code>--a string containing nothing at all--for our <code>c</code>.</p><pre><code>&quot;0&quot; == 0
true
0 == &quot;&quot;
true
&quot;0&quot; == &quot;&quot;
false</code></pre><p>Aaaaaa what just happened? Well, JavaScript gave us just enough rope to hang ourselves. A couple minutes ago, we earnestly wanted it to use the number represented by a string when comparing that string to a number. So when we compared <code>&quot;0&quot;</code> to <code>0</code>, it obligingly said &quot;yep, they&#39;re equal.&quot;</p><p>Then we asked it to compare <code>0</code> to <code>&quot;&quot;</code>. [4] &quot;Sure,&quot; JavaScript told us, &quot;those are both pretty no-ey. I&#39;ll say they&#39;re equal.&quot;</p><p>Finally, we asked it to compare <code>&quot;0&quot;</code> and <code>&quot;&quot;</code>. Well, now there&#39;s no coercion necessary. These are both strings, so JavaScript compares their contents, without worrying about whether they&#39;re maybe representing the same thing. Their contents are totally not the same, so JavaScript tells us they&#39;re not equal.</p><p>So, for people who like their software to do what they expect, JavaScript also provides <code>===</code>. <code>===</code> performs no type coercion: if you try to compare any string to any number using <code>===</code>, it will tell you &quot;not equal,&quot; no ifs ands or buts.</p><p>And that&#39;s why <code>===</code> is important, and why working with JavaScript makes Selena feel stabby.</p><p>[1] Some languages use other operators to avoid this whole froofaraw. Pascal, for example, uses <code>:=</code> for variable assignment.</p><p>[2] Except for all the ways they don&#39;t work the same way at all, which aren&#39;t interesting at this time.</p><p>[3] This is a filthy filthy lie. The internal representation is much more complicated. The complications are not interesting at this time, and the lie is at least one that points roughly toward the truth.</p><p>[4] Because of the lie I told you about the internal representation of strings, you might be tempted to think that JavaScript is comparing the number <code>0</code> to the 0 that is the only thing in the empty string. But remember, that was a lie; that&#39;s not what the string looks like inside at all. It might look like that, in some languages! It does not look like that in JavaScript. Good on you for thinking of it, though!</p>The System Pip On Lion Is Completely Broken??tag:blog.erincall.com,2008:p/the-system-pip-on-lion-is-completely-broken2012-02-18T19:58:00Z<p>I sat down to hack on <a href="https://github.com/erincall/pullme">Pullme</a> for the first time on my new macbook. I ran into unreasonable roadblocks:</p><pre><code>$ mkvirtualenv pullme
Running virtualenv with interpreter /usr/local/Cellar/python/2.7.2/bin/python
Overwriting pullme/lib/python2.7/orig-prefix.txt with new content
New python executable in pullme/bin/python
Installing setuptools.............done.
Installing pip...............done.
Traceback (most recent call last):
File &quot;&lt;string&gt;&quot;, line 1, in &lt;module&gt;
ImportError: No module named virtualenvwrapper.hook_loader</code></pre><p>Augh, what is happening? Well, for some reason the pip that comes with OSX Lion puts things in a directory that&#39;s <i>totally unrelated</i> to the directories that the system Python searches:</p><pre><code>$ python -c &#39;import sys; print &quot;\n&quot;.join(map(repr, sys.path))&#39;
&#39;&#39;
&#39;/usr/local/Cellar/python/2.7.2/lib/python2.7/site-packages/distribute-0.6.24-py2.7.egg&#39;
&#39;/usr/local/Cellar/python/2.7.2/lib/python2.7/site-packages/pip-1.1-py2.7.egg&#39;
...snip...
&#39;/usr/local/Cellar/python/2.7.2/lib/python2.7/lib-dynload&#39;
&#39;/usr/local/Cellar/python/2.7.2/lib/python2.7/site-packages&#39;
&#39;/usr/local/Cellar/python/2.7.2/lib/python2.7/site-packages/setuptools-0.6c11-py2.7.egg-info&#39;</code></pre><p>Python is only looking in <code>/usr/local/Cellar/...</code></p><pre><code>$ locate virtualenvwrapper
/Library/Python/2.7/site-packages/virtualenvwrapper
/Library/Python/2.7/site-packages/virtualenvwrapper/hook_loader.py
/Library/Python/2.7/site-packages/virtualenvwrapper/hook_loader.pyc
...snip...
/Library/Python/2.7/site-packages/virtualenvwrapper-3.0-py2.7.egg-info/requires.txt
/Library/Python/2.7/site-packages/virtualenvwrapper-3.0-py2.7.egg-info/top_level.txt
/usr/local/bin/virtualenvwrapper.sh</code></pre><p>Meanwhile, <code>virtualenvwrapper</code> is installed in <code>/Library/Python/...</code></p><p>I solved the problem with a new pip:</p><pre><code>$ easy_install pip
Searching for pip
Reading http://pypi.python.org/simple/pip/
Reading http://www.pip-installer.org
...snip...
Installed /usr/local/lib/python2.7/site-packages/pip-1.1-py2.7.egg
Processing dependencies for pip
Finished processing dependencies for pip
$ type pip
pip is hashed (/usr/local/bin/pip)
$ hash -r
$ type pip
pip is /usr/local/share/python/pip
$ pip install virtualenvwrapper
Downloading/unpacking virtualenvwrapper
Downloading virtualenvwrapper-3.0.tar.gz (717Kb): 717Kb downloaded
Running setup.py egg_info for package virtualenvwrapper
...snip...
Installing virtualenv script to /usr/local/share/python
Successfully installed virtualenvwrapper virtualenv
Cleaning up...
$ mkvirtualenv pullme
New python executable in pullme/bin/python
Installing setuptools.............done.
Installing pip...............done.
virtualenvwrapper.user_scripts creating /Users/erincall/Envs/pullme/bin/predeactivate
virtualenvwrapper.user_scripts creating /Users/erincall/Envs/pullme/bin/postdeactivate
virtualenvwrapper.user_scripts creating /Users/erincall/Envs/pullme/bin/preactivate
virtualenvwrapper.user_scripts creating /Users/erincall/Envs/pullme/bin/postactivate
virtualenvwrapper.user_scripts creating /Users/erincall/Envs/pullme/bin/get_env_details</code></pre><p>Success!</p><p>It&#39;s possible I actually installed <code>pip</code> some crazy way, but I don&#39;t know where I would&#39;ve gotten it other than <code>easy_install</code>. If Lion really is shipping with a <code>pip</code> that <i>doesn&#39;t install packages to Python&#39;s <code>sys.path</code></i>, well geez, that&#39;s just terrible.</p>Wanker News!tag:blog.erincall.com,2008:p/wanker-news2012-02-03T06:55:00Z<p>p. I made &quot;a juvenile website&quot;:http://wankernews.com!</p><p>p. So, maybe it requires some backstory? The fella behind &quot;pinboard&quot;:http://pinboard.in/ &quot;proposed&quot;:https://twitter.com/#!/Pinboard/status/155334419098517504 replacing instances of &quot;the cloud&quot; with &quot;the moon&quot;, and further &quot;proposed&quot;:https://twitter.com/#!/Pinboard/status/164847769285169152 replacing &quot;hack&quot; with &quot;wank.&quot; Hilarious!</p><p>p. Well, over lunch with a few co-workers, we discussed how much &quot;Hacker News&quot;:http://news.ycombinator.com might be improved with these replacements. So after work I dropped by a &quot;wankathon&quot;:http://calagator.org/events/1250461751, threw together a rails app that proxies news.ycombinator.com with some regex replacements, and sent it off &quot;to the moon&quot;:http://heroku.com.</p><p>p. Since then I&#39;ve been laughing pretty much non-stop. Come wank the auto industry! Who are we to assign connotations to &quot;wanker&quot;? ahahaha</p>Are You Kidding Metag:blog.erincall.com,2008:p/are-you-kidding-me2012-01-27T03:20:00Z<pre><code>SYNOPSIS
unzip [-Z] [-cflptTuvz[abjnoqsCKLMVWX$/:]] file[.zip] [file(s) ...] [-x xfile(s) ...] [-d exdir]</code></pre><p>Ah yes, excellent, this is exactly the sort of thing I am able to read. Well, yes, I <i>was</i> created by Dr. Soong, but why do you ask?</p>Annotation Station: hanke.pytag:blog.erincall.com,2008:p/annotation-station-hanke-py2012-01-17T06:59:00Z<p>A week or so ago, I annotated a chunk of code for a co-worker who was complaining that open-source did him little good if he didn&#39;t understand the language. I was surprised--and pleased--to find that I learned a lot from the exercise myself. I resolved immediately to do it some more. This is the first in what will be several trillion annotated chunks of code.</p><p>This is a python script written to convert between Gregorian (you may know them as &#39;normal&#39;) dates and dates in the <a href="http://henry.pha.jhu.edu/calendar.html">Hanke-Henry calendar</a>. It is written by github user <a href="https://github.com/elsewherean">elsewherean</a>.</p><p>The Hanke-Henry calendar is a bad idea. It isn&#39;t clear what problems it solves, but the many problems inherent in switching to a whole new calendar system are evident. However, we can still have fun and learn something from analyzing elsewherean&#39;s code. Let&#39;s get to it, shall we?</p><pre><code class="Python"># import a couple of functions for manipulating standard gregorian dates
from datetime import date,timedelta
#this&#39;ll define a collection of functions that can all operate on Hanke calendars.
class PermanentCalendar:
#the __init__ function is called when initializing a python object.
#self will implicitly be bound to a new object. The other params are optional;
#they default to the given values.
def __init__(self,year=2012,month=1,day=1):
#do some object setup. the `special` and `start` attributes are set on
#the object `self`
#`special` is initialized as an empty dict.
#I wonder what it&#39;s for! Nothing ever uses it.
self.special = {}
#`start` is initialized as an immutable tuple containing (presumably) a
#year, month, and day.
self.start = (2012,1,1)
#note that the year/month/day params passed in above were not used.
#they now pass out of scope, never to be seen again. Hm.
#this function will, presumably, convert the given date from a gregorian
#date to a Hanke date.
def convertFromGregorianDate(self,year,month,day):
#calculate the timedelta between the given gregorian date and the
#baseline date, 20120101, then report what Hanke date is that many days
#after the baseline date.
#note the use of subtraction on a pair of datetime.date objects.
#Python will call the __sub__ function on the left-hand date with the
#right-hand date as its argument.
diff = date(2012,1,1)-date(year,month,day)
#since Hanke-Henry takes effect 20120101, one can assume any date
#desired in Hanke-Henry is after that. So it&#39;s a little odd that the
#previous line subtracted the desired (larger) date from the baseline
#(smaller) date. Below, the timedelta is inverted, to give a positive
#argument to daysFrom.
return self.daysFrom(diff.days*-1)
#this&#39;ll convert a Hanke date to a gregorian date.
def convertToGregorianDate(self,year,month,day):
#create a `diff` counter. In a minute, this&#39;ll represent the timedelta
#in days between the given date and the baseline date.
diff = 0
#create a tuple holding the given gregorian date.
origday = (year,month,day)
#create a tuple holding the baseline date.
day = (2012,1,1)
#loop until the baseline date has been adjusted to match the given
#date. On each loop, increment or decrement the baseline date by one
#day to bring it closer to the given day. Track how many loops have
#happened; that will be the difference in days between the given date
#and the baseline date.
#this is a fairly standard &quot;days since epoch&quot; approach to date math,
#and it&#39;s a classic for a reason. The author avoids the temptation to
#try to move in chunks--decrement the year counter and add 365, for
#example--which, while tempting, is likely to run into heck of edge
#cases.
while (day!=origday):
if year &lt; 2012:
day = self.decrDay(day)
diff -=1
else:
day = self.incrDay(day)
diff +=1
#now delegate to the timedelta and date objects provided by python&#39;s
#datetime module.
newday = date(2012,1,1)+timedelta(diff)
#finally, the datetime object is split into the year-month-day tuple
#system that&#39;s used throughout PermanentCalendar.
return (newday.year,newday.month,newday.day)
#this function will tell us whether the given year has an extra week
#in the Hanke system.
def has_extra_week(self,year):
#date.weekday will return the day of week, counting from Monday = 0,
#of the given date. I don&#39;t see a difference between
#`date.weekday(date(year,1,1))` and `date(year,1,1).weekday()`. The
#author has seemed mistrustful in general of OOP (and given the
#atrocities that&#39;ve been committed in its name, who can blame him?).
#In any case, a year has an extra week if its gregorian counterpart
#starts or ends on a Thursday. Hmm.
return date.weekday(date(year,1,1)) == 3 or date.weekday(date(year,12,31))==3
#Given a date, return the date that is one day later.
#Note the unusual function signature: this function takes a year/month/day
#tuple, but unpacks it, as though it had simply been passed a year, month,
#and day individually.
def incrDay(self,(year,month,day)):
#Ah, date math. It will become rapidly evident to the reader that
#whatever else the Hanke calendar accomplishes, it doesn&#39;t cut down on
#edge cases.
#First edge case: last day of the year.
if month == 12 and day == 31:
#if this year has an extra week, it&#39;s not the last day of the year
#after all. It&#39;s the last day of the month.
if self.has_extra_week(year):
#Return the first day of next month.
return (year,month+1,1)
else:
#Return the first day of next year.
return (year+1,1,1)
#Next edge case: REALLY the last day of the year, for seriously.
elif month == 13 and day ==7:
#return the first day of next year.
return (year+1,1,1)
#Every third month in the Hanke calendar has 30 days. If the modulus-3
#of the month is 0--that is, if the month is a multiple of 3--the last
#day of the month is the 31st.
elif month % 3 == 0 and day == 31:
#Return the first day of next month.
return (year,month+1,1)
#For other months, the last day is the 30th.
elif month % 3 &gt; 0 and day == 30:
#return the first day of next month.
return (year, month+1,1)
#That&#39;s the end of the edge cases.
else:
#just return the next day of this month.
return (year, month, day+1)
#Return the date that is one day before the given date.
#This function has the same interesting tuple-in-function-signature pattern
#that incrDay had, above.
def decrDay(self,(year,month,day)):
#And we&#39;re right back into edge cases. If it&#39;s the first day of the
#month, the day before will be the previous month...
if day == 1:
#which might have been in the previous year...
if month == 1:
#which might have had an Xtr week...
if self.has_extra_week(year-1):
#return the last day of last year&#39;s Xtr week.
return (year-1,13,7)
else:
#return the last day of last year.
return (year-1,12,31)
#last month was in this year, but we still need to figure out how
#many days it had. As we saw above, every third month has 31 days.
#Check to see if last month was one of those:
elif (month-1) % 3 == 0:
#return the last day of the previous month.
return (year,month-1,31)
#if it&#39;s not, it&#39;s a 30-day month.
else:
#return the last day of the previous month.
return (year, month-1,30)
#we&#39;re finally out of the &quot;first day of the month&quot; case.
else:
#return the previous day of this month.
return (year,month,day-1)
#this function returns the Hanke date that is the given number of days after
#the given Hanke date (or the baseline date, if no date was provided).
def daysFrom(self,count,day=None):
#if `day` wasn&#39;t provided, default it to the baseline date.
if day is None:
#note that this code references self.start for the baseline date,
#rather than constructing it on the fly.
day = self.start
#if the given count is negative, find a date before the baseline date,
#rather than after.
if count &lt; 0:
#range() will create a list of numbers starting at the first
#argument and ending at the second argument, *noninclusive*,
#stepping equal to the third argument each time. So if `count` is
#-5, for example, it will return a list like [0, -1, -2, -3, -4].
#Then we&#39;ll loop through that list, assigning each element to `y`
#and executing the code in the loop.
#Note that y is not actually accessed. The loop doesn&#39;t care about
#the contents of the list, it just wants to be executed `count`
#times.
for y in range(0,count,-1):
#step down by one day.
day = self.decrDay(day)
else:
#Just as above, step forward one day, `count` many times.
for y in range(0,count,1):
day = self.incrDay(day)
return day
#this concludes the definition of the PermanentCalendar class.
#from this point on the code will execute as soon as the script runs, rather
#than waiting to be called as a function.
#create a PermanentCalendar object. Note that this calls the __init__ function
#defined way back at the beginning.
x = PermanentCalendar()
#Whoa, python golf! There is *so much* happening on this line. I&#39;m gonna
#annotate below instead of above, so the reader can get a look at it before
#I start talking.
#In short, though, it prints today&#39;s date in the Hanke calendar.
print x.convertFromGregorianDate(*map(lambda x:int(x),date.today().isoformat().split(&#39;-&#39;)))
#the first thing that happens is actually right in the middle:
# date.today().
# this generates a new date object for today&#39;s date.
#next:
# .isoformat()
# isoformat() returns a string in a standardized format. At the time of
# writing, date.today().isoformat() looks like &#39;2012-01-16&#39;.
#next:
# .split(&#39;-&#39;)
# split is a function on strings that breaks them up into lists. It looks
# through the string, closing it off and starting a new one every time it
# encounters the given token. In this case, we&#39;re splitting on &#39;-&#39;, so the
# string we got back from isoformat() will break up into year, month, and day.
# At this point they are still strings, not integers.
#So that&#39;s the second argument to the `map` function. The first one is a
#lambda, an anonymous function defined on-the-fly. This one takes a single
#argument `x` and passes it along to the `int` function.
#Now the lambda and the year-month-day list are passed to the `map` function.
#`map` will apply its first argument to each element of the list in its second
#argument. In this case, it&#39;ll return a list containing the year, month, and
#day--much as we got back from `split`, except now they&#39;re integers instead of
#strings.
#but wait, what&#39;s that * doing in there? Well, in a method call like this, it
#will expand the elements of the list that came out of `map`, so we&#39;ll be
#passing the year, month, and day to the convertFromGregorianDate method,
#rather than passing *a list containing them*. This is an important
#distinction, no matter what Larry Wall thinks.
#Finally, the `print` statement sends the Hanke-formatted date to the terminal.</code></pre><p>Oof, that last line is pretty hard to understand, huh. What do you say we re-write it?</p><p>First of all, there&#39;s no need to define that lambda at all. We can use <code>int</code> directly:</p><pre><code class="Python">print x.convertFromGregorianDate(*map(int,date.today().isoformat().split(&#39;-&#39;)))</code></pre><p>Except we don&#39;t really need the map at all. It turns out that <code>isoformat().split(&#39;-&#39;)</code> is a really odd way to get at the date&#39;s members, since date objects can tell you about the year/month/date directly.</p><pre><code class="Python">today = date.today()
print x.convertFromGregorianDate(today.year, today.month, today.day)</code></pre><p>Ok, I can live with it now.</p>Python's Scoping System Will Hurt You If You Let Ittag:blog.erincall.com,2008:p/pythons-scoping-system-will-hurt-you-if-you-let-it2012-01-10T04:57:00Z<p>So, I ran into a bit of Python&#39;s behavior that upset me. Here&#39;s some code that is wrong:</p><pre><code>if some_function():
x = 5
print x</code></pre><p>Depending on the return value of <code>some_function</code>, this code may or may not execute successfully. Whether or not it happens to succeed, though: it&#39;s wrong. You have this exception sitting there, waiting to jump up and bite you. It is a problem waiting to happen. Python doesn&#39;t care, though! It will happily accept that code and let you catch the error in production. But why? Why doesn&#39;t Python just define a new lexical scope when entering an <code>if</code> or <code>for</code> block? Then it could tell you during compile-time that your code is wrong, and you could fix it before it ever screwed you up.</p><p>So, as is my wont, I groused on IRC, and the nice people there taught me some surprising and upsetting things about Python&#39;s scoping. It turns out it&#39;s not nearly that simple. Here, let&#39;s look at some closures.</p><p><code>closures.rb</code>:</p><pre><code>methods = []
(1..5).each do |x|
methods.push(lambda { puts x })
end
methods.each do |method|
method.call()
end</code></pre><pre><code>$ ruby ./closures.rb
1
2
3
4
5</code></pre><p><code>closures.pl</code></p><pre><code>my @subs;
for my $i ( 1 .. 5 ) {
push @subs, sub {
print &quot;$i\n&quot;;
}
}
for my $sub (@subs) {
$sub-&gt;();
}</code></pre><pre><code>$ perl ./closures.pl
1
2
3
4
5</code></pre><p>Okay, you get the point: closures. Bear with me for one more go-around with Python, though. <code>closures.py</code>:</p><pre><code>from __future__ import print_function
functions = []
for i in xrange(1, 6):
functions.append(lambda: print(i))
for function in functions:
function()</code></pre><pre><code>$ python ./closures.py
5
5
5
5
5</code></pre><p>Wait, what just happened? Where&#39;d my nice sequence go?</p><p>It turns out that Python doesn&#39;t really do closures, exactly, at least not as I&#39;ve understood them in the past. In many languages, including Perl and Ruby, a function <i>closes around</i> its surrounding scope. The variables in the surrounding scope are imported into the function&#39;s own scope, with the values they held when the function was declared. In Python, on the other hand, a function <i>has access to</i> its surrounding scope. The Python interpreter can&#39;t check for declaredness until runtime, because until then there&#39;s no way to know if the variable might be declared in the surrounding scope. For example, Python is totally ok with <code>go_mad.py</code>:</p><pre><code>def go_mad():
print i
i = &#39;b-a-n-a-n-a-s&#39;
go_mad()</code></pre><pre><code>python ./go_mad.py
b-a-n-a-n-a-s</code></pre><p>Even though <code>i</code> isn&#39;t declared until well after <code>go_mad</code>, it&#39;s perfectly ok with Python if <code>go_mad</code> accesses it. Is that an invitation to bugs, or what?</p><p>Ok, so we&#39;ve seen why Python doesn&#39;t use compile-time scope checks to check for variables that may or may not be declared: it doesn&#39;t check scope until runtime. And we&#39;ve seen why it doesn&#39;t check scope until runtime: a function recieves its scope from its surroundings.</p><p>What I haven&#39;t seen is <i>why</i> a function receives its scope from its surroundings. What benefits does a Python programmer get that offset the cost? What do we gain in exchange for losing so much? Until I have an answer to that, here are some practices I&#39;ll be following to offset the dangers imposed by Python&#39;s scoping:</p><ul><li><p>Never define anything inside an if-block.</p></li></ul><pre><code> x = None
if some_function():
x = 5
print x</code></pre><ul><li>In fact, declare a function&#39;s variables at its beginning, as recommended in Javascript.</li><li>Delegate closures to extremely trivial functions, to force an earlier scope resolution. Here&#39;s a fixed version of @closures.py@:</li></ul><pre><code>from __future__ import print_function
functions = []
def generate_appender(val):
return lambda: print(val)
for i in range(1, 6):
functions.append(generate_appender(i))
for function in functions:
function()</code></pre><pre><code>$ python ./closures.py
1
2
3
4
5</code></pre><ul><li>In fact, I&#39;ll probably avoid closures entirely, barring a compelling reason to use them.</li><li>Write my own dang language, if I&#39;m so smart</li></ul>Code Is Evil And Should Be Destroyedtag:blog.erincall.com,2008:p/code-is-evil-and-should-be-destroyed2012-01-03T03:13:00Z<p>Writing code, while sometimes necessary, is inescapably evil. Yes, deploying an empty directory to production will be ineffective. Yes, most version control systems are so obstinate that they won&#39;t even accept an empty directory, and, ok, all right, I&#39;ll admit that perhaps if we intend to have features, we&#39;ll need to write some code at some point. However, it is completely unavoidable that this code will have bugs! <i>Completely unavaidable</i>. All code has bugs. There&#39;s a reason second-language learners often practice verb conjugation with the sentence &quot;Your code/my code/his/her/its code has bugs.&quot;</p><h3>So what can we do? It&#39;s simple: we kill as much code as we can possibly lose.</h3><p>First of all, get acquainted with your Eastern European friend, <a href="http://www.globalnerdy.com/2008/12/05/it-looks-like-youre-trying-to-stank-up-your-code-would-you-like-some-help/">Yagni</a>. If you&#39;re adding code and you don&#39;t know, with absolute certainty, that you&#39;re going to need it, you&#39;re gambling on the chance of later ease with the certainty of bugs and lost development time.</p><p>Second, stop hand-rolling things that someone else has already done. Whatever you&#39;ve been working lately, I&#39;ll bet you there&#39;s a library out there right now that can solve your problem already. It&#39;s better-tested than your code, it&#39;s more feature-complete, and the author came up with a cuter name for it than you did. Look, I know writing glue code isn&#39;t sexy. I know it&#39;s not the sort of exciting project that looks super-cool on a resume. But you know what else doesn&#39;t look good on a resume? &quot;My project was notable for its cost overruns, missed deadlines, and constant bugs.&quot;</p><p>Now I hear you over there saying, &quot;but Erin, if all I write is glue code, how will my program stand out from the other ones that glued those libraries together?&quot; Well, if you don&#39;t know that already, what exactly is it that you&#39;re doing? What value, exactly, do you intend to create? Answer that question, write that code, and knock off for the day, because the minute you go outside those bounds you&#39;re creating needless bugs.</p><p>Finally, never do the same thing three times. In fact, if you can get away with it, don&#39;t do it more than once. Every time you do it, there&#39;s a chance--a good chance, in fact--that you&#39;ll make a mistake. Put it in a utility function, put it in a little script, put it in a wiki where you can copy-paste it, <i>whatever</i>--write it down somewhere and use what you wrote down, so when you realize you made a mistake, you can fix it once and stop making it.</p><p>The single most important role a programmer has is software deleter. Get out there and make a red diff!</p>