enolib 0.5.0
Sign up to get free protection for your applications and to get access to all the features.
- 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
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA256:
|
3
|
+
metadata.gz: 8aeeb10368acccea0f1381c34c6101a552493aea19ef50d8bdc7616f9a898cc7
|
4
|
+
data.tar.gz: 576efc4e4191c945a4d262789e2d353d3521d712b63d1ce4236937d7a556e387
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: a37196b8d2b69595e22f68b2b9025f64ec01028442ceddf4d6181cea71903f570561d0c2487d515bbad98ecde76c912284be282f8b3c54590cd026c716c8da33
|
7
|
+
data.tar.gz: e9c8143539d6eb1fbf7a6e594208a70d5d6cad25f77d471556b1091d7bd4b3cfd268dbb28a0b764cbc0701b01184bd428cedfd6aecea24bac9d9c222ec8873f6
|
data/LICENSE.txt
ADDED
@@ -0,0 +1,21 @@
|
|
1
|
+
MIT License
|
2
|
+
|
3
|
+
Copyright (c) 2019 Simon Repp
|
4
|
+
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
7
|
+
in the Software without restriction, including without limitation the rights
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
10
|
+
furnished to do so, subject to the following conditions:
|
11
|
+
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
13
|
+
copies or substantial portions of the Software.
|
14
|
+
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
21
|
+
SOFTWARE.
|
data/README.md
ADDED
@@ -0,0 +1,52 @@
|
|
1
|
+
# enolib
|
2
|
+
|
3
|
+
The cross-language eno standard library
|
4
|
+
|
5
|
+
## Installation
|
6
|
+
|
7
|
+
### Bundler
|
8
|
+
|
9
|
+
Add `enolib` to your `Gemfile`:
|
10
|
+
|
11
|
+
```ruby
|
12
|
+
gem 'enolib'
|
13
|
+
```
|
14
|
+
Then let bundler install it for you:
|
15
|
+
|
16
|
+
```
|
17
|
+
bundle
|
18
|
+
```
|
19
|
+
|
20
|
+
### Manually
|
21
|
+
|
22
|
+
Alternatively you can also install it manually:
|
23
|
+
|
24
|
+
```
|
25
|
+
gem install enolib
|
26
|
+
```
|
27
|
+
|
28
|
+
## Getting started
|
29
|
+
|
30
|
+
A minimal example to read an eno document directly from a string with `enolib`:
|
31
|
+
|
32
|
+
```ruby
|
33
|
+
require 'enolib'
|
34
|
+
|
35
|
+
document = Enolib.parse('Greeting: Hello World!')
|
36
|
+
|
37
|
+
puts document.field('Greeting').required_string_value #=> 'Hello World!'
|
38
|
+
```
|
39
|
+
|
40
|
+
## Documentation
|
41
|
+
|
42
|
+
Until available please use the javascript or python documentation at [eno-lang.org/enolib](https://eno-lang.org/enolib/) and replace all shown syntax with the ruby equivalent (ie. different case for the most part) - except for language specifics the API is exactly the same for all implementations.
|
43
|
+
|
44
|
+
## Beta notice
|
45
|
+
|
46
|
+
enolib is currently in beta, if you encounter any issues please report them in the issue tracker - thank you for your help!
|
47
|
+
|
48
|
+
## Compatibility notice
|
49
|
+
|
50
|
+
enolib supports the not yet completely ratified [final specification](https://github.com/eno-lang/eno/tree/master/rfcs-final-spec) for eno. You are encouraged to use it in your projects already, thereby helping us to gather insights on whether there are any remaining flaws in the proposed final specifications (you're very welcome to report such issues if you encounter them).
|
51
|
+
|
52
|
+
From a practical point of view there are not likely any major changes ahead. Unless you build your application around edge cases of the exotic language features you should be perfectly safe and future-proof.
|
data/lib/enolib.rb
ADDED
@@ -0,0 +1,42 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'enolib/constants'
|
4
|
+
require 'enolib/context'
|
5
|
+
require 'enolib/errors'
|
6
|
+
require 'enolib/grammar_regexp'
|
7
|
+
require 'enolib/messages/en'
|
8
|
+
require 'enolib/lookup'
|
9
|
+
require 'enolib/parse'
|
10
|
+
require 'enolib/parser'
|
11
|
+
require 'enolib/register'
|
12
|
+
|
13
|
+
require 'enolib/elements/element_base'
|
14
|
+
require 'enolib/elements/value_element_base'
|
15
|
+
require 'enolib/elements/section_element'
|
16
|
+
require 'enolib/elements/element'
|
17
|
+
require 'enolib/elements/empty'
|
18
|
+
require 'enolib/elements/field'
|
19
|
+
require 'enolib/elements/fieldset_entry'
|
20
|
+
require 'enolib/elements/fieldset'
|
21
|
+
require 'enolib/elements/list_item'
|
22
|
+
require 'enolib/elements/list'
|
23
|
+
require 'enolib/elements/section'
|
24
|
+
|
25
|
+
require 'enolib/elements/missing/missing_element_base'
|
26
|
+
require 'enolib/elements/missing/missing_value_element_base'
|
27
|
+
require 'enolib/elements/missing/missing_empty'
|
28
|
+
require 'enolib/elements/missing/missing_field'
|
29
|
+
require 'enolib/elements/missing/missing_fieldset'
|
30
|
+
require 'enolib/elements/missing/missing_fieldset_entry'
|
31
|
+
require 'enolib/elements/missing/missing_list'
|
32
|
+
require 'enolib/elements/missing/missing_section'
|
33
|
+
require 'enolib/elements/missing/missing_section_element'
|
34
|
+
|
35
|
+
require 'enolib/errors/selections'
|
36
|
+
require 'enolib/errors/parsing'
|
37
|
+
require 'enolib/errors/validation'
|
38
|
+
|
39
|
+
require 'enolib/reporters/reporter'
|
40
|
+
require 'enolib/reporters/html_reporter'
|
41
|
+
require 'enolib/reporters/terminal_reporter'
|
42
|
+
require 'enolib/reporters/text_reporter'
|
@@ -0,0 +1,16 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Enolib
|
4
|
+
HUMAN_INDEXING = 1
|
5
|
+
PRETTY_TYPES = {
|
6
|
+
document: 'document',
|
7
|
+
empty_element: 'empty_element',
|
8
|
+
field: 'field',
|
9
|
+
fieldset: 'fieldset',
|
10
|
+
fieldset_entry: 'fieldset_entry',
|
11
|
+
list: 'list',
|
12
|
+
list_item: 'list_item',
|
13
|
+
multiline_field_begin: 'field',
|
14
|
+
section: 'section'
|
15
|
+
}
|
16
|
+
end
|
@@ -0,0 +1,220 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Enolib
|
4
|
+
class Context
|
5
|
+
attr_accessor :document, :input, :line_count, :messages, :meta, :reporter, :source
|
6
|
+
|
7
|
+
def initialize(input, locale: Messages::En, reporter: TextReporter, source: nil)
|
8
|
+
@input = input
|
9
|
+
@messages = locale
|
10
|
+
@reporter = reporter
|
11
|
+
@source = source
|
12
|
+
|
13
|
+
@document = {
|
14
|
+
elements: [],
|
15
|
+
type: :document
|
16
|
+
}
|
17
|
+
|
18
|
+
@meta = []
|
19
|
+
|
20
|
+
Parser.new(self).run
|
21
|
+
end
|
22
|
+
|
23
|
+
def comment(element)
|
24
|
+
unless element.has_key?(:computed_comment)
|
25
|
+
element[:computed_comment] =
|
26
|
+
if !element.has_key?(:comments)
|
27
|
+
nil
|
28
|
+
elsif element[:comments].length == 1
|
29
|
+
element[:comments].first[:comment]
|
30
|
+
else
|
31
|
+
first_non_empty_line_index = nil
|
32
|
+
last_non_empty_line_index = nil
|
33
|
+
shared_indent = Float::INFINITY
|
34
|
+
|
35
|
+
element[:comments].each_with_index do |comment, index|
|
36
|
+
if comment.has_key?(:comment)
|
37
|
+
first_non_empty_line_index = index unless first_non_empty_line_index
|
38
|
+
last_non_empty_line_index = index
|
39
|
+
|
40
|
+
indent = comment[:ranges][:comment][RANGE_BEGIN] - comment[:ranges][:line][RANGE_BEGIN]
|
41
|
+
shared_indent = indent if indent < shared_indent
|
42
|
+
end
|
43
|
+
end
|
44
|
+
|
45
|
+
if first_non_empty_line_index
|
46
|
+
non_empty_lines = element[:comments][first_non_empty_line_index..last_non_empty_line_index]
|
47
|
+
|
48
|
+
non_empty_lines.map do |comment|
|
49
|
+
if !comment.has_key?(:comment)
|
50
|
+
''
|
51
|
+
elsif (comment[:ranges][:comment][RANGE_BEGIN] - comment[:ranges][:line][RANGE_BEGIN]) == shared_indent
|
52
|
+
comment[:comment]
|
53
|
+
else
|
54
|
+
(' ' * (comment[:ranges][:comment][RANGE_BEGIN] - comment[:ranges][:line][RANGE_BEGIN] - shared_indent)) + comment[:comment]
|
55
|
+
end
|
56
|
+
end.join("\n")
|
57
|
+
else
|
58
|
+
nil
|
59
|
+
end
|
60
|
+
end
|
61
|
+
end
|
62
|
+
|
63
|
+
element[:computed_comment]
|
64
|
+
end
|
65
|
+
|
66
|
+
def elements(section)
|
67
|
+
return elements(section[:mirror]) if section.has_key?(:mirror)
|
68
|
+
|
69
|
+
unless section.has_key?(:computed_elements)
|
70
|
+
section[:computed_elements] = section[:elements]
|
71
|
+
section[:computed_elements_map] = {}
|
72
|
+
|
73
|
+
section[:computed_elements].each do |element|
|
74
|
+
if section[:computed_elements_map].has_key?(element[:key])
|
75
|
+
section[:computed_elements_map][element[:key]].push(element)
|
76
|
+
else
|
77
|
+
section[:computed_elements_map][element[:key]] = [element]
|
78
|
+
end
|
79
|
+
end
|
80
|
+
|
81
|
+
if section.has_key?(:extend)
|
82
|
+
copied_elements = elements(section[:extend]).reject { |element| section[:computed_elements_map].has_key?(element[:key]) }
|
83
|
+
|
84
|
+
section[:computed_elements] = copied_elements + section[:computed_elements]
|
85
|
+
|
86
|
+
copied_elements.each do |element|
|
87
|
+
if section[:computed_elements_map].has_key?(element[:key])
|
88
|
+
section[:computed_elements_map][element[:key]].push(element)
|
89
|
+
else
|
90
|
+
section[:computed_elements_map][element[:key]] = [element]
|
91
|
+
end
|
92
|
+
end
|
93
|
+
end
|
94
|
+
end
|
95
|
+
|
96
|
+
section[:computed_elements]
|
97
|
+
end
|
98
|
+
|
99
|
+
def entries(fieldset)
|
100
|
+
return entries(fieldset[:mirror]) if fieldset.has_key?(:mirror)
|
101
|
+
|
102
|
+
unless fieldset.has_key?(:computed_entries)
|
103
|
+
fieldset[:computed_entries] = fieldset[:entries]
|
104
|
+
fieldset[:computed_entries_map] = {}
|
105
|
+
|
106
|
+
fieldset[:computed_entries].each do |entry|
|
107
|
+
if fieldset[:computed_entries_map].has_key?(entry[:key])
|
108
|
+
fieldset[:computed_entries_map][entry[:key]].push(entry)
|
109
|
+
else
|
110
|
+
fieldset[:computed_entries_map][entry[:key]] = [entry]
|
111
|
+
end
|
112
|
+
end
|
113
|
+
|
114
|
+
if fieldset.has_key?(:extend)
|
115
|
+
copied_entries = entries(fieldset[:extend]).reject { |entry| fieldset[:computed_entries_map].has_key?(entry[:key]) }
|
116
|
+
|
117
|
+
fieldset[:computed_entries] = copied_entries + fieldset[:computed_entries]
|
118
|
+
|
119
|
+
copied_entries.each do |entry|
|
120
|
+
if fieldset[:computed_entries_map].has_key?(entry[:key])
|
121
|
+
fieldset[:computed_entries_map][entry[:key]].push(entry)
|
122
|
+
else
|
123
|
+
fieldset[:computed_entries_map][entry[:key]] = [entry]
|
124
|
+
end
|
125
|
+
end
|
126
|
+
end
|
127
|
+
end
|
128
|
+
|
129
|
+
fieldset[:computed_entries]
|
130
|
+
end
|
131
|
+
|
132
|
+
def items(list)
|
133
|
+
if list.has_key?(:mirror)
|
134
|
+
items(list[:mirror])
|
135
|
+
elsif list.has_key?(:extend)
|
136
|
+
items(list[:extend]) + list[:items]
|
137
|
+
elsif list.has_key?(:items)
|
138
|
+
list[:items]
|
139
|
+
else
|
140
|
+
[]
|
141
|
+
end
|
142
|
+
end
|
143
|
+
|
144
|
+
def raw(element)
|
145
|
+
# TODO: In other implementations there is only one 'field' type
|
146
|
+
# here we get a) symbols, b) including :multiline_field_begin
|
147
|
+
result = { type: element[:type] }
|
148
|
+
|
149
|
+
# TODO: Revisit to think through the case of a present but empty comment
|
150
|
+
result[:comment] = comment(element) if element.has_key?(:comments)
|
151
|
+
|
152
|
+
case element[:type]
|
153
|
+
when :empty_element
|
154
|
+
result[:key] = element[:key]
|
155
|
+
when :field
|
156
|
+
result[:key] = element[:key]
|
157
|
+
result[:value] = value(element)
|
158
|
+
when :list_item
|
159
|
+
result[:value] = value(element)
|
160
|
+
when :fieldset_entry
|
161
|
+
result[:key] = element[:key]
|
162
|
+
result[:value] = value(element)
|
163
|
+
when :multiline_field_begin
|
164
|
+
result[:key] = element[:key]
|
165
|
+
result[:value] = value(element)
|
166
|
+
when :list
|
167
|
+
result[:key] = element[:key]
|
168
|
+
result[:items] = items(element).map { |item| raw(item) }
|
169
|
+
when :fieldset
|
170
|
+
result[:key] = element[:key]
|
171
|
+
result[:entries] = entries(element).map { |entry| raw(entry) }
|
172
|
+
when :section
|
173
|
+
result[:key] = element[:key]
|
174
|
+
result[:elements] = elements(element).map { |section_element| raw(section_element) }
|
175
|
+
when :document
|
176
|
+
result[:elements] = elements(element).map { |section_element| raw(section_element) }
|
177
|
+
end
|
178
|
+
|
179
|
+
result
|
180
|
+
end
|
181
|
+
|
182
|
+
def value(element)
|
183
|
+
unless element.has_key?(:computed_value)
|
184
|
+
return value(element[:mirror]) if element.has_key?(:mirror)
|
185
|
+
|
186
|
+
element[:computed_value] = nil
|
187
|
+
|
188
|
+
if element[:type] == :multiline_field_begin
|
189
|
+
if element.has_key?(:lines)
|
190
|
+
element[:computed_value] = @input[
|
191
|
+
element[:lines][0][:ranges][:line][0]...element[:lines][-1][:ranges][:line][1]
|
192
|
+
]
|
193
|
+
end
|
194
|
+
else
|
195
|
+
element[:computed_value] = element[:value] if element.has_key?(:value)
|
196
|
+
|
197
|
+
if element.has_key?(:continuations)
|
198
|
+
unapplied_spacing = nil
|
199
|
+
|
200
|
+
element[:continuations].each do |continuation|
|
201
|
+
if element[:computed_value] == nil
|
202
|
+
element[:computed_value] = continuation[:value] if continuation.has_key?(:value)
|
203
|
+
unapplied_spacing = nil
|
204
|
+
elsif !continuation.has_key?(:value)
|
205
|
+
unapplied_spacing ||= continuation.has_key?(:spaced)
|
206
|
+
elsif continuation.has_key?(:spaced) || unapplied_spacing
|
207
|
+
element[:computed_value] += ' ' + continuation[:value]
|
208
|
+
unapplied_spacing = nil
|
209
|
+
else
|
210
|
+
element[:computed_value] += continuation[:value]
|
211
|
+
end
|
212
|
+
end
|
213
|
+
end
|
214
|
+
end
|
215
|
+
end
|
216
|
+
|
217
|
+
return element[:computed_value]
|
218
|
+
end
|
219
|
+
end
|
220
|
+
end
|
@@ -0,0 +1,42 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Enolib
|
4
|
+
class Element < SectionElement
|
5
|
+
def to_fieldset_entry
|
6
|
+
unless instance_variable_defined?(:@fieldset_entry)
|
7
|
+
unless @instruction[:type] == :fieldset_entry
|
8
|
+
# TODO: Below and in all implementations - why nil for key as second parameter?
|
9
|
+
raise Errors::Validation.unexpected_element_type(@context, nil, @instruction, 'expected_fieldset_entry')
|
10
|
+
end
|
11
|
+
|
12
|
+
@fieldset_entry = FieldsetEntry.new(@context, @instruction)
|
13
|
+
end
|
14
|
+
|
15
|
+
@fieldset_entry
|
16
|
+
end
|
17
|
+
|
18
|
+
def to_list_item
|
19
|
+
unless instance_variable_defined?(:@list_item)
|
20
|
+
unless @instruction[:type] == :list_item
|
21
|
+
raise Errors::Validation.unexpected_element_type(@context, nil, @instruction, 'expected_list_item')
|
22
|
+
end
|
23
|
+
|
24
|
+
@list_item = FieldsetEntry.new(@context, @instruction)
|
25
|
+
end
|
26
|
+
|
27
|
+
@list_item
|
28
|
+
end
|
29
|
+
|
30
|
+
def yields_fieldset_entry?
|
31
|
+
@instruction[:type] == :fieldset_entry
|
32
|
+
end
|
33
|
+
|
34
|
+
def yields_list_item?
|
35
|
+
@instruction[:type] == :list_item
|
36
|
+
end
|
37
|
+
|
38
|
+
def to_s
|
39
|
+
"#<Enolib::Element key=#{@instruction[:key]}>"
|
40
|
+
end
|
41
|
+
end
|
42
|
+
end
|
@@ -0,0 +1,141 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Enolib
|
4
|
+
class ElementBase
|
5
|
+
def initialize(context, instruction, parent = nil)
|
6
|
+
@context = context
|
7
|
+
@instruction = instruction
|
8
|
+
@parent = parent
|
9
|
+
end
|
10
|
+
|
11
|
+
def comment_error(message = nil)
|
12
|
+
if block_given?
|
13
|
+
message = yield(@context.comment(@instruction))
|
14
|
+
elsif message.is_a?(Proc)
|
15
|
+
message = message.call(@context.comment(@instruction))
|
16
|
+
end
|
17
|
+
|
18
|
+
unless message
|
19
|
+
raise ArgumentError.new('A message or message function must be provided')
|
20
|
+
end
|
21
|
+
|
22
|
+
Errors::Validation.comment_error(@context, message, @instruction)
|
23
|
+
end
|
24
|
+
|
25
|
+
def error(message = nil)
|
26
|
+
if block_given?
|
27
|
+
message = yield(self) # Revisit self in this context - problematic
|
28
|
+
elsif message.is_a?(Proc)
|
29
|
+
message = message.call(self) # Revisit self in this context - problematic
|
30
|
+
end
|
31
|
+
|
32
|
+
unless message
|
33
|
+
raise ArgumentError.new('A message or message function must be provided')
|
34
|
+
end
|
35
|
+
|
36
|
+
Errors::Validation.element_error(@context, message, @instruction)
|
37
|
+
end
|
38
|
+
|
39
|
+
def key(loader = nil)
|
40
|
+
loader = Proc.new if block_given?
|
41
|
+
|
42
|
+
@touched = true
|
43
|
+
|
44
|
+
unless loader
|
45
|
+
raise ArgumentError.new('A loader function must be provided')
|
46
|
+
end
|
47
|
+
|
48
|
+
begin
|
49
|
+
loader.call(_key)
|
50
|
+
rescue => message
|
51
|
+
raise Errors::Validation.key_error(@context, message, @instruction)
|
52
|
+
end
|
53
|
+
end
|
54
|
+
|
55
|
+
def key_error(message = nil)
|
56
|
+
if block_given?
|
57
|
+
message = yield(_key)
|
58
|
+
elsif message.is_a?(Proc)
|
59
|
+
message = message.call(_key)
|
60
|
+
end
|
61
|
+
|
62
|
+
unless message
|
63
|
+
raise ArgumentError.new('A message or message function must be provided')
|
64
|
+
end
|
65
|
+
|
66
|
+
Errors::Validation.key_error(@context, message, @instruction)
|
67
|
+
end
|
68
|
+
|
69
|
+
def optional_comment(loader = nil)
|
70
|
+
loader = Proc.new if block_given?
|
71
|
+
|
72
|
+
unless loader
|
73
|
+
raise ArgumentError.new('A loader function must be provided')
|
74
|
+
end
|
75
|
+
|
76
|
+
_comment(loader, required: false)
|
77
|
+
end
|
78
|
+
|
79
|
+
def optional_string_comment()
|
80
|
+
_comment(required: false)
|
81
|
+
end
|
82
|
+
|
83
|
+
def raw
|
84
|
+
@context.raw(@instruction)
|
85
|
+
end
|
86
|
+
|
87
|
+
def required_comment(loader = nil)
|
88
|
+
loader = Proc.new if block_given?
|
89
|
+
|
90
|
+
unless loader
|
91
|
+
raise ArgumentError.new('A loader function must be provided')
|
92
|
+
end
|
93
|
+
|
94
|
+
_comment(loader, required: true)
|
95
|
+
end
|
96
|
+
|
97
|
+
def required_string_comment()
|
98
|
+
_comment(required: true)
|
99
|
+
end
|
100
|
+
|
101
|
+
def string_key
|
102
|
+
@touched = true
|
103
|
+
|
104
|
+
_key
|
105
|
+
end
|
106
|
+
|
107
|
+
def touch
|
108
|
+
@touched = true
|
109
|
+
end
|
110
|
+
|
111
|
+
private
|
112
|
+
|
113
|
+
def _comment(loader = nil, required:)
|
114
|
+
@touched = true
|
115
|
+
|
116
|
+
comment = @context.comment(@instruction)
|
117
|
+
|
118
|
+
if comment
|
119
|
+
return comment unless loader
|
120
|
+
|
121
|
+
begin
|
122
|
+
loader.call(comment)
|
123
|
+
rescue => message
|
124
|
+
raise Errors::Validation.comment_error(@context, message, @instruction)
|
125
|
+
end
|
126
|
+
else
|
127
|
+
return nil unless required
|
128
|
+
|
129
|
+
raise Errors::Validation.missing_comment(@context, @instruction)
|
130
|
+
end
|
131
|
+
end
|
132
|
+
|
133
|
+
def _key
|
134
|
+
if @instruction[:type] == :list_item
|
135
|
+
@instruction[:parent][:key]
|
136
|
+
else
|
137
|
+
@instruction[:key]
|
138
|
+
end
|
139
|
+
end
|
140
|
+
end
|
141
|
+
end
|