dsl_compose 1.12.0 → 1.13.1

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: a22a3595bbf6990c27628cf872f751939156cc9cda865d6ae05ca9cd6e6ba544
4
- data.tar.gz: 79fcd4ddb46ecc10cc1dd84f50515c4bb85067a0b26482d7a6250112b4d44fc8
3
+ metadata.gz: 9ca102e9e28dc1732f0a6c14282bdfc6b0cbc3edddc8b76864921c8d5a295ec0
4
+ data.tar.gz: 349941451a9954f29938fb91b0324662b306d215ce725e68faa00d25937b883d
5
5
  SHA512:
6
- metadata.gz: 4cc5bf277a33d63d0dba6aa3053bfec1b5154d7f337c7e0e6fd0db2e5f429718e12097b640069904e44d95d02d2d576604d571cc45c96687ab8316064b4de3a7
7
- data.tar.gz: 52a29affc55dd62fe4abbc965dc22973c1a01ba7acc170e33e8f7191ddf1ab19e4456f288cf69864282bea49afa8ac445ce1c1316053c07a6b4f2ef0ef1ba10a
6
+ metadata.gz: '002348ac24e6f8bf513478702c6c08368fb1ee8c788ad8a893d7a848e41103ee6208e5e24f260e0be79eb069700047d408b0c51e51ea3b861bccf55fe2312882'
7
+ data.tar.gz: 14431a1691709376eb73196c5c7280a7d0ec451d4eb58a3e8ffa3bc43bb6cf26f4433b8f4d6fb488db57d9be06bbcf4f974e5175719d44766a1247271ea76e34
data/CHANGELOG.md CHANGED
@@ -1,5 +1,19 @@
1
1
  # Changelog
2
2
 
3
+ ## [1.13.1](https://github.com/craigulliott/dsl_compose/compare/v1.13.0...v1.13.1) (2023-07-19)
4
+
5
+
6
+ ### Bug Fixes
7
+
8
+ * unused optional arguments no longer raise an error within the parser ([#37](https://github.com/craigulliott/dsl_compose/issues/37)) ([99cd04b](https://github.com/craigulliott/dsl_compose/commit/99cd04b83387c7940e364dc86d527fdb51152405))
9
+
10
+ ## [1.13.0](https://github.com/craigulliott/dsl_compose/compare/v1.12.0...v1.13.0) (2023-07-17)
11
+
12
+
13
+ ### Features
14
+
15
+ * added for_final_children_of, for_inherited_dsl and for_dsl_or_inherited_dsl methods to the parser ([#35](https://github.com/craigulliott/dsl_compose/issues/35)) ([b01a629](https://github.com/craigulliott/dsl_compose/commit/b01a629e1b18540cbbd90ba53090b41b8fb41dfd))
16
+
3
17
  ## [1.12.0](https://github.com/craigulliott/dsl_compose/compare/v1.11.0...v1.12.0) (2023-07-13)
4
18
 
5
19
 
data/README.md CHANGED
@@ -241,13 +241,23 @@ A parser class can be used to process complicated DSLs. In the example below, a
241
241
  # create your own parser by creating a new class which extends DSLCompose::Parser
242
242
  MyParser < DSLCompose::Parser
243
243
  # `for_children_of` will process SomeBaseClass and yield the provided
244
- # block once for every class which extends SomeBaseClass
244
+ # block once for every class which extends SomeBaseClass.
245
+ #
246
+ # If you only want to process classes at the end of the class hierarchy (classes
247
+ # which extend the provided base class, but do not have their own children) then
248
+ # use `for_final_children_of` instead of `for_children_of`
245
249
  for_children_of SomeBaseClass do |child_class:|
246
- # `for_dsl` accepts a DSL name or an array of DSL names and will yield
247
- # it's provided block once for each time a DSL of that name has been
248
- # used on the child_class.
250
+ # `for_dsl` accepts a DSL name or an array of DSL names and will yield it's
251
+ # provided block once for each time a DSL of that name has been used
252
+ # directly on the child_class.
249
253
  #
250
- # An error will be raised if any of the provided DSL names does not exist
254
+ # If you want to yield the provided block for classes which didn't directly use
255
+ # one of the provided DSLs, but the DSL was used on one of their ancestors, then
256
+ # use `for_inherited_dsl :dsl_name` instead of `for_dsl :dsl_name`. If you want
257
+ # the block to yield whether the DSL was used directly on the provided class or
258
+ # anywhere in it's ancestor chain, then use `for_dsl_or_inherited_dsl :dsl_name`.
259
+ #
260
+ # An error will be raised if any of the provided DSL names does not exist.
251
261
  #
252
262
  # You can optionally provide keyword arguments which correspond to any
253
263
  # arguments that were defined for the DSL, if multiple dsl names are provided
@@ -44,6 +44,13 @@ module DSLCompose
44
44
  raise TooManyArgumentsError, "Too many arguments provided"
45
45
  end
46
46
 
47
+ # assume all optonal arguments are nil. If actual values were provided, then they will be set below
48
+ if arguments.optional_arguments.any?
49
+ arguments.optional_arguments.each do |optional_argument|
50
+ @arguments[optional_argument.name] = nil
51
+ end
52
+ end
53
+
47
54
  # asset that, if provided, then the optional argument (always the last one) is a Hash
48
55
  if has_optional_arguments && optional_arg.nil? === false
49
56
  unless optional_arg.is_a? Hash
@@ -42,8 +42,8 @@ module DSLCompose
42
42
 
43
43
  # Returns an array of all executions for a given name and class. This includes
44
44
  # any ancestors of the provided class
45
- def class_dsl_executions klass, dsl_name
46
- @executions.filter { |e| e.dsl.name == dsl_name && (e.klass == klass || klass < e.klass) }
45
+ def class_dsl_executions klass, dsl_name, on_current_class, on_ancestor_class
46
+ @executions.filter { |e| e.dsl.name == dsl_name && ((on_current_class && e.klass == klass) || (on_ancestor_class && klass < e.klass)) }
47
47
  end
48
48
 
49
49
  # removes all executions from the interpreter, this is primarily used from
@@ -0,0 +1,41 @@
1
+ # frozen_string_literal: true
2
+
3
+ module DSLCompose
4
+ class Parser
5
+ class ForChildrenOfParser
6
+ class Descendents
7
+ def initialize base_class, final_children_only
8
+ @base_class = base_class
9
+ @final_children_only = final_children_only
10
+ end
11
+
12
+ def classes
13
+ # all objects which extend the provided base class
14
+ extending_classes = ObjectSpace.each_object(Class).select { |klass| klass < @base_class }
15
+
16
+ # sort the results, classes are ordered first by the depth of their namespace, and second
17
+ # by their name
18
+ extending_classes.sort_by! do |child_class|
19
+ "#{child_class.name.split("::").count}_#{child_class.name}"
20
+ end
21
+
22
+ # if this is not a final child, but we are processing final children only, then skip it
23
+ if @final_children_only
24
+ # reject any classes which have descendents
25
+ extending_classes.reject! do |child_class|
26
+ has_descendents child_class
27
+ end
28
+ end
29
+
30
+ extending_classes
31
+ end
32
+
33
+ private
34
+
35
+ def has_descendents base_class
36
+ ObjectSpace.each_object(Class).count { |klass| klass < base_class } > 0
37
+ end
38
+ end
39
+ end
40
+ end
41
+ end
@@ -20,7 +20,7 @@ module DSLCompose
20
20
  # of the provided name is used by the child class.
21
21
  #
22
22
  # base_class and child_class are set from the ForChildrenOfParser
23
- def initialize base_class, child_class, dsl_names, &block
23
+ def initialize base_class, child_class, dsl_names, on_current_class, on_ancestor_class, &block
24
24
  @base_class = base_class
25
25
  @child_class = child_class
26
26
  @dsl_names = dsl_names
@@ -31,7 +31,7 @@ module DSLCompose
31
31
  end
32
32
 
33
33
  # if any arguments were provided, then assert that they are valid
34
- if block.parameters.any?
34
+ if block&.parameters&.any?
35
35
  # all parameters must be keyword arguments
36
36
  if block.parameters.filter { |p| p.first != :keyreq }.any?
37
37
  raise AllBlockParametersMustBeKeywordParametersError, "All block parameters must be keyword parameters, i.e. `for_dsl :dsl_name do |dsl_name:|`"
@@ -62,7 +62,7 @@ module DSLCompose
62
62
  dsl_names.each do |dsl_name|
63
63
  # a dsl can be execued multiple times on a class, so we find all of the executions
64
64
  # here and then yield the block once for each execution
65
- base_class.dsls.class_dsl_executions(child_class, dsl_name).each do |dsl_execution|
65
+ base_class.dsls.class_dsl_executions(child_class, dsl_name, on_current_class, on_ancestor_class).each do |dsl_execution|
66
66
  # we only provide the requested arguments to the block, this allows
67
67
  # us to use keyword arguments to force a naming convention on these arguments
68
68
  # and to validate their use
@@ -35,7 +35,7 @@ module DSLCompose
35
35
  # parser.for_children_of BaseClass do |child_class:|
36
36
  # # this will yield for ChildClass and GrandchildClass
37
37
  # and
38
- def initialize base_class, &block
38
+ def initialize base_class, final_children_only, &block
39
39
  # assert the provided class has the DSLCompose::Composer module installed
40
40
  unless base_class.respond_to? :dsls
41
41
  raise ClassDoesNotUseDSLComposeError, base_class
@@ -49,7 +49,7 @@ module DSLCompose
49
49
  end
50
50
 
51
51
  # if any arguments were provided, then assert that they are valid
52
- if block.parameters.any?
52
+ if block&.parameters&.any?
53
53
  # all parameters must be keyword arguments
54
54
  if block.parameters.filter { |p| p.first != :keyreq }.any?
55
55
  raise AllBlockParametersMustBeKeywordParametersError, "All block parameters must be keyword parameters, i.e. `for_children_of FooClass do |base_class:|`"
@@ -57,14 +57,17 @@ module DSLCompose
57
57
  end
58
58
 
59
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|
60
+ Descendents.new(base_class, final_children_only).classes.each do |child_class|
61
+ # determine which arguments to send to the block
61
62
  args = {}
62
63
  if BlockArguments.accepts_argument?(:child_class, &block)
63
64
  args[:child_class] = child_class
64
65
  end
66
+
65
67
  # set the child_class in an instance variable so that method calls to
66
68
  # `for_dsl` from within the block will have access to it
67
69
  @child_class = child_class
70
+
68
71
  # yield the block in the context of this class
69
72
  instance_exec(**args, &block)
70
73
  end
@@ -85,14 +88,38 @@ module DSLCompose
85
88
  # ...
86
89
  # end
87
90
  # end
88
- def for_dsl dsl_names, &block
91
+ #
92
+ # If `on_current_class` is true, then the block will be yielded to for each DSL
93
+ # which was used directly on the current class. If `oncurrent_class` is false,
94
+ # then the block will not be yielded to for any DSL which was used directly on.
95
+ # If `on_ancestor_class` is true, then the block will be yielded to for each DSL
96
+ # which was used on any class in the current classes ancestry. If `on_ancestor_class`
97
+ # is false, then the block will not be yielded to for any DSL which was used on
98
+ # any class in the current classes ancestry.
99
+ def for_dsl dsl_names, on_current_class: true, on_ancestor_class: false, &block
89
100
  child_class = @child_class
90
101
 
91
102
  unless child_class
92
103
  raise NoChildClassError, "No child_class was found, please call this method from within a `for_children_of` block"
93
104
  end
94
105
 
95
- ForDSLParser.new(@base_class, child_class, dsl_names, &block)
106
+ ForDSLParser.new(@base_class, child_class, dsl_names, on_current_class, on_ancestor_class, &block)
107
+ end
108
+
109
+ # this is a wrapper for the `for_dsl` method, but it provides a value of true
110
+ # for the `on_ancestor_class` argument and a value of false for the `on_current_class`
111
+ # argument. This will cause the parser to only yeild for dsls which were used on
112
+ # a class which is in the current classes ancestry, but not on the current class
113
+ def for_inherited_dsl dsl_names, &block
114
+ for_dsl dsl_names, on_current_class: false, on_ancestor_class: true, &block
115
+ end
116
+
117
+ # this is a wrapper for the `for_dsl` method, but it provides a value of true
118
+ # for the `on_ancestor_class` argument and a value of true for the `on_current_class`
119
+ # argument. This will cause the parser to yeild for dsls which were used on either
120
+ # the current class or any class in its ancestry
121
+ def for_dsl_or_inherited_dsl dsl_names, &block
122
+ for_dsl dsl_names, on_current_class: true, on_ancestor_class: true, &block
96
123
  end
97
124
  end
98
125
  end
@@ -24,30 +24,44 @@ module DSLCompose
24
24
  raise NotInitializable
25
25
  end
26
26
 
27
- # the first step in defining a parser is to set the base_class, this method
27
+ # The first step in defining a parser is to set the base_class, this method
28
28
  # will yield to the provided block for each child class of the provided base_class
29
- # provided that the child_class uses at least one of the base_classes defined DSLs
30
- def self.for_children_of base_class, rerun = false, &block
29
+ # provided that the child_class uses at least one of the base_classes defined DSLs.
30
+ # If `final_children_only` is true, then this will cause the parser to only return
31
+ # classes which are at the end of their class hierachy (meaning they dont have any
32
+ # children of their own)
33
+ def self.for_children_of base_class, final_children_only: false, rerun: false, &block
31
34
  unless rerun
32
35
  @runs ||= []
33
36
  @runs << {
34
37
  base_class: base_class,
38
+ final_children_only: final_children_only,
35
39
  block: block
36
40
  }
37
41
  end
38
42
 
39
43
  # we parse the provided block in the context of the ForChildrenOfParser class
40
44
  # to help make this code more readable, and to limit polluting the current namespace
41
- ForChildrenOfParser.new(base_class, &block)
45
+ ForChildrenOfParser.new(base_class, final_children_only, &block)
46
+ end
47
+
48
+ # this is a wrapper for the `for_children_of` method, but it provides a value
49
+ # of true for the `final_children_only` argument. This will cause the parser to only
50
+ # return classes which are at the end of the class hierachy (meaning they dont have
51
+ # any children of their own)
52
+ def self.for_final_children_of base_class, &block
53
+ for_children_of base_class, final_children_only: true, &block
42
54
  end
43
55
 
44
56
  # this method is used to rerun the parser, this is most useful from within a test suite
45
57
  # when you are testing the parser itself
46
58
  def self.rerun
59
+ # rerun each parset tests
47
60
  @runs&.each do |run|
48
61
  base_class = run[:base_class]
49
62
  block = run[:block]
50
- for_children_of base_class, true, &block
63
+ final_children_only = run[:final_children_only]
64
+ for_children_of base_class, rerun: true, final_children_only: final_children_only, &block
51
65
  end
52
66
  end
53
67
  end
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module DSLCompose
4
- VERSION = "1.12.0"
4
+ VERSION = "1.13.1"
5
5
  end
data/lib/dsl_compose.rb CHANGED
@@ -36,6 +36,7 @@ require "dsl_compose/interpreter"
36
36
 
37
37
  require "dsl_compose/parser/for_children_of_parser/for_dsl_parser/for_method_parser"
38
38
  require "dsl_compose/parser/for_children_of_parser/for_dsl_parser"
39
+ require "dsl_compose/parser/for_children_of_parser/descendents"
39
40
  require "dsl_compose/parser/for_children_of_parser"
40
41
  require "dsl_compose/parser/block_arguments"
41
42
  require "dsl_compose/parser"
@@ -49,6 +50,4 @@ require "dsl_compose/shared_configuration"
49
50
  require "dsl_compose/dsls"
50
51
 
51
52
  module DSLCompose
52
- class Error < StandardError
53
- end
54
53
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: dsl_compose
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.12.0
4
+ version: 1.13.1
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-13 00:00:00.000000000 Z
11
+ date: 2023-07-19 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: class_spec_helper
@@ -70,6 +70,7 @@ files:
70
70
  - lib/dsl_compose/parser.rb
71
71
  - lib/dsl_compose/parser/block_arguments.rb
72
72
  - lib/dsl_compose/parser/for_children_of_parser.rb
73
+ - lib/dsl_compose/parser/for_children_of_parser/descendents.rb
73
74
  - lib/dsl_compose/parser/for_children_of_parser/for_dsl_parser.rb
74
75
  - lib/dsl_compose/parser/for_children_of_parser/for_dsl_parser/for_method_parser.rb
75
76
  - lib/dsl_compose/shared_configuration.rb