Георги обнови решението на 15.01.2013 00:38 (преди около 12 години)
+require 'bigdecimal'
+require 'bigdecimal/util'
+
+class Rate
+ attr_accessor :rate
+
+ def initialize(from, to, rate)
+ @currencies = [] << from << to
+ @rate = rate
+ end
+
+ def is_between?(first, second)
+ @currencies.include? first and @currencies.include? second
+ end
+
+ def convert(currency, amount)
+ if @currencies[0] == currency
+ @rate * amount
+ elsif @currencies[1] == currency
+ (1 / @rate) * amount
+ end
+ end
+
+ def get_rate(from, to)
+ return false unless is_between?(from, to)
+ @currencies[0] == from ? @rate : 1 / @rate
+ end
+end
+
+class ExchangeRate
+ def initialize
+ @rates = []
+ end
+
+ def set(from, to, rate)
+ return nil if from == to
+ if find_rate(from, to).nil? then @rates << Rate.new(from, to, rate)
+ else find_rate(from, to).rate = rate
+ end
+ end
+
+ def get(from, to)
+ return 1.to_d if from == to
+ rate = find_rate(from, to)
+ rate.nil? ? rate : rate.get_rate(from, to)
+ end
+
+ def convert(from, to, amount)
+ return amount if from == to
+ rate = find_rate(from, to)
+ raise Unknown if rate.nil?
+ rate.convert(from, amount)
+ end
+
+ private
+
+ def find_rate(from, to)
+ @rates.each { |rate| return rate if rate.is_between?(from, to) }
+ nil
+ end
+
+ class Unknown < RuntimeError
+ end
+end
+
+class Money
+ attr_accessor :amount, :currency
+
+ def initialize(amount, currency)
+ @amount, @currency = amount, currency
+ end
+
+ def to_s
+ "#{"%.2f" % amount.to_f} #{currency.to_s}"
+ end
+
+ def *(other)
+ if other.kind_of? Numeric
+ Money.new (@amount * other), @currency
+ end
+ end
+
+ def /(other)
+ if other.kind_of? Numeric
+ Money.new (@amount / other), @currency
+ end
+ end
+
+ def ==(other)
+ type_check(other)
+
+ @amount == other.amount
+ end
+
+ def method_missing(method, *args)
+ p "here"
+ type_check(args[0]) if [:+, :-, :<=>, :<, :<=, :>, :>=].include? method
+
+ if [:+, :-].include?(method) then Money.new @amount.send(method, args[0].amount), @currency
+ elsif [:<=>, :<, :<=, :>, :>=].include?(method) then @amount.send(method, args[0].amount)
+ else super
+ end
+ end
+
+ def in(new_currency, exchange_rate)
+ new_amount = exchange_rate.convert(@currency, new_currency, @amount)
+ Money.new new_amount.to_d, new_currency
+ end
+
+ private
+
+ def type_check(other)
+ raise ArgumentError if other.class != self.class
+ raise IncompatibleCurrencies if currency != other.currency
+ end
+
+ class IncompatibleCurrencies < Exception
+ end
+end