dsl_maker 0.0.7 → 0.0.8
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 +4 -4
- data/Changes +10 -0
- data/lib/dsl/maker.rb +137 -98
- data/lib/dsl/maker/version.rb +1 -1
- data/spec/args_spec.rb +49 -46
- data/spec/class_as_subdsl_spec.rb +8 -11
- data/spec/error_spec.rb +38 -4
- data/spec/helper_spec.rb +6 -9
- data/spec/multi_level_spec.rb +19 -22
- data/spec/multiple_invocation_spec.rb +15 -18
- data/spec/single_level_spec.rb +34 -10
- data/spec/spec_helper.rb +16 -0
- data/spec/validation_spec.rb +27 -1
- metadata +2 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: fd8b809906f6bde7d8b1839095dc2ad018148190
|
4
|
+
data.tar.gz: 13708d81d5f96de3f5744736c77f4b55ef2d5748
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: d0c2f6464bc5d08693a7298dae3074c9f89f00c9f2959bb1366b86a5cfdf211bc6fc779c045cb111fd04f0e0e7aff1aa8304ccd895d084fa32119e4fdba7c951
|
7
|
+
data.tar.gz: c9ecc30fba1b36b6c6aac878de49a6191df39180ef05dd96996a411e82c5c52e44ef202254ba08736755e95fe9d8df3fba94ad512adc51ddc6d139d96f1a987a
|
data/Changes
CHANGED
@@ -1,5 +1,15 @@
|
|
1
1
|
Revision history for DSL::Maker (ordered by revision number).
|
2
2
|
|
3
|
+
0.0.8 Aug 07 2015
|
4
|
+
- Make sure to provide a unique value for each type name.
|
5
|
+
- Allow 'Object' to be used as an alias for 'Any'
|
6
|
+
- Signficant refactorings (Thanks, "Alex Burkhart <saterus@gmail.com>"!!)
|
7
|
+
- Test globals are properly segregated
|
8
|
+
- Properly separated the control and DSL classes
|
9
|
+
- Verifications can be used at every DSL class level
|
10
|
+
- Moved build_dsl_element() into private. It can still be used within the
|
11
|
+
control class under construction.
|
12
|
+
|
3
13
|
0.0.7 Aug 06 2015
|
4
14
|
- Added the 'Any' type coercion.
|
5
15
|
- Added a TL;DR section at the top of the README.
|
data/lib/dsl/maker.rb
CHANGED
@@ -2,6 +2,13 @@ require 'dsl/maker/version'
|
|
2
2
|
|
3
3
|
require 'docile'
|
4
4
|
|
5
|
+
# Children of DSL::Maker are the "control class". All of the classes that inherit
|
6
|
+
# from DSL::Maker::Base are "dsl classes" - classes that are passed to Docile and
|
7
|
+
# which represent levels of the DSL. In order for :parse_dsl/:execute_dsl to
|
8
|
+
# return back the accumulated values in the order provided, we need to "pierce the
|
9
|
+
# veil" (so to speak) between the control and dsl classes. That's done using the
|
10
|
+
# parent_class class attribute in the dsl classes.
|
11
|
+
|
5
12
|
# This is the base class we provide.
|
6
13
|
class DSL::Maker
|
7
14
|
# This is the base class for all DSL-parsing classes.
|
@@ -11,10 +18,32 @@ class DSL::Maker
|
|
11
18
|
|
12
19
|
# 21 character method names are obscene. Make it easier to read.
|
13
20
|
alias :___get :instance_variable_get
|
21
|
+
|
22
|
+
def get_binding
|
23
|
+
binding
|
24
|
+
end
|
25
|
+
|
26
|
+
define_singleton_method(:add_verification) do |&block|
|
27
|
+
# FIXME: This throws regardless. Is this because of the difference between
|
28
|
+
# proc and block?
|
29
|
+
#raise "Block required for add_verification" unless block_given?
|
30
|
+
|
31
|
+
@verifications ||= []
|
32
|
+
|
33
|
+
# This craziness converts the block provided into a proc that can be called
|
34
|
+
# in add_entrypoint(). Taken from http://stackoverflow.com/a/2946734/1732954
|
35
|
+
# Note: self is not preserved. This should be okay because the verification
|
36
|
+
# should be idempotent relative to the value provided (side-effect-free).
|
37
|
+
obj = Object.new
|
38
|
+
obj.define_singleton_method(:_, &block)
|
39
|
+
@verifications.push(obj.method(:_).to_proc)
|
40
|
+
|
41
|
+
return
|
42
|
+
end
|
14
43
|
end
|
15
44
|
|
16
|
-
# Create the DSL::Maker::Any type identifier
|
17
|
-
Any =
|
45
|
+
# Create the DSL::Maker::Any type identifier, equivalent to Object.
|
46
|
+
Any = Object
|
18
47
|
|
19
48
|
# This is a useful module that contains all the Boolean handling we need.
|
20
49
|
module Boolean
|
@@ -44,8 +73,11 @@ class DSL::Maker
|
|
44
73
|
# @param dsl [String] The DSL to be parsed by this class.
|
45
74
|
#
|
46
75
|
# @return [Object] Whatever is returned by the block defined in this class.
|
47
|
-
def self.parse_dsl(dsl)
|
48
|
-
|
76
|
+
def self.parse_dsl(dsl=nil)
|
77
|
+
raise 'Must call add_entrypoint before parse_dsl' unless @klass
|
78
|
+
raise 'String required for parse_dsl' unless dsl.instance_of? String
|
79
|
+
|
80
|
+
run_dsl() { eval dsl, @klass.new.get_binding }
|
49
81
|
end
|
50
82
|
|
51
83
|
# Execute the DSL provided in the block.
|
@@ -57,18 +89,15 @@ class DSL::Maker
|
|
57
89
|
#
|
58
90
|
# @return [Object] Whatever is returned by &block
|
59
91
|
def self.execute_dsl(&block)
|
92
|
+
raise 'Must call add_entrypoint before execute_dsl' unless @klass
|
60
93
|
raise 'Block required for execute_dsl' unless block_given?
|
61
94
|
|
62
|
-
|
95
|
+
run_dsl() { @klass.new.instance_eval(&block) }
|
63
96
|
end
|
64
97
|
|
65
|
-
# FIXME: This may have to be changed when the elements can be altered because
|
66
|
-
# it is global to the hierarchy. But, that may be desirable.
|
67
|
-
@@types = {}
|
68
|
-
|
69
98
|
# This adds a type coercion that's used when creating the DSL.
|
70
99
|
#
|
71
|
-
#
|
100
|
+
# @note These type coercions are global to all DSLs.
|
72
101
|
#
|
73
102
|
# @param type [Object] the name of the helper
|
74
103
|
# @param &block [Block] The function to be executed when the coercion is exercised.
|
@@ -96,44 +125,6 @@ class DSL::Maker
|
|
96
125
|
return
|
97
126
|
end
|
98
127
|
|
99
|
-
# Add a single element of a DSL to a class representing a level in a DSL.
|
100
|
-
#
|
101
|
-
# Each of the types represents a coercion - a guarantee and check of the value
|
102
|
-
# in that name. The standard type coercions are:
|
103
|
-
#
|
104
|
-
# * String - whatever you give is returned.
|
105
|
-
# * Integer - the integer value of whatever you give is returned.
|
106
|
-
# * Boolean - the truthiness of whatever you give is returned.
|
107
|
-
# * generate_dsl() - this represents a new level of the DSL.
|
108
|
-
#
|
109
|
-
# @param klass [Class] The class representing this level in the DSL.
|
110
|
-
# @param name [String] The name of the element we're working on.
|
111
|
-
# @param type [Class] The type of this element we're working on.
|
112
|
-
# This is the type coercion spoken above.
|
113
|
-
#
|
114
|
-
# @return nil
|
115
|
-
def self.build_dsl_element(klass, name, type)
|
116
|
-
if @@types.has_key?(type)
|
117
|
-
@@types[type].call(klass, name, type)
|
118
|
-
elsif __is_dsl(type)
|
119
|
-
as_attr = '@' + name.to_s
|
120
|
-
klass.class_eval do
|
121
|
-
define_method(name.to_sym) do |*args, &dsl_block|
|
122
|
-
unless (args.empty? && !dsl_block)
|
123
|
-
obj = type.new
|
124
|
-
Docile.dsl_eval(obj, &dsl_block) if dsl_block
|
125
|
-
___set(as_attr, obj.__apply(*args))
|
126
|
-
end
|
127
|
-
___get(as_attr)
|
128
|
-
end
|
129
|
-
end
|
130
|
-
else
|
131
|
-
raise "Unrecognized element type '#{type}'"
|
132
|
-
end
|
133
|
-
|
134
|
-
return
|
135
|
-
end
|
136
|
-
|
137
128
|
# Add the meat of a DSL block to some level of this class's DSL.
|
138
129
|
#
|
139
130
|
# In order for Docile to parse a DSL, each level must be represented by a
|
@@ -149,15 +140,13 @@ class DSL::Maker
|
|
149
140
|
def self.generate_dsl(args={}, &defn_block)
|
150
141
|
raise 'Block required for generate_dsl' unless block_given?
|
151
142
|
|
152
|
-
# Inherit from the Boolean class to gain access to the useful methods
|
153
|
-
# TODO: Convert DSL::Maker::Boolean into a Role
|
154
|
-
# TODO: Create a DSL::Maker::Base class to inherit from
|
155
143
|
dsl_class = Class.new(DSL::Maker::Base) do
|
156
144
|
include DSL::Maker::Boolean
|
157
145
|
|
158
|
-
|
159
|
-
|
160
|
-
|
146
|
+
class << self
|
147
|
+
attr_accessor :parent_class, :verifications
|
148
|
+
end
|
149
|
+
|
161
150
|
define_method(:__apply) do |*args|
|
162
151
|
instance_exec(*args, &defn_block)
|
163
152
|
end
|
@@ -189,11 +178,11 @@ class DSL::Maker
|
|
189
178
|
def self.add_entrypoint(name, args={}, &defn_block)
|
190
179
|
symname = name.to_sym
|
191
180
|
|
192
|
-
if
|
181
|
+
if is_entrypoint(symname)
|
193
182
|
raise "'#{name.to_s}' is already an entrypoint"
|
194
183
|
end
|
195
184
|
|
196
|
-
if
|
185
|
+
if is_dsl(args)
|
197
186
|
dsl_class = args
|
198
187
|
else
|
199
188
|
# Without defn_block, there's no way to give back the result of the
|
@@ -203,21 +192,20 @@ class DSL::Maker
|
|
203
192
|
raise "Block required for add_entrypoint" unless block_given?
|
204
193
|
dsl_class = generate_dsl(args, &defn_block)
|
205
194
|
end
|
206
|
-
|
207
|
-
|
208
|
-
|
209
|
-
|
210
|
-
|
211
|
-
|
212
|
-
|
213
|
-
|
214
|
-
|
215
|
-
|
216
|
-
|
217
|
-
|
218
|
-
|
219
|
-
@
|
220
|
-
return rv
|
195
|
+
|
196
|
+
if @klass
|
197
|
+
build_dsl_element(@klass, symname, dsl_class)
|
198
|
+
else
|
199
|
+
# FIXME: We shouldn't need the blank block here ...
|
200
|
+
# This blank block is representative of the implicit (and missing) outermost
|
201
|
+
# block around the DSL that we are not putting into place in :parse_dsl or
|
202
|
+
# :execute_dsl.
|
203
|
+
@klass = generate_dsl({
|
204
|
+
symname => dsl_class
|
205
|
+
}) {}
|
206
|
+
|
207
|
+
# This marks @klass as the root DSL class.
|
208
|
+
@klass.parent_class = self
|
221
209
|
end
|
222
210
|
|
223
211
|
@entrypoints ||= {}
|
@@ -230,7 +218,7 @@ class DSL::Maker
|
|
230
218
|
#
|
231
219
|
# @return [Class] The class that implements this name's DSL definition.
|
232
220
|
def self.entrypoint(name)
|
233
|
-
unless
|
221
|
+
unless is_entrypoint(name)
|
234
222
|
raise "'#{name.to_s}' is not an entrypoint"
|
235
223
|
end
|
236
224
|
|
@@ -265,8 +253,13 @@ class DSL::Maker
|
|
265
253
|
# execution. If the verification returns a true value (of any kind), then that
|
266
254
|
# will be raised as a runtime exception.
|
267
255
|
#
|
268
|
-
#
|
269
|
-
#
|
256
|
+
# You can also call add_verification on the return values from generate_dsl() or
|
257
|
+
# add_entrypoint(). In those cases, omit the :name because you have already
|
258
|
+
# chosen the DSL layer you're adding the verification to.
|
259
|
+
#
|
260
|
+
# @note These verifications are specific to the DSL you add them to.
|
261
|
+
#
|
262
|
+
# @note Verifications are called in the order you specify them.
|
270
263
|
#
|
271
264
|
# @param name [String] the name of the entrypoint to add a verification to
|
272
265
|
# @param &block [Block] The function to be executed when verifications execute
|
@@ -274,34 +267,79 @@ class DSL::Maker
|
|
274
267
|
# @return nil
|
275
268
|
def self.add_verification(name, &block)
|
276
269
|
raise "Block required for add_verification" unless block_given?
|
277
|
-
raise "'#{name.to_s}' is not an entrypoint for a verification" unless
|
270
|
+
raise "'#{name.to_s}' is not an entrypoint for a verification" unless is_entrypoint(name)
|
278
271
|
|
279
|
-
@
|
280
|
-
@verifications[name.to_sym] ||= []
|
281
|
-
|
282
|
-
# This craziness converts the block provided into a proc that can be called
|
283
|
-
# in add_entrypoint(). Taken from http://stackoverflow.com/a/2946734/1732954
|
284
|
-
# Note: self is not preserved. This should be okay because the verification
|
285
|
-
# should only care about the value provided.
|
286
|
-
obj = Object.new
|
287
|
-
obj.define_singleton_method(:_, &block)
|
288
|
-
@verifications[name.to_sym].push(obj.method(:_).to_proc)
|
289
|
-
|
290
|
-
return
|
272
|
+
@entrypoints[name.to_sym].add_verification(&block)
|
291
273
|
end
|
292
274
|
|
293
275
|
private
|
294
276
|
|
295
|
-
#
|
277
|
+
# This is deliberately global to the hierarchy in order for DSL::Maker to add
|
278
|
+
# the generic types. While this has the potential to cause userspace collisions,
|
279
|
+
# it's highly unlikely that DSLs with divergent types will coexist in the same
|
280
|
+
# Ruby process.
|
281
|
+
@@types = {}
|
282
|
+
|
283
|
+
# Add a single element of a DSL to a class representing a level in a DSL.
|
284
|
+
#
|
285
|
+
# Each of the types represents a coercion - a guarantee and check of the value
|
286
|
+
# in that name. The standard type coercions are:
|
296
287
|
#
|
297
|
-
#
|
298
|
-
|
299
|
-
|
288
|
+
# * Any - whatever you give is returned.
|
289
|
+
# * String - the string value of whatever you give is returned.
|
290
|
+
# * Integer - the integer value of whatever you give is returned.
|
291
|
+
# * Boolean - the truthiness of whatever you give is returned.
|
292
|
+
# * generate_dsl() - this represents a new level of the DSL.
|
293
|
+
#
|
294
|
+
# @param klass [Class] The class representing this level in the DSL.
|
295
|
+
# @param name [String] The name of the element we're working on.
|
296
|
+
# @param type [Class] The type of this element we're working on.
|
297
|
+
# This is the type coercion spoken above.
|
298
|
+
#
|
299
|
+
# @return nil
|
300
|
+
def self.build_dsl_element(klass, name, type)
|
301
|
+
if @@types.has_key?(type)
|
302
|
+
@@types[type].call(klass, name, type)
|
303
|
+
elsif is_dsl(type)
|
304
|
+
as_attr = '@' + name.to_s
|
305
|
+
klass.class_eval do
|
306
|
+
define_method(name.to_sym) do |*args, &dsl_block|
|
307
|
+
if (!args.empty? || dsl_block)
|
308
|
+
obj = type.new
|
309
|
+
Docile.dsl_eval(obj, &dsl_block) if dsl_block
|
310
|
+
rv = obj.__apply(*args)
|
311
|
+
|
312
|
+
if v = type.instance_variable_get(:@verifications)
|
313
|
+
v.each do |verify|
|
314
|
+
failure = verify.call(rv)
|
315
|
+
raise failure if failure
|
316
|
+
end
|
317
|
+
end
|
318
|
+
|
319
|
+
# This is the one place where we pull out the entrypoint results and
|
320
|
+
# put them into the control class.
|
321
|
+
if klass.parent_class
|
322
|
+
# Use the full instance_variable_get() in order to avoid having to
|
323
|
+
# create accessors that could be misused outside this class.
|
324
|
+
klass.parent_class.instance_variable_get(:@accumulator).push(rv)
|
325
|
+
end
|
326
|
+
|
327
|
+
___set(as_attr, rv)
|
328
|
+
end
|
329
|
+
___get(as_attr)
|
330
|
+
end
|
331
|
+
end
|
332
|
+
else
|
333
|
+
raise "Unrecognized element type '#{type}'"
|
334
|
+
end
|
335
|
+
|
336
|
+
return
|
300
337
|
end
|
301
338
|
|
302
|
-
def self.
|
303
|
-
#
|
304
|
-
# Reset it here so that we're only
|
339
|
+
def self.run_dsl()
|
340
|
+
# build_dsl_element() will use @accumulator to handle multiple entrypoints if
|
341
|
+
# the class in question is a root DSL class. Reset it here so that we're only
|
342
|
+
# handling the values from this run.
|
305
343
|
@accumulator = []
|
306
344
|
|
307
345
|
yield
|
@@ -312,16 +350,17 @@ class DSL::Maker
|
|
312
350
|
return @accumulator
|
313
351
|
end
|
314
352
|
|
315
|
-
def self.
|
353
|
+
def self.is_dsl(proto)
|
316
354
|
proto.is_a?(Class) && proto.ancestors.include?(DSL::Maker::Base)
|
317
355
|
end
|
318
356
|
|
319
|
-
def self.
|
320
|
-
|
357
|
+
def self.is_entrypoint(name)
|
358
|
+
@entrypoints && @entrypoints.has_key?(name.to_sym)
|
359
|
+
#@klass && @klass.new.respond_to?(name.to_sym)
|
321
360
|
end
|
322
361
|
end
|
323
362
|
|
324
|
-
# These are the default setups
|
363
|
+
# These are the default setups
|
325
364
|
|
326
365
|
DSL::Maker.add_type(DSL::Maker::Any) do |attr, *args|
|
327
366
|
___set(attr, args[0]) unless args.empty?
|
data/lib/dsl/maker/version.rb
CHANGED
data/spec/args_spec.rb
CHANGED
@@ -1,24 +1,23 @@
|
|
1
1
|
# This will use a DSL that defines fruit
|
2
2
|
|
3
3
|
describe "A DSL with argument handling describing fruit" do
|
4
|
-
$Color = Struct.new(:name)
|
5
|
-
$Fruit = Struct.new(:name, :color)
|
6
|
-
|
7
4
|
describe "with one argument in add_entrypoint" do
|
8
|
-
dsl_class
|
9
|
-
|
10
|
-
:
|
11
|
-
|
12
|
-
|
13
|
-
|
5
|
+
let(:dsl_class) {
|
6
|
+
Class.new(DSL::Maker) do
|
7
|
+
add_entrypoint(:fruit, {
|
8
|
+
:name => String,
|
9
|
+
}) do |*args|
|
10
|
+
default(:name, args, 0)
|
11
|
+
Structs::Fruit.new(name, nil)
|
12
|
+
end
|
14
13
|
end
|
15
|
-
|
14
|
+
}
|
16
15
|
|
17
16
|
it "can handle nil" do
|
18
17
|
fruit = dsl_class.parse_dsl("
|
19
|
-
fruit
|
18
|
+
fruit {}
|
20
19
|
")
|
21
|
-
expect(fruit).to be_instance_of(
|
20
|
+
expect(fruit).to be_instance_of(Structs::Fruit)
|
22
21
|
expect(fruit.name).to be_nil
|
23
22
|
end
|
24
23
|
|
@@ -26,7 +25,7 @@ describe "A DSL with argument handling describing fruit" do
|
|
26
25
|
fruit = dsl_class.parse_dsl("
|
27
26
|
fruit { name 'banana' }
|
28
27
|
")
|
29
|
-
expect(fruit).to be_instance_of(
|
28
|
+
expect(fruit).to be_instance_of(Structs::Fruit)
|
30
29
|
expect(fruit.name).to eq('banana')
|
31
30
|
end
|
32
31
|
|
@@ -34,7 +33,7 @@ describe "A DSL with argument handling describing fruit" do
|
|
34
33
|
fruit = dsl_class.parse_dsl("
|
35
34
|
fruit 'banana'
|
36
35
|
")
|
37
|
-
expect(fruit).to be_instance_of(
|
36
|
+
expect(fruit).to be_instance_of(Structs::Fruit)
|
38
37
|
expect(fruit.name).to eq('banana')
|
39
38
|
end
|
40
39
|
|
@@ -45,29 +44,31 @@ describe "A DSL with argument handling describing fruit" do
|
|
45
44
|
name 'banana'
|
46
45
|
end
|
47
46
|
")
|
48
|
-
expect(fruit).to be_instance_of(
|
47
|
+
expect(fruit).to be_instance_of(Structs::Fruit)
|
49
48
|
expect(fruit.name).to eq('banana')
|
50
49
|
end
|
51
50
|
end
|
52
51
|
|
53
52
|
describe "with two arguments in add_entrypoint" do
|
54
|
-
dsl_class
|
55
|
-
|
56
|
-
:
|
57
|
-
|
58
|
-
|
59
|
-
|
60
|
-
|
53
|
+
let(:dsl_class) {
|
54
|
+
Class.new(DSL::Maker) do
|
55
|
+
add_entrypoint(:fruit, {
|
56
|
+
:name => String,
|
57
|
+
:color => String,
|
58
|
+
}) do |*args|
|
59
|
+
default('name', args)
|
60
|
+
default('color', args, 1)
|
61
61
|
|
62
|
-
|
62
|
+
Structs::Fruit.new(name, color)
|
63
|
+
end
|
63
64
|
end
|
64
|
-
|
65
|
+
}
|
65
66
|
|
66
67
|
it "can handle no arguments" do
|
67
68
|
fruit = dsl_class.parse_dsl("
|
68
|
-
fruit
|
69
|
+
fruit {}
|
69
70
|
")
|
70
|
-
expect(fruit).to be_instance_of(
|
71
|
+
expect(fruit).to be_instance_of(Structs::Fruit)
|
71
72
|
expect(fruit.name).to be_nil
|
72
73
|
expect(fruit.color).to be_nil
|
73
74
|
end
|
@@ -79,7 +80,7 @@ describe "A DSL with argument handling describing fruit" do
|
|
79
80
|
color 'yellow'
|
80
81
|
}
|
81
82
|
")
|
82
|
-
expect(fruit).to be_instance_of(
|
83
|
+
expect(fruit).to be_instance_of(Structs::Fruit)
|
83
84
|
expect(fruit.name).to eq('banana')
|
84
85
|
expect(fruit.color).to eq('yellow')
|
85
86
|
|
@@ -89,7 +90,7 @@ describe "A DSL with argument handling describing fruit" do
|
|
89
90
|
color 'green'
|
90
91
|
end
|
91
92
|
")
|
92
|
-
expect(fruit).to be_instance_of(
|
93
|
+
expect(fruit).to be_instance_of(Structs::Fruit)
|
93
94
|
expect(fruit.name).to eq('plantain')
|
94
95
|
expect(fruit.color).to eq('green')
|
95
96
|
end
|
@@ -98,27 +99,29 @@ describe "A DSL with argument handling describing fruit" do
|
|
98
99
|
fruit = dsl_class.parse_dsl("
|
99
100
|
fruit 'banana', 'yellow'
|
100
101
|
")
|
101
|
-
expect(fruit).to be_instance_of(
|
102
|
+
expect(fruit).to be_instance_of(Structs::Fruit)
|
102
103
|
expect(fruit.name).to eq('banana')
|
103
104
|
expect(fruit.color).to eq('yellow')
|
104
105
|
end
|
105
106
|
end
|
106
107
|
|
107
108
|
describe "with one argument in generate_dsl" do
|
108
|
-
|
109
|
-
|
110
|
-
:
|
111
|
-
:color => generate_dsl({
|
109
|
+
let (:dsl_class) {
|
110
|
+
Class.new(DSL::Maker) do
|
111
|
+
add_entrypoint(:fruit, {
|
112
112
|
:name => String,
|
113
|
-
|
113
|
+
:color => generate_dsl({
|
114
|
+
:name => String,
|
115
|
+
}) { |*args|
|
116
|
+
default('name', args, 0)
|
117
|
+
Structs::Color.new(name)
|
118
|
+
}
|
119
|
+
}) do |*args|
|
114
120
|
default('name', args, 0)
|
115
|
-
|
116
|
-
|
117
|
-
}) do |*args|
|
118
|
-
default('name', args, 0)
|
119
|
-
$Fruit.new(name, color)
|
121
|
+
Structs::Fruit.new(name, color)
|
122
|
+
end
|
120
123
|
end
|
121
|
-
|
124
|
+
}
|
122
125
|
|
123
126
|
it "can handle arguments for fruit, but attribute for color" do
|
124
127
|
fruit = dsl_class.parse_dsl("
|
@@ -128,9 +131,9 @@ describe "A DSL with argument handling describing fruit" do
|
|
128
131
|
}
|
129
132
|
end
|
130
133
|
")
|
131
|
-
expect(fruit).to be_instance_of(
|
134
|
+
expect(fruit).to be_instance_of(Structs::Fruit)
|
132
135
|
expect(fruit.name).to eq('banana')
|
133
|
-
expect(fruit.color).to be_instance_of(
|
136
|
+
expect(fruit.color).to be_instance_of(Structs::Color)
|
134
137
|
expect(fruit.color.name).to eq('yellow')
|
135
138
|
end
|
136
139
|
|
@@ -140,9 +143,9 @@ describe "A DSL with argument handling describing fruit" do
|
|
140
143
|
color 'yellow'
|
141
144
|
end
|
142
145
|
")
|
143
|
-
expect(fruit).to be_instance_of(
|
146
|
+
expect(fruit).to be_instance_of(Structs::Fruit)
|
144
147
|
expect(fruit.name).to eq('banana')
|
145
|
-
expect(fruit.color).to be_instance_of(
|
148
|
+
expect(fruit.color).to be_instance_of(Structs::Color)
|
146
149
|
expect(fruit.color.name).to eq('yellow')
|
147
150
|
end
|
148
151
|
|
@@ -154,9 +157,9 @@ describe "A DSL with argument handling describing fruit" do
|
|
154
157
|
end
|
155
158
|
end
|
156
159
|
")
|
157
|
-
expect(fruit).to be_instance_of(
|
160
|
+
expect(fruit).to be_instance_of(Structs::Fruit)
|
158
161
|
expect(fruit.name).to eq('banana')
|
159
|
-
expect(fruit.color).to be_instance_of(
|
162
|
+
expect(fruit.color).to be_instance_of(Structs::Color)
|
160
163
|
expect(fruit.color.name).to eq('green')
|
161
164
|
end
|
162
165
|
end
|
@@ -2,9 +2,6 @@
|
|
2
2
|
# does the right thing.
|
3
3
|
|
4
4
|
describe "Passing a class into generate_dsl" do
|
5
|
-
$Car = Struct.new(:maker, :wheel)
|
6
|
-
$Wheel = Struct.new(:maker, :size)
|
7
|
-
|
8
5
|
it "can do it" do
|
9
6
|
wheel_dsl = Class.new(DSL::Maker) do
|
10
7
|
add_entrypoint(:wheel, {
|
@@ -12,7 +9,7 @@ describe "Passing a class into generate_dsl" do
|
|
12
9
|
:maker => String,
|
13
10
|
}) do |*args|
|
14
11
|
default(:maker, args, 0)
|
15
|
-
|
12
|
+
Structs::Wheel.new(maker, size)
|
16
13
|
end
|
17
14
|
end
|
18
15
|
|
@@ -22,7 +19,7 @@ describe "Passing a class into generate_dsl" do
|
|
22
19
|
:wheel => wheel_dsl.entrypoint(:wheel),
|
23
20
|
}) do |*args|
|
24
21
|
default(:maker, args, 0)
|
25
|
-
|
22
|
+
Structs::Car.new(maker, wheel)
|
26
23
|
end
|
27
24
|
end
|
28
25
|
|
@@ -33,9 +30,9 @@ describe "Passing a class into generate_dsl" do
|
|
33
30
|
end
|
34
31
|
end
|
35
32
|
end
|
36
|
-
expect(car).to be_instance_of(
|
33
|
+
expect(car).to be_instance_of(Structs::Car)
|
37
34
|
expect(car.maker).to eq('honda')
|
38
|
-
expect(car.wheel).to be_instance_of(
|
35
|
+
expect(car.wheel).to be_instance_of(Structs::Wheel)
|
39
36
|
expect(car.wheel.maker).to eq('goodyear')
|
40
37
|
expect(car.wheel.size).to eq(26)
|
41
38
|
end
|
@@ -49,7 +46,7 @@ describe "Passing a class into generate_dsl" do
|
|
49
46
|
:maker => String,
|
50
47
|
}) do |*args|
|
51
48
|
default(:maker, args, 0)
|
52
|
-
|
49
|
+
Structs::Wheel.new(maker, size)
|
53
50
|
end
|
54
51
|
end
|
55
52
|
|
@@ -63,7 +60,7 @@ describe "Passing a class into generate_dsl" do
|
|
63
60
|
:wheel => wheel_dsl.entrypoint(:wheel),
|
64
61
|
}) do |*args|
|
65
62
|
default(:maker, args, 0)
|
66
|
-
|
63
|
+
Structs::Car.new(maker, wheel)
|
67
64
|
end
|
68
65
|
end
|
69
66
|
|
@@ -74,9 +71,9 @@ describe "Passing a class into generate_dsl" do
|
|
74
71
|
end
|
75
72
|
end
|
76
73
|
end
|
77
|
-
expect(car).to be_instance_of(
|
74
|
+
expect(car).to be_instance_of(Structs::Car)
|
78
75
|
expect(car.maker).to eq('honda')
|
79
|
-
expect(car.wheel).to be_instance_of(
|
76
|
+
expect(car.wheel).to be_instance_of(Structs::Wheel)
|
80
77
|
expect(car.wheel.maker).to eq('goodyear')
|
81
78
|
expect(car.wheel.size).to eq(26)
|
82
79
|
end
|
data/spec/error_spec.rb
CHANGED
@@ -11,10 +11,44 @@ describe "DSL::Maker validation" do
|
|
11
11
|
}.to raise_error('Block required for generate_dsl')
|
12
12
|
end
|
13
13
|
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
14
|
+
describe "for :parse_dsl" do
|
15
|
+
it "requires an entrypoint" do
|
16
|
+
expect {
|
17
|
+
Class.new(DSL::Maker).parse_dsl("")
|
18
|
+
}.to raise_error('Must call add_entrypoint before parse_dsl')
|
19
|
+
end
|
20
|
+
|
21
|
+
it "requires a string (check nil)" do
|
22
|
+
expect {
|
23
|
+
kls = Class.new(DSL::Maker)
|
24
|
+
kls.add_entrypoint(:x) {}
|
25
|
+
kls.parse_dsl
|
26
|
+
}.to raise_error('String required for parse_dsl')
|
27
|
+
end
|
28
|
+
|
29
|
+
it "requires a string (check number)" do
|
30
|
+
expect {
|
31
|
+
kls = Class.new(DSL::Maker)
|
32
|
+
kls.add_entrypoint(:x) {}
|
33
|
+
kls.parse_dsl(1)
|
34
|
+
}.to raise_error('String required for parse_dsl')
|
35
|
+
end
|
36
|
+
end
|
37
|
+
|
38
|
+
describe "for :execute_dsl" do
|
39
|
+
it "requires an entrypoint" do
|
40
|
+
expect {
|
41
|
+
Class.new(DSL::Maker).execute_dsl {}
|
42
|
+
}.to raise_error('Must call add_entrypoint before execute_dsl')
|
43
|
+
end
|
44
|
+
|
45
|
+
it "requires a block" do
|
46
|
+
expect {
|
47
|
+
kls = Class.new(DSL::Maker)
|
48
|
+
kls.add_entrypoint(:x) {}
|
49
|
+
kls.execute_dsl
|
50
|
+
}.to raise_error('Block required for execute_dsl')
|
51
|
+
end
|
18
52
|
end
|
19
53
|
|
20
54
|
describe "for attributes" do
|
data/spec/helper_spec.rb
CHANGED
@@ -1,15 +1,12 @@
|
|
1
1
|
# This uses a DSL that also provides a set of useful helpers.
|
2
2
|
#
|
3
3
|
describe "A DSL with helpers" do
|
4
|
-
$Car = Struct.new(:maker, :wheel)
|
5
|
-
$Wheel = Struct.new(:maker, :size)
|
6
|
-
|
7
4
|
it "can add a helper that's useful" do
|
8
5
|
dsl_class = Class.new(DSL::Maker) do
|
9
6
|
add_entrypoint(:car, {
|
10
7
|
:maker => String,
|
11
8
|
}) do
|
12
|
-
|
9
|
+
Structs::Car.new(maker)
|
13
10
|
end
|
14
11
|
|
15
12
|
add_helper(:transform) do |name|
|
@@ -22,7 +19,7 @@ describe "A DSL with helpers" do
|
|
22
19
|
maker transform('Honda')
|
23
20
|
}
|
24
21
|
")
|
25
|
-
expect(car).to be_instance_of(
|
22
|
+
expect(car).to be_instance_of(Structs::Car)
|
26
23
|
expect(car.maker).to eq('HONDA')
|
27
24
|
end
|
28
25
|
|
@@ -36,10 +33,10 @@ describe "A DSL with helpers" do
|
|
36
33
|
:wheel => generate_dsl({
|
37
34
|
:maker => String,
|
38
35
|
}) do
|
39
|
-
|
36
|
+
Structs::Wheel.new(maker)
|
40
37
|
end
|
41
38
|
}) do
|
42
|
-
|
39
|
+
Structs::Car.new(maker, wheel)
|
43
40
|
end
|
44
41
|
|
45
42
|
add_helper(:transform2) do |name|
|
@@ -55,9 +52,9 @@ describe "A DSL with helpers" do
|
|
55
52
|
}
|
56
53
|
}
|
57
54
|
")
|
58
|
-
expect(car).to be_instance_of(
|
55
|
+
expect(car).to be_instance_of(Structs::Car)
|
59
56
|
expect(car.maker).to eq('Honda')
|
60
|
-
expect(car.wheel).to be_instance_of(
|
57
|
+
expect(car.wheel).to be_instance_of(Structs::Wheel)
|
61
58
|
expect(car.wheel.maker).to eq('GOODYEAR')
|
62
59
|
end
|
63
60
|
end
|
data/spec/multi_level_spec.rb
CHANGED
@@ -4,8 +4,6 @@
|
|
4
4
|
# 1. Because we're creating classes on the fly, we must fully-qualify the Boolean
|
5
5
|
# class name. If we created real classes, the context would be provided for us.
|
6
6
|
describe 'A multi-level DSL making family-trees' do
|
7
|
-
$Person = Struct.new(:name, :child)
|
8
|
-
|
9
7
|
it "can handle a simple single-level parse of a two-level DSL" do
|
10
8
|
dsl_class = Class.new(DSL::Maker) do
|
11
9
|
add_entrypoint(:person, {
|
@@ -13,15 +11,15 @@ describe 'A multi-level DSL making family-trees' do
|
|
13
11
|
:child => generate_dsl({
|
14
12
|
:name => String,
|
15
13
|
}) {
|
16
|
-
|
14
|
+
Structs::Person.new(name)
|
17
15
|
},
|
18
16
|
}) do
|
19
|
-
|
17
|
+
Structs::Person.new(name, child)
|
20
18
|
end
|
21
19
|
end
|
22
20
|
|
23
21
|
person = dsl_class.parse_dsl('person { name "Tom" }')
|
24
|
-
expect(person).to be_instance_of(
|
22
|
+
expect(person).to be_instance_of(Structs::Person)
|
25
23
|
expect(person.name).to eq('Tom')
|
26
24
|
expect(person.child).to be_nil
|
27
25
|
end
|
@@ -33,10 +31,10 @@ describe 'A multi-level DSL making family-trees' do
|
|
33
31
|
:child => generate_dsl({
|
34
32
|
:name => String,
|
35
33
|
}) {
|
36
|
-
|
34
|
+
Structs::Person.new(name, nil)
|
37
35
|
},
|
38
36
|
}) do
|
39
|
-
|
37
|
+
Structs::Person.new(name, child)
|
40
38
|
end
|
41
39
|
end
|
42
40
|
|
@@ -48,9 +46,9 @@ describe 'A multi-level DSL making family-trees' do
|
|
48
46
|
}
|
49
47
|
}
|
50
48
|
")
|
51
|
-
expect(person).to be_instance_of(
|
49
|
+
expect(person).to be_instance_of(Structs::Person)
|
52
50
|
expect(person.name).to eq('Tom')
|
53
|
-
expect(person.child).to be_instance_of(
|
51
|
+
expect(person.child).to be_instance_of(Structs::Person)
|
54
52
|
expect(person.child.name).to eq('Bill')
|
55
53
|
end
|
56
54
|
|
@@ -63,13 +61,13 @@ describe 'A multi-level DSL making family-trees' do
|
|
63
61
|
:child => generate_dsl({
|
64
62
|
:name => String,
|
65
63
|
}) {
|
66
|
-
|
64
|
+
Structs::Person.new(name, nil)
|
67
65
|
},
|
68
66
|
}) {
|
69
|
-
|
67
|
+
Structs::Person.new(name, child)
|
70
68
|
},
|
71
69
|
}) do
|
72
|
-
|
70
|
+
Structs::Person.new(name, child)
|
73
71
|
end
|
74
72
|
end
|
75
73
|
|
@@ -84,11 +82,11 @@ describe 'A multi-level DSL making family-trees' do
|
|
84
82
|
}
|
85
83
|
}
|
86
84
|
")
|
87
|
-
expect(person).to be_instance_of(
|
85
|
+
expect(person).to be_instance_of(Structs::Person)
|
88
86
|
expect(person.name).to eq('Tom')
|
89
|
-
expect(person.child).to be_instance_of(
|
87
|
+
expect(person.child).to be_instance_of(Structs::Person)
|
90
88
|
expect(person.child.name).to eq('Bill')
|
91
|
-
expect(person.child.child).to be_instance_of(
|
89
|
+
expect(person.child.child).to be_instance_of(Structs::Person)
|
92
90
|
expect(person.child.child.name).to eq('Judith')
|
93
91
|
end
|
94
92
|
|
@@ -98,7 +96,7 @@ describe 'A multi-level DSL making family-trees' do
|
|
98
96
|
person_dsl = add_entrypoint(:person, {
|
99
97
|
:name => String,
|
100
98
|
}) do
|
101
|
-
|
99
|
+
Structs::Person.new(name, child)
|
102
100
|
end
|
103
101
|
build_dsl_element(person_dsl, :child, person_dsl)
|
104
102
|
end
|
@@ -145,19 +143,18 @@ describe 'A multi-level DSL making family-trees' do
|
|
145
143
|
'Adam', 'Seth', 'Enos', 'Cainan', 'Mahalaleel', 'Jared',
|
146
144
|
'Enoch', 'Methuselah', 'Lamech', 'Noah', 'Shem',
|
147
145
|
].each do |name|
|
148
|
-
expect(person).to be_instance_of(
|
146
|
+
expect(person).to be_instance_of(Structs::Person)
|
149
147
|
expect(person.name).to eq(name)
|
150
148
|
person = person.child
|
151
149
|
end
|
152
150
|
end
|
153
151
|
|
154
152
|
it "can handle two axes of recursion" do
|
155
|
-
OtherPerson = Struct.new(:name, :mother, :father)
|
156
153
|
dsl_class = Class.new(DSL::Maker) do
|
157
154
|
person_dsl = add_entrypoint(:person, {
|
158
155
|
:name => String,
|
159
156
|
}) do
|
160
|
-
OtherPerson.new(name, mother, father)
|
157
|
+
Structs::OtherPerson.new(name, mother, father)
|
161
158
|
end
|
162
159
|
build_dsl_element(person_dsl, :mother, person_dsl)
|
163
160
|
build_dsl_element(person_dsl, :father, person_dsl)
|
@@ -175,15 +172,15 @@ describe 'A multi-level DSL making family-trees' do
|
|
175
172
|
}
|
176
173
|
")
|
177
174
|
|
178
|
-
expect(person).to be_instance_of(OtherPerson)
|
175
|
+
expect(person).to be_instance_of(Structs::OtherPerson)
|
179
176
|
expect(person.name).to eq('John Smith')
|
180
177
|
|
181
178
|
mother = person.mother
|
182
|
-
expect(mother).to be_instance_of(OtherPerson)
|
179
|
+
expect(mother).to be_instance_of(Structs::OtherPerson)
|
183
180
|
expect(mother.name).to eq('Mary Smith')
|
184
181
|
|
185
182
|
father = person.father
|
186
|
-
expect(father).to be_instance_of(OtherPerson)
|
183
|
+
expect(father).to be_instance_of(Structs::OtherPerson)
|
187
184
|
expect(father.name).to eq('Tom Smith')
|
188
185
|
end
|
189
186
|
end
|
@@ -1,15 +1,12 @@
|
|
1
|
-
# This will use a DSL that defines
|
1
|
+
# This will use a DSL that defines Structs::Cars
|
2
2
|
|
3
3
|
describe "A DSL describing cars used with multiple invocations" do
|
4
|
-
$Car = Struct.new(:maker, :wheel)
|
5
|
-
$Truck = Struct.new(:maker, :wheel)
|
6
|
-
|
7
4
|
it "returns two items in the right order" do
|
8
5
|
dsl_class = Class.new(DSL::Maker) do
|
9
6
|
add_entrypoint(:car, {
|
10
|
-
:maker =>
|
7
|
+
:maker => DSL::Maker::Any,
|
11
8
|
}) do
|
12
|
-
|
9
|
+
Structs::Car.new(maker)
|
13
10
|
end
|
14
11
|
end
|
15
12
|
|
@@ -19,9 +16,9 @@ describe "A DSL describing cars used with multiple invocations" do
|
|
19
16
|
")
|
20
17
|
expect(cars).to be_instance_of(Array)
|
21
18
|
expect(cars.length).to eq(2)
|
22
|
-
expect(cars[0]).to be_instance_of(
|
19
|
+
expect(cars[0]).to be_instance_of(Structs::Car)
|
23
20
|
expect(cars[0].maker).to eq('Honda')
|
24
|
-
expect(cars[1]).to be_instance_of(
|
21
|
+
expect(cars[1]).to be_instance_of(Structs::Car)
|
25
22
|
expect(cars[1].maker).to eq('Acura')
|
26
23
|
end
|
27
24
|
|
@@ -30,12 +27,12 @@ describe "A DSL describing cars used with multiple invocations" do
|
|
30
27
|
add_entrypoint(:car, {
|
31
28
|
:maker => String,
|
32
29
|
}) do
|
33
|
-
|
30
|
+
Structs::Car.new(maker)
|
34
31
|
end
|
35
32
|
add_entrypoint(:truck, {
|
36
33
|
:maker => String,
|
37
34
|
}) do
|
38
|
-
|
35
|
+
Structs::Truck.new(maker)
|
39
36
|
end
|
40
37
|
end
|
41
38
|
|
@@ -46,11 +43,11 @@ describe "A DSL describing cars used with multiple invocations" do
|
|
46
43
|
")
|
47
44
|
expect(vehicles).to be_instance_of(Array)
|
48
45
|
expect(vehicles.length).to eq(3)
|
49
|
-
expect(vehicles[0]).to be_instance_of(
|
46
|
+
expect(vehicles[0]).to be_instance_of(Structs::Truck)
|
50
47
|
expect(vehicles[0].maker).to eq('Ford')
|
51
|
-
expect(vehicles[1]).to be_instance_of(
|
48
|
+
expect(vehicles[1]).to be_instance_of(Structs::Car)
|
52
49
|
expect(vehicles[1].maker).to eq('Honda')
|
53
|
-
expect(vehicles[2]).to be_instance_of(
|
50
|
+
expect(vehicles[2]).to be_instance_of(Structs::Truck)
|
54
51
|
expect(vehicles[2].maker).to eq('Toyota')
|
55
52
|
end
|
56
53
|
|
@@ -59,12 +56,12 @@ describe "A DSL describing cars used with multiple invocations" do
|
|
59
56
|
add_entrypoint(:car, {
|
60
57
|
:maker => String,
|
61
58
|
}) do
|
62
|
-
|
59
|
+
Structs::Car.new(maker)
|
63
60
|
end
|
64
61
|
add_entrypoint(:truck, {
|
65
62
|
:maker => String,
|
66
63
|
}) do
|
67
|
-
|
64
|
+
Structs::Truck.new(maker)
|
68
65
|
end
|
69
66
|
end
|
70
67
|
|
@@ -75,11 +72,11 @@ describe "A DSL describing cars used with multiple invocations" do
|
|
75
72
|
end
|
76
73
|
expect(vehicles).to be_instance_of(Array)
|
77
74
|
expect(vehicles.length).to eq(3)
|
78
|
-
expect(vehicles[0]).to be_instance_of(
|
75
|
+
expect(vehicles[0]).to be_instance_of(Structs::Truck)
|
79
76
|
expect(vehicles[0].maker).to eq('Ford')
|
80
|
-
expect(vehicles[1]).to be_instance_of(
|
77
|
+
expect(vehicles[1]).to be_instance_of(Structs::Car)
|
81
78
|
expect(vehicles[1].maker).to eq('Honda')
|
82
|
-
expect(vehicles[2]).to be_instance_of(
|
79
|
+
expect(vehicles[2]).to be_instance_of(Structs::Truck)
|
83
80
|
expect(vehicles[2].maker).to eq('Toyota')
|
84
81
|
end
|
85
82
|
end
|
data/spec/single_level_spec.rb
CHANGED
@@ -11,22 +11,46 @@
|
|
11
11
|
# 1. Because we're creating classes on the fly, we must fully-qualify the Boolean
|
12
12
|
# class name. If we created real classes, the context would be provided for us.
|
13
13
|
describe 'A single-level DSL for pizza' do
|
14
|
-
$toppings
|
15
|
-
$Pizza = Struct.new(*$toppings)
|
16
|
-
|
14
|
+
# This uses $toppings defined in spec/spec_helper.rb
|
17
15
|
def verify_pizza(pizza, values={})
|
18
|
-
expect(pizza).to be_instance_of(
|
16
|
+
expect(pizza).to be_instance_of(Structs::Pizza)
|
19
17
|
$toppings.each do |topping|
|
20
18
|
expect(pizza.send(topping)).to eq(values[topping])
|
21
19
|
end
|
22
20
|
end
|
23
21
|
|
22
|
+
describe 'handles an empty DSL' do
|
23
|
+
it 'with :parse_dsl' do
|
24
|
+
dsl_class = Class.new(DSL::Maker) do
|
25
|
+
add_entrypoint(:pizza) {
|
26
|
+
Structs::Pizza.new
|
27
|
+
}
|
28
|
+
end
|
29
|
+
|
30
|
+
pizza = dsl_class.parse_dsl('')
|
31
|
+
expect(pizza).to be(nil)
|
32
|
+
end
|
33
|
+
|
34
|
+
it 'with :execute_dsl' do
|
35
|
+
dsl_class = Class.new(DSL::Maker) do
|
36
|
+
add_entrypoint(:pizza) {
|
37
|
+
Structs::Pizza.new
|
38
|
+
}
|
39
|
+
end
|
40
|
+
|
41
|
+
pizza = dsl_class.execute_dsl {}
|
42
|
+
expect(pizza).to be(nil)
|
43
|
+
end
|
44
|
+
end
|
45
|
+
|
24
46
|
it 'makes a blank pizza' do
|
25
47
|
dsl_class = Class.new(DSL::Maker) do
|
26
|
-
add_entrypoint(:pizza) {
|
48
|
+
add_entrypoint(:pizza) {
|
49
|
+
Structs::Pizza.new
|
50
|
+
}
|
27
51
|
end
|
28
52
|
|
29
|
-
pizza = dsl_class.parse_dsl('pizza')
|
53
|
+
pizza = dsl_class.parse_dsl('pizza {}')
|
30
54
|
verify_pizza(pizza)
|
31
55
|
end
|
32
56
|
|
@@ -36,7 +60,7 @@ describe 'A single-level DSL for pizza' do
|
|
36
60
|
add_entrypoint(:pizza, {
|
37
61
|
:cheese => DSL::Maker::Boolean,
|
38
62
|
}) do
|
39
|
-
|
63
|
+
Structs::Pizza.new(cheese, nil, nil, nil)
|
40
64
|
end
|
41
65
|
end
|
42
66
|
|
@@ -84,7 +108,7 @@ describe 'A single-level DSL for pizza' do
|
|
84
108
|
add_entrypoint(:pizza, {
|
85
109
|
:sauce => String,
|
86
110
|
}) do
|
87
|
-
|
111
|
+
Structs::Pizza.new(nil, nil, nil, sauce)
|
88
112
|
end
|
89
113
|
end
|
90
114
|
|
@@ -109,7 +133,7 @@ describe 'A single-level DSL for pizza' do
|
|
109
133
|
:pepperoni => DSL::Maker::Boolean,
|
110
134
|
:sauce => String,
|
111
135
|
}) do
|
112
|
-
|
136
|
+
Structs::Pizza.new(cheese, pepperoni, bacon, sauce)
|
113
137
|
end
|
114
138
|
|
115
139
|
add_entrypoint(:pizza, toppings_dsl)
|
@@ -139,7 +163,7 @@ describe 'A single-level DSL for pizza' do
|
|
139
163
|
:pepperoni => DSL::Maker::Boolean,
|
140
164
|
:sauce => String,
|
141
165
|
}) do
|
142
|
-
|
166
|
+
Structs::Pizza.new(cheese, pepperoni, bacon, sauce)
|
143
167
|
end
|
144
168
|
|
145
169
|
add_entrypoint(:pizza, toppings_dsl)
|
data/spec/spec_helper.rb
CHANGED
@@ -29,3 +29,19 @@ unless on_1_8?
|
|
29
29
|
end
|
30
30
|
|
31
31
|
require 'dsl/maker'
|
32
|
+
|
33
|
+
module Structs
|
34
|
+
Car = Struct.new(:maker, :wheel)
|
35
|
+
Truck = Struct.new(:maker, :wheel)
|
36
|
+
Wheel = Struct.new(:maker, :size)
|
37
|
+
|
38
|
+
Person = Struct.new(:name, :child)
|
39
|
+
OtherPerson = Struct.new(:name, :mother, :father)
|
40
|
+
|
41
|
+
$toppings = [:cheese, :pepperoni, :bacon, :sauce]
|
42
|
+
Pizza = Struct.new(*$toppings)
|
43
|
+
|
44
|
+
Color = Struct.new(:name)
|
45
|
+
Fruit = Struct.new(:name, :color)
|
46
|
+
end
|
47
|
+
|
data/spec/validation_spec.rb
CHANGED
@@ -9,7 +9,7 @@ describe "Validations" do
|
|
9
9
|
:pepperoni => DSL::Maker::Boolean,
|
10
10
|
:sauce => String,
|
11
11
|
}) do
|
12
|
-
|
12
|
+
Structs::Pizza.new(cheese, pepperoni, bacon, sauce)
|
13
13
|
end
|
14
14
|
|
15
15
|
add_entrypoint(:pizza, toppings_dsl)
|
@@ -26,4 +26,30 @@ describe "Validations" do
|
|
26
26
|
dsl_class.parse_dsl("pizza { sauce :extra }")
|
27
27
|
}.to_not raise_error
|
28
28
|
end
|
29
|
+
|
30
|
+
it "validates at the dsl_class level" do
|
31
|
+
dsl_class = Class.new(DSL::Maker) do
|
32
|
+
toppings_dsl = generate_dsl({
|
33
|
+
:cheese => DSL::Maker::Boolean,
|
34
|
+
:bacon => DSL::Maker::Boolean,
|
35
|
+
:pepperoni => DSL::Maker::Boolean,
|
36
|
+
:sauce => String,
|
37
|
+
}) do
|
38
|
+
Structs::Pizza.new(cheese, pepperoni, bacon, sauce)
|
39
|
+
end
|
40
|
+
toppings_dsl.add_verification do |item|
|
41
|
+
return "Pizza must have sauce" unless item.sauce
|
42
|
+
end
|
43
|
+
|
44
|
+
add_entrypoint(:pizza, toppings_dsl)
|
45
|
+
end
|
46
|
+
|
47
|
+
expect {
|
48
|
+
dsl_class.parse_dsl("pizza {}")
|
49
|
+
}.to raise_error("Pizza must have sauce")
|
50
|
+
|
51
|
+
expect {
|
52
|
+
dsl_class.parse_dsl("pizza { sauce :extra }")
|
53
|
+
}.to_not raise_error
|
54
|
+
end
|
29
55
|
end
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: dsl_maker
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.0.
|
4
|
+
version: 0.0.8
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Rob Kinyon
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2015-08-
|
11
|
+
date: 2015-08-07 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: docile
|