jobtick 0.1.1 → 0.1.3

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: ef3818e1dd990c8e9eb1a46b5ed83dcd91fa62e7487adecc999173a855de990a
4
- data.tar.gz: 1f5fa1a374a40d710d6ba193e46b0adef866546ec9f4e7b192d4801040c9975f
3
+ metadata.gz: 9fbf13c9887fe65ec082ad5a3515ec3d2767330318e9fbeafca2731f6ea7e1f3
4
+ data.tar.gz: 7c1624eaa666bdec4318d087f7d4bd264f6abbe03091ad9746d88a001f2e1bf2
5
5
  SHA512:
6
- metadata.gz: af465b7b4d392967a684e066c018d8f288b032a22590aa37f66e332b293fb0fb9c2964dc6e43ec5513b89d7e99150542cb0c475fd48b1dfe1dc25c582bd65dcd
7
- data.tar.gz: 1c42f458a2ba090e5afca3de65dba113e0fdf1c0db466fe411b97cdc068cba45a65c913bacce13bfa05ebe4724fd001f8fee9568fbd72da2490f0714f5c3e45d
6
+ metadata.gz: 7b1b71c36e97bd70144c69118d7af1af461424529fc92bfa7926bc2289fcecd65e92ad7ac49652dcd413244e4258dbdf9087424dd44ee5b392909fb4be771f8d
7
+ data.tar.gz: 7f32abaf56da135c05d76dd5f1b77043d29bf6f99c919059b138367a1e21abd06460eaa5496f42655026e23f17f740b044e6c46f249849cf27fde2f9c46deea5
data/README.md CHANGED
@@ -61,21 +61,6 @@ No changes to individual job files. No manual monitor creation. No names to keep
61
61
 
62
62
  ## What gets monitored
63
63
 
64
- ### Whenever (`config/schedule.rb`)
65
-
66
- ```ruby
67
- # Your existing schedule.rb — no changes needed
68
- every 1.day, at: '2:00 am' do
69
- runner 'InvoiceJob.perform_later'
70
- end
71
-
72
- every :hour do
73
- runner 'SyncInventoryJob.perform_later'
74
- end
75
- ```
76
-
77
- JobTick reads this file at deploy time and creates a monitor for each job automatically.
78
-
79
64
  ### Solid Queue (`config/recurring.yml`)
80
65
 
81
66
  ```yaml
@@ -89,12 +74,17 @@ sync_exchange_rates:
89
74
  schedule: every hour
90
75
  ```
91
76
 
92
- Each entry becomes a monitor. If `NightlyReportJob` doesn't run within its expected window, you get an alert.
77
+ At boot, JobTick reads this file and registers a monitor for each entry. It then installs an `around_perform` hook into `ActiveJob::Base` so every job execution automatically sends `started`, `completed`, and `failed` pings. No changes to your job files.
93
78
 
94
79
  ### Sidekiq periodic jobs
95
80
 
81
+ Supports both **sidekiq-cron** and **Sidekiq::Periodic**:
82
+
96
83
  ```ruby
97
- # Your existing Sidekiq config no changes needed
84
+ # sidekiq-cron loaded from config/sidekiq_cron.yml or in an initializer
85
+ Sidekiq::Cron::Job.create(name: 'Nightly Cleanup', cron: '0 2 * * *', class: 'NightlyCleanupWorker')
86
+
87
+ # Sidekiq::Periodic
98
88
  Sidekiq.configure_server do |config|
99
89
  config.periodic do |mgr|
100
90
  mgr.register('0 * * * *', HourlyDigestWorker)
@@ -103,6 +93,30 @@ Sidekiq.configure_server do |config|
103
93
  end
104
94
  ```
105
95
 
96
+ JobTick installs a server middleware that wraps every job execution. For native Sidekiq workers (`Sidekiq::Worker` subclasses) pings are sent by the middleware. For Active Job workers dispatched through Sidekiq, pings are sent by the `around_perform` hook instead, so nothing is double-counted.
97
+
98
+ ### Whenever (`config/schedule.rb`)
99
+
100
+ Whenever schedules jobs as cron shell commands, so there is no Ruby hook point to instrument automatically. One setup step is required: run the generator and update your `config/schedule.rb` to use the `jobtick_runner` and `jobtick_rake` job types instead of the built-in `runner` and `rake`:
101
+
102
+ ```
103
+ bundle exec rake jobtick:whenever:setup
104
+ ```
105
+
106
+ This prints the two `job_type` definitions to add to the top of your schedule file, then use them in place of the standard types:
107
+
108
+ ```ruby
109
+ every 1.day, at: '2:00 am' do
110
+ jobtick_runner 'InvoiceJob.perform_later', monitor_key: 'whenever.invoice_job'
111
+ end
112
+
113
+ every :hour do
114
+ jobtick_rake 'reports:sync', monitor_key: 'whenever.reports_sync'
115
+ end
116
+ ```
117
+
118
+ The job types wrap execution with `curl` pings to the JobTick API, so no changes to individual job classes are needed.
119
+
106
120
  ---
107
121
 
108
122
  ## What you get
@@ -121,7 +135,7 @@ end
121
135
 
122
136
  ## Requirements
123
137
 
124
- - Ruby >= 3.2
138
+ - Ruby >= 3.3
125
139
  - Rails >= 7.0
126
140
  - One or more of: Whenever, Solid Queue, Sidekiq
127
141
 
@@ -0,0 +1,16 @@
1
+ # frozen_string_literal: true
2
+
3
+ module JobTick
4
+ module Hooks
5
+ module ActiveJob
6
+ def self.included(base)
7
+ base.around_perform do |job, block|
8
+ key = JobTick.monitor_key_for(job.class.name)
9
+ next block.call unless key
10
+
11
+ JobTick::Monitor.run(key) { block.call }
12
+ end
13
+ end
14
+ end
15
+ end
16
+ end
@@ -0,0 +1,25 @@
1
+ # frozen_string_literal: true
2
+
3
+ module JobTick
4
+ module Middleware
5
+ class Sidekiq
6
+ def call(_worker, job, _queue, &)
7
+ # Active Job wrappers are handled by the around_perform hook
8
+ return yield if job["wrapped"]
9
+
10
+ key = JobTick.monitor_key_for(job["class"])
11
+ return yield unless key
12
+
13
+ JobTick::Monitor.run(key, &)
14
+ end
15
+
16
+ def self.install
17
+ ::Sidekiq.configure_server do |config|
18
+ config.server_middleware do |chain|
19
+ chain.add(JobTick::Middleware::Sidekiq)
20
+ end
21
+ end
22
+ end
23
+ end
24
+ end
25
+ end
@@ -2,6 +2,10 @@
2
2
 
3
3
  module JobTick
4
4
  module Parsers
5
+ def self.slugify(str)
6
+ str.downcase.gsub(/[^a-z0-9]+/, "_").gsub(/\A_+|_+\z/, "")
7
+ end
8
+
5
9
  class Sidekiq
6
10
  def self.parse
7
11
  return [] unless defined?(::Sidekiq)
@@ -16,7 +20,7 @@ module JobTick
16
20
 
17
21
  def self.parse_cron_jobs
18
22
  ::Sidekiq::Cron::Job.all.map do |job|
19
- { key: "sidekiq.#{slugify(job.name)}", schedule: job.cron, source: "sidekiq", task: job.klass }
23
+ { key: "sidekiq.#{Parsers.slugify(job.name)}", schedule: job.cron, source: "sidekiq", task: job.klass }
20
24
  end
21
25
  end
22
26
  private_class_method :parse_cron_jobs
@@ -24,17 +28,12 @@ module JobTick
24
28
  def self.parse_periodic_jobs
25
29
  periodic = sidekiq_periodic_config
26
30
  (periodic || []).map do |klass, opts|
27
- { key: "sidekiq.#{slugify(klass.to_s)}", schedule: opts[:cron] || opts[:every].to_s,
31
+ { key: "sidekiq.#{Parsers.slugify(klass.to_s)}", schedule: opts[:cron] || opts[:every].to_s,
28
32
  source: "sidekiq", task: klass.to_s }
29
33
  end
30
34
  end
31
35
  private_class_method :parse_periodic_jobs
32
36
 
33
- def self.slugify(str)
34
- str.downcase.gsub(/[^a-z0-9]+/, "_").gsub(/\A_+|_+\z/, "")
35
- end
36
- private_class_method :slugify
37
-
38
37
  def self.sidekiq_periodic_config
39
38
  if ::Sidekiq.respond_to?(:default_configuration)
40
39
  ::Sidekiq.default_configuration[:periodic]
@@ -26,9 +26,7 @@ module JobTick
26
26
  end
27
27
 
28
28
  def self.job_key(job)
29
- task = job[:task].to_s.strip
30
- slug = task.downcase.gsub(/[^a-z0-9]+/, "_").gsub(/\A_+|_+\z/, "")
31
- "whenever.#{slug}"
29
+ "whenever.#{Parsers.slugify(job[:task].to_s.strip)}"
32
30
  end
33
31
  private_class_method :job_key
34
32
  end
@@ -7,6 +7,9 @@ module JobTick
7
7
  next unless JobTick.config.enabled
8
8
 
9
9
  JobTick::Registry.sync
10
+
11
+ ::ActiveJob::Base.include(JobTick::Hooks::ActiveJob) if defined?(::ActiveJob::Base)
12
+ JobTick::Middleware::Sidekiq.install if defined?(::Sidekiq)
10
13
  end
11
14
  end
12
15
 
@@ -9,6 +9,10 @@ module JobTick
9
9
  Parsers::Sidekiq.parse
10
10
  ].flatten.compact
11
11
 
12
+ JobTick.monitor_map = monitors.each_with_object({}) do |m, map|
13
+ map[m[:task]] = m[:key] if m[:task]
14
+ end
15
+
12
16
  return [] if monitors.empty?
13
17
 
14
18
  app_name = Rails.application.class.module_parent_name if defined?(Rails)
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module JobTick
4
- VERSION = "0.1.1"
4
+ VERSION = "0.1.3"
5
5
  end
data/lib/jobtick.rb CHANGED
@@ -9,6 +9,8 @@ require_relative "jobtick/parsers/whenever"
9
9
  require_relative "jobtick/parsers/solid_queue"
10
10
  require_relative "jobtick/parsers/sidekiq"
11
11
  require_relative "jobtick/registry"
12
+ require_relative "jobtick/hooks/active_job"
13
+ require_relative "jobtick/middleware/sidekiq"
12
14
  require_relative "jobtick/railtie" if defined?(Rails::Railtie)
13
15
 
14
16
  module JobTick
@@ -31,9 +33,20 @@ module JobTick
31
33
  defined?(Rails) ? Rails.logger : Logger.new($stdout)
32
34
  end
33
35
 
36
+ def monitor_map
37
+ @monitor_map ||= {}
38
+ end
39
+
40
+ attr_writer :monitor_map
41
+
42
+ def monitor_key_for(class_name)
43
+ monitor_map[class_name]
44
+ end
45
+
34
46
  def reset!
35
47
  @config = nil
36
48
  @client = nil
49
+ @monitor_map = {}
37
50
  end
38
51
  end
39
52
  end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: jobtick
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.1.1
4
+ version: 0.1.3
5
5
  platform: ruby
6
6
  authors:
7
7
  - Clearstack Labs
@@ -39,6 +39,8 @@ files:
39
39
  - lib/jobtick.rb
40
40
  - lib/jobtick/client.rb
41
41
  - lib/jobtick/configuration.rb
42
+ - lib/jobtick/hooks/active_job.rb
43
+ - lib/jobtick/middleware/sidekiq.rb
42
44
  - lib/jobtick/monitor.rb
43
45
  - lib/jobtick/parsers/sidekiq.rb
44
46
  - lib/jobtick/parsers/solid_queue.rb