tracking_number 2.3.0 → 2.5.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/FUNDING.yml +2 -0
- data/CHANGELOG.md +14 -0
- data/README.md +3 -9
- data/lib/data/couriers/canadapost.json +3 -1
- data/lib/data/couriers/canpar.json +29 -0
- data/lib/data/couriers/dhl.json +77 -1
- data/lib/data/couriers/dpd.json +6 -2
- data/lib/data/couriers/fedex.json +31 -11
- data/lib/data/couriers/gofo.json +24 -0
- data/lib/data/couriers/lasership.json +21 -0
- data/lib/data/couriers/old_dominion.json +4 -2
- data/lib/data/couriers/ontrac.json +6 -2
- data/lib/data/couriers/purolator.json +50 -0
- data/lib/data/couriers/s10.json +8 -1
- data/lib/data/couriers/speedee.json +26 -0
- data/lib/data/couriers/ups.json +6 -2
- data/lib/data/couriers/usps.json +112 -4
- data/lib/data/couriers/yunexpress.json +25 -0
- data/lib/tracking_number/base.rb +17 -15
- data/lib/tracking_number/version.rb +1 -1
- data/package.json +1 -25
- metadata +8 -2
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 75fcefe87240b40ddf1bd16c155f13f93f46b9cbe96b343fb9b480875dd82ef6
|
|
4
|
+
data.tar.gz: f7ff78fe0acdc1a65ae6ddc63fe6e975ee4481c05e8d934b5fc8f09b1d2990ae
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: e4d70982f20080077fff3be398bc0f99baabd1d67c8a579e2253735a532dbfe0d1bc38edb91df9e28f873ce1683ba26177a447c26d0503fa1c5fc2047f156a0e
|
|
7
|
+
data.tar.gz: 1b22b16347f3ed186f7f539dc4bfc1a32ff07719faaf1cebc5a0983ced2139db4436303ab3405d4ad9b62081bdd0b632e769dc3822a1f7a5d47709e2832ca8f1
|
data/.github/FUNDING.yml
ADDED
data/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,19 @@
|
|
|
1
1
|
tracking_number changelog
|
|
2
2
|
|
|
3
|
+
# [2.5.0](https://github.com/jkeen/tracking_number/compare/v2.4.0...v2.5.0) (2026-08-13)
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
### Features
|
|
7
|
+
|
|
8
|
+
* update to tracking_number_data 1.12, which includes support for Canpar, Purolator, Spee-Dee, YunExpress, and an updated FedEx matcher ([ca67edf](https://github.com/jkeen/tracking_number/commit/ca67edf0eaeab33171008bf115e96bdcf55c0d4b))
|
|
9
|
+
|
|
10
|
+
# [2.4.0](https://github.com/jkeen/tracking_number/compare/v2.3.0...v2.4.0) (2025-08-21)
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
### Features
|
|
14
|
+
|
|
15
|
+
* update to tracking_number_data 1.9.0 ([f885bfd](https://github.com/jkeen/tracking_number/commit/f885bfd956ffacb22f38967754ec14551bca8d50))
|
|
16
|
+
|
|
3
17
|
# [2.3.0](https://github.com/jkeen/tracking_number/compare/v2.2.0...v2.3.0) (2025-08-21)
|
|
4
18
|
|
|
5
19
|
|
data/README.md
CHANGED
|
@@ -2,21 +2,15 @@
|
|
|
2
2
|
[](https://badge.fury.io/rb/tracking_number)
|
|
3
3
|
[]()
|
|
4
4
|
|
|
5
|
-
|
|
6
|
-
>
|
|
7
|
-
> <a href="https://www.buymeacoffee.com/jeffkeen" target="_blank"><img src="https://cdn.buymeacoffee.com/buttons/v2/default-yellow.png" alt="Buy Me A Coffee" style="height: auto !important;width: 150px !important;" ></a>
|
|
8
|
-
|
|
9
|
-
## Tracking Number (v1.x)
|
|
5
|
+
## Tracking Number
|
|
10
6
|
|
|
11
7
|
This gem identifies valid tracking numbers and can tell you a little bit about the shipment just from the number—there's quite a bit of info tucked away into those numbers, it turns out.
|
|
12
8
|
|
|
13
|
-
It detects tracking numbers from UPS, FedEx,
|
|
9
|
+
It detects tracking numbers from USPS, UPS, FedEx, Amazon, DHL, OnTrac, LaserShip, Canada Post, DPD, Purolator, Old Dominion Freight Line, Spee-Dee Delivery, Canpar, YunExpress, Landmark Global, GOFO Express, and any carrier using the S10 international standard.
|
|
14
10
|
|
|
15
11
|
This gem does not do tracking. That is left up to you.
|
|
16
12
|
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
Starting with the 1.0 release the specifications for detecting tracking numbers have been moved into a separate repository ([tracking_number_data](http://github.com/jkeen/tracking_number_data)) that this gem relies on. I did this so a) we can have a single place to document all tracking number types and it can be more of a crowdsourced effort, and b) so clients can be written in other languages easier.
|
|
13
|
+
All the matching data is kept in [tracking_number_data](https://github.com/jkeen/tracking_number_data) and displayed in full on [trackingnumber.fyi](https://trackingnumber.fyi).
|
|
20
14
|
|
|
21
15
|
## Usage
|
|
22
16
|
|
|
@@ -9,8 +9,10 @@
|
|
|
9
9
|
"validation": {
|
|
10
10
|
"checksum": {
|
|
11
11
|
"name": "mod10",
|
|
12
|
+
"modulo": 10,
|
|
12
13
|
"evens_multiplier": 3,
|
|
13
|
-
"odds_multiplier": 1
|
|
14
|
+
"odds_multiplier": 1,
|
|
15
|
+
"reverse": false
|
|
14
16
|
}
|
|
15
17
|
},
|
|
16
18
|
"tracking_url": "https://www.canadapost-postescanada.ca/track-reperage/en#/search?searchFor=%s",
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "Canpar",
|
|
3
|
+
"courier_code": "canpar",
|
|
4
|
+
"tracking_numbers": [
|
|
5
|
+
{
|
|
6
|
+
"name": "Canpar (22)",
|
|
7
|
+
"id": "canpar_22",
|
|
8
|
+
"regex": "\\s*(?<SerialNumber>[CDKLSUXZ]\\s*([0-9]\\s*){21})\\s*",
|
|
9
|
+
"validation": {},
|
|
10
|
+
"tracking_url": "https://www.canpar.com/en/track/tracking.jsp?reference=%s&locale=en",
|
|
11
|
+
"test_numbers": {
|
|
12
|
+
"valid": [
|
|
13
|
+
"D576002440000001718010",
|
|
14
|
+
"D576002440000001428004",
|
|
15
|
+
"D576002440000002496001",
|
|
16
|
+
"L576002440000000001004",
|
|
17
|
+
"L576002440000000004001",
|
|
18
|
+
"S576002440000000004001",
|
|
19
|
+
"S576002440000000025001"
|
|
20
|
+
],
|
|
21
|
+
"invalid": [
|
|
22
|
+
"1576002440000002510003",
|
|
23
|
+
"D57600244000000251000",
|
|
24
|
+
"M576002440000002510003"
|
|
25
|
+
]
|
|
26
|
+
}
|
|
27
|
+
}
|
|
28
|
+
]
|
|
29
|
+
}
|
data/lib/data/couriers/dhl.json
CHANGED
|
@@ -8,7 +8,8 @@
|
|
|
8
8
|
"regex": "\\s*(?<SerialNumber>([0-9]\\s*){9,10})(?<CheckDigit>([0-9]\\s*))",
|
|
9
9
|
"validation": {
|
|
10
10
|
"checksum": {
|
|
11
|
-
"name": "mod7"
|
|
11
|
+
"name": "mod7",
|
|
12
|
+
"modulo": 7
|
|
12
13
|
}
|
|
13
14
|
},
|
|
14
15
|
"tracking_url": "http://www.dhl.com/en/express/tracking.html?brand=DHL&AWB=%s",
|
|
@@ -89,6 +90,81 @@
|
|
|
89
90
|
"GR295117494011169045"
|
|
90
91
|
]
|
|
91
92
|
}
|
|
93
|
+
},
|
|
94
|
+
{
|
|
95
|
+
"name": "DHL E-Commerce (30)",
|
|
96
|
+
"id": "dhl_ecommerce_30",
|
|
97
|
+
"regex": [
|
|
98
|
+
"\\s*(?<RoutingApplicationId>4\\s*2\\s*0\\s*)(?<DestinationZip>([0-9]\\s*){5})",
|
|
99
|
+
"(?!\\s*9\\s*[1-5]\\b)",
|
|
100
|
+
"(?<SerialNumber>",
|
|
101
|
+
"(?<ServiceType>(9[2-5]\\s*))",
|
|
102
|
+
"(?<ShipperId>([0-9]\\s*){9})",
|
|
103
|
+
"(?<PackageId>([0-9]\\s*){10})",
|
|
104
|
+
")",
|
|
105
|
+
"(?<CheckDigit>[0-9]\\s*)"
|
|
106
|
+
],
|
|
107
|
+
"validation": {
|
|
108
|
+
"checksum": {
|
|
109
|
+
"name": "mod10",
|
|
110
|
+
"modulo": 10,
|
|
111
|
+
"evens_multiplier": 3,
|
|
112
|
+
"odds_multiplier": 1,
|
|
113
|
+
"reverse": true
|
|
114
|
+
}
|
|
115
|
+
},
|
|
116
|
+
"tracking_url": "https://ecommerceportal.dhl.com/track?trackingnumber=%s",
|
|
117
|
+
"partners": [{
|
|
118
|
+
"description": "DHL eCommerce hands off to USPS for the last mile",
|
|
119
|
+
"partner_type": "carrier",
|
|
120
|
+
"partner_id": "usps_22",
|
|
121
|
+
"validation": {
|
|
122
|
+
"matches_all": [
|
|
123
|
+
{ "regex_group_name": "RoutingApplicationId", "matches": "420" },
|
|
124
|
+
{ "regex_group_name": "ServiceType", "matches_regex": "9[2345]" }
|
|
125
|
+
]
|
|
126
|
+
}
|
|
127
|
+
},
|
|
128
|
+
{
|
|
129
|
+
"description": "DHL eCommerce hands off to USPS for the last mile",
|
|
130
|
+
"partner_type": "carrier",
|
|
131
|
+
"partner_id": "usps_91",
|
|
132
|
+
"validation": {
|
|
133
|
+
"matches_all": [
|
|
134
|
+
{ "regex_group_name": "RoutingApplicationId", "matches": "420" },
|
|
135
|
+
{ "regex_group_name": "ServiceType", "matches_regex": "9[2345]" }
|
|
136
|
+
]
|
|
137
|
+
}
|
|
138
|
+
}],
|
|
139
|
+
"additional": [
|
|
140
|
+
{
|
|
141
|
+
"name": "Service Type",
|
|
142
|
+
"regex_group_name": "ServiceType",
|
|
143
|
+
"lookup": [
|
|
144
|
+
{ "matches": "92", "name": "USPS Tracking (Commercial)" },
|
|
145
|
+
{ "matches": "93", "name": "Collect on Delivery (COD)" },
|
|
146
|
+
{ "matches": "94", "name": "USPS Tracking" },
|
|
147
|
+
{ "matches": "95", "name": "Certified Mail" }
|
|
148
|
+
]
|
|
149
|
+
}
|
|
150
|
+
],
|
|
151
|
+
"test_numbers": {
|
|
152
|
+
"valid": [
|
|
153
|
+
"420902459261290336128704042634",
|
|
154
|
+
"420941179261290336128704062441",
|
|
155
|
+
"420926029261290336128704067248",
|
|
156
|
+
"420902729261290336128704124163",
|
|
157
|
+
"420950549261290336128704133837",
|
|
158
|
+
"420193809261290336128704280838",
|
|
159
|
+
"420900469261290336128704216936"
|
|
160
|
+
],
|
|
161
|
+
"invalid": [
|
|
162
|
+
"420941179261290336128704062442",
|
|
163
|
+
"420941179261290336128704062400",
|
|
164
|
+
"420941179261290336128704062422"
|
|
165
|
+
]
|
|
166
|
+
}
|
|
92
167
|
}
|
|
168
|
+
|
|
93
169
|
]
|
|
94
170
|
}
|
data/lib/data/couriers/dpd.json
CHANGED
|
@@ -17,7 +17,9 @@
|
|
|
17
17
|
],
|
|
18
18
|
"validation": {
|
|
19
19
|
"checksum": {
|
|
20
|
-
"name": "mod_37_36"
|
|
20
|
+
"name": "mod_37_36",
|
|
21
|
+
"modulo": 36,
|
|
22
|
+
"alphabet": "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ"
|
|
21
23
|
}
|
|
22
24
|
},
|
|
23
25
|
"tracking_url": "https://www.dpdgroup.com/nl/mydpd/my-parcels/track?lang=en&parcelNumber=%s",
|
|
@@ -323,7 +325,9 @@
|
|
|
323
325
|
],
|
|
324
326
|
"validation": {
|
|
325
327
|
"checksum": {
|
|
326
|
-
"name": "mod_37_36"
|
|
328
|
+
"name": "mod_37_36",
|
|
329
|
+
"modulo": 36,
|
|
330
|
+
"alphabet": "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ"
|
|
327
331
|
}
|
|
328
332
|
},
|
|
329
333
|
"tracking_url": "https://www.dpdgroup.com/nl/mydpd/my-parcels/track?lang=en&parcelNumber=%s",
|
|
@@ -45,8 +45,7 @@
|
|
|
45
45
|
"name": "FedEx Express (34)",
|
|
46
46
|
"id": "fedex_34",
|
|
47
47
|
"regex": [
|
|
48
|
-
"\\s*
|
|
49
|
-
"([0-9]\\s*){10}",
|
|
48
|
+
"\\s*[0-8]\\s*([0-9]\\s*){14}",
|
|
50
49
|
"(?<DestinationZip>([0-9]\\s*){5})",
|
|
51
50
|
"(?<SerialNumber>([0-9]\\s*){13})",
|
|
52
51
|
"(?<CheckDigit>[0-9]\\s*)"
|
|
@@ -81,7 +80,11 @@
|
|
|
81
80
|
"1001901781990001000300617767839437",
|
|
82
81
|
" 1 0 0 1 9 0 1 7 8 1 9 9 0 0 0 1 0 0 0 3 0 0 6 1 7 7 6 7 8 3 9 4 3 7 ",
|
|
83
82
|
"1002297871540001000300790695517286",
|
|
84
|
-
"1027590111820004833500785458233610"
|
|
83
|
+
"1027590111820004833500785458233610",
|
|
84
|
+
"1195266473930007521100871079919940",
|
|
85
|
+
"1 1 9 52 6 64739 3000 75211 00 8710 799 199 40",
|
|
86
|
+
"1195271874060007521100871140316925",
|
|
87
|
+
"1195583874210007521100871140316936"
|
|
85
88
|
],
|
|
86
89
|
"invalid": [
|
|
87
90
|
"1001901781990001000300617767839438"
|
|
@@ -92,13 +95,16 @@
|
|
|
92
95
|
"name": "FedEx SmartPost",
|
|
93
96
|
"id": "fedex_smartpost",
|
|
94
97
|
"description": "Shipped by FedEx, Delivered by USPS",
|
|
98
|
+
"glossary": {
|
|
99
|
+
"ApplicationIdentifier": { "description": "Always 92 here, the code for a parcel handed to USPS for delivery." }
|
|
100
|
+
},
|
|
95
101
|
"regex": [
|
|
96
102
|
"\\s*(?:",
|
|
97
103
|
"(?:(?<RoutingApplicationId>4\\s*2\\s*0\\s*)(?<DestinationZip>([0-9]\\s*){5}))?",
|
|
98
104
|
"(?<ApplicationIdentifier>9\\s*2\\s*)",
|
|
99
105
|
")?",
|
|
100
106
|
"(?<SerialNumber>",
|
|
101
|
-
"(?<SCNC>
|
|
107
|
+
"(?<SCNC>6\\s*1\\s*)",
|
|
102
108
|
"(?<ServiceType>2\\s*9\\s*)",
|
|
103
109
|
"(?<ShipperId>([0-9]\\s*){8})",
|
|
104
110
|
"(?<PackageId>([0-9]\\s*){11}|([0-9]\\s*){7})",
|
|
@@ -125,8 +131,10 @@
|
|
|
125
131
|
"validation": {
|
|
126
132
|
"checksum": {
|
|
127
133
|
"name": "mod10",
|
|
134
|
+
"modulo": 10,
|
|
128
135
|
"evens_multiplier": 3,
|
|
129
|
-
"odds_multiplier": 1
|
|
136
|
+
"odds_multiplier": 1,
|
|
137
|
+
"reverse": false
|
|
130
138
|
},
|
|
131
139
|
"serial_number_format": {
|
|
132
140
|
"prepend_if": {
|
|
@@ -138,11 +146,9 @@
|
|
|
138
146
|
"tracking_url": "https://www.fedex.com/apps/fedextrack/?tracknumbers=%s",
|
|
139
147
|
"test_numbers": {
|
|
140
148
|
"valid": [
|
|
141
|
-
"61299998820821171811",
|
|
142
149
|
"9261292700768711948021",
|
|
143
150
|
"420 11213 92 6129098349792366623 8",
|
|
144
|
-
"92 6129098349792366623 8"
|
|
145
|
-
"6129098349792366623 8"
|
|
151
|
+
"92 6129098349792366623 8"
|
|
146
152
|
],
|
|
147
153
|
"invalid": [
|
|
148
154
|
"9261292700768711948020",
|
|
@@ -161,8 +167,10 @@
|
|
|
161
167
|
"validation": {
|
|
162
168
|
"checksum": {
|
|
163
169
|
"name": "mod10",
|
|
170
|
+
"modulo": 10,
|
|
164
171
|
"evens_multiplier": 1,
|
|
165
|
-
"odds_multiplier": 3
|
|
172
|
+
"odds_multiplier": 3,
|
|
173
|
+
"reverse": false
|
|
166
174
|
}
|
|
167
175
|
},
|
|
168
176
|
"tracking_url": "https://www.fedex.com/apps/fedextrack/?tracknumbers=%s",
|
|
@@ -186,8 +194,10 @@
|
|
|
186
194
|
"validation": {
|
|
187
195
|
"checksum": {
|
|
188
196
|
"name": "mod10",
|
|
197
|
+
"modulo": 10,
|
|
189
198
|
"evens_multiplier": 3,
|
|
190
|
-
"odds_multiplier": 1
|
|
199
|
+
"odds_multiplier": 1,
|
|
200
|
+
"reverse": false
|
|
191
201
|
}
|
|
192
202
|
},
|
|
193
203
|
"test_numbers": {
|
|
@@ -227,6 +237,10 @@
|
|
|
227
237
|
{
|
|
228
238
|
"name": "FedEx Ground 96 (22)",
|
|
229
239
|
"id": "fedex_ground_96",
|
|
240
|
+
"glossary": {
|
|
241
|
+
"ApplicationIdentifier": { "description": "Always 96, marking a ground shipping label." },
|
|
242
|
+
"SCNC": { "description": "FedEx Ground uses 11, 12 and 13." }
|
|
243
|
+
},
|
|
230
244
|
"regex": [
|
|
231
245
|
"\\s*(?<ApplicationIdentifier>9\\s*6\\s*)",
|
|
232
246
|
"(?<SCNC>([0-9]\\s*){2})",
|
|
@@ -237,8 +251,10 @@
|
|
|
237
251
|
"validation": {
|
|
238
252
|
"checksum": {
|
|
239
253
|
"name": "mod10",
|
|
254
|
+
"modulo": 10,
|
|
240
255
|
"evens_multiplier": 1,
|
|
241
|
-
"odds_multiplier": 3
|
|
256
|
+
"odds_multiplier": 3,
|
|
257
|
+
"reverse": false
|
|
242
258
|
}
|
|
243
259
|
},
|
|
244
260
|
"tracking_url": "https://www.fedex.com/apps/fedextrack/?tracknumbers=%s",
|
|
@@ -255,6 +271,10 @@
|
|
|
255
271
|
{
|
|
256
272
|
"name": "FedEx Ground GSN",
|
|
257
273
|
"id": "fedex_ground_gsn",
|
|
274
|
+
"glossary": {
|
|
275
|
+
"ApplicationIdentifier": { "description": "Always 96, marking a ground shipping label." },
|
|
276
|
+
"SCNC": { "description": "FedEx Ground uses 11, 12 and 13." }
|
|
277
|
+
},
|
|
258
278
|
"regex": [
|
|
259
279
|
"\\s*(?<ApplicationIdentifier>9\\s*6\\s*)",
|
|
260
280
|
"(?<SCNC>([0-9]\\s*){2})",
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "GOFO Express",
|
|
3
|
+
"courier_code": "gofo",
|
|
4
|
+
"tracking_numbers": [
|
|
5
|
+
{
|
|
6
|
+
"name": "GOFO Express (US)",
|
|
7
|
+
"id": "gofo_us",
|
|
8
|
+
"regex": [
|
|
9
|
+
"\\s*G\\s*F\\s*U\\s*S\\s*(?<SerialNumber>([0-9]\\s*){14})\\s*"
|
|
10
|
+
],
|
|
11
|
+
"tracking_url": "https://www.gofoexpress.com/tracking.html?searchID=%s",
|
|
12
|
+
"validation": {},
|
|
13
|
+
"test_numbers": {
|
|
14
|
+
"valid": [
|
|
15
|
+
"GFUS01011884214464",
|
|
16
|
+
"GFUS01011884214272"
|
|
17
|
+
],
|
|
18
|
+
"invalid": [
|
|
19
|
+
"GFUS0101188421446"
|
|
20
|
+
]
|
|
21
|
+
}
|
|
22
|
+
}
|
|
23
|
+
]
|
|
24
|
+
}
|
|
@@ -79,6 +79,27 @@
|
|
|
79
79
|
"1LS7119013618127-2"
|
|
80
80
|
]
|
|
81
81
|
}
|
|
82
|
+
},
|
|
83
|
+
{
|
|
84
|
+
"name": "LaserShip 1LSCX (15)",
|
|
85
|
+
"id": "lasership_1lscx",
|
|
86
|
+
"regex": [
|
|
87
|
+
"\\s*1\\s*L\\s*S\\s*",
|
|
88
|
+
"\\s*C\\s*X\\s*",
|
|
89
|
+
"(?<SerialNumber>",
|
|
90
|
+
"([0-9A-Z]\\s*){10}",
|
|
91
|
+
")"
|
|
92
|
+
],
|
|
93
|
+
"validation": {},
|
|
94
|
+
"test_numbers": {
|
|
95
|
+
"valid": [
|
|
96
|
+
"1LS CX VE00 58631Y",
|
|
97
|
+
"1LSCXVE005BUEFX"
|
|
98
|
+
],
|
|
99
|
+
"invalid": [
|
|
100
|
+
"1LSCZVE0058631Y"
|
|
101
|
+
]
|
|
102
|
+
}
|
|
82
103
|
}
|
|
83
104
|
]
|
|
84
105
|
}
|
|
@@ -13,7 +13,8 @@
|
|
|
13
13
|
],
|
|
14
14
|
"validation": {
|
|
15
15
|
"checksum": {
|
|
16
|
-
"name": "luhn"
|
|
16
|
+
"name": "luhn",
|
|
17
|
+
"modulo": 10
|
|
17
18
|
}
|
|
18
19
|
},
|
|
19
20
|
"tracking_url": "https://www.odfl.com/us/en/tools/trace-track-ltl-freight/trace.html?proNumbers=%s",
|
|
@@ -43,7 +44,8 @@
|
|
|
43
44
|
],
|
|
44
45
|
"validation": {
|
|
45
46
|
"checksum": {
|
|
46
|
-
"name": "luhn"
|
|
47
|
+
"name": "luhn",
|
|
48
|
+
"modulo": 10
|
|
47
49
|
}
|
|
48
50
|
},
|
|
49
51
|
"tracking_url": "https://www.odfl.com/us/en/tools/trace-track-ltl-freight/trace.html?proNumbers=%s",
|
|
@@ -9,8 +9,10 @@
|
|
|
9
9
|
"validation": {
|
|
10
10
|
"checksum": {
|
|
11
11
|
"name": "mod10",
|
|
12
|
+
"modulo": 10,
|
|
12
13
|
"evens_multiplier": 1,
|
|
13
|
-
"odds_multiplier": 2
|
|
14
|
+
"odds_multiplier": 2,
|
|
15
|
+
"reverse": false
|
|
14
16
|
},
|
|
15
17
|
"serial_number_format": {
|
|
16
18
|
"prepend_if": {
|
|
@@ -42,8 +44,10 @@
|
|
|
42
44
|
"validation": {
|
|
43
45
|
"checksum": {
|
|
44
46
|
"name": "mod10",
|
|
47
|
+
"modulo": 10,
|
|
45
48
|
"evens_multiplier": 1,
|
|
46
|
-
"odds_multiplier": 2
|
|
49
|
+
"odds_multiplier": 2,
|
|
50
|
+
"reverse": false
|
|
47
51
|
},
|
|
48
52
|
"serial_number_format": {
|
|
49
53
|
"prepend_if": {
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "Purolator",
|
|
3
|
+
"courier_code": "purolator",
|
|
4
|
+
"tracking_numbers": [
|
|
5
|
+
{
|
|
6
|
+
"name": "Purolator (12)",
|
|
7
|
+
"id": "purolator_numeric",
|
|
8
|
+
"regex": "\\s*(?<SerialNumber>[0-5]\\s*([0-9]\\s*){10})(?<CheckDigit>[0-9])\\s*",
|
|
9
|
+
"validation": {
|
|
10
|
+
"checksum": {
|
|
11
|
+
"name": "luhn"
|
|
12
|
+
}
|
|
13
|
+
},
|
|
14
|
+
"tracking_url": "https://www.purolator.com/en/shipping/tracker?searchValue=%s",
|
|
15
|
+
"test_numbers": {
|
|
16
|
+
"valid": [
|
|
17
|
+
"320595463938",
|
|
18
|
+
"320606146993",
|
|
19
|
+
"287809468872",
|
|
20
|
+
"320610804900",
|
|
21
|
+
"331426749957",
|
|
22
|
+
"331434972567"
|
|
23
|
+
],
|
|
24
|
+
"invalid": [
|
|
25
|
+
"320595463939",
|
|
26
|
+
"287809468875"
|
|
27
|
+
]
|
|
28
|
+
}
|
|
29
|
+
},
|
|
30
|
+
{
|
|
31
|
+
"name": "Purolator (alpha + 9)",
|
|
32
|
+
"id": "purolator_alpha",
|
|
33
|
+
"regex": "\\s*(?<SerialNumber>([A-Z]\\s*){3}([0-9]\\s*){9})\\s*",
|
|
34
|
+
"validation": {},
|
|
35
|
+
"tracking_url": "https://www.purolator.com/en/shipping/tracker?searchValue=%s",
|
|
36
|
+
"test_numbers": {
|
|
37
|
+
"valid": [
|
|
38
|
+
"KYV009956937",
|
|
39
|
+
"CGK002986959",
|
|
40
|
+
"JFV247545960",
|
|
41
|
+
"TLR000083964"
|
|
42
|
+
],
|
|
43
|
+
"invalid": [
|
|
44
|
+
"KY009956937",
|
|
45
|
+
"331426749957"
|
|
46
|
+
]
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
]
|
|
50
|
+
}
|
data/lib/data/couriers/s10.json
CHANGED
|
@@ -7,7 +7,9 @@
|
|
|
7
7
|
"name": "S10",
|
|
8
8
|
"validation": {
|
|
9
9
|
"checksum": {
|
|
10
|
-
"name": "s10"
|
|
10
|
+
"name": "s10",
|
|
11
|
+
"weightings": [8, 6, 4, 2, 3, 5, 9, 7],
|
|
12
|
+
"modulo": 11
|
|
11
13
|
},
|
|
12
14
|
"additional": {
|
|
13
15
|
"exists": [
|
|
@@ -17,6 +19,11 @@
|
|
|
17
19
|
},
|
|
18
20
|
"regex": "\\s*(?<ServiceType>([A-Z]\\s*){2})(?<SerialNumber>([0-9]\\s*){8})(?<CheckDigit>([0-9]\\s*))(?<CountryCode>([A-Z]\\s*){2})",
|
|
19
21
|
"tracking_url": null,
|
|
22
|
+
"glossary": {
|
|
23
|
+
"ServiceType": { "description": "Two letters for the class of postal service, the first of which identifies the service and the second the variant." },
|
|
24
|
+
"SerialNumber": { "description": "Eight digits assigned by the issuing postal service." },
|
|
25
|
+
"CountryCode": { "description": "Two letters for the country whose postal service issued the number, following ISO 3166." }
|
|
26
|
+
},
|
|
20
27
|
"test_numbers": {
|
|
21
28
|
"valid": [
|
|
22
29
|
"RB123456785GB",
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "Spee-Dee Delivery",
|
|
3
|
+
"courier_code": "speedee",
|
|
4
|
+
"tracking_numbers": [
|
|
5
|
+
{
|
|
6
|
+
"name": "Spee-Dee (20)",
|
|
7
|
+
"id": "speedee",
|
|
8
|
+
"regex": "\\s*S\\s*P\\s*(?<SerialNumber>([0-9]\\s*){18})\\s*",
|
|
9
|
+
"validation": {},
|
|
10
|
+
"tracking_url": "https://www.speedeedelivery.com/track/?tracking=%s",
|
|
11
|
+
"test_numbers": {
|
|
12
|
+
"valid": [
|
|
13
|
+
"SP029692510000920746",
|
|
14
|
+
"SP029692510000901479",
|
|
15
|
+
"SP029692510000688332",
|
|
16
|
+
"SP029692510000279301",
|
|
17
|
+
"SP029692450001607639"
|
|
18
|
+
],
|
|
19
|
+
"invalid": [
|
|
20
|
+
"029692510000920746",
|
|
21
|
+
"SX029692510000920746"
|
|
22
|
+
]
|
|
23
|
+
}
|
|
24
|
+
}
|
|
25
|
+
]
|
|
26
|
+
}
|
data/lib/data/couriers/ups.json
CHANGED
|
@@ -15,8 +15,10 @@
|
|
|
15
15
|
"validation": {
|
|
16
16
|
"checksum": {
|
|
17
17
|
"name": "mod10",
|
|
18
|
+
"modulo": 10,
|
|
18
19
|
"evens_multiplier": 1,
|
|
19
|
-
"odds_multiplier": 2
|
|
20
|
+
"odds_multiplier": 2,
|
|
21
|
+
"reverse": false
|
|
20
22
|
}
|
|
21
23
|
},
|
|
22
24
|
"tracking_url": "https://wwwapps.ups.com/WebTracking/track?track=yes&trackNums=%s",
|
|
@@ -146,8 +148,10 @@
|
|
|
146
148
|
"validation": {
|
|
147
149
|
"checksum": {
|
|
148
150
|
"name": "mod10",
|
|
151
|
+
"modulo": 10,
|
|
149
152
|
"evens_multiplier": 1,
|
|
150
|
-
"odds_multiplier": 2
|
|
153
|
+
"odds_multiplier": 2,
|
|
154
|
+
"reverse": false
|
|
151
155
|
}
|
|
152
156
|
},
|
|
153
157
|
"additional": [
|
data/lib/data/couriers/usps.json
CHANGED
|
@@ -3,10 +3,10 @@
|
|
|
3
3
|
"courier_code": "usps",
|
|
4
4
|
"tracking_numbers": [
|
|
5
5
|
{
|
|
6
|
-
"tracking_url": "https://tools.usps.com/go/TrackConfirmAction?tLabels=%s",
|
|
7
6
|
"name": "USPS 20",
|
|
8
7
|
"id": "usps_20",
|
|
9
8
|
"description": "20 digit USPS numbers",
|
|
9
|
+
"tracking_url": "https://tools.usps.com/go/TrackConfirmAction?tLabels=%s",
|
|
10
10
|
"regex": [
|
|
11
11
|
"\\s*(?<SerialNumber>",
|
|
12
12
|
"(?<ServiceType>([0-9]\\s*){2})",
|
|
@@ -18,8 +18,10 @@
|
|
|
18
18
|
"validation": {
|
|
19
19
|
"checksum": {
|
|
20
20
|
"name": "mod10",
|
|
21
|
+
"modulo": 10,
|
|
21
22
|
"evens_multiplier": 3,
|
|
22
|
-
"odds_multiplier": 1
|
|
23
|
+
"odds_multiplier": 1,
|
|
24
|
+
"reverse": false
|
|
23
25
|
}
|
|
24
26
|
},
|
|
25
27
|
"test_numbers": {
|
|
@@ -57,10 +59,84 @@
|
|
|
57
59
|
}
|
|
58
60
|
]
|
|
59
61
|
},
|
|
62
|
+
{
|
|
63
|
+
"name": "USPS 22",
|
|
64
|
+
"id": "usps_22",
|
|
65
|
+
"description": "22 digit USPS numbers",
|
|
66
|
+
"tracking_url": "https://tools.usps.com/go/TrackConfirmAction?tLabels=%s",
|
|
67
|
+
"regex": [
|
|
68
|
+
"\\s*((?<RoutingApplicationId>4\\s*2\\s*0\\s*)(?<DestinationZip>([0-9]\\s*){5}))?",
|
|
69
|
+
"(?<SerialNumber>",
|
|
70
|
+
"(?<ServiceType>(9\\s*[4-8]\\s*))",
|
|
71
|
+
"(?<ShipperId>([0-9]\\s*){9})",
|
|
72
|
+
"(?<PackageId>([0-9]\\s*){10})",
|
|
73
|
+
")",
|
|
74
|
+
"(?<CheckDigit>[0-9]\\s*)"
|
|
75
|
+
],
|
|
76
|
+
"validation": {
|
|
77
|
+
"checksum": {
|
|
78
|
+
"name": "mod10",
|
|
79
|
+
"modulo": 10,
|
|
80
|
+
"evens_multiplier": 3,
|
|
81
|
+
"odds_multiplier": 1,
|
|
82
|
+
"reverse": true
|
|
83
|
+
}
|
|
84
|
+
},
|
|
85
|
+
"test_numbers": {
|
|
86
|
+
"valid": [
|
|
87
|
+
"420787459400111206206406260787",
|
|
88
|
+
"9400111206206406260787",
|
|
89
|
+
"9434611206206406227577",
|
|
90
|
+
"9434611206206407667136",
|
|
91
|
+
"9400111206206407628746",
|
|
92
|
+
"9400111206206407628845"
|
|
93
|
+
],
|
|
94
|
+
"invalid": [
|
|
95
|
+
"9434611206206407667131",
|
|
96
|
+
"9434611306206407667222",
|
|
97
|
+
"2334611306206407667222"
|
|
98
|
+
]
|
|
99
|
+
},
|
|
100
|
+
"partners": [{
|
|
101
|
+
"description": "DHL ECommerce uses USPS for last mile delivery",
|
|
102
|
+
"partner_type": "shipper",
|
|
103
|
+
"partner_id": "dhl_ecommerce_30",
|
|
104
|
+
"validation": {
|
|
105
|
+
"matches_all": [
|
|
106
|
+
{
|
|
107
|
+
"regex_group_name": "RoutingApplicationId",
|
|
108
|
+
"matches": "420"
|
|
109
|
+
},
|
|
110
|
+
{
|
|
111
|
+
"matches_regex": "9[24]",
|
|
112
|
+
"regex_group_name": "ServiceType"
|
|
113
|
+
}
|
|
114
|
+
]
|
|
115
|
+
}
|
|
116
|
+
}],
|
|
117
|
+
"additional": [
|
|
118
|
+
{
|
|
119
|
+
"name": "Service Type",
|
|
120
|
+
"regex_group_name": "ServiceType",
|
|
121
|
+
"lookup": [
|
|
122
|
+
{ "matches": "92", "name": "Signature Confirmation" },
|
|
123
|
+
{ "matches": "93", "name": "Collect on Delivery (COD)" },
|
|
124
|
+
{ "matches": "94", "name": "USPS Tracking" },
|
|
125
|
+
{ "matches": "95", "name": "Certified Mail" },
|
|
126
|
+
{ "matches": "96", "name": "Insured Mail" },
|
|
127
|
+
{ "matches": "97", "name": "Registered Mail" },
|
|
128
|
+
{ "matches": "98", "name": "Return Receipt for Merchandise" }
|
|
129
|
+
]
|
|
130
|
+
}
|
|
131
|
+
]
|
|
132
|
+
},
|
|
60
133
|
{
|
|
61
134
|
"name": "USPS 34v2",
|
|
62
135
|
"id": "usps_32V2",
|
|
63
136
|
"description": "variation on 34 digit USPS IMpd numbers",
|
|
137
|
+
"glossary": {
|
|
138
|
+
"ApplicationIdentifier": { "description": "92 through 95, saying which USPS service and mailer ID scheme the rest belongs to." }
|
|
139
|
+
},
|
|
64
140
|
"regex": [
|
|
65
141
|
"\\s*(?<RoutingApplicationId>4\\s*2\\s*0\\s*)(?<DestinationZip>([0-9]\\s*){5})",
|
|
66
142
|
"(?<RoutingNumber>([0-9]\\s*){4})",
|
|
@@ -74,8 +150,10 @@
|
|
|
74
150
|
"validation": {
|
|
75
151
|
"checksum": {
|
|
76
152
|
"name": "mod10",
|
|
153
|
+
"modulo": 10,
|
|
77
154
|
"evens_multiplier": 3,
|
|
78
|
-
"odds_multiplier": 1
|
|
155
|
+
"odds_multiplier": 1,
|
|
156
|
+
"reverse": false
|
|
79
157
|
}
|
|
80
158
|
},
|
|
81
159
|
"tracking_url": "https://tools.usps.com/go/TrackConfirmAction?tLabels=%s",
|
|
@@ -94,6 +172,9 @@
|
|
|
94
172
|
"name": "USPS 91",
|
|
95
173
|
"id": "usps_91",
|
|
96
174
|
"description": "USPS now calls this the IMpd barcode format",
|
|
175
|
+
"glossary": {
|
|
176
|
+
"ApplicationIdentifier": { "description": "91 through 95, saying which USPS service and mailer ID scheme the rest belongs to." }
|
|
177
|
+
},
|
|
97
178
|
"regex": [
|
|
98
179
|
"\\s*(?:(?<RoutingApplicationId>4\\s*2\\s*0\\s*)(?<DestinationZip>([0-9]\\s*){5}))?",
|
|
99
180
|
"(?<SerialNumber>",
|
|
@@ -108,8 +189,10 @@
|
|
|
108
189
|
"validation": {
|
|
109
190
|
"checksum": {
|
|
110
191
|
"name": "mod10",
|
|
192
|
+
"modulo": 10,
|
|
111
193
|
"evens_multiplier": 3,
|
|
112
|
-
"odds_multiplier": 1
|
|
194
|
+
"odds_multiplier": 1,
|
|
195
|
+
"reverse": false
|
|
113
196
|
},
|
|
114
197
|
"serial_number_format": {
|
|
115
198
|
"prepend_if": {
|
|
@@ -131,6 +214,31 @@
|
|
|
131
214
|
{
|
|
132
215
|
"matches": "61",
|
|
133
216
|
"regex_group_name": "SCNC"
|
|
217
|
+
},
|
|
218
|
+
{
|
|
219
|
+
"regex_group_name": "ApplicationIdentifier",
|
|
220
|
+
"matches": "92"
|
|
221
|
+
}
|
|
222
|
+
]
|
|
223
|
+
}
|
|
224
|
+
},
|
|
225
|
+
{
|
|
226
|
+
"description": "DHL uses USPS for last mile delivery, but not all USPS91 numbers are DHL Ecommerce",
|
|
227
|
+
"partner_type": "shipper",
|
|
228
|
+
"partner_id": "dhl_ecommerce_30",
|
|
229
|
+
"validation": {
|
|
230
|
+
"matches_all": [
|
|
231
|
+
{
|
|
232
|
+
"regex_group_name": "ServiceType",
|
|
233
|
+
"matches": "29"
|
|
234
|
+
},
|
|
235
|
+
{
|
|
236
|
+
"matches": "61",
|
|
237
|
+
"regex_group_name": "SCNC"
|
|
238
|
+
},
|
|
239
|
+
{
|
|
240
|
+
"matches": "420",
|
|
241
|
+
"regex_group_name": "RoutingApplicationId"
|
|
134
242
|
}
|
|
135
243
|
]
|
|
136
244
|
}
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "YunExpress",
|
|
3
|
+
"courier_code": "yunexpress",
|
|
4
|
+
"tracking_numbers": [
|
|
5
|
+
{
|
|
6
|
+
"name": "YunExpress",
|
|
7
|
+
"id": "yunexpress",
|
|
8
|
+
"tracking_url": "https://www.yuntrack.com/parcelTracking?id=%s",
|
|
9
|
+
"regex": [
|
|
10
|
+
"\\s*Y\\s*T\\s*(?<SerialNumber>([0-9]\\s*){16})\\s*"
|
|
11
|
+
],
|
|
12
|
+
"validation": {},
|
|
13
|
+
"test_numbers": {
|
|
14
|
+
"valid": [
|
|
15
|
+
"YT2229521272164446",
|
|
16
|
+
"YT2229521272164447",
|
|
17
|
+
" Y T 2 2 2 9 5 2 1 2 7 2 1 6 4 4 4 7 "
|
|
18
|
+
],
|
|
19
|
+
"invalid": [
|
|
20
|
+
"YT123456789012345"
|
|
21
|
+
]
|
|
22
|
+
}
|
|
23
|
+
}
|
|
24
|
+
]
|
|
25
|
+
}
|
data/lib/tracking_number/base.rb
CHANGED
|
@@ -126,16 +126,16 @@ module TrackingNumber
|
|
|
126
126
|
|
|
127
127
|
def info
|
|
128
128
|
Info.new({
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
129
|
+
courier: courier_info,
|
|
130
|
+
service_type: service_type,
|
|
131
|
+
service_description: service_description,
|
|
132
|
+
destination_zip: destination_zip,
|
|
133
|
+
shipper_id: shipper_id,
|
|
134
|
+
package_type: package_type,
|
|
135
|
+
tracking_url: tracking_url,
|
|
136
|
+
partners: partners,
|
|
137
|
+
decode: decode
|
|
138
|
+
})
|
|
139
139
|
end
|
|
140
140
|
|
|
141
141
|
def courier_code
|
|
@@ -186,8 +186,7 @@ module TrackingNumber
|
|
|
186
186
|
partner_hash = {}
|
|
187
187
|
|
|
188
188
|
return unless (partner_tn = find_matching_partner)
|
|
189
|
-
|
|
190
|
-
possible_twin = partner_tn.send(:find_matching_partner)
|
|
189
|
+
possible_twin = partner_tn.send(:find_matching_partner, self.class.const_get("ID"))
|
|
191
190
|
|
|
192
191
|
if possible_twin.instance_of?(self.class) && possible_twin.tracking_number == tracking_number
|
|
193
192
|
partner_hash[partner_data[:partner_type].to_sym] = partner_tn
|
|
@@ -295,8 +294,12 @@ module TrackingNumber
|
|
|
295
294
|
nil
|
|
296
295
|
end
|
|
297
296
|
|
|
298
|
-
def find_matching_partner
|
|
299
|
-
partner_info =
|
|
297
|
+
def find_matching_partner(partner_id = nil)
|
|
298
|
+
partner_info = if partner_id
|
|
299
|
+
(self.class.const_get(:PARTNERS) || []).select { |info| info[:partner_id]&.to_sym == partner_id.to_sym }
|
|
300
|
+
else
|
|
301
|
+
self.class.const_get(:PARTNERS) || []
|
|
302
|
+
end
|
|
300
303
|
|
|
301
304
|
partner_info.each do |partner_data|
|
|
302
305
|
klass = find_tracking_class_by_id(partner_data[:partner_id])
|
|
@@ -304,7 +307,6 @@ module TrackingNumber
|
|
|
304
307
|
return false unless klass
|
|
305
308
|
|
|
306
309
|
tn = klass.new(tracking_number)
|
|
307
|
-
|
|
308
310
|
valid = if partner_data.dig(:validation, :matches_all)
|
|
309
311
|
tn.valid? && match_all(partner_data.dig(:validation, :matches_all))
|
|
310
312
|
elsif partner_data.dig(:validation, :matches_any)
|
data/package.json
CHANGED
|
@@ -38,31 +38,7 @@
|
|
|
38
38
|
"release": "minor"
|
|
39
39
|
},
|
|
40
40
|
{
|
|
41
|
-
"type": "
|
|
42
|
-
"release": "patch"
|
|
43
|
-
},
|
|
44
|
-
{
|
|
45
|
-
"type": "ci",
|
|
46
|
-
"release": "patch"
|
|
47
|
-
},
|
|
48
|
-
{
|
|
49
|
-
"type": "chore",
|
|
50
|
-
"release": "patch"
|
|
51
|
-
},
|
|
52
|
-
{
|
|
53
|
-
"type": "docs",
|
|
54
|
-
"release": "patch"
|
|
55
|
-
},
|
|
56
|
-
{
|
|
57
|
-
"type": "refactor",
|
|
58
|
-
"release": "patch"
|
|
59
|
-
},
|
|
60
|
-
{
|
|
61
|
-
"type": "style",
|
|
62
|
-
"release": "patch"
|
|
63
|
-
},
|
|
64
|
-
{
|
|
65
|
-
"type": "test",
|
|
41
|
+
"type": "fix",
|
|
66
42
|
"release": "patch"
|
|
67
43
|
}
|
|
68
44
|
],
|
metadata
CHANGED
|
@@ -1,14 +1,14 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: tracking_number
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 2.
|
|
4
|
+
version: 2.5.0
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Jeff Keen
|
|
8
8
|
autorequire:
|
|
9
9
|
bindir: bin
|
|
10
10
|
cert_chain: []
|
|
11
|
-
date:
|
|
11
|
+
date: 2026-08-13 00:00:00.000000000 Z
|
|
12
12
|
dependencies:
|
|
13
13
|
- !ruby/object:Gem::Dependency
|
|
14
14
|
name: activesupport
|
|
@@ -146,6 +146,7 @@ extra_rdoc_files:
|
|
|
146
146
|
- LICENSE.txt
|
|
147
147
|
files:
|
|
148
148
|
- ".document"
|
|
149
|
+
- ".github/FUNDING.yml"
|
|
149
150
|
- ".github/dependabot.yml"
|
|
150
151
|
- ".github/workflows/release.yml"
|
|
151
152
|
- ".github/workflows/ruby.yml"
|
|
@@ -160,16 +161,21 @@ files:
|
|
|
160
161
|
- bin/console
|
|
161
162
|
- lib/data/couriers/amazon.json
|
|
162
163
|
- lib/data/couriers/canadapost.json
|
|
164
|
+
- lib/data/couriers/canpar.json
|
|
163
165
|
- lib/data/couriers/dhl.json
|
|
164
166
|
- lib/data/couriers/dpd.json
|
|
165
167
|
- lib/data/couriers/fedex.json
|
|
168
|
+
- lib/data/couriers/gofo.json
|
|
166
169
|
- lib/data/couriers/landmark.json
|
|
167
170
|
- lib/data/couriers/lasership.json
|
|
168
171
|
- lib/data/couriers/old_dominion.json
|
|
169
172
|
- lib/data/couriers/ontrac.json
|
|
173
|
+
- lib/data/couriers/purolator.json
|
|
170
174
|
- lib/data/couriers/s10.json
|
|
175
|
+
- lib/data/couriers/speedee.json
|
|
171
176
|
- lib/data/couriers/ups.json
|
|
172
177
|
- lib/data/couriers/usps.json
|
|
178
|
+
- lib/data/couriers/yunexpress.json
|
|
173
179
|
- lib/tracking_number.rb
|
|
174
180
|
- lib/tracking_number/active_model_validator.rb
|
|
175
181
|
- lib/tracking_number/base.rb
|