0

私のフォームには、混合数 (38 1/2 など) を受け入れて小数に変換できる仮想属性があります。何かが爆発した場合にエラーをスローする検証もいくつかあります (これを正しく処理しているかどうかはわかりません)。

class Client < ActiveRecord::Base
  attr_accessible :mixed_chest

  attr_writer :mixed_chest

  before_save :save_mixed_chest

  validate :check_mixed_chest

  def mixed_chest
    @mixed_chest || chest
  end

  def save_mixed_chest
    if @mixed_chest.present?
      self.chest = mixed_to_decimal(@mixed_chest)
    else
       self.chest = ""
    end
  end

  def check_mixed_chest
    if @mixed_chest.present? && mixed_to_decimal(@mixed_chest).nil?
      errors.add :mixed_chest, "Invalid format. Try 38.5 or 38 1/2"
    end
  rescue ArgumentError
    errors.add :mixed_chest, "Invalid format. Try 38.5 or 38 1/2"
  end

  private

  def mixed_to_decimal(value)
    value.split.map{|r| Rational(r)}.inject(:+).to_d
  end

end 

ただし、仮想属性を持つウィングスパンという別の列を追加したいのですが、これ:mixed_wingspanを抽象化して再利用する方法がわかりません。数十の入力に対して同じ変換/検証を使用します。

accept_mixed :chest, :wingspan ...理想的には、カスタムゲッター、セッター、検証などを処理するようなものを使用したいと思います.

編集:

メタプログラミングで機能を再作成しようとしていますが、いくつかの場所で苦労しています:

def self.mixed_number(*attributes)
  attributes.each do |attribute|
    define_method("mixed_#{attribute}") do
      "@mixed_#{attribute}" || attribute
    end
  end
end

mixed_number :chest

これにより、チェストが「@mixed_chest」に設定されます。@mixed_chest上記のようにインスタンス変数を取得しようとしています。

4

1 に答える 1

1

カスタムバリデーターが必要になるでしょう

何かのようなもの

class MixedNumberValidator < ActiveModel::EachValidator
  def validate_each(record, attribute, value)
    if value.present? && MixedNumber.new(value).to_d.nil?
      record.errors[attribute] << (options[:message] || "Invalid format. Try 38.5 or 38 1/2")
    end
  end
end

それからあなたはすることができます

validates :chest, mixed_number: true

mixed_to_decimalものを別のクラスに抽出することに注意してください

class MixedNumber
  def initialize(value)
    @value = value
  end

  def to_s
    @value
  end

  def to_d
    return nil if @value.blank?
    @value.split.map{|r| Rational(r)}.inject(:+).to_d
  rescue ArgumentError
    nil
  end
end

この定義により、メソッドif内のステートメントを削除できます。save_chest

他の質問への回答で提案したように、メタプログラミングを実行してすべてを実行する必要があります。あなたは基本的に次のようなものが欲しいでしょう

def self.mixed_number(*attributes)
  attributes.each do |attribute|
    define_method("mixed_#{attribute}") do
      instance_variable_get("@mixed_#{attribute}") || send(attribute)
    end

    attr_writer "mixed_#{attribute}"

    define_method("save_mixed_#{attribute}") do
      # exercise for the reader ;)
    end

    before_save "save_#{attribute}"
    validates "mixed_#{attribute}", mixed_number: true
  end
end

mixed_number :chest, :waist, :etc
于 2012-12-12T23:16:55.380 に答える