dynflow 0.1.0 → 0.2.0
Sign up to get free protection for your applications and to get access to all the features.
- data/.gitignore +6 -0
- data/.travis.yml +9 -0
- data/Gemfile +0 -10
- data/MIT-LICENSE +1 -1
- data/README.md +99 -37
- data/Rakefile +2 -6
- data/doc/images/logo.png +0 -0
- data/dynflow.gemspec +10 -1
- data/examples/generate_work_for_daemon.rb +24 -0
- data/examples/orchestrate.rb +121 -0
- data/examples/run_daemon.rb +17 -0
- data/examples/web_console.rb +29 -0
- data/lib/dynflow.rb +27 -6
- data/lib/dynflow/action.rb +185 -77
- data/lib/dynflow/action/cancellable_polling.rb +18 -0
- data/lib/dynflow/action/finalize_phase.rb +18 -0
- data/lib/dynflow/action/flow_phase.rb +44 -0
- data/lib/dynflow/action/format.rb +46 -0
- data/lib/dynflow/action/missing.rb +26 -0
- data/lib/dynflow/action/plan_phase.rb +85 -0
- data/lib/dynflow/action/polling.rb +49 -0
- data/lib/dynflow/action/presenter.rb +51 -0
- data/lib/dynflow/action/progress.rb +62 -0
- data/lib/dynflow/action/run_phase.rb +43 -0
- data/lib/dynflow/action/suspended.rb +21 -0
- data/lib/dynflow/clock.rb +133 -0
- data/lib/dynflow/daemon.rb +29 -0
- data/lib/dynflow/execution_plan.rb +285 -33
- data/lib/dynflow/execution_plan/dependency_graph.rb +29 -0
- data/lib/dynflow/execution_plan/output_reference.rb +52 -0
- data/lib/dynflow/execution_plan/steps.rb +12 -0
- data/lib/dynflow/execution_plan/steps/abstract.rb +121 -0
- data/lib/dynflow/execution_plan/steps/abstract_flow_step.rb +52 -0
- data/lib/dynflow/execution_plan/steps/error.rb +33 -0
- data/lib/dynflow/execution_plan/steps/finalize_step.rb +23 -0
- data/lib/dynflow/execution_plan/steps/plan_step.rb +81 -0
- data/lib/dynflow/execution_plan/steps/run_step.rb +21 -0
- data/lib/dynflow/executors.rb +9 -0
- data/lib/dynflow/executors/abstract.rb +32 -0
- data/lib/dynflow/executors/parallel.rb +88 -0
- data/lib/dynflow/executors/parallel/core.rb +119 -0
- data/lib/dynflow/executors/parallel/execution_plan_manager.rb +120 -0
- data/lib/dynflow/executors/parallel/flow_manager.rb +48 -0
- data/lib/dynflow/executors/parallel/pool.rb +102 -0
- data/lib/dynflow/executors/parallel/running_steps_manager.rb +63 -0
- data/lib/dynflow/executors/parallel/sequence_cursor.rb +97 -0
- data/lib/dynflow/executors/parallel/sequential_manager.rb +81 -0
- data/lib/dynflow/executors/parallel/work_queue.rb +44 -0
- data/lib/dynflow/executors/parallel/worker.rb +30 -0
- data/lib/dynflow/executors/remote_via_socket.rb +38 -0
- data/lib/dynflow/executors/remote_via_socket/core.rb +150 -0
- data/lib/dynflow/flows.rb +13 -0
- data/lib/dynflow/flows/abstract.rb +36 -0
- data/lib/dynflow/flows/abstract_composed.rb +104 -0
- data/lib/dynflow/flows/atom.rb +36 -0
- data/lib/dynflow/flows/concurrence.rb +28 -0
- data/lib/dynflow/flows/sequence.rb +13 -0
- data/lib/dynflow/future.rb +173 -0
- data/lib/dynflow/listeners.rb +7 -0
- data/lib/dynflow/listeners/abstract.rb +13 -0
- data/lib/dynflow/listeners/serialization.rb +41 -0
- data/lib/dynflow/listeners/socket.rb +88 -0
- data/lib/dynflow/logger_adapters.rb +8 -0
- data/lib/dynflow/logger_adapters/abstract.rb +30 -0
- data/lib/dynflow/logger_adapters/delegator.rb +13 -0
- data/lib/dynflow/logger_adapters/formatters.rb +8 -0
- data/lib/dynflow/logger_adapters/formatters/abstract.rb +33 -0
- data/lib/dynflow/logger_adapters/formatters/exception.rb +15 -0
- data/lib/dynflow/logger_adapters/simple.rb +59 -0
- data/lib/dynflow/micro_actor.rb +102 -0
- data/lib/dynflow/persistence.rb +53 -0
- data/lib/dynflow/persistence_adapters.rb +6 -0
- data/lib/dynflow/persistence_adapters/abstract.rb +56 -0
- data/lib/dynflow/persistence_adapters/sequel.rb +160 -0
- data/lib/dynflow/persistence_adapters/sequel_migrations/001_initial.rb +52 -0
- data/lib/dynflow/serializable.rb +66 -0
- data/lib/dynflow/simple_world.rb +18 -0
- data/lib/dynflow/stateful.rb +40 -0
- data/lib/dynflow/testing.rb +32 -0
- data/lib/dynflow/testing/assertions.rb +64 -0
- data/lib/dynflow/testing/dummy_execution_plan.rb +40 -0
- data/lib/dynflow/testing/dummy_executor.rb +29 -0
- data/lib/dynflow/testing/dummy_planned_action.rb +18 -0
- data/lib/dynflow/testing/dummy_step.rb +19 -0
- data/lib/dynflow/testing/dummy_world.rb +33 -0
- data/lib/dynflow/testing/factories.rb +83 -0
- data/lib/dynflow/testing/managed_clock.rb +23 -0
- data/lib/dynflow/testing/mimic.rb +38 -0
- data/lib/dynflow/transaction_adapters.rb +9 -0
- data/lib/dynflow/transaction_adapters/abstract.rb +26 -0
- data/lib/dynflow/transaction_adapters/active_record.rb +27 -0
- data/lib/dynflow/transaction_adapters/none.rb +12 -0
- data/lib/dynflow/version.rb +1 -1
- data/lib/dynflow/web_console.rb +277 -0
- data/lib/dynflow/world.rb +168 -0
- data/test/action_test.rb +89 -11
- data/test/clock_test.rb +59 -0
- data/test/code_workflow_example.rb +382 -0
- data/test/execution_plan_test.rb +195 -64
- data/test/executor_test.rb +692 -0
- data/test/persistance_adapters_test.rb +173 -0
- data/test/test_helper.rb +316 -1
- data/test/testing_test.rb +148 -0
- data/test/web_console_test.rb +38 -0
- data/web/assets/javascripts/application.js +25 -0
- data/web/assets/stylesheets/application.css +101 -0
- data/web/assets/vendor/bootstrap/css/bootstrap-responsive.css +1109 -0
- data/web/assets/vendor/bootstrap/css/bootstrap-responsive.min.css +9 -0
- data/web/assets/vendor/bootstrap/css/bootstrap.css +6167 -0
- data/web/assets/vendor/bootstrap/css/bootstrap.min.css +9 -0
- data/web/assets/vendor/bootstrap/img/glyphicons-halflings-white.png +0 -0
- data/web/assets/vendor/bootstrap/img/glyphicons-halflings.png +0 -0
- data/web/assets/vendor/bootstrap/js/bootstrap.js +2280 -0
- data/web/assets/vendor/bootstrap/js/bootstrap.min.js +6 -0
- data/web/assets/vendor/google-code-prettify/lang-basic.js +3 -0
- data/web/assets/vendor/google-code-prettify/prettify.css +1 -0
- data/web/assets/vendor/google-code-prettify/prettify.js +30 -0
- data/web/assets/vendor/google-code-prettify/run_prettify.js +34 -0
- data/web/assets/vendor/jquery/jquery.js +9807 -0
- data/web/views/flow.erb +19 -0
- data/web/views/flow_step.erb +31 -0
- data/web/views/index.erb +39 -0
- data/web/views/layout.erb +20 -0
- data/web/views/plan_step.erb +11 -0
- data/web/views/show.erb +54 -0
- metadata +250 -11
- data/examples/events.rb +0 -71
- data/examples/workflow.rb +0 -140
- data/lib/dynflow/bus.rb +0 -168
- data/lib/dynflow/dispatcher.rb +0 -36
- data/lib/dynflow/logger.rb +0 -34
- data/lib/dynflow/step.rb +0 -234
- data/test/bus_test.rb +0 -150
@@ -0,0 +1,97 @@
|
|
1
|
+
module Dynflow
|
2
|
+
module Executors
|
3
|
+
class Parallel < Abstract
|
4
|
+
class SequenceCursor
|
5
|
+
|
6
|
+
def initialize(flow_manager, sequence, parent_cursor = nil)
|
7
|
+
@flow_manager = flow_manager
|
8
|
+
@sequence = sequence
|
9
|
+
@parent_cursor = parent_cursor
|
10
|
+
@todo = []
|
11
|
+
@index = -1 # starts before first element
|
12
|
+
@no_error_so_far = true
|
13
|
+
end
|
14
|
+
|
15
|
+
# @param [ExecutionPlan::Steps::Abstract, SequenceCursor] work
|
16
|
+
# step or sequence cursor that was done
|
17
|
+
# @param [true, false] success was the work finished successfully
|
18
|
+
# @return [Array<Integer>] new step_ids that can be done next
|
19
|
+
def what_is_next(work = nil, success = true)
|
20
|
+
unless work.nil? || @todo.delete(work)
|
21
|
+
raise "marking as done work that was not expected: #{work.inspect}"
|
22
|
+
end
|
23
|
+
|
24
|
+
@no_error_so_far &&= success
|
25
|
+
|
26
|
+
if done_here?
|
27
|
+
return next_steps
|
28
|
+
else
|
29
|
+
return []
|
30
|
+
end
|
31
|
+
end
|
32
|
+
|
33
|
+
|
34
|
+
# return true if we can't move the cursor further, either when
|
35
|
+
# everyting is done in the sequence or there was some failure
|
36
|
+
# that prevents us from moving
|
37
|
+
def done?
|
38
|
+
(!@no_error_so_far && done_here?) || @index == @sequence.size
|
39
|
+
end
|
40
|
+
|
41
|
+
protected
|
42
|
+
|
43
|
+
# steps we can do right now without waiting for anything
|
44
|
+
def steps_todo
|
45
|
+
@todo.map do |item|
|
46
|
+
case item
|
47
|
+
when SequenceCursor
|
48
|
+
item.steps_todo
|
49
|
+
else
|
50
|
+
item
|
51
|
+
end
|
52
|
+
end.flatten
|
53
|
+
end
|
54
|
+
|
55
|
+
def move
|
56
|
+
@index += 1
|
57
|
+
next_flow = @sequence.sub_flows[@index]
|
58
|
+
add_todo(next_flow)
|
59
|
+
end
|
60
|
+
|
61
|
+
private
|
62
|
+
|
63
|
+
def done_here?
|
64
|
+
@todo.empty?
|
65
|
+
end
|
66
|
+
|
67
|
+
def next_steps
|
68
|
+
move if @no_error_so_far
|
69
|
+
if done?
|
70
|
+
if @parent_cursor
|
71
|
+
return @parent_cursor.what_is_next(self, @no_error_so_far)
|
72
|
+
else
|
73
|
+
return []
|
74
|
+
end
|
75
|
+
else
|
76
|
+
return steps_todo
|
77
|
+
end
|
78
|
+
end
|
79
|
+
|
80
|
+
def add_todo(flow)
|
81
|
+
case flow
|
82
|
+
when Flows::Sequence
|
83
|
+
@todo << SequenceCursor.new(@flow_manager, flow, self).tap do |cursor|
|
84
|
+
cursor.move
|
85
|
+
end
|
86
|
+
when Flows::Concurrence
|
87
|
+
flow.sub_flows.each { |sub_flow| add_todo(sub_flow) }
|
88
|
+
when Flows::Atom
|
89
|
+
@flow_manager.cursor_index[flow.step_id] = self
|
90
|
+
@todo << @flow_manager.execution_plan.steps[flow.step_id]
|
91
|
+
end
|
92
|
+
end
|
93
|
+
|
94
|
+
end
|
95
|
+
end
|
96
|
+
end
|
97
|
+
end
|
@@ -0,0 +1,81 @@
|
|
1
|
+
module Dynflow
|
2
|
+
module Executors
|
3
|
+
class Parallel::SequentialManager
|
4
|
+
attr_reader :execution_plan, :world
|
5
|
+
|
6
|
+
def initialize(world, execution_plan)
|
7
|
+
@world = world
|
8
|
+
@execution_plan = execution_plan
|
9
|
+
@done = false
|
10
|
+
end
|
11
|
+
|
12
|
+
def run
|
13
|
+
with_state_updates do
|
14
|
+
dispatch(execution_plan.run_flow)
|
15
|
+
finalize
|
16
|
+
end
|
17
|
+
|
18
|
+
return execution_plan
|
19
|
+
end
|
20
|
+
|
21
|
+
def finalize
|
22
|
+
reset_finalize_steps
|
23
|
+
unless execution_plan.error?
|
24
|
+
world.transaction_adapter.transaction do
|
25
|
+
unless dispatch(execution_plan.finalize_flow)
|
26
|
+
world.transaction_adapter.rollback
|
27
|
+
end
|
28
|
+
end
|
29
|
+
end
|
30
|
+
@done = true
|
31
|
+
end
|
32
|
+
|
33
|
+
def reset_finalize_steps
|
34
|
+
execution_plan.finalize_flow.all_step_ids.each do |step_id|
|
35
|
+
step = execution_plan.steps[step_id]
|
36
|
+
step.state = :pending if [:success, :error].include? step.state
|
37
|
+
end
|
38
|
+
end
|
39
|
+
|
40
|
+
def done?
|
41
|
+
@done
|
42
|
+
end
|
43
|
+
|
44
|
+
private
|
45
|
+
|
46
|
+
def dispatch(flow)
|
47
|
+
case flow
|
48
|
+
when Flows::Sequence
|
49
|
+
run_in_sequence(flow.flows)
|
50
|
+
when Flows::Concurrence
|
51
|
+
run_in_concurrence(flow.flows)
|
52
|
+
when Flows::Atom
|
53
|
+
run_step(execution_plan.steps[flow.step_id])
|
54
|
+
else
|
55
|
+
raise ArgumentError, "Don't know how to run #{flow}"
|
56
|
+
end
|
57
|
+
end
|
58
|
+
|
59
|
+
def run_in_sequence(steps)
|
60
|
+
steps.all? { |s| dispatch(s) }
|
61
|
+
end
|
62
|
+
|
63
|
+
def run_in_concurrence(steps)
|
64
|
+
run_in_sequence(steps)
|
65
|
+
end
|
66
|
+
|
67
|
+
def run_step(step)
|
68
|
+
step.execute
|
69
|
+
execution_plan.update_execution_time step.execution_time
|
70
|
+
execution_plan.save
|
71
|
+
return step.state != :error
|
72
|
+
end
|
73
|
+
|
74
|
+
def with_state_updates(&block)
|
75
|
+
execution_plan.update_state(:running)
|
76
|
+
block.call
|
77
|
+
execution_plan.update_state(execution_plan.error? ? :paused : :stopped)
|
78
|
+
end
|
79
|
+
end
|
80
|
+
end
|
81
|
+
end
|
@@ -0,0 +1,44 @@
|
|
1
|
+
module Dynflow
|
2
|
+
module Executors
|
3
|
+
class Parallel < Abstract
|
4
|
+
class WorkQueue
|
5
|
+
include Algebrick::TypeCheck
|
6
|
+
|
7
|
+
def initialize(key_type = Object, work_type = Object)
|
8
|
+
@key_type = key_type
|
9
|
+
@work_type = work_type
|
10
|
+
@stash = Hash.new { |hash, key| hash[key] = [] }
|
11
|
+
end
|
12
|
+
|
13
|
+
def push(key, work)
|
14
|
+
Type! key, @key_type
|
15
|
+
Type! work, @work_type
|
16
|
+
@stash[key].push work
|
17
|
+
end
|
18
|
+
|
19
|
+
def shift(key)
|
20
|
+
return nil unless present? key
|
21
|
+
@stash[key].shift.tap { |work| @stash.delete(key) if @stash[key].empty? }
|
22
|
+
end
|
23
|
+
|
24
|
+
def present?(key)
|
25
|
+
@stash.key?(key)
|
26
|
+
end
|
27
|
+
|
28
|
+
def empty?(key)
|
29
|
+
!present?(key)
|
30
|
+
end
|
31
|
+
|
32
|
+
def size(key)
|
33
|
+
return 0 if empty?(key)
|
34
|
+
@stash[key].size
|
35
|
+
end
|
36
|
+
|
37
|
+
def first(key)
|
38
|
+
return nil if empty?(key)
|
39
|
+
@stash[key].first
|
40
|
+
end
|
41
|
+
end
|
42
|
+
end
|
43
|
+
end
|
44
|
+
end
|
@@ -0,0 +1,30 @@
|
|
1
|
+
module Dynflow
|
2
|
+
module Executors
|
3
|
+
class Parallel < Abstract
|
4
|
+
class Worker < MicroActor
|
5
|
+
def initialize(pool, transaction_adapter)
|
6
|
+
super(pool.logger, pool, transaction_adapter)
|
7
|
+
end
|
8
|
+
|
9
|
+
private
|
10
|
+
|
11
|
+
def delayed_initialize(pool, transaction_adapter)
|
12
|
+
@pool = pool
|
13
|
+
@transaction_adapter = Type! transaction_adapter, TransactionAdapters::Abstract
|
14
|
+
end
|
15
|
+
|
16
|
+
def on_message(message)
|
17
|
+
match message,
|
18
|
+
Work::Step.(step: ~any) | Work::Event.(step: ~any, event: Event.(event: ~any)) >-> step, event do
|
19
|
+
step.execute event
|
20
|
+
end,
|
21
|
+
Work::Finalize.(~any, any) >-> sequential_manager do
|
22
|
+
sequential_manager.finalize
|
23
|
+
end
|
24
|
+
@pool << WorkerDone[work: message, worker: self]
|
25
|
+
@transaction_adapter.cleanup
|
26
|
+
end
|
27
|
+
end
|
28
|
+
end
|
29
|
+
end
|
30
|
+
end
|
@@ -0,0 +1,38 @@
|
|
1
|
+
require 'multi_json'
|
2
|
+
require 'socket'
|
3
|
+
|
4
|
+
module Dynflow
|
5
|
+
module Executors
|
6
|
+
class RemoteViaSocket < Abstract
|
7
|
+
require 'dynflow/executors/remote_via_socket/core'
|
8
|
+
|
9
|
+
include Listeners::Serialization
|
10
|
+
include Algebrick::Matching
|
11
|
+
|
12
|
+
def initialize(world, socket_path)
|
13
|
+
super world
|
14
|
+
@core = Core.new world, socket_path
|
15
|
+
end
|
16
|
+
|
17
|
+
def execute(execution_plan_id, finished = Future.new)
|
18
|
+
@core.ask(Core::Execute[execution_plan_id, finished]).value!.value!
|
19
|
+
finished
|
20
|
+
rescue => e
|
21
|
+
finished.fail e unless finished.ready?
|
22
|
+
raise e
|
23
|
+
end
|
24
|
+
|
25
|
+
def event(execution_plan_id, step_id, event, future = Future)
|
26
|
+
raise 'events are handled in a process with real executor'
|
27
|
+
end
|
28
|
+
|
29
|
+
def terminate(future = Future.new)
|
30
|
+
@core.ask(MicroActor::Terminate, future)
|
31
|
+
end
|
32
|
+
|
33
|
+
def initialized
|
34
|
+
@core.initialized
|
35
|
+
end
|
36
|
+
end
|
37
|
+
end
|
38
|
+
end
|
@@ -0,0 +1,150 @@
|
|
1
|
+
module Dynflow
|
2
|
+
module Executors
|
3
|
+
class RemoteViaSocket < Abstract
|
4
|
+
class Core < MicroActor
|
5
|
+
include Listeners::Serialization
|
6
|
+
|
7
|
+
Message = Algebrick.type do
|
8
|
+
variants Closed = atom,
|
9
|
+
Received = type { fields message: SocketMessage },
|
10
|
+
Execute = type { fields execution_plan_uuid: String, future: Future }
|
11
|
+
end
|
12
|
+
|
13
|
+
Execution = Algebrick.type do
|
14
|
+
fields! id: Integer, accepted: Future, finished: Future
|
15
|
+
end
|
16
|
+
|
17
|
+
module Execution
|
18
|
+
def accept!
|
19
|
+
accepted.resolve true
|
20
|
+
self
|
21
|
+
end
|
22
|
+
|
23
|
+
def reject!(error)
|
24
|
+
accepted.fail error
|
25
|
+
finished.fail error
|
26
|
+
self
|
27
|
+
end
|
28
|
+
|
29
|
+
def success!(value)
|
30
|
+
raise unless accepted.ready?
|
31
|
+
finished.resolve value
|
32
|
+
self
|
33
|
+
end
|
34
|
+
|
35
|
+
def fail!(error)
|
36
|
+
if accepted.ready?
|
37
|
+
finished.fail error
|
38
|
+
else
|
39
|
+
reject! error
|
40
|
+
end
|
41
|
+
self
|
42
|
+
end
|
43
|
+
end
|
44
|
+
|
45
|
+
def initialize(world, socket_path)
|
46
|
+
super(world.logger, world, socket_path)
|
47
|
+
end
|
48
|
+
|
49
|
+
private
|
50
|
+
|
51
|
+
def delayed_initialize(world, socket_path)
|
52
|
+
@socket_path = Type! socket_path, String
|
53
|
+
@world = Type! world, World
|
54
|
+
@socket = nil
|
55
|
+
@last_id = 0
|
56
|
+
@executions = {}
|
57
|
+
connect
|
58
|
+
end
|
59
|
+
|
60
|
+
def termination
|
61
|
+
disconnect
|
62
|
+
end
|
63
|
+
|
64
|
+
def on_message(message)
|
65
|
+
match message,
|
66
|
+
|
67
|
+
(on Core::Execute.(~any, ~any) do |execution_plan_uuid, future|
|
68
|
+
raise 'terminating' if terminating?
|
69
|
+
id, accepted = add_execution future
|
70
|
+
success = connect && begin
|
71
|
+
send_message @socket, RemoteViaSocket::Execute[id, execution_plan_uuid]
|
72
|
+
true
|
73
|
+
rescue IOError => error
|
74
|
+
logger.warn error
|
75
|
+
false
|
76
|
+
end
|
77
|
+
|
78
|
+
unless success
|
79
|
+
@executions[id].reject! Dynflow::Error.new(
|
80
|
+
'No connection to RemoteViaSocket::Listener')
|
81
|
+
end
|
82
|
+
|
83
|
+
return accepted
|
84
|
+
end),
|
85
|
+
|
86
|
+
(on Received.(Accepted.(~any)) do |id|
|
87
|
+
@executions[id].accept!
|
88
|
+
end),
|
89
|
+
|
90
|
+
(on Received.(Failed.(~any, ~any)) do |id, error|
|
91
|
+
@executions.delete(id).reject! Dynflow::Error.new(error)
|
92
|
+
end),
|
93
|
+
|
94
|
+
(on Received.(Done.(~any, ~any)) do |id, uuid|
|
95
|
+
@executions.delete(id).success! @world.persistence.load_execution_plan(uuid)
|
96
|
+
end),
|
97
|
+
|
98
|
+
(on Closed do
|
99
|
+
@socket = nil
|
100
|
+
logger.info 'Disconnected from server.'
|
101
|
+
@executions.each { |_, c| c.fail! 'No connection to RemoteViaSocket::Listener' }
|
102
|
+
@executions.clear
|
103
|
+
terminate! if terminating?
|
104
|
+
end)
|
105
|
+
end
|
106
|
+
|
107
|
+
def add_execution(finished)
|
108
|
+
@executions[id = (@last_id += 1)] = Execution[id, accepted = Future.new, finished]
|
109
|
+
return id, accepted
|
110
|
+
end
|
111
|
+
|
112
|
+
def connect
|
113
|
+
return true if @socket
|
114
|
+
@socket = UNIXSocket.new @socket_path
|
115
|
+
logger.info 'Connected to server.'
|
116
|
+
read_socket_until_closed
|
117
|
+
true
|
118
|
+
rescue IOError => error
|
119
|
+
logger.warn error
|
120
|
+
false
|
121
|
+
end
|
122
|
+
|
123
|
+
def disconnect
|
124
|
+
return true unless @socket
|
125
|
+
@socket.shutdown :RDWR
|
126
|
+
true
|
127
|
+
end
|
128
|
+
|
129
|
+
def read_socket_until_closed
|
130
|
+
Thread.new do
|
131
|
+
catch(:stop_reading) do
|
132
|
+
loop { read_socket }
|
133
|
+
end
|
134
|
+
end
|
135
|
+
end
|
136
|
+
|
137
|
+
def read_socket
|
138
|
+
match message = receive_message(@socket),
|
139
|
+
SocketMessage >-> { self << Received[message] },
|
140
|
+
NilClass.to_m >-> do
|
141
|
+
self << Closed
|
142
|
+
throw :stop_reading
|
143
|
+
end
|
144
|
+
rescue => error
|
145
|
+
logger.fatal error
|
146
|
+
end
|
147
|
+
end
|
148
|
+
end
|
149
|
+
end
|
150
|
+
end
|