monetize 1.4.0 → 1.9.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 +4 -4
- data/.travis.yml +10 -1
- data/CHANGELOG.md +76 -11
- data/Gemfile +11 -1
- data/README.md +16 -6
- data/lib/monetize.rb +34 -193
- 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 +178 -0
- data/lib/monetize/version.rb +1 -1
- data/monetize.gemspec +10 -4
- data/spec/core_extensions_spec.rb +17 -1
- data/spec/monetize_spec.rb +157 -94
- metadata +22 -18
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA1:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 7c921047a6f291ae61ceb19db130de7b0288d8e2
|
|
4
|
+
data.tar.gz: f48eb20c8582b1453ef6114df30793f9367d934e
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 9b4a49126c30cfd92968d6fed6cd43a806e5cda807f8dfd0c8956d434e0e3a3331191d9e228e5648c4d11157f01d3c3fcdb72f72f21e52f96002ab803b70265a
|
|
7
|
+
data.tar.gz: 571ec0ec170014d408f4ba0208a1b40f04cdf3436b2939e7d43b922cfe2ce3e981fb5a51a6dc79eab8d07ccad4ecfbc270548e15b4293428df87dbc0c912d277
|
data/.travis.yml
CHANGED
|
@@ -1,8 +1,17 @@
|
|
|
1
|
+
sudo: false
|
|
1
2
|
language: ruby
|
|
2
3
|
rvm:
|
|
3
4
|
- 1.9.3
|
|
4
5
|
- 2.0.0
|
|
5
|
-
- 2.1.
|
|
6
|
+
- 2.1.10
|
|
7
|
+
- 2.2.10
|
|
8
|
+
- 2.3.7
|
|
9
|
+
- 2.4.4
|
|
10
|
+
- 2.5.1
|
|
11
|
+
- 2.6.0
|
|
12
|
+
- jruby-9.0.5.0
|
|
13
|
+
before_install:
|
|
14
|
+
- gem install bundler --version '~> 1.17'
|
|
6
15
|
script: bundle exec rspec spec
|
|
7
16
|
notifications:
|
|
8
17
|
recipients:
|
data/CHANGELOG.md
CHANGED
|
@@ -1,21 +1,86 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
-
## 1.
|
|
4
|
-
-
|
|
5
|
-
- Add capability to parse currency amounts given with suffixes (K, M, B, and T)
|
|
3
|
+
## 1.9.1
|
|
4
|
+
- Ruby 2.6 support
|
|
6
5
|
|
|
7
|
-
## 1.
|
|
8
|
-
-
|
|
9
|
-
-
|
|
10
|
-
-
|
|
11
|
-
- Add Lira Sign (₤) as a symbol for GBP
|
|
6
|
+
## 1.9.0
|
|
7
|
+
- Relax Money gem dependency to ~> 6.12
|
|
8
|
+
- Refactor `Monetize::Parser`
|
|
9
|
+
- Ruby 2.5 support
|
|
12
10
|
|
|
13
|
-
## 1.
|
|
14
|
-
-
|
|
11
|
+
## 1.8.0
|
|
12
|
+
- Money version updated to 6.11.2
|
|
13
|
+
- Fix rounding problem in `.from_bigdecimal`
|
|
14
|
+
|
|
15
|
+
## 1.7.0
|
|
16
|
+
- Add South Korean Won currency
|
|
17
|
+
- Money version updated to 6.9
|
|
18
|
+
- Coveralls version update to 0.8.20
|
|
19
|
+
- Add South Korean Won currency
|
|
20
|
+
- Improve conversion to Money from Hash
|
|
21
|
+
|
|
22
|
+
## 1.6.0
|
|
23
|
+
- Ruby 2.4.0 support
|
|
24
|
+
- Money version updated to 6.8
|
|
15
25
|
|
|
16
|
-
##
|
|
26
|
+
## 1.5.0
|
|
27
|
+
- Fix issue where parsing a Money object resulted in a Money object with its currency set to `Money.default_currency`,
|
|
28
|
+
rather than the currency that it was sent in as.
|
|
29
|
+
- Add extra currencies:
|
|
30
|
+
- Azerbaijani manat
|
|
31
|
+
- Chinese yuan
|
|
32
|
+
- Czech koruna
|
|
33
|
+
- Hungarian forint
|
|
34
|
+
- Indian rupee
|
|
35
|
+
- Russian ruble
|
|
36
|
+
- Turkish Lira
|
|
37
|
+
- Ukrainian Hryvnia
|
|
38
|
+
- Swiss Frank
|
|
39
|
+
- Polish Zloty
|
|
40
|
+
- Kazakhstani Tenge
|
|
41
|
+
- Fix issue with losing precision on BigDecimal input
|
|
42
|
+
- Add Swedish krona
|
|
43
|
+
- Exclude ambiguous kr symbol from parsing
|
|
44
|
+
- Fix JPY parsing
|
|
45
|
+
- Subclass all errors to Monetize::Error
|
|
46
|
+
- Fix ruby 1.9.3 compatibility
|
|
47
|
+
- Suppress errors when using parse. Use `parse!` instead
|
|
48
|
+
- Strip currency symbol prefix when parsing input
|
|
49
|
+
|
|
50
|
+
## 1.4.0
|
|
17
51
|
- Fixed support for <code>Money.infinite_precision = true</code> in .to_money
|
|
18
52
|
- Add Rubocop config to project
|
|
19
53
|
- Reformat code to adapt to Rubocop guidelines
|
|
20
54
|
- Add config setting to always enforce currency delimiters
|
|
21
55
|
- Add rake console task
|
|
56
|
+
- Required Forwardable on Collection to resolve NameError [\#44](https://github.com/RubyMoney/monetize/issues/44)
|
|
57
|
+
- Add capability to parse currency amounts given with suffixes (K, M, B, and T)
|
|
58
|
+
|
|
59
|
+
## 1.3.1
|
|
60
|
+
- Updated Money version dependency to 6.6
|
|
61
|
+
|
|
62
|
+
## 1.3.0
|
|
63
|
+
- Add Lira Sign (₤) as a symbol for GBP
|
|
64
|
+
|
|
65
|
+
## 1.2.0
|
|
66
|
+
- Add support for parsing Yen symbol
|
|
67
|
+
- Add `Monetize.parse_collection` and `Monetize::Collection` class for parsing multiple values
|
|
68
|
+
- Add parsing of C$ for Canadian Dollar
|
|
69
|
+
- Add NilClass extension
|
|
70
|
+
- Add Hash extension
|
|
71
|
+
|
|
72
|
+
## 1.1.0
|
|
73
|
+
- Add :assume_from_symbol option to #parse
|
|
74
|
+
- Enable #parse to detect currency with signed amounts
|
|
75
|
+
- Updated Money version dependency to 6.5.0
|
|
76
|
+
|
|
77
|
+
## 1.0.0
|
|
78
|
+
- Updated Money version dependency to 6.4.0
|
|
79
|
+
|
|
80
|
+
## 0.4.1
|
|
81
|
+
- Updated Money version dependency to 6.2.1
|
|
82
|
+
|
|
83
|
+
## 0.4.0
|
|
84
|
+
- Added correct parsing of Brazilian Real $R symbol
|
|
85
|
+
- Add testing task for Brazilian Real parsing
|
|
86
|
+
- 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,54 @@ 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
22
|
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
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
|
-
|
|
67
|
-
def self.from_fixnum(value, currency = Money.default_currency)
|
|
68
|
-
currency = Money::Currency.wrap(currency)
|
|
69
|
-
value *= currency.subunit_to_unit
|
|
70
|
-
Money.new(value, currency)
|
|
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
|
-
|
|
108
|
-
cents = major.to_i * currency.subunit_to_unit
|
|
109
28
|
|
|
110
|
-
|
|
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)
|
|
111
32
|
|
|
112
|
-
|
|
33
|
+
parser = Monetize::Parser.new(input, currency, options)
|
|
34
|
+
currency_from_input = Money::Currency.wrap(parser.parse_currency)
|
|
113
35
|
|
|
114
|
-
|
|
115
|
-
|
|
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.new(parser.parse_cents(currency_from_input), currency_from_input)
|
|
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
|
-
end
|
|
205
|
-
|
|
206
|
-
def self.set_minor_precision(minor, currency)
|
|
207
|
-
if Money.infinite_precision
|
|
208
|
-
(BigDecimal.new(minor) / (10**minor.size)) * currency.subunit_to_unit
|
|
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
|
|
59
|
+
def from_bigdecimal(value, currency = Money.default_currency)
|
|
60
|
+
Money.from_amount(value, currency)
|
|
219
61
|
end
|
|
220
|
-
end
|
|
221
62
|
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
end
|
|
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)
|
|
66
|
+
end
|
|
227
67
|
|
|
228
|
-
|
|
229
|
-
|
|
68
|
+
def extract_cents(input, currency = Money.default_currency)
|
|
69
|
+
Monetize::Parser.new(input).parse_cents(currency)
|
|
70
|
+
end
|
|
230
71
|
end
|
|
231
72
|
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,178 @@
|
|
|
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
|
+
def initialize(input, fallback_currency = Money.default_currency, options = {})
|
|
33
|
+
@input = input.to_s.strip
|
|
34
|
+
@fallback_currency = fallback_currency
|
|
35
|
+
@options = options
|
|
36
|
+
end
|
|
37
|
+
|
|
38
|
+
def parse_cents(currency)
|
|
39
|
+
multiplier_exp, input = extract_multiplier
|
|
40
|
+
|
|
41
|
+
num = input.gsub(/(?:^#{currency.symbol}|[^\d.,'-]+)/, '')
|
|
42
|
+
|
|
43
|
+
negative, num = extract_sign(num)
|
|
44
|
+
|
|
45
|
+
num.chop! if num =~ /[\.|,]$/
|
|
46
|
+
|
|
47
|
+
major, minor = extract_major_minor(num, currency)
|
|
48
|
+
|
|
49
|
+
major, minor = apply_multiplier(multiplier_exp, major.to_i, minor)
|
|
50
|
+
|
|
51
|
+
cents = major.to_i * currency.subunit_to_unit
|
|
52
|
+
|
|
53
|
+
cents += set_minor_precision(minor, currency)
|
|
54
|
+
|
|
55
|
+
apply_sign(negative, cents)
|
|
56
|
+
end
|
|
57
|
+
|
|
58
|
+
def parse_currency
|
|
59
|
+
computed_currency = nil
|
|
60
|
+
computed_currency = compute_currency if assume_from_symbol?
|
|
61
|
+
computed_currency ||= input[/[A-Z]{2,3}/]
|
|
62
|
+
|
|
63
|
+
computed_currency || fallback_currency || Money.default_currency
|
|
64
|
+
end
|
|
65
|
+
|
|
66
|
+
private
|
|
67
|
+
|
|
68
|
+
attr_reader :input, :fallback_currency, :options
|
|
69
|
+
|
|
70
|
+
def assume_from_symbol?
|
|
71
|
+
options.fetch(:assume_from_symbol) { Monetize.assume_from_symbol }
|
|
72
|
+
end
|
|
73
|
+
|
|
74
|
+
def apply_multiplier(multiplier_exp, major, minor)
|
|
75
|
+
major *= 10**multiplier_exp
|
|
76
|
+
minor = minor.to_s + ('0' * multiplier_exp)
|
|
77
|
+
shift = minor[0...multiplier_exp].to_i
|
|
78
|
+
major += shift
|
|
79
|
+
minor = (minor[multiplier_exp..-1] || '')
|
|
80
|
+
[major, minor]
|
|
81
|
+
end
|
|
82
|
+
|
|
83
|
+
def apply_sign(negative, cents)
|
|
84
|
+
negative ? cents * -1 : cents
|
|
85
|
+
end
|
|
86
|
+
|
|
87
|
+
def compute_currency
|
|
88
|
+
matches = input.match(currency_symbol_regex)
|
|
89
|
+
CURRENCY_SYMBOLS[matches[:symbol]] if matches
|
|
90
|
+
end
|
|
91
|
+
|
|
92
|
+
def extract_major_minor(num, currency)
|
|
93
|
+
used_delimiters = num.scan(/[^\d]/).uniq
|
|
94
|
+
|
|
95
|
+
case used_delimiters.length
|
|
96
|
+
when 0
|
|
97
|
+
[num, 0]
|
|
98
|
+
when 2
|
|
99
|
+
thousands_separator, decimal_mark = used_delimiters
|
|
100
|
+
split_major_minor(num.gsub(thousands_separator, ''), decimal_mark)
|
|
101
|
+
when 1
|
|
102
|
+
extract_major_minor_with_single_delimiter(num, currency, used_delimiters.first)
|
|
103
|
+
else
|
|
104
|
+
fail ParseError, 'Invalid amount'
|
|
105
|
+
end
|
|
106
|
+
end
|
|
107
|
+
|
|
108
|
+
def extract_major_minor_with_single_delimiter(num, currency, delimiter)
|
|
109
|
+
if delimiter == currency.decimal_mark
|
|
110
|
+
split_major_minor(num, delimiter)
|
|
111
|
+
elsif Monetize.enforce_currency_delimiters && delimiter == currency.thousands_separator
|
|
112
|
+
[num.gsub(delimiter, ''), 0]
|
|
113
|
+
else
|
|
114
|
+
extract_major_minor_with_tentative_delimiter(num, delimiter)
|
|
115
|
+
end
|
|
116
|
+
end
|
|
117
|
+
|
|
118
|
+
def extract_major_minor_with_tentative_delimiter(num, delimiter)
|
|
119
|
+
if num.scan(delimiter).length > 1
|
|
120
|
+
# Multiple matches; treat as thousands separator
|
|
121
|
+
[num.gsub(delimiter, ''), '00']
|
|
122
|
+
else
|
|
123
|
+
possible_major, possible_minor = split_major_minor(num, delimiter)
|
|
124
|
+
|
|
125
|
+
if possible_minor.length != 3 || possible_major.length > 3 || delimiter == '.'
|
|
126
|
+
# Doesn't look like thousands separator
|
|
127
|
+
[possible_major, possible_minor]
|
|
128
|
+
else
|
|
129
|
+
["#{possible_major}#{possible_minor}", '00']
|
|
130
|
+
end
|
|
131
|
+
end
|
|
132
|
+
end
|
|
133
|
+
|
|
134
|
+
def extract_multiplier
|
|
135
|
+
if (matches = MULTIPLIER_REGEXP.match(input))
|
|
136
|
+
multiplier_suffix = matches[2].upcase
|
|
137
|
+
[MULTIPLIER_SUFFIXES[multiplier_suffix], "#{$1}#{$3}"]
|
|
138
|
+
else
|
|
139
|
+
[0, input]
|
|
140
|
+
end
|
|
141
|
+
end
|
|
142
|
+
|
|
143
|
+
def extract_sign(input)
|
|
144
|
+
result = (input =~ /^-+(.*)$/ || input =~ /^(.*)-+$/) ? [true, $1] : [false, input]
|
|
145
|
+
fail ParseError, 'Invalid amount (hyphen)' if result[1].include?('-')
|
|
146
|
+
result
|
|
147
|
+
end
|
|
148
|
+
|
|
149
|
+
def regex_safe_symbols
|
|
150
|
+
CURRENCY_SYMBOLS.keys.map { |key| Regexp.escape(key) }.join('|')
|
|
151
|
+
end
|
|
152
|
+
|
|
153
|
+
def set_minor_precision(minor, currency)
|
|
154
|
+
if Money.infinite_precision
|
|
155
|
+
(BigDecimal(minor) / (10**minor.size)) * currency.subunit_to_unit
|
|
156
|
+
elsif minor.size < currency.decimal_places
|
|
157
|
+
(minor + ('0' * currency.decimal_places))[0, currency.decimal_places].to_i
|
|
158
|
+
elsif minor.size > currency.decimal_places
|
|
159
|
+
if minor[currency.decimal_places, 1].to_i >= 5
|
|
160
|
+
minor[0, currency.decimal_places].to_i + 1
|
|
161
|
+
else
|
|
162
|
+
minor[0, currency.decimal_places].to_i
|
|
163
|
+
end
|
|
164
|
+
else
|
|
165
|
+
minor.to_i
|
|
166
|
+
end
|
|
167
|
+
end
|
|
168
|
+
|
|
169
|
+
def split_major_minor(num, delimiter)
|
|
170
|
+
major, minor = num.split(delimiter)
|
|
171
|
+
[major, minor || '00']
|
|
172
|
+
end
|
|
173
|
+
|
|
174
|
+
def currency_symbol_regex
|
|
175
|
+
/\A[\+|\-]?(?<symbol>#{regex_safe_symbols})/
|
|
176
|
+
end
|
|
177
|
+
end
|
|
178
|
+
end
|
data/lib/monetize/version.rb
CHANGED
data/monetize.gemspec
CHANGED
|
@@ -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
|
|
@@ -182,6 +182,14 @@ describe Monetize, 'core extensions' do
|
|
|
182
182
|
end
|
|
183
183
|
end
|
|
184
184
|
|
|
185
|
+
context 'when currency argument is a hash' do
|
|
186
|
+
subject(:hash) { {cents: 100, currency: {iso_code: 'EUR'}} }
|
|
187
|
+
|
|
188
|
+
it 'converts Hash to Money using iso_code from currency hash' do
|
|
189
|
+
expect(hash.to_money).to eq(Money.new(100, 'EUR'))
|
|
190
|
+
end
|
|
191
|
+
end
|
|
192
|
+
|
|
185
193
|
context 'when no currency is passed' do
|
|
186
194
|
subject(:hash) { {cents: 123} }
|
|
187
195
|
|
|
@@ -191,6 +199,14 @@ describe Monetize, 'core extensions' do
|
|
|
191
199
|
expect(hash.to_money('USD')).to eq(Money.new(123, 'USD'))
|
|
192
200
|
end
|
|
193
201
|
end
|
|
202
|
+
|
|
203
|
+
context "when key name is 'fractional'" do
|
|
204
|
+
subject(:hash) { {fractional: 100} }
|
|
205
|
+
|
|
206
|
+
it 'converts Hash to Money, interpreting fractional as cents' do
|
|
207
|
+
expect(hash.to_money).to eq(Money.new(100, 'USD'))
|
|
208
|
+
end
|
|
209
|
+
end
|
|
194
210
|
end
|
|
195
211
|
end
|
|
196
212
|
|
data/spec/monetize_spec.rb
CHANGED
|
@@ -56,76 +56,69 @@ 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
|
+
context 'prefix' do
|
|
74
|
+
it 'parses formatted inputs with plus sign and currency as a symbol' do
|
|
75
|
+
expect(Monetize.parse("+#{symbol}#{amount_in_units}")).to eq Money.new(amount, iso_code)
|
|
76
|
+
end
|
|
77
|
+
|
|
78
|
+
it 'parses formatted inputs with minus 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
|
+
end
|
|
82
|
+
|
|
83
|
+
context 'postfix' do
|
|
84
|
+
it 'parses formatted inputs with currency symbol and postfix minus sign' do
|
|
85
|
+
expect(Monetize.parse("#{symbol}#{amount_in_units}-")).to eq Money.new(-amount, iso_code)
|
|
86
|
+
end
|
|
87
|
+
|
|
88
|
+
it 'parses formatted inputs with currency symbol and postfix plus sign' do
|
|
89
|
+
expect(Monetize.parse("#{symbol}#{amount_in_units}+")).to eq Money.new(amount, iso_code)
|
|
90
|
+
end
|
|
91
|
+
end
|
|
92
|
+
|
|
93
|
+
context 'amount suffixes' do
|
|
94
|
+
it 'parses formatted inputs with amounts given with suffixes' do
|
|
95
|
+
expect(Monetize.parse("#{symbol}1.26K")).to eq Money.new(1_260 * currency.subunit_to_unit, iso_code)
|
|
96
|
+
expect(Monetize.parse("#{symbol}126.36M")).to eq Money.new(126_360_000 * currency.subunit_to_unit, iso_code)
|
|
97
|
+
expect(Monetize.parse("#{symbol}.45B")).to eq Money.new(450_000_000 * currency.subunit_to_unit, iso_code)
|
|
98
|
+
expect(Monetize.parse("-#{symbol}2.45B")).to eq Money.new(-2_450_000_000 * currency.subunit_to_unit, iso_code)
|
|
99
|
+
expect(Monetize.parse("#{symbol}1.65T")).to eq Money.new(1_650_000_000_000 * currency.subunit_to_unit, iso_code)
|
|
100
|
+
end
|
|
101
|
+
end
|
|
102
|
+
|
|
103
|
+
it 'parses formatted inputs with symbol and surrounding spaces' do
|
|
104
|
+
expect(Monetize.parse(" #{symbol}#{amount_in_units} ")).to eq Money.new(amount, iso_code)
|
|
105
|
+
end
|
|
106
|
+
|
|
107
|
+
it 'parses formatted inputs without currency detection when overridden' do
|
|
108
|
+
expect(Monetize.parse("#{symbol}5.95", nil, assume_from_symbol: false)).to eq Money.new(amount, 'USD')
|
|
109
|
+
end
|
|
110
|
+
end
|
|
89
111
|
end
|
|
90
112
|
|
|
91
113
|
it 'should assume default currency if not a recognised symbol' do
|
|
92
114
|
expect(Monetize.parse('L9.99')).to eq Money.new(999, 'USD')
|
|
93
115
|
end
|
|
94
116
|
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
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
|
-
end
|
|
122
|
-
|
|
123
|
-
it 'parses formatted inputs with amounts given with suffixes' do
|
|
124
|
-
expect(Monetize.parse('$1.26K')).to eq Money.new(1_260_00, 'USD')
|
|
125
|
-
expect(Monetize.parse('$126.36M')).to eq Money.new(126_360_000_00, 'USD')
|
|
126
|
-
expect(Monetize.parse('€.45B')).to eq Money.new(450_000_000_00, 'EUR')
|
|
127
|
-
expect(Monetize.parse('-$2.45B')).to eq Money.new(-2_450_000_000_00, 'USD')
|
|
128
|
-
expect(Monetize.parse('€1.65T')).to eq Money.new(1_650_000_000_000_00, 'EUR')
|
|
117
|
+
context 'negatives' do
|
|
118
|
+
it 'ignores the ambiguous kr symbol' do
|
|
119
|
+
# Could mean either of DKK, EEK, ISK, NOK, SEK
|
|
120
|
+
expect(Monetize.parse('kr9.99')).to eq Money.new(999, 'USD')
|
|
121
|
+
end
|
|
129
122
|
end
|
|
130
123
|
end
|
|
131
124
|
|
|
@@ -158,6 +151,18 @@ describe Monetize do
|
|
|
158
151
|
it 'should opt out by default' do
|
|
159
152
|
expect(Monetize.assume_from_symbol).to be_falsy
|
|
160
153
|
end
|
|
154
|
+
|
|
155
|
+
context 'ISO code' do
|
|
156
|
+
it 'parses currency given as ISO code' do
|
|
157
|
+
expect('20.00 USD'.to_money).to eq Money.new(20_00, 'USD')
|
|
158
|
+
expect('20.00 EUR'.to_money).to eq Money.new(20_00, 'EUR')
|
|
159
|
+
expect('20.00 GBP'.to_money).to eq Money.new(20_00, 'GBP')
|
|
160
|
+
end
|
|
161
|
+
|
|
162
|
+
it 'raises an error if currency code is invalid' do
|
|
163
|
+
expect { '20.00 OMG'.to_money }.to raise_error Monetize::ParseError
|
|
164
|
+
end
|
|
165
|
+
end
|
|
161
166
|
end
|
|
162
167
|
|
|
163
168
|
it 'parses USD-formatted inputs under $10' do
|
|
@@ -177,10 +182,16 @@ describe Monetize do
|
|
|
177
182
|
expect(Monetize.parse('1,111,234,567.89')).to eq Money.new(1_111_234_567_89, 'USD')
|
|
178
183
|
end
|
|
179
184
|
|
|
180
|
-
it '
|
|
181
|
-
expect
|
|
182
|
-
expect
|
|
183
|
-
expect
|
|
185
|
+
it 'parses DKK-formatted inputs' do
|
|
186
|
+
expect(Monetize.parse('kr.123,45', 'DKK')).to eq Money.new(123_45, 'DKK')
|
|
187
|
+
expect(Monetize.parse('kr.123.45', 'DKK')).to eq Money.new(123_45, 'DKK')
|
|
188
|
+
expect(Monetize.parse('kr.45k', 'DKK')).to eq Money.new(45_000_00, 'DKK')
|
|
189
|
+
end
|
|
190
|
+
|
|
191
|
+
it 'returns nil if input is a price range' do
|
|
192
|
+
expect(Monetize.parse('$5.95-10.95')).to be_nil
|
|
193
|
+
expect(Monetize.parse('$5.95 - 10.95')).to be_nil
|
|
194
|
+
expect(Monetize.parse('$5.95 - $10.95')).to be_nil
|
|
184
195
|
end
|
|
185
196
|
|
|
186
197
|
it 'does not return a price for completely invalid input' do
|
|
@@ -197,8 +208,16 @@ describe Monetize do
|
|
|
197
208
|
expect(Monetize.parse('$5.95-')).to eq five_ninety_five
|
|
198
209
|
end
|
|
199
210
|
|
|
200
|
-
it '
|
|
201
|
-
expect
|
|
211
|
+
it 'returns nil when unable to detect polarity' do
|
|
212
|
+
expect(Monetize.parse('-$5.95-')).to be_nil
|
|
213
|
+
end
|
|
214
|
+
|
|
215
|
+
it 'returns nil when more than 2 digit separators are used' do
|
|
216
|
+
expect(Monetize.parse("123.34,56'89 EUR")).to be_nil
|
|
217
|
+
end
|
|
218
|
+
|
|
219
|
+
it 'parses correctly strings with repeated digit separator' do
|
|
220
|
+
expect(Monetize.parse('19.12.89', 'EUR')).to eq Money.new(191_289_00, 'EUR')
|
|
202
221
|
end
|
|
203
222
|
|
|
204
223
|
it 'parses correctly strings with exactly 3 decimal digits' do
|
|
@@ -210,6 +229,32 @@ describe Monetize do
|
|
|
210
229
|
Monetize.enforce_currency_delimiters = false
|
|
211
230
|
end
|
|
212
231
|
|
|
232
|
+
context 'Money object attempting to be parsed' do
|
|
233
|
+
let(:money) { Money.new(595, 'GBP') }
|
|
234
|
+
|
|
235
|
+
it 'returns the original Money object' do
|
|
236
|
+
expect(Monetize.parse(money)).to eq money
|
|
237
|
+
expect(Monetize.parse(money).currency).to eq 'GBP'
|
|
238
|
+
expect(Monetize.parse(money).cents).to eq 595
|
|
239
|
+
end
|
|
240
|
+
end
|
|
241
|
+
|
|
242
|
+
context 'parsing an instance of Numeric class' do
|
|
243
|
+
let(:integer) { 10 }
|
|
244
|
+
let(:float) { 10.0 }
|
|
245
|
+
let(:big_decimal) { BigDecimal('10') }
|
|
246
|
+
|
|
247
|
+
[:integer, :float, :big_decimal].each do |type|
|
|
248
|
+
it "returns a new Money object based on the #{type} input" do
|
|
249
|
+
money = Monetize.parse(send(type), 'USD')
|
|
250
|
+
|
|
251
|
+
expect(money).to be_instance_of(Money)
|
|
252
|
+
expect(money.currency).to eq('USD')
|
|
253
|
+
expect(money.cents).to eq(10_00)
|
|
254
|
+
end
|
|
255
|
+
end
|
|
256
|
+
end
|
|
257
|
+
|
|
213
258
|
context 'custom currencies with 4 decimal places' do
|
|
214
259
|
before :each do
|
|
215
260
|
Money::Currency.register(JSON.parse(bar, symbolize_names: true))
|
|
@@ -265,6 +310,18 @@ describe Monetize do
|
|
|
265
310
|
end
|
|
266
311
|
end
|
|
267
312
|
|
|
313
|
+
describe '.parse!' do
|
|
314
|
+
it 'does not return a price if there is a price range' do
|
|
315
|
+
expect { Monetize.parse!('$5.95-10.95') }.to raise_error Monetize::ParseError
|
|
316
|
+
expect { Monetize.parse!('$5.95 - 10.95') }.to raise_error Monetize::ParseError
|
|
317
|
+
expect { Monetize.parse!('$5.95 - $10.95') }.to raise_error Monetize::ParseError
|
|
318
|
+
end
|
|
319
|
+
|
|
320
|
+
it 'raises ArgumentError when unable to detect polarity' do
|
|
321
|
+
expect { Monetize.parse!('-$5.95-') }.to raise_error Monetize::ParseError
|
|
322
|
+
end
|
|
323
|
+
end
|
|
324
|
+
|
|
268
325
|
describe '.parse_collection' do
|
|
269
326
|
it 'parses into a Money::Collection' do
|
|
270
327
|
expect(Monetize.parse_collection('$7')).to be_a Monetize::Collection
|
|
@@ -287,6 +344,10 @@ describe Monetize do
|
|
|
287
344
|
expect(collection.first).to eq Monetize.parse('$4')
|
|
288
345
|
expect(collection.last).to eq Monetize.parse('$10')
|
|
289
346
|
end
|
|
347
|
+
|
|
348
|
+
it 'raises an error if argument is invalid' do
|
|
349
|
+
expect { Monetize.parse_collection(nil) }.to raise_error Monetize::ArgumentError
|
|
350
|
+
end
|
|
290
351
|
end
|
|
291
352
|
|
|
292
353
|
describe '.from_string' do
|
|
@@ -337,6 +398,10 @@ describe Monetize do
|
|
|
337
398
|
m = Monetize.from_fixnum(1, 'EUR')
|
|
338
399
|
expect(m.currency).to eq Money::Currency.wrap('EUR')
|
|
339
400
|
end
|
|
401
|
+
|
|
402
|
+
it 'is aliased as from_integer' do
|
|
403
|
+
expect(Monetize.from_integer(1)).to eq(Monetize.from_fixnum(1))
|
|
404
|
+
end
|
|
340
405
|
end
|
|
341
406
|
|
|
342
407
|
describe '.from_float' do
|
|
@@ -366,25 +431,31 @@ describe Monetize do
|
|
|
366
431
|
|
|
367
432
|
describe '.from_bigdecimal' do
|
|
368
433
|
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
|
|
434
|
+
expect(Monetize.from_bigdecimal(BigDecimal('1'))).to eq Money.new(1_00)
|
|
435
|
+
expect(Monetize.from_bigdecimal(BigDecimal('1'))).to eq Money.new(1_00, 'USD')
|
|
436
|
+
expect(Monetize.from_bigdecimal(BigDecimal('1'), 'EUR')).to eq Money.new(1_00, 'EUR')
|
|
372
437
|
end
|
|
373
438
|
|
|
374
439
|
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
|
|
440
|
+
expect(Monetize.from_bigdecimal(BigDecimal('1'), 'USD')).to eq Money.new(1_00, 'USD')
|
|
441
|
+
expect(Monetize.from_bigdecimal(BigDecimal('1'), 'TND')).to eq Money.new(1_000, 'TND')
|
|
442
|
+
expect(Monetize.from_bigdecimal(BigDecimal('1'), 'JPY')).to eq Money.new(1, 'JPY')
|
|
443
|
+
end
|
|
444
|
+
|
|
445
|
+
it 'respects rounding mode when rounding amount to the nearest cent' do
|
|
446
|
+
amount = BigDecimal('1.005')
|
|
447
|
+
|
|
448
|
+
expect(Monetize.from_bigdecimal(amount, 'USD')).to eq Money.from_amount(amount, 'USD')
|
|
378
449
|
end
|
|
379
450
|
|
|
380
451
|
it 'accepts a currency options' do
|
|
381
|
-
m = Monetize.from_bigdecimal(BigDecimal
|
|
452
|
+
m = Monetize.from_bigdecimal(BigDecimal('1'))
|
|
382
453
|
expect(m.currency).to eq Money.default_currency
|
|
383
454
|
|
|
384
|
-
m = Monetize.from_bigdecimal(BigDecimal
|
|
455
|
+
m = Monetize.from_bigdecimal(BigDecimal('1'), Money::Currency.wrap('EUR'))
|
|
385
456
|
expect(m.currency).to eq Money::Currency.wrap('EUR')
|
|
386
457
|
|
|
387
|
-
m = Monetize.from_bigdecimal(BigDecimal
|
|
458
|
+
m = Monetize.from_bigdecimal(BigDecimal('1'), 'EUR')
|
|
388
459
|
expect(m.currency).to eq Money::Currency.wrap('EUR')
|
|
389
460
|
end
|
|
390
461
|
|
|
@@ -398,11 +469,11 @@ describe Monetize do
|
|
|
398
469
|
end
|
|
399
470
|
|
|
400
471
|
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
|
|
472
|
+
expect(Monetize.from_bigdecimal(BigDecimal('1'))).to eq Money.new(100)
|
|
473
|
+
expect(Monetize.from_bigdecimal(BigDecimal('1.23456'))).to eq Money.new(123.456)
|
|
474
|
+
expect(Monetize.from_bigdecimal(BigDecimal('-1.23456'))).to eq Money.new(-123.456)
|
|
475
|
+
expect(Monetize.from_bigdecimal(BigDecimal('1.23456'))).to eq Money.new(123.456, 'USD')
|
|
476
|
+
expect(Monetize.from_bigdecimal(BigDecimal('1.23456'), 'EUR')).to eq Money.new(123.456, 'EUR')
|
|
406
477
|
|
|
407
478
|
expect('1'.to_money).to eq Money.new(100)
|
|
408
479
|
expect('1.23456'.to_money).to eq Money.new(123.456)
|
|
@@ -417,19 +488,11 @@ describe Monetize do
|
|
|
417
488
|
it 'converts given amount to cents' do
|
|
418
489
|
expect(Monetize.from_numeric(1)).to eq Money.new(1_00)
|
|
419
490
|
expect(Monetize.from_numeric(1.0)).to eq Money.new(1_00)
|
|
420
|
-
expect(Monetize.from_numeric(BigDecimal
|
|
491
|
+
expect(Monetize.from_numeric(BigDecimal('1'))).to eq Money.new(1_00)
|
|
421
492
|
end
|
|
422
493
|
|
|
423
494
|
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')
|
|
495
|
+
expect { Monetize.from_numeric('100') }.to raise_error(Monetize::ArgumentError)
|
|
433
496
|
end
|
|
434
497
|
|
|
435
498
|
it 'respects :subunit_to_unit currency property' do
|
metadata
CHANGED
|
@@ -1,14 +1,14 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: monetize
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 1.
|
|
4
|
+
version: 1.9.1
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Shane Emmons
|
|
8
8
|
autorequire:
|
|
9
9
|
bindir: bin
|
|
10
10
|
cert_chain: []
|
|
11
|
-
date:
|
|
11
|
+
date: 2019-02-16 00:00:00.000000000 Z
|
|
12
12
|
dependencies:
|
|
13
13
|
- !ruby/object:Gem::Dependency
|
|
14
14
|
name: money
|
|
@@ -16,56 +16,56 @@ dependencies:
|
|
|
16
16
|
requirements:
|
|
17
17
|
- - "~>"
|
|
18
18
|
- !ruby/object:Gem::Version
|
|
19
|
-
version: '6.
|
|
19
|
+
version: '6.12'
|
|
20
20
|
type: :runtime
|
|
21
21
|
prerelease: false
|
|
22
22
|
version_requirements: !ruby/object:Gem::Requirement
|
|
23
23
|
requirements:
|
|
24
24
|
- - "~>"
|
|
25
25
|
- !ruby/object:Gem::Version
|
|
26
|
-
version: '6.
|
|
26
|
+
version: '6.12'
|
|
27
27
|
- !ruby/object:Gem::Dependency
|
|
28
28
|
name: bundler
|
|
29
29
|
requirement: !ruby/object:Gem::Requirement
|
|
30
30
|
requirements:
|
|
31
|
-
- - "
|
|
31
|
+
- - ">="
|
|
32
32
|
- !ruby/object:Gem::Version
|
|
33
|
-
version: '
|
|
33
|
+
version: '0'
|
|
34
34
|
type: :development
|
|
35
35
|
prerelease: false
|
|
36
36
|
version_requirements: !ruby/object:Gem::Requirement
|
|
37
37
|
requirements:
|
|
38
|
-
- - "
|
|
38
|
+
- - ">="
|
|
39
39
|
- !ruby/object:Gem::Version
|
|
40
|
-
version: '
|
|
40
|
+
version: '0'
|
|
41
41
|
- !ruby/object:Gem::Dependency
|
|
42
42
|
name: rake
|
|
43
43
|
requirement: !ruby/object:Gem::Requirement
|
|
44
44
|
requirements:
|
|
45
|
-
- - "
|
|
45
|
+
- - "~>"
|
|
46
46
|
- !ruby/object:Gem::Version
|
|
47
|
-
version: '
|
|
47
|
+
version: '10.2'
|
|
48
48
|
type: :development
|
|
49
49
|
prerelease: false
|
|
50
50
|
version_requirements: !ruby/object:Gem::Requirement
|
|
51
51
|
requirements:
|
|
52
|
-
- - "
|
|
52
|
+
- - "~>"
|
|
53
53
|
- !ruby/object:Gem::Version
|
|
54
|
-
version: '
|
|
54
|
+
version: '10.2'
|
|
55
55
|
- !ruby/object:Gem::Dependency
|
|
56
56
|
name: rspec
|
|
57
57
|
requirement: !ruby/object:Gem::Requirement
|
|
58
58
|
requirements:
|
|
59
59
|
- - "~>"
|
|
60
60
|
- !ruby/object:Gem::Version
|
|
61
|
-
version: 3.0
|
|
61
|
+
version: '3.0'
|
|
62
62
|
type: :development
|
|
63
63
|
prerelease: false
|
|
64
64
|
version_requirements: !ruby/object:Gem::Requirement
|
|
65
65
|
requirements:
|
|
66
66
|
- - "~>"
|
|
67
67
|
- !ruby/object:Gem::Version
|
|
68
|
-
version: 3.0
|
|
68
|
+
version: '3.0'
|
|
69
69
|
description: A library for converting various objects into `Money` objects.
|
|
70
70
|
email:
|
|
71
71
|
- shane@emmons.io
|
|
@@ -83,14 +83,16 @@ files:
|
|
|
83
83
|
- LICENSE.txt
|
|
84
84
|
- README.md
|
|
85
85
|
- Rakefile
|
|
86
|
-
- lib/collection.rb
|
|
87
86
|
- lib/monetize.rb
|
|
87
|
+
- lib/monetize/collection.rb
|
|
88
88
|
- lib/monetize/core_extensions.rb
|
|
89
89
|
- lib/monetize/core_extensions/hash.rb
|
|
90
90
|
- lib/monetize/core_extensions/nil_class.rb
|
|
91
91
|
- lib/monetize/core_extensions/numeric.rb
|
|
92
92
|
- lib/monetize/core_extensions/string.rb
|
|
93
93
|
- lib/monetize/core_extensions/symbol.rb
|
|
94
|
+
- lib/monetize/errors.rb
|
|
95
|
+
- lib/monetize/parser.rb
|
|
94
96
|
- lib/monetize/version.rb
|
|
95
97
|
- monetize.gemspec
|
|
96
98
|
- spec/core_extensions_spec.rb
|
|
@@ -99,7 +101,10 @@ files:
|
|
|
99
101
|
homepage: https://github.com/RubyMoney/monetize
|
|
100
102
|
licenses:
|
|
101
103
|
- MIT
|
|
102
|
-
metadata:
|
|
104
|
+
metadata:
|
|
105
|
+
changelog_uri: https://github.com/RubyMoney/monetize/blob/master/CHANGELOG.md
|
|
106
|
+
source_code_uri: https://github.com/RubyMoney/monetize/
|
|
107
|
+
bug_tracker_uri: https://github.com/RubyMoney/monetize/issues
|
|
103
108
|
post_install_message:
|
|
104
109
|
rdoc_options: []
|
|
105
110
|
require_paths:
|
|
@@ -116,7 +121,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
|
116
121
|
version: '0'
|
|
117
122
|
requirements: []
|
|
118
123
|
rubyforge_project:
|
|
119
|
-
rubygems_version: 2.
|
|
124
|
+
rubygems_version: 2.6.8
|
|
120
125
|
signing_key:
|
|
121
126
|
specification_version: 4
|
|
122
127
|
summary: A library for converting various objects into `Money` objects.
|
|
@@ -124,4 +129,3 @@ test_files:
|
|
|
124
129
|
- spec/core_extensions_spec.rb
|
|
125
130
|
- spec/monetize_spec.rb
|
|
126
131
|
- spec/spec_helper.rb
|
|
127
|
-
has_rdoc:
|