money 6.0.1.beta2 → 6.0.1.beta3

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