tracking_number 0.2.0 → 0.3.0

Sign up to get free protection for your applications and to get access to all the features.
data/VERSION CHANGED
@@ -1 +1 @@
1
- 0.2.0
1
+ 0.3.0
data/lib/base.rb ADDED
@@ -0,0 +1,49 @@
1
+ class TrackingNumber
2
+ class Base
3
+ attr_accessor :tracking_number
4
+ def initialize(tracking_number)
5
+ @original_number = tracking_number
6
+ @tracking_number = tracking_number.gsub(" ", "").upcase
7
+ end
8
+
9
+ def self.search(body)
10
+ self.scan(body).uniq.collect { |possible| new(possible) }.select { |t| t.valid? }
11
+ end
12
+
13
+ def self.scan(body)
14
+ possibles = body.scan(self.const_get("SEARCH_PATTERN")).uniq.flatten
15
+ end
16
+
17
+ def valid?
18
+ return false unless valid_format?
19
+ return false unless valid_checksum?
20
+ return true
21
+ end
22
+
23
+ def valid_format?
24
+ !matches.nil? && !matches.empty?
25
+ end
26
+
27
+ def decode
28
+ {}
29
+ end
30
+
31
+ def matches
32
+ []
33
+ end
34
+
35
+ def valid_checksum?
36
+ false
37
+ end
38
+
39
+ def to_s
40
+ self.tracking_number
41
+ end
42
+ end
43
+
44
+ class Unknown < Base
45
+ def carrier
46
+ :unknown
47
+ end
48
+ end
49
+ end
data/lib/dhl.rb ADDED
@@ -0,0 +1,19 @@
1
+ class TrackingNumber
2
+ class DHL < Base
3
+ SEARCH_PATTERN = /(\b([0-9]\s*){11,11}\b)/
4
+ VERIFY_PATTERN = /^([0-9]{10,10})([0-9])$/
5
+ def carrier
6
+ :dhl
7
+ end
8
+
9
+ def matches
10
+ self.tracking_number.scan(VERIFY_PATTERN).flatten
11
+ end
12
+
13
+ def valid_checksum?
14
+ # standard mod 7 check
15
+ sequence, check_digit = matches
16
+ return true if sequence.to_i % 7 == check_digit.to_i
17
+ end
18
+ end
19
+ end
data/lib/fedex.rb ADDED
@@ -0,0 +1,130 @@
1
+ class TrackingNumber
2
+ class FedEx < Base
3
+ def carrier
4
+ :fedex
5
+ end
6
+ end
7
+
8
+ class FedExExpress < FedEx
9
+ SEARCH_PATTERN = /(\b([0-9]\s*){12,12}\b)/
10
+ VERIFY_PATTERN = /^([0-9]{11,11})([0-9])$/
11
+ LENGTH = 12
12
+
13
+ def matches
14
+ self.tracking_number.scan(VERIFY_PATTERN).flatten
15
+ end
16
+
17
+ def valid_checksum?
18
+ sequence = tracking_number.chars.to_a
19
+ check = sequence.pop
20
+ total = 0
21
+ sequence.zip([3,1,7,3,1,7,3,1,7,3,1]).collect { |pair| pair[0].to_i * pair[1].to_i }.each { |t| total += t.to_i }
22
+ return (total % 11) == check.to_i
23
+ end
24
+ end
25
+
26
+ #TODO Fix these FedEx ground numberss
27
+
28
+ class FedExGround96 < FedEx
29
+ SEARCH_PATTERN = /(\b9\s*6\s*([0-9]\s*){20,20}\b)/
30
+ VERIFY_PATTERN = /^96[0-9]{5,5}([0-9]{14,14})([0-9])$/
31
+ LENGTH = 22
32
+
33
+ def matches
34
+ self.tracking_number.scan(VERIFY_PATTERN).flatten
35
+ end
36
+
37
+ def decode
38
+ {:application_id => self.tracking_number.to_s.slice(0...2),
39
+ :serial_container => self.tracking_number.to_s.slice(2...4),
40
+ :service_code => self.tracking_number.to_s.slice(4...7),
41
+ :shipper_id => self.tracking_number.to_s.slice(7...14),
42
+ :package_identifier => self.tracking_number.to_s.slice(14...21),
43
+ :check_digit => self.tracking_number.slice(21...22)
44
+ }
45
+ end
46
+
47
+ def valid_checksum?
48
+ # 22 numbers
49
+ # http://fedex.com/us/solutions/ppe/FedEx_Ground_Label_Layout_Specification.pdf
50
+ # 96 - UCC/EAN Application Identifier
51
+
52
+ # [0-9]{2,2} - SCNC
53
+ # [0-9]{3,3} - Class Of Service
54
+ # [0-9]{7,7} - RPS Shipper ID (used in calculation)
55
+ # [0-9]{7,7} - Package Number (used in calculation)
56
+ # [0-9] - Check Digit
57
+ sequence, check_digit = matches
58
+
59
+ total = 0
60
+ sequence.chars.to_a.map(&:to_i).reverse.each_with_index do |x, i|
61
+ x *= 3 if i.even?
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 FedExGround < FedEx
72
+ SEARCH_PATTERN = /(\b([0-9]\s*){15,15}\b)/
73
+ VERIFY_PATTERN = /^([0-9]{15,15})$/
74
+ LENGTH = 15
75
+
76
+ def matches
77
+ self.tracking_number.scan(VERIFY_PATTERN).flatten
78
+ end
79
+
80
+ def valid_checksum?
81
+ sequence = tracking_number.chars.to_a.map(&:to_i)
82
+ check_digit = sequence.pop
83
+ total = 0
84
+ sequence.reverse.each_with_index do |x, i|
85
+ x *= 3 if i.even?
86
+ total += x
87
+ end
88
+ check = total % 10
89
+ check = (10 - check) unless (check.zero?)
90
+ return true if check == check_digit.to_i
91
+ end
92
+ end
93
+
94
+ class FedExGround18 < FedEx
95
+ SEARCH_PATTERN = /(\b([0-9]\s*){18,18}\b)/
96
+ VERIFY_PATTERN = /^[0-9]{2,2}([0-9]{15,15})([0-9])$/
97
+ LENGTH = 20
98
+
99
+ def matches
100
+ self.tracking_number.scan(VERIFY_PATTERN).flatten
101
+ end
102
+
103
+ def decode
104
+ {:application_id => self.tracking_number.to_s.slice(0...2),
105
+ :serial_container => self.tracking_number.to_s.slice(1...2),
106
+ :service_code => self.tracking_number.to_s.slice(2...3),
107
+ :shipper_id => self.tracking_number.to_s.slice(3...10),
108
+ :package_identifier => self.tracking_number.to_s.slice(10...17),
109
+ :check_digit => self.tracking_number.slice(17...18)
110
+ }
111
+ end
112
+
113
+ def valid_checksum?
114
+ # [0-9]{2,2} - Not used
115
+ # [0-9]{15, 15} - used for calculation
116
+
117
+ sequence = tracking_number.chars.to_a.map(&:to_i)
118
+ check_digit = sequence.pop
119
+ total = 0
120
+ sequence.reverse.each_with_index do |x, i|
121
+ x *= 3 if i.even?
122
+ total += x
123
+ end
124
+ check = total % 10
125
+ check = (10 - check) unless (check.zero?)
126
+ return true if check == check_digit.to_i
127
+ end
128
+ end
129
+
130
+ end
@@ -3,177 +3,31 @@
3
3
  # Information on validating tracking numbers found here:
4
4
  # http://answers.google.com/answers/threadview/id/207899.html
5
5
 
6
- class TrackingNumber
7
- SEARCH_PATTERNS = {
8
- :ups => /(\b1\s*Z\s*(\w\s*){16,16}\b)/,
9
- :fedex_express => /(\b([0-9]\s*){12,12}\b)/,
10
- :fedex_ground_96 => /(\b9\s*6\s*([0-9]\s*){20,20}\b)/,
11
- :fedex_ground_sscc18 => /(\b([0-9]\s*){18,18}\b)/,
12
- :dhl => /(\b([0-9]\s*){11,11}\b)/,
13
- :usps => /(\b([0-9]\s*){22,22}\b)/
14
- }
15
-
16
- VERIFY_PATTERNS = {
17
- :ups => /^1Z(\w{15,15})(\w)$/,
18
- :fedex_express => /^([0-9]{11,11})([0-9])$/,
19
- :fedex_ground_96 => /^96[0-9]{5,5}([0-9]{14,14})([0-9])$/,
20
- :fedex_ground_sscc18 => /^[0-9]{2,2}([0-9]{15,15})([0-9])$/,
21
- :dhl => /^([0-9]{10,10})([0-9])$/,
22
- :usps => /^([0-9]{21,21})([0-9])/
23
- }
6
+ require 'base'
7
+ require 'usps'
8
+ require 'fedex'
9
+ require 'ups'
10
+ require 'dhl'
24
11
 
25
- def self.scan(body)
26
- possibles = SEARCH_PATTERNS.values.collect { |pattern|
27
- body.scan(pattern)
28
- }.uniq.flatten
29
- end
12
+ class TrackingNumber
13
+ TYPES = [UPS, FedExExpress, FedExGround, FedExGround18, FedExGround96, USPS91, USPS20, USPS13, DHL]
30
14
 
31
15
  def self.search(body)
32
- self.scan(body).uniq.collect { |possible| TrackingNumber.new(possible) }.select { |t| t.valid? }
33
- end
34
-
35
- def initialize(tracking_number)
36
- @original_number = tracking_number
37
- @tracking_number = tracking_number.to_s.upcase.gsub(/\s/,"")
38
- end
39
-
40
- def valid?
41
- carrier != :unknown
16
+ TYPES.collect { |type| type.search(body) }.flatten
42
17
  end
43
18
 
44
- def carrier
45
- @carrier = if ups?(@tracking_number)
46
- :ups
47
- elsif fedex_express?(@tracking_number)
48
- :fedex
49
- elsif fedex_ground_96?(@tracking_number)
50
- :fedex
51
- elsif fedex_ground_sscc18?(@tracking_number)
52
- :fedex
53
- elsif usps?(@tracking_number)
54
- :usps
55
- elsif dhl?(@tracking_number)
56
- :dhl
57
- else
58
- :unknown
19
+ def self.detect(tracking_number)
20
+ detected = TYPES.collect do |test|
21
+ t = test.new(tracking_number)
22
+ t if t.valid?
59
23
  end
60
- end
61
-
62
- def original_number
63
- @original_number
64
- end
65
-
66
- def tracking_number
67
- @tracking_number
68
- end
24
+ found = detected.compact.first
69
25
 
70
- def to_s
71
- @tracking_number
26
+ return found if found
27
+ return Unknown.new(tracking_number)
72
28
  end
73
29
 
74
- private
75
-
76
- def ups?(tracking_number)
77
- results = tracking_number.scan(VERIFY_PATTERNS[:ups])
78
- return false if results.nil? || results.empty?
79
-
80
- sequence, check_digit = results.flatten
81
- total = 0
82
- sequence.chars.each_with_index do |c, i|
83
- x = if c[/[0-9]/] # numeric
84
- c.to_i
85
- else
86
- (c[0].ord - 3) % 10
87
- end
88
- x *= 2 if i.odd?
89
- total += x
90
- end
91
-
92
- check = (total % 10)
93
- check = (10 - check) unless (check.zero?)
94
-
95
- return true if (check.to_i == check_digit.to_i)
96
- end
97
-
98
-
99
- def fedex_express?(tracking_number)
100
- results = tracking_number.scan(VERIFY_PATTERNS[:fedex_express]).flatten
101
- return false if results.nil? || results.empty?
102
- sequence, check = results
103
-
104
- sequence = sequence.chars.to_a.map(&:to_i)
105
- total = 0
106
- sequence.zip([3,1,7,3,1,7,3,1,7,3,1]).collect { |pair| pair[0] * pair[1] }.each { |t| total += t.to_i }
107
- return true if (total % 11) == check.to_i
108
- end
109
-
110
- def fedex_ground_96?(tracking_number)
111
- # 22 numbers
112
- # http://fedex.com/us/solutions/ppe/FedEx_Ground_Label_Layout_Specification.pdf
113
- # 96 - UCC/EAN Application Identifier
114
- # [0-9]{2,2} - SCNC
115
- # [0-9]{3,3} - Class Of Service
116
-
117
- # [0-9]{7,7} - RPS Shipper ID (used in calculation)
118
- # [0-9]{7,7} - Package Number (used in calculation)
119
-
120
- # [0-9] - Check Digit
121
- results = tracking_number.scan(VERIFY_PATTERNS[:fedex_ground_96]).flatten
122
- return false if results.nil? || results.empty?
123
-
124
- sequence, check_digit = results.flatten
125
- total = 0
126
- sequence.chars.to_a.reverse.map(&:to_i).each_with_index do |x, i|
127
- x *= 3 if i.even?
128
- total += x
129
- end
130
- check = total % 10
131
- check = (10 - check) unless (check.zero?)
132
- return true if check == check_digit.to_i
133
- end
134
-
135
- def fedex_ground_sscc18?(tracking_number)
136
- # [0-9]{2,2} - Not used
137
- # [0-9]{15, 15} - used for calculation
138
- results = tracking_number.scan(VERIFY_PATTERNS[:fedex_ground_sscc18]).flatten
139
- return false if results.nil? || results.empty?
140
- sequence, check_digit = results
141
- total = 0
142
- sequence.chars.to_a.map(&:to_i).reverse.each_with_index do |x, i|
143
- x *= 3 if i.even?
144
- total += x
145
- end
146
-
147
- check = total % 10
148
- check = (10 - check) unless (check.zero?)
149
- return true if check == check_digit.to_i
150
- end
151
-
152
- def dhl?(tracking_number)
153
- # standard mod 7 check
154
- results = tracking_number.scan(VERIFY_PATTERNS[:dhl]).flatten
155
- return false if results.nil? || results.empty?
156
-
157
- sequence, check_digit = results
158
- return true if sequence.to_i % 7 == check_digit.to_i
159
- end
160
-
161
- def usps?(tracking_number)
162
- # standard mod 10 check
163
- results = tracking_number.scan(VERIFY_PATTERNS[:usps]).flatten
164
- return false if results.nil? || results.empty?
165
-
166
- sequence, check_digit = results
167
- total = 0
168
- sequence.chars.to_a.reverse.each_with_index do |c, i|
169
- x = c.to_i
170
- x *= 3 if i.even?
171
-
172
- total += x
173
- end
174
-
175
- check = total % 10
176
- check = 10 - check unless (check.zero?)
177
- return true if check == check_digit.to_i
178
- end
30
+ def self.new(tracking_number)
31
+ self.detect(tracking_number)
32
+ end
179
33
  end
data/lib/ups.rb ADDED
@@ -0,0 +1,92 @@
1
+ class 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
+ end
data/lib/usps.rb ADDED
@@ -0,0 +1,127 @@
1
+ class TrackingNumber
2
+ class USPS < Base
3
+ def carrier
4
+ :usps
5
+ end
6
+ end
7
+
8
+ class USPS91 < USPS
9
+ SEARCH_PATTERN = /(\b9\s*1\s*(([0-9]\s*){20,20}\b))/
10
+ VERIFY_PATTERN = /^(91[0-9]{19,19})([0-9])$/
11
+
12
+ def matches
13
+ self.tracking_number.scan(VERIFY_PATTERN).flatten
14
+ end
15
+
16
+ def decode
17
+ # Application ID: 91
18
+ # Service Code: 2 Digits
19
+ # Mailer Id: 8 Digits
20
+ # Package Id: 9 Digits
21
+ # Checksum: 1 Digit
22
+
23
+ {:application_id => self.tracking_number.to_s.slice(0...2),
24
+ :service_code => self.tracking_number.to_s.slice(2...4),
25
+ :mailer_id => self.tracking_number.to_s.slice(4...12),
26
+ :package_identifier => self.tracking_number.to_s.slice(12...21),
27
+ :check_digit => self.tracking_number.slice(21...22)
28
+ }
29
+ end
30
+
31
+ def valid_checksum?
32
+ chars = tracking_number.chars.to_a
33
+ check_digit = chars.pop
34
+
35
+ total = 0
36
+ chars.reverse.each_with_index do |c, i|
37
+ x = c.to_i
38
+ x *= 3 if i.even?
39
+
40
+ total += x
41
+ end
42
+
43
+ check = total % 10
44
+ check = 10 - check unless (check.zero?)
45
+ return true if check == check_digit.to_i
46
+ end
47
+ end
48
+
49
+ class USPS20 < USPS
50
+ SEARCH_PATTERN = /(\b([0-9]\s*){20,20}\b)/
51
+ VERIFY_PATTERN = /^([0-9]{2,2})([0-9]{9,9})([0-9]{8,8})([0-9])$/
52
+
53
+ def matches
54
+ self.tracking_number.scan(VERIFY_PATTERN).flatten
55
+ end
56
+
57
+ def decode
58
+ {:service_code => self.tracking_number.to_s.slice(0...2),
59
+ :mailer_id => self.tracking_number.to_s.slice(2...11),
60
+ :package_identifier => self.tracking_number.to_s.slice(11...19),
61
+ :check_digit => self.tracking_number.slice(19...20)
62
+ }
63
+ end
64
+
65
+ def service_type
66
+ case decode[:service_code]
67
+ when "71"
68
+ "Certified Mail"
69
+ when "73"
70
+ "Insured Mail"
71
+ when "77"
72
+ "Registered Mail"
73
+ when "81"
74
+ "Return Receipt for Merchandise"
75
+ end
76
+ end
77
+
78
+ def valid_checksum?
79
+ chars = tracking_number.chars.to_a
80
+ check_digit = chars.pop
81
+
82
+ total = 0
83
+ chars.reverse.each_with_index do |c, i|
84
+ x = c.to_i
85
+ x *= 3 if i.even?
86
+
87
+ total += x
88
+ end
89
+
90
+ check = total % 10
91
+ check = 10 - check unless (check.zero?)
92
+ return true if check == check_digit.to_i
93
+ end
94
+ end
95
+
96
+ class USPS13 < USPS
97
+ SEARCH_PATTERN = /(\b([A-Z]\s*){2,2}([0-9]\s*){9,9}([A-Z]\s*){2,2}\b)/
98
+ VERIFY_PATTERN = /^([A-Z]{2,2})([0-9]{9,9})([A-Z]{2,2})$/
99
+
100
+ def matches
101
+ self.tracking_number.scan(VERIFY_PATTERN).flatten
102
+ end
103
+
104
+ def valid_checksum?
105
+ sequence = tracking_number.scan(/[0-9]+/).flatten.to_s
106
+ chars = sequence.chars.to_a
107
+ check_digit = chars.pop.to_i
108
+
109
+ sum = 0
110
+ chars.zip([8,6,4,2,3,5,9,7]).each do |pair|
111
+ sum += (pair[0].to_i * pair[1].to_i)
112
+ end
113
+
114
+ remainder = sum % 11
115
+ check = case remainder
116
+ when 1
117
+ 0
118
+ when 0
119
+ 5
120
+ else
121
+ 11 - remainder
122
+ end
123
+
124
+ return check == check_digit
125
+ end
126
+ end
127
+ end
@@ -0,0 +1,17 @@
1
+ require 'test_helper'
2
+
3
+ class DHLTrackingNumberTest < Test::Unit::TestCase
4
+ context "a DHL tracking number" do
5
+ ["73891051146"].each do |valid_number|
6
+ should "return dhl for #{valid_number}" do
7
+ t = TrackingNumber.new(valid_number)
8
+ assert_equal :dhl, t.carrier
9
+ assert t.valid?
10
+ end
11
+
12
+ should "detect #{valid_number} regardless of spacing" do
13
+ should_detect_number_variants(valid_number, TrackingNumber::DHL)
14
+ end
15
+ end
16
+ end
17
+ end
@@ -0,0 +1,58 @@
1
+ require 'test_helper'
2
+
3
+ class FedExTrackingNumberTest < Test::Unit::TestCase
4
+ context "a FedEx tracking number" do
5
+ ["986578788855", "477179081230", "799531274483", "790535312317"].each do |valid_number|
6
+ should "return fedex express for #{valid_number}" do
7
+ t = TrackingNumber.new(valid_number)
8
+ assert_equal TrackingNumber::FedExExpress, t.class
9
+ assert_equal :fedex, t.carrier
10
+ assert t.valid?
11
+ end
12
+
13
+ should "detect #{valid_number} regardless of spacing" do
14
+ should_detect_number_variants(valid_number, TrackingNumber::FedExExpress)
15
+ end
16
+ end
17
+
18
+ ["9611020987654312345672"].each do |valid_number|
19
+ should "return fedex 96 for #{valid_number}" do
20
+ t = TrackingNumber.new(valid_number)
21
+ assert_equal TrackingNumber::FedExGround96, t.class
22
+ assert_equal :fedex, t.carrier
23
+ assert t.valid?
24
+ end
25
+
26
+ should "detect #{valid_number} regardless of spacing" do
27
+ should_detect_number_variants(valid_number, TrackingNumber::FedExGround96)
28
+ end
29
+ end
30
+
31
+ ["0414 4176 0228 964", "5682 8361 0012 000", "5682 8361 0012 734"].each do |valid_number|
32
+ should "return fedex ground for #{valid_number}" do
33
+ t = TrackingNumber.new(valid_number)
34
+ assert_equal TrackingNumber::FedExGround, t.class
35
+ assert_equal :fedex, t.carrier
36
+ assert t.valid?
37
+ end
38
+
39
+ should "detect #{valid_number} regardless of spacing" do
40
+ should_detect_number_variants(valid_number, TrackingNumber::FedExGround)
41
+ end
42
+ end
43
+
44
+ ["00 0123 4500 0000 0027"].each do |valid_number|
45
+ should "return fedex sscc18 for #{valid_number}" do
46
+ t = TrackingNumber.new(valid_number)
47
+ assert_equal TrackingNumber::FedExGround18, t.class
48
+ assert_equal :fedex, t.carrier
49
+ assert t.valid?
50
+ end
51
+
52
+ should "detect #{valid_number} regardless of spacing" do
53
+ should_detect_number_variants(valid_number, TrackingNumber::FedExGround18)
54
+ end
55
+ end
56
+
57
+ end
58
+ end
data/test/test_helper.rb CHANGED
@@ -16,13 +16,29 @@ require 'tracking_number'
16
16
 
17
17
  class Test::Unit::TestCase
18
18
  def possible_numbers(tracking)
19
+ tracking = tracking.to_s
19
20
  possible_numbers = []
20
21
  possible_numbers << tracking
22
+ possible_numbers << tracking.to_s.gsub(" ", "")
21
23
  possible_numbers << tracking.chars.to_a.join(" ")
22
24
  possible_numbers << tracking.chars.to_a.join(" ")
23
25
  possible_numbers << tracking.slice(0, (tracking.length / 2)) + " " + tracking.slice((tracking.length / 2), tracking.length)
24
-
25
- possible_numbers
26
+
27
+ possible_numbers.flatten.uniq
28
+ end
29
+
30
+ def possible_strings(tracking)
31
+ possible_numbers(tracking).flatten.collect { |t| search_string(t) }
32
+ end
33
+
34
+ def search_string(number)
35
+ %Q{Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod tempor #{number} ut labore et dolore magna aliqua.}
36
+ end
37
+
38
+ def should_detect_number_variants(valid_number, type)
39
+ possible_strings(valid_number).each do |string|
40
+ results = type.search(string)
41
+ assert_equal 1, results.size, "could not find #{type} #{valid_number} in #{string}"
42
+ end
26
43
  end
27
-
28
44
  end
@@ -1,96 +1,24 @@
1
1
  require 'test_helper'
2
2
 
3
- class TrackingNumberTest < Test::Unit::TestCase
4
- context "a UPS tracking number" do
5
- should "return ups as the carrier" do
6
- assert_equal :ups, TrackingNumber.new("1Z5R89390357567127").carrier
7
- assert_equal :ups, TrackingNumber.new("1Z879E930346834440").carrier
8
- assert_equal :ups, TrackingNumber.new("1Z410E7W0392751591").carrier
9
- assert_equal :ups, TrackingNumber.new("1Z8V92A70367203024").carrier
10
- end
11
- end
12
-
13
- context "a FedEx tracking number" do
14
- should "return fedex as the carrier if it's a valid fedex express number" do
15
- t = TrackingNumber.new("986578788855")
16
- assert_equal :fedex, t.carrier
17
- assert t.valid?
18
-
19
- t = TrackingNumber.new("477179081230")
20
- assert_equal :fedex, t.carrier
21
- assert t.valid?
22
- end
23
-
24
- should "return fedex as the carrier with valid fedex ground (96) number" do
25
- t = TrackingNumber.new("9611020987654312345672")
26
- assert_equal :fedex, t.carrier
27
- assert t.valid?
28
- end
29
-
30
- should "return fedex as the carrier with valid fedex ground (SSCC18) number" do
31
- t = TrackingNumber.new("000123450000000027")
32
- assert_equal :fedex, t.carrier
33
- assert t.valid?
34
- end
35
- end
36
-
37
- context "a DHL tracking number" do
38
- should "return dhl if it's a valid number" do
39
- t = TrackingNumber.new("73891051146")
40
- assert_equal :dhl, t.carrier
41
- assert t.valid?
42
- end
43
- end
44
-
45
- context "a USPS tracking number" do
46
- should "return usps with if number" do
47
- t = TrackingNumber.new("9101 1234 5678 9000 0000 13")
48
- assert_equal :usps, t.carrier
49
- assert t.valid?
50
- end
51
- end
52
-
3
+ class TrackingNumberTest < Test::Unit::TestCase
53
4
  context "a tracking number" do
54
5
  should "return unknown when given invalid number" do
55
6
  t = TrackingNumber.new("101")
7
+ assert_equal TrackingNumber::Unknown, t.class
56
8
  assert_equal :unknown, t.carrier
57
9
  assert !t.valid?
58
10
  end
59
-
11
+
60
12
  should "upcase and remove spaces from tracking number" do
61
13
  t = TrackingNumber.new("abc 123 def")
62
14
  assert_equal "ABC123DEF", t.tracking_number
63
15
  end
64
16
  end
65
-
66
- context "tracking number search" do
67
- [{"73891051146" => :dhl},
68
- {"1Z8V92A70367203024" => :ups},
69
- {"000123450000000027" => :fedex}, {"9611020987654312345672" => :fedex}, {"477179081230" => :fedex},
70
- {"9101123456789000000013" => :usps}].each do |pair|
71
- expected_service = pair.to_a.flatten[1]
72
- tracking = pair.to_a.flatten[0]
73
-
74
- should "detect #{expected_service} with number #{tracking} regardless of spacing" do
75
- possible_numbers(tracking).each do |spaced_tracking|
76
- results = TrackingNumber.search("blah blah #{spaced_tracking} blah blah")
77
- assert_equal 1, results.size, "#{spaced_tracking} did not match #{expected_service}"
78
- assert_equal expected_service, results.first.carrier
79
- end
80
- end
81
-
82
- should "not detect #{expected_service} when word word boundaries are not in tact" do
83
- possible_numbers(tracking).each do |spaced_tracking|
84
- bad_number = "x1#{spaced_tracking}1x"
85
- results = TrackingNumber.search("blah blah #{bad_number} blah blah")
86
- assert_equal 0, results.size, "#{bad_number} should not match #{expected_service}"
87
- end
88
- end
89
-
90
- should "return two tracking numbers when given string with two" do
91
- s = TrackingNumber.search("Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, 1Z879E930346834440 nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute 9611020987654312345672 dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum.")
92
- assert_equal 2, s.size
93
- end
17
+
18
+ context "tracking number search" do
19
+ should "return two tracking numbers when given string with two" do
20
+ s = TrackingNumber.search("Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, 1Z879E930346834440 nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute 9611020987654312345672 dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum.")
21
+ assert_equal 2, s.size
94
22
  end
95
23
  end
96
24
  end
@@ -0,0 +1,17 @@
1
+ require 'test_helper'
2
+
3
+ class UPSTrackingNumberTest < Test::Unit::TestCase
4
+ context "a UPS tracking number" do
5
+ ["1Z5R89390357567127", "1Z879E930346834440", "1Z410E7W0392751591", "1Z8V92A70367203024"].each do |valid_number|
6
+ should "return ups with valid number #{valid_number}" do
7
+ t = TrackingNumber.new(valid_number)
8
+ assert_equal TrackingNumber::UPS, t.class
9
+ assert_equal :ups, t.carrier
10
+ end
11
+
12
+ should "detect #{valid_number} regardless of spacing" do
13
+ should_detect_number_variants(valid_number, TrackingNumber::UPS)
14
+ end
15
+ end
16
+ end
17
+ end
@@ -0,0 +1,46 @@
1
+ require 'test_helper'
2
+
3
+ class USPSTrackingNumberTest < Test::Unit::TestCase
4
+ context "a USPS tracking number" do
5
+ ["9101 1234 5678 9000 0000 13"].each do |valid_number|
6
+ should "return usps with valid 22 digit number: #{valid_number}" do
7
+ t = TrackingNumber.new("9101 1234 5678 9000 0000 13")
8
+ assert_equal TrackingNumber::USPS91, t.class
9
+ assert_equal :usps, t.carrier
10
+ assert t.valid?
11
+ end
12
+
13
+ should "detect #{valid_number} regardless of spacing" do
14
+ should_detect_number_variants(valid_number, TrackingNumber::USPS91)
15
+ end
16
+ end
17
+
18
+ # Actual tracking number I got from the USPS that doesn't validate. UGghhh
19
+ #"7196 9010 7560 0307 7385",
20
+ ["0307 1790 0005 2348 3741"].each do |valid_number|
21
+ should "detect #{valid_number} regardless of spacing" do
22
+ should_detect_number_variants(valid_number, TrackingNumber::USPS20)
23
+ end
24
+
25
+ should "return usps with valid 20 digit number: #{valid_number}" do
26
+ t = TrackingNumber.new(valid_number)
27
+ assert_equal TrackingNumber::USPS20, t.class
28
+ assert_equal :usps, t.carrier
29
+ assert t.valid?
30
+ end
31
+ end
32
+
33
+ ["RB123456785US"].each do |valid_number|
34
+ should "return usps with valid 13 character number #{valid_number}" do
35
+ t = TrackingNumber.new(valid_number)
36
+ assert_equal TrackingNumber::USPS13, t.class
37
+ assert_equal :usps, t.carrier
38
+ assert t.valid?
39
+ end
40
+
41
+ should "detect #{valid_number} regardless of spacing" do
42
+ should_detect_number_variants(valid_number, TrackingNumber::USPS13)
43
+ end
44
+ end
45
+ end
46
+ end
@@ -5,11 +5,11 @@
5
5
 
6
6
  Gem::Specification.new do |s|
7
7
  s.name = %q{tracking_number}
8
- s.version = "0.2.0"
8
+ s.version = "0.3.0"
9
9
 
10
10
  s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version=
11
11
  s.authors = ["Jeff Keen"]
12
- s.date = %q{2011-01-29}
12
+ s.date = %q{2011-02-09}
13
13
  s.description = %q{Match tracking numbers to a service, and search blocks of text and pull out valid tracking numbers.}
14
14
  s.email = %q{jeff@keen.me}
15
15
  s.extra_rdoc_files = [
@@ -24,23 +24,35 @@ Gem::Specification.new do |s|
24
24
  "README.rdoc",
25
25
  "Rakefile",
26
26
  "VERSION",
27
+ "lib/base.rb",
28
+ "lib/dhl.rb",
29
+ "lib/fedex.rb",
27
30
  "lib/tracking_number.rb",
31
+ "lib/ups.rb",
32
+ "lib/usps.rb",
33
+ "test/test_dhl_tracking_number.rb",
34
+ "test/test_fedex_tracking_number.rb",
28
35
  "test/test_helper.rb",
29
36
  "test/test_tracking_number.rb",
37
+ "test/test_ups_tracking_number.rb",
38
+ "test/test_usps_tracking_number.rb",
30
39
  "tracking_number.gemspec"
31
40
  ]
32
41
  s.homepage = %q{http://github.com/jkeen/tracking_number}
33
42
  s.licenses = ["MIT"]
34
43
  s.require_paths = ["lib"]
35
- s.rubygems_version = %q{1.3.7}
44
+ s.rubygems_version = %q{1.4.1}
36
45
  s.summary = %q{Identifies valid tracking numbers}
37
46
  s.test_files = [
47
+ "test/test_dhl_tracking_number.rb",
48
+ "test/test_fedex_tracking_number.rb",
38
49
  "test/test_helper.rb",
39
- "test/test_tracking_number.rb"
50
+ "test/test_tracking_number.rb",
51
+ "test/test_ups_tracking_number.rb",
52
+ "test/test_usps_tracking_number.rb"
40
53
  ]
41
54
 
42
55
  if s.respond_to? :specification_version then
43
- current_version = Gem::Specification::CURRENT_SPECIFICATION_VERSION
44
56
  s.specification_version = 3
45
57
 
46
58
  if Gem::Version.new(Gem::VERSION) >= Gem::Version.new('1.2.0') then
metadata CHANGED
@@ -1,12 +1,13 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: tracking_number
3
3
  version: !ruby/object:Gem::Version
4
- prerelease: false
4
+ hash: 19
5
+ prerelease:
5
6
  segments:
6
7
  - 0
7
- - 2
8
+ - 3
8
9
  - 0
9
- version: 0.2.0
10
+ version: 0.3.0
10
11
  platform: ruby
11
12
  authors:
12
13
  - Jeff Keen
@@ -14,65 +15,69 @@ autorequire:
14
15
  bindir: bin
15
16
  cert_chain: []
16
17
 
17
- date: 2011-01-29 00:00:00 -06:00
18
+ date: 2011-02-09 00:00:00 -06:00
18
19
  default_executable:
19
20
  dependencies:
20
21
  - !ruby/object:Gem::Dependency
21
- name: shoulda
22
- requirement: &id001 !ruby/object:Gem::Requirement
22
+ version_requirements: &id001 !ruby/object:Gem::Requirement
23
23
  none: false
24
24
  requirements:
25
25
  - - ">="
26
26
  - !ruby/object:Gem::Version
27
+ hash: 3
27
28
  segments:
28
29
  - 0
29
30
  version: "0"
30
- type: :development
31
+ requirement: *id001
31
32
  prerelease: false
32
- version_requirements: *id001
33
+ name: shoulda
34
+ type: :development
33
35
  - !ruby/object:Gem::Dependency
34
- name: bundler
35
- requirement: &id002 !ruby/object:Gem::Requirement
36
+ version_requirements: &id002 !ruby/object:Gem::Requirement
36
37
  none: false
37
38
  requirements:
38
39
  - - ~>
39
40
  - !ruby/object:Gem::Version
41
+ hash: 23
40
42
  segments:
41
43
  - 1
42
44
  - 0
43
45
  - 0
44
46
  version: 1.0.0
45
- type: :development
47
+ requirement: *id002
46
48
  prerelease: false
47
- version_requirements: *id002
49
+ name: bundler
50
+ type: :development
48
51
  - !ruby/object:Gem::Dependency
49
- name: jeweler
50
- requirement: &id003 !ruby/object:Gem::Requirement
52
+ version_requirements: &id003 !ruby/object:Gem::Requirement
51
53
  none: false
52
54
  requirements:
53
55
  - - ~>
54
56
  - !ruby/object:Gem::Version
57
+ hash: 7
55
58
  segments:
56
59
  - 1
57
60
  - 5
58
61
  - 2
59
62
  version: 1.5.2
60
- type: :development
63
+ requirement: *id003
61
64
  prerelease: false
62
- version_requirements: *id003
65
+ name: jeweler
66
+ type: :development
63
67
  - !ruby/object:Gem::Dependency
64
- name: rcov
65
- requirement: &id004 !ruby/object:Gem::Requirement
68
+ version_requirements: &id004 !ruby/object:Gem::Requirement
66
69
  none: false
67
70
  requirements:
68
71
  - - ">="
69
72
  - !ruby/object:Gem::Version
73
+ hash: 3
70
74
  segments:
71
75
  - 0
72
76
  version: "0"
73
- type: :development
77
+ requirement: *id004
74
78
  prerelease: false
75
- version_requirements: *id004
79
+ name: rcov
80
+ type: :development
76
81
  description: Match tracking numbers to a service, and search blocks of text and pull out valid tracking numbers.
77
82
  email: jeff@keen.me
78
83
  executables: []
@@ -90,9 +95,18 @@ files:
90
95
  - README.rdoc
91
96
  - Rakefile
92
97
  - VERSION
98
+ - lib/base.rb
99
+ - lib/dhl.rb
100
+ - lib/fedex.rb
93
101
  - lib/tracking_number.rb
102
+ - lib/ups.rb
103
+ - lib/usps.rb
104
+ - test/test_dhl_tracking_number.rb
105
+ - test/test_fedex_tracking_number.rb
94
106
  - test/test_helper.rb
95
107
  - test/test_tracking_number.rb
108
+ - test/test_ups_tracking_number.rb
109
+ - test/test_usps_tracking_number.rb
96
110
  - tracking_number.gemspec
97
111
  has_rdoc: true
98
112
  homepage: http://github.com/jkeen/tracking_number
@@ -108,7 +122,7 @@ required_ruby_version: !ruby/object:Gem::Requirement
108
122
  requirements:
109
123
  - - ">="
110
124
  - !ruby/object:Gem::Version
111
- hash: -1308548554367875831
125
+ hash: 3
112
126
  segments:
113
127
  - 0
114
128
  version: "0"
@@ -117,16 +131,21 @@ required_rubygems_version: !ruby/object:Gem::Requirement
117
131
  requirements:
118
132
  - - ">="
119
133
  - !ruby/object:Gem::Version
134
+ hash: 3
120
135
  segments:
121
136
  - 0
122
137
  version: "0"
123
138
  requirements: []
124
139
 
125
140
  rubyforge_project:
126
- rubygems_version: 1.3.7
141
+ rubygems_version: 1.4.1
127
142
  signing_key:
128
143
  specification_version: 3
129
144
  summary: Identifies valid tracking numbers
130
145
  test_files:
146
+ - test/test_dhl_tracking_number.rb
147
+ - test/test_fedex_tracking_number.rb
131
148
  - test/test_helper.rb
132
149
  - test/test_tracking_number.rb
150
+ - test/test_ups_tracking_number.rb
151
+ - test/test_usps_tracking_number.rb