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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: d4269fe819882b592d12b6cce51673a6ef4d87116521ff267c76a7a23aab0382
4
- data.tar.gz: 9f13611b9417a7e34f0f81d4ccfda152eb5593168a483a011ccf2949a5624937
3
+ metadata.gz: a22a3595bbf6990c27628cf872f751939156cc9cda865d6ae05ca9cd6e6ba544
4
+ data.tar.gz: 79fcd4ddb46ecc10cc1dd84f50515c4bb85067a0b26482d7a6250112b4d44fc8
5
5
  SHA512:
6
- metadata.gz: 8923178c3d5ce6ab78229eb22a86412f2b4e7c9da41528b0b752965438dde12c6e8bfb1930880433ab4db2490efd36e15243e907f402544e4c2281a0b81f42e4
7
- data.tar.gz: e5a0d13825e052290e865365a933b36f6dedcc668f5642283e7f6cbe227617697e83898dec9224179f9f569aa7727a1a6b796201441377431d19ba2235d69fd3
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 :symbol
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 and uses at
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 :symbol"
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
- case optional_argument.type
56
- when :integer
57
- unless optional_arg_value.is_a? Integer
58
- raise InvalidArgumentTypeError, "#{optional_arg_value} is not an Integer"
59
- end
60
- optional_argument.validate_integer! optional_arg_value
61
-
62
- when :symbol
63
- unless optional_arg_value.is_a? Symbol
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
- optional_argument.validate_symbol! optional_arg_value
67
+ else
68
+ optional_arg[optional_argument_name]
69
+ end
67
70
 
68
- when :string
69
- unless optional_arg_value.is_a? String
70
- raise InvalidArgumentTypeError, "#{optional_arg_value} is not a String"
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
- when :boolean
75
- unless optional_arg_value.is_a?(TrueClass) || optional_arg_value.is_a?(FalseClass)
76
- raise InvalidArgumentTypeError, "#{optional_arg_value} is not a boolean"
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
- optional_argument.validate_boolean! optional_arg_value
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
- raise InvalidArgumentTypeError, "The argument #{optional_arg_value} is not a supported type"
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
- arg = args[i]
100
- case required_argument.type
101
- when :integer
102
- unless arg.is_a? Integer
103
- raise InvalidArgumentTypeError, "#{arg} is not an Integer"
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
- required_argument.validate_integer! arg
151
+ else
152
+ args[i]
153
+ end
106
154
 
107
- when :symbol
108
- unless arg.is_a? Symbol
109
- raise InvalidArgumentTypeError, "#{arg} is not a Symbol"
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
- when :string
114
- unless arg.is_a? String
115
- raise InvalidArgumentTypeError, "#{arg} is not a String"
116
- end
117
- required_argument.validate_string! arg
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
- when :boolean
120
- unless arg.is_a?(TrueClass) || arg.is_a?(FalseClass)
121
- raise InvalidArgumentTypeError, "#{arg} is not a boolean"
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
- required_argument.validate_boolean! arg
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
- raise InvalidArgumentTypeError, "The argument #{arg} is not a supported type"
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.klass == klass && e.dsl.name == dsl_name }
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
- args[name] = value
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
- args[name] = value
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 class which extends the base_class, provided
19
- # that the child also uses at least one of the DSLs which have been defined on the base_class
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
- # yield the provided block for each child class of the provided base_class
42
- # which uses a defined DSL
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
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module DSLCompose
4
- VERSION = "1.10.0"
4
+ VERSION = "1.12.0"
5
5
  end
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.10.0
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-11 00:00:00.000000000 Z
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