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,213 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module DSLCompose
|
4
|
+
class DSL
|
5
|
+
class DSLMethod
|
6
|
+
class ArgumentDoesNotExistError < StandardError
|
7
|
+
def message
|
8
|
+
"This argument does not exist for this DSLMethod"
|
9
|
+
end
|
10
|
+
end
|
11
|
+
|
12
|
+
class InvalidNameError < StandardError
|
13
|
+
def message
|
14
|
+
"The method #{method_name} is invalid, it must be of type symbol"
|
15
|
+
end
|
16
|
+
end
|
17
|
+
|
18
|
+
class MethodNameIsReservedError < StandardError
|
19
|
+
def message
|
20
|
+
"This method already would override an existing internal method"
|
21
|
+
end
|
22
|
+
end
|
23
|
+
|
24
|
+
class InvalidDescriptionError < StandardError
|
25
|
+
def message
|
26
|
+
"The DSL method description is invalid, it must be of type string and have length greater than 0"
|
27
|
+
end
|
28
|
+
end
|
29
|
+
|
30
|
+
class DescriptionAlreadyExistsError < StandardError
|
31
|
+
def message
|
32
|
+
"The description has already been set"
|
33
|
+
end
|
34
|
+
end
|
35
|
+
|
36
|
+
class ArgumentOrderingError < StandardError
|
37
|
+
def message
|
38
|
+
"Required arguments can not be added after optional ones"
|
39
|
+
end
|
40
|
+
end
|
41
|
+
|
42
|
+
class ArgumentAlreadyExistsError < StandardError
|
43
|
+
def message
|
44
|
+
"An argument with this name already exists for this DSL method"
|
45
|
+
end
|
46
|
+
end
|
47
|
+
|
48
|
+
class RequestedOptionalArgumentIsRequiredError < StandardError
|
49
|
+
def message
|
50
|
+
"A specific argument which was expected to be optional was requested, but the argument found was flagged as required"
|
51
|
+
end
|
52
|
+
end
|
53
|
+
|
54
|
+
class RequestedRequiredArgumentIsOptionalError < StandardError
|
55
|
+
def message
|
56
|
+
"A specific argument which was expected to be required was requested, but the argument found was flagged as optional"
|
57
|
+
end
|
58
|
+
end
|
59
|
+
|
60
|
+
# The name of this DSLMethod.
|
61
|
+
attr_reader :name
|
62
|
+
# if unique, then this DSLMethod can only be called once within the DSL.
|
63
|
+
attr_reader :unique
|
64
|
+
# if required, then this DSLMethod must be called at least once within the DSL.
|
65
|
+
attr_reader :required
|
66
|
+
# An otional description of this DSLMethod, if provided then it must be a string.
|
67
|
+
# The description accepts markdown and is used when generating documentation.
|
68
|
+
attr_reader :description
|
69
|
+
|
70
|
+
# Create a new DSLMethod object with the provided name and class.
|
71
|
+
#
|
72
|
+
# `name` must be a symbol.
|
73
|
+
# `unique` is a boolean which determines if this DSLMethod can only be called once witihn the DSL.
|
74
|
+
# `required` is a boolean which determines if this DSLMethod must be called at least once within the DSL.
|
75
|
+
# `block` contains the instructions to further configure this DSLMethod
|
76
|
+
def initialize name, unique, required, &block
|
77
|
+
@arguments = {}
|
78
|
+
|
79
|
+
if name.is_a? Symbol
|
80
|
+
|
81
|
+
# don't allow methods to override existing internal methods
|
82
|
+
if Class.respond_to? name
|
83
|
+
raise MethodNameIsReservedError
|
84
|
+
end
|
85
|
+
|
86
|
+
@name = name
|
87
|
+
else
|
88
|
+
raise InvalidNameError
|
89
|
+
end
|
90
|
+
|
91
|
+
@unique = unique ? true : false
|
92
|
+
@required = required ? true : false
|
93
|
+
|
94
|
+
# If a block was provided, then we evaluate it using a seperate
|
95
|
+
# interpreter class. We do this because the interpreter class contains
|
96
|
+
# no other methods or variables, if it was evaluated in the context of
|
97
|
+
# this class then the block would have access to all of the methods defined
|
98
|
+
# in here.
|
99
|
+
if block
|
100
|
+
Interpreter.new(self).instance_eval(&block)
|
101
|
+
end
|
102
|
+
end
|
103
|
+
|
104
|
+
# Set the description for this DSLMethod to the provided value.
|
105
|
+
#
|
106
|
+
# `description` must be a string with a length greater than 0.
|
107
|
+
# The `description` can only be set once per DSLMethod
|
108
|
+
def set_description description
|
109
|
+
unless description.is_a?(String) && description.length > 0
|
110
|
+
raise InvalidDescriptionError
|
111
|
+
end
|
112
|
+
|
113
|
+
if has_description?
|
114
|
+
raise DescriptionAlreadyExistsError
|
115
|
+
end
|
116
|
+
|
117
|
+
@description = description
|
118
|
+
end
|
119
|
+
|
120
|
+
# Returns `true` if this DSL has a description, else false.
|
121
|
+
def has_description?
|
122
|
+
@description.nil? == false
|
123
|
+
end
|
124
|
+
|
125
|
+
# Returns an array of all this DSLMethods Argument objects.
|
126
|
+
def arguments
|
127
|
+
@arguments.values
|
128
|
+
end
|
129
|
+
|
130
|
+
# Returns an array of only the optional Argument objects on this DSLMethod.
|
131
|
+
def optional_arguments
|
132
|
+
arguments.filter(&:optional?)
|
133
|
+
end
|
134
|
+
|
135
|
+
# Returns an array of only the required Argument objects on this DSLMethod.
|
136
|
+
def required_arguments
|
137
|
+
arguments.filter(&:required?)
|
138
|
+
end
|
139
|
+
|
140
|
+
# returns a specific Argument by it's name, if the Argument does not
|
141
|
+
# exist, then an error is raised
|
142
|
+
def argument name
|
143
|
+
if has_argument? name
|
144
|
+
@arguments[name]
|
145
|
+
else
|
146
|
+
raise ArgumentDoesNotExistError
|
147
|
+
end
|
148
|
+
end
|
149
|
+
|
150
|
+
# returns a specific optional Argument by it's name, if the Argument does not
|
151
|
+
# exist, or if it is required, then an error is raised
|
152
|
+
def optional_argument name
|
153
|
+
arg = argument name
|
154
|
+
if arg.optional?
|
155
|
+
@arguments[name]
|
156
|
+
else
|
157
|
+
raise RequestedOptionalArgumentIsRequiredError
|
158
|
+
end
|
159
|
+
end
|
160
|
+
|
161
|
+
# returns a specific required Argument by it's name, if the Argument does not
|
162
|
+
# exist, or if it is optional, then an error is raised
|
163
|
+
def required_argument name
|
164
|
+
arg = argument name
|
165
|
+
if arg.required?
|
166
|
+
@arguments[name]
|
167
|
+
else
|
168
|
+
raise RequestedRequiredArgumentIsOptionalError
|
169
|
+
end
|
170
|
+
end
|
171
|
+
|
172
|
+
# Returns `true` if an Argument with the provided name exists in this
|
173
|
+
# DSLMethod, otherwise it returns `false`.
|
174
|
+
def has_argument? name
|
175
|
+
@arguments.key? name
|
176
|
+
end
|
177
|
+
|
178
|
+
# returns true if this DSLMethod is flagged as unique, otherwise returns false.
|
179
|
+
def unique?
|
180
|
+
@unique == true
|
181
|
+
end
|
182
|
+
|
183
|
+
# returns true if this DSLMethod is flagged as required, otherwise returns false.
|
184
|
+
def required?
|
185
|
+
@required == true
|
186
|
+
end
|
187
|
+
|
188
|
+
# returns true if this DSLMethod is flagged as optional, otherwise returns false.
|
189
|
+
def optional?
|
190
|
+
@required == false
|
191
|
+
end
|
192
|
+
|
193
|
+
# Takes a method name, unique flag, required flag, and a block and creates
|
194
|
+
# a new Argument object.
|
195
|
+
#
|
196
|
+
# Argument `name` must be unique within the DSLMethod.
|
197
|
+
# `required` must be a boolean, and determines if this argument will be required
|
198
|
+
# or optional on the method which is exposed in our DSL.
|
199
|
+
def add_argument name, required, type, &block
|
200
|
+
if @arguments.key? name
|
201
|
+
raise ArgumentAlreadyExistsError
|
202
|
+
end
|
203
|
+
|
204
|
+
# required arguments may not come after optional ones
|
205
|
+
if required && optional_arguments.any?
|
206
|
+
raise ArgumentOrderingError
|
207
|
+
end
|
208
|
+
|
209
|
+
@arguments[name] = Argument.new(name, required, type, &block)
|
210
|
+
end
|
211
|
+
end
|
212
|
+
end
|
213
|
+
end
|
@@ -0,0 +1,52 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module DSLCompose
|
4
|
+
class DSL
|
5
|
+
# The class is reponsible for parsing and executing our internal DSL which is used to define
|
6
|
+
# a new dynamic DSL. This class is instantaited by the DSLCompose::DSL class and our internal
|
7
|
+
# DSL is evaluated by passing a block to `instance_eval` on this class.
|
8
|
+
#
|
9
|
+
# An example of our internal DSL:
|
10
|
+
# define_dsl :my_dsl do
|
11
|
+
# description "This is my DSL"
|
12
|
+
# add_method :my_method do
|
13
|
+
# # ...
|
14
|
+
# end
|
15
|
+
# add_unique_method :my_uniq_method do
|
16
|
+
# # ...
|
17
|
+
# end
|
18
|
+
# end
|
19
|
+
class Interpreter
|
20
|
+
def initialize dsl
|
21
|
+
@dsl = dsl
|
22
|
+
end
|
23
|
+
|
24
|
+
private
|
25
|
+
|
26
|
+
# sets the description of the DSL
|
27
|
+
def description description
|
28
|
+
@dsl.set_description description
|
29
|
+
end
|
30
|
+
|
31
|
+
# adds a new method to the DSL
|
32
|
+
#
|
33
|
+
# methods flagged as `required` will cause your DSLs to raise an error
|
34
|
+
# if they are not used at least once within your new DSLs
|
35
|
+
# `block` contains the method definition and will be evaluated seperately
|
36
|
+
# by the DSLMethod::Interpreter
|
37
|
+
def add_method name, required: nil, &block
|
38
|
+
@dsl.add_method name, false, required ? true : false, &block
|
39
|
+
end
|
40
|
+
|
41
|
+
# adds a new unique method to the DSL
|
42
|
+
#
|
43
|
+
# methods flagged as `required` will cause your DSLs to raise an error
|
44
|
+
# if they are not used at least once within your new DSLs
|
45
|
+
# `block` contains the method definition and will be evaluated seperately
|
46
|
+
# by the DSLMethod::Interpreter
|
47
|
+
def add_unique_method name, required: nil, &block
|
48
|
+
@dsl.add_method name, true, required ? true : false, &block
|
49
|
+
end
|
50
|
+
end
|
51
|
+
end
|
52
|
+
end
|
@@ -0,0 +1,148 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module DSLCompose
|
4
|
+
# The class is reponsible for creating and representing a dynamic DSL
|
5
|
+
#
|
6
|
+
# These new dynamic DSL's are created using our own internal DSL, which is accessed
|
7
|
+
# by calling `define_dsl` in a class and passing it a block which contains the DSL definition
|
8
|
+
class DSL
|
9
|
+
class MethodDoesNotExistError < StandardError
|
10
|
+
def message
|
11
|
+
"This method does not exist for this DSL"
|
12
|
+
end
|
13
|
+
end
|
14
|
+
|
15
|
+
class MethodAlreadyExistsError < StandardError
|
16
|
+
def message
|
17
|
+
"This method already exists for this DSL"
|
18
|
+
end
|
19
|
+
end
|
20
|
+
|
21
|
+
class InvalidNameError < StandardError
|
22
|
+
def message
|
23
|
+
"This DSL name is invalid, it must be of type symbol"
|
24
|
+
end
|
25
|
+
end
|
26
|
+
|
27
|
+
class InvalidDescriptionError < StandardError
|
28
|
+
def message
|
29
|
+
"This DSL description is invalid, it must be of type string and have length greater than 0"
|
30
|
+
end
|
31
|
+
end
|
32
|
+
|
33
|
+
class DescriptionAlreadyExistsError < StandardError
|
34
|
+
def message
|
35
|
+
"The DSL description has already been set"
|
36
|
+
end
|
37
|
+
end
|
38
|
+
|
39
|
+
class NoBlockProvidedError < StandardError
|
40
|
+
def message
|
41
|
+
"No block was provided for this DSL"
|
42
|
+
end
|
43
|
+
end
|
44
|
+
|
45
|
+
# The name of this DSL.
|
46
|
+
attr_reader :name
|
47
|
+
# klass will be the class where `define_dsl` was called.
|
48
|
+
attr_reader :klass
|
49
|
+
# An otional description of this DSL, if provided then it must be a string.
|
50
|
+
# The description accepts markdown and is used when generating documentation.
|
51
|
+
attr_reader :description
|
52
|
+
|
53
|
+
# Create a new DSL object with the provided name and class.
|
54
|
+
#
|
55
|
+
# `name` must be a symbol.
|
56
|
+
# `klass` should be the class in which `define_dsl` is being called.
|
57
|
+
def initialize name, klass
|
58
|
+
@dsl_methods = {}
|
59
|
+
|
60
|
+
if name.is_a? Symbol
|
61
|
+
@name = name
|
62
|
+
else
|
63
|
+
raise InvalidNameError
|
64
|
+
end
|
65
|
+
|
66
|
+
@klass = klass
|
67
|
+
end
|
68
|
+
|
69
|
+
# Evaluate the configuration block which defines our new DSL
|
70
|
+
# `block` contains the DSL definition and will be evaluated to create
|
71
|
+
# the rest of the DSL.
|
72
|
+
def evaluate_configuration_block &block
|
73
|
+
if block
|
74
|
+
# We evaluate the internal DSL configuration blocks using a seperate interpreter
|
75
|
+
# class. We do this because the interpreter class contains no other methods or
|
76
|
+
# variables, if it was evaluated in the context of this class then the block
|
77
|
+
# would have access to all of the methods defined in here.
|
78
|
+
Interpreter.new(self).instance_eval(&block)
|
79
|
+
else
|
80
|
+
raise NoBlockProvidedError
|
81
|
+
end
|
82
|
+
end
|
83
|
+
|
84
|
+
# Set the description for this DSL to the provided value.
|
85
|
+
#
|
86
|
+
# `description` must be a string with a length greater than 0.
|
87
|
+
# The `description` can only be set once per DSL
|
88
|
+
def set_description description
|
89
|
+
unless description.is_a?(String) && description.length > 0
|
90
|
+
raise InvalidDescriptionError
|
91
|
+
end
|
92
|
+
|
93
|
+
if has_description?
|
94
|
+
raise DescriptionAlreadyExistsError
|
95
|
+
end
|
96
|
+
|
97
|
+
@description = description
|
98
|
+
end
|
99
|
+
|
100
|
+
# Returns `true` if this DSL has a description, else false.
|
101
|
+
def has_description?
|
102
|
+
@description.nil? == false
|
103
|
+
end
|
104
|
+
|
105
|
+
# Takes a method name, unique flag, required flag, and a block and creates
|
106
|
+
# a new DSLMethod object.
|
107
|
+
#
|
108
|
+
# Method `name` must be unique within the DSL.
|
109
|
+
def add_method name, unique, required, &block
|
110
|
+
if has_dsl_method? name
|
111
|
+
raise MethodAlreadyExistsError
|
112
|
+
end
|
113
|
+
|
114
|
+
@dsl_methods[name] = DSLMethod.new(name, unique, required, &block)
|
115
|
+
end
|
116
|
+
|
117
|
+
# Returns an array of all this DSLs DSLMethods.
|
118
|
+
def dsl_methods
|
119
|
+
@dsl_methods.values
|
120
|
+
end
|
121
|
+
|
122
|
+
# Returns an array of only the required DSLMethods in this DSL.
|
123
|
+
def required_dsl_methods
|
124
|
+
dsl_methods.filter(&:required?)
|
125
|
+
end
|
126
|
+
|
127
|
+
# Returns an array of only the optional DSLMethods in this DSL.
|
128
|
+
def optional_dsl_methods
|
129
|
+
dsl_methods.filter(&:optional?)
|
130
|
+
end
|
131
|
+
|
132
|
+
# returns a specific DSLMethod by it's name, if the DSLMethod does not
|
133
|
+
# exist, then an error is raised
|
134
|
+
def dsl_method name
|
135
|
+
if has_dsl_method? name
|
136
|
+
@dsl_methods[name]
|
137
|
+
else
|
138
|
+
raise MethodDoesNotExistError
|
139
|
+
end
|
140
|
+
end
|
141
|
+
|
142
|
+
# Returns `true` if a DSLMethod with the provided name exists in this
|
143
|
+
# DSL, otherwise it returns `false`.
|
144
|
+
def has_dsl_method? name
|
145
|
+
@dsl_methods.key? name
|
146
|
+
end
|
147
|
+
end
|
148
|
+
end
|
@@ -0,0 +1,80 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module DSLCompose
|
4
|
+
module DSLs
|
5
|
+
class ClassDSLDefinitionDoesNotExistError < StandardError
|
6
|
+
def message
|
7
|
+
"The requested DSL does not exist on this class"
|
8
|
+
end
|
9
|
+
end
|
10
|
+
|
11
|
+
class DSLAlreadyExistsError < StandardError
|
12
|
+
def message
|
13
|
+
"A DSL with this name already exists"
|
14
|
+
end
|
15
|
+
end
|
16
|
+
|
17
|
+
class NoDSLDefinitionsForClassError < StandardError
|
18
|
+
def message
|
19
|
+
"No DSLs have been defined for this class"
|
20
|
+
end
|
21
|
+
end
|
22
|
+
|
23
|
+
# an object to hold all of the defined DSLs in our application, the DSLs are
|
24
|
+
# organized by the class in which they were defined
|
25
|
+
@dsls = {}
|
26
|
+
|
27
|
+
# create a new DSL definition for a class
|
28
|
+
# `klass` here is the class in which `define_dsl` was called
|
29
|
+
def self.create_dsl klass, name
|
30
|
+
@dsls[klass] ||= {}
|
31
|
+
|
32
|
+
if @dsls[klass].key? name
|
33
|
+
raise DSLAlreadyExistsError
|
34
|
+
end
|
35
|
+
|
36
|
+
@dsls[klass][name] = DSLCompose::DSL.new(name, klass)
|
37
|
+
end
|
38
|
+
|
39
|
+
# return all of the DSL definitions
|
40
|
+
def self.dsls
|
41
|
+
@dsls
|
42
|
+
end
|
43
|
+
|
44
|
+
# return a DSL with a provided name for the provided class, if the DSL doesn't
|
45
|
+
# exist then it will be automatically created
|
46
|
+
def self.class_dsl_exists? klass, name
|
47
|
+
@dsls.key?(klass) && @dsls[klass].key?(name)
|
48
|
+
end
|
49
|
+
|
50
|
+
# return an array of DSL definitions for a provided class, if no DSLs
|
51
|
+
# exist for the provided class, then an error is raised
|
52
|
+
def self.class_dsls klass
|
53
|
+
if @dsls.key? klass
|
54
|
+
@dsls[klass].values
|
55
|
+
else
|
56
|
+
raise NoDSLDefinitionsForClassError
|
57
|
+
end
|
58
|
+
end
|
59
|
+
|
60
|
+
# return a specific DSL definition for a provided class
|
61
|
+
# if it does not exist, then an error is raised
|
62
|
+
def self.class_dsl klass, name
|
63
|
+
if @dsls.key? klass
|
64
|
+
if @dsls[klass].key? name
|
65
|
+
@dsls[klass][name]
|
66
|
+
else
|
67
|
+
raise ClassDSLDefinitionDoesNotExistError
|
68
|
+
end
|
69
|
+
else
|
70
|
+
raise NoDSLDefinitionsForClassError
|
71
|
+
end
|
72
|
+
end
|
73
|
+
|
74
|
+
# removes all DSL deinitions, this method is typically used by the test
|
75
|
+
# suite for resetting state inbetween each test
|
76
|
+
def self.reset
|
77
|
+
@dsls = {}
|
78
|
+
end
|
79
|
+
end
|
80
|
+
end
|
@@ -0,0 +1,155 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module DSLCompose
|
4
|
+
class Interpreter
|
5
|
+
class Execution
|
6
|
+
class MethodCalls
|
7
|
+
class MethodCall
|
8
|
+
attr_reader :dsl_method
|
9
|
+
attr_reader :arguments
|
10
|
+
|
11
|
+
class MissingRequiredArgumentsError < StandardError
|
12
|
+
def initialize required_count, provided_count
|
13
|
+
super "This method requires #{required_count} arguments, but only #{required_count} were provided"
|
14
|
+
end
|
15
|
+
end
|
16
|
+
|
17
|
+
class TooManyArgumentsError < StandardError
|
18
|
+
def message
|
19
|
+
"Too many arguments provided to this method"
|
20
|
+
end
|
21
|
+
end
|
22
|
+
|
23
|
+
class OptionalArgsShouldBeHashError < StandardError
|
24
|
+
def message
|
25
|
+
"If provided, then the optional arguments must be last, and be represented as a Hash"
|
26
|
+
end
|
27
|
+
end
|
28
|
+
|
29
|
+
class InvalidArgumentTypeError < StandardError
|
30
|
+
def message
|
31
|
+
"The provided argument is the wrong type"
|
32
|
+
end
|
33
|
+
end
|
34
|
+
|
35
|
+
def initialize dsl_method, *args, &block
|
36
|
+
@dsl_method = dsl_method
|
37
|
+
@arguments = {}
|
38
|
+
|
39
|
+
required_argument_count = dsl_method.required_arguments.count
|
40
|
+
has_optional_arguments = dsl_method.optional_arguments.any?
|
41
|
+
|
42
|
+
# the first N args, where N = required_argument_count, are the
|
43
|
+
# provided required arguments
|
44
|
+
required_args = args.slice(0, required_argument_count)
|
45
|
+
# the optional arg which comes next is the hash which represents
|
46
|
+
# all the optional arguments
|
47
|
+
optional_arg = args[required_argument_count]
|
48
|
+
|
49
|
+
# assert that a value is provided for every required argument
|
50
|
+
unless required_argument_count == required_args.count
|
51
|
+
raise MissingRequiredArgumentsError.new required_argument_count, required_args.count
|
52
|
+
end
|
53
|
+
|
54
|
+
# assert that too many arguments have not been provided
|
55
|
+
if args.count > required_argument_count + (has_optional_arguments ? 1 : 0)
|
56
|
+
raise TooManyArgumentsError
|
57
|
+
end
|
58
|
+
|
59
|
+
# asset that, if provided, then the optional argument (always the last one) is a Hash
|
60
|
+
if has_optional_arguments && optional_arg.nil? === false
|
61
|
+
unless optional_arg.is_a? Hash
|
62
|
+
raise OptionalArgsShouldBeHashError
|
63
|
+
end
|
64
|
+
|
65
|
+
# assert the each provided optional argument is valid
|
66
|
+
optional_arg.keys.each do |optional_argument_name|
|
67
|
+
optional_arg_value = optional_arg[optional_argument_name]
|
68
|
+
optional_argument = dsl_method.optional_argument optional_argument_name
|
69
|
+
|
70
|
+
case optional_argument.type
|
71
|
+
when :integer
|
72
|
+
unless optional_arg_value.is_a? Integer
|
73
|
+
raise InvalidArgumentTypeError
|
74
|
+
end
|
75
|
+
optional_argument.validate_integer! optional_arg_value
|
76
|
+
|
77
|
+
when :symbol
|
78
|
+
unless optional_arg_value.is_a? Symbol
|
79
|
+
raise InvalidArgumentTypeError
|
80
|
+
end
|
81
|
+
optional_argument.validate_symbol! optional_arg_value
|
82
|
+
|
83
|
+
when :string
|
84
|
+
unless optional_arg_value.is_a? String
|
85
|
+
raise InvalidArgumentTypeError
|
86
|
+
end
|
87
|
+
optional_argument.validate_string! optional_arg_value
|
88
|
+
|
89
|
+
when :boolean
|
90
|
+
unless optional_arg_value.is_a?(TrueClass) || optional_arg_value.is_a?(FalseClass)
|
91
|
+
raise InvalidArgumentTypeError
|
92
|
+
end
|
93
|
+
optional_argument.validate_boolean! optional_arg_value
|
94
|
+
|
95
|
+
else
|
96
|
+
raise InvalidArgumentTypeError
|
97
|
+
end
|
98
|
+
|
99
|
+
# the provided value appears valid for this argument, save the value
|
100
|
+
@arguments[optional_argument_name] = optional_arg_value
|
101
|
+
end
|
102
|
+
|
103
|
+
end
|
104
|
+
|
105
|
+
# validate the value provided to each required argument
|
106
|
+
dsl_method.required_arguments.each_with_index do |required_argument, i|
|
107
|
+
arg = args[i]
|
108
|
+
case required_argument.type
|
109
|
+
when :integer
|
110
|
+
unless arg.is_a? Integer
|
111
|
+
raise InvalidArgumentTypeError
|
112
|
+
end
|
113
|
+
required_argument.validate_integer! arg
|
114
|
+
|
115
|
+
when :symbol
|
116
|
+
unless arg.is_a? Symbol
|
117
|
+
raise InvalidArgumentTypeError
|
118
|
+
end
|
119
|
+
required_argument.validate_symbol! arg
|
120
|
+
|
121
|
+
when :string
|
122
|
+
unless arg.is_a? String
|
123
|
+
raise InvalidArgumentTypeError
|
124
|
+
end
|
125
|
+
required_argument.validate_string! arg
|
126
|
+
|
127
|
+
when :boolean
|
128
|
+
unless arg.is_a?(TrueClass) || arg.is_a?(FalseClass)
|
129
|
+
raise InvalidArgumentTypeError
|
130
|
+
end
|
131
|
+
required_argument.validate_boolean! arg
|
132
|
+
|
133
|
+
else
|
134
|
+
raise InvalidArgumentTypeError
|
135
|
+
end
|
136
|
+
|
137
|
+
# the provided value appears valid for this argument, save the value
|
138
|
+
@arguments[required_argument.name] = arg
|
139
|
+
end
|
140
|
+
end
|
141
|
+
|
142
|
+
def method_name
|
143
|
+
@dsl_method.name
|
144
|
+
end
|
145
|
+
|
146
|
+
def to_h
|
147
|
+
{
|
148
|
+
arguments: @arguments
|
149
|
+
}
|
150
|
+
end
|
151
|
+
end
|
152
|
+
end
|
153
|
+
end
|
154
|
+
end
|
155
|
+
end
|
@@ -0,0 +1,25 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module DSLCompose
|
4
|
+
class Interpreter
|
5
|
+
class Execution
|
6
|
+
class MethodCalls
|
7
|
+
attr_reader :method_calls
|
8
|
+
|
9
|
+
def initialize
|
10
|
+
@method_calls = []
|
11
|
+
end
|
12
|
+
|
13
|
+
def method_called? method_name
|
14
|
+
@method_calls.filter { |mc| mc.method_name == method_name }.any?
|
15
|
+
end
|
16
|
+
|
17
|
+
def add_method_call dsl_method, *args, &block
|
18
|
+
method_call = MethodCall.new(dsl_method, *args, &block)
|
19
|
+
@method_calls << method_call
|
20
|
+
method_call
|
21
|
+
end
|
22
|
+
end
|
23
|
+
end
|
24
|
+
end
|
25
|
+
end
|