We often discuss the benefits of Innovation Accounting which is also known as Lean Startup with our clients. We experimented with different forms of learning - from plain old talking about the subjects and sketching the principles to presentations, we always had the feeling that there had to be a better way. Especially since the great ideas we help develop by facilitating idea development workshops will become even more valuable if you apply these hard to explain principles on top. Sven clustering ideas produced in a workshop The slides Sven prepared seemed to be interesting judging by the numbers on slideshare. But still the reactions when we gave that talk did not totally satisfy us. When Sven and Katrin of CoCreact sketched a workshop on the subject at Play for Agile 2013 we immediately knew that was the missing link between our experience and delivering it to our clients. Workshop participants working on their idea iteratively This new workshop we’re talking about simulates several iterations, so the agile and lean product development mindset is not only a subject that’s discussed, but an actual hands on experience that’s undergone by the participants. Ideas are developed, presented to potential customers and reworked several times. How we achieve this? Strict time-boxing of the iterations in a sand-box created by our experience in iterative product/campaign/idea design. Groups interchange their new ideas switching vendor and client roles The pictures originate from something we do before including a workshop in our portfolio: a final prototype workshop. We believe in iterative idea development so much that we apply it to the workshop on the topic too. It was quite a success although we made it hard for the participants by selecting them from very different expertise: advertising, strategic consulting and even human resources. We always advocate diverse teams, but to see how the groups that in some cases came from completely different working domains performed surprised even us. Diverse people - diverse footwear We were making a lot of things happen since the last blog post which is almost a year old. This workshop is one of our personal highlights since it manages to deliver the benefits of Lean Product Developement. We’re just preparing the next lean product development workshop for a client which Sven guided in developing ideas before. Now we’ll let them rinse and repeat the accelerated feedback loop technique described above. It’s a great way to see why we also call it Innovation Accounting: you have a timebox/budget, you deliver a lean increment to the customer once you run out of time/budget, you ship. Then you weave in the insights of your customer’s feedback into the next iteration. Accountants love this for the reduced risk, and we love the term Innovation Accounting since it underlines that Lean Startup is also a more than interesting process for mature companies.

The infinite chocolate trick is a nifty optical illusion, here’s how it works. This chocolate bar has $ m $ rows and $ n $ columns <!-- rows --> <!-- cols --> <!-- text --> m rows n cols We want to remove the top left piece, with length $ x $ and breadth $ y $. <!-- rows --> <!-- cols --> <!-- piece --> <!-- text --> x y m.x n.y We cut the bar into 4 components the piece we want to remove the column the piece is from a diagonal cut across the bar from the base and the large piece thats left over <!-- figure 1--> <!-- text --> 1 2 3 4 x y a b c d f <!-- figure 2 --> <!-- text --> 2 3 4 c b f d a When swapped, the tops of 4 and 2 align perfectly For this to happen, the left side of 2 (a) has to equal the right side of 4 (d) Since $ a = d $ and $ a + x = m \cdot x $ $\Rightarrow d + x = m \cdot x $ Since $ d + c = m \cdot x $ $\Rightarrow c = x $ We now know how to cut our chocolate bar to make infinite chocolate The Illusion The reason this illusion works is because the final chocolate bar isn’t that much smaller than the original. <!-- figure 1--> <!-- text --> x y (n-1)y (m-1)x x (m-1)x f g y x <!-- figure 2 --> <!-- text --> x n.y f y g (m-1)x The final length of the chocolate is $ x + f $ We also know that $ x + f + g = m \cdot x $ $ \Rightarrow g = m \cdot x - (x + f) $ Which means the new chocolate bar is only $ g $ units shorter than the original $ g = y \cdot tan(\theta) $ [$ \theta $ being the angle made by the lower wedge] $ g = y \cdot \frac{x}{n \cdot y} $ [trigonometry] $ g = \frac{x}{n} $ The area for the missing piece is taken as a sliver from the top row. The length of the sliver is equal to the length of the piece you want to remove divided by the number of columns An Alternate Solution (w/o trig) The total area of the chocolate bar is $ m \cdot x \times n \cdot y $ The area of the piece we remove is $ x \times y $ So the area thats left $ = m \cdot x \times n \cdot y - ( x \times y ) $ Dividing by the breadth, which doesn’t change, we get the new length $ \textrm{new length} = \frac{ m \cdot x \times n \cdot y - ( x \times y ) }{n \cdot y} $ $ \textrm{new length} = m \cdot x - \frac{x}{n} $ $ \textrm{new length} = \textrm{old length} - \frac{x}{n} $ Where $ \frac{x}{n} $ is the same as $ g $ in the first solution, the difference in lengths

The infinite chocolate trick is a nifty optical illusion, here’s how it works. This chocolate bar has $ m $ rows and $ n $ columns <!-- rows --> <!-- cols --> <!-- text --> m rows n cols We want to remove the top left piece, with length $ x $ and breadth $ y $. <!-- rows --> <!-- cols --> <!-- piece --> <!-- text --> x y m.x n.y We cut the bar into 4 components the piece we want to remove the column the piece is from a diagonal cut across the bar from the base and the large piece thats left over <!-- figure 1--> <!-- text --> 1 2 3 4 x y a b c d f <!-- figure 2 --> <!-- text --> 2 3 4 c b f d a When swapped, the tops of 4 and 2 align perfectly For this to happen, the left side of 2 (a) has to equal the right side of 4 (d) Since $ a = d $ and $ a + x = m \cdot x $ $\Rightarrow d + x = m \cdot x $ Since $ d + c = m \cdot x $ $\Rightarrow c = x $ We now know how to cut our chocolate bar to make infinite chocolate The Illusion The reason this illusion works is because the final chocolate bar isn’t that much smaller than the original. <!-- figure 1--> <!-- text --> x y (n-1)y (m-1)x x (m-1)x f g y x <!-- figure 2 --> <!-- text --> x n.y f y g (m-1)x The final length of the chocolate is $ x + f $ We also know that $ x + f + g = m \cdot x $ $ \Rightarrow g = m \cdot x - (x + f) $ Which means the new chocolate bar is only $ g $ units shorter than the original $ g = y \cdot tan(\theta) $ [$ \theta $ being the angle made by the lower wedge] $ g = y \cdot \frac{x}{n \cdot y} $ [trigonometry] $ g = \frac{x}{n} $ The area for the missing piece is taken as a sliver from the top row. The length of the sliver is equal to the length of the piece you want to remove divided by the number of columns An Alternate Solution (w/o trig) The total area of the chocolate bar is $ m \cdot x \times n \cdot y $ The area of the piece we remove is $ x \times y $ So the area thats left $ = m \cdot x \times n \cdot y - ( x \times y ) $ Dividing by the breadth, which doesn’t change, we get the new length $ \textrm{new length} = \frac{ m \cdot x \times n \cdot y - ( x \times y ) }{n \cdot y} $ $ \textrm{new length} = m \cdot x - \frac{x}{n} $ $ \textrm{new length} = \textrm{old length} - \frac{x}{n} $ Where $ \frac{x}{n} $ is the same as $ g $ in the first solution, the difference in lengths

Happy to announce a new release of json-proxy, a utility for HTML5 devs to run apps locally and proxy calls like http://localhost:9000/api to a remote server, all without CORS or JSONP. Grunt Plugin This release includes better support for running as a grunt plugin. A change in grunt-contrib-connect@0.8.0 simplifies life for proxy plugins inside the livereload task of grunt serve: 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 livereload: { options: { middleware: function(connect, options, middlewares) { // inject json-proxy to the front of the default middlewares array // requires grunt-contrib-connect v0.8.0+ middlewares.unshift( require('json-proxy').initialize({ proxy: { forward: { '/api/': 'http://api.example.com:8080' }, headers: { 'X-Forwarded-User': 'John Doe' } } }) ); return middlewares; } } } SSL Endpoints This release adds support for proxying to HTTPS endpoints. Here’s a sample config to forward http://localhost:9000/channel to https://www.youtube.com/channel . 1 2 3 4 5 6 7 { "proxy": { "forward": { "/channel": "https://www.youtube.com:443" } } } HTTP Proxy Gateways and Basic Authentication You can now pass your authentication credentials to a HTTP proxy gateway on your LAN via the proxy.gateway.auth config setting. The setting value uses the username:password format for HTTP basic authentication (without base64 encoding). Here’s an example config to proxying remote request via http://proxy.example.com:8080 as proxyuser with password C0mp13x_!d0rd$$@P! 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 var config = { "proxy": { "gateway": { "protocol: "http:", "host": "proxy.example.com", "port": 8080, "auth": "proxyuser:C0mp13x_!d0rd$$@P!" /** 'user:password' **/ }, "forward": { "/api": "http://api.example.com", "/foo/\\d+/bar": "http://www.example.com", "/secure/": "https://secure.example.com" } } }; Upgrade to NodeJitsu http-proxy v1.1 This release required heavy refactoring to use the latest bits of Nodejitsu’s http-proxy v1.1 This was necessary since version prior to 1.0 are no longer actively supported. Housekeeping There’s better unit test coverage, and the code validates against a reasonable set of jshint linting rules.

I usually write my JavaScript with CoffeeScript, it makes it look a lot cleaner and CoffeeScript also adds some nice syntactic sugar. Another common issue I face is organizing JS code about which I have written a few blogs already. Lately, I also read about Browser ...

I am a big believer in HTML5 in general, but the HTML5 video is one of my favourites due to my love of movies. And today I learned something new from the Mozilla Hacks blog.

crntaylor not so recently posted his mathematician’s fizzbuzz question on HN A jar has 1000 coins, of which 999 are fair and 1 is double headed. Pick a coin at random, and toss it 10 times. Given that you see 10 heads, what is the probability that the next toss of that coin is also a head? The Setup Pick a random coin out of the jar, whats the probability its the double headed coin? Well, there are a 1000 coins in the jar, theres only 1 double headed coin. So the probability of picking it is $ \frac{1}{1000} $ $$ \textrm { the probability of picking a double headed coin is } \frac{1}{1000} $$ $$ P( \textrm{ double headed coin } ) = \frac{1}{1000} $$ I don’t have any more information about how the coins are arranged so I’ll assume that I’m equally likely to pick any of them. This assumption can be wrong but theres not enough information to make a better guess. Adding Information Flip the coin, it lands tails, aha! more information. I now know that the coin I picked can’t be the double headed coin. A double headed coin can never land tails. $$ \textrm { the probability of tails given a double headed coin is } 0 $$ $$ P( \textrm{ tails } | \textrm{ double headed coin } ) = 0 $$ What about if it landed heads? Now I can’t tell for sure whether I have the double headed coin but I still have more information. Since I haven’t gotten a tails, the probability of having picked the double headed coin goes up. To find out by just how much I use Bayes’ rule Bayes’ rule $$ P(\textrm{ event }|\textrm{ evidence }) = \frac{P(\textrm{ evidence }|\textrm{ event }) \cdot P(\textrm{ event })}{P(\textrm{ evidence })} $$ $$ \textrm { or } $$ $$ P(\textrm{ double headed coin }|\textrm{ heads }) = \frac{P(\textrm{ heads }|\textrm{ double headed coin })P(\textrm{ double headed coin })}{P(\textrm{ heads })} $$ This might be completely unintuitive, check out the link above, it really helped me understand the concept, for now take it as fact and move on. In this case, the event I care about is picking the double headed coin, the evidence is the flip landing heads. The Evidence $ P(\textrm{ heads } ) $ is the probability of seeing a heads at all, whichever coin I picked. Thats equal to the probability of picking the double headed coin and landing a heads plus picking a fair coin and landing a heads. $$ P(\textrm{ heads } ) $$ $$ = P(\textrm{ double headed coin & heads }) + P(\textrm{ fair coin & heads }) $$ $$ = P(\textrm{ heads } | \textrm{ double headed coin }) \cdot P(\textrm{ double headed coin }) + P(\textrm{ heads } | \textrm{ fair coin }) \cdot P(\textrm{ fair coin })$$ $$ = 1 \cdot \frac{1}{1000} + \frac{1}{2} \cdot \frac{999}{1000}$$ $$ = 0.5005 $$ Which is slightly higher than $P(\textrm{ heads } | \textrm{ fair coin })$, after all, 999 out of 1000 coins in the jar are fair. (For an explanation of what just happened check out these Khan Academy videos) Picking the Double Headed Coin $$ P(\textrm{ double headed coin }| \textrm{ heads }) $$ $$ = \frac{P(\textrm{ heads }|\textrm{ double headed coin }) \cdot P(\textrm{ double headed coin })}{P(\textrm{ heads })} $$ $$ = \frac{1 \cdot \frac{1}{1000}}{1 \cdot \frac{1}{1000} + \frac{1}{2} \cdot \frac{999}{1000}} $$ $$ = \frac{2}{1001} $$ $$ = 0.0019 $$ Compared to the original probability of picking the double headed coin of $ 0.001 $ this isn’t a lot higher but then I’ve only flipped it once. Plugging in the values for 10 heads in a row. $$ P(\textrm{ double headed coin }| \textrm{ 10 heads }) $$ $$ = \frac{P(\textrm{ 10 heads }|\textrm{ double headed coin }) \cdot P(\textrm{ double headed coin })}{P(\textrm{ 10 heads })} $$ $$ = \frac{1 \cdot \frac{1}{1000}}{1 \cdot \frac{1}{1000} + (\frac{1}{2})^{10} \cdot \frac{999}{1000}} $$ $$ = \frac{1024}{2023} $$ $$ = 0.506 \textrm{ (or a little over 50%) } $$ Updating Posteriors Before I flipped anything, $P(\textrm{ heads })$ was 0.5005, only slightly higher than flipping a fair coin. Now after seeing 10 Heads in a row I must update my beliefs about the coin in my hand. My posterior becomes my new prior. $$ P(\textrm{ double headed coin }| \textrm{ 10 heads }) \Rightarrow P(\textrm{ double headed coin }) $$ $$ P(\textrm{ fair coin }| \textrm{ 10 heads }) \Rightarrow P(\textrm{ fair coin })$$ The 11th Flip Plugging the updated probabilities into the formula for $ P(\textrm{ heads }) $ $$ = P(\textrm{ double headed coin & heads }) + P(\textrm{ fair coin & heads }) $$ $$ = P(\textrm{ heads } | \textrm{ double headed coin }) \cdot P(\textrm{ double headed coin }) + P(\textrm{ heads } | \textrm{ fair coin }) \cdot P(\textrm{ fair coin })$$ $$ = 1 \cdot \frac{1024}{2023} + \frac{1}{2} \cdot \frac{999}{2023}$$ $$ = 0.753 $$ TLDR Initially, there was a small chance I was holding the double headed coin but with every flip landing heads I could no longer hold onto that belief. Bayes’ Rule quantifies my shift in belief with every additional flip, away from believing that I hold a fair coin closer to the belief that I hold the double headed coin.

crntaylor not so recently posted his mathematician’s fizzbuzz question on HN A jar has 1000 coins, of which 999 are fair and 1 is double headed. Pick a coin at random, and toss it 10 times. Given that you see 10 heads, what is the probability that the next toss of that coin is also a head? The Setup Pick a random coin out of the jar, whats the probability its the double headed coin? Well, there are a 1000 coins in the jar, theres only 1 double headed coin. So the probability of picking it is $ \frac{1}{1000} $ $$ \textrm { the probability of picking a double headed coin is } \frac{1}{1000} $$ $$ P( \textrm{ double headed coin } ) = \frac{1}{1000} $$ I don’t have any more information about how the coins are arranged so I’ll assume that I’m equally likely to pick any of them. This assumption can be wrong but theres not enough information to make a better guess. Adding Information Flip the coin, it lands tails, aha! more information. I now know that the coin I picked can’t be the double headed coin. A double headed coin can never land tails. $$ \textrm { the probability of tails given a double headed coin is } 0 $$ $$ P( \textrm{ tails } | \textrm{ double headed coin } ) = 0 $$ What about if it landed heads? Now I can’t tell for sure whether I have the double headed coin but I still have more information. Since I haven’t gotten a tails, the probability of having picked the double headed coin goes up. To find out by just how much I use Bayes’ rule Bayes’ rule $$ P(\textrm{ event }|\textrm{ evidence }) = \frac{P(\textrm{ evidence }|\textrm{ event }) \cdot P(\textrm{ event })}{P(\textrm{ evidence })} $$ $$ \textrm { or } $$ $$ P(\textrm{ double headed coin }|\textrm{ heads }) = \frac{P(\textrm{ heads }|\textrm{ double headed coin })P(\textrm{ double headed coin })}{P(\textrm{ heads })} $$ This might be completely unintuitive, check out the link above, it really helped me understand the concept, for now take it as fact and move on. In this case, the event I care about is picking the double headed coin, the evidence is the flip landing heads. The Evidence $ P(\textrm{ heads } ) $ is the probability of seeing a heads at all, whichever coin I picked. Thats equal to the probability of picking the double headed coin and landing a heads plus picking a fair coin and landing a heads. $$ P(\textrm{ heads } ) $$ $$ = P(\textrm{ double headed coin & heads }) + P(\textrm{ fair coin & heads }) $$ $$ = P(\textrm{ heads } | \textrm{ double headed coin }) \cdot P(\textrm{ double headed coin }) + P(\textrm{ heads } | \textrm{ fair coin }) \cdot P(\textrm{ fair coin })$$ $$ = 1 \cdot \frac{1}{1000} + \frac{1}{2} \cdot \frac{999}{1000}$$ $$ = 0.5005 $$ Which is slightly higher than $P(\textrm{ heads } | \textrm{ fair coin })$, after all, 999 out of 1000 coins in the jar are fair. (For an explanation of what just happened check out these Khan Academy videos) Picking the Double Headed Coin $$ P(\textrm{ double headed coin }| \textrm{ heads }) $$ $$ = \frac{P(\textrm{ heads }|\textrm{ double headed coin }) \cdot P(\textrm{ double headed coin })}{P(\textrm{ heads })} $$ $$ = \frac{1 \cdot \frac{1}{1000}}{1 \cdot \frac{1}{1000} + \frac{1}{2} \cdot \frac{999}{1000}} $$ $$ = \frac{2}{1001} $$ $$ = 0.0019 $$ Compared to the original probability of picking the double headed coin of $ 0.001 $ this isn’t a lot higher but then I’ve only flipped it once. Plugging in the values for 10 heads in a row. $$ P(\textrm{ double headed coin }| \textrm{ 10 heads }) $$ $$ = \frac{P(\textrm{ 10 heads }|\textrm{ double headed coin }) \cdot P(\textrm{ double headed coin })}{P(\textrm{ 10 heads })} $$ $$ = \frac{1 \cdot \frac{1}{1000}}{1 \cdot \frac{1}{1000} + (\frac{1}{2})^{10} \cdot \frac{999}{1000}} $$ $$ = \frac{1024}{2023} $$ $$ = 0.506 \textrm{ (or a little over 50%) } $$ Updating Posteriors Before I flipped anything, $P(\textrm{ heads })$ was 0.5005, only slightly higher than flipping a fair coin. Now after seeing 10 Heads in a row I must update my beliefs about the coin in my hand. My posterior becomes my new prior. $$ P(\textrm{ double headed coin }| \textrm{ 10 heads }) \Rightarrow P(\textrm{ double headed coin }) $$ $$ P(\textrm{ fair coin }| \textrm{ 10 heads }) \Rightarrow P(\textrm{ fair coin })$$ The 11th Flip Plugging the updated probabilities into the formula for $ P(\textrm{ heads }) $ $$ = P(\textrm{ double headed coin & heads }) + P(\textrm{ fair coin & heads }) $$ $$ = P(\textrm{ heads } | \textrm{ double headed coin }) \cdot P(\textrm{ double headed coin }) + P(\textrm{ heads } | \textrm{ fair coin }) \cdot P(\textrm{ fair coin })$$ $$ = 1 \cdot \frac{1024}{2023} + \frac{1}{2} \cdot \frac{999}{2023}$$ $$ = 0.753 $$ TLDR Initially, there was a small chance I was holding the double headed coin but with every flip landing heads I could no longer hold onto that belief. Bayes’ Rule quantifies my shift in belief with every additional flip, away from believing that I hold a fair coin closer to the belief that I hold the double headed coin.

SailsJS is great for realtime applications and as I play more with it, I like it more. What is interesting to me is, how easy/difficult to use/create APIs and query language in SailsJS.Problem statementWith this intent I would like to show how to build a small Address Book application (finder), that allows users to find contacts by name, phone or address.My Approach (build it in 60 mins!)Install node.js and sails.js (10 mins)Create new Sails application (5 mins)Understanding routes and defining custom routes (5 mins)Define Schema and perform CRUD operations (10 mins)Setup custom Layout (with bootstrap) and kill the default one (10 mins)Get search form ready (10 mins)Add search action to controller (5 mins)Add finder query in model (5 mins)Demo time!P.S. Team @kiprosh did it in 40 mins :)Install node.js and sails.js (10 mins)It literally took me 7 mins to install node.js on mac and 3 mins to install sails.js :)Create new Sails application (5 mins)Run below command to install new sails application, let's say address book:sails new address bookand start sails serversails liftTip#1 : You can do sails console too :)Tip#2 : Every time you make a change in controller/model/anything-in-api , you need to restart the server OR you can use forever as "forever -w start app.js", but that's for later :)Understanding routes and defining custom routes (5 mins)If you are familiar with Rails, it will be piece of cake for you. Checkout addressbook/config/routes.js and add custom routes in that file.Here are tips for static routes:To show only view, let's say root page of our application:'/': { view: 'addressbook/index' }, To define actions for get/post/put/delete http verbs:'get /addressbook/search': 'AddressbookController.search'// where addressbook/search route will be served by AddressbookController's search actionAlso checkout crud blueprints in the same routes.js file.Define Schema and perform CRUD operations (10 mins)You can add adapter of your favorite database to config/adapters.js. Default is module.exports.adapters = { 'default': 'disk', disk: { module: 'sails-disk' // this database is accessible at addressbook/.tmp/disk.db file },....}Defining your models are even simpler. Let's say you want to create addressbook/api/models/AddressBook.js model, here is how you can define it's schema:module.exports = { attributes: { name: 'string', address: 'string', phone: 'string' //Any instance method would go here }, //Any class method would go here...} To do crud operations, I would encourage use chrome extension called postman. Simply install this extension and launch app. With AddressBook model created, in postman you can do things like:GET http://localhost:1337/addressbook // to list all contactsPOST http://localhost:1337/addressbook // to create a contact, add name, address, phone and post itDELETE http://localhost:1337/addressbook?id=3 // to delete a contact with id = 3With postman handy, I would skip building CRUD UI and move over to search feature.Setup custom Layout (with bootstrap) and kill the default one (10 mins)Layout of our application resides in addressbook/views/layout.ejs, so go and open it up. You can download bootstrap and add it to link css and include bootstrap.jsAdd CSS files in addressbook/assets/styles directory, add JS files in addressbook/assets/js directory and give links in layout.ejs ...<script src="/js/bootstrap.min.js" type="text/javascript"></script><script src="/js/app.js" type="text/javascript"></script><script src="/js/custom.js" type="text/javascript"></script>Somewhere near body tag you would notice <%- body %>, this will include view file for current controller/action. Since in routes section above, we have added default(root) route asview: 'addressbook/index'We need to add something to addressbook/views/index.ejs file, so that by default our custom text would appear, let's say:My Addressbook Note: 'ejs' is templating engine and you can use engine of your own choice as well.Get search form ready (10 mins)Add search form to addressbook/views/addressbook/index.ejs add your own way of building a form.Add search action to controller (5 mins)Add Search action to addressbook/api/controllers/AddressbookController.jsAdd finder query in model (5 mins)Add getSearchResults function to model addressbook/api/models/AddressBook.js I am using 'contains' search on name, address and phone, you can play with it and do something like, starts with, exact match, etc :)Demo time!It would be great if you can show a demo app or may be share repos, so that even I can learn from it :) Mine is here: https://github.com/gouravtiwari/addressbookI hope you enjoyed building this toy application. As I said SailsJS is great for realtime apps, so go ahead and play with it.

SailsJS is great for realtime applications and as I play more with it, I like it more. What is interesting to me is, how easy/difficult to use/create APIs and query language in SailsJS.Problem statementWith this intent I would like to show how to build a small Address Book application (finder), that allows users to find contacts by name, phone or address.My Approach (build it in 60 mins!)Install node.js and sails.js (10 mins)Create new Sails application (5 mins)Understanding routes and defining custom routes (5 mins)Define Schema and perform CRUD operations (10 mins)Setup custom Layout (with bootstrap) and kill the default one (10 mins)Get search form ready (10 mins)Add search action to controller (5 mins)Add finder query in model (5 mins)Demo time!P.S. Team @kiprosh did it in 40 mins :)Install node.js and sails.js (10 mins)It literally took me 7 mins to install node.js on mac and 3 mins to install sails.js :)Create new Sails application (5 mins)Run below command to install new sails application, let's say address book:sails new address bookand start sails serversails liftTip#1 : You can do sails console too :)Tip#2 : Every time you make a change in controller/model/anything-in-api , you need to restart the server OR you can use forever as "forever -w start app.js", but that's for later :)Understanding routes and defining custom routes (5 mins)If you are familiar with Rails, it will be piece of cake for you. Checkout addressbook/config/routes.js and add custom routes in that file.Here are tips for static routes:To show only view, let's say root page of our application:'/': { view: 'addressbook/index' }, To define actions for get/post/put/delete http verbs:'get /addressbook/search': 'AddressbookController.search'// where addressbook/search route will be served by AddressbookController's search actionAlso checkout crud blueprints in the same routes.js file.Define Schema and perform CRUD operations (10 mins)You can add adapter of your favorite database to config/adapters.js. Default is module.exports.adapters = { 'default': 'disk', disk: { module: 'sails-disk' // this database is accessible at addressbook/.tmp/disk.db file },....}Defining your models are even simpler. Let's say you want to create addressbook/api/models/AddressBook.js model, here is how you can define it's schema:module.exports = { attributes: { name: 'string', address: 'string', phone: 'string' //Any instance method would go here }, //Any class method would go here...} To do crud operations, I would encourage use chrome extension called postman. Simply install this extension and launch app. With AddressBook model created, in postman you can do things like:GET http://localhost:1337/addressbook // to list all contactsPOST http://localhost:1337/addressbook // to create a contact, add name, address, phone and post itDELETE http://localhost:1337/addressbook?id=3 // to delete a contact with id = 3With postman handy, I would skip building CRUD UI and move over to search feature.Setup custom Layout (with bootstrap) and kill the default one (10 mins)Layout of our application resides in addressbook/views/layout.ejs, so go and open it up. You can download bootstrap and add it to link css and include bootstrap.jsAdd CSS files in addressbook/assets/styles directory, add JS files in addressbook/assets/js directory and give links in layout.ejs ...<script src="/js/bootstrap.min.js" type="text/javascript"></script><script src="/js/app.js" type="text/javascript"></script><script src="/js/custom.js" type="text/javascript"></script>Somewhere near body tag you would notice <%- body %>, this will include view file for current controller/action. Since in routes section above, we have added default(root) route asview: 'addressbook/index'We need to add something to addressbook/views/index.ejs file, so that by default our custom text would appear, let's say:My Addressbook Note: 'ejs' is templating engine and you can use engine of your own choice as well.Get search form ready (10 mins)Add search form to addressbook/views/addressbook/index.ejs add your own way of building a form.Add search action to controller (5 mins)Add Search action to addressbook/api/controllers/AddressbookController.jsAdd finder query in model (5 mins)Add getSearchResults function to model addressbook/api/models/AddressBook.js I am using 'contains' search on name, address and phone, you can play with it and do something like, starts with, exact match, etc :)Demo time!It would be great if you can show a demo app or may be share repos, so that even I can learn from it :) Mine is here: https://github.com/gouravtiwari/addressbookI hope you enjoyed building this toy application. As I said SailsJS is great for realtime apps, so go ahead and play with it.