evt-process_host 0.4.0.1

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.
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: 923259786e0921de59f0cac14e21764530396d08
4
+ data.tar.gz: 641f3df3fe348e387d6b00ef7b15e547d4314079
5
+ SHA512:
6
+ metadata.gz: eaa183daea5d2adc9e1e8001e136a71c16efda922ebd3b7dd16392419db21de7272804ec7e6b7694d1d4adf201dc63b72fe76c5dc1ec780dfcd0160ce3f60c61
7
+ data.tar.gz: 7d246d999ebc88db94fac3afdd38685ee4cfada4f07110803d9a1ec030c50921e99e4a288bda7f8c3ffc87fc5158be4b8ade0dd1ef3253b4db2b3b2a563721c6
@@ -0,0 +1,16 @@
1
+ require 'actor'
2
+ require 'async_invocation'
3
+ require 'casing'
4
+ require 'log'
5
+ require 'virtual'
6
+
7
+ require 'process_host/log'
8
+ require 'process_host/signal'
9
+
10
+ require 'process_host/host'
11
+ require 'process_host/process'
12
+ require 'process_host/process/process_name'
13
+ require 'process_host/supervisor_observers/log'
14
+ require 'process_host/supervisor_observers/record_errors'
15
+
16
+ require 'process_host/process_host'
@@ -0,0 +1,7 @@
1
+ require 'process_host/controls/component_name'
2
+ require 'process_host/controls/error'
3
+ require 'process_host/controls/process'
4
+ require 'process_host/controls/process/actor_crashes'
5
+ require 'process_host/controls/process/raises_error'
6
+ require 'process_host/controls/process/runs_continuously'
7
+ require 'process_host/controls/process/stops_immediately'
@@ -0,0 +1,9 @@
1
+ module ProcessHost
2
+ module Controls
3
+ module ComponentName
4
+ def self.example
5
+ 'some-component'
6
+ end
7
+ end
8
+ end
9
+ end
@@ -0,0 +1,11 @@
1
+ module ProcessHost
2
+ module Controls
3
+ module Error
4
+ def self.example
5
+ Example.new
6
+ end
7
+
8
+ Example = Class.new StandardError
9
+ end
10
+ end
11
+ end
@@ -0,0 +1,33 @@
1
+ module ProcessHost
2
+ module Controls
3
+ module Process
4
+ def self.example
5
+ Example.new
6
+ end
7
+
8
+ module Name
9
+ def self.example
10
+ :example_process
11
+ end
12
+ end
13
+
14
+ class Example
15
+ include ProcessHost::Process
16
+
17
+ process_name Name.example
18
+
19
+ attr_accessor :started
20
+
21
+ def start
22
+ self.started = true
23
+ end
24
+
25
+ module Assertions
26
+ def started?
27
+ started ? true : false
28
+ end
29
+ end
30
+ end
31
+ end
32
+ end
33
+ end
@@ -0,0 +1,29 @@
1
+ module ProcessHost
2
+ module Controls
3
+ module Process
4
+ class ActorCrashes
5
+ include ProcessHost::Process
6
+
7
+ def start
8
+ Actor.start
9
+ end
10
+
11
+ def self.error
12
+ @error ||= Error.example
13
+ end
14
+
15
+ class Actor
16
+ include ::Actor
17
+
18
+ handle :start do
19
+ raise error
20
+ end
21
+
22
+ def error
23
+ ActorCrashes.error
24
+ end
25
+ end
26
+ end
27
+ end
28
+ end
29
+ end
@@ -0,0 +1,21 @@
1
+ module ProcessHost
2
+ module Controls
3
+ module Process
4
+ class RaisesError
5
+ include ProcessHost::Process
6
+
7
+ def start
8
+ raise error
9
+ end
10
+
11
+ def error
12
+ self.class.error
13
+ end
14
+
15
+ def self.error
16
+ @error ||= Error.example
17
+ end
18
+ end
19
+ end
20
+ end
21
+ end
@@ -0,0 +1,42 @@
1
+ module ProcessHost
2
+ module Controls
3
+ module Process
4
+ class RunsContinuously
5
+ include ProcessHost::Process
6
+
7
+ def start
8
+ Actor.start
9
+ end
10
+
11
+ class Actor
12
+ include ::Actor
13
+ include ::Log::Dependency
14
+
15
+ attr_writer :counter
16
+
17
+ handle :start do
18
+ :print_heartbeat
19
+ end
20
+
21
+ handle :print_heartbeat do
22
+ logger.info "Heartbeat (Counter: #{counter})"
23
+
24
+ :next
25
+ end
26
+
27
+ handle :next do
28
+ self.counter += 1
29
+
30
+ sleep 1
31
+
32
+ :print_heartbeat
33
+ end
34
+
35
+ def counter
36
+ @counter ||= 0
37
+ end
38
+ end
39
+ end
40
+ end
41
+ end
42
+ end
@@ -0,0 +1,21 @@
1
+ module ProcessHost
2
+ module Controls
3
+ module Process
4
+ class StopsImmediately
5
+ include ProcessHost::Process
6
+
7
+ def start
8
+ Actor.start
9
+ end
10
+
11
+ class Actor
12
+ include ::Actor
13
+
14
+ handle :start do
15
+ :stop
16
+ end
17
+ end
18
+ end
19
+ end
20
+ end
21
+ end
@@ -0,0 +1,119 @@
1
+ module ProcessHost
2
+ class Host
3
+ include ::Log::Dependency
4
+
5
+ dependency :signal, Signal
6
+ dependency :send, Actor::Messaging::Send
7
+
8
+ def self.build
9
+ instance = new
10
+ Signal.configure instance
11
+ instance.send = Actor::Messaging::Send
12
+ instance
13
+ end
14
+
15
+ def register(process_class, process_name=nil)
16
+ logger.trace { "Registering process (ProcessClass: #{process_class}, Name: #{process_name.inspect})" }
17
+
18
+ process_name ||= process_class.process_name
19
+
20
+ if registered_process = processes[process_name]
21
+ error_message = "Process with specified name is already registered (ProcessClass: #{process_class}, Name: #{process_name.inspect}, RegisteredProcessClass: #{registered_process.name})"
22
+
23
+ logger.error error_message
24
+
25
+ raise NameConflictError, error_message
26
+ else
27
+ processes[process_name] = process_class
28
+ end
29
+
30
+ logger.debug { "Process registered (ProcessClass: #{process_class}, Name: #{process_name.inspect})" }
31
+
32
+ process_name
33
+ end
34
+
35
+ def record_error(&block)
36
+ record_errors_observer.record_error_proc = block
37
+ end
38
+
39
+ def start(&block)
40
+ started_processes = []
41
+
42
+ Actor::Supervisor.start do |supervisor|
43
+ supervisor.add_observer record_errors_observer
44
+
45
+ supervisor.add_observer log_observer
46
+
47
+ signal.trap 'TSTP' do
48
+ message = Actor::Messages::Suspend
49
+
50
+ send.(message, supervisor.address)
51
+
52
+ logger.info { "Handled TSTP signal (MessageName: #{message.message_name}, SupervisorAddress: #{supervisor.address.id})" }
53
+ end
54
+
55
+ signal.trap 'CONT' do
56
+ message = Actor::Messages::Resume
57
+
58
+ send.(message, supervisor.address)
59
+
60
+ logger.info { "Handled CONT signal (MessageName: #{message.message_name}, SupervisorAddress: #{supervisor.address.id})" }
61
+ end
62
+
63
+ signal.trap 'INT' do
64
+ message = Actor::Messages::Shutdown
65
+
66
+ send.(message, supervisor.address)
67
+
68
+ logger.info { "Handled INT signal (MessageName: #{message.message_name}, SupervisorAddress: #{supervisor.address.id})" }
69
+ end
70
+
71
+ start_processes do |process|
72
+ started_processes << process
73
+ end
74
+
75
+ block.(supervisor) if block
76
+ end
77
+
78
+ started_processes
79
+ end
80
+
81
+ def start_processes(&block)
82
+ processes.each_value do |process_class|
83
+ process = process_class.build
84
+
85
+ process.start
86
+
87
+ block.(process) if block
88
+ end
89
+
90
+ rescue => error
91
+ record_errors_observer.(error)
92
+ raise error
93
+ end
94
+
95
+ def record_errors_observer
96
+ @record_errors_observer ||= SupervisorObservers::RecordErrors.new
97
+ end
98
+
99
+ def log_observer
100
+ @log_observer ||= SupervisorObservers::Log.new
101
+ end
102
+
103
+ def processes
104
+ @processes ||= {}
105
+ end
106
+
107
+ NameConflictError = Class.new StandardError
108
+
109
+ module Assertions
110
+ def registered?(&block)
111
+ return processes.any? if block.nil?
112
+
113
+ processes.any? do |name, process_class|
114
+ block.(process_class, name)
115
+ end
116
+ end
117
+ end
118
+ end
119
+ end
@@ -0,0 +1,8 @@
1
+ module ProcessHost
2
+ class Log < Log
3
+ def tag!(tags)
4
+ tags << :process_host
5
+ tags << :verbose
6
+ end
7
+ end
8
+ end
@@ -0,0 +1,37 @@
1
+ module ProcessHost
2
+ module Process
3
+ def self.included(cls)
4
+ cls.class_exec do
5
+ include Log::Dependency
6
+
7
+ extend Build
8
+ extend ProcessName
9
+ prepend Start
10
+
11
+ dependency :send, Actor::Messaging::Send
12
+ end
13
+ end
14
+
15
+ Virtual::PureMethod.define self, :start
16
+
17
+ module Start
18
+ def start
19
+ logger.trace { "Starting process (ProcessName: #{self.class.process_name})" }
20
+
21
+ super
22
+
23
+ logger.debug { "Process started (ProcessName: #{self.class.process_name})" }
24
+
25
+ AsyncInvocation::Incorrect
26
+ end
27
+ end
28
+
29
+ module Build
30
+ def build
31
+ instance = new
32
+ instance.send = Actor::Messaging::Send.new
33
+ instance
34
+ end
35
+ end
36
+ end
37
+ end
@@ -0,0 +1,41 @@
1
+ module ProcessHost
2
+ module Process
3
+ module ProcessName
4
+ def self.extended(cls)
5
+ cls.singleton_class.class_exec do
6
+ attr_writer :process_name
7
+ end
8
+ end
9
+
10
+ def process_name_macro(name)
11
+ self.process_name = name
12
+ end
13
+
14
+ def process_name(name=nil)
15
+ if name.nil?
16
+ @process_name ||= Default.get self
17
+ else
18
+ process_name_macro name
19
+ end
20
+ end
21
+
22
+ module Default
23
+ def self.get(mod)
24
+ constant_name = mod.name
25
+
26
+ return unknown if constant_name.nil?
27
+
28
+ *, constant_name = constant_name.split '::'
29
+
30
+ constant_name = Casing::Underscore.(constant_name)
31
+
32
+ constant_name.to_sym
33
+ end
34
+
35
+ def self.unknown
36
+ :unknown
37
+ end
38
+ end
39
+ end
40
+ end
41
+ end
@@ -0,0 +1,13 @@
1
+ module ProcessHost
2
+ def self.start(component_name, &block)
3
+ logger = ::Log.get self
4
+
5
+ host = Host.build
6
+
7
+ host.instance_exec host, &block
8
+
9
+ host.start do
10
+ logger.info "Started component: #{component_name} (ProcessID: #{::Process.pid})"
11
+ end
12
+ end
13
+ end
@@ -0,0 +1,53 @@
1
+ module ProcessHost
2
+ module Signal
3
+ def self.configure(receiver, attr_name: nil)
4
+ attr_name ||= :signal
5
+
6
+ receiver.public_send "#{attr_name}=", ::Signal
7
+ end
8
+
9
+ module Substitute
10
+ def self.build
11
+ Signal.new
12
+ end
13
+
14
+ class Signal
15
+ def trap(signal, &handler)
16
+ handlers[signal] = handler
17
+ end
18
+
19
+ def simulate_signal(signal)
20
+ handler = handlers[signal]
21
+
22
+ return if handler.nil?
23
+
24
+ handler.()
25
+
26
+ record = Record.new signal
27
+ records << record
28
+ record
29
+ end
30
+
31
+ def handlers
32
+ @handlers ||= {}
33
+ end
34
+
35
+ def records
36
+ @records ||= []
37
+ end
38
+
39
+ Record = Struct.new :signal
40
+
41
+ module Assertions
42
+ def trapped?(signal=nil)
43
+ if signal.nil?
44
+ records.any?
45
+ else
46
+ records.any? { |record| record.signal == signal }
47
+ end
48
+ end
49
+ end
50
+ end
51
+ end
52
+ end
53
+ end
@@ -0,0 +1,22 @@
1
+ module ProcessHost
2
+ module SupervisorObservers
3
+ class Log
4
+ include Actor::Supervisor::Observer
5
+ include ProcessHost::Log::Dependency
6
+
7
+ handle Actor::Messages::ActorStarted do |msg|
8
+ logger.debug "Actor started (Address: #{msg.address.id}, Actor: #{msg.actor.digest})"
9
+ end
10
+
11
+ handle Actor::Messages::ActorStopped do |msg|
12
+ logger.debug "Actor stopped (Address: #{msg.address.id}, Actor: #{msg.actor.digest})"
13
+ end
14
+
15
+ handle Actor::Messages::ActorCrashed do |msg|
16
+ error = msg.error
17
+
18
+ logger.error "Error raised (ErrorClass: #{error.class.name}, Actor: #{msg.actor.digest}, Message: #{error.message.inspect})"
19
+ end
20
+ end
21
+ end
22
+ end
@@ -0,0 +1,23 @@
1
+ module ProcessHost
2
+ module SupervisorObservers
3
+ class RecordErrors
4
+ include Actor::Supervisor::Observer
5
+
6
+ attr_writer :record_error_proc
7
+
8
+ handle Actor::Messages::ActorCrashed do |msg|
9
+ error = msg.error
10
+
11
+ self.(error)
12
+ end
13
+
14
+ def call error
15
+ record_error_proc.(error)
16
+ end
17
+
18
+ def record_error_proc
19
+ @record_error_proc ||= proc { }
20
+ end
21
+ end
22
+ end
23
+ end
metadata ADDED
@@ -0,0 +1,144 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: evt-process_host
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.4.0.1
5
+ platform: ruby
6
+ authors:
7
+ - The Eventide Project
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+ date: 2016-12-22 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: evt-async_invocation
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - ">="
18
+ - !ruby/object:Gem::Version
19
+ version: '0'
20
+ type: :runtime
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - ">="
25
+ - !ruby/object:Gem::Version
26
+ version: '0'
27
+ - !ruby/object:Gem::Dependency
28
+ name: evt-casing
29
+ requirement: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - ">="
32
+ - !ruby/object:Gem::Version
33
+ version: '0'
34
+ type: :runtime
35
+ prerelease: false
36
+ version_requirements: !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - ">="
39
+ - !ruby/object:Gem::Version
40
+ version: '0'
41
+ - !ruby/object:Gem::Dependency
42
+ name: evt-log
43
+ requirement: !ruby/object:Gem::Requirement
44
+ requirements:
45
+ - - ">="
46
+ - !ruby/object:Gem::Version
47
+ version: '0'
48
+ type: :runtime
49
+ prerelease: false
50
+ version_requirements: !ruby/object:Gem::Requirement
51
+ requirements:
52
+ - - ">="
53
+ - !ruby/object:Gem::Version
54
+ version: '0'
55
+ - !ruby/object:Gem::Dependency
56
+ name: evt-virtual
57
+ requirement: !ruby/object:Gem::Requirement
58
+ requirements:
59
+ - - ">="
60
+ - !ruby/object:Gem::Version
61
+ version: '0'
62
+ type: :runtime
63
+ prerelease: false
64
+ version_requirements: !ruby/object:Gem::Requirement
65
+ requirements:
66
+ - - ">="
67
+ - !ruby/object:Gem::Version
68
+ version: '0'
69
+ - !ruby/object:Gem::Dependency
70
+ name: ntl-actor
71
+ requirement: !ruby/object:Gem::Requirement
72
+ requirements:
73
+ - - ">="
74
+ - !ruby/object:Gem::Version
75
+ version: '0'
76
+ type: :runtime
77
+ prerelease: false
78
+ version_requirements: !ruby/object:Gem::Requirement
79
+ requirements:
80
+ - - ">="
81
+ - !ruby/object:Gem::Version
82
+ version: '0'
83
+ - !ruby/object:Gem::Dependency
84
+ name: test_bench
85
+ requirement: !ruby/object:Gem::Requirement
86
+ requirements:
87
+ - - ">="
88
+ - !ruby/object:Gem::Version
89
+ version: '0'
90
+ type: :development
91
+ prerelease: false
92
+ version_requirements: !ruby/object:Gem::Requirement
93
+ requirements:
94
+ - - ">="
95
+ - !ruby/object:Gem::Version
96
+ version: '0'
97
+ description: " "
98
+ email: opensource@eventide-project.org
99
+ executables: []
100
+ extensions: []
101
+ extra_rdoc_files: []
102
+ files:
103
+ - lib/process_host.rb
104
+ - lib/process_host/controls.rb
105
+ - lib/process_host/controls/component_name.rb
106
+ - lib/process_host/controls/error.rb
107
+ - lib/process_host/controls/process.rb
108
+ - lib/process_host/controls/process/actor_crashes.rb
109
+ - lib/process_host/controls/process/raises_error.rb
110
+ - lib/process_host/controls/process/runs_continuously.rb
111
+ - lib/process_host/controls/process/stops_immediately.rb
112
+ - lib/process_host/host.rb
113
+ - lib/process_host/log.rb
114
+ - lib/process_host/process.rb
115
+ - lib/process_host/process/process_name.rb
116
+ - lib/process_host/process_host.rb
117
+ - lib/process_host/signal.rb
118
+ - lib/process_host/supervisor_observers/log.rb
119
+ - lib/process_host/supervisor_observers/record_errors.rb
120
+ homepage: https://github.com/eventide-project/process-host
121
+ licenses:
122
+ - MIT
123
+ metadata: {}
124
+ post_install_message:
125
+ rdoc_options: []
126
+ require_paths:
127
+ - lib
128
+ required_ruby_version: !ruby/object:Gem::Requirement
129
+ requirements:
130
+ - - ">="
131
+ - !ruby/object:Gem::Version
132
+ version: 2.3.3
133
+ required_rubygems_version: !ruby/object:Gem::Requirement
134
+ requirements:
135
+ - - ">="
136
+ - !ruby/object:Gem::Version
137
+ version: '0'
138
+ requirements: []
139
+ rubyforge_project:
140
+ rubygems_version: 2.6.8
141
+ signing_key:
142
+ specification_version: 4
143
+ summary: Run multiple logical processes inside a single physical process
144
+ test_files: []