nofxx-money 2.2.2 → 2.3.1
Sign up to get free protection for your applications and to get access to all the features.
- data/Manifest.txt +2 -1
- data/README.rdoc +11 -10
- data/Rakefile +2 -2
- data/lib/money.rb +1 -1
- data/lib/money/acts_as_money.rb +10 -9
- data/lib/money/core_extensions.rb +118 -18
- data/lib/money/money.rb +71 -40
- data/money.gemspec +5 -5
- data/spec/db/database.yml +1 -1
- data/spec/db/schema.rb +2 -4
- data/spec/money/acts_as_money_spec.rb +35 -17
- data/spec/money/core_extensions_spec.rb +5 -0
- data/spec/money/money_spec.rb +80 -44
- data/spec/spec.opts +4 -0
- data/spec/spec_helper.rb +1 -3
- metadata +8 -5
data/Manifest.txt
CHANGED
@@ -14,11 +14,12 @@ rails/init.rb
|
|
14
14
|
script/console
|
15
15
|
script/destroy
|
16
16
|
script/generate
|
17
|
-
spec/db/acts_as_money.sqlite3
|
18
17
|
spec/db/database.yml
|
19
18
|
spec/db/schema.rb
|
20
19
|
spec/money/acts_as_money_spec.rb
|
21
20
|
spec/money/core_extensions_spec.rb
|
22
21
|
spec/money/exchange_bank_spec.rb
|
23
22
|
spec/money/money_spec.rb
|
23
|
+
spec/spec.opts
|
24
24
|
spec/spec_helper.rb
|
25
|
+
tmp/acts_as_money.sqlite3
|
data/README.rdoc
CHANGED
@@ -17,6 +17,7 @@ Resources:
|
|
17
17
|
- RDoc API: http://money.rubyforge.org
|
18
18
|
- Git repository: http://github.com/FooBarWidget/money/tree/master
|
19
19
|
|
20
|
+
|
20
21
|
== Download
|
21
22
|
|
22
23
|
Install stable releases with the following command:
|
@@ -28,6 +29,7 @@ The development version (hosted on Github) can be installed with:
|
|
28
29
|
gem sources -a http://gems.github.com
|
29
30
|
gem install nofxx-money
|
30
31
|
|
32
|
+
|
31
33
|
== Usage
|
32
34
|
|
33
35
|
=== Synopsis
|
@@ -38,6 +40,7 @@ The development version (hosted on Github) can be installed with:
|
|
38
40
|
money = Money.new(1000, "USD")
|
39
41
|
money.cents # => 1000
|
40
42
|
money.currency # => "USD"
|
43
|
+
money.format # => "$10.00"
|
41
44
|
|
42
45
|
Money.new(1000, "USD") == Money.new(1000, "USD") # => true
|
43
46
|
Money.new(1000, "USD") == Money.new(100, "USD") # => false
|
@@ -56,6 +59,11 @@ an example of how it works:
|
|
56
59
|
Money.us_dollar(100).exchange_to("CAD") # => Money.new(124, "CAD")
|
57
60
|
Money.ca_dollar(100).exchange_to("USD") # => Money.new(80, "USD")
|
58
61
|
|
62
|
+
or
|
63
|
+
|
64
|
+
Money.us_dollar(100).to_cad # => Money.new(124, "CAD")
|
65
|
+
Money.ca_dollar(100).to_usd # => Money.new(80, "USD")
|
66
|
+
|
59
67
|
Comparison and arithmetic operations work as expected:
|
60
68
|
|
61
69
|
Money.new(1000, "USD") <=> Money.new(900, "USD") # => 1; 9.00 USD is smaller
|
@@ -86,15 +94,7 @@ app/models/product.rb
|
|
86
94
|
belongs_to :product
|
87
95
|
has_money :price
|
88
96
|
|
89
|
-
|
90
|
-
validate :cents_not_zero
|
91
|
-
|
92
|
-
def cents_not_zero
|
93
|
-
errors.add("price_cents", "cannot be zero or less") unless cents > 0
|
94
|
-
end
|
95
|
-
|
96
|
-
validates_presence_of :sku, :currency
|
97
|
-
validates_uniqueness_of :sku
|
97
|
+
validates_numericality_of :price_cents, :greater_than => 0
|
98
98
|
end
|
99
99
|
|
100
100
|
migration:
|
@@ -116,6 +116,7 @@ If you use Rails, then environment.rb is a very good place to put this.
|
|
116
116
|
|
117
117
|
== TODO
|
118
118
|
|
119
|
-
* Better validation
|
119
|
+
* Better validation (fix composed_of allow_nil)
|
120
120
|
* Interest (almost there..)
|
121
|
+
* Remote rate fetching
|
121
122
|
|
data/Rakefile
CHANGED
@@ -4,7 +4,7 @@ require File.dirname(__FILE__) + '/lib/money'
|
|
4
4
|
# Generate all the Rake tasks
|
5
5
|
# Run 'rake -T' to see list of generated tasks (from gem root directory)
|
6
6
|
$hoe = Hoe.new('money', Money::VERSION) do |p|
|
7
|
-
p.developer('
|
7
|
+
p.developer('Money Team', 'see@readme')
|
8
8
|
p.changes = p.paragraphs_of("History.txt", 0..1).join("\n\n")
|
9
9
|
p.rubyforge_name = p.name
|
10
10
|
p.summary = "This library aids one in handling money and different currencies."
|
@@ -16,7 +16,7 @@ $hoe = Hoe.new('money', Money::VERSION) do |p|
|
|
16
16
|
['newgem', ">= #{::Newgem::VERSION}"]
|
17
17
|
]
|
18
18
|
|
19
|
-
p.clean_globs |= %w[**/.DS_Store
|
19
|
+
p.clean_globs |= %w[**/.DS_Store *.log]
|
20
20
|
path = (p.rubyforge_name == p.name) ? p.rubyforge_name : "\#{p.rubyforge_name}/\#{p.name}"
|
21
21
|
p.remote_rdoc_dir = File.join(path.gsub(/^#{p.rubyforge_name}\/?/,''), 'rdoc')
|
22
22
|
p.rsync_args = '-av --delete --ignore-errors'
|
data/lib/money.rb
CHANGED
data/lib/money/acts_as_money.rb
CHANGED
@@ -12,25 +12,26 @@ module ActsAsMoney #:nodoc:
|
|
12
12
|
# end
|
13
13
|
#
|
14
14
|
# @product.value.class #=> "Money"
|
15
|
-
# @product.
|
15
|
+
# @product.value_cents #=> "1000"
|
16
16
|
# @product.tax_currency #=> "USD"
|
17
17
|
#
|
18
18
|
# Opts:
|
19
19
|
# :cents => "pennys" #=> @product.pennys
|
20
20
|
# :currency => "currency" #=> @product.currency
|
21
21
|
# :allow_nil => true
|
22
|
-
# :with_currency =>
|
22
|
+
# :with_currency => false
|
23
|
+
# :with_cents => true #=> 1000.to_money #=> #<Money @cents=1000>
|
23
24
|
#
|
24
25
|
def has_money(*attributes)
|
25
|
-
config = {:with_currency => true, :
|
26
|
-
:allow_nil =>
|
26
|
+
config = {:with_currency => true, :with_cents => false,
|
27
|
+
:allow_nil => true }.update(attributes.extract_options!)
|
27
28
|
|
28
|
-
attributes
|
29
|
-
mapping = [[config[:cents] || "#{
|
30
|
-
mapping << [config[:currency] || "#{
|
29
|
+
for attribute in attributes do
|
30
|
+
mapping = [[config[:cents] || "#{attribute}_cents", 'cents']]
|
31
|
+
mapping << [config[:currency] || "#{attribute}_currency", 'currency'] if config[:with_currency]
|
31
32
|
|
32
|
-
composed_of
|
33
|
-
:mapping => mapping, :converter => config[:
|
33
|
+
composed_of attribute, :class_name => 'Money', :allow_nil => config[:allow_nil],
|
34
|
+
:mapping => mapping, :converter => lambda { |m| (m||0).to_money(config[:with_cents]) }
|
34
35
|
end
|
35
36
|
|
36
37
|
end
|
@@ -1,11 +1,20 @@
|
|
1
1
|
class Numeric
|
2
2
|
# Converts this numeric to a Money object in the default currency. It
|
3
|
-
# multiplies the numeric value by 100 and treats that as cents.
|
3
|
+
# multiplies the numeric value by 100 and treats that as cents if receive false.
|
4
4
|
#
|
5
|
-
# 100.to_money => #<Money @cents=
|
5
|
+
# 100.to_money => #<Money @cents=100>
|
6
6
|
# 100.37.to_money => #<Money @cents=10037>
|
7
|
-
|
8
|
-
|
7
|
+
# 100.to_money(false) => #<Money @cents=10000>
|
8
|
+
def to_money(cents = false)
|
9
|
+
if cents
|
10
|
+
if self.is_a? Integer
|
11
|
+
Money.new(self)
|
12
|
+
else
|
13
|
+
Money.new(self.to_s.gsub(/\./,'').to_i)
|
14
|
+
end
|
15
|
+
else
|
16
|
+
Money.new(self * 100)
|
17
|
+
end
|
9
18
|
end
|
10
19
|
end
|
11
20
|
|
@@ -13,27 +22,118 @@ class String
|
|
13
22
|
# Parses the current string and converts it to a Money object.
|
14
23
|
# Excess characters will be discarded.
|
15
24
|
#
|
16
|
-
# '100'.to_money # => #<Money @cents=
|
25
|
+
# '100'.to_money # => #<Money @cents=100>
|
17
26
|
# '100.37'.to_money # => #<Money @cents=10037>
|
18
|
-
# '100 USD'.to_money # => #<Money @cents=
|
19
|
-
# 'USD 100'.to_money # => #<Money @cents=
|
20
|
-
# '$100 USD'.to_money # => #<Money @cents=
|
21
|
-
|
27
|
+
# '100 USD'.to_money # => #<Money @cents=100, @currency="USD">
|
28
|
+
# 'USD 100'.to_money # => #<Money @cents=100, @currency="USD">
|
29
|
+
# '$100 USD'.to_money # => #<Money @cents=100, @currency="USD">
|
30
|
+
# '$100 USD'.to_money(false) # => #<Money @cents=10000, @currency="USD">
|
31
|
+
def to_money(with_cents = false)
|
22
32
|
# Get the currency.
|
23
33
|
matches = scan /([A-Z]{2,3})/
|
24
34
|
currency = matches[0] ? matches[0][0] : Money.default_currency
|
35
|
+
cents = calculate_cents(self, with_cents)
|
36
|
+
Money.new(cents, currency)
|
37
|
+
end
|
38
|
+
|
39
|
+
private
|
40
|
+
|
41
|
+
def calculate_cents(number, with_cents = true)
|
42
|
+
# remove anything that's not a number, potential delimiter, or minus sign
|
43
|
+
num = number.gsub(/[^\d|\.|,|\'|\s|\-]/, '').strip
|
44
|
+
|
45
|
+
# set a boolean flag for if the number is negative or not
|
46
|
+
negative = num.split(//).first == "-"
|
47
|
+
|
48
|
+
# if negative, remove the minus sign from the number
|
49
|
+
num = num.gsub(/^-/, '') if negative
|
50
|
+
|
51
|
+
# gather all separators within the result number
|
52
|
+
used_separators = num.scan /[^\d]/
|
53
|
+
|
54
|
+
# determine the number of unique separators within the number
|
55
|
+
#
|
56
|
+
# e.g.
|
57
|
+
# $1,234,567.89 would return 2 (, and .)
|
58
|
+
# $125,00 would return 1
|
59
|
+
# $199 would return 0
|
60
|
+
# $1 234,567.89 would raise an error (separators are space, comma, and period)
|
61
|
+
case used_separators.uniq.length
|
62
|
+
# no separator or delimiter; major (dollars) is the number, and minor (cents) is 0
|
63
|
+
when 0 then major, minor = num, 0
|
64
|
+
|
65
|
+
# two separators, so we know the last item in this array is the
|
66
|
+
# major/minor delimiter and the rest are separators
|
67
|
+
when 2
|
68
|
+
separator, delimiter = used_separators.uniq
|
69
|
+
# remove all separators, split on the delimiter
|
70
|
+
major, minor = num.gsub(separator, '').split(delimiter)
|
71
|
+
min = 0 unless min
|
72
|
+
when 1
|
73
|
+
# we can't determine if the comma or period is supposed to be a separator or a delimiter
|
74
|
+
# e.g.
|
75
|
+
# 1,00 - comma is a delimiter
|
76
|
+
# 1.000 - period is a delimiter
|
77
|
+
# 1,000 - comma is a separator
|
78
|
+
# 1,000,000 - comma is a separator
|
79
|
+
# 10000,00 - comma is a delimiter
|
80
|
+
# 1000,000 - comma is a delimiter
|
25
81
|
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
|
82
|
+
# assign first separator for reusability
|
83
|
+
separator = used_separators.first
|
84
|
+
|
85
|
+
# separator is used as a separator when there are multiple instances, always
|
86
|
+
if num.scan(separator).length > 1 # multiple matches; treat as separator
|
87
|
+
major, minor = num.gsub(separator, ''), 0
|
88
|
+
else
|
89
|
+
# ex: 1,000 - 1.0000 - 10001.000
|
90
|
+
# split number into possible major (dollars) and minor (cents) values
|
91
|
+
possible_major, possible_minor = num.split(separator)
|
92
|
+
|
93
|
+
# if the minor (cents) length isn't 3, assign major/minor from the possibles
|
94
|
+
# e.g.
|
95
|
+
# 1,00 => 1.00
|
96
|
+
# 1.0000 => 1.00
|
97
|
+
# 1.2 => 1.20
|
98
|
+
if possible_minor.length != 3 # delimiter
|
99
|
+
major, minor = possible_major, possible_minor
|
100
|
+
else
|
101
|
+
# minor length is three
|
102
|
+
# let's try to figure out intent of the delimiter
|
103
|
+
|
104
|
+
# the major length is greater than three, which means
|
105
|
+
# the comma or period is used as a delimiter
|
106
|
+
# e.g.
|
107
|
+
# 1000,000
|
108
|
+
# 100000,000
|
109
|
+
if possible_major.length > 3
|
110
|
+
major, minor = possible_major, possible_minor
|
111
|
+
else
|
112
|
+
# number is in format ###{sep}### or ##{sep}### or #{sep}###
|
113
|
+
# handle as , is sep, . is delimiter
|
114
|
+
if separator == '.'
|
115
|
+
major, minor = possible_major, possible_minor
|
32
116
|
else
|
33
|
-
0
|
117
|
+
major, minor = "#{possible_major}#{possible_minor}", 0
|
34
118
|
end
|
119
|
+
end
|
120
|
+
end
|
121
|
+
end
|
122
|
+
else
|
123
|
+
raise ArgumentError, "Invalid currency amount"
|
124
|
+
end
|
35
125
|
|
36
|
-
|
126
|
+
# build the string based on major/minor since separator/delimiters have been removed
|
127
|
+
# transform to a float, multiply by 100 to convert to cents
|
128
|
+
if with_cents and minor == 0
|
129
|
+
cents = "#{major}.#{minor}".to_f
|
130
|
+
else
|
131
|
+
cents = "#{major}.#{minor}".to_f * 100
|
132
|
+
end
|
133
|
+
|
134
|
+
|
135
|
+
# if negative, multiply by -1; otherwise, return positive cents
|
136
|
+
negative ? cents * -1 : cents
|
37
137
|
end
|
38
|
-
end
|
39
138
|
|
139
|
+
end
|
data/lib/money/money.rb
CHANGED
@@ -28,6 +28,16 @@ class Money
|
|
28
28
|
attr_accessor :default_currency
|
29
29
|
end
|
30
30
|
|
31
|
+
CURRENCIES = {
|
32
|
+
"USD" => { :delimiter => ",", :separator => ".", :symbol => "$" },
|
33
|
+
"CAD" => { :delimiter => ",", :separator => ".", :symbol => "$" },
|
34
|
+
"HKD" => { :delimiter => ",", :separator => ".", :symbol => "$" },
|
35
|
+
"SGD" => { :delimiter => ",", :separator => ".", :symbol => "$" },
|
36
|
+
"BRL" => { :delimiter => ".", :separator => ",", :symbol => "R$" },
|
37
|
+
"EUR" => { :delimiter => ",", :separator => ".", :symbol => '€', :html => '€' },
|
38
|
+
"GBP" => { :delimiter => ",", :separator => ".", :symbol => '£', :html => '£' },
|
39
|
+
"JPY" => { :delimiter => ".", :separator => ".", :symbol => '¥', :html => '¥' },
|
40
|
+
}
|
31
41
|
self.default_bank = VariableExchangeBank.instance
|
32
42
|
self.default_currency = "USD"
|
33
43
|
|
@@ -52,7 +62,7 @@ class Money
|
|
52
62
|
Money.new(cents, "EUR")
|
53
63
|
end
|
54
64
|
|
55
|
-
# Creates a new Money object of the given value, using the
|
65
|
+
# Creates a new Money object of the given value, using the Brazilian Real currency.
|
56
66
|
def self.real(cents)
|
57
67
|
Money.new(cents, "BRL")
|
58
68
|
end
|
@@ -67,7 +77,7 @@ class Money
|
|
67
77
|
# Alternativly you can use the convinience methods like
|
68
78
|
# Money.ca_dollar and Money.us_dollar
|
69
79
|
def initialize(cents, currency = Money.default_currency, bank = Money.default_bank)
|
70
|
-
@cents = cents.round
|
80
|
+
@cents = cents.round
|
71
81
|
@currency = currency
|
72
82
|
@bank = bank
|
73
83
|
end
|
@@ -129,7 +139,6 @@ class Money
|
|
129
139
|
# Calculates compound interest
|
130
140
|
# Returns a money object with the sum of self + it
|
131
141
|
def compound_interest(rate,count=1)
|
132
|
-
|
133
142
|
Money.new(cents * ((1 + rate / 100.0 / 12) ** count - 1))
|
134
143
|
end
|
135
144
|
|
@@ -138,11 +147,11 @@ class Money
|
|
138
147
|
Money.new(rate/100/12*cents*count)
|
139
148
|
end
|
140
149
|
|
141
|
-
|
142
|
-
|
143
|
-
|
144
|
-
#
|
145
|
-
#
|
150
|
+
# Split money in number of installments
|
151
|
+
#
|
152
|
+
# Money.new(10_00).split_in_installments(3)
|
153
|
+
# => [ 3.34, 3.33, 3.33 ] (All Money instances)
|
154
|
+
#
|
146
155
|
def split_in_installments(fixnum,extra=nil,*opts)
|
147
156
|
wallet = Wallet.new(fixnum, Money.new(cents/fixnum,currency))
|
148
157
|
to_add = cents % fixnum
|
@@ -151,13 +160,17 @@ class Money
|
|
151
160
|
end
|
152
161
|
|
153
162
|
# Split money in installments based on payment value
|
163
|
+
#
|
164
|
+
# Money.new(1000_00).split_in_installments(Money.new(300_00))
|
165
|
+
# => [ 334_00, 333_00, 333_00 ] (All Money instances)
|
166
|
+
#
|
154
167
|
def in_installments_of(other_money,first=false)
|
155
168
|
split_in_installments(cents/other_money.cents,first)
|
156
169
|
end
|
157
170
|
|
158
171
|
# Just a helper if you got tax inputs in percentage.
|
159
|
-
# Ie.
|
160
|
-
def
|
172
|
+
# Ie. add_tax(20) => cents * 1.20
|
173
|
+
def add_tax(tax)
|
161
174
|
Money.new(cents + cents / 100 * tax)
|
162
175
|
end
|
163
176
|
|
@@ -186,15 +199,35 @@ class Money
|
|
186
199
|
# html:
|
187
200
|
#
|
188
201
|
# Money.ca_dollar(570).format(:html => true, :with_currency => true) => "$5.70 <span class=\"currency\">CAD</span>"
|
189
|
-
|
190
|
-
|
202
|
+
def format(*rules)
|
203
|
+
# support for old format parameters
|
204
|
+
rules = normalize_formatting_rules(rules)
|
205
|
+
|
206
|
+
if cents == 0
|
207
|
+
if rules[:display_free].respond_to?(:to_str)
|
208
|
+
return rules[:display_free]
|
209
|
+
elsif rules[:display_free]
|
210
|
+
return "free"
|
211
|
+
end
|
212
|
+
end
|
191
213
|
|
192
|
-
|
214
|
+
if rules.has_key?(:symbol)
|
215
|
+
if rules[:symbol]
|
216
|
+
symbol = rules[:symbol]
|
217
|
+
else
|
218
|
+
symbol = ""
|
219
|
+
end
|
220
|
+
else
|
221
|
+
symbol = CURRENCIES[currency][:symbol]
|
222
|
+
end
|
193
223
|
|
194
|
-
if rules
|
195
|
-
formatted = sprintf("
|
224
|
+
if rules[:no_cents]
|
225
|
+
formatted = sprintf("#{symbol}%d", cents.to_f / 100)
|
226
|
+
formatted.gsub!(/(\d)(?=(\d\d\d)+(?!\d))/, "\\1#{CURRENCIES[currency][:delimiter]}")
|
196
227
|
else
|
197
|
-
formatted = sprintf("
|
228
|
+
formatted = sprintf("#{symbol}%.2f", cents.to_f / 100).split('.')
|
229
|
+
formatted[0].gsub!(/(\d)(?=(\d\d\d)+(?!\d))/, "\\1#{CURRENCIES[currency][:delimiter]}")
|
230
|
+
formatted = formatted.join(CURRENCIES[currency][:separator])
|
198
231
|
end
|
199
232
|
|
200
233
|
if rules[:with_currency]
|
@@ -206,6 +239,20 @@ class Money
|
|
206
239
|
formatted
|
207
240
|
end
|
208
241
|
|
242
|
+
def normalize_formatting_rules(rules)
|
243
|
+
if rules.size == 1
|
244
|
+
rules = rules.pop
|
245
|
+
rules = { rules => true } if rules.is_a?(Symbol)
|
246
|
+
else
|
247
|
+
rules = rules.inject({}) do |h,s|
|
248
|
+
h[s] = true
|
249
|
+
h
|
250
|
+
end
|
251
|
+
end
|
252
|
+
rules
|
253
|
+
end
|
254
|
+
|
255
|
+
|
209
256
|
# Money.ca_dollar(100).to_s => "1.00"
|
210
257
|
def to_s
|
211
258
|
sprintf("%.2f", cents / 100.0)
|
@@ -221,34 +268,18 @@ class Money
|
|
221
268
|
Money.new(@bank.exchange(self.cents, currency, other_currency), other_currency)
|
222
269
|
end
|
223
270
|
|
224
|
-
# Recieve a money object with the same amount as the current Money object
|
225
|
-
# in american dollar
|
226
|
-
def as_us_dollar
|
227
|
-
exchange_to("USD")
|
228
|
-
end
|
229
|
-
|
230
|
-
# Recieve a money object with the same amount as the current Money object
|
231
|
-
# in canadian dollar
|
232
|
-
def as_ca_dollar
|
233
|
-
exchange_to("CAD")
|
234
|
-
end
|
235
|
-
|
236
|
-
# Recieve a money object with the same amount as the current Money object
|
237
|
-
# in euro
|
238
|
-
def as_euro
|
239
|
-
exchange_to("EUR")
|
240
|
-
end
|
241
|
-
|
242
|
-
# Recieve a money object with the same amount as the current Money object
|
243
|
-
# in real
|
244
|
-
def as_real
|
245
|
-
exchange_to("BRL")
|
246
|
-
end
|
247
|
-
|
248
271
|
# Conversation to self
|
249
272
|
def to_money
|
250
273
|
self
|
251
274
|
end
|
275
|
+
|
276
|
+
def method_missing(m,*x)
|
277
|
+
if m.to_s =~ /^as/
|
278
|
+
exchange_to(m.to_s.split("_").last.upcase)
|
279
|
+
else
|
280
|
+
super
|
281
|
+
end
|
282
|
+
end
|
252
283
|
end
|
253
284
|
|
254
285
|
#
|
data/money.gemspec
CHANGED
@@ -2,15 +2,15 @@
|
|
2
2
|
|
3
3
|
Gem::Specification.new do |s|
|
4
4
|
s.name = %q{money}
|
5
|
-
s.version = "2.
|
5
|
+
s.version = "2.3.1"
|
6
6
|
|
7
7
|
s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version=
|
8
|
-
s.authors = ["
|
9
|
-
s.date = %q{2009-
|
8
|
+
s.authors = ["Money Team"]
|
9
|
+
s.date = %q{2009-03-15}
|
10
10
|
s.description = %q{This library aids one in handling money and different currencies.}
|
11
|
-
s.email = ["
|
11
|
+
s.email = ["see@readme"]
|
12
12
|
s.extra_rdoc_files = ["History.txt", "Manifest.txt", "README.rdoc"]
|
13
|
-
s.files = ["History.txt", "MIT-LICENSE", "Manifest.txt", "README.rdoc", "Rakefile", "lib/money.rb", "lib/money/acts_as_money.rb", "lib/money/core_extensions.rb", "lib/money/errors.rb", "lib/money/money.rb", "lib/money/variable_exchange_bank.rb", "money.gemspec", "rails/init.rb", "script/console", "script/destroy", "script/generate", "spec/db/
|
13
|
+
s.files = ["History.txt", "MIT-LICENSE", "Manifest.txt", "README.rdoc", "Rakefile", "lib/money.rb", "lib/money/acts_as_money.rb", "lib/money/core_extensions.rb", "lib/money/errors.rb", "lib/money/money.rb", "lib/money/variable_exchange_bank.rb", "money.gemspec", "rails/init.rb", "script/console", "script/destroy", "script/generate", "spec/db/database.yml", "spec/db/schema.rb", "spec/money/acts_as_money_spec.rb", "spec/money/core_extensions_spec.rb", "spec/money/exchange_bank_spec.rb", "spec/money/money_spec.rb", "spec/spec.opts", "spec/spec_helper.rb", "tmp/acts_as_money.sqlite3"]
|
14
14
|
s.has_rdoc = true
|
15
15
|
s.homepage = %q{http://github.com/nofxx/money}
|
16
16
|
s.rdoc_options = ["--main", "README.rdoc"]
|
data/spec/db/database.yml
CHANGED
data/spec/db/schema.rb
CHANGED
@@ -1,14 +1,12 @@
|
|
1
|
-
require File.dirname(__FILE__) + '/../spec_helper.rb'
|
2
|
-
|
3
1
|
ActiveRecord::Schema.define() do
|
4
2
|
|
5
3
|
create_table :accounts, :force => true do |t|
|
6
|
-
t.integer :
|
4
|
+
t.integer :value_cents, :total_cents
|
7
5
|
t.string :value_currency, :total_currency
|
8
6
|
end
|
9
7
|
|
10
8
|
create_table :products, :force => true do |t|
|
11
|
-
t.integer :
|
9
|
+
t.integer :value_cents, :tax_pennys
|
12
10
|
t.string :value_currency
|
13
11
|
end
|
14
12
|
|
@@ -2,43 +2,63 @@ require File.dirname(__FILE__) + '/../spec_helper.rb'
|
|
2
2
|
require File.dirname(__FILE__) + '/../../rails/init.rb'
|
3
3
|
|
4
4
|
# Uncomment this once to create the db
|
5
|
-
load_schema
|
5
|
+
# load_schema
|
6
6
|
|
7
7
|
class Account < ActiveRecord::Base
|
8
8
|
has_money :value, :total, :allow_nil => true
|
9
9
|
end
|
10
10
|
|
11
11
|
class Product < ActiveRecord::Base
|
12
|
-
has_money :value
|
12
|
+
has_money :value, :allow_nil => false
|
13
13
|
has_money :tax, :cents => "pennys", :with_currency => false
|
14
14
|
|
15
|
-
|
15
|
+
validates_numericality_of :value_cents, :greater_than => 0
|
16
16
|
end
|
17
17
|
|
18
18
|
describe "Acts as Money" do
|
19
19
|
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
20
|
+
it "should accept nil" do
|
21
|
+
@account = Account.create(:value => nil)
|
22
|
+
@account.should be_valid
|
23
|
+
@account.value.should be_nil
|
24
|
+
end
|
25
|
+
|
26
|
+
it "should require money" do
|
27
|
+
@product = Product.create(:value => nil)
|
28
|
+
@product.should have(1).errors
|
29
|
+
@product.value.should == Money.new(0)
|
30
|
+
end
|
31
|
+
|
32
|
+
it "should require money" do
|
33
|
+
@product_fake = Product.create(:value => nil)
|
34
|
+
@product_fake.value_cents.should eql(0)
|
35
|
+
end
|
36
|
+
|
37
|
+
it "should create" do
|
38
|
+
@account = Account.create!(:value => 10, :total => "20 BRL")
|
39
|
+
@account.should be_valid
|
40
|
+
end
|
41
|
+
|
42
|
+
it "should write to the db" do
|
43
|
+
lambda do
|
44
|
+
@account = Account.create!(:value => 10, :total => "20 BRL")
|
45
|
+
end.should change(Account, :count).by(1)
|
46
|
+
Account.last.total.format.should eql("R$20,00")
|
47
|
+
end
|
24
48
|
|
25
|
-
it "should require money" do
|
26
|
-
@product_fake = Product.create(:value => nil)
|
27
|
-
@product_fake.value_in_cents.should eql(0)
|
28
|
-
end
|
29
49
|
|
30
50
|
describe "Account" do
|
31
51
|
|
32
52
|
before(:each) do
|
33
|
-
@account = Account.create(:value => 10, :total => "20 BRL")
|
53
|
+
@account = Account.create!(:value => 10, :total => "20 BRL")
|
34
54
|
end
|
35
55
|
|
36
56
|
it "should return an instance of Money" do
|
37
57
|
@account.value.should be_instance_of(Money)
|
38
58
|
end
|
39
59
|
|
40
|
-
it "should
|
41
|
-
|
60
|
+
it "should format out nicely" do
|
61
|
+
@account.value.format.should eql("$10.00")
|
42
62
|
end
|
43
63
|
|
44
64
|
it "should include nicely" do
|
@@ -47,7 +67,7 @@ describe "Acts as Money" do
|
|
47
67
|
end
|
48
68
|
|
49
69
|
it "should map cents" do
|
50
|
-
@account.
|
70
|
+
@account.value_cents.to_s.should eql("1000")
|
51
71
|
end
|
52
72
|
|
53
73
|
it "should map currency" do
|
@@ -71,8 +91,6 @@ describe "Acts as Money" do
|
|
71
91
|
@product.should_not respond_to(:tax_currency)
|
72
92
|
end
|
73
93
|
|
74
|
-
|
75
|
-
|
76
94
|
end
|
77
95
|
|
78
96
|
end
|
@@ -20,6 +20,8 @@ describe "Money core extensions" do
|
|
20
20
|
it { "100.37".to_money.should == Money.new(100_37) }
|
21
21
|
it { "100,37".to_money.should == Money.new(100_37) }
|
22
22
|
it { "100 000".to_money.should == Money.new(100_000_00) }
|
23
|
+
it { "100.000,45".to_money.should == Money.new(100_000_45) }
|
24
|
+
it { "-100.100,45".to_money.should == Money.new(-100_100_45) }
|
23
25
|
|
24
26
|
it { "100 USD".to_money.should == Money.new(100_00, "USD") }
|
25
27
|
it { "-100 USD".to_money.should == Money.new(-100_00, "USD") }
|
@@ -34,6 +36,9 @@ describe "Money core extensions" do
|
|
34
36
|
it { "EUR 100,37".to_money.should == Money.new(100_37, "EUR") }
|
35
37
|
it { "EUR -100,37".to_money.should == Money.new(-100_37, "EUR") }
|
36
38
|
|
39
|
+
it { "BRL 100,37".to_money.should == Money.new(100_37, "BRL") }
|
40
|
+
it { "BRL -100,37".to_money.should == Money.new(-100_37, "BRL") }
|
41
|
+
|
37
42
|
it {"$100 USD".to_money.should == Money.new(100_00, "USD") }
|
38
43
|
end
|
39
44
|
end
|
data/spec/money/money_spec.rb
CHANGED
@@ -1,3 +1,4 @@
|
|
1
|
+
# -*- coding: utf-8 -*-
|
1
2
|
require File.dirname(__FILE__) + '/../spec_helper.rb'
|
2
3
|
|
3
4
|
describe Money do
|
@@ -75,8 +76,8 @@ describe Money do
|
|
75
76
|
end
|
76
77
|
|
77
78
|
it "should calculate tax" do
|
78
|
-
Money.new(100).
|
79
|
-
Money.new(100).
|
79
|
+
Money.new(100).add_tax(20).cents.should eql(120)
|
80
|
+
Money.new(100).add_tax(-20).cents.should eql(80)
|
80
81
|
end
|
81
82
|
|
82
83
|
it "should calculate compound tax" do
|
@@ -95,17 +96,13 @@ describe Money do
|
|
95
96
|
end
|
96
97
|
|
97
98
|
it "shuld sum array" do
|
98
|
-
@money = Money.new(10_00).
|
99
|
+
@money = Money.new(10_00).add_tax(10)
|
99
100
|
@money.split_in_installments(3).sum.cents.should eql(1100)
|
100
101
|
end
|
101
102
|
|
102
|
-
it
|
103
|
-
|
104
|
-
end
|
103
|
+
it { Money.new(10_00).to_f.should eql(10.0) }
|
104
|
+
it { Money.new(10_00).to_s.should eql("10.00") }
|
105
105
|
|
106
|
-
it "should output as string" do
|
107
|
-
Money.new(10_00).to_s.should eql("10.00")
|
108
|
-
end
|
109
106
|
|
110
107
|
it "should create a new Money object of 0 cents if empty" do
|
111
108
|
Money.empty.should == Money.new(0)
|
@@ -131,50 +128,89 @@ describe Money do
|
|
131
128
|
Money.add_rate("EUR", "USD", 10)
|
132
129
|
Money.new(10_00, "EUR").exchange_to("USD").should == Money.new(100_00, "USD")
|
133
130
|
end
|
134
|
-
end
|
135
|
-
|
136
|
-
describe "Actions involving two Money objects" do
|
137
|
-
describe "if the other Money object has the same currency" do
|
138
|
-
it "#<=> compares the two objects' amounts" do
|
139
|
-
(Money.new(1_00, "USD") <=> Money.new(1_00, "USD")).should == 0
|
140
|
-
(Money.new(1_00, "USD") <=> Money.new(99, "USD")).should > 0
|
141
|
-
(Money.new(1_00, "USD") <=> Money.new(2_00, "USD")).should < 0
|
142
|
-
end
|
143
|
-
|
144
|
-
it "#+ adds the other object's amount to the current object's amount while retaining the currency" do
|
145
|
-
(Money.new(10_00, "USD") + Money.new(90, "USD")).should == Money.new(10_90, "USD")
|
146
|
-
end
|
147
131
|
|
148
|
-
|
149
|
-
|
150
|
-
|
132
|
+
it "Money method missing exchange" do
|
133
|
+
Money.add_rate("EUR", "BRL", 10)
|
134
|
+
Money.new(10_00, "EUR").as_brl.should == Money.new(100_00, "BRL")
|
151
135
|
end
|
152
136
|
|
153
|
-
describe "
|
154
|
-
it "#<=> compares the two objects' amount after converting the other object's amount to its own currency" do
|
155
|
-
target = Money.new(200_00, "EUR")
|
156
|
-
target.should_receive(:exchange_to).with("USD").and_return(Money.new(300_00, "USD"))
|
157
|
-
(Money.new(100_00, "USD") <=> target).should < 0
|
137
|
+
describe "Format out " do
|
158
138
|
|
159
|
-
|
160
|
-
|
161
|
-
|
139
|
+
describe "Options" do
|
140
|
+
before(:each) do
|
141
|
+
@cash = Money.new(200)
|
142
|
+
end
|
162
143
|
|
163
|
-
|
164
|
-
|
165
|
-
|
144
|
+
it { @cash.format.should eql("$2.00") }
|
145
|
+
it { @cash.format(:symbol => "R$ ").should eql("R$ 2.00") }
|
146
|
+
it { @cash.format(:no_cents => true).should eql("$2") }
|
147
|
+
it { @cash.format(:no_cents => true, :symbol => "R$ ").should eql("R$ 2") }
|
166
148
|
end
|
167
149
|
|
168
|
-
it
|
169
|
-
|
170
|
-
|
171
|
-
|
150
|
+
it { Money.new(0).format.should eql("$0.00") }
|
151
|
+
it { Money.new(0).format(:display_free => true).should eql("free") }
|
152
|
+
it { Money.new(0).format(:display_free => "GRATIS").should eql("GRATIS") }
|
153
|
+
|
154
|
+
it { Money.new(9).format.should eql("$0.09") }
|
155
|
+
it { Money.new(99).format.should eql("$0.99") }
|
156
|
+
it { Money.new(800).format.should eql("$8.00") }
|
157
|
+
it { Money.new(-8000).format(:no_cents => true).should eql("$-80") }
|
158
|
+
it { Money.new(80000).format.should eql("$800.00") }
|
159
|
+
it { Money.new(800000).format.should eql("$8,000.00") }
|
160
|
+
it { Money.new(-8000000, "JPY").format(:no_cents => true).should eql("¥-80.000") }
|
161
|
+
it { Money.new(87654321, "BRL").format.should eql("R$876.543,21") }
|
162
|
+
it { Money.new(800000000, "BRL").format.should eql("R$8.000.000,00") }
|
163
|
+
it { Money.new(8000000000, "BRL").format.should eql("R$80.000.000,00") }
|
164
|
+
it { Money.new(80000000000, "CAD").format.should eql("$800,000,000.00") }
|
165
|
+
it { Money.new(880000000000, "GBP").format(:no_cents => true).should eql("£8,800,000,000") }
|
166
|
+
it { Money.new(8800000000088, "EUR").format.should eql("€88,000,000,000.88") }
|
167
|
+
|
168
|
+
end
|
169
|
+
|
170
|
+
|
171
|
+
describe "Actions involving two Money objects" do
|
172
|
+
describe "if the other Money object has the same currency" do
|
173
|
+
it "#<=> compares the two objects' amounts" do
|
174
|
+
(Money.new(1_00, "USD") <=> Money.new(1_00, "USD")).should == 0
|
175
|
+
(Money.new(1_00, "USD") <=> Money.new(99, "USD")).should > 0
|
176
|
+
(Money.new(1_00, "USD") <=> Money.new(2_00, "USD")).should < 0
|
177
|
+
end
|
178
|
+
|
179
|
+
it "#+ adds the other object's amount to the current object's amount while retaining the currency" do
|
180
|
+
(Money.new(10_00, "USD") + Money.new(90, "USD")).should == Money.new(10_90, "USD")
|
181
|
+
end
|
182
|
+
|
183
|
+
it "#- substracts the other object's amount from the current object's amount while retaining the currency" do
|
184
|
+
(Money.new(10_00, "USD") - Money.new(90, "USD")).should == Money.new(9_10, "USD")
|
185
|
+
end
|
172
186
|
end
|
173
187
|
|
174
|
-
|
175
|
-
other
|
176
|
-
|
177
|
-
|
188
|
+
describe "if the other Money object has a different currency" do
|
189
|
+
it "#<=> compares the two objects' amount after converting the other object's amount to its own currency" do
|
190
|
+
target = Money.new(200_00, "EUR")
|
191
|
+
target.should_receive(:exchange_to).with("USD").and_return(Money.new(300_00, "USD"))
|
192
|
+
(Money.new(100_00, "USD") <=> target).should < 0
|
193
|
+
|
194
|
+
target = Money.new(200_00, "EUR")
|
195
|
+
target.should_receive(:exchange_to).with("USD").and_return(Money.new(100_00, "USD"))
|
196
|
+
(Money.new(100_00, "USD") <=> target).should == 0
|
197
|
+
|
198
|
+
target = Money.new(200_00, "EUR")
|
199
|
+
target.should_receive(:exchange_to).with("USD").and_return(Money.new(99_00, "USD"))
|
200
|
+
(Money.new(100_00, "USD") <=> target).should > 0
|
201
|
+
end
|
202
|
+
|
203
|
+
it "#+ adds the other object's amount, converted to this object's currency, to this object's amount while retaining its currency" do
|
204
|
+
other = Money.new(90, "EUR")
|
205
|
+
other.should_receive(:exchange_to).with("USD").and_return(Money.new(9_00, "USD"))
|
206
|
+
(Money.new(10_00, "USD") + other).should == Money.new(19_00, "USD")
|
207
|
+
end
|
208
|
+
|
209
|
+
it "#- substracts the other object's amount, converted to this object's currency, from this object's amount while retaining its currency" do
|
210
|
+
other = Money.new(90, "EUR")
|
211
|
+
other.should_receive(:exchange_to).with("USD").and_return(Money.new(9_00, "USD"))
|
212
|
+
(Money.new(10_00, "USD") - other).should == Money.new(1_00, "USD")
|
213
|
+
end
|
178
214
|
end
|
179
215
|
end
|
180
216
|
end
|
data/spec/spec.opts
ADDED
data/spec/spec_helper.rb
CHANGED
@@ -11,12 +11,10 @@ $:.unshift(File.dirname(__FILE__) + '/../lib')
|
|
11
11
|
require 'money'
|
12
12
|
|
13
13
|
config = YAML.load_file(File.dirname(__FILE__) + '/db/database.yml')
|
14
|
-
ActiveRecord::Base.logger = Logger.new(
|
14
|
+
ActiveRecord::Base.logger = Logger.new("/tmp/money-debug.log")
|
15
15
|
ActiveRecord::Base.establish_connection(config)
|
16
16
|
|
17
17
|
|
18
18
|
def load_schema
|
19
19
|
load(File.dirname(__FILE__) + "/db/schema.rb")
|
20
20
|
end
|
21
|
-
|
22
|
-
|
metadata
CHANGED
@@ -1,19 +1,20 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: nofxx-money
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 2.
|
4
|
+
version: 2.3.1
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
|
-
-
|
7
|
+
- Money Team
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
11
|
|
12
|
-
date: 2009-
|
12
|
+
date: 2009-03-15 00:00:00 -07:00
|
13
13
|
default_executable:
|
14
14
|
dependencies:
|
15
15
|
- !ruby/object:Gem::Dependency
|
16
16
|
name: newgem
|
17
|
+
type: :development
|
17
18
|
version_requirement:
|
18
19
|
version_requirements: !ruby/object:Gem::Requirement
|
19
20
|
requirements:
|
@@ -23,6 +24,7 @@ dependencies:
|
|
23
24
|
version:
|
24
25
|
- !ruby/object:Gem::Dependency
|
25
26
|
name: hoe
|
27
|
+
type: :development
|
26
28
|
version_requirement:
|
27
29
|
version_requirements: !ruby/object:Gem::Requirement
|
28
30
|
requirements:
|
@@ -32,7 +34,7 @@ dependencies:
|
|
32
34
|
version:
|
33
35
|
description: This library aids one in handling money and different currencies.
|
34
36
|
email:
|
35
|
-
-
|
37
|
+
- see@readme
|
36
38
|
executables: []
|
37
39
|
|
38
40
|
extensions: []
|
@@ -58,14 +60,15 @@ files:
|
|
58
60
|
- script/console
|
59
61
|
- script/destroy
|
60
62
|
- script/generate
|
61
|
-
- spec/db/acts_as_money.sqlite3
|
62
63
|
- spec/db/database.yml
|
63
64
|
- spec/db/schema.rb
|
64
65
|
- spec/money/acts_as_money_spec.rb
|
65
66
|
- spec/money/core_extensions_spec.rb
|
66
67
|
- spec/money/exchange_bank_spec.rb
|
67
68
|
- spec/money/money_spec.rb
|
69
|
+
- spec/spec.opts
|
68
70
|
- spec/spec_helper.rb
|
71
|
+
- tmp/acts_as_money.sqlite3
|
69
72
|
has_rdoc: true
|
70
73
|
homepage: http://github.com/nofxx/money
|
71
74
|
post_install_message:
|