alki-dsl 0.3.4 → 0.4.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/README.adoc +277 -0
- data/alki-dsl.gemspec +2 -1
- data/lib/alki/class_builder.rb +6 -8
- data/lib/alki/dsl.rb +2 -40
- data/lib/alki/dsl/evaluator.rb +16 -8
- data/lib/alki/dsl/version.rb +1 -1
- data/lib/alki/dsls/class.rb +1 -1
- data/lib/alki/dsls/dsl.rb +4 -4
- data/test/feature/config_test.rb +2 -2
- data/test/fixtures/example/lib/alki_loader.rb +2 -0
- data/test/integration/class_builder_test.rb +17 -13
- metadata +21 -10
- data/lib/alki/dsl/builder.rb +0 -42
- data/lib/alki/dsl/loader.rb +0 -28
- data/lib/alki/dsl/registry.rb +0 -76
- data/lib/alki/dsls/dsl_config.rb +0 -28
- data/test/fixtures/example/config/dsls.rb +0 -4
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 74a68f3b18db59488fcbe8c2037b3933f33aef5d
|
4
|
+
data.tar.gz: '070150789ebbd1bc8b2615395c3e803c0b259913'
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 420aa794f86a9c2339e784cabd08260d304f500e4c6d6b9731e4b66c7d607153f89a6e1fb3f570a54040a475b7c16e0dbed658f237fd5bab1ee163cbc1e84742
|
7
|
+
data.tar.gz: 2a8a0cc25a0fe87a36fe278f5c3ee4768d4816650ead0354dda92e4861ff15e418e5b263ac04e78ef009db02303da87cacc6d996cf667e252dda54d5c2ec2eea
|
data/README.adoc
ADDED
@@ -0,0 +1,277 @@
|
|
1
|
+
# Alki::Dsl
|
2
|
+
|
3
|
+
Alki::Dsl is a library for building DSLs. The resulting DSL buliders can be used standalone or as builders for
|
4
|
+
https://github.com/alki-project/alki-loader[Alki::Loader].
|
5
|
+
|
6
|
+
Alki::Dsl also allows composing and extending DSLs and comes with built in DSLs for building classes and
|
7
|
+
new DSLs.
|
8
|
+
|
9
|
+
## Synopsis
|
10
|
+
|
11
|
+
```ruby
|
12
|
+
require 'alki/dsl'
|
13
|
+
|
14
|
+
strings_dsl = Alki::Dsl.build 'alki/dsls/dsl' do
|
15
|
+
init do
|
16
|
+
@strings = []
|
17
|
+
end
|
18
|
+
|
19
|
+
dsl_method :add do |val|
|
20
|
+
@strings << val
|
21
|
+
end
|
22
|
+
|
23
|
+
finish do
|
24
|
+
ctx[:result] = @strings.join("\n")
|
25
|
+
end
|
26
|
+
end
|
27
|
+
|
28
|
+
val = strings_dsl.build do
|
29
|
+
add "hello"
|
30
|
+
add "world"
|
31
|
+
end
|
32
|
+
|
33
|
+
puts val
|
34
|
+
|
35
|
+
# output:
|
36
|
+
# hello
|
37
|
+
# world
|
38
|
+
```
|
39
|
+
|
40
|
+
## Installation
|
41
|
+
|
42
|
+
Add this line to your application's Gemfile:
|
43
|
+
|
44
|
+
```ruby
|
45
|
+
gem 'alki-dsl'
|
46
|
+
```
|
47
|
+
|
48
|
+
And then execute:
|
49
|
+
|
50
|
+
$ bundle
|
51
|
+
|
52
|
+
Or install it yourself as:
|
53
|
+
|
54
|
+
$ gem install alki-dsl
|
55
|
+
|
56
|
+
## Usage
|
57
|
+
|
58
|
+
All DSLs created with Alki::Dsl are Class objects with `::build` methods. These build methods take an optional
|
59
|
+
hash of parameters, along with a ruby block to be evaluated. DSLs created with Alki::Dsl cannot directly
|
60
|
+
strings.
|
61
|
+
|
62
|
+
While DSLs can be created with Alki::Dsl manually, the easiest way is to use the provided "dsl" DSL. Each
|
63
|
+
DSL defines any number of "dsl methods", which are methods that will be exposed to the user of the DSL.
|
64
|
+
The DSL can also define "init" and "finish" blocks which will be run before and after the DSL is evaluated.
|
65
|
+
|
66
|
+
DSLs can "require" other DSLs, causing both of their methods to be available to the user.
|
67
|
+
|
68
|
+
DSLs are always evaluated within a new instance, so instance variables can be used to store state, however
|
69
|
+
these will not be accessible to other DSLs being evaluated at the same time (via requires).
|
70
|
+
|
71
|
+
Data is passed into and out of a DSL via the `ctx` hash. It is initially set using the hash provided to the
|
72
|
+
DSLs build method, but can be updated by code in the DSL. Unlike instance variables, all DSLs being run share
|
73
|
+
a single ctx hash, so it can be used to pass data between them.
|
74
|
+
|
75
|
+
The result of the build method will either be the full ctx hash, or just the value of `ctx[:result]` if it
|
76
|
+
exists (including if it's set to false or nil).
|
77
|
+
|
78
|
+
```ruby
|
79
|
+
require 'alki/dsl'
|
80
|
+
|
81
|
+
strings_dsl = Alki::Dsl.build 'alki/dsls/dsl' do
|
82
|
+
init do # Init block, runs before any dsl methods are called
|
83
|
+
ctx[:strings] = [] # Store strings in ctx so other DSLs can access them
|
84
|
+
end
|
85
|
+
|
86
|
+
dsl_method :add do |val| # Simple dsl method called "add"
|
87
|
+
ctx[:strings] << val
|
88
|
+
end
|
89
|
+
|
90
|
+
finish do # Finish block, runs after any dsls methods are called
|
91
|
+
sep = ctx[:separator] || "\n" # Allow caller of dsl to set the separator
|
92
|
+
ctx[:result] = ctx[:strings].join(sep) # Set ctx[:result] so we only return this value
|
93
|
+
end
|
94
|
+
end
|
95
|
+
|
96
|
+
my_dsl = Alki::Dsl.build 'alki/dsls/dsl' do
|
97
|
+
require_dsl strings_dsl # Require other DSL. Value can also be a "load" string (see Alki::Loader section)
|
98
|
+
|
99
|
+
init do # This init block will be run *after* the strings_dsl one
|
100
|
+
@transform = nil
|
101
|
+
end
|
102
|
+
|
103
|
+
dsl_method :transform do |&blk|
|
104
|
+
@transform = blk # Don't need to share this, so just use instance variable
|
105
|
+
end
|
106
|
+
|
107
|
+
finish do # This finish block will be run *before* the strings_dsl one.
|
108
|
+
if @transform
|
109
|
+
ctx[:strings].map! &@transform
|
110
|
+
end
|
111
|
+
end
|
112
|
+
end
|
113
|
+
|
114
|
+
result = my_dsl.build(separator: ', ') do # Pass in a separator via data hash
|
115
|
+
transform(&:capitalize)
|
116
|
+
|
117
|
+
add "hello"
|
118
|
+
add "world"
|
119
|
+
end
|
120
|
+
|
121
|
+
puts result
|
122
|
+
|
123
|
+
# output: Hello, World
|
124
|
+
|
125
|
+
```
|
126
|
+
|
127
|
+
### Using with Alki::Loader
|
128
|
+
|
129
|
+
https://github.com/alki-project/alki-loader[Alki::Loader] is library that provides extra functionality
|
130
|
+
over base Ruby around loading source files. One of its features is to associate "builder" objects with files
|
131
|
+
or directories so that the code within them is processed by the builder object when they are loaded. More
|
132
|
+
documentation can be found at the Alki::Loader https://github.com/alki-project/alki-loader[github page].
|
133
|
+
|
134
|
+
The DSLs created by Alki::Dsl can be used as Alki::Loader builder objects, allowing DSLs to be used to define
|
135
|
+
classes and modules. In addition, because the provided "dsl" DSL creates classes, it can also be used with
|
136
|
+
Alki::Loader to allow defining your DSLs in standalone source files.
|
137
|
+
|
138
|
+
To get started, in your project create a dsls directory at something like `lib/my_project/dsls`. This will
|
139
|
+
be where we put our DSL source files.
|
140
|
+
|
141
|
+
To register it create a `lib/alki_loader.rb` file:
|
142
|
+
|
143
|
+
.lib/alki_loader.rb
|
144
|
+
```ruby
|
145
|
+
Alki::Loader.register 'my_project/dsls', builder: 'alki/dsls/dsl'
|
146
|
+
```
|
147
|
+
|
148
|
+
****
|
149
|
+
*Note*: This registers the builder using a string. This is a "load" string and is used frequently in Alki
|
150
|
+
projects. When used, the string will be `require`-d and then transformed into a constant name
|
151
|
+
(so "alki/dsls/dsl" becomes Alki::Dsls::Dsl) and the resulting class will be used. In addition to less
|
152
|
+
typing, this also allows lazy loading behavior, where the file and class are only loaded if needed.
|
153
|
+
|
154
|
+
The DSL class can be passed directly instead of the load string.
|
155
|
+
****
|
156
|
+
|
157
|
+
Now a DSL definition file can be created in `lib/my_project/dsls`. Revisiting the previous example, a "strings"
|
158
|
+
can be created.
|
159
|
+
|
160
|
+
.lib/my_project/dsls/strings.rb
|
161
|
+
```ruby
|
162
|
+
Alki do
|
163
|
+
init do
|
164
|
+
ctx[:strings] = []
|
165
|
+
end
|
166
|
+
|
167
|
+
dsl_method :add do |val|
|
168
|
+
ctx[:strings] << val
|
169
|
+
end
|
170
|
+
|
171
|
+
finish do
|
172
|
+
sep = ctx[:separator] || "\n"
|
173
|
+
ctx[:result] = ctx[:strings].join(sep)
|
174
|
+
end
|
175
|
+
end
|
176
|
+
```
|
177
|
+
|
178
|
+
The `Alki do ... end` block is part of Alki::Loader and is required. The rest of the DSL is the same
|
179
|
+
as before. When this file is loaded by Ruby, it will create a DSL class called MyProject::Dsls::Strings.
|
180
|
+
|
181
|
+
To use we can require the file normally (making sure to add `lib` to the load path and requiring 'alki/dsl'
|
182
|
+
first).
|
183
|
+
|
184
|
+
```
|
185
|
+
$ irb -Ilib
|
186
|
+
> require 'alki/dsl'
|
187
|
+
> require 'my_project/dsls/strings'
|
188
|
+
> MyProject::Dsls::Strings.build do
|
189
|
+
> add "hello"
|
190
|
+
> add "world"
|
191
|
+
> end
|
192
|
+
=> "hello\nworld"
|
193
|
+
>
|
194
|
+
```
|
195
|
+
|
196
|
+
The second DSL can now be setup the same way. Note that the `require_dsl` value has been replaced with a load
|
197
|
+
string.
|
198
|
+
|
199
|
+
.lib/my_project/dsls/transformable_strings.rb
|
200
|
+
```ruby
|
201
|
+
Alki do
|
202
|
+
require_dsl 'my_project/dsls/strings'
|
203
|
+
|
204
|
+
init do
|
205
|
+
@transform = nil
|
206
|
+
end
|
207
|
+
|
208
|
+
dsl_method :transform do |&blk|
|
209
|
+
@transform = blk
|
210
|
+
end
|
211
|
+
|
212
|
+
finish do
|
213
|
+
if @transform
|
214
|
+
ctx[:strings].map! &@transform
|
215
|
+
end
|
216
|
+
end
|
217
|
+
end
|
218
|
+
```
|
219
|
+
|
220
|
+
So what if we want to use our DSL with Alki::Loader as well? First, Alki::Loader requires builders to
|
221
|
+
define a constant with the correct name, so we need code to do that. Alki::Dsl comes with a "class" DSL
|
222
|
+
that makes this easy. First lets create a new DSL that adapts our transformable_strings DSL into a module
|
223
|
+
builder.
|
224
|
+
|
225
|
+
.lib/my_project/dsls/strings_class.rb
|
226
|
+
```ruby
|
227
|
+
Alki do
|
228
|
+
require_dsl 'alki/dsls/class'
|
229
|
+
require_dsl 'my_project/dsls/transformable_strings', :after # This makes it's finish runs before ours
|
230
|
+
|
231
|
+
finish do
|
232
|
+
# Helpers provided by alki/dsls/class
|
233
|
+
create_as_module # Don't need a class, just a module
|
234
|
+
value = ctx[:result]
|
235
|
+
add_class_method(:value) { value }
|
236
|
+
end
|
237
|
+
end
|
238
|
+
```
|
239
|
+
|
240
|
+
Now we can create a new directory, register it with Alki::Loader, and add a file that uses the DSL. Note
|
241
|
+
that we can set separator in the Alki::Loader register call. Any data values set here are passed in
|
242
|
+
as `ctx` in the DSL.
|
243
|
+
|
244
|
+
.lib/alki_loader.rb
|
245
|
+
```ruby
|
246
|
+
Alki::Loader.register 'my_project/dsls', builder: 'alki/dsls/dsl'
|
247
|
+
Alki::Loader.register 'my_project/strings', builder: 'my_project/dsls/strings_class', separator: ', '
|
248
|
+
```
|
249
|
+
|
250
|
+
.lib/my_project/strings/hello_world.rb
|
251
|
+
```ruby
|
252
|
+
Alki do
|
253
|
+
transform &:capitalize
|
254
|
+
|
255
|
+
add "hello"
|
256
|
+
add "world"
|
257
|
+
end
|
258
|
+
```
|
259
|
+
|
260
|
+
```
|
261
|
+
$ irb -Ilib
|
262
|
+
> require 'alki/dsl'
|
263
|
+
> require 'my_project/strings/hello_world'
|
264
|
+
> MyProject::Strings::HelloWorld.value
|
265
|
+
=> "Hello, World"
|
266
|
+
>
|
267
|
+
```
|
268
|
+
|
269
|
+
## Contributing
|
270
|
+
|
271
|
+
Bug reports and pull requests are welcome on GitHub at https://github.com/alki-project/alki-dsl. This project is intended to be a safe, welcoming space for collaboration, and contributors are expected to adhere to the http://contributor-covenant.org[Contributor Covenant] code of conduct.
|
272
|
+
|
273
|
+
|
274
|
+
## License
|
275
|
+
|
276
|
+
The gem is available as open source under the terms of the http://opensource.org/licenses/MIT[MIT License].
|
277
|
+
|
data/alki-dsl.gemspec
CHANGED
@@ -17,6 +17,7 @@ Gem::Specification.new do |spec|
|
|
17
17
|
spec.test_files = spec.files.grep(%r{^(test|spec|features)/})
|
18
18
|
spec.require_paths = ["lib"]
|
19
19
|
|
20
|
-
spec.add_dependency 'alki-support', '~> 0.
|
20
|
+
spec.add_dependency 'alki-support', '~> 0.7'
|
21
|
+
spec.add_dependency 'alki-loader', '~> 0.1'
|
21
22
|
spec.add_development_dependency 'minitest', '~> 5.9', '>= 5.9.1'
|
22
23
|
end
|
data/lib/alki/class_builder.rb
CHANGED
@@ -17,11 +17,9 @@ module Alki
|
|
17
17
|
end
|
18
18
|
|
19
19
|
def self.build(data)
|
20
|
-
class_name = data[:
|
21
|
-
if !class_name && data[:name]
|
22
|
-
class_name = Alki::Support.classify(
|
23
|
-
data[:prefix].empty? ? data[:name] : "#{data[:prefix]}/#{data[:name]}"
|
24
|
-
)
|
20
|
+
class_name = data[:constant_name]
|
21
|
+
if !class_name && data[:name]
|
22
|
+
class_name = Alki::Support.classify(data[:name])
|
25
23
|
end
|
26
24
|
|
27
25
|
klass = Alki::Support.constantize class_name, data[:parent_class] if class_name
|
@@ -40,7 +38,7 @@ module Alki
|
|
40
38
|
raise "#{class_name} already exists as is a #{klass.class}"
|
41
39
|
end
|
42
40
|
super_class = if data[:super_class]
|
43
|
-
Alki
|
41
|
+
Alki.load data[:super_class]
|
44
42
|
else
|
45
43
|
Object
|
46
44
|
end
|
@@ -59,8 +57,8 @@ module Alki
|
|
59
57
|
if data[:secondary_classes]
|
60
58
|
data[:secondary_classes].each do |data|
|
61
59
|
if data[:subclass]
|
62
|
-
data = data.merge(parent_class: klass,
|
63
|
-
elsif !data[:
|
60
|
+
data = data.merge(parent_class: klass,constant_name: data[:subclass])
|
61
|
+
elsif !data[:constant_name] && !data[:name]
|
64
62
|
raise ArgumentError.new("Secondary classes must have names")
|
65
63
|
end
|
66
64
|
build data
|
data/lib/alki/dsl.rb
CHANGED
@@ -1,48 +1,10 @@
|
|
1
|
-
require 'alki/
|
2
|
-
require 'alki/dsl/registry'
|
1
|
+
require 'alki/loader'
|
3
2
|
|
4
3
|
module Alki
|
5
4
|
module Dsl
|
6
|
-
@loaded = {}
|
7
|
-
def self.[]=(path,value)
|
8
|
-
@loaded[path] =value
|
9
|
-
end
|
10
|
-
|
11
|
-
def self.[](path)
|
12
|
-
@loaded[path]
|
13
|
-
end
|
14
|
-
|
15
|
-
def self.register(*args)
|
16
|
-
Alki::Dsl::Registry.register *args
|
17
|
-
end
|
18
|
-
|
19
|
-
def self.register_dir(*args)
|
20
|
-
Alki::Dsl::Registry.register_dir *args
|
21
|
-
end
|
22
|
-
|
23
|
-
def self.load(path)
|
24
|
-
path = File.absolute_path(path)
|
25
|
-
require path
|
26
|
-
self[path]
|
27
|
-
end
|
28
|
-
|
29
5
|
def self.build(name,data={},&blk)
|
30
|
-
Alki
|
6
|
+
Alki.load(name).build data, &blk
|
31
7
|
end
|
32
8
|
end
|
33
9
|
end
|
34
10
|
|
35
|
-
module Kernel
|
36
|
-
def Alki(builder=nil,&blk)
|
37
|
-
if blk
|
38
|
-
path = caller_locations(1,1)[0].absolute_path
|
39
|
-
result = if builder
|
40
|
-
builder.build({}, &blk)
|
41
|
-
else
|
42
|
-
Alki::Dsl::Registry.build path, &blk
|
43
|
-
end
|
44
|
-
Alki::Dsl[path] = result
|
45
|
-
end
|
46
|
-
::Alki
|
47
|
-
end
|
48
|
-
end
|
data/lib/alki/dsl/evaluator.rb
CHANGED
@@ -20,29 +20,37 @@ module Alki
|
|
20
20
|
@finishers.reverse_each(&:call)
|
21
21
|
clear_dsl_methods mod
|
22
22
|
|
23
|
-
|
24
|
-
|
23
|
+
if data.key? :result
|
24
|
+
data[:result]
|
25
|
+
else
|
26
|
+
data
|
25
27
|
end
|
26
|
-
|
27
|
-
data
|
28
28
|
end
|
29
29
|
|
30
30
|
def process_dsl(dsl,data)
|
31
31
|
return unless @dsls_seen.add? dsl
|
32
32
|
cbs = dsl.generate(data)
|
33
|
+
after_requires = []
|
33
34
|
if cbs[:requires]
|
34
|
-
cbs[:requires].each do |required_dsl|
|
35
|
-
|
35
|
+
cbs[:requires].each do |(required_dsl,order)|
|
36
|
+
case order
|
37
|
+
when :before
|
38
|
+
process_dsl Alki.load(required_dsl), data
|
39
|
+
when :after
|
40
|
+
after_requires << [Alki.load(required_dsl), data]
|
41
|
+
end
|
36
42
|
end
|
37
43
|
end
|
38
44
|
@inits << cbs[:init] if cbs[:init]
|
39
45
|
@finishers << cbs[:finish] if cbs[:finish]
|
40
|
-
@processors << cbs[:processors] if cbs[:processors]
|
41
46
|
if cbs[:methods]
|
42
47
|
cbs[:methods].each do |name, proc|
|
43
48
|
define_dsl_method data[:module], name, &proc
|
44
49
|
end
|
45
50
|
end
|
51
|
+
after_requires.each do |process_args|
|
52
|
+
process_dsl *process_args
|
53
|
+
end
|
46
54
|
end
|
47
55
|
|
48
56
|
def define_dsl_method(mod,name,&blk)
|
@@ -64,4 +72,4 @@ module Alki
|
|
64
72
|
end
|
65
73
|
end
|
66
74
|
end
|
67
|
-
end
|
75
|
+
end
|
data/lib/alki/dsl/version.rb
CHANGED
data/lib/alki/dsls/class.rb
CHANGED
data/lib/alki/dsls/dsl.rb
CHANGED
@@ -10,7 +10,7 @@ module Alki
|
|
10
10
|
|
11
11
|
def self.dsl_info
|
12
12
|
{
|
13
|
-
requires: ['alki/dsls/class'],
|
13
|
+
requires: [['alki/dsls/class',:before]],
|
14
14
|
methods: [
|
15
15
|
:dsl_method,
|
16
16
|
[:init,:dsl_init],
|
@@ -49,9 +49,9 @@ module Alki
|
|
49
49
|
@info[:finish] = :_dsl_finish
|
50
50
|
end
|
51
51
|
|
52
|
-
def require_dsl(dsl)
|
53
|
-
dsl_class = Alki
|
54
|
-
@info[:requires] << dsl_class
|
52
|
+
def require_dsl(dsl, order=:before)
|
53
|
+
dsl_class = Alki.load(dsl)
|
54
|
+
@info[:requires] << [dsl_class,order]
|
55
55
|
if defined? dsl_class::Helpers
|
56
56
|
add_module dsl_class::Helpers
|
57
57
|
add_helper_module dsl_class::Helpers
|
data/test/feature/config_test.rb
CHANGED
@@ -13,7 +13,7 @@ describe 'dsl configuration' do
|
|
13
13
|
end
|
14
14
|
|
15
15
|
it 'should allow using dsls specified in same dsl config' do
|
16
|
-
|
16
|
+
require 'alki_test/numbers/three'
|
17
17
|
AlkiTest::Numbers::Three.new.must_equal 3
|
18
18
|
end
|
19
|
-
end
|
19
|
+
end
|
@@ -105,11 +105,11 @@ describe Alki::ClassBuilder do
|
|
105
105
|
obj.instance_variable_get(:@b).must_equal 2
|
106
106
|
end
|
107
107
|
|
108
|
-
it 'should allow providing a
|
108
|
+
it 'should allow providing a constant name' do
|
109
109
|
if defined?(AlkiTestClass)
|
110
110
|
Object.send :remove_const, :AlkiTestClass
|
111
111
|
end
|
112
|
-
build(
|
112
|
+
build(constant_name: "AlkiTestClass")
|
113
113
|
assert(defined?(AlkiTestClass),'Expected AlkiTestClass to be defined')
|
114
114
|
Object.send :remove_const, :AlkiTestClass
|
115
115
|
assert(!defined?(AlkiTestClass))
|
@@ -119,16 +119,7 @@ describe Alki::ClassBuilder do
|
|
119
119
|
if defined?(AlkiTest::TestClass)
|
120
120
|
Object.send :remove_const, :AlkiTest
|
121
121
|
end
|
122
|
-
build(
|
123
|
-
assert(defined?(AlkiTest::TestClass),'Expected AlkiTest::TestClass to be defined')
|
124
|
-
Object.send :remove_const, :AlkiTest
|
125
|
-
end
|
126
|
-
|
127
|
-
it 'should use prefix and name to create class name' do
|
128
|
-
if defined?(AlkiTest::TestClass)
|
129
|
-
Object.send :remove_const, :AlkiTest
|
130
|
-
end
|
131
|
-
build(prefix: 'alki_test', name: "test_class")
|
122
|
+
build(name: "alki_test/test_class")
|
132
123
|
assert(defined?(AlkiTest::TestClass),'Expected AlkiTest::TestClass to be defined')
|
133
124
|
Object.send :remove_const, :AlkiTest
|
134
125
|
end
|
@@ -137,12 +128,25 @@ describe Alki::ClassBuilder do
|
|
137
128
|
build(
|
138
129
|
secondary_classes: [
|
139
130
|
{
|
140
|
-
|
131
|
+
constant_name: 'AlkiTestClass'
|
141
132
|
}
|
142
133
|
]
|
143
134
|
)
|
144
135
|
assert(defined?(AlkiTestClass),'Expected AlkiTestClass to be defined')
|
145
136
|
Object.send :remove_const, :AlkiTestClass
|
146
137
|
end
|
138
|
+
|
139
|
+
it 'should allow creating subclasses' do
|
140
|
+
build(
|
141
|
+
constant_name: 'AlkiTestClass',
|
142
|
+
secondary_classes: [
|
143
|
+
{
|
144
|
+
subclass: 'Subclass'
|
145
|
+
}
|
146
|
+
]
|
147
|
+
)
|
148
|
+
assert(defined?(AlkiTestClass::Subclass),'Expected AlkiTestClass::Subclass to be defined')
|
149
|
+
Object.send :remove_const, :AlkiTestClass
|
150
|
+
end
|
147
151
|
end
|
148
152
|
end
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: alki-dsl
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 0.4.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Matt Edlefsen
|
8
8
|
autorequire:
|
9
9
|
bindir: exe
|
10
10
|
cert_chain: []
|
11
|
-
date: 2016-12-
|
11
|
+
date: 2016-12-29 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: alki-support
|
@@ -16,14 +16,28 @@ dependencies:
|
|
16
16
|
requirements:
|
17
17
|
- - "~>"
|
18
18
|
- !ruby/object:Gem::Version
|
19
|
-
version: '0.
|
19
|
+
version: '0.7'
|
20
20
|
type: :runtime
|
21
21
|
prerelease: false
|
22
22
|
version_requirements: !ruby/object:Gem::Requirement
|
23
23
|
requirements:
|
24
24
|
- - "~>"
|
25
25
|
- !ruby/object:Gem::Version
|
26
|
-
version: '0.
|
26
|
+
version: '0.7'
|
27
|
+
- !ruby/object:Gem::Dependency
|
28
|
+
name: alki-loader
|
29
|
+
requirement: !ruby/object:Gem::Requirement
|
30
|
+
requirements:
|
31
|
+
- - "~>"
|
32
|
+
- !ruby/object:Gem::Version
|
33
|
+
version: '0.1'
|
34
|
+
type: :runtime
|
35
|
+
prerelease: false
|
36
|
+
version_requirements: !ruby/object:Gem::Requirement
|
37
|
+
requirements:
|
38
|
+
- - "~>"
|
39
|
+
- !ruby/object:Gem::Version
|
40
|
+
version: '0.1'
|
27
41
|
- !ruby/object:Gem::Dependency
|
28
42
|
name: minitest
|
29
43
|
requirement: !ruby/object:Gem::Requirement
|
@@ -53,21 +67,18 @@ extra_rdoc_files: []
|
|
53
67
|
files:
|
54
68
|
- ".gitignore"
|
55
69
|
- Gemfile
|
70
|
+
- README.adoc
|
56
71
|
- alki-dsl.gemspec
|
57
72
|
- lib/alki/class_builder.rb
|
58
73
|
- lib/alki/dsl.rb
|
59
74
|
- lib/alki/dsl/base.rb
|
60
|
-
- lib/alki/dsl/builder.rb
|
61
75
|
- lib/alki/dsl/class_helpers.rb
|
62
76
|
- lib/alki/dsl/evaluator.rb
|
63
|
-
- lib/alki/dsl/loader.rb
|
64
|
-
- lib/alki/dsl/registry.rb
|
65
77
|
- lib/alki/dsl/version.rb
|
66
78
|
- lib/alki/dsls/class.rb
|
67
79
|
- lib/alki/dsls/dsl.rb
|
68
|
-
- lib/alki/dsls/dsl_config.rb
|
69
80
|
- test/feature/config_test.rb
|
70
|
-
- test/fixtures/example/
|
81
|
+
- test/fixtures/example/lib/alki_loader.rb
|
71
82
|
- test/fixtures/example/lib/alki_test/dsls/number.rb
|
72
83
|
- test/fixtures/example/lib/alki_test/dsls/value.rb
|
73
84
|
- test/fixtures/example/numbers/three.rb
|
@@ -100,7 +111,7 @@ specification_version: 4
|
|
100
111
|
summary: Alki dsl library
|
101
112
|
test_files:
|
102
113
|
- test/feature/config_test.rb
|
103
|
-
- test/fixtures/example/
|
114
|
+
- test/fixtures/example/lib/alki_loader.rb
|
104
115
|
- test/fixtures/example/lib/alki_test/dsls/number.rb
|
105
116
|
- test/fixtures/example/lib/alki_test/dsls/value.rb
|
106
117
|
- test/fixtures/example/numbers/three.rb
|
data/lib/alki/dsl/builder.rb
DELETED
@@ -1,42 +0,0 @@
|
|
1
|
-
require 'alki/support'
|
2
|
-
require 'alki/dsl/evaluator'
|
3
|
-
|
4
|
-
module Alki
|
5
|
-
module Dsl
|
6
|
-
class Builder
|
7
|
-
def self.build(data,&blk)
|
8
|
-
result = Alki::Dsl::Evaluator.evaluate _dsls,data,&blk
|
9
|
-
if _processor
|
10
|
-
_processor.build result
|
11
|
-
else
|
12
|
-
result
|
13
|
-
end
|
14
|
-
end
|
15
|
-
|
16
|
-
private
|
17
|
-
|
18
|
-
def self.dsl(name)
|
19
|
-
klass = Alki::Support.load_class name
|
20
|
-
unless klass
|
21
|
-
raise "Unable to load class #{name.inspect}"
|
22
|
-
end
|
23
|
-
dsls = _dsls
|
24
|
-
dsls += [klass]
|
25
|
-
define_singleton_method(:_dsls) { dsls }
|
26
|
-
end
|
27
|
-
|
28
|
-
def self.processor(name)
|
29
|
-
klass = Alki::Support.load_class name
|
30
|
-
define_singleton_method(:_processor) { klass }
|
31
|
-
end
|
32
|
-
|
33
|
-
def self._dsls
|
34
|
-
[]
|
35
|
-
end
|
36
|
-
|
37
|
-
def self._processor
|
38
|
-
nil
|
39
|
-
end
|
40
|
-
end
|
41
|
-
end
|
42
|
-
end
|
data/lib/alki/dsl/loader.rb
DELETED
@@ -1,28 +0,0 @@
|
|
1
|
-
require 'alki/support'
|
2
|
-
require 'alki/dsl'
|
3
|
-
|
4
|
-
module Alki
|
5
|
-
module Dsl
|
6
|
-
class Loader
|
7
|
-
def initialize(root_dir)
|
8
|
-
@root_dir = root_dir
|
9
|
-
end
|
10
|
-
|
11
|
-
def all_paths
|
12
|
-
Dir[File.join(@root_dir,'**','*.rb')].map do |path|
|
13
|
-
path.gsub(File.join(@root_dir,''),'').gsub(/\.rb$/,'')
|
14
|
-
end
|
15
|
-
end
|
16
|
-
|
17
|
-
def load_all
|
18
|
-
all_paths.inject({}) do |h,path|
|
19
|
-
h.merge!(path => Alki::Dsl.load(path))
|
20
|
-
end
|
21
|
-
end
|
22
|
-
|
23
|
-
def load(file)
|
24
|
-
Alki::Dsl.load File.expand_path("#{file}.rb",@root_dir)
|
25
|
-
end
|
26
|
-
end
|
27
|
-
end
|
28
|
-
end
|
data/lib/alki/dsl/registry.rb
DELETED
@@ -1,76 +0,0 @@
|
|
1
|
-
require 'alki/dsl/loader'
|
2
|
-
require 'alki/support'
|
3
|
-
|
4
|
-
module Alki
|
5
|
-
module Dsl
|
6
|
-
module Registry
|
7
|
-
@registered_paths = {}
|
8
|
-
@registered_dirs = {}
|
9
|
-
|
10
|
-
def self.registered_paths
|
11
|
-
@registered_paths.keys
|
12
|
-
end
|
13
|
-
|
14
|
-
def self.registered_dirs
|
15
|
-
@registered_dirs.keys
|
16
|
-
end
|
17
|
-
|
18
|
-
def self.register(path,builder,**data)
|
19
|
-
@registered_paths[File.absolute_path(path)] = Entry.new(builder,data)
|
20
|
-
end
|
21
|
-
|
22
|
-
def self.register_dir(dir_path,builder,**data)
|
23
|
-
@registered_dirs[File.join(File.absolute_path(dir_path),'')] = [builder,data]
|
24
|
-
end
|
25
|
-
|
26
|
-
def self.lookup(path, load_configs: true)
|
27
|
-
path = File.absolute_path path
|
28
|
-
entry = @registered_paths[path]
|
29
|
-
return entry if entry
|
30
|
-
|
31
|
-
@registered_dirs.each do |dir,(builder,data)|
|
32
|
-
if path.start_with? dir
|
33
|
-
data = {name: Alki::Support.path_name(path, dir)}.merge data
|
34
|
-
return Entry.new(builder,data)
|
35
|
-
end
|
36
|
-
end
|
37
|
-
|
38
|
-
if load_configs
|
39
|
-
root = Alki::Support.find_root(path) do |dir|
|
40
|
-
File.exists?(File.join(dir,'config','dsls.rb'))
|
41
|
-
end
|
42
|
-
if root
|
43
|
-
config_file = File.join(root,'config','dsls.rb')
|
44
|
-
register config_file, 'alki/dsls/dsl_config', root: root
|
45
|
-
require config_file
|
46
|
-
return lookup path, load_configs: false
|
47
|
-
end
|
48
|
-
end
|
49
|
-
|
50
|
-
nil
|
51
|
-
end
|
52
|
-
|
53
|
-
def self.build(path,&blk)
|
54
|
-
entry = lookup path
|
55
|
-
if entry
|
56
|
-
entry.build blk
|
57
|
-
else
|
58
|
-
nil
|
59
|
-
end
|
60
|
-
end
|
61
|
-
|
62
|
-
class Entry
|
63
|
-
attr_reader :data
|
64
|
-
|
65
|
-
def initialize(builder,data)
|
66
|
-
@builder = builder
|
67
|
-
@data = data
|
68
|
-
end
|
69
|
-
|
70
|
-
def build(blk)
|
71
|
-
Alki::Support.load_class(@builder).build @data, &blk
|
72
|
-
end
|
73
|
-
end
|
74
|
-
end
|
75
|
-
end
|
76
|
-
end
|
data/lib/alki/dsls/dsl_config.rb
DELETED
@@ -1,28 +0,0 @@
|
|
1
|
-
require 'alki/dsl/base'
|
2
|
-
|
3
|
-
module Alki
|
4
|
-
module Dsls
|
5
|
-
class DslConfig < Alki::Dsl::Base
|
6
|
-
def self.dsl_info
|
7
|
-
{
|
8
|
-
methods: %i(register register_dir register_lib_dir)
|
9
|
-
}
|
10
|
-
end
|
11
|
-
|
12
|
-
def register(path,*args)
|
13
|
-
path = File.expand_path(path,@ctx[:root])
|
14
|
-
Alki::Dsl::Registry.register path, *args
|
15
|
-
end
|
16
|
-
|
17
|
-
def register_dir(path,*args)
|
18
|
-
path = File.expand_path(path,@ctx[:root])
|
19
|
-
Alki::Dsl::Registry.register_dir path, *args
|
20
|
-
end
|
21
|
-
|
22
|
-
def register_lib_dir(prefix,dsl,**data)
|
23
|
-
path = File.join(@ctx[:root],'lib', prefix)
|
24
|
-
Alki::Dsl::Registry.register_dir path, dsl, data.merge(prefix: prefix)
|
25
|
-
end
|
26
|
-
end
|
27
|
-
end
|
28
|
-
end
|