rsmp 0.8.6 → 0.9.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 437183789b08f12b23264bc2716b84fd6c2298c2abab58dfb0b64529d004ac7d
4
- data.tar.gz: 5595afbb4edb5d3e66320cae10c92c98a13fd56848095bb8ec4bb3615a097b05
3
+ metadata.gz: dde978c380def64dfcad00d0a7c9845a7c57476828623664dcf2ace7f3f6b669
4
+ data.tar.gz: 6dadc564b21089271b9921f876b95c899ad42c5bbc27b9250d34b47648447937
5
5
  SHA512:
6
- metadata.gz: f3cdc4723d2d4616cc703d18aca6432c354bdbd7e4084c443a5f17aa8c420463d567813e73dc3fb454f02461b773149970932ee1d6d84f0434d8eaf21324a0ff
7
- data.tar.gz: b90f7f2c8edc5722e0044cb31107779caabc4a9acbfce8bb713dd9a7d84e7272dd96fb2aad293c7d30e2350701758fe5300cfed776a774a754798cf8ac91037c
6
+ metadata.gz: 7bc25b5125c599a5c46db4c919e8bf6891ddad0631de6264dfe5df97eac5cdf85768f099df8f41bff26203fb6fb15d95f4b09c8e2d4b32e80b24b3065be88d1e
7
+ data.tar.gz: 8b58da0f830cc4e4fa7f0906a2d691a3d1548b364c21e320c0f597ce4da873dd16b2db0d5e196c70f21fa9126c24a4e3f4c951586768cad0f99b78047ed7eb18
@@ -0,0 +1,14 @@
1
+ # This workflow runs RSpec tests
2
+
3
+ name: RSpec
4
+ on: [push, pull_request]
5
+ jobs:
6
+ test:
7
+ runs-on: ubuntu-latest
8
+ steps:
9
+ - uses: actions/checkout@v2
10
+ - uses: ruby/setup-ruby@v1
11
+ with:
12
+ # ruby-version is not needed because we have a .ruby-version file
13
+ bundler-cache: true # runs 'bundle install' and caches installed gems automatically
14
+ - run: bundle exec rspec
data/Gemfile.lock CHANGED
@@ -1,7 +1,7 @@
1
1
  PATH
2
2
  remote: .
3
3
  specs:
4
- rsmp (0.8.6)
4
+ rsmp (0.9.0)
5
5
  async (~> 1.29.1)
6
6
  async-io (~> 1.32.2)
7
7
  colorize (~> 0.8.1)
@@ -103,6 +103,7 @@ GEM
103
103
  PLATFORMS
104
104
  x86_64-darwin-20
105
105
  x86_64-darwin-21
106
+ x86_64-linux
106
107
 
107
108
  DEPENDENCIES
108
109
  aruba (~> 2.0.0)
data/README.md CHANGED
@@ -23,7 +23,7 @@ $ git submodule update # fetch submodules
23
23
 
24
24
  Alternatively, you can pass --recurse-submodules to the git clone command, and it will automatically initialize and update each submodule in the repository.
25
25
 
26
- ## Usage
26
+ ## Usage
27
27
  ### Site and Supervisor
28
28
  The RSMP::Site and RSMP::Supervisor classes can be used to run a RSMP site.
29
29
 
@@ -172,7 +172,7 @@ Use the ```tlc``` site type to run an emulation of a traffic light controller. T
172
172
  ### CLI help and options.
173
173
  Use ```--help <command>``` to get a list of available options.
174
174
 
175
- Use ```--config <path>``` to point to a .yaml config file, controlling things like IP adresses, ports, and log output. Examples of config files can be found the folder ```config/```.
175
+ Use ```--config <path>``` to point to a .yaml config file, controlling things like IP adresses, ports, and log output. Examples of config files can be found the folder ```config/```.
176
176
 
177
177
  ## Tests
178
178
  ### RSpec
data/bin/console CHANGED
@@ -1,7 +1,7 @@
1
1
  #!/usr/bin/env ruby
2
2
 
3
3
  # Make IRB run inside Async, so async task
4
- # will run the the background.
4
+ # will run the the background.
5
5
 
6
6
  require 'bundler/setup'
7
7
  require 'irb'
@@ -14,13 +14,13 @@ SupervisorProxy SiteProxy - - include Components, SiteProxyWait
14
14
 
15
15
  ## Modules
16
16
  ### Logging
17
- Handle logging.
17
+ Handles logging.
18
18
 
19
- ### Wait
20
- Handles waiting for an async condition and block.
19
+ ### Task
20
+ Handles async tasks.
21
21
 
22
22
  ### Components
23
- Component handling.
23
+ Handles RSMP components.
24
24
 
25
25
  ## Classes
26
26
  ### Node
@@ -1,7 +1,7 @@
1
1
  # Collection
2
2
  You often need to collect messages or responses. The collector classes are used to collect message asyncronously. Other tasks continue until the collection completes, time outs or is cancelled.
3
3
 
4
- A collector can collect ingoing and/or outgoing messages.
4
+ A collector can collect ingoing and/or outgoing messages.
5
5
 
6
6
  An object that includes the Notifier module (or implements the same functionality) must be provided when you construct a Collected. The collector will attach itself to this notifier when it starts collecting, to receive messages. The SiteProxy and SupervisorProxy classes both include the Notifier module, and can therefore be used as message sources.
7
7
 
@@ -32,7 +32,7 @@ outgoing: Whether to collect outgoing messages. Defaults to true
32
32
  component: An RSMP component id.
33
33
 
34
34
  ### Collecting
35
- Use collect() to start collecting and wait for completion or timeout. The status will be returned.
35
+ Use collect() to start collecting and wait for completion or timeout. The status will be returned.
36
36
 
37
37
  ```ruby
38
38
  result = collector.collect # => :ok, :timeout or :cancelled
@@ -0,0 +1,149 @@
1
+ # Tasks
2
+
3
+ ## Concurrency
4
+ When you use a site or a supervisor, it runs asyncronously so you can run several concurrently, or do other things concurrently, like sending messages and waiting for reponses.
5
+
6
+ Concurrency is provided by Ruby Fiber Scheduler and the Async gem. and used to:
7
+
8
+ - Runn multiple sites or supervisors
9
+ - Run multiple connections from each site or supervisor
10
+ - Wait for messages, connections or states
11
+
12
+ Classes don't inherit from Async::Task. Instead they include task as instance variables. This means that the hierachy of objects and tasks can be different.
13
+
14
+ ## Task module
15
+ The `Task` module is used by the `Node` and `Proxy` classes and defines a life cycle for handling async tasks.
16
+
17
+ A single main tasks is kept in `@task`. If subclasses need subtask, they can start them as needed, inside the main task.
18
+
19
+ You first call `start`. If `@task` already exists, it will return immedatiately.
20
+ Otherwise an async task is created and stored in `@task`, and `run` is called inside this task, to handle any long-running jobs, like listening for incoming messages. By default `run` calls `start_subtasks`, but should be overriden to do actual work as well.
21
+ The call to `start` returns immediately, with the async task running concurently.
22
+
23
+ If you want to stop the task, call `stop`. It will fist call `stop_subtasks` and then `stop_task` which calls Async#stop on `@task` and sets `@task` to `nil`.
24
+
25
+ ## Stopping tasks
26
+ If a task stops itself, code after the call to `stop` will not be run, unless you use an ensure block:
27
+
28
+ ```ruby
29
+ require 'async'
30
+
31
+ Async do |task|
32
+ task.stop
33
+ puts "I just stopped" # this will not be reaced, because the task was stopped
34
+ end
35
+
36
+ Async do |task|
37
+ task.stop
38
+ ensure
39
+ puts "I just stopped" # this will be reached
40
+ end
41
+ ```
42
+ This can happen if either the task stops itself directly, or if it stops task higher up in the task hierarchy.
43
+
44
+ For example, when a timer task finds that an acknowledgement was not received in time, and then closes the connection by calling stop() on the Proxy, the proxy which will then in turn stop the timer task. So any code that should be run after that must be inside an ensure block.
45
+
46
+ ## Proxies
47
+ A site connects to one of more supervisors. A proxy is created and run to handle each connection.
48
+
49
+ A site can connect to one or more supervisor. It creates a proxy for each and runs them.
50
+
51
+ When the proxy is run, it creates an async task to read incoming messages. This reader task is stored in `@reader`and will be a sub task of the site/supervisor main task.
52
+
53
+ A proxy also creates a timer task, stored in `@timer`. This task is used to check watchdog and acknowledgement timeouts.
54
+
55
+ Proxies build on the Task module functionality by handling RSMP communication via a TCP socket.
56
+
57
+ The TCP socket can be open or closed. The RSMP communication first goes through a handshake sequence before being ready. The `status` attribute keeps track of this, and can be either `:disconnected`, `:connected` or `:ready`
58
+
59
+ Proxies provide `connect` and `close` for starting and stopping commununication.
60
+
61
+ A supervisor proxies connects actively to a site proxy, whereas a site proxy waits for the supervisor proxy to connect.
62
+
63
+ A supervisor proxy is created at startup, and is responsible for creating the TCP socket and connecting to the supervisor.
64
+
65
+ A site proxy is also created at startup, but the socket is created in the supervisor by `Aync::Endpoint#accept`when a site connects.
66
+
67
+ ## Object Hierarchy
68
+ A Site has one or more SupervisorProxies, which represent connections to the remote supervisor.
69
+
70
+ A Supervisor has one or more SiteProxies, which represent connections to the remote site.
71
+
72
+ SiteProxy and SupervisorProxy both inherit from Proxy and have a reader task and a timer task. These are just Async tasks, not separate classes.
73
+
74
+ ## Task Hierachy
75
+ When you start an Async task, the parent will be the currently running task, unless you specify a different parent task.
76
+
77
+ The Async task hierarchy is similar to the object hierachy, with the difference that proxies have reader and timer tasks. And Async::IO which is used to handle TPC connections concurently, will create some intermediate tasks:
78
+
79
+ ```
80
+ Supervisor @task
81
+ accepting connections # this task is created by Async::IO
82
+ incoming connection 1 # this task is created by Async::IO
83
+ SiteProxy @task
84
+ SiteProxy @reader
85
+ SiteProxy @timer
86
+ incoming connection 2 # this task is created by Async::IO
87
+ SiteProxy @task
88
+ SiteProxy @reader
89
+ SiteProxy @timer
90
+
91
+ ```
92
+
93
+
94
+ A Site run from the CLI, before it connects to a supervisor:
95
+
96
+ ```
97
+ #<Async::Reactor:0xb90 1 children (running)>
98
+ #<Async::Task:0xba4 cli (running)>
99
+ #<Async::Task:0xbcc RSMP::TLC::TrafficControllerSite main task (running)>
100
+ #<Async::Task:0xbe0 RSMP::SupervisorProxy main task (running)>
101
+ #<Async::Task:0xbf4 tlc timer (running)>
102
+ ```
103
+
104
+ After the site connects to a supervisor:
105
+
106
+ ```
107
+ #<Async::Reactor:0xadc 1 children (running)>
108
+ #<Async::Task:0xaf0 cli (running)>
109
+ #<Async::Task:0xb18 RSMP::TLC::TrafficControllerSite main task (running)>
110
+ #<Async::Task:0xb2c RSMP::SupervisorProxy main task (running)>
111
+ #<Async::Task:0xb40 reader (running)>
112
+ #<Async::Task:0xb54 timer (running)>
113
+ #<Async::Task:0xb68 tlc timer (running)>
114
+ ```
115
+
116
+
117
+
118
+ A supervisor run from the CLI, before any sites have connected:
119
+
120
+ ```
121
+ #<Async::Reactor:0x94c 1 children (running)>
122
+ #<Async::Task:0x960 cli (running)>
123
+ #<Async::Task:0x988 RSMP::Supervisor main task (running)>
124
+ #<Async::Task:0x99c accepting connections #<Addrinfo: 0.0.0.0:14111 TCP> [fd=12] (running)>
125
+ ```
126
+
127
+ The supervisor after a site has connected
128
+
129
+ ```
130
+ #<Async::Reactor:0x94c 1 children (running)>
131
+ #<Async::Task:0x960 cli (running)>
132
+ #<Async::Task:0x988 RSMP::Supervisor main task (running)>
133
+ #<Async::Task:0x99c accepting connections #<Addrinfo: 0.0.0.0:13111 TCP> [fd=12] (running)>
134
+ #<Async::Task:0xac8 incoming connection #<Addrinfo: 127.0.0.1:51778 TCP> [fd=13] (running)>
135
+ #<Async::Task:0xadc RSMP::SiteProxy main task (running)>
136
+ #<Async::Task:0xaf0 reader (running)>
137
+ #<Async::Task:0xb04 timer (running)>
138
+ ```
139
+
140
+ The task hierachy matters when you stop a task or iterate on subtasks. Note that calling `Task#wait`
141
+ does not wait for subtasks, whereas Task#stop stops all subtasks.
142
+
143
+ ## Transient tasks
144
+ If you mark an Async task with `transient: true` when you created it, that task will be stopped aas soon as all normal task are completed. They will also be moved up the hierarchy if the parent is complete, but not the grandparent.
145
+
146
+ Transient tasks can be used to cleanup, but using an `ensure` block in the transient task. When you call `stop` on a task, an Async::Stop exception is raised, which will run the code in the `ensure` block.
147
+
148
+ Some of the RSpec tests runs tests in in transient task. As soon as the main test code is complete, any subtasks like Sites or Supervisors that might otherwise prevent the test from completing, will be stopped automatically.
149
+
data/lib/rsmp/archive.rb CHANGED
@@ -20,11 +20,11 @@ module RSMP
20
20
 
21
21
  def self.prepare_item item
22
22
  raise ArgumentError unless item.is_a? Hash
23
-
23
+
24
24
  cleaned = item.select { |k,v| [:author,:level,:ip,:port,:site_id,:component,:text,:message,:exception].include? k }
25
25
  cleaned[:timestamp] = Clock.now
26
26
  if item[:message]
27
- cleaned[:direction] = item[:message].direction
27
+ cleaned[:direction] = item[:message].direction
28
28
  cleaned[:component] = item[:message].attributes['cId']
29
29
  end
30
30
 
@@ -54,7 +54,7 @@ module RSMP
54
54
  @items.shift
55
55
  end
56
56
  end
57
-
57
+
58
58
  private
59
59
 
60
60
  def find options, &block
data/lib/rsmp/cli.rb CHANGED
@@ -11,7 +11,7 @@ module RSMP
11
11
  desc "site", "Run RSMP site"
12
12
  method_option :config, :type => :string, :aliases => "-c", banner: 'Path to .yaml config file'
13
13
  method_option :id, :type => :string, :aliases => "-i", banner: 'RSMP site id'
14
- method_option :supervisors, :type => :string, :aliases => "-s", banner: 'ip:port,... list of supervisor to connect to'
14
+ method_option :supervisors, :type => :string, :aliases => "-s", banner: 'ip:port,... list of supervisor to connect to'
15
15
  method_option :log, :type => :string, :aliases => "-l", banner: 'Path to log file'
16
16
  method_option :json, :type => :boolean, :aliases => "-j", banner: 'Show JSON messages in log'
17
17
  method_option :type, :type => :string, :aliases => "-t", banner: 'Type of site: [tlc]'
@@ -60,19 +60,35 @@ module RSMP
60
60
  site_class = RSMP::Site
61
61
  end
62
62
  end
63
- site_class.new(site_settings:settings, log_settings: log_settings).start
63
+ Async do |task|
64
+ task.annotate 'cli'
65
+ loop do
66
+ begin
67
+ site = site_class.new(site_settings:settings, log_settings: log_settings)
68
+ site.start
69
+ site.wait
70
+ rescue RSMP::Restart
71
+ site.stop
72
+ end
73
+ end
74
+ end
75
+ rescue Interrupt
76
+ # cntr-c
64
77
  rescue RSMP::Schemer::UnknownSchemaTypeError => e
65
78
  puts "Cannot start site: #{e}"
66
79
  rescue RSMP::Schemer::UnknownSchemaVersionError => e
67
80
  puts "Cannot start site: #{e}"
68
81
  rescue Psych::SyntaxError => e
69
82
  puts "Cannot read config file #{e}"
83
+ rescue Exception => e
84
+ puts "Uncaught error: #{e}"
85
+ puts caller.join("\n")
70
86
  end
71
87
 
72
88
  desc "supervisor", "Run RSMP supervisor"
73
89
  method_option :config, :type => :string, :aliases => "-c", banner: 'Path to .yaml config file'
74
90
  method_option :id, :type => :string, :aliases => "-i", banner: 'RSMP site id'
75
- method_option :ip, :type => :numeric, banner: 'IP address to listen on'
91
+ method_option :ip, :type => :numeric, banner: 'IP address to listen on'
76
92
  method_option :port, :type => :string, :aliases => "-p", banner: 'Port to listen on'
77
93
  method_option :log, :type => :string, :aliases => "-l", banner: 'Path to log file'
78
94
  method_option :json, :type => :boolean, :aliases => "-j", banner: 'Show JSON messages in log'
@@ -110,7 +126,14 @@ module RSMP
110
126
  log_settings['json'] = options[:json]
111
127
  end
112
128
 
113
- RSMP::Supervisor.new(supervisor_settings:settings,log_settings:log_settings).start
129
+ Async do |task|
130
+ task.annotate 'cli'
131
+ supervisor = RSMP::Supervisor.new(supervisor_settings:settings,log_settings:log_settings)
132
+ supervisor.start
133
+ supervisor.wait
134
+ end
135
+ rescue Interrupt
136
+ # ctrl-c
114
137
  rescue RSMP::ConfigurationError => e
115
138
  puts "Cannot start supervisor: #{e}"
116
139
  end
@@ -25,7 +25,7 @@ module RSMP
25
25
  # matches this input:
26
26
  #
27
27
  # {"cCI"=>"M0104", "n"=>"month", "v"=>"9", "age"=>"recent"}
28
- #
28
+ #
29
29
  # And the result is stored as:
30
30
  # {
31
31
  # {"cCI"=>"M0104", "cO"=>"setDate", "n"=>"month", "v"=>/\d+/} =>
@@ -3,7 +3,7 @@
3
3
  module RSMP
4
4
  module Components
5
5
  attr_reader :components
6
-
6
+
7
7
  def initialize_components
8
8
  @components = {}
9
9
  end
@@ -25,10 +25,10 @@ module RSMP
25
25
 
26
26
  def check_main_component settings
27
27
  unless settings['main'] && settings['main'].size >= 1
28
- raise ConfigurationError.new("main component must be defined")
28
+ raise ConfigurationError.new("main component must be defined")
29
29
  end
30
30
  if settings['main'].size > 1
31
- raise ConfigurationError.new("only one main component can be defined, found #{settings['main'].keys.join(', ')}")
31
+ raise ConfigurationError.new("only one main component can be defined, found #{settings['main'].keys.join(', ')}")
32
32
  end
33
33
  end
34
34
 
@@ -23,7 +23,7 @@ module RSMP
23
23
 
24
24
  def self.build_value item
25
25
  out = {}
26
-
26
+
27
27
  if item['description']
28
28
  out["description"] = item['description']
29
29
  end
@@ -95,7 +95,7 @@ module RSMP
95
95
  }
96
96
  end
97
97
  json = {
98
- "properties" => {
98
+ "properties" => {
99
99
  "aCId" => { "enum" => items.keys.sort },
100
100
  "rvs" => { "items" => { "allOf" => list } }
101
101
  }
@@ -175,7 +175,7 @@ module RSMP
175
175
  }
176
176
  ]
177
177
  }
178
- out["sxl.json"] = output_json json
178
+ out["sxl.json"] = output_json json
179
179
  end
180
180
 
181
181
  def self.generate sxl
@@ -192,7 +192,7 @@ module RSMP
192
192
  out.each_pair do |relative_path,str|
193
193
  path = File.join(folder, relative_path)
194
194
  FileUtils.mkdir_p File.dirname(path) # create folders if needed
195
- file = File.open(path, 'w+') # w+ means truncate or create new file
195
+ file = File.open(path, 'w+') # w+ means truncate or create new file
196
196
  file.puts str
197
197
  end
198
198
  end
@@ -24,7 +24,7 @@ module RSMP
24
24
  commands: {}
25
25
  }
26
26
 
27
- yaml['objects'].each_pair do |type,object|
27
+ yaml['objects'].each_pair do |type,object|
28
28
  object["alarms"].each { |id,item| sxl[:alarms][id] = item }
29
29
  object["statuses"].each { |id,item| sxl[:statuses][id] = item }
30
30
  object["commands"].each { |id,item| sxl[:commands][id] = item }
data/lib/rsmp/error.rb CHANGED
@@ -64,7 +64,4 @@ module RSMP
64
64
 
65
65
  class RepeatedStatusError < Error
66
66
  end
67
-
68
- class TimestampError < Error
69
- end
70
67
  end
data/lib/rsmp/inspect.rb CHANGED
@@ -1,5 +1,5 @@
1
1
  # Costume inspect, to reduce noise
2
- #
2
+ #
3
3
  # Instance variables of classes starting with Async or RSMP are shown
4
4
  # with only their class name and object id, which reduces output,
5
5
  # especially for deep object structures.
data/lib/rsmp/logger.rb CHANGED
@@ -2,7 +2,7 @@ module RSMP
2
2
  class Logger
3
3
 
4
4
  attr_accessor :settings
5
-
5
+
6
6
  def initialize settings={}
7
7
  defaults = {
8
8
  'active'=>true,
@@ -115,7 +115,7 @@ module RSMP
115
115
  end
116
116
  end
117
117
  end
118
- return false if ack && @settings["acknowledgements"] == false &&
118
+ return false if ack && @settings["acknowledgements"] == false &&
119
119
  [:not_acknowledged,:warning,:error].include?(item[:level]) == false
120
120
  end
121
121
  true
@@ -159,7 +159,7 @@ module RSMP
159
159
 
160
160
  def log item, force:false
161
161
  if output?(item, force)
162
- output item[:level], build_output(item)
162
+ output item[:level], build_output(item)
163
163
  end
164
164
  end
165
165
 
@@ -169,7 +169,7 @@ module RSMP
169
169
  else
170
170
  ' '*length
171
171
  end
172
- end
172
+ end
173
173
 
174
174
  def dump archive, force:false, num:nil
175
175
  num ||= archive.items.size
@@ -183,7 +183,7 @@ module RSMP
183
183
  def build_part parts, item, key, &block
184
184
  skey = key.to_s
185
185
  return unless @settings[skey]
186
-
186
+
187
187
  part = item[key]
188
188
  part = yield part if block
189
189
  part = part.to_s
data/lib/rsmp/logging.rb CHANGED
@@ -8,7 +8,7 @@ module RSMP
8
8
 
9
9
  def initialize_logging options
10
10
  @archive = options[:archive] || RSMP::Archive.new
11
- @logger = options[:logger] || RSMP::Logger.new(options[:log_settings])
11
+ @logger = options[:logger] || RSMP::Logger.new(options[:log_settings])
12
12
  end
13
13
 
14
14
  def author
data/lib/rsmp/message.rb CHANGED
@@ -188,7 +188,7 @@ module RSMP
188
188
  class Unknown < Message
189
189
  end
190
190
 
191
- class AggregatedStatus < Message
191
+ class AggregatedStatus < Message
192
192
  def initialize attributes = {}
193
193
  super({
194
194
  "type" => "AggregatedStatus",
data/lib/rsmp/node.rb CHANGED
@@ -3,14 +3,14 @@
3
3
  module RSMP
4
4
  class Node
5
5
  include Logging
6
- include Wait
7
6
  include Inspect
7
+ include Task
8
8
 
9
9
  attr_reader :archive, :logger, :task, :deferred, :error_queue, :clock, :collector
10
10
 
11
11
  def initialize options
12
12
  initialize_logging options
13
- @task = options[:task]
13
+ initialize_task
14
14
  @deferred = []
15
15
  @clock = Clock.new
16
16
  @error_queue = Async::Queue.new
@@ -18,6 +18,13 @@ module RSMP
18
18
  @collect = options[:collect]
19
19
  end
20
20
 
21
+ # stop proxies, then call super
22
+ def stop_subtasks
23
+ @proxies.each { |proxy| proxy.stop }
24
+ @proxies.clear
25
+ super
26
+ end
27
+
21
28
  def ignore_errors classes, &block
22
29
  was, @ignore_errors = @ignore_errors, [classes].flatten
23
30
  yield
@@ -50,55 +57,13 @@ module RSMP
50
57
 
51
58
  def clear_deferred
52
59
  @deferred.clear
53
- end
54
-
55
- def do_start task
56
- task.annotate self.class.to_s
57
- @task = task
58
- start_action
59
- idle
60
- end
61
-
62
- def start
63
- starting
64
- if @task
65
- do_start @task
66
- else
67
- Async do |task|
68
- do_start task
69
- end
70
- end
71
- rescue Errno::EADDRINUSE => e
72
- log "Cannot start: #{e.to_s}", level: :error
73
- rescue SystemExit, SignalException, Interrupt
74
- @logger.unmute_all
75
- exiting
76
- end
77
-
78
- def idle
79
- loop do
80
- @task.sleep 60
81
- end
82
- end
83
-
84
- def stop
85
- @task.stop if @task
86
- end
87
-
88
- def restart
89
- stop
90
- start
91
- end
92
-
93
- def exiting
94
- log "Exiting", level: :info
95
60
  end
96
61
 
97
62
  def check_required_settings settings, required
98
63
  raise ArgumentError.new "Settings is empty" unless settings
99
64
  required.each do |setting|
100
65
  raise ArgumentError.new "Missing setting: #{setting}" unless settings.include? setting.to_s
101
- end
66
+ end
102
67
  end
103
68
 
104
69
  def author