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
@@ -0,0 +1,42 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module DSLCompose
|
4
|
+
class DSL
|
5
|
+
class DSLMethod
|
6
|
+
class Argument
|
7
|
+
class LengthValidation
|
8
|
+
class ValidationFailedError < StandardError
|
9
|
+
def message
|
10
|
+
"The argument is invalid"
|
11
|
+
end
|
12
|
+
end
|
13
|
+
|
14
|
+
def initialize maximum: nil, minimum: nil, is: nil
|
15
|
+
@maximum = maximum
|
16
|
+
@minimum = minimum
|
17
|
+
@is = is
|
18
|
+
end
|
19
|
+
|
20
|
+
def validate! value
|
21
|
+
maximum = @maximum
|
22
|
+
unless maximum.nil?
|
23
|
+
raise ValidationFailedError if value.length > maximum
|
24
|
+
end
|
25
|
+
|
26
|
+
minimum = @minimum
|
27
|
+
unless minimum.nil?
|
28
|
+
raise ValidationFailedError if value.length < minimum
|
29
|
+
end
|
30
|
+
|
31
|
+
is = @is
|
32
|
+
unless is.nil?
|
33
|
+
raise ValidationFailedError if value.length != is
|
34
|
+
end
|
35
|
+
|
36
|
+
true
|
37
|
+
end
|
38
|
+
end
|
39
|
+
end
|
40
|
+
end
|
41
|
+
end
|
42
|
+
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 LessThanOrEqualToValidation
|
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 LessThanValidation
|
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 NotInValidation
|
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 if @values.include?(value)
|
30
|
+
end
|
31
|
+
end
|
32
|
+
end
|
33
|
+
end
|
34
|
+
end
|
35
|
+
end
|
@@ -0,0 +1,299 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module DSLCompose
|
4
|
+
class DSL
|
5
|
+
class DSLMethod
|
6
|
+
class Argument
|
7
|
+
class ValidationIncompatibleError < StandardError
|
8
|
+
def message
|
9
|
+
"The validation is not compatible with this argument type"
|
10
|
+
end
|
11
|
+
end
|
12
|
+
|
13
|
+
class ValidationAlreadyExistsError < StandardError
|
14
|
+
def message
|
15
|
+
"This validation has already been applied to this method option."
|
16
|
+
end
|
17
|
+
end
|
18
|
+
|
19
|
+
class InvalidTypeError < StandardError
|
20
|
+
def message
|
21
|
+
"Argument type must be one of :integer, :boolean, :float, :string or :symbol"
|
22
|
+
end
|
23
|
+
end
|
24
|
+
|
25
|
+
class InvalidNameError < StandardError
|
26
|
+
def message
|
27
|
+
"The option name is invalid, it must be of type symbol."
|
28
|
+
end
|
29
|
+
end
|
30
|
+
|
31
|
+
class InvalidDescriptionError < StandardError
|
32
|
+
def message
|
33
|
+
"The option description is invalid, it must be of type string and have length greater than 0."
|
34
|
+
end
|
35
|
+
end
|
36
|
+
|
37
|
+
class DescriptionAlreadyExistsError < StandardError
|
38
|
+
def message
|
39
|
+
"The description has already been set"
|
40
|
+
end
|
41
|
+
end
|
42
|
+
|
43
|
+
# The name of this Argument.
|
44
|
+
attr_reader :name
|
45
|
+
# An arguments type. This determines what kind of value can be passed when calling the
|
46
|
+
# associated DSLMethod.
|
47
|
+
# `type` should be set to either :integer, :boolean, :float, :string or :symbol
|
48
|
+
attr_reader :type
|
49
|
+
# if required, then this Argument must be provided when calling its associated DSLMethod.
|
50
|
+
attr_reader :required
|
51
|
+
# An otional description of this Attribute, if provided then it must be a string.
|
52
|
+
# The description accepts markdown and is used when generating documentation.
|
53
|
+
attr_reader :description
|
54
|
+
# Optional validations that have been applied to this Argument. When the DSL is used
|
55
|
+
# each of these validations will be checked against the value provided to
|
56
|
+
# this Argument.
|
57
|
+
attr_reader :greater_than_validation
|
58
|
+
attr_reader :greater_than_or_equal_to_validation
|
59
|
+
attr_reader :less_than_validation
|
60
|
+
attr_reader :less_than_or_equal_to_validation
|
61
|
+
attr_reader :format_validation
|
62
|
+
attr_reader :equal_to_validation
|
63
|
+
attr_reader :in_validation
|
64
|
+
attr_reader :not_in_validation
|
65
|
+
attr_reader :length_validation
|
66
|
+
|
67
|
+
# Create a new Attribute object.
|
68
|
+
#
|
69
|
+
# `name` must be a symbol.
|
70
|
+
# `required` is a boolean which determines if this Attribute must be provided when
|
71
|
+
# calling its associated DSLMethod.
|
72
|
+
# `type` can be either :integer, :boolean, :float, :string or :symbol
|
73
|
+
# `block` contains the instructions to further configure this Attribute
|
74
|
+
def initialize name, required, type, &block
|
75
|
+
if name.is_a? Symbol
|
76
|
+
@name = name
|
77
|
+
else
|
78
|
+
raise InvalidNameError
|
79
|
+
end
|
80
|
+
|
81
|
+
if type == :integer || type == :boolean || type == :float || type == :string || type == :symbol
|
82
|
+
@type = type
|
83
|
+
else
|
84
|
+
raise InvalidTypeError
|
85
|
+
end
|
86
|
+
|
87
|
+
@required = required ? true : false
|
88
|
+
|
89
|
+
# If a block was provided, then we evaluate it using a seperate
|
90
|
+
# interpreter class. We do this because the interpreter class contains
|
91
|
+
# no other methods or variables, if it was evaluated in the context of
|
92
|
+
# this class then the block would have access to all of the methods defined
|
93
|
+
# in here.
|
94
|
+
if block
|
95
|
+
Interpreter.new(self).instance_eval(&block)
|
96
|
+
end
|
97
|
+
end
|
98
|
+
|
99
|
+
# Set the description for this Argument to the provided value.
|
100
|
+
#
|
101
|
+
# `description` must be a string with a length greater than 0.
|
102
|
+
# The `description` can only be set once per Argument
|
103
|
+
def set_description description
|
104
|
+
unless description.is_a?(String) && description.length > 0
|
105
|
+
raise InvalidDescriptionError
|
106
|
+
end
|
107
|
+
|
108
|
+
if has_description?
|
109
|
+
raise DescriptionAlreadyExistsError
|
110
|
+
end
|
111
|
+
|
112
|
+
@description = description
|
113
|
+
end
|
114
|
+
|
115
|
+
# Returns `true` if this DSL has a description, else false.
|
116
|
+
def has_description?
|
117
|
+
@description.nil? == false
|
118
|
+
end
|
119
|
+
|
120
|
+
# returns true if this DSLMethod is flagged as required, otherwise returns false.
|
121
|
+
def required?
|
122
|
+
@required == true
|
123
|
+
end
|
124
|
+
|
125
|
+
# returns true if this DSLMethod is flagged as optional, otherwise returns false.
|
126
|
+
def optional?
|
127
|
+
@required == false
|
128
|
+
end
|
129
|
+
|
130
|
+
def validate_greater_than value
|
131
|
+
if @greater_than_validation
|
132
|
+
raise ValidationAlreadyExistsError
|
133
|
+
end
|
134
|
+
|
135
|
+
unless @type == :integer || @type == :float
|
136
|
+
raise ValidationIncompatibleError
|
137
|
+
end
|
138
|
+
|
139
|
+
@greater_than_validation = GreaterThanValidation.new value
|
140
|
+
end
|
141
|
+
|
142
|
+
def validate_greater_than_or_equal_to value
|
143
|
+
if @greater_than_or_equal_to_validation
|
144
|
+
raise ValidationAlreadyExistsError
|
145
|
+
end
|
146
|
+
|
147
|
+
unless value.is_a?(Numeric)
|
148
|
+
raise ValidationInvalidArgumentError
|
149
|
+
end
|
150
|
+
|
151
|
+
unless @type == :integer || @type == :float
|
152
|
+
raise ValidationIncompatibleError
|
153
|
+
end
|
154
|
+
|
155
|
+
@greater_than_or_equal_to_validation = GreaterThanOrEqualToValidation.new value
|
156
|
+
end
|
157
|
+
|
158
|
+
def validate_less_than value
|
159
|
+
if @less_than_validation
|
160
|
+
raise ValidationAlreadyExistsError
|
161
|
+
end
|
162
|
+
|
163
|
+
unless value.is_a?(Numeric)
|
164
|
+
raise ValidationInvalidArgumentError
|
165
|
+
end
|
166
|
+
|
167
|
+
unless @type == :integer || @type == :float
|
168
|
+
raise ValidationIncompatibleError
|
169
|
+
end
|
170
|
+
|
171
|
+
@less_than_validation = LessThanValidation.new value
|
172
|
+
end
|
173
|
+
|
174
|
+
def validate_less_than_or_equal_to value
|
175
|
+
if @less_than_or_equal_to_validation
|
176
|
+
raise ValidationAlreadyExistsError
|
177
|
+
end
|
178
|
+
|
179
|
+
unless value.is_a?(Numeric)
|
180
|
+
raise ValidationInvalidArgumentError
|
181
|
+
end
|
182
|
+
|
183
|
+
unless @type == :integer || @type == :float
|
184
|
+
raise ValidationIncompatibleError
|
185
|
+
end
|
186
|
+
|
187
|
+
@less_than_or_equal_to_validation = LessThanOrEqualToValidation.new value
|
188
|
+
end
|
189
|
+
|
190
|
+
def validate_format regexp
|
191
|
+
if @format_validation
|
192
|
+
raise ValidationAlreadyExistsError
|
193
|
+
end
|
194
|
+
|
195
|
+
unless regexp.is_a? Regexp
|
196
|
+
raise ValidationInvalidArgumentError
|
197
|
+
end
|
198
|
+
|
199
|
+
unless @type == :string || @type == :symbol
|
200
|
+
raise ValidationIncompatibleError
|
201
|
+
end
|
202
|
+
@format_validation = FormatValidation.new regexp
|
203
|
+
end
|
204
|
+
|
205
|
+
def validate_equal_to value
|
206
|
+
if @equal_to_validation
|
207
|
+
raise ValidationAlreadyExistsError
|
208
|
+
end
|
209
|
+
|
210
|
+
unless @type == :integer || @type == :float || @type == :string || @type == :symbol || @type == :boolean
|
211
|
+
raise ValidationIncompatibleError
|
212
|
+
end
|
213
|
+
|
214
|
+
@equal_to_validation = EqualToValidation.new value
|
215
|
+
end
|
216
|
+
|
217
|
+
def validate_in values
|
218
|
+
if @in_validation
|
219
|
+
raise ValidationAlreadyExistsError
|
220
|
+
end
|
221
|
+
|
222
|
+
unless values.is_a? Array
|
223
|
+
raise ValidationInvalidArgumentError
|
224
|
+
end
|
225
|
+
|
226
|
+
unless @type == :integer || @type == :float || @type == :string || @type == :symbol
|
227
|
+
raise ValidationIncompatibleError
|
228
|
+
end
|
229
|
+
|
230
|
+
@in_validation = InValidation.new values
|
231
|
+
end
|
232
|
+
|
233
|
+
def validate_not_in values
|
234
|
+
if @not_in_validation
|
235
|
+
raise ValidationAlreadyExistsError
|
236
|
+
end
|
237
|
+
|
238
|
+
unless values.is_a? Array
|
239
|
+
raise ValidationInvalidArgumentError
|
240
|
+
end
|
241
|
+
|
242
|
+
unless @type == :integer || @type == :float || @type == :string || @type == :symbol
|
243
|
+
raise ValidationIncompatibleError
|
244
|
+
end
|
245
|
+
|
246
|
+
@not_in_validation = NotInValidation.new values
|
247
|
+
end
|
248
|
+
|
249
|
+
def validate_length maximum: nil, minimum: nil, is: nil
|
250
|
+
if @length_validation
|
251
|
+
raise ValidationAlreadyExistsError
|
252
|
+
end
|
253
|
+
|
254
|
+
unless @type == :string || @type == :symbol
|
255
|
+
raise ValidationIncompatibleError
|
256
|
+
end
|
257
|
+
|
258
|
+
@length_validation = LengthValidation.new(maximum: maximum, minimum: minimum, is: is)
|
259
|
+
end
|
260
|
+
|
261
|
+
# returns true if every provided integer validation also returns true
|
262
|
+
def validate_integer! value
|
263
|
+
(greater_than_validation.nil? || greater_than_validation.validate!(value)) &&
|
264
|
+
(greater_than_or_equal_to_validation.nil? || greater_than_or_equal_to_validation.validate!(value)) &&
|
265
|
+
(less_than_validation.nil? || less_than_validation.validate!(value)) &&
|
266
|
+
(less_than_or_equal_to_validation.nil? || less_than_or_equal_to_validation.validate!(value)) &&
|
267
|
+
(format_validation.nil? || format_validation.validate!(value)) &&
|
268
|
+
(equal_to_validation.nil? || equal_to_validation.validate!(value)) &&
|
269
|
+
(in_validation.nil? || in_validation.validate!(value)) &&
|
270
|
+
(not_in_validation.nil? || not_in_validation.validate!(value)) &&
|
271
|
+
(length_validation.nil? || length_validation.validate!(value))
|
272
|
+
end
|
273
|
+
|
274
|
+
# returns true if every provided symbol validation also returns true
|
275
|
+
def validate_symbol! value
|
276
|
+
(format_validation.nil? || format_validation.validate!(value)) &&
|
277
|
+
(equal_to_validation.nil? || equal_to_validation.validate!(value)) &&
|
278
|
+
(in_validation.nil? || in_validation.validate!(value)) &&
|
279
|
+
(not_in_validation.nil? || not_in_validation.validate!(value)) &&
|
280
|
+
(length_validation.nil? || length_validation.validate!(value))
|
281
|
+
end
|
282
|
+
|
283
|
+
# returns true if every provided string validation also returns true
|
284
|
+
def validate_string! value
|
285
|
+
(format_validation.nil? || format_validation.validate!(value)) &&
|
286
|
+
(equal_to_validation.nil? || equal_to_validation.validate!(value)) &&
|
287
|
+
(in_validation.nil? || in_validation.validate!(value)) &&
|
288
|
+
(not_in_validation.nil? || not_in_validation.validate!(value)) &&
|
289
|
+
(length_validation.nil? || length_validation.validate!(value))
|
290
|
+
end
|
291
|
+
|
292
|
+
# returns true if every provided boolean validation also returns true
|
293
|
+
def validate_boolean! value
|
294
|
+
(equal_to_validation.nil? || equal_to_validation.validate!(value))
|
295
|
+
end
|
296
|
+
end
|
297
|
+
end
|
298
|
+
end
|
299
|
+
end
|
@@ -0,0 +1,57 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module DSLCompose
|
4
|
+
class DSL
|
5
|
+
class DSLMethod
|
6
|
+
# This class is reponsible for parsing and executing method definitions
|
7
|
+
# within our internal DSL. These method definitions determine what methods
|
8
|
+
# will be available within our new dynamic DSL.
|
9
|
+
#
|
10
|
+
# This class is instantaited by the DSLCompose::DSL::DSLMethod class and the method definition
|
11
|
+
# part of our internal DSL is evaluated by passing a block to `instance_eval` on this class.
|
12
|
+
#
|
13
|
+
# An example of our internal DSL which includes a complex method definition:
|
14
|
+
# define_dsl :my_dsl do
|
15
|
+
# add_method :my_method, required: true do
|
16
|
+
# description "Description of my method"
|
17
|
+
# optional :my_optional_argument, :string do
|
18
|
+
# # ...
|
19
|
+
# end
|
20
|
+
# optional :my_optional_arg, String
|
21
|
+
# end
|
22
|
+
# end
|
23
|
+
class Interpreter
|
24
|
+
def initialize dsl_method
|
25
|
+
@dsl_method = dsl_method
|
26
|
+
end
|
27
|
+
|
28
|
+
private
|
29
|
+
|
30
|
+
# sets the description of the DSLMethod
|
31
|
+
def description description
|
32
|
+
@dsl_method.set_description description
|
33
|
+
end
|
34
|
+
|
35
|
+
# adds a new optional argument to the DSLMethod
|
36
|
+
#
|
37
|
+
# name must be a symbol
|
38
|
+
# `type` can be either :integer, :boolean, :float, :string or :symbol
|
39
|
+
# `block` contains the argument definition and will be evaluated seperately
|
40
|
+
# by the DSLMethod::Argument::Interpreter
|
41
|
+
def optional name, type, &block
|
42
|
+
@dsl_method.add_argument name, false, type, &block
|
43
|
+
end
|
44
|
+
|
45
|
+
# adds a new required argument to the DSLMethod
|
46
|
+
#
|
47
|
+
# name must be a symbol
|
48
|
+
# `type` can be either :integer, :boolean, :float, :string or :symbol
|
49
|
+
# `block` contains the argument definition and will be evaluated seperately
|
50
|
+
# by the DSLMethod::Argument::Interpreter
|
51
|
+
def requires name, type, &block
|
52
|
+
@dsl_method.add_argument name, true, type, &block
|
53
|
+
end
|
54
|
+
end
|
55
|
+
end
|
56
|
+
end
|
57
|
+
end
|