通貨・お金の取り扱い方法としては、どのようなものがありますか?

非常に基本的なショッピングカートのシステムを作っています。

テーブルitemsには、integer型のprice列があります。

ユーロとセントの両方を含む価格の場合、ビューに価格の値を表示するのに苦労しています。Rails フレームワークでの通貨の扱いについて、何か明らかなことを見逃しているのでしょうか。

ソリューション

データベースでは、おそらく DECIMAL 型を使いたいと思うでしょう。マイグレーションでは、以下のようにします。

# precision is the total number of digits
# scale is the number of digits to the right of the decimal point
add_column :items, :price, :decimal, :precision => 8, :scale => 2

Railsでは、:decimal型はBigDecimalとして返され、価格計算にはもってこいです。

整数を使うことにこだわると、BigDecimalとの間でいたるところで手動で変換しなければならず、おそらく面倒なことになるでしょう。

mclさんが指摘されているように、価格を表示するには

number_to_currency(price, :unit => "€")
#=> €1,234.01
解説 (13)

ここでは、composed_of(ActiveRecordの一部で、ValueObjectパターンを使用)とMoney gemを活用した、素晴らしくシンプルなアプローチを紹介します。

次のものが必要です。

  • Money gem]1 (バージョン 4.1.0)
  • モデル(例:Product)。
  • モデル(およびデータベース)内の`integer列、例えば:priceなど

これを product.rb ファイルに記述します。

class Product > ActiveRecord::Base

  composed_of :price,
              :class_name => 'Money',
              :mapping => %w(price cents),
              :converter => Proc.new { |value| Money.new(value) }
  # ...

あなたが得られるもの

  • 何も変更しなくても、すべてのフォームでドルとセントが表示されるようになりますが、内部表現はセントのままです。フォームは "$12,034.95" のような値を受け入れ、変換してくれます。モデルに余分なハンドラや属性を追加したり、ビューにヘルパーを追加する必要はありません。
  • product.price = "$12.00"` は、自動的に Money クラスに変換されます。
  • product.price.to_s は、10進数で表示します ("1234.00")
  • product.price.format は、通貨に応じて適切にフォーマットされた文字列を表示します。
  • ペニーを必要とするペイメントゲートウェイに)セントを送る必要がある場合は、product.price.cents.to_sとします。
  • 無料の通貨変換
解説 (9)

通貨を扱う場合、一般的には10進数型を使用します。 以下は、"Agile Web Development with Rails&quotからの簡単な例です。

add_column :products, :price, :decimal, :precision => 8, :scale => 2 

これにより、-999,999.99から999,999.99までの価格を扱うことができます。 また、以下のようにアイテムにバリデーションを入れることもできます。

def validate 
  errors.add(:price, "should be at least 0.01") if price.nil? || price < 0.01 
end 

のようにアイテムにバリデーションを入れて、値の健全性をチェックすることもできます。

解説 (2)