enolib 0.5.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/LICENSE.txt +21 -0
- data/README.md +52 -0
- data/lib/enolib.rb +42 -0
- data/lib/enolib/constants.rb +16 -0
- data/lib/enolib/context.rb +220 -0
- data/lib/enolib/elements/element.rb +42 -0
- data/lib/enolib/elements/element_base.rb +141 -0
- data/lib/enolib/elements/empty.rb +9 -0
- data/lib/enolib/elements/field.rb +63 -0
- data/lib/enolib/elements/fieldset.rb +151 -0
- data/lib/enolib/elements/fieldset_entry.rb +15 -0
- data/lib/enolib/elements/list.rb +107 -0
- data/lib/enolib/elements/list_item.rb +13 -0
- data/lib/enolib/elements/missing/missing_element_base.rb +44 -0
- data/lib/enolib/elements/missing/missing_empty.rb +13 -0
- data/lib/enolib/elements/missing/missing_field.rb +13 -0
- data/lib/enolib/elements/missing/missing_fieldset.rb +29 -0
- data/lib/enolib/elements/missing/missing_fieldset_entry.rb +13 -0
- data/lib/enolib/elements/missing/missing_list.rb +33 -0
- data/lib/enolib/elements/missing/missing_section.rb +105 -0
- data/lib/enolib/elements/missing/missing_section_element.rb +53 -0
- data/lib/enolib/elements/missing/missing_value_element_base.rb +21 -0
- data/lib/enolib/elements/section.rb +560 -0
- data/lib/enolib/elements/section_element.rb +141 -0
- data/lib/enolib/elements/value_element_base.rb +79 -0
- data/lib/enolib/errors.rb +25 -0
- data/lib/enolib/errors/parsing.rb +136 -0
- data/lib/enolib/errors/selections.rb +83 -0
- data/lib/enolib/errors/validation.rb +146 -0
- data/lib/enolib/grammar_regexp.rb +103 -0
- data/lib/enolib/lookup.rb +235 -0
- data/lib/enolib/messages/de.rb +79 -0
- data/lib/enolib/messages/en.rb +79 -0
- data/lib/enolib/messages/es.rb +79 -0
- data/lib/enolib/parse.rb +9 -0
- data/lib/enolib/parser.rb +708 -0
- data/lib/enolib/register.rb +24 -0
- data/lib/enolib/reporters/html_reporter.rb +115 -0
- data/lib/enolib/reporters/reporter.rb +258 -0
- data/lib/enolib/reporters/terminal_reporter.rb +107 -0
- data/lib/enolib/reporters/text_reporter.rb +46 -0
- metadata +130 -0
@@ -0,0 +1,33 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Enolib
|
4
|
+
class MissingList < MissingElementBase
|
5
|
+
def items
|
6
|
+
[]
|
7
|
+
end
|
8
|
+
|
9
|
+
def optional_string_values
|
10
|
+
[]
|
11
|
+
end
|
12
|
+
|
13
|
+
def optional_values(_loader)
|
14
|
+
[]
|
15
|
+
end
|
16
|
+
|
17
|
+
def required_string_values
|
18
|
+
[]
|
19
|
+
end
|
20
|
+
|
21
|
+
def required_values(_loader)
|
22
|
+
[]
|
23
|
+
end
|
24
|
+
|
25
|
+
def to_s
|
26
|
+
if @key
|
27
|
+
"#<Enolib::MissingList key=#{@key}>"
|
28
|
+
else
|
29
|
+
'#<Enolib::MissingList>'
|
30
|
+
end
|
31
|
+
end
|
32
|
+
end
|
33
|
+
end
|
@@ -0,0 +1,105 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Enolib
|
4
|
+
class MissingSection < MissingElementBase
|
5
|
+
def element(key = nil)
|
6
|
+
MissingSectionElement.new(key, self)
|
7
|
+
end
|
8
|
+
|
9
|
+
def elements
|
10
|
+
[]
|
11
|
+
end
|
12
|
+
|
13
|
+
def empty(key = nil)
|
14
|
+
MissingEmpty.new(key, self)
|
15
|
+
end
|
16
|
+
|
17
|
+
def field(key = nil)
|
18
|
+
MissingField.new(key, self)
|
19
|
+
end
|
20
|
+
|
21
|
+
def fields(_key = nil)
|
22
|
+
[]
|
23
|
+
end
|
24
|
+
|
25
|
+
def fieldset(key = nil)
|
26
|
+
MissingFieldset.new(key, self)
|
27
|
+
end
|
28
|
+
|
29
|
+
def fieldsets(_key = nil)
|
30
|
+
[]
|
31
|
+
end
|
32
|
+
|
33
|
+
def list(key = nil)
|
34
|
+
MissingList.new(key, self)
|
35
|
+
end
|
36
|
+
|
37
|
+
def lists(_key = nil)
|
38
|
+
[]
|
39
|
+
end
|
40
|
+
|
41
|
+
def optional_element(_key = nil)
|
42
|
+
nil
|
43
|
+
end
|
44
|
+
|
45
|
+
def optional_empty(_key = nil)
|
46
|
+
nil
|
47
|
+
end
|
48
|
+
|
49
|
+
def optional_field(_key = nil)
|
50
|
+
nil
|
51
|
+
end
|
52
|
+
|
53
|
+
def optional_fieldset(_key = nil)
|
54
|
+
nil
|
55
|
+
end
|
56
|
+
|
57
|
+
def optional_list(_key = nil)
|
58
|
+
nil
|
59
|
+
end
|
60
|
+
|
61
|
+
def optional_section(_key = nil)
|
62
|
+
nil
|
63
|
+
end
|
64
|
+
|
65
|
+
def required_element(_key = nil)
|
66
|
+
@parent._missing_error(self)
|
67
|
+
end
|
68
|
+
|
69
|
+
def required_empty(_key = nil)
|
70
|
+
@parent._missing_error(self)
|
71
|
+
end
|
72
|
+
|
73
|
+
def required_field(_key = nil)
|
74
|
+
@parent._missing_error(self)
|
75
|
+
end
|
76
|
+
|
77
|
+
def required_fieldset(_key = nil)
|
78
|
+
@parent._missing_error(self)
|
79
|
+
end
|
80
|
+
|
81
|
+
def required_list(_key = nil)
|
82
|
+
@parent._missing_error(self)
|
83
|
+
end
|
84
|
+
|
85
|
+
def required_section(_key = nil)
|
86
|
+
@parent._missing_error(self)
|
87
|
+
end
|
88
|
+
|
89
|
+
def section(key = nil)
|
90
|
+
MissingSection.new(key, self)
|
91
|
+
end
|
92
|
+
|
93
|
+
def sections(_key = nil)
|
94
|
+
[]
|
95
|
+
end
|
96
|
+
|
97
|
+
def to_s
|
98
|
+
if @key
|
99
|
+
"#<Enolib::MissingSection key=#{@key}>"
|
100
|
+
else
|
101
|
+
'#<Enolib::MissingSection>'
|
102
|
+
end
|
103
|
+
end
|
104
|
+
end
|
105
|
+
end
|
@@ -0,0 +1,53 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Enolib
|
4
|
+
class MissingSectionElement < MissingElementBase
|
5
|
+
def to_empty
|
6
|
+
MissingEmpty.new(@key, @parent)
|
7
|
+
end
|
8
|
+
|
9
|
+
def to_field
|
10
|
+
MissingField.new(@key, @parent)
|
11
|
+
end
|
12
|
+
|
13
|
+
def to_fieldset
|
14
|
+
MissingFieldset.new(@key, @parent)
|
15
|
+
end
|
16
|
+
|
17
|
+
def to_list
|
18
|
+
MissingList.new(@key, @parent)
|
19
|
+
end
|
20
|
+
|
21
|
+
def to_section
|
22
|
+
MissingSection.new(@key, @parent)
|
23
|
+
end
|
24
|
+
|
25
|
+
def yields_empty
|
26
|
+
true
|
27
|
+
end
|
28
|
+
|
29
|
+
def yields_field
|
30
|
+
true
|
31
|
+
end
|
32
|
+
|
33
|
+
def yields_fieldset
|
34
|
+
true
|
35
|
+
end
|
36
|
+
|
37
|
+
def yields_list
|
38
|
+
true
|
39
|
+
end
|
40
|
+
|
41
|
+
def yields_section
|
42
|
+
true
|
43
|
+
end
|
44
|
+
|
45
|
+
def to_s
|
46
|
+
if @key
|
47
|
+
"#<Enolib::MissingSectionElement key=#{@key}>"
|
48
|
+
else
|
49
|
+
'#<Enolib::MissingSectionElement>'
|
50
|
+
end
|
51
|
+
end
|
52
|
+
end
|
53
|
+
end
|
@@ -0,0 +1,21 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Enolib
|
4
|
+
class MissingValueElementBase < MissingElementBase
|
5
|
+
def optional_value(_loader)
|
6
|
+
nil
|
7
|
+
end
|
8
|
+
|
9
|
+
def optional_string_value
|
10
|
+
nil
|
11
|
+
end
|
12
|
+
|
13
|
+
def required_string_value
|
14
|
+
@parent._missing_error(self)
|
15
|
+
end
|
16
|
+
|
17
|
+
def required_value(_loader)
|
18
|
+
@parent._missing_error(self)
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end
|
@@ -0,0 +1,560 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
# TODO: For each value store the representational type as well ? (e.g. string may come from "- foo" or -- foo\nxxx\n-- foo) and use that for precise error messages?
|
4
|
+
|
5
|
+
module Enolib
|
6
|
+
class Section < ElementBase
|
7
|
+
def initialize(context, instruction, parent = nil)
|
8
|
+
super(context, instruction, parent)
|
9
|
+
|
10
|
+
@all_elements_required = parent ? parent.all_elements_required? : false
|
11
|
+
end
|
12
|
+
|
13
|
+
def _missing_error(element)
|
14
|
+
case element
|
15
|
+
when MissingField
|
16
|
+
raise Errors::Validation.missing_element(@context, element.instance_variable_get(:@key), @instruction, 'missing_field')
|
17
|
+
when MissingFieldset
|
18
|
+
raise Errors::Validation.missing_element(@context, element.instance_variable_get(:@key), @instruction, 'missing_fieldset')
|
19
|
+
when MissingList
|
20
|
+
raise Errors::Validation.missing_element(@context, element.instance_variable_get(:@key), @instruction, 'missing_list')
|
21
|
+
when MissingSection
|
22
|
+
raise Errors::Validation.missing_element(@context, element.instance_variable_get(:@key), @instruction, 'missing_section')
|
23
|
+
else
|
24
|
+
raise Errors::Validation.missing_element(@context, element.instance_variable_get(:@key), @instruction, 'missing_element')
|
25
|
+
end
|
26
|
+
end
|
27
|
+
|
28
|
+
def _untouched
|
29
|
+
return @instruction unless instance_variable_defined?(:@touched)
|
30
|
+
|
31
|
+
_elements.each do |element|
|
32
|
+
untouched_element = element._untouched
|
33
|
+
return untouched_element if untouched_element
|
34
|
+
end
|
35
|
+
|
36
|
+
false
|
37
|
+
end
|
38
|
+
|
39
|
+
def all_elements_required(required = true)
|
40
|
+
@all_elements_required = required
|
41
|
+
|
42
|
+
_elements.each do |element|
|
43
|
+
if element.instruciton[:type] == :section && element.yielded?
|
44
|
+
element.to_section.all_elements_required(required)
|
45
|
+
elsif element.instruciton[:type] == :fieldset && element.yielded?
|
46
|
+
element.to_fieldset.all_entries_required(required)
|
47
|
+
end
|
48
|
+
end
|
49
|
+
end
|
50
|
+
|
51
|
+
def all_elements_required?
|
52
|
+
@all_elements_required
|
53
|
+
end
|
54
|
+
|
55
|
+
def assert_all_touched(message = nil, except: nil, only: nil)
|
56
|
+
message = Proc.new if block_given?
|
57
|
+
|
58
|
+
_elements(map: true).each do |key, elements|
|
59
|
+
next if except && except.include?(key) || only && !only.include?(key)
|
60
|
+
|
61
|
+
elements.each do |element|
|
62
|
+
untouched = element._untouched
|
63
|
+
|
64
|
+
if untouched
|
65
|
+
if message.is_a?(Proc)
|
66
|
+
message = message.call(Element.new(@context, untouched, self))
|
67
|
+
end
|
68
|
+
|
69
|
+
raise Errors::Validation.unexpected_element(@context, message, untouched)
|
70
|
+
end
|
71
|
+
end
|
72
|
+
end
|
73
|
+
end
|
74
|
+
|
75
|
+
def element(key = nil)
|
76
|
+
_element(key)
|
77
|
+
end
|
78
|
+
|
79
|
+
def elements(key = nil)
|
80
|
+
@touched = true
|
81
|
+
|
82
|
+
if key
|
83
|
+
elements_map = _elements(map: true)
|
84
|
+
elements_map.has_key?(key) ? elements_map[key] : []
|
85
|
+
else
|
86
|
+
_elements
|
87
|
+
end
|
88
|
+
end
|
89
|
+
|
90
|
+
def empty(key = nil)
|
91
|
+
_empty(key)
|
92
|
+
end
|
93
|
+
|
94
|
+
def field(key = nil)
|
95
|
+
_field(key)
|
96
|
+
end
|
97
|
+
|
98
|
+
def fields(key = nil)
|
99
|
+
@touched = true
|
100
|
+
|
101
|
+
if key
|
102
|
+
elements_map = _elements(map: true)
|
103
|
+
elements = elements_map.has_key?(key) ? elements_map[key] : []
|
104
|
+
else
|
105
|
+
elements = _elements
|
106
|
+
end
|
107
|
+
|
108
|
+
elements.map do |element|
|
109
|
+
unless element.yields_field?
|
110
|
+
raise Errors::Validation.unexpected_element_type(
|
111
|
+
@context,
|
112
|
+
key,
|
113
|
+
element.instruction,
|
114
|
+
'expected_fields'
|
115
|
+
)
|
116
|
+
end
|
117
|
+
|
118
|
+
element.to_field
|
119
|
+
end
|
120
|
+
end
|
121
|
+
|
122
|
+
def fieldset(key = nil)
|
123
|
+
_fieldset(key)
|
124
|
+
end
|
125
|
+
|
126
|
+
def fieldsets(key = nil)
|
127
|
+
@touched = true
|
128
|
+
|
129
|
+
if key
|
130
|
+
elements_map = _elements(map: true)
|
131
|
+
elements = elements_map.has_key?(key) ? elements_map[key] : []
|
132
|
+
else
|
133
|
+
elements = _elements
|
134
|
+
end
|
135
|
+
|
136
|
+
elements.map do |element|
|
137
|
+
unless element.yields_fieldset?
|
138
|
+
raise Errors::Validation.unexpected_element_type(
|
139
|
+
@context,
|
140
|
+
key,
|
141
|
+
element.instruction,
|
142
|
+
'expected_fieldsets'
|
143
|
+
)
|
144
|
+
end
|
145
|
+
|
146
|
+
element.to_fieldset
|
147
|
+
end
|
148
|
+
end
|
149
|
+
|
150
|
+
def list(key = nil)
|
151
|
+
_list(key)
|
152
|
+
end
|
153
|
+
|
154
|
+
def lists(key = nil)
|
155
|
+
@touched = true
|
156
|
+
|
157
|
+
if key
|
158
|
+
elements_map = _elements(map: true)
|
159
|
+
elements = elements_map.has_key?(key) ? elements_map[key] : []
|
160
|
+
else
|
161
|
+
elements = _elements
|
162
|
+
end
|
163
|
+
|
164
|
+
elements.map do |element|
|
165
|
+
unless element.yields_list?
|
166
|
+
raise Errors::Validation.unexpected_element_type(
|
167
|
+
@context,
|
168
|
+
key,
|
169
|
+
element.instruction,
|
170
|
+
'expected_lists'
|
171
|
+
)
|
172
|
+
end
|
173
|
+
|
174
|
+
element.to_list
|
175
|
+
end
|
176
|
+
end
|
177
|
+
|
178
|
+
def optional_element(key = nil)
|
179
|
+
_element(key, required: false)
|
180
|
+
end
|
181
|
+
|
182
|
+
def optional_empty(key = nil)
|
183
|
+
_empty(key, required: false)
|
184
|
+
end
|
185
|
+
|
186
|
+
def optional_field(key = nil)
|
187
|
+
_field(key, required: false)
|
188
|
+
end
|
189
|
+
|
190
|
+
def optional_fieldset(key = nil)
|
191
|
+
_fieldset(key, required: false)
|
192
|
+
end
|
193
|
+
|
194
|
+
def optional_list(key = nil)
|
195
|
+
_list(key, required: false)
|
196
|
+
end
|
197
|
+
|
198
|
+
def optional_section(key = nil)
|
199
|
+
_section(key, required: false)
|
200
|
+
end
|
201
|
+
|
202
|
+
def parent
|
203
|
+
if @instruction[:type] == :document
|
204
|
+
nil
|
205
|
+
else
|
206
|
+
@parent || Section.new(@context, @instruction[:parent])
|
207
|
+
end
|
208
|
+
end
|
209
|
+
|
210
|
+
def required_element(key = nil)
|
211
|
+
_element(key, required: true)
|
212
|
+
end
|
213
|
+
|
214
|
+
def required_empty(key = nil)
|
215
|
+
_empty(key, required: true)
|
216
|
+
end
|
217
|
+
|
218
|
+
def required_field(key = nil)
|
219
|
+
_field(key, required: true)
|
220
|
+
end
|
221
|
+
|
222
|
+
def required_fieldset(key = nil)
|
223
|
+
_fieldset(key, required: true)
|
224
|
+
end
|
225
|
+
|
226
|
+
def required_list(key = nil)
|
227
|
+
_list(key, required: true)
|
228
|
+
end
|
229
|
+
|
230
|
+
def required_section(key = nil)
|
231
|
+
_section(key, required: true)
|
232
|
+
end
|
233
|
+
|
234
|
+
def section(key = nil)
|
235
|
+
_section(key)
|
236
|
+
end
|
237
|
+
|
238
|
+
def sections(key = nil)
|
239
|
+
@touched = true
|
240
|
+
|
241
|
+
if key
|
242
|
+
elements_map = _elements(map: true)
|
243
|
+
elements = elements_map.has_key?(key) ? elements_map[key] : []
|
244
|
+
else
|
245
|
+
elements = _elements
|
246
|
+
end
|
247
|
+
|
248
|
+
elements.map do |element|
|
249
|
+
unless element.yields_section?
|
250
|
+
raise Errors::Validation.unexpected_element_type(
|
251
|
+
@context,
|
252
|
+
key,
|
253
|
+
element.instruction,
|
254
|
+
'expected_sections'
|
255
|
+
)
|
256
|
+
end
|
257
|
+
|
258
|
+
element.to_section
|
259
|
+
end
|
260
|
+
end
|
261
|
+
|
262
|
+
def to_s
|
263
|
+
if @instruction[:type] == :document
|
264
|
+
"#<Enolib::Section document elements=#{elements.length}>"
|
265
|
+
else
|
266
|
+
"#<Enolib::Section key=#{@instruction[:key]} elements=#{elements.length}>"
|
267
|
+
end
|
268
|
+
end
|
269
|
+
|
270
|
+
def touch
|
271
|
+
@touched = true
|
272
|
+
|
273
|
+
_elements.each do |element|
|
274
|
+
element.touch
|
275
|
+
end
|
276
|
+
end
|
277
|
+
|
278
|
+
private
|
279
|
+
|
280
|
+
def _element(key = nil, required: nil)
|
281
|
+
@touched = true
|
282
|
+
|
283
|
+
if key
|
284
|
+
elements_map = _elements(map: true)
|
285
|
+
elements = elements_map.has_key?(key) ? elements_map[key] : []
|
286
|
+
else
|
287
|
+
elements = _elements
|
288
|
+
end
|
289
|
+
|
290
|
+
if elements.empty?
|
291
|
+
if required || @all_elements_required
|
292
|
+
raise Errors::Validation.missing_element(@context, key, @instruction, 'missing_element')
|
293
|
+
elsif required == nil
|
294
|
+
return MissingElement.new(key, self)
|
295
|
+
else
|
296
|
+
return nil
|
297
|
+
end
|
298
|
+
end
|
299
|
+
|
300
|
+
if elements.length > 1
|
301
|
+
raise Errors::Validation.unexpected_multiple_elements(
|
302
|
+
@context,
|
303
|
+
key,
|
304
|
+
elements.map(&:instruction),
|
305
|
+
'expected_single_element'
|
306
|
+
)
|
307
|
+
end
|
308
|
+
|
309
|
+
elements[0]
|
310
|
+
end
|
311
|
+
|
312
|
+
def _elements(map: false)
|
313
|
+
unless instance_variable_defined?(:@instantiated_elements)
|
314
|
+
@instantiated_elements = []
|
315
|
+
@instantiated_elements_map = {}
|
316
|
+
instantiate_elements(@instruction)
|
317
|
+
end
|
318
|
+
|
319
|
+
map ? @instantiated_elements_map : @instantiated_elements
|
320
|
+
end
|
321
|
+
|
322
|
+
def _empty(key = nil, required: nil)
|
323
|
+
@touched = true
|
324
|
+
|
325
|
+
if key
|
326
|
+
elements_map = _elements(map: true)
|
327
|
+
elements = elements_map.has_key?(key) ? elements_map[key] : []
|
328
|
+
else
|
329
|
+
elements = _elements
|
330
|
+
end
|
331
|
+
|
332
|
+
if elements.empty?
|
333
|
+
if required || @all_elements_required
|
334
|
+
raise Errors::Validation.missing_element(@context, key, @instruction, 'missing_empty')
|
335
|
+
elsif required == nil
|
336
|
+
return MissingEmpty.new(key, self)
|
337
|
+
else
|
338
|
+
return nil
|
339
|
+
end
|
340
|
+
end
|
341
|
+
|
342
|
+
if elements.length > 1
|
343
|
+
raise Errors::Validation.unexpected_multiple_elements(
|
344
|
+
@context,
|
345
|
+
key,
|
346
|
+
elements.map(&:instruction),
|
347
|
+
'expected_single_empty'
|
348
|
+
)
|
349
|
+
end
|
350
|
+
|
351
|
+
element = elements[0]
|
352
|
+
|
353
|
+
unless element.yields_empty?
|
354
|
+
raise Errors::Validation.unexpected_element_type(
|
355
|
+
@context,
|
356
|
+
key,
|
357
|
+
element.instruction,
|
358
|
+
'expected_empty'
|
359
|
+
)
|
360
|
+
end
|
361
|
+
|
362
|
+
element.to_empty
|
363
|
+
end
|
364
|
+
|
365
|
+
def _field(key = nil, required: nil)
|
366
|
+
@touched = true
|
367
|
+
|
368
|
+
if key
|
369
|
+
elements_map = _elements(map: true)
|
370
|
+
elements = elements_map.has_key?(key) ? elements_map[key] : []
|
371
|
+
else
|
372
|
+
elements = _elements
|
373
|
+
end
|
374
|
+
|
375
|
+
if elements.empty?
|
376
|
+
if required || @all_elements_required
|
377
|
+
raise Errors::Validation.missing_element(@context, key, @instruction, 'missing_field')
|
378
|
+
elsif required == nil
|
379
|
+
return MissingField.new(key, self)
|
380
|
+
else
|
381
|
+
return nil
|
382
|
+
end
|
383
|
+
end
|
384
|
+
|
385
|
+
if elements.length > 1
|
386
|
+
raise Errors::Validation.unexpected_multiple_elements(
|
387
|
+
@context,
|
388
|
+
key,
|
389
|
+
elements.map(&:instruction),
|
390
|
+
'expected_single_field'
|
391
|
+
)
|
392
|
+
end
|
393
|
+
|
394
|
+
element = elements[0]
|
395
|
+
|
396
|
+
unless element.yields_field?
|
397
|
+
raise Errors::Validation.unexpected_element_type(
|
398
|
+
@context,
|
399
|
+
key,
|
400
|
+
element.instruction,
|
401
|
+
'expected_field'
|
402
|
+
)
|
403
|
+
end
|
404
|
+
|
405
|
+
element.to_field
|
406
|
+
end
|
407
|
+
|
408
|
+
def _fieldset(key = nil, required: nil)
|
409
|
+
@touched = true
|
410
|
+
|
411
|
+
if key
|
412
|
+
elements_map = _elements(map: true)
|
413
|
+
elements = elements_map.has_key?(key) ? elements_map[key] : []
|
414
|
+
else
|
415
|
+
elements = _elements
|
416
|
+
end
|
417
|
+
|
418
|
+
if elements.empty?
|
419
|
+
if required || @all_elements_required
|
420
|
+
raise Errors::Validation.missing_element(@context, key, @instruction, 'missing_fieldset')
|
421
|
+
elsif required == nil
|
422
|
+
return MissingFieldset.new(key, self)
|
423
|
+
else
|
424
|
+
return nil
|
425
|
+
end
|
426
|
+
end
|
427
|
+
|
428
|
+
if elements.length > 1
|
429
|
+
raise Errors::Validation.unexpected_multiple_elements(
|
430
|
+
@context,
|
431
|
+
key,
|
432
|
+
elements.map(&:instruction),
|
433
|
+
'expected_single_fieldset'
|
434
|
+
)
|
435
|
+
end
|
436
|
+
|
437
|
+
element = elements[0]
|
438
|
+
|
439
|
+
unless element.yields_fieldset?
|
440
|
+
raise Errors::Validation.unexpected_element_type(
|
441
|
+
@context,
|
442
|
+
key,
|
443
|
+
element.instruction,
|
444
|
+
'expected_fieldset'
|
445
|
+
)
|
446
|
+
end
|
447
|
+
|
448
|
+
element.to_fieldset
|
449
|
+
end
|
450
|
+
|
451
|
+
def instantiate_elements(section)
|
452
|
+
if section.has_key?(:mirror)
|
453
|
+
instantiate_elements(section[:mirror])
|
454
|
+
else
|
455
|
+
filtered = section[:elements].reject { |element| @instantiated_elements_map.has_key?(element[:key]) }
|
456
|
+
instantiated = filtered.map do |element|
|
457
|
+
instance = SectionElement.new(@context, element, self)
|
458
|
+
|
459
|
+
if @instantiated_elements_map.has_key?(element[:key])
|
460
|
+
@instantiated_elements_map[element[:key]].push(instance)
|
461
|
+
else
|
462
|
+
@instantiated_elements_map[element[:key]] = [instance]
|
463
|
+
end
|
464
|
+
|
465
|
+
instance
|
466
|
+
end
|
467
|
+
|
468
|
+
@instantiated_elements.concat(instantiated) # TODO: Revisit order of this and the following
|
469
|
+
|
470
|
+
instantiate_elements(section[:extend]) if section.has_key?(:extend)
|
471
|
+
end
|
472
|
+
end
|
473
|
+
|
474
|
+
def _list(key = nil, required: nil)
|
475
|
+
@touched = true
|
476
|
+
|
477
|
+
if key
|
478
|
+
elements_map = _elements(map: true)
|
479
|
+
elements = elements_map.has_key?(key) ? elements_map[key] : []
|
480
|
+
else
|
481
|
+
elements = _elements
|
482
|
+
end
|
483
|
+
|
484
|
+
if elements.empty?
|
485
|
+
if required || @all_elements_required
|
486
|
+
raise Errors::Validation.missing_element(@context, key, @instruction, 'missing_list')
|
487
|
+
elsif required == nil
|
488
|
+
return MissingList.new(key, self)
|
489
|
+
else
|
490
|
+
return nil
|
491
|
+
end
|
492
|
+
end
|
493
|
+
|
494
|
+
if elements.length > 1
|
495
|
+
raise Errors::Validation.unexpected_multiple_elements(
|
496
|
+
@context,
|
497
|
+
key,
|
498
|
+
elements.map(&:instruction),
|
499
|
+
'expected_single_list'
|
500
|
+
)
|
501
|
+
end
|
502
|
+
|
503
|
+
element = elements[0]
|
504
|
+
|
505
|
+
unless element.yields_list?
|
506
|
+
raise Errors::Validation.unexpected_element_type(
|
507
|
+
@context,
|
508
|
+
key,
|
509
|
+
element.instruction,
|
510
|
+
'expected_list'
|
511
|
+
)
|
512
|
+
end
|
513
|
+
|
514
|
+
element.to_list
|
515
|
+
end
|
516
|
+
|
517
|
+
def _section(key = nil, required: nil)
|
518
|
+
@touched = true
|
519
|
+
|
520
|
+
if key
|
521
|
+
elements_map = _elements(map: true)
|
522
|
+
elements = elements_map.has_key?(key) ? elements_map[key] : []
|
523
|
+
else
|
524
|
+
elements = _elements
|
525
|
+
end
|
526
|
+
|
527
|
+
if elements.empty?
|
528
|
+
if required || @all_elements_required
|
529
|
+
raise Errors::Validation.missing_element(@context, key, @instruction, 'missing_section')
|
530
|
+
elsif required == nil
|
531
|
+
return MissingSection.new(key, self)
|
532
|
+
else
|
533
|
+
return nil
|
534
|
+
end
|
535
|
+
end
|
536
|
+
|
537
|
+
if elements.length > 1
|
538
|
+
raise Errors::Validation.unexpected_multiple_elements(
|
539
|
+
@context,
|
540
|
+
key,
|
541
|
+
elements.map(&:instruction),
|
542
|
+
'expected_single_section'
|
543
|
+
)
|
544
|
+
end
|
545
|
+
|
546
|
+
element = elements[0]
|
547
|
+
|
548
|
+
unless element.yields_section?
|
549
|
+
raise Errors::Validation.unexpected_element_type(
|
550
|
+
@context,
|
551
|
+
key,
|
552
|
+
element.instruction,
|
553
|
+
'expected_section'
|
554
|
+
)
|
555
|
+
end
|
556
|
+
|
557
|
+
element.to_section
|
558
|
+
end
|
559
|
+
end
|
560
|
+
end
|