ruby-clock 2.0.0.beta3 → 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 -10
- data/README.md +20 -9
- data/example-app/Clockfile +23 -9
- data/example-app/Gemfile.lock +1 -1
- data/example-rails-app/Clockfile +14 -0
- data/example-rails-app/app/models/example.rb +16 -0
- data/exe/clock +10 -60
- 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 +14 -98
- 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,19 +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
|
-
*
|
10
|
-
*
|
11
|
-
*
|
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
|
+
|
12
21
|
|
13
22
|
### Migrating from ruby-clock version 1 to version 2
|
14
23
|
|
15
|
-
*
|
16
|
-
|
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.
|
17
32
|
* There is no longer a need to have a binstub in rails. You can delete bin/clock from your app.
|
18
33
|
* The invocations (in Procfile, or wherever else you start ruby-clock) should change from
|
19
34
|
|
@@ -21,9 +36,6 @@
|
|
21
36
|
to
|
22
37
|
|
23
38
|
bundle exec clock
|
24
|
-
* Your existing Clockfile will still work, but you now have the option to use
|
25
|
-
`every`, `cron`, and `on_error` at the top-level, without referencing `schedule`.
|
26
|
-
See the readme for examples.
|
27
39
|
|
28
40
|
## 1.0.0
|
29
41
|
|
data/README.md
CHANGED
@@ -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,11 +144,17 @@ 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
|
|
@@ -198,7 +206,7 @@ You can run shell commands in your jobs.
|
|
198
206
|
|
199
207
|
```ruby
|
200
208
|
every '1 day' do
|
201
|
-
shell('sh scripts/process_stuff.sh')
|
209
|
+
RubyClock::Runners.shell('sh scripts/process_stuff.sh')
|
202
210
|
end
|
203
211
|
```
|
204
212
|
|
@@ -244,11 +252,12 @@ needing to shell out and start another process.
|
|
244
252
|
|
245
253
|
```ruby
|
246
254
|
every '1 day' do
|
247
|
-
rake('reports:daily')
|
255
|
+
RubyClock::Runners.rake('reports:daily')
|
248
256
|
end
|
249
257
|
```
|
250
258
|
|
251
|
-
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)
|
252
261
|
and [this article](https://code.jjb.cc/running-rake-tasks-from-within-ruby-on-rails-code) for more info.
|
253
262
|
|
254
263
|
### Job Identifier
|
@@ -286,10 +295,12 @@ This can be used for keeping track of job behavior in logs or a
|
|
286
295
|
stats tracker. For example:
|
287
296
|
|
288
297
|
```ruby
|
289
|
-
|
298
|
+
around_action(job_proc, job_info)
|
299
|
+
trigger_time = Time.now
|
300
|
+
job_proc.call
|
290
301
|
duration = Time.now-trigger_time.to_t
|
291
302
|
StatsTracker.value('Clock: Job Execution Time', duration.round(2))
|
292
|
-
StatsTracker.value("Clock: Job #{
|
303
|
+
StatsTracker.value("Clock: Job #{job_info.identifier} Execution Time", duration.round(2))
|
293
304
|
StatsTracker.increment('Clock: Job Executions')
|
294
305
|
end
|
295
306
|
|
data/example-app/Clockfile
CHANGED
@@ -1,6 +1,14 @@
|
|
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
|
3
10
|
end
|
11
|
+
|
4
12
|
# on_error do |job, error|
|
5
13
|
# raise error
|
6
14
|
# end
|
@@ -17,24 +25,30 @@ around_action do |job_proc|
|
|
17
25
|
puts "after2"
|
18
26
|
end
|
19
27
|
|
20
|
-
around_action do |job_proc, job_info|
|
21
|
-
puts "before3 #{job_info.class}"
|
22
|
-
job_proc.call
|
23
|
-
puts "after3"
|
24
|
-
end
|
25
|
-
|
26
28
|
every('2 seconds') do
|
27
29
|
puts "hello from a ruby-clock job"
|
28
30
|
end
|
29
31
|
|
30
32
|
every('2 seconds') do
|
31
|
-
shell 'say hello'
|
33
|
+
RubyClock::Runners.shell 'say hello'
|
32
34
|
end
|
33
35
|
|
34
36
|
every('2 seconds') do
|
35
|
-
raise "
|
37
|
+
raise "🐈️ this error is expected, to test the error catcher"
|
36
38
|
end
|
37
39
|
|
38
40
|
cron('*/10 * * * * *') do
|
39
41
|
puts "cron running on every 10th second #{Time.now}"
|
40
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
data/example-rails-app/Clockfile
CHANGED
@@ -1,5 +1,19 @@
|
|
1
|
+
using RubyClock::DSL
|
2
|
+
|
1
3
|
load '../example-app/Clockfile'
|
2
4
|
|
3
5
|
every('2 seconds') do
|
4
6
|
puts Example.count
|
5
7
|
end
|
8
|
+
|
9
|
+
every('2 seconds') do
|
10
|
+
puts Example.check_for_global_method
|
11
|
+
end
|
12
|
+
|
13
|
+
every('2 seconds') do
|
14
|
+
puts Example.check_for_runner
|
15
|
+
end
|
16
|
+
|
17
|
+
every('2 seconds') do
|
18
|
+
RubyClock::Runners.rake 'about'
|
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,72 +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 around_action(&b)
|
45
|
-
RubyClock.instance.around_actions << b
|
46
|
-
end
|
47
|
-
|
48
|
-
def cron(...)
|
49
|
-
RubyClock.instance.schedule.cron(...)
|
50
|
-
end
|
51
|
-
|
52
|
-
def every(...)
|
53
|
-
RubyClock.instance.schedule.every(...)
|
54
|
-
end
|
55
|
-
|
56
|
-
def shell(string)
|
57
|
-
RubyClock.instance.shell(string)
|
58
|
-
end
|
59
|
-
#####################################
|
60
17
|
|
61
|
-
|
62
|
-
load ARGV[0] || 'Clockfile'
|
63
|
-
|
64
|
-
if defined?(::Rails)
|
65
|
-
around_action do |job_proc|
|
66
|
-
::Rails.application.reloader.wrap do
|
67
|
-
job_proc.call
|
68
|
-
end
|
69
|
-
end
|
18
|
+
raise
|
70
19
|
end
|
71
20
|
|
21
|
+
RubyClock.instance.freeze_around_actions
|
72
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,23 +1,26 @@
|
|
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
|
-
attr_accessor :on_error
|
20
|
+
attr_accessor :on_error
|
10
21
|
|
11
22
|
def initialize
|
12
|
-
|
13
|
-
|
14
|
-
def schedule.around_trigger(job_info, &job_proc)
|
15
|
-
RubyClock.instance.call_with_around_action_stack(
|
16
|
-
RubyClock.instance.around_actions.reverse,
|
17
|
-
job_proc,
|
18
|
-
job_info
|
19
|
-
)
|
20
|
-
end
|
23
|
+
set_up_around_actions
|
21
24
|
end
|
22
25
|
|
23
26
|
def wait_seconds
|
@@ -54,91 +57,4 @@ class RubyClock
|
|
54
57
|
schedule.resume
|
55
58
|
schedule.join
|
56
59
|
end
|
57
|
-
|
58
|
-
def prepare_rake
|
59
|
-
if defined?(::Rails) && Rails.application
|
60
|
-
Rails.application.load_tasks
|
61
|
-
Rake::Task.tasks.each{|t| t.prerequisites.delete 'environment' }
|
62
|
-
@rake_mutex = Mutex.new
|
63
|
-
else
|
64
|
-
puts <<~MESSAGE
|
65
|
-
Because this is not a rails application, we do not know how to load your
|
66
|
-
rake tasks. You can do this yourself at the top of your Clockfile if you want
|
67
|
-
to run rake tasks from ruby-clock.
|
68
|
-
MESSAGE
|
69
|
-
end
|
70
|
-
end
|
71
|
-
|
72
|
-
# See https://code.jjb.cc/running-rake-tasks-from-within-ruby-on-rails-code
|
73
|
-
|
74
|
-
# for tasks that don't have dependencies
|
75
|
-
def rake_execute(task)
|
76
|
-
Rake::Task[task].execute
|
77
|
-
end
|
78
|
-
|
79
|
-
# If the task doesn't share dependencies with another task,
|
80
|
-
# or if it does and you know you'll never run tasks such that any overlap
|
81
|
-
def rake_async(task)
|
82
|
-
Rake::Task[task].invoke
|
83
|
-
ensure
|
84
|
-
Rake::Task[task].reenable
|
85
|
-
Rake::Task[task].all_prerequisite_tasks.each(&:reenable)
|
86
|
-
end
|
87
|
-
|
88
|
-
# If the task has shared dependencies and you might run more than one at the same time
|
89
|
-
# This is the safest option and hence the default.
|
90
|
-
def rake(task)
|
91
|
-
@rake_mutex.synchronize { rake_async(task) }
|
92
|
-
end
|
93
|
-
|
94
|
-
def shell_runner
|
95
|
-
@shell_runner ||= begin
|
96
|
-
require 'terrapin'
|
97
|
-
require 'posix-spawn'
|
98
|
-
|
99
|
-
unless Terrapin::CommandLine.runner.class == Terrapin::CommandLine::PosixRunner
|
100
|
-
puts <<~MESSAGE
|
101
|
-
|
102
|
-
🤷 terrapin and posix-spawn are installed, but for some reason terrapin is
|
103
|
-
not using posix-spawn as its runner.
|
104
|
-
|
105
|
-
MESSAGE
|
106
|
-
end
|
107
|
-
|
108
|
-
puts '🐆 Using terrapin for shell commands.'
|
109
|
-
:terrapin
|
110
|
-
rescue LoadError
|
111
|
-
puts <<~MESSAGE
|
112
|
-
|
113
|
-
🦥 Using ruby backticks for shell commands.
|
114
|
-
For better performance, install the terrapin and posix-spawn gems.
|
115
|
-
See README.md for more info.
|
116
|
-
|
117
|
-
MESSAGE
|
118
|
-
:backticks
|
119
|
-
end
|
120
|
-
end
|
121
|
-
|
122
|
-
def shell(command)
|
123
|
-
case shell_runner
|
124
|
-
when :terrapin
|
125
|
-
Terrapin::CommandLine.new(command).run
|
126
|
-
when :backticks
|
127
|
-
`#{command}`
|
128
|
-
end
|
129
|
-
end
|
130
|
-
|
131
|
-
def call_with_around_action_stack(wrappers, job_proc, job_info)
|
132
|
-
case wrappers.count
|
133
|
-
when 0
|
134
|
-
job_proc.call(job_info)
|
135
|
-
else
|
136
|
-
call_with_around_action_stack(
|
137
|
-
wrappers[1..],
|
138
|
-
Proc.new{ wrappers.first.call(job_proc, job_info) },
|
139
|
-
job_info
|
140
|
-
)
|
141
|
-
end
|
142
|
-
end
|
143
|
-
|
144
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
|
+
...
|