Slide 1

Slide 1 text

ḿΔ͔΋͠Εͳ͍ϑϩϯτΤϯυ։ൃ؀ڥ @koba04 (2012/10/21) 13೥10݄23೔ਫ༵೔

Slide 2

Slide 2 text

໨త • ͳΔ΂ࣗ͘ಈԽ • ςετΛॻ͘ • αʔόʔͱϑϩϯτͷ։ൃΛૄ݁߹ʹ • ָ͘͠։ൃ 13೥10݄23೔ਫ༵೔

Slide 3

Slide 3 text

૝ఆ • Backbone.js + Handlebars΍Angular.jsΛ࢖ͬͯɺΫϥΠΞϯτଆͰDOMΛ૊ Έཱ͍ͯͯ͘ΞϓϦΛ૝ఆ • αʔόʔଆ͸APIΛJSONͳͲͰฦ͚ͩ͢ 13೥10݄23೔ਫ༵೔

Slide 4

Slide 4 text

gruntͷpluginΛ࢖͏ 13೥10݄23೔ਫ༵೔

Slide 5

Slide 5 text

gruntͷϓϥάΠϯΛ࢖͏ • Πϯετʔϧ͍ͨ͠ϓϥάΠϯΛnpm install xxx --save-dev ͢Δ • Gruntfile.coffee(js) ͷதͰgrunt.loadTasksͯ͠ϓϥάΠϯͷઃఆΛॻ͚ͩ͘ % npm install grunt-contrib-coffee --save-dev % cat Gruntfile.coffee module.exports = (grunt) -> grunt.loadNpmTasks "grunt-contrib-coffee" grunt.initConfig coffee: compile: files: “public/js/app.js”: [ “coffee/lib.coffee” “coffee/app.coffee” ] % grunt coffee 13೥10݄23೔ਫ༵೔

Slide 6

Slide 6 text

livereload + watch + connectͰF5ΛࣗಈԽ 13೥10݄23೔ਫ༵೔

Slide 7

Slide 7 text

LiveReloadͱ͸ • ϑΝΠϧΛมߋͨ͠Βϒϥ΢βΛࣗಈతʹ࠶ಡΈࠐΈͯ͘͠ΕΔ LiveReload Server ϒϥ΢β֦ுΛ࢖͏͔ɺ LiveReload༻ͷScriptΛ ຒΊࠐΜͰ͓͘ 13೥10݄23೔ਫ༵೔

Slide 8

Slide 8 text

LiveReloadͱ͸ • ϑΝΠϧΛมߋͨ͠Βϒϥ΢βΛࣗಈతʹ࠶ಡΈࠐΈͯ͘͠ΕΔ LiveReload Server ϒϥ΢β֦ுΛ࢖͏͔ɺ LiveReload༻ͷScriptΛ ຒΊࠐΜͰ͓͘ ϑΝΠϧߋ৽ 13೥10݄23೔ਫ༵೔

Slide 9

Slide 9 text

LiveReloadͱ͸ • ϑΝΠϧΛมߋͨ͠Βϒϥ΢βΛࣗಈతʹ࠶ಡΈࠐΈͯ͘͠ΕΔ ϑΝΠϧͷ؂ࢹͯ͠ มߋ͕͋Ε͹௨஌ʂ LiveReload Server ϒϥ΢β֦ுΛ࢖͏͔ɺ LiveReload༻ͷScriptΛ ຒΊࠐΜͰ͓͘ ϑΝΠϧߋ৽ 13೥10݄23೔ਫ༵೔

Slide 10

Slide 10 text

LiveReloadͱ͸ • ϑΝΠϧΛมߋͨ͠Βϒϥ΢βΛࣗಈతʹ࠶ಡΈࠐΈͯ͘͠ΕΔ ϑΝΠϧͷ؂ࢹͯ͠ มߋ͕͋Ε͹௨஌ʂ LiveReload Server ϒϥ΢β֦ுΛ࢖͏͔ɺ LiveReload༻ͷScriptΛ ຒΊࠐΜͰ͓͘ ࠶ಡΈࠐΈ͢ΔΑ͏ ϒϥ΢βʹ௨஌ʂ ϑΝΠϧߋ৽ 13೥10݄23೔ਫ༵೔

Slide 11

Slide 11 text

LiveReloadͱ͸ • ϑΝΠϧΛมߋͨ͠Βϒϥ΢βΛࣗಈతʹ࠶ಡΈࠐΈͯ͘͠ΕΔ ϑΝΠϧͷ؂ࢹͯ͠ มߋ͕͋Ε͹௨஌ʂ LiveReload Server ϒϥ΢β֦ுΛ࢖͏͔ɺ LiveReload༻ͷScriptΛ ຒΊࠐΜͰ͓͘ ࠶ಡΈࠐΈ͢ΔΑ͏ ϒϥ΢βʹ௨஌ʂ ࠶ಡΈࠐΈʂ ϑΝΠϧߋ৽ 13೥10݄23೔ਫ༵೔

Slide 12

Slide 12 text

ඞཁͳ΋ͷ • grunt-contrib-watch • ϑΝΠϧͷ؂ࢹͱLiveReload Server (tiny-lr) • grunt-contrib-connect • LiveReloadͷͨΊͷScriptΛHTMLʹຒΊࠐΜͰ഑৴ͯ͘͠ΕΔ 13೥10݄23೔ਫ༵೔

Slide 13

Slide 13 text

grunt-contrib-watch • ؂ࢹ͢ΔϑΝΠϧͷઃఆͱLiveReloadͷ༗ޮԽ watch: options: livereload: true coffee: files: "coffee/**/*.coffee" tasks: ["coffee2js"] handlebars: files: "template/**/*.hbs" tasks: ["handlebars"] scss: files: "scss/**/*.scss" tasks: ["compass"] assemble: files: "assemble/**/*" tasks: ["assemble"] 13೥10݄23೔ਫ༵೔

Slide 14

Slide 14 text

grunt-contrib-connect • ੩తϑΝΠϧ഑৴αʔόʔͷઃఆ connect: server: options: livereload: true port: 9000 base: "public" 13೥10݄23೔ਫ༵೔

Slide 15

Slide 15 text

connect + proxy + easymock Ͱ αʔόʔ͔Β੾Γ཭͢ 13೥10݄23೔ਫ༵೔

Slide 16

Slide 16 text

connect + proxy + easymock • connectͰαʔόʔΛཱͯͯɺproxyڬΜͰ೚ҙʹAPIΛࢦఆग़དྷΔΑ͏ʹͨ͠ ΓɺmockͰఆٛͨ͠JSONฦ͢Α͏ʹ͢Δ • αʔόʔଆͷ։ൃͷ͜ͱΛؾʹͤͣɺAPIͷ࢓༷ΛܾΊ͓͚ͯ͹ϑϩϯτΤϯ υͷ։ൃΛਐΊΔ͜ͱ͕ग़དྷΔ • gruntͷΦϓγϣϯͰAPIͷ઀ଓઌΛࢦఆग़དྷΔΑ͏ʹ͓ͯ͘͜͠ͱͰɺAPIͱ ͷ݁߹ςετ΋؆୯ʹग़དྷΔ 13೥10݄23೔ਫ༵೔

Slide 17

Slide 17 text

connect + proxy + easymock Ͱͷߏ੒ HTTP /api/hoge proxy server A͞Μͷ։ൃ؀ڥ ઃఆʹΑͬͯ઀ଓઌΛ ม͑Δ stage؀ڥ easymockʹΑΔ mock؀ڥ ※Access-Control-Allow-Origin Headerͷઃఆ͕ඞཁ 13೥10݄23೔ਫ༵೔

Slide 18

Slide 18 text

grunt-contrib-connect, grunt-connect-proxy • proxyͷઃఆ proxyHost = if grunt.option 'proxy_host' ? then grunt.option 'proxy_host' else null : connect: server: options: livereload: true port: 9000 base: "public" middleware: (connect, options) -> [ connect.static(options.base) require("grunt-connect-proxy/lib/utils").proxyRequest ] proxies: [ context: [ "/users/" "/items/" ] host: if proxyHost? then proxyHost else "localhost" port: if proxyHost? then 80 else 3000 changeOrigin: true ] % grunt --proxy_host example.com ͷΑ͏ʹ઀ଓઌΛࢦఆग़དྷΔΑ͏ʹ 13೥10݄23೔ਫ༵೔

Slide 19

Slide 19 text

easymock (npm install -g easymock) • JSONϑΝΠϧΛஔ͚ͩ͘Ͱ؆୯ʹAPIͷϞοΫΛ࡞Δ͜ͱ͕ग़དྷΔ • proxyઌͷαʔόʔ΋ࢦఆͰ͖ΔͷͰɺ࣮૷ग़དྷ͍ͯͳ͍API͚ͩϞοΫ͢Δ ͜ͱ΋Մೳ • APIͷυΩϡϝϯτϖʔδ΋࡞ͬͯ͘ΕΔͷͰ࢓༷ͱͯ͠΋࢖͑ͦ͏ 13೥10݄23೔ਫ༵೔

Slide 20

Slide 20 text

easymock (config.json) • config.jsonʹઃఆΛॻ͘ { "proxy": { "server": "http://example.com", "calls": { "/items/": { "get": true } } }, "routes": [ "/users/:id" ] } proxy͍ͤͨ͞αʔόʔ͕͋Ε͹ઃఆ callsͰserverʹ౉͢ϦΫΤετΛఆٛ /user/1, user/2 ͷΑ͏ͳ ύεʹ஋͕෇͍ͯ͘ΔϧʔςΟϯάΛఆٛ 13೥10݄23೔ਫ༵೔

Slide 21

Slide 21 text

easymock (response) • APIߏ଄ʹԊͬͨσΟϨΫτϦߏ଄Λ࡞Δ % tree . !"" users # $"" _get.json ← GET /users/ ʹର͢ΔϨεϙϯε # !"" id_get.json ← GET /users/:id ʹର͢ΔϨεϙϯε !"" ... % cat users/id_get.json { "id": #{id}, ← /user/:id ͷidΛม਺ͱͯ͠࢖༻Ͱ͖Δ "name": "Sasuke", "age": 15, "message": "I'm Ninja" } • ϨεϙϯεͷJSONΛ഑ஔ 13೥10݄23೔ਫ༵೔

Slide 22

Slide 22 text

easymock (document) 13೥10݄23೔ਫ༵೔

Slide 23

Slide 23 text

testem + phantomjs + browser Ͱշదςετ 13೥10݄23೔ਫ༵೔

Slide 24

Slide 24 text

JavaScriptͷςετ • ༷ʑͳϨΠϠʔɺϥΠϒϥϦ͕͋ͬͯΑ͘Θ͔Βͳ͍... • TestRunner ... testem, karma • ࣮ߦ؀ڥ ... browser, phantomjs • Testing Framework ... mocha, Jasmine, Qunit • assertion library ... chai, expect, power assert 13೥10݄23೔ਫ༵೔

Slide 25

Slide 25 text

JavaScriptͷςετ • ༷ʑͳϨΠϠʔɺϥΠϒϥϦ͕͋ͬͯΑ͘Θ͔Βͳ͍... • TestRunner ... testem, karma • ࣮ߦ؀ڥ ... browser, phantomjs • Testing Framework ... mocha, Jasmine, Qunit • assertion library ... chai, expect, power assert ͱΓ͋͑ͣ࢖ͬͯΈΔͱΘ͔ͬͯ͘ΔͷͰ testem + phantonjs & browser + mocha + expect Λࢼͯ͠ΈΔ 13೥10݄23೔ਫ༵೔

Slide 26

Slide 26 text

testem • JavaScriptͷςετͷ࣮ߦΛ؅ཧͯ͘͠ΕΔ • ࣮ߦ͢Δϒϥ΢βͷࢦఆɺมߋΛ؂ࢹͯ͠ͷࣗಈ࣮ߦɺςετϑϨʔϜϫ ʔΫͷαϙʔτ 13೥10݄23೔ਫ༵೔

Slide 27

Slide 27 text

grunt-contrib-testem • grunt-contrib-testemೖΕͯɺGruntfileॻ͚ͩ͘ͰOK testem: app: src: [ "bower_components/expect/expect.js" "bower_components/sinon/index.js" "public/js/vendor.js" "public/js/template.js" "public/js/app.js" "spec/**/*_spec.coffee" ] options: test_page: "spec/runner.mustache" #parallel: 4 launch_in_dev: ["PhantomJS", "Chrome", "Safari"] launch_in_ci: ["PhantomJS", "Firefox"] ϥΠϒϥϦɺΞϓϦͷίʔυɺςετΛࢦఆ ࣮ߦ͢Δϒϥ΢β ςετΛ࣮ߦ͢ΔHTML mustacheͰ΋OK 13೥10݄23೔ਫ༵೔

Slide 28

Slide 28 text

grunt-contrib-testem(test_page) Test'em mocha.setup('bdd')
{{#serve_files}} {{/serve_files}} mocha.run() testemʹؚ·Ε͍ͯΔmochaͷϑΝΠϧͱ testem.jsΛಡΈࠐΉ GruntfileͰsrcʹॻ͍͓͍ͯͨ ϑΝΠϧΛsrcࢦఆ mochaͰ࣮ߦ mochaͷsetup DOMͷ४උ 13೥10݄23೔ਫ༵೔

Slide 29

Slide 29 text

mocha + expect + sinon Ͱָ͘͠ςετ 13೥10݄23೔ਫ༵೔

Slide 30

Slide 30 text

mocha + expect + sinon Ͱςετ • mocha͸࠷௿ݶͷςετϑϨʔϜϫʔΫͳͷͰɺassertion library΍mockతͳ ΋ͷ͸ࣗ෼ͰબͿඞཁ͕͋Δ (Jasmine͸શ෦ೖͬͯΔ) • ࠓճassertͷछྨ͕ଟ͔ͬͨͷͰexpect.jsΛ࢖༻ɻpower-assert΋γϯϓϧͰ Θ͔Γ΍͍݁͢ՌΛग़ྗͯ͘͠ΕΔͷͰؾʹͳ͍ͬͯΔ • stubɺmockϥΠϒϥϦ͸sinon.jsΛ࢖༻ • HTTP Request΍࣌ؒͷϞοΫɺ͜ͷؔ਺͕Ͳ͏͍͏Ҿ਺ͰԿճݺ͹Εͨͷ ͔(spy)ͳͲɺ୯ମςετʹ͸ඞਢ 13೥10݄23೔ਫ༵೔

Slide 31

Slide 31 text

mochaͰͷςετ݁Ռͷग़ྗ (browser) • ςετ݁Ռ͕៉ྷʹग़ྗ͞ΕΔ͜ͱ͸ॏཁ 13೥10݄23೔ਫ༵೔

Slide 32

Slide 32 text

mochaͰͷςετ describe("model.Base", -> describe "override Model.sync", -> user = null server = null User = myapp.model.Base.extend urlRoot: "/users/" initialize: (attrs) -> @setStorage "model:user:#{attrs.id}" before -> server = sinon.fakeServer.create() server.respondWith "GET", /\/users\//, [ 200, {}, JSON.stringify id: 1, name: "jim", age: 21 ] after -> server.restore() beforeEach -> user = new User id: 1, name: "jim", age: 20 it "set storage fetched data", -> spy = sinon.spy user.storage, 'set' user.fetch() server.respond() expect(spy.calledOnce).to.be.ok() expect(spy.args[0]).to.be.eql [ id:1, name:"jim", age:21 "read" ] spy.reset() ) coffeescriptͰॻ͚Δ serverͷmock࡞ͬͯ GET /users/ʹର͢Δ ϨεϙϯεΛఆٛ before͸࠷ॳʹ1ճ after͸࠷ޙʹ1ճ (serverΛ໭ͯ͠Δ) beforeEach͸ ຖճ࠷ॳʹ1ճ (afterEach΋͋Δ) spyͨؔ͠਺ͷ ঢ়ଶΛςετ 13೥10݄23೔ਫ༵೔

Slide 33

Slide 33 text

Travis-CIͰ͞ΒͳΔຬ଍ײΛ 13೥10݄23೔ਫ༵೔

Slide 34

Slide 34 text

Travis-CI • GitHubͰެ։ͯ͠ΔͳΒઃఆ͓ͯ͘͠΂͖ʢGreenʹͳΔ͜ͱͷຬ଍ײʣ • PhantomJSͱFirefoxͰςετ͕ग़དྷΔ 13೥10݄23೔ਫ༵೔

Slide 35

Slide 35 text

Travis-CI • travis.yml language: node_js node_js: - "0.10" before_script: - npm install -g grunt-cli bower - bower install - "export DISPLAY=:99.0" - "sh -e /etc/init.d/xvfb start" FirefoxͰςετΛ͢ΔͨΊʹඞཁ • package.json • Gruntfile.coffee "scripts": { "test": "grunt testem:ci:app" }, launch_in_ci: ["PhantomJS", "Firefox"] npm test Ͱ࣮ߦͰ͖ΔΑ͏ʹ͢Δ PhantomJSͱFIrefoxΛࢦఆ 13೥10݄23೔ਫ༵೔

Slide 36

Slide 36 text

assembleͰޮ཰HTML؅ཧ 13೥10݄23೔ਫ༵೔

Slide 37

Slide 37 text

assembleͱ͸ʁͦͯ͠࢖͏ཧ༝ʁ • assembleͱ͸ɺ੩తαΠτΛ࡞ΔͨΊͷgruntϓϥάΠϯɻ(like jekyll) • grunt-xxxxͰͳ͍͚ͲGruntͷϓϥάΠϯ • YAML(JSON)Λఆٛͨ͠΋ͷΛHandlebarsΛ࢖ͬͯHTMLʹు͖ग़͢͜ͱ͕ग़ དྷΔ • ن໿ͱ͔ϔϧϓͱ͔ΛYAMLͰ؅ཧ͍ͨ͠ • αʔόʔαΠυͷςϯϓϨʔτΤϯδϯΛ࢖Θͳ͍͚ͲɺimgɺjsɺcssͳͲΛ ຊ൪ͱ։ൃͰ෼͚ͨΓόʔδϣϯΛ෇Ճ͍ͨ͠ 13೥10݄23೔ਫ༵೔

Slide 38

Slide 38 text

assemble (Gruntfile) assemble: term: options: ext: ".hbs" data: "assemble/data/term.yaml" layout: "assemble/layout/term.hbs" files: [ expand: true cwd: "assemble/template/term" src: "**/*.hbs" dest: "template/term" ] publicHTMLDevelop: options: product: false jsVersion: 1 cssVersion: 1 files: [ src: "assemble/template/public_html/index.hbs" dest: "public/index.html" ] publicHTMLProduct: options: product: true jsVersion: 1 cssVersion: 1 staticHost: staticHost files: [ src: "assemble/template/public_html/index.hbs" dest: "public/index.product.html" ] ن໿ΛYAMLΛݩʹlayout/term.hbsΛݩʹɺ term/ҎԼͷhbsϑΝΠϧΛ࢖ͬͯ࡞੒͠ɺ ͜͜Ͱ͸͞ΒʹhbsϑΝΠϧͱͯ͠࢖༻ (BackboneͷςϯϓϨʔτͱͯ͠࢖͍͍ͨͨΊ) ຊ൪ͱ։ൃ༻ͷHTMLɻ productม਺ʹΑͬͯ index.hbs಺Ͱग़͠෼͚Δ 13೥10݄23೔ਫ༵೔

Slide 39

Slide 39 text

assemble (public/index.html) backbone and handlebars sample {{#if product}} {{/if}} {{#unless product}} {{/unless}} : {{#if product}} {{/if}} {{#unless product}} {{/unless}} productม਺ʹΑͬͯ index.hbs಺Ͱग़͠෼͚Δ 13೥10݄23೔ਫ༵೔

Slide 40

Slide 40 text

assemble (term) assemble/data/term.yaml term1: title: term1 content: this is term1 content term2: title: term2 content: this is term2 content assemble/layout/term.hbs
{{> body}}
assemble/template/term/term1.hbs

{{term.term1.title}}

{{term.term1.content}}

assemble/template/term/term2.hbs

{{term.term2.title}}

{{term.term2.content}}

template/term/term1.hbs

term1

this is term1 content

template/term/term2.hbs

term2

this is term2 content

YAMLܗࣜͷ σʔλ ϨΠΞ΢τ ςϯϓϨʔτ ֤ϖʔδͷ ςϯϓϨʔτ ੜ੒͞Εͨ ςϯϓϨʔτ 13೥10݄23೔ਫ༵೔

Slide 41

Slide 41 text

foremanͰ·ͱΊͯϓϩηε؅ཧ 13೥10݄23೔ਫ༵೔

Slide 42

Slide 42 text

foreman • ϓϩηε؅ཧͷRubyGems • easymockͱgruntΛ·ͱΊͯىಈ͢Δͷʹ࢖༻ • gruntͷtaskͰ΋ग़དྷΔ͚Ͳɺޙʑ૿͑Δ͜ͱ΋ߟ͑Δͱforemanͷํָ͕ • Procfileʹ࣮ߦίϚϯυॻ͚ͩ͘ % cat Procfile grunt: grunt easymock: sh easymock.sh 13೥10݄23೔ਫ༵೔

Slide 43

Slide 43 text

foreman 13೥10݄23೔ਫ༵೔

Slide 44

Slide 44 text

grunt-notifyͰ௨஌ 13೥10݄23೔ਫ༵೔

Slide 45

Slide 45 text

grunt-notify • ۭؾͷΑ͏ʹcoffeeͱ͔scssͷίϯύΠϧͯ͘͠ΕΔ͚Ͳɺࣦഊͨ͠ͱ͖͸ڭ ͑ͯཉ͍͠ • grunt-notifyΛ࢖ͬͯgrowl ͔௨஌ηϯλʔͰ௨஌͢Δ • Mac OS X 10.7ҎԼͷ৔߹͸ɺGrowl͚ͩͰͳ͘GrowlNotify΋Πϯετʔϧ͢ Δඞཁ͕͋Δ • http://growl.info/downloads#generaldownloads • Mac OS X 10.8Ҏ্ͷ৔߹͸ɺ௨஌ηϯλʔͰ௨஌ͯ͘͠ΕΔͷͰԿ΋ඞཁͳ ͍ 13೥10݄23೔ਫ༵೔

Slide 46

Slide 46 text

։ൃͷ༷ࢠ 13೥10݄23೔ਫ༵೔

Slide 47

Slide 47 text

։ൃͷ༷ࢠ gruntͱeasymock ͷ֬ೝ ςετ݁Ռ ͷ֬ೝ 13೥10݄23೔ਫ༵೔

Slide 48

Slide 48 text

։ൃͷ༷ࢠ gruntͱeasymock ͷ֬ೝ ςετ݁Ռ ͷ֬ೝ ίʔυΛॻ͍ͯΔ ͚ͩͰࣗಈ࣮ߦ͞Εͯ ݁ՌΛ֬ೝग़དྷΔʂ 13೥10݄23೔ਫ༵೔

Slide 49

Slide 49 text

͓·͚ 13೥10݄23೔ਫ༵೔

Slide 50

Slide 50 text

grunt-remove-logging • console.logΛফͯ͘͠ΕΔͷͰσόοά༻ʹΨϯΨϯconsole.logΛೖΕ͓ͯ ͚Δ • product༻ʹugilify͢Δલʹ࢖ͬͯΔ removelogging: product: src: "public/js/app.js" dest: "public/js/app.product.js" 13೥10݄23೔ਫ༵೔

Slide 51

Slide 51 text

matchdep • grunt.loadNpmTasksΛશ෦͢Δͷ͕໘౗Ͱ࢖ͬͯΔ require("matchdep").filterDev("grunt-*").forEach(grunt.loadNpmTasks) grunt.loadNpmTasks "assemble" # grunt-*ͳ໊લͰ͸ͳ͍ͷͰ.... 13೥10݄23೔ਫ༵೔

Slide 52

Slide 52 text

grunt-contrib-handlebarsͰCDNରԠ • processContentΛ࢖͏͜ͱͰɺίϯύΠϧ࣌ʹॲཧΛڬΊΔͷͰsrcଐੑͷ஋ ΛFQDNʹஔ׵͢Δͱ͔ग़དྷΔ staticHost = "http://example.com" grunt.initConfig : : handlebars: options: processContent: (content) -> content.replace /src="(.*)?"/gm, "src=\"#{staticHost}$1\"" 13೥10݄23೔ਫ༵೔

Slide 53

Slide 53 text

͓͠·͍ https://github.com/koba04/backbone-boilerplate 13೥10݄23೔ਫ༵೔