procrastinator 1.0.0.pre.rc2 → 1.0.0.pre.rc4
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:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: a25c7e80284d0ae3ad43b279a597deba81fa7f748229f0b7dec8ad34821c6d49
|
4
|
+
data.tar.gz: 3f426d76ab8375a74b9e612ce5f18a083bc42159f931f1d2666c17e1444c129c
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: aa0eb5516defc7045e72ee4991316b51caa93ca51ee3ea912a54a805d87a66e9973e4235ccc81bd1fc985b0b5b00f3729467f1b5fb6bbad16880cce0d4660acf
|
7
|
+
data.tar.gz: e79298482620d17e4ab5ba07dab6fbcb02bbf81ef3d51970262175f0dbe0c4c6d7f466cb05ba0c1b6cc7082cd27bb660f9a0bf5cde986841ef74ffad507b6c7d
|
@@ -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.rc4
|
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
|