trackerific 0.1.1

Sign up to get free protection for your applications and to get access to all the features.
data/.document ADDED
@@ -0,0 +1,5 @@
1
+ lib/**/*.rb
2
+ bin/*
3
+ -
4
+ features/**/*.feature
5
+ LICENSE.txt
data/.rspec ADDED
@@ -0,0 +1,2 @@
1
+ --colour
2
+ --format d
data/Gemfile ADDED
@@ -0,0 +1,22 @@
1
+ source "http://rubygems.org"
2
+
3
+ gem 'rails', '>= 3.0.0'
4
+
5
+ gem 'httparty'
6
+ gem 'builder'
7
+ gem 'savon'
8
+ gem 'curb'
9
+
10
+ group :development do
11
+ gem 'bundler'
12
+ gem 'jeweler'
13
+ end
14
+
15
+ group :development, :test do
16
+ gem 'rspec'
17
+ gem 'rspec-rails'
18
+ end
19
+
20
+ group :test do
21
+ gem 'fakeweb'
22
+ end
data/Gemfile.lock ADDED
@@ -0,0 +1,115 @@
1
+ GEM
2
+ remote: http://rubygems.org/
3
+ specs:
4
+ abstract (1.0.0)
5
+ actionmailer (3.0.5)
6
+ actionpack (= 3.0.5)
7
+ mail (~> 2.2.15)
8
+ actionpack (3.0.5)
9
+ activemodel (= 3.0.5)
10
+ activesupport (= 3.0.5)
11
+ builder (~> 2.1.2)
12
+ erubis (~> 2.6.6)
13
+ i18n (~> 0.4)
14
+ rack (~> 1.2.1)
15
+ rack-mount (~> 0.6.13)
16
+ rack-test (~> 0.5.7)
17
+ tzinfo (~> 0.3.23)
18
+ activemodel (3.0.5)
19
+ activesupport (= 3.0.5)
20
+ builder (~> 2.1.2)
21
+ i18n (~> 0.4)
22
+ activerecord (3.0.5)
23
+ activemodel (= 3.0.5)
24
+ activesupport (= 3.0.5)
25
+ arel (~> 2.0.2)
26
+ tzinfo (~> 0.3.23)
27
+ activeresource (3.0.5)
28
+ activemodel (= 3.0.5)
29
+ activesupport (= 3.0.5)
30
+ activesupport (3.0.5)
31
+ arel (2.0.9)
32
+ builder (2.1.2)
33
+ crack (0.1.8)
34
+ curb (0.7.15)
35
+ diff-lcs (1.1.2)
36
+ erubis (2.6.6)
37
+ abstract (>= 1.0.0)
38
+ fakeweb (1.3.0)
39
+ git (1.2.5)
40
+ gyoku (0.3.1)
41
+ builder (>= 2.1.2)
42
+ httparty (0.7.4)
43
+ crack (= 0.1.8)
44
+ httpi (0.9.0)
45
+ ntlm-http (>= 0.1.1)
46
+ rack
47
+ i18n (0.5.0)
48
+ jeweler (1.5.2)
49
+ bundler (~> 1.0.0)
50
+ git (>= 1.2.5)
51
+ rake
52
+ mail (2.2.15)
53
+ activesupport (>= 2.3.6)
54
+ i18n (>= 0.4.0)
55
+ mime-types (~> 1.16)
56
+ treetop (~> 1.4.8)
57
+ mime-types (1.16)
58
+ ntlm-http (0.1.1)
59
+ polyglot (0.3.1)
60
+ rack (1.2.2)
61
+ rack-mount (0.6.14)
62
+ rack (>= 1.0.0)
63
+ rack-test (0.5.7)
64
+ rack (>= 1.0)
65
+ rails (3.0.5)
66
+ actionmailer (= 3.0.5)
67
+ actionpack (= 3.0.5)
68
+ activerecord (= 3.0.5)
69
+ activeresource (= 3.0.5)
70
+ activesupport (= 3.0.5)
71
+ bundler (~> 1.0)
72
+ railties (= 3.0.5)
73
+ railties (3.0.5)
74
+ actionpack (= 3.0.5)
75
+ activesupport (= 3.0.5)
76
+ rake (>= 0.8.7)
77
+ thor (~> 0.14.4)
78
+ rake (0.8.7)
79
+ rspec (2.6.0)
80
+ rspec-core (~> 2.6.0)
81
+ rspec-expectations (~> 2.6.0)
82
+ rspec-mocks (~> 2.6.0)
83
+ rspec-core (2.6.4)
84
+ rspec-expectations (2.6.0)
85
+ diff-lcs (~> 1.1.2)
86
+ rspec-mocks (2.6.0)
87
+ rspec-rails (2.6.1)
88
+ actionpack (~> 3.0)
89
+ activesupport (~> 3.0)
90
+ railties (~> 3.0)
91
+ rspec (~> 2.6.0)
92
+ savon (0.8.6)
93
+ builder (>= 2.1.2)
94
+ crack (~> 0.1.8)
95
+ gyoku (>= 0.3.0)
96
+ httpi (>= 0.7.8)
97
+ thor (0.14.6)
98
+ treetop (1.4.9)
99
+ polyglot (>= 0.3.1)
100
+ tzinfo (0.3.26)
101
+
102
+ PLATFORMS
103
+ ruby
104
+
105
+ DEPENDENCIES
106
+ builder
107
+ bundler
108
+ curb
109
+ fakeweb
110
+ httparty
111
+ jeweler
112
+ rails (>= 3.0.0)
113
+ rspec
114
+ rspec-rails
115
+ savon
data/LICENSE.txt ADDED
@@ -0,0 +1,20 @@
1
+ Copyright (c) 2011 Travis Haynes
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.
data/README.rdoc ADDED
@@ -0,0 +1,70 @@
1
+ == Installation:
2
+
3
+ To use this gem, add this line to your Gemfile
4
+ gem 'trackerific', :git => 'http://github.com/travishaynes/trackerific.git'
5
+ and then run
6
+ bundle install
7
+
8
+ == Usage:
9
+
10
+ # Track a FedEx package:
11
+ fedex = Trackerific::FedEx.new :account => '123456789', :meter => '123456789', :key => 'kQdEJwuHBjtQ7g2', :password => 'qqPpiRoE5WdsMO78j8j5GRdoI'
12
+ tracking_info = fedex.track_package('999999999999')
13
+
14
+ # Track a USPS package:
15
+ usps = Trackerific::USPS.new :user_id => '123USERID4567'
16
+ tracking_info = usps.track_package('EJ958083578US')
17
+
18
+ # Track a UPS package:
19
+ ups = Trackerific::UPS.new :user_id => 'userid', :key => 'kQdEJwuHBjtQ7g2', :password => 'secret'
20
+ tracking_info = ups.track_package('1Z12345E0291980793')
21
+
22
+ === Tracking Results
23
+
24
+ The tracking results for all three providers are returned in a Hash in this format:
25
+
26
+ {
27
+ :package_id => 'the package id being tracked',
28
+ :summary => 'summary of the tracking results',
29
+ :details => ['array containing each tracking status by date']
30
+ }
31
+
32
+ Here's an example of how to print the tracking results out in a readable format:
33
+
34
+ puts "Tracking information for package ID: %s" % tracking_info[:package_id]
35
+ puts tracking_info[:summary]
36
+ puts " * %s" % tracking_info[:details].join("\n * ")
37
+
38
+ The above code would yield something that looks like this:
39
+
40
+ Tracking information for package ID: EJ958083578US
41
+ Your item was delivered at 8:10 am on June 1 in Wilmington DE 19801.
42
+ * May 30 11:07 am NOTICE LEFT WILMINGTON DE 19801.
43
+ * May 30 10:08 am ARRIVAL AT UNIT WILMINGTON DE 19850.
44
+ * May 29 9:55 am ACCEPT OR PICKUP EDGEWATER NJ 07020.
45
+
46
+ === Exception handling:
47
+
48
+ Exception handling is esssential for tracking packages. If, for example, you enter the wrong number, or the tracking provider has yet to have added the tracking number to their system, a Trackerific::Error will be raised. Here's an example on how to handle Trackerific::Errors:
49
+
50
+ begin
51
+ usps.track_package('EJ958083578US')
52
+ rescue Trackerific::Error => e
53
+ puts e.message
54
+ end
55
+
56
+ == Contributing to trackerific
57
+
58
+ * Check out the latest master to make sure the feature hasn't been implemented or the bug hasn't been fixed yet
59
+ * Check out the issue tracker to make sure someone already hasn't requested it and/or contributed it
60
+ * Fork the project
61
+ * Start a feature/bugfix branch
62
+ * Commit and push until you are happy with your contribution
63
+ * Make sure to add tests for it. This is important so I don't break it in a future version unintentionally.
64
+ * Please try not to mess with the Rakefile, version, or history. If you want to have your own version, or is otherwise necessary, that is fine, but please isolate to its own commit so I can cherry-pick around it.
65
+
66
+ == Copyright
67
+
68
+ Copyright (c) 2011 Travis Haynes. See LICENSE.txt for
69
+ further details.
70
+
data/Rakefile ADDED
@@ -0,0 +1,31 @@
1
+ require 'rubygems'
2
+ require 'bundler'
3
+ begin
4
+ Bundler.setup(:default, :development)
5
+ rescue Bundler::BundlerError => e
6
+ $stderr.puts e.message
7
+ $stderr.puts "Run `bundle install` to install missing gems"
8
+ exit e.status_code
9
+ end
10
+ require 'rake'
11
+
12
+ require 'jeweler'
13
+ Jeweler::Tasks.new do |gem|
14
+ # gem is a Gem::Specification... see http://docs.rubygems.org/read/chapter/20 for more options
15
+ gem.name = "trackerific"
16
+ gem.homepage = "http://github.com/travishaynes/trackerific"
17
+ gem.license = "MIT"
18
+ gem.summary = %Q{Trackerific provides package tracking to Rails.}
19
+ gem.description = %Q{Trackerific provides USPS, FedEx and UPS package tracking to Rails.}
20
+ gem.email = "travis.j.haynes@gmail.com"
21
+ gem.authors = ["Travis Haynes"]
22
+ gem.add_dependency 'builder', "~> 2.1.2"
23
+ gem.add_dependency 'xml-simple', "~> 1.0.15"
24
+ gem.add_dependency 'savon', "~> 0.8.6"
25
+ gem.add_dependency 'curb', "~> 0.7.15"
26
+ # Include your dependencies below. Runtime dependencies are required when using your gem,
27
+ # and development dependencies are only needed for development (ie running rake tasks, tests, etc)
28
+ # gem.add_runtime_dependency 'jabber4r', '> 0.1'
29
+ # gem.add_development_dependency 'rspec', '> 1.2.3'
30
+ end
31
+ Jeweler::RubygemsDotOrgTasks.new
data/VERSION ADDED
@@ -0,0 +1 @@
1
+ 0.1.1
data/lib/fedex.rb ADDED
@@ -0,0 +1,65 @@
1
+ module Trackerific
2
+ require 'savon'
3
+
4
+ class FedEx < Base
5
+ include Trackerific::SoapClient
6
+
7
+ TEST_TRACKING_NUMBERS = ['183689015000001']
8
+
9
+ def required_options
10
+ [:account, :meter, :key, :password]
11
+ end
12
+
13
+ def track_package(package_id)
14
+ super
15
+ begin
16
+ tracking_response = soap_client.request :track do
17
+ soap.input = 'wsdl:TrackRequest'
18
+ soap.body = soap_body
19
+ end.to_hash
20
+ rescue Savon::HTTP::Error => e
21
+ raise Trackerific::Error, e.message
22
+ end
23
+ if tracking_response[:track_reply][:highest_severity] == 'ERROR'
24
+ raise Trackerific::Error, tracking_response[:track_reply][:notifications][:message]
25
+ end
26
+ return {
27
+ :package_id => package_id,
28
+ :summary => tracking_response[:track_reply][:transaction_detail],
29
+ :details => tracking_response[:track_reply][:track_details]
30
+ }
31
+ end
32
+
33
+ protected
34
+
35
+ def soap_body
36
+ {
37
+ :WebAuthenticationDetail => {
38
+ :UserCredential => {
39
+ :Key => @options[:key],
40
+ :Password => @options[:password],
41
+ :order! => [:Key, :Password]
42
+ }
43
+ },
44
+ :ClientDetail => {
45
+ :AccountNumber => @options[:account],
46
+ :MeterNumber => @options[:meter]
47
+ },
48
+ :Version => {
49
+ :ServiceId => 'trck',
50
+ :Major => '4',
51
+ :Intermediate => '0',
52
+ :Minor => '0'
53
+ },
54
+ :PackageIdentifier => {
55
+ :Value => @package_id,
56
+ :Type => 'TRACKING_NUMBER_OR_DOORTAG',
57
+ :order! => [:Value, :Type]
58
+ },
59
+ :IncludeDetailedScans => 'true',
60
+ :order! => [:WebAuthenticationDetail, :ClientDetail, :Version, :PackageIdentifier, :IncludeDetailedScans]
61
+ }
62
+ end
63
+
64
+ end
65
+ end
@@ -0,0 +1,13 @@
1
+ module Trackerific
2
+ require 'savon'
3
+
4
+ module SoapClient
5
+ def soap_client
6
+ return @soap_client unless @soap_client.nil?
7
+ wsdl_document = "#{::File.dirname(__FILE__)}/wsdl/#{self.class.to_s.rpartition("::")[2].downcase}/#{Rails.env}.wsdl"
8
+ @soap_client = Savon::Client.new do
9
+ wsdl.document = wsdl_document
10
+ end
11
+ end
12
+ end
13
+ end
@@ -0,0 +1,32 @@
1
+ module Trackerific
2
+ require 'rails'
3
+
4
+ class Error < StandardError
5
+ end
6
+
7
+ class Base
8
+ def initialize(options = {})
9
+ required = required_options
10
+ required.each do |k|
11
+ raise ArgumentError.new("Missing required parameter: #{k}") unless options.has_key?(k)
12
+ end
13
+ options.each do |k, v|
14
+ raise ArgumentError.new("Invalid parameter: #{k}") unless required.include?(k)
15
+ end
16
+ @options = options
17
+ end
18
+
19
+ def required_options
20
+ []
21
+ end
22
+
23
+ def track_package(package_id)
24
+ @package_id = package_id
25
+ end
26
+ end
27
+
28
+ require 'soap_client'
29
+ require 'usps'
30
+ require 'fedex'
31
+ require 'ups'
32
+ end
data/lib/ups.rb ADDED
@@ -0,0 +1,81 @@
1
+ module Trackerific
2
+ require 'httparty'
3
+
4
+ class UPS < Base
5
+ TEST_TRACKING_NUMBERS = [
6
+ '1Z12345E0291980793', '1Z12345E6692804405','1Z12345E1392654435',
7
+ '1Z12345E6892410845','1Z12345E029198079','1Z12345E1591910450'
8
+ ]
9
+
10
+ def required_options
11
+ [:key, :user_id, :password]
12
+ end
13
+
14
+ def track_package(package_id)
15
+ super
16
+ http_response = HTTP.post('/Track', :body => build_xml_request)
17
+ http_response.error! unless http_response.code == 200
18
+ case http_response['TrackResponse']['Response']['ResponseStatusCode']
19
+ when "0" then raise Trackerific::Error, parse_error_response(http_response)
20
+ when "1" then return parse_success_response(http_response)
21
+ else raise Trackerific::Error, "Invalid response code returned from server."
22
+ end
23
+ end
24
+
25
+ protected
26
+
27
+ def parse_success_response(http_response)
28
+ activity = http_response['TrackResponse']['Shipment']['Package']['Activity']
29
+ activity = [activity] if activity.is_a? Hash
30
+ details = []
31
+ activity.each do |a|
32
+ status = a['Status']['StatusType']['Description']
33
+ if status != "UPS INTERNAL ACTIVITY CODE"
34
+ address = a['ActivityLocation']['Address'].map {|k,v| v}.join(" ")
35
+ date = "#{a['Date'].to_date} #{a['Time'][0..1]}:#{a['Time'][2..3]}:#{a['Time'][4..5]}".to_datetime
36
+ details << "#{date.strftime('%b %d %I:%M %P')} #{status}"
37
+ end
38
+ end
39
+ # UPS does not provide a summary, so just use the last tracking details
40
+ summary = details.last
41
+ details.delete(summary)
42
+ return {
43
+ :package_id => @package_id,
44
+ :summary => summary,
45
+ :details => details.reverse
46
+ }
47
+ end
48
+
49
+ def parse_error_response(http_response)
50
+ http_response['TrackResponse']['Response']['Error']['ErrorDescription']
51
+ end
52
+
53
+ def build_xml_request
54
+ xml = ""
55
+ builder = ::Builder::XmlMarkup.new(:target => xml)
56
+ builder.AccessRequest do |ar|
57
+ ar.AccessLicenseNumber @options[:key]
58
+ ar.UserId @options[:user_id]
59
+ ar.Password @options[:password]
60
+ end
61
+ builder.TrackRequest do |tr|
62
+ tr.Request do |r|
63
+ r.RequestAction 'Track'
64
+ end
65
+ tr.TrackingNumber @package_id
66
+ end
67
+ return xml
68
+ end
69
+
70
+ private
71
+
72
+ class HTTP
73
+ include ::HTTParty
74
+ format :xml
75
+ base_uri case Rails.env
76
+ when 'test','development' then 'https://wwwcie.ups.com/ups.app/xml'
77
+ when 'production' then 'https://www.ups.com/ups.app/xml'
78
+ end
79
+ end
80
+ end
81
+ end