monetize 1.12.0 → 1.13.0

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 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.