<?xml version="1.0" encoding="UTF-8"?>
<?xml-stylesheet type="text/xsl" media="screen" href="/~d/styles/rss2full.xsl"?><?xml-stylesheet type="text/css" media="screen" href="http://feeds.kyleslattery.com/~d/styles/itemcontent.css"?><rss xmlns:atom="http://www.w3.org/2005/Atom" version="2.0">
  <channel>
    <title>Kyle Slattery - Entries</title>
    <description>Entries from KyleSlattery.com</description>
    <link>http://kyleslattery.com/entries</link>
    
    <atom10:link xmlns:atom10="http://www.w3.org/2005/Atom" rel="self" type="application/rss+xml" href="http://feeds.kyleslattery.com/KyleSlatteryEntries" /><feedburner:info xmlns:feedburner="http://rssnamespace.org/feedburner/ext/1.0" uri="kyleslatteryentries" /><atom10:link xmlns:atom10="http://www.w3.org/2005/Atom" rel="hub" href="http://pubsubhubbub.appspot.com/" /><item>
      <title>Requiring SSL Using Route Constraints in Rails 3</title>
      <description>&lt;p&gt;The &lt;a href="http://yehudakatz.com/2009/12/26/the-rails-3-router-rack-it-up/"&gt;new router in Rails 3&lt;/a&gt; makes it super easy to require SSL for certain routes.  Just use the following in your &lt;code&gt;config/routes.rb&lt;/code&gt;:&lt;/p&gt;

&lt;pre&gt;&lt;code class="ruby"&gt;MyApp::Application.routes.draw do
  class SslConstraint
    def self.matches?(request)
      request.ssl?
    end
  end

  scope :constraints =&amp;gt; SslConstraint do
    resources :payments
    # Other SSL routes go in here
  end
end
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;Now, this is a pretty simple example&amp;#8211;you&amp;#8217;ll likely want to also have routes to redirect if a user tries to access without SSL, but it definitely shows off the power of the new router.&lt;/p&gt;
</description>
      <content>&lt;p&gt;The &lt;a href="http://yehudakatz.com/2009/12/26/the-rails-3-router-rack-it-up/"&gt;new router in Rails 3&lt;/a&gt; makes it super easy to require SSL for certain routes.  Just use the following in your &lt;code&gt;config/routes.rb&lt;/code&gt;:&lt;/p&gt;

&lt;pre&gt;&lt;code class="ruby"&gt;MyApp::Application.routes.draw do
  class SslConstraint
    def self.matches?(request)
      request.ssl?
    end
  end

  scope :constraints =&amp;gt; SslConstraint do
    resources :payments
    # Other SSL routes go in here
  end
end
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;Now, this is a pretty simple example&amp;#8211;you&amp;#8217;ll likely want to also have routes to redirect if a user tries to access without SSL, but it definitely shows off the power of the new router.&lt;/p&gt;
</content>
      <pubDate>Fri, 13 Aug 2010 13:31:00 -0400</pubDate>
      <link>http://kyleslattery.com/entries/requiring-ssl-using-route-constraints-in-rails-3</link>
      <guid>http://kyleslattery.com/entries/requiring-ssl-using-route-constraints-in-rails-3</guid>
    </item>
    <item>
      <title>Using Git Branches as Patches</title>
      <description>&lt;p&gt;At &lt;a href="http://viddler.com"&gt;Viddler&lt;/a&gt;, we&amp;#8217;re now using Git for projects, and it&amp;#8217;s going really well so far. While we haven&amp;#8217;t figured out the perfect workflow just yet, we&amp;#8217;re doing some things I really like, and one of them is treating branches like patches.&lt;/p&gt;

&lt;p&gt;Often, people think of Git branches as a full copy of the parent branch, but it&amp;#8217;s better to treat them as a simple collection of new commits, to be applied to the parent branch later.  This might not seem too revolutionary, but this small change in thinking can really improve your workflow.&lt;/p&gt;

&lt;p&gt;For example, at Viddler we use &lt;a href="http://trac.edgewall.org/"&gt;Trac&lt;/a&gt; to manage tickets, and in Git.  For each ticket in Trac, we create a branch, called something like &lt;code&gt;3241-fix-embed-codes&lt;/code&gt;.  We have two permanent branches: &lt;code&gt;dev&lt;/code&gt;, which reflects current development, and &lt;code&gt;master&lt;/code&gt;, which is considered always production-ready.  So, &lt;code&gt;dev&lt;/code&gt; and &lt;code&gt;master&lt;/code&gt; are going to have different code to reflect their reflective stability.  To get started with a fix, we first create a feature branch from &lt;code&gt;master&lt;/code&gt;:&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;git checkout -b 3241-fix-embed-codes master
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;This simply creates a new branch of &lt;code&gt;master&lt;/code&gt; called &lt;code&gt;3241-fix-embed-codes&lt;/code&gt; and checks it out. When the ticket is completed and the code has been committed, the patch thinking really comes into play.  Since this now needs to be tested in the &lt;code&gt;dev&lt;/code&gt; environment, the branch first gets applied to the &lt;code&gt;dev&lt;/code&gt; branch:&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;git checkout dev
git merge &amp;#8212;no-ff 3241-fix-embed-codes
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;Using the &lt;code&gt;&amp;#8212;no-ff&lt;/code&gt; option on &lt;code&gt;git merge&lt;/code&gt; is important for this patch mindset: it creates a separate commit for the merge itself, which allows you to &lt;code&gt;git revert&lt;/code&gt; the entire thing (if necessary), rather than having to undo each individual commit within it.&lt;/p&gt;

&lt;p&gt;Once we&amp;#8217;ve decided this fix is read for production, it&amp;#8217;s time to move the code over to &lt;code&gt;master&lt;/code&gt;.  Without the patch mindset, you might consider merging &lt;code&gt;dev&lt;/code&gt; into &lt;code&gt;master&lt;/code&gt;, but that means you&amp;#8217;d be copying anything that&amp;#8217;s applied to &lt;code&gt;dev&lt;/code&gt;, some of which might not yet be ready.  When you think of your feature branch as a patch, however, it&amp;#8217;s easy to only apply the one you need.  To apply this patch to &lt;code&gt;master&lt;/code&gt;, just use a similar method as before:&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;git checkout master
git merge &amp;amp;#45;-no-ff 3241-fix-embed-codes
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;Now you&amp;#8217;ve only moved the safe commits over, leaving any buggy code safely in &lt;code&gt;dev&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;This method may seem obvious, but the mindset has really changed the way I use Git, and I think it makes it a much more powerful tool, especially when you&amp;#8217;re working across multiple environments (like production and staging).  I&amp;#8217;ve skipped over some additional considerations, like merging &lt;code&gt;master&lt;/code&gt;/&lt;code&gt;dev&lt;/code&gt; into your feature branch, but those are topics for a future post.&lt;/p&gt;
</description>
      <content>&lt;p&gt;At &lt;a href="http://viddler.com"&gt;Viddler&lt;/a&gt;, we&amp;#8217;re now using Git for projects, and it&amp;#8217;s going really well so far. While we haven&amp;#8217;t figured out the perfect workflow just yet, we&amp;#8217;re doing some things I really like, and one of them is treating branches like patches.&lt;/p&gt;

&lt;p&gt;Often, people think of Git branches as a full copy of the parent branch, but it&amp;#8217;s better to treat them as a simple collection of new commits, to be applied to the parent branch later.  This might not seem too revolutionary, but this small change in thinking can really improve your workflow.&lt;/p&gt;

&lt;p&gt;For example, at Viddler we use &lt;a href="http://trac.edgewall.org/"&gt;Trac&lt;/a&gt; to manage tickets, and in Git.  For each ticket in Trac, we create a branch, called something like &lt;code&gt;3241-fix-embed-codes&lt;/code&gt;.  We have two permanent branches: &lt;code&gt;dev&lt;/code&gt;, which reflects current development, and &lt;code&gt;master&lt;/code&gt;, which is considered always production-ready.  So, &lt;code&gt;dev&lt;/code&gt; and &lt;code&gt;master&lt;/code&gt; are going to have different code to reflect their reflective stability.  To get started with a fix, we first create a feature branch from &lt;code&gt;master&lt;/code&gt;:&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;git checkout -b 3241-fix-embed-codes master
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;This simply creates a new branch of &lt;code&gt;master&lt;/code&gt; called &lt;code&gt;3241-fix-embed-codes&lt;/code&gt; and checks it out. When the ticket is completed and the code has been committed, the patch thinking really comes into play.  Since this now needs to be tested in the &lt;code&gt;dev&lt;/code&gt; environment, the branch first gets applied to the &lt;code&gt;dev&lt;/code&gt; branch:&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;git checkout dev
git merge &amp;#8212;no-ff 3241-fix-embed-codes
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;Using the &lt;code&gt;&amp;#8212;no-ff&lt;/code&gt; option on &lt;code&gt;git merge&lt;/code&gt; is important for this patch mindset: it creates a separate commit for the merge itself, which allows you to &lt;code&gt;git revert&lt;/code&gt; the entire thing (if necessary), rather than having to undo each individual commit within it.&lt;/p&gt;

&lt;p&gt;Once we&amp;#8217;ve decided this fix is read for production, it&amp;#8217;s time to move the code over to &lt;code&gt;master&lt;/code&gt;.  Without the patch mindset, you might consider merging &lt;code&gt;dev&lt;/code&gt; into &lt;code&gt;master&lt;/code&gt;, but that means you&amp;#8217;d be copying anything that&amp;#8217;s applied to &lt;code&gt;dev&lt;/code&gt;, some of which might not yet be ready.  When you think of your feature branch as a patch, however, it&amp;#8217;s easy to only apply the one you need.  To apply this patch to &lt;code&gt;master&lt;/code&gt;, just use a similar method as before:&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;git checkout master
git merge &amp;amp;#45;-no-ff 3241-fix-embed-codes
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;Now you&amp;#8217;ve only moved the safe commits over, leaving any buggy code safely in &lt;code&gt;dev&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;This method may seem obvious, but the mindset has really changed the way I use Git, and I think it makes it a much more powerful tool, especially when you&amp;#8217;re working across multiple environments (like production and staging).  I&amp;#8217;ve skipped over some additional considerations, like merging &lt;code&gt;master&lt;/code&gt;/&lt;code&gt;dev&lt;/code&gt; into your feature branch, but those are topics for a future post.&lt;/p&gt;
</content>
      <pubDate>Thu, 06 May 2010 00:29:00 -0400</pubDate>
      <link>http://kyleslattery.com/entries/using-git-branches-as-patches</link>
      <guid>http://kyleslattery.com/entries/using-git-branches-as-patches</guid>
    </item>
    <item>
      <title>Introducing node-xml2object</title>
      <description>&lt;p&gt;Recently, I&amp;#8217;ve been playing around with &lt;a href="http://nodejs.org"&gt;node.js&lt;/a&gt;, because, well, it&amp;#8217;s really, &lt;em&gt;really&lt;/em&gt; cool.  I found myself wanting to parse XML into a Javascript object, but I couldn&amp;#8217;t find any existing modules that did what I needed.  So, I went ahead and made my own, &lt;a href="http://github.com/kyleslattery/node-xml2object"&gt;node-xml2object&lt;/a&gt;.  It&amp;#8217;s dead simple to use, and I hope others find it as useful as I have.  For example, imagine you have some XML like this:&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;&amp;lt;root&amp;gt;
  &amp;lt;videos total="2"&amp;gt;
    &amp;lt;video id="1" length="20"&amp;gt;
      &amp;lt;title&amp;gt;Video 1&amp;lt;/title&amp;gt;
    &amp;lt;/video&amp;gt;
    &amp;lt;video&amp;gt;
      &amp;lt;title&amp;gt;Video 2&amp;lt;/title&amp;gt;
    &amp;lt;/video&amp;gt;
  &amp;lt;/videos&amp;gt;
&amp;lt;/root&amp;gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;Here&amp;#8217;s how you would access it as an object, assuming the XML is stored in the variable &lt;code&gt;xml&lt;/code&gt;:&lt;/p&gt;

&lt;pre&gt;&lt;code class="js"&gt;var sys        = require('sys'),
    xml2object = require('./xml2object');

var response   = xml2object.parseString(xml);

response.addCallback(function(obj) {

  // To output a simple value, just use as an object
  // Since there are 2 video elements under &amp;lt;videos&amp;gt;, it's stored as an array
  sys.puts(obj.root.videos.video[1].title) // outputs "Video 2"

  // attr(key) returns the value for attribute with name "key"
  sys.puts(obj.root.videos.attr("total")) // outputs "2"

  // attrs() returns an object of all attributes
  sys.puts(obj.root.videos.video[0].attrs().id); // outputs "1"
});
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;Node-xml2object depends on the &lt;a href="http://github.com/robrighter/node-xml"&gt;node-xml&lt;/a&gt; module, and the tests use &lt;a href="http://github.com/technoweenie/ntest"&gt;ntest&lt;/a&gt;.  Right now, I&amp;#8217;m embedding them as Git submodules, but I&amp;#8217;m planning on changing that in the near future, since it&amp;#8217;s pretty messy this way.  The source code is licensed under the &lt;a href="http://en.wikipedia.org/wiki/MIT_License"&gt;MIT license&lt;/a&gt;, so feel free to use it however you&amp;#8217;d like.  I&amp;#8217;m pretty happy with where it&amp;#8217;s at now, but if anyone would like to fork it and improve things, go for it!&lt;/p&gt;
</description>
      <content>&lt;p&gt;Recently, I&amp;#8217;ve been playing around with &lt;a href="http://nodejs.org"&gt;node.js&lt;/a&gt;, because, well, it&amp;#8217;s really, &lt;em&gt;really&lt;/em&gt; cool.  I found myself wanting to parse XML into a Javascript object, but I couldn&amp;#8217;t find any existing modules that did what I needed.  So, I went ahead and made my own, &lt;a href="http://github.com/kyleslattery/node-xml2object"&gt;node-xml2object&lt;/a&gt;.  It&amp;#8217;s dead simple to use, and I hope others find it as useful as I have.  For example, imagine you have some XML like this:&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;&amp;lt;root&amp;gt;
  &amp;lt;videos total="2"&amp;gt;
    &amp;lt;video id="1" length="20"&amp;gt;
      &amp;lt;title&amp;gt;Video 1&amp;lt;/title&amp;gt;
    &amp;lt;/video&amp;gt;
    &amp;lt;video&amp;gt;
      &amp;lt;title&amp;gt;Video 2&amp;lt;/title&amp;gt;
    &amp;lt;/video&amp;gt;
  &amp;lt;/videos&amp;gt;
&amp;lt;/root&amp;gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;Here&amp;#8217;s how you would access it as an object, assuming the XML is stored in the variable &lt;code&gt;xml&lt;/code&gt;:&lt;/p&gt;

&lt;pre&gt;&lt;code class="js"&gt;var sys        = require('sys'),
    xml2object = require('./xml2object');

var response   = xml2object.parseString(xml);

response.addCallback(function(obj) {

  // To output a simple value, just use as an object
  // Since there are 2 video elements under &amp;lt;videos&amp;gt;, it's stored as an array
  sys.puts(obj.root.videos.video[1].title) // outputs "Video 2"

  // attr(key) returns the value for attribute with name "key"
  sys.puts(obj.root.videos.attr("total")) // outputs "2"

  // attrs() returns an object of all attributes
  sys.puts(obj.root.videos.video[0].attrs().id); // outputs "1"
});
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;Node-xml2object depends on the &lt;a href="http://github.com/robrighter/node-xml"&gt;node-xml&lt;/a&gt; module, and the tests use &lt;a href="http://github.com/technoweenie/ntest"&gt;ntest&lt;/a&gt;.  Right now, I&amp;#8217;m embedding them as Git submodules, but I&amp;#8217;m planning on changing that in the near future, since it&amp;#8217;s pretty messy this way.  The source code is licensed under the &lt;a href="http://en.wikipedia.org/wiki/MIT_License"&gt;MIT license&lt;/a&gt;, so feel free to use it however you&amp;#8217;d like.  I&amp;#8217;m pretty happy with where it&amp;#8217;s at now, but if anyone would like to fork it and improve things, go for it!&lt;/p&gt;
</content>
      <pubDate>Wed, 10 Feb 2010 21:47:00 -0500</pubDate>
      <link>http://kyleslattery.com/entries/introducing-node-xml2object</link>
      <guid>http://kyleslattery.com/entries/introducing-node-xml2object</guid>
    </item>
    <item>
      <title>iPad Keyboard Fragmentation</title>
      <description>&lt;p&gt;As usual, I enjoyed reading &lt;a href="http://daringfireball.net/2010/01/various_ipad_thoughts"&gt;John Gruber&amp;#8217;s musings on the iPad&lt;/a&gt;. I was intrigued most, however, by his thoughts on the iPad&amp;#8217;s support for hardware keyboards:&lt;/p&gt;

&lt;blockquote&gt;&lt;p&gt;Having used the hardware keyboard yesterday, though, it is clearly a secondary form of input. You cannot even vaguely drive the iPad interface by keyboard alone. [&amp;#8230;] There are some glaring holes. For example, in iPad Mail, when you start typing in the To: field to address a message, and the iPhone-style autocomplete suggestion list appears under the field, you cannot select from it using the keyboard. You have to touch the screen. [&amp;#8230;] It just seems like it’s not finished yet.&lt;/p&gt;&lt;/blockquote&gt;

&lt;p&gt;What struck me as interesting is how similar this is to Android development&amp;#8217;s biggest drawback: hardware fragmentation. Due to the vast array of Android devices, app developers have to consciously design their apps to support several different input methods. This may seem fairly trivial, but it is actually a fairly significant problem, especially for games and more complex UIs. In the Android Market, some games and applications just don&amp;#8217;t work without a hardware keyboard, likely because the app developer didn&amp;#8217;t take the time to consider touchscreen input. Apple seems to now be encountering this problem as well.&lt;/p&gt;

&lt;p&gt;Maybe John&amp;#8217;s right, and this is a rough edge that will be smoothed over when the device launches. But, there&amp;#8217;s more to it than just fixing iPad Mail. What about the 140,000 apps in the App Store? They certainly weren&amp;#8217;t built with any considerations of keyboard navigation, so the experience is likely to be sub-par, unless the developer has taken the time to support an input method that he or she might not even have access to (assuming he or she doesn&amp;#8217;t own an iPad).&lt;/p&gt;

&lt;p&gt;If keyboard support is &lt;em&gt;solely&lt;/em&gt; intended for text input, as John suggests, a lot of users will be confused.  Standard  operations, such as tabbing between fields and navigating via arrow keys, are expected behavior when a user is given a keyboard. It would be frustrating to have that taken away. Certainly, Apple could fix this issue by the time the iPad hits the market, but for a platform that preaches ease-of-development due to the uniformity of its hardware, it&amp;#8217;ll be interesting to see how it&amp;#8217;s handled.&lt;/p&gt;
</description>
      <content>&lt;p&gt;As usual, I enjoyed reading &lt;a href="http://daringfireball.net/2010/01/various_ipad_thoughts"&gt;John Gruber&amp;#8217;s musings on the iPad&lt;/a&gt;. I was intrigued most, however, by his thoughts on the iPad&amp;#8217;s support for hardware keyboards:&lt;/p&gt;

&lt;blockquote&gt;&lt;p&gt;Having used the hardware keyboard yesterday, though, it is clearly a secondary form of input. You cannot even vaguely drive the iPad interface by keyboard alone. [&amp;#8230;] There are some glaring holes. For example, in iPad Mail, when you start typing in the To: field to address a message, and the iPhone-style autocomplete suggestion list appears under the field, you cannot select from it using the keyboard. You have to touch the screen. [&amp;#8230;] It just seems like it’s not finished yet.&lt;/p&gt;&lt;/blockquote&gt;

&lt;p&gt;What struck me as interesting is how similar this is to Android development&amp;#8217;s biggest drawback: hardware fragmentation. Due to the vast array of Android devices, app developers have to consciously design their apps to support several different input methods. This may seem fairly trivial, but it is actually a fairly significant problem, especially for games and more complex UIs. In the Android Market, some games and applications just don&amp;#8217;t work without a hardware keyboard, likely because the app developer didn&amp;#8217;t take the time to consider touchscreen input. Apple seems to now be encountering this problem as well.&lt;/p&gt;

&lt;p&gt;Maybe John&amp;#8217;s right, and this is a rough edge that will be smoothed over when the device launches. But, there&amp;#8217;s more to it than just fixing iPad Mail. What about the 140,000 apps in the App Store? They certainly weren&amp;#8217;t built with any considerations of keyboard navigation, so the experience is likely to be sub-par, unless the developer has taken the time to support an input method that he or she might not even have access to (assuming he or she doesn&amp;#8217;t own an iPad).&lt;/p&gt;

&lt;p&gt;If keyboard support is &lt;em&gt;solely&lt;/em&gt; intended for text input, as John suggests, a lot of users will be confused.  Standard  operations, such as tabbing between fields and navigating via arrow keys, are expected behavior when a user is given a keyboard. It would be frustrating to have that taken away. Certainly, Apple could fix this issue by the time the iPad hits the market, but for a platform that preaches ease-of-development due to the uniformity of its hardware, it&amp;#8217;ll be interesting to see how it&amp;#8217;s handled.&lt;/p&gt;
</content>
      <pubDate>Thu, 28 Jan 2010 23:54:00 -0500</pubDate>
      <link>http://kyleslattery.com/entries/ipad-keyboard-fragmentation</link>
      <guid>http://kyleslattery.com/entries/ipad-keyboard-fragmentation</guid>
    </item>
    <item>
      <title>Three Weeks with Swype</title>
      <description>&lt;p&gt;Roughly three weeks ago, I stumbled across the &lt;a href="http://kyleslattery.com/links/swype-keyboard-preview-leaked"&gt;Swype for Android preview&lt;/a&gt;. My first impressions were very positive, and over the last three weeks, I&amp;#8217;ve been using Swype exclusively on my Droid. If you&amp;#8217;re not familiar with Swype, it&amp;#8217;s a new method of typing on a touchscreen device, from the inventor of T9. Instead of tapping out each letter, you just drag your finger across the keyboard, hitting each of the letters in the word in sequence. I recorded a short video showing its basic features:&lt;/p&gt;

&lt;p&gt;&lt;object classid="clsid:D27CDB6E-AE6D-11cf-96B8-444553540000" width="545" height="451" id="viddler_d23f1ea7"&gt;&lt;param name="movie" value="http://www.viddler.com/player/d23f1ea7/" /&gt;&lt;param name="allowScriptAccess" value="always" /&gt;&lt;param name="allowFullScreen" value="true" /&gt;&lt;embed src="http://www.viddler.com/player/d23f1ea7/" width="545" height="451" type="application/x-shockwave-flash" allowScriptAccess="always" allowFullScreen="true" name="viddler_d23f1ea7"&gt;&lt;/embed&gt;&lt;/object&gt;&lt;/p&gt;

&lt;h2&gt;What I Liked&lt;/h2&gt;

&lt;p&gt;What really surprised me about Swype was how quickly I was able to get the hang of it.  When I watched demo videos, it seemed like it was overly-complicated, but once I used it myself, &lt;a href="http://twitter.com/kyleslattery/status/7257511910"&gt;I couldn&amp;#8217;t believe&lt;/a&gt; how simple it was. For some of the less intuitive features (such as capitalization, which requires you swipe above the keyboard), I had to run through the built-in tutorial, but after that, I could swipe very easily.  One big perk with Swype is that it&amp;#8217;s actually pretty easy to use without looking, once you get the hang of it.  I found that I could type reasonably well without ever looking at my fingers, something I would have no chance of doing with the standard keyboard.&lt;/p&gt;

&lt;h2&gt;Drawbacks&lt;/h2&gt;

&lt;p&gt;My biggest gripe with Swype is it&amp;#8217;s just nowhere near as polished as the built-in keyboard.  For starters, it&amp;#8217;s much more difficult to access symbols, and numbers are laid out pretty strangely. Also, there&amp;#8217;s a complete lack on auto-completion for text fields that support it.  For instance, if you&amp;#8217;re typing the name of a contact in landscape view, the regular Android keyboard will have suggestions below the field that will allow you to select the person without typing their whole name. Swype does not seem to support this at all, which is really pretty annoying.  In addition, it&amp;#8217;s difficult to type out words normally, if they do not exist in the Swype dictionary.  When tapping out letters, Swype tends to interpret your taps as swipes, adding unnecessary spaces between letters.  Once you type an unknown word the first time, however, you never have to do it again, because it gets added to the dictionary, which is pretty nice.&lt;/p&gt;

&lt;p&gt;In addition, I&amp;#8217;ve found that the sensitivity of the keyboard can be a little strange when swiping quickly. Many times, it won&amp;#8217;t recognize that you&amp;#8217;ve stopped a word and started a new one (which is indicated by lifting your finger up), so it won&amp;#8217;t recognize what you&amp;#8217;re trying to input at all.  In the options panel, there are ways to adjust the sensitivity, but I&amp;#8217;d rather these settings were hidden away from users, because it seems like a bit of a cop-out.  Swype should instead learn how you type, adjusting its algorithm as necessary.&lt;/p&gt;

&lt;h2&gt;Conclusions&lt;/h2&gt;

&lt;p&gt;While there certainly are some issues with the Swype build I used for this review, pretty much all of problems are fixable, and I&amp;#8217;m excited to see what the final version turns out to be.  They&amp;#8217;ve got a really great concept that, with tweaking, could become the best way to input text on a touchscreen mobile device, especially for those who aren&amp;#8217;t very adept with traditional on-screen keyboards.  I&amp;#8217;m not sure what Swype&amp;#8217;s strategy is going be with regards to pricing: whether it will be a free app, or if you&amp;#8217;ll have to pay for it.  Personally, if they&amp;#8217;re able to fix my issues above, I would be happy to pay $5-6 for it, and maybe even more, because it really is quite incredible.  Even if you can type well with the standard keyboard, definitely give Swype a try.&lt;/p&gt;
</description>
      <content>&lt;p&gt;Roughly three weeks ago, I stumbled across the &lt;a href="http://kyleslattery.com/links/swype-keyboard-preview-leaked"&gt;Swype for Android preview&lt;/a&gt;. My first impressions were very positive, and over the last three weeks, I&amp;#8217;ve been using Swype exclusively on my Droid. If you&amp;#8217;re not familiar with Swype, it&amp;#8217;s a new method of typing on a touchscreen device, from the inventor of T9. Instead of tapping out each letter, you just drag your finger across the keyboard, hitting each of the letters in the word in sequence. I recorded a short video showing its basic features:&lt;/p&gt;

&lt;p&gt;&lt;object classid="clsid:D27CDB6E-AE6D-11cf-96B8-444553540000" width="545" height="451" id="viddler_d23f1ea7"&gt;&lt;param name="movie" value="http://www.viddler.com/player/d23f1ea7/" /&gt;&lt;param name="allowScriptAccess" value="always" /&gt;&lt;param name="allowFullScreen" value="true" /&gt;&lt;embed src="http://www.viddler.com/player/d23f1ea7/" width="545" height="451" type="application/x-shockwave-flash" allowScriptAccess="always" allowFullScreen="true" name="viddler_d23f1ea7"&gt;&lt;/embed&gt;&lt;/object&gt;&lt;/p&gt;

&lt;h2&gt;What I Liked&lt;/h2&gt;

&lt;p&gt;What really surprised me about Swype was how quickly I was able to get the hang of it.  When I watched demo videos, it seemed like it was overly-complicated, but once I used it myself, &lt;a href="http://twitter.com/kyleslattery/status/7257511910"&gt;I couldn&amp;#8217;t believe&lt;/a&gt; how simple it was. For some of the less intuitive features (such as capitalization, which requires you swipe above the keyboard), I had to run through the built-in tutorial, but after that, I could swipe very easily.  One big perk with Swype is that it&amp;#8217;s actually pretty easy to use without looking, once you get the hang of it.  I found that I could type reasonably well without ever looking at my fingers, something I would have no chance of doing with the standard keyboard.&lt;/p&gt;

&lt;h2&gt;Drawbacks&lt;/h2&gt;

&lt;p&gt;My biggest gripe with Swype is it&amp;#8217;s just nowhere near as polished as the built-in keyboard.  For starters, it&amp;#8217;s much more difficult to access symbols, and numbers are laid out pretty strangely. Also, there&amp;#8217;s a complete lack on auto-completion for text fields that support it.  For instance, if you&amp;#8217;re typing the name of a contact in landscape view, the regular Android keyboard will have suggestions below the field that will allow you to select the person without typing their whole name. Swype does not seem to support this at all, which is really pretty annoying.  In addition, it&amp;#8217;s difficult to type out words normally, if they do not exist in the Swype dictionary.  When tapping out letters, Swype tends to interpret your taps as swipes, adding unnecessary spaces between letters.  Once you type an unknown word the first time, however, you never have to do it again, because it gets added to the dictionary, which is pretty nice.&lt;/p&gt;

&lt;p&gt;In addition, I&amp;#8217;ve found that the sensitivity of the keyboard can be a little strange when swiping quickly. Many times, it won&amp;#8217;t recognize that you&amp;#8217;ve stopped a word and started a new one (which is indicated by lifting your finger up), so it won&amp;#8217;t recognize what you&amp;#8217;re trying to input at all.  In the options panel, there are ways to adjust the sensitivity, but I&amp;#8217;d rather these settings were hidden away from users, because it seems like a bit of a cop-out.  Swype should instead learn how you type, adjusting its algorithm as necessary.&lt;/p&gt;

&lt;h2&gt;Conclusions&lt;/h2&gt;

&lt;p&gt;While there certainly are some issues with the Swype build I used for this review, pretty much all of problems are fixable, and I&amp;#8217;m excited to see what the final version turns out to be.  They&amp;#8217;ve got a really great concept that, with tweaking, could become the best way to input text on a touchscreen mobile device, especially for those who aren&amp;#8217;t very adept with traditional on-screen keyboards.  I&amp;#8217;m not sure what Swype&amp;#8217;s strategy is going be with regards to pricing: whether it will be a free app, or if you&amp;#8217;ll have to pay for it.  Personally, if they&amp;#8217;re able to fix my issues above, I would be happy to pay $5-6 for it, and maybe even more, because it really is quite incredible.  Even if you can type well with the standard keyboard, definitely give Swype a try.&lt;/p&gt;
</content>
      <pubDate>Fri, 15 Jan 2010 21:26:00 -0500</pubDate>
      <link>http://kyleslattery.com/entries/three-weeks-with-swype</link>
      <guid>http://kyleslattery.com/entries/three-weeks-with-swype</guid>
    </item>
    <item>
      <title>A Geotagged Vacation</title>
      <description>&lt;p&gt;Over the holidays, my family and I took a three-day trip to Chicago, and because I had left my charger for my Canon Powershot G9 at home, I was stuck taking pictures with my Droid&amp;#8217;s built-in camera.  I thought it would turn out to be a disappointing endeavor, but it actually got me really excited about the future of internet connected cameras.&lt;/p&gt;

&lt;p&gt;My least favorite part of photography has always been all of the work afterwards to get it online. I have to take out the SD card, copy the files to my computer, edit them, and then post them to Flickr.  When I was taking photos with my Droid in Chicago, I loved how easy it was to instantly post photos online.  Using &lt;a href="http://twidroid.com"&gt;Twidroid&lt;/a&gt;, I was able to easily upload photos to yFrog, tweet the link, and have it all geotagged, almost instantly. It allowed me to share what I was doing with my friends as I was doing it, something I could never do with my G9.&lt;/p&gt;

&lt;p&gt;The quality of the photos certainly isn&amp;#8217;t anywhere near what I could accomplish with a better camera, but that really didn&amp;#8217;t bother me too much. As the quality of phone cameras improves, this will become less and less of an issue. Since I had a limited amount of time in the city, I was more concerned with sharing snapshots of where I was (like &lt;a href="http://twitter.com/kyleslattery/status/7135552066"&gt;at the &amp;#8220;Bean&amp;#8221;&lt;/a&gt;) than with creating works of art.  On a longer trip, where I have the ability to take my time and set up shots, I&amp;#8217;m sure I would want to have a nicer camera along, but for a quick 2.5-day trip to Chicago, my phone worked pretty well.&lt;/p&gt;

&lt;p&gt;There are certainly areas where this method of taking photos could improve, however. Like I said before, the quality of the camera is a drawback, but it was one I was willing to accept. Beyond that, I was a little disappointed by the geotagging abilities of Twidroid. If I took a picture of something inside (like the &lt;a href="http://twitter.com/kyleslattery/status/7170275308"&gt;Apollo 8 command module&lt;/a&gt;, cool!), the coordinates would be way off, since it couldn&amp;#8217;t get a good GPS signal. I would have preferred to be able to pick the &lt;a href="http://www.msichicago.org/"&gt;Museum of Science and Industry&lt;/a&gt; as my location, rather than relying on what the GPS picked out for me.  This is something that appears it would be relatively simple to fix. The &lt;a href="http://www.androlib.com/android.application.com-twidroidplugins-maps-xptF.aspx"&gt;Google Maps 4 Twidroid&lt;/a&gt; source code is &lt;a href="http://code.google.com/p/android-twitter-googlemaps/"&gt;available online&lt;/a&gt;, so I might try and update it myself to use places (using Google&amp;#8217;s local search API or something similar), or at the very least it could allow you to check back into a previous location.  I am also hoping Twitter releases more features related to geotagging on their site.  Right now, coordinates are only visible through the API, so unless you&amp;#8217;re viewing my tweets in a geo-enabled Twitter client, you won&amp;#8217;t see my location.&lt;/p&gt;

&lt;p&gt;I certainly could use a service such as &lt;a href="http://brightkite.com"&gt;Brightkite&lt;/a&gt; to fix some of the issues above, but I really want to stick to just using Twitter. It&amp;#8217;s become clear to me that Brightkite really isn&amp;#8217;t pushing the envelope any more as a service, and there really is little incentive to post there. I definitely agree with &lt;a href="http://cdevroe.com/notes/what-brightkite-should-be/"&gt;Colin Devroe&lt;/a&gt; that their future should be to act as a Twitter client, rather than as their own community, since I really don&amp;#8217;t see them being very successful otherwise.&lt;/p&gt;

&lt;p&gt;Clearly, the future is bright for geotagging, and I can&amp;#8217;t wait for the day when I can have a high-quality camera in my pocket that allows me to do everything my Droid does on vacation. While I&amp;#8217;m not someone who likes broadcasting my location constantly, it&amp;#8217;s a nice ability to have while travelling, to both share your trip with others, and to be able to look back at what you did. Hopefully the tools will continue to improve, and they&amp;#8217;ll be more and more universal, because I think this is something everyone could enjoy.&lt;/p&gt;
</description>
      <content>&lt;p&gt;Over the holidays, my family and I took a three-day trip to Chicago, and because I had left my charger for my Canon Powershot G9 at home, I was stuck taking pictures with my Droid&amp;#8217;s built-in camera.  I thought it would turn out to be a disappointing endeavor, but it actually got me really excited about the future of internet connected cameras.&lt;/p&gt;

&lt;p&gt;My least favorite part of photography has always been all of the work afterwards to get it online. I have to take out the SD card, copy the files to my computer, edit them, and then post them to Flickr.  When I was taking photos with my Droid in Chicago, I loved how easy it was to instantly post photos online.  Using &lt;a href="http://twidroid.com"&gt;Twidroid&lt;/a&gt;, I was able to easily upload photos to yFrog, tweet the link, and have it all geotagged, almost instantly. It allowed me to share what I was doing with my friends as I was doing it, something I could never do with my G9.&lt;/p&gt;

&lt;p&gt;The quality of the photos certainly isn&amp;#8217;t anywhere near what I could accomplish with a better camera, but that really didn&amp;#8217;t bother me too much. As the quality of phone cameras improves, this will become less and less of an issue. Since I had a limited amount of time in the city, I was more concerned with sharing snapshots of where I was (like &lt;a href="http://twitter.com/kyleslattery/status/7135552066"&gt;at the &amp;#8220;Bean&amp;#8221;&lt;/a&gt;) than with creating works of art.  On a longer trip, where I have the ability to take my time and set up shots, I&amp;#8217;m sure I would want to have a nicer camera along, but for a quick 2.5-day trip to Chicago, my phone worked pretty well.&lt;/p&gt;

&lt;p&gt;There are certainly areas where this method of taking photos could improve, however. Like I said before, the quality of the camera is a drawback, but it was one I was willing to accept. Beyond that, I was a little disappointed by the geotagging abilities of Twidroid. If I took a picture of something inside (like the &lt;a href="http://twitter.com/kyleslattery/status/7170275308"&gt;Apollo 8 command module&lt;/a&gt;, cool!), the coordinates would be way off, since it couldn&amp;#8217;t get a good GPS signal. I would have preferred to be able to pick the &lt;a href="http://www.msichicago.org/"&gt;Museum of Science and Industry&lt;/a&gt; as my location, rather than relying on what the GPS picked out for me.  This is something that appears it would be relatively simple to fix. The &lt;a href="http://www.androlib.com/android.application.com-twidroidplugins-maps-xptF.aspx"&gt;Google Maps 4 Twidroid&lt;/a&gt; source code is &lt;a href="http://code.google.com/p/android-twitter-googlemaps/"&gt;available online&lt;/a&gt;, so I might try and update it myself to use places (using Google&amp;#8217;s local search API or something similar), or at the very least it could allow you to check back into a previous location.  I am also hoping Twitter releases more features related to geotagging on their site.  Right now, coordinates are only visible through the API, so unless you&amp;#8217;re viewing my tweets in a geo-enabled Twitter client, you won&amp;#8217;t see my location.&lt;/p&gt;

&lt;p&gt;I certainly could use a service such as &lt;a href="http://brightkite.com"&gt;Brightkite&lt;/a&gt; to fix some of the issues above, but I really want to stick to just using Twitter. It&amp;#8217;s become clear to me that Brightkite really isn&amp;#8217;t pushing the envelope any more as a service, and there really is little incentive to post there. I definitely agree with &lt;a href="http://cdevroe.com/notes/what-brightkite-should-be/"&gt;Colin Devroe&lt;/a&gt; that their future should be to act as a Twitter client, rather than as their own community, since I really don&amp;#8217;t see them being very successful otherwise.&lt;/p&gt;

&lt;p&gt;Clearly, the future is bright for geotagging, and I can&amp;#8217;t wait for the day when I can have a high-quality camera in my pocket that allows me to do everything my Droid does on vacation. While I&amp;#8217;m not someone who likes broadcasting my location constantly, it&amp;#8217;s a nice ability to have while travelling, to both share your trip with others, and to be able to look back at what you did. Hopefully the tools will continue to improve, and they&amp;#8217;ll be more and more universal, because I think this is something everyone could enjoy.&lt;/p&gt;
</content>
      <pubDate>Sat, 09 Jan 2010 10:44:00 -0500</pubDate>
      <link>http://kyleslattery.com/entries/geotagged-vacation</link>
      <guid>http://kyleslattery.com/entries/geotagged-vacation</guid>
    </item>
    <item>
      <title>Easy Deploys to Multiple Environments with Capistrano</title>
      <description>&lt;p&gt;At &lt;a href="http://viddler.com"&gt;Viddler&lt;/a&gt;, we&amp;#8217;re switching to using Git and &lt;a href="http://capify.org"&gt;Capistrano&lt;/a&gt; for our internal projects, and I was tasked with setting up the system to deploy our code to both staging and production environments. Capistrano doesn&amp;#8217;t have built-in environment switching, but it&amp;#8217;s dead simple to add it yourself.&lt;/p&gt;

&lt;p&gt;The key to getting this to work is to utilize Capistrano&amp;#8217;s ability to chain tasks&amp;#8211;if you run &lt;code&gt;cap task1 task2&lt;/code&gt;, it will run the two commands in the order you listed them. Variables are shared between the tasks, so if you set a variable in &lt;code&gt;task1&lt;/code&gt;, &lt;code&gt;task2&lt;/code&gt; will be able to access it.  To get multiple environments working, you just create tasks to set environment variables, which you call before the actual task you want to run.&lt;/p&gt;

&lt;p&gt;The first step is to figure out which variables you need to change, and then move them into their own tasks. Here&amp;#8217;s an example of the tasks we added:&lt;/p&gt;

&lt;pre&gt;&lt;code class="ruby"&gt;desc "Run on staging server"
task :staging do
  server "staging.myserver.com", :app, :web, :db, :primary =&amp;gt; true
end

desc "Run on production server"
task :production do
  server "myserver.com", :app, :web, :db, :primary =&amp;gt; true
end
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;Now, if you want to deploy to staging, just run &lt;code&gt;cap staging deploy&lt;/code&gt;, and for production run &lt;code&gt;cap production deploy&lt;/code&gt;. Easy.&lt;/p&gt;
</description>
      <content>&lt;p&gt;At &lt;a href="http://viddler.com"&gt;Viddler&lt;/a&gt;, we&amp;#8217;re switching to using Git and &lt;a href="http://capify.org"&gt;Capistrano&lt;/a&gt; for our internal projects, and I was tasked with setting up the system to deploy our code to both staging and production environments. Capistrano doesn&amp;#8217;t have built-in environment switching, but it&amp;#8217;s dead simple to add it yourself.&lt;/p&gt;

&lt;p&gt;The key to getting this to work is to utilize Capistrano&amp;#8217;s ability to chain tasks&amp;#8211;if you run &lt;code&gt;cap task1 task2&lt;/code&gt;, it will run the two commands in the order you listed them. Variables are shared between the tasks, so if you set a variable in &lt;code&gt;task1&lt;/code&gt;, &lt;code&gt;task2&lt;/code&gt; will be able to access it.  To get multiple environments working, you just create tasks to set environment variables, which you call before the actual task you want to run.&lt;/p&gt;

&lt;p&gt;The first step is to figure out which variables you need to change, and then move them into their own tasks. Here&amp;#8217;s an example of the tasks we added:&lt;/p&gt;

&lt;pre&gt;&lt;code class="ruby"&gt;desc "Run on staging server"
task :staging do
  server "staging.myserver.com", :app, :web, :db, :primary =&amp;gt; true
end

desc "Run on production server"
task :production do
  server "myserver.com", :app, :web, :db, :primary =&amp;gt; true
end
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;Now, if you want to deploy to staging, just run &lt;code&gt;cap staging deploy&lt;/code&gt;, and for production run &lt;code&gt;cap production deploy&lt;/code&gt;. Easy.&lt;/p&gt;
</content>
      <pubDate>Thu, 10 Dec 2009 15:17:00 -0500</pubDate>
      <link>http://kyleslattery.com/entries/easy-deploys-to-multiple-environments-with-capistrano</link>
      <guid>http://kyleslattery.com/entries/easy-deploys-to-multiple-environments-with-capistrano</guid>
    </item>
    <item>
      <title>The Perfect Git Workflow for a One Person Project</title>
      <description>&lt;p&gt;A few months ago, I started investigating &lt;a href="http://git-scm.com/"&gt;Git&lt;/a&gt;, and I fell in love with how much easier it made managing my code.  I&amp;#8217;m managing the source code to this site with Git, and along the way I&amp;#8217;ve come up with a pretty good workflow for myself.  The basic steps are:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Branch&lt;/li&gt;
&lt;li&gt;Commit&lt;/li&gt;
&lt;li&gt;Rebase&lt;/li&gt;
&lt;li&gt;Merge&lt;/li&gt;
&lt;li&gt;Deploy&lt;/li&gt;
&lt;/ol&gt;


&lt;p&gt;Let&amp;#8217;s go through each one to see how it all works together.&lt;/p&gt;

&lt;h2&gt;Branch&lt;/h2&gt;

&lt;p&gt;With SVN, I found myself hating branching&amp;#8211;it was always a complicated procedure, and I could never remember how to do it.  In Git it&amp;#8217;s as easy as &lt;code&gt;git checkout -b &amp;lt;branch-name&amp;gt;&lt;/code&gt;, and you&amp;#8217;re ready to go.  Once you have a branch, you can modify it however you want, and you don&amp;#8217;t have to worry about interfering with the master branch.  In order to keep your master branch bug free, &lt;strong&gt;commit only to branches, not to master itself&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;In addition to creating new branches for major features, I always have a few branches around that I pop into for certain things:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&amp;#8220;design&amp;#8221; - any changes I want to make to the design go here&lt;/li&gt;
&lt;li&gt;&amp;#8220;optimize&amp;#8221; - optimizations to the site&lt;/li&gt;
&lt;li&gt;&amp;#8220;bugfixes&amp;#8221; - a place to work on minor bugfixes&lt;/li&gt;
&lt;/ul&gt;


&lt;p&gt;Having these branches allows me to make small fixes to the site, and if it turns out it&amp;#8217;s more than just a small fix, it doesn&amp;#8217;t interfere with anything else.&lt;/p&gt;

&lt;h2&gt;Commit&lt;/h2&gt;

&lt;p&gt;In the Subversion world, it&amp;#8217;s a pretty common practice to make very large commits, consisting of many changes.  With Git, &lt;strong&gt;you should constantly be committing&lt;/strong&gt;.  By making many commits, you make it easier to find bugs you may have introduced, and it makes it a lot easier to track your progress.  If you don&amp;#8217;t like the thought of wading through long lists of commits in your logs, don&amp;#8217;t worry&amp;#8211;before bringing it over to the master branch, you can consolidate things with &lt;a href="http://book.git-scm.com/4_interactive_rebasing.html"&gt;interactive rebasing&lt;/a&gt;, but while you&amp;#8217;re hacking away on a branch, it really is advantageous to have many small commits.&lt;/p&gt;

&lt;h2&gt;Rebase&lt;/h2&gt;

&lt;p&gt;Rebasing is one of the harder things to grasp when you&amp;#8217;re first learning about Git. For in-depth coverage of the topic, check out the &lt;a href="http://book.git-scm.com/4_rebasing.html"&gt;Rebasing page&lt;/a&gt; in the &lt;a href="http://book.git-scm.com/"&gt;Git Community Book&lt;/a&gt;.  In a nutshell, doing &lt;code&gt;git rebase master&lt;/code&gt; takes any commits to &lt;code&gt;master&lt;/code&gt; and inserts them into your current branch, so you can then make sure your new code still works, and it&amp;#8217;s a lot less hazardous than doing a merge.  Rebasing your branch before putting into &lt;code&gt;master&lt;/code&gt; is really important because &lt;strong&gt;it allows you to deal with any merge issues before the code goes to your main branch&lt;/strong&gt;. To rebase, just run &lt;code&gt;git rebase master&lt;/code&gt;.&lt;/p&gt;

&lt;h2&gt;Merge&lt;/h2&gt;

&lt;p&gt;After rebasing the branch, it&amp;#8217;s safe to merge it into master.  Since I&amp;#8217;ve already dealt with any merge issues with the previous step, it&amp;#8217;s as simple as checking out the master branch and running &lt;code&gt;git merge &amp;lt;branch-name&amp;gt;&lt;/code&gt;&lt;/p&gt;

&lt;h2&gt;Deploy&lt;/h2&gt;

&lt;p&gt;I use &lt;a href="http://capify.org"&gt;Capistrano&lt;/a&gt; to deploy my code, so I&amp;#8217;m constantly typing &lt;code&gt;git push&lt;/code&gt; followed by &lt;code&gt;cap deploy&lt;/code&gt; to deploy changes to my server.  To make it easier, I just put both commands into one git alias:&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;deploy = !git push &amp;amp;&amp;amp; cap deploy
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;Now I just need to run &lt;code&gt;git deploy&lt;/code&gt; and it automatically pushes all of my changes to the remote Git repository and then deploys the site using Capistrano.  Here&amp;#8217;s &lt;a href="http://git.or.cz/gitwiki/Aliases"&gt;some more information about Git aliases&lt;/a&gt;.&lt;/p&gt;

&lt;h2&gt;Useful tools&lt;/h2&gt;

&lt;p&gt;Though I primarily use Git through the command line, I really like using &lt;a href="http://gitx.frim.nl/"&gt;GitX&lt;/a&gt; to visualize branches.  To host my repositories on my server, I use &lt;a href="http://scie.nti.st/2007/11/14/hosting-git-repositories-the-easy-and-secure-way"&gt;Gitosis&lt;/a&gt;, though if I had a few more projects, I&amp;#8217;d dole out the money for a paid account at &lt;a href="http://github.com"&gt;GitHub&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Have a great Git workflow? Think mine&amp;#8217;s terrible? Let me know in the comments!&lt;/p&gt;
</description>
      <content>&lt;p&gt;A few months ago, I started investigating &lt;a href="http://git-scm.com/"&gt;Git&lt;/a&gt;, and I fell in love with how much easier it made managing my code.  I&amp;#8217;m managing the source code to this site with Git, and along the way I&amp;#8217;ve come up with a pretty good workflow for myself.  The basic steps are:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Branch&lt;/li&gt;
&lt;li&gt;Commit&lt;/li&gt;
&lt;li&gt;Rebase&lt;/li&gt;
&lt;li&gt;Merge&lt;/li&gt;
&lt;li&gt;Deploy&lt;/li&gt;
&lt;/ol&gt;


&lt;p&gt;Let&amp;#8217;s go through each one to see how it all works together.&lt;/p&gt;

&lt;h2&gt;Branch&lt;/h2&gt;

&lt;p&gt;With SVN, I found myself hating branching&amp;#8211;it was always a complicated procedure, and I could never remember how to do it.  In Git it&amp;#8217;s as easy as &lt;code&gt;git checkout -b &amp;lt;branch-name&amp;gt;&lt;/code&gt;, and you&amp;#8217;re ready to go.  Once you have a branch, you can modify it however you want, and you don&amp;#8217;t have to worry about interfering with the master branch.  In order to keep your master branch bug free, &lt;strong&gt;commit only to branches, not to master itself&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;In addition to creating new branches for major features, I always have a few branches around that I pop into for certain things:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&amp;#8220;design&amp;#8221; - any changes I want to make to the design go here&lt;/li&gt;
&lt;li&gt;&amp;#8220;optimize&amp;#8221; - optimizations to the site&lt;/li&gt;
&lt;li&gt;&amp;#8220;bugfixes&amp;#8221; - a place to work on minor bugfixes&lt;/li&gt;
&lt;/ul&gt;


&lt;p&gt;Having these branches allows me to make small fixes to the site, and if it turns out it&amp;#8217;s more than just a small fix, it doesn&amp;#8217;t interfere with anything else.&lt;/p&gt;

&lt;h2&gt;Commit&lt;/h2&gt;

&lt;p&gt;In the Subversion world, it&amp;#8217;s a pretty common practice to make very large commits, consisting of many changes.  With Git, &lt;strong&gt;you should constantly be committing&lt;/strong&gt;.  By making many commits, you make it easier to find bugs you may have introduced, and it makes it a lot easier to track your progress.  If you don&amp;#8217;t like the thought of wading through long lists of commits in your logs, don&amp;#8217;t worry&amp;#8211;before bringing it over to the master branch, you can consolidate things with &lt;a href="http://book.git-scm.com/4_interactive_rebasing.html"&gt;interactive rebasing&lt;/a&gt;, but while you&amp;#8217;re hacking away on a branch, it really is advantageous to have many small commits.&lt;/p&gt;

&lt;h2&gt;Rebase&lt;/h2&gt;

&lt;p&gt;Rebasing is one of the harder things to grasp when you&amp;#8217;re first learning about Git. For in-depth coverage of the topic, check out the &lt;a href="http://book.git-scm.com/4_rebasing.html"&gt;Rebasing page&lt;/a&gt; in the &lt;a href="http://book.git-scm.com/"&gt;Git Community Book&lt;/a&gt;.  In a nutshell, doing &lt;code&gt;git rebase master&lt;/code&gt; takes any commits to &lt;code&gt;master&lt;/code&gt; and inserts them into your current branch, so you can then make sure your new code still works, and it&amp;#8217;s a lot less hazardous than doing a merge.  Rebasing your branch before putting into &lt;code&gt;master&lt;/code&gt; is really important because &lt;strong&gt;it allows you to deal with any merge issues before the code goes to your main branch&lt;/strong&gt;. To rebase, just run &lt;code&gt;git rebase master&lt;/code&gt;.&lt;/p&gt;

&lt;h2&gt;Merge&lt;/h2&gt;

&lt;p&gt;After rebasing the branch, it&amp;#8217;s safe to merge it into master.  Since I&amp;#8217;ve already dealt with any merge issues with the previous step, it&amp;#8217;s as simple as checking out the master branch and running &lt;code&gt;git merge &amp;lt;branch-name&amp;gt;&lt;/code&gt;&lt;/p&gt;

&lt;h2&gt;Deploy&lt;/h2&gt;

&lt;p&gt;I use &lt;a href="http://capify.org"&gt;Capistrano&lt;/a&gt; to deploy my code, so I&amp;#8217;m constantly typing &lt;code&gt;git push&lt;/code&gt; followed by &lt;code&gt;cap deploy&lt;/code&gt; to deploy changes to my server.  To make it easier, I just put both commands into one git alias:&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;deploy = !git push &amp;amp;&amp;amp; cap deploy
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;Now I just need to run &lt;code&gt;git deploy&lt;/code&gt; and it automatically pushes all of my changes to the remote Git repository and then deploys the site using Capistrano.  Here&amp;#8217;s &lt;a href="http://git.or.cz/gitwiki/Aliases"&gt;some more information about Git aliases&lt;/a&gt;.&lt;/p&gt;

&lt;h2&gt;Useful tools&lt;/h2&gt;

&lt;p&gt;Though I primarily use Git through the command line, I really like using &lt;a href="http://gitx.frim.nl/"&gt;GitX&lt;/a&gt; to visualize branches.  To host my repositories on my server, I use &lt;a href="http://scie.nti.st/2007/11/14/hosting-git-repositories-the-easy-and-secure-way"&gt;Gitosis&lt;/a&gt;, though if I had a few more projects, I&amp;#8217;d dole out the money for a paid account at &lt;a href="http://github.com"&gt;GitHub&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Have a great Git workflow? Think mine&amp;#8217;s terrible? Let me know in the comments!&lt;/p&gt;
</content>
      <pubDate>Thu, 30 Jul 2009 14:41:00 -0400</pubDate>
      <link>http://kyleslattery.com/entries/the-perfect-git-workflow-for-a-one-person-project</link>
      <guid>http://kyleslattery.com/entries/the-perfect-git-workflow-for-a-one-person-project</guid>
    </item>
    <item>
      <title>4 Easy Frontend Optimizations for Your Site</title>
      <description>&lt;p&gt;Over the past few weeks, I&amp;#8217;ve been tweaking the site a bit to help speed up page loads.  After running &lt;a href="http://developer.yahoo.com/yslow/"&gt;YSlow&lt;/a&gt; and &lt;a href="http://code.google.com/speed/page-speed/"&gt;Page Speed&lt;/a&gt;, it seemed like the slowest aspects of my site weren&amp;#8217;t necessarily happening behind the scenes with my Ruby code, but rather on the frontend with images, Javascript, and CSS.  Below, I&amp;#8217;ve listed four ways you can squeeze more speed out of your site without even touching backend code.&lt;/p&gt;

&lt;h2&gt;Gzip your files&lt;/h2&gt;

&lt;p&gt;One of the first things I did to speed up my site was to start using &lt;a href="http://httpd.apache.org/docs/2.0/mod/mod_deflate.html"&gt;mod_deflate&lt;/a&gt; to gzip compress data from my site before it&amp;#8217;s even sent to the viewer&amp;#8217;s browser.  Basically what happens is after my Rails app returns the HTML for the page, Apache then compresses it and sends the compressed version to the user. Enabling this brought my front page size down to 6KB from around 22KB, &lt;strong&gt;a 73% improvement&lt;/strong&gt;!  You should enable this on all of your plaintext files, such as external CSS and Javascript files, as well as any HTML that&amp;#8217;s being sent to the browser&amp;#8211;essentially as many files as you can get away with.&lt;/p&gt;

&lt;p&gt;For more information, make sure to read &lt;a href="http://paulstamatiou.com/how-to-optimize-your-apache-site-with-mod-deflate"&gt;Paul Stamatiou&amp;#8217;s excellent post on mod_deflate&lt;/a&gt;, where he goes through the steps of gzipping your site on Apache.&lt;/p&gt;

&lt;h2&gt;Set a far future expires header for images, stylesheets, and JS&lt;/h2&gt;

&lt;p&gt;When your browser wants to fetch a file, it first checks to see if it&amp;#8217;s stored in its local cache, and if it has a new enough version of the file, it will display that instead of taking the time to download it from the server.  The problem is, how does your browser know if the cached version is &amp;#8220;new enough&amp;#8221;?  The trick is to use the &lt;code&gt;Expires&lt;/code&gt; header for any files you know aren&amp;#8217;t going to change very often (such as images, stylesheets, and javascript).  I did this on all of my &lt;a href="/notebook/photos/"&gt;photos&lt;/a&gt;, and it has made a huge difference&amp;#8211;after turning on the header, my &lt;strong&gt;bandwidth usage went down by 70%&lt;/strong&gt;, since browsers were no longer re-requesting images they already had.&lt;/p&gt;

&lt;p&gt;For how to set a far future expires header with Apache, check out &lt;a href="http://www.cjohansen.no/en/apache/using_a_far_future_expires_header"&gt;Christian Johansen&amp;#8217;s tutorial on the subject&lt;/a&gt;.&lt;/p&gt;

&lt;h2&gt;Load Javascript Libraries from Google&lt;/h2&gt;

&lt;p&gt;Nowadays, just about every site is using a javascript library of some sort, whether it&amp;#8217;s &lt;a href="http://www.prototypejs.org/"&gt;Prototype&lt;/a&gt;, &lt;a href="http://jquery.com"&gt;jQuery&lt;/a&gt;, or something else.  Since the library might not change from site to site, why not leverage that to cut down on download times?  Google has done the work for you, with their &lt;a href="http://code.google.com/apis/ajaxlibs/"&gt;AJAX Libraries API&lt;/a&gt;, which provides you with a CDN-hosted version (&lt;a href="http://en.wikipedia.org/wiki/Content_delivery_network"&gt;what is a CDN?&lt;/a&gt;) of popular JS libraries, so they&amp;#8217;re super fast, and once your browser downloads the library for the first time, it will be retrieved from cache for any site that uses the same file.&lt;/p&gt;

&lt;p&gt;There are 2 ways to use Google&amp;#8217;s hosted libraries, via the &lt;a href="http://code.google.com/apis/ajaxlibs/"&gt;API listed on their page&lt;/a&gt; or just by including the file.  For instance, I&amp;#8217;m including jQuery 1.3.1 with this code:&lt;/p&gt;

&lt;pre&gt;&lt;code class="html"&gt;&amp;lt;script type="text/javascript" src="http://ajax.googleapis.com/ajax/libs/jquery/1.3.1/jquery.min.js"&amp;gt;&amp;lt;/script&amp;gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;I&amp;#8217;m not sure exactly how much faster this has made my site, but it definitely keeps bandwidth usage down and makes the user experience much quicker.&lt;/p&gt;

&lt;h2&gt;Defer Loading of Stat-tracking Javascript&lt;/h2&gt;

&lt;p&gt;I&amp;#8217;ve always run two separate stat tracking scripts on this site: &lt;a href="http://google.com/analytics/"&gt;Google Analytics&lt;/a&gt; and &lt;a href="http://haveamint.com/"&gt;Mint&lt;/a&gt;, but recently, I really started to notice they were slowing down page loading time (especially with Mint).  To combat this, I decided to defer loading the scripts until after the page has finished, so as to ensure they don&amp;#8217;t impact the user experience negatively.  To do this, I just used jQuery&amp;#8217;s &lt;a href="http://docs.jquery.com/Ajax/jQuery.getScript"&gt;&lt;code&gt;$.getScript()&lt;/code&gt; function&lt;/a&gt; like this:&lt;/p&gt;

&lt;pre&gt;&lt;code class="js"&gt;$(document).ready(function() {
  var gaJsHost = (("https:" == document.location.protocol) ? "https://ssl." : "http://www.");
  $.getScript(gaJsHost + 'google-analytics.com/ga.js', function() {
    var pageTracker = _gat._getTracker("GOOGLE TRACKING CODE");
    pageTracker._initData();
    pageTracker._trackPageview();
  });
  $.getScript('http://path/to/mint/?js');
});
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;Just customize it to point at your stat tracking scripts, and you&amp;#8217;re ready to go.  One caveat with this&amp;#8211;&lt;strong&gt;it may impact your stats&lt;/strong&gt;, because scripts won&amp;#8217;t be loaded until the page finishes rendering, meaning if someone clicks off the page before them, it might not register their visit.  I haven&amp;#8217;t had enough time to see if my decision to defer loading has affected my stats, but I&amp;#8217;m guessing it would have at least a minor impact.&lt;/p&gt;

&lt;h2&gt;Anything else?&lt;/h2&gt;

&lt;p&gt;Is there anything else you&amp;#8217;ve done for your site that&amp;#8217;s really lowered your loading times?  Let me know in the comments!&lt;/p&gt;
</description>
      <content>&lt;p&gt;Over the past few weeks, I&amp;#8217;ve been tweaking the site a bit to help speed up page loads.  After running &lt;a href="http://developer.yahoo.com/yslow/"&gt;YSlow&lt;/a&gt; and &lt;a href="http://code.google.com/speed/page-speed/"&gt;Page Speed&lt;/a&gt;, it seemed like the slowest aspects of my site weren&amp;#8217;t necessarily happening behind the scenes with my Ruby code, but rather on the frontend with images, Javascript, and CSS.  Below, I&amp;#8217;ve listed four ways you can squeeze more speed out of your site without even touching backend code.&lt;/p&gt;

&lt;h2&gt;Gzip your files&lt;/h2&gt;

&lt;p&gt;One of the first things I did to speed up my site was to start using &lt;a href="http://httpd.apache.org/docs/2.0/mod/mod_deflate.html"&gt;mod_deflate&lt;/a&gt; to gzip compress data from my site before it&amp;#8217;s even sent to the viewer&amp;#8217;s browser.  Basically what happens is after my Rails app returns the HTML for the page, Apache then compresses it and sends the compressed version to the user. Enabling this brought my front page size down to 6KB from around 22KB, &lt;strong&gt;a 73% improvement&lt;/strong&gt;!  You should enable this on all of your plaintext files, such as external CSS and Javascript files, as well as any HTML that&amp;#8217;s being sent to the browser&amp;#8211;essentially as many files as you can get away with.&lt;/p&gt;

&lt;p&gt;For more information, make sure to read &lt;a href="http://paulstamatiou.com/how-to-optimize-your-apache-site-with-mod-deflate"&gt;Paul Stamatiou&amp;#8217;s excellent post on mod_deflate&lt;/a&gt;, where he goes through the steps of gzipping your site on Apache.&lt;/p&gt;

&lt;h2&gt;Set a far future expires header for images, stylesheets, and JS&lt;/h2&gt;

&lt;p&gt;When your browser wants to fetch a file, it first checks to see if it&amp;#8217;s stored in its local cache, and if it has a new enough version of the file, it will display that instead of taking the time to download it from the server.  The problem is, how does your browser know if the cached version is &amp;#8220;new enough&amp;#8221;?  The trick is to use the &lt;code&gt;Expires&lt;/code&gt; header for any files you know aren&amp;#8217;t going to change very often (such as images, stylesheets, and javascript).  I did this on all of my &lt;a href="/notebook/photos/"&gt;photos&lt;/a&gt;, and it has made a huge difference&amp;#8211;after turning on the header, my &lt;strong&gt;bandwidth usage went down by 70%&lt;/strong&gt;, since browsers were no longer re-requesting images they already had.&lt;/p&gt;

&lt;p&gt;For how to set a far future expires header with Apache, check out &lt;a href="http://www.cjohansen.no/en/apache/using_a_far_future_expires_header"&gt;Christian Johansen&amp;#8217;s tutorial on the subject&lt;/a&gt;.&lt;/p&gt;

&lt;h2&gt;Load Javascript Libraries from Google&lt;/h2&gt;

&lt;p&gt;Nowadays, just about every site is using a javascript library of some sort, whether it&amp;#8217;s &lt;a href="http://www.prototypejs.org/"&gt;Prototype&lt;/a&gt;, &lt;a href="http://jquery.com"&gt;jQuery&lt;/a&gt;, or something else.  Since the library might not change from site to site, why not leverage that to cut down on download times?  Google has done the work for you, with their &lt;a href="http://code.google.com/apis/ajaxlibs/"&gt;AJAX Libraries API&lt;/a&gt;, which provides you with a CDN-hosted version (&lt;a href="http://en.wikipedia.org/wiki/Content_delivery_network"&gt;what is a CDN?&lt;/a&gt;) of popular JS libraries, so they&amp;#8217;re super fast, and once your browser downloads the library for the first time, it will be retrieved from cache for any site that uses the same file.&lt;/p&gt;

&lt;p&gt;There are 2 ways to use Google&amp;#8217;s hosted libraries, via the &lt;a href="http://code.google.com/apis/ajaxlibs/"&gt;API listed on their page&lt;/a&gt; or just by including the file.  For instance, I&amp;#8217;m including jQuery 1.3.1 with this code:&lt;/p&gt;

&lt;pre&gt;&lt;code class="html"&gt;&amp;lt;script type="text/javascript" src="http://ajax.googleapis.com/ajax/libs/jquery/1.3.1/jquery.min.js"&amp;gt;&amp;lt;/script&amp;gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;I&amp;#8217;m not sure exactly how much faster this has made my site, but it definitely keeps bandwidth usage down and makes the user experience much quicker.&lt;/p&gt;

&lt;h2&gt;Defer Loading of Stat-tracking Javascript&lt;/h2&gt;

&lt;p&gt;I&amp;#8217;ve always run two separate stat tracking scripts on this site: &lt;a href="http://google.com/analytics/"&gt;Google Analytics&lt;/a&gt; and &lt;a href="http://haveamint.com/"&gt;Mint&lt;/a&gt;, but recently, I really started to notice they were slowing down page loading time (especially with Mint).  To combat this, I decided to defer loading the scripts until after the page has finished, so as to ensure they don&amp;#8217;t impact the user experience negatively.  To do this, I just used jQuery&amp;#8217;s &lt;a href="http://docs.jquery.com/Ajax/jQuery.getScript"&gt;&lt;code&gt;$.getScript()&lt;/code&gt; function&lt;/a&gt; like this:&lt;/p&gt;

&lt;pre&gt;&lt;code class="js"&gt;$(document).ready(function() {
  var gaJsHost = (("https:" == document.location.protocol) ? "https://ssl." : "http://www.");
  $.getScript(gaJsHost + 'google-analytics.com/ga.js', function() {
    var pageTracker = _gat._getTracker("GOOGLE TRACKING CODE");
    pageTracker._initData();
    pageTracker._trackPageview();
  });
  $.getScript('http://path/to/mint/?js');
});
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;Just customize it to point at your stat tracking scripts, and you&amp;#8217;re ready to go.  One caveat with this&amp;#8211;&lt;strong&gt;it may impact your stats&lt;/strong&gt;, because scripts won&amp;#8217;t be loaded until the page finishes rendering, meaning if someone clicks off the page before them, it might not register their visit.  I haven&amp;#8217;t had enough time to see if my decision to defer loading has affected my stats, but I&amp;#8217;m guessing it would have at least a minor impact.&lt;/p&gt;

&lt;h2&gt;Anything else?&lt;/h2&gt;

&lt;p&gt;Is there anything else you&amp;#8217;ve done for your site that&amp;#8217;s really lowered your loading times?  Let me know in the comments!&lt;/p&gt;
</content>
      <pubDate>Sat, 04 Jul 2009 06:29:00 -0400</pubDate>
      <link>http://kyleslattery.com/entries/4-easy-frontend-optimizations-for-your-site</link>
      <guid>http://kyleslattery.com/entries/4-easy-frontend-optimizations-for-your-site</guid>
    </item>
    <item>
      <title>Sass Without the Hass(le)</title>
      <description>&lt;p&gt;For a recent project, I decided to try out &lt;a href="http://haml.hamptoncatlin.com/docs/rdoc/classes/Sass.html"&gt;Sass&lt;/a&gt;, a &amp;#8220;meta-language on top of CSS,&amp;#8221; which allows to do all sorts of neat things like use variables, do math, and have mixins.  The only problem: since you&amp;#8217;re not writing CSS, &lt;strong&gt;it has to be compiled whenever you want to view your page&lt;/strong&gt;, which can be pretty annoying if you&amp;#8217;re just working on a static template (which I was).&lt;/p&gt;

&lt;p&gt;At first, I was using the standard command: &lt;code&gt;sass input.sass output.css&lt;/code&gt;, but that became too tiresome, and I was struck with an idea&amp;#8211;what if I just created &lt;strong&gt;a server solely for serving the compiled CSS file&lt;/strong&gt;?  Using Sinatra, I was able to make a dead easy way to use Sass while working on a static template.  Follow along to see how you can do this yourself.&lt;/p&gt;

&lt;p&gt;First, make sure you&amp;#8217;ve got the &lt;code&gt;haml&lt;/code&gt; and &lt;code&gt;sinatra&lt;/code&gt; gems installed:&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;gem install haml sinatra
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;Now, in your development directory, create a file called &lt;code&gt;app.rb&lt;/code&gt; and paste the following code:&lt;/p&gt;

&lt;pre&gt;&lt;code class="ruby"&gt;require 'rubygems'
require 'sinatra'
require 'haml'

get '/style.css' do
  headers 'Content-Type' =&amp;gt; 'text/css; charset=utf-8'
  sass :style
end
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;Now, create a &lt;code&gt;views/&lt;/code&gt; directory in that same folder, and drop a &lt;code&gt;style.sass&lt;/code&gt; file in there.  This is where you&amp;#8217;ll be writing your Sass.  Now, you need to start up your Sinatra server, and to do that, just run the following command:&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;ruby app.rb
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;Now, if you go to &lt;code&gt;http://localhost:4567/style.css&lt;/code&gt;, you&amp;#8217;ll see your compiled CSS, and every time you update your Sass file, the code is recompiled.  Just change the CSS &lt;code&gt;&amp;lt;link&amp;gt;&lt;/code&gt; tag in your HTML template to point to the &lt;code&gt;style.css&lt;/code&gt; file, and you&amp;#8217;re ready to go!&lt;/p&gt;
</description>
      <content>&lt;p&gt;For a recent project, I decided to try out &lt;a href="http://haml.hamptoncatlin.com/docs/rdoc/classes/Sass.html"&gt;Sass&lt;/a&gt;, a &amp;#8220;meta-language on top of CSS,&amp;#8221; which allows to do all sorts of neat things like use variables, do math, and have mixins.  The only problem: since you&amp;#8217;re not writing CSS, &lt;strong&gt;it has to be compiled whenever you want to view your page&lt;/strong&gt;, which can be pretty annoying if you&amp;#8217;re just working on a static template (which I was).&lt;/p&gt;

&lt;p&gt;At first, I was using the standard command: &lt;code&gt;sass input.sass output.css&lt;/code&gt;, but that became too tiresome, and I was struck with an idea&amp;#8211;what if I just created &lt;strong&gt;a server solely for serving the compiled CSS file&lt;/strong&gt;?  Using Sinatra, I was able to make a dead easy way to use Sass while working on a static template.  Follow along to see how you can do this yourself.&lt;/p&gt;

&lt;p&gt;First, make sure you&amp;#8217;ve got the &lt;code&gt;haml&lt;/code&gt; and &lt;code&gt;sinatra&lt;/code&gt; gems installed:&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;gem install haml sinatra
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;Now, in your development directory, create a file called &lt;code&gt;app.rb&lt;/code&gt; and paste the following code:&lt;/p&gt;

&lt;pre&gt;&lt;code class="ruby"&gt;require 'rubygems'
require 'sinatra'
require 'haml'

get '/style.css' do
  headers 'Content-Type' =&amp;gt; 'text/css; charset=utf-8'
  sass :style
end
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;Now, create a &lt;code&gt;views/&lt;/code&gt; directory in that same folder, and drop a &lt;code&gt;style.sass&lt;/code&gt; file in there.  This is where you&amp;#8217;ll be writing your Sass.  Now, you need to start up your Sinatra server, and to do that, just run the following command:&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;ruby app.rb
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;Now, if you go to &lt;code&gt;http://localhost:4567/style.css&lt;/code&gt;, you&amp;#8217;ll see your compiled CSS, and every time you update your Sass file, the code is recompiled.  Just change the CSS &lt;code&gt;&amp;lt;link&amp;gt;&lt;/code&gt; tag in your HTML template to point to the &lt;code&gt;style.css&lt;/code&gt; file, and you&amp;#8217;re ready to go!&lt;/p&gt;
</content>
      <pubDate>Fri, 19 Jun 2009 06:16:00 -0400</pubDate>
      <link>http://kyleslattery.com/entries/sass-without-the-hassle</link>
      <guid>http://kyleslattery.com/entries/sass-without-the-hassle</guid>
    </item>
    <item>
      <title>Improving on Related Entries</title>
      <description>&lt;p&gt;A little while back, I posted about &lt;a href="http://kyleslattery.com/notebook/entries/finding-related-entries-using-tags-with-ruby-on-rails"&gt;how I was determining related entries&lt;/a&gt; for my site.  That method worked, but once I &lt;a href="http://kyleslattery.com/notebook/entries/recalibration"&gt;redid my site&lt;/a&gt; and added my 250+ Flickr photos, it started to really slow down when finding related photos, because of the increase in tags and posts.  The real issue was that I was doing most of the work in Ruby, when it really should have been done with SQL.  So, I decided to rewrite it.&lt;/p&gt;

&lt;p&gt;Note: If you haven&amp;#8217;t looked at &lt;a href="http://kyleslattery.com/notebook/entries/finding-related-entries-using-tags-with-ruby-on-rails"&gt;my previous entry on the subject&lt;/a&gt;, you might want to take a look at it, just for the general idea of what I&amp;#8217;m trying to accomplish.  Essentially, I&amp;#8217;m trying to find related posts by comparing tags.  Here&amp;#8217;s my new &lt;code&gt;Post#related&lt;/code&gt; code:&lt;/p&gt;

&lt;pre&gt;&lt;code class="ruby"&gt;def related(limit=5)  
  return [] if tags.empty?

  join_array = tags.collect {|tag| "posts_tags.id = #{tag.id}"}

  tags_join = "AND (#{join_array.join(' OR ')})"

  self.class.find(:all,
                  :joins =&amp;gt; "INNER JOIN taggings posts_taggings ON posts_taggings.taggable_id = posts.id" +
                            "INNER JOIN tags posts_tags ON posts_tags.id = posts_taggings.tag_id #{tags_join}",
                  :conditions =&amp;gt; ["posts.id != ?", id], :group =&amp;gt; "posts.id",
                  :order =&amp;gt; "COUNT(*) DESC",
                  :limit =&amp;gt; limit)
end
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;You can see, as I mentioned, that all the work is being done in the SQL now.  I first create a list of tags from the current post, which I then feed into the query to search for other posts with similar tags.  The SQL instructs the database to search for any posts with any of these tags, and then orders them based on how many tags match between the 2 posts.  The SQL ended up being fairly complicated, with a lot of joins, but it&amp;#8217;s now a whole lot faster, because I&amp;#8217;m not creating a lot of overhead by dealing with the computation in Ruby.  If you&amp;#8217;re interested, here&amp;#8217;s an example related entry query:&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;SELECT `posts`.* FROM `posts`
INNER JOIN taggings posts_taggings ON posts_taggings.taggable_id = posts.id
INNER JOIN tags posts_tags ON posts_tags.id = posts_taggings.tag_id
    AND (posts_tags.id = 695
        OR posts_tags.id = 192
        OR posts_tags.id = 195)
WHERE (posts.id != 4322) AND ( (`posts`.`type` = 'FlickrPhoto' ) )
GROUP BY posts.id
ORDER BY COUNT(*) DESC
LIMIT 5
&lt;/code&gt;&lt;/pre&gt;
</description>
      <content>&lt;p&gt;A little while back, I posted about &lt;a href="http://kyleslattery.com/notebook/entries/finding-related-entries-using-tags-with-ruby-on-rails"&gt;how I was determining related entries&lt;/a&gt; for my site.  That method worked, but once I &lt;a href="http://kyleslattery.com/notebook/entries/recalibration"&gt;redid my site&lt;/a&gt; and added my 250+ Flickr photos, it started to really slow down when finding related photos, because of the increase in tags and posts.  The real issue was that I was doing most of the work in Ruby, when it really should have been done with SQL.  So, I decided to rewrite it.&lt;/p&gt;

&lt;p&gt;Note: If you haven&amp;#8217;t looked at &lt;a href="http://kyleslattery.com/notebook/entries/finding-related-entries-using-tags-with-ruby-on-rails"&gt;my previous entry on the subject&lt;/a&gt;, you might want to take a look at it, just for the general idea of what I&amp;#8217;m trying to accomplish.  Essentially, I&amp;#8217;m trying to find related posts by comparing tags.  Here&amp;#8217;s my new &lt;code&gt;Post#related&lt;/code&gt; code:&lt;/p&gt;

&lt;pre&gt;&lt;code class="ruby"&gt;def related(limit=5)  
  return [] if tags.empty?

  join_array = tags.collect {|tag| "posts_tags.id = #{tag.id}"}

  tags_join = "AND (#{join_array.join(' OR ')})"

  self.class.find(:all,
                  :joins =&amp;gt; "INNER JOIN taggings posts_taggings ON posts_taggings.taggable_id = posts.id" +
                            "INNER JOIN tags posts_tags ON posts_tags.id = posts_taggings.tag_id #{tags_join}",
                  :conditions =&amp;gt; ["posts.id != ?", id], :group =&amp;gt; "posts.id",
                  :order =&amp;gt; "COUNT(*) DESC",
                  :limit =&amp;gt; limit)
end
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;You can see, as I mentioned, that all the work is being done in the SQL now.  I first create a list of tags from the current post, which I then feed into the query to search for other posts with similar tags.  The SQL instructs the database to search for any posts with any of these tags, and then orders them based on how many tags match between the 2 posts.  The SQL ended up being fairly complicated, with a lot of joins, but it&amp;#8217;s now a whole lot faster, because I&amp;#8217;m not creating a lot of overhead by dealing with the computation in Ruby.  If you&amp;#8217;re interested, here&amp;#8217;s an example related entry query:&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;SELECT `posts`.* FROM `posts`
INNER JOIN taggings posts_taggings ON posts_taggings.taggable_id = posts.id
INNER JOIN tags posts_tags ON posts_tags.id = posts_taggings.tag_id
    AND (posts_tags.id = 695
        OR posts_tags.id = 192
        OR posts_tags.id = 195)
WHERE (posts.id != 4322) AND ( (`posts`.`type` = 'FlickrPhoto' ) )
GROUP BY posts.id
ORDER BY COUNT(*) DESC
LIMIT 5
&lt;/code&gt;&lt;/pre&gt;
</content>
      <pubDate>Mon, 13 Apr 2009 18:02:00 -0400</pubDate>
      <link>http://kyleslattery.com/entries/improving-on-related-entries</link>
      <guid>http://kyleslattery.com/entries/improving-on-related-entries</guid>
    </item>
    <item>
      <title>How to Take Better Photos</title>
      <description>&lt;p&gt;I&amp;#8217;ve been into photography for a while now, and while I definitely don&amp;#8217;t consider myself an expert on any level, I&amp;#8217;ve come across a lot of tips over the years that have really helped to improve my photos.  I&amp;#8217;ve collected together a few of my favorite ones below, in the hopes they&amp;#8217;ll help someone else.&lt;/p&gt;

&lt;h2&gt;Constrain yourself&lt;/h2&gt;

&lt;div class="figure-right figure-190"&gt;
  &lt;a href="http://kyleslattery.com/notebook/photos/peeling-paint-and-window-venice-italy"&gt;&lt;img width="190" alt="Peeling Paint and Window, Venice, Italy" src="http://farm2.static.flickr.com/1118/753948743_221d5ba60b_m.jpg"/&gt;&lt;/a&gt;
  &lt;p class="caption"&gt;&lt;a href="http://kyleslattery.com/notebook/photos/peeling-paint-and-window-venice-italy"&gt;Peeling Paint and Window&lt;/a&gt;, taken with my 50mm lens&lt;/p&gt;
&lt;/div&gt;


&lt;p&gt;Next time you go out to take some photos, &lt;strong&gt;give yourself a constraint&lt;/strong&gt;.  For example, maybe just bring one lens, or choose just one focal length to shoot at (a prime lens works perfectly for this).  When I went to Italy, I brought 2 lenses, one of which appeared to be broken when I got there, so I had to spend the week using only my 50mm lens. This actually turned out to be a blessing in disguise, as I ended up with some of my favorite photos I&amp;#8217;ve ever taken.  Another great exercise in constraints is to place a piece of paper on the ground, and then take 50 photos without taking your feet off the paper.  Really, you&amp;#8217;re just trying to get yourself to &lt;strong&gt;look at the world in a different way&lt;/strong&gt;.&lt;/p&gt;

&lt;h2&gt;Take your camera everywhere&lt;/h2&gt;

&lt;p&gt;A lot of great photos come about because someone was in the right spot at the right time.  However, in order to capture the moment, you have to have a camera to do it with.  &lt;strong&gt;If you&amp;#8217;re not carrying your camera with you everywhere, you&amp;#8217;re probably missing out on a lot of opportunities&lt;/strong&gt;.  One of the best things I ever did was purchase a point and shoot camera that fit in my pocket&amp;#8211;now I can carry it wherever I go, ready for the unexpected.&lt;/p&gt;

&lt;h2&gt;Stop worrying about equipment&lt;/h2&gt;

&lt;div class="figure-left figure-190"&gt;
  &lt;a href="http://kyleslattery.com/notebook/photos/weeping-lily"&gt;&lt;img width="190" alt="Weeping Lily" src="http://farm1.static.flickr.com/7/10438753_0e8c9f16fa_m.jpg"/&gt;&lt;/a&gt;
  &lt;p class="caption"&gt;&lt;a href="http://kyleslattery.com/notebook/photos/weeping-lily"&gt;Weeping Lily&lt;/a&gt;, taken on a tiny point and shoot!&lt;/p&gt;
&lt;/div&gt;


&lt;p&gt;So many times, I&amp;#8217;ve caught myself saying, &amp;#8220;I would love to take that photo, but my camera&amp;#8217;s not good enough.&amp;#8221;  This is stupid.  Ansel Adams didn&amp;#8217;t have a fancy 20 megapixel DSLR, and he took photos better than most people can even dream of.  Instead, work with what you have&amp;#8211;&lt;strong&gt;it&amp;#8217;s the thought behind the photo, not the camera, that truly matters&lt;/strong&gt;.  I&amp;#8217;ve taken great photos with tiny little point and shoot cameras, and I&amp;#8217;ve taken horrible photos with my big DSLR.  The sooner you realize your equipment doesn&amp;#8217;t matter, the better.&lt;/p&gt;

&lt;h2&gt;Ask for critique&lt;/h2&gt;

&lt;div class="figure-right figure-190"&gt;
  &lt;a href="http://kyleslattery.com/notebook/photos/lauren-1"&gt;&lt;img width="190" alt="Lauren" src="http://farm1.static.flickr.com/27/42838174_38655887c7_m.jpg"/&gt;&lt;/a&gt;
  &lt;p class="caption"&gt;This &lt;a href="http://kyleslattery.com/notebook/photos/lauren-final-crop"&gt;photo of my sister&lt;/a&gt; became a whole lot better after having it &lt;a href="http://www.flickr.com/groups/cafe/discuss/85311/"&gt;critiqued on Flickr&lt;/a&gt;.&lt;/p&gt;
&lt;/div&gt;


&lt;p&gt;Some of the best photography experiences I&amp;#8217;ve had were when I&amp;#8217;ve had my photos critiqued by others, and when I&amp;#8217;ve critiqued others.  &lt;strong&gt;Having someone else take a look at your work can be incredibly helpful&lt;/strong&gt;, as they&amp;#8217;ll often notice things you never saw yourself, and critiquing other people&amp;#8217;s photos can help you find new techniques and approaches to photography.  If you have a group of friends that are all into photography, maybe you can meet every so often and swap shots.  Or, join a site like &lt;a href="http://flickr.com"&gt;Flickr&lt;/a&gt;, and start commenting on photos by other users.  Soon enough, they&amp;#8217;ll be commenting back on yours, and you&amp;#8217;ll start seeing your photos in a whole new light.&lt;/p&gt;

&lt;h2&gt;Take lots of photos&lt;/h2&gt;

&lt;p&gt;Memory cards are cheap, so why skimp on photos?  I&amp;#8217;ve found that &lt;strong&gt;the more photos I take, the better I become at photography&lt;/strong&gt;.  But it&amp;#8217;s not just about taking lots of photos, it&amp;#8217;s about going back and seeing what your mistakes were and learning from them.  If you aren&amp;#8217;t taking a whole bunch of photos, you&amp;#8217;re not going to learn from your mistakes, because you won&amp;#8217;t be making them.&lt;/p&gt;

&lt;h2&gt;Don&amp;#8217;t think about Photoshop&lt;/h2&gt;

&lt;div class="figure-left figure-190"&gt;
  &lt;a href="http://kyleslattery.com/notebook/photos/dreaming"&gt;&lt;img width="190" alt="Dreaming" src="http://farm1.static.flickr.com/30/44052446_1a7f49d6a2_m.jpg"/&gt;&lt;/a&gt;
  &lt;p class="caption"&gt;This photo, &lt;a href="http://kyleslattery.com/notebook/photos/dreaming"&gt;Dreaming&lt;/a&gt;, was weak from the start, and my attempts to save it really didn&amp;#8217;t do much.&lt;/p&gt;
&lt;/div&gt;


&lt;p&gt;For a while, I was really into Photoshop&amp;#8211;nearly every photo I took was manipulated in some way to make it better, and I started to even think about how I was going to process an image before I even pressed the shutter button.  This is bad.  &lt;strong&gt;Focus on making your photos as perfect as possible before they get to your computer&lt;/strong&gt;, and you&amp;#8217;ll end up with a much nicer finished product.  That&amp;#8217;s not to say you shouldn&amp;#8217;t tweak photos afterwards, but you just have to remember that taking an amazing photo isn&amp;#8217;t about what filters you use, but rather about how you compose the image in the viewfinder.&lt;/p&gt;

&lt;p&gt;Hopefully these tips are helpful, and if you have any to share, make sure to leave comments below!&lt;/p&gt;
</description>
      <content>&lt;p&gt;I&amp;#8217;ve been into photography for a while now, and while I definitely don&amp;#8217;t consider myself an expert on any level, I&amp;#8217;ve come across a lot of tips over the years that have really helped to improve my photos.  I&amp;#8217;ve collected together a few of my favorite ones below, in the hopes they&amp;#8217;ll help someone else.&lt;/p&gt;

&lt;h2&gt;Constrain yourself&lt;/h2&gt;

&lt;div class="figure-right figure-190"&gt;
  &lt;a href="http://kyleslattery.com/notebook/photos/peeling-paint-and-window-venice-italy"&gt;&lt;img width="190" alt="Peeling Paint and Window, Venice, Italy" src="http://farm2.static.flickr.com/1118/753948743_221d5ba60b_m.jpg"/&gt;&lt;/a&gt;
  &lt;p class="caption"&gt;&lt;a href="http://kyleslattery.com/notebook/photos/peeling-paint-and-window-venice-italy"&gt;Peeling Paint and Window&lt;/a&gt;, taken with my 50mm lens&lt;/p&gt;
&lt;/div&gt;


&lt;p&gt;Next time you go out to take some photos, &lt;strong&gt;give yourself a constraint&lt;/strong&gt;.  For example, maybe just bring one lens, or choose just one focal length to shoot at (a prime lens works perfectly for this).  When I went to Italy, I brought 2 lenses, one of which appeared to be broken when I got there, so I had to spend the week using only my 50mm lens. This actually turned out to be a blessing in disguise, as I ended up with some of my favorite photos I&amp;#8217;ve ever taken.  Another great exercise in constraints is to place a piece of paper on the ground, and then take 50 photos without taking your feet off the paper.  Really, you&amp;#8217;re just trying to get yourself to &lt;strong&gt;look at the world in a different way&lt;/strong&gt;.&lt;/p&gt;

&lt;h2&gt;Take your camera everywhere&lt;/h2&gt;

&lt;p&gt;A lot of great photos come about because someone was in the right spot at the right time.  However, in order to capture the moment, you have to have a camera to do it with.  &lt;strong&gt;If you&amp;#8217;re not carrying your camera with you everywhere, you&amp;#8217;re probably missing out on a lot of opportunities&lt;/strong&gt;.  One of the best things I ever did was purchase a point and shoot camera that fit in my pocket&amp;#8211;now I can carry it wherever I go, ready for the unexpected.&lt;/p&gt;

&lt;h2&gt;Stop worrying about equipment&lt;/h2&gt;

&lt;div class="figure-left figure-190"&gt;
  &lt;a href="http://kyleslattery.com/notebook/photos/weeping-lily"&gt;&lt;img width="190" alt="Weeping Lily" src="http://farm1.static.flickr.com/7/10438753_0e8c9f16fa_m.jpg"/&gt;&lt;/a&gt;
  &lt;p class="caption"&gt;&lt;a href="http://kyleslattery.com/notebook/photos/weeping-lily"&gt;Weeping Lily&lt;/a&gt;, taken on a tiny point and shoot!&lt;/p&gt;
&lt;/div&gt;


&lt;p&gt;So many times, I&amp;#8217;ve caught myself saying, &amp;#8220;I would love to take that photo, but my camera&amp;#8217;s not good enough.&amp;#8221;  This is stupid.  Ansel Adams didn&amp;#8217;t have a fancy 20 megapixel DSLR, and he took photos better than most people can even dream of.  Instead, work with what you have&amp;#8211;&lt;strong&gt;it&amp;#8217;s the thought behind the photo, not the camera, that truly matters&lt;/strong&gt;.  I&amp;#8217;ve taken great photos with tiny little point and shoot cameras, and I&amp;#8217;ve taken horrible photos with my big DSLR.  The sooner you realize your equipment doesn&amp;#8217;t matter, the better.&lt;/p&gt;

&lt;h2&gt;Ask for critique&lt;/h2&gt;

&lt;div class="figure-right figure-190"&gt;
  &lt;a href="http://kyleslattery.com/notebook/photos/lauren-1"&gt;&lt;img width="190" alt="Lauren" src="http://farm1.static.flickr.com/27/42838174_38655887c7_m.jpg"/&gt;&lt;/a&gt;
  &lt;p class="caption"&gt;This &lt;a href="http://kyleslattery.com/notebook/photos/lauren-final-crop"&gt;photo of my sister&lt;/a&gt; became a whole lot better after having it &lt;a href="http://www.flickr.com/groups/cafe/discuss/85311/"&gt;critiqued on Flickr&lt;/a&gt;.&lt;/p&gt;
&lt;/div&gt;


&lt;p&gt;Some of the best photography experiences I&amp;#8217;ve had were when I&amp;#8217;ve had my photos critiqued by others, and when I&amp;#8217;ve critiqued others.  &lt;strong&gt;Having someone else take a look at your work can be incredibly helpful&lt;/strong&gt;, as they&amp;#8217;ll often notice things you never saw yourself, and critiquing other people&amp;#8217;s photos can help you find new techniques and approaches to photography.  If you have a group of friends that are all into photography, maybe you can meet every so often and swap shots.  Or, join a site like &lt;a href="http://flickr.com"&gt;Flickr&lt;/a&gt;, and start commenting on photos by other users.  Soon enough, they&amp;#8217;ll be commenting back on yours, and you&amp;#8217;ll start seeing your photos in a whole new light.&lt;/p&gt;

&lt;h2&gt;Take lots of photos&lt;/h2&gt;

&lt;p&gt;Memory cards are cheap, so why skimp on photos?  I&amp;#8217;ve found that &lt;strong&gt;the more photos I take, the better I become at photography&lt;/strong&gt;.  But it&amp;#8217;s not just about taking lots of photos, it&amp;#8217;s about going back and seeing what your mistakes were and learning from them.  If you aren&amp;#8217;t taking a whole bunch of photos, you&amp;#8217;re not going to learn from your mistakes, because you won&amp;#8217;t be making them.&lt;/p&gt;

&lt;h2&gt;Don&amp;#8217;t think about Photoshop&lt;/h2&gt;

&lt;div class="figure-left figure-190"&gt;
  &lt;a href="http://kyleslattery.com/notebook/photos/dreaming"&gt;&lt;img width="190" alt="Dreaming" src="http://farm1.static.flickr.com/30/44052446_1a7f49d6a2_m.jpg"/&gt;&lt;/a&gt;
  &lt;p class="caption"&gt;This photo, &lt;a href="http://kyleslattery.com/notebook/photos/dreaming"&gt;Dreaming&lt;/a&gt;, was weak from the start, and my attempts to save it really didn&amp;#8217;t do much.&lt;/p&gt;
&lt;/div&gt;


&lt;p&gt;For a while, I was really into Photoshop&amp;#8211;nearly every photo I took was manipulated in some way to make it better, and I started to even think about how I was going to process an image before I even pressed the shutter button.  This is bad.  &lt;strong&gt;Focus on making your photos as perfect as possible before they get to your computer&lt;/strong&gt;, and you&amp;#8217;ll end up with a much nicer finished product.  That&amp;#8217;s not to say you shouldn&amp;#8217;t tweak photos afterwards, but you just have to remember that taking an amazing photo isn&amp;#8217;t about what filters you use, but rather about how you compose the image in the viewfinder.&lt;/p&gt;

&lt;p&gt;Hopefully these tips are helpful, and if you have any to share, make sure to leave comments below!&lt;/p&gt;
</content>
      <pubDate>Sun, 05 Apr 2009 10:53:00 -0400</pubDate>
      <link>http://kyleslattery.com/entries/how-to-take-better-photos</link>
      <guid>http://kyleslattery.com/entries/how-to-take-better-photos</guid>
    </item>
  </channel>
</rss>

