Мартин обнови решението на 10.01.2013 14:23 (преди около 12 години)
+require 'bigdecimal'
+require 'bigdecimal/util'
+
+class ExchangeRate
+ const_set("Unknown", Class.new(RuntimeError))
+
+ def initialize
+ @currency_hash = {}
+ end
+
+ def set(from_currency, to_currency, rate)
+ if from_currency != to_currency
+ @currency_hash[[from_currency,to_currency]] = rate
+ reverse_rate = 1/rate
+ @currency_hash[[to_currency, from_currency]] = reverse_rate
+ end
+ end
+
+ def get(from_currency, to_currency)
+ if from_currency != to_currency
+ @currency_hash[[from_currency,to_currency]]
+ else 1.to_d
+ end
+ end
+
+ def convert(from_currency, to_currency, amount)
+ rate = get from_currency, to_currency
+ rate ? rate * amount : (raise Unknown)
+ end
+end
+
+class Money
+ include Comparable
+ attr_accessor :amount, :currency
+
+ const_set("IncompatibleCurrencies", Class.new(ArgumentError))
+
+ def initialize(amount, currency)
+ @amount = amount
+ @currency = currency
+ end
+
+ def in(_currency, exchange_rate)
+ new_amount = exchange_rate.convert(@currency, _currency, @amount)
+ Money.new new_amount, _currency
+ end
+
+ def to_s
+ "#{@amount.round(2).to_s('F')} #{@currency}"
+ end
+
+ def <=>(other)
+ operation_with_other_money(:<=>, other)
+ end
+
+ def +(other)
+ Money.new operation_with_other_money(:+, other), currency
+ end
+
+ def -(other)
+ Money.new operation_with_other_money(:-, other), currency
+ end
+
+ def *(coefficient)
+ operation_with_numeric(:*,coefficient)
+ end
+
+ def /(coefficient)
+ operation_with_numeric(:/,coefficient)
+ end
+
+ private
+
+ def operation_with_numeric(operation, coefficient)
+ if coefficient.kind_of? Numeric
+ Money.new amount.send(operation, coefficient), currency
+ else
+ ArgumentError
+ end
+ end
+
+ def operation_with_other_money(operation, other)
+ if self.class != other.class
+ raise ArgumentError
+ elsif currency != other.currency
+ raise IncompatibleCurrencies
+ else
+ amount.send(operation, other.amount)
+ end
+ end
+end
Този const_set да го разкараш :)