ActiveRecord / Rails / Ruby

ActiveRecordモデルのコードはこんな感じです。

# app/models/blog/site.rb
class Blog::Site < ActiveRecord::Base
  validates :title, presence: true
  validates :description, presence: true
  validates_with Blog::SiteDescriptionSignificanceValidator

  before_save Blog::SiteLanguageEstimator.new(:language)

  def rating
    @rating ||= Blog::SiteRating.from_description(description)
  end
end

ここでは

  • カスタムValidatorでdescriptionの内容をチェックする
  • Callbackクラスでlanguageを設定するbefore_saveを設定する
  • Valueオブジェクトでratingを扱う

ということを行っています。

以下でそれぞれについて簡単に説明します。

カスタムValidatorクラス

カスタムValidatorではdescriptionの内容が「ひらがなを含んで10文字以上」か「ひらがなを含まずに3単語以上」となっていることを確認します。

# app/validators/blog/site_description_significance_validator.rb
class Blog::SiteDescriptionSignificanceValidator < ActiveModel::Validator
  def validate(record)
    description = record.description
    if description
      if /[ぁ-ゔ]/ =~ description
        return if description.length >= 10
      else
        return if description.split(/[\s,\.]+/).size >= 3
      end
    end
    record.errors.add :description, :not_significant
  end
end

Callbackクラス

Callbackではbefore_savelanguageの設定をしています。

今回の例ではひらがなを含んでいればlanguage:jaを設定します。

# app/callbacks/blog/site_language_estimator.rb
class Blog::SiteLanguageEstimator
  def initialize(attribute)
    @attribute = attribute
  end

  def before_save(record)
    return if record.send(@attribute)
    language = (/[ぁ-ゔ]/ =~ record.description ? :ja : '')
    record.send("#{@attribute}=", language)
  end
end

Valueオブジェクト

Valueオブジェクトではratingの比較を行えるようにしています。

# app/values/blog/site_rating.rb
class Blog::SiteRating
  include Comparable

  def self.from_description(description)
    description.length >= 10 ? new('A') : new('B')
  end

  def initialize(letter)
    @letter = letter
  end

  def better_than?(other)
    self > other
  end

  def <=>(other)
    other.to_s <=> to_s
  end

  def hash
    @letter.hash
  end

  def eql?(other)
    to_s == other.to_s
  end

  def to_s
    @letter.to_s
  end
end

参考文献

この記事を書くにあたって、CallbackクラスとValidatorについては

を、Valueオブジェクトについては

を参考にしました。

どちらもよくまとまっているのでオススメです。