不關聯到資料庫的model
2014-11-24 12:39:48

問題

最近處理一些使用simple form的頁面遇到了一個問題。有一個頁面有兩個email input的欄位,要使用者輸入兩次email,rails這邊要做email格式與email確認的檢查。我們知道如果要讓simple form支援錯誤顯示,則simple_form必須要跟一個model做關連,但以這個例子而言,這個model只有兩個attribute - email與email_confirmation,很明顯不會跟database有關係,但又要支援model的validation,所以要想辦法做一個不關聯到資料庫的model。

解法

下面是一個範例:

class ExtraauthMail
  include ActiveModel::Validations
  include ActiveModel::Conversion
  extend ActiveModel::Naming
  extend ActiveModel::Translation

  attr_accessor :email, :email_confirmation
  attr_reader   :errors

  validates :email, presence: true,
                    confirmation: true,
                    format: { with: /\A[-a-z0-9_+\.]+\@([-a-z0-9]+\.)+[a-z0-9]{2,4}\z/i }
  validates :email_confirmation, presence: true
  validate :check_duplicated_email

  def initialize(params = {})
    @email = params[:email]
    @email_confirmation = params[:email_confirmation]
    @errors = ActiveModel::Errors.new(self)
  end

  def check_duplicated_email
    if @email.present? && User.where(email: @email).take
      @errors.add(:email, I18n.t("activemodel.errors.models.extraauth_mail.duplicated"))
    end
  end
end

在網路上有許多類似的topic,例如在code school中的rails4 pattern也有提到怎麼做這種model。不過在實際上還是會遇到很多坑,在這裡記錄一下:

  • 如果要支援validation,除了要加上include ActiveModel::Validations之外,記得還要加attr_reader :errors,並且在initialize中初始化它。
  • 理論上支援confiramtion的validation,應該不用自已加attr_accessor :email_confirmation,但實際測試還是要加,不然confirmation會失效。
  • 預設的translation路徑需要改變,從activerecord變成activemodel。
zh-TW:
  activemodel:
    errors:
      models:
        extraauth_mail:
          blank: "請輸入您的email。"
          confirmation: "您的email不一致。"
          invalid: "您的email格式不正確。"
          duplicated: "您輸入的email已經註冊過了。"