usps 0.1.2 → 0.1.3

Sign up to get free protection for your applications and to get access to all the features.
Files changed (39) hide show
  1. checksums.yaml +7 -0
  2. data/README.md +84 -0
  3. data/Rakefile +6 -8
  4. data/lib/usps.rb +6 -5
  5. data/lib/usps/address.rb +1 -1
  6. data/lib/usps/configuration.rb +1 -1
  7. data/lib/usps/request.rb +2 -1
  8. data/lib/usps/request/tracking_field_lookup.rb +28 -0
  9. data/lib/usps/response.rb +1 -0
  10. data/lib/usps/response/address_standardization.rb +15 -1
  11. data/lib/usps/response/city_and_state_lookup.rb +16 -2
  12. data/lib/usps/response/tracking_field_lookup.rb +34 -0
  13. data/lib/usps/response/tracking_lookup.rb +1 -1
  14. data/lib/usps/track_detail.rb +49 -0
  15. data/lib/usps/version.rb +1 -1
  16. data/spec/address_spec.rb +14 -14
  17. data/spec/configuration_spec.rb +4 -4
  18. data/spec/data/tracking_field_lookup.xml +41 -0
  19. data/spec/data/tracking_field_lookup_2.xml +17 -0
  20. data/spec/request/address_standardization_spec.rb +13 -13
  21. data/spec/request/base_spec.rb +2 -2
  22. data/spec/request/city_and_state_lookup_spec.rb +10 -10
  23. data/spec/request/delivery_confirmation_certify_spec.rb +3 -3
  24. data/spec/request/delivery_confirmation_spec.rb +27 -27
  25. data/spec/request/tracking_field_spec.rb +20 -0
  26. data/spec/request/tracking_spec.rb +5 -5
  27. data/spec/request/zip_code_lookup_spec.rb +11 -11
  28. data/spec/response/address_standardization_spec.rb +9 -9
  29. data/spec/response/city_and_state_lookup_spec.rb +6 -6
  30. data/spec/response/delivery_confirmation_spec.rb +19 -19
  31. data/spec/response/tracking_field_lookup_spec.rb +28 -0
  32. data/spec/response/tracking_lookup_spec.rb +10 -10
  33. data/spec/track_detail_spec.rb +51 -0
  34. data/spec/usps_spec.rb +1 -1
  35. metadata +66 -90
  36. data/README.rdoc +0 -60
  37. data/lib/usps/request/signature_confirmation.rb +0 -44
  38. data/spec/request/signature_confirmation_spec.rb +0 -0
  39. data/spec/response/signature_confirmation_spec.rb +0 -0
@@ -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
@@ -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 'spec/rake/spectask'
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
@@ -5,11 +5,12 @@ module USPS
5
5
  require 'usps/errors'
6
6
  require 'usps/configuration'
7
7
 
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'
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
@@ -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
@@ -6,7 +6,7 @@ module USPS
6
6
  # (only specific requests are supported).
7
7
  class Configuration < Struct.new(:username, :timeout, :testing)
8
8
  def initialize
9
- self.timeout = 5000
9
+ self.timeout = 5
10
10
  self.testing = false
11
11
  self.username = ENV['USPS_USER']
12
12
  end
@@ -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, 'usps/request/tracking_lookup'
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
@@ -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 an address representing the standardized version of the given
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
@@ -14,6 +14,6 @@ module USPS::Response
14
14
  @details << detail.text
15
15
  end
16
16
  end
17
-
17
+
18
18
  end
19
19
  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
@@ -1,3 +1,3 @@
1
1
  module USPS
2
- VERSION = '0.1.2'
2
+ VERSION = '0.1.3'
3
3
  end
@@ -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.should respond_to(
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.should == 'Chris'
26
- address.address.should == '123 Main St'
27
- address.city.should == 'Holland'
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.should == '12345'
32
- USPS::Address.new(:zip5 => 12345, :zip4 => 9999).zip.should == '12345-9999'
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.should == '12345'
38
- addy.zip4.should == '9988'
39
- addy.zip.should == '12345-9988'
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.should_receive(:request).and_return(
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?.should be_true
57
+ expect(addy.valid?).to be_truthy
58
58
 
59
59
  error = USPS::Error.new('error', '1234', 'source')
60
60
  # Failure
61
- USPS.client.should_receive(:request).and_raise(error)
62
- addy.valid?.should be_false
63
- addy.error.should be(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
@@ -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.should be_nil
11
- @config.timeout.should == 5000
12
- @config.testing.should be_false
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.should == 'malcom77'
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>