money 3.5.5 → 3.6.0
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.
- data/.gemtest +0 -0
- data/CHANGELOG.md +14 -0
- data/README.md +2 -1
- data/Rakefile +49 -0
- data/lib/money/currency.rb +1 -1
- data/lib/money/money.rb +17 -5
- data/money.gemspec +27 -0
- data/spec/bank/base_spec.rb +72 -0
- data/spec/bank/variable_exchange_spec.rb +222 -0
- data/spec/core_extensions_spec.rb +142 -0
- data/spec/currency_spec.rb +128 -0
- data/spec/money_spec.rb +1268 -0
- data/spec/spec_helper.rb +17 -0
- metadata +22 -6
@@ -0,0 +1,142 @@
|
|
1
|
+
require "spec_helper"
|
2
|
+
|
3
|
+
describe "Money core extensions" do
|
4
|
+
|
5
|
+
describe Numeric do
|
6
|
+
describe "#to_money" do
|
7
|
+
it "should work" do
|
8
|
+
money = 1234.to_money
|
9
|
+
money.cents.should == 1234_00
|
10
|
+
money.currency.should == Money.default_currency
|
11
|
+
|
12
|
+
money = 100.37.to_money
|
13
|
+
money.cents.should == 100_37
|
14
|
+
money.currency.should == Money.default_currency
|
15
|
+
|
16
|
+
money = BigDecimal.new('1234').to_money
|
17
|
+
money.cents.should == 1234_00
|
18
|
+
money.currency.should == Money.default_currency
|
19
|
+
end
|
20
|
+
|
21
|
+
it "should accept optional currency" do
|
22
|
+
1234.to_money('USD').should == Money.new(123400, 'USD')
|
23
|
+
1234.to_money('EUR').should == Money.new(123400, 'EUR')
|
24
|
+
end
|
25
|
+
|
26
|
+
it "should respect :subunit_to_unit currency property" do
|
27
|
+
10.to_money('USD').should == Money.new(10_00, 'USD')
|
28
|
+
10.to_money('TND').should == Money.new(10_000, 'TND')
|
29
|
+
10.to_money('CLP').should == Money.new(10, 'CLP')
|
30
|
+
end
|
31
|
+
|
32
|
+
specify "#issue/15" do
|
33
|
+
amount = 555.55.to_money
|
34
|
+
amount.should == Money.new(55555)
|
35
|
+
end
|
36
|
+
end
|
37
|
+
end
|
38
|
+
|
39
|
+
describe String do
|
40
|
+
describe "#to_money" do
|
41
|
+
|
42
|
+
StringToMoney = {
|
43
|
+
"20.15" => Money.new(20_15) ,
|
44
|
+
"100" => Money.new(100_00) ,
|
45
|
+
"100.37" => Money.new(100_37) ,
|
46
|
+
"100,37" => Money.new(100_37) ,
|
47
|
+
"100 000" => Money.new(100_000_00) ,
|
48
|
+
"100,000.00" => Money.new(100_000_00) ,
|
49
|
+
"1,000" => Money.new(1_000_00) ,
|
50
|
+
"-1,000" => Money.new(-1_000_00) ,
|
51
|
+
"1,000.5" => Money.new(1_000_50) ,
|
52
|
+
"1,000.51" => Money.new(1_000_51) ,
|
53
|
+
"1,000.505" => Money.new(1_000_51) ,
|
54
|
+
"1,000.504" => Money.new(1_000_50) ,
|
55
|
+
"1,000.0000" => Money.new(1_000_00) ,
|
56
|
+
"1,000.5000" => Money.new(1_000_50) ,
|
57
|
+
"1,000.5099" => Money.new(1_000_51) ,
|
58
|
+
"1.550" => Money.new(1_55) ,
|
59
|
+
"25." => Money.new(25_00) ,
|
60
|
+
".75" => Money.new(75) ,
|
61
|
+
|
62
|
+
"100 USD" => Money.new(100_00, "USD") ,
|
63
|
+
"-100 USD" => Money.new(-100_00, "USD") ,
|
64
|
+
"100 EUR" => Money.new(100_00, "EUR") ,
|
65
|
+
"100.37 EUR" => Money.new(100_37, "EUR") ,
|
66
|
+
"100,37 EUR" => Money.new(100_37, "EUR") ,
|
67
|
+
"100,000.00 USD" => Money.new(100_000_00, "USD") ,
|
68
|
+
"100.000,00 EUR" => Money.new(100_000_00, "EUR") ,
|
69
|
+
"1,000 USD" => Money.new(1_000_00, "USD") ,
|
70
|
+
"-1,000 USD" => Money.new(-1_000_00, "USD") ,
|
71
|
+
"1,000.5500 USD" => Money.new(1_000_55, "USD") ,
|
72
|
+
"-1,000.6500 USD" => Money.new(-1_000_65, "USD") ,
|
73
|
+
"1.550 USD" => Money.new(1_55, "USD") ,
|
74
|
+
|
75
|
+
"USD 100" => Money.new(100_00, "USD") ,
|
76
|
+
"EUR 100" => Money.new(100_00, "EUR") ,
|
77
|
+
"EUR 100.37" => Money.new(100_37, "EUR") ,
|
78
|
+
"CAD -100.37" => Money.new(-100_37, "CAD") ,
|
79
|
+
"EUR 100,37" => Money.new(100_37, "EUR") ,
|
80
|
+
"EUR -100,37" => Money.new(-100_37, "EUR") ,
|
81
|
+
"USD 100,000.00" => Money.new(100_000_00, "USD") ,
|
82
|
+
"EUR 100.000,00" => Money.new(100_000_00, "EUR") ,
|
83
|
+
"USD 1,000" => Money.new(1_000_00, "USD") ,
|
84
|
+
"USD -1,000" => Money.new(-1_000_00, "USD") ,
|
85
|
+
"USD 1,000.9000" => Money.new(1_000_90, "USD") ,
|
86
|
+
"USD -1,000.090" => Money.new(-1_000_09, "USD") ,
|
87
|
+
"USD 1.5500" => Money.new(1_55, "USD") ,
|
88
|
+
|
89
|
+
"$100 USD" => Money.new(100_00, "USD") ,
|
90
|
+
"$1,194.59 USD" => Money.new(1_194_59, "USD") ,
|
91
|
+
"$-1,955 USD" => Money.new(-1_955_00, "USD") ,
|
92
|
+
"$1,194.5900 USD" => Money.new(1_194_59, "USD") ,
|
93
|
+
"$-1,955.000 USD" => Money.new(-1_955_00, "USD") ,
|
94
|
+
"$1.99000 USD" => Money.new(1_99, "USD") ,
|
95
|
+
}
|
96
|
+
|
97
|
+
specify "it should work" do
|
98
|
+
StringToMoney.each do |string, money|
|
99
|
+
string.to_money.should == money
|
100
|
+
end
|
101
|
+
end
|
102
|
+
|
103
|
+
it "should coerce input to string" do
|
104
|
+
Money.parse(20, "USD").should == Money.new(20_00, "USD")
|
105
|
+
end
|
106
|
+
|
107
|
+
it "should accept optional currency" do
|
108
|
+
"10.10".to_money('USD').should == Money.new(1010, 'USD')
|
109
|
+
"10.10".to_money('EUR').should == Money.new(1010, 'EUR')
|
110
|
+
"10.10 USD".to_money('USD').should == Money.new(1010, 'USD')
|
111
|
+
end
|
112
|
+
|
113
|
+
it "should raise error if optional currency doesn't match string currency" do
|
114
|
+
lambda{ "10.10 USD".to_money('EUR') }.should raise_error(/Mismatching Currencies/)
|
115
|
+
end
|
116
|
+
|
117
|
+
it "should ignore unrecognized data" do
|
118
|
+
"hello 2000 world".to_money.should == Money.new(2000_00)
|
119
|
+
end
|
120
|
+
|
121
|
+
it "should respect :subunit_to_unit currency property" do
|
122
|
+
"1".to_money("USD").should == Money.new(1_00, "USD")
|
123
|
+
"1".to_money("TND").should == Money.new(1_000, "TND")
|
124
|
+
"1".to_money("CLP").should == Money.new(1, "CLP")
|
125
|
+
"1.5".to_money("KWD").cents.should == 1500
|
126
|
+
end
|
127
|
+
end
|
128
|
+
|
129
|
+
describe "#to_currency" do
|
130
|
+
it "should convert string to Currency" do
|
131
|
+
"USD".to_currency.should == Money::Currency.new(:usd)
|
132
|
+
"EUR".to_currency.should == Money::Currency.new(:eur)
|
133
|
+
end
|
134
|
+
|
135
|
+
it "should raise Money::Currency::UnknownCurrency with unknown Currency" do
|
136
|
+
lambda { "XXX".to_currency }.should raise_error(Money::Currency::UnknownCurrency)
|
137
|
+
lambda { " ".to_currency }.should raise_error(Money::Currency::UnknownCurrency)
|
138
|
+
end
|
139
|
+
end
|
140
|
+
end
|
141
|
+
|
142
|
+
end
|
@@ -0,0 +1,128 @@
|
|
1
|
+
# encoding: utf-8
|
2
|
+
|
3
|
+
require "spec_helper"
|
4
|
+
|
5
|
+
describe Money::Currency do
|
6
|
+
|
7
|
+
specify "#initialize should lookup data from TABLE" do
|
8
|
+
with_custom_definitions do
|
9
|
+
Money::Currency::TABLE[:usd] = {:priority => 1, :iso_code => "USD", :name => "United States Dollar", :symbol => "$", :subunit => "Cent", :subunit_to_unit => 100, :decimal_mark => ".", :thousands_separator => ","}
|
10
|
+
Money::Currency::TABLE[:eur] = {:priority => 2, :iso_code => "EUR", :name => "Euro", :symbol => "€", :subunit => "Cent", :subunit_to_unit => 100, :decimal_mark => ".", :thousands_separator => ","}
|
11
|
+
|
12
|
+
currency = Money::Currency.new("USD")
|
13
|
+
currency.id.should == :usd
|
14
|
+
currency.priority.should == 1
|
15
|
+
currency.iso_code.should == "USD"
|
16
|
+
currency.name.should == "United States Dollar"
|
17
|
+
currency.decimal_mark.should == "."
|
18
|
+
currency.separator.should == "."
|
19
|
+
currency.thousands_separator.should == ","
|
20
|
+
currency.delimiter.should == ","
|
21
|
+
end
|
22
|
+
end
|
23
|
+
|
24
|
+
specify "#initialize should raise UnknownMoney::Currency with unknown currency" do
|
25
|
+
lambda { Money::Currency.new("xxx") }.should raise_error(Money::Currency::UnknownCurrency, /xxx/)
|
26
|
+
end
|
27
|
+
|
28
|
+
specify "#== should return true if self === other" do
|
29
|
+
currency = Money::Currency.new(:eur)
|
30
|
+
currency.should == currency
|
31
|
+
end
|
32
|
+
|
33
|
+
specify "#== should return true if the id is equal" do
|
34
|
+
Money::Currency.new(:eur).should == Money::Currency.new(:eur)
|
35
|
+
Money::Currency.new(:eur).should_not == Money::Currency.new(:usd)
|
36
|
+
end
|
37
|
+
|
38
|
+
specify "#eql? should return true if #== returns true" do
|
39
|
+
Money::Currency.new(:eur).eql?(Money::Currency.new(:eur)).should be true
|
40
|
+
Money::Currency.new(:eur).eql?(Money::Currency.new(:usd)).should be false
|
41
|
+
end
|
42
|
+
|
43
|
+
specify "#hash should return the same value for equal objects" do
|
44
|
+
Money::Currency.new(:eur).hash.should == Money::Currency.new(:eur).hash
|
45
|
+
Money::Currency.new(:eur).hash.should_not == Money::Currency.new(:usd).hash
|
46
|
+
end
|
47
|
+
|
48
|
+
specify "#hash can be used to return the intersection of Currency object arrays" do
|
49
|
+
intersection = [Money::Currency.new(:eur), Money::Currency.new(:usd)] & [Money::Currency.new(:eur)]
|
50
|
+
intersection.should == [Money::Currency.new(:eur)]
|
51
|
+
end
|
52
|
+
|
53
|
+
specify "#<=> should compare objects by priority" do
|
54
|
+
Money::Currency.new(:cad).should > Money::Currency.new(:usd)
|
55
|
+
Money::Currency.new(:usd).should < Money::Currency.new(:eur)
|
56
|
+
end
|
57
|
+
|
58
|
+
specify "#to_s" do
|
59
|
+
Money::Currency.new(:usd).to_s.should == "USD"
|
60
|
+
Money::Currency.new(:eur).to_s.should == "EUR"
|
61
|
+
end
|
62
|
+
|
63
|
+
specify "#inspect" do
|
64
|
+
Money::Currency.new(:usd).inspect.should ==
|
65
|
+
%Q{#<Money::Currency id: usd, priority: 1, symbol_first: true, thousands_separator: ,, html_entity: $, decimal_mark: ., name: United States Dollar, symbol: $, subunit_to_unit: 100, iso_code: USD, subunit: Cent>}
|
66
|
+
end
|
67
|
+
|
68
|
+
|
69
|
+
specify "#self.find should return currency matching given id" do
|
70
|
+
with_custom_definitions do
|
71
|
+
Money::Currency::TABLE[:usd] = { :priority => 1, :iso_code => "USD", :name => "United States Dollar", :symbol => "$", :subunit => "Cent", :subunit_to_unit => 100, :decimal_mark => ".", :thousands_separator => "," }
|
72
|
+
Money::Currency::TABLE[:eur] = { :priority => 2, :iso_code => "EUR", :name => "Euro", :symbol => "€", :subunit => "Cent", :subunit_to_unit => 100, :decimal_mark => ".", :thousands_separator => "," }
|
73
|
+
|
74
|
+
expected = Money::Currency.new(:eur)
|
75
|
+
Money::Currency.find(:eur).should == expected
|
76
|
+
Money::Currency.find(:EUR).should == expected
|
77
|
+
Money::Currency.find("eur").should == expected
|
78
|
+
Money::Currency.find("EUR").should == expected
|
79
|
+
end
|
80
|
+
end
|
81
|
+
|
82
|
+
specify "#self.find should return nil unless currency matching given id" do
|
83
|
+
with_custom_definitions do
|
84
|
+
Money::Currency::TABLE[:usd] = { :position => 1, :iso_code => "USD", :name => "United States Dollar", :symbol => "$", :subunit => "Cent", :subunit_to_unit => 100, :decimal_mark => ".", :thousands_separator => "," }
|
85
|
+
Money::Currency::TABLE[:eur] = { :position => 2, :iso_code => "EUR", :name => "Euro", :symbol => "€", :subunit => "Cent", :subunit_to_unit => 100, :decimal_mark => ".", :thousands_separator => "," }
|
86
|
+
|
87
|
+
expected = Money::Currency.new(:eur)
|
88
|
+
Money::Currency.find(:eur).should == expected
|
89
|
+
Money::Currency.find(:EUR).should == expected
|
90
|
+
Money::Currency.find("eur").should == expected
|
91
|
+
Money::Currency.find("EUR").should == expected
|
92
|
+
end
|
93
|
+
end
|
94
|
+
|
95
|
+
specify "#self.wrap should return nil if object is nil" do
|
96
|
+
Money::Currency.wrap(nil).should == nil
|
97
|
+
Money::Currency.wrap(Money::Currency.new(:usd)).should == Money::Currency.new(:usd)
|
98
|
+
Money::Currency.wrap(:usd).should == Money::Currency.new(:usd)
|
99
|
+
end
|
100
|
+
|
101
|
+
|
102
|
+
def with_custom_definitions(&block)
|
103
|
+
begin
|
104
|
+
old = Money::Currency::TABLE.dup
|
105
|
+
Money::Currency::TABLE.clear
|
106
|
+
yield
|
107
|
+
ensure
|
108
|
+
silence_warnings do
|
109
|
+
Money::Currency.const_set("TABLE", old)
|
110
|
+
end
|
111
|
+
end
|
112
|
+
end
|
113
|
+
|
114
|
+
# Sets $VERBOSE to nil for the duration of the block and back to its original value afterwards.
|
115
|
+
#
|
116
|
+
# silence_warnings do
|
117
|
+
# value = noisy_call # no warning voiced
|
118
|
+
# end
|
119
|
+
#
|
120
|
+
# noisy_call # warning voiced
|
121
|
+
def silence_warnings
|
122
|
+
old_verbose, $VERBOSE = $VERBOSE, nil
|
123
|
+
yield
|
124
|
+
ensure
|
125
|
+
$VERBOSE = old_verbose
|
126
|
+
end
|
127
|
+
|
128
|
+
end
|
data/spec/money_spec.rb
ADDED
@@ -0,0 +1,1268 @@
|
|
1
|
+
# encoding: utf-8
|
2
|
+
|
3
|
+
require "spec_helper"
|
4
|
+
|
5
|
+
describe Money do
|
6
|
+
|
7
|
+
describe "#new" do
|
8
|
+
it "rounds the given cents to an integer" do
|
9
|
+
Money.new(1.00, "USD").cents.should == 1
|
10
|
+
Money.new(1.01, "USD").cents.should == 1
|
11
|
+
Money.new(1.50, "USD").cents.should == 2
|
12
|
+
end
|
13
|
+
|
14
|
+
it "is associated to the singleton instance of Bank::VariableExchange by default" do
|
15
|
+
Money.new(0).bank.should be_equal(Money::Bank::VariableExchange.instance)
|
16
|
+
end
|
17
|
+
|
18
|
+
end
|
19
|
+
|
20
|
+
describe "#cents" do
|
21
|
+
it "returns the amount of cents passed to the constructor" do
|
22
|
+
Money.new(200_00, "USD").cents.should == 200_00
|
23
|
+
end
|
24
|
+
|
25
|
+
it "stores cents as an integer regardless of what is passed into the constructor" do
|
26
|
+
[ Money.new(100), 1.to_money, 1.00.to_money, BigDecimal('1.00').to_money ].each do |m|
|
27
|
+
m.cents.should == 100
|
28
|
+
m.cents.should be_an_instance_of(Fixnum)
|
29
|
+
end
|
30
|
+
end
|
31
|
+
end
|
32
|
+
|
33
|
+
describe "#dollars" do
|
34
|
+
it "gets cents as dollars" do
|
35
|
+
Money.new_with_dollars(1).should == Money.new(100)
|
36
|
+
Money.new_with_dollars(1, "USD").should == Money.new(100, "USD")
|
37
|
+
Money.new_with_dollars(1, "EUR").should == Money.new(100, "EUR")
|
38
|
+
end
|
39
|
+
|
40
|
+
it "should respect :subunit_to_unit currency property" do
|
41
|
+
Money.new(1_00, "USD").dollars.should == 1
|
42
|
+
Money.new(1_000, "TND").dollars.should == 1
|
43
|
+
Money.new(1, "CLP").dollars.should == 1
|
44
|
+
end
|
45
|
+
|
46
|
+
it "should not loose precision" do
|
47
|
+
Money.new(100_37).dollars.should == 100.37
|
48
|
+
Money.new_with_dollars(100.37).dollars.should == 100.37
|
49
|
+
end
|
50
|
+
end
|
51
|
+
|
52
|
+
specify "#currency returns the currency passed to the constructor" do
|
53
|
+
Money.new(200_00, "USD").currency.should == Money::Currency.new("USD")
|
54
|
+
end
|
55
|
+
|
56
|
+
specify "#currency_string returns the iso_code of the currency object" do
|
57
|
+
Money.new(200_00, "USD").currency_as_string.should == Money::Currency.new("USD").to_s
|
58
|
+
Money.new(200_00, "USD").currency_as_string.should == "USD"
|
59
|
+
Money.new(200_00, "EUR").currency_as_string.should == "EUR"
|
60
|
+
Money.new(200_00, "YEN").currency_as_string.should == "YEN"
|
61
|
+
end
|
62
|
+
|
63
|
+
specify "#currency_string= set the currency object using the provided string" do
|
64
|
+
obj = Money.new(200_00, "USD")
|
65
|
+
obj.currency_as_string = "EUR"
|
66
|
+
obj.currency.should == Money::Currency.new("EUR")
|
67
|
+
obj.currency_as_string = "YEN"
|
68
|
+
obj.currency.should == Money::Currency.new("YEN")
|
69
|
+
obj.currency_as_string = "USD"
|
70
|
+
obj.currency.should == Money::Currency.new("USD")
|
71
|
+
end
|
72
|
+
|
73
|
+
specify "#zero? returns whether the amount is 0" do
|
74
|
+
Money.new(0, "USD").should be_zero
|
75
|
+
Money.new(0, "EUR").should be_zero
|
76
|
+
Money.new(1, "USD").should_not be_zero
|
77
|
+
Money.new(10, "YEN").should_not be_zero
|
78
|
+
Money.new(-1, "EUR").should_not be_zero
|
79
|
+
end
|
80
|
+
|
81
|
+
specify "#nonzero? returns whether the amount is not 0" do
|
82
|
+
Money.new(0, "USD").should_not be_nonzero
|
83
|
+
Money.new(0, "EUR").should_not be_nonzero
|
84
|
+
Money.new(1, "USD").should be_nonzero
|
85
|
+
Money.new(10, "YEN").should be_nonzero
|
86
|
+
Money.new(-1, "EUR").should be_nonzero
|
87
|
+
end
|
88
|
+
|
89
|
+
specify "#nonzero? has the same return-value semantics as Numeric#nonzero?" do
|
90
|
+
Money.new(0, "USD").nonzero?.should be_nil
|
91
|
+
|
92
|
+
money = Money.new(1, "USD")
|
93
|
+
money.nonzero?.should be_equal(money)
|
94
|
+
end
|
95
|
+
|
96
|
+
specify "#exchange_to exchanges the amount via its exchange bank" do
|
97
|
+
money = Money.new(100_00, "USD")
|
98
|
+
money.bank.should_receive(:exchange_with).with(Money.new(100_00, Money::Currency.new("USD")), Money::Currency.new("EUR")).and_return(Money.new(200_00, Money::Currency.new('EUR')))
|
99
|
+
money.exchange_to("EUR")
|
100
|
+
end
|
101
|
+
|
102
|
+
specify "#exchange_to exchanges the amount properly" do
|
103
|
+
money = Money.new(100_00, "USD")
|
104
|
+
money.bank.should_receive(:exchange_with).with(Money.new(100_00, Money::Currency.new("USD")), Money::Currency.new("EUR")).and_return(Money.new(200_00, Money::Currency.new('EUR')))
|
105
|
+
money.exchange_to("EUR").should == Money.new(200_00, "EUR")
|
106
|
+
end
|
107
|
+
|
108
|
+
specify "#== returns true if and only if their amount and currency are equal" do
|
109
|
+
Money.new(1_00, "USD").should == Money.new(1_00, "USD")
|
110
|
+
Money.new(1_00, "USD").should_not == Money.new(1_00, "EUR")
|
111
|
+
Money.new(1_00, "USD").should_not == Money.new(2_00, "USD")
|
112
|
+
Money.new(1_00, "USD").should_not == Money.new(99_00, "EUR")
|
113
|
+
end
|
114
|
+
|
115
|
+
specify "#== can be used to compare with a String money value" do
|
116
|
+
Money.new(1_00, "USD").should == "1.00"
|
117
|
+
Money.new(1_00, "USD").should_not == "2.00"
|
118
|
+
Money.new(1_00, "GBP").should_not == "1.00"
|
119
|
+
end
|
120
|
+
|
121
|
+
specify "#== can be used to compare with a Numeric money value" do
|
122
|
+
Money.new(1_00, "USD").should == 1
|
123
|
+
Money.new(1_57, "USD").should == 1.57
|
124
|
+
Money.new(1_00, "USD").should_not == 2
|
125
|
+
Money.new(1_00, "GBP").should_not == 1
|
126
|
+
end
|
127
|
+
|
128
|
+
specify "#== can be used to compare with an object that responds to #to_money" do
|
129
|
+
klass = Class.new do
|
130
|
+
def initialize(money)
|
131
|
+
@money = money
|
132
|
+
end
|
133
|
+
|
134
|
+
def to_money
|
135
|
+
@money
|
136
|
+
end
|
137
|
+
end
|
138
|
+
|
139
|
+
Money.new(1_00, "USD").should == klass.new(Money.new(1_00, "USD"))
|
140
|
+
Money.new(2_50, "USD").should == klass.new(Money.new(2_50, "USD"))
|
141
|
+
Money.new(2_50, "USD").should_not == klass.new(Money.new(3_00, "USD"))
|
142
|
+
Money.new(1_00, "GBP").should_not == klass.new(Money.new(1_00, "USD"))
|
143
|
+
end
|
144
|
+
|
145
|
+
specify "#== returns false if used to compare with an object that doesn't respond to #to_money" do
|
146
|
+
Money.new(1_00, "USD").should_not == Object.new
|
147
|
+
Money.new(1_00, "USD").should_not == Class
|
148
|
+
Money.new(1_00, "USD").should_not == Kernel
|
149
|
+
Money.new(1_00, "USD").should_not == /foo/
|
150
|
+
Money.new(1_00, "USD").should_not == nil
|
151
|
+
end
|
152
|
+
|
153
|
+
specify "#eql? returns true if and only if their amount and currency are equal" do
|
154
|
+
Money.new(1_00, "USD").eql?(Money.new(1_00, "USD")).should be true
|
155
|
+
Money.new(1_00, "USD").eql?(Money.new(1_00, "EUR")).should be false
|
156
|
+
Money.new(1_00, "USD").eql?(Money.new(2_00, "USD")).should be false
|
157
|
+
Money.new(1_00, "USD").eql?(Money.new(99_00, "EUR")).should be false
|
158
|
+
end
|
159
|
+
|
160
|
+
specify "#eql? can be used to compare with a String money value" do
|
161
|
+
Money.new(1_00, "USD").eql?("1.00").should be true
|
162
|
+
Money.new(1_00, "USD").eql?("2.00").should be false
|
163
|
+
Money.new(1_00, "GBP").eql?("1.00").should be false
|
164
|
+
end
|
165
|
+
|
166
|
+
specify "#eql? can be used to compare with a Numeric money value" do
|
167
|
+
Money.new(1_00, "USD").eql?(1).should be true
|
168
|
+
Money.new(1_57, "USD").eql?(1.57).should be true
|
169
|
+
Money.new(1_00, "USD").eql?(2).should be false
|
170
|
+
Money.new(1_00, "GBP").eql?(1).should be false
|
171
|
+
end
|
172
|
+
|
173
|
+
specify "#eql? can be used to compare with an object that responds to #to_money" do
|
174
|
+
klass = Class.new do
|
175
|
+
def initialize(money)
|
176
|
+
@money = money
|
177
|
+
end
|
178
|
+
|
179
|
+
def to_money
|
180
|
+
@money
|
181
|
+
end
|
182
|
+
end
|
183
|
+
|
184
|
+
Money.new(1_00, "USD").eql?(klass.new(Money.new(1_00, "USD"))).should be true
|
185
|
+
Money.new(2_50, "USD").eql?(klass.new(Money.new(2_50, "USD"))).should be true
|
186
|
+
Money.new(2_50, "USD").eql?(klass.new(Money.new(3_00, "USD"))).should be false
|
187
|
+
Money.new(1_00, "GBP").eql?(klass.new(Money.new(1_00, "USD"))).should be false
|
188
|
+
end
|
189
|
+
|
190
|
+
specify "#eql? returns false if used to compare with an object that doesn't respond to #to_money" do
|
191
|
+
Money.new(1_00, "USD").eql?(Object.new).should be false
|
192
|
+
Money.new(1_00, "USD").eql?(Class).should be false
|
193
|
+
Money.new(1_00, "USD").eql?(Kernel).should be false
|
194
|
+
Money.new(1_00, "USD").eql?(/foo/).should be false
|
195
|
+
Money.new(1_00, "USD").eql?(nil).should be false
|
196
|
+
end
|
197
|
+
|
198
|
+
specify "#hash should return the same value for equal objects" do
|
199
|
+
Money.new(1_00, :eur).hash.should == Money.new(1_00, :eur).hash
|
200
|
+
Money.new(2_00, :usd).hash.should == Money.new(2_00, :usd).hash
|
201
|
+
Money.new(1_00, :eur).hash.should_not == Money.new(2_00, :eur).hash
|
202
|
+
Money.new(1_00, :eur).hash.should_not == Money.new(1_00, :usd).hash
|
203
|
+
Money.new(1_00, :eur).hash.should_not == Money.new(2_00, :usd).hash
|
204
|
+
end
|
205
|
+
|
206
|
+
specify "#hash can be used to return the intersection of Money object arrays" do
|
207
|
+
intersection = [Money.new(1_00, :eur), Money.new(1_00, :usd)] & [Money.new(1_00, :eur)]
|
208
|
+
intersection.should == [Money.new(1_00, :eur)]
|
209
|
+
end
|
210
|
+
|
211
|
+
specify "#<=> can be used to compare with a String money value" do
|
212
|
+
(Money.new(1_00) <=> "1.00").should == 0
|
213
|
+
(Money.new(1_00) <=> ".99").should > 0
|
214
|
+
(Money.new(1_00) <=> "2.00").should < 0
|
215
|
+
end
|
216
|
+
|
217
|
+
specify "#<=> can be used to compare with a Numeric money value" do
|
218
|
+
(Money.new(1_00) <=> 1).should == 0
|
219
|
+
(Money.new(1_00) <=> 0.99).should > 0
|
220
|
+
(Money.new(1_00) <=> 2.00).should < 0
|
221
|
+
end
|
222
|
+
|
223
|
+
specify "#<=> can be used to compare with an object that responds to #to_money" do
|
224
|
+
klass = Class.new do
|
225
|
+
def initialize(money)
|
226
|
+
@money = money
|
227
|
+
end
|
228
|
+
|
229
|
+
def to_money
|
230
|
+
@money
|
231
|
+
end
|
232
|
+
end
|
233
|
+
|
234
|
+
(Money.new(1_00) <=> klass.new(Money.new(1_00))).should == 0
|
235
|
+
(Money.new(1_00) <=> klass.new(Money.new(99))).should > 0
|
236
|
+
(Money.new(1_00) <=> klass.new(Money.new(2_00))).should < 0
|
237
|
+
end
|
238
|
+
|
239
|
+
specify "#<=> raises ArgumentError when used to compare with an object that doesn't respond to #to_money" do
|
240
|
+
expected_message = /Comparison .+ failed/
|
241
|
+
lambda{ Money.new(1_00) <=> Object.new }.should raise_error(ArgumentError, expected_message)
|
242
|
+
lambda{ Money.new(1_00) <=> Class }.should raise_error(ArgumentError, expected_message)
|
243
|
+
lambda{ Money.new(1_00) <=> Kernel }.should raise_error(ArgumentError, expected_message)
|
244
|
+
lambda{ Money.new(1_00) <=> /foo/ }.should raise_error(ArgumentError, expected_message)
|
245
|
+
end
|
246
|
+
|
247
|
+
describe "#*" do
|
248
|
+
it "should multiply current money amount by the multiplier while retaining the currency" do
|
249
|
+
(Money.new(1_00, "USD") * 10).should == Money.new(10_00, "USD")
|
250
|
+
end
|
251
|
+
|
252
|
+
it "should multiply Money by Fixnum and returns Money" do
|
253
|
+
ts = [
|
254
|
+
{:a => Money.new( 10, :USD), :b => 4, :c => Money.new( 40, :USD)},
|
255
|
+
{:a => Money.new( 10, :USD), :b => -4, :c => Money.new(-40, :USD)},
|
256
|
+
{:a => Money.new(-10, :USD), :b => 4, :c => Money.new(-40, :USD)},
|
257
|
+
{:a => Money.new(-10, :USD), :b => -4, :c => Money.new( 40, :USD)},
|
258
|
+
]
|
259
|
+
ts.each do |t|
|
260
|
+
(t[:a] * t[:b]).should == t[:c]
|
261
|
+
end
|
262
|
+
end
|
263
|
+
|
264
|
+
it "should not multiply Money by Money (same currency)" do
|
265
|
+
lambda { Money.new( 10, :USD) * Money.new( 4, :USD) }.should raise_error(ArgumentError)
|
266
|
+
end
|
267
|
+
|
268
|
+
it "should not multiply Money by Money (different currency)" do
|
269
|
+
lambda { Money.new( 10, :USD) * Money.new( 4, :EUR) }.should raise_error(ArgumentError)
|
270
|
+
end
|
271
|
+
end
|
272
|
+
|
273
|
+
describe "#/" do
|
274
|
+
it "divides current money amount by the divisor while retaining the currency" do
|
275
|
+
(Money.new(10_00, "USD") / 10).should == Money.new(1_00, "USD")
|
276
|
+
end
|
277
|
+
|
278
|
+
it "divides Money by Fixnum and returns Money" do
|
279
|
+
ts = [
|
280
|
+
{:a => Money.new( 13, :USD), :b => 4, :c => Money.new( 3, :USD)},
|
281
|
+
{:a => Money.new( 13, :USD), :b => -4, :c => Money.new(-4, :USD)},
|
282
|
+
{:a => Money.new(-13, :USD), :b => 4, :c => Money.new(-4, :USD)},
|
283
|
+
{:a => Money.new(-13, :USD), :b => -4, :c => Money.new( 3, :USD)},
|
284
|
+
]
|
285
|
+
ts.each do |t|
|
286
|
+
(t[:a] / t[:b]).should == t[:c]
|
287
|
+
end
|
288
|
+
end
|
289
|
+
|
290
|
+
it "divides Money by Money (same currency) and returns Float" do
|
291
|
+
ts = [
|
292
|
+
{:a => Money.new( 13, :USD), :b => Money.new( 4, :USD), :c => 3.25},
|
293
|
+
{:a => Money.new( 13, :USD), :b => Money.new(-4, :USD), :c => -3.25},
|
294
|
+
{:a => Money.new(-13, :USD), :b => Money.new( 4, :USD), :c => -3.25},
|
295
|
+
{:a => Money.new(-13, :USD), :b => Money.new(-4, :USD), :c => 3.25},
|
296
|
+
]
|
297
|
+
ts.each do |t|
|
298
|
+
(t[:a] / t[:b]).should == t[:c]
|
299
|
+
end
|
300
|
+
end
|
301
|
+
|
302
|
+
it "divides Money by Money (different currency) and returns Float" do
|
303
|
+
ts = [
|
304
|
+
{:a => Money.new( 13, :USD), :b => Money.new( 4, :EUR), :c => 1.625},
|
305
|
+
{:a => Money.new( 13, :USD), :b => Money.new(-4, :EUR), :c => -1.625},
|
306
|
+
{:a => Money.new(-13, :USD), :b => Money.new( 4, :EUR), :c => -1.625},
|
307
|
+
{:a => Money.new(-13, :USD), :b => Money.new(-4, :EUR), :c => 1.625},
|
308
|
+
]
|
309
|
+
ts.each do |t|
|
310
|
+
t[:b].should_receive(:exchange_to).once.with(t[:a].currency).and_return(Money.new(t[:b].cents * 2, :USD))
|
311
|
+
(t[:a] / t[:b]).should == t[:c]
|
312
|
+
end
|
313
|
+
end
|
314
|
+
end
|
315
|
+
|
316
|
+
specify "#div -> money / fixnum" do
|
317
|
+
ts = [
|
318
|
+
{:a => Money.new( 13, :USD), :b => 4, :c => Money.new( 3, :USD)},
|
319
|
+
{:a => Money.new( 13, :USD), :b => -4, :c => Money.new(-4, :USD)},
|
320
|
+
{:a => Money.new(-13, :USD), :b => 4, :c => Money.new(-4, :USD)},
|
321
|
+
{:a => Money.new(-13, :USD), :b => -4, :c => Money.new( 3, :USD)},
|
322
|
+
]
|
323
|
+
ts.each do |t|
|
324
|
+
t[:a].div(t[:b]).should == t[:c]
|
325
|
+
end
|
326
|
+
end
|
327
|
+
|
328
|
+
specify "#div -> money / money (same currency)" do
|
329
|
+
ts = [
|
330
|
+
{:a => Money.new( 13, :USD), :b => Money.new( 4, :USD), :c => 3.25},
|
331
|
+
{:a => Money.new( 13, :USD), :b => Money.new(-4, :USD), :c => -3.25},
|
332
|
+
{:a => Money.new(-13, :USD), :b => Money.new( 4, :USD), :c => -3.25},
|
333
|
+
{:a => Money.new(-13, :USD), :b => Money.new(-4, :USD), :c => 3.25},
|
334
|
+
]
|
335
|
+
ts.each do |t|
|
336
|
+
t[:a].div(t[:b]).should == t[:c]
|
337
|
+
end
|
338
|
+
end
|
339
|
+
|
340
|
+
specify "#div -> money / money (different currency)" do
|
341
|
+
ts = [
|
342
|
+
{:a => Money.new( 13, :USD), :b => Money.new( 4, :EUR), :c => 1.625},
|
343
|
+
{:a => Money.new( 13, :USD), :b => Money.new(-4, :EUR), :c => -1.625},
|
344
|
+
{:a => Money.new(-13, :USD), :b => Money.new( 4, :EUR), :c => -1.625},
|
345
|
+
{:a => Money.new(-13, :USD), :b => Money.new(-4, :EUR), :c => 1.625},
|
346
|
+
]
|
347
|
+
ts.each do |t|
|
348
|
+
t[:b].should_receive(:exchange_to).once.with(t[:a].currency).and_return(Money.new(t[:b].cents * 2, :USD))
|
349
|
+
t[:a].div(t[:b]).should == t[:c]
|
350
|
+
end
|
351
|
+
end
|
352
|
+
|
353
|
+
specify "#divmod -> money `divmod` fixnum" do
|
354
|
+
ts = [
|
355
|
+
{:a => Money.new( 13, :USD), :b => 4, :c => [Money.new( 3, :USD), Money.new( 1, :USD)]},
|
356
|
+
{:a => Money.new( 13, :USD), :b => -4, :c => [Money.new(-4, :USD), Money.new(-3, :USD)]},
|
357
|
+
{:a => Money.new(-13, :USD), :b => 4, :c => [Money.new(-4, :USD), Money.new( 3, :USD)]},
|
358
|
+
{:a => Money.new(-13, :USD), :b => -4, :c => [Money.new( 3, :USD), Money.new(-1, :USD)]},
|
359
|
+
]
|
360
|
+
ts.each do |t|
|
361
|
+
t[:a].divmod(t[:b]).should == t[:c]
|
362
|
+
end
|
363
|
+
end
|
364
|
+
|
365
|
+
specify "#divmod -> money `divmod` money (same currency)" do
|
366
|
+
ts = [
|
367
|
+
{:a => Money.new( 13, :USD), :b => Money.new( 4, :USD), :c => [ 3, Money.new( 1, :USD)]},
|
368
|
+
{:a => Money.new( 13, :USD), :b => Money.new(-4, :USD), :c => [-4, Money.new(-3, :USD)]},
|
369
|
+
{:a => Money.new(-13, :USD), :b => Money.new( 4, :USD), :c => [-4, Money.new( 3, :USD)]},
|
370
|
+
{:a => Money.new(-13, :USD), :b => Money.new(-4, :USD), :c => [ 3, Money.new(-1, :USD)]},
|
371
|
+
]
|
372
|
+
ts.each do |t|
|
373
|
+
t[:a].divmod(t[:b]).should == t[:c]
|
374
|
+
end
|
375
|
+
end
|
376
|
+
|
377
|
+
specify "#divmod -> money `divmod` money (different currency)" do
|
378
|
+
ts = [
|
379
|
+
{:a => Money.new( 13, :USD), :b => Money.new( 4, :EUR), :c => [ 1, Money.new( 5, :USD)]},
|
380
|
+
{:a => Money.new( 13, :USD), :b => Money.new(-4, :EUR), :c => [-2, Money.new(-3, :USD)]},
|
381
|
+
{:a => Money.new(-13, :USD), :b => Money.new( 4, :EUR), :c => [-2, Money.new( 3, :USD)]},
|
382
|
+
{:a => Money.new(-13, :USD), :b => Money.new(-4, :EUR), :c => [ 1, Money.new(-5, :USD)]},
|
383
|
+
]
|
384
|
+
ts.each do |t|
|
385
|
+
t[:b].should_receive(:exchange_to).once.with(t[:a].currency).and_return(Money.new(t[:b].cents * 2, :USD))
|
386
|
+
t[:a].divmod(t[:b]).should == t[:c]
|
387
|
+
end
|
388
|
+
end
|
389
|
+
|
390
|
+
specify "#modulo -> money `modulo` fixnum" do
|
391
|
+
ts = [
|
392
|
+
{:a => Money.new( 13, :USD), :b => 4, :c => Money.new( 1, :USD)},
|
393
|
+
{:a => Money.new( 13, :USD), :b => -4, :c => Money.new(-3, :USD)},
|
394
|
+
{:a => Money.new(-13, :USD), :b => 4, :c => Money.new( 3, :USD)},
|
395
|
+
{:a => Money.new(-13, :USD), :b => -4, :c => Money.new(-1, :USD)},
|
396
|
+
]
|
397
|
+
ts.each do |t|
|
398
|
+
t[:a].modulo(t[:b]).should == t[:c]
|
399
|
+
end
|
400
|
+
end
|
401
|
+
|
402
|
+
specify "#modulo -> money `modulo` money (same currency)" do
|
403
|
+
ts = [
|
404
|
+
{:a => Money.new( 13, :USD), :b => Money.new( 4, :USD), :c => Money.new( 1, :USD)},
|
405
|
+
{:a => Money.new( 13, :USD), :b => Money.new(-4, :USD), :c => Money.new(-3, :USD)},
|
406
|
+
{:a => Money.new(-13, :USD), :b => Money.new( 4, :USD), :c => Money.new( 3, :USD)},
|
407
|
+
{:a => Money.new(-13, :USD), :b => Money.new(-4, :USD), :c => Money.new(-1, :USD)},
|
408
|
+
]
|
409
|
+
ts.each do |t|
|
410
|
+
t[:a].modulo(t[:b]).should == t[:c]
|
411
|
+
end
|
412
|
+
end
|
413
|
+
|
414
|
+
specify "#modulo -> money `modulo` money (different currency)" do
|
415
|
+
ts = [
|
416
|
+
{:a => Money.new( 13, :USD), :b => Money.new( 4, :EUR), :c => Money.new( 5, :USD)},
|
417
|
+
{:a => Money.new( 13, :USD), :b => Money.new(-4, :EUR), :c => Money.new(-3, :USD)},
|
418
|
+
{:a => Money.new(-13, :USD), :b => Money.new( 4, :EUR), :c => Money.new( 3, :USD)},
|
419
|
+
{:a => Money.new(-13, :USD), :b => Money.new(-4, :EUR), :c => Money.new(-5, :USD)},
|
420
|
+
]
|
421
|
+
ts.each do |t|
|
422
|
+
t[:b].should_receive(:exchange_to).once.with(t[:a].currency).and_return(Money.new(t[:b].cents * 2, :USD))
|
423
|
+
t[:a].modulo(t[:b]).should == t[:c]
|
424
|
+
end
|
425
|
+
end
|
426
|
+
|
427
|
+
specify "#% -> money % fixnum" do
|
428
|
+
ts = [
|
429
|
+
{:a => Money.new( 13, :USD), :b => 4, :c => Money.new( 1, :USD)},
|
430
|
+
{:a => Money.new( 13, :USD), :b => -4, :c => Money.new(-3, :USD)},
|
431
|
+
{:a => Money.new(-13, :USD), :b => 4, :c => Money.new( 3, :USD)},
|
432
|
+
{:a => Money.new(-13, :USD), :b => -4, :c => Money.new(-1, :USD)},
|
433
|
+
]
|
434
|
+
ts.each do |t|
|
435
|
+
(t[:a] % t[:b]).should == t[:c]
|
436
|
+
end
|
437
|
+
end
|
438
|
+
|
439
|
+
specify "#% -> money % money (same currency)" do
|
440
|
+
ts = [
|
441
|
+
{:a => Money.new( 13, :USD), :b => Money.new( 4, :USD), :c => Money.new( 1, :USD)},
|
442
|
+
{:a => Money.new( 13, :USD), :b => Money.new(-4, :USD), :c => Money.new(-3, :USD)},
|
443
|
+
{:a => Money.new(-13, :USD), :b => Money.new( 4, :USD), :c => Money.new( 3, :USD)},
|
444
|
+
{:a => Money.new(-13, :USD), :b => Money.new(-4, :USD), :c => Money.new(-1, :USD)},
|
445
|
+
]
|
446
|
+
ts.each do |t|
|
447
|
+
(t[:a] % t[:b]).should == t[:c]
|
448
|
+
end
|
449
|
+
end
|
450
|
+
|
451
|
+
specify "#% -> money % money (different currency)" do
|
452
|
+
ts = [
|
453
|
+
{:a => Money.new( 13, :USD), :b => Money.new( 4, :EUR), :c => Money.new( 5, :USD)},
|
454
|
+
{:a => Money.new( 13, :USD), :b => Money.new(-4, :EUR), :c => Money.new(-3, :USD)},
|
455
|
+
{:a => Money.new(-13, :USD), :b => Money.new( 4, :EUR), :c => Money.new( 3, :USD)},
|
456
|
+
{:a => Money.new(-13, :USD), :b => Money.new(-4, :EUR), :c => Money.new(-5, :USD)},
|
457
|
+
]
|
458
|
+
ts.each do |t|
|
459
|
+
t[:b].should_receive(:exchange_to).once.with(t[:a].currency).and_return(Money.new(t[:b].cents * 2, :USD))
|
460
|
+
(t[:a] % t[:b]).should == t[:c]
|
461
|
+
end
|
462
|
+
end
|
463
|
+
|
464
|
+
specify "#remainder -> money `remainder` fixnum" do
|
465
|
+
ts = [
|
466
|
+
{:a => Money.new( 13, :USD), :b => 4, :c => Money.new( 1, :USD)},
|
467
|
+
{:a => Money.new( 13, :USD), :b => -4, :c => Money.new( 1, :USD)},
|
468
|
+
{:a => Money.new(-13, :USD), :b => 4, :c => Money.new(-1, :USD)},
|
469
|
+
{:a => Money.new(-13, :USD), :b => -4, :c => Money.new(-1, :USD)},
|
470
|
+
]
|
471
|
+
ts.each do |t|
|
472
|
+
t[:a].remainder(t[:b]).should == t[:c]
|
473
|
+
end
|
474
|
+
end
|
475
|
+
|
476
|
+
specify "#remainder -> money `remainder` money (same currency)" do
|
477
|
+
ts = [
|
478
|
+
{:a => Money.new( 13, :USD), :b => Money.new( 4, :USD), :c => Money.new( 1, :USD)},
|
479
|
+
{:a => Money.new( 13, :USD), :b => Money.new(-4, :USD), :c => Money.new( 1, :USD)},
|
480
|
+
{:a => Money.new(-13, :USD), :b => Money.new( 4, :USD), :c => Money.new(-1, :USD)},
|
481
|
+
{:a => Money.new(-13, :USD), :b => Money.new(-4, :USD), :c => Money.new(-1, :USD)},
|
482
|
+
]
|
483
|
+
ts.each do |t|
|
484
|
+
t[:a].remainder(t[:b]).should == t[:c]
|
485
|
+
end
|
486
|
+
end
|
487
|
+
|
488
|
+
specify "#remainder -> money `remainder` money (different currency)" do
|
489
|
+
ts = [
|
490
|
+
{:a => Money.new( 13, :USD), :b => Money.new( 4, :EUR), :c => Money.new( 5, :USD)},
|
491
|
+
{:a => Money.new( 13, :USD), :b => Money.new(-4, :EUR), :c => Money.new( 5, :USD)},
|
492
|
+
{:a => Money.new(-13, :USD), :b => Money.new( 4, :EUR), :c => Money.new(-5, :USD)},
|
493
|
+
{:a => Money.new(-13, :USD), :b => Money.new(-4, :EUR), :c => Money.new(-5, :USD)},
|
494
|
+
]
|
495
|
+
ts.each do |t|
|
496
|
+
t[:b].should_receive(:exchange_to).once.with(t[:a].currency).and_return(Money.new(t[:b].cents * 2, :USD))
|
497
|
+
t[:a].remainder(t[:b]).should == t[:c]
|
498
|
+
end
|
499
|
+
end
|
500
|
+
|
501
|
+
specify "#abs correctly returns the absolute value as a new Money object" do
|
502
|
+
n = Money.new(-1, :USD)
|
503
|
+
n.abs.should == Money.new( 1, :USD)
|
504
|
+
n.should == Money.new(-1, :USD)
|
505
|
+
end
|
506
|
+
|
507
|
+
specify "Money.format brute force :subunit_to_unit = 1" do
|
508
|
+
("0".."9").each do |amt|
|
509
|
+
amt.to_money("VUV").format(:symbol => false).should == amt
|
510
|
+
end
|
511
|
+
("-1".."-9").each do |amt|
|
512
|
+
amt.to_money("VUV").format(:symbol => false).should == amt
|
513
|
+
end
|
514
|
+
"1000".to_money("VUV").format(:symbol => false).should == "1,000"
|
515
|
+
"-1000".to_money("VUV").format(:symbol => false).should == "-1,000"
|
516
|
+
end
|
517
|
+
|
518
|
+
specify "Money.format brute force :subunit_to_unit = 5" do
|
519
|
+
("0.0".."9.4").each do |amt|
|
520
|
+
next if amt[-1].to_i > 4
|
521
|
+
amt.to_money("MGA").format(:symbol => false).should == amt
|
522
|
+
end
|
523
|
+
("-0.1".."-9.4").each do |amt|
|
524
|
+
next if amt[-1].to_i > 4
|
525
|
+
amt.to_money("MGA").format(:symbol => false).should == amt
|
526
|
+
end
|
527
|
+
"1000.0".to_money("MGA").format(:symbol => false).should == "1,000.0"
|
528
|
+
"-1000.0".to_money("MGA").format(:symbol => false).should == "-1,000.0"
|
529
|
+
end
|
530
|
+
|
531
|
+
specify "Money.format brute force :subunit_to_unit = 10" do
|
532
|
+
("0.0".."9.9").each do |amt|
|
533
|
+
amt.to_money("VND").format(:symbol => false).should == amt.to_s.gsub(/\./, ",")
|
534
|
+
end
|
535
|
+
("-0.1".."-9.9").each do |amt|
|
536
|
+
amt.to_money("VND").format(:symbol => false).should == amt.to_s.gsub(/\./, ",")
|
537
|
+
end
|
538
|
+
"1000.0".to_money("VND").format(:symbol => false).should == "1.000,0"
|
539
|
+
"-1000.0".to_money("VND").format(:symbol => false).should == "-1.000,0"
|
540
|
+
end
|
541
|
+
|
542
|
+
specify "Money.format brute force :subunit_to_unit = 100" do
|
543
|
+
("0.00".."9.99").each do |amt|
|
544
|
+
amt.to_money("USD").format(:symbol => false).should == amt
|
545
|
+
end
|
546
|
+
("-0.01".."-9.99").each do |amt|
|
547
|
+
amt.to_money("USD").format(:symbol => false).should == amt
|
548
|
+
end
|
549
|
+
"1000.00".to_money("USD").format(:symbol => false).should == "1,000.00"
|
550
|
+
"-1000.00".to_money("USD").format(:symbol => false).should == "-1,000.00"
|
551
|
+
end
|
552
|
+
|
553
|
+
specify "Money.format brute force :subunit_to_unit = 1000" do
|
554
|
+
("0.000".."9.999").each do |amt|
|
555
|
+
amt.to_money("IQD").format(:symbol => false).should == amt
|
556
|
+
end
|
557
|
+
("-0.001".."-9.999").each do |amt|
|
558
|
+
amt.to_money("IQD").format(:symbol => false).should == amt
|
559
|
+
end
|
560
|
+
"1000.000".to_money("IQD").format(:symbol => false).should == "1,000.000"
|
561
|
+
"-1000.000".to_money("IQD").format(:symbol => false).should == "-1,000.000"
|
562
|
+
end
|
563
|
+
|
564
|
+
specify "Money.to_s works" do
|
565
|
+
Money.new(10_00).to_s.should == "10.00"
|
566
|
+
Money.new(400_08).to_s.should == "400.08"
|
567
|
+
Money.new(-237_43).to_s.should == "-237.43"
|
568
|
+
end
|
569
|
+
|
570
|
+
specify "Money.to_s should respect :subunit_to_unit currency property" do
|
571
|
+
Money.new(10_00, "BHD").to_s.should == "1.000"
|
572
|
+
Money.new(10_00, "CNY").to_s.should == "10.00"
|
573
|
+
end
|
574
|
+
|
575
|
+
specify "Money.to_s shouldn't have decimal when :subunit_to_unit is 1" do
|
576
|
+
Money.new(10_00, "CLP").to_s.should == "1000"
|
577
|
+
end
|
578
|
+
|
579
|
+
specify "Money.to_s should work with :subunit_to_unit == 5" do
|
580
|
+
Money.new(10_00, "MGA").to_s.should == "200.0"
|
581
|
+
end
|
582
|
+
|
583
|
+
specify "Money.to_s should respect :decimal_mark" do
|
584
|
+
Money.new(10_00, "BRL").to_s.should == "10,00"
|
585
|
+
end
|
586
|
+
|
587
|
+
specify "Money.to_f works" do
|
588
|
+
Money.new(10_00).to_f.should == 10.0
|
589
|
+
end
|
590
|
+
|
591
|
+
specify "Money.to_f should respect :subunit_to_unit currency property" do
|
592
|
+
Money.new(10_00, "BHD").to_f.should == 1.0
|
593
|
+
end
|
594
|
+
|
595
|
+
specify "#symbol works as documented" do
|
596
|
+
currency = Money::Currency.new("EUR")
|
597
|
+
currency.should_receive(:symbol).and_return("€")
|
598
|
+
Money.empty(currency).symbol.should == "€"
|
599
|
+
|
600
|
+
currency = Money::Currency.new("EUR")
|
601
|
+
currency.should_receive(:symbol).and_return(nil)
|
602
|
+
Money.empty(currency).symbol.should == "¤"
|
603
|
+
end
|
604
|
+
|
605
|
+
{
|
606
|
+
:thousands_separator => { :default => ",", :other => "." },
|
607
|
+
:decimal_mark => { :default => ".", :other => "," }
|
608
|
+
}.each do |method, options|
|
609
|
+
describe "##{method}" do
|
610
|
+
context "without I18n" do
|
611
|
+
it "works as documented" do
|
612
|
+
Money.empty("USD").send(method).should == options[:default]
|
613
|
+
Money.empty("EUR").send(method).should == options[:other]
|
614
|
+
Money.empty("BRL").send(method).should == options[:other]
|
615
|
+
end
|
616
|
+
end
|
617
|
+
|
618
|
+
if Object.const_defined?("I18n")
|
619
|
+
context "with I18n" do
|
620
|
+
before :all do
|
621
|
+
reset_i18n
|
622
|
+
store_number_formats(:en, method => options[:default])
|
623
|
+
store_number_formats(:de, method => options[:other])
|
624
|
+
end
|
625
|
+
|
626
|
+
it "looks up #{method} for current locale" do
|
627
|
+
I18n.locale = :en
|
628
|
+
Money.empty("USD").send(method).should == options[:default]
|
629
|
+
I18n.locale = :de
|
630
|
+
Money.empty("USD").send(method).should == options[:other]
|
631
|
+
end
|
632
|
+
|
633
|
+
it "fallbacks to default behaviour for missing translations" do
|
634
|
+
I18n.locale = :de
|
635
|
+
Money.empty("USD").send(method).should == options[:other]
|
636
|
+
I18n.locale = :fr
|
637
|
+
Money.empty("USD").send(method).should == options[:default]
|
638
|
+
end
|
639
|
+
|
640
|
+
after :all do
|
641
|
+
reset_i18n
|
642
|
+
end
|
643
|
+
end
|
644
|
+
else
|
645
|
+
puts "can't test ##{method} with I18n because it isn't loaded"
|
646
|
+
end
|
647
|
+
end
|
648
|
+
end
|
649
|
+
|
650
|
+
describe "#format" do
|
651
|
+
it "returns the monetary value as a string" do
|
652
|
+
Money.ca_dollar(100).format.should == "$1.00"
|
653
|
+
Money.new(40008).format.should == "$400.08"
|
654
|
+
end
|
655
|
+
|
656
|
+
it "should respect :subunit_to_unit currency property" do
|
657
|
+
Money.new(10_00, "BHD").format.should == "ب.د1.000"
|
658
|
+
end
|
659
|
+
|
660
|
+
it "doesn't display a decimal when :subunit_to_unit is 1" do
|
661
|
+
Money.new(10_00, "CLP").format.should == "$1.000"
|
662
|
+
end
|
663
|
+
|
664
|
+
it "respects the thousands_separator and decimal_mark defaults" do
|
665
|
+
one_thousand = Proc.new do |currency|
|
666
|
+
Money.new(1000_00, currency).format
|
667
|
+
end
|
668
|
+
|
669
|
+
# Pounds
|
670
|
+
one_thousand["GBP"].should == "£1,000.00"
|
671
|
+
|
672
|
+
# Dollars
|
673
|
+
one_thousand["USD"].should == "$1,000.00"
|
674
|
+
one_thousand["CAD"].should == "$1,000.00"
|
675
|
+
one_thousand["AUD"].should == "$1,000.00"
|
676
|
+
one_thousand["NZD"].should == "$1,000.00"
|
677
|
+
one_thousand["ZWD"].should == "$1,000.00"
|
678
|
+
|
679
|
+
# Yen
|
680
|
+
one_thousand["JPY"].should == "¥1,000.00"
|
681
|
+
one_thousand["CNY"].should == "¥1,000.00"
|
682
|
+
|
683
|
+
# Euro
|
684
|
+
one_thousand["EUR"].should == "1.000,00 €"
|
685
|
+
|
686
|
+
# Rupees
|
687
|
+
one_thousand["INR"].should == "₨1,000.00"
|
688
|
+
one_thousand["NPR"].should == "₨1,000.00"
|
689
|
+
one_thousand["SCR"].should == "1,000.00 ₨"
|
690
|
+
one_thousand["LKR"].should == "1,000.00 ₨"
|
691
|
+
|
692
|
+
# Brazilian Real
|
693
|
+
one_thousand["BRL"].should == "R$ 1.000,00"
|
694
|
+
|
695
|
+
# Other
|
696
|
+
one_thousand["SEK"].should == "kr1,000.00"
|
697
|
+
one_thousand["GHC"].should == "₵1,000.00"
|
698
|
+
end
|
699
|
+
|
700
|
+
describe "if the monetary value is 0" do
|
701
|
+
before :each do
|
702
|
+
@money = Money.us_dollar(0)
|
703
|
+
end
|
704
|
+
|
705
|
+
it "returns 'free' when :display_free is true" do
|
706
|
+
@money.format(:display_free => true).should == 'free'
|
707
|
+
end
|
708
|
+
|
709
|
+
it "returns '$0.00' when :display_free is false or not given" do
|
710
|
+
@money.format.should == '$0.00'
|
711
|
+
@money.format(:display_free => false).should == '$0.00'
|
712
|
+
@money.format(:display_free => nil).should == '$0.00'
|
713
|
+
end
|
714
|
+
|
715
|
+
it "returns the value specified by :display_free if it's a string-like object" do
|
716
|
+
@money.format(:display_free => 'gratis').should == 'gratis'
|
717
|
+
end
|
718
|
+
end
|
719
|
+
|
720
|
+
specify "#format(:with_currency => true) works as documented" do
|
721
|
+
Money.ca_dollar(100).format(:with_currency => true).should == "$1.00 CAD"
|
722
|
+
Money.us_dollar(85).format(:with_currency => true).should == "$0.85 USD"
|
723
|
+
end
|
724
|
+
|
725
|
+
specify "#format(:no_cents => true) works as documented" do
|
726
|
+
Money.ca_dollar(100).format(:no_cents => true).should == "$1"
|
727
|
+
Money.ca_dollar(599).format(:no_cents => true).should == "$5"
|
728
|
+
Money.ca_dollar(570).format(:no_cents => true, :with_currency => true).should == "$5 CAD"
|
729
|
+
Money.ca_dollar(39000).format(:no_cents => true).should == "$390"
|
730
|
+
end
|
731
|
+
|
732
|
+
specify "#format(:no_cents => true) should respect :subunit_to_unit currency property" do
|
733
|
+
Money.new(10_00, "BHD").format(:no_cents => true).should == "ب.د1"
|
734
|
+
end
|
735
|
+
|
736
|
+
specify "#format(:symbol => a symbol string) uses the given value as the money symbol" do
|
737
|
+
Money.new(100, "GBP").format(:symbol => "£").should == "£1.00"
|
738
|
+
end
|
739
|
+
|
740
|
+
specify "#format(:symbol => true) returns symbol based on the given currency code" do
|
741
|
+
one = Proc.new do |currency|
|
742
|
+
Money.new(100, currency).format(:symbol => true)
|
743
|
+
end
|
744
|
+
|
745
|
+
# Pounds
|
746
|
+
one["GBP"].should == "£1.00"
|
747
|
+
|
748
|
+
# Dollars
|
749
|
+
one["USD"].should == "$1.00"
|
750
|
+
one["CAD"].should == "$1.00"
|
751
|
+
one["AUD"].should == "$1.00"
|
752
|
+
one["NZD"].should == "$1.00"
|
753
|
+
one["ZWD"].should == "$1.00"
|
754
|
+
|
755
|
+
# Yen
|
756
|
+
one["JPY"].should == "¥1.00"
|
757
|
+
one["CNY"].should == "¥1.00"
|
758
|
+
|
759
|
+
# Euro
|
760
|
+
one["EUR"].should == "1,00 €"
|
761
|
+
|
762
|
+
# Rupees
|
763
|
+
one["INR"].should == "₨1.00"
|
764
|
+
one["NPR"].should == "₨1.00"
|
765
|
+
one["SCR"].should == "1.00 ₨"
|
766
|
+
one["LKR"].should == "1.00 ₨"
|
767
|
+
|
768
|
+
# Brazilian Real
|
769
|
+
one["BRL"].should == "R$ 1,00"
|
770
|
+
|
771
|
+
# Other
|
772
|
+
one["SEK"].should == "kr1.00"
|
773
|
+
one["GHC"].should == "₵1.00"
|
774
|
+
end
|
775
|
+
|
776
|
+
specify "#format(:symbol => true) returns $ when currency code is not recognized" do
|
777
|
+
currency = Money::Currency.new("EUR")
|
778
|
+
currency.should_receive(:symbol).and_return(nil)
|
779
|
+
Money.new(100, currency).format(:symbol => true).should == "1,00 ¤"
|
780
|
+
end
|
781
|
+
|
782
|
+
specify "#format(:symbol => some non-Boolean value that evaluates to true) returns symbol based on the given currency code" do
|
783
|
+
Money.new(100, "GBP").format(:symbol => true).should == "£1.00"
|
784
|
+
Money.new(100, "EUR").format(:symbol => true).should == "1,00 €"
|
785
|
+
Money.new(100, "SEK").format(:symbol => true).should == "kr1.00"
|
786
|
+
end
|
787
|
+
|
788
|
+
specify "#format with :symbol == "", nil or false returns the amount without a symbol" do
|
789
|
+
money = Money.new(100, "GBP")
|
790
|
+
money.format(:symbol => "").should == "1.00"
|
791
|
+
money.format(:symbol => nil).should == "1.00"
|
792
|
+
money.format(:symbol => false).should == "1.00"
|
793
|
+
end
|
794
|
+
|
795
|
+
specify "#format without :symbol assumes that :symbol is set to true" do
|
796
|
+
money = Money.new(100)
|
797
|
+
money.format.should == "$1.00"
|
798
|
+
|
799
|
+
money = Money.new(100, "GBP")
|
800
|
+
money.format.should == "£1.00"
|
801
|
+
|
802
|
+
money = Money.new(100, "EUR")
|
803
|
+
money.format.should == "1,00 €"
|
804
|
+
end
|
805
|
+
|
806
|
+
specify "#format(:decimal_mark => a decimal_mark string) works as documented" do
|
807
|
+
Money.us_dollar(100).format(:decimal_mark => ",").should == "$1,00"
|
808
|
+
end
|
809
|
+
|
810
|
+
specify "#format will default decimal_mark to '.' if currency isn't recognized" do
|
811
|
+
Money.new(100, "ZWD").format.should == "$1.00"
|
812
|
+
end
|
813
|
+
|
814
|
+
specify "#format(:separator => a separator string) works as documented" do
|
815
|
+
Money.us_dollar(100).format(:separator => ",").should == "$1,00"
|
816
|
+
end
|
817
|
+
|
818
|
+
specify "#format(:thousands_separator => a thousands_separator string) works as documented" do
|
819
|
+
Money.us_dollar(100000).format(:thousands_separator => ".").should == "$1.000.00"
|
820
|
+
Money.us_dollar(200000).format(:thousands_separator => "").should == "$2000.00"
|
821
|
+
end
|
822
|
+
|
823
|
+
specify "#format(:thousands_separator => false or nil) works as documented" do
|
824
|
+
Money.us_dollar(100000).format(:thousands_separator => false).should == "$1000.00"
|
825
|
+
Money.us_dollar(200000).format(:thousands_separator => nil).should == "$2000.00"
|
826
|
+
end
|
827
|
+
|
828
|
+
specify "#format(:delimiter => a delimiter string) works as documented" do
|
829
|
+
Money.us_dollar(100000).format(:delimiter => ".").should == "$1.000.00"
|
830
|
+
Money.us_dollar(200000).format(:delimiter => "").should == "$2000.00"
|
831
|
+
end
|
832
|
+
|
833
|
+
specify "#format(:delimiter => false or nil) works as documented" do
|
834
|
+
Money.us_dollar(100000).format(:delimiter => false).should == "$1000.00"
|
835
|
+
Money.us_dollar(200000).format(:delimiter => nil).should == "$2000.00"
|
836
|
+
end
|
837
|
+
|
838
|
+
specify "#format will default thousands_separator to ',' if currency isn't recognized" do
|
839
|
+
Money.new(100000, "ZWD").format.should == "$1,000.00"
|
840
|
+
end
|
841
|
+
|
842
|
+
specify "#format(:html => true) works as documented" do
|
843
|
+
string = Money.ca_dollar(570).format(:html => true, :with_currency => true)
|
844
|
+
string.should == "$5.70 <span class=\"currency\">CAD</span>"
|
845
|
+
end
|
846
|
+
|
847
|
+
it "should insert commas into the result if the amount is sufficiently large" do
|
848
|
+
Money.us_dollar(1_000_000_000_12).format.should == "$1,000,000,000.12"
|
849
|
+
Money.us_dollar(1_000_000_000_12).format(:no_cents => true).should == "$1,000,000,000"
|
850
|
+
end
|
851
|
+
|
852
|
+
it "inserts thousands separator into the result if the amount is sufficiently large and the currency symbol is at the end" do
|
853
|
+
Money.euro(1_234_567_12).format.should == "1.234.567,12 €"
|
854
|
+
Money.euro(1_234_567_12).format(:no_cents => true).should == "1.234.567 €"
|
855
|
+
end
|
856
|
+
|
857
|
+
it "inserts currency symbol before the amount when :symbol_position is set to :before" do
|
858
|
+
Money.euro(1_234_567_12).format(:symbol_position => :before).should == "€1.234.567,12"
|
859
|
+
end
|
860
|
+
|
861
|
+
it "inserts currency symbol after the amount when :symbol_position is set to :after" do
|
862
|
+
Money.us_dollar(1_000_000_000_12).format(:symbol_position => :after).should == "1,000,000,000.12 $"
|
863
|
+
end
|
864
|
+
end
|
865
|
+
|
866
|
+
|
867
|
+
describe "Money.empty" do
|
868
|
+
it "Money.empty creates a new Money object of 0 cents" do
|
869
|
+
Money.empty.should == Money.new(0)
|
870
|
+
end
|
871
|
+
end
|
872
|
+
|
873
|
+
describe "Money.ca_dollar" do
|
874
|
+
it "creates a new Money object of the given value in CAD" do
|
875
|
+
Money.ca_dollar(50).should == Money.new(50, "CAD")
|
876
|
+
end
|
877
|
+
end
|
878
|
+
|
879
|
+
describe "Money.us_dollar" do
|
880
|
+
it "creates a new Money object of the given value in USD" do
|
881
|
+
Money.us_dollar(50).should == Money.new(50, "USD")
|
882
|
+
end
|
883
|
+
end
|
884
|
+
|
885
|
+
describe "Money.euro" do
|
886
|
+
it "creates a new Money object of the given value in EUR" do
|
887
|
+
Money.euro(50).should == Money.new(50, "EUR")
|
888
|
+
end
|
889
|
+
end
|
890
|
+
|
891
|
+
|
892
|
+
describe "Money.new_with_dollars" do
|
893
|
+
it "converts given amount to cents" do
|
894
|
+
Money.new_with_dollars(1).should == Money.new(100)
|
895
|
+
Money.new_with_dollars(1, "USD").should == Money.new(100, "USD")
|
896
|
+
Money.new_with_dollars(1, "EUR").should == Money.new(100, "EUR")
|
897
|
+
end
|
898
|
+
|
899
|
+
it "should respect :subunit_to_unit currency property" do
|
900
|
+
Money.new_with_dollars(1, "USD").should == Money.new(1_00, "USD")
|
901
|
+
Money.new_with_dollars(1, "TND").should == Money.new(1_000, "TND")
|
902
|
+
Money.new_with_dollars(1, "CLP").should == Money.new(1, "CLP")
|
903
|
+
end
|
904
|
+
|
905
|
+
it "should not loose precision" do
|
906
|
+
Money.new_with_dollars(1234).cents.should == 1234_00
|
907
|
+
Money.new_with_dollars(100.37).cents.should == 100_37
|
908
|
+
Money.new_with_dollars(BigDecimal.new('1234')).cents.should == 1234_00
|
909
|
+
end
|
910
|
+
|
911
|
+
it "accepts a currency options" do
|
912
|
+
m = Money.new_with_dollars(1)
|
913
|
+
m.currency.should == Money.default_currency
|
914
|
+
|
915
|
+
m = Money.new_with_dollars(1, Money::Currency.wrap("EUR"))
|
916
|
+
m.currency.should == Money::Currency.wrap("EUR")
|
917
|
+
|
918
|
+
m = Money.new_with_dollars(1, "EUR")
|
919
|
+
m.currency.should == Money::Currency.wrap("EUR")
|
920
|
+
end
|
921
|
+
|
922
|
+
it "accepts a bank options" do
|
923
|
+
m = Money.new_with_dollars(1)
|
924
|
+
m.bank.should == Money.default_bank
|
925
|
+
|
926
|
+
m = Money.new_with_dollars(1, "EUR", bank = Object.new)
|
927
|
+
m.bank.should == bank
|
928
|
+
end
|
929
|
+
|
930
|
+
it "is associated to the singleton instance of Bank::VariableExchange by default" do
|
931
|
+
Money.new_with_dollars(0).bank.should be_equal(Money::Bank::VariableExchange.instance)
|
932
|
+
end
|
933
|
+
end
|
934
|
+
|
935
|
+
describe "Money.from_string" do
|
936
|
+
it "converts given amount to cents" do
|
937
|
+
Money.from_string("1").should == Money.new(1_00)
|
938
|
+
Money.from_string("1").should == Money.new(1_00, "USD")
|
939
|
+
Money.from_string("1", "EUR").should == Money.new(1_00, "EUR")
|
940
|
+
end
|
941
|
+
|
942
|
+
it "should respect :subunit_to_unit currency property" do
|
943
|
+
Money.from_string("1", "USD").should == Money.new(1_00, "USD")
|
944
|
+
Money.from_string("1", "TND").should == Money.new(1_000, "TND")
|
945
|
+
Money.from_string("1", "CLP").should == Money.new(1, "CLP")
|
946
|
+
end
|
947
|
+
|
948
|
+
it "accepts a currency options" do
|
949
|
+
m = Money.from_string("1")
|
950
|
+
m.currency.should == Money.default_currency
|
951
|
+
|
952
|
+
m = Money.from_string("1", Money::Currency.wrap("EUR"))
|
953
|
+
m.currency.should == Money::Currency.wrap("EUR")
|
954
|
+
|
955
|
+
m = Money.from_string("1", "EUR")
|
956
|
+
m.currency.should == Money::Currency.wrap("EUR")
|
957
|
+
end
|
958
|
+
end
|
959
|
+
|
960
|
+
describe "Money.from_fixnum" do
|
961
|
+
it "converts given amount to cents" do
|
962
|
+
Money.from_fixnum(1).should == Money.new(1_00)
|
963
|
+
Money.from_fixnum(1).should == Money.new(1_00, "USD")
|
964
|
+
Money.from_fixnum(1, "EUR").should == Money.new(1_00, "EUR")
|
965
|
+
end
|
966
|
+
|
967
|
+
it "should respect :subunit_to_unit currency property" do
|
968
|
+
Money.from_fixnum(1, "USD").should == Money.new(1_00, "USD")
|
969
|
+
Money.from_fixnum(1, "TND").should == Money.new(1_000, "TND")
|
970
|
+
Money.from_fixnum(1, "CLP").should == Money.new(1, "CLP")
|
971
|
+
end
|
972
|
+
|
973
|
+
it "accepts a currency options" do
|
974
|
+
m = Money.from_fixnum(1)
|
975
|
+
m.currency.should == Money.default_currency
|
976
|
+
|
977
|
+
m = Money.from_fixnum(1, Money::Currency.wrap("EUR"))
|
978
|
+
m.currency.should == Money::Currency.wrap("EUR")
|
979
|
+
|
980
|
+
m = Money.from_fixnum(1, "EUR")
|
981
|
+
m.currency.should == Money::Currency.wrap("EUR")
|
982
|
+
end
|
983
|
+
end
|
984
|
+
|
985
|
+
describe "Money.from_float" do
|
986
|
+
it "converts given amount to cents" do
|
987
|
+
Money.from_float(1.2).should == Money.new(1_20)
|
988
|
+
Money.from_float(1.2).should == Money.new(1_20, "USD")
|
989
|
+
Money.from_float(1.2, "EUR").should == Money.new(1_20, "EUR")
|
990
|
+
end
|
991
|
+
|
992
|
+
it "should respect :subunit_to_unit currency property" do
|
993
|
+
Money.from_float(1.2, "USD").should == Money.new(1_20, "USD")
|
994
|
+
Money.from_float(1.2, "TND").should == Money.new(1_200, "TND")
|
995
|
+
Money.from_float(1.2, "CLP").should == Money.new(1, "CLP")
|
996
|
+
end
|
997
|
+
|
998
|
+
it "accepts a currency options" do
|
999
|
+
m = Money.from_float(1.2)
|
1000
|
+
m.currency.should == Money.default_currency
|
1001
|
+
|
1002
|
+
m = Money.from_float(1.2, Money::Currency.wrap("EUR"))
|
1003
|
+
m.currency.should == Money::Currency.wrap("EUR")
|
1004
|
+
|
1005
|
+
m = Money.from_float(1.2, "EUR")
|
1006
|
+
m.currency.should == Money::Currency.wrap("EUR")
|
1007
|
+
end
|
1008
|
+
end
|
1009
|
+
|
1010
|
+
describe "Money.from_bigdecimal" do
|
1011
|
+
it "converts given amount to cents" do
|
1012
|
+
Money.from_bigdecimal(BigDecimal.new("1")).should == Money.new(1_00)
|
1013
|
+
Money.from_bigdecimal(BigDecimal.new("1")).should == Money.new(1_00, "USD")
|
1014
|
+
Money.from_bigdecimal(BigDecimal.new("1"), "EUR").should == Money.new(1_00, "EUR")
|
1015
|
+
end
|
1016
|
+
|
1017
|
+
it "should respect :subunit_to_unit currency property" do
|
1018
|
+
Money.from_bigdecimal(BigDecimal.new("1"), "USD").should == Money.new(1_00, "USD")
|
1019
|
+
Money.from_bigdecimal(BigDecimal.new("1"), "TND").should == Money.new(1_000, "TND")
|
1020
|
+
Money.from_bigdecimal(BigDecimal.new("1"), "CLP").should == Money.new(1, "CLP")
|
1021
|
+
end
|
1022
|
+
|
1023
|
+
it "accepts a currency options" do
|
1024
|
+
m = Money.from_bigdecimal(BigDecimal.new("1"))
|
1025
|
+
m.currency.should == Money.default_currency
|
1026
|
+
|
1027
|
+
m = Money.from_bigdecimal(BigDecimal.new("1"), Money::Currency.wrap("EUR"))
|
1028
|
+
m.currency.should == Money::Currency.wrap("EUR")
|
1029
|
+
|
1030
|
+
m = Money.from_bigdecimal(BigDecimal.new("1"), "EUR")
|
1031
|
+
m.currency.should == Money::Currency.wrap("EUR")
|
1032
|
+
end
|
1033
|
+
end
|
1034
|
+
|
1035
|
+
describe "Money.from_numeric" do
|
1036
|
+
it "converts given amount to cents" do
|
1037
|
+
Money.from_numeric(1).should == Money.new(1_00)
|
1038
|
+
Money.from_numeric(1.0).should == Money.new(1_00)
|
1039
|
+
Money.from_numeric(BigDecimal.new("1")).should == Money.new(1_00)
|
1040
|
+
end
|
1041
|
+
|
1042
|
+
it "should raise ArgumentError with unsupported argument" do
|
1043
|
+
lambda { Money.from_numeric("100") }.should raise_error(ArgumentError)
|
1044
|
+
end
|
1045
|
+
|
1046
|
+
it "should optimize workload" do
|
1047
|
+
Money.should_receive(:from_fixnum).with(1, "USD").and_return(Money.new(1_00, "USD"))
|
1048
|
+
Money.from_numeric(1, "USD").should == Money.new(1_00, "USD")
|
1049
|
+
Money.should_receive(:from_bigdecimal).with(BigDecimal.new("1.0"), "USD").and_return(Money.new(1_00, "USD"))
|
1050
|
+
Money.from_numeric(1.0, "USD").should == Money.new(1_00, "USD")
|
1051
|
+
end
|
1052
|
+
|
1053
|
+
it "should respect :subunit_to_unit currency property" do
|
1054
|
+
Money.from_numeric(1, "USD").should == Money.new(1_00, "USD")
|
1055
|
+
Money.from_numeric(1, "TND").should == Money.new(1_000, "TND")
|
1056
|
+
Money.from_numeric(1, "CLP").should == Money.new(1, "CLP")
|
1057
|
+
end
|
1058
|
+
|
1059
|
+
it "accepts a bank option" do
|
1060
|
+
Money.from_numeric(1).should == Money.new(1_00)
|
1061
|
+
Money.from_numeric(1).should == Money.new(1_00, "USD")
|
1062
|
+
Money.from_numeric(1, "EUR").should == Money.new(1_00, "EUR")
|
1063
|
+
end
|
1064
|
+
|
1065
|
+
it "accepts a currency options" do
|
1066
|
+
m = Money.from_numeric(1)
|
1067
|
+
m.currency.should == Money.default_currency
|
1068
|
+
|
1069
|
+
m = Money.from_numeric(1, Money::Currency.wrap("EUR"))
|
1070
|
+
m.currency.should == Money::Currency.wrap("EUR")
|
1071
|
+
|
1072
|
+
m = Money.from_numeric(1, "EUR")
|
1073
|
+
m.currency.should == Money::Currency.wrap("EUR")
|
1074
|
+
end
|
1075
|
+
end
|
1076
|
+
|
1077
|
+
describe "split" do
|
1078
|
+
specify "#split needs at least one party" do
|
1079
|
+
lambda {Money.us_dollar(1).split(0)}.should raise_error(ArgumentError)
|
1080
|
+
lambda {Money.us_dollar(1).split(-1)}.should raise_error(ArgumentError)
|
1081
|
+
end
|
1082
|
+
|
1083
|
+
|
1084
|
+
specify "#gives 1 cent to both people if we start with 2" do
|
1085
|
+
Money.us_dollar(2).split(2).should == [Money.us_dollar(1), Money.us_dollar(1)]
|
1086
|
+
end
|
1087
|
+
|
1088
|
+
specify "#split may distribute no money to some parties if there isnt enough to go around" do
|
1089
|
+
Money.us_dollar(2).split(3).should == [Money.us_dollar(1), Money.us_dollar(1), Money.us_dollar(0)]
|
1090
|
+
end
|
1091
|
+
|
1092
|
+
specify "#split does not lose pennies" do
|
1093
|
+
Money.us_dollar(5).split(2).should == [Money.us_dollar(3), Money.us_dollar(2)]
|
1094
|
+
end
|
1095
|
+
|
1096
|
+
specify "#split a dollar" do
|
1097
|
+
moneys = Money.us_dollar(100).split(3)
|
1098
|
+
moneys[0].cents.should == 34
|
1099
|
+
moneys[1].cents.should == 33
|
1100
|
+
moneys[2].cents.should == 33
|
1101
|
+
end
|
1102
|
+
end
|
1103
|
+
|
1104
|
+
describe "allocation" do
|
1105
|
+
specify "#allocate takes no action when one gets all" do
|
1106
|
+
Money.us_dollar(005).allocate([1]).should == [Money.us_dollar(5)]
|
1107
|
+
end
|
1108
|
+
|
1109
|
+
specify "#allocate keeps currencies intact" do
|
1110
|
+
Money.ca_dollar(005).allocate([1]).should == [Money.ca_dollar(5)]
|
1111
|
+
end
|
1112
|
+
|
1113
|
+
specify "#allocate does not loose pennies" do
|
1114
|
+
moneys = Money.us_dollar(5).allocate([0.3,0.7])
|
1115
|
+
moneys[0].should == Money.us_dollar(2)
|
1116
|
+
moneys[1].should == Money.us_dollar(3)
|
1117
|
+
end
|
1118
|
+
|
1119
|
+
specify "#allocate does not loose pennies" do
|
1120
|
+
moneys = Money.us_dollar(100).allocate([0.333,0.333, 0.333])
|
1121
|
+
moneys[0].cents.should == 34
|
1122
|
+
moneys[1].cents.should == 33
|
1123
|
+
moneys[2].cents.should == 33
|
1124
|
+
end
|
1125
|
+
|
1126
|
+
specify "#allocate requires total to be less then 1" do
|
1127
|
+
lambda { Money.us_dollar(0.05).allocate([0.5,0.6]) }.should raise_error(ArgumentError)
|
1128
|
+
end
|
1129
|
+
end
|
1130
|
+
|
1131
|
+
describe "Money.add_rate" do
|
1132
|
+
it "saves rate into current bank" do
|
1133
|
+
Money.add_rate("EUR", "USD", 10)
|
1134
|
+
Money.new(10_00, "EUR").exchange_to("USD").should == Money.new(100_00, "USD")
|
1135
|
+
end
|
1136
|
+
end
|
1137
|
+
|
1138
|
+
|
1139
|
+
# Sets $VERBOSE to nil for the duration of the block and back to its original value afterwards.
|
1140
|
+
#
|
1141
|
+
# silence_warnings do
|
1142
|
+
# value = noisy_call # no warning voiced
|
1143
|
+
# end
|
1144
|
+
#
|
1145
|
+
# noisy_call # warning voiced
|
1146
|
+
def silence_warnings
|
1147
|
+
old_verbose, $VERBOSE = $VERBOSE, nil
|
1148
|
+
yield
|
1149
|
+
ensure
|
1150
|
+
$VERBOSE = old_verbose
|
1151
|
+
end
|
1152
|
+
|
1153
|
+
end
|
1154
|
+
|
1155
|
+
describe "Actions involving two Money objects" do
|
1156
|
+
describe "if the other Money object has the same currency" do
|
1157
|
+
specify "#<=> compares the two object amounts" do
|
1158
|
+
(Money.new(1_00, "USD") <=> Money.new(1_00, "USD")).should == 0
|
1159
|
+
(Money.new(1_00, "USD") <=> Money.new(99, "USD")).should > 0
|
1160
|
+
(Money.new(1_00, "USD") <=> Money.new(2_00, "USD")).should < 0
|
1161
|
+
end
|
1162
|
+
|
1163
|
+
specify "#+ adds other amount to current amount and returns a Money while retaining the currency" do
|
1164
|
+
(Money.new(10_00, "USD") + Money.new(90, "USD")).should == Money.new(10_90, "USD")
|
1165
|
+
end
|
1166
|
+
|
1167
|
+
specify "#- subtracts other amount from current amount and returns a Money while retaining the currency" do
|
1168
|
+
(Money.new(10_00, "USD") - Money.new(90, "USD")).should == Money.new(9_10, "USD")
|
1169
|
+
end
|
1170
|
+
|
1171
|
+
specify "#* should raise ArgumentError" do
|
1172
|
+
lambda { Money.new(10_00, "USD") * Money.new(2, "USD") }.should raise_error(ArgumentError)
|
1173
|
+
end
|
1174
|
+
|
1175
|
+
specify "#/ divides current amount by other amount and returns a Float" do
|
1176
|
+
(Money.new(10_00, "USD") / Money.new(100_00, "USD")).should == 0.1
|
1177
|
+
end
|
1178
|
+
end
|
1179
|
+
|
1180
|
+
describe "if the other Money object has a different currency" do
|
1181
|
+
specify "#<=> converts other object amount to current currency, then compares the two object amounts" do
|
1182
|
+
target = Money.new(200_00, "EUR")
|
1183
|
+
target.should_receive(:exchange_to).with(Money::Currency.new("USD")).and_return(Money.new(300_00, "USD"))
|
1184
|
+
(Money.new(100_00, "USD") <=> target).should < 0
|
1185
|
+
|
1186
|
+
target = Money.new(200_00, "EUR")
|
1187
|
+
target.should_receive(:exchange_to).with(Money::Currency.new("USD")).and_return(Money.new(100_00, "USD"))
|
1188
|
+
(Money.new(100_00, "USD") <=> target).should == 0
|
1189
|
+
|
1190
|
+
target = Money.new(200_00, "EUR")
|
1191
|
+
target.should_receive(:exchange_to).with(Money::Currency.new("USD")).and_return(Money.new(99_00, "USD"))
|
1192
|
+
(Money.new(100_00, "USD") <=> target).should > 0
|
1193
|
+
end
|
1194
|
+
|
1195
|
+
specify "#+ converts other object amount to current currency, then adds other amount to current amount and returns a Money" do
|
1196
|
+
other = Money.new(90, "EUR")
|
1197
|
+
other.should_receive(:exchange_to).with(Money::Currency.new("USD")).and_return(Money.new(9_00, "USD"))
|
1198
|
+
(Money.new(10_00, "USD") + other).should == Money.new(19_00, "USD")
|
1199
|
+
end
|
1200
|
+
|
1201
|
+
specify "#- converts other object amount to current currency, then subtracts other amount from current amount and returns a Money" do
|
1202
|
+
other = Money.new(90, "EUR")
|
1203
|
+
other.should_receive(:exchange_to).with(Money::Currency.new("USD")).and_return(Money.new(9_00, "USD"))
|
1204
|
+
(Money.new(10_00, "USD") - other).should == Money.new(1_00, "USD")
|
1205
|
+
end
|
1206
|
+
|
1207
|
+
specify "#* should raise ArgumentError" do
|
1208
|
+
lambda { Money.new(10_00, "USD") * Money.new(10, "EUR") }.should raise_error(ArgumentError)
|
1209
|
+
end
|
1210
|
+
|
1211
|
+
specify "#/ converts other object amount to current currency, then divides current amount by other amount and returns a Float" do
|
1212
|
+
other = Money.new(1000, "EUR")
|
1213
|
+
other.should_receive(:exchange_to).with(Money::Currency.new("USD")).and_return(Money.new(100_00, "USD"))
|
1214
|
+
(Money.new(10_00, "USD") / other).should == 0.1
|
1215
|
+
end
|
1216
|
+
end
|
1217
|
+
end
|
1218
|
+
|
1219
|
+
|
1220
|
+
describe "Money.parse" do
|
1221
|
+
|
1222
|
+
it "should be able to parse european-formatted inputs under 10EUR" do
|
1223
|
+
five_ninety_five = Money.new(595, 'EUR')
|
1224
|
+
|
1225
|
+
Money.parse('EUR 5,95').should == five_ninety_five
|
1226
|
+
#TODO: try and handle these
|
1227
|
+
#Money.parse('€5,95').should == five_ninety_five
|
1228
|
+
#Money.parse('$5.95').should == five_ninety_five
|
1229
|
+
end
|
1230
|
+
|
1231
|
+
it "should be able to parse european-formatted inputs with multiple thousands-seperators" do
|
1232
|
+
Money.parse('EUR 1.234.567,89').should == Money.new(123456789, 'EUR')
|
1233
|
+
Money.parse('EUR 1.111.234.567,89').should == Money.new(111123456789, 'EUR')
|
1234
|
+
end
|
1235
|
+
|
1236
|
+
it "should be able to parse USD-formatted inputs under $10" do
|
1237
|
+
five_ninety_five = Money.new(595, 'USD')
|
1238
|
+
|
1239
|
+
Money.parse(5.95).should == five_ninety_five
|
1240
|
+
Money.parse('5.95').should == five_ninety_five
|
1241
|
+
Money.parse('$5.95').should == five_ninety_five
|
1242
|
+
Money.parse("\n $5.95 \n").should == five_ninety_five
|
1243
|
+
Money.parse('$ 5.95').should == five_ninety_five
|
1244
|
+
Money.parse('$5.95 ea.').should == five_ninety_five
|
1245
|
+
Money.parse('$5.95, each').should == five_ninety_five
|
1246
|
+
end
|
1247
|
+
|
1248
|
+
it "should be able to parse USD-formatted inputs with multiple thousands-seperators" do
|
1249
|
+
Money.parse('1,234,567.89').should == Money.new(123456789, 'USD')
|
1250
|
+
Money.parse('1,111,234,567.89').should == Money.new(111123456789, 'USD')
|
1251
|
+
end
|
1252
|
+
|
1253
|
+
it "should not return a price if there is a price range" do
|
1254
|
+
lambda {Money.parse('$5.95-10.95')}.should raise_error ArgumentError
|
1255
|
+
lambda {Money.parse('$5.95 - 10.95')}.should raise_error ArgumentError
|
1256
|
+
lambda {Money.parse('$5.95 - $10.95')}.should raise_error ArgumentError
|
1257
|
+
end
|
1258
|
+
|
1259
|
+
it "should not return a price for completely invalid input" do
|
1260
|
+
# TODO: shouldn't these throw an error instead of being considered
|
1261
|
+
# equal to $0.0?
|
1262
|
+
empty_price = Money.new(0, 'USD')
|
1263
|
+
|
1264
|
+
Money.parse(nil).should == empty_price
|
1265
|
+
Money.parse('hellothere').should == empty_price
|
1266
|
+
Money.parse('').should == empty_price
|
1267
|
+
end
|
1268
|
+
end
|