tracking_number 0.10.5 → 1.0.0.pre1
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +5 -5
- data/.gitmodules +3 -0
- data/.travis.yml +15 -7
- data/Gemfile +2 -0
- data/README.md +87 -15
- data/Rakefile +3 -0
- data/lib/checksum_validations.rb +63 -0
- data/lib/tasks/stats.rake +90 -0
- data/lib/tracking_number.rb +79 -8
- data/lib/tracking_number/base.rb +169 -15
- data/lib/tracking_number/info.rb +18 -0
- data/lib/tracking_number/unknown.rb +33 -0
- data/lib/tracking_number/version.rb +1 -1
- data/test/test_helper.rb +10 -2
- data/test/tracking_number_data_test.rb +161 -0
- data/test/tracking_number_test.rb +105 -0
- data/tracking_number.gemspec +3 -2
- metadata +52 -23
- data/VERSION +0 -1
- data/lib/tracking_number/dhl.rb +0 -34
- data/lib/tracking_number/fedex.rb +0 -156
- data/lib/tracking_number/ontrac.rb +0 -34
- data/lib/tracking_number/ups.rb +0 -108
- data/lib/tracking_number/usps.rb +0 -166
- data/test/dhl_tracking_number_test.rb +0 -35
- data/test/fedex_tracking_number_test.rb +0 -75
- data/test/ontrac_tracking_number_test.rb +0 -24
- data/test/ups_tracking_number_test.rb +0 -36
- data/test/usps_tracking_number_test.rb +0 -73
@@ -1,34 +0,0 @@
|
|
1
|
-
module TrackingNumber
|
2
|
-
class OnTrac < Base
|
3
|
-
SEARCH_PATTERN = /(\b(C\s*)([0-9]\s*){14,14}\b)/
|
4
|
-
VERIFY_PATTERN = /^(C[0-9]{13,13})([0-9])$/
|
5
|
-
def carrier
|
6
|
-
:ontrac
|
7
|
-
end
|
8
|
-
|
9
|
-
def matches
|
10
|
-
self.tracking_number.scan(VERIFY_PATTERN).flatten
|
11
|
-
end
|
12
|
-
|
13
|
-
def valid_checksum?
|
14
|
-
# checksum calculation is the same as UPS
|
15
|
-
sequence, check_digit = matches
|
16
|
-
|
17
|
-
total = 0
|
18
|
-
sequence.chars.each_with_index do |c, i|
|
19
|
-
x = if c[/[0-9]/] # numeric
|
20
|
-
c.to_i
|
21
|
-
else
|
22
|
-
(c[0].ord - 3) % 10
|
23
|
-
end
|
24
|
-
x *= 2 if i.odd?
|
25
|
-
total += x
|
26
|
-
end
|
27
|
-
|
28
|
-
check = (total % 10)
|
29
|
-
check = (10 - check) unless (check.zero?)
|
30
|
-
|
31
|
-
return (check.to_i == check_digit.to_i)
|
32
|
-
end
|
33
|
-
end
|
34
|
-
end
|
data/lib/tracking_number/ups.rb
DELETED
@@ -1,108 +0,0 @@
|
|
1
|
-
module TrackingNumber
|
2
|
-
class UPS < Base
|
3
|
-
SEARCH_PATTERN = /(\b1\s*Z\s*(\w\s*){16,16}\b)/
|
4
|
-
VERIFY_PATTERN = /^1Z(\w{15,15})(\w)$/
|
5
|
-
|
6
|
-
def carrier
|
7
|
-
:ups
|
8
|
-
end
|
9
|
-
|
10
|
-
def matches
|
11
|
-
self.tracking_number.scan(VERIFY_PATTERN).flatten
|
12
|
-
end
|
13
|
-
|
14
|
-
def valid_checksum?
|
15
|
-
sequence = tracking_number.slice(2...17)
|
16
|
-
check_digit = tracking_number.slice(17, 18)
|
17
|
-
|
18
|
-
total = 0
|
19
|
-
sequence.chars.each_with_index do |c, i|
|
20
|
-
x = if c[/[0-9]/] # numeric
|
21
|
-
c.to_i
|
22
|
-
else
|
23
|
-
(c[0].ord - 3) % 10
|
24
|
-
end
|
25
|
-
x *= 2 if i.odd?
|
26
|
-
total += x
|
27
|
-
end
|
28
|
-
|
29
|
-
check = (total % 10)
|
30
|
-
check = (10 - check) unless (check.zero?)
|
31
|
-
|
32
|
-
return (check.to_i == check_digit.to_i)
|
33
|
-
end
|
34
|
-
|
35
|
-
def decode
|
36
|
-
{:shipper_account => self.tracking_number.to_s.slice(2...8),
|
37
|
-
:service_type => self.tracking_number.to_s.slice(8...10),
|
38
|
-
:package_identifier => self.tracking_number.to_s.slice(10...17),
|
39
|
-
:check_digit => self.tracking_number.to_s.slice(17, 18)
|
40
|
-
}
|
41
|
-
end
|
42
|
-
|
43
|
-
def service_type
|
44
|
-
case decode[:service_type]
|
45
|
-
when "01"
|
46
|
-
"UPS United States Next Day Air (Red)"
|
47
|
-
when "02"
|
48
|
-
"UPS United States Second Day Air (Blue)"
|
49
|
-
when "03"
|
50
|
-
"UPS United States Ground"
|
51
|
-
when "12"
|
52
|
-
"UPS United States Third Day Select"
|
53
|
-
when "13"
|
54
|
-
"UPS United States Next Day Air Saver (Red Saver)"
|
55
|
-
when "15"
|
56
|
-
"UPS United States Next Day Air Early A.M."
|
57
|
-
when "22"
|
58
|
-
"UPS United States Ground - Returns Plus - Three Pickup Attempts"
|
59
|
-
when "32"
|
60
|
-
"UPS United States Next Day Air Early A.M. - COD"
|
61
|
-
when "33"
|
62
|
-
"UPS United States Next Day Air Early A.M. - Saturday Delivery, COD"
|
63
|
-
when "41"
|
64
|
-
"UPS United States Next Day Air Early A.M. - Saturday Delivery"
|
65
|
-
when "42"
|
66
|
-
"UPS United States Ground - Signature Required"
|
67
|
-
when "44"
|
68
|
-
"UPS United States Next Day Air - Saturday Delivery"
|
69
|
-
when "66"
|
70
|
-
"UPS United States Worldwide Express"
|
71
|
-
when "72"
|
72
|
-
"UPS United States Ground - Collect on Delivery"
|
73
|
-
when "78"
|
74
|
-
"UPS United States Ground - Returns Plus - One Pickup Attempt"
|
75
|
-
when "90"
|
76
|
-
"UPS United States Ground - Returns - UPS Prints and Mails Label"
|
77
|
-
when "A0"
|
78
|
-
"UPS United States Next Day Air Early A.M. - Adult Signature Required"
|
79
|
-
when "A1"
|
80
|
-
"UPS United States Next Day Air Early A.M. - Saturday Delivery, Adult Signature Required"
|
81
|
-
when "A2"
|
82
|
-
"UPS United States Next Day Air - Adult Signature Required"
|
83
|
-
when "A8"
|
84
|
-
"UPS United States Ground - Adult Signature Required"
|
85
|
-
when "A9"
|
86
|
-
"UPS United States Next Day Air Early A.M. - Adult Signature Required, COD"
|
87
|
-
when "AA"
|
88
|
-
"UPS United States Next Day Air Early A.M. - Saturday Delivery, Adult Signature Required, COD"
|
89
|
-
end
|
90
|
-
end
|
91
|
-
end
|
92
|
-
|
93
|
-
class UPSTest < UPS
|
94
|
-
# Easypost UPS test numbers as described here:
|
95
|
-
# https://www.easypost.com/docs/api#tracking (scroll down a bit).
|
96
|
-
SEARCH_PATTERN = /^EZ(\d)00000000\1$/
|
97
|
-
VERIFY_PATTERN = SEARCH_PATTERN
|
98
|
-
|
99
|
-
def matches
|
100
|
-
self.tracking_number.scan(VERIFY_PATTERN).flatten
|
101
|
-
end
|
102
|
-
|
103
|
-
def valid_checksum?
|
104
|
-
sequence = tracking_number.scan(/[a-zA-Z0-9]+/).flatten.join
|
105
|
-
return sequence =~ /EZ(\d)00000000\1/
|
106
|
-
end
|
107
|
-
end
|
108
|
-
end
|
data/lib/tracking_number/usps.rb
DELETED
@@ -1,166 +0,0 @@
|
|
1
|
-
module TrackingNumber
|
2
|
-
class USPS < Base
|
3
|
-
def carrier
|
4
|
-
:usps
|
5
|
-
end
|
6
|
-
end
|
7
|
-
|
8
|
-
class USPS91 < USPS
|
9
|
-
SEARCH_PATTERN = [/(\b(?:420\s*\d{5})?9\s*[1-5]\s*(?:(?:(?:[0-9]\s*){20}\b)|(?:(?:[0-9]\s*){24}\b)))/, /(\b([0-9]\s*){20}\b)/]
|
10
|
-
VERIFY_PATTERN = /^(?:420\d{5})?(9[1-5](?:[0-9]{19}|[0-9]{23}))([0-9])$/
|
11
|
-
|
12
|
-
# Sometimes these numbers will appear without the leading 91, 93, or 94, though, so we need to account for that case
|
13
|
-
|
14
|
-
def decode
|
15
|
-
# Application ID: 91, 93, 94 or 95
|
16
|
-
# Service Code: 2 Digits
|
17
|
-
# Mailer Id: 8 Digits
|
18
|
-
# Package Id: 9 Digits
|
19
|
-
# Checksum: 1 Digit
|
20
|
-
|
21
|
-
base_tracking_number = self.tracking_number.to_s.gsub(/^420\d{5}/, '')
|
22
|
-
|
23
|
-
{:application_id => base_tracking_number.to_s.slice(0...2),
|
24
|
-
:service_code => base_tracking_number.to_s.slice(2...4),
|
25
|
-
:mailer_id => base_tracking_number.to_s.slice(4...12),
|
26
|
-
:package_identifier => base_tracking_number.to_s.slice(12...21),
|
27
|
-
:check_digit => base_tracking_number.slice(21...22)
|
28
|
-
}
|
29
|
-
end
|
30
|
-
|
31
|
-
def matches
|
32
|
-
if self.tracking_number =~ /^(420\d{5})?9[1-5]/
|
33
|
-
self.tracking_number.scan(VERIFY_PATTERN).flatten
|
34
|
-
else
|
35
|
-
"91#{self.tracking_number}".scan(VERIFY_PATTERN).flatten
|
36
|
-
end
|
37
|
-
end
|
38
|
-
|
39
|
-
def valid_checksum?
|
40
|
-
if self.tracking_number =~ /^(420\d{5})?9[1-5]/
|
41
|
-
return true if weighted_usps_checksum_valid?(tracking_number)
|
42
|
-
else
|
43
|
-
if weighted_usps_checksum_valid?("91#{self.tracking_number}")
|
44
|
-
# set the tracking number to the 91 format if it passes this test
|
45
|
-
self.tracking_number = "91#{self.tracking_number}"
|
46
|
-
return true
|
47
|
-
end
|
48
|
-
end
|
49
|
-
end
|
50
|
-
|
51
|
-
private
|
52
|
-
|
53
|
-
def weighted_usps_checksum_valid?(sequence)
|
54
|
-
chars = sequence.gsub(/^420\d{5}/, '').chars.to_a
|
55
|
-
check_digit = chars.pop
|
56
|
-
|
57
|
-
total = 0
|
58
|
-
chars.reverse.each_with_index do |c, i|
|
59
|
-
x = c.to_i
|
60
|
-
x *= 3 if i.even?
|
61
|
-
|
62
|
-
total += x
|
63
|
-
end
|
64
|
-
|
65
|
-
check = total % 10
|
66
|
-
check = 10 - check unless (check.zero?)
|
67
|
-
return true if check == check_digit.to_i
|
68
|
-
end
|
69
|
-
end
|
70
|
-
|
71
|
-
class USPS20 < USPS
|
72
|
-
# http://www.usps.com/cpim/ftp/pubs/pub109.pdf (Publication 109. Extra Services Technical Guide, pg. 19)
|
73
|
-
# http://www.usps.com/cpim/ftp/pubs/pub91.pdf (Publication 91. Confirmation Services Technical Guide pg. 38)
|
74
|
-
|
75
|
-
SEARCH_PATTERN = /(\b([0-9]\s*){20,20}\b)/
|
76
|
-
VERIFY_PATTERN = /^([0-9]{2,2})([0-9]{9,9})([0-9]{8,8})([0-9])$/
|
77
|
-
|
78
|
-
def matches
|
79
|
-
self.tracking_number.scan(VERIFY_PATTERN).flatten
|
80
|
-
end
|
81
|
-
|
82
|
-
def decode
|
83
|
-
{:service_code => self.tracking_number.to_s.slice(0...2),
|
84
|
-
:mailer_id => self.tracking_number.to_s.slice(2...11),
|
85
|
-
:package_identifier => self.tracking_number.to_s.slice(11...19),
|
86
|
-
:check_digit => self.tracking_number.slice(19...20)
|
87
|
-
}
|
88
|
-
end
|
89
|
-
|
90
|
-
def service_type
|
91
|
-
case decode[:service_code]
|
92
|
-
when "71"
|
93
|
-
"Certified Mail"
|
94
|
-
when "73"
|
95
|
-
"Insured Mail"
|
96
|
-
when "77"
|
97
|
-
"Registered Mail"
|
98
|
-
when "81"
|
99
|
-
"Return Receipt for Merchandise"
|
100
|
-
end
|
101
|
-
end
|
102
|
-
|
103
|
-
def valid_checksum?
|
104
|
-
chars = tracking_number.chars.to_a
|
105
|
-
check_digit = chars.pop
|
106
|
-
|
107
|
-
total = 0
|
108
|
-
chars.reverse.each_with_index do |c, i|
|
109
|
-
x = c.to_i
|
110
|
-
x *= 3 if i.even?
|
111
|
-
total += x
|
112
|
-
end
|
113
|
-
|
114
|
-
check = total % 10
|
115
|
-
check = 10 - check unless (check.zero?)
|
116
|
-
return true if check == check_digit.to_i
|
117
|
-
end
|
118
|
-
end
|
119
|
-
|
120
|
-
class USPS13 < USPS
|
121
|
-
SEARCH_PATTERN = /(\b([A-Z]\s*){2,2}([0-9]\s*){9,9}([A-Z]\s*){2,2}\b)/
|
122
|
-
VERIFY_PATTERN = /^([A-Z]{2,2})([0-9]{9,9})([A-Z]{2,2})$/
|
123
|
-
|
124
|
-
def matches
|
125
|
-
self.tracking_number.scan(VERIFY_PATTERN).flatten
|
126
|
-
end
|
127
|
-
|
128
|
-
def valid_checksum?
|
129
|
-
sequence = tracking_number.scan(/[0-9]+/).flatten.join
|
130
|
-
chars = sequence.chars.to_a
|
131
|
-
check_digit = chars.pop.to_i
|
132
|
-
|
133
|
-
sum = 0
|
134
|
-
chars.zip([8,6,4,2,3,5,9,7]).each do |pair|
|
135
|
-
sum += (pair[0].to_i * pair[1].to_i)
|
136
|
-
end
|
137
|
-
|
138
|
-
remainder = sum % 11
|
139
|
-
check = case remainder
|
140
|
-
when 1
|
141
|
-
0
|
142
|
-
when 0
|
143
|
-
5
|
144
|
-
else
|
145
|
-
11 - remainder
|
146
|
-
end
|
147
|
-
|
148
|
-
return check == check_digit
|
149
|
-
end
|
150
|
-
end
|
151
|
-
|
152
|
-
class USPSTest < USPS
|
153
|
-
# USPS Test Number From Easypost. IE: 9499 9071 2345 6123 4567 81
|
154
|
-
SEARCH_PATTERN = /(\b([0-9]\s*){22,22}\b)/
|
155
|
-
VERIFY_PATTERN = SEARCH_PATTERN
|
156
|
-
|
157
|
-
def matches
|
158
|
-
self.tracking_number.scan(VERIFY_PATTERN).flatten
|
159
|
-
end
|
160
|
-
|
161
|
-
def valid_checksum?
|
162
|
-
sequence = tracking_number.scan(/[0-9]+/).flatten.join
|
163
|
-
return sequence == "9499907123456123456781"
|
164
|
-
end
|
165
|
-
end
|
166
|
-
end
|
@@ -1,35 +0,0 @@
|
|
1
|
-
require 'test_helper'
|
2
|
-
|
3
|
-
class DHLTrackingNumberTest < Minitest::Test
|
4
|
-
context "DHLExpressAir tracking number" do
|
5
|
-
["73891051146"].each do |valid_number|
|
6
|
-
should "return dhl for #{valid_number}" do
|
7
|
-
should_be_valid_number(valid_number, TrackingNumber::DHLExpressAir, :dhl)
|
8
|
-
end
|
9
|
-
|
10
|
-
should "fail on check digit changes on #{valid_number}" do
|
11
|
-
should_fail_on_check_digit_changes(valid_number)
|
12
|
-
end
|
13
|
-
|
14
|
-
should "detect #{valid_number} regardless of spacing" do
|
15
|
-
should_detect_number_variants(valid_number, TrackingNumber::DHLExpressAir)
|
16
|
-
end
|
17
|
-
end
|
18
|
-
end
|
19
|
-
|
20
|
-
context "DHLExpress tracking numbers" do
|
21
|
-
["3318810025", "8487135506", "3318810036", "3318810014"].each do |valid_number|
|
22
|
-
should "return dhl for #{valid_number}" do
|
23
|
-
should_be_valid_number(valid_number, TrackingNumber::DHLExpress, :dhl)
|
24
|
-
end
|
25
|
-
|
26
|
-
should "fail on check digit changes on #{valid_number}" do
|
27
|
-
should_fail_on_check_digit_changes(valid_number)
|
28
|
-
end
|
29
|
-
|
30
|
-
should "detect #{valid_number} regardless of spacing" do
|
31
|
-
should_detect_number_variants(valid_number, TrackingNumber::DHLExpress)
|
32
|
-
end
|
33
|
-
end
|
34
|
-
end
|
35
|
-
end
|
@@ -1,75 +0,0 @@
|
|
1
|
-
require 'test_helper'
|
2
|
-
|
3
|
-
class FedExTrackingNumberTest < Minitest::Test
|
4
|
-
context "a FedEx tracking number" do
|
5
|
-
["986578788855", "477179081230", "799531274483", "790535312317", "974367662710"].each do |valid_number|
|
6
|
-
should "return fedex express for #{valid_number}" do
|
7
|
-
should_be_valid_number(valid_number, TrackingNumber::FedExExpress, :fedex)
|
8
|
-
end
|
9
|
-
|
10
|
-
should "fail on check digit changes on #{valid_number}" do
|
11
|
-
should_fail_on_check_digit_changes(valid_number)
|
12
|
-
end
|
13
|
-
|
14
|
-
should "detect #{valid_number} regardless of spacing" do
|
15
|
-
should_detect_number_variants(valid_number, TrackingNumber::FedExExpress)
|
16
|
-
end
|
17
|
-
end
|
18
|
-
|
19
|
-
["9611020987654312345672"].each do |valid_number|
|
20
|
-
should "return fedex 96 for #{valid_number}" do
|
21
|
-
should_be_valid_number(valid_number, TrackingNumber::FedExGround96, :fedex)
|
22
|
-
end
|
23
|
-
|
24
|
-
should "fail on check digit changes on #{valid_number}" do
|
25
|
-
should_fail_on_check_digit_changes(valid_number)
|
26
|
-
end
|
27
|
-
|
28
|
-
should "detect #{valid_number} regardless of spacing" do
|
29
|
-
should_detect_number_variants(valid_number, TrackingNumber::FedExGround96)
|
30
|
-
end
|
31
|
-
end
|
32
|
-
|
33
|
-
["0414 4176 0228 964", "5682 8361 0012 000", "5682 8361 0012 734"].each do |valid_number|
|
34
|
-
should "return fedex ground for #{valid_number}" do
|
35
|
-
should_be_valid_number(valid_number, TrackingNumber::FedExGround, :fedex)
|
36
|
-
end
|
37
|
-
|
38
|
-
should "fail on check digit changes on #{valid_number}" do
|
39
|
-
should_fail_on_check_digit_changes(valid_number)
|
40
|
-
end
|
41
|
-
|
42
|
-
should "detect #{valid_number} regardless of spacing" do
|
43
|
-
should_detect_number_variants(valid_number, TrackingNumber::FedExGround)
|
44
|
-
end
|
45
|
-
end
|
46
|
-
|
47
|
-
["00 0123 4500 0000 0027"].each do |valid_number|
|
48
|
-
should "return fedex sscc18 for #{valid_number}" do
|
49
|
-
should_be_valid_number(valid_number, TrackingNumber::FedExGround18, :fedex)
|
50
|
-
end
|
51
|
-
|
52
|
-
should "fail on check digit changes on #{valid_number}" do
|
53
|
-
should_fail_on_check_digit_changes(valid_number)
|
54
|
-
end
|
55
|
-
|
56
|
-
should "detect #{valid_number} regardless of spacing" do
|
57
|
-
should_detect_number_variants(valid_number, TrackingNumber::FedExGround18)
|
58
|
-
end
|
59
|
-
end
|
60
|
-
|
61
|
-
['61299998820821171811', '9261292700768711948021'].each do |valid_number|
|
62
|
-
should "return fedex smart post for #{valid_number}" do
|
63
|
-
should_be_valid_number(valid_number, TrackingNumber::FedExSmartPost, :fedex)
|
64
|
-
end
|
65
|
-
|
66
|
-
should "fail on check digit changes on #{valid_number}" do
|
67
|
-
should_fail_on_check_digit_changes(valid_number)
|
68
|
-
end
|
69
|
-
|
70
|
-
should "detect #{valid_number} regardless of spacing" do
|
71
|
-
should_detect_number_variants(valid_number, TrackingNumber::FedExSmartPost)
|
72
|
-
end
|
73
|
-
end
|
74
|
-
end
|
75
|
-
end
|
@@ -1,24 +0,0 @@
|
|
1
|
-
require 'test_helper'
|
2
|
-
|
3
|
-
class OnTracTrackingNumberTest < Minitest::Test
|
4
|
-
context "an OnTrac tracking number" do
|
5
|
-
["C11031500001879", "C10999911320231"].each do |valid_number|
|
6
|
-
should "return ontrac for #{valid_number}" do
|
7
|
-
should_be_valid_number(valid_number, TrackingNumber::OnTrac, :ontrac)
|
8
|
-
end
|
9
|
-
|
10
|
-
should "fail on check digit changes on #{valid_number}" do
|
11
|
-
should_fail_on_check_digit_changes(valid_number)
|
12
|
-
end
|
13
|
-
|
14
|
-
should "detect #{valid_number} regardless of spacing" do
|
15
|
-
should_detect_number_variants(valid_number, TrackingNumber::OnTrac)
|
16
|
-
end
|
17
|
-
end
|
18
|
-
|
19
|
-
should "not detect an invalid number" do
|
20
|
-
results = TrackingNumber::OnTrac.search("C10999911320230")
|
21
|
-
assert_equal 0, results.size
|
22
|
-
end
|
23
|
-
end
|
24
|
-
end
|