addressing 0.1.1 → 0.3.1
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/CHANGELOG.md +15 -0
- data/README.md +18 -2
- data/data/country/zh.json +1 -1
- data/lib/addressing/address_format.rb +21 -0
- data/lib/addressing/field_override.rb +33 -0
- data/lib/addressing/lazy_subdivisions.rb +5 -0
- data/lib/addressing/model.rb +110 -0
- data/lib/addressing/version.rb +1 -1
- data/lib/addressing.rb +8 -0
- metadata +4 -3
- data/lib/addressing/address_format_validator.rb +0 -0
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 70ce82947444306a38c71f41034dbc93a842d06125f66d2288ab212b663af42c
|
4
|
+
data.tar.gz: 69a1dc6eb190e4d1dca977df29cf3d6c3fe8b2c87334d8a919e1f7022f57bdb2
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 6e8be1a338edaa1a0f39ac942b04786a7c0e88937c148c48ffe851b52b79dfcebb235e6a31800c1ad0c862b26be8ac255469bf3682fdb73e86b11f9d898e18f4
|
7
|
+
data.tar.gz: c4473163b7243a66c8846e77d6f6d267af3c3a33f3b483aa2ef2098d3dd8942885dcb7044e0bc15f2a3a889b79453ea658ad8cf3ea503c54770aa1725ced5315
|
data/CHANGELOG.md
CHANGED
@@ -2,6 +2,21 @@
|
|
2
2
|
|
3
3
|
All notable changes to `addressing` will be documented in this file.
|
4
4
|
|
5
|
+
## 0.3.1 (04-04-2022)
|
6
|
+
|
7
|
+
- Break with both subdivisions and parents when subdivision field is empty
|
8
|
+
- Only retrieve field if it exists on model
|
9
|
+
|
10
|
+
## 0.3.0 (04-04-2022)
|
11
|
+
|
12
|
+
- Allow field validation to be overridden
|
13
|
+
- Only verify address when one of the field changes
|
14
|
+
- Allow used address fields to be configured
|
15
|
+
|
16
|
+
## 0.2.0 (25-02-2022)
|
17
|
+
|
18
|
+
- Add ActiveRecord validations
|
19
|
+
|
5
20
|
## 0.1.1 (21-02-2022)
|
6
21
|
|
7
22
|
- Fix typo in DE custom format
|
data/README.md
CHANGED
@@ -172,6 +172,24 @@ p formatter.format(address, origin_country: "FR")
|
|
172
172
|
# ÉTATS-UNIS - UNITED STATES
|
173
173
|
```
|
174
174
|
|
175
|
+
### Validating addresses
|
176
|
+
|
177
|
+
For Active Record models, use:
|
178
|
+
|
179
|
+
```rb
|
180
|
+
class User < ApplicationRecord
|
181
|
+
validates_address_format
|
182
|
+
end
|
183
|
+
```
|
184
|
+
|
185
|
+
For performance, the address is only verified if at least one of the fields changes. Set your own condition with:
|
186
|
+
|
187
|
+
```rb
|
188
|
+
class User < ApplicationRecord
|
189
|
+
validates_address if: -> { something_changed? }, ...
|
190
|
+
end
|
191
|
+
```
|
192
|
+
|
175
193
|
## Changelog
|
176
194
|
|
177
195
|
Please see [CHANGELOG](CHANGELOG.md) for more information on what has changed recently.
|
@@ -203,5 +221,3 @@ Feel free to open an issue to get feedback on your idea before spending too much
|
|
203
221
|
## License
|
204
222
|
|
205
223
|
The MIT License (MIT). Please see [License File](LICENSE.md) for more information.
|
206
|
-
|
207
|
-
https://github.com/countries/countries/blob/eddb4af3889e5c8b66d897f9789d43c4a1ee0598/spec/thread_safety_spec.rb
|
data/data/country/zh.json
CHANGED
@@ -35,7 +35,6 @@
|
|
35
35
|
"MK": "北马其顿",
|
36
36
|
"BJ": "贝宁",
|
37
37
|
"BE": "比利时",
|
38
|
-
"PE": "秘鲁",
|
39
38
|
"IS": "冰岛",
|
40
39
|
"PR": "波多黎各",
|
41
40
|
"PL": "波兰",
|
@@ -149,6 +148,7 @@
|
|
149
148
|
"MN": "蒙古",
|
150
149
|
"MS": "蒙特塞拉特",
|
151
150
|
"BD": "孟加拉国",
|
151
|
+
"PE": "秘鲁",
|
152
152
|
"FM": "密克罗尼西亚",
|
153
153
|
"MM": "缅甸",
|
154
154
|
"MD": "摩尔多瓦",
|
@@ -141,4 +141,25 @@ module Addressing
|
|
141
141
|
fields & used_fields
|
142
142
|
end
|
143
143
|
end
|
144
|
+
|
145
|
+
class AddressFormatHelper
|
146
|
+
class << self
|
147
|
+
# Gets the required fields.
|
148
|
+
#
|
149
|
+
# Applies field overrides to the required fields
|
150
|
+
# specified by the address format.
|
151
|
+
def required_fields(address_format, field_overrides)
|
152
|
+
required_fields = address_format.required_fields
|
153
|
+
required_fields = required_fields - field_overrides.optional_fields
|
154
|
+
required_fields = required_fields - field_overrides.hidden_fields
|
155
|
+
|
156
|
+
if field_overrides.required_fields
|
157
|
+
required_fields = required_fields + field_overrides.required_fields
|
158
|
+
required_fields = required_fields.uniq
|
159
|
+
end
|
160
|
+
|
161
|
+
required_fields
|
162
|
+
end
|
163
|
+
end
|
164
|
+
end
|
144
165
|
end
|
@@ -0,0 +1,33 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Addressing
|
4
|
+
class FieldOverride < Enum
|
5
|
+
HIDDEN = "hidden"
|
6
|
+
OPTIONAL = "optional"
|
7
|
+
REQUIRED = "required"
|
8
|
+
end
|
9
|
+
|
10
|
+
class FieldOverrides
|
11
|
+
attr_reader :hidden_fields, :optional_fields, :required_fields
|
12
|
+
|
13
|
+
def initialize(definition)
|
14
|
+
AddressField.assert_all_exist(definition.keys)
|
15
|
+
FieldOverride.assert_all_exist(definition.values)
|
16
|
+
|
17
|
+
@hidden_fields = []
|
18
|
+
@optional_fields = []
|
19
|
+
@required_fields = []
|
20
|
+
|
21
|
+
definition.each do |field, override|
|
22
|
+
case override
|
23
|
+
when FieldOverride::HIDDEN
|
24
|
+
@hidden_fields << field
|
25
|
+
when FieldOverride::OPTIONAL
|
26
|
+
@optional_fields << field
|
27
|
+
when FieldOverride::REQUIRED
|
28
|
+
@required_fields << field
|
29
|
+
end
|
30
|
+
end
|
31
|
+
end
|
32
|
+
end
|
33
|
+
end
|
@@ -0,0 +1,110 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Addressing
|
4
|
+
module Model
|
5
|
+
def validates_address_format(
|
6
|
+
fields: [:country_code, :administrative_area, :locality, :dependent_locality, :postal_code, :sorting_code, :address_line1, :address_line2, :organization, :given_name, :additional_name, :family_name, :locale], field_overrides: nil, **options)
|
7
|
+
fields = Array(fields)
|
8
|
+
field_overrides ||= FieldOverrides.new({})
|
9
|
+
|
10
|
+
options[:if] ||= -> { fields.any? { |f| changes.key?(f.to_s) } } unless options[:unless]
|
11
|
+
|
12
|
+
class_eval do
|
13
|
+
validate :verify_address_format, **options
|
14
|
+
|
15
|
+
define_method :verify_address_format do
|
16
|
+
values = fields.each_with_object({}) { |f, v| v[f] = send(f) if self.respond_to?(f) }
|
17
|
+
address = Address.new(**values)
|
18
|
+
|
19
|
+
return unless address.country_code.present?
|
20
|
+
|
21
|
+
address_format = AddressFormat.get(address.country_code)
|
22
|
+
address_format.used_fields
|
23
|
+
|
24
|
+
return unless address.country_code.present?
|
25
|
+
|
26
|
+
address_format = AddressFormat.get(address.country_code)
|
27
|
+
address_format.used_fields
|
28
|
+
|
29
|
+
# Validate the presence of required fields.
|
30
|
+
AddressFormatHelper::required_fields(address_format, field_overrides).each do |required_field|
|
31
|
+
next unless address.send(required_field).blank?
|
32
|
+
|
33
|
+
errors.add(required_field, "should not be blank")
|
34
|
+
end
|
35
|
+
|
36
|
+
used_fields = address_format.used_fields - field_overrides.hidden_fields
|
37
|
+
|
38
|
+
# Validate the absence of unused fields.
|
39
|
+
unused_fields = AddressField.all.values - used_fields
|
40
|
+
unused_fields.each do |unused_field|
|
41
|
+
next if address.send(unused_field).blank?
|
42
|
+
|
43
|
+
errors.add(unused_field, "should be blank")
|
44
|
+
end
|
45
|
+
|
46
|
+
# Validate subdivisions.
|
47
|
+
subdivisions = verify_subdivisions(address, address_format, field_overrides)
|
48
|
+
|
49
|
+
# Validate postal code.
|
50
|
+
verify_postal_code(address.postal_code, subdivisions, address_format) if used_fields.include?(AddressField::POSTAL_CODE)
|
51
|
+
end
|
52
|
+
|
53
|
+
define_method :verify_subdivisions do |address, address_format, field_overrides|
|
54
|
+
# No predefined subdivisions exist, nothing to validate against.
|
55
|
+
return [] if address_format.subdivision_depth < 1
|
56
|
+
|
57
|
+
subdivisions, _parents = address_format.used_subdivision_fields.each_with_index.inject([[], []]) do |(subdivisions, parents), (field, index)|
|
58
|
+
# The field is empty or validation is disabled.
|
59
|
+
break [subdivisions, parents] if address.send(field).blank? || field_overrides.hidden_fields.include?(field)
|
60
|
+
|
61
|
+
parents << (index > 0 ? address.send(address_format.used_subdivision_fields[index - 1]) : address_format.country_code)
|
62
|
+
subdivision = Subdivision.get(address.send(field), parents)
|
63
|
+
if subdivision.nil?
|
64
|
+
errors.add(field, "should be valid")
|
65
|
+
break [subdivisions, parents]
|
66
|
+
end
|
67
|
+
|
68
|
+
subdivisions << subdivision
|
69
|
+
# No predefined subdivisions below this level, stop here.
|
70
|
+
break [subdivisions, parents] if subdivision.children.empty?
|
71
|
+
|
72
|
+
[subdivisions, parents]
|
73
|
+
end
|
74
|
+
|
75
|
+
subdivisions
|
76
|
+
end
|
77
|
+
|
78
|
+
define_method :verify_postal_code do |postal_code, subdivisions, address_format|
|
79
|
+
# Nothing to validate.
|
80
|
+
return if postal_code.blank?
|
81
|
+
|
82
|
+
full_pattern, start_pattern = subdivisions.inject([address_format.postal_code_pattern, nil]) do |(full_pattern, start_pattern), subdivision|
|
83
|
+
pattern = subdivision.postal_code_pattern
|
84
|
+
next [full_pattern, start_pattern] if pattern.blank?
|
85
|
+
next [pattern, start_pattern] if subdivision.postal_code_pattern_type == PatternType::FULL
|
86
|
+
|
87
|
+
[full_pattern, pattern]
|
88
|
+
end
|
89
|
+
|
90
|
+
if full_pattern
|
91
|
+
# The pattern must match the provided value completely.
|
92
|
+
match = postal_code.match(Regexp.new(full_pattern.gsub("\\\\", "\\").to_s, "i"))
|
93
|
+
if match.nil? || match[0] != postal_code
|
94
|
+
errors.add(AddressField::POSTAL_CODE, "should be valid")
|
95
|
+
return
|
96
|
+
end
|
97
|
+
end
|
98
|
+
|
99
|
+
if start_pattern
|
100
|
+
# The pattern must match the start of the provided value.
|
101
|
+
match = postal_code.match(Regexp.new(start_pattern.gsub("\\\\", "\\").to_s, "i"))
|
102
|
+
if match.nil? || postal_code.index(match[0]) != 0
|
103
|
+
errors.add(AddressField::POSTAL_CODE, "should be valid")
|
104
|
+
end
|
105
|
+
end
|
106
|
+
end
|
107
|
+
end
|
108
|
+
end
|
109
|
+
end
|
110
|
+
end
|
data/lib/addressing/version.rb
CHANGED
data/lib/addressing.rb
CHANGED
@@ -12,6 +12,7 @@ require "addressing/administrative_area_type"
|
|
12
12
|
require "addressing/country"
|
13
13
|
require "addressing/default_formatter"
|
14
14
|
require "addressing/dependent_locality_type"
|
15
|
+
require "addressing/field_override"
|
15
16
|
require "addressing/lazy_subdivisions"
|
16
17
|
require "addressing/locale"
|
17
18
|
require "addressing/locality_type"
|
@@ -28,3 +29,10 @@ module Addressing
|
|
28
29
|
|
29
30
|
class UnknownLocaleError < Error; end
|
30
31
|
end
|
32
|
+
|
33
|
+
if defined?(ActiveSupport.on_load)
|
34
|
+
ActiveSupport.on_load(:active_record) do
|
35
|
+
require "addressing/model"
|
36
|
+
extend Addressing::Model
|
37
|
+
end
|
38
|
+
end
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: addressing
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 0.3.1
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Robin van der Vleuten
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2022-
|
11
|
+
date: 2022-04-04 00:00:00.000000000 Z
|
12
12
|
dependencies: []
|
13
13
|
description:
|
14
14
|
email: robinvdvleuten@gmail.com
|
@@ -684,15 +684,16 @@ files:
|
|
684
684
|
- lib/addressing/address.rb
|
685
685
|
- lib/addressing/address_field.rb
|
686
686
|
- lib/addressing/address_format.rb
|
687
|
-
- lib/addressing/address_format_validator.rb
|
688
687
|
- lib/addressing/administrative_area_type.rb
|
689
688
|
- lib/addressing/country.rb
|
690
689
|
- lib/addressing/default_formatter.rb
|
691
690
|
- lib/addressing/dependent_locality_type.rb
|
692
691
|
- lib/addressing/enum.rb
|
692
|
+
- lib/addressing/field_override.rb
|
693
693
|
- lib/addressing/lazy_subdivisions.rb
|
694
694
|
- lib/addressing/locale.rb
|
695
695
|
- lib/addressing/locality_type.rb
|
696
|
+
- lib/addressing/model.rb
|
696
697
|
- lib/addressing/pattern_type.rb
|
697
698
|
- lib/addressing/postal_code_type.rb
|
698
699
|
- lib/addressing/postal_label_formatter.rb
|
File without changes
|