さかなソフトブログ

プログラミングやソフトウェア開発に関する情報

ツール

ずっと放置してたRailsアプリをRuby 2.1.6→2.5.0、Rails 4.2→5.1、RSpec 2→3にアップデートした時の手順と非互換になった部分の修正まとめ

更新日:

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_filterbefore_actionに変更
  • render >nothing: truerender body: nilに変更
  • Modelのbelongs_toの値がデフォルトで必須に変更
  • migrationファイルがRails5で非互換の変更がある

最初の2つは例外発生で挙動しなくなって、最後の2つはDBまわりなので怖いですね。各種見ていきます:

before_filterbefore_actionはrbファイルロードが通らないだけなのでコントローラファイルを直すだけです。

class MessagesController < ApplicationController
-  before_filter :authenticate
+  before_action :authenticate

render >nothing: truerender 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に上げたことで修正が必要になった部分も含まれているとは思いますが対応した部分です。

shouldexpectに変更することを推奨

       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

pendingskipに変更(そもそも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.rbconfig.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

stuballowに、stub_chainreceive_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ライフを:)

正方形336

正方形336

-ツール
-, ,

Copyright© さかなソフトブログ , 2019 All Rights Reserved.