Slide 1

Slide 1 text

PINOCCHIO the plight of JavaScript's quest to become a real language

Slide 2

Slide 2 text

once upon a time...

Slide 3

Slide 3 text

one fine day...

Slide 4

Slide 4 text

we built businesses...

Slide 5

Slide 5 text

...on this toy language

Slide 6

Slide 6 text

go to school, study, always tell the truth, and be obedient.

Slide 7

Slide 7 text

If we are going to use JavaScript to do real work, then we have to do the things that we do with real languages.

Slide 8

Slide 8 text

github.com/bkeepers @bkeepers

Slide 9

Slide 9 text

in real languages, we...

Slide 10

Slide 10 text

in real languages, we... Use Test Driven Development

Slide 11

Slide 11 text

in real languages, we... Use Test Driven Development Refactor

Slide 12

Slide 12 text

in real languages, we... Use Test Driven Development Refactor Separate Concerns

Slide 13

Slide 13 text

in real languages, we... Use Test Driven Development Refactor Separate Concerns Create Abstractions

Slide 14

Slide 14 text

in real languages, we... Use Test Driven Development Apply Design Patterns Refactor Separate Concerns Create Abstractions

Slide 15

Slide 15 text

in real languages, we... Use Test Driven Development Apply Design Patterns Decouple Refactor Separate Concerns Create Abstractions

Slide 16

Slide 16 text

in real languages, we... Use Test Driven Development Apply Design Patterns Decouple Refactor Separate Concerns Use Encapsulation Create Abstractions

Slide 17

Slide 17 text

in real languages, we... Use Test Driven Development Apply Design Patterns Decouple Refactor Separate Concerns Use Encapsulation Create Abstractions DRY

Slide 18

Slide 18 text

why? Use Test Driven Development Apply Design Patterns Decouple Refactor Separate Concerns Use Encapsulation Create Abstractions DRY

Slide 19

Slide 19 text

These principles and practices help us build flexible and maintainable systems.

Slide 20

Slide 20 text

Litmus Test If your site or application breaks without JavaScript, then you should treat it like a real language.

Slide 21

Slide 21 text

Use Test Driven Development Apply Design Patterns in real languages, we

Slide 22

Slide 22 text

the simplest app ever... github.com/bkeepers/monologue

Slide 23

Slide 23 text

jQuery(function($) { }); a typical JavaScript example... example lovingly stolen from @searls

Slide 24

Slide 24 text

a typical JavaScript example... jQuery(function($) { $('#new-status').on('submit', function() { return false; }); }); example lovingly stolen from @searls

Slide 25

Slide 25 text

a typical JavaScript example... jQuery(function($) { $('#new-status').on('submit', function() { $.ajax({ url: '/statuses', type: 'POST', }); return false; }); }); example lovingly stolen from @searls

Slide 26

Slide 26 text

a typical JavaScript example... jQuery(function($) { $('#new-status').on('submit', function() { $.ajax({ url: '/statuses', type: 'POST', dataType: 'json', data: {text: $(this).find('textarea').val()}, }); return false; }); }); example lovingly stolen from @searls

Slide 27

Slide 27 text

a typical JavaScript example... jQuery(function($) { $('#new-status').on('submit', function() { $.ajax({ url: '/statuses', type: 'POST', dataType: 'json', data: {text: $(this).find('textarea').val()}, success: function(data) { } }); return false; }); }); example lovingly stolen from @searls

Slide 28

Slide 28 text

a typical JavaScript example... jQuery(function($) { $('#new-status').on('submit', function() { $.ajax({ url: '/statuses', type: 'POST', dataType: 'json', data: {text: $(this).find('textarea').val()}, success: function(data) { $('#statuses').append( '
  • ' + data.text + '
  • '); } }); return false; }); }); example lovingly stolen from @searls

    Slide 29

    Slide 29 text

    Use Test Driven Development in real languages, we

    Slide 30

    Slide 30 text

    the evolution of testing...

    Slide 31

    Slide 31 text

    Developers initially view testing as a verification process.

    Slide 32

    Slide 32 text

    A good test suite quickly becomes an insurance policy.

    Slide 33

    Slide 33 text

    Test driven development is a design process.

    Slide 34

    Slide 34 text

    Growing Object-Oriented Software Guided by Tests Steve Freeman, Nat Pryce “Starting with a test means that we have to describe what we want to achieve before we consider how.”

    Slide 35

    Slide 35 text

    Test driven development in JavaScript is fast and easy.

    Slide 36

    Slide 36 text

    “ Testing JavaScript is not worth it.”

    Slide 37

    Slide 37 text

    “ Testing JavaScript is not worth it.” “It changes too frequently.”

    Slide 38

    Slide 38 text

    “ Testing JavaScript is not worth it.” “It changes too frequently.” “The tests break all the time.”

    Slide 39

    Slide 39 text

    “ Testing JavaScript is not worth it.” “It changes too frequently.” “It’s just view code.” “The tests break all the time.”

    Slide 40

    Slide 40 text

    “ Testing JavaScript is not worth it.” “It changes too frequently.” “Browser implementations are too different.” “It’s just view code.” “The tests break all the time.”

    Slide 41

    Slide 41 text

    “ Testing JavaScript is not worth it.” “It changes too frequently.” “Browser implementations are too different.” “It’s just view code.” “The tests break all the time.” “It’s too hard.”

    Slide 42

    Slide 42 text

    Testing is hard when we write bad code.

    Slide 43

    Slide 43 text

    jQuery(function($) { $('#new-status').on('submit', function() { $.ajax({ url: '/statuses', type: 'POST', dataType: 'json', data: {text: $(this).find('textarea').val()}, success: function(data) { $('#statuses').append('
  • ' + data.text + '
  • '); } }); return false; }); }); back to our example... example lovingly stolen from @searls

    Slide 44

    Slide 44 text

    jQuery(function($) { $('#new-status').on('submit', function() { $.ajax({ url: '/statuses', type: 'POST', dataType: 'json', data: {text: $(this).find('textarea').val()}, success: function(data) { $('#statuses').append('
  • ' + data.text + '
  • '); } }); return false; }); }); back to our example... 1. page event example lovingly stolen from @searls

    Slide 45

    Slide 45 text

    jQuery(function($) { $('#new-status').on('submit', function() { $.ajax({ url: '/statuses', type: 'POST', dataType: 'json', data: {text: $(this).find('textarea').val()}, success: function(data) { $('#statuses').append('
  • ' + data.text + '
  • '); } }); return false; }); }); back to our example... 1. page event 2. user event example lovingly stolen from @searls

    Slide 46

    Slide 46 text

    jQuery(function($) { $('#new-status').on('submit', function() { $.ajax({ url: '/statuses', type: 'POST', dataType: 'json', data: {text: $(this).find('textarea').val()}, success: function(data) { $('#statuses').append('
  • ' + data.text + '
  • '); } }); return false; }); }); back to our example... 1. page event 2. user event 3. network IO example lovingly stolen from @searls

    Slide 47

    Slide 47 text

    jQuery(function($) { $('#new-status').on('submit', function() { $.ajax({ url: '/statuses', type: 'POST', dataType: 'json', data: {text: $(this).find('textarea').val()}, success: function(data) { $('#statuses').append('
  • ' + data.text + '
  • '); } }); return false; }); }); back to our example... 1. page event 2. user event 3. network IO 4. user input example lovingly stolen from @searls

    Slide 48

    Slide 48 text

    jQuery(function($) { $('#new-status').on('submit', function() { $.ajax({ url: '/statuses', type: 'POST', dataType: 'json', data: {text: $(this).find('textarea').val()}, success: function(data) { $('#statuses').append('
  • ' + data.text + '
  • '); } }); return false; }); }); back to our example... 1. page event 2. user event 3. network IO 4. user input 5. network event example lovingly stolen from @searls

    Slide 49

    Slide 49 text

    jQuery(function($) { $('#new-status').on('submit', function() { $.ajax({ url: '/statuses', type: 'POST', dataType: 'json', data: {text: $(this).find('textarea').val()}, success: function(data) { $('#statuses').append('
  • ' + data.text + '
  • '); } }); return false; }); }); back to our example... 1. page event 2. user event 3. network IO 4. user input 5. network event 6. HTML templating example lovingly stolen from @searls

    Slide 50

    Slide 50 text

    Now try to test that.

    Slide 51

    Slide 51 text

    Testing makes design problems painfully obvious.

    Slide 52

    Slide 52 text

    “The same structure that makes the code difficult to test now will make it difficult to change in the future.” Growing Object-Oriented Software Guided by Tests Steve Freeman, Nat Pryce

    Slide 53

    Slide 53 text

    So how do we test this?

    Slide 54

    Slide 54 text

    Well...let’s talk about system testing.

    Slide 55

    Slide 55 text

    capybara js-test-driver buster.js

    Slide 56

    Slide 56 text

    system tests with cucumber... Feature: Telling the world about my every breath Scenario: Successfully posting a status update When I am on the home page And I fill in "Status" with "Integration tests are easy" And I press "Post Update" Then I should see "Integration tests are easy"

    Slide 57

    Slide 57 text

    system tests with cucumber... $ cucumber Feature: Posting Statuses Scenario: Successfully posting a status When I am on the home page And I fill in "Status" with "Integration tests are easy" And I press "Post Update" Then I should see "Integration tests are easy" 1 scenario (1 passed) 4 steps (4 passed) 0m0.988s

    Slide 58

    Slide 58 text

    there are many advantages to system testing

    Slide 59

    Slide 59 text

    there are many advantages to system testing easy to write

    Slide 60

    Slide 60 text

    there are many advantages to system testing easy to write relatively easy to maintain

    Slide 61

    Slide 61 text

    there are many advantages to system testing easy to write run in real browser environment relatively easy to maintain

    Slide 62

    Slide 62 text

    there are many advantages to system testing easy to write run in real browser environment verifies system actually works relatively easy to maintain

    Slide 63

    Slide 63 text

    there are many disadvantages to system testing

    Slide 64

    Slide 64 text

    there are many disadvantages to system testing run very slow

    Slide 65

    Slide 65 text

    there are many disadvantages to system testing test failures are difficult to decipher run very slow

    Slide 66

    Slide 66 text

    there are many disadvantages to system testing harder to simulate failure cases test failures are difficult to decipher run very slow

    Slide 67

    Slide 67 text

    there are many disadvantages to system testing harder to simulate failure cases test failures are difficult to decipher run very slow does not encourage good design

    Slide 68

    Slide 68 text

    System tests tell you your application is functioning, but tell you nothing about the internal state.

    Slide 69

    Slide 69 text

    Me Just Now™ “With system tests, write as few as possible, but no fewer.”

    Slide 70

    Slide 70 text

    Unit tests help ensure components are well designed and function properly.

    Slide 71

    Slide 71 text

    “To construct an object for a unit test, we have to pass its dependencies to it, which means that we have to know what they are.” Growing Object-Oriented Software Guided by Tests Steve Freeman, Nat Pryce

    Slide 72

    Slide 72 text

    “To keep unit tests understandable (and, so, maintainable), we have to limit their scope.” Growing Object-Oriented Software Guided by Tests Steve Freeman, Nat Pryce

    Slide 73

    Slide 73 text

    There are about 1,690,000 different unit testing frameworks in JavaScript.

    Slide 74

    Slide 74 text

    Just pick one that looks healthy.

    Slide 75

    Slide 75 text

    a unit test in jasmine require('/js/views/status_list.js'); describe("View.StatusList", function() { beforeEach(function() { this.view = new View.StatusList(); }); it("fetches records from the server", function() { expect(this.view.collection.fetch).toHaveBeenCalled(); }); it("renders when collection is reset", function() { this.view.collection.reset([{text: 'Unit testing is fun'}]); expect(this.view.$el.find('li').text()).toEqual('Unit testing is fun'); }); it("appends newly added items", function() {

    Slide 76

    Slide 76 text

    We can use unit tests to improve our design, but first…

    Slide 77

    Slide 77 text

    Apply Design Patterns in real languages, we

    Slide 78

    Slide 78 text

    Architect Christopher Alexander “Each pattern describes a problem that occurs over and over again in our environment, and then describes the core of the solution to that problem, in such a way that you can use this solution a million times over, without ever doing it the same way twice.”

    Slide 79

    Slide 79 text

    Design patterns are the basic parts of speech that allow us to create coherent, well-structured systems.

    Slide 80

    Slide 80 text

    No content

    Slide 81

    Slide 81 text

    Creational Abstract Factory Builder Factory Method Prototype Singleton Structural Adapter Bridge Composite Decorator Flyweight Proxy Behavioral Chain of responsibility Command Interpreter Iterator Mediator Memento Observer State Strategy Template method Visitor “Classical Design Patterns”

    Slide 82

    Slide 82 text

    Advantages of Design Patterns

    Slide 83

    Slide 83 text

    Advantages of Design Patterns removes duplication

    Slide 84

    Slide 84 text

    Advantages of Design Patterns removes duplication shared vocabulary

    Slide 85

    Slide 85 text

    Advantages of Design Patterns removes duplication shared vocabulary faster communication

    Slide 86

    Slide 86 text

    Advantages of Design Patterns removes duplication shared vocabulary generic, reusable components faster communication

    Slide 87

    Slide 87 text

    Advantages of Design Patterns removes duplication shared vocabulary generic, reusable components faster communication proven paradigms

    Slide 88

    Slide 88 text

    Advantages of Design Patterns removes duplication shared vocabulary generic, reusable components faster communication proven paradigms easier to test

    Slide 89

    Slide 89 text

    Advantages of Design Patterns removes duplication shared vocabulary generic, reusable components faster communication proven paradigms easier to test supports system changes

    Slide 90

    Slide 90 text

    Addy Osmani Learning JavaScript Design Patterns “Patterns don’t solve all design problems, nor do they replace good software designers, however, they do support them.”

    Slide 91

    Slide 91 text

    Model, View, Controller almost every web-related framework uses

    Slide 92

    Slide 92 text

    Martin Fowler http://www.martinfowler.com/ “The idea behind [MVC] is to make a clear division between domain objects that model our perception of the real world, and presentation objects that are the GUI elements we see on the screen.”

    Slide 93

    Slide 93 text

    Martin Fowler http://www.martinfowler.com/ “...Domain objects should be completely self contained and work without reference to the presentation, they should also be able to support multiple presentations, possibly simultaneously.”

    Slide 94

    Slide 94 text

    Model domain or structural objects representing the application’s state

    Slide 95

    Slide 95 text

    If your application has data or logic, then you need a model.

    Slide 96

    Slide 96 text

    jQuery(function($) { $('#new-status').on('submit', function() { $.ajax({ url: '/statuses', type: 'POST', dataType: 'json', data: {text: $(this).find('textarea').val()}, success: function(data) { $('#statuses').append( '
  • ' + data.text + '
  • '); } }); return false; }); }); back to our example...

    Slide 97

    Slide 97 text

    jQuery(function($) { $('#new-status').on('submit', function() { $.ajax({ url: '/statuses', type: 'POST', dataType: 'json', data: {text: $(this).find('textarea').val()}, success: function(data) { $('#statuses').append( '
  • ' + data.text + '
  • '); } }); return false; }); }); back to our example...

    Slide 98

    Slide 98 text

    refactoring to use models... // models/status.js Model.Status = Backbone.Model.extend({ }); // collections/statuses.js Collection.Statuses = Backbone.Collection.extend({ model: Model.Status, url: '/statuses' });

    Slide 99

    Slide 99 text

    and the result... // app.js jQuery(function($) { var statuses = new Collection.Statuses(); $('#new-status').on('submit', function() { statuses.create({ text: $(this).find('textarea').val() }); return false; }); statuses.on('add', function(status) { $('#statuses').append( '
  • ' + status.get('text') + '
  • '); }); });

    Slide 100

    Slide 100 text

    and the result... // app.js jQuery(function($) { var statuses = new Collection.Statuses(); $('#new-status').on('submit', function() { statuses.create({ text: $(this).find('textarea').val() }); return false; }); statuses.on('add', function(status) { $('#statuses').append( '
  • ' + status.get('text') + '
  • '); }); }); Observer Pattern

    Slide 101

    Slide 101 text

    Our models don’t have logic yet, so we’re skipping the unit test for now.

    Slide 102

    Slide 102 text

    but our system test still passes... $ cucumber Feature: Posting Statuses Scenario: Succesfully posting a status When I am on the home page And I fill in "Status" with "Integration tests are easy" And I press "Post Update" Then I should see "Integration tests are easy" 1 scenario (1 passed) 4 steps (4 passed) 0m0.988s

    Slide 103

    Slide 103 text

    Controller translates user input into operations on the model

    Slide 104

    Slide 104 text

    back to our example... // app.js jQuery(function($) { var statuses = new Collection.Statuses(); $('#new-status').on('submit', function() { statuses.create({text: $(this).find('textarea').val()}); return false; }); statuses.on('add', function(status) { $('#statuses').append( '
  • ' + status.get('text') + '
  • '); }); });

    Slide 105

    Slide 105 text

    back to our example... // app.js jQuery(function($) { var statuses = new Collection.Statuses(); $('#new-status').on('submit', function() { statuses.create({text: $(this).find('textarea').val()}); return false; }); statuses.on('add', function(status) { $('#statuses').append( '
  • ' + status.get('text') + '
  • '); }); });

    Slide 106

    Slide 106 text

    test driven refactoring... describe("PostStatus", function() { describe("submitting the form", function() { it("creates a status", function() { }); }); });

    Slide 107

    Slide 107 text

    test driven refactoring... describe("PostStatus", function() { describe("submitting the form", function() { it("creates a status", function() { $el.trigger('submit'); }); }); });

    Slide 108

    Slide 108 text

    test driven refactoring... describe("PostStatus", function() { describe("submitting the form", function() { it("creates a status", function() { $el.trigger('submit'); expect(collection.create).toHaveBeenCalledWith( {text: "It’s easy!"}); }); }); });

    Slide 109

    Slide 109 text

    test driven refactoring... describe("PostStatus", function() { var $el; beforeEach(function() { $el = $("It's easy!"); }); describe("submitting the form", function() { it("creates a status", function() { $el.trigger('submit'); expect(collection.create).toHaveBeenCalledWith( {text: "It’s easy!"}); }); }); });

    Slide 110

    Slide 110 text

    test driven refactoring... describe("PostStatus", function() { var $el; beforeEach(function() { $el = $("It's easy!"); }); describe("submitting the form", function() { it("creates a status", function() { $el.trigger('submit'); expect(collection.create).toHaveBeenCalledWith( {text: "It’s easy!"}); }); }); }); unattached from DOM

    Slide 111

    Slide 111 text

    test driven refactoring... describe("PostStatus", function() { var $el, collection; beforeEach(function() { $el = $("It's easy!"); collection = new Collection.Statuses(); }); describe("submitting the form", function() { it("creates a status", function() { $el.trigger('submit'); expect(collection.create).toHaveBeenCalledWith( {text: "It’s easy!"}); }); }); });

    Slide 112

    Slide 112 text

    test driven refactoring... describe("PostStatus", function() { var $el, collection; beforeEach(function() { $el = $("It's easy!"); collection = new Collection.Statuses(); }); describe("submitting the form", function() { it("creates a status", function() { $el.trigger('submit'); expect(collection.create).toHaveBeenCalledWith( {text: "It’s easy!"}); }); }); }); do we need this test dependency?

    Slide 113

    Slide 113 text

    test driven refactoring... describe("PostStatus", function() { var $el, collection; beforeEach(function() { $el = $("It's easy!"); collection = {create: jasmine.createSpy('create')}; }); describe("submitting the form", function() { it("creates a status", function() { $el.trigger('submit'); expect(collection.create).toHaveBeenCalledWith( {text: "It’s easy!"}); }); }); });

    Slide 114

    Slide 114 text

    test driven refactoring... describe("PostStatus", function() { var $el, collection, view; beforeEach(function() { $el = $("It's easy!"); collection = {create: jasmine.createSpy('create')}; view = new View.PostStatus({ el: $el, collection: collection }); }); describe("submitting the form", function() { it("creates a status", function() { $el.trigger('submit'); expect(collection.create).toHaveBeenCalledWith(

    Slide 115

    Slide 115 text

    test driven refactoring... describe("PostStatus", function() { var $el, collection, view; beforeEach(function() { $el = $("It's easy!"); collection = {create: jasmine.createSpy('create')}; view = new View.PostStatus({ el: $el, collection: collection }); }); describe("submitting the form", function() { it("creates a status", function() { $el.trigger('submit'); expect(collection.create).toHaveBeenCalledWith( dependency injection

    Slide 116

    Slide 116 text

    a failing test...

    Slide 117

    Slide 117 text

    refactoring to use controllers... // views/post_status.js View.PostStatus = Backbone.View.extend({ events: { "submit": "submit" }, submit: function(e) { var $input = this.$el.find('textarea'); this.collection.create({text: $input.val()}); return false; } });

    Slide 118

    Slide 118 text

    all green

    Slide 119

    Slide 119 text

    and the result... // app.js jQuery(function($) { var statuses = new Collection.Statuses(); new View.PostStatus({ el: $('#new-status'), collection: statuses }); statuses.on('add', function(status) { $('#statuses').append( '
  • ' + status.get('text') + '
  • '); }); });

    Slide 120

    Slide 120 text

    and our system test still passes... $ cucumber Feature: Posting Statuses Scenario: Succesfully posting a status When I am on the home page And I fill in "Status" with "Integration tests are easy" And I press "Post Update" Then I should see "Integration tests are easy" 1 scenario (1 passed) 4 steps (4 passed) 0m0.988s

    Slide 121

    Slide 121 text

    View observes the model’s state and generates output for the user

    Slide 122

    Slide 122 text

    // app.js jQuery(function($) { var statuses = new Collection.Statuses(); new View.PostStatus({ el: $('#new-status'), collection: statuses }); statuses.on('add', function(status) { $('#statuses').append( '
  • ' + status.get('text') + '
  • '); }); }); back to our example...

    Slide 123

    Slide 123 text

    // app.js jQuery(function($) { var statuses = new Collection.Statuses(); new View.PostStatus({ el: $('#new-status'), collection: statuses }); statuses.on('add', function(status) { $('#statuses').append( '
  • ' + status.get('text') + '
  • '); }); }); back to our example...

    Slide 124

    Slide 124 text

    test driven refactoring... describe("View.StatusList", function() { it("appends newly added items", function() { collection.add({text: 'this is fun!'}); expect($el.find('li').length).toBe(1); expect($el.find('li').text()).toEqual('this is fun!'); }); });

    Slide 125

    Slide 125 text

    test driven refactoring... describe("View.StatusList", function() { beforeEach(function() { $el = $('
      '); collection = new Backbone.Collection(); view = new View.StatusList({ el: $el, collection: collection }); }); it("appends newly added items", function() { collection.add({text: 'this is fun!'}); expect($el.find('li').length).toBe(1); expect($el.find('li').text()).toEqual('this is fun!'); }); });

      Slide 126

      Slide 126 text

      refactoring to use views... // views/status_list.js View.StatusList = Backbone.View.extend({ initialize: function() { _.bindAll(this, 'add'); this.collection.on('add', this.add); }, add: function(model) { this.$el.append(this.template(model)) }, template: function(model) { return this.make('li', {className:'status'}, model.get('text')); } });

      Slide 127

      Slide 127 text

      and the result... // app.js jQuery(function($) { var statuses = new Collection.Statuses(); new View.PostStatus({ el: $('#new-status'), collection: statuses }); new View.StatusList({ el: $('#statuses'), collection: statuses }); });

      Slide 128

      Slide 128 text

      Let’s add a new feature.

      Slide 129

      Slide 129 text

      adding new features, the cowboy way jQuery(function($) { $.ajax({ url: '/statuses', dataType: 'json', success: function(data) { var $statuses = $('#statuses'); for(var i = 0; data.length > i; i++) { $('#statuses').append( '
    • ' + data[i].text + '
    • '); } } }); });

      Slide 130

      Slide 130 text

      but we’re better than that now... Feature: Telling the world about my every breath # ... Scenario: Viewing previous status updates Given the following statuses exist: | This is outside-in development | | It’s not so bad | When I am on the home page Then I should see "This is outside-in development" And I should see "It’s not so bad"

      Slide 131

      Slide 131 text

      failing system test... $ cucumber features/statuses.feature:9 Feature: Posting Statuses Scenario: Viewing previous status updates Given the following statuses exist: | This is outside-in development | | It's not so bad | When I am on the home page Then I should see "This is outside-in development" expected there to be content "This is outside-in development" in "Post Update" (RSpec::Expectations::ExpectationNotMetError) And I should see "It's not so bad" Failing Scenarios: cucumber features/statuses.feature:9 1 scenario (1 failed) 4 steps (1 failed, 1 skipped, 3 passed) 0m2.619s

      Slide 132

      Slide 132 text

      then a unit test... describe("View.StatusList", function() { // … it("fetches records from the server", function() { expect(collection.fetch).toHaveBeenCalled(); }); it("renders when collection is reset", function() { collection.reset([{text: 'This is fun!'}]); expect($el.find('li').length).toBe(1); expect($el.find('li').text()).toEqual('This is fun!'); }); });

      Slide 133

      Slide 133 text

      and finally, the implementation... // views/status_list.js View.StatusList = Backbone.View.extend({ initialize: function() { this.collection.on('add', this.add); }, add: function(model) { this.$el.append(this.template(model)) }, template: function(model) { return this.make('li', {className:'status'}, model.get('text')); } });

      Slide 134

      Slide 134 text

      and finally, the implementation... // views/status_list.js View.StatusList = Backbone.View.extend({ initialize: function() { this.collection.on('add', this.add); this.collection.on('reset', this.render); this.collection.fetch(); }, render: function() { this.collection.each(this.add); }, add: function(model) { this.$el.append(this.template(model)) }, // ... template: function(model) {

      Slide 135

      Slide 135 text

      our top level code didn’t change... // app.js jQuery(function($) { var statuses = new Collection.Statuses(); new View.PostStatus({ el: $('#new-status'), collection: statuses }); new View.StatusList({ el: $('#statuses'), collection: statuses }); });

      Slide 136

      Slide 136 text

      MVC is just one design pattern. There are many others that can help us.

      Slide 137

      Slide 137 text

      No content

      Slide 138

      Slide 138 text

      Epilogue before we depart, an

      Slide 139

      Slide 139 text

      Robert C. Martin Clean Code “Writing clean code requires the disciplined use of a myriad of little techniques applied through a painstakingly acquired sense of ‘cleanliness.’ ”

      Slide 140

      Slide 140 text

      single responsibility principle Every object should have a single responsibility, and that responsibility should be entirely encapsulated by the object.

      Slide 141

      Slide 141 text

      “Our heuristic is that we should be able to describe what an object does without using any conjunctions (‘and’, ‘or’).” Growing Object-Oriented Software Guided by Tests Steve Freeman, Nat Pryce

      Slide 142

      Slide 142 text

      responsibility & dependencies // Responsibility: // Create a status from user input // // Dependencies: // * el - A form element with a textarea // * collection - the collection that the status will be // created in View.PostStatus = Backbone.View.extend({ // ... });

      Slide 143

      Slide 143 text

      References

      Slide 144

      Slide 144 text

      Carlo Collodi Adventures of Pinocchio “One day I was about to become a boy, a real boy, but on account of my laziness and my hatred of books, and because I listened to bad companions…I awoke to find myself changed into a donkey– long ears, gray coat, even a tail!”

      Slide 145

      Slide 145 text

      Thanks! @bkeepers http://bit.ly/pinocchio-slides Example App: github.com/bkeepers/monologue