dsl_compose 2.1.0 → 2.2.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: 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: