usps_intelligent_barcode 0.3.0 → 0.3.1

Sign up to get free protection for your applications and to get access to all the features.
@@ -1,52 +0,0 @@
1
- require 'USPS-intelligent-barcode/numeric_conversions'
2
-
3
- module Imb
4
-
5
- # @!group Internal
6
-
7
- # Calculates the Intelligent Mail Barcode CRC.
8
-
9
- class Crc
10
-
11
- include NumericConversions
12
-
13
- # Calculate a CRC.
14
- # @param [Integer] binary_data A 102-bit integer
15
- # @return [Integer] An 11-bit CRC
16
-
17
- def crc(binary_data)
18
- crc = MASK
19
- bytes = numeric_to_bytes(binary_data, NUM_INPUT_BYTES)
20
- crc = crc_byte(crc, bytes.first, LEADING_BITS_TO_IGNORE)
21
- for byte in bytes[1...NUM_INPUT_BYTES]
22
- crc = crc_byte(crc, byte, 0)
23
- end
24
- crc
25
- end
26
-
27
- private
28
-
29
- LEADING_BITS_TO_IGNORE = 2
30
- CRC_BITS = 11
31
- CRC_MSB_MASK = 1 << (CRC_BITS - 1)
32
- BITS_PER_BYTE = 8
33
- POLYNOMIAL = 0x0F35
34
- MASK = (1 << CRC_BITS) - 1
35
- NUM_INPUT_BYTES = 13
36
-
37
- def crc_byte(crc, byte, leading_bits_to_ignore)
38
- num_bits = BITS_PER_BYTE - leading_bits_to_ignore
39
- data = byte << CRC_BITS - BITS_PER_BYTE + leading_bits_to_ignore
40
- num_bits.times do
41
- use_polynomial = (crc ^ data) & CRC_MSB_MASK
42
- crc <<= 1
43
- crc ^= POLYNOMIAL if use_polynomial != 0
44
- crc &= MASK
45
- data <<= 1
46
- end
47
- crc
48
- end
49
-
50
- end
51
-
52
- end
@@ -1,105 +0,0 @@
1
- module Imb
2
-
3
- # This class represents a mailer ID.
4
-
5
- class MailerId
6
-
7
- # The allowable range for a short (6-digit) mailer ID
8
- SHORT_RANGE = 0..899_999
9
-
10
- # The allowable range for a long (9-digit) mailer ID
11
- LONG_RANGE = 900_000_000..999_999_999
12
-
13
- # The list of all allowable ranges for a mailer ID
14
- RANGES = [SHORT_RANGE, LONG_RANGE]
15
-
16
- # Turn the argument into a MailerID if possible. Accepts:
17
- # * {MailerId}
18
- # * String
19
- # * Integer
20
- # @return [MailerId]
21
- # @raise [ArgumentError] If the argument cannot be coerced
22
-
23
- def self.coerce(o)
24
- case o
25
- when MailerId
26
- o
27
- when String
28
- new(o.to_i)
29
- when Integer
30
- new(o)
31
- else
32
- raise ArgumentError, 'Cannot coerce to MailerId'
33
- end
34
- end
35
-
36
- # @param [Integer] value
37
-
38
- def initialize(value)
39
- @value = value
40
- end
41
-
42
- # Return true if this object is equal to o
43
- # @param [Object] o Any object acceptable to {.coerce}
44
-
45
- def ==(o)
46
- MailerId.coerce(o).to_i == to_i
47
- rescue ArgumentError
48
- false
49
- end
50
-
51
- # @return [Integer] The value of the mailer ID
52
-
53
- def to_i
54
- @value
55
- end
56
-
57
- # @!group Internal
58
-
59
- # Return true if this is a long (9 digit) mailer ID
60
-
61
- def long?
62
- LONG_RANGE === @value
63
- end
64
-
65
- # Validate the value.
66
- # @param long_mailer_id truthy if the mailer ID is long (9 digits).
67
- # @raise ArgumentError if invalid
68
-
69
- def validate(long_mailer_id)
70
- unless in_range?
71
- raise ArgumentError, "Must be #{RANGES.join(' or ')}"
72
- end
73
- end
74
-
75
- # Add this object's value to target, shifting it left as many
76
- # digts as are needed to make room.
77
- # @param [Integer] target The target to be shifted and added to
78
- # @param long_mailer_id truthy if the mailer ID is long (9 digits).
79
- # @return [Integer] The new value of the target
80
-
81
- def shift_and_add_to(target, long_mailer_id)
82
- target * 10 ** num_digits + to_i
83
- end
84
-
85
- # @!endgroup
86
-
87
- private
88
-
89
- def in_range?
90
- RANGES.any? do |range|
91
- range === @value
92
- end
93
- end
94
-
95
- def num_digits
96
- if long?
97
- 9
98
- else
99
- 6
100
- end
101
- end
102
-
103
- end
104
-
105
- end
@@ -1,22 +0,0 @@
1
- module Imb
2
-
3
- # @!group Internal
4
-
5
- # Numeric conversions
6
-
7
- module NumericConversions
8
-
9
- # Convert a numeric to an array of at least +min_bytes+ bytes.
10
- # @param [Numeric] n
11
- # @param [Integer] min_bytes
12
- # @return [[Integer]] Array of bytes
13
-
14
- def numeric_to_bytes(n, min_bytes=0)
15
- n.to_s(16).rjust(2 * min_bytes, '0').scan(/../).map do |s|
16
- s.to_i(16)
17
- end
18
- end
19
-
20
- end
21
-
22
- end
@@ -1,134 +0,0 @@
1
- module Imb
2
-
3
- # Represents a routing code
4
-
5
- class RoutingCode
6
-
7
- # Turn the argument into a RoutingCode if possible. Accepts:
8
- # * {RoutingCode}
9
- # * nil (no routing code)
10
- # * String of length:
11
- # * 0 - no routing code
12
- # * 5 - zip
13
- # * 9 - zip + plus4
14
- # * 11 - zip + plus4 + delivery point
15
- # * Array of [zip, plus4, delivery point]
16
- # @return [RoutingCode]
17
-
18
- def self.coerce(o)
19
- case o
20
- when nil
21
- coerce('')
22
- when RoutingCode
23
- o
24
- when Array
25
- RoutingCode.new(*o)
26
- when String
27
- RoutingCode.new(*string_to_array(o))
28
- else
29
- raise ArgumentError, 'Cannot coerce to RoutingCode'
30
- end
31
- end
32
-
33
- # @return [Integer] The ZIP (or nil)
34
- attr_accessor :zip
35
-
36
- # @return [Integer] The plus4 (or nil)
37
- attr_accessor :plus4
38
-
39
- # @return [Integer] The delivery point (or nil)
40
- attr_accessor :delivery_point
41
-
42
- # Create a RoutingCode. Arguments are:
43
- # * +zip+ - Integer zip (or nil)
44
- # * +plus4+ - Integer plus4 (or nil)
45
- # * +delivery_point+ - Integer delivery poitn (or nil)
46
-
47
- def initialize(zip, plus4, delivery_point)
48
- @zip = arg_to_i(zip)
49
- @plus4 = arg_to_i(plus4)
50
- @delivery_point = arg_to_i(delivery_point)
51
- end
52
-
53
- # Return true if this object is equal to o
54
- # @param [Object] o Any object acceptable to {.coerce}
55
-
56
- def ==(o)
57
- RoutingCode.coerce(o).to_a == to_a
58
- rescue ArgumentError
59
- false
60
- end
61
-
62
- # @!group Internal
63
-
64
- # Convert a string representation of a routing code into
65
- # an array that can be passed to the constructor.
66
- # +s+ is a string of length:
67
- # * 0 - no routing code
68
- # * 5 - zip
69
- # * 9 - zip + plus4
70
- # * 11 - zip + plus4 + delivery point
71
- # The result is an array of [zip, zip4, delivery point]
72
-
73
- def self.string_to_array(s)
74
- s = s.gsub(/[\D]/, '')
75
- match = /^(?:(\d{5})(?:(\d{4})(\d{2})?)?)?$/.match(s)
76
- unless match
77
- raise ArgumentError, "Bad routing code: #{s.inspect}"
78
- end
79
- zip, plus4, delivery_point = match.to_a[1..-1]
80
- [zip, plus4, delivery_point]
81
- end
82
-
83
- # Validate the value.
84
- # @param long_mailer_id truthy if the mailer ID is long (9 digits).
85
- # @raise ArgumentError if invalid
86
-
87
- def validate(long_mailer_id)
88
- end
89
-
90
- # Add this object's value to target, shifting it left as many
91
- # digts as are needed to make room.
92
- # @param [Integer] target The target to be shifted and added to
93
- # @param long_mailer_id truthy if the mailer ID is long (9 digits).
94
- # @return [Integer] The new value of the target
95
-
96
- def shift_and_add_to(target, long_mailer_id)
97
- target * 10 ** NUM_DIGITS + convert
98
- end
99
-
100
- # @!endgroup
101
-
102
- protected
103
-
104
- # Convert to an array of [zip, plus4, delivery point]
105
-
106
- def to_a
107
- [@zip, @plus4, @delivery_point]
108
- end
109
-
110
- private
111
-
112
- NUM_DIGITS = 11 #:nodoc:
113
-
114
- def arg_to_i(o)
115
- o.andand.to_i
116
- end
117
-
118
- # Convert to an integer value
119
-
120
- def convert
121
- if @zip && @plus4 && @delivery_point
122
- @zip * 1000000 + @plus4 * 100 + @delivery_point + 1000100001
123
- elsif @zip && @plus4
124
- @zip * 10000 + @plus4 + 100001
125
- elsif @zip
126
- @zip + 1
127
- else
128
- 0
129
- end
130
- end
131
-
132
- end
133
-
134
- end
@@ -1,88 +0,0 @@
1
- module Imb
2
-
3
- # This class represents the mail piece's serial number.
4
-
5
- class SerialNumber
6
-
7
- # Turn the argument into a SerialNumber if possible. Accepts:
8
- # * {SerialNumber}
9
- # * String
10
- # * Integer
11
- # @return [SerialNumber]
12
-
13
- def self.coerce(o)
14
- case o
15
- when SerialNumber
16
- o
17
- when String
18
- new(o.to_i)
19
- when Integer
20
- new(o)
21
- else
22
- raise ArgumentError, 'Cannot coerce to SerialNumber'
23
- end
24
- end
25
-
26
- # @param [Integer] value
27
-
28
- def initialize(value)
29
- @value = value
30
- end
31
-
32
- # Return true if this object is equal to o
33
- # @param [Object] o Any object acceptable to {.coerce}
34
-
35
- def ==(o)
36
- SerialNumber.coerce(o).to_i == to_i
37
- rescue ArgumentError
38
- false
39
- end
40
-
41
- # @return [Integer] The value of the serial number
42
-
43
- def to_i
44
- @value
45
- end
46
-
47
- # @!group Internal
48
-
49
- # Validate the value.
50
- # @param long_mailer_id truthy if the mailer ID is long (9 digits).
51
- # @raise ArgumentError if invalid
52
-
53
- def validate(long_mailer_id)
54
- range = 0..max_value(long_mailer_id)
55
- unless range === @value
56
- raise ArgumentError, "Must be #{range}"
57
- end
58
- end
59
-
60
- # Add this object's value to target, shifting it left as many
61
- # digts as are needed to make room.
62
- # @param [Integer] target The target to be shifted and added to
63
- # @param long_mailer_id truthy if the mailer ID is long (9 digits).
64
- # @return [Integer] The new value of the target
65
-
66
- def shift_and_add_to(target, long_mailer_id)
67
- target * 10 ** num_digits(long_mailer_id) + to_i
68
- end
69
-
70
- # @!endgroup
71
-
72
- private
73
-
74
- def max_value(long_mailer_id)
75
- max_value = 10 ** num_digits(long_mailer_id) - 1
76
- end
77
-
78
- def num_digits(long_mailer_id)
79
- if long_mailer_id
80
- 6
81
- else
82
- 9
83
- end
84
- end
85
-
86
- end
87
-
88
- end
@@ -1,79 +0,0 @@
1
- module Imb
2
-
3
- # This class represents a service type.
4
-
5
- class ServiceType
6
-
7
- # The valid range of a service type
8
- RANGE = 0..999
9
-
10
- # Turn the argument into a ServiceType if possible. Accepts:
11
- # * {ServiceType}
12
- # * String
13
- # * Integer
14
-
15
- def self.coerce(o)
16
- case o
17
- when ServiceType
18
- o
19
- when String
20
- new(o.to_i)
21
- when Integer
22
- new(o)
23
- else
24
- raise ArgumentError, 'Cannot coerce to ServiceType'
25
- end
26
- end
27
-
28
- # @param [Integer] value
29
-
30
- def initialize(value)
31
- @value = value
32
- end
33
-
34
- # Return true if this object is equal to o
35
- # @param [Object] o Any object acceptable to {.coerce}
36
-
37
- def ==(o)
38
- ServiceType.coerce(o).to_i == to_i
39
- rescue ArgumentError
40
- false
41
- end
42
-
43
- # @return [Integer] The value of the service type
44
-
45
- def to_i
46
- @value
47
- end
48
-
49
- # @!group Internal
50
-
51
- # Validate the value.
52
- # @param long_mailer_id truthy if the mailer ID is long (9 digits).
53
- # @raise ArgumentError if invalid
54
-
55
- def validate(long_mailer_id)
56
- unless (RANGE) === @value
57
- raise ArgumentError, "Must be #{RANGE}"
58
- end
59
- end
60
-
61
- # Add this object's value to target, shifting it left as many
62
- # digts as are needed to make room.
63
- # @param [Integer] target The target to be shifted and added to
64
- # @param long_mailer_id truthy if the mailer ID is long (9 digits).
65
- # @return [Integer] The new value of the target
66
-
67
- def shift_and_add_to(target, long_mailer_id)
68
- target * 10 ** NUM_DIGITS + to_i
69
- end
70
-
71
- # @!endgroup
72
-
73
- private
74
-
75
- NUM_DIGITS = 3 #:nodoc:
76
-
77
- end
78
-
79
- end