Slide 1

Slide 1 text

1st class web development with lineman

Slide 2

Slide 2 text

My name is Justin Searls Please tweet me @searls & Say [email protected]

Slide 3

Slide 3 text

@linemanjs linemanjs.com

Slide 4

Slide 4 text

1

Slide 5

Slide 5 text

1 2

Slide 6

Slide 6 text

1 3 2

Slide 7

Slide 7 text

No content

Slide 8

Slide 8 text

Java Developers

Slide 9

Slide 9 text

Java Developers

Slide 10

Slide 10 text

Ruby Developers

Slide 11

Slide 11 text

Ruby Developers

Slide 12

Slide 12 text

.NET Developers

Slide 13

Slide 13 text

.NET Developers

Slide 14

Slide 14 text

JavaScript Developers

Slide 15

Slide 15 text

JavaScript Developers

Slide 16

Slide 16 text

JavaScript Developers

Slide 17

Slide 17 text

JavaScript Developers

Slide 18

Slide 18 text

JavaScript Developers

Slide 19

Slide 19 text

not soluble

Slide 20

Slide 20 text

Inefficiency

Slide 21

Slide 21 text

No content

Slide 22

Slide 22 text

Browser capability

Slide 23

Slide 23 text

Browser capability Opportunity cost

Slide 24

Slide 24 text

Browser capability Opportunity cost

Slide 25

Slide 25 text

No content

Slide 26

Slide 26 text

Rails won the war.

Slide 27

Slide 27 text

Rails won the war? which

Slide 28

Slide 28 text

"Good frameworks are extractions, not inventions." - DHH

Slide 29

Slide 29 text

No content

Slide 30

Slide 30 text

Routing

Slide 31

Slide 31 text

Routing Models

Slide 32

Slide 32 text

Routing Models Persistence

Slide 33

Slide 33 text

Routing Models Persistence Sessions

Slide 34

Slide 34 text

Routing Models Persistence Sessions Mailers

Slide 35

Slide 35 text

Routing Models Persistence Sessions Mailers JS-free Dynamism

Slide 36

Slide 36 text

HTML UI

Slide 37

Slide 37 text

HTML UI ! Item 1

Slide 38

Slide 38 text

HTML UI ! Item 1 !

Slide 39

Slide 39 text

JavaScript UI

Slide 40

Slide 40 text

JavaScript UI ! new ItemView('#items').render()

Slide 41

Slide 41 text

JavaScript UI ! new ItemView('#items').render() ! $('button').click(function(e){ $('.alert').text('Thanks!').show() });

Slide 42

Slide 42 text

HTML UI ! app ├── controllers │ └── items_controller.rb ├── models │ └── item.rb └── views └── items └── index.html.erb

Slide 43

Slide 43 text

HTML UI ! app ├── controllers │ └── items_controller.rb ├── models │ └── item.rb ├── views │ └── items │ └── index.html.erb └── assets └── javascripts └── application.js

Slide 44

Slide 44 text

HTML UI + JS UI ! app ├── controllers │ └── items_controller.rb ├── models │ └── item.rb ├── views │ └── items │ └── index.html.erb └── assets └── javascripts └── more_app ├── controllers │ └── items_controller.js ├── models │ └── item.js └── views └── items └── index.jst.ejs

Slide 45

Slide 45 text

HTML UI + JS UI ! app ├── controllers │ └── items_controller.rb ├── models │ └── item.rb ├── views │ └── items │ └── index.html.erb └── assets └── javascripts └── more_app ├── controllers │ └── items_controller.js ├── models │ └── item.js └── views └── items └── index.jst.ejs

Slide 46

Slide 46 text

HTML UI + JS UI ! app ├── controllers │ └── items_controller.rb ├── models │ └── item.rb ├── views │ └── items │ └── index.html.erb └── assets └── javascripts └── more_app ├── controllers │ └── items_controller.js ├── models │ └── item.js └── views └── items └── index.jst.ejs

Slide 47

Slide 47 text

HTML UI + JS UI ! app ├── controllers │ └── items_controller.rb ├── models │ └── item.rb ├── views │ └── items │ └── index.html.erb └── assets └── javascripts └── more_app ├── controllers │ └── items_controller.js ├── models │ └── item.js └── views └── items └── index.jst.ejs

Slide 48

Slide 48 text

JSON API + JS UI ! app ├── controllers │ └── items_controller.rb ├── models │ └── item.rb │ │ │ └── assets └── javascripts └── more_app ├── controllers │ └── items_controller.js ├── models │ └── item.js └── views └── items └── index.jst.ejs

Slide 49

Slide 49 text

JSON API + JS UI ! app ├── controllers │ └── items_controller.rb ├── models │ └── item.rb │ │ │ └── assets └── javascripts └── more_app ├── controllers │ └── items_controller.js ├── models │ └── item.js └── views └── items └── index.jst.ejs vestigial appendage

Slide 50

Slide 50 text

what's wrong with vestigial appendages?

Slide 51

Slide 51 text

app/

Slide 52

Slide 52 text

app/

Slide 53

Slide 53 text

app/

Slide 54

Slide 54 text

app/

Slide 55

Slide 55 text

ap

Slide 56

Slide 56 text

ap

Slide 57

Slide 57 text

ap p/

Slide 58

Slide 58 text

app1/ ! app2/

Slide 59

Slide 59 text

app1/ ! app2/

Slide 60

Slide 60 text

app1/ ! app2/

Slide 61

Slide 61 text

app1/ ! app2/

Slide 62

Slide 62 text

app1

Slide 63

Slide 63 text

app1

Slide 64

Slide 64 text

app1 app2

Slide 65

Slide 65 text

late extraction costs more than early abstraction

Slide 66

Slide 66 text

No content

Slide 67

Slide 67 text

No content

Slide 68

Slide 68 text

No content

Slide 69

Slide 69 text

No content

Slide 70

Slide 70 text

No content

Slide 71

Slide 71 text

No content

Slide 72

Slide 72 text

No content

Slide 73

Slide 73 text

late extraction costs more than early abstraction

Slide 74

Slide 74 text

When you see this:

Slide 75

Slide 75 text

When you see this: !

Slide 76

Slide 76 text

When you see this: ! user = <%= user.to_json %>

Slide 77

Slide 77 text

When you see this: ! user = <%= user.to_json %> time = <%= Time.now.to_i %>

Slide 78

Slide 78 text

When you see this: ! user = <%= user.to_json %> time = <%= Time.now.to_i %> items = <%= items.to_json %>

Slide 79

Slide 79 text

When you see this: ! user = <%= user.to_json %> time = <%= Time.now.to_i %> items = <%= items.to_json %> token = "<%= @token %>"

Slide 80

Slide 80 text

When you see this: ! user = <%= user.to_json %> time = <%= Time.now.to_i %> items = <%= items.to_json %> token = "<%= @token %>" your API is broken

Slide 81

Slide 81 text

No content

Slide 82

Slide 82 text

We think we're

Slide 83

Slide 83 text

No content

Slide 84

Slide 84 text

But sometimes we're

Slide 85

Slide 85 text

No content

Slide 86

Slide 86 text

mature ≈ convenient

Slide 87

Slide 87 text

Before Rails was easy

Slide 88

Slide 88 text

Before Rails was easy JavaScript wasn't hard

Slide 89

Slide 89 text

Convenience

Slide 90

Slide 90 text

Server-side

Slide 91

Slide 91 text

Client-side

Slide 92

Slide 92 text

Just kidding.

Slide 93

Slide 93 text

Client-side

Slide 94

Slide 94 text

No content

Slide 95

Slide 95 text

Do the Simplest Thing

Slide 96

Slide 96 text

Do the Simplest Thing 1. Show data in a table

Slide 97

Slide 97 text

Do the Simplest Thing 1. Show data in a table 2. Now render it as a graph

Slide 98

Slide 98 text

Do the Simplest Thing 1. Show data in a table 2. Now render it as a graph 3. (ͮŇ㷩㷩Ň)ͮ

Slide 99

Slide 99 text

Do the Simplest Thing 1. Show data in a table 2. Now render it as a graph 3. (ͮŇ㷩㷩Ň)ͮ 4. Now add filters, sliders, & zoom to the graph!

Slide 100

Slide 100 text

Do the Simplest Thing 1. Show data in a table 2. Now render it as a graph 3. (ͮŇ㷩㷩Ň)ͮ 4. Now add filters, sliders, & zoom to the graph! 5. (ಠ_ಠ)

Slide 101

Slide 101 text

the simple cliff

Slide 102

Slide 102 text

the simple cliff

Slide 103

Slide 103 text

the simple cliff

Slide 104

Slide 104 text

the simple cliff

Slide 105

Slide 105 text

the simple cliff

Slide 106

Slide 106 text

the simple cliff (›°□°ʣ›ớ

Slide 107

Slide 107 text

No content

Slide 108

Slide 108 text

No content

Slide 109

Slide 109 text

No content

Slide 110

Slide 110 text

Compete on easy.

Slide 111

Slide 111 text

App Framework Convention & Config Build Automation

Slide 112

Slide 112 text

Rails App Framework Convention & Config Build Automation

Slide 113

Slide 113 text

Rails Rails App Framework Convention & Config Build Automation

Slide 114

Slide 114 text

Rails Rails Railsy Rake App Framework Convention & Config Build Automation

Slide 115

Slide 115 text

Rails Rails Railsy Rake App Framework Convention & Config Build Automation Backbone

Slide 116

Slide 116 text

Rails Rails Railsy Rake App Framework Convention & Config Build Automation Backbone Ember

Slide 117

Slide 117 text

Rails Rails Railsy Rake App Framework Convention & Config Build Automation Backbone Ember Angular

Slide 118

Slide 118 text

Rails Rails Railsy Rake App Framework Convention & Config Build Automation Backbone Ember Angular ᵇ(´-ʆ)ᵃ


Slide 119

Slide 119 text

Rails Rails Railsy Rake App Framework Convention & Config Build Automation Grunt (Node.js) Backbone Ember Angular ᵇ(´-ʆ)ᵃ


Slide 120

Slide 120 text

Rails Rails Railsy Rake App Framework Convention & Config Build Automation Grunt (Node.js) Lineman Backbone Ember Angular ᵇ(´-ʆ)ᵃ


Slide 121

Slide 121 text

! $ npm install -g lineman Install

Slide 122

Slide 122 text

! $ npm install -g lineman Install ! $ lineman new my-app Create

Slide 123

Slide 123 text

No content

Slide 124

Slide 124 text

No content

Slide 125

Slide 125 text

No content

Slide 126

Slide 126 text

No content

Slide 127

Slide 127 text

No content

Slide 128

Slide 128 text

No content

Slide 129

Slide 129 text

No content

Slide 130

Slide 130 text

No content

Slide 131

Slide 131 text

No content

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

No content

Slide 136

Slide 136 text

No content

Slide 137

Slide 137 text

No content

Slide 138

Slide 138 text

;?

Slide 139

Slide 139 text

;?❤️!

Slide 140

Slide 140 text

;?❤️! # @ ;

Slide 141

Slide 141 text

;?❤️! # @ ; ☕-script!

Slide 142

Slide 142 text

No content

Slide 143

Slide 143 text

Grunt

Slide 144

Slide 144 text

} make Cmake Rake Ant / NAnt MSBuild Maven Grunt

Slide 145

Slide 145 text

Easy peasy ! $ cat tasks/hello.js !

Slide 146

Slide 146 text

Easy peasy ! $ cat tasks/hello.js ! module.exports = function(grunt) { };

Slide 147

Slide 147 text

Easy peasy ! $ cat tasks/hello.js ! module.exports = function(grunt) { grunt.registerTask("hello",function(){ }); };

Slide 148

Slide 148 text

Easy peasy ! $ cat tasks/hello.js ! module.exports = function(grunt) { grunt.registerTask("hello",function(){ grunt.log.writeln("Hello!"); }); };

Slide 149

Slide 149 text

Easy peasy ! $ grunt hello !

Slide 150

Slide 150 text

Easy peasy ! $ grunt hello ! Running "hello" task Hello! ! Done, without errors.

Slide 151

Slide 151 text

No content

Slide 152

Slide 152 text

tasks

Slide 153

Slide 153 text

config tasks

Slide 154

Slide 154 text

No content

Slide 155

Slide 155 text

grunt-contrib-clean grunt-contrib-coffee grunt-contrib-concat grunt-contrib-copy grunt-contrib-handlebars grunt-contrib-jshint grunt-contrib-jst grunt-contrib-less grunt-contrib-sass grunt-contrib-cssmin grunt-contrib-uglify grunt-watch-nospawn grunt-contrib-imagemin grunt-jasmine-bundle grunt-contrib-compass

Slide 156

Slide 156 text

grunt-contrib-clean grunt-contrib-coffee grunt-contrib-concat grunt-contrib-copy grunt-contrib-handlebars grunt-contrib-jshint grunt-contrib-jst grunt-contrib-less grunt-contrib-sass grunt-contrib-cssmin grunt-contrib-uglify grunt-watch-nospawn grunt-contrib-imagemin grunt-jasmine-bundle grunt-contrib-compass grunt-*

Slide 157

Slide 157 text

grunt-contrib-clean grunt-contrib-coffee grunt-contrib-concat grunt-contrib-copy grunt-contrib-handlebars grunt-contrib-jshint grunt-contrib-jst grunt-contrib-less grunt-contrib-sass grunt-contrib-cssmin grunt-contrib-uglify grunt-watch-nospawn grunt-contrib-imagemin grunt-jasmine-bundle grunt-contrib-compass grunt-*

Slide 158

Slide 158 text

No content

Slide 159

Slide 159 text

No content

Slide 160

Slide 160 text

Client Server

Slide 161

Slide 161 text

Client Server ?

Slide 162

Slide 162 text

Client Server

Slide 163

Slide 163 text

Client Server

Slide 164

Slide 164 text

Lineman Sinatra

Slide 165

Slide 165 text

Lineman Sinatra

Slide 166

Slide 166 text

Lineman Sinatra

Slide 167

Slide 167 text

Lineman Sinatra

Slide 168

Slide 168 text

Lineman Sinatra

Slide 169

Slide 169 text

No content

Slide 170

Slide 170 text

Client Server

Slide 171

Slide 171 text

Client Server

Slide 172

Slide 172 text

Client Server

Slide 173

Slide 173 text

Client Server

Slide 174

Slide 174 text

Lineman Sinatra

Slide 175

Slide 175 text

Lineman Sinatra

Slide 176

Slide 176 text

Lineman Sinatra

Slide 177

Slide 177 text

Lineman Sinatra

Slide 178

Slide 178 text

No content

Slide 179

Slide 179 text

30 minute test build

Slide 180

Slide 180 text

No content

Slide 181

Slide 181 text

4 minutes

Slide 182

Slide 182 text

4 minutes 4 minutes +

Slide 183

Slide 183 text

4 minutes 4 minutes + + 2 minutes

Slide 184

Slide 184 text

4 minutes 4 minutes + + 2 minutes 10 minute test build

Slide 185

Slide 185 text

It's habit forming.

Slide 186

Slide 186 text

It's habit forming.

Slide 187

Slide 187 text

A good start

Slide 188

Slide 188 text

No content

Slide 189

Slide 189 text

hi() code

Slide 190

Slide 190 text

hi() code % save

Slide 191

Slide 191 text

hi() code % save loadTask = (module) -> if fs.existsSync("#{process.cwd()}/node_modules/#{module}") grunt.loadNpmTasks(module) else grunt.loadTasks("#{__dirname}/../node_modules/#{module}/ tasks") npmTasks = grunt.util._(linemanNpmTasks).chain(). union("grunt-contrib-sass" if config.enableSass). union(config.loadNpmTasks). compact().value() loadTask task for task in npmTasks grunt.renameTask "copy", "images" loadTask "grunt-contrib-copy" # load again so webfonts can use it grunt.renameTask "copy", "webfonts" loadTask "grunt-contrib-copy" # load again to make available in userland compile

Slide 192

Slide 192 text

hi() code % save loadTask = (module) -> if fs.existsSync("#{process.cwd()}/node_modules/#{module}") grunt.loadNpmTasks(module) else grunt.loadTasks("#{__dirname}/../node_modules/#{module}/ tasks") npmTasks = grunt.util._(linemanNpmTasks).chain(). union("grunt-contrib-sass" if config.enableSass). union(config.loadNpmTasks). compact().value() loadTask task for task in npmTasks grunt.renameTask "copy", "images" loadTask "grunt-contrib-copy" # load again so webfonts can use it grunt.renameTask "copy", "webfonts" loadTask "grunt-contrib-copy" # load again to make available in userland compile loadTask task for task in npmTasks grunt.renameTask "copy", "images" loadTask "grunt-contrib-copy" # load again so webfonts can use it grunt.renameTask "copy", "webfonts" loadTask "grunt-contrib-copy" # load again to make available in userland loadTask = (module) -> if fs.existsSync("#{process.cwd()}/node_modules/#{module}") grunt.loadNpmTasks(module) else grunt.loadTasks("#{__dirname}/../node_modules/#{module}/tasks") npmTasks = grunt.util._(linemanNpmTasks).chain(). union("grunt-contrib-sass" if config.enableSass). union(config.loadNpmTasks). compact().value() concat

Slide 193

Slide 193 text

hi() code % save loadTask = (module) -> if fs.existsSync("#{process.cwd()}/node_modules/#{module}") grunt.loadNpmTasks(module) else grunt.loadTasks("#{__dirname}/../node_modules/#{module}/ tasks") npmTasks = grunt.util._(linemanNpmTasks).chain(). union("grunt-contrib-sass" if config.enableSass). union(config.loadNpmTasks). compact().value() loadTask task for task in npmTasks grunt.renameTask "copy", "images" loadTask "grunt-contrib-copy" # load again so webfonts can use it grunt.renameTask "copy", "webfonts" loadTask "grunt-contrib-copy" # load again to make available in userland compile loadTask task for task in npmTasks grunt.renameTask "copy", "images" loadTask "grunt-contrib-copy" # load again so webfonts can use it grunt.renameTask "copy", "webfonts" loadTask "grunt-contrib-copy" # load again to make available in userland loadTask = (module) -> if fs.existsSync("#{process.cwd()}/node_modules/#{module}") grunt.loadNpmTasks(module) else grunt.loadTasks("#{__dirname}/../node_modules/#{module}/tasks") npmTasks = grunt.util._(linemanNpmTasks).chain(). union("grunt-contrib-sass" if config.enableSass). union(config.loadNpmTasks). compact().value() concat hello world! play

Slide 194

Slide 194 text

< 100ms hi() code % save loadTask = (module) -> if fs.existsSync("#{process.cwd()}/node_modules/#{module}") grunt.loadNpmTasks(module) else grunt.loadTasks("#{__dirname}/../node_modules/#{module}/ tasks") npmTasks = grunt.util._(linemanNpmTasks).chain(). union("grunt-contrib-sass" if config.enableSass). union(config.loadNpmTasks). compact().value() loadTask task for task in npmTasks grunt.renameTask "copy", "images" loadTask "grunt-contrib-copy" # load again so webfonts can use it grunt.renameTask "copy", "webfonts" loadTask "grunt-contrib-copy" # load again to make available in userland compile loadTask task for task in npmTasks grunt.renameTask "copy", "images" loadTask "grunt-contrib-copy" # load again so webfonts can use it grunt.renameTask "copy", "webfonts" loadTask "grunt-contrib-copy" # load again to make available in userland loadTask = (module) -> if fs.existsSync("#{process.cwd()}/node_modules/#{module}") grunt.loadNpmTasks(module) else grunt.loadTasks("#{__dirname}/../node_modules/#{module}/tasks") npmTasks = grunt.util._(linemanNpmTasks).chain(). union("grunt-contrib-sass" if config.enableSass). union(config.loadNpmTasks). compact().value() concat hello world! play

Slide 195

Slide 195 text

No content

Slide 196

Slide 196 text

< 100ms hi() code % save loadTask = (module) -> if fs.existsSync("#{process.cwd()}/node_modules/#{module}") grunt.loadNpmTasks(module) else grunt.loadTasks("#{__dirname}/../node_modules/#{module}/ tasks") npmTasks = grunt.util._(linemanNpmTasks).chain(). union("grunt-contrib-sass" if config.enableSass). union(config.loadNpmTasks). compact().value() loadTask task for task in npmTasks grunt.renameTask "copy", "images" loadTask "grunt-contrib-copy" # load again so webfonts can use it grunt.renameTask "copy", "webfonts" loadTask "grunt-contrib-copy" # load again to make available in userland compile loadTask task for task in npmTasks grunt.renameTask "copy", "images" loadTask "grunt-contrib-copy" # load again so webfonts can use it grunt.renameTask "copy", "webfonts" loadTask "grunt-contrib-copy" # load again to make available in userland loadTask = (module) -> if fs.existsSync("#{process.cwd()}/node_modules/#{module}") grunt.loadNpmTasks(module) else grunt.loadTasks("#{__dirname}/../node_modules/#{module}/tasks") npmTasks = grunt.util._(linemanNpmTasks).chain(). union("grunt-contrib-sass" if config.enableSass). union(config.loadNpmTasks). compact().value() concat hello world! play

Slide 197

Slide 197 text

hi() code % save loadTask = (module) -> if fs.existsSync("#{process.cwd()}/node_modules/#{module}") grunt.loadNpmTasks(module) else grunt.loadTasks("#{__dirname}/../node_modules/#{module}/ tasks") npmTasks = grunt.util._(linemanNpmTasks).chain(). union("grunt-contrib-sass" if config.enableSass). union(config.loadNpmTasks). compact().value() loadTask task for task in npmTasks grunt.renameTask "copy", "images" loadTask "grunt-contrib-copy" # load again so webfonts can use it grunt.renameTask "copy", "webfonts" loadTask "grunt-contrib-copy" # load again to make available in userland compile loadTask task for task in npmTasks grunt.renameTask "copy", "images" loadTask "grunt-contrib-copy" # load again so webfonts can use it grunt.renameTask "copy", "webfonts" loadTask "grunt-contrib-copy" # load again to make available in userland loadTask = (module) -> if fs.existsSync("#{process.cwd()}/node_modules/#{module}") grunt.loadNpmTasks(module) else grunt.loadTasks("#{__dirname}/../node_modules/#{module}/tasks") npmTasks = grunt.util._(linemanNpmTasks).chain(). union("grunt-contrib-sass" if config.enableSass). union(config.loadNpmTasks). compact().value() concat test < 100ms

Slide 198

Slide 198 text

No content

Slide 199

Slide 199 text

Can your server host static files?

Slide 200

Slide 200 text

! $ lineman build !

Slide 201

Slide 201 text

! $ lineman build ! $ tree dist

Slide 202

Slide 202 text

! $ lineman build ! $ tree dist ! dist ├── css │ └── app.css ├── index.html └── js └── app.js

Slide 203

Slide 203 text

! $ heroku config:set BUILDPACK_URL=http://github.com/ testdouble/heroku-buildpack-lineman.git ! $ git push Heroku

Slide 204

Slide 204 text

! $ heroku config:set BUILDPACK_URL=http://github.com/ testdouble/heroku-buildpack-lineman.git ! $ git push Heroku Builds with Node.js

Slide 205

Slide 205 text

! $ heroku config:set BUILDPACK_URL=http://github.com/ testdouble/heroku-buildpack-lineman.git ! $ git push Heroku Builds with Node.js Runs without Node.js

Slide 206

Slide 206 text

Other Starter Projects

Slide 207

Slide 207 text

angular Other Starter Projects

Slide 208

Slide 208 text

backbone angular Other Starter Projects

Slide 209

Slide 209 text

ember backbone angular Other Starter Projects

Slide 210

Slide 210 text

ember backbone angular web libs Other Starter Projects

Slide 211

Slide 211 text

ember backbone angular web libs markdown blogs Other Starter Projects

Slide 212

Slide 212 text

Eminently extensible

Slide 213

Slide 213 text

Add a dependency ! $ npm install --save-dev grunt-typescript !

Slide 214

Slide 214 text

Add a dependency ! $ npm install --save-dev grunt-typescript ! $ cat package.json

Slide 215

Slide 215 text

Add a dependency ! $ npm install --save-dev grunt-typescript ! $ cat package.json ... "devDependencies": { ... "grunt-typescript": "0.1.6" }

Slide 216

Slide 216 text

! loadNpmTasks: ['grunt-typescript'], config/application.js

Slide 217

Slide 217 text

! loadNpmTasks: ['grunt-typescript'], ! prependTasks: { common: ['typescript'] }, config/application.js

Slide 218

Slide 218 text

! loadNpmTasks: ['grunt-typescript'], ! prependTasks: { common: ['typescript'] }, ! typescript: { compile: { src: 'app/js/**/*.ts', dest: 'generated/js/app.ts.js' } } config/application.js

Slide 219

Slide 219 text

! $ lineman run ! ... Running "typescript:compile" (typescript) task js: 0 files, map: 0 files, declaration: 0 files ... ! Run the task

Slide 220

Slide 220 text

JavaScript made easy.

Slide 221

Slide 221 text

No content

Slide 222

Slide 222 text

My name is Justin Searls Please tweet me @searls & Say [email protected]

Slide 223

Slide 223 text

noun project attribution Yarn designed by Marie Coons from The Noun Project! ! Scale designed by Ritika Khasgiwale from The Noun Project! ! Business Man designed by Piotrek Chuchla from The Noun Project! ! Business Man designed by Toke Thieden from The Noun Project! ! Airplane designed by Sven Gabriel from The Noun Project! ! Brick Wall designed by Juan Pablo Bravo from The Noun Project