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.
@@ -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