🦙

モジュールを使った名前空間でRailsのコードをリファクタリングする

2024/09/17に公開

名前空間とは

名前空間はメソッドや変数の名前が衝突しないように管理するための仕組みで、様々なプログラミング言語で実装されていて、Rubyではモジュールを使って名前空間を実現できます。

module Hoge
  class Fuga  
    def hello
      puts "hello namespace"
    end
  end
end

fuga = Hoge::Fuga.new
fuga.hello

名前空間を活用するとクラス名や名前の衝突を防ぐほかに、
関連するクラスやメソッドを特定のフォルダにまとめることで、プロジェクト全体の構造を把握しやすくなります。

Rails内部で利用されている機能例としては ActionMailer::BaseActiveRecord::Base などが挙げられます。

ここからは、Railsで名前空間を使う具体例を交えながら、そのメリットについて説明していきます。

モデルやクラスを整理する

名前空間を利用してディレクトリを作成し、モデルやクラスを集約することができます。

PaymentProcessPaymentRefundというクラス名を Payment::ProcessPayment::Refundすることができ、ディレクトリ構成についても下記のように整理が可能です。

before

app/
  └── models/
       ├── payment_process.rb
       └── payment_refund.rb

after

app/
  └── models/
       └── payment/
             ├── process.rb
             └── refund.rb

このように関連するクラスをひとまとめに整理することで、
多くのクラスが増えてきたときに階層化しておくことでPaymentに関わるModelを見つけるのが容易になります。

Controllerを整理する

Controllerでも名前空間を使ってコードをリファクタリングすることが可能です。
下記のように管理者用の画面を実装する場合に、Adminという名前空間の配下にControllerを作っておくと見通しが良くなります。

class Admin::StaffsController < Admin::ApplicationController
    def index
        # ...
    end
end

Controllerでもディレクトリ構造もこのようになります。

app
└── controllers/
    ├── admin/
    │   ├── application_controller.rb
    │   └── staffs_controller.rb
    ├── application_controller.rb
    └── users_controller.rb

Viewファイルも下記のような構造になっています。

└── views/
    ├── admin/
    │   └── staffs/
    │       └── index.html.erb
    └── users/
        ├── _form.html.erb
        ├── _user.html.erb
        ├── index.html.erb
        └── show.html.erb

管理者(Admin)用のControllerを作る

Admin::ApplicationController を作って継承することで、
管理者用Controllerの共通処理をAdmin::ApplicationControllerにまとめておくことが可能です。

ルーティングについても下記のように定義することで対応可能です。

Rails.application.routes.draw do
  namespace :admin do
    resources :staffs
  end
end

デメリット

名前空間を使いすぎるとディレクトリが深くなりすぎて、コードの見通しが悪くなるため、2〜3階層にとどめることをお勧めします

app/
  └── models/
       └── user/
             └── comment
                 └── response
                       └── image.rb

モジュールが使えないケース

Railsでは自動でクラスを読み込む仕組みがあり、モジュール名と同じ名前のクラスがすでに存在しているとエラーが発生することがあります。
このときはモジュールは利用できないので下記のように定義しましょう。

class User::Session
  def initialize(user)
    @user = user
  end
  
  def create!
    # ...
  end
end
User::Session.new(user).create!

まとめ

名前空間を使ってモデルやControllerを整理する事例を紹介しました。
モデルやControllerの整理が容易になり、クラス名が長くなりすぎることを防げます。ディレクトリ構造が整理されることで、クラス間の関係性も把握しやすくなります。弊社でもこの方法を積極的に活用しており、基本的な内容ではありますがリファクタリングの参考となれば幸いです。

OSIRO テックブログ

Discussion