ecr_money 3.6.5
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- data/CHANGELOG.md +309 -0
- data/LICENSE +21 -0
- data/README.md +209 -0
- data/lib/money/bank/base.rb +131 -0
- data/lib/money/bank/variable_exchange.rb +241 -0
- data/lib/money/core_extensions.rb +63 -0
- data/lib/money/currency.rb +415 -0
- data/lib/money/money.rb +1213 -0
- data/lib/money.rb +27 -0
- metadata +110 -0
@@ -0,0 +1,241 @@
|
|
1
|
+
require 'money/bank/base'
|
2
|
+
autoload :JSON, 'json'
|
3
|
+
autoload :YAML, 'yaml'
|
4
|
+
|
5
|
+
class Money
|
6
|
+
module Bank
|
7
|
+
# Thrown when an unknown rate format is requested.
|
8
|
+
class UnknownRateFormat < StandardError; end
|
9
|
+
|
10
|
+
# Class for aiding in exchanging money between different currencies. By
|
11
|
+
# default, the +Money+ class uses an object of this class (accessible
|
12
|
+
# through +Money#bank+) for performing currency exchanges.
|
13
|
+
#
|
14
|
+
# By default, +Money::Bank::VariableExchange+ has no knowledge about
|
15
|
+
# conversion rates. One must manually specify them with +add_rate+, after
|
16
|
+
# which one can perform exchanges with +#exchange_with+.
|
17
|
+
#
|
18
|
+
# @example
|
19
|
+
# bank = Money::Bank::VariableExchange.new
|
20
|
+
# bank.add_rate("USD", "CAD", 1.24515)
|
21
|
+
# bank.add_rate("CAD", "USD", 0.803115)
|
22
|
+
#
|
23
|
+
# c1 = 100_00.to_money("USD")
|
24
|
+
# c2 = 100_00.to_money("CAD")
|
25
|
+
#
|
26
|
+
# # Exchange 100 USD to CAD:
|
27
|
+
# bank.exchange_with(c1, "CAD") #=> #<Money @cents=1245150>
|
28
|
+
#
|
29
|
+
# # Exchange 100 CAD to USD:
|
30
|
+
# bank.exchange_with(c2, "USD") #=> #<Money @cents=803115>
|
31
|
+
class VariableExchange < Base
|
32
|
+
|
33
|
+
# Available formats for importing/exporting rates.
|
34
|
+
RATE_FORMATS = [:json, :ruby, :yaml]
|
35
|
+
|
36
|
+
# Setup rates hash and mutex for rates locking
|
37
|
+
#
|
38
|
+
# @return [self]
|
39
|
+
def setup
|
40
|
+
@rates = {}
|
41
|
+
@mutex = Mutex.new
|
42
|
+
self
|
43
|
+
end
|
44
|
+
|
45
|
+
# Exchanges the given +Money+ object to a new +Money+ object in
|
46
|
+
# +to_currency+.
|
47
|
+
#
|
48
|
+
# @param [Money] from The +Money+ object to exchange.
|
49
|
+
# @param [Currency, String, Symbol] to_currency The currency to exchange
|
50
|
+
# to.
|
51
|
+
#
|
52
|
+
# @yield [n] Optional block to use when rounding after exchanging one
|
53
|
+
# currency for another.
|
54
|
+
# @yieldparam [Float] n The resulting float after exchanging one currency
|
55
|
+
# for another.
|
56
|
+
# @yieldreturn [Integer]
|
57
|
+
#
|
58
|
+
# @return [Money]
|
59
|
+
#
|
60
|
+
# @raise +Money::Bank::UnknownRate+ if the conversion rate is unknown.
|
61
|
+
#
|
62
|
+
# @example
|
63
|
+
# bank = Money::Bank::VariableExchange.new
|
64
|
+
# bank.add_rate("USD", "CAD", 1.24515)
|
65
|
+
# bank.add_rate("CAD", "USD", 0.803115)
|
66
|
+
#
|
67
|
+
# c1 = 100_00.to_money("USD")
|
68
|
+
# c2 = 100_00.to_money("CAD")
|
69
|
+
#
|
70
|
+
# # Exchange 100 USD to CAD:
|
71
|
+
# bank.exchange_with(c1, "CAD") #=> #<Money @cents=1245150>
|
72
|
+
#
|
73
|
+
# # Exchange 100 CAD to USD:
|
74
|
+
# bank.exchange_with(c2, "USD") #=> #<Money @cents=803115>
|
75
|
+
def exchange_with(from, to_currency, &block)
|
76
|
+
return from if same_currency?(from.currency, to_currency)
|
77
|
+
|
78
|
+
rate = get_rate(from.currency, to_currency)
|
79
|
+
unless rate
|
80
|
+
raise UnknownRate, "No conversion rate known for '#{from.currency.iso_code}' -> '#{to_currency}'"
|
81
|
+
end
|
82
|
+
_to_currency_ = Currency.wrap(to_currency)
|
83
|
+
|
84
|
+
#cents = BigDecimal.new(from.cents.to_s) / (BigDecimal.new(from.currency.subunit_to_unit.to_s) / BigDecimal.new(_to_currency_.subunit_to_unit.to_s))
|
85
|
+
cents = BigDecimal.new(from.exact_number.to_s) / (BigDecimal.new(from.currency.subunit_to_unit.to_f.to_s) / BigDecimal.new(_to_currency_.subunit_to_unit.to_f.to_s))
|
86
|
+
|
87
|
+
ex = cents * BigDecimal.new(rate.to_s)
|
88
|
+
ex = ex.to_f
|
89
|
+
ex = if block_given?
|
90
|
+
block.call(ex)
|
91
|
+
elsif @rounding_method
|
92
|
+
@rounding_method.call(ex)
|
93
|
+
else
|
94
|
+
ex
|
95
|
+
end
|
96
|
+
Money.new(ex, _to_currency_)
|
97
|
+
end
|
98
|
+
|
99
|
+
# Registers a conversion rate and returns it (uses +#set_rate+).
|
100
|
+
#
|
101
|
+
# @param [Currency, String, Symbol] from Currency to exchange from.
|
102
|
+
# @param [Currency, String, Symbol] to Currency to exchange to.
|
103
|
+
# @param [Numeric] rate Rate to use when exchanging currencies.
|
104
|
+
#
|
105
|
+
# @return [Numeric]
|
106
|
+
#
|
107
|
+
# @example
|
108
|
+
# bank = Money::Bank::VariableExchange.new
|
109
|
+
# bank.add_rate("USD", "CAD", 1.24515)
|
110
|
+
# bank.add_rate("CAD", "USD", 0.803115)
|
111
|
+
def add_rate(from, to, rate)
|
112
|
+
set_rate(from, to, rate)
|
113
|
+
end
|
114
|
+
|
115
|
+
# Set the rate for the given currencies. Uses +Mutex+ to synchronize data
|
116
|
+
# access.
|
117
|
+
#
|
118
|
+
# @param [Currency, String, Symbol] from Currency to exchange from.
|
119
|
+
# @param [Currency, String, Symbol] to Currency to exchange to.
|
120
|
+
# @param [Numeric] rate Rate to use when exchanging currencies.
|
121
|
+
#
|
122
|
+
# @return [Numeric]
|
123
|
+
#
|
124
|
+
# @example
|
125
|
+
# bank = Money::Bank::VariableExchange.new
|
126
|
+
# bank.set_rate("USD", "CAD", 1.24515)
|
127
|
+
# bank.set_rate("CAD", "USD", 0.803115)
|
128
|
+
def set_rate(from, to, rate)
|
129
|
+
@mutex.synchronize { @rates[rate_key_for(from, to)] = rate }
|
130
|
+
end
|
131
|
+
|
132
|
+
# Retrieve the rate for the given currencies. Uses +Mutex+ to synchronize
|
133
|
+
# data access.
|
134
|
+
#
|
135
|
+
# @param [Currency, String, Symbol] from Currency to exchange from.
|
136
|
+
# @param [Currency, String, Symbol] to Currency to exchange to.
|
137
|
+
#
|
138
|
+
# @return [Numeric]
|
139
|
+
#
|
140
|
+
# @example
|
141
|
+
# bank = Money::Bank::VariableExchange.new
|
142
|
+
# bank.set_rate("USD", "CAD", 1.24515)
|
143
|
+
# bank.set_rate("CAD", "USD", 0.803115)
|
144
|
+
#
|
145
|
+
# bank.get_rate("USD", "CAD") #=> 1.24515
|
146
|
+
# bank.get_rate("CAD", "USD") #=> 0.803115
|
147
|
+
def get_rate(from, to)
|
148
|
+
@mutex.synchronize { @rates[rate_key_for(from, to)] }
|
149
|
+
end
|
150
|
+
|
151
|
+
# Return the known rates as a string in the format specified. If +file+
|
152
|
+
# is given will also write the string out to the file specified.
|
153
|
+
# Available formats are +:json+, +:ruby+ and +:yaml+.
|
154
|
+
#
|
155
|
+
# @param [Symbol] format Request format for the resulting string.
|
156
|
+
# @param [String] file Optional file location to write the rates to.
|
157
|
+
#
|
158
|
+
# @return [String]
|
159
|
+
#
|
160
|
+
# @raise +Money::Bank::UnknownRateFormat+ if format is unknown.
|
161
|
+
#
|
162
|
+
# @example
|
163
|
+
# bank = Money::Bank::VariableExchange.new
|
164
|
+
# bank.set_rate("USD", "CAD", 1.24515)
|
165
|
+
# bank.set_rate("CAD", "USD", 0.803115)
|
166
|
+
#
|
167
|
+
# s = bank.export_rates(:json)
|
168
|
+
# s #=> "{\"USD_TO_CAD\":1.24515,\"CAD_TO_USD\":0.803115}"
|
169
|
+
def export_rates(format, file=nil)
|
170
|
+
raise Money::Bank::UnknownRateFormat unless
|
171
|
+
RATE_FORMATS.include? format
|
172
|
+
|
173
|
+
s = ""
|
174
|
+
@mutex.synchronize {
|
175
|
+
s = case format
|
176
|
+
when :json
|
177
|
+
JSON.dump(@rates)
|
178
|
+
when :ruby
|
179
|
+
Marshal.dump(@rates)
|
180
|
+
when :yaml
|
181
|
+
YAML.dump(@rates)
|
182
|
+
end
|
183
|
+
|
184
|
+
unless file.nil?
|
185
|
+
File.open(file, "w").write(s)
|
186
|
+
end
|
187
|
+
}
|
188
|
+
s
|
189
|
+
end
|
190
|
+
|
191
|
+
# Loads rates provided in +s+ given the specified format. Available
|
192
|
+
# formats are +:json+, +:ruby+ and +:yaml+.
|
193
|
+
#
|
194
|
+
# @param [Symbol] format The format of +s+.
|
195
|
+
# @param [String] s The rates string.
|
196
|
+
#
|
197
|
+
# @return [self]
|
198
|
+
#
|
199
|
+
# @raise +Money::Bank::UnknownRateFormat+ if format is unknown.
|
200
|
+
#
|
201
|
+
# @example
|
202
|
+
# s = "{\"USD_TO_CAD\":1.24515,\"CAD_TO_USD\":0.803115}"
|
203
|
+
# bank = Money::Bank::VariableExchange.new
|
204
|
+
# bank.import_rates(:json, s)
|
205
|
+
#
|
206
|
+
# bank.get_rate("USD", "CAD") #=> 1.24515
|
207
|
+
# bank.get_rate("CAD", "USD") #=> 0.803115
|
208
|
+
def import_rates(format, s)
|
209
|
+
raise Money::Bank::UnknownRateFormat unless
|
210
|
+
RATE_FORMATS.include? format
|
211
|
+
|
212
|
+
@mutex.synchronize {
|
213
|
+
@rates = case format
|
214
|
+
when :json
|
215
|
+
JSON.load(s)
|
216
|
+
when :ruby
|
217
|
+
Marshal.load(s)
|
218
|
+
when :yaml
|
219
|
+
YAML.load(s)
|
220
|
+
end
|
221
|
+
}
|
222
|
+
self
|
223
|
+
end
|
224
|
+
|
225
|
+
private
|
226
|
+
|
227
|
+
# Return the rate hashkey for the given currencies.
|
228
|
+
#
|
229
|
+
# @param [Currency, String, Symbol] from The currency to exchange from.
|
230
|
+
# @param [Currency, String, Symbol] to The currency to exchange to.
|
231
|
+
#
|
232
|
+
# @return [String]
|
233
|
+
#
|
234
|
+
# @example
|
235
|
+
# rate_key_for("USD", "CAD") #=> "USD_TO_CAD"
|
236
|
+
def rate_key_for(from, to)
|
237
|
+
"#{Currency.wrap(from).iso_code}_TO_#{Currency.wrap(to).iso_code}".upcase
|
238
|
+
end
|
239
|
+
end
|
240
|
+
end
|
241
|
+
end
|
@@ -0,0 +1,63 @@
|
|
1
|
+
# Open +Numeric+ to add new method.
|
2
|
+
class Numeric
|
3
|
+
|
4
|
+
# Converts this numeric into a +Money+ object in the given +currency+.
|
5
|
+
#
|
6
|
+
# @param [Currency, String, Symbol] currency
|
7
|
+
# The currency to set the resulting +Money+ object to.
|
8
|
+
#
|
9
|
+
# @return [Money]
|
10
|
+
#
|
11
|
+
# @example
|
12
|
+
# 100.to_money #=> #<Money @cents=10000>
|
13
|
+
# 100.37.to_money #=> #<Money @cents=10037>
|
14
|
+
# BigDecimal.new('100').to_money #=> #<Money @cents=10000>
|
15
|
+
#
|
16
|
+
# @see Money.from_numeric
|
17
|
+
#
|
18
|
+
def to_money(currency = nil)
|
19
|
+
Money.from_numeric(self, currency || Money.default_currency)
|
20
|
+
end
|
21
|
+
|
22
|
+
end
|
23
|
+
|
24
|
+
# Open +String+ to add new methods.
|
25
|
+
class String
|
26
|
+
|
27
|
+
# Parses the current string and converts it to a +Money+ object.
|
28
|
+
# Excess characters will be discarded.
|
29
|
+
#
|
30
|
+
# @param [Currency, String, Symbol] currency
|
31
|
+
# The currency to set the resulting +Money+ object to.
|
32
|
+
#
|
33
|
+
# @return [Money]
|
34
|
+
#
|
35
|
+
# @example
|
36
|
+
# '100'.to_money #=> #<Money @cents=10000>
|
37
|
+
# '100.37'.to_money #=> #<Money @cents=10037>
|
38
|
+
# '100 USD'.to_money #=> #<Money @cents=10000, @currency=#<Money::Currency id: usd>>
|
39
|
+
# 'USD 100'.to_money #=> #<Money @cents=10000, @currency=#<Money::Currency id: usd>>
|
40
|
+
# '$100 USD'.to_money #=> #<Money @cents=10000, @currency=#<Money::Currency id: usd>>
|
41
|
+
# 'hello 2000 world'.to_money #=> #<Money @cents=200000 @currency=#<Money::Currency id: usd>>
|
42
|
+
#
|
43
|
+
# @see Money.from_string
|
44
|
+
#
|
45
|
+
def to_money(currency = nil)
|
46
|
+
Money.parse(self, currency)
|
47
|
+
end
|
48
|
+
|
49
|
+
# Converts the current string into a +Currency+ object.
|
50
|
+
#
|
51
|
+
# @return [Money::Currency]
|
52
|
+
#
|
53
|
+
# @raise [Money::Currency::UnknownCurrency]
|
54
|
+
# If this String reference an unknown currency.
|
55
|
+
#
|
56
|
+
# @example
|
57
|
+
# "USD".to_currency #=> #<Money::Currency id: usd>
|
58
|
+
#
|
59
|
+
def to_currency
|
60
|
+
Money::Currency.new(self)
|
61
|
+
end
|
62
|
+
|
63
|
+
end
|