<?xml version="1.0" encoding="UTF-8"?>
<rss xmlns:dc="http://purl.org/dc/elements/1.1/" version="2.0"><channel><atom:link rel="hub" href="http://tumblr.superfeedr.com/" xmlns:atom="http://www.w3.org/2005/Atom"/><description>I’m developer from Kentucky who specializes in building awesome web applications and has a passion for nonprofits and open source code.</description><title>Kelli Shaver</title><generator>Tumblr (3.0; @kellishaver)</generator><link>http://kellishaver.tumblr.com/</link><item><title>Early this morning, the first commercial manned space craft...</title><description>&lt;img src="http://25.media.tumblr.com/tumblr_m4ld8v0CKN1rnnanlo1_500.jpg"/&gt;&lt;br/&gt;&lt;br/&gt;&lt;p&gt;Early this morning, the first commercial manned space craft docked with the International Space Station, 230 miles above the Earth’s surface. As if this wasn’t awesome enough, you could watch this event live, from anywhere in the world, on your phone.&lt;/p&gt;</description><link>http://kellishaver.tumblr.com/post/23744077297</link><guid>http://kellishaver.tumblr.com/post/23744077297</guid><pubDate>Fri, 25 May 2012 14:41:19 -0400</pubDate><category>space</category></item><item><title>The Dot is Optional</title><description>&lt;a href="http://nitch.cc/podcast/episode-6-the-dot-is-optional"&gt;The Dot is Optional&lt;/a&gt;: &lt;p&gt;My favorite podcast recording yet.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;Jonathan and Kelli talk about accessing REST APIs over SMS, wrestling with Google Chat bots, confusion about the HTML5 Audio tag, and so much more.&lt;/p&gt;
&lt;/blockquote&gt;</description><link>http://kellishaver.tumblr.com/post/23567009430</link><guid>http://kellishaver.tumblr.com/post/23567009430</guid><pubDate>Tue, 22 May 2012 18:06:38 -0400</pubDate><category>nitch</category><category>podcast</category></item><item><title>Experimenting with the Architecture of Ember.js</title><description>&lt;a href="http://techiferous.com/2012/05/experimenting-with-the-architecture-of-ember-js/"&gt;Experimenting with the Architecture of Ember.js&lt;/a&gt;: &lt;p&gt;Since I’m feeling too overworked at the moment to write my own blog post, I’ll link to one written recently by &lt;a href="http://twitter.com/techiferous" title="wyatt"&gt;my very good friend&lt;/a&gt;, wherein he re-engineers the architecture of Ember.js to add a couple more degrees of separation.&lt;/p&gt;
&lt;p&gt;It’s not for everyone, as he admits, but personally, I really like the idea of separation on the client side between UI interactions and application state (separate from the data model itself).&lt;/p&gt;
&lt;p&gt;In some ways, it feels logically very similar to some of the ideas I’ve been working on lately with regards to server-side setups for scaling Sinatra applications…. but that’s a whole other blog post in and of itself, which I’ll go into at some point.&lt;/p&gt;</description><link>http://kellishaver.tumblr.com/post/23566680557</link><guid>http://kellishaver.tumblr.com/post/23566680557</guid><pubDate>Tue, 22 May 2012 18:02:00 -0400</pubDate><category>javascript</category><category>ember.js</category><category>mvc</category><category>dev</category><category>friends</category></item><item><title>Run.</title><description>&lt;iframe width="400" height="300" src="http://www.youtube.com/embed/Tp5UjQy87XA?wmode=transparent&amp;autohide=1&amp;egm=0&amp;hd=1&amp;iv_load_policy=3&amp;modestbranding=1&amp;rel=0&amp;showinfo=0&amp;showsearch=0" frameborder="0" allowfullscreen&gt;&lt;/iframe&gt;&lt;br/&gt;&lt;br/&gt;&lt;p&gt;Run.&lt;/p&gt;</description><link>http://kellishaver.tumblr.com/post/23565946645</link><guid>http://kellishaver.tumblr.com/post/23565946645</guid><pubDate>Tue, 22 May 2012 17:51:23 -0400</pubDate><category>music</category><category>videos</category></item><item><title>GTalk and Ruby</title><description>&lt;p&gt;Yesterday I had the need to create a small GTalk bot in Ruby. I started looking around and, to be honest, it was really hard to find any examples newer than 2009 or so. Even the gems are old.&lt;/p&gt;
&lt;p&gt;Sure, maybe the XMPP protocol hasn&amp;#8217;t changed that much over the years, but Ruby has, so I thought I would make a quick note about how I finally got this working.&lt;/p&gt;
&lt;p&gt;XMPP4R-Simple won&amp;#8217;t work in Ruby 1.9.x (and I didn&amp;#8217;t have time to debug it)&lt;/p&gt;
&lt;p&gt;The Easy-GTalk-Bot gem is a very simple wrapper for XMPP4R and it worked fine for the most basic of bots, but it couldn&amp;#8217;t handle the asynchronous nature of what I was building. The multi-threading would break down and the connection would drop.&lt;/p&gt;
&lt;p&gt;Blather looked promising, but also seemed like overkill for what I needed and connection handling just looked like too much work (not really difficult, but overly involved for what should be necessary in this particular instance).&lt;/p&gt;
&lt;p&gt;The gem you&amp;#8217;ll want to use is just the plain and simple XMPP4R.&lt;/p&gt;
&lt;p&gt;Here&amp;#8217;s the basics to get you started. The below script will authenticate to GTalk, automatically accept any incoming &amp;#8220;add contact&amp;#8221; requests, and echo received messages back to the user. &amp;#8212; Totally useless, but functional.&lt;/p&gt;
&lt;pre&gt;# gtalk.rb&lt;br/&gt;require 'rubygems'
require 'xmpp4r'
require 'xmpp4r/roster'

@username = 'you@gmail.com'
@password = 'seekrit'

@jid = JID::new(@username)
@client = Client.new(@jid)
@client.connect
@client.auth(@password)
@client.send(Presence.new.set_type(:available))

def roster
  @roster ||= Roster::Helper.new(@client)
end

roster.add_subscription_request_callback do |_, presence|
  inviter = JID::new(presence.from)
  roster.accept_subscription(inviter)
  invite(JID::new(inviter))
end

@jabber_client.add_message_callback do |message|
  unless message.body.nil?
    # do stuff....

    text = "You said '#{message.body}'"

    response = Message::new(JID::new(message.from), text)
    @client.send(response)
  end
end

Thread.stop
&lt;/pre&gt;
&lt;p&gt;And here&amp;#8217;s how you daemonize it with the Daemons gem.&lt;/p&gt;
&lt;pre&gt;# im.rb&lt;br/&gt;require 'rubygems'
require 'daemons'

Daemons.run('gtalk.rb',
  {
    :app_name =&amp;gt; 'myIMBot',
    :monitor =&amp;gt; true,
    :log_output =&amp;gt; false
  }
)
&lt;/pre&gt;
&lt;p&gt;You can now run the script with &lt;code&gt;ruby im.rb start&lt;/code&gt;, &lt;code&gt;ruby im.rb restart&lt;/code&gt;, and &lt;code&gt;ruby im.rb stop&lt;/code&gt;. If you want to save error output, just enable logging. As an added bonus, Monit will now watch the script and restart it if it crashes.&lt;/p&gt;</description><link>http://kellishaver.tumblr.com/post/23166947320</link><guid>http://kellishaver.tumblr.com/post/23166947320</guid><pubDate>Wed, 16 May 2012 11:16:00 -0400</pubDate><category>ruby</category><category>gtalk</category><category>xmpp</category><category>im</category></item><item><title>The Making of Octicons</title><description>&lt;a href="https://github.com/blog/1135-the-making-of-octicons"&gt;The Making of Octicons&lt;/a&gt;: &lt;p&gt;A nice little write-up of the workflow and process of creating Github’s new icon font.&lt;/p&gt;</description><link>http://kellishaver.tumblr.com/post/22741882520</link><guid>http://kellishaver.tumblr.com/post/22741882520</guid><pubDate>Wed, 09 May 2012 18:58:30 -0400</pubDate><category>design</category><category>fonts</category><category>github</category><category>icons</category></item><item><title>Shakespeare in the original pronunciation.</title><description>&lt;iframe width="400" height="225" src="http://www.youtube.com/embed/gPlpphT7n9s?wmode=transparent&amp;autohide=1&amp;egm=0&amp;hd=1&amp;iv_load_policy=3&amp;modestbranding=1&amp;rel=0&amp;showinfo=0&amp;showsearch=0" frameborder="0" allowfullscreen&gt;&lt;/iframe&gt;&lt;br/&gt;&lt;br/&gt;&lt;p&gt;Shakespeare in the original pronunciation.&lt;/p&gt;</description><link>http://kellishaver.tumblr.com/post/22523863625</link><guid>http://kellishaver.tumblr.com/post/22523863625</guid><pubDate>Sun, 06 May 2012 12:49:24 -0400</pubDate><category>language</category></item><item><title>MIT and Harvard Announce edX</title><description>&lt;a href="http://web.mit.edu/press/2012/mit-harvard-edx-announcement.html"&gt;MIT and Harvard Announce edX&lt;/a&gt;: &lt;p&gt;This is ridiculously exciting. I feel like we’re only a few years from seeing a major shift in higher education.&lt;/p&gt;</description><link>http://kellishaver.tumblr.com/post/22273140993</link><guid>http://kellishaver.tumblr.com/post/22273140993</guid><pubDate>Wed, 02 May 2012 16:52:00 -0400</pubDate><category>education</category><category>elearning</category></item><item><title>9x12in Marker</title><description>&lt;img src="http://25.media.tumblr.com/tumblr_m30zx5kANN1rnnanlo1_500.png"/&gt;&lt;br/&gt;&lt;br/&gt;&lt;p&gt;9x12in Marker&lt;/p&gt;</description><link>http://kellishaver.tumblr.com/post/21773924230</link><guid>http://kellishaver.tumblr.com/post/21773924230</guid><pubDate>Wed, 25 Apr 2012 04:07:53 -0400</pubDate><category>drawing</category><category>marker</category></item><item><title>File API Upload Image Preview - Now a jQuery Plug-in</title><description>&lt;a href="https://github.com/kellishaver/File-Input-Image-Preview"&gt;File API Upload Image Preview - Now a jQuery Plug-in&lt;/a&gt;: &lt;p&gt;I took last night’s script and turned it into a jQuery plug-in. Enjoy. :)&lt;/p&gt;</description><link>http://kellishaver.tumblr.com/post/21479704972</link><guid>http://kellishaver.tumblr.com/post/21479704972</guid><pubDate>Sat, 21 Apr 2012 00:44:10 -0400</pubDate><category>dev</category><category>jquery</category><category>javascript</category><category>file api</category><category>images</category></item><item><title>Using the File API to Preview Images before Uploading</title><description>&lt;p&gt;I was working on a little document builder tonight and wanted a way to preview images before uploading, which is something you commonly see associated with AJAX file upload scripts.&lt;/p&gt;
&lt;p&gt;I didn&amp;#8217;t want to actually upload the files via AJAX, though.&lt;/p&gt;
&lt;p&gt;As it turns out, this was pretty straightforward and simple to do. Here&amp;#8217;s an example.&lt;/p&gt;
&lt;p&gt;&lt;img height="225" src="http://i.imgur.com/3GNDc.png" width="420"/&gt;&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;First, some CSS&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;Mostly for display purposes - feel free to change it, although, you should specify a width and height.&lt;/p&gt;
&lt;pre&gt;form div.upload { overflow:hidden; }
&lt;br/&gt;form div.upload label { font-weight:bold; display:block; margin-bottom:0.25em; }&lt;br/&gt;
form div.upload div.file-preview {
    background:#ccc;
    border:1px solid #000;
    display:inline-block;
    float:left;
    margin-right:1em;
    width:60px;
    height:60px;
    text-align:center;
}
&lt;/pre&gt;
&lt;p&gt;&lt;strong&gt;The Markup&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;Drop this into your form.&lt;/p&gt;
&lt;pre&gt;&amp;lt;div class="upload"&amp;gt;
    &amp;lt;div class="file-preview"&amp;gt;&amp;lt;/div&amp;gt;
    &amp;lt;label&amp;gt;Select a File:&amp;lt;/label&amp;gt;
    &amp;lt;input type="file" name="file"&amp;gt;
&amp;lt;/div&amp;gt;&lt;/pre&gt;
&lt;p&gt;&lt;strong&gt;Now the JavaScript&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;Event listening and DOM manipulation with jQuery - you could do that with native JS, or any other library.&lt;/p&gt;
&lt;pre&gt;$('input[type=file]').change(function(e) {
    if(typeof FileReader == "undefined") return true;

    var elem = $(this);
    var files = e.target.files;

    for (var i=0, file; file=files[i]; i++) {
        if (file.type.match('image.*')) {
            var reader = new FileReader();
            reader.onload = (function(theFile) {
                return function(e) {
                    var image = e.target.result;
                    previewDiv = $('.file-preview', elem.parent());
                    bgWidth = previewDiv.width() * 2;
                    previewDiv.css({
                        "background-size":bgWidth + "px, auto",
                        "background-position":"50%, 50%",
                        "background-image":"url("+image+")",
                    });
                };
            })(file);
            reader.readAsDataURL(file);
        }
    }
});&lt;/pre&gt;</description><link>http://kellishaver.tumblr.com/post/21428272282</link><guid>http://kellishaver.tumblr.com/post/21428272282</guid><pubDate>Fri, 20 Apr 2012 04:19:00 -0400</pubDate><category>dev</category><category>javascript</category><category>file api</category><category>images</category></item><item><title>Self-deprecating Intro to Podcasting</title><description>&lt;p&gt;Hey, look! &lt;a href="http://nitch.cc/podcast"&gt;We made a podcast.&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;A while ago, my &lt;a href="http://jonathanstark.com"&gt;awesome boss&lt;/a&gt; (and friend) decided we should do a podcast, for fun and learning, and to hopefully start some good discussion.&lt;/p&gt;
&lt;p&gt;OK, so the first couple of times are always the worst, right?&lt;/p&gt;
&lt;p&gt;I am incredibly introverted (which, for some reason, comes as a surprise to many people), so this was a big leap for me, even though I was only talking to imaginary listeners that didn&amp;#8217;t yet exist. As a result, the first episode was full of all kinds of annoying verbal tics that I didn&amp;#8217;t know I had until I listened to the first episode played back.&lt;/p&gt;
&lt;p&gt;I&amp;#8217;m trying to get a handle on it, and the second episode was better than the first, so hopefully I can continue to improve.&lt;/p&gt;
&lt;p&gt;One of the reasons I jumped on the idea (aside from the fact that it just sounded fun) was because I &lt;em&gt;knew&lt;/em&gt; I would be horrible at it. I figured this was a good opportunity to learn how to be a better public speaker and push myself to conquer something outside of my comfort zone.&lt;/p&gt;
&lt;p&gt;It&amp;#8217;s sort of a personal-growth thing.&lt;/p&gt;
&lt;p&gt;Also, I need to invest in a good mic.&lt;/p&gt;
&lt;p&gt;Tune in, though! We have a lot of fun as we talk about things we&amp;#8217;re working on, problems we faced, techniques we used, and various technical, design, and accessibility issues that surround building apps that run everywhere.&lt;/p&gt;</description><link>http://kellishaver.tumblr.com/post/21369342887</link><guid>http://kellishaver.tumblr.com/post/21369342887</guid><pubDate>Thu, 19 Apr 2012 01:17:00 -0400</pubDate><category>work</category><category>dev</category><category>nitch</category><category>podcast</category></item><item><title>Happy Docs Rewrite &amp; More</title><description>&lt;p&gt;A few weeks ago, I &lt;a href="http://kellishaver.tumblr.com/post/18777943438/document-your-apis-with-happy-docs"&gt;posted&lt;/a&gt; about a little OSS CodeIgniter app I&amp;#8217;d built for documenting APIs called Happy Docs. That code is still available, but Happy Docs has now gone in a new direction.&lt;/p&gt;
&lt;p&gt;&lt;a href="http://happydocs.net"&gt;&lt;strong&gt;You can now use Happy Docs as a hosted service!&lt;/strong&gt;&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;I gave the system a complete rewrite, added multi-user support, improved private projects, and gave the interface a bit of an overhaul.&lt;/p&gt;
&lt;p&gt;The end result:&lt;/p&gt;
&lt;p&gt;&lt;img height="658" src="http://i.imgur.com/0ZCAZ.png" width="1009"/&gt;&lt;/p&gt;
&lt;p&gt;&lt;img height="658" src="http://i.imgur.com/No7AH.png" width="1009"/&gt;&lt;/p&gt;
&lt;p&gt;Happy&amp;#8217;s free to use, so if you build APIs, go ahead and sign up and give it a spin.&lt;/p&gt;
&lt;p&gt;Finally, I&amp;#8217;d like to talk a little about the rewrite.&lt;/p&gt;
&lt;p&gt;The initial, open source version was written in PHP. I did this primarily because deploying something with PHP is dead-simple and the software can live on pretty much any web host on the planet. For something you plan to give away, the ability to &amp;#8220;run anywhere&amp;#8221; is a huge benefit, in terms of reach, and in terms of support.&lt;/p&gt;
&lt;p&gt;When it came time to start building in more functionality, however, I began to seriously re-think the approach.&lt;/p&gt;
&lt;p&gt;Ultimately what it came down to was that:&lt;/p&gt;
&lt;ol&gt;&lt;li&gt;I was going to have to make a lot of changes if I wanted more integrated authoring/viewing (no separate admin interface) and user accounts, with multiple contributors per project.&lt;/li&gt;
&lt;li&gt;Since I was changing a lot of code anyway, I might as well just rewrite the thing.&lt;/li&gt;
&lt;li&gt;If I&amp;#8217;m going to be maintaining and expanding on the app, I&amp;#8217;d really prefer to be looking at and working with Ruby code over PHP.&lt;/li&gt;
&lt;/ol&gt;&lt;p&gt;That last bit mostly comes down to personal preference. I&amp;#8217;m not a PHP-hater. I know it quite well, use it often, and I don&amp;#8217;t mind developing in it, but when the project is mine, I&amp;#8217;m going to use the language I prefer to develop in (&lt;em&gt;Yeah, so, give it a month and maybe I&amp;#8217;ll end up rewriting v3 in Python&amp;#8230;&lt;/em&gt;).&lt;/p&gt;</description><link>http://kellishaver.tumblr.com/post/21368863439</link><guid>http://kellishaver.tumblr.com/post/21368863439</guid><pubDate>Thu, 19 Apr 2012 01:03:00 -0400</pubDate><category>happy docs</category><category>work</category><category>dev</category><category>api</category><category>ruby</category></item><item><title>Wrist-mounted Sonar for the Blind</title><description>&lt;a href="http://vimeo.com/27675622"&gt;Wrist-mounted Sonar for the Blind&lt;/a&gt;: &lt;p&gt;This is a pretty cool little piece of tech, and I can see how it could be very useful.&lt;/p&gt;
&lt;p&gt;Having some minor experience with using a white cane, I don’t think I could see this working as a total replacement. It’s amazing how much tactile feedback you can gain about your environment courtesy of a long stick, like a change in the texture of flooring that indicates a change between rooms, a sudden drop-off or stairs, a wrinkle in a rug that could be tripped over, or a kick plate that indicates a fire exit in a hallway.&lt;/p&gt;
&lt;p&gt;Instant feedback in certain situations is vital for safety, and I have my doubts that a device like Tacit would be as instantly telling as along cane when it comes to a sudden drop-off at the edge of the curb.&lt;/p&gt;
&lt;p&gt;That said, a device that would give you a much broader overall assessment of your surroundings that extends beyond the range of the white cane, or provides tactile feedback when interacting with nearby objects in a setting other than mobility (like picking up a pitcher of water) sounds amazing.&lt;/p&gt;
&lt;p&gt;Had my eye surgery last December not gone so well, I’d be queuing up to buy one of these.&lt;/p&gt;</description><link>http://kellishaver.tumblr.com/post/21218726716</link><guid>http://kellishaver.tumblr.com/post/21218726716</guid><pubDate>Mon, 16 Apr 2012 14:17:53 -0400</pubDate><category>accessibility</category><category>adaptive technology</category><category>gadgets</category><category>blindness</category></item><item><title>Old Bugs and Code that Doesn't Play Nice</title><description>&lt;p&gt;I was poking around at a little project earlier and ran into an interesting issue with Apache and a couple of enabled modules. Since I&amp;#8217;ve been asked about it, I thought I&amp;#8217;d go ahead and do a quick write-up of what happened and the process I went through to debug it.&lt;/p&gt;
&lt;p&gt;Basically, I was trying to follow a URL (on my local machine) to a non-existent file that would then rewrite to another location. - That&amp;#8217;s a pretty typical setup when creating SE-friendly links, routing to a controller class, etc. I&amp;#8217;ve been doing it for years without issue.&lt;/p&gt;
&lt;p&gt;Today, though, something was very broken. Let me paint the picture&amp;#8230;. Assume for a moment that I had the following setup:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;project/&lt;br/&gt;- .htaccess&lt;br/&gt;- hello.html&lt;br/&gt;- sub-folder/&lt;br/&gt;-- index.php &lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Inside the &lt;code&gt;.htaccess&lt;/code&gt; was a rewrite rule to redirect all traffic through &lt;code&gt;sub-folder/index.php&lt;/code&gt;, like so.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;RewriteEngline On &lt;br/&gt;RewriteRule ^(.*)$ sub-folder/index.php?file=$1 [L] &lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Now, whenever I would visit the URL by navigating to &lt;code&gt;http://localhost/project/&lt;/code&gt; the &lt;code&gt;index.php&lt;/code&gt; page would load as expected.&lt;/p&gt;
&lt;p&gt;Going to a non-existent sub-directory, like so: &lt;code&gt;http://localhost/project/aaa&lt;/code&gt; resulted in a 403 error.&lt;/p&gt;
&lt;p&gt;Ah, yes, of course. I had forgotten to enable &lt;code&gt;FollowSymLinks&lt;/code&gt; when setting up the rewrite rules. No big deal, a quick fix.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;Options +FollowSymLinks &lt;br/&gt;RewriteEngline On &lt;br/&gt;RewriteRule ^(.*)$ sub-folder/index.php?file=$1 [L] &lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;There, problem solved - or so I thought.&lt;/p&gt;
&lt;p&gt;Testing a second time, I navigated to &lt;code&gt;http://localhost/project/aaa&lt;/code&gt; and indeed, the &lt;code&gt;index.php&lt;/code&gt; file was loaded and told me that the value of &lt;code&gt;$_GET['file']&lt;/code&gt; was, in fact, &lt;code&gt;aaa&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;Cool.&lt;/p&gt;
&lt;p&gt;Then I tried to load the contents of the HTML file based on the value of &lt;code&gt;$file&lt;/code&gt;, like so:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;&amp;lt;? var_dump(file_get_contents('../'.$_GET['file'].'.html')); ?&amp;gt; &lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;I pointed my browser to &lt;code&gt;http://localhost/project/hello&lt;/code&gt; - intending to load &lt;code&gt;hello.html&lt;/code&gt; into the script.&lt;/p&gt;
&lt;p&gt;The result was an instant 404.&lt;/p&gt;
&lt;p&gt;Any time I would pass the variable containing a file name that didn&amp;#8217;t exist, the script would work. If I used a real file name, even though I wasn&amp;#8217;t providing an extension, I got a 404.&lt;/p&gt;
&lt;p&gt;This didn&amp;#8217;t make a lot of sense to me, because if I wasn&amp;#8217;t providing an extension, &lt;code&gt;hello&lt;/code&gt; was no more &lt;em&gt;real&lt;/em&gt; than &lt;code&gt;aaa&lt;/code&gt; was.&lt;/p&gt;
&lt;p&gt;Did you know you can log Apache rewrite calls? You probably did, but I&amp;#8217;ve never had caue to before, so I wasn&amp;#8217;t immediately aware that this functionality existed.&lt;/p&gt;
&lt;p&gt;I soon found it, though, and decided to turn it on (note: this has to be done in the vhost file - it can&amp;#8217;t be configured via &lt;code&gt;.htaccess&lt;/code&gt;)&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;RewriteLog /Users/kelli/www/rewrite.log &lt;br/&gt;RewriteLogLevel 2 &lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;I went back to the browser, loaded up both pages (&lt;code&gt;./aaa&lt;/code&gt; and &lt;code&gt;./hello&lt;/code&gt;) and compared log file entries.&lt;/p&gt;
&lt;p&gt;It turned out that when I would supply a file name of &lt;code&gt;aaa&lt;/code&gt; Apache would perform, rewriting the url to &lt;code&gt;sub-folder/index.php?file=aaa&lt;/code&gt; as expected and PHP would look for &lt;code&gt;../aaa.html&lt;/code&gt; (not finding it, of course).&lt;/p&gt;
&lt;p&gt;However, any time I tried to load &lt;code&gt;hello&lt;/code&gt; Apache would rewrite it as &lt;code&gt;sub-folder/index.php?file=hello.html&lt;/code&gt; which the script would then append &lt;code&gt;.html&lt;/code&gt; to, as I&amp;#8217;d written it to, causing the 404 page to load.&lt;/p&gt;
&lt;p&gt;So Apache was somehow guessing the file extension!&lt;/p&gt;
&lt;p&gt;To make a long story somewhat shorter, the little bit of magic responsible for this was &lt;a href="http://nanoweb.si.kz/manual/mod_multiviews.html"&gt;mod_multiviews&lt;/a&gt; - an Apache content-negotiation module. It seems there is a very clear conflict there between mod&lt;em&gt;_&lt;/em&gt;rewriite and mod_multiviews.&lt;/p&gt;
&lt;p&gt;The solution is to disable mod_multiviews:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;Options +FollowSymLinks -MultiViews &lt;br/&gt;RewriteEngline On &lt;br/&gt;RewriteRule ^(.*)$ sub-folder/index.php?file=$1 [L] &lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;There are a couple of interesting points about this issue.&lt;/p&gt;
&lt;ol&gt;&lt;li&gt;
&lt;p&gt;The conflict with mod_rewrite is a bug and it&amp;#8217;s nothing new. It turns out this has been around since at least 2003. &lt;em&gt;That&amp;#8217;s just plain depressing&lt;/em&gt;.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Despite this, I&amp;#8217;d never actually encountered the problem before, which is kind of amazing, since I&amp;#8217;ve been doing this kind of thing every day since, well, 2003.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;mod_multiviews is apparently enabled by default on some versions of Apache shipping on OS X. It could be in Snow Leopard, it could have been something that got changed when I upgraded to Lion. I&amp;#8217;m not sure. It&amp;#8217;s most likely the same in both.&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;&lt;p&gt;So there you have it. Hopefully this will help someone else out. At the very least, it gave me an interesting head-scratcher for a few minutes there while I dug around to find the problem.&lt;/p&gt;</description><link>http://kellishaver.tumblr.com/post/20832332455</link><guid>http://kellishaver.tumblr.com/post/20832332455</guid><pubDate>Tue, 10 Apr 2012 03:04:00 -0400</pubDate><category>apache</category><category>mod_rewrite</category><category>mod_multiviews</category><category>dev</category><category>os x</category></item><item><title>Bash Script to set up Sinatra App</title><description>&lt;a href="https://gist.github.com/2254604"&gt;Bash Script to set up Sinatra App&lt;/a&gt;: &lt;p&gt;Last week, I talked about &lt;a href="http://kellishaver.tumblr.com/post/19770596604/structuring-sinatra-applications"&gt;how I structure my Sinatra apps&lt;/a&gt;. Today I decided to automate that process, so here’s a small shell script to auto-generate the directory structure and a few essential base files.&lt;/p&gt;</description><link>http://kellishaver.tumblr.com/post/20184137082</link><guid>http://kellishaver.tumblr.com/post/20184137082</guid><pubDate>Fri, 30 Mar 2012 16:29:00 -0400</pubDate><category>bash</category><category>sinatra</category><category>ruby</category><category>work</category><category>automation</category><category>dev</category></item><item><title>This year’s big home-improvement project - fixing a couple...</title><description>&lt;img src="http://24.media.tumblr.com/tumblr_m1kg81sRW31rnnanlo1_500.png"/&gt;&lt;br/&gt; Replacing some bad wood in the floor&lt;br/&gt;&lt;br/&gt; &lt;img src="http://25.media.tumblr.com/tumblr_m1kg81sRW31rnnanlo2_500.png"/&gt;&lt;br/&gt; Patched.&lt;br/&gt;&lt;br/&gt; &lt;img src="http://25.media.tumblr.com/tumblr_m1kg81sRW31rnnanlo3_500.png"/&gt;&lt;br/&gt; The new flooring&lt;br/&gt;&lt;br/&gt; &lt;img src="http://25.media.tumblr.com/tumblr_m1kg81sRW31rnnanlo4_500.png"/&gt;&lt;br/&gt; A closer look&lt;br/&gt;&lt;br/&gt; &lt;img src="http://24.media.tumblr.com/tumblr_m1kg81sRW31rnnanlo5_500.png"/&gt;&lt;br/&gt; Goodbye, carpet.&lt;br/&gt;&lt;br/&gt; &lt;img src="http://25.media.tumblr.com/tumblr_m1kg81sRW31rnnanlo8_r1_500.png"/&gt;&lt;br/&gt; New flooring going down.&lt;br/&gt;&lt;br/&gt; &lt;img src="http://25.media.tumblr.com/tumblr_m1kg81sRW31rnnanlo9_r1_500.png"/&gt;&lt;br/&gt; &lt;br/&gt;&lt;img src="http://25.media.tumblr.com/tumblr_m1kg81sRW31rnnanlo10_r1_500.png"/&gt;&lt;br/&gt; &lt;br/&gt;&lt;img src="http://24.media.tumblr.com/tumblr_m1kg81sRW31rnnanlo11_r1_500.png"/&gt;&lt;br/&gt; &lt;br/&gt;&lt;img src="http://25.media.tumblr.com/tumblr_m1kg81sRW31rnnanlo12_r1_500.png"/&gt;&lt;br/&gt; &lt;br/&gt;&lt;p&gt;This year’s big home-improvement project - fixing a couple of bad spots in the floor and replacing the carpet and linoleum with a floating wood floor. I’ll add more photos as work progresses.&lt;/p&gt;</description><link>http://kellishaver.tumblr.com/post/20030755655</link><guid>http://kellishaver.tumblr.com/post/20030755655</guid><pubDate>Tue, 27 Mar 2012 19:07:00 -0400</pubDate><category>home</category><category>floor!</category></item><item><title>Structuring Sinatra Applications</title><description>&lt;p&gt;I thought it might be interesting to show how I structure my Sinatra applications. The app shown below (itemize.cc) is fairly small, so some of this may be a bit overkill, but I decided to go ahead and go with this level of structure, so I could easily expand the app in the future without having to worry about things getting disorganized.&lt;/p&gt;
&lt;p&gt;&lt;img height="550" src="http://i.imgur.com/ntOXe.png" width="192"/&gt;&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Things of note:&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;The &lt;code&gt;app.yml&lt;/code&gt; file is a config file that stores app-specific config items. For instance, in this case, it has the db connection string, wkhtmltopdf path, whether or not to run migrations on start up, and SMTP server details.&lt;/p&gt;
&lt;p&gt;Inside the main app, I typically pull in files by directory, so dropping in new files is painless.&lt;/p&gt;
&lt;p&gt;Often, I&amp;#8217;ll also include a &lt;code&gt;lang&lt;/code&gt; directory, with language files (e.g. &lt;code&gt;en-us.rb&lt;/code&gt;) and then set the default language in &lt;code&gt;app.yml&lt;/code&gt; as well.&lt;/p&gt;
&lt;p&gt;You can see it all come together like so:&lt;/p&gt;
&lt;pre&gt;require 'rubygems'
require 'sinatra'

configure do
  @@config = YAML.load_file("./app.yml") rescue nil || {}
end

Dir["./helpers/*.rb"].each {|file| require file}

DataMapper.setup(:default, ENV['DATABASE_URL'] || @@config["db_connection_string"])
DataMapper::Property::String.length(255)

Dir["./models/*.rb"].each {|file| require file} 
DataMapper.finalize 

if @@config["db_run_migrations"] == true 
  DataMapper.auto_migrate! 
  DataMapper.auto_upgrade! 
end

not_found do 
    erb :not_found 
end 

error do 
  erb :error 
end 

get '/' do 
  erb :index 
end 

Dir["./routes/*.rb"].each {|file| require file}
&lt;/pre&gt;</description><link>http://kellishaver.tumblr.com/post/19770596604</link><guid>http://kellishaver.tumblr.com/post/19770596604</guid><pubDate>Fri, 23 Mar 2012 00:21:00 -0400</pubDate><category>dev</category><category>sinatra</category></item><item><title>Re-Thinking The Way I Use Git</title><description>&lt;p&gt;Let me preface this by saying that I am probably late to the party on this one.&lt;/p&gt;
&lt;p&gt;I&amp;#8217;ve been using Git as my VCS for a long time, and I love it, but I have to admit, I&amp;#8217;ve been pretty disorganized in the way in which i use it. My Git workflow has been basically this:&lt;/p&gt;
&lt;p&gt;&lt;em&gt;Do some work, get to a point where things work, make a commit, push. &lt;/em&gt;&lt;/p&gt;
&lt;p&gt;I&amp;#8217;d make the occasional branch if was doing something radically different, like a big rewrite, or refactoring, throw in an occasional rebase or filter-branch, sure, but that was basically how it went.&lt;/p&gt;
&lt;p&gt;For most of the work I do, I am the only developer involved, or there may be one other person reviewing the code and making small commits. So this method has worked OK, in that let me do things like roll back to an earlier commit when I would screw something up.&lt;/p&gt;
&lt;p&gt;However, it never felt like I was utilizing Git to its fullest potential, and I&amp;#8217;d never really thought about Git as a tool for &lt;em&gt;documenting&lt;/em&gt; the development life-cycle of my code. That is, not until a few days ago when a good friend introduced me to &lt;a href="https://github.com/nvie/gitflow"&gt;Git-flow&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;Git-flow is both a methodology and a set of command-line tools to put that methodology into practice. In a nutshell, you work off of a development branch, creating hotfixes, support code, and features that follow a standard naming convention, and then get merged into the development branch. From there, you create releases, which them merge with the master, once you&amp;#8217;ve done all of your testing and tidying up. So your master branch never has anything other than stable release code merged into it and your development branch has a full history of all changes, all properly categorized.&lt;/p&gt;
&lt;p&gt;I&amp;#8217;ve only been at this a few days, but i absolutely love it. It takes a bit of getting used to, but I love the history that it creates, the built-in safety net, and the structure it imposes on the code. I&amp;#8217;m still adjusting, but I think it&amp;#8217;s definitely been a worthwhile switch that will save a lot of headache in the future, especially when multiple developers are involved.&lt;/p&gt;</description><link>http://kellishaver.tumblr.com/post/19762711836</link><guid>http://kellishaver.tumblr.com/post/19762711836</guid><pubDate>Thu, 22 Mar 2012 21:41:00 -0400</pubDate><category>git</category><category>git-flow</category><category>dev</category><category>code</category><category>work</category></item><item><title>Simple Invoicing for Small Jobs</title><description>&lt;p&gt;Say Hello to &lt;strong&gt;Itemize.cc&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;&lt;a href="http://itemize.cc"&gt;&lt;strong&gt;&lt;img alt="Itemize" height="345" src="http://i.imgur.com/Dt7VU.png" width="508"/&gt;&lt;/strong&gt;&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;I&amp;#8217;ve been working on a little personal project and yesterday I put it up on the web for all to see (and this morning i found and fixed a fatal bug&amp;#8230;.. go figure). So here it is: &lt;a href="http://itemise.cc" title="itemize.cc"&gt;&lt;strong&gt;Itemize.cc&lt;/strong&gt;&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;It&amp;#8217;s a very simple invoicing tool. It lets you quickly create and share invoices with your clients, and gives them a big, friendly button that they can click to pay your invoice through PayPal. It also gives them, and you, an option to download the invoice as a PDF file.&lt;/p&gt;
&lt;p&gt;That&amp;#8217;s about it. There&amp;#8217;s no sign-up or registration required. It&amp;#8217;s just very basic, simple invoicing.&lt;/p&gt;
&lt;p&gt;I still need to do some work on it, optimization for mobile, that kind of thing, so consider this a beta release.&lt;/p&gt;
&lt;p&gt;&lt;em&gt;Itemize was built with Sinatra.&lt;/em&gt;&lt;/p&gt;</description><link>http://kellishaver.tumblr.com/post/19730867399</link><guid>http://kellishaver.tumblr.com/post/19730867399</guid><pubDate>Thu, 22 Mar 2012 09:44:00 -0400</pubDate><category>dev</category><category>itemize</category><category>web apps</category><category>business</category></item></channel></rss>

