dsl_compose 2.15.3 → 2.15.5
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/CHANGELOG.md +15 -0
- data/lib/dsl_compose/composer.rb +3 -1
- data/lib/dsl_compose/dsl/arguments/argument.rb +1 -1
- data/lib/dsl_compose/dsl/dsl_method.rb +1 -1
- data/lib/dsl_compose/dsl.rb +0 -2
- data/lib/dsl_compose/interpreter/execution/arguments.rb +31 -29
- data/lib/dsl_compose/interpreter/execution/method_calls/method_call.rb +6 -4
- data/lib/dsl_compose/interpreter/execution/method_calls.rb +5 -3
- data/lib/dsl_compose/interpreter/execution.rb +13 -9
- data/lib/dsl_compose/interpreter/interpreter_error.rb +18 -0
- data/lib/dsl_compose/interpreter.rb +19 -12
- data/lib/dsl_compose/parser/for_children_of_parser/for_dsl_parser/for_method_parser.rb +10 -0
- data/lib/dsl_compose/parser/for_children_of_parser/for_dsl_parser.rb +10 -2
- data/lib/dsl_compose/parser/for_children_of_parser.rb +14 -8
- data/lib/dsl_compose/reader.rb +3 -3
- data/lib/dsl_compose/version.rb +1 -1
- data/lib/dsl_compose.rb +1 -0
- data/sig/dsl_compose/interpreter/execution/arguments.rbs +8 -7
- data/sig/dsl_compose/interpreter/execution/method_calls/method_call.rbs +5 -4
- data/sig/dsl_compose/interpreter/execution/method_calls.rbs +1 -1
- data/sig/dsl_compose/interpreter/execution.rbs +8 -3
- data/sig/dsl_compose/interpreter/interpreter_error.rbs +11 -0
- data/sig/dsl_compose/interpreter.rbs +1 -4
- data/sig/dsl_compose/parser/for_children_of_parser/for_dsl_parser.rbs +1 -1
- data/sig/dsl_compose/parser/for_children_of_parser.rbs +5 -3
- metadata +4 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: e0750c158e424591df6b08e2ac3786b689116b5850152b6f6fe51fc39092895a
|
4
|
+
data.tar.gz: 7e818d88dd0dde898b80faa4eb24f18eaa6ca8bd64a4c83fc974621ccee71e3e
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: a374a44e85a232cc6681c98034a1196cc7ccc077689a3f9bb75354aa54e0e02be8758a527de68944af95c81f04157b07df4bcaccab20877cfee48f2164eddad2
|
7
|
+
data.tar.gz: 73fd1c43e1677b42c2389a4919e0ced10feda5e35ca564d1d025376fbb357282f5d8bf7411980d3d9d744c46b6d50cbc2fa370314ad0c4de58093eec569fc53d
|
data/CHANGELOG.md
CHANGED
@@ -1,5 +1,20 @@
|
|
1
1
|
# Changelog
|
2
2
|
|
3
|
+
## [2.15.5](https://github.com/craigulliott/dsl_compose/compare/v2.15.4...v2.15.5) (2023-10-06)
|
4
|
+
|
5
|
+
|
6
|
+
### Bug Fixes
|
7
|
+
|
8
|
+
* added a first_use_only option to the parser so that only the most recent execution of a DSL will be used ([717293d](https://github.com/craigulliott/dsl_compose/commit/717293d906a183d8a13ffa1c4ae47e901de428a0))
|
9
|
+
* specs and fixes for `first_use_only` option on parser ([a7dc087](https://github.com/craigulliott/dsl_compose/commit/a7dc0878ffef9a104c25d4f43d5796f939cb7567))
|
10
|
+
|
11
|
+
## [2.15.4](https://github.com/craigulliott/dsl_compose/compare/v2.15.3...v2.15.4) (2023-10-04)
|
12
|
+
|
13
|
+
|
14
|
+
### Bug Fixes
|
15
|
+
|
16
|
+
* passing context along with where DSL's were defined, and then including this context in error messages ([5238a3d](https://github.com/craigulliott/dsl_compose/commit/5238a3daa8c1a32e77783d56c7c429b6335995bd))
|
17
|
+
|
3
18
|
## [2.15.3](https://github.com/craigulliott/dsl_compose/compare/v2.15.2...v2.15.3) (2023-10-03)
|
4
19
|
|
5
20
|
|
data/lib/dsl_compose/composer.rb
CHANGED
@@ -48,9 +48,11 @@ module DSLCompose
|
|
48
48
|
|
49
49
|
# add a singleton method with the name of this new DSL onto our class, this is how our new DSL will be accessed
|
50
50
|
define_singleton_method name do |*args, &block|
|
51
|
+
called_from = caller(1..1).first
|
52
|
+
|
51
53
|
# when it is called, we process this new dynamic DSL with the interpreter
|
52
54
|
# `self` here is the class in which the dsl is being used, not the class in which the DSL was defined
|
53
|
-
interpreter.execute_dsl self, dsl, *args, &block
|
55
|
+
interpreter.execute_dsl self, dsl, called_from, *args, &block
|
54
56
|
end
|
55
57
|
|
56
58
|
end
|
@@ -127,7 +127,7 @@ module DSLCompose
|
|
127
127
|
Interpreter.new(self).instance_eval(&block)
|
128
128
|
end
|
129
129
|
rescue => e
|
130
|
-
raise e, "Error defining argument #{name}
|
130
|
+
raise e, "Error while defining argument #{name}\n#{e.message}", e.backtrace
|
131
131
|
end
|
132
132
|
|
133
133
|
# Set the description for this Argument to the provided value.
|
@@ -60,7 +60,7 @@ module DSLCompose
|
|
60
60
|
Interpreter.new(self).instance_eval(&block)
|
61
61
|
end
|
62
62
|
rescue => e
|
63
|
-
raise e, "Error defining method #{name}
|
63
|
+
raise e, "Error while defining method #{name}\n#{e.message}", e.backtrace
|
64
64
|
end
|
65
65
|
|
66
66
|
# Set the description for this DSLMethod to the provided value.
|
data/lib/dsl_compose/dsl.rb
CHANGED
@@ -83,8 +83,6 @@ module DSLCompose
|
|
83
83
|
else
|
84
84
|
raise NoBlockProvidedError, "No block was provided for this DSL"
|
85
85
|
end
|
86
|
-
rescue => e
|
87
|
-
raise e, "Error defining DSL #{@name} on #{@klass}: #{e.message}", e.backtrace
|
88
86
|
end
|
89
87
|
|
90
88
|
# Set the description for this DSL to the provided value.
|
@@ -4,25 +4,27 @@ module DSLCompose
|
|
4
4
|
class Interpreter
|
5
5
|
class Execution
|
6
6
|
class Arguments
|
7
|
-
class MissingRequiredArgumentsError <
|
7
|
+
class MissingRequiredArgumentsError < InterpreterError
|
8
8
|
end
|
9
9
|
|
10
|
-
class TooManyArgumentsError <
|
10
|
+
class TooManyArgumentsError < InterpreterError
|
11
11
|
end
|
12
12
|
|
13
|
-
class OptionalArgumentsShouldBeHashError <
|
13
|
+
class OptionalArgumentsShouldBeHashError < InterpreterError
|
14
14
|
end
|
15
15
|
|
16
|
-
class InvalidArgumentTypeError <
|
16
|
+
class InvalidArgumentTypeError < InterpreterError
|
17
17
|
end
|
18
18
|
|
19
|
-
class ArrayNotValidError <
|
19
|
+
class ArrayNotValidError < InterpreterError
|
20
20
|
end
|
21
21
|
|
22
22
|
attr_reader :arguments
|
23
|
+
attr_reader :called_from
|
23
24
|
|
24
|
-
def initialize arguments, *args
|
25
|
+
def initialize arguments, called_from, *args
|
25
26
|
@arguments = {}
|
27
|
+
@called_from = called_from
|
26
28
|
|
27
29
|
required_argument_count = arguments.required_arguments.count
|
28
30
|
required_non_kwarg_argument_count = arguments.required_arguments.count { |a| !a.kwarg }
|
@@ -40,7 +42,7 @@ module DSLCompose
|
|
40
42
|
required_kwargs = arguments.required_arguments.filter { |a| a.kwarg }
|
41
43
|
|
42
44
|
if required_kwargs.any? && !all_kwargs.is_a?(Hash)
|
43
|
-
raise MissingRequiredArgumentsError
|
45
|
+
raise MissingRequiredArgumentsError.new("This has required keyword arguments, but no keyword arguments were provided", called_from)
|
44
46
|
end
|
45
47
|
|
46
48
|
required_kwargs.each do |required_kwarg|
|
@@ -51,7 +53,7 @@ module DSLCompose
|
|
51
53
|
# left with only the optional args
|
52
54
|
all_kwargs.delete required_kwarg.name
|
53
55
|
else
|
54
|
-
raise MissingRequiredArgumentsError
|
56
|
+
raise MissingRequiredArgumentsError.new("The required kwarg `#{required_kwarg.name}` was not provided", called_from)
|
55
57
|
end
|
56
58
|
end
|
57
59
|
|
@@ -61,12 +63,12 @@ module DSLCompose
|
|
61
63
|
|
62
64
|
# assert that a value is provided for every required argument
|
63
65
|
unless required_argument_count == required_args.count
|
64
|
-
raise MissingRequiredArgumentsError
|
66
|
+
raise MissingRequiredArgumentsError.new("This requires #{required_non_kwarg_argument_count} arguments, but only #{required_args.count} were provided", called_from)
|
65
67
|
end
|
66
68
|
|
67
69
|
# assert that too many arguments have not been provided
|
68
70
|
if args.count > required_argument_count + (has_optional_arguments ? 1 : 0)
|
69
|
-
raise TooManyArgumentsError
|
71
|
+
raise TooManyArgumentsError.new("Too many arguments provided", called_from)
|
70
72
|
end
|
71
73
|
|
72
74
|
# Assume all optonal arguments are their defaults (except booleans, which default to false).
|
@@ -88,7 +90,7 @@ module DSLCompose
|
|
88
90
|
# asset that, if provided, then the optional argument (always the last one) is a Hash
|
89
91
|
if has_optional_arguments && !optional_arg.nil?
|
90
92
|
unless optional_arg.is_a? Hash
|
91
|
-
raise OptionalArgumentsShouldBeHashError
|
93
|
+
raise OptionalArgumentsShouldBeHashError.new("If provided, then the optional arguments must be last, and be represented as a Hash", called_from)
|
92
94
|
end
|
93
95
|
|
94
96
|
# assert the each provided optional argument is valid
|
@@ -110,7 +112,7 @@ module DSLCompose
|
|
110
112
|
end
|
111
113
|
|
112
114
|
if optional_arg_value.is_a?(Array) && !optional_argument.array
|
113
|
-
raise ArrayNotValidError
|
115
|
+
raise ArrayNotValidError.new("An array was provided to an argument which does not accept an array of values", called_from)
|
114
116
|
end
|
115
117
|
|
116
118
|
# to simplify the code, we always process the reset of the validations as an array, even
|
@@ -121,38 +123,38 @@ module DSLCompose
|
|
121
123
|
case optional_argument.type
|
122
124
|
when :integer
|
123
125
|
unless value.is_a? Integer
|
124
|
-
raise InvalidArgumentTypeError
|
126
|
+
raise InvalidArgumentTypeError.new("`#{value}` is not an Integer", called_from)
|
125
127
|
end
|
126
128
|
optional_argument.validate_integer! value
|
127
129
|
|
128
130
|
when :float
|
129
131
|
# float allows either floats or integers
|
130
132
|
unless value.is_a?(Float) || value.is_a?(Integer)
|
131
|
-
raise InvalidArgumentTypeError
|
133
|
+
raise InvalidArgumentTypeError.new("`#{value}` is not an Float or Integer", called_from)
|
132
134
|
end
|
133
135
|
optional_argument.validate_float! value
|
134
136
|
|
135
137
|
when :symbol
|
136
138
|
unless value.is_a? Symbol
|
137
|
-
raise InvalidArgumentTypeError
|
139
|
+
raise InvalidArgumentTypeError.new("`#{value}` is not a Symbol", called_from)
|
138
140
|
end
|
139
141
|
optional_argument.validate_symbol! value
|
140
142
|
|
141
143
|
when :string
|
142
144
|
unless value.is_a? String
|
143
|
-
raise InvalidArgumentTypeError
|
145
|
+
raise InvalidArgumentTypeError.new("`#{value}` is not a String", called_from)
|
144
146
|
end
|
145
147
|
optional_argument.validate_string! value
|
146
148
|
|
147
149
|
when :boolean
|
148
150
|
unless value.is_a?(TrueClass) || value.is_a?(FalseClass)
|
149
|
-
raise InvalidArgumentTypeError
|
151
|
+
raise InvalidArgumentTypeError.new("`#{value}` is not a boolean", called_from)
|
150
152
|
end
|
151
153
|
optional_argument.validate_boolean! value
|
152
154
|
|
153
155
|
when :class
|
154
156
|
unless value.is_a?(ClassCoerce)
|
155
|
-
raise InvalidArgumentTypeError
|
157
|
+
raise InvalidArgumentTypeError.new("`#{value}` is not a class coerce (String)", called_from)
|
156
158
|
end
|
157
159
|
optional_argument.validate_class! value
|
158
160
|
|
@@ -160,7 +162,7 @@ module DSLCompose
|
|
160
162
|
optional_argument.validate_object! value
|
161
163
|
|
162
164
|
else
|
163
|
-
raise InvalidArgumentTypeError
|
165
|
+
raise InvalidArgumentTypeError.new("The argument value `#{value}` is not a supported type", called_from)
|
164
166
|
end
|
165
167
|
end
|
166
168
|
|
@@ -174,7 +176,7 @@ module DSLCompose
|
|
174
176
|
optional_arg_value
|
175
177
|
end
|
176
178
|
rescue => e
|
177
|
-
raise e, "Error processing optional argument #{optional_argument_name}
|
179
|
+
raise e, "Error processing optional argument #{optional_argument_name}\n#{e.message}", e.backtrace
|
178
180
|
end
|
179
181
|
|
180
182
|
end
|
@@ -200,7 +202,7 @@ module DSLCompose
|
|
200
202
|
end
|
201
203
|
|
202
204
|
if required_arg_value.is_a?(Array) && !required_argument.array
|
203
|
-
raise ArrayNotValidError
|
205
|
+
raise ArrayNotValidError.new("An array was provided to an argument which does not accept an array of values", called_from)
|
204
206
|
end
|
205
207
|
|
206
208
|
# to simplify the code, we always process the reset of the validations as an array, even
|
@@ -211,38 +213,38 @@ module DSLCompose
|
|
211
213
|
case required_argument.type
|
212
214
|
when :integer
|
213
215
|
unless value.is_a? Integer
|
214
|
-
raise InvalidArgumentTypeError
|
216
|
+
raise InvalidArgumentTypeError.new("`#{value}` is not an Integer", called_from)
|
215
217
|
end
|
216
218
|
required_argument.validate_integer! value
|
217
219
|
|
218
220
|
when :float
|
219
221
|
# float allows either floats or integers
|
220
222
|
unless value.is_a?(Float) || value.is_a?(Integer)
|
221
|
-
raise InvalidArgumentTypeError
|
223
|
+
raise InvalidArgumentTypeError.new("`#{value}` is not an Float or Integer", called_from)
|
222
224
|
end
|
223
225
|
required_argument.validate_float! value
|
224
226
|
|
225
227
|
when :symbol
|
226
228
|
unless value.is_a? Symbol
|
227
|
-
raise InvalidArgumentTypeError
|
229
|
+
raise InvalidArgumentTypeError.new("`#{value}` is not a Symbol", called_from)
|
228
230
|
end
|
229
231
|
required_argument.validate_symbol! value
|
230
232
|
|
231
233
|
when :string
|
232
234
|
unless value.is_a? String
|
233
|
-
raise InvalidArgumentTypeError
|
235
|
+
raise InvalidArgumentTypeError.new("`#{value}` is not a String", called_from)
|
234
236
|
end
|
235
237
|
required_argument.validate_string! value
|
236
238
|
|
237
239
|
when :boolean
|
238
240
|
unless value.is_a?(TrueClass) || value.is_a?(FalseClass)
|
239
|
-
raise InvalidArgumentTypeError
|
241
|
+
raise InvalidArgumentTypeError.new("`#{value}` is not a boolean", called_from)
|
240
242
|
end
|
241
243
|
required_argument.validate_boolean! value
|
242
244
|
|
243
245
|
when :class
|
244
246
|
unless value.is_a?(ClassCoerce)
|
245
|
-
raise InvalidArgumentTypeError
|
247
|
+
raise InvalidArgumentTypeError.new("`#{value}` is not a class coerce (String)", called_from)
|
246
248
|
end
|
247
249
|
required_argument.validate_class! value
|
248
250
|
|
@@ -250,7 +252,7 @@ module DSLCompose
|
|
250
252
|
required_argument.validate_object! value
|
251
253
|
|
252
254
|
else
|
253
|
-
raise InvalidArgumentTypeError
|
255
|
+
raise InvalidArgumentTypeError.new("The argument `#{value}` is not a supported type", called_from)
|
254
256
|
end
|
255
257
|
end
|
256
258
|
|
@@ -264,7 +266,7 @@ module DSLCompose
|
|
264
266
|
required_arg_value
|
265
267
|
end
|
266
268
|
rescue => e
|
267
|
-
raise e, "Error processing required argument #{argument_name}
|
269
|
+
raise e, "Error processing required argument #{argument_name}\n#{e.message}", e.backtrace
|
268
270
|
end
|
269
271
|
end
|
270
272
|
|
@@ -5,15 +5,17 @@ module DSLCompose
|
|
5
5
|
class Execution
|
6
6
|
class MethodCalls
|
7
7
|
class MethodCall
|
8
|
-
class InvalidDescriptionError <
|
8
|
+
class InvalidDescriptionError < InterpreterError
|
9
9
|
end
|
10
10
|
|
11
11
|
attr_reader :dsl_method
|
12
|
+
attr_reader :called_from
|
12
13
|
attr_reader :arguments
|
13
14
|
|
14
|
-
def initialize dsl_method, *args, &block
|
15
|
+
def initialize dsl_method, called_from, *args, &block
|
15
16
|
@dsl_method = dsl_method
|
16
|
-
@
|
17
|
+
@called_from = called_from
|
18
|
+
@arguments = Arguments.new(dsl_method.arguments, called_from, *args)
|
17
19
|
end
|
18
20
|
|
19
21
|
def method_name
|
@@ -30,7 +32,7 @@ module DSLCompose
|
|
30
32
|
# generate documentation
|
31
33
|
def add_parser_usage_note note
|
32
34
|
unless note.is_a?(String) && note.strip.length > 0
|
33
|
-
raise InvalidDescriptionError
|
35
|
+
raise InvalidDescriptionError.new("The parser usage description `#{note}` is invalid, it must be of type string and have length greater than 0", @called_from)
|
34
36
|
end
|
35
37
|
|
36
38
|
@parser_usage_notes ||= []
|
@@ -14,16 +14,18 @@ module DSLCompose
|
|
14
14
|
@method_calls.filter { |mc| mc.method_name == method_name }.any?
|
15
15
|
end
|
16
16
|
|
17
|
-
def add_method_call(dsl_method, ...)
|
17
|
+
def add_method_call(dsl_method, called_from, ...)
|
18
18
|
# make sure we always have a variable which can be used in the exception message
|
19
|
+
# set to nil first, so that if we get an exception while setting them
|
20
|
+
# it wont break the error message generation
|
19
21
|
dsl_method_name = nil
|
20
22
|
dsl_method_name = dsl_method.name
|
21
23
|
|
22
|
-
method_call = MethodCall.new(dsl_method, ...)
|
24
|
+
method_call = MethodCall.new(dsl_method, called_from, ...)
|
23
25
|
@method_calls << method_call
|
24
26
|
method_call
|
25
27
|
rescue => e
|
26
|
-
raise e, "Error while executing method #{dsl_method_name}
|
28
|
+
raise e, "Error while executing method #{dsl_method_name}\n#{e.message}", e.backtrace
|
27
29
|
end
|
28
30
|
|
29
31
|
def method_calls_by_name method_name
|
@@ -3,26 +3,28 @@
|
|
3
3
|
module DSLCompose
|
4
4
|
class Interpreter
|
5
5
|
class Execution
|
6
|
-
class MethodIsUniqueError <
|
6
|
+
class MethodIsUniqueError < InterpreterError
|
7
7
|
end
|
8
8
|
|
9
|
-
class RequiredMethodNotCalledError <
|
9
|
+
class RequiredMethodNotCalledError < InterpreterError
|
10
10
|
end
|
11
11
|
|
12
|
-
class InvalidDescriptionError <
|
12
|
+
class InvalidDescriptionError < InterpreterError
|
13
13
|
end
|
14
14
|
|
15
15
|
attr_reader :dsl
|
16
|
+
attr_reader :called_from
|
16
17
|
attr_reader :klass
|
17
18
|
attr_reader :method_calls
|
18
19
|
attr_reader :arguments
|
19
20
|
|
20
21
|
# execute/process a dynamically defined DSL
|
21
|
-
def initialize klass, dsl, *args, &block
|
22
|
+
def initialize klass, dsl, called_from, *args, &block
|
22
23
|
@klass = klass
|
23
24
|
@dsl = dsl
|
25
|
+
@called_from = called_from
|
24
26
|
@method_calls = MethodCalls.new
|
25
|
-
@arguments = Arguments.new(dsl.arguments, *args)
|
27
|
+
@arguments = Arguments.new(dsl.arguments, called_from, *args)
|
26
28
|
|
27
29
|
# dynamically process the DSL by calling the provided block
|
28
30
|
# all methods executions will be caught and processed by the method_missing method below
|
@@ -34,7 +36,7 @@ module DSLCompose
|
|
34
36
|
dsl.required_dsl_methods.each do |dsl_method|
|
35
37
|
unless @method_calls.method_called? dsl_method.name
|
36
38
|
dsl_method_name = dsl_method&.name
|
37
|
-
raise RequiredMethodNotCalledError
|
39
|
+
raise RequiredMethodNotCalledError.new("The method #{dsl_method_name} is required, but was not called within this DSL", called_from)
|
38
40
|
end
|
39
41
|
end
|
40
42
|
end
|
@@ -43,7 +45,7 @@ module DSLCompose
|
|
43
45
|
# generate documentation
|
44
46
|
def add_parser_usage_note note
|
45
47
|
unless note.is_a?(String) && note.strip.length > 0
|
46
|
-
raise InvalidDescriptionError
|
48
|
+
raise InvalidDescriptionError.new("The parser usage description `#{note}` is invalid, it must be of type string and have length greater than 0", called_from)
|
47
49
|
end
|
48
50
|
|
49
51
|
@parser_usage_notes ||= []
|
@@ -60,15 +62,17 @@ module DSLCompose
|
|
60
62
|
|
61
63
|
# catch and process any method calls within the DSL block
|
62
64
|
def method_missing(method_name, ...)
|
65
|
+
called_from = caller(1..1).first
|
66
|
+
|
63
67
|
# if the method does not exist, then this will raise a MethodDoesNotExistError
|
64
68
|
dsl_method = @dsl.dsl_method method_name
|
65
69
|
|
66
70
|
# if the method is unique, then it can only be called once per DSL
|
67
71
|
if dsl_method.unique? && @method_calls.method_called?(method_name)
|
68
|
-
raise MethodIsUniqueError
|
72
|
+
raise MethodIsUniqueError.new("This method `#{method_name}` is unique and can only be called once within this DSL", called_from)
|
69
73
|
end
|
70
74
|
|
71
|
-
@method_calls.add_method_call(dsl_method, ...)
|
75
|
+
@method_calls.add_method_call(dsl_method, called_from, ...)
|
72
76
|
end
|
73
77
|
|
74
78
|
def respond_to_missing?(method_name, *args)
|
@@ -0,0 +1,18 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module DSLCompose
|
4
|
+
class Interpreter
|
5
|
+
class InterpreterError < StandardError
|
6
|
+
attr_reader :original_context
|
7
|
+
|
8
|
+
def initialize(message, original_context)
|
9
|
+
super(message)
|
10
|
+
@original_context = original_context
|
11
|
+
end
|
12
|
+
|
13
|
+
def to_s
|
14
|
+
"#{super}\ndsl source: #{@original_context}"
|
15
|
+
end
|
16
|
+
end
|
17
|
+
end
|
18
|
+
end
|
@@ -4,10 +4,7 @@ module DSLCompose
|
|
4
4
|
# The class is reponsible for parsing and executing a dynamic DSL (dynamic DSLs are
|
5
5
|
# created using the DSLCompose::DSL class).
|
6
6
|
class Interpreter
|
7
|
-
class
|
8
|
-
end
|
9
|
-
|
10
|
-
class InvalidDescriptionError < StandardError
|
7
|
+
class InvalidDescriptionError < InterpreterError
|
11
8
|
end
|
12
9
|
|
13
10
|
# A dynamic DSL can be used multiple times on the same class, each time the DSL is used
|
@@ -23,7 +20,7 @@ module DSLCompose
|
|
23
20
|
# generate documentation
|
24
21
|
def add_parser_usage_note child_class, note
|
25
22
|
unless note.is_a?(String) && note.strip.length > 0
|
26
|
-
raise InvalidDescriptionError
|
23
|
+
raise InvalidDescriptionError.new("The parser usage description `#{note}` is invalid, it must be of type string and have length greater than 0", called_from)
|
27
24
|
end
|
28
25
|
@parser_usage_notes ||= {}
|
29
26
|
@parser_usage_notes[child_class] ||= []
|
@@ -40,20 +37,23 @@ module DSLCompose
|
|
40
37
|
# Execute/process a dynamically defined DSL on a class.
|
41
38
|
# `klass` is the class in which the DSL is being used, not
|
42
39
|
# the class in which the DSL was defined.
|
43
|
-
def execute_dsl(klass, dsl, ...)
|
40
|
+
def execute_dsl(klass, dsl, called_from, ...)
|
44
41
|
# make sure we have these variables for the exception message below
|
42
|
+
# set to nil first, so that if we get an exception while setting them
|
43
|
+
# it wont break the error message generation
|
45
44
|
class_name = nil
|
46
|
-
class_name = klass.name
|
47
45
|
dsl_name = nil
|
46
|
+
class_name = klass.name
|
48
47
|
dsl_name = dsl.name
|
49
48
|
|
50
|
-
execution = Execution.new(klass, dsl, ...)
|
49
|
+
execution = Execution.new(klass, dsl, called_from, ...)
|
51
50
|
@executions << execution
|
52
51
|
execution
|
53
52
|
rescue => e
|
54
|
-
raise e, "Error
|
53
|
+
raise e, "Error while defining DSL #{dsl_name} for class #{class_name}:\n#{e.message}", e.backtrace
|
55
54
|
end
|
56
55
|
|
56
|
+
#
|
57
57
|
# Returns an array of all executions for a given class.
|
58
58
|
def class_executions klass
|
59
59
|
@executions.filter { |e| e.klass == klass }
|
@@ -66,8 +66,15 @@ module DSLCompose
|
|
66
66
|
|
67
67
|
# Returns an array of all executions for a given name and class. This includes
|
68
68
|
# any ancestors of the provided class
|
69
|
-
def class_dsl_executions klass, dsl_name, on_current_class, on_ancestor_class
|
70
|
-
@executions.filter { |e| e.dsl.name == dsl_name && ((on_current_class && e.klass == klass) || (on_ancestor_class && klass < e.klass)) }
|
69
|
+
def class_dsl_executions klass, dsl_name, on_current_class, on_ancestor_class, first_use_only
|
70
|
+
filtered_executions = @executions.filter { |e| e.dsl.name == dsl_name && ((on_current_class && e.klass == klass) || (on_ancestor_class && klass < e.klass)) }
|
71
|
+
# Because the classes were evaluated in order, we can just return the
|
72
|
+
# last execution
|
73
|
+
if first_use_only && filtered_executions.length > 0
|
74
|
+
[filtered_executions.last]
|
75
|
+
else
|
76
|
+
filtered_executions
|
77
|
+
end
|
71
78
|
end
|
72
79
|
|
73
80
|
# returns the most recent, closest single execution of a dsl with the
|
@@ -83,7 +90,7 @@ module DSLCompose
|
|
83
90
|
# note that this method does not need to do any special sorting, the required
|
84
91
|
# order for getting the most recent execution is already guaranteed because
|
85
92
|
# parent classes in ruby always have to be evaluated before their descendants
|
86
|
-
class_dsl_executions(klass, dsl_name, true, true).last
|
93
|
+
class_dsl_executions(klass, dsl_name, true, true, false).last
|
87
94
|
end
|
88
95
|
|
89
96
|
# removes all executions from the interpreter, and any parser_usage_notes
|
@@ -28,6 +28,8 @@ module DSLCompose
|
|
28
28
|
@dsl_execution = dsl_execution
|
29
29
|
@method_names = method_names
|
30
30
|
|
31
|
+
dsl_name = dsl_execution.dsl.name
|
32
|
+
|
31
33
|
# assert that a block was provided
|
32
34
|
unless block
|
33
35
|
raise NoBlockProvided
|
@@ -100,6 +102,14 @@ module DSLCompose
|
|
100
102
|
|
101
103
|
# yeild the block in the context of this class
|
102
104
|
instance_exec(**args, &block)
|
105
|
+
rescue => e
|
106
|
+
# if this is an InterpreterError, then it already has the called_from metadata
|
107
|
+
# just continue raising the original error
|
108
|
+
if e.is_a? Interpreter::InterpreterError
|
109
|
+
raise
|
110
|
+
end
|
111
|
+
# otherwise, decorate the error with where the DSL was defined
|
112
|
+
raise e, "#{e.message}\nparsing class: #{child_class.name}\ndsl name: #{dsl_name}\ndsl method name: #{method_name}\ndsl source: #{method_call.called_from}", e.backtrace
|
103
113
|
end
|
104
114
|
end
|
105
115
|
end
|
@@ -20,7 +20,7 @@ module DSLCompose
|
|
20
20
|
# of the provided name is used by the child class.
|
21
21
|
#
|
22
22
|
# base_class and child_class are set from the ForChildrenOfParser
|
23
|
-
def initialize base_class, child_class, dsl_names, on_current_class, on_ancestor_class, &block
|
23
|
+
def initialize base_class, child_class, dsl_names, on_current_class, on_ancestor_class, first_use_only, &block
|
24
24
|
@base_class = base_class
|
25
25
|
@child_class = child_class
|
26
26
|
@dsl_names = dsl_names
|
@@ -62,7 +62,7 @@ module DSLCompose
|
|
62
62
|
dsl_names.each do |dsl_name|
|
63
63
|
# a dsl can be execued multiple times on a class, so we find all of the executions
|
64
64
|
# here and then yield the block once for each execution
|
65
|
-
base_class.dsls.class_dsl_executions(child_class, dsl_name, on_current_class, on_ancestor_class).each do |dsl_execution|
|
65
|
+
base_class.dsls.class_dsl_executions(child_class, dsl_name, on_current_class, on_ancestor_class, first_use_only).each do |dsl_execution|
|
66
66
|
# we only provide the requested arguments to the block, this allows
|
67
67
|
# us to use keyword arguments to force a naming convention on these arguments
|
68
68
|
# and to validate their use
|
@@ -108,6 +108,14 @@ module DSLCompose
|
|
108
108
|
@dsl_execution = dsl_execution
|
109
109
|
# yield the block in the context of this class
|
110
110
|
instance_exec(**args, &block)
|
111
|
+
rescue => e
|
112
|
+
# if this is an InterpreterError, then it already has the called_from metadata
|
113
|
+
# just continue raising the original error
|
114
|
+
if e.is_a? Interpreter::InterpreterError
|
115
|
+
raise
|
116
|
+
end
|
117
|
+
# otherwise, decorate the error with where the DSL was defined
|
118
|
+
raise e, "#{e.message}\nparsing class: #{child_class.name}\ndsl name: #{dsl_name}\ndsl source: #{dsl_execution.called_from}", e.backtrace
|
111
119
|
end
|
112
120
|
end
|
113
121
|
end
|
@@ -90,36 +90,42 @@ module DSLCompose
|
|
90
90
|
# end
|
91
91
|
#
|
92
92
|
# If `on_current_class` is true, then the block will be yielded to for each DSL
|
93
|
-
# which was used directly on the current class. If `
|
94
|
-
# then the block will not be yielded to for any DSL which was used directly on.
|
93
|
+
# which was used directly on the current class. If `on_current_class` is false,
|
94
|
+
# then the block will not be yielded to for any DSL which was used directly on it.
|
95
95
|
# If `on_ancestor_class` is true, then the block will be yielded to for each DSL
|
96
96
|
# which was used on any class in the current classes ancestry. If `on_ancestor_class`
|
97
97
|
# is false, then the block will not be yielded to for any DSL which was used on
|
98
98
|
# any class in the current classes ancestry.
|
99
|
-
|
99
|
+
#
|
100
|
+
# If `first_use_only` is true, then this block will only yeild once for each subject class
|
101
|
+
# which directly uses or inherits use of the provided DSL. The DSL execution which occurs
|
102
|
+
# first in the class will be selected, and if the class does not use the DSL then each of
|
103
|
+
# the classes ancestors will be tested until an execution is found (only the current class
|
104
|
+
# will be tested if skip_inherited_dsls has been set to true).
|
105
|
+
def for_dsl dsl_names, on_current_class: true, on_ancestor_class: false, first_use_only: false, &block
|
100
106
|
child_class = @child_class
|
101
107
|
|
102
108
|
unless child_class
|
103
109
|
raise NoChildClassError, "No child_class was found, please call this method from within a `for_children_of` block"
|
104
110
|
end
|
105
111
|
|
106
|
-
ForDSLParser.new(@base_class, child_class, dsl_names, on_current_class, on_ancestor_class, &block)
|
112
|
+
ForDSLParser.new(@base_class, child_class, dsl_names, on_current_class, on_ancestor_class, first_use_only, &block)
|
107
113
|
end
|
108
114
|
|
109
115
|
# this is a wrapper for the `for_dsl` method, but it provides a value of true
|
110
116
|
# for the `on_ancestor_class` argument and a value of false for the `on_current_class`
|
111
117
|
# argument. This will cause the parser to only yeild for dsls which were used on
|
112
118
|
# a class which is in the current classes ancestry, but not on the current class
|
113
|
-
def for_inherited_dsl dsl_names, &block
|
114
|
-
for_dsl dsl_names, on_current_class: false, on_ancestor_class: true, &block
|
119
|
+
def for_inherited_dsl dsl_names, first_use_only: false, &block
|
120
|
+
for_dsl dsl_names, on_current_class: false, on_ancestor_class: true, first_use_only: first_use_only, &block
|
115
121
|
end
|
116
122
|
|
117
123
|
# this is a wrapper for the `for_dsl` method, but it provides a value of true
|
118
124
|
# for the `on_ancestor_class` argument and a value of true for the `on_current_class`
|
119
125
|
# argument. This will cause the parser to yeild for dsls which were used on either
|
120
126
|
# the current class or any class in its ancestry
|
121
|
-
def for_dsl_or_inherited_dsl dsl_names, &block
|
122
|
-
for_dsl dsl_names, on_current_class: true, on_ancestor_class: true, &block
|
127
|
+
def for_dsl_or_inherited_dsl dsl_names, first_use_only: false, &block
|
128
|
+
for_dsl dsl_names, on_current_class: true, on_ancestor_class: true, first_use_only: first_use_only, &block
|
123
129
|
end
|
124
130
|
|
125
131
|
# takes a description of what this parser does and stores it against the DSL definition
|
data/lib/dsl_compose/reader.rb
CHANGED
@@ -89,7 +89,7 @@ module DSLCompose
|
|
89
89
|
# Returns an array of ExecutionReaders to represent each time the DSL was used
|
90
90
|
# on the provided class.
|
91
91
|
def executions
|
92
|
-
@dsl_defining_class.dsls.class_dsl_executions(@klass, @dsl_name, true, false).map do |execution|
|
92
|
+
@dsl_defining_class.dsls.class_dsl_executions(@klass, @dsl_name, true, false, false).map do |execution|
|
93
93
|
ExecutionReader.new execution
|
94
94
|
end
|
95
95
|
end
|
@@ -100,7 +100,7 @@ module DSLCompose
|
|
100
100
|
# earliest ancestor first and if the DSL was used more than once on a class then
|
101
101
|
# the order they were used.
|
102
102
|
def ancestor_executions
|
103
|
-
@dsl_defining_class.dsls.class_dsl_executions(@klass, @dsl_name, false, true).map do |execution|
|
103
|
+
@dsl_defining_class.dsls.class_dsl_executions(@klass, @dsl_name, false, true, false).map do |execution|
|
104
104
|
ExecutionReader.new execution
|
105
105
|
end
|
106
106
|
end
|
@@ -110,7 +110,7 @@ module DSLCompose
|
|
110
110
|
# be returned in the order they were executed, which is the earliest ancestor first
|
111
111
|
# and if the DSL was used more than once on a class then the order they were used.
|
112
112
|
def all_executions
|
113
|
-
@dsl_defining_class.dsls.class_dsl_executions(@klass, @dsl_name, true, true).map do |execution|
|
113
|
+
@dsl_defining_class.dsls.class_dsl_executions(@klass, @dsl_name, true, true, false).map do |execution|
|
114
114
|
ExecutionReader.new execution
|
115
115
|
end
|
116
116
|
end
|
data/lib/dsl_compose/version.rb
CHANGED
data/lib/dsl_compose.rb
CHANGED
@@ -34,6 +34,7 @@ require "dsl_compose/reader/execution_reader/arguments_reader"
|
|
34
34
|
|
35
35
|
require "dsl_compose/reader_base"
|
36
36
|
|
37
|
+
require "dsl_compose/interpreter/interpreter_error"
|
37
38
|
require "dsl_compose/interpreter/execution/method_calls/method_call"
|
38
39
|
require "dsl_compose/interpreter/execution/method_calls"
|
39
40
|
require "dsl_compose/interpreter/execution/arguments"
|
@@ -4,23 +4,24 @@ module DSLCompose
|
|
4
4
|
class Execution
|
5
5
|
class Arguments
|
6
6
|
attr_reader arguments: Hash[untyped, untyped]
|
7
|
-
|
7
|
+
attr_reader called_from: String
|
8
|
+
|
9
|
+
def initialize: (untyped arguments, String called_from, *untyped args) -> void
|
8
10
|
def to_h: -> Hash[untyped, untyped]
|
9
11
|
|
10
|
-
class MissingRequiredArgumentsError <
|
11
|
-
def initialize: (untyped required_count, untyped provided_count) -> void
|
12
|
+
class MissingRequiredArgumentsError < InterpreterError
|
12
13
|
end
|
13
14
|
|
14
|
-
class TooManyArgumentsError <
|
15
|
+
class TooManyArgumentsError < InterpreterError
|
15
16
|
end
|
16
17
|
|
17
|
-
class OptionalArgumentsShouldBeHashError <
|
18
|
+
class OptionalArgumentsShouldBeHashError < InterpreterError
|
18
19
|
end
|
19
20
|
|
20
|
-
class InvalidArgumentTypeError <
|
21
|
+
class InvalidArgumentTypeError < InterpreterError
|
21
22
|
end
|
22
23
|
|
23
|
-
class ArrayNotValidError <
|
24
|
+
class ArrayNotValidError < InterpreterError
|
24
25
|
end
|
25
26
|
end
|
26
27
|
end
|
@@ -4,21 +4,22 @@ module DSLCompose
|
|
4
4
|
class Execution
|
5
5
|
class MethodCalls
|
6
6
|
class MethodCall
|
7
|
-
class InvalidDescriptionError < StandardError
|
8
|
-
end
|
9
|
-
|
10
7
|
@method_call: MethodCall
|
11
8
|
@parser_usage_notes: Array[String]
|
12
9
|
|
13
10
|
attr_reader dsl_method: DSL::DSLMethod
|
11
|
+
attr_reader called_from: String
|
14
12
|
attr_reader arguments: Arguments
|
15
13
|
|
16
|
-
def initialize: (DSL::DSLMethod dsl_method, *untyped args) -> void
|
14
|
+
def initialize: (DSL::DSLMethod dsl_method, String called_from, *untyped args) -> void
|
17
15
|
def method_name: -> Symbol
|
18
16
|
def add_parser_usage_note: (String note) -> void
|
19
17
|
def parser_usage_notes: () -> Array[String]
|
20
18
|
|
21
19
|
def to_h: -> Hash[untyped, untyped]
|
20
|
+
|
21
|
+
class InvalidDescriptionError < InterpreterError
|
22
|
+
end
|
22
23
|
end
|
23
24
|
end
|
24
25
|
end
|
@@ -9,7 +9,7 @@ module DSLCompose
|
|
9
9
|
def method_calls: () -> Array[MethodCall]
|
10
10
|
def method_called?: (Symbol method_name) -> bool
|
11
11
|
def method_calls_by_name: (Symbol method_name) -> Array[MethodCall]
|
12
|
-
def add_method_call: (DSL::DSLMethod dsl_method, *untyped args) -> MethodCall
|
12
|
+
def add_method_call: (DSL::DSLMethod dsl_method, String called_from, *untyped args) -> MethodCall
|
13
13
|
end
|
14
14
|
end
|
15
15
|
end
|
@@ -5,11 +5,12 @@ module DSLCompose
|
|
5
5
|
@parser_usage_notes: Array[String]
|
6
6
|
|
7
7
|
attr_reader dsl: DSL
|
8
|
+
attr_reader called_from: String
|
8
9
|
attr_reader klass: Object
|
9
10
|
attr_reader method_calls: MethodCalls
|
10
11
|
attr_reader arguments: Arguments
|
11
12
|
|
12
|
-
def initialize: (Object klass, DSL dsl, *untyped args) -> void
|
13
|
+
def initialize: (Object klass, DSL dsl, String called_from, *untyped args) -> void
|
13
14
|
def instance_eval: () -> void
|
14
15
|
def add_parser_usage_note: (String note) -> void
|
15
16
|
def parser_usage_notes: () -> Array[String]
|
@@ -18,11 +19,15 @@ module DSLCompose
|
|
18
19
|
def method_missing: (Symbol method_name, *untyped args) -> untyped
|
19
20
|
def respond_to_missing?: (Symbol method_name, *untyped args) -> untyped
|
20
21
|
|
21
|
-
class MethodIsUniqueError <
|
22
|
+
class MethodIsUniqueError < InterpreterError
|
22
23
|
end
|
23
24
|
|
24
|
-
class RequiredMethodNotCalledError <
|
25
|
+
class RequiredMethodNotCalledError < InterpreterError
|
25
26
|
end
|
27
|
+
|
28
|
+
class InvalidDescriptionError < InterpreterError
|
29
|
+
end
|
30
|
+
|
26
31
|
end
|
27
32
|
end
|
28
33
|
end
|
@@ -12,10 +12,7 @@ module DSLCompose
|
|
12
12
|
def add_parser_usage_note: (singleton(Object) klass, String note) -> void
|
13
13
|
def parser_usage_notes: (singleton(Object) klass) -> Array[String]
|
14
14
|
|
15
|
-
class InvalidDescriptionError <
|
16
|
-
end
|
17
|
-
|
18
|
-
class DSLExecutionNotFoundError < StandardError
|
15
|
+
class InvalidDescriptionError < InterpreterError
|
19
16
|
end
|
20
17
|
end
|
21
18
|
end
|
@@ -8,7 +8,7 @@ module DSLCompose
|
|
8
8
|
@dsl_names: Array[Symbol]
|
9
9
|
@dsl_execution: Interpreter::Execution
|
10
10
|
|
11
|
-
def initialize: (singleton(Object) base_class, singleton(Object) child_class, Array[Symbol] dsl_names, bool on_current_class, bool on_ancestor_class) -> void
|
11
|
+
def initialize: (singleton(Object) base_class, singleton(Object) child_class, Array[Symbol] dsl_names, bool on_current_class, bool on_ancestor_class, bool first_use_only) -> void
|
12
12
|
|
13
13
|
# overriding this method because steep doesn't
|
14
14
|
# correctly infer that a block is being passed to this method
|
@@ -14,10 +14,12 @@ module DSLCompose
|
|
14
14
|
) -> void
|
15
15
|
|
16
16
|
private
|
17
|
-
def for_dsl: (Array[Symbol] dsl_names, ?on_current_class: bool, ?on_ancestor_class: bool) -> untyped
|
17
|
+
def for_dsl: (Array[Symbol] dsl_names, ?on_current_class: bool, ?on_ancestor_class: bool, ?first_use_only: bool) -> untyped
|
18
|
+
def for_inherited_dsl: (Array[Symbol] dsl_names, ?first_use_only: bool) -> untyped
|
19
|
+
def for_dsl_or_inherited_dsl: (Array[Symbol] dsl_names, ?first_use_only: bool) -> untyped
|
18
20
|
|
19
21
|
class AllBlockParametersMustBeKeywordParametersError < StandardError
|
20
|
-
|
22
|
+
end
|
21
23
|
|
22
24
|
class ClassDoesNotUseDSLComposeError < StandardError
|
23
25
|
end
|
@@ -26,7 +28,7 @@ module DSLCompose
|
|
26
28
|
end
|
27
29
|
|
28
30
|
class NoChildClassError < StandardError
|
29
|
-
|
31
|
+
end
|
30
32
|
end
|
31
33
|
end
|
32
34
|
end
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: dsl_compose
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 2.15.
|
4
|
+
version: 2.15.5
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Craig Ulliott
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2023-10-
|
11
|
+
date: 2023-10-06 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: class_spec_helper
|
@@ -67,6 +67,7 @@ files:
|
|
67
67
|
- lib/dsl_compose/interpreter/execution/arguments.rb
|
68
68
|
- lib/dsl_compose/interpreter/execution/method_calls.rb
|
69
69
|
- lib/dsl_compose/interpreter/execution/method_calls/method_call.rb
|
70
|
+
- lib/dsl_compose/interpreter/interpreter_error.rb
|
70
71
|
- lib/dsl_compose/parser.rb
|
71
72
|
- lib/dsl_compose/parser/block_arguments.rb
|
72
73
|
- lib/dsl_compose/parser/for_children_of_parser.rb
|
@@ -109,6 +110,7 @@ files:
|
|
109
110
|
- sig/dsl_compose/interpreter/execution/arguments.rbs
|
110
111
|
- sig/dsl_compose/interpreter/execution/method_calls.rbs
|
111
112
|
- sig/dsl_compose/interpreter/execution/method_calls/method_call.rbs
|
113
|
+
- sig/dsl_compose/interpreter/interpreter_error.rbs
|
112
114
|
- sig/dsl_compose/parser.rbs
|
113
115
|
- sig/dsl_compose/parser/block_arguments.rbs
|
114
116
|
- sig/dsl_compose/parser/for_children_of_parser.rbs
|