validates_zipcode 0.2.4 → 0.5.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/.gitignore +1 -0
- data/.rubocop.yml +23 -43
- data/.travis.yml +9 -6
- data/Appraisals +2 -2
- data/CHANGELOG.md +34 -0
- data/README.md +69 -10
- data/gemfiles/rails_4.2.gemfile +4 -3
- data/gemfiles/rails_4.2.gemfile.lock +30 -5
- data/gemfiles/rails_5.2.gemfile +4 -3
- data/gemfiles/rails_5.2.gemfile.lock +30 -5
- data/gemfiles/rails_6.1.gemfile +8 -0
- data/gemfiles/rails_6.1.gemfile.lock +85 -0
- data/gemfiles/rails_edge.gemfile +4 -3
- data/gemfiles/rails_edge.gemfile.lock +30 -5
- data/lib/validates_zipcode.rb +12 -2
- data/lib/validates_zipcode/cldr_regex_collection.rb +94 -91
- data/lib/validates_zipcode/formatter.rb +53 -0
- data/lib/validates_zipcode/invalid_zipcode_error.rb +5 -0
- data/lib/validates_zipcode/validator.rb +8 -7
- data/lib/validates_zipcode/version.rb +1 -1
- data/lib/validates_zipcode/zipcode.rb +14 -1
- data/spec/format_zipcode_spec.rb +42 -0
- data/spec/spec_helper.rb +3566 -0
- data/spec/validates_zipcode_spec.rb +60 -353
- data/validates_zipcode.gemspec +7 -5
- metadata +47 -15
- data/gemfiles/rails_6.0.gemfile +0 -7
- data/gemfiles/rails_6.0.gemfile.lock +0 -57
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 214ffb965fb27983b44b38dbac18b42041d665e313862b10e809bf3f598c7698
|
4
|
+
data.tar.gz: bf2137923c16d09823dfb1fac10c372c2cb0f6f0fbb4c9053b7be5ee38098a2e
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 40bdbcb70fc0284c0b3f4d1ab3ad626a5f8fe37cd7ef893d5d38ff80961c30bd8736df59752cb4de8367768fa9a57b872a91af09e1376386af33c652b7629cf0
|
7
|
+
data.tar.gz: b359270f28b23d87c69f8af0646b7ad0e59140e09bd85b4fadb994b8fce94252c40a32b461f46c3d5fa6b16c806a7fd09c040ab37bbc7491b90b055ccf306edf
|
data/.gitignore
CHANGED
data/.rubocop.yml
CHANGED
@@ -5,11 +5,14 @@ inherit_from:
|
|
5
5
|
# - enabled.yml
|
6
6
|
# - disabled.yml
|
7
7
|
|
8
|
+
require:
|
9
|
+
- rubocop-rails
|
10
|
+
|
8
11
|
# Common configuration.
|
9
12
|
AllCops:
|
10
|
-
TargetRubyVersion: 2.
|
13
|
+
TargetRubyVersion: 2.4
|
11
14
|
|
12
|
-
TargetRailsVersion:
|
15
|
+
TargetRailsVersion: 5.2
|
13
16
|
|
14
17
|
# Include common Ruby source files.
|
15
18
|
Include:
|
@@ -58,7 +61,7 @@ Layout/AccessModifierIndentation:
|
|
58
61
|
- indent
|
59
62
|
|
60
63
|
# Align the elements of a hash literal if they span more than one line.
|
61
|
-
Layout/
|
64
|
+
Layout/HashAlignment:
|
62
65
|
# Alignment of entries using hash rocket as separator. Valid values are:
|
63
66
|
#
|
64
67
|
# key - left alignment of keys
|
@@ -126,7 +129,7 @@ Layout/AlignHash:
|
|
126
129
|
Style/Alias:
|
127
130
|
Enabled: false
|
128
131
|
|
129
|
-
Layout/
|
132
|
+
Layout/ParameterAlignment:
|
130
133
|
# Alignment of parameters in multi-line method calls.
|
131
134
|
#
|
132
135
|
# The `with_first_parameter` style aligns the following lines along the same
|
@@ -227,20 +230,6 @@ Style/BlockDelimiters:
|
|
227
230
|
- proc
|
228
231
|
- it
|
229
232
|
|
230
|
-
Style/BracesAroundHashParameters:
|
231
|
-
EnforcedStyle: no_braces
|
232
|
-
SupportedStyles:
|
233
|
-
# The `braces` style enforces braces around all method parameters that are
|
234
|
-
# hashes.
|
235
|
-
- braces
|
236
|
-
# The `no_braces` style checks that the last parameter doesn't have braces
|
237
|
-
# around it.
|
238
|
-
- no_braces
|
239
|
-
# The `context_dependent` style checks that the last parameter doesn't have
|
240
|
-
# braces around it, but requires braces if the second to last parameter is
|
241
|
-
# also a hash literal.
|
242
|
-
- context_dependent
|
243
|
-
|
244
233
|
# Indentation of `when`.
|
245
234
|
Layout/CaseIndentation:
|
246
235
|
SupportedStyles:
|
@@ -387,25 +376,18 @@ Layout/EmptyLinesAroundModuleBody:
|
|
387
376
|
- empty_lines
|
388
377
|
- no_empty_lines
|
389
378
|
|
390
|
-
|
379
|
+
Naming/FileName:
|
391
380
|
# File names listed in AllCops:Include are excluded by default. Add extra
|
392
381
|
# excludes here.
|
393
382
|
Exclude: []
|
394
383
|
|
395
384
|
Layout/FirstParameterIndentation:
|
396
|
-
EnforcedStyle:
|
385
|
+
EnforcedStyle: consistent
|
397
386
|
SupportedStyles:
|
398
387
|
# The first parameter should always be indented one step more than the
|
399
388
|
# preceding line.
|
400
389
|
- consistent
|
401
|
-
|
402
|
-
# preceding line, but if it's a parameter for a method call that is itself
|
403
|
-
# a parameter in a method call, then the inner parameter should be indented
|
404
|
-
# relative to the inner method.
|
405
|
-
- special_for_inner_method_call
|
406
|
-
# Same as special_for_inner_method_call except that the special rule only
|
407
|
-
# applies if the outer method call encloses its arguments in parentheses.
|
408
|
-
- special_for_inner_method_call_in_parentheses
|
390
|
+
- align_parentheses
|
409
391
|
|
410
392
|
Style/EmptyMethod:
|
411
393
|
EnforcedStyle: expanded
|
@@ -439,11 +421,9 @@ Style/FormatStringToken:
|
|
439
421
|
- template
|
440
422
|
|
441
423
|
Style/FrozenStringLiteralComment:
|
442
|
-
EnforcedStyle:
|
424
|
+
EnforcedStyle: always
|
443
425
|
SupportedStyles:
|
444
|
-
|
445
|
-
# only when the `TargetRubyVersion` is set to 2.3+.
|
446
|
-
- when_needed
|
426
|
+
- always_true
|
447
427
|
# `always` will always add the frozen string literal comment to a file
|
448
428
|
# regardless of the Ruby version or if `freeze` or `<<` are called on a
|
449
429
|
# string literal. If you run code against multiple versions of Ruby, it is
|
@@ -483,7 +463,7 @@ Layout/IndentationWidth:
|
|
483
463
|
Width: 2
|
484
464
|
|
485
465
|
# Checks the indentation of the first key in a hash literal.
|
486
|
-
Layout/
|
466
|
+
Layout/FirstHashElementIndentation:
|
487
467
|
# The value `special_inside_parentheses` means that hash literals with braces
|
488
468
|
# that have their opening brace on the same line as a surrounding opening
|
489
469
|
# round parenthesis, shall have their first key indented relative to the
|
@@ -532,7 +512,7 @@ Style/MethodDefParentheses:
|
|
532
512
|
- require_parentheses
|
533
513
|
- require_no_parentheses
|
534
514
|
|
535
|
-
|
515
|
+
Naming/MethodName:
|
536
516
|
EnforcedStyle: snake_case
|
537
517
|
SupportedStyles:
|
538
518
|
- snake_case
|
@@ -569,14 +549,14 @@ Style/PercentQLiterals:
|
|
569
549
|
- lower_case_q # Use %q when possible, %Q when necessary
|
570
550
|
- upper_case_q # Always use %Q
|
571
551
|
|
572
|
-
|
552
|
+
Naming/PredicateName:
|
573
553
|
# Predicate name prefices.
|
574
554
|
NamePrefix:
|
575
555
|
- is_
|
576
556
|
- has_
|
577
557
|
- have_
|
578
558
|
# Predicate name prefices that should be removed.
|
579
|
-
|
559
|
+
ForbiddenPrefixes:
|
580
560
|
- is_
|
581
561
|
- has_
|
582
562
|
- have_
|
@@ -694,7 +674,7 @@ Style/SymbolProc:
|
|
694
674
|
IgnoredMethods:
|
695
675
|
- respond_to
|
696
676
|
|
697
|
-
Layout/
|
677
|
+
Layout/TrailingEmptyLines:
|
698
678
|
EnforcedStyle: final_newline
|
699
679
|
SupportedStyles:
|
700
680
|
- final_newline
|
@@ -756,7 +736,7 @@ Style/TrivialAccessors:
|
|
756
736
|
# Commonly used in DSLs
|
757
737
|
AllowDSLWriters: false
|
758
738
|
IgnoreClassMethods: false
|
759
|
-
|
739
|
+
AllowedMethods:
|
760
740
|
- to_ary
|
761
741
|
- to_a
|
762
742
|
- to_c
|
@@ -775,7 +755,7 @@ Style/TrivialAccessors:
|
|
775
755
|
- to_s
|
776
756
|
- to_sym
|
777
757
|
|
778
|
-
|
758
|
+
Naming/VariableName:
|
779
759
|
EnforcedStyle: snake_case
|
780
760
|
SupportedStyles:
|
781
761
|
- snake_case
|
@@ -825,7 +805,7 @@ Metrics/CyclomaticComplexity:
|
|
825
805
|
Max: 6
|
826
806
|
|
827
807
|
# Second-wave metric. Disabled now to prevent noise.
|
828
|
-
|
808
|
+
Layout/LineLength:
|
829
809
|
Enabled: false
|
830
810
|
Max: 120
|
831
811
|
# To make it possible to copy or click on URIs in the code, we allow lines
|
@@ -856,7 +836,7 @@ Lint/AssignmentInCondition:
|
|
856
836
|
AllowSafeAssignment: true
|
857
837
|
|
858
838
|
# Align ends correctly.
|
859
|
-
|
839
|
+
Layout/EndAlignment:
|
860
840
|
# The value `keyword` means that `end` should be aligned with the matching
|
861
841
|
# keyword (if, while, etc.).
|
862
842
|
# The value `variable` means that in assignments, `end` should be aligned
|
@@ -869,7 +849,7 @@ Lint/EndAlignment:
|
|
869
849
|
- start_of_line
|
870
850
|
AutoCorrect: false
|
871
851
|
|
872
|
-
|
852
|
+
Layout/DefEndAlignment:
|
873
853
|
# The value `def` means that `end` should be aligned with the def keyword.
|
874
854
|
# The value `start_of_line` means that `end` should be aligned with method
|
875
855
|
# calls like `private`, `public`, etc, if present in front of the `def`
|
@@ -882,7 +862,7 @@ Lint/DefEndAlignment:
|
|
882
862
|
|
883
863
|
##################### Rails ##################################
|
884
864
|
Rails:
|
885
|
-
Enabled:
|
865
|
+
Enabled: false
|
886
866
|
|
887
867
|
Rails/ActionFilter:
|
888
868
|
EnforcedStyle: action
|
data/.travis.yml
CHANGED
@@ -7,23 +7,24 @@ cache: bundler
|
|
7
7
|
bundler_args: --jobs 3 --retry 3
|
8
8
|
|
9
9
|
before_install:
|
10
|
-
-
|
11
|
-
-
|
10
|
+
- 'travis_retry gem update --system'
|
11
|
+
- 'travis_retry gem install bundler'
|
12
12
|
|
13
|
-
script:
|
13
|
+
script: 'bundle exec rake'
|
14
14
|
|
15
15
|
rvm:
|
16
16
|
- 2.4.9
|
17
17
|
- 2.5.7
|
18
18
|
- 2.6.5
|
19
|
-
- 2.7.
|
19
|
+
- 2.7.2
|
20
|
+
- 3.0.0
|
20
21
|
- ruby-head
|
21
22
|
- truffleruby
|
22
23
|
|
23
24
|
gemfile:
|
24
25
|
- gemfiles/rails_4.2.gemfile
|
25
26
|
- gemfiles/rails_5.2.gemfile
|
26
|
-
- gemfiles/rails_6.
|
27
|
+
- gemfiles/rails_6.1.gemfile
|
27
28
|
- gemfiles/rails_edge.gemfile
|
28
29
|
|
29
30
|
matrix:
|
@@ -32,6 +33,8 @@ matrix:
|
|
32
33
|
- rvm: ruby-head
|
33
34
|
exclude:
|
34
35
|
- rvm: 2.4.9
|
35
|
-
gemfile: gemfiles/rails_6.gemfile
|
36
|
+
gemfile: gemfiles/rails_6.1.gemfile
|
36
37
|
- rvm: 2.4.9
|
37
38
|
gemfile: gemfiles/rails_edge.gemfile
|
39
|
+
- rvm: 3.0.0
|
40
|
+
gemfile: gemfiles/rails_edge.gemfile
|
data/Appraisals
CHANGED
data/CHANGELOG.md
CHANGED
@@ -2,6 +2,40 @@
|
|
2
2
|
|
3
3
|
All notable changes to this project will be documented in this file.
|
4
4
|
|
5
|
+
## 0.5.0
|
6
|
+
|
7
|
+
- Pass `format: true` option to the `validates_zipcode` or the constructor to attempt for format a given code using the (uncomplete) list of formatters by country. This process will happen before the validation takes place, dealing with case sensitive formats and whitespace, amongst others. This option is false by default for backwards compatibility. ~ @chloe-meister
|
8
|
+
- Fixed unconsistent behaviour by which the `ValidatesZipcode.format` and `ValidatesZipcode::Formatter.new(..).format` where formatting an invalid code, the former, and raising a validity error, the latter. ~ @chloe-meister
|
9
|
+
|
10
|
+
## 0.4.0
|
11
|
+
|
12
|
+
**HEADS UP**: While I don't think this update would break any system for anyone, many formats where revisited to make them more loose or strict, potentially rendering your data invalid, particularly in edge cases or with data that was already incorrect in the first place and not flagges as such. I recommend you check your data with the new version in case these validations are critical to you. I am not releasing this as a 1.0 because there are no breaking changes in the API though. See PR #54 for more details.
|
13
|
+
|
14
|
+
- Many regexps where modified to include recent changes in postal services, fix issues, narrow down formats that where unnecessary broad, allow for some looser cases such as optional whitespaces and making many of them case insensitive and removing countries with no postal codes - See PR #54. Thanks to ~ @ojsdude with some help from @dgilperez
|
15
|
+
- Allow the gem to generate test data from the formatters. See README ~ @ojsdude
|
16
|
+
- Officially support Ruby 3 and Rails 6.1
|
17
|
+
|
18
|
+
## 0.3.3
|
19
|
+
|
20
|
+
- Fixes AR regex
|
21
|
+
|
22
|
+
## 0.3.2
|
23
|
+
|
24
|
+
- Adds formatter for CA ~ @devthiago
|
25
|
+
|
26
|
+
## 0.3.1
|
27
|
+
|
28
|
+
- Removes forbidden combinations in the Netherlands ~ @gerard76
|
29
|
+
|
30
|
+
## 0.3.0
|
31
|
+
|
32
|
+
- Adds zipcode formatting feature ~ @aiomaster
|
33
|
+
- Drops support for EoL Rails < 4.2 and Ruby < 2.4. ~ @dgilperez
|
34
|
+
|
35
|
+
## 0.2.5
|
36
|
+
|
37
|
+
- Allows for a custom message passed to the validator ~ @andychongyz
|
38
|
+
|
5
39
|
## 0.2.4
|
6
40
|
|
7
41
|
- Fixes LV postal code format
|
data/README.md
CHANGED
@@ -4,13 +4,14 @@ Adds zipcode / postal code validation support to Rails (ActiveModel), considerin
|
|
4
4
|
|
5
5
|
``ValidatesZipcode`` currently support **233 country codes**. Regex data taken from several sources, being the main source the [CLDR](http://unicode.org/cldr/trac/browser/tags/release-27-d05/common/supplemental/postalCodeData.xml) database (release 27, around 159). Any other country's postal code will validate without errors.
|
6
6
|
|
7
|
-
``ValidatesZipcode`` supports Rails >= 4.2 and Ruby >= 2.
|
7
|
+
``ValidatesZipcode`` supports Rails >= 4.2 and Ruby >= 2.4. This gem could work in Rails 3.2 and Ruby 1.9.3 as well, yet unsupported; try v0.2 series if having trouble with later versions. Truffleruby is also tested, but no reports of working in production apps for now.
|
8
8
|
|
9
9
|
## Installation
|
10
10
|
|
11
11
|
Add this line to your application's Gemfile:
|
12
|
-
|
13
|
-
|
12
|
+
```ruby
|
13
|
+
gem 'validates_zipcode'
|
14
|
+
```
|
14
15
|
|
15
16
|
And then execute:
|
16
17
|
|
@@ -22,23 +23,81 @@ Or install it yourself as:
|
|
22
23
|
|
23
24
|
## Usage
|
24
25
|
|
25
|
-
|
26
|
+
### With ActiveModel::Validations
|
27
|
+
|
28
|
+
```ruby
|
29
|
+
validates_zipcode :zipcode
|
26
30
|
|
27
|
-
|
31
|
+
validates :zipcode, zipcode: true
|
32
|
+
```
|
28
33
|
|
29
34
|
``ValidatesZipcode`` expects the model to have an attribute called ``country_alpha2`` to contain the country code.
|
30
35
|
You can provide your own country_code using ``:country_code`` option, or specify which attribute contains this information
|
31
36
|
using ``:country_code_attribute`` option.
|
32
37
|
|
33
|
-
|
38
|
+
```ruby
|
39
|
+
validates :zipcode, zipcode: { country_code: :es }
|
40
|
+
|
41
|
+
validates :zipcode, zipcode: { country_code_attribute: :my_country_code_column }
|
42
|
+
```
|
34
43
|
|
35
|
-
|
44
|
+
#### Error Messaging
|
36
45
|
|
37
46
|
If you need to localize the error message, just add this to your I18n locale file:
|
38
47
|
|
39
|
-
|
40
|
-
|
41
|
-
|
48
|
+
```yaml
|
49
|
+
errors:
|
50
|
+
messages:
|
51
|
+
invalid_zipcode: Your zipcode error message.
|
52
|
+
```
|
53
|
+
|
54
|
+
You can override this on a per-model basis by passing in a ``:message`` key with the validation:
|
55
|
+
|
56
|
+
```ruby
|
57
|
+
validates :zipcode, zipcode: { message: 'Your per-model zipcode error message.' }
|
58
|
+
```
|
59
|
+
|
60
|
+
### Without ActiveModel::Validations
|
61
|
+
|
62
|
+
```ruby
|
63
|
+
ValidatesZipcode.valid?('93108', 'ES')
|
64
|
+
# => true
|
65
|
+
```
|
66
|
+
|
67
|
+
### Formatting
|
68
|
+
|
69
|
+
This gem can also be used for formatting zipcodes according to country specific rules.
|
70
|
+
|
71
|
+
```ruby
|
72
|
+
ValidatesZipcode.format('Sw1A 2aA', 'UK')
|
73
|
+
# => 'SW1A 2AA'
|
74
|
+
```
|
75
|
+
|
76
|
+
If the zipcode is not valid an exception is raised.
|
77
|
+
|
78
|
+
```ruby
|
79
|
+
ValidatesZipcode.format('Sw1A 2aA', 'FR')
|
80
|
+
# => raises ValidatesZipcode::InvalidZipcodeError
|
81
|
+
```
|
82
|
+
|
83
|
+
At the moment not every country is supported. See [lib/validates_zipcode/formatter.rb](lib/validates_zipcode/formatter.rb) to find all available countries.
|
84
|
+
|
85
|
+
### Test data
|
86
|
+
|
87
|
+
In order to generate test data, we suggest using the [regexp-examples gem](https://github.com/tom-lord/regexp-examples)
|
88
|
+
This dependency will allow you to create examples as follows:
|
89
|
+
|
90
|
+
```ruby
|
91
|
+
require "regexp-examples"
|
92
|
+
|
93
|
+
ValidatesZipcode::CldrRegexpCollection::ZIPCODES_REGEX[:ES].examples
|
94
|
+
|
95
|
+
# => ["00000", "00001", "00002", "00003", ..., "44443", "44444"]
|
96
|
+
|
97
|
+
ValidatesZipcode::CldrRegexpCollection::ZIPCODES_REGEX[:ES].random_example
|
98
|
+
|
99
|
+
# => "27072"
|
100
|
+
```
|
42
101
|
|
43
102
|
## Contributing
|
44
103
|
|
data/gemfiles/rails_4.2.gemfile
CHANGED
@@ -1,8 +1,8 @@
|
|
1
1
|
PATH
|
2
2
|
remote: ..
|
3
3
|
specs:
|
4
|
-
validates_zipcode (0.
|
5
|
-
activemodel (>=
|
4
|
+
validates_zipcode (0.5.0)
|
5
|
+
activemodel (>= 4.2.0)
|
6
6
|
|
7
7
|
GEM
|
8
8
|
remote: https://rubygems.org/
|
@@ -19,13 +19,20 @@ GEM
|
|
19
19
|
bundler
|
20
20
|
rake
|
21
21
|
thor (>= 0.14.0)
|
22
|
+
ast (2.4.0)
|
22
23
|
builder (3.2.3)
|
23
24
|
concurrent-ruby (1.1.4)
|
24
25
|
diff-lcs (1.3)
|
25
26
|
i18n (0.9.5)
|
26
27
|
concurrent-ruby (~> 1.0)
|
27
28
|
minitest (5.11.3)
|
28
|
-
|
29
|
+
parallel (1.19.1)
|
30
|
+
parser (2.7.1.2)
|
31
|
+
ast (~> 2.4.0)
|
32
|
+
rack (2.2.2)
|
33
|
+
rainbow (3.0.0)
|
34
|
+
rake (13.0.1)
|
35
|
+
rexml (3.2.4)
|
29
36
|
rspec (3.8.0)
|
30
37
|
rspec-core (~> 3.8.0)
|
31
38
|
rspec-expectations (~> 3.8.0)
|
@@ -39,10 +46,26 @@ GEM
|
|
39
46
|
diff-lcs (>= 1.2.0, < 2.0)
|
40
47
|
rspec-support (~> 3.8.0)
|
41
48
|
rspec-support (3.8.0)
|
49
|
+
rubocop (0.84.0)
|
50
|
+
parallel (~> 1.10)
|
51
|
+
parser (>= 2.7.0.1)
|
52
|
+
rainbow (>= 2.2.2, < 4.0)
|
53
|
+
rexml
|
54
|
+
rubocop-ast (>= 0.0.3)
|
55
|
+
ruby-progressbar (~> 1.7)
|
56
|
+
unicode-display_width (>= 1.4.0, < 2.0)
|
57
|
+
rubocop-ast (0.0.3)
|
58
|
+
parser (>= 2.7.0.1)
|
59
|
+
rubocop-rails (2.5.2)
|
60
|
+
activesupport
|
61
|
+
rack (>= 1.1)
|
62
|
+
rubocop (>= 0.72.0)
|
63
|
+
ruby-progressbar (1.10.1)
|
42
64
|
thor (0.20.3)
|
43
65
|
thread_safe (0.3.6)
|
44
66
|
tzinfo (1.2.5)
|
45
67
|
thread_safe (~> 0.1)
|
68
|
+
unicode-display_width (1.7.0)
|
46
69
|
|
47
70
|
PLATFORMS
|
48
71
|
ruby
|
@@ -51,9 +74,11 @@ DEPENDENCIES
|
|
51
74
|
activemodel (~> 4.2.0)
|
52
75
|
appraisal
|
53
76
|
bundler
|
54
|
-
rake (
|
77
|
+
rake (>= 12.3.3)
|
55
78
|
rspec
|
79
|
+
rubocop
|
80
|
+
rubocop-rails
|
56
81
|
validates_zipcode!
|
57
82
|
|
58
83
|
BUNDLED WITH
|
59
|
-
2.
|
84
|
+
2.1.4
|