背景
自分が仕事で書く WEB アプリケーションは多くの場合が小粒で、何か書く場合には sinatra
を使っています。
さらにテンプレートエンジンは slim
で、ビューが必要な場合は twitter bootstrap
を使って書きます。
で、新規で何か書き始める時に、それっぽいディレクトリ構成を作って、twitter bootstrap
とか jquery
とかをダウンロードして解凍してそれっぽいところに設置してー(もしくは既存プロジェクトをディレクトリごとコピーしてきて要らないファイル消してネームスペース変更してー)、とかっていうローテクな感じのことを毎回手動で行っていて、すっと実装に入れない!めんどくせー!ってなることが多いので、いったん整備してみました。
studio3104/ore-no-sinatra-skelton · GitHub
構成
javascript/css ライブラリ群は bower
で管理して、自分で書く javascript/css は coffee/sass で assets 配下に記述し、grunt
によって自動コンパイルされます。
いずれも public
配下に勝手に設置されます。
ore-no-sinatra-skelton ├── Gemfile ├── Gemfile.lock ├── Gruntfile.coffee ├── Procfile ├── assets │ ├── application.coffee │ └── application.scss ├── bower.json ├── config.ru ├── lib │ ├── skelton │ │ └── app.rb │ └── skelton.rb ├── package.json └── views ├── index.slim └── layout.slim
とりあえず試すだけならこんな感じでやればすぐに sinatra
まわりの実装に入れるでしょう
$ git clone https://github.com/studio3104/ore-no-sinatra-skelton.git $ cd ore-no-sinatra-skelton $ npm install -g grunt-cli $ npm install $ bundle $ bundle exec foreman start
これで、localhost:9292
でアプリケーションが起動します。
あとは lib
, views
, assets
配下のファイルを編集して開発していくだけです。
少し詳しく解説
bower
bower は javascript/css ライブラリ群 のパッケージマネージャで、bower.json
に記述されたパッケージを Search Bower packages から取得してきて bower_components
ディレクトリ配下にインストールして管理してくれます。
しかしただそれだけであって、例えば javascript のファイルを public/js
配下に、css のファイルを public/css
に設置するなど、そういったことを柔軟にやってくれるわけではありません。
そういったことは、後述する grunt
によって解決します。
bower.json 全体
dependencies
に必要なライブラリ名とバージョンを記述します。
exportsOverride
は、後述する grunt
の grunt-bower-task
から利用する場合に、dependencies
に記述されたそれぞれのライブラリの中からどのファイルが必要なのか明示するために記述します。
{ "name": "ore no sinatra skelton", "version": "0.0.1", "dependencies": { "vue": "latest", "underscore": "latest", "jquery": "latest", "bootstrap": "latest", "bootswatch": "latest", "Font-Awesome": "latest" }, "exportsOverride": { "jquery": { "js": "dist/*" }, "underscore": { "js": [ "underscore-min.js", "underscore-min.map" ] }, "vue": { "js": "dist/vue.min.js" }, "bootstrap": { "js": "dist/js/bootstrap.min.js", "css": [ "dist/css/*.min.css", "dist/css/*.map" ], "fonts": "dist/fonts/*" }, "bootswatch": { "css": "journal/bootstrap.min.css" }, "Font-Awesome": { "css": "css/font-awesome.min.css", "fonts": "fonts/*" } } }
bower 単体での利用
後述する grunt
などと組み合わせてではなく、単体で利用する場合は、上記のような bower.json
を用意するか、 bower init
コマンドによって対話的に bower.json
を作成した後、 bower install
コマンドを実行します。
bower install [package name]
コマンドを実行すると、[package name]
のみインストールすることも出来ます。
また、 bower install [package name] --save
コマンドを実行することで、[package name]
の情報が bower.json
に記述されます。
今回は単体利用の目的ではないので、詳しく知りたい場合は後述の参考リンクを参照してください。
grunt
grunt は javascript で振る舞いを記述するタスクランナーで、プラグインとの組み合わせによって、例えば指定したディレクトリ配下の css ファイルを minify したり結合したり、 例えば altJS で書かれたファイルをコンパイルして指定ディレクトリに設置したりなどが出来ます。
Gruntfile.coffee 全体
この Gruntfile.coffee
を設置して grunt
コマンドを実行すると以下の様になります。
(npm install -g grunt-cli
と、リポジトリ内の package.json
を設置しての npm install
も必要です)
bower install
を実行し、必要なファイルを適切にpublic
配下に設置assets
配下にあるassets
配下を監視し、ファイルの変更を検知したらコンパイルして適切に配置
module.exports = (grunt) -> grunt.initConfig coffee: compile: options: bara: true files: [ expand: true cwd: 'assets' src: [ '**/*.coffee' ] dest: 'public/js/' ext: '.js' ] compass: dist: options: sassDir: 'assets' cssDir: 'public/css/' cssmin: my_target: files: [ expand: true, cwd: 'public/css', src: [ '*.css', '!*.min.css' ], dest: 'public/css/', ext: '.min.css' ] bower: install: options: targetDir: 'public' layout: (type, component, bower_path) -> path = if component == 'bootswatch' && type == 'css' "#{type}/#{component}" else if component == 'bootstrap' && type == 'fonts' "css/#{type}" else type path install: true cleanTargetDir: true cleanBowerDir: false esteWatch: options: dirs: [ 'assets' ] 'coffee': (path) -> [ 'coffee' ] 'scss': (path) -> [ 'compass', 'cssmin' ] grunt.loadNpmTasks 'grunt-bower-task' grunt.loadNpmTasks 'grunt-contrib-coffee' grunt.loadNpmTasks 'grunt-contrib-compass' grunt.loadNpmTasks 'grunt-contrib-cssmin' grunt.loadNpmTasks 'grunt-este-watch' grunt.registerTask 'make', [ 'bower', 'coffee', 'compass', 'cssmin' ] grunt.registerTask 'default', [ 'make', 'esteWatch' ]
grunt-bower-task
Gruntfile.coffee
の bower
の部分だけ抜粋。
bower: install: options: targetDir: 'public' layout: (type, component, bower_path) -> path = if component == 'bootswatch' && type == 'css' "#{type}/#{component}" else if component == 'bootstrap' && type == 'fonts' "css/#{type}" else type path install: true cleanTargetDir: true cleanBowerDir: false
targetDir
bower install
したファイルをどこに設置するか指定。
layout
どのようなレイアウトで targetDir
配下に設置するか指定。
byType
, byComponent
から選ぶか、自前で定義する。
- byType で実行した場合のレイアウトの例
public ├── css │ ├── Font-Awesome │ │ └── font-awesome.min.css │ ├── application.css │ ├── bootstrap │ │ ├── bootstrap-theme.css.map │ │ ├── bootstrap-theme.min.css │ │ ├── bootstrap.css.map │ │ └── bootstrap.min.css │ └── bootswatch │ └── bootstrap.min.css ├── fonts │ ├── Font-Awesome │ │ ├── FontAwesome.otf │ │ ├── fontawesome-webfont.eot │ │ ├── fontawesome-webfont.svg │ │ ├── fontawesome-webfont.ttf │ │ └── fontawesome-webfont.woff │ └── bootstrap │ ├── glyphicons-halflings-regular.eot │ ├── glyphicons-halflings-regular.svg │ ├── glyphicons-halflings-regular.ttf │ └── glyphicons-halflings-regular.woff └── js ├── application.js ├── bootstrap │ └── bootstrap.min.js ├── jquery │ ├── jquery.js │ ├── jquery.min.js │ └── jquery.min.map ├── underscore │ ├── underscore-min.js │ └── underscore-min.map └── vue └── vue.min.js
- byComponent で実行した場合のレイアウトの例
public ├── Font-Awesome │ ├── css │ │ └── font-awesome.min.css │ └── fonts │ ├── FontAwesome.otf │ ├── fontawesome-webfont.eot │ ├── fontawesome-webfont.svg │ ├── fontawesome-webfont.ttf │ └── fontawesome-webfont.woff ├── bootstrap │ ├── css │ │ ├── bootstrap-theme.css.map │ │ ├── bootstrap-theme.min.css │ │ ├── bootstrap.css.map │ │ └── bootstrap.min.css │ ├── fonts │ │ ├── glyphicons-halflings-regular.eot │ │ ├── glyphicons-halflings-regular.svg │ │ ├── glyphicons-halflings-regular.ttf │ │ └── glyphicons-halflings-regular.woff │ └── js │ └── bootstrap.min.js ├── bootswatch │ └── css │ └── bootstrap.min.css ├── css │ └── application.css ├── jquery │ └── js │ ├── jquery.js │ ├── jquery.min.js │ └── jquery.min.map ├── js │ └── application.js ├── underscore │ └── js │ ├── underscore-min.js │ └── underscore-min.map └── vue └── js └── vue.min.js
- 自前で定義する場合
上述の2例では、vue/js/vue.min.js
か js/vue/vue.min.js
というようなスタイルになり、少々冗長な感じになってしまうので、自前で無名関数を定義してあげ、パスを返すようにしてあげれば、そのとおりの場所に設置してもらえる。
javascript ファイルは public/js
配下にフラットに並べたいし、css ファイルは public/css
配下にフラットに並べたいことが多いと思う。
例えば、このように定義してあげると、
layout: (type, component, bower_path) -> path = if component == 'bootswatch' && type == 'css' "#{type}/#{component}" else if component == 'bootstrap' && type == 'fonts' "css/#{type}" else type path
(bootswatch
に含まれる css ファイルは bootstrap.min.css
というファイル名で、bootstrap
のそれとカブってしまっていて上書きされてしまうので、byType
のレイアウトで設置)
(bootstrap
の bootstrap.min.css
は ../fonts/
でフォントファイルを参照しているので、public/css/fonts
配下に設置)
このようになる
public ├── css │ ├── application.css │ ├── bootstrap-theme.css.map │ ├── bootstrap-theme.min.css │ ├── bootstrap.css.map │ ├── bootstrap.min.css │ ├── bootswatch │ │ └── bootstrap.min.css │ ├── font-awesome.min.css │ └── fonts │ ├── glyphicons-halflings-regular.eot │ ├── glyphicons-halflings-regular.svg │ ├── glyphicons-halflings-regular.ttf │ └── glyphicons-halflings-regular.woff ├── fonts │ ├── FontAwesome.otf │ ├── fontawesome-webfont.eot │ ├── fontawesome-webfont.svg │ ├── fontawesome-webfont.ttf │ └── fontawesome-webfont.woff └── js ├── application.js ├── bootstrap.min.js ├── jquery.js ├── jquery.min.js ├── jquery.min.map ├── underscore-min.js ├── underscore-min.map └── vue.min.js
cleanTargetDir
targetDir
を初期化してからタスクを実行するかどうか指定。
上記の例だと、public
ディレクトリが実行前に削除され、再作成されます。
(これめっちゃコワイんだけど、targetDir
に '.' を指定して cleanTargetDir
を true にして実行するとプロジェクトがディレクトリごと消滅します。.git
とかも全部消える。コワイ。)
cleanBowerDir
特に指定のない場合は bower install
によって bower_components
ディレクトリにインストールされますが、そこを実行時に初期化するかどうかを指定する。
foreman
grunt-este-watch
を実行するようにした状態で grunt
コマンドを実行すると、フォアグラウンドで grunt
が起動しっぱなしになってしまうので、rack
アプリケーションを起動するために別のコンソールを起動しなくてはならなくなってしまいます。
そうしなくてよいように、以下のような Procfile
を用意して、bundle exec foreman start
します。
application: bundle exec rackup grunt: grunt
これであとはもう lib
, views
, assets
配下のファイルを編集して開発していくだけ。
やろうとしてやめたこととか
最初は、 views/layout.slim
に js/application.js
と css/application.min.css
だけ読み込ませるように記述して、requirejs
や browserify
などを利用して自前で書いた javascript と bower
でインストールしたライブラリをいい感じにガッチャンコしたひとつのファイルを作成する、わざわざ scss ファイルをインストールして compass
を利用して自前で書いた css と bower
でインストールしたライブラリをいい感じにガッチャンコしたひとつのファイルを作成する、などということを試みました。
が、やめました。
意図しない挙動などのトラブルに対処するにあたり、ガッチャンコされて minify されてしまっていると、どのライブラリ起因の問題なのか切り分けるときに一個ずつ読み込ませないようにして探るとか出来なくてとても大変です。
使うライブラリを link rel="stylesheet" type="text/css" href="/css/bootstrap.min.css"
だったり script src="/js/vue.min.js"
みたいにそれぞれ書けばいいじゃん。
javascript/css のファイルをテンプレートでそれぞれ一つずつ読み込ませたいというだけのために頑張ることじゃないよなぁ。ということで。
あと、「requirejs
は オワコンなんで browserify
使いましょう」とか突然若者に言われたので、両方それなりに調べて使えるようにしたけど、いずれを使うにしても目的と天秤にかけたらとにかくコストが高すぎると感じた。
現在ナウいものを選択したとしても、来年にはそれがオワコン化してるかもしれないし、本業の領域ではない分野のトレンドを追いかけ続けてオワコンじゃないものに切り替えていくのは、やはり今回の目的からすると異常な高コストでしかなかった。
なぜ今 coffee script
なのか
「coffee script
はオワコン。本命 TypeScript
, 対抗は Haxe
, Dreamy だけどおもしろいのが Dart
だ。」
みたいな意見を数人から頂戴した。
coffee script
は利用者が減ってきていて、コミュニティも以前のような活発さがなくなっているとのことだが、それは現状自分にとってはあまりリスクではなく、解説記事やサンプルコードなど、そんなに古くないものがまだまだいくらでもインターネット上に存在しているので、困難に対処しやすいという観点でもまだまだメリットが多い。
それに自分としては「文字列内での変数展開、簡潔なイテレーション記法、省略記法が使える」など、「簡潔な記述が出来る」ということだけ求めているので、情報の多さ(特に母国語での)という観点から coffee script
でとりあえず書いてみようということにした。
もしメンテを継続していくプロジェクトにおいて coffee script
を使わなくなったとしても、ゴリゴリ javascript を書いてるわけではないので他の altjs で書き換えるのはさほどコストをかけずに出来るだろうし、変換されたナマの javascript をベースにして開発を継続すれば良いのかなと思っております。
参考
昨今の自分用Webアプリケーションひな形 - naoyaのはてなダイアリー
昨今のWebアプリケーションのひな形その2 - Grunt - naoyaのはてなダイアリー
Bower入門(基礎編) - from scratch
Bower入門(応用編) - from scratch
jade, sass/compass, coffeescript, bowerで静的Webサイトを作るGrunt.js秘伝のタレ - Qiita