高尾宏治日記 on はてなブログ

プログラミング言語Rubyを中心とした技術情報を扱うブログです(Microsoft Surfaceはやめました)。

Turnipによる受け入れ試験中に、Rails.envをtestではないのもの(standalone)に変更する方法 (その2) #RubyJP

この記事はその1の続きです。

受け入れ試験の任意のタイミングで app/assets/javascripts/*.js.coffee.erb を再コンパイルする方法を調べています。

後述するようにとりあえずの方法はわかりましたが、あとでいろいろ考え直します。

[ダメ] Poltergeist の session.driver.quit

まずはPhantomJSのセッションを削除してみることにします。

jonleighton / poltergeist - Memory leak より

If you run a few capybara sessions manually please make sure you've called session.driver.quit when you don't need session anymore. Forgetting about this causes memory leakage and your system's resources can be exhausted earlier than you may expect.

spec/spec_helper.rb に以下を追加。

config.before(standalone: true) do
  page.driver.quit # 追加
  @_rails_env = Rails.env
  Rails.env = ENV['RAILS_ENV'] = 'standalone'
end

config.after(standalone: true) do
  Rails.env = ENV['RAILS_ENV'] = @_rails_env
  page.driver.quit # 追加
end

さっぱり feature が通らなくなった。セッションが切れたらだめだよね。

[ダメ] sprockets のキャッシュをクリア

tmp/cache/assets/test/sprockets/ 以下のファイルを削除してみる。

spec/spec_helper.rb に以下を追加。

config.before(standalone: true) do
  Dir.glob(Rails.root.join('tmp/cache/assets/test/sprockets/*')) { |f| FileUtils.remove_entry_secure(f) } # 追加
  @_rails_env = Rails.env
  Rails.env = ENV['RAILS_ENV'] = 'standalone'
end

config.after(standalone: true) do
  Rails.env = ENV['RAILS_ENV'] = @_rails_env
  Dir.glob(Rails.root.join('tmp/cache/assets/test/sprockets/*')) { |f| FileUtils.remove_entry_secure(f) } # 追加
end

再コンパイルはしない。 ただし、sprockets のキャッシュが削除されれば次回のテスト実行時に assets を再コンパイルすることが分かった。

[ダメ] feature を分けてみる

test と standalone の feature を分けてみたが、ダメだった。

[ダメ] feature を分けてbefore/after(:all)で sprockets のキャッシュをクリア

test と standalone の feature を分けて、なおかつ spec/spec_helper.rb に以下を追加したがダメだった。

config.before(:all, standalone: true) do
  Dir.glob(Rails.root.join('tmp/cache/assets/test/sprockets/*')) { |f| FileUtils.remove_entry_secure(f) }
end

config.after(:all, standalone: true) do
  Dir.glob(Rails.root.join('tmp/cache/assets/test/sprockets/*')) { |f| FileUtils.remove_entry_secure(f) }
end

(そろそろ Poltergeist/PhantomJS または Turnipソースコードを読まないといけないかな)

[ダメ] Capybara::Poltergeist::Driver#restart いろいろ

以下、ダメ。

config.before(:all, javascript: true, standalone: true) do
  Dir.glob(Rails.root.join('tmp/cache/assets/test/sprockets/*')) { |f| FileUtils.remove_entry_secure(f) }
end

config.after(:all, javascript: true, standalone: true) do
  Dir.glob(Rails.root.join('tmp/cache/assets/test/sprockets/*')) { |f| FileUtils.remove_entry_secure(f) }
end

config.before(javascript: true, standalone: true) do
  page.driver.restart
  @_rails_env = Rails.env
  Rails.env = ENV['RAILS_ENV'] = 'standalone'
end

config.after(javascript: true, standalone: true) do
  Rails.env = ENV['RAILS_ENV'] = @_rails_env
  page.driver.restart
end

以下もダメ。

config.before(:all, javascript: true, standalone: true) do
  Dir.glob(Rails.root.join('tmp/cache/assets/test/sprockets/*')) { |f| FileUtils.remove_entry_secure(f) }
end

config.before(javascript: true, standalone: true) do
  page.driver.reset!
  page.driver.restart
  @_rails_env = Rails.env
  Rails.env = ENV['RAILS_ENV'] = 'standalone'
end

config.after(javascript: true, standalone: true) do
  Rails.env = ENV['RAILS_ENV'] = @_rails_env
end

結局 sprockets 関連のソースコードを読んだ

sprockets の結果はメモリにキャッシュされるようだ。

  • sprockets-rails-2.0.1/lib/sprockets/railtie.rb:26 キャッシュのディレクトリを指定
  • sprockets-rails-2.0.1/lib/sprockets/railtie.rb:113 キャッシュをSprockets::Indexに変更している
  • その他もごにょごにょ <- 面倒になって記録をしなかった

なので(?)、以下のようにするとキャッシュが消えて、assets が再コンパイルされるようになった!

# assetsのキャッシュを削除する
#
# ただし、このメソッドを呼び出した後で page.driver.restart を実行しなけ
# ればブラウザ側のキャッシュが有効になったままとなる。
def expire_assets_cache
  env = Rails.application.assets
  if Rails.application.config.cache_classes
    env.instance_variable_set('@assets', {})
    env.instance_variable_set('@digests', {})
    env = env.instance_variable_get('@environment')
  end
  env.send(:expire_index!)
  Dir.glob(Rails.root.join('tmp/cache/assets/test/sprockets/*')) { |f|
    FileUtils.remove_entry_secure(f)
  }
end

config.before(:all, javascript: true, standalone: true) do
  expire_assets_cache
end

config.after(:all, javascript: true, standalone: true) do
  expire_assets_cache
end

config.before(javascript: true, standalone: true) do
  page.driver.restart

  @_rails_env = Rails.env
  Rails.env = ENV['RAILS_ENV'] = 'standalone'
end

config.after(javascript: true, standalone: true) do
  Rails.env = ENV['RAILS_ENV'] = @_rails_env

  page.driver.restart
end

とりあえずはこれで様子を見よう。

でもね、これはRails実装依存なのがとてもよくないですね。そもそも Rails.env が standalone のときだけ動作を切り替えるというのは実装がまずいのかもしれませんね。あとで考え直すというタスクをTODOに積んでおこう♪