Mercurial > browser-couch
changeset 95:86158b61b732 blog-post
Reworked the tutorial so that we don't have to have each snippet be a callback from the previous snippet, which makes the tutorial a lot harder to follow.
author | Atul Varma <varmaa@toolness.com> |
---|---|
date | Tue, 21 Apr 2009 12:13:18 -0700 |
parents | 97820cdda26f |
children | 954b2b739d41 |
files | js/tutorial.js tutorial.html |
diffstat | 2 files changed, 100 insertions(+), 75 deletions(-) [+] |
line wrap: on
line diff
--- a/js/tutorial.js Tue Apr 21 10:11:12 2009 -0700 +++ b/js/tutorial.js Tue Apr 21 12:13:18 2009 -0700 @@ -32,24 +32,45 @@ $('#try-my-view').text(''); var code = $('.try-code').val(); eval(code); - eval('tryMyView();'); } $('.try-code').blur(executeTryCode); $('#content').fadeIn(); - // Iterate through all the code snippets and trim them. - var allCode = ''; + // Iterate through all the code snippets, gather them for + // execution, and trim them for display. + var snippets = []; + var DONE_FUNC_NAME = 'DONE'; + var DONE_FUNC_CALL = 'DONE();'; $('.example-code').each( function() { var code = $(this).val() || $(this).text(); - allCode += code; - $(this).text(jQuery.trim(code)); + if (code.indexOf(DONE_FUNC_CALL) == -1) + code += DONE_FUNC_CALL; + var snippet = {code: code}; + snippets.push(snippet); + code = code.replace(DONE_FUNC_CALL, ''); + code = jQuery.trim(code); + if ($(this).val()) + $(this).val(code); + else + $(this).text(code); }); + snippets.reverse(); + // Now execute all the code snippets. - var dataUri = 'data:text/javascript,' + encodeURI(allCode); - var script = document.createElement('script'); - script.setAttribute('src', dataUri); - document.body.appendChild(script); + function executeNextSnippet() { + if (snippets.length) { + var snippet = snippets.pop(); + var dataUri = 'data:text/javascript,' + encodeURI(snippet.code); + var script = document.createElement('script'); + script.setAttribute('src', dataUri); + document.body.appendChild(script); + } + } + + window[DONE_FUNC_NAME] = executeNextSnippet; + + executeNextSnippet(); });
--- a/tutorial.html Tue Apr 21 10:11:12 2009 -0700 +++ b/tutorial.html Tue Apr 21 12:13:18 2009 -0700 @@ -24,11 +24,12 @@ <p>Finally, a note about the code examples in this tutorial: they're actually being executed in your browser, and their output is sometimes being displayed in this tutorial too. While this helps ensure that the -software is working as intended and also allows for some interactive -learning opportunities, right now it also means that some parts of the -code examples may look a bit unusual. Furthermore, if you see any -conspicuously blank areas in this tutorial, it could be because the -tutorial code crashed—our apologies if this occurs.</p> +software is working as intended and also allows for some <a +class="intra-wiki" href="#try">interactive learning opportunities</a>, +right now it also means that some parts of the code examples may look +a bit unusual. Furthermore, if you see any conspicuously blank areas +in this tutorial, it could be because the tutorial code +crashed—our apologies if this occurs.</p> <p>With that out of the way, let's get started.</p> @@ -39,7 +40,11 @@ following function:</p> <div class="example-code"> -BrowserCouch.get('blog-posts', onRetrieveCb, new FakeStorage()); +BrowserCouch.get('blog-posts', + function onRetrieveCb(db) { + blogDb = db; /* Save the DB for later. */ DONE(); + }, + new FakeStorage()); </div> <p>It's clear that the first parameter is the name of the database we @@ -54,25 +59,24 @@ the best storage backend based on our browser's capabilities.</p> <p>If the database doesn't already exist, an empty one will be created -for us. Putting blog posts into the database can be done via -an <tt>onRetrieveCb()</tt> function like this:</p> +for us. Putting blog posts into the database is done through the +<tt>put()</tt> method like so:</p> <div class="example-code"> -function onRetrieveCb(db) { - blogDb = db; - blogDb.put( - [{id: 0, author: 'Myk', title: 'Burritos', content: 'Burritos are yum.'}, - {id: 1, author: 'Thunder', title: 'Bacon', content: 'I like bacon.'}, - {id: 2, author: 'Thunder', title: 'Beer', content: 'Beer is good too.'}], - onPutCb - ); -}; +blogDb.put( + [{id: 0, author: 'Myk', title: 'Burritos', content: 'Burritos are yum.'}, + {id: 1, author: 'Thunder', title: 'Bacon', content: 'I like bacon.'}, + {id: 2, author: 'Thunder', title: 'Beer', content: 'Beer is good too.'}], + function onDone() { /* Do stuff... */ DONE();} +); </div> <p>Every item we put into our database needs to have an <tt>id</tt> attribute, but aside from that, the item can contain any JSON-encodable data.</p> +<h1>Views</h1> + <p>Now that we've put some data into our database, we can play around with generating views on the data using the <a href="http://en.wikipedia.org/wiki/MapReduce">MapReduce</a> mechanism. @@ -80,19 +84,22 @@ organizes all the post titles by author:</p> <div class="example-code"> -function onPutCb() { - blogDb.view({ - map: function(doc, emit) { - emit(doc.author, doc.title); - }, - finished: function(result) { - displayInElement(result, 'author-keyed-view'); - tryAnotherView(); - } - }); -} +blogDb.view({ + map: function(doc, emit) { + emit(doc.author, doc.title); + }, + finished: function(result) { + displayInElement(result, 'author-keyed-view'); DONE(); + } +}); </div> +<p>The <tt>view()</tt> method above has lots of optional arguments, +which is why we're passing in a single object with keys corresponding +to argument names. The <tt>map</tt> argument is the function to use +for the map phase, and the <tt>finished</tt> argument is the callback +to pass the view results into when processing is complete.</p> + <p>The output placed in the <tt>author-keyed-view</tt> element is:</p> <div class="example-output" id="author-keyed-view"></div> @@ -104,33 +111,35 @@ worth noting that <tt>map()</tt> can call <tt>emit()</tt> as much as it wants to; each call will add a new row to the view.</p> -<p>We could also try creating another view that adds a -<tt>reduce()</tt> function to group together the blog post titles with -the authors:</p> +<p>At this point you may want to jump to the <a class="intra-wiki" +href="#try">Try It For Yourself</a> section to play around with making +your own <tt>map()</tt> functions.</p> + +<p>The reduce phase of a view is totally optional and a little +confusing. Let's try adding a <tt>reduce()</tt> function to our +earlier view to group together the blog post titles with the +authors:</p> <div class="example-code"> -function tryAnotherView() { - blogDb.view({ - map: function(doc, emit) { - emit(doc.author, doc.title); - }, - reduce: function(keys, values) { - return values; - }, - finished: function(result) { - displayInElement(result, 'author-titles-view'); - findRows(result); - } - }); -} +blogDb.view({ + map: function(doc, emit) { + emit(doc.author, doc.title); + }, + reduce: function(keys, values) { + return values; + }, + finished: function(result) { + authors = result; /* Save the result for later. */ + displayInElement(authors, 'author-titles-view'); DONE(); + } +}); </div> <p>The output is as follows:</p> <div class="example-output" id="author-titles-view"></div> -<p>The <tt>reduce()</tt> mechanism is a bit harder to -understand. Essentially, BrowserCouch takes all the rows generated by +<p>Essentially, BrowserCouch takes all the rows generated by <tt>map()</tt> and generates a new list of key-value rows, where the value of each row is the list of all values that match the row's key. This explains what the <tt>values</tt> argument passed to @@ -148,18 +157,15 @@ one you provide. For example:</p> <div class="example-code"> -function findRows(result) { - var rowIndex = result.findRow('Thunder'); - displayInElement(result.rows[rowIndex], 'author-find-row-view'); - tryMyView(); -} +var rowIndex = authors.findRow('Thunder'); +displayInElement(authors.rows[rowIndex], 'author-find-row-view'); </div> <p>The output for this one is:</p> <div class="example-output" id="author-find-row-view"></div> -<h1>Now You Try!</h1> +<a name="try"><h1>Try It For Yourself</h1></a> <p>If your eyes are crossed right now, no worries—most people take a long time to understand exactly what MapReduce is doing. That @@ -170,19 +176,17 @@ tab key when you're done making changes to recompute the view.</p> <textarea class="example-code try-code"> -function tryMyView() { - blogDb.view({ - map: function(doc, emit) { - emit(doc.author, doc.title); - }, - reduce: function(keys, values) { - return values; - }, - finished: function(result) { - displayInElement(result, 'try-my-view'); - } - }); -} +blogDb.view({ + map: function(doc, emit) { + emit(doc.author, doc.title); + }, + reduce: function(keys, values) { + return values; + }, + finished: function(result) { + displayInElement(result, 'try-my-view'); DONE(); + } +}); </textarea> <p>Here's the output to the above view:</p>