rista’s blog

株式会社リスタの技術?ブログ

Rails 5.1, Ruby 2.4にアップデートしました

最近iOSアプリエンジニアに転向ぎみの@mikedaです。

JOBLISTで使っているrubyrailsのバージョンがRuby 2.3.1、Rails 4.2.6とけっこう古くなっていたので、最近、エイヤとアップデートしました。

やったこととか対応したエラーなどをざっとメモっておきます。

作業は以下のように少しずつ進めました。

  1. Rails 4.2.6 -> 4.2.8。合わせて全体的にgemのバージョンアップ
  2. Rails 4.2.8 -> 5.0.3
  3. Ruby 2.3.0 -> 2.4.1
  4. Rails 5.0.3 -> 5.1.1

Rails 4.2.6 -> 4.2.8

まずはrailsを4系の最新版にアップデート。
合わせて全体的にgemのバージョンアップしました。

bundle update

# Gemfile
# gem 'rails', '4.2.8'

bundle update rails

エラー対応など

railsは特に問題なく動きましたが、他のgemがいろいろおかしくなったので調整しました。

rubocop、haml-lint

追加された新しいルールについて取捨選択&調整しました。
rubocopはauto-correctオプションでだいたい自動修正できるんですが、haml-lintは全部手動なのでけっこう辛かったです。

jquery-ui-rails

requireするパスがかわっていたので修正しました。

# Sprockets::FileNotFound - couldn't find file 'jquery-ui/dialog' with type 'application/javascript':

# app/assets/javascripts/application.js
-//= require jquery-ui/sortable
+//= require jquery-ui/widgets/sortable

CarrierWave

CarrierWave::Storage::Fogがが明示的にrequireしないと見つからなくなっていました。

# uninitialized constant CarrierWave::Storage::Fog (NameError)

# config/initializers/carrierwave.rb
+ require 'carrierwave/storage/abstract'
+ require 'carrierwave/storage/file'
+ require 'carrierwave/storage/fog'

set_content_typeは不要になったらしいので削除しました。

# uninitialized constant CarrierWave::MimeTypes

# 各種Uploader
- process :set_content_type

slack-notifier

channelの指定のしかたがちょっと変わってました。

# NameError: undefined local variable or method `channel' for main:Object

- notifier = Slack::Notifier.new ENV['SLACK_WEBHOOK_URL']
- notifier.channel = channel
+ notifier = Slack::Notifier.new ENV['SLACK_WEBHOOK_URL'], channel: channel

draper

sourceメソッドは無くなったようです。

  def latitude
 -   source.latitude.to_f
 +   object.latitude.to_f
  end

Rails 4.2.8 -> 5.0.3

Railsを5.0にアップデートしました。
Rails アップグレードガイドなど見ながら以下の手順で作業しました。

  • bundle update rails
  • rails app:update
  • エラー修正、deplicated対応

bundle update rails

bundle update railsをがんばって成功させます。

railtiesの依存関係でコケるものが多いです。

Bundler could not find compatible versions for gem "railties":
  In Gemfile:
    rspec-rails was resolved to 3.6.0, which depends on
      railties (>= 3.0)

コケたものは基本的にはいっしょにバージョンアップしてやります。

bundle update rails rspec-rails

しかしそもそもgemが更新されてないものもあります。

f:id:mikeda:20170611185730p:plain

https://rubygems.org などで検索して最新版が対応してないものは諦めて、使わないようにするか、パッチ作ります。

rails app:update

実行すると各種コンフィグが上書きされるのでdiff見ながら調整します。

明示的に調整していたもの以外のデフォルト値変更は基本的に全て適用しました。

新しい項目で調整したものはこのあたりです。

# config/application.rb
+ config.active_record.time_zone_aware_types = %i[datetime time]

# config/environments/production.rb
# これをtrueにしないとproductionでautoloadがきかなくなっていた
+ config.enable_dependency_loading = true

# DEPRECATION WARNING: `config.serve_static_files` is deprecated and will be removed in Rails 5.1.
# Please use `config.public_file_server.enabled = false` instead.
# config/environments/xxx.rb
-  config.serve_static_files = ENV['RAILS_SERVE_STATIC_FILES'].present?
+  config.public_file_server.enabled = ENV['RAILS_SERVE_STATIC_FILES'].present?

エラー修正、deplicated対応

あとはテスト通るまでエラーを修正していきます。
deplicated対応はパッと出来そうなものだけいっしょに対応して、あとは別対応にしたり5.1アップデート時にまわしたりしました。

record_tag_helper

div_for、content_tag_forはrecord_tag_helperというgemに分離されたようです。

# NoMethodError - The `div_for` method has been removed from Rails. To continue using it, add the `record_tag_helper` gem to your Gemfile:              
#  gem 'record_tag_helper', '~> 1.0' 

# Gemfile
+gem 'record_tag_helper'

年月日がhiddenでそのまま渡せなくなった

パラメータを確認すると文字列として認識されています。

"birthday"=>"{1=>2017, 2=>5, 3=>22}"

記述を調整するとちゃんと渡せました。

- = f.hidden_field :birthday
+ = hidden_field_tag "entry[birthday]", @entry.birthday

render text

render :textが非推奨になったようです。 S3に保存したHTMLをそのまま表示するというへんな箇所を修正。

- render text: html
+ render html: html.html_safe

uniq -> distinct

ActiveRecord::Relationのuniqはdistinctになりました。

# DEPRECATION WARNING: uniq is deprecated and will be removed from Rails 5.1 (use distinct instead).

- .uniq
+ .distinct

where_valuesが使えなくなった

複雑なOR条件などで使っていたwhere_valuesが使えなくなっていました。
rails 5で追加されたorを使うように書き換えました。

ApplicationRecord

各種モデルはActiveRecord::BaseではなくApplicationRecordを継承するようにとのこと。
rails app:updateでは自動作成されませんでしたがせっかくなので手動で対応しておきます。

class ApplicationRecord < ActiveRecord::Base
  self.abstract_class = true
end
- class City < ActiveRecord::Base
+ class City < ApplicationRecord

sql_mode: TRADITIONAL

group byしたカラムをselectで使っていないものがエラーになるようになりました。
そもそもSQLなおすべきですが、いったん設定調整してエラーにならないようにしました。

# ActiveRecord::StatementInvalid - Mysql2::Error: Expression #1 of SELECT list is not in GROUP BY clause and contains nonaggregated column 'joblist_devlopment.jobs.id' which is not functionally dependent on columns in GROUP BY clause; this is incompatible with sql_mode=only_full_group_by: ...

# config/database.yml
+  variables:
+    sql_mode: TRADITIONAL

enumのカラムから数値を取り出す方法が変わった

こういうモデルがあった時に、

class Job
  enum status: %i[draft published]
end

以前はオブジェクトにカラム名を指定した[]アクセスをすると数値が取り出せていましたが、

> job[:status]
=> 1

Rails 5からは文字列を返すようになりました。

> job[:status]
=> "published"

数値を取り出すには以下のどちらかの方法が必要です。

> job.status_before_type_cast
=> 1
> Job.statuses[job.status]
=> 1

request specのdeplication warning

引数の渡し方がキーワード引数になったようです。

# DEPRECATION WARNING: ActionDispatch::IntegrationTest
# HTTP request methods will accept only the following
# keyword arguments in future Rails versions:                                                       
# params, headers, env, xhr, as

- get url, params, headers
+ get url, params: params, headers: headers

alias_method_chainがdeplicated

Module#prepend使うように修正しました。

バージョンアップできないgemを削除

jquery-cookie-rails, readmorejs-railsrails 5に対応してませんでした。
直接ファイルを設置してgemを使わないように調整しました。

JSライブラリをgem化したものはどんどん動かなくなっているし、ファイル直接おくのもけっこうつらいので、npm的な仕組みに移行したいです。

riby 2.3.0 -> ruby 2.4

rubyをバージョンアップします。

今までサーバ上ではUbuntu 16.04の標準パッケージのrubyを使っていたので、rbenvを使うようにしました。
これは今回の話とはあまり関係がないので割愛します。

細かいところでいくつか問題がおこったので調整しました。

Ubuntu 16.04のtzinfo

WerckerのCIで使っているUbuntu 16.04のdockerイメージをビルドし直すと、タイムゾーンを日本語に設定するところで、

FROM ubuntu:16.04
RUN cp /usr/share/zoneinfo/Asia/Tokyo /etc/localtime && \
    echo 'Asia/Tokyo' > /etc/timezone && date

以下のエラーが出るようになってました。

cp: cannot stat '/usr/share/zoneinfo/Asia/Tokyo': No such file or directory

いつのまにかUbuntu 16.04のイメージでタイムゾーンファイルがデフォルトでインストールされなくなっていたのかな?

tzinfoをインストールするようにしました。

FROM ubuntu:16.04
RUN apt-get update && \
    apt-get install -y tzinfo ...
RUN cp /usr/share/zoneinfo/Asia/Tokyo /etc/localtime && \
    echo 'Asia/Tokyo' > /etc/timezone && date

別のちゃんとした方法があるかも。

seory -> meta-tags

SEO用のメタタグ設定にseoryというgemを使っていたのですが、こういうエラーが出るようになってました。

FATAL -- : ActionView::Template::Error (wrong argument type PrefecturesController (expected Regexp)):

ruby2.4で追加されたmatch?メソッドの影響でこのあたりの挙動がおかしくなっているっぽいですが、

https://github.com/esminc/seory/blob/master/lib/seory/condition.rb#L13

メンテされてなさそうなのでmeta-tagsに移行しました。

Rails 5.0.3 -> 5.1.1

基本的な手順は5.0へのアップデート時と同じです。

app:update後の対応箇所だけいくつかあげておきます。

redis-session-store

動かなくなっていました・・・ メンテ止まってそうなので他のgemに移行したいところですが、いったんこのissueを参考にパッチあてたレポジトリを作成して5.1に対応しました。

# Gemfile
- gem 'redis-session-store'
+ gem 'redis-session-store', github: 'rista-inc/redis-session-store' # rails 5.1対応用のfork

belongs_to optional: true

5系からデフォルトでbelongs_toのカラムはrequiredになっていたので、このタイミングで対応しました。

Rails.application.config.active_record.belongs_to_required_by_default = true

今まで明示的にrequiredなどを記載していなかったので、requiredではないところにだけoptional: trueを追加しました。

- belongs_to :company
+ belongs_to :company, optional: true

validate false

belongs_toの件で、一部のデータ作成順序を考慮していないテストデータ作成バッチでvalidateがコケるようになってしまったので、一時的にvalidate無効化しました。

- photo.save!
+ photo.save!(validate: false)

- City.import(batch)
+ City.import(batch, validate: false)

i18nのrequiredエラー

同じくbelongs_toの件で、エラー時のメッセージが無かったので追加しました。

# config/locales/ja.yml
+ required: を入力してください

f:id:mikeda:20170611185824p:plain

f:id:mikeda:20170611185832p:plain

rails sがこける

--bind 0.0.0.0つけるとコケる。

air:joblist mikeda$ bin/rails s --bind 0.0.0.0
/Users/mikeda/.rbenv/versions/2.4.1/lib/ruby/gems/2.4.0/gems/thor-0.19.4/lib/thor/base.rb:484:in `handle_argument_error': ERROR: "rails server" was called with arguments ["0.0.0.0"] (Thor::InvocationError)
Usage: "rails server [puma, thin etc] [options]"

いつのまにか動かなくなっていたけどまだそんなに困ってないので未対応。
対応したら追記します。

gem listen

# これなんかデフォルトで有効になった以下の設定で必要っぽい
# config.file_watcher = ActiveSupport::EventedFileUpdateChecker
# developmentだけなのであんまちゃんと調べてない

# Gemfile
+ gem 'listen'

まとめ

というわけでrubyrailsをアップデートしました。

特に大きな問題は起こりませんでしたが、まとめてやるのやっぱしんどいので、もっとこまめにアップデートするようにします。
あとjsライブラリのバージョン管理をなんとかしよう。