dsl_compose 1.10.0 → 1.12.0

Sign up to get free protection for your applications and to get access to all the features.
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