0

データベースに小数として保存している多数のフィールドを含むフォームがあります。

ただし、ユーザーが分数、特に混合数 (例: 38 1/2) を入力できるようにしたいと考えています。

この回答は、混合数を分数に変換するための優れたソリューションを提供します。これを変更して、モデルの関数に入れました。

def to_dec
  self.chest = self.chest.split.map { |r| Rational(r) }.inject(:+).to_f
end

そして、私はそれをvalidate :to_decand で呼び出すことを実験しましbefore_save :to_decた。ただし、これらはどちらも機能しません。

理由はわかっていますが、それを修正する方法はありません。

を使用puts self.chestすることで、値を数値に変換する方法を微調整する前に、その値が数値に変換されていることを知ることができます。フォームに「10 1/2」と入力して保存するとputs self.chest、関数が次の場合でも 10.0 になります。

def to_dec
  puts self.chest
end

さらに、puts '38 1/2'.split.map { |r| Rational(r) }.inject(:+).to_d38.5 が表示されるので、変換に問題がないことがわかります。

ビューの入力タイプを数値から文字列に変更しましたが、モデルではまだ数値になっているようです。

私は立ち往生しています。

また、他のフィールドの一部も混合数をサポートする必要があります。値ごとに異なる関数を記述する必要がないように、何らかの方法でこれを抽象化したいと考えています。

4

1 に答える 1

1

その理由は、ActiveRecord がモデル属性を定義するときにデータベース スキーマを参照するためです。これは、単純な古い Ruby オブジェクトの場合のようにattr_accessor :chest(またはdef chestと)を使用して手動で定義することなく、すべての属性がモデル クラスに表示される方法です。def chest=

その際、ActiveRecord は列の型も調べ、属性を割り当てるときに型キャストを適用します。したがって、chest列は小数であるため、指定した文字列を浮動小数点数に変換します。そうすることで、変換可能な文字列の最初のビットを取得し、残りを破棄します38.0

いくつかの方法でこれを回避できます。数値を文字列としてデータベースに格納するだけで、いくつかの変換を節約できます (ただし、クエリで数値表現を使用する必要がある場合はあまり役に立ちません)。または、仮想属性を使用できます。

基本的に、次のようにゲッターとセッターを定義する必要があります。

def rational_chest
  # I assume this would create a rational from a float
  # and respond to `to_s` in a sane manner...
  Rational(chest) if chest
end

def rational_chest=(value)
  self.chest = parse_rational(value)
end

private

def parse_rational(value)
  return nil if value.blank?
  value.split.map{|r| Rational(r)}.inject(:+).to_f
end

次にrational_chest、単にではなくフォームで使用しますchest

多くのフィールドでこれを行う必要があるため、ちょっとした気の利いたメタプログラミングに頼ることができます。

def self.rational_attribute(attribute)
  define_method("rational_#{attribute}") do
    Rational(send(attribute))
  end

  define_method("rational_#{attribute}=") do |value|
    send "#{attribute}=", value.split.map{|r| Rational(r)}.inject(:+).to_f
  end
end

def self.rational_attributes(*attributes)
  attributes.each {|attribute| rational_attribute attribute}
end

rational_attributes :chest, :waist, :etc

これを複数のモデルで使用する場合は、モジュールに入れます ( self.s とextendit なしで!

于 2012-12-10T23:30:04.047 に答える