<?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>Boldr</title><link>http://boldr.net/</link><atom10:link xmlns:atom10="http://www.w3.org/2005/Atom" rel="self" type="application/rss+xml" href="http://feeds.feedburner.com/fr/boldr" /><language>en</language><lastBuildDate>Sat, 31 Dec 2011 16:00:00 PST</lastBuildDate><feedburner:info uri="fr/boldr" /><atom10:link xmlns:atom10="http://www.w3.org/2005/Atom" rel="hub" href="http://pubsubhubbub.appspot.com/" /><description>Ruby, Rails, Mac et Web 2.0</description><item><title>Undesign</title><link>http://feedproxy.google.com/~r/fr/boldr/~3/WmhdZYqNsGk/undesign</link><dc:creator xmlns:dc="http://purl.org/dc/elements/1.1/">Nicolas Mérouze</dc:creator><pubDate>Sat, 31 Dec 2011 16:00:00 PST</pubDate><guid isPermaLink="false">tag:boldr.net,2009:undesign</guid><description>&lt;!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN" "http://www.w3.org/TR/REC-html40/loose.dtd"&gt;
&lt;html&gt;&lt;body&gt;
&lt;p&gt;I wanted to reopen this blog for a few weeks now and I didn't know what type of design I wanted to make. I decided a few days ago to undesign it and keep only what's really necessary. So no textures, no images and no colors (except for code highlighting).&lt;/p&gt;

&lt;p&gt;The design  and the development was fast and I can finally write. I like the result and now I can focus on the experience you'll have on the blog, from reading to interacting (no interactions yet, but comments are coming soon).&lt;/p&gt;
&lt;/body&gt;&lt;/html&gt;&lt;div class="feedflare"&gt;
&lt;a href="http://feeds.feedburner.com/~ff/fr/boldr?a=WmhdZYqNsGk:k2VxhkpzRgE:7Q72WNTAKBA"&gt;&lt;img src="http://feeds.feedburner.com/~ff/fr/boldr?d=7Q72WNTAKBA" border="0"&gt;&lt;/img&gt;&lt;/a&gt;
&lt;/div&gt;&lt;img src="http://feeds.feedburner.com/~r/fr/boldr/~4/WmhdZYqNsGk" height="1" width="1"/&gt;</description><feedburner:origLink>http://boldr.net/undesign</feedburner:origLink></item><item><title>The past, the present and the future of Boldr</title><link>http://feedproxy.google.com/~r/fr/boldr/~3/mRHAZKLt7wA/past-future-boldr</link><dc:creator xmlns:dc="http://purl.org/dc/elements/1.1/">Nicolas Mérouze</dc:creator><pubDate>Sat, 31 Dec 2011 16:00:00 PST</pubDate><guid isPermaLink="false">tag:boldr.net,2009:past-future-boldr</guid><description>&lt;!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN" "http://www.w3.org/TR/REC-html40/loose.dtd"&gt;
&lt;html&gt;&lt;body&gt;
&lt;p&gt;I stopped blogging about 18 months ago. I've been very busy with my startup. This has been great but ultimately I realized that spending so much time and energy and stressing all day wasn't what I wanted for my life. So a few months ago I had the opportunity to become a freelance again and I took it.&lt;/p&gt;

&lt;p&gt;Now that I work less I'm happier, the stress has vanished, I got married and I can save money for later. I also have more time to blog again and to focus on new side projects. 2011 has globally been a great year for me and I perfected my skills in other areas than programming, so expect to have articles about more things than HTML5/CSS3, Ruby and Javascript.&lt;/p&gt;

&lt;p&gt;Boldr is now the name of my company which is also why I decided to write again in order to prepare the company for future clients. I'm almost full for 2012 but I would be happy to work on small projects.&lt;/p&gt;
&lt;/body&gt;&lt;/html&gt;&lt;div class="feedflare"&gt;
&lt;a href="http://feeds.feedburner.com/~ff/fr/boldr?a=mRHAZKLt7wA:eqSE-zHqxAo:7Q72WNTAKBA"&gt;&lt;img src="http://feeds.feedburner.com/~ff/fr/boldr?d=7Q72WNTAKBA" border="0"&gt;&lt;/img&gt;&lt;/a&gt;
&lt;/div&gt;&lt;img src="http://feeds.feedburner.com/~r/fr/boldr/~4/mRHAZKLt7wA" height="1" width="1"/&gt;</description><feedburner:origLink>http://boldr.net/past-future-boldr</feedburner:origLink></item><item><title>Rails 3 + Mongoid + HAML + RSpec 2 template</title><link>http://feedproxy.google.com/~r/fr/boldr/~3/y22Y9i113Es/rails-3-app-template-mongoid-rspec-haml</link><dc:creator xmlns:dc="http://purl.org/dc/elements/1.1/">Nicolas Mérouze</dc:creator><pubDate>Sat, 17 Jul 2010 17:00:00 PDT</pubDate><guid isPermaLink="false">tag:boldr.net,2009:rails-3-app-template-mongoid-rspec-haml</guid><description>&lt;!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN" "http://www.w3.org/TR/REC-html40/loose.dtd"&gt;
&lt;html&gt;&lt;body&gt;
&lt;p&gt;Quick app template based on &lt;a href="http://blog.leshill.org/blog/2010/05/08/rails-3-rspec-factory-girl-haml-and-jquery.html"&gt;the one from Les Hill&lt;/a&gt; :&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;rails new my_app -OJT -m http://gist.github.com/raw/473676/ca54399b18bd1f35587ef0a3891aa6b427508da7/app.rb
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;It gives you Mongoid + HAML + RSpec 2 + jQuery. This is my standard stack when I'm creating a new application nowadays.&lt;/p&gt;
&lt;/body&gt;&lt;/html&gt;&lt;div class="feedflare"&gt;
&lt;a href="http://feeds.feedburner.com/~ff/fr/boldr?a=y22Y9i113Es:AKtenn2oGwI:7Q72WNTAKBA"&gt;&lt;img src="http://feeds.feedburner.com/~ff/fr/boldr?d=7Q72WNTAKBA" border="0"&gt;&lt;/img&gt;&lt;/a&gt;
&lt;/div&gt;&lt;img src="http://feeds.feedburner.com/~r/fr/boldr/~4/y22Y9i113Es" height="1" width="1"/&gt;</description><feedburner:origLink>http://boldr.net/rails-3-app-template-mongoid-rspec-haml</feedburner:origLink></item><item><title>Getting started with Duby and MongoDB</title><link>http://feedproxy.google.com/~r/fr/boldr/~3/abaHW_allnM/getting-started-duby-mongodb</link><dc:creator xmlns:dc="http://purl.org/dc/elements/1.1/">Nicolas Mérouze</dc:creator><pubDate>Thu, 18 Feb 2010 16:00:00 PST</pubDate><guid isPermaLink="false">tag:boldr.net,2009:getting-started-duby-mongodb</guid><description>&lt;!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN" "http://www.w3.org/TR/REC-html40/loose.dtd"&gt;
&lt;html&gt;&lt;body&gt;
&lt;p&gt;I first talked about &lt;a href="http://boldr.net/my-jruby-talk-at-blockcampparis"&gt;Duby back in November&lt;/a&gt;. The project has since evolved, there's now more features and it's more usable. I'm able to run MongoDB so I decided to share it. If you know nothing about Duby, &lt;a href="http://github.com/headius/duby"&gt;read the README&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;NOTE&lt;/strong&gt;: The following example is based on the &lt;a href="http://github.com/mongodb/mongo-java-driver/blob/master/examples/QuickTour.java"&gt;java example from the mongo-java-driver repo&lt;/a&gt;.&lt;/p&gt;

&lt;h2&gt;Setup&lt;/h2&gt;

&lt;p&gt;We first need to download the &lt;a href="http://github.com/downloads/mongodb/mongo-java-driver/mongo-1.2.jar"&gt;MongoDB jar&lt;/a&gt; and add its path to CLASSPATH:&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;$ export CLASSPATH=.:/my/path/to/mongo-1.2.jar
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;Then we create our Duby file we name &lt;code&gt;MongoTest.duby&lt;/code&gt;.&lt;/p&gt;

&lt;h2&gt;Connection to MongoDB&lt;/h2&gt;

&lt;p&gt;We start by importing all we need:&lt;/p&gt;

&lt;div class="highlight"&gt;
&lt;pre&gt;&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="nn"&gt;com.mongodb.Mongo&lt;/span&gt;
&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="nn"&gt;com.mongodb.DBCollection&lt;/span&gt;
&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="nn"&gt;com.mongodb.BasicDBObject&lt;/span&gt;
&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="nn"&gt;com.mongodb.DBObject&lt;/span&gt;
&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="nn"&gt;com.mongodb.DBCursor&lt;/span&gt;
&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="nn"&gt;com.mongodb.DB&lt;/span&gt;

&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="nn"&gt;java.util.Set&lt;/span&gt;
&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="nn"&gt;java.util.List&lt;/span&gt;
&lt;/pre&gt;
&lt;/div&gt;


&lt;p&gt;Then get the DB connection:&lt;/p&gt;

&lt;div class="highlight"&gt;
&lt;pre&gt;&lt;span class="k"&gt;begin&lt;/span&gt;
  &lt;span class="n"&gt;m&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="no"&gt;Mongo&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;new&lt;/span&gt;
  &lt;span class="n"&gt;db&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;m&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;getDB&lt;/span&gt; &lt;span class="s2"&gt;"mydb"&lt;/span&gt;
  &lt;span class="c1"&gt;# the rest of the code will go here&lt;/span&gt;
&lt;span class="k"&gt;rescue&lt;/span&gt; &lt;span class="no"&gt;Exception&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;
&lt;/pre&gt;
&lt;/div&gt;


&lt;h2&gt;Cool new feature: Iterators&lt;/h2&gt;

&lt;p&gt;One reason I love Ruby is because iterators are nice! This feature landed in Duby a while ago and it's just a pleasure to use it (here to display collection names):&lt;/p&gt;

&lt;div class="highlight"&gt;
&lt;pre&gt;&lt;span class="n"&gt;colls&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;db&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;getCollectionNames&lt;/span&gt;

&lt;span class="n"&gt;colls&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;each&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt;&lt;span class="n"&gt;coll&lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt;
  &lt;span class="nb"&gt;puts&lt;/span&gt; &lt;span class="n"&gt;coll&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;
&lt;/pre&gt;
&lt;/div&gt;


&lt;h2&gt;Inserting a new document&lt;/h2&gt;

&lt;p&gt;To insert a new document, we must get a collection:&lt;/p&gt;

&lt;div class="highlight"&gt;
&lt;pre&gt;&lt;span class="n"&gt;testColl&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;db&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;getCollection&lt;/span&gt; &lt;span class="s2"&gt;"testCollection"&lt;/span&gt;
&lt;span class="n"&gt;testColl&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;drop&lt;/span&gt; &lt;span class="c1"&gt;# drop the data from previous test&lt;/span&gt;
&lt;/pre&gt;
&lt;/div&gt;


&lt;p&gt;To build a new document we need to use &lt;code&gt;BasicDBObject&lt;/code&gt; as a container and there's a &lt;code&gt;BasicDBObject#put(key, value)&lt;/code&gt; to add information:&lt;/p&gt;

&lt;div class="highlight"&gt;
&lt;pre&gt;&lt;span class="n"&gt;doc&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="no"&gt;BasicDBObject&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;new&lt;/span&gt;
&lt;span class="n"&gt;doc&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;put&lt;/span&gt; &lt;span class="s2"&gt;"name"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;"MongoDB"&lt;/span&gt;
&lt;span class="n"&gt;doc&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;put&lt;/span&gt; &lt;span class="s2"&gt;"type"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;"database"&lt;/span&gt;
&lt;span class="n"&gt;doc&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;put&lt;/span&gt; &lt;span class="s2"&gt;"count"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;

&lt;span class="n"&gt;info&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="no"&gt;BasicDBObject&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;new&lt;/span&gt;
&lt;span class="n"&gt;info&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;put&lt;/span&gt; &lt;span class="s2"&gt;"x"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;203&lt;/span&gt;
&lt;span class="n"&gt;info&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;put&lt;/span&gt; &lt;span class="s2"&gt;"y"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;102&lt;/span&gt;

&lt;span class="n"&gt;doc&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;put&lt;/span&gt; &lt;span class="s2"&gt;"info"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;info&lt;/span&gt;
&lt;/pre&gt;
&lt;/div&gt;


&lt;p&gt;Of course we can put a &lt;code&gt;BasicDBObject&lt;/code&gt; inside another &lt;code&gt;BasicDBObject&lt;/code&gt;. And here's how to insert the document we've just build:&lt;/p&gt;

&lt;div class="highlight"&gt;
&lt;pre&gt;&lt;span class="n"&gt;testColl&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;insert&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;doc&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/pre&gt;
&lt;/div&gt;


&lt;h2&gt;Getting an existing document&lt;/h2&gt;

&lt;p&gt;Nothing really to say about how to get the document we've just inserted:&lt;/p&gt;

&lt;div class="highlight"&gt;
&lt;pre&gt;&lt;span class="n"&gt;myDoc&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;testColl&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;findOne&lt;/span&gt;
&lt;span class="nb"&gt;puts&lt;/span&gt; &lt;span class="n"&gt;myDoc&lt;/span&gt;
&lt;/pre&gt;
&lt;/div&gt;


&lt;h2&gt;Executing the file&lt;/h2&gt;

&lt;p&gt;I hasn't been able to run the file via &lt;code&gt;duby MongoTest.duby&lt;/code&gt; I had to compile it then call it with Java &lt;code&gt;dubyc -java MongoTest.duby &amp;amp;&amp;amp; javac MongoTest.java &amp;amp;&amp;amp; java MongoTest&lt;/code&gt;&lt;/p&gt;

&lt;h2&gt;Thoughts about Duby&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;dubyc -java&lt;/code&gt; converts Duby code to Java code. So you don't have to worry about performance.&lt;/li&gt;
&lt;li&gt;Duby code really feels like Ruby. And this feeling's getting more and more true.&lt;/li&gt;
&lt;li&gt;You can create a Duby class and use it in your Java code (unlike JRuby). It's because "Duby is Java with Ruby syntax". And it's wonderful.&lt;/li&gt;
&lt;li&gt;The true awesomeness is that you can create Android applications with Duby! &lt;a href="http://github.com/technomancy/ohai-android"&gt;Check out this example&lt;/a&gt;.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Are you excited now? You should be and try Duby right now!&lt;/p&gt;
&lt;/body&gt;&lt;/html&gt;&lt;div class="feedflare"&gt;
&lt;a href="http://feeds.feedburner.com/~ff/fr/boldr?a=abaHW_allnM:JVWoLPpAnSY:7Q72WNTAKBA"&gt;&lt;img src="http://feeds.feedburner.com/~ff/fr/boldr?d=7Q72WNTAKBA" border="0"&gt;&lt;/img&gt;&lt;/a&gt;
&lt;/div&gt;&lt;img src="http://feeds.feedburner.com/~r/fr/boldr/~4/abaHW_allnM" height="1" width="1"/&gt;</description><feedburner:origLink>http://boldr.net/getting-started-duby-mongodb</feedburner:origLink></item><item><title>Store Rails sessions in MongoDB</title><link>http://feedproxy.google.com/~r/fr/boldr/~3/x8gY5BCrZlI/rails-sessions-mongodb</link><dc:creator xmlns:dc="http://purl.org/dc/elements/1.1/">Nicolas Mérouze</dc:creator><pubDate>Wed, 17 Feb 2010 16:00:00 PST</pubDate><guid isPermaLink="false">tag:boldr.net,2009:rails-sessions-mongodb</guid><description>&lt;!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN" "http://www.w3.org/TR/REC-html40/loose.dtd"&gt;
&lt;html&gt;&lt;body&gt;
&lt;p&gt;There are a few built-in session stores in Rails but if you want to store them in MongoDB (or anything not using ActiveRecord) there's nothing. Not anymore with &lt;a href="http://github.com/nmerouze/mongo_session_store"&gt;mongo_session_store&lt;/a&gt;!&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;NOTE&lt;/strong&gt;: The project was initially created by Chris Brickley with MongoMapper support but without Mongoid support and it was not in a gem.&lt;/p&gt;

&lt;h2&gt;Usage with MongoMapper&lt;/h2&gt;

&lt;p&gt;First we load the gem:&lt;/p&gt;

&lt;div class="highlight"&gt;
&lt;pre&gt;&lt;span class="n"&gt;config&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;gem&lt;/span&gt; &lt;span class="s2"&gt;"mongoid"&lt;/span&gt;
&lt;span class="n"&gt;config&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;gem&lt;/span&gt; &lt;span class="s2"&gt;"mongo_session_store"&lt;/span&gt;
&lt;/pre&gt;
&lt;/div&gt;


&lt;p&gt;Don't forget to create an initializer to add the connection to the database. Then configure Rails to use MongoMapper for sessions (in the session store initializer):&lt;/p&gt;

&lt;div class="highlight"&gt;
&lt;pre&gt;&lt;span class="n"&gt;config&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;gem&lt;/span&gt; &lt;span class="s2"&gt;"mongo_mapper"&lt;/span&gt;
&lt;span class="n"&gt;config&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;gem&lt;/span&gt; &lt;span class="s2"&gt;"mongo_session_store"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;:lib&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="s2"&gt;"mongo_session_store/mongo_mapper"&lt;/span&gt;
&lt;/pre&gt;
&lt;/div&gt;


&lt;h2&gt;Usage with Mongoid&lt;/h2&gt;

&lt;p&gt;It's almost the same with Mongoid. First setup the environment correctly:&lt;/p&gt;

&lt;div class="highlight"&gt;
&lt;pre&gt;&lt;span class="nb"&gt;require&lt;/span&gt; &lt;span class="s2"&gt;"mongo_session_store/mongoid"&lt;/span&gt;
&lt;span class="no"&gt;ActionController&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;Base&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;session_store&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="ss"&gt;:mongoid_store&lt;/span&gt;
&lt;/pre&gt;
&lt;/div&gt;


&lt;p&gt;Then the session store initializer:&lt;/p&gt;

&lt;div class="highlight"&gt;
&lt;pre&gt;&lt;span class="no"&gt;ActionController&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;Base&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;session_store&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="ss"&gt;:mongo_mapper_store&lt;/span&gt;
&lt;/pre&gt;
&lt;/div&gt;


&lt;p&gt;&lt;strong&gt;NOTE&lt;/strong&gt;: Mongoid acts a bit differently than MongoMapper but I havn't had the time to fix &lt;a href="http://github.com/nmerouze/mongo_session_store/issues/issue/1"&gt;the issue&lt;/a&gt;.&lt;/p&gt;

&lt;h2&gt;Next&lt;/h2&gt;

&lt;p&gt;I'm gonna add some tests, try to have the same usage for Mongoid and MongoMapper and add Rails 3 support. But if you want some other things please &lt;a href="http://github.com/nmerouze/mongo_session_store"&gt;fork it&lt;/a&gt;.&lt;/p&gt;
&lt;/body&gt;&lt;/html&gt;&lt;div class="feedflare"&gt;
&lt;a href="http://feeds.feedburner.com/~ff/fr/boldr?a=x8gY5BCrZlI:g6gYP1YvZNE:7Q72WNTAKBA"&gt;&lt;img src="http://feeds.feedburner.com/~ff/fr/boldr?d=7Q72WNTAKBA" border="0"&gt;&lt;/img&gt;&lt;/a&gt;
&lt;/div&gt;&lt;img src="http://feeds.feedburner.com/~r/fr/boldr/~4/x8gY5BCrZlI" height="1" width="1"/&gt;</description><feedburner:origLink>http://boldr.net/rails-sessions-mongodb</feedburner:origLink></item><item><title>MVC with Node.js - Which modules?</title><link>http://feedproxy.google.com/~r/fr/boldr/~3/48UGKXdse3A/mvc-stack-node-js-ejsgi-scylla-mustache</link><dc:creator xmlns:dc="http://purl.org/dc/elements/1.1/">Nicolas Mérouze</dc:creator><pubDate>Mon, 15 Feb 2010 16:00:00 PST</pubDate><guid isPermaLink="false">tag:boldr.net,2009:mvc-stack-node-js-ejsgi-scylla-mustache</guid><description>&lt;!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN" "http://www.w3.org/TR/REC-html40/loose.dtd"&gt;
&lt;html&gt;&lt;body&gt;
&lt;p&gt;When you start a new technology you may be lost. Node.js is young (1 year today) but there's already a &lt;a href="http://wiki.github.com/ry/node/modules#web-frameworks-full"&gt;bunch of frameworks&lt;/a&gt; (express seems to be the most advanced) and a &lt;a href="http://wiki.github.com/ry/node/modules"&gt;&lt;strong&gt;lot&lt;/strong&gt; of modules&lt;/a&gt;. The best way to begin is to explore the ecosystem. To do that, let's create our own Model-View-Controller stack.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;NOTE&lt;/strong&gt;: The whole example is &lt;a href="http://github.com/nmerouze/node-examples"&gt;available on GitHub&lt;/a&gt; and not all the code is in the article, so be sure to check it out.&lt;/p&gt;

&lt;h2&gt;Models&lt;/h2&gt;

&lt;p&gt;There's a lot of &lt;a href="http://wiki.github.com/ry/node/modules#database"&gt;modules for databases&lt;/a&gt; and some of them have ORMs. We can use Redis, MongoDB, CouchDB, Tokyo Cabinet, Postgres, Sqlite 3, Riak... For this example we're going to use &lt;a href="http://github.com/felixge/node-dirty"&gt;node-dirty&lt;/a&gt; which do not require any database installation. node-dirty is a fast JSON store and it's easy to use, have a look at the following code:&lt;/p&gt;

&lt;div class="highlight"&gt;
&lt;pre&gt;&lt;span class="cm"&gt;/* Requirements */&lt;/span&gt;
&lt;span class="nx"&gt;require&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;paths&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;unshift&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;"./vendor/node-dirty/lib"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="kd"&gt;var&lt;/span&gt; &lt;span class="nx"&gt;sys&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;require&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;"sys"&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
    &lt;span class="nx"&gt;Dirty&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;require&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;"dirty"&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nx"&gt;Dirty&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="cm"&gt;/* Model */&lt;/span&gt;
&lt;span class="kd"&gt;var&lt;/span&gt; &lt;span class="nx"&gt;posts&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nx"&gt;Dirty&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;"posts.dirty"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;flushInterval&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;10&lt;/span&gt; &lt;span class="p"&gt;});&lt;/span&gt;

&lt;span class="cm"&gt;/* Persistence */&lt;/span&gt;
&lt;span class="kd"&gt;var&lt;/span&gt; &lt;span class="nx"&gt;post&lt;/span&gt;  &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;title&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"Awesome post"&lt;/span&gt; &lt;span class="p"&gt;};&lt;/span&gt;
&lt;span class="nx"&gt;posts&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;add&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;post&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="cm"&gt;/* Querying */&lt;/span&gt;
&lt;span class="nx"&gt;sys&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;puts&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;posts&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;post&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;_id&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nx"&gt;title&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/pre&gt;
&lt;/div&gt;


&lt;h2&gt;Views&lt;/h2&gt;

&lt;p&gt;Now we have data we will need views to display them. I've already written something about &lt;a href="http://boldr.net/create-a-web-app-with-node"&gt;Mustache.js&lt;/a&gt; and for this example I'm gonna use it too but there are other &lt;a href="http://wiki.github.com/ry/node/modules#templating"&gt;modules available for templating&lt;/a&gt;. You can use EJS, HAML, Sass (for css only)...&lt;/p&gt;

&lt;p&gt;To use &lt;a href="http://github.com/janl/mustache.js"&gt;Mustache.js&lt;/a&gt;, don't forget to use the one from the commonjs branch (master will not work with Node.js). There's another implementation faster and with built-in async named &lt;a href="http://github.com/raycmorgan/Mu"&gt;Mu&lt;/a&gt;.&lt;/p&gt;

&lt;h2&gt;Controllers&lt;/h2&gt;

&lt;p&gt;The important part here is not the controller itself because it's just a function which will be called by the &lt;strong&gt;router&lt;/strong&gt;. There are &lt;a href="http://wiki.github.com/ry/node/modules#web-frameworks-routers"&gt;some routers&lt;/a&gt; but the most interesting is &lt;a href="http://github.com/ithinkihaveacat/node-scylla"&gt;Scylla&lt;/a&gt; mainly because it uses &lt;a href="http://github.com/isaacs/ejsgi"&gt;EJSGI&lt;/a&gt; (an async Rack-like interface for Javascript).&lt;/p&gt;

&lt;p&gt;With Scylla we're defining the route with a string containing the HTTP verb and the pattern. Then we bind a function to it.&lt;/p&gt;

&lt;div class="highlight"&gt;
&lt;pre&gt;&lt;span class="cm"&gt;/* Requirements */&lt;/span&gt;
&lt;span class="nx"&gt;require&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;paths&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;unshift&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;"./vendor/ejsgi/lib"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="nx"&gt;require&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;paths&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;unshift&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;"./vendor/node-scylla/lib"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="kd"&gt;var&lt;/span&gt; &lt;span class="nx"&gt;scylla&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;require&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;"scylla"&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
    &lt;span class="nx"&gt;ejsgi&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;require&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;"ejsgi"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="cm"&gt;/* Controller */&lt;/span&gt;
&lt;span class="kd"&gt;var&lt;/span&gt; &lt;span class="nx"&gt;HomeController&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;index&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;req&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="kd"&gt;var&lt;/span&gt; &lt;span class="nx"&gt;body&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"Hello World!"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

    &lt;span class="kd"&gt;var&lt;/span&gt; &lt;span class="nx"&gt;res&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="nx"&gt;body&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nx"&gt;req&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;jsgi&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;stream&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt;
      &lt;span class="nx"&gt;status&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;200&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="nx"&gt;headers&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="s2"&gt;"content-type"&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"text/html"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="s2"&gt;"content-length"&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;body&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;length&lt;/span&gt;
      &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="p"&gt;};&lt;/span&gt;
    &lt;span class="nx"&gt;res&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;body&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;write&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;body&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="nx"&gt;res&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;body&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;close&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;

    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;res&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;};&lt;/span&gt;

&lt;span class="cm"&gt;/* Router */&lt;/span&gt;
&lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nx"&gt;Router&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;scylla&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;Base&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;call&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="nx"&gt;Router&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;prototype&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;Object&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;create&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;scylla&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;Base&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;prototype&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="nx"&gt;process&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;mixin&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;Router&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;prototype&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="s2"&gt;"GET /"&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;HomeController&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;index&lt;/span&gt; &lt;span class="p"&gt;});&lt;/span&gt;

&lt;span class="cm"&gt;/* Server */&lt;/span&gt;
&lt;span class="nx"&gt;ejsgi&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;Server&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nx"&gt;Router&lt;/span&gt;&lt;span class="p"&gt;().&lt;/span&gt;&lt;span class="nx"&gt;adapter&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;"ejsgi"&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="s2"&gt;"localhost"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;8000&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nx"&gt;start&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;/pre&gt;
&lt;/div&gt;


&lt;p&gt;&lt;strong&gt;NOTE&lt;/strong&gt;: I included the router stuff in the controller part because most of the code is in the controller.&lt;/p&gt;

&lt;h2&gt;Conclusion&lt;/h2&gt;

&lt;p&gt;As we've seen there are a lot of modules for each part of the MVC stack. It's kinda difficult to choose but it's mainly about what you like (which database you want to use, which templating engine you like and which is the most powerful router).&lt;/p&gt;

&lt;p&gt;Most frameworks for Node.js are not fully agnostic (nor fully opiniated) yet so it can be a dealbreaker if you like things that ain't in a framework you want to use. If you're like this you can comment the article and write about your preferences. It will maybe ease the choice for newcomers in the Node.s world.&lt;/p&gt;
&lt;/body&gt;&lt;/html&gt;&lt;div class="feedflare"&gt;
&lt;a href="http://feeds.feedburner.com/~ff/fr/boldr?a=48UGKXdse3A:wf_W6Up33D8:7Q72WNTAKBA"&gt;&lt;img src="http://feeds.feedburner.com/~ff/fr/boldr?d=7Q72WNTAKBA" border="0"&gt;&lt;/img&gt;&lt;/a&gt;
&lt;/div&gt;&lt;img src="http://feeds.feedburner.com/~r/fr/boldr/~4/48UGKXdse3A" height="1" width="1"/&gt;</description><feedburner:origLink>http://boldr.net/mvc-stack-node-js-ejsgi-scylla-mustache</feedburner:origLink></item><item><title>Design refresh thanks to CSS3</title><link>http://feedproxy.google.com/~r/fr/boldr/~3/2DGiGovUfPM/new-design-css3-font-face-webkit-transition</link><dc:creator xmlns:dc="http://purl.org/dc/elements/1.1/">Nicolas Mérouze</dc:creator><pubDate>Tue, 09 Feb 2010 16:00:00 PST</pubDate><guid isPermaLink="false">tag:boldr.net,2009:new-design-css3-font-face-webkit-transition</guid><description>&lt;!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN" "http://www.w3.org/TR/REC-html40/loose.dtd"&gt;
&lt;html&gt;&lt;body&gt;
&lt;p&gt;I have talked about it on my &lt;a href="http://twitter.net/nmerouze"&gt;twitter account&lt;/a&gt; it's now there. Here's a small design refresh for Boldr. The HTML code has not changed it's just a new CSS. I was using HTML5 since the beginning. Meanwhile, I was not using CSS3 at all. I decided to do something and now I'm using &lt;code&gt;@font-face&lt;/code&gt; for the titles, &lt;code&gt;@font-face&lt;/code&gt; and rounded corners for the logo and CSS Animations for the links. There will be more details in a series of CSS3 articles I'll be writing soon.&lt;/p&gt;

&lt;p&gt;And the browser compatibility is pretty large:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;@font-face&lt;/code&gt; and rounded corners should be OK with the majority of the browsers (including IE6, 7 and 8).&lt;/li&gt;
&lt;li&gt;CSS Animations should be OK with Safari/Chrome/WebKit and Firefox nightlies.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;It's awesome to see that you can do a bit of CSS3 even with Internet Explorer and I will do more and more CSS3 in the next iterations.&lt;/p&gt;

&lt;p&gt;I added Intense Debate to the articles, so if you have any suggestions or comments about the design you can tell me this here.&lt;/p&gt;
&lt;/body&gt;&lt;/html&gt;&lt;div class="feedflare"&gt;
&lt;a href="http://feeds.feedburner.com/~ff/fr/boldr?a=2DGiGovUfPM:cqi58cUx8F4:7Q72WNTAKBA"&gt;&lt;img src="http://feeds.feedburner.com/~ff/fr/boldr?d=7Q72WNTAKBA" border="0"&gt;&lt;/img&gt;&lt;/a&gt;
&lt;/div&gt;&lt;img src="http://feeds.feedburner.com/~r/fr/boldr/~4/2DGiGovUfPM" height="1" width="1"/&gt;</description><feedburner:origLink>http://boldr.net/new-design-css3-font-face-webkit-transition</feedburner:origLink></item><item><title>How to upgrade plugins to Rails 3.0</title><link>http://feedproxy.google.com/~r/fr/boldr/~3/cA_FVCY6h6c/upgrade-plugins-gems-rails-3</link><dc:creator xmlns:dc="http://purl.org/dc/elements/1.1/">Nicolas Mérouze</dc:creator><pubDate>Thu, 04 Feb 2010 16:00:00 PST</pubDate><guid isPermaLink="false">tag:boldr.net,2009:upgrade-plugins-gems-rails-3</guid><description>&lt;!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN" "http://www.w3.org/TR/REC-html40/loose.dtd"&gt;
&lt;html&gt;&lt;body&gt;
&lt;p&gt;Rails 3.0 beta is out and it's now time to upgrade all the plugins available. To show you how to do it I've decided to create a small plugin compatible with Rails 2.x and Rails 3.0. It's a wrapper around &lt;a href="http://github.com/rtomayko/rack-cache"&gt;Rack::Cache&lt;/a&gt; to insert it automatically in a Rails application.&lt;/p&gt;

&lt;h2&gt;It's a gem&lt;/h2&gt;

&lt;p&gt;First things first we create the gem we will name rails-cache. Thanks to Jeweler it's dead easy. Just a few lines of code in a Rakefile and it's done:&lt;/p&gt;

&lt;div class="highlight"&gt;
&lt;pre&gt;&lt;span class="nb"&gt;require&lt;/span&gt; &lt;span class="s2"&gt;"rake"&lt;/span&gt;

&lt;span class="k"&gt;begin&lt;/span&gt;
  &lt;span class="nb"&gt;require&lt;/span&gt; &lt;span class="s2"&gt;"jeweler"&lt;/span&gt;
  &lt;span class="no"&gt;Jeweler&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;Tasks&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;new&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt;&lt;span class="n"&gt;gem&lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt;
    &lt;span class="n"&gt;gem&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;name&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"rails-cache"&lt;/span&gt;
    &lt;span class="n"&gt;gem&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;summary&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"Add Rack::Cache to your Rails app"&lt;/span&gt;
    &lt;span class="n"&gt;gem&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;email&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"nicolas.merouze@gmail.com"&lt;/span&gt;
    &lt;span class="n"&gt;gem&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;homepage&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"http://github.com/nmerouze/rails-cache"&lt;/span&gt;
    &lt;span class="n"&gt;gem&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;authors&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="o"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;"Nicolas MÃÂ©rouze"&lt;/span&gt;&lt;span class="o"&gt;]&lt;/span&gt;
    &lt;span class="n"&gt;gem&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;files&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="no"&gt;Dir&lt;/span&gt;&lt;span class="o"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;"*"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;"{lib}/**/*"&lt;/span&gt;&lt;span class="o"&gt;]&lt;/span&gt;
    &lt;span class="n"&gt;gem&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;add_dependency&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;"rack-cache"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;"~&amp;gt; 0.5.2"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="k"&gt;end&lt;/span&gt;

  &lt;span class="no"&gt;Jeweler&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;GemcutterTasks&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;new&lt;/span&gt;
&lt;span class="k"&gt;rescue&lt;/span&gt; &lt;span class="no"&gt;LoadError&lt;/span&gt;
  &lt;span class="nb"&gt;puts&lt;/span&gt; &lt;span class="s2"&gt;"Jeweler (or a dependency) not available. Install it with: sudo gem install jeweler"&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;
&lt;/pre&gt;
&lt;/div&gt;


&lt;p&gt;To create the VERSION file a &lt;code&gt;echo "1.0.0" &amp;gt; VERSION&lt;/code&gt; is enough.&lt;/p&gt;

&lt;h2&gt;Rails 2.x way&lt;/h2&gt;

&lt;p&gt;The code of the gem will be in &lt;code&gt;lib/rails/cache.rb&lt;/code&gt; and it's just the insertion of the Rack::Cache middleware:&lt;/p&gt;

&lt;div class="highlight"&gt;
&lt;pre&gt;&lt;span class="nb"&gt;require&lt;/span&gt; &lt;span class="s2"&gt;"rack/cache"&lt;/span&gt;

&lt;span class="no"&gt;Rails&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;configuration&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;middleware&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;use&lt;/span&gt; &lt;span class="no"&gt;Rack&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;Cache&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; 
  &lt;span class="ss"&gt;:metastore&lt;/span&gt;   &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="s2"&gt;"file:&lt;/span&gt;&lt;span class="si"&gt;#{&lt;/span&gt;&lt;span class="no"&gt;Rails&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;root&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="s2"&gt;"cache/rack/meta"&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="ss"&gt;:entitystore&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="s2"&gt;"file:&lt;/span&gt;&lt;span class="si"&gt;#{&lt;/span&gt;&lt;span class="no"&gt;Rails&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;root&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="s2"&gt;"cache/rack/body"&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="ss"&gt;:verbose&lt;/span&gt;     &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="kp"&gt;true&lt;/span&gt;
&lt;/pre&gt;
&lt;/div&gt;


&lt;p&gt;We build the gem with &lt;code&gt;rake build&lt;/code&gt; and we install it with &lt;code&gt;gem install --local pkg/rails-cache-1.0.0.gem&lt;/code&gt;. Then we need to load it in our environment.rb:&lt;/p&gt;

&lt;div class="highlight"&gt;
&lt;pre&gt;&lt;span class="no"&gt;RAILS_GEM_VERSION&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s1"&gt;'2.3.5'&lt;/span&gt; &lt;span class="k"&gt;unless&lt;/span&gt; &lt;span class="n"&gt;defined?&lt;/span&gt; &lt;span class="no"&gt;RAILS_GEM_VERSION&lt;/span&gt;
&lt;span class="nb"&gt;require&lt;/span&gt; &lt;span class="no"&gt;File&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;join&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="no"&gt;File&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;dirname&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="bp"&gt;__FILE__&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="s1"&gt;'boot'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="no"&gt;Rails&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;Initializer&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;run&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt;&lt;span class="n"&gt;config&lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt;
  &lt;span class="n"&gt;config&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;gem&lt;/span&gt; &lt;span class="s2"&gt;"rails-cache"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;:lib&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="s2"&gt;"rails/cache"&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;
&lt;/pre&gt;
&lt;/div&gt;


&lt;p&gt;Rack::Cache is now plugged with the Rails application!&lt;/p&gt;

&lt;h2&gt;The new way&lt;/h2&gt;

&lt;p&gt;Rails 3.0 comes with Bundler. A cool thing with Bundler is that you can load a gem which have a custom path:&lt;/p&gt;

&lt;div class="highlight"&gt;
&lt;pre&gt;&lt;span class="n"&gt;gem&lt;/span&gt; &lt;span class="s2"&gt;"rails-cache"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;:require&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="s2"&gt;"rails/cache"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;:path&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="s2"&gt;"../rails-cache"&lt;/span&gt;
&lt;/pre&gt;
&lt;/div&gt;


&lt;p&gt;The &lt;code&gt;:path&lt;/code&gt; option is the path to our gem. We have a lot of solution to do the same in Rails 2.x, with a plugin instead of a gem for example, but it is not as elegant as with Bundler (Bundler is usable with Rails 2.x).&lt;/p&gt;

&lt;p&gt;To create a plugin for Rails there's an object for that now, &lt;code&gt;Rails::Railtie&lt;/code&gt;. So we just need to write an object which inherit from &lt;code&gt;Rails::Railtie&lt;/code&gt; in &lt;code&gt;lib/rails/cache.rb&lt;/code&gt;:&lt;/p&gt;

&lt;div class="highlight"&gt;
&lt;pre&gt;&lt;span class="nb"&gt;require&lt;/span&gt; &lt;span class="s2"&gt;"rack/cache"&lt;/span&gt;

&lt;span class="k"&gt;module&lt;/span&gt; &lt;span class="nn"&gt;Cache&lt;/span&gt;
  &lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;Railtie&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="no"&gt;Rails&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;Railtie&lt;/span&gt;
    &lt;span class="n"&gt;railtie_name&lt;/span&gt; &lt;span class="ss"&gt;:rails_cache&lt;/span&gt;

    &lt;span class="n"&gt;initializer&lt;/span&gt; &lt;span class="s2"&gt;"rails_cache.insert_rack_cache"&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt;&lt;span class="n"&gt;app&lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt;
      &lt;span class="n"&gt;app&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;config&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;middleware&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;use&lt;/span&gt; &lt;span class="no"&gt;Rack&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;Cache&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="ss"&gt;:metastore&lt;/span&gt;   &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="s2"&gt;"file:&lt;/span&gt;&lt;span class="si"&gt;#{&lt;/span&gt;&lt;span class="no"&gt;Rails&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;root&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="s2"&gt;"cache/rack/meta"&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="ss"&gt;:entitystore&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="s2"&gt;"file:&lt;/span&gt;&lt;span class="si"&gt;#{&lt;/span&gt;&lt;span class="no"&gt;Rails&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;root&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="s2"&gt;"cache/rack/body"&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="ss"&gt;:verbose&lt;/span&gt;     &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="kp"&gt;true&lt;/span&gt;
    &lt;span class="k"&gt;end&lt;/span&gt;
  &lt;span class="k"&gt;end&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;
&lt;/pre&gt;
&lt;/div&gt;


&lt;p&gt;The gem has been upgraded to Rails 3.0!&lt;/p&gt;

&lt;h2&gt;Rails 2.x and 3.0 in a gem&lt;/h2&gt;

&lt;p&gt;This is just an example here, but it's possible there's a better solution:&lt;/p&gt;

&lt;div class="highlight"&gt;
&lt;pre&gt;&lt;span class="nb"&gt;require&lt;/span&gt; &lt;span class="s2"&gt;"rack/cache"&lt;/span&gt;

&lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="no"&gt;Rails&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;VERSION&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;MAJOR&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt;
  &lt;span class="no"&gt;Rails&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;configuration&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;middleware&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;use&lt;/span&gt; &lt;span class="no"&gt;Rack&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;Cache&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; 
    &lt;span class="ss"&gt;:metastore&lt;/span&gt;   &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="s2"&gt;"file:&lt;/span&gt;&lt;span class="si"&gt;#{&lt;/span&gt;&lt;span class="no"&gt;Rails&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;root&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="s2"&gt;"cache/rack/meta"&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="ss"&gt;:entitystore&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="s2"&gt;"file:&lt;/span&gt;&lt;span class="si"&gt;#{&lt;/span&gt;&lt;span class="no"&gt;Rails&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;root&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="s2"&gt;"cache/rack/body"&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="ss"&gt;:verbose&lt;/span&gt;     &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="kp"&gt;true&lt;/span&gt;
&lt;span class="k"&gt;else&lt;/span&gt;
  &lt;span class="k"&gt;module&lt;/span&gt; &lt;span class="nn"&gt;Cache&lt;/span&gt;
    &lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;Railtie&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="no"&gt;Rails&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;Railtie&lt;/span&gt;
      &lt;span class="n"&gt;railtie_name&lt;/span&gt; &lt;span class="ss"&gt;:rails_cache&lt;/span&gt;

      &lt;span class="n"&gt;initializer&lt;/span&gt; &lt;span class="s2"&gt;"rails_cache.insert_rack_cache"&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt;&lt;span class="n"&gt;app&lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt;
        &lt;span class="n"&gt;app&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;config&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;middleware&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;use&lt;/span&gt; &lt;span class="no"&gt;Rack&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;Cache&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
          &lt;span class="ss"&gt;:metastore&lt;/span&gt;   &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="s2"&gt;"file:&lt;/span&gt;&lt;span class="si"&gt;#{&lt;/span&gt;&lt;span class="no"&gt;Rails&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;root&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="s2"&gt;"cache/rack/meta"&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
          &lt;span class="ss"&gt;:entitystore&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="s2"&gt;"file:&lt;/span&gt;&lt;span class="si"&gt;#{&lt;/span&gt;&lt;span class="no"&gt;Rails&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;root&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="s2"&gt;"cache/rack/body"&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
          &lt;span class="ss"&gt;:verbose&lt;/span&gt;     &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="kp"&gt;true&lt;/span&gt;
      &lt;span class="k"&gt;end&lt;/span&gt;
    &lt;span class="k"&gt;end&lt;/span&gt;
  &lt;span class="k"&gt;end&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;
&lt;/pre&gt;
&lt;/div&gt;


&lt;p&gt;And you can find the &lt;a href="http://github.com/nmerouze/rails-cache"&gt;whole code on GitHub&lt;/a&gt;.&lt;/p&gt;

&lt;h2&gt;More about Plugins/Engines&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;José Valim has written a &lt;a href="https://gist.github.com/e139fa787aa882c0aa9c"&gt;draft documentation&lt;/a&gt; about Rails::Railtie, Rails::Engine and Rails::Application.&lt;/li&gt;
&lt;li&gt;Yehuda Katz has made a &lt;a href="http://www.engineyard.com/blog/2010/rails-and-merb-merge-rails-core-part-4-of-6/"&gt;blog post about it&lt;/a&gt;.&lt;/li&gt;
&lt;li&gt;
&lt;a href="http://github.com/dkubb/rails3_datamapper"&gt;rails3_datamapper&lt;/a&gt; is a good example to see how to write a Rails 3.0 plugin.&lt;/li&gt;
&lt;li&gt;The latest commits for &lt;a href="http://github.com/jnicklas/carrierwave"&gt;CarrierWave&lt;/a&gt; have been made to upgrade it to Rails 3.0.&lt;/li&gt;
&lt;/ul&gt;
&lt;/body&gt;&lt;/html&gt;&lt;div class="feedflare"&gt;
&lt;a href="http://feeds.feedburner.com/~ff/fr/boldr?a=cA_FVCY6h6c:PnT41qfTYTw:7Q72WNTAKBA"&gt;&lt;img src="http://feeds.feedburner.com/~ff/fr/boldr?d=7Q72WNTAKBA" border="0"&gt;&lt;/img&gt;&lt;/a&gt;
&lt;/div&gt;&lt;img src="http://feeds.feedburner.com/~r/fr/boldr/~4/cA_FVCY6h6c" height="1" width="1"/&gt;</description><feedburner:origLink>http://boldr.net/upgrade-plugins-gems-rails-3</feedburner:origLink></item><item><title>Real World Node.js - Redis and streaming</title><link>http://feedproxy.google.com/~r/fr/boldr/~3/qu_1N8wIXhk/real-world-node-js-redis-html5-websockets-streaming</link><dc:creator xmlns:dc="http://purl.org/dc/elements/1.1/">Nicolas Mérouze</dc:creator><pubDate>Tue, 02 Feb 2010 16:00:00 PST</pubDate><guid isPermaLink="false">tag:boldr.net,2009:real-world-node-js-redis-html5-websockets-streaming</guid><description>&lt;!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN" "http://www.w3.org/TR/REC-html40/loose.dtd"&gt;
&lt;html&gt;&lt;body&gt;
&lt;p&gt;Non-blocking async I/O frameworks are the new hotness, I've already said it. But aside the buzzwords, what can we do with these frameworks? Here's a few examples using Node.js.&lt;/p&gt;

&lt;h2&gt;Track users with Redis&lt;/h2&gt;

&lt;p&gt;&lt;a href="http://techno-weenie.net"&gt;Rick Olson&lt;/a&gt; has developed a library to &lt;a href="http://github.com/technoweenie/wheres-waldo"&gt;track what users are on which pages&lt;/a&gt; using &lt;a href="http://code.google.com/p/redis/"&gt;Redis&lt;/a&gt; to store the data.&lt;/p&gt;

&lt;p&gt;&lt;img src="http://img.skitch.com/20100118-py3rmqkfw51d4ra6im7ump6wyk.png" alt="Track users with Redis and Node.js"&gt;&lt;/p&gt;

&lt;p&gt;Now you can track users in real-time! It's a neat feature to know who is editing a wiki page at the same time as you but real-time generates a lot of requests. If your server blocks the process for each request your server is gonna be stuck quickly.&lt;/p&gt;

&lt;p&gt;Thanks to Node.js and the async &lt;a href="http://github.com/fictorial/redis-node-client"&gt;Redis client&lt;/a&gt; your server will be in better shape.&lt;/p&gt;

&lt;h2&gt;Streaming&lt;/h2&gt;

&lt;p&gt;Remember my article about &lt;a href="http://boldr.net/html5-websockets-cramp"&gt;Twitter Stream with Websockets&lt;/a&gt;? There's the same &lt;a href="http://blog.andregoncalves.com/2009/12/29/Nodejs-twitter-streaming-with-html5-websockets.html"&gt;here but with Node.js&lt;/a&gt;. As you can see the RabbitMQ part is not present because it is not really necessary with a non-blocking framework. But having a queue is recommended at some point (generally when you use more than one process).&lt;/p&gt;

&lt;p&gt;Streaming with a classic web framework is hard to code (= to avoid blocking the process). With Node.js you almost don't realize that you stream data!&lt;/p&gt;

&lt;p&gt;Here's some useful code about streaming with Node.js:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;In addition to the tracker with Redis, Rick Olson has also developed a &lt;a href="http://github.com/technoweenie/twitter-node"&gt;Twitter Stream&lt;/a&gt; library for Node.js.&lt;/li&gt;
&lt;li&gt;You can &lt;a href="http://debuggable.com/posts/streaming-file-uploads-with-node-js:4ac094b2-b6c8-4a7f-bd07-28accbdd56cb"&gt;stream file uploads&lt;/a&gt;.&lt;/li&gt;
&lt;li&gt;In fact New Bamboo has a long &lt;a href="http://blog.new-bamboo.co.uk/2009/12/7/real-time-online-activity-monitor-example-with-node-js-and-websocket"&gt;article about different streaming techniques with Node.js&lt;/a&gt;.&lt;/li&gt;
&lt;li&gt;If you want to use Comet with less than 10 lines of code, &lt;a href="http://blog.jcoglan.com/2010/02/02/faye-a-comet-client-and-server-for-node-js-and-rack/"&gt;Faye is for you&lt;/a&gt;.&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;There's more&lt;/h2&gt;

&lt;p&gt;Don't forget to check out the &lt;a href="http://wiki.github.com/ry/node/"&gt;Node.js wiki&lt;/a&gt; to have a longer list of applications.&lt;/p&gt;
&lt;/body&gt;&lt;/html&gt;&lt;div class="feedflare"&gt;
&lt;a href="http://feeds.feedburner.com/~ff/fr/boldr?a=qu_1N8wIXhk:h-t0qT5aU50:7Q72WNTAKBA"&gt;&lt;img src="http://feeds.feedburner.com/~ff/fr/boldr?d=7Q72WNTAKBA" border="0"&gt;&lt;/img&gt;&lt;/a&gt;
&lt;/div&gt;&lt;img src="http://feeds.feedburner.com/~r/fr/boldr/~4/qu_1N8wIXhk" height="1" width="1"/&gt;</description><feedburner:origLink>http://boldr.net/real-world-node-js-redis-html5-websockets-streaming</feedburner:origLink></item><item><title>Links of link lists about Rails 3</title><link>http://feedproxy.google.com/~r/fr/boldr/~3/TEnLAEXIJig/rails-3-articles</link><dc:creator xmlns:dc="http://purl.org/dc/elements/1.1/">Nicolas Mérouze</dc:creator><pubDate>Mon, 01 Feb 2010 16:00:00 PST</pubDate><guid isPermaLink="false">tag:boldr.net,2009:rails-3-articles</guid><description>&lt;!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN" "http://www.w3.org/TR/REC-html40/loose.dtd"&gt;
&lt;html&gt;&lt;body&gt;
&lt;p&gt;There's a lot of articles about Rails 3 out there and it's difficult to keep the list up to date. But here's 3 pages full of links about the Rails 3 :&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;a href="http://mediumexposure.com/rails-3-reading-material/"&gt;Rails 3 reading material&lt;/a&gt; by Medium Exposure&lt;/li&gt;
&lt;li&gt;
&lt;a href="http://railslove.com/weblog/2010/02/02/on-the-way-to-rails-3-a-link-list/"&gt;On the way to Rails 3 - a link list&lt;/a&gt; by Rails Love&lt;/li&gt;
&lt;li&gt;
&lt;a href="http://guides.rails.info/3_0_release_notes.html"&gt;Rails 3 release notes&lt;/a&gt;. There's a bunch of links with the very useful release notes.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;These lists are already obsolete because José Valim has just published another article about &lt;a href="http://blog.plataformatec.com.br/2010/02/rails-3-i18n-changes/"&gt;Rails 3 I18n changes&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Rails 3 beta/RC? should be out today or tomorrow but you have a lot to read now ;)&lt;/p&gt;
&lt;/body&gt;&lt;/html&gt;&lt;div class="feedflare"&gt;
&lt;a href="http://feeds.feedburner.com/~ff/fr/boldr?a=TEnLAEXIJig:SVzc6wsNk7c:7Q72WNTAKBA"&gt;&lt;img src="http://feeds.feedburner.com/~ff/fr/boldr?d=7Q72WNTAKBA" border="0"&gt;&lt;/img&gt;&lt;/a&gt;
&lt;/div&gt;&lt;img src="http://feeds.feedburner.com/~r/fr/boldr/~4/TEnLAEXIJig" height="1" width="1"/&gt;</description><feedburner:origLink>http://boldr.net/rails-3-articles</feedburner:origLink></item><item><title>Twitter/HTML5 Websockets with Cramp</title><link>http://feedproxy.google.com/~r/fr/boldr/~3/_jMZozaWoIQ/html5-websockets-cramp</link><dc:creator xmlns:dc="http://purl.org/dc/elements/1.1/">Nicolas Mérouze</dc:creator><pubDate>Mon, 18 Jan 2010 16:00:00 PST</pubDate><guid isPermaLink="false">tag:boldr.net,2009:html5-websockets-cramp</guid><description>&lt;!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN" "http://www.w3.org/TR/REC-html40/loose.dtd"&gt;
&lt;html&gt;&lt;body&gt;
&lt;p&gt;After &lt;a href="/faster-ajax-html5-localstorage"&gt;HTML5 Web Storage&lt;/a&gt;, I'm gonna show a use case of &lt;a href="http://dev.w3.org/html5/websockets"&gt;HTML5 Websockets&lt;/a&gt;. It's quickly becoming a hype solution for bidirectional communication between client and server (think TCP for browsers). Hype but not really usable in production because of the compatibility with the browsers.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Compatibility of Websockets&lt;/strong&gt; (as of January 2010) : nightly builds of WebKit and Chrome.&lt;/p&gt;

&lt;p&gt;Because I'm lazy and there are some examples out there I decided to convert an &lt;a href="http://github.com/rubenfonseca/twitter-amqp-websocket-example"&gt;existing Twitter Stream example&lt;/a&gt; to &lt;a href="http://m.onkey.org/2010/1/7/introducing-cramp"&gt;Cramp&lt;/a&gt; (a new Ruby async framework by @lifo from the Rails Core team). So first read the &lt;a href="http://blog.0x82.com/2009/12/28/twitter-amqp-websocket-example-no-polling"&gt;article from the author of the initial example&lt;/a&gt;. Finish? Now we can convert it to Cramp.&lt;/p&gt;

&lt;h2&gt;Cramp for Websockets&lt;/h2&gt;

&lt;p&gt;Lifo &lt;a href="http://m.onkey.org/2010/1/15/websockets-made-easy-with-cramp"&gt;introduced it on his blog&lt;/a&gt;. A few lines later we have the application configured for Cramp instead of EventMachine in &lt;code&gt;socket.rb&lt;/code&gt;:&lt;/p&gt;

&lt;div class="highlight"&gt;
&lt;pre&gt;&lt;span class="nb"&gt;require&lt;/span&gt; &lt;span class="s1"&gt;'vendor/gems/environment'&lt;/span&gt;
&lt;span class="nb"&gt;require&lt;/span&gt; &lt;span class="s1"&gt;'cramp/controller'&lt;/span&gt;
&lt;span class="nb"&gt;require&lt;/span&gt; &lt;span class="s1"&gt;'uuid'&lt;/span&gt;
&lt;span class="nb"&gt;require&lt;/span&gt; &lt;span class="s1"&gt;'mq'&lt;/span&gt;

&lt;span class="no"&gt;Cramp&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;Controller&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;Websocket&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;backend&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="ss"&gt;:thin&lt;/span&gt;

&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;TweetsController&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="no"&gt;Cramp&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;Controller&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;Websocket&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;

&lt;span class="no"&gt;Rack&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;Handler&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;Thin&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;run&lt;/span&gt; &lt;span class="no"&gt;TweetsController&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;:Port&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="mi"&gt;3000&lt;/span&gt;
&lt;/pre&gt;
&lt;/div&gt;


&lt;h2&gt;on_start&lt;/h2&gt;

&lt;p&gt;Now we want to convert the open event to Cramp. For that, Cramp has a &lt;code&gt;on_start&lt;/code&gt; method to call the method executed for this event. So the open event will be like that: &lt;/p&gt;

&lt;div class="highlight"&gt;
&lt;pre&gt;&lt;span class="nb"&gt;require&lt;/span&gt; &lt;span class="s1"&gt;'vendor/gems/environment'&lt;/span&gt;
&lt;span class="nb"&gt;require&lt;/span&gt; &lt;span class="s1"&gt;'cramp/controller'&lt;/span&gt;
&lt;span class="nb"&gt;require&lt;/span&gt; &lt;span class="s1"&gt;'uuid'&lt;/span&gt;
&lt;span class="nb"&gt;require&lt;/span&gt; &lt;span class="s1"&gt;'mq'&lt;/span&gt;

&lt;span class="no"&gt;Cramp&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;Controller&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;Websocket&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;backend&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="ss"&gt;:thin&lt;/span&gt;

&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;TweetsController&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="no"&gt;Cramp&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;Controller&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;Websocket&lt;/span&gt;
  &lt;span class="vc"&gt;@@uuid&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="no"&gt;UUID&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;new&lt;/span&gt;

  &lt;span class="n"&gt;on_start&lt;/span&gt; &lt;span class="ss"&gt;:display&lt;/span&gt;

  &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;display&lt;/span&gt;
    &lt;span class="nb"&gt;puts&lt;/span&gt; &lt;span class="s2"&gt;"WebSocket opened"&lt;/span&gt;

    &lt;span class="n"&gt;twitter&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="no"&gt;MQ&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;new&lt;/span&gt;
    &lt;span class="n"&gt;twitter&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;queue&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="vc"&gt;@@uuid&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;generate&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;bind&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;twitter&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;fanout&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'twitter'&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;subscribe&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt;&lt;span class="n"&gt;t&lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt;
      &lt;span class="n"&gt;render&lt;/span&gt; &lt;span class="n"&gt;t&lt;/span&gt;
    &lt;span class="k"&gt;end&lt;/span&gt;
  &lt;span class="k"&gt;end&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;

&lt;span class="no"&gt;Rack&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;Handler&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;Thin&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;run&lt;/span&gt; &lt;span class="no"&gt;TweetsController&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;:Port&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="mi"&gt;3000&lt;/span&gt;
&lt;/pre&gt;
&lt;/div&gt;


&lt;p&gt;To send a message to the user, Cramp has a &lt;code&gt;render&lt;/code&gt; method.&lt;/p&gt;

&lt;h2&gt;on_finish&lt;/h2&gt;

&lt;p&gt;When the user ends the execution of the socket, there's a close event. When this event is fired, the method passed to &lt;code&gt;on_finish&lt;/code&gt; will be executed.&lt;/p&gt;

&lt;div class="highlight"&gt;
&lt;pre&gt;&lt;span class="nb"&gt;require&lt;/span&gt; &lt;span class="s1"&gt;'vendor/gems/environment'&lt;/span&gt;
&lt;span class="nb"&gt;require&lt;/span&gt; &lt;span class="s1"&gt;'cramp/controller'&lt;/span&gt;
&lt;span class="nb"&gt;require&lt;/span&gt; &lt;span class="s1"&gt;'uuid'&lt;/span&gt;
&lt;span class="nb"&gt;require&lt;/span&gt; &lt;span class="s1"&gt;'mq'&lt;/span&gt;

&lt;span class="no"&gt;Cramp&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;Controller&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;Websocket&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;backend&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="ss"&gt;:thin&lt;/span&gt;

&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;TweetsController&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="no"&gt;Cramp&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;Controller&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;Websocket&lt;/span&gt;
  &lt;span class="vc"&gt;@@uuid&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="no"&gt;UUID&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;new&lt;/span&gt;

  &lt;span class="n"&gt;on_start&lt;/span&gt; &lt;span class="ss"&gt;:display&lt;/span&gt;
  &lt;span class="n"&gt;on_finish&lt;/span&gt; &lt;span class="ss"&gt;:close&lt;/span&gt;

  &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;display&lt;/span&gt;
    &lt;span class="nb"&gt;puts&lt;/span&gt; &lt;span class="s2"&gt;"WebSocket opened"&lt;/span&gt;

    &lt;span class="n"&gt;twitter&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="no"&gt;MQ&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;new&lt;/span&gt;
    &lt;span class="n"&gt;twitter&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;queue&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="vc"&gt;@@uuid&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;generate&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;bind&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;twitter&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;fanout&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'twitter'&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;subscribe&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt;&lt;span class="n"&gt;t&lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt;
      &lt;span class="n"&gt;render&lt;/span&gt; &lt;span class="n"&gt;t&lt;/span&gt;
    &lt;span class="k"&gt;end&lt;/span&gt;
  &lt;span class="k"&gt;end&lt;/span&gt;

  &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;close&lt;/span&gt;
    &lt;span class="nb"&gt;puts&lt;/span&gt; &lt;span class="s2"&gt;"WebSocket closed"&lt;/span&gt;
  &lt;span class="k"&gt;end&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;

&lt;span class="no"&gt;Rack&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;Handler&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;Thin&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;run&lt;/span&gt; &lt;span class="no"&gt;TweetsController&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;:Port&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="mi"&gt;3000&lt;/span&gt;
&lt;/pre&gt;
&lt;/div&gt;


&lt;p&gt;We just have to start the server and test with &lt;code&gt;client.html&lt;/code&gt; to see the tweets.&lt;/p&gt;

&lt;h2&gt;Conclusion&lt;/h2&gt;

&lt;p&gt;If you read the code you'll see that Websockets make more sense to a developer than Comet. And it's the same for a lot of HTML5 features. Having more logical things and less hacks will may finally be a reality soon. You can see the whole code of this example in &lt;a href="http://github.com/nmerouze/twitter-amqp-websocket-example/tree/cramp"&gt;my fork of the original one&lt;/a&gt;.&lt;/p&gt;
&lt;/body&gt;&lt;/html&gt;&lt;div class="feedflare"&gt;
&lt;a href="http://feeds.feedburner.com/~ff/fr/boldr?a=_jMZozaWoIQ:_rdg-3RorN4:7Q72WNTAKBA"&gt;&lt;img src="http://feeds.feedburner.com/~ff/fr/boldr?d=7Q72WNTAKBA" border="0"&gt;&lt;/img&gt;&lt;/a&gt;
&lt;/div&gt;&lt;img src="http://feeds.feedburner.com/~r/fr/boldr/~4/_jMZozaWoIQ" height="1" width="1"/&gt;</description><feedburner:origLink>http://boldr.net/html5-websockets-cramp</feedburner:origLink></item><item><title>Speed up Ajax with HTML5 Web Storage</title><link>http://feedproxy.google.com/~r/fr/boldr/~3/lrU9vUpCJ0I/faster-ajax-html5-localstorage</link><dc:creator xmlns:dc="http://purl.org/dc/elements/1.1/">Nicolas Mérouze</dc:creator><pubDate>Thu, 14 Jan 2010 16:00:00 PST</pubDate><guid isPermaLink="false">tag:boldr.net,2009:faster-ajax-html5-localstorage</guid><description>&lt;!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN" "http://www.w3.org/TR/REC-html40/loose.dtd"&gt;
&lt;html&gt;&lt;body&gt;
&lt;p&gt;I'm beginning a series of articles about the modern features of Web browsers. This article will be about HTML5 Web Storage and particularly localStorage. And because I like it, another twitter example folks!&lt;/p&gt;

&lt;h2&gt;What is localStorage?&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;Compatibility of localStorage&lt;/strong&gt; (as of January 2010) : IE8, Firefox 3.5, Safari 4, Chrome 4, Opera 10.50&lt;/p&gt;

&lt;p&gt;Basically it's like cookies but restricted to the client (the server cannot get data from localStorage), it can store more data (the limit depends of the browser but it's generally 5MB) and it doesn't have a time expiration. It's time for the example!&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;WARNING&lt;/strong&gt;: You must test the above example with a real URL (not file://). Otherwise it won't work in IE and Firefox (but seems to be fine in Safari and Chrome)&lt;/p&gt;

&lt;h2&gt;The slow way&lt;/h2&gt;

&lt;p&gt;First we get the tweets and display them. It is slow because each time we access the page the Javascript will request Twitter. And it's ugly because we have a loading message before displaying the tweets.&lt;/p&gt;

&lt;div class="highlight"&gt;
&lt;pre&gt;&lt;span class="cp"&gt;&amp;lt;!DOCTYPE html&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;html&lt;/span&gt; &lt;span class="na"&gt;lang=&lt;/span&gt;&lt;span class="s"&gt;"en"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;head&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;meta&lt;/span&gt; &lt;span class="na"&gt;charset=&lt;/span&gt;&lt;span class="s"&gt;"utf-8"&lt;/span&gt; &lt;span class="nt"&gt;/&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;script &lt;/span&gt;&lt;span class="na"&gt;src=&lt;/span&gt;&lt;span class="s"&gt;"http://ajax.googleapis.com/ajax/libs/jquery/1.3.2/jquery.min.js"&lt;/span&gt; &lt;span class="na"&gt;type=&lt;/span&gt;&lt;span class="s"&gt;"text/javascript"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&amp;lt;/script&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;script &lt;/span&gt;&lt;span class="na"&gt;type=&lt;/span&gt;&lt;span class="s"&gt;"text/javascript"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
    &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nx"&gt;displayTweets&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;data&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="nx"&gt;$&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;"#loading"&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nx"&gt;remove&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
      &lt;span class="nx"&gt;$&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;each&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;data&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;results&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;i&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;item&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="nx"&gt;$&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;"#tweets"&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nx"&gt;append&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;$&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;"&amp;lt;li/&amp;gt;"&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nx"&gt;text&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;item&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;text&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt;
      &lt;span class="p"&gt;});&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="nx"&gt;$&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kd"&gt;function&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="nx"&gt;$&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;"#tweets"&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nx"&gt;before&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;$&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;"&amp;lt;div id='loading'/&amp;gt;"&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nx"&gt;text&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;"Loading..."&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt;
      &lt;span class="nx"&gt;$&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;getJSON&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;"http://search.twitter.com/search.json?q=from:nmerouze&amp;amp;rpp=5&amp;amp;callback=?"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;data&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="nx"&gt;displayTweets&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;data&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
      &lt;span class="p"&gt;});&lt;/span&gt;
    &lt;span class="p"&gt;});&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;/script&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/head&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;body&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;ul&lt;/span&gt; &lt;span class="na"&gt;id=&lt;/span&gt;&lt;span class="s"&gt;"tweets"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&amp;lt;/ul&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/body&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/html&amp;gt;&lt;/span&gt;
&lt;/pre&gt;
&lt;/div&gt;


&lt;p&gt;To display the tweets instantly we're gonna store them.&lt;/p&gt;

&lt;h2&gt;Setter&lt;/h2&gt;

&lt;p&gt;After each request to Twitter we store the whole JSON response to localStorage. It is just one line:&lt;/p&gt;

&lt;div class="highlight"&gt;
&lt;pre&gt;&lt;span class="cp"&gt;&amp;lt;!DOCTYPE html&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;html&lt;/span&gt; &lt;span class="na"&gt;lang=&lt;/span&gt;&lt;span class="s"&gt;"en"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;head&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;meta&lt;/span&gt; &lt;span class="na"&gt;charset=&lt;/span&gt;&lt;span class="s"&gt;"utf-8"&lt;/span&gt; &lt;span class="nt"&gt;/&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;script &lt;/span&gt;&lt;span class="na"&gt;src=&lt;/span&gt;&lt;span class="s"&gt;"http://ajax.googleapis.com/ajax/libs/jquery/1.3.2/jquery.min.js"&lt;/span&gt; &lt;span class="na"&gt;type=&lt;/span&gt;&lt;span class="s"&gt;"text/javascript"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&amp;lt;/script&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;script &lt;/span&gt;&lt;span class="na"&gt;type=&lt;/span&gt;&lt;span class="s"&gt;"text/javascript"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
    &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nx"&gt;displayTweets&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;data&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="nx"&gt;$&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;"#loading"&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nx"&gt;remove&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
      &lt;span class="nx"&gt;$&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;each&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;data&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;results&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;i&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;item&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="nx"&gt;$&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;"#tweets"&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nx"&gt;append&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;$&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;"&amp;lt;li/&amp;gt;"&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nx"&gt;text&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;item&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;text&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt;
      &lt;span class="p"&gt;});&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="nx"&gt;$&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kd"&gt;function&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="nx"&gt;$&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;getJSON&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;"http://search.twitter.com/search.json?q=from:nmerouze&amp;amp;rpp=5&amp;amp;callback=?"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;data&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="nx"&gt;displayTweets&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;data&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
        &lt;span class="nx"&gt;localStorage&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;setItem&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;"tweets"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;JSON&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;stringify&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;data&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt;
      &lt;span class="p"&gt;});&lt;/span&gt;
    &lt;span class="p"&gt;});&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;/script&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/head&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;body&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;ul&lt;/span&gt; &lt;span class="na"&gt;id=&lt;/span&gt;&lt;span class="s"&gt;"tweets"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&amp;lt;/ul&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/body&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/html&amp;gt;&lt;/span&gt;
&lt;/pre&gt;
&lt;/div&gt;


&lt;p&gt;&lt;code&gt;localStorage.setItem(key, value)&lt;/code&gt; take a key for the first argument ("tweets" here) and the value for the second argument (the Twitter JSON response here). &lt;strong&gt;WARNING&lt;/strong&gt;: The value must be a string so if we want to store an object, we stringify it with &lt;code&gt;JSON.stringify(data)&lt;/code&gt;.&lt;/p&gt;

&lt;h2&gt;Getter&lt;/h2&gt;

&lt;p&gt;Then we write the code to get the tweets from localStorage.&lt;/p&gt;

&lt;div class="highlight"&gt;
&lt;pre&gt;&lt;span class="cp"&gt;&amp;lt;!DOCTYPE html&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;html&lt;/span&gt; &lt;span class="na"&gt;lang=&lt;/span&gt;&lt;span class="s"&gt;"en"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;head&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;meta&lt;/span&gt; &lt;span class="na"&gt;charset=&lt;/span&gt;&lt;span class="s"&gt;"utf-8"&lt;/span&gt; &lt;span class="nt"&gt;/&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;script &lt;/span&gt;&lt;span class="na"&gt;src=&lt;/span&gt;&lt;span class="s"&gt;"http://ajax.googleapis.com/ajax/libs/jquery/1.3.2/jquery.min.js"&lt;/span&gt; &lt;span class="na"&gt;type=&lt;/span&gt;&lt;span class="s"&gt;"text/javascript"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&amp;lt;/script&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;script &lt;/span&gt;&lt;span class="na"&gt;type=&lt;/span&gt;&lt;span class="s"&gt;"text/javascript"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
    &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nx"&gt;displayTweets&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;data&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="nx"&gt;$&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;"#loading"&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nx"&gt;remove&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
      &lt;span class="nx"&gt;$&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;"#tweets"&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nx"&gt;children&lt;/span&gt;&lt;span class="p"&gt;().&lt;/span&gt;&lt;span class="nx"&gt;remove&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
      &lt;span class="nx"&gt;$&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;each&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;data&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;results&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;i&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;item&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="nx"&gt;$&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;"#tweets"&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nx"&gt;append&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;$&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;"&amp;lt;li/&amp;gt;"&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nx"&gt;text&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;item&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;text&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt;
      &lt;span class="p"&gt;});&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="nx"&gt;$&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kd"&gt;function&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="nx"&gt;$&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;"#tweets"&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nx"&gt;before&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;$&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;"&amp;lt;div id='loading'/&amp;gt;"&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nx"&gt;text&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;"Loading..."&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt;
      &lt;span class="kd"&gt;var&lt;/span&gt; &lt;span class="nx"&gt;tweets&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;localStorage&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;getItem&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;"tweets"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
      &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;tweets&lt;/span&gt; &lt;span class="o"&gt;!=&lt;/span&gt; &lt;span class="kc"&gt;null&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="nx"&gt;displayTweets&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;JSON&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;parse&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;tweets&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt;
      &lt;span class="nx"&gt;$&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;getJSON&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;"http://search.twitter.com/search.json?q=from:nmerouze&amp;amp;rpp=5&amp;amp;callback=?"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;data&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="nx"&gt;displayTweets&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;data&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
        &lt;span class="nx"&gt;localStorage&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;setItem&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;"tweets"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;JSON&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;stringify&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;data&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt;
      &lt;span class="p"&gt;});&lt;/span&gt;
    &lt;span class="p"&gt;});&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;/script&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/head&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;body&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;ul&lt;/span&gt; &lt;span class="na"&gt;id=&lt;/span&gt;&lt;span class="s"&gt;"tweets"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&amp;lt;/ul&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;button&lt;/span&gt; &lt;span class="na"&gt;onclick=&lt;/span&gt;&lt;span class="s"&gt;"javascript:localStorage.clear();"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;Clear localStorage&lt;span class="nt"&gt;&amp;lt;/button&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/body&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/html&amp;gt;&lt;/span&gt;
&lt;/pre&gt;
&lt;/div&gt;


&lt;p&gt;To get the data it's &lt;code&gt;localStorage.getItem(key)&lt;/code&gt;. If there's something in it we display them. The first time we access the page there won't be anything in localStorage so the process will be the same as "The slow way". The other times the tweets will be displayed instantly. The Twitter request is running too, so it will refresh the list a little bit after the first display in case there's any new tweets. The rendering will be way prettier than the loading message! You can see the &lt;a href="/examples/faster-ajax-html5-localstorage"&gt;example here&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;If we want to clear the localStorage it's as simple as &lt;code&gt;localStorage.clear()&lt;/code&gt;.&lt;/p&gt;

&lt;h2&gt;Old browsers compatibility (progressive enhancement)&lt;/h2&gt;

&lt;p&gt;If you do that in production, the Javascript will fail for the users who have old browsers. To be safe, we will download &lt;a href="http://modernizr.com"&gt;Modernizr&lt;/a&gt; (I will write an article about Modernizr soon) and check if the browser has a localStorage:&lt;/p&gt;

&lt;div class="highlight"&gt;
&lt;pre&gt;&lt;span class="cp"&gt;&amp;lt;!DOCTYPE html&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;html&lt;/span&gt; &lt;span class="na"&gt;lang=&lt;/span&gt;&lt;span class="s"&gt;"en"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;head&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;meta&lt;/span&gt; &lt;span class="na"&gt;charset=&lt;/span&gt;&lt;span class="s"&gt;"utf-8"&lt;/span&gt; &lt;span class="nt"&gt;/&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;script &lt;/span&gt;&lt;span class="na"&gt;src=&lt;/span&gt;&lt;span class="s"&gt;"http://ajax.googleapis.com/ajax/libs/jquery/1.3.2/jquery.min.js"&lt;/span&gt; &lt;span class="na"&gt;type=&lt;/span&gt;&lt;span class="s"&gt;"text/javascript"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&amp;lt;/script&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;script &lt;/span&gt;&lt;span class="na"&gt;src=&lt;/span&gt;&lt;span class="s"&gt;"modernizr.js"&lt;/span&gt; &lt;span class="na"&gt;type=&lt;/span&gt;&lt;span class="s"&gt;"text/javascript"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&amp;lt;/script&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;script &lt;/span&gt;&lt;span class="na"&gt;type=&lt;/span&gt;&lt;span class="s"&gt;"text/javascript"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
    &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nx"&gt;displayTweets&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;data&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="nx"&gt;$&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;"#loading"&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nx"&gt;remove&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
      &lt;span class="nx"&gt;$&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;"#tweets"&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nx"&gt;children&lt;/span&gt;&lt;span class="p"&gt;().&lt;/span&gt;&lt;span class="nx"&gt;remove&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
      &lt;span class="nx"&gt;$&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;each&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;data&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;results&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;i&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;item&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="nx"&gt;$&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;"#tweets"&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nx"&gt;append&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;$&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;"&amp;lt;li/&amp;gt;"&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nx"&gt;text&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;item&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;text&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt;
      &lt;span class="p"&gt;});&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="nx"&gt;$&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kd"&gt;function&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="nx"&gt;$&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;"#tweets"&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nx"&gt;before&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;$&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;"&amp;lt;div id='loading'/&amp;gt;"&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nx"&gt;text&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;"Loading..."&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt;
      &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;Modernizr&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;localstorage&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="kd"&gt;var&lt;/span&gt; &lt;span class="nx"&gt;tweets&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;localStorage&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;getItem&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;"tweets"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
        &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;tweets&lt;/span&gt; &lt;span class="o"&gt;!=&lt;/span&gt; &lt;span class="kc"&gt;null&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="nx"&gt;displayTweets&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;JSON&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;parse&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;tweets&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt;
      &lt;span class="p"&gt;}&lt;/span&gt;
      &lt;span class="nx"&gt;$&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;getJSON&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;"http://search.twitter.com/search.json?q=from:nmerouze&amp;amp;rpp=5&amp;amp;callback=?"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;data&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="nx"&gt;displayTweets&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;data&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
        &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;Modernizr&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;localstorage&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="nx"&gt;localStorage&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;setItem&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;"tweets"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;JSON&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;stringify&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;data&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt;
      &lt;span class="p"&gt;});&lt;/span&gt;
    &lt;span class="p"&gt;});&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;/script&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/head&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;body&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;ul&lt;/span&gt; &lt;span class="na"&gt;id=&lt;/span&gt;&lt;span class="s"&gt;"tweets"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&amp;lt;/ul&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/body&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/html&amp;gt;&lt;/span&gt;
&lt;/pre&gt;
&lt;/div&gt;


&lt;h2&gt;Result&lt;/h2&gt;

&lt;p&gt;As a result I made a fork of &lt;a href="http://github.com/seaofclouds/tweet"&gt;jQuery tweet&lt;/a&gt; with localStorage support, so &lt;a href="http://github.com/nmerouze/tweet/blob/master/tweet/jquery.tweet.js#L146"&gt;check it out&lt;/a&gt;! You won't see a difference with a server-side rendering.&lt;/p&gt;

&lt;h2&gt;Links&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="http://www.quirksmode.org/dom/html5.html#localstorage"&gt;HTML5 Web storage compatibilty&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="http://hacks.mozilla.org/2009/06/localstorage/"&gt;Tutorial with all the different syntaxes&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/body&gt;&lt;/html&gt;&lt;div class="feedflare"&gt;
&lt;a href="http://feeds.feedburner.com/~ff/fr/boldr?a=lrU9vUpCJ0I:BchgOvvbRdg:7Q72WNTAKBA"&gt;&lt;img src="http://feeds.feedburner.com/~ff/fr/boldr?d=7Q72WNTAKBA" border="0"&gt;&lt;/img&gt;&lt;/a&gt;
&lt;/div&gt;&lt;img src="http://feeds.feedburner.com/~r/fr/boldr/~4/lrU9vUpCJ0I" height="1" width="1"/&gt;</description><feedburner:origLink>http://boldr.net/faster-ajax-html5-localstorage</feedburner:origLink></item><item><title>Easy Testing MongoMapper models</title><link>http://feedproxy.google.com/~r/fr/boldr/~3/BdbstRWoqlU/test-mongomapper</link><dc:creator xmlns:dc="http://purl.org/dc/elements/1.1/">Nicolas Mérouze</dc:creator><pubDate>Tue, 12 Jan 2010 16:00:00 PST</pubDate><guid isPermaLink="false">tag:boldr.net,2009:test-mongomapper</guid><description>&lt;!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN" "http://www.w3.org/TR/REC-html40/loose.dtd"&gt;
&lt;html&gt;&lt;body&gt;
&lt;p&gt;There's a bunch of libraries to write tests easily for your app. &lt;a href="http://github.com/thoughtbot/factory_girl/"&gt;factory_girl&lt;/a&gt; and &lt;a href="http://github.com/notahat/machinist"&gt;Machinist&lt;/a&gt; for the fixtures. &lt;a href="http://github.com/carlosbrando/remarkable"&gt;Remarkable&lt;/a&gt; for the macros.&lt;/p&gt;

&lt;p&gt;There's no core support for &lt;a href="http://github.com/jnunemaker/mongomapper"&gt;MongoMapper&lt;/a&gt; in any of these solutions but Machinist and Remarkable are agnostic so it's easy to add a MongoMapper support. That's what I did a few months ago with &lt;a href="http://github.com/nmerouze/machinist_mongo"&gt;machinist_mongo&lt;/a&gt; and &lt;a href="http://github.com/nmerouze/remarkable_mongo"&gt;remarkable_mongo&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;I'm refactoring them to add &lt;a href="http://github.com/durran/mongoid"&gt;Mongoid&lt;/a&gt; support in a near future and I think it's time to have a bit of docs on these 2 gems. Let's see how to use them in a Rails project with RSpec.&lt;/p&gt;

&lt;h2&gt;Machinist for MongoMapper&lt;/h2&gt;

&lt;p&gt;First we load the gem:&lt;/p&gt;

&lt;div class="highlight"&gt;
&lt;pre&gt;&lt;span class="n"&gt;config&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;gem&lt;/span&gt; &lt;span class="s2"&gt;"machinist_mongo"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;:lib&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="s2"&gt;"machinist/mongo_mapper"&lt;/span&gt;
&lt;/pre&gt;
&lt;/div&gt;


&lt;p&gt;Then it's like any other Machinist adapter. We create the blueprint:&lt;/p&gt;

&lt;div class="highlight"&gt;
&lt;pre&gt;&lt;span class="no"&gt;Post&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;blueprint&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
  &lt;span class="n"&gt;title&lt;/span&gt; &lt;span class="s2"&gt;"I like MongoDB!"&lt;/span&gt;
  &lt;span class="n"&gt;body&lt;/span&gt; &lt;span class="s2"&gt;"I have a ton of reasons for that."&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;
&lt;/pre&gt;
&lt;/div&gt;


&lt;p&gt;We require the blueprints.rb file into spec_helper.rb:&lt;/p&gt;

&lt;div class="highlight"&gt;
&lt;pre&gt;&lt;span class="no"&gt;ENV&lt;/span&gt;&lt;span class="o"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;"RAILS_ENV"&lt;/span&gt;&lt;span class="o"&gt;]&lt;/span&gt; &lt;span class="o"&gt;||=&lt;/span&gt; &lt;span class="s1"&gt;'test'&lt;/span&gt;
&lt;span class="nb"&gt;require&lt;/span&gt; &lt;span class="no"&gt;File&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;expand_path&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="no"&gt;File&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;join&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="no"&gt;File&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;dirname&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="bp"&gt;__FILE__&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;&lt;span class="s1"&gt;'..'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="s1"&gt;'config'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="s1"&gt;'environment'&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
&lt;span class="nb"&gt;require&lt;/span&gt; &lt;span class="s1"&gt;'spec/autorun'&lt;/span&gt;
&lt;span class="nb"&gt;require&lt;/span&gt; &lt;span class="s1"&gt;'spec/rails'&lt;/span&gt;
&lt;span class="nb"&gt;require&lt;/span&gt; &lt;span class="s1"&gt;'spec/blueprints'&lt;/span&gt;

&lt;span class="no"&gt;Spec&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;Runner&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;configure&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt;&lt;span class="n"&gt;config&lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;
&lt;/pre&gt;
&lt;/div&gt;


&lt;p&gt;We write the specs:&lt;/p&gt;

&lt;div class="highlight"&gt;
&lt;pre&gt;&lt;span class="nb"&gt;require&lt;/span&gt; &lt;span class="no"&gt;File&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;expand_path&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="no"&gt;File&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;dirname&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="bp"&gt;__FILE__&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="s1"&gt;'/../spec_helper'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="n"&gt;describe&lt;/span&gt; &lt;span class="no"&gt;Post&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
  &lt;span class="n"&gt;before&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="ss"&gt;:all&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
    &lt;span class="vi"&gt;@post&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="no"&gt;Post&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;make&lt;/span&gt;
  &lt;span class="k"&gt;end&lt;/span&gt;

  &lt;span class="n"&gt;it&lt;/span&gt; &lt;span class="s2"&gt;"should have a title"&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
    &lt;span class="vi"&gt;@post&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;title&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;should&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="s2"&gt;"I like MongoDB!"&lt;/span&gt;
  &lt;span class="k"&gt;end&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;
&lt;/pre&gt;
&lt;/div&gt;


&lt;p&gt;And finally the related model:&lt;/p&gt;

&lt;div class="highlight"&gt;
&lt;pre&gt;&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;Post&lt;/span&gt;
  &lt;span class="kp"&gt;include&lt;/span&gt; &lt;span class="no"&gt;MongoMapper&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;Document&lt;/span&gt;

  &lt;span class="n"&gt;key&lt;/span&gt; &lt;span class="ss"&gt;:title&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nb"&gt;String&lt;/span&gt;
  &lt;span class="n"&gt;key&lt;/span&gt; &lt;span class="ss"&gt;:body&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nb"&gt;String&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;
&lt;/pre&gt;
&lt;/div&gt;


&lt;p&gt;Like Machinist for ActiveRecord you have the 3 following methods: Model.make, Model.make_unsaved, Model.plan. And there's a preliminary support for embedded documents.&lt;/p&gt;

&lt;h2&gt;Remarkable for MongoMapper&lt;/h2&gt;

&lt;p&gt;The first step is the same that Machinist, requiring the gems:&lt;/p&gt;

&lt;div class="highlight"&gt;
&lt;pre&gt;&lt;span class="n"&gt;config&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;gem&lt;/span&gt; &lt;span class="s2"&gt;"remarkable_rails"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;:lib&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="s2"&gt;"remarkable_rails"&lt;/span&gt;
&lt;span class="n"&gt;config&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;gem&lt;/span&gt; &lt;span class="s2"&gt;"remarkable_mongo"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;:lib&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="s2"&gt;"remarkable/mongo_mapper"&lt;/span&gt;
&lt;/pre&gt;
&lt;/div&gt;


&lt;p&gt;Let's add the specs. Say we want to check the existence of title and body keys and the title presence validation:&lt;/p&gt;

&lt;div class="highlight"&gt;
&lt;pre&gt;&lt;span class="nb"&gt;require&lt;/span&gt; &lt;span class="no"&gt;File&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;expand_path&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="no"&gt;File&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;dirname&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="bp"&gt;__FILE__&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="s1"&gt;'/../spec_helper'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="n"&gt;describe&lt;/span&gt; &lt;span class="no"&gt;Post&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
  &lt;span class="n"&gt;it&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="n"&gt;should&lt;/span&gt; &lt;span class="n"&gt;have_keys&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="ss"&gt;:title&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;:body&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nb"&gt;String&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="n"&gt;it&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="n"&gt;should&lt;/span&gt; &lt;span class="n"&gt;validate_presence_of&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="ss"&gt;:title&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="c1"&gt;# ...&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;
&lt;/pre&gt;
&lt;/div&gt;


&lt;p&gt;Finally we modify the model to pass the specs:&lt;/p&gt;

&lt;div class="highlight"&gt;
&lt;pre&gt;&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;Post&lt;/span&gt;
  &lt;span class="c1"&gt;# ...&lt;/span&gt;
  &lt;span class="n"&gt;validates_presence_of&lt;/span&gt; &lt;span class="ss"&gt;:title&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;
&lt;/pre&gt;
&lt;/div&gt;


&lt;p&gt;Remarkable for MongoMapper is not feature complete (as of v0.1.2). Here's the list of macros but they don't support all the options of the related MongoMapper methods.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;belong_to&lt;/li&gt;
&lt;li&gt;have_many&lt;/li&gt;
&lt;li&gt;have_key/have_keys&lt;/li&gt;
&lt;li&gt;validate_presence_of&lt;/li&gt;
&lt;li&gt;validate_confirmation_of&lt;/li&gt;
&lt;li&gt;validate_length_of (bugged)&lt;/li&gt;
&lt;li&gt;allow_values_for&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;If you want to add features, &lt;a href="http://github.com/nmerouze/remarkable_mongo"&gt;fork the repo&lt;/a&gt;.&lt;/p&gt;

&lt;h2&gt;Conclusion&lt;/h2&gt;

&lt;p&gt;If you've already used Machinist and/or Remarkable you won't have problems. For the other ones it is an easy solution to write your tests quickly.&lt;/p&gt;
&lt;/body&gt;&lt;/html&gt;&lt;div class="feedflare"&gt;
&lt;a href="http://feeds.feedburner.com/~ff/fr/boldr?a=BdbstRWoqlU:ovvFpTRlXA0:7Q72WNTAKBA"&gt;&lt;img src="http://feeds.feedburner.com/~ff/fr/boldr?d=7Q72WNTAKBA" border="0"&gt;&lt;/img&gt;&lt;/a&gt;
&lt;/div&gt;&lt;img src="http://feeds.feedburner.com/~r/fr/boldr/~4/BdbstRWoqlU" height="1" width="1"/&gt;</description><feedburner:origLink>http://boldr.net/test-mongomapper</feedburner:origLink></item><item><title>iPhone app in 5 minutes with Titanium Mobile</title><link>http://feedproxy.google.com/~r/fr/boldr/~3/gkD_YlmfWpY/iphone-app-with-titanium-mobile</link><dc:creator xmlns:dc="http://purl.org/dc/elements/1.1/">Nicolas Mérouze</dc:creator><pubDate>Mon, 04 Jan 2010 16:00:00 PST</pubDate><guid isPermaLink="false">tag:boldr.net,2009:iphone-app-with-titanium-mobile</guid><description>&lt;!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN" "http://www.w3.org/TR/REC-html40/loose.dtd"&gt;
&lt;html&gt;&lt;body&gt;
&lt;p&gt;Do you want to build native iPhone/Android apps with HTML/Javascript? And I mean real native not native but web like &lt;a href="http://rhomobile.com/products/rhodes/"&gt;Rhodes&lt;/a&gt; and &lt;a href="http://phonegap.com/"&gt;PhoneGap&lt;/a&gt;. If this is what you want you may give &lt;a href="http://www.appcelerator.com/products/titanium-mobile/"&gt;Titanium Mobile&lt;/a&gt; a try.&lt;/p&gt;

&lt;p&gt;To convince you I have an example to show you. Let's take a twitter viewer (how original it is!). To be simple, we will get the JSON from a search and create a table view with the 10 last tweets.&lt;/p&gt;

&lt;h2&gt;Bootstrap&lt;/h2&gt;

&lt;p&gt;After downloading and installing Titanium Mobile we create a mobile application. To test it with the iPhone XCode and the iPhone SDK must be installed. For Android, the Android SDK must be installed.&lt;/p&gt;

&lt;p&gt;The first step is to edit the main view (index.html). We add all the JS we'll need: jQuery and Underscore.js&lt;/p&gt;

&lt;div class="highlight"&gt;
&lt;pre&gt;&lt;span class="cp"&gt;&amp;lt;!DOCTYPE html&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;html&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;head&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;meta&lt;/span&gt; &lt;span class="na"&gt;charset=&lt;/span&gt;&lt;span class="s"&gt;"utf-8"&lt;/span&gt; &lt;span class="nt"&gt;/&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;script &lt;/span&gt;&lt;span class="na"&gt;type=&lt;/span&gt;&lt;span class="s"&gt;"text/javascript"&lt;/span&gt; &lt;span class="na"&gt;src=&lt;/span&gt;&lt;span class="s"&gt;"js/jquery.js"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&amp;lt;/script&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;script &lt;/span&gt;&lt;span class="na"&gt;type=&lt;/span&gt;&lt;span class="s"&gt;"text/javascript"&lt;/span&gt; &lt;span class="na"&gt;src=&lt;/span&gt;&lt;span class="s"&gt;"js/underscore.js"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&amp;lt;/script&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;script &lt;/span&gt;&lt;span class="na"&gt;type=&lt;/span&gt;&lt;span class="s"&gt;"text/javascript"&lt;/span&gt; &lt;span class="na"&gt;src=&lt;/span&gt;&lt;span class="s"&gt;"index.js"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&amp;lt;/script&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/head&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;body&amp;gt;&amp;lt;/body&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/html&amp;gt;&lt;/span&gt;
&lt;/pre&gt;
&lt;/div&gt;


&lt;h2&gt;Javascript&lt;/h2&gt;

&lt;p&gt;Titanium Mobile is a bit like &lt;a href="http://sproutcore.com/"&gt;Sproutcore&lt;/a&gt;. The biggest part of the code will be JS. To begin with it we need to get the tweets extracting text and profile image URLs:&lt;/p&gt;

&lt;div class="highlight"&gt;
&lt;pre&gt;&lt;span class="nx"&gt;$&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kd"&gt;function&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;$&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;getJSON&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;"http://search.twitter.com/search.json?&amp;amp;q=appcelerator&amp;amp;rpp=10&amp;amp;callback=?"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;json&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="kd"&gt;var&lt;/span&gt; &lt;span class="nx"&gt;data&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;_&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;map&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;json&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;results&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;item&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;text&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;&lt;span class="nx"&gt;item&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;text&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;image&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;&lt;span class="nx"&gt;item&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;profile_image_url&lt;/span&gt; &lt;span class="p"&gt;};&lt;/span&gt;
    &lt;span class="p"&gt;});&lt;/span&gt;
  &lt;span class="p"&gt;});&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;
&lt;/pre&gt;
&lt;/div&gt;


&lt;p&gt;Then we want a column layout with 2 things: text and image. So we apply the style for it:&lt;/p&gt;

&lt;div class="highlight"&gt;
&lt;pre&gt;&lt;span class="nx"&gt;$&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kd"&gt;function&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;$&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;getJSON&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;"http://search.twitter.com/search.json?&amp;amp;q=appcelerator&amp;amp;rpp=10&amp;amp;callback=?"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;json&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="kd"&gt;var&lt;/span&gt; &lt;span class="nx"&gt;data&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;_&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;map&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;json&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;results&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;item&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;text&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;&lt;span class="nx"&gt;item&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;text&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;image&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;&lt;span class="nx"&gt;item&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;profile_image_url&lt;/span&gt; &lt;span class="p"&gt;};&lt;/span&gt;
    &lt;span class="p"&gt;});&lt;/span&gt;

    &lt;span class="kd"&gt;var&lt;/span&gt; &lt;span class="nx"&gt;template&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="nx"&gt;selectedBackgroundColor&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;&lt;span class="s2"&gt;"#111"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="nx"&gt;rowHeight&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;&lt;span class="mi"&gt;50&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="nx"&gt;layout&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;
        &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;type&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;&lt;span class="s2"&gt;"text"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;name&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;&lt;span class="s2"&gt;"text"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;fontSize&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;&lt;span class="mi"&gt;10&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;fontWeight&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;&lt;span class="s2"&gt;"normal"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;color&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;&lt;span class="s2"&gt;"#999"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;top&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;&lt;span class="mi"&gt;5&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;left&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;&lt;span class="mi"&gt;50&lt;/span&gt; &lt;span class="p"&gt;},&lt;/span&gt;
        &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;type&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;&lt;span class="s2"&gt;"image"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;name&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;&lt;span class="s2"&gt;"image"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;width&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;&lt;span class="mi"&gt;40&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;height&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;&lt;span class="mi"&gt;40&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;top&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;&lt;span class="mi"&gt;5&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;left&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;&lt;span class="mi"&gt;5&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
      &lt;span class="p"&gt;]&lt;/span&gt;
    &lt;span class="p"&gt;};&lt;/span&gt;
  &lt;span class="p"&gt;});&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;
&lt;/pre&gt;
&lt;/div&gt;


&lt;p&gt;And we initialize the table view:&lt;/p&gt;

&lt;div class="highlight"&gt;
&lt;pre&gt;&lt;span class="nx"&gt;$&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kd"&gt;function&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;$&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;getJSON&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;"http://search.twitter.com/search.json?&amp;amp;q=appcelerator&amp;amp;rpp=10&amp;amp;callback=?"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;json&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="kd"&gt;var&lt;/span&gt; &lt;span class="nx"&gt;data&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;_&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;map&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;json&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;results&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;item&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;text&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;&lt;span class="nx"&gt;item&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;text&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;image&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;&lt;span class="nx"&gt;item&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;profile_image_url&lt;/span&gt; &lt;span class="p"&gt;};&lt;/span&gt;
    &lt;span class="p"&gt;});&lt;/span&gt;

    &lt;span class="kd"&gt;var&lt;/span&gt; &lt;span class="nx"&gt;template&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="nx"&gt;selectedBackgroundColor&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;&lt;span class="s2"&gt;"#111"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="nx"&gt;rowHeight&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;&lt;span class="mi"&gt;50&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="nx"&gt;layout&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;
        &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;type&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;&lt;span class="s2"&gt;"text"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;name&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;&lt;span class="s2"&gt;"text"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;fontSize&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;&lt;span class="mi"&gt;10&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;fontWeight&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;&lt;span class="s2"&gt;"normal"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;color&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;&lt;span class="s2"&gt;"#999"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;top&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;&lt;span class="mi"&gt;5&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;left&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;&lt;span class="mi"&gt;50&lt;/span&gt; &lt;span class="p"&gt;},&lt;/span&gt;
        &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;type&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;&lt;span class="s2"&gt;"image"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;name&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;&lt;span class="s2"&gt;"image"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;width&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;&lt;span class="mi"&gt;40&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;height&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;&lt;span class="mi"&gt;40&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;top&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;&lt;span class="mi"&gt;5&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;left&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;&lt;span class="mi"&gt;5&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
      &lt;span class="p"&gt;]&lt;/span&gt;
    &lt;span class="p"&gt;};&lt;/span&gt;

    &lt;span class="kd"&gt;var&lt;/span&gt; &lt;span class="nx"&gt;tableView&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;Titanium&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;UI&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;createTableView&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
      &lt;span class="nx"&gt;backgroundColor&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;&lt;span class="s2"&gt;"transparent"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="nx"&gt;borderColor&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;&lt;span class="s2"&gt;"#333"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="nx"&gt;template&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;&lt;span class="nx"&gt;template&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="nx"&gt;data&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;&lt;span class="nx"&gt;data&lt;/span&gt;
    &lt;span class="p"&gt;},&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;eventObject&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{});&lt;/span&gt;
  &lt;span class="p"&gt;});&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;
&lt;/pre&gt;
&lt;/div&gt;


&lt;p&gt;Finally we show it in the current window:&lt;/p&gt;

&lt;div class="highlight"&gt;
&lt;pre&gt;&lt;span class="nx"&gt;$&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kd"&gt;function&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;$&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;getJSON&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;"http://search.twitter.com/search.json?&amp;amp;q=appcelerator&amp;amp;rpp=10&amp;amp;callback=?"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;json&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="kd"&gt;var&lt;/span&gt; &lt;span class="nx"&gt;data&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;_&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;map&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;json&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;results&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;item&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;text&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;&lt;span class="nx"&gt;item&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;text&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;image&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;&lt;span class="nx"&gt;item&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;profile_image_url&lt;/span&gt; &lt;span class="p"&gt;};&lt;/span&gt;
    &lt;span class="p"&gt;});&lt;/span&gt;

    &lt;span class="kd"&gt;var&lt;/span&gt; &lt;span class="nx"&gt;template&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="nx"&gt;selectedBackgroundColor&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;&lt;span class="s2"&gt;"#111"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="nx"&gt;rowHeight&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;&lt;span class="mi"&gt;50&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="nx"&gt;layout&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;
        &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;type&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;&lt;span class="s2"&gt;"text"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;name&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;&lt;span class="s2"&gt;"text"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;fontSize&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;&lt;span class="mi"&gt;10&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;fontWeight&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;&lt;span class="s2"&gt;"normal"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;color&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;&lt;span class="s2"&gt;"#999"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;top&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;&lt;span class="mi"&gt;5&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;left&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;&lt;span class="mi"&gt;50&lt;/span&gt; &lt;span class="p"&gt;},&lt;/span&gt;
        &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;type&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;&lt;span class="s2"&gt;"image"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;name&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;&lt;span class="s2"&gt;"image"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;width&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;&lt;span class="mi"&gt;40&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;height&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;&lt;span class="mi"&gt;40&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;top&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;&lt;span class="mi"&gt;5&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;left&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;&lt;span class="mi"&gt;5&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
      &lt;span class="p"&gt;]&lt;/span&gt;
    &lt;span class="p"&gt;};&lt;/span&gt;

    &lt;span class="kd"&gt;var&lt;/span&gt; &lt;span class="nx"&gt;tableView&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;Titanium&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;UI&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;createTableView&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
      &lt;span class="nx"&gt;backgroundColor&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;&lt;span class="s2"&gt;"transparent"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="nx"&gt;borderColor&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;&lt;span class="s2"&gt;"#333"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="nx"&gt;template&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;&lt;span class="nx"&gt;template&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="nx"&gt;data&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;&lt;span class="nx"&gt;data&lt;/span&gt;
    &lt;span class="p"&gt;},&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;eventObject&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{});&lt;/span&gt;

    &lt;span class="nx"&gt;Titanium&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;UI&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;currentWindow&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;addView&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;tableView&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="nx"&gt;Titanium&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;UI&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;currentWindow&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;showView&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;tableView&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="p"&gt;});&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;
&lt;/pre&gt;
&lt;/div&gt;


&lt;p&gt;26 lines of Javascript to create a twitter viewer! People who have already develop for iPhone will see how easier Titanium Mobile is.&lt;/p&gt;

&lt;p&gt;&lt;img src="http://boldr.net/images/assets/titanium_mobile_test.png" alt="Twitter Viewer with Titanium Mobile"&gt;&lt;/p&gt;

&lt;p&gt;There's a lot of cool things like composite views, scrollable views, coverflow, maps, facebook connect... And it's as easy as this example. If you don't want to bother with a language you don't know or/and you are tight on your schedule, Titanium Mobile is for you.&lt;/p&gt;

&lt;p&gt;If you're convinced and want to learn more about it the developers maintain a really useful &lt;a href="http://github.com/kwhinnery/KitchenSink/"&gt;example app&lt;/a&gt;.&lt;/p&gt;
&lt;/body&gt;&lt;/html&gt;&lt;div class="feedflare"&gt;
&lt;a href="http://feeds.feedburner.com/~ff/fr/boldr?a=gkD_YlmfWpY:4V3ySRB0cOw:7Q72WNTAKBA"&gt;&lt;img src="http://feeds.feedburner.com/~ff/fr/boldr?d=7Q72WNTAKBA" border="0"&gt;&lt;/img&gt;&lt;/a&gt;
&lt;/div&gt;&lt;img src="http://feeds.feedburner.com/~r/fr/boldr/~4/gkD_YlmfWpY" height="1" width="1"/&gt;</description><feedburner:origLink>http://boldr.net/iphone-app-with-titanium-mobile</feedburner:origLink></item><item><title>Create a Web App with Node.js</title><link>http://feedproxy.google.com/~r/fr/boldr/~3/3DzCgNKWx8w/create-a-web-app-with-node</link><dc:creator xmlns:dc="http://purl.org/dc/elements/1.1/">Nicolas Mérouze</dc:creator><pubDate>Mon, 21 Dec 2009 16:00:00 PST</pubDate><guid isPermaLink="false">tag:boldr.net,2009:create-a-web-app-with-node</guid><description>&lt;!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN" "http://www.w3.org/TR/REC-html40/loose.dtd"&gt;
&lt;html&gt;&lt;body&gt;
&lt;p&gt;Node.js is the new hype for server-side webish things. It's evented, non-blocking, fast and in javascript (not another language to learn, yay!). Real-Time Web is around the corner.&lt;/p&gt;

&lt;p&gt;Javascript means you can use existing JS librairies. In this example I will use Mustache.js and Underscore to create the core of a minimal web application.&lt;/p&gt;

&lt;h2&gt;Let's begin&lt;/h2&gt;

&lt;p&gt;I have 4 files: main.js, mustache.js, underscore.js, templates/index.html&lt;/p&gt;

&lt;p&gt;We will put our code in main.js. First we require the things we'll need:&lt;/p&gt;

&lt;div class="highlight"&gt;
&lt;pre&gt;&lt;span class="kd"&gt;var&lt;/span&gt; &lt;span class="nx"&gt;sys&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;require&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'sys'&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="nx"&gt;http&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;require&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'http'&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="nx"&gt;posix&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;require&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'posix'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="nx"&gt;require&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'./underscore'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="kd"&gt;var&lt;/span&gt; &lt;span class="nx"&gt;Mustache&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;require&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'./mustache'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/pre&gt;
&lt;/div&gt;


&lt;p&gt;Note that Underscore is CommonJS compliant so you don't have to do anything for it. Then we add our actions which are composed by 3 things: URI, template name and the view.&lt;/p&gt;

&lt;div class="highlight"&gt;
&lt;pre&gt;&lt;span class="kd"&gt;var&lt;/span&gt; &lt;span class="nx"&gt;actions&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[];&lt;/span&gt;

&lt;span class="nx"&gt;actions&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;push&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
  &lt;span class="nx"&gt;path&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"/"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="nx"&gt;template&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"index.html"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="nx"&gt;view&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nx"&gt;title&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"Joe"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="nx"&gt;calc&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="mi"&gt;4&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;})&lt;/span&gt;
&lt;/pre&gt;
&lt;/div&gt;


&lt;p&gt;We can create as many actions as we want. Now let's create the server:&lt;/p&gt;

&lt;div class="highlight"&gt;
&lt;pre&gt;&lt;span class="nx"&gt;http&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;createServer&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;req&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;res&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{}).&lt;/span&gt;&lt;span class="nx"&gt;listen&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;8000&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/pre&gt;
&lt;/div&gt;


&lt;p&gt;The server will be running but will not know how not handle actions. Let's add a request listener:&lt;/p&gt;

&lt;div class="highlight"&gt;
&lt;pre&gt;&lt;span class="nx"&gt;http&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;createServer&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;req&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;res&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;req&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;addListener&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'complete'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{})&lt;/span&gt;
&lt;span class="p"&gt;}).&lt;/span&gt;&lt;span class="nx"&gt;listen&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;8000&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/pre&gt;
&lt;/div&gt;


&lt;p&gt;The req variable contains the URI, so we will select the action corresponding to the request:&lt;/p&gt;

&lt;div class="highlight"&gt;
&lt;pre&gt;&lt;span class="nx"&gt;http&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;createServer&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;req&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;res&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;req&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;addListener&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'complete'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="kd"&gt;var&lt;/span&gt; &lt;span class="nx"&gt;action&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;_&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;actions&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nx"&gt;chain&lt;/span&gt;&lt;span class="p"&gt;().&lt;/span&gt;&lt;span class="nx"&gt;select&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kd"&gt;function&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;a&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;req&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;uri&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;path&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="nx"&gt;a&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;path&lt;/span&gt; &lt;span class="p"&gt;}).&lt;/span&gt;&lt;span class="nx"&gt;first&lt;/span&gt;&lt;span class="p"&gt;().&lt;/span&gt;&lt;span class="nx"&gt;value&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
  &lt;span class="p"&gt;})&lt;/span&gt;
&lt;span class="p"&gt;}).&lt;/span&gt;&lt;span class="nx"&gt;listen&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;8000&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/pre&gt;
&lt;/div&gt;


&lt;p&gt;If the request doesn't match an action, we will send an error:&lt;/p&gt;

&lt;div class="highlight"&gt;
&lt;pre&gt;&lt;span class="nx"&gt;http&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;createServer&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;req&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;res&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;req&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;addListener&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'complete'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="kd"&gt;var&lt;/span&gt; &lt;span class="nx"&gt;action&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;_&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;actions&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nx"&gt;chain&lt;/span&gt;&lt;span class="p"&gt;().&lt;/span&gt;&lt;span class="nx"&gt;select&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kd"&gt;function&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;a&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;req&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;uri&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;path&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="nx"&gt;a&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;path&lt;/span&gt; &lt;span class="p"&gt;}).&lt;/span&gt;&lt;span class="nx"&gt;first&lt;/span&gt;&lt;span class="p"&gt;().&lt;/span&gt;&lt;span class="nx"&gt;value&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;

    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;_&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;isEmpty&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;action&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="nx"&gt;res&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;sendHeader&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;404&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="s1"&gt;'Content-Type'&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="s1"&gt;'text/plain'&lt;/span&gt;&lt;span class="p"&gt;})&lt;/span&gt;
      &lt;span class="nx"&gt;res&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;sendBody&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;"Error"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
      &lt;span class="nx"&gt;res&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;finish&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;else&lt;/span&gt; &lt;span class="p"&gt;{}&lt;/span&gt;
  &lt;span class="p"&gt;})&lt;/span&gt;
&lt;span class="p"&gt;}).&lt;/span&gt;&lt;span class="nx"&gt;listen&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;8000&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/pre&gt;
&lt;/div&gt;


&lt;p&gt;In the other case we render the action:&lt;/p&gt;

&lt;div class="highlight"&gt;
&lt;pre&gt;&lt;span class="nx"&gt;http&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;createServer&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;req&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;res&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;req&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;addListener&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'complete'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="kd"&gt;var&lt;/span&gt; &lt;span class="nx"&gt;action&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;_&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;actions&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nx"&gt;chain&lt;/span&gt;&lt;span class="p"&gt;().&lt;/span&gt;&lt;span class="nx"&gt;select&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kd"&gt;function&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;a&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;req&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;uri&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;path&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="nx"&gt;a&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;path&lt;/span&gt; &lt;span class="p"&gt;}).&lt;/span&gt;&lt;span class="nx"&gt;first&lt;/span&gt;&lt;span class="p"&gt;().&lt;/span&gt;&lt;span class="nx"&gt;value&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;

    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;_&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;isEmpty&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;action&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="nx"&gt;res&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;sendHeader&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;404&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="s1"&gt;'Content-Type'&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="s1"&gt;'text/plain'&lt;/span&gt;&lt;span class="p"&gt;})&lt;/span&gt;
      &lt;span class="nx"&gt;res&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;sendBody&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;"Error"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
      &lt;span class="nx"&gt;res&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;finish&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;else&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="nx"&gt;posix&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;cat&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;"./templates/"&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="nx"&gt;action&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;template&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nx"&gt;addCallback&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kd"&gt;function&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;template&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="nx"&gt;res&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;sendHeader&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;200&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="s1"&gt;'Content-Type'&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="s1"&gt;'text/html'&lt;/span&gt;&lt;span class="p"&gt;})&lt;/span&gt;
        &lt;span class="nx"&gt;res&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;sendBody&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;Mustache&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;to_html&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;template&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;action&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;view&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
        &lt;span class="nx"&gt;res&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;finish&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
      &lt;span class="p"&gt;})&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="p"&gt;})&lt;/span&gt;
&lt;span class="p"&gt;}).&lt;/span&gt;&lt;span class="nx"&gt;listen&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;8000&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/pre&gt;
&lt;/div&gt;


&lt;p&gt;We get the template content then renders it when the I/O process is complete. Here's the template:&lt;/p&gt;

&lt;div class="highlight"&gt;
&lt;pre&gt;&lt;span class="cp"&gt;&amp;lt;!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"&lt;/span&gt;
&lt;span class="cp"&gt;  "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"&amp;gt;&lt;/span&gt;

&lt;span class="nt"&gt;&amp;lt;html&lt;/span&gt; &lt;span class="na"&gt;xmlns=&lt;/span&gt;&lt;span class="s"&gt;"http://www.w3.org/1999/xhtml"&lt;/span&gt; &lt;span class="na"&gt;xml:lang=&lt;/span&gt;&lt;span class="s"&gt;"en"&lt;/span&gt; &lt;span class="na"&gt;lang=&lt;/span&gt;&lt;span class="s"&gt;"en"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;head&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;meta&lt;/span&gt; &lt;span class="na"&gt;http-equiv=&lt;/span&gt;&lt;span class="s"&gt;"Content-Type"&lt;/span&gt; &lt;span class="na"&gt;content=&lt;/span&gt;&lt;span class="s"&gt;"text/html; charset=utf-8"&lt;/span&gt;&lt;span class="nt"&gt;/&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;title&amp;gt;&lt;/span&gt;My Framework&lt;span class="nt"&gt;&amp;lt;/title&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/head&amp;gt;&lt;/span&gt;

&lt;span class="nt"&gt;&amp;lt;body&amp;gt;&lt;/span&gt;
  {{title}} spends {{calc}}
&lt;span class="nt"&gt;&amp;lt;/body&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/html&amp;gt;&lt;/span&gt;
&lt;/pre&gt;
&lt;/div&gt;


&lt;p&gt;And that's all! It's running and you can create your blog with it for example.&lt;/p&gt;

&lt;p&gt;There's already a lot of Node.js frameworks that you can find on GitHub if you search for "node". But there's nothing really mature at this time.&lt;/p&gt;

&lt;h2&gt;Update (10 Feb 2010)&lt;/h2&gt;

&lt;p&gt;I forgot to mention that the mustache.js I've used was tweaked. We just have to add this following code at the end of the mustache.js file:&lt;/p&gt;

&lt;div class="highlight"&gt;
&lt;pre&gt;&lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kd"&gt;var&lt;/span&gt; &lt;span class="nx"&gt;name&lt;/span&gt; &lt;span class="k"&gt;in&lt;/span&gt; &lt;span class="nx"&gt;Mustache&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;Object&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;prototype&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;hasOwnProperty&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;call&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;Mustache&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;name&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
        &lt;span class="nx"&gt;exports&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;name&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;Mustache&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;name&lt;/span&gt;&lt;span class="p"&gt;];&lt;/span&gt;
&lt;/pre&gt;
&lt;/div&gt;


&lt;p&gt;This will be merge into the main library in the near future and you &lt;a href="http://github.com/janl/mustache.js/blob/commonjs/lib/mustache.js"&gt;can find the whole file here&lt;/a&gt;&lt;/p&gt;
&lt;/body&gt;&lt;/html&gt;&lt;div class="feedflare"&gt;
&lt;a href="http://feeds.feedburner.com/~ff/fr/boldr?a=3DzCgNKWx8w:4e5-eIeBnR4:7Q72WNTAKBA"&gt;&lt;img src="http://feeds.feedburner.com/~ff/fr/boldr?d=7Q72WNTAKBA" border="0"&gt;&lt;/img&gt;&lt;/a&gt;
&lt;/div&gt;&lt;img src="http://feeds.feedburner.com/~r/fr/boldr/~4/3DzCgNKWx8w" height="1" width="1"/&gt;</description><feedburner:origLink>http://boldr.net/create-a-web-app-with-node</feedburner:origLink></item><item><title>Bookmarks #2</title><link>http://feedproxy.google.com/~r/fr/boldr/~3/QLkV6tr5AOk/bookmarks-2</link><dc:creator xmlns:dc="http://purl.org/dc/elements/1.1/">Nicolas Mérouze</dc:creator><pubDate>Sat, 19 Dec 2009 16:00:00 PST</pubDate><guid isPermaLink="false">tag:boldr.net,2009:bookmarks-2</guid><description>&lt;!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN" "http://www.w3.org/TR/REC-html40/loose.dtd"&gt;
&lt;html&gt;&lt;body&gt;
&lt;p&gt;The rest of my bookmarks. I finally finish to read all my tech &amp;amp; design bookmarks, yay!&lt;/p&gt;

&lt;h2&gt;Ruby&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="http://github.com/josh/MacOnRack"&gt;Mac On Rack&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="http://www.subelsky.com/2009/11/lean-startup-tools-for-rails-apps.html"&gt;Lean startup tools for Rails apps&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="http://ozmm.org/posts/static_sites_with_mustache.html"&gt;Static sites with Mustache&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;Javascript&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="http://desizntech.info/2009/12/15-handpicked-fresh-and-useful-jquery-tutorials/"&gt;15 jQuery plugins (some I like in there)&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="http://ajaxian.com/archives/google-analytics-unblocks-the-web-w-async-support"&gt;Async Google Analytics&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;HTML &amp;amp; CSS&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="http://www.webair.it/blog/2009/07/09/mixing-css3-and-jquery-how-to-css3-effects-via-jquery/"&gt;CSS3 effects with jQuery&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="http://www.smashingmagazine.com/2009/12/02/pushing-your-buttons-with-practical-css3/"&gt;Good looking buttons with CSS3&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="http://desandro.com/resources/css-speech-bubble-icon"&gt;CSS Speech Bubble&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="http://line25.com/articles/15-useful-resources-to-get-clued-up-on-html5"&gt;HTML5 resources&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;Design&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="http://www.instantshift.com/2009/12/03/30-excellent-fresh-free-fonts-for-your-designs"&gt;A lot of great free fonts&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="http://designshack.co.uk/articles/inspiration/10-expert-tips-for-designing-a-one-page-portfolio"&gt;Beautiful portfolios&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="http://fontfabric.com/"&gt;Some other free fonts&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;iPhone&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="http://notepod.net/"&gt;Useful to do wireframes for an iPhone app&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/body&gt;&lt;/html&gt;&lt;div class="feedflare"&gt;
&lt;a href="http://feeds.feedburner.com/~ff/fr/boldr?a=QLkV6tr5AOk:onIoFCqwgfY:7Q72WNTAKBA"&gt;&lt;img src="http://feeds.feedburner.com/~ff/fr/boldr?d=7Q72WNTAKBA" border="0"&gt;&lt;/img&gt;&lt;/a&gt;
&lt;/div&gt;&lt;img src="http://feeds.feedburner.com/~r/fr/boldr/~4/QLkV6tr5AOk" height="1" width="1"/&gt;</description><feedburner:origLink>http://boldr.net/bookmarks-2</feedburner:origLink></item><item><title>Bookmarks #1</title><link>http://feedproxy.google.com/~r/fr/boldr/~3/9re8YGgFDmQ/bookmarks-1</link><dc:creator xmlns:dc="http://purl.org/dc/elements/1.1/">Nicolas Mérouze</dc:creator><pubDate>Wed, 16 Dec 2009 16:00:00 PST</pubDate><guid isPermaLink="false">tag:boldr.net,2009:bookmarks-1</guid><description>&lt;!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN" "http://www.w3.org/TR/REC-html40/loose.dtd"&gt;
&lt;html&gt;&lt;body&gt;
&lt;p&gt;I'm currently reading my long list of bookmarks I hadn't time to read before. I thought it could be a good idea to share with you some of them. There will be other posts like that this week.&lt;/p&gt;

&lt;h2&gt;Ruby&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="http://effectif.com/ruby/manor"&gt;Notes of RubyManor talks&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="http://github.com/botanicus/rails-template-inheritance"&gt;Template Inheritance for Rails&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="http://ohm.keyvalue.org"&gt;ORM for Redis named Ohm&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;
&lt;a href="http://blog.plataformatec.com.br/2009/12/controllers-mount-up-the-plataforma-way/"&gt;How to dry up your controllers&lt;/a&gt; and &lt;a href="http://www.binarylogic.com/2009/10/06/discontinuing-resourcelogic"&gt;a post about why it may not be good to do that&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;&lt;a href="http://frozenplague.net/2009/12/ruby-1-9-1-friends-11-months-on/"&gt;Switch to Ruby 1.9 today&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="http://blog.envylabs.com/2009/12/rubyconf-videos/"&gt;RubyConf 09 in 22 minutes&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="http://foolsworkshop.com/rubycocoa/2008/06/missing-macruby-project-template/"&gt;You don't find MacRuby project template in XCode?&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;Javascript&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="http://quasipartikel.at/tween/"&gt;Tweening with Processing.js&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="http://github.com/suvajitgupta/Tasks"&gt;Task manager with SproutCore&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="http://jsconf.eu/2009/video_nodejs_by_ryan_dahl.html"&gt;Node.js presentation from JSConf.eu 09&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="http://www.viget.com/extend/benchmarking-javascript-templating-libraries"&gt;Benchmark for some Javascript Templating librairies&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="http://www.newmediacampaigns.com/page/nmcformhelper"&gt;jQuery plugin to use HTML5 form attributes&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="http://github.com/creationix/haml-js"&gt;HAML for JS&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;iPhone&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="http://bees4honey.com/blog/marketing/where-to-promote-iphone-app-for-free/"&gt;Promote your app for free&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="http://freeappalert.com/"&gt;Monitor paid apps becoming free&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="http://sdruby.org/podcast/68"&gt;Presentation about Titanium Mobile&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="http://www.marketingyourapp.com/2009/12/how-to-convince-app-store-shoppers-to-buy-your-app/"&gt;Convincing people to buy your app&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;Git&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="http://git-wt-commit.rubyforge.org/"&gt;Some git tools&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;Design&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="http://www.webdesignerdepot.com/2009/12/minimalist-web-design-when-less-is-more/"&gt;How to make minimalist designs&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/body&gt;&lt;/html&gt;&lt;div class="feedflare"&gt;
&lt;a href="http://feeds.feedburner.com/~ff/fr/boldr?a=9re8YGgFDmQ:Vw-2h5CVh4c:7Q72WNTAKBA"&gt;&lt;img src="http://feeds.feedburner.com/~ff/fr/boldr?d=7Q72WNTAKBA" border="0"&gt;&lt;/img&gt;&lt;/a&gt;
&lt;/div&gt;&lt;img src="http://feeds.feedburner.com/~r/fr/boldr/~4/9re8YGgFDmQ" height="1" width="1"/&gt;</description><feedburner:origLink>http://boldr.net/bookmarks-1</feedburner:origLink></item><item><title>Convert HTML presentations to PDF</title><link>http://feedproxy.google.com/~r/fr/boldr/~3/FdxVx7JpLW4/convert-html-presentations-to-pdf</link><dc:creator xmlns:dc="http://purl.org/dc/elements/1.1/">Nicolas Mérouze</dc:creator><pubDate>Thu, 03 Dec 2009 16:00:00 PST</pubDate><guid isPermaLink="false">tag:boldr.net,2009:convert-html-presentations-to-pdf</guid><description>&lt;!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN" "http://www.w3.org/TR/REC-html40/loose.dtd"&gt;
&lt;html&gt;&lt;body&gt;
&lt;p&gt;You're maybe familiar with &lt;a href="http://slideshow.rubyforge.org/"&gt;slideshow&lt;/a&gt; (aka S9) to have your presentation in HTML and to modify it very easily in Textile. For my presentations I now use &lt;a href="http://github.com/nakajima/slidedown"&gt;slidedown&lt;/a&gt; which is very cool too. But my ultimate goal was to convert them to PDF.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;WARNING&lt;/strong&gt;: My solution is build with MacRuby. So install it before trying.&lt;/p&gt;

&lt;p&gt;I finally find a way to do it, thanks to the work of Tom Ward with &lt;a href="http://github.com/tomafro/macruby-snapper/"&gt;Snapper&lt;/a&gt; and Matt Aimonetti with &lt;a href="http://github.com/masterkain/macruby/blob/master/sample-macruby/Scripts/search_to_pdf.rb"&gt;his recent MacRuby sample&lt;/a&gt;. Here's &lt;a href="http://github.com/nmerouze/slidedown-to-pdf"&gt;the script&lt;/a&gt; which is very easy to use:&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;./to_pdf -n 27 http://nmerouze.github.com/blockcampparis09
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;This will convert the presentation to PDF! You can see the result &lt;a href="http://cloud.github.com/downloads/nmerouze/blockcampparis09/slides.pdf"&gt;here&lt;/a&gt;. The generated PDF is named slides.pdf but you can name it yourself in the command line:&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;./to_pdf -n 27 http://nmerouze.github.com/blockcampparis09 myfilename.pdf
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;The -n option is to know how many slides you have in the presentation.&lt;/p&gt;
&lt;/body&gt;&lt;/html&gt;&lt;div class="feedflare"&gt;
&lt;a href="http://feeds.feedburner.com/~ff/fr/boldr?a=FdxVx7JpLW4:OA_4toox4Hc:7Q72WNTAKBA"&gt;&lt;img src="http://feeds.feedburner.com/~ff/fr/boldr?d=7Q72WNTAKBA" border="0"&gt;&lt;/img&gt;&lt;/a&gt;
&lt;/div&gt;&lt;img src="http://feeds.feedburner.com/~r/fr/boldr/~4/FdxVx7JpLW4" height="1" width="1"/&gt;</description><feedburner:origLink>http://boldr.net/convert-html-presentations-to-pdf</feedburner:origLink></item><item><title>jQuery Underscore together</title><link>http://feedproxy.google.com/~r/fr/boldr/~3/ifNM4YedqOg/jquery-underscore-together</link><dc:creator xmlns:dc="http://purl.org/dc/elements/1.1/">Nicolas Mérouze</dc:creator><pubDate>Mon, 30 Nov 2009 16:00:00 PST</pubDate><guid isPermaLink="false">tag:boldr.net,2009:jquery-underscore-together</guid><description>&lt;!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN" "http://www.w3.org/TR/REC-html40/loose.dtd"&gt;
&lt;html&gt;&lt;body&gt;
&lt;p&gt;I like jQuery, but sometimes I miss utility functions from Prototype.js. But with &lt;a href="http://documentcloud.github.com/underscore"&gt;Underscore&lt;/a&gt;, this is over! You can use invoke, first, last, compact... and it's only 2kb packed and gzipped.&lt;/p&gt;

&lt;h2&gt;Example&lt;/h2&gt;

&lt;p&gt;I find it very useful when I get JSON with jQuery. Let's take a Twitter example:&lt;/p&gt;

&lt;div class="highlight"&gt;
&lt;pre&gt;&lt;span class="nx"&gt;$&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;getJSON&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;"http://search.twitter.com/search.json?&amp;amp;q=from:nmerouze&amp;amp;callback=?"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;data&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;
&lt;/pre&gt;
&lt;/div&gt;


&lt;p&gt;To get the results with jQuery you will be very limited: $.each, $.map and that's pretty much all. With Underscore it's easy to build an array of tweets from the results:&lt;/p&gt;

&lt;div class="highlight"&gt;
&lt;pre&gt;&lt;span class="nx"&gt;$&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;getJSON&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;"http://search.twitter.com/search.json?q=from:nmerouze&amp;amp;callback=?"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;data&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;var&lt;/span&gt; &lt;span class="nx"&gt;tweets&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;_&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;pluck&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;data&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;results&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;"text"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;
&lt;/pre&gt;
&lt;/div&gt;


&lt;p&gt;In addition, if you're a Ruby developer you're addicted to first and last on arrays:&lt;/p&gt;

&lt;div class="highlight"&gt;
&lt;pre&gt;&lt;span class="nx"&gt;$&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;getJSON&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;"http://search.twitter.com/search.json?q=from:nmerouze&amp;amp;callback=?"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;data&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;var&lt;/span&gt; &lt;span class="nx"&gt;tweets&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;_&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;pluck&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;data&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;results&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;"text"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="nx"&gt;alert&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;_&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;first&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;tweets&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt; &lt;span class="c1"&gt;// or alert(_.last(tweets));&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;
&lt;/pre&gt;
&lt;/div&gt;


&lt;p&gt;And you have an OOP style too:&lt;/p&gt;

&lt;div class="highlight"&gt;
&lt;pre&gt;&lt;span class="nx"&gt;$&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;getJSON&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;"http://search.twitter.com/search.json?q=from:nmerouze&amp;amp;callback=?"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;data&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;var&lt;/span&gt; &lt;span class="nx"&gt;tweets&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;_&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;data&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;results&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nx"&gt;pluck&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;"text"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="nx"&gt;alert&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;_&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;tweets&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nx"&gt;first&lt;/span&gt;&lt;span class="p"&gt;());&lt;/span&gt; &lt;span class="c1"&gt;// or alert(_(tweets).last());&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;
&lt;/pre&gt;
&lt;/div&gt;


&lt;p&gt;There's a lot of functions (not just for arrays), so take a look at &lt;a href="http://documentcloud.github.com/underscore"&gt;the documentation&lt;/a&gt; which is great!&lt;/p&gt;

&lt;h2&gt;A final library&lt;/h2&gt;

&lt;p&gt;Now I have my utility functions, I want to use classes. I found a &lt;a href="http://github.com/grockit/june/blob/master/vendor/foundation.js"&gt;little Javascript lib&lt;/a&gt; from &lt;a href="http://github.com/grockit/june"&gt;June&lt;/a&gt; to do that in a way I like. It's very small and there's just what I need: modules, classes, methods and mixins.&lt;/p&gt;

&lt;div class="highlight"&gt;
&lt;pre&gt;&lt;span class="nx"&gt;module&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;"Boldr"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;c&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="kd"&gt;with&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;c&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;module&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;"Hello"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nx"&gt;def&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;"hello"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="nx"&gt;alert&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;"hello "&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;name&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="p"&gt;});&lt;/span&gt;
  &lt;span class="p"&gt;});&lt;/span&gt;

  &lt;span class="nx"&gt;constructor&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;"MyClass"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nx"&gt;include&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;Boldr&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;Hello&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

    &lt;span class="nx"&gt;def&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;"initialize"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;name&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;name&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;name&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="p"&gt;});&lt;/span&gt;
  &lt;span class="p"&gt;});&lt;/span&gt;
&lt;span class="p"&gt;}});&lt;/span&gt;

&lt;span class="kd"&gt;var&lt;/span&gt; &lt;span class="nx"&gt;myClass&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nx"&gt;Boldr&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;MyClass&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;"world"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="nx"&gt;myClass&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;hello&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;/pre&gt;
&lt;/div&gt;


&lt;p&gt;I'm in a more familiar world here. There's &lt;a href="http://jsclass.jcoglan.com/"&gt;JS.Class&lt;/a&gt; to do that too but I don't need all the core functions.&lt;/p&gt;

&lt;h2&gt;Conclusion&lt;/h2&gt;

&lt;p&gt;Now I have a fast and small way to write my Javascript and it's perfect (for now anyway).&lt;/p&gt;
&lt;/body&gt;&lt;/html&gt;&lt;div class="feedflare"&gt;
&lt;a href="http://feeds.feedburner.com/~ff/fr/boldr?a=ifNM4YedqOg:Z7KuOJcx5yY:7Q72WNTAKBA"&gt;&lt;img src="http://feeds.feedburner.com/~ff/fr/boldr?d=7Q72WNTAKBA" border="0"&gt;&lt;/img&gt;&lt;/a&gt;
&lt;/div&gt;&lt;img src="http://feeds.feedburner.com/~r/fr/boldr/~4/ifNM4YedqOg" height="1" width="1"/&gt;</description><feedburner:origLink>http://boldr.net/jquery-underscore-together</feedburner:origLink></item><item><title>My JRuby talk at BlockCampParis</title><link>http://feedproxy.google.com/~r/fr/boldr/~3/lLpsuBX9_6k/my-jruby-talk-at-blockcampparis</link><dc:creator xmlns:dc="http://purl.org/dc/elements/1.1/">Nicolas Mérouze</dc:creator><pubDate>Fri, 27 Nov 2009 16:00:00 PST</pubDate><guid isPermaLink="false">tag:boldr.net,2009:my-jruby-talk-at-blockcampparis</guid><description>&lt;!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN" "http://www.w3.org/TR/REC-html40/loose.dtd"&gt;
&lt;html&gt;&lt;body&gt;
&lt;p&gt;I was at &lt;a href="http://boldr.net/blockcampparis"&gt;BlockCampParis&lt;/a&gt; today and I gave a talk about JRuby. I'm not a JRuby expert so it was not very technical but the thing I wanted to demonstrate was that JRuby is a viable Ruby implementation with the possibility to use a lot of great librairies from Java.&lt;/p&gt;

&lt;p&gt;I hope everyone enjoyed the talk and I'll probably complete it for a next time. You can see the presentation &lt;a href="http://nmerouze.github.com/blockcampparis09"&gt;here&lt;/a&gt; and &lt;a href="http://github.com/nmerouze/blockcampparis09"&gt;play with the examples&lt;/a&gt;.&lt;/p&gt;
&lt;/body&gt;&lt;/html&gt;&lt;div class="feedflare"&gt;
&lt;a href="http://feeds.feedburner.com/~ff/fr/boldr?a=lLpsuBX9_6k:AeqDI3q2vAA:7Q72WNTAKBA"&gt;&lt;img src="http://feeds.feedburner.com/~ff/fr/boldr?d=7Q72WNTAKBA" border="0"&gt;&lt;/img&gt;&lt;/a&gt;
&lt;/div&gt;&lt;img src="http://feeds.feedburner.com/~r/fr/boldr/~4/lLpsuBX9_6k" height="1" width="1"/&gt;</description><feedburner:origLink>http://boldr.net/my-jruby-talk-at-blockcampparis</feedburner:origLink></item><item><title>BlockCampParis</title><link>http://feedproxy.google.com/~r/fr/boldr/~3/FWv6O40Yy2g/blockcampparis</link><dc:creator xmlns:dc="http://purl.org/dc/elements/1.1/">Nicolas Mérouze</dc:creator><pubDate>Sat, 21 Nov 2009 16:00:00 PST</pubDate><guid isPermaLink="false">tag:boldr.net,2009:blockcampparis</guid><description>&lt;!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN" "http://www.w3.org/TR/REC-html40/loose.dtd"&gt;
&lt;html&gt;&lt;body&gt;
&lt;p&gt;&lt;img src="http://boldr.net/images/assets/logo_blockcamp.jpg" alt="BlockCampParis"&gt;&lt;/p&gt;

&lt;p&gt;There's gonna be a Smalltalk and Ruby Barcamp named BlockCampParis on November 28th. I will talk about JRuby and the (many) things you can do with it.&lt;/p&gt;

&lt;p&gt;Some links:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="http://barcamp.org/BlockCampParis"&gt;BarCamp page&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="http://blockcampparis.heroku.com"&gt;Subscriptions&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;More details on &lt;a href="http://www.rubyfrance.org/articles/2009/11/19/barcamp-ruby-et-smalltalk-blockcamp-paris-samedi-28-novembre-2009"&gt;RubyFrance&lt;/a&gt; and &lt;a href="http://www.esug.org/About/ESUGblog/BlockCamp+Paris+2009?_s=pamK14eQVeGc0NfF&amp;amp;_k=NP5taBZF&amp;amp;_n&amp;amp;18"&gt;ESUG&lt;/a&gt; websites&lt;/li&gt;
&lt;/ul&gt;
&lt;/body&gt;&lt;/html&gt;&lt;div class="feedflare"&gt;
&lt;a href="http://feeds.feedburner.com/~ff/fr/boldr?a=FWv6O40Yy2g:S5yofoKYFZc:7Q72WNTAKBA"&gt;&lt;img src="http://feeds.feedburner.com/~ff/fr/boldr?d=7Q72WNTAKBA" border="0"&gt;&lt;/img&gt;&lt;/a&gt;
&lt;/div&gt;&lt;img src="http://feeds.feedburner.com/~r/fr/boldr/~4/FWv6O40Yy2g" height="1" width="1"/&gt;</description><feedburner:origLink>http://boldr.net/blockcampparis</feedburner:origLink></item><item><title>Relaunch again</title><link>http://feedproxy.google.com/~r/fr/boldr/~3/x1bGCPP3Iys/relaunch-again</link><dc:creator xmlns:dc="http://purl.org/dc/elements/1.1/">Nicolas Mérouze</dc:creator><pubDate>Sat, 14 Nov 2009 16:00:00 PST</pubDate><guid isPermaLink="false">tag:boldr.net,2009:relaunch-again</guid><description>&lt;!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN" "http://www.w3.org/TR/REC-html40/loose.dtd"&gt;
&lt;html&gt;&lt;body&gt;
&lt;p&gt;I'm relauching Boldr... again.&lt;/p&gt;

&lt;p&gt;I have not much to say on the subject except that I will write about more things than just Ruby/Rails, because I'm trying many things not related to Ruby so it would be a waste not to write about these things.&lt;/p&gt;

&lt;p&gt;And if you are wondering why the post is in english it's because Boldr is no longer in french.&lt;/p&gt;
&lt;/body&gt;&lt;/html&gt;&lt;div class="feedflare"&gt;
&lt;a href="http://feeds.feedburner.com/~ff/fr/boldr?a=x1bGCPP3Iys:d_ihw9tUPqw:7Q72WNTAKBA"&gt;&lt;img src="http://feeds.feedburner.com/~ff/fr/boldr?d=7Q72WNTAKBA" border="0"&gt;&lt;/img&gt;&lt;/a&gt;
&lt;/div&gt;&lt;img src="http://feeds.feedburner.com/~r/fr/boldr/~4/x1bGCPP3Iys" height="1" width="1"/&gt;</description><feedburner:origLink>http://boldr.net/relaunch-again</feedburner:origLink></item></channel></rss>

