# HG changeset patch # User Atul Varma # Date 1240341198 25200 # Node ID 86158b61b732707876953a4d816af4e33ecc2948 # Parent 97820cdda26fbb162013a51211fb00d36a3d1638 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. diff -r 97820cdda26f -r 86158b61b732 js/tutorial.js --- 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(); }); diff -r 97820cdda26f -r 86158b61b732 tutorial.html --- 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 @@

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.

+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.

With that out of the way, let's get started.

@@ -39,7 +40,11 @@ following function:

-BrowserCouch.get('blog-posts', onRetrieveCb, new FakeStorage()); +BrowserCouch.get('blog-posts', + function onRetrieveCb(db) { + blogDb = db; /* Save the DB for later. */ DONE(); + }, + new FakeStorage());

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.

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 onRetrieveCb() function like this:

+for us. Putting blog posts into the database is done through the +put() method like so:

-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();} +);

Every item we put into our database needs to have an id attribute, but aside from that, the item can contain any JSON-encodable data.

+

Views

+

Now that we've put some data into our database, we can play around with generating views on the data using the MapReduce mechanism. @@ -80,19 +84,22 @@ organizes all the post titles by author:

-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(); + } +});
+

The view() method above has lots of optional arguments, +which is why we're passing in a single object with keys corresponding +to argument names. The map argument is the function to use +for the map phase, and the finished argument is the callback +to pass the view results into when processing is complete.

+

The output placed in the author-keyed-view element is:

@@ -104,33 +111,35 @@ worth noting that map() can call emit() as much as it wants to; each call will add a new row to the view.

-

We could also try creating another view that adds a -reduce() function to group together the blog post titles with -the authors:

+

At this point you may want to jump to the Try It For Yourself section to play around with making +your own map() functions.

+ +

The reduce phase of a view is totally optional and a little +confusing. Let's try adding a reduce() function to our +earlier view to group together the blog post titles with the +authors:

-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(); + } +});

The output is as follows:

-

The reduce() mechanism is a bit harder to -understand. Essentially, BrowserCouch takes all the rows generated by +

Essentially, BrowserCouch takes all the rows generated by map() 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 values argument passed to @@ -148,18 +157,15 @@ one you provide. For example:

-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');

The output for this one is:

-

Now You Try!

+

Try It For Yourself

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.

Here's the output to the above view: