Techioz Blog

Railsでカスタムモジュールアプリを作成するにはどうすればよいですか?

概要

私はRailsの初心者なので、APIのみのアプリを試し始めました。次のような構造のモジュール型アプリを試してみたいと思います。

├── app
│   ├── channels
│   │   └── application_cable
│   │       ├── channel.rb
│   │       └── connection.rb
│   ├── controllers
│   │   ├── application_controller.rb
│   │   └── concerns
│   ├── jobs
│   │   └── application_job.rb
│   ├── mailers
│   │   └── application_mailer.rb
│   ├── models
│   │   ├── application_record.rb
│   │   └── concerns
│   ├── users
│   │   ├── controllers
│   │   │   └── auth_controller.rb
│   │   ├── models
│   │   │   └── user_model.rb
│   │   ├── routes
│   │   │   └── user_routes.rb
│   │   ├── schema
│   │   │   └── request
│   │   │       └── auth_request.rb
│   │   └── usecase
│   │       └── auth_usecase.rb

しかし、auth_controller.rbが作成したルートにマッピングできないこれらの構造に問題があります。以下にコードをリストします。

auth_controller.rb

module Users
  class AuthController < ActionController::API
    def login
      body = JSON.parse(request.body.read)

      login_request = LoginRequest.new(body[:email],body[:password])
      if login_request.valid?

      else
        render json: { error: 'email or password can\'t be empty' }, status: :bad_request
      end

    rescue JSON::ParserError
      render json: { error: 'Invalid JSON format' }, status: :bad_request
    end
  end
end

ユーザーモデル.rb

module Users
    class UserModel < ApplicationRecord
      this.table_name = "users"

      def create_user(users)
        save(users)
      end

      def find_user_by_id(id)
      end

      def find_user_by_email_password(email, password)
      end
    end
end

ユーザールート.rb

module Users
  module Routes
    def self.draw
      Rails.application.routes.draw do
        namespace :users do
            post 'auth/login', to: 'auth#login'
            post 'auth/register', to: 'auth#register'
            post 'auth/reset-password', to: 'auth#reset_password'
            post 'auth/send-otp', to: 'auth#send_otp'
        end
      end
    end
  end
end

auth_request.rb

module Users
  module Schema
    module Request
      LoginRequest = Struct.new(:email,:password) do
        def valid?
          valid_email? && password.present?
        end
      end

      RegisterRequest = Struct.new(:first_name,:last_name,:email,:password,:phone) do
        def valid?
          first_name.present? && last_name.present? && valid_email? && password.present? && phone.present?
        end

        def valid_email?
          email =~ /\A[^@\s]+@[^@\s]+\z/
        end
      end
    end
  end
end

auth_usecase.rb

module Users
  module Usecase
    class AuthUsecase
      def initialize(user_model)
        @user_model = user_model
      end

      def login(email, password)
        _ = user_model.find_user_by_email_password(email,password)
      end

      def register(user)
        user = user_model.create_user(user)
      end
    end
  end
end

config/routes.rb

Rails.application.routes.draw do
  # Define your application routes per the DSL in https://guides.rubyonrails.org/routing.html

  # users routing /v1/users/*
  require_relative '../app/users/routes/user_routes'
  Users::Routes.draw

  # Reveal health status on /up that returns 200 if the app boots with no exceptions, otherwise 500.
  # Can be used by load balancers and uptime monitors to verify that the app is live.
  get "up" => "rails/health#show", as: :rails_health_check

  # Defines the root path route ("/")
  # root "posts#index"
end

いつもこのようなエラーが発生します

{
    "status": 404,
    "error": "Not Found",
    "exception": "#<ActionController::RoutingError: uninitialized constant Users::AuthController>",
    "traces": {
        "Application Trace": [],
        "Framework Trace": [],
        "Full Trace": []
    }
}

誰がこれらのエラーを解決できるか教えていただけますか?そして、コントローラーフォルダー内にモジュール名コントローラーを作成する必要がありますか? Rails7.1.2を使用しています

解決策

これは行わないでください。ただし、Rails での自動ロードがどのように機能するかを学ぶと役立つかもしれません。

app/
└── users/
    ├── controllers/
    │   └── auth_controller.rb
    ├── models/
    │   └── user_model.rb
    └── schema/
        └── request/
            └── auth_request.rb
# config/application.rb

Users = Module.new
Rails.autoloaders.main.push_dir(Rails.root.join("app/users"), namespace: Users)
Rails.autoloaders.main.collapse(Rails.root.join("app/users/controllers"))
Rails.autoloaders.main.collapse(Rails.root.join("app/users/models"))

定数を定義するときは、ここでも何らかの構造に従う必要があります。モデルとコントローラーのディレクトリは折りたたまれているため、対応する定数は必要ありません。

# app/users/controllers/user_controller.rb
module Users
  class AuthController
  end
end      
# app/users/models/user_model.rb
module Users
  class UserModel
  end
end   
# app/users/schema/request/auth_request.rb
module Users
  module Schema
    module Request
      module AuthRequest
      end
    end
  end
end

テスト:

>> Users::AuthController
=> Users::AuthController
>> Users::UserModel
=> Users::UserModel
>> Users::Schema::Request::AuthRequest
=> Users::Schema::Request::AuthRequest

https://translate.google.com/translate?hl=ja&sl=en&tl=ja&u=https://github.com/fxn/zeitwerk

ルートについては、routes.rb を複数のファイルに分割する描画メソッドがあります。 https://translate.google.com/translate?hl=ja&sl=en&tl=ja&u=https://api.rubyonrails.org/classes/ActionDispatch/Routing/Mapper/Resources.html#method-i-draw