smartystreets_ruby_sdk 8.1.1 → 9.1.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.
Files changed (38) hide show
  1. checksums.yaml +4 -4
  2. data/.github/workflows/ci.yml +10 -0
  3. data/.github/workflows/publish.yml +29 -0
  4. data/.github/workflows/test.yml +21 -0
  5. data/CLAUDE.md +2 -2
  6. data/Makefile +11 -5
  7. data/README.md +2 -0
  8. data/examples/us_autocomplete_example.rb +69 -0
  9. data/examples/us_autocomplete_pro_example.rb +3 -1
  10. data/examples/us_enrichment_business_name_search_example.rb +76 -0
  11. data/examples/us_enrichment_etag_example.rb +22 -14
  12. data/examples/us_enrichment_example.rb +1 -1
  13. data/examples/us_reverse_geo_example.rb +3 -0
  14. data/examples/us_street_match_strategy_example.rb +85 -0
  15. data/lib/smartystreets_ruby_sdk/client_builder.rb +7 -0
  16. data/lib/smartystreets_ruby_sdk/errors.rb +16 -11
  17. data/lib/smartystreets_ruby_sdk/exceptions.rb +6 -0
  18. data/lib/smartystreets_ruby_sdk/international_street/components.rb +8 -3
  19. data/lib/smartystreets_ruby_sdk/international_street/rootlevel.rb +1 -5
  20. data/lib/smartystreets_ruby_sdk/native_serializer.rb +1 -0
  21. data/lib/smartystreets_ruby_sdk/status_code_sender.rb +29 -25
  22. data/lib/smartystreets_ruby_sdk/us_autocomplete/client.rb +82 -0
  23. data/lib/smartystreets_ruby_sdk/us_autocomplete/geolocation_type.rb +8 -0
  24. data/lib/smartystreets_ruby_sdk/us_autocomplete/lookup.rb +73 -0
  25. data/lib/smartystreets_ruby_sdk/us_autocomplete/source_type.rb +8 -0
  26. data/lib/smartystreets_ruby_sdk/us_autocomplete/suggestion.rb +21 -0
  27. data/lib/smartystreets_ruby_sdk/us_autocomplete.rb +10 -0
  28. data/lib/smartystreets_ruby_sdk/us_autocomplete_pro/source_type.rb +8 -0
  29. data/lib/smartystreets_ruby_sdk/us_autocomplete_pro.rb +1 -0
  30. data/lib/smartystreets_ruby_sdk/us_enrichment/client.rb +5 -2
  31. data/lib/smartystreets_ruby_sdk/us_enrichment/lookup.rb +3 -2
  32. data/lib/smartystreets_ruby_sdk/us_reverse_geo/source_type.rb +8 -0
  33. data/lib/smartystreets_ruby_sdk/us_reverse_geo/us_reverse_geo_response.rb +1 -1
  34. data/lib/smartystreets_ruby_sdk/us_reverse_geo.rb +1 -0
  35. data/lib/smartystreets_ruby_sdk/version.rb +1 -1
  36. data/lib/smartystreets_ruby_sdk.rb +1 -0
  37. metadata +17 -4
  38. data/.github/workflows/gem-publish.yml +0 -27
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: efc93c57b151ba68bae6b932c750aa6dd99c152d8461777be6de4e99bc3c92ab
4
- data.tar.gz: 2109c0a80c243df5141d2569c4d48f19f854cca030a8a5be771fd2128b9aeff4
3
+ metadata.gz: f033c107bfea0fb806b080faed0a58d7d9e2a062acc88f74b162fd0b2f4db73a
4
+ data.tar.gz: eb2ec5974ad3143e1abd197daea71fb6ac817eaf9bc2b4e84a1cd34d2c621012
5
5
  SHA512:
6
- metadata.gz: 23260f7cf247a188e01fb3dca36a0644c8019fb2a72968d2545825cd27044c4785e98722f259415bdf9ef720b0d2a7a9099482a185585bb2c4bd0c92cf789e62
7
- data.tar.gz: 794bc979c32379a448b2330451438c92239e63706cb3a15ddaab0f2738c85736611ca6cd85f583545922c4268aeb22371e8fa7a6944cb161f98db5c425335dfd
6
+ metadata.gz: 4d6cdd50b585f1bec44a2ca6cf8391613774c9bea95f6537343f9565f884fd56a976cc91ebb089c9d426b3f762e0fb4bf23ec3c2e0084bbadc3179234ab3108d
7
+ data.tar.gz: 2c45d8292d54cf45b2f1add26eecc7ba8de652f4668d448fcf404b9371910ba9e9638e5804fae06d8decd3e70dd1dbb95abe67c1e7ada889076cb4fe71adc90f
@@ -0,0 +1,10 @@
1
+ name: Ruby Tests
2
+
3
+ on:
4
+ push:
5
+ branches:
6
+ - '**'
7
+
8
+ jobs:
9
+ test:
10
+ uses: ./.github/workflows/test.yml
@@ -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
 
@@ -58,7 +58,7 @@ URLPrefixSender → CustomQuerySender → LicenseSender → RetrySender → Sign
58
58
 
59
59
  ### Supported APIs
60
60
 
61
- - US Street, US ZipCode, US Autocomplete Pro, US Extract, US Reverse Geo, US Enrichment
61
+ - US Street, US ZipCode, US Autocomplete, US Autocomplete Pro, US Extract, US Reverse Geo, US Enrichment
62
62
  - International Street, International Autocomplete, International Postal Code
63
63
 
64
64
  ### Test Structure
data/Makefile CHANGED
@@ -12,7 +12,7 @@ test:
12
12
  dependencies:
13
13
  gem install minitest
14
14
 
15
- package: clean dependencies test
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)"
@@ -33,11 +33,14 @@ international_postal_code_api:
33
33
  international_street_api:
34
34
  cd examples && ruby international_example.rb
35
35
 
36
+ us_autocomplete_api:
37
+ cd examples && ruby us_autocomplete_example.rb
38
+
36
39
  us_autocomplete_pro_api:
37
40
  cd examples && ruby us_autocomplete_pro_example.rb
38
41
 
39
42
  us_enrichment_api:
40
- cd examples && ruby us_enrichment_example.rb && ruby us_enrichment_business_example.rb && ruby us_enrichment_etag_example.rb
43
+ 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
44
 
42
45
  us_extract_api:
43
46
  cd examples && ruby us_extract_example.rb
@@ -46,11 +49,14 @@ us_reverse_geo_api:
46
49
  cd examples && ruby us_reverse_geo_example.rb
47
50
 
48
51
  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
52
+ 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
53
+
54
+ us_street_match_strategy_api:
55
+ cd examples && ruby us_street_match_strategy_example.rb
50
56
 
51
57
  us_zipcode_api:
52
58
  cd examples && ruby us_zipcode_single_lookup_example.rb && ruby us_zipcode_multiple_lookup_example.rb
53
59
 
54
- 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
60
+ examples: international_autocomplete_api international_postal_code_api international_street_api us_autocomplete_pro_api us_autocomplete_api us_enrichment_api us_extract_api us_reverse_geo_api us_street_api us_zipcode_api
55
61
 
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
62
+ .PHONY: clean test dependencies package publish international_autocomplete_api international_postal_code_api international_street_api us_autocomplete_pro_api us_autocomplete_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)
@@ -0,0 +1,69 @@
1
+ require '../lib/smartystreets_ruby_sdk/shared_credentials'
2
+ require '../lib/smartystreets_ruby_sdk/static_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_autocomplete/lookup'
6
+ require '../lib/smartystreets_ruby_sdk/us_autocomplete/source_type'
7
+
8
+ # This example is for US Autocomplete (V2). It has the same name as a previous product
9
+ # which has been deprecated since 2022, which we refer to as US Autocomplete Basic.
10
+ # If you are still using US Autocomplete Basic, this SDK will not work.
11
+ class USAutocompleteExample
12
+ Lookup = SmartyStreets::USAutocomplete::Lookup
13
+ SourceType = SmartyStreets::USAutocomplete::SourceType
14
+
15
+ def run
16
+ # key = 'Your SmartyStreets Auth Key here'
17
+ # referer = 'Your host name here'
18
+ # We recommend storing your secret keys in environment variables instead---it's safer!
19
+ # key = ENV['SMARTY_AUTH_WEB']
20
+ # referer = ENV['SMARTY_AUTH_REFERER']
21
+ # credentials = SmartyStreets::SharedCredentials.new(key, referer)
22
+
23
+ id = ENV['SMARTY_AUTH_ID']
24
+ token = ENV['SMARTY_AUTH_TOKEN']
25
+ credentials = SmartyStreets::BasicAuthCredentials.new(id, token)
26
+
27
+ client = SmartyStreets::ClientBuilder.new(credentials).build_us_autocomplete_api_client
28
+
29
+ # Documentation for input fields can be found at:
30
+ # https://www.smarty.com/docs/apis/us-autocomplete-v2/reference#http-request-input-fields
31
+
32
+ lookup = Lookup.new('1042 W Center')
33
+ lookup.add_city_filter('Denver,Aurora,CO')
34
+ lookup.add_city_filter('Orem,UT')
35
+ lookup.max_results = 5
36
+ lookup.prefer_ratio = 3
37
+ lookup.source = SourceType::ALL
38
+
39
+ # lookup.add_custom_parameter('parameter', 'value')
40
+
41
+ suggestions = client.send(lookup) # The client will also return the suggestions directly
42
+
43
+ puts
44
+ puts '*** Result with some filters ***'
45
+ puts
46
+
47
+ entry_id = nil
48
+ suggestions.each do |suggestion|
49
+ puts "#{suggestion.street_line} #{suggestion.city}, #{suggestion.state}"
50
+ entry_id = suggestion.entry_id if suggestion.entry_id && !suggestion.entry_id.empty?
51
+ end
52
+
53
+ # Expand the secondaries of a result that has an entry_id by passing it back as the selected address.
54
+ if entry_id && !entry_id.empty?
55
+ lookup.selected = entry_id
56
+ suggestions = client.send(lookup)
57
+
58
+ puts
59
+ puts '*** Secondaries ***'
60
+ puts
61
+
62
+ suggestions.each do |suggestion|
63
+ puts "#{suggestion.street_line} #{suggestion.city}, #{suggestion.state}"
64
+ end
65
+ end
66
+ end
67
+ end
68
+
69
+ USAutocompleteExample.new.run
@@ -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 = "all"
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
- puts " Call 2 (matching Etag): 200 - server did NOT honor the conditional. Etag=#{display(second.response_etag)}"
45
- rescue SmartyStreets::NotModifiedInfo => ex
46
- puts " Call 2 (matching Etag): 304 NotModifiedInfo - caller treats this as cache-valid. Refreshed Etag=#{display(ex.response_etag)}"
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
- puts " Call 3 (mutated Etag): 200 as expected. Etag=#{display(third.response_etag)}"
57
- rescue SmartyStreets::NotModifiedInfo
58
- puts " Call 3 (mutated Etag): 304 - UNEXPECTED. Server treated a different Etag as matching."
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
- puts " Call 2 (matching Etag): 200 - server did NOT honor the conditional. Etag=#{display(second.response_etag)}"
90
- rescue SmartyStreets::NotModifiedInfo => ex
91
- puts " Call 2 (matching Etag): 304 NotModifiedInfo - caller treats this as cache-valid. Refreshed Etag=#{display(ex.response_etag)}"
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
- puts " Call 3 (mutated Etag): 200 as expected. Etag=#{display(third.response_etag)}"
102
- rescue SmartyStreets::NotModifiedInfo
103
- puts " Call 3 (mutated Etag): 304 - UNEXPECTED. Server treated a different Etag as matching."
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.filter_map do |var|
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
@@ -17,6 +17,7 @@ require_relative 'international_street/client'
17
17
  require_relative 'international_autocomplete/client'
18
18
  require_relative 'us_reverse_geo/client'
19
19
  require_relative 'us_autocomplete_pro/client'
20
+ require_relative 'us_autocomplete/client'
20
21
  require_relative 'us_enrichment/client'
21
22
  require_relative 'international_postal_code/client'
22
23
 
@@ -28,6 +29,7 @@ module SmartyStreets
28
29
  INTERNATIONAL_STREET_API_URL = 'https://international-street.api.smarty.com/verify'.freeze
29
30
  INTERNATIONAL_AUTOCOMPLETE_API_URL = "https://international-autocomplete.api.smarty.com/v2/lookup".freeze
30
31
  US_AUTOCOMPLETE_PRO_API_URL = 'https://us-autocomplete-pro.api.smarty.com/lookup'.freeze
32
+ US_AUTOCOMPLETE_API_URL = 'https://us-autocomplete.api.smarty.com/v2/lookup'.freeze
31
33
  US_EXTRACT_API_URL = 'https://us-extract.api.smarty.com/'.freeze
32
34
  US_STREET_API_URL = 'https://us-street.api.smarty.com/street-address'.freeze
33
35
  US_ZIP_CODE_API_URL = 'https://us-zipcode.api.smarty.com/lookup'.freeze
@@ -185,6 +187,11 @@ module SmartyStreets
185
187
  USAutocompletePro::Client.new(build_sender, @serializer)
186
188
  end
187
189
 
190
+ def build_us_autocomplete_api_client
191
+ ensure_url_prefix_not_null(US_AUTOCOMPLETE_API_URL)
192
+ USAutocomplete::Client.new(build_sender, @serializer)
193
+ end
194
+
188
195
  def build_us_extract_api_client
189
196
  ensure_url_prefix_not_null(US_EXTRACT_API_URL)
190
197
  USExtract::Client.new(build_sender, @serializer)
@@ -1,28 +1,33 @@
1
1
  module SmartyStreets
2
- NOT_MODIFIED = 'Not Modified: This data has not been modified since the last request. Please remove the etag header to fetch this data'.freeze
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
- for the account associated with the credentials submitted with the request.'.freeze
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 = 'Because the international service is currently in a limited release phase, only approved accounts' \
11
- ' may access the service.'.freeze
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 street field or the request body of a
16
- POST request contained malformed JSON, possibly because a value was submitted as a number rather than as a string.'.freeze
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
- 'A serious, yet rare occurrence indeed.'.freeze
32
+ 'A serious, yet rare occurrence indeed.'.freeze
28
33
  end
@@ -18,6 +18,12 @@ module SmartyStreets
18
18
  class ForbiddenError < SmartyError
19
19
  end
20
20
 
21
+ class RequestTimeoutError < SmartyError
22
+ end
23
+
24
+ class BadGatewayError < SmartyError
25
+ end
26
+
21
27
  class PaymentRequiredError < SmartyError
22
28
  end
23
29
 
@@ -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,:administrative_area_short, :administrative_area_long, :post_box,
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
- @administrative_area_short = obj.fetch('administrative_area_short', nil)
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, :address9, :address10, :address11, :address12
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
@@ -7,6 +7,7 @@ module SmartyStreets
7
7
  end
8
8
 
9
9
  def deserialize(payload)
10
+ return {} if payload.nil? || payload.empty?
10
11
  JSON.load(payload)
11
12
  end
12
13
  end
@@ -22,38 +22,38 @@ module SmartyStreets
22
22
  private
23
23
 
24
24
  def parse_rate_limit_response(response)
25
- error_message = ""
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
- return fallback if response.payload.nil?
41
-
42
- errors = JSON.parse(response.payload)["errors"]
43
- return fallback if errors.nil? || errors.empty?
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
- message = errors.map { |error| error["message"] }.join(" ")
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
- NotModifiedInfo.new(NOT_MODIFIED, response.find_header('etag'))
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
- nil
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
@@ -0,0 +1,82 @@
1
+ require_relative '../request'
2
+ require_relative '../exceptions'
3
+ require_relative 'geolocation_type'
4
+ require_relative 'suggestion'
5
+
6
+ module SmartyStreets
7
+ module USAutocomplete
8
+ # It is recommended to instantiate this class using ClientBuilder.build_us_autocomplete_api_client
9
+ class Client
10
+ def initialize(sender, serializer)
11
+ @sender = sender
12
+ @serializer = serializer
13
+ end
14
+
15
+ # Sends a Lookup object to the US Autocomplete API and stores the result in the Lookup's result field.
16
+ def send(lookup)
17
+ if not lookup or not lookup.search
18
+ raise SmartyStreets::SmartyError, 'Send() must be passed a Lookup with the search field set.'
19
+ end
20
+
21
+ request = build_request(lookup)
22
+
23
+ response = @sender.send(request)
24
+
25
+ raise response.error if response.error
26
+
27
+ result = @serializer.deserialize(response.payload)
28
+ suggestions = convert_suggestions(result.fetch('suggestions', []))
29
+ lookup.result = suggestions
30
+ end
31
+
32
+
33
+ def build_request(lookup)
34
+ request = Request.new
35
+
36
+ add_parameter(request, 'search', lookup.search)
37
+ add_parameter(request, 'max_results', lookup.max_results.to_s)
38
+ add_parameter(request, 'include_only_cities', build_filter_string(lookup.city_filter))
39
+ add_parameter(request, 'include_only_states', build_filter_string(lookup.state_filter))
40
+ add_parameter(request, 'include_only_zip_codes', build_filter_string(lookup.zip_filter))
41
+ add_parameter(request, 'exclude_states', build_filter_string(lookup.exclude_states))
42
+ add_parameter(request, 'prefer_cities', build_filter_string(lookup.prefer_cities))
43
+ add_parameter(request, 'prefer_states', build_filter_string(lookup.prefer_states))
44
+ add_parameter(request, 'prefer_zip_codes', build_filter_string(lookup.prefer_zip_codes))
45
+ add_parameter(request, 'prefer_ratio', lookup.prefer_ratio.to_s)
46
+ add_parameter(request, 'source', lookup.source)
47
+ if lookup.prefer_zip_codes.any? || lookup.zip_filter.any?
48
+ request.parameters['prefer_geolocation'] = GeolocationType::NONE
49
+ else
50
+ add_parameter(request, 'prefer_geolocation', lookup.prefer_geolocation)
51
+ end
52
+ add_parameter(request, 'selected', lookup.selected)
53
+ add_parameter(request, 'exclude', build_filter_string(lookup.exclude, ','))
54
+
55
+ for key in lookup.custom_param_hash.keys do
56
+ add_parameter(request, key, lookup.custom_param_hash[key])
57
+ end
58
+
59
+ request
60
+ end
61
+
62
+ def build_filter_string(filter_list, separator=';')
63
+ filter_list ? filter_list.join(separator) : nil
64
+ end
65
+
66
+ def convert_suggestions(suggestion_hashes)
67
+ converted_suggestions = []
68
+ return converted_suggestions if suggestion_hashes.nil?
69
+
70
+ suggestion_hashes.each do |suggestion|
71
+ converted_suggestions.push(USAutocomplete::Suggestion.new(suggestion))
72
+ end
73
+
74
+ converted_suggestions
75
+ end
76
+
77
+ def add_parameter(request, key, value)
78
+ request.parameters[key] = value unless value.nil? or value.empty?
79
+ end
80
+ end
81
+ end
82
+ end
@@ -0,0 +1,8 @@
1
+ module SmartyStreets
2
+ module USAutocomplete
3
+ module GeolocationType
4
+ CITY = 'city'
5
+ NONE = 'none'
6
+ end
7
+ end
8
+ end
@@ -0,0 +1,73 @@
1
+ require_relative '../json_able'
2
+
3
+ module SmartyStreets
4
+ module USAutocomplete
5
+ # In addition to holding all of the input data for this lookup, this class also will contain the result
6
+ # of the lookup after it comes back from the API.
7
+ #
8
+ # See "https://www.smarty.com/docs/apis/us-autocomplete-v2/reference#http-request-input-fields"
9
+ class Lookup < JSONAble
10
+
11
+ attr_accessor :result, :search, :max_results, :city_filter, :state_filter, :zip_filter,
12
+ :exclude_states, :prefer_cities, :prefer_states, :prefer_zip_codes, :prefer_ratio,
13
+ :prefer_geolocation, :selected, :exclude, :source, :custom_param_hash
14
+
15
+ def initialize(search=nil, max_results=nil, city_filter=nil, state_filter=nil, zip_filter=nil,
16
+ exclude_states=nil, prefer_cities=nil, prefer_states=nil, prefer_zips=nil, prefer_ratio=nil,
17
+ prefer_geolocation=nil, selected=nil, exclude=nil, source=nil, custom_param_hash=nil)
18
+ @result = []
19
+ @search = search
20
+ @max_results = max_results
21
+ @city_filter = city_filter ? city_filter : []
22
+ @state_filter = state_filter ? state_filter : []
23
+ @zip_filter = zip_filter ? zip_filter : []
24
+ @exclude_states = exclude_states ? exclude_states : []
25
+ @prefer_cities = prefer_cities ? prefer_cities : []
26
+ @prefer_states = prefer_states ? prefer_states : []
27
+ @prefer_zip_codes = prefer_zips ? prefer_zips : []
28
+ @prefer_ratio = prefer_ratio
29
+ @prefer_geolocation = prefer_geolocation
30
+ @selected = selected
31
+ @exclude = exclude ? exclude : []
32
+ @source = source
33
+ @custom_param_hash = {}
34
+ end
35
+
36
+ def add_custom_parameter(parameter, value)
37
+ @custom_param_hash[parameter] = value
38
+ end
39
+
40
+ def add_city_filter(city)
41
+ @city_filter.push(city)
42
+ end
43
+
44
+ def add_state_filter(state)
45
+ @state_filter.push(state)
46
+ end
47
+
48
+ def add_zip_filter(zip)
49
+ @zip_filter.push(zip)
50
+ end
51
+
52
+ def add_state_exclusion(state)
53
+ @exclude_states.push(state)
54
+ end
55
+
56
+ def add_preferred_city(city)
57
+ @prefer_cities.push(city)
58
+ end
59
+
60
+ def add_preferred_state(state)
61
+ @prefer_states.push(state)
62
+ end
63
+
64
+ def add_preferred_zip(zip)
65
+ @prefer_zip_codes.push(zip)
66
+ end
67
+
68
+ def add_exclude(exclude_type)
69
+ @exclude.push(exclude_type)
70
+ end
71
+ end
72
+ end
73
+ end
@@ -0,0 +1,8 @@
1
+ module SmartyStreets
2
+ module USAutocomplete
3
+ module SourceType
4
+ ALL = 'all'.freeze
5
+ POSTAL = 'postal'.freeze
6
+ end
7
+ end
8
+ end
@@ -0,0 +1,21 @@
1
+ module SmartyStreets
2
+ module USAutocomplete
3
+ # See "https://www.smarty.com/docs/apis/us-autocomplete-v2/reference#http-response-status"
4
+ class Suggestion
5
+
6
+ attr_reader :smarty_key, :entry_id, :street_line, :secondary, :city, :state, :zipcode, :entries, :source
7
+
8
+ def initialize(obj)
9
+ @smarty_key = obj.fetch('smarty_key', nil)
10
+ @entry_id = obj.fetch('entry_id', nil)
11
+ @street_line = obj.fetch('street_line', nil)
12
+ @secondary = obj.fetch('secondary', nil)
13
+ @city = obj.fetch('city', nil)
14
+ @state = obj.fetch('state', nil)
15
+ @zipcode = obj.fetch('zipcode', nil)
16
+ @entries = obj.fetch('entries', 0)
17
+ @source = obj.fetch('source', nil)
18
+ end
19
+ end
20
+ end
21
+ end
@@ -0,0 +1,10 @@
1
+ require_relative './us_autocomplete/lookup'
2
+ require_relative './us_autocomplete/geolocation_type'
3
+ require_relative './us_autocomplete/source_type'
4
+ require_relative './us_autocomplete/suggestion'
5
+ require_relative './us_autocomplete/client'
6
+
7
+ module SmartyStreets
8
+ module USAutocomplete
9
+ end
10
+ end
@@ -0,0 +1,8 @@
1
+ module SmartyStreets
2
+ module USAutocompletePro
3
+ module SourceType
4
+ ALL = 'all'.freeze
5
+ POSTAL = 'postal'.freeze
6
+ end
7
+ end
8
+ end
@@ -1,5 +1,6 @@
1
1
  require_relative './us_autocomplete_pro/lookup'
2
2
  require_relative './us_autocomplete_pro/geolocation_type'
3
+ require_relative './us_autocomplete_pro/source_type'
3
4
  require_relative './us_autocomplete_pro/suggestion'
4
5
  require_relative './us_autocomplete_pro/client'
5
6
 
@@ -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 'smartykey', 'street', or 'freeform' to be set")
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
 
@@ -0,0 +1,8 @@
1
+ module SmartyStreets
2
+ module USReverseGeo
3
+ module SourceType
4
+ ALL = 'all'.freeze
5
+ POSTAL = 'postal'.freeze
6
+ end
7
+ end
8
+ end
@@ -8,7 +8,7 @@ module SmartyStreets
8
8
  def initialize(obj)
9
9
  @results = []
10
10
 
11
- obj['results'].each do |result|
11
+ obj.fetch('results', []).each do |result|
12
12
  @results.push(Result.new(result))
13
13
  end
14
14
  end
@@ -1,4 +1,5 @@
1
1
  require_relative './us_reverse_geo/address'
2
+ require_relative './us_reverse_geo/source_type'
2
3
  require_relative './us_reverse_geo/client'
3
4
  require_relative './us_reverse_geo/coordinate'
4
5
  require_relative './us_reverse_geo/lookup'
@@ -1,3 +1,3 @@
1
1
  module SmartyStreets
2
- VERSION = '8.1.1' # DO NOT EDIT (this is updated by a build job when a new release is published)
2
+ VERSION = '9.1.0' # DO NOT EDIT (this is updated by a build job when a new release is published)
3
3
  end
@@ -24,6 +24,7 @@ require 'smartystreets_ruby_sdk/us_extract'
24
24
  require 'smartystreets_ruby_sdk/us_street'
25
25
  require 'smartystreets_ruby_sdk/us_zipcode'
26
26
  require 'smartystreets_ruby_sdk/us_autocomplete_pro'
27
+ require 'smartystreets_ruby_sdk/us_autocomplete'
27
28
  require 'smartystreets_ruby_sdk/international_street'
28
29
  require 'smartystreets_ruby_sdk/international_autocomplete'
29
30
  require 'smartystreets_ruby_sdk/international_postal_code'
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: 8.1.1
4
+ version: 9.1.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-08 00:00:00.000000000 Z
11
+ date: 2026-06-24 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/gem-publish.yml"
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
@@ -96,14 +98,17 @@ files:
96
98
  - examples/international_autocomplete_example.rb
97
99
  - examples/international_example.rb
98
100
  - examples/international_postal_code_example.rb
101
+ - examples/us_autocomplete_example.rb
99
102
  - examples/us_autocomplete_pro_example.rb
100
103
  - examples/us_enrichment_business_example.rb
104
+ - examples/us_enrichment_business_name_search_example.rb
101
105
  - examples/us_enrichment_etag_example.rb
102
106
  - examples/us_enrichment_example.rb
103
107
  - examples/us_extract_example.rb
104
108
  - examples/us_reverse_geo_example.rb
105
109
  - examples/us_street_component_analysis_example.rb
106
110
  - examples/us_street_iana_timezone_example.rb
111
+ - examples/us_street_match_strategy_example.rb
107
112
  - examples/us_street_multiple_address_example.rb
108
113
  - examples/us_street_single_address_example.rb
109
114
  - examples/us_zipcode_multiple_lookup_example.rb
@@ -149,10 +154,17 @@ files:
149
154
  - lib/smartystreets_ruby_sdk/static_credentials.rb
150
155
  - lib/smartystreets_ruby_sdk/status_code_sender.rb
151
156
  - lib/smartystreets_ruby_sdk/url_prefix_sender.rb
157
+ - lib/smartystreets_ruby_sdk/us_autocomplete.rb
158
+ - lib/smartystreets_ruby_sdk/us_autocomplete/client.rb
159
+ - lib/smartystreets_ruby_sdk/us_autocomplete/geolocation_type.rb
160
+ - lib/smartystreets_ruby_sdk/us_autocomplete/lookup.rb
161
+ - lib/smartystreets_ruby_sdk/us_autocomplete/source_type.rb
162
+ - lib/smartystreets_ruby_sdk/us_autocomplete/suggestion.rb
152
163
  - lib/smartystreets_ruby_sdk/us_autocomplete_pro.rb
153
164
  - lib/smartystreets_ruby_sdk/us_autocomplete_pro/client.rb
154
165
  - lib/smartystreets_ruby_sdk/us_autocomplete_pro/geolocation_type.rb
155
166
  - lib/smartystreets_ruby_sdk/us_autocomplete_pro/lookup.rb
167
+ - lib/smartystreets_ruby_sdk/us_autocomplete_pro/source_type.rb
156
168
  - lib/smartystreets_ruby_sdk/us_autocomplete_pro/suggestion.rb
157
169
  - lib/smartystreets_ruby_sdk/us_enrichment.rb
158
170
  - lib/smartystreets_ruby_sdk/us_enrichment/business/detail/attributes.rb
@@ -188,6 +200,7 @@ files:
188
200
  - lib/smartystreets_ruby_sdk/us_reverse_geo/coordinate.rb
189
201
  - lib/smartystreets_ruby_sdk/us_reverse_geo/lookup.rb
190
202
  - lib/smartystreets_ruby_sdk/us_reverse_geo/result.rb
203
+ - lib/smartystreets_ruby_sdk/us_reverse_geo/source_type.rb
191
204
  - lib/smartystreets_ruby_sdk/us_reverse_geo/us_reverse_geo_response.rb
192
205
  - lib/smartystreets_ruby_sdk/us_street.rb
193
206
  - lib/smartystreets_ruby_sdk/us_street/analysis.rb
@@ -227,7 +240,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
227
240
  - !ruby/object:Gem::Version
228
241
  version: '0'
229
242
  requirements: []
230
- rubygems_version: 3.4.19
243
+ rubygems_version: 3.5.22
231
244
  signing_key:
232
245
  specification_version: 4
233
246
  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 }}"