Kentaro Kuribayashi's blog

Software Engineering, Management, Books, and Daily Journal.

RailsでTypeScriptを使う

JavaScriptは設計が難しい。経験上、すぐグシャグシャになってしまう。よくわからなくなる。もちろん、私のスキル不足というのはあるだろうけれども、スキルが不足してるのはしかたないので、学習は続けることは前提であるにしても、技術的に解決できるなら技術に頼りたい。そうした意味で、いわゆるAltJSの中ではTypeScriptが有望だろうと思う。

RailsとTypeScript

TypeScriptを使うにしても、それ単体で使うというシーンは、Webアプリケーション開発という文脈ではあまりない。たとえば、Railsで開発しているWebアプリケーションのフロントエンドを構成する言語として使うことになるだろう。その際、まず考えるべきことは、Asset Pipelineとどう折り合いをつけるかということだろう。

Asset Pipelineは、以下の機能を担っている:

  • 拡張子(例:application.js.coffee)により、各種言語のフロントエンドを判別し、最終的なプロダクトとしてのJavaScriptCSSを書き出す
  • 多数からなるそれらのファイルを、任意の数にまとめる
  • 一意のsignitureをファイル名に付与することで、ブラウザキャッシュとの折り合いをうまいことする

技術要素のライフサイクル

Railsから、フロントエンドを成す言語(coffee scriptやSassなど)を使う場合、一般には、それらに対応したRailsプラグイン的なものを使うことになる。それはそれでよいのではあるけれども、それらの処理系、あるいは、それがライブラリをも含む場合(jQueryとかBackboneとか)においては、Railsのライフサイクルとそれらのライフサイクルが異なりうる。すなわち、JavaScriptのライブラリやその派生言語であれば、それらのライフサイクルに基いて管理する方が適切であるという判断があり得る。

TypeScriptの場合、typescript-railsというプロダクトにより、Asset Pipelineの仕組み上でいい感じにしてくれるものがあるのだが、上記した通り、そもそもRailsとライフサイクルの異なるものを扱うのであれば、わざわざRailsに合わせる必要もないというのもひとつの考えだ。その場合、Railsの外で、自前でビルドする仕組みを用意することで、Asset Pipelineに頼らない環境を作るという選択肢もあるだろう。

Gulp + Rails

そのような環境を構築するに際して、それはそれでいろいろな選択肢が考えられるが、ここではgulp.jsを使ってみたいと思う。これは、単に私がそもそもフロントエンドまわりの技術に明るくないため、流行っていそうなものがよさそうだと思ってしまう偏向の現れである。

gulp.jsは、makeやrakeやその他もろもろそういった、処理の自動化を担当するツールである。まあ、rakeでもなんでもいいんだろうけど、今回使用するのはTypeScriptであるし、また、フロントエンド周りの各種言語、ツールへの対応が充実しているようなので、gulpを使うことにした。

全体としてはkentaro/gulp-for-railsに書いた通り。

gulpによるTypeScriptのビルド

TypeScriptは、JavaScriptコンパイルすることによって最終的な実行ファイルを生成するタイプの言語なので、Web開発においては、できるだけ楽な方法を採ることにより、開発スピードをさまたげないようにしたい。そこで、以下のようなgulpfile.jsを用意した。やっていることは単純で、app/assets/typescript以下にあるTypeScriptで書かれたファイルを監視して、JavaScriptコンパイルするというだけのものである。

var gulp = require('gulp');
var plumber = require('gulp-plumber');
var typescript = require('gulp-tsc');

gulp.task('typescript', function(){
  gulp.src(['app/assets/typescripts/*.ts'])
    .pipe(plumber())
    .pipe(typescript())
    .pipe(gulp.dest('public/javascripts/'))
});

gulp.task('watch', ['typescript'], function() {
  gulp.watch('app/assets/typescripts/*.ts', ['typescript'])
});

ちなみに、gulp-plumberを使っているのは、TypeScriptのコンパイルエラー時にgulpプロセスが落ちないようにするためのものである。詳細は、このGistの通り。

しかし、これをいちいち手動で起動するのは面倒なので、config/initializers/gulp_wathcer.rbに以下のような記述を行うことで、rails s時に自動的にgulp watchするようにした:

if Rails.env.development?
  pid = fork { exec 'gulp watch' }
  Process.detach(pid)
end

Asset Pipeline置き換えに向けて

上述のAsset Pipelineの機能のうち、上記だけだとTypeScriptをコンパイルし適当な場所に配置しただけであり、その機能の置き換えにはなっていない。とりあえず、RailsとTypeScirptを使ってみたかったぐらいの感じで試したので、上記はそこまでに留まっているが、それ以上に事を進めるためには、たとえば以下のエントリが参考になると思う。

まとめ

  • Railsと、JavaScriptあるいはそのライブラリとはライフサイクルが違うので、別に管理するという選択肢があり得る
  • TypeScriptとってもいいので、Railsや他のフレームワーク/言語に基づくプロダクトでも使いたい
  • いろんなツールを使えば、Asset Pipelineを置き換えることもできそうなので、がんばろう

しかし、Asset Pipelineを置き換えようと思うと自前でやることが多すぎるので、もっといい感じの仕組みがあるとよいなあとは思う。というか、Railsにもフロントエンド周りの現状にも明るくないので、こうやるのがベストプラクティス!みたいなのを知りたいところ。