alki 0.3.0 → 0.4.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 (45) hide show
  1. checksums.yaml +4 -4
  2. data/Gemfile +2 -0
  3. data/README.md +1 -1
  4. data/alki.gemspec +3 -0
  5. data/config/dsls.rb +4 -0
  6. data/lib/alki/assembly.rb +43 -0
  7. data/lib/alki/assembly_executor.rb +137 -0
  8. data/lib/alki/assembly_handler_base.rb +19 -0
  9. data/lib/alki/dsls/assembly.rb +24 -0
  10. data/lib/alki/dsls/assembly_type.rb +48 -0
  11. data/lib/alki/dsls/assembly_type_dsl.rb +21 -0
  12. data/lib/alki/dsls/assembly_types/assembly.rb +84 -0
  13. data/lib/alki/dsls/assembly_types/group.rb +46 -0
  14. data/lib/alki/dsls/assembly_types/load.rb +37 -0
  15. data/lib/alki/dsls/assembly_types/overlay.rb +9 -0
  16. data/lib/alki/dsls/assembly_types/service.rb +25 -0
  17. data/lib/alki/dsls/service.rb +22 -0
  18. data/lib/alki/test.rb +38 -18
  19. data/lib/alki/version.rb +1 -1
  20. data/lib/alki.rb +27 -21
  21. data/test/feature/create_package_test.rb +4 -3
  22. data/test/fixtures/example/config/{package.rb → assembly.rb} +5 -2
  23. data/test/fixtures/example/config/handlers.rb +1 -1
  24. data/test/fixtures/example/lib/example.rb +1 -1
  25. data/test/fixtures/example/lib/switch_handler.rb +2 -2
  26. data/test/fixtures/tlogger/config/assembly.rb +10 -0
  27. data/test/fixtures/tlogger/lib/tlogger.rb +3 -0
  28. data/test/integration/dsls/assembly_test.rb +28 -0
  29. data/test/integration/dsls/assembly_type_test.rb +58 -0
  30. data/test/integration/dsls/service_dsl_test.rb +57 -0
  31. metadata +69 -21
  32. data/lib/alki/class_builder.rb +0 -41
  33. data/lib/alki/dsl_builder.rb +0 -43
  34. data/lib/alki/group_builder.rb +0 -25
  35. data/lib/alki/group_dsl.rb +0 -98
  36. data/lib/alki/loader.rb +0 -32
  37. data/lib/alki/package.rb +0 -33
  38. data/lib/alki/package_builder.rb +0 -2
  39. data/lib/alki/package_executor.rb +0 -120
  40. data/lib/alki/package_processor.rb +0 -167
  41. data/lib/alki/standard_package.rb +0 -19
  42. data/lib/alki/util.rb +0 -35
  43. data/test/integration/class_builder_test.rb +0 -130
  44. data/test/integration/loader_test.rb +0 -36
  45. data/test/unit/package_processor_test.rb +0 -319
data/lib/alki/package.rb DELETED
@@ -1,33 +0,0 @@
1
- require 'alki/package_executor'
2
- require 'alki/package_processor'
3
-
4
- module Alki
5
- class Package
6
- def initialize(package_definition)
7
- @def = package_definition
8
- @cache = {}
9
- end
10
-
11
- def root
12
- @root ||= __executor__.call @def, @cache, []
13
- end
14
-
15
- def package_definition
16
- @def
17
- end
18
-
19
- def respond_to_missing?(name,include_all)
20
- root.respond_to? name
21
- end
22
-
23
- def method_missing(name,*args,&blk)
24
- root.send name, *args, &blk
25
- end
26
-
27
- private
28
-
29
- def __executor__
30
- @executor ||= Alki::PackageExecutor.new Alki::PackageProcessor.new
31
- end
32
- end
33
- end
@@ -1,2 +0,0 @@
1
- module Alki
2
- end
@@ -1,120 +0,0 @@
1
- require 'alki/service_delegator'
2
- require 'alki/overlay_delegator'
3
-
4
- module Alki
5
- class PackageExecutor
6
- def initialize(processor)
7
- @processor = processor
8
- end
9
-
10
- def call(pkg,cache,path,*args,&blk)
11
- elem = @processor.lookup(pkg,path)
12
- triple = [pkg,cache,elem]
13
- case elem[:type]
14
- when :service
15
- service triple, path, *args
16
- when :factory
17
- factory triple, path, *args, &blk
18
- when :group
19
- group triple, *args
20
- end
21
- end
22
-
23
- def service(triple,path)
24
- pkg,cache,elem = triple
25
- unless cache[path]
26
- with_scope_context(triple) do |ctx,blk|
27
- cache[path] = apply_overlays triple,path, ctx.instance_exec(&blk)
28
- end
29
- end
30
- cache[path]
31
- end
32
-
33
- def apply_overlays((pkg,cache,elem),path,obj)
34
- elem[:overlays].inject(obj) do |obj,overlay_elem|
35
- unless cache[overlay_elem[:block]]
36
- with_scope_context([pkg,cache,overlay_elem]) do |ctx,blk|
37
- cache[overlay_elem[:block]] = ctx.instance_exec(&blk)
38
- end
39
- end
40
- local_path = path[overlay_elem[:scope][:root].size..-1].join('.')
41
- Alki::OverlayDelegator.new local_path,obj, cache[overlay_elem[:block]]
42
- end
43
- end
44
-
45
- def factory(triple,path,*args,&blk)
46
- pkg,cache,elem = triple
47
- unless cache[path]
48
- with_scope_context(triple) do |ctx,blk|
49
- cache[path] = ctx.instance_exec(&blk)
50
- end
51
- end
52
- cache[path].call *args, &blk
53
- end
54
-
55
- def group((pkg,cache,elem))
56
- proc = -> (name,*args,&blk) {
57
- call pkg, cache, elem[:children][name], *args, &blk
58
- }
59
-
60
- GroupContext.new(proc,elem[:children].keys)
61
- end
62
-
63
- def with_scope_context((pkg,cache,elem))
64
- proc = -> (name,*args,&blk) {
65
- call pkg, cache, elem[:scope][name], *args, &blk
66
- }
67
-
68
- yield ServiceContext.new(proc,elem[:scope].keys), elem[:block]
69
- end
70
-
71
- class Context
72
- def initialize(executor,scope)
73
- @executor = executor
74
- @scope = scope
75
- end
76
-
77
- def respond_to_missing?(name,include_all)
78
- @scope.include? name
79
- end
80
-
81
- def method_missing(name,*args,&blk)
82
- if @scope.include? name
83
- @executor.call name, *args, &blk
84
- else
85
- super
86
- end
87
- end
88
- end
89
-
90
- class ServiceContext < Context
91
- def lookup(path)
92
- unless path.is_a?(String) or path.is_a?(Symbol)
93
- raise ArgumentError.new("lookup can only take Strings or Symbols")
94
- end
95
- path.to_s.split('.').inject(self) do |group,name|
96
- raise "Invalid lookup path" unless group.is_a? Context
97
- group.send name.to_sym
98
- end
99
- end
100
-
101
- def lazy(path)
102
- unless path.is_a?(String) or path.is_a?(Symbol)
103
- raise ArgumentError.new("lazy can only take Strings or Symbols")
104
- end
105
- Alki::ServiceDelegator.new root, path
106
- end
107
- end
108
-
109
- class GroupContext < Context
110
- def lookup(path)
111
- unless path.is_a?(String) or path.is_a?(Symbol)
112
- raise ArgumentError.new("lookup can only take Strings or Symbols")
113
- end
114
- path.to_s.split('.').inject(self) do |group,name|
115
- group.send name.to_sym
116
- end
117
- end
118
- end
119
- end
120
- end
@@ -1,167 +0,0 @@
1
- module Alki
2
- class PackageProcessor
3
- def lookup(package,path)
4
- lookup_path(package,path)
5
- end
6
-
7
- private
8
-
9
- def lookup_path(desc, path, data = {})
10
- data_defaults data
11
- path = path.dup
12
- update_data data, desc
13
- until path.empty?
14
- key = path.shift
15
- data[:prefix] += [key]
16
- elem = desc[key]
17
- if !elem
18
- return nil
19
- else
20
- unless path.empty?
21
- if elem[:type] == :group
22
- desc = elem[:children]
23
- update_data data, desc
24
- elsif elem[:type] == :package
25
- return package_lookup_path elem, path, data
26
- else
27
- raise "Invalid path"
28
- end
29
- end
30
- end
31
- end
32
- if elem
33
- case elem[:type]
34
- when :group
35
- {type: :group, children: prefix_keys(elem[:children], data[:prefix])}
36
- when :package
37
- children = prefix_keys([elem[:children],elem[:overrides]], data[:prefix])
38
- {type: :group, children: children}
39
- when :service, :factory
40
- last_clear = data[:overlays].rindex(:clear)
41
- overlays = last_clear ? data[:overlays][(last_clear + 1)..-1] : data[:overlays]
42
- {type: elem[:type], block: elem[:block], overlays: overlays, scope: data[:scope]}
43
- else
44
- raise "Invalid elem type: #{elem[:type]}"
45
- end
46
- else
47
- {type: :group, children: prefix_keys(desc,data[:prefix])}
48
- end
49
- end
50
-
51
- def package_lookup_path(elem,path,data)
52
- if path[0] == :orig
53
- if path.size == 1
54
- {type: :group, children: prefix_keys(elem[:children],data[:prefix]+[:orig])}
55
- else
56
- lookup_path(elem[:children],path[1..-1],data.merge(scope:nil))
57
- end
58
- else
59
- override_scope = data[:scope].merge(pkg: data[:prefix]+[:orig])
60
- override_data = data.merge(overlays: data[:overlays].dup, scope: override_scope)
61
- lookup_path(merge_overlays(elem[:overrides],elem[:children]), path, override_data) or
62
- lookup_path(
63
- merge_overrides(elem[:children], elem[:overrides]),
64
- path, data.merge(scope: nil)
65
- )
66
- end
67
- end
68
-
69
- def data_defaults(data)
70
- data[:overlays] ||= []
71
- data[:prefix] ||= []
72
- data[:scope] ||= {root: data[:prefix]}
73
- end
74
-
75
- def prefix_keys(source, prefix, result={})
76
- source = [source] unless source.is_a? Array
77
- source.inject([]){|a,h| a|=h.keys}.inject(result) do |h,k|
78
- h.merge! k => (prefix+[k])
79
- end
80
- result
81
- end
82
-
83
- def update_data(data,desc)
84
- prefix_keys desc, data[:prefix], data[:scope]
85
- if desc['overlays']
86
- data[:overlays].push *desc['overlays'].map { |o|
87
- o == :clear ? o : {block: o, scope: data[:scope]}
88
- }
89
- end
90
- end
91
-
92
- def merge_overlays(a,b)
93
- updates = {}
94
- b.keys.each do |k|
95
- if k == 'overlays'
96
- updates[k] = (a[k] || []) + b[k]
97
- elsif a.key? k
98
- val = merge_item_overlays(a[k],b[k])
99
- updates[k] = val unless a[k].equal? val
100
- end
101
- end
102
- if updates.empty?
103
- a
104
- else
105
- a.merge updates
106
- end
107
- end
108
-
109
- def merge_item_overlays(a,b)
110
- if a[:children] && b[:children]
111
- if b[:type] == :package
112
- b_children = merge_overlays(b[:children],b[:overrides])
113
- elsif b[:type] == :group
114
- b_children = b[:children]
115
- end
116
- if a[:type] == :package
117
- a.merge(overrides: merge_overlays(a[:overrides],b_children))
118
- elsif b[:type] == :group
119
- a.merge(children: merge_overlays(a[:children],b_children))
120
- end
121
- else
122
- a
123
- end
124
- end
125
-
126
- def merge_overrides(a,b)
127
- updates = {}
128
- b.keys.each do |k|
129
- if a.key? k
130
- if k == 'overlays'
131
- updates[k] = b[k] + a[k]
132
- else
133
- val = merge_item(a[k],b[k])
134
- updates[k] = val unless a[k].equal? val
135
- end
136
- else
137
- updates[k] = b[k]
138
- end
139
- end
140
- if updates.empty?
141
- a
142
- else
143
- a.merge updates
144
- end
145
-
146
- end
147
-
148
- def merge_item(a,b)
149
- if a[:children] && b[:children]
150
- if b[:type] == :package
151
- b_children = merge_overrides(b[:children],b[:overrides])
152
- elsif b[:type] == :group
153
- b_children = b[:children]
154
- end
155
- if a[:type] == :package
156
- a.merge(overrides: merge_overrides(a[:overrides],b_children))
157
- elsif b[:type] == :group
158
- a.merge(children: merge_overrides(a[:children],b_children))
159
- end
160
- elsif a != b
161
- b
162
- else
163
- a
164
- end
165
- end
166
- end
167
- end
@@ -1,19 +0,0 @@
1
- require 'alki/package'
2
- require 'alki/loader'
3
- require 'alki/group_builder'
4
-
5
- module Alki
6
- class StandardPackage < Package
7
- def initialize(root_dir)
8
- @root_dir = root_dir
9
- loader = Alki::Loader.new(File.join(root_dir,'config'))
10
- builder = Alki::GroupBuilder.new loader
11
- pkg = builder.build &loader.load('package')
12
- loader_proc = ->() { loader }
13
- builder.build pkg do
14
- service :loader, &loader_proc
15
- end
16
- super pkg
17
- end
18
- end
19
- end
data/lib/alki/util.rb DELETED
@@ -1,35 +0,0 @@
1
- module Alki
2
- module Util
3
- def self.classify(str)
4
- str.split('/').map do |c|
5
- c.split('_').map{|n| n.capitalize }.join('')
6
- end.join('::')
7
- end
8
-
9
- def self.create_class(name,klass = Class.new)
10
- *ans, ln = name.to_s.split(/::/)
11
- parent = Object
12
- ans.each do |a|
13
- unless parent.const_defined? a
14
- parent.const_set a, Module.new
15
- end
16
- parent = parent.const_get a
17
- end
18
-
19
- parent.const_set ln, klass
20
- end
21
-
22
- def self.find_pkg_root(path)
23
- old_dir = File.absolute_path(path)
24
- dir = File.dirname(old_dir)
25
- until dir == old_dir || File.exists?(File.join(dir,'config','package.rb'))
26
- old_dir = dir
27
- dir = File.dirname(old_dir)
28
- end
29
- if dir == old_dir
30
- raise "Couldn't find app root"
31
- end
32
- dir
33
- end
34
- end
35
- end
@@ -1,130 +0,0 @@
1
- require 'alki/test'
2
- require 'alki/class_builder'
3
-
4
- describe Alki::ClassBuilder do
5
- class DoitBuilder
6
- def initialize(c)
7
- @c = c
8
- @num = 0
9
- end
10
-
11
- def dsl_methods
12
- [:doit]
13
- end
14
-
15
- def doit(&blk)
16
- @c.send(:define_method,"doit#{@num}",&blk)
17
- @num += 1
18
- end
19
-
20
- def finalize
21
- num = @num
22
- @c.define_singleton_method :doits do
23
- num
24
- end
25
-
26
- @c.send(:define_method,:doit_all) do
27
- num.times.map {|i| send "doit#{i}"}
28
- end
29
- end
30
- end
31
-
32
- class JobTypeBuilder
33
- def initialize(c)
34
- @c = c
35
- end
36
-
37
- def dsl_methods
38
- [:job]
39
- end
40
-
41
- def job(job)
42
- @job = job
43
- end
44
-
45
- def finalize
46
- job = @job
47
- @c.send :define_method, :job do
48
- job
49
- end
50
- end
51
- end
52
-
53
- describe :build do
54
- it 'should allow calling methods in dsl' do
55
- c = Alki::ClassBuilder.build(DoitBuilder) do
56
- doit { "hello" }
57
- doit { "world" }
58
- end
59
- c.new.doit_all.must_equal ["hello","world"]
60
- end
61
-
62
- it 'should allow defining normal methods' do
63
- c = Alki::ClassBuilder.build(DoitBuilder) do
64
- def initialize(msg)
65
- @msg = msg
66
- end
67
- def msg
68
- @msg
69
- end
70
- doit { msg }
71
- doit { "world" }
72
- end
73
- obj = c.new "hello"
74
- obj.msg.must_equal "hello"
75
- obj.doit_all.must_equal ["hello","world"]
76
- end
77
-
78
- it 'should allow setting class methods' do
79
- c = Alki::ClassBuilder.build(DoitBuilder) do
80
- def klass.doit_count
81
- doits
82
- end
83
- end
84
- c.doit_count.must_equal 0
85
- end
86
-
87
- it 'should not make dsl methods available on class' do
88
- c = Alki::ClassBuilder.build(DoitBuilder) do
89
- end
90
- c.methods.wont_include :doit
91
- assert_raises NoMethodError do
92
- c.doit { puts "test" }
93
- end
94
- assert_raises NoMethodError do
95
- c.send(:doit) { puts "test" }
96
- end
97
- end
98
-
99
- it 'should only allow calling dsl methods returned from #dsl_methods' do
100
- assert_raises NoMethodError do
101
- Alki::ClassBuilder.build(DoitBuilder) do
102
- finalize()
103
- end
104
- end
105
- end
106
-
107
- it 'should allow creating classes with names' do
108
- Alki::ClassBuilder.build(:TestClass,DoitBuilder) do
109
- end
110
- TestClass.new.doit_all.must_equal []
111
- end
112
-
113
- it 'should allow creating classes in modules' do
114
- module TestModule; end
115
- Alki::ClassBuilder.build('TestModule::TestClass',DoitBuilder) do
116
- end
117
- TestModule::TestClass.new.doit_all.must_equal []
118
- end
119
-
120
- it 'should allow passing in multiple dsl builders' do
121
- c = Alki::ClassBuilder.build([DoitBuilder,JobTypeBuilder]) do
122
- job "greeter"
123
- doit { "Welcome!" }
124
- end
125
- obj = c.new
126
- obj.doit_all.must_equal ["Welcome!"]
127
- obj.job.must_equal "greeter"
128
- end
129
- end
130
- end
@@ -1,36 +0,0 @@
1
- require_relative '../test_helper'
2
-
3
- require 'alki/loader'
4
-
5
- describe Alki::Loader do
6
- describe 'load' do
7
- before do
8
- @loader = Alki::Loader.new fixtures_path
9
- end
10
-
11
- it 'should load config file from root directory' do
12
- @loader.load(:config).call.must_equal 0
13
- end
14
- end
15
-
16
- describe 'self.load' do
17
- before do
18
- @config_path = fixture_path('config.rb')
19
- end
20
-
21
- it 'should load a config file and call block with proc' do
22
- Alki::Loader::load(@config_path).call.must_equal 0
23
- end
24
-
25
- it 'should be threadsafe' do
26
- t1 = Thread.new do
27
- $wait = 1
28
- Alki::Loader::load(@config_path).call
29
- end
30
- sleep 0.1
31
- $wait = 0
32
- Alki::Loader::load(@config_path).call.must_equal 0
33
- t1.value.must_equal 1
34
- end
35
- end
36
- end