読者です 読者をやめる 読者になる 読者になる

Goldstine研究所

mosuke5's tech blog

sinatra-assetpackをproduction環境で使う時にはまったー

ruby sinatra sinatra-assetpack jsminify

Sinatraアプリケーションで、JSファイルを圧縮するsinatra-assetpackを利用していて、
production環境で動作させようとしたら動かなくなってしまった問題について調査した。

起こったこと

Sinatraを使ってアプリケーションを作っていて、development環境で完成したので、 prorudction環境で動作させようとしたら、jsのエラーが出るようになってしまい、正常に動かなくなった。

アクセスすると、以下のエラーがでる。要はjqueryがないとのこと。

Uncaught ReferenceError: $ is not defined

jQueryはもちろん読み込ませてるし、なんでproduction環境でだけ???

ソースコード

sinatraのメインアプリケーションであるapp.rbには以下のように、sinatra-assetpackを利用してjsを読み込んでいる。

assets do
  serve '/js', from: 'public/js'
  serve '/bower_components', from: 'bower_components'

  js :app, '/js/app.js', [
    '/js/index.js',
  ]

  js :libs, '/js/libs.js', [
    '/bower_components/jquery/dist/jquery.js',
    '/bower_components/bootstrap/dist/js/bootstrap.js',
  ]

  js_compression :jsmin
end

layout.erbにはもちろん、libs.jsが先に来るように記述している。

<%= js :libs %>
<%= js :app %>

sinatra-assetpackの挙動

productionでのみ発生する事象なので、改めてsinatra-assetpackのproduction環境時の挙動を確認した。
production環境では、複数のjsファイルを1つのファイルにまとめ、圧縮を行う。

development環境

3つのjsファイルがあったら以下のように3つ別々に読み込まれる。

<script type='text/javascript' src='/js/vendor/jquery.283479.js'></script>
<script type='text/javascript' src='/js/vendor/underscore.589491.js'></script>
<script type='text/javascript' src='/js/app/main.589491.js'></script>
production環境

3つあったjsファイルは1つにまとめられ、また圧縮される。

<script type='text/javascript' src='/js/app.589491.js'></script>

詳細はこちら
rstacruz/sinatra-assetpack · GitHub

事象の理由

ChromeデバッグツールのNetworkでファイルのダウンロード状況を確認してみると意外なことがわかった。 f:id:mosuke5:20150508171833p:plain

画像が小さくて見づらいかもしれないが、 5行目と6行目のapp.jsとlibs.jsで先にlibs.jsを読み込んでいるのに、おそらく圧縮とダウンロードに時間がかかり、
app.jsのほうが先にダウンロードが終わっている。

libs.jsにはjQueryなどが含まれていて、app.js内でjQueryを利用する。
よって、先にapp.jsが読み込まれてしまったことで、jQueryがねーぞ!と怒られてしまったのである。

対策と考慮

sinatra-assetpackなどを利用して、jsを圧縮する際には、 ファイルを1つにまとめたり圧縮したりする時間がかかることを十分に考慮しなければいけない。

あまり賢い手段をは言えないが、libs.jsとapp.jsひとつにまとめることで今回の事象は避けられる。 app.rb

assets do
  serve '/js', from: 'public/js'
  serve '/bower_components', from: 'bower_components'

  js :app, '/js/app.js', [
    '/bower_components/jquery/dist/jquery.js',
    '/bower_components/bootstrap/dist/js/bootstrap.js',
    '/js/index.js',
  ]

  js_compression :jsmin
end

また、事前に圧縮しておいて、ダウンロードだけする状態にしてもいいかもしれない。
rstacruz/sinatra-assetpack · GitHub