あるモデルのサブクラスを定義する

サブクラスの作成等を、 Rails が受け持ってくれる機能について。ファイルのアップロードなど、場合によって validation の条件を分けたりしたい時などにも便利です。

前提

Rails の機能で簡単にサブクラスを作成する場合、親クラスに type というカラムを string 型で定義します。

# RAILS_ROOT/bd/migrate/XXX_create_users.rb
class CreateUsers < ActiveRecord::Migration
  def self.up
    create_table :users do |t|
      t.string :login
      t.string :password
      t.string :type
      t.timestamps
    end
  end

  def self.down
    drop_table :users
  end
end

これだけで、サブクラスの実装を Rails 側で自動的に行ってくれます。

サブクラスの定義

サブクラスの定義は、通常の Ruby の文法で行います。

# RAILS_ROOT/app/models/user.rb
class User < ActiveRecord::Base
end
# RAILS_ROOT/app/models/super_user.rb
class SuperUser < User
end

これで SuperUser が User のサブクラスになりました。

使用方法

上のように定義しておくと、 find などを用いる際にサブクラスを指定する事で、自動的にサブクラスのみから検索を行うよう Rails が処理してくれます。

# すべてのスーパーユーザを検索する
@super_users = SuperUser.find(:all)

# すべてのユーザを検索する
@users = User.find(:all)

# スーパーユーザを新規作成(type カラムに "SuperUser" が自動入力される)
@new_super_user = SuperUser.new

# ユーザを新規作成(type カラムは設定されない)
@new_user = User.new

コードにも書きましたが、親クラスを new した場合 type カラムには値が代入されないので注意して下さい。
また、サブクラス間でインスタンスを直接代入する事もできません。

# RAILS_ROOT/app/models/another_user.rb
class AnotherUser < User
end
@another_user = AnotherUser.find(params[:id])
# これはエラーになる
@super_user = SuperUser.new(@another_user)

このような場合、一つ一つの値を代入していくのは馬鹿らしいので、手動で type に値を代入する方法などが考えられます。

# User として find してくる
@another_user = User.find(params[:id])
# こちらも User として新規作成して
@super_user = User.new(@another_user)
# type を設定してしまう
@super_user.update_attribute(:type, "SuperUser")