エラーページをAsset Pipelineで管理する
はじめに
Railsでエラーページをいかに楽に書いて管理するかを模索した結果、Asset Pipelineを使う方法が良さそうだったのでまとめます。
追記(2015/08/17)
このエントリで提案した手法をgem化し、その使い方を解説したエントリを書きました。
よろしければこちらもご一読ください。
以下、元エントリになります。
背景
Railsはproduction
環境などでエラーが発生するとpublic
ディレクトリ下の404.html
などを表示するという仕組みを標準で用意しています。
単純でとてもいいと思うのですが、ただのhtml
なので以下のような辛みがあります。
- テンプレートエンジンが使えない
public/assets
以下のファイルを利用できない- ヘッダーやフッターを共有(部分テンプレートが利用)できない
これらの問題を解決策のひとつとして、config.exceptions_app
を使ってごにょごにょすることで、動的にエラーページを生成して返すという方法があります。
詳しくは以下の投稿(とコメント)を参照して下さい。
動的生成の限界
@keenさんが指摘するように、動的にエラーページを生成する手法では、メンテナンス時の503ページなどに対応できないという限界があります。
@yuki24 これ最大の問題は、rails落とすメンテ時の503とかRackの400とか、nginxが直接かえすstaticなページの需要が必ず残るので、動的だけでは足りなくて、同じ仕組みで静的ファイル生成も必要ってとこよねぇ。さらにassetのリンク切れ問題もあったり。
— Kenn Ejima (@kenn) 2014年9月2日
そこで本エントリでは、共通のヘッダーやフッターを使うのを諦める代わりに、エラーページを静的ファイルとしてAsset Pipelineで管理する方法を提案します。
提案手法
提案手法の流れは次の通りです。
- 標準の静的エラーページを削除
- 使用するテンプレートエンジンを登録する
app/assets/templates
のようなディレクトリを作り、そこにエラーページを書くassets:precompile
をフックして、実行後にエラーページをpublic
配下に移す
以下、順番に説明していきます。
使用するテンプレートエンジンを登録する
例えばslim
を使いたい場合、config/initializers/assets.rb
に次の行を追加します。
Rails.application.assets.register_engine('.slim', Slim::Template)
app/assets/templates
にエラーページを書く
別にディレクトリ名はなんでもいいんですが、僕はapp/assets/templates
という名前にしました。
このディレクトリに次のようなエラーページを書いておきます。
doctype html html head title | TestApp = stylesheet_link_tag 'application', media: 'all', 'data-turbolinks-track': true = javascript_include_tag 'application', 'data-turbolinks-track': true body = image_tag('404.png')
assets:precompile
実行後にエラーページをpublic
ディレクトリ下に移す
lib/tasks/assets.rake
のようなファイルを作成して、assets:precompile
の実行後にエラーページをpublic
ディレクトリ下に移す処理を書きます。
Rake::Task['assets:precompile'].enhance do logger = Logger.new(STDOUT) %i(404 422 500 503).each do |status_code| pattern = Rails.public_path.join('assets', "#{status_code}-*.html") src = Dir.glob(pattern).sort_by { |path| File.mtime(path) }.last next if src.nil? dest = Rails.public_path.join("#{status_code}.html") logger.info("mv #{src} to #{dest}") FileUtils.mv(src, dest) end end
使い方
app/assets/templates
以下をバージョン管理するようにして、デプロイ時にrake assets:precompile
を実行するようにすればOK。
デメリット
上でも述べたように、(render
メソッドが使えないので)ヘッダーやフッターのような共通のテンプレートを読み込むことができないことに加え、修正時に表示を確認するのが面倒くさい*1、というところでしょうか。
後者ですが、エラーページはそこまで頻繁に修正されることはないと思うので、許容範囲なのかなあと思っています。
おわりに
これをgemに組み込んで、各microserviceでエラーページを共通化するというのが本当にやりたかったことだったりする。
参考
- Asset Pipeline Error Pages — Devoh
- Notes / Precompiled Rails Static 404 and 500 Pages — Icelab, an Australian design and development studio
- GitHub - bronson/error_page_assets: Let the asset pipeline generate your static error pages.
- Rails: Slim Templates in the Asset Pipeline
- [rake] Rake で任意のタスクの前後に別のタスクを実行する - HsbtDiary(2012-02-10)