ruby-clock 2.0.0.beta2 → 2.0.0.beta4
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/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
|
+
...
|