usps 0.1.2 → 0.1.3
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/README.md +84 -0
- data/Rakefile +6 -8
- data/lib/usps.rb +6 -5
- data/lib/usps/address.rb +1 -1
- data/lib/usps/configuration.rb +1 -1
- data/lib/usps/request.rb +2 -1
- data/lib/usps/request/tracking_field_lookup.rb +28 -0
- data/lib/usps/response.rb +1 -0
- data/lib/usps/response/address_standardization.rb +15 -1
- data/lib/usps/response/city_and_state_lookup.rb +16 -2
- data/lib/usps/response/tracking_field_lookup.rb +34 -0
- data/lib/usps/response/tracking_lookup.rb +1 -1
- data/lib/usps/track_detail.rb +49 -0
- data/lib/usps/version.rb +1 -1
- data/spec/address_spec.rb +14 -14
- data/spec/configuration_spec.rb +4 -4
- data/spec/data/tracking_field_lookup.xml +41 -0
- data/spec/data/tracking_field_lookup_2.xml +17 -0
- data/spec/request/address_standardization_spec.rb +13 -13
- data/spec/request/base_spec.rb +2 -2
- data/spec/request/city_and_state_lookup_spec.rb +10 -10
- data/spec/request/delivery_confirmation_certify_spec.rb +3 -3
- data/spec/request/delivery_confirmation_spec.rb +27 -27
- data/spec/request/tracking_field_spec.rb +20 -0
- data/spec/request/tracking_spec.rb +5 -5
- data/spec/request/zip_code_lookup_spec.rb +11 -11
- data/spec/response/address_standardization_spec.rb +9 -9
- data/spec/response/city_and_state_lookup_spec.rb +6 -6
- data/spec/response/delivery_confirmation_spec.rb +19 -19
- data/spec/response/tracking_field_lookup_spec.rb +28 -0
- data/spec/response/tracking_lookup_spec.rb +10 -10
- data/spec/track_detail_spec.rb +51 -0
- data/spec/usps_spec.rb +1 -1
- metadata +66 -90
- data/README.rdoc +0 -60
- data/lib/usps/request/signature_confirmation.rb +0 -44
- data/spec/request/signature_confirmation_spec.rb +0 -0
- data/spec/response/signature_confirmation_spec.rb +0 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA1:
|
3
|
+
metadata.gz: 32013f6daa8af9cd35517d0b37b200bf9db91974
|
4
|
+
data.tar.gz: d9309024e63911cf3cb3d3b150160e77963f4ee3
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: 391f24ffa46925ac60facbfa9ee5ab1add219781d35c216b093257c12866ffc329f59f2ca8d89165567bbae8a051f57be706329366c3d7e84d3edfb28dee21d6
|
7
|
+
data.tar.gz: c1f48611b6b72cd797266ddb77efba8d5a7736fdedfeaebc4a3d839e63c543720cdedfa9c5f2e93be35d436c02c02c626ab41cd4652bc60c288ede67098a6992
|
data/README.md
ADDED
@@ -0,0 +1,84 @@
|
|
1
|
+
# usps
|
2
|
+
|
3
|
+
[![Build Status](https://travis-ci.org/gaffneyc/usps.svg?branch=master)](https://travis-ci.org/gaffneyc/usps)
|
4
|
+
|
5
|
+
Ruby API for accessing the USPS WebTools API found here: https://www.usps.com/business/webtools.htm
|
6
|
+
|
7
|
+
PDF Guides can be found here: https://www.usps.com/business/webtools-technical-guides.htm
|
8
|
+
|
9
|
+
Usage of this library assumes you already have a USPS API account and that all priviledges have been granted.
|
10
|
+
|
11
|
+
## Project Status - Looking for a Maintainer
|
12
|
+
|
13
|
+
This code is no longer properly maintained as I'm no longer with the company it was developed for.
|
14
|
+
If you're using it and would be interested in maintaining it please send me a message and I can
|
15
|
+
get you set up.
|
16
|
+
|
17
|
+
## Exposed API Calls
|
18
|
+
|
19
|
+
The following USPS API calls are currently exposed through this library:
|
20
|
+
|
21
|
+
```
|
22
|
+
<AddressValidateRequest> -- USPS::Request::AddressStandardization
|
23
|
+
<CityStateLookupRequest> -- USPS::Request::CityAndStateLookup
|
24
|
+
<ZipCodeLookupRequest> -- USPS::Request::ZipCodeLookup
|
25
|
+
<TrackRequest> -- USPS::Request::TrackingLookup
|
26
|
+
<TrackFieldRequest> -- USPS::Request::TrackingFieldLookup
|
27
|
+
|
28
|
+
<DeliveryConfirmationV3.0Request> -- USPS::Request::DeliveryConfirmation (for production)
|
29
|
+
<DeliveryConfirmCertifyV3.0Request> -- USPS::Request::DeliveryConfirmationCertify (for testing)
|
30
|
+
```
|
31
|
+
|
32
|
+
## Usage
|
33
|
+
|
34
|
+
Using the library is as simple as building a new USPS::Request::[type] object, calling #send! and using the response.
|
35
|
+
For example, to send a tracking request you'd do the following:
|
36
|
+
|
37
|
+
```ruby
|
38
|
+
request = USPS::Request::TrackingLookup.new(tracking_number)
|
39
|
+
response = request.send!
|
40
|
+
|
41
|
+
response.summary
|
42
|
+
response.details
|
43
|
+
```
|
44
|
+
|
45
|
+
The library assumes that either ENV['USPS_USER'] is set, or that you set USPS.username to your USPS API username.
|
46
|
+
|
47
|
+
See the individual USPS::Request classes for details on how to use them.
|
48
|
+
|
49
|
+
## USPS API Certification
|
50
|
+
|
51
|
+
Part of the process of setting up an account with the USPS API is to run certain tests against the USPS API.
|
52
|
+
This library has all the requisite tests built in, runnable with rake:
|
53
|
+
|
54
|
+
```
|
55
|
+
$ USPS_USER="[username]" rake certify
|
56
|
+
```
|
57
|
+
|
58
|
+
or as an installed gem:
|
59
|
+
|
60
|
+
```
|
61
|
+
$ USPS_USER="[username]" ruby -rubygems -e "require 'usps/test'"
|
62
|
+
```
|
63
|
+
|
64
|
+
If any of the tests fail, you don't have access to that API and may need to work with USPS to fix it.
|
65
|
+
|
66
|
+
## Note on Patches/Pull Requests
|
67
|
+
|
68
|
+
* Fork the project.
|
69
|
+
* Make your feature addition or bug fix.
|
70
|
+
* Add tests for it. This is important so I don't break it in a
|
71
|
+
future version unintentionally.
|
72
|
+
* Commit, do not mess with rakefile, version, or history.
|
73
|
+
(if you want to have your own version, that is fine but bump version in a commit by itself I can ignore when I pull)
|
74
|
+
* Send me a pull request. Bonus points for topic branches.
|
75
|
+
|
76
|
+
## Testing
|
77
|
+
|
78
|
+
```
|
79
|
+
$ rspec
|
80
|
+
```
|
81
|
+
|
82
|
+
## Copyright
|
83
|
+
|
84
|
+
Copyright (c) 2014 Chris Gaffney. See LICENSE for details.
|
data/Rakefile
CHANGED
@@ -2,16 +2,14 @@ require 'bundler/setup'
|
|
2
2
|
Bundler::GemHelper.install_tasks
|
3
3
|
|
4
4
|
require 'rake'
|
5
|
-
require '
|
6
|
-
|
7
|
-
Spec::Rake::SpecTask.new(:spec) do |spec|
|
8
|
-
spec.libs << 'lib' << 'spec'
|
9
|
-
spec.spec_files = FileList['spec/**/*_spec.rb']
|
10
|
-
end
|
11
|
-
|
12
|
-
task :default => :spec
|
5
|
+
require 'rspec/core/rake_task'
|
13
6
|
|
14
7
|
desc "Run the certification tests against USPS's API. Requires ENV['USPS_USER'] to be set or passed in."
|
15
8
|
task :certify do
|
16
9
|
ruby "-rubygems -Ilib lib/usps/test.rb"
|
17
10
|
end
|
11
|
+
|
12
|
+
desc "Run RSpec tests"
|
13
|
+
RSpec::Core::RakeTask.new
|
14
|
+
|
15
|
+
task :default => :spec
|
data/lib/usps.rb
CHANGED
@@ -5,11 +5,12 @@ module USPS
|
|
5
5
|
require 'usps/errors'
|
6
6
|
require 'usps/configuration'
|
7
7
|
|
8
|
-
autoload :Client,
|
9
|
-
autoload :Address,
|
10
|
-
autoload :Request,
|
11
|
-
autoload :VERSION,
|
12
|
-
autoload :Response,
|
8
|
+
autoload :Client, 'usps/client'
|
9
|
+
autoload :Address, 'usps/address'
|
10
|
+
autoload :Request, 'usps/request'
|
11
|
+
autoload :VERSION, 'usps/version'
|
12
|
+
autoload :Response, 'usps/response'
|
13
|
+
autoload :TrackDetail, 'usps/track_detail'
|
13
14
|
|
14
15
|
class << self
|
15
16
|
attr_writer :config
|
data/lib/usps/address.rb
CHANGED
@@ -4,7 +4,7 @@
|
|
4
4
|
# the apartment, suite, etc... I have switched them to match how I see them on an envelope.
|
5
5
|
# Additionally they are refered to address and extra_address though both address1 and address2
|
6
6
|
# work. Just remember they are flip flopped based on the USPS documentation.
|
7
|
-
class USPS::Address < Struct.new(:name, :company, :address1, :address2, :city, :state, :zip5, :zip4)
|
7
|
+
class USPS::Address < Struct.new(:name, :company, :address1, :address2, :city, :state, :zip5, :zip4, :return_text)
|
8
8
|
|
9
9
|
# Alias address getters and setters for a slightly more expressive api
|
10
10
|
alias :address :address1
|
data/lib/usps/configuration.rb
CHANGED
data/lib/usps/request.rb
CHANGED
@@ -9,5 +9,6 @@ module USPS::Request
|
|
9
9
|
autoload :DeliveryConfirmation, 'usps/request/delivery_confirmation'
|
10
10
|
autoload :DeliveryConfirmationCertify, 'usps/request/delivery_confirmation_certify'
|
11
11
|
|
12
|
-
autoload :TrackingLookup,
|
12
|
+
autoload :TrackingLookup, 'usps/request/tracking_lookup'
|
13
|
+
autoload :TrackingFieldLookup, 'usps/request/tracking_field_lookup'
|
13
14
|
end
|
@@ -0,0 +1,28 @@
|
|
1
|
+
module USPS::Request
|
2
|
+
# Given a valid USPS tracking number, use this class to request
|
3
|
+
# tracking information from USPS's systems.
|
4
|
+
#
|
5
|
+
# Returns a USPS::Response::TrackingFieldLookup object with the pertinent
|
6
|
+
# information
|
7
|
+
class TrackingFieldLookup < Base
|
8
|
+
config(
|
9
|
+
:api => 'TrackV2',
|
10
|
+
:tag => 'TrackFieldRequest',
|
11
|
+
:secure => false,
|
12
|
+
:response => USPS::Response::TrackingFieldLookup
|
13
|
+
)
|
14
|
+
|
15
|
+
# Build a new TrackingLookup request.
|
16
|
+
# Takes the USPS tracking number to request information for
|
17
|
+
def initialize(track_id)
|
18
|
+
@track_id = track_id
|
19
|
+
end
|
20
|
+
|
21
|
+
def build
|
22
|
+
super do |builder|
|
23
|
+
builder.tag!('TrackID', :ID => @track_id)
|
24
|
+
end
|
25
|
+
end
|
26
|
+
|
27
|
+
end
|
28
|
+
end
|
data/lib/usps/response.rb
CHANGED
@@ -4,4 +4,5 @@ module USPS::Response
|
|
4
4
|
autoload :DeliveryConfirmation, 'usps/response/delivery_confirmation'
|
5
5
|
autoload :AddressStandardization, 'usps/response/address_standardization'
|
6
6
|
autoload :TrackingLookup, 'usps/response/tracking_lookup'
|
7
|
+
autoload :TrackingFieldLookup, 'usps/response/tracking_field_lookup'
|
7
8
|
end
|
@@ -21,6 +21,19 @@ module USPS::Response
|
|
21
21
|
@addresses[address]
|
22
22
|
end
|
23
23
|
alias :[] :get
|
24
|
+
|
25
|
+
def addresses
|
26
|
+
@addresses
|
27
|
+
end
|
28
|
+
|
29
|
+
def to_h
|
30
|
+
hash = {}
|
31
|
+
@addresses.each_pair do |key, value|
|
32
|
+
hash[key.to_h] = value.to_h
|
33
|
+
end
|
34
|
+
|
35
|
+
hash
|
36
|
+
end
|
24
37
|
|
25
38
|
private
|
26
39
|
def parse(node)
|
@@ -31,7 +44,8 @@ module USPS::Response
|
|
31
44
|
:city => node.search('City').text,
|
32
45
|
:state => node.search('State').text,
|
33
46
|
:zip5 => node.search('Zip5').text,
|
34
|
-
:zip4 => node.search('Zip4').text
|
47
|
+
:zip4 => node.search('Zip4').text,
|
48
|
+
:return_text => node.search('ReturnText').text,
|
35
49
|
)
|
36
50
|
end
|
37
51
|
end
|
@@ -17,12 +17,26 @@ module USPS::Response
|
|
17
17
|
end
|
18
18
|
end
|
19
19
|
|
20
|
-
# Returns
|
21
|
-
# address from the results of the query.
|
20
|
+
# Returns a single city/state pair given a zip5
|
22
21
|
def get(zip)
|
23
22
|
@data[zip.to_i]
|
24
23
|
end
|
25
24
|
alias :[] :get
|
25
|
+
|
26
|
+
# Returns all city/state data from the query results
|
27
|
+
def data
|
28
|
+
@data
|
29
|
+
end
|
30
|
+
|
31
|
+
# Returns all city/state data as a pure Ruby hash (e.g. no Structs as values)
|
32
|
+
def to_h
|
33
|
+
hash = {}
|
34
|
+
@data.each_pair do |key, value|
|
35
|
+
hash[key] = value.to_h
|
36
|
+
end
|
37
|
+
|
38
|
+
hash
|
39
|
+
end
|
26
40
|
end
|
27
41
|
end
|
28
42
|
|
@@ -0,0 +1,34 @@
|
|
1
|
+
module USPS::Response
|
2
|
+
# Response object from a USPS::Request::TrackingLookup request.
|
3
|
+
# Includes a summary of the current status of the shipment, along with
|
4
|
+
# an array of details of the shipment's progress
|
5
|
+
class TrackingFieldLookup < Base
|
6
|
+
|
7
|
+
attr_accessor :summary, :details
|
8
|
+
|
9
|
+
|
10
|
+
def initialize(xml)
|
11
|
+
@summary = parse(xml.search("TrackSummary"))
|
12
|
+
@details = []
|
13
|
+
xml.search("TrackDetail").each do |detail|
|
14
|
+
@details << parse(detail)
|
15
|
+
end
|
16
|
+
end
|
17
|
+
|
18
|
+
private
|
19
|
+
def parse(node)
|
20
|
+
USPS::TrackDetail.new(
|
21
|
+
:event_time => node.search('EventTime').text,
|
22
|
+
:event_date => node.search('EventDate').text,
|
23
|
+
:event => node.search('Event').text,
|
24
|
+
:event_city => node.search('EventCity').text,
|
25
|
+
:event_state => node.search('EventState').text,
|
26
|
+
:event_zip_code => node.search('EventZIPCode').text,
|
27
|
+
:event_country => node.search('EventCountry').text,
|
28
|
+
:firm_name => node.search('FirmName').text,
|
29
|
+
:name => node.search('Name').text,
|
30
|
+
:authorized_agent => node.search('AuthorizedAgent').text
|
31
|
+
)
|
32
|
+
end
|
33
|
+
end
|
34
|
+
end
|
@@ -0,0 +1,49 @@
|
|
1
|
+
require 'time'
|
2
|
+
# TODO: Documentation
|
3
|
+
#
|
4
|
+
class USPS::TrackDetail < Struct.new(:event_time, :event_date, :event, :event_city, :event_state, :event_zip_code, :event_country, :firm_name, :name, :authorized_agent)
|
5
|
+
|
6
|
+
# Alias address getters and setters for a slightly more expressive api
|
7
|
+
alias :city :event_city
|
8
|
+
alias :city= :event_city=
|
9
|
+
alias :state :event_state
|
10
|
+
alias :state= :event_state=
|
11
|
+
alias :zip_code :event_zip_code
|
12
|
+
alias :zip_code= :event_zip_code=
|
13
|
+
alias :country :event_country
|
14
|
+
alias :country= :event_country=
|
15
|
+
|
16
|
+
attr_reader :error
|
17
|
+
|
18
|
+
def initialize(options = {}, &block)
|
19
|
+
options.each_pair do |k, v|
|
20
|
+
self.send("#{k}=", v)
|
21
|
+
end
|
22
|
+
|
23
|
+
block.call(self) if block
|
24
|
+
end
|
25
|
+
|
26
|
+
def date
|
27
|
+
time = "#{event_date} #{event_time}".strip
|
28
|
+
begin
|
29
|
+
Time.parse(time) unless time.empty?
|
30
|
+
rescue ArgumentError
|
31
|
+
return nil
|
32
|
+
end
|
33
|
+
end
|
34
|
+
|
35
|
+
# Similar to Hash#replace, overwrite the values of this object with the other.
|
36
|
+
# It will not replace a provided key on the original object that does not exist
|
37
|
+
# on the replacing object (such as name with verification requests).
|
38
|
+
def replace(other)
|
39
|
+
raise ArgumentError unless other.is_a?(USPS::Address)
|
40
|
+
|
41
|
+
other.each_pair do |key, val|
|
42
|
+
# Do not overwrite values that may exist on the original but not on
|
43
|
+
# the replacement.
|
44
|
+
self[key] = val unless val.nil?
|
45
|
+
end
|
46
|
+
|
47
|
+
self
|
48
|
+
end
|
49
|
+
end
|
data/lib/usps/version.rb
CHANGED
data/spec/address_spec.rb
CHANGED
@@ -4,7 +4,7 @@ describe USPS::Address do
|
|
4
4
|
it "should have the expected fields" do
|
5
5
|
address = USPS::Address.new
|
6
6
|
|
7
|
-
address.
|
7
|
+
expect(address).to respond_to(
|
8
8
|
:name, :name=,
|
9
9
|
:address1, :address1=,
|
10
10
|
:address2, :address2=,
|
@@ -22,21 +22,21 @@ describe USPS::Address do
|
|
22
22
|
:city => 'Holland'
|
23
23
|
)
|
24
24
|
|
25
|
-
address.name.
|
26
|
-
address.address.
|
27
|
-
address.city.
|
25
|
+
expect(address.name).to eq('Chris')
|
26
|
+
expect(address.address).to eq('123 Main St')
|
27
|
+
expect(address.city).to eq('Holland')
|
28
28
|
end
|
29
29
|
|
30
30
|
it "know how to combine the zip coes" do
|
31
|
-
USPS::Address.new(:zip5 => 12345).zip.
|
32
|
-
USPS::Address.new(:zip5 => 12345, :zip4 => 9999).zip.
|
31
|
+
expect(USPS::Address.new(:zip5 => 12345).zip).to eq('12345')
|
32
|
+
expect(USPS::Address.new(:zip5 => 12345, :zip4 => 9999).zip).to eq('12345-9999')
|
33
33
|
end
|
34
34
|
|
35
35
|
it "should be able to parse zip into individual parts" do
|
36
36
|
addy = USPS::Address.new(:zip => '12345-9988')
|
37
|
-
addy.zip5.
|
38
|
-
addy.zip4.
|
39
|
-
addy.zip.
|
37
|
+
expect(addy.zip5).to eq('12345')
|
38
|
+
expect(addy.zip4).to eq('9988')
|
39
|
+
expect(addy.zip).to eq('12345-9988')
|
40
40
|
end
|
41
41
|
|
42
42
|
it "should be able to be verified with the USPS" do
|
@@ -48,18 +48,18 @@ describe USPS::Address do
|
|
48
48
|
:zip => 20006
|
49
49
|
)
|
50
50
|
|
51
|
-
USPS.client.
|
51
|
+
expect(USPS.client).to receive(:request).and_return(
|
52
52
|
USPS::Response::AddressStandardization.new(
|
53
53
|
addy, load_xml('address_standardization_1.xml')
|
54
54
|
)
|
55
55
|
)
|
56
56
|
|
57
|
-
addy.valid
|
57
|
+
expect(addy.valid?).to be_truthy
|
58
58
|
|
59
59
|
error = USPS::Error.new('error', '1234', 'source')
|
60
60
|
# Failure
|
61
|
-
USPS.client.
|
62
|
-
addy.valid
|
63
|
-
addy.error.
|
61
|
+
expect(USPS.client).to receive(:request).and_raise(error)
|
62
|
+
expect(addy.valid?).to be_falsey
|
63
|
+
expect(addy.error).to be(error)
|
64
64
|
end
|
65
65
|
end
|
data/spec/configuration_spec.rb
CHANGED
@@ -7,13 +7,13 @@ describe USPS::Configuration do
|
|
7
7
|
end
|
8
8
|
|
9
9
|
it "should have some sensible defaults" do
|
10
|
-
@config.username.
|
11
|
-
@config.timeout.
|
12
|
-
@config.testing.
|
10
|
+
expect(@config.username).to be_nil
|
11
|
+
expect(@config.timeout).to eq(5)
|
12
|
+
expect(@config.testing).to be_falsey
|
13
13
|
end
|
14
14
|
|
15
15
|
it "should grab the username from the environment if available" do
|
16
16
|
ENV['USPS_USER'] = 'malcom77'
|
17
|
-
USPS::Configuration.new.username.
|
17
|
+
expect(USPS::Configuration.new.username).to eq('malcom77')
|
18
18
|
end
|
19
19
|
end
|
@@ -0,0 +1,41 @@
|
|
1
|
+
<?xml version="1.0"?>
|
2
|
+
<TrackResponse>
|
3
|
+
<TrackInfo ID="01805213907042762274">
|
4
|
+
<TrackSummary>
|
5
|
+
<EventTime>12:12 pm</EventTime>
|
6
|
+
<EventDate>May 21, 2001</EventDate>
|
7
|
+
<Event>DELIVERED</Event>
|
8
|
+
<EventCity>NEWTON</EventCity>
|
9
|
+
<EventState>IA</EventState>
|
10
|
+
<EventZIPCode>50208</EventZIPCode>
|
11
|
+
<EventCountry/>
|
12
|
+
<FirmName></FirmName>
|
13
|
+
<Name></Name>
|
14
|
+
<AuthorizedAgent></AuthorizedAgent>
|
15
|
+
</TrackSummary>
|
16
|
+
<TrackDetail>
|
17
|
+
<EventTime>9:24 pm</EventTime>
|
18
|
+
<EventDate>March 28, 2001</EventDate>
|
19
|
+
<Event>ENROUTE</Event>
|
20
|
+
<EventCity>DES MOINES</EventCity>
|
21
|
+
<EventState>IA</EventState>
|
22
|
+
<EventZIPCode>50395</EventZIPCode>
|
23
|
+
<EventCountry/>
|
24
|
+
<FirmName/>
|
25
|
+
<Name/>
|
26
|
+
<AuthorizedAgent/>
|
27
|
+
</TrackDetail>
|
28
|
+
<TrackDetail>
|
29
|
+
<EventTime>10:00 pm</EventTime>
|
30
|
+
<EventDate>March 27, 2001</EventDate>
|
31
|
+
<Event>ACCEPTANCE</Event>
|
32
|
+
<EventCity>BLAINE</EventCity>
|
33
|
+
<EventState>WA</EventState>
|
34
|
+
<EventZIPCode>98231</EventZIPCode>
|
35
|
+
<EventCountry/>
|
36
|
+
<FirmName/>
|
37
|
+
<Name/>
|
38
|
+
<AuthorizedAgent/>
|
39
|
+
</TrackDetail>
|
40
|
+
</TrackInfo>
|
41
|
+
</TrackResponse>
|