money 6.0.0 → 6.0.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +7 -0
- data/.coveralls.yml +1 -0
- data/.gitignore +19 -0
- data/.travis.yml +11 -0
- data/AUTHORS +101 -0
- data/CHANGELOG.md +6 -0
- data/CONTRIBUTING.md +17 -0
- data/Gemfile +6 -0
- data/README.md +9 -10
- data/Rakefile +4 -45
- data/config/currency_iso.json +1 -1
- data/lib/money/bank/variable_exchange.rb +67 -28
- data/lib/money/core_extensions/numeric.rb +7 -0
- data/lib/money/core_extensions/string.rb +13 -0
- data/lib/money/core_extensions/symbol.rb +7 -0
- data/lib/money/core_extensions.rb +5 -78
- data/lib/money/currency/heuristics.rb +121 -117
- data/lib/money/currency/loader.rb +19 -17
- data/lib/money/currency.rb +91 -101
- data/lib/money/deprecations.rb +6 -1
- data/lib/money/money/arithmetic.rb +45 -31
- data/lib/money/money/formatting.rb +41 -55
- data/lib/money/money/parsing.rb +14 -344
- data/lib/money/money.rb +167 -143
- data/lib/money/version.rb +3 -0
- data/lib/money.rb +4 -2
- data/money.gemspec +25 -23
- data/spec/bank/variable_exchange_spec.rb +42 -0
- data/spec/core_extensions_spec.rb +2 -2
- data/spec/currency_spec.rb +16 -0
- data/spec/money/deprecations_spec.rb +51 -0
- data/spec/money/parsing_spec.rb +3 -6
- data/spec/money_spec.rb +75 -9
- data/spec/spec_helper.rb +7 -4
- metadata +120 -65
|
@@ -11,7 +11,6 @@ class Money
|
|
|
11
11
|
Money.new(-fractional, currency)
|
|
12
12
|
end
|
|
13
13
|
|
|
14
|
-
|
|
15
14
|
# Checks whether two money objects have the same currency and the same
|
|
16
15
|
# amount. Checks against money objects with a different currency and checks
|
|
17
16
|
# against objects that do not respond to #to_money will always return false.
|
|
@@ -25,7 +24,9 @@ class Money
|
|
|
25
24
|
# Money.new(100) == Money.new(100) #=> true
|
|
26
25
|
def ==(other_money)
|
|
27
26
|
if other_money.respond_to?(:to_money)
|
|
28
|
-
|
|
27
|
+
unless other_money.is_a?(Money)
|
|
28
|
+
Money.deprecate "as of Money 6.1.0 you must `require 'monetize/core_extensions'` to compare Money to core classes. Please start using the Monetize gem from https://github.com/RubyMoney/monetize if you are not already doing so"
|
|
29
|
+
end
|
|
29
30
|
other_money = other_money.to_money
|
|
30
31
|
fractional == other_money.fractional && currency == other_money.currency
|
|
31
32
|
else
|
|
@@ -44,17 +45,21 @@ class Money
|
|
|
44
45
|
self == other_money
|
|
45
46
|
end
|
|
46
47
|
|
|
47
|
-
def <=>(
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
48
|
+
def <=>(val)
|
|
49
|
+
check_compare_deprecate(val)
|
|
50
|
+
|
|
51
|
+
val = val.to_money
|
|
52
|
+
unless fractional == 0 || val.fractional == 0 || currency == val.currency
|
|
53
|
+
val = val.exchange_to(currency)
|
|
54
|
+
end
|
|
55
|
+
fractional <=> val.fractional
|
|
56
|
+
rescue NoMethodError
|
|
57
|
+
raise ArgumentError, "Comparison of #{self.class} with #{val.inspect} failed"
|
|
58
|
+
end
|
|
59
|
+
|
|
60
|
+
def check_compare_deprecate(val)
|
|
61
|
+
unless val.is_a?(Money)
|
|
62
|
+
Money.deprecate "as of Money 6.1.0 you must `require 'monetize/core_extensions'` to compare Money to core classes. Please start using the Monetize gem from https://github.com/RubyMoney/monetize if you are not already doing so"
|
|
58
63
|
end
|
|
59
64
|
end
|
|
60
65
|
|
|
@@ -185,20 +190,29 @@ class Money
|
|
|
185
190
|
# Money.new(100).divmod(Money.new(9)) #=> [11, #<Money @fractional=1>]
|
|
186
191
|
def divmod(val)
|
|
187
192
|
if val.is_a?(Money)
|
|
188
|
-
|
|
189
|
-
b = val.exchange_to(currency).cents
|
|
190
|
-
q, m = a.divmod(b)
|
|
191
|
-
return [q, Money.new(m, currency)]
|
|
193
|
+
divmod_money(val)
|
|
192
194
|
else
|
|
193
|
-
|
|
194
|
-
q, m = fractional.divmod(as_d(val))
|
|
195
|
-
return [Money.new(q, currency), Money.new(m, currency)]
|
|
196
|
-
else
|
|
197
|
-
return [div(val), Money.new(fractional.modulo(val), currency)]
|
|
198
|
-
end
|
|
195
|
+
divmod_other(val)
|
|
199
196
|
end
|
|
200
197
|
end
|
|
201
198
|
|
|
199
|
+
def divmod_money(val)
|
|
200
|
+
cents = val.exchange_to(currency).cents
|
|
201
|
+
quotient, remainder = fractional.divmod(cents)
|
|
202
|
+
[quotient, Money.new(remainder, currency)]
|
|
203
|
+
end
|
|
204
|
+
private :divmod_money
|
|
205
|
+
|
|
206
|
+
def divmod_other(val)
|
|
207
|
+
if self.class.infinite_precision
|
|
208
|
+
quotient, remainder = fractional.divmod(as_d(val))
|
|
209
|
+
[Money.new(quotient, currency), Money.new(remainder, currency)]
|
|
210
|
+
else
|
|
211
|
+
[div(val), Money.new(fractional.modulo(val), currency)]
|
|
212
|
+
end
|
|
213
|
+
end
|
|
214
|
+
private :divmod_other
|
|
215
|
+
|
|
202
216
|
# Equivalent to +self.divmod(val)[1]+
|
|
203
217
|
#
|
|
204
218
|
# @param [Money, Fixnum] val Number take modulo with.
|
|
@@ -232,15 +246,15 @@ class Money
|
|
|
232
246
|
# @example
|
|
233
247
|
# Money.new(100).remainder(9) #=> #<Money @fractional=1>
|
|
234
248
|
def remainder(val)
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
a_sign, b_sign = :pos, :pos
|
|
239
|
-
a_sign = :neg if a.fractional < 0
|
|
240
|
-
b_sign = :neg if (b.is_a?(Money) and b.fractional < 0) or (b < 0)
|
|
249
|
+
if val.is_a?(Money) && currency != val.currency
|
|
250
|
+
val = val.exchange_to(currency)
|
|
251
|
+
end
|
|
241
252
|
|
|
242
|
-
|
|
243
|
-
|
|
253
|
+
if (fractional < 0 && val < 0) || (fractional > 0 && val > 0)
|
|
254
|
+
self.modulo(val)
|
|
255
|
+
else
|
|
256
|
+
self.modulo(val) - (val.is_a?(Money) ? val : Money.new(val, currency))
|
|
257
|
+
end
|
|
244
258
|
end
|
|
245
259
|
|
|
246
260
|
# Return absolute value of self as a new Money object.
|
|
@@ -1,49 +1,30 @@
|
|
|
1
1
|
# encoding: UTF-8
|
|
2
2
|
class Money
|
|
3
3
|
module Formatting
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
:default => I18n.t(
|
|
11
|
-
:"number.format.delimiter",
|
|
12
|
-
:default => (currency.thousands_separator || ",")
|
|
13
|
-
)
|
|
14
|
-
)
|
|
15
|
-
else
|
|
16
|
-
currency.thousands_separator || ","
|
|
17
|
-
end
|
|
18
|
-
end
|
|
19
|
-
else
|
|
20
|
-
def thousands_separator
|
|
21
|
-
currency.thousands_separator || ","
|
|
4
|
+
def self.included(base)
|
|
5
|
+
[
|
|
6
|
+
[:thousands_separator, :delimiter, "."],
|
|
7
|
+
[:decimal_mark, :separator, ","]
|
|
8
|
+
].each do |method, name, character|
|
|
9
|
+
define_i18n_method(method, name, character)
|
|
22
10
|
end
|
|
23
11
|
end
|
|
24
|
-
alias :delimiter :thousands_separator
|
|
25
12
|
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
def decimal_mark
|
|
13
|
+
def self.define_i18n_method(method, name, character)
|
|
14
|
+
define_method(method) do
|
|
29
15
|
if self.class.use_i18n
|
|
30
16
|
I18n.t(
|
|
31
|
-
:"number.currency.format
|
|
32
|
-
|
|
33
|
-
:
|
|
34
|
-
:default => (currency.decimal_mark || ".")
|
|
17
|
+
:"number.currency.format.#{name}", :default => I18n.t(
|
|
18
|
+
:"number.format.#{name}",
|
|
19
|
+
:default => (currency.send(method) || character)
|
|
35
20
|
)
|
|
36
21
|
)
|
|
37
22
|
else
|
|
38
|
-
currency.
|
|
23
|
+
currency.send(method) || character
|
|
39
24
|
end
|
|
40
25
|
end
|
|
41
|
-
|
|
42
|
-
def decimal_mark
|
|
43
|
-
currency.decimal_mark || "."
|
|
44
|
-
end
|
|
26
|
+
alias_method name, method
|
|
45
27
|
end
|
|
46
|
-
alias :separator :decimal_mark
|
|
47
28
|
|
|
48
29
|
# Creates a formatted price string according to several rules.
|
|
49
30
|
#
|
|
@@ -213,20 +194,7 @@ class Money
|
|
|
213
194
|
end
|
|
214
195
|
end
|
|
215
196
|
|
|
216
|
-
symbol_value =
|
|
217
|
-
if rules.has_key?(:symbol)
|
|
218
|
-
if rules[:symbol] === true
|
|
219
|
-
symbol
|
|
220
|
-
elsif rules[:symbol]
|
|
221
|
-
rules[:symbol]
|
|
222
|
-
else
|
|
223
|
-
""
|
|
224
|
-
end
|
|
225
|
-
elsif rules[:html]
|
|
226
|
-
currency.html_entity == '' ? currency.symbol : currency.html_entity
|
|
227
|
-
else
|
|
228
|
-
symbol
|
|
229
|
-
end
|
|
197
|
+
symbol_value = symbol_value_from(rules)
|
|
230
198
|
|
|
231
199
|
formatted = self.abs.to_s
|
|
232
200
|
|
|
@@ -254,14 +222,7 @@ class Money
|
|
|
254
222
|
formatted.gsub!(regexp_format(formatted, rules, decimal_mark, symbol_value),
|
|
255
223
|
"\\1#{thousands_separator_value}")
|
|
256
224
|
|
|
257
|
-
symbol_position =
|
|
258
|
-
if rules.has_key?(:symbol_position)
|
|
259
|
-
rules[:symbol_position]
|
|
260
|
-
elsif currency.symbol_first?
|
|
261
|
-
:before
|
|
262
|
-
else
|
|
263
|
-
:after
|
|
264
|
-
end
|
|
225
|
+
symbol_position = symbol_position_from(rules)
|
|
265
226
|
|
|
266
227
|
if rules[:sign_positive] == true && self.positive?
|
|
267
228
|
sign = '+'
|
|
@@ -298,7 +259,6 @@ class Money
|
|
|
298
259
|
formatted
|
|
299
260
|
end
|
|
300
261
|
|
|
301
|
-
|
|
302
262
|
private
|
|
303
263
|
|
|
304
264
|
# Cleans up formatting rules.
|
|
@@ -345,4 +305,30 @@ class Money
|
|
|
345
305
|
end
|
|
346
306
|
rules
|
|
347
307
|
end
|
|
308
|
+
|
|
309
|
+
def symbol_value_from(rules)
|
|
310
|
+
if rules.has_key?(:symbol)
|
|
311
|
+
if rules[:symbol] === true
|
|
312
|
+
symbol
|
|
313
|
+
elsif rules[:symbol]
|
|
314
|
+
rules[:symbol]
|
|
315
|
+
else
|
|
316
|
+
""
|
|
317
|
+
end
|
|
318
|
+
elsif rules[:html]
|
|
319
|
+
currency.html_entity == '' ? currency.symbol : currency.html_entity
|
|
320
|
+
else
|
|
321
|
+
symbol
|
|
322
|
+
end
|
|
323
|
+
end
|
|
324
|
+
|
|
325
|
+
def symbol_position_from(rules)
|
|
326
|
+
if rules.has_key?(:symbol_position)
|
|
327
|
+
rules[:symbol_position]
|
|
328
|
+
elsif currency.symbol_first?
|
|
329
|
+
:before
|
|
330
|
+
else
|
|
331
|
+
:after
|
|
332
|
+
end
|
|
333
|
+
end
|
|
348
334
|
end
|
data/lib/money/money/parsing.rb
CHANGED
|
@@ -7,369 +7,39 @@ class Money
|
|
|
7
7
|
end
|
|
8
8
|
|
|
9
9
|
module ClassMethods
|
|
10
|
-
# Parses the current string and converts it to a +Money+ object.
|
|
11
|
-
# Excess characters will be discarded.
|
|
12
|
-
#
|
|
13
|
-
# @param [String, #to_s] input The input to parse.
|
|
14
|
-
# @param [Currency, String, Symbol] currency The currency format.
|
|
15
|
-
# The currency to set the resulting +Money+ object to.
|
|
16
|
-
#
|
|
17
|
-
# @return [Money]
|
|
18
|
-
#
|
|
19
|
-
# @raise [ArgumentError] If any +currency+ is supplied and
|
|
20
|
-
# given value doesn't match the one extracted from
|
|
21
|
-
# the +input+ string.
|
|
22
|
-
#
|
|
23
|
-
# @example
|
|
24
|
-
# '100'.to_money #=> #<Money @fractional=10000>
|
|
25
|
-
# '100.37'.to_money #=> #<Money @fractional=10037>
|
|
26
|
-
# '100 USD'.to_money #=> #<Money @fractional=10000, @currency=#<Money::Currency id: usd>>
|
|
27
|
-
# 'USD 100'.to_money #=> #<Money @fractional=10000, @currency=#<Money::Currency id: usd>>
|
|
28
|
-
# '$100 USD'.to_money #=> #<Money @fractional=10000, @currency=#<Money::Currency id: usd>>
|
|
29
|
-
# 'hello 2000 world'.to_money #=> #<Money @fractional=200000 @currency=#<Money::Currency id: usd>>
|
|
30
|
-
#
|
|
31
|
-
# @example Mismatching currencies
|
|
32
|
-
# 'USD 2000'.to_money("EUR") #=> ArgumentError
|
|
33
|
-
#
|
|
34
|
-
# @see #from_string
|
|
35
|
-
#
|
|
36
10
|
def parse(input, currency = nil)
|
|
37
|
-
Money.deprecate ".parse is
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
# raise Money::Currency.table.collect{|c| c[1][:symbol]}.inspect
|
|
41
|
-
|
|
42
|
-
# Check the first character for a currency symbol, alternatively get it
|
|
43
|
-
# from the stated currency string
|
|
44
|
-
c = if Money.assume_from_symbol && i =~ /^(\$|€|£)/
|
|
45
|
-
case i
|
|
46
|
-
when /^\$/ then "USD"
|
|
47
|
-
when /^€/ then "EUR"
|
|
48
|
-
when /^£/ then "GBP"
|
|
49
|
-
end
|
|
50
|
-
else
|
|
51
|
-
i[/[A-Z]{2,3}/]
|
|
52
|
-
end
|
|
53
|
-
|
|
54
|
-
# check that currency passed and embedded currency are the same,
|
|
55
|
-
# and negotiate the final currency
|
|
56
|
-
if currency.nil? and c.nil?
|
|
57
|
-
currency = Money.default_currency
|
|
58
|
-
elsif currency.nil?
|
|
59
|
-
currency = c
|
|
60
|
-
elsif c.nil?
|
|
61
|
-
currency = currency
|
|
62
|
-
elsif currency != c
|
|
63
|
-
# TODO: ParseError
|
|
64
|
-
raise ArgumentError, "Mismatching Currencies"
|
|
65
|
-
end
|
|
66
|
-
currency = Money::Currency.wrap(currency)
|
|
67
|
-
|
|
68
|
-
fractional = extract_cents(i, currency)
|
|
69
|
-
new(fractional, currency)
|
|
11
|
+
Money.deprecate ".parse is deprecated and will be removed in 6.1.0. Please use the `monetize` gem."
|
|
12
|
+
Monetize.parse(input, currency)
|
|
70
13
|
end
|
|
71
14
|
|
|
72
|
-
# Converts a String into a Money object treating the +value+
|
|
73
|
-
# as amount and converting to fractional unit,
|
|
74
|
-
# according to +currency+ subunit property,
|
|
75
|
-
# before instantiating the Money object.
|
|
76
|
-
#
|
|
77
|
-
# Behind the scenes, this method relies on {#from_bigdecimal}
|
|
78
|
-
# to avoid problems with string-to-numeric conversion.
|
|
79
|
-
#
|
|
80
|
-
# @param [String, #to_s] value The money amount, in dollars.
|
|
81
|
-
# @param [Currency, String, Symbol] currency
|
|
82
|
-
# The currency to set the resulting +Money+ object to.
|
|
83
|
-
#
|
|
84
|
-
# @return [Money]
|
|
85
|
-
#
|
|
86
|
-
# @example
|
|
87
|
-
# Money.from_string("100")
|
|
88
|
-
# #=> #<Money @fractional=10000 @currency="USD">
|
|
89
|
-
# Money.from_string("100", "USD")
|
|
90
|
-
# #=> #<Money @fractional=10000 @currency="USD">
|
|
91
|
-
# Money.from_string("100", "EUR")
|
|
92
|
-
# #=> #<Money @fractional=10000 @currency="EUR">
|
|
93
|
-
# Money.from_string("100", "BHD")
|
|
94
|
-
# #=> #<Money @fractional=100 @currency="BHD">
|
|
95
|
-
#
|
|
96
|
-
# @see String#to_money
|
|
97
|
-
# @see #parse
|
|
98
|
-
#
|
|
99
15
|
def from_string(value, currency = Money.default_currency)
|
|
100
|
-
Money.deprecate ".from_string is
|
|
101
|
-
|
|
16
|
+
Money.deprecate ".from_string is deprecated and will be removed in 6.1.0. Please use the `monetize` gem."
|
|
17
|
+
Monetize.from_string(value, currency)
|
|
102
18
|
end
|
|
103
19
|
|
|
104
|
-
# Converts a Fixnum into a Money object treating the +value+
|
|
105
|
-
# as amount and to corresponding fractional unit,
|
|
106
|
-
# according to +currency+ subunit property,
|
|
107
|
-
# before instantiating the Money object.
|
|
108
|
-
#
|
|
109
|
-
# @param [Fixnum] value The money amount, in dollars.
|
|
110
|
-
# @param [Currency, String, Symbol] currency The currency format.
|
|
111
|
-
#
|
|
112
|
-
# @return [Money]
|
|
113
|
-
#
|
|
114
|
-
# @example
|
|
115
|
-
# Money.from_fixnum(100)
|
|
116
|
-
# #=> #<Money @fractional=10000 @currency="USD">
|
|
117
|
-
# Money.from_fixnum(100, "USD")
|
|
118
|
-
# #=> #<Money @fractional=10000 @currency="USD">
|
|
119
|
-
# Money.from_fixnum(100, "EUR")
|
|
120
|
-
# #=> #<Money @fractional=10000 @currency="EUR">
|
|
121
|
-
# Money.from_fixnum(100, "BHD")
|
|
122
|
-
# #=> #<Money @fractional=100 @currency="BHD">
|
|
123
|
-
#
|
|
124
|
-
# @see Fixnum#to_money
|
|
125
|
-
# @see #from_numeric
|
|
126
|
-
#
|
|
127
20
|
def from_fixnum(value, currency = Money.default_currency)
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
new(amount, currency)
|
|
21
|
+
Money.deprecate ".from_fixnum is deprecated and will be removed in 6.1.0. Please use the `monetize` gem."
|
|
22
|
+
Monetize.from_fixnum(value, currency)
|
|
131
23
|
end
|
|
132
24
|
|
|
133
|
-
# Converts a Float into a Money object treating the +value+
|
|
134
|
-
# as dollars and to corresponding fractional unit,
|
|
135
|
-
# according to +currency+ subunit property,
|
|
136
|
-
# before instantiating the Money object.
|
|
137
|
-
#
|
|
138
|
-
# Behind the scenes, this method relies on Money.from_bigdecimal
|
|
139
|
-
# to avoid problems with floating point precision.
|
|
140
|
-
#
|
|
141
|
-
# @param [Float] value The money amount, in dollars.
|
|
142
|
-
# @param [Currency, String, Symbol] currency The currency format.
|
|
143
|
-
#
|
|
144
|
-
# @return [Money]
|
|
145
|
-
#
|
|
146
|
-
# @example
|
|
147
|
-
# Money.from_float(100.0)
|
|
148
|
-
# #=> #<Money @fractional=10000 @currency="USD">
|
|
149
|
-
# Money.from_float(100.0, "USD")
|
|
150
|
-
# #=> #<Money @fractional=10000 @currency="USD">
|
|
151
|
-
# Money.from_float(100.0, "EUR")
|
|
152
|
-
# #=> #<Money @fractional=10000 @currency="EUR">
|
|
153
|
-
# Money.from_float(100.0, "BHD")
|
|
154
|
-
# #=> #<Money @fractional=100 @currency="BHD">
|
|
155
|
-
#
|
|
156
|
-
# @see Float#to_money
|
|
157
|
-
# @see #from_numeric
|
|
158
|
-
#
|
|
159
25
|
def from_float(value, currency = Money.default_currency)
|
|
160
|
-
Money.deprecate ".from_float is
|
|
161
|
-
|
|
26
|
+
Money.deprecate ".from_float is deprecated and will be removed in 6.1.0. Please use the `monetize` gem."
|
|
27
|
+
Monetize.from_float(value, currency)
|
|
162
28
|
end
|
|
163
29
|
|
|
164
|
-
# Converts a BigDecimal into a Money object treating the +value+
|
|
165
|
-
# as dollars and converting to corresponding fractional unit,
|
|
166
|
-
# according to +currency+ subunit property,
|
|
167
|
-
# before instantiating the Money object.
|
|
168
|
-
#
|
|
169
|
-
# @param [BigDecimal] value The money amount, in dollars.
|
|
170
|
-
# @param [Currency, String, Symbol] currency The currency format.
|
|
171
|
-
#
|
|
172
|
-
# @return [Money]
|
|
173
|
-
#
|
|
174
|
-
# @example
|
|
175
|
-
# Money.from_bigdecimal(BigDecimal.new("100")
|
|
176
|
-
# #=> #<Money @fractional=10000 @currency="USD">
|
|
177
|
-
# Money.from_bigdecimal(BigDecimal.new("100", "USD")
|
|
178
|
-
# #=> #<Money @fractional=10000 @currency="USD">
|
|
179
|
-
# Money.from_bigdecimal(BigDecimal.new("100", "EUR")
|
|
180
|
-
# #=> #<Money @fractional=10000 @currency="EUR">
|
|
181
|
-
# Money.from_bigdecimal(BigDecimal.new("100", "BHD")
|
|
182
|
-
# #=> #<Money @fractional=100 @currency="BHD">
|
|
183
|
-
#
|
|
184
|
-
# @see BigDecimal#to_money
|
|
185
|
-
# @see #from_numeric
|
|
186
|
-
#
|
|
187
30
|
def from_bigdecimal(value, currency = Money.default_currency)
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
amount = amount.round unless Money.infinite_precision
|
|
191
|
-
new(amount, currency)
|
|
31
|
+
Money.deprecate ".from_bigdecimal is deprecated and will be removed in 6.1.0. Please use the `monetize` gem."
|
|
32
|
+
Monetize.from_bigdecimal(value, currency)
|
|
192
33
|
end
|
|
193
34
|
|
|
194
|
-
# Converts a Numeric value into a Money object treating the +value+
|
|
195
|
-
# as dollars and converting to corresponding fractional unit,
|
|
196
|
-
# according to +currency+ subunit property,
|
|
197
|
-
# before instantiating the Money object.
|
|
198
|
-
#
|
|
199
|
-
# This method relies on various +Money.from_*+ methods
|
|
200
|
-
# and tries to forwards the call to the most appropriate method
|
|
201
|
-
# in order to reduce computation effort.
|
|
202
|
-
# For instance, if +value+ is an Integer, this method calls
|
|
203
|
-
# {#from_fixnum} instead of using the default
|
|
204
|
-
# {#from_bigdecimal} which adds the overload to converts
|
|
205
|
-
# the value into a slower BigDecimal instance.
|
|
206
|
-
#
|
|
207
|
-
# @param [Numeric] value The money amount, in dollars.
|
|
208
|
-
# @param [Currency, String, Symbol] currency The currency format.
|
|
209
|
-
#
|
|
210
|
-
# @return [Money]
|
|
211
|
-
#
|
|
212
|
-
# @raise +ArgumentError+ Unless +value+ is a supported type.
|
|
213
|
-
#
|
|
214
|
-
# @example
|
|
215
|
-
# Money.from_numeric(100)
|
|
216
|
-
# #=> #<Money @fractional=10000 @currency="USD">
|
|
217
|
-
# Money.from_numeric(100.00)
|
|
218
|
-
# #=> #<Money @fractional=10000 @currency="USD">
|
|
219
|
-
# Money.from_numeric("100")
|
|
220
|
-
# #=> ArgumentError
|
|
221
|
-
#
|
|
222
|
-
# @see Numeric#to_money
|
|
223
|
-
# @see #from_fixnum
|
|
224
|
-
# @see #from_bigdecimal
|
|
225
|
-
#
|
|
226
35
|
def from_numeric(value, currency = Money.default_currency)
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
from_fixnum(value, currency)
|
|
230
|
-
when Numeric
|
|
231
|
-
from_bigdecimal(BigDecimal.new(value.to_s), currency)
|
|
232
|
-
else
|
|
233
|
-
raise ArgumentError, "`value' should be a Numeric object"
|
|
234
|
-
end
|
|
36
|
+
Money.deprecate ".from_numeric is deprecated and will be removed in 6.1.0. Please use the `monetize` gem."
|
|
37
|
+
Monetize.from_numeric(value, currency)
|
|
235
38
|
end
|
|
236
39
|
|
|
237
|
-
# Takes a number string and attempts to massage out the number.
|
|
238
|
-
#
|
|
239
|
-
# @param [String] input The string containing a potential number.
|
|
240
|
-
#
|
|
241
|
-
# @return [Integer]
|
|
242
|
-
#
|
|
243
40
|
def extract_cents(input, currency = Money.default_currency)
|
|
244
|
-
Money.deprecate ".extract_cents is
|
|
245
|
-
|
|
246
|
-
num = input.gsub(/[^\d.,'-]/, '')
|
|
247
|
-
|
|
248
|
-
# set a boolean flag for if the number is negative or not
|
|
249
|
-
negative = num =~ /^-|-$/ ? true : false
|
|
250
|
-
|
|
251
|
-
# decimal mark character
|
|
252
|
-
decimal_char = currency.decimal_mark
|
|
253
|
-
|
|
254
|
-
# if negative, remove the minus sign from the number
|
|
255
|
-
# if it's not negative, the hyphen makes the value invalid
|
|
256
|
-
if negative
|
|
257
|
-
num = num.sub(/^-|-$/, '')
|
|
258
|
-
end
|
|
259
|
-
|
|
260
|
-
raise ArgumentError, "Invalid currency amount (hyphen)" if num.include?('-')
|
|
261
|
-
|
|
262
|
-
#if the number ends with punctuation, just throw it out. If it means decimal,
|
|
263
|
-
#it won't hurt anything. If it means a literal period or comma, this will
|
|
264
|
-
#save it from being mis-interpreted as a decimal.
|
|
265
|
-
num.chop! if num.match(/[\.|,]$/)
|
|
266
|
-
|
|
267
|
-
# gather all decimal_marks within the result number
|
|
268
|
-
used_delimiters = num.scan(/[^\d]/)
|
|
269
|
-
|
|
270
|
-
# determine the number of unique decimal_marks within the number
|
|
271
|
-
#
|
|
272
|
-
# e.g.
|
|
273
|
-
# $1,234,567.89 would return 2 (, and .)
|
|
274
|
-
# $125,00 would return 1
|
|
275
|
-
# $199 would return 0
|
|
276
|
-
# $1 234,567.89 would raise an error (decimal_marks are space, comma, and period)
|
|
277
|
-
case used_delimiters.uniq.length
|
|
278
|
-
# no decimal_mark or thousands_separator; major (dollars) is the number, and minor (cents) is 0
|
|
279
|
-
when 0 then major, minor = num, 0
|
|
280
|
-
|
|
281
|
-
# two decimal_marks, so we know the last item in this array is the
|
|
282
|
-
# major/minor thousands_separator and the rest are decimal_marks
|
|
283
|
-
when 2
|
|
284
|
-
thousands_separator, decimal_mark = used_delimiters.uniq
|
|
285
|
-
|
|
286
|
-
# remove all thousands_separator, split on the decimal_mark
|
|
287
|
-
major, minor = num.gsub(thousands_separator, '').split(decimal_mark)
|
|
288
|
-
min = 0 unless min
|
|
289
|
-
when 1
|
|
290
|
-
# we can't determine if the comma or period is supposed to be a decimal_mark or a thousands_separator
|
|
291
|
-
# e.g.
|
|
292
|
-
# 1,00 - comma is a thousands_separator
|
|
293
|
-
# 1.000 - period is a thousands_separator
|
|
294
|
-
# 1,000 - comma is a decimal_mark
|
|
295
|
-
# 1,000,000 - comma is a decimal_mark
|
|
296
|
-
# 10000,00 - comma is a thousands_separator
|
|
297
|
-
# 1000,000 - comma is a thousands_separator
|
|
298
|
-
|
|
299
|
-
# assign first decimal_mark for reusability
|
|
300
|
-
decimal_mark = used_delimiters.first
|
|
301
|
-
|
|
302
|
-
# When we have identified the decimal mark character
|
|
303
|
-
if decimal_char == decimal_mark
|
|
304
|
-
major, minor = num.split(decimal_char)
|
|
305
|
-
|
|
306
|
-
else
|
|
307
|
-
# decimal_mark is used as a decimal_mark when there are multiple instances, always
|
|
308
|
-
if num.scan(decimal_mark).length > 1 # multiple matches; treat as decimal_mark
|
|
309
|
-
major, minor = num.gsub(decimal_mark, ''), 0
|
|
310
|
-
else
|
|
311
|
-
# ex: 1,000 - 1.0000 - 10001.000
|
|
312
|
-
# split number into possible major (dollars) and minor (cents) values
|
|
313
|
-
possible_major, possible_minor = num.split(decimal_mark)
|
|
314
|
-
possible_major ||= "0"
|
|
315
|
-
possible_minor ||= "00"
|
|
316
|
-
|
|
317
|
-
# if the minor (cents) length isn't 3, assign major/minor from the possibles
|
|
318
|
-
# e.g.
|
|
319
|
-
# 1,00 => 1.00
|
|
320
|
-
# 1.0000 => 1.00
|
|
321
|
-
# 1.2 => 1.20
|
|
322
|
-
if possible_minor.length != 3 # thousands_separator
|
|
323
|
-
major, minor = possible_major, possible_minor
|
|
324
|
-
else
|
|
325
|
-
# minor length is three
|
|
326
|
-
# let's try to figure out intent of the thousands_separator
|
|
327
|
-
|
|
328
|
-
# the major length is greater than three, which means
|
|
329
|
-
# the comma or period is used as a thousands_separator
|
|
330
|
-
# e.g.
|
|
331
|
-
# 1000,000
|
|
332
|
-
# 100000,000
|
|
333
|
-
if possible_major.length > 3
|
|
334
|
-
major, minor = possible_major, possible_minor
|
|
335
|
-
else
|
|
336
|
-
# number is in format ###{sep}### or ##{sep}### or #{sep}###
|
|
337
|
-
# handle as , is sep, . is thousands_separator
|
|
338
|
-
if decimal_mark == '.'
|
|
339
|
-
major, minor = possible_major, possible_minor
|
|
340
|
-
else
|
|
341
|
-
major, minor = "#{possible_major}#{possible_minor}", 0
|
|
342
|
-
end
|
|
343
|
-
end
|
|
344
|
-
end
|
|
345
|
-
end
|
|
346
|
-
end
|
|
347
|
-
else
|
|
348
|
-
# TODO: ParseError
|
|
349
|
-
raise ArgumentError, "Invalid currency amount"
|
|
350
|
-
end
|
|
351
|
-
|
|
352
|
-
# build the string based on major/minor since decimal_mark/thousands_separator have been removed
|
|
353
|
-
# avoiding floating point arithmetic here to ensure accuracy
|
|
354
|
-
cents = (major.to_i * currency.subunit_to_unit)
|
|
355
|
-
# Because of an bug in JRuby, we can't just call #floor
|
|
356
|
-
minor = minor.to_s
|
|
357
|
-
minor = if minor.size < currency.decimal_places
|
|
358
|
-
(minor + ("0" * currency.decimal_places))[0,currency.decimal_places].to_i
|
|
359
|
-
elsif minor.size > currency.decimal_places
|
|
360
|
-
if minor[currency.decimal_places,1].to_i >= 5
|
|
361
|
-
minor[0,currency.decimal_places].to_i+1
|
|
362
|
-
else
|
|
363
|
-
minor[0,currency.decimal_places].to_i
|
|
364
|
-
end
|
|
365
|
-
else
|
|
366
|
-
minor.to_i
|
|
367
|
-
end
|
|
368
|
-
|
|
369
|
-
cents += minor
|
|
370
|
-
|
|
371
|
-
# if negative, multiply by -1; otherwise, return positive cents
|
|
372
|
-
negative ? cents * -1 : cents
|
|
41
|
+
Money.deprecate ".extract_cents is deprecated and will be removed in 6.1.0. Please use the `monetize` gem."
|
|
42
|
+
Monetize.extract_cents(input, currency)
|
|
373
43
|
end
|
|
374
44
|
end
|
|
375
45
|
end
|