ntl-orchestra 0.9.2 → 0.9.3
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.
- checksums.yaml +4 -4
- data/.travis.yml +3 -0
- data/Gemfile +1 -1
- data/README.md +91 -90
- data/lib/orchestra/conductor.rb +6 -6
- data/lib/orchestra/dsl/object_adapter.rb +16 -16
- data/lib/orchestra/dsl/operations.rb +35 -35
- data/lib/orchestra/dsl/{nodes.rb → steps.rb} +7 -9
- data/lib/orchestra/execution.rb +158 -0
- data/lib/orchestra/operation.rb +16 -16
- data/lib/orchestra/recording/playback.rb +47 -0
- data/lib/orchestra/recording.rb +5 -45
- data/lib/orchestra/run_list.rb +49 -49
- data/lib/orchestra/{node → step}/output.rb +8 -8
- data/lib/orchestra/{node.rb → step.rb} +22 -25
- data/lib/orchestra/thread_pool.rb +3 -3
- data/lib/orchestra/version.rb +1 -1
- data/lib/orchestra.rb +2 -2
- data/test/examples/fizz_buzz.rb +5 -5
- data/test/examples/invitation_service.rb +9 -9
- data/test/integration/multithreading_test.rb +5 -5
- data/test/integration/recording_telemetry_test.rb +3 -6
- data/test/integration/replayable_operation_test.rb +4 -4
- data/test/lib/console.rb +1 -1
- data/test/support/telemetry_recorder.rb +7 -7
- data/test/unit/dsl_test.rb +26 -26
- data/test/unit/object_adapter_test.rb +14 -14
- data/test/unit/operation_test.rb +45 -45
- data/test/unit/run_list_test.rb +12 -12
- data/test/unit/step_test.rb +122 -0
- data/test/unit/thread_pool_test.rb +2 -2
- metadata +15 -13
- data/lib/orchestra/performance.rb +0 -137
- data/test/unit/node_test.rb +0 -122
@@ -5,61 +5,61 @@ module Orchestra
|
|
5
5
|
attr_writer :command, :result
|
6
6
|
|
7
7
|
def initialize
|
8
|
-
@
|
8
|
+
@steps = {}
|
9
9
|
end
|
10
10
|
|
11
11
|
def build_operation
|
12
12
|
raise ArgumentError, "Must supply a result" if @result.nil?
|
13
|
-
raise ArgumentError, "Must supply at least one
|
13
|
+
raise ArgumentError, "Must supply at least one step" if @steps.empty?
|
14
14
|
Operation.new(
|
15
15
|
:command => @command,
|
16
|
-
:
|
16
|
+
:steps => @steps,
|
17
17
|
:result => @result,
|
18
18
|
)
|
19
19
|
end
|
20
20
|
|
21
|
-
def
|
22
|
-
name,
|
23
|
-
when nil then
|
24
|
-
when Operation then
|
25
|
-
when ::String, ::Symbol then
|
26
|
-
else
|
21
|
+
def add_step name_or_object, args = {}, &block
|
22
|
+
name, step = case name_or_object
|
23
|
+
when nil then build_anonymous_step block
|
24
|
+
when Operation then build_embedded_operation_step name_or_object
|
25
|
+
when ::String, ::Symbol then build_inline_step name_or_object, block
|
26
|
+
else build_object_step name_or_object, args
|
27
27
|
end
|
28
|
-
|
29
|
-
|
28
|
+
step.provisions << name.to_sym if step.provisions.empty?
|
29
|
+
set_step name.to_sym, step
|
30
30
|
end
|
31
31
|
|
32
|
-
def
|
33
|
-
if @
|
34
|
-
raise ArgumentError, "There are duplicate
|
32
|
+
def set_step name, step
|
33
|
+
if @steps.has_key? name
|
34
|
+
raise ArgumentError, "There are duplicate steps named #{name.inspect}"
|
35
35
|
end
|
36
|
-
@
|
37
|
-
|
36
|
+
@steps[name] = step
|
37
|
+
step.freeze
|
38
38
|
end
|
39
39
|
|
40
|
-
def
|
41
|
-
|
42
|
-
unless
|
43
|
-
raise ArgumentError, "Could not infer name for
|
40
|
+
def build_anonymous_step block
|
41
|
+
step = Step::InlineStep.build &block
|
42
|
+
unless step.provisions.size == 1
|
43
|
+
raise ArgumentError, "Could not infer name for step from a provision"
|
44
44
|
end
|
45
|
-
name =
|
46
|
-
[name,
|
45
|
+
name = step.provisions.fetch 0
|
46
|
+
[name, step]
|
47
47
|
end
|
48
48
|
|
49
|
-
def
|
49
|
+
def build_embedded_operation_step operation
|
50
50
|
name = object_name operation
|
51
51
|
[name || operation.result, operation]
|
52
52
|
end
|
53
53
|
|
54
|
-
def
|
55
|
-
|
56
|
-
[name,
|
54
|
+
def build_inline_step name, block
|
55
|
+
step = Step::InlineStep.build &block
|
56
|
+
[name, step]
|
57
57
|
end
|
58
58
|
|
59
|
-
def
|
59
|
+
def build_object_step object, args
|
60
60
|
name = object_name object
|
61
|
-
|
62
|
-
[name,
|
61
|
+
step = ObjectAdapter.build_step object, args
|
62
|
+
[name, step]
|
63
63
|
end
|
64
64
|
|
65
65
|
private
|
@@ -75,14 +75,14 @@ module Orchestra
|
|
75
75
|
context.instance_eval &block
|
76
76
|
end
|
77
77
|
|
78
|
-
attr :
|
78
|
+
attr :steps
|
79
79
|
|
80
80
|
def initialize builder
|
81
81
|
@builder = builder
|
82
82
|
end
|
83
83
|
|
84
|
-
def
|
85
|
-
@builder.
|
84
|
+
def step *args, &block
|
85
|
+
@builder.add_step *args, &block
|
86
86
|
nil
|
87
87
|
end
|
88
88
|
|
@@ -92,13 +92,13 @@ module Orchestra
|
|
92
92
|
end
|
93
93
|
|
94
94
|
def result name = nil, &block
|
95
|
-
|
96
|
-
name ||=
|
95
|
+
step = @builder.add_step name, &block
|
96
|
+
name ||= step.provisions.fetch 0
|
97
97
|
self.result = name
|
98
98
|
end
|
99
99
|
|
100
100
|
def finally name = :__finally__, &block
|
101
|
-
@builder.
|
101
|
+
@builder.add_step name, &block
|
102
102
|
@builder.command = true
|
103
103
|
self.result = name
|
104
104
|
end
|
@@ -1,8 +1,8 @@
|
|
1
1
|
module Orchestra
|
2
2
|
module DSL
|
3
|
-
module
|
3
|
+
module Steps
|
4
4
|
class Builder
|
5
|
-
attr_accessor :collection, :
|
5
|
+
attr_accessor :collection, :execute_block
|
6
6
|
|
7
7
|
attr :defaults, :dependencies, :provisions
|
8
8
|
|
@@ -12,12 +12,12 @@ module Orchestra
|
|
12
12
|
@provisions = []
|
13
13
|
end
|
14
14
|
|
15
|
-
def
|
16
|
-
|
15
|
+
def build_step
|
16
|
+
Step::InlineStep.new(
|
17
17
|
:collection => collection,
|
18
18
|
:defaults => defaults,
|
19
19
|
:dependencies => dependencies,
|
20
|
-
:
|
20
|
+
:execute_block => execute_block,
|
21
21
|
:provides => provisions,
|
22
22
|
)
|
23
23
|
end
|
@@ -29,8 +29,6 @@ module Orchestra
|
|
29
29
|
context.instance_eval &block
|
30
30
|
end
|
31
31
|
|
32
|
-
attr :collection, :perform
|
33
|
-
|
34
32
|
def initialize builder
|
35
33
|
@builder = builder
|
36
34
|
end
|
@@ -58,8 +56,8 @@ module Orchestra
|
|
58
56
|
@builder.provisions.concat provisions
|
59
57
|
end
|
60
58
|
|
61
|
-
def
|
62
|
-
@builder.
|
59
|
+
def execute &block
|
60
|
+
@builder.execute_block = block
|
63
61
|
end
|
64
62
|
|
65
63
|
def iterates_over dependency
|
@@ -0,0 +1,158 @@
|
|
1
|
+
module Orchestra
|
2
|
+
module Execution
|
3
|
+
extend self
|
4
|
+
|
5
|
+
def start_operation *args
|
6
|
+
Operation.new *args
|
7
|
+
end
|
8
|
+
|
9
|
+
def execute_step step, input
|
10
|
+
operation_execution = Operation.new Conductor.new, {}, input
|
11
|
+
Step.execute step, 'anonymous', operation_execution
|
12
|
+
end
|
13
|
+
|
14
|
+
class Operation
|
15
|
+
include Observable
|
16
|
+
extend Forwardable
|
17
|
+
|
18
|
+
def_delegators :@run_list, :provisions, :dependencies,
|
19
|
+
:optional_dependencies, :required_dependencies
|
20
|
+
|
21
|
+
attr :conductor, :input, :state, :registry, :run_list
|
22
|
+
|
23
|
+
def initialize conductor, run_list, input
|
24
|
+
@conductor = conductor
|
25
|
+
@input = input.dup
|
26
|
+
@run_list = run_list
|
27
|
+
@registry = conductor.build_registry self
|
28
|
+
@state = registry.merge input
|
29
|
+
end
|
30
|
+
|
31
|
+
def execute
|
32
|
+
ensure_inputs_are_present!
|
33
|
+
run_list.each do |name, step| process name, step end
|
34
|
+
rescue => error
|
35
|
+
publish :error_raised, error
|
36
|
+
raise error
|
37
|
+
end
|
38
|
+
|
39
|
+
def process name, step
|
40
|
+
output = Step.execute step, name, self
|
41
|
+
state.merge! output
|
42
|
+
end
|
43
|
+
|
44
|
+
def ensure_inputs_are_present!
|
45
|
+
has_dep = state.method :[]
|
46
|
+
missing_input = required_dependencies.reject &has_dep
|
47
|
+
raise MissingInputError.new missing_input unless missing_input.empty?
|
48
|
+
end
|
49
|
+
|
50
|
+
def extract_result result
|
51
|
+
state.fetch result
|
52
|
+
end
|
53
|
+
|
54
|
+
def publish event, *payload
|
55
|
+
changed
|
56
|
+
notify_observers event, *payload
|
57
|
+
end
|
58
|
+
|
59
|
+
def thread_pool
|
60
|
+
conductor.thread_pool
|
61
|
+
end
|
62
|
+
end
|
63
|
+
|
64
|
+
class Step
|
65
|
+
def self.execute step, *args
|
66
|
+
instance = new step, *args
|
67
|
+
instance.execute
|
68
|
+
end
|
69
|
+
|
70
|
+
def self.new step, *args
|
71
|
+
if step.is_a? Orchestra::Operation
|
72
|
+
klass = EmbeddedOperation
|
73
|
+
else
|
74
|
+
klass = step.collection ? CollectionStep : self
|
75
|
+
end
|
76
|
+
instance = klass.allocate
|
77
|
+
instance.send :initialize, step, *args
|
78
|
+
instance
|
79
|
+
end
|
80
|
+
|
81
|
+
attr :context, :name, :operation_execution, :step
|
82
|
+
|
83
|
+
def initialize step, name, operation_execution
|
84
|
+
@name = name
|
85
|
+
@operation_execution = operation_execution
|
86
|
+
@step = step
|
87
|
+
@context = build_context
|
88
|
+
end
|
89
|
+
|
90
|
+
def execute
|
91
|
+
operation_execution.publish :step_entered, name, input
|
92
|
+
output = step.process invoke
|
93
|
+
operation_execution.publish :step_exited, name, output
|
94
|
+
output
|
95
|
+
end
|
96
|
+
|
97
|
+
def input
|
98
|
+
registry = operation_execution.registry
|
99
|
+
operation_execution.state.reject do |key, val|
|
100
|
+
registry[key] == val or not step.dependencies.include? key
|
101
|
+
end
|
102
|
+
end
|
103
|
+
|
104
|
+
def invoke
|
105
|
+
context.execute
|
106
|
+
end
|
107
|
+
|
108
|
+
def build_context
|
109
|
+
step.build_context operation_execution.state
|
110
|
+
end
|
111
|
+
end
|
112
|
+
|
113
|
+
class CollectionStep < Step
|
114
|
+
def invoke
|
115
|
+
batch, output = prepare_collection
|
116
|
+
jobs = enqueue_jobs batch do |result, index| output[index] = result end
|
117
|
+
jobs.each &:wait
|
118
|
+
output
|
119
|
+
end
|
120
|
+
|
121
|
+
def enqueue_jobs batch, &block
|
122
|
+
batch.map.with_index do |element, index|
|
123
|
+
enqueue_job element, index, &block
|
124
|
+
end
|
125
|
+
end
|
126
|
+
|
127
|
+
def enqueue_job element, index
|
128
|
+
operation_execution.thread_pool.enqueue do
|
129
|
+
result = context.execute element
|
130
|
+
yield [result, index]
|
131
|
+
end
|
132
|
+
end
|
133
|
+
|
134
|
+
def prepare_collection
|
135
|
+
batch = context.fetch_collection
|
136
|
+
output = [nil] * batch.size
|
137
|
+
[batch, output]
|
138
|
+
end
|
139
|
+
end
|
140
|
+
|
141
|
+
class EmbeddedOperation < Step
|
142
|
+
def invoke
|
143
|
+
super
|
144
|
+
context.state.select do |k,_| k == step.result end
|
145
|
+
end
|
146
|
+
|
147
|
+
def build_context
|
148
|
+
conductor = operation_execution.registry[:conductor]
|
149
|
+
copy_observers = conductor.method :copy_observers
|
150
|
+
step.start_execution conductor, input, ©_observers
|
151
|
+
end
|
152
|
+
|
153
|
+
def input
|
154
|
+
operation_execution.state
|
155
|
+
end
|
156
|
+
end
|
157
|
+
end
|
158
|
+
end
|
data/lib/orchestra/operation.rb
CHANGED
@@ -12,35 +12,35 @@ module Orchestra
|
|
12
12
|
|
13
13
|
extend Forwardable
|
14
14
|
|
15
|
-
def_delegators :@default_run_list, :
|
15
|
+
def_delegators :@default_run_list, :provisions, :dependencies,
|
16
16
|
:optional_dependencies, :required_dependencies
|
17
17
|
|
18
|
-
attr :registry, :result, :
|
18
|
+
attr :registry, :result, :steps
|
19
19
|
|
20
20
|
def initialize args = {}
|
21
|
-
@result, @command, @
|
22
|
-
:result, :command => false, :
|
23
|
-
@default_run_list = RunList.build
|
21
|
+
@result, @command, @steps = Util.extract_key_args args,
|
22
|
+
:result, :command => false, :steps => {}
|
23
|
+
@default_run_list = RunList.build steps, result, []
|
24
24
|
end
|
25
25
|
|
26
26
|
def process output
|
27
27
|
output.select do |key, _| key = result end
|
28
28
|
end
|
29
29
|
|
30
|
-
def
|
30
|
+
def start_execution *args
|
31
31
|
conductor, input = extract_args args
|
32
|
-
run_list = RunList.build
|
33
|
-
|
34
|
-
yield
|
35
|
-
|
36
|
-
|
32
|
+
run_list = RunList.build steps, result, input.keys
|
33
|
+
execution = Execution.start_operation conductor, run_list, input
|
34
|
+
yield execution if block_given?
|
35
|
+
execution.publish :operation_entered, name, input
|
36
|
+
execution
|
37
37
|
end
|
38
38
|
|
39
|
-
def
|
40
|
-
|
41
|
-
|
42
|
-
output =
|
43
|
-
|
39
|
+
def execute *args, &block
|
40
|
+
execution = start_execution *args, &block
|
41
|
+
execution.execute
|
42
|
+
output = execution.extract_result result
|
43
|
+
execution.publish :operation_exited, name, output
|
44
44
|
@command ? nil : output
|
45
45
|
end
|
46
46
|
|
@@ -0,0 +1,47 @@
|
|
1
|
+
module Orchestra
|
2
|
+
class Recording
|
3
|
+
class Playback < BasicObject
|
4
|
+
attr :mocks
|
5
|
+
|
6
|
+
def initialize mocks
|
7
|
+
@mocks = mocks
|
8
|
+
end
|
9
|
+
|
10
|
+
def respond_to? meth
|
11
|
+
mocks.has_key? meth
|
12
|
+
end
|
13
|
+
|
14
|
+
def self.build service_recording
|
15
|
+
factory = Factory.new
|
16
|
+
factory.build service_recording
|
17
|
+
end
|
18
|
+
|
19
|
+
class Factory
|
20
|
+
attr :klass, :mocks
|
21
|
+
|
22
|
+
def initialize
|
23
|
+
@klass = Class.new Playback
|
24
|
+
@mocks = Hash.new do |hsh, meth| hsh[meth] = {} end
|
25
|
+
end
|
26
|
+
|
27
|
+
def build service_recording
|
28
|
+
record = method :<<
|
29
|
+
service_recording.each &record
|
30
|
+
klass.new mocks
|
31
|
+
end
|
32
|
+
|
33
|
+
def << record
|
34
|
+
method = record[:method].to_sym
|
35
|
+
unless klass.instance_methods.include? method
|
36
|
+
klass.send :define_method, method do |*args| mocks[method][args] end
|
37
|
+
end
|
38
|
+
mocks[method][record[:input]] = record[:output]
|
39
|
+
end
|
40
|
+
|
41
|
+
def singleton
|
42
|
+
singleton = class << instance ; self end
|
43
|
+
end
|
44
|
+
end
|
45
|
+
end
|
46
|
+
end
|
47
|
+
end
|
data/lib/orchestra/recording.rb
CHANGED
@@ -27,57 +27,17 @@ module Orchestra
|
|
27
27
|
}
|
28
28
|
end
|
29
29
|
|
30
|
+
def to_json generator
|
31
|
+
generator.generate to_h
|
32
|
+
end
|
33
|
+
|
30
34
|
def self.replay operation, input, service_recordings
|
31
35
|
replayed_services = {}
|
32
36
|
service_recordings.each do |svc, service_recording|
|
33
37
|
replayed_services[svc] = Playback.build service_recording
|
34
38
|
end
|
35
39
|
conductor = Conductor.new replayed_services
|
36
|
-
conductor.
|
37
|
-
end
|
38
|
-
|
39
|
-
class Playback < BasicObject
|
40
|
-
attr :mocks
|
41
|
-
|
42
|
-
def initialize mocks
|
43
|
-
@mocks = mocks
|
44
|
-
end
|
45
|
-
|
46
|
-
def respond_to? meth
|
47
|
-
mocks.has_key? meth
|
48
|
-
end
|
49
|
-
|
50
|
-
def self.build service_recording
|
51
|
-
factory = Factory.new
|
52
|
-
factory.build service_recording
|
53
|
-
end
|
54
|
-
|
55
|
-
class Factory
|
56
|
-
attr :klass, :mocks
|
57
|
-
|
58
|
-
def initialize
|
59
|
-
@klass = Class.new Playback
|
60
|
-
@mocks = Hash.new do |hsh, meth| hsh[meth] = {} end
|
61
|
-
end
|
62
|
-
|
63
|
-
def build service_recording
|
64
|
-
record = method :<<
|
65
|
-
service_recording.each &record
|
66
|
-
klass.new mocks
|
67
|
-
end
|
68
|
-
|
69
|
-
def << record
|
70
|
-
method = record[:method].to_sym
|
71
|
-
unless klass.instance_methods.include? method
|
72
|
-
klass.send :define_method, method do |*args| mocks[method][args] end
|
73
|
-
end
|
74
|
-
mocks[method][record[:input]] = record[:output]
|
75
|
-
end
|
76
|
-
|
77
|
-
def singleton
|
78
|
-
singleton = class << instance ; self end
|
79
|
-
end
|
80
|
-
end
|
40
|
+
conductor.execute operation, input
|
81
41
|
end
|
82
42
|
end
|
83
43
|
end
|