WebSocket(Pusher)を使ってリアルタイムチャット&囲碁が出来るTiramisuというRailsアプリがあるんですが、数年前にRails4.2.1にバージョンを上げたっきりで放置してあってherokuの動作中のOSも既にサポート終了しているCedar-10(Ubuntu 10.04 LTSベース)のままでデプロイすら出来ない状態になってました。。
WebSocketの学習とリアルタイムで何が出来るかを検討して作った実験アプリですが現状でもちゃんと動きますし腐らせて永遠にキャッチアップ出来ないってのも辛いので現状の最新版Railsにキャッチアップしたので、その時の手順とdeprecatedや削除されていた非互換でひっかかった修正部分をまとめておきます。
主なアップデート内容
- Ruby 2.1.6 → 2.5.0
- 言語仕様で引っかかることは全く無し。Native Buildが必要なgemで古いバージョンのtherubyracer等がインストール出来なくなっていたのでRailsともに最新に上げるようにして解決。
- Ruby on Rails 4.2.1 → 4.2.10 → 5.1.0
- 4.2.1 → 4.2.10 はとくになにも弄ること無く起動した。4.2.10 → 5.1.0 は 5.0 をすっ飛ばしたおかげでdeprecatedすら出ずいくつか期待しない挙動や例外が出た。まともなアプリなら5.0のアップグレードは飛ばさない方が良い。
- RSpec 2.12.0 → 2.99.2 → 3.7.0
- RSpec 2系から3系に上げるときは2.99に一旦上げてwarningを潰してから3.7.0に上げたが3系に上げた後のテスト実行でも非互換部分があった。
詳細は後述。
Ruby 2.1.6 → 2.5.0
今回のコードでは言語仕様に関しては完全互換でした。お見事。rubyracerは数年前はlibv8のバージョンの相性などでなかなかインストールしにくいとても辛い状況だったのでバージョン指定しており、0.11.4だとRuby 2.5.0でNative Buildが通らなかったのでGemfile
から指定を解除して最新版にbundle update
することですんなり通りました:
-gem 'therubyracer', '0.11.4'
+gem 'therubyracer'
Rails 4.2.1 → 4.2.10 → 5.1.0
Rails 4.2.1 → 4.2.10
まずRails 4.2.1 → 4.2.10
の方は単にbundle update
だけで起動もすんなりいきました。 config.serve_static_assets
がdeprecatedになっていてconfig.serve_static_files
に差し替えたのとViewでJS / CSSのURL解決が出来なくなっていたのでprecompile対象として追加しました:
# Disable Rails's static asset server (Apache or nginx will already do this).
- config.serve_static_assets = true
+ config.serve_static_files = true
TABLE_ENABLE = ENV['TABLE_ENABLE'].present? ? ENV['TABLE_ENABLE']=='true' : false
WEBRTC_ENABLE = ENV['WEBRTC_ENABLE'].present? ? ENV['WEBRTC_ENABLE']=='true' : false
+
+Rails.application.config.assets.precompile += %w( *.js *.css )
Rails 4.2.10 → 5.1.0
Rails 4系から5系にアップデートする際はconfigファイルもかなり変更されますがアップデートのコマンドがRailsに用意されており、競合した場合はdiffを参照して上書きする・しないを自分で選択しながら進めることが出来る様になっています。以下のコマンドで出来ます:
$ rails app:update
対話形式になっていて、h
コマンドでヘルプもでるので実行すれば使い方は分かると思います。自分はroutes.rbだけは全てのアドレスが消されてしまったので手動で修正した以外は他のファイルはほぼ上書きして問題無かったです。
また、本題のRails 4.2.10 → 5.1.0
の方は、かなり大きい非互換部分がありました:
before_filter
がbefore_action
に変更render >nothing: true
がrender body: nil
に変更- Modelの
belongs_to
の値がデフォルトで必須に変更 - migrationファイルがRails5で非互換の変更がある
最初の2つは例外発生で挙動しなくなって、最後の2つはDBまわりなので怖いですね。各種見ていきます:
before_filter
がbefore_action
はrbファイルロードが通らないだけなのでコントローラファイルを直すだけです。
class MessagesController < ApplicationController
- before_filter :authenticate
+ before_action :authenticate
render >nothing: true
がrender body: nil
もコントローラファイルを直すだけ。
if @message.save
Pusher['presence-channel-' + params[:channel_id]].trigger('message_added', {message: {user_name: h(@message.user_name), content: view_context.auto_link(@message.content)}, time: @message.created_at.localtime.strftime('%H:%M')})
- render status: :created, nothing: true
+ render status: :created, body: nil
else
render status: :forbiden, text: 'The message can\'t save'
end
「belongs_to
の値がデフォルトで必須に変更」これは4.2以前までは関連先がnilとなってもOKだったのがNGとなってvalidationで失敗してしまうので危険なので移行時には気を付ける必要があります。もともと必須扱いで利用していた場合は修正の必要がありませんが関連先の有無を任意としたい場合はoptional: true
を追加する必要があります。
class Channel < ActiveRecord::Base
include ActiveModel::ForbiddenAttributesProtection
has_many :messages
- belongs_to :table
+ belongs_to :table, optional: true
end
migrationファイルの記載方法がRails5で非互換のアップデートが加えられており、Rails4.2以前に記述したものに対しては互換性を保つ為明示的にActiveRecord::Migration[4.2]
を継承する必要があります。
-class CreateMessages < ActiveRecord::Migration
+class CreateMessages < ActiveRecord::Migration[4.2]
def change
create_table :messages do |t|
t.string :content
ローカル環境で$ rake db:drop; rake db:migrate
しましたが特に↑の修正で問題無かったです。Tiramisuアプリは8ファイルしか無かったので大したことは無かったですが沢山あると大変そうですね・・・。
RSpec 2.12.0 → 2.99.2 → 3.7.0
RSpecを2系から3系に上げる際は互換性チェックの為の2.99が用意されているので先ずはこちらでバージョンアップしてチェックし、warningを全て潰してから3系にアップデートすると比較的楽に移行出来るかと思います。以下、Rails5に上げたことで修正が必要になった部分も含まれているとは思いますが対応した部分です。
should
はexpect
に変更することを推奨
it "assigns a newly created message as @message" do
post :create, {:message => valid_attributes, channel_id: 1}, valid_session
- assigns(:message).should be_a(Message)
- assigns(:message).should be_persisted
+ expect(assigns(:message)).to be_a(Message)
+ expect(assigns(:message)).to be_persisted
end<\code>
describe MessagesController do
describe "routing" do
- it "routes to #create" do
- post("channels/1/messages").should route_to(controller: 'messages', action: 'create', channel_id: '1')
- end
+ subject { post "channels/1/messages" }
+ it { is_expected.to route_to(controller: 'messages', action: 'create', channel_id: '1') }
end
end
pending
はskip
に変更(そもそもpendingコード無ければ良いんですけどね・・・)
describe ChannelsHelper do
- pending "add some examples to (or delete) #{__FILE__}"
+ skip "add some examples to (or delete) #{__FILE__}"
end
spec/controllers/messages_controller_spec.rb
等のようにフォルダ名でコントローラテストやモデルテストなどのテスト種類の区別が無くなったらしいです。そう言われても急に移行出来ないので従来通りにしたい場合はspec_helper.rb
にconfig.infer_spec_type_from_file_location!
を追加すると互換となります。
# the seed, which is printed after each run.
# --seed 1234
config.order = "random"
+
+ config.infer_spec_type_from_file_location!
end
Rails5よりコントローラテストが標準から外されてインテグレーションテストになっている様で、必要であればrails-controller-testing
gem必要で、設定も追加が必要です。ついでにrspec/autorun
のrequireも不要になっているようなのでspec_helper.rb
を修正しておきます。
ENV["RAILS_ENV"] ||= 'test'
require File.expand_path("../../config/environment", __FILE__)
require 'rspec/rails'
-require 'rspec/autorun'
+require 'rails-controller-testing'
+Rails::Controller::Testing.install
# Requires supporting ruby files with custom matchers and macros, etc,
# in spec/support/ and its subdirectories.
また、コントローラテストのpost(url, params, session)
はpost(url, options)
に変更になっているようで、以下の様な感じで変更が必要です。
describe "with valid params" do
it "creates a new Message" do
expect {
- post :create, {:message => valid_attributes, channel_id: 1}, valid_session
+ post :create, {params: {message: valid_attributes, channel_id: 1}, session: valid_session}
}.to change(Message, :count).by(1)
end
stub
はallow
に、stub_chain
はreceive_message_chain
で記載するように変更になっていました。
describe "POST create" do
before do
- Channel.stub(find: stub_model(Channel, id: 1))
- Pusher.stub_chain(:[], :trigger)
+ allow(Channel).to receive(:find).and_return(stub_model(Channel, id: 1))
+ allow(Pusher).to receive_message_chain(:[], :trigger)
end
Railsバージョンアップはこまめにやりましょう
パッチバージョンはセキュリティFIXも含まれるのでリリースされたら即時、マイナーバージョンは自分の様にバージョンを複数飛ばしで上げると下手するとサービス破壊しかねないので本気プロダクトの場合は必ず1つづつ上げていくのが宜しいかと思います。
Rubyに関しては、上げられるなら先に上げておくのが良さそうです。
それではより良いRailsライフを:)