Help us understand the problem. What is going on with this article?

Rails5へのアップグレードや変更点まとめ

More than 3 years have passed since last update.

Rails4系で動いているサービスをRails5にバージョンアップする機会があったので、その時に実施した手順や調べた内容をまとめてみた。

注意:
Rspecなどのテスト関連についてはこの記事では扱ってないです。
勉強会で使用した内容を組み合わせて書いているので、かなり箇条書きになってます。

Rails 5の概要

Rails 5の主な変更点

  • フレームワークのAPIはほとんどRails4と同じ

    • Rails 4の知識の大半はRails5で活かせそう
  • Ruby 2.2.2以上が必須(Rails 4は1.9.3以上)

    • 基本的には最新版を使用
  • railsコマンド
    Rails4.x系で使用していたrakeコマンドはrailsコマンドで代用できるようになった。

例)
$ bin/rails db:migrate   # bin/rake db:migrate
$ bin/rails test   # bin/rake test
$ bin/rails restart   # touch tmp/restart.txt
$ bin/update   # bundle install, db:migrate等を実行
  • belongs_toの参照先がnilの場合はバリデーションエラーになる
model
validates :model, presence: true # デフォルト

この挙動は以下のconfig/initializers/new_framework_defaults.rbを変更することでRails4.x系に戻すことができる。

config/initializers/new_framework_defaults.rb
Rails.application.config.active_record.belongs_to_required_by_default = true

新規作成時には注意する。
アップグレードの場合はデフォルトでfalseになっている。

  • ActiveRecordのモデルがApplicationRecordから継承されるようになった
app/models/application_record.rb
class ApplicationRecord < ActiveRecord::Base
  self.abstract_class = true
end

アプリケーションのモデル全体に機能を追加したい場合はApplication Recordにメソッドを追加する。

  • ActiveJobのジョブがApplicationJobから継承されるようになった
app/jobs/application_job.rb
class ApplicationJob < ActiveJob::Base
end
  • ActionMailerApplicationMailerから継承されるようになった
app/mailers/application_mailer.rb
class ApplicationMailer < ActionMailer::Base
  default from: 'from@example.com'
  layout 'mailer'
end
  • ActiveRecordコールバックの止め方
model
before_save :ensure_admin

def ensure_admin
  throw(:abort) unless self.admin
end

throw(:abort)ではなくfalseだと処理が続行される。

  • orメソッド
User.where(name: 'Tom').or(User.where('height > ?', 179))
#=> SELECT * FROM users WHERE name = 'Tom' OR height > 179;

Rails 4では生のSQLを使うかArelを使って実現していた。

  • ActiveRecordの#saveにtouchオプションが追加
updated_at = article
artile.save!(touch: false)
article.update_at == updated_at
=> true

この例ではarticleデータを更新してもupdate_atは更新されない。

  • migration versioning
    マイグレーションファイルに、Railsのどのバージョンで生成されたマイグレーションファイルなのかという情報を付与し、そのバージョン情報によりAPIの挙動を変えるというもの。
class CreateBooks < ActiveRecord::Migration[5.0]
  def change
    create_table :books do |t|
      t.string :name

      t.timestamps
    end
  end
end

[5.0]というマイグレーションファイル作成時のRailsのバージョンが追加される。

  • セキュアなトークンを扱うAPIの拡張
    • has_secure_token
      • ワンタイムトークンの実装
      • MySQLの設定を上手くしないと衝突の問題あり
model
class User < ApplicationRecord
  has_secure_token :auth_token   # カラムを指定
end
>> user = User.new(name: 'Tom')
>> user.save
=> true

>> user.auth_token
=> "fppFh6xAsGfNTiDxzreW283f"
  • rails newした時のフォルダ構成
.
|-- .gitignore
|-- Gemfile
|-- Gemfile.lock
|-- README.md
|-- Rakefile
|-- app
|   |-- assets
|   |   |-- config
|   |   |   `-- manifest.js
|   |   |-- images
|   |   |   `-- .keep
|   |   |-- javascripts
|   |   |   |-- application.js
|   |   |   |-- cable.js
|   |   |   `-- channels
|   |   |       `-- .keep
|   |   `-- stylesheets
|   |       `-- application.css
|   |-- channels
|   |   `-- application_cable
|   |       |-- channel.rb
|   |       `-- connection.rb
|   |-- controllers
|   |   |-- application_controller.rb
|   |   `-- concerns
|   |       `-- .keep
|   |-- helpers
|   |   `-- application_helper.rb
|   |-- jobs
|   |   `-- application_job.rb
|   |-- mailers
|   |   `-- application_mailer.rb
|   |-- models
|   |   |-- application_record.rb
|   |   `-- concerns
|   |       `-- .keep
|   `-- views
|       `-- layouts
|           |-- application.html.erb
|           |-- mailer.html.erb
|           `-- mailer.text.erb
|-- bin
|   |-- bundle
|   |-- rails
|   |-- rake
|   |-- setup
|   |-- spring
|   `-- update
|-- config
|   |-- application.rb
|   |-- boot.rb
|   |-- cable.yml
|   |-- database.yml
|   |-- environment.rb
|   |-- environments
|   |   |-- development.rb
|   |   |-- production.rb
|   |   `-- test.rb
|   |-- initializers
|   |   |-- application_controller_renderer.rb
|   |   |-- assets.rb
|   |   |-- backtrace_silencers.rb
|   |   |-- cookies_serializer.rb
|   |   |-- filter_parameter_logging.rb
|   |   |-- inflections.rb
|   |   |-- mime_types.rb
|   |   |-- new_framework_defaults.rb
|   |   |-- session_store.rb
|   |   `-- wrap_parameters.rb
|   |-- locales
|   |   `-- en.yml
|   |-- puma.rb
|   |-- routes.rb
|   |-- secrets.yml
|   `-- spring.rb
|-- config.ru
|-- db
|   `-- seeds.rb
|-- lib
|   |-- assets
|   |   `-- .keep
|   `-- tasks
|       `-- .keep
|-- log
|   `-- .keep
|-- public
|   |-- 404.html
|   |-- 422.html
|   |-- 500.html
|   |-- apple-touch-icon-precomposed.png
|   |-- apple-touch-icon.png
|   |-- favicon.ico
|   `-- robots.txt
|-- test
|   |-- controllers
|   |   `-- .keep
|   |-- fixtures
|   |   |-- .keep
|   |   `-- files
|   |       `-- .keep
|   |-- helpers
|   |   `-- .keep
|   |-- integration
|   |   `-- .keep
|   |-- mailers
|   |   `-- .keep
|   |-- models
|   |   `-- .keep
|   `-- test_helper.rb
|-- tmp
|   |-- .keep
|   `-- cache
|       `-- assets
`-- vendor
    `-- assets
        |-- javascripts
        |   `-- .keep
        `-- stylesheets
            `-- .keep

44 directories, 75 files
  • rails newした時にできるGemfile
source 'https://rubygems.org'


# Bundle edge Rails instead: gem 'rails', github: 'rails/rails'
gem 'rails', '~> 5.0.0'
# Use sqlite3 as the database for Active Record
gem 'sqlite3'
# Use Puma as the app server
gem 'puma', '~> 3.0'
# Use SCSS for stylesheets
gem 'sass-rails', '~> 5.0'
# Use Uglifier as compressor for JavaScript assets
gem 'uglifier', '>= 1.3.0'
# Use CoffeeScript for .coffee assets and views
gem 'coffee-rails', '~> 4.2'
# See https://github.com/rails/execjs#readme for more supported runtimes
# gem 'therubyracer', platforms: :ruby

# Use jquery as the JavaScript library
gem 'jquery-rails'
# Turbolinks makes navigating your web application faster. Read more: https://github.com/turbolinks/turbolinks
gem 'turbolinks', '~> 5'
# Build JSON APIs with ease. Read more: https://github.com/rails/jbuilder
gem 'jbuilder', '~> 2.5'
# Use Redis adapter to run Action Cable in production
# gem 'redis', '~> 3.0'
# Use ActiveModel has_secure_password
# gem 'bcrypt', '~> 3.1.7'

# Use Capistrano for deployment
# gem 'capistrano-rails', group: :development

group :development, :test do
  # Call 'byebug' anywhere in the code to stop execution and get a debugger console
  gem 'byebug', platform: :mri
end

group :development do
  # Access an IRB console on exception pages or by using <%= console %> anywhere in the code.
  gem 'web-console'
  gem 'listen', '~> 3.0.5'
  # Spring speeds up development by keeping your application running in the background. Read more: https://github.com/rails/spring
  gem 'spring'
  gem 'spring-watcher-listen', '~> 2.0.0'
end

# Windows does not include zoneinfo files, so bundle the tzinfo-data gem
gem 'tzinfo-data', platforms: [:mingw, :mswin, :x64_mingw, :jruby]

Rails 5の主な新機能

Action Cable

Action Cable はRails 5 に新しく導入されたフレームワークであり、Rails アプリケーションで WebSockets とその他の部分をシームレスに統合します。
通常のRailsアプリケーションと同じスタイル・方法でリアルタイム機能をRubyで書くことができる

  • リアルタイムに双方向通信を行う機能
    • 内部的にはWebSocketプロトコルを使っている
    • サーバー側はRuby、クライアント側はCoffeeScriptで実装
  • 用途:チャット、プッシュ通知、オンラインゲーム?
$ bin/rails generate channel chat   # コードを生成

Rails API

APIのみを提供するシンプルなアプリケーションをRailsで簡単に作成できるようになりました。 Twitter APIや GitHub APIのような一般公開APIサーバーはもちろん、カスタムアプリケーション用APIサーバーの作成・公開にも便利です。

$ bin/rails new my_api --api
  • Railsを使って、画面がない、Web APIのみのアプリケーションを簡単に作るための仕組み
  • formatがjsonになる
  • APIのための機能がつくわけではない
    • RailsからAPIにとって不要な要素を除くだけ

Rails5へのアップグレード

  • 今回の対象サービス

    • Rails 4.2.5
    • Ruby 2.0.0
    • rvm 1.27
    • Passenger
  • アップグレード作業

    • 公式ドキュメント見る(ネットで調べる)
    • アップグレード用の作業ブランチを作る
    • 現状のテストが全部パスすることを確認する
    • Rubyのバージョンを最新版にアップグレードする
    • テスト関連のgemを最新版にアップグレードする
    • Rails 5.0.0にアップグレードする
    • Rails 5の書き方に変更する

今回のアップグレードでは3、5はやってない。本当はやるべき。

公式ドキュメント見る(ネットで調べる)

参考にしたもの

アップグレード用の作業ブランチを作る

$ git checkout -b rails-5 origin/rails-5
  • いきなりmasterで作業するのは危ないので作業用ブランチを作って最後にマージするようにした
  • いつでもROLLBACKできるようにしておく

Rubyのバージョンを最新版にアップグレードする

今回は2.3.0にした。

  • rbenv使っている場合
$ rbenv install 2.3.0
$ rbenv global 2.3.0
$ rbenv rehash
$ ruby -v   # 2.3.0
  • rvm使っている場合
$ rvm install 2.3.0
$ rvm use 2.3.0
$ ruby -v   # 2.3.0

Rails 5.0.0にアップグレードする

  • Gemfileを変更
Gemfile
gem 'rails', '5.0.0'
  • bundle updateする
$ bundle update rails
$ bin/rails -v   # Rails 5.0.0
  • 依存関係でエラーが出た場合は関連するgemも一緒に最新版にアップグレードする
  • Rails5に対応していないgemがあった場合はwarningが出る

ここで試しにbin/rails serverでサーバーが動くか試したところ動かない・・・

vendor/bundle/ruby/2.3.0/gems/sinatra-1.0/lib/sinatra/showexceptions.rb:1:in `require': cannot load such file -- rack/showexceptions (LoadError)

調査した結果、原因はsinatraRails 5でサポートしているrackのバージョンが違うため?

参考:https://github.com/splitrb/split/issues/354

gem fileを該当箇所を以下に変更

Gemfile
gem 'sinatra', github: 'sinatra/sinatra'

そしてbundle installすると

$ bin/rails server
=> Booting Puma
=> Rails 5.0.0 application starting in development on http://localhost:3000
=> Run `rails server -h` for more startup options
Puma starting in single mode...
* Version 3.5.2 (ruby 2.3.0-p0), codename: Amateur Raccoon Rocketry
* Min threads: 0, max threads: 16
* Environment: development
* Listening on tcp://localhost:3000
Use Ctrl-C to stop

無事サーバが動いた

  • 設定ファイルをアップグレードする
$ bin/rails app:update
    conflict  config/boot.rb
Overwrite /Users/username/Project/project_name/config/routes.rb? (enter "h" for help) [Ynaqdh] y
       force  config/routes.rb

既存ファイルを上書きしていいか聞かれるのでここでは全てYesと答える

  • 上書きされた差分をgit diffを確認しながら必要に応じて元に戻す
    • 地道な作業
    • routes.rbの設定とか全て消えてるので注意
    • Action Mailerの設定も上書きされるので注意

ここまででアップグレードは大体終わる。

Rails 5の書き方に変更する

  • schema.rbの更新
    • Rails 5では形式が変わっている

下記コマンドでOK

$ bin/rails db:migrate
  • ApplicationRecordへ変更
    • app/models/application_record.rbを作成
    • 既存モデルの継承元をActiveRecord::BaseからApplicationRecordに変更
app/models/application_record.rb
class ApplicationRecord < ActiveRecord::Base
  self.abstract_class = true
end
〇〇model
class Product < ApplicationRecord
  • ApplicationJobへ変更

    • app/jobs/application_job.rbを作成
    • 既存ジョブの継承元をActiveJob::BaseからApplicationJobに変更
    • Modelと同じ感じ
  • ApplicationMailerへ変更

    • app/mailers/application_mailer.rbを作成
    • 既存ジョブの継承元をActionMailer::BaseからApplicationMailerに変更
    • これもModelと同じ感じ
  • ログのDEPRECATION WARNINGをひたすら修正する

    • 古いバージョンの書き方をしている箇所で発生
    • gemが対応してなくて発生
    • Rails 5ではAPIが色々変わっているために発生したりもする
    • ログに内容が出るので一つずつ修正していく

以降は動作確認しながら適宜修正

  • 動作確認すると特定のページでエラー発生

ActiveRecord::StatementInvalid - Mysql2::Error: Expression #1 of SELECT list is not in GROUP BY clause and contains nonaggregated column

原因
Hoge.all.group("fuga")

テーブルの構成の問題?よくわかってない。。。
やりたいこととしては特定の2カラムに対して、重複を除いたレコードを取得したかったので以下のように変更した。

対応
Hoge.select(:fuga, :piyo).distinct

参考:http://qiita.com/bboobbaa/items/9fdca834076cb4c3389e

番外編

  • 本番デプロイ時にハマったこと
本番サーバ
$ rvm use 2.3.0   # ruby 2.3.0を使う
$ git pull
$ bin/update
$ bin/rails restart    # これで動くはず!

結果、動かない・・・
Railsのログにも情報がない・・・

Apacheのログを確認すると・・・

Could not find rake-10.4.2 in any of the sources (Bundler::GemNotFound)

???

ruby 2.3.0に切り替えてbundle installしているのに使われてないような気がする・・・

原因はPassengerだった

PassengerはRailsアプリのデプロイツールで、アプリが使用するrubyのデフォルトバージョンを設定でき、これがruby 2.0.0のままだった。。。

設定ファイルを変更し、Apache再起動で無事デプロイ完了!

所感

  • 細かい設定の変更はあるが、一人でもできそう
  • エラーが出たらよく内容を読んで頑張る(笑)
  • 正式リリース直後は英語のドキュメントが多くて苦労したけど、今は日本語のドキュメントもまとまってる
  • Rails 4の知識があればそんなに困らない
  • これから勉強するならまず、Rails 4から始めてその後にRails 5にチャレンジするのが良さそう!
harayama2
#HAB&Co. CTO #Oita Creative Academy 講師 #フリーランス
https://toaru-kaihatsu.com
Why not register and get more from Qiita?
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away