usps-fork 0.1.0

This diff has not been reviewed by any users.
Sign up to get free protection for your applications and to get access to all the features.
Files changed (64) hide show
  1. checksums.yaml +7 -0
  2. data/LICENSE +20 -0
  3. data/README.md +89 -0
  4. data/Rakefile +15 -0
  5. data/lib/usps.rb +47 -0
  6. data/lib/usps/address.rb +72 -0
  7. data/lib/usps/client.rb +55 -0
  8. data/lib/usps/configuration.rb +17 -0
  9. data/lib/usps/errors.rb +46 -0
  10. data/lib/usps/request.rb +14 -0
  11. data/lib/usps/request/address_standardization.rb +44 -0
  12. data/lib/usps/request/base.rb +42 -0
  13. data/lib/usps/request/city_and_state_lookup.rb +32 -0
  14. data/lib/usps/request/delivery_confirmation.rb +88 -0
  15. data/lib/usps/request/delivery_confirmation_certify.rb +10 -0
  16. data/lib/usps/request/tracking_field_lookup.rb +28 -0
  17. data/lib/usps/request/tracking_lookup.rb +28 -0
  18. data/lib/usps/request/zip_code_lookup.rb +45 -0
  19. data/lib/usps/response.rb +8 -0
  20. data/lib/usps/response/address_standardization.rb +52 -0
  21. data/lib/usps/response/base.rb +13 -0
  22. data/lib/usps/response/city_and_state_lookup.rb +42 -0
  23. data/lib/usps/response/delivery_confirmation.rb +25 -0
  24. data/lib/usps/response/tracking_field_lookup.rb +34 -0
  25. data/lib/usps/response/tracking_lookup.rb +19 -0
  26. data/lib/usps/test.rb +34 -0
  27. data/lib/usps/test/address_verification.rb +31 -0
  28. data/lib/usps/test/city_and_state_lookup.rb +19 -0
  29. data/lib/usps/test/delivery_confirmation.rb +59 -0
  30. data/lib/usps/test/tracking_lookup.rb +29 -0
  31. data/lib/usps/test/zip_code_lookup.rb +37 -0
  32. data/lib/usps/track_detail.rb +49 -0
  33. data/lib/usps/version.rb +3 -0
  34. data/spec/address_spec.rb +65 -0
  35. data/spec/configuration_spec.rb +19 -0
  36. data/spec/data/address_standardization_1.xml +1 -0
  37. data/spec/data/address_standardization_2.xml +1 -0
  38. data/spec/data/city_and_state_lookup_1.xml +1 -0
  39. data/spec/data/city_and_state_lookup_2.xml +1 -0
  40. data/spec/data/delivery_confirmation_1.xml +1 -0
  41. data/spec/data/delivery_confirmation_2.xml +1 -0
  42. data/spec/data/tracking_field_lookup.xml +41 -0
  43. data/spec/data/tracking_field_lookup_2.xml +17 -0
  44. data/spec/data/tracking_lookup_1.xml +1 -0
  45. data/spec/data/tracking_lookup_2.xml +1 -0
  46. data/spec/request/address_standardization_spec.rb +45 -0
  47. data/spec/request/base_spec.rb +16 -0
  48. data/spec/request/city_and_state_lookup_spec.rb +32 -0
  49. data/spec/request/delivery_confirmation_certify_spec.rb +11 -0
  50. data/spec/request/delivery_confirmation_spec.rb +57 -0
  51. data/spec/request/tracking_field_spec.rb +20 -0
  52. data/spec/request/tracking_spec.rb +21 -0
  53. data/spec/request/zip_code_lookup_spec.rb +42 -0
  54. data/spec/response/address_standardization_spec.rb +54 -0
  55. data/spec/response/base_spec.rb +0 -0
  56. data/spec/response/city_and_state_lookup_spec.rb +27 -0
  57. data/spec/response/delivery_confirmation_spec.rb +43 -0
  58. data/spec/response/tracking_field_lookup_spec.rb +29 -0
  59. data/spec/response/tracking_lookup_spec.rb +27 -0
  60. data/spec/spec.opts +1 -0
  61. data/spec/spec_helper.rb +13 -0
  62. data/spec/track_detail_spec.rb +51 -0
  63. data/spec/usps_spec.rb +8 -0
  64. metadata +147 -0
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: 889fe783648fc3fec6339e83589c3a2082142798
4
+ data.tar.gz: a7f2607ab4f25de1e616ff259d07d159e33887d4
5
+ SHA512:
6
+ metadata.gz: 0ec28c0dae08024f7df5116f411a1ac11c6221a5f31c4457ee736060cc410fa9bfcee80fcee9f9acd7ff6db8da0b4aa9f6304fe134ff2f3d76311c3f7c3a4c54
7
+ data.tar.gz: 695cfb8dff0db59bb3a63fa3e5efe99723fc191e0a4fcb11d8516ef571dec820e42d665f22a9102236426e24f2675c132f5f350b0ec45fd1f2e0334a3db265ff
data/LICENSE ADDED
@@ -0,0 +1,20 @@
1
+ Copyright (c) 2009 Chris Gaffney
2
+
3
+ Permission is hereby granted, free of charge, to any person obtaining
4
+ a copy of this software and associated documentation files (the
5
+ "Software"), to deal in the Software without restriction, including
6
+ without limitation the rights to use, copy, modify, merge, publish,
7
+ distribute, sublicense, and/or sell copies of the Software, and to
8
+ permit persons to whom the Software is furnished to do so, subject to
9
+ the following conditions:
10
+
11
+ The above copyright notice and this permission notice shall be
12
+ included in all copies or substantial portions of the Software.
13
+
14
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
15
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
16
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
17
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
18
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
19
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
20
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
@@ -0,0 +1,89 @@
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
+ ## Further Reading
83
+
84
+ - [USPS API docs](https://www.usps.com/business/web-tools-apis/welcome.htm)
85
+ - [18F's USPS API Notes](https://github.com/18F/usps-api-notes)
86
+
87
+ ## Copyright
88
+
89
+ Copyright (c) 2014 Chris Gaffney. See LICENSE for details.
@@ -0,0 +1,15 @@
1
+ require 'bundler/setup'
2
+ Bundler::GemHelper.install_tasks
3
+
4
+ require 'rake'
5
+ require 'rspec/core/rake_task'
6
+
7
+ desc "Run the certification tests against USPS's API. Requires ENV['USPS_USER'] to be set or passed in."
8
+ task :certify do
9
+ ruby "-rubygems -Ilib lib/usps/test.rb"
10
+ end
11
+
12
+ desc "Run RSpec tests"
13
+ RSpec::Core::RakeTask.new
14
+
15
+ task :default => :spec
@@ -0,0 +1,47 @@
1
+ require 'builder'
2
+ require 'nokogiri'
3
+
4
+ module USPS
5
+ require 'usps/errors'
6
+ require 'usps/configuration'
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'
13
+ autoload :TrackDetail, 'usps/track_detail'
14
+
15
+ class << self
16
+ attr_writer :config
17
+
18
+ def client
19
+ @client ||= Client.new
20
+ end
21
+
22
+ def testing=(val)
23
+ config.testing = val
24
+ end
25
+
26
+ def config
27
+ @config ||= Configuration.new
28
+ end
29
+
30
+ def configure(&block)
31
+ block.call(self.config)
32
+ end
33
+
34
+ # These two methods are currently here for backwards compatability
35
+ def username=(user)
36
+ config.username = user
37
+ end
38
+
39
+ def username
40
+ config.username
41
+ end
42
+
43
+ def get_city_and_state_for_zip(zip)
44
+ USPS::Request::CityAndStateLookup.new(zip).send!.get(zip)
45
+ end
46
+ end
47
+ end
@@ -0,0 +1,72 @@
1
+ # TODO: Documentation
2
+ #
3
+ # The USPS API uses a standard where Address2 is the street adress and Address1 is
4
+ # the apartment, suite, etc... I have switched them to match how I see them on an envelope.
5
+ # Additionally they are refered to address and extra_address though both address1 and address2
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, :return_text)
8
+
9
+ # Alias address getters and setters for a slightly more expressive api
10
+ alias :address :address1
11
+ alias :address= :address1=
12
+ alias :extra_address :address2
13
+ alias :extra_address= :address2=
14
+
15
+ # The USPS always refers to company as firm
16
+ alias :firm :company
17
+ alias :firm= :company=
18
+
19
+ attr_reader :error
20
+
21
+ def initialize(options = {}, &block)
22
+ options.each_pair do |k, v|
23
+ self.send("#{k}=", v)
24
+ end
25
+
26
+ block.call(self) if block
27
+ end
28
+
29
+ def zip
30
+ zip4 ? "#{zip5}-#{zip4}" : zip5.to_s
31
+ end
32
+
33
+ # Sets zip5 and zip4 if given a zip code in the format "99881" or "99881-1234"
34
+ def zip=(val)
35
+ self.zip5, self.zip4 = val.to_s.split('-')
36
+ end
37
+
38
+ # Check with the USPS if this address can be verified and will in missing
39
+ # fields (such as zip code) if they are available.
40
+ def valid?
41
+ @error = nil
42
+ standardize
43
+ true
44
+ rescue USPS::Error => e
45
+ @error = e
46
+ false
47
+ end
48
+
49
+ def standardize
50
+ response = USPS::Request::AddressStandardization.new(self).send!
51
+ response[self]
52
+ end
53
+
54
+ def standardize!
55
+ replace(self.standardize)
56
+ end
57
+
58
+ # Similar to Hash#replace, overwrite the values of this object with the other.
59
+ # It will not replace a provided key on the original object that does not exist
60
+ # on the replacing object (such as name with verification requests).
61
+ def replace(other)
62
+ raise ArgumentError unless other.is_a?(USPS::Address)
63
+
64
+ other.each_pair do |key, val|
65
+ # Do not overwrite values that may exist on the original but not on
66
+ # the replacement.
67
+ self[key] = val unless val.nil?
68
+ end
69
+
70
+ self
71
+ end
72
+ end
@@ -0,0 +1,55 @@
1
+ require 'typhoeus'
2
+
3
+ module USPS
4
+ class Client
5
+ def request(request, &block)
6
+ server = server(request)
7
+
8
+ # Make the API request to the USPS servers. Used to support POST, now it's
9
+ # just GET request *grumble*.
10
+ options = { timeout: USPS.config.timeout,
11
+ params: { "API" => request.api, "XML" => request.build } }
12
+
13
+ unless USPS.config.proxy.blank?
14
+ options.merge!({ proxy: USPS.config.proxy })
15
+ end
16
+
17
+ response = Typhoeus::Request.get(server, options)
18
+
19
+ # Parse the request
20
+ xml = Nokogiri::XML.parse(response.body)
21
+
22
+ # Process and raise errors
23
+ if((error = xml.search('Error')).any?)
24
+ # This is where things get a little tricky. There are a bunch of errors
25
+ # that the USPS can send back.
26
+ why = error.search('Description').text
27
+ code = error.search('Number').text
28
+ source = error.search('Source').text
29
+
30
+ raise Error.for_code(code).new(why, code, source)
31
+ end
32
+
33
+ # Initialize the proper response object and parse the message
34
+ request.response_for(xml)
35
+ end
36
+
37
+ def testing?
38
+ USPS.config.testing
39
+ end
40
+
41
+ private
42
+ def server(request)
43
+ dll = testing? ? "ShippingAPITest.dll" : "ShippingAPI.dll"
44
+
45
+ case
46
+ when request.secure?
47
+ "https://secure.shippingapis.com/#{dll}"
48
+ when testing?
49
+ "http://testing.shippingapis.com/#{dll}"
50
+ else
51
+ "http://production.shippingapis.com/#{dll}"
52
+ end
53
+ end
54
+ end
55
+ end
@@ -0,0 +1,17 @@
1
+ module USPS
2
+ # Configuration options:
3
+ # username: Provided by the USPS during registration
4
+ # timeout: Connection timeout in milliseconds, nil to disable
5
+ # testing: Should requests be done against the testing service or not
6
+ # (only specific requests are supported).
7
+ class Configuration < Struct.new(:username, :timeout, :testing, :proxy)
8
+ def initialize
9
+ self.timeout = 5
10
+ self.testing = false
11
+ self.username = ENV['USPS_USER']
12
+ self.proxy = nil
13
+ end
14
+
15
+ alias :testing? :testing
16
+ end
17
+ end
@@ -0,0 +1,46 @@
1
+ module USPS
2
+ # Error Hierarchy
3
+ #
4
+ # StandardError
5
+ # USPS::Error
6
+ # USPS::AuthorizationError
7
+ # USPS::ValidationError
8
+ # USPS::InvalidCityError
9
+ # USPS::InvalidStateError
10
+ # USPS::AddressNotFoundError
11
+ # USPS::MultipleAddressError
12
+ # USPS::InvalidImageTypeError
13
+ class Error < StandardError
14
+ attr_reader :number, :source
15
+
16
+ def initialize(message, number, source)
17
+ super(message)
18
+
19
+ @number = number
20
+ @source = source
21
+ end
22
+
23
+ class << self
24
+ def for_code(code)
25
+ case code
26
+ when '80040b1a' ; AuthorizationError
27
+ when '-2147219400'; InvalidCityError
28
+ when '-2147219401'; AddressNotFoundError
29
+ when '-2147219402'; InvalidStateError
30
+ when '-2147219403'; MultipleAddressError
31
+ when '-2147218900'; InvalidImageTypeError
32
+ else ; Error
33
+ end
34
+ end
35
+ end
36
+ end
37
+
38
+ class AuthorizationError < Error; end
39
+
40
+ class ValidationError < Error; end
41
+ class InvalidCityError < ValidationError; end
42
+ class InvalidStateError < ValidationError; end
43
+ class AddressNotFoundError < ValidationError; end
44
+ class MultipleAddressError < ValidationError; end
45
+ class InvalidImageTypeError < ValidationError; end
46
+ end
@@ -0,0 +1,14 @@
1
+ module USPS::Request
2
+ autoload :Base, 'usps/request/base'
3
+ autoload :ZipCodeLookup, 'usps/request/zip_code_lookup'
4
+ autoload :CityAndStateLookup, 'usps/request/city_and_state_lookup'
5
+ autoload :AddressStandardization, 'usps/request/address_standardization'
6
+
7
+ # Delivery and Signature confirmation.
8
+ # DeliveryConfirmationCertify and SignatureConfirmationCertify should be used for testing
9
+ autoload :DeliveryConfirmation, 'usps/request/delivery_confirmation'
10
+ autoload :DeliveryConfirmationCertify, 'usps/request/delivery_confirmation_certify'
11
+
12
+ autoload :TrackingLookup, 'usps/request/tracking_lookup'
13
+ autoload :TrackingFieldLookup, 'usps/request/tracking_field_lookup'
14
+ end
@@ -0,0 +1,44 @@
1
+ module USPS::Request
2
+ # TODO: #send! could be made smarter to send lookup batches
3
+ class AddressStandardization < Base
4
+ config(
5
+ :api => 'Verify',
6
+ :tag => 'AddressValidateRequest',
7
+ :secure => false,
8
+ :response => USPS::Response::AddressStandardization
9
+ )
10
+
11
+ # At most 5 addresses can be verified
12
+ def initialize(*addresses)
13
+ @addresses = addresses.flatten
14
+
15
+ if @addresses.size > 5
16
+ raise ArgumentError, 'at most 5 addresses can be verified at a time'
17
+ end
18
+ end
19
+
20
+ def response_for(xml)
21
+ self.class.response.new(@addresses, xml)
22
+ end
23
+
24
+ def build
25
+ super do |builder|
26
+ @addresses.each_with_index do |addy, i|
27
+ builder.tag!('Address', :ID => i) do
28
+ builder.tag!('FirmName', addy.firm)
29
+
30
+ # Address fields are swapped in the USPS API
31
+ builder.tag!('Address1', addy.extra_address)
32
+ builder.tag!('Address2', addy.address)
33
+
34
+ builder.tag!('City', addy.city)
35
+ builder.tag!('State', addy.state)
36
+
37
+ builder.tag!('Zip5', addy.zip5)
38
+ builder.tag!('Zip4', addy.zip4)
39
+ end
40
+ end
41
+ end
42
+ end
43
+ end
44
+ end
@@ -0,0 +1,42 @@
1
+ module USPS::Request
2
+ class Base
3
+ class << self
4
+ attr_reader :api, :tag, :secure, :response
5
+
6
+ alias :secure? :secure
7
+
8
+ # Config given
9
+ # api: The USPS API name as given in the request URL
10
+ # tag: The root tag used for the request
11
+ # secure: Whether or not the request is against the secure server
12
+ # response: The USPS::Response class used to handle responses
13
+ def config(options = {})
14
+ @api = options[:api].to_s
15
+ @tag = options[:tag].to_s
16
+ @secure = !!options[:secure]
17
+ @response = options[:response]
18
+ end
19
+ end
20
+
21
+ def send!
22
+ USPS.client.request(self)
23
+ end
24
+
25
+ def secure?
26
+ !!self.class.secure?
27
+ end
28
+
29
+ def api
30
+ self.class.api
31
+ end
32
+
33
+ def response_for(xml)
34
+ self.class.response.parse(xml)
35
+ end
36
+
37
+ def build(&block)
38
+ builder = Builder::XmlMarkup.new(:indent => 0)
39
+ builder.tag!(self.class.tag, :USERID => USPS.config.username, &block)
40
+ end
41
+ end
42
+ end