Upgrade to Pro
— share decks privately, control downloads, hide ads and more …
Speaker Deck
Speaker Deck
PRO
Sign in
Sign up
for free
捗るかもしれないフロントエンド開発環境
koba04
October 21, 2013
Programming
50
12k
捗るかもしれないフロントエンド開発環境
LiveReload
connect + proxy + easymock
testem
mocha + expect + sinon
assemble、foreman....
などを試してみた話です
koba04
October 21, 2013
Tweet
Share
More Decks by koba04
See All by koba04
koba04
0
500
koba04
2
1.4k
koba04
9
7.8k
koba04
0
87
koba04
1
1.1k
koba04
4
13k
koba04
6
1k
koba04
2
1.7k
koba04
2
420
Other Decks in Programming
See All in Programming
kawaji_scratch
0
100
manfredsteyer
PRO
0
150
aftiopk
0
100
temoki
2
220
lilobase
PRO
1
710
manfredsteyer
PRO
0
130
ianaya89
1
180
danilop
1
730
doyaaaaaken
0
820
xrdnk
0
110
grapecity_dev
0
170
alperhankendi
1
150
Featured
See All Featured
lara
15
2.7k
chrislema
231
16k
brad_frost
157
6.4k
eitanlees
112
10k
paulrobertlloyd
71
3.6k
caitiem20
308
17k
orderedlist
PRO
328
36k
michaelherold
225
8.5k
tmm1
61
9.3k
cromwellryan
104
6.1k
bkeepers
321
53k
jlugia
217
16k
Transcript
ḿΔ͔͠Εͳ͍ϑϩϯτΤϯυ։ൃڥ @koba04 (2012/10/21) 1310݄23ਫ༵
త • ͳΔࣗ͘ಈԽ • ςετΛॻ͘ • αʔόʔͱϑϩϯτͷ։ൃΛૄ݁߹ʹ • ָ͘͠։ൃ 1310݄23ਫ༵
ఆ • Backbone.js + HandlebarsAngular.jsΛͬͯɺΫϥΠΞϯτଆͰDOMΛ Έཱ͍ͯͯ͘ΞϓϦΛఆ • αʔόʔଆAPIΛJSONͳͲͰฦ͚ͩ͢ 1310݄23ਫ༵
gruntͷpluginΛ͏ 1310݄23ਫ༵
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 1310݄23ਫ༵
livereload + watch + connectͰF5ΛࣗಈԽ 1310݄23ਫ༵
LiveReloadͱ • ϑΝΠϧΛมߋͨ͠ΒϒϥβΛࣗಈతʹ࠶ಡΈࠐΈͯ͘͠ΕΔ LiveReload Server ϒϥβ֦ுΛ͏͔ɺ LiveReload༻ͷScriptΛ ຒΊࠐΜͰ͓͘ 1310݄23ਫ༵
LiveReloadͱ • ϑΝΠϧΛมߋͨ͠ΒϒϥβΛࣗಈతʹ࠶ಡΈࠐΈͯ͘͠ΕΔ LiveReload Server ϒϥβ֦ுΛ͏͔ɺ LiveReload༻ͷScriptΛ ຒΊࠐΜͰ͓͘ ϑΝΠϧߋ৽ 1310݄23ਫ༵
LiveReloadͱ • ϑΝΠϧΛมߋͨ͠ΒϒϥβΛࣗಈతʹ࠶ಡΈࠐΈͯ͘͠ΕΔ ϑΝΠϧͷࢹͯ͠ มߋ͕͋Ε௨ʂ LiveReload Server ϒϥβ֦ுΛ͏͔ɺ LiveReload༻ͷScriptΛ ຒΊࠐΜͰ͓͘
ϑΝΠϧߋ৽ 1310݄23ਫ༵
LiveReloadͱ • ϑΝΠϧΛมߋͨ͠ΒϒϥβΛࣗಈతʹ࠶ಡΈࠐΈͯ͘͠ΕΔ ϑΝΠϧͷࢹͯ͠ มߋ͕͋Ε௨ʂ LiveReload Server ϒϥβ֦ுΛ͏͔ɺ LiveReload༻ͷScriptΛ ຒΊࠐΜͰ͓͘
࠶ಡΈࠐΈ͢ΔΑ͏ ϒϥβʹ௨ʂ ϑΝΠϧߋ৽ 1310݄23ਫ༵
LiveReloadͱ • ϑΝΠϧΛมߋͨ͠ΒϒϥβΛࣗಈతʹ࠶ಡΈࠐΈͯ͘͠ΕΔ ϑΝΠϧͷࢹͯ͠ มߋ͕͋Ε௨ʂ LiveReload Server ϒϥβ֦ுΛ͏͔ɺ LiveReload༻ͷScriptΛ ຒΊࠐΜͰ͓͘
࠶ಡΈࠐΈ͢ΔΑ͏ ϒϥβʹ௨ʂ ࠶ಡΈࠐΈʂ ϑΝΠϧߋ৽ 1310݄23ਫ༵
ඞཁͳͷ • grunt-contrib-watch • ϑΝΠϧͷࢹͱLiveReload Server (tiny-lr) • grunt-contrib-connect •
LiveReloadͷͨΊͷScriptΛHTMLʹຒΊࠐΜͰ৴ͯ͘͠ΕΔ 1310݄23ਫ༵
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"] 1310݄23ਫ༵
grunt-contrib-connect • ੩తϑΝΠϧ৴αʔόʔͷઃఆ connect: server: options: livereload: true port: 9000
base: "public" 1310݄23ਫ༵
connect + proxy + easymock Ͱ αʔόʔ͔ΒΓ͢ 1310݄23ਫ༵
connect + proxy + easymock • connectͰαʔόʔΛཱͯͯɺproxyڬΜͰҙʹAPIΛࢦఆग़དྷΔΑ͏ʹͨ͠ ΓɺmockͰఆٛͨ͠JSONฦ͢Α͏ʹ͢Δ • αʔόʔଆͷ։ൃͷ͜ͱΛؾʹͤͣɺAPIͷ༷ΛܾΊ͓͚ͯϑϩϯτΤϯ
υͷ։ൃΛਐΊΔ͜ͱ͕ग़དྷΔ • gruntͷΦϓγϣϯͰAPIͷଓઌΛࢦఆग़དྷΔΑ͏ʹ͓ͯ͘͜͠ͱͰɺAPIͱ ͷ݁߹ςετ؆୯ʹग़དྷΔ 1310݄23ਫ༵
connect + proxy + easymock Ͱͷߏ HTTP /api/hoge proxy server
A͞Μͷ։ൃڥ ઃఆʹΑͬͯଓઌΛ ม͑Δ stageڥ easymockʹΑΔ mockڥ ※Access-Control-Allow-Origin Headerͷઃఆ͕ඞཁ 1310݄23ਫ༵
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 ͷΑ͏ʹଓઌΛࢦఆग़དྷΔΑ͏ʹ 1310݄23ਫ༵
easymock (npm install -g easymock) • JSONϑΝΠϧΛஔ͚ͩ͘Ͱ؆୯ʹAPIͷϞοΫΛ࡞Δ͜ͱ͕ग़དྷΔ • proxyઌͷαʔόʔࢦఆͰ͖ΔͷͰɺ࣮ग़དྷ͍ͯͳ͍API͚ͩϞοΫ͢Δ ͜ͱՄೳ
• APIͷυΩϡϝϯτϖʔδ࡞ͬͯ͘ΕΔͷͰ༷ͱͯ͑ͦ͠͏ 1310݄23ਫ༵
easymock (config.json) • config.jsonʹઃఆΛॻ͘ { "proxy": { "server": "http://example.com", "calls":
{ "/items/": { "get": true } } }, "routes": [ "/users/:id" ] } proxy͍ͤͨ͞αʔόʔ͕͋Εઃఆ callsͰserverʹ͢ϦΫΤετΛఆٛ /user/1, user/2 ͷΑ͏ͳ ύεʹ͕͍ͯ͘ΔϧʔςΟϯάΛఆٛ 1310݄23ਫ༵
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Λஔ 1310݄23ਫ༵
easymock (document) 1310݄23ਫ༵
testem + phantomjs + browser Ͱշదςετ 1310݄23ਫ༵
JavaScriptͷςετ • ༷ʑͳϨΠϠʔɺϥΠϒϥϦ͕͋ͬͯΑ͘Θ͔Βͳ͍... • TestRunner ... testem, karma • ࣮ߦڥ
... browser, phantomjs • Testing Framework ... mocha, Jasmine, Qunit • assertion library ... chai, expect, power assert 1310݄23ਫ༵
JavaScriptͷςετ • ༷ʑͳϨΠϠʔɺϥΠϒϥϦ͕͋ͬͯΑ͘Θ͔Βͳ͍... • TestRunner ... testem, karma • ࣮ߦڥ
... browser, phantomjs • Testing Framework ... mocha, Jasmine, Qunit • assertion library ... chai, expect, power assert ͱΓ͋͑ͣͬͯΈΔͱΘ͔ͬͯ͘ΔͷͰ testem + phantonjs & browser + mocha + expect Λࢼͯ͠ΈΔ 1310݄23ਫ༵
testem • JavaScriptͷςετͷ࣮ߦΛཧͯ͘͠ΕΔ • ࣮ߦ͢ΔϒϥβͷࢦఆɺมߋΛࢹͯ͠ͷࣗಈ࣮ߦɺςετϑϨʔϜϫ ʔΫͷαϙʔτ 1310݄23ਫ༵
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 1310݄23ਫ༵
grunt-contrib-testem(test_page) <!doctype html> <html> <head> <title>Test'em</title> <link rel="stylesheet" href="/testem/mocha.css"> <script
src="/testem/mocha.js"></script> <script src="/testem.js"></script> <script> mocha.setup('bdd') </script> </head> <body> <div id="mocha"></div> <!-- for unit test --> <article style="display: none;"> <section id="mainview"></section> <section id="subview"></section> </article> {{#serve_files}} <script src="{{src}}"></script> {{/serve_files}} <script> mocha.run() </script> </body> </html> testemʹؚ·Ε͍ͯΔmochaͷϑΝΠϧͱ testem.jsΛಡΈࠐΉ GruntfileͰsrcʹॻ͍͓͍ͯͨ ϑΝΠϧΛsrcࢦఆ mochaͰ࣮ߦ mochaͷsetup DOMͷ४උ 1310݄23ਫ༵
mocha + expect + sinon Ͱָ͘͠ςετ 1310݄23ਫ༵
mocha + expect + sinon Ͱςετ • mocha࠷ݶͷςετϑϨʔϜϫʔΫͳͷͰɺassertion librarymockతͳ ͷࣗͰબͿඞཁ͕͋Δ
(Jasmineશ෦ೖͬͯΔ) • ࠓճassertͷछྨ͕ଟ͔ͬͨͷͰexpect.jsΛ༻ɻpower-assertγϯϓϧͰ Θ͔Γ͍݁͢ՌΛग़ྗͯ͘͠ΕΔͷͰؾʹͳ͍ͬͯΔ • stubɺmockϥΠϒϥϦsinon.jsΛ༻ • HTTP Request࣌ؒͷϞοΫɺ͜ͷ͕ؔͲ͏͍͏ҾͰԿճݺΕͨͷ ͔(spy)ͳͲɺ୯ମςετʹඞਢ 1310݄23ਫ༵
mochaͰͷςετ݁Ռͷग़ྗ (browser) • ςετ݁Ռ͕៉ྷʹग़ྗ͞ΕΔ͜ͱॏཁ 1310݄23ਫ༵
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ͨؔ͠ͷ ঢ়ଶΛςετ 1310݄23ਫ༵
Travis-CIͰ͞ΒͳΔຬײΛ 1310݄23ਫ༵
Travis-CI • GitHubͰެ։ͯ͠ΔͳΒઃఆ͓͖ͯ͘͠ʢGreenʹͳΔ͜ͱͷຬײʣ • PhantomJSͱFirefoxͰςετ͕ग़དྷΔ 1310݄23ਫ༵
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Λࢦఆ 1310݄23ਫ༵
assembleͰޮHTMLཧ 1310݄23ਫ༵
assembleͱʁͦͯ͠͏ཧ༝ʁ • assembleͱɺ੩తαΠτΛ࡞ΔͨΊͷgruntϓϥάΠϯɻ(like jekyll) • grunt-xxxxͰͳ͍͚ͲGruntͷϓϥάΠϯ • YAML(JSON)Λఆٛͨ͠ͷΛHandlebarsΛͬͯHTMLʹు͖ग़͢͜ͱ͕ग़ དྷΔ •
نͱ͔ϔϧϓͱ͔ΛYAMLͰཧ͍ͨ͠ • αʔόʔαΠυͷςϯϓϨʔτΤϯδϯΛΘͳ͍͚ͲɺimgɺjsɺcssͳͲΛ ຊ൪ͱ։ൃͰ͚ͨΓόʔδϣϯΛՃ͍ͨ͠ 1310݄23ਫ༵
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Ͱग़͚͠Δ 1310݄23ਫ༵
assemble (public/index.html) <!DOCTYPE html> <!-- generated by assemble --> <html
lang="ja"> <head> <title>backbone and handlebars sample</title> <meta http-equiv="Cache-Control" content="no-cache"> <meta http-equiv="Pragma" content="no-cache"> <meta charset="utf8" /> {{#if product}} <link rel="stylesheet" href="{{staticHost}}/css/product/app.css?v={{cssVersion}}"> {{/if}} {{#unless product}} <link rel="stylesheet" href="/css/app.css?v={{cssVersion}}"> {{/unless}} </head> <body> : {{#if product}} <script src="{{staticHost}}/js/vendor.js?v={{jsVersion}}"></script> <script src="{{staticHost}}/js/template.product.js?v={{jsVersion}}"></script> <script src="{{staticHost}}/js/app.product.js?v={{jsVersion}}"></script> {{/if}} {{#unless product}} <script src="/js/vendor.js?v={{jsVersion}}"></script> <script src="/js/template.js?v={{jsVersion}}"></script> <script src="/js/app.js?v={{jsVersion}}"></script> {{/unless}} </body> </html> productมʹΑͬͯ index.hbsͰग़͚͠Δ 1310݄23ਫ༵
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 <div> <!-- generated by assemble --> {{> body}} </div> assemble/template/term/term1.hbs <h1>{{term.term1.title}}</h1> <p>{{term.term1.content}}</p> assemble/template/term/term2.hbs <h1>{{term.term2.title}}</h1> <p>{{term.term2.content}}</p> template/term/term1.hbs <div> <!-- generated by assemble --> <h1>term1</h1> <p>this is term1 content</p> </div> template/term/term2.hbs <div> <!-- generated by assemble --> <h1>term2</h1> <p>this is term2 content</p> </div> YAMLܗࣜͷ σʔλ ϨΠΞτ ςϯϓϨʔτ ֤ϖʔδͷ ςϯϓϨʔτ ੜ͞Εͨ ςϯϓϨʔτ 1310݄23ਫ༵
foremanͰ·ͱΊͯϓϩηεཧ 1310݄23ਫ༵
foreman • ϓϩηεཧͷRubyGems • easymockͱgruntΛ·ͱΊͯىಈ͢Δͷʹ༻ • gruntͷtaskͰग़དྷΔ͚Ͳɺޙʑ૿͑Δ͜ͱߟ͑Δͱforemanͷํָ͕ • Procfileʹ࣮ߦίϚϯυॻ͚ͩ͘ %
cat Procfile grunt: grunt easymock: sh easymock.sh 1310݄23ਫ༵
foreman 1310݄23ਫ༵
grunt-notifyͰ௨ 1310݄23ਫ༵
grunt-notify • ۭؾͷΑ͏ʹcoffeeͱ͔scssͷίϯύΠϧͯ͘͠ΕΔ͚Ͳɺࣦഊͨ͠ͱ͖ڭ ͑ͯཉ͍͠ • grunt-notifyΛͬͯgrowl ͔௨ηϯλʔͰ௨͢Δ • Mac OS
X 10.7ҎԼͷ߹ɺGrowl͚ͩͰͳ͘GrowlNotifyΠϯετʔϧ͢ Δඞཁ͕͋Δ • http://growl.info/downloads#generaldownloads • Mac OS X 10.8Ҏ্ͷ߹ɺ௨ηϯλʔͰ௨ͯ͘͠ΕΔͷͰԿඞཁͳ ͍ 1310݄23ਫ༵
։ൃͷ༷ࢠ 1310݄23ਫ༵
։ൃͷ༷ࢠ gruntͱeasymock ͷ֬ೝ ςετ݁Ռ ͷ֬ೝ 1310݄23ਫ༵
։ൃͷ༷ࢠ gruntͱeasymock ͷ֬ೝ ςετ݁Ռ ͷ֬ೝ ίʔυΛॻ͍ͯΔ ͚ͩͰࣗಈ࣮ߦ͞Εͯ ݁ՌΛ֬ೝग़དྷΔʂ 1310݄23ਫ༵
͓·͚ 1310݄23ਫ༵
grunt-remove-logging • console.logΛফͯ͘͠ΕΔͷͰσόοά༻ʹΨϯΨϯconsole.logΛೖΕ͓ͯ ͚Δ • product༻ʹugilify͢ΔલʹͬͯΔ removelogging: product: src: "public/js/app.js"
dest: "public/js/app.product.js" 1310݄23ਫ༵
matchdep • grunt.loadNpmTasksΛશ෦͢Δͷ͕໘ͰͬͯΔ require("matchdep").filterDev("grunt-*").forEach(grunt.loadNpmTasks) grunt.loadNpmTasks "assemble" # grunt-*ͳ໊લͰͳ͍ͷͰ.... 1310݄23ਫ༵
grunt-contrib-handlebarsͰCDNରԠ • processContentΛ͏͜ͱͰɺίϯύΠϧ࣌ʹॲཧΛڬΊΔͷͰsrcଐੑͷ ΛFQDNʹஔ͢Δͱ͔ग़དྷΔ staticHost = "http://example.com" grunt.initConfig : :
handlebars: options: processContent: (content) -> content.replace /src="(.*)?"/gm, "src=\"#{staticHost}$1\"" 1310݄23ਫ༵
͓͠·͍ https://github.com/koba04/backbone-boilerplate 1310݄23ਫ༵