worldwide 0.2.0 → 0.4.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: 2c3c323e57e56b609ebdc7fe5d7965a2d5348fede9b72110627528529b3b7ab5
4
- data.tar.gz: f4007e0986bc9702af7d95f3c352a11937292d9f6fbbd8c21e8a1a635fcc7993
3
+ metadata.gz: 4dca8f095b6020792ecd6186606db9cd6159d03414b735b0c2d4b30f0746b680
4
+ data.tar.gz: 7e40cbbd862960354faf241a3320c03b1a081fa005f42063bf27951ca94f3c1d
5
5
  SHA512:
6
- metadata.gz: 333628df88179b5d2696634f3dadec1a548a5a2ef4baebcac86832077565f5f826036da107cdf8d5d4ed84323563235265df4b0bf37648f7d7691ef120e81216
7
- data.tar.gz: cdbad17228b52fc39d188fdbf2e13800aa4015caf18cd314d9cdfcd7f3a7b63f585c2cbd69eec36bb8c450dae83e649c35a0c661c651e55293c626582e03a785
6
+ metadata.gz: 1c385197a1a3239d1d1251a0cb7b45fe3f04f4d9e75dc3ed02227fe9692184b12dc5754c4c5292bfbd24e4a079df48b2ea63689e63b11511ec1f7017627ac15a
7
+ data.tar.gz: 666f0e760f82cfab270d47328a9600204e7c93c04bd16106b5f6d46cadefcd15f90dc64ea5663f0eeefc2827458aa2b47147468b45da059a57c37dd146893444
data/CHANGELOG.md CHANGED
@@ -28,6 +28,21 @@ Nil.
28
28
 
29
29
  ---
30
30
 
31
+ [0.4.0] - 2023-11-08
32
+ - Add region name alternates [#32](https://github.com/Shopify/worldwide/pull/32)
33
+ - Cache `Region#parent_name` [#33](https://github.com/Shopify/worldwide/pull/33)
34
+ - Use hash tables to look up regions by code [#36](https://github.com/Shopify/worldwide/pull/36)
35
+
36
+ [0.3.0] - 2023-11-03
37
+ - Add code alternates for Japan [#23](https://github.com/Shopify/worldwide/pull/23)
38
+ - Add code alternates for Puerto Rico [#24](https://github.com/Shopify/worldwide/pull/24)
39
+ - Record multiple parents per region [#27](https://github.com/Shopify/worldwide/pull/27)
40
+ - Add region.building_number_may_be_in_address2 [#28](https://github.com/Shopify/worldwide/pull/28)
41
+ - Lookup by parent-child ISO and CLDR codes for dual-status territories
42
+ [#29](https://github.com/Shopify/worldwide/pull/29)
43
+ - Handle ISO_CODE only zones lookup [#26](https://github.com/Shopify/worldwide/pull/26)
44
+
45
+
31
46
  [0.2.0] - 2023-11-01
32
47
 
33
48
  - Add Region#group and Region#group_name [#15](https://github.com/Shopify/worldwide/pull/15)
data/Gemfile.lock CHANGED
@@ -13,7 +13,7 @@ GIT
13
13
  PATH
14
14
  remote: .
15
15
  specs:
16
- worldwide (0.2.0)
16
+ worldwide (0.4.0)
17
17
  activesupport (~> 7.0)
18
18
  i18n (~> 1.12.0)
19
19
  phonelib (~> 0.8)
@@ -14,6 +14,7 @@ zip_regex: "^(IT?-?)?\\d{5}$"
14
14
  zip_example: '00144'
15
15
  phone_number_prefix: 39
16
16
  building_number_required: true
17
+ building_number_may_be_in_address2: true
17
18
  week_start_day: monday
18
19
  languages:
19
20
  - it
@@ -7,6 +7,8 @@ unit_system: imperial
7
7
  tax_name: Federal Tax
8
8
  group: North American Countries
9
9
  group_name: North America
10
+ name_alternates:
11
+ - United States of America
10
12
  zip_label: Zip code
11
13
  zip_regex: "(?-mix:\\A\\d{5}(-\\d{4})?\\z)|(?i-mx:\\AFPO\\s*A[A-Z]\\s*\\d{5}-\\d{4}\\z)"
12
14
  zip_example: '90210'
@@ -34,7 +34,9 @@ module Worldwide
34
34
  :zip_requirement,
35
35
  ]
36
36
 
37
- attr_accessor :parent
37
+ # A region may have more than one parent.
38
+ # For example, Puerto Rico (PR/US-PR) is associated with both the US and the Caribbean (029)
39
+ attr_accessor :parents
38
40
 
39
41
  # ISO-3166 three-letter code for this region, if there is one.
40
42
  # Otherwise, nil.
@@ -45,6 +47,10 @@ module Worldwide
45
47
  # If we require a building number in an address, then this will be true.
46
48
  attr_accessor :building_number_required
47
49
 
50
+ # In some countries, an address may have the building number in address2.
51
+ # If we are allowed to have a building number in address2, then this will be true.
52
+ attr_accessor :building_number_may_be_in_address2
53
+
48
54
  # Alternate codes which may be used to designate this region
49
55
  attr_accessor :code_alternates
50
56
 
@@ -104,6 +110,10 @@ module Worldwide
104
110
  # This name is the name that was traditionally returned by "country_db".
105
111
  attr_reader :legacy_name
106
112
 
113
+ # Other names that may be used to refer to this region.
114
+ # E.g., "Czech Republic" is also known as "Czechia".
115
+ attr_accessor :name_alternates
116
+
107
117
  # iso_code values of regions (subdivisions) within the same country that border this region.
108
118
  # E.g., for CA-ON, the neighbouring zones are CA-MB, CA-NU and CA-QC.
109
119
  attr_accessor :neighbours
@@ -217,6 +227,7 @@ module Worldwide
217
227
  @use_zone_code_as_short_name = use_zone_code_as_short_name
218
228
 
219
229
  @building_number_required = false
230
+ @building_number_may_be_in_address2 = false
220
231
  @currency = nil
221
232
  @flag = nil
222
233
  @format = {}
@@ -236,7 +247,7 @@ module Worldwide
236
247
  @zip_prefixes = []
237
248
  @zip_regex = nil
238
249
 
239
- @parent = nil
250
+ @parents = [].to_set
240
251
  @zones = []
241
252
  end
242
253
 
@@ -249,12 +260,18 @@ module Worldwide
249
260
  def add_zone(region)
250
261
  return if @zones.include?(region)
251
262
 
252
- region.parent = self
263
+ region.parents << self
253
264
  @zones.append(region)
254
265
  end
255
266
 
256
267
  # Attributes
257
268
 
269
+ def associated_country
270
+ return self if country?
271
+
272
+ parent_country
273
+ end
274
+
258
275
  # The value with which to autofill the zip, if this region has zip autofill active;
259
276
  # otherwise, nil.
260
277
  def autofill_zip
@@ -333,7 +350,8 @@ module Worldwide
333
350
 
334
351
  zones.find do |region|
335
352
  [search_code, alt_search_code].any? do |candidate|
336
- candidate == region.alpha_three ||
353
+ candidate == subdivision_code(region.iso_code) ||
354
+ candidate == region.alpha_three ||
337
355
  candidate == region.iso_code ||
338
356
  candidate == region.legacy_code ||
339
357
  candidate == region.numeric_three ||
@@ -371,7 +389,7 @@ module Worldwide
371
389
  # is the given postal code value valid for this region?
372
390
  def valid_zip?(zip, partial_match: false)
373
391
  normalized = Zip.normalize(
374
- country_code: province? && parent.iso_code ? parent.iso_code : iso_code,
392
+ country_code: province? && associated_country.iso_code ? associated_country.iso_code : iso_code,
375
393
  zip: zip,
376
394
  )
377
395
  valid_normalized_zip?(normalized, partial_match: partial_match)
@@ -384,6 +402,21 @@ module Worldwide
384
402
 
385
403
  private
386
404
 
405
+ def answers_to_cldr_code(search_code)
406
+ return false if Util.blank?(search_code) || Util.blank?(cldr_code)
407
+ return true if search_code.casecmp(cldr_code).zero?
408
+
409
+ pc = parent_country
410
+ "#{pc&.cldr_code&.downcase}#{cldr_code.downcase}" == search_code.downcase
411
+ end
412
+
413
+ def answers_to_iso_code(search_code)
414
+ return true if search_code == iso_code
415
+
416
+ pc = parent_country
417
+ "#{pc&.iso_code}-#{iso_code}" == search_code
418
+ end
419
+
387
420
  def cross_border_zip_includes_province?(zip:, province_code:)
388
421
  return false unless country?
389
422
 
@@ -408,11 +441,17 @@ module Worldwide
408
441
  INSPECTION_FIELDS.map { |field_name| "@#{field_name}=#{send(field_name).inspect}" }.join(", ")
409
442
  end
410
443
 
444
+ def parent_country
445
+ @parent_country ||= parents.find(&:country?)
446
+ end
447
+
411
448
  # Checks whether the given value is acceptable according to the regular expression defined for the country.
412
449
  # @param value [String] for the postal code
413
450
  # @return [Boolean]
414
451
  def passes_country_zip_regexp?(value:, partial_match: false)
415
- return parent.send(:passes_country_zip_regexp?, value: value, partial_match: partial_match) if province?
452
+ if province?
453
+ return associated_country.send(:passes_country_zip_regexp?, value: value, partial_match: partial_match)
454
+ end
416
455
 
417
456
  return false if partial_match && partial_zip_regex.nil?
418
457
 
@@ -445,11 +484,18 @@ module Worldwide
445
484
  end&.first
446
485
  end
447
486
 
487
+ def subdivision_code(iso_code)
488
+ return iso_code if iso_code.nil? || iso_code.length < 3
489
+
490
+ country_code, subdivision_code = iso_code.split("-")
491
+ return subdivision_code if country_code.casecmp(associated_country.iso_code).zero?
492
+ end
493
+
448
494
  def valid_normalized_zip?(normalized, province_code: nil, partial_match: false)
449
495
  if country?
450
496
  country = self
451
497
  elsif province?
452
- country = parent
498
+ country = associated_country
453
499
  province_code ||= legacy_code
454
500
  end
455
501
 
@@ -16,7 +16,7 @@ module Worldwide
16
16
  end
17
17
 
18
18
  def initialize
19
- @regions = RegionsLoader.new.load_regions
19
+ @regions, @regions_by_cldr_code, @regions_by_iso_code = RegionsLoader.new.load_regions
20
20
  end
21
21
 
22
22
  def all
@@ -29,17 +29,9 @@ module Worldwide
29
29
  end
30
30
 
31
31
  result = if cldr
32
- search_code = cldr.to_s.upcase
33
-
34
- @regions.find do |r|
35
- r.cldr_code.upcase == search_code
36
- end
32
+ @regions_by_cldr_code[cldr.to_s.upcase]
37
33
  elsif code
38
- search_code = code.to_s.upcase
39
-
40
- @regions.find do |r|
41
- r.iso_code == search_code || r.alpha_three == search_code || r.numeric_three == search_code
42
- end
34
+ @regions_by_iso_code[code.to_s.upcase]
43
35
  else # search by name
44
36
  search_name = name.upcase
45
37
 
@@ -9,6 +9,9 @@ module Worldwide
9
9
  WORLD_CODE = "001" # UN code for the whole world
10
10
 
11
11
  def load_regions
12
+ @regions_by_cldr_code = {}
13
+ @regions_by_iso_code = {}
14
+
12
15
  # Load country/region definitions out of the db/data/regions/??.yml files
13
16
  @regions = Dir["#{Worldwide::Paths::REGIONS_ROOT}/*.yml"].map do |filename|
14
17
  load_territory(filename)
@@ -25,11 +28,39 @@ module Worldwide
25
28
 
26
29
  construct_unknown
27
30
 
28
- @regions.freeze
31
+ @regions.each do |region|
32
+ construct_lookup_info(region)
33
+ end
34
+
35
+ [@regions.freeze, @regions_by_cldr_code, @regions_by_iso_code]
29
36
  end
30
37
 
31
38
  private
32
39
 
40
+ def construct_lookup_info(region)
41
+ pc = region.send(:parent_country)
42
+
43
+ # Remember CLDR code(s) for later use during lookup
44
+
45
+ search_code = region.cldr_code.to_s.upcase
46
+ @regions_by_cldr_code[search_code] = region if Util.present?(search_code)
47
+
48
+ @regions_by_cldr_code["#{pc.cldr_code.upcase}#{search_code}"] = region if Util.present?(pc&.cldr_code)
49
+
50
+ # Remember ISO 3166 code(s) for later use during lookup
51
+
52
+ iso_code = region.iso_code
53
+ @regions_by_iso_code[iso_code] = region if Util.present?(iso_code)
54
+
55
+ @regions_by_iso_code["#{pc.iso_code}-#{iso_code}"] = region if Util.present?(pc)
56
+
57
+ alpha_three = region.alpha_three
58
+ @regions_by_iso_code[alpha_three] = region if Util.present?(alpha_three)
59
+
60
+ numeric_three = region.numeric_three
61
+ @regions_by_iso_code[numeric_three] = region if Util.present?(numeric_three)
62
+ end
63
+
33
64
  def apply_hierarchy(parent:, code:, children:)
34
65
  current_region = find_region(code: code)
35
66
  if current_region.nil?
@@ -46,9 +77,7 @@ module Worldwide
46
77
  @regions << current_region
47
78
  end
48
79
 
49
- if parent.present? && current_region.parent != parent
50
- current_region.parent = parent
51
- end
80
+ current_region.parents << parent if Util.present?(parent)
52
81
  parent&.add_zone(current_region)
53
82
  return current_region if children.nil?
54
83
 
@@ -61,6 +90,7 @@ module Worldwide
61
90
 
62
91
  def apply_territory_attributes(region, spec)
63
92
  region.building_number_required = spec["building_number_required"] || true
93
+ region.building_number_may_be_in_address2 = spec["building_number_may_be_in_address2"] || false
64
94
  currency_code = spec["currency"]
65
95
  region.currency = Worldwide.currency(code: currency_code) unless currency_code.nil?
66
96
  region.flag = spec["emoji"]
@@ -83,10 +113,12 @@ module Worldwide
83
113
  region.zip_requirement = spec["zip_requirement"]
84
114
  region.zip_regex = spec["zip_regex"]
85
115
  region.zips_crossing_provinces = spec["zips_crossing_provinces"]
116
+ region.name_alternates = spec["name_alternates"] || []
86
117
  end
87
118
 
88
119
  def apply_zone_attributes(region, zone)
89
120
  region.code_alternates = zone["code_alternates"] || []
121
+ region.name_alternates = zone["name_alternates"] || []
90
122
  region.example_city = zone["example_city"]
91
123
  region.neighbours = zone["neighboring_zones"]
92
124
  region.zip_prefixes = zone["zip_prefixes"] || []
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Worldwide
4
- VERSION = "0.2.0"
4
+ VERSION = "0.4.0"
5
5
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: worldwide
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.2.0
4
+ version: 0.4.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Shopify
8
8
  autorequire:
9
9
  bindir: exe
10
10
  cert_chain: []
11
- date: 2023-11-01 00:00:00.000000000 Z
11
+ date: 2023-11-08 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: activesupport