sidekiq-cron 1.12.0 → 2.0.0.rc1

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.
@@ -1,22 +1,25 @@
1
1
  <header class='row'>
2
2
  <div class='col-sm-5 pull-left'>
3
- <h3><%= t('CronJobs') %></h3>
3
+ <h3>
4
+ <%= t('CronJobs') %>
5
+ <small><%= @current_namespace %></small>
6
+ </h3>
4
7
  </div>
5
8
  <div class='col-sm-7 pull-right' style="margin-top: 20px; margin-bottom: 10px;">
6
9
  <% if @cron_jobs.size > 0 %>
7
- <form action="<%= root_path %>cron/__all__/delete" method="post" class="pull-right">
10
+ <form action="<%= root_path %>cron/namespaces/<%= @current_namespace %>/all/delete" method="post" class="pull-right">
8
11
  <%= csrf_tag if respond_to?(:csrf_tag) %>
9
12
  <input class="btn btn-danger" type="submit" name="delete" value="<%= t('DeleteAll') %>" data-confirm="<%= t('AreYouSureDeleteCronJobs') %>" />
10
13
  </form>
11
- <form action="<%= root_path %>cron/__all__/disable" method="post" class="pull-right">
14
+ <form action="<%= root_path %>cron/namespaces/<%= @current_namespace %>/all/disable" method="post" class="pull-right">
12
15
  <%= csrf_tag if respond_to?(:csrf_tag) %>
13
16
  <input class="btn btn-warn" type="submit" name="enque" value="<%= t('DisableAll') %>" />
14
17
  </form>
15
- <form action="<%= root_path %>cron/__all__/enable" method="post" class="pull-right">
18
+ <form action="<%= root_path %>cron/namespaces/<%= @current_namespace %>/all/enable" method="post" class="pull-right">
16
19
  <%= csrf_tag if respond_to?(:csrf_tag) %>
17
20
  <input class="btn btn-warn" type="submit" name="enque" value="<%= t('EnableAll') %>" />
18
21
  </form>
19
- <form action="<%= root_path %>cron/__all__/enque" method="post" class="pull-right">
22
+ <form action="<%= root_path %>cron/namespaces/<%= @current_namespace %>/all/enque" method="post" class="pull-right">
20
23
  <%= csrf_tag if respond_to?(:csrf_tag) %>
21
24
  <input class="btn btn-warn" type="submit" name="enque" value="<%= t('EnqueueAll') %>" data-confirm="<%= t('AreYouSureEnqueueCronJobs') %>" />
22
25
  </form>
@@ -24,8 +27,24 @@
24
27
  </div>
25
28
  </header>
26
29
 
27
- <% if @cron_jobs.size > 0 %>
30
+ <!-- Namespaces -->
31
+ <div class='row'>
32
+ <div class="col-sm-12 summary_bar">
33
+ <ul class="list-unstyled summary row">
34
+ <% @namespaces.sort_by { |namespace| namespace[:name] } .each do |namespace| %>
35
+ <li class="col-sm-1">
36
+ <a href="<%= root_path %>cron/namespaces/<%= namespace[:name] %>">
37
+ <span class="count"><%= namespace[:count] %></span>
38
+ <span class="desc"><%= namespace[:name] %></span>
39
+ </a>
40
+ </li>
41
+ <% end %>
42
+ </ul>
43
+ </div>
44
+ </div>
45
+ <!-- Namespaces -->
28
46
 
47
+ <% if @cron_jobs.size > 0 %>
29
48
  <table class="table table-hover table-bordered table-striped table-white">
30
49
  <thead>
31
50
  <th><%= t('Status') %></th>
@@ -41,7 +60,7 @@
41
60
  <tr>
42
61
  <td style="<%= style %>"><%= t job.status %></td>
43
62
  <td style="<%= style %>">
44
- <a href="<%= root_path %>cron/<%= CGI.escape(job.name).gsub('+', '%20') %>">
63
+ <a href="<%= root_path %>cron/namespaces/<%= @current_namespace %>/jobs/<%= CGI.escape(job.name).gsub('+', '%20') %>" title="<%= job.description %>">
45
64
  <b style="<%= style %>"><%= job.name %></b>
46
65
  </a>
47
66
  <hr style="margin:3px;border:0;">
@@ -61,28 +80,28 @@
61
80
  <% end %>
62
81
  </small>
63
82
  </td>
64
- <td style="<%= style %>"><b><%= job.cron.gsub(" ", "&nbsp;") %></b></td>
83
+ <td style="<%= style %>"><b><%= job.human_cron %><br/><small><%= job.cron.gsub(" ", "&nbsp;") %></small></b></td>
65
84
  <td style="<%= style %>"><%= job.last_enqueue_time ? relative_time(job.last_enqueue_time) : "-" %></td>
66
85
  <td style="<%= style %>">
67
86
  <% if job.status == 'enabled' %>
68
- <form action="<%= root_path %>cron/<%= CGI.escape(job.name).gsub('+', '%20') %>/enque" method="post">
87
+ <form action="<%= root_path %>cron/namespaces/<%= @current_namespace %>/jobs/<%= CGI.escape(job.name).gsub('+', '%20') %>/enque" method="post">
69
88
  <%= csrf_tag if respond_to?(:csrf_tag) %>
70
89
  <input class='btn btn-warn btn-xs pull-left' type="submit" name="enque" value="<%= t('EnqueueNow') %>" data-confirm="<%= t('AreYouSureEnqueueCronJob', :job => job.name) %>"/>
71
90
  </form>
72
- <form action="<%= root_path %>cron/<%= CGI.escape(job.name).gsub('+', '%20') %>/disable" method="post">
91
+ <form action="<%= root_path %>cron/namespaces/<%= @current_namespace %>/jobs/<%= CGI.escape(job.name).gsub('+', '%20') %>/disable" method="post">
73
92
  <%= csrf_tag if respond_to?(:csrf_tag) %>
74
93
  <input class='btn btn-warn btn-xs pull-left' type="submit" name="disable" value="<%= t('Disable') %>"/>
75
94
  </form>
76
95
  <% else %>
77
- <form action="<%= root_path %>cron/<%= CGI.escape(job.name).gsub('+', '%20') %>/enque" method="post">
96
+ <form action="<%= root_path %>cron/namespaces/<%= @current_namespace %>/jobs/<%= CGI.escape(job.name).gsub('+', '%20') %>/enque" method="post">
78
97
  <%= csrf_tag if respond_to?(:csrf_tag) %>
79
98
  <input class='btn btn-warn btn-xs pull-left' type="submit" name="enque" value="<%= t('EnqueueNow') %>"/>
80
99
  </form>
81
- <form action="<%= root_path %>cron/<%= CGI.escape(job.name).gsub('+', '%20') %>/enable" method="post">
100
+ <form action="<%= root_path %>cron/namespaces/<%= @current_namespace %>/jobs/<%= CGI.escape(job.name).gsub('+', '%20') %>/enable" method="post">
82
101
  <%= csrf_tag if respond_to?(:csrf_tag) %>
83
102
  <input class='btn btn-warn btn-xs pull-left' type="submit" name="enable" value="<%= t('Enable') %>"/>
84
103
  </form>
85
- <form action="<%= root_path %>cron/<%= CGI.escape(job.name).gsub('+', '%20') %>/delete" method="post">
104
+ <form action="<%= root_path %>cron/namespaces/<%= @current_namespace %>/jobs/<%= CGI.escape(job.name).gsub('+', '%20') %>/delete" method="post">
86
105
  <%= csrf_tag if respond_to?(:csrf_tag) %>
87
106
  <input class='btn btn-xs btn-danger pull-left' type="submit" name="delete" value="<%= t('Delete') %>" data-confirm="<%= t('AreYouSureDeleteCronJob', :job => job.name) %>"/>
88
107
  </form>
@@ -6,8 +6,8 @@
6
6
  </h3>
7
7
  </div>
8
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">
9
+ <% cron_job_path = "#{root_path}cron/namespaces/#{@current_namespace}/jobs/#{CGI.escape(@job.name).gsub('+', '%20')}" %>
10
+ <form action="<%= cron_job_path %>/enque?redirect=<%= cron_job_path %>" class="pull-right" method="post">
11
11
  <%= csrf_tag if respond_to?(:csrf_tag) %>
12
12
  <input class="btn btn-warn pull-left" name="enque" type="submit" value="<%= t('EnqueueNow') %>" data-confirm="<%= t('AreYouSureEnqueueCronJob', :job => @job.name) %>" />
13
13
  </form>
@@ -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>
@@ -6,60 +6,99 @@ module Sidekiq
6
6
 
7
7
  # Index page of cron jobs.
8
8
  app.get '/cron' do
9
- view_path = File.join(File.expand_path("..", __FILE__), "views")
9
+ view_path = File.join(File.expand_path("..", __FILE__), "views")
10
+
11
+ @current_namespace = 'default'
12
+
13
+ @namespaces = Sidekiq::Cron::Namespace.all_with_count
10
14
 
15
+ # Not passing namespace takes all the jobs from the default one.
11
16
  @cron_jobs = Sidekiq::Cron::Job.all
12
17
 
13
18
  render(:erb, File.read(File.join(view_path, "cron.erb")))
14
19
  end
15
20
 
21
+ app.get '/cron/namespaces/:name' do
22
+ view_path = File.join(File.expand_path("..", __FILE__), "views")
23
+
24
+ @current_namespace = route_params[:name]
25
+
26
+ @namespaces = Sidekiq::Cron::Namespace.all_with_count
27
+
28
+ @cron_jobs = Sidekiq::Cron::Job.all(@current_namespace)
29
+
30
+ render(:erb, File.read(File.join(view_path, "cron.erb")))
31
+ end
32
+
16
33
  # Display job detail + jid history.
17
- app.get '/cron/:name' do
34
+ app.get '/cron/namespaces/:namespace/jobs/:name' do
18
35
  view_path = File.join(File.expand_path("..", __FILE__), "views")
19
36
 
20
- @job = Sidekiq::Cron::Job.find(route_params[:name])
37
+ @current_namespace = route_params[:namespace]
38
+ @job_name = route_params[:name]
39
+
40
+ @namespaces = Sidekiq::Cron::Namespace.all_with_count
41
+
42
+ @job = Sidekiq::Cron::Job.find(@job_name, @current_namespace)
43
+
21
44
  if @job
22
45
  render(:erb, File.read(File.join(view_path, "cron_show.erb")))
23
46
  else
24
- redirect "#{root_path}cron"
47
+ redirect "#{root_path}cron/namespaces/#{route_params[:namespace]}"
25
48
  end
26
49
  end
27
50
 
51
+ # Enque all cron jobs.
52
+ app.post '/cron/namespaces/:namespace/all/enque' do
53
+ Sidekiq::Cron::Job.all(route_params[:namespace]).each(&:enque!)
54
+ redirect params['redirect'] || "#{root_path}cron/namespaces/#{route_params[:namespace]}"
55
+ end
56
+
28
57
  # 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])
58
+ app.post '/cron/namespaces/:namespace/jobs/:name/enque' do
59
+ if job = Sidekiq::Cron::Job.find(route_params[:name], route_params[:namespace])
33
60
  job.enque!
34
61
  end
35
62
  redirect params['redirect'] || "#{root_path}cron"
36
63
  end
37
64
 
65
+ # Delete all schedules.
66
+ app.post '/cron/namespaces/:namespace/all/delete' do
67
+ Sidekiq::Cron::Job.all(route_params[:namespace]).each(&:destroy)
68
+ redirect "#{root_path}cron/namespaces/#{route_params[:namespace]}"
69
+ end
70
+
38
71
  # 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])
72
+ app.post '/cron/namespaces/:namespace/jobs/:name/delete' do
73
+ if job = Sidekiq::Cron::Job.find(route_params[:name], route_params[:namespace])
43
74
  job.destroy
44
75
  end
45
76
  redirect "#{root_path}cron"
46
77
  end
47
78
 
79
+ # Enable all jobs.
80
+ app.post '/cron/namespaces/:namespace/all/enable' do
81
+ Sidekiq::Cron::Job.all(route_params[:namespace]).each(&:enable!)
82
+ redirect params['redirect'] || "#{root_path}cron/namespaces/#{route_params[:namespace]}"
83
+ end
84
+
48
85
  # 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])
86
+ app.post '/cron/namespaces/:namespace/jobs/:name/enable' do
87
+ if job = Sidekiq::Cron::Job.find(route_params[:name], route_params[:namespace])
53
88
  job.enable!
54
89
  end
55
90
  redirect params['redirect'] || "#{root_path}cron"
56
91
  end
57
92
 
93
+ # Disable all jobs.
94
+ app.post '/cron/namespaces/:namespace/all/disable' do
95
+ Sidekiq::Cron::Job.all(route_params[:namespace]).each(&:disable!)
96
+ redirect params['redirect'] || "#{root_path}cron/namespaces/#{route_params[:namespace]}"
97
+ end
98
+
58
99
  # 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])
100
+ app.post '/cron/namespaces/:namespace/jobs/:name/disable' do
101
+ if job = Sidekiq::Cron::Job.find(route_params[:name], route_params[:namespace])
63
102
  job.disable!
64
103
  end
65
104
  redirect params['redirect'] || "#{root_path}cron"
data/lib/sidekiq/cron.rb CHANGED
@@ -1,9 +1,53 @@
1
1
  require "sidekiq/cron/job"
2
+ require "sidekiq/cron/namespace"
2
3
  require "sidekiq/cron/poller"
3
4
  require "sidekiq/cron/launcher"
4
5
  require "sidekiq/cron/schedule_loader"
5
6
 
6
7
  module Sidekiq
7
8
  module Cron
9
+ class << self
10
+ attr_accessor :configuration
11
+ end
12
+
13
+ def self.configure
14
+ self.configuration ||= Configuration.new
15
+ yield(configuration) if block_given?
16
+ end
17
+
18
+ class Configuration
19
+ # The default namespace is used when no namespace is specified.
20
+ attr_accessor :default_namespace
21
+
22
+ # The parsing mode when using the natural language cron syntax from the `fugit` gem.
23
+ #
24
+ # :single -- use the first parsed cron line and ignore the rest (default)
25
+ # :strict -- raise an error if multiple cron lines are parsed from one string
26
+ attr_reader :natural_cron_parsing_mode
27
+
28
+ # The poller will not enqueue jobs that are late by more than this amount of seconds.
29
+ # Defaults to 60 seconds.
30
+ #
31
+ # This is useful when sidekiq (and sidekiq-cron) is not used in zero downtime deployments and
32
+ # when the deployment is done and sidekiq-cron starts to catch up, it will consider older
33
+ # jobs that missed their schedules during the deployment. E.g., jobs that run once a day.
34
+ attr_accessor :reschedule_grace_period
35
+
36
+ def initialize
37
+ @default_namespace = 'default'
38
+ @natural_cron_parsing_mode = :single
39
+ @reschedule_grace_period = 60
40
+ end
41
+
42
+ def natural_cron_parsing_mode=(mode)
43
+ unless %i[single strict].include?(mode)
44
+ raise ArgumentError, "Unknown natural cron parsing mode: #{mode.inspect}"
45
+ end
46
+
47
+ @natural_cron_parsing_mode = mode
48
+ end
49
+ end
8
50
  end
9
51
  end
52
+
53
+ Sidekiq::Cron.configure
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("cronex", ">= 0.13.0")
29
30
  s.add_dependency("fugit", "~> 1.8")
30
- s.add_dependency("sidekiq", ">= 6")
31
31
  s.add_dependency("globalid", ">= 1.0.1")
32
+ s.add_dependency("sidekiq", ">= 6")
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.0.0.rc1
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: 2024-07-31 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
@@ -25,33 +39,33 @@ dependencies:
25
39
  - !ruby/object:Gem::Version
26
40
  version: '1.8'
27
41
  - !ruby/object:Gem::Dependency
28
- name: sidekiq
42
+ name: globalid
29
43
  requirement: !ruby/object:Gem::Requirement
30
44
  requirements:
31
45
  - - ">="
32
46
  - !ruby/object:Gem::Version
33
- version: '6'
47
+ version: 1.0.1
34
48
  type: :runtime
35
49
  prerelease: false
36
50
  version_requirements: !ruby/object:Gem::Requirement
37
51
  requirements:
38
52
  - - ">="
39
53
  - !ruby/object:Gem::Version
40
- version: '6'
54
+ version: 1.0.1
41
55
  - !ruby/object:Gem::Dependency
42
- name: globalid
56
+ name: sidekiq
43
57
  requirement: !ruby/object:Gem::Requirement
44
58
  requirements:
45
59
  - - ">="
46
60
  - !ruby/object:Gem::Version
47
- version: 1.0.1
61
+ version: '6'
48
62
  type: :runtime
49
63
  prerelease: false
50
64
  version_requirements: !ruby/object:Gem::Requirement
51
65
  requirements:
52
66
  - - ">="
53
67
  - !ruby/object:Gem::Version
54
- version: 1.0.1
68
+ version: '6'
55
69
  - !ruby/object:Gem::Dependency
56
70
  name: minitest
57
71
  requirement: !ruby/object:Gem::Requirement
@@ -170,11 +184,13 @@ files:
170
184
  - lib/sidekiq/cron/launcher.rb
171
185
  - lib/sidekiq/cron/locales/de.yml
172
186
  - lib/sidekiq/cron/locales/en.yml
187
+ - lib/sidekiq/cron/locales/id.yml
173
188
  - lib/sidekiq/cron/locales/it.yml
174
189
  - lib/sidekiq/cron/locales/ja.yml
175
190
  - lib/sidekiq/cron/locales/pt.yml
176
191
  - lib/sidekiq/cron/locales/ru.yml
177
192
  - lib/sidekiq/cron/locales/zh-CN.yml
193
+ - lib/sidekiq/cron/namespace.rb
178
194
  - lib/sidekiq/cron/poller.rb
179
195
  - lib/sidekiq/cron/schedule_loader.rb
180
196
  - lib/sidekiq/cron/support.rb
@@ -200,9 +216,9 @@ required_ruby_version: !ruby/object:Gem::Requirement
200
216
  version: '2.7'
201
217
  required_rubygems_version: !ruby/object:Gem::Requirement
202
218
  requirements:
203
- - - ">="
219
+ - - ">"
204
220
  - !ruby/object:Gem::Version
205
- version: '0'
221
+ version: 1.3.1
206
222
  requirements: []
207
223
  rubygems_version: 3.4.10
208
224
  signing_key: