Rails command generetor を作ってみた
angularJsとbootstrapの練習を兼ねてRailsのgenerate modelのコマンドを出力するサービス(ツール?)を作ってみた。
Rails command Generator - http://rails-command-generator.herokuapp.com/
サービスそのものにはあまり意味はなく、個人的なプログラミングの練習ということで。(個人的にはコマンドをよく忘れるので重宝しますがw)
他のコマンドは時間があれば作りたいな。
以降は使った技術の話
今回使った技術は、
- まずnpmで開発に必要な各パッケージを管理。
- bowerを使って、js,CSSの外部ライブラリ群を管理。
- メタ言語としてTypeScript(JavaScript)、scss(CSS)、jade(html)を利用しました。
- gruntでその各メタ言語のファイルのコンパイルと自動リロードをさせています。
- javascriptのライブラリはangularJSを使っています(これがメインw)
- 一部に自動補完をするtypeahead.jsを利用しています。
- 全体のレイアウト・スタイルにはtwitter Bootstarpを使って細かい所は手で修正しています。
- テストフレームワークにはjasmineを使い、テストランナーとして、karmaを使っています。
以下は半分備忘録のつもりで書いているので、なんか雑ですが。。。
npm
今回利用したgruntライブラリ群と、karmaのライブラリ群を中心にパッケージとして登録しています。 expressとpathはherokuでアプリ動かすため用に入れています。
//package.json { "name": "rails-command-generator", "version": "0.0.0", "description": "", "main": "web.js", "scripts": { "test": "echo \"Error: no test specified\" && exit 1" }, "author": "", "license": "BSD", "devDependencies": { "grunt": "~0.4.1", "grunt-contrib-watch": "~0.5.3", "grunt-contrib-jade": "~0.8.0", "grunt-contrib-sass": "~0.5.0", "grunt-contrib-coffee": "~0.7.0", "grunt-contrib-compass": "~0.6.0", "grunt-contrib-connect": "~0.5.0", "grunt-bower-task": "~0.3.4", "grunt-ts": "~1.4.4", "karma-script-launcher": "~0.1.0", "karma-chrome-launcher": "~0.1.1", "karma-html2js-preprocessor": "~0.1.0", "karma-firefox-launcher": "~0.1.2", "karma-jasmine": "~0.1.3", "karma-coffee-preprocessor": "~0.1.1", "requirejs": "~2.1.9", "karma-requirejs": "~0.2.0", "karma-phantomjs-launcher": "~0.1.1", "karma": "~0.10.7", "karma-ng-scenario": "~0.1.0", "karma-coverage":"~0.1.4" }, "dependencies": { "express": "~3.4.5", "path": "~0.4.9" } }
bower
続いてbower.json、最終的には以下のようになりました。
ライブラリ群をdependenciesに書いているけど、devDependenciesでも良かったのかなと思います。
//bower.json { "name" : "rails-command-generator", "version" : "0.0.0", "authors" : [ "kyohei8 <tsukuda.kyouhei@gmail.com>" ], "license" : "MIT", "ignore" : [ "**/.*", "node_modules", "bower_components", "test", "tests" ], "dependencies" : { "angular" : "~1.2.2", "bootstrap" : "~3.0.2", "jasmine" : "~1.3.1", "typeahead.js" : "latest", "angular-typeahead" : "latest" }, "exportsOverride": { "angular" : { "js": "**/angular.min.js" }, "bootstrap" : { "js" : "**/bootstrap.min.js", "css": "**/bootstrap.min.css" }, "jquery" : { "js": "**/jquery.min.js" }, "jasmine" : { "js" : ["**/jasmine.js", "**/jasmine-html.js"], "css" : "**/jasmine.css", "favicon": "**/jasmine_favicon.png" }, "typeahead.js":{ "js" : "dist/typeahead.min.js" }, "angular-typeahead":{ "js" : "**/angular-typeahead.js" } } }
ディレクトリ構成を
root ├─ lib │ ├─js │ │ ├ angular.min.js │ │ └ … │ └─css │ └ bootstarp.css
のようにしたかったので、exportsOverrideは個別にファイルを指定しています。(詳細はこちらの記事参照)
grunt
gruntでは以下のことをしています。
bower:install
grunt-bower-task
を使ってbowerのインストールと各ファイルを指定のフォルダに配置。
(詳細はこちらの記事にもあります)
watch
app/src以下にある、ts, scss, jade のファイルが更新された時に、compileを行いそれぞれを js, css, htmlファイルとしてapp/distディレクトリに出力しています。 typescriptのcompileがCPUに結構影響するみたいで、corei7のPCでは2秒くらいなのに対し、 c2Dのcpuの場合は10秒くらいかかるので、すぐ保存ボタンを押してしまう僕には少しストレスでした。
connect
livereload
を指定することによって、保存時に(上記のcompileの後)ブラウザでリロードが行われます。
Chromeで行う場合はExtensionのliveReloadが必要になるので、インストール。
Chrome以外は以下のページを参考 https://github.com/livereload/livereload-extensions
grunt実行後、コンソール上では
$ grunt > Running "connect:livereload" (connect) task > Started connect web server on 0.0.0.0:9000. > Running "watch" task > Waiting...
のように表示され、この状態で 0.0.0.0:9000にアクセスし、ChromeのExtensionのliveReloadのアイコンの中心が黒くなったら(結構わかりづらいw)、connectされています。 この状態でなにかファイルを編集すると、ブラウザが自動的にリロードしてくれます。
module.exports = function (grunt) { 'use strict'; grunt.initConfig({ pkg : grunt.file.readJSON("package.json"), /** * bower install * $ grunt bower:install */ bower: { install: { options: { //中略 } } }, /** * watch * ファイル変更時、変換とブラウザのリロードをおこなう */ watch: { options: { livereload: true }, html: { files: ['app/src/**/*.jade'], tasks: ['jade'] }, css : { files: ['app/src/**/*.scss'], tasks: ['sass'] }, cs : { files: ['app/src/coffee/**/*.coffee', 'test/spec/**/*.coffee'], tasks: ['coffee'] }, ts : { files: ['app/src/ts/**/*.ts', 'test/spec/**/*.ts'], tasks: ['ts'] } }, /** * connect * 0.0.0.0:9000へアクセス */ connect: { livereload: { options: { hostname: '*', port : 9000 } } }, /** * jadeファイルのcompileを行う */ jade : { dest: { expand : true, cwd : 'app/src/jade', src : ['**/*.jade', '!**/*tmpl.jade'], dest : 'app/dist', ext : '.html', options: { pretty: true } } }, /** * sass */ sass : { dest: { // 略 } }, /** * compile CoffeeScript */ coffee : { dest : { // 略 }, test: {//同じフォルダに生成 // 略 } }, /** * compile TypeScript */ ts: { dest: { // 略 } } }); // load grunt.loadNpmTasks('grunt-bower-task'); grunt.loadNpmTasks('grunt-contrib-watch'); grunt.loadNpmTasks('grunt-contrib-jade'); grunt.loadNpmTasks('grunt-contrib-sass'); grunt.loadNpmTasks('grunt-contrib-coffee'); grunt.loadNpmTasks('grunt-contrib-connect'); grunt.loadNpmTasks('grunt-contrib-compass'); grunt.loadNpmTasks('grunt-ts'); // default task grunt.registerTask('default', ['connect', 'watch']); };
まとめ
モダンな開発手法をためしたくこんなことをしてみました。 個人的にはgruntのwatchからのコンパイル、リロードは便利で、メタ言語を使って開発する場合はなくてはならないコマンドになるのかなと思います。
実はgruntがなくても、Webstromがあればできたりするのですが、やはりチームでの開発とかだと、同じ構成(手法)でやらないといけなかったりするので、結局はgruntでやるほうがいいのかな。
あと、Gruntfile.jsとかbower.jsonとかは一度作っておけば流用もできるのでそこも利点かなと思います。