フロントエンドエンジニアのための「俺の最近のRailsのJS開発環境」を考察する


# 「俺の最近のRailsのJS開発環境」 for フロントエンドエンジニア
## TL;DR
* 「片手間のJS」ならhogehoge-railsで大丈夫（e.g. browserify-rails, sass-rails, etc.）
* 「本気でJS書く」ならsprocketsから切り離す（e.g. SPA, etc.）
* 迷えるフロントエンドエンジニアに愛の手を:pray::pray::pray:

※ 個人の見解です

## はじめに
本記事の内容は個人の見解であり、所属する組織の公式見解ではありません．
本職は学生，片手間にRailsエンジニア，そのさらに片手間に過激派フロントエンドエンジニアやってる人間の戯言です．

『[**俺の最近のRailsのJS開発環境を教えてやる**][recent-rails-js]』を受けて，フロントエンドエンジニアの視点から見たRailsのJS環境を考察してみた記事になります．
元記事をdisっているわけではなく，むしろリスペクトしたうえで視点を変えてみたという内容です（不愉快に思われたらもうしわけありません:bow:）．

あと，途中まで書いて1週間ぐらい寝かせたら何書いてたか忘れたので，推敲せずそのまま公開しちゃった記事になります．
言ってることが破綻していたら適宜修正するので優しく教示いただけると幸いです．

## [俺の最近のRailsのJS開発環境を教えてやる][recent-rails-js]について
Sprocketsを使っており，**Rails Wayから逸脱することなく**モダンJSに追従していくという例であり非常に参考になる．
開発時はいつもどおりに`app/assets`以下にコード書いていけばいいし，デプロイはいつもどおり`rake assets:precompile`すればいいだけである．
Railsエンジニアはいままでの環境をほぼ崩さなくていい．フロントエンドエンジニアは普段通りBrowserifyが利用できる．Babelifyを使えばES6のコードが書ける．頑張ったらテストだってできる．

Railsエンジニアが片手間にJS書いてて，最新のJSに追い付きたいということであれば十分である．

## なにが足りない？
一見すると何も問題ない，みんなが幸せになれる環境である．
なにが足りないのか．

例としてSassを取り上げる．
Sass 3.3より[mapというデータタイプが追加されている][sass-map]．飛び散りがちなSassの変数定義をいい感じのデータ構造でくるんだりできる，素晴らしい機能である．このSass 3.3がリリースされたのが[2014年7月7日][sass-3.3]．ではRailsのSprockets上でSassを扱うためのgemである[sass-rails][sass-rails]がSass 3.3に対応したのはいつか，[2014年12月13日リリースのv5.0.0][sass-rails-5]である（それ以前はSass 3.1しか使えなかった）．
Railsエンジニアは最新追っかけて新機能とキャッキャウフフしてて楽しいかもしれないが，フロントエンドエンジニアはSprocketsが対応してくれるまで首を長〜くして延々待たないといけないのである．
もし何らかの理由でRailsがアップデート出来ない状況であれば，それにつられてSassも半分ミイラ化した古のバージョンを使い続けることを強いられてしまう．

似たような状況はSassとsass-railsのような関係を持つライブラリ全てに起こり得る．
Browserifyとbrowserify-rails，TypeScriptとtsify，fontawesomeとfontawesome-rails，…．すぐに本家に追従してくれるかの保証はない．大きくAPIが変わったりするとついていってくれないかもしれない．

これらの被害を被るのはすべてフロントエンドエンジニアなのである．

普段からブラウザの依存問題に悩まされてるのに，こんどはRailsとモダンJSの相性問題に苦しめられるなんて…．

### JSのテストどうすんねん問題
個人的にはこれもかなり深刻だと思っている．

* sprocketsの`require`ディレクティブを使ったJSのコードをテストしたいとき
* `app/assets/templates`等に配置したJSから利用するテンプレート群の扱い

など．
JSのテストしたいだけなのにわざわざRailsアプリ立ち上げてコンパイルとかしてたら時間の無駄になる．
AngularJSのテストしたいからkarmaつかいたい！ karma-rails入れよう！とかなるとまた無駄な闇を踏むかもしれない．

karma-rails使うために面倒くさい作業をするのは「JSのテストなんてあまり関係ないRailsエンジニア」か「Railsは専門でない，JSのテスト書きたいフロントエンドエンジニア」か，どちらになるんでしょうかね．

### Sprockets.evil? # => false
別にSprocketsが悪であると言いたいわけではない．
登場した当初はCSS/JSの依存を解決することができる`require`ディレクティブは画期的であり素晴らしいものであった（筆者はSprockets以前のRailsを知らない世代ではあるが…）．

ただ，CSS/JSのエコシステム外からそれらを管理するということに無理が生じるのはどうしようもないことであるような気がする．
RailsがRails自身のことを考えつつ，CSS/JSの進化に完全に追従していくというのがそもそも無理な要求なのかもしれない…．


## ならどうするか: JSの世界でJSを書く
Sprocketsからの脱却を目指す．

### 方針
gulp/gruntなどのフロントエンド界のツールを利用してJS/CSSをビルドしていく．
これで無理なくフロントエンド側のエコシステムに追従できる．

Railsとの共存だが，ビルド結果を`public/{javascript,stylesheet}s`以下に配置することで`javascript_include_tag`や`stylesheet_include_tag`等のViewヘルパを利用できる．
また，本番ビルド（Rails界で言うところの`rake assets:precompile`）時に[**gulp-rev**][gulp-rev]及び[**gulp-rev-rails-manifest**][gulp-rev-rails-manifest]を利用することでdigestの恩恵も受けることができる．
これについては『[**Sprockets再考 モダンなJSのエコシステムとRailsのより良い関係を探す - Qiita**][joker1007-sprockets]』を参照されたい．

#### gulpfile？
gulpfileについては以下の記事でも読んで全力で憔悴したらいいと思う．
自分の専門領域ならyak shavingはたのしい:trollface: 

* [npm で依存もタスクも一元化する - Qiita][jxck-gulpfile]
* [もうgulpで憔悴しない - 低依存gulpfileのレシピ - Qiita][83-gulpfile]
* [web-starter-kit/gulpfile.babel.js at master · google/web-starter-kit][wsk-gulpfile]

### ディレクトリ構造
`app/assets`は使わない．
JavaScript/StyleSheetはすべて`frontend/assets`以下に書いていく: **Railsとはほぼ別プロジェクト扱い**になる．

```text
.
├── Gemfile                # いつもの（Rails側に徹する）
├── package.json           # JSのライブラリ（全部npmで）
├── gulpfile.coffee        # JS/CSSのビルドスクリプト（別に何でもいい）
├── app
│   ├── assets            # つかわない
・   ・
・   ・
・   ・
│   
├── frontend
│   └── assets
│       ├── javascripts   # *.js/*.coffee/*.ts等の元ファイル
│       ├── templates     # JSから利用するHTML等のテンプレート
│       └── typings       # TypeScript用型定義ファイル
├── tmp
│   └── javascripts       # 一時ファイル（templateをJSにしたやつとか）
├── public
│       ├── javascripts   # 開発用ビルドされたJSが入る
│       ├── stylesheets   # 開発用ビルドされたCSSが入る
│       └── assets        # 本番用ビルドされたJS/CSSが入る
・
・
・
```

### 別の方策: Sprocketsのload pathを弄る
@joker1007 氏よりフィードバックをいただいた．

<blockquote class="twitter-tweet" data-cards="hidden" lang="en"><p lang="ja" dir="ltr">俺の書いた記事が出てた。最近はgulp + browserifyを適当な所に吐いてそこをsprocketsのロードパスにしつつsourcemapの出力先を調整するか、public/assetsに直接吐くかのどっちかって感じ。 <a href="http://t.co/sLS46kUvON">http://t.co/sLS46kUvON</a></p>&mdash; イカはじめました joker0x3ef (@joker1007) <a href="https://twitter.com/joker1007/status/615711526209523712">June 30, 2015</a></blockquote>

<blockquote class="twitter-tweet" data-conversation="none" lang="en"><p lang="ja" dir="ltr"><a href="https://twitter.com/izumin5210">@izumin5210</a> ありがとうございますー。sprocketsのロードパスにしておいて圧縮とかmanifestの生成をassets:precompileに任せるイメージですね。vendor/assets以下にjqueryのプラグインを置くみたいなイメージです。</p>&mdash; イカはじめました joker0x3ef (@joker1007) <a href="https://twitter.com/joker1007/status/615722296175685633">June 30, 2015</a></blockquote>

<blockquote class="twitter-tweet" data-conversation="none" lang="en"><p lang="ja" dir="ltr"><a href="https://twitter.com/izumin5210">@izumin5210</a> やはり何もかも上手くいくわけはなく、sourcemapに対してブラウザがアクセスする時のパスとsourcemapが認識しているソースコードまでのパスを上手く調整しておかないと駄目なのが少し難点です。</p>&mdash; イカはじめました joker0x3ef (@joker1007) <a href="https://twitter.com/joker1007/status/615723596686737408">June 30, 2015</a></blockquote>

この記事では`public/javascripts`に吐いてたもの（=> browserifyのビルド結果とか）を何処か別の，**Sprocketsのload pathに含まれるディレクトリ**に吐くようにしたもの．これで`gulp-rev`/`gulp-rev-rails-manifest`やminify系のモジュールを使わず，`rake assets:precompile`だけで済ませられる．
イメージとしては`vendor/assets`に追加したみたいな感じ．
ただ，sourcemapまわりでややこしさは残るらしい．

僕もどちらかと言うと@joker1007さんのやり方が好きかもしれない（？）

Sprocketsのload path弄るのは『[2.2.1 Search Paths | The Asset Pipeline — Ruby on Rails Guides][sprockets-load-path]』に書いてある．



# Conclusion
だらだらの思いつくままに書いてみたが，結局は冒頭に書いたような考え方でいい気がしている．

> * 「片手間のJS」ならhogehoge-railsで大丈夫（e.g. browserify-rails, sass-rails, etc.）
> * 「本気でJS書く」ならsprocketsから切り離す（e.g. SPA, etc.）
> * 迷えるフロントエンドエンジニアに愛の手を:pray::pray::pray:

sprocketsと共に生きるか，見捨てるか．
現時点ではどちらがベストとも言い難い．
みんなで一緒になって，血で血を洗う殴り合いと泥臭い議論をしよう．


# References
* [**俺の最近のRailsのJS開発環境を教えてやる - Qiita**][recent-rails-js]
* [**Sprockets再考 モダンなJSのエコシステムとRailsのより良い関係を探す - Qiita**][joker1007-sprockets]
* [Sass 3.3で追加された新しいデータタイプ「マップ」まとめ解説 | HTML5Experts.jp][sass-map]

* [もうgulpで憔悴しない - 低依存gulpfileのレシピ - Qiita][83-gulpfile]
* [npm で依存もタスクも一元化する - Qiita][jxck-gulpfile]
* [web-starter-kit/gulpfile.babel.js at master · google/web-starter-kit][wsk-gulpfile]



[recent-rails-js]: http://qiita.com/kozo002/items/a276569e85395fce801e
[joker1007-sprockets]: http://qiita.com/joker1007/items/9068e223744b3ac8c6dd

[sass-map]: https://html5experts.jp/kosei27/3727/
[sass-3.3]: http://sass-lang.com/documentation/file.SASS_CHANGELOG.html#330_7_march_2014
[sass-rails]: https://github.com/rails/sass-rails
[sass-rails-5]: https://github.com/rails/sass-rails/releases/tag/v5.0.0

[gulp-rev]: https://www.npmjs.com/package/gulp-rev
[gulp-rev-rails-manifest]: https://www.npmjs.com/package/gulp-rev-rails-manifest

[83-gulpfile]: http://qiita.com/armorik83/items/0860dc097d11f18b9973
[jxck-gulpfile]: http://qiita.com/Jxck_/items/efaff21b977ddc782971
[wsk-gulpfile]: https://github.com/google/web-starter-kit/blob/master/gulpfile.babel.js

[sprockets-load-path]: http://guides.rubyonrails.org/asset_pipeline.html#search-paths