Кирчо обнови решението на 12.01.2013 20:47 (преди почти 12 години)
+require 'bigdecimal'
+require 'bigdecimal/util'
+
+class CurrencyRate
+ attr_accessor :currency, :rate
+
+ def initialize(currency, rate)
+ @currency = currency
+ @rate = rate
+ end
+
+ def ==(other)
+ self.currency == other.currency
+ end
+
+ alias eql? ==
+
+ def hash
+ @currency.hash
+ end
+end
+
+class ExchangeRate
+ def initialize
+ @exchange = Hash.new { |hash, key| hash[key] = [CurrencyRate.new(key, '1'.to_d)] }
+ end
+
+ def set(from_currency, to_currency, rate)
+ if from_currency != to_currency
+ set_exchange_rate from_currency, to_currency, rate
+ set_exchange_rate to_currency, from_currency, 1 / rate
+ end
+ end
+
+ def get(from_currency, to_currency)
+ currency_rate = @exchange[from_currency].detect do |single_rate|
+ single_rate.currency == to_currency
+ end
+ currency_rate ? currency_rate.rate : nil
+ end
+
+ def convert(from_currency, to_currency, amount)
+ rate = get from_currency, to_currency
+ rate ? amount * rate : (raise Unknown.new)
+ end
+
+ private
+
+ def set_exchange_rate(from_currency, to_currency, rate)
+ @exchange[from_currency] =
+ [CurrencyRate.new(to_currency, rate)] | @exchange[from_currency]
+ end
+
+ class Unknown < RuntimeError
+ end
+end
+
+class Money
+ include Comparable
+ attr_reader :amount, :currency
+
+ def initialize(amount, currency)
+ @amount = amount
+ @currency = currency
+ end
+
+ def to_s
+ "%.2f #{currency}" % amount
+ end
+
+ def in(currency, exchange_rate)
+ Money.new exchange_rate.convert(@currency, currency, @amount), currency
+ end
+
+ def +(other)
+ compatible_money other
+ Money.new self.amount + other.amount, @currency
+ end
+
+ def <=>(other)
+ compatible_money other
+ self.amount <=> other.amount
+ end
+
+ def -@
+ Money.new -@amount, @currency
+ end
+
+ def -(other)
+ self + (-other)
+ end
+
+ def *(other)
+ calculate_if_numeric :mult, other
+ end
+
+ def /(other)
+ calculate_if_numeric :div, other
+ end
+
+ private
+
+ def calculate_if_numeric(operation, other)
+ raise ArgumentError unless other.is_a? Numeric
+ Money.new @amount.send(operation, other, 10), @currency
+ end
+
+ def compatible_money(other)
+ raise ArgumentError unless other.is_a? Money
+ raise IncompatibleCurrencies.new unless self.currency == other.currency
+ end
+
+ class IncompatibleCurrencies < ArgumentError
+ end
+end