Rails Namespace Modelを利用した際のrelation を修正する

# Rails Namespace Modelを利用する

## TL; DR

rails7の場合に(8だと解消するPRが含まれていたchange logがあったきがするが探せてないw)、nested classの参照解決をして、利用しやすいように適宜renameする。

> Fix inference of association model on nested models with the same demodularized name.

[change log]()

## 0. 環境

| name | version |
| :--: | :---: |
| rails | 7.2.1.1 |
| ruby | 3.3.5 |
| mysql | 8.4 |

## 1. 想定するModelとdatabase schema

### 1-1. Model

```mermaid
classDiagram
    class Organization {
        +Int id
    }
    class OrganizationUser {
        +Int id
    }
    class User {
        +Int id
    }
```

```mermaid
erDiagram
    organizations {
        int id PK
        varchar(255) name
    }
    organization_users {
        int id PK
        int organization_id FK
        int user_id FK
    }
    users {
        int id PK
        varchar(255) name
    }
```

### 1-2. Model Files

flat directory modelだった際のファイル構成は以下を想定しています。
(throughについては後続で修正します。)

```app/models/organization.rb
class Organization < ApplicationRecord
    has_many :organization_users
end
```

```app/models/organization/user.rb
class Organization::User
    belongs_to :organization
    belongs_to :user
end
```

```app/modles/user.rb
class User < ApplicationRecord
  has_many :organization_users
end
```

## 2. 修正

### 2-0. 現状のModel構成を動かす

<details>
<summary> Oragnization Modelから参照した場合のエラー </summary>

```terminal
% rails c
api(dev)> Organization.first.organization_users
Organization Load (0.9ms)  SELECT `organizations`.* FROM `organizations` ORDER BY `organizations`.`id` ASC LIMIT 1
(api):28:in `<main>': Missing model class OrganizationUser for the Organization#organization_users association. You can specify a different model class with the :class_name option. (NameError)

            raise NameError.new(message, name)
            ^^^^^
/usr/local/bundle/gems/activerecord-7.2.1.1/lib/active_record/inheritance.rb:266:in `compute_type': uninitialized constant Organization::OrganizationUser (NameError)

            raise NameError.new("uninitialized constant #{candidates.first}", candidates.first)
```
</details>

<details>
<summary> User Modelから参照した場合のエラー </summary>

```terminal
% rails c
api(dev)> User.first.organization_users
  User Load (1.4ms)  SELECT `users`.* FROM `users` ORDER BY `users`.`id` ASC LIMIT 1
(api):61:in `<main>': Missing model class OrganizationUser for the User#organization_users association. You can specify a different model class with the :class_name option. (NameError)

            raise NameError.new(message, name)
            ^^^^^
/usr/local/bundle/gems/activerecord-7.2.1.1/lib/active_record/inheritance.rb:266:in `compute_type': uninitialized constant User::OrganizationUser (NameError)

            raise NameError.new("uninitialized constant #{candidates.first}", candidates.first)
            ^^^^^
```

</details>


### 2-1. nested classの参照を解決して、Organization ModelからUser Modelにアクセスする可能にする

#### 2-1-1. 参照の解決

`class Organization`から`class Organization::User`を参照する際には、nestedになっているため、`has_many :organization_users`ではなく、`has_many :users`を利用して参照をする。

```models/Organization.rb
class Organization < ApplicationRecord
    has_many :users
end
```

```terminal
% rails c
api(dev)> Organization.first.users
  Organization Load (1.3ms)  SELECT `organizations`.* FROM `organizations` ORDER BY `organizations`.`id` ASC LIMIT 1
  Organization::User Load (0.3ms)  SELECT `organization_users`.* FROM `organization_users` WHERE `organization_users`.`organization_id` = 1 /* loading for pp */ LIMIT 11
=> []
```

#### 2-1-2. ModelのRelationを利用して、Userにアクセスする

現状の`has_many :users`で、`organization_users`にアクセスする形式の場合は、認識齟齬が発生しやすいので、usersの場合にusers tableを取得するように変更を実施します。


```app/models/organization.rb
class Organization < ApplicationRecord
    has_many :organization_users, class_name: "Organization::User"
    has_many :users, through: :organization_users
end
```

<details>
<summary>[補足]models/oragnization.rbの場合だとuserの循環参照が起きる</summary>

```terminal
Organization.first.users
  Organization Load (1.2ms)  SELECT `organizations`.* FROM `organizations` ORDER BY `organizations`.`id` ASC LIMIT 1
  Organization::User Load (2.3ms)  SELECT `organization_users`.* FROM `organization_users` INNER JOIN `organization_users` `organization_users_users` ON `organization_users`.`id` = `organization_users_users`.`user_id` WHERE `organization_users_users`.`organization_id` = 1 /* loading for pp */ LIMIT 11
=>
[#<Organization::User:0x0000ffff68ede618
  id: 1,
  organization_id: 1,
  user_id: 1,
  created_at:
   "2024-11-12 05:06:34.040329000 +0000",
  updated_at:
   "2024-11-12 05:06:34.040329000 +0000">]
```

</details>

`belongs_to: :user`の参照先は、`Organization -> Organization::User`が参照先になってしまうため、`User Model`にアクセスさせる。
`::`を入れて、参照先を`User(models/user.rb)`に参照先を変更する。

```app/models/organization/user.rb
class Organization::User < ApplicationRecord
    belongs_to :organization
    belongs_to :user, class_name: "::User"
end
```

```terminal
% rails c
api(dev)> Organization.first.users
  Organization Load (2.9ms)  SELECT `organizations`.* FROM `organizations` ORDER BY `organizations`.`id` ASC LIMIT 1
  User Load (0.5ms)  SELECT `users`.* FROM `users` INNER JOIN `organization_users` ON `users`.`id` = `organization_users`.`user_id` WHERE `organization_users`.`organization_id` = 1 /* loading for pp */ LIMIT 11
=>
[#<User:0x0000ffff63e246a0
  id: 1,
  name: "user",
  created_at:
   "2024-11-12 04:04:25.961929000 +0000",
  updated_at:
   "2024-11-12 04:04:25.961929000 +0000">]
```

### 2-2. nested classの参照を解決して、User ModelからOrganization Modelにアクセスする可能にする

#### 2-2-1. 参照の解決

```models/user.rb
class User < ApplicationRecord
    has_many :organization_users, class_name: "Organization::User"
end
```

#### 2-2-2. Modelを利用して、UserからOrganizationに接続する

```models/user.rb
class User < ApplicationRecord
    has_many :organization_users, class_name: "Organization::User"
    has_many :organizations, through: :organization_users
end
```

```
User.first.organizations
  User Load (6.7ms)  SELECT `users`.* FROM `users` ORDER BY `users`.`id` ASC LIMIT 1
  Organization Load (1.0ms)  SELECT `organizations`.* FROM `organizations` INNER JOIN `organization_users` ON `organizations`.`id` = `organization_users`.`organization_id` WHERE `organization_users`.`user_id` = 1 /* loading for pp */ LIMIT 11
=>
[#<Organization:0x0000ffff63ed1cd8
  id: 1,
  name: "organization",
  created_at:
   "2024-11-12 04:04:10.864765000 +0000",
  updated_at:
   "2024-11-12 04:04:10.864765000 +0000">]
```

## 3. 結果

```app/models/organization.rb
class Organization < ApplicationRecord
    has_many :organization_users, class_name: "Organization::User"
    has_many :users, through: :organization_users
end
```

```app/models/organization/user.rb
class Organization::User < ApplicationRecord
    belongs_to :organization
    belongs_to :user, class_name: "::User"
end
```

```app/models/user.rb
class User < ApplicationRecord
    has_many :organization_users, class_name: "Organization::User"
    has_many :organizations, through: :organization_users
end
```