Rails7.1.3 での table_name_prefix の挙動の違い

## Rails7.1.3へのアップデート

Rails7.1.3のアップデートの際に思わぬところで躓いたので共有です
Rails7.1.3で適用された、このPRについてです

https://github.com/rails/rails/pull/50247

**対象読者**

- Rails エンジンで `table_name_prefix` を自分で定義している人

## さきに結論

- `blorgh`というエンジンがあったとして `lib/blorgh.rb`で `table_name_prefix`を上書きしていた場合、Rails 7.1.3で効かなくなった
- `isolate_namespace`の呼び出し前に、`Blorgh`モジュールで`table_name_prefix`を上書きする

## 状況

### Rails7.1.2 までの環境

Railsエンジン側でモデルを用意しており、`table_name_prefix`を定義していたとします

`blorgh`というエンジンだとすると、下記のような感じ

```lib/blorgh.rb
require "blorgh/version"
require "blorgh/engine"

module Blorgh
  def self.table_name_prefix
    "custom_blorgh_"
  end
end
```

```lib/blorgh/engine.rb
module Blorgh
  class Engine < ::Rails::Engine
    isolate_namespace Blorgh
  end
end
```

`Blorgh::User`モデルを定義すると、テーブル名は以下のようになります

```bash
❯ bin/rails console
Loading development environment (Rails 7.1.2)
irb(main):001> Blorgh::User.table_name
=> "custom_blorgh_users"
```

### Rails 7.1.3 にアップデートします

アップデート後、テーブル名を確認してみます

```bash
❯ bin/rails console
Loading development environment (Rails 7.1.3)
irb(main):001> Blorgh::User.table_name
=> "blorgh_users"
```

なんと、、、 **プレフィックスが外れています**

Rails7.1.3でマージされた [Make isolated engines aware of ActiveRecord::Base table name prefix by chaadow ](https://github.com/rails/rails/pull/50247) の中で、新たに処理が追加されています

PRで変更が入った`Rails::Engine#isolate_namespace`の実装を見てみます

```engine.rb
unless mod.respond_to?(:table_name_prefix)
  define_method(:table_name_prefix) { "#{name}_" }

  ActiveSupport.on_load(:active_record) do
    mod.singleton_class.redefine_method(:table_name_prefix) do
      "#{ActiveRecord::Base.table_name_prefix}#{name}_"
    end
  end
end
```

[https://github.com/rails/rails/blob/v7.1.3/railties/lib/rails/engine.rb#L400-L404](https://github.com/rails/rails/blob/v7.1.3/railties/lib/rails/engine.rb#L400-L404)

つまり、rails engineのmoduleに`table_name_prefix`メソッドが定義されてなければ、再定義するようになっています

## 対応

なので、`Rails::Engine#isolate_namespace`の意図を汲み取りつつ、`isolate_namespace`を呼ぶ前に`table_name_prefix`を定義してみます

```diff_ruby:lib/blorgh.rb
require "blorgh/version"
require "blorgh/engine"

module Blorgh
-  def self.table_name_prefix
-    "custom_blorgh_"
-  end
end
```

```diff_ruby:lib/blorgh/engine.rb
module Blorgh
+  def self.table_name_prefix
+    "custom_blorgh_"
+  end

  class Engine < ::Rails::Engine
    isolate_namespace Blorgh
  end
end
```

とすると、

```bash
❯ bin/rails console
Loading development environment (Rails 7.1.3)
irb(main):001> Blorgh::Admin.table_name
=> "custom_blorgh_admins"
```

プレフィックスがちゃんとつきました

Railsのテストを参考）https://github.com/rails/rails/blob/main/railties/test/railties/engine_test.rb#L1283

もちろん、下記のように `application_record.rb`に定義して、継承する形でも問題ないでしょう

```models/blorgh/application_record.rb
module Blorgh
  def self.table_name_prefix
    "custom_blorgh_"
  end

  class ApplicationRecord < ActiveRecord::Base
    self.abstract_class = true
  end
end
```

Railsのテストモデルを参考) https://github.com/rails/rails/blob/main/activerecord/test/models/company_in_module.rb#L34

## まとめ

- `blorgh`というエンジンがあったとして `lib/blorgh.rb`で `table_name_prefix`を上書きしていた場合、Rails 7.1.3で効かなくなった
- `isolate_namespace`の呼び出し前に、`Blorgh`モジュールで`table_name_prefix`を上書きする

何気ないパッチアップデートでも引っかかったので、Railsのバージョンアップはやはり注意してみないといけないですね