Railsで作っているFacebookアプリをRails3.1に更新する

HerokuでFacebookアプリを運用しており、当初はRails3.0.9ベースで作り始めたが、Railsが進化するスピードが速いので常にEdgeの状況を追いかけていたいので版をあげることにした。
基本はRailsCastsで紹介されていることをベースに修正していけばよいがいくつかはまった点があるのでメモとして残しておく。

Gemfileを更新する

Gemfileで定義しているrailsの版を3.1以降を使うように修正する。

gem '~> 3.1'

そのあとbundle updateする。
あとdeprecatedになったので、development.rbの設定で

config.action_view.debug_rjs = true

を削除しておく。

Asset Pipelineを有効化する

Rails3.1より前のRailsアプリではpublic/以下に画像やスタイルシートJavaScriptを置いておく。もちろんこのままでもRailsは以前と変わらず動作し続けるし、問題ないけど3.1にアップグレードする意味がない。3.1にアップグレードして嬉しいことは、やはりAsset Pipelineを使えることだろう。
Gemfileに次のgemを使うように定義する。Rails3.1以降でRailsアプリを作り始めたのであれば最初から定義されているものだ。

group :assets do
  gem 'sass-rails', "~> 3.1"
  gem 'coffee-rails', "~> 3.1"
  gem 'uglifier', "~> 3.1"
end

次に、config/application.rbでassetsの設定を記述する。Rails3.1まではapplication.rbの先頭はこのような定義だった。

# config/application.rb
Bundle.require(:default, Rails.env) if defined?(Bundler)

3.1からは次のように定義されるようになった。

if defined?(Bundler)
  Bundler.require *Rails.groups(:assets => %w(development test))
end

さらにApplicationクラスで次の設定項目を加える

config.assets.enabled = true
config.assets.version = '1.0'

また、環境毎にAsset Pipelineの挙動を変更する。開発時はデバッグを有効にして圧縮しないように設定しておくと便利だと思う(3.1のデフォルト設定)。

# config/development.rb 
config.assets.compress = false
config.assets.debug = true

もちろんリリース時には圧縮するようにして、さらにURLにfingerprintを付け加える。

# config/production.rb
config.assets.compress = true
config.assets.compile = false
config.assets.digest = true

assetsを移動させる

asset pipelineはapp/assets以下に保存しているものを扱う。public/images、public/javascripts、public/stylesheetsを移動させる。

$ mkdir app/assets
$ mv public/images app/assets
$ mv public/javascripts app/assets
$ mv public/stylesheets app/assets

jQueryに関連するjavascripts以下にあるファイルは移動させなくてもよい。jquery.jsやjquery.min.js、rails.jsだ。これらは、jquery-railsのgemが保持しているものをコピーしてくれるからだ。サードパーティ製のライブラリをわざわざアプリケーション側で管理することがなくなった。
また、jQuery pluginファイルはlib/assetsやvendor/assets以下に置いたほうがよい。

次に、manifestファイルの作成にとりかかる。
デフォルトでsprocketsはassets/stylesheets/application.cssとassets/javascripts/application.jsを読み込む設定だ。application.css、application.jsで

/*
 *= require base
 */
//= require jquery
//= require jquery_ujs
//= require_tree .

このコメントはどのassetをコンパイルすればいいのかを示すものだ。

画像のハードコーディングを修正する

運営しているFacebookアプリではいくつか画像をハードコードしている部分があった。その場合、画像へのGETが走っても"No route matches"となってしまう。
簡単な解決方法として、

<img src="/images/please_like.jpg">

<img src="/assets/please_like.jpg">

に修正する。

これでdevelopmentモードの場合はうまくいくが、先ほどproductionモードではassets.digestを有効化した。そのためURLが生成されず、画像にアクセスできない。
したがってimage_tag()ヘルパーメソッドを使って画像を表示しなければならない。

さらに、CSSで背景等に画像を指定していることはよくある。

.foo-link { background:url(/images/foo-link.png) no-repeat; }

この場合は、asset_path()ヘルパーを使う。スタイルファイルを*.css.erbというファイル名に変更しておくことでerbで評価してくれたあと、CSSを出力してくれる。

.foo-link { background:url(<%= asset_path('foo-link.png') no-repeat %>) }

Herokuにデプロイする前にproductionモードで確認する

では、Herokuにデプロイする前にproductionモードでrails serverを起動してテストしよう。

$ r server -e production

こうすると/assets/{image}でアクセスできなくなった。assetを事前にコンパイルしておかなければならない。もしくはconfig/applicationで事前コンパイルを無効にしておく。

config.assets.initialize_on_precompile

HerokuでFacebookアプリを運営しているので必ずこの設定が必要だ。
そして、

$ rake assets::precompile

rakeタスクを実行してassetをコンパイルする。precompileするのはデフォルトでapp/assets/css/application.css、app/assets/javascripts/application.js なので、たとえば、アプリケーション本体と管理画面等で一つのRailsアプリのなかで分割している場合は、管理ページ側ではapplication.css等を読み込まず、admin.css等を読み込むことにしているだろう。
そこでproduction.rbにprecompileするファイルを追加する必要がある。

config.assets.precompile += %w(admin.css)

これで再度rakeタスクを実行するとadmin.cssもコンパイルしてくれるようになる。ローカルでテストして問題なければHerokuにデプロイしよう。