<?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:feedburner="http://rssnamespace.org/feedburner/ext/1.0" version="2.0"><channel><title>Debuggable.com - Blog</title><link>http://www.debuggable.com/</link><description /><atom10:link xmlns:atom10="http://www.w3.org/2005/Atom" rel="self" href="http://feeds.feedburner.com/debuggable" type="application/rss+xml" /><atom10:link xmlns:atom10="http://www.w3.org/2005/Atom" rel="hub" href="http://pubsubhubbub.appspot.com" /><item><title>FFMPEG multiple thumbnails</title><link>http://feedproxy.google.com/~r/debuggable/~3/NhQQMrkCmJs/FFMPEG_multiple_thumbnails:4aded79c-6744-4bc1-b30e-59bccbdd56cb</link><guid isPermaLink="false">http://www.debuggable.com/posts/FFMPEG_multiple_thumbnails:4aded79c-6744-4bc1-b30e-59bccbdd56cb</guid><description>&lt;p&gt;I'm currently implementing /video/thumbnail functionality for &lt;a href="http://transload.it/"&gt;transload.it&lt;/a&gt; and did some research on how to implement it.&lt;/p&gt;
&lt;p&gt;First I set out to create a single thumbnail which was very easy:&lt;/p&gt;
&lt;pre class="terminal"&gt;
ffmpeg -i intro.mov -vframes 1 -s 320x240 -ss 10 thumb.jpg
&lt;/pre&gt;
&lt;p&gt;This takes a video file (-i intro.mov) and extract a single frame (-vframes 1) with 320x240px (-s 320x240) at an offset of 10 seconds (-ss 10) and saves it as thumb.jpg.&lt;/p&gt;
&lt;p&gt;So far so good. But we actually want to offer the ability to take multiple thumbnails (like 8) in 1/8th increments of the video playtime. My initial idea was that there had to be more efficient way then calling up ffmpeg 8 times, and indeed I found one:&lt;/p&gt;
&lt;pre class="terminal"&gt;
ffmpeg -i intro.mov -r 1/10 -s 320x240 thumb_%03d.jpg
&lt;/pre&gt;
&lt;p&gt;This command works the same as the one above, except that it tries to set the frame rate to 1/10 (-r 1/10 = 1 frame every 10 seconds) and saves the results as thumb_000.jpg, thumb_001.jpg, thumb_002.jpg etc. Unfortunately I could not get it to produce the exact results I wanted. I would always end up with the first frame being captured twice, and the frame rate I set would be off by 2-3 seconds.&lt;/p&gt;
&lt;p&gt;So I hopped to IRC and asked #ffmpeg for help. &lt;a href="http://x264dev.multimedia.cx/"&gt;Dark_Shikari&lt;/a&gt; (one of the crazy people who build the best video codec in the world)  was kind enough to help me.&lt;/p&gt;
&lt;p&gt;It turns out that the offset parameter (-ss) needs to be set before the input parameter (-i). That will cause ffmpeg to seek to that position in the stream *without* decoding it and in fact skipping anything but key frames! This is pretty significant as performance improved from ~20 seconds for 4 thumbnails to about 2-3 seconds.&lt;/p&gt;
&lt;p&gt;So my final setup is pretty much like this. First I find out the video duration by running:&lt;/p&gt;
&lt;pre class="terminal"&gt;
midentify intro.mov
&lt;/pre&gt;
&lt;p&gt;This gives me all kinds of useful information including the length of the video:&lt;/p&gt;
&lt;pre class="terminal"&gt;
ID_AUDIO_ID=0
ID_VIDEO_ID=1
ID_FILENAME=intro.mov
ID_DEMUXER=mov
ID_VIDEO_FORMAT=avc1
ID_VIDEO_BITRATE=0
ID_VIDEO_WIDTH=630
ID_VIDEO_HEIGHT=360
ID_VIDEO_FPS=25.000
ID_VIDEO_ASPECT=0.0000
ID_AUDIO_FORMAT=sowt
ID_AUDIO_BITRATE=0
ID_AUDIO_RATE=48000
ID_AUDIO_NCH=2
ID_LENGTH=81.32
ID_VIDEO_CODEC=ffh264
ID_AUDIO_BITRATE=1536000
ID_AUDIO_RATE=48000
ID_AUDIO_NCH=2
ID_AUDIO_CODEC=pcm
&lt;/pre&gt;
&lt;p&gt;I then divide the length by the amount of thumbnails I need, and run a loop like this:&lt;/p&gt;
&lt;pre class="terminal"&gt;
ffmpeg -ss $i*$interval -i intro.mov -vframes 1 -s 320x240 thumb_$i.jpg
&lt;/pre&gt;
&lt;p&gt;Where $i is the number of the thumb I'm extracting and $interval is the duration of the video divided by the amount of thumbs.&lt;/p&gt;
&lt;p&gt;Works like a charm!&lt;/p&gt;
&lt;p&gt;-- Felix Geisendörfer aka the_undefined&lt;br /&gt;
&lt;img style="display: none;" src="http://www.debuggable.com/posts/tick/4aded79c-6744-4bc1-b30e-59bccbdd56cb"&gt;&lt;/p&gt;&lt;img src="http://feeds.feedburner.com/~r/debuggable/~4/NhQQMrkCmJs" height="1" width="1"/&gt;</description><author>felix@debuggable.com</author><pubDate>Wed, 21 Oct 2009 12:22:21 +0000</pubDate><comments>http://www.debuggable.com/posts/FFMPEG_multiple_thumbnails:4aded79c-6744-4bc1-b30e-59bccbdd56cb</comments><feedburner:origLink>http://www.debuggable.com/posts/FFMPEG_multiple_thumbnails:4aded79c-6744-4bc1-b30e-59bccbdd56cb</feedburner:origLink></item><item><title>Burning Apple</title><link>http://feedproxy.google.com/~r/debuggable/~3/E-_gXVH4hdA/burning-apple:4ad4a03b-6998-4bb8-b8a6-6c43cbdd56cb</link><guid isPermaLink="false">http://www.debuggable.com/posts/burning-apple:4ad4a03b-6998-4bb8-b8a6-6c43cbdd56cb</guid><description>&lt;p&gt;Apple fanboy? Caught in the distortion field? No problem, there is a cure:&lt;/p&gt;
&lt;p&gt;&lt;a style="width: 640px; height: 426px; display: block; line-height: 426px;" class="flowplayer" href="http://debuggable.com/posts/burning-apple:4ad4a03b-6998-4bb8-b8a6-6c43cbdd56cb" rel="http://s3.transload.it/6045605448f06b8028cb47b508e66ce9/da/7c4aee9560338041b8536885ce23ca/Burning Apple.flv"&gt;Play video&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;As you can see, I have not always been a Mac user. In fact I passionately hated anything Mac after my first contact with an iMac (OS 10.3). I did this video in 2005 while I was a foreign exchange student in Atlanta. My friend in the video is still using PCs - don't flash you fancy macbook next to him in a coffee shop!&lt;/p&gt;
&lt;p&gt;Why I have held back posting this for so long? I just integrated &lt;a href="http://transload.it"&gt;transload.it&lt;/a&gt; into debuggable.com and needed a video to test. More gems like this are on their way : ).&lt;/p&gt;
&lt;p&gt;-- Felix Geisendörfer aka the_undefined&lt;br /&gt;
&lt;img style="display: none;" src="http://www.debuggable.com/posts/tick/4ad4a03b-6998-4bb8-b8a6-6c43cbdd56cb"&gt;&lt;/p&gt;&lt;img src="http://feeds.feedburner.com/~r/debuggable/~4/E-_gXVH4hdA" height="1" width="1"/&gt;</description><author>felix@debuggable.com</author><pubDate>Tue, 13 Oct 2009 16:13:44 +0000</pubDate><comments>http://www.debuggable.com/posts/burning-apple:4ad4a03b-6998-4bb8-b8a6-6c43cbdd56cb</comments><feedburner:origLink>http://www.debuggable.com/posts/burning-apple:4ad4a03b-6998-4bb8-b8a6-6c43cbdd56cb</feedburner:origLink></item><item><title>7 + 8 === 7 in JavaScript</title><link>http://feedproxy.google.com/~r/debuggable/~3/StvR6MqyK1I/7+8===7-in-javascript:4acba016-d204-489b-b5a0-1fd0cbdd56cb</link><guid isPermaLink="false">http://www.debuggable.com/posts/7+8===7-in-javascript:4acba016-d204-489b-b5a0-1fd0cbdd56cb</guid><description>&lt;p&gt;I kid you not, this is an issue I actually ran into a long time ago and have been terribly careful of avoiding ever since.&lt;/p&gt;
&lt;p&gt;It must have been a fantastic day in the "Bad Parts" JavaScript department. While one team was busy screwing up the + operator to be responsible for both string concatenation and addition, another team set out to build a nasty trap inside the parseInt() function.&lt;/p&gt;
&lt;p&gt;This together results in probably my favourite JavaScript bug of all times. It goes like this: You have two strings that contain zero padded numbers (like hours or minutes) and you want to perform some math on them:&lt;/p&gt;
&lt;div class="clear"&gt;&lt;/div&gt;
&lt;div class="code debuggable_sh_js" style="white-space: wrap;white-space: nowrap;"&gt;
&lt;span class="kw2"&gt;var&lt;/span&gt; a = &lt;span class="st0"&gt;'07'&lt;/span&gt;;&lt;br /&gt;
&lt;span class="kw2"&gt;var&lt;/span&gt; b = &lt;span class="st0"&gt;'08'&lt;/span&gt;&lt;br /&gt;
&lt;br /&gt;
&lt;span class="kw3"&gt;alert&lt;/span&gt;&lt;span class="br0"&gt;&amp;#40;&lt;/span&gt;a + b&lt;span class="br0"&gt;&amp;#41;&lt;/span&gt;;&lt;/div&gt;
&lt;p&gt;Of course you would not write such code, but maybe you have a friend who has done it. This friend probably quickly realized that '0708' was not the result he was hoping for, so he got clever and updated his code to:&lt;/p&gt;
&lt;div class="clear"&gt;&lt;/div&gt;
&lt;div class="code debuggable_sh_js" style="white-space: wrap;white-space: nowrap;"&gt;
&lt;span class="kw2"&gt;var&lt;/span&gt; a = &lt;span class="st0"&gt;'07'&lt;/span&gt;;&lt;br /&gt;
&lt;span class="kw2"&gt;var&lt;/span&gt; b = &lt;span class="st0"&gt;'08'&lt;/span&gt;&lt;br /&gt;
&lt;br /&gt;
&lt;span class="kw3"&gt;alert&lt;/span&gt;&lt;span class="br0"&gt;&amp;#40;&lt;/span&gt;parseInt&lt;span class="br0"&gt;&amp;#40;&lt;/span&gt;a&lt;span class="br0"&gt;&amp;#41;&lt;/span&gt; + parseInt&lt;span class="br0"&gt;&amp;#40;&lt;/span&gt;b&lt;span class="br0"&gt;&amp;#41;&lt;/span&gt;&lt;span class="br0"&gt;&amp;#41;&lt;/span&gt;;&lt;/div&gt;
&lt;p&gt;However, it turns out that the result of this operation is actually 7. SEVEN you ask? Yes: it's not a bug, it's a feature.&lt;/p&gt;
&lt;p&gt;JavaScript assumes that any 0-prefixed string ought to be referring to an octal number. This will not become apparent until your string represents an invalid octal such as '08'. So in order to outsmart this "feature" you have to explicitly provide the base for your integer:&lt;/p&gt;
&lt;div class="clear"&gt;&lt;/div&gt;
&lt;div class="code debuggable_sh_js" style="white-space: wrap;white-space: nowrap;"&gt;
&lt;span class="kw2"&gt;var&lt;/span&gt; a = &lt;span class="st0"&gt;'07'&lt;/span&gt;;&lt;br /&gt;
&lt;span class="kw2"&gt;var&lt;/span&gt; b = &lt;span class="st0"&gt;'08'&lt;/span&gt;&lt;br /&gt;
&lt;br /&gt;
&lt;span class="kw3"&gt;alert&lt;/span&gt;&lt;span class="br0"&gt;&amp;#40;&lt;/span&gt;parseInt&lt;span class="br0"&gt;&amp;#40;&lt;/span&gt;a, &lt;span class="nu0"&gt;10&lt;/span&gt;&lt;span class="br0"&gt;&amp;#41;&lt;/span&gt; + parseInt&lt;span class="br0"&gt;&amp;#40;&lt;/span&gt;b, &lt;span class="nu0"&gt;10&lt;/span&gt;&lt;span class="br0"&gt;&amp;#41;&lt;/span&gt;&lt;span class="br0"&gt;&amp;#41;&lt;/span&gt;;&lt;/div&gt;
&lt;p&gt;I hope this will one day safe somebody the hour of my life that went into this : ).&lt;/p&gt;
&lt;p&gt;-- Felix Geisendörfer aka the_undefined&lt;/p&gt;
&lt;p&gt;PS: Got a great JavaScript bug like this as well? I'd be happy to hear about it in the comments.&lt;br /&gt;
&lt;img style="display: none;" src="http://www.debuggable.com/posts/tick/4acba016-d204-489b-b5a0-1fd0cbdd56cb"&gt;&lt;/p&gt;&lt;img src="http://feeds.feedburner.com/~r/debuggable/~4/StvR6MqyK1I" height="1" width="1"/&gt;</description><author>felix@debuggable.com</author><pubDate>Tue, 06 Oct 2009 19:52:54 +0000</pubDate><comments>http://www.debuggable.com/posts/7+8===7-in-javascript:4acba016-d204-489b-b5a0-1fd0cbdd56cb</comments><feedburner:origLink>http://www.debuggable.com/posts/7+8===7-in-javascript:4acba016-d204-489b-b5a0-1fd0cbdd56cb</feedburner:origLink></item><item><title>Turning JavaScript's arguments object into an array </title><link>http://feedproxy.google.com/~r/debuggable/~3/NhNDcbxGTHc/turning-javascripts-arguments-object-into-an-array:4ac50ef8-3bd0-4a2d-8c2e-535ccbdd56cb</link><guid isPermaLink="false">http://www.debuggable.com/posts/turning-javascripts-arguments-object-into-an-array:4ac50ef8-3bd0-4a2d-8c2e-535ccbdd56cb</guid><description>&lt;p&gt;&lt;img src="http://static.debuggable.com.s3.amazonaws.com/img/js-good-parts.jpg"&gt;&lt;/p&gt;
&lt;p&gt;JavaScript is awesome when you stick to &lt;a href="http://www.youtube.com/watch?v=hQVTIJBZook"&gt;the good parts&lt;/a&gt;. But sometimes you have to touch it's bad parts. Consider this:&lt;/p&gt;
&lt;div class="clear"&gt;&lt;/div&gt;
&lt;div class="code debuggable_sh_js" style="white-space: wrap;white-space: nowrap;"&gt;
&lt;span class="kw2"&gt;function&lt;/span&gt; hello&lt;span class="br0"&gt;&amp;#40;&lt;/span&gt;&lt;span class="br0"&gt;&amp;#41;&lt;/span&gt; &lt;span class="br0"&gt;&amp;#123;&lt;/span&gt;&lt;br /&gt;
&amp;nbsp; &amp;nbsp; arguments.&lt;span class="me1"&gt;unshift&lt;/span&gt;&lt;span class="br0"&gt;&amp;#40;&lt;/span&gt;&lt;span class="st0"&gt;'hello'&lt;/span&gt;&lt;span class="br0"&gt;&amp;#41;&lt;/span&gt;;&lt;br /&gt;
&amp;nbsp; &amp;nbsp; &lt;span class="kw3"&gt;alert&lt;/span&gt;&lt;span class="br0"&gt;&amp;#40;&lt;/span&gt;arguments.&lt;span class="me1"&gt;join&lt;/span&gt;&lt;span class="br0"&gt;&amp;#40;&lt;/span&gt;&lt;span class="st0"&gt;' '&lt;/span&gt;&lt;span class="br0"&gt;&amp;#41;&lt;/span&gt;&lt;span class="br0"&gt;&amp;#41;&lt;/span&gt;;&lt;br /&gt;
&lt;span class="br0"&gt;&amp;#125;&lt;/span&gt;&lt;br /&gt;
&lt;br /&gt;
hello&lt;span class="br0"&gt;&amp;#40;&lt;/span&gt;&lt;span class="st0"&gt;'pretty'&lt;/span&gt;, &lt;span class="st0"&gt;'world'&lt;/span&gt;&lt;span class="br0"&gt;&amp;#41;&lt;/span&gt;;&lt;/div&gt;
&lt;p&gt;"TypeError: arguments. unshift is not a function"! How dare you JavaScript?&lt;/p&gt;
&lt;p&gt;JavaScript might trick you into thinking the arguments variable is an array because you can access it like one (arguments[0], arguments[1], ...), but it's lying. The truth is that the arguments variable really &lt;a href="https://developer.mozilla.org/en/Core_JavaScript_1.5_Reference/Functions/arguments"&gt;is an object&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;Lucky for us, the good parts of JavaScript come to rescue, namely prototypical inheritance:&lt;/p&gt;
&lt;div class="clear"&gt;&lt;/div&gt;
&lt;div class="code debuggable_sh_js" style="white-space: wrap;white-space: nowrap;"&gt;
&lt;span class="kw2"&gt;function&lt;/span&gt; hello&lt;span class="br0"&gt;&amp;#40;&lt;/span&gt;&lt;span class="br0"&gt;&amp;#41;&lt;/span&gt; &lt;span class="br0"&gt;&amp;#123;&lt;/span&gt;&lt;br /&gt;
&amp;nbsp; &amp;nbsp; &lt;span class="kw2"&gt;var&lt;/span&gt; args = Array.&lt;span class="me1"&gt;prototype&lt;/span&gt;.&lt;span class="me1"&gt;slice&lt;/span&gt;.&lt;span class="me1"&gt;call&lt;/span&gt;&lt;span class="br0"&gt;&amp;#40;&lt;/span&gt;arguments&lt;span class="br0"&gt;&amp;#41;&lt;/span&gt;;&lt;br /&gt;
&amp;nbsp; &amp;nbsp; args.&lt;span class="me1"&gt;unshift&lt;/span&gt;&lt;span class="br0"&gt;&amp;#40;&lt;/span&gt;&lt;span class="st0"&gt;'hello'&lt;/span&gt;&lt;span class="br0"&gt;&amp;#41;&lt;/span&gt;;&lt;br /&gt;
&amp;nbsp; &amp;nbsp; &lt;span class="kw3"&gt;alert&lt;/span&gt;&lt;span class="br0"&gt;&amp;#40;&lt;/span&gt;args.&lt;span class="me1"&gt;join&lt;/span&gt;&lt;span class="br0"&gt;&amp;#40;&lt;/span&gt;&lt;span class="st0"&gt;' '&lt;/span&gt;&lt;span class="br0"&gt;&amp;#41;&lt;/span&gt;&lt;span class="br0"&gt;&amp;#41;&lt;/span&gt;;&lt;br /&gt;
&lt;span class="br0"&gt;&amp;#125;&lt;/span&gt;&lt;br /&gt;
&lt;br /&gt;
hello&lt;span class="br0"&gt;&amp;#40;&lt;/span&gt;&lt;span class="st0"&gt;'pretty'&lt;/span&gt;, &lt;span class="st0"&gt;'world'&lt;/span&gt;&lt;span class="br0"&gt;&amp;#41;&lt;/span&gt;;&lt;/div&gt;
&lt;p&gt;This creates a variable called args that holds a true array version of the arguments variable. This works by hjacking the Array.splice function to make it work on the arguments variable.&lt;/p&gt;
&lt;p&gt;Yes, this is the kind of code you might want to document for the poor kids who will have to debug grandpa's "AJAX museum" one day.&lt;/p&gt;
&lt;p&gt;Got a better solution? Let me know!&lt;/p&gt;
&lt;p&gt;-- Felix Geisendörfer aka the_undefined&lt;br /&gt;
&lt;img style="display: none;" src="http://www.debuggable.com/posts/tick/4ac50ef8-3bd0-4a2d-8c2e-535ccbdd56cb"&gt;&lt;/p&gt;&lt;img src="http://feeds.feedburner.com/~r/debuggable/~4/NhNDcbxGTHc" height="1" width="1"/&gt;</description><author>felix@debuggable.com</author><pubDate>Thu, 01 Oct 2009 21:10:24 +0000</pubDate><comments>http://www.debuggable.com/posts/turning-javascripts-arguments-object-into-an-array:4ac50ef8-3bd0-4a2d-8c2e-535ccbdd56cb</comments><feedburner:origLink>http://www.debuggable.com/posts/turning-javascripts-arguments-object-into-an-array:4ac50ef8-3bd0-4a2d-8c2e-535ccbdd56cb</feedburner:origLink></item><item><title>Going to JSConf.eu</title><link>http://feedproxy.google.com/~r/debuggable/~3/BR_4cQOEGbw/Going_to_JSConf_eu:4ac4d946-8204-43ae-b912-440ecbdd56cb</link><guid isPermaLink="false">http://www.debuggable.com/posts/Going_to_JSConf_eu:4ac4d946-8204-43ae-b912-440ecbdd56cb</guid><description>&lt;p&gt;Tim and I just got our tickets for the upcoming &lt;a href="http://jsconf.eu/2009/"&gt;JSConf.eu&lt;/a&gt; in Berlin. It's almost sold out now, but in case you already have a ticket  let us know so we can hang out.&lt;/p&gt;
&lt;p&gt;I'm very thrilled about the speaker lineup (Amy Hoy, Thomas Fuchs, John Resig, Dion Almaer &amp; Ben Galbraith, Douglas Crockford, Ryan Dahl, Kevin Dangoor, Kyle Simpson, Steve Souders). It's especially exciting to see that Ryan Dahl has been selected as a speaker to &lt;a href="http://jsconf.eu/2009/speakers_selected.html"&gt;talk about node.js&lt;/a&gt;, my new favourite OS project!&lt;/p&gt;
&lt;p&gt;I will try my best to cover his as well as some of the other talks for the blog!&lt;/p&gt;
&lt;p&gt;-- Felix Geisendörfer aka the_undefined&lt;br /&gt;
&lt;img style="display: none;" src="http://www.debuggable.com/posts/tick/4ac4d946-8204-43ae-b912-440ecbdd56cb"&gt;&lt;/p&gt;&lt;img src="http://feeds.feedburner.com/~r/debuggable/~4/BR_4cQOEGbw" height="1" width="1"/&gt;</description><author>felix@debuggable.com</author><pubDate>Thu, 01 Oct 2009 16:40:27 +0000</pubDate><comments>http://www.debuggable.com/posts/Going_to_JSConf_eu:4ac4d946-8204-43ae-b912-440ecbdd56cb</comments><feedburner:origLink>http://www.debuggable.com/posts/Going_to_JSConf_eu:4ac4d946-8204-43ae-b912-440ecbdd56cb</feedburner:origLink></item><item><title>Streaming file uploads with node.js</title><link>http://feedproxy.google.com/~r/debuggable/~3/tWs27OCCqgM/streaming-file-uploads-with-node-js:4ac094b2-b6c8-4a7f-bd07-28accbdd56cb</link><guid isPermaLink="false">http://www.debuggable.com/posts/streaming-file-uploads-with-node-js:4ac094b2-b6c8-4a7f-bd07-28accbdd56cb</guid><description>&lt;p&gt;Not excited by &lt;a href="http://debuggable.com/posts/node_js:4ab4d9d7-b788-41d4-85c0-1b51cbdd56cb"&gt;hello world&lt;/a&gt; in node.js? No problem. &lt;/p&gt;
&lt;p&gt;Let's say you are a &lt;a href="http://transload.it/"&gt;startup focusing on upload technology&lt;/a&gt; and you want the maximum level of control for your file uploads. In our case that means having the ability to directly interact with the multipart data stream as it comes in (so we can abort the upload if something isn't right, - beats the hell out of letting the user wait an hour to tell him after the upload has finished).&lt;/p&gt;
&lt;p&gt;Here is a complete example on how to accomplish this in node.js (you'll need the bleeding edge git version):&lt;/p&gt;
&lt;div class="clear"&gt;&lt;/div&gt;
&lt;div class="code debuggable_sh_js" style="white-space: wrap;white-space: nowrap;"&gt;
&lt;span class="kw2"&gt;var&lt;/span&gt; http = require&lt;span class="br0"&gt;&amp;#40;&lt;/span&gt;&lt;span class="st0"&gt;'/http.js'&lt;/span&gt;&lt;span class="br0"&gt;&amp;#41;&lt;/span&gt;;&lt;br /&gt;
&lt;span class="kw2"&gt;var&lt;/span&gt; multipart = require&lt;span class="br0"&gt;&amp;#40;&lt;/span&gt;&lt;span class="st0"&gt;'/multipart.js'&lt;/span&gt;&lt;span class="br0"&gt;&amp;#41;&lt;/span&gt;;&lt;br /&gt;
&lt;br /&gt;
&lt;span class="kw2"&gt;var&lt;/span&gt; server = http.&lt;span class="me1"&gt;createServer&lt;/span&gt;&lt;span class="br0"&gt;&amp;#40;&lt;/span&gt;&lt;span class="kw2"&gt;function&lt;/span&gt;&lt;span class="br0"&gt;&amp;#40;&lt;/span&gt;req, res&lt;span class="br0"&gt;&amp;#41;&lt;/span&gt; &lt;span class="br0"&gt;&amp;#123;&lt;/span&gt;&lt;br /&gt;
&amp;nbsp; &lt;span class="kw1"&gt;switch&lt;/span&gt; &lt;span class="br0"&gt;&amp;#40;&lt;/span&gt;req.&lt;span class="me1"&gt;uri&lt;/span&gt;.&lt;span class="me1"&gt;path&lt;/span&gt;&lt;span class="br0"&gt;&amp;#41;&lt;/span&gt; &lt;span class="br0"&gt;&amp;#123;&lt;/span&gt;&lt;br /&gt;
&amp;nbsp; &amp;nbsp; &lt;span class="kw1"&gt;case&lt;/span&gt; &lt;span class="st0"&gt;'/'&lt;/span&gt;:&lt;br /&gt;
&amp;nbsp; &amp;nbsp; &amp;nbsp; display_form&lt;span class="br0"&gt;&amp;#40;&lt;/span&gt;req, res&lt;span class="br0"&gt;&amp;#41;&lt;/span&gt;;&lt;br /&gt;
&amp;nbsp; &amp;nbsp; &amp;nbsp; &lt;span class="kw1"&gt;break&lt;/span&gt;;&lt;br /&gt;
&amp;nbsp; &amp;nbsp; &lt;span class="kw1"&gt;case&lt;/span&gt; &lt;span class="st0"&gt;'/upload'&lt;/span&gt;:&lt;br /&gt;
&amp;nbsp; &amp;nbsp; &amp;nbsp; upload_file&lt;span class="br0"&gt;&amp;#40;&lt;/span&gt;req, res&lt;span class="br0"&gt;&amp;#41;&lt;/span&gt;;&lt;br /&gt;
&amp;nbsp; &amp;nbsp; &amp;nbsp; &lt;span class="kw1"&gt;break&lt;/span&gt;;&lt;br /&gt;
&amp;nbsp; &amp;nbsp; &lt;span class="kw2"&gt;default&lt;/span&gt;:&lt;br /&gt;
&amp;nbsp; &amp;nbsp; &amp;nbsp; show_404&lt;span class="br0"&gt;&amp;#40;&lt;/span&gt;req, res&lt;span class="br0"&gt;&amp;#41;&lt;/span&gt;;&lt;br /&gt;
&amp;nbsp; &amp;nbsp; &amp;nbsp; &lt;span class="kw1"&gt;break&lt;/span&gt;;&lt;br /&gt;
&amp;nbsp; &lt;span class="br0"&gt;&amp;#125;&lt;/span&gt;&lt;br /&gt;
&lt;span class="br0"&gt;&amp;#125;&lt;/span&gt;&lt;span class="br0"&gt;&amp;#41;&lt;/span&gt;;&lt;br /&gt;
server.&lt;span class="me1"&gt;listen&lt;/span&gt;&lt;span class="br0"&gt;&amp;#40;&lt;/span&gt;&lt;span class="nu0"&gt;8000&lt;/span&gt;&lt;span class="br0"&gt;&amp;#41;&lt;/span&gt;;&lt;br /&gt;
&lt;br /&gt;
&lt;span class="kw2"&gt;function&lt;/span&gt; display_form&lt;span class="br0"&gt;&amp;#40;&lt;/span&gt;req, res&lt;span class="br0"&gt;&amp;#41;&lt;/span&gt; &lt;span class="br0"&gt;&amp;#123;&lt;/span&gt;&lt;br /&gt;
&amp;nbsp; res.&lt;span class="me1"&gt;sendHeader&lt;/span&gt;&lt;span class="br0"&gt;&amp;#40;&lt;/span&gt;&lt;span class="nu0"&gt;200&lt;/span&gt;, &lt;span class="br0"&gt;&amp;#123;&lt;/span&gt;&lt;span class="st0"&gt;'Content-Type'&lt;/span&gt;: &lt;span class="st0"&gt;'text/html'&lt;/span&gt;&lt;span class="br0"&gt;&amp;#125;&lt;/span&gt;&lt;span class="br0"&gt;&amp;#41;&lt;/span&gt;;&lt;br /&gt;
&amp;nbsp; res.&lt;span class="me1"&gt;sendBody&lt;/span&gt;&lt;span class="br0"&gt;&amp;#40;&lt;/span&gt;&lt;br /&gt;
&amp;nbsp; &amp;nbsp; &lt;span class="st0"&gt;'&amp;lt;form action=&amp;quot;/upload&amp;quot; method=&amp;quot;post&amp;quot; enctype=&amp;quot;multipart/form-data&amp;quot;&amp;gt;'&lt;/span&gt;+&lt;br /&gt;
&amp;nbsp; &amp;nbsp; &lt;span class="st0"&gt;'&amp;lt;input type=&amp;quot;file&amp;quot; name=&amp;quot;upload-file&amp;quot;&amp;gt;'&lt;/span&gt;+&lt;br /&gt;
&amp;nbsp; &amp;nbsp; &lt;span class="st0"&gt;'&amp;lt;input type=&amp;quot;submit&amp;quot; value=&amp;quot;Upload&amp;quot;&amp;gt;'&lt;/span&gt;+&lt;br /&gt;
&amp;nbsp; &amp;nbsp; &lt;span class="st0"&gt;'&amp;lt;/form&amp;gt;'&lt;/span&gt;&lt;br /&gt;
&amp;nbsp; &lt;span class="br0"&gt;&amp;#41;&lt;/span&gt;;&lt;br /&gt;
&amp;nbsp; res.&lt;span class="me1"&gt;finish&lt;/span&gt;&lt;span class="br0"&gt;&amp;#40;&lt;/span&gt;&lt;span class="br0"&gt;&amp;#41;&lt;/span&gt;;&lt;br /&gt;
&lt;span class="br0"&gt;&amp;#125;&lt;/span&gt;&lt;br /&gt;
&lt;br /&gt;
&lt;span class="kw2"&gt;function&lt;/span&gt; upload_file&lt;span class="br0"&gt;&amp;#40;&lt;/span&gt;req, res&lt;span class="br0"&gt;&amp;#41;&lt;/span&gt; &lt;span class="br0"&gt;&amp;#123;&lt;/span&gt;&lt;br /&gt;
&amp;nbsp; req.&lt;span class="me1"&gt;setBodyEncoding&lt;/span&gt;&lt;span class="br0"&gt;&amp;#40;&lt;/span&gt;&lt;span class="st0"&gt;'binary'&lt;/span&gt;&lt;span class="br0"&gt;&amp;#41;&lt;/span&gt;;&lt;br /&gt;
&lt;br /&gt;
&amp;nbsp; &lt;span class="kw2"&gt;var&lt;/span&gt; stream = &lt;span class="kw2"&gt;new&lt;/span&gt; multipart.&lt;span class="me1"&gt;Stream&lt;/span&gt;&lt;span class="br0"&gt;&amp;#40;&lt;/span&gt;req&lt;span class="br0"&gt;&amp;#41;&lt;/span&gt;;&lt;br /&gt;
&amp;nbsp; stream.&lt;span class="me1"&gt;addListener&lt;/span&gt;&lt;span class="br0"&gt;&amp;#40;&lt;/span&gt;&lt;span class="st0"&gt;'part'&lt;/span&gt;, &lt;span class="kw2"&gt;function&lt;/span&gt;&lt;span class="br0"&gt;&amp;#40;&lt;/span&gt;part&lt;span class="br0"&gt;&amp;#41;&lt;/span&gt; &lt;span class="br0"&gt;&amp;#123;&lt;/span&gt;&lt;br /&gt;
&amp;nbsp; &amp;nbsp; part.&lt;span class="me1"&gt;addListener&lt;/span&gt;&lt;span class="br0"&gt;&amp;#40;&lt;/span&gt;&lt;span class="st0"&gt;'body'&lt;/span&gt;, &lt;span class="kw2"&gt;function&lt;/span&gt;&lt;span class="br0"&gt;&amp;#40;&lt;/span&gt;chunk&lt;span class="br0"&gt;&amp;#41;&lt;/span&gt; &lt;span class="br0"&gt;&amp;#123;&lt;/span&gt;&lt;br /&gt;
&amp;nbsp; &amp;nbsp; &amp;nbsp; &lt;span class="kw2"&gt;var&lt;/span&gt; progress = &lt;span class="br0"&gt;&amp;#40;&lt;/span&gt;stream.&lt;span class="me1"&gt;bytesReceived&lt;/span&gt; / stream.&lt;span class="me1"&gt;bytesTotal&lt;/span&gt; * &lt;span class="nu0"&gt;100&lt;/span&gt;&lt;span class="br0"&gt;&amp;#41;&lt;/span&gt;.&lt;span class="me1"&gt;toFixed&lt;/span&gt;&lt;span class="br0"&gt;&amp;#40;&lt;/span&gt;&lt;span class="nu0"&gt;2&lt;/span&gt;&lt;span class="br0"&gt;&amp;#41;&lt;/span&gt;;&lt;br /&gt;
&amp;nbsp; &amp;nbsp; &amp;nbsp; &lt;span class="kw2"&gt;var&lt;/span&gt; mb = &lt;span class="br0"&gt;&amp;#40;&lt;/span&gt;stream.&lt;span class="me1"&gt;bytesTotal&lt;/span&gt; &lt;span class="re0"&gt;/ &lt;span class="nu0"&gt;1024&lt;/span&gt; /&lt;/span&gt; &lt;span class="nu0"&gt;1024&lt;/span&gt;&lt;span class="br0"&gt;&amp;#41;&lt;/span&gt;.&lt;span class="me1"&gt;toFixed&lt;/span&gt;&lt;span class="br0"&gt;&amp;#40;&lt;/span&gt;&lt;span class="nu0"&gt;1&lt;/span&gt;&lt;span class="br0"&gt;&amp;#41;&lt;/span&gt;;&lt;br /&gt;
&lt;br /&gt;
&amp;nbsp; &amp;nbsp; &amp;nbsp; &lt;span class="kw3"&gt;print&lt;/span&gt;&lt;span class="br0"&gt;&amp;#40;&lt;/span&gt;&lt;span class="st0"&gt;&amp;quot;Uploading &amp;quot;&lt;/span&gt;+mb+&lt;span class="st0"&gt;&amp;quot;mb (&amp;quot;&lt;/span&gt;+progress+&lt;span class="st0"&gt;&amp;quot;%)&lt;span class="es0"&gt;\0&lt;/span&gt;15&amp;quot;&lt;/span&gt;&lt;span class="br0"&gt;&amp;#41;&lt;/span&gt;;&lt;br /&gt;
&lt;br /&gt;
&amp;nbsp; &amp;nbsp; &amp;nbsp; &lt;span class="co1"&gt;// chunk could be appended to a file if the uploaded file needs to be saved&lt;/span&gt;&lt;br /&gt;
&amp;nbsp; &amp;nbsp; &lt;span class="br0"&gt;&amp;#125;&lt;/span&gt;&lt;span class="br0"&gt;&amp;#41;&lt;/span&gt;;&lt;br /&gt;
&amp;nbsp; &lt;span class="br0"&gt;&amp;#125;&lt;/span&gt;&lt;span class="br0"&gt;&amp;#41;&lt;/span&gt;;&lt;br /&gt;
&amp;nbsp; stream.&lt;span class="me1"&gt;addListener&lt;/span&gt;&lt;span class="br0"&gt;&amp;#40;&lt;/span&gt;&lt;span class="st0"&gt;'complete'&lt;/span&gt;, &lt;span class="kw2"&gt;function&lt;/span&gt;&lt;span class="br0"&gt;&amp;#40;&lt;/span&gt;&lt;span class="br0"&gt;&amp;#41;&lt;/span&gt; &lt;span class="br0"&gt;&amp;#123;&lt;/span&gt;&lt;br /&gt;
&amp;nbsp; &amp;nbsp; res.&lt;span class="me1"&gt;sendHeader&lt;/span&gt;&lt;span class="br0"&gt;&amp;#40;&lt;/span&gt;&lt;span class="nu0"&gt;200&lt;/span&gt;, &lt;span class="br0"&gt;&amp;#123;&lt;/span&gt;&lt;span class="st0"&gt;'Content-Type'&lt;/span&gt;: &lt;span class="st0"&gt;'text/plain'&lt;/span&gt;&lt;span class="br0"&gt;&amp;#125;&lt;/span&gt;&lt;span class="br0"&gt;&amp;#41;&lt;/span&gt;;&lt;br /&gt;
&amp;nbsp; &amp;nbsp; res.&lt;span class="me1"&gt;sendBody&lt;/span&gt;&lt;span class="br0"&gt;&amp;#40;&lt;/span&gt;&lt;span class="st0"&gt;'Thanks for playing!'&lt;/span&gt;&lt;span class="br0"&gt;&amp;#41;&lt;/span&gt;;&lt;br /&gt;
&amp;nbsp; &amp;nbsp; res.&lt;span class="me1"&gt;finish&lt;/span&gt;&lt;span class="br0"&gt;&amp;#40;&lt;/span&gt;&lt;span class="br0"&gt;&amp;#41;&lt;/span&gt;;&lt;br /&gt;
&amp;nbsp; &amp;nbsp; puts&lt;span class="br0"&gt;&amp;#40;&lt;/span&gt;&lt;span class="st0"&gt;&amp;quot;&lt;span class="es0"&gt;\n&lt;/span&gt;=&amp;gt; Done&amp;quot;&lt;/span&gt;&lt;span class="br0"&gt;&amp;#41;&lt;/span&gt;;&lt;br /&gt;
&amp;nbsp; &lt;span class="br0"&gt;&amp;#125;&lt;/span&gt;&lt;span class="br0"&gt;&amp;#41;&lt;/span&gt;;&lt;br /&gt;
&lt;span class="br0"&gt;&amp;#125;&lt;/span&gt;&lt;br /&gt;
&lt;br /&gt;
&lt;span class="kw2"&gt;function&lt;/span&gt; show_404&lt;span class="br0"&gt;&amp;#40;&lt;/span&gt;req, res&lt;span class="br0"&gt;&amp;#41;&lt;/span&gt; &lt;span class="br0"&gt;&amp;#123;&lt;/span&gt;&lt;br /&gt;
&amp;nbsp; res.&lt;span class="me1"&gt;sendHeader&lt;/span&gt;&lt;span class="br0"&gt;&amp;#40;&lt;/span&gt;&lt;span class="nu0"&gt;404&lt;/span&gt;, &lt;span class="br0"&gt;&amp;#123;&lt;/span&gt;&lt;span class="st0"&gt;'Content-Type'&lt;/span&gt;: &lt;span class="st0"&gt;'text/plain'&lt;/span&gt;&lt;span class="br0"&gt;&amp;#125;&lt;/span&gt;&lt;span class="br0"&gt;&amp;#41;&lt;/span&gt;;&lt;br /&gt;
&amp;nbsp; res.&lt;span class="me1"&gt;sendBody&lt;/span&gt;&lt;span class="br0"&gt;&amp;#40;&lt;/span&gt;&lt;span class="st0"&gt;'You r doing it rong!'&lt;/span&gt;&lt;span class="br0"&gt;&amp;#41;&lt;/span&gt;;&lt;br /&gt;
&amp;nbsp; res.&lt;span class="me1"&gt;finish&lt;/span&gt;&lt;span class="br0"&gt;&amp;#40;&lt;/span&gt;&lt;span class="br0"&gt;&amp;#41;&lt;/span&gt;;&lt;br /&gt;
&lt;span class="br0"&gt;&amp;#125;&lt;/span&gt;&lt;/div&gt;
&lt;p&gt;The code is rather straight forward. First of all we include the multipart.js parser which is a library that has just been added to node.js.&lt;/p&gt;
&lt;p&gt;Next we create a server listening on port 8000 that dispatches incoming requests to one of our 3 functions: display_form, upload_file or show_404. Again, very straight forward.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;display_form&lt;/strong&gt; serves a very short (and invalid) piece of HTML that will render a file upload form with a submit button. You can get to it by running the example (via: node uploader.js) and pointing your browser to http://localhost:8000/.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;upload_file&lt;/strong&gt; kicks in as soon as you select a file and hit the submit button. It tells the request object to expect binary data and then passes the work on to the multipart Stream parser. The result is a new stream object that emits two kinds of events: 'part' and 'complete'. 'part' is called whenever a new element is found within the multipart stream, you can find all the information about it by looking at  the first argument's headers property. In order to get the actual contents of this part we attach a 'body' listener to it, which gets called for each chunk of bytes getting uploaded. In our example we just use this event to render a progress indicator in our command line, but we could also append this chunk to a file which would eventually become the entire file as uploaded from the browser. Finally the 'complete' event sends a response to the browser indicating the file has been uploaded.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;show_404&lt;/strong&gt; is handling all unknown urls by returning an error response.&lt;/p&gt;
&lt;p&gt;As you can see, the entire process is pretty simple, yet gives you a ton of control. You can also easily use this technique to show an AJAX progress bar for the upload to your users. The multipart parser also works with non-HTTP requests, just pass it the {boundary: '...'} option into the constructor and use steam.write() to pass it some data to parse. Check out the &lt;a href="http://github.com/ry/node/blob/master/lib/multipart.js"&gt;source of the parser&lt;/a&gt; if you're curious how it works internally.&lt;/p&gt;
&lt;p&gt;-- Felix Geisendörfer aka the_undefined&lt;br /&gt;
&lt;img style="display: none;" src="http://www.debuggable.com/posts/tick/4ac094b2-b6c8-4a7f-bd07-28accbdd56cb"&gt;&lt;/p&gt;&lt;img src="http://feeds.feedburner.com/~r/debuggable/~4/tWs27OCCqgM" height="1" width="1"/&gt;</description><author>felix@debuggable.com</author><pubDate>Mon, 28 Sep 2009 10:49:22 +0000</pubDate><comments>http://www.debuggable.com/posts/streaming-file-uploads-with-node-js:4ac094b2-b6c8-4a7f-bd07-28accbdd56cb</comments><feedburner:origLink>http://www.debuggable.com/posts/streaming-file-uploads-with-node-js:4ac094b2-b6c8-4a7f-bd07-28accbdd56cb</feedburner:origLink></item><item><title>node.js</title><link>http://feedproxy.google.com/~r/debuggable/~3/XCpYP4BfKW0/node_js:4ab4d9d7-b788-41d4-85c0-1b51cbdd56cb</link><guid isPermaLink="false">http://www.debuggable.com/posts/node_js:4ab4d9d7-b788-41d4-85c0-1b51cbdd56cb</guid><description>&lt;p&gt;What happens if you take an &lt;a href="http://code.google.com/p/v8/"&gt;insanely fast JavaScript engine&lt;/a&gt; and build an asynchronous I/O library around it? You get &lt;a href="http://tinyclouds.org/node/"&gt;node.js&lt;/a&gt;, an up and coming project that brings you bloat-free server-side JavaScript.&lt;/p&gt;
&lt;p&gt;This enables you to write any kind of "backend" service with just a few lines of JavaScript. But don't take my word for it, here is how a "Hello World" example looks in node.js:&lt;/p&gt;
&lt;div class="clear"&gt;&lt;/div&gt;
&lt;div class="code debuggable_sh_js" style="white-space: wrap;white-space: nowrap;"&gt;
node.&lt;span class="me1"&gt;http&lt;/span&gt;.&lt;span class="me1"&gt;createServer&lt;/span&gt;&lt;span class="br0"&gt;&amp;#40;&lt;/span&gt;&lt;span class="kw2"&gt;function&lt;/span&gt; &lt;span class="br0"&gt;&amp;#40;&lt;/span&gt;req, res&lt;span class="br0"&gt;&amp;#41;&lt;/span&gt; &lt;span class="br0"&gt;&amp;#123;&lt;/span&gt;&lt;br /&gt;
&amp;nbsp; setTimeout&lt;span class="br0"&gt;&amp;#40;&lt;/span&gt;&lt;span class="kw2"&gt;function&lt;/span&gt; &lt;span class="br0"&gt;&amp;#40;&lt;/span&gt;&lt;span class="br0"&gt;&amp;#41;&lt;/span&gt; &lt;span class="br0"&gt;&amp;#123;&lt;/span&gt;&lt;br /&gt;
&amp;nbsp; &amp;nbsp; res.&lt;span class="me1"&gt;sendHeader&lt;/span&gt;&lt;span class="br0"&gt;&amp;#40;&lt;/span&gt;&lt;span class="nu0"&gt;200&lt;/span&gt;, &lt;span class="br0"&gt;&amp;#123;&lt;/span&gt;&lt;span class="st0"&gt;&amp;quot;Content-Type&amp;quot;&lt;/span&gt;: &lt;span class="st0"&gt;&amp;quot;text/plain&amp;quot;&lt;/span&gt;&lt;span class="br0"&gt;&amp;#125;&lt;/span&gt;&lt;span class="br0"&gt;&amp;#41;&lt;/span&gt;;&lt;br /&gt;
&amp;nbsp; &amp;nbsp; res.&lt;span class="me1"&gt;sendBody&lt;/span&gt;&lt;span class="br0"&gt;&amp;#40;&lt;/span&gt;&lt;span class="st0"&gt;&amp;quot;Hello World&amp;quot;&lt;/span&gt;&lt;span class="br0"&gt;&amp;#41;&lt;/span&gt;;&lt;br /&gt;
&amp;nbsp; &amp;nbsp; res.&lt;span class="me1"&gt;finish&lt;/span&gt;&lt;span class="br0"&gt;&amp;#40;&lt;/span&gt;&lt;span class="br0"&gt;&amp;#41;&lt;/span&gt;;&lt;br /&gt;
&amp;nbsp; &lt;span class="br0"&gt;&amp;#125;&lt;/span&gt;, &lt;span class="nu0"&gt;2000&lt;/span&gt;&lt;span class="br0"&gt;&amp;#41;&lt;/span&gt;;&lt;br /&gt;
&lt;span class="br0"&gt;&amp;#125;&lt;/span&gt;&lt;span class="br0"&gt;&amp;#41;&lt;/span&gt;.&lt;span class="me1"&gt;listen&lt;/span&gt;&lt;span class="br0"&gt;&amp;#40;&lt;/span&gt;&lt;span class="nu0"&gt;8000&lt;/span&gt;&lt;span class="br0"&gt;&amp;#41;&lt;/span&gt;;&lt;br /&gt;
puts&lt;span class="br0"&gt;&amp;#40;&lt;/span&gt;&lt;span class="st0"&gt;&amp;quot;Server running at http://127.0.0.1:8000/&amp;quot;&lt;/span&gt;&lt;span class="br0"&gt;&amp;#41;&lt;/span&gt;;&lt;/div&gt;
&lt;p&gt;This code creates a new http server and tells it to listen on port 8000. Whenever a request comes in, the closure function passed to createServer is fired. In this example we are waiting 2 seconds before sending a "Hello World" response back to the client.&lt;/p&gt;
&lt;p&gt;However, during those 2 seconds the server will continue to accept new connections. That gives you a very scalable hello world server. Talking about performance: Ryan (the awesome guy behind node.js) has done some &lt;a href="http://s3.amazonaws.com/four.livejournal/20090911/benchmark.html"&gt;initial benchmarks&lt;/a&gt; which show how fast of a beast we are talking about here. To me the most notable aspect is the extremely low response times you can achieve with node.js (pretty much 0-1ms).&lt;/p&gt;
&lt;p&gt;Here is another chart from a req/sec benchmark among various server-side JS libs:&lt;/p&gt;
&lt;p&gt;&lt;img src="http://static.debuggable.com.s3.amazonaws.com/img/node.js/node-bench.png"&gt;&lt;/p&gt;
&lt;p&gt;Now you may think this is all very nice and stuff, but what do you actually need it for? Well, it depends. There are a few people writing web frameworks for node.js, but at this point it would be rather adventurous to build a full blown web application on top of node. However, node has incredible potential if you want to develop chat applications, check &lt;a href="http://chat.tinyclouds.org/"&gt;out this one for example&lt;/a&gt;. We are using node.js to run the backend for &lt;a href="http://transload.it/"&gt;transload.it&lt;/a&gt; which we'll share more details about in the future. One could also easily write his own key-value store in node.js - let's say you'd like to have something like Memcached but with a REST interface.&lt;/p&gt;
&lt;p&gt;Trying out node.js requires you to have a Linux/Unix machine but it's pretty much as simple as downloading the code and running:&lt;/p&gt;
&lt;pre class="terminal"&gt;
./configure
make
make install
&lt;/pre&gt;
&lt;p&gt;The &lt;a href="http://tinyclouds.org/node/api.html"&gt;documentation&lt;/a&gt; is pretty excellent and there are lots of friendly people on the &lt;a href="http://groups.google.com/group/nodejs"&gt;mailing list&lt;/a&gt; to help if you have any problems.&lt;/p&gt;
&lt;p&gt;To me the most exciting thing about node.js is its incredible potential to reunite. You can have long debates about using Python, Ruby, PHP, Java, ASP or Perl for a web project, but nobody will debate that JavaScript is *the* language of the web. Now imagine the smartest people from all those communities creating code in a single language that works on your browser as well as your server ...&lt;/p&gt;
&lt;p&gt;I'd love to hear your thoughts on node.js and any questions you might have. Just leave comment!&lt;/p&gt;
&lt;p&gt;-- Felix Geisendörfer aka the_undefined&lt;br /&gt;
&lt;img style="display: none;" src="http://www.debuggable.com/posts/tick/4ab4d9d7-b788-41d4-85c0-1b51cbdd56cb"&gt;&lt;/p&gt;&lt;img src="http://feeds.feedburner.com/~r/debuggable/~4/XCpYP4BfKW0" height="1" width="1"/&gt;</description><author>felix@debuggable.com</author><pubDate>Thu, 24 Sep 2009 10:03:55 +0000</pubDate><comments>http://www.debuggable.com/posts/node_js:4ab4d9d7-b788-41d4-85c0-1b51cbdd56cb</comments><feedburner:origLink>http://www.debuggable.com/posts/node_js:4ab4d9d7-b788-41d4-85c0-1b51cbdd56cb</feedburner:origLink></item><item><title>Fixing non-atomic commits in git</title><link>http://feedproxy.google.com/~r/debuggable/~3/33w4syxdWtQ/Fixing_a_bad_commit_in_git:4aafbda3-ac2c-49b8-b041-1bc9cbdd56cb</link><guid isPermaLink="false">http://www.debuggable.com/posts/Fixing_a_bad_commit_in_git:4aafbda3-ac2c-49b8-b041-1bc9cbdd56cb</guid><description>&lt;p&gt;Let's say somebody else made a commit that mixes a bug fix and a new feature together.  This sucks if you only want to take the bugfix to merge it into your stable branch (using git cherry-pick).&lt;/p&gt;
&lt;p&gt;If you were using SVN you'd be screwed now. However, if you are using git you can actually clean this mess in an elegant and transparent fashion:&lt;/p&gt;
&lt;pre class="terminal"&gt;
git revert --no-edit &lt;BADSHA1&gt;
git revert --no-edit HEAD
git reset HEAD^
git add -p # or other commands to partially stage stuff from the current tree
git commit -m "Commit A"
git add -p
git commit -m "Commit B"
&lt;/pre&gt;
&lt;p&gt;Let's look at this step by step:&lt;/p&gt;
&lt;pre class="terminal"&gt;
git revert --no-edit &lt;BADSHA1&gt;
&lt;/pre&gt;
&lt;p&gt;This command creates a new commit that reverts the bad commit.&lt;/p&gt;
&lt;pre class="terminal"&gt;
git revert --no-edit HEAD
&lt;/pre&gt;
&lt;p&gt;The second 'revert' creates a new commit that basically redoes the bad commit. Why? Because now you have a "clone" of the bad commit sitting cleanly on top of your HEAD, waiting for you to be messed with. This is much better than using git reset to go back to it, because you are not deleting history (which is very likely to give you merge conflicts).&lt;/p&gt;
&lt;pre class="terminal"&gt;
git reset HEAD^
&lt;/pre&gt;
&lt;p&gt;By using git revert you can now go back to the moment before the "clone" commit was created, but still have all of its changes in your tree (=local file system). This essentially takes you back in time to the moment before your evil teammate hit the commit button and gives you the ability to yell "Nooooooo! Let me tell you about atomic commits buddy". &lt;/p&gt;
&lt;p&gt;Now you can use 'git add' or 'git add -p' to stage all your changes for the first atomic change and commit it. Repeat the process until you've split the bad commit into nice atomic pieces.&lt;/p&gt;
&lt;p&gt;All that is left to do now is to explain to your teammate that next time he accidentally makes a bad commit, he should use 'git reset HEAD^' right away. This way the problem can be fixed before git push distributes the bad commit to your team.&lt;/p&gt;
&lt;p&gt;Let me know if this makes sense or if you have any question. I also accept "Git Challenges". That is if you have a problem you'd like to know how it could be solved in Git, just let me know in the comments and I'll blog about it : ).&lt;/p&gt;
&lt;p&gt;-- Felix Geisendörfer aka the_undefined&lt;br /&gt;
&lt;img style="display: none;" src="http://www.debuggable.com/posts/tick/4aafbda3-ac2c-49b8-b041-1bc9cbdd56cb"&gt;&lt;/p&gt;&lt;img src="http://feeds.feedburner.com/~r/debuggable/~4/33w4syxdWtQ" height="1" width="1"/&gt;</description><author>felix@debuggable.com</author><pubDate>Tue, 15 Sep 2009 16:44:15 +0000</pubDate><comments>http://www.debuggable.com/posts/Fixing_a_bad_commit_in_git:4aafbda3-ac2c-49b8-b041-1bc9cbdd56cb</comments><feedburner:origLink>http://www.debuggable.com/posts/Fixing_a_bad_commit_in_git:4aafbda3-ac2c-49b8-b041-1bc9cbdd56cb</feedburner:origLink></item><item><title>The open source business model</title><link>http://feedproxy.google.com/~r/debuggable/~3/M9XNNIi_06Q/The_open_source_business_model:4aab63c1-bf9c-42e8-be7e-5607cbdd56cb</link><guid isPermaLink="false">http://www.debuggable.com/posts/The_open_source_business_model:4aab63c1-bf9c-42e8-be7e-5607cbdd56cb</guid><description>&lt;p&gt;Seth Godin &lt;a href="http://sethgodin.typepad.com/seths_blog/2009/09/flipping-abundance-and-scarity.html"&gt;explains it perfectly&lt;/a&gt; this morning:&lt;/p&gt;
&lt;p&gt;&lt;em&gt;You need to make something else abundant in order to gain attention. Then, and only then, will you be able to sell something that's naturally scarce.&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;Tim and I stumbled across this model by accident when we started our blogs a few years ago. Since then our business has been a 99% byproduct of the "free" stuff we are doing.&lt;/p&gt;
&lt;p&gt;-- Felix Geisendörfer aka the_undefined&lt;br /&gt;
&lt;img style="display: none;" src="http://www.debuggable.com/posts/tick/4aab63c1-bf9c-42e8-be7e-5607cbdd56cb"&gt;&lt;/p&gt;&lt;img src="http://feeds.feedburner.com/~r/debuggable/~4/M9XNNIi_06Q" height="1" width="1"/&gt;</description><author>felix@debuggable.com</author><pubDate>Sat, 12 Sep 2009 09:08:56 +0000</pubDate><comments>http://www.debuggable.com/posts/The_open_source_business_model:4aab63c1-bf9c-42e8-be7e-5607cbdd56cb</comments><feedburner:origLink>http://www.debuggable.com/posts/The_open_source_business_model:4aab63c1-bf9c-42e8-be7e-5607cbdd56cb</feedburner:origLink></item><item><title>Google Chrome for Mac</title><link>http://feedproxy.google.com/~r/debuggable/~3/dPpzCrL_Z3Y/Google_Chrome_for_Mac:4aaa8f79-859c-4792-aa13-4c6ccbdd56cb</link><guid isPermaLink="false">http://www.debuggable.com/posts/Google_Chrome_for_Mac:4aaa8f79-859c-4792-aa13-4c6ccbdd56cb</guid><description>&lt;p&gt;When &lt;a href="http://twitter.com/predominant/status/3899762280"&gt;@predominant&lt;/a&gt; linked to a OSX release for google chrome the other day I couldn't resist.&lt;/p&gt;
&lt;p&gt;My initial feeling: Awesome. Feels much snappier than Safari 4. My only two issues: a) No support for Safari-style keyboard shortcuts for the bookmark bar items. b) Even so chrome integrates with Keychain, it doesn't seem to use my Safari logins.&lt;/p&gt;
&lt;p&gt;Lucky for me, Google also just released their &lt;a href="http://code.google.com/chrome/extensions/getstarted.html"&gt;Extension API&lt;/a&gt;. Two hours down the sink, I created my first extension: &lt;a href="http://github.com/felixge/chrome-bookmark-bar-shortcuts/tree/master"&gt;Bookmark Bar Shortcuts&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;I switched from Firefox to Safari as my main browser a long time ago because I just could not deal with its slowness on OSX. I still have a few issues with Chrome, but I think they are due to the experimental Mac release. Once Chrome goes beta I'm pretty sure Safari won't be running on my computer anymore. Nor will Firefox once somebody ports Firebug to Google Chrome.&lt;/p&gt;
&lt;p&gt;-- Felix Geisendörfer aka the_undefined&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Update:&lt;/strong&gt; &lt;a href="http://twitter.com/reconbot/status/3915571309"&gt;@reconbot&lt;/a&gt; says Ctrl + &lt;1-9&gt; already works on Windows. Can somebody confirm?&lt;br /&gt;
&lt;img style="display: none;" src="http://www.debuggable.com/posts/tick/4aaa8f79-859c-4792-aa13-4c6ccbdd56cb"&gt;&lt;/p&gt;&lt;img src="http://feeds.feedburner.com/~r/debuggable/~4/dPpzCrL_Z3Y" height="1" width="1"/&gt;</description><author>felix@debuggable.com</author><pubDate>Fri, 11 Sep 2009 18:05:44 +0000</pubDate><comments>http://www.debuggable.com/posts/Google_Chrome_for_Mac:4aaa8f79-859c-4792-aa13-4c6ccbdd56cb</comments><feedburner:origLink>http://www.debuggable.com/posts/Google_Chrome_for_Mac:4aaa8f79-859c-4792-aa13-4c6ccbdd56cb</feedburner:origLink></item><link rel="stylesheet" type="text/css" href="/css/syntax_hilite.css" /></channel></rss>
