col 1.0.0 → 1.0.1a

Sign up to get free protection for your applications and to get access to all the features.
Files changed (5) hide show
  1. data/README +35 -35
  2. data/lib/col.rb +350 -342
  3. data/test/col.rb +241 -229
  4. data/test/col_db.rb +14 -14
  5. metadata +33 -57
data/lib/col.rb CHANGED
@@ -1,342 +1,350 @@
1
- require 'rubygems'
2
- require 'term/ansicolor'
3
-
4
- # --------------------------------------------------------------------------- #
5
-
6
- class Col
7
- # args: array of strings (to_s is called on each)
8
- def initialize(*args)
9
- @strings = args.map { |a| a.to_s }
10
- end
11
-
12
- def Col.[](*args)
13
- Col.new(*args)
14
- end
15
-
16
- COLORED_REGEXP = /\e\[ # opening character sequence
17
- (?: [34][0-7] | [0-9] ) ? # optional code
18
- ( ; (?: [34][0-7] | [0-9] ))* # more optional codes
19
- m # closing character
20
- /x
21
-
22
- # Convenience method to remove color codes from a string.
23
- # Also available as Col.plain.
24
- def Col.uncolored(string)
25
- string.gsub(COLORED_REGEXP, '')
26
- end
27
-
28
- # Convenience method to remove color codes from a string.
29
- # Also available as Col.uncolored.
30
- def Col.plain(string)
31
- string.gsub(COLORED_REGEXP, '')
32
- end
33
-
34
- def Col.inline(*args)
35
- unless args.size.even?
36
- raise Col::Error, "Col.inline requires an even number of arguments"
37
- end
38
- String.new.tap { |result|
39
- args.each_slice(2) do |string, format|
40
- result << Col(string.to_s).fmt(format)
41
- end
42
- }
43
- end
44
-
45
- # e.g.
46
- # Col("one", "two", "three", "four").fmt "rb,y,cb,_b"
47
- # # same as:
48
- # "one".red.bold + "two".yellow + "three".cyan.bold + "four".bold
49
- #
50
- # Col
51
- def fmt(*spec)
52
- ::Col::Formatter.new(@strings, *spec).result
53
- end
54
-
55
- def to_s
56
- @strings.join
57
- end
58
-
59
- def method_missing(message, *args, &block)
60
- unless args.empty?
61
- super # We're not interested in a message with arguments; NoMethodError
62
- end
63
- if Col::DB.method?(message)
64
- Col.new( self.fmt(message) ) # Col["..."].yellow -> Col
65
- # to allow Col["..."].yellow.bold
66
- else
67
- self.fmt(message) # Col["..."].gbow -> String
68
- end
69
- end
70
- end
71
-
72
- def Col(*args)
73
- Col.new(*args)
74
- end
75
-
76
- # --------------------------------------------------------------------------- #
77
-
78
- class Col::Error < StandardError
79
- end
80
-
81
- # --------------------------------------------------------------------------- #
82
-
83
- class Col::Formatter
84
- def initialize(strings, *spec)
85
- check_correct_number_of_arguments(strings, *spec)
86
- @strings = strings
87
- @format_spec = normalise_format_spec(*spec)
88
- end
89
-
90
- def result
91
- unless @strings.size == @format_spec.size
92
- raise Col::Error, "mismatching strings and specs"
93
- end
94
- String.new.tap { |str|
95
- @strings.zip(@format_spec).each do |string, spec|
96
- d = decorated_string(string, spec)
97
- str << d
98
- end
99
- }
100
- end
101
-
102
- # e.g.
103
- # string = "hello"
104
- # spec = [:yellow, :bold, :on_red]
105
- # result = "hello".send(:yellow).send(:bold).send(:on_red)
106
- def decorated_string(string, spec)
107
- raise Col::Error unless string.is_a? String and spec.is_a? Array \
108
- and spec.all? { |e| e.is_a? Symbol }
109
- spec.inject(string) { |str, symbol| Term::ANSIColor.send(symbol, str) }
110
- end
111
-
112
- #
113
- # In general, there should be the same number of arguments as there are
114
- # strings:
115
- #
116
- # Col["one"].fmt( :b )
117
- # Col["one", "two"].fmt( [:red, :on_white], [:bold, :negative] )
118
- # Col["one", "two", "three"].fmt( :yellow, [:green, :bold], :italic )
119
- # Col["one", "two", "three", "four"].fmt(:rb, :y, :cb, :m)
120
- # Col["one", "two", "three", "four"].fmt "rb,y,cb,m"
121
- #
122
- # As a special case, if there is only one string, it can have any number of
123
- # arguments:
124
- #
125
- # Col["string"].fmt( :yellow, :bold, :italic, :blink, :negative, :on_magenta )
126
- #
127
- # If the number of arguments is incorrect, a Col::Error is thrown.
128
- #
129
- def check_correct_number_of_arguments(strings, *spec)
130
- nargs = spec.size
131
- if nargs == 1 and spec.first.is_a? String
132
- nargs = spec.first.split(/,/).size
133
- end
134
- if strings.size > 1 and nargs != strings.size
135
- raise Col::Error, "incorrect number of arguments: #{render(spec)}"
136
- end
137
- end
138
-
139
- #
140
- # Each spec in the following groups is equivalent. The last one is
141
- # normalised.
142
- #
143
- # [ :rb ]
144
- # [ "rb" ]
145
- # [ [:red, :bold] ]
146
- #
147
- # [ :rb, :_n, :y ]
148
- # [ "rb", "_n", "y" ]
149
- # [ [:red, :bold], [:negative], [:yellow] ]
150
- #
151
- # [ [:green, :concealed], :blue, :bold ]
152
- # [ [:green, :concealed], [:blue], [:bold] ]
153
- #
154
- # [ "rb,y,_i,g_ow" ]
155
- # [ "rb", "y", "_i", "g_ow" ]
156
- # [ [:red, :bold], [:yellow], [:italic], [:green, :on_white] ]
157
- #
158
- # {spec} is definitely an array because it was gathered like this:
159
- # def fmt(*spec)
160
- #
161
- # "Normalised" means an array with one element for each string.
162
- # Each element is itself an array of all the properties that apply to that
163
- # string.
164
- #
165
- def normalise_format_spec(*spec)
166
- if spec.size == 1 and spec.first.is_a? String and spec.first.index(',')
167
- # ^^^ "rb,y,_n"
168
- spec = spec.first.split(',') # ['rb', 'y', '_n']
169
- normalise_format_spec(*spec)
170
- else
171
- # We have an array of items. We need to treat each item individually and
172
- # put the items together. We remove nil elements.
173
- spec.map { |item| normalise_item(item) }.compact
174
- end
175
- end
176
-
177
- # Examples
178
- # Item Normalised item
179
- # :r [:red]
180
- # "r" [:red]
181
- # :red [:red]
182
- # [:red] [:red]
183
- # "rb" [:red, :bold]
184
- # :rb [:red, :bold]
185
- # [:red, :bold] [:red, :bold]
186
- # :b [:blue]
187
- # :_b [:bold]
188
- # :__b error
189
- # :__ob [:on_blue]
190
- # "gsow" [:green, :strikethrough, :on_white]
191
- # "_noB" [:negative, :on_black]
192
- # :_ []
193
- # [:_] []
194
- # [:_, :_] []
195
- # (etc.)
196
- def normalise_item(item)
197
- case item
198
- when Symbol then normalise_string(item.to_s)
199
- when Array then normalise_array(item)
200
- when String then normalise_string(item)
201
- else raise Col::Error, "Invalid item type: #{item.class}"
202
- end
203
- end
204
-
205
- # Input: array of symbols
206
- # Result: array of symbols, each of which is a legitimate ANSIColor method
207
- # Note: underscores and nil items are removed from the array
208
- def normalise_array(array)
209
- array.reject! { |x| x.nil? or x == :_ or x == '_' }
210
- invalid_items = array.select { |item| not Col::DB.method? item }
211
- case invalid_items.size
212
- when 0 then return array
213
- when 1 then raise Col::Error, "Invalid item: #{invalid_items.first.inspect}"
214
- else raise Col::Error, "Invalid items: #{invalid_items.inspect}"
215
- end
216
- end
217
-
218
- # Examples
219
- # Input Output
220
- # r [:red]
221
- # b [:blue]
222
- # rb [:red, :bold]
223
- # red [:red]
224
- # bold [:bold]
225
- # gsow [:green, :strikethrough, :on_white]
226
- # _b [:bold]
227
- # __ob [:on_blue]
228
- # _ []
229
- def normalise_string(string)
230
- # Is it already one of the methods? If so, easy. If not, split and parse.
231
- if string == "_"
232
- []
233
- elsif Col::DB.method? string
234
- [ string.intern ]
235
- elsif (1..4).include? string.size # say: "g", "gb", "gbow"
236
- color, style, backg = extract(string)
237
- color = Col::DB.color(color) # 'g' -> :green
238
- style = Col::DB.style(style) # 'b' -> :bold
239
- backg = Col::DB.background(backg) # 'ow' -> :on_white
240
- [ color, style, backg ].compact # remove nil elements
241
- else
242
- raise Col::Error, "Invalid item: #{string.inspect}"
243
- end
244
- rescue Col::Error => e
245
- raise Col::Error, "Invalid item: #{string.inspect} (#{e.message})"
246
- end
247
-
248
- # Extracts color, style and background color.
249
- # "gbow" -> ["g", "b", "ow"]
250
- # "g" -> ["g", nil, nil]
251
- # "_b" -> [nil, "b", nil]
252
- def extract(string)
253
- string += " "
254
- color, style, backg = /^(.)(.)(..)/.match(string).captures
255
- color = nil if [' ', '_'].include? color
256
- style = nil if [' ', '_'].include? style
257
- backg = nil if [' ', '__'].include? backg
258
- [color, style, backg]
259
- end
260
-
261
- def render(spec)
262
- ( (spec.size == 1) ? spec.first : spec ).inspect
263
- end
264
- end
265
-
266
- # --------------------------------------------------------------------------- #
267
-
268
- class Col::DB
269
- COLORS = {
270
- 'B' => :black,
271
- 'r' => :red,
272
- 'g' => :green,
273
- 'y' => :yellow,
274
- 'b' => :blue,
275
- 'm' => :magenta,
276
- 'c' => :cyan,
277
- 'w' => :white
278
- }
279
-
280
- STYLES = {
281
- 'b' => :bold,
282
- 'd' => :dark,
283
- 'i' => :italic,
284
- 'u' => :underline,
285
- 'U' => :underscore,
286
- 'k' => :blink,
287
- 'r' => :rapid_blink,
288
- 'n' => :negative,
289
- 'c' => :concealed,
290
- 's' => :strikethrough,
291
- }
292
-
293
- BACKGROUND = {
294
- 'oB' => :on_black,
295
- 'or' => :on_red,
296
- 'og' => :on_green,
297
- 'oy' => :on_yellow,
298
- 'ob' => :on_blue,
299
- 'om' => :on_magenta,
300
- 'oc' => :on_cyan,
301
- 'ow' => :on_white
302
- }
303
-
304
- ALL_METHODS_SYMBOL = COLORS.values + STYLES.values + BACKGROUND.values
305
- ALL_METHODS_STRING = ALL_METHODS_SYMBOL.map { |x| x.to_s }
306
- require 'set'
307
- ALL_METHODS = Set[*ALL_METHODS_SYMBOL] + Set[*ALL_METHODS_STRING]
308
-
309
- def self.method?(x)
310
- ALL_METHODS.include? x
311
- end
312
-
313
- def self.color(key)
314
- get_value COLORS, key, "color"
315
- end
316
-
317
- def self.style(key)
318
- get_value STYLES, key, "style"
319
- end
320
-
321
- def self.background(key)
322
- get_value BACKGROUND, key, "background color"
323
- end
324
-
325
- # If the 'key' is nil, we return nil. Otherwise, we insist that the key be a
326
- # valid color, style or background color. If it's not, we raise an error
327
- # (that's what 'name' is for).
328
- #
329
- # Return the method name sought: :green, :bold, :on_white, etc.
330
- def self.get_value(hash, key, name)
331
- if key.nil?
332
- nil
333
- else
334
- method = hash[key.to_s]
335
- if method.nil?
336
- raise Col::Error, "Invalid #{name} code: #{key}"
337
- end
338
- method
339
- end
340
- end
341
-
342
- end # Col::DB
1
+ require 'rubygems'
2
+ require 'term/ansicolor'
3
+
4
+ # --------------------------------------------------------------------------- #
5
+
6
+ class Col
7
+
8
+ VERSION = "1.0.1a"
9
+
10
+ # args: array of strings (to_s is called on each)
11
+ def initialize(*args)
12
+ @strings = args.map { |a| a.to_s }
13
+ end
14
+
15
+ def Col.[](*args)
16
+ Col.new(*args)
17
+ end
18
+
19
+ COLORED_REGEXP = /\e\[ # opening character sequence
20
+ (?: [34][0-7] | [0-9] ) ? # optional code
21
+ ( ; (?: [34][0-7] | [0-9] ))* # more optional codes
22
+ m # closing character
23
+ /x
24
+
25
+ # Convenience method to remove color codes from a string.
26
+ # Also available as Col.plain.
27
+ def Col.uncolored(string)
28
+ string.gsub(COLORED_REGEXP, '')
29
+ end
30
+
31
+ # Convenience method to remove color codes from a string.
32
+ # Also available as Col.uncolored.
33
+ def Col.plain(string)
34
+ string.gsub(COLORED_REGEXP, '')
35
+ end
36
+
37
+ def Col.inline(*args)
38
+ unless args.size.even?
39
+ raise Col::Error, "Col.inline requires an even number of arguments"
40
+ end
41
+ String.new.tap { |result|
42
+ args.each_slice(2) do |string, format|
43
+ result << Col(string.to_s).fmt(format)
44
+ end
45
+ }
46
+ end
47
+
48
+ # e.g.
49
+ # Col("one", "two", "three", "four").fmt "rb,y,cb,_b"
50
+ # # same as:
51
+ # "one".red.bold + "two".yellow + "three".cyan.bold + "four".bold
52
+ #
53
+ # Col
54
+ def fmt(*spec)
55
+ ::Col::Formatter.new(@strings, *spec).result
56
+ end
57
+
58
+ def to_s
59
+ @strings.join
60
+ end
61
+
62
+ # Works nicely with puts. E.g. puts Col("...").red.bold
63
+ def to_str
64
+ to_s
65
+ end
66
+
67
+ def method_missing(message, *args, &block)
68
+ unless args.empty?
69
+ super # We're not interested in a message with arguments; NoMethodError
70
+ end
71
+ if Col::DB.method?(message)
72
+ Col.new( self.fmt(message) ) # Col["..."].yellow -> Col
73
+ # to allow Col["..."].yellow.bold
74
+ else
75
+ self.fmt(message) # Col["..."].gbow -> String
76
+ end
77
+ end
78
+ end
79
+
80
+ def Col(*args)
81
+ Col.new(*args)
82
+ end
83
+
84
+ # --------------------------------------------------------------------------- #
85
+
86
+ class Col::Error < StandardError
87
+ end
88
+
89
+ # --------------------------------------------------------------------------- #
90
+
91
+ class Col::Formatter
92
+ def initialize(strings, *spec)
93
+ check_correct_number_of_arguments(strings, *spec)
94
+ @strings = strings
95
+ @format_spec = normalise_format_spec(*spec)
96
+ end
97
+
98
+ def result
99
+ unless @strings.size == @format_spec.size
100
+ raise Col::Error, "mismatching strings and specs"
101
+ end
102
+ String.new.tap { |str|
103
+ @strings.zip(@format_spec).each do |string, spec|
104
+ d = decorated_string(string, spec)
105
+ str << d
106
+ end
107
+ }
108
+ end
109
+
110
+ # e.g.
111
+ # string = "hello"
112
+ # spec = [:yellow, :bold, :on_red]
113
+ # result = "hello".send(:yellow).send(:bold).send(:on_red)
114
+ def decorated_string(string, spec)
115
+ raise Col::Error unless string.is_a? String and spec.is_a? Array \
116
+ and spec.all? { |e| e.is_a? Symbol }
117
+ spec.inject(string) { |str, symbol| Term::ANSIColor.send(symbol, str) }
118
+ end
119
+
120
+ #
121
+ # In general, there should be the same number of arguments as there are
122
+ # strings:
123
+ #
124
+ # Col["one"].fmt( :b )
125
+ # Col["one", "two"].fmt( [:red, :on_white], [:bold, :negative] )
126
+ # Col["one", "two", "three"].fmt( :yellow, [:green, :bold], :italic )
127
+ # Col["one", "two", "three", "four"].fmt(:rb, :y, :cb, :m)
128
+ # Col["one", "two", "three", "four"].fmt "rb,y,cb,m"
129
+ #
130
+ # As a special case, if there is only one string, it can have any number of
131
+ # arguments:
132
+ #
133
+ # Col["string"].fmt( :yellow, :bold, :italic, :blink, :negative, :on_magenta )
134
+ #
135
+ # If the number of arguments is incorrect, a Col::Error is thrown.
136
+ #
137
+ def check_correct_number_of_arguments(strings, *spec)
138
+ nargs = spec.size
139
+ if nargs == 1 and spec.first.is_a? String
140
+ nargs = spec.first.split(/,/).size
141
+ end
142
+ if strings.size > 1 and nargs != strings.size
143
+ raise Col::Error, "incorrect number of arguments: #{render(spec)}"
144
+ end
145
+ end
146
+
147
+ #
148
+ # Each spec in the following groups is equivalent. The last one is
149
+ # normalised.
150
+ #
151
+ # [ :rb ]
152
+ # [ "rb" ]
153
+ # [ [:red, :bold] ]
154
+ #
155
+ # [ :rb, :_n, :y ]
156
+ # [ "rb", "_n", "y" ]
157
+ # [ [:red, :bold], [:negative], [:yellow] ]
158
+ #
159
+ # [ [:green, :concealed], :blue, :bold ]
160
+ # [ [:green, :concealed], [:blue], [:bold] ]
161
+ #
162
+ # [ "rb,y,_i,g_ow" ]
163
+ # [ "rb", "y", "_i", "g_ow" ]
164
+ # [ [:red, :bold], [:yellow], [:italic], [:green, :on_white] ]
165
+ #
166
+ # {spec} is definitely an array because it was gathered like this:
167
+ # def fmt(*spec)
168
+ #
169
+ # "Normalised" means an array with one element for each string.
170
+ # Each element is itself an array of all the properties that apply to that
171
+ # string.
172
+ #
173
+ def normalise_format_spec(*spec)
174
+ if spec.size == 1 and spec.first.is_a? String and spec.first.index(',')
175
+ # ^^^ "rb,y,_n"
176
+ spec = spec.first.split(',') # ['rb', 'y', '_n']
177
+ normalise_format_spec(*spec)
178
+ else
179
+ # We have an array of items. We need to treat each item individually and
180
+ # put the items together. We remove nil elements.
181
+ spec.map { |item| normalise_item(item) }.compact
182
+ end
183
+ end
184
+
185
+ # Examples
186
+ # Item Normalised item
187
+ # :r [:red]
188
+ # "r" [:red]
189
+ # :red [:red]
190
+ # [:red] [:red]
191
+ # "rb" [:red, :bold]
192
+ # :rb [:red, :bold]
193
+ # [:red, :bold] [:red, :bold]
194
+ # :b [:blue]
195
+ # :_b [:bold]
196
+ # :__b error
197
+ # :__ob [:on_blue]
198
+ # "gsow" [:green, :strikethrough, :on_white]
199
+ # "_noB" [:negative, :on_black]
200
+ # :_ []
201
+ # [:_] []
202
+ # [:_, :_] []
203
+ # (etc.)
204
+ def normalise_item(item)
205
+ case item
206
+ when Symbol then normalise_string(item.to_s)
207
+ when Array then normalise_array(item)
208
+ when String then normalise_string(item)
209
+ else raise Col::Error, "Invalid item type: #{item.class}"
210
+ end
211
+ end
212
+
213
+ # Input: array of symbols
214
+ # Result: array of symbols, each of which is a legitimate ANSIColor method
215
+ # Note: underscores and nil items are removed from the array
216
+ def normalise_array(array)
217
+ array.reject! { |x| x.nil? or x == :_ or x == '_' }
218
+ invalid_items = array.select { |item| not Col::DB.method? item }
219
+ case invalid_items.size
220
+ when 0 then return array
221
+ when 1 then raise Col::Error, "Invalid item: #{invalid_items.first.inspect}"
222
+ else raise Col::Error, "Invalid items: #{invalid_items.inspect}"
223
+ end
224
+ end
225
+
226
+ # Examples
227
+ # Input Output
228
+ # r [:red]
229
+ # b [:blue]
230
+ # rb [:red, :bold]
231
+ # red [:red]
232
+ # bold [:bold]
233
+ # gsow [:green, :strikethrough, :on_white]
234
+ # _b [:bold]
235
+ # __ob [:on_blue]
236
+ # _ []
237
+ def normalise_string(string)
238
+ # Is it already one of the methods? If so, easy. If not, split and parse.
239
+ if string == "_"
240
+ []
241
+ elsif Col::DB.method? string
242
+ [ string.intern ]
243
+ elsif (1..4).include? string.size # say: "g", "gb", "gbow"
244
+ color, style, backg = extract(string)
245
+ color = Col::DB.color(color) # 'g' -> :green
246
+ style = Col::DB.style(style) # 'b' -> :bold
247
+ backg = Col::DB.background(backg) # 'ow' -> :on_white
248
+ [ color, style, backg ].compact # remove nil elements
249
+ else
250
+ raise Col::Error, "Invalid item: #{string.inspect}"
251
+ end
252
+ rescue Col::Error => e
253
+ raise Col::Error, "Invalid item: #{string.inspect} (#{e.message})"
254
+ end
255
+
256
+ # Extracts color, style and background color.
257
+ # "gbow" -> ["g", "b", "ow"]
258
+ # "g" -> ["g", nil, nil]
259
+ # "_b" -> [nil, "b", nil]
260
+ def extract(string)
261
+ string += " "
262
+ color, style, backg = /^(.)(.)(..)/.match(string).captures
263
+ color = nil if [' ', '_'].include? color
264
+ style = nil if [' ', '_'].include? style
265
+ backg = nil if [' ', '__'].include? backg
266
+ [color, style, backg]
267
+ end
268
+
269
+ def render(spec)
270
+ ( (spec.size == 1) ? spec.first : spec ).inspect
271
+ end
272
+ end
273
+
274
+ # --------------------------------------------------------------------------- #
275
+
276
+ class Col::DB
277
+ COLORS = {
278
+ 'B' => :black,
279
+ 'r' => :red,
280
+ 'g' => :green,
281
+ 'y' => :yellow,
282
+ 'b' => :blue,
283
+ 'm' => :magenta,
284
+ 'c' => :cyan,
285
+ 'w' => :white
286
+ }
287
+
288
+ STYLES = {
289
+ 'b' => :bold,
290
+ 'd' => :dark,
291
+ 'i' => :italic,
292
+ 'u' => :underline,
293
+ 'U' => :underscore,
294
+ 'k' => :blink,
295
+ 'r' => :rapid_blink,
296
+ 'n' => :negative,
297
+ 'c' => :concealed,
298
+ 's' => :strikethrough,
299
+ }
300
+
301
+ BACKGROUND = {
302
+ 'oB' => :on_black,
303
+ 'or' => :on_red,
304
+ 'og' => :on_green,
305
+ 'oy' => :on_yellow,
306
+ 'ob' => :on_blue,
307
+ 'om' => :on_magenta,
308
+ 'oc' => :on_cyan,
309
+ 'ow' => :on_white
310
+ }
311
+
312
+ ALL_METHODS_SYMBOL = COLORS.values + STYLES.values + BACKGROUND.values
313
+ ALL_METHODS_STRING = ALL_METHODS_SYMBOL.map { |x| x.to_s }
314
+ require 'set'
315
+ ALL_METHODS = Set[*ALL_METHODS_SYMBOL] + Set[*ALL_METHODS_STRING]
316
+
317
+ def self.method?(x)
318
+ ALL_METHODS.include? x
319
+ end
320
+
321
+ def self.color(key)
322
+ get_value COLORS, key, "color"
323
+ end
324
+
325
+ def self.style(key)
326
+ get_value STYLES, key, "style"
327
+ end
328
+
329
+ def self.background(key)
330
+ get_value BACKGROUND, key, "background color"
331
+ end
332
+
333
+ # If the 'key' is nil, we return nil. Otherwise, we insist that the key be a
334
+ # valid color, style or background color. If it's not, we raise an error
335
+ # (that's what 'name' is for).
336
+ #
337
+ # Return the method name sought: :green, :bold, :on_white, etc.
338
+ def self.get_value(hash, key, name)
339
+ if key.nil?
340
+ nil
341
+ else
342
+ method = hash[key.to_s]
343
+ if method.nil?
344
+ raise Col::Error, "Invalid #{name} code: #{key}"
345
+ end
346
+ method
347
+ end
348
+ end
349
+
350
+ end # Col::DB