dsl_compose 1.0.0 → 1.1.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/CHANGELOG.md +13 -0
- data/README.md +300 -3
- data/lib/dsl_compose/composer.rb +74 -0
- data/lib/dsl_compose/dsl/dsl_method/argument/equal_to_validation.rb +25 -0
- data/lib/dsl_compose/dsl/dsl_method/argument/format_validation.rb +25 -0
- data/lib/dsl_compose/dsl/dsl_method/argument/greater_than_or_equal_to_validation.rb +35 -0
- data/lib/dsl_compose/dsl/dsl_method/argument/greater_than_validation.rb +35 -0
- data/lib/dsl_compose/dsl/dsl_method/argument/in_validation.rb +35 -0
- data/lib/dsl_compose/dsl/dsl_method/argument/interpreter.rb +86 -0
- data/lib/dsl_compose/dsl/dsl_method/argument/length_validation.rb +42 -0
- data/lib/dsl_compose/dsl/dsl_method/argument/less_than_or_equal_to_validation.rb +35 -0
- data/lib/dsl_compose/dsl/dsl_method/argument/less_than_validation.rb +35 -0
- data/lib/dsl_compose/dsl/dsl_method/argument/not_in_validation.rb +35 -0
- data/lib/dsl_compose/dsl/dsl_method/argument.rb +299 -0
- data/lib/dsl_compose/dsl/dsl_method/interpreter.rb +57 -0
- data/lib/dsl_compose/dsl/dsl_method.rb +213 -0
- data/lib/dsl_compose/dsl/interpreter.rb +52 -0
- data/lib/dsl_compose/dsl.rb +148 -0
- data/lib/dsl_compose/dsls.rb +80 -0
- data/lib/dsl_compose/interpreter/execution/method_calls/method_call.rb +155 -0
- data/lib/dsl_compose/interpreter/execution/method_calls.rb +25 -0
- data/lib/dsl_compose/interpreter/execution.rb +60 -0
- data/lib/dsl_compose/interpreter.rb +43 -0
- data/lib/dsl_compose/version.rb +2 -2
- data/lib/dsl_compose.rb +32 -4
- metadata +24 -3
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: c6093d554121170f4ea168c0a5d0e6480befc3ae02f24396b2006534ac7252b2
|
4
|
+
data.tar.gz: 759d6dcf22ff3f049f29f0e4e5fffac72fd535cd1b4b0927934b94cba379ece0
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 2406e9b5a49d8764a4dc66b00489f58686cb2135980345cac72dbf451c5b97693cfae625153980dd2c124bf84a80b96c8aa59c924c275b36ae825a366194b194
|
7
|
+
data.tar.gz: 83bf9a8302f434adb63906e1abf07ffb3d56a3bcc9336465b0647710e42ff3b076be3d255df19aad6ff7a6f4a35fcbcec37aee4a3bf1f9d5b05ff767d07a377e
|
data/CHANGELOG.md
CHANGED
@@ -1,5 +1,18 @@
|
|
1
1
|
# Changelog
|
2
2
|
|
3
|
+
## [1.1.0](https://github.com/craigulliott/dsl_compose/compare/v1.0.0...v1.1.0) (2023-06-20)
|
4
|
+
|
5
|
+
|
6
|
+
### Features
|
7
|
+
|
8
|
+
* a first complete working version ([#3](https://github.com/craigulliott/dsl_compose/issues/3)) ([d64a666](https://github.com/craigulliott/dsl_compose/commit/d64a6663e5cedcf67af1c8cbad1736c2a7e81282))
|
9
|
+
* basic first version which includes internal DSL for composing dynamic DSLs ([37a3753](https://github.com/craigulliott/dsl_compose/commit/37a3753c68455bec579d879f4989f272480e2b06))
|
10
|
+
|
11
|
+
|
12
|
+
### Bug Fixes
|
13
|
+
|
14
|
+
* incorrect constant name used in version.rb ([a8cdd0b](https://github.com/craigulliott/dsl_compose/commit/a8cdd0b45fddf12fc1db8c0048c00e36cf183673))
|
15
|
+
|
3
16
|
## 1.0.0 (2023-06-09)
|
4
17
|
|
5
18
|
|
data/README.md
CHANGED
@@ -1,7 +1,21 @@
|
|
1
|
-
#
|
1
|
+
# DSLCompose
|
2
2
|
|
3
3
|
Ruby gem to add dynamic DSLs to classes
|
4
4
|
|
5
|
+
[![Gem Version](https://badge.fury.io/rb/dsl_compose.svg)](https://badge.fury.io/rb/dsl_compose)
|
6
|
+
[![Specs](https://github.com/craigulliott/dsl_compose/actions/workflows/specs.yml/badge.svg)](https://github.com/craigulliott/dsl_compose/actions/workflows/specs.yml)
|
7
|
+
[![Types](https://github.com/craigulliott/dsl_compose/actions/workflows/types.yml/badge.svg)](https://github.com/craigulliott/dsl_compose/actions/workflows/types.yml)
|
8
|
+
[![Coding Style](https://github.com/craigulliott/dsl_compose/actions/workflows/linter.yml/badge.svg)](https://github.com/craigulliott/dsl_compose/actions/workflows/linter.yml)
|
9
|
+
|
10
|
+
## Key Features
|
11
|
+
|
12
|
+
* Contains a simple internal DSL which is used to declare dynamic DSLs on your classes
|
13
|
+
* Takes special care not to pollute the namespace of classes where it is used
|
14
|
+
* Use of your declared DSLs is validated at run time
|
15
|
+
* Automatically generate documentation and instructions for your DSLs
|
16
|
+
* Complete test covereage
|
17
|
+
* Very lightweight and no external dependencies
|
18
|
+
|
5
19
|
## Installation
|
6
20
|
|
7
21
|
Install the gem and add to the application's Gemfile by executing:
|
@@ -16,13 +30,296 @@ If bundler is not being used to manage dependencies, install the gem by executin
|
|
16
30
|
|
17
31
|
DSLs are added to classes by including the DSLCompose module, and then calling the add_dsl singleton method within the class or a child class.
|
18
32
|
|
33
|
+
### Defining your DSL
|
34
|
+
|
35
|
+
```ruby
|
36
|
+
class Foo
|
37
|
+
include DSLCompose::Composer
|
38
|
+
|
39
|
+
# Define and name the DSL. Your DSL will available on this
|
40
|
+
# class and any children of this class.
|
41
|
+
define_dsl :my_dsl do
|
42
|
+
|
43
|
+
# A description of your DSL.
|
44
|
+
description <<-DESCRIPTION
|
45
|
+
Add a description of your DSL here, this description will be
|
46
|
+
used when generating the documentation for your DSL.
|
47
|
+
|
48
|
+
You can use **Markdown** in this description.
|
49
|
+
DESCRIPTION
|
50
|
+
|
51
|
+
# Define a method which will be available within your DSL. These
|
52
|
+
# methods will be exposed inside your DSL and can be called multiple times.
|
53
|
+
add_method :an_optional_method do
|
54
|
+
# You should provide descriptions for your methods. These descriptions will
|
55
|
+
# be used when generating your documentation. Both of these descriptions
|
56
|
+
# accept markdown
|
57
|
+
description "A description of my awesome method"
|
58
|
+
|
59
|
+
# add your method argument definition here
|
60
|
+
end
|
61
|
+
|
62
|
+
# Define a required within your DSL. If a class uses your DSL but
|
63
|
+
# does not execute this method then an error will be raised.
|
64
|
+
add_method :a_required_method, required: true do
|
65
|
+
# add your description and method argument definition here (see below)
|
66
|
+
end
|
67
|
+
|
68
|
+
# Define a method which can only be called once within your DSL. These
|
69
|
+
# methods will raise an error of they are called multiple times.
|
70
|
+
#
|
71
|
+
# There "unique" methods can be optionally marked as required.
|
72
|
+
add_unique_method :an_optional_method do
|
73
|
+
# add your description and method argument definition here (see below)
|
74
|
+
end
|
75
|
+
|
76
|
+
# Define a method in your DSL which takes arguments
|
77
|
+
add_method :my_method do
|
78
|
+
# A description of my DSL method
|
79
|
+
description "A description of my DSL method"
|
80
|
+
|
81
|
+
# You can add required arguments to your methods. The order in which you
|
82
|
+
# define these arguments determines the order of the arguments in your final DSL.
|
83
|
+
#
|
84
|
+
# Arguments are validated, and their expected type must be defined. Supported
|
85
|
+
# argument types are :integer, :boolean, :float, :string or :symbol
|
86
|
+
requires :my_first_argument, :symbol do
|
87
|
+
# You should provide descriptions for your arguments. These descriptions will
|
88
|
+
# be used when generating your documentation. This description supports markdown
|
89
|
+
description "A description of the first argument for this method"
|
90
|
+
end
|
91
|
+
|
92
|
+
# You can also add optional arguments to your DSL methods. All optional
|
93
|
+
# arguments must be added after required ones. An error will be raised if
|
94
|
+
# you define a required argument after an optional one.
|
95
|
+
optional :an_optional_argument, :integer do
|
96
|
+
description "A description of an optional argument"
|
97
|
+
|
98
|
+
# You can add validation to your arguments. A full list is provided later in this document
|
99
|
+
validate_greater_than 0
|
100
|
+
end
|
101
|
+
end
|
102
|
+
|
103
|
+
end
|
104
|
+
end
|
105
|
+
|
106
|
+
```
|
107
|
+
|
108
|
+
### Using your DSL
|
109
|
+
|
110
|
+
Child classes can then use your new DSL
|
111
|
+
|
112
|
+
```ruby
|
113
|
+
class Bar << Foo
|
114
|
+
|
115
|
+
my_dsl do
|
116
|
+
my_method my_first_argument, my_second_argument, optional_arg: optional_arg_value
|
117
|
+
end
|
118
|
+
|
119
|
+
end
|
120
|
+
```
|
121
|
+
|
122
|
+
## Examples
|
123
|
+
|
124
|
+
### Defining a DSL which can be used to configure a client library
|
125
|
+
|
126
|
+
Define the DSL
|
127
|
+
|
128
|
+
```ruby
|
129
|
+
class MyClientLibrary
|
130
|
+
include DSLCompose::Composer
|
131
|
+
|
132
|
+
define_dsl :configure do
|
133
|
+
description "Configure the settings for MyClientLibrary"
|
134
|
+
|
135
|
+
add_unique_method :api_key, required: true do
|
136
|
+
description <<-DESCRIPTION
|
137
|
+
Your API key.
|
138
|
+
|
139
|
+
API keys can be generated from your developer
|
140
|
+
portal at https://developer.example.com/keys
|
141
|
+
DESCRIPTION
|
142
|
+
|
143
|
+
requires :key, :string do
|
144
|
+
description "The api key"
|
145
|
+
validate_format /\A[a-z]{4}-[a-z0-9]{16}\Z/
|
146
|
+
end
|
147
|
+
end
|
148
|
+
|
149
|
+
add_method :log_provider, required: true do
|
150
|
+
description "Activate and configure a log provider for MyClientLibrary"
|
151
|
+
|
152
|
+
requires :provider, :symbol do
|
153
|
+
description "The log provider"
|
154
|
+
validate_in [:stdout, :rails]
|
155
|
+
end
|
156
|
+
|
157
|
+
optional :verbosity, :integer do
|
158
|
+
description "The log provider"
|
159
|
+
validate_greater_than_or_equal_to 0
|
160
|
+
validate_less_than_or_equal_to 3
|
161
|
+
end
|
162
|
+
end
|
163
|
+
|
164
|
+
end
|
165
|
+
end
|
166
|
+
```
|
167
|
+
|
168
|
+
Using the DSL
|
169
|
+
|
170
|
+
```ruby
|
171
|
+
MyClientLibrary.configure do
|
172
|
+
api_key "afdl-f1ifb2tslhzqwdis"
|
173
|
+
log_provider :stdout, verbosity: 1
|
174
|
+
log_provider :rails, verbosity: 3
|
175
|
+
end
|
176
|
+
```
|
177
|
+
|
178
|
+
## Argument validations
|
179
|
+
|
180
|
+
The following validations can be added to the arguments of your DSL methods. Validations can be added to both required and optional arguments, and you can add multiple validations to each argument.
|
181
|
+
|
182
|
+
### Numeric Attributes (:integer and :float)
|
183
|
+
|
184
|
+
```ruby
|
185
|
+
define_dsl :my_dsl do
|
186
|
+
add_method :my_method do
|
187
|
+
|
188
|
+
requires :my_first_argument, :integer do
|
189
|
+
# The argument must be greater than a provided number.
|
190
|
+
validate_greater_than 0
|
191
|
+
|
192
|
+
# The argument must be greater than or equal to a provided number.
|
193
|
+
validate_greater_than_or_equal_to 0
|
194
|
+
|
195
|
+
# The argument must be less than a provided number.
|
196
|
+
validate_less_than 10
|
197
|
+
|
198
|
+
# The argument must be less than or equal to a provided number.
|
199
|
+
validate_less_than_or_equal_to 10
|
200
|
+
|
201
|
+
# The argument must be exactly equal to a provided number.
|
202
|
+
validate_equal_to 5
|
203
|
+
|
204
|
+
# The argument must not be one of the provided values.
|
205
|
+
validate_not_in [1, 2]
|
206
|
+
|
207
|
+
# The argument must be one of the provided values.
|
208
|
+
validate_in [3, 4]
|
209
|
+
end
|
210
|
+
|
211
|
+
end
|
212
|
+
end
|
213
|
+
```
|
214
|
+
|
215
|
+
### String attributes (:string)
|
216
|
+
|
217
|
+
```ruby
|
218
|
+
define_dsl :my_dsl do
|
219
|
+
add_method :my_method do
|
220
|
+
|
221
|
+
requires :my_first_argument, :string do
|
222
|
+
|
223
|
+
# The text value must be at least a provided length.
|
224
|
+
validate_length minimum: 0
|
225
|
+
|
226
|
+
# The text value must be at most a provided length.
|
227
|
+
validate_length maximum: 10
|
228
|
+
|
229
|
+
# you can combine the minimum and maximum validations
|
230
|
+
validate_length minimum: 0, maximum: 10
|
231
|
+
|
232
|
+
# The length of the text must be exactly the provided value.
|
233
|
+
validate_length is: 10
|
234
|
+
|
235
|
+
# The argument must not be one of the provided values.
|
236
|
+
validate_not_in ["foo", "bar"]
|
237
|
+
|
238
|
+
# The argument must be one of the provided values.
|
239
|
+
validate_in ["cat", "dog"]
|
240
|
+
|
241
|
+
# The argument must match the provided regex.
|
242
|
+
validate_format /\A[A-Z][a-z]+\Z/
|
243
|
+
|
244
|
+
end
|
245
|
+
|
246
|
+
end
|
247
|
+
end
|
248
|
+
```
|
249
|
+
|
250
|
+
### Symbol attributes (:symbol)
|
251
|
+
|
252
|
+
```ruby
|
253
|
+
define_dsl :my_dsl do
|
254
|
+
add_method :my_method do
|
255
|
+
|
256
|
+
requires :my_first_argument, :symbol do
|
257
|
+
|
258
|
+
# The text value must be at least a provided length.
|
259
|
+
validate_length minimum: 0
|
260
|
+
|
261
|
+
# The text value must be at most a provided length.
|
262
|
+
validate_length maximum: 10
|
263
|
+
|
264
|
+
# you can combine the minimum and maximum validations
|
265
|
+
validate_length minimum: 0, maximum: 10
|
266
|
+
|
267
|
+
# The length of the text must be exactly the provided value.
|
268
|
+
validate_length is: 10
|
269
|
+
|
270
|
+
# The argument must not be one of the provided values.
|
271
|
+
validate_not_in [:foo, :bar]
|
272
|
+
|
273
|
+
# The argument must be one of the provided values.
|
274
|
+
validate_in [:cat, :dog]
|
275
|
+
|
276
|
+
# The argument must match the provided regex.
|
277
|
+
validate_format /\A[A-Z][a-z]+\Z/
|
278
|
+
|
279
|
+
end
|
280
|
+
|
281
|
+
end
|
282
|
+
end
|
283
|
+
```
|
284
|
+
|
285
|
+
### Boolean attributes (:boolean)
|
286
|
+
|
287
|
+
```ruby
|
288
|
+
define_dsl :my_dsl do
|
289
|
+
add_method :my_method do
|
290
|
+
|
291
|
+
requires :my_first_argument, :boolean do
|
292
|
+
|
293
|
+
# The argument must be equal to a provided value
|
294
|
+
# for boolean attributes, this provided value can be either false or true.
|
295
|
+
|
296
|
+
# The provided attribute must be `false`.
|
297
|
+
validate_equal_to false
|
298
|
+
|
299
|
+
end
|
300
|
+
|
301
|
+
end
|
302
|
+
end
|
303
|
+
```
|
304
|
+
|
305
|
+
|
19
306
|
## Development
|
20
307
|
|
21
308
|
After checking out the repo, run `bin/setup` to install dependencies. Then, run `rake spec` to run the tests. You can also run `bin/console` for an interactive prompt that will allow you to experiment.
|
22
309
|
|
23
310
|
We use [Conventional Commit Messages](https://gist.github.com/qoomon/5dfcdf8eec66a051ecd85625518cfd13).
|
24
311
|
|
25
|
-
|
312
|
+
Code should be linted and formatted according to [Ruby Standard](https://github.com/standardrb/standard).
|
313
|
+
|
314
|
+
Publishing is automated via github actions and Googles [Release Please](https://github.com/google-github-actions/release-please-action) github action
|
315
|
+
|
316
|
+
We prefer using squash-merges when merging pull requests because it helps keep a linear git history and allows more fine grained control of commit messages which get sent to release-please and ultimately show up in the changelog.
|
317
|
+
|
318
|
+
Type checking is enabled for this project. You can find the corresponding `rbs` files in the sig folder.
|
319
|
+
|
320
|
+
Install types for the packages used in development (such as `rspec`) by running
|
321
|
+
|
322
|
+
$ rbs collection install
|
26
323
|
|
27
324
|
## Contributing
|
28
325
|
|
@@ -34,4 +331,4 @@ The gem is available as open source under the terms of the [MIT License](https:/
|
|
34
331
|
|
35
332
|
## Code of Conduct
|
36
333
|
|
37
|
-
Everyone interacting in the
|
334
|
+
Everyone interacting in the DSLCompose project's codebases, issue trackers, chat rooms and mailing lists is expected to follow the [code of conduct](https://github.com/craigulliott/dsl_compose/blob/master/CODE_OF_CONDUCT.md).
|
@@ -0,0 +1,74 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module DSLCompose
|
4
|
+
module Composer
|
5
|
+
class ComposerAlreadyInstalledError < StandardError
|
6
|
+
def message
|
7
|
+
"This module has already been included or the define_dsl singleton method already exists for this class."
|
8
|
+
end
|
9
|
+
end
|
10
|
+
|
11
|
+
class MethodAlreadyExistsWithThisDSLNameError < StandardError
|
12
|
+
def message
|
13
|
+
"The define_dsl singleton method already exists for this class."
|
14
|
+
end
|
15
|
+
end
|
16
|
+
|
17
|
+
class GetDSLExecutionResultsMethodAlreadyExistsError < StandardError
|
18
|
+
def message
|
19
|
+
"The dsl_interpreter singleton method already exists for this class."
|
20
|
+
end
|
21
|
+
end
|
22
|
+
|
23
|
+
def self.included klass
|
24
|
+
if (klass.private_methods + klass.methods).include? :define_dsl
|
25
|
+
raise ComposerAlreadyInstalledError
|
26
|
+
end
|
27
|
+
|
28
|
+
# create an interpreter for this class which will be shared by all child classes and all
|
29
|
+
# the dynamicly defined DSLs in this class
|
30
|
+
interpreter = DSLCompose::Interpreter.new
|
31
|
+
|
32
|
+
# return a specific DSL which is defined for this class
|
33
|
+
if klass.respond_to? :dsl_interpreter
|
34
|
+
raise GetDSLExecutionResultsMethodAlreadyExistsError
|
35
|
+
end
|
36
|
+
|
37
|
+
klass.define_singleton_method :dsl_interpreter do
|
38
|
+
interpreter
|
39
|
+
end
|
40
|
+
|
41
|
+
# return a specific DSL which is defined for this class
|
42
|
+
klass.define_singleton_method :define_dsl do |name, &block|
|
43
|
+
# Find or create a DSL with this name for the provided class. We allow an existing
|
44
|
+
# DSL to be accessed so that it can be be extended (have new methods added to it).
|
45
|
+
# `self` here is the class in which `define_dsl` is being called
|
46
|
+
if DSLCompose::DSLs.class_dsl_exists? self, name
|
47
|
+
# get the existing DSL
|
48
|
+
dsl = DSLCompose::DSLs.class_dsl self, name
|
49
|
+
|
50
|
+
else
|
51
|
+
dsl = DSLCompose::DSLs.create_dsl self, name
|
52
|
+
|
53
|
+
# ensure that creating this DSL will not override any existing methods on this class
|
54
|
+
if respond_to? name
|
55
|
+
raise MethodAlreadyExistsWithThisDSLNameError
|
56
|
+
end
|
57
|
+
|
58
|
+
# add a singleton method with the name of this new DSL onto our class, this is how our new DSL will be accessed
|
59
|
+
define_singleton_method name do |&block|
|
60
|
+
# when it is called, we process this new dynamic DSL with the interpreter
|
61
|
+
# `self` here is the class in which the dsl is being used, not the class in which the DSL was defined
|
62
|
+
interpreter.execute_dsl self, dsl, &block
|
63
|
+
end
|
64
|
+
|
65
|
+
end
|
66
|
+
|
67
|
+
# evaluate the configuration block which uses our internal DSL to define this dynamic DSL
|
68
|
+
if block
|
69
|
+
dsl.evaluate_configuration_block(&block)
|
70
|
+
end
|
71
|
+
end
|
72
|
+
end
|
73
|
+
end
|
74
|
+
end
|
@@ -0,0 +1,25 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module DSLCompose
|
4
|
+
class DSL
|
5
|
+
class DSLMethod
|
6
|
+
class Argument
|
7
|
+
class EqualToValidation
|
8
|
+
class ValidationFailedError < StandardError
|
9
|
+
def message
|
10
|
+
"The argument is invalid"
|
11
|
+
end
|
12
|
+
end
|
13
|
+
|
14
|
+
def initialize value
|
15
|
+
@value = value
|
16
|
+
end
|
17
|
+
|
18
|
+
def validate! value
|
19
|
+
raise ValidationFailedError unless value == @value
|
20
|
+
end
|
21
|
+
end
|
22
|
+
end
|
23
|
+
end
|
24
|
+
end
|
25
|
+
end
|
@@ -0,0 +1,25 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module DSLCompose
|
4
|
+
class DSL
|
5
|
+
class DSLMethod
|
6
|
+
class Argument
|
7
|
+
class FormatValidation
|
8
|
+
class ValidationFailedError < StandardError
|
9
|
+
def message
|
10
|
+
"The argument is invalid"
|
11
|
+
end
|
12
|
+
end
|
13
|
+
|
14
|
+
def initialize regex
|
15
|
+
@regex = regex
|
16
|
+
end
|
17
|
+
|
18
|
+
def validate! value
|
19
|
+
raise ValidationFailedError if @regex.match(value).nil?
|
20
|
+
end
|
21
|
+
end
|
22
|
+
end
|
23
|
+
end
|
24
|
+
end
|
25
|
+
end
|
@@ -0,0 +1,35 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module DSLCompose
|
4
|
+
class DSL
|
5
|
+
class DSLMethod
|
6
|
+
class Argument
|
7
|
+
class GreaterThanOrEqualToValidation
|
8
|
+
class InvalidValueError < StandardError
|
9
|
+
def message
|
10
|
+
"The value provided to validate_greater_than must be a number"
|
11
|
+
end
|
12
|
+
end
|
13
|
+
|
14
|
+
class ValidationFailedError < StandardError
|
15
|
+
def message
|
16
|
+
"The argument is invalid"
|
17
|
+
end
|
18
|
+
end
|
19
|
+
|
20
|
+
def initialize value
|
21
|
+
unless value.is_a?(Numeric)
|
22
|
+
raise InvalidValueError
|
23
|
+
end
|
24
|
+
|
25
|
+
@value = value
|
26
|
+
end
|
27
|
+
|
28
|
+
def validate! value
|
29
|
+
raise ValidationFailedError unless value >= @value
|
30
|
+
end
|
31
|
+
end
|
32
|
+
end
|
33
|
+
end
|
34
|
+
end
|
35
|
+
end
|
@@ -0,0 +1,35 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module DSLCompose
|
4
|
+
class DSL
|
5
|
+
class DSLMethod
|
6
|
+
class Argument
|
7
|
+
class GreaterThanValidation
|
8
|
+
class InvalidValueError < StandardError
|
9
|
+
def message
|
10
|
+
"The value provided to validate_greater_than must be a number"
|
11
|
+
end
|
12
|
+
end
|
13
|
+
|
14
|
+
class ValidationFailedError < StandardError
|
15
|
+
def message
|
16
|
+
"The argument is invalid"
|
17
|
+
end
|
18
|
+
end
|
19
|
+
|
20
|
+
def initialize value
|
21
|
+
unless value.is_a?(Numeric)
|
22
|
+
raise InvalidValueError
|
23
|
+
end
|
24
|
+
|
25
|
+
@value = value
|
26
|
+
end
|
27
|
+
|
28
|
+
def validate! value
|
29
|
+
raise ValidationFailedError unless value > @value
|
30
|
+
end
|
31
|
+
end
|
32
|
+
end
|
33
|
+
end
|
34
|
+
end
|
35
|
+
end
|
@@ -0,0 +1,35 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module DSLCompose
|
4
|
+
class DSL
|
5
|
+
class DSLMethod
|
6
|
+
class Argument
|
7
|
+
class InValidation
|
8
|
+
class InvalidValueError < StandardError
|
9
|
+
def message
|
10
|
+
"The value provided to validate_greater_than must be a number"
|
11
|
+
end
|
12
|
+
end
|
13
|
+
|
14
|
+
class ValidationFailedError < StandardError
|
15
|
+
def message
|
16
|
+
"The argument is invalid"
|
17
|
+
end
|
18
|
+
end
|
19
|
+
|
20
|
+
def initialize values
|
21
|
+
unless values.is_a?(Array)
|
22
|
+
raise InvalidValueError
|
23
|
+
end
|
24
|
+
|
25
|
+
@values = values
|
26
|
+
end
|
27
|
+
|
28
|
+
def validate! value
|
29
|
+
raise ValidationFailedError unless @values.include? value
|
30
|
+
end
|
31
|
+
end
|
32
|
+
end
|
33
|
+
end
|
34
|
+
end
|
35
|
+
end
|
@@ -0,0 +1,86 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module DSLCompose
|
4
|
+
class DSL
|
5
|
+
class DSLMethod
|
6
|
+
class Argument
|
7
|
+
# This class is reponsible for parsing and executing argument definitions
|
8
|
+
# within our internal DSL. These argument definitions determine what arguments
|
9
|
+
# will be available on the methods within our new dynamic DSL.
|
10
|
+
#
|
11
|
+
# This class is instantaited by the DSLCompose::DSL::DSLMethod::Argument class and the
|
12
|
+
# method argument definition part of our internal DSL is evaluated by passing a block
|
13
|
+
# to `instance_eval` on this class.
|
14
|
+
#
|
15
|
+
# An example of our internal DSL which includes a method definition with complex arguments:
|
16
|
+
# define_dsl :my_dsl do
|
17
|
+
# add_method :my_method, required: true do
|
18
|
+
# description "Description of my method"
|
19
|
+
# optional :my_optional_argument, :string do
|
20
|
+
# description "A description of this argument"
|
21
|
+
# validate_greater_than 10
|
22
|
+
# end
|
23
|
+
# optional :my_optional_arg, String
|
24
|
+
# end
|
25
|
+
# end
|
26
|
+
class Interpreter
|
27
|
+
def initialize argument
|
28
|
+
@argument = argument
|
29
|
+
end
|
30
|
+
|
31
|
+
private
|
32
|
+
|
33
|
+
# sets the description of the Argument
|
34
|
+
def description description
|
35
|
+
@argument.set_description description
|
36
|
+
end
|
37
|
+
|
38
|
+
# adds a greater_than validator to the argument
|
39
|
+
def validate_greater_than value
|
40
|
+
@argument.validate_greater_than value
|
41
|
+
end
|
42
|
+
|
43
|
+
# adds a greater_than_or_equal_to validator to the argument
|
44
|
+
def validate_greater_than_or_equal_to value
|
45
|
+
@argument.validate_greater_than_or_equal_to value
|
46
|
+
end
|
47
|
+
|
48
|
+
# adds a less_than validator to the argument
|
49
|
+
def validate_less_than value
|
50
|
+
@argument.validate_less_than value
|
51
|
+
end
|
52
|
+
|
53
|
+
# adds a less_than_or_equal_to validator to the argument
|
54
|
+
def validate_less_than_or_equal_to value
|
55
|
+
@argument.validate_less_than_or_equal_to value
|
56
|
+
end
|
57
|
+
|
58
|
+
# adds a equal_to validator to the argument
|
59
|
+
def validate_equal_to value
|
60
|
+
@argument.validate_equal_to value
|
61
|
+
end
|
62
|
+
|
63
|
+
# adds a length validator to the argument
|
64
|
+
def validate_length minimum: nil, maximum: nil, is: nil
|
65
|
+
@argument.validate_length minimum: minimum, maximum: maximum, is: is
|
66
|
+
end
|
67
|
+
|
68
|
+
# adds a not_in validator to the argument
|
69
|
+
def validate_not_in values
|
70
|
+
@argument.validate_not_in values
|
71
|
+
end
|
72
|
+
|
73
|
+
# adds a in validator to the argument
|
74
|
+
def validate_in values
|
75
|
+
@argument.validate_in values
|
76
|
+
end
|
77
|
+
|
78
|
+
# adds a format validator to the argument
|
79
|
+
def validate_format regexp
|
80
|
+
@argument.validate_format regexp
|
81
|
+
end
|
82
|
+
end
|
83
|
+
end
|
84
|
+
end
|
85
|
+
end
|
86
|
+
end
|