dsl_compose 2.1.0 → 2.2.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: 535a5086604fa5366f7a9baf2cc1a1ef823d4f29f16d8fc273b78a680180c3e2
4
- data.tar.gz: 12f9bfc80a59ad1cbf61ad03795fd6e29535618c41143dbb7d8afb1228c7f3ae
3
+ metadata.gz: 409bc8ce86ea967b4a22f675ea029b609a9bb925d8b35255dd4f0a010049f115
4
+ data.tar.gz: e8d99aa8be417b6c670978dc5cf4461be8ca65dc4316b27ee840b63d82128738
5
5
  SHA512:
6
- metadata.gz: 768fbc012d89a3542f0c96934bfc9187098c790a4a7d975dff80ae63ba1e50c0eb19e8f8abddacf234410839fd1a5688eb76c30a51ed939485d219c67263c5f7
7
- data.tar.gz: 3553a51fc949265b84866761ce8ee5ed2ae4de67d2db17da36b4d646a21a5feb34b91f0085a3fcdfa54c5707457aa3cc0adc9c63d3da5820e042e372d5224603
6
+ metadata.gz: a57708bdfb09d3d35cfa3b3e6a76ffa931bcf71c34c14ece5b8737079f6660fefc9596ab5d079cfbf7fed3f733896212b60f81f23a02504dccbcd89a110e01c1
7
+ data.tar.gz: d87d67e5e6a1f22d49d0b8bb0642f97755f004684d8c0fa7ca1850ba3ce64e46dc016f3ab5fb21edd068e221734d2bae52c09e92f59135f94b0fabd7ac7d3356
data/CHANGELOG.md CHANGED
@@ -1,5 +1,19 @@
1
1
  # Changelog
2
2
 
3
+ ## [2.2.0](https://github.com/craigulliott/dsl_compose/compare/v2.1.1...v2.2.0) (2023-07-27)
4
+
5
+
6
+ ### Features
7
+
8
+ * Added a ReaderBase which can be extended to create custom reader classes. Also added dsl_used?, dsl_used_on_ancestors? and dsl_used_on_class_or_ancestors? helper methods to the Reader class ([6507e4a](https://github.com/craigulliott/dsl_compose/commit/6507e4a082c9a0d7e63c580a17bcb8eb2bd940ea))
9
+
10
+ ## [2.1.1](https://github.com/craigulliott/dsl_compose/compare/v2.1.0...v2.1.1) (2023-07-26)
11
+
12
+
13
+ ### Bug Fixes
14
+
15
+ * fixed bug where Execution classes were being returned from within the Reader but ExecutionReader classes were expected ([#56](https://github.com/craigulliott/dsl_compose/issues/56)) ([546cdaf](https://github.com/craigulliott/dsl_compose/commit/546cdaf323f3fc3be6ad09a47e5f99c3a59a50e2))
16
+
3
17
  ## [2.1.0](https://github.com/craigulliott/dsl_compose/compare/v2.0.1...v2.1.0) (2023-07-24)
4
18
 
5
19
 
data/README.md CHANGED
@@ -168,7 +168,7 @@ You can use `import_shared` anywhere within your DSL definition, you can even us
168
168
  Child classes can then use your new DSL
169
169
 
170
170
  ```ruby
171
- class Bar << Foo
171
+ class Bar < Foo
172
172
 
173
173
  your_dsl :first_dsl_argument, do
174
174
  your_method "first_method_argument", optional_argument: 123
@@ -239,7 +239,7 @@ A parser class can be used to process complicated DSLs. In the example below, a
239
239
 
240
240
  ```ruby
241
241
  # create your own parser by creating a new class which extends DSLCompose::Parser
242
- MyParser < DSLCompose::Parser
242
+ class MyParser < DSLCompose::Parser
243
243
  # `for_children_of` will process SomeBaseClass and yield the provided
244
244
  # block once for every class which extends SomeBaseClass.
245
245
  #
@@ -312,7 +312,7 @@ MyParser < DSLCompose::Parser
312
312
  end
313
313
  ```
314
314
 
315
- In addition to parser classes (or as a useful tool within parser classes) you can access the results of DSL execution via the class (or via a descendent of the class) where the DSL was originally defined.
315
+ In addition to parser classes (or as a useful tool within parser classes) you can access the results of DSL execution with a Reader class.
316
316
 
317
317
  ```ruby
318
318
  # Create a new Reader object.
@@ -339,10 +339,6 @@ reader = DSLCompose::Reader.new MyClass, :my_dsl
339
339
  # If no execution of the DSL is found, then nil will be returned
340
340
  execution = reader.last_execution
341
341
 
342
- # `execution.arguments` returns an ArgumentsReader object which allows access
343
- # via dot notation to to any argument values provided to the DSL
344
- execution.arguments.my_dsl_argument # returns the value provided for the argument, or nil
345
-
346
342
  # `execution.method_called?` will return true if the method with the provided
347
343
  # name was called, if a method with this name does exist, but was not called
348
344
  # then false will be returned. If a method with this name does not exist, then
@@ -372,7 +368,9 @@ execution.my_dsl_method.each do |arguments|
372
368
  end
373
369
 
374
370
  # Returns an array of ExecutionReaders to represent each time the DSL was used
375
- # on ancestors of the provided class.
371
+ # on the provided class.
372
+ executions = reader.executions
373
+
376
374
  # Returns an array of ExecutionReaders to represent each time the DSL was used
377
375
  # on the ancestors of the provided class, but not on the provided class itself.
378
376
  # The executions will be returned in the order they were executed, which is the
@@ -386,6 +384,106 @@ executions = reader.ancestor_executions
386
384
  # and if the DSL was used more than once on a class then the order they were used.
387
385
  executions = reader.all_executions
388
386
 
387
+ # Returns true if dsl has been executed once or more on the provided class,
388
+ # otherwise returns false.
389
+ was_used = reader.dsl_used?
390
+
391
+ # Returns true if dsl has been executed once or more on one of the ancestors
392
+ # of the provided class, otherwise returns false.
393
+ was_used = reader.dsl_used_on_ancestors?
394
+
395
+ # Returns true if dsl has been executed once or more on the provided class or
396
+ # any of its ancestors, otherwise returns false.
397
+ was_used = reader.dsl_used_on_class_or_ancestors?
398
+
399
+ # `execution.arguments` returns an ArgumentsReader object which allows access
400
+ # via dot notation to to any argument values provided to the DSL
401
+ execution.arguments.my_dsl_argument # returns the value provided for the argument, or nil
402
+ ```
403
+
404
+ A ReaderBase class is also provided. You can extend this class to create your own Reader objects and provide a much cleaner interface to your specific DSLs.
405
+
406
+ ```ruby
407
+ # This is an example DSL for configuring which database and schema should be used
408
+ # by classes which extend a BaseModel (it's from a hypothetical ORM).
409
+ class BaseModel
410
+ # define a DSL which can be used on descendants of this class
411
+ # to determine which database should be used
412
+ define_dsl :database_configuration do
413
+ requires :server_type, :symbol do
414
+ validate_in [:postgres, :mysql]
415
+ end
416
+ requires :server_name, :symbol
417
+ optional :database_name, :symbol
418
+ end
419
+ # define a DSL to set the schema which should be used
420
+ define_dsl :schema do
421
+ requires :schema_name, :symbol
422
+ end
423
+ end
424
+
425
+ # A hypothetical model which extends BaseModel and uses this DSL
426
+ class User < BaseModel
427
+ # Use the primary postgres server and the default database_name (because
428
+ # we are not using the optional `database_name: :foo` argument).
429
+ database_configuration :postgres, :primary
430
+ # Persist this models data in the `users` schema.
431
+ schema :users
432
+ end
433
+
434
+ # An example reader which could be created to provide a more concise
435
+ # interface to this DSL.
436
+ # Because this DSL Reader is named DatabaseConfigurationDSLReader it
437
+ # will automatically refer to the dsl named :database_configuration.
438
+ class DatabaseConfigurationDSLReader < DSLCompose::ReaderBase
439
+ # The three methods below provide a more concise access to the DSLs
440
+ # arguments. If the DSL was never used on the provided class, or any
441
+ # of it's ancestors, then an error will be raised. This error will
442
+ # be raised because we are using `last_execution!` instead of `last_execution`.
443
+ def server_type
444
+ last_execution!.arguments.server_type
445
+ end
446
+
447
+ def server_name
448
+ last_execution!.arguments.server_name
449
+ end
450
+
451
+ def database_name
452
+ last_execution!.arguments.database_name
453
+ end
454
+
455
+ # Returns true or false, depending on if the optional database_name
456
+ # attribute was used when executing the DSL. If the DSL was never
457
+ # used on the provided class, or any of it's ancestors, then an error will
458
+ # be raised (because of the bang method).
459
+ def has_database_name?
460
+ last_execution!.arguments.database_name != nil
461
+ end
462
+
463
+ # This method is provided as an example of how to reference other DSLs within
464
+ # your reader.
465
+ def schema_name
466
+ last_execution_of_schema&.arguments&.schema_name || :public
467
+ end
468
+
469
+ private
470
+
471
+ # This method is used by the schema_name method above, and is included as an
472
+ # example of how to reference other DSLs within your Reader.
473
+ def last_execution_of_schema
474
+ # Note that there is no bang method here (no "!" after last_execution), so it
475
+ # will return nil if the DSL was not used.
476
+ @schema_reader ||= DSLCompose::Reader.new(@base_class, :schema).last_execution
477
+ end
478
+ end
479
+
480
+ # Use our DatabaseConfigurationDSLReader when parsing this class to
481
+ # enjoy a more concise API for accessing our DSL values
482
+ reader = DatabaseConfigurationDSLReader.new(User)
483
+ reader.server_type # :postgres
484
+ reader.has_database_name? # false
485
+ reader.schema_name # :users
486
+
389
487
  ```
390
488
 
391
489
 
@@ -15,6 +15,8 @@ module DSLCompose
15
15
  class MethodDoesNotExist < StandardError
16
16
  end
17
17
 
18
+ attr_reader :execution
19
+
18
20
  def initialize execution
19
21
  raise InvalidExecution unless execution.is_a? Interpreter::Execution
20
22
  @execution = execution
@@ -11,6 +11,9 @@ module DSLCompose
11
11
  class DSLNotFound < StandardError
12
12
  end
13
13
 
14
+ class NoDSLExecutionFound < StandardError
15
+ end
16
+
14
17
  # given a class and the DSL name, finds and creates a reference to the DSL
15
18
  # and the ancestor class where the DSL was originally defined
16
19
  def initialize klass, dsl_name
@@ -42,6 +45,24 @@ module DSLCompose
42
45
  end
43
46
  end
44
47
 
48
+ # Returns true if dsl has been executed once or more on the provided class,
49
+ # otherwise returns false.
50
+ def dsl_used?
51
+ executions.any?
52
+ end
53
+
54
+ # Returns true if dsl has been executed once or more on one of the ancestors
55
+ # of the provided class, otherwise returns false.
56
+ def dsl_used_on_ancestors?
57
+ ancestor_executions.any?
58
+ end
59
+
60
+ # Returns true if dsl has been executed once or more on the provided class or
61
+ # any of its ancestors, otherwise returns false.
62
+ def dsl_used_on_class_or_ancestors?
63
+ all_executions.any?
64
+ end
65
+
45
66
  # Returns an ExecutionReader class which exposes a simple API to access the
46
67
  # arguments, methods and method arguments provided when using this DSL.
47
68
  #
@@ -51,13 +72,24 @@ module DSLCompose
51
72
  # and look for the last time it was executed on each ancestor.
52
73
  # If no execution of the DSL is found, then nil will be returned
53
74
  def last_execution
54
- @dsl_defining_class.dsls.get_last_dsl_execution @klass, @dsl_name
75
+ ExecutionReader.new(@dsl_defining_class.dsls.get_last_dsl_execution(@klass, @dsl_name))
76
+ end
77
+
78
+ # A wrapper for last_execution which raises an error if no execution exists
79
+ def last_execution!
80
+ execution = @dsl_defining_class.dsls.get_last_dsl_execution(@klass, @dsl_name)
81
+ if execution.nil?
82
+ raise NoDSLExecutionFound, "No execution of the `#{@dsl_name}` dsl was found on `#{@klass}` or any of its ancestors"
83
+ end
84
+ ExecutionReader.new execution
55
85
  end
56
86
 
57
87
  # Returns an array of ExecutionReaders to represent each time the DSL was used
58
88
  # on the provided class.
59
89
  def executions
60
- @dsl_defining_class.dsls.class_dsl_executions @klass, @dsl_name, true, false
90
+ @dsl_defining_class.dsls.class_dsl_executions(@klass, @dsl_name, true, false).map do |execution|
91
+ ExecutionReader.new execution
92
+ end
61
93
  end
62
94
 
63
95
  # Returns an array of ExecutionReaders to represent each time the DSL was used
@@ -66,7 +98,9 @@ module DSLCompose
66
98
  # earliest ancestor first and if the DSL was used more than once on a class then
67
99
  # the order they were used.
68
100
  def ancestor_executions
69
- @dsl_defining_class.dsls.class_dsl_executions @klass, @dsl_name, false, true
101
+ @dsl_defining_class.dsls.class_dsl_executions(@klass, @dsl_name, false, true).map do |execution|
102
+ ExecutionReader.new execution
103
+ end
70
104
  end
71
105
 
72
106
  # Returns an array of ExecutionReaders to represent each time the DSL was used
@@ -74,7 +108,9 @@ module DSLCompose
74
108
  # be returned in the order they were executed, which is the earliest ancestor first
75
109
  # and if the DSL was used more than once on a class then the order they were used.
76
110
  def all_executions
77
- @dsl_defining_class.dsls.class_dsl_executions @klass, @dsl_name, true, true
111
+ @dsl_defining_class.dsls.class_dsl_executions(@klass, @dsl_name, true, true).map do |execution|
112
+ ExecutionReader.new execution
113
+ end
78
114
  end
79
115
  end
80
116
  end
@@ -0,0 +1,64 @@
1
+ module DSLCompose
2
+ # This class provides a skeleton for creating your own DSL readers.
3
+ #
4
+ # All classes which extend from this base class must follow a strict naming convention. The class name
5
+ # must be the camelcased form of the DSL name, followed by the string "DSLReader". For example, a reader
6
+ # for a DLS named `:foo_bar` must have the class name `FooBarDSLReader`, as seen below
7
+ #
8
+ # class FooBarDSLReader < DSLCompose::ReaderBase
9
+ # # ...
10
+ # end
11
+ class ReaderBase
12
+ attr_reader :base_class
13
+ attr_reader :dsl_name
14
+ attr_reader :reader
15
+
16
+ def initialize base_class
17
+ @base_class = base_class
18
+ # get the DSL name from the class name
19
+ @dsl_name = dsl_name_from_class_name self.class
20
+ # create the reader
21
+ @reader = DSLCompose::Reader.new(base_class, @dsl_name)
22
+ end
23
+
24
+ private
25
+
26
+ def dsl_name_from_class_name reader_class
27
+ camelcased = reader_class.name.split("::").last.gsub(/DSLReader\Z/, "")
28
+ underscored = camelcased.gsub(/([A-Z]+)([A-Z][a-z])/, '\1_\2').gsub(/([a-z\d])([A-Z])/, '\1_\2')
29
+ underscored.downcase.to_sym
30
+ end
31
+
32
+ def dsl_used?
33
+ @reader.dsl_used?
34
+ end
35
+
36
+ def dsl_used_on_ancestors?
37
+ @reader.dsl_used_on_ancestors?
38
+ end
39
+
40
+ def dsl_used_on_class_or_ancestors?
41
+ @reader.dsl_used_on_class_or_ancestors?
42
+ end
43
+
44
+ def last_execution
45
+ @reader.last_execution
46
+ end
47
+
48
+ def last_execution!
49
+ @reader.last_execution!
50
+ end
51
+
52
+ def executions
53
+ @reader.executions
54
+ end
55
+
56
+ def ancestor_executions
57
+ @reader.ancestor_executions
58
+ end
59
+
60
+ def all_executions
61
+ @reader.all_executions
62
+ end
63
+ end
64
+ end
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module DSLCompose
4
- VERSION = "2.1.0"
4
+ VERSION = "2.2.0"
5
5
  end
data/lib/dsl_compose.rb CHANGED
@@ -32,6 +32,8 @@ require "dsl_compose/reader"
32
32
  require "dsl_compose/reader/execution_reader"
33
33
  require "dsl_compose/reader/execution_reader/arguments_reader"
34
34
 
35
+ require "dsl_compose/reader_base"
36
+
35
37
  require "dsl_compose/interpreter/execution/method_calls/method_call"
36
38
  require "dsl_compose/interpreter/execution/method_calls"
37
39
  require "dsl_compose/interpreter/execution/arguments"
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: 2.1.0
4
+ version: 2.2.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-24 00:00:00.000000000 Z
11
+ date: 2023-07-27 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: class_spec_helper
@@ -77,6 +77,7 @@ files:
77
77
  - lib/dsl_compose/reader.rb
78
78
  - lib/dsl_compose/reader/execution_reader.rb
79
79
  - lib/dsl_compose/reader/execution_reader/arguments_reader.rb
80
+ - lib/dsl_compose/reader_base.rb
80
81
  - lib/dsl_compose/shared_configuration.rb
81
82
  - lib/dsl_compose/version.rb
82
83
  homepage: