tracking_number 1.0.3 → 1.0.4

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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: b7f7fa343ef418dd7cd2ace8046a191aaf354798c49afab48f74dc2d086efcb7
4
- data.tar.gz: f39e99015f0ad90fc5bc4e25387ab45f5ecd28a0002a666b60c41e2984855fa8
3
+ metadata.gz: c982bd19b169cb2d7c3451f310d0408185e3ca3da9056dfd7f08f6d70194d9f1
4
+ data.tar.gz: ead95abd691ee8572fd295b3e8aed206b44cbb791437c8d40c90702c1e8cd5e7
5
5
  SHA512:
6
- metadata.gz: 37351ce16a47c35af8f4cd516931251e9c0b8e30ca216857298d09f29988df0944c68081e773c9f0afe806f51fa98169055b924f1dda9d58e633a7abab1d826b
7
- data.tar.gz: 9ada728daa08df0c616db4cf053fe812e0afa346ccd17ad8fe592838658c88fd6e82bbd1c9070e1916bdbac931071d371d4da927f3224adbfe14d02b62735b39
6
+ metadata.gz: '086856f2923929118bd32f6e6ccd9725c0099c9d274273a9ac5ff072fb2f561460cd940d6b95506ca3ea2685b998310cba8534ef3404732353bdb8a2452094e4'
7
+ data.tar.gz: 2280018d8645ce2181819c7cc35a53e8ea857ef7e785a335ed03c6bac6b813e47f6bcb5ac6831e251e77b7f17ffc5451680fea15d69cbaec84bb753dacef3f1e
@@ -4,9 +4,9 @@ cache: bundler
4
4
  rvm:
5
5
  - 2.0.0
6
6
  - 2.1.10
7
- - 2.2.7
8
- - 2.3.4
9
- - 2.4.1
7
+ - 2.2.9
8
+ - 2.3.6
9
+ - 2.4.3
10
10
  - 2.5.0
11
11
  before_install:
12
12
  - travis_retry gem update --system
data/README.md CHANGED
@@ -9,6 +9,10 @@ It detects tracking numbers from UPS, FedEx, DHL, USPS, OnTrac, Amazon Logistics
9
9
 
10
10
  This gem does not do tracking. That is left up to you.
11
11
 
12
+ #### New in 1.0
13
+
14
+ 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.
15
+
12
16
  ## Usage
13
17
 
14
18
  #### Checking an individual tracking number
@@ -119,9 +123,6 @@ class Shipment < ActiveRecord::Base
119
123
  end
120
124
  ```
121
125
 
122
- ## Where the data comes from
123
- Starting with the 1.0 release of this gem the data for tracking numbers has been extracted into a separate repository ([tracking_number_data](http://github.com/jkeen/tracking_number_data)) so non-ruby clients can benefit from the detection/documentation that used to be contained deep in the code of this gem. If you want to write a client in some other language, that's the stuff you want.
124
-
125
126
  ## Contributing to tracking_number
126
127
  * Check out the latest master to make sure the feature hasn't been implemented or the bug hasn't been fixed yet
127
128
  * Check out the issue tracker to make sure someone already hasn't requested it and/or contributed it
@@ -1,10 +1,11 @@
1
1
  # Identify if tracking numbers are valid, and which service they belong to
2
2
 
3
3
  require 'json'
4
+ require 'tracking_number/checksum_validations'
5
+ require 'tracking_number/loader'
4
6
  require 'tracking_number/base'
5
7
  require 'tracking_number/info'
6
8
  require 'tracking_number/unknown'
7
- require 'checksum_validations'
8
9
  require 'active_support/core_ext/string'
9
10
  require 'active_support/core_ext/hash'
10
11
 
@@ -12,67 +13,7 @@ if defined?(ActiveModel::EachValidator)
12
13
  require 'tracking_number/active_model_validator'
13
14
  end
14
15
 
15
- def has_test_numbers?(tracking)
16
- return tracking[:test_numbers] && tracking[:test_numbers][:valid]
17
- end
18
-
19
- def test_numbers_return_required_groups?(tracking, regex)
20
- test_number = tracking[:test_numbers][:valid][0]
21
- matches = test_number.match(regex)
22
-
23
- return matches["SerialNumber"]
24
- end
25
-
26
- def read_courier_info(file)
27
- return JSON.parse(File.read(file)).deep_symbolize_keys!
28
- end
29
-
30
- def create_class(klass, courier_info, tracking_info)
31
- klass = Class.new(TrackingNumber::Base)
32
- klass.const_set("COURIER_CODE", courier_info[:courier_code])
33
- info = courier_info.dup
34
- info.delete(:tracking_numbers)
35
- klass.const_set("COURIER_INFO", info)
36
-
37
- pattern = tracking_info[:regex]
38
- pattern = tracking_info[:regex].join if tracking_info[:regex].is_a?(Array)
39
-
40
- verify_pattern = "^#{pattern}$"
41
- search_pattern = "\\b#{pattern}\\b"
42
-
43
- klass.const_set("SEARCH_PATTERN", Regexp.new(search_pattern))
44
- klass.const_set("VERIFY_PATTERN", Regexp.new(verify_pattern))
45
-
46
- klass.const_set("VALIDATION", tracking_info[:validation])
47
- klass.const_set("ADDITIONAL", tracking_info[:additional])
48
-
49
- return klass
50
- end
51
-
52
- def register_class(klass, tracking_name)
53
- klass_name = tracking_name.gsub(/[^0-9A-Za-z]/, '')
54
- return TrackingNumber.const_set(klass_name, klass)
55
- end
56
-
57
- tracking_number_types = []
58
-
59
- Dir.glob(File.join(File.dirname(__FILE__), "data/couriers/*.json")).each do |file|
60
- courier_info = read_courier_info(file)
61
-
62
- courier_info[:tracking_numbers].each do |tracking_info|
63
- tracking_name = tracking_info[:name]
64
- klass = create_class(klass, courier_info, tracking_info)
65
-
66
- # Do some basic checks on the data file
67
- throw 'missing test numbers' unless has_test_numbers?(tracking_info)
68
- throw 'missing regex match groups' unless test_numbers_return_required_groups?(tracking_info, Regexp.new(klass::VERIFY_PATTERN))
69
-
70
- const = register_class(klass, tracking_name)
71
- tracking_number_types.push(const)
72
- end
73
- end
74
-
75
- TrackingNumber.const_set("TYPES", tracking_number_types)
16
+ TrackingNumber::Loader.load_tracking_number_data
76
17
 
77
18
  module TrackingNumber
78
19
  def self.search(body)
@@ -1,5 +1,3 @@
1
- require 'checksum_validations'
2
-
3
1
  module TrackingNumber
4
2
  class Base
5
3
  attr_accessor :tracking_number
@@ -95,7 +93,7 @@ module TrackingNumber
95
93
  name = checksum_info[:name]
96
94
  method_name = "validates_#{name}?"
97
95
 
98
- ChecksumValidations.send(method_name, serial_number, check_digit, checksum_info)
96
+ TrackingNumber::ChecksumValidations.send(method_name, serial_number, check_digit, checksum_info)
99
97
  end
100
98
 
101
99
  def to_s
@@ -113,8 +111,7 @@ module TrackingNumber
113
111
  :service_description => service_description,
114
112
  :destination_zip => destination_zip,
115
113
  :shipper_id => shipper_id,
116
- :package_type => package_type,
117
- :package_description => package_description
114
+ :package_type => package_type
118
115
  })
119
116
  end
120
117
 
@@ -172,6 +169,21 @@ module TrackingNumber
172
169
  match_group("ShipperId")
173
170
  end
174
171
 
172
+ def tracking_url
173
+ url = nil
174
+ if matching_additional["Courier"]
175
+ url = matching_additional["Courier"][:tracking_url]
176
+ else
177
+ if self.class.const_defined?(:TRACKING_URL)
178
+ url = self.class.const_get(:TRACKING_URL)
179
+ end
180
+ end
181
+
182
+ if url
183
+ url.sub('%s', self.tracking_number)
184
+ end
185
+ end
186
+
175
187
  def matching_additional
176
188
  additional = self.class.const_get(:ADDITIONAL) || []
177
189
 
@@ -0,0 +1,65 @@
1
+ module TrackingNumber
2
+ module ChecksumValidations
3
+ class << self
4
+ def validates_s10?(sequence, check_digit, extras = {})
5
+ weighting = [8,6,4,2,3,5,9,7]
6
+
7
+ total = 0
8
+ sequence.chars.to_a.zip(weighting).each do |(a,b)|
9
+ total += a.to_i * b.to_i
10
+ end
11
+
12
+ remainder = total % 11
13
+ check = case remainder
14
+ when 1
15
+ 0
16
+ when 0
17
+ 5
18
+ else
19
+ 11 - remainder
20
+ end
21
+
22
+ return check.to_i == check_digit.to_i
23
+ end
24
+
25
+ def validates_sum_product_with_weightings_and_modulo?(sequence, check_digit, extras = {})
26
+ weighting = extras[:weightings] || []
27
+
28
+ total = 0
29
+ sequence.chars.to_a.zip(weighting).each do |(a,b)|
30
+ total += a.to_i * b
31
+ end
32
+ return (total % extras[:modulo1] % extras[:modulo2]) == check_digit.to_i
33
+ end
34
+
35
+ def validates_mod10?(sequence, check_digit, extras = {})
36
+ total = 0
37
+ sequence.chars.each_with_index do |c, i|
38
+ x = if c[/[0-9]/] # numeric
39
+ c.to_i
40
+ else
41
+ (c[0].ord - 3) % 10
42
+ end
43
+
44
+ if extras[:odds_multiplier] && i.odd?
45
+ x *= extras[:odds_multiplier].to_i
46
+ elsif extras[:evens_multiplier] && i.even?
47
+ x *= extras[:evens_multiplier].to_i
48
+ end
49
+
50
+ total += x
51
+ end
52
+
53
+ check = (total % 10)
54
+ check = (10 - check) unless (check.zero?)
55
+
56
+ return (check.to_i == check_digit.to_i)
57
+ end
58
+
59
+ def validates_mod7?(sequence, check_digit, extras = {})
60
+ # standard mod 7 check
61
+ return true if sequence.to_i % 7 == check_digit.to_i
62
+ end
63
+ end
64
+ end
65
+ end
@@ -11,8 +11,16 @@ module TrackingNumber
11
11
  end
12
12
  end
13
13
 
14
+ def method_missing(*args)
15
+ nil
16
+ end
17
+
14
18
  def to_s
15
19
  @default || @name
16
20
  end
21
+
22
+ def to_json
23
+
24
+ end
17
25
  end
18
26
  end
@@ -0,0 +1,73 @@
1
+
2
+ module TrackingNumber
3
+ module Loader
4
+ class << self
5
+ def load_tracking_number_data
6
+ tracking_number_types = []
7
+
8
+ Dir.glob(File.join(File.dirname(__FILE__), "../data/couriers/*.json")).each do |file|
9
+ courier_info = read_courier_info(file)
10
+
11
+ courier_info[:tracking_numbers].each do |tracking_info|
12
+ tracking_name = tracking_info[:name]
13
+ klass = create_class(klass, courier_info, tracking_info)
14
+
15
+ # Do some basic checks on the data file
16
+ throw 'missing test numbers' unless has_test_numbers?(tracking_info)
17
+ throw 'missing regex match groups' unless test_numbers_return_required_groups?(tracking_info, Regexp.new(klass::VERIFY_PATTERN))
18
+
19
+ const = register_class(klass, tracking_name)
20
+ tracking_number_types.push(const)
21
+ end
22
+ end
23
+
24
+ TrackingNumber.const_set("TYPES", tracking_number_types)
25
+ end
26
+
27
+ private
28
+
29
+ def has_test_numbers?(tracking)
30
+ return tracking[:test_numbers] && tracking[:test_numbers][:valid]
31
+ end
32
+
33
+ def test_numbers_return_required_groups?(tracking, regex)
34
+ test_number = tracking[:test_numbers][:valid][0]
35
+ matches = test_number.match(regex)
36
+
37
+ return matches["SerialNumber"]
38
+ end
39
+
40
+ def read_courier_info(file)
41
+ return JSON.parse(File.read(file)).deep_symbolize_keys!
42
+ end
43
+
44
+ def create_class(klass, courier_info, tracking_info)
45
+ klass = Class.new(TrackingNumber::Base)
46
+ klass.const_set("COURIER_CODE", courier_info[:courier_code])
47
+ info = courier_info.dup
48
+ info.delete(:tracking_numbers)
49
+ klass.const_set("COURIER_INFO", info)
50
+
51
+ pattern = tracking_info[:regex]
52
+ pattern = tracking_info[:regex].join if tracking_info[:regex].is_a?(Array)
53
+
54
+ verify_pattern = "^#{pattern}$"
55
+ search_pattern = "\\b#{pattern}\\b"
56
+
57
+ klass.const_set("SEARCH_PATTERN", Regexp.new(search_pattern))
58
+ klass.const_set("VERIFY_PATTERN", Regexp.new(verify_pattern))
59
+
60
+ klass.const_set("VALIDATION", tracking_info[:validation])
61
+ klass.const_set("ADDITIONAL", tracking_info[:additional])
62
+ klass.const_set("TRACKING_URL", tracking_info[:tracking_url])
63
+
64
+ return klass
65
+ end
66
+
67
+ def register_class(klass, tracking_name)
68
+ klass_name = tracking_name.gsub(/[^0-9A-Za-z]/, '')
69
+ return TrackingNumber.const_set(klass_name, klass)
70
+ end
71
+ end
72
+ end
73
+ end
@@ -1,3 +1,3 @@
1
1
  module TrackingNumber
2
- VERSION = "1.0.3"
2
+ VERSION = "1.0.4"
3
3
  end
@@ -21,13 +21,6 @@ class TrackingNumberMetaTest < Minitest::Test
21
21
 
22
22
  describe "[#{tracking_info[:name]}]" do
23
23
  tracking_info[:test_numbers][:valid].each do |valid_number|
24
-
25
- should "validate #{valid_number} with #{klass_name}" do
26
- t = klass.new(valid_number)
27
- assert_equal courier_code, t.carrier
28
- assert t.valid?, "should be valid"
29
- end
30
-
31
24
  should "detect #{valid_number} as #{klass_name}" do
32
25
  #TODO fix this multiple matching thing
33
26
  matches = TrackingNumber.search(valid_number)
@@ -45,47 +38,53 @@ class TrackingNumberMetaTest < Minitest::Test
45
38
  should_detect_number_variants(valid_number, "TrackingNumber::#{klass_name}".constantize)
46
39
  end
47
40
 
48
- should "return correct courier code on #{valid_number} when calling #courier_code" do
49
- t = klass.new(valid_number)
50
- assert_equal courier_info[:courier_code].to_sym, t.courier_code
51
- assert_equal courier_info[:courier_code].to_sym, t.courier_code
52
- end
41
+ context "number test" do
42
+ tracking_number = klass.new(valid_number)
53
43
 
54
- should "return correct courier name on #{valid_number} when calling #courier_name" do
55
- t = klass.new(valid_number)
44
+ should "validate #{valid_number} with #{klass_name}" do
45
+ assert_equal courier_code, tracking_number.carrier
46
+ assert tracking_number.valid?, "should be valid"
47
+ end
56
48
 
57
- if (t.matching_additional["Courier"])
58
- assert_equal t.matching_additional["Courier"][:courier], t.courier_name
59
- else
60
- assert_equal courier_name, t.courier_name
49
+ should "return correct courier code on #{valid_number} when calling #courier_code" do
50
+ tracking_number = klass.new(valid_number)
51
+ assert_equal courier_info[:courier_code].to_sym, tracking_number.courier_code
52
+ assert_equal courier_info[:courier_code].to_sym,tracking_number.courier_code
61
53
  end
62
- end
63
54
 
64
- should "not throw an error when calling #service_type on #{valid_number}" do
65
- t = klass.new(valid_number)
66
- service_type = t.service_type
67
- assert service_type.is_a?(String) || service_type.nil?
68
- end
55
+ should "return correct courier name on #{valid_number} when calling #courier_name" do
56
+ if (tracking_number.matching_additional["Courier"])
57
+ assert_equal tracking_number.matching_additional["Courier"][:courier], tracking_number.courier_name
58
+ else
59
+ assert_equal courier_name, tracking_number.courier_name
60
+ end
61
+ end
69
62
 
70
- should "not throw an error when calling #destination on #{valid_number}" do
71
- t = klass.new(valid_number)
72
- assert t.destination_zip.is_a?(String) || t.destination_zip.nil?
73
- end
63
+ should "not throw an error when calling #service_type on #{valid_number}" do
64
+ service_type = tracking_number.service_type
65
+ assert service_type.is_a?(String) || service_type.nil?
66
+ end
74
67
 
75
- should "not throw an error when calling #shipper on #{valid_number}" do
76
- t = klass.new(valid_number)
77
- assert t.shipper_id.is_a?(String) || t.shipper_id.nil?
78
- end
68
+ should "not throw an error when calling #destination on #{valid_number}" do
69
+ assert tracking_number.destination_zip.is_a?(String) || tracking_number.destination_zip.nil?
70
+ end
79
71
 
80
- should "not throw an error when calling #package_type on #{valid_number}" do
81
- t = klass.new(valid_number)
82
- assert t.package_type.is_a?(String) || t.package_type.nil?
83
- end
72
+ should "not throw an error when calling #shipper on #{valid_number}" do
73
+ t = klass.new(valid_number)
74
+ assert tracking_number.shipper_id.is_a?(String) || tracking_number.shipper_id.nil?
75
+ end
76
+
77
+ should "not throw an error when calling #package_type on #{valid_number}" do
78
+ t = klass.new(valid_number)
79
+ assert tracking_number.package_type.is_a?(String) || tracking_number.package_type.nil?
80
+ end
81
+
82
+ should "not throw an error when calling #decode on #{valid_number}" do
83
+ t = klass.new(valid_number)
84
+ decode = tracking_number.decode
85
+ assert decode.is_a?(Hash)
86
+ end
84
87
 
85
- should "not throw an error when calling #decode on #{valid_number}" do
86
- t = klass.new(valid_number)
87
- decode = t.decode
88
- assert decode.is_a?(Hash)
89
88
  end
90
89
  end
91
90
 
@@ -64,6 +64,11 @@ class TrackingNumberTest < Minitest::Test
64
64
  should "report correct no package info" do
65
65
  assert_nil tracking_number.package_type
66
66
  end
67
+
68
+ should "have valid tracking url" do
69
+ assert tracking_number.tracking_url, "Tracking url should not be blank"
70
+ assert tracking_number.tracking_url.include?(tracking_number.tracking_number), "Should include tracking number in the url"
71
+ end
67
72
  end
68
73
 
69
74
  context "tracking number additional data for s10" do
@@ -112,6 +117,11 @@ class TrackingNumberTest < Minitest::Test
112
117
  should "report correct no package info" do
113
118
  assert_nil tracking_number.package_type
114
119
  end
120
+
121
+ should "have valid tracking url" do
122
+ assert tracking_number.tracking_url, "Tracking url should not be blank"
123
+ assert tracking_number.tracking_url.include?(tracking_number.tracking_number), "Should include tracking number in the url"
124
+ end
115
125
  end
116
126
 
117
127
  context "tracking number additional data for USPS 34v2" do
@@ -136,5 +146,10 @@ class TrackingNumberTest < Minitest::Test
136
146
  should "report correct no package info" do
137
147
  assert_nil tracking_number.package_type
138
148
  end
149
+
150
+ should "have valid tracking url" do
151
+ assert tracking_number.tracking_url, "Tracking url should not be blank"
152
+ assert tracking_number.tracking_url.include?(tracking_number.tracking_number), "Should include tracking number in the url"
153
+ end
139
154
  end
140
155
  end
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: 1.0.3
4
+ version: 1.0.4
5
5
  platform: ruby
6
6
  authors:
7
7
  - Jeff Keen
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2018-02-06 00:00:00.000000000 Z
11
+ date: 2018-09-13 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: activesupport
@@ -139,7 +139,6 @@ files:
139
139
  - LICENSE.txt
140
140
  - README.md
141
141
  - Rakefile
142
- - lib/checksum_validations.rb
143
142
  - lib/data/couriers/amazon.json
144
143
  - lib/data/couriers/dhl.json
145
144
  - lib/data/couriers/fedex.json
@@ -150,7 +149,9 @@ files:
150
149
  - lib/tracking_number.rb
151
150
  - lib/tracking_number/active_model_validator.rb
152
151
  - lib/tracking_number/base.rb
152
+ - lib/tracking_number/checksum_validations.rb
153
153
  - lib/tracking_number/info.rb
154
+ - lib/tracking_number/loader.rb
154
155
  - lib/tracking_number/unknown.rb
155
156
  - lib/tracking_number/version.rb
156
157
  - test/active_model_validator_test.rb
@@ -182,4 +183,8 @@ rubygems_version: 2.7.4
182
183
  signing_key:
183
184
  specification_version: 4
184
185
  summary: Identifies valid tracking numbers
185
- test_files: []
186
+ test_files:
187
+ - test/active_model_validator_test.rb
188
+ - test/test_helper.rb
189
+ - test/tracking_number_meta_test.rb
190
+ - test/tracking_number_test.rb
@@ -1,63 +0,0 @@
1
- module ChecksumValidations
2
- class << self
3
- def validates_s10?(sequence, check_digit, extras = {})
4
- weighting = [8,6,4,2,3,5,9,7]
5
-
6
- total = 0
7
- sequence.chars.to_a.zip(weighting).each do |(a,b)|
8
- total += a.to_i * b.to_i
9
- end
10
-
11
- remainder = total % 11
12
- check = case remainder
13
- when 1
14
- 0
15
- when 0
16
- 5
17
- else
18
- 11 - remainder
19
- end
20
-
21
- return check.to_i == check_digit.to_i
22
- end
23
-
24
- def validates_sum_product_with_weightings_and_modulo?(sequence, check_digit, extras = {})
25
- weighting = extras[:weightings] || []
26
-
27
- total = 0
28
- sequence.chars.to_a.zip(weighting).each do |(a,b)|
29
- total += a.to_i * b
30
- end
31
- return (total % extras[:modulo1] % extras[:modulo2]) == check_digit.to_i
32
- end
33
-
34
- def validates_mod10?(sequence, check_digit, extras = {})
35
- total = 0
36
- sequence.chars.each_with_index do |c, i|
37
- x = if c[/[0-9]/] # numeric
38
- c.to_i
39
- else
40
- (c[0].ord - 3) % 10
41
- end
42
-
43
- if extras[:odds_multiplier] && i.odd?
44
- x *= extras[:odds_multiplier].to_i
45
- elsif extras[:evens_multiplier] && i.even?
46
- x *= extras[:evens_multiplier].to_i
47
- end
48
-
49
- total += x
50
- end
51
-
52
- check = (total % 10)
53
- check = (10 - check) unless (check.zero?)
54
-
55
- return (check.to_i == check_digit.to_i)
56
- end
57
-
58
- def validates_mod7?(sequence, check_digit, extras = {})
59
- # standard mod 7 check
60
- return true if sequence.to_i % 7 == check_digit.to_i
61
- end
62
- end
63
- end