初めまして、システム事業部の坂本です。
以前、Railsのマイグレーション(バージョンアップ)業務を担当したことがありました。 その際Railsのバージョンアップに伴い、「バージョンを上げると使えなくなる記述」が非常に多く骨が折れた記憶があります。 自身の備忘録も兼ねて、主にこの記事では以下3点にフォーカスを当ててRailsのマイグレーションに関して書いて行きたいと思います。
- ステップ1:そもそもマイグレーションってどうやったの?
- ステップ2:修正が必要な個所を洗い出してみる
- ステップ3:実際に修正してみる(具体例有)
マイグレーション業務に関わる予定がある方でも無い方でも、最後まで見て頂ければ幸いです。 「そんな記述が昔は出来たんだ」「あのバージョンでこの機能はまだ実装されてなかったんだ」という発見があって、ちょっと面白いと思います。
ステップ1:そもそもマイグレーションってどうやったの?
根本的な話からです。大まかに説明すると、私が担当した際はRailsのマイグレーション作業を以下の流れで行いました。
- Gemfileを修正し、
bundle install
等でRailsのマイナーバージョンを1段階上げる - ソースを修正する
- アプリを動かして、一通りの機能を動かしてみる
- 修正が不十分であればアプリが落ちるので、必要に応じて更に適宜修正
- 全ての修正が完了したら1.へ戻り、目的のRailsバージョンまで繰り返す
上記は飽くまでも一例ですが、基本的には「1段階上げて修正、完了したらまた1段階上げて修正、完了したらまた1段階……」という風に、1ステップずつ作業を行うのが適切です。*1 いきなりRails3.2系からRails5.0系まで上げる、みたいなことをすると多分詰みます。(経験談) 慌てず1段階ずつ行きましょう。
Gemfileの修正について
Gemfileを開いて、railsに対応するバージョンを上書きします。 ここで、例えばrailsを「4.0」と指定すれば「actionmailer」「activemodel」「activesupport」等、Railsのコアライブラリも「4.0」まで上がります。
Gemによっては「actionmailer4.0に対応しているバージョンはX.X.Xから」「rails4.0に対応しているバージョンはX.X.Xから」という風に指定があるため、 bundle install
のエラーやRubyGems*2を参考にしながら、適切なバージョンまで必要なGemのバージョンを上げていきます。
ステップ2:修正が必要な個所を洗い出してみる
さてソースコード修正開始の前に、下準備から行きましょう。いざとやろうとしても、闇雲にやると後で良く分からなくなってしまいます。 まず全体を通して、以下の記述3点は後程修正が必要になる可能性が非常に高いです。
- モデルに対して検索・更新・削除等が行われる記述(特にwhereメソッドを利用していない箇所)
- Gemファイルに依存する記述(バージョンアップで記述が変わるGemもある)
- コントローラー、モデルに対するオプション全般
そしてRails3.2系から5.0系への大幅なマイグレーションを行った中で、 ソース修正時のメインとなったのは大きく分けて以下の3通りでした。
- find(3.2→4.0へアップグレード)
- updated_all、Strong Parametersへの対応(4.0→4.1へアップグレード)
- validate全般(4.2→5.0へアップグレード)
間違いなく3.2から4.0に挙げた際の「find」の修正が一番の壁でした。 マイグレーション作業の4割近くはこのメソッドに対する対応だった記憶があります。
勿論上記以外にも修正すべき場所はまだありますが、少なくともここまで私が言及した内容に関してはマイグレーション作業の中で修正が必要になると思われます。 修正が必要なファイル・対応行を一通り書き留めて、後で修正が完了したか確認出来る簡易的なチェックリストを用意しておきましょう。
ステップ3:実際に修正してみる(具体例有)
さて、ここからが本題です。実際にソースコードを修正するとどうなるのか?をコメントと共に記載します。 特に説明が無い限り、以下の通りに記述していきます。
修正前 ↓ 修正後
修正した個所は沢山ありましたが、今回はその中でも特に印象に残った以下4点をご紹介します。
- find
- update_all
- validate全般
- Strong Parameter (Rails3系からRails4系)
「find」を修正してみる
基本的な書き換え
User.find(:all, :conditions=> "postal_code=1234567 and gender=0", :order => "name") ↓ User.where(postal_code: 1234567, gender: 0).order(:name)
マイグレーション作業でまず一番最初に驚いた記述がこれでした。Rails3.2系では、findメソッドの中に検索条件・Order条件等を一通り入れることが出来たのですね……
しかし今のRailsではそんなことは出来ませんので、シンプルにwhereメソッドやorderメソッドを利用した記述に変更しましょう。
条件に対する1件目のみ取得
User.find(:first, :conditions=> "name='ゼネット太郎'") ↓ User.find_by(name: 'ゼネット太郎')
先程との差異は「find(:all, ......)」だったのが「find(:first, ......)」になっているところですね。 検索結果の1件目のみ取得したいのであれば、「find_by」が利用できるのでそちらを使用します。
「update_all」を修正してみる
User.update_all("first_name= '田中'", "first_name= '山田'") ↓ User.where(first_name: '山田').update_all(first_name: '田中')
やりたいことはどちらも「苗字が'山田'のユーザ全員の苗字を、'田中'に変更する」です。 以前までは「update_all(更新内容, 条件式)」を利用して、条件式に該当するデータ全てを更新することが出来ましたが現在はupdated_allに引数を2つ以上持たせることは出来ません。 そのため、「where(条件式).update_all(更新内容)」に書き換えます。
余談ですが、修正前をよくよく見てみるとSQLっぽいですね。 「修正前はUPDATE users SET first_name='田中' WHERE first_name='山田';
を取り合えずRailsで実装してみて、修正後はよりRailsらしく分かりやすい実装にしてみた」という印象を受けます。
validate全般を修正してみる
だいたい修正の方法が同一なので、まとめて4パターンのみ記載します。
ユーザの名前の長さを最大10文字までにする
validates_length_of :name, :maximum => 10 ↓ validates :name, length: { maximum: 10 }
ユーザの年齢は整数のみ許可する
validates_numericality_of :age, :only_integer => true ↓ validates :age, numericality: { only_integer: true }
ユーザの名前は必須とする
validates_presence_of :name ↓ validates :name, presence: true
ユーザの月収は数字のみ許可する
validates_numericality_of :salary ↓ validates :salary, numericality: true
ここまで、修正方法4パターンを紹介しました。何となく法則性があるのが見て取れると思います。 他にもフォーマットの修正・ユニークの設定等もありますが、基本的には以下の2パターンで修正することが出来ます。
validates_XXXXXXXX_of :カラム名, :オプションキー => オプション値 ↓ validates :カラム名, XXXXXXXX: {オプションキー: オプション値}
validates_XXXXXXXX_of :カラム名 ↓ validates :カラム名, XXXXXXXX: true
Strong Parametersへの対応(Rails3系からRails4系)
Strong Parametersを大まかに説明すると、ユーザからは特定の項目のみ更新可能にしてセキュリティを強化する仕組みです。ですが、Strong ParameterはRails4系から実装されたものでありRails3系では無かった機能となります。 そのため、Rails3系からRails4系にアップグレードした際、必要であれば対応を行って下さい。
長くなるためここではStrong Parametersに関する言及は行いませんが、詳しくはRuby on Railsガイドのこちらの記事をご確認下さい。
Action Controllerの概要 4.5 Strong Parameters | Railsガイド
所感など
さて、ここまでRailsのマイグレーション作業に関して色々書きましたが、もっと書こうと思うと長くなりすぎてしまうので今回はこの程度に収めておきます。
この記事に記載したソースコードのBefore→Afterを改めて見ると、全体的にスッキリして読みやすくなっている印象を受けました。 特にバリデーションはただ単純にスッキリしているだけではなく、実装のしやすさも変わっていると思います。 例えば、実際に「ユーザの名前は必須かつユニークで、最大10文字まで可能」というバリデーションを実装しようとするとRails3系までの記述・Rails5系からの記述ではこのように変わります。
validate_presence_of :name validate_uniqueness_of :name validate_length_of :name, :maximum => 10 ↓ validates :name, presence: true, uniqueness: true, length: {maximum: 10}
上記のBefore→Afterを見ていると、Rails5の方が文字数も少なくスッキリまとまっていて、パッと見た時に意味が分かりやすい印象を受けるのではないでしょうか。
Railsに限らず、様々な言語でバージョンアップに伴う細かい記述方法の変更があると思われますが、「何故この記述に変更になったのだろうか?」「変更されたことで何かメリットはあるのだろうか?」と考えてみると、ちゃんとした意味があることに気づけて面白いと思いました。
マイグレーションの作業は想像以上に大変だと思いますので、慌てず計画的にやっていきましょう!
最後まで見て頂き、ありがとうございました。