money 6.0.1.beta2 → 6.0.1.beta3

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.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 7dbef5f1733e04f6823d0e504dbbe73112545214
4
- data.tar.gz: 62e28891ffa15550bf323af2671a20cfa663279f
3
+ metadata.gz: cf2817efb2d831c4dc20a21ad35e2668ef18f877
4
+ data.tar.gz: ab6fb4c372bae304d46f672517844816e2d9788e
5
5
  SHA512:
6
- metadata.gz: f1b30704d8098a4b7a5301775d4de24b9d8a2c0a02da1e175130319b7d177adca746203277ac7c7ee78e012ad6dd8df535333ae280fc7b235b469abe0330371f
7
- data.tar.gz: 3de4d2fe5d9b244d7358f168b9f3fe6c5b6fb9f48b87a2be65436f95b7789bb12268dc83bb8dba0295656c28dd13cef522ef3f45c69f2f274e098f0afb1a41c4
6
+ metadata.gz: f6a4ce82b5f4d9624d9b1568a39357b0d35d190f2c6b94f30bce534f2b2daffa103e40c9da450788c36c2d994e33eeb1554eb1931655c29fa0cd474ceb971402
7
+ data.tar.gz: f08fda3dc489e7031a6937c28a32678bdef279b8e0e8725c832019576f380da9ee4b217aa6a5d7055985dcdff49cec1d6e48117a0e49843ad5792683290356aa
@@ -30,7 +30,7 @@ class Money
30
30
  # bank.exchange_with(c2, "USD") #=> #<Money @fractional=803115>
31
31
  class VariableExchange < Base
32
32
 
33
- attr_reader :rates
33
+ attr_reader :rates, :mutex
34
34
 
35
35
  # Available formats for importing/exporting rates.
36
36
  RATE_FORMATS = [:json, :ruby, :yaml]
@@ -84,27 +84,38 @@ class Money
84
84
  #
85
85
  # # Exchange 100 CAD to USD:
86
86
  # bank.exchange_with(c2, "USD") #=> #<Money @fractional=803115>
87
- def exchange_with(from, to_currency)
88
- return from if same_currency?(from.currency, to_currency)
87
+ def exchange_with(from, to_currency, &block)
88
+ to_currency = Currency.wrap(to_currency)
89
+ if from.currency == to_currency
90
+ from
91
+ else
92
+ if rate = get_rate(from.currency, to_currency)
93
+ fractional = calculate_fractional(from, to_currency)
94
+ Money.new(
95
+ exchange(fractional, rate, &block), to_currency
96
+ )
97
+ else
98
+ raise UnknownRate, "No conversion rate known for '#{from.currency.iso_code}' -> '#{to_currency}'"
99
+ end
100
+ end
101
+ end
102
+
103
+ def calculate_fractional(from, to_currency)
104
+ BigDecimal.new(from.fractional.to_s) / (
105
+ BigDecimal.new(from.currency.subunit_to_unit.to_s) /
106
+ BigDecimal.new(to_currency.subunit_to_unit.to_s)
107
+ )
108
+ end
89
109
 
90
- rate = get_rate(from.currency, to_currency)
91
- unless rate
92
- raise UnknownRate, "No conversion rate known for '#{from.currency.iso_code}' -> '#{to_currency}'"
110
+ def exchange(fractional, rate, &block)
111
+ ex = (fractional * BigDecimal.new(rate.to_s)).to_f
112
+ if block_given?
113
+ yield ex
114
+ elsif @rounding_method
115
+ @rounding_method.call(ex)
116
+ else
117
+ ex.to_s.to_i
93
118
  end
94
- _to_currency_ = Currency.wrap(to_currency)
95
-
96
- fractional = BigDecimal.new(from.fractional.to_s) / (BigDecimal.new(from.currency.subunit_to_unit.to_s) / BigDecimal.new(_to_currency_.subunit_to_unit.to_s))
97
-
98
- ex = fractional * BigDecimal.new(rate.to_s)
99
- ex = ex.to_f
100
- ex = if block_given?
101
- yield ex
102
- elsif @rounding_method
103
- @rounding_method.call(ex)
104
- else
105
- ex.to_s.to_i
106
- end
107
- Money.new(ex, _to_currency_)
108
119
  end
109
120
 
110
121
  # Registers a conversion rate and returns it (uses +#set_rate+).
@@ -129,6 +140,8 @@ class Money
129
140
  # @param [Currency, String, Symbol] from Currency to exchange from.
130
141
  # @param [Currency, String, Symbol] to Currency to exchange to.
131
142
  # @param [Numeric] rate Rate to use when exchanging currencies.
143
+ # @param [Hash] opts Options hash to set special parameters
144
+ # @option opts [Boolean] :without_mutex disables the usage of a mutex
132
145
  #
133
146
  # @return [Numeric]
134
147
  #
@@ -136,8 +149,13 @@ class Money
136
149
  # bank = Money::Bank::VariableExchange.new
137
150
  # bank.set_rate("USD", "CAD", 1.24515)
138
151
  # bank.set_rate("CAD", "USD", 0.803115)
139
- def set_rate(from, to, rate)
140
- @mutex.synchronize { @rates[rate_key_for(from, to)] = rate }
152
+ def set_rate(from, to, rate, opts = {})
153
+ fn = -> { @rates[rate_key_for(from, to)] = rate }
154
+ if opts[:without_mutex]
155
+ fn.call
156
+ else
157
+ @mutex.synchronize { fn.call }
158
+ end
141
159
  end
142
160
 
143
161
  # Retrieve the rate for the given currencies. Uses +Mutex+ to synchronize
@@ -145,6 +163,8 @@ class Money
145
163
  #
146
164
  # @param [Currency, String, Symbol] from Currency to exchange from.
147
165
  # @param [Currency, String, Symbol] to Currency to exchange to.
166
+ # @param [Hash] opts Options hash to set special parameters
167
+ # @option opts [Boolean] :without_mutex disables the usage of a mutex
148
168
  #
149
169
  # @return [Numeric]
150
170
  #
@@ -155,8 +175,13 @@ class Money
155
175
  #
156
176
  # bank.get_rate("USD", "CAD") #=> 1.24515
157
177
  # bank.get_rate("CAD", "USD") #=> 0.803115
158
- def get_rate(from, to)
159
- @mutex.synchronize { @rates[rate_key_for(from, to)] }
178
+ def get_rate(from, to, opts = {})
179
+ fn = -> { @rates[rate_key_for(from, to)] }
180
+ if opts[:without_mutex]
181
+ fn.call
182
+ else
183
+ @mutex.synchronize { fn.call }
184
+ end
160
185
  end
161
186
 
162
187
  # Return the known rates as a string in the format specified. If +file+
@@ -165,6 +190,8 @@ class Money
165
190
  #
166
191
  # @param [Symbol] format Request format for the resulting string.
167
192
  # @param [String] file Optional file location to write the rates to.
193
+ # @param [Hash] opts Options hash to set special parameters
194
+ # @option opts [Boolean] :without_mutex disables the usage of a mutex
168
195
  #
169
196
  # @return [String]
170
197
  #
@@ -177,12 +204,12 @@ class Money
177
204
  #
178
205
  # s = bank.export_rates(:json)
179
206
  # s #=> "{\"USD_TO_CAD\":1.24515,\"CAD_TO_USD\":0.803115}"
180
- def export_rates(format, file=nil)
207
+ def export_rates(format, file = nil, opts = {})
181
208
  raise Money::Bank::UnknownRateFormat unless
182
209
  RATE_FORMATS.include? format
183
210
 
184
211
  s = ""
185
- @mutex.synchronize {
212
+ fn = -> {
186
213
  s = case format
187
214
  when :json
188
215
  JSON.dump(@rates)
@@ -196,6 +223,11 @@ class Money
196
223
  File.open(file, "w") {|f| f.write(s) }
197
224
  end
198
225
  }
226
+ if opts[:without_mutex]
227
+ fn.call
228
+ else
229
+ @mutex.synchronize { fn.call }
230
+ end
199
231
  s
200
232
  end
201
233
 
@@ -204,6 +236,8 @@ class Money
204
236
  #
205
237
  # @param [Symbol] format The format of +s+.
206
238
  # @param [String] s The rates string.
239
+ # @param [Hash] opts Options hash to set special parameters
240
+ # @option opts [Boolean] :without_mutex disables the usage of a mutex
207
241
  #
208
242
  # @return [self]
209
243
  #
@@ -216,11 +250,11 @@ class Money
216
250
  #
217
251
  # bank.get_rate("USD", "CAD") #=> 1.24515
218
252
  # bank.get_rate("CAD", "USD") #=> 0.803115
219
- def import_rates(format, s)
253
+ def import_rates(format, s, opts = {})
220
254
  raise Money::Bank::UnknownRateFormat unless
221
255
  RATE_FORMATS.include? format
222
256
 
223
- @mutex.synchronize {
257
+ fn = -> {
224
258
  @rates = case format
225
259
  when :json
226
260
  JSON.load(s)
@@ -230,6 +264,11 @@ class Money
230
264
  YAML.load(s)
231
265
  end
232
266
  }
267
+ if opts[:without_mutex]
268
+ fn.call
269
+ else
270
+ @mutex.synchronize { fn.call }
271
+ end
233
272
  self
234
273
  end
235
274
 
@@ -1,20 +1,19 @@
1
1
  # encoding: utf-8
2
2
 
3
- require 'json'
3
+ require "json"
4
+ require "money/currency/loader"
5
+ require "money/currency/heuristics"
4
6
 
5
7
  class Money
6
8
 
7
9
  # Represents a specific currency unit.
8
10
  #
9
11
  # @see http://en.wikipedia.org/wiki/Currency
12
+ # @see http://iso4217.net/
10
13
  class Currency
11
14
  include Comparable
12
-
13
- require "money/currency/loader"
14
- extend Loader
15
-
16
- require "money/currency/heuristics"
17
- extend Heuristics
15
+ extend Money::Currency::Loader
16
+ extend Money::Currency::Heuristics
18
17
 
19
18
  # Thrown when an unknown currency is requested.
20
19
  class UnknownCurrency < StandardError; end
@@ -34,7 +33,9 @@ class Money
34
33
  # Money::Currency.find(:foo) #=> nil
35
34
  def find(id)
36
35
  id = id.to_s.downcase.to_sym
37
- new(id) if self.table[id]
36
+ new(id)
37
+ rescue UnknownCurrency
38
+ nil
38
39
  end
39
40
 
40
41
  # Lookup a currency with given +num+ as an ISO 4217 numeric and returns an
@@ -51,7 +52,9 @@ class Money
51
52
  def find_by_iso_numeric(num)
52
53
  num = num.to_s
53
54
  id, _ = self.table.find{|key, currency| currency[:iso_numeric] == num}
54
- new(id) if self.table[id]
55
+ new(id)
56
+ rescue UnknownCurrency
57
+ nil
55
58
  end
56
59
 
57
60
  # Wraps the object in a +Currency+ unless it's already a +Currency+
@@ -68,9 +71,7 @@ class Money
68
71
  # Money::Currency.wrap(c1) #=> #<Money::Currency id: usd ...>
69
72
  # Money::Currency.wrap("usd") #=> #<Money::Currency id: usd ...>
70
73
  def wrap(object)
71
- if object.nil?
72
- nil
73
- elsif object.is_a?(Currency)
74
+ if object.nil? || object.is_a?(Currency)
74
75
  object
75
76
  else
76
77
  Currency.new(object)
@@ -128,71 +129,33 @@ class Money
128
129
  end
129
130
  end
130
131
 
131
- # The symbol used to identify the currency, usually the lowercase
132
- # +iso_code+ attribute.
133
- #
134
- # @return [Symbol]
135
- attr_reader :id
136
-
137
- # A numerical value you can use to sort/group the currency list.
138
- #
139
- # @return [Integer]
140
- attr_reader :priority
141
-
142
- # The international 3-letter code as defined by the ISO 4217 standard.
143
- #
144
- # @return [String]
145
- # @see http://iso4217.net/
146
- attr_reader :iso_code
147
-
148
- #
149
- # The international 3-numeric code as defined by the ISO 4217 standard.
150
- #
151
- # @return [String]
152
- # @see http://iso4217.net/
153
- attr_reader :iso_numeric
154
-
155
- # The currency name.
156
- #
157
- # @return [String]
158
- attr_reader :name
159
-
160
- # The currency symbol (UTF-8 encoded).
161
- #
162
- # @return [String]
163
- attr_reader :symbol
164
-
165
- # The html entity for the currency symbol
166
- #
167
- # @return [String]
168
- attr_reader :html_entity
169
-
170
- # The name of the fractional monetary unit.
171
- #
172
- # @return [String]
173
- attr_reader :subunit
174
-
175
- # The proportion between the unit and the subunit
176
- #
177
- # @return [Integer]
178
- attr_reader :subunit_to_unit
179
-
180
- # The decimal mark, or character used to separate the whole unit from the subunit.
181
- #
182
- # @return [String]
183
- attr_reader :decimal_mark
184
- alias :separator :decimal_mark
185
-
186
- # The character used to separate thousands grouping of the whole unit.
187
- #
188
- # @return [String]
189
- attr_reader :thousands_separator
190
- alias :delimiter :thousands_separator
191
-
192
- # Should the currency symbol precede the amount, or should it come after?
193
- #
194
- # @return [Boolean]
195
- attr_reader :symbol_first
132
+ # @attr_reader [Symbol] id The symbol used to identify the currency,
133
+ # usually the lowercase +iso_code+ attribute.
134
+ # @attr_reader [Integer] priority A numerical value you can use to
135
+ # sort/group the currency list.
136
+ # @attr_reader [String] iso_code The international 3-letter code as defined
137
+ # by the ISO 4217 standard.
138
+ # @attr_reader [String] iso_numeric The international 3-numeric code as
139
+ # defined by the ISO 4217 standard.
140
+ # @attr_reader [String] name The currency name.
141
+ # @attr_reader [String] symbol The currency symbol (UTF-8 encoded).
142
+ # @attr_reader [String] html_entity The html entity for the currency symbol
143
+ # @attr_reader [String] subunit The name of the fractional monetary unit.
144
+ # @attr_reader [Integer] subunit_to_unit The proportion between the unit
145
+ # and the subunit
146
+ # @attr_reader [String] decimal_mark The decimal mark, or character used to
147
+ # separate the whole unit from the subunit.
148
+ # @attr_reader [String] The character used to separate thousands grouping
149
+ # of the whole unit.
150
+ # @attr_reader [Boolean] symbol_first Should the currency symbol precede
151
+ # the amount, or should it come after?
152
+
153
+ attr_reader :id, :priority, :iso_code, :iso_numeric, :name, :symbol,
154
+ :html_entity, :subunit, :subunit_to_unit, :decimal_mark,
155
+ :thousands_separator, :symbol_first
156
+
157
+ alias_method :separator, :decimal_mark
158
+ alias_method :delimiter, :thousands_separator
196
159
 
197
160
  # Create a new +Currency+ object.
198
161
  #
@@ -205,12 +168,15 @@ class Money
205
168
  # Money::Currency.new(:usd) #=> #<Money::Currency id: usd ...>
206
169
  def initialize(id)
207
170
  id = id.to_s.downcase
208
- raise(UnknownCurrency, "Unknown currency `#{id}'") unless self.class.stringified_keys.include?(id)
209
171
 
210
- @id = id.to_sym
211
- data = self.class.table[@id]
212
- data.each_pair do |key, value|
213
- instance_variable_set(:"@#{key}", value)
172
+ if self.class.stringified_keys.include?(id)
173
+ @id = id.to_sym
174
+ data = self.class.table[@id]
175
+ data.each_pair do |key, value|
176
+ instance_variable_set(:"@#{key}", value)
177
+ end
178
+ else
179
+ raise UnknownCurrency, "Unknown currency '#{id}'"
214
180
  end
215
181
  end
216
182
 
@@ -244,9 +210,18 @@ class Money
244
210
  # c1 == c1 #=> true
245
211
  # c1 == c2 #=> false
246
212
  def ==(other_currency)
247
- self.equal?(other_currency) ||
248
- self.id.to_s.downcase == (other_currency.is_a?(Currency) ? other_currency.id.to_s.downcase : other_currency.to_s.downcase)
213
+ self.equal?(other_currency) || compare_ids(other_currency)
214
+ end
215
+
216
+ def compare_ids(other_currency)
217
+ other_currency_id = if other_currency.is_a?(Currency)
218
+ other_currency.id.to_s.downcase
219
+ else
220
+ other_currency.to_s.downcase
221
+ end
222
+ self.id.to_s.downcase == other_currency_id
249
223
  end
224
+ private :compare_ids
250
225
 
251
226
  # Compares +self+ with +other_currency+ and returns +true+ if the are the
252
227
  # same or if their +id+ attributes match.
@@ -288,7 +263,7 @@ class Money
288
263
  # Returns a string representation corresponding to the upcase +id+
289
264
  # attribute.
290
265
  #
291
- # -–
266
+ # --
292
267
  # DEV: id.to_s.upcase corresponds to iso_code but don't use ISO_CODE for consistency.
293
268
  #
294
269
  # @return [String]
@@ -311,7 +286,7 @@ class Money
311
286
  def to_str
312
287
  id.to_s.upcase
313
288
  end
314
-
289
+
315
290
  # Returns a symbol representation corresponding to the upcase +id+
316
291
  # attribute.
317
292
  #
@@ -321,12 +296,7 @@ class Money
321
296
  # Money::Currency.new(:usd).to_sym #=> :USD
322
297
  # Money::Currency.new(:eur).to_sym #=> :EUR
323
298
  def to_sym
324
- if id.respond_to?(:upcase)
325
- id.upcase
326
- else
327
- # Ruby <= 1.8.7 doesn't support Symbol#upcase
328
- id.to_s.upcase.to_sym
329
- end
299
+ id.to_s.upcase.to_sym
330
300
  end
331
301
 
332
302
  # Conversation to +self+.
@@ -336,7 +306,6 @@ class Money
336
306
  self
337
307
  end
338
308
 
339
-
340
309
  # Returns currency symbol or iso code for currencies with no symbol.
341
310
  #
342
311
  # @return [String]
@@ -357,31 +326,24 @@ class Money
357
326
 
358
327
  # Cache decimal places for subunit_to_unit values. Common ones pre-cached.
359
328
  def self.decimal_places_cache
360
- @decimal_places_cache ||= {
361
- 1 => 0,
362
- 10 => 1,
363
- 100 => 2,
364
- 1000 => 3
365
- }
329
+ @decimal_places_cache ||= {1 => 0, 10 => 1, 100 => 2, 1000 => 3}
366
330
  end
367
331
 
368
332
  # The number of decimal places needed.
369
333
  #
370
334
  # @return [Integer]
371
335
  def decimal_places
372
- cache = self.class.decimal_places_cache
373
- places = cache[subunit_to_unit]
374
- unless places
375
- places = calculate_decimal_places(subunit_to_unit)
376
- cache[subunit_to_unit] = places
377
- end
378
- places
336
+ cache[subunit_to_unit] ||= calculate_decimal_places(subunit_to_unit)
379
337
  end
380
338
 
339
+ def cache
340
+ self.class.decimal_places_cache
341
+ end
342
+ private :cache
343
+
381
344
  # If we need to figure out how many decimal places we need we
382
345
  # use repeated integer division.
383
346
  def calculate_decimal_places(num)
384
- return 0 if num == 1
385
347
  i = 1
386
348
  while num >= 10
387
349
  num /= 10
@@ -390,6 +352,5 @@ class Money
390
352
  i
391
353
  end
392
354
  private :calculate_decimal_places
393
-
394
355
  end
395
356
  end