packetgen 3.1.4 → 3.2.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 +4 -4
- data/README.md +0 -1
- data/bin/pgconsole +1 -0
- data/lib/packetgen.rb +19 -3
- data/lib/packetgen/capture.rb +30 -9
- data/lib/packetgen/config.rb +15 -9
- data/lib/packetgen/deprecation.rb +1 -1
- data/lib/packetgen/header/asn1_base.rb +19 -9
- data/lib/packetgen/header/base.rb +68 -70
- data/lib/packetgen/header/dhcpv6/duid.rb +3 -1
- data/lib/packetgen/header/dhcpv6/option.rb +4 -12
- data/lib/packetgen/header/dns/name.rb +18 -7
- data/lib/packetgen/header/dns/qdsection.rb +1 -1
- data/lib/packetgen/header/dns/question.rb +2 -0
- data/lib/packetgen/header/dot11.rb +25 -38
- data/lib/packetgen/header/dot11/data.rb +28 -34
- data/lib/packetgen/header/dot1x.rb +1 -14
- data/lib/packetgen/header/eap.rb +14 -17
- data/lib/packetgen/header/eth.rb +5 -6
- data/lib/packetgen/header/http/headers.rb +4 -2
- data/lib/packetgen/header/http/request.rb +37 -18
- data/lib/packetgen/header/http/response.rb +11 -5
- data/lib/packetgen/header/http/verbs.rb +1 -1
- data/lib/packetgen/header/igmpv3/group_record.rb +2 -0
- data/lib/packetgen/header/ip.rb +27 -26
- data/lib/packetgen/header/ip/addr.rb +3 -1
- data/lib/packetgen/header/ip/option.rb +4 -4
- data/lib/packetgen/header/ipv6/addr.rb +2 -0
- data/lib/packetgen/header/mldv2/mcast_address_record.rb +2 -0
- data/lib/packetgen/header/ospfv2/ls_request.rb +2 -0
- data/lib/packetgen/header/ospfv2/lsa.rb +13 -3
- data/lib/packetgen/header/ospfv2/lsa_header.rb +2 -1
- data/lib/packetgen/header/ospfv3/ipv6_prefix.rb +2 -0
- data/lib/packetgen/header/ospfv3/ls_request.rb +2 -0
- data/lib/packetgen/header/ospfv3/lsa.rb +9 -3
- data/lib/packetgen/header/ospfv3/lsa_header.rb +2 -1
- data/lib/packetgen/header/snmp.rb +3 -2
- data/lib/packetgen/header/tcp.rb +1 -20
- data/lib/packetgen/header/tcp/option.rb +8 -6
- data/lib/packetgen/inspect.rb +1 -17
- data/lib/packetgen/packet.rb +10 -6
- data/lib/packetgen/pcapng.rb +11 -11
- data/lib/packetgen/pcapng/block.rb +15 -2
- data/lib/packetgen/pcapng/epb.rb +22 -15
- data/lib/packetgen/pcapng/file.rb +166 -81
- data/lib/packetgen/pcapng/idb.rb +7 -9
- data/lib/packetgen/pcapng/shb.rb +35 -28
- data/lib/packetgen/pcapng/spb.rb +16 -12
- data/lib/packetgen/pcapng/unknown_block.rb +3 -11
- data/lib/packetgen/pcaprub_wrapper.rb +25 -11
- data/lib/packetgen/types.rb +1 -0
- data/lib/packetgen/types/abstract_tlv.rb +3 -1
- data/lib/packetgen/types/array.rb +17 -10
- data/lib/packetgen/types/cstring.rb +56 -19
- data/lib/packetgen/types/enum.rb +4 -0
- data/lib/packetgen/types/fieldable.rb +65 -0
- data/lib/packetgen/types/fields.rb +180 -113
- data/lib/packetgen/types/int.rb +15 -1
- data/lib/packetgen/types/int_string.rb +8 -0
- data/lib/packetgen/types/length_from.rb +18 -10
- data/lib/packetgen/types/oui.rb +2 -0
- data/lib/packetgen/types/string.rb +58 -7
- data/lib/packetgen/types/tlv.rb +2 -0
- data/lib/packetgen/unknown_packet.rb +84 -0
- data/lib/packetgen/utils.rb +6 -7
- data/lib/packetgen/version.rb +1 -1
- metadata +18 -15
data/lib/packetgen/types/enum.rb
CHANGED
@@ -0,0 +1,65 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
# This file is part of PacketGen
|
4
|
+
# See https://github.com/sdaubert/packetgen for more informations
|
5
|
+
# Copyright (C) 2016 Sylvain Daubert <sylvain.daubert@laposte.net>
|
6
|
+
# This program is published under MIT license.
|
7
|
+
|
8
|
+
module PacketGen
|
9
|
+
module Types
|
10
|
+
# Mixin to define minimal API for a class to be embbeded as a field in
|
11
|
+
# {Fields} type.
|
12
|
+
#
|
13
|
+
# == Optional methods
|
14
|
+
# These methods may, optionally, be defined by fieldable types:
|
15
|
+
# * +from_human+ to load data from a human-readable string.
|
16
|
+
# @author Sylvain Daubert
|
17
|
+
# @since 3.1.6
|
18
|
+
module Fieldable
|
19
|
+
# Get type name
|
20
|
+
# @return [String]
|
21
|
+
def type_name
|
22
|
+
self.class.to_s.split('::').last
|
23
|
+
end
|
24
|
+
|
25
|
+
# rubocop:disable Lint/UselessMethodDefinition
|
26
|
+
# These methods are defined for documentation.
|
27
|
+
|
28
|
+
# Populate object from a binary string
|
29
|
+
# @param [String] str
|
30
|
+
# @return [Fields] self
|
31
|
+
# @abstract subclass should overload it.
|
32
|
+
def read(str)
|
33
|
+
super
|
34
|
+
end
|
35
|
+
|
36
|
+
# Return object as a binary string
|
37
|
+
# @return [String]
|
38
|
+
# @abstract subclass should overload it.
|
39
|
+
def to_s
|
40
|
+
super
|
41
|
+
end
|
42
|
+
|
43
|
+
# Size of object as binary string
|
44
|
+
# @return [Integer]
|
45
|
+
def sz
|
46
|
+
to_s.size
|
47
|
+
end
|
48
|
+
|
49
|
+
# Return a human-readbale string
|
50
|
+
# @return [String]
|
51
|
+
# @abstract subclass should overload it.
|
52
|
+
def to_human
|
53
|
+
super
|
54
|
+
end
|
55
|
+
|
56
|
+
# rubocop:enable Lint/UselessMethodDefinition
|
57
|
+
|
58
|
+
# Format object when inspecting a {Field} object
|
59
|
+
# @return [String]
|
60
|
+
def format_inspect
|
61
|
+
to_human
|
62
|
+
end
|
63
|
+
end
|
64
|
+
end
|
65
|
+
end
|
@@ -5,6 +5,8 @@
|
|
5
5
|
# Copyright (C) 2016 Sylvain Daubert <sylvain.daubert@laposte.net>
|
6
6
|
# This program is published under MIT license.
|
7
7
|
|
8
|
+
# rubocop:disable Metrics/ClassLength
|
9
|
+
|
8
10
|
module PacketGen
|
9
11
|
module Types
|
10
12
|
# @abstract Set of fields
|
@@ -13,8 +15,7 @@ module PacketGen
|
|
13
15
|
#
|
14
16
|
# == Basics
|
15
17
|
# A {Fields} subclass is generaly composed of multiple binary fields. These fields
|
16
|
-
# have each a given type. All
|
17
|
-
# {Fields} subclasses may also be used as field type.
|
18
|
+
# have each a given type. All {Fieldable} types are supported.
|
18
19
|
#
|
19
20
|
# To define a new subclass, it has to inherit from {Fields}. And some class
|
20
21
|
# methods have to be used to declare attributes/fields:
|
@@ -119,17 +120,23 @@ module PacketGen
|
|
119
120
|
# @return [Hash]
|
120
121
|
# @since 3.1.0
|
121
122
|
attr_reader :field_defs
|
123
|
+
# Get bit fields defintions for this class
|
124
|
+
# @return [Hash]
|
125
|
+
# @since 3.1.5
|
126
|
+
attr_reader :bit_fields
|
122
127
|
|
123
128
|
# On inheritage, create +@field_defs+ class variable
|
124
129
|
# @param [Class] klass
|
125
130
|
# @return [void]
|
126
131
|
def inherited(klass)
|
132
|
+
super
|
133
|
+
|
127
134
|
field_defs = {}
|
128
135
|
@field_defs.each do |k, v|
|
129
136
|
field_defs[k] = v.clone
|
130
137
|
end
|
131
138
|
ordered = @ordered_fields.clone
|
132
|
-
bf =
|
139
|
+
bf = bit_fields.clone
|
133
140
|
|
134
141
|
klass.class_eval do
|
135
142
|
@ordered_fields = ordered
|
@@ -158,7 +165,7 @@ module PacketGen
|
|
158
165
|
# bs[value1] # => Types::Int8
|
159
166
|
# bs.value1 # => Integer
|
160
167
|
# @param [Symbol] name field name
|
161
|
-
# @param [
|
168
|
+
# @param [Fieldable] type class or instance
|
162
169
|
# @param [Hash] options Unrecognized options are passed to object builder if
|
163
170
|
# +:builder+ option is not set.
|
164
171
|
# @option options [Object] :default default value. May be a proc. This lambda
|
@@ -172,44 +179,22 @@ module PacketGen
|
|
172
179
|
# Define enumeration: hash's keys are +String+, and values are +Integer+.
|
173
180
|
# @return [void]
|
174
181
|
def define_field(name, type, options={})
|
175
|
-
|
176
|
-
if type < Types::Enum
|
177
|
-
define << "def #{name}; self[:#{name}].to_i; end"
|
178
|
-
define << "def #{name}=(val) self[:#{name}].value = val; end"
|
179
|
-
else
|
180
|
-
define << "def #{name}\n" \
|
181
|
-
" if self[:#{name}].respond_to?(:to_human) && self[:#{name}].respond_to?(:from_human)\n" \
|
182
|
-
" self[:#{name}].to_human\n" \
|
183
|
-
" else\n" \
|
184
|
-
" self[:#{name}]\n" \
|
185
|
-
" end\n" \
|
186
|
-
'end'
|
187
|
-
define << "def #{name}=(val)\n" \
|
188
|
-
" if self[:#{name}].respond_to?(:to_human) && self[:#{name}].respond_to?(:from_human)\n" \
|
189
|
-
" self[:#{name}].from_human val\n" \
|
190
|
-
" else\n" \
|
191
|
-
" self[:#{name}].read val\n" \
|
192
|
-
" end\n" \
|
193
|
-
'end'
|
194
|
-
end
|
195
|
-
|
196
|
-
define.delete_at(1) if instance_methods.include? "#{name}=".to_sym
|
197
|
-
define.delete_at(0) if instance_methods.include? name
|
198
|
-
class_eval define.join("\n")
|
182
|
+
fields << name
|
199
183
|
field_defs[name] = FieldDef.new(type,
|
200
184
|
options.delete(:default),
|
201
185
|
options.delete(:builder),
|
202
186
|
options.delete(:optional),
|
203
187
|
options.delete(:enum),
|
204
188
|
options)
|
205
|
-
|
189
|
+
|
190
|
+
add_methods(name, type)
|
206
191
|
end
|
207
192
|
|
208
193
|
# Define a field, before another one
|
209
194
|
# @param [Symbol,nil] other field name to create a new one before. If +nil+,
|
210
195
|
# new field is appended.
|
211
196
|
# @param [Symbol] name field name to create
|
212
|
-
# @param [
|
197
|
+
# @param [Fieldable] type class or instance
|
213
198
|
# @param [Hash] options See {.define_field}.
|
214
199
|
# @return [void]
|
215
200
|
# @see .define_field
|
@@ -228,7 +213,7 @@ module PacketGen
|
|
228
213
|
# @param [Symbol,nil] other field name to create a new one after. If +nil+,
|
229
214
|
# new field is appended.
|
230
215
|
# @param [Symbol] name field name to create
|
231
|
-
# @param [
|
216
|
+
# @param [Fieldable] type class or instance
|
232
217
|
# @param [Hash] options See {.define_field}.
|
233
218
|
# @return [void]
|
234
219
|
# @see .define_field
|
@@ -262,12 +247,12 @@ module PacketGen
|
|
262
247
|
# @raise [ArgumentError] unknown +field+
|
263
248
|
# @since 2.8.4
|
264
249
|
def update_field(field, options)
|
265
|
-
|
250
|
+
check_existence_of field
|
251
|
+
|
252
|
+
%i[default builder optional enum].each do |property|
|
253
|
+
field_defs_property_from(field, property, options)
|
254
|
+
end
|
266
255
|
|
267
|
-
field_defs[field].default = options.delete(:default) if options.key?(:default)
|
268
|
-
field_defs[field].builder = options.delete(:builder) if options.key?(:builder)
|
269
|
-
field_defs[field].optional = options.delete(:optional) if options.key?(:optional)
|
270
|
-
field_defs[field].enum = options.delete(:enum) if options.key?(:enum)
|
271
256
|
field_defs[field].options.merge!(options)
|
272
257
|
end
|
273
258
|
|
@@ -289,61 +274,29 @@ module PacketGen
|
|
289
274
|
# subclass)
|
290
275
|
# @param [Array] args list of bitfield names. Name may be followed
|
291
276
|
# by bitfield size. If no size is given, 1 bit is assumed.
|
277
|
+
# @raise [ArgumentError] unknown +attr+
|
292
278
|
# @return [void]
|
293
279
|
def define_bit_fields_on(attr, *args)
|
294
|
-
|
295
|
-
raise ArgumentError, "unknown #{attr} field" if attr_def.nil?
|
280
|
+
check_existence_of attr
|
296
281
|
|
297
|
-
type =
|
282
|
+
type = field_defs[attr].type
|
298
283
|
raise TypeError, "#{attr} is not a PacketGen::Types::Int" unless type < Types::Int
|
299
284
|
|
300
285
|
total_size = type.new.width * 8
|
301
286
|
idx = total_size - 1
|
302
287
|
|
303
|
-
|
304
|
-
|
288
|
+
until args.empty?
|
289
|
+
field = args.shift
|
305
290
|
next unless field.is_a? Symbol
|
306
291
|
|
307
|
-
size =
|
308
|
-
|
309
|
-
else
|
310
|
-
1
|
311
|
-
end
|
292
|
+
size = size_from(args)
|
293
|
+
|
312
294
|
unless field == :_
|
313
|
-
|
314
|
-
|
315
|
-
clear_mask = (2**total_size - 1) & (~field_mask & (2**total_size - 1))
|
316
|
-
|
317
|
-
if size == 1
|
318
|
-
class_eval <<-METHODS
|
319
|
-
def #{field}?
|
320
|
-
val = (self[:#{attr}].to_i & #{field_mask}) >> #{shift}
|
321
|
-
val != 0
|
322
|
-
end
|
323
|
-
def #{field}=(v)
|
324
|
-
val = v ? 1 : 0
|
325
|
-
self[:#{attr}].value = self[:#{attr}].to_i & #{clear_mask}
|
326
|
-
self[:#{attr}].value |= val << #{shift}
|
327
|
-
end
|
328
|
-
METHODS
|
329
|
-
else
|
330
|
-
class_eval <<-METHODS
|
331
|
-
def #{field}
|
332
|
-
(self[:#{attr}].to_i & #{field_mask}) >> #{shift}
|
333
|
-
end
|
334
|
-
def #{field}=(v)
|
335
|
-
self[:#{attr}].value = self[:#{attr}].to_i & #{clear_mask}
|
336
|
-
self[:#{attr}].value |= (v & #{2**size - 1}) << #{shift}
|
337
|
-
end
|
338
|
-
METHODS
|
339
|
-
end
|
340
|
-
|
341
|
-
@bit_fields[attr] = {} if @bit_fields[attr].nil?
|
342
|
-
@bit_fields[attr][field] = size
|
295
|
+
add_bit_methods(attr, field, size, total_size, idx)
|
296
|
+
register_bit_field_size(attr, field, size)
|
343
297
|
end
|
344
298
|
|
345
299
|
idx -= size
|
346
|
-
field = args.shift
|
347
300
|
end
|
348
301
|
end
|
349
302
|
|
@@ -352,7 +305,7 @@ module PacketGen
|
|
352
305
|
# @return [void]
|
353
306
|
# @since 2.8.4
|
354
307
|
def remove_bit_fields_on(attr)
|
355
|
-
fields =
|
308
|
+
fields = bit_fields.delete(attr)
|
356
309
|
return if fields.nil?
|
357
310
|
|
358
311
|
fields.each do |field, size|
|
@@ -360,6 +313,98 @@ module PacketGen
|
|
360
313
|
undef_method(size == 1 ? "#{field}?" : field)
|
361
314
|
end
|
362
315
|
end
|
316
|
+
|
317
|
+
private
|
318
|
+
|
319
|
+
def add_methods(name, type)
|
320
|
+
define = []
|
321
|
+
if type < Types::Enum
|
322
|
+
define << "def #{name}; self[:#{name}].to_i; end"
|
323
|
+
define << "def #{name}=(val) self[:#{name}].value = val; end"
|
324
|
+
else
|
325
|
+
define << "def #{name}\n" \
|
326
|
+
" to_and_from_human?(:#{name}) ? self[:#{name}].to_human : self[:#{name}]\n" \
|
327
|
+
'end'
|
328
|
+
define << "def #{name}=(val)\n" \
|
329
|
+
" to_and_from_human?(:#{name}) ? self[:#{name}].from_human(val) : self[:#{name}].read(val)\n" \
|
330
|
+
'end'
|
331
|
+
end
|
332
|
+
|
333
|
+
define.delete_at(1) if instance_methods.include? "#{name}=".to_sym
|
334
|
+
define.delete_at(0) if instance_methods.include? name
|
335
|
+
class_eval define.join("\n")
|
336
|
+
end
|
337
|
+
|
338
|
+
def add_bit_methods(attr, name, size, total_size, idx)
|
339
|
+
shift = idx - (size - 1)
|
340
|
+
|
341
|
+
if size == 1
|
342
|
+
add_single_bit_methods(attr, name, size, total_size, shift)
|
343
|
+
else
|
344
|
+
add_multibit_methods(attr, name, size, total_size, shift)
|
345
|
+
end
|
346
|
+
end
|
347
|
+
|
348
|
+
def compute_field_mask(size, shift)
|
349
|
+
(2**size - 1) << shift
|
350
|
+
end
|
351
|
+
|
352
|
+
def compute_clear_mask(total_size, field_mask)
|
353
|
+
(2**total_size - 1) & (~field_mask & (2**total_size - 1))
|
354
|
+
end
|
355
|
+
|
356
|
+
def add_single_bit_methods(attr, name, size, total_size, shift)
|
357
|
+
field_mask = compute_field_mask(size, shift)
|
358
|
+
clear_mask = compute_clear_mask(total_size, field_mask)
|
359
|
+
|
360
|
+
class_eval <<-METHODS
|
361
|
+
def #{name}?
|
362
|
+
val = (self[:#{attr}].to_i & #{field_mask}) >> #{shift}
|
363
|
+
val != 0
|
364
|
+
end
|
365
|
+
def #{name}=(v)
|
366
|
+
val = v ? 1 : 0
|
367
|
+
self[:#{attr}].value = self[:#{attr}].to_i & #{clear_mask}
|
368
|
+
self[:#{attr}].value |= val << #{shift}
|
369
|
+
end
|
370
|
+
METHODS
|
371
|
+
end
|
372
|
+
|
373
|
+
def add_multibit_methods(attr, name, size, total_size, shift)
|
374
|
+
field_mask = compute_field_mask(size, shift)
|
375
|
+
clear_mask = compute_clear_mask(total_size, field_mask)
|
376
|
+
|
377
|
+
class_eval <<-METHODS
|
378
|
+
def #{name}
|
379
|
+
(self[:#{attr}].to_i & #{field_mask}) >> #{shift}
|
380
|
+
end
|
381
|
+
def #{name}=(v)
|
382
|
+
self[:#{attr}].value = self[:#{attr}].to_i & #{clear_mask}
|
383
|
+
self[:#{attr}].value |= (v & #{2**size - 1}) << #{shift}
|
384
|
+
end
|
385
|
+
METHODS
|
386
|
+
end
|
387
|
+
|
388
|
+
def register_bit_field_size(attr, field, size)
|
389
|
+
bit_fields[attr] = {} if bit_fields[attr].nil?
|
390
|
+
bit_fields[attr][field] = size
|
391
|
+
end
|
392
|
+
|
393
|
+
def field_defs_property_from(field, property, options)
|
394
|
+
field_defs[field].send("#{property}=", options.delete(property)) if options.key?(property)
|
395
|
+
end
|
396
|
+
|
397
|
+
def size_from(args)
|
398
|
+
if args.first.is_a? Integer
|
399
|
+
args.shift
|
400
|
+
else
|
401
|
+
1
|
402
|
+
end
|
403
|
+
end
|
404
|
+
|
405
|
+
def check_existence_of(field)
|
406
|
+
raise ArgumentError, "unknown #{field} field for #{self}" unless field_defs.key?(field)
|
407
|
+
end
|
363
408
|
end
|
364
409
|
|
365
410
|
# Create a new fields object
|
@@ -369,36 +414,13 @@ module PacketGen
|
|
369
414
|
@fields = {}
|
370
415
|
@optional_fields = {}
|
371
416
|
|
372
|
-
field_defs = self.class.field_defs
|
373
417
|
self.class.fields.each do |field|
|
374
|
-
|
375
|
-
|
376
|
-
|
377
|
-
builder = field_defs[field].builder
|
378
|
-
optional = field_defs[field].optional
|
379
|
-
enum = field_defs[field].enum
|
380
|
-
field_options = field_defs[field].options
|
381
|
-
|
382
|
-
@fields[field] = if builder
|
383
|
-
builder.call(self, type)
|
384
|
-
elsif enum
|
385
|
-
type.new(enum)
|
386
|
-
elsif !field_options.empty?
|
387
|
-
type.new(field_options)
|
388
|
-
else
|
389
|
-
type.new
|
390
|
-
end
|
391
|
-
|
392
|
-
value = options[field] || default
|
393
|
-
if value.class <= type
|
394
|
-
@fields[field] = value
|
395
|
-
elsif @fields[field].respond_to? :from_human
|
396
|
-
@fields[field].from_human(value)
|
397
|
-
end
|
398
|
-
|
399
|
-
@optional_fields[field] = optional if optional
|
418
|
+
build_field field
|
419
|
+
initialize_value field, options[field]
|
420
|
+
initialize_optional field
|
400
421
|
end
|
401
|
-
|
422
|
+
|
423
|
+
self.class.bit_fields.each do |_, hsh|
|
402
424
|
hsh.each_key do |bit_field|
|
403
425
|
self.send "#{bit_field}=", options[bit_field] if options[bit_field]
|
404
426
|
end
|
@@ -407,7 +429,7 @@ module PacketGen
|
|
407
429
|
|
408
430
|
# Get field object
|
409
431
|
# @param [Symbol] field
|
410
|
-
# @return [
|
432
|
+
# @return [Fieldable]
|
411
433
|
def [](field)
|
412
434
|
@fields[field]
|
413
435
|
end
|
@@ -427,6 +449,7 @@ module PacketGen
|
|
427
449
|
end
|
428
450
|
|
429
451
|
# Get all optional field name
|
452
|
+
# @return[Array<Symbol>,nil]
|
430
453
|
def optional_fields
|
431
454
|
@optional_fields.keys
|
432
455
|
end
|
@@ -457,11 +480,7 @@ module PacketGen
|
|
457
480
|
next unless present?(field)
|
458
481
|
|
459
482
|
obj = self[field].read str[start..-1]
|
460
|
-
|
461
|
-
start += self[field].sz
|
462
|
-
else
|
463
|
-
start = str.size
|
464
|
-
end
|
483
|
+
start += self[field].sz
|
465
484
|
self[field] = obj unless obj == self[field]
|
466
485
|
end
|
467
486
|
|
@@ -528,7 +547,7 @@ module PacketGen
|
|
528
547
|
# @return [Hash,nil] keys: bit fields, values: their size in bits
|
529
548
|
# @since 2.8.3
|
530
549
|
def bits_on(field)
|
531
|
-
self.class.
|
550
|
+
self.class.bit_fields[field]
|
532
551
|
end
|
533
552
|
|
534
553
|
private
|
@@ -547,6 +566,54 @@ module PacketGen
|
|
547
566
|
def force_binary(str)
|
548
567
|
PacketGen.force_binary(str)
|
549
568
|
end
|
569
|
+
|
570
|
+
# @param [Symbol] attr attribute
|
571
|
+
# @return [Boolean] +tru+e if #from_human and #to_human are both defined for given attribute
|
572
|
+
def to_and_from_human?(attr)
|
573
|
+
self[attr].respond_to?(:to_human) && self[attr].respond_to?(:from_human)
|
574
|
+
end
|
575
|
+
|
576
|
+
def field_defs
|
577
|
+
self.class.field_defs
|
578
|
+
end
|
579
|
+
|
580
|
+
# rubocop:disable Metrics/AbcSize
|
581
|
+
def build_field(field)
|
582
|
+
type = field_defs[field].type
|
583
|
+
|
584
|
+
@fields[field] = if field_defs[field].builder
|
585
|
+
field_defs[field].builder.call(self, type)
|
586
|
+
elsif field_defs[field].enum
|
587
|
+
type.new(field_defs[field].enum)
|
588
|
+
elsif !field_defs[field].options.empty?
|
589
|
+
type.new(field_defs[field].options)
|
590
|
+
else
|
591
|
+
type.new
|
592
|
+
end
|
593
|
+
end
|
594
|
+
# rubocop:enable Metrics/AbcSize
|
595
|
+
|
596
|
+
def initialize_value(field, val)
|
597
|
+
type = field_defs[field].type
|
598
|
+
default = field_defs[field].default
|
599
|
+
default = default.to_proc.call(self) if default.is_a?(Proc)
|
600
|
+
|
601
|
+
value = val || default
|
602
|
+
if value.class <= type
|
603
|
+
@fields[field] = value
|
604
|
+
elsif @fields[field].respond_to? :from_human
|
605
|
+
@fields[field].from_human(value)
|
606
|
+
else
|
607
|
+
@fields[field].read(value)
|
608
|
+
end
|
609
|
+
end
|
610
|
+
|
611
|
+
def initialize_optional(field)
|
612
|
+
optional = field_defs[field].optional
|
613
|
+
@optional_fields[field] = optional if optional
|
614
|
+
end
|
550
615
|
end
|
551
616
|
end
|
552
617
|
end
|
618
|
+
|
619
|
+
# rubocop:enable Metrics/ClassLength
|