Slide 1

Slide 1 text

No content

Slide 2

Slide 2 text

Régis Hanol @ZogStriP

Slide 3

Slide 3 text

No content

Slide 4

Slide 4 text

Jeff Atwood @codinghorror Sam Saffron @samsaffron

Slide 5

Slide 5 text

Jeff Atwood @codinghorror Sam Saffron @samsaffron

Slide 6

Slide 6 text

Jeff Atwood @codinghorror Sam Saffron @samsaffron Robin Ward @eviltrout

Slide 7

Slide 7 text

Jeff Atwood @codinghorror Sam Saffron @samsaffron Robin Ward @eviltrout

Slide 8

Slide 8 text

No content

Slide 9

Slide 9 text

No content

Slide 10

Slide 10 text

A with

Slide 11

Slide 11 text

February '13

Slide 12

Slide 12 text

JSON API BROWSER APPLICATION

Slide 13

Slide 13 text

Ruby Rails JavaScript Ember

Slide 14

Slide 14 text

Ruby Rails JavaScript Ember

Slide 15

Slide 15 text

Ruby Rails JavaScript Ember

Slide 16

Slide 16 text

Ruby Rails JavaScript Ember

Slide 17

Slide 17 text

Ruby Rails JavaScript Ember

Slide 18

Slide 18 text

No content

Slide 19

Slide 19 text

Ember.js incorporates common idioms so you can focus on what makes your app special, not reinventing the wheel.

Slide 20

Slide 20 text

Convention Configuration

Slide 21

Slide 21 text

this.route('user');

Slide 22

Slide 22 text

UserRoute /user this.route('user');

Slide 23

Slide 23 text

UserRoute User LOADS /user this.route('user');

Slide 24

Slide 24 text

UserRoute User LOADS UserController SETS MODEL ON /user this.route('user');

Slide 25

Slide 25 text

UserRoute User LOADS UserController UserView SETS MODEL ON RENDERS /user this.route('user');

Slide 26

Slide 26 text

UserRoute User LOADS UserController UserView user.handlebars SETS MODEL ON RENDERS BASED ON /user this.route('user');

Slide 27

Slide 27 text

UserRoute User LOADS UserController UserView user.handlebars SETS MODEL ON RENDERS GETS STATE FROM BASED ON /user this.route('user');

Slide 28

Slide 28 text

No content

Slide 29

Slide 29 text

No content

Slide 30

Slide 30 text

No content

Slide 31

Slide 31 text

No content

Slide 32

Slide 32 text

No content

Slide 33

Slide 33 text

No content

Slide 34

Slide 34 text

No content

Slide 35

Slide 35 text

No content

Slide 36

Slide 36 text

MVC != MVC Ember Data Outdated help

Slide 37

Slide 37 text

No content

Slide 38

Slide 38 text

CLIENT SIDE SERVER SIDE MVC

Slide 39

Slide 39 text

CLIENT SIDE SERVER SIDE LONG-LIVED STATE-FULL

Slide 40

Slide 40 text

CLIENT SIDE SERVER SIDE LONG-LIVED SHORT-LIVED STATE-FULL STATE-LESS

Slide 41

Slide 41 text

CLIENT SERVER

Slide 42

Slide 42 text

CLIENT SERVER GET /posts

Slide 43

Slide 43 text

CLIENT SERVER GET /posts Router

Slide 44

Slide 44 text

CLIENT SERVER GET /posts Router PostsController

Slide 45

Slide 45 text

CLIENT SERVER GET /posts Router PostsController Post

Slide 46

Slide 46 text

CLIENT SERVER GET /posts Router PostsController Post PostsView

Slide 47

Slide 47 text

CLIENT SERVER GET /posts Router PostsController Post PostsView

Slide 48

Slide 48 text

CLIENT SERVER GET /posts Router PostsController Post PostsView {

Slide 49

Slide 49 text

CLIENT SERVER Router

Slide 50

Slide 50 text

CLIENT SERVER Router Post

Slide 51

Slide 51 text

CLIENT SERVER Router Post GET /posts.json {…}

Slide 52

Slide 52 text

CLIENT SERVER Router PostsController Post GET /posts.json {…}

Slide 53

Slide 53 text

CLIENT SERVER Router PostsController Post PostsView PostsTemplate GET /posts.json {…}

Slide 54

Slide 54 text

DATA

Slide 55

Slide 55 text

App.Post = Ember.Object.extend({});

Slide 56

Slide 56 text

var post = App.Post.create(); ! ! ! App.Post = Ember.Object.extend({});

Slide 57

Slide 57 text

var post = App.Post.create(); ! post.set(‘title’, ‘Hello World’); ! App.Post = Ember.Object.extend({});

Slide 58

Slide 58 text

var post = App.Post.create(); ! post.set(‘title’, ‘Hello World’); ! post.get(‘title’); // Hello World App.Post = Ember.Object.extend({});

Slide 59

Slide 59 text

var post = App.Post.create({ title: “EmberFest" }); ! App.Post = Ember.Object.extend({});

Slide 60

Slide 60 text

var post = App.Post.create({ title: “EmberFest" }); ! post.get(‘title’); // EmberFest App.Post = Ember.Object.extend({});

Slide 61

Slide 61 text

$.getJSON(‘http://...', function(posts) { // posts contains the JSON result ! }); });

Slide 62

Slide 62 text

$.getJSON(‘http://...', function(posts) { return posts.map(function(post){ return App.Post.create(post); }); });

Slide 63

Slide 63 text

$.getJSON(‘http://...') .then(function(posts){ return posts.map(function(post){ return App.Post.create(post); }); });

Slide 64

Slide 64 text

App.Post.reopenClass({ ! findAll: function(){ return $.getJSON('http://...') .then(function(posts){ return posts.map(function(post){ return App.Post.create(post); });
 }); } ! });

Slide 65

Slide 65 text

App.PostRoute = Ember.Route.extend({ ! model: function() {
 return App.Post.findAll(); } ! };

Slide 66

Slide 66 text

RELEASE BETA CANARY

Slide 67

Slide 67 text

RELEASE BETA CANARY day

Slide 68

Slide 68 text

RELEASE BETA CANARY week day

Slide 69

Slide 69 text

RELEASE BETA CANARY 6 weeks week day

Slide 70

Slide 70 text

No content

Slide 71

Slide 71 text

No content

Slide 72

Slide 72 text

DEBUG ALL THE THINGS!

Slide 73

Slide 73 text

{{log object}}

Slide 74

Slide 74 text

{{debugger}}

Slide 75

Slide 75 text

Ember.Application.create({ ! });

Slide 76

Slide 76 text

Transitioned into 'latest' Transitioned into 'new' Transitioned into 'categories' Ember.Application.create({ LOG_TRANSITIONS: true });

Slide 77

Slide 77 text

Ember.Application.create({ LOG_TRANSITIONS_INTERNAL: true }); Attempting URL transition to / Transition #0: application: calling beforeModel hook Transition #0: application: calling deserialize hook Transition #0: application: calling afterModel hook Transition #0: discovery: calling beforeModel hook Transition #0: discovery: calling deserialize hook Transition #0: discovery: calling afterModel hook Transition #0: discovery.latest: calling beforeModel hook Transition #0: discovery.latest: calling deserialize hook Transition #0: discovery.latest: calling afterModel hook Transition #0: Resolved all models on destination route; finalizing transition. Transition #0: TRANSITION COMPLETE.

Slide 78

Slide 78 text

Ember.Application.create({ LOG_VIEW_LOOKUPS: true }); Rendering application with Object {fullName: "view:application"} ! Rendering topic with Object {fullName: "view:topic"} ! Could not find "topic.fromParams" template or view. Nothing will be rendered Object {fullName: "template:topic.fromParams"} ! Rendering discovery with default view Object {fullName: "view:discovery"}

Slide 79

Slide 79 text

Ember.Application.create({ LOG_ACTIVE_GENERATION: true }); generated -> route:adminUsers Object {fullName: "route:adminUsers"} generated -> route:adminGroups.index Object {fullName: "route:adminGroups.index"} generated -> route:adminEmail Object {fullName: "route:adminEmail"} generated -> route:adminFlags Object {fullName: "route:adminFlags"} generated -> route:adminLogs Object {fullName: "route:adminLogs"} generated -> route:adminCustomize Object {fullName: "route:adminCustomize"}

Slide 80

Slide 80 text

Ember.Application.create({ LOG_RESOLVER: true }); [✓] view:modal ............................. Discourse.ModalView [✓] controller:modal ....................... Discourse.ModalController [ ] template:modal ......................... template at modal [✓] template:modal/modal ................... template at modal/modal [✓] view:topic-entrance .................... Discourse.TopicEntranceView [✓] controller:topic-entrance .............. Discourse.TopicEntranceController [✓] template:topic-entrance ................ template at topic-entrance [✓] controller:composer .................... Discourse.ComposerController [✓] controller:composer-messages ........... Discourse.ComposerMessagesController [✓] template:composer ...................... template at composer [✓] view:composer-messages ................. Discourse.ComposerMessagesView [ ] template:composer-messages ............. template at composer-messages

Slide 81

Slide 81 text

var container = App.__container__; ! container.lookup("route:application") container.lookup("controller:post") container.lookup("view:post") container.lookup("template:comment")

Slide 82

Slide 82 text

Ember.onerror = function(error){ console.log(error); };

Slide 83

Slide 83 text

Ember.RSVP.on('error', function (error) { console.log(error); } );

Slide 84

Slide 84 text

INSPECTOR github.com/emberjs/ember-inspector

Slide 85

Slide 85 text

No content

Slide 86

Slide 86 text

No content

Slide 87

Slide 87 text

No content

Slide 88

Slide 88 text

No content

Slide 89

Slide 89 text

No content

Slide 90

Slide 90 text

No content

Slide 91

Slide 91 text

No content

Slide 92

Slide 92 text

No content

Slide 93

Slide 93 text

No content

Slide 94

Slide 94 text

Y U NO TEST?!!

Slide 95

Slide 95 text

INTEGRATION UNIT TESTS

Slide 96

Slide 96 text

App.rootElement = '#ember-testing'; ! ! !

Slide 97

Slide 97 text

App.rootElement = '#ember-testing'; ! App.setupForTesting(); !

Slide 98

Slide 98 text

App.rootElement = '#ember-testing'; ! App.setupForTesting(); ! App.injectTestHelpers();

Slide 99

Slide 99 text

Ember Tests

Slide 100

Slide 100 text

github.com/airportyh/testem github.com/karma-runner/karma TEST’EM ARMA

Slide 101

Slide 101 text

INTEGRATION UNIT TESTS

Slide 102

Slide 102 text

visit(url) 1/

Slide 103

Slide 103 text

visit(url) fillIn(selector, text) click(selector) keyEvent(selector, type, keyCode) triggerEvent(selector, type, options) 1/ 2/

Slide 104

Slide 104 text

visit(url) fillIn(selector, text) click(selector) keyEvent(selector, type, keyCode) triggerEvent(selector, type, options) find(selector, context) 1/ 2/ 3/

Slide 105

Slide 105 text

visit(url) fillIn(selector, text) click(selector) keyEvent(selector, type, keyCode) triggerEvent(selector, type, options) find(selector, context) 1/ 2/ 3/

Slide 106

Slide 106 text

visit(url) fillIn(selector, text) click(selector) keyEvent(selector, type, keyCode) triggerEvent(selector, type, options) find(selector, context) 1/ 2/ 3/

Slide 107

Slide 107 text

test(“creating a post", function(){ ! ! ! ! ! ! ! ! ! ! ! ! ! ! });

Slide 108

Slide 108 text

test(“creating a post", function(){ ! // 1 - visit visit(“/posts/new"); ! ! ! ! ! ! ! ! ! ! ! });

Slide 109

Slide 109 text

test(“creating a post", function(){ ! // 1 - visit visit(“/posts/new"); ! // 2 - interact fillIn("input.title", "My new post"); click(“button.submit"); ! ! ! ! ! ! ! });

Slide 110

Slide 110 text

test(“creating a post", function(){ ! // 1 - visit visit(“/posts/new"); ! // 2 - interact fillIn("input.title", "My new post"); click(“button.submit"); ! ! ! // 3 - check equal(find(“ul.posts li:last").text(), "My new post”); ! ! });

Slide 111

Slide 111 text

visit(url) fillIn(selector, text) click(selector) keyEvent(selector, type, keyCode) triggerEvent(selector, type, options) find(selector, context) 1/ 2/ 3/ ASYNCHRONOUS

Slide 112

Slide 112 text

visit(url) fillIn(selector, text) click(selector) keyEvent(selector, type, keyCode) triggerEvent(selector, type, options) find(selector, context) 1/ 2/ 3/ ASYNCHRONOUS SYNCHRONOUS

Slide 113

Slide 113 text

test(“creating a post", function(){ ! // 1 - visit visit(“/posts/new"); ! // 2 - interact fillIn("input.title", "My new post"); click(“button.submit"); ! ! ! // 3 - check equal(find(“ul.posts li:last").text(), "My new post”); ! ! });

Slide 114

Slide 114 text

test(“creating a post", function(){ ! // 1 - visit visit(“/posts/new"); ! // 2 - interact fillIn("input.title", "My new post"); click(“button.submit"); ! // wait for asynchronous helpers above to complete andThen(function(){ // 3 - check equal(find(“ul.posts li:last").text(), "My new post”); }); ! });

Slide 115

Slide 115 text

Ember.Test.registerAsyncHelper("addPost", function(app, title, context) { visit("/posts/new"); fillIn("input.title", title); click("button.submit"); } ); ! !

Slide 116

Slide 116 text

Ember.Test.registerAsyncHelper("addPost", function(app, title, context) { visit("/posts/new"); fillIn("input.title", title); click("button.submit"); } ); ! // addPost(“Hello World"); // addPost(“Hello Emberfest");

Slide 117

Slide 117 text

module("Integration", { setup: function() { App.reset(); } });

Slide 118

Slide 118 text

INTEGRATION UNIT TESTS

Slide 119

Slide 119 text

Ember.Object

Slide 120

Slide 120 text

Ember.Object 1/ instantiate

Slide 121

Slide 121 text

Ember.Object 1/ 2/ instantiate change state

Slide 122

Slide 122 text

Ember.Object 1/ 2/ 3/ instantiate change state assert

Slide 123

Slide 123 text

App.Post = Ember.Object.extend({ status: 'draft', ! isPublished: function() { return this.get(‘status’) == 'published'; }.property(‘status') ! });

Slide 124

Slide 124 text

App.Post = Ember.Object.extend({ status: 'draft', ! isPublished: function() { return this.get(‘status’) == 'published'; }.property(‘status') ! }); test(‘isPublished is false by default', function() { ! ! ! });

Slide 125

Slide 125 text

App.Post = Ember.Object.extend({ status: 'draft', ! isPublished: function() { return this.get(‘status’) == 'published'; }.property(‘status') ! }); test(‘isPublished is false by default', function() { var post = App.Post.create(); ! ! });

Slide 126

Slide 126 text

App.Post = Ember.Object.extend({ status: 'draft', ! isPublished: function() { return this.get(‘status’) == 'published'; }.property(‘status') ! }); test(‘isPublished is false by default', function() { var post = App.Post.create(); ! equal(post.get(‘isPublished’), false); });

Slide 127

Slide 127 text

test(‘isPublished is true when published', function() { ! ! ! }); App.Post = Ember.Object.extend({ status: 'draft', ! isPublished: function() { return this.get(‘status’) == 'published'; }.property(‘status') ! });

Slide 128

Slide 128 text

test(‘isPublished is true when published', function() { var post = App.Post.create(); ! ! }); App.Post = Ember.Object.extend({ status: 'draft', ! isPublished: function() { return this.get(‘status’) == 'published'; }.property(‘status') ! });

Slide 129

Slide 129 text

test(‘isPublished is true when published', function() { var post = App.Post.create(); post.set('status', ‘published'); ! }); App.Post = Ember.Object.extend({ status: 'draft', ! isPublished: function() { return this.get(‘status’) == 'published'; }.property(‘status') ! });

Slide 130

Slide 130 text

test(‘isPublished is true when published', function() { var post = App.Post.create(); post.set('status', 'published'); equal(post.get(‘isPublished’), true); }); App.Post = Ember.Object.extend({ status: 'draft', ! isPublished: function() { return this.get(‘status’) == 'published'; }.property(‘status') ! });

Slide 131

Slide 131 text

ACHIEVEMENT UNLOCKED - - Model - Controller - Route - Component - View

Slide 132

Slide 132 text

No content

Slide 133

Slide 133 text

No content

Slide 134

Slide 134 text

No content

Slide 135

Slide 135 text

THANKS!