# Rails での例外処理

Rails での例外処理について簡単に残しておきます。

# lib/csv_reader.rb

module CsvReader
  require 'csv'

  def import_user(csv_path)
    read_csv = CSV.read(csv_path, headers: true)
    ActiveRecord::Base.transaction do
      read_csv.each do |csv|
        user = User.find_or_initialize_by(id: csv['ID'])
        user.update!(user_attr(csv))
      end
    end
    true
  rescue ActiveRecord::RecordInvalid => e
    error_messages = e.record.errors.full_messages.map { |error| "ID: #{e.record.id}#{error}" }
    Rails.logger.debug error_messages
    error_messages
  end

  def user_attr(csv)
    {
      id: csv['ID'],
      first_name: csv['名前'],
      last_name: csv['苗字'],
      age: csv['年齢']
    }
  end
end

# find_or_initialize_by

find_or_initialize_by は、引数の条件に該当するデータを探し、 あれば find_by(引数)、なければ new(引数) する。

find_or_create_by は、引数の条件に該当するデータを探し、 あれば find_by(引数)、なければ create(引数) する。

# update!

update! のように、 update! をつけることによって、 update できなかったときに、例外を発生させることができます。

# ActiveRecord::Base.transaction

ActiveRecord::Base.transaction do ~ end で囲まれた部分で例外が起きると、 transaction で囲まれた部分でロールバック(処理が行われる前の状態に戻る処理)が走ります。

# ActiveRecord::RecordInvlalid

バリデーションに引っかかった時、起きるエラーです。

# 例外を発生させた時の情報

ActiveRecord の例外を発生させたときのインスタンスの情報を e.record で取れます。 エラーメッセージは e.record.errors.full_messages で取れます。

# UsersController で、CsvReader.import_user を呼ぶ

# app/controllers/users_controller.rb

# frozen_string_literal: true

class UsersController < ApplicationController
  ...

  def import
    csv_file = params[:csv_file]

    if csv_file.present?
      @user = CsvReader.import_user(csv_file.path)
      if @user == true
        flash[:success] = 'インポートに成功しました'
      else
        flash[:danger] = ['インポートに失敗しました', @user].flatten
      end
    else
      flash[:danger] = 'ファイルが選択されていません'
    end
    redirect_to new_user_path
  end
end

# 例外を発生させることができるもの

  • save!
  • create!
  • update!

# 参考