ruby-clock 2.0.0.beta2 → 2.0.0.beta4
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/CHANGELOG.md +22 -6
- data/README.md +58 -11
- data/example-app/Clockfile +41 -2
- data/example-app/Gemfile.lock +9 -8
- data/example-rails-app/Clockfile +7 -11
- data/example-rails-app/app/models/example.rb +16 -0
- data/exe/clock +10 -59
- data/lib/ruby-clock/around_actions.rb +33 -0
- data/lib/ruby-clock/dsl.rb +26 -0
- data/lib/ruby-clock/rails.rb +24 -0
- data/lib/ruby-clock/rake.rb +37 -0
- data/lib/ruby-clock/runners.rb +23 -0
- data/lib/ruby-clock/shell.rb +38 -0
- data/lib/ruby-clock/version.rb +1 -1
- data/lib/ruby-clock.rb +16 -75
- data/lib/rufus_monkeypatch.rb +14 -0
- data/ruby-clock.gemspec +10 -1
- metadata +19 -4
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 8900519602f68a1eb7e0a5b49aa1752c5a42ffb65a16b409b8f12c003237c568
|
4
|
+
data.tar.gz: bb9d30d35bafa38b970f061faa0416bffb66197f5fddd5d46a691c05e04fe82b
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 50482fd2178c9098bd8e2eb1218f56c56596d5983d82571246d5516cd977227a344598f056019027f2b97222ffc8a628eed503ed2d10defea116bfe5d6b953f1
|
7
|
+
data.tar.gz: 5eb78d5f6dffb1d09d09e9d1ada59a0bae9646253f789affec38311237b74b937192b1f7a414b2a5ff3e8fc2b34a3beb6fe1e8fda8a6aeffedfac80941e8a5a5
|
data/CHANGELOG.md
CHANGED
@@ -1,15 +1,34 @@
|
|
1
1
|
## 2.0.0 beta
|
2
2
|
|
3
|
+
### Features
|
3
4
|
* The way the [rails app reloader](https://guides.rubyonrails.org/threading_and_code_execution.html)
|
4
5
|
is implemented is now compatible with both rails 6 and 7
|
5
|
-
* The setup for rails is now less complicated
|
6
6
|
* RUBY_CLOCK_SHUTDOWN_WAIT_SECONDS value is logged when starting
|
7
|
-
* Code reorganization so there are no unnecessary methods in top-level Kernel namespace
|
8
7
|
* DSL methods are now at the top-level namespace (`schedule.every` → `every`, `schedule.cron` → `cron`)
|
9
|
-
*
|
8
|
+
* Error handler definition is now at the top-level namespace (`def schedule.on_error` → `on_error do`)
|
9
|
+
* Around callbacks now have a top-level namespace method. `def schedule.around_trigger` → `around_action` - see readme
|
10
|
+
* Multiple around callbacks can be consecutively assigned - no need to put all behavior into one action
|
11
|
+
* Errors encountered when loading Clockfile (such as incorrect cron syntaxt)
|
12
|
+
will be reported to the error handler
|
13
|
+
|
14
|
+
### Anti-Features
|
15
|
+
* rake and shell runners are no longer top-level (`shell` → `RubyClock::Runners.shell`, `rake` → `RubyClock::Runners.rake`)
|
16
|
+
|
17
|
+
### Code Improvements
|
18
|
+
* The code which implements the rails reloader/executor is now less complicated
|
19
|
+
* Code reorganization so there are no unnecessary methods in top-level Kernel namespace
|
20
|
+
|
10
21
|
|
11
22
|
### Migrating from ruby-clock version 1 to version 2
|
12
23
|
|
24
|
+
* The top of every Clockfile must begin with `using RubyClock::DSL`
|
25
|
+
* rake and shell runners must be invoked like so: `RubyClock::Runners.rake`, `RubyClock::Runners.shell`, etc.
|
26
|
+
* If you have an existing `def schedule.around_trigger`, you will need to change it to use the new
|
27
|
+
`around_action` method. Failure to change this will silently appear to keep working,
|
28
|
+
but will break the rails reloader/executor implementation.
|
29
|
+
* Your existing Clockfile will still work, but you now have the option to use
|
30
|
+
`every`, `cron`, and `on_error` at the top-level, without referencing `schedule`.
|
31
|
+
See the readme for examples.
|
13
32
|
* There is no longer a need to have a binstub in rails. You can delete bin/clock from your app.
|
14
33
|
* The invocations (in Procfile, or wherever else you start ruby-clock) should change from
|
15
34
|
|
@@ -17,9 +36,6 @@
|
|
17
36
|
to
|
18
37
|
|
19
38
|
bundle exec clock
|
20
|
-
* Your existing Clockfile will still work, but you now have the option to use
|
21
|
-
`every`, `cron`, and `on_error` at the top-level, without referencing `schedule`.
|
22
|
-
See the readme for examples.
|
23
39
|
|
24
40
|
## 1.0.0
|
25
41
|
|
data/README.md
CHANGED
@@ -1,4 +1,4 @@
|
|
1
|
-
THESE ARE THE DOCS FOR VERSION 2.0.0.
|
1
|
+
THESE ARE THE DOCS FOR VERSION 2.0.0.beta
|
2
2
|
|
3
3
|
See version 1 docs here: https://github.com/jjb/ruby-clock/tree/v1.0.0
|
4
4
|
|
@@ -50,6 +50,8 @@ Create a file named Clockfile. This will hold your job definitions.
|
|
50
50
|
Define jobs like this:
|
51
51
|
|
52
52
|
```ruby
|
53
|
+
using RubyClock::DSL
|
54
|
+
|
53
55
|
every('5 minutes') do
|
54
56
|
UserDataReports.generate
|
55
57
|
end
|
@@ -90,9 +92,9 @@ You may wish to
|
|
90
92
|
for your jobs. You can do so with the around trigger:
|
91
93
|
|
92
94
|
```ruby
|
93
|
-
|
95
|
+
around_action(job_proc)
|
94
96
|
ActiveRecord::Base.uncached do
|
95
|
-
|
97
|
+
job_proc.call
|
96
98
|
end
|
97
99
|
end
|
98
100
|
```
|
@@ -142,17 +144,59 @@ add `$stdout.sync = true` to the top of your Clockfile.
|
|
142
144
|
### Error Handling
|
143
145
|
|
144
146
|
You can catch and report errors raised in your jobs by defining an error catcher at
|
145
|
-
the top of your Clockfile like this
|
147
|
+
the top of your Clockfile like this. You should handle these two cases so that you can get
|
148
|
+
error reports about problems while loading the Clockfile:
|
146
149
|
|
147
150
|
```ruby
|
148
151
|
on_error do |job, error|
|
149
|
-
|
152
|
+
case job
|
153
|
+
when String # this means there was a problem parsing the Clockfile while starting
|
154
|
+
ErrorReporter.track_exception(error, tag: 'clock', severity: 'high')
|
155
|
+
else
|
156
|
+
ErrorReporter.track_exception(error, tag: 'clock', custom_attribute: {job_name: job.identifier})
|
157
|
+
end
|
150
158
|
end
|
151
159
|
```
|
152
160
|
|
153
161
|
### Callbacks
|
154
162
|
|
155
|
-
You can define
|
163
|
+
You can define around callbacks which will run for all jobs, like shown below.
|
164
|
+
This somewhat awkward syntax is necessary in order to enable the ability to define multiple callbacks.
|
165
|
+
(perhaps in different files, shared by multiple Clockfiles, etc.).
|
166
|
+
|
167
|
+
```ruby
|
168
|
+
around_action do |job_proc, job_info|
|
169
|
+
puts "before1 #{job_info.class}"
|
170
|
+
job_proc.call
|
171
|
+
puts "after1"
|
172
|
+
end
|
173
|
+
|
174
|
+
around_action do |job_proc|
|
175
|
+
puts "before2"
|
176
|
+
job_proc.call
|
177
|
+
puts "after2"
|
178
|
+
end
|
179
|
+
|
180
|
+
every('2 seconds') do
|
181
|
+
puts "hello from a ruby-clock job"
|
182
|
+
end
|
183
|
+
```
|
184
|
+
|
185
|
+
|
186
|
+
```
|
187
|
+
before1 Rufus::Scheduler::EveryJob
|
188
|
+
before2
|
189
|
+
hello from a ruby-clock job
|
190
|
+
after2
|
191
|
+
after1
|
192
|
+
```
|
193
|
+
|
194
|
+
The around callbacks code will be run in the individual job thread.
|
195
|
+
|
196
|
+
rufus-scheduler also provides before and after hooks. ruby-clock does not provide convenience methods for these
|
197
|
+
but you can easily use them via the `schedule` object. These will run in the outer scheduling thread and not in
|
198
|
+
the job thread, so they may have slightly different behavior in some cases. There is likely no reason to use them
|
199
|
+
instead of `around_action`.
|
156
200
|
Read [the rufus-scheduler documentation](https://github.com/jmettraux/rufus-scheduler/#callbacks)
|
157
201
|
to learn how to do this. Where the documentation references `s`, you should use `schedule`.
|
158
202
|
|
@@ -162,7 +206,7 @@ You can run shell commands in your jobs.
|
|
162
206
|
|
163
207
|
```ruby
|
164
208
|
every '1 day' do
|
165
|
-
shell('sh scripts/process_stuff.sh')
|
209
|
+
RubyClock::Runners.shell('sh scripts/process_stuff.sh')
|
166
210
|
end
|
167
211
|
```
|
168
212
|
|
@@ -208,11 +252,12 @@ needing to shell out and start another process.
|
|
208
252
|
|
209
253
|
```ruby
|
210
254
|
every '1 day' do
|
211
|
-
rake('reports:daily')
|
255
|
+
RubyClock::Runners.rake('reports:daily')
|
212
256
|
end
|
213
257
|
```
|
214
258
|
|
215
|
-
There is also `rake_execute` and `rake_async`.
|
259
|
+
There is also `RubyClock::Runners.rake_execute` and `RubyClock::Runners.rake_async`.
|
260
|
+
See [the code](https://github.com/jjb/ruby-clock/blob/main/lib/rake.rb)
|
216
261
|
and [this article](https://code.jjb.cc/running-rake-tasks-from-within-ruby-on-rails-code) for more info.
|
217
262
|
|
218
263
|
### Job Identifier
|
@@ -250,10 +295,12 @@ This can be used for keeping track of job behavior in logs or a
|
|
250
295
|
stats tracker. For example:
|
251
296
|
|
252
297
|
```ruby
|
253
|
-
|
298
|
+
around_action(job_proc, job_info)
|
299
|
+
trigger_time = Time.now
|
300
|
+
job_proc.call
|
254
301
|
duration = Time.now-trigger_time.to_t
|
255
302
|
StatsTracker.value('Clock: Job Execution Time', duration.round(2))
|
256
|
-
StatsTracker.value("Clock: Job #{
|
303
|
+
StatsTracker.value("Clock: Job #{job_info.identifier} Execution Time", duration.round(2))
|
257
304
|
StatsTracker.increment('Clock: Job Executions')
|
258
305
|
end
|
259
306
|
|
data/example-app/Clockfile
CHANGED
@@ -1,5 +1,28 @@
|
|
1
|
+
using RubyClock::DSL
|
2
|
+
|
1
3
|
on_error do |job, error|
|
2
|
-
|
4
|
+
case job
|
5
|
+
when String
|
6
|
+
puts "#{job}: #{error.class}: #{error.message}"
|
7
|
+
else
|
8
|
+
puts "An error has occurred with job #{job.identifier}: #{error.class}: #{error.message}"
|
9
|
+
end
|
10
|
+
end
|
11
|
+
|
12
|
+
# on_error do |job, error|
|
13
|
+
# raise error
|
14
|
+
# end
|
15
|
+
|
16
|
+
around_action do |job_proc, job_info|
|
17
|
+
puts "before1 #{job_info.class}"
|
18
|
+
job_proc.call
|
19
|
+
puts "after1"
|
20
|
+
end
|
21
|
+
|
22
|
+
around_action do |job_proc|
|
23
|
+
puts "before2"
|
24
|
+
job_proc.call
|
25
|
+
puts "after2"
|
3
26
|
end
|
4
27
|
|
5
28
|
every('2 seconds') do
|
@@ -7,9 +30,25 @@ every('2 seconds') do
|
|
7
30
|
end
|
8
31
|
|
9
32
|
every('2 seconds') do
|
10
|
-
shell 'say hello'
|
33
|
+
RubyClock::Runners.shell 'say hello'
|
34
|
+
end
|
35
|
+
|
36
|
+
every('2 seconds') do
|
37
|
+
raise "🐈️ this error is expected, to test the error catcher"
|
11
38
|
end
|
12
39
|
|
13
40
|
cron('*/10 * * * * *') do
|
14
41
|
puts "cron running on every 10th second #{Time.now}"
|
15
42
|
end
|
43
|
+
|
44
|
+
every('2 seconds') do
|
45
|
+
if defined?(schedule)
|
46
|
+
raise "💥 we do not expect the ruby-clock DSL to be available inside a job, but it is"
|
47
|
+
end
|
48
|
+
end
|
49
|
+
|
50
|
+
every('2 seconds') do
|
51
|
+
if defined?(shell) || defined?(rake)
|
52
|
+
puts "💥 we do not expect runners to be available inside a job, but it is"
|
53
|
+
end
|
54
|
+
end
|
data/example-app/Gemfile.lock
CHANGED
@@ -1,31 +1,32 @@
|
|
1
1
|
PATH
|
2
2
|
remote: ..
|
3
3
|
specs:
|
4
|
-
ruby-clock (0.
|
4
|
+
ruby-clock (2.0.0.beta4)
|
5
5
|
method_source
|
6
6
|
rufus-scheduler (~> 3.8)
|
7
7
|
|
8
8
|
GEM
|
9
9
|
remote: https://rubygems.org/
|
10
10
|
specs:
|
11
|
-
concurrent-ruby (1.1.
|
12
|
-
et-orbi (1.2.
|
11
|
+
concurrent-ruby (1.1.10)
|
12
|
+
et-orbi (1.2.7)
|
13
13
|
tzinfo
|
14
|
-
fugit (1.
|
15
|
-
et-orbi (~> 1
|
14
|
+
fugit (1.7.1)
|
15
|
+
et-orbi (~> 1, >= 1.2.7)
|
16
16
|
raabro (~> 1.4)
|
17
17
|
method_source (1.0.0)
|
18
18
|
raabro (1.4.0)
|
19
|
-
rufus-scheduler (3.8.
|
19
|
+
rufus-scheduler (3.8.2)
|
20
20
|
fugit (~> 1.1, >= 1.1.6)
|
21
|
-
tzinfo (2.0.
|
21
|
+
tzinfo (2.0.5)
|
22
22
|
concurrent-ruby (~> 1.0)
|
23
23
|
|
24
24
|
PLATFORMS
|
25
25
|
arm64-darwin-20
|
26
|
+
arm64-darwin-21
|
26
27
|
|
27
28
|
DEPENDENCIES
|
28
29
|
ruby-clock!
|
29
30
|
|
30
31
|
BUNDLED WITH
|
31
|
-
2.
|
32
|
+
2.3.8
|
data/example-rails-app/Clockfile
CHANGED
@@ -1,23 +1,19 @@
|
|
1
|
-
|
2
|
-
puts "An error has occurred: #{error.class}: #{error.message}"
|
3
|
-
end
|
1
|
+
using RubyClock::DSL
|
4
2
|
|
5
|
-
|
6
|
-
puts "hello from a ruby-clock job"
|
7
|
-
end
|
3
|
+
load '../example-app/Clockfile'
|
8
4
|
|
9
5
|
every('2 seconds') do
|
10
|
-
|
6
|
+
puts Example.count
|
11
7
|
end
|
12
8
|
|
13
9
|
every('2 seconds') do
|
14
|
-
puts Example.
|
10
|
+
puts Example.check_for_global_method
|
15
11
|
end
|
16
12
|
|
17
13
|
every('2 seconds') do
|
18
|
-
|
14
|
+
puts Example.check_for_runner
|
19
15
|
end
|
20
16
|
|
21
|
-
|
22
|
-
|
17
|
+
every('2 seconds') do
|
18
|
+
RubyClock::Runners.rake 'about'
|
23
19
|
end
|
@@ -1,2 +1,18 @@
|
|
1
1
|
class Example < ApplicationRecord
|
2
|
+
def self.check_for_global_method
|
3
|
+
if defined?(schedule)
|
4
|
+
raise "💥 Oh no, the ruby-clock DSL is in the global environment! 💥"
|
5
|
+
else
|
6
|
+
"🦝"
|
7
|
+
end
|
8
|
+
end
|
9
|
+
|
10
|
+
def self.check_for_runner
|
11
|
+
if defined?(shell) || defined?(rake)
|
12
|
+
raise "💥 Oh no, the runners got included in the global environment! 💥"
|
13
|
+
else
|
14
|
+
"🐅"
|
15
|
+
end
|
16
|
+
end
|
17
|
+
|
2
18
|
end
|
data/exe/clock
CHANGED
@@ -1,71 +1,22 @@
|
|
1
1
|
#!/usr/bin/env ruby
|
2
2
|
|
3
|
-
begin
|
4
|
-
require './config/environment.rb'
|
5
|
-
puts "Detected rails app has been loaded."
|
6
|
-
rescue LoadError
|
7
|
-
end
|
8
|
-
|
9
3
|
require 'ruby-clock'
|
10
|
-
|
11
|
-
|
12
|
-
class Rufus::Scheduler::Job
|
13
|
-
def identifier
|
14
|
-
@identifier ||= begin
|
15
|
-
name || handler.source.split("\n").reject(&:empty?).grep_v(/#.*/)[-2].strip
|
16
|
-
rescue
|
17
|
-
begin
|
18
|
-
source_location.join('-')
|
19
|
-
rescue
|
20
|
-
'error-calculating-job-identifier'
|
21
|
-
end
|
22
|
-
end
|
23
|
-
end
|
24
|
-
end
|
25
|
-
|
4
|
+
RubyClock.detect_and_load_rails_app
|
5
|
+
require 'rufus_monkeypatch'
|
26
6
|
RubyClock.instance.listen_to_signals
|
27
7
|
RubyClock.instance.prepare_rake
|
28
8
|
RubyClock.instance.schedule.pause
|
9
|
+
RubyClock.instance.add_rails_executor_to_around_actions
|
29
10
|
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
end
|
36
|
-
|
37
|
-
def on_error(&on_error_block)
|
38
|
-
RubyClock.instance.on_error = on_error_block
|
39
|
-
def schedule.on_error(job, error)
|
40
|
-
RubyClock.instance.on_error.call(job, error)
|
11
|
+
begin
|
12
|
+
load ARGV[0] || 'Clockfile'
|
13
|
+
rescue => clockfile_error
|
14
|
+
if RubyClock.instance.on_error
|
15
|
+
RubyClock.instance.on_error.call("An error has occured while parsing the clockfile", clockfile_error)
|
41
16
|
end
|
42
|
-
end
|
43
|
-
|
44
|
-
def cron(...)
|
45
|
-
RubyClock.instance.schedule.cron(...)
|
46
|
-
end
|
47
|
-
|
48
|
-
def every(...)
|
49
|
-
RubyClock.instance.schedule.every(...)
|
50
|
-
end
|
51
17
|
|
52
|
-
|
53
|
-
RubyClock.instance.shell(string)
|
54
|
-
end
|
55
|
-
#####################################
|
56
|
-
|
57
|
-
|
58
|
-
load ARGV[0] || 'Clockfile'
|
59
|
-
|
60
|
-
if defined?(::Rails)
|
61
|
-
schedule.instance_eval do
|
62
|
-
@old_around_trigger = method :around_trigger
|
63
|
-
def around_trigger(job)
|
64
|
-
::Rails.application.reloader.wrap do
|
65
|
-
@old_around_trigger.call(job){ yield }
|
66
|
-
end
|
67
|
-
end
|
68
|
-
end
|
18
|
+
raise
|
69
19
|
end
|
70
20
|
|
21
|
+
RubyClock.instance.freeze_around_actions
|
71
22
|
RubyClock.instance.run_jobs
|
@@ -0,0 +1,33 @@
|
|
1
|
+
module RubyClock::AroundActions
|
2
|
+
|
3
|
+
attr_accessor :around_actions
|
4
|
+
|
5
|
+
def freeze_around_actions
|
6
|
+
@around_actions.freeze
|
7
|
+
end
|
8
|
+
|
9
|
+
def set_up_around_actions
|
10
|
+
@around_actions = []
|
11
|
+
def schedule.around_trigger(job_info, &job_proc)
|
12
|
+
RubyClock.instance.call_with_around_action_stack(
|
13
|
+
RubyClock.instance.around_actions.reverse,
|
14
|
+
job_proc,
|
15
|
+
job_info
|
16
|
+
)
|
17
|
+
end
|
18
|
+
end
|
19
|
+
|
20
|
+
def call_with_around_action_stack(wrappers, job_proc, job_info)
|
21
|
+
case wrappers.count
|
22
|
+
when 0
|
23
|
+
job_proc.call(job_info)
|
24
|
+
else
|
25
|
+
call_with_around_action_stack(
|
26
|
+
wrappers[1..],
|
27
|
+
Proc.new{ wrappers.first.call(job_proc, job_info) },
|
28
|
+
job_info
|
29
|
+
)
|
30
|
+
end
|
31
|
+
end
|
32
|
+
|
33
|
+
end
|
@@ -0,0 +1,26 @@
|
|
1
|
+
module RubyClock::DSL
|
2
|
+
refine ::Kernel do
|
3
|
+
def schedule
|
4
|
+
RubyClock.instance.schedule
|
5
|
+
end
|
6
|
+
|
7
|
+
def on_error(&on_error_block)
|
8
|
+
RubyClock.instance.on_error = on_error_block
|
9
|
+
def schedule.on_error(job, error)
|
10
|
+
RubyClock.instance.on_error.call(job, error)
|
11
|
+
end
|
12
|
+
end
|
13
|
+
|
14
|
+
def around_action(&b)
|
15
|
+
RubyClock.instance.around_actions << b
|
16
|
+
end
|
17
|
+
|
18
|
+
def cron(...)
|
19
|
+
RubyClock.instance.schedule.cron(...)
|
20
|
+
end
|
21
|
+
|
22
|
+
def every(...)
|
23
|
+
RubyClock.instance.schedule.every(...)
|
24
|
+
end
|
25
|
+
end
|
26
|
+
end
|
@@ -0,0 +1,24 @@
|
|
1
|
+
# There is also rails-relevant code in rake.rb
|
2
|
+
module RubyClock::Rails
|
3
|
+
module ClassMethods
|
4
|
+
def detect_and_load_rails_app
|
5
|
+
begin
|
6
|
+
require './config/environment.rb'
|
7
|
+
puts "Detected rails app has been loaded."
|
8
|
+
rescue LoadError
|
9
|
+
end
|
10
|
+
end
|
11
|
+
end
|
12
|
+
|
13
|
+
module InstanceMethods
|
14
|
+
def add_rails_executor_to_around_actions
|
15
|
+
if defined?(::Rails)
|
16
|
+
RubyClock.instance.around_actions << Proc.new do |job_proc|
|
17
|
+
::Rails.application.executor.wrap do
|
18
|
+
job_proc.call
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end
|
22
|
+
end
|
23
|
+
end
|
24
|
+
end
|
@@ -0,0 +1,37 @@
|
|
1
|
+
# See https://code.jjb.cc/running-rake-tasks-from-within-ruby-on-rails-code
|
2
|
+
module RubyClock::Rake
|
3
|
+
def prepare_rake
|
4
|
+
if defined?(::Rails) && Rails.application
|
5
|
+
Rails.application.load_tasks
|
6
|
+
Rake::Task.tasks.each{|t| t.prerequisites.delete 'environment' }
|
7
|
+
@rake_mutex = Mutex.new
|
8
|
+
else
|
9
|
+
puts <<~MESSAGE
|
10
|
+
Because this is not a rails application, we do not know how to load your
|
11
|
+
rake tasks. You can do this yourself at the top of your Clockfile if you want
|
12
|
+
to run rake tasks from ruby-clock.
|
13
|
+
MESSAGE
|
14
|
+
end
|
15
|
+
end
|
16
|
+
|
17
|
+
# for tasks that don't have dependencies
|
18
|
+
def rake_execute(task)
|
19
|
+
Rake::Task[task].execute
|
20
|
+
end
|
21
|
+
|
22
|
+
# If the task doesn't share dependencies with another task,
|
23
|
+
# or if it does and you know you'll never run tasks such that any overlap
|
24
|
+
def rake_async(task)
|
25
|
+
Rake::Task[task].invoke
|
26
|
+
ensure
|
27
|
+
Rake::Task[task].reenable
|
28
|
+
Rake::Task[task].all_prerequisite_tasks.each(&:reenable)
|
29
|
+
end
|
30
|
+
|
31
|
+
# If the task has shared dependencies and you might run more than one at the same time
|
32
|
+
# This is the safest option and hence the default.
|
33
|
+
def rake(task)
|
34
|
+
@rake_mutex.synchronize { rake_async(task) }
|
35
|
+
end
|
36
|
+
|
37
|
+
end
|
@@ -0,0 +1,23 @@
|
|
1
|
+
# if we ever want to make these available at the top-level using refinements,
|
2
|
+
# like we do with the DSL, it doesn't work in Rails because Rails prepends Kernel after
|
3
|
+
# we use the refinment. Initial tests show that prepending ActiveSupport::ForkTracker::CoreExtPrivate
|
4
|
+
# works, but maybe that's not reliable or has unknown side effects
|
5
|
+
# https://stackoverflow.com/questions/74119178/
|
6
|
+
|
7
|
+
module RubyClock::Runners
|
8
|
+
def self.shell(string)
|
9
|
+
RubyClock.instance.shell(string)
|
10
|
+
end
|
11
|
+
|
12
|
+
def self.rake(string)
|
13
|
+
RubyClock.instance.rake(string)
|
14
|
+
end
|
15
|
+
|
16
|
+
def self.rake_execute(string)
|
17
|
+
RubyClock.instance.rake_execute(string)
|
18
|
+
end
|
19
|
+
|
20
|
+
def self.rake_async(string)
|
21
|
+
RubyClock.instance.rake_async(string)
|
22
|
+
end
|
23
|
+
end
|
@@ -0,0 +1,38 @@
|
|
1
|
+
module RubyClock::Shell
|
2
|
+
def shell_runner
|
3
|
+
@shell_runner ||= begin
|
4
|
+
require 'terrapin'
|
5
|
+
require 'posix-spawn'
|
6
|
+
|
7
|
+
unless Terrapin::CommandLine.runner.class == Terrapin::CommandLine::PosixRunner
|
8
|
+
puts <<~MESSAGE
|
9
|
+
|
10
|
+
🤷 terrapin and posix-spawn are installed, but for some reason terrapin is
|
11
|
+
not using posix-spawn as its runner.
|
12
|
+
|
13
|
+
MESSAGE
|
14
|
+
end
|
15
|
+
|
16
|
+
puts '🐆 Using terrapin for shell commands.'
|
17
|
+
:terrapin
|
18
|
+
rescue LoadError
|
19
|
+
puts <<~MESSAGE
|
20
|
+
|
21
|
+
🦥 Using ruby backticks for shell commands.
|
22
|
+
For better performance, install the terrapin and posix-spawn gems.
|
23
|
+
See README.md for more info.
|
24
|
+
|
25
|
+
MESSAGE
|
26
|
+
:backticks
|
27
|
+
end
|
28
|
+
end
|
29
|
+
|
30
|
+
def shell(command)
|
31
|
+
case shell_runner
|
32
|
+
when :terrapin
|
33
|
+
Terrapin::CommandLine.new(command).run
|
34
|
+
when :backticks
|
35
|
+
`#{command}`
|
36
|
+
end
|
37
|
+
end
|
38
|
+
end
|
data/lib/ruby-clock/version.rb
CHANGED
data/lib/ruby-clock.rb
CHANGED
@@ -1,13 +1,28 @@
|
|
1
|
+
require "ruby-clock/dsl"
|
1
2
|
require "ruby-clock/version"
|
3
|
+
require "ruby-clock/rake"
|
4
|
+
require "ruby-clock/shell"
|
5
|
+
require "ruby-clock/around_actions"
|
6
|
+
require "ruby-clock/dsl"
|
7
|
+
require "ruby-clock/runners"
|
8
|
+
require "ruby-clock/rails"
|
2
9
|
require 'rufus-scheduler'
|
3
10
|
require 'singleton'
|
4
11
|
|
5
12
|
class RubyClock
|
6
|
-
|
7
13
|
include Singleton
|
14
|
+
include RubyClock::Rails::InstanceMethods
|
15
|
+
extend RubyClock::Rails::ClassMethods
|
16
|
+
include RubyClock::Rake
|
17
|
+
include RubyClock::Shell
|
18
|
+
include RubyClock::AroundActions
|
8
19
|
|
9
20
|
attr_accessor :on_error
|
10
21
|
|
22
|
+
def initialize
|
23
|
+
set_up_around_actions
|
24
|
+
end
|
25
|
+
|
11
26
|
def wait_seconds
|
12
27
|
ENV['RUBY_CLOCK_SHUTDOWN_WAIT_SECONDS']&.to_i || 29
|
13
28
|
end
|
@@ -42,78 +57,4 @@ class RubyClock
|
|
42
57
|
schedule.resume
|
43
58
|
schedule.join
|
44
59
|
end
|
45
|
-
|
46
|
-
def prepare_rake
|
47
|
-
if defined?(::Rails) && Rails.application
|
48
|
-
Rails.application.load_tasks
|
49
|
-
Rake::Task.tasks.each{|t| t.prerequisites.delete 'environment' }
|
50
|
-
@rake_mutex = Mutex.new
|
51
|
-
else
|
52
|
-
puts <<~MESSAGE
|
53
|
-
Because this is not a rails application, we do not know how to load your
|
54
|
-
rake tasks. You can do this yourself at the top of your Clockfile if you want
|
55
|
-
to run rake tasks from ruby-clock.
|
56
|
-
MESSAGE
|
57
|
-
end
|
58
|
-
end
|
59
|
-
|
60
|
-
# See https://code.jjb.cc/running-rake-tasks-from-within-ruby-on-rails-code
|
61
|
-
|
62
|
-
# for tasks that don't have dependencies
|
63
|
-
def rake_execute(task)
|
64
|
-
Rake::Task[task].execute
|
65
|
-
end
|
66
|
-
|
67
|
-
# If the task doesn't share dependencies with another task,
|
68
|
-
# or if it does and you know you'll never run tasks such that any overlap
|
69
|
-
def rake_async(task)
|
70
|
-
Rake::Task[task].invoke
|
71
|
-
ensure
|
72
|
-
Rake::Task[task].reenable
|
73
|
-
Rake::Task[task].all_prerequisite_tasks.each(&:reenable)
|
74
|
-
end
|
75
|
-
|
76
|
-
# If the task has shared dependencies and you might run more than one at the same time
|
77
|
-
# This is the safest option and hence the default.
|
78
|
-
def rake(task)
|
79
|
-
@rake_mutex.synchronize { rake_async(task) }
|
80
|
-
end
|
81
|
-
|
82
|
-
def shell_runner
|
83
|
-
@shell_runner ||= begin
|
84
|
-
require 'terrapin'
|
85
|
-
require 'posix-spawn'
|
86
|
-
|
87
|
-
unless Terrapin::CommandLine.runner.class == Terrapin::CommandLine::PosixRunner
|
88
|
-
puts <<~MESSAGE
|
89
|
-
|
90
|
-
🤷 terrapin and posix-spawn are installed, but for some reason terrapin is
|
91
|
-
not using posix-spawn as its runner.
|
92
|
-
|
93
|
-
MESSAGE
|
94
|
-
end
|
95
|
-
|
96
|
-
puts '🐆 Using terrapin for shell commands.'
|
97
|
-
:terrapin
|
98
|
-
rescue LoadError
|
99
|
-
puts <<~MESSAGE
|
100
|
-
|
101
|
-
🦥 Using ruby backticks for shell commands.
|
102
|
-
For better performance, install the terrapin and posix-spawn gems.
|
103
|
-
See README.md for more info.
|
104
|
-
|
105
|
-
MESSAGE
|
106
|
-
:backticks
|
107
|
-
end
|
108
|
-
end
|
109
|
-
|
110
|
-
def shell(command)
|
111
|
-
case shell_runner
|
112
|
-
when :terrapin
|
113
|
-
Terrapin::CommandLine.new(command).run
|
114
|
-
when :backticks
|
115
|
-
`#{command}`
|
116
|
-
end
|
117
|
-
end
|
118
|
-
|
119
60
|
end
|
@@ -0,0 +1,14 @@
|
|
1
|
+
require 'method_source'
|
2
|
+
class Rufus::Scheduler::Job
|
3
|
+
def identifier
|
4
|
+
@identifier ||= begin
|
5
|
+
name || handler.source.split("\n").reject(&:empty?).grep_v(/#.*/)[-2].strip
|
6
|
+
rescue
|
7
|
+
begin
|
8
|
+
source_location.join('-')
|
9
|
+
rescue
|
10
|
+
'error-calculating-job-identifier'
|
11
|
+
end
|
12
|
+
end
|
13
|
+
end
|
14
|
+
end
|
data/ruby-clock.gemspec
CHANGED
@@ -6,8 +6,17 @@ Gem::Specification.new do |spec|
|
|
6
6
|
spec.authors = ["John Bachir"]
|
7
7
|
spec.email = ["j@jjb.cc"]
|
8
8
|
|
9
|
-
spec.summary = 'A
|
9
|
+
spec.summary = 'A job scheduler which runs jobs each in their own thread in a persistent process.'
|
10
10
|
# spec.description = %q{TODO: Write a longer description or delete this line.}
|
11
|
+
spec.post_install_message = <<~MESSAGE
|
12
|
+
|
13
|
+
If you are updating ruby-clock from 1→2, there are a few things you need to change in your Clockfile.
|
14
|
+
|
15
|
+
It's quick, easy, and fun! See instructions here:
|
16
|
+
https://github.com/jjb/ruby-clock/blob/main/CHANGELOG.md#migrating-from-ruby-clock-version-1-to-version-2
|
17
|
+
|
18
|
+
MESSAGE
|
19
|
+
|
11
20
|
spec.homepage = "https://github.com/jjb/ruby-clock"
|
12
21
|
spec.license = "MIT"
|
13
22
|
spec.required_ruby_version = Gem::Requirement.new(">= 2.7.0")
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: ruby-clock
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 2.0.0.
|
4
|
+
version: 2.0.0.beta4
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- John Bachir
|
8
8
|
autorequire:
|
9
9
|
bindir: exe
|
10
10
|
cert_chain: []
|
11
|
-
date: 2022-10-
|
11
|
+
date: 2022-10-23 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: rufus-scheduler
|
@@ -142,7 +142,14 @@ files:
|
|
142
142
|
- example-rails-app/vendor/javascript/.keep
|
143
143
|
- exe/clock
|
144
144
|
- lib/ruby-clock.rb
|
145
|
+
- lib/ruby-clock/around_actions.rb
|
146
|
+
- lib/ruby-clock/dsl.rb
|
147
|
+
- lib/ruby-clock/rails.rb
|
148
|
+
- lib/ruby-clock/rake.rb
|
149
|
+
- lib/ruby-clock/runners.rb
|
150
|
+
- lib/ruby-clock/shell.rb
|
145
151
|
- lib/ruby-clock/version.rb
|
152
|
+
- lib/rufus_monkeypatch.rb
|
146
153
|
- release.md
|
147
154
|
- ruby-clock.gemspec
|
148
155
|
homepage: https://github.com/jjb/ruby-clock
|
@@ -151,7 +158,13 @@ licenses:
|
|
151
158
|
metadata:
|
152
159
|
homepage_uri: https://github.com/jjb/ruby-clock
|
153
160
|
source_code_uri: https://github.com/jjb/ruby-clock
|
154
|
-
post_install_message:
|
161
|
+
post_install_message: |2+
|
162
|
+
|
163
|
+
If you are updating ruby-clock from 1→2, there are a few things you need to change in your Clockfile.
|
164
|
+
|
165
|
+
It's quick, easy, and fun! See instructions here:
|
166
|
+
https://github.com/jjb/ruby-clock/blob/main/CHANGELOG.md#migrating-from-ruby-clock-version-1-to-version-2
|
167
|
+
|
155
168
|
rdoc_options: []
|
156
169
|
require_paths:
|
157
170
|
- lib
|
@@ -169,5 +182,7 @@ requirements: []
|
|
169
182
|
rubygems_version: 3.1.6
|
170
183
|
signing_key:
|
171
184
|
specification_version: 4
|
172
|
-
summary: A
|
185
|
+
summary: A job scheduler which runs jobs each in their own thread in a persistent
|
186
|
+
process.
|
173
187
|
test_files: []
|
188
|
+
...
|