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

まっしろけっけ

めもてきなやーつ

Rails3から4へUpgradeしたお話

今回はお仕事で開発をしているweb applicationで使用しているRuby on Railsのversionを
3.2から4.1へUpgradeしたお話です。
その際の進め方や遭遇した問題をメモてきにまとめておきます。

Rails3のコードをRails4のコードにconvertする

今回はRubyKaigiでお話を聞いてきたsynvertというgemを使用しました。
デフォルトのsnippetsをそのまま使用するとskip_filterがskip_actionに変換され(正確にはskip_action_callback)エラーが発生する等細かいバグがあるのでforkしてごにょごにょして使用しました。

$ gem install synvert
$ synvert -l # snippetの一覧が表示される
$ synvert -r rails/upgrade_3_2_to_4_0 # rails 3.2から4.0へのコードの変換
$ synvert -r rails/upgrade_4_0_to_4_1 # rails 4.0から4.1へのコードの変換

synvertはこちらでweb toolも公開してます。

Gemfileを更新する

次はGemfileを変更します。

- gem 'rails', '3.2.19'
+ gem 'rails', '4.1.6'

- gem 'strong_parameters' # 事前に導入していた為削除(Rails4からはRailsに含まれています)

とりあえずrailsのversionを変更

$ bundle update rails

使用しているgemの依存関係でエラーが出たので
逐一使用している各gemのversionを適切なものを使用するように書き換えました。

config関連の修正

# config/application.rb
- require "active_resource/railtie" # active_resourceはgemに切り出されました。gemも使用していないので削除

- config.paths["config/routes"] << Rails.root.join("config/routes/hoge.rb")
+ config.paths["config/routes.rb"] << Rails.root.join("config/routes/hoge.rb")

# config/environments/*.rb
- config.active_record.auto_explain_threshold_in_seconds = 0.5
セッション flash の互換性維持の為
# config/initializers/cookies_serializer.rb
- Rails.application.config.action_dispatch.cookies_serializer = :json
+ Rails.application.config.action_dispatch.cookies_serializer = :marshal
matchを使用する場合は明示的に記述する必要がある為
# config/routes.rb
- match "hoge" => "hoge#index"
+ match "hoge" => "hoge#index", via: [:get, :post]
staging環境をproductionと一緒の設定で動作させる為
# config/secrets.yml
+ staging
+   secret_key_base: <%= ENV["SECRET_KEY_BASE"] %>

secret_key_baseの生成方法

$ rake secret
=> "secret_key_base"

envの設定

$ vi ~/.bashrc
+ export SECRET_KEY_BASE="上記で生成したsecret_key_base"

model関連の修正

ActiveRecord Relation周りの変更対応(shift,pop等一部のmethodがない)
# rails 3.2
users = User.limit(10)
users.pop

# rails 4.1
users = User.limit(10)
users.pop
# NoMethodError: undefined method `pop'

# rails4.1でrails3.2と同じ挙動にする場合
users = User.limit(10).to_a
users.pop
conditionsがdeprecatedになった為
+ has_many :posts, conditions: '`id` <= 100000'
- has_many :posts, -> { where('`id` <= 100000') }
minimumのオプション修正(オプションでjoinsとか渡せなくなった?)
- Post.minimum('rank', {joins: :user, group: :user.id})
+ Post.joins(:user).group(:user_id).minimum('rank')
slow queryに注意

ActiveRecordにて自動で生成されるqueryで数カ所slow queryが発生していた為
修正が必要になりました。

sweeperの削除

sweeperがrails-observersというgemに切り出された為にsweeperを削除
fragment cacheのキーにupdated_atを含んで更新されたら別cacheを使用するように変更(なんで最初からupdated_atを使わなかったのか・・・orz)
rails-observersを使用してもいいと思います

test修正

上記の作業が一通り終了した段階でtestを走らせて落ちている部分を修正していきました。
testはrspec3を使用しています。

一部gemで問題発生

Upgrade作業を開始した当初はcomposite_primary_keysの最新版がRails4.1.6に対応しておらず
一部でエラーが発生していました。
しかし、commit履歴を見ると4.1.6対応は行われているもののreleaseがされていないといことで下記のようにGemfileを変更してみました。

- gem 'composite_primary_keys', '7.0.10'
+ gem 'composite_primary_keys', git: 'https://github.com/composite-primary-keys/composite_primary_keys', branch: 'ar_4.1.x'

その後bundle installでrails serverを立ち上げようするとcomposite_primary_keysでエラーが発生してしまったので、
composite_primary_keysをforkしてerror箇所を修正しpull requestを送りGemfileで指定しているrepositorieを自分のrepositorieに変更して動作確認を行いエラーが発生しないことを確認しました。
(Rails4.1.6へのUpgradeリリース前にpull requestがmergeされcomposite_primary_keysの7.0.11もリリースされたのでGemfileを変更し7.0.11を使用するように変更しました)

そもそもcomposite_primary_keysを使用する設計が・・・

動作検証

testが全て通っても心配なので動作検証はちゃんと行いました。
cache周りとmodel周りでバグがそれぞれ一件あったので修正

感想

上記のような手順でUpgrade作業を進めて無事にリリース出来ました。
実際には上記の手順の内Rails3.2で対応しておいても問題にならない部分はRails3.2で対応してリリースしておき、
Rails4への移行の際には最小限の作業ですむように事前に準備しておきました。

今回も感じたのはtestがしっかりしていると実際に動作検証した際の不具合も少なくて済み
安心感がありました。