# 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: