dsl_compose 1.10.0 → 1.12.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/CHANGELOG.md +14 -0
- data/README.md +53 -4
- data/lib/dsl_compose/class_coerce.rb +20 -0
- data/lib/dsl_compose/dsl/arguments/argument/interpreter.rb +5 -0
- data/lib/dsl_compose/dsl/arguments/argument/is_a_validation.rb +23 -0
- data/lib/dsl_compose/dsl/arguments/argument.rb +32 -3
- data/lib/dsl_compose/dsl/arguments.rb +2 -2
- data/lib/dsl_compose/dsl/dsl_method/interpreter.rb +4 -4
- data/lib/dsl_compose/dsl/interpreter.rb +4 -4
- data/lib/dsl_compose/interpreter/execution/arguments.rb +130 -49
- data/lib/dsl_compose/interpreter.rb +3 -2
- data/lib/dsl_compose/parser/for_children_of_parser/for_dsl_parser/for_method_parser.rb +3 -1
- data/lib/dsl_compose/parser/for_children_of_parser/for_dsl_parser.rb +3 -1
- data/lib/dsl_compose/parser/for_children_of_parser.rb +23 -6
- data/lib/dsl_compose/version.rb +1 -1
- data/lib/dsl_compose.rb +3 -0
- metadata +19 -3
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: a22a3595bbf6990c27628cf872f751939156cc9cda865d6ae05ca9cd6e6ba544
|
4
|
+
data.tar.gz: 79fcd4ddb46ecc10cc1dd84f50515c4bb85067a0b26482d7a6250112b4d44fc8
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 4cc5bf277a33d63d0dba6aa3053bfec1b5154d7f337c7e0e6fd0db2e5f429718e12097b640069904e44d95d02d2d576604d571cc45c96687ab8316064b4de3a7
|
7
|
+
data.tar.gz: 52a29affc55dd62fe4abbc965dc22973c1a01ba7acc170e33e8f7191ddf1ab19e4456f288cf69864282bea49afa8ac445ce1c1316053c07a6b4f2ef0ef1ba10a
|
data/CHANGELOG.md
CHANGED
@@ -1,5 +1,19 @@
|
|
1
1
|
# Changelog
|
2
2
|
|
3
|
+
## [1.12.0](https://github.com/craigulliott/dsl_compose/compare/v1.11.0...v1.12.0) (2023-07-13)
|
4
|
+
|
5
|
+
|
6
|
+
### Features
|
7
|
+
|
8
|
+
* parsers are now aware of class inheritance, and all classes now assume the DSL configurations of all their ancestors ([#33](https://github.com/craigulliott/dsl_compose/issues/33)) ([6a6cfb1](https://github.com/craigulliott/dsl_compose/commit/6a6cfb1fd9bb44c2ea949888c7b7be14ddc0cef8))
|
9
|
+
|
10
|
+
## [1.11.0](https://github.com/craigulliott/dsl_compose/compare/v1.10.0...v1.11.0) (2023-07-12)
|
11
|
+
|
12
|
+
|
13
|
+
### Features
|
14
|
+
|
15
|
+
* arguments can optionally accept arrays of values, also added a :class and :object type, and an associated "is_a" validator for :object ([#31](https://github.com/craigulliott/dsl_compose/issues/31)) ([acd7034](https://github.com/craigulliott/dsl_compose/commit/acd70345a4d1873e657d0e897f527269ca320453))
|
16
|
+
|
3
17
|
## [1.10.0](https://github.com/craigulliott/dsl_compose/compare/v1.9.1...v1.10.0) (2023-07-11)
|
4
18
|
|
5
19
|
|
data/README.md
CHANGED
@@ -54,7 +54,9 @@ class Foo
|
|
54
54
|
# `required`)
|
55
55
|
#
|
56
56
|
# Arguments are validated, and their expected type must be defined. Supported
|
57
|
-
# argument types are :integer, :boolean, :float, :string or :
|
57
|
+
# argument types are :integer, :boolean, :float, :string, :symbol, :class or :object
|
58
|
+
# when :class is used, it should be provided to your final DSL as a string of the
|
59
|
+
# classes name, such as "Users::UserModel" and not the actual class.
|
58
60
|
requires :first_dsl_argument, :symbol do
|
59
61
|
# You should provide descriptions for your arguments. These descriptions will
|
60
62
|
# be used when generating your documentation. This description supports markdown
|
@@ -111,6 +113,16 @@ class Foo
|
|
111
113
|
# You can add validation to your arguments. A full list is provided later in this document
|
112
114
|
validate_greater_than 0
|
113
115
|
end
|
116
|
+
|
117
|
+
# All optional and required arguments can optionally accept an array of values. When using
|
118
|
+
# your final DSL, a single item can be provided but will automatically be converted to an
|
119
|
+
# array of items. All items in the array must be of the expected type.
|
120
|
+
optional :another_optional_argument, :object, array: true do
|
121
|
+
description "A description of an optional argument which accepts an array"
|
122
|
+
|
123
|
+
# You can add validation to your arguments. A full list is provided later in this document
|
124
|
+
validate_is_a Date
|
125
|
+
end
|
114
126
|
end
|
115
127
|
|
116
128
|
end
|
@@ -229,8 +241,7 @@ A parser class can be used to process complicated DSLs. In the example below, a
|
|
229
241
|
# create your own parser by creating a new class which extends DSLCompose::Parser
|
230
242
|
MyParser < DSLCompose::Parser
|
231
243
|
# `for_children_of` will process SomeBaseClass and yield the provided
|
232
|
-
# block once for every class which extends SomeBaseClass
|
233
|
-
# least one of the DSLs that have been defined on it.
|
244
|
+
# block once for every class which extends SomeBaseClass
|
234
245
|
for_children_of SomeBaseClass do |child_class:|
|
235
246
|
# `for_dsl` accepts a DSL name or an array of DSL names and will yield
|
236
247
|
# it's provided block once for each time a DSL of that name has been
|
@@ -241,7 +252,9 @@ MyParser < DSLCompose::Parser
|
|
241
252
|
# You can optionally provide keyword arguments which correspond to any
|
242
253
|
# arguments that were defined for the DSL, if multiple dsl names are provided
|
243
254
|
# then the requested dsl argument must be present on all DSLs otherwise an
|
244
|
-
# error will be raised.
|
255
|
+
# error will be raised. The parser is aware of class inheritance, and will
|
256
|
+
# consider a DSL to have been executed on the actual class it was used, and
|
257
|
+
# any classes which are descendants of that class
|
245
258
|
for_dsl [:dsl1, :dsl2] do |dsl_name:, a_dsl_argument:|
|
246
259
|
# `for_method` accepts a method name or an array of method names and will
|
247
260
|
# yield it's provided block once for each time a method with this name is
|
@@ -414,6 +427,42 @@ The following validations can be added to the arguments of your DSL methods. Val
|
|
414
427
|
end
|
415
428
|
```
|
416
429
|
|
430
|
+
### Class attributes (:class)
|
431
|
+
|
432
|
+
```ruby
|
433
|
+
define_dsl :my_dsl do
|
434
|
+
add_method :my_method do
|
435
|
+
|
436
|
+
requires :my_first_argument, :class do
|
437
|
+
|
438
|
+
# There are no validations for :class types
|
439
|
+
|
440
|
+
end
|
441
|
+
|
442
|
+
end
|
443
|
+
end
|
444
|
+
```
|
445
|
+
|
446
|
+
### Object attributes (:object)
|
447
|
+
|
448
|
+
```ruby
|
449
|
+
define_dsl :my_dsl do
|
450
|
+
add_method :my_method do
|
451
|
+
|
452
|
+
requires :my_first_argument, :object do
|
453
|
+
|
454
|
+
# The provided attribute must be an instantiated object which
|
455
|
+
# is an instance of the class.
|
456
|
+
#
|
457
|
+
# For example, this would accept a regexp object such as /[a-z]+/
|
458
|
+
validate_is_a Regexp
|
459
|
+
|
460
|
+
end
|
461
|
+
|
462
|
+
end
|
463
|
+
end
|
464
|
+
```
|
465
|
+
|
417
466
|
|
418
467
|
## Development
|
419
468
|
|
@@ -0,0 +1,20 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module DSLCompose
|
4
|
+
class ClassCoerce
|
5
|
+
class UnexpectedClassNameError < StandardError
|
6
|
+
end
|
7
|
+
|
8
|
+
def initialize class_name
|
9
|
+
unless class_name.is_a? String
|
10
|
+
raise UnexpectedClassNameError, "expected `#{class_name}` to be a String"
|
11
|
+
end
|
12
|
+
|
13
|
+
@class_name = class_name
|
14
|
+
end
|
15
|
+
|
16
|
+
def to_class
|
17
|
+
Object.const_get @class_name
|
18
|
+
end
|
19
|
+
end
|
20
|
+
end
|
@@ -100,6 +100,11 @@ module DSLCompose
|
|
100
100
|
@argument.validate_format regexp
|
101
101
|
end
|
102
102
|
|
103
|
+
# adds an 'is a' validator to the argument
|
104
|
+
def validate_is_a klass
|
105
|
+
@argument.validate_is_a klass
|
106
|
+
end
|
107
|
+
|
103
108
|
# executes the shared configuration block with the given name within the
|
104
109
|
# context of this part of the DSL, this is a mechanism to share configuration
|
105
110
|
# between DSLs
|
@@ -0,0 +1,23 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module DSLCompose
|
4
|
+
class DSL
|
5
|
+
class Arguments
|
6
|
+
class Argument
|
7
|
+
class IsAValidation
|
8
|
+
class ValidationFailedError < StandardError
|
9
|
+
end
|
10
|
+
|
11
|
+
def initialize value
|
12
|
+
@value = value
|
13
|
+
end
|
14
|
+
|
15
|
+
def validate! value
|
16
|
+
raise ValidationFailedError, "The argument is invalid. Expected #{value} to be an instance of #{@value}" unless value.is_a?(@value)
|
17
|
+
true
|
18
|
+
end
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end
|
22
|
+
end
|
23
|
+
end
|
@@ -42,6 +42,9 @@ module DSLCompose
|
|
42
42
|
attr_reader :type
|
43
43
|
# if required, then this Argument must be provided when calling its associated DSLMethod.
|
44
44
|
attr_reader :required
|
45
|
+
# If true, then this argument accepts an array of values. It will also accept a single value,
|
46
|
+
# but that single value will be automatically converted to an array
|
47
|
+
attr_reader :array
|
45
48
|
# An otional description of this Attribute, if provided then it must be a string.
|
46
49
|
# The description accepts markdown and is used when generating documentation.
|
47
50
|
attr_reader :description
|
@@ -61,6 +64,7 @@ module DSLCompose
|
|
61
64
|
attr_reader :start_with_validation
|
62
65
|
attr_reader :not_start_with_validation
|
63
66
|
attr_reader :length_validation
|
67
|
+
attr_reader :is_a_validation
|
64
68
|
|
65
69
|
# Create a new Attribute object.
|
66
70
|
#
|
@@ -69,7 +73,7 @@ module DSLCompose
|
|
69
73
|
# calling its associated DSLMethod.
|
70
74
|
# `type` can be either :integer, :boolean, :float, :string or :symbol
|
71
75
|
# `block` contains the instructions to further configure this Attribute
|
72
|
-
def initialize name, required, type, &block
|
76
|
+
def initialize name, required, type, array: false, &block
|
73
77
|
if name.is_a? Symbol
|
74
78
|
|
75
79
|
if RESERVED_ARGUMENT_NAMES.include? name
|
@@ -81,14 +85,16 @@ module DSLCompose
|
|
81
85
|
raise InvalidNameError, "The option name `#{name}` is invalid, it must be of type symbol."
|
82
86
|
end
|
83
87
|
|
84
|
-
if type == :integer || type == :boolean || type == :float || type == :string || type == :symbol
|
88
|
+
if type == :integer || type == :boolean || type == :float || type == :string || type == :symbol || type == :class || type == :object
|
85
89
|
@type = type
|
86
90
|
else
|
87
|
-
raise InvalidTypeError, "Argument type `#{type}` must be one of :integer, :boolean, :float, :string or :
|
91
|
+
raise InvalidTypeError, "Argument type `#{type}` must be one of :integer, :boolean, :float, :string, :symbol, :class or :object"
|
88
92
|
end
|
89
93
|
|
90
94
|
@required = required ? true : false
|
91
95
|
|
96
|
+
@array = array ? true : false
|
97
|
+
|
92
98
|
# If a block was provided, then we evaluate it using a seperate
|
93
99
|
# interpreter class. We do this because the interpreter class contains
|
94
100
|
# no other methods or variables, if it was evaluated in the context of
|
@@ -331,6 +337,18 @@ module DSLCompose
|
|
331
337
|
@length_validation = LengthValidation.new(maximum: maximum, minimum: minimum, is: is)
|
332
338
|
end
|
333
339
|
|
340
|
+
def validate_is_a klass
|
341
|
+
if @is_a_validation
|
342
|
+
raise ValidationAlreadyExistsError, "The validation `is_a` has already been applied to this method option."
|
343
|
+
end
|
344
|
+
|
345
|
+
unless @type == :object
|
346
|
+
raise ValidationIncompatibleError, "The validation type #{@type} is not compatible with this argument type"
|
347
|
+
end
|
348
|
+
|
349
|
+
@is_a_validation = IsAValidation.new(klass)
|
350
|
+
end
|
351
|
+
|
334
352
|
# returns true if every provided integer validation also returns true
|
335
353
|
def validate_integer! value
|
336
354
|
(greater_than_validation.nil? || greater_than_validation.validate!(value)) &&
|
@@ -374,6 +392,17 @@ module DSLCompose
|
|
374
392
|
def validate_boolean! value
|
375
393
|
(equal_to_validation.nil? || equal_to_validation.validate!(value))
|
376
394
|
end
|
395
|
+
|
396
|
+
# returns true if every provided class validation also returns true
|
397
|
+
def validate_class! value
|
398
|
+
# there are no class validations
|
399
|
+
true
|
400
|
+
end
|
401
|
+
|
402
|
+
# returns true if every provided object validation also returns true
|
403
|
+
def validate_object! value
|
404
|
+
(is_a_validation.nil? || is_a_validation.validate!(value))
|
405
|
+
end
|
377
406
|
end
|
378
407
|
end
|
379
408
|
end
|
@@ -81,7 +81,7 @@ module DSLCompose
|
|
81
81
|
# Argument `name` must be unique within the DSLMethod.
|
82
82
|
# `required` must be a boolean, and determines if this argument will be required
|
83
83
|
# or optional on the method which is exposed in our DSL.
|
84
|
-
def add_argument name, required, type, &block
|
84
|
+
def add_argument name, required, type, array: false, &block
|
85
85
|
if @arguments.key? name
|
86
86
|
raise ArgumentAlreadyExistsError, "An argument with the name `#{name}` already exists for this DSL method"
|
87
87
|
end
|
@@ -91,7 +91,7 @@ module DSLCompose
|
|
91
91
|
raise ArgumentOrderingError, "Required arguments can not be added after optional ones"
|
92
92
|
end
|
93
93
|
|
94
|
-
@arguments[name] = Argument.new(name, required, type, &block)
|
94
|
+
@arguments[name] = Argument.new(name, required, type, array: array, &block)
|
95
95
|
end
|
96
96
|
end
|
97
97
|
end
|
@@ -38,8 +38,8 @@ module DSLCompose
|
|
38
38
|
# `type` can be either :integer, :boolean, :float, :string or :symbol
|
39
39
|
# `block` contains the argument definition and will be evaluated seperately
|
40
40
|
# by the Argument::Interpreter
|
41
|
-
def optional name, type, &block
|
42
|
-
@dsl_method.arguments.add_argument name, false, type, &block
|
41
|
+
def optional name, type, array: false, &block
|
42
|
+
@dsl_method.arguments.add_argument name, false, type, array: array, &block
|
43
43
|
end
|
44
44
|
|
45
45
|
# adds a new required argument to the DSLMethod
|
@@ -48,8 +48,8 @@ module DSLCompose
|
|
48
48
|
# `type` can be either :integer, :boolean, :float, :string or :symbol
|
49
49
|
# `block` contains the argument definition and will be evaluated seperately
|
50
50
|
# by the Argument::Interpreter
|
51
|
-
def requires name, type, &block
|
52
|
-
@dsl_method.arguments.add_argument name, true, type, &block
|
51
|
+
def requires name, type, array: false, &block
|
52
|
+
@dsl_method.arguments.add_argument name, true, type, array: array, &block
|
53
53
|
end
|
54
54
|
|
55
55
|
# executes the shared configuration block with the given name within the
|
@@ -54,8 +54,8 @@ module DSLCompose
|
|
54
54
|
# `type` can be either :integer, :boolean, :float, :string or :symbol
|
55
55
|
# `block` contains the argument definition and will be evaluated seperately
|
56
56
|
# by the Argument::Interpreter
|
57
|
-
def optional name, type, &block
|
58
|
-
@dsl.arguments.add_argument name, false, type, &block
|
57
|
+
def optional name, type, array: false, &block
|
58
|
+
@dsl.arguments.add_argument name, false, type, array: array, &block
|
59
59
|
end
|
60
60
|
|
61
61
|
# adds a new required argument to the DSLMethod
|
@@ -64,8 +64,8 @@ module DSLCompose
|
|
64
64
|
# `type` can be either :integer, :boolean, :float, :string or :symbol
|
65
65
|
# `block` contains the argument definition and will be evaluated seperately
|
66
66
|
# by the Argument::Interpreter
|
67
|
-
def requires name, type, &block
|
68
|
-
@dsl.arguments.add_argument name, true, type, &block
|
67
|
+
def requires name, type, array: false, &block
|
68
|
+
@dsl.arguments.add_argument name, true, type, array: array, &block
|
69
69
|
end
|
70
70
|
|
71
71
|
# executes the shared configuration block with the given name within the
|
@@ -16,6 +16,9 @@ module DSLCompose
|
|
16
16
|
class InvalidArgumentTypeError < StandardError
|
17
17
|
end
|
18
18
|
|
19
|
+
class ArrayNotValidError < StandardError
|
20
|
+
end
|
21
|
+
|
19
22
|
attr_reader :arguments
|
20
23
|
|
21
24
|
def initialize arguments, *args
|
@@ -49,41 +52,80 @@ module DSLCompose
|
|
49
52
|
|
50
53
|
# assert the each provided optional argument is valid
|
51
54
|
optional_arg.keys.each do |optional_argument_name|
|
52
|
-
optional_arg_value = optional_arg[optional_argument_name]
|
53
55
|
optional_argument = arguments.optional_argument optional_argument_name
|
54
56
|
|
55
|
-
|
56
|
-
|
57
|
-
|
58
|
-
|
59
|
-
|
60
|
-
|
61
|
-
|
62
|
-
|
63
|
-
|
64
|
-
raise InvalidArgumentTypeError, "#{optional_arg_value} is not a Symbol"
|
57
|
+
# the value for class types are wrapped in a ClassCoerce class so that they can be
|
58
|
+
# treated specially by the parser (it automatically converts them from a string name
|
59
|
+
# to the corresponding class, logic which doesn't happen here in case the class doesnt
|
60
|
+
# exist yet)
|
61
|
+
optional_arg_value = if optional_argument.type == :class
|
62
|
+
if optional_arg[optional_argument_name].is_a?(Array)
|
63
|
+
optional_arg[optional_argument_name].map { |v| ClassCoerce.new v }
|
64
|
+
else
|
65
|
+
ClassCoerce.new optional_arg[optional_argument_name]
|
65
66
|
end
|
66
|
-
|
67
|
+
else
|
68
|
+
optional_arg[optional_argument_name]
|
69
|
+
end
|
67
70
|
|
68
|
-
|
69
|
-
|
70
|
-
|
71
|
-
end
|
72
|
-
optional_argument.validate_string! optional_arg_value
|
71
|
+
if optional_arg_value.is_a?(Array) && !optional_argument.array
|
72
|
+
raise ArrayNotValidError, "An array was provided to an argument which does not accept an array of values"
|
73
|
+
end
|
73
74
|
|
74
|
-
|
75
|
-
|
76
|
-
|
75
|
+
# to simplify the code, we always process the reset of the validations as an array, even
|
76
|
+
# if the argument is not of type array
|
77
|
+
values = optional_arg_value.is_a?(Array) ? optional_arg_value : [optional_arg_value]
|
78
|
+
|
79
|
+
values.each do |value|
|
80
|
+
case optional_argument.type
|
81
|
+
when :integer
|
82
|
+
unless value.is_a? Integer
|
83
|
+
raise InvalidArgumentTypeError, "`#{value}` is not an Integer"
|
84
|
+
end
|
85
|
+
optional_argument.validate_integer! value
|
86
|
+
|
87
|
+
when :symbol
|
88
|
+
unless value.is_a? Symbol
|
89
|
+
raise InvalidArgumentTypeError, "`#{value}` is not a Symbol"
|
90
|
+
end
|
91
|
+
optional_argument.validate_symbol! value
|
92
|
+
|
93
|
+
when :string
|
94
|
+
unless value.is_a? String
|
95
|
+
raise InvalidArgumentTypeError, "`#{value}` is not a String"
|
96
|
+
end
|
97
|
+
optional_argument.validate_string! value
|
98
|
+
|
99
|
+
when :boolean
|
100
|
+
unless value.is_a?(TrueClass) || value.is_a?(FalseClass)
|
101
|
+
raise InvalidArgumentTypeError, "`#{value}` is not a boolean"
|
102
|
+
end
|
103
|
+
optional_argument.validate_boolean! value
|
104
|
+
|
105
|
+
when :class
|
106
|
+
unless value.is_a?(ClassCoerce)
|
107
|
+
raise InvalidArgumentTypeError, "`#{value}` is not a class coerce (String)"
|
108
|
+
end
|
109
|
+
optional_argument.validate_class! value
|
110
|
+
|
111
|
+
when :object
|
112
|
+
optional_argument.validate_object! value
|
113
|
+
|
114
|
+
else
|
115
|
+
raise InvalidArgumentTypeError, "The argument value `#{value}` is not a supported type"
|
77
116
|
end
|
78
|
-
|
117
|
+
end
|
79
118
|
|
119
|
+
# The provided value appears valid for this argument, save the value.
|
120
|
+
#
|
121
|
+
# If the argument accepts an array of values, then automatically convert valid singular values
|
122
|
+
# into an array.
|
123
|
+
@arguments[optional_argument_name] = if optional_argument.array && !optional_arg_value.is_a?(Array)
|
124
|
+
[optional_arg_value]
|
80
125
|
else
|
81
|
-
|
126
|
+
optional_arg_value
|
82
127
|
end
|
83
128
|
|
84
|
-
# the provided value appears valid for this argument, save the value
|
85
|
-
@arguments[optional_argument_name] = optional_arg_value
|
86
|
-
|
87
129
|
rescue => e
|
88
130
|
raise e, "Error processing optional argument #{optional_argument_name}: #{e.message}", e.backtrace
|
89
131
|
end
|
@@ -96,39 +138,78 @@ module DSLCompose
|
|
96
138
|
argument_name = nil
|
97
139
|
argument_name = required_argument.name
|
98
140
|
|
99
|
-
|
100
|
-
|
101
|
-
|
102
|
-
|
103
|
-
|
141
|
+
# the value for class types are wrapped in a ClassCoerce class so that they can be
|
142
|
+
# treated specially by the parser (it automatically converts them from a string name
|
143
|
+
# to the corresponding class, logic which doesn't happen here in case the class doesnt
|
144
|
+
# exist yet)
|
145
|
+
required_arg_value = if required_argument.type == :class
|
146
|
+
if args[i].is_a?(Array)
|
147
|
+
args[i].map { |v| ClassCoerce.new v }
|
148
|
+
else
|
149
|
+
ClassCoerce.new args[i]
|
104
150
|
end
|
105
|
-
|
151
|
+
else
|
152
|
+
args[i]
|
153
|
+
end
|
106
154
|
|
107
|
-
|
108
|
-
|
109
|
-
|
110
|
-
end
|
111
|
-
required_argument.validate_symbol! arg
|
155
|
+
if required_arg_value.is_a?(Array) && !required_argument.array
|
156
|
+
raise ArrayNotValidError, "An array was provided to an argument which does not accept an array of values"
|
157
|
+
end
|
112
158
|
|
113
|
-
|
114
|
-
|
115
|
-
|
116
|
-
|
117
|
-
|
159
|
+
# to simplify the code, we always process the reset of the validations as an array, even
|
160
|
+
# if the argument is not of type array
|
161
|
+
values = required_arg_value.is_a?(Array) ? required_arg_value : [required_arg_value]
|
162
|
+
|
163
|
+
values.each do |value|
|
164
|
+
case required_argument.type
|
165
|
+
when :integer
|
166
|
+
unless value.is_a? Integer
|
167
|
+
raise InvalidArgumentTypeError, "`#{value}` is not an Integer"
|
168
|
+
end
|
169
|
+
required_argument.validate_integer! value
|
170
|
+
|
171
|
+
when :symbol
|
172
|
+
unless value.is_a? Symbol
|
173
|
+
raise InvalidArgumentTypeError, "`#{value}` is not a Symbol"
|
174
|
+
end
|
175
|
+
required_argument.validate_symbol! value
|
176
|
+
|
177
|
+
when :string
|
178
|
+
unless value.is_a? String
|
179
|
+
raise InvalidArgumentTypeError, "`#{value}` is not a String"
|
180
|
+
end
|
181
|
+
required_argument.validate_string! value
|
118
182
|
|
119
|
-
|
120
|
-
|
121
|
-
|
183
|
+
when :boolean
|
184
|
+
unless value.is_a?(TrueClass) || value.is_a?(FalseClass)
|
185
|
+
raise InvalidArgumentTypeError, "`#{value}` is not a boolean"
|
186
|
+
end
|
187
|
+
required_argument.validate_boolean! value
|
188
|
+
|
189
|
+
when :class
|
190
|
+
unless value.is_a?(ClassCoerce)
|
191
|
+
raise InvalidArgumentTypeError, "`#{value}` is not a class coerce (String)"
|
192
|
+
end
|
193
|
+
required_argument.validate_class! value
|
194
|
+
|
195
|
+
when :object
|
196
|
+
required_argument.validate_object! value
|
197
|
+
|
198
|
+
else
|
199
|
+
raise InvalidArgumentTypeError, "The argument `#{value}` is not a supported type"
|
122
200
|
end
|
123
|
-
|
201
|
+
end
|
124
202
|
|
203
|
+
# The provided value appears valid for this argument, save the value.
|
204
|
+
#
|
205
|
+
# If the argument accepts an array of values, then automatically convert valid singular values
|
206
|
+
# into an array.
|
207
|
+
@arguments[argument_name] = if required_argument.array && !required_arg_value.is_a?(Array)
|
208
|
+
[required_arg_value]
|
125
209
|
else
|
126
|
-
|
210
|
+
required_arg_value
|
127
211
|
end
|
128
212
|
|
129
|
-
# the provided value appears valid for this argument, save the value
|
130
|
-
@arguments[required_argument.name] = arg
|
131
|
-
|
132
213
|
rescue => e
|
133
214
|
raise e, "Error processing required argument #{argument_name}: #{e.message}", e.backtrace
|
134
215
|
end
|
@@ -40,9 +40,10 @@ module DSLCompose
|
|
40
40
|
@executions.filter { |e| e.dsl.name == dsl_name }
|
41
41
|
end
|
42
42
|
|
43
|
-
# Returns an array of all executions for a given name and class.
|
43
|
+
# Returns an array of all executions for a given name and class. This includes
|
44
|
+
# any ancestors of the provided class
|
44
45
|
def class_dsl_executions klass, dsl_name
|
45
|
-
@executions.filter { |e| e.
|
46
|
+
@executions.filter { |e| e.dsl.name == dsl_name && (e.klass == klass || klass < e.klass) }
|
46
47
|
end
|
47
48
|
|
48
49
|
# removes all executions from the interpreter, this is primarily used from
|
@@ -77,7 +77,9 @@ module DSLCompose
|
|
77
77
|
# add any arguments that were provided to the method call
|
78
78
|
method_call.arguments.arguments.each do |name, value|
|
79
79
|
if BlockArguments.accepts_argument?(name, &block)
|
80
|
-
|
80
|
+
# if this value is a ClassCoerce object, then convert it from its original
|
81
|
+
# string value to a class
|
82
|
+
args[name] = value.is_a?(ClassCoerce) ? value.to_class : value
|
81
83
|
end
|
82
84
|
end
|
83
85
|
|
@@ -73,7 +73,9 @@ module DSLCompose
|
|
73
73
|
# add any arguments that were provided to the DSL
|
74
74
|
dsl_execution.arguments.arguments.each do |name, value|
|
75
75
|
if BlockArguments.accepts_argument?(name, &block)
|
76
|
-
|
76
|
+
# if this value is a ClassCoerce object, then convert it from its original
|
77
|
+
# string value to a class
|
78
|
+
args[name] = value.is_a?(ClassCoerce) ? value.to_class : value
|
77
79
|
end
|
78
80
|
end
|
79
81
|
# set the dsl_execution in an instance variable so that method calls to `for_method`
|
@@ -15,8 +15,26 @@ module DSLCompose
|
|
15
15
|
class NoChildClassError < StandardError
|
16
16
|
end
|
17
17
|
|
18
|
-
# This class will yield to the provided block for each
|
19
|
-
#
|
18
|
+
# This class will yield to the provided block for each descendant
|
19
|
+
# class of the provided base_class
|
20
|
+
#
|
21
|
+
# For example:
|
22
|
+
#
|
23
|
+
# class BaseClass
|
24
|
+
# include DSLCompose::Composer
|
25
|
+
# define_dsl :my_foo_dsl
|
26
|
+
# end
|
27
|
+
#
|
28
|
+
# class ChildClass < BaseClass
|
29
|
+
# my_foo_dsl
|
30
|
+
# end
|
31
|
+
#
|
32
|
+
# class GrandchildClass < ChildClass
|
33
|
+
# end
|
34
|
+
#
|
35
|
+
# parser.for_children_of BaseClass do |child_class:|
|
36
|
+
# # this will yield for ChildClass and GrandchildClass
|
37
|
+
# and
|
20
38
|
def initialize base_class, &block
|
21
39
|
# assert the provided class has the DSLCompose::Composer module installed
|
22
40
|
unless base_class.respond_to? :dsls
|
@@ -38,9 +56,8 @@ module DSLCompose
|
|
38
56
|
end
|
39
57
|
end
|
40
58
|
|
41
|
-
#
|
42
|
-
|
43
|
-
base_class.dsls.executions_by_class.each do |child_class, dsl|
|
59
|
+
# yeild the block for all descendents of the provided base_class
|
60
|
+
ObjectSpace.each_object(Class).select { |klass| klass < base_class }.each do |child_class|
|
44
61
|
args = {}
|
45
62
|
if BlockArguments.accepts_argument?(:child_class, &block)
|
46
63
|
args[:child_class] = child_class
|
@@ -57,7 +74,7 @@ module DSLCompose
|
|
57
74
|
|
58
75
|
# Given a dsl name, or array of dsl names, this method will yield to the
|
59
76
|
# provided block once for each time a dsl with one of the provided names
|
60
|
-
# was used on the correspondng child_class
|
77
|
+
# was used on the correspondng child_class or any of its ancestors.
|
61
78
|
#
|
62
79
|
# The value of child_class and base_class are set from the yield of this
|
63
80
|
# classes initializer, meaning that the use of this method should look
|
data/lib/dsl_compose/version.rb
CHANGED
data/lib/dsl_compose.rb
CHANGED
@@ -8,6 +8,7 @@ require "dsl_compose/dsl/arguments/argument/format_validation"
|
|
8
8
|
require "dsl_compose/dsl/arguments/argument/greater_than_or_equal_to_validation"
|
9
9
|
require "dsl_compose/dsl/arguments/argument/greater_than_validation"
|
10
10
|
require "dsl_compose/dsl/arguments/argument/in_validation"
|
11
|
+
require "dsl_compose/dsl/arguments/argument/is_a_validation"
|
11
12
|
require "dsl_compose/dsl/arguments/argument/length_validation"
|
12
13
|
require "dsl_compose/dsl/arguments/argument/less_than_or_equal_to_validation"
|
13
14
|
require "dsl_compose/dsl/arguments/argument/less_than_validation"
|
@@ -41,6 +42,8 @@ require "dsl_compose/parser"
|
|
41
42
|
|
42
43
|
require "dsl_compose/composer"
|
43
44
|
|
45
|
+
require "dsl_compose/class_coerce"
|
46
|
+
|
44
47
|
require "dsl_compose/shared_configuration"
|
45
48
|
|
46
49
|
require "dsl_compose/dsls"
|
metadata
CHANGED
@@ -1,15 +1,29 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: dsl_compose
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 1.
|
4
|
+
version: 1.12.0
|
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-07-
|
12
|
-
dependencies:
|
11
|
+
date: 2023-07-13 00:00:00.000000000 Z
|
12
|
+
dependencies:
|
13
|
+
- !ruby/object:Gem::Dependency
|
14
|
+
name: class_spec_helper
|
15
|
+
requirement: !ruby/object:Gem::Requirement
|
16
|
+
requirements:
|
17
|
+
- - ">="
|
18
|
+
- !ruby/object:Gem::Version
|
19
|
+
version: '0'
|
20
|
+
type: :development
|
21
|
+
prerelease: false
|
22
|
+
version_requirements: !ruby/object:Gem::Requirement
|
23
|
+
requirements:
|
24
|
+
- - ">="
|
25
|
+
- !ruby/object:Gem::Version
|
26
|
+
version: '0'
|
13
27
|
description: Ruby gem to add dynamic DSLs to classes. DSLs are added to classes by
|
14
28
|
including the DSLCompose module, and then calling the add_dsl singleton method within
|
15
29
|
the class or a child class.
|
@@ -24,6 +38,7 @@ files:
|
|
24
38
|
- LICENSE.txt
|
25
39
|
- README.md
|
26
40
|
- lib/dsl_compose.rb
|
41
|
+
- lib/dsl_compose/class_coerce.rb
|
27
42
|
- lib/dsl_compose/composer.rb
|
28
43
|
- lib/dsl_compose/dsl.rb
|
29
44
|
- lib/dsl_compose/dsl/arguments.rb
|
@@ -35,6 +50,7 @@ files:
|
|
35
50
|
- lib/dsl_compose/dsl/arguments/argument/greater_than_validation.rb
|
36
51
|
- lib/dsl_compose/dsl/arguments/argument/in_validation.rb
|
37
52
|
- lib/dsl_compose/dsl/arguments/argument/interpreter.rb
|
53
|
+
- lib/dsl_compose/dsl/arguments/argument/is_a_validation.rb
|
38
54
|
- lib/dsl_compose/dsl/arguments/argument/length_validation.rb
|
39
55
|
- lib/dsl_compose/dsl/arguments/argument/less_than_or_equal_to_validation.rb
|
40
56
|
- lib/dsl_compose/dsl/arguments/argument/less_than_validation.rb
|