iso8583 0.1.1 → 0.1.3

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,4 @@
1
+ module ISO8583
2
+ class ISO8583Exception < Exception; end
3
+ class ISO8583ParseException < ISO8583Exception; end
4
+ end
data/lib/8583/field.rb ADDED
@@ -0,0 +1,90 @@
1
+ module ISO8583
2
+
3
+ class Field
4
+ # may either be some other Field in which the length is encoded or a Fixnum for
5
+ # fixed length fields. Length should always be the length of the *encoded* value.
6
+ # A 6 digit BCD field will require a length 3, as will a 5 digit BCD field.
7
+ # The subclass BCDField handles this to keep things consistant.
8
+ attr_accessor :length
9
+ attr_accessor :codec
10
+ attr_accessor :padding
11
+ attr_accessor :max
12
+
13
+ attr_writer :name
14
+ attr_accessor :bmp
15
+
16
+ def name
17
+ "BMP #{bmp}: #{@name}"
18
+ end
19
+
20
+ def parse(raw)
21
+ len, raw = case length
22
+ when Fixnum
23
+ [length, raw]
24
+ when Field
25
+ length.parse(raw)
26
+ else
27
+ raise ISO8583Exception.new("Cannot determine the length of '#{name}' field")
28
+ end
29
+
30
+ raw_value = raw[0,len]
31
+
32
+ # make sure we have enough data ...
33
+ if raw_value.length != len
34
+ mes = "Field has incorrect length! field: #{raw_value} len/expected: #{raw_value.length}/#{len}"
35
+ raise ISO8583ParseException.new(mes)
36
+ end
37
+
38
+ rest = raw[len, raw.length]
39
+ begin
40
+ real_value = codec.decode(raw_value)
41
+ rescue
42
+ raise ISO8583ParseException.new($!.message+" (#{name})")
43
+ end
44
+
45
+ [ real_value, rest ]
46
+ end
47
+
48
+
49
+ # Encoding needs to consider length representation, the actual encoding (such as charset or BCD)
50
+ # and padding.
51
+ # The order may be important! This impl calls codec.encode and then pads, in case you need the other
52
+ # special treatment, you may need to override this method alltogether.
53
+ # In other cases, the padding has to be implemented by the codec, such as BCD with an odd number of nibbles.
54
+ def encode(value)
55
+ encoded_value = codec.encode(value)
56
+
57
+ if padding
58
+ if padding.arity == 1
59
+ encoded_value = padding.call(encoded_value)
60
+ elsif padding.arity == 2
61
+ encoded_value = padding.call(encoded_value, length)
62
+ end
63
+ end
64
+
65
+ len_str = case length
66
+ when Fixnum
67
+ raise ISO8583Exception.new("Too long: #{value} (#{name})! length=#{length}") if encoded_value.length > length
68
+ raise ISO8583Exception.new("Too short: #{value} (#{name})! length=#{length}") if encoded_value.length < length
69
+ ""
70
+ when Field
71
+ raise ISO8583Exception.new("Max lenth exceeded: #{value}, max: #{max}") if max && encoded_value.length > max
72
+ length.encode(encoded_value.length)
73
+ else
74
+ raise ISO8583Exception.new("Invalid length (#{length}) for '#{name}' field")
75
+ end
76
+
77
+ len_str + encoded_value
78
+ end
79
+ end
80
+
81
+ class BCDField < Field
82
+ # This corrects the length for BCD fields, as their encoded length is half (+ parity) of the
83
+ # content length. E.g. 123 (length = 3) encodes to "\x01\x23" (length 2)
84
+ def length
85
+ _length = super
86
+ (_length % 2) != 0 ? (_length / 2) + 1 : _length / 2
87
+ end
88
+ end
89
+
90
+ end
@@ -0,0 +1,148 @@
1
+ #--
2
+ # Copyright 2009 by Tim Becker (tim.becker@kuriostaet.de)
3
+ # MIT License, for details, see the LICENSE file accompaning
4
+ # this distribution
5
+ #++
6
+
7
+ module ISO8583
8
+
9
+ # This file contains a number of preinstantiated Field definitions. You
10
+ # will probably need to create own fields in your implementation, please
11
+ # see Field and Codec for further discussion on how to do this.
12
+ # The fields currently available are those necessary to implement the
13
+ # Berlin Groups Authorization Spec.
14
+ #
15
+ # The following fields are available:
16
+ #
17
+ # [+LL+] special form to de/encode variable length indicators, two bytes ASCII numerals
18
+ # [+LLL+] special form to de/encode variable length indicators, two bytes ASCII numerals
19
+ # [+LL_BCD+] special form to de/encode variable length indicators, two BCD digits
20
+ # [+LLVAR_N+] two byte variable length ASCII numeral, payload ASCII numerals
21
+ # [+LLLVAR_N+] three byte variable length ASCII numeral, payload ASCII numerals
22
+ # [+LLVAR_Z+] two byte variable length ASCII numeral, payload Track2 data
23
+ # [+LLVAR_ANS+] two byte variable length ASCII numeral, payload ASCII+special
24
+ # [+LLLVAR_ANS+] three byte variable length ASCII numeral, payload ASCII+special
25
+ # [+LLVAR_B+] Two byte variable length binary payload
26
+ # [+LLLVAR_B+] Three byte variable length binary payload
27
+ # [+N+] fixed lengh numerals, repesented in ASCII, padding right justified using zeros
28
+ # [+AN+] fixed lengh ASCII [A-Za-z0-9], padding left justified using spaces.
29
+ # [+ANP+] fixed lengh ASCII [A-Za-z0-9] and space, padding left, spaces
30
+ # [+ANS+] fixed length ASCII [\x20-\x7E], padding left, spaces
31
+ # [+B+] binary data, padding left using nulls (0x00)
32
+ # [+MMDDhhmmss+] Date, formatted as described in ASCII numerals
33
+ # [+YYMMDDhhmmss+] Date, formatted as named in ASCII numerals
34
+ # [+YYMM+] Expiration Date, formatted as named in ASCII numerals
35
+
36
+
37
+ # Special form to de/encode variable length indicators, two bytes ASCII numerals
38
+ LL = Field.new
39
+ LL.name = "LL"
40
+ LL.length = 2
41
+ LL.codec = ASCII_Number
42
+ LL.padding = lambda {|value|
43
+ sprintf("%02d", value)
44
+ }
45
+ # Special form to de/encode variable length indicators, three bytes ASCII numerals
46
+ LLL = Field.new
47
+ LLL.name = "LLL"
48
+ LLL.length = 3
49
+ LLL.codec = ASCII_Number
50
+ LLL.padding = lambda {|value|
51
+ sprintf("%03d", value)
52
+ }
53
+
54
+ LL_BCD = BCDField.new
55
+ LL_BCD.length = 2
56
+ LL_BCD.codec = Packed_Number
57
+
58
+ # Two byte variable length ASCII numeral, payload ASCII numerals
59
+ LLVAR_N = Field.new
60
+ LLVAR_N.length = LL
61
+ LLVAR_N.codec = ASCII_Number
62
+
63
+ # Three byte variable length ASCII numeral, payload ASCII numerals
64
+ LLLVAR_N = Field.new
65
+ LLLVAR_N.length = LLL
66
+ LLLVAR_N.codec = ASCII_Number
67
+
68
+ # Two byte variable length ASCII numeral, payload Track2 data
69
+ LLVAR_Z = Field.new
70
+ LLVAR_Z.length = LL
71
+ LLVAR_Z.codec = Track2
72
+
73
+ # Two byte variable length ASCII numeral, payload ASCII+special
74
+ LLVAR_ANS = Field.new
75
+ LLVAR_ANS.length = LL
76
+ LLVAR_ANS.codec = ANS_Codec
77
+
78
+ # Three byte variable length ASCII numeral, payload ASCII+special
79
+ LLLVAR_ANS = Field.new
80
+ LLLVAR_ANS.length = LLL
81
+ LLLVAR_ANS.codec = ANS_Codec
82
+
83
+ # Two byte variable length binary payload
84
+ LLVAR_B = Field.new
85
+ LLVAR_B.length = LL
86
+ LLVAR_B.codec = Null_Codec
87
+
88
+
89
+ # Three byte variable length binary payload
90
+ LLLVAR_B = Field.new
91
+ LLLVAR_B.length = LLL
92
+ LLLVAR_B.codec = Null_Codec
93
+
94
+ # Fixed lengh numerals, repesented in ASCII, padding right justified using zeros
95
+ N = Field.new
96
+ N.codec = ASCII_Number
97
+ N.padding = lambda {|val, len|
98
+ sprintf("%0#{len}d", val)
99
+ }
100
+
101
+ N_BCD = BCDField.new
102
+ N_BCD.codec = Packed_Number
103
+
104
+ PADDING_LEFT_JUSTIFIED_SPACES = lambda {|val, len|
105
+ sprintf "%-#{len}s", val
106
+ }
107
+
108
+ # Fixed lengh ASCII [A-Za-z0-9], padding left justified using spaces.
109
+ AN = Field.new
110
+ AN.codec = AN_Codec
111
+ AN.padding = PADDING_LEFT_JUSTIFIED_SPACES
112
+
113
+ # Fixed lengh ASCII [A-Za-z0-9] and space, padding left, spaces
114
+ ANP = Field.new
115
+ ANP.codec = ANP_Codec
116
+ ANP.padding = PADDING_LEFT_JUSTIFIED_SPACES
117
+
118
+ # Fixed length ASCII [\x20-\x7E], padding left, spaces
119
+ ANS = Field.new
120
+ ANS.codec = ANS_Codec
121
+ ANS.padding = PADDING_LEFT_JUSTIFIED_SPACES
122
+
123
+ # Binary data, padding left using nulls (0x00)
124
+ B = Field.new
125
+ B.codec = Null_Codec
126
+ B.padding = lambda {|val, len|
127
+ while val.length < len
128
+ val = val + "\000"
129
+ end
130
+ val
131
+ }
132
+
133
+ # Date, formatted as described in ASCII numerals
134
+ MMDDhhmmss = Field.new
135
+ MMDDhhmmss.codec = MMDDhhmmssCodec
136
+ MMDDhhmmss.length = 10
137
+
138
+ #Date, formatted as described in ASCII numerals
139
+ YYMMDDhhmmss = Field.new
140
+ YYMMDDhhmmss.codec = YYMMDDhhmmssCodec
141
+ YYMMDDhhmmss.length = 12
142
+
143
+ #Date, formatted as described in ASCII numerals
144
+ YYMM = Field.new
145
+ YYMM.codec = YYMMCodec
146
+ YYMM.length = 4
147
+
148
+ end
@@ -0,0 +1,419 @@
1
+ # Copyright 2009 by Tim Becker (tim.becker@kuriostaet.de)
2
+ # MIT License, for details, see the LICENSE file accompaning
3
+ # this distribution
4
+
5
+ module ISO8583
6
+
7
+ # The class `Message` defines functionality to describe classes
8
+ # representing different type of messages, or message families.
9
+ # A message family consists of a number of possible message types that
10
+ # are allowed, and a way of naming and encoding the bitmaps allowed in
11
+ # the messages.
12
+ #
13
+ # To create your own message, start by subclassing Message:
14
+ #
15
+ # class MyMessage < Message
16
+ # (...)
17
+ # end
18
+ #
19
+ # the subtyped message should be told how the MTI is encoded:
20
+ #
21
+ # class MyMessage < Message
22
+ # mti_format N, :length => 4
23
+ # (...)
24
+ # end
25
+ #
26
+ # `N` above is an instance of Field which encodes numbers into their
27
+ # ASCII representations in a fixed length field. The option `length=>4`
28
+ # indicates the length of the fixed field.
29
+ #
30
+ # Next, the allowed message types are specified:
31
+ #
32
+ # class MyMessage < Message
33
+ # (...)
34
+ # mti 1100, "Authorization Request Acquirer Gateway"
35
+ # mti 1110, "Authorization Request Response Issuer Gateway"
36
+ # (...)
37
+ # end
38
+ #
39
+ # This basically defines to message types, 1100 and 1110 which may
40
+ # be accessed later either via their name or value:
41
+ #
42
+ # mes = MyMessage.new 1100
43
+ #
44
+ # or
45
+ # mes = MyMessage.new "Authorization Request Acquirer Gateway"
46
+ #
47
+ # or
48
+ # mes = MyMessage.new
49
+ # mes.mti = 1110 # or Auth. Req. Acq. Gateway ...
50
+ #
51
+ # Finally the allowed bitmaps, their names and the encoding rules are
52
+ # specified:
53
+ #
54
+ # class MyMessage < Message
55
+ # (...)
56
+ # bmp 2, "Primary Account Number (PAN)", LLVAR_N, :max => 19
57
+ # bmp 3, "Processing Code", N, :length => 6
58
+ # bmp 4, "Amount (Transaction)", N, :length => 12
59
+ # bmp 6, "Amount, Cardholder Billing" , N, :length => 12
60
+ # (...)
61
+ # end
62
+ #
63
+ # The example above defines four bitmaps (2,3,4 and 6), and provides
64
+ # their bitmap number and description. The PAN field is variable length
65
+ # encoded (LL length indicator, ASCII, contents numeric, ASCII) and the
66
+ # maximum length of the field is limited to 19 using options.
67
+ #
68
+ # The other fields are fixed length numeric ASCII fields (the length of the fields is
69
+ # indicated by the `:length` options.)
70
+ #
71
+ # This message may be used as follows in order to interpret a received message.:
72
+ #
73
+ # mes = MyMessage.parse inputData
74
+ # puts mes[2] # prints the PAN from the message.
75
+ #
76
+ # Constructing own messages works as follows:
77
+ #
78
+ # mes = MyMessage.new 1100
79
+ # mes[2]= 474747474747
80
+ # # Alternatively
81
+ # mes["Primary Account Number (PAN)"]= 4747474747
82
+ # mes[3] = 1234 # padding is added by the Field en/decoder
83
+ # mes["Amount (Transaction)"] = 100
84
+ # mes[6] = 200
85
+ #
86
+ # the convenience method bmp_alias may be used in defining the class in
87
+ # order to provide direct access to fields using methods:
88
+ #
89
+ # class MyMessage < Message
90
+ # (...)
91
+ # bmp 2, "Primary Account Number (PAN)", LLVAR_N, :max => 19
92
+ # (...)
93
+ # bmp_alias 2, :pan
94
+ # end
95
+ #
96
+ # this allows accessing fields in the following manner:
97
+ #
98
+ # mes = MyMessage.new 1100
99
+ # mes.pan = 474747474747
100
+ # puts mes.pan
101
+ # # Identical functionality to:
102
+ # mes[2]= 474747474747
103
+ # # or:
104
+ # mes["Primary Account Number (PAN)"]= 4747474747
105
+ #
106
+ # Most of the work in implementing a new set of message type lays in
107
+ # figuring out the correct fields to use defining the Message class via
108
+ # bmp.
109
+ #
110
+ class Message
111
+
112
+ # The value of the MTI (Message Type Indicator) of this message.
113
+ attr_reader :mti
114
+
115
+ # Instantiate a new instance of this type of Message
116
+ # optionally specifying an mti.
117
+ def initialize(mti = nil)
118
+ # values is an internal field used to collect all the
119
+ # bmp number | bmp name | field en/decoders | values
120
+ # which are set in this message.
121
+ @values = {}
122
+ self.mti = mti if mti
123
+ end
124
+
125
+ # Set the mti of the Message using either the actual value
126
+ # or the name of the message type that was defined using
127
+ # Message.mti
128
+ #
129
+ # === Example
130
+ # class MyMessage < Message
131
+ # (...)
132
+ # mti 1100, "Authorization Request Acquirer Gateway"
133
+ # end
134
+ #
135
+ # mes = MyMessage.new
136
+ # mes.mti = 1100 # or mes.mti = "Authorization Request Acquirer Gateway"
137
+ def mti=(value)
138
+ num, name = _get_mti_definition(value)
139
+ @mti = num
140
+ end
141
+
142
+ # Set a field in this message, `key` is either the
143
+ # bmp number or it's name.
144
+ # ===Example
145
+ #
146
+ # mes = BlaBlaMessage.new
147
+ # mes[2]=47474747 # bmp 2 is generally the PAN
148
+ # mes["Primary Account Number"]=47474747 # if thats what you called the field in Message.bmp.
149
+ def []=(key, value)
150
+ bmp_def = _get_definition key
151
+ bmp_def.value = value
152
+ @values[bmp_def.bmp] = bmp_def
153
+ end
154
+
155
+ # Retrieve the decoded value of the contents of a bitmap
156
+ # described either by the bitmap number or name.
157
+ #
158
+ # ===Example
159
+ #
160
+ # mes = BlaBlaMessage.parse someMessageBytes
161
+ # mes[2] # bmp 2 is generally the PAN
162
+ # mes["Primary Account Number"] # if thats what you called the field in Message.bmp.
163
+ def [](key)
164
+ bmp_def = _get_definition key
165
+ bmp = @values[bmp_def.bmp]
166
+ bmp ? bmp.value : nil
167
+ end
168
+
169
+ # Retrieve the byte representation of the bitmap.
170
+ def to_b
171
+ raise ISO8583Exception.new "no MTI set!" unless mti
172
+ mti_enc = self.class._mti_format.encode(mti)
173
+ mti_enc << _body.join
174
+ end
175
+
176
+ # Returns a nicely formatted representation of this
177
+ # message.
178
+ def to_s
179
+ _mti_name = _get_mti_definition(mti)[1]
180
+ str = "MTI:#{mti} (#{_mti_name})\n\n"
181
+ _max = @values.values.max {|a,b|
182
+ a.name.length <=> b.name.length
183
+ }
184
+ _max_name = _max.name.length
185
+
186
+ @values.keys.sort.each{|bmp_num|
187
+ _bmp = @values[bmp_num]
188
+ str += ("%03d %#{_max_name}s : %s\n" % [bmp_num, _bmp.name, _bmp.value])
189
+ }
190
+ str
191
+ end
192
+
193
+
194
+ # METHODS starting with an underscore are meant for
195
+ # internal use only ...
196
+
197
+ # Returns an array of two byte arrays:
198
+ # [bitmap_bytes, message_bytes]
199
+ def _body
200
+ bitmap = Bitmap.new
201
+ message = ""
202
+ @values.keys.sort.each do |bmp_num|
203
+ bitmap.set(bmp_num)
204
+ enc_value = @values[bmp_num].encode
205
+ message << enc_value
206
+ end
207
+ [ bitmap.to_bytes, message ]
208
+ end
209
+
210
+ def _get_definition(key) #:nodoc:
211
+ b = self.class._definitions[key]
212
+ unless b
213
+ raise ISO8583Exception.new "no definition for field: #{key}"
214
+ end
215
+ b.dup
216
+ end
217
+
218
+ # return [mti_num, mti_value] for key being either
219
+ # mti_num or mti_value
220
+ def _get_mti_definition(key)
221
+ num_hash,name_hash = self.class._mti_definitions
222
+ if num_hash[key]
223
+ [key, num_hash[key]]
224
+ elsif name_hash[key]
225
+ [name_hash[key], key]
226
+ else
227
+ raise ISO8583Exception.new("MTI: #{key} not allowed!")
228
+ end
229
+ end
230
+
231
+ class << self
232
+
233
+ # Defines how the message type indicator is encoded into bytes.
234
+ # ===Params:
235
+ # * field : the decoder/encoder for the MTI
236
+ # * opts : the options to pass to this field
237
+ #
238
+ # === Example
239
+ # class MyMessage < Message
240
+ # mti_format N, :length =>4
241
+ # (...)
242
+ # end
243
+ #
244
+ # encodes the mti of this message using the `N` field (fixed
245
+ # length, plain ASCII) and sets the fixed lengh to 4 bytes.
246
+ #
247
+ # See also: mti
248
+ def mti_format(field, opts)
249
+ f = field.dup
250
+ _handle_opts(f, opts)
251
+ @mti_format = f
252
+ end
253
+
254
+ # Defines the message types allowed for this type of message and
255
+ # gives them names
256
+ #
257
+ # === Example
258
+ # class MyMessage < Message
259
+ # (...)
260
+ # mti 1100, "Authorization Request Acquirer Gateway"
261
+ # end
262
+ #
263
+ # mes = MyMessage.new
264
+ # mes.mti = 1100 # or mes.mti = "Authorization Request Acquirer Gateway"
265
+ #
266
+ # See Also: mti_format
267
+ def mti(value, name)
268
+ @mtis_v ||= {}
269
+ @mtis_n ||= {}
270
+ @mtis_v[value] = name
271
+ @mtis_n[name] = value
272
+ end
273
+
274
+ # Define a bitmap in the message
275
+ # ===Params:
276
+ # * bmp : bitmap number
277
+ # * name : human readable form
278
+ # * field : field for encoding/decoding
279
+ # * opts : options to pass to the field, e.g. length for fxed len fields.
280
+ #
281
+ # ===Example
282
+ #
283
+ # class MyMessage < Message
284
+ # bmp 2, "PAN", LLVAR_N, :max =>19
285
+ # (...)
286
+ # end
287
+ #
288
+ # creates a class MyMessage that allows for a bitmap 2 which
289
+ # is named "PAN" and encoded by an LLVAR_N Field. The maximum
290
+ # length of the value is 19. This class may be used as follows:
291
+ #
292
+ # mes = MyMessage.new
293
+ # mes[2] = 474747474747 # or mes["PAN"] = 4747474747
294
+ #
295
+ def bmp(bmp, name, field, opts = nil)
296
+ @defs ||= {}
297
+
298
+ field = field.dup
299
+ field.name = name
300
+ field.bmp = bmp
301
+ _handle_opts(field, opts) if opts
302
+
303
+ bmp_def = BMP.new bmp, name, field
304
+
305
+ @defs[bmp] = bmp_def
306
+ @defs[name] = bmp_def
307
+ end
308
+
309
+ # Create an alias to access bitmaps directly using a method.
310
+ # Example:
311
+ # class MyMessage < Message
312
+ # (...)
313
+ # bmp 2, "PAN", LLVAR_N
314
+ # (...)
315
+ # bmp_alias 2, :pan
316
+ # end #class
317
+ #
318
+ # would allow you to access the PAN like this:
319
+ #
320
+ # mes.pan = 1234
321
+ # puts mes.pan
322
+ #
323
+ # instead of:
324
+ #
325
+ # mes[2] = 1234
326
+ #
327
+ def bmp_alias(bmp, aliaz)
328
+ define_method (aliaz) {
329
+ bmp_ = @values[bmp]
330
+ bmp_ ? bmp_.value : nil
331
+ }
332
+
333
+ define_method ("#{aliaz}=") {|value|
334
+ self[bmp] = value
335
+ # bmp_def = _get_definition(bmp)
336
+ # bmp_def.value= value
337
+ # @values[bmp] = bmp_def
338
+ }
339
+ end
340
+
341
+ # Parse the bytes `str` returning a message of the defined type.
342
+ def parse(str)
343
+ message = self.new
344
+ message.mti, rest = _mti_format.parse(str)
345
+ bmp,rest = Bitmap.parse(rest)
346
+ bmp.each {|bit|
347
+ bmp_def = _definitions[bit]
348
+ value, rest = bmp_def.field.parse(rest)
349
+ message[bit] = value
350
+ }
351
+ message
352
+ end
353
+
354
+ # access the mti definitions applicable to the Message
355
+ #
356
+ # returns a pair of hashes containing:
357
+ #
358
+ # mti_value => mti_name
359
+ #
360
+ # mti_name => mti_value
361
+ #
362
+ def _mti_definitions
363
+ [@mtis_v, @mtis_n]
364
+ end
365
+
366
+ # Access the field definitions of this class, this is a
367
+ # hash containing [bmp_number, BMP] and [bitmap_name, BMP]
368
+ # pairs.
369
+ #
370
+ def _definitions
371
+ @defs
372
+ end
373
+
374
+ # Returns the field definition to format the mti.
375
+ def _mti_format
376
+ @mti_format
377
+ end
378
+
379
+
380
+
381
+ # METHODS starting with an underscore are meant for
382
+ # internal use only ...
383
+
384
+ # Modifies the field definitions of the fields passed
385
+ # in through the `bmp` and `mti_format` class methods.
386
+ #
387
+ def _handle_opts(field, opts)
388
+ opts.each_pair {|key, value|
389
+ key = (key.to_s+"=").to_sym
390
+ if field.respond_to?(key)
391
+ field.send(key, value)
392
+ else
393
+ warn "unknown option #{key} for #{field.name}"
394
+ end
395
+ }
396
+ end
397
+ end
398
+ end
399
+
400
+ # Internal class used to tie together name, bitmap number, field en/decoder
401
+ # and the value of the corresponding field
402
+ class BMP
403
+ attr_accessor :bmp
404
+ attr_accessor :name
405
+ attr_accessor :field
406
+ attr_accessor :value
407
+
408
+ def initialize(bmp, name, field)
409
+ @bmp = bmp
410
+ @name = name
411
+ @field = field
412
+ end
413
+
414
+ def encode
415
+ field.encode(value)
416
+ end
417
+ end
418
+
419
+ end