Rails に Concerns を追加して発生した `TypeError: superclass mismatch for XXX` の解決

## 原因

`superclass mismatch for class Product (TypeError)`

Concern を追加したら、Production 環境でエラーが発生

問題のコードが下記のようになっており、

```ruby:app/models/product.rb
class Product < ApplicationRecord
  include Register

  ...
end
```

```ruby:app/models/concerns/register.rb
class Product
  module Register
    extend ActiveSupport::Concern

    ...
  end
end
```

Concern の方が先に require/load されてしまうと、下記のようなコードを実行したのと同じ状況になり、`TypeError` が発生してしまう。

**注意:**そもそもの問題として、model に依存している concern だからという理由で、class 名の名前空間に concern を置いているのが原因で、それ自体を見直した方がいい

```ruby
class Product
end
class Product < ApplicationRecord
end
```

参考: [クラス／メソッドの定義 \(Ruby 2\.5\.0\)](https://docs.ruby-lang.org/ja/latest/doc/spec=2fdef.html#class)

## 解決策

もっとも素直なやり方としては、Concern にも super class `ApplicationRecord` を書いてしまう。

```ruby:app/models/concerns/register.rb
class Product < ApplicationRecord
  module Register
    extend ActiveSupport::Concern

    ...
  end
end
```

でも、これは２度定義している感じが気持ちが悪い。
そもそも２度定義しているのが問題なので、下記のように修正して見た

```ruby:app/models/concerns/register.rb
module Product::Register
  extend ActiveSupport::Concern

  ...
end
```

```shell-session
$ RAILS_ENV=production rails c
/project/vendor/bundle/ruby/2.5.0/gems/activesupport-5.2.0/lib/active_support/dependencies.rb:500:in `load_missing_constant': Circular dependency detected while autoloading constant Product::Register (RuntimeError)
	from /project/vendor/bundle/ruby/2.5.0/gems/bootsnap-1.3.0/lib/bootsnap/load_path_cache/core_ext/active_support.rb:43:in `load_missing_constant'
	from /project/vendor/bundle/ruby/2.5.0/gems/activesupport-5.2.0/lib/active_support/dependencies.rb:193:in `const_missing'
	from /project/app/models/product.rb:48:in `<class:Product>'
	from /project/app/models/product.rb:47:in `<main>'
...
```

今度は Concern ロードしようとしたら、Model ロードする必要が出たのでロードしたら、Model で include してる Concern ロードする必要が出たので…というループに入ってしまった様子

Concern からではなく、Model からロードした場合には class 定義を終えた後で Concern をロードしようとするので、この矛盾は発生しなくなるので、事前に Model をロードするようにして見た。

```ruby:config/initializers/requirements.rb
require 'product'
```

これで一応解決。

~~でも、なんとなく require そのまま書くのが違う気がしたので（今後同じことが起きる可能性あるし）、~~

`require` してしまうと、開発環境での `reload!` などが効かなくなってしまい、
spring 実行環境下や、`rails server` 実行中に concerns 以下のファイルの編集を行ったりすると、`A copy of XXX has been removed from the module tree but is still active!` などと怒られてしまうことがあります。

なので、`app/models/concerns` 直下のディレクトリ名を元にクラス名を `eval` して、model class を autoload してしまうことにしました。

```ruby:config/initializers/requirements.rb
# Concern を利用するモデルを事前に autoload します。
# TODO: バグで動かない為、後半のコードを利用(ruby 2.6 以降なら動くはず)
# @see https://bugs.ruby-lang.org/issues/14899
# Rails.root.join('app/models/concerns').glob('**/*/').each do |p|
#   dir_name = %r{/app/models/concerns/(.+?)/?$}.match(p.to_s)&.captures&.first
#   Object.instance_eval(dir_name.camelize) if dir_name
# end

Dir.glob("#{Rails.root}/app/models/concerns/**/*/").each do |p|
  dir_name = %r{#{Rails.root}/app/models/concerns/(.+?)/?$}.match(p.to_s)&.captures&.first
  Object.instance_eval(dir_name.camelize) if dir_name
end
```

もし、モデル名以外のディレクトリを置いたとしてもディレクトリ名と同じ class/module があれば基本的には大丈夫なはず。

## 参考

- [warning: toplevel constant/RuntimeError: Circular dependency detectedエラー \| EasyRamble](https://easyramble.com/warning-toplevel-constant-with-rails.html)
- [定数の自動読み込みと再読み込み \| Rails ガイド](https://railsguides.jp/constant_autoloading_and_reloading.html#%E5%AE%9A%E6%95%B0%E3%81%8C%E3%83%88%E3%83%AA%E3%82%AC%E3%83%BC%E3%81%95%E3%82%8C%E3%81%AA%E3%81%84%E5%A0%B4%E5%90%88)