alki 0.12.0 → 0.12.1
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/Gemfile +1 -0
- data/README.adoc +132 -20
- data/Rakefile +2 -27
- data/alki.gemspec +1 -3
- data/bin/bundler +17 -0
- data/bin/rake +17 -0
- data/doc/assemblies.adoc +2 -3
- data/doc/assembly_dsl.adoc +191 -30
- data/doc/{projects.adoc → executables.adoc} +40 -16
- data/doc/index.adoc +4 -3
- data/lib/alki.rb +3 -0
- data/lib/alki/assembly.rb +4 -34
- data/lib/alki/assembly/builder.rb +3 -3
- data/lib/alki/assembly/instance.rb +31 -5
- data/lib/alki/assembly/instance_builder.rb +48 -0
- data/lib/alki/assembly/meta/overlay.rb +7 -2
- data/lib/alki/assembly/meta/tags.rb +4 -4
- data/lib/alki/assembly/types.rb +9 -0
- data/lib/alki/assembly/types/assembly.rb +3 -3
- data/lib/alki/assembly/types/group.rb +16 -25
- data/lib/alki/assembly/types/original.rb +12 -0
- data/lib/alki/assembly/types/override.rb +0 -2
- data/lib/alki/assembly/types/service.rb +4 -4
- data/lib/alki/circular_reference_error.rb +25 -0
- data/lib/alki/dsls/assembly.rb +1 -1
- data/lib/alki/dsls/assembly_group.rb +28 -12
- data/lib/alki/execution/context_class_builder.rb +1 -1
- data/lib/alki/execution/helpers.rb +4 -4
- data/lib/alki/execution/overlay_map.rb +37 -0
- data/lib/alki/execution/tag_map.rb +42 -0
- data/lib/alki/executor.rb +140 -0
- data/lib/alki/override_builder.rb +30 -24
- data/lib/alki/overrides.rb +4 -0
- data/lib/alki/version.rb +1 -1
- data/test/feature/mounts_test.rb +15 -0
- data/test/feature/multithreading_test.rb +0 -3
- data/test/feature/overlays_test.rb +2 -2
- data/test/feature/overrides_test.rb +26 -1
- data/test/feature/references_test.rb +35 -0
- data/test/feature/try_mounts_test.rb +23 -0
- data/test/feature/values_test.rb +14 -0
- data/test/feature_test_helper.rb +1 -0
- data/test/fixtures/example/config/assembly.rb +17 -8
- data/test/fixtures/example/config/handlers.rb +10 -5
- data/test/fixtures/example/lib/dsls/num_handler.rb +2 -2
- data/test/fixtures/example/lib/example/array_output.rb +13 -0
- data/test/fixtures/example/lib/example/echo_handler.rb +11 -0
- data/test/fixtures/example/lib/example/log_overlay.rb +12 -0
- data/test/fixtures/example/lib/example/num_handler.rb +13 -0
- data/test/fixtures/example/lib/example/range_handler.rb +13 -0
- data/test/fixtures/example/lib/example/switch_handler.rb +11 -0
- metadata +39 -44
- data/lib/alki/assembly/executor.rb +0 -137
- data/test/fixtures/example/lib/array_output.rb +0 -11
- data/test/fixtures/example/lib/echo_handler.rb +0 -9
- data/test/fixtures/example/lib/log_overlay.rb +0 -10
- data/test/fixtures/example/lib/num_handler.rb +0 -11
- data/test/fixtures/example/lib/range_handler.rb +0 -11
- data/test/fixtures/example/lib/switch_handler.rb +0 -9
@@ -1,36 +1,48 @@
|
|
1
|
-
|
2
|
-
|
1
|
+
Creating Executables
|
2
|
+
====================
|
3
3
|
:toc:
|
4
4
|
|
5
|
-
Generally, projects that use alki
|
6
|
-
|
5
|
+
Generally, projects that use alki are setup the same as normal Ruby projects but there are a couple of
|
6
|
+
few differences plus some common conventions.
|
7
7
|
|
8
|
-
Alki projects should always use Bundler and have a `Gemfile` in the project root. It is also optional
|
9
|
-
that the project itself be a gem, with a `<project name>.gemspec` file in the project root and a `gemspec`
|
10
|
-
line in the `Gemfile`.
|
11
8
|
|
12
|
-
|
13
|
-
|
14
|
-
----------------------------
|
9
|
+
Without a gemspec
|
10
|
+
-----------------
|
15
11
|
|
16
12
|
If your project isn't a gem, and doesn't have a gemspec, executables should go in `bin` and should include these two
|
17
13
|
lines before `require`-ing any other files.
|
18
14
|
```ruby
|
19
15
|
require 'bundler/setup'
|
20
|
-
require 'alki/bin
|
16
|
+
require 'alki/bin'
|
21
17
|
```
|
22
18
|
|
23
|
-
After that you can
|
19
|
+
After that you can use your library.
|
20
|
+
|
21
|
+
.bin/my_exe
|
22
|
+
```ruby
|
23
|
+
require 'bundler/setup'
|
24
|
+
require 'alki/bin'
|
25
|
+
|
26
|
+
require 'my_project'
|
24
27
|
|
25
|
-
|
26
|
-
|
28
|
+
MyProject.new.cli.run
|
29
|
+
```
|
30
|
+
|
31
|
+
```
|
32
|
+
$ bin/my_exe
|
33
|
+
...
|
34
|
+
```
|
35
|
+
|
36
|
+
|
37
|
+
With a gemspec
|
38
|
+
--------------
|
27
39
|
|
28
40
|
If your project *is* a gem, you'll do things a bit differently.
|
29
41
|
|
30
42
|
First, in your gemspec, set the `bindir` option to `'exe'` (not `'bin'`). Also if automatically setting
|
31
43
|
the `executables` option, make sure it is looking for files in `exe`, not `bin`.
|
32
44
|
|
33
|
-
|
45
|
+
Executables should be placed in `exe`, not `bin`.
|
34
46
|
|
35
47
|
Finally, once your gemspec is setup, run `bundle binstubs <gem name>` where `<gem name>` is whatever
|
36
48
|
you set `spec.name` to in your gemspec.
|
@@ -40,5 +52,17 @@ should run for testing/development. If you add new executables to `exe`, just ru
|
|
40
52
|
generate new binstubs for them.
|
41
53
|
|
42
54
|
Second, executables in `exe` shouldn't require any extra files other then the project files they need to
|
43
|
-
run (
|
55
|
+
run (**do not require `bundle/setup` or `alki/bin`**).
|
56
|
+
|
57
|
+
.exe/my_exe
|
58
|
+
```ruby
|
59
|
+
require 'my_gem'
|
44
60
|
|
61
|
+
MyGem.new.cli.run
|
62
|
+
```
|
63
|
+
|
64
|
+
```
|
65
|
+
$ bundle binstubs my_gem
|
66
|
+
$ bin/my_exe
|
67
|
+
...
|
68
|
+
```
|
data/doc/index.adoc
CHANGED
@@ -6,11 +6,12 @@ Alki is a framework for creating projects that are modular, testable, and well o
|
|
6
6
|
|
7
7
|
It's goal is to remove uncertainty and friction when building Ruby projects, allowing developers to focus on implementing business logic.
|
8
8
|
|
9
|
-
:leveloffset: 1
|
10
|
-
include::projects.adoc[]
|
11
|
-
|
12
9
|
:leveloffset: 1
|
13
10
|
include::assemblies.adoc[]
|
14
11
|
|
15
12
|
:leveloffset: 1
|
16
13
|
include::assembly_dsl.adoc[]
|
14
|
+
|
15
|
+
:leveloffset: 1
|
16
|
+
include::executables.adoc[]
|
17
|
+
|
data/lib/alki.rb
CHANGED
data/lib/alki/assembly.rb
CHANGED
@@ -1,35 +1,11 @@
|
|
1
|
-
require 'alki/override_builder'
|
2
1
|
require 'alki/assembly/instance'
|
3
|
-
require 'alki/
|
4
|
-
require 'alki/assembly/meta/overlay'
|
5
|
-
require 'alki/overlay_info'
|
6
|
-
require 'ice_nine'
|
2
|
+
require 'alki/override_builder'
|
7
3
|
|
8
4
|
module Alki
|
9
5
|
module Assembly
|
10
|
-
def new(
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
def raw_instance(overrides,blk,&wrapper)
|
15
|
-
overrides_info = OverrideBuilder.build(overrides,&blk)
|
16
|
-
override_root = overrides_info[:root] || build(:group)
|
17
|
-
|
18
|
-
assembly = build :assembly, root, override_root
|
19
|
-
update_instance_overlay = [[],Meta::Overlay.new(
|
20
|
-
:value,
|
21
|
-
[:assembly_instance],
|
22
|
-
->obj{wrapper.call obj},
|
23
|
-
[]
|
24
|
-
)]
|
25
|
-
all_meta = meta+overrides_info[:meta]+[update_instance_overlay]
|
26
|
-
IceNine.deep_freeze all_meta
|
27
|
-
executor = Executor.new(assembly, all_meta)
|
28
|
-
|
29
|
-
override_root.children[:assembly_instance] = build(:service,->{ root })
|
30
|
-
override_root.children[:assembly_executor] = build(:value,executor)
|
31
|
-
|
32
|
-
executor.call [:assembly_instance]
|
6
|
+
def new(override_values={},&override_blk)
|
7
|
+
overrides = OverrideBuilder.build override_values, &override_blk
|
8
|
+
Instance.new load_class, overrides
|
33
9
|
end
|
34
10
|
|
35
11
|
def root
|
@@ -39,11 +15,5 @@ module Alki
|
|
39
15
|
def meta
|
40
16
|
self.definition.meta
|
41
17
|
end
|
42
|
-
|
43
|
-
private
|
44
|
-
|
45
|
-
def build(type,*args)
|
46
|
-
Alki.load("alki/assembly/types/#{type}").new *args
|
47
|
-
end
|
48
18
|
end
|
49
19
|
end
|
@@ -46,8 +46,8 @@ module Alki
|
|
46
46
|
|
47
47
|
def setup_project_assembly(path)
|
48
48
|
root = Alki::Support.find_root(path) do |dir|
|
49
|
-
File.
|
50
|
-
File.
|
49
|
+
File.exist?(File.join(dir,'config',"#{@primary_config}.rb")) ||
|
50
|
+
File.exist?(File.join(dir,'Gemfile')) ||
|
51
51
|
!Dir.glob(File.join(dir,'*.gemspec')).empty?
|
52
52
|
end
|
53
53
|
if root
|
@@ -62,7 +62,7 @@ module Alki
|
|
62
62
|
|
63
63
|
unless @config_dir
|
64
64
|
config_dir = File.join(root,'config')
|
65
|
-
@config_dir = config_dir if File.
|
65
|
+
@config_dir = config_dir if File.exist? config_dir
|
66
66
|
end
|
67
67
|
end
|
68
68
|
end
|
@@ -1,16 +1,19 @@
|
|
1
1
|
require 'delegate'
|
2
2
|
require 'concurrent'
|
3
3
|
require 'alki/support'
|
4
|
+
require 'alki/assembly/instance_builder'
|
4
5
|
|
5
6
|
module Alki
|
6
7
|
module Assembly
|
7
8
|
class Instance < Delegator
|
8
|
-
def initialize(assembly_module,
|
9
|
+
def initialize(assembly_module,overrides)
|
9
10
|
@assembly_module = assembly_module
|
10
|
-
@
|
11
|
+
@overrides = overrides
|
11
12
|
@version = 0
|
12
13
|
@needs_load = true
|
13
14
|
@lock = Concurrent::ReentrantReadWriteLock.new
|
15
|
+
@obj = nil
|
16
|
+
@executor = nil
|
14
17
|
end
|
15
18
|
|
16
19
|
def __reload__
|
@@ -30,6 +33,13 @@ module Alki
|
|
30
33
|
end
|
31
34
|
end
|
32
35
|
|
36
|
+
def __executor__
|
37
|
+
@lock.with_read_lock do
|
38
|
+
__load__ if @needs_load
|
39
|
+
@executor
|
40
|
+
end
|
41
|
+
end
|
42
|
+
|
33
43
|
def __getobj__
|
34
44
|
@lock.with_read_lock do
|
35
45
|
__load__ if @needs_load
|
@@ -37,15 +47,31 @@ module Alki
|
|
37
47
|
end
|
38
48
|
end
|
39
49
|
|
50
|
+
def to_s
|
51
|
+
inspect
|
52
|
+
end
|
53
|
+
|
54
|
+
def inspect
|
55
|
+
if @assembly_module.is_a?(Module)
|
56
|
+
name = @assembly_module.name || 'AnonymousAssembly'
|
57
|
+
else
|
58
|
+
name = Alki::Support.classify(@assembly_module.to_s)
|
59
|
+
end
|
60
|
+
"#<#{name}:#{object_id}>"
|
61
|
+
end
|
62
|
+
|
63
|
+
def pretty_print(q)
|
64
|
+
q.text(inspect)
|
65
|
+
end
|
40
66
|
private
|
41
67
|
|
42
68
|
def __load__
|
43
|
-
# Calls __setobj__
|
44
69
|
@lock.with_write_lock do
|
45
70
|
@needs_load = false
|
46
71
|
@obj.__unload__ if @obj.respond_to?(:__unload__)
|
47
|
-
|
48
|
-
@obj =
|
72
|
+
InstanceBuilder.build @assembly_module, @overrides do |instance,executor|
|
73
|
+
@obj = instance
|
74
|
+
@executor = executor
|
49
75
|
self
|
50
76
|
end
|
51
77
|
end
|
@@ -0,0 +1,48 @@
|
|
1
|
+
require 'alki/assembly/types'
|
2
|
+
require 'alki/executor'
|
3
|
+
require 'alki/override_builder'
|
4
|
+
require 'alki/assembly/meta/overlay'
|
5
|
+
require 'alki/overrides'
|
6
|
+
require 'ice_nine'
|
7
|
+
|
8
|
+
module Alki
|
9
|
+
module Assembly
|
10
|
+
module InstanceBuilder
|
11
|
+
class << self
|
12
|
+
def build(assembly,overrides,&instance_wrapper)
|
13
|
+
assembly = Alki.load(assembly)
|
14
|
+
executor = Executor.new
|
15
|
+
|
16
|
+
overrides = inject_assembly_instance overrides, instance_wrapper, executor
|
17
|
+
|
18
|
+
executor.root = Types.build :assembly, assembly.root, overrides.root
|
19
|
+
executor.meta = IceNine.deep_freeze(assembly.meta+overrides.meta)
|
20
|
+
|
21
|
+
executor.call [:assembly_instance]
|
22
|
+
end
|
23
|
+
|
24
|
+
private
|
25
|
+
|
26
|
+
def inject_assembly_instance(overrides,instance_wrapper,executor)
|
27
|
+
root = overrides.root.dup
|
28
|
+
root.children = root.children.merge(assembly_instance: assembly_instance)
|
29
|
+
meta = overrides.meta + [wrap_assembly_instance(instance_wrapper,executor)]
|
30
|
+
Overrides.new(root,meta)
|
31
|
+
end
|
32
|
+
|
33
|
+
def assembly_instance
|
34
|
+
Types.build(:service,-> { root })
|
35
|
+
end
|
36
|
+
|
37
|
+
def wrap_assembly_instance(wrapper,executor)
|
38
|
+
[[],Meta::Overlay.new(
|
39
|
+
:value,
|
40
|
+
[:assembly_instance],
|
41
|
+
-> obj { wrapper.call obj, executor },
|
42
|
+
[]
|
43
|
+
)]
|
44
|
+
end
|
45
|
+
end
|
46
|
+
end
|
47
|
+
end
|
48
|
+
end
|
@@ -1,5 +1,6 @@
|
|
1
1
|
require 'alki/invalid_path_error'
|
2
2
|
require 'alki/overlay_info'
|
3
|
+
require 'alki/execution/overlay_map'
|
3
4
|
|
4
5
|
module Alki
|
5
6
|
module Assembly
|
@@ -14,7 +15,8 @@ module Alki
|
|
14
15
|
|
15
16
|
def process(executor,from,data)
|
16
17
|
data[:total_overlays] ||= 0
|
17
|
-
data[:overlays]||=
|
18
|
+
data[:overlays] ||= Execution::OverlayMap.new
|
19
|
+
|
18
20
|
target_path = @target.dup
|
19
21
|
if target_path.last.to_s.start_with?('%')
|
20
22
|
tag = target_path.pop
|
@@ -22,8 +24,10 @@ module Alki
|
|
22
24
|
if target_path == []
|
23
25
|
target_path = [:root]
|
24
26
|
end
|
27
|
+
|
25
28
|
target = executor.canonical_path(from,target_path) or
|
26
29
|
raise InvalidPathError.new("Invalid overlay target #{@target.join('.')}")
|
30
|
+
|
27
31
|
target = target.dup.push tag if tag
|
28
32
|
overlay = @overlay
|
29
33
|
if overlay.is_a?(Array)
|
@@ -31,7 +35,8 @@ module Alki
|
|
31
35
|
raise InvalidPathError.new("Invalid overlay path #{@overlay.join('.')}")
|
32
36
|
end
|
33
37
|
order = data[:total_overlays]
|
34
|
-
|
38
|
+
|
39
|
+
data[:overlays].add target, OverlayInfo.new(order,@type, overlay, @args)
|
35
40
|
data[:total_overlays] += 1
|
36
41
|
end
|
37
42
|
end
|
@@ -1,3 +1,5 @@
|
|
1
|
+
require 'alki/execution/tag_map'
|
2
|
+
|
1
3
|
module Alki
|
2
4
|
module Assembly
|
3
5
|
module Meta
|
@@ -7,10 +9,8 @@ module Alki
|
|
7
9
|
end
|
8
10
|
|
9
11
|
def process(_executor,from,data)
|
10
|
-
data[:tags]||=
|
11
|
-
|
12
|
-
(data[:tags][tag.to_sym]||={})[from] = value
|
13
|
-
end
|
12
|
+
data[:tags]||=Execution::TagMap.new
|
13
|
+
data[:tags].add from, @tags
|
14
14
|
end
|
15
15
|
end
|
16
16
|
end
|
@@ -1,4 +1,5 @@
|
|
1
1
|
require 'alki/assembly/types/override'
|
2
|
+
require 'alki/assembly/types/original'
|
2
3
|
|
3
4
|
Alki do
|
4
5
|
attr :root
|
@@ -16,7 +17,7 @@ Alki do
|
|
16
17
|
data[:scope][k] = data[:prefix] + [k]
|
17
18
|
end
|
18
19
|
end
|
19
|
-
root
|
20
|
+
Alki::Assembly::Types::Original.new root
|
20
21
|
else
|
21
22
|
if overrides
|
22
23
|
data.replace(
|
@@ -40,9 +41,8 @@ Alki do
|
|
40
41
|
|
41
42
|
output = root.output(data)
|
42
43
|
add_parent_path output[:scope]
|
43
|
-
update_scope data, output[:
|
44
|
+
update_scope data, output[:scope]
|
44
45
|
output[:scope].merge! overrides.output(data)[:scope] if overrides
|
45
|
-
output[:full_scope].merge! overrides.output(data)[:full_scope] if overrides
|
46
46
|
output
|
47
47
|
end
|
48
48
|
|
@@ -1,5 +1,4 @@
|
|
1
1
|
Alki do
|
2
|
-
require 'ostruct'
|
3
2
|
require 'alki/execution/helpers'
|
4
3
|
|
5
4
|
attr(:children){ {} }
|
@@ -7,30 +6,12 @@ Alki do
|
|
7
6
|
index do
|
8
7
|
update_scope children, data[:prefix], data[:scope]
|
9
8
|
|
10
|
-
data[:tags]
|
11
|
-
|
12
|
-
tagged.each do |path,value|
|
13
|
-
if path.empty? || path[0] == key.to_sym
|
14
|
-
(tags[tag]||={})[(path[1..-1]||[])] = value
|
15
|
-
end
|
16
|
-
end
|
17
|
-
tags
|
9
|
+
if data[:tags]
|
10
|
+
data[:tags] = data[:tags].index key
|
18
11
|
end
|
19
12
|
|
20
|
-
data[:overlays]
|
21
|
-
|
22
|
-
target = target.dup
|
23
|
-
if target.size == 1 && target[0].to_s.start_with?('%')
|
24
|
-
tags = data[:tags][target[0].to_s[1..-1].to_sym]
|
25
|
-
if tags
|
26
|
-
tags.keys.each do |path|
|
27
|
-
(no[path]||=[]).push *overlays
|
28
|
-
end
|
29
|
-
end
|
30
|
-
elsif target.empty? || target.shift == key.to_sym
|
31
|
-
(no[target]||=[]).push *overlays
|
32
|
-
end
|
33
|
-
no
|
13
|
+
if data[:overlays]
|
14
|
+
data[:overlays] = data[:overlays].index key, data[:tags]
|
34
15
|
end
|
35
16
|
|
36
17
|
data[:prefix] << key
|
@@ -41,13 +22,23 @@ Alki do
|
|
41
22
|
output do
|
42
23
|
children_names = children.keys.map(&:to_sym)
|
43
24
|
{
|
44
|
-
|
45
|
-
scope: update_scope(children,data[:prefix]),
|
25
|
+
lookup_methods: update_scope(children,data[:prefix]),
|
26
|
+
scope: update_scope(children, data[:prefix], data[:scope]),
|
46
27
|
modules: [Alki::Execution::Helpers],
|
47
28
|
methods: {
|
48
29
|
children: -> {
|
49
30
|
children_names
|
50
31
|
},
|
32
|
+
elements: -> {
|
33
|
+
children.inject([]) do |elems, child_name|
|
34
|
+
child = send(child_name)
|
35
|
+
if child.respond_to?(:elements)
|
36
|
+
elems.push *child.elements
|
37
|
+
else
|
38
|
+
elems.push child
|
39
|
+
end
|
40
|
+
end
|
41
|
+
}
|
51
42
|
},
|
52
43
|
proc: ->{self}
|
53
44
|
}
|