tracking_number 1.3.2 → 1.4.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- 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
|