<?xml version="1.0" encoding="UTF-8"?>
<?xml-stylesheet type="text/xsl" media="screen" href="/~d/styles/rss2full.xsl"?><?xml-stylesheet type="text/css" media="screen" href="http://feeds.feedburner.com/~d/styles/itemcontent.css"?><rss xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:feedburner="http://rssnamespace.org/feedburner/ext/1.0" version="2.0">
  <channel>
    <title>Helicoid Insider</title>
    <link>http://insider.helicoid.net</link>
    <description>Helicoid's tips on designing and coding web applications</description>
    <language>en-gb</language>
    <ttl>40</ttl>
    <atom10:link xmlns:atom10="http://www.w3.org/2005/Atom" rel="self" href="http://feeds.feedburner.com/helicoid_insider" type="application/rss+xml" /><item>
      <title>Agile Deployment</title>
      <description>&lt;p&gt;I&amp;#8217;m working on a series of articles about deploying Rails apps.  It&amp;#8217;s based on things I&amp;#8217;ve learned at Helicoid, and it&amp;#8217;s called &lt;a href="http://alexyoung.org/2009/07/13/agile-deployment-introduction/"&gt;Agile Deployment&lt;/a&gt;.  Read it over at my &lt;a href="http://alexyoung.org"&gt;personal blog&lt;/a&gt;.&lt;/p&gt;&lt;img src="http://feeds.feedburner.com/~r/helicoid_insider/~4/s2Z1rdwDPjo" height="1" width="1"/&gt;</description>
      <pubDate>Mon, 13 Jul 2009 16:17:39 BST</pubDate>
      <link>http://feedproxy.google.com/~r/helicoid_insider/~3/s2Z1rdwDPjo/agile_deployment</link>
      <dc:creator>Alex Young</dc:creator>
    <feedburner:origLink>http://insider.helicoid.net/articles/show/39/agile_deployment</feedburner:origLink></item>
    <item>
      <title>Open Source: JsChat</title>
      <description>&lt;p&gt;As well as &lt;a href="http://insider.helicoid.net/articles/show/37/loom_open_source_edition"&gt;Loom&lt;/a&gt;, I&amp;#8217;ve been working on a few other open source projects funded by Helicoid.  The biggest so far is &lt;a href="http://jschat.org/"&gt;JsChat&lt;/a&gt; which is an &lt;span class="caps"&gt;MIT&lt;/span&gt;-licensed &lt;span class="caps"&gt;JSON&lt;/span&gt;-based chat system with a nifty web interface:&lt;/p&gt;


	&lt;p&gt;&lt;img src="http://files.getdropbox.com/u/221414/helicoid/insider/jschat_insider.png" alt="" /&gt;&lt;/p&gt;


	&lt;p&gt;People have called it an &amp;#8220;open source Campfire&amp;#8221;, but it&amp;#8217;s not really meant to be a Campfire competitor.  It&amp;#8217;s actually a response to these questions:&lt;/p&gt;


	&lt;ul&gt;
	&lt;li&gt;Can we make a chat system like &lt;span class="caps"&gt;IRC&lt;/span&gt; without the legacy and bloated protocol?&lt;/li&gt;
		&lt;li&gt;Can it work naturally on the web, desktop and console?&lt;/li&gt;
	&lt;/ul&gt;


	&lt;p&gt;The &amp;#8220;Js&amp;#8221; in JsChat refers to &lt;span class="caps"&gt;JSON&lt;/span&gt; rather than JavaScript.  It&amp;#8217;s very easy to build bots and clients.  For more information, see the &lt;a href="http://jschat.org"&gt;JsChat site&lt;/a&gt; and &lt;a href="http://blog.jschat.org"&gt;JsChat blog&lt;/a&gt;.&lt;/p&gt;&lt;img src="http://feeds.feedburner.com/~r/helicoid_insider/~4/exxTDrwwJAA" height="1" width="1"/&gt;</description>
      <pubDate>Wed,  1 Jul 2009 12:38:15 BST</pubDate>
      <link>http://feedproxy.google.com/~r/helicoid_insider/~3/exxTDrwwJAA/open_source_jschat</link>
      <dc:creator>Alex Young</dc:creator>
    <feedburner:origLink>http://insider.helicoid.net/articles/show/38/open_source_jschat</feedburner:origLink></item>
    <item>
      <title>Loom: Open Source Edition</title>
      <description>&lt;p&gt;&lt;a href="http://loomapp.com/"&gt;Loom&lt;/a&gt; is our issue tracking system.  It evolved from Bugtagger, which I created for internal use in 2006.  Loom has a few nifty features: the bug report form automatically snoops browser and OS details (to help designers), and it has exception tracking for developers.  You can sign up to try it for free right now.&lt;/p&gt;


	&lt;p&gt;But the future of Loom is open source.  I&amp;#8217;ve set up a private GitHub repo to prepare it for release.  It will be &lt;span class="caps"&gt;MIT&lt;/span&gt; licensed, and easy to set up for use within your company.  I&amp;#8217;ll continue to develop the server-based &amp;#8220;closed&amp;#8221; version, but features will be pushed back into the open source edition.  The only major difference will be that Loomapp.com will be integrated into Helicoid&amp;#8217;s single sign-on system that Helipad and Deadline use.&lt;/p&gt;


	&lt;p&gt;If you&amp;#8217;re interested in helping prepare Loom for the public, &lt;a href="http://helicoid.net/contact/"&gt;contact us&lt;/a&gt; and I&amp;#8217;ll add you to the private GitHub repo.  The repository will become public once we&amp;#8217;ve verified it installs and runs on enough systems&amp;#8212;we initially had Ruby 1.8.6 issues (it was my fault), which &lt;a href="http://www.ricroberts.com/"&gt;Ric Roberts&lt;/a&gt; helped me fix.&lt;/p&gt;


	&lt;p&gt;Loom is built with Ruby on Rails, but help from designers and writers would be awesome too!  You&amp;#8217;ll get fully credited of course!&lt;/p&gt;&lt;img src="http://feeds.feedburner.com/~r/helicoid_insider/~4/r1QQ2aWVNbo" height="1" width="1"/&gt;</description>
      <pubDate>Fri, 26 Jun 2009 13:09:10 BST</pubDate>
      <link>http://feedproxy.google.com/~r/helicoid_insider/~3/r1QQ2aWVNbo/loom_open_source_edition</link>
      <dc:creator>Alex Young</dc:creator>
    <feedburner:origLink>http://insider.helicoid.net/articles/show/37/loom_open_source_edition</feedburner:origLink></item>
    <item>
      <title>Secure Trading Rails Plugin</title>
      <description>&lt;p&gt;&lt;img src="http://dl.getdropbox.com/u/221414/blogs/securetrading.png" alt="" /&gt;&lt;/p&gt;


	&lt;p&gt;We use &lt;a href="http://www.securetrading.com/"&gt;Secure Trading&lt;/a&gt; for &lt;a href="http://helicoid.net"&gt;Helicoid&amp;#8217;s&lt;/a&gt; payment processing.  I wrote a payment processor plugin for our apps about two years ago, and it&amp;#8217;s been in production since then.  A few people have asked me to open source the code, so here it is: &lt;a href="http://github.com/alexyoung/securetrading-rails/tree/master"&gt;securetrading-rails&lt;/a&gt;.&lt;/p&gt;


	&lt;p&gt;I&amp;#8217;d like to integrate it with &lt;a href="http://www.activemerchant.org/"&gt;ActiveMerchant&lt;/a&gt; because I like their &lt;span class="caps"&gt;API&lt;/span&gt;, but I haven&amp;#8217;t had time so far.&lt;/p&gt;


	&lt;p&gt;Out of all the payment processing companies I&amp;#8217;ve dealt with, which is a lot, Secure Trading have been the most reliable with the best customer service.  Their &lt;span class="caps"&gt;XML API&lt;/span&gt; is relatively straightforward too.&lt;/p&gt;&lt;img src="http://feeds.feedburner.com/~r/helicoid_insider/~4/iuGfPYRvbFM" height="1" width="1"/&gt;</description>
      <pubDate>Wed, 15 Apr 2009 12:06:22 BST</pubDate>
      <link>http://feedproxy.google.com/~r/helicoid_insider/~3/iuGfPYRvbFM/secure_trading_rails_plugin</link>
      <dc:creator>Alex Young</dc:creator>
    <feedburner:origLink>http://insider.helicoid.net/articles/show/36/secure_trading_rails_plugin</feedburner:origLink></item>
    <item>
      <title>Loom exception logging for your apps</title>
      <description>&lt;p&gt;We&amp;#8217;ve recently added exception logging to &lt;a href="http://loomapp.com"&gt;Loom&lt;/a&gt;.  There&amp;#8217;s an open source &lt;a href="http://github.com/alexyoung/loom-exceptions-rails-plugin/tree/master"&gt;rails plugin&lt;/a&gt; which you can use to integrate Loom with your Rails apps.  The &lt;span class="caps"&gt;API&lt;/span&gt; is simple so we&amp;#8217;ll probably collect plugins and libraries for other platforms (I&amp;#8217;ll look into iPhone support for iPhone apps).&lt;/p&gt;


	&lt;p&gt;If you haven&amp;#8217;t seen it before, &lt;a href="http://loomapp.com/"&gt;Loom&lt;/a&gt; is a tiny app we built to track bugs in our apps.  It also works well as a customer support tool.  It originally started off as Bugtagger in 2006, and it had some work towards remote exception logging but this was never finished.  Bugtagger was a quick app I built to support &lt;a href="http://tiktrac.com's"&gt;Tiktrac&lt;/a&gt; customer support in the early days.&lt;/p&gt;


	&lt;p&gt;So now remote exception logging is &lt;em&gt;almost&lt;/em&gt; ready for the public.  You can check out the plugin and integrate it with your apps by adding your Loom account details to a controller.  The &lt;span class="caps"&gt;README&lt;/span&gt; on Github has more details.  The code looks like this:&lt;/p&gt;


&lt;pre&gt;&lt;code&gt;
  enable_loom :email =&amp;gt; 'alex@example.com',
              :password =&amp;gt; 'test',
              :url =&amp;gt; 'http://yoursubdomain.loomapp.com',
              :project_id =&amp;gt; 1
&lt;/code&gt;&lt;/pre&gt;

	&lt;p&gt;There&amp;#8217;s a lot of work to do to make this a truly useful tool, but I have a feeling closely integrating bug reports with support requests and logged exceptions could lead to something incredibly useful.&lt;/p&gt;&lt;img src="http://feeds.feedburner.com/~r/helicoid_insider/~4/5kl-aJ64D6w" height="1" width="1"/&gt;</description>
      <pubDate>Fri, 20 Feb 2009 20:14:47 GMT</pubDate>
      <link>http://feedproxy.google.com/~r/helicoid_insider/~3/5kl-aJ64D6w/loom_exception_logging_for_your_apps</link>
      <dc:creator>Alex Young</dc:creator>
    <feedburner:origLink>http://insider.helicoid.net/articles/show/35/loom_exception_logging_for_your_apps</feedburner:origLink></item>
    <item>
      <title>Add ShareThis buttons to Tumblr</title>
      <description>&lt;p&gt;&lt;img src="http://dl.getdropbox.com/u/221414/helicoid/insider/sharethis.png" alt="" /&gt; &lt;img src="http://dl.getdropbox.com/u/221414/helicoid/insider/tumblr.png" alt="" /&gt;&lt;/p&gt;


	&lt;p&gt;&lt;a href="http://tumblr.com"&gt;Tumblr&lt;/a&gt; can be extended heavily by editing the templates and by adding services through JavaScript.  I just wrote this &lt;a href="http://sharethis.com"&gt;ShareThis&lt;/a&gt; JavaScript that will automatically add ShareThis buttons to your Tumblr posts.  This will:&lt;/p&gt;


	&lt;ul&gt;
	&lt;li&gt;Add Digg/Delicious/etc buttons in a menu so people can easily Digg your articles&lt;/li&gt;
		&lt;li&gt;Add the correct link and title to pages on your Tumblr index page, as well as on the article pages themselves&lt;/li&gt;
	&lt;/ul&gt;


	&lt;p&gt;Here&amp;#8217;s the code:&lt;/p&gt;


&lt;script src="http://gist.github.com/64346.js"&gt;&lt;/script&gt;

	&lt;p&gt;To use it follow these steps:&lt;/p&gt;


	&lt;ul&gt;
	&lt;li&gt;Sign up to &lt;a href="http://sharethis.com"&gt;ShareThis&lt;/a&gt;&lt;/li&gt;
		&lt;li&gt;Create JavaScript for &amp;#8220;Other Blog&amp;#8221; (&lt;strong&gt;ShareThis JS&lt;/strong&gt;)&lt;/li&gt;
		&lt;li&gt;Login to Tumblr, and go to Customize, Theme&lt;/li&gt;
		&lt;li&gt;Get my JavaScript: &lt;a href="http://gist.github.com/64346"&gt;tumblr_sharethis.js&lt;/a&gt;&lt;/li&gt;
		&lt;li&gt;Replace the first &lt;strong&gt;script&lt;/strong&gt; line with your ShareThis JavaScript (&lt;strong&gt;ShareThis JS&lt;/strong&gt;)&lt;/li&gt;
		&lt;li&gt;Paste your edited tumblr_sharethis.js into the header of your Tumblr Template&lt;/li&gt;
		&lt;li&gt;Change the template where it says {Block:regular} to include &lt;code&gt;&amp;amp;lt;div class="ShareThis"&amp;amp;gt;&amp;amp;lt;/div&amp;amp;gt;&lt;/code&gt; after {Body}&lt;/li&gt;
	&lt;/ul&gt;


	&lt;p&gt;I&amp;#8217;ve been quite explicit with these instructions but it&amp;#8217;s actually very easy to do.  Just remember that you need to edit my JavaScript with your ShareThis code, because ShareThis uses your publisher ID for its &lt;span class="caps"&gt;API&lt;/span&gt;.&lt;/p&gt;


	&lt;p&gt;You can see this JavaScript in use on &lt;a href="http://quiteuseful.co.uk"&gt;QuiteUseful&lt;/a&gt;.&lt;/p&gt;&lt;img src="http://feeds.feedburner.com/~r/helicoid_insider/~4/uSQBXCdJ4jk" height="1" width="1"/&gt;</description>
      <pubDate>Sat, 14 Feb 2009 11:51:06 GMT</pubDate>
      <link>http://feedproxy.google.com/~r/helicoid_insider/~3/uSQBXCdJ4jk/add_sharethis_buttons_to_tumblr</link>
      <dc:creator>Alex Young</dc:creator>
    <feedburner:origLink>http://insider.helicoid.net/articles/show/34/add_sharethis_buttons_to_tumblr</feedburner:origLink></item>
    <item>
      <title>Awesome Helipad ruby gem</title>
      <description>&lt;p&gt;If you want to write software that works with Helipad, there&amp;#8217;s now an awesome &lt;a href="http://blog.nyerm.com/2009/01/08/helipad-ruby-interface/"&gt;Helipad gem&lt;/a&gt; written by Lonnon Foster.  I&amp;#8217;ve checked over the code, and it&amp;#8217;s clean and well-written.&lt;/p&gt;


	&lt;p&gt;You could build a lot of cool stuff with this.  How about a &lt;a href="http://shoooes.net/"&gt;Shoes&lt;/a&gt; Helipad app?  Or maybe something with RubyCocoa?  Let us know what you build and we&amp;#8217;ll give you mad props (and possibly riches).&lt;/p&gt;


	&lt;p&gt;Here&amp;#8217;s some example code from Lonnon&amp;#8217;s library:&lt;/p&gt;


&lt;script src="http://gist.github.com/53931.js"&gt;&lt;/script&gt;&lt;img src="http://feeds.feedburner.com/~r/helicoid_insider/~4/S-jaRksFUX4" height="1" width="1"/&gt;</description>
      <pubDate>Wed, 28 Jan 2009 12:46:18 GMT</pubDate>
      <link>http://feedproxy.google.com/~r/helicoid_insider/~3/S-jaRksFUX4/awesome_helipad_ruby_gem</link>
      <dc:creator>Alex Young</dc:creator>
    <feedburner:origLink>http://insider.helicoid.net/articles/show/33/awesome_helipad_ruby_gem</feedburner:origLink></item>
    <item>
      <title>Unified Single Site Browser (SSB) support</title>
      <description>&lt;p&gt;&lt;img src="http://dl.getdropbox.com/u/221414/blogs/ssbx.png" alt="" /&gt;&lt;/p&gt;


	&lt;p&gt;In &lt;a href="http://insider.helicoid.net/articles/show/30/rapid_fluid_support"&gt;Rapid Fluid support&lt;/a&gt; I talked about adapting web applications for &lt;a href="http://fluidapp.com"&gt;Fluid&lt;/a&gt;.  Since &lt;a href="http://bubbleshq.com"&gt;Bubbles&lt;/a&gt; for Windows has a similar &lt;span class="caps"&gt;API I&lt;/span&gt; decided to write a wrapper around Fluid and Bubbles so you can support both SSBs in your web applications.&lt;/p&gt;


	&lt;p&gt;This wrapper is called &lt;a href="http://github.com/alexyoung/ssbx/tree/master"&gt;ssbx&lt;/a&gt;, and is used like this:&lt;/p&gt;


&lt;pre class="code"&gt;
&lt;code&gt;
if (SSBX.isAvailable()) {
  SSBX.log('hello');
  SSBX.notify({ message: 'This is a message', title: 'Title' });
  SSBX.notifyOnce({ message: 'This is a message', title: 'Title', unique_id: 5});
  SSBX.setDockBadge(5);
  SSBX.log(SSBX.availableDriver);
}
&lt;/code&gt;&lt;/pre&gt;

	&lt;p&gt;You can also only run when there&amp;#8217;s a compatible &lt;span class="caps"&gt;SSB&lt;/span&gt; available like this:&lt;/p&gt;


&lt;pre class="code"&gt;
&lt;code&gt;
SSBX.run(function() {
  SSBX.log('hello');
}
&lt;/code&gt;&lt;/pre&gt;

	&lt;p&gt;It&amp;#8217;s still evolving, so add it to your watch list on GitHub if you&amp;#8217;d like to stay up to date.&lt;/p&gt;&lt;img src="http://feeds.feedburner.com/~r/helicoid_insider/~4/ViKGS5JBhqw" height="1" width="1"/&gt;</description>
      <pubDate>Wed, 14 Jan 2009 16:04:00 GMT</pubDate>
      <link>http://feedproxy.google.com/~r/helicoid_insider/~3/ViKGS5JBhqw/unified_single_site_browser_ssb_support</link>
      <dc:creator>Alex Young</dc:creator>
    <feedburner:origLink>http://insider.helicoid.net/articles/show/32/unified_single_site_browser_ssb_support</feedburner:origLink></item>
    <item>
      <title>Tips on sending SMS from web applications</title>
      <description>&lt;p&gt;We recently added &lt;span class="caps"&gt;SMS&lt;/span&gt; support to &lt;a href="http://deadlineapp.com"&gt;Deadline&lt;/a&gt;.  Finding a bulk &lt;span class="caps"&gt;SMS&lt;/span&gt; provider that we liked was actually more work than supporting the technical requirements in our software.&lt;/p&gt;


	&lt;p&gt;There used to be several free web and email to &lt;span class="caps"&gt;SMS&lt;/span&gt; gateways.  Certain mobile operators in the US make still make this possible (also Orange in France have a developer programme that provides free &lt;span class="caps"&gt;SMS&lt;/span&gt; for France).  However: &lt;strong&gt;generally &lt;span class="caps"&gt;SMS&lt;/span&gt; is expensive&lt;/strong&gt;.&lt;/p&gt;


	&lt;p&gt;Most bulk &lt;span class="caps"&gt;SMS&lt;/span&gt; providers allow you to buy credits.  The cost of sending a message to a phone on in each country (and for each operator) varies.  For example, Clickatell charge you 1 credit for the whole of the US, but operators in the UK vary between 1 and 2 credits.&lt;/p&gt;


	&lt;p&gt;We tested both free and commercial bulk &lt;span class="caps"&gt;SMS&lt;/span&gt; services, with the following criteria:&lt;/p&gt;


	&lt;ul&gt;
	&lt;li&gt;Transparent international support&lt;/li&gt;
		&lt;li&gt;Clean &lt;span class="caps"&gt;API&lt;/span&gt;&lt;/li&gt;
		&lt;li&gt;Affordable pricing&lt;/li&gt;
	&lt;/ul&gt;


	&lt;p&gt;Technically supporting &lt;span class="caps"&gt;SMS&lt;/span&gt; is incredibly easy.  Our code basically does the following:&lt;/p&gt;


	&lt;ul&gt;
	&lt;li&gt;Tracks the number of messages sent per-user each day to prevent abuse&lt;/li&gt;
		&lt;li&gt;Records message send status to catch phone numbers the bulk &lt;span class="caps"&gt;SMS&lt;/span&gt; provider has issues with&lt;/li&gt;
		&lt;li&gt;Abstracts the bulk &lt;span class="caps"&gt;SMS&lt;/span&gt; provider&amp;#8217;s &lt;span class="caps"&gt;API&lt;/span&gt; in case we need to switch&lt;/li&gt;
	&lt;/ul&gt;


	&lt;h3&gt;Pitfalls to watch out for&lt;/h3&gt;


	&lt;p&gt;There&amp;#8217;s actually a few pitfalls to be aware of when sending messages:&lt;/p&gt;


	&lt;ul&gt;
	&lt;li&gt;Be sure to check your provider&amp;#8217;s &lt;span class="caps"&gt;API&lt;/span&gt; documentation for acceptable telephone number formats (most expect integers with full international dialing code and no + at the start)&lt;/li&gt;
		&lt;li&gt;Even though some providers allow you to set an alphanumeric from address, some mobile operators have equipment that can&amp;#8217;t cope with &lt;span class="caps"&gt;UTF&lt;/span&gt;-8 or spaces&lt;/li&gt;
		&lt;li&gt;Check that the bulk &lt;span class="caps"&gt;SMS&lt;/span&gt; provider you&amp;#8217;re looking at can provide message receipts so you can track users who are having problems receiving messages&lt;/li&gt;
	&lt;/ul&gt;


	&lt;h3&gt;Services&lt;/h3&gt;


	&lt;p&gt;We found Wadja and Clickatell to be the best services in our roundup.&lt;/p&gt;


	&lt;p&gt;&lt;a href="http://wadja.com"&gt;wadja.com&lt;/a&gt; has an easy to use &lt;span class="caps"&gt;API&lt;/span&gt; and allows you to send a fixed number of free messages per day.  Your registered name is sent as the &amp;#8220;from&amp;#8221; name for the &lt;span class="caps"&gt;SMS&lt;/span&gt;, and they add adverts to support their service. They support international messaging and also allow you to pay for messages to remove ads.&lt;/p&gt;


	&lt;p&gt;&lt;a href="http://www.clickatell.com/"&gt;Clickatell&lt;/a&gt; is purely focused on &lt;span class="caps"&gt;SMS&lt;/span&gt;.  As such their documentation makes the pitfalls of sending &lt;span class="caps"&gt;SMS&lt;/span&gt; very clear.  Their &lt;span class="caps"&gt;XML&lt;/span&gt; APIs are a little bit overcomplicated for what they do, but their &lt;span class="caps"&gt;HTTP API&lt;/span&gt; does almost everything and is as easy as sending a &lt;span class="caps"&gt;HTTP GET&lt;/span&gt;.&lt;/p&gt;


	&lt;h3&gt;Conclusion&lt;/h3&gt;


	&lt;p&gt;We got Deadline working with both Wadja and Clickatell&amp;#8212;the live site is currently using Clickatell.  We ended up purchasing &lt;span class="caps"&gt;SMS&lt;/span&gt; credits because of the restrictions of Wadja&amp;#8217;s free service.&lt;/p&gt;


	&lt;p&gt;It&amp;#8217;s frustrating when larger businesses can absorb the cost of &lt;span class="caps"&gt;SMS&lt;/span&gt; and make them free, but this is impossible for a small business like ours.  That means our supporting business model was one of the most important considerations when adding &lt;span class="caps"&gt;SMS&lt;/span&gt; to our app.&lt;/p&gt;&lt;img src="http://feeds.feedburner.com/~r/helicoid_insider/~4/7T8plkIT8wE" height="1" width="1"/&gt;</description>
      <pubDate>Sat,  3 Jan 2009 15:56:49 GMT</pubDate>
      <link>http://feedproxy.google.com/~r/helicoid_insider/~3/7T8plkIT8wE/tips_on_sending_sms_from_web_applications</link>
      <dc:creator>Alex Young</dc:creator>
    <feedburner:origLink>http://insider.helicoid.net/articles/show/31/tips_on_sending_sms_from_web_applications</feedburner:origLink></item>
    <item>
      <title>Rapid Fluid support</title>
      <description>&lt;p&gt;&lt;a href="http://fluidapp.com/"&gt;Fluid&lt;/a&gt; is a Site Specific Browser (SSB) for Mac OS.  It allows web applications to run in their own window with a dock icon, dock badge and supports &lt;a href="http://growl.info/"&gt;Growl&lt;/a&gt; notifications.  You can support Fluid in your web app without any server-side code changes.&lt;/p&gt;


	&lt;p&gt;I recently added Fluid support to &lt;a href="http://deadlineapp.com"&gt;Deadline&lt;/a&gt;.  Deadline basically a calendar/reminder system, so it works particularly well with Growl notifications.  It was a rewarding development experience: Fluid&amp;#8217;s &lt;span class="caps"&gt;API&lt;/span&gt; is clean and delivers excellent results with minimal effort.&lt;/p&gt;


	&lt;h3&gt;Browser support detection&lt;/h3&gt;


	&lt;p&gt;It&amp;#8217;s important to check if Fluid is available before trying to call its &lt;span class="caps"&gt;API&lt;/span&gt;.&lt;/p&gt;


	&lt;p&gt;The easiest way to do this is:&lt;/p&gt;


&lt;pre class="code"&gt;
&lt;code&gt;
  if (!Object.isUndefined(window.fluid)) {
    return new FluidAppMode
  }
&lt;/code&gt;
&lt;/pre&gt;

	&lt;p&gt;This stops browsers from trying to load Fluid functionality when its not available.&lt;/p&gt;


	&lt;h3&gt;Auto-refresh&lt;/h3&gt;


	&lt;p&gt;Deadline displays a list of events with no automatic browser refresh.  When adding Fluid support I decided to automatically update the displayed items using &lt;code&gt;PeriodicalExecuter&lt;/code&gt; (from Prototype).  This makes it feel a little bit more application-like.  Deadline shows inline editors, so it&amp;#8217;s trivial to check if any inline editors are visible to hold off &lt;code&gt;PeriodicalExecuter&lt;/code&gt; to prevent it from updating while the user is editing.&lt;/p&gt;


	&lt;p&gt;As time goes by each deadline&amp;#8217;s event date is reached and a notification is displayed.  To track which ones have been displayed I use a cookie that stores a list of deadline IDs.&lt;/p&gt;


	&lt;h3&gt;Dock badge&lt;/h3&gt;


	&lt;p&gt;Deadline reminders that are ready are counted and displayed in the dock icon:&lt;/p&gt;


	&lt;p&gt;&lt;img src="http://dl.getdropbox.com/u/221414/deadline/deadline_dock.png" alt="" /&gt;&lt;/p&gt;


	&lt;p&gt;Changing the dock icon is easy: &lt;code&gt;window.fluid.dockBadge = count&lt;/code&gt;.  To avoid changing server-side code, I derive the dock badge count by counting the number of deadlines marked with &amp;#8220;Now&amp;#8221;.&lt;/p&gt;


	&lt;h3&gt;Growl Notifications&lt;/h3&gt;


	&lt;p&gt;&lt;img src="http://dl.getdropbox.com/u/221414/deadline/deadline_fluid.png" alt="" /&gt;&lt;/p&gt;


	&lt;p&gt;Growl notifications can be displayed with this Fluid &lt;span class="caps"&gt;API&lt;/span&gt; call:&lt;/p&gt;


&lt;pre class="code"&gt;
&lt;code&gt;
    window.fluid.showGrowlNotification({
        title: "Deadline Reminder", 
        description: message, 
        priority: 1, 
        sticky: false,
        identifier: message_id
    })
&lt;/code&gt;
&lt;/pre&gt;

	&lt;p&gt;You can&amp;#8217;t just display notifications blindly, however.  For Deadline I decided to record the displayed deadline IDs using a cookie.  We have an internal JavaScript library called Azuki which abstracts cookie management, but you should be able to follow this code without it:&lt;/p&gt;


&lt;pre class="code"&gt;
&lt;code&gt;
  showNotifierMessage: function(message, message_id) {
    if (this.displayedMessage(message_id)) {
      return
    }

    window.fluid.showGrowlNotification({
        title: "Deadline Reminder", 
        description: message, 
        priority: 1, 
        sticky: false,
        identifier: message_id
    })

    this.logMessageDisplay(message_id)
  },

  logMessageDisplay: function(message_id) {
    // Log message to cookie
    var deadline_notifier_messages = Azuki.Storage.Cookie.find('deadline_notifier_messages')

    if (Object.isUndefined(deadline_notifier_messages)) {
      deadline_notifier_messages = message_id
    } else {
      deadline_notifier_messages = deadline_notifier_messages + ',' + message_id
    }

    Azuki.Storage.Cookie.create('deadline_notifier_messages', deadline_notifier_messages, 365)

    this.log(deadline_notifier_messages)
  },

  displayedMessage: function(message_id) {
    var deadline_notifier_messages = Azuki.Storage.Cookie.find('deadline_notifier_messages')
    message_id = parseInt(message_id)

    if (Object.isUndefined(deadline_notifier_messages)) {
      return false
    }

    var message_ids = $A(deadline_notifier_messages.split(','))

    return message_ids.find(function(logged_id) {
      if (parseInt(logged_id) == message_id) {
        return true
      }
    })
  }
&lt;/code&gt;
&lt;/pre&gt;

	&lt;p&gt;As you can see I store each displayed deadline ID separated by commas in a cookie.  This then allows Deadline to only notify new deadlines.&lt;/p&gt;


	&lt;h3&gt;Supporting multiple SSBs&lt;/h3&gt;


	&lt;p&gt;There&amp;#8217;s a few SSBs available.  I&amp;#8217;ve chosen to support &lt;a href="http://bubbleshq.com/api"&gt;Bubbles&lt;/a&gt; for Windows.&lt;/p&gt;


	&lt;p&gt;To support multiple SSBs you could either use inheritance or delegation to manage shared code.  In Deadline&amp;#8217;s case I&amp;#8217;ve shared the notification cookie management code and the use of PeriodicalExecuter between Fluid and Bubbles using inheritance.  Remember that Prototype&amp;#8217;s inheritance mechanism is now specified through &lt;code&gt;Class.create&lt;/code&gt;&lt;/p&gt;


&lt;pre class="code"&gt;
&lt;code&gt;
var FluidAppMode = Class.create(WebAppMode, {
  initialize: function() {
&lt;/code&gt;
&lt;/pre&gt;

	&lt;p&gt;I&amp;#8217;ve detected the availability of Bubbles as follows:&lt;/p&gt;


&lt;pre class="code"&gt;
&lt;code&gt;
  if (typeof(SSB) != 'undefined') {
    return new BubblesAppMode
  }
&lt;/code&gt;
&lt;/pre&gt;

	&lt;p&gt;Bubbles&amp;#8217; &lt;span class="caps"&gt;API&lt;/span&gt; is accessed through the &lt;span class="caps"&gt;SSB&lt;/span&gt; singleton object.&lt;/p&gt;


	&lt;h3&gt;Share your &lt;span class="caps"&gt;SSB&lt;/span&gt; support&lt;/h3&gt;


	&lt;p&gt;Have you supported an &lt;span class="caps"&gt;SSB&lt;/span&gt; with your web app?  Why not share your links and experiences in the comments?&lt;/p&gt;&lt;img src="http://feeds.feedburner.com/~r/helicoid_insider/~4/C1lW1S-FHu4" height="1" width="1"/&gt;</description>
      <pubDate>Fri, 26 Dec 2008 15:59:49 GMT</pubDate>
      <link>http://feedproxy.google.com/~r/helicoid_insider/~3/C1lW1S-FHu4/rapid_fluid_support</link>
      <dc:creator>Alex Young</dc:creator>
    <feedburner:origLink>http://insider.helicoid.net/articles/show/30/rapid_fluid_support</feedburner:origLink></item>
  </channel>
</rss>
