barcodes 0.0.1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (68) hide show
  1. data/.gitignore +5 -0
  2. data/.rspec +2 -0
  3. data/.rvmrc +1 -0
  4. data/Gemfile +4 -0
  5. data/LICENSE +22 -0
  6. data/README.md +107 -0
  7. data/Rakefile +11 -0
  8. data/barcodes.gemspec +24 -0
  9. data/bin/barcodes +6 -0
  10. data/lib/barcodes.rb +63 -0
  11. data/lib/barcodes/exec.rb +74 -0
  12. data/lib/barcodes/renderer.rb +9 -0
  13. data/lib/barcodes/renderer/ascii.rb +199 -0
  14. data/lib/barcodes/renderer/pdf.rb +257 -0
  15. data/lib/barcodes/symbology.rb +47 -0
  16. data/lib/barcodes/symbology/base.rb +103 -0
  17. data/lib/barcodes/symbology/codabar.rb +36 -0
  18. data/lib/barcodes/symbology/code11.rb +74 -0
  19. data/lib/barcodes/symbology/code128.rb +317 -0
  20. data/lib/barcodes/symbology/code39.rb +48 -0
  21. data/lib/barcodes/symbology/code39extended.rb +93 -0
  22. data/lib/barcodes/symbology/code39extendedmod43.rb +73 -0
  23. data/lib/barcodes/symbology/code39mod43.rb +73 -0
  24. data/lib/barcodes/symbology/code93.rb +142 -0
  25. data/lib/barcodes/symbology/code93extended.rb +116 -0
  26. data/lib/barcodes/symbology/ean.rb +149 -0
  27. data/lib/barcodes/symbology/ean13.rb +27 -0
  28. data/lib/barcodes/symbology/ean8.rb +50 -0
  29. data/lib/barcodes/symbology/interleaved2of5.rb +30 -0
  30. data/lib/barcodes/symbology/interleaved2of5mod10.rb +40 -0
  31. data/lib/barcodes/symbology/msi.rb +30 -0
  32. data/lib/barcodes/symbology/msimod10.rb +50 -0
  33. data/lib/barcodes/symbology/msimod11.rb +37 -0
  34. data/lib/barcodes/symbology/planet.rb +83 -0
  35. data/lib/barcodes/symbology/postnet.rb +83 -0
  36. data/lib/barcodes/symbology/standard2of5.rb +30 -0
  37. data/lib/barcodes/symbology/standard2of5mod10.rb +40 -0
  38. data/lib/barcodes/symbology/upca.rb +27 -0
  39. data/lib/barcodes/version.rb +3 -0
  40. data/spec/barcodes/exec_spec.rb +14 -0
  41. data/spec/barcodes/renderer/ascii_spec.rb +12 -0
  42. data/spec/barcodes/renderer/pdf_spec.rb +12 -0
  43. data/spec/barcodes/symbology/base_spec.rb +8 -0
  44. data/spec/barcodes/symbology/codabar_spec.rb +8 -0
  45. data/spec/barcodes/symbology/code11_spec.rb +8 -0
  46. data/spec/barcodes/symbology/code128_spec.rb +8 -0
  47. data/spec/barcodes/symbology/code39_spec.rb +8 -0
  48. data/spec/barcodes/symbology/code39extended_spec.rb +8 -0
  49. data/spec/barcodes/symbology/code39extendedmod43_spec.rb +8 -0
  50. data/spec/barcodes/symbology/code39mod43_spec.rb +8 -0
  51. data/spec/barcodes/symbology/code93_spec.rb +8 -0
  52. data/spec/barcodes/symbology/code93extended_spec.rb +8 -0
  53. data/spec/barcodes/symbology/ean13_spec.rb +8 -0
  54. data/spec/barcodes/symbology/ean8_spec.rb +8 -0
  55. data/spec/barcodes/symbology/ean_spec.rb +8 -0
  56. data/spec/barcodes/symbology/interleaved2of5_spec.rb +8 -0
  57. data/spec/barcodes/symbology/interleaved2of5mod10_spec.rb +8 -0
  58. data/spec/barcodes/symbology/msi_spec.rb +8 -0
  59. data/spec/barcodes/symbology/msimod10_spec.rb +8 -0
  60. data/spec/barcodes/symbology/msimod11_spec.rb +8 -0
  61. data/spec/barcodes/symbology/planet_spec.rb +8 -0
  62. data/spec/barcodes/symbology/postnet_spec.rb +8 -0
  63. data/spec/barcodes/symbology/standard2of5_spec.rb +8 -0
  64. data/spec/barcodes/symbology/standard2of5mod10_spec.rb +8 -0
  65. data/spec/barcodes/symbology/upca_spec.rb +8 -0
  66. data/spec/barcodes_spec.rb +27 -0
  67. data/spec/spec_helper.rb +17 -0
  68. metadata +174 -0
@@ -0,0 +1,257 @@
1
+ require 'prawn'
2
+
3
+ module Barcodes
4
+ module Renderer
5
+ class Pdf
6
+ attr_accessor :barcode
7
+
8
+ def initialize(barcode=nil)
9
+ @barcode = barcode
10
+ end
11
+
12
+ def render(filename=nil)
13
+ unless filename.nil?
14
+ self.pdf.render_file filename
15
+ else
16
+ self.pdf.render
17
+ end
18
+ end
19
+
20
+ def pdf
21
+ width = (@barcode.width * 0.001) * 72.0
22
+ height = (@barcode.height * 0.001) * 72.0
23
+
24
+ document_options = {
25
+ :page_size => [width, height],
26
+ :left_margin => 0,
27
+ :right_margin => 0,
28
+ :top_margin => 0,
29
+ :bottom_margin => 0
30
+ }
31
+
32
+ Prawn::Document.new document_options do |pdf|
33
+ if @barcode.class == Barcodes::Symbology::Ean8 || @barcode.class == Barcodes::Symbology::Ean13 || @barcode.class == Barcodes::Symbology::UpcA
34
+ self._draw_ean_upc(@barcode, pdf)
35
+ elsif @barcode.class == Barcodes::Symbology::Planet || @barcode.class == Barcodes::Symbology::Postnet
36
+ self._draw_planet_postnet(@barcode, pdf)
37
+ else
38
+ self._draw_standard(@barcode, pdf)
39
+ end
40
+ end
41
+ end
42
+
43
+ protected
44
+
45
+ def _draw_standard(barcode, pdf)
46
+ if barcode.valid?
47
+ pdf.save_graphics_state
48
+
49
+ pdf.fill_color barcode.color
50
+
51
+ pdf.transparent barcode.alpha do
52
+ quiet_zone_width_pixels = (barcode.quiet_zone_width * 0.001) * 72.0
53
+ barcode_box_width_pixels = ((barcode.encoded_data.length * barcode.bar_width) * 0.001) * 72.0
54
+ bar_height_pixels = (barcode.bar_height * 0.001) * 72.0
55
+ bar_width_pixels = (barcode.bar_width * 0.001) * 72.0
56
+ caption_size_pixels = ((barcode.caption_size * 0.001) * 72.0)
57
+ caption_height_pixels = ((barcode.caption_height * 0.001) * 72.0)
58
+ barcode_height_pixels = ((barcode.height * 0.001) * 72.0)
59
+
60
+ pdf.bounding_box([quiet_zone_width_pixels, barcode_height_pixels], :width => barcode_box_width_pixels, :height => bar_height_pixels) do
61
+ index = 0
62
+ bar_count = 0
63
+ origin = []
64
+ barcode.encoded_data.each_char do |char|
65
+ if char == '1'
66
+ if bar_count == 0
67
+ origin = [index * bar_width_pixels, bar_height_pixels]
68
+ end
69
+ bar_count += 1
70
+ else
71
+ pdf.fill_rectangle origin, bar_width_pixels * bar_count, bar_height_pixels
72
+ bar_count = 0
73
+ end
74
+ index += 1
75
+ end
76
+
77
+ if bar_count > 0
78
+ pdf.fill_rectangle origin, bar_width_pixels * bar_count, bar_height_pixels
79
+ end
80
+ end
81
+
82
+ if barcode.captioned
83
+ options = {
84
+ :at => [quiet_zone_width_pixels, caption_height_pixels],
85
+ :width => barcode_box_width_pixels,
86
+ :height => caption_height_pixels,
87
+ :overflow => :truncate,
88
+ :size => caption_size_pixels,
89
+ :fallback_fonts => ['Courier'],
90
+ :valign => :center,
91
+ :align => :center,
92
+ :style => :normal
93
+ }
94
+ pdf.text_box barcode.caption_data, options
95
+ end
96
+ end
97
+
98
+ pdf.restore_graphics_state
99
+ end
100
+ end
101
+
102
+ def _draw_ean_upc(barcode, pdf)
103
+ if barcode.valid?
104
+ pdf.save_graphics_state
105
+
106
+ pdf.fill_color barcode.color
107
+
108
+ pdf.transparent barcode.alpha do
109
+ quiet_zone_width_pixels = (barcode.quiet_zone_width * 0.001) * 72.0
110
+ barcode_box_width_pixels = ((barcode.encoded_data.length * barcode.bar_width) * 0.001) * 72.0
111
+ bar_height_pixels = (barcode.bar_height * 0.001) * 72.0
112
+ bar_width_pixels = (barcode.bar_width * 0.001) * 72.0
113
+ caption_size_pixels = ((barcode.caption_size * 0.001) * 72.0)
114
+ caption_height_pixels = ((barcode.caption_height * 0.001) * 72.0)
115
+ barcode_height_pixels = ((barcode.height * 0.001) * 72.0)
116
+ encoded_data = barcode.encoded_data
117
+
118
+ if barcode.data.length == 7 # EAN8
119
+ start_range = (0..3)
120
+ middle_range = (32..35)
121
+ end_range = (65..encoded_data.length)
122
+ elsif barcode.data.length == 11 # UPCA
123
+ start_range = (0..10)
124
+ middle_range = (46..49)
125
+ end_range = (85..encoded_data.length)
126
+ elsif barcode.data.length == 12 # EAN13
127
+ start_range = (0..3)
128
+ middle_range = (46..49)
129
+ end_range = (93..encoded_data.length)
130
+ end
131
+
132
+ pdf.bounding_box([quiet_zone_width_pixels, barcode_height_pixels], :width => barcode_box_width_pixels, :height => bar_height_pixels) do
133
+ index = 0
134
+ bar_count = 0
135
+ origin = []
136
+ encoded_data.each_char do |char|
137
+ if char == '1'
138
+ if bar_count == 0
139
+ origin = [index * bar_width_pixels, bar_height_pixels]
140
+ end
141
+ bar_count += 1
142
+ else
143
+ if start_range.include?(index) || middle_range.include?(index) || end_range.include?(index)
144
+ pdf.fill_rectangle origin, bar_width_pixels * bar_count, bar_height_pixels + (caption_height_pixels * 0.5)
145
+ else
146
+ pdf.fill_rectangle origin, bar_width_pixels * bar_count, bar_height_pixels
147
+ end
148
+ bar_count = 0
149
+ end
150
+ index += 1
151
+ end
152
+
153
+ if bar_count > 0
154
+ pdf.fill_rectangle origin, bar_width_pixels * bar_count, bar_height_pixels + (caption_height_pixels * 0.5)
155
+ end
156
+ end
157
+
158
+ if barcode.captioned
159
+ number_system_options = {
160
+ :at => [0, caption_height_pixels * 1.5],
161
+ :width => quiet_zone_width_pixels,
162
+ :height => caption_height_pixels,
163
+ :overflow => :truncate,
164
+ :size => caption_size_pixels,
165
+ :valign => :center,
166
+ :align => :center,
167
+ :style => :normal
168
+ }
169
+ check_digit_options = {
170
+ :at => [pdf.bounds.width - quiet_zone_width_pixels, caption_height_pixels * 1.5],
171
+ :width => quiet_zone_width_pixels,
172
+ :height => caption_height_pixels,
173
+ :overflow => :truncate,
174
+ :size => caption_size_pixels,
175
+ :valign => :center,
176
+ :align => :center,
177
+ :style => :normal
178
+ }
179
+ left_hand_options = {
180
+ :at => [quiet_zone_width_pixels, caption_height_pixels],
181
+ :width => barcode_box_width_pixels * 0.5,
182
+ :height => caption_height_pixels,
183
+ :overflow => :truncate,
184
+ :size => caption_size_pixels,
185
+ :valign => :center,
186
+ :align => :center,
187
+ :style => :normal
188
+ }
189
+ right_hand_options = {
190
+ :at => [quiet_zone_width_pixels + (barcode_box_width_pixels * 0.5), caption_height_pixels],
191
+ :width => barcode_box_width_pixels * 0.5,
192
+ :height => caption_height_pixels,
193
+ :overflow => :truncate,
194
+ :size => caption_size_pixels,
195
+ :valign => :center,
196
+ :align => :center,
197
+ :style => :normal
198
+ }
199
+
200
+ if barcode.data.length == 7 # EAN8
201
+ pdf.text_box barcode.caption_data[0..3], left_hand_options
202
+ pdf.text_box barcode.caption_data[4..7], right_hand_options
203
+ elsif barcode.data.length == 11 # UPCA
204
+ left_hand_options[:at] = [quiet_zone_width_pixels + (10 * bar_width_pixels), caption_height_pixels]
205
+ left_hand_options[:width] = (barcode_box_width_pixels * 0.5) - (10 * bar_width_pixels)
206
+ right_hand_options[:at] = [quiet_zone_width_pixels + (barcode_box_width_pixels * 0.5), caption_height_pixels]
207
+ right_hand_options[:width] = barcode_box_width_pixels * 0.5 - (10 * bar_width_pixels)
208
+
209
+ pdf.text_box barcode.caption_data[0], number_system_options
210
+ pdf.text_box barcode.caption_data[11], check_digit_options
211
+ pdf.text_box barcode.caption_data[1..5], left_hand_options
212
+ pdf.text_box barcode.caption_data[6..10], right_hand_options
213
+ elsif barcode.data.length == 12 # EAN13
214
+ pdf.text_box barcode.caption_data[0], number_system_options
215
+ pdf.text_box barcode.caption_data[1..6], left_hand_options
216
+ pdf.text_box barcode.caption_data[7..12], right_hand_options
217
+ end
218
+ end
219
+ end
220
+
221
+ pdf.restore_graphics_state
222
+ end
223
+ end
224
+
225
+ def _draw_planet_postnet(barcode, pdf)
226
+ if barcode.valid?
227
+ pdf.save_graphics_state
228
+
229
+ pdf.fill_color barcode.color
230
+
231
+ pdf.transparent barcode.alpha do
232
+ barcode_box_width = (barcode.width * 0.001) * 72.0
233
+ full_bar_height_pixels = (125 * 0.001) * 72.0
234
+ half_bar_height_pixels = (50 * 0.001) * 72.0
235
+ bar_width_pixels = (20 * 0.001) * 72.0
236
+
237
+ pdf.bounding_box([0, full_bar_height_pixels], :width => barcode_box_width, :height => full_bar_height_pixels) do
238
+ index = 0
239
+ barcode.encoded_data.each_char do |char|
240
+ if char == '1'
241
+ origin = [index * (bar_width_pixels * 2.0), full_bar_height_pixels]
242
+ pdf.fill_rectangle origin, bar_width_pixels, full_bar_height_pixels
243
+ else
244
+ origin = [index * (bar_width_pixels * 2.0), half_bar_height_pixels]
245
+ pdf.fill_rectangle origin, bar_width_pixels, half_bar_height_pixels
246
+ end
247
+ index += 1
248
+ end
249
+ end
250
+ end
251
+
252
+ pdf.restore_graphics_state
253
+ end
254
+ end
255
+ end
256
+ end
257
+ end
@@ -0,0 +1,47 @@
1
+ require 'barcodes/symbology/codabar'
2
+ require 'barcodes/symbology/code11'
3
+ require 'barcodes/symbology/code39'
4
+ require 'barcodes/symbology/code39mod43'
5
+ require 'barcodes/symbology/code39extended'
6
+ require 'barcodes/symbology/code39extendedmod43'
7
+ require 'barcodes/symbology/code93'
8
+ require 'barcodes/symbology/code93extended'
9
+ require 'barcodes/symbology/code128'
10
+ require 'barcodes/symbology/ean8'
11
+ require 'barcodes/symbology/ean13'
12
+ require 'barcodes/symbology/msi'
13
+ require 'barcodes/symbology/msimod10'
14
+ require 'barcodes/symbology/msimod11'
15
+ require 'barcodes/symbology/standard2of5'
16
+ require 'barcodes/symbology/standard2of5mod10'
17
+ require 'barcodes/symbology/interleaved2of5'
18
+ require 'barcodes/symbology/interleaved2of5mod10'
19
+ require 'barcodes/symbology/planet'
20
+ require 'barcodes/symbology/postnet'
21
+ require 'barcodes/symbology/upca'
22
+
23
+ module Barcodes
24
+ module Symbology
25
+ CODABAR = ['Codabar', 'codabar']
26
+ CODE_11 = ['Code11', 'Code 11', 'code11']
27
+ CODE_39 = ['Code39', 'Code 39', 'code39']
28
+ CODE_39_MOD_43 = ['Code39Mod43', 'Code 39 Mod 43', 'code39mod43']
29
+ CODE_39_EXTENDED = ['Code39Extended', 'Code 39 Extended', 'code39extended']
30
+ CODE_39_EXTENDED_MOD_43 = ['Code39ExtendedMod43', 'Code 39 Extended Mod 43', 'code39extendedmod43']
31
+ CODE_93 = ['Code93', 'Code 93', 'code93']
32
+ CODE_93_EXTENDED = ['Code93Extended', 'Code 93 Extended', 'code93extended']
33
+ CODE_128 = ['Code128', 'Code 128', 'code128']
34
+ EAN_8 = ['EAN8', 'EAN 8', 'EAN-8', 'ean8']
35
+ EAN_13 = ['EAN13', 'EAN 13', 'EAN-13', 'ean13']
36
+ MSI = ['MSI', 'Modified Plessey', 'msi']
37
+ MSI_MOD_10 = ['MSI Mod 10', 'Modified Plessey Mod 10', 'msimod10']
38
+ MSI_MOD_11 = ['MSI Mod 11', 'Modified Plessey Mod 11', 'msimod11']
39
+ STANDARD_2_OF_5 = ['Standard2of5', 'Standard 2 of 5', 'standard_2_of_5']
40
+ STANDARD_2_OF_5_MOD_10 = ['Standard2of5Mod10', 'Standard 2 of 5 Mod 10', 'standard_2_of_5_mod_10']
41
+ INTERLEAVED_2_OF_5 = ['Interleaved2of5', 'Interleaved 2 of 5', 'interleaved_2_of_5']
42
+ INTERLEAVED_2_OF_5_MOD_10 = ['Interleaved2of5Mod10', 'Interleaved 2 of 5 Mod 10', 'interleaved_2_of_5_mod_10']
43
+ PLANET = ['Planet', 'PLANET', 'planet']
44
+ POSTNET = ['Postnet', 'POSTNET', 'postnet']
45
+ UPC_A = ['UPCA', 'UPC-A', 'upc_a']
46
+ end
47
+ end
@@ -0,0 +1,103 @@
1
+ module Barcodes
2
+ module Symbology
3
+ class Base
4
+ attr_accessor :data
5
+ attr_accessor :start_character
6
+ attr_accessor :stop_character
7
+ attr_accessor :bar_width
8
+ attr_accessor :bar_height
9
+ attr_accessor :alpha
10
+ attr_accessor :color
11
+ attr_accessor :caption_height
12
+ attr_accessor :caption_size
13
+ attr_accessor :captioned
14
+
15
+ def self.charset
16
+ # Should be overridden by subclass to provide charset
17
+ [].collect {|c| c.bytes.to_a[0] }
18
+ end
19
+
20
+ def self.valueset
21
+ # Should be overridden by subclass to provide valueset
22
+ []
23
+ end
24
+
25
+ def initialize(args={})
26
+ @data = '0123456789'
27
+ @start_character = ''
28
+ @stop_character = ''
29
+ @bar_width = 20
30
+ @bar_height = 1000
31
+ @alpha = 1.0
32
+ @color = '000000'
33
+ @caption_height = 180
34
+ @caption_size = 167
35
+ @captioned = true
36
+
37
+ args.each do |k,v|
38
+ instance_variable_set("@#{k}", v) unless v.nil?
39
+ end
40
+ end
41
+
42
+ def caption_data
43
+ # Can be overridden by subclass to format data string for display
44
+ self.data
45
+ end
46
+
47
+ def formatted_data
48
+ # Can be overridden by subclass to add additional formatting to data string
49
+ self.data
50
+ end
51
+
52
+ def encoded_data
53
+ if self.valid?
54
+ encoded_data = ""
55
+ self.formatted_data.each_byte do |char|
56
+ encoded_data += self._encode_character char
57
+ end
58
+ encoded_data
59
+ end
60
+ end
61
+
62
+ def quiet_zone_width
63
+ # Can be overriden in subclass to adjust quiet zone width
64
+ 0
65
+ end
66
+
67
+ def width
68
+ if valid?
69
+ (self.encoded_data.length * self.bar_width) + (self.quiet_zone_width * 2)
70
+ else
71
+ 0
72
+ end
73
+ end
74
+
75
+ def height
76
+ self.captioned ? self.caption_height + self.bar_height : self.bar_height
77
+ end
78
+
79
+ def valid?
80
+ # Can be overridden in subclass to validate barcode
81
+ valid = self.data.length > 0 ? true : false
82
+
83
+ self.data.each_byte do |char|
84
+ if self._encode_character(char).nil?
85
+ return false
86
+ end
87
+ end
88
+
89
+ return valid
90
+ end
91
+
92
+ protected
93
+
94
+ def _encode_character(character)
95
+ if self.class.charset.include? character
96
+ return self.class.valueset.at(self.class.charset.index(character))
97
+ else
98
+ return nil
99
+ end
100
+ end
101
+ end
102
+ end
103
+ end
@@ -0,0 +1,36 @@
1
+ require 'barcodes/symbology/base'
2
+
3
+ module Barcodes
4
+ module Symbology
5
+ class Codabar < Base
6
+ def self.charset
7
+ ['0','1','2','3','4','5','6','7','8','9','-','$',':','/','.','+','A','B','C','D'].collect {|c| c.bytes.to_a[0] }
8
+ end
9
+
10
+ def self.valueset
11
+ [
12
+ '1010100110','1010110010','1010010110','110010101',
13
+ '101101001','110101001','100101011','100101101',
14
+ '100110101','110100101','101001101','101100101',
15
+ '1101011011','1101101011','1101101101','101100110011',
16
+ '1011001001','1010010011','1001001011','1010011001'
17
+ ]
18
+ end
19
+
20
+ def initialize(args={})
21
+ unless args.has_key? :start_character
22
+ args[:start_character] = 'A'
23
+ end
24
+ unless args.has_key? :stop_character
25
+ args[:stop_character] = 'B'
26
+ end
27
+
28
+ super(args)
29
+ end
30
+
31
+ def formatted_data
32
+ @start_character + @data + @stop_character
33
+ end
34
+ end
35
+ end
36
+ end
@@ -0,0 +1,74 @@
1
+ require 'barcodes/symbology/base'
2
+
3
+ module Barcodes
4
+ module Symbology
5
+ class Code11 < Base
6
+ def self.charset
7
+ ['0','1','2','3','4','5','6','7','8','9','-','S'].collect {|c| c.bytes.to_a[0] }
8
+ end
9
+
10
+ def self.valueset
11
+ [
12
+ '1010110','11010110','10010110',
13
+ '11001010','10110110','11011010',
14
+ '10011010','10100110','11010010',
15
+ '1101010','1011010','10110010'
16
+ ]
17
+ end
18
+
19
+ def initialize(args={})
20
+ super(args)
21
+
22
+ @start_character = 'S'
23
+ @stop_character = 'S'
24
+ end
25
+
26
+ def formatted_data
27
+ checksum = self.checksum
28
+ unless checksum.nil?
29
+ @start_character + @data + checksum + @stop_character
30
+ end
31
+ end
32
+
33
+ def checksum
34
+ if valid?
35
+ unless @data.length >= 10
36
+ c_value = self._checksum(@data, 10)
37
+ return c_value
38
+ else
39
+ c_value = self._checksum(@data, 10)
40
+ k_value = self._checksum(@data + c_value, 9)
41
+ return c_value + k_value
42
+ end
43
+ end
44
+ end
45
+
46
+ protected
47
+
48
+ def _checksum(data, weight_max)
49
+ sum = 0
50
+ weight = 0
51
+ data.reverse.each_char do |char|
52
+ if ('0'..'9').include? char
53
+ sum += weight * char.to_i
54
+ elsif char == '-'
55
+ sum += weight * 10
56
+ end
57
+ if weight < weight_max
58
+ weight += 1
59
+ else
60
+ weight = 0
61
+ end
62
+ end
63
+
64
+ value = sum % 11
65
+
66
+ if (0..9).include? value
67
+ return value.to_s
68
+ elsif value == 10
69
+ return '-'
70
+ end
71
+ end
72
+ end
73
+ end
74
+ end