addressing 0.1.1 → 0.3.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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 93c92e35c1ba9b99ef0f8614354be6ce1140417527f1e3b53d97f5e03e430f42
4
- data.tar.gz: fdc66a367d15c31344f36e82db8c5740d57eefb34dc9e9219822bb5de4b2501d
3
+ metadata.gz: 70ce82947444306a38c71f41034dbc93a842d06125f66d2288ab212b663af42c
4
+ data.tar.gz: 69a1dc6eb190e4d1dca977df29cf3d6c3fe8b2c87334d8a919e1f7022f57bdb2
5
5
  SHA512:
6
- metadata.gz: c30cd13e71d74a015a551975032e0800b44ccded30378fee6c1603572bf2a8dee27f57dfda894ac358ff33e26407692ed11d0e9d7ef72f3a67756e77295f812f
7
- data.tar.gz: 35897859d6db9035e7fdde7282af57ebe042034a8d759a5eb8a9a79d76b91ea30ed6a5e84403c8ee74071f10c6d8879adf421d56037b5d641f8b384d22a6fd98
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
@@ -18,6 +18,11 @@ module Addressing
18
18
  @subdivisions.any?
19
19
  end
20
20
 
21
+ def empty?
22
+ do_initialize unless @initialized
23
+ @subdivisions.empty?
24
+ end
25
+
21
26
  private
22
27
 
23
28
  def do_initialize
@@ -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
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Addressing
4
- VERSION = "0.1.1"
4
+ VERSION = "0.3.1"
5
5
  end
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.1.1
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-02-21 00:00:00.000000000 Z
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