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: a0d5fdab8cda8f1e8eee673be60373214b9619e6dfccbc8e1859bfd13d8f2709
4
- data.tar.gz: 932688bb438481332e0ad0f22bcb243760400fafe54a641db4687fb7242d28f2
3
+ metadata.gz: 40bd65d0f873fba25ce55e8ab2e00934c15340ae3106d64e461abee8a08ff043
4
+ data.tar.gz: f28a1f18b563a29f8d4f3030ae8df273b47d387fdde7b03e4ed92dc40eebf810
5
5
  SHA512:
6
- metadata.gz: c6d67aa9803f623778a2660075be6634f31f23a18463b2cd86cce55ccbba6d4c009febd2be35bcc411a635a530c10beaee8eb6f523d9312767ac77f89e59ca2a
7
- data.tar.gz: 2f0dc42e8824e40aee38526e0f4963c7347de9290c4b0b061e77e498282f8c40055ad477c7b0c6dfda78440bbd8d78d7b1c44040356aafc8c2ec0b0d1f582bc5
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 scheduler [Procrastinator::Scheduler]
30
- # @param pid_path [Pathname, File, String, nil]
31
- def define(scheduler:, pid_path: nil, &block)
32
- pid_path = Scheduler::DaemonWorking.normalize_pid(pid_path)
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
- scheduler.work.daemonized!(pid_path, &block)
40
+ start(yield)
37
41
  end
38
42
 
43
+ desc 'Show Procrastinator daemon status'
39
44
  task :status do
40
- if Scheduler::DaemonWorking.running?(pid_path)
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
- task :stop do
48
- Scheduler::DaemonWorking.halt!(pid_path)
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 givne identifying information.
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. A log will be started in the log
253
- # directory defined in the configuration block.
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
- # @yield [void] Block to run after daemonization
260
- def daemonized!(pid_path = nil, &block)
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
- pid_path = Pathname.new(pid_path || DEFAULT_PID_DIR)
271
- pid_path /= "#{ PROG_NAME.downcase }#{ PID_EXT }" unless pid_path.extname == PID_EXT
269
+ normalized = Pathname.new(pid_path || DEFAULT_PID_DIR)
270
+ normalized /= "#{ PROG_NAME.downcase }#{ PID_EXT }" unless normalized.extname == PID_EXT
272
271
 
273
- pid_path.expand_path
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
- # "You, search from the spastic dentistry department down through disembowelment. You, cover children's dance
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.fatal ([e.message] + e.backtrace).join("\n")
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['RUBY_ROOT'] }"
350
- @logger.debug "Bundler Path: #{ ENV['BUNDLE_BIN_PATH'] }"
351
- # logname is the posix standard and is set by cron, so probably reliable.
352
- @logger.debug "Runtime User: #{ ENV['LOGNAME'] || ENV['USERNAME'] }"
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
- FileTransaction.new(@path).read do |existing_data|
58
- parse(existing_data).select do |row|
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
- FileTransaction.new(@path).write do |existing_data|
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
- FileTransaction.new(@path).write do |existing_data|
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
- FileTransaction.new(@path).write do |file_content|
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
- private
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
- def parse(csv_string)
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
- headers = data.shift || HEADERS
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
- data = data.collect do |d|
137
- headers.zip(d).to_h
138
- end
136
+ headers = data.shift || HEADERS
139
137
 
140
- correct_types(data)
141
- end
138
+ data = data.collect do |d|
139
+ headers.zip(d).to_h
140
+ end
142
141
 
143
- def correct_types(data)
144
- non_empty_keys = [:run_at, :expire_at, :attempts, :last_fail_at]
142
+ correct_types(data)
143
+ end
145
144
 
146
- data.collect do |hash|
147
- non_empty_keys.each do |key|
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
- hash[:attempts] ||= 0
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
- # hash[:data] = (hash[:data] || '').gsub('""', '"')
154
- hash[:queue] = hash[:queue].to_sym
153
+ hash[:attempts] ||= 0
155
154
 
156
- hash
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
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Procrastinator
4
- VERSION = '1.0.0-rc2'
4
+ VERSION = '1.0.0-rc3'
5
5
  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.rc2
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-14 00:00:00.000000000 Z
11
+ date: 2022-09-17 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: bundler