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