sidekiq-cron 1.12.0 → 2.1.0

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.
@@ -5,25 +5,25 @@
5
5
  <small><%= @job.name %></small>
6
6
  </h3>
7
7
  </div>
8
- <div class="span col-sm-7 pull-right" style="margin-top: 20px; margin-bottom: 10px;">
9
- <% cron_job_path = "#{root_path}cron/#{CGI.escape(@job.name).gsub('+', '%20')}" %>
10
- <form action="<%= cron_job_path %>/enque?redirect=<%= cron_job_path %>" method="post">
11
- <%= csrf_tag if respond_to?(:csrf_tag) %>
12
- <input class="btn btn-warn pull-left" name="enque" type="submit" value="<%= t('EnqueueNow') %>" data-confirm="<%= t('AreYouSureEnqueueCronJob', :job => @job.name) %>" />
8
+ <div class="span col-sm-7 pull-right h2">
9
+ <% cron_job_path = "#{root_path}cron/namespaces/#{@current_namespace}/jobs/#{CGI.escape(@job.name).gsub('+', '%20')}" %>
10
+ <form action="<%= cron_job_path %>/enqueue?redirect=<%= cron_job_path %>" class="pull-right" method="post">
11
+ <%= csrf_tag %>
12
+ <input class="btn btn-warn pull-left" name="enqueue" type="submit" value="<%= t('EnqueueNow') %>" data-confirm="<%= t('AreYouSureEnqueueCronJob', :job => @job.name) %>" />
13
13
  </form>
14
14
  <% if @job.status == 'enabled' %>
15
- <form action="<%= cron_job_path %>/disable?redirect=<%= cron_job_path %>" method="post">
16
- <%= csrf_tag if respond_to?(:csrf_tag) %>
15
+ <form action="<%= cron_job_path %>/disable?redirect=<%= cron_job_path %>" class="pull-right" method="post">
16
+ <%= csrf_tag %>
17
17
  <input class="btn btn-warn pull-left" name="disable" type="submit" value="<%= t('Disable') %>" />
18
18
  </form>
19
19
  <% else %>
20
- <form action="<%= cron_job_path %>/enable?redirect=<%= cron_job_path %>" method="post">
21
- <%= csrf_tag if respond_to?(:csrf_tag) %>
20
+ <form action="<%= cron_job_path %>/enable?redirect=<%= cron_job_path %>" class="pull-right" method="post">
21
+ <%= csrf_tag %>
22
22
  <input class="btn btn-warn pull-left" name="enable" type="submit" value="<%= t('Enable') %>" />
23
23
  </form>
24
- <form action="<%= cron_job_path %>/delete" method="post">
25
- <%= csrf_tag if respond_to?(:csrf_tag) %>
26
- <input class="btn btn-danger" data-confirm="<%= t('AreYouSureDeleteCronJob', :job => @job.name) %>" name="delete" type="submit" value="<%= t('Delete') %>" />
24
+ <form action="<%= cron_job_path %>/delete" class="pull-right" method="post">
25
+ <%= csrf_tag %>
26
+ <input class="btn btn-danger pull-left" data-confirm="<%= t('AreYouSureDeleteCronJob', :job => @job.name) %>" name="delete" type="submit" value="<%= t('Delete') %>" />
27
27
  </form>
28
28
  <% end %>
29
29
  </div>
@@ -39,6 +39,10 @@
39
39
  <th><%= t 'Name' %></th>
40
40
  <td><%= @job.name %></td>
41
41
  </tr>
42
+ <tr>
43
+ <th><%= t 'Namespace' %></th>
44
+ <td><%= @job.namespace %></td>
45
+ </tr>
42
46
  <tr>
43
47
  <th><%= t 'Description' %></th>
44
48
  <td><%= @job.description %></td>
@@ -1,7 +1,17 @@
1
1
  require "sidekiq/cron/web_extension"
2
2
  require "sidekiq/cron/job"
3
+ require "sidekiq/cron/namespace"
3
4
 
4
5
  if defined?(Sidekiq::Web)
5
- Sidekiq::Web.register Sidekiq::Cron::WebExtension
6
- Sidekiq::Web.tabs["Cron"] = "cron"
6
+ if Gem::Version.new(Sidekiq::VERSION) >= Gem::Version.new('7.3.0')
7
+ Sidekiq::Web.register(
8
+ Sidekiq::Cron::WebExtension, # Class which contains the HTTP actions, required
9
+ name: "cron", # the name of the extension, used to namespace assets
10
+ tab: "Cron", # labels(s) of the UI tabs
11
+ index: "cron", # index route(s) for each tab
12
+ )
13
+ else
14
+ Sidekiq::Web.register Sidekiq::Cron::WebExtension
15
+ Sidekiq::Web.tabs["Cron"] = "cron"
16
+ end
7
17
  end
@@ -4,65 +4,112 @@ module Sidekiq
4
4
  def self.registered(app)
5
5
  app.settings.locales << File.join(File.expand_path("..", __FILE__), "locales")
6
6
 
7
- # Index page of cron jobs.
7
+ app.helpers do
8
+ # This method constructs the URL for the cron jobs page within the specified namespace.
9
+ def namespace_redirect_path
10
+ "#{root_path}cron/namespaces/#{route_params[:namespace]}"
11
+ end
12
+
13
+ def redirect_to_previous_or_default
14
+ redirect params['redirect'] || namespace_redirect_path
15
+ end
16
+
17
+ def render_erb(view)
18
+ views_path = File.join(File.expand_path("..", __FILE__), "views")
19
+ erb(File.read(File.join(views_path, "#{view}.erb")))
20
+ end
21
+ end
22
+
23
+ # Index page.
8
24
  app.get '/cron' do
9
- view_path = File.join(File.expand_path("..", __FILE__), "views")
25
+ @current_namespace = 'default'
26
+ @cron_jobs = Sidekiq::Cron::Job.all(@current_namespace)
10
27
 
11
- @cron_jobs = Sidekiq::Cron::Job.all
28
+ render_erb(:cron)
29
+ end
30
+
31
+ # Detail page for a specific namespace.
32
+ app.get '/cron/namespaces/:name' do
33
+ @current_namespace = route_params[:name]
34
+ @cron_jobs = Sidekiq::Cron::Job.all(@current_namespace)
12
35
 
13
- render(:erb, File.read(File.join(view_path, "cron.erb")))
36
+ render_erb(:cron)
14
37
  end
15
38
 
16
39
  # Display job detail + jid history.
17
- app.get '/cron/:name' do
18
- view_path = File.join(File.expand_path("..", __FILE__), "views")
40
+ app.get '/cron/namespaces/:namespace/jobs/:name' do
41
+ @current_namespace = route_params[:namespace]
42
+ @job = Sidekiq::Cron::Job.find(route_params[:name], @current_namespace)
19
43
 
20
- @job = Sidekiq::Cron::Job.find(route_params[:name])
21
44
  if @job
22
- render(:erb, File.read(File.join(view_path, "cron_show.erb")))
45
+ render_erb(:cron_show)
23
46
  else
24
- redirect "#{root_path}cron"
47
+ redirect namespace_redirect_path
25
48
  end
26
49
  end
27
50
 
51
+ # Enqueue all cron jobs.
52
+ app.post '/cron/namespaces/:namespace/all/enqueue' do
53
+ Sidekiq::Cron::Job.all(route_params[:namespace]).each(&:enqueue!)
54
+
55
+ redirect_to_previous_or_default
56
+ end
57
+
28
58
  # Enqueue cron job.
29
- app.post '/cron/:name/enque' do
30
- if route_params[:name] === '__all__'
31
- Sidekiq::Cron::Job.all.each(&:enque!)
32
- elsif job = Sidekiq::Cron::Job.find(route_params[:name])
33
- job.enque!
59
+ app.post '/cron/namespaces/:namespace/jobs/:name/enqueue' do
60
+ if job = Sidekiq::Cron::Job.find(route_params[:name], route_params[:namespace])
61
+ job.enqueue!
34
62
  end
35
- redirect params['redirect'] || "#{root_path}cron"
63
+
64
+ redirect_to_previous_or_default
65
+ end
66
+
67
+ # Delete all schedules.
68
+ app.post '/cron/namespaces/:namespace/all/delete' do
69
+ Sidekiq::Cron::Job.all(route_params[:namespace]).each(&:destroy)
70
+
71
+ redirect_to_previous_or_default
36
72
  end
37
73
 
38
74
  # Delete schedule.
39
- app.post '/cron/:name/delete' do
40
- if route_params[:name] === '__all__'
41
- Sidekiq::Cron::Job.all.each(&:destroy)
42
- elsif job = Sidekiq::Cron::Job.find(route_params[:name])
75
+ app.post '/cron/namespaces/:namespace/jobs/:name/delete' do
76
+ if job = Sidekiq::Cron::Job.find(route_params[:name], route_params[:namespace])
43
77
  job.destroy
44
78
  end
45
- redirect "#{root_path}cron"
79
+
80
+ redirect_to_previous_or_default
81
+ end
82
+
83
+ # Enable all jobs.
84
+ app.post '/cron/namespaces/:namespace/all/enable' do
85
+ Sidekiq::Cron::Job.all(route_params[:namespace]).each(&:enable!)
86
+
87
+ redirect_to_previous_or_default
46
88
  end
47
89
 
48
90
  # Enable job.
49
- app.post '/cron/:name/enable' do
50
- if route_params[:name] === '__all__'
51
- Sidekiq::Cron::Job.all.each(&:enable!)
52
- elsif job = Sidekiq::Cron::Job.find(route_params[:name])
91
+ app.post '/cron/namespaces/:namespace/jobs/:name/enable' do
92
+ if job = Sidekiq::Cron::Job.find(route_params[:name], route_params[:namespace])
53
93
  job.enable!
54
94
  end
55
- redirect params['redirect'] || "#{root_path}cron"
95
+
96
+ redirect_to_previous_or_default
97
+ end
98
+
99
+ # Disable all jobs.
100
+ app.post '/cron/namespaces/:namespace/all/disable' do
101
+ Sidekiq::Cron::Job.all(route_params[:namespace]).each(&:disable!)
102
+
103
+ redirect_to_previous_or_default
56
104
  end
57
105
 
58
106
  # Disable job.
59
- app.post '/cron/:name/disable' do
60
- if route_params[:name] === '__all__'
61
- Sidekiq::Cron::Job.all.each(&:disable!)
62
- elsif job = Sidekiq::Cron::Job.find(route_params[:name])
107
+ app.post '/cron/namespaces/:namespace/jobs/:name/disable' do
108
+ if job = Sidekiq::Cron::Job.find(route_params[:name], route_params[:namespace])
63
109
  job.disable!
64
110
  end
65
- redirect params['redirect'] || "#{root_path}cron"
111
+
112
+ redirect_to_previous_or_default
66
113
  end
67
114
  end
68
115
  end
data/lib/sidekiq/cron.rb CHANGED
@@ -1,9 +1,77 @@
1
- require "sidekiq/cron/job"
2
- require "sidekiq/cron/poller"
3
- require "sidekiq/cron/launcher"
4
- require "sidekiq/cron/schedule_loader"
5
-
6
1
  module Sidekiq
7
2
  module Cron
3
+ class << self
4
+ attr_accessor :configuration
5
+ end
6
+
7
+ def self.configure
8
+ self.configuration ||= Configuration.new
9
+ yield(configuration) if block_given?
10
+ end
11
+
12
+ def self.reset!
13
+ self.configuration = Configuration.new
14
+ end
15
+
16
+ class Configuration
17
+ # The interval, in seconds, at which to poll for scheduled cron jobs.
18
+ # This determines how frequently the scheduler checks for jobs to enqueue.
19
+ attr_accessor :cron_poll_interval
20
+
21
+ # The path to a YAML file containing multiple cron job schedules.
22
+ attr_accessor :cron_schedule_file
23
+
24
+ # The maximum number of recent cron job execution histories to retain.
25
+ # This value controls how many past job executions are stored.
26
+ attr_accessor :cron_history_size
27
+
28
+ # The default namespace is used when no namespace is specified.
29
+ attr_accessor :default_namespace
30
+
31
+ # The parsing mode when using the natural language cron syntax from the `fugit` gem.
32
+ #
33
+ # :single -- use the first parsed cron line and ignore the rest (default)
34
+ # :strict -- raise an error if multiple cron lines are parsed from one string
35
+ attr_reader :natural_cron_parsing_mode
36
+
37
+ # The poller will not enqueue jobs that are late by more than this amount of seconds.
38
+ # Defaults to 60 seconds.
39
+ #
40
+ # This is useful when Sidekiq (and Sidekiq-Cron) is not used in zero downtime deployments and
41
+ # when the deployment is done and Sidekiq-Cron starts to catch up, it will consider older
42
+ # jobs that missed their schedules during the deployment. E.g., jobs that run once a day.
43
+ attr_accessor :reschedule_grace_period
44
+
45
+ # List of available namespaces
46
+ #
47
+ # If not set, Sidekiq Cron will dynamically fetch available namespaces
48
+ # by retrieving existing jobs from Redis.
49
+ #
50
+ # This dynamic fetching can negatively impact performance in certain cases.
51
+ # To mitigate this, you can provide the list of namespaces explicitly.
52
+ # If a job specifies a namespace that is not included in the provided list,
53
+ # a warning will be logged, and the job will be assigned to the default namespace.
54
+ attr_accessor :available_namespaces
55
+
56
+ def initialize
57
+ @cron_poll_interval = 30
58
+ @cron_schedule_file = 'config/schedule.yml'
59
+ @cron_history_size = 10
60
+ @default_namespace = 'default'
61
+ @natural_cron_parsing_mode = :single
62
+ @reschedule_grace_period = 60
63
+ @available_namespaces = nil
64
+ end
65
+
66
+ def natural_cron_parsing_mode=(mode)
67
+ unless %i[single strict].include?(mode)
68
+ raise ArgumentError, "Unknown natural cron parsing mode: #{mode.inspect}"
69
+ end
70
+
71
+ @natural_cron_parsing_mode = mode
72
+ end
73
+ end
8
74
  end
9
75
  end
76
+
77
+ Sidekiq::Cron.configure
@@ -1,5 +1,4 @@
1
- require 'sidekiq'
2
-
1
+ # Module to access Sidekiq config
3
2
  module Sidekiq
4
3
  module Options
5
4
  def self.[](key)
@@ -16,13 +15,12 @@ module Sidekiq
16
15
 
17
16
  def self.options_field
18
17
  return @options_field unless @options_field.nil?
18
+
19
19
  sidekiq_version = Gem::Version.new(Sidekiq::VERSION)
20
20
  @options_field = if sidekiq_version >= Gem::Version.new('7.0')
21
21
  :default_configuration
22
- elsif sidekiq_version >= Gem::Version.new('6.5')
23
- false
24
22
  else
25
- :options
23
+ false
26
24
  end
27
25
  end
28
26
  end
data/lib/sidekiq-cron.rb CHANGED
@@ -1,2 +1,8 @@
1
1
  require "sidekiq"
2
2
  require "sidekiq/cron"
3
+ require "sidekiq/options"
4
+ require "sidekiq/cron/job"
5
+ require "sidekiq/cron/namespace"
6
+ require "sidekiq/cron/poller"
7
+ require "sidekiq/cron/launcher"
8
+ require "sidekiq/cron/schedule_loader"
data/sidekiq-cron.gemspec CHANGED
@@ -26,9 +26,10 @@ Gem::Specification.new do |s|
26
26
 
27
27
  s.required_ruby_version = ">= 2.7"
28
28
 
29
- s.add_dependency("fugit", "~> 1.8")
30
- s.add_dependency("sidekiq", ">= 6")
29
+ s.add_dependency("cronex", ">= 0.13.0")
30
+ s.add_dependency("fugit", "~> 1.8", ">= 1.11.1")
31
31
  s.add_dependency("globalid", ">= 1.0.1")
32
+ s.add_dependency("sidekiq", ">= 6.5.0")
32
33
 
33
34
  s.add_development_dependency("minitest", "~> 5.15")
34
35
  s.add_development_dependency("mocha", "~> 2.1")
metadata CHANGED
@@ -1,15 +1,29 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: sidekiq-cron
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.12.0
4
+ version: 2.1.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Ondrej Bartas
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2023-12-08 00:00:00.000000000 Z
11
+ date: 2025-01-20 00:00:00.000000000 Z
12
12
  dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: cronex
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - ">="
18
+ - !ruby/object:Gem::Version
19
+ version: 0.13.0
20
+ type: :runtime
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - ">="
25
+ - !ruby/object:Gem::Version
26
+ version: 0.13.0
13
27
  - !ruby/object:Gem::Dependency
14
28
  name: fugit
15
29
  requirement: !ruby/object:Gem::Requirement
@@ -17,6 +31,9 @@ dependencies:
17
31
  - - "~>"
18
32
  - !ruby/object:Gem::Version
19
33
  version: '1.8'
34
+ - - ">="
35
+ - !ruby/object:Gem::Version
36
+ version: 1.11.1
20
37
  type: :runtime
21
38
  prerelease: false
22
39
  version_requirements: !ruby/object:Gem::Requirement
@@ -24,34 +41,37 @@ dependencies:
24
41
  - - "~>"
25
42
  - !ruby/object:Gem::Version
26
43
  version: '1.8'
44
+ - - ">="
45
+ - !ruby/object:Gem::Version
46
+ version: 1.11.1
27
47
  - !ruby/object:Gem::Dependency
28
- name: sidekiq
48
+ name: globalid
29
49
  requirement: !ruby/object:Gem::Requirement
30
50
  requirements:
31
51
  - - ">="
32
52
  - !ruby/object:Gem::Version
33
- version: '6'
53
+ version: 1.0.1
34
54
  type: :runtime
35
55
  prerelease: false
36
56
  version_requirements: !ruby/object:Gem::Requirement
37
57
  requirements:
38
58
  - - ">="
39
59
  - !ruby/object:Gem::Version
40
- version: '6'
60
+ version: 1.0.1
41
61
  - !ruby/object:Gem::Dependency
42
- name: globalid
62
+ name: sidekiq
43
63
  requirement: !ruby/object:Gem::Requirement
44
64
  requirements:
45
65
  - - ">="
46
66
  - !ruby/object:Gem::Version
47
- version: 1.0.1
67
+ version: 6.5.0
48
68
  type: :runtime
49
69
  prerelease: false
50
70
  version_requirements: !ruby/object:Gem::Requirement
51
71
  requirements:
52
72
  - - ">="
53
73
  - !ruby/object:Gem::Version
54
- version: 1.0.1
74
+ version: 6.5.0
55
75
  - !ruby/object:Gem::Dependency
56
76
  name: minitest
57
77
  requirement: !ruby/object:Gem::Requirement
@@ -170,11 +190,14 @@ files:
170
190
  - lib/sidekiq/cron/launcher.rb
171
191
  - lib/sidekiq/cron/locales/de.yml
172
192
  - lib/sidekiq/cron/locales/en.yml
193
+ - lib/sidekiq/cron/locales/es.yml
194
+ - lib/sidekiq/cron/locales/id.yml
173
195
  - lib/sidekiq/cron/locales/it.yml
174
196
  - lib/sidekiq/cron/locales/ja.yml
175
197
  - lib/sidekiq/cron/locales/pt.yml
176
198
  - lib/sidekiq/cron/locales/ru.yml
177
199
  - lib/sidekiq/cron/locales/zh-CN.yml
200
+ - lib/sidekiq/cron/namespace.rb
178
201
  - lib/sidekiq/cron/poller.rb
179
202
  - lib/sidekiq/cron/schedule_loader.rb
180
203
  - lib/sidekiq/cron/support.rb