Railsアプリケーションでmixinを利用する

# 概要

Rubyの特徴であるmixinをどのようにRailsアプリケーションで活用したときに、どのようなメリットがあるのか考える。

# mixinとは
moduleをclassでincludeすることにより、moduleのメソッドをclassで使用できるようにすること。

```ruby
module TestModule
  def test
    "test"
  end
end

class TestClass
  include TestModule
end

Test.new.test
=> "test"
```

# Railsアプリケーションでの利用
業務ロジックをmoduleとして定義し、Controllerでincludeする

```ruby:controllers/concerns/user_client.rb
module UserClient
  extend ActiveSupport::Concern

  included do
    helper_method :user
  end

  def user
    @_user ||= User.find(params[:user_id])
  end
end
```

```ruby:controllers/concerns/user_profile_client.rb
module UserProfileClient
  extend ActiveSupport::Concern
  include UserClient

  included do
    helper_method :user_profile
  end

  def user_profile
    @_user_profile ||= UserProfile.find(user.id)
  end
end
```

```ruby:controllers/users_controller.rb
class UsersController < ApplicationController
  include UserClient
  include UserProfileClient

  def show
  end
end
```

```erb:views/users/show.erb.html
<%= user.name %>
<%= user_profile.age %>
```

# mixin利用のメリット

## Fat Controllerからの脱却

従来の実装では、Controllerに処理の記述が多くなってしまう。
mixinを利用することで、moduleとして切り出すことができる。

```ruby:controllers/users_controller.rb
class UsersController < ApplicationController
  def show
    @user = User.find(params[:user_id])
    @user_profile = UserProfile.find(@user.id)
  end
end
```

## Fat Modelからの脱却

Fat Controllerから脱却手段としては、Modelに業務ロジックを記載する方法もある。
別モデルの処理の記述が入ったりして、特定モデルの処理の記述が多くなってしまう。
複数のモデルを参照する処理をmoduleに切り出すことで、特定モデルの記述を最小限にする。

```ruby:models/users.rb
class UserModel < ActiveRecord::Base
  # relationで取得するのがベストだが、あくまでmixinとの比較のため今回はメソッド定義の形とする。
  def user_profile
    UserProfile.find(id)
  end
end
```

## 処理順を気にしなくてよくなる

Fat Controllerの例では、userを生成してから、user_profileを生成するという処理順を気にする必要がある。

処理順を逆にするとエラーになる。

```ruby:controllers/users_controller.rb
class UsersController < ApplicationController
  def show
    @user_profile = UserProfile.find(@user.id) # @userはnilなので、idメソッドをコールできずにエラーとなる。
    @user = User.find(params[:user_id])
  end
end
```

一方で、mixinではmodule間の依存関係に気をつければよく、処理順を気にする必要はない。

```ruby:controllers/concerns/user_profile_client.rb
module UserProfileClient
  extend ActiveSupport::Concern
  # 依存関係を定義
  include UserClient

  included do
    helper_method :user_profile
  end

  def user_profile
    # userメソッドをコールしたときに処理が実行されるので、何らかの事前処理は不要。
    @_user_profile ||= UserProfile.find(user.id)
  end
end
```

## Controllerのbefore_actionの煩わしさから解放される
特定のUser情報だけ必要なdef showと、すべてのUser情報が必要なdef indexを定義したとき、
それぞれのメソッドで必要な情報を考慮して、before_actionにonly指定をする必要がある。
only指定をしない場合、showですべてのUser情報取得の処理が実行され、無駄な処理をしてしまう。

```ruby:controllers/users_controller.rb
class UsersController < ApplicationController
  before_action :user, only: :show
  before_action :users, only: :index

  def show
  end

  def index
  end

  private

  def user
    @user = User.find(params[:user_id])
  end

  def users
    @users = User.all
  end
end
```

一方で、mixinを利用したとき、moduleのメソッドがコールされたときに処理が実行されるので、moduleで必要となるメソッドを定義しておいても無駄な処理は発生しない。必要なときに必要な処理量で、必要な情報を取得できる。

```ruby:controllers/concerns/user_client.rb
module UserClient
  extend ActiveSupport::Concern

  included do
    helper_method :user
    helper_method :users
  end

  def user
    @_user ||= User.find(params[:user_id])
  end

  def users
    @_users ||= User.all
  end
end
```

```ruby:controllers/concerns/user_profile_client.rb
module UserProfileClient
  extend ActiveSupport::Concern
  include UserClient

  def show
  end

  def index
  end
end
```

```erb:views/users/show.erb.html
<%= user.name %>
<%# usersをコールしない限り、usersの処理は実行されないが、必要な時はコールできる %>
<%= users.size %>

```

```erb:views/users/index.erb.html
<%= users.name %>
```

## 論点

### moduleで定義したメソッドをviewで使用する場合、helper_methodとして定義するか、helper側でcontrollerにdelegateするか

paramsはhelper側でdelegateしている
https://github.com/rails/rails/blob/master/actionview/lib/action_view/helpers/controller_helper.rb#L16