money 6.0.0 → 6.0.1
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 +7 -0
- data/.coveralls.yml +1 -0
- data/.gitignore +19 -0
- data/.travis.yml +11 -0
- data/AUTHORS +101 -0
- data/CHANGELOG.md +6 -0
- data/CONTRIBUTING.md +17 -0
- data/Gemfile +6 -0
- data/README.md +9 -10
- data/Rakefile +4 -45
- data/config/currency_iso.json +1 -1
- data/lib/money/bank/variable_exchange.rb +67 -28
- data/lib/money/core_extensions/numeric.rb +7 -0
- data/lib/money/core_extensions/string.rb +13 -0
- data/lib/money/core_extensions/symbol.rb +7 -0
- data/lib/money/core_extensions.rb +5 -78
- data/lib/money/currency/heuristics.rb +121 -117
- data/lib/money/currency/loader.rb +19 -17
- data/lib/money/currency.rb +91 -101
- data/lib/money/deprecations.rb +6 -1
- data/lib/money/money/arithmetic.rb +45 -31
- data/lib/money/money/formatting.rb +41 -55
- data/lib/money/money/parsing.rb +14 -344
- data/lib/money/money.rb +167 -143
- data/lib/money/version.rb +3 -0
- data/lib/money.rb +4 -2
- data/money.gemspec +25 -23
- data/spec/bank/variable_exchange_spec.rb +42 -0
- data/spec/core_extensions_spec.rb +2 -2
- data/spec/currency_spec.rb +16 -0
- data/spec/money/deprecations_spec.rb +51 -0
- data/spec/money/parsing_spec.rb +3 -6
- data/spec/money_spec.rb +75 -9
- data/spec/spec_helper.rb +7 -4
- metadata +120 -65
data/lib/money/money.rb
CHANGED
|
@@ -1,4 +1,7 @@
|
|
|
1
1
|
# encoding: utf-8
|
|
2
|
+
require "monetize"
|
|
3
|
+
require "monetize/core_extensions"
|
|
4
|
+
|
|
2
5
|
require "money/bank/variable_exchange"
|
|
3
6
|
require "money/bank/single_currency"
|
|
4
7
|
require "money/money/arithmetic"
|
|
@@ -15,10 +18,7 @@ require "money/money/formatting"
|
|
|
15
18
|
#
|
|
16
19
|
# @see http://en.wikipedia.org/wiki/Money
|
|
17
20
|
class Money
|
|
18
|
-
include Comparable
|
|
19
|
-
include Arithmetic
|
|
20
|
-
include Formatting
|
|
21
|
-
include Parsing
|
|
21
|
+
include Comparable, Money::Arithmetic, Money::Formatting, Money::Parsing
|
|
22
22
|
|
|
23
23
|
# Convenience method for fractional part of the amount. Synonym of #fractional
|
|
24
24
|
#
|
|
@@ -64,68 +64,50 @@ class Money
|
|
|
64
64
|
end
|
|
65
65
|
end
|
|
66
66
|
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
num.to_d
|
|
72
|
-
else
|
|
73
|
-
BigDecimal.new(num.to_s)
|
|
74
|
-
end
|
|
75
|
-
end
|
|
76
|
-
private :as_d
|
|
77
|
-
|
|
78
|
-
# The currency the money is in.
|
|
79
|
-
#
|
|
80
|
-
# @return [Currency]
|
|
81
|
-
attr_reader :currency
|
|
82
|
-
|
|
83
|
-
# The +Money::Bank+ based object used to perform currency exchanges with.
|
|
84
|
-
#
|
|
85
|
-
# @return [Money::Bank::*]
|
|
86
|
-
attr_reader :bank
|
|
67
|
+
# @attr_reader [Currency] currency The currency the money is in.
|
|
68
|
+
# @attr_reader [Money::Bank::*] bank The +Money::Bank+ based object used to
|
|
69
|
+
# perform currency exchanges with.
|
|
70
|
+
attr_reader :currency, :bank
|
|
87
71
|
|
|
88
72
|
# Class Methods
|
|
89
73
|
class << self
|
|
90
|
-
#
|
|
91
|
-
#
|
|
92
|
-
#
|
|
93
|
-
#
|
|
74
|
+
# @attr_accessor [Money::Bank::*] default_bank Each Money object is
|
|
75
|
+
# associated to a bank object, which is responsible for currency exchange.
|
|
76
|
+
# This property allows you to specify the default bank object. The default
|
|
77
|
+
# value for this property is an instance of +Bank::VariableExchange.+ It
|
|
78
|
+
# allows one to specify custom exchange rates.
|
|
79
|
+
# @attr_accessor [Money::Currency] default_currency The default currency,
|
|
80
|
+
# which is used when +Money.new+ is called without an explicit currency
|
|
81
|
+
# argument. The default value is Currency.new("USD"). The value must be a
|
|
82
|
+
# valid +Money::Currency+ instance.
|
|
83
|
+
# @attr_accessor [true, false] use_i18n Use this to disable i18n even if
|
|
84
|
+
# it's used by other objects in your app.
|
|
85
|
+
# @attr_accessor [true, false] assume_from_symbol Use this to enable the
|
|
86
|
+
# ability to assume the currency from a passed symbol
|
|
87
|
+
# @attr_accessor [true, false] infinite_precision Use this to enable
|
|
88
|
+
# infinite precision cents
|
|
89
|
+
# @attr_accessor [Integer] conversion_precision Use this to specify
|
|
90
|
+
# precision for converting Rational to BigDecimal
|
|
91
|
+
attr_accessor :default_bank, :default_currency, :use_i18n,
|
|
92
|
+
:assume_from_symbol, :infinite_precision, :conversion_precision,
|
|
93
|
+
:silence_core_extensions_deprecations
|
|
94
|
+
|
|
95
|
+
# @attr_writer rounding_mode Use this to specify the rounding mode
|
|
96
|
+
attr_writer :rounding_mode
|
|
97
|
+
|
|
98
|
+
# Create a new money object with value 0.
|
|
94
99
|
#
|
|
95
|
-
# @
|
|
96
|
-
attr_accessor :default_bank
|
|
97
|
-
|
|
98
|
-
# The default currency, which is used when +Money.new+ is called without an
|
|
99
|
-
# explicit currency argument. The default value is Currency.new("USD"). The
|
|
100
|
-
# value must be a valid +Money::Currency+ instance.
|
|
100
|
+
# @param [Currency, String, Symbol] currency The currency to use.
|
|
101
101
|
#
|
|
102
|
-
# @return [Money
|
|
103
|
-
attr_accessor :default_currency
|
|
104
|
-
|
|
105
|
-
# Use this to disable i18n even if it's used by other objects in your app.
|
|
102
|
+
# @return [Money]
|
|
106
103
|
#
|
|
107
|
-
# @
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
# Use this to enable infinite precision cents
|
|
116
|
-
#
|
|
117
|
-
# @return [true,false]
|
|
118
|
-
attr_accessor :infinite_precision
|
|
119
|
-
|
|
120
|
-
# Use this to specify the rounding mode
|
|
121
|
-
#
|
|
122
|
-
# @return [BigDecimal::ROUND_MODE]
|
|
123
|
-
attr_accessor :rounding_mode
|
|
124
|
-
|
|
125
|
-
# Use this to specify precision for converting Rational to BigDecimal
|
|
126
|
-
#
|
|
127
|
-
# @return [Integer]
|
|
128
|
-
attr_accessor :conversion_precision
|
|
104
|
+
# @example
|
|
105
|
+
# Money.empty #=> #<Money @fractional=0>
|
|
106
|
+
def empty(currency = default_currency)
|
|
107
|
+
@empty ||= {}
|
|
108
|
+
@empty[currency] ||= Money.new(0, currency)
|
|
109
|
+
end
|
|
110
|
+
alias_method :zero, :empty
|
|
129
111
|
end
|
|
130
112
|
|
|
131
113
|
def self.setup_defaults
|
|
@@ -149,6 +131,9 @@ class Money
|
|
|
149
131
|
|
|
150
132
|
# Default the conversion of Rationals precision to 16
|
|
151
133
|
self.conversion_precision = 16
|
|
134
|
+
|
|
135
|
+
# Default alerting about deprecations
|
|
136
|
+
self.silence_core_extensions_deprecations = false
|
|
152
137
|
end
|
|
153
138
|
|
|
154
139
|
def self.inherited(base)
|
|
@@ -157,16 +142,27 @@ class Money
|
|
|
157
142
|
|
|
158
143
|
setup_defaults
|
|
159
144
|
|
|
160
|
-
#
|
|
145
|
+
# Use this to return the rounding mode. You may also pass a
|
|
146
|
+
# rounding mode and a block to temporatly change it. It will
|
|
147
|
+
# then return the results of the block instead.
|
|
161
148
|
#
|
|
162
|
-
# @param [
|
|
149
|
+
# @param [BigDecimal::ROUND_MODE] optional
|
|
163
150
|
#
|
|
164
|
-
# @return [
|
|
151
|
+
# @return [BigDecimal::ROUND_MODE,Yield] rounding mode or block results
|
|
165
152
|
#
|
|
166
153
|
# @example
|
|
167
|
-
#
|
|
168
|
-
|
|
169
|
-
|
|
154
|
+
# fee = Money.rounding_mode(BigDecimal::ROUND_HALF_UP) do
|
|
155
|
+
# Money.new(1200) * BigDecimal.new('0.029')
|
|
156
|
+
# end
|
|
157
|
+
def self.rounding_mode(mode=nil)
|
|
158
|
+
if mode.nil?
|
|
159
|
+
Thread.current[:money_rounding_mode] || @rounding_mode
|
|
160
|
+
else
|
|
161
|
+
Thread.current[:money_rounding_mode] = mode
|
|
162
|
+
yield
|
|
163
|
+
end
|
|
164
|
+
ensure
|
|
165
|
+
Thread.current[:money_rounding_mode] = nil
|
|
170
166
|
end
|
|
171
167
|
|
|
172
168
|
# Creates a new Money object of the given value, using the Canadian
|
|
@@ -234,10 +230,9 @@ class Money
|
|
|
234
230
|
# @see Money.new
|
|
235
231
|
#
|
|
236
232
|
def self.new_with_amount(amount, currency = Money.default_currency, bank = Money.default_bank)
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
money
|
|
233
|
+
from_numeric(amount, currency).tap do |money|
|
|
234
|
+
money.instance_variable_set("@bank", bank) # Hack! You can't change a bank
|
|
235
|
+
end
|
|
241
236
|
end
|
|
242
237
|
|
|
243
238
|
# Synonym of #new_with_amount
|
|
@@ -286,17 +281,14 @@ class Money
|
|
|
286
281
|
#
|
|
287
282
|
# @see Money.new_with_dollars
|
|
288
283
|
#
|
|
289
|
-
def initialize(
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
@currency = Currency.wrap(currency)
|
|
298
|
-
@bank = bank
|
|
299
|
-
end
|
|
284
|
+
def initialize(obj, currency = Money.default_currency, bank = Money.default_bank)
|
|
285
|
+
@fractional = obj.fractional
|
|
286
|
+
@currency = obj.currency
|
|
287
|
+
@bank = obj.bank
|
|
288
|
+
rescue NoMethodError
|
|
289
|
+
@fractional = as_d(obj)
|
|
290
|
+
@currency = Currency.wrap(currency)
|
|
291
|
+
@bank = bank
|
|
300
292
|
end
|
|
301
293
|
|
|
302
294
|
# Assuming using a currency using dollars:
|
|
@@ -391,41 +383,19 @@ class Money
|
|
|
391
383
|
# @example
|
|
392
384
|
# Money.ca_dollar(100).to_s #=> "1.00"
|
|
393
385
|
def to_s
|
|
394
|
-
unit, subunit =
|
|
395
|
-
|
|
396
|
-
unit_str = ""
|
|
397
|
-
subunit_str = ""
|
|
398
|
-
fraction_str = ""
|
|
399
|
-
|
|
400
|
-
if self.class.infinite_precision
|
|
401
|
-
subunit, fraction = subunit.divmod(BigDecimal("1"))
|
|
402
|
-
|
|
403
|
-
unit_str = unit.to_i.to_s
|
|
404
|
-
subunit_str = subunit.to_i.to_s
|
|
405
|
-
fraction_str = fraction.to_s("F")[2..-1] # want fractional part "0.xxx"
|
|
406
|
-
|
|
407
|
-
fraction_str = "" if fraction_str =~ /^0+$/
|
|
408
|
-
else
|
|
409
|
-
unit_str, subunit_str = unit.to_s, subunit.to_s
|
|
410
|
-
end
|
|
411
|
-
|
|
412
|
-
absolute_str = if currency.decimal_places == 0
|
|
413
|
-
if fraction_str == ""
|
|
414
|
-
unit_str
|
|
415
|
-
else
|
|
416
|
-
"#{unit_str}#{decimal_mark}#{fraction_str}"
|
|
417
|
-
end
|
|
418
|
-
else
|
|
419
|
-
# need to pad subunit to right position,
|
|
420
|
-
# for example 1 usd 3 cents should be 1.03 not 1.3
|
|
421
|
-
subunit_str.insert(0, '0') while subunit_str.length < currency.decimal_places
|
|
386
|
+
unit, subunit, fraction = strings_from_fractional
|
|
422
387
|
|
|
423
|
-
|
|
424
|
-
|
|
388
|
+
str = if currency.decimal_places == 0
|
|
389
|
+
if fraction == ""
|
|
390
|
+
unit
|
|
391
|
+
else
|
|
392
|
+
"#{unit}#{decimal_mark}#{fraction}"
|
|
393
|
+
end
|
|
394
|
+
else
|
|
395
|
+
"#{unit}#{decimal_mark}#{pad_subunit(subunit)}#{fraction}"
|
|
396
|
+
end
|
|
425
397
|
|
|
426
|
-
|
|
427
|
-
str.insert(0, "-") if fractional() < 0
|
|
428
|
-
end
|
|
398
|
+
fractional < 0 ? "-#{str}" : str
|
|
429
399
|
end
|
|
430
400
|
|
|
431
401
|
# Return the amount of money as a BigDecimal.
|
|
@@ -455,7 +425,7 @@ class Money
|
|
|
455
425
|
#
|
|
456
426
|
# @return [self]
|
|
457
427
|
def to_money(given_currency = nil)
|
|
458
|
-
given_currency = Currency.wrap(given_currency)
|
|
428
|
+
given_currency = Currency.wrap(given_currency)
|
|
459
429
|
if given_currency.nil? || self.currency == given_currency
|
|
460
430
|
self
|
|
461
431
|
else
|
|
@@ -527,23 +497,13 @@ class Money
|
|
|
527
497
|
# Money.new(100, "USD").allocate([0.33, 0.33, 0.33]) #=> [Money.new(34), Money.new(33), Money.new(33)]
|
|
528
498
|
#
|
|
529
499
|
def allocate(splits)
|
|
530
|
-
allocations = splits
|
|
500
|
+
allocations = allocations_from_splits(splits)
|
|
531
501
|
|
|
532
502
|
if (allocations - BigDecimal("1")) > Float::EPSILON
|
|
533
503
|
raise ArgumentError, "splits add to more then 100%"
|
|
534
504
|
end
|
|
535
505
|
|
|
536
|
-
left_over =
|
|
537
|
-
|
|
538
|
-
amounts = splits.map do |ratio|
|
|
539
|
-
if self.class.infinite_precision
|
|
540
|
-
fraction = fractional * ratio
|
|
541
|
-
else
|
|
542
|
-
fraction = (fractional * ratio / allocations).floor
|
|
543
|
-
left_over -= fraction
|
|
544
|
-
fraction
|
|
545
|
-
end
|
|
546
|
-
end
|
|
506
|
+
amounts, left_over = amounts_from_splits(allocations, splits)
|
|
547
507
|
|
|
548
508
|
unless self.class.infinite_precision
|
|
549
509
|
left_over.to_i.times { |i| amounts[i % amounts.length] += 1 }
|
|
@@ -564,21 +524,10 @@ class Money
|
|
|
564
524
|
raise ArgumentError, "need at least one party" if num < 1
|
|
565
525
|
|
|
566
526
|
if self.class.infinite_precision
|
|
567
|
-
|
|
568
|
-
|
|
569
|
-
|
|
570
|
-
|
|
571
|
-
low = Money.new(fractional / num, currency)
|
|
572
|
-
high = Money.new(low.fractional + 1, currency)
|
|
573
|
-
|
|
574
|
-
remainder = fractional % num
|
|
575
|
-
result = []
|
|
576
|
-
|
|
577
|
-
num.times do |index|
|
|
578
|
-
result[index] = index < remainder ? high : low
|
|
527
|
+
split_infinite(num)
|
|
528
|
+
else
|
|
529
|
+
split_flat(num)
|
|
579
530
|
end
|
|
580
|
-
|
|
581
|
-
result
|
|
582
531
|
end
|
|
583
532
|
|
|
584
533
|
# Round the monetary amount to smallest unit of coinage.
|
|
@@ -598,10 +547,85 @@ class Money
|
|
|
598
547
|
#
|
|
599
548
|
def round(rounding_mode = self.class.rounding_mode)
|
|
600
549
|
if self.class.infinite_precision
|
|
601
|
-
|
|
550
|
+
Money.new(fractional.round(0, rounding_mode), self.currency)
|
|
551
|
+
else
|
|
552
|
+
self
|
|
553
|
+
end
|
|
554
|
+
end
|
|
555
|
+
|
|
556
|
+
private
|
|
557
|
+
|
|
558
|
+
def as_d(num)
|
|
559
|
+
if num.is_a?(Rational)
|
|
560
|
+
num.to_d(self.class.conversion_precision)
|
|
561
|
+
else
|
|
562
|
+
num.to_d
|
|
563
|
+
end
|
|
564
|
+
rescue NoMethodError
|
|
565
|
+
BigDecimal.new(num.to_s)
|
|
566
|
+
end
|
|
567
|
+
|
|
568
|
+
def strings_from_fractional
|
|
569
|
+
unit, subunit = fractional().abs.divmod(currency.subunit_to_unit)
|
|
570
|
+
|
|
571
|
+
if self.class.infinite_precision
|
|
572
|
+
strings_for_infinite_precision(unit, subunit)
|
|
602
573
|
else
|
|
603
|
-
|
|
574
|
+
strings_for_base_precision(unit, subunit)
|
|
575
|
+
end
|
|
576
|
+
end
|
|
577
|
+
|
|
578
|
+
def strings_for_infinite_precision(unit, subunit)
|
|
579
|
+
subunit, fraction = subunit.divmod(BigDecimal("1"))
|
|
580
|
+
fraction = fraction.to_s("F")[2..-1] # want fractional part "0.xxx"
|
|
581
|
+
fraction = "" if fraction =~ /^0+$/
|
|
582
|
+
|
|
583
|
+
[unit.to_i.to_s, subunit.to_i.to_s, fraction]
|
|
584
|
+
end
|
|
585
|
+
|
|
586
|
+
def strings_for_base_precision(unit, subunit)
|
|
587
|
+
[unit.to_s, subunit.to_s, ""]
|
|
588
|
+
end
|
|
589
|
+
|
|
590
|
+
def pad_subunit(subunit)
|
|
591
|
+
cnt = currency.decimal_places
|
|
592
|
+
padding = "0" * cnt
|
|
593
|
+
"#{padding}#{subunit}"[-1 * cnt, cnt]
|
|
594
|
+
end
|
|
595
|
+
|
|
596
|
+
def allocations_from_splits(splits)
|
|
597
|
+
splits.inject(0) { |sum, n| sum + as_d(n) }
|
|
598
|
+
end
|
|
599
|
+
|
|
600
|
+
def amounts_from_splits(allocations, splits)
|
|
601
|
+
left_over = fractional
|
|
602
|
+
|
|
603
|
+
amounts = splits.map do |ratio|
|
|
604
|
+
if self.class.infinite_precision
|
|
605
|
+
fractional * ratio
|
|
606
|
+
else
|
|
607
|
+
(fractional * ratio / allocations).floor.tap do |frac|
|
|
608
|
+
left_over -= frac
|
|
609
|
+
end
|
|
610
|
+
end
|
|
604
611
|
end
|
|
612
|
+
|
|
613
|
+
[amounts, left_over]
|
|
605
614
|
end
|
|
606
615
|
|
|
616
|
+
def split_infinite(num)
|
|
617
|
+
amt = div(as_d(num))
|
|
618
|
+
1.upto(num).map{amt}
|
|
619
|
+
end
|
|
620
|
+
|
|
621
|
+
def split_flat(num)
|
|
622
|
+
low = Money.new(fractional / num, currency)
|
|
623
|
+
high = Money.new(low.fractional + 1, currency)
|
|
624
|
+
|
|
625
|
+
remainder = fractional % num
|
|
626
|
+
|
|
627
|
+
Array.new(num).each_with_index.map do |_, index|
|
|
628
|
+
index < remainder ? high : low
|
|
629
|
+
end
|
|
630
|
+
end
|
|
607
631
|
end
|
data/lib/money.rb
CHANGED
|
@@ -1,7 +1,9 @@
|
|
|
1
1
|
require "bigdecimal"
|
|
2
2
|
require "bigdecimal/util"
|
|
3
|
-
require "i18n"
|
|
3
|
+
require "i18n"
|
|
4
4
|
require "money/currency"
|
|
5
5
|
require "money/money"
|
|
6
|
-
require "money/core_extensions"
|
|
6
|
+
require "money/core_extensions/numeric"
|
|
7
|
+
require "money/core_extensions/string"
|
|
8
|
+
require "money/core_extensions/symbol"
|
|
7
9
|
require "money/deprecations"
|
data/money.gemspec
CHANGED
|
@@ -1,16 +1,20 @@
|
|
|
1
1
|
# -*- encoding: utf-8 -*-
|
|
2
|
+
lib = File.expand_path('../lib', __FILE__)
|
|
3
|
+
$LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
|
|
4
|
+
require "money/version"
|
|
5
|
+
|
|
2
6
|
Gem::Specification.new do |s|
|
|
3
7
|
s.name = "money"
|
|
4
|
-
s.version =
|
|
8
|
+
s.version = Money::VERSION
|
|
5
9
|
s.platform = Gem::Platform::RUBY
|
|
6
|
-
s.authors = ["
|
|
7
|
-
|
|
8
|
-
s.
|
|
9
|
-
s.
|
|
10
|
-
s.
|
|
11
|
-
s.
|
|
12
|
-
|
|
13
|
-
s.post_install_message =
|
|
10
|
+
s.authors = ["Shane Emmons"]
|
|
11
|
+
s.email = ["shane@emmons.io"]
|
|
12
|
+
s.homepage = "http://rubymoney.github.io/money"
|
|
13
|
+
s.summary = "A Ruby Library for dealing with money and currency conversion."
|
|
14
|
+
s.description = "A Ruby Library for dealing with money and currency conversion."
|
|
15
|
+
s.license = "MIT"
|
|
16
|
+
|
|
17
|
+
s.post_install_message = <<MSG
|
|
14
18
|
Please note the following API changes in Money version 6
|
|
15
19
|
|
|
16
20
|
- Money#amount, Money#dollars methods now return instances of BigDecimal (rather than Float).
|
|
@@ -19,21 +23,19 @@ Please read the migration notes at https://github.com/RubyMoney/money#migration-
|
|
|
19
23
|
and choose the migration that best suits your application.
|
|
20
24
|
|
|
21
25
|
Test responsibly :-)
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
s.required_ruby_version = ">= 1.8.7"
|
|
25
|
-
|
|
26
|
-
s.add_dependency "i18n", "~> 0.6.4"
|
|
27
|
-
|
|
28
|
-
s.add_development_dependency "rspec", "~> 2.14"
|
|
29
|
-
s.add_development_dependency "yard", "~> 0.8"
|
|
30
|
-
s.add_development_dependency "kramdown", "~> 1.1"
|
|
26
|
+
MSG
|
|
31
27
|
|
|
32
|
-
s.
|
|
28
|
+
s.add_dependency "i18n", "~> 0.6.4"
|
|
29
|
+
s.add_dependency "monetize", "~> 0.1.3"
|
|
33
30
|
|
|
34
|
-
s.
|
|
35
|
-
s.
|
|
36
|
-
s.
|
|
31
|
+
s.add_development_dependency "bundler", "~> 1.3"
|
|
32
|
+
s.add_development_dependency "rake"
|
|
33
|
+
s.add_development_dependency "rspec", "~> 2.14"
|
|
34
|
+
s.add_development_dependency "yard", "~> 0.8"
|
|
35
|
+
s.add_development_dependency "kramdown", "~> 1.1"
|
|
37
36
|
|
|
38
|
-
s.
|
|
37
|
+
s.files = `git ls-files`.split($/)
|
|
38
|
+
s.executables = s.files.grep(%r{^bin/}) { |f| File.basename(f) }
|
|
39
|
+
s.test_files = s.files.grep(%r{^(test|spec|features)/})
|
|
40
|
+
s.require_paths = ["lib"]
|
|
39
41
|
end
|
|
@@ -96,6 +96,16 @@ describe Money::Bank::VariableExchange do
|
|
|
96
96
|
it "raises an UnknownCurrency exception when an unknown currency is passed" do
|
|
97
97
|
expect { subject.set_rate('AAA', 'BBB', 1.25) }.to raise_exception(Money::Currency::UnknownCurrency)
|
|
98
98
|
end
|
|
99
|
+
|
|
100
|
+
it "uses a mutex by default" do
|
|
101
|
+
subject.instance_variable_get(:@mutex).should_receive(:synchronize)
|
|
102
|
+
subject.set_rate('USD', 'EUR', 1.25)
|
|
103
|
+
end
|
|
104
|
+
|
|
105
|
+
it "doesn't use mutex if requested not to" do
|
|
106
|
+
subject.instance_variable_get(:@mutex).should_not_receive(:synchronize)
|
|
107
|
+
subject.set_rate('USD', 'EUR', 1.25, :without_mutex => true)
|
|
108
|
+
end
|
|
99
109
|
end
|
|
100
110
|
|
|
101
111
|
describe "#get_rate" do
|
|
@@ -107,6 +117,16 @@ describe Money::Bank::VariableExchange do
|
|
|
107
117
|
it "raises an UnknownCurrency exception when an unknown currency is passed" do
|
|
108
118
|
expect { subject.get_rate('AAA', 'BBB') }.to raise_exception(Money::Currency::UnknownCurrency)
|
|
109
119
|
end
|
|
120
|
+
|
|
121
|
+
it "uses a mutex by default" do
|
|
122
|
+
subject.instance_variable_get(:@mutex).should_receive(:synchronize)
|
|
123
|
+
subject.get_rate('USD', 'EUR')
|
|
124
|
+
end
|
|
125
|
+
|
|
126
|
+
it "doesn't use mutex if requested not to" do
|
|
127
|
+
subject.instance_variable_get(:@mutex).should_not_receive(:synchronize)
|
|
128
|
+
subject.get_rate('USD', 'EUR', :without_mutex => true)
|
|
129
|
+
end
|
|
110
130
|
end
|
|
111
131
|
|
|
112
132
|
describe "#export_rates" do
|
|
@@ -152,6 +172,16 @@ describe Money::Bank::VariableExchange do
|
|
|
152
172
|
subject.export_rates(:json, 'null')
|
|
153
173
|
end
|
|
154
174
|
end
|
|
175
|
+
|
|
176
|
+
it "uses a mutex by default" do
|
|
177
|
+
subject.instance_variable_get(:@mutex).should_receive(:synchronize)
|
|
178
|
+
subject.export_rates(:yaml)
|
|
179
|
+
end
|
|
180
|
+
|
|
181
|
+
it "doesn't use mutex if requested not to" do
|
|
182
|
+
subject.instance_variable_get(:@mutex).should_not_receive(:synchronize)
|
|
183
|
+
subject.export_rates(:yaml, nil, :without_mutex => true)
|
|
184
|
+
end
|
|
155
185
|
end
|
|
156
186
|
|
|
157
187
|
describe "#import_rates" do
|
|
@@ -187,6 +217,18 @@ describe Money::Bank::VariableExchange do
|
|
|
187
217
|
expect { subject.import_rates(:foo, "")}.to raise_error Money::Bank::UnknownRateFormat
|
|
188
218
|
end
|
|
189
219
|
end
|
|
220
|
+
|
|
221
|
+
it "uses a mutex by default" do
|
|
222
|
+
subject.instance_variable_get(:@mutex).should_receive(:synchronize)
|
|
223
|
+
s = "--- \nUSD_TO_EUR: 1.25\nUSD_TO_JPY: 2.55\n"
|
|
224
|
+
subject.import_rates(:yaml, s)
|
|
225
|
+
end
|
|
226
|
+
|
|
227
|
+
it "doesn't use mutex if requested not to" do
|
|
228
|
+
subject.instance_variable_get(:@mutex).should_not_receive(:synchronize)
|
|
229
|
+
s = "--- \nUSD_TO_EUR: 1.25\nUSD_TO_JPY: 2.55\n"
|
|
230
|
+
subject.import_rates(:yaml, s, :without_mutex => true)
|
|
231
|
+
end
|
|
190
232
|
end
|
|
191
233
|
|
|
192
234
|
describe "#rate_key_for" do
|
|
@@ -110,8 +110,8 @@ describe Money, "core extensions" do
|
|
|
110
110
|
"10.10 USD".to_money('USD').should == Money.new(1010, 'USD')
|
|
111
111
|
end
|
|
112
112
|
|
|
113
|
-
it "
|
|
114
|
-
expect
|
|
113
|
+
it "uses the parsed currency even if a currency was specified by the method" do
|
|
114
|
+
expect("10.10 USD".to_money("EUR")).to eq Money.new(1010, "USD")
|
|
115
115
|
end
|
|
116
116
|
|
|
117
117
|
it "ignores unrecognized data" do
|
data/spec/currency_spec.rb
CHANGED
|
@@ -112,6 +112,8 @@ describe Money::Currency do
|
|
|
112
112
|
|
|
113
113
|
it "allows direct comparison of currencies and symbols/strings" do
|
|
114
114
|
Money::Currency.new(:eur).should == 'eur'
|
|
115
|
+
Money::Currency.new(:eur).should == 'EUR'
|
|
116
|
+
Money::Currency.new(:eur).should == :eur
|
|
115
117
|
Money::Currency.new(:eur).should == :EUR
|
|
116
118
|
Money::Currency.new(:eur).should_not == 'usd'
|
|
117
119
|
end
|
|
@@ -154,6 +156,20 @@ describe Money::Currency do
|
|
|
154
156
|
end
|
|
155
157
|
end
|
|
156
158
|
|
|
159
|
+
describe "#to_str" do
|
|
160
|
+
it "works as documented" do
|
|
161
|
+
Money::Currency.new(:usd).to_str.should == "USD"
|
|
162
|
+
Money::Currency.new(:eur).to_str.should == "EUR"
|
|
163
|
+
end
|
|
164
|
+
end
|
|
165
|
+
|
|
166
|
+
describe "#to_sym" do
|
|
167
|
+
it "works as documented" do
|
|
168
|
+
Money::Currency.new(:usd).to_sym.should == :USD
|
|
169
|
+
Money::Currency.new(:eur).to_sym.should == :EUR
|
|
170
|
+
end
|
|
171
|
+
end
|
|
172
|
+
|
|
157
173
|
describe "#to_currency" do
|
|
158
174
|
it "works as documented" do
|
|
159
175
|
usd = Money::Currency.new(:usd)
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
# encoding: utf-8
|
|
2
|
+
|
|
3
|
+
require "spec_helper"
|
|
4
|
+
|
|
5
|
+
describe Money do
|
|
6
|
+
describe "::deprecate" do
|
|
7
|
+
it "should send a deprecation message with caller" do
|
|
8
|
+
error_message = "Deprecated method triggered here"
|
|
9
|
+
|
|
10
|
+
Money.should_receive(:warn).with do |message|
|
|
11
|
+
message.should =~ /DEPRECATION WARNING: #{error_message} \(called from:.*:\d+\)/
|
|
12
|
+
end
|
|
13
|
+
|
|
14
|
+
Money.deprecate(error_message)
|
|
15
|
+
end
|
|
16
|
+
|
|
17
|
+
context "when silenced" do
|
|
18
|
+
it "should not warn" do
|
|
19
|
+
Money.should_not_receive(:warn)
|
|
20
|
+
|
|
21
|
+
while_silenced { Money.deprecate("anything") }
|
|
22
|
+
end
|
|
23
|
+
end
|
|
24
|
+
end
|
|
25
|
+
|
|
26
|
+
describe "core extensions" do
|
|
27
|
+
it "does not print deprecations when silenced" do
|
|
28
|
+
while_silenced do
|
|
29
|
+
expect_no_deprecation_for { "$1.00".to_money }
|
|
30
|
+
expect_no_deprecation_for { "USD".to_currency }
|
|
31
|
+
expect_no_deprecation_for { 1.to_money }
|
|
32
|
+
expect_no_deprecation_for { :USD.to_currency }
|
|
33
|
+
end
|
|
34
|
+
end
|
|
35
|
+
|
|
36
|
+
def expect_no_deprecation_for(&block)
|
|
37
|
+
Money.should_not_receive(:warn)
|
|
38
|
+
yield
|
|
39
|
+
end
|
|
40
|
+
end
|
|
41
|
+
|
|
42
|
+
def while_silenced(&block)
|
|
43
|
+
begin
|
|
44
|
+
old_setting = Money.silence_core_extensions_deprecations
|
|
45
|
+
Money.silence_core_extensions_deprecations = true
|
|
46
|
+
yield
|
|
47
|
+
ensure
|
|
48
|
+
Money.silence_core_extensions_deprecations = old_setting
|
|
49
|
+
end
|
|
50
|
+
end
|
|
51
|
+
end
|
data/spec/money/parsing_spec.rb
CHANGED
|
@@ -8,7 +8,7 @@ describe Money, "parsing" do
|
|
|
8
8
|
eu4 = '{ "priority": 1, "iso_code": "EU4", "iso_numeric": "841", "name": "Euro with 4 decimal places", "symbol": "€", "subunit": "Cent", "subunit_to_unit": 10000, "symbol_first": true, "html_entity": "€", "decimal_mark": ",", "thousands_separator": "." }'
|
|
9
9
|
|
|
10
10
|
describe ".parse" do
|
|
11
|
-
it "is
|
|
11
|
+
it "is deprecated" do
|
|
12
12
|
Money.should_receive(:deprecate).at_least(1).times
|
|
13
13
|
Money.parse("1.95")
|
|
14
14
|
end
|
|
@@ -17,9 +17,6 @@ describe Money, "parsing" do
|
|
|
17
17
|
five_ninety_five = Money.new(595, 'EUR')
|
|
18
18
|
|
|
19
19
|
Money.parse('EUR 5,95').should == five_ninety_five
|
|
20
|
-
#TODO: try and handle these
|
|
21
|
-
#Money.parse('€5,95').should == five_ninety_five
|
|
22
|
-
#Money.parse('$5.95').should == five_ninety_five
|
|
23
20
|
end
|
|
24
21
|
|
|
25
22
|
it "parses european-formatted inputs with multiple thousands-seperators" do
|
|
@@ -291,9 +288,9 @@ describe Money, "parsing" do
|
|
|
291
288
|
end
|
|
292
289
|
|
|
293
290
|
it "optimizes workload" do
|
|
294
|
-
|
|
291
|
+
Monetize.should_receive(:from_fixnum).with(1, "USD").and_return(Money.new(1_00, "USD"))
|
|
295
292
|
Money.from_numeric(1, "USD").should == Money.new(1_00, "USD")
|
|
296
|
-
|
|
293
|
+
Monetize.should_receive(:from_bigdecimal).with(BigDecimal.new("1.0"), "USD").and_return(Money.new(1_00, "USD"))
|
|
297
294
|
Money.from_numeric(1.0, "USD").should == Money.new(1_00, "USD")
|
|
298
295
|
end
|
|
299
296
|
|