costa_rica_address_utils 0.3.2 → 0.5.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 +4 -4
- data/CHANGELOG.md +17 -1
- data/Gemfile.lock +1 -1
- data/README.md +8 -2
- data/data/costa_rica_dataset.json +2956 -1
- data/data/guatemala_dataset.json +2674 -1
- data/data/locations_dataset.json +2956 -1
- data/lib/costa_rica_address_utils/costa_rica.rb +101 -0
- data/lib/costa_rica_address_utils/guatemala.rb +62 -0
- data/lib/costa_rica_address_utils/version.rb +1 -1
- data/lib/costa_rica_address_utils.rb +41 -92
- metadata +4 -2
|
@@ -0,0 +1,101 @@
|
|
|
1
|
+
require 'json'
|
|
2
|
+
|
|
3
|
+
# TODO: find way to optimize dataset loading
|
|
4
|
+
|
|
5
|
+
module CostaRicaAddressUtils
|
|
6
|
+
module CostaRica
|
|
7
|
+
JSON_FILE_PATH = File.join(File.dirname(__FILE__), '..', '..', 'data', 'locations_dataset.json')
|
|
8
|
+
NEW_JSON_FILE_PATH = File.join(File.dirname(__FILE__), '..', '..', 'data', 'costa_rica_dataset.json')
|
|
9
|
+
|
|
10
|
+
# Load the JSON file and parse it into a Ruby object for general usage
|
|
11
|
+
LOCATIONS_DATASET = JSON.parse(File.read(JSON_FILE_PATH))
|
|
12
|
+
NEW_LOCATIONS_DATASET = JSON.parse(File.read(NEW_JSON_FILE_PATH)) # Should replace locations_dataset.json in the future
|
|
13
|
+
|
|
14
|
+
class Error < StandardError; end
|
|
15
|
+
# Your code goes here...
|
|
16
|
+
|
|
17
|
+
# Fetch the address data from provided input, return options for subaddresses on each level and zip code if full address is valid
|
|
18
|
+
def fetch_address_data(province:, canton: nil, district: nil)
|
|
19
|
+
raise CostaRicaAddressUtils::InvalidData, 'Province is required' if province.nil? || province.empty?
|
|
20
|
+
|
|
21
|
+
province_data = LOCATIONS_DATASET[province]
|
|
22
|
+
canton_data = !!province_data ? province_data['cantons'][canton] : nil
|
|
23
|
+
district_data = !!canton_data ? canton_data['districts'][district] : nil
|
|
24
|
+
|
|
25
|
+
canton_options = !!province_data ? province_data['cantons'].keys : [] # Cantons options, only if province is valid
|
|
26
|
+
district_options = !!canton_data ? canton_data['districts'].keys : [] # Districts options, only if canton is valid
|
|
27
|
+
zip = (!!district_data && district_data['zip_code']) || nil # Zip code, only if full address is valid
|
|
28
|
+
|
|
29
|
+
{
|
|
30
|
+
zip: zip,
|
|
31
|
+
# Names options
|
|
32
|
+
province_options: LOCATIONS_DATASET.keys,
|
|
33
|
+
canton_options: canton_options,
|
|
34
|
+
district_options: district_options
|
|
35
|
+
}
|
|
36
|
+
end
|
|
37
|
+
|
|
38
|
+
# Get one address information from a zip code
|
|
39
|
+
def fetch_address_from_zip(zip_code, new_dataset: false)
|
|
40
|
+
return nil unless zip_valid?(zip_code)
|
|
41
|
+
|
|
42
|
+
zip_code_s = zip_code.to_s
|
|
43
|
+
|
|
44
|
+
if new_dataset
|
|
45
|
+
NEW_LOCATIONS_DATASET.each do |province, province_data|
|
|
46
|
+
province_data['locationsLevel2'].each do |canton, canton_data|
|
|
47
|
+
canton_data['locationsLevel3'].each do |district, district_data|
|
|
48
|
+
if district_data['zipCode'] == zip_code_s
|
|
49
|
+
return {
|
|
50
|
+
province: province,
|
|
51
|
+
canton: canton,
|
|
52
|
+
district: district,
|
|
53
|
+
zip: zip_code_s
|
|
54
|
+
}
|
|
55
|
+
end
|
|
56
|
+
end
|
|
57
|
+
end
|
|
58
|
+
end
|
|
59
|
+
else
|
|
60
|
+
LOCATIONS_DATASET.each do |province, province_data|
|
|
61
|
+
province_data['cantons'].each do |canton, canton_data|
|
|
62
|
+
canton_data['districts'].each do |district, district_data|
|
|
63
|
+
if district_data['zip_code'] == zip_code_s
|
|
64
|
+
return {
|
|
65
|
+
province: province,
|
|
66
|
+
canton: canton,
|
|
67
|
+
district: district,
|
|
68
|
+
zip: zip_code_s
|
|
69
|
+
}
|
|
70
|
+
end
|
|
71
|
+
end
|
|
72
|
+
end
|
|
73
|
+
end
|
|
74
|
+
end
|
|
75
|
+
|
|
76
|
+
nil
|
|
77
|
+
end
|
|
78
|
+
|
|
79
|
+
def fetch_address_from_zip!(zip_code, new_dataset: false)
|
|
80
|
+
raise "Zip code provided #{zip_code} is invalid. Must be a 5 digits number" unless zip_valid?(zip_code)
|
|
81
|
+
|
|
82
|
+
fetch_address_from_zip(zip_code, new_dataset: new_dataset)
|
|
83
|
+
end
|
|
84
|
+
|
|
85
|
+
def address_valid?(province:, canton:, district:)
|
|
86
|
+
is_valid = true
|
|
87
|
+
begin
|
|
88
|
+
data = fetch_address_data(province: province, canton: canton, district: district)
|
|
89
|
+
is_valid = !!data[:zip] # Is valid if matched to a zip code
|
|
90
|
+
rescue CostaRicaAddressUtils::InvalidData => e
|
|
91
|
+
is_valid = false
|
|
92
|
+
end
|
|
93
|
+
|
|
94
|
+
is_valid
|
|
95
|
+
end
|
|
96
|
+
|
|
97
|
+
def zip_valid?(zip_code)
|
|
98
|
+
!!zip_code && zip_code.to_s.length == 5
|
|
99
|
+
end
|
|
100
|
+
end # CostaRica
|
|
101
|
+
end
|
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module CostaRicaAddressUtils
|
|
4
|
+
module Guatemala
|
|
5
|
+
# Guatemala-specific implementation
|
|
6
|
+
JSON_FILE_PATH = File.join(File.dirname(__FILE__), '..', '..', 'data', 'guatemala_dataset.json')
|
|
7
|
+
LOCATIONS_DATASET = JSON.parse(File.read(JSON_FILE_PATH))
|
|
8
|
+
|
|
9
|
+
# Your Guatemala-specific methods here
|
|
10
|
+
|
|
11
|
+
# For a given address of 2 levels, return options for each level found and zip code if full address is valid
|
|
12
|
+
def self.fetch_address_data(department:, municipality:)
|
|
13
|
+
raise CostaRicaAddressUtils::InvalidData, 'Department is required' if department.nil? || department.empty?
|
|
14
|
+
|
|
15
|
+
department_data = LOCATIONS_DATASET[department]
|
|
16
|
+
municipality_data = !!department_data ? department_data['locationsLevel2'][municipality] : nil
|
|
17
|
+
|
|
18
|
+
# Municipality options(lv2), only if department(lv1) is valid
|
|
19
|
+
municipality_options = !!department_data ? department_data['locationsLevel2'].keys : []
|
|
20
|
+
zip = (!!municipality_data && municipality_data['zipCode']) || nil # Zip code, only if full address is valid
|
|
21
|
+
|
|
22
|
+
{
|
|
23
|
+
zip: zip,
|
|
24
|
+
# Names options
|
|
25
|
+
department_options: LOCATIONS_DATASET.keys,
|
|
26
|
+
municipality_options: municipality_options
|
|
27
|
+
}
|
|
28
|
+
end
|
|
29
|
+
|
|
30
|
+
# Get one address information from a zip code
|
|
31
|
+
def self.fetch_address_from_zip(zip_code)
|
|
32
|
+
return nil unless zip_valid?(zip_code)
|
|
33
|
+
|
|
34
|
+
zip_code_s = zip_code.to_s
|
|
35
|
+
LOCATIONS_DATASET.each do |department, department_data|
|
|
36
|
+
department_data['locationsLevel2'].each do |municipality, municipality_data|
|
|
37
|
+
if municipality_data['zipCode'] == zip_code_s
|
|
38
|
+
return {
|
|
39
|
+
department: department,
|
|
40
|
+
municipality: municipality,
|
|
41
|
+
zip: zip_code_s
|
|
42
|
+
}
|
|
43
|
+
end
|
|
44
|
+
end
|
|
45
|
+
end
|
|
46
|
+
|
|
47
|
+
nil
|
|
48
|
+
end
|
|
49
|
+
|
|
50
|
+
def self.address_valid?(department:, municipality:)
|
|
51
|
+
is_valid = true
|
|
52
|
+
begin
|
|
53
|
+
data = fetch_address_data(department: department, municipality: municipality)
|
|
54
|
+
is_valid = !!data[:zip] # Is valid if matched to a zip code
|
|
55
|
+
rescue CostaRicaAddressUtils::InvalidData => e
|
|
56
|
+
is_valid = false
|
|
57
|
+
end
|
|
58
|
+
|
|
59
|
+
is_valid
|
|
60
|
+
end
|
|
61
|
+
end # Guatemala
|
|
62
|
+
end # CostaRicaAddressUtils
|
|
@@ -1,118 +1,67 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
|
-
# TODO find way to optimize dataset loading
|
|
3
|
-
require 'json'
|
|
4
|
-
require_relative "costa_rica_address_utils/version"
|
|
5
|
-
require_relative "costa_rica_address_utils/errors"
|
|
6
2
|
|
|
7
|
-
|
|
3
|
+
require_relative 'costa_rica_address_utils/version'
|
|
4
|
+
require_relative 'costa_rica_address_utils/errors'
|
|
5
|
+
require_relative 'costa_rica_address_utils/costa_rica'
|
|
6
|
+
require_relative 'costa_rica_address_utils/guatemala'
|
|
8
7
|
|
|
9
|
-
module CostaRicaAddressUtils
|
|
10
|
-
#
|
|
11
|
-
|
|
12
|
-
|
|
8
|
+
module CostaRicaAddressUtils # rubocop:disable Style/Documentation
|
|
9
|
+
# Backwards compatibility before usage of for_country
|
|
10
|
+
# Old version will be able to call methods with CostaRicaAddressUtils directly
|
|
11
|
+
extend CostaRicaAddressUtils::CostaRica
|
|
13
12
|
|
|
14
|
-
|
|
15
|
-
|
|
13
|
+
SUPPORTED_COUNTRIES = %i[costa_rica guatemala].freeze
|
|
14
|
+
VALID_INPUT_PROVIDERS = %i[shopify brightpearl].freeze
|
|
16
15
|
|
|
17
|
-
#
|
|
18
|
-
|
|
19
|
-
raise InvalidData, "Province is required" if province.nil? || province.empty?
|
|
16
|
+
# TODO: merge the methods from both countries using new dataset for costa rica instead of previous one
|
|
17
|
+
# Newer files contains a standarized format for fields.
|
|
20
18
|
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
return {
|
|
30
|
-
zip: zip,
|
|
31
|
-
# Names options
|
|
32
|
-
province_options: LOCATIONS_DATASET.keys,
|
|
33
|
-
canton_options: canton_options,
|
|
34
|
-
district_options: district_options,
|
|
35
|
-
}
|
|
36
|
-
end
|
|
37
|
-
|
|
38
|
-
# Get one address information from a zip code
|
|
39
|
-
def self.fetch_address_from_zip(zip_code)
|
|
40
|
-
return nil unless zip_valid?(zip_code)
|
|
41
|
-
zip_code_s = zip_code.to_s
|
|
42
|
-
LOCATIONS_DATASET.each do |province, province_data|
|
|
43
|
-
province_data["cantons"].each do |canton, canton_data|
|
|
44
|
-
canton_data["districts"].each do |district, district_data|
|
|
45
|
-
if district_data["zip_code"] == zip_code_s
|
|
46
|
-
return {
|
|
47
|
-
province: province,
|
|
48
|
-
canton: canton,
|
|
49
|
-
district: district,
|
|
50
|
-
zip: zip_code_s,
|
|
51
|
-
}
|
|
52
|
-
end
|
|
53
|
-
end
|
|
54
|
-
end
|
|
55
|
-
end
|
|
56
|
-
|
|
57
|
-
return nil
|
|
58
|
-
end
|
|
59
|
-
|
|
60
|
-
def self.fetch_address_from_zip!(zip_code)
|
|
61
|
-
raise "Zip code provided #{zip_code} is invalid. Must be a 5 digits number" unless zip_valid?(zip_code)
|
|
62
|
-
fetch_address_from_zip(zip_code)
|
|
63
|
-
end
|
|
64
|
-
|
|
65
|
-
def self.address_valid?(province:, canton:, district:)
|
|
66
|
-
is_valid = true
|
|
67
|
-
begin
|
|
68
|
-
data = fetch_address_data(province: province, canton: canton, district: district)
|
|
69
|
-
is_valid = !!data[:zip] # Is valid if matched to a zip code
|
|
70
|
-
rescue InvalidData => e
|
|
71
|
-
is_valid = false
|
|
19
|
+
def self.for_country(country)
|
|
20
|
+
case country.to_sym
|
|
21
|
+
when :costa_rica
|
|
22
|
+
CostaRicaAddressUtils::CostaRica
|
|
23
|
+
when :guatemala
|
|
24
|
+
CostaRicaAddressUtils::Guatemala
|
|
25
|
+
else
|
|
26
|
+
raise ArgumentError, "Unsupported country: #{country}. Supported countries are: #{SUPPORTED_COUNTRIES}"
|
|
72
27
|
end
|
|
73
|
-
|
|
74
|
-
return is_valid
|
|
75
|
-
end
|
|
76
|
-
|
|
77
|
-
def self.zip_valid?(zip_code)
|
|
78
|
-
!!zip_code && zip_code.to_s.length == 5
|
|
79
28
|
end
|
|
80
29
|
|
|
81
30
|
# Build a Costa Rica address from an address of an external provider (Shopify, Brightpearl, etc)
|
|
82
31
|
# https://shopify.dev/api/admin-graphql/2022-10/objects/mailingaddress
|
|
83
32
|
# https://api-docs.brightpearl.com/contact/postal-address/get.html
|
|
84
|
-
|
|
33
|
+
# Standarized format locationLevelX for the address Province/Department, Municipality/Canton, District/Parish
|
|
34
|
+
# since they are named differently in each country
|
|
35
|
+
def self.build_address_from_provider(address:, provider:)
|
|
85
36
|
case provider
|
|
86
37
|
when :shopify
|
|
87
|
-
|
|
38
|
+
{
|
|
88
39
|
name: address.name, # Customer name
|
|
89
40
|
address1: address.address1,
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
41
|
+
|
|
42
|
+
locationLevel1: address.province,
|
|
43
|
+
locationLevel2: address.city,
|
|
44
|
+
locationLevel3: address.address2,
|
|
94
45
|
zip: address.zip,
|
|
95
46
|
|
|
96
47
|
national_id: address.company,
|
|
97
|
-
phone: address.phone
|
|
48
|
+
phone: address.phone
|
|
98
49
|
}
|
|
99
50
|
when :brightpearl
|
|
100
|
-
|
|
101
|
-
name: address[
|
|
102
|
-
address1: address[
|
|
103
|
-
|
|
104
|
-
province: address["addressLine4"],
|
|
105
|
-
canton: address["addressLine3"],
|
|
106
|
-
district: address["addressLine2"],
|
|
107
|
-
zip: address["postalCode"],
|
|
51
|
+
{
|
|
52
|
+
name: address['addressFullName'], # Customer name
|
|
53
|
+
address1: address['addressLine1'],
|
|
108
54
|
|
|
109
|
-
|
|
110
|
-
|
|
55
|
+
locationLevel1: address['addressLine4'],
|
|
56
|
+
locationLevel2: address['addressLine3'],
|
|
57
|
+
locationLevel3: address['addressLine2'],
|
|
58
|
+
zip: address['postalCode'],
|
|
59
|
+
|
|
60
|
+
national_id: address['companyName'],
|
|
61
|
+
phone: address['telephone']
|
|
111
62
|
}
|
|
112
63
|
else
|
|
113
|
-
raise InvalidData("Invalid provider, valid providers are: #{
|
|
64
|
+
raise InvalidData("Invalid provider, valid providers are: #{VALID_INPUT_PROVIDERS}")
|
|
114
65
|
end
|
|
115
66
|
end
|
|
116
|
-
|
|
117
|
-
private
|
|
118
|
-
end # module CostaRicaAddressUtils
|
|
67
|
+
end
|
metadata
CHANGED
|
@@ -1,14 +1,14 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: costa_rica_address_utils
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 0.
|
|
4
|
+
version: 0.5.0
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- vicvans20
|
|
8
8
|
autorequire:
|
|
9
9
|
bindir: exe
|
|
10
10
|
cert_chain: []
|
|
11
|
-
date:
|
|
11
|
+
date: 2025-05-12 00:00:00.000000000 Z
|
|
12
12
|
dependencies:
|
|
13
13
|
- !ruby/object:Gem::Dependency
|
|
14
14
|
name: byebug
|
|
@@ -45,7 +45,9 @@ files:
|
|
|
45
45
|
- data/guatemala_dataset.json
|
|
46
46
|
- data/locations_dataset.json
|
|
47
47
|
- lib/costa_rica_address_utils.rb
|
|
48
|
+
- lib/costa_rica_address_utils/costa_rica.rb
|
|
48
49
|
- lib/costa_rica_address_utils/errors.rb
|
|
50
|
+
- lib/costa_rica_address_utils/guatemala.rb
|
|
49
51
|
- lib/costa_rica_address_utils/version.rb
|
|
50
52
|
- sig/costa_rica_address_utils.rbs
|
|
51
53
|
homepage: https://github.com/vicvans20/costa_rica_address_utils
|