usps_intelligent_barcode 0.3.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 +7 -0
- data/.travis.yml +8 -0
- data/CHANGELOG.markdown +67 -0
- data/Gemfile +13 -0
- data/Gemfile.lock +90 -0
- data/LICENSE.md +11 -0
- data/README.markdown +64 -0
- data/Rakefile +40 -0
- data/VERSION +1 -0
- data/examples/example.rb +17 -0
- data/lib/USPS-intelligent-barcode.rb +17 -0
- data/lib/USPS-intelligent-barcode/bar_map.rb +52 -0
- data/lib/USPS-intelligent-barcode/bar_position.rb +40 -0
- data/lib/USPS-intelligent-barcode/bar_symbol.rb +49 -0
- data/lib/USPS-intelligent-barcode/bar_to_character_mapping.yml +66 -0
- data/lib/USPS-intelligent-barcode/barcode.rb +152 -0
- data/lib/USPS-intelligent-barcode/barcode_id.rb +98 -0
- data/lib/USPS-intelligent-barcode/character_position.rb +28 -0
- data/lib/USPS-intelligent-barcode/codeword_map.rb +38 -0
- data/lib/USPS-intelligent-barcode/codeword_to_character_mapping.yml +1366 -0
- data/lib/USPS-intelligent-barcode/crc.rb +52 -0
- data/lib/USPS-intelligent-barcode/mailer_id.rb +105 -0
- data/lib/USPS-intelligent-barcode/numeric_conversions.rb +22 -0
- data/lib/USPS-intelligent-barcode/routing_code.rb +134 -0
- data/lib/USPS-intelligent-barcode/serial_number.rb +88 -0
- data/lib/USPS-intelligent-barcode/service_type.rb +79 -0
- data/lib/usps_intelligent_barcode.rb +17 -0
- data/lib/usps_intelligent_barcode/bar_map.rb +52 -0
- data/lib/usps_intelligent_barcode/bar_position.rb +40 -0
- data/lib/usps_intelligent_barcode/bar_symbol.rb +49 -0
- data/lib/usps_intelligent_barcode/bar_to_character_mapping.yml +66 -0
- data/lib/usps_intelligent_barcode/barcode.rb +152 -0
- data/lib/usps_intelligent_barcode/barcode_id.rb +98 -0
- data/lib/usps_intelligent_barcode/character_position.rb +28 -0
- data/lib/usps_intelligent_barcode/codeword_map.rb +38 -0
- data/lib/usps_intelligent_barcode/codeword_to_character_mapping.yml +1366 -0
- data/lib/usps_intelligent_barcode/crc.rb +52 -0
- data/lib/usps_intelligent_barcode/mailer_id.rb +105 -0
- data/lib/usps_intelligent_barcode/numeric_conversions.rb +22 -0
- data/lib/usps_intelligent_barcode/routing_code.rb +134 -0
- data/lib/usps_intelligent_barcode/serial_number.rb +88 -0
- data/lib/usps_intelligent_barcode/service_type.rb +79 -0
- data/spec/bar_map_spec.rb +30 -0
- data/spec/bar_position_spec.rb +40 -0
- data/spec/bar_symbol_spec.rb +39 -0
- data/spec/barcode_id_spec.rb +106 -0
- data/spec/barcode_spec.rb +213 -0
- data/spec/character_position_spec.rb +25 -0
- data/spec/codeword_map_spec.rb +22 -0
- data/spec/crc_spec.rb +21 -0
- data/spec/mailer_id_spec.rb +124 -0
- data/spec/numeric_conversions_spec.rb +23 -0
- data/spec/routing_code_spec.rb +180 -0
- data/spec/serial_number_spec.rb +117 -0
- data/spec/service_type_spec.rb +93 -0
- data/spec/spec_helper.rb +8 -0
- data/usps_intelligent_barcode.gemspec +117 -0
- metadata +216 -0
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
require 'andand'
|
|
2
|
+
require 'memoizer'
|
|
3
|
+
require 'yaml'
|
|
4
|
+
|
|
5
|
+
require 'usps_intelligent_barcode/bar_map'
|
|
6
|
+
require 'usps_intelligent_barcode/bar_position'
|
|
7
|
+
require 'usps_intelligent_barcode/bar_symbol'
|
|
8
|
+
require 'usps_intelligent_barcode/barcode'
|
|
9
|
+
require 'usps_intelligent_barcode/barcode_id'
|
|
10
|
+
require 'usps_intelligent_barcode/character_position'
|
|
11
|
+
require 'usps_intelligent_barcode/codeword_map'
|
|
12
|
+
require 'usps_intelligent_barcode/crc'
|
|
13
|
+
require 'usps_intelligent_barcode/mailer_id'
|
|
14
|
+
require 'usps_intelligent_barcode/numeric_conversions'
|
|
15
|
+
require 'usps_intelligent_barcode/routing_code'
|
|
16
|
+
require 'usps_intelligent_barcode/serial_number'
|
|
17
|
+
require 'usps_intelligent_barcode/service_type'
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
require 'usps_intelligent_barcode/character_position'
|
|
2
|
+
|
|
3
|
+
# @!group Internal
|
|
4
|
+
|
|
5
|
+
module Imb
|
|
6
|
+
|
|
7
|
+
# Maps intelligent barcode "characters" to codes that indicate what
|
|
8
|
+
# type of bar to print at each given position.
|
|
9
|
+
|
|
10
|
+
class BarMap
|
|
11
|
+
|
|
12
|
+
def initialize
|
|
13
|
+
@mapping = load_mapping
|
|
14
|
+
end
|
|
15
|
+
|
|
16
|
+
# Given an array of intelligent barcode "characters", return an
|
|
17
|
+
# the symbols for each position.
|
|
18
|
+
# @param [[Integer]] characters array of characters
|
|
19
|
+
# @return [[BarSymbol]] array of symbols
|
|
20
|
+
|
|
21
|
+
def symbols(characters)
|
|
22
|
+
@mapping.map do |bar_position|
|
|
23
|
+
bar_position.map(characters)
|
|
24
|
+
end
|
|
25
|
+
end
|
|
26
|
+
|
|
27
|
+
private
|
|
28
|
+
|
|
29
|
+
def load_mapping
|
|
30
|
+
convert_mapping_data(load_mapping_data)
|
|
31
|
+
end
|
|
32
|
+
|
|
33
|
+
def convert_mapping_data(mapping_data)
|
|
34
|
+
mapping_data.map do |descender, ascender|
|
|
35
|
+
descender_character_position = CharacterPosition.new(*descender)
|
|
36
|
+
ascender_character_position = CharacterPosition.new(*ascender)
|
|
37
|
+
BarPosition.new(descender_character_position,
|
|
38
|
+
ascender_character_position)
|
|
39
|
+
end
|
|
40
|
+
end
|
|
41
|
+
|
|
42
|
+
def load_mapping_data
|
|
43
|
+
YAML.load_file(mapping_path)
|
|
44
|
+
end
|
|
45
|
+
|
|
46
|
+
def mapping_path
|
|
47
|
+
File.expand_path('bar_to_character_mapping.yml', File.dirname(__FILE__))
|
|
48
|
+
end
|
|
49
|
+
|
|
50
|
+
end
|
|
51
|
+
|
|
52
|
+
end
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
module Imb
|
|
2
|
+
|
|
3
|
+
# @!group Internal
|
|
4
|
+
|
|
5
|
+
# Represents a position (one line) in the barcode. This class is
|
|
6
|
+
# internal and may change.
|
|
7
|
+
|
|
8
|
+
class BarPosition
|
|
9
|
+
|
|
10
|
+
# @param [CharacterPosition] descender_character_position
|
|
11
|
+
# @param [CharacterPosition] ascender_character_position
|
|
12
|
+
|
|
13
|
+
def initialize(descender_character_position, ascender_character_position)
|
|
14
|
+
@descender_character_position = descender_character_position
|
|
15
|
+
@ascender_character_position = ascender_character_position
|
|
16
|
+
end
|
|
17
|
+
|
|
18
|
+
# Given an array of characters, return a symbol for this
|
|
19
|
+
# barcode position.
|
|
20
|
+
# @param [[Integer]] characters character codes
|
|
21
|
+
# @return [BarSymbol] symbol code
|
|
22
|
+
|
|
23
|
+
def map(characters)
|
|
24
|
+
BarSymbol.make(ascender_bit(characters),
|
|
25
|
+
descender_bit(characters))
|
|
26
|
+
end
|
|
27
|
+
|
|
28
|
+
private
|
|
29
|
+
|
|
30
|
+
def descender_bit(characters)
|
|
31
|
+
@descender_character_position.extract_bit_from_characters(characters)
|
|
32
|
+
end
|
|
33
|
+
|
|
34
|
+
def ascender_bit(characters)
|
|
35
|
+
@ascender_character_position.extract_bit_from_characters(characters)
|
|
36
|
+
end
|
|
37
|
+
|
|
38
|
+
end
|
|
39
|
+
|
|
40
|
+
end
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
module Imb
|
|
2
|
+
|
|
3
|
+
# @!group Internal
|
|
4
|
+
|
|
5
|
+
# Represents a symbol in the barcode.
|
|
6
|
+
|
|
7
|
+
class BarSymbol
|
|
8
|
+
|
|
9
|
+
# @param [Integer] ascender_bit (0..1)
|
|
10
|
+
# @param [Integer] descender_bit (0..1)
|
|
11
|
+
# @return [BarSymbol]
|
|
12
|
+
|
|
13
|
+
def self.make(ascender_bit, descender_bit)
|
|
14
|
+
case [ascender_bit, descender_bit]
|
|
15
|
+
when [0, 0]
|
|
16
|
+
TRACKER
|
|
17
|
+
when [0, 1]
|
|
18
|
+
DESCENDER
|
|
19
|
+
when [1, 0]
|
|
20
|
+
ASCENDER
|
|
21
|
+
when [1, 1]
|
|
22
|
+
FULL
|
|
23
|
+
end
|
|
24
|
+
end
|
|
25
|
+
|
|
26
|
+
# @return [Integer] the code for this symbol
|
|
27
|
+
attr_reader :code
|
|
28
|
+
|
|
29
|
+
# @return [String] the letter for this symbol
|
|
30
|
+
attr_reader :letter
|
|
31
|
+
|
|
32
|
+
# @param [Integer] code (0..3)
|
|
33
|
+
# @param [String] letter
|
|
34
|
+
|
|
35
|
+
def initialize(code, letter)
|
|
36
|
+
@code = code
|
|
37
|
+
@letter = letter
|
|
38
|
+
end
|
|
39
|
+
|
|
40
|
+
private
|
|
41
|
+
|
|
42
|
+
TRACKER = BarSymbol.new(0, 'T')
|
|
43
|
+
DESCENDER = BarSymbol.new(1, 'D')
|
|
44
|
+
ASCENDER = BarSymbol.new(2, 'A')
|
|
45
|
+
FULL = BarSymbol.new(3, 'F')
|
|
46
|
+
|
|
47
|
+
end
|
|
48
|
+
|
|
49
|
+
end
|
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
---
|
|
2
|
+
- [[7, 2], [4, 3]]
|
|
3
|
+
- [[1, 10], [0, 0]]
|
|
4
|
+
- [[9, 12], [2, 8]]
|
|
5
|
+
- [[5, 5], [6, 11]]
|
|
6
|
+
- [[8, 9], [3, 1]]
|
|
7
|
+
- [[0, 1], [5, 12]]
|
|
8
|
+
- [[2, 5], [1, 8]]
|
|
9
|
+
- [[4, 4], [9, 11]]
|
|
10
|
+
- [[6, 3], [8, 10]]
|
|
11
|
+
- [[3, 9], [7, 6]]
|
|
12
|
+
- [[5, 11], [1, 4]]
|
|
13
|
+
- [[8, 5], [2, 12]]
|
|
14
|
+
- [[9, 10], [0, 2]]
|
|
15
|
+
- [[7, 1], [6, 7]]
|
|
16
|
+
- [[3, 6], [4, 9]]
|
|
17
|
+
- [[0, 3], [8, 6]]
|
|
18
|
+
- [[6, 4], [2, 7]]
|
|
19
|
+
- [[1, 1], [9, 9]]
|
|
20
|
+
- [[7, 10], [5, 2]]
|
|
21
|
+
- [[4, 0], [3, 8]]
|
|
22
|
+
- [[6, 2], [0, 4]]
|
|
23
|
+
- [[8, 11], [1, 0]]
|
|
24
|
+
- [[9, 8], [3, 12]]
|
|
25
|
+
- [[2, 6], [7, 7]]
|
|
26
|
+
- [[5, 1], [4, 10]]
|
|
27
|
+
- [[1, 12], [6, 9]]
|
|
28
|
+
- [[7, 3], [8, 0]]
|
|
29
|
+
- [[5, 8], [9, 7]]
|
|
30
|
+
- [[4, 6], [2, 10]]
|
|
31
|
+
- [[3, 4], [0, 5]]
|
|
32
|
+
- [[8, 4], [5, 7]]
|
|
33
|
+
- [[7, 11], [1, 9]]
|
|
34
|
+
- [[6, 0], [9, 6]]
|
|
35
|
+
- [[0, 6], [4, 8]]
|
|
36
|
+
- [[2, 1], [3, 2]]
|
|
37
|
+
- [[5, 9], [8, 12]]
|
|
38
|
+
- [[4, 11], [6, 1]]
|
|
39
|
+
- [[9, 5], [7, 4]]
|
|
40
|
+
- [[3, 3], [1, 2]]
|
|
41
|
+
- [[0, 7], [2, 0]]
|
|
42
|
+
- [[1, 3], [4, 1]]
|
|
43
|
+
- [[6, 10], [3, 5]]
|
|
44
|
+
- [[8, 7], [9, 4]]
|
|
45
|
+
- [[2, 11], [5, 6]]
|
|
46
|
+
- [[0, 8], [7, 12]]
|
|
47
|
+
- [[4, 2], [8, 1]]
|
|
48
|
+
- [[5, 10], [3, 0]]
|
|
49
|
+
- [[9, 3], [0, 9]]
|
|
50
|
+
- [[6, 5], [2, 4]]
|
|
51
|
+
- [[7, 8], [1, 7]]
|
|
52
|
+
- [[5, 0], [4, 5]]
|
|
53
|
+
- [[2, 3], [0, 10]]
|
|
54
|
+
- [[6, 12], [9, 2]]
|
|
55
|
+
- [[3, 11], [1, 6]]
|
|
56
|
+
- [[8, 8], [7, 9]]
|
|
57
|
+
- [[5, 4], [0, 11]]
|
|
58
|
+
- [[1, 5], [2, 2]]
|
|
59
|
+
- [[9, 1], [4, 12]]
|
|
60
|
+
- [[8, 3], [6, 6]]
|
|
61
|
+
- [[7, 0], [3, 7]]
|
|
62
|
+
- [[4, 7], [7, 5]]
|
|
63
|
+
- [[0, 12], [1, 11]]
|
|
64
|
+
- [[2, 9], [9, 0]]
|
|
65
|
+
- [[6, 8], [5, 3]]
|
|
66
|
+
- [[3, 10], [8, 2]]
|
|
@@ -0,0 +1,152 @@
|
|
|
1
|
+
require 'usps_intelligent_barcode/codeword_map'
|
|
2
|
+
require 'usps_intelligent_barcode/crc'
|
|
3
|
+
|
|
4
|
+
# The namespace for everything in this library.
|
|
5
|
+
|
|
6
|
+
module Imb
|
|
7
|
+
|
|
8
|
+
# This class represents a barcode.
|
|
9
|
+
|
|
10
|
+
class Barcode
|
|
11
|
+
|
|
12
|
+
include Memoizer
|
|
13
|
+
|
|
14
|
+
# @return [BarcodeId]
|
|
15
|
+
attr_reader :barcode_id
|
|
16
|
+
|
|
17
|
+
# @return [ServiceType]
|
|
18
|
+
attr_reader :service_type
|
|
19
|
+
|
|
20
|
+
# @return [MailerId]
|
|
21
|
+
attr_reader :mailer_id
|
|
22
|
+
|
|
23
|
+
# @return [SerialNumber]
|
|
24
|
+
attr_reader :serial_number
|
|
25
|
+
|
|
26
|
+
# @return [RoutingCode]
|
|
27
|
+
attr_reader :routing_code
|
|
28
|
+
|
|
29
|
+
# @param
|
|
30
|
+
|
|
31
|
+
# Create a new barcode
|
|
32
|
+
#
|
|
33
|
+
# @param barcode_id [String] Nominally a String, but can be
|
|
34
|
+
# anything that {BarcodeId.coerce} will accept.
|
|
35
|
+
# @param service_type [String] Nominally a String, but can be
|
|
36
|
+
# anything that {ServiceType.coerce} will accept.
|
|
37
|
+
# @param mailer_id [String] Nominally a String, but can be
|
|
38
|
+
# anything that {MailerId.coerce} will accept.
|
|
39
|
+
# @param serial_number [String] Nominally a String, but can be
|
|
40
|
+
# anything that {SerialNumber.coerce} will accept.
|
|
41
|
+
# @param routing_code [String] Nominally a String, but can be
|
|
42
|
+
# anything that {RoutingCode.coerce} will accept.
|
|
43
|
+
|
|
44
|
+
def initialize(barcode_id,
|
|
45
|
+
service_type,
|
|
46
|
+
mailer_id,
|
|
47
|
+
serial_number,
|
|
48
|
+
routing_code)
|
|
49
|
+
@barcode_id = BarcodeId.coerce(barcode_id)
|
|
50
|
+
@service_type = ServiceType.coerce(service_type)
|
|
51
|
+
@mailer_id = MailerId.coerce(mailer_id)
|
|
52
|
+
@serial_number = SerialNumber.coerce(serial_number)
|
|
53
|
+
@routing_code = RoutingCode.coerce(routing_code)
|
|
54
|
+
validate_components
|
|
55
|
+
end
|
|
56
|
+
|
|
57
|
+
# Return a string to print using one of the USPS Intelligent Mail
|
|
58
|
+
# Barcode fonts. Each character of the string will be one of:
|
|
59
|
+
# * 'T' for a tracking mark (neither ascender nor descender)
|
|
60
|
+
# * 'A' for an ascender mark
|
|
61
|
+
# * 'D' for a descender mark
|
|
62
|
+
# * 'F' for a full mark (both ascender and descender)
|
|
63
|
+
# @return [String] A string that represents the barcode.
|
|
64
|
+
|
|
65
|
+
def barcode_letters
|
|
66
|
+
symbols.map(&:letter).join
|
|
67
|
+
end
|
|
68
|
+
|
|
69
|
+
private
|
|
70
|
+
|
|
71
|
+
# :stopdoc:
|
|
72
|
+
BAR_MAP = BarMap.new
|
|
73
|
+
CODEWORD_MAP = CodewordMap.new
|
|
74
|
+
CRC = Crc.new
|
|
75
|
+
# :startdoc:
|
|
76
|
+
|
|
77
|
+
def validate_components
|
|
78
|
+
components.each do |component|
|
|
79
|
+
component.validate(long_mailer_id?)
|
|
80
|
+
end
|
|
81
|
+
end
|
|
82
|
+
|
|
83
|
+
def components
|
|
84
|
+
[
|
|
85
|
+
@routing_code,
|
|
86
|
+
@barcode_id,
|
|
87
|
+
@service_type,
|
|
88
|
+
@mailer_id,
|
|
89
|
+
@serial_number,
|
|
90
|
+
]
|
|
91
|
+
end
|
|
92
|
+
|
|
93
|
+
def long_mailer_id?
|
|
94
|
+
@mailer_id.long?
|
|
95
|
+
end
|
|
96
|
+
|
|
97
|
+
def binary_data
|
|
98
|
+
components.inject(0) do |data, component|
|
|
99
|
+
component.shift_and_add_to(data, long_mailer_id?)
|
|
100
|
+
end
|
|
101
|
+
end
|
|
102
|
+
memoize :binary_data
|
|
103
|
+
|
|
104
|
+
def frame_check_sequence
|
|
105
|
+
CRC.crc(binary_data)
|
|
106
|
+
end
|
|
107
|
+
memoize :frame_check_sequence
|
|
108
|
+
|
|
109
|
+
def codewords
|
|
110
|
+
codewords = []
|
|
111
|
+
data = binary_data
|
|
112
|
+
data, codewords[9] = data.divmod 636
|
|
113
|
+
8.downto(0) do |i|
|
|
114
|
+
data, codewords[i] = data.divmod 1365
|
|
115
|
+
end
|
|
116
|
+
codewords
|
|
117
|
+
end
|
|
118
|
+
memoize :codewords
|
|
119
|
+
|
|
120
|
+
def codewords_with_orientation_in_character_j
|
|
121
|
+
result = codewords.dup
|
|
122
|
+
result[9] *= 2
|
|
123
|
+
result
|
|
124
|
+
end
|
|
125
|
+
|
|
126
|
+
def codewords_with_fcs_bit_in_character_a
|
|
127
|
+
result = codewords_with_orientation_in_character_j.dup
|
|
128
|
+
result[0] += 659 if frame_check_sequence[10] == 1
|
|
129
|
+
result
|
|
130
|
+
end
|
|
131
|
+
|
|
132
|
+
def characters
|
|
133
|
+
CODEWORD_MAP.characters(codewords_with_fcs_bit_in_character_a)
|
|
134
|
+
end
|
|
135
|
+
|
|
136
|
+
def characters_with_fcs_bits_0_through_9
|
|
137
|
+
characters.each_with_index.map do |character, i|
|
|
138
|
+
if frame_check_sequence[i] == 1
|
|
139
|
+
character ^ 0b1111111111111
|
|
140
|
+
else
|
|
141
|
+
character
|
|
142
|
+
end
|
|
143
|
+
end
|
|
144
|
+
end
|
|
145
|
+
|
|
146
|
+
def symbols
|
|
147
|
+
BAR_MAP.symbols(characters_with_fcs_bits_0_through_9)
|
|
148
|
+
end
|
|
149
|
+
|
|
150
|
+
end
|
|
151
|
+
|
|
152
|
+
end
|
|
@@ -0,0 +1,98 @@
|
|
|
1
|
+
module Imb
|
|
2
|
+
|
|
3
|
+
# This class represents a Barcode ID
|
|
4
|
+
|
|
5
|
+
class BarcodeId
|
|
6
|
+
|
|
7
|
+
# The allowable range of a barcode ID
|
|
8
|
+
RANGE = 0..94
|
|
9
|
+
|
|
10
|
+
# The allowable range of a barcode ID's least significant digit
|
|
11
|
+
LSD_RANGE = 0..4
|
|
12
|
+
|
|
13
|
+
# Turn the argument into a BarcodeID if possible. Accepts any of:
|
|
14
|
+
# * {BarcodeId}
|
|
15
|
+
# * String
|
|
16
|
+
# * Integer
|
|
17
|
+
# @return [BarcodeId]
|
|
18
|
+
# @raise [ArgumentError] If the argument cannot be coerced
|
|
19
|
+
|
|
20
|
+
def self.coerce(o)
|
|
21
|
+
case o
|
|
22
|
+
when BarcodeId
|
|
23
|
+
o
|
|
24
|
+
when String
|
|
25
|
+
new(o.to_i)
|
|
26
|
+
when Integer
|
|
27
|
+
new(o)
|
|
28
|
+
else
|
|
29
|
+
raise ArgumentError, 'Cannot coerce to BarcodeId'
|
|
30
|
+
end
|
|
31
|
+
end
|
|
32
|
+
|
|
33
|
+
# Create a new BarcodeId
|
|
34
|
+
# @param [Integer] value The barcode ID
|
|
35
|
+
|
|
36
|
+
def initialize(value)
|
|
37
|
+
@value = value
|
|
38
|
+
end
|
|
39
|
+
|
|
40
|
+
# Return true if this object is equal to o
|
|
41
|
+
# @param [Object] o Any object acceptable to {.coerce}
|
|
42
|
+
|
|
43
|
+
def ==(o)
|
|
44
|
+
BarcodeId.coerce(o).to_i == to_i
|
|
45
|
+
rescue ArgumentError
|
|
46
|
+
false
|
|
47
|
+
end
|
|
48
|
+
|
|
49
|
+
# @return [Integer] The integer value of the barcode ID
|
|
50
|
+
|
|
51
|
+
def to_i
|
|
52
|
+
@value
|
|
53
|
+
end
|
|
54
|
+
|
|
55
|
+
# @!group Internal
|
|
56
|
+
|
|
57
|
+
# Validate the value.
|
|
58
|
+
# @param long_mailer_id truthy if the mailer ID is long (9 digits).
|
|
59
|
+
# @raise ArgumentError if invalid
|
|
60
|
+
|
|
61
|
+
def validate(long_mailer_id)
|
|
62
|
+
unless RANGE === @value
|
|
63
|
+
raise ArgumentError, "Must be #{RANGE}"
|
|
64
|
+
end
|
|
65
|
+
unless LSD_RANGE === least_significant_digit
|
|
66
|
+
raise ArgumentError, "Least significant digit must be #{LSD_RANGE}"
|
|
67
|
+
end
|
|
68
|
+
end
|
|
69
|
+
|
|
70
|
+
# Add this object's value to target, shifting it left as many
|
|
71
|
+
# digts as are needed to make room.
|
|
72
|
+
# @param [Integer] target The target to be shifted and added to
|
|
73
|
+
# @param long_mailer_id truthy if the mailer ID is long (9 digits).
|
|
74
|
+
# @return [Integer] The new value of the target
|
|
75
|
+
|
|
76
|
+
def shift_and_add_to(target, long_mailer_id)
|
|
77
|
+
target *= 10
|
|
78
|
+
target += most_significant_digit
|
|
79
|
+
target *= 5
|
|
80
|
+
target += least_significant_digit
|
|
81
|
+
target
|
|
82
|
+
end
|
|
83
|
+
|
|
84
|
+
# @!endgroup
|
|
85
|
+
|
|
86
|
+
private
|
|
87
|
+
|
|
88
|
+
def most_significant_digit
|
|
89
|
+
@value / 10
|
|
90
|
+
end
|
|
91
|
+
|
|
92
|
+
def least_significant_digit
|
|
93
|
+
@value % 10
|
|
94
|
+
end
|
|
95
|
+
|
|
96
|
+
end
|
|
97
|
+
|
|
98
|
+
end
|