pampa 2.0.29 → 2.0.30
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.
- checksums.yaml +4 -4
- data/lib/pampa/app.rb +194 -0
- data/lib/pampa/dispatcher.rb +133 -0
- data/lib/pampa/worker.rb +172 -0
- data/lib/pampa.rb +42 -384
- data/pampa.gemspec +43 -0
- metadata +80 -17
- data/worker.rb +0 -147
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 8610d7df9639c7fe8e09b420795dc61c0cdb76b4df04b04a935689bec1d3d5be
|
4
|
+
data.tar.gz: 2c9d0b02219822ca36091805ab2e71ea59285db83255da737acd84d23eda7414
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 4500f05166c288b46cdce8f6c361cc9ca9a26314300b1e97248474ac2be432d863b2e34e0e3b24398c677910ef109ca0cbdeddf65d5fc387227560806966d784
|
7
|
+
data.tar.gz: e364de99b69c3c93b30fb59f69450a5e8637e91f36d8b810fb7eeb19b084c1686362841a48a1118aadc2fe347f31b0a3c2fc370754cbe536d941d1f770a2de4d
|
data/lib/pampa/app.rb
ADDED
@@ -0,0 +1,194 @@
|
|
1
|
+
# MySaaS - Pampa Dashboard
|
2
|
+
# Copyright (C) 2022 ExpandedVenture, LLC.
|
3
|
+
#
|
4
|
+
# Licensed under the Apache License, Version 2.0 (the "License");
|
5
|
+
# You may not use this file except in compliance with the License.
|
6
|
+
#
|
7
|
+
# Authors: Leandro Daniel Sardi (https://github.com/leandrosardi)
|
8
|
+
#
|
9
|
+
|
10
|
+
require 'pampa'
|
11
|
+
require "rubygems"
|
12
|
+
|
13
|
+
#
|
14
|
+
PARSER = BlackStack::SimpleCommandLineParser.new(
|
15
|
+
:description => 'This command will launch a Sinatra-based Pampa dashboard.',
|
16
|
+
:configuration => [{
|
17
|
+
:name=>'port',
|
18
|
+
:mandatory=>false,
|
19
|
+
:description=>'Listening port. Default: 3000.',
|
20
|
+
:type=>BlackStack::SimpleCommandLineParser::INT,
|
21
|
+
:default => 3000,
|
22
|
+
}, {
|
23
|
+
:name=>'config',
|
24
|
+
:mandatory=>false,
|
25
|
+
:description=>'Configuration file. Default: "config.rb".',
|
26
|
+
:type=>BlackStack::SimpleCommandLineParser::STRING,
|
27
|
+
:default => 'config.rb',
|
28
|
+
}, {
|
29
|
+
:name=>'db',
|
30
|
+
:mandatory=>false,
|
31
|
+
:default=>'postgres',
|
32
|
+
:description=>'Database driver. Supported values: postgres, crdb. Default: postgres.',
|
33
|
+
:type=>BlackStack::SimpleCommandLineParser::STRING,
|
34
|
+
}, {
|
35
|
+
:name=>'log',
|
36
|
+
:mandatory=>false,
|
37
|
+
:default=>true,
|
38
|
+
:description=>'If write log in the file ./app.log or not. Default: "yes"',
|
39
|
+
:type=>BlackStack::SimpleCommandLineParser::BOOL,
|
40
|
+
}]
|
41
|
+
)
|
42
|
+
|
43
|
+
# create logger
|
44
|
+
l = PARSER.value('log') ? BlackStack::LocalLogger.new('app.log') : BlackStack::BaseLogger.new(nil)
|
45
|
+
|
46
|
+
#
|
47
|
+
# load config file
|
48
|
+
l.logs "Loading #{PARSER.value('config').to_s.blue}... "
|
49
|
+
require PARSER.value('config')
|
50
|
+
l.logf 'done'.green
|
51
|
+
|
52
|
+
l.logs 'Connecting to database... '
|
53
|
+
if PARSER.value('db') == 'postgres'
|
54
|
+
DB = BlackStack::PostgreSQL::connect
|
55
|
+
elsif PARSER.value('db') == 'crdb'
|
56
|
+
DB = BlackStack::CockroachDB::connect
|
57
|
+
else
|
58
|
+
raise 'Unknown database driver.'
|
59
|
+
end
|
60
|
+
l.logf 'done'.green
|
61
|
+
|
62
|
+
#
|
63
|
+
spec = Gem.loaded_specs['pampa']
|
64
|
+
puts '
|
65
|
+
_______ _______ __ __ _______ _______
|
66
|
+
| || _ || |_| || || _ |
|
67
|
+
| _ || |_| || || _ || |_| |
|
68
|
+
| |_| || || || |_| || |
|
69
|
+
| ___|| || || ___|| |
|
70
|
+
| | | _ || ||_|| || | | _ |
|
71
|
+
|___| |__| |__||_| |_||___| |__| |__|
|
72
|
+
|
73
|
+
Version: '+spec.version.to_s.green+'.
|
74
|
+
Authors: '+spec.authors.join(', ').green+'.
|
75
|
+
Documentation: '+spec.homepage.blue+'
|
76
|
+
|
77
|
+
Sandbox: '+ (BlackStack.sandbox? ? 'yes'.green : 'no'.yellow) +'
|
78
|
+
|
79
|
+
'
|
80
|
+
|
81
|
+
PORT = PARSER.value("port")
|
82
|
+
|
83
|
+
configure { set :server, :puma }
|
84
|
+
set :bind, '0.0.0.0'
|
85
|
+
set :port, PORT
|
86
|
+
enable :sessions
|
87
|
+
enable :static
|
88
|
+
|
89
|
+
configure do
|
90
|
+
enable :cross_origin
|
91
|
+
end
|
92
|
+
|
93
|
+
before do
|
94
|
+
headers 'Access-Control-Allow-Origin' => '*',
|
95
|
+
'Access-Control-Allow-Methods' => ['OPTIONS', 'GET', 'POST']
|
96
|
+
end
|
97
|
+
|
98
|
+
set :protection, false
|
99
|
+
|
100
|
+
# Setting the root of views and public folders in the `~/code` folder in order to have access to extensions.
|
101
|
+
# reference: https://stackoverflow.com/questions/69028408/change-sinatra-views-directory-location
|
102
|
+
set :root, '.'
|
103
|
+
binding.pry
|
104
|
+
set :views, Gem.loaded_specs['pampa'].full_gem_path
|
105
|
+
|
106
|
+
# page not found redirection
|
107
|
+
not_found do
|
108
|
+
redirect '/404'
|
109
|
+
end
|
110
|
+
|
111
|
+
# unhandled exception redirectiopn
|
112
|
+
error do
|
113
|
+
max_lenght = 8000
|
114
|
+
s = "message=#{CGI.escape(env['sinatra.error'].to_s)}&"
|
115
|
+
s += "backtrace_size=#{CGI.escape(env['sinatra.error'].backtrace.size.to_s)}&"
|
116
|
+
i = 0
|
117
|
+
env['sinatra.error'].backtrace.each { |a|
|
118
|
+
a = "backtrace[#{i.to_s}]=#{CGI.escape(a.to_s)}&"
|
119
|
+
and_more = "backtrace[#{i.to_s}]=..."
|
120
|
+
if (s+a).size > max_lenght - and_more.size
|
121
|
+
s += and_more
|
122
|
+
break
|
123
|
+
else
|
124
|
+
s += a
|
125
|
+
end
|
126
|
+
i += 1
|
127
|
+
}
|
128
|
+
redirect "/500?#{s}"
|
129
|
+
end
|
130
|
+
|
131
|
+
# condition: api_key parameter is required too for the access points
|
132
|
+
set(:api_key) do |*roles|
|
133
|
+
condition do
|
134
|
+
@return_message = {}
|
135
|
+
|
136
|
+
@return_message[:status] = 'success'
|
137
|
+
|
138
|
+
# validate: the pages using the :api_key condition must work as post only.
|
139
|
+
if request.request_method != 'POST'
|
140
|
+
@return_message[:status] = 'Pages with an `api_key` parameter are only available for POST requests.'
|
141
|
+
@return_message[:value] = ""
|
142
|
+
halt @return_message.to_json
|
143
|
+
end
|
144
|
+
|
145
|
+
@body = JSON.parse(request.body.read)
|
146
|
+
|
147
|
+
if !@body.has_key?('api_key')
|
148
|
+
# libero recursos
|
149
|
+
DB.disconnect
|
150
|
+
GC.start
|
151
|
+
@return_message[:status] = "api_key is required on #{@body.to_s}"
|
152
|
+
@return_message[:value] = ""
|
153
|
+
halt @return_message.to_json
|
154
|
+
end
|
155
|
+
|
156
|
+
if !@body['api_key'].guid?
|
157
|
+
# libero recursos
|
158
|
+
DB.disconnect
|
159
|
+
GC.start
|
160
|
+
|
161
|
+
@return_message[:status] = "Invalid api_key (#{@body['api_key']}))"
|
162
|
+
@return_message[:value] = ""
|
163
|
+
halt @return_message.to_json
|
164
|
+
end
|
165
|
+
|
166
|
+
validation_api_key = @body['api_key'].to_guid.downcase
|
167
|
+
|
168
|
+
if validation_api_key != API_KEY
|
169
|
+
# libero recursos
|
170
|
+
DB.disconnect
|
171
|
+
GC.start
|
172
|
+
#
|
173
|
+
@return_message[:status] = 'Wrong api_key'
|
174
|
+
@return_message[:value] = ""
|
175
|
+
halt @return_message.to_json
|
176
|
+
end
|
177
|
+
end
|
178
|
+
end
|
179
|
+
|
180
|
+
get '/404', :agent => /(.*)/ do
|
181
|
+
erb :'views/404', :layout => :'/views/layouts/public'
|
182
|
+
end
|
183
|
+
|
184
|
+
get '/500', :agent => /(.*)/ do
|
185
|
+
erb :'views/500', :layout => :'/views/layouts/public'
|
186
|
+
end
|
187
|
+
|
188
|
+
# dashboard
|
189
|
+
get '/', :agent => /(.*)/ do
|
190
|
+
redirect '/dashboard'
|
191
|
+
end
|
192
|
+
get '/dashboard', :agent => /(.*)/ do
|
193
|
+
erb :'views/dashboard' #, :layout => :'/views/layouts/public'
|
194
|
+
end
|
@@ -0,0 +1,133 @@
|
|
1
|
+
# Pampa Dispatcher
|
2
|
+
# Copyright (C) 2022 ExpandedVenture, LLC.
|
3
|
+
#
|
4
|
+
# Licensed under the Apache License, Version 2.0 (the "License");
|
5
|
+
# You may not use this file except in compliance with the License.
|
6
|
+
#
|
7
|
+
# Authors: Leandro Daniel Sardi (https://github.com/leandrosardi)
|
8
|
+
#
|
9
|
+
|
10
|
+
require 'pampa'
|
11
|
+
|
12
|
+
# parse command line parameters
|
13
|
+
PARSER = BlackStack::SimpleCommandLineParser.new(
|
14
|
+
:description => 'This script starts an infinite loop. Each loop will look for a task to perform. Must be a delay between each loop.',
|
15
|
+
:configuration => [{
|
16
|
+
:name=>'delay',
|
17
|
+
:mandatory=>false,
|
18
|
+
:default=>10, # 5 minutes
|
19
|
+
:description=>'Minimum delay between loops. A minimum of 10 seconds is recommended, in order to don\'t hard the database server. Default is 30 seconds.',
|
20
|
+
:type=>BlackStack::SimpleCommandLineParser::INT,
|
21
|
+
}, {
|
22
|
+
:name=>'config',
|
23
|
+
:mandatory=>false,
|
24
|
+
:default=>'config.rb',
|
25
|
+
:description=>'Configuration file. Default: config.',
|
26
|
+
:type=>BlackStack::SimpleCommandLineParser::STRING,
|
27
|
+
}, {
|
28
|
+
:name=>'db',
|
29
|
+
:mandatory=>false,
|
30
|
+
:default=>'postgres',
|
31
|
+
:description=>'Database driver. Supported values: postgres, crdb. Default: postgres.',
|
32
|
+
:type=>BlackStack::SimpleCommandLineParser::STRING,
|
33
|
+
}, {
|
34
|
+
:name=>'log',
|
35
|
+
:mandatory=>false,
|
36
|
+
:default=>true,
|
37
|
+
:description=>'If write log in the file ./dispatcher.log or not. Default: "yes"',
|
38
|
+
:type=>BlackStack::SimpleCommandLineParser::BOOL,
|
39
|
+
}]
|
40
|
+
)
|
41
|
+
|
42
|
+
# create logger
|
43
|
+
l = PARSER.value('log') ? BlackStack::LocalLogger.new('dispatcher.log') : BlackStack::BaseLogger.new(nil)
|
44
|
+
|
45
|
+
# assign logger to pampa
|
46
|
+
BlackStack::Pampa.set_logger(l)
|
47
|
+
|
48
|
+
# load config file
|
49
|
+
l.logs "Loading #{PARSER.value('config').to_s.blue}... "
|
50
|
+
require PARSER.value('config')
|
51
|
+
l.logf 'done'.green
|
52
|
+
|
53
|
+
l.logs 'Connecting to database... '
|
54
|
+
if PARSER.value('db') == 'postgres'
|
55
|
+
DB = BlackStack::PostgreSQL::connect
|
56
|
+
elsif PARSER.value('db') == 'crdb'
|
57
|
+
DB = BlackStack::CockroachDB::connect
|
58
|
+
else
|
59
|
+
raise 'Unknown database driver.'
|
60
|
+
end
|
61
|
+
l.logf 'done'.green
|
62
|
+
|
63
|
+
# loop
|
64
|
+
begin
|
65
|
+
while true
|
66
|
+
# get the start loop time
|
67
|
+
l.logs 'Starting loop... '
|
68
|
+
start = Time.now()
|
69
|
+
l.logf 'done'.green
|
70
|
+
|
71
|
+
begin
|
72
|
+
# assign workers to each job
|
73
|
+
l.logs 'Stretching clusters... '
|
74
|
+
BlackStack::Pampa.stretch
|
75
|
+
l.logf 'done'.green
|
76
|
+
|
77
|
+
# relaunch expired tasks
|
78
|
+
l.logs 'Relaunching expired tasks... '
|
79
|
+
BlackStack::Pampa.relaunch
|
80
|
+
l.logf 'done'.green
|
81
|
+
|
82
|
+
# dispatch tasks to each worker
|
83
|
+
l.logs 'Dispatching tasks to workers... '
|
84
|
+
BlackStack::Pampa.dispatch
|
85
|
+
l.logf 'done'.green
|
86
|
+
|
87
|
+
# note: this catches the CTRL+C signal.
|
88
|
+
# note: this catches the `kill` command, ONLY if it has not the `-9` option.
|
89
|
+
rescue SignalException, SystemExit, Interrupt => e
|
90
|
+
l.logf 'Bye!'.yellow
|
91
|
+
raise e
|
92
|
+
rescue => e
|
93
|
+
l.logf "Error: #{e.to_console}".red
|
94
|
+
end
|
95
|
+
|
96
|
+
# release resource
|
97
|
+
l.logs 'Releasing resources... '
|
98
|
+
GC.start
|
99
|
+
DB.disconnect
|
100
|
+
l.logf 'done'.green
|
101
|
+
|
102
|
+
# get the end loop time
|
103
|
+
l.logs 'Ending loop... '
|
104
|
+
finish = Time.now()
|
105
|
+
l.logf 'done'.green
|
106
|
+
|
107
|
+
# get different in seconds between start and finish
|
108
|
+
# if diff > 30 seconds
|
109
|
+
l.logs 'Calculating loop duration... '
|
110
|
+
diff = finish - start
|
111
|
+
l.logf 'done'.green + " (#{diff.to_s.blue})"
|
112
|
+
|
113
|
+
if diff < PARSER.value('delay')
|
114
|
+
# sleep for 30 seconds
|
115
|
+
n = PARSER.value('delay')-diff
|
116
|
+
|
117
|
+
l.logs 'Sleeping for '+n.to_label+' seconds... '
|
118
|
+
sleep n
|
119
|
+
l.logf 'done'.green
|
120
|
+
else
|
121
|
+
l.log 'No sleeping. The loop took '+diff.to_label+' seconds.'
|
122
|
+
end
|
123
|
+
end # while true
|
124
|
+
rescue SignalException, SystemExit, Interrupt
|
125
|
+
# note: this catches the CTRL+C signal.
|
126
|
+
# note: this catches the `kill` command, ONLY if it has not the `-9` option.
|
127
|
+
l.logf 'Process Interrumpted.'.yellow
|
128
|
+
l.log 'Bye!'.yellow
|
129
|
+
rescue => e
|
130
|
+
l.logf "Fatal Error: #{e.to_console}".red
|
131
|
+
rescue
|
132
|
+
l.logf 'Unknown Fatal Error.'.red
|
133
|
+
end # begin
|
data/lib/pampa/worker.rb
ADDED
@@ -0,0 +1,172 @@
|
|
1
|
+
# MySaaS - Pampa Worker
|
2
|
+
# Copyright (C) 2022 ExpandedVenture, LLC.
|
3
|
+
#
|
4
|
+
# Licensed under the Apache License, Version 2.0 (the "License");
|
5
|
+
# You may not use this file except in compliance with the License.
|
6
|
+
#
|
7
|
+
# Authors: Leandro Daniel Sardi (https://github.com/leandrosardi)
|
8
|
+
#
|
9
|
+
|
10
|
+
# load gem and connect database
|
11
|
+
require 'pampa'
|
12
|
+
|
13
|
+
# parse command line parameters
|
14
|
+
PARSER = BlackStack::SimpleCommandLineParser.new(
|
15
|
+
:description => 'This script starts an infinite loop. Each loop will look for a task to perform. Must be a delay between each loop.',
|
16
|
+
:configuration => [{
|
17
|
+
:name=>'delay',
|
18
|
+
:mandatory=>false,
|
19
|
+
:default=>10,
|
20
|
+
:description=>'Minimum delay between loops. A minimum of 10 seconds is recommended, in order to don\'t hard the database server. Default is 30 seconds.',
|
21
|
+
:type=>BlackStack::SimpleCommandLineParser::INT,
|
22
|
+
}, {
|
23
|
+
:name=>'config',
|
24
|
+
:mandatory=>false,
|
25
|
+
:default=>'config.rb',
|
26
|
+
:description=>'Configuration file. Default: config.',
|
27
|
+
:type=>BlackStack::SimpleCommandLineParser::STRING,
|
28
|
+
}, {
|
29
|
+
:name=>'id',
|
30
|
+
:mandatory=>true,
|
31
|
+
:description=>'Write here a unique identifier for the worker.',
|
32
|
+
:type=>BlackStack::SimpleCommandLineParser::STRING,
|
33
|
+
}, {
|
34
|
+
:name=>'db',
|
35
|
+
:mandatory=>false,
|
36
|
+
:default=>'postgres',
|
37
|
+
:description=>'Database driver. Values: postgres, crdb. Default: postgres.',
|
38
|
+
:type=>BlackStack::SimpleCommandLineParser::STRING,
|
39
|
+
}, {
|
40
|
+
:name=>'log',
|
41
|
+
:mandatory=>false,
|
42
|
+
:default=>true,
|
43
|
+
:description=>'If write log in the file ./worker.#{id}.log or not. Default: "yes"',
|
44
|
+
:type=>BlackStack::SimpleCommandLineParser::BOOL,
|
45
|
+
}]
|
46
|
+
)
|
47
|
+
|
48
|
+
# create logger
|
49
|
+
l = PARSER.value('log') ? BlackStack::LocalLogger.new("worker.#{PARSER.value('id')}.log") : BlackStack::BaseLogger.new(nil)
|
50
|
+
|
51
|
+
# assign logger to pampa
|
52
|
+
BlackStack::Pampa.set_logger(l)
|
53
|
+
|
54
|
+
# load config file
|
55
|
+
l.logs "Loading #{PARSER.value('config').to_s.blue}... "
|
56
|
+
require PARSER.value('config')
|
57
|
+
l.logf 'done'.green
|
58
|
+
|
59
|
+
l.logs 'Connecting to database... '
|
60
|
+
if PARSER.value('db') == 'postgres'
|
61
|
+
DB = BlackStack::PostgreSQL::connect
|
62
|
+
elsif PARSER.value('db') == 'crdb'
|
63
|
+
DB = BlackStack::CockroachDB::connect
|
64
|
+
else
|
65
|
+
raise 'Unknown database driver.'
|
66
|
+
end
|
67
|
+
l.logf 'done'.green
|
68
|
+
|
69
|
+
begin
|
70
|
+
# getting the worker object
|
71
|
+
l.logs 'Getting worker '+PARSER.value('id').blue+'... '
|
72
|
+
worker = BlackStack::Pampa.workers.select { |w| w.id == PARSER.value('id') }.first
|
73
|
+
raise 'Worker '+PARSER.value('id')+' not found.' if worker.nil?
|
74
|
+
l.logf 'done'.green
|
75
|
+
|
76
|
+
# start the loop
|
77
|
+
while true
|
78
|
+
# get the start loop time
|
79
|
+
l.logs 'Starting loop... '
|
80
|
+
start = Time.now()
|
81
|
+
l.done
|
82
|
+
|
83
|
+
begin
|
84
|
+
l.log ''
|
85
|
+
l.logs 'Starting cycle... '
|
86
|
+
|
87
|
+
BlackStack::Pampa.jobs.each { |job|
|
88
|
+
task = nil
|
89
|
+
begin
|
90
|
+
l.logs 'Processing job '+job.name.blue+'... '
|
91
|
+
tasks = job.occupied_slots(worker)
|
92
|
+
l.logf tasks.size.to_s+' tasks in queue.'
|
93
|
+
|
94
|
+
tasks.each { |t|
|
95
|
+
task = t
|
96
|
+
|
97
|
+
l.logs 'Flag task '+job.name.blue+'.'+task[job.field_primary_key.to_sym].to_s.blue+' started... '
|
98
|
+
job.start(task)
|
99
|
+
l.logf 'done'.green
|
100
|
+
|
101
|
+
l.logs 'Processing task '+task[job.field_primary_key.to_sym].to_s.blue+'... '
|
102
|
+
job.processing_function.call(task, l, job, worker)
|
103
|
+
l.logf 'done'.green
|
104
|
+
|
105
|
+
l.logs 'Flag task '+job.name.blue+'.'+task[job.field_primary_key.to_sym].to_s.blue+' finished... '
|
106
|
+
job.finish(task)
|
107
|
+
l.logf 'done'.green
|
108
|
+
}
|
109
|
+
# note: this catches the CTRL+C signal.
|
110
|
+
# note: this catches the `kill` command, ONLY if it has not the `-9` option.
|
111
|
+
rescue SignalException, SystemExit, Interrupt => e
|
112
|
+
l.logs 'Flag task '+job.name.blue+'.'+task[job.field_primary_key.to_sym].to_s.blue+' interrumpted... '
|
113
|
+
job.finish(task, e)
|
114
|
+
l.logf 'done'.green
|
115
|
+
|
116
|
+
l.logf 'Bye!'.yellow
|
117
|
+
|
118
|
+
raise e
|
119
|
+
|
120
|
+
rescue => e
|
121
|
+
l.logs 'Flag task '+job.name.blue+'.'+task[job.field_primary_key.to_sym].to_s.blue+' failed... '
|
122
|
+
job.finish(task, e)
|
123
|
+
l.logf 'done'.green
|
124
|
+
|
125
|
+
l.logf "Error: #{e.to_console}".red
|
126
|
+
end
|
127
|
+
}
|
128
|
+
|
129
|
+
l.done
|
130
|
+
|
131
|
+
rescue => e
|
132
|
+
l.logf 'Error: '+e.message
|
133
|
+
end
|
134
|
+
|
135
|
+
# release resource
|
136
|
+
l.logs 'Releasing resources... '
|
137
|
+
GC.start
|
138
|
+
DB.disconnect
|
139
|
+
l.logf 'done'.green
|
140
|
+
|
141
|
+
# get the end loop time
|
142
|
+
l.logs 'Ending loop... '
|
143
|
+
finish = Time.now()
|
144
|
+
l.logf 'done'.green
|
145
|
+
|
146
|
+
# get different in seconds between start and finish
|
147
|
+
# if diff > 30 seconds
|
148
|
+
l.logs 'Calculating loop duration... '
|
149
|
+
diff = finish - start
|
150
|
+
l.logf 'done'.green + " (#{diff.to_s.blue})"
|
151
|
+
|
152
|
+
if diff < PARSER.value('delay')
|
153
|
+
# sleep for 30 seconds
|
154
|
+
n = PARSER.value('delay')-diff
|
155
|
+
|
156
|
+
l.logs 'Sleeping for '+n.to_label+' seconds... '
|
157
|
+
sleep n
|
158
|
+
l.logf 'done'.green
|
159
|
+
else
|
160
|
+
l.log 'No sleeping. The loop took '+diff.to_label.blue+' seconds.'
|
161
|
+
end
|
162
|
+
end # while true
|
163
|
+
rescue SignalException, SystemExit, Interrupt
|
164
|
+
# note: this catches the CTRL+C signal.
|
165
|
+
# note: this catches the `kill` command, ONLY if it has not the `-9` option.
|
166
|
+
l.logf 'Process Interrumpted.'.yellow
|
167
|
+
l.log 'Bye!'.yellow
|
168
|
+
rescue => e
|
169
|
+
l.logf "Fatal Error: #{e.to_console}".red
|
170
|
+
rescue
|
171
|
+
l.logf 'Unknown Fatal Error.'.red
|
172
|
+
end
|