Пламен обнови решението на 16.01.2013 00:34 (преди около 12 години)
+require 'bigdecimal'
+require 'bigdecimal/util'
+
+class ExchangeRate
+ class Unknown < RuntimeError
+ end
+
+ def initialize
+ @rates = {}
+ end
+
+ def set(from_currency, to_currency, rate)
+ if from_currency == to_currency then return end
+
+ @rates[from_currency] ||= {}
+ @rates[from_currency][to_currency] = rate
+ @rates[to_currency] ||= {}
+ @rates[to_currency][from_currency] = 1 / rate
+ end
+
+ def get(from_currency, to_currency)
+ if from_currency == to_currency then return 1.to_d end
+
+ from_rates = @rates[from_currency]
+ return nil if !from_rates
+
+ from_rates[to_currency]
+ end
+
+ def convert(from_currency, to_currency, amount)
+ rate = get(from_currency, to_currency)
+ raise Unknown if !rate
+
+ rate * amount
+ end
+end
+
+class Money
+ include Comparable
+ attr_reader :amount, :currency
+
+ class IncompatibleCurrencies < RuntimeError
+ end
+
+ 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 *(number)
+ if !number.is_a? Numeric then raise ArgumentError end
+
+ Money.new @amount * number, @currency
+ end
+
+ def /(number)
+ if !number.is_a? Numeric then raise ArgumentError end
+
+ Money.new @amount / number, @currency
+ end
+
+ def +(other)
+ if !other.is_a? Money then raise ArgumentError end
+ if @currency != other.currency then raise IncompatibleCurrencies end
+
+ Money.new @amount + other.amount, @currency
+ end
+
+ def -(other)
+ if !other.is_a? Money then raise ArgumentError end
+ if @currency != other.currency then raise IncompatibleCurrencies end
+
+ Money.new @amount - other.amount, @currency
+ end
+
+ def <=>(other)
+ if !other.is_a? Money then raise ArgumentError end
+ if @currency != other.currency then raise IncompatibleCurrencies end
+
+ @amount <=> other.amount
+ end
+
+ def to_s
+ "%.2f %s" % [@amount, @currency]
+ end
+end