usps-fork 0.1.0

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