Railsでネームスペース付きのモデルでテーブル名にプレフィックスをつける


タイトルの通りですが、具体的には以下のコマンドでモデルをgenerateしてあげれば、実現できます。
Admin::User というモデルで admin_users というテーブルを作りたい場合、以下のコマンドで作成できます。

bundle exec rails g model Admin::User email password flag

実行結果としてこれらのファイルが生成されます
      invoke  active_record
      create    db/migrate/20160202020202_create_admin_users.rb
      create    app/models/admin/user.rb
      create    app/models/admin.rb
      invoke    rspec
      create      spec/models/admin/user_spec.rb
app/models/admin.rb
module Admin
  def self.table_name_prefix
    'admin_'  #<= DBのtable名にadmin_というプレフィックスがあることが前提
  end
end
app/models/admin/user.rb
class Admin::User < ActiveRecord::Base
end

app/models/admin.rbtable_name_prefix というクラスメソッドをオーバーライドしています。

Railsではなぜかネームスペースとなる親のModuleに定義してあげると table_name_prefix が有効な状態となります。
ネームスペースとなっているModuleにメソッドを定義するだけで、そのメソッドが実行できるわけではないので気になって詳しく調べてみました。
通常Rubyだと self.hoge などで始まる 特異メソッドextend しないとダメです。

以下のActiveRecordのソースにコメントで書かれていました。

activerecord/lib/active_record/model_schema.rb#L25-L27
      # If you are organising your models within modules you can add a prefix to the models within
      # a namespace by defining a singleton method in the parent module called table_name_prefix which
      # returns your chosen prefix.

意訳すると以下の感じでしょうか。

まとめたい(organisingしたい)ときはネームスペース(parent)となっている Admin のモジュールに定義することで可能。

実装コードは以下。parents で親のモジュールに table_name_prefix の定義があるかを判定しあれば返すという実装になっていました。

activerecord/lib/active_record/model_schema.rb#L151-L153
      def full_table_name_prefix #:nodoc:
        (parents.detect{ |p| p.respond_to?(:table_name_prefix) } || self).table_name_prefix
      end