Don't Forget to Plant It!2011-10-08T11:56:05-04:00http://blog.sourcebender.com/Calvin YuMy Scoutmob Setup2011-08-24T23:42:00-04:00http://blog.sourcebender.com/my-scoutmob-setup<p>When I got my Scoutmob laptop (a quad-core 15' MBP) a couple of months ago, I wrote down everything I needed for making it ready for development. Here's my minimum viable development setup.</p>
<p><strong>Chrome (Dev Channel).</strong> At one point I was dabbling with writing Chrome Extensions, but there's not really a good reason to continue using the dev channel - 'cept that it's been pretty stable for me.</p>
<p><strong>iTerm2.</strong> I was a tmux user, and screen before that. Based on a recommendation from <a href="http://amro.co">Amro</a>, I've been using <a href="http://iterm2.com">iTerm2</a> for a few weeks now. Not totally sold on it, but it's a narrow win over tmux because of its ease of set up. I recommend downloading the <a href="http://ethanschoonover.com/solarized">Solarized theme</a> for it.</p>
<p><strong>Divvy.</strong> Great app for organizing windows. Use it all the time.</p>
<p><strong>Dropbox.</strong> Easy filesharing. More importantly, I need it for my next app, which is...</p>
<p><strong>1Password.</strong> I use this a lot already, but I need to use this more. You're only as safe as your weakest password.</p>
<p><strong>Homebrew.</strong> Best package manager for OSX. No reason to use anything else.</p>
<p><strong>XCode 4.</strong> Requirement for Homebrew, but starting some serious iOS development as well.</p>
<p><strong>TextMate.</strong> I vi occasionally, but TextMate is my editor of choice, especially for Ruby/Rails development. I recommend the Solarized theme for it as well.</p>
<p><strong>Twitter.</strong> I don't tweet enough to need anything more.</p>
<p><strong>Notational Velocity.</strong> Synchronized to Simplenote, I've built up enough notes over time to where this is tool is now an essential part of how I work. I've tried Evernote, but it wants to be more than I really need.</p>
<p><strong>iStat Menu.</strong> I tried to go without installing this initially - until my machine started getting sluggish. Essential for finding misbehaving processes.</p>
<p><strong>JumpCut.</strong> Clipboard history. Nice not having to worry about overwriting your current clipboard contents.</p>
<p><strong>Git (via Homebrew).</strong> I would use this even if I didn't need to push to a remote repository. It's the safety net for software developers.</p>
<p><strong>RVM.</strong> Nice that it makes upgrading Ruby a breeze, but Gemsets is the killer feature.</p>
<p><strong>MySQL (via Homebrew).</strong> It's what we use at Scoutmob.</p>
<p><strong>POW.</strong> Always available Rails apps. Install the powder gem to make managing applications a snap.</p>
<p><strong>Ack (via Homebrew).</strong> Super fast searches on the command line. Also, I recommend install the <a href="https://github.com/protocool/ackmate">Ackmate</a> plugin for TextMate.</p>
<p>My <a href="http://github.com/cyu/dotfiles">dotfiles</a> and <a href="http://github.com/cyu/dotvim">dotvim</a> Projects. Gets my terminal environment how I like it quickly.</p>
<p>In addition, there are two settings I do every time I setup a new machine:</p>
<ul>
<li><em>Turn off Dashboard.</em> Never use it. and it hijacks a very useful function key. Speaking of function keys...</li>
<li><em>Use F1, F2, ... Keys.</em> Instead of the feature keys. Developers pretty much live in those function keys.</li>
</ul>
Building an HTML5 Application, Part 3: Let's Take this Offline2011-04-28T00:00:00-04:00http://blog.sourcebender.com/building-an-html5-application-part3<p><em>Here's <a href="building-an-html5-application-part1.html">Part 1</a> and <a href="building-an-html5-application-part2.html">Part 2</a> of this series.</em></p>
<p>So after taking a break to spend time with family (yay!) and doing taxes (bleh!), I was able to spend some time on Thymer again. It's time to get it working offline.</p>
<p>Since it doesn't require a server, Thymer is a perfect candidate to be an offline application. In HTML5 you do this by telling the browser what files it should store locally so they are available offline. You do this in a file called the <strong>Cache Manifest</strong> file. The <a href="https://github.com/cyu/thymer/blob/part3/thymer.appcache">one for Thymer</a> looks like this:</p>
<div><figure role=code> <div class="highlight"><table cellpadding="0" cellspacing="0"><tr><td class="gutter"><pre class="line-numbers"><span class='line'>1</span>
<span class='line'>2</span>
<span class='line'>3</span>
<span class='line'>4</span>
<span class='line'>5</span>
<span class='line'>6</span>
<span class='line'>7</span>
<span class='line'>8</span>
<span class='line'>9</span>
<span class='line'>10</span>
<span class='line'>11</span>
<span class='line'>12</span>
</pre></td><td class='code' width='100%'><pre><code class='bash'><div class='line'>CACHE MANIFEST
</div><div class='line'><span class="c"># Revision: 10</span>
</div><div class='line'>alarm.mp3
</div><div class='line'>alarm.ogg
</div><div class='line'>alarm.wav
</div><div class='line'>clock_32x32.png
</div><div class='line'>index.html
</div><div class='line'>jquery-1.5.2.min.js
</div><div class='line'>minus_12x3.png
</div><div class='line'>reload_12x14.png
</div><div class='line'>thymer.css
</div><div class='line'>thymer.js
</div></code></pre></td></tr></table></div></figure></div>
<p>In this file, I list all the files I'm going to need when offline. It's important to note that files listed here must follow the <strong>same origin policy</strong>, meaning the files must be served from the same domain. I ran into this since I was using a Google hosted version of jQuery, and had to switch to self-hosting instead.</p>
<p>Another important thing to note is the second line of the manifest, where I specify the revision of the manifest in a comment. Browsers will only update their offline cache when the manifest changes, and not when the files listed in the manifest change. Common practice then is to update the revision number in the manifest to notify browsers to update their cache.</p>
<p>I ended up creating <a href="https://github.com/cyu/thymer/blob/part3/Rakefile">a rake task to generate the cache manifest</a>, updating the file list when new files are added and incrementing the revision number on any changes. To update the manifest, I just run the manifest task:</p>
<div><figure role=code> <div class="highlight"><table cellpadding="0" cellspacing="0"><tr><td class="gutter"><pre class="line-numbers"><span class='line'>1</span>
</pre></td><td class='code' width='100%'><pre><code class='bash'><div class='line'>rake thymer.appcache
</div></code></pre></td></tr></table></div></figure></div>
<p>Once the manifest file is created, I need to reference that manifest from the page. You do this by setting the <strong>manifest</strong> attribute on the <em>html</em> element:</p>
<div><figure role=code> <div class="highlight"><table cellpadding="0" cellspacing="0"><tr><td class="gutter"><pre class="line-numbers"><span class='line'>1</span>
<span class='line'>2</span>
</pre></td><td class='code' width='100%'><pre><code class='html'><div class='line'><span class="cp"><!doctype html></span>
</div><div class='line'><span class="nt"><html</span> <span class="na">manifest=</span><span class="s">"thymer.appcache"</span><span class="nt">></span>
</div></code></pre></td></tr></table></div></figure></div>
<h2>Handling Updates in the Browser</h2>
<p>Some browsers must be explicitly told to update the application cache when it sees an update. Here's how that is done:</p>
<div><figure role=code> <div class="highlight"><table cellpadding="0" cellspacing="0"><tr><td class="gutter"><pre class="line-numbers"><span class='line'>1</span>
<span class='line'>2</span>
<span class='line'>3</span>
<span class='line'>4</span>
<span class='line'>5</span>
<span class='line'>6</span>
<span class='line'>7</span>
<span class='line'>8</span>
<span class='line'>9</span>
<span class='line'>10</span>
<span class='line'>11</span>
<span class='line'>12</span>
<span class='line'>13</span>
</pre></td><td class='code' width='100%'><pre><code class='javascript'><div class='line'><span class="c1">// check for updates</span>
</div><div class='line'><span class="k">if</span> <span class="p">(</span><span class="s1">'applicationCache'</span> <span class="k">in</span> <span class="nb">window</span><span class="p">)</span> <span class="p">{</span>
</div><div class='line'> <span class="nx">$</span><span class="p">(</span><span class="s1">'#update-button'</span><span class="p">).</span><span class="nx">click</span><span class="p">(</span><span class="kd">function</span><span class="p">()</span> <span class="p">{</span>
</div><div class='line'> <span class="c1">// Ensure the browser uses the latest version of the code</span>
</div><div class='line'> <span class="nb">window</span><span class="p">.</span><span class="nx">applicationCache</span><span class="p">.</span><span class="nx">swapCache</span><span class="p">();</span>
</div><div class='line'> <span class="c1">// Reload the application</span>
</div><div class='line'> <span class="nb">window</span><span class="p">.</span><span class="nx">location</span><span class="p">.</span><span class="nx">reload</span><span class="p">();</span>
</div><div class='line'> <span class="p">});</span>
</div><div class='line'>
</div><div class='line'> <span class="nb">window</span><span class="p">.</span><span class="nx">applicationCache</span><span class="p">.</span><span class="nx">addEventListener</span><span class="p">(</span><span class="s1">'updateready'</span><span class="p">,</span> <span class="kd">function</span><span class="p">()</span> <span class="p">{</span>
</div><div class='line'> <span class="nx">$</span><span class="p">(</span><span class="s2">"#update"</span><span class="p">).</span><span class="nx">show</span><span class="p">();</span>
</div><div class='line'> <span class="p">},</span> <span class="kc">false</span><span class="p">);</span>
</div><div class='line'><span class="p">}</span>
</div></code></pre></td></tr></table></div></figure></div>
<p>In the above code, I just show a short message with a link to <em>swapCache()</em> and reload when a new update is available. Note that in some browsers, the update doesn't happen until <em>swapCache()</em> is called, while others it's automatic and all you really have to do is reload.</p>
<p>In JavaScript, you can detect your current offline/online status with <em>navigator.onLine</em>. You can also listen to <em>offline</em> and <em>online</em> events in the document body. For Thymer, I decided to check the manifest for updates when the browser comes back online:</p>
<div><figure role=code> <div class="highlight"><table cellpadding="0" cellspacing="0"><tr><td class="gutter"><pre class="line-numbers"><span class='line'>1</span>
<span class='line'>2</span>
<span class='line'>3</span>
<span class='line'>4</span>
</pre></td><td class='code' width='100%'><pre><code class='javascript'><div class='line'><span class="c1">// Check for updates when the browser comes back online</span>
</div><div class='line'><span class="nx">$</span><span class="p">(</span><span class="nb">document</span><span class="p">.</span><span class="nx">body</span><span class="p">).</span><span class="nx">bind</span><span class="p">(</span><span class="s1">'online'</span><span class="p">,</span> <span class="kd">function</span><span class="p">(){</span>
</div><div class='line'> <span class="nb">window</span><span class="p">.</span><span class="nx">applicationCache</span><span class="p">.</span><span class="nx">update</span><span class="p">();</span>
</div><div class='line'><span class="p">});</span>
</div></code></pre></td></tr></table></div></figure></div>
<p>In testing, it looks like listening for <em>online</em> event doesn't yet work in Chrome or Safari.</p>
<h2>Other Notes</h2>
<ul>
<li><p>When serving the manifest file, make sure it is served with a <em>Content-Type</em> header of 'text/cache-manifest'.</p></li>
<li><p>A lot of references and tutorials I found often named the manifest file <em>cache.manifest</em>, but it looks like <em>.appcache</em> will ultimately be <a href="http://www.w3.org/TR/html5/iana.html#text-cache-manifest">the standard extension</a>. This make sense, since the latter will probably have a lesser chance of name collisions with other file types.</p></li>
<li><p>I ended up declaring all versions of my audio file in the manifest, which is less that optimal since browsers would only use one of the three sources. I could dynamically generate the manifest based on User Agent, but that's less than ideal.</p></li>
<li><p>You can browse the contents of the offline cache in FF by going to <strong>about:cache</strong> from the address bar. In Chrome, you can see it for the current page under the Resources tab from within the Web Inspector. Didn't see where this information was available in Safari.</p></li>
</ul>
<h2>That's a Wrap</h2>
<p>That's it for Offline mode. In the next and most likely the final post in this series, I'm going to get Thymer onto the Chrome web store. If you want to learn more about Offline mode, HTML5Rocks has curated a <a href="http://www.html5rocks.com/features/offline">bunch of articles related to offline</a>. And here is <a href="https://github.com/cyu/thymer/tree/part3">the source code</a> referenced by this post.</p>
Building an HTML5 Application, Part 2: Web Notifications & <Audio>2011-04-05T00:00:00-04:00http://blog.sourcebender.com/building-an-html5-application-part2<p>This is part 2 in a series of posts walking through my experiences building <a href="http://blog.sourcebender.com/thymer">Thymer</a>, a single-page web application using HTML5 and related technologies. In <a href="building-an-html5-application-part1.html">my first post</a>, I got the basics of the application working and used the Local <strong>Storage API</strong> to store timers between browser refreshes. In this article, I'm going to talk about the <strong>Notification API</strong> and the <strong><audio></strong> tag.</p>
<h2>The Notification API</h2>
<p>Since my last post, I've made some minor fixes and UI tweaks to get Thymer feeling more like a usable application, but Thymer really wasn't going to be useful unless it can grab a user's attention when a timer ends. Recently Gmail introduced a new feature in Chrome where I get a nice little Growl-like notification whenever I received a Google Talk message. This was done using something called the Notification API. I wanted to see how easy it would be use this API to show a notification when a timer completes.</p>
<p>The Notification API is currently just a <a href="http://www.chromium.org/developers/design-documents/desktop-notifications/api-specification">draft spec</a>, and it's support is currently only limited to Chrome. Once the API gets wider adoption, there's a very good chance the API will change (at the minimum to rename the <em>window.webkitNotifications</em> object). Getting it working was super easy, basically by following <a href="http://www.html5rocks.com/tutorials/notifications/quick/">this tutorial here</a>.</p>
<p>First, I request permission to use the Notifications API when the first timer is created. It's important to note that <strong>you can only request permission on a user triggered event like a mouse click or key event.</strong> In Thymer's case, I request permission when the Enter key is pressed to submit a new timer:</p>
<div><figure role=code> <div class="highlight"><table cellpadding="0" cellspacing="0"><tr><td class="gutter"><pre class="line-numbers"><span class='line'>1</span>
<span class='line'>2</span>
<span class='line'>3</span>
<span class='line'>4</span>
<span class='line'>5</span>
<span class='line'>6</span>
<span class='line'>7</span>
<span class='line'>8</span>
<span class='line'>9</span>
<span class='line'>10</span>
<span class='line'>11</span>
<span class='line'>12</span>
<span class='line'>13</span>
<span class='line'>14</span>
<span class='line'>15</span>
<span class='line'>16</span>
<span class='line'>17</span>
<span class='line'>18</span>
</pre></td><td class='code' width='100%'><pre><code class='javascript'><div class='line'><span class="nx">$</span><span class="p">(</span><span class="s1">'#add-timer-form input'</span><span class="p">).</span><span class="nx">keypress</span><span class="p">(</span><span class="kd">function</span><span class="p">(</span><span class="nx">event</span><span class="p">)</span> <span class="p">{</span>
</div><div class='line'> <span class="k">if</span> <span class="p">(</span><span class="nx">event</span><span class="p">.</span><span class="nx">which</span> <span class="o">==</span> <span class="s1">'13'</span><span class="p">)</span> <span class="p">{</span> <span class="c1">// Enter key</span>
</div><div class='line'> <span class="nx">event</span><span class="p">.</span><span class="nx">preventDefault</span><span class="p">();</span>
</div><div class='line'> <span class="kd">var</span> <span class="nx">seconds</span> <span class="o">=</span> <span class="nb">parseInt</span><span class="p">(</span><span class="nx">$</span><span class="p">(</span><span class="s1">'#secs'</span><span class="p">).</span><span class="nx">val</span><span class="p">())</span> <span class="o">+</span>
</div><div class='line'> <span class="p">(</span><span class="nb">parseInt</span><span class="p">(</span><span class="nx">$</span><span class="p">(</span><span class="s1">'#mins'</span><span class="p">).</span><span class="nx">val</span><span class="p">())</span> <span class="o">*</span> <span class="mi">60</span><span class="p">)</span> <span class="o">+</span>
</div><div class='line'> <span class="p">(</span><span class="nb">parseInt</span><span class="p">(</span><span class="nx">$</span><span class="p">(</span><span class="s1">'#hours'</span><span class="p">).</span><span class="nx">val</span><span class="p">())</span> <span class="o">*</span> <span class="mi">60</span> <span class="o">*</span> <span class="mi">60</span><span class="p">);</span>
</div><div class='line'>
</div><div class='line'> <span class="nx">Thymer</span><span class="p">.</span><span class="nx">addTimer</span><span class="p">(</span><span class="k">new</span> <span class="nx">Timer</span><span class="p">(</span><span class="nx">$</span><span class="p">(</span><span class="s1">'#timer-name'</span><span class="p">).</span><span class="nx">val</span><span class="p">(),</span> <span class="nx">seconds</span><span class="p">));</span>
</div><div class='line'> <span class="nx">$</span><span class="p">(</span><span class="s1">'#timer-name'</span><span class="p">).</span><span class="nx">val</span><span class="p">(</span><span class="s1">''</span><span class="p">);</span>
</div><div class='line'>
</div><div class='line'> <span class="c1">// request notifications permission if API is supported. We do</span>
</div><div class='line'> <span class="c1">// this here because we can only do this on user triggered events.</span>
</div><div class='line'> <span class="k">if</span> <span class="p">(</span><span class="nb">window</span><span class="p">.</span><span class="nx">webkitNotifications</span> <span class="o">&&</span>
</div><div class='line'> <span class="nb">window</span><span class="p">.</span><span class="nx">webkitNotifications</span><span class="p">.</span><span class="nx">checkPermission</span><span class="p">()</span> <span class="o">!=</span> <span class="mi">0</span><span class="p">)</span> <span class="p">{</span>
</div><div class='line'> <span class="nb">window</span><span class="p">.</span><span class="nx">webkitNotifications</span><span class="p">.</span><span class="nx">requestPermission</span><span class="p">();</span>
</div><div class='line'> <span class="p">}</span>
</div><div class='line'> <span class="p">}</span>
</div><div class='line'><span class="p">})</span>
</div></code></pre></td></tr></table></div></figure></div>
<p>Then I create a notification when the timer ends:</p>
<div><figure role=code> <div class="highlight"><table cellpadding="0" cellspacing="0"><tr><td class="gutter"><pre class="line-numbers"><span class='line'>1</span>
<span class='line'>2</span>
<span class='line'>3</span>
<span class='line'>4</span>
<span class='line'>5</span>
<span class='line'>6</span>
<span class='line'>7</span>
<span class='line'>8</span>
<span class='line'>9</span>
</pre></td><td class='code' width='100%'><pre><code class='javascript'><div class='line'><span class="nx">_alarm</span><span class="o">:</span><span class="kd">function</span><span class="p">()</span> <span class="p">{</span>
</div><div class='line'> <span class="c1">// show a notification if the browser supports it.</span>
</div><div class='line'> <span class="k">if</span> <span class="p">(</span><span class="nb">window</span><span class="p">.</span><span class="nx">webkitNotifications</span> <span class="o">&&</span>
</div><div class='line'> <span class="nb">window</span><span class="p">.</span><span class="nx">webkitNotifications</span><span class="p">.</span><span class="nx">checkPermission</span><span class="p">()</span> <span class="o">==</span> <span class="mi">0</span><span class="p">)</span> <span class="p">{</span>
</div><div class='line'>
</div><div class='line'> <span class="nb">window</span><span class="p">.</span><span class="nx">webkitNotifications</span><span class="p">.</span><span class="nx">createNotification</span><span class="p">(</span><span class="s1">'clock_32x32.png'</span><span class="p">,</span>
</div><div class='line'> <span class="s1">'Thymer'</span><span class="p">,</span> <span class="s1">'"'</span> <span class="o">+</span> <span class="k">this</span><span class="p">.</span><span class="nx">name</span> <span class="o">+</span> <span class="s1">'" timer has completed'</span><span class="p">).</span><span class="nx">show</span><span class="p">();</span>
</div><div class='line'> <span class="p">}</span>
</div><div class='line'><span class="p">}</span>
</div></code></pre></td></tr></table></div></figure></div>
<p>Here's what the notification looks like:</p>
<p><img src="images/thymer-notification.jpg" alt="Thymer Notification in Chrome"></p>
<p>Some notes about the Notifications API:</p>
<ol>
<li>I had issues with <em>requestPermission()</em> when using the <em>file://</em> protocol where my permission got set as if I denied permission and didn't give me a way to reset it. If you're testing locally I recommend serving your page via localhost.</li>
<li>Once you approve or deny the notification permission, users will not receive the permission infobar again when you call <em>requestPermission()</em>. You can reset the permission in Chrome from <strong>Preferences</strong> > <strong>Under the Hood</strong> > <strong>Content Settings</strong> (Under <em>Privacy</em>).</li>
<li>The image parameter in <em>createNotification()</em> is not optional. Passing <strong>null</strong> will show a broken image.</li>
</ol>
<h2>The Audio Tag</h2>
<p>So now I have a way to grab user's attention when a timer completes in Chrome, but what about other browsers? How about playing an alarm sound when the timer completes? This turned out to be pretty easy with the <strong><audio></strong> tag:</p>
<div><figure role=code> <div class="highlight"><table cellpadding="0" cellspacing="0"><tr><td class="gutter"><pre class="line-numbers"><span class='line'>1</span>
<span class='line'>2</span>
<span class='line'>3</span>
<span class='line'>4</span>
<span class='line'>5</span>
</pre></td><td class='code' width='100%'><pre><code class='html'><div class='line'><span class="nt"><audio</span> <span class="na">id=</span><span class="s">alarm-sound</span><span class="nt">></span>
</div><div class='line'> <span class="nt"><source</span> <span class="na">src=</span><span class="s">alarm.mp3</span> <span class="na">type=</span><span class="s">audio/mpeg</span> <span class="nt">/></span>
</div><div class='line'> <span class="nt"><source</span> <span class="na">src=</span><span class="s">alarm.ogg</span> <span class="na">type=</span><span class="s">audio/ogg</span> <span class="nt">/></span>
</div><div class='line'> <span class="nt"><source</span> <span class="na">src=</span><span class="s">alarm.wav</span> <span class="na">type=</span><span class="s">audio/wav</span> <span class="nt">/></span>
</div><div class='line'><span class="nt"></audio></span>
</div></code></pre></td></tr></table></div></figure></div>
<p>This sets up a audio clip that I can access from JavaScript via the element id. Since I don't specify a <strong>controls</strong> or <strong>autoplay</strong> attribute, the audio won't play until I want it to. I'm also specifying the three different sources for audio (gotta love proprietary sound formats). Between MP3 & Ogg Vorbis, all modern browsers should be covered, but I'm going to throw a WAV in there for completeness anyway. I am concerned about how this will affect my footprint when I setup offline mode for Thymer, but that's an issue for a different post.</p>
<p>Playing the audio from JavaScript is cake:</p>
<div><figure role=code> <div class="highlight"><table cellpadding="0" cellspacing="0"><tr><td class="gutter"><pre class="line-numbers"><span class='line'>1</span>
<span class='line'>2</span>
<span class='line'>3</span>
<span class='line'>4</span>
<span class='line'>5</span>
<span class='line'>6</span>
<span class='line'>7</span>
<span class='line'>8</span>
<span class='line'>9</span>
</pre></td><td class='code' width='100%'><pre><code class='javascript'><div class='line'><span class="nx">_alarm</span><span class="o">:</span><span class="kd">function</span><span class="p">()</span> <span class="p">{</span>
</div><div class='line'> <span class="c1">// show a notification if the browser supports it.</span>
</div><div class='line'> <span class="k">if</span> <span class="p">(</span><span class="nb">window</span><span class="p">.</span><span class="nx">webkitNotifications</span> <span class="o">&&</span> <span class="nb">window</span><span class="p">.</span><span class="nx">webkitNotifications</span><span class="p">.</span><span class="nx">checkPermission</span><span class="p">()</span> <span class="o">==</span> <span class="mi">0</span><span class="p">)</span> <span class="p">{</span>
</div><div class='line'> <span class="nb">window</span><span class="p">.</span><span class="nx">webkitNotifications</span><span class="p">.</span><span class="nx">createNotification</span><span class="p">(</span><span class="s1">'clock_32x32.png'</span><span class="p">,</span> <span class="s1">'Thymer'</span><span class="p">,</span> <span class="s1">'"'</span> <span class="o">+</span> <span class="k">this</span><span class="p">.</span><span class="nx">name</span> <span class="o">+</span> <span class="s1">'" timer has completed'</span><span class="p">).</span><span class="nx">show</span><span class="p">();</span>
</div><div class='line'> <span class="p">}</span>
</div><div class='line'>
</div><div class='line'> <span class="c1">// play the alarm sound</span>
</div><div class='line'> <span class="nb">document</span><span class="p">.</span><span class="nx">getElementById</span><span class="p">(</span><span class="s1">'alarm-sound'</span><span class="p">).</span><span class="nx">play</span><span class="p">();</span>
</div><div class='line'><span class="p">}</span>
</div></code></pre></td></tr></table></div></figure></div>
<p>For more information about the Audio tag, I recommend <a href="http://html5doctor.com/native-audio-in-the-browser/">this article at <html>5doctor</a>. In that article they have a helpful table showing the browser support for the different audio formats.</p>
<p>This concludes Part 2 of this series. You can <a href="https://github.com/cyu/thymer/tree/part2">see the final code for this part here</a>. You can also see the final application on <a href="https://github.com/cyu/thymer">the master branch</a> as well as play with a <a href="http://blog.sourcebender.com/thymer">running version of Thymer here</a>. In part 3, I'm going to configure Thymer to run in offline mode by creating a cache manifest.</p>
Building an HTML5 Application, Part 1: Local Storage2011-03-29T00:00:00-04:00http://blog.sourcebender.com/building-an-html5-application-part1<p>So far, I've <a href="easy-storage-for-html5-applications.html">dabble with pieces of HTML5</a>, having build the rite-of-passage Todo application - now it's time to jump in and build a more complete application. In a series of posts, I'm going to build an HTML5 application from scratch and document my experiences along the way. Let's start off by exploring HTML5's Web Storage feature.</p>
<p>The application I've decided to build is a timer application - basically, you enter a time interval (in hours, minutes, seconds), and it'll notify you when that time has been reached. I start things off by creating the basics of what the application would look like via HTML:</p>
<div><figure role=code> <div class="highlight"><table cellpadding="0" cellspacing="0"><tr><td class="gutter"><pre class="line-numbers"><span class='line'>1</span>
<span class='line'>2</span>
<span class='line'>3</span>
<span class='line'>4</span>
<span class='line'>5</span>
<span class='line'>6</span>
<span class='line'>7</span>
<span class='line'>8</span>
<span class='line'>9</span>
<span class='line'>10</span>
<span class='line'>11</span>
<span class='line'>12</span>
<span class='line'>13</span>
<span class='line'>14</span>
<span class='line'>15</span>
<span class='line'>16</span>
<span class='line'>17</span>
<span class='line'>18</span>
<span class='line'>19</span>
<span class='line'>20</span>
<span class='line'>21</span>
<span class='line'>22</span>
</pre></td><td class='code' width='100%'><pre><code class='html'><div class='line'><span class="cp"><!doctype html></span>
</div><div class='line'><span class="nt"><html></span>
</div><div class='line'><span class="nt"><head></span>
</div><div class='line'><span class="nt"><title></span>Thymer<span class="nt"></title></span>
</div><div class='line'><span class="nt"></head></span>
</div><div class='line'><span class="nt"><body></span>
</div><div class='line'> <span class="nt"><header></span>
</div><div class='line'> <span class="nt"><h1></span>Thymer<span class="nt"></h1></span>
</div><div class='line'> <span class="nt"></header></span>
</div><div class='line'> <span class="nt"><div</span> <span class="na">id=</span><span class="s">content</span><span class="nt">></span>
</div><div class='line'> <span class="nt"><div</span> <span class="na">id=</span><span class="s">add-timer-form</span><span class="nt">></span>
</div><div class='line'> <span class="nt"><input</span> <span class="na">class=</span><span class="s">text</span> <span class="na">name=</span><span class="s">hours</span> <span class="na">value=</span><span class="s">00</span> <span class="na">size=</span><span class="s">2</span><span class="nt">></span> :
</div><div class='line'> <span class="nt"><input</span> <span class="na">class=</span><span class="s">text</span> <span class="na">name=</span><span class="s">mins</span> <span class="na">value=</span><span class="s">05</span> <span class="na">size=</span><span class="s">2</span><span class="nt">></span> :
</div><div class='line'> <span class="nt"><input</span> <span class="na">class=</span><span class="s">text</span> <span class="na">name=</span><span class="s">secs</span> <span class="na">value=</span><span class="s">00</span> <span class="na">size=</span><span class="s">2</span><span class="nt">></span>
</div><div class='line'> <span class="nt"><input</span> <span class="na">name=</span><span class="s">name</span> <span class="na">placeholder=</span><span class="s">'Timer Name'</span><span class="nt">></span>
</div><div class='line'> <span class="nt"><input</span> <span class="na">type=</span><span class="s">submit</span> <span class="na">value=</span><span class="s">'Create Timer'</span><span class="nt">></span>
</div><div class='line'> <span class="nt"></div></span>
</div><div class='line'> <span class="nt"><ul</span> <span class="na">id=</span><span class="s">timer-list</span><span class="nt">></ul></span>
</div><div class='line'> <span class="nt"></div></span>
</div><div class='line'> <span class="nt"><script </span><span class="na">src=</span><span class="s">thymer.js</span><span class="nt">></script></span>
</div><div class='line'><span class="nt"></body></span>
</div><div class='line'><span class="nt"></html></span>
</div></code></pre></td></tr></table></div></figure></div>
<p>Couple of notes about the above snippet:</p>
<ol>
<li><p>The <em>doctype</em> and lack of attribute quoting are standard to the spec. One of the key points of HTML5 was identify the lowest common denominator between the popular browsers and based the spec around them. In this case, the doctype of <em>html</em> was the minimum needed to be a valid doctype and attribute values do not need to be quoted unless there are whitespace in the value.</p></li>
<li><p>Line #21: I'm using the new <em>placeholder</em> attribute which puts a placeholder text in the text input when the input is empty and doesn't have value. Once you put focus on that text input and enter a value, the place holder text goes away.</p></li>
</ol>
<p>Next comes the <em>thymer.js</em> script. Nothing HTML5 specific here, I'm just putting the code here for completeness.</p>
<div><figure role=code> <div class="highlight"><table cellpadding="0" cellspacing="0"><tr><td class="gutter"><pre class="line-numbers"><span class='line'>1</span>
<span class='line'>2</span>
<span class='line'>3</span>
<span class='line'>4</span>
<span class='line'>5</span>
<span class='line'>6</span>
<span class='line'>7</span>
<span class='line'>8</span>
<span class='line'>9</span>
<span class='line'>10</span>
<span class='line'>11</span>
<span class='line'>12</span>
<span class='line'>13</span>
<span class='line'>14</span>
<span class='line'>15</span>
<span class='line'>16</span>
<span class='line'>17</span>
<span class='line'>18</span>
<span class='line'>19</span>
<span class='line'>20</span>
<span class='line'>21</span>
<span class='line'>22</span>
<span class='line'>23</span>
<span class='line'>24</span>
<span class='line'>25</span>
<span class='line'>26</span>
<span class='line'>27</span>
<span class='line'>28</span>
<span class='line'>29</span>
<span class='line'>30</span>
<span class='line'>31</span>
<span class='line'>32</span>
<span class='line'>33</span>
<span class='line'>34</span>
<span class='line'>35</span>
<span class='line'>36</span>
<span class='line'>37</span>
<span class='line'>38</span>
<span class='line'>39</span>
<span class='line'>40</span>
<span class='line'>41</span>
<span class='line'>42</span>
<span class='line'>43</span>
<span class='line'>44</span>
<span class='line'>45</span>
<span class='line'>46</span>
<span class='line'>47</span>
<span class='line'>48</span>
<span class='line'>49</span>
<span class='line'>50</span>
<span class='line'>51</span>
<span class='line'>52</span>
<span class='line'>53</span>
<span class='line'>54</span>
<span class='line'>55</span>
<span class='line'>56</span>
<span class='line'>57</span>
<span class='line'>58</span>
<span class='line'>59</span>
<span class='line'>60</span>
<span class='line'>61</span>
<span class='line'>62</span>
<span class='line'>63</span>
<span class='line'>64</span>
<span class='line'>65</span>
<span class='line'>66</span>
<span class='line'>67</span>
<span class='line'>68</span>
<span class='line'>69</span>
<span class='line'>70</span>
<span class='line'>71</span>
<span class='line'>72</span>
<span class='line'>73</span>
<span class='line'>74</span>
<span class='line'>75</span>
<span class='line'>76</span>
<span class='line'>77</span>
<span class='line'>78</span>
<span class='line'>79</span>
<span class='line'>80</span>
<span class='line'>81</span>
<span class='line'>82</span>
<span class='line'>83</span>
<span class='line'>84</span>
<span class='line'>85</span>
<span class='line'>86</span>
<span class='line'>87</span>
<span class='line'>88</span>
<span class='line'>89</span>
<span class='line'>90</span>
<span class='line'>91</span>
<span class='line'>92</span>
<span class='line'>93</span>
<span class='line'>94</span>
<span class='line'>95</span>
<span class='line'>96</span>
<span class='line'>97</span>
<span class='line'>98</span>
<span class='line'>99</span>
<span class='line'>100</span>
<span class='line'>101</span>
<span class='line'>102</span>
<span class='line'>103</span>
</pre></td><td class='code' width='100%'><pre><code class='javascript'><div class='line'><span class="nx">$</span><span class="p">().</span><span class="nx">ready</span><span class="p">(</span><span class="kd">function</span><span class="p">(){</span>
</div><div class='line'> <span class="kd">var</span> <span class="nx">timerList</span> <span class="o">=</span> <span class="nx">$</span><span class="p">(</span><span class="s1">'#timer-list'</span><span class="p">);</span>
</div><div class='line'> <span class="kd">var</span> <span class="nx">timers</span> <span class="o">=</span> <span class="p">[];</span>
</div><div class='line'>
</div><div class='line'> <span class="kd">var</span> <span class="nx">UpdateLoop</span> <span class="o">=</span> <span class="p">{</span>
</div><div class='line'> <span class="nx">start</span><span class="o">:</span><span class="kd">function</span><span class="p">()</span> <span class="p">{</span>
</div><div class='line'> <span class="k">if</span> <span class="p">(</span><span class="o">!</span><span class="k">this</span><span class="p">.</span><span class="nx">started</span><span class="p">)</span> <span class="p">{</span>
</div><div class='line'> <span class="k">this</span><span class="p">.</span><span class="nx">started</span> <span class="o">=</span> <span class="kc">true</span><span class="p">;</span>
</div><div class='line'> <span class="k">this</span><span class="p">.</span><span class="nx">interval</span> <span class="o">=</span> <span class="nx">setInterval</span><span class="p">(</span><span class="kd">function</span><span class="p">(){</span>
</div><div class='line'> <span class="nx">UpdateLoop</span><span class="p">.</span><span class="nx">update</span><span class="p">();</span>
</div><div class='line'> <span class="p">},</span><span class="mi">1000</span><span class="p">);</span>
</div><div class='line'> <span class="p">}</span>
</div><div class='line'> <span class="p">},</span>
</div><div class='line'> <span class="nx">update</span><span class="o">:</span><span class="kd">function</span><span class="p">()</span> <span class="p">{</span>
</div><div class='line'> <span class="k">if</span> <span class="p">(</span><span class="nx">timers</span><span class="p">.</span><span class="nx">length</span> <span class="o">></span> <span class="mi">0</span><span class="p">)</span> <span class="p">{</span>
</div><div class='line'> <span class="k">for</span> <span class="p">(</span><span class="kd">var</span> <span class="nx">i</span> <span class="k">in</span> <span class="nx">timers</span><span class="p">)</span> <span class="p">{</span>
</div><div class='line'> <span class="nx">timers</span><span class="p">[</span><span class="nx">i</span><span class="p">].</span><span class="nx">update</span><span class="p">();</span>
</div><div class='line'> <span class="p">}</span>
</div><div class='line'>
</div><div class='line'> <span class="p">}</span> <span class="k">else</span> <span class="p">{</span>
</div><div class='line'> <span class="nx">clearInterval</span><span class="p">(</span><span class="k">this</span><span class="p">.</span><span class="nx">interval</span><span class="p">);</span>
</div><div class='line'> <span class="k">this</span><span class="p">.</span><span class="nx">interval</span> <span class="o">=</span> <span class="kc">null</span><span class="p">;</span>
</div><div class='line'> <span class="k">this</span><span class="p">.</span><span class="nx">started</span> <span class="o">=</span> <span class="kc">false</span><span class="p">;</span>
</div><div class='line'> <span class="p">}</span>
</div><div class='line'> <span class="p">}</span>
</div><div class='line'> <span class="p">};</span>
</div><div class='line'>
</div><div class='line'> <span class="kd">var</span> <span class="nx">Timer</span> <span class="o">=</span> <span class="kd">function</span><span class="p">(</span><span class="nx">name</span><span class="p">,</span> <span class="nx">seconds</span><span class="p">)</span> <span class="p">{</span>
</div><div class='line'> <span class="k">this</span><span class="p">.</span><span class="nx">name</span> <span class="o">=</span> <span class="nx">name</span><span class="p">;</span>
</div><div class='line'> <span class="k">this</span><span class="p">.</span><span class="nx">seconds</span> <span class="o">=</span> <span class="nx">seconds</span><span class="p">;</span>
</div><div class='line'> <span class="k">this</span><span class="p">.</span><span class="nx">started</span> <span class="o">=</span> <span class="k">new</span> <span class="nb">Date</span><span class="p">().</span><span class="nx">getTime</span><span class="p">();</span>
</div><div class='line'> <span class="k">this</span><span class="p">.</span><span class="nx">finished</span> <span class="o">=</span> <span class="kc">false</span><span class="p">;</span>
</div><div class='line'> <span class="k">this</span><span class="p">.</span><span class="nx">start</span><span class="p">();</span>
</div><div class='line'> <span class="p">};</span>
</div><div class='line'> <span class="nx">Timer</span><span class="p">.</span><span class="nx">prototype</span> <span class="o">=</span> <span class="p">{</span>
</div><div class='line'> <span class="nx">start</span><span class="o">:</span><span class="kd">function</span><span class="p">()</span> <span class="p">{</span>
</div><div class='line'> <span class="k">this</span><span class="p">.</span><span class="nx">el</span> <span class="o">=</span> <span class="nx">$</span><span class="p">(</span><span class="s2">"<li>"</span> <span class="o">+</span> <span class="k">this</span><span class="p">.</span><span class="nx">buildDisplayString</span><span class="p">()</span> <span class="o">+</span> <span class="s2">"</li>"</span><span class="p">);</span>
</div><div class='line'> <span class="nx">timerList</span><span class="p">.</span><span class="nx">append</span><span class="p">(</span><span class="k">this</span><span class="p">.</span><span class="nx">el</span><span class="p">);</span>
</div><div class='line'> <span class="p">},</span>
</div><div class='line'> <span class="nx">update</span><span class="o">:</span><span class="kd">function</span><span class="p">()</span> <span class="p">{</span>
</div><div class='line'> <span class="k">if</span> <span class="p">(</span><span class="o">!</span><span class="k">this</span><span class="p">.</span><span class="nx">finished</span><span class="p">)</span> <span class="p">{</span>
</div><div class='line'> <span class="k">if</span> <span class="p">(</span><span class="k">this</span><span class="p">.</span><span class="nx">check</span><span class="p">())</span> <span class="p">{</span>
</div><div class='line'> <span class="kd">var</span> <span class="nx">timer</span> <span class="o">=</span> <span class="k">this</span><span class="p">;</span>
</div><div class='line'> <span class="kd">var</span> <span class="nx">removeLink</span> <span class="o">=</span> <span class="nx">$</span><span class="p">(</span><span class="s1">'<a href="#">remove</a>'</span><span class="p">).</span><span class="nx">click</span><span class="p">(</span><span class="kd">function</span><span class="p">(){</span>
</div><div class='line'> <span class="nx">timer</span><span class="p">.</span><span class="nx">remove</span><span class="p">();</span>
</div><div class='line'> <span class="p">});</span>
</div><div class='line'> <span class="k">this</span><span class="p">.</span><span class="nx">el</span><span class="p">.</span><span class="nx">text</span><span class="p">(</span><span class="s1">'ALARM!!! - '</span> <span class="o">+</span> <span class="k">this</span><span class="p">.</span><span class="nx">name</span> <span class="o">+</span> <span class="s1">' '</span><span class="p">);</span>
</div><div class='line'> <span class="k">this</span><span class="p">.</span><span class="nx">el</span><span class="p">.</span><span class="nx">append</span><span class="p">(</span><span class="nx">removeLink</span><span class="p">);</span>
</div><div class='line'> <span class="p">}</span> <span class="k">else</span> <span class="p">{</span>
</div><div class='line'> <span class="k">this</span><span class="p">.</span><span class="nx">el</span><span class="p">.</span><span class="nx">text</span><span class="p">(</span><span class="k">this</span><span class="p">.</span><span class="nx">buildDisplayString</span><span class="p">());</span>
</div><div class='line'> <span class="p">}</span>
</div><div class='line'> <span class="p">}</span>
</div><div class='line'> <span class="p">},</span>
</div><div class='line'> <span class="nx">remove</span><span class="o">:</span><span class="kd">function</span><span class="p">()</span> <span class="p">{</span>
</div><div class='line'> <span class="k">this</span><span class="p">.</span><span class="nx">el</span><span class="p">.</span><span class="nx">remove</span><span class="p">();</span>
</div><div class='line'> <span class="k">for</span> <span class="p">(</span><span class="kd">var</span> <span class="nx">i</span> <span class="k">in</span> <span class="nx">timers</span><span class="p">)</span> <span class="p">{</span>
</div><div class='line'> <span class="k">if</span> <span class="p">(</span><span class="nx">timers</span><span class="p">[</span><span class="nx">i</span><span class="p">]</span> <span class="o">==</span> <span class="k">this</span><span class="p">)</span> <span class="p">{</span>
</div><div class='line'> <span class="nx">timers</span><span class="p">.</span><span class="nx">splice</span><span class="p">(</span><span class="nx">i</span><span class="p">,</span><span class="mi">1</span><span class="p">);</span>
</div><div class='line'> <span class="k">break</span><span class="p">;</span>
</div><div class='line'> <span class="p">}</span>
</div><div class='line'> <span class="p">}</span>
</div><div class='line'> <span class="p">},</span>
</div><div class='line'> <span class="nx">check</span><span class="o">:</span><span class="kd">function</span><span class="p">()</span> <span class="p">{</span>
</div><div class='line'> <span class="kd">var</span> <span class="nx">remaining</span> <span class="o">=</span> <span class="k">this</span><span class="p">.</span><span class="nx">calculateRemaining</span><span class="p">();</span>
</div><div class='line'> <span class="k">if</span> <span class="p">(</span><span class="nx">remaining</span> <span class="o"><=</span> <span class="mi">0</span><span class="p">)</span> <span class="p">{</span>
</div><div class='line'> <span class="k">this</span><span class="p">.</span><span class="nx">finished</span> <span class="o">=</span> <span class="kc">true</span><span class="p">;</span>
</div><div class='line'> <span class="k">return</span> <span class="kc">true</span><span class="p">;</span>
</div><div class='line'> <span class="p">}</span>
</div><div class='line'> <span class="k">return</span> <span class="kc">false</span><span class="p">;</span>
</div><div class='line'> <span class="p">},</span>
</div><div class='line'> <span class="nx">calculateRemaining</span><span class="o">:</span><span class="kd">function</span><span class="p">()</span> <span class="p">{</span>
</div><div class='line'> <span class="k">return</span> <span class="k">this</span><span class="p">.</span><span class="nx">seconds</span> <span class="o">-</span> <span class="nb">Math</span><span class="p">.</span><span class="nx">floor</span><span class="p">((</span><span class="k">new</span> <span class="nb">Date</span><span class="p">().</span><span class="nx">getTime</span><span class="p">()</span> <span class="o">-</span> <span class="k">this</span><span class="p">.</span><span class="nx">started</span><span class="p">)</span> <span class="o">/</span> <span class="mi">1000</span><span class="p">);</span>
</div><div class='line'> <span class="p">},</span>
</div><div class='line'> <span class="nx">buildDisplayString</span><span class="o">:</span><span class="kd">function</span><span class="p">()</span> <span class="p">{</span>
</div><div class='line'> <span class="kd">var</span> <span class="nx">s</span> <span class="o">=</span> <span class="p">[];</span>
</div><div class='line'>
</div><div class='line'> <span class="kd">var</span> <span class="nx">remaining</span> <span class="o">=</span> <span class="k">this</span><span class="p">.</span><span class="nx">calculateRemaining</span><span class="p">();</span>
</div><div class='line'> <span class="nx">remaining</span> <span class="o">=</span> <span class="k">this</span><span class="p">.</span><span class="nx">buildTimeSegment</span><span class="p">(</span><span class="s1">'h'</span><span class="p">,</span> <span class="mi">60</span><span class="o">*</span><span class="mi">60</span><span class="p">,</span> <span class="nx">remaining</span><span class="p">,</span> <span class="nx">s</span><span class="p">);</span>
</div><div class='line'> <span class="nx">remaining</span> <span class="o">=</span> <span class="k">this</span><span class="p">.</span><span class="nx">buildTimeSegment</span><span class="p">(</span><span class="s1">'m'</span><span class="p">,</span> <span class="mi">60</span><span class="p">,</span> <span class="nx">remaining</span><span class="p">,</span> <span class="nx">s</span><span class="p">);</span>
</div><div class='line'> <span class="nx">s</span><span class="p">.</span><span class="nx">push</span><span class="p">(</span><span class="nx">remaining</span> <span class="o">+</span> <span class="s1">'s'</span><span class="p">);</span>
</div><div class='line'>
</div><div class='line'> <span class="k">return</span> <span class="nx">s</span><span class="p">.</span><span class="nx">join</span><span class="p">(</span><span class="s1">' '</span><span class="p">)</span> <span class="o">+</span> <span class="s1">' - '</span> <span class="o">+</span> <span class="k">this</span><span class="p">.</span><span class="nx">name</span><span class="p">;</span>
</div><div class='line'> <span class="p">},</span>
</div><div class='line'> <span class="nx">buildTimeSegment</span><span class="o">:</span><span class="kd">function</span><span class="p">(</span><span class="nx">suffix</span><span class="p">,</span> <span class="nx">divisor</span><span class="p">,</span> <span class="nx">secondsRemaining</span><span class="p">,</span> <span class="nx">segments</span><span class="p">)</span> <span class="p">{</span>
</div><div class='line'> <span class="kd">var</span> <span class="nx">units</span> <span class="o">=</span> <span class="nb">Math</span><span class="p">.</span><span class="nx">floor</span><span class="p">(</span><span class="nx">secondsRemaining</span> <span class="o">/</span> <span class="nx">divisor</span><span class="p">);</span>
</div><div class='line'> <span class="k">if</span> <span class="p">(</span><span class="nx">units</span> <span class="o">></span> <span class="mi">0</span><span class="p">)</span> <span class="p">{</span>
</div><div class='line'> <span class="nx">segments</span><span class="p">.</span><span class="nx">push</span><span class="p">(</span><span class="nx">units</span> <span class="o">+</span> <span class="nx">suffix</span><span class="p">);</span>
</div><div class='line'> <span class="k">return</span> <span class="nx">secondsRemaining</span> <span class="o">%</span> <span class="nx">divisor</span><span class="p">;</span>
</div><div class='line'> <span class="p">}</span>
</div><div class='line'> <span class="k">return</span> <span class="nx">secondsRemaining</span><span class="p">;</span>
</div><div class='line'> <span class="p">}</span>
</div><div class='line'> <span class="p">};</span>
</div><div class='line'>
</div><div class='line'> <span class="nx">$</span><span class="p">(</span><span class="s1">'#add-timer-form form'</span><span class="p">).</span><span class="nx">submit</span><span class="p">(</span><span class="kd">function</span><span class="p">(){</span>
</div><div class='line'> <span class="kd">var</span> <span class="nx">seconds</span> <span class="o">=</span> <span class="nb">parseInt</span><span class="p">(</span><span class="nx">$</span><span class="p">(</span><span class="s1">'#secs'</span><span class="p">).</span><span class="nx">val</span><span class="p">())</span> <span class="o">+</span>
</div><div class='line'> <span class="p">(</span><span class="nb">parseInt</span><span class="p">(</span><span class="nx">$</span><span class="p">(</span><span class="s1">'#mins'</span><span class="p">).</span><span class="nx">val</span><span class="p">())</span> <span class="o">*</span> <span class="mi">60</span><span class="p">)</span> <span class="o">+</span>
</div><div class='line'> <span class="p">(</span><span class="nb">parseInt</span><span class="p">(</span><span class="nx">$</span><span class="p">(</span><span class="s1">'#hours'</span><span class="p">).</span><span class="nx">val</span><span class="p">())</span> <span class="o">*</span> <span class="mi">60</span> <span class="o">*</span> <span class="mi">60</span><span class="p">);</span>
</div><div class='line'> <span class="kd">var</span> <span class="nx">timer</span> <span class="o">=</span> <span class="k">new</span> <span class="nx">Timer</span><span class="p">(</span><span class="nx">$</span><span class="p">(</span><span class="s1">'#timer-name'</span><span class="p">).</span><span class="nx">val</span><span class="p">(),</span> <span class="nx">seconds</span><span class="p">)</span>
</div><div class='line'> <span class="nx">timers</span><span class="p">.</span><span class="nx">push</span><span class="p">(</span><span class="nx">timer</span><span class="p">);</span>
</div><div class='line'> <span class="nx">UpdateLoop</span><span class="p">.</span><span class="nx">start</span><span class="p">();</span>
</div><div class='line'> <span class="k">return</span> <span class="kc">false</span><span class="p">;</span>
</div><div class='line'> <span class="p">});</span>
</div><div class='line'><span class="p">});</span>
</div></code></pre></td></tr></table></div></figure></div>
<p>I now have a basic timer application. Now for the HTML5 goodness.</p>
<p>For any desktop to be truly useful (desktop or web), it needs to be able to remember state from the last time you ran the application. For desktop applications, there are many different ways to do this, but web applications have always been limited to cookie or server-based storage. In HTML5, there are actually a few options for storing data, with the most commonly supported one being the <strong>Local Storage API</strong>. Local Storage API is a simple API that allows you to store data as key/value pairs. For my Thymer application, I'm going to store the timer array so any created timers will be persisted across browser reloads.</p>
<p>First, I update the <em>Timer</em> object so it can be converted to/from a save state:</p>
<div><figure role=code> <div class="highlight"><table cellpadding="0" cellspacing="0"><tr><td class="gutter"><pre class="line-numbers"><span class='line'>1</span>
<span class='line'>2</span>
<span class='line'>3</span>
<span class='line'>4</span>
<span class='line'>5</span>
<span class='line'>6</span>
<span class='line'>7</span>
<span class='line'>8</span>
<span class='line'>9</span>
<span class='line'>10</span>
<span class='line'>11</span>
<span class='line'>12</span>
<span class='line'>13</span>
<span class='line'>14</span>
<span class='line'>15</span>
<span class='line'>16</span>
<span class='line'>17</span>
<span class='line'>18</span>
<span class='line'>19</span>
</pre></td><td class='code' width='100%'><pre><code class='javascript'><div class='line'><span class="kd">var</span> <span class="nx">Timer</span> <span class="o">=</span> <span class="kd">function</span><span class="p">(</span><span class="nx">name</span><span class="p">,</span> <span class="nx">seconds</span><span class="p">,</span> <span class="nx">started</span><span class="p">,</span> <span class="nx">finished</span><span class="p">)</span> <span class="p">{</span>
</div><div class='line'> <span class="k">this</span><span class="p">.</span><span class="nx">name</span> <span class="o">=</span> <span class="nx">name</span><span class="p">;</span>
</div><div class='line'> <span class="k">this</span><span class="p">.</span><span class="nx">seconds</span> <span class="o">=</span> <span class="nx">seconds</span><span class="p">;</span>
</div><div class='line'> <span class="k">this</span><span class="p">.</span><span class="nx">started</span> <span class="o">=</span> <span class="nx">started</span> <span class="o">?</span> <span class="nx">started</span> <span class="o">:</span> <span class="k">new</span> <span class="nb">Date</span><span class="p">().</span><span class="nx">getTime</span><span class="p">();</span>
</div><div class='line'> <span class="k">this</span><span class="p">.</span><span class="nx">finished</span> <span class="o">=</span> <span class="nx">finished</span> <span class="o">||</span> <span class="kc">false</span><span class="p">;</span>
</div><div class='line'> <span class="k">this</span><span class="p">.</span><span class="nx">start</span><span class="p">();</span>
</div><div class='line'><span class="p">};</span>
</div><div class='line'><span class="nx">Timer</span><span class="p">.</span><span class="nx">prototype</span> <span class="o">=</span> <span class="p">{</span>
</div><div class='line'> <span class="c1">// the rest of the Timer prototype...</span>
</div><div class='line'>
</div><div class='line'> <span class="nx">toObject</span><span class="o">:</span><span class="kd">function</span><span class="p">()</span> <span class="p">{</span>
</div><div class='line'> <span class="k">return</span> <span class="p">{</span>
</div><div class='line'> <span class="nx">name</span><span class="o">:</span> <span class="k">this</span><span class="p">.</span><span class="nx">name</span><span class="p">,</span>
</div><div class='line'> <span class="nx">seconds</span><span class="o">:</span> <span class="k">this</span><span class="p">.</span><span class="nx">seconds</span><span class="p">,</span>
</div><div class='line'> <span class="nx">started</span><span class="o">:</span> <span class="k">this</span><span class="p">.</span><span class="nx">started</span><span class="p">,</span>
</div><div class='line'> <span class="nx">finished</span><span class="o">:</span> <span class="k">this</span><span class="p">.</span><span class="nx">finished</span>
</div><div class='line'> <span class="p">};</span>
</div><div class='line'> <span class="p">}</span>
</div><div class='line'><span class="p">}</span>
</div></code></pre></td></tr></table></div></figure></div>
<p>Then I save the timer array every time a new timer is created, and load the stored timers when the application starts:</p>
<div><figure role=code> <div class="highlight"><table cellpadding="0" cellspacing="0"><tr><td class="gutter"><pre class="line-numbers"><span class='line'>1</span>
<span class='line'>2</span>
<span class='line'>3</span>
<span class='line'>4</span>
<span class='line'>5</span>
<span class='line'>6</span>
<span class='line'>7</span>
<span class='line'>8</span>
<span class='line'>9</span>
<span class='line'>10</span>
<span class='line'>11</span>
<span class='line'>12</span>
<span class='line'>13</span>
<span class='line'>14</span>
<span class='line'>15</span>
<span class='line'>16</span>
<span class='line'>17</span>
<span class='line'>18</span>
<span class='line'>19</span>
<span class='line'>20</span>
<span class='line'>21</span>
<span class='line'>22</span>
<span class='line'>23</span>
<span class='line'>24</span>
<span class='line'>25</span>
<span class='line'>26</span>
<span class='line'>27</span>
<span class='line'>28</span>
<span class='line'>29</span>
<span class='line'>30</span>
<span class='line'>31</span>
<span class='line'>32</span>
<span class='line'>33</span>
</pre></td><td class='code' width='100%'><pre><code class='javascript'><div class='line'><span class="nx">$</span><span class="p">(</span><span class="s1">'#add-timer-form form'</span><span class="p">).</span><span class="nx">submit</span><span class="p">(</span><span class="kd">function</span><span class="p">(){</span>
</div><div class='line'> <span class="kd">var</span> <span class="nx">seconds</span> <span class="o">=</span> <span class="nb">parseInt</span><span class="p">(</span><span class="nx">$</span><span class="p">(</span><span class="s1">'#secs'</span><span class="p">).</span><span class="nx">val</span><span class="p">())</span> <span class="o">+</span>
</div><div class='line'> <span class="p">(</span><span class="nb">parseInt</span><span class="p">(</span><span class="nx">$</span><span class="p">(</span><span class="s1">'#mins'</span><span class="p">).</span><span class="nx">val</span><span class="p">())</span> <span class="o">*</span> <span class="mi">60</span><span class="p">)</span> <span class="o">+</span>
</div><div class='line'> <span class="p">(</span><span class="nb">parseInt</span><span class="p">(</span><span class="nx">$</span><span class="p">(</span><span class="s1">'#hours'</span><span class="p">).</span><span class="nx">val</span><span class="p">())</span> <span class="o">*</span> <span class="mi">60</span> <span class="o">*</span> <span class="mi">60</span><span class="p">);</span>
</div><div class='line'>
</div><div class='line'> <span class="kd">var</span> <span class="nx">timer</span> <span class="o">=</span> <span class="k">new</span> <span class="nx">Timer</span><span class="p">(</span><span class="nx">$</span><span class="p">(</span><span class="s1">'#timer-name'</span><span class="p">).</span><span class="nx">val</span><span class="p">(),</span> <span class="nx">seconds</span><span class="p">)</span>
</div><div class='line'> <span class="nx">timers</span><span class="p">.</span><span class="nx">push</span><span class="p">(</span><span class="nx">timer</span><span class="p">);</span>
</div><div class='line'>
</div><div class='line'> <span class="c1">// store timers</span>
</div><div class='line'> <span class="k">if</span> <span class="p">(</span><span class="s1">'localStorage'</span> <span class="k">in</span> <span class="nb">window</span><span class="p">)</span> <span class="p">{</span>
</div><div class='line'> <span class="kd">var</span> <span class="nx">arr</span> <span class="o">=</span> <span class="p">[];</span>
</div><div class='line'> <span class="k">for</span> <span class="p">(</span><span class="kd">var</span> <span class="nx">i</span> <span class="k">in</span> <span class="nx">timers</span><span class="p">)</span> <span class="p">{</span>
</div><div class='line'> <span class="nx">arr</span><span class="p">.</span><span class="nx">push</span><span class="p">(</span><span class="nx">timers</span><span class="p">[</span><span class="nx">i</span><span class="p">].</span><span class="nx">toObject</span><span class="p">());</span>
</div><div class='line'> <span class="p">}</span>
</div><div class='line'> <span class="nb">window</span><span class="p">.</span><span class="nx">localStorage</span><span class="p">.</span><span class="nx">setItem</span><span class="p">(</span><span class="s1">'timers'</span><span class="p">,</span> <span class="nx">JSON</span><span class="p">.</span><span class="nx">stringify</span><span class="p">(</span><span class="nx">arr</span><span class="p">));</span>
</div><div class='line'> <span class="p">}</span>
</div><div class='line'>
</div><div class='line'> <span class="nx">UpdateLoop</span><span class="p">.</span><span class="nx">start</span><span class="p">();</span>
</div><div class='line'> <span class="k">return</span> <span class="kc">false</span><span class="p">;</span>
</div><div class='line'><span class="p">});</span>
</div><div class='line'>
</div><div class='line'><span class="c1">// load stored timers</span>
</div><div class='line'><span class="k">if</span> <span class="p">(</span><span class="s1">'localStorage'</span> <span class="k">in</span> <span class="nb">window</span><span class="p">)</span> <span class="p">{</span>
</div><div class='line'> <span class="kd">var</span> <span class="nx">timersData</span> <span class="o">=</span> <span class="nb">window</span><span class="p">.</span><span class="nx">localStorage</span><span class="p">.</span><span class="nx">getItem</span><span class="p">(</span><span class="s1">'timers'</span><span class="p">);</span>
</div><div class='line'> <span class="k">if</span> <span class="p">(</span><span class="nx">timersData</span><span class="p">)</span> <span class="p">{</span>
</div><div class='line'> <span class="kd">var</span> <span class="nx">timersData</span> <span class="o">=</span> <span class="nx">JSON</span><span class="p">.</span><span class="nx">parse</span><span class="p">(</span><span class="nx">timersData</span><span class="p">);</span>
</div><div class='line'> <span class="k">for</span> <span class="p">(</span><span class="kd">var</span> <span class="nx">i</span> <span class="k">in</span> <span class="nx">timersData</span><span class="p">)</span> <span class="p">{</span>
</div><div class='line'> <span class="kd">var</span> <span class="nx">t</span> <span class="o">=</span> <span class="nx">timersData</span><span class="p">[</span><span class="nx">i</span><span class="p">];</span>
</div><div class='line'> <span class="nx">timers</span><span class="p">.</span><span class="nx">push</span><span class="p">(</span><span class="k">new</span> <span class="nx">Timer</span><span class="p">(</span><span class="nx">t</span><span class="p">.</span><span class="nx">name</span><span class="p">,</span> <span class="nx">t</span><span class="p">.</span><span class="nx">seconds</span><span class="p">,</span> <span class="nx">t</span><span class="p">.</span><span class="nx">started</span><span class="p">,</span> <span class="nx">t</span><span class="p">.</span><span class="nx">finished</span><span class="p">));</span>
</div><div class='line'> <span class="p">}</span>
</div><div class='line'> <span class="nx">UpdateLoop</span><span class="p">.</span><span class="nx">start</span><span class="p">();</span>
</div><div class='line'> <span class="p">}</span>
</div><div class='line'><span class="p">}</span>
</div></code></pre></td></tr></table></div></figure></div>
<p>Some notes about the <strong>Local Storage API</strong>:</p>
<ol>
<li><p><em>localStorage</em> only stores string values, so use <em>JSON.stringify()</em> and <em>JSON.parse()</em> to convert your data to/from JSON.</p></li>
<li><p>In addition to <em>setItem</em> and <em>getItem</em>, there are also <em>clear</em> and <em>removeItem</em> functions available.</p></li>
<li><p>You can also access stored values directly as properties on the <em>localStorage</em> object (e.g. <em>window.localStorage.timers</em>).</p></li>
<li><p>Values are stored to an <em>origin</em>, which is the combination of <strong>scheme (http/https) + host + port</strong>. This means other pages on that server could access those stored values, so attention should be paid to avoid naming collisions.</p></li>
<li><p>Origin pinning also means that if a page can be viewed via multiple domains (www.example.com and example.com for example), you won't be able to access stored values between those two domains. Consider using permanent redirects to get users under one domain.</p></li>
<li><p>Local Storage doesn't seem to work in Firefox with pages served via the <em>file://</em> protocol - you'll need to serve your page from a web server when testing locally.</p></li>
</ol>
<p>So that's it for the part 1. A lot of this is common knowledge for those who have already read up on HTML5, but now that we've setup the basic of the application, we can move on to more interesting aspects of HTML5. In part 2, I'll cover playing audio natively and using Chrome's notification API.</p>
<p>You can get <a href="https://github.com/cyu/thymer/tree/part1">the final code for Part I here</a>. If you're interested in going deeper into HTML5, I highly recommend visiting Mark Pilgrim's <a href="http://diveintohtml5.org/">Dive Into HTML5</a> and getting his <em>HTML5: Up and Running</em> book.</p>
Stor.IO and Backbone.js2011-03-08T00:00:00-05:00http://blog.sourcebender.com/storio-and-backbonejs<p><a href="http://documentcloud.github.com/backbone/">Backbone.js</a> is a nice JavaScript framework that provides some basic structure to your JavaScript application. What's nice about it is that it does this while at the same time being flexible and letting you make some design decisions on your own. One of those is how you choose store you data.</p>
<p>While looking at Backbone's extensive documentation, I couldn't help but notice that they had an example todo application that used localStorage as a backing store. And since the <a href="easy-storage-for-html5-applications.html">Stor.IO</a> API so closely resembles the localStorage API, it was easy to adapt the example application to use Stor.IO as a backend. Here's the <a href="http://github.com/cyu/storio-backbone-todos">Github Project</a>. And you can <a href="http://cyu.github.com/storio-backbone-todos">play around with the application here</a>.</p>
<p>In other news, I've setup <a href="https://convore.com/storio/">a group on Convore to discuss Stor.IO</a>. I'm on there pretty regularly, so there's a good chance you'll find me there if you have questions.</p>
Easy Storage for HTML5 Applications2011-02-22T00:00:00-05:00http://blog.sourcebender.com/easy-storage-for-html5-applications<p>I'm a big believer that HTML5 will be a game changer. It reminds me of the introduction of <a href="http://en.wikipedia.org/wiki/Dhtml">DHTML</a> over a decade ago, and although DHTML the term never really caught on, it definitely paved the way to the eventual discovery of Ajax and an integral part of what is considered standard web application practice today.</p>
<p>A core component of HTML5 is the <a href="http://diveintohtml5.org/storage.html">localStorage API</a>. With it, you can write a fairly useful application — all in JavaScript. There are limitations however: without server-side storage, your data is trapped to that browser, and with the diversity of devices we use today (laptop, smartphones and tablets), applications need to be available wherever you have a browser.</p>
<p>So what if you can write an application, still all in client-side JavaScript, but store data centrally so it can be accessible on all devices and browsers? This is what <a href="http://stor.io">Stor.IO</a> does. Here's some example code:</p>
<div><figure role=code> <div class="highlight"><table cellpadding="0" cellspacing="0"><tr><td class="gutter"><pre class="line-numbers"><span class='line'>1</span>
<span class='line'>2</span>
<span class='line'>3</span>
<span class='line'>4</span>
<span class='line'>5</span>
<span class='line'>6</span>
<span class='line'>7</span>
<span class='line'>8</span>
</pre></td><td class='code' width='100%'><pre><code class='javascript'><div class='line'><span class="o"><</span><span class="nx">script</span> <span class="nx">src</span><span class="o">=</span><span class="s2">"http://stor.io/app_storage.js"</span><span class="o">><</span><span class="err">/script></span>
</div><div class='line'><span class="o"><</span><span class="nx">script</span><span class="o">></span>
</div><div class='line'> <span class="nb">window</span><span class="p">.</span><span class="nx">appStorage</span><span class="p">.</span><span class="nx">$</span><span class="p">.</span><span class="nx">connect</span><span class="p">({</span><span class="nx">email</span><span class="o">:</span> <span class="s1">'my@emailaddress.com'</span><span class="p">});</span>
</div><div class='line'> <span class="c1">//window.appStorage.$.connect({twitter:true}); // use Twitter OAuth</span>
</div><div class='line'> <span class="nb">window</span><span class="p">.</span><span class="nx">appStorage</span><span class="p">.</span><span class="nx">setItem</span><span class="p">(</span><span class="s1">'foo'</span><span class="p">,</span> <span class="s1">'bar'</span><span class="p">);</span>
</div><div class='line'> <span class="nb">window</span><span class="p">.</span><span class="nx">appStorage</span><span class="p">.</span><span class="nx">getItem</span><span class="p">(</span><span class="s1">'foo'</span><span class="p">);</span> <span class="c1">// "bar"</span>
</div><div class='line'> <span class="nb">window</span><span class="p">.</span><span class="nx">appStorage</span><span class="p">.</span><span class="nx">foo</span><span class="p">;</span> <span class="c1">// "bar"</span>
</div><div class='line'><span class="o"><</span><span class="err">/script></span>
</div></code></pre></td></tr></table></div></figure></div>
<p>Including the <em>app_storage.js</em> script creates the <strong>appStorage</strong> object on the browser window. Next, you call the <em>connect</em> method on the server object (<em>$</em>), passing in the identity of the user. Currently, an email-only method of authenticating is available, as well as Twitter OAuth. Once connected, the appStorage API behaves essentially like the localStorage API. And it doesn't force you to store strings like localStorage does, meaning you don't need to serialize/deserialize objects to/from JSON.</p>
<p>For reading data from Stor.IO, you'll need to wait on the server to identify the user and retrieve their data, and there's a <em>ready()</em> handler available for that:</p>
<div><figure role=code> <div class="highlight"><table cellpadding="0" cellspacing="0"><tr><td class="gutter"><pre class="line-numbers"><span class='line'>1</span>
<span class='line'>2</span>
<span class='line'>3</span>
<span class='line'>4</span>
<span class='line'>5</span>
<span class='line'>6</span>
<span class='line'>7</span>
<span class='line'>8</span>
</pre></td><td class='code' width='100%'><pre><code class='javascript'><div class='line'><span class="o"><</span><span class="nx">script</span><span class="o">></span>
</div><div class='line'><span class="nb">window</span><span class="p">.</span><span class="nx">appStorage</span><span class="p">.</span><span class="nx">$</span><span class="p">.</span><span class="nx">ready</span><span class="p">(</span><span class="kd">function</span><span class="p">(){</span>
</div><div class='line'> <span class="kd">var</span> <span class="nx">tasks</span> <span class="o">=</span> <span class="nb">window</span><span class="p">.</span><span class="nx">appStorage</span><span class="p">.</span><span class="nx">tasks</span><span class="p">;</span>
</div><div class='line'> <span class="k">for</span> <span class="p">(</span><span class="kd">var</span> <span class="nx">i</span><span class="o">=</span><span class="mi">0</span><span class="p">;</span> <span class="nx">i</span><span class="o"><</span><span class="nx">tasks</span><span class="p">.</span><span class="nx">length</span><span class="p">;</span> <span class="nx">i</span><span class="o">++</span><span class="p">)</span> <span class="p">{</span>
</div><div class='line'> <span class="nx">console</span><span class="p">.</span><span class="nx">log</span><span class="p">(</span><span class="nx">tasks</span><span class="p">[</span><span class="nx">i</span><span class="p">].</span><span class="nx">text</span><span class="p">);</span>
</div><div class='line'> <span class="p">}</span>
</div><div class='line'><span class="p">});</span>
</div><div class='line'><span class="o"><</span><span class="err">/script></span>
</div></code></pre></td></tr></table></div></figure></div>
<p>Underneath, how Stor.IO works is that it creates an iframe and uses the <a href="http://ajaxian.com/archives/cross-window-messaging-with-html-5-postmessage">postMessage API</a> to work around the same origin policy that handicaps Ajax calls. The server is Sinatra app with the data being stored in MongoDB. The data is isolated by the user identity and the application path, which is a substring the current URL up to the first directory path, so http://localhost/app1 and http://localhost/app2 will actually hold different data sets.</p>
<p>To flush out the initial implementation, I wrote a very rudimentary <a href="http://blog.sourcebender.com/todojs">todo application</a>. It's actually hosted on Github pages, so you can see how everything works from the <a href="http://github.com/cyu/todojs">project page</a>. Even better is that you can fork the project and immediately have groundwork on your own todo application, with hosting courtesy of Github.</p>
<p>Please give it a shot and join the <a href="http://groups.google.com/group/storio">Google Group</a> if you have questions or are interested in further development.</p>
Leaving WordPress for Jekyll2011-02-12T00:00:00-05:00http://blog.sourcebender.com/leaving-wordpress-for-jekyll<p>Taking <a href="http://paulstamatiou.com/how-to-wordpress-to-jekyll">inspiration from Paul</a>, I've switched my blog from a self-hosted WordPress one to a static HTML site using <a href="http://github.com/mojombo/jekyll">Jekyll</a>. I didn't initially look at Jekyll because it seemed like the migration process was going to take a lot of work, but eventually settled on it because:</p>
<ol>
<li>I couldn't easily migrate my blog data to Tumblr, and</li>
<li>Although I was able to migrate to Posterous, the customization options was limited and buggy at times. Also, some of the HTML it generated (i.e. comments) looked pretty gnarly to style.</li>
</ol>
<p>So I gave Jekyll a shot, and came out quite happy with the results. The migration script to convert my WP data worked as well as expected, and since the results were plain text it was easy to make the necessary tweaks to complete the migration.</p>
<p>In the switch over, I elected not to migrate all the sidebar widgets, instead putting the focus primarily on the articles. At the end of the day, that's really want I want people to get out of this site.</p>
<p><em>Apologies to my RSS subscribers (all 70 of you) who will likely see all my posts as new posts.</em></p>
Loading CouchDB Views from Source Files2010-07-28T00:00:00-04:00http://blog.sourcebender.com/2010/07/28/loading-couchdb-views<p>I've been doing a lot work with CouchDB lately for Socialytics, and which means writing map/reduce functions in JavaScript for building views. I was beginning to have a healthy set of views, it made sense to have this code in version control and be able to load these views to a CouchDB instance via the command line. Not finding anything like this on the interwebs, I decided to come with something on my own.</p>
<p>My first pass at it was a Ruby implementation, but was fragile since it found the map and reduce functions via regular expressions. This past weekend I decided to give a go with a new implementation using <a href="http://nodejs.org">Node.js</a>. Since I'm now using JavaScript, I no longer had to use regular expressions to sniff out the map/reduce functions - I can just load the scripts up as code. Another additional benefit is that now the view code got parsed, so I find syntax errors before the are loaded to Couch.</p>
<p><a title="loader.js" href="http://gist.github.com/492124">Here's the code</a>. The script expects a <em>designs/</em> folder at the same level as the loader script, with a subfolder for each design document underneath. A design folder should have a <em>.js</em> file for each view. A view file looks like this:</p>
<div><figure role=code> <div class="highlight"><table cellpadding="0" cellspacing="0"><tr><td class="gutter"><pre class="line-numbers"><span class='line'>1</span>
<span class='line'>2</span>
<span class='line'>3</span>
<span class='line'>4</span>
<span class='line'>5</span>
<span class='line'>6</span>
</pre></td><td class='code' width='100%'><pre><code class='javascript'><div class='line'><span class="nx">design</span><span class="p">.</span><span class="nx">view</span> <span class="o">=</span> <span class="p">{</span>
</div><div class='line'> <span class="nx">map</span><span class="o">:</span> <span class="kd">function</span><span class="p">(</span><span class="nx">doc</span><span class="p">)</span> <span class="p">{</span>
</div><div class='line'> <span class="nx">emit</span><span class="p">(</span><span class="nx">doc</span><span class="p">.</span><span class="nx">created_at</span><span class="p">,</span> <span class="kc">null</span><span class="p">);</span>
</div><div class='line'> <span class="p">},</span>
</div><div class='line'> <span class="nx">reduce</span><span class="o">:</span> <span class="s1">'_count'</span>
</div><div class='line'><span class="p">};</span>
</div></code></pre></td></tr></table></div></figure></div>
<p><a href="http://wiki.apache.org/couchdb/Document_Update_Handlers">CouchDB document update handlers</a> are supported as well by assigning a function to <strong>design.update</strong>. Once your view files are ready, just run <strong>loader.js</strong> with the CouchDB database URL as the parameter:</p>
<pre>node db/couchdb/loader.js http://localhost:5984/myDatabase</pre>
<p>So what do you think? Does something like this exist already? Is there a better way to do this?</p>
Introducing Rack::CORS2010-06-09T00:00:00-04:00http://blog.sourcebender.com/2010/06/09/introducin-rack-cors<p>Recently, I've been working on an HTML5 project that needed to need to retrieve data from a different origin, and decided to look at using CORS.</p>
<p>CORS, or Cross-Origin Resource Sharing is a specification that allows web applications to make AJAX calls cross-origin without resorting to workarounds such as <a title="Wikipedia write up on JSONP" href="http://en.wikipedia.org/wiki/JSON#JSONP">JSONP</a>.</p>
<p>Searching around, I found an CORS extension for Sinatra, which happened to be the framework I was using. However, the extension didn't properly implement the spec, nor did it support CORS preflighting (required for more complex AJAX requests). So I rolled my own, but as a Rack Middleware. Here's an example of a Rackup that shows it in action (this example uses <a title="Rack::CORS Rubygem" href="http://rubygems.org/gems/rack-cors">Rack::CORS</a> in Sinatra app, but should be able to use it in any Rack compatible framework):</p>
<div><figure role=code> <div class="highlight"><table cellpadding="0" cellspacing="0"><tr><td class="gutter"><pre class="line-numbers"><span class='line'>1</span>
<span class='line'>2</span>
<span class='line'>3</span>
<span class='line'>4</span>
<span class='line'>5</span>
<span class='line'>6</span>
<span class='line'>7</span>
<span class='line'>8</span>
<span class='line'>9</span>
<span class='line'>10</span>
<span class='line'>11</span>
<span class='line'>12</span>
<span class='line'>13</span>
<span class='line'>14</span>
<span class='line'>15</span>
<span class='line'>16</span>
<span class='line'>17</span>
<span class='line'>18</span>
<span class='line'>19</span>
<span class='line'>20</span>
<span class='line'>21</span>
</pre></td><td class='code' width='100%'><pre><code class='ruby'><div class='line'><span class="nb">require</span> <span class="s1">'sinatra'</span>
</div><div class='line'><span class="nb">require</span> <span class="s1">'rack/cors'</span>
</div><div class='line'>
</div><div class='line'><span class="n">use</span> <span class="no">Rack</span><span class="o">::</span><span class="no">Cors</span> <span class="k">do</span> <span class="o">|</span><span class="n">config</span><span class="o">|</span>
</div><div class='line'> <span class="n">config</span><span class="o">.</span><span class="n">allow</span> <span class="k">do</span> <span class="o">|</span><span class="n">allow</span><span class="o">|</span>
</div><div class='line'> <span class="n">allow</span><span class="o">.</span><span class="n">origins</span> <span class="s1">'*'</span>
</div><div class='line'> <span class="n">allow</span><span class="o">.</span><span class="n">resource</span> <span class="s1">'/file/list_all/'</span><span class="p">,</span> <span class="ss">:headers</span> <span class="o">=&</span><span class="n">gt</span><span class="p">;</span> <span class="ss">:any</span>
</div><div class='line'> <span class="n">allow</span><span class="o">.</span><span class="n">resource</span> <span class="s1">'/file/at/*'</span><span class="p">,</span>
</div><div class='line'> <span class="ss">:methods</span> <span class="o">=&</span><span class="n">gt</span><span class="p">;</span> <span class="o">[</span><span class="ss">:get</span><span class="p">,</span> <span class="ss">:post</span><span class="p">,</span> <span class="ss">:put</span><span class="p">,</span> <span class="ss">:delete</span><span class="o">]</span><span class="p">,</span>
</div><div class='line'> <span class="ss">:headers</span> <span class="o">=&</span><span class="n">gt</span><span class="p">;</span> <span class="ss">:any</span><span class="p">,</span>
</div><div class='line'> <span class="ss">:max_age</span> <span class="o">=&</span><span class="n">gt</span><span class="p">;</span> <span class="mi">0</span>
</div><div class='line'> <span class="k">end</span>
</div><div class='line'><span class="k">end</span>
</div><div class='line'>
</div><div class='line'><span class="n">get</span> <span class="s1">'/file/list_all/'</span> <span class="k">do</span>
</div><div class='line'> <span class="c1">#...</span>
</div><div class='line'><span class="k">end</span>
</div><div class='line'>
</div><div class='line'><span class="n">get</span> <span class="s1">'/file/at/*'</span> <span class="k">do</span>
</div><div class='line'> <span class="c1">#...</span>
</div><div class='line'><span class="k">end</span>
</div></code></pre></td></tr></table></div></figure></div>
<p>To get going with Rack::CORS, just install the rack-cors Gem. To check out the source, see <a href="http://github.com/cyu/rack-cors">the project on Github</a>.</p>
<p>If you want to learn more about CORS, here are some good links I found along the way:</p>
<ul>
<li>The <a title="Cross-Origin Resource Sharing Working Draft" href="http://www.w3.org/TR/access-control/">W3C Working Draft on CORS</a>, for good night time reading.</li>
<li>A <a title="Cross-domain Ajax with Cross-Origin Resource Sharing" href="http://www.nczonline.net/blog/2010/05/25/cross-domain-ajax-with-cross-origin-resource-sharing/">good article about CORS</a> that summarizes the CORS spec.</li>
<li>You can <a title="CORS Support Tests" href="http://rdfa.digitalbazaar.com/tests/cors/">check if your browsers support CORS here</a>. This site records all pass/fails so you'll be able to see a list of CORS supported (and not supported) browsers.</li>
<li>The <a title="Cross Origin Resource Sharing with Sinatra" href="http://britg.com/2009/12/29/cross-origin-resource-sharing-with-sinatra/">Sinatra CORS Extension</a> I found.</li>
</ul>
Setting up Virtual Hosts on Mac OSX2010-03-26T00:00:00-04:00http://blog.sourcebender.com/2010/03/26/setting-up-virtual-hosts-on-mac-osx<p>I've been juggling a few different web projects lately, and decided to setup different virtual hosts on my Mac so that I can easily work with them. Googling around gave me a lot of different answers, none of which seem to work completely. This is what finally worked for me (on Snow Leopard).</p>
<p>First, add a new local domain to your <em>/etc/hosts</em> file:</p>
<pre>127.0.0.1 localhost devsite.local</pre>
<p>Next, you'll need to configure Apache with this new virtual host. Fortunately, the default Apache config has this partially setup. Open up <em>/etc/apache2/httpd.conf</em> and uncomment the following Include:</p>
<div><figure role=code> <div class="highlight"><table cellpadding="0" cellspacing="0"><tr><td class="gutter"><pre class="line-numbers"><span class='line'>1</span>
<span class='line'>2</span>
</pre></td><td class='code' width='100%'><pre><code class='apache'><div class='line'><span class="c"># Virtual hosts</span>
</div><div class='line'><span class="c">#Include /private/etc/apache2/extra/httpd-vhosts.conf</span>
</div></code></pre></td></tr></table></div></figure></div>
<p>Now, we need to add our virtual host to the <em>httpd-vhosts.conf </em>file referenced above. The file already had a couple of sample configuration in it, but I commented out those and added the following:</p>
<div><figure role=code> <div class="highlight"><table cellpadding="0" cellspacing="0"><tr><td class="gutter"><pre class="line-numbers"><span class='line'>1</span>
<span class='line'>2</span>
<span class='line'>3</span>
<span class='line'>4</span>
<span class='line'>5</span>
<span class='line'>6</span>
<span class='line'>7</span>
<span class='line'>8</span>
<span class='line'>9</span>
</pre></td><td class='code' width='100%'><pre><code class='apache'><div class='line'><span class="nt"><VirtualHost</span> <span class="s">*:80</span><span class="nt">></span>
</div><div class='line'> <span class="nb">DocumentRoot</span> <span class="s2">"/Library/WebServer/Documents"</span>
</div><div class='line'> <span class="nb">ServerName</span> localhost
</div><div class='line'><span class="nt"></VirtualHost></span>
</div><div class='line'>
</div><div class='line'><span class="nt"><VirtualHost</span> <span class="s">*:80</span><span class="nt">></span>
</div><div class='line'> <span class="nb">DocumentRoot</span> <span class="s2">"/usr/docs/devsite.local"</span>
</div><div class='line'> <span class="nb">ServerName</span> devsite.local
</div><div class='line'><span class="nt"></VirtualHost></span>
</div></code></pre></td></tr></table></div></figure></div>
<p>This first entry will map localhost to its default document location (without it http://localhost won't work correctly). The second entry maps my new domain. Additionally, you'll want to make sure files in your new docs directory have adequate access permissions. I ended adding a new Directory section to <em>httpd-vhosts.conf </em>file:</p>
<div><figure role=code> <div class="highlight"><table cellpadding="0" cellspacing="0"><tr><td class="gutter"><pre class="line-numbers"><span class='line'>1</span>
<span class='line'>2</span>
<span class='line'>3</span>
<span class='line'>4</span>
<span class='line'>5</span>
<span class='line'>6</span>
</pre></td><td class='code' width='100%'><pre><code class='apache'><div class='line'><span class="nt"><Directory</span> <span class="s">"/usr/docs/devsite.local"</span><span class="nt">></span>
</div><div class='line'> <span class="nb">Options</span> Indexes FollowSymLinks MultiViews
</div><div class='line'> <span class="nb">AllowOverride</span> <span class="k">None</span>
</div><div class='line'> <span class="nb">Order</span> allow,deny
</div><div class='line'> <span class="nb">Allow</span> from <span class="k">all</span>
</div><div class='line'><span class="nt"></Directory></span>
</div></code></pre></td></tr></table></div></figure></div>
<p>Now all you have to do is put your web files in <em>/usr/docs/devsite.local</em>. I originally had my new local domain map to <em><user dir>/Sites/devsite.local</em>, but changed it because I would have to make sure Apache could access to all the directories leading up to those docs. So instead I just symlinked my http docs from my user directory into <em>/usr/docs.</em></p>
Installing Native Gems with Custom Library Paths2009-09-06T00:00:00-04:00http://blog.sourcebender.com/2009/09/06/installing-native-gems-with-custom-library-paths<p>A few weeks ago, I started using the <a href="http://github.com/toland/patron/tree/master">Patron</a> Gem for Skribit and ran into an issue on our CentOS production servers which uses a very old version of libcurl. I got it working by compiling a new version of libcurl and building the Gem against those binaries. Since I didn't want to overwrite the libcurl that CentOS provided, I installed the binaries in another location instead, and updated the <strong>LD_LIBRARY_PATH</strong> environment variable so Rails could properly load the Gem.</p>
<p>A couple of days ago, <a href="http://paulstamatiou.com">Paul</a> brought to my attention that <a href="http://linuxmafia.com/faq/Admin/ld-lib-path.html">using LD_LIBRARY_PATH isn't a good thing</a>. While I didn't necessarily think it was a big deal, it did peak my curiosity on how I would get this work without it. Here's the command I finally used to get it to work:</p>
<div><figure role=code> <div class="highlight"><table cellpadding="0" cellspacing="0"><tr><td class="gutter"><pre class="line-numbers"><span class='line'>1</span>
<span class='line'>2</span>
<span class='line'>3</span>
</pre></td><td class='code' width='100%'><pre><code class='bash'><div class='line'>sudo env <span class="nv">PATH</span><span class="o">=</span><span class="s2">"/opt/curl/bin:$PATH"</span> gem install toland-patron <span class="se">\</span>
</div><div class='line'> -v <span class="s2">"0.4.1"</span> --source http://gems.github.com <span class="se">\</span>
</div><div class='line'> -- --with-ldflags<span class="o">=</span><span class="s2">"-Wl,-R/opt/curl/lib"</span>
</div></code></pre></td></tr></table></div></figure></div>
<p>The key part is the <strong>--with-ldflags</strong> option at the very end. The <strong>-Wl,-R<path></strong> option adds the given path to the list of paths the linker will use to find libraries at runtime. Hopefully, someone will find this information useful, since I couldn't find this information myself on the 'nets anywhere.</p>
Switching from Windows to Mac: A Retrospective2009-07-08T00:00:00-04:00http://blog.sourcebender.com/2009/07/08/switching-from-windows-to-mac-a-retrospective<p>Just recently done a 2 week stint working on Windows while Apple Geniuses were working on my MBP, I thought it would be a good time to blog on <a title="My Skribit Suggestion" href="http://skribit.com/suggestions/switching-to-from-windows-to-mac">my own Skribit Suggestion</a> and reflect on my switch to Mac 14 months ago.</p>
<h2>Reasons for Switching</h2>
<p>Prior to my MBP, I've was working on a personal HP laptop that was on it's last leg. My screen had started developing an assortment of 1 pixel vertical lines and the keyboard would randomly doouble typed keys. I had a choice to make - I can either get another (and better) Windows laptop, or do the the switch. Ultimately, I decided on switching. Here were my reasons.</p>
<h3>Apple made great <span style="text-decoration: line-through;">looking</span> laptops that looks good too</h3>
<p>Apple really makes some really good looking laptops, but it's easy to forget that they they're functionally great laptops too. When my wife first got her MacBook, I was amazed at how everything looked so crisp on it. And speaking as someone that has broken a power connector on a laptop before, MagSafe is a must-have awesome feature.</p>
<h3>A Unix based Operating System</h3>
<p>When you do development, working on a Unix based environment with a real command line is a big advantage. Cygwin can help bridge that gap on Windows, but it's nothing like having a terminal that can size itself properly and not having to constantly translate Windows paths to Unix paths and vice versa.</p>
<p>Another benefit of having a Unix OS is that Open Source software plays much better on it than on Windows. Most OSS is developed to target Unix first, Windows second. When working on Elf Island on Windows, my workflow while working on the game (which is developed primarily using OSS), involved starting two different services from Windows System Tray, starting another service from the Windows Services, and finally running a few commands from Cygwin. I can do the equivalent of all that from the command line on a Unix environment.</p>
<h3>A Paid-For Software Friendly Ecosystem</h3>
<p>For some reason, it seems that Mac users are willing to pay for software, and as a result there is a lot of <a title="TextMate Editor" href="http://macromates.com/">reasonably price</a> <a title="Transmit" href="http://www.panic.com/transmit/">software</a> for Macs. Doing software development myself, I can appreciate and definitely want to support this line of thinking. Sure, Windows has tons of commercial, <a href="http://downloads.com">paid-for software</a>, but I can't say I feel comfortable about downloading them for fear of excessive ads, poor installations practices, and/or spyware.</p>
<h3>Experiencing Something New</h3>
<p>Probably the biggest reason for doing the switch is that I've been using Windows for years. It was time for a change - a change for changes' sake. Programmers are told that they should regularly learn new languages to keep their skills sharp and to explore new ways of doing things. It should be the same with the software you use as well. If you develop software, you owe it to yourself to see how the other side does it to gain some perspective.</p>
<h3>Conclusions</h3>
<p>So after a year of working fully on the Mac, here's are some things I like and don't like about my OS X:</p>
<h4>Pros</h4>
<ul>
<li><strong>Windows Management.</strong> It took a while getting used to, but I have grown to prefer <strong>Command+Tab</strong> & <strong>Command+`</strong> to Windows <strong>Alt+Tab</strong> model.</li>
<li><strong>OS Degradation.</strong> With my prior laptop, Windows had greatly degraded by it's first anniversary. I haven't experience that with my MBP yet.</li>
<li><strong>Great TrackPad.</strong> When programming, I try to stay on the keyboard as much as possible, but I find that transitioning between the trackpad and keyboard on my MBP the easiest of any laptop I've used before.</li>
</ul>
<h4>Cons</h4>
<ul>
<li><strong>Keyboard Shortcut Images. </strong>For some strange reason keyboard shortcuts are still displayed using icons (<img style="border: 0px initial initial;" title="Apple Keys" src="http://blog.codeeg.com/wp-content/uploads/2009/06/mac_keys.png" alt="Apple Keys" width="47" height="11" />) that in some cases are no longer shown on the keyboard. Trying to remember what icon is what key can feel like akin to decipher hieroglyphics.</li>
<li><strong>Alt Key Menu Access.</strong> I miss how the <strong>Alt </strong>key in Windows gave you keyboard access to the application menu. I often used that function keyboard shortcut.</li>
<li><strong>I Miss My Alt+Ctrl+Delete.</strong> I kind of feel that Windows does a better job of preventing errant applications from locking up the whole OS. I attribute that to Window's plentiful experience with crashes and lockups. It seems that lockups on OS X are more catastrophic (requiring a hard reboot) than they should be.</li>
</ul>
<div id="_mcePaste" style="overflow: hidden; position: absolute; left: -10000px; top: 247px; width: 1px; height: 1px;">http://macromates.com/</div>
Problems Uploading Files to WordPress?2009-06-25T00:00:00-04:00http://blog.sourcebender.com/2009/06/25/having-problems-uploading-files-to-wordpress<p>A Google search seems to indicate that problems uploading files on WordPress installs is a pretty common occurrence. Unfortunately, I had the hardest time finding the solution to my particular ailment. Hopefully, this post will find those others who run into this problem in the future.</p>
<p>From the WP Admin pages, go to <strong>Settings > Miscellaneous</strong>. Make sure the value for 'Store uploads in this folder' is where you want your files stored (in most cases, it should be <em>wp-content/uploads</em>).</p>
<p style="text-align: center;"><a href="http://blog.sourcebender.com/images/wp/wp-settings.jpg"><img title="Miscellaneous Settings ‹ Don’t Forget to Plant It! — WordPress" src="http://blog.sourcebender.com/images/wp/wp-settings.jpg" alt="Miscellaneous Settings ‹ Don’t Forget to Plant It! — WordPress" width="500" height="196" /></a></p>
<p>In my case, this was pointing to a folder I was using for a WP install I was using to test the Thesis theme I'm using now. Reverting it to the default fixed my problem.</p>
Creating Static Pages w/ Rails ActionViews2009-05-24T00:00:00-04:00http://blog.sourcebender.com/2009/05/24/creating-static-pages-w-rails-actionviews<p>Recently, I needed to create some static reporting pages for Skribit. From a quick search, I got a lot of results that talk about Rails and static pages, but none did exactly what I needed:</p>
<ol>
<li>To be able to generate pages with different paths from one URL</li>
<li>Pages to persist across Rails deployments</li>
</ol>
<p>Not seeing any solutions that fit my needs, I set out to come up with my own. Here is what I ended up with.</p>
<p>First, I needed to add a route for generating/displaying the reports:</p>
<div><figure role=code> <div class="highlight"><table cellpadding="0" cellspacing="0"><tr><td class="gutter"><pre class="line-numbers"><span class='line'>1</span>
<span class='line'>2</span>
</pre></td><td class='code' width='100%'><pre><code class='ruby'><div class='line'><span class="n">map</span><span class="o">.</span><span class="n">reports</span> <span class="s1">'report/:year/:month/:day'</span><span class="p">,</span> <span class="ss">:controller</span> <span class="o">=></span> <span class="s1">'report'</span><span class="p">,</span>
</div><div class='line'> <span class="ss">:action</span> <span class="o">=></span> <span class="s1">'show'</span><span class="p">,</span> <span class="ss">:year</span> <span class="o">=></span> <span class="kp">nil</span><span class="p">,</span> <span class="ss">:month</span> <span class="o">=></span> <span class="kp">nil</span><span class="p">,</span> <span class="ss">:day</span> <span class="o">=></span> <span class="kp">nil</span>
</div></code></pre></td></tr></table></div></figure></div>
<p>From here, I could just use the standard <strong>caches_page :show</strong> declaration, but that would only generate the page I wanted if I used <em>/report/2009/05/25 </em>as the URL. What if I wanted <em>/report</em> to generate the report for the current week? Well, you can do something like this:</p>
<div><figure role=code> <div class="highlight"><table cellpadding="0" cellspacing="0"><tr><td class="gutter"><pre class="line-numbers"><span class='line'>1</span>
<span class='line'>2</span>
<span class='line'>3</span>
<span class='line'>4</span>
<span class='line'>5</span>
<span class='line'>6</span>
<span class='line'>7</span>
<span class='line'>8</span>
<span class='line'>9</span>
<span class='line'>10</span>
<span class='line'>11</span>
<span class='line'>12</span>
<span class='line'>13</span>
<span class='line'>14</span>
<span class='line'>15</span>
<span class='line'>16</span>
<span class='line'>17</span>
<span class='line'>18</span>
<span class='line'>19</span>
<span class='line'>20</span>
<span class='line'>21</span>
<span class='line'>22</span>
<span class='line'>23</span>
<span class='line'>24</span>
<span class='line'>25</span>
<span class='line'>26</span>
<span class='line'>27</span>
<span class='line'>28</span>
<span class='line'>29</span>
<span class='line'>30</span>
<span class='line'>31</span>
<span class='line'>32</span>
<span class='line'>33</span>
<span class='line'>34</span>
</pre></td><td class='code' width='100%'><pre><code class='ruby'><div class='line'><span class="k">class</span> <span class="nc">ReportController</span> <span class="o"><</span> <span class="no">ApplicationController</span>
</div><div class='line'> <span class="n">after_filter</span> <span class="ss">:cache_weekly_report</span><span class="p">,</span> <span class="ss">:only</span> <span class="o">=></span> <span class="ss">:show</span>
</div><div class='line'>
</div><div class='line'> <span class="k">def</span> <span class="nf">show</span>
</div><div class='line'> <span class="k">if</span> <span class="n">params</span><span class="o">[</span><span class="ss">:year</span><span class="o">]</span>
</div><div class='line'> <span class="vi">@year</span> <span class="o">=</span> <span class="n">params</span><span class="o">[</span><span class="ss">:year</span><span class="o">]</span>
</div><div class='line'> <span class="vi">@month</span> <span class="o">=</span> <span class="n">params</span><span class="o">[</span><span class="ss">:month</span><span class="o">]</span>
</div><div class='line'> <span class="vi">@day</span> <span class="o">=</span> <span class="n">params</span><span class="o">[</span><span class="ss">:day</span><span class="o">]</span>
</div><div class='line'> <span class="k">if</span> <span class="no">File</span><span class="o">.</span><span class="n">exist?</span><span class="p">(</span><span class="s2">"</span><span class="si">#{</span><span class="no">Rails</span><span class="o">.</span><span class="n">root</span><span class="si">}</span><span class="s2">/public/report/</span><span class="si">#{</span><span class="vi">@year</span><span class="si">}</span><span class="s2">/</span><span class="si">#{</span><span class="vi">@month</span><span class="si">}</span><span class="s2">/</span><span class="si">#{</span><span class="vi">@day</span><span class="si">}</span><span class="s2">.html"</span><span class="p">)</span>
</div><div class='line'> <span class="n">redirect_to</span> <span class="n">reports_path</span><span class="p">(</span>
</div><div class='line'> <span class="ss">:year</span> <span class="o">=></span> <span class="vi">@year</span><span class="p">,</span>
</div><div class='line'> <span class="ss">:month</span> <span class="o">=></span> <span class="vi">@month</span><span class="p">,</span>
</div><div class='line'> <span class="ss">:day</span> <span class="o">=></span> <span class="vi">@day</span><span class="p">)</span>
</div><div class='line'> <span class="k">else</span>
</div><div class='line'> <span class="c1"># render report for a specific day</span>
</div><div class='line'> <span class="k">end</span>
</div><div class='line'> <span class="k">else</span>
</div><div class='line'> <span class="n">today</span> <span class="o">=</span> <span class="no">Date</span><span class="o">.</span><span class="n">today</span>
</div><div class='line'> <span class="vi">@year</span> <span class="o">=</span> <span class="n">today</span><span class="o">.</span><span class="n">year</span>
</div><div class='line'> <span class="vi">@month</span> <span class="o">=</span> <span class="n">today</span><span class="o">.</span><span class="n">month</span>
</div><div class='line'> <span class="vi">@day</span> <span class="o">=</span> <span class="n">today</span><span class="o">.</span><span class="n">day</span>
</div><div class='line'>
</div><div class='line'> <span class="c1"># render report for the current week</span>
</div><div class='line'> <span class="k">end</span>
</div><div class='line'> <span class="k">end</span>
</div><div class='line'>
</div><div class='line'> <span class="kp">protected</span>
</div><div class='line'>
</div><div class='line'> <span class="k">def</span> <span class="nf">cache_weekly_report</span>
</div><div class='line'> <span class="n">cache_page</span><span class="p">(</span><span class="n">response</span><span class="o">.</span><span class="n">body</span><span class="p">,</span>
</div><div class='line'> <span class="s2">"/report/</span><span class="si">#{</span><span class="vi">@year</span><span class="si">}</span><span class="s2">/</span><span class="si">#{</span><span class="vi">@month</span><span class="si">}</span><span class="s2">/</span><span class="si">#{</span><span class="vi">@day</span><span class="si">}</span><span class="s2">.html"</span><span class="p">)</span> <span class="k">if</span> <span class="vi">@year</span>
</div><div class='line'> <span class="k">end</span>
</div><div class='line'>
</div><div class='line'><span class="k">end</span>
</div></code></pre></td></tr></table></div></figure></div>
<p>The magic is in the <strong>after_filter</strong> method <strong>cache_weekly_report</strong>. We basically use the same mechanism Rails page caching uses to save our new report page. Now, calling <em>/report</em> will generate a static report at <em>/report/2009/05/25</em>, or whatever the current day is.</p>
<p>The last thing to do is to make sure that the reports persist through new server deployments. That can easily be done with a symlink in your capistrano script:</p>
<div><figure role=code> <div class="highlight"><table cellpadding="0" cellspacing="0"><tr><td class="gutter"><pre class="line-numbers"><span class='line'>1</span>
<span class='line'>2</span>
<span class='line'>3</span>
<span class='line'>4</span>
</pre></td><td class='code' width='100%'><pre><code class='ruby'><div class='line'><span class="n">task</span> <span class="ss">:symlink_reports</span> <span class="k">do</span>
</div><div class='line'> <span class="n">run</span> <span class="s2">"mkdir -p </span><span class="si">#{</span><span class="n">shared_path</span><span class="si">}</span><span class="s2">/report; ln -nfs </span><span class="si">#{</span><span class="n">shared_path</span><span class="si">}</span><span class="s2">/report </span><span class="si">#{</span><span class="n">release_path</span><span class="si">}</span><span class="s2">/public/report"</span>
</div><div class='line'><span class="k">end</span>
</div><div class='line'><span class="n">after</span> <span class="s1">'deploy:update_code'</span><span class="p">,</span> <span class="s1">'deploy:symlink_reports'</span>
</div></code></pre></td></tr></table></div></figure></div>
<p>And that's it! What do you think? I'd love to know if there are any simpler solutions to this.</p>
Building Software to their Audiences2009-02-24T00:00:00-05:00http://blog.sourcebender.com/2009/02/24/building-software-to-their-audiences<p>When building software, it's a good idea to identify the needs of 3 different kinds of audiences.</p>
<p>The first 2 kinds are obvious ones. We know to listen to what our End Users ask for, but not necessarily build everything they ask. And we want to balance what the Business wants with when they want it.</p>
<p>The last audience, the fellow Developer, is often forgotten. This means writing clear and concise code. This also means identifying the common maintenance points in your software and making it easy to work with.</p>
<p>And this also means factoring into the design the capabilities of the development team. Just like it is unacceptable to write bad code, it should be just as unacceptable to over design software beyond the hiring practices of the company you work for.</p>
Turning a New Page2008-11-15T00:00:00-05:00http://blog.sourcebender.com/2008/11/15/turning-a-new-page<p>It's been a month now since I left my contract at AutoTrader.com. For the most part, I've enjoyed my time there, but the thought of celebrating my two year anniversary there was a little frightening. While I am very adaptable to the corporate life, it isn't for me. When FlickStation (the last startup I was a part of) desolved, the plan was to serve a year of corporate duty to get my bearings before heading back into the startup arena. But some interesting work, interesting politics, and great co-workers kept me there longer than I expected. But ultimately, I'm addicted to execution and delivery, both of which seem very hard to do well and often in large companies.</p>
<p>Also, for people with entrepreneurial aspirations, the corporate world has a way of slowly draining it from you, replacing it with contentedness. And when you're building your startup, those aspirations provides the fuel for you to push on. I was worried that one day I wake up and find it all gone.</p>
<p>I'm now spending my time at Good Egg Studios, where we just now wrapped up our second week in private beta of <a title="Elf Island" href="http://elfisland.com">Elf Island</a>. Working here has been like a breathe of fresh air. The technology is familiar and not so familiar at the same time, and I'm working with some great people (including <a href="http://atlanta.startupweekend.com">Startup Weekend</a> alums <a title="Rob Kischuk" href="http://blog.kischuk.com/">Rob</a> and <a title="Amro Mousa - iPhone Developer :)" href="http://amromousa.com">Amro</a>) and leadership with great character. As an added bonus, my commute is a lot shorter now, and also I'm much closer to <a title="Skribit - Blog Suggestion Application" href="http://skribit.com">Skribit</a>.</p>
<p>Oh, and did I mention I was working on a game! A fscking game!</p>
Growl Notifications for Ant2008-10-18T00:00:00-04:00http://blog.sourcebender.com/2008/10/18/growl-notifications-for-ant<p>It's a real refreshing change to be doing developing on Mac these days. Currently, our Ant builds at work are less than optimal, taking ten's of minutes to do a full build. Fixing it is something we definitely want to do, but because of the complexity of the build and existing deadlines, right now isn't the best time. So instead of constantly checking on the progress of my build, I installed this <a href="http://blog.slimeslurp.net/2007/03/18/ant-build-notifications-via-growl/">Growl Ant build listener</a> which will display a Growl notification when a build has completed.</p>
<p>The <a href="http://code.google.com/p/growlbuildlistener/wiki/README">README</a> for the listener got me started - the only thing that didn't work for me is setting the build listener using the <em>ANT_OPT</em> environment variable. It looks like the default install of Ant on Leopard uses that environment variable as arguments to pass to the Java VM. So instead, I just used an alias:</p>
<div><figure role=code> <div class="highlight"><table cellpadding="0" cellspacing="0"><tr><td class="gutter"><pre class="line-numbers"><span class='line'>1</span>
</pre></td><td class='code' width='100%'><pre><code class='ruby'><div class='line'><span class="k">alias</span> <span class="n">ant</span><span class="o">=</span><span class="s1">'ant -listener net.slimeslurp.growl.GrowlListener'</span>
</div></code></pre></td></tr></table></div></figure></div>
<p>Just add this line to your <em>~/.bash_login</em> to use the build listener every time you build.</p>
Moving Off WP.com2008-10-07T00:00:00-04:00http://blog.sourcebender.com/2008/10/07/moving-off-wpcom<p>So after three months of WP.com, I've decided to move back to my own hosted WordPress blog. One reason for the move was so that I can beta test our <a href="http://skribit.com">Skribit</a> widget, which can now be easily styled to blend perfectly to your site. Please give it a shot, and shoot us any feedback. Right now, this is a one off install, but I'm starting to put things together so that we can roll this new widget out to more testers.</p>
<p>Also, I felt that by moving to WP.com my blog got sort of bleh. I had to use from one of the provided themes, and was limited in the plugins and widgets I was able to use. It's not like it was anything special before, but at least it wasn't special by my choosing. So, I'm on my own again, using the WP Subversion installation that <a href="http://paulstamatiou.com">Paul</a> recommended. I also bought this <a title="Thesis WordPress theme" href="http://diythemes.com/thesis/">wicket cool theme</a> that is very well done and supported. Now, I need to get some cool plugins - any recommendations?</p>
Running FiveRuns TuneUp in a Separate Environment2008-10-05T00:00:00-04:00http://blog.sourcebender.com/2008/10/05/running-fiveruns-tuneup-in-a-separate-environment<p><a href="http://www.fiveruns.com/products/tuneup">FiveRuns' TuneUp</a> is a great tool for profiling your Rails app, but by default it is always running in development. This causes two issues 1) every request is slower in development as it is always collecting profiling data, and 2) the TuneUp bar can mess with the layout of your application, especially if you're rendering content in iframes like we do with <a href="http://skribit.com">Skribit</a>. So instead of having TuneUp always run in development mode, I've changed it to run only when the server is started in a new environment called 'profiler'. Here's how I did it.</p>
<p>First, you need to tell TuneUp to only run in the profiler environment. You do this by modifying <em>config/tuneup.rb</em> (create this file if it doesn't already exist):</p>
<div><figure role=code> <div class="highlight"><table cellpadding="0" cellspacing="0"><tr><td class="gutter"><pre class="line-numbers"><span class='line'>1</span>
<span class='line'>2</span>
<span class='line'>3</span>
<span class='line'>4</span>
</pre></td><td class='code' width='100%'><pre><code class='ruby'><div class='line'><span class="no">Fiveruns</span><span class="o">::</span><span class="no">Tuneup</span><span class="o">.</span><span class="n">config</span> <span class="k">do</span> <span class="o">|</span><span class="n">config</span><span class="o">|</span>
</div><div class='line'> <span class="n">config</span><span class="o">.</span><span class="n">environments</span><span class="o">.</span><span class="n">delete</span><span class="p">(</span><span class="s1">'development'</span><span class="p">)</span>
</div><div class='line'> <span class="n">config</span><span class="o">.</span><span class="n">environments</span> <span class="o"><<</span> <span class="s1">'profiler'</span>
</div><div class='line'><span class="k">end</span>
</div></code></pre></td></tr></table></div></figure></div>
<p>Next, copy the development configuration block in <em>config/database.yml</em> and create a new block called <em>profiler</em>:</p>
<div><figure role=code> <div class="highlight"><table cellpadding="0" cellspacing="0"><tr><td class="gutter"><pre class="line-numbers"><span class='line'>1</span>
<span class='line'>2</span>
<span class='line'>3</span>
<span class='line'>4</span>
<span class='line'>5</span>
<span class='line'>6</span>
<span class='line'>7</span>
</pre></td><td class='code' width='100%'><pre><code class='yaml'><div class='line'><span class="l-Scalar-Plain">profiler</span><span class="p-Indicator">:</span>
</div><div class='line'> <span class="l-Scalar-Plain">adapter</span><span class="p-Indicator">:</span> <span class="l-Scalar-Plain">mysql</span>
</div><div class='line'> <span class="l-Scalar-Plain">encoding</span><span class="p-Indicator">:</span> <span class="l-Scalar-Plain">utf8</span>
</div><div class='line'> <span class="l-Scalar-Plain">database</span><span class="p-Indicator">:</span> <span class="l-Scalar-Plain">YOUR_DATABASE</span>
</div><div class='line'> <span class="l-Scalar-Plain">username</span><span class="p-Indicator">:</span> <span class="l-Scalar-Plain">YOUR_USERNAME</span>
</div><div class='line'> <span class="l-Scalar-Plain">password</span><span class="p-Indicator">:</span> <span class="l-Scalar-Plain">YOUR_PASSWORD</span>
</div><div class='line'> <span class="l-Scalar-Plain">socket</span><span class="p-Indicator">:</span> <span class="l-Scalar-Plain">/tmp/mysql.sock</span>
</div></code></pre></td></tr></table></div></figure></div>
<p>Finally, create your profiler environment configs by copying your development configs:</p>
<div><figure role=code> <div class="highlight"><table cellpadding="0" cellspacing="0"><tr><td class="gutter"><pre class="line-numbers"><span class='line'>1</span>
</pre></td><td class='code' width='100%'><pre><code class='bash'><div class='line'>cp config/environments/development.rb config/environments/profiler.rb
</div></code></pre></td></tr></table></div></figure></div>
<p>You're all set! Now, to run the server with TuneUp on, just run the server in the <em>profiler</em> environment:</p>
<div><figure role=code> <div class="highlight"><table cellpadding="0" cellspacing="0"><tr><td class="gutter"><pre class="line-numbers"><span class='line'>1</span>
</pre></td><td class='code' width='100%'><pre><code class='bash'><div class='line'>script/server -e profiler
</div></code></pre></td></tr></table></div></figure></div>
<p>Now, I have TuneUp available to me only when I'm looking to optimize performance.</p>
Aptana Goes into the Clouds2008-09-07T00:00:00-04:00http://blog.sourcebender.com/2008/09/07/aptana-goes-into-the-clouds<p>I use <a title="Aptana Website" href="http://www.aptana.com">Aptana</a> for RoR development. Not because it's a good Ruby or Rails IDE, but because I do so much Java at my paying job and switching between Java development on Eclipse to RoR development on Aptana is a breeze. Today, I was checking out Aptana's website for updates when I noticed that they now have a product called <a href="http://www.aptana.com/cloud">Aptana Cloud</a>.</p>
<p>Aptana Cloud appears to be a integrated deployment/hosting platform for Aptana. You can write applications in PHP, Jaxer (Aptana's AJAX framework), and Ruby on Rails (coming soon), and deploy them to a cloud with a push of a button. Right now, it deploys to Joyent's Cloud, but it appears that the intention is to be provider agnostic, which would be key for adoption.</p>
<p>I'm very interested in trying this out, though I'm going to wait until RoR support is ready. Hopefully, this will lead to some standards to cloud computing, making it easier to switch from one provider to another. No doubt this is where lightweight development frameworks should be going (looking at you Appcelerator).</p>