Ruby
Rails
grape

Grapeでヘルパーメソッドを別ファイルに切り出して使う方法

More than 1 year has passed since last update.

環境

  • ruby歴 初心者
  • rails5
  • ruby 2.4.1p111

これはなに?

grape を用いた api 開発時にヘルパーメソッドを別ファイルに切り出して活用する方法のメモです。

「ここ間違ってるよ!」
「ここはこうした方がいいんじゃん?」
などなどあればコメントお願い致しますm(_ _)m

発端

grape を用いた api 開発を進めていく中で、共通の処理、独自エラーなどをルートファイルに書きまくって
fat & unreadable になったため切り出しを考えました。

ディレクトリ状況としては下記のとおりです。

├── app
│   ├── apis
│   │   └── api
│   │       ├── concerns
│   │       ├── root.rb
│   │       └── v1
│   │           └── sample.rb

実装

はまった部分はありましたが、先に結果を書きます。
※はまった部分に関しては、後述しますので興味があれば!

まず、共通処理部分をモジュールとして切り出します。
今回、バージョンごとのヘルパーメソッドとするので
モジュール名は V1::Helpers::AuthenticateHelper とします。
名前空間を V1::Helpers としているので
下記のように配置します。

├── app
│   ├── apis
│   │   └── api
│   │       ├── concerns
│   │       ├── root.rb
│   │       └── v1
│   │           ├── sample.rb
│   │           └── helpers
│   │                   └── authenticate_helper.rb

あとは、それぞれの実装

authenticate_helper.rb
module V1
    module Helpers
        module AuthenticateHelper
            def authenticate!
                # someting proccess...
            end

            def current_user
                # someting proccess...
            end
        end
    end
end

root.rb
class API::ROOT < Grape::API
    helpers V1::Helpers::AuthenticateHelper # -> ここに追加

    version "v1", using: :path
    format :json
    formatter :json, Grape::Formatter::Rabl
    prefix "api"

    namespace :sp do
      mount API::V1::Sample
    end

    # something method & proccess
end

実際の利用シーン
rable を組み合わせて実装しています。

sample.rb
class API::V1::CalendarPhotoAssets < Grape::API
    before { authenticate! }

    get :samples, rabl: "samples/index" do
      @samples = current_user.samples
    end
end

名前空間とMix-in

モジュールには名前空間Mix-inという複数の役割があるという理解ができておらずハマってしまいました。
自分はMix-inのみで考えていたため、うまく読み込めずメソッドをコールしても「そんなメソッドねえよ」と怒られるばかりでした。
V1::Helper の部分は名前空間なのでMix-inする対象とは何も関係ありません。

GrapeにおけるMix-in

最初、 include V1::Helpers::AuthenticateHelper と宣言していましたが、エラーしていました。
これは grape の実装手順に則っていたためですが include ではなく helpers でモジュールを Mix-in します。
include する場合は、ROOT を継承すれば問題ありません。

おまけ

ヘルパーモジュールが増えてくると
下記のように箇条書きされていき fat 要因になります。

root.rb
class API::ROOT < Grape::API
    helpers V1::Helpers::AuthenticateHelper
    helpers V1::Helpers::Sample1Helper
    helpers V1::Helpers::Sample2Helper
    helpers V1::Helpers::Sample3Helper

    version "v1", using: :path
    format :json
    formatter :json, Grape::Formatter::Rabl
    prefix "api"

    namespace :sp do
      mount API::V1::Sample
    end

    # something method & proccess
end

なので、宣言自体を切り出して
1行の宣言で済むようにします。
※ namespace名と同じファイル名にしてください

├── app
│   ├── apis
│   │   └── api
│   │       ├── concerns
│   │       ├── root.rb
│   │       └── v1
│   │           ├── sample.rb
│   │           ├── helpers.rb # -> ここに namespace名.rb を追加
│   │           └── helpers
│   │                   └── authenticate_helper.rb

helpers.rb
module V1
    module Helpers
        # helpers は grape だけなので include で Mix-inします
        include V1::Helpers::AuthenticateHelper
        include V1::Helpers::Sample1Helper
        include V1::Helpers::Sample2Helper
        include V1::Helpers::Sample3Helper
    end
end
root.rb
class API::ROOT < Grape::API
    helpers V1::Helpers

    version "v1", using: :path
    format :json
    formatter :json, Grape::Formatter::Rabl
    prefix "api"

    namespace :sp do
      mount API::V1::Sample
    end

    # something method & proccess
end

学んだこと

・モジュールの役割について
・Grape における Mix-in

参考

RubyのModuleの使い方とはいったい

rubyのモジュールを名前空間で使っているのかmix-inで使っているのか