sevenwire-money 2.0.0 → 2.1.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/MIT-LICENSE +1 -0
- data/lib/money/core_extensions.rb +24 -9
- data/lib/money/errors.rb +1 -2
- data/lib/money/money.rb +88 -93
- data/lib/money/variable_exchange_bank.rb +12 -11
- data/lib/money.rb +4 -23
- data/money.gemspec +5 -5
- data/test/core_extensions_spec.rb +33 -31
- data/test/exchange_bank_spec.rb +43 -40
- data/test/money_spec.rb +119 -116
- metadata +5 -4
data/MIT-LICENSE
CHANGED
|
@@ -1,15 +1,24 @@
|
|
|
1
1
|
class Numeric
|
|
2
|
+
|
|
2
3
|
# Converts this numeric to a Money object in the default currency. It
|
|
3
4
|
# multiplies the numeric value by 100 and treats that as cents.
|
|
4
5
|
#
|
|
5
6
|
# 100.to_money => #<Money @cents=10000>
|
|
6
7
|
# 100.37.to_money => #<Money @cents=10037>
|
|
7
|
-
|
|
8
|
-
|
|
8
|
+
# 1000.to_money(:millicents) => #<Money @cents=1>
|
|
9
|
+
def to_money(units=:cents)
|
|
10
|
+
if units == :millicents
|
|
11
|
+
Money.new(self, :units => :millicents)
|
|
12
|
+
else
|
|
13
|
+
Money.new(self * 100)
|
|
14
|
+
end
|
|
9
15
|
end
|
|
16
|
+
|
|
10
17
|
end
|
|
11
18
|
|
|
19
|
+
|
|
12
20
|
class String
|
|
21
|
+
|
|
13
22
|
# Parses the current string and converts it to a Money object.
|
|
14
23
|
# Excess characters will be discarded.
|
|
15
24
|
#
|
|
@@ -18,21 +27,27 @@ class String
|
|
|
18
27
|
# '100 USD'.to_money # => #<Money @cents=10000, @currency="USD">
|
|
19
28
|
# 'USD 100'.to_money # => #<Money @cents=10000, @currency="USD">
|
|
20
29
|
# '$100 USD'.to_money # => #<Money @cents=10000, @currency="USD">
|
|
21
|
-
|
|
30
|
+
# '1000'.to_money(:millicents) # => #<Money @cents=1, @currency="USD">
|
|
31
|
+
def to_money(units=:cents)
|
|
22
32
|
# Get the currency.
|
|
23
|
-
matches = scan /([A-Z]{2,3})/
|
|
33
|
+
matches = scan /([A-Z]{2,3})/
|
|
24
34
|
currency = matches[0] ? matches[0][0] : Money.default_currency
|
|
25
|
-
|
|
35
|
+
|
|
26
36
|
# Get the cents amount
|
|
27
37
|
sans_spaces = gsub(/\s+/, '')
|
|
28
38
|
matches = sans_spaces.scan /(\-?\d+(?:[\.,]\d+)?)/
|
|
29
|
-
|
|
39
|
+
money = if matches[0]
|
|
30
40
|
value = matches[0][0].gsub(/,/, '.')
|
|
31
|
-
value.to_f
|
|
41
|
+
value.to_f
|
|
32
42
|
else
|
|
33
43
|
0
|
|
34
44
|
end
|
|
35
|
-
|
|
36
|
-
|
|
45
|
+
|
|
46
|
+
if units == :millicents
|
|
47
|
+
Money.new(money, currency, :units => :millicents)
|
|
48
|
+
else
|
|
49
|
+
Money.new(money * 100, currency)
|
|
50
|
+
end
|
|
37
51
|
end
|
|
52
|
+
|
|
38
53
|
end
|
data/lib/money/errors.rb
CHANGED
data/lib/money/money.rb
CHANGED
|
@@ -1,23 +1,24 @@
|
|
|
1
|
-
require 'money/variable_exchange_bank'
|
|
2
|
-
|
|
3
1
|
# Represents an amount of money in a certain currency.
|
|
4
2
|
class Money
|
|
3
|
+
|
|
5
4
|
include Comparable
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
5
|
+
|
|
6
|
+
|
|
7
|
+
### ATTRIBUTES
|
|
8
|
+
|
|
9
9
|
class << self
|
|
10
|
+
|
|
10
11
|
# Each Money object is associated to a bank object, which is responsible
|
|
11
12
|
# for currency exchange. This property allows one to specify the default
|
|
12
13
|
# bank object.
|
|
13
14
|
#
|
|
14
15
|
# bank1 = MyBank.new
|
|
15
16
|
# bank2 = MyOtherBank.new
|
|
16
|
-
#
|
|
17
|
+
#
|
|
17
18
|
# Money.default_bank = bank1
|
|
18
19
|
# money1 = Money.new(10)
|
|
19
20
|
# money1.bank # => bank1
|
|
20
|
-
#
|
|
21
|
+
#
|
|
21
22
|
# Money.default_bank = bank2
|
|
22
23
|
# money2 = Money.new(10)
|
|
23
24
|
# money2.bank # => bank2
|
|
@@ -31,117 +32,131 @@ class Money
|
|
|
31
32
|
# Money.us_dollar(100).exchange_to("CAD") # => Money.ca_dollar(124)
|
|
32
33
|
# Money.ca_dollar(100).exchange_to("USD") # => Money.us_dollar(80)
|
|
33
34
|
attr_accessor :default_bank
|
|
34
|
-
|
|
35
|
+
|
|
35
36
|
# The default currency, which is used when <tt>Money.new</tt> is called
|
|
36
37
|
# without an explicit currency argument. The default value is "USD".
|
|
37
38
|
attr_accessor :default_currency
|
|
39
|
+
|
|
38
40
|
end
|
|
39
|
-
|
|
41
|
+
|
|
42
|
+
# Set the default bank and currency.
|
|
40
43
|
self.default_bank = VariableExchangeBank.instance
|
|
41
44
|
self.default_currency = "USD"
|
|
42
|
-
|
|
43
|
-
|
|
45
|
+
|
|
46
|
+
# Set up attribute accessors for millicents, currency, and bank.
|
|
47
|
+
attr_reader :cents, :millicents, :currency, :bank
|
|
48
|
+
|
|
49
|
+
|
|
50
|
+
### INSTANTIATION
|
|
51
|
+
|
|
52
|
+
# Creates a new money object.
|
|
53
|
+
# Money.new(100)
|
|
54
|
+
#
|
|
55
|
+
# By default, it will accept money in cents, however,
|
|
56
|
+
# you can also pass millicents by specifying :units => :millicents.
|
|
57
|
+
def initialize(money, *args)
|
|
58
|
+
options = args.last.is_a?(Hash) ? args.pop : {}
|
|
59
|
+
|
|
60
|
+
if options[:units] == :millicents
|
|
61
|
+
@millicents = money.round
|
|
62
|
+
@cents = (money / 1000.0).round
|
|
63
|
+
else
|
|
64
|
+
@millicents = (money * 1000.0).round
|
|
65
|
+
@cents = money.round
|
|
66
|
+
end
|
|
67
|
+
@currency = args[0] || Money.default_currency
|
|
68
|
+
@bank = args[1] || Money.default_bank
|
|
69
|
+
end
|
|
70
|
+
|
|
44
71
|
# Create a new money object with value 0.
|
|
45
72
|
def self.empty(currency = default_currency)
|
|
46
|
-
|
|
73
|
+
new(0, currency)
|
|
47
74
|
end
|
|
48
75
|
|
|
49
|
-
# Creates a new Money object of the given value, using the Canadian dollar currency.
|
|
50
|
-
def self.ca_dollar(cents)
|
|
51
|
-
Money.new(cents, "CAD")
|
|
52
|
-
end
|
|
53
76
|
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
end
|
|
58
|
-
|
|
59
|
-
# Creates a new Money object of the given value, using the Euro currency.
|
|
60
|
-
def self.euro(cents)
|
|
61
|
-
Money.new(cents, "EUR")
|
|
62
|
-
end
|
|
63
|
-
|
|
77
|
+
### EXHANGES
|
|
78
|
+
|
|
79
|
+
# Adds a new exchange rate from one currency to another.
|
|
64
80
|
def self.add_rate(from_currency, to_currency, rate)
|
|
65
|
-
|
|
81
|
+
default_bank.add_rate(from_currency, to_currency, rate)
|
|
66
82
|
end
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
#
|
|
72
|
-
# Alternativly you can use the convinience methods like
|
|
73
|
-
# Money.ca_dollar and Money.us_dollar
|
|
74
|
-
def initialize(cents, currency = Money.default_currency, bank = Money.default_bank)
|
|
75
|
-
@cents = cents.round
|
|
76
|
-
@currency = currency
|
|
77
|
-
@bank = bank
|
|
83
|
+
|
|
84
|
+
# Recieve the amount of this money object in another currency.
|
|
85
|
+
def exchange_to(other_currency)
|
|
86
|
+
self.class.new(@bank.exchange(millicents, currency, other_currency), other_currency, :units => :millicents)
|
|
78
87
|
end
|
|
79
88
|
|
|
80
|
-
|
|
89
|
+
|
|
90
|
+
### COMPARISONS
|
|
91
|
+
|
|
92
|
+
# Are two money objects equal? Only works if both objects are of the same currency.
|
|
81
93
|
def ==(other_money)
|
|
82
|
-
|
|
94
|
+
millicents == other_money.millicents && bank.same_currency?(currency, other_money.currency)
|
|
83
95
|
end
|
|
84
96
|
|
|
97
|
+
# Compare two money objects.
|
|
85
98
|
def <=>(other_money)
|
|
86
99
|
if bank.same_currency?(currency, other_money.currency)
|
|
87
|
-
|
|
100
|
+
millicents <=> other_money.millicents
|
|
88
101
|
else
|
|
89
|
-
|
|
102
|
+
millicents <=> other_money.exchange_to(currency).millicents
|
|
90
103
|
end
|
|
91
104
|
end
|
|
92
105
|
|
|
106
|
+
|
|
107
|
+
### MATH
|
|
108
|
+
|
|
109
|
+
# Add two money objects.
|
|
93
110
|
def +(other_money)
|
|
94
111
|
if currency == other_money.currency
|
|
95
|
-
|
|
112
|
+
self.class.new(millicents + other_money.millicents, other_money.currency, :units => :millicents)
|
|
96
113
|
else
|
|
97
|
-
|
|
98
|
-
end
|
|
114
|
+
self.class.new(millicents + other_money.exchange_to(currency).millicents, currency, :units => :millicents)
|
|
115
|
+
end
|
|
99
116
|
end
|
|
100
117
|
|
|
118
|
+
# Subtract two money objects.
|
|
101
119
|
def -(other_money)
|
|
102
120
|
if currency == other_money.currency
|
|
103
|
-
|
|
121
|
+
self.class.new(millicents - other_money.millicents, other_money.currency, :units => :millicents)
|
|
104
122
|
else
|
|
105
|
-
|
|
106
|
-
end
|
|
107
|
-
end
|
|
108
|
-
|
|
109
|
-
# get the cents value of the object
|
|
110
|
-
def cents
|
|
111
|
-
@cents
|
|
123
|
+
self.class.new(millicents - other_money.exchange_to(currency).millicents, currency, :units => :millicents)
|
|
124
|
+
end
|
|
112
125
|
end
|
|
113
126
|
|
|
114
|
-
#
|
|
127
|
+
# Multiply money by fixnum.
|
|
115
128
|
def *(fixnum)
|
|
116
|
-
|
|
129
|
+
self.class.new(millicents * fixnum, currency, :units => :millicents)
|
|
117
130
|
end
|
|
118
131
|
|
|
119
|
-
#
|
|
132
|
+
# Divide money by fixnum.
|
|
120
133
|
def /(fixnum)
|
|
121
|
-
|
|
134
|
+
self.class.new(millicents / fixnum, currency, :units => :millicents)
|
|
122
135
|
end
|
|
123
|
-
|
|
124
|
-
# Test if the money amount is zero
|
|
136
|
+
|
|
137
|
+
# Test if the money amount is zero.
|
|
125
138
|
def zero?
|
|
126
|
-
|
|
139
|
+
millicents == 0
|
|
127
140
|
end
|
|
128
141
|
|
|
129
142
|
|
|
143
|
+
### OUTPUT/CONVERSIONS
|
|
144
|
+
|
|
130
145
|
# Format the price according to several rules
|
|
131
146
|
# Currently supported are :with_currency, :no_cents and :html
|
|
132
147
|
#
|
|
133
|
-
# with_currency:
|
|
148
|
+
# with_currency:
|
|
134
149
|
#
|
|
135
150
|
# Money.ca_dollar(0).format => "free"
|
|
136
151
|
# Money.ca_dollar(100).format => "$1.00"
|
|
137
152
|
# Money.ca_dollar(100).format(:with_currency) => "$1.00 CAD"
|
|
138
153
|
# Money.us_dollar(85).format(:with_currency) => "$0.85 USD"
|
|
139
154
|
#
|
|
140
|
-
# no_cents:
|
|
155
|
+
# no_cents:
|
|
141
156
|
#
|
|
142
157
|
# Money.ca_dollar(100).format(:no_cents) => "$1"
|
|
143
158
|
# Money.ca_dollar(599).format(:no_cents) => "$5"
|
|
144
|
-
#
|
|
159
|
+
#
|
|
145
160
|
# Money.ca_dollar(570).format(:no_cents, :with_currency) => "$5 CAD"
|
|
146
161
|
# Money.ca_dollar(39000).format(:no_cents) => "$390"
|
|
147
162
|
#
|
|
@@ -152,9 +167,9 @@ class Money
|
|
|
152
167
|
rules = rules.flatten
|
|
153
168
|
|
|
154
169
|
if rules.include?(:no_cents)
|
|
155
|
-
formatted = sprintf("$%d", cents.to_f / 100
|
|
170
|
+
formatted = sprintf("$%d", cents.to_f / 100)
|
|
156
171
|
else
|
|
157
|
-
formatted = sprintf("$%.2f", cents.to_f / 100
|
|
172
|
+
formatted = sprintf("$%.2f", cents.to_f / 100)
|
|
158
173
|
end
|
|
159
174
|
|
|
160
175
|
if rules.include?(:with_currency)
|
|
@@ -165,37 +180,17 @@ class Money
|
|
|
165
180
|
end
|
|
166
181
|
formatted
|
|
167
182
|
end
|
|
168
|
-
|
|
169
|
-
#
|
|
183
|
+
|
|
184
|
+
# Output a money object as a string.
|
|
185
|
+
# Example:
|
|
186
|
+
# Money.new(100).to_s => "1.00"
|
|
170
187
|
def to_s
|
|
171
|
-
sprintf("%.2f", cents / 100
|
|
172
|
-
end
|
|
173
|
-
|
|
174
|
-
# Recieve the amount of this money object in another currency.
|
|
175
|
-
def exchange_to(other_currency)
|
|
176
|
-
Money.new(@bank.exchange(self.cents, currency, other_currency), other_currency)
|
|
177
|
-
end
|
|
178
|
-
|
|
179
|
-
# Recieve a money object with the same amount as the current Money object
|
|
180
|
-
# in american dollar
|
|
181
|
-
def as_us_dollar
|
|
182
|
-
exchange_to("USD")
|
|
183
|
-
end
|
|
184
|
-
|
|
185
|
-
# Recieve a money object with the same amount as the current Money object
|
|
186
|
-
# in canadian dollar
|
|
187
|
-
def as_ca_dollar
|
|
188
|
-
exchange_to("CAD")
|
|
189
|
-
end
|
|
190
|
-
|
|
191
|
-
# Recieve a money object with the same amount as the current Money object
|
|
192
|
-
# in euro
|
|
193
|
-
def as_euro
|
|
194
|
-
exchange_to("EUR")
|
|
188
|
+
sprintf("%.2f", cents.to_f / 100)
|
|
195
189
|
end
|
|
196
|
-
|
|
190
|
+
|
|
197
191
|
# Conversation to self
|
|
198
192
|
def to_money
|
|
199
193
|
self
|
|
200
|
-
end
|
|
194
|
+
end
|
|
195
|
+
|
|
201
196
|
end
|
|
@@ -1,5 +1,4 @@
|
|
|
1
1
|
require 'thread'
|
|
2
|
-
require 'money/errors'
|
|
3
2
|
|
|
4
3
|
# Class for aiding in exchanging money between different currencies.
|
|
5
4
|
# By default, the Money class uses an object of this class (accessible through
|
|
@@ -12,12 +11,13 @@ require 'money/errors'
|
|
|
12
11
|
# bank = Money::VariableExchangeBank.new
|
|
13
12
|
# bank.add_rate("USD", "CAD", 1.24515)
|
|
14
13
|
# bank.add_rate("CAD", "USD", 0.803115)
|
|
15
|
-
#
|
|
14
|
+
#
|
|
16
15
|
# # Exchange 100 CAD to USD:
|
|
17
16
|
# bank.exchange(100_00, "CAD", "USD") # => 124
|
|
18
17
|
# # Exchange 100 USD to CAD:
|
|
19
18
|
# bank.exchange(100_00, "USD", "CAD") # => 80
|
|
20
19
|
class Money
|
|
20
|
+
|
|
21
21
|
class VariableExchangeBank
|
|
22
22
|
# Returns the singleton instance of VariableExchangeBank.
|
|
23
23
|
#
|
|
@@ -25,27 +25,27 @@ class Money
|
|
|
25
25
|
def self.instance
|
|
26
26
|
@@singleton
|
|
27
27
|
end
|
|
28
|
-
|
|
28
|
+
|
|
29
29
|
def initialize
|
|
30
30
|
@rates = {}
|
|
31
31
|
@mutex = Mutex.new
|
|
32
32
|
end
|
|
33
|
-
|
|
33
|
+
|
|
34
34
|
# Registers a conversion rate. +from+ and +to+ are both currency names.
|
|
35
35
|
def add_rate(from, to, rate)
|
|
36
36
|
@mutex.synchronize do
|
|
37
37
|
@rates["#{from}_TO_#{to}".upcase] = rate
|
|
38
38
|
end
|
|
39
39
|
end
|
|
40
|
-
|
|
40
|
+
|
|
41
41
|
# Gets the rate for exchanging the currency named +from+ to the currency
|
|
42
42
|
# named +to+. Returns nil if the rate is unknown.
|
|
43
43
|
def get_rate(from, to)
|
|
44
44
|
@mutex.synchronize do
|
|
45
|
-
@rates["#{from}_TO_#{to}".upcase]
|
|
45
|
+
@rates["#{from}_TO_#{to}".upcase]
|
|
46
46
|
end
|
|
47
47
|
end
|
|
48
|
-
|
|
48
|
+
|
|
49
49
|
# Given two currency names, checks whether they're both the same currency.
|
|
50
50
|
#
|
|
51
51
|
# bank = VariableExchangeBank.new
|
|
@@ -54,19 +54,20 @@ class Money
|
|
|
54
54
|
def same_currency?(currency1, currency2)
|
|
55
55
|
currency1.upcase == currency2.upcase
|
|
56
56
|
end
|
|
57
|
-
|
|
57
|
+
|
|
58
58
|
# Exchange the given amount of cents in +from_currency+ to +to_currency+.
|
|
59
59
|
# Returns the amount of cents in +to_currency+ as an integer, rounded down.
|
|
60
60
|
#
|
|
61
61
|
# If the conversion rate is unknown, then Money::UnknownRate will be raised.
|
|
62
|
-
def exchange(
|
|
62
|
+
def exchange(millicents, from_currency, to_currency)
|
|
63
63
|
rate = get_rate(from_currency, to_currency)
|
|
64
64
|
if !rate
|
|
65
65
|
raise Money::UnknownRate, "No conversion rate known for '#{from_currency}' -> '#{to_currency}'"
|
|
66
66
|
end
|
|
67
|
-
(
|
|
67
|
+
(millicents * rate).floor
|
|
68
68
|
end
|
|
69
|
-
|
|
69
|
+
|
|
70
70
|
@@singleton = VariableExchangeBank.new
|
|
71
71
|
end
|
|
72
|
+
|
|
72
73
|
end
|
data/lib/money.rb
CHANGED
|
@@ -1,25 +1,6 @@
|
|
|
1
|
-
# Copyright (c) 2005 Tobias Luetke
|
|
2
|
-
# Copyright (c) 2008 Phusion
|
|
3
|
-
#
|
|
4
|
-
# Permission is hereby granted, free of charge, to any person obtaining
|
|
5
|
-
# a copy of this software and associated documentation files (the
|
|
6
|
-
# "Software"), to deal in the Software without restriction, including
|
|
7
|
-
# without limitation the rights to use, copy, modify, merge, publish,
|
|
8
|
-
# distribute, sublicense, and/or sell copies of the Software, and to
|
|
9
|
-
# permit persons to whom the Software is furnished to do so, subject to
|
|
10
|
-
# the following conditions:
|
|
11
|
-
#
|
|
12
|
-
# The above copyright notice and this permission notice shall be
|
|
13
|
-
# included in all copies or substantial portions of the Software.
|
|
14
|
-
#
|
|
15
|
-
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
|
16
|
-
# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
|
17
|
-
# MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
|
18
|
-
# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
|
19
|
-
# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
|
20
|
-
# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
|
21
|
-
# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
|
22
|
-
|
|
23
1
|
$LOAD_PATH << File.expand_path(File.dirname(__FILE__))
|
|
24
|
-
require 'money/
|
|
2
|
+
require 'money/variable_exchange_bank'
|
|
3
|
+
require 'money/currencies'
|
|
4
|
+
require 'money/errors'
|
|
25
5
|
require 'money/core_extensions'
|
|
6
|
+
require 'money/money'
|
data/money.gemspec
CHANGED
|
@@ -1,13 +1,13 @@
|
|
|
1
1
|
Gem::Specification.new do |s|
|
|
2
2
|
s.name = "money"
|
|
3
|
-
s.version = "2.
|
|
3
|
+
s.version = "2.1.0"
|
|
4
4
|
s.summary = "Money and currency exchange support library"
|
|
5
|
-
s.email = "
|
|
6
|
-
s.homepage = "http://
|
|
7
|
-
s.description = "Money and currency exchange support library.
|
|
5
|
+
s.email = "brandon@sevenwire.com"
|
|
6
|
+
s.homepage = "http://github.com/sevenwire/money"
|
|
7
|
+
s.description = "Money and currency exchange support library."
|
|
8
8
|
s.has_rdoc = true
|
|
9
9
|
s.rubyforge_project = "money"
|
|
10
|
-
s.authors = ["Tobias Luetke", "Hongli Lai"]
|
|
10
|
+
s.authors = ["Tobias Luetke", "Hongli Lai", "Brandon Arbini"]
|
|
11
11
|
|
|
12
12
|
s.files = [
|
|
13
13
|
"README.rdoc", "MIT-LICENSE", "money.gemspec", "Rakefile",
|
|
@@ -2,35 +2,37 @@ $LOAD_PATH << File.expand_path(File.dirname(__FILE__) + "/../lib")
|
|
|
2
2
|
require 'money/core_extensions'
|
|
3
3
|
|
|
4
4
|
describe "Money core extensions" do
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
5
|
+
|
|
6
|
+
specify "Numeric#to_money works" do
|
|
7
|
+
money = 1234.to_money
|
|
8
|
+
money.cents.should == 1234_00
|
|
9
|
+
money.currency.should == Money.default_currency
|
|
10
|
+
|
|
11
|
+
money = 100.37.to_money
|
|
12
|
+
money.cents.should == 100_37
|
|
13
|
+
money.currency.should == Money.default_currency
|
|
14
|
+
end
|
|
15
|
+
|
|
16
|
+
specify "String#to_money works" do
|
|
17
|
+
"100".to_money.should == Money.new(100_00)
|
|
18
|
+
"100.37".to_money.should == Money.new(100_37)
|
|
19
|
+
"100,37".to_money.should == Money.new(100_37)
|
|
20
|
+
"100 000".to_money.should == Money.new(100_000_00)
|
|
21
|
+
|
|
22
|
+
"100 USD".to_money.should == Money.new(100_00, "USD")
|
|
23
|
+
"-100 USD".to_money.should == Money.new(-100_00, "USD")
|
|
24
|
+
"100 EUR".to_money.should == Money.new(100_00, "EUR")
|
|
25
|
+
"100.37 EUR".to_money.should == Money.new(100_37, "EUR")
|
|
26
|
+
"100,37 EUR".to_money.should == Money.new(100_37, "EUR")
|
|
27
|
+
|
|
28
|
+
"USD 100".to_money.should == Money.new(100_00, "USD")
|
|
29
|
+
"EUR 100".to_money.should == Money.new(100_00, "EUR")
|
|
30
|
+
"EUR 100.37".to_money.should == Money.new(100_37, "EUR")
|
|
31
|
+
"CAD -100.37".to_money.should == Money.new(-100_37, "CAD")
|
|
32
|
+
"EUR 100,37".to_money.should == Money.new(100_37, "EUR")
|
|
33
|
+
"EUR -100,37".to_money.should == Money.new(-100_37, "EUR")
|
|
34
|
+
|
|
35
|
+
"$100 USD".to_money.should == Money.new(100_00, "USD")
|
|
36
|
+
end
|
|
37
|
+
|
|
36
38
|
end
|
data/test/exchange_bank_spec.rb
CHANGED
|
@@ -1,45 +1,48 @@
|
|
|
1
1
|
$LOAD_PATH << File.expand_path(File.dirname(__FILE__) + "/../lib")
|
|
2
2
|
require 'money/variable_exchange_bank'
|
|
3
|
+
require 'money/errors'
|
|
3
4
|
|
|
4
5
|
describe Money::VariableExchangeBank do
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
6
|
+
|
|
7
|
+
before :each do
|
|
8
|
+
@bank = Money::VariableExchangeBank.new
|
|
9
|
+
end
|
|
10
|
+
|
|
11
|
+
it "returns the previously specified conversion rate" do
|
|
12
|
+
@bank.add_rate("USD", "EUR", 0.788332676)
|
|
13
|
+
@bank.add_rate("EUR", "YEN", 122.631477)
|
|
14
|
+
@bank.get_rate("USD", "EUR").should == 0.788332676
|
|
15
|
+
@bank.get_rate("EUR", "YEN").should == 122.631477
|
|
16
|
+
end
|
|
17
|
+
|
|
18
|
+
it "treats currency names case-insensitively" do
|
|
19
|
+
@bank.add_rate("usd", "eur", 1)
|
|
20
|
+
@bank.get_rate("USD", "EUR").should == 1
|
|
21
|
+
@bank.same_currency?("USD", "usd").should be_true
|
|
22
|
+
@bank.same_currency?("EUR", "usd").should be_false
|
|
23
|
+
end
|
|
24
|
+
|
|
25
|
+
it "returns nil if the conversion rate is unknown" do
|
|
26
|
+
@bank.get_rate("American Pesos", "EUR").should be_nil
|
|
27
|
+
end
|
|
28
|
+
|
|
29
|
+
it "exchanges money from one currency to another according to the specified conversion rates" do
|
|
30
|
+
@bank.add_rate("USD", "EUR", 0.5)
|
|
31
|
+
@bank.add_rate("EUR", "YEN", 10)
|
|
32
|
+
@bank.exchange(10_00, "USD", "EUR").should == 5_00
|
|
33
|
+
@bank.exchange(500_00, "EUR", "YEN").should == 5000_00
|
|
34
|
+
end
|
|
35
|
+
|
|
36
|
+
it "rounds the exchanged result down" do
|
|
37
|
+
@bank.add_rate("USD", "EUR", 0.788332676)
|
|
38
|
+
@bank.add_rate("EUR", "YEN", 122.631477)
|
|
39
|
+
@bank.exchange(10_00, "USD", "EUR").should == 788
|
|
40
|
+
@bank.exchange(500_00, "EUR", "YEN").should == 6131573
|
|
41
|
+
end
|
|
42
|
+
|
|
43
|
+
it "raises Money::UnknownRate upon conversion if the conversion rate is unknown" do
|
|
44
|
+
block = lambda { @bank.exchange(10, "USD", "EUR") }
|
|
45
|
+
block.should raise_error(Money::UnknownRate)
|
|
46
|
+
end
|
|
47
|
+
|
|
45
48
|
end
|
data/test/money_spec.rb
CHANGED
|
@@ -1,124 +1,127 @@
|
|
|
1
1
|
$LOAD_PATH << File.expand_path(File.dirname(__FILE__) + "/../lib")
|
|
2
2
|
require 'money/money'
|
|
3
|
+
require 'money/currencies'
|
|
3
4
|
|
|
4
5
|
describe Money do
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
6
|
+
|
|
7
|
+
it "is associated to the singleton instance of VariableExchangeBank by default" do
|
|
8
|
+
Money.new(0).bank.object_id.should == Money::VariableExchangeBank.instance.object_id
|
|
9
|
+
end
|
|
10
|
+
|
|
11
|
+
specify "#cents returns the amount of cents passed to the constructor" do
|
|
12
|
+
Money.new(200_00, "USD").cents.should == 200_00
|
|
13
|
+
end
|
|
14
|
+
|
|
15
|
+
it "rounds the given cents to an integer" do
|
|
16
|
+
Money.new(1.00, "USD").cents.should == 1
|
|
17
|
+
Money.new(1.01, "USD").cents.should == 1
|
|
18
|
+
Money.new(1.50, "USD").cents.should == 2
|
|
19
|
+
end
|
|
20
|
+
|
|
21
|
+
specify "#currency returns the currency passed to the constructor" do
|
|
22
|
+
Money.new(200_00, "USD").currency.should == "USD"
|
|
23
|
+
end
|
|
24
|
+
|
|
25
|
+
specify "#zero? returns whether the amount is 0" do
|
|
26
|
+
Money.new(0, "USD").should be_zero
|
|
27
|
+
Money.new(0, "EUR").should be_zero
|
|
28
|
+
Money.new(1, "USD").should_not be_zero
|
|
29
|
+
Money.new(10, "YEN").should_not be_zero
|
|
30
|
+
Money.new(-1, "EUR").should_not be_zero
|
|
31
|
+
end
|
|
32
|
+
|
|
33
|
+
specify "#exchange_to exchanges the amount via its exchange bank" do
|
|
34
|
+
money = Money.new(100_00, "USD")
|
|
35
|
+
money.bank.should_receive(:exchange).with(100000_00, "USD", "EUR").and_return(200_00)
|
|
36
|
+
money.exchange_to("EUR")
|
|
37
|
+
end
|
|
38
|
+
|
|
39
|
+
specify "#exchange_to exchanges the amount properly" do
|
|
40
|
+
money = Money.new(100_00, "USD")
|
|
41
|
+
money.bank.should_receive(:exchange).with(100000_00, "USD", "EUR").and_return(200000_00)
|
|
42
|
+
money.exchange_to("EUR").should == Money.new(200_00, "EUR")
|
|
43
|
+
end
|
|
44
|
+
|
|
45
|
+
specify "#== returns true if and only if their amount and currency are equal" do
|
|
46
|
+
Money.new(1_00, "USD").should == Money.new(1_00, "USD")
|
|
47
|
+
Money.new(1_00, "USD").should_not == Money.new(1_00, "EUR")
|
|
48
|
+
Money.new(1_00, "USD").should_not == Money.new(2_00, "USD")
|
|
49
|
+
Money.new(1_00, "USD").should_not == Money.new(99_00, "EUR")
|
|
50
|
+
end
|
|
51
|
+
|
|
52
|
+
specify "#* multiplies the money's amount by the multiplier while retaining the currency" do
|
|
53
|
+
(Money.new(1_00, "USD") * 10).should == Money.new(10_00, "USD")
|
|
54
|
+
end
|
|
55
|
+
|
|
56
|
+
specify "#* divides the money's amount by the divisor while retaining the currency" do
|
|
57
|
+
(Money.new(10_00, "USD") / 10).should == Money.new(1_00, "USD")
|
|
58
|
+
end
|
|
59
|
+
|
|
60
|
+
specify "Money.empty creates a new Money object of 0 cents" do
|
|
61
|
+
Money.empty.should == Money.new(0)
|
|
62
|
+
end
|
|
63
|
+
|
|
64
|
+
specify "Money.ca_dollar creates a new Money object of the given value in CAD" do
|
|
65
|
+
Money.ca_dollar(50).should == Money.new(50, "CAD")
|
|
66
|
+
end
|
|
67
|
+
|
|
68
|
+
specify "Money.ca_dollar creates a new Money object of the given value in USD" do
|
|
69
|
+
Money.us_dollar(50).should == Money.new(50, "USD")
|
|
70
|
+
end
|
|
71
|
+
|
|
72
|
+
specify "Money.ca_dollar creates a new Money object of the given value in EUR" do
|
|
73
|
+
Money.euro(50).should == Money.new(50, "EUR")
|
|
74
|
+
end
|
|
75
|
+
|
|
76
|
+
specify "Money.add_rate works" do
|
|
77
|
+
Money.add_rate("EUR", "USD", 10)
|
|
78
|
+
Money.new(10_00, "EUR").exchange_to("USD").should == Money.new(100_00, "USD")
|
|
79
|
+
end
|
|
78
80
|
end
|
|
79
81
|
|
|
80
82
|
describe "Actions involving two Money objects" do
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
83
|
+
describe "if the other Money object has the same currency" do
|
|
84
|
+
specify "#<=> compares the two objects' amounts" do
|
|
85
|
+
(Money.new(1_00, "USD") <=> Money.new(1_00, "USD")).should == 0
|
|
86
|
+
(Money.new(1_00, "USD") <=> Money.new(99, "USD")).should > 0
|
|
87
|
+
(Money.new(1_00, "USD") <=> Money.new(2_00, "USD")).should < 0
|
|
88
|
+
end
|
|
89
|
+
|
|
90
|
+
specify "#+ adds the other object's amount to the current object's amount while retaining the currency" do
|
|
91
|
+
(Money.new(10_00, "USD") + Money.new(90, "USD")).should == Money.new(10_90, "USD")
|
|
92
|
+
end
|
|
93
|
+
|
|
94
|
+
specify "#- substracts the other object's amount from the current object's amount while retaining the currency" do
|
|
95
|
+
(Money.new(10_00, "USD") - Money.new(90, "USD")).should == Money.new(9_10, "USD")
|
|
96
|
+
end
|
|
97
|
+
end
|
|
98
|
+
|
|
99
|
+
describe "if the other Money object has a different currency" do
|
|
100
|
+
specify "#<=> compares the two objects' amount after converting the other object's amount to its own currency" do
|
|
101
|
+
target = Money.new(200_00, "EUR")
|
|
102
|
+
target.should_receive(:exchange_to).with("USD").and_return(Money.new(300_00, "USD"))
|
|
103
|
+
(Money.new(100_00, "USD") <=> target).should < 0
|
|
104
|
+
|
|
105
|
+
target = Money.new(200_00, "EUR")
|
|
106
|
+
target.should_receive(:exchange_to).with("USD").and_return(Money.new(100_00, "USD"))
|
|
107
|
+
(Money.new(100_00, "USD") <=> target).should == 0
|
|
108
|
+
|
|
109
|
+
target = Money.new(200_00, "EUR")
|
|
110
|
+
target.should_receive(:exchange_to).with("USD").and_return(Money.new(99_00, "USD"))
|
|
111
|
+
(Money.new(100_00, "USD") <=> target).should > 0
|
|
112
|
+
end
|
|
113
|
+
|
|
114
|
+
specify "#+ adds the other object's amount, converted to this object's currency, to this object's amount while retaining its currency" do
|
|
115
|
+
other = Money.new(90, "EUR")
|
|
116
|
+
other.should_receive(:exchange_to).with("USD").and_return(Money.new(9_00, "USD"))
|
|
117
|
+
(Money.new(10_00, "USD") + other).should == Money.new(19_00, "USD")
|
|
118
|
+
end
|
|
119
|
+
|
|
120
|
+
specify "#- substracts the other object's amount, converted to this object's currency, from this object's amount while retaining its currency" do
|
|
121
|
+
other = Money.new(90, "EUR")
|
|
122
|
+
other.should_receive(:exchange_to).with("USD").and_return(Money.new(9_00, "USD"))
|
|
123
|
+
(Money.new(10_00, "USD") - other).should == Money.new(1_00, "USD")
|
|
124
|
+
end
|
|
125
|
+
end
|
|
126
|
+
|
|
124
127
|
end
|
metadata
CHANGED
|
@@ -1,11 +1,12 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: sevenwire-money
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 2.
|
|
4
|
+
version: 2.1.0
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Tobias Luetke
|
|
8
8
|
- Hongli Lai
|
|
9
|
+
- Brandon Arbini
|
|
9
10
|
autorequire:
|
|
10
11
|
bindir: bin
|
|
11
12
|
cert_chain: []
|
|
@@ -14,8 +15,8 @@ date: 2008-12-22 00:00:00 -08:00
|
|
|
14
15
|
default_executable:
|
|
15
16
|
dependencies: []
|
|
16
17
|
|
|
17
|
-
description: Money and currency exchange support library.
|
|
18
|
-
email:
|
|
18
|
+
description: Money and currency exchange support library.
|
|
19
|
+
email: brandon@sevenwire.com
|
|
19
20
|
executables: []
|
|
20
21
|
|
|
21
22
|
extensions: []
|
|
@@ -36,7 +37,7 @@ files:
|
|
|
36
37
|
- test/exchange_bank_spec.rb
|
|
37
38
|
- test/money_spec.rb
|
|
38
39
|
has_rdoc: true
|
|
39
|
-
homepage: http://
|
|
40
|
+
homepage: http://github.com/sevenwire/money
|
|
40
41
|
post_install_message:
|
|
41
42
|
rdoc_options: []
|
|
42
43
|
|