address_validator 0.1.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +7 -0
- data/.gitignore +19 -0
- data/.rspec +2 -0
- data/.travis.yml +9 -0
- data/Gemfile +12 -0
- data/LICENSE.txt +22 -0
- data/README.md +79 -0
- data/Rakefile +1 -0
- data/address_validator.gemspec +22 -0
- data/lib/address_validator.rb +18 -0
- data/lib/address_validator/address.rb +79 -0
- data/lib/address_validator/client.rb +45 -0
- data/lib/address_validator/config.rb +5 -0
- data/lib/address_validator/response.rb +47 -0
- data/lib/address_validator/validator.rb +40 -0
- data/lib/address_validator/version.rb +3 -0
- data/spec/config.yml.example +6 -0
- data/spec/data/vcr_cassettes/invalid-address.yml +43 -0
- data/spec/data/vcr_cassettes/valid-address-hash.yml +45 -0
- data/spec/data/vcr_cassettes/valid-address.yml +45 -0
- data/spec/lib/address_validator/address_spec.rb +142 -0
- data/spec/lib/address_validator/validator_spec.rb +111 -0
- data/spec/lib/address_validator_spec.rb +37 -0
- data/spec/spec_helper.rb +35 -0
- data/spec/support/vcr.rb +20 -0
- metadata +104 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA1:
|
3
|
+
metadata.gz: a5af87fb8d4f36b232617af62df65bfc65e5fd9a
|
4
|
+
data.tar.gz: ac831584b8e803d1c026faf83890e705d3854f74
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: 06be75bc5fb2f47af0fdfc772bc44837c0b90c83b10d13e97f12b2b56209dafcf525f790a8900234a11a0f34c68b1b18e0acfa38df4ecadfbfdd45ff70ddc183
|
7
|
+
data.tar.gz: 07787078a406a37018ad7dc6db921daf6b0955d17c1248a1cb5fff7de29c91a004fc8afa89c79a1b940ae461cee4007c5d2a40c46428fa61b4c01a65400cbba6
|
data/.gitignore
ADDED
data/.rspec
ADDED
data/.travis.yml
ADDED
data/Gemfile
ADDED
@@ -0,0 +1,12 @@
|
|
1
|
+
source 'https://rubygems.org'
|
2
|
+
|
3
|
+
# Specify your gem's dependencies in address_validator.gemspec
|
4
|
+
gemspec
|
5
|
+
|
6
|
+
group :test do
|
7
|
+
gem 'rspec', '~> 2.12.0'
|
8
|
+
gem 'webmock', '~> 1.9.0'
|
9
|
+
gem 'vcr', '~> 2.4.0'
|
10
|
+
gem 'mocha', '~> 0.10.5'
|
11
|
+
gem 'faker', '~> 1.1.2'
|
12
|
+
end
|
data/LICENSE.txt
ADDED
@@ -0,0 +1,22 @@
|
|
1
|
+
Copyright (c) 2014 robhurring
|
2
|
+
|
3
|
+
MIT License
|
4
|
+
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining
|
6
|
+
a copy of this software and associated documentation files (the
|
7
|
+
"Software"), to deal in the Software without restriction, including
|
8
|
+
without limitation the rights to use, copy, modify, merge, publish,
|
9
|
+
distribute, sublicense, and/or sell copies of the Software, and to
|
10
|
+
permit persons to whom the Software is furnished to do so, subject to
|
11
|
+
the following conditions:
|
12
|
+
|
13
|
+
The above copyright notice and this permission notice shall be
|
14
|
+
included in all copies or substantial portions of the Software.
|
15
|
+
|
16
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
17
|
+
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
18
|
+
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
19
|
+
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
20
|
+
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
21
|
+
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
22
|
+
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
data/README.md
ADDED
@@ -0,0 +1,79 @@
|
|
1
|
+
[](https://travis-ci.org/robhurring/address-validator)
|
2
|
+
|
3
|
+
# AddressValidator
|
4
|
+
|
5
|
+
Wrapper around the UPS street level validation API. Nowhere near a complete implementation. This will just give some basic information back, such as address classification (residential/commercial) and if it is valid or not.
|
6
|
+
|
7
|
+
This can be modified to include address suggestions pretty easily in the `Validator#build_request` method, but we have no need of this right now.
|
8
|
+
|
9
|
+
## Installation
|
10
|
+
|
11
|
+
Add this line to your application's Gemfile:
|
12
|
+
|
13
|
+
gem 'address_validator', github: 'robhurring/address-validator'
|
14
|
+
|
15
|
+
And then execute:
|
16
|
+
|
17
|
+
$ bundle
|
18
|
+
|
19
|
+
## Usage
|
20
|
+
|
21
|
+
Configuring the client
|
22
|
+
|
23
|
+
```ruby
|
24
|
+
AddressValidator.configure do |config|
|
25
|
+
config.key = 'youraccesskeyfromups'
|
26
|
+
config.username = 'username'
|
27
|
+
config.password = 'hunter2'
|
28
|
+
end
|
29
|
+
```
|
30
|
+
|
31
|
+
Basic Example
|
32
|
+
|
33
|
+
```ruby
|
34
|
+
address = AddressValidator::Address.new(
|
35
|
+
name: 'Yum',
|
36
|
+
street1: '33 St. Marks Place',
|
37
|
+
city: 'New York',
|
38
|
+
state: 'NY',
|
39
|
+
zip: '10003',
|
40
|
+
country: 'US'
|
41
|
+
)
|
42
|
+
|
43
|
+
validator = AddressValidator::Validator.new
|
44
|
+
response = validator.validate(address)
|
45
|
+
|
46
|
+
# check if address is valid
|
47
|
+
response.valid?
|
48
|
+
|
49
|
+
# get the returned address
|
50
|
+
new_address = response.address
|
51
|
+
|
52
|
+
# check if its a house
|
53
|
+
new_address.residential?
|
54
|
+
```
|
55
|
+
|
56
|
+
You can also pass hashes directly into the validator if you want.
|
57
|
+
|
58
|
+
```ruby
|
59
|
+
validator = AddressValidator::Validator.new
|
60
|
+
response = validator.validate(
|
61
|
+
name: 'Yum',
|
62
|
+
street1: '33 St. Marks Place',
|
63
|
+
city: 'New York',
|
64
|
+
state: 'NY',
|
65
|
+
zip: '10003',
|
66
|
+
country: 'US'
|
67
|
+
)
|
68
|
+
|
69
|
+
# check if address is valid
|
70
|
+
response.valid?
|
71
|
+
```
|
72
|
+
|
73
|
+
## Contributing
|
74
|
+
|
75
|
+
1. Fork it
|
76
|
+
2. Create your feature branch (`git checkout -b my-new-feature`)
|
77
|
+
3. Commit your changes (`git commit -am 'Add some feature'`)
|
78
|
+
4. Push to the branch (`git push origin my-new-feature`)
|
79
|
+
5. Create new Pull Request
|
data/Rakefile
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
require "bundler/gem_tasks"
|
@@ -0,0 +1,22 @@
|
|
1
|
+
# -*- encoding: utf-8 -*-
|
2
|
+
lib = File.expand_path('../lib', __FILE__)
|
3
|
+
$LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
|
4
|
+
require 'address_validator/version'
|
5
|
+
|
6
|
+
Gem::Specification.new do |gem|
|
7
|
+
gem.name = "address_validator"
|
8
|
+
gem.version = AddressValidator::VERSION
|
9
|
+
gem.authors = ["robhurring"]
|
10
|
+
gem.email = ["robhurring@gmail.com"]
|
11
|
+
gem.description = %q{UPS address validation gem}
|
12
|
+
gem.summary = %q{Simple address validator using the UPS address validation API}
|
13
|
+
gem.homepage = ""
|
14
|
+
|
15
|
+
gem.files = `git ls-files`.split($/)
|
16
|
+
gem.executables = gem.files.grep(%r{^bin/}).map{ |f| File.basename(f) }
|
17
|
+
gem.test_files = gem.files.grep(%r{^(test|spec|features)/})
|
18
|
+
gem.require_paths = ["lib"]
|
19
|
+
|
20
|
+
gem.add_dependency 'httparty', '>= 0.11.0'
|
21
|
+
gem.add_dependency 'builder', '>= 3.0.4'
|
22
|
+
end
|
@@ -0,0 +1,18 @@
|
|
1
|
+
require "address_validator/version"
|
2
|
+
|
3
|
+
require_relative './address_validator/config'
|
4
|
+
require_relative './address_validator/address'
|
5
|
+
require_relative './address_validator/response'
|
6
|
+
require_relative './address_validator/client'
|
7
|
+
require_relative './address_validator/validator'
|
8
|
+
|
9
|
+
module AddressValidator
|
10
|
+
class << self
|
11
|
+
attr_accessor :config
|
12
|
+
end
|
13
|
+
|
14
|
+
def self.configure(&block)
|
15
|
+
self.config = Config.new
|
16
|
+
yield self.config
|
17
|
+
end
|
18
|
+
end
|
@@ -0,0 +1,79 @@
|
|
1
|
+
require 'builder'
|
2
|
+
|
3
|
+
module AddressValidator
|
4
|
+
class Address
|
5
|
+
CLASSIFICATION_UNKNOWN = 0
|
6
|
+
CLASSIFICATION_COMMERCIAL = 1
|
7
|
+
CLASSIFICATION_RESIDENTIAL = 2
|
8
|
+
|
9
|
+
class << self
|
10
|
+
# Public: Build an address from the API's response xml.
|
11
|
+
#
|
12
|
+
# attrs - Hash of address attributes.
|
13
|
+
#
|
14
|
+
# Returns an address.
|
15
|
+
def from_xml(attrs = {})
|
16
|
+
classification = nil
|
17
|
+
|
18
|
+
if _classification = attrs['AddressClassification']
|
19
|
+
classification = _classification['Code']
|
20
|
+
end
|
21
|
+
|
22
|
+
# AddressLine can come in as a single element, or upto 3 elements according
|
23
|
+
# to the UPS docs, so we force it into an array and split them up
|
24
|
+
address_lines = Array(attrs['AddressLine'])
|
25
|
+
|
26
|
+
new(
|
27
|
+
street1: address_lines[0],
|
28
|
+
street2: address_lines[1],
|
29
|
+
street3: address_lines[2],
|
30
|
+
city: attrs['PoliticalDivision2'],
|
31
|
+
state: attrs['PoliticalDivision1'],
|
32
|
+
zip: attrs['PostcodePrimaryLow'],
|
33
|
+
country: attrs['CountryCode'],
|
34
|
+
classification: classification
|
35
|
+
)
|
36
|
+
end
|
37
|
+
end
|
38
|
+
|
39
|
+
attr_accessor :name, :street1, :street2, :street3, :city, :state, :zip, :country, :classification
|
40
|
+
|
41
|
+
def initialize(name: name(), street1: street1(), street2: street2(), street3: street3(), city: city(), state: state(), zip: zip(), country: country(), classification: classification())
|
42
|
+
@name = name
|
43
|
+
@street1 = street1
|
44
|
+
@street2 = street2
|
45
|
+
@street3 = street3
|
46
|
+
@city = city
|
47
|
+
@state = state
|
48
|
+
@zip = zip
|
49
|
+
@country = country
|
50
|
+
@classification = (classification || CLASSIFICATION_UNKNOWN).to_i
|
51
|
+
end
|
52
|
+
|
53
|
+
def residential?
|
54
|
+
classification == CLASSIFICATION_RESIDENTIAL
|
55
|
+
end
|
56
|
+
|
57
|
+
def commercial?
|
58
|
+
classification == CLASSIFICATION_COMMERCIAL
|
59
|
+
end
|
60
|
+
|
61
|
+
def to_xml(options={})
|
62
|
+
|
63
|
+
xml = Builder::XmlMarkup.new(options)
|
64
|
+
|
65
|
+
xml.AddressKeyFormat do
|
66
|
+
xml.ConsigneeName(self.name)
|
67
|
+
xml.tag! 'AddressLine', self.street1
|
68
|
+
xml.tag! 'AddressLine', self.street2 if self.street2
|
69
|
+
xml.tag! 'AddressLine', self.street3 if self.street3
|
70
|
+
xml.PoliticalDivision2(self.city)
|
71
|
+
xml.PoliticalDivision1(self.state)
|
72
|
+
xml.PostcodePrimaryLow(self.zip)
|
73
|
+
xml.CountryCode(self.country)
|
74
|
+
end
|
75
|
+
|
76
|
+
xml.target!
|
77
|
+
end
|
78
|
+
end
|
79
|
+
end
|
@@ -0,0 +1,45 @@
|
|
1
|
+
require 'httparty'
|
2
|
+
require 'builder'
|
3
|
+
|
4
|
+
module AddressValidator
|
5
|
+
class Client
|
6
|
+
include HTTParty
|
7
|
+
format :xml
|
8
|
+
|
9
|
+
def initialize
|
10
|
+
@config = AddressValidator.config
|
11
|
+
set_base_uri
|
12
|
+
end
|
13
|
+
|
14
|
+
def access_request
|
15
|
+
xml = Builder::XmlMarkup.new
|
16
|
+
|
17
|
+
xml.instruct!
|
18
|
+
xml.AccessRequest do
|
19
|
+
xml.AccessLicenseNumber(@config.key)
|
20
|
+
xml.UserId(@config.username)
|
21
|
+
xml.Password(@config.password)
|
22
|
+
end
|
23
|
+
end
|
24
|
+
|
25
|
+
def set_base_uri
|
26
|
+
if @config.testing
|
27
|
+
endpoint = 'https://wwwcie.ups.com'
|
28
|
+
else
|
29
|
+
endpoint = 'https://onlinetools.ups.com'
|
30
|
+
end
|
31
|
+
|
32
|
+
self.class.base_uri(endpoint)
|
33
|
+
end
|
34
|
+
|
35
|
+
def post(request)
|
36
|
+
xml = Builder::XmlMarkup.new
|
37
|
+
xml << access_request
|
38
|
+
xml << request
|
39
|
+
body = xml.target!
|
40
|
+
|
41
|
+
api_response = self.class.post('/ups.app/xml/XAV', body: body)
|
42
|
+
Response.new(api_response)
|
43
|
+
end
|
44
|
+
end
|
45
|
+
end
|
@@ -0,0 +1,47 @@
|
|
1
|
+
require 'forwardable'
|
2
|
+
|
3
|
+
module AddressValidator
|
4
|
+
class Response
|
5
|
+
extend Forwardable
|
6
|
+
|
7
|
+
attr_reader :api_response
|
8
|
+
def_delegators :api_response, :[]
|
9
|
+
|
10
|
+
def initialize(api_response)
|
11
|
+
@api_response = api_response
|
12
|
+
end
|
13
|
+
|
14
|
+
def ok?
|
15
|
+
@api_response.ok?
|
16
|
+
end
|
17
|
+
|
18
|
+
def success?
|
19
|
+
ok? && response['Response']['ResponseStatusCode'] == '1'
|
20
|
+
end
|
21
|
+
|
22
|
+
def response
|
23
|
+
@response ||= self['AddressValidationResponse']
|
24
|
+
end
|
25
|
+
|
26
|
+
def error
|
27
|
+
_error_container = response['Response']['Error']
|
28
|
+
_error_container && _error_container['ErrorDescription']
|
29
|
+
end
|
30
|
+
|
31
|
+
def valid?
|
32
|
+
response.has_key?('ValidAddressIndicator')
|
33
|
+
end
|
34
|
+
|
35
|
+
def ambiguous?
|
36
|
+
response.has_key?('AmbiguousAddressIndicator')
|
37
|
+
end
|
38
|
+
|
39
|
+
def no_canidates?
|
40
|
+
response.has_key?('NoCandidatesIndicator')
|
41
|
+
end
|
42
|
+
|
43
|
+
def address
|
44
|
+
AddressValidator::Address.from_xml(response['AddressKeyFormat'])
|
45
|
+
end
|
46
|
+
end
|
47
|
+
end
|
@@ -0,0 +1,40 @@
|
|
1
|
+
require 'builder'
|
2
|
+
|
3
|
+
module AddressValidator
|
4
|
+
class Validator
|
5
|
+
attr_reader :client
|
6
|
+
|
7
|
+
def initialize
|
8
|
+
@client = Client.new
|
9
|
+
end
|
10
|
+
|
11
|
+
def validate(address)
|
12
|
+
if address.is_a?(::Hash)
|
13
|
+
address = build_address(address)
|
14
|
+
end
|
15
|
+
|
16
|
+
request = build_request(address)
|
17
|
+
@client.post(request)
|
18
|
+
end
|
19
|
+
|
20
|
+
def build_address(attrs)
|
21
|
+
AddressValidator::Address.new(attrs)
|
22
|
+
end
|
23
|
+
|
24
|
+
def build_request(address)
|
25
|
+
xml = Builder::XmlMarkup.new
|
26
|
+
|
27
|
+
xml.instruct!
|
28
|
+
xml.AddressValidationRequest do
|
29
|
+
xml.Request do
|
30
|
+
xml.RequestAction 'XAV' # must be XAV
|
31
|
+
xml.RequestOption '3' # validation + classification
|
32
|
+
end
|
33
|
+
xml.MaximumListSize('1')
|
34
|
+
xml << address.to_xml
|
35
|
+
end
|
36
|
+
|
37
|
+
xml.target!
|
38
|
+
end
|
39
|
+
end
|
40
|
+
end
|
@@ -0,0 +1,43 @@
|
|
1
|
+
---
|
2
|
+
http_interactions:
|
3
|
+
- request:
|
4
|
+
method: post
|
5
|
+
uri: https://onlinetools.ups.com/ups.app/xml/XAV
|
6
|
+
body:
|
7
|
+
encoding: UTF-8
|
8
|
+
string: <?xml version="1.0" encoding="UTF-8"?><AccessRequest><AccessLicenseNumber>%{AccessLicenseNumber}</AccessLicenseNumber><UserId>%{UserId}</UserId><Password>%{Password}</Password></AccessRequest><?xml
|
9
|
+
version="1.0" encoding="UTF-8"?><AddressValidationRequest><Request><RequestAction>XAV</RequestAction><RequestOption>3</RequestOption></Request><MaximumListSize>1</MaximumListSize><AddressKeyFormat><ConsigneeName>Doctor
|
10
|
+
Daves Backyard Dentistry</ConsigneeName><AddressLine>1 Seseme Street</AddressLine><PoliticalDivision2>New
|
11
|
+
York</PoliticalDivision2><PoliticalDivision1>NY</PoliticalDivision1><PostcodePrimaryLow>10012</PostcodePrimaryLow><CountryCode>US</CountryCode></AddressKeyFormat></AddressValidationRequest>
|
12
|
+
headers:
|
13
|
+
Accept-Encoding:
|
14
|
+
- gzip;q=1.0,deflate;q=0.6,identity;q=0.3
|
15
|
+
Accept:
|
16
|
+
- '*/*'
|
17
|
+
User-Agent:
|
18
|
+
- Ruby
|
19
|
+
response:
|
20
|
+
status:
|
21
|
+
code: 200
|
22
|
+
message: OK
|
23
|
+
headers:
|
24
|
+
Date:
|
25
|
+
- Sun, 15 Nov 2015 00:36:42 GMT
|
26
|
+
Server:
|
27
|
+
- Apache
|
28
|
+
X-Frame-Options:
|
29
|
+
- SAMEORIGIN
|
30
|
+
Pragma:
|
31
|
+
- no-cache
|
32
|
+
Content-Length:
|
33
|
+
- '365'
|
34
|
+
X-Content-Type-Options:
|
35
|
+
- nosniff
|
36
|
+
Content-Type:
|
37
|
+
- application/xml
|
38
|
+
body:
|
39
|
+
encoding: UTF-8
|
40
|
+
string: <?xml version="1.0"?><AddressValidationResponse><Response><TransactionReference></TransactionReference><ResponseStatusCode>1</ResponseStatusCode><ResponseStatusDescription>Success</ResponseStatusDescription></Response><NoCandidatesIndicator/><AddressClassification><Code>0</Code><Description>Unknown</Description></AddressClassification></AddressValidationResponse>
|
41
|
+
http_version:
|
42
|
+
recorded_at: Sun, 15 Nov 2015 00:36:42 GMT
|
43
|
+
recorded_with: VCR 2.4.0
|
@@ -0,0 +1,45 @@
|
|
1
|
+
---
|
2
|
+
http_interactions:
|
3
|
+
- request:
|
4
|
+
method: post
|
5
|
+
uri: https://onlinetools.ups.com/ups.app/xml/XAV
|
6
|
+
body:
|
7
|
+
encoding: UTF-8
|
8
|
+
string: <?xml version="1.0" encoding="UTF-8"?><AccessRequest><AccessLicenseNumber>%{AccessLicenseNumber}</AccessLicenseNumber><UserId>%{UserId}</UserId><Password>%{Password}</Password></AccessRequest><?xml
|
9
|
+
version="1.0" encoding="UTF-8"?><AddressValidationRequest><Request><RequestAction>XAV</RequestAction><RequestOption>3</RequestOption></Request><MaximumListSize>1</MaximumListSize><AddressKeyFormat><ConsigneeName>Yum</ConsigneeName><AddressLine>33
|
10
|
+
St. Marks Place</AddressLine><AddressLine>Suite 3</AddressLine><AddressLine>C/O
|
11
|
+
Some Dude</AddressLine><PoliticalDivision2>New York</PoliticalDivision2><PoliticalDivision1>NY</PoliticalDivision1><PostcodePrimaryLow>10003</PostcodePrimaryLow><CountryCode>US</CountryCode></AddressKeyFormat></AddressValidationRequest>
|
12
|
+
headers:
|
13
|
+
Accept-Encoding:
|
14
|
+
- gzip;q=1.0,deflate;q=0.6,identity;q=0.3
|
15
|
+
Accept:
|
16
|
+
- '*/*'
|
17
|
+
User-Agent:
|
18
|
+
- Ruby
|
19
|
+
response:
|
20
|
+
status:
|
21
|
+
code: 200
|
22
|
+
message: OK
|
23
|
+
headers:
|
24
|
+
Date:
|
25
|
+
- Sun, 15 Nov 2015 00:36:44 GMT
|
26
|
+
Server:
|
27
|
+
- Apache
|
28
|
+
X-Frame-Options:
|
29
|
+
- SAMEORIGIN
|
30
|
+
Pragma:
|
31
|
+
- no-cache
|
32
|
+
Content-Length:
|
33
|
+
- '829'
|
34
|
+
X-Content-Type-Options:
|
35
|
+
- nosniff
|
36
|
+
Content-Type:
|
37
|
+
- application/xml
|
38
|
+
body:
|
39
|
+
encoding: UTF-8
|
40
|
+
string: <?xml version="1.0"?><AddressValidationResponse><Response><TransactionReference></TransactionReference><ResponseStatusCode>1</ResponseStatusCode><ResponseStatusDescription>Success</ResponseStatusDescription></Response><ValidAddressIndicator/><AddressClassification><Code>1</Code><Description>Commercial</Description></AddressClassification><AddressKeyFormat><AddressClassification><Code>1</Code><Description>Commercial</Description></AddressClassification><AddressLine>33
|
41
|
+
ST MARKS PL</AddressLine><AddressLine>APT 3</AddressLine><Region>NEW YORK
|
42
|
+
NY 10003-7832</Region><PoliticalDivision2>NEW YORK</PoliticalDivision2><PoliticalDivision1>NY</PoliticalDivision1><PostcodePrimaryLow>10003</PostcodePrimaryLow><PostcodeExtendedLow>7832</PostcodeExtendedLow><CountryCode>US</CountryCode></AddressKeyFormat></AddressValidationResponse>
|
43
|
+
http_version:
|
44
|
+
recorded_at: Sun, 15 Nov 2015 00:36:43 GMT
|
45
|
+
recorded_with: VCR 2.4.0
|
@@ -0,0 +1,45 @@
|
|
1
|
+
---
|
2
|
+
http_interactions:
|
3
|
+
- request:
|
4
|
+
method: post
|
5
|
+
uri: https://onlinetools.ups.com/ups.app/xml/XAV
|
6
|
+
body:
|
7
|
+
encoding: UTF-8
|
8
|
+
string: <?xml version="1.0" encoding="UTF-8"?><AccessRequest><AccessLicenseNumber>%{AccessLicenseNumber}</AccessLicenseNumber><UserId>%{UserId}</UserId><Password>%{Password}</Password></AccessRequest><?xml
|
9
|
+
version="1.0" encoding="UTF-8"?><AddressValidationRequest><Request><RequestAction>XAV</RequestAction><RequestOption>3</RequestOption></Request><MaximumListSize>1</MaximumListSize><AddressKeyFormat><ConsigneeName>Yum</ConsigneeName><AddressLine>33
|
10
|
+
St. Marks Place</AddressLine><AddressLine>Suite 3</AddressLine><PoliticalDivision2>New
|
11
|
+
York</PoliticalDivision2><PoliticalDivision1>NY</PoliticalDivision1><PostcodePrimaryLow>10003</PostcodePrimaryLow><CountryCode>US</CountryCode></AddressKeyFormat></AddressValidationRequest>
|
12
|
+
headers:
|
13
|
+
Accept-Encoding:
|
14
|
+
- gzip;q=1.0,deflate;q=0.6,identity;q=0.3
|
15
|
+
Accept:
|
16
|
+
- '*/*'
|
17
|
+
User-Agent:
|
18
|
+
- Ruby
|
19
|
+
response:
|
20
|
+
status:
|
21
|
+
code: 200
|
22
|
+
message: OK
|
23
|
+
headers:
|
24
|
+
Date:
|
25
|
+
- Sun, 15 Nov 2015 00:36:41 GMT
|
26
|
+
Server:
|
27
|
+
- Apache
|
28
|
+
X-Frame-Options:
|
29
|
+
- SAMEORIGIN
|
30
|
+
Pragma:
|
31
|
+
- no-cache
|
32
|
+
Content-Length:
|
33
|
+
- '829'
|
34
|
+
X-Content-Type-Options:
|
35
|
+
- nosniff
|
36
|
+
Content-Type:
|
37
|
+
- application/xml
|
38
|
+
body:
|
39
|
+
encoding: UTF-8
|
40
|
+
string: <?xml version="1.0"?><AddressValidationResponse><Response><TransactionReference></TransactionReference><ResponseStatusCode>1</ResponseStatusCode><ResponseStatusDescription>Success</ResponseStatusDescription></Response><ValidAddressIndicator/><AddressClassification><Code>1</Code><Description>Commercial</Description></AddressClassification><AddressKeyFormat><AddressClassification><Code>1</Code><Description>Commercial</Description></AddressClassification><AddressLine>33
|
41
|
+
ST MARKS PL</AddressLine><AddressLine>APT 3</AddressLine><Region>NEW YORK
|
42
|
+
NY 10003-7832</Region><PoliticalDivision2>NEW YORK</PoliticalDivision2><PoliticalDivision1>NY</PoliticalDivision1><PostcodePrimaryLow>10003</PostcodePrimaryLow><PostcodeExtendedLow>7832</PostcodeExtendedLow><CountryCode>US</CountryCode></AddressKeyFormat></AddressValidationResponse>
|
43
|
+
http_version:
|
44
|
+
recorded_at: Sun, 15 Nov 2015 00:36:41 GMT
|
45
|
+
recorded_with: VCR 2.4.0
|
@@ -0,0 +1,142 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe AddressValidator::Address do
|
4
|
+
describe 'properties' do
|
5
|
+
subject(:address) do
|
6
|
+
described_class.new(
|
7
|
+
name: 'rob',
|
8
|
+
street1: 'street1',
|
9
|
+
street2: 'street2',
|
10
|
+
street3: 'street3',
|
11
|
+
city: 'city',
|
12
|
+
state: 'state',
|
13
|
+
zip: '90210',
|
14
|
+
country: 'US'
|
15
|
+
)
|
16
|
+
end
|
17
|
+
|
18
|
+
its(:name) { should eq 'rob' }
|
19
|
+
its(:street1) { should eq 'street1' }
|
20
|
+
its(:street2) { should eq 'street2' }
|
21
|
+
its(:street3) { should eq 'street3' }
|
22
|
+
its(:city) { should eq 'city' }
|
23
|
+
its(:state) { should eq 'state' }
|
24
|
+
its(:zip) { should eq '90210' }
|
25
|
+
its(:country) { should eq 'US' }
|
26
|
+
end
|
27
|
+
|
28
|
+
describe '#to_xml' do
|
29
|
+
let(:address) do
|
30
|
+
described_class.new(
|
31
|
+
name: 'rob',
|
32
|
+
street1: 'street1',
|
33
|
+
street2: 'street2',
|
34
|
+
city: 'city',
|
35
|
+
state: 'state',
|
36
|
+
zip: '90210',
|
37
|
+
country: 'US'
|
38
|
+
)
|
39
|
+
end
|
40
|
+
|
41
|
+
let(:xml){ address.to_xml }
|
42
|
+
|
43
|
+
it 'should have a ConsigneeName' do
|
44
|
+
xml.should =~ /<ConsigneeName>rob<\/ConsigneeName>/
|
45
|
+
end
|
46
|
+
|
47
|
+
it 'should have a AddressLine for street1' do
|
48
|
+
xml.should =~ /<AddressLine>street1<\/AddressLine>/
|
49
|
+
end
|
50
|
+
|
51
|
+
it 'should have a AddressLine for street2' do
|
52
|
+
xml.should =~ /<AddressLine>street2<\/AddressLine>/
|
53
|
+
end
|
54
|
+
|
55
|
+
it 'should not have a AddressLine for street3' do
|
56
|
+
xml.should_not =~ /<AddressLine>street3<\/AddressLine>/
|
57
|
+
end
|
58
|
+
|
59
|
+
it 'should have a PoliticalDivision2' do
|
60
|
+
xml.should =~ /<PoliticalDivision2>city<\/PoliticalDivision2>/
|
61
|
+
end
|
62
|
+
|
63
|
+
it 'should have a PoliticalDivision1' do
|
64
|
+
xml.should =~ /<PoliticalDivision1>state<\/PoliticalDivision1>/
|
65
|
+
end
|
66
|
+
|
67
|
+
it 'should have a PostcodePrimaryLow' do
|
68
|
+
xml.should =~ /<PostcodePrimaryLow>90210<\/PostcodePrimaryLow>/
|
69
|
+
end
|
70
|
+
|
71
|
+
it 'should have a CountryCode' do
|
72
|
+
xml.should =~ /<CountryCode>US<\/CountryCode>/
|
73
|
+
end
|
74
|
+
end
|
75
|
+
|
76
|
+
describe '#from_xml' do
|
77
|
+
context 'when only a single AddressLine comes back' do
|
78
|
+
let!(:xml) do
|
79
|
+
MultiXml.parse %{
|
80
|
+
<AddressKeyFormat>
|
81
|
+
<AddressClassification>
|
82
|
+
<Code>1</Code>
|
83
|
+
<Description>Commercial</Description>
|
84
|
+
</AddressClassification>
|
85
|
+
<AddressLine>648 BROADWAY</AddressLine>
|
86
|
+
<Region>NEW YORK NY 10012-2348</Region>
|
87
|
+
<PoliticalDivision2>NEW YORK</PoliticalDivision2>
|
88
|
+
<PoliticalDivision1>NY</PoliticalDivision1>
|
89
|
+
<PostcodePrimaryLow>10012</PostcodePrimaryLow>
|
90
|
+
<PostcodeExtendedLow>2348</PostcodeExtendedLow>
|
91
|
+
<CountryCode>US</CountryCode>
|
92
|
+
</AddressKeyFormat>
|
93
|
+
}
|
94
|
+
end
|
95
|
+
|
96
|
+
subject(:address){ described_class.from_xml(xml['AddressKeyFormat']) }
|
97
|
+
|
98
|
+
its(:street1){ should eq '648 BROADWAY' }
|
99
|
+
its(:street2){ should be_nil }
|
100
|
+
its(:street3){ should be_nil }
|
101
|
+
its(:classification){ should eq described_class::CLASSIFICATION_COMMERCIAL }
|
102
|
+
its(:city){ should eq 'NEW YORK' }
|
103
|
+
its(:state){ should eq 'NY' }
|
104
|
+
its(:zip){ should eq '10012' }
|
105
|
+
its(:country){ should eq 'US' }
|
106
|
+
end
|
107
|
+
|
108
|
+
context 'when multiple AddressLines comes back' do
|
109
|
+
let!(:xml) do
|
110
|
+
MultiXml.parse %{
|
111
|
+
<AddressKeyFormat>
|
112
|
+
<AddressClassification>
|
113
|
+
<Code>1</Code>
|
114
|
+
<Description>Commercial</Description>
|
115
|
+
</AddressClassification>
|
116
|
+
<AddressLine>648 BROADWAY</AddressLine>
|
117
|
+
<AddressLine>SUITE 1</AddressLine>
|
118
|
+
<AddressLine>C/O SOME GUY</AddressLine>
|
119
|
+
<Region>NEW YORK NY 10012-2348</Region>
|
120
|
+
<PoliticalDivision2>NEW YORK</PoliticalDivision2>
|
121
|
+
<PoliticalDivision1>NY</PoliticalDivision1>
|
122
|
+
<PostcodePrimaryLow>10012</PostcodePrimaryLow>
|
123
|
+
<PostcodeExtendedLow>2348</PostcodeExtendedLow>
|
124
|
+
<CountryCode>US</CountryCode>
|
125
|
+
</AddressKeyFormat>
|
126
|
+
}
|
127
|
+
end
|
128
|
+
|
129
|
+
subject(:address){ described_class.from_xml(xml['AddressKeyFormat']) }
|
130
|
+
|
131
|
+
its(:street1){ should eq '648 BROADWAY' }
|
132
|
+
its(:street2){ should eq 'SUITE 1' }
|
133
|
+
its(:street3){ should eq 'C/O SOME GUY' }
|
134
|
+
its(:classification){ should eq described_class::CLASSIFICATION_COMMERCIAL }
|
135
|
+
its(:city){ should eq 'NEW YORK' }
|
136
|
+
its(:state){ should eq 'NY' }
|
137
|
+
its(:zip){ should eq '10012' }
|
138
|
+
its(:country){ should eq 'US' }
|
139
|
+
end
|
140
|
+
|
141
|
+
end
|
142
|
+
end
|
@@ -0,0 +1,111 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe AddressValidator::Validator do
|
4
|
+
let(:validator){ described_class.new }
|
5
|
+
|
6
|
+
context 'with a valid address', vcr: {cassette_name: 'valid-address'} do
|
7
|
+
let(:address) do
|
8
|
+
AddressValidator::Address.new(
|
9
|
+
name: 'Yum',
|
10
|
+
street1: '33 St. Marks Place',
|
11
|
+
street2: 'Suite 3',
|
12
|
+
city: 'New York',
|
13
|
+
state: 'NY',
|
14
|
+
zip: '10003',
|
15
|
+
country: 'US'
|
16
|
+
)
|
17
|
+
end
|
18
|
+
|
19
|
+
describe '#validate' do
|
20
|
+
let(:response){ validator.validate(address) }
|
21
|
+
|
22
|
+
it 'should be HTTP ok' do
|
23
|
+
response.should be_ok
|
24
|
+
end
|
25
|
+
|
26
|
+
it 'should be a successful request' do
|
27
|
+
response.should be_success
|
28
|
+
end
|
29
|
+
|
30
|
+
it 'should be a valid address' do
|
31
|
+
response.should be_valid
|
32
|
+
end
|
33
|
+
|
34
|
+
it 'should have an address' do
|
35
|
+
response.address.should_not be_nil
|
36
|
+
end
|
37
|
+
|
38
|
+
it 'should have no errors' do
|
39
|
+
response.error.should be_nil
|
40
|
+
end
|
41
|
+
end
|
42
|
+
end
|
43
|
+
|
44
|
+
context 'with an invalid address', vcr: {cassette_name: 'invalid-address'} do
|
45
|
+
let(:address) do
|
46
|
+
AddressValidator::Address.new(
|
47
|
+
name: 'Doctor Daves Backyard Dentistry',
|
48
|
+
street1: '1 Seseme Street',
|
49
|
+
city: 'New York',
|
50
|
+
state: 'NY',
|
51
|
+
zip: '10012',
|
52
|
+
country: 'US'
|
53
|
+
)
|
54
|
+
end
|
55
|
+
|
56
|
+
describe '#validate' do
|
57
|
+
let(:response){ validator.validate(address) }
|
58
|
+
|
59
|
+
it 'should be HTTP ok' do
|
60
|
+
response.should be_ok
|
61
|
+
end
|
62
|
+
|
63
|
+
it 'should be a successful request' do
|
64
|
+
response.should be_success
|
65
|
+
end
|
66
|
+
|
67
|
+
it 'should not be a valid address' do
|
68
|
+
response.should_not be_valid
|
69
|
+
end
|
70
|
+
end
|
71
|
+
end
|
72
|
+
|
73
|
+
context 'when passing in address hashes', vcr: {cassette_name: 'valid-address-hash'} do
|
74
|
+
let(:address_hash) do
|
75
|
+
{
|
76
|
+
name: 'Yum',
|
77
|
+
street1: '33 St. Marks Place',
|
78
|
+
street2: 'Suite 3',
|
79
|
+
street3: 'C/O Some Dude',
|
80
|
+
city: 'New York',
|
81
|
+
state: 'NY',
|
82
|
+
zip: '10003',
|
83
|
+
country: 'US'
|
84
|
+
}
|
85
|
+
end
|
86
|
+
|
87
|
+
describe '#validate' do
|
88
|
+
let(:response){ validator.validate(address_hash) }
|
89
|
+
|
90
|
+
it 'should be HTTP ok' do
|
91
|
+
response.should be_ok
|
92
|
+
end
|
93
|
+
|
94
|
+
it 'should be a successful request' do
|
95
|
+
response.should be_success
|
96
|
+
end
|
97
|
+
|
98
|
+
it 'should be a valid address' do
|
99
|
+
response.should be_valid
|
100
|
+
end
|
101
|
+
|
102
|
+
it 'should have an address' do
|
103
|
+
response.address.should_not be_nil
|
104
|
+
end
|
105
|
+
|
106
|
+
it 'should have no errors' do
|
107
|
+
response.error.should be_nil
|
108
|
+
end
|
109
|
+
end
|
110
|
+
end
|
111
|
+
end
|
@@ -0,0 +1,37 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe AddressValidator do
|
4
|
+
describe '.configure' do
|
5
|
+
it 'should set the key' do
|
6
|
+
described_class.configure do |config|
|
7
|
+
config.key = '90210'
|
8
|
+
end
|
9
|
+
|
10
|
+
described_class.config.key.should eq '90210'
|
11
|
+
end
|
12
|
+
|
13
|
+
it 'should set the username' do
|
14
|
+
described_class.configure do |config|
|
15
|
+
config.username = 'usr'
|
16
|
+
end
|
17
|
+
|
18
|
+
described_class.config.username.should eq 'usr'
|
19
|
+
end
|
20
|
+
|
21
|
+
it 'should set the password' do
|
22
|
+
described_class.configure do |config|
|
23
|
+
config.password = 's3cre7'
|
24
|
+
end
|
25
|
+
|
26
|
+
described_class.config.password.should eq 's3cre7'
|
27
|
+
end
|
28
|
+
|
29
|
+
it 'should set the testing flag' do
|
30
|
+
described_class.configure do |config|
|
31
|
+
config.testing = true
|
32
|
+
end
|
33
|
+
|
34
|
+
described_class.config.testing.should be_true
|
35
|
+
end
|
36
|
+
end
|
37
|
+
end
|
data/spec/spec_helper.rb
ADDED
@@ -0,0 +1,35 @@
|
|
1
|
+
ENV["RAILS_ENV"] ||= 'test'
|
2
|
+
|
3
|
+
require 'rubygems'
|
4
|
+
require 'bundler/setup'
|
5
|
+
Bundler.require(:default, :test)
|
6
|
+
require 'faker'
|
7
|
+
require 'address_validator'
|
8
|
+
|
9
|
+
default_config = AddressValidator::Config.new
|
10
|
+
config_file = File.expand_path('../config.yml', __FILE__)
|
11
|
+
if File.exists?(config_file)
|
12
|
+
API_CONFIG = YAML.load_file(config_file)
|
13
|
+
default_config.key = API_CONFIG['access_key']
|
14
|
+
default_config.username = API_CONFIG['username']
|
15
|
+
default_config.password = API_CONFIG['password']
|
16
|
+
else
|
17
|
+
puts "ERROR!"
|
18
|
+
puts "No config file was found! Copy the config.yml.example file to spec/config.yml and fill out the details."
|
19
|
+
exit 1
|
20
|
+
end
|
21
|
+
|
22
|
+
Dir[File.expand_path('../support/**/*.rb', __FILE__)].each{ |f| require f }
|
23
|
+
|
24
|
+
RSpec.configure do |config|
|
25
|
+
config.treat_symbols_as_metadata_keys_with_true_values = true
|
26
|
+
config.run_all_when_everything_filtered = true
|
27
|
+
config.filter_run :focus
|
28
|
+
config.order = "random"
|
29
|
+
config.mock_with :mocha
|
30
|
+
|
31
|
+
config.before(:each) do
|
32
|
+
# reset any config changes we've made
|
33
|
+
AddressValidator.config = default_config
|
34
|
+
end
|
35
|
+
end
|
data/spec/support/vcr.rb
ADDED
@@ -0,0 +1,20 @@
|
|
1
|
+
VCR.configure do |c|
|
2
|
+
c.cassette_library_dir = 'spec/data/vcr_cassettes'
|
3
|
+
c.hook_into :webmock
|
4
|
+
c.ignore_localhost = true
|
5
|
+
c.configure_rspec_metadata!
|
6
|
+
c.default_cassette_options = {record: :new_episodes}
|
7
|
+
c.filter_sensitive_data('%{AccessLicenseNumber}'){ API_CONFIG['access_key'] }
|
8
|
+
c.filter_sensitive_data('%{UserId}'){ API_CONFIG['username'] }
|
9
|
+
c.filter_sensitive_data('%{Password}'){ API_CONFIG['password'] }
|
10
|
+
end
|
11
|
+
|
12
|
+
# Re-Record all VCR cassettes
|
13
|
+
# Usage: RERECORD=1 rspec
|
14
|
+
unless ENV['RERECORD'].to_i.zero?
|
15
|
+
$stderr << "Re-Recording...\n\n"
|
16
|
+
|
17
|
+
VCR.configure do |c|
|
18
|
+
c.default_cassette_options = {record: :all}
|
19
|
+
end
|
20
|
+
end
|
metadata
ADDED
@@ -0,0 +1,104 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: address_validator
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.1.1
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- robhurring
|
8
|
+
autorequire:
|
9
|
+
bindir: bin
|
10
|
+
cert_chain: []
|
11
|
+
date: 2015-11-15 00:00:00.000000000 Z
|
12
|
+
dependencies:
|
13
|
+
- !ruby/object:Gem::Dependency
|
14
|
+
name: httparty
|
15
|
+
requirement: !ruby/object:Gem::Requirement
|
16
|
+
requirements:
|
17
|
+
- - '>='
|
18
|
+
- !ruby/object:Gem::Version
|
19
|
+
version: 0.11.0
|
20
|
+
type: :runtime
|
21
|
+
prerelease: false
|
22
|
+
version_requirements: !ruby/object:Gem::Requirement
|
23
|
+
requirements:
|
24
|
+
- - '>='
|
25
|
+
- !ruby/object:Gem::Version
|
26
|
+
version: 0.11.0
|
27
|
+
- !ruby/object:Gem::Dependency
|
28
|
+
name: builder
|
29
|
+
requirement: !ruby/object:Gem::Requirement
|
30
|
+
requirements:
|
31
|
+
- - '>='
|
32
|
+
- !ruby/object:Gem::Version
|
33
|
+
version: 3.0.4
|
34
|
+
type: :runtime
|
35
|
+
prerelease: false
|
36
|
+
version_requirements: !ruby/object:Gem::Requirement
|
37
|
+
requirements:
|
38
|
+
- - '>='
|
39
|
+
- !ruby/object:Gem::Version
|
40
|
+
version: 3.0.4
|
41
|
+
description: UPS address validation gem
|
42
|
+
email:
|
43
|
+
- robhurring@gmail.com
|
44
|
+
executables: []
|
45
|
+
extensions: []
|
46
|
+
extra_rdoc_files: []
|
47
|
+
files:
|
48
|
+
- .gitignore
|
49
|
+
- .rspec
|
50
|
+
- .travis.yml
|
51
|
+
- Gemfile
|
52
|
+
- LICENSE.txt
|
53
|
+
- README.md
|
54
|
+
- Rakefile
|
55
|
+
- address_validator.gemspec
|
56
|
+
- lib/address_validator.rb
|
57
|
+
- lib/address_validator/address.rb
|
58
|
+
- lib/address_validator/client.rb
|
59
|
+
- lib/address_validator/config.rb
|
60
|
+
- lib/address_validator/response.rb
|
61
|
+
- lib/address_validator/validator.rb
|
62
|
+
- lib/address_validator/version.rb
|
63
|
+
- spec/config.yml.example
|
64
|
+
- spec/data/vcr_cassettes/invalid-address.yml
|
65
|
+
- spec/data/vcr_cassettes/valid-address-hash.yml
|
66
|
+
- spec/data/vcr_cassettes/valid-address.yml
|
67
|
+
- spec/lib/address_validator/address_spec.rb
|
68
|
+
- spec/lib/address_validator/validator_spec.rb
|
69
|
+
- spec/lib/address_validator_spec.rb
|
70
|
+
- spec/spec_helper.rb
|
71
|
+
- spec/support/vcr.rb
|
72
|
+
homepage: ''
|
73
|
+
licenses: []
|
74
|
+
metadata: {}
|
75
|
+
post_install_message:
|
76
|
+
rdoc_options: []
|
77
|
+
require_paths:
|
78
|
+
- lib
|
79
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
80
|
+
requirements:
|
81
|
+
- - '>='
|
82
|
+
- !ruby/object:Gem::Version
|
83
|
+
version: '0'
|
84
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
85
|
+
requirements:
|
86
|
+
- - '>='
|
87
|
+
- !ruby/object:Gem::Version
|
88
|
+
version: '0'
|
89
|
+
requirements: []
|
90
|
+
rubyforge_project:
|
91
|
+
rubygems_version: 2.4.8
|
92
|
+
signing_key:
|
93
|
+
specification_version: 4
|
94
|
+
summary: Simple address validator using the UPS address validation API
|
95
|
+
test_files:
|
96
|
+
- spec/config.yml.example
|
97
|
+
- spec/data/vcr_cassettes/invalid-address.yml
|
98
|
+
- spec/data/vcr_cassettes/valid-address-hash.yml
|
99
|
+
- spec/data/vcr_cassettes/valid-address.yml
|
100
|
+
- spec/lib/address_validator/address_spec.rb
|
101
|
+
- spec/lib/address_validator/validator_spec.rb
|
102
|
+
- spec/lib/address_validator_spec.rb
|
103
|
+
- spec/spec_helper.rb
|
104
|
+
- spec/support/vcr.rb
|