alki 0.3.0 → 0.4.0

Sign up to get free protection for your applications and to get access to all the features.
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