alki 0.8.0 → 0.9.0

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.
Files changed (57) hide show
  1. checksums.yaml +4 -4
  2. data/README.adoc +108 -6
  3. data/alki.gemspec +2 -2
  4. data/bin/alki +17 -0
  5. data/config/dsls.rb +2 -2
  6. data/doc/assembly_dsl.adoc +6 -6
  7. data/exe/alki +275 -0
  8. data/lib/alki/assembly/builder.rb +113 -0
  9. data/lib/alki/assembly/executor.rb +127 -0
  10. data/lib/alki/assembly/handler_base.rb +21 -0
  11. data/lib/alki/assembly/instance.rb +21 -0
  12. data/lib/alki/assembly/types/assembly.rb +92 -0
  13. data/lib/alki/assembly/types/factory.rb +29 -0
  14. data/lib/alki/assembly/types/func.rb +12 -0
  15. data/lib/alki/assembly/types/group.rb +39 -0
  16. data/lib/alki/assembly/types/override.rb +40 -0
  17. data/lib/alki/assembly/types/proc_value.rb +18 -0
  18. data/lib/alki/assembly/types/service.rb +27 -0
  19. data/lib/alki/assembly/types/value.rb +9 -0
  20. data/lib/alki/assembly.rb +21 -40
  21. data/lib/alki/dsls/assembly.rb +10 -12
  22. data/lib/alki/dsls/assembly_group.rb +92 -0
  23. data/lib/alki/dsls/assembly_type.rb +5 -15
  24. data/lib/alki/execution/cache_entry.rb +16 -0
  25. data/lib/alki/execution/context.rb +29 -0
  26. data/lib/alki/execution/context_class_builder.rb +36 -0
  27. data/lib/alki/execution/value_context.rb +14 -0
  28. data/lib/alki/overlay_delegator.rb +8 -20
  29. data/lib/alki/overlay_info.rb +3 -0
  30. data/lib/alki/override_builder.rb +6 -4
  31. data/lib/alki/service_delegator.rb +3 -3
  32. data/lib/alki/version.rb +1 -1
  33. data/lib/alki.rb +4 -4
  34. data/test/feature/alki_test.rb +1 -2
  35. data/test/feature/example_test.rb +2 -3
  36. data/test/feature/factories_test.rb +48 -0
  37. data/test/feature/overlays_test.rb +225 -0
  38. data/test/feature/overrides_test.rb +1 -2
  39. data/test/feature/pseudo_elements_test.rb +67 -0
  40. data/test/feature_test_helper.rb +1 -0
  41. data/test/fixtures/example/config/assembly.rb +11 -2
  42. data/test/fixtures/example/config/handlers.rb +2 -7
  43. data/test/fixtures/example/lib/log_overlay.rb +3 -3
  44. data/test/integration/dsls/assembly_test.rb +3 -8
  45. data/test/integration/dsls/assembly_type_test.rb +2 -2
  46. data/test/integration/dsls/service_dsl_test.rb +2 -2
  47. metadata +36 -18
  48. data/lib/alki/assembly_builder.rb +0 -109
  49. data/lib/alki/assembly_executor.rb +0 -129
  50. data/lib/alki/assembly_handler_base.rb +0 -19
  51. data/lib/alki/dsls/assembly_type_dsl.rb +0 -21
  52. data/lib/alki/dsls/assembly_types/assembly.rb +0 -101
  53. data/lib/alki/dsls/assembly_types/group.rb +0 -41
  54. data/lib/alki/dsls/assembly_types/load.rb +0 -31
  55. data/lib/alki/dsls/assembly_types/overlay.rb +0 -9
  56. data/lib/alki/dsls/assembly_types/value.rb +0 -100
  57. data/test/test_helper.rb +0 -1
@@ -0,0 +1,16 @@
1
+ module Alki
2
+ module Execution
3
+ class CacheEntry
4
+ attr_accessor :type,:value,:status
5
+ def initialize
6
+ @status = :building
7
+ end
8
+
9
+ def finish(type,value)
10
+ @status = :done
11
+ @type = type
12
+ @value = value
13
+ end
14
+ end
15
+ end
16
+ end
@@ -0,0 +1,29 @@
1
+ require 'alki/service_delegator'
2
+
3
+ module Alki
4
+ module Execution
5
+ module Context
6
+ def lookup(*path)
7
+ path.flatten.inject(self) do |group,elem|
8
+ unless elem.is_a?(String) or elem.is_a?(Symbol)
9
+ raise ArgumentError.new("lookup can only take Strings or Symbols")
10
+ end
11
+ elem.to_s.split('.').inject(group) do |group,name|
12
+ raise "Invalid lookup elem" unless group.is_a? Context
13
+ group.send name.to_sym
14
+ end
15
+ end
16
+ end
17
+
18
+ def lazy(*path)
19
+ path = path.flatten.inject('') do |path,elem|
20
+ unless elem.is_a?(String) or elem.is_a?(Symbol)
21
+ raise ArgumentError.new("lookup can only take Strings or Symbols")
22
+ end
23
+ path << elem.to_s
24
+ end
25
+ Alki::ServiceDelegator.new self, path
26
+ end
27
+ end
28
+ end
29
+ end
@@ -0,0 +1,36 @@
1
+ require 'alki/class_builder'
2
+
3
+ module Alki
4
+ module Execution
5
+ module ContextClassBuilder
6
+ def self.build(config)
7
+ if config[:body]
8
+ methods = {
9
+ __call__: {body: (config[:body])},
10
+ meta: {body: ->{@__meta__}}
11
+ }
12
+ else
13
+ methods = {}
14
+ end
15
+ (config[:scope]||{}).each do |name,path|
16
+ methods[name] = {
17
+ body:->(*args,&blk) {
18
+ @__executor__.execute @__meta__, path, args, blk
19
+ }
20
+ }
21
+ end
22
+ (config[:methods]||{}).each do |name,body|
23
+ methods[name] = {
24
+ body: body,
25
+ private: true,
26
+ }
27
+ end
28
+ ClassBuilder.build(
29
+ initialize_params: [:__executor__,:__meta__],
30
+ modules: config[:modules],
31
+ instance_methods: methods,
32
+ )
33
+ end
34
+ end
35
+ end
36
+ end
@@ -0,0 +1,14 @@
1
+ require 'alki/execution/context'
2
+ require 'alki/overlay_delegator'
3
+
4
+ module Alki
5
+ module Execution
6
+ module ValueContext
7
+ include Context
8
+
9
+ def delegate_overlay(obj,overlay,**args)
10
+ Alki::OverlayDelegator.new(obj,overlay,args)
11
+ end
12
+ end
13
+ end
14
+ end
@@ -1,33 +1,21 @@
1
1
  module Alki
2
2
  class OverlayDelegator
3
- def initialize(name,obj,overlay)
4
- @name = name
3
+ def initialize(obj,overlay,info=nil)
5
4
  @obj = obj
6
5
  @overlay = overlay
7
- @key = :"#{obj.object_id}:#{overlay.object_id}"
6
+ @info = info
8
7
  end
9
8
 
10
9
  def respond_to_missing(method,include_private = false)
11
- unless Thread.current[@key]
12
- Thread.current[@key] = true
13
- if @overlay.respond_to? :overlay_respond_to?
14
- @overlay.overlay_respond_to? @obj, method, include_private
15
- else
16
- @obj.respond_to? method, include_private
17
- end
18
- Thread.current[@key] = false
10
+ if @overlay.respond_to? :overlay_respond_to?
11
+ @overlay.overlay_respond_to? @obj, method, include_private
12
+ else
13
+ @obj.respond_to? method, include_private
19
14
  end
20
15
  end
21
16
 
22
17
  def method_missing(method,*args,&blk)
23
- if Thread.current[@key]
24
- res = @obj.send method, *args, &blk
25
- else
26
- Thread.current[@key] = true
27
- res = @overlay.overlay_send @name, @obj, method, *args, &blk
28
- Thread.current[@key] = false
29
- end
30
- res
18
+ @overlay.overlay_send @obj, @info, method, *args, &blk
31
19
  end
32
20
  end
33
- end
21
+ end
@@ -0,0 +1,3 @@
1
+ module Alki
2
+ OverlayInfo = Struct.new(:target,:overlay,:args)
3
+ end
@@ -4,9 +4,11 @@ module Alki
4
4
  module OverrideBuilder
5
5
  def self.build(override_hash=nil,&blk)
6
6
  if blk
7
- Alki::Dsls::Assembly.build(&blk)[:root]
7
+ Alki::Dsls::AssemblyGroup.build(&blk)
8
8
  elsif override_hash && !override_hash.empty?
9
- create_override_group override_hash
9
+ { root: create_override_group(override_hash), overlays: [] }
10
+ else
11
+ { root: nil, overlays: [] }
10
12
  end
11
13
  end
12
14
 
@@ -28,8 +30,8 @@ module Alki
28
30
  end
29
31
 
30
32
  def self.build_type(type,*args)
31
- Alki::Support.load_class("alki/assembly_types/#{type}").new *args
33
+ Alki::Support.load_class("alki/assembly/types/#{type}").new *args
32
34
  end
33
35
 
34
36
  end
35
- end
37
+ end
@@ -2,13 +2,13 @@ require 'delegate'
2
2
 
3
3
  module Alki
4
4
  class ServiceDelegator < Delegator
5
- def initialize(app,path)
6
- @app = app
5
+ def initialize(elem,path)
6
+ @elem = elem
7
7
  @path = path
8
8
  end
9
9
 
10
10
  def __getobj__
11
- @obj ||= @app.lookup(@path)
11
+ @obj ||= @elem.lookup(@path)
12
12
  end
13
13
  end
14
14
  end
data/lib/alki/version.rb CHANGED
@@ -1,3 +1,3 @@
1
1
  module Alki
2
- VERSION = "0.8.0"
2
+ VERSION = "0.9.0"
3
3
  end
data/lib/alki.rb CHANGED
@@ -1,17 +1,17 @@
1
1
  require 'alki/dsl'
2
- require 'alki/assembly_builder'
2
+ require 'alki/assembly/builder'
3
3
 
4
4
  module Alki
5
5
  class << self
6
6
  def project_assembly!(opts={},&blk)
7
7
  opts[:project_assembly] ||= caller_locations(1,1)[0].absolute_path
8
- AssemblyBuilder.build(opts,&blk)
8
+ Assembly::Builder.build(opts,&blk)
9
9
  end
10
10
 
11
11
  alias_method :create_assembly!, :project_assembly!
12
12
 
13
13
  def create_assembly(opts={},&blk)
14
- AssemblyBuilder.build(opts,&blk)
14
+ Assembly::Builder.build(opts,&blk)
15
15
  end
16
16
  end
17
- end
17
+ end
@@ -1,5 +1,4 @@
1
- require_relative '../test_helper'
2
- require 'alki'
1
+ require 'alki/feature_test'
3
2
 
4
3
  describe Alki do
5
4
  describe :create_assembly do
@@ -1,5 +1,4 @@
1
- require_relative '../test_helper'
2
-
1
+ require 'alki/test'
3
2
 
4
3
  $LOAD_PATH.unshift Alki::Test.fixture_path('example','lib')
5
4
  $LOAD_PATH.unshift Alki::Test.fixture_path('tlogger','lib')
@@ -46,4 +45,4 @@ describe 'Example' do
46
45
  it 'should have config_dir value' do
47
46
  Example.new.config_dir.must_equal Alki::Test.fixture_path('example','config')
48
47
  end
49
- end
48
+ end
@@ -0,0 +1,48 @@
1
+ require 'alki/feature_test'
2
+
3
+ describe 'Factories' do
4
+ def assembly(&blk)
5
+ @assembly = Alki.create_assembly(&blk)
6
+ end
7
+
8
+ def obj
9
+ @obj ||= @assembly.new
10
+ end
11
+
12
+ it 'should call returned callable when referenced' do
13
+ assembly do
14
+ factory :test do
15
+ -> (val) { val + 1 }
16
+ end
17
+ end
18
+ obj.test(1).must_equal 2
19
+ end
20
+
21
+ it 'should evaluate the given block once' do
22
+ counter = 0
23
+ proc_counter = 0
24
+ assembly do
25
+ factory :test do
26
+ counter += 1
27
+ -> (val) {
28
+ proc_counter += 1
29
+ val
30
+ }
31
+ end
32
+ end
33
+ obj.test(1)
34
+ obj.test(1)
35
+ counter.must_equal 1
36
+ proc_counter.must_equal 2
37
+ end
38
+
39
+ it 'should return Proc of factory if no arguments are provided when referenced' do
40
+ assembly do
41
+ factory :test do
42
+ -> (val) { val + 1 }
43
+ end
44
+ end
45
+ proc = obj.test
46
+ proc.call(1).must_equal 2
47
+ end
48
+ end
@@ -0,0 +1,225 @@
1
+ require 'alki/feature_test'
2
+
3
+ describe 'Overlays' do
4
+ it 'should allow setting an overlay on a service' do
5
+ values = []
6
+ assembly = Alki.create_assembly do
7
+ service :svc do
8
+ :test
9
+ end
10
+
11
+ overlay :svc, :test_overlay
12
+
13
+ set :test_overlay, ->(value) {values << value; :transformed}
14
+ end
15
+ assembly.new.svc.must_equal :transformed
16
+ values.must_equal [:test]
17
+ end
18
+
19
+ it 'should call new if overlay responds to it' do
20
+ assembly = Alki.create_assembly do
21
+ service :svc do
22
+ :test
23
+ end
24
+
25
+ overlay :svc, :test_overlay
26
+
27
+ set :test_overlay, Struct.new(:val)
28
+ end
29
+ assembly.new.svc.val.must_equal :test
30
+ end
31
+
32
+ it 'should allow using factories as overlays' do
33
+ values = []
34
+ assembly = Alki.create_assembly do
35
+ service :svc do
36
+ :test
37
+ end
38
+
39
+ overlay :svc, :test_overlay
40
+
41
+ factory :test_overlay do
42
+ ->(value) {values << value; :transformed}
43
+ end
44
+ end
45
+ assembly.new.svc.must_equal :transformed
46
+ values.must_equal [:test]
47
+ end
48
+
49
+ it 'should allow setting an overlay on groups of services' do
50
+ values = []
51
+ assembly = Alki.create_assembly do
52
+ service :svc do
53
+ :test
54
+ end
55
+
56
+ group :svcs do
57
+ service :one do
58
+ :svc_one
59
+ end
60
+
61
+ service :two do
62
+ :svc_two
63
+ end
64
+ end
65
+
66
+ overlay :svcs, :test_overlay
67
+
68
+ set :test_overlay, ->(value) {values << value; "overlay_#{value}".to_sym}
69
+ end
70
+ obj = assembly.new
71
+ obj.svc.must_equal :test
72
+ obj.svcs.one.must_equal :overlay_svc_one
73
+ obj.svcs.two.must_equal :overlay_svc_two
74
+ values.must_equal [:svc_one,:svc_two]
75
+ end
76
+
77
+ it 'should not apply to non-services' do
78
+ values = []
79
+ assembly = Alki.create_assembly do
80
+ group :vals do
81
+ set :one do
82
+ :val_one
83
+ end
84
+
85
+ factory :two do
86
+ ->(v) { "val_two_#{v}".to_sym }
87
+ end
88
+
89
+ func :three do
90
+ :val_three
91
+ end
92
+ end
93
+
94
+ overlay :vals, :test_overlay
95
+
96
+ set :test_overlay, ->(value) {values << value; "overlay_#{value}".to_sym}
97
+ end
98
+ obj = assembly.new
99
+ obj.vals.one.must_equal :val_one
100
+ obj.vals.two(1).must_equal :val_two_1
101
+ obj.vals.three.must_equal :val_three
102
+ values.must_equal []
103
+ end
104
+
105
+ it 'should chain overlays when multiple are set' do
106
+ values = []
107
+ assembly = Alki.create_assembly do
108
+
109
+ group :svcs do
110
+ service :svc do
111
+ :test
112
+ end
113
+ end
114
+
115
+ overlay :svcs, :overlay1
116
+ overlay 'svcs.svc', :overlay2
117
+
118
+ set :overlay1, ->(value) {values << value; "overlay_#{value}".to_sym}
119
+ set :overlay2, ->(value) {values << value; :transformed}
120
+
121
+ end
122
+ obj = assembly.new
123
+ obj.svcs.svc.must_equal :transformed
124
+ values.must_equal [:test,:overlay_test]
125
+ end
126
+
127
+ it 'should follow paths when setting overlay targets' do
128
+ values = []
129
+ assembly = Alki.create_assembly do
130
+ service :svc do
131
+ :test
132
+ end
133
+
134
+ group :grp do
135
+ overlay 'assembly.svc', :test_overlay
136
+
137
+ set :test_overlay, ->(value) {values << value; :transformed}
138
+ end
139
+ end
140
+ obj = assembly.new
141
+ obj.svc.must_equal :transformed
142
+ values.must_equal [:test]
143
+ end
144
+
145
+ it 'should raise error if either target or overlay paths are invalid' do
146
+ assembly = Alki.create_assembly do
147
+ service :svc do
148
+ :test
149
+ end
150
+ overlay :invalid, :test_overlay
151
+ set :test_overlay, ->(value) {:child}
152
+ end
153
+ assert_raises Alki::InvalidPathError do
154
+ assembly.new.svc
155
+ end
156
+ assembly = Alki.create_assembly do
157
+ service :svc do
158
+ :test
159
+ end
160
+ overlay :svc, :invalid
161
+ end
162
+ assert_raises Alki::InvalidPathError do
163
+ assembly.new.svc
164
+ end
165
+ end
166
+
167
+ it 'should set overlays through mounted assemblies' do
168
+ child = Alki.create_assembly do
169
+ service :svc do
170
+ :test
171
+ end
172
+ overlay 'parent.svc', :test_overlay
173
+ set :test_overlay, ->(value) {:child}
174
+ end
175
+ assembly = Alki.create_assembly do
176
+ service :svc do
177
+ :test
178
+ end
179
+ mount :mounted, child
180
+ overlay 'mounted.svc', :test_overlay
181
+ set :test_overlay, ->(value) {:parent}
182
+ end
183
+ obj = assembly.new
184
+ obj.svc.must_equal :child
185
+ obj.mounted.svc.must_equal :parent
186
+ end
187
+
188
+ it 'should set overlays from overrides' do
189
+ child = Alki.create_assembly do
190
+ service :svc1 do
191
+ :test
192
+ end
193
+ end
194
+ assembly = Alki.create_assembly do
195
+ service :svc2 do
196
+ :test
197
+ end
198
+ mount :mounted, child do
199
+ overlay 'original.svc1', :test_overlay
200
+ set :test_overlay, ->(value) {:parent}
201
+ end
202
+ end
203
+ obj = assembly.new do
204
+ overlay 'original.svc2', :test_overlay
205
+ set :test_overlay, ->(value) {:child}
206
+ end
207
+ obj.svc2.must_equal :child
208
+ obj.mounted.svc1.must_equal :parent
209
+ end
210
+
211
+ it 'should allow setting overlays on assembly_instance' do
212
+ values = []
213
+ assembly = Alki.create_assembly do
214
+ overlay :assembly_instance, :test_overlay
215
+
216
+ set :test_overlay, ->(value) {
217
+ values << value
218
+ :transformed
219
+ }
220
+ end
221
+ assembly.new.must_equal :transformed
222
+ values.size.must_equal 1
223
+ values[0].must_be_instance_of Alki::Assembly::Instance
224
+ end
225
+ end
@@ -1,6 +1,5 @@
1
- require_relative '../test_helper'
1
+ require 'alki/feature_test'
2
2
  require 'logger'
3
- require 'alki'
4
3
  require 'stringio'
5
4
 
6
5
  describe 'Overrides' do
@@ -0,0 +1,67 @@
1
+ require 'alki/feature_test'
2
+
3
+ describe 'Pseudo Elements' do
4
+ before do
5
+ @config_dir = fixture_path('example','config')
6
+ two = Alki.create_assembly do
7
+ set :num, 2
8
+ set :assembly_num do
9
+ assembly.num
10
+ end
11
+ set :parent_num do
12
+ parent.num
13
+ end
14
+ set :grandparent_num do
15
+ parent.parent.num
16
+ end
17
+ set :root_num do
18
+ root.num
19
+ end
20
+ end
21
+ one = Alki.create_assembly do
22
+ mount :two, two
23
+ set :num, 1
24
+ end
25
+ zero = Alki.create_assembly(config_dir: @config_dir) do
26
+ mount :one, one
27
+ set :num, 0
28
+ set :has_parent do
29
+ respond_to?(:parent)
30
+ end
31
+ end
32
+ @assembly = zero
33
+ @obj = @assembly.new
34
+ end
35
+
36
+ describe 'assembly' do
37
+ it 'should be the local assembly of the referring element' do
38
+ @obj.one.two.assembly_num.must_equal 2
39
+ end
40
+ end
41
+
42
+ describe 'parent' do
43
+ it 'should be the parent assembly of the referring element' do
44
+ @obj.one.two.parent_num.must_equal 1
45
+ end
46
+
47
+ it 'should be attribute on all assemblies with parents' do
48
+ @obj.one.two.grandparent_num.must_equal 0
49
+ end
50
+
51
+ it 'should only exist if assembly has parent' do
52
+ @obj.has_parent.must_equal false
53
+ end
54
+ end
55
+
56
+ describe 'root' do
57
+ it 'should be the root assembly' do
58
+ @obj.one.two.root_num.must_equal 0
59
+ end
60
+ end
61
+
62
+ describe 'config_dir' do
63
+ it 'should be the config_dir setting set in the assembly when it was created' do
64
+ @obj.config_dir.must_equal @config_dir
65
+ end
66
+ end
67
+ end
@@ -0,0 +1 @@
1
+ require 'alki'
@@ -2,6 +2,15 @@ Alki do
2
2
  load :settings
3
3
  load :handlers
4
4
 
5
+ factory :log_overlay do
6
+ require 'log_overlay'
7
+ -> (obj) {
8
+ delegate_overlay obj, LogOverlay.new(log), name: meta[:building]
9
+ }
10
+ end
11
+
12
+ overlay :handlers, :log_overlay
13
+
5
14
  service :handler do
6
15
  require 'switch_handler'
7
16
  SwitchHandler.new [
@@ -31,7 +40,7 @@ Alki do
31
40
  StringIO.new
32
41
  end
33
42
 
34
- assembly :tlogger do
43
+ mount :tlogger do
35
44
  set :io do
36
45
  log_io
37
46
  end
@@ -46,4 +55,4 @@ Alki do
46
55
  msg[0].upcase+msg[1..-1].downcase+"!"
47
56
  }
48
57
  end
49
- end
58
+ end
@@ -2,7 +2,7 @@ Alki do
2
2
  factory :num_handler do
3
3
  require 'num_handler'
4
4
  -> (num,str) {
5
- NumHandler.new num, str, output
5
+ NumHandler.new(num, str, output)
6
6
  }
7
7
  end
8
8
 
@@ -22,9 +22,4 @@ Alki do
22
22
  require 'echo_handler'
23
23
  EchoHandler.new output
24
24
  end
25
-
26
- overlay do
27
- require 'log_overlay'
28
- LogOverlay.new lookup('log')
29
- end
30
- end
25
+ end
@@ -3,8 +3,8 @@ class LogOverlay
3
3
  @log = log
4
4
  end
5
5
 
6
- def overlay_send(name,obj,method,*args,&blk)
7
- @log << "Calling #{name}##{method} #{args.join(", ")}\n"
6
+ def overlay_send(obj,info,method,*args,&blk)
7
+ @log << "Calling #{info[:name]}##{method} #{args.join(", ")}\n"
8
8
  obj.public_send method, *args, &blk
9
9
  end
10
- end
10
+ end
@@ -1,25 +1,20 @@
1
- require_relative '../../test_helper'
1
+ require 'alki/test'
2
2
  require 'alki/dsls/assembly'
3
3
 
4
4
  describe Alki::Dsls::Assembly do
5
- it 'should work' do
5
+ it 'should allow creating Assembly config classes' do
6
6
  res = Alki::Dsls::Assembly.build do
7
7
  service :test do
8
8
  :val
9
9
  end
10
10
 
11
- load 'file'
12
-
13
11
  group :group1 do
14
12
  service :test2 do
15
13
  :val2
16
14
  end
17
15
  end
18
16
  end
19
- res[:class].root.children[:file].name.must_equal 'file'
20
17
  res[:class].root.children[:test].must_respond_to :block
21
18
  res[:class].root.children[:group1].children[:test2].must_respond_to :block
22
-
23
- r = res[:class].root.lookup [:group1,:test2]
24
19
  end
25
- end
20
+ end