dsl_compose 1.9.1 → 1.11.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 +83 -1
- data/lib/dsl_compose/class_coerce.rb +20 -0
- data/lib/dsl_compose/dsl/arguments/argument/interpreter.rb +13 -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 +12 -4
- data/lib/dsl_compose/dsl/interpreter.rb +12 -4
- data/lib/dsl_compose/interpreter/execution/arguments.rb +130 -49
- 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/shared_configuration.rb +44 -0
- data/lib/dsl_compose/version.rb +1 -1
- data/lib/dsl_compose.rb +5 -0
- metadata +5 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: c6a23cf9faddf37ebe1fdec13fe554e8e7601e01f5c53495d8d3d37f28a63faf
|
4
|
+
data.tar.gz: 7254f3f5c46716e462189c72d2bd02b0131cdca1fb39387a8b86fd79cb2e9c6c
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: c71462286d85bdc0b4f2d3879b08bf04a142b4f10bb9554c230fa263d80e69d08fe73bd3cc198d516d8950df1a8b39863823cf0993b3a80af2110b80de69f2a6
|
7
|
+
data.tar.gz: 5b03471c8e676610297c16312b517bd62692fa5d85e5a610aed709684058032764668550622c84089d4cc6b7240d76c2d6a9d21487a4abf074eecaf93b6c19bd
|
data/CHANGELOG.md
CHANGED
@@ -1,5 +1,19 @@
|
|
1
1
|
# Changelog
|
2
2
|
|
3
|
+
## [1.11.0](https://github.com/craigulliott/dsl_compose/compare/v1.10.0...v1.11.0) (2023-07-12)
|
4
|
+
|
5
|
+
|
6
|
+
### Features
|
7
|
+
|
8
|
+
* 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))
|
9
|
+
|
10
|
+
## [1.10.0](https://github.com/craigulliott/dsl_compose/compare/v1.9.1...v1.10.0) (2023-07-11)
|
11
|
+
|
12
|
+
|
13
|
+
### Features
|
14
|
+
|
15
|
+
* ability to easily share configuration between multiple DSLs ([#27](https://github.com/craigulliott/dsl_compose/issues/27)) ([38ff61b](https://github.com/craigulliott/dsl_compose/commit/38ff61bed7c9d4357a0019c40fc20bc54d62bebb))
|
16
|
+
|
3
17
|
## [1.9.1](https://github.com/craigulliott/dsl_compose/compare/v1.9.0...v1.9.1) (2023-07-11)
|
4
18
|
|
5
19
|
|
data/README.md
CHANGED
@@ -15,6 +15,7 @@ Ruby gem to add dynamic DSLs to classes
|
|
15
15
|
* Automatically generate documentation for your DSLs
|
16
16
|
* Extensive test coverage
|
17
17
|
* Very lightweight and no external dependencies
|
18
|
+
* Share common DSL configuration between DSLs
|
18
19
|
|
19
20
|
## Installation
|
20
21
|
|
@@ -53,7 +54,9 @@ class Foo
|
|
53
54
|
# `required`)
|
54
55
|
#
|
55
56
|
# Arguments are validated, and their expected type must be defined. Supported
|
56
|
-
# 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.
|
57
60
|
requires :first_dsl_argument, :symbol do
|
58
61
|
# You should provide descriptions for your arguments. These descriptions will
|
59
62
|
# be used when generating your documentation. This description supports markdown
|
@@ -110,6 +113,16 @@ class Foo
|
|
110
113
|
# You can add validation to your arguments. A full list is provided later in this document
|
111
114
|
validate_greater_than 0
|
112
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
|
113
126
|
end
|
114
127
|
|
115
128
|
end
|
@@ -117,6 +130,39 @@ end
|
|
117
130
|
|
118
131
|
```
|
119
132
|
|
133
|
+
### Shared Configuration
|
134
|
+
|
135
|
+
If you are composing many DSLs across one or many classes and these DSLs share common configuration, then you can share configuration between them.
|
136
|
+
|
137
|
+
Define your shared configuration
|
138
|
+
|
139
|
+
```ruby
|
140
|
+
DSLCompose::SharedConfiguration.add :my_common_validators do
|
141
|
+
validate_not_end_with [:_id, :_at, :_count, :_type]
|
142
|
+
validate_not_in [:type, :stage]
|
143
|
+
validate_length minimum: 0, maximum: 10
|
144
|
+
validate_format /\A[a-z]+(_[a-z]+)*\Z/
|
145
|
+
end
|
146
|
+
```
|
147
|
+
|
148
|
+
Use your shared DSL from within any of your DSL compositions
|
149
|
+
|
150
|
+
```ruby
|
151
|
+
class Foo
|
152
|
+
include DSLCompose::Composer
|
153
|
+
|
154
|
+
define_dsl :your_dsl do
|
155
|
+
optional :optional_argument, :integer do
|
156
|
+
# this will import and apply your shared configuration
|
157
|
+
import_shared :my_common_validators
|
158
|
+
end
|
159
|
+
|
160
|
+
end
|
161
|
+
end
|
162
|
+
```
|
163
|
+
|
164
|
+
You can use `import_shared` anywhere within your DSL definition, you can even use `import_shared` within other shared configuration
|
165
|
+
|
120
166
|
### Using your DSL
|
121
167
|
|
122
168
|
Child classes can then use your new DSL
|
@@ -380,6 +426,42 @@ The following validations can be added to the arguments of your DSL methods. Val
|
|
380
426
|
end
|
381
427
|
```
|
382
428
|
|
429
|
+
### Class attributes (:class)
|
430
|
+
|
431
|
+
```ruby
|
432
|
+
define_dsl :my_dsl do
|
433
|
+
add_method :my_method do
|
434
|
+
|
435
|
+
requires :my_first_argument, :class do
|
436
|
+
|
437
|
+
# There are no validations for :class types
|
438
|
+
|
439
|
+
end
|
440
|
+
|
441
|
+
end
|
442
|
+
end
|
443
|
+
```
|
444
|
+
|
445
|
+
### Object attributes (:object)
|
446
|
+
|
447
|
+
```ruby
|
448
|
+
define_dsl :my_dsl do
|
449
|
+
add_method :my_method do
|
450
|
+
|
451
|
+
requires :my_first_argument, :object do
|
452
|
+
|
453
|
+
# The provided attribute must be an instantiated object which
|
454
|
+
# is an instance of the class.
|
455
|
+
#
|
456
|
+
# For example, this would accept a regexp object such as /[a-z]+/
|
457
|
+
validate_is_a Regexp
|
458
|
+
|
459
|
+
end
|
460
|
+
|
461
|
+
end
|
462
|
+
end
|
463
|
+
```
|
464
|
+
|
383
465
|
|
384
466
|
## Development
|
385
467
|
|
@@ -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
|
@@ -99,6 +99,19 @@ module DSLCompose
|
|
99
99
|
def validate_format regexp
|
100
100
|
@argument.validate_format regexp
|
101
101
|
end
|
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
|
+
|
108
|
+
# executes the shared configuration block with the given name within the
|
109
|
+
# context of this part of the DSL, this is a mechanism to share configuration
|
110
|
+
# between DSLs
|
111
|
+
def import_shared shared_configuration_name
|
112
|
+
block = SharedConfiguration.get shared_configuration_name
|
113
|
+
instance_eval(&block)
|
114
|
+
end
|
102
115
|
end
|
103
116
|
end
|
104
117
|
end
|
@@ -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,16 @@ 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
|
+
end
|
54
|
+
|
55
|
+
# executes the shared configuration block with the given name within the
|
56
|
+
# context of this part of the DSL, this is a mechanism to share configuration
|
57
|
+
# between DSLs
|
58
|
+
def import_shared shared_configuration_name
|
59
|
+
block = SharedConfiguration.get shared_configuration_name
|
60
|
+
instance_eval(&block)
|
53
61
|
end
|
54
62
|
end
|
55
63
|
end
|
@@ -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,16 @@ 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
|
+
end
|
70
|
+
|
71
|
+
# executes the shared configuration block with the given name within the
|
72
|
+
# context of this part of the DSL, this is a mechanism to share configuration
|
73
|
+
# between DSLs
|
74
|
+
def import_shared shared_configuration_name
|
75
|
+
block = SharedConfiguration.get shared_configuration_name
|
76
|
+
instance_eval(&block)
|
69
77
|
end
|
70
78
|
end
|
71
79
|
end
|
@@ -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
|
@@ -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`
|
@@ -0,0 +1,44 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module DSLCompose
|
4
|
+
# This module is used to store shared configuration blocks which can be imported and
|
5
|
+
# used within your base or method dsl configuration blocks.
|
6
|
+
module SharedConfiguration
|
7
|
+
class SharedConfigurationAlreadyExistsError < StandardError
|
8
|
+
end
|
9
|
+
|
10
|
+
class SharedConfigurationDoesNotExistError < StandardError
|
11
|
+
end
|
12
|
+
|
13
|
+
class InvalidSharedConfigurationNameError < StandardError
|
14
|
+
end
|
15
|
+
|
16
|
+
class NoBlockProvidedError < StandardError
|
17
|
+
end
|
18
|
+
|
19
|
+
# takes a name and a block and stores it in the shared_configuration hash
|
20
|
+
# if a block with this name already exists, it raises an error
|
21
|
+
def self.add name, &block
|
22
|
+
raise InvalidSharedConfigurationNameError unless name.is_a?(Symbol)
|
23
|
+
raise SharedConfigurationAlreadyExistsError if @shared_configuration&.key?(name)
|
24
|
+
raise NoBlockProvidedError if block.nil?
|
25
|
+
|
26
|
+
@shared_configuration ||= {}
|
27
|
+
@shared_configuration[name] = block
|
28
|
+
end
|
29
|
+
|
30
|
+
# takes a name and returns the block stored in the shared_configuration hash
|
31
|
+
# if a block with this name does not exist, it raises an error
|
32
|
+
def self.get name
|
33
|
+
raise InvalidSharedConfigurationNameError unless name.is_a?(Symbol)
|
34
|
+
raise SharedConfigurationDoesNotExistError unless @shared_configuration&.key?(name)
|
35
|
+
|
36
|
+
@shared_configuration[name]
|
37
|
+
end
|
38
|
+
|
39
|
+
# clears the shared_configuration hash, this is typically used from the test suite
|
40
|
+
def self.clear
|
41
|
+
@shared_configuration = {}
|
42
|
+
end
|
43
|
+
end
|
44
|
+
end
|
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,10 @@ require "dsl_compose/parser"
|
|
41
42
|
|
42
43
|
require "dsl_compose/composer"
|
43
44
|
|
45
|
+
require "dsl_compose/class_coerce"
|
46
|
+
|
47
|
+
require "dsl_compose/shared_configuration"
|
48
|
+
|
44
49
|
require "dsl_compose/dsls"
|
45
50
|
|
46
51
|
module DSLCompose
|
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: 1.
|
4
|
+
version: 1.11.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-
|
11
|
+
date: 2023-07-12 00:00:00.000000000 Z
|
12
12
|
dependencies: []
|
13
13
|
description: Ruby gem to add dynamic DSLs to classes. DSLs are added to classes by
|
14
14
|
including the DSLCompose module, and then calling the add_dsl singleton method within
|
@@ -24,6 +24,7 @@ files:
|
|
24
24
|
- LICENSE.txt
|
25
25
|
- README.md
|
26
26
|
- lib/dsl_compose.rb
|
27
|
+
- lib/dsl_compose/class_coerce.rb
|
27
28
|
- lib/dsl_compose/composer.rb
|
28
29
|
- lib/dsl_compose/dsl.rb
|
29
30
|
- lib/dsl_compose/dsl/arguments.rb
|
@@ -35,6 +36,7 @@ files:
|
|
35
36
|
- lib/dsl_compose/dsl/arguments/argument/greater_than_validation.rb
|
36
37
|
- lib/dsl_compose/dsl/arguments/argument/in_validation.rb
|
37
38
|
- lib/dsl_compose/dsl/arguments/argument/interpreter.rb
|
39
|
+
- lib/dsl_compose/dsl/arguments/argument/is_a_validation.rb
|
38
40
|
- lib/dsl_compose/dsl/arguments/argument/length_validation.rb
|
39
41
|
- lib/dsl_compose/dsl/arguments/argument/less_than_or_equal_to_validation.rb
|
40
42
|
- lib/dsl_compose/dsl/arguments/argument/less_than_validation.rb
|
@@ -56,6 +58,7 @@ files:
|
|
56
58
|
- lib/dsl_compose/parser/for_children_of_parser.rb
|
57
59
|
- lib/dsl_compose/parser/for_children_of_parser/for_dsl_parser.rb
|
58
60
|
- lib/dsl_compose/parser/for_children_of_parser/for_dsl_parser/for_method_parser.rb
|
61
|
+
- lib/dsl_compose/shared_configuration.rb
|
59
62
|
- lib/dsl_compose/version.rb
|
60
63
|
homepage:
|
61
64
|
licenses:
|