-
Notifications
You must be signed in to change notification settings - Fork 619
/
memory.rb
118 lines (108 loc) · 3.8 KB
/
memory.rb
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
class Money
module RatesStore
# Class for thread-safe storage of exchange rate pairs.
# Used by instances of +Money::Bank::VariableExchange+.
#
# @example
# store = Money::RatesStore::Memory.new
# store.add_rate 'USD', 'CAD', 0.98
# store.get_rate 'USD', 'CAD' # => 0.98
# # iterates rates
# store.each_rate {|iso_from, iso_to, rate| puts "#{from} -> #{to}: #{rate}" }
class Memory
INDEX_KEY_SEPARATOR = '_TO_'.freeze
# Initializes a new +Money::RatesStore::Memory+ object.
#
# @param [Hash] opts Optional store options.
# @option opts [Boolean] :without_mutex disables the usage of a mutex
# @param [Hash] rt Optional initial exchange rate data.
def initialize(opts = {}, rt = {})
@options, @index = opts, rt
@mutex = Mutex.new
@in_transaction = false
end
# Registers a conversion rate and returns it. Uses +Mutex+ to synchronize data access.
#
# @param [String] currency_iso_from Currency to exchange from.
# @param [String] currency_iso_to Currency to exchange to.
# @param [Numeric] rate Rate to use when exchanging currencies.
#
# @return [Numeric]
#
# @example
# store = Money::RatesStore::Memory.new
# store.add_rate("USD", "CAD", 1.24515)
# store.add_rate("CAD", "USD", 0.803115)
def add_rate(currency_iso_from, currency_iso_to, rate)
transaction { index[rate_key_for(currency_iso_from, currency_iso_to)] = rate }
end
# Retrieve the rate for the given currencies. Uses +Mutex+ to synchronize data access.
# Delegates to +Money::RatesStore::Memory+
#
# @param [String] currency_iso_from Currency to exchange from.
# @param [String] currency_iso_to Currency to exchange to.
#
# @return [Numeric]
#
# @example
# store = Money::RatesStore::Memory.new
# store.add_rate("USD", "CAD", 1.24515)
#
# store.get_rate("USD", "CAD") #=> 1.24515
def get_rate(currency_iso_from, currency_iso_to)
transaction { index[rate_key_for(currency_iso_from, currency_iso_to)] }
end
def marshal_dump
[self.class, options, index]
end
# Wraps block execution in a thread-safe transaction
def transaction(&block)
if @in_transaction || options[:without_mutex]
block.call self
else
@mutex.synchronize do
@in_transaction = true
result = block.call
@in_transaction = false
result
end
end
end
# Iterate over rate tuples (iso_from, iso_to, rate)
#
# @yieldparam iso_from [String] Currency ISO string.
# @yieldparam iso_to [String] Currency ISO string.
# @yieldparam rate [Numeric] Exchange rate.
#
# @return [Enumerator]
#
# @example
# store.each_rate do |iso_from, iso_to, rate|
# puts [iso_from, iso_to, rate].join
# end
def each_rate(&block)
enum = Enumerator.new do |yielder|
index.each do |key, rate|
iso_from, iso_to = key.split(INDEX_KEY_SEPARATOR)
yielder.yield iso_from, iso_to, rate
end
end
block_given? ? enum.each(&block) : enum
end
private
attr_reader :index, :options
# Return the rate hashkey for the given currencies.
#
# @param [String] currency_iso_from The currency to exchange from.
# @param [String] currency_iso_to The currency to exchange to.
#
# @return [String]
#
# @example
# rate_key_for("USD", "CAD") #=> "USD_TO_CAD"
def rate_key_for(currency_iso_from, currency_iso_to)
[currency_iso_from, currency_iso_to].join(INDEX_KEY_SEPARATOR).upcase
end
end
end
end