smartystreets_ruby_sdk 2.0.2 → 3.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.
Files changed (58) hide show
  1. checksums.yaml +4 -4
  2. data/Makefile +1 -1
  3. data/Vagrantfile +4 -2
  4. data/examples/international_example.rb +31 -0
  5. data/examples/us_autocomplete_example.rb +38 -0
  6. data/examples/us_extract_example.rb +52 -0
  7. data/examples/us_street_multiple_address_example.rb +11 -9
  8. data/examples/us_street_single_address_example.rb +3 -3
  9. data/examples/us_zipcode_multiple_lookup_example.rb +7 -5
  10. data/examples/us_zipcode_single_lookup_example.rb +3 -3
  11. data/lib/smartystreets_ruby_sdk/batch.rb +7 -3
  12. data/lib/smartystreets_ruby_sdk/client_builder.rb +118 -0
  13. data/lib/smartystreets_ruby_sdk/errors.rb +15 -7
  14. data/lib/smartystreets_ruby_sdk/exceptions.rb +12 -5
  15. data/lib/smartystreets_ruby_sdk/international_street.rb +10 -0
  16. data/lib/smartystreets_ruby_sdk/international_street/analysis.rb +13 -0
  17. data/lib/smartystreets_ruby_sdk/international_street/candidate.rb +33 -0
  18. data/lib/smartystreets_ruby_sdk/international_street/client.rb +56 -0
  19. data/lib/smartystreets_ruby_sdk/international_street/components.rb +55 -0
  20. data/lib/smartystreets_ruby_sdk/international_street/language_mode.rb +5 -0
  21. data/lib/smartystreets_ruby_sdk/international_street/lookup.rb +80 -0
  22. data/lib/smartystreets_ruby_sdk/international_street/metadata.rb +14 -0
  23. data/lib/smartystreets_ruby_sdk/json_able.rb +3 -3
  24. data/lib/smartystreets_ruby_sdk/native_sender.rb +6 -2
  25. data/lib/smartystreets_ruby_sdk/request.rb +2 -1
  26. data/lib/smartystreets_ruby_sdk/retry_sender.rb +5 -7
  27. data/lib/smartystreets_ruby_sdk/us_autocomplete.rb +7 -0
  28. data/lib/smartystreets_ruby_sdk/us_autocomplete/client.rb +66 -0
  29. data/lib/smartystreets_ruby_sdk/us_autocomplete/geolocation_type.rb +5 -0
  30. data/lib/smartystreets_ruby_sdk/us_autocomplete/lookup.rb +34 -0
  31. data/lib/smartystreets_ruby_sdk/us_autocomplete/suggestion.rb +14 -0
  32. data/lib/smartystreets_ruby_sdk/us_extract.rb +8 -0
  33. data/lib/smartystreets_ruby_sdk/us_extract/address.rb +22 -0
  34. data/lib/smartystreets_ruby_sdk/us_extract/client.rb +46 -0
  35. data/lib/smartystreets_ruby_sdk/us_extract/lookup.rb +20 -0
  36. data/lib/smartystreets_ruby_sdk/us_extract/metadata.rb +15 -0
  37. data/lib/smartystreets_ruby_sdk/us_extract/result.rb +19 -0
  38. data/lib/smartystreets_ruby_sdk/us_street.rb +1 -1
  39. data/lib/smartystreets_ruby_sdk/us_street/analysis.rb +1 -0
  40. data/lib/smartystreets_ruby_sdk/us_street/candidate.rb +1 -0
  41. data/lib/smartystreets_ruby_sdk/us_street/client.rb +9 -5
  42. data/lib/smartystreets_ruby_sdk/us_street/components.rb +3 -0
  43. data/lib/smartystreets_ruby_sdk/us_street/lookup.rb +6 -0
  44. data/lib/smartystreets_ruby_sdk/us_street/match_type.rb +5 -0
  45. data/lib/smartystreets_ruby_sdk/us_street/metadata.rb +1 -0
  46. data/lib/smartystreets_ruby_sdk/us_zipcode.rb +1 -1
  47. data/lib/smartystreets_ruby_sdk/us_zipcode/alternate_county.rb +13 -0
  48. data/lib/smartystreets_ruby_sdk/us_zipcode/city.rb +2 -0
  49. data/lib/smartystreets_ruby_sdk/us_zipcode/client.rb +12 -4
  50. data/lib/smartystreets_ruby_sdk/us_zipcode/lookup.rb +4 -0
  51. data/lib/smartystreets_ruby_sdk/us_zipcode/result.rb +3 -2
  52. data/lib/smartystreets_ruby_sdk/us_zipcode/zip_code.rb +16 -3
  53. data/lib/smartystreets_ruby_sdk/version.rb +1 -1
  54. metadata +28 -7
  55. data/lib/smartystreets_ruby_sdk/core_client_builder.rb +0 -57
  56. data/lib/smartystreets_ruby_sdk/match_type.rb +0 -5
  57. data/lib/smartystreets_ruby_sdk/us_street/client_builder.rb +0 -15
  58. data/lib/smartystreets_ruby_sdk/us_zipcode/client_builder.rb +0 -15
@@ -4,25 +4,32 @@ end
4
4
  class BadCredentialsError < SmartyException
5
5
  end
6
6
 
7
- class PaymentRequiredError < SmartyException
7
+ class ForbiddenError < SmartyException
8
8
  end
9
9
 
10
+ class PaymentRequiredError < SmartyException
11
+ end
10
12
 
11
13
  class RequestEntityTooLargeError < SmartyException
12
14
  end
13
15
 
14
-
15
16
  class BadRequestError < SmartyException
16
17
  end
17
18
 
19
+ class UnprocessableEntityError < SmartyException
20
+ end
18
21
 
19
22
  class TooManyRequestsError < SmartyException
20
23
  end
21
24
 
22
-
23
25
  class InternalServerError < SmartyException
24
26
  end
25
27
 
26
-
27
28
  class ServiceUnavailableError < SmartyException
28
- end
29
+ end
30
+
31
+ class GatewayTimeoutError < SmartyException
32
+ end
33
+
34
+ class BatchFullError < SmartyException
35
+ end
@@ -0,0 +1,10 @@
1
+ require_relative './international_street/lookup'
2
+ require_relative './international_street/metadata'
3
+ require_relative './international_street/analysis'
4
+ require_relative './international_street/components'
5
+ require_relative './international_street/candidate'
6
+ require_relative './international_street/client'
7
+ require_relative './international_street/language_mode'
8
+
9
+ module InternationalStreet
10
+ end
@@ -0,0 +1,13 @@
1
+ module InternationalStreet
2
+ # See "https://smartystreets.com/docs/cloud/international-street-api#analysis"
3
+ class Analysis
4
+
5
+ attr_reader :max_address_precision, :verification_status, :address_precision
6
+
7
+ def initialize(obj)
8
+ @verification_status = obj.fetch('verification_status', nil)
9
+ @address_precision = obj.fetch('address_precision', nil)
10
+ @max_address_precision = obj.fetch('max_address_precision', nil)
11
+ end
12
+ end
13
+ end
@@ -0,0 +1,33 @@
1
+ require_relative 'components'
2
+ require_relative 'metadata'
3
+ require_relative 'analysis'
4
+
5
+ module InternationalStreet
6
+ # A candidate is a possible match for an address that was submitted. A lookup can have multiple
7
+ # candidates if the address was ambiguous.
8
+ #
9
+ # See "https://smartystreets.com/docs/cloud/international-street-api#root"
10
+ class Candidate
11
+ attr_reader :metadata, :address3, :address11, :address2, :address12, :address1, :address10,
12
+ :address9, :address8, :address7, :organization, :address6, :address5, :address4, :components, :analysis
13
+
14
+ def initialize(obj)
15
+ @organization = obj.fetch('organization', nil)
16
+ @address1 = obj.fetch('address1', nil)
17
+ @address2 = obj.fetch('address2', nil)
18
+ @address3 = obj.fetch('address3', nil)
19
+ @address4 = obj.fetch('address4', nil)
20
+ @address5 = obj.fetch('address5', nil)
21
+ @address6 = obj.fetch('address6', nil)
22
+ @address7 = obj.fetch('address7', nil)
23
+ @address8 = obj.fetch('address8', nil)
24
+ @address9 = obj.fetch('address9', nil)
25
+ @address10 = obj.fetch('address10', nil)
26
+ @address11 = obj.fetch('address11', nil)
27
+ @address12 = obj.fetch('address12', nil)
28
+ @components = Components.new(obj.fetch('components', {}))
29
+ @metadata = Metadata.new(obj.fetch('metadata', {}))
30
+ @analysis = Analysis.new(obj.fetch('analysis', {}))
31
+ end
32
+ end
33
+ end
@@ -0,0 +1,56 @@
1
+ require_relative '../request'
2
+ require_relative 'candidate'
3
+
4
+ module InternationalStreet
5
+ # It is recommended to instantiate this class using ClientBuilder.build_international_street_api_client()
6
+ class Client
7
+ def initialize(sender, serializer)
8
+ @sender = sender
9
+ @serializer = serializer
10
+ end
11
+
12
+ # Sends a Lookup object to the International Street API and stores the result in the Lookup's result field.
13
+ def send(lookup)
14
+ lookup.ensure_enough_info
15
+ request = build_request(lookup)
16
+
17
+ response = @sender.send(request)
18
+
19
+ candidates = convert_candidates(@serializer.deserialize(response.payload))
20
+ lookup.result = candidates
21
+ end
22
+
23
+ def build_request(lookup)
24
+ request = Request.new
25
+
26
+ add_parameter(request, 'country', lookup.country)
27
+ add_parameter(request, 'geocode', lookup.geocode.to_s)
28
+ add_parameter(request, 'language', lookup.language)
29
+ add_parameter(request, 'freeform', lookup.freeform)
30
+ add_parameter(request, 'address1', lookup.address1)
31
+ add_parameter(request, 'address2', lookup.address2)
32
+ add_parameter(request, 'address3', lookup.address3)
33
+ add_parameter(request, 'address4', lookup.address4)
34
+ add_parameter(request, 'organization', lookup.organization)
35
+ add_parameter(request, 'locality', lookup.locality)
36
+ add_parameter(request, 'administrative_area', lookup.administrative_area)
37
+ add_parameter(request, 'postal_code', lookup.postal_code)
38
+
39
+ request
40
+ end
41
+
42
+ def add_parameter(request, key, value)
43
+ request.parameters[key] = value unless value.nil? or value.empty?
44
+ end
45
+
46
+ def convert_candidates(raw_candidates)
47
+ candidates = []
48
+
49
+ raw_candidates.each do |candidate|
50
+ candidates.push(Candidate.new(candidate))
51
+ end
52
+
53
+ candidates
54
+ end
55
+ end
56
+ end
@@ -0,0 +1,55 @@
1
+ module InternationalStreet
2
+ # See "https://smartystreets.com/docs/cloud/international-street-api#components"
3
+ class Components
4
+ attr_reader :premise, :thoroughfare_trailing_type, :sub_building, :locality, :post_box_number,
5
+ :thoroughfare_name, :thoroughfare_postdirection, :dependent_thoroughfare,
6
+ :thoroughfare, :dependent_thoroughfare_name, :postal_code_short, :dependent_thoroughfare_trailing_type,
7
+ :administrative_area, :post_box, :building_leading_type, :dependent_locality_name, :thoroughfare_type,
8
+ :dependent_thoroughfare_postdirection, :double_dependent_locality, :premise_number,
9
+ :dependent_thoroughfare_type, :post_box_type, :building, :sub_administrative_area, :postal_code_extra,
10
+ :sub_building_name, :postal_code, :dependent_locality, :premise_type, :sub_building_number,
11
+ :super_administrative_area, :premise_extra, :dependent_thoroughfare_predirection,
12
+ :building_trailing_type, :thoroughfare_predirection, :building_name, :country_iso_3, :sub_building_type
13
+
14
+ def initialize(obj)
15
+ @country_iso_3 = obj.fetch('country_iso_3', nil)
16
+ @super_administrative_area = obj.fetch('super_administrative_area', nil)
17
+ @administrative_area = obj.fetch('administrative_area', nil)
18
+ @sub_administrative_area = obj.fetch('sub_administrative_area', nil)
19
+ @dependent_locality= obj.fetch('dependent_locality', nil)
20
+ @dependent_locality_name = obj.fetch('dependent_locality_name', nil)
21
+ @double_dependent_locality = obj.fetch('double_dependent_locality', nil)
22
+ @locality = obj.fetch('locality', nil)
23
+ @postal_code = obj.fetch('postal_code', nil)
24
+ @postal_code_short = obj.fetch('postal_code_short', nil)
25
+ @postal_code_extra = obj.fetch('postal_code_extra', nil)
26
+ @premise = obj.fetch('premise', nil)
27
+ @premise_extra = obj.fetch('premise_extra', nil)
28
+ @premise_number = obj.fetch('premise_number', nil)
29
+ @premise_type = obj.fetch('premise_type', nil)
30
+ @thoroughfare = obj.fetch('thoroughfare', nil)
31
+ @thoroughfare_predirection = obj.fetch('thoroughfare_predirection', nil)
32
+ @thoroughfare_postdirection = obj.fetch('thoroughfare_postdirection', nil)
33
+ @thoroughfare_name = obj.fetch('thoroughfare_name', nil)
34
+ @thoroughfare_trailing_type = obj.fetch('thoroughfare_trailing_type', nil)
35
+ @thoroughfare_type = obj.fetch('thoroughfare_type', nil)
36
+ @dependent_thoroughfare = obj.fetch('dependent_thoroughfare', nil)
37
+ @dependent_thoroughfare_predirection = obj.fetch('dependent_thoroughfare_predirection', nil)
38
+ @dependent_thoroughfare_postdirection = obj.fetch('dependent_thoroughfare_postdirection', nil)
39
+ @dependent_thoroughfare_name = obj.fetch('dependent_thoroughfare_name', nil)
40
+ @dependent_thoroughfare_trailing_type = obj.fetch('dependent_thoroughfare_trailing_type', nil)
41
+ @dependent_thoroughfare_type = obj.fetch('dependent_thoroughfare_type', nil)
42
+ @building = obj.fetch('building', nil)
43
+ @building_leading_type = obj.fetch('building_leading_type', nil)
44
+ @building_name = obj.fetch('building_name', nil)
45
+ @building_trailing_type = obj.fetch('building_trailing_type', nil)
46
+ @sub_building_type = obj.fetch('sub_building_type', nil)
47
+ @sub_building_number = obj.fetch('sub_building_number', nil)
48
+ @sub_building_name = obj.fetch('sub_building_name', nil)
49
+ @sub_building = obj.fetch('sub_building', nil)
50
+ @post_box = obj.fetch('post_box', nil)
51
+ @post_box_type = obj.fetch('post_box_type', nil)
52
+ @post_box_number = obj.fetch('post_box_number', nil)
53
+ end
54
+ end
55
+ end
@@ -0,0 +1,5 @@
1
+ module LanguageMode
2
+ NATIVE = 'native'.freeze
3
+
4
+ LATIN = 'latin'.freeze
5
+ end
@@ -0,0 +1,80 @@
1
+ module InternationalStreet
2
+ # In addition to holding all of the input data for this lookup, this class also will contain the
3
+ # result of the lookup after it comes back from the API.
4
+ #
5
+ # Note: Lookups must have certain required fields set with non-blank values.
6
+ # These can be found at the URL below.
7
+ #
8
+ # See "https://smartystreets.com/docs/cloud/international-street-api#http-input-fields"
9
+ #
10
+ # @geocode:: Disabled by default. Set to true to enable.
11
+ # @language:: When not set, the output language will match the language of the input values.
12
+ # When set to language_mode.NATIVE, the results will always be in the language of the output country.
13
+ # When set to language_mode.LATIN, the results will always be provided using a Latin character set.
14
+ class Lookup
15
+
16
+ attr_accessor :freeform, :locality, :postal_code, :address3, :address2, :inputId, :address1,
17
+ :geocode, :administrative_area, :country, :organization, :language, :address4, :result
18
+
19
+ def initialize(freeform=nil, country=nil)
20
+ @result = []
21
+
22
+ @inputId = nil
23
+ @country = country
24
+ @geocode = nil
25
+ @language = nil
26
+ @freeform = freeform
27
+ @address1 = nil
28
+ @address2 = nil
29
+ @address3 = nil
30
+ @address4 = nil
31
+ @organization = nil
32
+ @locality = nil
33
+ @administrative_area = nil
34
+ @postal_code = nil
35
+ end
36
+
37
+ def missing_country
38
+ field_is_missing(@country)
39
+ end
40
+
41
+ def has_freeform
42
+ field_is_set(@freeform)
43
+ end
44
+
45
+ def missing_address1
46
+ field_is_missing(@address1)
47
+ end
48
+
49
+ def has_postal_code
50
+ field_is_set(@postal_code)
51
+ end
52
+
53
+ def missing_locality_or_administrative_area
54
+ field_is_missing(@locality) or field_is_missing(@administrative_area)
55
+ end
56
+
57
+ def field_is_missing(field)
58
+ field.nil? or field.empty?
59
+ end
60
+
61
+ def field_is_set(field)
62
+ not field_is_missing(field)
63
+ end
64
+
65
+ def ensure_enough_info
66
+ raise UnprocessableEntityError, 'Country field is required.' if missing_country
67
+
68
+ return true if has_freeform
69
+
70
+ raise UnprocessableEntityError, 'Either freeform or address1 is required.' if missing_address1
71
+
72
+ return true if has_postal_code
73
+
74
+ if missing_locality_or_administrative_area
75
+ raise UnprocessableEntityError, 'Insufficient information:'\
76
+ 'One or more required fields were not set on the lookup.'
77
+ end
78
+ end
79
+ end
80
+ end
@@ -0,0 +1,14 @@
1
+ module InternationalStreet
2
+ # See "https://smartystreets.com/docs/cloud/international-street-api#metadata"
3
+ class Metadata
4
+
5
+ attr_reader :longitude, :geocode_precision, :max_geocode_precision, :latitude
6
+
7
+ def initialize(obj)
8
+ @latitude = obj.fetch('latitude', nil)
9
+ @longitude = obj.fetch('longitude', nil)
10
+ @geocode_precision = obj.fetch('geocode_precision', nil)
11
+ @max_geocode_precision = obj.fetch('max_geocode_precision', nil)
12
+ end
13
+ end
14
+ end
@@ -3,15 +3,15 @@ require 'json'
3
3
  class JSONAble
4
4
  def to_json(options={})
5
5
  hash = {}
6
- self.instance_variables.each do |var|
7
- hash[var.to_s.delete('@')] = self.instance_variable_get var
6
+ instance_variables.each do |var|
7
+ hash[var.to_s.delete('@')] = instance_variable_get var
8
8
  end
9
9
  hash.to_json
10
10
  end
11
11
 
12
12
  def from_json!(string)
13
13
  JSON.load(string).each do |var, val|
14
- self.instance_variable_set var, val
14
+ instance_variable_set var, val
15
15
  end
16
16
  end
17
17
  end
@@ -29,11 +29,15 @@ class NativeSender
29
29
 
30
30
  def self.build_request(smarty_request)
31
31
  query = create_query(smarty_request)
32
- request = Net::HTTP::Post.new(URI.parse("#{smarty_request.url_prefix}?#{query}"))
32
+ if smarty_request.payload.nil?
33
+ request = Net::HTTP::Get.new(URI.parse("#{smarty_request.url_prefix}?#{query}"))
34
+ else
35
+ request = Net::HTTP::Post.new(URI.parse("#{smarty_request.url_prefix}?#{query}"))
36
+ end
33
37
  request.content_type = 'application/json'
34
38
  request.body = smarty_request.payload
35
39
  request['User-Agent'] = "smartystreets (sdk:ruby@#{SmartystreetsRubySdk::VERSION})"
36
- request['Referer'] = smarty_request.referer if smarty_request.referer != nil
40
+ request['Referer'] = smarty_request.referer unless smarty_request.referer.nil?
37
41
  set_custom_headers(smarty_request.headers, request)
38
42
  request
39
43
  end
@@ -1,5 +1,5 @@
1
1
  class Request
2
- attr_accessor :parameters, :payload, :url_prefix, :referer, :headers
2
+ attr_accessor :parameters, :payload, :url_prefix, :referer, :headers, :content_type
3
3
 
4
4
  def initialize
5
5
  @parameters = {}
@@ -7,5 +7,6 @@ class Request
7
7
  @url_prefix = nil
8
8
  @referer = nil
9
9
  @headers = {}
10
+ @content_type = 'application/json'
10
11
  end
11
12
  end
@@ -1,6 +1,6 @@
1
1
  class RetrySender
2
2
  MAX_BACKOFF_DURATION = 10
3
- STATUS_OK = '200'
3
+ STATUS_OK = '200'.freeze
4
4
 
5
5
  def initialize(max_retries, inner, sleeper, logger)
6
6
  @max_retries = max_retries
@@ -12,17 +12,15 @@ class RetrySender
12
12
  def send(request)
13
13
  response = @inner.send(request)
14
14
 
15
- (0..@max_retries-1).each { |i|
16
- if response.status_code == STATUS_OK
17
- break
18
- end
15
+ (0..@max_retries-1).each do |i|
16
+ break if response.status_code == STATUS_OK
19
17
 
20
18
  backoff(i)
21
19
 
22
20
  response = @inner.send(request)
23
- }
21
+ end
24
22
 
25
- response
23
+ response
26
24
  end
27
25
 
28
26
  def backoff(attempt)
@@ -0,0 +1,7 @@
1
+ require_relative './us_autocomplete/lookup'
2
+ require_relative './us_autocomplete/geolocation_type'
3
+ require_relative './us_autocomplete/suggestion'
4
+ require_relative './us_autocomplete/client'
5
+
6
+ module USAutocomplete
7
+ end
@@ -0,0 +1,66 @@
1
+ require_relative '../request'
2
+ require_relative '../exceptions'
3
+ require_relative 'geolocation_type'
4
+ require_relative 'suggestion'
5
+
6
+ module USAutocomplete
7
+ # It is recommended to instantiate this class using ClientBuilder.build_us_autocomplete_api_client
8
+ class Client
9
+ def initialize(sender, serializer)
10
+ @sender = sender
11
+ @serializer = serializer
12
+ end
13
+
14
+ # Sends a Lookup object to the US Autocomplete API and stores the result in the Lookup's result field.
15
+ def send(lookup)
16
+ if not lookup or not lookup.prefix
17
+ raise SmartyException, 'Send() must be passed a Lookup with the prefix field set.'
18
+ end
19
+
20
+ request = build_request(lookup)
21
+
22
+ response = @sender.send(request)
23
+
24
+ result = @serializer.deserialize(response.payload)
25
+ suggestions = convert_suggestions(result.fetch('suggestions', []))
26
+ lookup.result = suggestions
27
+ end
28
+
29
+
30
+ def build_request(lookup)
31
+ request = Request.new
32
+
33
+ add_parameter(request, 'prefix', lookup.prefix)
34
+ add_parameter(request, 'suggestions', lookup.max_suggestions.to_s)
35
+ add_parameter(request, 'city_filter', build_filter_string(lookup.city_filter))
36
+ add_parameter(request, 'state_filter', build_filter_string(lookup.state_filter))
37
+ add_parameter(request, 'prefer', build_filter_string(lookup.prefer))
38
+ if lookup.geolocate_type != GeolocationType::NONE
39
+ request.parameters['geolocate'] = 'true'
40
+ request.parameters['geolocate_precision'] = lookup.geolocate_type
41
+ else
42
+ request.parameters['geolocate'] = 'false'
43
+ end
44
+
45
+ request
46
+ end
47
+
48
+ def build_filter_string(filter_list)
49
+ filter_list ? filter_list.join(',') : nil
50
+ end
51
+
52
+ def convert_suggestions(suggestion_hashes)
53
+ converted_suggestions = []
54
+
55
+ suggestion_hashes.each do |suggestion|
56
+ converted_suggestions.push(USAutocomplete::Suggestion.new(suggestion))
57
+ end
58
+
59
+ converted_suggestions
60
+ end
61
+
62
+ def add_parameter(request, key, value)
63
+ request.parameters[key] = value unless value.nil? or value.empty?
64
+ end
65
+ end
66
+ end