Skip to content
This repository

HTTPS clone URL

Subversion checkout URL

You can clone with HTTPS or Subversion.

Download ZIP
Browse code

Finished the resource tutorial

  • Loading branch information...
commit 8c4db3daf6337f04bddffda73ceaf793efd7e26e 1 parent 5b325f9
Miguel Madero authored September 30, 2012

Showing 1 changed file with 83 additions and 180 deletions. Show diff stats Hide diff stats

  1. 263  site/tutorial.html
263  site/tutorial.html
@@ -266,8 +266,7 @@ <h2 id="scaffolding">Scaffolding application tutorial</h2>
266 266
   });
267 267
 ...
268 268
 };
269  
-Todo = geddy.model.register('Todo', Todo);
270  
-            </pre>
  269
+Todo = geddy.model.register('Todo', Todo);</pre>
271 270
           <p>Here we are making it so the title property is required and have a minumum of 5 characters. We also made it so the status acts like a boolean attribute but uses custom names instead of true/false. Now we'll need to edit the 'edit' and 'add' views to reflect the status changes.</p>
272 271
           <!--TODO: We could use the helper 'contentTag('select', '', {class:'span6', name:'status', options: ['open', 'done']})' if we merge it instead -->
273 272
           <!-- TODO: Consider using partials for edit/add forms when using geddy scaffold-->
@@ -283,8 +282,7 @@ <h2 id="scaffolding">Scaffolding application tutorial</h2>
283 282
     &lt;/select&gt;
284 283
   &lt;/div&gt;
285 284
 &lt;/div&gt;
286  
-...
287  
-            </pre>
  285
+...</pre>
288 286
             <p>Now that we've made the needed changes, restart Geddy to update our model changes, now we've got a good todo application running and didn't really have to do anything. Scaffolding is very good when you don't need to do much. To learn more about models and applications keep reading and follow the <a href="#resources">resource</a> application tutorial to get a better understanding of views and controller which we didn't really cover in the Scaffold Tutorial.</p>
289 287
 
290 288
             <h2 id="resources">Resource application tutorial</h2>
@@ -347,7 +345,8 @@ <h2 id="resources">Resource application tutorial</h2>
347 345
     , action = isUpdate ? todoPath(params.id) + '?_method=PUT' : todosPath
348 346
     , deleteAction = isUpdate ? todoPath(params.id) + '?_method=DELETE' : ''
349 347
     , btnText = isUpdate ? 'Update' : 'Add'
350  
-    , doneStatus = isUpdate ? 'checked' : ''
  348
+    , doneSelectAttributes = isUpdate && todo.status === 'done' ? "selected=true" : ''
  349
+    , openSelectAttributes = isUpdate && todo.status === 'open' ? "selected=true" : ''
351 350
     , titleValue = isUpdate ? todo.title : ''
352 351
     , errors = params.errors;
353 352
 %&gt;
@@ -369,11 +368,11 @@ <h2 id="resources">Resource application tutorial</h2>
369 368
     &lt;/div&gt;
370 369
     &lt;% if (isUpdate) { %&gt;
371 370
       &lt;div class="control-group"&gt;
372  
-        &lt;label for="status"&gt;Status&lt;/label&gt;
  371
+        &lt;label for="status" class="control-label"&gt;Status&lt;/label&gt;
373 372
         &lt;div class="controls"&gt;
374  
-          &lt;select name="status"&gt;
375  
-            &lt;option&gt;open&lt;/option&gt;
376  
-            &lt;option&gt;done&lt;/option&gt;
  373
+          &lt;select name="status" class="span6"&gt;
  374
+            &lt;option &lt;%=openSelectAttributes%&gt;&gt;open&lt;/option&gt;
  375
+            &lt;option &lt;%=doneSelectAttributes%&gt;&gt;done&lt;/option&gt;
377 376
           &lt;/select&gt;
378 377
         &lt;/div&gt;
379 378
       &lt;/div&gt;
@@ -385,29 +384,33 @@ <h2 id="resources">Resource application tutorial</h2>
385 384
       &lt;% } %&gt;
386 385
     &lt;/div&gt;
387 386
   &lt;/fieldset&gt;
388  
-&lt;/form&gt;
389  
-            </pre>
  387
+&lt;/form&gt;</pre>
  388
+
390 389
             <p>Here we created a couple variables so we can tell if it's for a edit or add action, then if we have any errors we dislay them. Also we are using a couple view helpers (contentTag) which are helpful with dealing with assets, links, etc. You can read more about our view helpers <a href="https://github.com/mde/geddy/wiki/View-Helpers">here</a>.</p>
391 390
 
392  
-            <p>Now that we've created a base for our add and edit actions, we'll do them now. They're simple sense they just use the _form partial</p>
  391
+            <p>Now that we've created a base for our add and edit actions, we'll do them now. They're simple they just use the _form partial. Add the following code to add.html.ejs</p>
393 392
             <pre class="prettyprint">
394 393
 &lt;div class="hero-unit"&gt;
395 394
   &lt;%= partial('_form', {params: params}); %&gt;
396  
-&lt;/div&gt;
397  
-            </pre>
  395
+&lt;/div&gt;</pre>
  396
+
  397
+            <p>The edit view is slightly different because we will need to pass the todo object to the partial. Modify <code>app/views/todos/edit.html.ejs</code> with the following code</p>
  398
+
  399
+            <pre class="prettyprint">&lt;%= partial('_form', {params: params, todo: todo}); %&gt;</pre>
  400
+
398 401
             <p>This template will be for both add and edit actions. We just create a partial from _form and include all the params.</p>
399 402
 
400 403
             <p>Now that we have views that will create todo items let's create the show action so we can view the todo items we create.</p>
401 404
             <pre class="prettyprint">
402 405
 &lt;div class="hero-unit"&gt;
  406
+  &lt;%- linkTo('Edit this todo', editTodoPath(params.id), {class: 'btn pull-right'}); %&gt;
403 407
   &lt;h3&gt;Params&lt;/h3&gt;
404 408
   &lt;ul&gt;
405 409
   &lt;% for (var p in params) { %&gt;
406 410
     &lt;li&gt;&lt;%= p + ': ' + params[p]; %&gt;&lt;/li&gt;
407 411
   &lt;% } %&gt;
408 412
   &lt;/ul&gt;
409  
-&lt;/div&gt;
410  
-            </pre>
  413
+&lt;/div&gt;</pre>
411 414
             <p>I will leave the show action to you for a exercise, but herre I just loop through the params and show them.</p>
412 415
 
413 416
             <p>Finally we need to create the index action to link everything together.</p>
@@ -420,16 +423,16 @@ <h2 id="resources">Resource application tutorial</h2>
420 423
   &lt;% for (var i in todos) { %&gt;
421 424
   &lt;div class="row todo-item"&gt;
422 425
     &lt;div class="span8"&gt;
423  
-        &lt;h3&gt;&lt;%- linkTo(todos[i].title, editTodoPath(todos[i].id)) %&gt;&lt;/h3&gt;
  426
+        &lt;h3&gt;&lt;%- linkTo(todos[i].title, todoPath(todos[i].id)) %&gt;&lt;/h3&gt;
424 427
     &lt;/div&gt;
425 428
     &lt;div class="span4"&gt;&lt;h3&gt;&lt;i class="icon-list-alt"&gt;&lt;/i&gt;&lt;%= todos[i].status; %&gt;&lt;/h3&gt;&lt;/div&gt;
426 429
   &lt;/div&gt;
427 430
   &lt;% } %&gt;
428  
-&lt;% } %&gt;
429  
-            </pre>
  431
+&lt;% } %&gt;</pre>
  432
+
430 433
             <p>For the index action we just have a link to add new items, and a list of ll the items, with a link to each of their edit paths. If you notice we're using special helpers here, that create links to the path specified.</p>
431 434
 
432  
-            <h3>Creating the Todo model</h3>
  435
+            <h3>The Todo model</h3>
433 436
             <p>We're ready to start in on modeling our data. Geddy provides us with some pretty cool tools to do this:</p>
434 437
             <ul>
435 438
               <li>Validation</li>
@@ -446,15 +449,14 @@ <h2 id="resources">Resource application tutorial</h2>
446 449
 var Todo = function () {
447 450
 
448 451
   this.defineProperties({
449  
-      id: {type: 'string', required: true}
450  
-    , title: {type: 'string'}
  452
+      title: {type: 'string'}
451 453
     , status: {type: 'string'}
452 454
   });
453 455
 
454 456
 };
455 457
 
456  
-Todo = geddy.model.register('Todo', Todo);
457  
-            </pre>
  458
+Todo = geddy.model.register('Todo', Todo);</pre>
  459
+
458 460
             <p>The `defineProperties` method takes any number of properties to be added to the model. The keys in the object will be added as properties on the model. The values are just objects that describe the properties. When we ran the scaffold command it created these for us. But we want to change it so they are all `required` so set `title` and `status` to match `id` now.</p>
459 461
 
460 462
             <p>To learn more, check out the <a href="https://github.com/mde/geddy/blob/master/README.md">readme</a>.</p>
@@ -467,8 +469,7 @@ <h2 id="resources">Resource application tutorial</h2>
467 469
 
468 470
 this.validatesWithFunction('status', function (status) {
469 471
   return status == 'open' || status == 'done';
470  
-});
471  
-            </pre>
  472
+});</pre>
472 473
 
473 474
             <p>For the <code>title</code> property, we made sure that the property is always present and we made sure that the <code>title</code> property is a minimum of 5 characters long.</p>
474 475
 
@@ -487,206 +488,107 @@ <h2 id="resources">Resource application tutorial</h2>
487 488
             <h4>Optional: use mongo for persistence </h4>
488 489
             <p>Install a <a href="http://www.mongodb.org/downloads">mongodb</a> server if you haven't already and <code> $ [sudo] npm install -g mongodb-wrapper</code> to install the required mongodb-wrapper and leave the <code>defaultAdapter = 'mongo'</code> in config/development.js to use mongo instead of the memory adapter. </p>
489 490
 
490  
-            <h3 id="saving-todos">Saving todos</h3>
491  
-
492  
-            <p>Now that we have our model and model adapter in place, we can start in on the app logic. Lets start with adding to do items to our to do list.</p>
  491
+            <h3>The Todo Controller</h3>
  492
+            <p>TODO: add some intro about controllers. Explain what Geddy adds by default and what we're about to change</p>
493 493
 
494  
-            <h4>Edit the save method on the adapter to save a todo instance</h4>
495  
-
496  
-            <p>When working with data, the first place you should go is the model adapter. We need to be able to save an instance of our <code>Todo</code> model to our <code>geddy.todos</code> array. So open up <code>lib/model_adapters/todo.js</code> and add in a <code>save</code> method:</p>
497  
-
498  
-            <pre class="prettyprint">
499  
-this.save = function (todo) {
500  
-  todo.saved = true;
501  
-  geddy.todos.push(todo);
502  
-};
503  
-            </pre>
504  
-
505  
-            <p>All we have to do is set the instance's <code>saved</code> property to true and push the item into the <code>geddy.todos</code> array. Now lets move on to the controller <code>create</code> action.</p>
506  
-
507  
-            <h4>Edit the create action to save a todo instance</h4>
508  
-
509  
-            <p>Go ahead and take a look at the <code>create</code> action in <code>app/controllers/todos.js</code></p>
510  
-
511  
-            <pre class="prettyprint">
512  
-this.create = function (req, resp, params) {
513  
-  // Save the resource, then display index page
514  
-  this.redirect({controller: this.name});
515  
-};
516  
-            </pre>
  494
+            <h4 id="saving-todos">Saving todos</h4>
517 495
 
518  
-            <p>Pretty simple, right? It's stubbed it out for you. So let's modify it a little bit:</p>
  496
+            <p>To save a todo we need edit the create action in <code>app/controllers/todos.js</code>. It's not doing much at the momment so lets modify it a little bit.</p>
519 497
 
520 498
             <pre class="prettyprint">
521 499
 this.create = function (req, resp, params) {
522  
-  var todo = geddy.model.Todo.create({title: params.title, id: geddy.string.uuid(10), status: 'open'});
523  
-  if (todo.isValid()) {
524  
-    todo.save();
525  
-    this.redirect({controller: this.name});
526  
-  } else {
527  
-    this.redirect({controller: this.name, action: 'add?error=true'});
528  
-  }
529  
-};
530  
-            </pre>
531  
-
532  
-            <p>First, we create a new instance of the <code>Todo</code> model with <code>geddy.model.Todo.create</code>, passing in the title that our form will post up to us, and setting up the defaults for the id and status.</p>
  500
+  var self = this
  501
+    , todo = geddy.model.Todo.create({title:params.title, status:'open'});
  502
+
  503
+  todo.save(function(err, data) {
  504
+    if (err) {
  505
+      params.errors = err;
  506
+      self.transfer('add');
  507
+    } else {
  508
+      self.redirect({controller: self.name});
  509
+    }
  510
+  });
  511
+};</pre>
533 512
 
534  
-            <p>Then we check to see if the model passed validation, if it did, we call the <code>save</code> method that we created on the model adapter and redirect the user back to the <code>/todos</code> route. If it didn't pass validation, we redirect the user back to the </code>/todos/add</code> route and pass an error as a query parameter.</p>
  513
+            <p>First, we create a new instance of the <code>Todo</code> model with <code>geddy.model.Todo.create</code>, passing in the title that our form will post up to us, and setting up the default status.</p>
535 514
 
536  
-            <p>Geddy has built-in sessions too, so this might be another good spot to improve your app after you finish the tutorial.</p>
  515
+            <p>Then we call we call the <code>save</code> method and redirect the user back to the /todos route. Internally, save does two things. It validates the model based on the rules we defined earlier. This is similar to calling <code>todo.isValid()</code>. If the model was valid, it will delegate to the model adapter configured previously to actually persist the model. If either step fails, you will get an error collection as the first parameter of the function and we we redirect the user back to the /todos/add route. Otherwise we redirect to the controller default action <code>self.redirect({controller: self.name});</code>.</p>
537 516
 
538 517
             <h3 id="list-todos">Listing all todos</h3>
539 518
 
540  
-            <p>Now that we have user input To Do items being added into our <code>geddy.todos</code> array, we should probably list them somewhere. Lets start in on the <code>index</code> action in the <code>todos</code> controller.</p>
  519
+            <p>Now that we we can create To Do items, we should probably list them somewhere. Lets change the <code>index</code> action in the <code>todos</code> controller.</p>
541 520
 
542 521
             <h4>Edit the index action to show all todos</h4>
543 522
 
544  
-            <p>Open up <code>/app/controllers/todos.js</code> again and take a look at the <code>index</code> action. It should look something like this:</p>
  523
+            <p>Open up <code>/app/controllers/todos.js</code> again and replace the current implementaton with the following code.</p>
545 524
 
546 525
             <pre class="prettyprint">
547 526
 this.index = function (req, resp, params) {
548  
-  this.respond({params: params});
549  
-};
550  
-            </pre>
  527
+  var self = this;
551 528
 
552  
-            <p>This part is really simple, just replace the <code>params</code> property in the template variable object with a <code>todos</code> property and set it to <code>geddy.todos</code>, to pass that list down into your view.</p>
  529
+  geddy.model.Todo.all(function(err, todos) {
  530
+    self.respond({params: params, todos: todos});
  531
+  });
  532
+};</pre>
553 533
 
554  
-            <pre class="prettyprint">
555  
-this.index = function (req, resp, params) {
556  
-  this.respond({todos: geddy.todos});
557  
-};
558  
-            </pre>
  534
+            <p>This part is a bit simpler and it follows a similar pattern. Instead of calling create in <code>geddy.model.Todo</code> this time we simply call <code>all</code> and we pass the data back to the view for rendering</p>
559 535
 
560 536
             <p>Now that we can can load todo items you can test it by starting up Geddy and go to <a href="http://localhost:4000/todos">http://localhost:4000/todos</a> and you can view the list of items.</p>
561 537
 
562 538
             <h3 id="show-todo">Showing a todo</h3>
563 539
 
564  
-            <p>Now that we have our index action working as expected, we should probably work on the <code>show</code> controller action.</p>
565  
-
566  
-            <h4>Create a load method in the model adapter</h4>
567  
-            <p>Open up your model adapter again (<code>/lib/model_adapters/todo.js</code>) - by now, it should look something like this: </p>
568  
-            <pre class="prettyprint">
569  
-var Todo = new (function () {
570  
-
571  
-  this.save = function (todo) {
572  
-    todo.saved = true;
573  
-    geddy.todos.push(todo);
574  
-  };
575  
-
576  
-})();
577  
-
578  
-exports.Todo = Todo;
579  
-            </pre>
580  
-
581  
-            <p>Lets define a <code>load</code> method in this adapter, for getting one of the todos from the list:</p>
582  
-            <pre class="prettyprint">
583  
-var Todo = new (function () {
584  
-
585  
-  this.load = function (id, callback) {
586  
-    for (var i in geddy.todos) {
587  
-      if (geddy.todos[i].id == id) {
588  
-        return callback(geddy.todos[i]);
589  
-      }
590  
-    }
591  
-    callback({});
592  
-  };
593  
-
594  
-  this.save = function (todo) {
595  
-    todo.saved = true;
596  
-    geddy.todos.push(todo);
597  
-  };
598  
-
599  
-})();
600  
-
601  
-exports.Todo = Todo;
602  
-            </pre>
603  
-
604  
-            <p>This <code>load</code> method takes an <code>id</code> and a <code>callback</code>. It loops through the items in <code>geddy.todos</code> and checks to see if the current item's <code>id</code> matches the passed in <code>id</code>. If it does, it calls the callback, passing the <code>todo</code> item back. If it doesn't find a match, it calls the callback with a blank object. Now we need to use this method in the <code>todos</code> controller's show action.</p>
605  
-
606  
-            <p>This is a simple example that finds a single item by iterating the entire collection, but you could write an adapter that does a SQL-call to a database, or makes an API-call to a Web service.</p>
  540
+            <p>Now that we have our index action working as expected, we should probably work on the <code>show</code> controller action to display todo details.</p>
607 541
 
608 542
             <h4>Edit the show action to find a todo</h4>
609 543
 
610  
-            <p>Open up your <code>todos</code> controller again and take a look at it's <code>show</code> action. It should look something like this:</p>
611  
-
612  
-            <pre class="prettyprint">
613  
-this.show = function (req, resp, params) {
614  
-  this.respond({params: params});
615  
-};
616  
-            </pre>
617  
-
618  
-            <p>Lets use the load method that we just created:</p>
  544
+            <p>Open up your <code>todos</code> controller again. This time we need to edit the show action to make it look like the following code.</p>
619 545
 
620 546
             <pre class="prettyprint">
621 547
 this.show = function (req, resp, params) {
622 548
   var self = this;
623  
-  geddy.model.adapter.Todo.load(params.id, function (todo) {
624  
-    self.respond({todo: todo});
  549
+
  550
+  geddy.model.Todo.load(params.id, function(err, todo) {
  551
+    self.respond({params: params, todo: todo.toObj()});
625 552
   });
626  
-};
627  
-            </pre>
628  
-            <p>Now we have a working show action in the controller to load items from the list.</p>
  553
+};</pre>
  554
+            <p>Now we have a working show action in the controller to load items.</p>
629 555
 
630 556
             <h3 id="update-todo">Updating a todo</h3>
631 557
 
632  
-            <h4>Edit the save method in the model adapter to save over existing todos</h4>
633  
-
634  
-            <p>Open up your model adapter again. We're going to want to change the save method to allow for saving over existing model instances. You're <code>save</code> method should look something like this:</p>
635  
-
636  
-            <pre class="prettyprint">
637  
-this.save = function (todo, callback) {
638  
-  todo.saved = true;
639  
-  geddy.todos.push(todo);
640  
-};
641  
-            </pre>
  558
+            <h4>Edit the update action to find a todo, change the status, and save it</h4>
642 559
 
643  
-            <p>Lets edit it to look like this:</p>
  560
+            <p>Alright, now that we can view our todos lets edit the <code>update</code> action in the <code>todos</code> controller. It should look something like this.</p>
644 561
 
645 562
             <pre class="prettyprint">
646  
-this.save = function (todo, callback) {
647  
-  for (var i in geddy.todos) {
648  
-
649  
-    // if it's already there, save it
650  
-    if (geddy.todos[i].id == todo.id) {
651  
-      geddy.todos[i] = todo;
652  
-      return;
653  
-    }
654  
-
655  
-  }
656  
-  todo.saved = true;
657  
-  geddy.todos.push(todo);
  563
+this.edit = function (req, resp, params) {
  564
+  var self = this;
658 565
 
  566
+  geddy.model.Todo.load(params.id, function(err, todo) {
  567
+    self.respond({params: params, todo: todo});
  568
+  });
659 569
 };
660  
-            </pre>
661  
-
662  
-            <p>This loops over all the <code>todo</code>'s in <code>geddy.todos</code> and if the id is already there, replace that <code>todo</code> with the new <code>todo</code> instance. If you were hooked up to a real DB here, you'd do a SQL UPDATE or similar here instead.</p>
663  
-
664  
-            <h4>Edit the update action to find a todo, change the status, and save it</h4>
665 570
 
666  
-            <p>Alright, now that we have our <code>save</code> method in order, lets edit the <code>update</code> action in the <code>todos</code> controller. It should look something like this right now:</p>
667  
-
668  
-            <pre class="prettyprint">
669 571
 this.update = function (req, resp, params) {
670  
-  // Save the resource, then display the item page
671  
-  this.redirect({controller: this.name, id: params.id});
672  
-};
673  
-            </pre>
  572
+  var self = this;
674 573
 
675  
-            <p>You'll want to edit it to make it look like this:</p>
  574
+  geddy.model.Todo.load(params.id, function(err, todo) {
  575
+    todo.updateAttributes(params);
676 576
 
677  
-            <pre class="prettyprint">
678  
-this.update = function (req, resp, params) {
679  
-  var self = this;
680  
-  geddy.model.adapter.Todo.load(params.id, function (todo) {
681  
-    todo.status = params.status;
682  
-    todo.save();
683  
-    self.redirect({controller: this.name, id: params.id});
  577
+    todo.save(function(err, data) {
  578
+      if (err) {
  579
+        params.errors = err;
  580
+        self.transfer('edit');
  581
+      } else {
  582
+        self.redirect({controller: self.name});
  583
+      }
  584
+    });
684 585
   });
685  
-};
686  
-            </pre>
  586
+};</pre>
687 587
 
688  
-            <p>We're taking the id that we sent up via the ajax <code>PUT</code> request on the <code>show</code> page and using the <code>load</code> method that we created earlier to find a <code>todo</code> item. Then we're setting its <code>status</code> to be what we sent up in the params ('done'). Then we use the <code>save</code> method that we just updated to save over the existing <code>todo</code> item. Then, in case this isn't coming from an ajax request, we're redirecting the request over to the <code>show</code> action (hooray for progressive enhancment).</p>
  588
+            <h3 id="delete-todo">Deleting a todo</h3>
689 589
 
  590
+            <p>The delete is really simple specially now that you're familiar with the pattern. This time you will have to call remove passing the id of the todo you want to delete. We will leave the details as an excercise. Remember that you can always compare your solution to the <a href="https://github.com/mde/geddy/tree/master/examples/todo_app">final version</a>.</p>
  591
+            
690 592
             <h3 id="conclusion">Conclusion</h3>
691 593
 
692 594
             <p>At this point you should have a working To Do List app!</p>
@@ -706,6 +608,7 @@ <h3 id="conclusion">Conclusion</h3>
706 608
             <li>Change the <code>Main#index</code> route to point to the <code>Todos#index</code> action (hint, check out <code>config/router.js</code>)</li>
707 609
             <li>Add some logging with <code>geddy.log</code></li>
708 610
             <li>Set up metrics by running <code>npm install metrics</code>, and uncomment the metrics entry (<code>metrics: { port: 4001 }</code>) in your <code>config/environment.js</code> file</li>
  611
+            <li>Configure mongo, riak or postgress and use it instead of the memory modelAdapter. See how easy it's to switch</li>
709 612
             </ul>
710 613
 
711 614
           </div>

0 notes on commit 8c4db3d

Please sign in to comment.
Something went wrong with that request. Please try again.