ntl-orchestra 0.9.2 → 0.9.3

Sign up to get free protection for your applications and to get access to all the features.
@@ -5,61 +5,61 @@ module Orchestra
5
5
  attr_writer :command, :result
6
6
 
7
7
  def initialize
8
- @nodes = {}
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 node" if @nodes.empty?
13
+ raise ArgumentError, "Must supply at least one step" if @steps.empty?
14
14
  Operation.new(
15
15
  :command => @command,
16
- :nodes => @nodes,
16
+ :steps => @steps,
17
17
  :result => @result,
18
18
  )
19
19
  end
20
20
 
21
- def add_node name_or_object, args = {}, &block
22
- name, node = case name_or_object
23
- when nil then build_anonymous_node block
24
- when Operation then build_embedded_operation_node name_or_object
25
- when ::String, ::Symbol then build_inline_node name_or_object, block
26
- else build_object_node name_or_object, args
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
- node.provisions << name.to_sym if node.provisions.empty?
29
- set_node name.to_sym, node
28
+ step.provisions << name.to_sym if step.provisions.empty?
29
+ set_step name.to_sym, step
30
30
  end
31
31
 
32
- def set_node name, node
33
- if @nodes.has_key? name
34
- raise ArgumentError, "There are duplicate nodes named #{name.inspect}"
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
- @nodes[name] = node
37
- node.freeze
36
+ @steps[name] = step
37
+ step.freeze
38
38
  end
39
39
 
40
- def build_anonymous_node block
41
- node = Node::InlineNode.build &block
42
- unless node.provisions.size == 1
43
- raise ArgumentError, "Could not infer name for node from a provision"
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 = node.provisions.fetch 0
46
- [name, node]
45
+ name = step.provisions.fetch 0
46
+ [name, step]
47
47
  end
48
48
 
49
- def build_embedded_operation_node operation
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 build_inline_node name, block
55
- node = Node::InlineNode.build &block
56
- [name, node]
54
+ def build_inline_step name, block
55
+ step = Step::InlineStep.build &block
56
+ [name, step]
57
57
  end
58
58
 
59
- def build_object_node object, args
59
+ def build_object_step object, args
60
60
  name = object_name object
61
- node = ObjectAdapter.build_node object, args
62
- [name, node]
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 :nodes
78
+ attr :steps
79
79
 
80
80
  def initialize builder
81
81
  @builder = builder
82
82
  end
83
83
 
84
- def node *args, &block
85
- @builder.add_node *args, &block
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
- node = @builder.add_node name, &block
96
- name ||= node.provisions.fetch 0
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.add_node name, &block
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 Nodes
3
+ module Steps
4
4
  class Builder
5
- attr_accessor :collection, :perform_block
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 build_node
16
- Node::InlineNode.new(
15
+ def build_step
16
+ Step::InlineStep.new(
17
17
  :collection => collection,
18
18
  :defaults => defaults,
19
19
  :dependencies => dependencies,
20
- :perform_block => perform_block,
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 perform &block
62
- @builder.perform_block = block
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, &copy_observers
151
+ end
152
+
153
+ def input
154
+ operation_execution.state
155
+ end
156
+ end
157
+ end
158
+ end
@@ -12,35 +12,35 @@ module Orchestra
12
12
 
13
13
  extend Forwardable
14
14
 
15
- def_delegators :@default_run_list, :node_names, :provisions, :dependencies,
15
+ def_delegators :@default_run_list, :provisions, :dependencies,
16
16
  :optional_dependencies, :required_dependencies
17
17
 
18
- attr :registry, :result, :nodes
18
+ attr :registry, :result, :steps
19
19
 
20
20
  def initialize args = {}
21
- @result, @command, @nodes = Util.extract_key_args args,
22
- :result, :command => false, :nodes => {}
23
- @default_run_list = RunList.build nodes, result, []
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 start_performance *args
30
+ def start_execution *args
31
31
  conductor, input = extract_args args
32
- run_list = RunList.build nodes, result, input.keys
33
- performance = Performance.new conductor, run_list, input
34
- yield performance if block_given?
35
- performance.publish :operation_entered, name, input
36
- performance
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 perform *args, &block
40
- performance = start_performance *args, &block
41
- performance.perform
42
- output = performance.extract_result result
43
- performance.publish :operation_exited, name, output
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
@@ -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.perform operation, input
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