<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0"
	xmlns:content="http://purl.org/rss/1.0/modules/content/"
	xmlns:wfw="http://wellformedweb.org/CommentAPI/"
	xmlns:dc="http://purl.org/dc/elements/1.1/"
	xmlns:atom="http://www.w3.org/2005/Atom"
	xmlns:sy="http://purl.org/rss/1.0/modules/syndication/"
	xmlns:slash="http://purl.org/rss/1.0/modules/slash/"
	>

<channel>
	<title>HiddenTao</title>
	<atom:link href="http://www.hiddentao.com/feed/" rel="self" type="application/rss+xml" />
	<link>http://www.hiddentao.com</link>
	<description>software, websites, mobile, technology</description>
	<lastBuildDate>Sun, 01 Apr 2012 17:01:57 +0000</lastBuildDate>
	<language>en</language>
	<sy:updatePeriod>hourly</sy:updatePeriod>
	<sy:updateFrequency>1</sy:updateFrequency>
	<generator>http://wordpress.org/?v=</generator>
		<item>
		<title>google-tts &#8211; a Javascript API for Google Text-to-Speech engine</title>
		<link>http://www.hiddentao.com/archives/2012/04/01/google-tts-a-javascript-api-for-google-text-to-speech-engine/</link>
		<comments>http://www.hiddentao.com/archives/2012/04/01/google-tts-a-javascript-api-for-google-text-to-speech-engine/#comments</comments>
		<pubDate>Sun, 01 Apr 2012 16:28:02 +0000</pubDate>
		<dc:creator>ram</dc:creator>
				<category><![CDATA[Uncategorized]]></category>
		<category><![CDATA[Audio]]></category>
		<category><![CDATA[Github]]></category>
		<category><![CDATA[HTML5]]></category>
		<category><![CDATA[Javascript]]></category>
		<category><![CDATA[Speech]]></category>

		<guid isPermaLink="false">http://www.hiddentao.com/?p=1395</guid>
		<description><![CDATA[One of the features I want to add to my experimental &#8220;learn Chinese&#8221; web app &#8211; http://zhongwen.co.uk/ &#8211; is the ability to have the Chinese characters read out to you so that you can practise the sounds too. To this end I&#8217;d been looking for web services which would do the text-to-speech conversion for me [...]]]></description>
			<content:encoded><![CDATA[<p>One of the features I want to add to my experimental &#8220;learn Chinese&#8221; web app &#8211; <a href="http://zhongwen.co.uk/" class="link-external">http://zhongwen.co.uk/</a> &#8211; is the ability to have the Chinese characters read out to you so that you can practise the sounds too. To this end I&#8217;d been looking for web services which would do the text-to-speech conversion for me and stumbed upon (http://weston.ruter.net/projects/google-tts/) which demonstrates how to use Google&#8217;s text-to-speech engine from Javascript. This is the engine which reads out the translations on <a href="http://translate.google.com/" class="link-external">Google translate</a> and using it as simple as calling a URL with some parameters and then playing the MP3 which gets returned.</p>

<p>I decided to spend a few hours packaging up Weston&#8217;s code as a re-usable library and here it is &#8211; <a href="https://github.com/hiddentao/google-tts" class="link-external">https://github.com/hiddentao/google-tts</a>. It&#8217;s small (&lt; 1KB minified and gzipped), supports 42 languages, and plays back the audio using the HTML5 Audio tag (just as Weston&#8217;s example does).</p>

<p>Note: Audio playback in the browser will only work when running the script locally as Google&#8217;s server only
returns audio if you can prevent the browser from sending the Referrer HTTP Header to their server. If you want to add
background playback to your online site perhaps <a href="http://www.schillmania.com/projects/soundmanager2/" class="link-external">SoundManager</a>
will do the trick (I haven&#8217;t tested this).</p>
]]></content:encoded>
			<wfw:commentRss>http://www.hiddentao.com/archives/2012/04/01/google-tts-a-javascript-api-for-google-text-to-speech-engine/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>Weber &#8211; compile scripts, stylesheets and templates on-the-fly</title>
		<link>http://www.hiddentao.com/archives/2012/03/15/weber-compile-scripts-stylesheets-and-templates-on-the-fly/</link>
		<comments>http://www.hiddentao.com/archives/2012/03/15/weber-compile-scripts-stylesheets-and-templates-on-the-fly/#comments</comments>
		<pubDate>Thu, 15 Mar 2012 08:08:27 +0000</pubDate>
		<dc:creator>ram</dc:creator>
				<category><![CDATA[Uncategorized]]></category>
		<category><![CDATA[CoffeeScript]]></category>
		<category><![CDATA[Javascript]]></category>
		<category><![CDATA[Utils]]></category>

		<guid isPermaLink="false">http://www.hiddentao.com/?p=1386</guid>
		<description><![CDATA[I&#8217;d like to introduce Weber, a slick command-line tool for compiling, concatenating and minifying scripts, stylesheets and templates on-the-fly. Weber is a fork of the excellent Hem tool by Alex Maccaw which improves on the original by providing more flexibility. To get started with Weber install it using npm: Then goto the root of your [...]]]></description>
			<content:encoded><![CDATA[<p>I&#8217;d like to introduce <a href="https://github.com/hiddentao/weber" class="link-external">Weber</a>, a slick command-line tool for compiling, concatenating and minifying scripts, stylesheets and templates <strong>on-the-fly</strong>. Weber is a fork of the excellent <a href="https://github.com/maccaw/hem" class="link-external">Hem</a> tool by <a href="http://alexmaccaw.com/" class="link-external">Alex Maccaw</a> which improves on the original by providing more flexibility.
<span id="more-1386"></span></p>

<p>To get started with Weber install it using <a href="http://npmjs.org" title="Node Package Manager" class="link-external">npm</a>:</p>

<div><pre class="brush: bash; title: ; notranslate">
$ npm install -g weber
</pre></div>

<p>Then goto the root of your project folder and create a skeleton Weber config file with:</p>

<div><pre class="brush: bash; title: ; notranslate">
$ weber init
</pre></div>

<p>At this point you will have a file called <code>weber.json</code> which looks something like this:</p>

<div><pre class="brush: jscript; title: ; notranslate">
{
    &quot;/&quot; : &quot;./public_assets&quot;,

    &quot;/css/app.css&quot; : [
        &quot;./css/reset.css&quot;,
        &quot;./css/main.styl&quot;
    ],

    &quot;/css/test.css&quot; : {
        &quot;minify&quot;: false,
        &quot;input&quot; : [
            &quot;./css/test&quot;
        ]
    },

    &quot;/js/app.js&quot; : {
        &quot;build&quot; : &quot;./app.js&quot;,
        &quot;input&quot; : {
            &quot;dependency&quot; : [ &quot;es5-shimify&quot; ],
            &quot;lib&quot; : [ &quot;./lib/jquery.js&quot; ],
            &quot;module&quot;: [ &quot;./coffee&quot; ]
        }
    },

    &quot;/js/test.js&quot; : {
        &quot;minify&quot;: false,
        &quot;input&quot;: [
            &quot;./test/testbase.js&quot;,
            &quot;./test/test.coffee&quot;
        ]
    }
}
</pre></div>

<p>The above configuration is put there by Weber just to show you what configuration options are available.</p>

<p>You can now run Weber in <em>server</em> mode by typing:</p>

<div><pre class="brush: bash; title: ; notranslate">
$ weber server
</pre></div>

<p>Weber is now running a simple HTTP server accessible at <strong>http://localhost:9294</strong>. This mode allows you to quickly test out and rapidly iterate with your scripts, styles and templates.</p>

<p>The first key-value pair in the config file above is mandatory and tells Weber which folder to use as the document root when running in server mode. In this case Weber will expect an <code>index.html</code> file inside the <code>./public_assets</code> folder to serve up when the browser navigates to the address above. By the way, Weber can be told to listen on a different port by adding a <code>port: &lt;portnum&gt;</code> key in the config file above.</p>

<p>The remaining key-value pairs in the config file tell Weber what to do when the browser visits the relative URLs <code>/css/app.css</code>, <code>/css/test.css</code>, <code>/js/app.js</code> and <code>/js/test.js</code> respectively. When this happens Weber dynamically builds and outputs these files using the information provided in the config file. Here is what it does for each file:</p>

<ul>
<li><code>/css/app.css</code> &#8211; concatenates reset.css with main.styl and then minifies the final result.</li>
<li><code>/css/test.css</code> &#8211; concatenates all CSS and Stylus files in <code>./css/test</code>.</li>
<li><code>/js/apps.js</code> &#8211; concatenates the node module <code>es5-shimify</code> with <code>./lib/jquery.js</code> with all the modularized versions of all the code in <code>./coffee</code> and then minifies the final result.</li>
<li><code>/js/test.js</code> &#8211; concatenates modularized versions of <code>./test/testbase.js</code> and <code>./test/test.coffee</code>.</li>
</ul>

<p>Two points to note:</p>

<ul>
<li>Weber automatically compiles coffee, stylus and other files into their JS and CSS equivalents when needed.</li>
<li>To modularize means to wrap the code such that it thinks of itself and behaves like a CommonJS module, i.e. require, exports, module are available.</li>
</ul>

<p>The example folder in the Weber source code contains a fully working example app with the above configuration file.</p>

<p>Once you&#8217;re done testing in server mode you can tell Weber to build the actual static output files configured by doing:</p>

<div><pre class="brush: bash; title: ; notranslate">
$ weber build
</pre></div>

<p>Weber automatically decided where to place the built output file based on the config. For example, if we have:</p>

<div><pre class="brush: jscript; title: ; notranslate">
{
    &quot;/&quot; : &quot;./public_assets&quot;,
    &quot;/js/app.js&quot; : [ &quot;./coffee&quot; ]
}
</pre></div>

<p>Then the output file for <code>/js/app.js</code> will be at <code>./public_assets/js/app.js</code>. But we can override this path by using the build key as follows:</p>

<div><pre class="brush: jscript; title: ; notranslate">
{
    &quot;/&quot; : &quot;./public_assets&quot;,
    &quot;/js/app.js&quot; : {
        &quot;build&quot; : [ &quot;./built_app.js&quot; ]
        &quot;input&quot; : [ &quot;./coffee&quot; ]
    }
}
</pre></div>

<p>Now Weber will build the output file at <code>./built_app.js</code>. Weber can also watch the input files and folders for changes and automatically rebuild the output files when necessary if you start it in <em>watch</em> mode:</p>

<div><pre class="brush: bash; title: ; notranslate">
$ weber watch
</pre></div>

<p>Weber works quite well for now but there are some limitations. It doesn&#8217;t support all the available dialects and languages for input sources (e.g. SASS, LESS, Mustache) so these need adding. It would also be nice to be able to write pluggable input and output formats for Weber so that you could process your own custom format. And finally Weber needs some tests!</p>

<p>You can fork it at: <a href="https://github.com/hiddentao/weber" class="link-external">https://github.com/hiddentao/weber</a></p>
]]></content:encoded>
			<wfw:commentRss>http://www.hiddentao.com/archives/2012/03/15/weber-compile-scripts-stylesheets-and-templates-on-the-fly/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>Zhongwen.co.uk &#8211; a simple jQuery Mobile web app</title>
		<link>http://www.hiddentao.com/archives/2012/03/14/zhongwen-co-uk-a-simple-jquery-mobile-web-app/</link>
		<comments>http://www.hiddentao.com/archives/2012/03/14/zhongwen-co-uk-a-simple-jquery-mobile-web-app/#comments</comments>
		<pubDate>Wed, 14 Mar 2012 08:27:51 +0000</pubDate>
		<dc:creator>ram</dc:creator>
				<category><![CDATA[Uncategorized]]></category>
		<category><![CDATA[CoffeeScript]]></category>
		<category><![CDATA[jQuery]]></category>
		<category><![CDATA[Mandarin]]></category>
		<category><![CDATA[Mobile]]></category>
		<category><![CDATA[OCR]]></category>

		<guid isPermaLink="false">http://www.hiddentao.com/?p=1379</guid>
		<description><![CDATA[I started attending Mandarin language classes recently at the Meridian Chinese School in London. Studying involves a 2 hour lesson once a week and a few hours spent at home revising what I&#8217;ve learnt. And one of the best ways to study is to practise writing the characters (fun too!) and translating sentences. So I [...]]]></description>
			<content:encoded><![CDATA[<p>I started attending Mandarin language classes recently at the <a href="http://www.meridiandao.co.uk/" class="link-external">Meridian Chinese School</a> in London. Studying involves a 2 hour lesson once a week and a few hours spent at home revising what I&#8217;ve learnt. And one of the best ways to study is to practise writing the characters (fun too!) and translating sentences. So I decided to build a web app which would allow me to practise whilst on the go. My aim was to enable character recognition using HTML 5 canvas and get it working on mobiles.
<span id="more-1379"></span></p>

<p><strong>You can see the result at:  <a href="http://zhongwen.co.uk" class="link-external">zhongwen.co.uk</a>.</strong>  The source is code on <a href="http://github.com/hiddentao/zhongwen" class="link-external">github</a>. It&#8217;s built using <a href="http://github.com/hiddentao/spine" class="link-external">Spine</a>, <a href="http://jquerymobile.com/" class="link-external">jQuery Mobile</a>, <a href="http://coffeescript.org" class="link-external">Coffeescript</a> and <a href="http://github.com/hiddentao/weber" class="link-external">Weber</a>.</p>

<p>Here are some notes on the technical aspects:</p>

<h2>Stroke input recognition</h2>

<p>For the character recognition I was able to find an existing Javascript demo of <a href="http://www.lab4games.net/zz85/blog/2010/02/17/js-%E4%B8%AD%E6%96%87%E7%AC%94%E7%94%BB%E8%BE%93%E5%85%A5%E6%B3%95-javascript-chinese-stroke-input/">stroke input</a>. I grabbed this code, cleaned it up and optimized and got it working in a canvas on my mobile, only to find that the character recognition algorithm was particularly weak. It calculates the angle and length of every stroke you make in proportion to the overall character size and then matches this information to a database of character strokes. The problem is that if there is even a slight different in stroke order the matching will fail to find the right character. It uses the <a href="http://www.lab4games.net/zz85/blog/2010/01/21/geeknotes-shortstrawjs-fast-and-simple-corner-detection/" class="link-external">Shortstraw</a> algorithm for finding corners &#8211; this algorithm doesn&#8217;t tend to do too well for curved lines.</p>

<p>After much testing I decided to disable canvas stroke input for now and instead provide Pinyin input as well as the ability to input characters directly (in case you have a Chinese keyboard input method available for your device, which I do <img src='http://www.hiddentao.com/wp-includes/images/smilies/icon_smile.gif' alt=':)' class='wp-smiley' /> .</p>

<p><em>Note: Thinking about the stroke input recognition, a smarter algorithm would compare the changes in angle and length between consecutive strokes rather than the individual stroke measurements themselves. A harder problem is solving for stroke mismatches. If the user inputs a curved line which gets interpreted as a straight line and yet the actual dictionary character stroke sees it as two closely connected straight lines, how do yo match them up in a consistently, repeatable manner? I might get around to these problems later on. For now you can see the code I&#8217;ve got by looking at the <strong>canvas_stroke_input</strong> branch in the Git repository.</em></p>

<h2>Data strings</h2>

<p>For now I&#8217;ve hard-coded a whole bunch of sentences and their English translations in the <a href="https://github.com/hiddentao/zhongwen/blob/master/js/data.coffee" class="link-external"><code>data</code> module</a>, categorizing them by study unit. In future it would be good to implement true sentence builders, i.e. algorithms which pick a subject, object, action, etc. and construct an appropriate sentence. Such randomization will be a better test for the user.</p>

<h2>The dictionary module</h2>

<p>A core part of the system is the <a href="https://github.com/hiddentao/zhongwen/blob/master/js/dict.coffee" class="link-external"><code>dict</code> module</a>. This contains a list of characters along with their matching pinyin representations (one character may have multiple pinyin representations) and also contains methods for looking up character by pinyin.</p>

<p>There is also a <code>Sentence</code> object. This takes as input a string of characters and then allows you to see whether they match another string of characters to. The matching algorithm is careful enough to avoid punctuation marks (because different users may input them differently) and also returns a list of mismatched characters. To understandl exactly how it works you can look at the <a href="https://github.com/hiddentao/zhongwen/blob/master/test/dict.coffee" class="link-external">nodeunit test</a> for this module.</p>

<h2>Contributions</h2>

<p>I&#8217;ll happily accept any contributions to making this site better, and in particular any help on the stroke input recognition system. Please feel free to fork the <a href="https://github.com/hiddentao/zhongwen" class="link-external">github repo</a>.</p>
]]></content:encoded>
			<wfw:commentRss>http://www.hiddentao.com/archives/2012/03/14/zhongwen-co-uk-a-simple-jquery-mobile-web-app/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>My talk on Squel.js at the London Node.js User Group</title>
		<link>http://www.hiddentao.com/archives/2012/03/02/my-talk-on-squel-js-at-the-london-node-js-user-group/</link>
		<comments>http://www.hiddentao.com/archives/2012/03/02/my-talk-on-squel-js-at-the-london-node-js-user-group/#comments</comments>
		<pubDate>Fri, 02 Mar 2012 18:49:54 +0000</pubDate>
		<dc:creator>ram</dc:creator>
				<category><![CDATA[Uncategorized]]></category>
		<category><![CDATA[Javascript]]></category>
		<category><![CDATA[node.js]]></category>
		<category><![CDATA[Squel]]></category>
		<category><![CDATA[Video]]></category>

		<guid isPermaLink="false">http://www.hiddentao.com/?p=1371</guid>
		<description><![CDATA[Here is the talk I did on Squel a few weeks back at the London Node.js User Group February meetup. I realize I was speaking very fast &#8211; it was my first ever tech presentation and in front of 50+ people so I was pretty nervous! Nevertheless, my slides did a good job enough: LNUG [...]]]></description>
			<content:encoded><![CDATA[<p>Here is the talk I did on <a href="http://www.hiddentao.com/archives/2011/12/23/squel-js-lightweight-javascript-library-for-building-sql-query-strings-in-node-js-or-the-browser/" title="Squel.js – lightweight Javascript library for building SQL query strings in node.js or the browser">Squel</a> a few weeks back at the <a href="http://lnug.org/" class="link-external">London Node.js User Group</a> February meetup. I realize I was speaking very fast &#8211; it was my first ever tech presentation and in front of 50+ people so I was pretty nervous! Nevertheless, my slides did a good job enough:
<span id="more-1371"></span></p>

<iframe src="http://player.vimeo.com/video/37796578?title=0&amp;byline=0&amp;portrait=0" width="400" height="225" frameborder="0" webkitAllowFullScreen mozallowfullscreen allowFullScreen></iframe>

<p><a href="http://vimeo.com/37796578" class="link-external">LNUG February 2012 &#8211; Ramesh Nair</a> from <a href="http://vimeo.com/forwardtechnology" class="link-external">Forward Technology</a> on <a href="http://vimeo.com" class="link-external">Vimeo</a>.</p>
]]></content:encoded>
			<wfw:commentRss>http://www.hiddentao.com/archives/2012/03/02/my-talk-on-squel-js-at-the-london-node-js-user-group/feed/</wfw:commentRss>
		<slash:comments>3</slash:comments>
		</item>
		<item>
		<title>Squel.js site is now mobile-friendly</title>
		<link>http://www.hiddentao.com/archives/2012/02/06/squel-js-site-is-now-mobile-friendly/</link>
		<comments>http://www.hiddentao.com/archives/2012/02/06/squel-js-site-is-now-mobile-friendly/#comments</comments>
		<pubDate>Mon, 06 Feb 2012 13:39:48 +0000</pubDate>
		<dc:creator>ram</dc:creator>
				<category><![CDATA[Uncategorized]]></category>
		<category><![CDATA[CoffeeScript]]></category>
		<category><![CDATA[Javascript]]></category>
		<category><![CDATA[node.js]]></category>
		<category><![CDATA[Squel]]></category>

		<guid isPermaLink="false">http://www.hiddentao.com/?p=1363</guid>
		<description><![CDATA[I&#8217;ve added some media queries to the squeljs.org sytlesheet so it should now look and work better on your mobile device screen. Plus I&#8217;m (hopefully) giving a talk tonight at London CoffeeScript meetup on Squel and my thoughts on CoffeeScript so far. Here&#8217;s a prezzi I&#8217;ve made for this (use the links down below if [...]]]></description>
			<content:encoded><![CDATA[<p>I&#8217;ve added some media queries to the <a href="http://squeljs.org/" class="link-external">squeljs.org</a> sytlesheet so it should now look and work better on your mobile device screen. Plus I&#8217;m (hopefully) giving a talk tonight at <a href="http://www.meetup.com/London-CoffeeScript/events/47483522/" class="link-external">London CoffeeScript meetup</a> on Squel and my thoughts on CoffeeScript so far. Here&#8217;s a prezzi I&#8217;ve made for this (use the links down below if you can&#8217;t see it):
<span id="more-1363"></span></p>

<div class="prezi-player"><style type="text/css" media="screen">.prezi-player { width: 550px; } .prezi-player-links { text-align: center; }</style><object id="prezi_8fdvjo6ripkw" name="prezi_8fdvjo6ripkw" classid="clsid:D27CDB6E-AE6D-11cf-96B8-444553540000" width="550" height="400"><param name="movie" value="http://prezi.com/bin/preziloader.swf"/><param name="allowfullscreen" value="true"/><param name="allowscriptaccess" value="always"/><param name="bgcolor" value="#ffffff"/><param name="flashvars" value="prezi_id=8fdvjo6ripkw&amp;lock_to_path=0&amp;color=ffffff&amp;autoplay=no&amp;autohide_ctrls=0"/><embed id="preziEmbed_8fdvjo6ripkw" name="preziEmbed_8fdvjo6ripkw" src="http://prezi.com/bin/preziloader.swf" type="application/x-shockwave-flash" allowfullscreen="true" allowscriptaccess="always" width="550" height="400" bgcolor="#ffffff" flashvars="prezi_id=8fdvjo6ripkw&amp;lock_to_path=0&amp;color=ffffff&amp;autoplay=no&amp;autohide_ctrls=0"></embed></object><div class="prezi-player-links"><p><a href="http://prezi.com/8fdvjo6ripkw/squeljs/" title="Squel.js" class="link-external">Squel.js</a> on <a href="http://prezi.com" class="link-external">Prezi</a></p></div></div>

<p><strong>Update:</strong> I recently gave a <a href="http://www.hiddentao.com/archives/2012/03/02/my-talk-on-squel-js-at-the-london-node-js-user-group/" class="liinternal">talk on Squel</a> at the <a href="http://lnug.org/" class="link-external">London Node.js User Group</a> meetup.</p>
]]></content:encoded>
			<wfw:commentRss>http://www.hiddentao.com/archives/2012/02/06/squel-js-site-is-now-mobile-friendly/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>Processing long-running Django tasks using Celery + RabbitMQ + Supervisord + Monit</title>
		<link>http://www.hiddentao.com/archives/2012/01/27/processing-long-running-django-tasks-using-celery-rabbitmq-supervisord-monit/</link>
		<comments>http://www.hiddentao.com/archives/2012/01/27/processing-long-running-django-tasks-using-celery-rabbitmq-supervisord-monit/#comments</comments>
		<pubDate>Fri, 27 Jan 2012 13:11:27 +0000</pubDate>
		<dc:creator>ram</dc:creator>
				<category><![CDATA[Uncategorized]]></category>
		<category><![CDATA[Celery]]></category>
		<category><![CDATA[Django]]></category>
		<category><![CDATA[Python]]></category>
		<category><![CDATA[RabbitMQ]]></category>

		<guid isPermaLink="false">http://www.hiddentao.com/?p=1356</guid>
		<description><![CDATA[For a recent project I&#8217;m working on we had a requirement to be able to process long-running tasks in Django triggered by user interaction on the front-end. For instance, the user would click a button on the web page in order to trigger the back-end to, for example, build a CSV file containing a subset [...]]]></description>
			<content:encoded><![CDATA[<p>For a recent project I&#8217;m working on we had a requirement to be able to process long-running tasks in Django triggered by user interaction on the front-end. For instance, the user would click a button on the web page in order to trigger the back-end to, for example, build a CSV file containing a subset of the data in the database. In the browser a little popup window would get displayed, showing the progress of the task in the back-end. Once the task got completed in the back-end the user would be notified in the front-end of its completion and provided a link to download the final output (e.g. the built CSV file).
<span id="more-1356"></span></p>

<p>For a requirement like this, you don&#8217;t really want to be performing such a long-running task as part of the standard Django <em>request -> response</em> cycle since 1) the Django server is single-threaded, and 2) even if you&#8217;re using <a href="http://code.google.com/p/modwsgi/wiki/IntegrationWithDjango" class="link-external">WSGI</a> to run your Django app behind Apache or a similar server you&#8217;re not really going to want one of your Apache worker processes being tied up with a single client request for long periods of time as this would adversely impact other incoming client requests. Thus, any such long-running task should be performed in a process which then informs the main Django app once it&#8217;s done. <a href="http://celeryproject.org/" class="link-external">Celery</a> is a readily-available such system (a <em>task-queue</em> to be precise) which enables this and it is easy to integrate into Django using <a href="http://pypi.python.org/pypi/django-celery" class="link-external">django-celery</a>.</p>

<h2>Setting up Django Celery</h2>

<p>Setting up Django Celery has already been <a href="http://django-celery.readthedocs.org/en/latest/getting-started/first-steps-with-django.html" class="link-external">documented</a> <a href="http://pypi.python.org/pypi/django-celery" class="link-external">elsewhere</a> so I&#8217;ll simply list the settings I used to get things working (<em>Note: I&#8217;m assuming that you&#8217;re running a Debian-based Linux system</em>). First of all I installed <a href="">RabbitMQ</a> to use the message queue system:</p>

<div><pre class="brush: bash; title: ; notranslate">
$ sudo apt-get install rabbitmq-server
</pre></div>

<p>Then I added a <code>vhost</code> and username and password for my Django app to RabbitMQ:</p>

<div><pre class="brush: bash; title: ; notranslate">
$ sudo rabbitmqctl add_user myapp myapp
$ sudo rabbitmqctl add_vhost myapp
$ sudo rabbitmqctl set_permissions -p myapp myapp &quot;.*&quot; &quot;.*&quot; &quot;.*&quot;
</pre></div>

<p>Then in my <code>celeryconfig.py</code> I set the following:</p>

<div><pre class="brush: python; title: ; notranslate">
BROKER_HOST = &quot;127.0.0.1&quot;
BROKER_PORT = 5672   # default RabbitMQ listening port
BROKER_USER = &quot;myapp&quot;
BROKER_PASSWORD = &quot;myapp&quot;
BROKER_VHOST = &quot;myapp&quot;
CELERY_BACKEND = &quot;amqp&quot;  # telling Celery to report the results back to RabbitMQ
CELERY_RESULT_DBURI = &quot;&quot;
</pre></div>

<p>To test that my setup was correct I ran:</p>

<div><pre class="brush: bash; title: ; notranslate">
$ ./manage.py celeryd -l INFO

[2012-01-27 12:29:01,344: WARNING/MainProcess]  
/home/ram/dev/myapp/virtualenv/lib/python2.7/site-packages/djcelery/loaders.py:86: UserWarning: Using settings.DEBUG leads to a memory leak, never use this setting in production environments!
  warnings.warn(&quot;Using settings.DEBUG leads to a memory leak, never &quot;
[2012-01-27 12:29:01,344: WARNING/MainProcess]  

 -------------- celery@RamLaptop2 v2.4.6
---- **** -----
--- * ***  * -- [Configuration]
-- * - **** ---   . broker:      amqp://guest@localhost:5672//
- ** ----------   . loader:      djcelery.loaders.DjangoLoader
- ** ----------   . logfile:     [stderr]@INFO
- ** ----------   . concurrency: 8
- ** ----------   . events:      OFF
- *** --- * ---   . beat:        OFF
-- ******* ----
--- ***** ----- [Queues]
 --------------   . celery:      exchange:celery (direct) binding:celery
                  
[Tasks]
  . REPORT_CREATE

[2012-01-27 12:29:01,399: INFO/PoolWorker-1] child process calling self.run()
[2012-01-27 12:29:01,401: INFO/PoolWorker-2] child process calling self.run()
[2012-01-27 12:29:01,403: INFO/PoolWorker-3] child process calling self.run()
[2012-01-27 12:29:01,405: INFO/PoolWorker-4] child process calling self.run()
[2012-01-27 12:29:01,406: INFO/PoolWorker-5] child process calling self.run()
[2012-01-27 12:29:01,408: INFO/PoolWorker-6] child process calling self.run()
[2012-01-27 12:29:01,409: INFO/PoolWorker-7] child process calling self.run()
[2012-01-27 12:29:01,410: INFO/PoolWorker-8] child process calling self.run()
[2012-01-27 12:29:01,411: WARNING/MainProcess] celery@RamLaptop2 has started.
</pre></div>

<p>At this point if you&#8217;re not familiar with writing Celery tasks then check out their tutorial on <a href="http://docs.celeryproject.org/en/latest/tutorials/clickcounter.html" class="link-external">how to write Celery tasks</a> for use by Celery daemon workers started above.</p>

<h2>Deploying to production using Supervisord</h2>

<p>In the production environment we need a reliable way of running the Celery daemon processes. Enter <a href="http://supervisord.org/" class="link-external">Supervisord</a>. Essentially it&#8217;s a processes which in turn launches other processes you tell it to launch, and then monitors those child processes, restarting them if they die, etc. Here is what I did to set it up:</p>

<div><pre class="brush: bash; title: ; notranslate">
$ sudo easy_install supervisor
</pre></div>

<p>I created <code>/etc/supervisord</code> to hold all the configuration info:</p>

<div><pre class="brush: bash; title: ; notranslate">
$ sudo mkdir /etc/supervisord
[/code lang=&quot;bash&quot;]

I then edited `/etc/supervisord/supervisord.conf`:

1
[unix_http_server]
file=/tmp/supervisor.sock   ; (the path to the socket file)

[supervisord]
logfile=/var/log/supervisord/main.log ; (main log file;default $CWD/supervisord.log)
logfile_maxbytes=50MB        ; (max main logfile bytes b4 rotation;default 50MB)
logfile_backups=10           ; (num of main logfile rotation backups;default 10)
loglevel=info                ; (log level;default info; others: debug,warn,trace)
pidfile=/tmp/supervisord.pid ; (supervisord pidfile;default supervisord.pid)
nodaemon=false               ; (start in foreground if true;default false)
minfds=1024                  ; (min. avail startup file descriptors;default 1024)
minprocs=200                 ; (min. avail process descriptors;default 200)
childlogdir=/var/log/supervisord            ; ('AUTO' child log dir, default $TEMP)

[rpcinterface:supervisor]
supervisor.rpcinterface_factory = supervisor.rpcinterface:make_main_rpcinterface

[supervisorctl]
serverurl=unix:///tmp/supervisor.sock ; use a unix:// URL  for a unix socket

[include]
files = /etc/supervisord/conf.d/*.conf
</pre></div>

<p>I created a folder called <code>conf.d</code> inside <code>/etc/supervisord</code> and created a file called <code>myapp-celery.conf</code> inside that:</p>

<div><pre class="brush: plain; title: ; notranslate">
[program:myapp-celery]
command=/home/ram/dev/myapp/virtualenv/bin/python /home/ram/dev/myapp/manage.py celeryd --loglevel=INFO
environment=PYTHONPATH=/home/ram/dev/myapp
directory=/home/ram/dev/myapp
user=www-data
numprocs=1
stdout_logfile=/var/log/celeryd/myapp.log
stderr_logfile=/var/log/celeryd/myapp.log
autostart=true
autorestart=true
startsecs=10
stopwaitsecs = 600
priority=998
</pre></div>

<p><strong>Note: I'm running my Django app inside a virtual environment, which is why I specify the path to the Python interpreter in the above file.</strong></p>

<p>I created the <code>/var/log/supervisord</code> and <code>/var/log/celeryd</code> log folders and setup the appropriate permissions on them to enable logging.</p>

<p>To run Supervisord I did the following:</p>

<div><pre class="brush: bash; title: ; notranslate">
$ supervisord -c /etc/supervisord/supervisord.conf
</pre></div>

<p>I checked that my Celery workers were active:</p>

<div><pre class="brush: bash; title: ; notranslate">
$ ps aux | grep celeryd
...
www-data  26655  0.5  0.3 210000 36248 ?        Sl   12:49   0:02 /home/ram/dev/myapp/virtualenv/bin/python /home/ram/dev/myapp/manage.py celeryd --loglevel=INFO
www-data  26656  0.5  0.3 210012 36232 ?        Sl   12:49   0:02 /home/ram/dev/myapp/virtualenv/bin/python /home/ram/dev/myapp/manage.py celeryd --loglevel=INFO
www-data  26671  0.0  0.2 103364 33012 ?        S    12:49   0:00 /home/ram/dev/myapp/virtualenv/bin/python /home/ram/dev/myapp/manage.py celeryd --loglevel=INFO
...
</pre></div>

<p>To shut it down I did:</p>

<div><pre class="brush: bash; title: ; notranslate">
$ supervisorctl -c /etc/supervisord/supervisord.conf shutdown
</pre></div>

<h2>Monitoring Supervisord</h2>

<p>Supervisord will monitor our Celery workers and ensure they stay alive, but how will we ensure that Supervisord itself stays alive? Enter <a href="http://mmonit.com/monit/" class="link-external">Monit</a>. Monit does essentially the same thing as Supervisord except that it monitors child processes through pid files and other checks (e.g. checking that a web page loads in order to verify that Apache is running) and doesn't directly own them (unlike Supervisord). Monit also installs itself as an <code>init.d</code> script which gets launched at server boot time. It comes with a web interface (with optional authentication) which lets you easily start, stop and restart any services it is monitoring along with providing memory and CPU usage statistics.</p>

<p>I installed Monit:</p>

<div><pre class="brush: bash; title: ; notranslate">
$ sudo apt-get install monit
</pre></div>

<p>I edited <code>/etc/monit/monitrc</code> and uncommented the lines relating to the web interface:</p>

<div><pre class="brush: plain; title: ; notranslate">
set httpd port 2812 and
#     use address localhost  # only accept connection from localhost
#     allow localhost        # allow localhost to connect to the server and
#     allow admin:monit      # require user 'admin' with password 'monit'
     allow @monit           # allow users of group 'monit' to connect (rw)
#     allow @users readonly  # allow users of group 'users' to connect readonly
</pre></div>

<p><strong>Note: I configured it above such that it accepts authenticated connections from anywhere, whereby only local Linux users who belongs to the <code>monit</code> group can gain access.</strong>.</p>

<p>Then I created <code>/etc/monit/conf.d/supervisord.monit</code>:</p>

<div><pre class="brush: plain; title: ; notranslate">
 check process supervisord with pidfile /tmp/supervisord.pid
   group supervisord
   start program = &quot;/usr/local/bin/supervisord -c /etc/supervisord/supervisord.conf&quot;
   stop  program = &quot;/usr/local/bin/supervisorctl -c /etc/supervisord/supervisord.conf shutdown&quot;
   if 5 restarts within 5 cycles then timeout
</pre></div>

<p>Restart Monit:</p>

<div><pre class="brush: bash; title: ; notranslate">
$ sudo /etc/init.d/monit restart
</pre></div>

<p>All done! I then created a <code>monit</code> Linux group and added myself to it. From then on I was able to visit <code>http://localhost:2812</code> to see the status of the <code>supervisord</code> process and control it.</p>
]]></content:encoded>
			<wfw:commentRss>http://www.hiddentao.com/archives/2012/01/27/processing-long-running-django-tasks-using-celery-rabbitmq-supervisord-monit/feed/</wfw:commentRss>
		<slash:comments>2</slash:comments>
		</item>
		<item>
		<title>Squel.js &#8211; lightweight Javascript library for building SQL query strings in node.js or the browser</title>
		<link>http://www.hiddentao.com/archives/2011/12/23/squel-js-lightweight-javascript-library-for-building-sql-query-strings-in-node-js-or-the-browser/</link>
		<comments>http://www.hiddentao.com/archives/2011/12/23/squel-js-lightweight-javascript-library-for-building-sql-query-strings-in-node-js-or-the-browser/#comments</comments>
		<pubDate>Fri, 23 Dec 2011 17:31:48 +0000</pubDate>
		<dc:creator>ram</dc:creator>
				<category><![CDATA[Uncategorized]]></category>
		<category><![CDATA[CoffeeScript]]></category>
		<category><![CDATA[Javascript]]></category>
		<category><![CDATA[SQL]]></category>

		<guid isPermaLink="false">http://www.hiddentao.com/?p=1353</guid>
		<description><![CDATA[I&#8217;d like to announce the immediate availability of Squel.js, a lightweight Javascript library for building SQL query strings. Squel exposes an object-oriented API for building SQL query strings in a server-side or browser-side Javascript environment. It&#8217;s well tested using Vows and is available for forking on github. Full documentation along with in-browser examples are available [...]]]></description>
			<content:encoded><![CDATA[<p>I&#8217;d like to announce the immediate availability of <a href="http://squeljs.org/" class="link-external">Squel.js</a>, a lightweight Javascript library for building SQL query strings. Squel exposes an object-oriented API for building SQL query strings in a server-side or browser-side Javascript environment. It&#8217;s well tested using <a href="http://vowsjs.org/" class="link-external">Vows</a> and is available for forking on <a href="https://github.com/hiddentao/squel" class="link-external">github</a>.
<span id="more-1353"></span></p>

<p>Full documentation along with in-browser examples are available at <a href="http://squeljs.org/" class="link-external">squeljs.org</a>. To get started with using Squel in your node.js program use <code>npm install squel</code>.</p>

<p>Enjoy <img src='http://www.hiddentao.com/wp-includes/images/smilies/icon_smile.gif' alt=':)' class='wp-smiley' /> </p>

<p><strong>Update:</strong> I&#8217;ve created a <a href="http://prezi.com/8fdvjo6ripkw/squeljs/" class="link-external">Prezi for Squel</a> which gives you an overview!</p>

<p><strong>Update:</strong> I recently gave a <a href="http://www.hiddentao.com/archives/2012/03/02/my-talk-on-squel-js-at-the-london-node-js-user-group/" class="liinternal">talk on Squel</a> at the <a href="http://lnug.org/" class="link-external">London Node.js User Group</a> meetup.</p>
]]></content:encoded>
			<wfw:commentRss>http://www.hiddentao.com/archives/2011/12/23/squel-js-lightweight-javascript-library-for-building-sql-query-strings-in-node-js-or-the-browser/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>Instructing Cakefile to exit with error if a vows test fails</title>
		<link>http://www.hiddentao.com/archives/2011/12/21/instructing-cakefile-to-exit-with-error-if-a-vows-test-fails/</link>
		<comments>http://www.hiddentao.com/archives/2011/12/21/instructing-cakefile-to-exit-with-error-if-a-vows-test-fails/#comments</comments>
		<pubDate>Wed, 21 Dec 2011 17:24:51 +0000</pubDate>
		<dc:creator>ram</dc:creator>
				<category><![CDATA[Uncategorized]]></category>
		<category><![CDATA[Build]]></category>
		<category><![CDATA[CoffeeScript]]></category>
		<category><![CDATA[Javascript]]></category>
		<category><![CDATA[node.js]]></category>

		<guid isPermaLink="false">http://www.hiddentao.com/?p=1343</guid>
		<description><![CDATA[I&#8217;m using the vows &#8220;behaviour-driven development&#8221; framework to test the squel Javascript library. I have a Cakefile which acts as my build script and inside it there is function which runs all of my tests. I decided to hook it up to the excellent Travis CI automated build system to ensure continuous testing on commits. [...]]]></description>
			<content:encoded><![CDATA[<p>I&#8217;m using the <a href="http://vowsjs.org/" class="link-external">vows</a> &#8220;behaviour-driven development&#8221; framework to test the <a href="https://github.com/hiddentao/squel" class="link-external">squel</a> Javascript library. I have a Cakefile which acts as my build script and inside it there is function which runs all of my tests. I decided to hook it up to the excellent <a href="http://travis-ci.org/" class="link-external">Travis CI</a> automated build system to ensure continuous testing on commits. One of the problems I had was that <code>cake</code> script exited normally even when one of the vows tests broke.
<span id="more-1343"></span></p>

<p>I came up with this little snippet to ensure that <code>cake</code> exited abnormally (with error code 1) if any of the tests failed:</p>

<div><pre class="brush: jscript; title: ; notranslate">
run_tests = (callback) -&gt;
    options = [
        'test/expression.coffee'
        'test/select.coffee'
        'test/update.coffee'
        'test/delete.coffee'
        'test/insert.coffee'
        '--spec'
    ]
    vows = spawn &quot;#{binpath}/vows&quot;, options
    output = &quot;&quot;
    data_handler = (data) -&gt;
        output =+ data if data
        stream_data_handler data
    vows.stdout.on 'data', data_handler
    vows.stderr.on 'data', data_handler
    vows.on 'exit', (status) -&gt;
        if 0 isnt status or (output and -1 isnt output.indexOf(&quot;✗ Broken&quot;))
            return process.exit(1)
        callback?()
</pre></div>

<p>Essentially it looks for the <code>✗ Broken</code> string in the vows output &#8211; this string only gets output if one or more vows were &#8216;broken&#8217;. Now Travis also picks up on test failures. There might be a more elegant way to do the above &#8211; if you know give me a shout. Otherwise I hope somebody else finds this useful.</p>
]]></content:encoded>
			<wfw:commentRss>http://www.hiddentao.com/archives/2011/12/21/instructing-cakefile-to-exit-with-error-if-a-vows-test-fails/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>Django &#8211; fetching list of all SQL queries executed so far for all requests</title>
		<link>http://www.hiddentao.com/archives/2011/12/18/django-fetching-list-of-all-sql-queries-executed-so-far-for-all-requests/</link>
		<comments>http://www.hiddentao.com/archives/2011/12/18/django-fetching-list-of-all-sql-queries-executed-so-far-for-all-requests/#comments</comments>
		<pubDate>Sun, 18 Dec 2011 10:56:58 +0000</pubDate>
		<dc:creator>ram</dc:creator>
				<category><![CDATA[Uncategorized]]></category>
		<category><![CDATA[AJAX]]></category>
		<category><![CDATA[Django]]></category>
		<category><![CDATA[Profiling]]></category>
		<category><![CDATA[Python]]></category>

		<guid isPermaLink="false">http://www.hiddentao.com/?p=1336</guid>
		<description><![CDATA[As part of a recent Django project I had to provide a way to view all the SQL queries which had been executed through the Django app across all requests, when using the Django test server. Although the debug toolbar that ships with Django shows such query information it doesn&#8217;t work for queries executed as [...]]]></description>
			<content:encoded><![CDATA[<p>As part of a recent Django project I had to provide a way to view all the SQL queries which had been executed through the Django app across all requests, when using the Django test server. Although the debug toolbar that ships with Django shows such query information it doesn&#8217;t work for queries executed as a part of AJAX requests. 
<span id="more-1336"></span></p>

<p>Although the Django test server is single-threaded (and thus all requests execute in the same thread) it does not maintain a global application state which is accessible to each request, as far as I&#8217;m aware. However, because it is single-threaded we can simulate a globally accessible application state through the use of static class member variables.</p>

<p>My snippet below does just this. It uses request-response middleware which kicks in during the response phase to add all SQL queries executed as part of the current request to a static class member list variable. Accessing the data requires calling a separate URL (<code>/profiling</code>) which will then output all the SQL queries so far &#8216;collected&#8217;.</p>

<p>Further improvements to this may include establishing a persistent socket connection to the server through which SQL query strings are received as and when they get executed. And instead of displaying the list of queries on their own page we could inject them into the Debug toolbar, if it&#8217;s enabled. In fact, enhancing the Debug toolbar itself to pick up queries executed as part of AJAX requests would be the best implementation yet.</p>

<p>The snippet (see <a href="http://djangosnippets.org/snippets/2632/" class="link-external">http://djangosnippets.org/snippets/2632/</a>):</p>

<div><pre class="brush: python; title: ; notranslate">

# file: settings.py

MIDDLEWARE_CLASSES = (
    'django.middleware.common.CommonMiddleware',
    'yourapp.middleware.SqlProfilingMiddleware',
    ...
)



# file: yourapp/midddlware.py

from django.db import connection
import time

class SqlProfilingMiddleware(object):
    Queries = []

    def process_request(self, request):
        return None
    def process_view(self, request, view_func, view_args, view_kwargs):
        return None
    def process_template_response(self, request, response):
        self._add_sql_queries(request)
        return response
    def process_response(self, request, response):
        self._add_sql_queries(request)
        return response
    def process_exception(self, request, exception):
        return None

    def _add_sql_queries(self, request):
        for q in connection.queries:
            q[&quot;time&quot;] = time.time() + float(q[&quot;time&quot;])
            SqlProfilingMiddleware.Queries.insert(0, q)
            # add request info as a separator
        SqlProfilingMiddleware.Queries.insert(0, {&quot;time&quot;: time.time(), &quot;path&quot; : request.path})



# file: yourapp/views.py

from yourapp.middleware import SqlProfilingMiddleware

def profiling(request):
    return render_to_response(&quot;profiling.html&quot;, {&quot;queries&quot;: SqlProfilingMiddleware.Queries})



# file: urls.py

urlpatterns = patterns('',
  (r'^profiling/$', 'yourapp.views.profiling'),
)

</pre></div>
]]></content:encoded>
			<wfw:commentRss>http://www.hiddentao.com/archives/2011/12/18/django-fetching-list-of-all-sql-queries-executed-so-far-for-all-requests/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>Primary key, foreign key improvements to Sequelize + Date.js alternatives</title>
		<link>http://www.hiddentao.com/archives/2011/11/18/primary-key-foreign-key-improvements-to-sequelize-date-js-alternatives/</link>
		<comments>http://www.hiddentao.com/archives/2011/11/18/primary-key-foreign-key-improvements-to-sequelize-date-js-alternatives/#comments</comments>
		<pubDate>Fri, 18 Nov 2011 16:54:51 +0000</pubDate>
		<dc:creator>ram</dc:creator>
				<category><![CDATA[Uncategorized]]></category>
		<category><![CDATA[Javascript]]></category>
		<category><![CDATA[node.js]]></category>
		<category><![CDATA[ORM]]></category>

		<guid isPermaLink="false">http://www.hiddentao.com/?p=1332</guid>
		<description><![CDATA[I&#8217;ve created a (https://github.com/sdepold/sequelize/pull/110) for Sequelize which enables one to use primary key fields as foreign keys too, something I find myself doing often with my relational schemas as I don&#8217;t like using the standard id primary key field unless it&#8217;s the most sensible primary key to have. In practice the changes mean you can [...]]]></description>
			<content:encoded><![CDATA[<p>I&#8217;ve created a (https://github.com/sdepold/sequelize/pull/110) for <a href="http://sequelizejs.com/" class="link-external">Sequelize</a> which enables one to use primary key fields as foreign keys too, something I find myself doing often with my relational schemas as I don&#8217;t like using the standard <code>id</code> primary key field unless it&#8217;s the most sensible primary key to have. In practice the changes mean you can do the following (not currently possible in vanilla Sequelize):
<span id="more-1332"></span></p>

<div><pre class="brush: jscript; title: ; notranslate">
var User = sequelize.define('user', {
  name: Sequelize.STRING
},{
  underscored : true
})

var Profile = sequelize.define('profile', {
  user_id: {
    type: Sequelize.INTEGER,
    primaryKey: true
  },
  dob: Sequelize.DATE
},{
  underscored : true
})

var Profile2 = sequelize.define('profile2', {
  dob: Sequelize.DATE
},{
  underscored : true
})

User.hasOne(Profile)
User.hasOne(Profile2)

/* 
At this point Profile.user_id will act as both primary and foreign key, whereas Profile2.user_id and Profile.id will have been auto-added by Sequelize and will act as foreign key and primary key respectively.
*/
</pre></div>

<h2>Date.js alternatives</h2>

<p><a href="http://www.datejs.com/" class="link-external">Date.js</a> has been causing me a number of problems due to its nature of extending/modifying the built-in <code>Date</code> object. It really messed up my use of cookie sessions in Express, specifically to do with setting the cookie expiry dates. I still haven&#8217;t figured out exactly why cookies weren&#8217;t getting set properly but I suspect that it&#8217;s some internal change to <code>Date</code> made by date.js which was causing the problem. I then also had problems with it on the browser side of things.</p>

<p>Looking for an alternative I&#8217;m happy to have found <a href="http://arshaw.com/xdate/" class="link-external">XDate</a>, a more light-weight and better-designed date library than date.js. It wraps the default <code>Date</code> object whilst still exposing an identical API as well as providing its own. The nice thing about XDate is that its custom API methods are almost identical to those provided by date.js so switching one out for the other didn&#8217;t require any massive code changes.</p>

<p>XDate was very recently created and it&#8217;s on Github too so I expect it will only get better with time. There isn&#8217;t a node module for it yet, as far as I&#8217;m aware. Then again, I&#8217;ve just had a look and found <a href="http://momentjs.com/" class="link-external">Moment</a>, another nice-looking date library available through NPM. I might give that a whirl for my server side.</p>
]]></content:encoded>
			<wfw:commentRss>http://www.hiddentao.com/archives/2011/11/18/primary-key-foreign-key-improvements-to-sequelize-date-js-alternatives/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
	</channel>
</rss>

