smartystreets_ruby_sdk 0.0.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (98) hide show
  1. checksums.yaml +7 -0
  2. data/.github/workflows/gem-publish.yml +58 -0
  3. data/.gitignore +4 -0
  4. data/CHANGELOG.md +5 -0
  5. data/Dockerfile +10 -0
  6. data/Gemfile +4 -0
  7. data/LICENSE.md +202 -0
  8. data/Makefile +33 -0
  9. data/README.md +31 -0
  10. data/Rakefile +5 -0
  11. data/bin/console +14 -0
  12. data/bin/setup +8 -0
  13. data/docker-compose.yml +12 -0
  14. data/examples/international_autocomplete_example.rb +47 -0
  15. data/examples/international_example.rb +53 -0
  16. data/examples/us_autocomplete_pro_example.rb +56 -0
  17. data/examples/us_extract_example.rb +63 -0
  18. data/examples/us_reverse_geo_example.rb +47 -0
  19. data/examples/us_street_multiple_address_example.rb +87 -0
  20. data/examples/us_street_single_address_example.rb +67 -0
  21. data/examples/us_zipcode_multiple_lookup_example.rb +80 -0
  22. data/examples/us_zipcode_single_lookup_example.rb +55 -0
  23. data/lib/smartystreets_ruby_sdk/batch.rb +59 -0
  24. data/lib/smartystreets_ruby_sdk/client_builder.rb +188 -0
  25. data/lib/smartystreets_ruby_sdk/custom_header_sender.rb +13 -0
  26. data/lib/smartystreets_ruby_sdk/errors.rb +27 -0
  27. data/lib/smartystreets_ruby_sdk/exceptions.rb +39 -0
  28. data/lib/smartystreets_ruby_sdk/international_autocomplete/client.rb +61 -0
  29. data/lib/smartystreets_ruby_sdk/international_autocomplete/lookup.rb +21 -0
  30. data/lib/smartystreets_ruby_sdk/international_autocomplete/suggestion.rb +16 -0
  31. data/lib/smartystreets_ruby_sdk/international_street/analysis.rb +17 -0
  32. data/lib/smartystreets_ruby_sdk/international_street/candidate.rb +23 -0
  33. data/lib/smartystreets_ruby_sdk/international_street/changes.rb +15 -0
  34. data/lib/smartystreets_ruby_sdk/international_street/client.rb +63 -0
  35. data/lib/smartystreets_ruby_sdk/international_street/components.rb +58 -0
  36. data/lib/smartystreets_ruby_sdk/international_street/language_mode.rb +9 -0
  37. data/lib/smartystreets_ruby_sdk/international_street/lookup.rb +82 -0
  38. data/lib/smartystreets_ruby_sdk/international_street/metadata.rb +17 -0
  39. data/lib/smartystreets_ruby_sdk/international_street/rootlevel.rb +25 -0
  40. data/lib/smartystreets_ruby_sdk/international_street.rb +12 -0
  41. data/lib/smartystreets_ruby_sdk/json_able.rb +19 -0
  42. data/lib/smartystreets_ruby_sdk/license_sender.rb +15 -0
  43. data/lib/smartystreets_ruby_sdk/logger.rb +7 -0
  44. data/lib/smartystreets_ruby_sdk/native_sender.rb +85 -0
  45. data/lib/smartystreets_ruby_sdk/native_serializer.rb +13 -0
  46. data/lib/smartystreets_ruby_sdk/proxy.rb +16 -0
  47. data/lib/smartystreets_ruby_sdk/request.rb +14 -0
  48. data/lib/smartystreets_ruby_sdk/response.rb +11 -0
  49. data/lib/smartystreets_ruby_sdk/retry_sender.rb +39 -0
  50. data/lib/smartystreets_ruby_sdk/shared_credentials.rb +13 -0
  51. data/lib/smartystreets_ruby_sdk/signing_sender.rb +13 -0
  52. data/lib/smartystreets_ruby_sdk/sleeper.rb +7 -0
  53. data/lib/smartystreets_ruby_sdk/static_credentials.rb +13 -0
  54. data/lib/smartystreets_ruby_sdk/status_code_sender.rb +41 -0
  55. data/lib/smartystreets_ruby_sdk/url_prefix_sender.rb +14 -0
  56. data/lib/smartystreets_ruby_sdk/us_autocomplete/client.rb +72 -0
  57. data/lib/smartystreets_ruby_sdk/us_autocomplete/geolocation_type.rb +9 -0
  58. data/lib/smartystreets_ruby_sdk/us_autocomplete/lookup.rb +38 -0
  59. data/lib/smartystreets_ruby_sdk/us_autocomplete/suggestion.rb +16 -0
  60. data/lib/smartystreets_ruby_sdk/us_autocomplete.rb +9 -0
  61. data/lib/smartystreets_ruby_sdk/us_autocomplete_pro/client.rb +78 -0
  62. data/lib/smartystreets_ruby_sdk/us_autocomplete_pro/geolocation_type.rb +8 -0
  63. data/lib/smartystreets_ruby_sdk/us_autocomplete_pro/lookup.rb +63 -0
  64. data/lib/smartystreets_ruby_sdk/us_autocomplete_pro/suggestion.rb +18 -0
  65. data/lib/smartystreets_ruby_sdk/us_autocomplete_pro.rb +10 -0
  66. data/lib/smartystreets_ruby_sdk/us_extract/address.rb +24 -0
  67. data/lib/smartystreets_ruby_sdk/us_extract/client.rb +49 -0
  68. data/lib/smartystreets_ruby_sdk/us_extract/lookup.rb +22 -0
  69. data/lib/smartystreets_ruby_sdk/us_extract/metadata.rb +17 -0
  70. data/lib/smartystreets_ruby_sdk/us_extract/result.rb +21 -0
  71. data/lib/smartystreets_ruby_sdk/us_extract.rb +10 -0
  72. data/lib/smartystreets_ruby_sdk/us_reverse_geo/address.rb +16 -0
  73. data/lib/smartystreets_ruby_sdk/us_reverse_geo/client.rb +38 -0
  74. data/lib/smartystreets_ruby_sdk/us_reverse_geo/coordinate.rb +25 -0
  75. data/lib/smartystreets_ruby_sdk/us_reverse_geo/lookup.rb +21 -0
  76. data/lib/smartystreets_ruby_sdk/us_reverse_geo/result.rb +20 -0
  77. data/lib/smartystreets_ruby_sdk/us_reverse_geo/us_reverse_geo_response.rb +17 -0
  78. data/lib/smartystreets_ruby_sdk/us_reverse_geo.rb +12 -0
  79. data/lib/smartystreets_ruby_sdk/us_street/analysis.rb +24 -0
  80. data/lib/smartystreets_ruby_sdk/us_street/candidate.rb +27 -0
  81. data/lib/smartystreets_ruby_sdk/us_street/client.rb +78 -0
  82. data/lib/smartystreets_ruby_sdk/us_street/components.rb +35 -0
  83. data/lib/smartystreets_ruby_sdk/us_street/lookup.rb +32 -0
  84. data/lib/smartystreets_ruby_sdk/us_street/match_type.rb +10 -0
  85. data/lib/smartystreets_ruby_sdk/us_street/metadata.rb +30 -0
  86. data/lib/smartystreets_ruby_sdk/us_street.rb +12 -0
  87. data/lib/smartystreets_ruby_sdk/us_zipcode/alternate_county.rb +15 -0
  88. data/lib/smartystreets_ruby_sdk/us_zipcode/city.rb +16 -0
  89. data/lib/smartystreets_ruby_sdk/us_zipcode/client.rb +72 -0
  90. data/lib/smartystreets_ruby_sdk/us_zipcode/lookup.rb +21 -0
  91. data/lib/smartystreets_ruby_sdk/us_zipcode/result.rb +47 -0
  92. data/lib/smartystreets_ruby_sdk/us_zipcode/zip_code.rb +33 -0
  93. data/lib/smartystreets_ruby_sdk/us_zipcode.rb +11 -0
  94. data/lib/smartystreets_ruby_sdk/version.rb +3 -0
  95. data/lib/smartystreets_ruby_sdk.rb +33 -0
  96. data/ruby-sdk-demo.json +354 -0
  97. data/smartystreets_ruby_sdk.gemspec +26 -0
  98. metadata +201 -0
@@ -0,0 +1,61 @@
1
+ require_relative '../request'
2
+ require_relative '../exceptions'
3
+ require_relative 'suggestion'
4
+
5
+ module SmartyStreets
6
+ module InternationalAutocomplete
7
+ # It is recommended to instantiate this class using ClientBuilder.build_international_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 International Autocomplete API and stores the result in the Lookup's result field.
15
+ def send(lookup)
16
+ if not lookup or not lookup.search
17
+ raise SmartyStreets::SmartyError, '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
+ raise response.error if response.error
25
+
26
+ result = @serializer.deserialize(response.payload)
27
+ suggestions = convert_suggestions(result.fetch('candidates', []))
28
+ lookup.result = suggestions
29
+ end
30
+
31
+
32
+ def build_request(lookup)
33
+ request = Request.new
34
+
35
+ add_parameter(request, 'search', lookup.search)
36
+ add_parameter(request, 'country', lookup.country)
37
+ add_parameter(request, 'include_only_administrative_area', lookup.administrative_area)
38
+ add_parameter(request, 'include_only_locality', lookup.locality)
39
+ add_parameter(request, 'include_only_postal_code', lookup.postal_code)
40
+
41
+ request
42
+ end
43
+
44
+ def convert_suggestions(suggestion_hashes)
45
+ converted_suggestions = []
46
+ return converted_suggestions if suggestion_hashes.nil?
47
+
48
+ suggestion_hashes.each do |suggestion|
49
+ converted_suggestions.push(InternationalAutocomplete::Suggestion.new(suggestion))
50
+ end
51
+
52
+ converted_suggestions
53
+ end
54
+
55
+ def add_parameter(request, key, value)
56
+ request.parameters[key] = value unless value.nil? or value.empty?
57
+ end
58
+ end
59
+ end
60
+ end
61
+
@@ -0,0 +1,21 @@
1
+ require_relative '../json_able'
2
+
3
+ module SmartyStreets
4
+ module InternationalAutocomplete
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
+ class Lookup < JSONAble
8
+
9
+ attr_accessor :result, :search, :country, :administrative_area, :locality, :postal_code
10
+
11
+ def initialize(search=nil, country=nil, administrative_area=nil, locality=nil, postal_code=nil)
12
+ @result = []
13
+ @search = search
14
+ @country = country
15
+ @administrative_area = administrative_area
16
+ @locality = locality
17
+ @postal_code = postal_code
18
+ end
19
+ end
20
+ end
21
+ end
@@ -0,0 +1,16 @@
1
+ module SmartyStreets
2
+ module InternationalAutocomplete
3
+ class Suggestion
4
+
5
+ attr_reader :street, :locality, :administrative_area, :postal_code, :country_iso3
6
+
7
+ def initialize(obj)
8
+ @street = obj.fetch('street', nil)
9
+ @locality = obj.fetch('locality', nil)
10
+ @administrative_area = obj.fetch('administrative_area', nil)
11
+ @postal_code = obj.fetch('postal_code', nil)
12
+ @country_iso3 = obj.fetch('country_iso3', nil)
13
+ end
14
+ end
15
+ end
16
+ end
@@ -0,0 +1,17 @@
1
+ require_relative 'changes'
2
+ module SmartyStreets
3
+ module InternationalStreet
4
+ # See "https://smartystreets.com/docs/cloud/international-street-api#analysis"
5
+ class Analysis
6
+
7
+ attr_reader :max_address_precision, :verification_status, :address_precision, :changes
8
+
9
+ def initialize(obj)
10
+ @verification_status = obj.fetch('verification_status', nil)
11
+ @address_precision = obj.fetch('address_precision', nil)
12
+ @max_address_precision = obj.fetch('max_address_precision', nil)
13
+ @changes = Changes.new(obj.fetch('changes', {}))
14
+ end
15
+ end
16
+ end
17
+ end
@@ -0,0 +1,23 @@
1
+ require_relative 'components'
2
+ require_relative 'metadata'
3
+ require_relative 'analysis'
4
+ require_relative 'rootlevel'
5
+
6
+ module SmartyStreets
7
+ module InternationalStreet
8
+ # A candidate is a possible match for an address that was submitted. A lookup can have multiple
9
+ # candidates if the address was ambiguous.
10
+ #
11
+ # See "https://smartystreets.com/docs/cloud/international-street-api#root"
12
+ class Candidate < RootLevel
13
+ attr_reader :metadata, :components, :analysis
14
+
15
+ def initialize(obj)
16
+ @components = Components.new(obj.fetch('components', {}))
17
+ @metadata = Metadata.new(obj.fetch('metadata', {}))
18
+ @analysis = Analysis.new(obj.fetch('analysis', {}))
19
+ super(obj)
20
+ end
21
+ end
22
+ end
23
+ end
@@ -0,0 +1,15 @@
1
+ require_relative 'rootlevel'
2
+ require_relative 'components'
3
+
4
+ module SmartyStreets
5
+ module InternationalStreet
6
+ class Changes < RootLevel
7
+ attr_reader :components
8
+
9
+ def initialize(obj)
10
+ @components = Components.new(obj.fetch('components', {}))
11
+ super(obj)
12
+ end
13
+ end
14
+ end
15
+ end
@@ -0,0 +1,63 @@
1
+ require_relative '../request'
2
+ require_relative 'candidate'
3
+
4
+ module SmartyStreets
5
+ module InternationalStreet
6
+ # It is recommended to instantiate this class using ClientBuilder.build_international_street_api_client()
7
+ class Client
8
+ def initialize(sender, serializer)
9
+ @sender = sender
10
+ @serializer = serializer
11
+ end
12
+
13
+ # Sends a Lookup object to the International Street API and stores the result in the Lookup's result field.
14
+ def send(lookup)
15
+ lookup.ensure_enough_info
16
+ request = build_request(lookup)
17
+
18
+ response = @sender.send(request)
19
+
20
+ raise response.error if response.error
21
+
22
+ candidates = convert_candidates(@serializer.deserialize(response.payload))
23
+ lookup.result = candidates
24
+ end
25
+
26
+ def build_request(lookup)
27
+ request = SmartyStreets::Request.new
28
+
29
+ add_parameter(request, 'input_id', lookup.input_id)
30
+ add_parameter(request, 'country', lookup.country)
31
+ add_parameter(request, 'geocode', lookup.geocode.to_s)
32
+ add_parameter(request, 'language', lookup.language)
33
+ add_parameter(request, 'freeform', lookup.freeform)
34
+ add_parameter(request, 'address1', lookup.address1)
35
+ add_parameter(request, 'address2', lookup.address2)
36
+ add_parameter(request, 'address3', lookup.address3)
37
+ add_parameter(request, 'address4', lookup.address4)
38
+ add_parameter(request, 'organization', lookup.organization)
39
+ add_parameter(request, 'locality', lookup.locality)
40
+ add_parameter(request, 'administrative_area', lookup.administrative_area)
41
+ add_parameter(request, 'postal_code', lookup.postal_code)
42
+
43
+ request
44
+ end
45
+
46
+ def add_parameter(request, key, value)
47
+ request.parameters[key] = value unless value.nil? or value.empty?
48
+ end
49
+
50
+ def convert_candidates(raw_candidates)
51
+ candidates = []
52
+
53
+ unless raw_candidates.nil?
54
+ raw_candidates.each do |candidate|
55
+ candidates.push(Candidate.new(candidate))
56
+ end
57
+ end
58
+
59
+ candidates
60
+ end
61
+ end
62
+ end
63
+ end
@@ -0,0 +1,58 @@
1
+ module SmartyStreets
2
+ module InternationalStreet
3
+ # See "https://smartystreets.com/docs/cloud/international-street-api#components"
4
+ class Components
5
+ attr_reader :premise, :thoroughfare_trailing_type, :sub_building, :locality, :post_box_number,
6
+ :thoroughfare_name, :thoroughfare_postdirection, :dependent_thoroughfare, :premise_prefix_number,
7
+ :thoroughfare, :dependent_thoroughfare_name, :postal_code_short, :dependent_thoroughfare_trailing_type,
8
+ :administrative_area, :post_box, :building_leading_type, :dependent_locality_name, :thoroughfare_type,
9
+ :dependent_thoroughfare_postdirection, :double_dependent_locality, :premise_number,
10
+ :dependent_thoroughfare_type, :post_box_type, :building, :sub_administrative_area, :postal_code_extra,
11
+ :sub_building_name, :postal_code, :dependent_locality, :premise_type, :sub_building_number,
12
+ :super_administrative_area, :premise_extra, :dependent_thoroughfare_predirection,
13
+ :building_trailing_type, :thoroughfare_predirection, :building_name, :country_iso_3, :sub_building_type
14
+
15
+ def initialize(obj)
16
+ @country_iso_3 = obj.fetch('country_iso_3', nil)
17
+ @super_administrative_area = obj.fetch('super_administrative_area', nil)
18
+ @administrative_area = obj.fetch('administrative_area', nil)
19
+ @sub_administrative_area = obj.fetch('sub_administrative_area', nil)
20
+ @dependent_locality= obj.fetch('dependent_locality', nil)
21
+ @dependent_locality_name = obj.fetch('dependent_locality_name', nil)
22
+ @double_dependent_locality = obj.fetch('double_dependent_locality', nil)
23
+ @locality = obj.fetch('locality', nil)
24
+ @postal_code = obj.fetch('postal_code', nil)
25
+ @postal_code_short = obj.fetch('postal_code_short', nil)
26
+ @postal_code_extra = obj.fetch('postal_code_extra', nil)
27
+ @premise = obj.fetch('premise', nil)
28
+ @premise_extra = obj.fetch('premise_extra', nil)
29
+ @premise_number = obj.fetch('premise_number', nil)
30
+ @premise_prefix_number = obj.fetch('premise_prefix_number', nil)
31
+ @premise_type = obj.fetch('premise_type', nil)
32
+ @thoroughfare = obj.fetch('thoroughfare', nil)
33
+ @thoroughfare_predirection = obj.fetch('thoroughfare_predirection', nil)
34
+ @thoroughfare_postdirection = obj.fetch('thoroughfare_postdirection', nil)
35
+ @thoroughfare_name = obj.fetch('thoroughfare_name', nil)
36
+ @thoroughfare_trailing_type = obj.fetch('thoroughfare_trailing_type', nil)
37
+ @thoroughfare_type = obj.fetch('thoroughfare_type', nil)
38
+ @dependent_thoroughfare = obj.fetch('dependent_thoroughfare', nil)
39
+ @dependent_thoroughfare_predirection = obj.fetch('dependent_thoroughfare_predirection', nil)
40
+ @dependent_thoroughfare_postdirection = obj.fetch('dependent_thoroughfare_postdirection', nil)
41
+ @dependent_thoroughfare_name = obj.fetch('dependent_thoroughfare_name', nil)
42
+ @dependent_thoroughfare_trailing_type = obj.fetch('dependent_thoroughfare_trailing_type', nil)
43
+ @dependent_thoroughfare_type = obj.fetch('dependent_thoroughfare_type', nil)
44
+ @building = obj.fetch('building', nil)
45
+ @building_leading_type = obj.fetch('building_leading_type', nil)
46
+ @building_name = obj.fetch('building_name', nil)
47
+ @building_trailing_type = obj.fetch('building_trailing_type', nil)
48
+ @sub_building_type = obj.fetch('sub_building_type', nil)
49
+ @sub_building_number = obj.fetch('sub_building_number', nil)
50
+ @sub_building_name = obj.fetch('sub_building_name', nil)
51
+ @sub_building = obj.fetch('sub_building', nil)
52
+ @post_box = obj.fetch('post_box', nil)
53
+ @post_box_type = obj.fetch('post_box_type', nil)
54
+ @post_box_number = obj.fetch('post_box_number', nil)
55
+ end
56
+ end
57
+ end
58
+ end
@@ -0,0 +1,9 @@
1
+ module SmartyStreets
2
+ module InternationalStreet
3
+ module LanguageMode
4
+ NATIVE = 'native'.freeze
5
+
6
+ LATIN = 'latin'.freeze
7
+ end
8
+ end
9
+ end
@@ -0,0 +1,82 @@
1
+ module SmartyStreets
2
+ module InternationalStreet
3
+ # In addition to holding all of the input data for this lookup, this class also will contain the
4
+ # result of the lookup after it comes back from the API.
5
+ #
6
+ # Note: Lookups must have certain required fields set with non-blank values.
7
+ # These can be found at the URL below.
8
+ #
9
+ # See "https://smartystreets.com/docs/cloud/international-street-api#http-input-fields"
10
+ #
11
+ # @geocode:: Disabled by default. Set to true to enable.
12
+ # @language:: When not set, the output language will match the language of the input values.
13
+ # When set to language_mode.NATIVE, the results will always be in the language of the output country.
14
+ # When set to language_mode.LATIN, the results will always be provided using a Latin character set.
15
+ class Lookup
16
+
17
+ attr_accessor :input_id, :freeform, :locality, :postal_code, :address3, :address2, :inputId, :address1,
18
+ :geocode, :administrative_area, :country, :organization, :language, :address4, :result
19
+
20
+ def initialize(freeform=nil, country=nil)
21
+ @result = []
22
+
23
+ @input_id = nil
24
+ @country = country
25
+ @geocode = nil
26
+ @language = nil
27
+ @freeform = freeform
28
+ @address1 = nil
29
+ @address2 = nil
30
+ @address3 = nil
31
+ @address4 = nil
32
+ @organization = nil
33
+ @locality = nil
34
+ @administrative_area = nil
35
+ @postal_code = nil
36
+ end
37
+
38
+ def missing_country
39
+ field_is_missing(@country)
40
+ end
41
+
42
+ def has_freeform
43
+ field_is_set(@freeform)
44
+ end
45
+
46
+ def missing_address1
47
+ field_is_missing(@address1)
48
+ end
49
+
50
+ def has_postal_code
51
+ field_is_set(@postal_code)
52
+ end
53
+
54
+ def missing_locality_or_administrative_area
55
+ field_is_missing(@locality) or field_is_missing(@administrative_area)
56
+ end
57
+
58
+ def field_is_missing(field)
59
+ field.nil? or field.empty?
60
+ end
61
+
62
+ def field_is_set(field)
63
+ not field_is_missing(field)
64
+ end
65
+
66
+ def ensure_enough_info
67
+ raise UnprocessableEntityError, 'Country field is required.' if missing_country
68
+
69
+ return true if has_freeform
70
+
71
+ raise UnprocessableEntityError, 'Either freeform or address1 is required.' if missing_address1
72
+
73
+ return true if has_postal_code
74
+
75
+ if missing_locality_or_administrative_area
76
+ raise UnprocessableEntityError, 'Insufficient information:'\
77
+ 'One or more required fields were not set on the lookup.'
78
+ end
79
+ end
80
+ end
81
+ end
82
+ end
@@ -0,0 +1,17 @@
1
+ module SmartyStreets
2
+ module InternationalStreet
3
+ # See "https://smartystreets.com/docs/cloud/international-street-api#metadata"
4
+ class Metadata
5
+
6
+ attr_reader :longitude, :geocode_precision, :max_geocode_precision, :latitude, :address_format
7
+
8
+ def initialize(obj)
9
+ @latitude = obj.fetch('latitude', nil)
10
+ @longitude = obj.fetch('longitude', nil)
11
+ @geocode_precision = obj.fetch('geocode_precision', nil)
12
+ @max_geocode_precision = obj.fetch('max_geocode_precision', nil)
13
+ @address_format = obj.fetch('address_format', nil)
14
+ end
15
+ end
16
+ end
17
+ end
@@ -0,0 +1,25 @@
1
+ module SmartyStreets
2
+ module InternationalStreet
3
+ class RootLevel
4
+ attr_reader :input_id, :organization, :address1, :address2, :address3, :address4, :address5, :address6, :address7,
5
+ :address8, :address9, :address10, :address11, :address12
6
+
7
+ def initialize(obj)
8
+ @input_id = obj.fetch('input_id', nil)
9
+ @organization = obj.fetch('organization', nil)
10
+ @address1 = obj.fetch('address1', nil)
11
+ @address2 = obj.fetch('address2', nil)
12
+ @address3 = obj.fetch('address3', nil)
13
+ @address4 = obj.fetch('address4', nil)
14
+ @address5 = obj.fetch('address5', nil)
15
+ @address6 = obj.fetch('address6', nil)
16
+ @address7 = obj.fetch('address7', nil)
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
+ end
23
+ end
24
+ end
25
+ end
@@ -0,0 +1,12 @@
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 SmartyStreets
10
+ module InternationalStreet
11
+ end
12
+ end
@@ -0,0 +1,19 @@
1
+ require 'json'
2
+
3
+ module SmartyStreets
4
+ class JSONAble
5
+ def to_json(options={})
6
+ hash = {}
7
+ instance_variables.each do |var|
8
+ hash[var.to_s.delete('@')] = instance_variable_get var
9
+ end
10
+ hash.to_json
11
+ end
12
+
13
+ def from_json!(string)
14
+ JSON.load(string).each do |var, val|
15
+ instance_variable_set var, val
16
+ end
17
+ end
18
+ end
19
+ end
@@ -0,0 +1,15 @@
1
+ module SmartyStreets
2
+ class LicenseSender
3
+ def initialize(inner, licenses)
4
+ @inner = inner
5
+ @licenses = licenses
6
+ end
7
+
8
+ def send(request)
9
+ if @licenses.length > 0
10
+ request.parameters['license'] = @licenses.join(',')
11
+ end
12
+ @inner.send(request)
13
+ end
14
+ end
15
+ end
@@ -0,0 +1,7 @@
1
+ module SmartyStreets
2
+ class Logger
3
+ def log(message)
4
+ puts message
5
+ end
6
+ end
7
+ end
@@ -0,0 +1,85 @@
1
+ require 'net/http'
2
+ require_relative 'version'
3
+ require_relative 'response'
4
+
5
+ module SmartyStreets
6
+ class NativeSender
7
+ def initialize(max_timeout = 10, proxy = nil, debug = false)
8
+ @max_timeout = max_timeout
9
+ @proxy = proxy
10
+ @debug = debug
11
+ end
12
+
13
+ def send(smarty_request)
14
+ request = self.class.build_request(smarty_request)
15
+
16
+ begin
17
+ http = build_http(request)
18
+ http.use_ssl = true
19
+ http.ssl_version = :TLSv1_2
20
+ http.open_timeout = @max_timeout
21
+ http.read_timeout = @max_timeout
22
+
23
+ response = http.request(request)
24
+
25
+ http.finish if http.started?
26
+ rescue StandardError => err
27
+ return Response.new(nil, nil, err)
28
+ end
29
+
30
+ build_smarty_response(response)
31
+ end
32
+
33
+ def self.build_request(smarty_request)
34
+ query = create_query(smarty_request)
35
+
36
+ if smarty_request.payload.nil?
37
+ request = Net::HTTP::Get.new(URI.parse("#{smarty_request.url_prefix}?#{query}"))
38
+ else
39
+ request = Net::HTTP::Post.new(URI.parse("#{smarty_request.url_prefix}?#{query}"))
40
+ end
41
+
42
+ request.content_type = 'application/json'
43
+ request.body = smarty_request.payload
44
+ request['User-Agent'] = "smartystreets (sdk:ruby@#{SmartyStreets::VERSION})"
45
+ request['Referer'] = smarty_request.referer unless smarty_request.referer.nil?
46
+ set_custom_headers(smarty_request.headers, request)
47
+ request
48
+ end
49
+
50
+ def build_smarty_response(native_response)
51
+ Response.new(native_response.body, native_response.code)
52
+ end
53
+
54
+ def build_http(request)
55
+ uri = request.uri
56
+
57
+ if @proxy.nil?
58
+ http = Net::HTTP.new(uri.hostname, uri.port)
59
+ else
60
+ http = Net::HTTP.new(uri.hostname, uri.port, @proxy.host,
61
+ @proxy.port, @proxy.username, @proxy.password)
62
+ end
63
+
64
+ http.set_debug_output($stdout) if @debug
65
+
66
+ http
67
+ end
68
+
69
+ def self.create_query(smarty_request)
70
+ URI.encode_www_form(smarty_request.parameters)
71
+ end
72
+
73
+ def self.set_custom_headers(smarty_headers, request)
74
+ smarty_headers.each do |key, values|
75
+ if values.respond_to? :each
76
+ values.each do |value|
77
+ request.add_field(key, value)
78
+ end
79
+ else
80
+ request.add_field(key, values)
81
+ end
82
+ end
83
+ end
84
+ end
85
+ end
@@ -0,0 +1,13 @@
1
+ require 'json'
2
+
3
+ module SmartyStreets
4
+ class NativeSerializer
5
+ def serialize(obj)
6
+ obj.to_json
7
+ end
8
+
9
+ def deserialize(payload)
10
+ JSON.load(payload)
11
+ end
12
+ end
13
+ end
@@ -0,0 +1,16 @@
1
+ module SmartyStreets
2
+ # Contains information about the proxy through which all requests will be sent.
3
+ #
4
+ # host should not include a scheme
5
+ class Proxy
6
+
7
+ attr_accessor :port, :host, :username, :password
8
+
9
+ def initialize(host, port, username = nil, password = nil)
10
+ @host = host
11
+ @port = port
12
+ @username = username
13
+ @password = password
14
+ end
15
+ end
16
+ end
@@ -0,0 +1,14 @@
1
+ module SmartyStreets
2
+ class Request
3
+ attr_accessor :parameters, :payload, :url_prefix, :referer, :headers, :content_type
4
+
5
+ def initialize
6
+ @parameters = {}
7
+ @payload = nil
8
+ @url_prefix = nil
9
+ @referer = nil
10
+ @headers = {}
11
+ @content_type = 'application/json'
12
+ end
13
+ end
14
+ end
@@ -0,0 +1,11 @@
1
+ module SmartyStreets
2
+ class Response
3
+ attr_accessor :payload, :status_code, :error
4
+
5
+ def initialize(payload, status_code, error = nil)
6
+ @payload = payload
7
+ @status_code = status_code
8
+ @error = error
9
+ end
10
+ end
11
+ end
@@ -0,0 +1,39 @@
1
+ module SmartyStreets
2
+ class RetrySender
3
+ MAX_BACKOFF_DURATION = 10
4
+ STATUS_INTERNAL_SERVER_ERROR = 500
5
+ STATUS_TOO_MANY_REQUESTS = 429
6
+
7
+ def initialize(max_retries, inner, sleeper, logger)
8
+ @max_retries = max_retries
9
+ @inner = inner
10
+ @sleeper = sleeper
11
+ @logger = logger
12
+ end
13
+
14
+ def send(request)
15
+ response = @inner.send(request)
16
+
17
+ (0..@max_retries-1).each do |i|
18
+ if response.status_code.to_i == STATUS_TOO_MANY_REQUESTS
19
+ backoff(5)
20
+ end
21
+
22
+ break if response.status_code.to_i < STATUS_INTERNAL_SERVER_ERROR
23
+
24
+ backoff(i)
25
+
26
+ response = @inner.send(request)
27
+ end
28
+
29
+ response
30
+ end
31
+
32
+ def backoff(attempt)
33
+ backoff_duration = [attempt, MAX_BACKOFF_DURATION].min
34
+
35
+ @logger.log("There was an error processing the request. Retrying in #{backoff_duration} seconds...")
36
+ @sleeper.sleep(backoff_duration)
37
+ end
38
+ end
39
+ end
@@ -0,0 +1,13 @@
1
+ module SmartyStreets
2
+ class SharedCredentials
3
+ def initialize(id, host_name)
4
+ @id = id
5
+ @host_name = host_name
6
+ end
7
+
8
+ def sign(request)
9
+ request.parameters['key'] = @id
10
+ request.referer = @host_name
11
+ end
12
+ end
13
+ end