schemacop 2.4.7 → 3.0.0.rc0

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.
Files changed (172) hide show
  1. checksums.yaml +4 -4
  2. data/.gitignore +3 -0
  3. data/.rubocop.yml +25 -1
  4. data/.travis.yml +2 -1
  5. data/CHANGELOG.md +8 -0
  6. data/README.md +41 -708
  7. data/README_V2.md +775 -0
  8. data/README_V3.md +683 -0
  9. data/Rakefile +8 -12
  10. data/VERSION +1 -1
  11. data/lib/schemacop.rb +35 -37
  12. data/lib/schemacop/base_schema.rb +37 -0
  13. data/lib/schemacop/railtie.rb +10 -0
  14. data/lib/schemacop/schema.rb +1 -60
  15. data/lib/schemacop/schema2.rb +22 -0
  16. data/lib/schemacop/schema3.rb +21 -0
  17. data/lib/schemacop/scoped_env.rb +25 -13
  18. data/lib/schemacop/v2.rb +26 -0
  19. data/lib/schemacop/{caster.rb → v2/caster.rb} +16 -2
  20. data/lib/schemacop/{collector.rb → v2/collector.rb} +5 -2
  21. data/lib/schemacop/{dupper.rb → v2/dupper.rb} +1 -1
  22. data/lib/schemacop/{field_node.rb → v2/field_node.rb} +4 -3
  23. data/lib/schemacop/v2/node.rb +142 -0
  24. data/lib/schemacop/{node_resolver.rb → v2/node_resolver.rb} +1 -1
  25. data/lib/schemacop/{node_supporting_field.rb → v2/node_supporting_field.rb} +8 -10
  26. data/lib/schemacop/{node_supporting_type.rb → v2/node_supporting_type.rb} +6 -3
  27. data/lib/schemacop/{node_with_block.rb → v2/node_with_block.rb} +3 -2
  28. data/lib/schemacop/v2/root_node.rb +6 -0
  29. data/lib/schemacop/v2/validator/array_validator.rb +32 -0
  30. data/lib/schemacop/{validator → v2/validator}/boolean_validator.rb +1 -1
  31. data/lib/schemacop/v2/validator/float_validator.rb +7 -0
  32. data/lib/schemacop/v2/validator/hash_validator.rb +37 -0
  33. data/lib/schemacop/v2/validator/integer_validator.rb +7 -0
  34. data/lib/schemacop/{validator → v2/validator}/nil_validator.rb +1 -1
  35. data/lib/schemacop/v2/validator/number_validator.rb +21 -0
  36. data/lib/schemacop/v2/validator/object_validator.rb +29 -0
  37. data/lib/schemacop/v2/validator/string_validator.rb +39 -0
  38. data/lib/schemacop/{validator → v2/validator}/symbol_validator.rb +1 -1
  39. data/lib/schemacop/v3.rb +45 -0
  40. data/lib/schemacop/v3/all_of_node.rb +27 -0
  41. data/lib/schemacop/v3/any_of_node.rb +28 -0
  42. data/lib/schemacop/v3/array_node.rb +219 -0
  43. data/lib/schemacop/v3/boolean_node.rb +16 -0
  44. data/lib/schemacop/v3/combination_node.rb +45 -0
  45. data/lib/schemacop/v3/context.rb +17 -0
  46. data/lib/schemacop/v3/dsl_scope.rb +46 -0
  47. data/lib/schemacop/v3/global_context.rb +114 -0
  48. data/lib/schemacop/v3/hash_node.rb +217 -0
  49. data/lib/schemacop/v3/integer_node.rb +13 -0
  50. data/lib/schemacop/v3/is_not_node.rb +32 -0
  51. data/lib/schemacop/v3/node.rb +214 -0
  52. data/lib/schemacop/v3/node_registry.rb +49 -0
  53. data/lib/schemacop/v3/number_node.rb +18 -0
  54. data/lib/schemacop/v3/numeric_node.rb +76 -0
  55. data/lib/schemacop/v3/object_node.rb +40 -0
  56. data/lib/schemacop/v3/one_of_node.rb +28 -0
  57. data/lib/schemacop/v3/reference_node.rb +49 -0
  58. data/lib/schemacop/v3/result.rb +58 -0
  59. data/lib/schemacop/v3/string_node.rb +124 -0
  60. data/lib/schemacop/v3/symbol_node.rb +13 -0
  61. data/schemacop.gemspec +24 -27
  62. data/test/lib/test_helper.rb +152 -0
  63. data/test/schemas/nested/group.rb +6 -0
  64. data/test/schemas/user.rb +7 -0
  65. data/test/unit/schemacop/v2/casting_test.rb +120 -0
  66. data/test/unit/schemacop/v2/collector_test.rb +47 -0
  67. data/test/unit/schemacop/v2/custom_check_test.rb +95 -0
  68. data/test/unit/schemacop/v2/custom_if_test.rb +97 -0
  69. data/test/unit/schemacop/v2/defaults_test.rb +95 -0
  70. data/test/unit/schemacop/v2/empty_test.rb +16 -0
  71. data/test/unit/schemacop/v2/nil_dis_allow_test.rb +43 -0
  72. data/test/unit/schemacop/v2/node_resolver_test.rb +28 -0
  73. data/test/unit/schemacop/v2/short_forms_test.rb +351 -0
  74. data/test/unit/schemacop/v2/types_test.rb +88 -0
  75. data/test/unit/schemacop/v2/validator_array_test.rb +99 -0
  76. data/test/unit/schemacop/v2/validator_boolean_test.rb +17 -0
  77. data/test/unit/schemacop/v2/validator_float_test.rb +59 -0
  78. data/test/unit/schemacop/v2/validator_hash_test.rb +95 -0
  79. data/test/unit/schemacop/v2/validator_integer_test.rb +48 -0
  80. data/test/unit/schemacop/v2/validator_nil_test.rb +15 -0
  81. data/test/unit/schemacop/v2/validator_number_test.rb +62 -0
  82. data/test/unit/schemacop/v2/validator_object_test.rb +141 -0
  83. data/test/unit/schemacop/v2/validator_string_test.rb +78 -0
  84. data/test/unit/schemacop/v2/validator_symbol_test.rb +18 -0
  85. data/test/unit/schemacop/v3/all_of_node_test.rb +199 -0
  86. data/test/unit/schemacop/v3/any_of_node_test.rb +218 -0
  87. data/test/unit/schemacop/v3/array_node_test.rb +805 -0
  88. data/test/unit/schemacop/v3/boolean_node_test.rb +126 -0
  89. data/test/unit/schemacop/v3/global_context_test.rb +164 -0
  90. data/test/unit/schemacop/v3/hash_node_test.rb +775 -0
  91. data/test/unit/schemacop/v3/integer_node_test.rb +323 -0
  92. data/test/unit/schemacop/v3/is_not_node_test.rb +173 -0
  93. data/test/unit/schemacop/v3/node_test.rb +148 -0
  94. data/test/unit/schemacop/v3/number_node_test.rb +292 -0
  95. data/test/unit/schemacop/v3/object_node_test.rb +170 -0
  96. data/test/unit/schemacop/v3/one_of_node_test.rb +187 -0
  97. data/test/unit/schemacop/v3/reference_node_test.rb +351 -0
  98. data/test/unit/schemacop/v3/string_node_test.rb +334 -0
  99. data/test/unit/schemacop/v3/symbol_node_test.rb +75 -0
  100. metadata +152 -145
  101. data/doc/Schemacop.html +0 -146
  102. data/doc/Schemacop/ArrayValidator.html +0 -329
  103. data/doc/Schemacop/BooleanValidator.html +0 -145
  104. data/doc/Schemacop/Caster.html +0 -379
  105. data/doc/Schemacop/Collector.html +0 -787
  106. data/doc/Schemacop/Dupper.html +0 -214
  107. data/doc/Schemacop/Exceptions.html +0 -115
  108. data/doc/Schemacop/Exceptions/InvalidSchemaError.html +0 -124
  109. data/doc/Schemacop/Exceptions/ValidationError.html +0 -124
  110. data/doc/Schemacop/FieldNode.html +0 -421
  111. data/doc/Schemacop/FloatValidator.html +0 -158
  112. data/doc/Schemacop/HashValidator.html +0 -293
  113. data/doc/Schemacop/IntegerValidator.html +0 -158
  114. data/doc/Schemacop/NilValidator.html +0 -145
  115. data/doc/Schemacop/Node.html +0 -1438
  116. data/doc/Schemacop/NodeResolver.html +0 -258
  117. data/doc/Schemacop/NodeSupportingField.html +0 -590
  118. data/doc/Schemacop/NodeSupportingType.html +0 -612
  119. data/doc/Schemacop/NodeWithBlock.html +0 -289
  120. data/doc/Schemacop/NumberValidator.html +0 -232
  121. data/doc/Schemacop/ObjectValidator.html +0 -298
  122. data/doc/Schemacop/RootNode.html +0 -171
  123. data/doc/Schemacop/Schema.html +0 -699
  124. data/doc/Schemacop/StringValidator.html +0 -295
  125. data/doc/Schemacop/SymbolValidator.html +0 -145
  126. data/doc/ScopedEnv.html +0 -351
  127. data/doc/_index.html +0 -379
  128. data/doc/class_list.html +0 -51
  129. data/doc/css/common.css +0 -1
  130. data/doc/css/full_list.css +0 -58
  131. data/doc/css/style.css +0 -496
  132. data/doc/file.README.html +0 -833
  133. data/doc/file_list.html +0 -56
  134. data/doc/frames.html +0 -17
  135. data/doc/index.html +0 -833
  136. data/doc/inheritance.graphml +0 -524
  137. data/doc/inheritance.pdf +0 -825
  138. data/doc/js/app.js +0 -303
  139. data/doc/js/full_list.js +0 -216
  140. data/doc/js/jquery.js +0 -4
  141. data/doc/method_list.html +0 -587
  142. data/doc/top-level-namespace.html +0 -112
  143. data/lib/schemacop/node.rb +0 -139
  144. data/lib/schemacop/root_node.rb +0 -4
  145. data/lib/schemacop/validator/array_validator.rb +0 -30
  146. data/lib/schemacop/validator/float_validator.rb +0 -5
  147. data/lib/schemacop/validator/hash_validator.rb +0 -35
  148. data/lib/schemacop/validator/integer_validator.rb +0 -5
  149. data/lib/schemacop/validator/number_validator.rb +0 -19
  150. data/lib/schemacop/validator/object_validator.rb +0 -27
  151. data/lib/schemacop/validator/string_validator.rb +0 -37
  152. data/test/casting_test.rb +0 -118
  153. data/test/collector_test.rb +0 -45
  154. data/test/custom_check_test.rb +0 -93
  155. data/test/custom_if_test.rb +0 -95
  156. data/test/defaults_test.rb +0 -93
  157. data/test/empty_test.rb +0 -14
  158. data/test/nil_dis_allow_test.rb +0 -41
  159. data/test/node_resolver_test.rb +0 -26
  160. data/test/short_forms_test.rb +0 -349
  161. data/test/test_helper.rb +0 -13
  162. data/test/types_test.rb +0 -84
  163. data/test/validator_array_test.rb +0 -97
  164. data/test/validator_boolean_test.rb +0 -15
  165. data/test/validator_float_test.rb +0 -57
  166. data/test/validator_hash_test.rb +0 -93
  167. data/test/validator_integer_test.rb +0 -46
  168. data/test/validator_nil_test.rb +0 -13
  169. data/test/validator_number_test.rb +0 -60
  170. data/test/validator_object_test.rb +0 -139
  171. data/test/validator_string_test.rb +0 -76
  172. data/test/validator_symbol_test.rb +0 -16
@@ -1,4 +1,4 @@
1
- module Schemacop
1
+ module Schemacop::V2
2
2
  class SymbolValidator < Node
3
3
  register symbols: :symbol, klasses: Symbol
4
4
  end
@@ -0,0 +1,45 @@
1
+ module Schemacop
2
+ module V3
3
+ def self.register(*args)
4
+ NodeRegistry.register(*args)
5
+ end
6
+ end
7
+ end
8
+
9
+ # Require V3 files
10
+ require 'schemacop/v3/node_registry'
11
+ require 'schemacop/v3/dsl_scope'
12
+ require 'schemacop/v3/context'
13
+ require 'schemacop/v3/global_context'
14
+ require 'schemacop/v3/result'
15
+ require 'schemacop/v3/node'
16
+ require 'schemacop/v3/combination_node'
17
+ require 'schemacop/v3/numeric_node'
18
+ require 'schemacop/v3/all_of_node'
19
+ require 'schemacop/v3/any_of_node'
20
+ require 'schemacop/v3/array_node'
21
+ require 'schemacop/v3/boolean_node'
22
+ require 'schemacop/v3/hash_node'
23
+ require 'schemacop/v3/integer_node'
24
+ require 'schemacop/v3/is_not_node'
25
+ require 'schemacop/v3/number_node'
26
+ require 'schemacop/v3/object_node'
27
+ require 'schemacop/v3/one_of_node'
28
+ require 'schemacop/v3/reference_node'
29
+ require 'schemacop/v3/string_node'
30
+ require 'schemacop/v3/symbol_node'
31
+
32
+ # Register built-in nodes
33
+ Schemacop::V3.register :all_of, :all_of, Schemacop::V3::AllOfNode
34
+ Schemacop::V3.register :any_of, :any_of, Schemacop::V3::AnyOfNode
35
+ Schemacop::V3.register :array, :ary, Schemacop::V3::ArrayNode
36
+ Schemacop::V3.register :boolean, :boo, Schemacop::V3::BooleanNode
37
+ Schemacop::V3.register :integer, :int, Schemacop::V3::IntegerNode
38
+ Schemacop::V3.register :is_not, :is_not, Schemacop::V3::IsNotNode
39
+ Schemacop::V3.register :number, :num, Schemacop::V3::NumberNode
40
+ Schemacop::V3.register :hash, :hsh, Schemacop::V3::HashNode
41
+ Schemacop::V3.register :one_of, :one_of, Schemacop::V3::OneOfNode
42
+ Schemacop::V3.register :reference, :ref, Schemacop::V3::ReferenceNode
43
+ Schemacop::V3.register :object, :obj, Schemacop::V3::ObjectNode
44
+ Schemacop::V3.register :string, :str, Schemacop::V3::StringNode
45
+ Schemacop::V3.register :symbol, :sym, Schemacop::V3::SymbolNode
@@ -0,0 +1,27 @@
1
+ module Schemacop
2
+ module V3
3
+ class AllOfNode < CombinationNode
4
+ def type
5
+ :allOf
6
+ end
7
+
8
+ def _validate(data, result:)
9
+ super_data = super
10
+ return if super_data.nil?
11
+
12
+ if matches(super_data).size != @items.size
13
+ result.error 'Does not match all allOf conditions.'
14
+ end
15
+ end
16
+
17
+ def cast(value)
18
+ items = matches(value)
19
+ return value unless items
20
+
21
+ casted_value = value.dup
22
+ items.each { |i| casted_value = i.cast(casted_value) }
23
+ return casted_value
24
+ end
25
+ end
26
+ end
27
+ end
@@ -0,0 +1,28 @@
1
+ module Schemacop
2
+ module V3
3
+ class AnyOfNode < CombinationNode
4
+ def type
5
+ :anyOf
6
+ end
7
+
8
+ def _validate(data, result:)
9
+ super_data = super
10
+ return if super_data.nil?
11
+
12
+ match = match(super_data)
13
+
14
+ if match
15
+ match._validate(super_data, result: result)
16
+ else
17
+ result.error 'Does not match any anyOf condition.'
18
+ end
19
+ end
20
+
21
+ def validate_self
22
+ if @items.empty?
23
+ fail 'Node "any_of" makes only sense with at least 1 item.'
24
+ end
25
+ end
26
+ end
27
+ end
28
+ end
@@ -0,0 +1,219 @@
1
+ module Schemacop
2
+ module V3
3
+ class ArrayNode < Node
4
+ ATTRIBUTES = %i[
5
+ min_items
6
+ max_items
7
+ unique_items
8
+ ].freeze
9
+
10
+ supports_children
11
+
12
+ def self.allowed_options
13
+ super + ATTRIBUTES + %i[additional_items]
14
+ end
15
+
16
+ def self.dsl_methods
17
+ super + NodeRegistry.dsl_methods(false) + %i[dsl_add dsl_list dsl_cont]
18
+ end
19
+
20
+ attr_reader :items
21
+ attr_accessor :list_item
22
+ attr_accessor :cont_item
23
+
24
+ def dsl_add(type, **options, &block)
25
+ if @options[:additional_items].is_a?(Node)
26
+ fail Exceptions::InvalidSchemaError, 'You can only use "add" once to specify additional items.'
27
+ end
28
+
29
+ @options[:additional_items] = create(type, **options, &block)
30
+ end
31
+
32
+ def dsl_list(type, **options, &block)
33
+ if list_item.is_a?(Node)
34
+ fail Exceptions::InvalidSchemaError, 'You can only use "list" once.'
35
+ end
36
+
37
+ @list_item = create(type, **options, &block)
38
+ end
39
+
40
+ def dsl_cont(type, **options, &block)
41
+ if cont_item.is_a?(Node)
42
+ fail Exceptions::InvalidSchemaError, 'You can only use "cont" once.'
43
+ end
44
+
45
+ @cont_item = create(type, **options, &block)
46
+ end
47
+
48
+ def add_child(node)
49
+ @items << node
50
+ end
51
+
52
+ def as_json
53
+ json = { type: :array }
54
+
55
+ if cont_item
56
+ json[:contains] = cont_item.as_json
57
+ end
58
+
59
+ if list?
60
+ json[:items] = @list_item.as_json
61
+ elsif @items.any?
62
+ json[:items] = @items.map(&:as_json)
63
+ if options[:additional_items] == true
64
+ json[:additionalItems] = true
65
+ elsif options[:additional_items].is_a?(Node)
66
+ json[:additionalItems] = options[:additional_items].as_json
67
+ else
68
+ json[:additionalItems] = false
69
+ end
70
+ end
71
+
72
+ return process_json(ATTRIBUTES, json)
73
+ end
74
+
75
+ def allowed_types
76
+ { Array => :array }
77
+ end
78
+
79
+ def _validate(data, result:)
80
+ super_data = super
81
+ return if super_data.nil?
82
+
83
+ # Validate length #
84
+ length = super_data.size
85
+
86
+ if options[:min_items] && length < options[:min_items]
87
+ result.error "Array has #{length} items but needs at least #{options[:min_items]}."
88
+ end
89
+
90
+ if options[:max_items] && length > options[:max_items]
91
+ result.error "Array has #{length} items but needs at most #{options[:max_items]}."
92
+ end
93
+
94
+ if list?
95
+ # Validate list
96
+ super_data.each_with_index do |value, index|
97
+ result.in_path :"[#{index}]" do
98
+ list_item._validate(value, result: result)
99
+ end
100
+ end
101
+ elsif items.any?
102
+ # Validate tuple
103
+ if length == items.size || (options[:additional_items] != false && length >= items.size)
104
+ items.each_with_index do |child_node, index|
105
+ value = super_data[index]
106
+
107
+ result.in_path :"[#{index}]" do
108
+ child_node._validate(value, result: result)
109
+ end
110
+ end
111
+
112
+ # Validate additional items #
113
+ if options[:additional_items].is_a?(Node)
114
+ (items.size..(length - 1)).each do |index|
115
+ additional_item = super_data[index]
116
+ result.in_path :"[#{index}]" do
117
+ options[:additional_items]._validate(additional_item, result: result)
118
+ end
119
+ end
120
+ end
121
+ else
122
+ result.error "Array has #{length} items but must have exactly #{items.size}."
123
+ end
124
+ end
125
+
126
+ if cont_item.present? && super_data.none? { |obj| item_matches?(cont_item, obj) }
127
+ result.error "At least one entry must match schema #{cont_item.as_json.inspect}."
128
+ end
129
+
130
+ # Validate uniqueness #
131
+ if options[:unique_items] && super_data.size != super_data.uniq.size
132
+ result.error 'Array has duplicate items.'
133
+ end
134
+ end
135
+
136
+ def children
137
+ (@items + [@cont_item]).compact
138
+ end
139
+
140
+ def cast(value)
141
+ return default unless value
142
+
143
+ result = []
144
+
145
+ value.each_with_index do |value_item, index|
146
+ if cont_item.present? && item_matches?(cont_item, value_item)
147
+ result << cont_item.cast(value_item)
148
+ elsif list?
149
+ result << list_item.cast(value_item)
150
+ elsif items.any?
151
+ if options[:additional_items] != false && index >= items.size
152
+ if options[:additional_items].is_a?(Node)
153
+ result << options[:additional_items].cast(value_item)
154
+ else
155
+ result << value_item
156
+ end
157
+ else
158
+ item = item_for_data(value_item)
159
+ result << item.cast(value_item)
160
+ end
161
+ else
162
+ result << value_item
163
+ end
164
+ end
165
+
166
+ return result
167
+ end
168
+
169
+ protected
170
+
171
+ def list?
172
+ list_item.present?
173
+ end
174
+
175
+ def item_for_data(data, force: true)
176
+ item = children.find { |c| item_matches?(c, data) }
177
+ return item if item
178
+ return nil unless force
179
+
180
+ fail "Could not find specification for item #{data.inspect}."
181
+ end
182
+
183
+ def init
184
+ @items = []
185
+ @cont_item = nil
186
+
187
+ if options[:additional_items].nil?
188
+ options[:additional_items] = false
189
+ end
190
+ end
191
+
192
+ def validate_self
193
+ if list? && items.any?
194
+ fail 'Can\'t use "list" and normal items.'
195
+ end
196
+
197
+ if list? && @options[:additional_items].is_a?(Node)
198
+ fail 'Can\'t use "list" and additional items.'
199
+ end
200
+
201
+ unless options[:min_items].nil? || options[:min_items].is_a?(Integer)
202
+ fail 'Option "min_items" must be an "integer"'
203
+ end
204
+
205
+ unless options[:max_items].nil? || options[:max_items].is_a?(Integer)
206
+ fail 'Option "max_items" must be an "integer"'
207
+ end
208
+
209
+ unless options[:unique_items].nil? || options[:unique_items].is_a?(TrueClass) || options[:unique_items].is_a?(FalseClass)
210
+ fail 'Option "unique_items" must be a "boolean".'
211
+ end
212
+
213
+ if options[:min_items] && options[:max_items] && options[:min_items] > options[:max_items]
214
+ fail 'Option "min_items" can\'t be greater than "max_items".'
215
+ end
216
+ end
217
+ end
218
+ end
219
+ end
@@ -0,0 +1,16 @@
1
+ module Schemacop
2
+ module V3
3
+ class BooleanNode < Node
4
+ def as_json
5
+ process_json([], type: :boolean)
6
+ end
7
+
8
+ def allowed_types
9
+ {
10
+ TrueClass => :boolean,
11
+ FalseClass => :boolean
12
+ }
13
+ end
14
+ end
15
+ end
16
+ end
@@ -0,0 +1,45 @@
1
+ module Schemacop
2
+ module V3
3
+ # @abstract
4
+ class CombinationNode < Node
5
+ def self.dsl_methods
6
+ super + NodeRegistry.dsl_methods(false)
7
+ end
8
+
9
+ supports_children
10
+
11
+ def init
12
+ @items = []
13
+ end
14
+
15
+ def as_json
16
+ process_json([], type => @items.map(&:as_json))
17
+ end
18
+
19
+ def cast(value)
20
+ item = match(value)
21
+ return value unless item
22
+
23
+ return item.cast(value)
24
+ end
25
+
26
+ def add_child(node)
27
+ @items << node
28
+ end
29
+
30
+ protected
31
+
32
+ def type
33
+ fail NotImplementedError
34
+ end
35
+
36
+ def match(data)
37
+ matches(data).first
38
+ end
39
+
40
+ def matches(data)
41
+ @items.filter { |i| item_matches?(i, data) }
42
+ end
43
+ end
44
+ end
45
+ end
@@ -0,0 +1,17 @@
1
+ module Schemacop
2
+ module V3
3
+ class Context
4
+ attr_accessor :schemas
5
+
6
+ def initialize
7
+ @schemas = {}.with_indifferent_access.freeze
8
+ end
9
+
10
+ def schema(name, type = :hash, **options, &block)
11
+ @schemas = @schemas.merge(
12
+ name => Node.create(type, **options, &block)
13
+ ).freeze
14
+ end
15
+ end
16
+ end
17
+ end
@@ -0,0 +1,46 @@
1
+ module Schemacop
2
+ module V3
3
+ class DslScope
4
+ EXP_NAME = /^dsl_([a-z_]+)([?!])?$/.freeze
5
+
6
+ def initialize(node)
7
+ @node = node
8
+ @with_name = @node.class.supports_children_options[:name]
9
+ end
10
+
11
+ def method_missing(name, *args, **options, &block)
12
+ match = EXP_NAME.match(name)
13
+ return super unless match
14
+
15
+ base_name, req_optional = match.captures
16
+
17
+ if req_optional == '!'
18
+ options[:required] = true
19
+ elsif req_optional == '?'
20
+ options[:required] = false
21
+ end
22
+
23
+ options[:parent] = @node
24
+
25
+ if (klass = NodeRegistry.by_short_name(base_name))
26
+ if @with_name
27
+ options[:name] = args.shift
28
+ end
29
+ node = klass.create(*args, **options, &block)
30
+ @node.add_child node
31
+ return node
32
+ else
33
+ return super
34
+ end
35
+ end
36
+
37
+ def respond_to_missing?(name, *args)
38
+ match = EXP_NAME.match(name)
39
+ return super unless match
40
+
41
+ base_name, _req_optional = match.captures
42
+ return NodeRegistry.by_short_name(base_name).present? || super
43
+ end
44
+ end
45
+ end
46
+ end