Github has just announced GPG signature verification, which helps verify that commits made in someone's name were indeed made by that person. It's long been a dirty secret of git that you can impersonate anyone with minimal effort, so GPG verification adds a nice layer of assurance. Now that I've gone through the hassle of setting up automatic commit signatures, here's how you can do it too.
SSL is an important part of the infrastucture of the Internet. It provides three assurances: The computer you're talking to is the one you meant to talk to, the message you're reading was really sent by the computer you're talking to, and the message you're reading hasn't been read by anyone else. I've mentioned before that I think increasing the cryptographic noise floor is important, so I thought I'd write a bit about what SSL does and how to put together a strong webserver configuration.
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't have to see me say "now just do an inverted Woollongong Shimmy and you're finished!"
Over the last week I'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 . Super convenient!
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'd have the usual password-distribution problems.
I really really want to love the Old West as a setting. It's so romantic: hard, lonely men and women wresting a living from a hard, lonely land under a huge, lonely sky. It's a place and time where you can'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's.
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's no getting around it. If the story has no natives, it'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.
I recently finished The Incorruptibles, by John Hornor Jacobs. I'm sorry to say that it makes the Native Americans into terrible creatures, ones with whom there is no possibility of dialogue or compromise.
If you've worked in Ruby much, you're probably familiar with using
end to catch exceptions. Did you know there are other ways to use rescue? It's true! You may be able to delete a bit of software by using inline or implicit rescues.
In an email, Rachel King asked a fairly open-ended question about writing tests:
I'm looking for guides on how to write tests. Trying to get better at debugging...
Since everything I know about testing is stuff I just picked up along the way, I don'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'd make it a blog post instead. Let's get to it!
First off, sorry about the delay getting this last post up! I was at Pete Keen's wedding, and the preparations took up all my blogging time.
Thursday was a good day to visit Prague's New Town. Although it's new by comparison to the Old Town, it's still full of beautiful old buildings.
By this point I'd burst a blister on my foot and wasn'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.
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't find one place with vacancies all my remaining nights). It was a bit of a hike, but soon enough I was off and sightseeing!
I'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.
There's not much else to say, but as always be sure to check out my pictures from the day!
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'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.
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'd found the subway I would've found a ticket machine. At the time, all I knew was I couldn't get a ticket.
I took a trip to Prague! I've been wanting to visit Europe for some time, and Prague seemed like a great entree to the continent. It'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'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.
Wow, when I write it out like that, it sounds incredibly ghoulish. Let's say instead that it preserves tangible remembrances of Europe's history, and change the subject.
There's a piece of "wisdom" 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.
Once the business is running, though, it's ludicrous. The programmers aren't the only work center that matters any more, and they probably aren't even the most critical one. If you as a programmer have information that someone needs, it's part of your job to take your headphones off for a minute and talk to them.
I have this app Catsnap 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--"d5hwde6hzncg6.cloudfront.net". The links don't look like something you should click.
So, this problem has a trivial solution, right? Just make a CNAME pointing e.g. cdn.erincall.com to d5whatever.cloudfront.net? Yes, BUT: I'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). I think using SSL/TLS is important, so that wasn't acceptable.
Fortunately, the SNI extension to TLS offers a fix for this, and since March 2014, Cloudfront supports it. Let's get into setting it up!
I'd like to introduce you to my newest project, Bloge (pronounced like "doge"). It's a semi-static markdown blog generator written in Haskell, using the Snap framework and Heist HTML-rendering engine.
My previous blog was built using Enki, 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't have a modal editor in my browser, so...in the end I was just dissatisfied.
I'm hoping this new built-from-the-ground-up system will make me much happier! :~)
I'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 "Bike Bot":
There's a particular detail in the workings of the @git push@ command that's almost always elided in tutorials. It's one of those details where you don't often need to exercise your knowledge of it, but you occasionally need to know it exists. If you don't know you don't know it, you can end up in a frustratingly confusing state.
That's false! The syntax of the command is
git push <remote> <local-ref>:<remote-branch>. You can omit some of the keywords, and their values will be inferred in various ways. And in fact, it's extremely common to omit the
local-ref and colon, leaving the command looking the way tutorials claim it always looks.
I received a surprising note in an email:
I came across your blog and really enjoyed your post about 'microaggressions'. I went to college at UCLA, and met many people who, though their hearts were in the right places, would twist others' words to frame them as microaggressions or otherwise sexist/racist comments when they, by intent, were not.
This person has misconstrued my purpose in writing the previous post! Let me clarify: I do believe such a thing as a microaggression exists, I just wanted to understand more clearly what it was.
This email-sender strikes a note I'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 "I didn't mean to" as a first line of defense, most of us grow out of it in time--except when the harm was done to someone else's feelings.
I'm not sure why it'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 "aggression" there at the back end of "microaggression" does suggest some intentionality. The problem in general extends beyond this one word, though. Richie Incognito's use of "n----r" is unacceptable for all that it "comes from a place of love," for example.
Our culture has an aphorism for this situation: "the road to hell is paved with good intentions." What matters is always the actual result of your actions, not what you hoped would happen.
Update: "wilkie" has knocked it out of the park with a thorough explanation in the comments. Make sure you check it out!
This post involves a discussion of two people, "Rita" and "Bastian," whose identities I've chosen to obfuscate. An astute reader will be able to divine their identities. A responsible reader will choose not to do so. I invite you to be responsible.
My friend Shawna Scott encountered a need for regular expressions for the first time recently. I went to link her to some information, but I couldn'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's syntax. They're inescapable--yet no tutorial measures up. Let's see what I can do, shall we?
While the browser was doing things, it was very quick indeed. But at what looked like random times, it would just sit and think about philosophy for a while. I asked about this on the Splinter mailing list and got no responses (I'm not sure how active that mailing list is), so I finally did some investigation on my own.
I created a repository that demonstrates the slow behavior. I've built a fairly minimal amount of behavior, so it's easy to read through. Unfortunately, even though it demonstrates the problem, it still doesn't explain it. I still have no idea why this particular set of steps leads to such a sudden drop-off in performance.
At this point I think I've exhausted my ability to debug this any farther. I'm hoping to take it to the Cobrateam, uh, team, and see if they're interested in helping track down the root cause.
For the last few days I've been eating, breathing, and sleeping a new project. Code has flown from my fingers like sparks from a millstone. Today I'd like to announce the result of my fevered efforts: Radlibs! 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 phrasal template.
The "Rad" in "radlibs" comes from its recursive nature: category-members may also be phrasal templates. Thus, with the right libraries Radlibs can generate some good clean fun:
When you use git at work and for personal projects, it'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'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'll show you how I manage that problem.
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.
You'll need to run the server code somewhere. The instructions below assume you're using a VPS or EC2. However, you could also run catsnap on your personal computer, or a managed service like Heroku. In any case, you'll need to create a postgres database for Catsnap to use. First-time Catsnap users will also need to create an S3 bucket.
block_given? to see if the caller provided a block.
So, I've been working on this catsnap project of mine, improving the performance. The big problem is that it'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ïve approach: loop through the images associated with a tag, asking DynamoDB about each one. Fortunately, the excellent boto library for interacting with aws has a batched lookup mode that lets me grab all the images at once.
Only...not quite. DynamoDB has this thing going on where if you ask for some items, it'll give some or all of them back. It'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.
I'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 Champoeg State Park, which turns out to be fantastic!
The route I took was about 35 miles, which is quite a ways with a heavily-laden bike! The poundng heat didn't help, either. As you can imagine I was pretty glad to arrive!
p. Lately I've been geeking out about The Name Of The Wind with friends, so I went in for a re-read. I was immediately struck by how dense the opening chapters are, laying out themes and setting up later events. I'm gonna take a few minutes to blather at you about how much I like them.
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.
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's natural that we formed our own tribe. This wasn't Revenge Of The Nerds, where the nerds win by adopting mainstream norms. This was nerds living well on their own terms.
UPDATE: I fixed it! I had a directory
assets/ with some non-site-related assets in it--layered image files, etc.--and had therefore put "assets" in my
.slugignore. Apparently, Heroku was applying this to
app/assets/ as well.
I'm having a bear of a time upgrading this site from rails 3.0 to 3.2. The bulk of the engine work was done upstream (thanks upstream, upstream rules) but I can't get
application.css to compile on Heroku. It's working locally, but on production no matter what I do, I get exceptions on pageload:
A ActionView::Template::Error occurred in posts#index: application.css isn't precompiled
Molly wants to know what
I sat down to hack on Pullme for the first time on my new macbook. I ran into unreasonable roadblocks:
p. I made "a juvenile website":http://wankernews.com!
p. So, maybe it requires some backstory? The fella behind "pinboard":http://pinboard.in/ "proposed":https://twitter.com/#!/Pinboard/status/155334419098517504 replacing instances of "the cloud" with "the moon", and further "proposed":https://twitter.com/#!/Pinboard/status/164847769285169152 replacing "hack" with "wank." Hilarious!
p. Well, over lunch with a few co-workers, we discussed how much "Hacker News":http://news.ycombinator.com might be improved with these replacements. So after work I dropped by a "wankathon":http://calagator.org/events/1250461751, threw together a rails app that proxies news.ycombinator.com with some regex replacements, and sent it off "to the moon":http://heroku.com.
p. Since then I've been laughing pretty much non-stop. Come wank the auto industry! Who are we to assign connotations to "wanker"? ahahaha
SYNOPSIS unzip [-Z] [-cflptTuvz[abjnoqsCKLMVWX$/:]] file[.zip] [file(s) ...] [-x xfile(s) ...] [-d exdir]
Ah yes, excellent, this is exactly the sort of thing I am able to read. Well, yes, I was created by Dr. Soong, but why do you ask?
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'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.
So, I ran into a bit of Python's behavior that upset me. Here's some code that is wrong:
if some_function(): x = 5 print x
Depending on the return value of
some_function, this code may or may not execute successfully. Whether or not it happens to succeed, though: it's wrong. You have this exception sitting there, waiting to jump up and bite you. It is a problem waiting to happen. Python doesn't care, though! It will happily accept that code and let you catch the error in production. But why? Why doesn't Python just define a new lexical scope when entering an
for 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.
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't even accept an empty directory, and, ok, all right, I'll admit that perhaps if we intend to have features, we'll need to write some code at some point. However, it is completely unavoidable that this code will have bugs! Completely unavaidable. All code has bugs. There's a reason second-language learners often practice verb conjugation with the sentence "Your code/my code/his/her/its code has bugs."
So what can we do? It's simple: we kill as much code as we can possibly lose.
First of all, get acquainted with your Eastern European friend, Yagni. If you're adding code and you don't know, with absolute certainty, that you're going to need it, you're gambling on the chance of later ease with the certainty of bugs and lost development time.
Second, stop hand-rolling things that someone else has already done. Whatever you've been working lately, I'll bet you there's a library out there right now that can solve your problem already. It's better-tested than your code, it'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't sexy. I know it's not the sort of exciting project that looks super-cool on a resume. But you know what else doesn't look good on a resume? "My project was notable for its cost overruns, missed deadlines, and constant bugs."
Now I hear you over there saying, "but Erin, if all I write is glue code, how will my program stand out from the other ones that glued those libraries together?" Well, if you don't know that already, what exactly is it that you'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're creating needless bugs.
Finally, never do the same thing three times. In fact, if you can get away with it, don't do it more than once. Every time you do it, there's a chance--a good chance, in fact--that you'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, whatever--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.
The single most important role a programmer has is software deleter. Get out there and make a red diff!