procrastinator 1.0.0.pre.rc2 → 1.0.0.pre.rc3
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
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 40bd65d0f873fba25ce55e8ab2e00934c15340ae3106d64e461abee8a08ff043
|
4
|
+
data.tar.gz: f28a1f18b563a29f8d4f3030ae8df273b47d387fdde7b03e4ed92dc40eebf810
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: '084cbefb918ee3193ef0589712d6ddbf2fde592d3f43c0d8b080e6816956c32d7f00fda8f136ee3d0ffa953314c288f195d7343eec110d0156386a72b792fe4f'
|
7
|
+
data.tar.gz: fcdbe66ed04995bdf89a6b4a13ddf5b9dc0302257d3f78521d9c28bbf7b81b227357254884afb56258a5191c7927588266d608e0aabc213121657d9107999a4f
|
@@ -19,36 +19,68 @@ module Procrastinator
|
|
19
19
|
#
|
20
20
|
# @param (see #define)
|
21
21
|
# @see DaemonTasks#define
|
22
|
-
def self.define(**args)
|
23
|
-
new.define(**args)
|
22
|
+
def self.define(**args, &block)
|
23
|
+
new.define(**args, &block)
|
24
24
|
end
|
25
25
|
|
26
26
|
# Defines procrastinator:start and procrastinator:stop Rake tasks that operate on the given scheduler.
|
27
|
-
# If provided a block, that block will run in the daemon process.
|
28
27
|
#
|
29
|
-
# @param
|
30
|
-
# @
|
31
|
-
|
32
|
-
|
28
|
+
# @param pid_path [Pathname, File, String, nil] The pid file path
|
29
|
+
# @yieldreturn scheduler [Procrastinator::Scheduler]
|
30
|
+
#
|
31
|
+
# @see Scheduler::DaemonWorking#daemonized!
|
32
|
+
def define(pid_path: nil)
|
33
|
+
raise ArgumentError, 'must provide a scheduler builder block' unless block_given?
|
34
|
+
|
35
|
+
@pid_path = Scheduler::DaemonWorking.normalize_pid pid_path
|
33
36
|
|
34
37
|
namespace :procrastinator do
|
38
|
+
desc 'Start the Procrastinator daemon'
|
35
39
|
task :start do
|
36
|
-
|
40
|
+
start(yield)
|
37
41
|
end
|
38
42
|
|
43
|
+
desc 'Show Procrastinator daemon status'
|
39
44
|
task :status do
|
40
|
-
|
41
|
-
warn "Procrastinator instance running (pid #{ File.read(pid_path) })"
|
42
|
-
else
|
43
|
-
warn "No Procrastinator instance detected for #{ pid_path }"
|
44
|
-
end
|
45
|
+
status
|
45
46
|
end
|
46
47
|
|
47
|
-
|
48
|
-
|
48
|
+
desc 'Stop the Procrastinator daemon'
|
49
|
+
task stop: [:status] do
|
50
|
+
stop
|
49
51
|
end
|
52
|
+
|
53
|
+
desc 'Restart Procrastinator daemon'
|
54
|
+
task restart: [:stop, :start]
|
50
55
|
end
|
51
56
|
end
|
57
|
+
|
58
|
+
private
|
59
|
+
|
60
|
+
def start(scheduler)
|
61
|
+
warn 'Starting Procrastinator'
|
62
|
+
scheduler.work.daemonized!(@pid_path)
|
63
|
+
end
|
64
|
+
|
65
|
+
def status
|
66
|
+
warn "Checking #{ @pid_path }..."
|
67
|
+
msg = if Scheduler::DaemonWorking.running?(@pid_path)
|
68
|
+
"Procrastinator pid #{ File.read(@pid_path) } instance running."
|
69
|
+
elsif File.exist?(@pid_path)
|
70
|
+
"Procrastinator pid #{ File.read(@pid_path) } is not running. Maybe it crashed?"
|
71
|
+
else
|
72
|
+
"Procrastinator is not running (No such file - #{ @pid_path })"
|
73
|
+
end
|
74
|
+
|
75
|
+
warn msg
|
76
|
+
end
|
77
|
+
|
78
|
+
def stop
|
79
|
+
return unless Scheduler::DaemonWorking.running?(@pid_path)
|
80
|
+
|
81
|
+
Scheduler::DaemonWorking.halt!(@pid_path)
|
82
|
+
warn "Procrastinator pid #{ File.read(@pid_path) } halted."
|
83
|
+
end
|
52
84
|
end
|
53
85
|
end
|
54
86
|
end
|
@@ -47,7 +47,7 @@ module Procrastinator
|
|
47
47
|
UpdateProxy.new(@config, identifier: identifier.merge(queue: queue.to_s))
|
48
48
|
end
|
49
49
|
|
50
|
-
# Removes an existing task, as located by the
|
50
|
+
# Removes an existing task, as located by the given identifying information.
|
51
51
|
#
|
52
52
|
# The identifier can include any data field stored in the task loader. Often this is the information in :data.
|
53
53
|
#
|
@@ -249,28 +249,27 @@ module Procrastinator
|
|
249
249
|
# 15 chars is linux limit
|
250
250
|
MAX_PROC_LEN = 15
|
251
251
|
|
252
|
-
# Consumes the current process and turns it into a background daemon
|
253
|
-
#
|
252
|
+
# Consumes the current process and turns it into a background daemon and proceed as #threaded.
|
253
|
+
# Additional logging is recorded in the directory specified by the Procrastinator.setup configuration.
|
254
254
|
#
|
255
255
|
# If pid_path ends with extension '.pid', the basename will be requested as process title (depending on OS
|
256
256
|
# support). An extensionless path is assumed to be a directory and a default filename (and proctitle) is used.
|
257
257
|
#
|
258
258
|
# @param pid_path [Pathname, File, String, nil] Path to where the process ID file is to be kept.
|
259
|
-
|
260
|
-
|
261
|
-
spawn_daemon(pid_path, &block)
|
259
|
+
def daemonized!(pid_path = nil)
|
260
|
+
spawn_daemon(pid_path)
|
262
261
|
|
263
262
|
threaded
|
264
263
|
end
|
265
264
|
|
266
265
|
# Normalizes the given pid path, including conversion to absolute path and defaults.
|
267
266
|
#
|
268
|
-
# @param pid_path [Pathname, String] path to normalize
|
267
|
+
# @param pid_path [Pathname, File, String, nil] path to normalize
|
269
268
|
def self.normalize_pid(pid_path)
|
270
|
-
|
271
|
-
|
269
|
+
normalized = Pathname.new(pid_path || DEFAULT_PID_DIR)
|
270
|
+
normalized /= "#{ PROG_NAME.downcase }#{ PID_EXT }" unless normalized.extname == PID_EXT
|
272
271
|
|
273
|
-
|
272
|
+
normalized.expand_path
|
274
273
|
end
|
275
274
|
|
276
275
|
# Stops the procrastinator process denoted by the provided pid file
|
@@ -287,15 +286,13 @@ module Procrastinator
|
|
287
286
|
Process.getpgid pid
|
288
287
|
|
289
288
|
true
|
290
|
-
rescue Errno::ESRCH
|
289
|
+
rescue Errno::ENOENT, Errno::ESRCH
|
291
290
|
false
|
292
291
|
end
|
293
292
|
|
294
293
|
private
|
295
294
|
|
296
|
-
|
297
|
-
# recitals through holiday weekend IKEA. Go."
|
298
|
-
def spawn_daemon(pid_path, &block)
|
295
|
+
def spawn_daemon(pid_path)
|
299
296
|
pid_path = DaemonWorking.normalize_pid pid_path
|
300
297
|
|
301
298
|
open_log quiet: true
|
@@ -303,14 +300,14 @@ module Procrastinator
|
|
303
300
|
|
304
301
|
print_debug_context
|
305
302
|
|
303
|
+
# "You, search from the spastic dentistry department down through disembowelment.
|
304
|
+
# You, cover children's dance recitals through holiday weekend IKEA. Go."
|
306
305
|
Process.daemon
|
307
306
|
|
308
307
|
manage_pid pid_path
|
309
308
|
rename_process pid_path
|
310
|
-
|
311
|
-
yield if block
|
312
309
|
rescue StandardError => e
|
313
|
-
@logger
|
310
|
+
@logger&.fatal ([e.message] + e.backtrace).join("\n")
|
314
311
|
raise e
|
315
312
|
end
|
316
313
|
|
@@ -346,10 +343,10 @@ module Procrastinator
|
|
346
343
|
end
|
347
344
|
|
348
345
|
def print_debug_context
|
349
|
-
@logger.debug "Ruby Path: #{ ENV
|
350
|
-
@logger.debug "Bundler Path: #{ ENV
|
351
|
-
#
|
352
|
-
@logger.debug "Runtime User: #{ ENV
|
346
|
+
@logger.debug "Ruby Path: #{ ENV.fetch 'RUBY_ROOT' }"
|
347
|
+
@logger.debug "Bundler Path: #{ ENV.fetch 'BUNDLE_BIN_PATH' }"
|
348
|
+
# LOGNAME is the posix standard and is set by cron, so probably reliable.
|
349
|
+
@logger.debug "Runtime User: #{ ENV.fetch('LOGNAME') || ENV.fetch('USERNAME') }"
|
353
350
|
end
|
354
351
|
|
355
352
|
def rename_process(pid_path)
|
@@ -54,8 +54,8 @@ module Procrastinator
|
|
54
54
|
end
|
55
55
|
|
56
56
|
def read(filter = {})
|
57
|
-
|
58
|
-
|
57
|
+
CSVFileTransaction.new(@path).read do |existing_data|
|
58
|
+
existing_data.select do |row|
|
59
59
|
filter.keys.all? do |key|
|
60
60
|
row[key] == filter[key]
|
61
61
|
end
|
@@ -70,8 +70,7 @@ module Procrastinator
|
|
70
70
|
# @param initial_run_at [Time, nil] first time to run the task at. Defaults to run_at.
|
71
71
|
# @param expire_at [Time, nil] time to expire the task
|
72
72
|
def create(queue:, run_at:, expire_at:, data: '', initial_run_at: nil)
|
73
|
-
|
74
|
-
tasks = parse(existing_data)
|
73
|
+
CSVFileTransaction.new(@path).write do |tasks|
|
75
74
|
max_id = tasks.collect { |task| task[:id] }.max || 0
|
76
75
|
|
77
76
|
new_data = {
|
@@ -89,8 +88,7 @@ module Procrastinator
|
|
89
88
|
end
|
90
89
|
|
91
90
|
def update(id, data)
|
92
|
-
|
93
|
-
tasks = parse(existing_data)
|
91
|
+
CSVFileTransaction.new(@path).write do |tasks|
|
94
92
|
task_data = tasks.find do |task|
|
95
93
|
task[:id] == id
|
96
94
|
end
|
@@ -102,8 +100,7 @@ module Procrastinator
|
|
102
100
|
end
|
103
101
|
|
104
102
|
def delete(id)
|
105
|
-
|
106
|
-
existing_data = parse(file_content)
|
103
|
+
CSVFileTransaction.new(@path).write do |existing_data|
|
107
104
|
generate(existing_data.reject { |task| task[:id] == id })
|
108
105
|
end
|
109
106
|
end
|
@@ -121,39 +118,45 @@ module Procrastinator
|
|
121
118
|
lines.join("\n") << "\n"
|
122
119
|
end
|
123
120
|
|
124
|
-
|
121
|
+
# Adds CSV parsing to the file reading
|
122
|
+
class CSVFileTransaction < FileTransaction
|
123
|
+
def transact(writable: nil)
|
124
|
+
super(writable: writable) do |file_str|
|
125
|
+
yield(parse(file_str))
|
126
|
+
end
|
127
|
+
end
|
125
128
|
|
126
|
-
|
127
|
-
data = CSV.parse(csv_string,
|
128
|
-
headers: true,
|
129
|
-
header_converters: :symbol,
|
130
|
-
skip_blanks: true,
|
131
|
-
converters: READ_CONVERTER,
|
132
|
-
force_quotes: true).to_a
|
129
|
+
private
|
133
130
|
|
134
|
-
|
131
|
+
def parse(csv_string)
|
132
|
+
data = CSV.parse(csv_string,
|
133
|
+
headers: true, header_converters: :symbol,
|
134
|
+
skip_blanks: true, converters: READ_CONVERTER, force_quotes: true).to_a
|
135
135
|
|
136
|
-
|
137
|
-
headers.zip(d).to_h
|
138
|
-
end
|
136
|
+
headers = data.shift || HEADERS
|
139
137
|
|
140
|
-
|
141
|
-
|
138
|
+
data = data.collect do |d|
|
139
|
+
headers.zip(d).to_h
|
140
|
+
end
|
142
141
|
|
143
|
-
|
144
|
-
|
142
|
+
correct_types(data)
|
143
|
+
end
|
145
144
|
|
146
|
-
data
|
147
|
-
non_empty_keys
|
148
|
-
hash.delete(key) if hash[key].is_a?(String) && hash[key].empty?
|
149
|
-
end
|
145
|
+
def correct_types(data)
|
146
|
+
non_empty_keys = [:run_at, :expire_at, :attempts, :last_fail_at]
|
150
147
|
|
151
|
-
|
148
|
+
data.collect do |hash|
|
149
|
+
non_empty_keys.each do |key|
|
150
|
+
hash.delete(key) if hash[key].is_a?(String) && hash[key].empty?
|
151
|
+
end
|
152
152
|
|
153
|
-
|
154
|
-
hash[:queue] = hash[:queue].to_sym
|
153
|
+
hash[:attempts] ||= 0
|
155
154
|
|
156
|
-
|
155
|
+
# hash[:data] = (hash[:data] || '').gsub('""', '"')
|
156
|
+
hash[:queue] = hash[:queue].to_sym
|
157
|
+
|
158
|
+
hash
|
159
|
+
end
|
157
160
|
end
|
158
161
|
end
|
159
162
|
end
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: procrastinator
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 1.0.0.pre.
|
4
|
+
version: 1.0.0.pre.rc3
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Robin Miller
|
8
8
|
autorequire:
|
9
9
|
bindir: exe
|
10
10
|
cert_chain: []
|
11
|
-
date: 2022-09-
|
11
|
+
date: 2022-09-17 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: bundler
|