rails のconcernを使ってエラーレスポンスを返す忘備録

## concernとは
``app/concerns``というディレクトリが存在する。``concerns``は

・複数のコントローラー（モデル）から利用される処理を共通化する目的
・ある特定のまとまった処理を切り出して見通しをよくする目的

上記の目的で使われることが多い。

## Concernの書き方
まずは``concern``のファイルを記入し、その後コントローラーにて``include``で表記する。
```MyConcern.rb
module MyConcern
  extend ActiveSupport::Concern

  # Scope や Callbackなどの処理を実装する
  included do
    scope :without_deleted, lambda{ where(deleted_at: nil) }
  end

  module ClassMethods
    def foo
      puts 'foo'
    end
  end

  def bar
    puts 'bar'
  end
end
```

```application_controller.rb
class ApplicationController < ActionController
   include MyConcern
   
   処理
   .
   .
end
```
## エラーレスポンスを記入
```app/controllers/concerns/api/exception_handler.rb
module Api::ExceptionHandler
  extend ActiveSupport::Concern

  included do
    rescue_from StandardError, with: :render_500
    rescue_from ActiveRecord::RecordNotFound, with: :render_404
  end

  private

  def render_400(exception = nil, messages = nil)
    render_error(400, 'Bad Request', exception&.message, *messages)
  end

  def render_404(exception = nil, messages = nil)
    render_error(404, 'Record Not Found', exception&.message, *messages)
  end

  def render_500(exception = nil, messages = nil)
    render_error(500, 'Internal Server Error', exception&.message, *messages)
  end

  def render_error(code, message, *error_messages)
    response = {
        message: message,
        errors: error_messages.compact
    }

    render json: response, status: code
  end
end
```
**``・rescue_from``**
``rescue_from``は、特定の種類または複数の種類の例外を1つのコントローラ全体およびそのサブクラスで扱えるようにするもの。
▶︎[rescueとrescue_from](https://qiita.com/fgem28/items/2de15a6a0873b6ec01b2)

**``・exception``**
例外オブジェクトを生成して返すメソッド。
```rb
e = Exception.new("some message")
p e         # => #<Exception: some message>
p e.message # => "some message"
```
▶︎[exception](https://docs.ruby-lang.org/ja/latest/class/Exception.html#S_EXCEPTION)

**``・exception&.message``**
ぼっち演算子。レシーバーが``nil``のときだけは、メソッドが呼び出されないで``nil``を返す。
▶︎[Ruby ぼっち演算子について](https://qiita.com/yoshi_4/items/e987b698c1978d248cfc)

**``・*messages``**
メソッドの引数にアスタリスクがある。アスタリスク1つなら可変長引数、アスタリスク2つならオプション引数という。

**可変長引数（アスタリスクが１つ）**
``*``をつければ引数を配列に指定できる。
メソッドには1つの引数の定義で、複数の引数を設定でき、配列として結果を返す。ただしこの可変長引数は1メソッドにつき、1つだけしか指定できない。
``*``と記述することで受け取った引数を無視するようにも使用できる。

```rb
#複数の引数を配列にする
def array(*a)
  p a
end

> array(1,2)
=> [1, 2] 
> array(1, 2, 3)
=> [1, 2, 3]
```

**オプション引数(アスタリスクが２つ)**
``**``をつければ引数を、ハッシュに指定できる。
メソッドには一つの引数の定義で、複数の引数を設定でき、ハッシュとして結果を返す。

```rb
#複数の引数をハッシュにする
def array(**a)
  p a
end

> array(b: 1, c: 2)
=> {:b=>1, :c=>2} 
> array(b: 1, c: 2, d: 3)
=> {:b=>1, :c=>2, :d=>3}
```
▶︎[*と記述することで受け取った引数を無視するようにも使用](https://qiita.com/TakeshiFukushima/items/f2c338cd70d56fd1d800#%E5%8F%AF%E5%A4%89%E9%95%B7%E5%BC%95%E6%95%B0)

**``・compact``**
``compact``は自身から``nil``を取り除いた配列を生成して返す。
```rb
ary = [1, nil, 2, nil, 3, nil]
p ary.compact   #=> [1, 2, 3]
p ary           #=> [1, nil, 2, nil, 3, nil]
ary.compact!
p ary           #=> [1, 2, 3]
p ary.compact!  #=> nil
```
▶︎[compact](https://docs.ruby-lang.org/ja/latest/method/Array/i/compact.html)

## rspec
```spec/requests/api/v1/exception_handler_spec.rb
RSpec.describe 'Api::ExceptionHandler', type: :request do
  let!(:user) { create(:user) }

  describe 'render 500' do
    it 'returns errors in json format' do
      allow(Article).to receive(:all).and_raise(StandardError)
      get api_v1_articles_path, headers: { CONTENT_TYPE: 'application/json', ACCEPT: 'application/json' }

      expect(body['message']).to eq('Internal Server Error')
      expect(body['errors'].size).to eq(1)
      expect(response).to have_http_status(500)
    end
  end

  describe 'render 404' do
    let(:id) { 1 }

    it 'returns errors in json format' do
      get api_v1_article_path(id), headers: { CONTENT_TYPE: 'application/json', ACCEPT: 'application/json' }

      expect(body['message']).to eq('Record Not Found')
      expect(body['errors']).to eq(["Couldn't find Article with 'id'=#{id}"])
      expect(response).to have_http_status(404)
    end
  end
end
```
▶︎[使えるRSpec入門・その3「ゼロからわかるモック（mock）を使ったテストの書き方」](https://qiita.com/jnchito/items/640f17e124ab263a54dd#2-%E3%83%A2%E3%83%83%E3%82%AF%E3%81%AB%E8%BF%94%E4%BA%8B%E3%81%AE%E4%BB%95%E6%96%B9%E3%82%92%E6%95%99%E3%81%88%E8%BE%BC%E3%82%80)

**``・allow(Article).to receive(:all)``**
``allow(モックオブジェクト).to receive(メソッド名)``の形で使われる。英語の意味からも分かるように、``allow A to receive B``で、「A が B を受け取ることを許可する」の意味になる。
つまり上記は、Articleの全てを受け取ることを許可する、と言う意味になる。

## 参考記事
[rails concernの使い方](https://lanlib.com/2021/04/04/rails-concern%e3%81%ae%e4%bd%bf%e3%81%84%e6%96%b9/)
[Rails/Model(Active Record)のConcern](https://morizyun.github.io/ruby/active-record-concern.html)