tracking_number 1.0.3 → 1.0.4

Sign up to get free protection for your applications and to get access to all the features.
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