tracking_number 0.1.0 → 0.2.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.1.0
1
+ 0.2.0
@@ -5,12 +5,12 @@
5
5
 
6
6
  class TrackingNumber
7
7
  SEARCH_PATTERNS = {
8
- :ups => /\b1Z\w{16,16}\b/,
9
- :fedex_express => /\b[0-9]{12,12}\b/,
10
- :fedex_ground_96 => /\b96[0-9]{20,20}\b/,
11
- :fedex_ground_sscc18 => /\b[0-9]{18,18}\b/,
12
- :dhl => /\b[0-9]{11,11}\b/,
13
- :usps => /\b[0-9]{22,22}\b/
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
14
  }
15
15
 
16
16
  VERIFY_PATTERNS = {
@@ -22,151 +22,158 @@ class TrackingNumber
22
22
  :usps => /^([0-9]{21,21})([0-9])/
23
23
  }
24
24
 
25
+ def self.scan(body)
26
+ possibles = SEARCH_PATTERNS.values.collect { |pattern|
27
+ body.scan(pattern)
28
+ }.uniq.flatten
29
+ end
30
+
25
31
  def self.search(body)
26
- possibles = SEARCH_PATTERNS.values.collect { |pattern|
27
- body.scan(pattern)
28
- }.uniq.flatten
29
-
30
- possibles.collect { |possible| TrackingNumber.new(possible) }.select { |t| t.valid? }
32
+ self.scan(body).uniq.collect { |possible| TrackingNumber.new(possible) }.select { |t| t.valid? }
31
33
  end
32
34
 
33
35
  def initialize(tracking_number)
34
- @tracking_number = tracking_number.to_s.upcase.gsub(/\s/,"")
35
- end
36
+ @original_number = tracking_number
37
+ @tracking_number = tracking_number.to_s.upcase.gsub(/\s/,"")
38
+ end
36
39
 
37
- def valid?
38
- carrier != :unknown
39
- end
40
+ def valid?
41
+ carrier != :unknown
42
+ end
40
43
 
41
- def carrier
42
- @carrier = if ups?(@tracking_number)
43
- :ups
44
- elsif fedex_express?(@tracking_number)
45
- :fedex
46
- elsif fedex_ground_96?(@tracking_number)
47
- :fedex
48
- elsif fedex_ground_sscc18?(@tracking_number)
49
- :fedex
50
- elsif usps?(@tracking_number)
51
- :usps
52
- elsif dhl?(@tracking_number)
53
- :dhl
54
- else
55
- :unknown
56
- end
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
57
59
  end
60
+ end
61
+
62
+ def original_number
63
+ @original_number
64
+ end
65
+
66
+ def tracking_number
67
+ @tracking_number
68
+ end
58
69
 
59
- def tracking_number
60
- @tracking_number
61
- end
70
+ def to_s
71
+ @tracking_number
72
+ end
62
73
 
63
- def to_s
64
- @tracking_number
65
- end
74
+ private
75
+
76
+ def ups?(tracking_number)
77
+ results = tracking_number.scan(VERIFY_PATTERNS[:ups])
78
+ return false if results.nil? || results.empty?
66
79
 
67
- private
68
-
69
- def ups?(tracking_number)
70
- results = tracking_number.scan(VERIFY_PATTERNS[:ups])
71
- return false if results.nil? || results.empty?
72
-
73
- sequence, check_digit = results.flatten
74
- total = 0
75
- sequence.chars.each_with_index do |c, i|
76
- x = if c[/[0-9]/] # numeric
77
- c.to_i
78
- else
79
- (c[0].ord - 3) % 10
80
- end
81
- x *= 2 if i.odd?
82
- total += x
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
83
87
  end
88
+ x *= 2 if i.odd?
89
+ total += x
90
+ end
84
91
 
85
- check = (total % 10)
86
- check = (10 - check) unless (check.zero?)
92
+ check = (total % 10)
93
+ check = (10 - check) unless (check.zero?)
87
94
 
88
- return true if (check.to_i == check_digit.to_i)
89
- end
95
+ return true if (check.to_i == check_digit.to_i)
96
+ end
90
97
 
91
98
 
92
- def fedex_express?(tracking_number)
93
- results = tracking_number.scan(VERIFY_PATTERNS[:fedex_express]).flatten
94
- return false if results.nil? || results.empty?
95
- sequence, check = results
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
96
103
 
97
- sequence = sequence.chars.to_a.map(&:to_i)
98
- total = 0
99
- 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 }
100
- return true if (total % 11) == check.to_i
101
- end
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
102
109
 
103
- def fedex_ground_96?(tracking_number)
104
- # 22 numbers
105
- # http://fedex.com/us/solutions/ppe/FedEx_Ground_Label_Layout_Specification.pdf
106
- # 96 - UCC/EAN Application Identifier
107
- # [0-9]{2,2} - SCNC
108
- # [0-9]{3,3} - Class Of Service
109
-
110
- # [0-9]{7,7} - RPS Shipper ID (used in calculation)
111
- # [0-9]{7,7} - Package Number (used in calculation)
112
-
113
- # [0-9] - Check Digit
114
- results = tracking_number.scan(VERIFY_PATTERNS[:fedex_ground_96]).flatten
115
- return false if results.nil? || results.empty?
116
-
117
- sequence, check_digit = results.flatten
118
- total = 0
119
- sequence.chars.to_a.reverse.map(&:to_i).each_with_index do |x, i|
120
- x *= 3 if i.even?
121
- total += x
122
- end
123
- check = total % 10
124
- check = (10 - check) unless (check.zero?)
125
- return true if check == check_digit.to_i
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
126
129
  end
130
+ check = total % 10
131
+ check = (10 - check) unless (check.zero?)
132
+ return true if check == check_digit.to_i
133
+ end
127
134
 
128
- def fedex_ground_sscc18?(tracking_number)
129
- # [0-9]{2,2} - Not used
130
- # [0-9]{15, 15} - used for calculation
131
- results = tracking_number.scan(VERIFY_PATTERNS[:fedex_ground_sscc18]).flatten
132
- return false if results.nil? || results.empty?
133
- sequence, check_digit = results
134
- total = 0
135
- sequence.chars.to_a.map(&:to_i).reverse.each_with_index do |x, i|
136
- x *= 3 if i.even?
137
- total += x
138
- end
139
-
140
- check = total % 10
141
- check = (10 - check) unless (check.zero?)
142
- return true if check == check_digit.to_i
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
143
145
  end
144
146
 
145
- def dhl?(tracking_number)
146
- # standard mod 7 check
147
- results = tracking_number.scan(VERIFY_PATTERNS[:dhl]).flatten
148
- return false if results.nil? || results.empty?
147
+ check = total % 10
148
+ check = (10 - check) unless (check.zero?)
149
+ return true if check == check_digit.to_i
150
+ end
149
151
 
150
- sequence, check_digit = results
151
- return true if sequence.to_i % 7 == check_digit.to_i
152
- end
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?
153
156
 
154
- def usps?(tracking_number)
155
- # standard mod 10 check
156
- results = tracking_number.scan(VERIFY_PATTERNS[:usps]).flatten
157
- return false if results.nil? || results.empty?
157
+ sequence, check_digit = results
158
+ return true if sequence.to_i % 7 == check_digit.to_i
159
+ end
158
160
 
159
- sequence, check_digit = results
160
- total = 0
161
- sequence.chars.to_a.reverse.each_with_index do |c, i|
162
- x = c.to_i
163
- x *= 3 if i.even?
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?
164
165
 
165
- total += x
166
- end
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?
167
171
 
168
- check = total % 10
169
- check = 10 - check unless (check.zero?)
170
- return true if check == check_digit.to_i
171
- end
172
+ total += x
172
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
179
+ end
data/test/test_helper.rb CHANGED
@@ -15,4 +15,14 @@ $LOAD_PATH.unshift(File.dirname(__FILE__))
15
15
  require 'tracking_number'
16
16
 
17
17
  class Test::Unit::TestCase
18
+ def possible_numbers(tracking)
19
+ possible_numbers = []
20
+ possible_numbers << tracking
21
+ possible_numbers << tracking.chars.to_a.join(" ")
22
+ possible_numbers << tracking.chars.to_a.join(" ")
23
+ possible_numbers << tracking.slice(0, (tracking.length / 2)) + " " + tracking.slice((tracking.length / 2), tracking.length)
24
+
25
+ possible_numbers
26
+ end
27
+
18
28
  end
@@ -1,61 +1,96 @@
1
1
  require 'test_helper'
2
2
 
3
- class TrackingNumberTest < Test::Unit::TestCase
4
- should "return ups with valid ups number" do
5
- assert_equal :ups, TrackingNumber.new("1Z5R89390357567127").carrier
6
- assert_equal :ups, TrackingNumber.new("1Z879E930346834440").carrier
7
- assert_equal :ups, TrackingNumber.new("1Z410E7W0392751591").carrier
8
- assert_equal :ups, TrackingNumber.new("1Z8V92A70367203024").carrier
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
9
11
  end
10
-
11
- should "return fedex with valid fedex express number" do
12
- t = TrackingNumber.new("986578788855")
13
- assert_equal :fedex, t.carrier
14
- assert t.valid?
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?
15
18
 
16
- t = TrackingNumber.new("477179081230")
17
- assert_equal :fedex, t.carrier
18
- assert t.valid?
19
- end
19
+ t = TrackingNumber.new("477179081230")
20
+ assert_equal :fedex, t.carrier
21
+ assert t.valid?
22
+ end
20
23
 
21
- should "return fedex with valid fedex ground (96) number" do
22
- t = TrackingNumber.new("9611020987654312345672")
23
- assert_equal :fedex, t.carrier
24
- assert t.valid?
25
- end
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
26
29
 
27
- should "return fedex with valid fedex ground (SSCC18) number" do
28
- t = TrackingNumber.new("000123450000000027")
29
- assert_equal :fedex, t.carrier
30
- assert t.valid?
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
31
35
  end
32
36
 
33
- should "return dhl with valid dhl number" do
34
- t = TrackingNumber.new("73891051146")
35
- assert_equal :dhl, t.carrier
36
- assert t.valid?
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
37
43
  end
38
44
 
39
- should "return usps with valid usps number" do
40
- t = TrackingNumber.new("9101 1234 5678 9000 0000 13")
41
- assert_equal :usps, t.carrier
42
- assert t.valid?
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
43
51
  end
44
52
 
45
- should "return unknown when given invalid number" do
46
- t = TrackingNumber.new("101")
47
- assert_equal :unknown, t.carrier
48
- assert !t.valid?
49
- end
53
+ context "a tracking number" do
54
+ should "return unknown when given invalid number" do
55
+ t = TrackingNumber.new("101")
56
+ assert_equal :unknown, t.carrier
57
+ assert !t.valid?
58
+ end
50
59
 
51
- should "upcase and remove spaces from tracking number" do
52
- t = TrackingNumber.new("abc 123 def")
53
- assert_equal "ABC123DEF", t.tracking_number
60
+ should "upcase and remove spaces from tracking number" do
61
+ t = TrackingNumber.new("abc 123 def")
62
+ assert_equal "ABC123DEF", t.tracking_number
63
+ end
54
64
  end
55
65
 
56
- should "return two tracking numbers when given test search string" do
57
- 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.")
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
58
81
 
59
- assert_equal 2, s.size
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
94
+ end
60
95
  end
61
96
  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.1.0"
8
+ s.version = "0.2.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{2010-12-30}
12
+ s.date = %q{2011-01-29}
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 = [
metadata CHANGED
@@ -4,9 +4,9 @@ version: !ruby/object:Gem::Version
4
4
  prerelease: false
5
5
  segments:
6
6
  - 0
7
- - 1
7
+ - 2
8
8
  - 0
9
- version: 0.1.0
9
+ version: 0.2.0
10
10
  platform: ruby
11
11
  authors:
12
12
  - Jeff Keen
@@ -14,7 +14,7 @@ autorequire:
14
14
  bindir: bin
15
15
  cert_chain: []
16
16
 
17
- date: 2010-12-30 00:00:00 -06:00
17
+ date: 2011-01-29 00:00:00 -06:00
18
18
  default_executable:
19
19
  dependencies:
20
20
  - !ruby/object:Gem::Dependency
@@ -108,7 +108,7 @@ required_ruby_version: !ruby/object:Gem::Requirement
108
108
  requirements:
109
109
  - - ">="
110
110
  - !ruby/object:Gem::Version
111
- hash: 2711137712783242213
111
+ hash: -1308548554367875831
112
112
  segments:
113
113
  - 0
114
114
  version: "0"