ActiveRecordモデルのカスタムバリデーションメソッドを共有したいんです

委譲でなく。エラーはerrorsに書き込みたいから……と考えるとバリデーションの委譲はたぶん面倒臭い。

だからMixinしてやる。こういうやり口ってRailsのベストプラクティスでまとめられていたりしそう。

ruby 1.9.2p290 (2011-07-09) [i386-mingw32]
activerecord (3.1.0)

Departmentに、manager_idに関するカスタムバリデーションメソッドsampleがあるとする。

class Department < ActiveRecord::Base
  self.primary_key = :department_id
  belongs_to :location
  has_many :employees
  has_many :job_histories

  def manager
    Employee.where(employee_id: manager_id)
  end

  validate :sample

  def sample
    if manager_id <= 100
      errors[:manager_id] = 'manager_id must be greater than 100'
    end
  end
end

dept = Department.new
dept.manager_id = 99
p dept.valid? ? 'correct' : dept.errors.full_messages
# => ["Manager manager_id must be greater than 100"]

おなじことを他のモデルでもやりたい場合、この例だとしょぼいから何とでもなりそうだが、バリデーション内容が複雑になればなるほどメソッドを共用したくなる。

そしたらModuleを作ってincludeしてしまえばよい。

class Department < ActiveRecord::Base
  self.primary_key = :department_id
  belongs_to :location
  has_many :employees
  has_many :job_histories

  def manager
    Employee.where(employee_id: manager_id)
  end

  validate :sample
  module CustomValidateModule
    def sample
      if manager_id <= 100
        errors[:manager_id] = 'manager_id must be greater than 100'
      end
    end
  end
  include CustomValidateModule
end

class Employee < ActiveRecord::Base
  self.primary_key = :employee_id
  belongs_to :job
  belongs_to :department
  has_many :job_histories

  def manager
    Employee.where(employee_id: manager_id)
  end
  def members
    Employee.where(manager_id: employee_id)
  end

  validate :sample
  include Department::CustomValidateModule
end

dept = Department.new
dept.manager_id = 99
p dept.valid? ? 'correct' : dept.errors.full_messages
# => ["Manager manager_id must be greater than 100"]

emp = Employee.new
emp.manager_id = 99
p emp.valid? ? 'correct' : emp.errors.full_messages
# => ["Manager manager_id must be greater than 100"]

(^ω^)にこっ