<?xml version="1.0" encoding="utf-8"?>
<feed xmlns="http://www.w3.org/2005/Atom">
 
 <title>Jon Canady</title>
 <subtitle>Now with more Canady</subtitle>
 <link href="http://joncanady.com/atom.xml" rel="self"/>
 <link href="http://joncanady.com/"/>
 <updated>2010-03-08T15:03:57-05:00</updated>
 <id>http://joncanady.com/</id>
 <author>
   <name>Jon Canady</name>
   <email>jon@joncanady.com</email>
 </author>
 
 
 <entry>
   <title>GPS Failures and User Experience</title>
   <link href="http://joncanady.com/2010/03/08/gps-failures-and-user-experience/"/>
   <updated>2010-03-08T00:00:00-05:00</updated>
   <id>http://joncanady.com/2010/03/08/gps-failures-and-user-experience</id>
   <content type="html">&lt;p&gt;My GPS taught me a valuable lesson about user experience the other day.&lt;/p&gt;

&lt;p&gt;I normally drive on the highways whenever possible, but there was a massive snowstorm in Columbus one evening and I told my GPS to ignore highways due to numerous accidents.&lt;/p&gt;

&lt;p&gt;I'd forgotten that I'd set that option the next time I needed the GPS. So I turn it on, enter my destination, and get on the highway before it's able to get good enough reception to calculate a route. Of course, since I'd told it to forbid highways, it wasn't able to find a route that it could accept, and it failed.&lt;/p&gt;

&lt;p&gt;The problem is, it told me so with a paragraph-long error message about how it couldn't locate the origin. In the middle of driving down a freeway, it's hard to parse that kind of crap.&lt;/p&gt;

&lt;p&gt;Web applications also run into similar issues: how do you handle the case where a user asks to do something, but an option she's set prevents her from doing it?&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Good&lt;/strong&gt;: Explain to the user the problem using non-domain-specific terms. In my example, a quick &quot;We're unable to generate a route that matches your options.&quot; would work.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Better&lt;/strong&gt;: Explain the problem, and provide a suggested solution. &quot;We cannot generate a valid route. Please check Route Options (under Settings) and try again.&quot;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Best&lt;/strong&gt;: Explain the issue and provide a one-click way to fix it. &quot;We can't generate a valid route because you've turned &lt;em&gt;Forbid Highways&lt;/em&gt; on. [Allow highway access] [Cancel this route]&quot;&lt;/p&gt;
</content>
 </entry>
 
 <entry>
   <title>The Security Magic Bullet</title>
   <link href="http://joncanady.com/2010/02/26/the-security-magic-bullet/"/>
   <updated>2010-02-26T00:00:00-05:00</updated>
   <id>http://joncanady.com/2010/02/26/the-security-magic-bullet</id>
   <content type="html">&lt;blockquote&gt;&lt;p&gt;Screw input validation, love output normalization.&lt;/p&gt;&lt;/blockquote&gt;

&lt;p&gt;That was a recent comment on Hacker News, probably made by a programmer. How are there still people that don't understand &lt;em&gt;there's no magic bullet&lt;/em&gt;?&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Assume hostility&lt;/li&gt;
&lt;li&gt;Input validation&lt;/li&gt;
&lt;li&gt;Enforce business rules&lt;/li&gt;
&lt;li&gt;Output sanitization&lt;/li&gt;
&lt;/ol&gt;


&lt;p&gt;Output normalization fixes your XSS attacks and SQL injections, sure. But it doesn't help at all if I send 'admin=true' to your &lt;code&gt;/users/me/update&lt;/code&gt; URL or I give you a comma-separated list of 50,000 emails to add to your mailing list.&lt;/p&gt;
</content>
 </entry>
 
 <entry>
   <title>If you have REST, why XML-RPC?</title>
   <link href="http://joncanady.com/2010/01/14/if-you-have-rest-why-xml-rpc/"/>
   <updated>2010-01-14T00:00:00-05:00</updated>
   <id>http://joncanady.com/2010/01/14/if-you-have-rest-why-xml-rpc</id>
   <content type="html">&lt;h2&gt;Update&lt;/h2&gt;

&lt;p&gt;There's points going back and forth on &lt;a href=&quot;http://news.ycombinator.com/item?id=1052926&quot;&gt;Hacker News&lt;/a&gt; today -- I don't know if HN archives its threads forever, so if that link breaks let me know.&lt;/p&gt;

&lt;h2&gt;The original article follows&lt;/h2&gt;

&lt;p&gt;There's a large re-architecture project for one of our products at work that's in the planning stages, and we're trying to decide between XML-RPC and REST for communication between two layers. The title of the post should make my bias pretty obvious.&lt;/p&gt;

&lt;h2&gt;XML-RPC&lt;/h2&gt;

&lt;p&gt;XML-RPC is just a protocol that defines how you can call methods on a remote system. It works by serializing your request and any parameters into a standard chunk of XML, and similarly encoding the response and data from the server. Its up to the client and server to agree upon what methods are available and what parameters they take, although this can be facilitated by providing that data as an XML file that clients can consume, which most implementations auto-generate for you. It also provides for standard responses that indicate error codes and descriptions, which are up to the implementor to create.&lt;/p&gt;

&lt;h2&gt;Wait, what's wrong with that?&lt;/h2&gt;

&lt;p&gt;It's completely unnecessary. We're building a web service, which runs over HTTP. HTTP already has facilities for making requests and returning responses. Plus, its error states are already well known, and it provides for other things, such as client-side caching and authentication, that XML-RPC doesn't.&lt;/p&gt;

&lt;h2&gt;So what's REST?&lt;/h2&gt;

&lt;p&gt;Pretty much that. REST over HTTP says you use all the things HTTP gives you, and you provide representations of your resources. You do this with the Web all day: when you go to someone's Twitter profile, for instance, Twitter is giving you an HTML representation of a resource they have, namely a Twitter user.&lt;/p&gt;

&lt;h2&gt;But what's the point?&lt;/h2&gt;

&lt;p&gt;Let's say you know I provide Employee information for a company, and you need to get information for a specific employee.  You've come to my site, and read the documentation for my service so you know how to interact with it.&lt;/p&gt;

&lt;h3&gt;With XML-RPC&lt;/h3&gt;

&lt;p&gt;In your programming language of choice, fire up an appropriate library and have it read in my auto-generated XML description of my service. Then, call the &lt;code&gt;SearchEmployeesByName()&lt;/code&gt; method and give it the employee's name. Your library will create the XML for the request, decode the XML from the response, and likely give you a native object that you can manipulate.  You can then find the employee you want and call &lt;code&gt;GetEmployeeDetailsById()&lt;/code&gt; and pass the specific employee ID you found in step one.&lt;/p&gt;

&lt;h3&gt;With REST&lt;/h3&gt;

&lt;p&gt;If you just need some data, open your web browser and go to &lt;code&gt;http://my-fake-service.com/employees/&lt;/code&gt;.  Click the employee name you want, which might take you to &lt;code&gt;/employees/121&lt;/code&gt;.  I've provided you the HTML representation of the resource (a web page). You're done.&lt;/p&gt;

&lt;p&gt;Assuming you want to access the data programmatically, make an HTTP request to &lt;code&gt;/employees.json&lt;/code&gt; and get a JSON representation of my employees. Then, once you've have the ID, make a second request to &lt;code&gt;/employees/121.json&lt;/code&gt;, and decode the JSON response.&lt;/p&gt;

&lt;p&gt;That's not all. If you need an image of the employee, try &lt;code&gt;/employees/121.jpg&lt;/code&gt;. If you need the data as a PDF, try &lt;code&gt;/employees/121.pdf&lt;/code&gt;.  REST isn't limited to providing XML responses, it can provide any format the server supports.&lt;br/&gt;
&lt;/p&gt;

&lt;p&gt;And it's easy to provide a list of related links to other representations or related resources.&lt;/p&gt;

&lt;h2&gt;Why wouldn't anyone like that?&lt;/h2&gt;

&lt;p&gt;No clue. During an impassioned speech, I got a lot of questions or statements, usually things that XML-RPC handles that REST seemingly doesn't.&lt;/p&gt;

&lt;dl&gt;
    &lt;dt&gt;How do you know if an error occurred?&lt;/dt&gt;
    &lt;dd&gt;HTTP provides statuses for nearly any error you can encounter. You probably know about &quot;404 Document Not Found&quot;; there's &lt;a href=&quot;http://en.wikipedia.org/wiki/List_of_HTTP_status_codes&quot;&gt;tons more&lt;/a&gt;, including &quot;400 Bad Request&quot; (I don't understand what you want), &quot;401 Unauthorized&quot; (You need to authenticate first), and more esoteric but useful codes such as &quot;201 Created&quot; (We've created a resource based on your input), &quot;402 Payment Required&quot; (We need money for this), and &quot;409 Conflict&quot; (Someone else changed this resource before you did, you should try again).  With XML-RPC you have to re-invent all of these.&lt;/dd&gt;
    
    &lt;dt&gt;XML-RPC provides automatic documentation.&lt;/dt&gt;
    &lt;dd&gt;Well, your library automatically generates documentation like &quot;getUser() takes an integer and returns a UserStruct&quot; -- you still have to define what a UserStruct is and what that integer means. Kudos if your library can automatically generate those, but now we're discussing specific implementations, which is a separate issue.&lt;/dd&gt;
    
    &lt;dt&gt;How do you know if your data's valid?&lt;/dt&gt;
    &lt;dd&gt;You test it, same way you do with XML-RPC. Someone actually asked how you know a date is a date if the XML doesn't specifically tell you it's a date. If I'm providing a piece of data called &quot;expiration_date&quot; and it's &lt;em&gt;not&lt;/em&gt; a date, that's something the client needs to handle regardless of how it obtained it.&lt;/dd&gt;
    
    &lt;dt&gt;REST seems too restrictive.&lt;/dt&gt;
    &lt;dd&gt;REST doesn't force you to always talk about representations of resources, but you gain a lot of simplicity if you do. For example, you can have a &quot;Dashboard&quot; resource who's representation is as a bunch of overview data in whatever format.&lt;br&gt;&lt;br&gt;One of our issues was &quot;I want to provide a count of a particular resource instead of returning the whole list.&quot; You can absolutely do that: if you have /employees.json that provides a JSON list of all employees, knock a parameter on there: /employees.json?count=true -- it seems unclean, but all the logic for retrieving employee data stays in one place, instead of scattered around multiple methods like &quot;GetEmployees()&quot;, &quot;GetEmployeeCount()&quot;, &quot;GetActiveEmployeeCount()&quot; and so on.&lt;/dd&gt;

    &lt;dt&gt;But all our clients use XML-RPC!&lt;/dt&gt;
    &lt;dd&gt;TouchÃ©.&lt;/dd&gt;
&lt;/dl&gt;


&lt;h2&gt;Clients, The Business Kind&lt;/h2&gt;

&lt;p&gt;If the field you're working in is dominated by companies that have (for whatever reasons) standardized on things like XML-RPC and SOAP, and you want to integrate with them, it's probably best to use XML-RPC. I like to think that REST provides &lt;em&gt;obvious&lt;/em&gt; advantages over XML-RPC as described above, but in the end if the people giving you money want XML-RPC, then you give them XML-RPC.&lt;/p&gt;

&lt;p&gt;Responses to this are encouraged, and quality or thought-provoking responses may be added. &lt;a href=&quot;http://twitter.com/joncanady&quot;&gt;Twitter&lt;/a&gt; or &lt;a href=&quot;mailto:jon@joncanady.com&quot;&gt;email&lt;/a&gt; your thoughts.&lt;/p&gt;
</content>
 </entry>
 
 <entry>
   <title>Review: Kinesis Freestyle Keyboard</title>
   <link href="http://joncanady.com/2010/01/06/review%3A-kinesis-freestyle-keyboard/"/>
   <updated>2010-01-06T00:00:00-05:00</updated>
   <id>http://joncanady.com/2010/01/06/review:-kinesis-freestyle-keyboard</id>
   <content type="html">&lt;p&gt;I've been using my new Kinesis Freestyle keyboard all this afternoon.&lt;/p&gt;

&lt;p&gt;Check out my &lt;a href=&quot;http://www.flickr.com/photos/jcanady/sets/72157623032263907/&quot;&gt;Flickr set&lt;/a&gt; for some pictures of it in action, as well as some closeups of the issues I'll mention below.&lt;/p&gt;

&lt;h2&gt;Long Story Short&lt;/h2&gt;

&lt;p&gt;I can get past the few slight issues, and I love it.&lt;/p&gt;

&lt;h2&gt;First Impressions&lt;/h2&gt;

&lt;p&gt;I'm no keyboard connoisseur, but I know what I like, and I like the switches on the Freestyle. The keys have just enough travel and resistance to suit my tastes. They feel good, and make a pleasing sound. I don't think they're mechanical switches, but I can live with that.&lt;/p&gt;

&lt;h2&gt;The Top Function Keys&lt;/h2&gt;

&lt;p&gt;The top row of F-keys doubles as your standard Mac modifier keys, using a layout very similar to current Apple models.  They've added a button that toggles Dock auto-hiding, which is neat if not useful, and a power button.&lt;/p&gt;

&lt;p&gt;I normally hate function-keys-cum-media-keys -- I like to be able to adjust my volume, play my media, &lt;em&gt;and&lt;/em&gt; hit function keys when I need them, without using a modifier key. Kinesis thought of me when making this keyboard: the fn key (which toggles between F1-F12 and Apple shortcut keys) isn't a modifier, it's a mode-switcher (like caps lock). When I'm in need of full F1-F12 love, I hit that key and I've got it. Otherwise, I leave it off and have my media keys and so on. I can't say how much I appreciate that.&lt;/p&gt;

&lt;h2&gt;The Driverless Hotkeys&lt;/h2&gt;

&lt;p&gt;There are some hotkeys on the left-hand side which appear to be hardware macros. Internet back/forward, beginning/end of line, cut, copy, paste, undo, select all, and the aforementioned &quot;fn&quot; key.&lt;/p&gt;

&lt;p&gt;I can't see becoming dependent on them, but they're handy if you remember to use
them.&lt;/p&gt;

&lt;h2&gt;Gotchas&lt;/h2&gt;

&lt;p&gt;If you're coming from a regular Ergonomic keyboard (I previously used the Microsoft Natural Elite 4000), the location of the left command key is weird: it's positioned (below) halfway between &quot;x&quot; and &quot;z&quot; instead of directly under &quot;x&quot;. I constantly miss it.&lt;/p&gt;

&lt;p&gt;Same with the right Shift key: there's padding between it and the up-arrow, and I sometimes shove my finger there instead of hitting the Shift.&lt;/p&gt;

&lt;p&gt;Since it's a compact keyboard, you don't get the pageup/pagedown/home/end/insert/delete block. There's no insert key (which is fine), delete is above the backspace key (and just as wide), and the other four keys are vertically aligned on the right-hand side of the keyboard.  Not the best layout, but given the constraints, not the worst.&lt;/p&gt;

&lt;h2&gt;The Last Word&lt;/h2&gt;

&lt;p&gt;I've got mine at the Innova office right now, and I'm strongly considering buying one for home.&lt;/p&gt;
</content>
 </entry>
 
 <entry>
   <title>Jekyll Helper Scripts</title>
   <link href="http://joncanady.com/2010/01/04/jekyll-helper-scripts/"/>
   <updated>2010-01-04T00:00:00-05:00</updated>
   <id>http://joncanady.com/2010/01/04/jekyll-helper-scripts</id>
   <content type="html">&lt;p&gt;I have a couple of helper scripts that I couldn't use Jekyll without:&lt;/p&gt;

&lt;h2&gt;deploy&lt;/h2&gt;

&lt;p&gt;Shamelessly stolen from &lt;a href=&quot;http://henrik.nyh.se/&quot;&gt;The Pug Automatic&lt;/a&gt;, it's pretty quick (and all on one line, despite what word-wrapping occurs here):&lt;/p&gt;

&lt;div class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;span class=&quot;lineno&quot;&gt;1&lt;/span&gt; &lt;span class=&quot;go&quot;&gt;jekyll --no-auto &amp;amp;&amp;amp; rsync -avz --delete _site/ blog:/pub/jcanady/joncanady.com/&lt;/span&gt;
&lt;/pre&gt;
&lt;/div&gt;


&lt;p&gt;Run &lt;code&gt;jekyll&lt;/code&gt; -- I keep auto-regeneration turned on in my script, so disable that -- then rsync the site directory up to my blog's host. &lt;code&gt;blog&lt;/code&gt; is an ssh alias. I like to have my blog's repository hosted on Github, and for various reasons Github can't connect to my blog's server, so this is nearly as good.&lt;/p&gt;

&lt;h2&gt;newpost&lt;/h2&gt;

&lt;p&gt;This one is a lifesaver. I can't remember where I found the original ruby script, but after sprinkling a little &lt;code&gt;optparse&lt;/code&gt; over it, it became an indispensable utility; just drop it somewhere on your &lt;code&gt;$PATH&lt;/code&gt; (&lt;code&gt;~/bin/&lt;/code&gt; is nice).&lt;/p&gt;

&lt;div class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;span class=&quot;lineno&quot;&gt; 1&lt;/span&gt; &lt;span class=&quot;c1&quot;&gt;#!/usr/bin/env ruby&lt;/span&gt;
&lt;span class=&quot;lineno&quot;&gt; 2&lt;/span&gt; 
&lt;span class=&quot;lineno&quot;&gt; 3&lt;/span&gt; &lt;span class=&quot;c1&quot;&gt;###&lt;/span&gt;
&lt;span class=&quot;lineno&quot;&gt; 4&lt;/span&gt; &lt;span class=&quot;c1&quot;&gt;# newpost -- creates a new Jekyll post&lt;/span&gt;
&lt;span class=&quot;lineno&quot;&gt; 5&lt;/span&gt; &lt;span class=&quot;c1&quot;&gt;# Change the blog_dir variable below, and you should be set.&lt;/span&gt;
&lt;span class=&quot;lineno&quot;&gt; 6&lt;/span&gt; &lt;span class=&quot;c1&quot;&gt;# For maximum win, put in ~/bin/ (and add that to your $PATH)&lt;/span&gt;
&lt;span class=&quot;lineno&quot;&gt; 7&lt;/span&gt; &lt;span class=&quot;c1&quot;&gt;#&lt;/span&gt;
&lt;span class=&quot;lineno&quot;&gt; 8&lt;/span&gt; &lt;span class=&quot;c1&quot;&gt;# Jon Canady, 2010. &lt;/span&gt;
&lt;span class=&quot;lineno&quot;&gt; 9&lt;/span&gt; &lt;span class=&quot;c1&quot;&gt;# Covered by the WTFPL: http://sam.zoy.org/wtfpl/&lt;/span&gt;
&lt;span class=&quot;lineno&quot;&gt;10&lt;/span&gt; &lt;span class=&quot;c1&quot;&gt;###&lt;/span&gt;
&lt;span class=&quot;lineno&quot;&gt;11&lt;/span&gt; 
&lt;span class=&quot;lineno&quot;&gt;12&lt;/span&gt; &lt;span class=&quot;c1&quot;&gt;# omit the trailing slash, please&lt;/span&gt;
&lt;span class=&quot;lineno&quot;&gt;13&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;blog_dir&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&amp;quot;/Users/jonc/Sites/joncanady-blog&amp;quot;&lt;/span&gt; 
&lt;span class=&quot;lineno&quot;&gt;14&lt;/span&gt; 
&lt;span class=&quot;lineno&quot;&gt;15&lt;/span&gt; 
&lt;span class=&quot;lineno&quot;&gt;16&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;require&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&amp;#39;optparse&amp;#39;&lt;/span&gt;
&lt;span class=&quot;lineno&quot;&gt;17&lt;/span&gt; 
&lt;span class=&quot;lineno&quot;&gt;18&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;options&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{}&lt;/span&gt;
&lt;span class=&quot;lineno&quot;&gt;19&lt;/span&gt; 
&lt;span class=&quot;lineno&quot;&gt;20&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;optparse&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;OptionParser&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;do&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;|&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;opts&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;|&lt;/span&gt;
&lt;span class=&quot;lineno&quot;&gt;21&lt;/span&gt; 
&lt;span class=&quot;lineno&quot;&gt;22&lt;/span&gt;   &lt;span class=&quot;c1&quot;&gt;# banner&lt;/span&gt;
&lt;span class=&quot;lineno&quot;&gt;23&lt;/span&gt;   &lt;span class=&quot;n&quot;&gt;opts&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;banner&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&amp;quot;Usage: newpost [options] &lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\&amp;quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;title of post&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\&amp;quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&amp;quot;&lt;/span&gt;
&lt;span class=&quot;lineno&quot;&gt;24&lt;/span&gt; 
&lt;span class=&quot;lineno&quot;&gt;25&lt;/span&gt;   &lt;span class=&quot;n&quot;&gt;options&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;ss&quot;&gt;:draft&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;kp&quot;&gt;false&lt;/span&gt;
&lt;span class=&quot;lineno&quot;&gt;26&lt;/span&gt;   &lt;span class=&quot;n&quot;&gt;opts&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;on&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&amp;#39;-d&amp;#39;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&amp;#39;--draft&amp;#39;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&amp;#39;Create post as a draft&amp;#39;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;do&lt;/span&gt; 
&lt;span class=&quot;lineno&quot;&gt;27&lt;/span&gt;     &lt;span class=&quot;n&quot;&gt;options&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;ss&quot;&gt;:draft&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;kp&quot;&gt;true&lt;/span&gt;
&lt;span class=&quot;lineno&quot;&gt;28&lt;/span&gt;   &lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;
&lt;span class=&quot;lineno&quot;&gt;29&lt;/span&gt; 
&lt;span class=&quot;lineno&quot;&gt;30&lt;/span&gt;   &lt;span class=&quot;n&quot;&gt;options&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;ss&quot;&gt;:format&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;ss&quot;&gt;:markdown&lt;/span&gt;
&lt;span class=&quot;lineno&quot;&gt;31&lt;/span&gt;   &lt;span class=&quot;n&quot;&gt;opts&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;on&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&amp;#39;-f&amp;#39;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&amp;#39;--format FORMAT&amp;#39;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&amp;#39;Post is in [markdown|textile] (default markdown)&amp;#39;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;do&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;|&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;format&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;|&lt;/span&gt;
&lt;span class=&quot;lineno&quot;&gt;32&lt;/span&gt;     &lt;span class=&quot;n&quot;&gt;valid_formats&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;sx&quot;&gt;%w(markdown textile)&lt;/span&gt;
&lt;span class=&quot;lineno&quot;&gt;33&lt;/span&gt;     &lt;span class=&quot;nb&quot;&gt;puts&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;opts&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;exit&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;unless&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;valid_formats&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;include?&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;format&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;lineno&quot;&gt;34&lt;/span&gt;     &lt;span class=&quot;n&quot;&gt;options&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;ss&quot;&gt;:format&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;format&lt;/span&gt;
&lt;span class=&quot;lineno&quot;&gt;35&lt;/span&gt;   &lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;
&lt;span class=&quot;lineno&quot;&gt;36&lt;/span&gt; 
&lt;span class=&quot;lineno&quot;&gt;37&lt;/span&gt;   &lt;span class=&quot;n&quot;&gt;opts&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;on&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&amp;#39;-h&amp;#39;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&amp;#39;--help&amp;#39;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&amp;#39;Display this screen&amp;#39;&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;do&lt;/span&gt;
&lt;span class=&quot;lineno&quot;&gt;38&lt;/span&gt;     &lt;span class=&quot;nb&quot;&gt;puts&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;opts&lt;/span&gt;
&lt;span class=&quot;lineno&quot;&gt;39&lt;/span&gt;     &lt;span class=&quot;nb&quot;&gt;exit&lt;/span&gt;
&lt;span class=&quot;lineno&quot;&gt;40&lt;/span&gt;   &lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;
&lt;span class=&quot;lineno&quot;&gt;41&lt;/span&gt; 
&lt;span class=&quot;lineno&quot;&gt;42&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;
&lt;span class=&quot;lineno&quot;&gt;43&lt;/span&gt; 
&lt;span class=&quot;lineno&quot;&gt;44&lt;/span&gt; 
&lt;span class=&quot;lineno&quot;&gt;45&lt;/span&gt; 
&lt;span class=&quot;lineno&quot;&gt;46&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;begin&lt;/span&gt;
&lt;span class=&quot;lineno&quot;&gt;47&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;optparse&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;parse!&lt;/span&gt;
&lt;span class=&quot;lineno&quot;&gt;48&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;rescue&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;OptionParser&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;no&quot;&gt;InvalidOption&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;e&lt;/span&gt;
&lt;span class=&quot;lineno&quot;&gt;49&lt;/span&gt;   &lt;span class=&quot;nb&quot;&gt;puts&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;e&lt;/span&gt;
&lt;span class=&quot;lineno&quot;&gt;50&lt;/span&gt;   &lt;span class=&quot;nb&quot;&gt;puts&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;o&lt;/span&gt;
&lt;span class=&quot;lineno&quot;&gt;51&lt;/span&gt;   &lt;span class=&quot;nb&quot;&gt;exit&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;1&lt;/span&gt;
&lt;span class=&quot;lineno&quot;&gt;52&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;
&lt;span class=&quot;lineno&quot;&gt;53&lt;/span&gt;  
&lt;span class=&quot;lineno&quot;&gt;54&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;ARGV&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;].&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;nil?&lt;/span&gt;
&lt;span class=&quot;lineno&quot;&gt;55&lt;/span&gt;   &lt;span class=&quot;n&quot;&gt;optparse&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;display&lt;/span&gt;
&lt;span class=&quot;lineno&quot;&gt;56&lt;/span&gt;   &lt;span class=&quot;nb&quot;&gt;exit&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;1&lt;/span&gt;
&lt;span class=&quot;lineno&quot;&gt;57&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;
&lt;span class=&quot;lineno&quot;&gt;58&lt;/span&gt; 
&lt;span class=&quot;lineno&quot;&gt;59&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;puts&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&amp;quot;Generating post...&amp;quot;&lt;/span&gt;
&lt;span class=&quot;lineno&quot;&gt;60&lt;/span&gt; 
&lt;span class=&quot;lineno&quot;&gt;61&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;date_prefix&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;Time&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;now&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;strftime&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&amp;quot;%Y-%m-%d&amp;quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;lineno&quot;&gt;62&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;postname&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;ARGV&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;].&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;strip&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;downcase&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;gsub&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;sr&quot;&gt;/ /&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&amp;#39;-&amp;#39;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;lineno&quot;&gt;63&lt;/span&gt; 
&lt;span class=&quot;lineno&quot;&gt;64&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;post_dir&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;options&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;ss&quot;&gt;:draft&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;?&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&amp;#39;_drafts&amp;#39;&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&amp;#39;_posts&amp;#39;&lt;/span&gt;
&lt;span class=&quot;lineno&quot;&gt;65&lt;/span&gt; 
&lt;span class=&quot;lineno&quot;&gt;66&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;post&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&amp;quot;&lt;/span&gt;&lt;span class=&quot;si&quot;&gt;#{&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;blog_dir&lt;/span&gt;&lt;span class=&quot;si&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;/&lt;/span&gt;&lt;span class=&quot;si&quot;&gt;#{&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;post_dir&lt;/span&gt;&lt;span class=&quot;si&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;/&lt;/span&gt;&lt;span class=&quot;si&quot;&gt;#{&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;date_prefix&lt;/span&gt;&lt;span class=&quot;si&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;-&lt;/span&gt;&lt;span class=&quot;si&quot;&gt;#{&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;postname&lt;/span&gt;&lt;span class=&quot;si&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;si&quot;&gt;#{&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;options&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;ss&quot;&gt;:format&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;si&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&amp;quot;&lt;/span&gt;
&lt;span class=&quot;lineno&quot;&gt;67&lt;/span&gt;  
&lt;span class=&quot;lineno&quot;&gt;68&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;header&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&amp;lt;&amp;lt;-&lt;/span&gt;&lt;span class=&quot;no&quot;&gt;END&lt;/span&gt;
&lt;span class=&quot;lineno&quot;&gt;69&lt;/span&gt; &lt;span class=&quot;sh&quot;&gt;---&lt;/span&gt;
&lt;span class=&quot;lineno&quot;&gt;70&lt;/span&gt; &lt;span class=&quot;sh&quot;&gt;layout: post&lt;/span&gt;
&lt;span class=&quot;lineno&quot;&gt;71&lt;/span&gt; &lt;span class=&quot;sh&quot;&gt;title: #{ARGV[0]}&lt;/span&gt;
&lt;span class=&quot;lineno&quot;&gt;72&lt;/span&gt; &lt;span class=&quot;sh&quot;&gt;---&lt;/span&gt;
&lt;span class=&quot;lineno&quot;&gt;73&lt;/span&gt; &lt;span class=&quot;sh&quot;&gt; &lt;/span&gt;
&lt;span class=&quot;lineno&quot;&gt;74&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;END&lt;/span&gt;
&lt;span class=&quot;lineno&quot;&gt;75&lt;/span&gt;  
&lt;span class=&quot;lineno&quot;&gt;76&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;File&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;open&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;post&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&amp;#39;w&amp;#39;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;do&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;|&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;f&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;|&lt;/span&gt;
&lt;span class=&quot;lineno&quot;&gt;77&lt;/span&gt;   &lt;span class=&quot;n&quot;&gt;f&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&amp;lt;&amp;lt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;header&lt;/span&gt;
&lt;span class=&quot;lineno&quot;&gt;78&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;
&lt;span class=&quot;lineno&quot;&gt;79&lt;/span&gt;  
&lt;span class=&quot;lineno&quot;&gt;80&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;system&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&amp;quot;mate&amp;quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&amp;quot;-a&amp;quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;post&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;/pre&gt;
&lt;/div&gt;


&lt;p&gt;I bundle both of these with my blog's source; the latest versions can always be found in my &lt;a href=&quot;http://github.com/joncanady/joncanady.com/tree/master/tasks&quot;&gt;&lt;code&gt;tasks/&lt;/code&gt;&lt;/a&gt; directory.&lt;/p&gt;
</content>
 </entry>
 
 <entry>
   <title>Android Basics My iPhone Still Can't Get Right</title>
   <link href="http://joncanady.com/2010/01/03/android-basics-my-iphone-still-cant-get-right/"/>
   <updated>2010-01-03T00:00:00-05:00</updated>
   <id>http://joncanady.com/2010/01/03/android-basics-my-iphone-still-cant-get-right</id>
   <content type="html">&lt;p&gt;Man, Android's starting to look good. The interface is not &lt;em&gt;nearly&lt;/em&gt; as polished as the iPhone's, but the iPhone's failing on other levels which bothers me more and more every day:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Notifcations&lt;/strong&gt;: I really love Android's pull-down notification center. The iPhone's handling of notifications is infantile by comparison:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;You get one dialog on the lock screen that shows everything.&lt;/li&gt;
&lt;li&gt;The minute you unlock the device it goes away and leaves you with individual app badges as the only notification that something happened.&lt;/li&gt;
&lt;li&gt;Third-party apps can use icon badges but they can't use the lock-screen notifications, so even if your twitter client is getting push notifications, it doesn't show on the home screen.&lt;/li&gt;
&lt;li&gt;Not to mention that I can't respond to an SMS without &lt;em&gt;quitting&lt;/em&gt; what I'm doing.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;3rd-Party App Backgrounding&lt;/strong&gt;: My iPhone Twitter client can't pull messages if it isn't running.  Things for iPhone can't update its dock badge without being run once, which makes the dock badge near-useless.  And I can never have something like &lt;a href=&quot;http://www.twofortyfouram.com/&quot;&gt;Locale&lt;/a&gt;, which is too awesome for words.&lt;br/&gt;
&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Customization&lt;/strong&gt;: Hey Apple, why can I change my ringtone but not the New Mail, New SMS, or Calendar Alert sounds? Why can't I put a background &lt;em&gt;behind&lt;/em&gt; the app icons? And why can't I replace the dialer with Google Voice?&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Mail Client&lt;/strong&gt;: To be fair I've never used the Android mail client, but it's gotta be better than the MobileMail.app. No unified inbox, no spam filters, no PUSH IMAP for GMail, and no &quot;Mark All As Read&quot; option. Nearly useless.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;


&lt;p&gt;I'm still sticking with my iPhone, but if Apple doesn't up its game with its 2010 offering, it may be time to jump ship.&lt;/p&gt;
</content>
 </entry>
 
 <entry>
   <title>Switching to Jekyll</title>
   <link href="http://joncanady.com/2009/12/28/switching-from-wordpress-to-jekyll/"/>
   <updated>2009-12-28T00:00:00-05:00</updated>
   <id>http://joncanady.com/2009/12/28/switching-from-wordpress-to-jekyll</id>
   <content type="html">&lt;p&gt;Overnight there's been some pretty major changes around here.&lt;/p&gt;

&lt;h2&gt;Design&lt;/h2&gt;

&lt;p&gt;Inspired by various blogs and products, I've taken up a more minimalist design. I've long since grown tired of overly-active blog themes with way too much cruft in the sidebars that detracts from the actual content. It's a work-in-progress, and comments are welcome.  Speaking of comments:&lt;/p&gt;

&lt;h2&gt;Comments Gone&lt;/h2&gt;

&lt;p&gt;As a side effect of updating blog software (more below), comments are now gone. The new system doesn't support comments out-of-the-box, and I'm seeing that as a feature. I can't remember a single time that comments on a blog have been very helpful, but can point to thousands of examples of comments detracting from a piece.&lt;/p&gt;

&lt;p&gt;If you want to comment on a post, my twitter and email are well-published here; feel free to send me feedback. Or, feel free to write something on your own site; if it's well done I'm happy to update my post and link to it. Manual trackbacks, I suppose.&lt;/p&gt;

&lt;h2&gt;Jekyll&lt;/h2&gt;

&lt;p&gt;The meat of the update is that I've ditched Wordpress for &lt;a href=&quot;http://github.com/mojombo/jekyll&quot;&gt;Jekyll&lt;/a&gt;, a blog-aware, static site generator written in Ruby.  Instead of having a database of blog posts and pages, I now have HTML/Markdown and files that Jekyll turns into a static web site.  The benefits of this system are enormous:&lt;/p&gt;

&lt;h3&gt;Everything's a File&lt;/h3&gt;

&lt;p&gt;Since everything's based on flat files, I can store &lt;em&gt;everything&lt;/em&gt; -- posts, layout, content pages -- in &lt;a href=&quot;http://git-scm.org&quot;&gt;Git&lt;/a&gt;, edit everything in whatever editor I'm feeling like using (TextMate, Emacs, Vim, what have you), and that's all there is to it. Gone are the days of periodically running mysqldump on a wordpress database and stashing tarballs somewhere.&lt;/p&gt;

&lt;h3&gt;No More Wordpress Themes&lt;/h3&gt;

&lt;p&gt;I never got my mind around writing Wordpress themes, which means if I ever wanted my site to have a different visual theme I had to crack open Wordpress docs and try to modify whatever theme I was using.  Nuts to that.&lt;/p&gt;

&lt;p&gt;Now I write CSS and HTML with a little &lt;a href=&quot;http://www.liquidmarkup.org/&quot;&gt;Liquid&lt;/a&gt; templating thrown in and I'm good to go. If I wanted, I could switch to &lt;a href=&quot;http://haml-lang.com/&quot;&gt;Haml/Sass&lt;/a&gt; and use that instead, which is amazing.&lt;/p&gt;

&lt;h3&gt;No Backend&lt;/h3&gt;

&lt;p&gt;Git and rsync are all the backend I have to speak of. There's no database to get overloaded, and no admin panel that needs &lt;a href=&quot;http://www.seoegghead.com/software/wordpress-firewall.seo&quot;&gt;esoteric&lt;/a&gt; &lt;a href=&quot;http://www.bad-neighborhood.com/login-lockdown.html&quot;&gt;plugins&lt;/a&gt; to secure it because the default install can't figure security out.  My webserver serves non-interpreted HTML files, so the only limit is the capacity of my server and pipe.&lt;/p&gt;

&lt;p&gt;If this all sounds awesome to you, I'm putting together a quick tutorial on how to migrate a Wordpress site to Jekyll which should be up in a few days.&lt;/p&gt;
</content>
 </entry>
 
 <entry>
   <title>HTML 5 Video Annoying, Usable Today</title>
   <link href="http://joncanady.com/2009/12/21/HTML-5-Video-Annoying-Usable/"/>
   <updated>2009-12-21T00:00:00-05:00</updated>
   <id>http://joncanady.com/2009/12/21/HTML-5-Video-Annoying-Usable</id>
   <content type="html">&lt;p&gt;&lt;a href=&quot;http://daringfireball.com&quot;&gt;Gruber&lt;/a&gt; &lt;a href=&quot;http://daringfireball.net/2009/12/html5_video_unusable&quot;&gt;suggests&lt;/a&gt; that HTML 5 video is &quot;effectively unusable&quot; for the following two reasons:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;It doesn't allow poster images.&lt;/li&gt;
&lt;li&gt;Both audio and video always auto-buffer instead of waiting for a click.&lt;/li&gt;
&lt;/ol&gt;


&lt;p&gt;Our hero gets past the first issue with some Javascript hackery, which also fixes the second issue.  So, completely usable before (with a few issues), 100% usable afterwards.&lt;/p&gt;

&lt;p&gt;There is the ever-present IE issue -- no version of Internet Explorer as of this writing supports &lt;code&gt;&amp;lt;video&amp;gt;&lt;/code&gt; or &lt;code&gt;&amp;lt;audio&amp;gt;&lt;/code&gt; -- but this isn't at all an issue for Gruber.&lt;br/&gt;
&lt;/p&gt;

&lt;p&gt;The effective conclusion to Gruber's &quot;effectively unusable&quot; piece is &quot;I used it for my &lt;a href=&quot;http://daringfireball.net/misc/2009/12/user_guide_demos&quot;&gt;PastryKit video page&lt;/a&gt; but I had to use Javascript and create two versions of each video and I don't like that.&quot;&lt;/p&gt;

&lt;p&gt;Which seems, y'know, &lt;em&gt;usable&lt;/em&gt; to me.&lt;/p&gt;
</content>
 </entry>
 
 <entry>
   <title>Two Simple Rules for Screencasting</title>
   <link href="http://joncanady.com/2009/11/20/two-simple-rules-for-screencasting/"/>
   <updated>2009-11-20T00:00:00-05:00</updated>
   <id>http://joncanady.com/2009/11/20/two-simple-rules-for-screencasting</id>
   <content type="html">&lt;p&gt;During the process of trying to figure out what &lt;a href=&quot;http://compass-style.org/&quot;&gt;Compass&lt;/a&gt; is, I tried to watch the official Compass &lt;a href=&quot;http://wiki.github.com/chriseppstein/compass&quot;&gt;screencast&lt;/a&gt;. This was a mistake. Thinking of writing an introductory screencast for your product? Learn from their mistakes.&lt;/p&gt;

&lt;h3&gt;Keep It Short&lt;/h3&gt;

&lt;p&gt;No shit, the Compass screencast is &lt;em&gt;an hour long.&lt;/em&gt;  The only people who are going to watch that are people with a vested interest in the project, who presumably already know the material.&lt;/p&gt;

&lt;p&gt;Keep your screencasts to around five minutes. You aren't  &lt;a href=&quot;http://peepcode.com/&quot;&gt;PeepCode&lt;/a&gt;, and this thing isn't your documentation. If you can't keep a basic explanation of your product/service down to five minutes, you have bigger problems.&lt;/p&gt;

&lt;h3&gt;Show, Don't Tell&lt;/h3&gt;

&lt;p&gt;Up until about 2:20, the Compass screencast doesn't even change, and it takes another minute before there's any actual content on the screen. If the viewer wanted to read a thorough explanation of your product, they would have read the &quot;about&quot; page.&lt;/p&gt;

&lt;p&gt;Start with a brief intro on what your product is, then jump right into the demonstration. Impress the viewer quickly and keep them impressed.&lt;/p&gt;

&lt;h3&gt;It's That Simple&lt;/h3&gt;

&lt;p&gt;That's all you need for an effective screencast. It's almost a definition: &quot;short&quot; and &quot;demonstration&quot;. It's hard to go wrong.&lt;/p&gt;
</content>
 </entry>
 
 <entry>
   <title>How To Fix a Shitty Project</title>
   <link href="http://joncanady.com/2009/11/18/how-to-fix-a-shitty-project/"/>
   <updated>2009-11-18T00:00:00-05:00</updated>
   <id>http://joncanady.com/2009/11/18/how-to-fix-a-shitty-project</id>
   <content type="html">&lt;p&gt;Way back in the Dark Ages of the Internet (2000), Joel Spolsky &lt;a href=&quot;http://www.joelonsoftware.com/articles/fog0000000069.html&quot;&gt;wrote&lt;/a&gt; of Netscape's impending version 6 release:&lt;/p&gt;

&lt;blockquote&gt;&lt;p&gt;It's a bit smarmy of me to criticize them for waiting so long between releases. They didn't do it on purpose, now, did they?&lt;/p&gt;

&lt;p&gt;Well, yes. They did. They did it by making the &lt;strong&gt;single worst strategic mistake&lt;/strong&gt; that any software company can make:&lt;/p&gt;

&lt;p&gt;They decided to rewrite the code from scratch.&lt;/p&gt;&lt;/blockquote&gt;

&lt;p&gt;Most developers can instantly conjure up at least one project they'd love to rewrite from the ground up.  This time, you'll use the right language, the right libraries, the right style -- you'll &lt;a href=&quot;http://www.jwz.org/doc/cadt.html&quot;&gt;do it &lt;em&gt;right&lt;/em&gt;&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Don't do it.  It's a horrible idea.  Now you've reset your entire timeline back to zero, and you don't know &lt;em&gt;when&lt;/em&gt; you'll be done.  (Say, where's &lt;a href=&quot;http://macromates.com&quot;&gt;TextMate 2&lt;/a&gt;?)  Not to mention the regression bugs you'll undoubtedly let slip in.&lt;/p&gt;

&lt;p&gt;Instead of rewriting the whole thing from nothing, &lt;strong&gt;incrementally fix things.&lt;/strong&gt;  Don't commit a &lt;em&gt;single&lt;/em&gt; change that doesn't leave the code better than you found it.  Move business logic out of controllers and into models.  Changing a feature?  Cover the whole feature with tests.&lt;br/&gt;
&lt;/p&gt;

&lt;p&gt;Eventually, you'll find you've rewritten all the shitty parts of the project anyway and it's not so bad anymore.&lt;/p&gt;
</content>
 </entry>
 
 <entry>
   <title>Multi-Developer Git Suggestions</title>
   <link href="http://joncanady.com/2009/11/08/multi-developer-git-suggestions/"/>
   <updated>2009-11-08T00:00:00-05:00</updated>
   <id>http://joncanady.com/2009/11/08/multi-developer-git-suggestions</id>
   <content type="html">&lt;p&gt;Git's pretty simple to use when it's just you and your Github repo, but as developers increases, complexity increases.&lt;/p&gt;

&lt;h2&gt;Each Developer Has Her Own Branch&lt;/h2&gt;

&lt;p&gt;Make each developer on the team maintain their own branch where their changes initially go before being merged into &lt;code&gt;master&lt;/code&gt;. Bonus: you can vet new developer's changes before they get pushed into whatever the production branch is.&lt;/p&gt;

&lt;p&gt;So, here's what I do:&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;jonc$ git checkout -b jonc
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;Create and switch to the branch &lt;code&gt;jonc&lt;/code&gt;.&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;jonc$ git checkout master
jonc$ git merge jonc
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;Merge all the changes from &lt;code&gt;jonc&lt;/code&gt; into &lt;code&gt;master&lt;/code&gt;
(This, of course, works in reverse, if you need to pull new stuff from &lt;code&gt;master&lt;/code&gt; into your personal branch)&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;jonc$ git push origin jonc:refs/heads/jonc
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;Now the &lt;code&gt;jonc&lt;/code&gt; branch exists on Github.  Anyone else that wants to check out my work is free to do so:&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;otherdev$ git checkout --track -b jonc origin/jonc
&lt;/code&gt;&lt;/pre&gt;

&lt;h2&gt;Features Have Their Own Branches&lt;/h2&gt;

&lt;p&gt;If a feature is going to take longer than one sitting to completely implement -- including tests -- then it goes in its own branch.  Every time I've excepted this rule I've regretted it later.&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;jonc$ git checkout -b bugfix_553
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;If you're going to be collaborating on something with another developer, create it remotely first, then both of you can push/pull from the get-go.&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;jonc$ git push origin origin:refs/heads/bugfix_553
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;That creates the remote branch, and any developer that wants to start working on it does the following:&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;jonc$ git fetch origin    
jonc$ git checkout --track -b bugfix_553 origin/bugfix_553
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;When you're on the &lt;code&gt;bugfix_553&lt;/code&gt; branch, pushes and pulls go to and from there instead of &lt;code&gt;master&lt;/code&gt;.  When ready, you merge it into master as usual.  Once you're 100% done with the branch, delete it as normal (&lt;code&gt;git branch -d bugfix_553&lt;/code&gt;) and remove it from the server if necessary:&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;jonc$ git push origin :heads/bugfix_553
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;No, that command isn't intuitive in the slightest.&lt;/p&gt;

&lt;h2&gt;Production Always Builds&lt;/h2&gt;

&lt;p&gt;The production branch should &lt;em&gt;never&lt;/em&gt; have code in it that breaks.  Hopefully it's got a good test suite, and with any luck it doesn't incur much technical debt, but at the very least it needs to:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Run&lt;/li&gt;
&lt;li&gt;Produce correct output&lt;/li&gt;
&lt;/ol&gt;


&lt;p&gt;This means that deployments can happen whenever. Developing directly in the production branch or merging changes prematurely means that if critical bugfixes need to go out &lt;strong&gt;now&lt;/strong&gt; you can handle that without having to clean up messes first.&lt;/p&gt;

&lt;p&gt;Developers that consistently push bad code into the production branch should be beaten. Having some Continuous Integration process watching that branch isn't a bad idea either.&lt;/p&gt;

&lt;p&gt;(I've always used &lt;code&gt;master&lt;/code&gt; as the production branch, but use whatever makes you feel good.)&lt;/p&gt;
</content>
 </entry>
 
 <entry>
   <title>Flickr is Not Your Backup Service</title>
   <link href="http://joncanady.com/2009/08/07/flickr-is-not-your-backup-service/"/>
   <updated>2009-08-07T00:00:00-04:00</updated>
   <id>http://joncanady.com/2009/08/07/flickr-is-not-your-backup-service</id>
   <content type="html">&lt;p&gt;Dude's Flickr account was compromised, his password changed, and then his account terminated.  &lt;a href=&quot;http://notnixon.com/post/158066658/fuck-flickr-support-i-want-this-to-spread-like&quot;&gt;He seems upset.&lt;/a&gt;&lt;/p&gt;

&lt;blockquote&gt;&lt;p&gt;I want this to spread like wildfire. I want people to know that Flickr, their customer service and overall infrastructure is incredibly disappointing and it needs to be fixed ASAP.&lt;/p&gt;&lt;/blockquote&gt;

&lt;p&gt;...and...&lt;/p&gt;

&lt;blockquote&gt;&lt;p&gt;There were 3,000+ photos on my Flickr photostream from 2004-present. Thousands and thousands of dollars spent on film and developing were pumped into that account and it vanishes within minutes and it’s been days and still I’ve found no resolution with my case.&lt;/p&gt;&lt;/blockquote&gt;

&lt;ol&gt;
&lt;li&gt;Flickr is an awesome web app for sharing your photos with the world, but it is &lt;em&gt;not&lt;/em&gt; an archival service. Flickr happens to host a load of digital images on its servers as effect of providing these photos to the world, not for the purpose of storing them in perptuity.&lt;/li&gt;
&lt;li&gt;Dude spent &quot;thousands and thousands of dollars on film and developing&quot; and  didn't keep local backups of his work? Did he just upload the files to Flickr and then delete them? Come on.&lt;/li&gt;
&lt;/ol&gt;


&lt;p&gt;On the assumption that he didn't keep backups: &lt;a href=&quot;http://www.jwz.org/doc/backups.html&quot;&gt;it's not that hard&lt;/a&gt;. If backups are kept, than at most a bunch of metadata was lost, but then the argument isn't &quot;I wasted a bunch of money developing photos that you deleted&quot; but &quot;I spent hundreds of hours tagging and describing and all for nothing&quot; -- that sucks, but that isn't nearly worth this level of bitchitude.&lt;/p&gt;
</content>
 </entry>
 
 <entry>
   <title>Awesome One-Liners</title>
   <link href="http://joncanady.com/2009/06/10/awesome-one-liners/"/>
   <updated>2009-06-10T00:00:00-04:00</updated>
   <id>http://joncanady.com/2009/06/10/awesome-one-liners</id>
   <content type="html">&lt;p&gt;If you just used the built-in Rails generators to create a new controller and you got a bunch of &lt;code&gt;.html.erb&lt;/code&gt; files, you'll naturally want &lt;code&gt;.html.haml&lt;/code&gt; files instead. Bash can fix those files right fast:&lt;/p&gt;

&lt;script src=&quot;http://gist.github.com/127632.js&quot;&gt;&lt;/script&gt;

</content>
 </entry>
 
 <entry>
   <title>iPhone Lock Code and iTunes 8.2</title>
   <link href="http://joncanady.com/2009/06/01/iphone-lock-code-and-itunes-82/"/>
   <updated>2009-06-01T00:00:00-04:00</updated>
   <id>http://joncanady.com/2009/06/01/iphone-lock-code-and-itunes-82</id>
   <content type="html">&lt;p&gt;Installed the iTunes 8.2 update, docked my phone, and saw a new error message:&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/images/iphone_sync.png&quot; alt=&quot;iTunes 8.2: cannot unlock due to passcode&quot; /&gt;&lt;/p&gt;

&lt;p&gt;I don't know how to feel about this.  The security-conscious (read: paranoid) part of me thinks it's probably a good idea to not let just anyone do a sync of your locked and protected iPhone.  The lazy jerk part of me that just wants to slap his phone in a dock and have the damn thing sync is a little miffed right now.&lt;/p&gt;

&lt;p&gt;I wonder how helpful this really is?  Is the control built into iTunes or the iPhone itself?  Could someone &lt;em&gt;not&lt;/em&gt; using iTunes pull a dump down of the iPhone's HDD?&lt;/p&gt;
</content>
 </entry>
 
 <entry>
   <title>Know when to Fold</title>
   <link href="http://joncanady.com/2009/05/19/know-when-to-fold/"/>
   <updated>2009-05-19T00:00:00-04:00</updated>
   <id>http://joncanady.com/2009/05/19/know-when-to-fold</id>
   <content type="html">&lt;p&gt;From Phillip Greenspun's &quot;&lt;a href=&quot;http://blogs.law.harvard.edu/philg/2009/05/18/ruby-on-rails-and-the-importance-of-being-stupid/&quot;&gt;Ruby on Rails and the importance of being stupid&lt;/a&gt;&quot;, a comparison between a hastily thrown together .NET site and a hastily thrown together Rails site:&lt;/p&gt;

&lt;blockquote&gt;&lt;p&gt;How could a “slice” with 800 MB of RAM run out of memory and start swapping when all it was trying to do was run an HTTP server and a scripting language interpreter? Only a dinosaur would use SQL as a query language. Much better to pull entire tables into Ruby, the most beautiful computer language ever designed, and filter down to the desired rows using Ruby and its “ActiveRecord” facility.&lt;/p&gt;&lt;/blockquote&gt;

&lt;p&gt;Now, this is &lt;em&gt;Greenspun&lt;/em&gt;; he knows when he's writing a biased, one-sided article to make a point.  I'm not certain where the comparison to .NET fits in, but otherwise there's a fine point present.&lt;/p&gt;

&lt;h2&gt;Know when to hold 'em&lt;/h2&gt;

&lt;p&gt;Rails, like every other framework in programming, wants to take something inherently complex and make it simple.  And, starting out, there's no reason not to use every time-saver you can get: use #find, take advantage of associations, use &lt;code&gt;script/runner&lt;/code&gt; to power your cron runs, why not?  Any time you take second-guessing the framework unnecessarily is time you're not spending solving your business problem.&lt;/p&gt;

&lt;h2&gt;Know when to fold 'em&lt;/h2&gt;

&lt;p&gt;The honeymoon will eventually be over and your site will start to bog under the pressure.  Maybe you need some database indices, which ActiveRecord's migrations won't easily provide for you.  Perhaps ActiveRecord is generating sub-optimal queries and you need to step in and (shock) write some SQL.  In fact, &lt;code&gt;script/runner&lt;/code&gt; loading your entire Rails environment just to run a cronjob may not be the speediest solution.&lt;/p&gt;

&lt;p&gt;When these happen, &lt;strong&gt;go with it&lt;/strong&gt;.  If you don't already know about how a column index works or how to hand-craft a funky JOIN, then go learn.  Write cron scripts that just execute some SQL against the server.  Don't futz around with more RAM you don't need, or trying to shoehorn your problems into Rails' solutions.  Instead, find your problem points, and don't hesitate to abandon your framework in places if it's holding you back.&lt;/p&gt;

&lt;p&gt;With apologies to Kenny Rogers.&lt;/p&gt;
</content>
 </entry>
 
 
</feed>