trackerific 0.3.2 → 0.3.3
Sign up to get free protection for your applications and to get access to all the features.
- data/Gemfile +1 -0
- data/Gemfile.lock +40 -38
- data/README.rdoc +4 -9
- data/VERSION +1 -1
- data/doc/Trackerific.html +34 -22
- data/doc/Trackerific/Base.html +237 -20
- data/doc/Trackerific/Details.html +6 -6
- data/doc/Trackerific/Error.html +2 -2
- data/doc/Trackerific/Event.html +7 -7
- data/doc/Trackerific/FedEx.html +190 -3
- data/doc/Trackerific/UPS.html +190 -3
- data/doc/Trackerific/USPS.html +190 -3
- data/doc/_index.html +1 -1
- data/doc/file.README.html +6 -24
- data/doc/index.html +6 -24
- data/doc/method_list.html +68 -4
- data/doc/top-level-namespace.html +1 -1
- data/lib/trackerific.rb +37 -74
- data/lib/trackerific/base.rb +70 -0
- data/lib/{trackerific_details.rb → trackerific/details.rb} +0 -0
- data/lib/trackerific/error.rb +4 -0
- data/lib/{trackerific_event.rb → trackerific/event.rb} +0 -0
- data/lib/{fedex.rb → trackerific/services/fedex.rb} +9 -2
- data/lib/{ups.rb → trackerific/services/ups.rb} +10 -4
- data/lib/{usps.rb → trackerific/services/usps.rb} +10 -3
- data/spec/lib/trackerific/base_spec.rb +34 -0
- data/spec/lib/{trackerific_details_spec.rb → trackerific/details_spec.rb} +0 -0
- data/spec/lib/trackerific/error_spec.rb +5 -0
- data/spec/lib/{trackerific_event_spec.rb → trackerific/event_spec.rb} +0 -0
- data/spec/lib/{fedex_spec.rb → trackerific/services/fedex_spec.rb} +19 -14
- data/spec/lib/{ups_spec.rb → trackerific/services/ups_spec.rb} +20 -14
- data/spec/lib/{usps_spec.rb → trackerific/services/usps_spec.rb} +18 -14
- data/spec/lib/trackerific_spec.rb +7 -30
- data/spec/spec_helper.rb +1 -0
- data/trackerific.gemspec +18 -22
- metadata +38 -27
data/lib/trackerific.rb
CHANGED
@@ -1,71 +1,15 @@
|
|
1
|
-
|
1
|
+
require 'rails'
|
2
|
+
require 'trackerific/base'
|
3
|
+
require 'trackerific/error'
|
4
|
+
require 'trackerific/details'
|
5
|
+
require 'trackerific/event'
|
6
|
+
|
7
|
+
# require all the Trackerific services
|
8
|
+
services_path = File.join(File.dirname(__FILE__), "trackerific", "services", "**", "*.rb")
|
9
|
+
Dir[File.expand_path(services_path)].each { |file| require file }
|
10
|
+
|
11
|
+
# Trackerific provides package tracking to Rails apps.
|
2
12
|
module Trackerific
|
3
|
-
require 'rails'
|
4
|
-
require 'trackerific_details'
|
5
|
-
require 'trackerific_event'
|
6
|
-
|
7
|
-
# Raised if something other than tracking information is returned.
|
8
|
-
class Error < StandardError; end
|
9
|
-
|
10
|
-
# Base class for Trackerific package tracking services.
|
11
|
-
class Base
|
12
|
-
# Creates a new instance of Trackerific::Base with required options
|
13
|
-
# @api private
|
14
|
-
def initialize(options = {})
|
15
|
-
required = required_options
|
16
|
-
# make sure all the required options exist
|
17
|
-
required.each do |k|
|
18
|
-
raise ArgumentError.new("Missing required parameter: #{k}") unless options.has_key?(k)
|
19
|
-
end
|
20
|
-
# make sure no invalid options exist
|
21
|
-
options.each do |k, v|
|
22
|
-
raise ArgumentError.new("Invalid parameter: #{k}") unless required.include?(k)
|
23
|
-
end
|
24
|
-
@options = options
|
25
|
-
end
|
26
|
-
|
27
|
-
# Gets the tracking information for the package from the server
|
28
|
-
# @param [String] package_id the package identifier
|
29
|
-
# @return [Trackerific::Details] the tracking details
|
30
|
-
# @example Override this method in your custom tracking provider to implement tracking
|
31
|
-
# module Trackerific
|
32
|
-
# class MyTrackingProvider < Base
|
33
|
-
# def track_package
|
34
|
-
# Trackerific::Details.new(
|
35
|
-
# "summary of tracking events",
|
36
|
-
# [Trackerific::Event.new(Time.now, "summary", "location")]
|
37
|
-
# )
|
38
|
-
# end
|
39
|
-
# end
|
40
|
-
# end
|
41
|
-
# @api public
|
42
|
-
def track_package(package_id)
|
43
|
-
@package_id = package_id
|
44
|
-
end
|
45
|
-
|
46
|
-
protected
|
47
|
-
|
48
|
-
# An array of options that are required to create a new instance of this class
|
49
|
-
# @return [Array] the required options
|
50
|
-
# @example Override this method in your custom tracking provider to enforce some options
|
51
|
-
# module Trackerific
|
52
|
-
# class MyTrackingProvider < Base
|
53
|
-
# def required_options
|
54
|
-
# [:all, :these, :are, :required]
|
55
|
-
# end
|
56
|
-
# end
|
57
|
-
# end
|
58
|
-
# @api private
|
59
|
-
def required_options
|
60
|
-
[]
|
61
|
-
end
|
62
|
-
|
63
|
-
end
|
64
|
-
|
65
|
-
require 'usps'
|
66
|
-
require 'fedex'
|
67
|
-
require 'ups'
|
68
|
-
|
69
13
|
# Checks a string for a valid package tracking service
|
70
14
|
# @param [String] package_id the package identifier
|
71
15
|
# @return [Trackerific::Base] the Trackerific class that can track the given
|
@@ -75,14 +19,33 @@ module Trackerific
|
|
75
19
|
# tracking_service "183689015000001" # => Trackerific::FedEx
|
76
20
|
# @api public
|
77
21
|
def tracking_service(package_id)
|
78
|
-
|
79
|
-
|
80
|
-
|
81
|
-
|
82
|
-
|
83
|
-
|
84
|
-
|
22
|
+
# loop through each constant in Trackerific
|
23
|
+
Trackerific.constants.each do |const|
|
24
|
+
# get the constant's class
|
25
|
+
cls = Trackerific.const_get(const)
|
26
|
+
# check if it descends from Trackerific::Base
|
27
|
+
if cls.superclass == Trackerific::Base
|
28
|
+
# loop through each package id matcher
|
29
|
+
cls.package_id_matchers.each do |matcher|
|
30
|
+
# return the class if it matches
|
31
|
+
return cls if package_id =~ matcher
|
32
|
+
end
|
85
33
|
end
|
86
34
|
end
|
35
|
+
# if we've made it this far, nothing matched
|
36
|
+
nil
|
87
37
|
end
|
38
|
+
|
39
|
+
# TODO: Tracks a package by determining its service from the package id
|
40
|
+
# @param [String] package_id the package identifier
|
41
|
+
# @return [Trackerific::Details] the tracking results
|
42
|
+
# @raise [Trackerific::Error] raised when the server returns an error (invalid credentials, tracking package, etc.)
|
43
|
+
# @example Track a package
|
44
|
+
# include Trackerific
|
45
|
+
# details = track_package("183689015000001")
|
46
|
+
# @api public
|
47
|
+
# def track_package(package_id)
|
48
|
+
# service = tracking_service(package_id)
|
49
|
+
#
|
50
|
+
# end
|
88
51
|
end
|
@@ -0,0 +1,70 @@
|
|
1
|
+
module Trackerific
|
2
|
+
# Base class for Trackerific package tracking services.
|
3
|
+
class Base
|
4
|
+
# Creates a new instance of Trackerific::Base with required options
|
5
|
+
# @api private
|
6
|
+
def initialize(options = {})
|
7
|
+
required = self.class.required_options
|
8
|
+
# make sure all the required options exist
|
9
|
+
required.each do |k|
|
10
|
+
raise ArgumentError.new("Missing required parameter: #{k}") unless options.has_key?(k)
|
11
|
+
end
|
12
|
+
# make sure no invalid options exist
|
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
|
+
# Gets the tracking information for the package from the server
|
20
|
+
# @param [String] package_id the package identifier
|
21
|
+
# @return [Trackerific::Details] the tracking details
|
22
|
+
# @example Override this method in your custom tracking service to implement tracking
|
23
|
+
# module Trackerific
|
24
|
+
# class MyTrackingService < Base
|
25
|
+
# def track_package
|
26
|
+
# # your tracking code here
|
27
|
+
# Trackerific::Details.new(
|
28
|
+
# "summary of tracking events",
|
29
|
+
# [Trackerific::Event.new(Time.now, "summary", "location")]
|
30
|
+
# )
|
31
|
+
# end
|
32
|
+
# end
|
33
|
+
# end
|
34
|
+
# @api semipublic
|
35
|
+
def track_package(package_id)
|
36
|
+
@package_id = package_id
|
37
|
+
end
|
38
|
+
|
39
|
+
# An Array of Regexp that matches valid package identifiers for your service
|
40
|
+
# @example Override this method in your custom tracking service
|
41
|
+
# module Trackerific
|
42
|
+
# class MyTrackingService < Base
|
43
|
+
# def self.package_id_matchers
|
44
|
+
# [ /^.Z/, /^[HK].{10}$/ ] # matchers for UPS package identifiers
|
45
|
+
# end
|
46
|
+
# end
|
47
|
+
# end
|
48
|
+
# @return [Array, Regexp] an array of regular expressions
|
49
|
+
# @api semipublic
|
50
|
+
def self.package_id_matchers
|
51
|
+
nil
|
52
|
+
end
|
53
|
+
|
54
|
+
# An array of options that are required to create a new instance of this class
|
55
|
+
# @return [Array] the required options
|
56
|
+
# @example Override this method in your custom tracking service to enforce some options
|
57
|
+
# module Trackerific
|
58
|
+
# class MyTrackingService < Base
|
59
|
+
# def self.required_options
|
60
|
+
# [:all, :these, :are, :required]
|
61
|
+
# end
|
62
|
+
# end
|
63
|
+
# end
|
64
|
+
# @api semipublic
|
65
|
+
def self.required_options
|
66
|
+
[]
|
67
|
+
end
|
68
|
+
|
69
|
+
end
|
70
|
+
end
|
File without changes
|
File without changes
|
@@ -45,16 +45,23 @@ module Trackerific
|
|
45
45
|
)
|
46
46
|
end
|
47
47
|
|
48
|
-
|
48
|
+
# A regex that matches valid package identifiers for FedEx package ids
|
49
|
+
# @return [Regexp] the regular expression
|
50
|
+
# @api private
|
51
|
+
def self.package_id_matchers
|
52
|
+
[ /^[0-9]{15}$/ ]
|
53
|
+
end
|
49
54
|
|
50
55
|
# Returns an Array of required options used when creating a new instance
|
51
56
|
# @return [Array] required options for tracking a FedEx package are :account
|
52
57
|
# and :meter
|
53
58
|
# @api private
|
54
|
-
def required_options
|
59
|
+
def self.required_options
|
55
60
|
[:account, :meter]
|
56
61
|
end
|
57
62
|
|
63
|
+
protected
|
64
|
+
|
58
65
|
# Builds the XML request to send to FedEx
|
59
66
|
# @return [String] a FDXTrack2Request XML
|
60
67
|
# @api private
|
@@ -37,15 +37,21 @@ module Trackerific
|
|
37
37
|
end
|
38
38
|
end
|
39
39
|
|
40
|
-
|
41
|
-
|
40
|
+
# A regex that matches valid package identifiers for UPS package ids
|
41
|
+
# @return [Regexp] the regular expression
|
42
|
+
# @api private
|
43
|
+
def self.package_id_matchers
|
44
|
+
[ /^.Z/, /^[HK].{10}$/ ]
|
45
|
+
end
|
42
46
|
# The required options for tracking a UPS package
|
43
|
-
# @return [Array] the required options for tracking a UPS package
|
47
|
+
# @return [Array] the required options for tracking a UPS package
|
44
48
|
# @api private
|
45
|
-
def required_options
|
49
|
+
def self.required_options
|
46
50
|
[:key, :user_id, :password]
|
47
51
|
end
|
48
52
|
|
53
|
+
protected
|
54
|
+
|
49
55
|
# Parses the response from UPS
|
50
56
|
# @return [Trackerific::Details]
|
51
57
|
# @api private
|
@@ -56,15 +56,22 @@ module Trackerific
|
|
56
56
|
)
|
57
57
|
end
|
58
58
|
|
59
|
-
|
59
|
+
# A regex that matches valid package identifiers for USPS package ids
|
60
|
+
# @return [Regexp] the regular expression
|
61
|
+
# @api private
|
62
|
+
def self.package_id_matchers
|
63
|
+
[ /^E\D{1}\d{9}\D{2}$|^9\d{15,21}$/ ]
|
64
|
+
end
|
60
65
|
|
61
66
|
# The required options for tracking a UPS package
|
62
|
-
# @return [Array] the required options for tracking a UPS package
|
67
|
+
# @return [Array] the required options for tracking a UPS package
|
63
68
|
# @api private
|
64
|
-
def required_options
|
69
|
+
def self.required_options
|
65
70
|
[:user_id]
|
66
71
|
end
|
67
72
|
|
73
|
+
protected
|
74
|
+
|
68
75
|
# Builds an XML request to send to USPS
|
69
76
|
# @return [String] the xml request
|
70
77
|
# @api private
|
@@ -0,0 +1,34 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
class TestBaseClass < Trackerific::Base
|
4
|
+
def self.required_options
|
5
|
+
[:required, :also_required]
|
6
|
+
end
|
7
|
+
end
|
8
|
+
|
9
|
+
describe 'Trackerific::Base' do
|
10
|
+
|
11
|
+
before(:all) do
|
12
|
+
@base = Trackerific::Base.new
|
13
|
+
end
|
14
|
+
|
15
|
+
context "with a new Trackerific::Base class that has required options" do
|
16
|
+
context "has all the required options" do
|
17
|
+
it "should be able to create a new instance" do
|
18
|
+
t = TestBaseClass.new(:required => true, :also_required => :yup)
|
19
|
+
t.should be_a TestBaseClass
|
20
|
+
end
|
21
|
+
end
|
22
|
+
context "is missing some required options" do
|
23
|
+
it "should raise an ArgumentError" do
|
24
|
+
lambda { TestBaseClass.new() }.should raise_error(ArgumentError)
|
25
|
+
end
|
26
|
+
end
|
27
|
+
context "has an invalid option" do
|
28
|
+
it "should raise an ArgumentError" do
|
29
|
+
lambda { TestBaseClass.new(:unknown => :argument ) }.should raise_error(ArgumentError)
|
30
|
+
end
|
31
|
+
end
|
32
|
+
end
|
33
|
+
|
34
|
+
end
|
File without changes
|
File without changes
|
@@ -6,19 +6,27 @@ FEDEX_TRACK_URL = "https://gateway.fedex.com/GatewayDC"
|
|
6
6
|
describe "Trackerific::FedEx" do
|
7
7
|
include Fixtures
|
8
8
|
|
9
|
-
|
10
|
-
|
11
|
-
|
9
|
+
describe :required_options do
|
10
|
+
subject { Trackerific::FedEx.required_options }
|
11
|
+
it { should include(:account) }
|
12
|
+
it { should include(:meter) }
|
12
13
|
end
|
13
14
|
|
14
|
-
describe
|
15
|
+
describe :package_id_matchers do
|
16
|
+
it "should be an Array of Regexp" do
|
17
|
+
Trackerific::FedEx.package_id_matchers.should each { |m| m.should be_a Regexp }
|
18
|
+
end
|
19
|
+
end
|
20
|
+
|
21
|
+
describe :track_package do
|
22
|
+
before(:all) do
|
23
|
+
@package_id = "183689015000001"
|
24
|
+
@fedex = Trackerific::FedEx.new :account => "123456789", :meter => "123456789"
|
25
|
+
end
|
26
|
+
|
15
27
|
context "with a successful response from the server" do
|
16
28
|
before(:each) do
|
17
|
-
FakeWeb.register_uri(
|
18
|
-
:post,
|
19
|
-
FEDEX_TRACK_URL,
|
20
|
-
:body => load_fixture(:fedex_success_response)
|
21
|
-
)
|
29
|
+
FakeWeb.register_uri(:post, FEDEX_TRACK_URL, :body => load_fixture(:fedex_success_response))
|
22
30
|
@tracking = @fedex.track_package(@package_id)
|
23
31
|
end
|
24
32
|
specify { @tracking.should be_a Trackerific::Details }
|
@@ -29,13 +37,10 @@ describe "Trackerific::FedEx" do
|
|
29
37
|
@tracking.summary.should_not be_empty
|
30
38
|
end
|
31
39
|
end
|
40
|
+
|
32
41
|
context "with an error response from the server" do
|
33
42
|
before(:all) do
|
34
|
-
FakeWeb.register_uri(
|
35
|
-
:post,
|
36
|
-
FEDEX_TRACK_URL,
|
37
|
-
:body => load_fixture(:fedex_error_response)
|
38
|
-
)
|
43
|
+
FakeWeb.register_uri(:post, FEDEX_TRACK_URL, :body => load_fixture(:fedex_error_response))
|
39
44
|
end
|
40
45
|
specify { lambda { @fedex.track_package("invalid package id") }.should raise_error(Trackerific::Error) }
|
41
46
|
end
|
@@ -6,19 +6,28 @@ UPS_TRACK_URL = 'https://wwwcie.ups.com/ups.app/xml/Track'
|
|
6
6
|
describe "Trackerific::UPS" do
|
7
7
|
include Fixtures
|
8
8
|
|
9
|
-
|
10
|
-
|
11
|
-
|
9
|
+
describe :required_options do
|
10
|
+
subject { Trackerific::UPS.required_options }
|
11
|
+
it { should include(:key) }
|
12
|
+
it { should include(:user_id) }
|
13
|
+
it { should include(:password) }
|
12
14
|
end
|
13
15
|
|
14
|
-
describe
|
16
|
+
describe :package_id_matchers do
|
17
|
+
it "should be an Array of Regexp" do
|
18
|
+
Trackerific::UPS.package_id_matchers.should each { |m| m.should be_a Regexp }
|
19
|
+
end
|
20
|
+
end
|
21
|
+
|
22
|
+
describe :track_package do
|
23
|
+
before(:all) do
|
24
|
+
@package_id = '1Z12345E0291980793'
|
25
|
+
@ups = Trackerific::UPS.new :key => 'testkey', :user_id => 'testuser', :password => 'secret'
|
26
|
+
end
|
27
|
+
|
15
28
|
context "with a successful response from the server" do
|
16
29
|
before(:all) do
|
17
|
-
FakeWeb.register_uri(
|
18
|
-
:post,
|
19
|
-
UPS_TRACK_URL,
|
20
|
-
:body => load_fixture(:ups_success_response)
|
21
|
-
)
|
30
|
+
FakeWeb.register_uri(:post, UPS_TRACK_URL, :body => load_fixture(:ups_success_response))
|
22
31
|
@tracking = @ups.track_package(@package_id)
|
23
32
|
end
|
24
33
|
specify { @tracking.should be_a Trackerific::Details }
|
@@ -29,13 +38,10 @@ describe "Trackerific::UPS" do
|
|
29
38
|
@tracking.summary.should_not be_empty
|
30
39
|
end
|
31
40
|
end
|
41
|
+
|
32
42
|
context "with an error response from the server" do
|
33
43
|
before(:all) do
|
34
|
-
FakeWeb.register_uri(
|
35
|
-
:post,
|
36
|
-
UPS_TRACK_URL,
|
37
|
-
:body => load_fixture(:ups_error_response)
|
38
|
-
)
|
44
|
+
FakeWeb.register_uri(:post, UPS_TRACK_URL, :body => load_fixture(:ups_error_response))
|
39
45
|
end
|
40
46
|
specify { lambda { @ups.track_package("invalid package id") }.should raise_error(Trackerific::Error) }
|
41
47
|
end
|