monetize 1.12.0 → 1.13.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: fec107ece48dc97d0e602c4176dc4bee8e9dd7be4172c0a507df4efa941ddc12
4
- data.tar.gz: d15c4dd9bfc270948d69e6ffb9483baa9cde97743111d0fb68b3f6f1db8d6c9d
3
+ metadata.gz: 07adb2e223118b56f5944a6b90b8c51901df761f6b5ecebea8fd8ee8dea3343e
4
+ data.tar.gz: a61b3fda9d0c22fefb028043db74625c91083787068b31ecf639a05286f92cff
5
5
  SHA512:
6
- metadata.gz: 2bae292db1e6349b333248bab902017130ee85741e0321e8429216c83ca17c79003f36355eaced3e21fa8e9969df5a29f8fe538fe79ecef7f951b882f8cafe10
7
- data.tar.gz: f5cc392537c1d8487c3117c680f9e49d0d38ec0db67fcda73594a4a7ce0dfddd8224338eaf5e97da880a86a5e31b4181fb287b9dd8b079f47ec104fd0edbfd49
6
+ metadata.gz: 6d4774d81cc075b5c68849cd07c69f2311683f16adfbb00075d85e7530f4a1cb052a82a1b772b812b9b029fbe47b8b4400aabb0588ce27a35ef6e9d6340122cb
7
+ data.tar.gz: c5e7266b20bf556a2619974f8d97a690e6817eec1a56e9cd0497c6569bc36c681fc417c19900fa95c1ae7fcf7c10569867bec072d4a25e8c99d6f49c0b9901e1
@@ -19,10 +19,10 @@ jobs:
19
19
  runs-on: ubuntu-latest
20
20
  strategy:
21
21
  matrix:
22
- ruby-version: ['2.6', '2.7', '3.0']
22
+ ruby-version: ['2.6', '2.7', '3.0', '3.1', '3.2']
23
23
 
24
24
  steps:
25
- - uses: actions/checkout@v2
25
+ - uses: actions/checkout@v3
26
26
  - name: Set up Ruby
27
27
  # To automatically get bug fixes and new Ruby versions for ruby/setup-ruby,
28
28
  # change this to (see https://github.com/ruby/setup-ruby#versioning):
data/LICENSE CHANGED
@@ -1,6 +1,6 @@
1
1
  MIT License
2
2
 
3
- Copyright (c) 2021 Shane Emmons
3
+ Copyright (c) 2024 Shane Emmons
4
4
 
5
5
  Permission is hereby granted, free of charge, to any person obtaining a copy
6
6
  of this software and associated documentation files (the "Software"), to deal
data/README.md CHANGED
@@ -10,13 +10,9 @@ A library for converting various objects into `Money` objects.
10
10
 
11
11
  ## Installation
12
12
 
13
- Add this line to your application's Gemfile:
13
+ Run:
14
14
 
15
- gem 'monetize'
16
-
17
- And then execute:
18
-
19
- $ bundle
15
+ bundle add monetize
20
16
 
21
17
  Or install it yourself as:
22
18
 
@@ -53,6 +49,22 @@ Monetize.parse("£100") == Money.new(100_00, "GBP")
53
49
  "€100".to_money == Money.new(100_00, "EUR")
54
50
  ```
55
51
 
52
+ Parsing can be improved where the input is not expected to contain fractonal subunits.
53
+ To do this, set `Monetize.expect_whole_subunits = true`
54
+
55
+ ```ruby
56
+ Monetize.parse('EUR 10,000') == Money.new(100_00, "EUR")
57
+
58
+ Monetize.expect_whole_subunits = true
59
+ Monetize.parse('EUR 10,000') == Money.new(10_000_00, "EUR")
60
+ ```
61
+
62
+ Why does this work? If we expect fractional subunits then the parser will treat a single
63
+ delimiter as a decimal marker if it matches the currency's decimal marker. But often
64
+ this is not the case - a European site will show $10.000 because that's the local format.
65
+ As a human, if this was a stock ticker we might expect fractional cents. If it's a retail price we know it's actually an incorrect thousands separator.
66
+
67
+
56
68
  Monetize can also parse a list of values, returning an array-like object ([Monetize::Collection](lib/collection.rb)):
57
69
 
58
70
  ```ruby
@@ -8,6 +8,8 @@ module Monetize
8
8
  '£' => 'GBP',
9
9
  '₤' => 'GBP',
10
10
  'R$' => 'BRL',
11
+ 'RM' => 'MYR',
12
+ 'Rp' => 'IDR',
11
13
  'R' => 'ZAR',
12
14
  '¥' => 'JPY',
13
15
  'C$' => 'CAD',
@@ -23,6 +25,10 @@ module Monetize
23
25
  'zł' => 'PLN',
24
26
  '₸' => 'KZT',
25
27
  "₩" => 'KRW',
28
+ 'S$' => 'SGD',
29
+ 'HK$'=> 'HKD',
30
+ 'NT$'=> 'TWD',
31
+ '₱' => 'PHP',
26
32
  }
27
33
 
28
34
  MULTIPLIER_SUFFIXES = { 'K' => 3, 'M' => 6, 'B' => 9, 'T' => 12 }
@@ -70,6 +76,7 @@ module Monetize
70
76
  def parse_currency
71
77
  computed_currency = nil
72
78
  computed_currency = input[/[A-Z]{2,3}/]
79
+ computed_currency = nil unless Monetize::Parser::CURRENCY_SYMBOLS.value?(computed_currency)
73
80
  computed_currency ||= compute_currency if assume_from_symbol?
74
81
 
75
82
 
@@ -80,6 +87,10 @@ module Monetize
80
87
  options.fetch(:assume_from_symbol) { Monetize.assume_from_symbol }
81
88
  end
82
89
 
90
+ def expect_whole_subunits?
91
+ options.fetch(:expect_whole_subunits) { Monetize.expect_whole_subunits }
92
+ end
93
+
83
94
  def apply_multiplier(multiplier_exp, amount)
84
95
  amount * 10**multiplier_exp
85
96
  end
@@ -109,13 +120,26 @@ module Monetize
109
120
  end
110
121
  end
111
122
 
123
+ def minor_has_correct_dp_for_currency_subunit?(minor, currency)
124
+ minor.length == currency.subunit_to_unit.to_s.length - 1
125
+ end
126
+
112
127
  def extract_major_minor_with_single_delimiter(num, currency, delimiter)
113
- if delimiter == currency.decimal_mark
114
- split_major_minor(num, delimiter)
115
- elsif Monetize.enforce_currency_delimiters && delimiter == currency.thousands_separator
116
- [num.gsub(delimiter, ''), 0]
128
+ if expect_whole_subunits?
129
+ possible_major, possible_minor = split_major_minor(num, delimiter)
130
+ if minor_has_correct_dp_for_currency_subunit?(possible_minor, currency)
131
+ split_major_minor(num, delimiter)
132
+ else
133
+ extract_major_minor_with_tentative_delimiter(num, delimiter)
134
+ end
117
135
  else
118
- extract_major_minor_with_tentative_delimiter(num, delimiter)
136
+ if delimiter == currency.decimal_mark
137
+ split_major_minor(num, delimiter)
138
+ elsif Monetize.enforce_currency_delimiters && delimiter == currency.thousands_separator
139
+ [num.gsub(delimiter, ''), 0]
140
+ else
141
+ extract_major_minor_with_tentative_delimiter(num, delimiter)
142
+ end
119
143
  end
120
144
  end
121
145
 
@@ -130,7 +154,7 @@ module Monetize
130
154
  is_decimal_mark = possible_minor.length != 3 ||
131
155
  possible_major.length > 3 ||
132
156
  possible_major.to_i == 0 ||
133
- delimiter == '.'
157
+ (!expect_whole_subunits? && delimiter == '.')
134
158
 
135
159
  if is_decimal_mark
136
160
  [possible_major, possible_minor]
@@ -1,5 +1,5 @@
1
1
  # encoding: utf-8
2
2
 
3
3
  module Monetize
4
- VERSION = '1.12.0'
4
+ VERSION = '1.13.0'
5
5
  end
data/lib/monetize.rb CHANGED
@@ -20,6 +20,12 @@ module Monetize
20
20
  # to true to enforce the delimiters set in the currency all the time.
21
21
  attr_accessor :enforce_currency_delimiters
22
22
 
23
+
24
+ # Where this set to true, the behavior for parsing thousands separators is changed to
25
+ # expect that eg. €10.000 is EUR 10 000 and not EUR 10.000 - it's incredibly rare when parsing
26
+ # human text that we're dealing with fractions of cents.
27
+ attr_accessor :expect_whole_subunits
28
+
23
29
  def parse(input, currency = Money.default_currency, options = {})
24
30
  parse! input, currency, options
25
31
  rescue Error
@@ -172,8 +172,28 @@ describe Monetize do
172
172
  expect('20.00 GBP'.to_money).to eq Money.new(20_00, 'GBP')
173
173
  end
174
174
 
175
- it 'raises an error if currency code is invalid' do
176
- expect { '20.00 OMG'.to_money }.to raise_error Monetize::ParseError
175
+ context 'with default currency' do
176
+ before do
177
+ Money.default_currency = Money::Currency.new('USD')
178
+ end
179
+
180
+ it 'parses currency given using default currency' do
181
+ expect('20.00 OMG'.to_money).to eq Money.new(20_00, 'USD')
182
+ end
183
+ end
184
+
185
+ context 'without default currency' do
186
+ before do
187
+ Money.default_currency = nil
188
+ end
189
+
190
+ after do
191
+ Money.default_currency = Money::Currency.new('USD')
192
+ end
193
+
194
+ it 'raises an error if currency code is invalid' do
195
+ expect { '20.00 OMG'.to_money }.to raise_error
196
+ end
177
197
  end
178
198
  end
179
199
  end
@@ -344,6 +364,32 @@ describe Monetize do
344
364
  expect('€0.5324'.to_money('EU4')).to eq Money.new(5324, 'EU4')
345
365
  end
346
366
  end
367
+
368
+ describe "expecting whole subunits" do
369
+ before(:all) do
370
+ Monetize.expect_whole_subunits = true
371
+ Monetize.assume_from_symbol = true
372
+ end
373
+
374
+ after(:all) do
375
+ Monetize.expect_whole_subunits = false
376
+ Monetize.assume_from_symbol = false
377
+ end
378
+
379
+ it "handles euros" do
380
+ expect(Monetize.parse('€10,000')).to eq Money.new(10_000_00, 'EUR')
381
+ expect(Monetize.parse('€10,00')).to eq Money.new(10_00, 'EUR')
382
+ expect(Monetize.parse('€10.00')).to eq Money.new(10_00, 'EUR')
383
+ expect(Monetize.parse('EUR 10,000.00')).to eq Money.new(10_000_00, 'EUR')
384
+ end
385
+
386
+ it "handles GBP" do
387
+ expect(Monetize.parse('£10,000')).to eq Money.new(10_000_00, 'GBP')
388
+ expect(Monetize.parse('£10.000')).to eq Money.new(10_000_00, 'GBP')
389
+ expect(Monetize.parse('£10,00')).to eq Money.new(10_00, 'GBP')
390
+ expect(Monetize.parse('£10.00')).to eq Money.new(10_00, 'GBP')
391
+ end
392
+ end
347
393
  end
348
394
 
349
395
  describe '.parse!' do
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: monetize
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.12.0
4
+ version: 1.13.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Shane Emmons
@@ -9,7 +9,7 @@ authors:
9
9
  autorequire:
10
10
  bindir: bin
11
11
  cert_chain: []
12
- date: 2022-01-07 00:00:00.000000000 Z
12
+ date: 2024-01-29 00:00:00.000000000 Z
13
13
  dependencies:
14
14
  - !ruby/object:Gem::Dependency
15
15
  name: money
@@ -121,7 +121,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
121
121
  - !ruby/object:Gem::Version
122
122
  version: '0'
123
123
  requirements: []
124
- rubygems_version: 3.2.22
124
+ rubygems_version: 3.5.3
125
125
  signing_key:
126
126
  specification_version: 4
127
127
  summary: A library for converting various objects into `Money` objects.