ecr_money 3.6.5

Sign up to get free protection for your applications and to get access to all the features.
@@ -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