tracking_number 1.3.2 → 1.4.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/.github/workflows/release.yml +12 -3
- data/.github/workflows/ruby.yml +4 -2
- data/CHANGELOG.md +15 -0
- data/README.md +48 -0
- data/bin/console +14 -0
- data/lib/data/couriers/amazon.json +45 -0
- data/lib/data/couriers/canadapost.json +31 -0
- data/lib/data/couriers/dhl.json +51 -0
- data/lib/data/couriers/dpd.json +341 -0
- data/lib/data/couriers/fedex.json +303 -0
- data/lib/data/couriers/landmark.json +26 -0
- data/lib/data/couriers/lasership.json +84 -0
- data/lib/data/couriers/ontrac.json +70 -0
- data/lib/data/couriers/s10.json +1435 -0
- data/lib/data/couriers/ups.json +193 -0
- data/lib/data/couriers/usps.json +175 -0
- data/lib/tracking_number/base.rb +166 -85
- data/lib/tracking_number/loader.rb +23 -22
- data/lib/tracking_number/partnership.rb +7 -0
- data/lib/tracking_number/version.rb +1 -1
- data/lib/tracking_number.rb +32 -9
- data/test/test_helper.rb +0 -2
- data/test/tracking_number_meta_test.rb +1 -1
- data/test/tracking_number_test.rb +68 -0
- data/tracking_number.gemspec +15 -6
- data/yarn.lock +448 -448
- metadata +18 -5
data/lib/tracking_number/base.rb
CHANGED
@@ -1,15 +1,16 @@
|
|
1
1
|
module TrackingNumber
|
2
2
|
class Base
|
3
|
-
attr_accessor :tracking_number
|
4
|
-
|
3
|
+
attr_accessor :tracking_number, :original_number, :partner, :partner_data
|
4
|
+
|
5
|
+
PartnerStruct = Struct.new(:shipper, :carrier)
|
5
6
|
|
6
7
|
def initialize(tracking_number)
|
7
8
|
@original_number = tracking_number
|
8
|
-
@tracking_number = tracking_number.strip.gsub(
|
9
|
+
@tracking_number = tracking_number.strip.gsub(' ', '').upcase
|
9
10
|
end
|
10
11
|
|
11
12
|
def self.search(body)
|
12
|
-
valids =
|
13
|
+
valids = scan(body).uniq.collect { |possible| new(possible) }.select { |t| t.valid? }
|
13
14
|
|
14
15
|
uniques = {}
|
15
16
|
valids.each do |t|
|
@@ -23,10 +24,10 @@ module TrackingNumber
|
|
23
24
|
# matches with match groups within the match data
|
24
25
|
matches = []
|
25
26
|
|
26
|
-
body.scan(
|
27
|
-
#get the match data instead, which is needed with these types of regexes
|
27
|
+
body.scan(const_get(:SEARCH_PATTERN)) do
|
28
|
+
# get the match data instead, which is needed with these types of regexes
|
28
29
|
matches << $~
|
29
|
-
|
30
|
+
end
|
30
31
|
|
31
32
|
if matches
|
32
33
|
matches.collect { |m| m[0] }
|
@@ -36,31 +37,29 @@ module TrackingNumber
|
|
36
37
|
end
|
37
38
|
|
38
39
|
def serial_number
|
39
|
-
return match_group(
|
40
|
+
return match_group('SerialNumber') unless self.class.const_get('VALIDATION')
|
40
41
|
|
41
42
|
format_info = self.class.const_get(:VALIDATION)[:serial_number_format]
|
42
|
-
raw_serial = match_group(
|
43
|
+
raw_serial = match_group('SerialNumber')
|
43
44
|
|
44
|
-
if format_info
|
45
|
-
|
46
|
-
|
47
|
-
elsif format_info[:prepend_if_missing]
|
45
|
+
if format_info && format_info[:prepend_if] && raw_serial.match(Regexp.new(format_info[:prepend_if][:matches_regex]))
|
46
|
+
return "#{format_info[:prepend_if][:content]}#{raw_serial}"
|
47
|
+
# elsif format_info && format_info[:prepend_if_missing]
|
48
48
|
|
49
|
-
end
|
50
49
|
end
|
51
50
|
|
52
|
-
|
51
|
+
raw_serial
|
53
52
|
end
|
54
53
|
|
55
54
|
def check_digit
|
56
|
-
match_group(
|
55
|
+
match_group('CheckDigit')
|
57
56
|
end
|
58
57
|
|
59
58
|
def decode
|
60
59
|
decoded = {}
|
61
|
-
(
|
60
|
+
(matches.try(:names) || []).each do |name|
|
62
61
|
sym = name.underscore.to_sym
|
63
|
-
decoded[sym] =
|
62
|
+
decoded[sym] = matches[name]
|
64
63
|
end
|
65
64
|
|
66
65
|
decoded
|
@@ -70,7 +69,8 @@ module TrackingNumber
|
|
70
69
|
return false unless valid_format?
|
71
70
|
return false unless valid_checksum?
|
72
71
|
return false unless valid_optional_checks?
|
73
|
-
|
72
|
+
|
73
|
+
true
|
74
74
|
end
|
75
75
|
|
76
76
|
def valid_format?
|
@@ -78,7 +78,7 @@ module TrackingNumber
|
|
78
78
|
end
|
79
79
|
|
80
80
|
def valid_optional_checks?
|
81
|
-
additional_check = self.class.const_get(
|
81
|
+
additional_check = self.class.const_get('VALIDATION')[:additional]
|
82
82
|
return true unless additional_check
|
83
83
|
|
84
84
|
exist_checks = (additional_check[:exists] ||= [])
|
@@ -86,8 +86,9 @@ module TrackingNumber
|
|
86
86
|
end
|
87
87
|
|
88
88
|
def valid_checksum?
|
89
|
-
return false unless
|
90
|
-
|
89
|
+
return false unless valid_format?
|
90
|
+
|
91
|
+
checksum_info = self.class.const_get(:VALIDATION)[:checksum]
|
91
92
|
return true unless checksum_info
|
92
93
|
|
93
94
|
name = checksum_info[:name]
|
@@ -97,22 +98,25 @@ module TrackingNumber
|
|
97
98
|
end
|
98
99
|
|
99
100
|
def to_s
|
100
|
-
|
101
|
+
tracking_number
|
101
102
|
end
|
102
103
|
|
103
104
|
def inspect
|
104
|
-
|
105
|
+
format('#<%s:%#0x %s>', self.class.to_s, object_id, tracking_number)
|
105
106
|
end
|
106
107
|
|
107
108
|
def info
|
108
109
|
Info.new({
|
109
|
-
|
110
|
-
|
111
|
-
|
112
|
-
|
113
|
-
|
114
|
-
|
115
|
-
|
110
|
+
courier: courier_info,
|
111
|
+
service_type: service_type,
|
112
|
+
service_description: service_description,
|
113
|
+
destination_zip: destination_zip,
|
114
|
+
shipper_id: shipper_id,
|
115
|
+
package_type: package_type,
|
116
|
+
tracking_url: tracking_url,
|
117
|
+
partners: partners,
|
118
|
+
decode: decode
|
119
|
+
})
|
116
120
|
end
|
117
121
|
|
118
122
|
def courier_code
|
@@ -120,68 +124,88 @@ module TrackingNumber
|
|
120
124
|
end
|
121
125
|
|
122
126
|
def courier_name
|
123
|
-
if matching_additional[
|
124
|
-
matching_additional[
|
125
|
-
|
126
|
-
|
127
|
-
self.class.const_get(:COURIER_INFO)[:name]
|
128
|
-
end
|
127
|
+
if matching_additional['Courier']
|
128
|
+
matching_additional['Courier'][:courier]
|
129
|
+
elsif self.class.constants.include?(:COURIER_INFO)
|
130
|
+
self.class.const_get(:COURIER_INFO)[:name]
|
129
131
|
end
|
130
132
|
end
|
131
133
|
|
132
|
-
|
133
|
-
|
134
|
-
|
134
|
+
alias carrier courier_code # OG tracking_number gem used :carrier.
|
135
|
+
alias carrier_code courier_code
|
136
|
+
alias carrier_name courier_name
|
135
137
|
|
136
138
|
def courier_info
|
137
|
-
basics = {:
|
139
|
+
basics = { name: courier_name, code: courier_code }
|
138
140
|
|
139
|
-
if info = matching_additional[
|
140
|
-
basics.merge!(:
|
141
|
+
if info = matching_additional['Courier']
|
142
|
+
basics.merge!(name: info[:courier], url: info[:courier_url], country: info[:country])
|
141
143
|
end
|
142
144
|
|
143
145
|
@courier ||= Info.new(basics)
|
144
146
|
end
|
145
147
|
|
146
|
-
def
|
147
|
-
|
148
|
-
|
148
|
+
def partnership?
|
149
|
+
partners.present?
|
150
|
+
end
|
151
|
+
|
152
|
+
def shipper?
|
153
|
+
return true unless partnership?
|
154
|
+
|
155
|
+
partners.shipper == self
|
156
|
+
end
|
157
|
+
|
158
|
+
def carrier?
|
159
|
+
return true unless partnership?
|
160
|
+
|
161
|
+
partners.carrier == self
|
162
|
+
end
|
163
|
+
|
164
|
+
def partners
|
165
|
+
return unless self.class.const_defined?(:PARTNERS)
|
166
|
+
|
167
|
+
partner_hash = {}
|
168
|
+
|
169
|
+
return unless (partner_tn = find_matching_partner)
|
170
|
+
|
171
|
+
possible_twin = partner_tn.send(:find_matching_partner)
|
172
|
+
if possible_twin.instance_of?(self.class) && possible_twin.tracking_number == tracking_number
|
173
|
+
partner_hash[partner_data[:partner_type].to_sym] = partner_tn
|
174
|
+
partner_hash[partner_tn.partner_data[:partner_type].to_sym] = self
|
149
175
|
end
|
176
|
+
|
177
|
+
PartnerStruct.new(partner_hash[:shipper], partner_hash[:carrier]) if partner_hash.keys.any?
|
178
|
+
end
|
179
|
+
|
180
|
+
def service_type
|
181
|
+
@service_type ||= Info.new(matching_additional['Service Type']).name if matching_additional['Service Type']
|
150
182
|
end
|
151
183
|
|
152
184
|
def service_description
|
153
|
-
if matching_additional[
|
154
|
-
@service_description ||= Info.new(matching_additional["Service Type"]).description
|
155
|
-
end
|
185
|
+
@service_description ||= Info.new(matching_additional['Service Type']).description if matching_additional['Service Type']
|
156
186
|
end
|
157
187
|
|
158
188
|
def package_type
|
159
|
-
if matching_additional[
|
160
|
-
@package_type ||= Info.new(matching_additional["Container Type"]).name
|
161
|
-
end
|
189
|
+
@package_type ||= Info.new(matching_additional['Container Type']).name if matching_additional['Container Type']
|
162
190
|
end
|
163
191
|
|
164
192
|
def destination_zip
|
165
|
-
match_group(
|
193
|
+
match_group('DestinationZip')
|
166
194
|
end
|
167
195
|
|
168
196
|
def shipper_id
|
169
|
-
match_group(
|
197
|
+
match_group('ShipperId')
|
170
198
|
end
|
171
199
|
|
172
200
|
def tracking_url
|
173
201
|
url = nil
|
174
|
-
if matching_additional[
|
175
|
-
url = matching_additional[
|
176
|
-
|
177
|
-
|
178
|
-
url = self.class.const_get(:TRACKING_URL)
|
179
|
-
end
|
202
|
+
if matching_additional['Courier']
|
203
|
+
url = matching_additional['Courier'][:tracking_url]
|
204
|
+
elsif self.class.const_defined?(:TRACKING_URL)
|
205
|
+
url = self.class.const_get(:TRACKING_URL)
|
180
206
|
end
|
181
207
|
|
182
|
-
if url
|
183
|
-
url.sub('%s', self.tracking_number)
|
184
|
-
end
|
208
|
+
url.sub('%s', tracking_number) if url
|
185
209
|
end
|
186
210
|
|
187
211
|
def matching_additional
|
@@ -189,21 +213,21 @@ module TrackingNumber
|
|
189
213
|
|
190
214
|
relevant_sections = {}
|
191
215
|
|
192
|
-
additional.each do |
|
193
|
-
|
194
|
-
|
195
|
-
|
196
|
-
|
197
|
-
|
198
|
-
|
199
|
-
|
200
|
-
|
201
|
-
|
202
|
-
end
|
203
|
-
|
204
|
-
relevant_sections[info[:name]] = matches
|
216
|
+
additional.each do |additional_info|
|
217
|
+
next unless matches && matches.length > 0 # skip if no match groups
|
218
|
+
value = matches[additional_info[:regex_group_name]].gsub(/\s/, '') # match is empty
|
219
|
+
next unless value
|
220
|
+
|
221
|
+
matches = additional_info[:lookup].find do |i|
|
222
|
+
if i[:matches]
|
223
|
+
value == i[:matches]
|
224
|
+
elsif i[:matches_regex]
|
225
|
+
value =~ Regexp.new(i[:matches_regex])
|
205
226
|
end
|
206
227
|
end
|
228
|
+
|
229
|
+
# has matching value
|
230
|
+
relevant_sections[additional_info[:name]] = matches
|
207
231
|
end
|
208
232
|
|
209
233
|
relevant_sections
|
@@ -211,21 +235,78 @@ module TrackingNumber
|
|
211
235
|
|
212
236
|
protected
|
213
237
|
|
214
|
-
def
|
215
|
-
|
216
|
-
|
217
|
-
|
218
|
-
[]
|
238
|
+
def match_all(items)
|
239
|
+
items.all? do |i|
|
240
|
+
if i[:matches] && matches[i[:regex_group_name]]
|
241
|
+
matches[i[:regex_group_name]] == i[:matches]
|
242
|
+
elsif i[:matches_regex] && matches[i[:regex_group_name]]
|
243
|
+
matches[i[:regex_group_name]] =~ Regexp.new(i[:matches_regex])
|
244
|
+
else
|
245
|
+
false
|
246
|
+
end
|
219
247
|
end
|
220
248
|
end
|
221
249
|
|
250
|
+
def match_any(items)
|
251
|
+
items.find do |i|
|
252
|
+
if i[:matches] && matches[i[:regex_group_name]]
|
253
|
+
matches[i[:regex_group_name]] == i[:matches]
|
254
|
+
elsif i[:matches_regex] && matches[i[:regex_group_name]]
|
255
|
+
matches[i[:regex_group_name]] =~ Regexp.new(i[:matches_regex])
|
256
|
+
else
|
257
|
+
false
|
258
|
+
end
|
259
|
+
end
|
260
|
+
end
|
261
|
+
|
262
|
+
def matches
|
263
|
+
@matches ||= if self.class.constants.include?(:VERIFY_PATTERN)
|
264
|
+
tracking_number.match(self.class.const_get(:VERIFY_PATTERN))
|
265
|
+
else
|
266
|
+
[]
|
267
|
+
end
|
268
|
+
end
|
269
|
+
|
222
270
|
def match_group(name)
|
223
|
-
|
224
|
-
|
225
|
-
|
226
|
-
|
271
|
+
matches[name].gsub(/\s/, '')
|
272
|
+
rescue StandardError
|
273
|
+
nil
|
274
|
+
end
|
275
|
+
|
276
|
+
def find_matching_partner
|
277
|
+
partner_info = self.class.const_get(:PARTNERS) || []
|
278
|
+
|
279
|
+
partner_info.each do |partner_data|
|
280
|
+
klass = find_tracking_class_by_id(partner_data[:partner_id])
|
281
|
+
|
282
|
+
return false unless klass
|
283
|
+
|
284
|
+
tn = klass.new(tracking_number)
|
285
|
+
|
286
|
+
valid = if partner_data.dig(:validation, :matches_all)
|
287
|
+
tn.valid? && match_all(partner_data.dig(:validation, :matches_all))
|
288
|
+
elsif partner_data.dig(:validation, :matches_any)
|
289
|
+
tn.valid? && match_any(partner_data.dig(:validation, :matches_any))
|
290
|
+
else
|
291
|
+
tn.valid?
|
292
|
+
end
|
293
|
+
|
294
|
+
next unless valid
|
295
|
+
|
296
|
+
@partner = tn
|
297
|
+
@partner_data = partner_data
|
298
|
+
break
|
227
299
|
end
|
300
|
+
|
301
|
+
return @partner
|
228
302
|
end
|
229
303
|
|
304
|
+
def find_tracking_class_by_id(id)
|
305
|
+
return unless id
|
306
|
+
|
307
|
+
TrackingNumber::TYPES.detect do |type|
|
308
|
+
type.const_get('ID') == id
|
309
|
+
end
|
310
|
+
end
|
230
311
|
end
|
231
312
|
end
|
@@ -1,22 +1,21 @@
|
|
1
|
-
|
2
1
|
module TrackingNumber
|
3
2
|
module Loader
|
4
3
|
class << self
|
5
|
-
def load_tracking_number_data(couriers_path = File.join(File.dirname(__FILE__),
|
4
|
+
def load_tracking_number_data(couriers_path = File.join(File.dirname(__FILE__), '../data/couriers/'))
|
6
5
|
mapping_data = []
|
7
6
|
tracking_number_types = []
|
8
7
|
Dir.glob(File.join(couriers_path, '/*.json')).each do |file|
|
9
|
-
courier_info
|
8
|
+
courier_info = read_courier_info(file)
|
10
9
|
|
11
10
|
courier_info[:tracking_numbers].each do |tracking_info|
|
12
|
-
|
13
|
-
klass = create_class(klass, courier_info, tracking_info)
|
11
|
+
klass = create_class(courier_info, tracking_info)
|
14
12
|
|
15
13
|
# Do some basic checks on the data file
|
16
14
|
throw 'missing test numbers' unless has_test_numbers?(tracking_info)
|
17
|
-
throw 'missing regex match groups' unless test_numbers_return_required_groups?(tracking_info,
|
15
|
+
throw 'missing regex match groups' unless test_numbers_return_required_groups?(tracking_info,
|
16
|
+
Regexp.new(klass::VERIFY_PATTERN))
|
17
|
+
const = register_class(klass, tracking_info[:name])
|
18
18
|
|
19
|
-
const = register_class(klass, tracking_name)
|
20
19
|
tracking_number_types.push(const)
|
21
20
|
mapping_data << {
|
22
21
|
class: const,
|
@@ -28,33 +27,33 @@ module TrackingNumber
|
|
28
27
|
end
|
29
28
|
|
30
29
|
TrackingNumber.const_set('TYPES', tracking_number_types)
|
31
|
-
|
32
|
-
|
30
|
+
|
31
|
+
mapping_data
|
33
32
|
end
|
34
33
|
|
35
34
|
private
|
36
35
|
|
37
36
|
def has_test_numbers?(tracking)
|
38
|
-
|
37
|
+
tracking[:test_numbers] && tracking[:test_numbers][:valid]
|
39
38
|
end
|
40
39
|
|
41
40
|
def test_numbers_return_required_groups?(tracking, regex)
|
42
41
|
test_number = tracking[:test_numbers][:valid][0]
|
43
42
|
matches = test_number.match(regex)
|
44
43
|
|
45
|
-
|
44
|
+
matches['SerialNumber']
|
46
45
|
end
|
47
46
|
|
48
47
|
def read_courier_info(file)
|
49
|
-
|
48
|
+
JSON.parse(File.read(file)).deep_symbolize_keys!
|
50
49
|
end
|
51
50
|
|
52
|
-
def create_class(
|
51
|
+
def create_class(courier_info, tracking_info)
|
53
52
|
klass = Class.new(TrackingNumber::Base)
|
54
|
-
klass.const_set(
|
53
|
+
klass.const_set('COURIER_CODE', courier_info[:courier_code])
|
55
54
|
info = courier_info.dup
|
56
55
|
info.delete(:tracking_numbers)
|
57
|
-
klass.const_set(
|
56
|
+
klass.const_set('COURIER_INFO', info)
|
58
57
|
|
59
58
|
pattern = tracking_info[:regex]
|
60
59
|
pattern = tracking_info[:regex].join if tracking_info[:regex].is_a?(Array)
|
@@ -62,19 +61,21 @@ module TrackingNumber
|
|
62
61
|
verify_pattern = "^#{pattern}$"
|
63
62
|
search_pattern = "\\b#{pattern}\\b"
|
64
63
|
|
65
|
-
klass.const_set(
|
66
|
-
klass.const_set(
|
64
|
+
klass.const_set('SEARCH_PATTERN', Regexp.new(search_pattern))
|
65
|
+
klass.const_set('VERIFY_PATTERN', Regexp.new(verify_pattern))
|
67
66
|
|
68
|
-
klass.const_set(
|
69
|
-
klass.const_set(
|
70
|
-
klass.const_set(
|
67
|
+
klass.const_set('VALIDATION', tracking_info[:validation])
|
68
|
+
klass.const_set('ADDITIONAL', tracking_info[:additional])
|
69
|
+
klass.const_set('TRACKING_URL', tracking_info[:tracking_url])
|
70
|
+
klass.const_set('PARTNERS', tracking_info[:partners])
|
71
|
+
klass.const_set('ID', tracking_info[:id])
|
71
72
|
|
72
|
-
|
73
|
+
klass
|
73
74
|
end
|
74
75
|
|
75
76
|
def register_class(klass, tracking_name)
|
76
77
|
klass_name = tracking_name.gsub(/[^0-9A-Za-z]/, '')
|
77
|
-
|
78
|
+
TrackingNumber.const_set(klass_name, klass)
|
78
79
|
end
|
79
80
|
end
|
80
81
|
end
|
data/lib/tracking_number.rb
CHANGED
@@ -5,6 +5,7 @@ require 'tracking_number/checksum_validations'
|
|
5
5
|
require 'tracking_number/loader'
|
6
6
|
require 'tracking_number/base'
|
7
7
|
require 'tracking_number/info'
|
8
|
+
require 'tracking_number/partnership'
|
8
9
|
require 'tracking_number/unknown'
|
9
10
|
require 'active_support/core_ext/string'
|
10
11
|
require 'active_support/core_ext/hash'
|
@@ -16,29 +17,51 @@ end
|
|
16
17
|
TrackingNumber::Loader.load_tracking_number_data
|
17
18
|
|
18
19
|
module TrackingNumber
|
19
|
-
def self.search(body)
|
20
|
-
TYPES.collect { |type| type.search(body) }.flatten
|
20
|
+
def self.search(body, match: :carrier)
|
21
|
+
matches = TYPES.collect { |type| type.search(body) }.flatten
|
22
|
+
|
23
|
+
# Some tracking numbers (e.g. Fedex Smartpost) are partnerships between two parties, where one party is the shipper (e.g. Fedex)
|
24
|
+
# and the other party is the [last mile] carrier (e.g. USPS). We're probably interested in the last mile aspect of
|
25
|
+
# the partnership, so by default we'll show those
|
26
|
+
|
27
|
+
# Tracking numbers without a partnership are both the shipper and carrier.
|
28
|
+
|
29
|
+
case match
|
30
|
+
when :carrier
|
31
|
+
matches.filter(&:carrier?)
|
32
|
+
when :shipper
|
33
|
+
matches.filter(&:shipper?)
|
34
|
+
when :all
|
35
|
+
matches
|
36
|
+
else
|
37
|
+
matches
|
38
|
+
end
|
21
39
|
end
|
22
40
|
|
23
|
-
def self.detect(tracking_number)
|
41
|
+
def self.detect(tracking_number, match: :carrier)
|
24
42
|
tn = nil
|
25
|
-
|
43
|
+
(TYPES + [Unknown]).each do |test_klass|
|
26
44
|
tn = test_klass.new(tracking_number)
|
27
|
-
|
45
|
+
if tn.valid? && (!match || match == :all)
|
46
|
+
break
|
47
|
+
elsif tn.valid? && tn.send("#{match}?")
|
48
|
+
break
|
49
|
+
end
|
28
50
|
end
|
29
|
-
|
51
|
+
tn
|
30
52
|
end
|
31
53
|
|
32
54
|
def self.detect_all(tracking_number)
|
33
55
|
matches = []
|
34
|
-
|
56
|
+
(TYPES + [Unknown]).each do |test_klass|
|
35
57
|
tn = test_klass.new(tracking_number)
|
36
58
|
matches << tn if tn.valid?
|
37
59
|
end
|
38
|
-
|
60
|
+
|
61
|
+
matches
|
39
62
|
end
|
40
63
|
|
41
64
|
def self.new(tracking_number)
|
42
|
-
|
65
|
+
detect(tracking_number)
|
43
66
|
end
|
44
67
|
end
|
data/test/test_helper.rb
CHANGED
@@ -18,8 +18,6 @@ class Minitest::Test
|
|
18
18
|
possible_numbers = []
|
19
19
|
possible_numbers << tracking
|
20
20
|
possible_numbers << tracking.to_s.gsub(" ", "")
|
21
|
-
possible_numbers << tracking.chars.to_a.join(" ")
|
22
|
-
possible_numbers << tracking.chars.to_a.join(" ")
|
23
21
|
possible_numbers << tracking.slice(0, (tracking.length / 2)) + " " + tracking.slice((tracking.length / 2), tracking.length)
|
24
22
|
|
25
23
|
possible_numbers.flatten.uniq
|
@@ -23,7 +23,7 @@ class TrackingNumberMetaTest < Minitest::Test
|
|
23
23
|
tracking_info[:test_numbers][:valid].each do |valid_number|
|
24
24
|
should "detect #{valid_number} as #{klass_name}" do
|
25
25
|
#TODO fix this multiple matching thing
|
26
|
-
matches = TrackingNumber.search(valid_number)
|
26
|
+
matches = TrackingNumber.search(valid_number, match: :all)
|
27
27
|
assert matches.collect(&:class).include?(klass)
|
28
28
|
end
|
29
29
|
|
@@ -147,9 +147,77 @@ class TrackingNumberTest < Minitest::Test
|
|
147
147
|
assert_nil tracking_number.package_type
|
148
148
|
end
|
149
149
|
|
150
|
+
should "report no partnership" do
|
151
|
+
assert_equal false, tracking_number.partnership?
|
152
|
+
end
|
153
|
+
|
154
|
+
should "report no partners" do
|
155
|
+
assert_equal nil, tracking_number.partners
|
156
|
+
end
|
157
|
+
|
158
|
+
should "report as shipper and carrier" do
|
159
|
+
assert_equal true, tracking_number.shipper?
|
160
|
+
assert_equal true, tracking_number.carrier?
|
161
|
+
end
|
162
|
+
|
150
163
|
should "have valid tracking url" do
|
151
164
|
assert tracking_number.tracking_url, "Tracking url should not be blank"
|
152
165
|
assert tracking_number.tracking_url.include?(tracking_number.tracking_number), "Should include tracking number in the url"
|
153
166
|
end
|
154
167
|
end
|
168
|
+
|
169
|
+
context "tracking number partnership data for FedExSmartPost/USPS91" do
|
170
|
+
tracking_number = TrackingNumber.new("420 11213 92 6129098349792366623 8")
|
171
|
+
|
172
|
+
should "report correct courier name" do
|
173
|
+
assert_equal "United States Postal Service", tracking_number.courier_name
|
174
|
+
end
|
175
|
+
|
176
|
+
should "report correct courier code" do
|
177
|
+
assert_equal :usps, tracking_number.courier_code
|
178
|
+
end
|
179
|
+
|
180
|
+
should "report correct service type" do
|
181
|
+
assert_equal "Fedex Smart Post", tracking_number.service_type
|
182
|
+
end
|
183
|
+
|
184
|
+
should "report partnership" do
|
185
|
+
assert_equal true, tracking_number.partnership?
|
186
|
+
end
|
187
|
+
|
188
|
+
should "report not shipper side of the partnership" do
|
189
|
+
assert_equal false, tracking_number.shipper?
|
190
|
+
end
|
191
|
+
|
192
|
+
should "report carrier side of the partnership" do
|
193
|
+
assert_equal true, tracking_number.carrier?
|
194
|
+
end
|
195
|
+
|
196
|
+
should "report partner pairing" do
|
197
|
+
assert_equal :fedex, tracking_number.partners.shipper.courier_code
|
198
|
+
end
|
199
|
+
end
|
200
|
+
|
201
|
+
context "searching numbers that have partners" do
|
202
|
+
partnership_number = "420 11213 92 6129098349792366623 8"
|
203
|
+
single_number = "0307 1790 0005 2348 3741"
|
204
|
+
|
205
|
+
search_string = ["number that matches two services", partnership_number, " number that matches only one: ", single_number, "let's see if that does it"].join(' ')
|
206
|
+
|
207
|
+
should "match only carriers by default" do
|
208
|
+
matches = TrackingNumber.search(search_string)
|
209
|
+
assert_equal 2, matches.size
|
210
|
+
assert_equal [true, true], matches.collect { |t| t.carrier? }
|
211
|
+
end
|
212
|
+
|
213
|
+
should "match all if specified" do
|
214
|
+
matches = TrackingNumber.search(search_string, match: :all)
|
215
|
+
assert_equal 3, matches.size
|
216
|
+
end
|
217
|
+
|
218
|
+
should "match only shippers if specified" do
|
219
|
+
matches = TrackingNumber.search(search_string, match: :shipper)
|
220
|
+
assert_equal 2, matches.size
|
221
|
+
end
|
222
|
+
end
|
155
223
|
end
|