smartystreets_ruby_sdk 8.1.1 → 9.0.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/.github/workflows/ci.yml +10 -0
- data/.github/workflows/publish.yml +29 -0
- data/.github/workflows/test.yml +21 -0
- data/CLAUDE.md +1 -1
- data/Makefile +7 -4
- data/README.md +2 -0
- data/examples/us_autocomplete_pro_example.rb +3 -1
- data/examples/us_enrichment_business_name_search_example.rb +76 -0
- data/examples/us_enrichment_etag_example.rb +22 -14
- data/examples/us_enrichment_example.rb +1 -1
- data/examples/us_reverse_geo_example.rb +3 -0
- data/examples/us_street_match_strategy_example.rb +85 -0
- data/lib/smartystreets_ruby_sdk/errors.rb +16 -11
- data/lib/smartystreets_ruby_sdk/exceptions.rb +6 -0
- data/lib/smartystreets_ruby_sdk/international_street/components.rb +8 -3
- data/lib/smartystreets_ruby_sdk/international_street/rootlevel.rb +1 -5
- data/lib/smartystreets_ruby_sdk/native_serializer.rb +1 -0
- data/lib/smartystreets_ruby_sdk/status_code_sender.rb +29 -25
- data/lib/smartystreets_ruby_sdk/us_autocomplete_pro/source_type.rb +8 -0
- data/lib/smartystreets_ruby_sdk/us_autocomplete_pro.rb +1 -0
- data/lib/smartystreets_ruby_sdk/us_enrichment/client.rb +5 -2
- data/lib/smartystreets_ruby_sdk/us_enrichment/lookup.rb +3 -2
- data/lib/smartystreets_ruby_sdk/us_reverse_geo/source_type.rb +8 -0
- data/lib/smartystreets_ruby_sdk/us_reverse_geo/us_reverse_geo_response.rb +1 -1
- data/lib/smartystreets_ruby_sdk/us_reverse_geo.rb +1 -0
- data/lib/smartystreets_ruby_sdk/version.rb +1 -1
- metadata +10 -4
- data/.github/workflows/gem-publish.yml +0 -27
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 1f80a7994c3b9e2b9c0936875503a6fe6751db574e64861014ead029f5e1079e
|
|
4
|
+
data.tar.gz: d488f5c6698c089bc147d562793d67e5478b70c23da9d76e8607fe03111b0a93
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 739c0edcc1fc7e0a6d4cff83ec12adb0f2f146518e211fe981988d2d4dc18921c16866e911de63612df5e3638a72835bdf6f74a69a8b218c459211941a257c3b
|
|
7
|
+
data.tar.gz: 1f8de619ff3655c99a97d7acb5a3c95d892451c7aab832dd8e0cc88c32ecb12a8b1d6e8237cb3f9032ca7fee8573c43cbdcf805313f10b3c67de8fab42317484
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
name: Ruby Gem Publish
|
|
2
|
+
|
|
3
|
+
on:
|
|
4
|
+
push:
|
|
5
|
+
tags:
|
|
6
|
+
- '**'
|
|
7
|
+
|
|
8
|
+
jobs:
|
|
9
|
+
test:
|
|
10
|
+
uses: ./.github/workflows/test.yml
|
|
11
|
+
|
|
12
|
+
publish:
|
|
13
|
+
needs: test
|
|
14
|
+
runs-on: ubuntu-latest
|
|
15
|
+
steps:
|
|
16
|
+
- uses: actions/checkout@v6
|
|
17
|
+
with:
|
|
18
|
+
fetch-depth: 0
|
|
19
|
+
|
|
20
|
+
- uses: ruby/setup-ruby@v1
|
|
21
|
+
with:
|
|
22
|
+
ruby-version: '3.3'
|
|
23
|
+
bundler-cache: false
|
|
24
|
+
|
|
25
|
+
- name: Publish
|
|
26
|
+
run: |
|
|
27
|
+
VERSION=${GITHUB_REF#refs/*/} make publish
|
|
28
|
+
env:
|
|
29
|
+
API_KEY: "${{ secrets.GEM_HOST_API_KEY }}"
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
name: Ruby Tests (reusable)
|
|
2
|
+
|
|
3
|
+
on:
|
|
4
|
+
workflow_call:
|
|
5
|
+
|
|
6
|
+
jobs:
|
|
7
|
+
test:
|
|
8
|
+
runs-on: ubuntu-latest
|
|
9
|
+
strategy:
|
|
10
|
+
matrix:
|
|
11
|
+
ruby-version: ['3.3', '3.4', '4.0']
|
|
12
|
+
steps:
|
|
13
|
+
- uses: actions/checkout@v6
|
|
14
|
+
- uses: ruby/setup-ruby@v1
|
|
15
|
+
with:
|
|
16
|
+
ruby-version: ${{ matrix.ruby-version }}
|
|
17
|
+
bundler-cache: false
|
|
18
|
+
- name: Install dependencies
|
|
19
|
+
run: make dependencies
|
|
20
|
+
- name: Test
|
|
21
|
+
run: make test
|
data/CLAUDE.md
CHANGED
|
@@ -25,7 +25,7 @@ make examples
|
|
|
25
25
|
|
|
26
26
|
## Architecture
|
|
27
27
|
|
|
28
|
-
This is the official SmartyStreets Ruby SDK for address verification APIs. It uses pure Ruby with Net::HTTP (no external runtime dependencies).
|
|
28
|
+
This is the official SmartyStreets Ruby SDK for address verification APIs. It uses pure Ruby with Net::HTTP (no external runtime dependencies). Compatible with Ruby 3.3 and later.
|
|
29
29
|
|
|
30
30
|
### Core Design Patterns
|
|
31
31
|
|
data/Makefile
CHANGED
|
@@ -12,7 +12,7 @@ test:
|
|
|
12
12
|
dependencies:
|
|
13
13
|
gem install minitest
|
|
14
14
|
|
|
15
|
-
package: clean dependencies
|
|
15
|
+
package: clean dependencies
|
|
16
16
|
sed -i "s/0\.0\.0/${VERSION}/g" "$(VERSION_FILE)" \
|
|
17
17
|
&& gem build *.gemspec \
|
|
18
18
|
&& git checkout "$(VERSION_FILE)"
|
|
@@ -37,7 +37,7 @@ us_autocomplete_pro_api:
|
|
|
37
37
|
cd examples && ruby us_autocomplete_pro_example.rb
|
|
38
38
|
|
|
39
39
|
us_enrichment_api:
|
|
40
|
-
cd examples && ruby us_enrichment_example.rb && ruby us_enrichment_business_example.rb && ruby us_enrichment_etag_example.rb
|
|
40
|
+
cd examples && ruby us_enrichment_example.rb && ruby us_enrichment_business_example.rb && ruby us_enrichment_etag_example.rb && ruby us_enrichment_business_name_search_example.rb
|
|
41
41
|
|
|
42
42
|
us_extract_api:
|
|
43
43
|
cd examples && ruby us_extract_example.rb
|
|
@@ -46,11 +46,14 @@ us_reverse_geo_api:
|
|
|
46
46
|
cd examples && ruby us_reverse_geo_example.rb
|
|
47
47
|
|
|
48
48
|
us_street_api:
|
|
49
|
-
cd examples && ruby us_street_single_address_example.rb && ruby us_street_multiple_address_example.rb && ruby us_street_component_analysis_example.rb && ruby us_street_iana_timezone_example.rb
|
|
49
|
+
cd examples && ruby us_street_single_address_example.rb && ruby us_street_multiple_address_example.rb && ruby us_street_component_analysis_example.rb && ruby us_street_iana_timezone_example.rb && ruby us_street_match_strategy_example.rb
|
|
50
|
+
|
|
51
|
+
us_street_match_strategy_api:
|
|
52
|
+
cd examples && ruby us_street_match_strategy_example.rb
|
|
50
53
|
|
|
51
54
|
us_zipcode_api:
|
|
52
55
|
cd examples && ruby us_zipcode_single_lookup_example.rb && ruby us_zipcode_multiple_lookup_example.rb
|
|
53
56
|
|
|
54
57
|
examples: international_autocomplete_api international_postal_code_api international_street_api us_autocomplete_pro_api us_enrichment_api us_extract_api us_reverse_geo_api us_street_api us_zipcode_api
|
|
55
58
|
|
|
56
|
-
.PHONY: clean test dependencies package publish international_autocomplete_api international_postal_code_api international_street_api us_autocomplete_pro_api us_enrichment_api us_extract_api us_reverse_geo_api us_street_api us_zipcode_api examples
|
|
59
|
+
.PHONY: clean test dependencies package publish international_autocomplete_api international_postal_code_api international_street_api us_autocomplete_pro_api us_enrichment_api us_extract_api us_reverse_geo_api us_street_api us_street_match_strategy_api us_zipcode_api examples
|
data/README.md
CHANGED
|
@@ -4,6 +4,8 @@
|
|
|
4
4
|
|
|
5
5
|
The official client libraries for accessing SmartyStreets APIs from Ruby
|
|
6
6
|
|
|
7
|
+
Compatible with Ruby 3.3 and later.
|
|
8
|
+
|
|
7
9
|
You may have noticed this page is curiously sparse. Don't panic, there's [documentation](https://smartystreets.com/docs/sdk/ruby) and [examples](examples).
|
|
8
10
|
|
|
9
11
|
[Apache 2.0 License](LICENSE.txt)
|
|
@@ -3,9 +3,11 @@ require '../lib/smartystreets_ruby_sdk/static_credentials'
|
|
|
3
3
|
require '../lib/smartystreets_ruby_sdk/basic_auth_credentials'
|
|
4
4
|
require '../lib/smartystreets_ruby_sdk/client_builder'
|
|
5
5
|
require '../lib/smartystreets_ruby_sdk/us_autocomplete_pro/lookup'
|
|
6
|
+
require '../lib/smartystreets_ruby_sdk/us_autocomplete_pro/source_type'
|
|
6
7
|
|
|
7
8
|
class USAutocompleteProExample
|
|
8
9
|
Lookup = SmartyStreets::USAutocompletePro::Lookup
|
|
10
|
+
SourceType = SmartyStreets::USAutocompletePro::SourceType
|
|
9
11
|
|
|
10
12
|
def run
|
|
11
13
|
# key = 'Your SmartyStreets Auth Key here'
|
|
@@ -29,7 +31,7 @@ class USAutocompleteProExample
|
|
|
29
31
|
lookup.add_city_filter('Orem,UT')
|
|
30
32
|
lookup.max_results = 5
|
|
31
33
|
lookup.prefer_ratio = 3
|
|
32
|
-
lookup.source =
|
|
34
|
+
lookup.source = SourceType::ALL
|
|
33
35
|
|
|
34
36
|
# lookup.add_custom_parameter('parameter', 'value')
|
|
35
37
|
|
|
@@ -0,0 +1,76 @@
|
|
|
1
|
+
require '../lib/smartystreets_ruby_sdk/static_credentials'
|
|
2
|
+
require '../lib/smartystreets_ruby_sdk/shared_credentials'
|
|
3
|
+
require '../lib/smartystreets_ruby_sdk/basic_auth_credentials'
|
|
4
|
+
require '../lib/smartystreets_ruby_sdk/client_builder'
|
|
5
|
+
require '../lib/smartystreets_ruby_sdk/us_enrichment/lookup'
|
|
6
|
+
|
|
7
|
+
class USEnrichmentBusinessNameSearchExample
|
|
8
|
+
def run
|
|
9
|
+
id = ENV['SMARTY_AUTH_ID']
|
|
10
|
+
token = ENV['SMARTY_AUTH_TOKEN']
|
|
11
|
+
credentials = SmartyStreets::BasicAuthCredentials.new(id, token)
|
|
12
|
+
|
|
13
|
+
client = SmartyStreets::ClientBuilder.new(credentials).build_us_enrichment_api_client
|
|
14
|
+
|
|
15
|
+
business_name = "delta air"
|
|
16
|
+
|
|
17
|
+
lookup = SmartyStreets::USEnrichment::Lookup.new
|
|
18
|
+
lookup.business_name = business_name
|
|
19
|
+
lookup.city = "atlanta"
|
|
20
|
+
|
|
21
|
+
begin
|
|
22
|
+
summary_results = client.send_business_lookup(lookup)
|
|
23
|
+
rescue SmartyStreets::SmartyError => err
|
|
24
|
+
puts err
|
|
25
|
+
return
|
|
26
|
+
end
|
|
27
|
+
|
|
28
|
+
if summary_results.nil? || summary_results.empty?
|
|
29
|
+
puts "No response returned for business-name search"
|
|
30
|
+
return
|
|
31
|
+
end
|
|
32
|
+
|
|
33
|
+
summary = summary_results[0]
|
|
34
|
+
if summary.businesses.nil? || summary.businesses.empty?
|
|
35
|
+
puts "No businesses found for this business name search"
|
|
36
|
+
return
|
|
37
|
+
end
|
|
38
|
+
|
|
39
|
+
puts "Summary results for BusinessName: #{business_name}"
|
|
40
|
+
summary.businesses.each do |biz|
|
|
41
|
+
puts " - #{biz.company_name} (ID: #{biz.business_id})"
|
|
42
|
+
end
|
|
43
|
+
|
|
44
|
+
first = summary.businesses[0]
|
|
45
|
+
puts
|
|
46
|
+
puts "Fetching details for business: #{first.company_name} (ID: #{first.business_id})"
|
|
47
|
+
|
|
48
|
+
begin
|
|
49
|
+
detail_result = client.send_business_detail_lookup(first.business_id)
|
|
50
|
+
rescue SmartyStreets::SmartyError => err
|
|
51
|
+
puts err
|
|
52
|
+
return
|
|
53
|
+
end
|
|
54
|
+
|
|
55
|
+
if detail_result.nil?
|
|
56
|
+
puts "No detail result returned"
|
|
57
|
+
return
|
|
58
|
+
end
|
|
59
|
+
|
|
60
|
+
puts
|
|
61
|
+
puts "Detail result:"
|
|
62
|
+
puts " smarty_key: #{detail_result.smarty_key}"
|
|
63
|
+
puts " data_set_name: #{detail_result.data_set_name}"
|
|
64
|
+
puts " business_id: #{detail_result.business_id}"
|
|
65
|
+
attrs = detail_result.attributes
|
|
66
|
+
return if attrs.nil?
|
|
67
|
+
attrs.instance_variables.each do |var|
|
|
68
|
+
value = attrs.instance_variable_get(var)
|
|
69
|
+
next if value.nil?
|
|
70
|
+
puts " #{var.to_s.delete('@')}: #{value}"
|
|
71
|
+
end
|
|
72
|
+
end
|
|
73
|
+
end
|
|
74
|
+
|
|
75
|
+
example = USEnrichmentBusinessNameSearchExample.new
|
|
76
|
+
example.run
|
|
@@ -40,10 +40,12 @@ class USEnrichmentEtagExample
|
|
|
40
40
|
second = SmartyStreets::USEnrichment::Lookup.new(smarty_key, 'business')
|
|
41
41
|
second.request_etag = captured_etag
|
|
42
42
|
begin
|
|
43
|
-
@client.send_business_lookup(second)
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
43
|
+
results = @client.send_business_lookup(second)
|
|
44
|
+
if results.empty?
|
|
45
|
+
puts " Call 2 (matching Etag): 304 not modified - cache is still valid. Refreshed Etag=#{display(second.response_etag)}"
|
|
46
|
+
else
|
|
47
|
+
puts " Call 2 (matching Etag): 200 - server did NOT honor the conditional. Etag=#{display(second.response_etag)}"
|
|
48
|
+
end
|
|
47
49
|
rescue SmartyStreets::SmartyError => err
|
|
48
50
|
puts " Call 2 unexpected failure: #{err.class}: #{err.message}"
|
|
49
51
|
return nil
|
|
@@ -52,10 +54,12 @@ class USEnrichmentEtagExample
|
|
|
52
54
|
third = SmartyStreets::USEnrichment::Lookup.new(smarty_key, 'business')
|
|
53
55
|
third.request_etag = captured_etag + "X"
|
|
54
56
|
begin
|
|
55
|
-
@client.send_business_lookup(third)
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
57
|
+
results = @client.send_business_lookup(third)
|
|
58
|
+
if results.empty?
|
|
59
|
+
puts " Call 3 (mutated Etag): 304 - UNEXPECTED. Server treated a different Etag as matching."
|
|
60
|
+
else
|
|
61
|
+
puts " Call 3 (mutated Etag): 200 as expected. Etag=#{display(third.response_etag)}"
|
|
62
|
+
end
|
|
59
63
|
rescue SmartyStreets::SmartyError => err
|
|
60
64
|
puts " Call 3 unexpected failure: #{err.class}: #{err.message}"
|
|
61
65
|
end
|
|
@@ -86,9 +90,11 @@ class USEnrichmentEtagExample
|
|
|
86
90
|
second.request_etag = captured_etag
|
|
87
91
|
begin
|
|
88
92
|
@client.send_business_detail_lookup(second)
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
93
|
+
if second.result.nil?
|
|
94
|
+
puts " Call 2 (matching Etag): 304 not modified - cache is still valid. Refreshed Etag=#{display(second.response_etag)}"
|
|
95
|
+
else
|
|
96
|
+
puts " Call 2 (matching Etag): 200 - server did NOT honor the conditional. Etag=#{display(second.response_etag)}"
|
|
97
|
+
end
|
|
92
98
|
rescue SmartyStreets::SmartyError => err
|
|
93
99
|
puts " Call 2 unexpected failure: #{err.class}: #{err.message}"
|
|
94
100
|
return
|
|
@@ -98,9 +104,11 @@ class USEnrichmentEtagExample
|
|
|
98
104
|
third.request_etag = captured_etag + "X"
|
|
99
105
|
begin
|
|
100
106
|
@client.send_business_detail_lookup(third)
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
107
|
+
if third.result.nil?
|
|
108
|
+
puts " Call 3 (mutated Etag): 304 - UNEXPECTED. Server treated a different Etag as matching."
|
|
109
|
+
else
|
|
110
|
+
puts " Call 3 (mutated Etag): 200 as expected. Etag=#{display(third.response_etag)}"
|
|
111
|
+
end
|
|
104
112
|
rescue SmartyStreets::SmartyError => err
|
|
105
113
|
puts " Call 3 unexpected failure: #{err.class}: #{err.message}"
|
|
106
114
|
end
|
|
@@ -154,7 +154,7 @@ class USEnrichmentAddressExample
|
|
|
154
154
|
end
|
|
155
155
|
|
|
156
156
|
def one_liner(obj)
|
|
157
|
-
obj.instance_variables.
|
|
157
|
+
obj.instance_variables.filter do |var|
|
|
158
158
|
value = obj.instance_variable_get(var)
|
|
159
159
|
"#{var.to_s.delete('@')}=#{value}" unless value.nil?
|
|
160
160
|
end.join(' ')
|
|
@@ -3,9 +3,11 @@ require '../lib/smartystreets_ruby_sdk/shared_credentials'
|
|
|
3
3
|
require '../lib/smartystreets_ruby_sdk/basic_auth_credentials'
|
|
4
4
|
require '../lib/smartystreets_ruby_sdk/client_builder'
|
|
5
5
|
require '../lib/smartystreets_ruby_sdk/us_reverse_geo/lookup'
|
|
6
|
+
require '../lib/smartystreets_ruby_sdk/us_reverse_geo/source_type'
|
|
6
7
|
|
|
7
8
|
class USReverseGeoExample
|
|
8
9
|
Lookup = SmartyStreets::USReverseGeo::Lookup
|
|
10
|
+
SourceType = SmartyStreets::USReverseGeo::SourceType
|
|
9
11
|
|
|
10
12
|
def run
|
|
11
13
|
# key = 'Your SmartyStreets Auth Key here'
|
|
@@ -25,6 +27,7 @@ class USReverseGeoExample
|
|
|
25
27
|
# https://smartystreets.com/docs/cloud/us-reverse-geo-api#http-request-input-fields
|
|
26
28
|
|
|
27
29
|
lookup = Lookup.new(40.111111, -111.111111)
|
|
30
|
+
lookup.source = SourceType::ALL
|
|
28
31
|
|
|
29
32
|
# lookup.add_custom_parameter('parameter', 'value')
|
|
30
33
|
|
|
@@ -0,0 +1,85 @@
|
|
|
1
|
+
require '../lib/smartystreets_ruby_sdk/static_credentials'
|
|
2
|
+
require '../lib/smartystreets_ruby_sdk/shared_credentials'
|
|
3
|
+
require '../lib/smartystreets_ruby_sdk/basic_auth_credentials'
|
|
4
|
+
require '../lib/smartystreets_ruby_sdk/client_builder'
|
|
5
|
+
require '../lib/smartystreets_ruby_sdk/batch'
|
|
6
|
+
require '../lib/smartystreets_ruby_sdk/us_street/lookup'
|
|
7
|
+
require '../lib/smartystreets_ruby_sdk/us_street/match_type'
|
|
8
|
+
|
|
9
|
+
class USStreetLookupsWithMatchStrategyExample
|
|
10
|
+
Lookup = SmartyStreets::USStreet::Lookup
|
|
11
|
+
MatchType = SmartyStreets::USStreet::MatchType
|
|
12
|
+
|
|
13
|
+
def run
|
|
14
|
+
# We recommend storing your secret keys in environment variables instead---it's safer!
|
|
15
|
+
id = ENV['SMARTY_AUTH_ID']
|
|
16
|
+
token = ENV['SMARTY_AUTH_TOKEN']
|
|
17
|
+
credentials = SmartyStreets::BasicAuthCredentials.new(id, token)
|
|
18
|
+
client = SmartyStreets::ClientBuilder.new(credentials).build_us_street_api_client
|
|
19
|
+
|
|
20
|
+
# Each address is run through all three match strategies so you can compare how
|
|
21
|
+
# 'strict', 'enhanced', and 'invalid' each handle a valid, an invalid, and an
|
|
22
|
+
# ambiguous address.
|
|
23
|
+
# - strict: only returns candidates that are valid, mailable addresses.
|
|
24
|
+
# - enhanced: returns a more comprehensive dataset (requires a US Core or Rooftop license).
|
|
25
|
+
# - invalid: most permissive; always returns at least one candidate (a best-guess standardization).
|
|
26
|
+
# Documentation for input fields: https://smartystreets.com/docs/cloud/us-street-api
|
|
27
|
+
addresses = [
|
|
28
|
+
['valid (real, deliverable)', '1600 Amphitheatre Pkwy', 'Mountain View', 'CA', '94043'],
|
|
29
|
+
['invalid (no such address)', '9999 W 1150 S', 'Provo', 'UT', '84601'],
|
|
30
|
+
['ambiguous (missing ZIP/unit)', '1 Rosedale St', 'Baltimore', 'MD', '']
|
|
31
|
+
]
|
|
32
|
+
strategies = [MatchType::STRICT, MatchType::ENHANCED, MatchType::INVALID]
|
|
33
|
+
|
|
34
|
+
batch = SmartyStreets::Batch.new
|
|
35
|
+
cases = [] # parallel metadata for each lookup, in the order they are added to the batch
|
|
36
|
+
|
|
37
|
+
addresses.each do |label, street, city, state, zipcode|
|
|
38
|
+
strategies.each do |strategy|
|
|
39
|
+
lookup = Lookup.new
|
|
40
|
+
lookup.street = street
|
|
41
|
+
lookup.city = city
|
|
42
|
+
lookup.state = state
|
|
43
|
+
lookup.zipcode = zipcode
|
|
44
|
+
lookup.match = strategy
|
|
45
|
+
lookup.candidates = 10 # allow ambiguous addresses to return more than one match
|
|
46
|
+
batch.add(lookup)
|
|
47
|
+
cases << [label, "#{street}, #{city}, #{state}", strategy]
|
|
48
|
+
end
|
|
49
|
+
end
|
|
50
|
+
|
|
51
|
+
begin
|
|
52
|
+
client.send_batch(batch)
|
|
53
|
+
rescue SmartyStreets::SmartyError => err
|
|
54
|
+
puts err
|
|
55
|
+
return
|
|
56
|
+
end
|
|
57
|
+
|
|
58
|
+
last_address = nil
|
|
59
|
+
batch.each_with_index do |lookup, i|
|
|
60
|
+
label, address_display, strategy = cases[i]
|
|
61
|
+
|
|
62
|
+
unless address_display == last_address
|
|
63
|
+
puts "\n" + ('=' * 70)
|
|
64
|
+
puts " Address: #{address_display} [#{label}]"
|
|
65
|
+
puts('=' * 70)
|
|
66
|
+
last_address = address_display
|
|
67
|
+
end
|
|
68
|
+
|
|
69
|
+
candidates = lookup.result
|
|
70
|
+
puts "\n--- '#{strategy}' strategy ---"
|
|
71
|
+
|
|
72
|
+
if candidates.empty?
|
|
73
|
+
puts ' 0 candidates - no match returned under this strategy.'
|
|
74
|
+
next
|
|
75
|
+
end
|
|
76
|
+
|
|
77
|
+
puts " #{candidates.length} candidate(s):"
|
|
78
|
+
candidates.each do |candidate|
|
|
79
|
+
puts " [#{candidate.candidate_index}] #{candidate.delivery_line_1} #{candidate.last_line}"
|
|
80
|
+
end
|
|
81
|
+
end
|
|
82
|
+
end
|
|
83
|
+
end
|
|
84
|
+
|
|
85
|
+
USStreetLookupsWithMatchStrategyExample.new.run
|
|
@@ -1,28 +1,33 @@
|
|
|
1
1
|
module SmartyStreets
|
|
2
|
-
NOT_MODIFIED = 'Not Modified:
|
|
2
|
+
NOT_MODIFIED = 'Not Modified: The requested record has not been modified since the previous request' \
|
|
3
|
+
' with the Etag value.'.freeze
|
|
3
4
|
|
|
4
|
-
BAD_CREDENTIALS = 'Unauthorized: The credentials were provided incorrectly or did not match any existing,
|
|
5
|
-
active credentials.'.freeze
|
|
5
|
+
BAD_CREDENTIALS = 'Unauthorized: The credentials were provided incorrectly or did not match any existing,' \
|
|
6
|
+
' active credentials.'.freeze
|
|
6
7
|
|
|
7
|
-
PAYMENT_REQUIRED = 'Payment Required: There is no active subscription
|
|
8
|
-
|
|
8
|
+
PAYMENT_REQUIRED = 'Payment Required: There is no active subscription for the account associated with the' \
|
|
9
|
+
' credentials submitted with the request.'.freeze
|
|
9
10
|
|
|
10
|
-
FORBIDDEN = '
|
|
11
|
-
|
|
11
|
+
FORBIDDEN = 'Forbidden: The request contained valid data and was understood by the server, but the server' \
|
|
12
|
+
' is refusing action.'.freeze
|
|
13
|
+
|
|
14
|
+
REQUEST_TIMEOUT = 'Request timeout error.'.freeze
|
|
12
15
|
|
|
13
16
|
REQUEST_ENTITY_TOO_LARGE = 'Request Entity Too Large: The request body has exceeded the maximum size.'.freeze
|
|
14
17
|
|
|
15
|
-
BAD_REQUEST = 'Bad Request (Malformed Payload): A GET request lacked a
|
|
16
|
-
POST request contained malformed JSON
|
|
18
|
+
BAD_REQUEST = 'Bad Request (Malformed Payload): A GET request lacked a required field or the request body' \
|
|
19
|
+
' of a POST request contained malformed JSON.'.freeze
|
|
17
20
|
|
|
18
21
|
UNPROCESSABLE_ENTITY = 'GET request lacked required fields.'.freeze
|
|
19
22
|
|
|
20
|
-
TOO_MANY_REQUESTS = 'The rate limit has been exceeded.'.freeze
|
|
23
|
+
TOO_MANY_REQUESTS = 'Too Many Requests: The rate limit for your account has been exceeded.'.freeze
|
|
21
24
|
|
|
22
25
|
INTERNAL_SERVER_ERROR = 'Internal Server Error.'.freeze
|
|
23
26
|
|
|
27
|
+
BAD_GATEWAY = 'Bad Gateway error.'.freeze
|
|
28
|
+
|
|
24
29
|
SERVICE_UNAVAILABLE = 'Service Unavailable. Try again later.'.freeze
|
|
25
30
|
|
|
26
31
|
GATEWAY_TIMEOUT = 'The upstream data provider did not respond in a timely fashion and the request failed. ' \
|
|
27
|
-
|
|
32
|
+
'A serious, yet rare occurrence indeed.'.freeze
|
|
28
33
|
end
|
|
@@ -5,11 +5,12 @@ module SmartyStreets
|
|
|
5
5
|
attr_reader :premise, :thoroughfare_trailing_type, :sub_building, :locality, :post_box_number,
|
|
6
6
|
:thoroughfare_name, :thoroughfare_postdirection, :dependent_thoroughfare, :premise_prefix_number,
|
|
7
7
|
:thoroughfare, :dependent_thoroughfare_name, :postal_code_short, :dependent_thoroughfare_trailing_type,
|
|
8
|
-
:administrative_area, :administrative_area_iso2
|
|
8
|
+
:administrative_area, :administrative_area_iso2, :attention, :post_box,
|
|
9
9
|
:building_leading_type, :dependent_locality_name, :thoroughfare_type,
|
|
10
10
|
:dependent_thoroughfare_postdirection, :double_dependent_locality, :premise_number,
|
|
11
11
|
:dependent_thoroughfare_type, :post_box_type, :building, :sub_administrative_area, :postal_code_extra,
|
|
12
12
|
:sub_building_name, :postal_code, :dependent_locality, :premise_type, :sub_building_number,
|
|
13
|
+
:short_address_code, :sub_building_leading_type, :sub_building_block, :sub_building_door, :sub_building_staircase,
|
|
13
14
|
:super_administrative_area, :premise_extra, :dependent_thoroughfare_predirection,
|
|
14
15
|
:building_trailing_type, :thoroughfare_predirection, :building_name, :level_type, :level_number,
|
|
15
16
|
:country_iso_3, :sub_building_type, :additional_content, :delivery_installation, :delivery_installation_type,
|
|
@@ -20,8 +21,7 @@ module SmartyStreets
|
|
|
20
21
|
@super_administrative_area = obj.fetch('super_administrative_area', nil)
|
|
21
22
|
@administrative_area = obj.fetch('administrative_area', nil)
|
|
22
23
|
@administrative_area_iso2 = obj.fetch('administrative_area_iso2', nil)
|
|
23
|
-
@
|
|
24
|
-
@administrative_area_long = obj.fetch('administrative_area_long', nil)
|
|
24
|
+
@attention = obj.fetch('attention', nil)
|
|
25
25
|
@sub_administrative_area = obj.fetch('sub_administrative_area', nil)
|
|
26
26
|
@dependent_locality= obj.fetch('dependent_locality', nil)
|
|
27
27
|
@dependent_locality_name = obj.fetch('dependent_locality_name', nil)
|
|
@@ -35,6 +35,11 @@ module SmartyStreets
|
|
|
35
35
|
@premise_number = obj.fetch('premise_number', nil)
|
|
36
36
|
@premise_prefix_number = obj.fetch('premise_prefix_number', nil)
|
|
37
37
|
@premise_type = obj.fetch('premise_type', nil)
|
|
38
|
+
@short_address_code = obj.fetch('short_address_code', nil)
|
|
39
|
+
@sub_building_leading_type = obj.fetch('sub_building_leading_type', nil)
|
|
40
|
+
@sub_building_block = obj.fetch('sub_building_block', nil)
|
|
41
|
+
@sub_building_door = obj.fetch('sub_building_door', nil)
|
|
42
|
+
@sub_building_staircase = obj.fetch('sub_building_staircase', nil)
|
|
38
43
|
@thoroughfare = obj.fetch('thoroughfare', nil)
|
|
39
44
|
@thoroughfare_predirection = obj.fetch('thoroughfare_predirection', nil)
|
|
40
45
|
@thoroughfare_postdirection = obj.fetch('thoroughfare_postdirection', nil)
|
|
@@ -2,7 +2,7 @@ module SmartyStreets
|
|
|
2
2
|
module InternationalStreet
|
|
3
3
|
class RootLevel
|
|
4
4
|
attr_reader :input_id, :organization, :address1, :address2, :address3, :address4, :address5, :address6, :address7,
|
|
5
|
-
:address8
|
|
5
|
+
:address8
|
|
6
6
|
|
|
7
7
|
def initialize(obj)
|
|
8
8
|
@input_id = obj.fetch('input_id', nil)
|
|
@@ -15,10 +15,6 @@ module SmartyStreets
|
|
|
15
15
|
@address6 = obj.fetch('address6', nil)
|
|
16
16
|
@address7 = obj.fetch('address7', nil)
|
|
17
17
|
@address8 = obj.fetch('address8', nil)
|
|
18
|
-
@address9 = obj.fetch('address9', nil)
|
|
19
|
-
@address10 = obj.fetch('address10', nil)
|
|
20
|
-
@address11 = obj.fetch('address11', nil)
|
|
21
|
-
@address12 = obj.fetch('address12', nil)
|
|
22
18
|
end
|
|
23
19
|
end
|
|
24
20
|
end
|
|
@@ -22,38 +22,38 @@ module SmartyStreets
|
|
|
22
22
|
private
|
|
23
23
|
|
|
24
24
|
def parse_rate_limit_response(response)
|
|
25
|
-
|
|
26
|
-
if !response.payload.nil?
|
|
27
|
-
response_json = JSON.parse(response.payload)
|
|
28
|
-
response_json["errors"].each do |error|
|
|
29
|
-
error_message += (" " + error["message"])
|
|
30
|
-
end
|
|
31
|
-
error_message.strip!
|
|
32
|
-
end
|
|
33
|
-
if error_message == ""
|
|
34
|
-
error_message = TOO_MANY_REQUESTS
|
|
35
|
-
end
|
|
36
|
-
TooManyRequestsError.new(error_message)
|
|
25
|
+
TooManyRequestsError.new(from_message(response, TOO_MANY_REQUESTS))
|
|
37
26
|
end
|
|
38
27
|
|
|
39
28
|
def from_message(response, fallback)
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
29
|
+
body = response.payload.nil? ? '' : response.payload.to_s.strip
|
|
30
|
+
unless body.empty?
|
|
31
|
+
begin
|
|
32
|
+
errors = JSON.parse(response.payload)["errors"]
|
|
33
|
+
rescue JSON::ParserError, TypeError
|
|
34
|
+
errors = nil
|
|
35
|
+
end
|
|
36
|
+
unless errors.nil? || errors.empty?
|
|
37
|
+
message = errors.map { |error| error["message"] }.join(" ")
|
|
38
|
+
return message unless message.empty?
|
|
39
|
+
end
|
|
40
|
+
end
|
|
44
41
|
|
|
45
|
-
|
|
46
|
-
message.empty? ? fallback : message
|
|
42
|
+
"#{fallback} Body: #{body}".strip
|
|
47
43
|
end
|
|
48
44
|
|
|
49
45
|
def assign_exception(response)
|
|
50
|
-
response.error = case response.status_code
|
|
51
|
-
when '304'
|
|
52
|
-
|
|
46
|
+
response.error = case response.status_code.to_s
|
|
47
|
+
when '200', '304'
|
|
48
|
+
nil
|
|
53
49
|
when '401'
|
|
54
50
|
BadCredentialsError.new(from_message(response, BAD_CREDENTIALS))
|
|
55
51
|
when '402'
|
|
56
52
|
PaymentRequiredError.new(from_message(response, PAYMENT_REQUIRED))
|
|
53
|
+
when '403'
|
|
54
|
+
ForbiddenError.new(from_message(response, FORBIDDEN))
|
|
55
|
+
when '408'
|
|
56
|
+
RequestTimeoutError.new(from_message(response, REQUEST_TIMEOUT))
|
|
57
57
|
when '413'
|
|
58
58
|
RequestEntityTooLargeError.new(from_message(response, REQUEST_ENTITY_TOO_LARGE))
|
|
59
59
|
when '400'
|
|
@@ -61,13 +61,17 @@ module SmartyStreets
|
|
|
61
61
|
when '422'
|
|
62
62
|
UnprocessableEntityError.new(from_message(response, UNPROCESSABLE_ENTITY))
|
|
63
63
|
when '429'
|
|
64
|
-
TooManyRequestsError.new(TOO_MANY_REQUESTS)
|
|
64
|
+
TooManyRequestsError.new(from_message(response, TOO_MANY_REQUESTS))
|
|
65
65
|
when '500'
|
|
66
|
-
InternalServerError.new(INTERNAL_SERVER_ERROR)
|
|
66
|
+
InternalServerError.new(from_message(response, INTERNAL_SERVER_ERROR))
|
|
67
|
+
when '502'
|
|
68
|
+
BadGatewayError.new(from_message(response, BAD_GATEWAY))
|
|
67
69
|
when '503'
|
|
68
|
-
ServiceUnavailableError.new(SERVICE_UNAVAILABLE)
|
|
70
|
+
ServiceUnavailableError.new(from_message(response, SERVICE_UNAVAILABLE))
|
|
71
|
+
when '504'
|
|
72
|
+
GatewayTimeoutError.new(from_message(response, GATEWAY_TIMEOUT))
|
|
69
73
|
else
|
|
70
|
-
|
|
74
|
+
SmartyError.new(from_message(response, "The server returned an unexpected HTTP status code: #{response.status_code}"))
|
|
71
75
|
end
|
|
72
76
|
end
|
|
73
77
|
end
|
|
@@ -84,6 +84,7 @@ module SmartyStreets
|
|
|
84
84
|
|
|
85
85
|
response = @sender.send(smarty_request)
|
|
86
86
|
capture_response_etag(response, lookup)
|
|
87
|
+
return lookup.result if response.status_code.to_s == '304'
|
|
87
88
|
results = @serializer.deserialize(response.payload)
|
|
88
89
|
|
|
89
90
|
results = [] if results.nil?
|
|
@@ -114,8 +115,8 @@ module SmartyStreets
|
|
|
114
115
|
private
|
|
115
116
|
|
|
116
117
|
def __send(lookup)
|
|
117
|
-
if lookup.nil? || (blank?(lookup.smarty_key) && blank?(lookup.street) && blank?(lookup.freeform))
|
|
118
|
-
raise SmartyError.new("Lookup requires one of '
|
|
118
|
+
if lookup.nil? || (blank?(lookup.smarty_key) && blank?(lookup.street) && blank?(lookup.freeform) && blank?(lookup.business_name))
|
|
119
|
+
raise SmartyError.new("Lookup requires one of 'smarty_key', 'street', 'freeform', or 'business_name' to be set")
|
|
119
120
|
end
|
|
120
121
|
|
|
121
122
|
smarty_request = Request.new
|
|
@@ -135,6 +136,7 @@ module SmartyStreets
|
|
|
135
136
|
add_parameter(smarty_request, 'city', lookup.city)
|
|
136
137
|
add_parameter(smarty_request, 'state', lookup.state)
|
|
137
138
|
add_parameter(smarty_request, 'zipcode', lookup.zipcode)
|
|
139
|
+
add_parameter(smarty_request, 'business_name', lookup.business_name)
|
|
138
140
|
else
|
|
139
141
|
if (lookup.data_sub_set.nil?)
|
|
140
142
|
smarty_request.url_components = '/' + lookup.smarty_key + '/' + lookup.data_set
|
|
@@ -147,6 +149,7 @@ module SmartyStreets
|
|
|
147
149
|
|
|
148
150
|
response = @sender.send(smarty_request)
|
|
149
151
|
capture_response_etag(response, lookup)
|
|
152
|
+
return [] if response.status_code.to_s == '304'
|
|
150
153
|
results = @serializer.deserialize(response.payload)
|
|
151
154
|
|
|
152
155
|
results = [] if results.nil?
|
|
@@ -28,9 +28,9 @@ module SmartyStreets
|
|
|
28
28
|
end
|
|
29
29
|
|
|
30
30
|
class Lookup < LookupBase
|
|
31
|
-
attr_accessor :smarty_key, :data_set, :data_sub_set, :freeform, :street, :city, :state, :zipcode
|
|
31
|
+
attr_accessor :smarty_key, :data_set, :data_sub_set, :freeform, :street, :city, :state, :zipcode, :business_name
|
|
32
32
|
|
|
33
|
-
def initialize(smarty_key=nil, data_set=nil, data_sub_set=nil, freeform=nil, street=nil, city=nil, state=nil, zipcode=nil, request_etag=nil, features=nil)
|
|
33
|
+
def initialize(smarty_key=nil, data_set=nil, data_sub_set=nil, freeform=nil, street=nil, city=nil, state=nil, zipcode=nil, request_etag=nil, features=nil, business_name=nil)
|
|
34
34
|
super()
|
|
35
35
|
@smarty_key = smarty_key
|
|
36
36
|
@data_set = data_set
|
|
@@ -42,6 +42,7 @@ module SmartyStreets
|
|
|
42
42
|
@zipcode = zipcode
|
|
43
43
|
@request_etag = request_etag
|
|
44
44
|
@features = features
|
|
45
|
+
@business_name = business_name
|
|
45
46
|
end
|
|
46
47
|
end
|
|
47
48
|
|
metadata
CHANGED
|
@@ -1,14 +1,14 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: smartystreets_ruby_sdk
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version:
|
|
4
|
+
version: 9.0.0
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- SmartyStreets SDK Team
|
|
8
8
|
autorequire:
|
|
9
9
|
bindir: exe
|
|
10
10
|
cert_chain: []
|
|
11
|
-
date: 2026-06-
|
|
11
|
+
date: 2026-06-23 00:00:00.000000000 Z
|
|
12
12
|
dependencies:
|
|
13
13
|
- !ruby/object:Gem::Dependency
|
|
14
14
|
name: bundler
|
|
@@ -80,7 +80,9 @@ extensions: []
|
|
|
80
80
|
extra_rdoc_files: []
|
|
81
81
|
files:
|
|
82
82
|
- ".claude/settings.json"
|
|
83
|
-
- ".github/workflows/
|
|
83
|
+
- ".github/workflows/ci.yml"
|
|
84
|
+
- ".github/workflows/publish.yml"
|
|
85
|
+
- ".github/workflows/test.yml"
|
|
84
86
|
- ".gitignore"
|
|
85
87
|
- CHANGELOG.md
|
|
86
88
|
- CLAUDE.md
|
|
@@ -98,12 +100,14 @@ files:
|
|
|
98
100
|
- examples/international_postal_code_example.rb
|
|
99
101
|
- examples/us_autocomplete_pro_example.rb
|
|
100
102
|
- examples/us_enrichment_business_example.rb
|
|
103
|
+
- examples/us_enrichment_business_name_search_example.rb
|
|
101
104
|
- examples/us_enrichment_etag_example.rb
|
|
102
105
|
- examples/us_enrichment_example.rb
|
|
103
106
|
- examples/us_extract_example.rb
|
|
104
107
|
- examples/us_reverse_geo_example.rb
|
|
105
108
|
- examples/us_street_component_analysis_example.rb
|
|
106
109
|
- examples/us_street_iana_timezone_example.rb
|
|
110
|
+
- examples/us_street_match_strategy_example.rb
|
|
107
111
|
- examples/us_street_multiple_address_example.rb
|
|
108
112
|
- examples/us_street_single_address_example.rb
|
|
109
113
|
- examples/us_zipcode_multiple_lookup_example.rb
|
|
@@ -153,6 +157,7 @@ files:
|
|
|
153
157
|
- lib/smartystreets_ruby_sdk/us_autocomplete_pro/client.rb
|
|
154
158
|
- lib/smartystreets_ruby_sdk/us_autocomplete_pro/geolocation_type.rb
|
|
155
159
|
- lib/smartystreets_ruby_sdk/us_autocomplete_pro/lookup.rb
|
|
160
|
+
- lib/smartystreets_ruby_sdk/us_autocomplete_pro/source_type.rb
|
|
156
161
|
- lib/smartystreets_ruby_sdk/us_autocomplete_pro/suggestion.rb
|
|
157
162
|
- lib/smartystreets_ruby_sdk/us_enrichment.rb
|
|
158
163
|
- lib/smartystreets_ruby_sdk/us_enrichment/business/detail/attributes.rb
|
|
@@ -188,6 +193,7 @@ files:
|
|
|
188
193
|
- lib/smartystreets_ruby_sdk/us_reverse_geo/coordinate.rb
|
|
189
194
|
- lib/smartystreets_ruby_sdk/us_reverse_geo/lookup.rb
|
|
190
195
|
- lib/smartystreets_ruby_sdk/us_reverse_geo/result.rb
|
|
196
|
+
- lib/smartystreets_ruby_sdk/us_reverse_geo/source_type.rb
|
|
191
197
|
- lib/smartystreets_ruby_sdk/us_reverse_geo/us_reverse_geo_response.rb
|
|
192
198
|
- lib/smartystreets_ruby_sdk/us_street.rb
|
|
193
199
|
- lib/smartystreets_ruby_sdk/us_street/analysis.rb
|
|
@@ -227,7 +233,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
|
227
233
|
- !ruby/object:Gem::Version
|
|
228
234
|
version: '0'
|
|
229
235
|
requirements: []
|
|
230
|
-
rubygems_version: 3.
|
|
236
|
+
rubygems_version: 3.5.22
|
|
231
237
|
signing_key:
|
|
232
238
|
specification_version: 4
|
|
233
239
|
summary: An official library for the SmartyStreets APIs
|
|
@@ -1,27 +0,0 @@
|
|
|
1
|
-
name: Ruby Gem Publish
|
|
2
|
-
|
|
3
|
-
on:
|
|
4
|
-
push:
|
|
5
|
-
tags:
|
|
6
|
-
- '*'
|
|
7
|
-
jobs:
|
|
8
|
-
publish:
|
|
9
|
-
runs-on: ubuntu-latest
|
|
10
|
-
env:
|
|
11
|
-
GEM_HOST_API_KEY: ${{ secrets.GEM_HOST_API_KEY }}
|
|
12
|
-
|
|
13
|
-
steps:
|
|
14
|
-
- uses: actions/checkout@v3
|
|
15
|
-
with:
|
|
16
|
-
fetch-depth: 0
|
|
17
|
-
|
|
18
|
-
- uses: ruby/setup-ruby@v1
|
|
19
|
-
with:
|
|
20
|
-
ruby-version: '3.2'
|
|
21
|
-
bundler-cache: false
|
|
22
|
-
|
|
23
|
-
- name: Publish
|
|
24
|
-
run: |
|
|
25
|
-
VERSION=${GITHUB_REF#refs/*/} API_KEY=${{ secrets.GEM_HOST_API_KEY }} make publish
|
|
26
|
-
env:
|
|
27
|
-
API_KEY: "${{ secrets.GEM_HOST_API_KEY }}"
|