コード日進月歩

しんくうの技術的な小話、メモ、つれづれ、など

Railsのcallbackは事前にメソッドを呼び出すだけなので、raiseしたExceptionはshowなどのメソッドのrescueでキャッチしてくれない

よくよく考えたら当たり前なんだけど、もしかしたらやってくれるのでは!?みたいな淡い期待を打ち砕いてくれたので、やってみたメモ

環境

rails (5.2.0)

処理例

仕様

/sample/1 を指定したときは {sample: true}jsonが返却され、それ以外のときは {sample: false} が返ってくる

コード例

class SampleController < ApplicationController
  def show
    # パラメータは文字列なので数値に
    response_bool = params[:id].to_i == 1 ? true : false
    render json: {sample: response_bool}
  end
end
Rails.application.routes.draw do
  resources :sample
  #... (以下略)....
require "rails_helper"

RSpec.describe "SampleController", type: :request do
  describe "GET /sample" do
    subject { get sample_path(id), params: {}, headers:{} }
    context "idが1でリクエストしたとき" do
      let(:id) {1}
      it "trueが返ってくる" do
        subject
        request_response = JSON.parse(response.body)
        expect(request_response["sample"]).to eq(true)
      end
    end
    context "idが2でリクエストしたとき" do
      let(:id) {2}
      it "falseが返ってくる" do
        subject
        request_response = JSON.parse(response.body)
        expect(request_response["sample"]).to eq(false)
      end
    end
  end
end

改変案

before_actionでエラーチェックして false をrenderすればいいのでは…?

class SampleController < ApplicationController
  before_action :check_param
  def show
    render json: {sample: true}
  rescue ArgumentError
    render json: {sample: false}
  end

  private

    def check_param
      # 独自Exceptionが望ましいけど例示なのでArgumentError
      raise ArgumentError unless params[:id].to_i == 1
    end
end

こんなふうに書けばいけるのでは!?とか思ったが盛大にコケる

$ bundle exec rspec spec/request/sample_spec.rb 
.F

Failures:

  1) SampleController GET /sample idが2でリクエストしたとき falseが返ってくる
     Failure/Error: raise ArgumentError unless params[:id].to_i == 1
     
     ArgumentError:
       ArgumentError

ですよねーって感じです。

やるべきだったやり方

class SampleController < ApplicationController
  def show
    check_param
    render json: {sample: true}
  rescue ArgumentError
    render json: {sample: false}
  end

  private

    def check_param
      # 独自Exceptionが望ましいけど例示なのでArgumentError
      raise ArgumentError unless params[:id].to_i == 1
    end
end
$ bundle exec rspec spec/request/sample_spec.rb 
..

Finished in 0.44626 seconds (files took 9.61 seconds to load)
2 examples, 0 failures

意図通りに行きました。