dynflow 0.2.0 → 0.3.0
Sign up to get free protection for your applications and to get access to all the features.
- data/.travis.yml +1 -0
- data/lib/dynflow.rb +1 -0
- data/lib/dynflow/action.rb +5 -3
- data/lib/dynflow/action/finalize_phase.rb +3 -1
- data/lib/dynflow/action/plan_phase.rb +4 -2
- data/lib/dynflow/action/run_phase.rb +3 -1
- data/lib/dynflow/daemon.rb +1 -0
- data/lib/dynflow/execution_plan.rb +6 -4
- data/lib/dynflow/executors/abstract.rb +12 -0
- data/lib/dynflow/executors/parallel.rb +16 -35
- data/lib/dynflow/executors/parallel/core.rb +13 -8
- data/lib/dynflow/executors/parallel/execution_plan_manager.rb +21 -29
- data/lib/dynflow/executors/parallel/flow_manager.rb +0 -3
- data/lib/dynflow/executors/parallel/running_steps_manager.rb +5 -3
- data/lib/dynflow/executors/parallel/sequential_manager.rb +6 -2
- data/lib/dynflow/executors/parallel/worker.rb +5 -4
- data/lib/dynflow/executors/remote_via_socket.rb +7 -2
- data/lib/dynflow/executors/remote_via_socket/core.rb +66 -32
- data/lib/dynflow/future.rb +1 -1
- data/lib/dynflow/listeners/abstract.rb +4 -0
- data/lib/dynflow/listeners/serialization.rb +42 -8
- data/lib/dynflow/listeners/socket.rb +49 -19
- data/lib/dynflow/middleware.rb +46 -0
- data/lib/dynflow/middleware/action.rb +9 -0
- data/lib/dynflow/middleware/register.rb +32 -0
- data/lib/dynflow/middleware/resolver.rb +63 -0
- data/lib/dynflow/middleware/stack.rb +29 -0
- data/lib/dynflow/middleware/world.rb +58 -0
- data/lib/dynflow/simple_world.rb +1 -0
- data/lib/dynflow/testing/dummy_world.rb +3 -1
- data/lib/dynflow/version.rb +1 -1
- data/lib/dynflow/web_console.rb +7 -2
- data/lib/dynflow/world.rb +29 -9
- data/test/action_test.rb +5 -6
- data/test/execution_plan_test.rb +10 -11
- data/test/executor_test.rb +152 -89
- data/test/middleware_test.rb +109 -0
- data/test/remote_via_socket_test.rb +166 -0
- data/test/{code_workflow_example.rb → support/code_workflow_example.rb} +39 -30
- data/test/support/middleware_example.rb +132 -0
- data/test/test_helper.rb +18 -16
- data/test/testing_test.rb +4 -3
- data/test/web_console_test.rb +1 -2
- metadata +16 -4
data/.travis.yml
CHANGED
data/lib/dynflow.rb
CHANGED
data/lib/dynflow/action.rb
CHANGED
@@ -10,6 +10,8 @@ module Dynflow
|
|
10
10
|
require 'dynflow/action/format'
|
11
11
|
extend Format
|
12
12
|
|
13
|
+
extend Middleware::Action
|
14
|
+
|
13
15
|
require 'dynflow/action/progress'
|
14
16
|
include Progress
|
15
17
|
|
@@ -188,19 +190,19 @@ module Dynflow
|
|
188
190
|
|
189
191
|
private
|
190
192
|
|
191
|
-
|
193
|
+
ERROR = Object.new
|
192
194
|
|
193
195
|
# DSL to terminate action execution and set it to error
|
194
196
|
def error!(error)
|
195
197
|
set_error(error)
|
196
|
-
throw
|
198
|
+
throw ERROR
|
197
199
|
end
|
198
200
|
|
199
201
|
def with_error_handling(&block)
|
200
202
|
raise "wrong state #{self.state}" unless self.state == :running
|
201
203
|
|
202
204
|
begin
|
203
|
-
catch(
|
205
|
+
catch(ERROR) { block.call }
|
204
206
|
rescue Exception => error
|
205
207
|
set_error(error)
|
206
208
|
# reraise low-level exceptions
|
@@ -20,8 +20,10 @@ module Dynflow
|
|
20
20
|
self.state = :running
|
21
21
|
save_state
|
22
22
|
with_error_handling do
|
23
|
-
|
24
|
-
plan
|
23
|
+
concurrence do
|
24
|
+
world.middleware.execute(:plan, self, *args) do |*new_args|
|
25
|
+
plan(*new_args)
|
26
|
+
end
|
25
27
|
end
|
26
28
|
|
27
29
|
subscribed_actions = world.subscribed_actions(self.action_class)
|
@@ -22,7 +22,9 @@ module Dynflow
|
|
22
22
|
self.state = :running
|
23
23
|
save_state
|
24
24
|
with_error_handling do
|
25
|
-
result = catch(SUSPEND)
|
25
|
+
result = catch(SUSPEND) do
|
26
|
+
world.middleware.execute(:run, self, *Array(event)) { |*args| run(*args) }
|
27
|
+
end
|
26
28
|
if result == SUSPEND
|
27
29
|
self.state = :suspended
|
28
30
|
end
|
data/lib/dynflow/daemon.rb
CHANGED
@@ -114,11 +114,13 @@ module Dynflow
|
|
114
114
|
def plan(*args)
|
115
115
|
update_state(:planning)
|
116
116
|
world.transaction_adapter.transaction do
|
117
|
-
|
118
|
-
|
117
|
+
world.middleware.execute(:plan_phase, root_plan_step.action_class) do
|
118
|
+
with_planning_scope do
|
119
|
+
root_plan_step.execute(self, nil, *args)
|
119
120
|
|
120
|
-
|
121
|
-
|
121
|
+
if @dependency_graph.unresolved?
|
122
|
+
raise "Some dependencies were not resolved: #{@dependency_graph.inspect}"
|
123
|
+
end
|
122
124
|
end
|
123
125
|
end
|
124
126
|
|
@@ -1,6 +1,18 @@
|
|
1
1
|
module Dynflow
|
2
2
|
module Executors
|
3
3
|
class Abstract
|
4
|
+
Event = Algebrick.type do
|
5
|
+
fields! execution_plan_id: String,
|
6
|
+
step_id: Fixnum,
|
7
|
+
event: Object,
|
8
|
+
result: Future
|
9
|
+
end
|
10
|
+
|
11
|
+
Execution = Algebrick.type do
|
12
|
+
fields! execution_plan_id: String,
|
13
|
+
finished: Future
|
14
|
+
end
|
15
|
+
|
4
16
|
include Algebrick::TypeCheck
|
5
17
|
attr_reader :world, :logger
|
6
18
|
|
@@ -14,50 +14,31 @@ module Dynflow
|
|
14
14
|
|
15
15
|
UnprocessableEvent = Class.new(Dynflow::Error)
|
16
16
|
|
17
|
-
|
18
|
-
|
19
|
-
Boolean = type { variants TrueClass, FalseClass }
|
17
|
+
Algebrick.type do |work|
|
18
|
+
Work = work
|
20
19
|
|
21
|
-
|
22
|
-
fields!
|
23
|
-
|
20
|
+
Work::Finalize = type do
|
21
|
+
fields! sequential_manager: SequentialManager,
|
22
|
+
execution_plan_id: String
|
24
23
|
end
|
25
24
|
|
26
|
-
|
27
|
-
fields!
|
28
|
-
|
29
|
-
event: Object,
|
30
|
-
result: Future
|
25
|
+
Work::Step = type do
|
26
|
+
fields! step: ExecutionPlan::Steps::AbstractFlowStep,
|
27
|
+
execution_plan_id: String
|
31
28
|
end
|
32
29
|
|
33
|
-
Work = type do
|
34
|
-
|
35
|
-
|
36
|
-
|
37
|
-
end
|
38
|
-
|
39
|
-
work::Step = type do
|
40
|
-
fields! step: ExecutionPlan::Steps::AbstractFlowStep,
|
41
|
-
execution_plan_id: String
|
42
|
-
end
|
43
|
-
|
44
|
-
work::Event = type do
|
45
|
-
fields! step: ExecutionPlan::Steps::AbstractFlowStep,
|
46
|
-
execution_plan_id: String,
|
47
|
-
event: Event
|
48
|
-
end
|
49
|
-
|
50
|
-
variants work::Step, work::Event, work::Finalize
|
30
|
+
Work::Event = type do
|
31
|
+
fields! step: ExecutionPlan::Steps::AbstractFlowStep,
|
32
|
+
execution_plan_id: String,
|
33
|
+
event: Event
|
51
34
|
end
|
52
35
|
|
53
|
-
|
54
|
-
fields! work: Work
|
55
|
-
end
|
56
|
-
WorkerDone = type do
|
57
|
-
fields! work: Work, worker: Worker
|
58
|
-
end
|
36
|
+
variants Work::Step, Work::Event, Work::Finalize
|
59
37
|
end
|
60
38
|
|
39
|
+
PoolDone = Algebrick.type { fields! work: Work }
|
40
|
+
WorkerDone = Algebrick.type { fields! work: Work, worker: Worker }
|
41
|
+
|
61
42
|
def initialize(world, pool_size = 10)
|
62
43
|
super(world)
|
63
44
|
@core = Core.new world, pool_size
|
@@ -18,11 +18,11 @@ module Dynflow
|
|
18
18
|
|
19
19
|
def on_message(message)
|
20
20
|
match message,
|
21
|
-
(on ~Execution do |(execution_plan_id, finished)|
|
21
|
+
(on ~Parallel::Execution do |(execution_plan_id, finished)|
|
22
22
|
start_executing track_execution_plan(execution_plan_id, finished)
|
23
23
|
true
|
24
24
|
end),
|
25
|
-
(on ~Event do |event|
|
25
|
+
(on ~Parallel::Event do |event|
|
26
26
|
event(event)
|
27
27
|
end),
|
28
28
|
(on PoolDone.(~any) do |step|
|
@@ -40,15 +40,18 @@ module Dynflow
|
|
40
40
|
execution_plan = @world.persistence.load_execution_plan(execution_plan_id)
|
41
41
|
|
42
42
|
if terminating?
|
43
|
-
raise Dynflow::Error,
|
43
|
+
raise Dynflow::Error,
|
44
|
+
"cannot accept execution_plan_id:#{execution_plan_id} core is terminating"
|
44
45
|
end
|
45
46
|
|
46
47
|
if @execution_plan_managers[execution_plan_id]
|
47
|
-
raise Dynflow::Error,
|
48
|
+
raise Dynflow::Error,
|
49
|
+
"cannot execute execution_plan_id:#{execution_plan_id} it's already running"
|
48
50
|
end
|
49
51
|
|
50
52
|
if execution_plan.state == :stopped
|
51
|
-
raise Dynflow::Error,
|
53
|
+
raise Dynflow::Error,
|
54
|
+
"cannot execute execution_plan_id:#{execution_plan_id} it's stopped"
|
52
55
|
end
|
53
56
|
|
54
57
|
@execution_plan_managers[execution_plan_id] =
|
@@ -95,14 +98,16 @@ module Dynflow
|
|
95
98
|
end
|
96
99
|
|
97
100
|
def event(event)
|
98
|
-
Type! event, Event
|
101
|
+
Type! event, Parallel::Event
|
99
102
|
execution_plan_manager = @execution_plan_managers[event.execution_plan_id]
|
100
103
|
if execution_plan_manager
|
101
104
|
feed_pool execution_plan_manager.event(event)
|
102
105
|
true
|
103
106
|
else
|
104
|
-
logger.warn
|
105
|
-
|
107
|
+
logger.warn format('dropping event %s - no manager for %s:%s',
|
108
|
+
event, event.execution_plan_id, event.step_id)
|
109
|
+
event.result.fail UnprocessableEvent.new(
|
110
|
+
"no manager for #{event.execution_plan_id}:#{event.step_id}")
|
106
111
|
end
|
107
112
|
end
|
108
113
|
|
@@ -46,38 +46,30 @@ module Dynflow
|
|
46
46
|
end
|
47
47
|
end
|
48
48
|
|
49
|
-
match
|
50
|
-
|
51
|
-
|
52
|
-
|
53
|
-
|
54
|
-
|
55
|
-
|
56
|
-
|
57
|
-
|
58
|
-
|
59
|
-
|
60
|
-
|
61
|
-
|
62
|
-
|
63
|
-
|
64
|
-
|
65
|
-
|
66
|
-
|
67
|
-
|
68
|
-
|
69
|
-
compute_next_from_step.call step
|
70
|
-
end
|
71
|
-
end,
|
72
|
-
|
73
|
-
Work::Finalize >-> do
|
74
|
-
raise unless @finalize_manager
|
75
|
-
finish
|
76
|
-
end
|
49
|
+
match(work,
|
50
|
+
(on Work::Step.(step: ~any) | Work::Event.(step: ~any) do |step|
|
51
|
+
execution_plan.steps[step.id] = step
|
52
|
+
suspended, work = @running_steps_manager.done(step)
|
53
|
+
unless suspended
|
54
|
+
execution_plan.update_execution_time step.execution_time
|
55
|
+
work = compute_next_from_step.call step
|
56
|
+
end
|
57
|
+
# TODO: can be probably disabled to improve
|
58
|
+
# performance, execution time will not be updated,
|
59
|
+
# maybe more - check on the other side, it allows
|
60
|
+
# us to use persistence adapter for hooking into
|
61
|
+
# the running process.
|
62
|
+
execution_plan.save
|
63
|
+
work
|
64
|
+
end),
|
65
|
+
(on Work::Finalize do
|
66
|
+
raise unless @finalize_manager
|
67
|
+
finish
|
68
|
+
end))
|
77
69
|
end
|
78
70
|
|
79
71
|
def event(event)
|
80
|
-
Type! event, Event
|
72
|
+
Type! event, Parallel::Event
|
81
73
|
raise unless event.execution_plan_id == @execution_plan.id
|
82
74
|
@running_steps_manager.event(event)
|
83
75
|
end
|
@@ -19,9 +19,6 @@ module Dynflow
|
|
19
19
|
|
20
20
|
# @return [Set] of steps to continue with
|
21
21
|
def what_is_next(flow_step)
|
22
|
-
execution_plan.steps[flow_step.id] = flow_step
|
23
|
-
# TODO can be probably disabled to improve performance, execution time will not be updated, maybe more - check
|
24
|
-
execution_plan.save
|
25
22
|
return [] if flow_step.state == :suspended
|
26
23
|
|
27
24
|
success = flow_step.state != :error
|
@@ -34,7 +34,8 @@ module Dynflow
|
|
34
34
|
while (event = @events.shift(step.id))
|
35
35
|
message = "step #{step.execution_plan_id}:#{step.id} dropping event #{event.event}"
|
36
36
|
@world.logger.warn message
|
37
|
-
event.event.result.fail UnprocessableEvent.new(message).
|
37
|
+
event.event.result.fail UnprocessableEvent.new(message).
|
38
|
+
tap { |e| e.set_backtrace(caller) }
|
38
39
|
end
|
39
40
|
raise 'assert' unless @events.empty?(step.id)
|
40
41
|
@running_steps.delete(step.id)
|
@@ -44,11 +45,12 @@ module Dynflow
|
|
44
45
|
|
45
46
|
# @returns [Work, nil]
|
46
47
|
def event(event)
|
47
|
-
Type! event, Event
|
48
|
+
Type! event, Parallel::Event
|
48
49
|
|
49
50
|
step = @running_steps[event.step_id]
|
50
51
|
unless step
|
51
|
-
event.result.fail UnprocessableEvent.new(
|
52
|
+
event.result.fail UnprocessableEvent.new(
|
53
|
+
'step is not suspended, it cannot process events')
|
52
54
|
return nil
|
53
55
|
end
|
54
56
|
|
@@ -22,8 +22,12 @@ module Dynflow
|
|
22
22
|
reset_finalize_steps
|
23
23
|
unless execution_plan.error?
|
24
24
|
world.transaction_adapter.transaction do
|
25
|
-
|
26
|
-
|
25
|
+
step_id = execution_plan.finalize_flow.all_step_ids.first
|
26
|
+
action_class = execution_plan.steps[step_id].action_class
|
27
|
+
world.middleware.execute(:finalize_phase, action_class) do
|
28
|
+
unless dispatch(execution_plan.finalize_flow)
|
29
|
+
world.transaction_adapter.rollback
|
30
|
+
end
|
27
31
|
end
|
28
32
|
end
|
29
33
|
end
|
@@ -15,12 +15,13 @@ module Dynflow
|
|
15
15
|
|
16
16
|
def on_message(message)
|
17
17
|
match message,
|
18
|
-
Work::Step.(step: ~any) |
|
18
|
+
(on Work::Step.(step: ~any) |
|
19
|
+
Work::Event.(step: ~any, event: Parallel::Event.(event: ~any)) do |step, event|
|
19
20
|
step.execute event
|
20
|
-
end,
|
21
|
-
Work::Finalize.(~any, any)
|
21
|
+
end),
|
22
|
+
(on Work::Finalize.(~any, any) do |sequential_manager|
|
22
23
|
sequential_manager.finalize
|
23
|
-
end
|
24
|
+
end)
|
24
25
|
@pool << WorkerDone[work: message, worker: self]
|
25
26
|
@transaction_adapter.cleanup
|
26
27
|
end
|
@@ -15,7 +15,7 @@ module Dynflow
|
|
15
15
|
end
|
16
16
|
|
17
17
|
def execute(execution_plan_id, finished = Future.new)
|
18
|
-
@core.ask(Core::
|
18
|
+
@core.ask(Core::Execution[execution_plan_id, finished]).value!.value!
|
19
19
|
finished
|
20
20
|
rescue => e
|
21
21
|
finished.fail e unless finished.ready?
|
@@ -23,7 +23,8 @@ module Dynflow
|
|
23
23
|
end
|
24
24
|
|
25
25
|
def event(execution_plan_id, step_id, event, future = Future)
|
26
|
-
|
26
|
+
@core.ask(Core::Event[execution_plan_id, step_id, event, future]).value!
|
27
|
+
future
|
27
28
|
end
|
28
29
|
|
29
30
|
def terminate(future = Future.new)
|
@@ -33,6 +34,10 @@ module Dynflow
|
|
33
34
|
def initialized
|
34
35
|
@core.initialized
|
35
36
|
end
|
37
|
+
|
38
|
+
def connected?
|
39
|
+
@core.ask(Core::Connect).value!
|
40
|
+
end
|
36
41
|
end
|
37
42
|
end
|
38
43
|
end
|
@@ -5,16 +5,22 @@ module Dynflow
|
|
5
5
|
include Listeners::Serialization
|
6
6
|
|
7
7
|
Message = Algebrick.type do
|
8
|
+
Job = Algebrick.type do
|
9
|
+
variants Event = Executors::Abstract::Event,
|
10
|
+
Execution = Executors::Abstract::Execution
|
11
|
+
end
|
12
|
+
|
8
13
|
variants Closed = atom,
|
9
|
-
Received = type { fields message:
|
10
|
-
|
14
|
+
Received = type { fields message: Protocol::Response },
|
15
|
+
Connect = atom,
|
16
|
+
Job
|
11
17
|
end
|
12
18
|
|
13
|
-
|
14
|
-
fields! id: Integer, accepted: Future, finished: Future
|
19
|
+
TrackedJob = Algebrick.type do
|
20
|
+
fields! id: Integer, job: Protocol::Job, accepted: Future, finished: Future
|
15
21
|
end
|
16
22
|
|
17
|
-
module
|
23
|
+
module TrackedJob
|
18
24
|
def accept!
|
19
25
|
accepted.resolve true
|
20
26
|
self
|
@@ -26,9 +32,16 @@ module Dynflow
|
|
26
32
|
self
|
27
33
|
end
|
28
34
|
|
29
|
-
def success!(
|
35
|
+
def success!(world)
|
30
36
|
raise unless accepted.ready?
|
31
|
-
finished.resolve
|
37
|
+
finished.resolve(
|
38
|
+
match job,
|
39
|
+
(on Core::Protocol::Execution.(execution_plan_id: ~any) do |uuid|
|
40
|
+
world.persistence.load_execution_plan(uuid)
|
41
|
+
end),
|
42
|
+
(on Core::Protocol::Event do
|
43
|
+
true
|
44
|
+
end))
|
32
45
|
self
|
33
46
|
end
|
34
47
|
|
@@ -49,26 +62,34 @@ module Dynflow
|
|
49
62
|
private
|
50
63
|
|
51
64
|
def delayed_initialize(world, socket_path)
|
52
|
-
@socket_path
|
53
|
-
@world
|
54
|
-
@socket
|
55
|
-
@last_id
|
56
|
-
@
|
65
|
+
@socket_path = Type! socket_path, String
|
66
|
+
@world = Type! world, World
|
67
|
+
@socket = nil
|
68
|
+
@last_id = 0
|
69
|
+
@tracked_jobs = {}
|
57
70
|
connect
|
58
71
|
end
|
59
72
|
|
60
73
|
def termination
|
61
|
-
disconnect
|
74
|
+
terminate! if disconnect
|
62
75
|
end
|
63
76
|
|
64
77
|
def on_message(message)
|
78
|
+
Type! message, Message
|
65
79
|
match message,
|
66
|
-
|
67
|
-
(on Core::Execute.(~any, ~any) do |execution_plan_uuid, future|
|
80
|
+
(on ~Job do |job|
|
68
81
|
raise 'terminating' if terminating?
|
69
|
-
|
82
|
+
job, future =
|
83
|
+
match job,
|
84
|
+
(on ~Execution do |(execution_plan_uuid, future)|
|
85
|
+
[Protocol::Execution[execution_plan_uuid], future]
|
86
|
+
end),
|
87
|
+
(on ~Event do |(execution_plan_id, step_id, event, future)|
|
88
|
+
[Protocol::Event[execution_plan_id, step_id, event], future]
|
89
|
+
end)
|
90
|
+
id, accepted = add_tracked_job future, job
|
70
91
|
success = connect && begin
|
71
|
-
send_message @socket,
|
92
|
+
send_message @socket, Protocol::Do[id, job]
|
72
93
|
true
|
73
94
|
rescue IOError => error
|
74
95
|
logger.warn error
|
@@ -76,36 +97,43 @@ module Dynflow
|
|
76
97
|
end
|
77
98
|
|
78
99
|
unless success
|
79
|
-
@
|
80
|
-
|
100
|
+
@tracked_jobs[id].reject!(
|
101
|
+
Dynflow::Error.new(
|
102
|
+
"Cannot do #{message}, no connection to a Listener"))
|
81
103
|
end
|
82
104
|
|
83
105
|
return accepted
|
84
106
|
end),
|
85
107
|
|
86
|
-
(on Received.(Accepted
|
87
|
-
@
|
108
|
+
(on Received.(~Protocol::Accepted) do |(id)|
|
109
|
+
@tracked_jobs[id].accept!
|
88
110
|
end),
|
89
111
|
|
90
|
-
(on Received.(Failed
|
91
|
-
@
|
112
|
+
(on Received.(~Protocol::Failed) do |(id, error)|
|
113
|
+
@tracked_jobs.delete(id).reject! Dynflow::Error.new(error)
|
92
114
|
end),
|
93
115
|
|
94
|
-
(on Received.(Done
|
95
|
-
@
|
116
|
+
(on Received.(~Protocol::Done) do |(id)|
|
117
|
+
@tracked_jobs.delete(id).success! @world
|
96
118
|
end),
|
97
119
|
|
98
120
|
(on Closed do
|
99
121
|
@socket = nil
|
100
122
|
logger.info 'Disconnected from server.'
|
101
|
-
@
|
102
|
-
|
123
|
+
@tracked_jobs.each do |_, c|
|
124
|
+
c.fail! 'Connection to a Listener lost.'
|
125
|
+
end
|
126
|
+
@tracked_jobs.clear
|
103
127
|
terminate! if terminating?
|
128
|
+
end),
|
129
|
+
|
130
|
+
(on Connect do
|
131
|
+
connect
|
104
132
|
end)
|
105
133
|
end
|
106
134
|
|
107
|
-
def
|
108
|
-
@
|
135
|
+
def add_tracked_job(finished, job)
|
136
|
+
@tracked_jobs[id = (@last_id += 1)] = TrackedJob[id, job, accepted = Future.new, finished]
|
109
137
|
return id, accepted
|
110
138
|
end
|
111
139
|
|
@@ -115,14 +143,20 @@ module Dynflow
|
|
115
143
|
logger.info 'Connected to server.'
|
116
144
|
read_socket_until_closed
|
117
145
|
true
|
118
|
-
rescue IOError => error
|
146
|
+
rescue SystemCallError, IOError => error
|
119
147
|
logger.warn error
|
120
148
|
false
|
149
|
+
rescue => error
|
150
|
+
logger.fatal error
|
151
|
+
raise error
|
121
152
|
end
|
122
153
|
|
123
154
|
def disconnect
|
124
155
|
return true unless @socket
|
125
|
-
|
156
|
+
|
157
|
+
@socket.close
|
158
|
+
false
|
159
|
+
rescue Errno::ENOTCONN
|
126
160
|
true
|
127
161
|
end
|
128
162
|
|
@@ -136,7 +170,7 @@ module Dynflow
|
|
136
170
|
|
137
171
|
def read_socket
|
138
172
|
match message = receive_message(@socket),
|
139
|
-
|
173
|
+
Protocol::Message >-> { self << Received[message] },
|
140
174
|
NilClass.to_m >-> do
|
141
175
|
self << Closed
|
142
176
|
throw :stop_reading
|