alki-dsl 0.4.2 → 0.5.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 4d2cf31784168eb5de6e6fd04ac4d6d7763aa16a
4
- data.tar.gz: '0299ce32b0c21d2567c6a201696262a0442af954'
3
+ metadata.gz: 4450c3089f0ffa33bd64349a3f6a06e3e2c93192
4
+ data.tar.gz: 81c39bf6c311568c9b1ab3e633209622bc8c2798
5
5
  SHA512:
6
- metadata.gz: b306403e0261d49b4d4f5f16203d7599cc8468d5b1cb9cfa57e546dbc7be2f872590c4bc0d444037370d6a863b47358e1dda0027aecf7b3f532aeeb892a41feb
7
- data.tar.gz: 525dfdbe6892594eda9330edfde6c7b77bf63ccfec42915f20fd7495d89f22dc459a5140ccc20a9566975616a75feb9a7b5bc08d49de7966ba75ebfd7d37a82a
6
+ metadata.gz: 56fe3c9044b3e0f416b363615269344ec5150292a422964fed7c05002e614b4cd7feb7a6137738287d443cfc2f8d3ef8aae3c061f776b7019573131e5cbf5124
7
+ data.tar.gz: a5d8ce35078e0cf74a313dbfbb7da8ce5312955dac27b06ba3c92582b06dee58d917cc1e055ca64f5ad4c390bde769b01ccd2214291f71974afeee05af9856de
@@ -57,7 +57,7 @@ Or install it yourself as:
57
57
 
58
58
  All DSLs created with Alki::Dsl are Class objects with `::build` methods. These build methods take an optional
59
59
  hash of parameters, along with a ruby block to be evaluated. DSLs created with Alki::Dsl cannot directly
60
- strings.
60
+ evaluate strings, just ruby blocks.
61
61
 
62
62
  While DSLs can be created with Alki::Dsl manually, the easiest way is to use the provided "dsl" DSL. Each
63
63
  DSL defines any number of "dsl methods", which are methods that will be exposed to the user of the DSL.
@@ -124,10 +124,57 @@ puts result
124
124
 
125
125
  ```
126
126
 
127
+ #### Helpers ####
128
+
129
+ In addition to defining dsl methods, the 'dsl' DSL also allows defining helper methods,
130
+ which can be called within other dsls that require it.
131
+
132
+ ```ruby
133
+ require 'alki/dsl'
134
+
135
+ strings_dsl = Alki::Dsl.build 'alki/dsls/dsl' do
136
+ init do
137
+ ctx[:strings] = []
138
+ end
139
+
140
+ helper :set_separator do |sep|
141
+ ctx[:separator] = sep
142
+ end
143
+
144
+ dsl_method :add do |val|
145
+ ctx[:strings] << val
146
+ end
147
+
148
+ finish do
149
+ sep = ctx[:separator] || "\n"
150
+ ctx[:result] = ctx[:strings].join(sep)
151
+ end
152
+ end
153
+
154
+ my_dsl = Alki::Dsl.build 'alki/dsls/dsl' do
155
+ require_dsl strings_dsl
156
+
157
+ dsl_method :separator do |sep|
158
+ set_separator sep # Call helper from strings_dsl
159
+ end
160
+ end
161
+
162
+ result = my_dsl.build do
163
+ separator ' '
164
+
165
+ add "hello"
166
+ add "world"
167
+ end
168
+
169
+ puts result
170
+
171
+ # output: hello world
172
+ ```
173
+
127
174
  ### Using with Alki::Loader
128
175
 
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
176
+ https://github.com/alki-project/alki-loader[Alki::Loader] is a library that extends Ruby's `require` method.
177
+ It can be used to associate "builder" objects with files
131
178
  or directories so that the code within them is processed by the builder object when they are loaded. More
132
179
  documentation can be found at the Alki::Loader https://github.com/alki-project/alki-loader[github page].
133
180
 
@@ -142,6 +189,7 @@ To register it create a `lib/alki_loader.rb` file:
142
189
 
143
190
  .lib/alki_loader.rb
144
191
  ```ruby
192
+ # Treat all ruby source files in lib/my_project/dsls as DSL definition files
145
193
  Alki::Loader.register 'my_project/dsls', builder: 'alki/dsls/dsl'
146
194
  ```
147
195
 
@@ -155,7 +203,8 @@ The DSL class can be passed directly instead of the load string.
155
203
  ****
156
204
 
157
205
  Now a DSL definition file can be created in `lib/my_project/dsls`. Revisiting the previous example, a "strings"
158
- can be created.
206
+ dsl file can be created. Because the file has been registered with the 'alki/dsls/dsl' builder,
207
+ it will be automatically processed as a DSL definition when loaded.
159
208
 
160
209
  .lib/my_project/dsls/strings.rb
161
210
  ```ruby
@@ -217,16 +266,17 @@ Alki do
217
266
  end
218
267
  ```
219
268
 
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.
269
+ So what if we want to use our DSL with Alki::Loader as well? First, our DSL right now produces
270
+ a string, but Alki::Loader requires builders to define a constant with the correct name. Alki::Dsl comes with a
271
+ "class" DSL that makes this easy. First lets create a new DSL that adapts our transformable_strings DSL into a one
272
+ that defines a module.
224
273
 
225
274
  .lib/my_project/dsls/strings_class.rb
226
275
  ```ruby
227
276
  Alki do
228
277
  require_dsl 'alki/dsls/class'
229
- require_dsl 'my_project/dsls/transformable_strings', :after # This makes it's finish runs before ours
278
+ require_dsl 'my_project/dsls/transformable_strings', :after # This makes it's finish hook
279
+ # run before ours
230
280
 
231
281
  finish do
232
282
  # Helpers provided by alki/dsls/class
@@ -19,5 +19,5 @@ Gem::Specification.new do |spec|
19
19
 
20
20
  spec.add_dependency 'alki-support', '~> 0.7'
21
21
  spec.add_dependency 'alki-loader', '~> 0.2'
22
- spec.add_development_dependency 'minitest', '~> 5.9', '>= 5.9.1'
22
+ spec.add_development_dependency 'alki-testing', '~> 0.1'
23
23
  end
@@ -6,8 +6,8 @@ module Alki
6
6
  class Base
7
7
  extend Alki::Dsl::Builder
8
8
 
9
- def self.generate(ctx)
10
- obj = new(ctx)
9
+ def self.generate(evaluator,ctx)
10
+ obj = new(ctx,evaluator)
11
11
  result = {methods: {}}
12
12
  info = self.dsl_info
13
13
 
@@ -25,7 +25,18 @@ module Alki
25
25
  result[:methods][name] = obj.method method
26
26
  end
27
27
  end
28
- result
28
+
29
+ if info[:helpers]
30
+ info[:helpers].each do |method|
31
+ if method.is_a?(Array)
32
+ name, method = method
33
+ else
34
+ name = method
35
+ end
36
+ result[:helpers][name] = obj.method method
37
+ end
38
+ end
39
+ evaluator.update result
29
40
  end
30
41
 
31
42
  def self.dsl_info
@@ -33,15 +44,12 @@ module Alki
33
44
  end
34
45
 
35
46
  def self.helpers
36
- if defined? self::Helpers
37
- [self::Helpers]
38
- else
39
- []
40
- end
47
+ []
41
48
  end
42
49
 
43
- def initialize(ctx)
50
+ def initialize(ctx,evaluator)
44
51
  @ctx = ctx
52
+ @evaluator = evaluator
45
53
  end
46
54
 
47
55
  attr_reader :ctx
@@ -47,16 +47,6 @@ module Alki
47
47
  }
48
48
  end
49
49
 
50
- def add_helper(name,&blk)
51
- add_method name, &blk
52
- add_method name, subclass: 'Helpers', &blk
53
- end
54
-
55
- def add_helper_module(mod)
56
- add_module mod
57
- add_module mod, subclass: 'Helpers'
58
- end
59
-
60
50
  def add_initialize_param(name,opts={},&default)
61
51
  name = name.to_sym
62
52
  param = default ? [name,default] : name
@@ -1,54 +1,71 @@
1
1
  require 'set'
2
2
  require 'alki/support'
3
+ require 'alki/dsl/evaluator_instance'
3
4
 
4
5
  module Alki
5
6
  module Dsl
6
7
  class Evaluator
7
- def initialize
8
+ def self.evaluate(dsl, data={}, &blk)
9
+ evaluator = new(dsl,data)
10
+ evaluator.evaluate &blk
11
+ evaluator.finish
12
+ end
13
+
14
+ def initialize(dsl,data)
8
15
  @inits = []
9
16
  @finishers = []
10
17
  @processors = []
11
18
  @dsls_seen = Set.new
19
+ @data = data
20
+ @mod = (data[:module] ||= Module.new)
21
+ @dsl = dsl
22
+ evaluator = self
23
+ define_dsl_method :method_missing do |meth,*args,&b|
24
+ evaluator.context.send meth, *args, &b
25
+ end
26
+ process_dsl @dsl
12
27
  end
13
28
 
14
- def evaluate(dsl,data={},&blk)
15
- mod = (data[:module] ||= Module.new)
16
- process_dsl dsl, data
29
+ def build(data={},&blk)
30
+ self.class.evaluate(@dsl,data,&blk)
31
+ end
17
32
 
18
- @inits.each(&:call)
19
- define_dsl_method mod, :method_missing do |meth,*args,&b|
20
- blk.binding.receiver.send meth, *args, &b
21
- end
22
- dsl_exec mod, &blk
23
- @finishers.reverse_each(&:call)
24
- clear_dsl_methods mod
33
+ def process_dsl(dsl)
34
+ return unless @dsls_seen.add? dsl
35
+ dsl.generate(self,@data)
36
+ end
25
37
 
26
- if data.key? :result
27
- data[:result]
28
- else
29
- data
30
- end
38
+ def context
39
+ Thread.current[:__alki_dsl_context]
31
40
  end
32
41
 
33
- def process_dsl(dsl,data)
34
- return unless @dsls_seen.add? dsl
35
- cbs = dsl.generate(data)
42
+ def evaluate(&blk)
43
+ Thread.current[:__alki_dsl_context] = @data[:context] || blk.binding.receiver
44
+ @mod.class_exec &blk
45
+ end
46
+
47
+ def require_dsl(source,dsl)
48
+ dsl_class = Alki.load(dsl)
49
+ dsl_class.new(EvaluatorInstance.new(self,@data))
50
+ end
51
+
52
+ def update(cbs)
36
53
  after_requires = []
37
54
  if cbs[:requires]
38
55
  cbs[:requires].each do |(required_dsl,order)|
39
56
  case order
40
57
  when :before
41
- process_dsl Alki.load(required_dsl), data
58
+ process_dsl Alki.load(required_dsl)
42
59
  when :after
43
- after_requires << [Alki.load(required_dsl), data]
60
+ after_requires << [Alki.load(required_dsl)]
44
61
  end
45
62
  end
46
63
  end
47
- @inits << cbs[:init] if cbs[:init]
64
+ cbs[:init].call if cbs[:init]
48
65
  @finishers << cbs[:finish] if cbs[:finish]
49
66
  if cbs[:methods]
50
67
  cbs[:methods].each do |name, proc|
51
- define_dsl_method data[:module], name, &proc
68
+ define_dsl_method name, &proc
52
69
  end
53
70
  end
54
71
  after_requires.each do |process_args|
@@ -56,22 +73,27 @@ module Alki
56
73
  end
57
74
  end
58
75
 
59
- def define_dsl_method(mod,name,&blk)
60
- mod.define_singleton_method name, &blk
76
+ def define_dsl_method(name,&blk)
77
+ @mod.define_singleton_method name, &blk
61
78
  end
62
79
 
63
- def clear_dsl_methods(mod)
64
- mod.singleton_methods do |m|
65
- mod.singleton_class.send :remove_method, m
80
+ def finish
81
+ @finishers.reverse_each(&:call)
82
+ clear_dsl_methods
83
+
84
+ if @data.key? :result
85
+ @data[:result]
86
+ else
87
+ @data
66
88
  end
67
89
  end
68
90
 
69
- def dsl_exec(mod,&blk)
70
- mod.class_exec &blk
71
- end
91
+ private
72
92
 
73
- def self.evaluate(dsl, data={}, &blk)
74
- new.evaluate dsl, data, &blk
93
+ def clear_dsl_methods
94
+ @mod.singleton_methods do |m|
95
+ @mod.singleton_class.send :remove_method, m
96
+ end
75
97
  end
76
98
  end
77
99
  end
@@ -0,0 +1,17 @@
1
+ module Alki
2
+ module Dsl
3
+ class EvaluatorInstance
4
+ attr_reader :data
5
+
6
+ def initialize(evaluator,data,dsl)
7
+ @evaluator = evaluator
8
+ @data = data
9
+ @dsl = dsl
10
+ end
11
+
12
+ def require_dsl(dsl)
13
+ @evaluator.require_dsl(self,dsl)
14
+ end
15
+ end
16
+ end
17
+ end
@@ -10,10 +10,8 @@ module Alki
10
10
  @requires = dsls.map{|dsl| [dsl,:before]}.freeze
11
11
  end
12
12
 
13
- def generate(_ctx)
14
- {
15
- requires: @requires
16
- }
13
+ def generate(evaluator,_ctx)
14
+ evaluator.update requires: @requires
17
15
  end
18
16
 
19
17
  def helpers
@@ -1,5 +1,5 @@
1
1
  module Alki
2
2
  module Dsl
3
- VERSION = '0.4.2'
3
+ VERSION = '0.5.0'
4
4
  end
5
5
  end
@@ -7,7 +7,9 @@ module Alki
7
7
  class Class < Alki::Dsl::Base
8
8
  include Alki::Dsl::ClassHelpers
9
9
 
10
- self::Helpers = Alki::Dsl::ClassHelpers
10
+ def self.helpers
11
+ [Alki::Dsl::ClassHelpers]
12
+ end
11
13
 
12
14
  def self.dsl_info
13
15
  {
@@ -5,8 +5,25 @@ require 'alki/dsl/class_helpers'
5
5
  module Alki
6
6
  module Dsls
7
7
  class Dsl < Alki::Dsl::Base
8
- include Alki::Dsl::ClassHelpers
9
- Helpers = Alki::Dsl::ClassHelpers
8
+ module Helpers
9
+ include Alki::Dsl::ClassHelpers
10
+
11
+ def add_helper(name,&blk)
12
+ add_method name, &blk
13
+ add_method name, subclass: 'Helpers', &blk
14
+ end
15
+
16
+ def add_helper_module(mod)
17
+ add_module mod
18
+ add_module mod, subclass: 'Helpers'
19
+ end
20
+ end
21
+
22
+ include Helpers
23
+
24
+ def self.helpers
25
+ [Helpers]
26
+ end
10
27
 
11
28
  def self.dsl_info
12
29
  {
@@ -68,7 +85,8 @@ module Alki
68
85
 
69
86
  def finish
70
87
  set_super_class 'alki/dsl/base'
71
- class_builder('Helpers')[:type] = :module
88
+ create_as_module(subclass: 'Helpers')
89
+
72
90
  add_class_method :helpers do
73
91
  [self::Helpers]
74
92
  end
@@ -76,6 +94,14 @@ module Alki
76
94
  add_class_method :dsl_info do
77
95
  info
78
96
  end
97
+
98
+ add_method(:require_dsl,private: true) do |dsl,order=:before|
99
+ dsl_class = Alki.load dsl
100
+ dsl_class.helpers.each do |helper|
101
+ self.singleton_class.send :include, helper
102
+ end
103
+ @evaluator.update requires: [[dsl,order]]
104
+ end
79
105
  end
80
106
  end
81
107
  end
@@ -1,11 +1,7 @@
1
- require_relative '../test_helper'
1
+ require 'alki/feature_test'
2
2
  require 'alki/dsl'
3
3
 
4
4
  describe 'dsl configuration' do
5
- before do
6
- $LOAD_PATH.unshift fixture_path('example','lib')
7
- end
8
-
9
5
  it 'should be automatically loaded when requiring a dsl' do
10
6
  require 'alki_test/dsls/number'
11
7
  AlkiTest::Dsls::Number.must_be_instance_of Class
@@ -16,9 +12,4 @@ describe 'dsl configuration' do
16
12
  require 'alki_test/numbers/three'
17
13
  AlkiTest::Numbers::Three.new.must_equal 3
18
14
  end
19
-
20
- it 'should allow using Alki::Dsl.merge to merge dsls together' do
21
- require 'alki_test/numbers/three'
22
- AlkiTest::Numbers::Three.simple.must_equal true
23
- end
24
15
  end
@@ -0,0 +1,65 @@
1
+ require 'alki/feature_test'
2
+
3
+ describe 'Merging Dsls' do
4
+ before do
5
+ @dsl1 = build_dsl do
6
+ init do
7
+ ctx[:result] << :init1
8
+ end
9
+ helper :do_one do
10
+ ctx[:result] << :one
11
+ end
12
+ dsl_method :one do
13
+ do_one
14
+ end
15
+ finish do
16
+ ctx[:result] << :finish1
17
+ end
18
+ end
19
+ @dsl2 = build_dsl do
20
+ init do
21
+ ctx[:result] << :init2
22
+ end
23
+ helper :do_two do
24
+ ctx[:result] << :two
25
+ end
26
+ dsl_method :two do
27
+ do_two
28
+ end
29
+ finish do
30
+ ctx[:result] << :finish2
31
+ end
32
+ end
33
+ @merged = Alki::Dsl.merge(@dsl1,@dsl2)
34
+ end
35
+
36
+ def build(dsl = @merged,&blk)
37
+ dsl.build result: [], &(blk || ->{})
38
+ end
39
+
40
+ it 'should run init and finish blocks of both dsls in order' do
41
+ build.must_equal %i(init1 init2 finish2 finish1)
42
+ end
43
+
44
+ it 'should expose dsl methods of both dsls' do
45
+ result = build { one; two }
46
+ result.must_include :one
47
+ result.must_include :two
48
+ end
49
+
50
+ it 'should provide helpers from both dsls when required by another dsl' do
51
+ merged = @merged
52
+ dsl = build_dsl do
53
+ require_dsl merged
54
+
55
+ finish do
56
+ do_one
57
+ do_two
58
+ end
59
+ end
60
+ result = build(dsl)
61
+
62
+ result.must_include :one
63
+ result.must_include :two
64
+ end
65
+ end
@@ -0,0 +1,5 @@
1
+ require 'alki/feature_test'
2
+
3
+ describe 'Runtime requires' do
4
+ it 'should be allowed at '
5
+ end
@@ -0,0 +1,8 @@
1
+ require 'alki/dsl'
2
+ $LOAD_PATH.unshift Alki::Test.fixture_path('example','lib')
3
+
4
+ class Minitest::Spec
5
+ def build_dsl(&blk)
6
+ Alki::Dsl.build 'alki/dsls/dsl', &blk
7
+ end
8
+ end
@@ -1,7 +1,7 @@
1
1
  Alki do
2
- require_dsl 'alki/dsls/class'
3
2
 
4
3
  finish do
4
+ require_dsl 'alki/dsls/class'
5
5
  add_class_method :simple do
6
6
  true
7
7
  end
@@ -0,0 +1,3 @@
1
+ Alki do
2
+
3
+ end
@@ -1,4 +1,4 @@
1
- require_relative '../test_helper'
1
+ require 'alki/test'
2
2
  require 'alki/class_builder'
3
3
 
4
4
  describe Alki::ClassBuilder do
@@ -1,6 +1,6 @@
1
- require_relative '../test_helper'
1
+ require 'alki/test'
2
2
  require 'alki/dsl/evaluator'
3
3
 
4
4
  describe Alki::Dsl::Evaluator do
5
5
 
6
- end
6
+ 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.2
4
+ version: 0.5.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: 2017-03-05 00:00:00.000000000 Z
11
+ date: 2017-05-16 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: alki-support
@@ -39,25 +39,19 @@ dependencies:
39
39
  - !ruby/object:Gem::Version
40
40
  version: '0.2'
41
41
  - !ruby/object:Gem::Dependency
42
- name: minitest
42
+ name: alki-testing
43
43
  requirement: !ruby/object:Gem::Requirement
44
44
  requirements:
45
45
  - - "~>"
46
46
  - !ruby/object:Gem::Version
47
- version: '5.9'
48
- - - ">="
49
- - !ruby/object:Gem::Version
50
- version: 5.9.1
47
+ version: '0.1'
51
48
  type: :development
52
49
  prerelease: false
53
50
  version_requirements: !ruby/object:Gem::Requirement
54
51
  requirements:
55
52
  - - "~>"
56
53
  - !ruby/object:Gem::Version
57
- version: '5.9'
58
- - - ">="
59
- - !ruby/object:Gem::Version
60
- version: 5.9.1
54
+ version: '0.1'
61
55
  description: Library for defining and using DSLs
62
56
  email:
63
57
  - matt.edlefsen@gmail.com
@@ -75,15 +69,20 @@ files:
75
69
  - lib/alki/dsl/builder.rb
76
70
  - lib/alki/dsl/class_helpers.rb
77
71
  - lib/alki/dsl/evaluator.rb
72
+ - lib/alki/dsl/evaluator_instance.rb
78
73
  - lib/alki/dsl/merge.rb
79
74
  - lib/alki/dsl/version.rb
80
75
  - lib/alki/dsls/class.rb
81
76
  - lib/alki/dsls/dsl.rb
82
77
  - test/feature/config_test.rb
78
+ - test/feature/merge_test.rb
79
+ - test/feature/runtime_requires.rb
80
+ - test/feature_test_helper.rb
83
81
  - test/fixtures/example/lib/alki_loader.rb
84
82
  - test/fixtures/example/lib/alki_test/dsls/number.rb
85
83
  - test/fixtures/example/lib/alki_test/dsls/simple.rb
86
84
  - test/fixtures/example/lib/alki_test/dsls/value.rb
85
+ - test/fixtures/example/merged.rb
87
86
  - test/fixtures/example/numbers/three.rb
88
87
  - test/integration/class_builder_test.rb
89
88
  - test/test_helper.rb
@@ -114,10 +113,14 @@ specification_version: 4
114
113
  summary: Alki dsl library
115
114
  test_files:
116
115
  - test/feature/config_test.rb
116
+ - test/feature/merge_test.rb
117
+ - test/feature/runtime_requires.rb
118
+ - test/feature_test_helper.rb
117
119
  - test/fixtures/example/lib/alki_loader.rb
118
120
  - test/fixtures/example/lib/alki_test/dsls/number.rb
119
121
  - test/fixtures/example/lib/alki_test/dsls/simple.rb
120
122
  - test/fixtures/example/lib/alki_test/dsls/value.rb
123
+ - test/fixtures/example/merged.rb
121
124
  - test/fixtures/example/numbers/three.rb
122
125
  - test/integration/class_builder_test.rb
123
126
  - test/test_helper.rb