monetize 1.4.0 → 1.9.4
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 +5 -5
- data/.travis.yml +14 -2
- data/CHANGELOG.md +86 -11
- data/Gemfile +11 -1
- data/README.md +16 -6
- data/lib/monetize.rb +36 -192
- data/lib/{collection.rb → monetize/collection.rb} +0 -1
- data/lib/monetize/core_extensions/hash.rb +2 -1
- data/lib/monetize/core_extensions/string.rb +1 -1
- data/lib/monetize/errors.rb +6 -0
- data/lib/monetize/parser.rb +159 -0
- data/lib/monetize/version.rb +1 -1
- data/monetize.gemspec +12 -6
- data/spec/core_extensions_spec.rb +41 -2
- data/spec/monetize_spec.rb +178 -92
- metadata +24 -19
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
|
-
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
2
|
+
SHA256:
|
3
|
+
metadata.gz: 6268692fea7908fbc268790bf98cca9795df28eed380b07d2683eed6f5dad4d1
|
4
|
+
data.tar.gz: 8d795add425b915dc88ecf910505af69b7c1f340ae412083e4c230f0622c2e27
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: e4291ebf0a43a4bea5a2973186b1dca2d9d9fa08729acb956073efb2628d6898620f5eb1110ade3bba5dbac313894babd4ab59c0d1db92b8cb8e041974a0d402
|
7
|
+
data.tar.gz: 06d080183a460c5777b9f7443655bcb00d214ee18a6294f0838a3ac4bfec95715ab1ad5704001d286a6bc1ed0966a4a031942bd946ef9d249372ea527a120785
|
data/.travis.yml
CHANGED
@@ -1,8 +1,20 @@
|
|
1
|
-
|
1
|
+
sudo: false
|
2
|
+
language:
|
3
|
+
- ruby
|
4
|
+
- java
|
2
5
|
rvm:
|
3
6
|
- 1.9.3
|
4
7
|
- 2.0.0
|
5
|
-
- 2.1.
|
8
|
+
- 2.1.10
|
9
|
+
- 2.2.10
|
10
|
+
- 2.3.8
|
11
|
+
- 2.4.6
|
12
|
+
- 2.5.5
|
13
|
+
- 2.6.3
|
14
|
+
- jruby-9.0.5.0
|
15
|
+
jdk: openjdk8
|
16
|
+
before_install:
|
17
|
+
- gem install bundler --version '~> 1.17'
|
6
18
|
script: bundle exec rspec spec
|
7
19
|
notifications:
|
8
20
|
recipients:
|
data/CHANGELOG.md
CHANGED
@@ -1,21 +1,96 @@
|
|
1
1
|
# Changelog
|
2
2
|
|
3
|
-
## 1.4
|
4
|
-
-
|
5
|
-
- Add capability to parse currency amounts given with suffixes (K, M, B, and T)
|
3
|
+
## 1.9.4
|
4
|
+
- Fix symbol parsing that are surrounded by other characters
|
6
5
|
|
7
|
-
## 1.3
|
8
|
-
-
|
9
|
-
- Added correct parsing of Brazilian Real $R symbol
|
10
|
-
- Add testing task for Brazilian Real parsing
|
11
|
-
- Add Lira Sign (₤) as a symbol for GBP
|
6
|
+
## 1.9.3
|
7
|
+
- Fix input parsing when currency symbol after the amount
|
12
8
|
|
13
|
-
## 1.
|
14
|
-
-
|
9
|
+
## 1.9.2
|
10
|
+
- Respect Money.rounding_mode when parsing strings
|
11
|
+
- Deprecate Monetize.extract_cents
|
12
|
+
|
13
|
+
## 1.9.1
|
14
|
+
- Ruby 2.6 support
|
15
|
+
|
16
|
+
## 1.9.0
|
17
|
+
- Relax Money gem dependency to ~> 6.12
|
18
|
+
- Refactor `Monetize::Parser`
|
19
|
+
- Ruby 2.5 support
|
20
|
+
|
21
|
+
## 1.8.0
|
22
|
+
- Money version updated to 6.11.2
|
23
|
+
- Fix rounding problem in `.from_bigdecimal`
|
15
24
|
|
16
|
-
##
|
25
|
+
## 1.7.0
|
26
|
+
- Add South Korean Won currency
|
27
|
+
- Money version updated to 6.9
|
28
|
+
- Coveralls version update to 0.8.20
|
29
|
+
- Add South Korean Won currency
|
30
|
+
- Improve conversion to Money from Hash
|
31
|
+
|
32
|
+
## 1.6.0
|
33
|
+
- Ruby 2.4.0 support
|
34
|
+
- Money version updated to 6.8
|
35
|
+
|
36
|
+
## 1.5.0
|
37
|
+
- Fix issue where parsing a Money object resulted in a Money object with its currency set to `Money.default_currency`,
|
38
|
+
rather than the currency that it was sent in as.
|
39
|
+
- Add extra currencies:
|
40
|
+
- Azerbaijani manat
|
41
|
+
- Chinese yuan
|
42
|
+
- Czech koruna
|
43
|
+
- Hungarian forint
|
44
|
+
- Indian rupee
|
45
|
+
- Russian ruble
|
46
|
+
- Turkish Lira
|
47
|
+
- Ukrainian Hryvnia
|
48
|
+
- Swiss Frank
|
49
|
+
- Polish Zloty
|
50
|
+
- Kazakhstani Tenge
|
51
|
+
- Fix issue with losing precision on BigDecimal input
|
52
|
+
- Add Swedish krona
|
53
|
+
- Exclude ambiguous kr symbol from parsing
|
54
|
+
- Fix JPY parsing
|
55
|
+
- Subclass all errors to Monetize::Error
|
56
|
+
- Fix ruby 1.9.3 compatibility
|
57
|
+
- Suppress errors when using parse. Use `parse!` instead
|
58
|
+
- Strip currency symbol prefix when parsing input
|
59
|
+
|
60
|
+
## 1.4.0
|
17
61
|
- Fixed support for <code>Money.infinite_precision = true</code> in .to_money
|
18
62
|
- Add Rubocop config to project
|
19
63
|
- Reformat code to adapt to Rubocop guidelines
|
20
64
|
- Add config setting to always enforce currency delimiters
|
21
65
|
- Add rake console task
|
66
|
+
- Required Forwardable on Collection to resolve NameError [\#44](https://github.com/RubyMoney/monetize/issues/44)
|
67
|
+
- Add capability to parse currency amounts given with suffixes (K, M, B, and T)
|
68
|
+
|
69
|
+
## 1.3.1
|
70
|
+
- Updated Money version dependency to 6.6
|
71
|
+
|
72
|
+
## 1.3.0
|
73
|
+
- Add Lira Sign (₤) as a symbol for GBP
|
74
|
+
|
75
|
+
## 1.2.0
|
76
|
+
- Add support for parsing Yen symbol
|
77
|
+
- Add `Monetize.parse_collection` and `Monetize::Collection` class for parsing multiple values
|
78
|
+
- Add parsing of C$ for Canadian Dollar
|
79
|
+
- Add NilClass extension
|
80
|
+
- Add Hash extension
|
81
|
+
|
82
|
+
## 1.1.0
|
83
|
+
- Add :assume_from_symbol option to #parse
|
84
|
+
- Enable #parse to detect currency with signed amounts
|
85
|
+
- Updated Money version dependency to 6.5.0
|
86
|
+
|
87
|
+
## 1.0.0
|
88
|
+
- Updated Money version dependency to 6.4.0
|
89
|
+
|
90
|
+
## 0.4.1
|
91
|
+
- Updated Money version dependency to 6.2.1
|
92
|
+
|
93
|
+
## 0.4.0
|
94
|
+
- Added correct parsing of Brazilian Real $R symbol
|
95
|
+
- Add testing task for Brazilian Real parsing
|
96
|
+
- Updated Money version dependency to 6.2.0
|
data/Gemfile
CHANGED
@@ -1,5 +1,15 @@
|
|
1
1
|
source 'https://rubygems.org'
|
2
2
|
|
3
|
-
gem 'coveralls', require: false
|
3
|
+
gem 'coveralls', '>= 0.8.20', require: false
|
4
|
+
|
5
|
+
# JSON and I18n gem no longer supports ruby < 2.0.0
|
6
|
+
if defined?(JRUBY_VERSION)
|
7
|
+
gem 'json'
|
8
|
+
elsif RUBY_VERSION =~ /^1/
|
9
|
+
gem 'json', '~> 1.8.3'
|
10
|
+
gem 'tins', '~> 1.6.0'
|
11
|
+
gem 'term-ansicolor', '~> 1.3.0'
|
12
|
+
gem 'i18n', '~> 0.9'
|
13
|
+
end
|
4
14
|
|
5
15
|
gemspec
|
data/README.md
CHANGED
@@ -1,11 +1,11 @@
|
|
1
1
|
# Monetize
|
2
2
|
|
3
|
-
[](http://badge.fury.io/rb/monetize)
|
4
|
+
[](https://travis-ci.org/RubyMoney/monetize)
|
5
|
+
[](https://codeclimate.com/github/RubyMoney/monetize)
|
6
|
+
[](https://coveralls.io/r/RubyMoney/monetize)
|
7
|
+
[](https://gemnasium.com/RubyMoney/monetize)
|
8
|
+
[](http://opensource.org/licenses/MIT)
|
9
9
|
|
10
10
|
A library for converting various objects into `Money` objects.
|
11
11
|
|
@@ -33,6 +33,16 @@ Monetize.parse("GBP 100") == Money.new(100_00, "GBP")
|
|
33
33
|
"100".to_money == Money.new(100_00, "USD")
|
34
34
|
```
|
35
35
|
|
36
|
+
`parse` will return `nil` if it is unable to parse the input. Use `parse!` instead if you want a `Monetize::Error` (or one of the subclasses) to be raised instead:
|
37
|
+
|
38
|
+
```ruby
|
39
|
+
>> Monetize.parse('OMG 100')
|
40
|
+
=> nil
|
41
|
+
|
42
|
+
>> Monetize.parse!('OMG 100')
|
43
|
+
Monetize::ParseError: Unknown currency 'omg'
|
44
|
+
```
|
45
|
+
|
36
46
|
Optionally, enable the ability to assume the currency from a passed symbol. Otherwise, currency symbols will be ignored, and USD used as the default currency:
|
37
47
|
|
38
48
|
```ruby
|
data/lib/monetize.rb
CHANGED
@@ -2,30 +2,12 @@
|
|
2
2
|
|
3
3
|
require 'money'
|
4
4
|
require 'monetize/core_extensions'
|
5
|
+
require 'monetize/errors'
|
5
6
|
require 'monetize/version'
|
6
|
-
require '
|
7
|
+
require 'monetize/parser'
|
8
|
+
require 'monetize/collection'
|
7
9
|
|
8
10
|
module Monetize
|
9
|
-
CURRENCY_SYMBOLS = {
|
10
|
-
'$' => 'USD',
|
11
|
-
'€' => 'EUR',
|
12
|
-
'£' => 'GBP',
|
13
|
-
'₤' => 'GBP',
|
14
|
-
'R$' => 'BRL',
|
15
|
-
'R' => 'ZAR',
|
16
|
-
'¥' => 'JPY',
|
17
|
-
'C$' => 'CAD'
|
18
|
-
}
|
19
|
-
|
20
|
-
MULTIPLIER_SUFFIXES = {
|
21
|
-
'K' => 3,
|
22
|
-
'M' => 6,
|
23
|
-
'B' => 9,
|
24
|
-
'T' => 12
|
25
|
-
}
|
26
|
-
MULTIPLIER_SUFFIXES.default = 0
|
27
|
-
MULTIPLIER_REGEXP = Regexp.new(format('^(.*?\d)(%s)\b([^\d]*)$', MULTIPLIER_SUFFIXES.keys.join('|')), 'i')
|
28
|
-
|
29
11
|
# Class methods
|
30
12
|
class << self
|
31
13
|
# @attr_accessor [true, false] assume_from_symbol Use this to enable the
|
@@ -37,195 +19,57 @@ module Monetize
|
|
37
19
|
# though, it will try to determine the correct separator by itself. Set this
|
38
20
|
# to true to enforce the delimiters set in the currency all the time.
|
39
21
|
attr_accessor :enforce_currency_delimiters
|
40
|
-
end
|
41
|
-
|
42
|
-
def self.parse(input, currency = Money.default_currency, options = {})
|
43
|
-
input = input.to_s.strip
|
44
|
-
|
45
|
-
computed_currency = if options.fetch(:assume_from_symbol) { assume_from_symbol }
|
46
|
-
compute_currency(input)
|
47
|
-
else
|
48
|
-
input[/[A-Z]{2,3}/]
|
49
|
-
end
|
50
|
-
|
51
|
-
currency = computed_currency || currency || Money.default_currency
|
52
|
-
currency = Money::Currency.wrap(currency)
|
53
|
-
|
54
|
-
fractional = extract_cents(input, currency)
|
55
|
-
Money.new(fractional, currency)
|
56
|
-
end
|
57
|
-
|
58
|
-
def self.parse_collection(input, currency = Money.default_currency, options = {})
|
59
|
-
Collection.parse(input, currency, options)
|
60
|
-
end
|
61
|
-
|
62
|
-
def self.from_string(value, currency = Money.default_currency)
|
63
|
-
value = BigDecimal.new(value.to_s)
|
64
|
-
from_bigdecimal(value, currency)
|
65
|
-
end
|
66
22
|
|
67
|
-
|
68
|
-
|
69
|
-
|
70
|
-
|
71
|
-
end
|
72
|
-
|
73
|
-
def self.from_float(value, currency = Money.default_currency)
|
74
|
-
value = BigDecimal.new(value.to_s)
|
75
|
-
from_bigdecimal(value, currency)
|
76
|
-
end
|
77
|
-
|
78
|
-
def self.from_bigdecimal(value, currency = Money.default_currency)
|
79
|
-
currency = Money::Currency.wrap(currency)
|
80
|
-
value *= currency.subunit_to_unit
|
81
|
-
value = value.round unless Money.infinite_precision
|
82
|
-
Money.new(value, currency)
|
83
|
-
end
|
84
|
-
|
85
|
-
def self.from_numeric(value, currency = Money.default_currency)
|
86
|
-
case value
|
87
|
-
when Fixnum
|
88
|
-
from_fixnum(value, currency)
|
89
|
-
when Numeric
|
90
|
-
value = BigDecimal.new(value.to_s)
|
91
|
-
from_bigdecimal(value, currency)
|
92
|
-
else
|
93
|
-
fail ArgumentError, "'value' should be a type of Numeric"
|
23
|
+
def parse(input, currency = Money.default_currency, options = {})
|
24
|
+
parse! input, currency, options
|
25
|
+
rescue Error
|
26
|
+
nil
|
94
27
|
end
|
95
|
-
end
|
96
|
-
|
97
|
-
def self.extract_cents(input, currency = Money.default_currency)
|
98
|
-
multiplier_exp, input = extract_multiplier(input)
|
99
|
-
|
100
|
-
num = input.gsub(/[^\d.,'-]/, '')
|
101
|
-
|
102
|
-
negative, num = extract_sign(num)
|
103
|
-
|
104
|
-
num.chop! if num.match(/[\.|,]$/)
|
105
|
-
|
106
|
-
major, minor = extract_major_minor(num, currency)
|
107
28
|
|
108
|
-
|
29
|
+
def parse!(input, currency = Money.default_currency, options = {})
|
30
|
+
return input if input.is_a?(Money)
|
31
|
+
return from_numeric(input, currency) if input.is_a?(Numeric)
|
109
32
|
|
110
|
-
|
33
|
+
parser = Monetize::Parser.new(input, currency, options)
|
34
|
+
amount, currency = parser.parse
|
111
35
|
|
112
|
-
|
113
|
-
|
114
|
-
|
115
|
-
end
|
116
|
-
|
117
|
-
private
|
118
|
-
|
119
|
-
def self.apply_multiplier(multiplier_exp, cents, minor)
|
120
|
-
cents *= (10**multiplier_exp)
|
121
|
-
minor = minor.to_s + ('0' * multiplier_exp)
|
122
|
-
shift = minor[0...multiplier_exp].to_i * 100
|
123
|
-
cents += shift
|
124
|
-
minor = (minor[multiplier_exp..-1] || '')
|
125
|
-
[cents, minor]
|
126
|
-
end
|
127
|
-
|
128
|
-
def self.apply_sign(negative, cents)
|
129
|
-
negative ? cents * -1 : cents
|
130
|
-
end
|
131
|
-
|
132
|
-
def self.contains_currency_symbol?(amount)
|
133
|
-
amount =~ currency_symbol_regex
|
134
|
-
end
|
135
|
-
|
136
|
-
def self.compute_currency(amount)
|
137
|
-
if contains_currency_symbol?(amount)
|
138
|
-
matches = amount.match(currency_symbol_regex)
|
139
|
-
CURRENCY_SYMBOLS[matches[:symbol]]
|
140
|
-
else
|
141
|
-
amount[/[A-Z]{2,3}/]
|
36
|
+
Money.from_amount(amount, currency)
|
37
|
+
rescue Money::Currency::UnknownCurrency => e
|
38
|
+
fail ParseError, e.message
|
142
39
|
end
|
143
|
-
end
|
144
|
-
|
145
|
-
def self.extract_major_minor(num, currency)
|
146
|
-
used_delimiters = num.scan(/[^\d]/).uniq
|
147
40
|
|
148
|
-
|
149
|
-
|
150
|
-
[num, 0]
|
151
|
-
when 2
|
152
|
-
thousands_separator, decimal_mark = used_delimiters
|
153
|
-
split_major_minor(num.gsub(thousands_separator, ''), decimal_mark)
|
154
|
-
when 1
|
155
|
-
extract_major_minor_with_single_delimiter(num, currency, used_delimiters.first)
|
156
|
-
else
|
157
|
-
fail ArgumentError, 'Invalid currency amount'
|
41
|
+
def parse_collection(input, currency = Money.default_currency, options = {})
|
42
|
+
Collection.parse(input, currency, options)
|
158
43
|
end
|
159
|
-
end
|
160
44
|
|
161
|
-
|
162
|
-
|
163
|
-
|
164
|
-
elsif enforce_currency_delimiters and delimiter == currency.thousands_separator
|
165
|
-
[num.gsub(delimiter, ''), 0]
|
166
|
-
else
|
167
|
-
extract_major_minor_with_tentative_delimiter(num, delimiter)
|
45
|
+
def from_string(value, currency = Money.default_currency)
|
46
|
+
value = BigDecimal(value.to_s)
|
47
|
+
Money.from_amount(value, currency)
|
168
48
|
end
|
169
|
-
end
|
170
|
-
|
171
|
-
def self.extract_major_minor_with_tentative_delimiter(num, delimiter)
|
172
|
-
if num.scan(delimiter).length > 1
|
173
|
-
# Multiple matches; treat as thousands separator
|
174
|
-
[num.gsub(delimiter, ''), '00']
|
175
|
-
else
|
176
|
-
possible_major, possible_minor = split_major_minor(num, delimiter)
|
177
49
|
|
178
|
-
|
179
|
-
|
180
|
-
[possible_major, possible_minor]
|
181
|
-
else
|
182
|
-
["#{possible_major}#{possible_minor}", '00']
|
183
|
-
end
|
50
|
+
def from_fixnum(value, currency = Money.default_currency)
|
51
|
+
Money.from_amount(value, currency)
|
184
52
|
end
|
185
|
-
|
53
|
+
alias_method :from_integer, :from_fixnum
|
186
54
|
|
187
|
-
|
188
|
-
|
189
|
-
multiplier_suffix = matches[2].upcase
|
190
|
-
[MULTIPLIER_SUFFIXES[multiplier_suffix], "#{$1}#{$3}"]
|
191
|
-
else
|
192
|
-
[0, input]
|
55
|
+
def from_float(value, currency = Money.default_currency)
|
56
|
+
Money.from_amount(value, currency)
|
193
57
|
end
|
194
|
-
end
|
195
|
-
|
196
|
-
def self.extract_sign(input)
|
197
|
-
result = (input =~ /^-+(.*)$/ or input =~ /^(.*)-+$/) ? [true, $1] : [false, input]
|
198
|
-
fail ArgumentError, 'Invalid currency amount (hyphen)' if result[1].include?('-')
|
199
|
-
result
|
200
|
-
end
|
201
58
|
|
202
|
-
|
203
|
-
|
204
|
-
|
59
|
+
def from_bigdecimal(value, currency = Money.default_currency)
|
60
|
+
Money.from_amount(value, currency)
|
61
|
+
end
|
205
62
|
|
206
|
-
|
207
|
-
|
208
|
-
|
209
|
-
elsif minor.size < currency.decimal_places
|
210
|
-
(minor + ('0' * currency.decimal_places))[0, currency.decimal_places].to_i
|
211
|
-
elsif minor.size > currency.decimal_places
|
212
|
-
if minor[currency.decimal_places, 1].to_i >= 5
|
213
|
-
minor[0, currency.decimal_places].to_i + 1
|
214
|
-
else
|
215
|
-
minor[0, currency.decimal_places].to_i
|
216
|
-
end
|
217
|
-
else
|
218
|
-
minor.to_i
|
63
|
+
def from_numeric(value, currency = Money.default_currency)
|
64
|
+
fail ArgumentError, "'value' should be a type of Numeric" unless value.is_a?(Numeric)
|
65
|
+
Money.from_amount(value, currency)
|
219
66
|
end
|
220
|
-
end
|
221
67
|
|
222
|
-
|
223
|
-
|
224
|
-
minor = '00' unless minor
|
225
|
-
[major, minor]
|
226
|
-
end
|
68
|
+
def extract_cents(input, currency = Money.default_currency)
|
69
|
+
warn '[DEPRECATION] Monetize.extract_cents is deprecated. Use Monetize.parse().cents'
|
227
70
|
|
228
|
-
|
229
|
-
|
71
|
+
money = parse(input, currency)
|
72
|
+
money.cents if money
|
73
|
+
end
|
230
74
|
end
|
231
75
|
end
|
@@ -2,6 +2,7 @@
|
|
2
2
|
|
3
3
|
class Hash
|
4
4
|
def to_money(currency = nil)
|
5
|
-
|
5
|
+
hash_currency = self[:currency].is_a?(Hash) ? self[:currency][:iso_code] : self[:currency]
|
6
|
+
Money.new(self[:cents] || self[:fractional], hash_currency || currency || Money.default_currency)
|
6
7
|
end
|
7
8
|
end
|
@@ -0,0 +1,159 @@
|
|
1
|
+
# encoding: utf-8
|
2
|
+
|
3
|
+
module Monetize
|
4
|
+
class Parser
|
5
|
+
CURRENCY_SYMBOLS = {
|
6
|
+
'$' => 'USD',
|
7
|
+
'€' => 'EUR',
|
8
|
+
'£' => 'GBP',
|
9
|
+
'₤' => 'GBP',
|
10
|
+
'R$' => 'BRL',
|
11
|
+
'R' => 'ZAR',
|
12
|
+
'¥' => 'JPY',
|
13
|
+
'C$' => 'CAD',
|
14
|
+
'₼' => 'AZN',
|
15
|
+
'元' => 'CNY',
|
16
|
+
'Kč' => 'CZK',
|
17
|
+
'Ft' => 'HUF',
|
18
|
+
'₹' => 'INR',
|
19
|
+
'₽' => 'RUB',
|
20
|
+
'₺' => 'TRY',
|
21
|
+
'₴' => 'UAH',
|
22
|
+
'Fr' => 'CHF',
|
23
|
+
'zł' => 'PLN',
|
24
|
+
'₸' => 'KZT',
|
25
|
+
"₩" => 'KRW',
|
26
|
+
}
|
27
|
+
|
28
|
+
MULTIPLIER_SUFFIXES = { 'K' => 3, 'M' => 6, 'B' => 9, 'T' => 12 }
|
29
|
+
MULTIPLIER_SUFFIXES.default = 0
|
30
|
+
MULTIPLIER_REGEXP = Regexp.new(format('^(.*?\d)(%s)\b([^\d]*)$', MULTIPLIER_SUFFIXES.keys.join('|')), 'i')
|
31
|
+
|
32
|
+
DEFAULT_DECIMAL_MARK = '.'.freeze
|
33
|
+
|
34
|
+
def initialize(input, fallback_currency = Money.default_currency, options = {})
|
35
|
+
@input = input.to_s.strip
|
36
|
+
@fallback_currency = fallback_currency
|
37
|
+
@options = options
|
38
|
+
end
|
39
|
+
|
40
|
+
def parse
|
41
|
+
currency = Money::Currency.wrap(parse_currency)
|
42
|
+
|
43
|
+
multiplier_exp, input = extract_multiplier
|
44
|
+
|
45
|
+
num = input.gsub(/(?:^#{currency.symbol}|[^\d.,'-]+)/, '')
|
46
|
+
|
47
|
+
negative, num = extract_sign(num)
|
48
|
+
|
49
|
+
num.chop! if num =~ /[\.|,]$/
|
50
|
+
|
51
|
+
major, minor = extract_major_minor(num, currency)
|
52
|
+
|
53
|
+
amount = BigDecimal([major, minor].join(DEFAULT_DECIMAL_MARK))
|
54
|
+
amount = apply_multiplier(multiplier_exp, amount)
|
55
|
+
amount = apply_sign(negative, amount)
|
56
|
+
|
57
|
+
[amount, currency]
|
58
|
+
end
|
59
|
+
|
60
|
+
private
|
61
|
+
|
62
|
+
attr_reader :input, :fallback_currency, :options
|
63
|
+
|
64
|
+
def parse_currency
|
65
|
+
computed_currency = nil
|
66
|
+
computed_currency = compute_currency if assume_from_symbol?
|
67
|
+
computed_currency ||= input[/[A-Z]{2,3}/]
|
68
|
+
|
69
|
+
computed_currency || fallback_currency || Money.default_currency
|
70
|
+
end
|
71
|
+
|
72
|
+
def assume_from_symbol?
|
73
|
+
options.fetch(:assume_from_symbol) { Monetize.assume_from_symbol }
|
74
|
+
end
|
75
|
+
|
76
|
+
def apply_multiplier(multiplier_exp, amount)
|
77
|
+
amount * 10**multiplier_exp
|
78
|
+
end
|
79
|
+
|
80
|
+
def apply_sign(negative, amount)
|
81
|
+
negative ? amount * -1 : amount
|
82
|
+
end
|
83
|
+
|
84
|
+
def compute_currency
|
85
|
+
match = input.match(currency_symbol_regex)
|
86
|
+
CURRENCY_SYMBOLS[match.to_s] if match
|
87
|
+
end
|
88
|
+
|
89
|
+
def extract_major_minor(num, currency)
|
90
|
+
used_delimiters = num.scan(/[^\d]/).uniq
|
91
|
+
|
92
|
+
case used_delimiters.length
|
93
|
+
when 0
|
94
|
+
[num, 0]
|
95
|
+
when 2
|
96
|
+
thousands_separator, decimal_mark = used_delimiters
|
97
|
+
split_major_minor(num.gsub(thousands_separator, ''), decimal_mark)
|
98
|
+
when 1
|
99
|
+
extract_major_minor_with_single_delimiter(num, currency, used_delimiters.first)
|
100
|
+
else
|
101
|
+
fail ParseError, 'Invalid amount'
|
102
|
+
end
|
103
|
+
end
|
104
|
+
|
105
|
+
def extract_major_minor_with_single_delimiter(num, currency, delimiter)
|
106
|
+
if delimiter == currency.decimal_mark
|
107
|
+
split_major_minor(num, delimiter)
|
108
|
+
elsif Monetize.enforce_currency_delimiters && delimiter == currency.thousands_separator
|
109
|
+
[num.gsub(delimiter, ''), 0]
|
110
|
+
else
|
111
|
+
extract_major_minor_with_tentative_delimiter(num, delimiter)
|
112
|
+
end
|
113
|
+
end
|
114
|
+
|
115
|
+
def extract_major_minor_with_tentative_delimiter(num, delimiter)
|
116
|
+
if num.scan(delimiter).length > 1
|
117
|
+
# Multiple matches; treat as thousands separator
|
118
|
+
[num.gsub(delimiter, ''), '00']
|
119
|
+
else
|
120
|
+
possible_major, possible_minor = split_major_minor(num, delimiter)
|
121
|
+
|
122
|
+
if possible_minor.length != 3 || possible_major.length > 3 || delimiter == '.'
|
123
|
+
# Doesn't look like thousands separator
|
124
|
+
[possible_major, possible_minor]
|
125
|
+
else
|
126
|
+
["#{possible_major}#{possible_minor}", '00']
|
127
|
+
end
|
128
|
+
end
|
129
|
+
end
|
130
|
+
|
131
|
+
def extract_multiplier
|
132
|
+
if (matches = MULTIPLIER_REGEXP.match(input))
|
133
|
+
multiplier_suffix = matches[2].upcase
|
134
|
+
[MULTIPLIER_SUFFIXES[multiplier_suffix], "#{$1}#{$3}"]
|
135
|
+
else
|
136
|
+
[0, input]
|
137
|
+
end
|
138
|
+
end
|
139
|
+
|
140
|
+
def extract_sign(input)
|
141
|
+
result = (input =~ /^-+(.*)$/ || input =~ /^(.*)-+$/) ? [true, $1] : [false, input]
|
142
|
+
fail ParseError, 'Invalid amount (hyphen)' if result[1].include?('-')
|
143
|
+
result
|
144
|
+
end
|
145
|
+
|
146
|
+
def regex_safe_symbols
|
147
|
+
CURRENCY_SYMBOLS.keys.map { |key| Regexp.escape(key) }.join('|')
|
148
|
+
end
|
149
|
+
|
150
|
+
def split_major_minor(num, delimiter)
|
151
|
+
major, minor = num.split(delimiter)
|
152
|
+
[major, minor || '00']
|
153
|
+
end
|
154
|
+
|
155
|
+
def currency_symbol_regex
|
156
|
+
/(?<![A-Z])(#{regex_safe_symbols})(?![A-Z])/i
|
157
|
+
end
|
158
|
+
end
|
159
|
+
end
|
data/lib/monetize/version.rb
CHANGED
data/monetize.gemspec
CHANGED
@@ -7,8 +7,8 @@ require 'English'
|
|
7
7
|
Gem::Specification.new do |spec|
|
8
8
|
spec.name = 'monetize'
|
9
9
|
spec.version = Monetize::VERSION
|
10
|
-
spec.authors = ['Shane Emmons']
|
11
|
-
spec.email = ['shane@emmons.io']
|
10
|
+
spec.authors = ['Shane Emmons', 'Anthony Dmitriyev']
|
11
|
+
spec.email = ['shane@emmons.io', 'anthony.dmitriyev@gmail.com']
|
12
12
|
spec.description = 'A library for converting various objects into `Money` objects.'
|
13
13
|
spec.summary = 'A library for converting various objects into `Money` objects.'
|
14
14
|
spec.homepage = 'https://github.com/RubyMoney/monetize'
|
@@ -19,9 +19,15 @@ Gem::Specification.new do |spec|
|
|
19
19
|
spec.test_files = spec.files.grep(%r{^(test|spec|features)/})
|
20
20
|
spec.require_paths = ['lib']
|
21
21
|
|
22
|
-
spec.add_dependency 'money', '~> 6.
|
22
|
+
spec.add_dependency 'money', '~> 6.12'
|
23
23
|
|
24
|
-
spec.add_development_dependency 'bundler'
|
25
|
-
spec.add_development_dependency 'rake'
|
26
|
-
spec.add_development_dependency 'rspec', '~> 3.0
|
24
|
+
spec.add_development_dependency 'bundler'
|
25
|
+
spec.add_development_dependency 'rake', '~> 10.2'
|
26
|
+
spec.add_development_dependency 'rspec', '~> 3.0'
|
27
|
+
|
28
|
+
if spec.respond_to?(:metadata)
|
29
|
+
spec.metadata['changelog_uri'] = 'https://github.com/RubyMoney/monetize/blob/master/CHANGELOG.md'
|
30
|
+
spec.metadata['source_code_uri'] = 'https://github.com/RubyMoney/monetize/'
|
31
|
+
spec.metadata['bug_tracker_uri'] = 'https://github.com/RubyMoney/monetize/issues'
|
32
|
+
end
|
27
33
|
end
|
@@ -31,7 +31,7 @@ describe Monetize, 'core extensions' do
|
|
31
31
|
expect(money.cents).to eq 100_37
|
32
32
|
expect(money.currency).to eq Money.default_currency
|
33
33
|
|
34
|
-
money = BigDecimal
|
34
|
+
money = BigDecimal('1234').to_money
|
35
35
|
expect(money.cents).to eq 1_234_00
|
36
36
|
expect(money.currency).to eq Money.default_currency
|
37
37
|
end
|
@@ -67,7 +67,8 @@ describe Monetize, 'core extensions' do
|
|
67
67
|
'-1,000' => Money.new(-1_000_00),
|
68
68
|
'1,000.5' => Money.new(1_000_50),
|
69
69
|
'1,000.51' => Money.new(1_000_51),
|
70
|
-
'1,000.505' => Money.new(
|
70
|
+
'1,000.505' => Money.new(1_000_50), # ROUND_HALF_EVEN default bankers rounding
|
71
|
+
'1,000.515' => Money.new(1_000_52), # ROUND_HALF_EVEN default bankers rounding
|
71
72
|
'1,000.504' => Money.new(1_000_50),
|
72
73
|
'1,000.0000' => Money.new(1_000_00),
|
73
74
|
'1,000.5000' => Money.new(1_000_50),
|
@@ -149,6 +150,28 @@ describe Monetize, 'core extensions' do
|
|
149
150
|
expect('1'.to_money('JPY')).to eq Money.new(1, 'JPY')
|
150
151
|
expect('1.5'.to_money('KWD').cents).to eq 1_500
|
151
152
|
end
|
153
|
+
|
154
|
+
it 'respects Money.rounding_mode' do
|
155
|
+
expect('1.009'.to_money).to eq(Money.new(1_01))
|
156
|
+
|
157
|
+
Money.rounding_mode(BigDecimal::ROUND_DOWN) do
|
158
|
+
expect('1.009'.to_money).to eq(Money.new(1_00))
|
159
|
+
end
|
160
|
+
|
161
|
+
expect('1.001'.to_money).to eq(Money.new(1_00))
|
162
|
+
|
163
|
+
Money.rounding_mode(BigDecimal::ROUND_UP) do
|
164
|
+
expect('1.001'.to_money).to eq(Money.new(1_01))
|
165
|
+
end
|
166
|
+
end
|
167
|
+
|
168
|
+
it 'produces results similar to Money.from_amount for all the rounding edge cases' do
|
169
|
+
(1_000..1_010).each do |amount|
|
170
|
+
amount = amount.to_f / 1000
|
171
|
+
|
172
|
+
expect(amount.to_s.to_money).to eq(Money.from_amount(amount))
|
173
|
+
end
|
174
|
+
end
|
152
175
|
end
|
153
176
|
|
154
177
|
describe '#to_currency' do
|
@@ -182,6 +205,14 @@ describe Monetize, 'core extensions' do
|
|
182
205
|
end
|
183
206
|
end
|
184
207
|
|
208
|
+
context 'when currency argument is a hash' do
|
209
|
+
subject(:hash) { {cents: 100, currency: {iso_code: 'EUR'}} }
|
210
|
+
|
211
|
+
it 'converts Hash to Money using iso_code from currency hash' do
|
212
|
+
expect(hash.to_money).to eq(Money.new(100, 'EUR'))
|
213
|
+
end
|
214
|
+
end
|
215
|
+
|
185
216
|
context 'when no currency is passed' do
|
186
217
|
subject(:hash) { {cents: 123} }
|
187
218
|
|
@@ -191,6 +222,14 @@ describe Monetize, 'core extensions' do
|
|
191
222
|
expect(hash.to_money('USD')).to eq(Money.new(123, 'USD'))
|
192
223
|
end
|
193
224
|
end
|
225
|
+
|
226
|
+
context "when key name is 'fractional'" do
|
227
|
+
subject(:hash) { {fractional: 100} }
|
228
|
+
|
229
|
+
it 'converts Hash to Money, interpreting fractional as cents' do
|
230
|
+
expect(hash.to_money).to eq(Money.new(100, 'USD'))
|
231
|
+
end
|
232
|
+
end
|
194
233
|
end
|
195
234
|
end
|
196
235
|
|
data/spec/monetize_spec.rb
CHANGED
@@ -56,76 +56,78 @@ describe Monetize do
|
|
56
56
|
Monetize.assume_from_symbol = false
|
57
57
|
end
|
58
58
|
|
59
|
-
|
60
|
-
|
61
|
-
|
62
|
-
|
63
|
-
|
64
|
-
|
65
|
-
|
66
|
-
|
67
|
-
|
68
|
-
|
69
|
-
|
70
|
-
|
71
|
-
|
72
|
-
|
73
|
-
|
74
|
-
|
75
|
-
|
76
|
-
|
77
|
-
|
78
|
-
|
79
|
-
|
80
|
-
|
81
|
-
|
82
|
-
|
83
|
-
|
84
|
-
|
85
|
-
|
86
|
-
|
87
|
-
|
88
|
-
|
59
|
+
Monetize::Parser::CURRENCY_SYMBOLS.each_pair do |symbol, iso_code|
|
60
|
+
context iso_code do
|
61
|
+
let(:currency) { Money::Currency.find(iso_code) }
|
62
|
+
let(:amount) { 5_95 }
|
63
|
+
let(:amount_in_units) { amount.to_f / currency.subunit_to_unit }
|
64
|
+
|
65
|
+
it 'ensures correct amount calculations for test' do
|
66
|
+
expect(amount_in_units * currency.subunit_to_unit).to eq(amount)
|
67
|
+
end
|
68
|
+
|
69
|
+
it "parses formatted inputs with #{iso_code} passed as a symbol" do
|
70
|
+
expect(Monetize.parse("#{symbol}#{amount_in_units}")).to eq Money.new(amount, iso_code)
|
71
|
+
end
|
72
|
+
|
73
|
+
it "parses formatted inputs with #{iso_code} symbol is after the amount" do
|
74
|
+
expect(Monetize.parse("#{amount_in_units}#{symbol}")).to eq Money.new(amount, iso_code)
|
75
|
+
end
|
76
|
+
|
77
|
+
context 'prefix' do
|
78
|
+
it 'parses formatted inputs with plus sign and currency as a symbol' do
|
79
|
+
expect(Monetize.parse("+#{symbol}#{amount_in_units}")).to eq Money.new(amount, iso_code)
|
80
|
+
end
|
81
|
+
|
82
|
+
it 'parses formatted inputs with minus sign and currency as a symbol' do
|
83
|
+
expect(Monetize.parse("-#{symbol}#{amount_in_units}")).to eq Money.new(-amount, iso_code)
|
84
|
+
end
|
85
|
+
end
|
86
|
+
|
87
|
+
context 'postfix' do
|
88
|
+
it 'parses formatted inputs with currency symbol and postfix minus sign' do
|
89
|
+
expect(Monetize.parse("#{symbol}#{amount_in_units}-")).to eq Money.new(-amount, iso_code)
|
90
|
+
end
|
91
|
+
|
92
|
+
it 'parses formatted inputs with currency symbol and postfix plus sign' do
|
93
|
+
expect(Monetize.parse("#{symbol}#{amount_in_units}+")).to eq Money.new(amount, iso_code)
|
94
|
+
end
|
95
|
+
end
|
96
|
+
|
97
|
+
context 'amount suffixes' do
|
98
|
+
it 'parses formatted inputs with amounts given with suffixes' do
|
99
|
+
expect(Monetize.parse("#{symbol}1.26K")).to eq Money.new(1_260 * currency.subunit_to_unit, iso_code)
|
100
|
+
expect(Monetize.parse("#{symbol}126.36M")).to eq Money.new(126_360_000 * currency.subunit_to_unit, iso_code)
|
101
|
+
expect(Monetize.parse("#{symbol}.45B")).to eq Money.new(450_000_000 * currency.subunit_to_unit, iso_code)
|
102
|
+
expect(Monetize.parse("-#{symbol}2.45B")).to eq Money.new(-2_450_000_000 * currency.subunit_to_unit, iso_code)
|
103
|
+
expect(Monetize.parse("#{symbol}1.65T")).to eq Money.new(1_650_000_000_000 * currency.subunit_to_unit, iso_code)
|
104
|
+
end
|
105
|
+
end
|
106
|
+
|
107
|
+
it 'parses formatted inputs with symbol and surrounding spaces' do
|
108
|
+
expect(Monetize.parse(" #{symbol}#{amount_in_units} ")).to eq Money.new(amount, iso_code)
|
109
|
+
end
|
110
|
+
|
111
|
+
it 'parses formatted inputs without currency detection when overridden' do
|
112
|
+
expect(Monetize.parse("#{symbol}5.95", nil, assume_from_symbol: false)).to eq Money.new(amount, 'USD')
|
113
|
+
end
|
114
|
+
end
|
89
115
|
end
|
90
116
|
|
91
117
|
it 'should assume default currency if not a recognised symbol' do
|
92
118
|
expect(Monetize.parse('L9.99')).to eq Money.new(999, 'USD')
|
93
119
|
end
|
94
120
|
|
95
|
-
it '
|
96
|
-
expect(Monetize.parse('
|
97
|
-
|
98
|
-
|
99
|
-
it 'parses formatted inputs with minus sign and currency symbol' do
|
100
|
-
expect(Monetize.parse('-€9.99')).to eq Money.new(-999, 'EUR')
|
101
|
-
expect(Monetize.parse('-£9.99')).to eq Money.new(-999, 'GBP')
|
102
|
-
expect(Monetize.parse('-R$R9.99')).to eq Money.new(-999, 'BRL')
|
103
|
-
expect(Monetize.parse('-¥999')).to eq Money.new(-999, 'JPY')
|
104
|
-
expect(Monetize.parse('-C$9.99')).to eq Money.new(-999, 'CAD')
|
105
|
-
end
|
106
|
-
|
107
|
-
it 'parses formatted inputs with plus and GBP passed as symbol' do
|
108
|
-
expect(Monetize.parse('+€9.99')).to eq Money.new(999, 'EUR')
|
109
|
-
expect(Monetize.parse('+£9.99')).to eq Money.new(999, 'GBP')
|
110
|
-
expect(Monetize.parse('+R$R9.99')).to eq Money.new(999, 'BRL')
|
111
|
-
expect(Monetize.parse('+¥999')).to eq Money.new(999, 'JPY')
|
112
|
-
expect(Monetize.parse('+C$9.99')).to eq Money.new(999, 'CAD')
|
113
|
-
end
|
114
|
-
|
115
|
-
it 'parses formatted inputs with currency symbol and postfix minus sign' do
|
116
|
-
expect(Monetize.parse('€9.99-')).to eq Money.new(-999, 'EUR')
|
117
|
-
end
|
118
|
-
|
119
|
-
it 'parses formatted inputs with currency symbol and postfix plus sign' do
|
120
|
-
expect(Monetize.parse('€9.99+')).to eq Money.new(999, 'EUR')
|
121
|
+
it 'ignores ZAR symbols that is part of a text' do
|
122
|
+
expect(Monetize.parse('EUR 9.99')).to eq Money.new(999, 'EUR')
|
123
|
+
expect(Monetize.parse('9.99 EUR')).to eq Money.new(999, 'EUR')
|
121
124
|
end
|
122
125
|
|
123
|
-
|
124
|
-
|
125
|
-
|
126
|
-
|
127
|
-
|
128
|
-
expect(Monetize.parse('€1.65T')).to eq Money.new(1_650_000_000_000_00, 'EUR')
|
126
|
+
context 'negatives' do
|
127
|
+
it 'ignores the ambiguous kr symbol' do
|
128
|
+
# Could mean either of DKK, EEK, ISK, NOK, SEK
|
129
|
+
expect(Monetize.parse('kr9.99')).to eq Money.new(999, 'USD')
|
130
|
+
end
|
129
131
|
end
|
130
132
|
end
|
131
133
|
|
@@ -158,6 +160,18 @@ describe Monetize do
|
|
158
160
|
it 'should opt out by default' do
|
159
161
|
expect(Monetize.assume_from_symbol).to be_falsy
|
160
162
|
end
|
163
|
+
|
164
|
+
context 'ISO code' do
|
165
|
+
it 'parses currency given as ISO code' do
|
166
|
+
expect('20.00 USD'.to_money).to eq Money.new(20_00, 'USD')
|
167
|
+
expect('20.00 EUR'.to_money).to eq Money.new(20_00, 'EUR')
|
168
|
+
expect('20.00 GBP'.to_money).to eq Money.new(20_00, 'GBP')
|
169
|
+
end
|
170
|
+
|
171
|
+
it 'raises an error if currency code is invalid' do
|
172
|
+
expect { '20.00 OMG'.to_money }.to raise_error Monetize::ParseError
|
173
|
+
end
|
174
|
+
end
|
161
175
|
end
|
162
176
|
|
163
177
|
it 'parses USD-formatted inputs under $10' do
|
@@ -177,10 +191,16 @@ describe Monetize do
|
|
177
191
|
expect(Monetize.parse('1,111,234,567.89')).to eq Money.new(1_111_234_567_89, 'USD')
|
178
192
|
end
|
179
193
|
|
180
|
-
it '
|
181
|
-
expect
|
182
|
-
expect
|
183
|
-
expect
|
194
|
+
it 'parses DKK-formatted inputs' do
|
195
|
+
expect(Monetize.parse('kr.123,45', 'DKK')).to eq Money.new(123_45, 'DKK')
|
196
|
+
expect(Monetize.parse('kr.123.45', 'DKK')).to eq Money.new(123_45, 'DKK')
|
197
|
+
expect(Monetize.parse('kr.45k', 'DKK')).to eq Money.new(45_000_00, 'DKK')
|
198
|
+
end
|
199
|
+
|
200
|
+
it 'returns nil if input is a price range' do
|
201
|
+
expect(Monetize.parse('$5.95-10.95')).to be_nil
|
202
|
+
expect(Monetize.parse('$5.95 - 10.95')).to be_nil
|
203
|
+
expect(Monetize.parse('$5.95 - $10.95')).to be_nil
|
184
204
|
end
|
185
205
|
|
186
206
|
it 'does not return a price for completely invalid input' do
|
@@ -197,8 +217,16 @@ describe Monetize do
|
|
197
217
|
expect(Monetize.parse('$5.95-')).to eq five_ninety_five
|
198
218
|
end
|
199
219
|
|
200
|
-
it '
|
201
|
-
expect
|
220
|
+
it 'returns nil when unable to detect polarity' do
|
221
|
+
expect(Monetize.parse('-$5.95-')).to be_nil
|
222
|
+
end
|
223
|
+
|
224
|
+
it 'returns nil when more than 2 digit separators are used' do
|
225
|
+
expect(Monetize.parse("123.34,56'89 EUR")).to be_nil
|
226
|
+
end
|
227
|
+
|
228
|
+
it 'parses correctly strings with repeated digit separator' do
|
229
|
+
expect(Monetize.parse('19.12.89', 'EUR')).to eq Money.new(191_289_00, 'EUR')
|
202
230
|
end
|
203
231
|
|
204
232
|
it 'parses correctly strings with exactly 3 decimal digits' do
|
@@ -210,6 +238,32 @@ describe Monetize do
|
|
210
238
|
Monetize.enforce_currency_delimiters = false
|
211
239
|
end
|
212
240
|
|
241
|
+
context 'Money object attempting to be parsed' do
|
242
|
+
let(:money) { Money.new(595, 'GBP') }
|
243
|
+
|
244
|
+
it 'returns the original Money object' do
|
245
|
+
expect(Monetize.parse(money)).to eq money
|
246
|
+
expect(Monetize.parse(money).currency).to eq 'GBP'
|
247
|
+
expect(Monetize.parse(money).cents).to eq 595
|
248
|
+
end
|
249
|
+
end
|
250
|
+
|
251
|
+
context 'parsing an instance of Numeric class' do
|
252
|
+
let(:integer) { 10 }
|
253
|
+
let(:float) { 10.0 }
|
254
|
+
let(:big_decimal) { BigDecimal('10') }
|
255
|
+
|
256
|
+
[:integer, :float, :big_decimal].each do |type|
|
257
|
+
it "returns a new Money object based on the #{type} input" do
|
258
|
+
money = Monetize.parse(send(type), 'USD')
|
259
|
+
|
260
|
+
expect(money).to be_instance_of(Money)
|
261
|
+
expect(money.currency).to eq('USD')
|
262
|
+
expect(money.cents).to eq(10_00)
|
263
|
+
end
|
264
|
+
end
|
265
|
+
end
|
266
|
+
|
213
267
|
context 'custom currencies with 4 decimal places' do
|
214
268
|
before :each do
|
215
269
|
Money::Currency.register(JSON.parse(bar, symbolize_names: true))
|
@@ -265,6 +319,18 @@ describe Monetize do
|
|
265
319
|
end
|
266
320
|
end
|
267
321
|
|
322
|
+
describe '.parse!' do
|
323
|
+
it 'does not return a price if there is a price range' do
|
324
|
+
expect { Monetize.parse!('$5.95-10.95') }.to raise_error Monetize::ParseError
|
325
|
+
expect { Monetize.parse!('$5.95 - 10.95') }.to raise_error Monetize::ParseError
|
326
|
+
expect { Monetize.parse!('$5.95 - $10.95') }.to raise_error Monetize::ParseError
|
327
|
+
end
|
328
|
+
|
329
|
+
it 'raises ArgumentError when unable to detect polarity' do
|
330
|
+
expect { Monetize.parse!('-$5.95-') }.to raise_error Monetize::ParseError
|
331
|
+
end
|
332
|
+
end
|
333
|
+
|
268
334
|
describe '.parse_collection' do
|
269
335
|
it 'parses into a Money::Collection' do
|
270
336
|
expect(Monetize.parse_collection('$7')).to be_a Monetize::Collection
|
@@ -287,6 +353,10 @@ describe Monetize do
|
|
287
353
|
expect(collection.first).to eq Monetize.parse('$4')
|
288
354
|
expect(collection.last).to eq Monetize.parse('$10')
|
289
355
|
end
|
356
|
+
|
357
|
+
it 'raises an error if argument is invalid' do
|
358
|
+
expect { Monetize.parse_collection(nil) }.to raise_error Monetize::ArgumentError
|
359
|
+
end
|
290
360
|
end
|
291
361
|
|
292
362
|
describe '.from_string' do
|
@@ -337,6 +407,10 @@ describe Monetize do
|
|
337
407
|
m = Monetize.from_fixnum(1, 'EUR')
|
338
408
|
expect(m.currency).to eq Money::Currency.wrap('EUR')
|
339
409
|
end
|
410
|
+
|
411
|
+
it 'is aliased as from_integer' do
|
412
|
+
expect(Monetize.from_integer(1)).to eq(Monetize.from_fixnum(1))
|
413
|
+
end
|
340
414
|
end
|
341
415
|
|
342
416
|
describe '.from_float' do
|
@@ -366,25 +440,31 @@ describe Monetize do
|
|
366
440
|
|
367
441
|
describe '.from_bigdecimal' do
|
368
442
|
it 'converts given amount to cents' do
|
369
|
-
expect(Monetize.from_bigdecimal(BigDecimal
|
370
|
-
expect(Monetize.from_bigdecimal(BigDecimal
|
371
|
-
expect(Monetize.from_bigdecimal(BigDecimal
|
443
|
+
expect(Monetize.from_bigdecimal(BigDecimal('1'))).to eq Money.new(1_00)
|
444
|
+
expect(Monetize.from_bigdecimal(BigDecimal('1'))).to eq Money.new(1_00, 'USD')
|
445
|
+
expect(Monetize.from_bigdecimal(BigDecimal('1'), 'EUR')).to eq Money.new(1_00, 'EUR')
|
372
446
|
end
|
373
447
|
|
374
448
|
it 'respects :subunit_to_unit currency property' do
|
375
|
-
expect(Monetize.from_bigdecimal(BigDecimal
|
376
|
-
expect(Monetize.from_bigdecimal(BigDecimal
|
377
|
-
expect(Monetize.from_bigdecimal(BigDecimal
|
449
|
+
expect(Monetize.from_bigdecimal(BigDecimal('1'), 'USD')).to eq Money.new(1_00, 'USD')
|
450
|
+
expect(Monetize.from_bigdecimal(BigDecimal('1'), 'TND')).to eq Money.new(1_000, 'TND')
|
451
|
+
expect(Monetize.from_bigdecimal(BigDecimal('1'), 'JPY')).to eq Money.new(1, 'JPY')
|
452
|
+
end
|
453
|
+
|
454
|
+
it 'respects rounding mode when rounding amount to the nearest cent' do
|
455
|
+
amount = BigDecimal('1.005')
|
456
|
+
|
457
|
+
expect(Monetize.from_bigdecimal(amount, 'USD')).to eq Money.from_amount(amount, 'USD')
|
378
458
|
end
|
379
459
|
|
380
460
|
it 'accepts a currency options' do
|
381
|
-
m = Monetize.from_bigdecimal(BigDecimal
|
461
|
+
m = Monetize.from_bigdecimal(BigDecimal('1'))
|
382
462
|
expect(m.currency).to eq Money.default_currency
|
383
463
|
|
384
|
-
m = Monetize.from_bigdecimal(BigDecimal
|
464
|
+
m = Monetize.from_bigdecimal(BigDecimal('1'), Money::Currency.wrap('EUR'))
|
385
465
|
expect(m.currency).to eq Money::Currency.wrap('EUR')
|
386
466
|
|
387
|
-
m = Monetize.from_bigdecimal(BigDecimal
|
467
|
+
m = Monetize.from_bigdecimal(BigDecimal('1'), 'EUR')
|
388
468
|
expect(m.currency).to eq Money::Currency.wrap('EUR')
|
389
469
|
end
|
390
470
|
|
@@ -398,11 +478,11 @@ describe Monetize do
|
|
398
478
|
end
|
399
479
|
|
400
480
|
it 'keeps precision' do
|
401
|
-
expect(Monetize.from_bigdecimal(BigDecimal
|
402
|
-
expect(Monetize.from_bigdecimal(BigDecimal
|
403
|
-
expect(Monetize.from_bigdecimal(BigDecimal
|
404
|
-
expect(Monetize.from_bigdecimal(BigDecimal
|
405
|
-
expect(Monetize.from_bigdecimal(BigDecimal
|
481
|
+
expect(Monetize.from_bigdecimal(BigDecimal('1'))).to eq Money.new(100)
|
482
|
+
expect(Monetize.from_bigdecimal(BigDecimal('1.23456'))).to eq Money.new(123.456)
|
483
|
+
expect(Monetize.from_bigdecimal(BigDecimal('-1.23456'))).to eq Money.new(-123.456)
|
484
|
+
expect(Monetize.from_bigdecimal(BigDecimal('1.23456'))).to eq Money.new(123.456, 'USD')
|
485
|
+
expect(Monetize.from_bigdecimal(BigDecimal('1.23456'), 'EUR')).to eq Money.new(123.456, 'EUR')
|
406
486
|
|
407
487
|
expect('1'.to_money).to eq Money.new(100)
|
408
488
|
expect('1.23456'.to_money).to eq Money.new(123.456)
|
@@ -417,19 +497,11 @@ describe Monetize do
|
|
417
497
|
it 'converts given amount to cents' do
|
418
498
|
expect(Monetize.from_numeric(1)).to eq Money.new(1_00)
|
419
499
|
expect(Monetize.from_numeric(1.0)).to eq Money.new(1_00)
|
420
|
-
expect(Monetize.from_numeric(BigDecimal
|
500
|
+
expect(Monetize.from_numeric(BigDecimal('1'))).to eq Money.new(1_00)
|
421
501
|
end
|
422
502
|
|
423
503
|
it 'raises ArgumentError with unsupported argument' do
|
424
|
-
expect { Monetize.from_numeric('100') }.to raise_error(ArgumentError)
|
425
|
-
end
|
426
|
-
|
427
|
-
it 'optimizes workload' do
|
428
|
-
expect(Monetize).to receive(:from_fixnum).with(1, 'USD').and_return(Money.new(1_00, 'USD'))
|
429
|
-
expect(Monetize.from_numeric(1, 'USD')).to eq Money.new(1_00, 'USD')
|
430
|
-
expect(Monetize).to receive(:from_bigdecimal).with(BigDecimal.new('1.0'), 'USD').
|
431
|
-
and_return(Money.new(1_00, 'USD'))
|
432
|
-
expect(Monetize.from_numeric(1.0, 'USD')).to eq Money.new(1_00, 'USD')
|
504
|
+
expect { Monetize.from_numeric('100') }.to raise_error(Monetize::ArgumentError)
|
433
505
|
end
|
434
506
|
|
435
507
|
it 'respects :subunit_to_unit currency property' do
|
@@ -457,6 +529,20 @@ describe Monetize do
|
|
457
529
|
end
|
458
530
|
|
459
531
|
describe '.extract_cents' do
|
532
|
+
it 'is deprecated' do
|
533
|
+
allow(Monetize).to receive(:warn)
|
534
|
+
|
535
|
+
Monetize.extract_cents('100')
|
536
|
+
|
537
|
+
expect(Monetize)
|
538
|
+
.to have_received(:warn)
|
539
|
+
.with('[DEPRECATION] Monetize.extract_cents is deprecated. Use Monetize.parse().cents')
|
540
|
+
end
|
541
|
+
|
542
|
+
it 'extracts cents from a given string' do
|
543
|
+
expect(Monetize.extract_cents('10.99')).to eq(1099)
|
544
|
+
end
|
545
|
+
|
460
546
|
it "correctly treats pipe marks '|' in input (regression test)" do
|
461
547
|
expect(Monetize.extract_cents('100|0')).to eq Monetize.extract_cents('100!0')
|
462
548
|
end
|
metadata
CHANGED
@@ -1,14 +1,15 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: monetize
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 1.4
|
4
|
+
version: 1.9.4
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Shane Emmons
|
8
|
+
- Anthony Dmitriyev
|
8
9
|
autorequire:
|
9
10
|
bindir: bin
|
10
11
|
cert_chain: []
|
11
|
-
date:
|
12
|
+
date: 2020-01-07 00:00:00.000000000 Z
|
12
13
|
dependencies:
|
13
14
|
- !ruby/object:Gem::Dependency
|
14
15
|
name: money
|
@@ -16,59 +17,60 @@ dependencies:
|
|
16
17
|
requirements:
|
17
18
|
- - "~>"
|
18
19
|
- !ruby/object:Gem::Version
|
19
|
-
version: '6.
|
20
|
+
version: '6.12'
|
20
21
|
type: :runtime
|
21
22
|
prerelease: false
|
22
23
|
version_requirements: !ruby/object:Gem::Requirement
|
23
24
|
requirements:
|
24
25
|
- - "~>"
|
25
26
|
- !ruby/object:Gem::Version
|
26
|
-
version: '6.
|
27
|
+
version: '6.12'
|
27
28
|
- !ruby/object:Gem::Dependency
|
28
29
|
name: bundler
|
29
30
|
requirement: !ruby/object:Gem::Requirement
|
30
31
|
requirements:
|
31
|
-
- - "
|
32
|
+
- - ">="
|
32
33
|
- !ruby/object:Gem::Version
|
33
|
-
version: '
|
34
|
+
version: '0'
|
34
35
|
type: :development
|
35
36
|
prerelease: false
|
36
37
|
version_requirements: !ruby/object:Gem::Requirement
|
37
38
|
requirements:
|
38
|
-
- - "
|
39
|
+
- - ">="
|
39
40
|
- !ruby/object:Gem::Version
|
40
|
-
version: '
|
41
|
+
version: '0'
|
41
42
|
- !ruby/object:Gem::Dependency
|
42
43
|
name: rake
|
43
44
|
requirement: !ruby/object:Gem::Requirement
|
44
45
|
requirements:
|
45
|
-
- - "
|
46
|
+
- - "~>"
|
46
47
|
- !ruby/object:Gem::Version
|
47
|
-
version: '
|
48
|
+
version: '10.2'
|
48
49
|
type: :development
|
49
50
|
prerelease: false
|
50
51
|
version_requirements: !ruby/object:Gem::Requirement
|
51
52
|
requirements:
|
52
|
-
- - "
|
53
|
+
- - "~>"
|
53
54
|
- !ruby/object:Gem::Version
|
54
|
-
version: '
|
55
|
+
version: '10.2'
|
55
56
|
- !ruby/object:Gem::Dependency
|
56
57
|
name: rspec
|
57
58
|
requirement: !ruby/object:Gem::Requirement
|
58
59
|
requirements:
|
59
60
|
- - "~>"
|
60
61
|
- !ruby/object:Gem::Version
|
61
|
-
version: 3.0
|
62
|
+
version: '3.0'
|
62
63
|
type: :development
|
63
64
|
prerelease: false
|
64
65
|
version_requirements: !ruby/object:Gem::Requirement
|
65
66
|
requirements:
|
66
67
|
- - "~>"
|
67
68
|
- !ruby/object:Gem::Version
|
68
|
-
version: 3.0
|
69
|
+
version: '3.0'
|
69
70
|
description: A library for converting various objects into `Money` objects.
|
70
71
|
email:
|
71
72
|
- shane@emmons.io
|
73
|
+
- anthony.dmitriyev@gmail.com
|
72
74
|
executables: []
|
73
75
|
extensions: []
|
74
76
|
extra_rdoc_files: []
|
@@ -83,14 +85,16 @@ files:
|
|
83
85
|
- LICENSE.txt
|
84
86
|
- README.md
|
85
87
|
- Rakefile
|
86
|
-
- lib/collection.rb
|
87
88
|
- lib/monetize.rb
|
89
|
+
- lib/monetize/collection.rb
|
88
90
|
- lib/monetize/core_extensions.rb
|
89
91
|
- lib/monetize/core_extensions/hash.rb
|
90
92
|
- lib/monetize/core_extensions/nil_class.rb
|
91
93
|
- lib/monetize/core_extensions/numeric.rb
|
92
94
|
- lib/monetize/core_extensions/string.rb
|
93
95
|
- lib/monetize/core_extensions/symbol.rb
|
96
|
+
- lib/monetize/errors.rb
|
97
|
+
- lib/monetize/parser.rb
|
94
98
|
- lib/monetize/version.rb
|
95
99
|
- monetize.gemspec
|
96
100
|
- spec/core_extensions_spec.rb
|
@@ -99,7 +103,10 @@ files:
|
|
99
103
|
homepage: https://github.com/RubyMoney/monetize
|
100
104
|
licenses:
|
101
105
|
- MIT
|
102
|
-
metadata:
|
106
|
+
metadata:
|
107
|
+
changelog_uri: https://github.com/RubyMoney/monetize/blob/master/CHANGELOG.md
|
108
|
+
source_code_uri: https://github.com/RubyMoney/monetize/
|
109
|
+
bug_tracker_uri: https://github.com/RubyMoney/monetize/issues
|
103
110
|
post_install_message:
|
104
111
|
rdoc_options: []
|
105
112
|
require_paths:
|
@@ -115,8 +122,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
115
122
|
- !ruby/object:Gem::Version
|
116
123
|
version: '0'
|
117
124
|
requirements: []
|
118
|
-
|
119
|
-
rubygems_version: 2.4.5.1
|
125
|
+
rubygems_version: 3.0.3
|
120
126
|
signing_key:
|
121
127
|
specification_version: 4
|
122
128
|
summary: A library for converting various objects into `Money` objects.
|
@@ -124,4 +130,3 @@ test_files:
|
|
124
130
|
- spec/core_extensions_spec.rb
|
125
131
|
- spec/monetize_spec.rb
|
126
132
|
- spec/spec_helper.rb
|
127
|
-
has_rdoc:
|