sidekiq-tasks 0.1.0 → 0.1.2

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: c2e154e5c7dce24a8e35fe82c1bf604aecdceb88bcdc09949bee07b250666044
4
- data.tar.gz: 79f0abe2f5d49378ab319b8c4889e35603773616a3c83e4acc12c16a3ad5bf5d
3
+ metadata.gz: 66094bc7c16bfcdf4a54af810f3cc6b277ec26f16c45644bd87d234fcf5108fa
4
+ data.tar.gz: 5f7858178970a54368b222deabeb83221015bd2a7ff7d591d0368f4cda4366cb
5
5
  SHA512:
6
- metadata.gz: 46a46f7d5fe0aad48100eb29756bdf9161a6516716a2f0c2e9bfc9ee5d9ffb054ff9cd7c4d19c88032f30fcef2b4eb2253f231ebc752769b54ccd5d9b212e873
7
- data.tar.gz: 3381e43c02d4b3c7766ce818fa5b3a8a0272e1a605c2f404fc7ac758b09a420833c4fe90fa83047e9acfdf07568d0609d4168dfc9ffec1b28c5a9705a355ac9f
6
+ metadata.gz: 248e0b836f29e9145fd146907cb59c53d59d54ea3dcca8359368d93e9841d94108b418b7f2af23127f2ee5e9d4679c700ecab8a962b90d4fe7261d1178333912
7
+ data.tar.gz: 0024c156a0b2c334dc7c1dbd0fc235a1a98e33ccb838f1f28a118c13644f9feb00b6693941137ad746cb7ad7c98b419d972dcb8a54a50320e9395f8c5b7f4ff7
data/.rubocop.yml CHANGED
@@ -6,6 +6,10 @@ AllCops:
6
6
  Layout/SpaceInsideHashLiteralBraces:
7
7
  EnforcedStyle: no_space
8
8
 
9
+ Layout/LineLength:
10
+ Exclude:
11
+ - spec/**/*
12
+
9
13
  Metrics/AbcSize:
10
14
  Exclude:
11
15
  - lib/sidekiq/tasks/web/extension.rb
@@ -23,6 +27,10 @@ Metrics/ParameterLists:
23
27
  Exclude:
24
28
  - spec/**/*
25
29
 
30
+ Metrics/PerceivedComplexity:
31
+ Exclude:
32
+ - lib/sidekiq/tasks/web/extension.rb
33
+
26
34
  Naming/FileName:
27
35
  Exclude:
28
36
  - lib/sidekiq/sidekiq-tasks.rb
@@ -57,3 +65,6 @@ Style/TrailingCommaInArrayLiteral:
57
65
 
58
66
  Style/TrailingCommaInHashLiteral:
59
67
  EnforcedStyleForMultiline: consistent_comma
68
+
69
+ Style/WordArray:
70
+ Enabled: false
data/CHANGELOG.md CHANGED
@@ -1,5 +1,13 @@
1
1
  ## Changelog
2
2
 
3
+ ### [0.1.2] - 2025-03-22
4
+
5
+ - Update plugin registration for Sidekiq 7.3+ compatibility.
6
+
7
+ ### [0.1.1] - 2025-02-25
8
+
9
+ - Add an environment confirmation input to the task enqueuing form.
10
+
3
11
  ### [0.1.0] - 2025-02-23
4
12
 
5
- - Initial release
13
+ - Initial release.
data/docs/task.png CHANGED
Binary file
@@ -1,6 +1,10 @@
1
1
  module Sidekiq
2
2
  module Tasks
3
3
  class Set
4
+ extend Forwardable
5
+
6
+ def_delegators :objects, :[], :each, :size, :first, :last, :empty?
7
+
4
8
  include Enumerable
5
9
 
6
10
  def self.match?(object, attributes)
@@ -15,10 +19,6 @@ module Sidekiq
15
19
  @objects = objects
16
20
  end
17
21
 
18
- def each(&block)
19
- objects.each(&block)
20
- end
21
-
22
22
  def where(attributes = {})
23
23
  reflect(objects.select { |object| self.class.match?(object, attributes) })
24
24
  end
@@ -31,22 +31,6 @@ module Sidekiq
31
31
  find_by(name: name) || raise(NotFoundError, "'#{name}' not found")
32
32
  end
33
33
 
34
- def size
35
- objects.size
36
- end
37
-
38
- def first
39
- objects[0]
40
- end
41
-
42
- def last
43
- objects[-1]
44
- end
45
-
46
- def empty?
47
- objects.empty?
48
- end
49
-
50
34
  private
51
35
 
52
36
  def reflect(objects)
@@ -2,6 +2,6 @@
2
2
 
3
3
  module Sidekiq
4
4
  module Tasks
5
- VERSION = "0.1.0"
5
+ VERSION = "0.1.2"
6
6
  end
7
7
  end
@@ -1,33 +1,39 @@
1
+ require "sidekiq/tasks/web/helpers/application_helper"
2
+ require "sidekiq/tasks/web/helpers/task_helper"
3
+ require "sidekiq/tasks/web/helpers/pagination_helper"
4
+ require "sidekiq/tasks/web/search"
5
+ require "sidekiq/tasks/web/pagination"
6
+ require "sidekiq/tasks/web/params"
7
+
1
8
  module Sidekiq
2
9
  module Tasks
3
10
  module Web
4
11
  class Extension
5
- LOCALES_PATH = File.expand_path("../web/locales", __dir__).freeze
6
-
7
12
  def self.registered(app)
8
- app.settings.locales << File.join(LOCALES_PATH)
9
-
10
- app.helpers do
11
- include Sidekiq::Tasks::Web::Helpers::ApplicationHelper
12
- include Sidekiq::Tasks::Web::Helpers::TaskHelper
13
- end
13
+ app.helpers(Sidekiq::Tasks::Web::Helpers::ApplicationHelper)
14
+ app.helpers(Sidekiq::Tasks::Web::Helpers::TaskHelper)
15
+ app.helpers(Sidekiq::Tasks::Web::Helpers::PaginationHelper)
14
16
 
15
17
  app.get "/tasks" do
16
- @search = Sidekiq::Tasks::Web::Search.new(params)
18
+ @search = Sidekiq::Tasks::Web::Search.new(params.transform_keys(&:to_sym))
17
19
 
18
20
  erb(read_view(:tasks), locals: {search: @search})
19
21
  end
20
22
 
21
23
  app.get "/tasks/:name" do
22
- @task = find_task!(params["name"])
24
+ @task = find_task!(env["rack.route_params"][:name])
23
25
 
24
- erb(read_view(:_task), locals: {task: @task})
26
+ erb(read_view(:task), locals: {task: @task})
25
27
  rescue Sidekiq::Tasks::NotFoundError
26
28
  throw :halt, [404, {Rack::CONTENT_TYPE => "text/plain"}, ["Task not found"]]
27
29
  end
28
30
 
29
31
  app.post "/tasks/:name/enqueue" do
30
- task = find_task!(params["name"])
32
+ if params["env_confirmation"] != current_env
33
+ throw :halt, [400, {Rack::CONTENT_TYPE => "text/plain"}, ["Invalid confirm"]]
34
+ end
35
+
36
+ task = find_task!(env["rack.route_params"][:name])
31
37
  args = Sidekiq::Tasks::Web::Params.new(task, params["args"]).permit!
32
38
 
33
39
  task.enqueue(args)
@@ -5,10 +5,12 @@ module Sidekiq
5
5
  module ApplicationHelper
6
6
  extend self
7
7
 
8
- VIEW_PATH = File.expand_path("../../web/views", __dir__).freeze
9
-
10
8
  def read_view(name)
11
- File.read(File.join(VIEW_PATH, "#{name}.html.erb"))
9
+ File.read(File.join(Sidekiq::Tasks::Web::ROOT, "views", "#{name}.erb"))
10
+ end
11
+
12
+ def current_env
13
+ ENV["RAILS_ENV"] || ENV["RACK_ENV"]
12
14
  end
13
15
  end
14
16
  end
@@ -0,0 +1,40 @@
1
+ module Sidekiq
2
+ module Tasks
3
+ module Web
4
+ module Helpers
5
+ module PaginationHelper
6
+ extend self
7
+
8
+ def pagination_link(root_path, link, search)
9
+ build_tag(:li, class: "st-page-item") do
10
+ build_tag(
11
+ :a,
12
+ link[:text],
13
+ class: build_classes("st-page-link", disabled: link[:disabled], active: link[:active]),
14
+ href: pagination_url(root_path, search, link[:page])
15
+ )
16
+ end
17
+ end
18
+
19
+ private
20
+
21
+ def build_tag(tag, content = nil, **attributes, &block)
22
+ attr_string = attributes.map { |key, value| "#{key}=\"#{value}\"" }.join(" ")
23
+
24
+ "<#{tag} #{attr_string}>#{block&.call || content}</#{tag}>"
25
+ end
26
+
27
+ def build_classes(*classes, **conditions)
28
+ condition_classes = conditions.select { |_, value| value }.keys
29
+
30
+ (classes + condition_classes).join(" ")
31
+ end
32
+
33
+ def pagination_url(root_path, search, page)
34
+ "#{root_path}tasks?filter=#{ERB::Util.url_encode(search.filter)}&count=#{search.count}&page=#{page}"
35
+ end
36
+ end
37
+ end
38
+ end
39
+ end
40
+ end
@@ -0,0 +1,86 @@
1
+ module Sidekiq
2
+ module Tasks
3
+ module Web
4
+ class Pagination
5
+ PREV_LABEL = "«".freeze
6
+ NEXT_LABEL = "»".freeze
7
+ ELLIPSIS_LABEL = "...".freeze
8
+ MIN_PAGE_DISPLAY = 7
9
+ PAGE_OFFSET = 2
10
+
11
+ attr_reader :current_page, :total_pages
12
+
13
+ def initialize(current_page, total_pages)
14
+ @current_page = current_page
15
+ @total_pages = total_pages
16
+ end
17
+
18
+ def links
19
+ return [] if total_pages <= 1
20
+
21
+ build_links.flatten.compact
22
+ end
23
+
24
+ private
25
+
26
+ def build_links
27
+ [
28
+ navigation_link(PREV_LABEL, current_page - 1, current_page > 1),
29
+ first_page_link,
30
+ leading_ellipsis,
31
+ middle_page_links,
32
+ trailing_ellipsis,
33
+ last_page_link,
34
+ navigation_link(NEXT_LABEL, current_page + 1, current_page < total_pages),
35
+ ]
36
+ end
37
+
38
+ def navigation_link(text, page, enabled)
39
+ enabled ? {page: page, text: text} : {page: nil, text: text, disabled: true}
40
+ end
41
+
42
+ def page_link(page)
43
+ {page: page, text: page.to_s, active: page == current_page}
44
+ end
45
+
46
+ def ellipsis
47
+ {page: 1, text: ELLIPSIS_LABEL, disabled: true}
48
+ end
49
+
50
+ def first_page_link
51
+ return nil if total_pages <= MIN_PAGE_DISPLAY
52
+
53
+ page_link(1)
54
+ end
55
+
56
+ def last_page_link
57
+ return nil if total_pages <= MIN_PAGE_DISPLAY
58
+
59
+ page_link(total_pages)
60
+ end
61
+
62
+ def leading_ellipsis
63
+ return nil if total_pages <= MIN_PAGE_DISPLAY || current_page <= PAGE_OFFSET + 2
64
+
65
+ ellipsis
66
+ end
67
+
68
+ def trailing_ellipsis
69
+ return nil if total_pages <= MIN_PAGE_DISPLAY || current_page >= total_pages - (PAGE_OFFSET + 1)
70
+
71
+ ellipsis
72
+ end
73
+
74
+ def middle_page_links
75
+ if total_pages <= MIN_PAGE_DISPLAY
76
+ (1..total_pages).map { |page| page_link(page) }
77
+ else
78
+ start_page = [current_page - PAGE_OFFSET, 2].max
79
+ end_page = [current_page + PAGE_OFFSET, total_pages - 1].min
80
+ (start_page..end_page).map { |page| page_link(page) }
81
+ end
82
+ end
83
+ end
84
+ end
85
+ end
86
+ end
@@ -2,10 +2,10 @@ module Sidekiq
2
2
  module Tasks
3
3
  module Web
4
4
  class Search
5
- DEFAULT_COUNT = 25
5
+ DEFAULT_COUNT = 15
6
6
 
7
7
  def self.count_options
8
- (1..4).map { |index| index * DEFAULT_COUNT }
8
+ (0..3).map { |index| DEFAULT_COUNT * (2**index) }
9
9
  end
10
10
 
11
11
  attr_reader :params
@@ -1,18 +1,37 @@
1
- require "sidekiq/tasks/web/helpers/application_helper"
2
- require "sidekiq/tasks/web/helpers/task_helper"
3
- require "sidekiq/tasks/web/search"
4
- require "sidekiq/tasks/task"
1
+ require "sidekiq/tasks"
5
2
 
6
3
  module Sidekiq
7
4
  module Tasks
8
5
  module Web
9
6
  autoload :Extension, "sidekiq/tasks/web/extension"
10
- autoload :Params, "sidekiq/tasks/web/params"
7
+
8
+ ROOT = File.expand_path("../../../web", File.dirname(__FILE__))
9
+ SIDEKIQ_GTE_7_3_0 = Gem::Version.new(Sidekiq::VERSION) >= Gem::Version.new("7.3.0")
11
10
  end
12
11
  end
13
12
  end
14
13
 
15
- if defined?(Sidekiq::Web)
16
- Sidekiq::Web.register(Sidekiq::Tasks::Web::Extension)
14
+ if Sidekiq::Tasks::Web::SIDEKIQ_GTE_7_3_0
15
+ Sidekiq::Web.configure do |config|
16
+ config.register(
17
+ Sidekiq::Tasks::Web::Extension,
18
+ name: "tasks",
19
+ tab: ["Tasks"],
20
+ index: ["tasks"],
21
+ root_dir: Sidekiq::Tasks::Web::ROOT,
22
+ asset_paths: ["js", "css"]
23
+ )
24
+ end
25
+ else
17
26
  Sidekiq::Web.tabs["Tasks"] = "tasks"
27
+ Sidekiq::Web.register(Sidekiq::Tasks::Web::Extension)
28
+ Sidekiq::Web.locales << "#{Sidekiq::Tasks::Web::ROOT}/locales"
29
+
30
+ Sidekiq::Web.use(
31
+ Rack::Static,
32
+ urls: ["/tasks/css", "/tasks/js"],
33
+ root: "#{Sidekiq::Tasks::Web::ROOT}/assets",
34
+ cascade: true,
35
+ header_rules: [[:all, {"cache-control" => "private, max-age=86400"}]]
36
+ )
18
37
  end
@@ -0,0 +1,41 @@
1
+ :root {
2
+ --st-button-bg: #b1003e;
3
+ --st-button-text: white;
4
+ --st-button-border: none;
5
+ --st-button-hover-bg: #900030;
6
+ --st-button-disabled-bg: #ccc;
7
+ --st-button-disabled-text: #666;
8
+ }
9
+
10
+ @media (prefers-color-scheme: dark) {
11
+ :root {
12
+ --st-button-bg: #900030;
13
+ --st-button-text: white;
14
+ --st-button-border: none;
15
+ --st-button-hover-bg: #700024;
16
+ --st-button-disabled-bg: #555;
17
+ --st-button-disabled-text: #aaa;
18
+ }
19
+ }
20
+
21
+ .st-button {
22
+ background-color: var(--st-button-bg);
23
+ color: var(--st-button-text);
24
+ border: var(--st-button-border);
25
+ padding: 8px 16px;
26
+ border-radius: 6px;
27
+ font-size: 16px;
28
+ cursor: pointer;
29
+ transition: background 0.3s ease-in-out;
30
+ }
31
+
32
+ .st-button:hover {
33
+ background-color: var(--st-button-hover-bg);
34
+ }
35
+
36
+ .st-button:disabled {
37
+ background-color: var(--st-button-disabled-bg);
38
+ color: var(--st-button-disabled-text);
39
+ cursor: not-allowed;
40
+ }
41
+
@@ -0,0 +1,110 @@
1
+ :root {
2
+ --st-border-color: #ccc;
3
+ --st-focus-border-color: #b1003e;
4
+ --st-input-bg: white;
5
+ --st-input-text: #000;
6
+ --st-select-bg: #ddd;
7
+ --st-label-color: #000;
8
+ }
9
+
10
+ @media (prefers-color-scheme: dark) {
11
+ :root {
12
+ --st-border-color: #555;
13
+ --st-focus-border-color: #900030;
14
+ --st-input-bg: #222;
15
+ --st-input-text: #ddd;
16
+ --st-select-bg: #333;
17
+ --st-label-color: #ddd;
18
+ }
19
+ }
20
+
21
+ .st-search-form {
22
+ display: flex;
23
+ align-items: center;
24
+ gap: 8px;
25
+ max-width: 350px;
26
+ }
27
+
28
+ .st-form-container {
29
+ max-width: 600px;
30
+ }
31
+
32
+ .st-form-row {
33
+ display: flex;
34
+ flex-wrap: wrap;
35
+ gap: 15px;
36
+ }
37
+
38
+ .st-form-group {
39
+ flex: 1 1 calc(50% - 15px);
40
+ display: flex;
41
+ flex-direction: column;
42
+ margin-bottom: 15px;
43
+ }
44
+
45
+ .st-form-group-full {
46
+ flex: 1 1 100%;
47
+ display: flex;
48
+ flex-direction: column;
49
+ margin-bottom: 15px;
50
+ }
51
+
52
+ .st-label {
53
+ font-weight: bold;
54
+ margin-bottom: 5px;
55
+ font-size: 14px;
56
+ color: var(--st-label-color);
57
+ }
58
+
59
+ .st-input {
60
+ width: 100%;
61
+ height: auto;
62
+ border: 1px solid var(--st-border-color);
63
+ padding: 8px 12px;
64
+ border-radius: 6px;
65
+ font-size: 16px;
66
+ background-color: var(--st-input-bg);
67
+ color: var(--st-input-text);
68
+ transition: border 0.3s ease-in-out, background 0.3s ease-in-out;
69
+ }
70
+
71
+ .st-input:focus,
72
+ .st-select:focus {
73
+ outline: none;
74
+ border: 1px solid var(--st-focus-border-color);
75
+ }
76
+
77
+ .st-select {
78
+ height: auto;
79
+ border: 1px solid var(--st-border-color);
80
+ padding: 8px;
81
+ border-radius: 6px;
82
+ font-size: 16px;
83
+ background-color: var(--st-select-bg);
84
+ color: var(--st-input-text);
85
+ transition: border 0.3s ease-in-out, background 0.3s ease-in-out;
86
+ }
87
+
88
+ .st-input,
89
+ .st-select,
90
+ .st-button {
91
+ height: 40px;
92
+ box-sizing: border-box;
93
+ }
94
+
95
+ @media (max-width: 600px) {
96
+ .st-form-row {
97
+ flex-direction: column;
98
+ gap: 0;
99
+ }
100
+
101
+ .st-form-group,
102
+ .st-form-group-full {
103
+ flex: 1 1 100%;
104
+ }
105
+
106
+ .st-form-container {
107
+ max-width: 100%;
108
+ padding: 10px 0;
109
+ }
110
+ }
@@ -0,0 +1,52 @@
1
+ :root {
2
+ --st-pagination-text: #b1003e;
3
+ --st-pagination-active-opacity: 0.5;
4
+ --st-pagination-disabled-text: #ccc;
5
+ }
6
+
7
+ @media (prefers-color-scheme: dark) {
8
+ :root {
9
+ --st-pagination-text: #ff4d79;
10
+ --st-pagination-active-opacity: 0.7;
11
+ --st-pagination-disabled-text: #555;
12
+ }
13
+ }
14
+
15
+ .st-pagination-wrapper {
16
+ overflow-x: auto;
17
+ padding-bottom: 10px;
18
+ display: flex;
19
+ width: 100%;
20
+ justify-content: space-between;
21
+ }
22
+
23
+ .st-pagination {
24
+ display: flex;
25
+ gap: 5px;
26
+ list-style: none;
27
+ padding: 0;
28
+ margin: 0;
29
+ white-space: nowrap;
30
+ }
31
+
32
+ .st-page-item {
33
+ display: flex;
34
+ }
35
+
36
+ a.st-page-link {
37
+ text-decoration: none;
38
+ display: block;
39
+ color: var(--st-pagination-text);
40
+ font-size: 14px;
41
+ transition: background 0.2s ease-in-out, color 0.2s ease-in-out;
42
+ }
43
+
44
+ a.st-page-link.active {
45
+ opacity: var(--st-pagination-active-opacity);
46
+ }
47
+
48
+ a.st-page-link.disabled {
49
+ pointer-events: none;
50
+ color: var(--st-pagination-disabled-text);
51
+ }
52
+
@@ -0,0 +1,48 @@
1
+ :root {
2
+ --st-table-bg: transparent;
3
+ --st-table-text: #333;
4
+ --st-table-border: #ddd;
5
+ --st-table-odd-bg: #fcfcfc;
6
+ --st-table-even-bg: transparent;
7
+ --st-table-header-bg: transparent;
8
+ }
9
+
10
+ @media (prefers-color-scheme: dark) {
11
+ :root {
12
+ --st-table-bg: transparent;
13
+ --st-table-text: #ddd;
14
+ --st-table-border: #555;
15
+ --st-table-odd-bg: #222;
16
+ --st-table-even-bg: #333;
17
+ --st-table-header-bg: #111;
18
+ }
19
+ }
20
+
21
+ .st-table {
22
+ background-color: var(--st-table-bg);
23
+ color: var(--st-table-text);
24
+ width: 100%;
25
+ max-width: 100%;
26
+ margin-bottom: 20px;
27
+ }
28
+
29
+ .st-table thead > tr > th,
30
+ .st-table tbody > tr > th,
31
+ .st-table tbody > tr > td {
32
+ border: 1px solid var(--st-table-border);
33
+ padding: 7px 10px;
34
+ }
35
+
36
+ .st-table tbody tr:nth-child(odd) {
37
+ background-color: var(--st-table-odd-bg);
38
+ }
39
+
40
+ .st-table tbody tr:nth-child(even) th,
41
+ .st-table tbody tr:nth-child(even) td {
42
+ background-color: var(--st-table-even-bg);
43
+ }
44
+
45
+ .st-table thead > tr > th {
46
+ background-color: var(--st-table-header-bg);
47
+ }
48
+
@@ -0,0 +1,7 @@
1
+ @import url("utilities.css");
2
+ @import url("layouts/header.css");
3
+ @import url("components/forms.css");
4
+ @import url("components/tables.css");
5
+ @import url("components/buttons.css");
6
+ @import url("components/pagination.css");
7
+
@@ -0,0 +1,5 @@
1
+ .st-header {
2
+ padding-top: 15px;
3
+ padding-bottom: 15px;
4
+ }
5
+
@@ -0,0 +1,8 @@
1
+ .st-text-primary {
2
+ color: #b1003e;
3
+ }
4
+
5
+ .st-ms-1 {
6
+ margin-left: 0.25rem;
7
+ }
8
+
@@ -0,0 +1,20 @@
1
+ class EnvConfirmation {
2
+ constructor(envConfirmationInputSelector, submitBtnSelector) {
3
+ this.envConfirmationInput = document.querySelector(envConfirmationInputSelector);
4
+ this.submitBtn = document.querySelector(submitBtnSelector);
5
+ }
6
+
7
+ init() {
8
+ this.submitBtn.disabled = true;
9
+ this.currentEnv = this.envConfirmationInput.dataset.currentEnv;
10
+ this.envConfirmationInput.addEventListener("input", this.#handleInputChange.bind(this));
11
+ }
12
+
13
+ #handleInputChange() {
14
+ this.submitBtn.disabled = this.envConfirmationInput.value !== this.currentEnv;
15
+ }
16
+ }
17
+
18
+ document.addEventListener("DOMContentLoaded", function() {
19
+ new EnvConfirmation("#envConfirmationInput", "#submitBtn").init();
20
+ });
@@ -1,4 +1,5 @@
1
1
  en:
2
+ search: "Search"
2
3
  prev: "Prev"
3
4
  next: "Next"
4
5
  tasks: "Tasks"
@@ -10,10 +11,11 @@ en:
10
11
  no_history: "No history"
11
12
  jid: "JID"
12
13
  args: "Arguments"
13
- enqueued_at: "Enqueued at"
14
- executed_at: "Executed at"
14
+ enqueued: "Enqueued"
15
+ executed: "Executed"
15
16
  task: "Task"
16
17
  desc: "Description"
17
18
  strategy: "Strategy"
18
19
  run_task: "Run task"
19
20
  enqueue: "Enqueue"
21
+ env_confirmation: "Please enter '%{current_env}' to confirm."
@@ -1,4 +1,5 @@
1
1
  fr:
2
+ search: "Rechercher"
2
3
  prev: "Précédent"
3
4
  next: "Suivant"
4
5
  tasks: "Tâches"
@@ -10,10 +11,11 @@ fr:
10
11
  no_history: "Aucun historique"
11
12
  jid: "JID"
12
13
  args: "Arguments"
13
- enqueued_at: "Mise en file d'attente le"
14
- executed_at: "Exécuté le"
14
+ enqueued: "Mise en file d'attente"
15
+ executed: "Exécuté"
15
16
  task: "Tâche"
16
17
  desc: "Description"
17
18
  strategy: "Stratégie"
18
19
  run_task: "Exécuter la tâche"
19
20
  enqueue: "Mettre en file d'attente"
21
+ env_confirmation: "Veuillez saisir '%{current_env}' pour confirmer."
@@ -0,0 +1,15 @@
1
+ <% if search.total_pages > 1 %>
2
+ <div class="st-pagination-wrapper">
3
+ <div>
4
+ <small class="st-text-primary">
5
+ <%= search.tasks.size %> / <%= Sidekiq::Tasks.tasks.size %>
6
+ </small>
7
+ </div>
8
+
9
+ <ul class="st-pagination">
10
+ <% Sidekiq::Tasks::Web::Pagination.new(search.page, search.total_pages).links.each do |link| %>
11
+ <%= pagination_link(root_path, link, search) %>
12
+ <% end %>
13
+ </ul>
14
+ </div>
15
+ <% end %>
@@ -0,0 +1,97 @@
1
+ <% if Sidekiq::Tasks::Web::SIDEKIQ_GTE_7_3_0 %>
2
+ <% style_tag "tasks/css/ext.css" %>
3
+ <% else %>
4
+ <link href="<%= root_path %>tasks/css/ext.css" media="screen" rel="stylesheet" type="text/css"/>
5
+ <% end %>
6
+
7
+ <header class="st-header">
8
+ <h1><%= t("task") %></h1>
9
+ </header>
10
+
11
+ <table class="st-table">
12
+ <tbody>
13
+ <tr>
14
+ <th><%= t("name") %></th>
15
+ <td><%= task.name %></td>
16
+ </tr>
17
+ <tr>
18
+ <th><%= t("desc") %></th>
19
+ <td><%= task.desc %></td>
20
+ </tr>
21
+ <tr>
22
+ <th><%= t("strategy") %></th>
23
+ <td><%= task.strategy.name %></td>
24
+ </tr>
25
+ <tr>
26
+ <th><%= t("last_enqueued") %></th>
27
+ <td><%= task.last_enqueue_at ? relative_time(task.last_enqueue_at) : "-" %></td>
28
+ </tr>
29
+ </tbody>
30
+ </table>
31
+
32
+ <header class="">
33
+ <div class="">
34
+ <h2><%= t("history") %></h2>
35
+ </div>
36
+ </header>
37
+
38
+ <% if task.history.empty? %>
39
+ <p><%= t("no_history") %></p>
40
+ <% else %>
41
+ <table class="st-table">
42
+ <thead>
43
+ <tr>
44
+ <th><%= t("jid") %></th>
45
+ <th><%= t("args") %></th>
46
+ <th><%= t("enqueued") %></th>
47
+ <th><%= t("executed") %></th>
48
+ </tr>
49
+ </thead>
50
+ <tbody>
51
+ <% task.history.each do |jid_history| %>
52
+ <tr>
53
+ <td><%= jid_history["jid"] %></td>
54
+ <td><%= jid_history["args"] %></td>
55
+ <td><%= jid_history["enqueued_at"] ? relative_time(jid_history["enqueued_at"]) : "-" %></td>
56
+ <td><%= jid_history["executed_at"] ? relative_time(jid_history["executed_at"]) : "-" %></td>
57
+ </tr>
58
+ <% end %>
59
+ </tbody>
60
+ </table>
61
+ <% end %>
62
+
63
+ <header class="">
64
+ <div class="">
65
+ <h2><%= t("run_task") %></h2>
66
+ </div>
67
+ </header>
68
+
69
+ <form action="<%= task_url(root_path, task) %>/enqueue" method="post" class="st-form-container">
70
+ <%= csrf_tag %>
71
+
72
+ <div class="st-form-row">
73
+ <% task.args.each do |arg| %>
74
+ <div class="st-form-group">
75
+ <label for="<%= arg %>" class="st-label"><%= arg %></label>
76
+ <input type="text" id="<%= arg %>" class="st-input" name="args[<%= arg %>]"/>
77
+ </div>
78
+ <% end %>
79
+ </div>
80
+
81
+ <div class="st-form-group-full">
82
+ <label for="envConfirmationInput" class="st-label">
83
+ <%= t("env_confirmation", current_env: current_env) %>
84
+ </label>
85
+ <input type="text" id="envConfirmationInput" class="st-input" name="env_confirmation" data-current-env="<%= current_env %>" required/>
86
+ </div>
87
+
88
+ <button type="submit" class="st-button" id="submitBtn" disabled>
89
+ <%= t("enqueue") %>
90
+ </button>
91
+ </form>
92
+
93
+ <% if Sidekiq::Tasks::Web::SIDEKIQ_GTE_7_3_0 %>
94
+ <%= script_tag "tasks/js/env_confirmation.js" %>
95
+ <% else %>
96
+ <script type="text/javascript" src="<%= root_path %>tasks/js/env_confirmation.js"></script>
97
+ <% end %>
@@ -1,10 +1,14 @@
1
- <div class="header-container">
2
- <h1><%= t("tasks") %></h1>
1
+ <% if Sidekiq::Tasks::Web::SIDEKIQ_GTE_7_3_0 %>
2
+ <% style_tag "tasks/css/ext.css" %>
3
+ <% else %>
4
+ <link href="<%= root_path %>tasks/css/ext.css" media="screen" rel="stylesheet" type="text/css"/>
5
+ <% end %>
3
6
 
4
- <form action="<%= root_path %>tasks" class="form-inline" method="get">
5
- <input name="filter" class="form-control" type="text" value="<%= @search.filter %>" />
7
+ <header class="st-header">
8
+ <form action="<%= root_path %>tasks" method="get" class="st-search-form">
9
+ <input name="filter" class="st-input" type="text" value="<%= @search.filter %>" placeholder="<%= t("search") %>">
6
10
 
7
- <select name="count" class="form-control">
11
+ <select name="count" class="st-select">
8
12
  <% Sidekiq::Tasks::Web::Search.count_options.each do |count| %>
9
13
  <option value="<%= count %>" <%= "selected" if @search.count == count %>>
10
14
  <%= count %>
@@ -12,18 +16,16 @@
12
16
  <% end %>
13
17
  </select>
14
18
 
15
- <input name="page" type="hidden" value="<%= @search.page %>" />
16
-
17
- <button class="btn btn-default" type="submit">
19
+ <button type="submit" class="st-button st-button-primary">
18
20
  <%= t("filter") %>
19
21
  </button>
20
22
  </form>
21
- </div>
23
+ </header>
22
24
 
23
25
  <% if @search.tasks.empty? %>
24
26
  <%= t("no_tasks") %>
25
27
  <% else %>
26
- <table class="table table-hover table-bordered table-striped table-white">
28
+ <table class="st-table">
27
29
  <thead>
28
30
  <tr>
29
31
  <th><%= t("name") %></th>
@@ -34,7 +36,7 @@
34
36
  <% @search.tasks.each do |task| %>
35
37
  <tr>
36
38
  <td>
37
- <a href="<%= task_url(root_path, task) %>">
39
+ <a href="<%= task_url(root_path, task) %>" class="st-text-primary">
38
40
  <b><%= task.name %></b>
39
41
  </a>
40
42
  </td>
@@ -45,9 +47,5 @@
45
47
  </tbody>
46
48
  </table>
47
49
 
48
- <div class="pull-left">
49
- <%= @search.tasks.size %> / <%= Sidekiq::Tasks.tasks.size %>
50
- </div>
51
-
52
- <%= render(:erb, read_view(:_pagination), locals: {search: @search}) %>
50
+ <%= erb(read_view(:_pagination), locals: {search: @search}) %>
53
51
  <% end %>
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: sidekiq-tasks
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.1.0
4
+ version: 0.1.2
5
5
  platform: ruby
6
6
  authors:
7
7
  - Victor
8
8
  autorequire:
9
9
  bindir: exe
10
10
  cert_chain: []
11
- date: 2025-02-23 00:00:00.000000000 Z
11
+ date: 2025-03-22 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: rake
@@ -30,182 +30,196 @@ dependencies:
30
30
  requirements:
31
31
  - - ">="
32
32
  - !ruby/object:Gem::Version
33
- version: '6'
33
+ version: '6.5'
34
34
  type: :runtime
35
35
  prerelease: false
36
36
  version_requirements: !ruby/object:Gem::Requirement
37
37
  requirements:
38
38
  - - ">="
39
39
  - !ruby/object:Gem::Version
40
- version: '6'
40
+ version: '6.5'
41
41
  - !ruby/object:Gem::Dependency
42
42
  name: base64
43
43
  requirement: !ruby/object:Gem::Requirement
44
44
  requirements:
45
- - - "~>"
45
+ - - ">="
46
46
  - !ruby/object:Gem::Version
47
- version: 0.1.0
47
+ version: '0'
48
48
  type: :development
49
49
  prerelease: false
50
50
  version_requirements: !ruby/object:Gem::Requirement
51
51
  requirements:
52
- - - "~>"
52
+ - - ">="
53
53
  - !ruby/object:Gem::Version
54
- version: 0.1.0
54
+ version: '0'
55
55
  - !ruby/object:Gem::Dependency
56
56
  name: capybara
57
57
  requirement: !ruby/object:Gem::Requirement
58
58
  requirements:
59
- - - "~>"
59
+ - - ">="
60
60
  - !ruby/object:Gem::Version
61
- version: '3.40'
61
+ version: '0'
62
62
  type: :development
63
63
  prerelease: false
64
64
  version_requirements: !ruby/object:Gem::Requirement
65
65
  requirements:
66
- - - "~>"
66
+ - - ">="
67
67
  - !ruby/object:Gem::Version
68
- version: '3.40'
68
+ version: '0'
69
69
  - !ruby/object:Gem::Dependency
70
70
  name: rack-session
71
71
  requirement: !ruby/object:Gem::Requirement
72
72
  requirements:
73
- - - "~>"
73
+ - - ">="
74
74
  - !ruby/object:Gem::Version
75
- version: '1.0'
75
+ version: '0'
76
76
  type: :development
77
77
  prerelease: false
78
78
  version_requirements: !ruby/object:Gem::Requirement
79
79
  requirements:
80
- - - "~>"
80
+ - - ">="
81
81
  - !ruby/object:Gem::Version
82
- version: '1.0'
82
+ version: '0'
83
83
  - !ruby/object:Gem::Dependency
84
84
  name: rack-test
85
85
  requirement: !ruby/object:Gem::Requirement
86
86
  requirements:
87
- - - "~>"
87
+ - - ">="
88
88
  - !ruby/object:Gem::Version
89
- version: '1.1'
89
+ version: '0'
90
90
  type: :development
91
91
  prerelease: false
92
92
  version_requirements: !ruby/object:Gem::Requirement
93
93
  requirements:
94
- - - "~>"
94
+ - - ">="
95
+ - !ruby/object:Gem::Version
96
+ version: '0'
97
+ - !ruby/object:Gem::Dependency
98
+ name: rackup
99
+ requirement: !ruby/object:Gem::Requirement
100
+ requirements:
101
+ - - ">="
102
+ - !ruby/object:Gem::Version
103
+ version: '0'
104
+ type: :development
105
+ prerelease: false
106
+ version_requirements: !ruby/object:Gem::Requirement
107
+ requirements:
108
+ - - ">="
95
109
  - !ruby/object:Gem::Version
96
- version: '1.1'
110
+ version: '0'
97
111
  - !ruby/object:Gem::Dependency
98
112
  name: rake
99
113
  requirement: !ruby/object:Gem::Requirement
100
114
  requirements:
101
- - - "~>"
115
+ - - ">="
102
116
  - !ruby/object:Gem::Version
103
- version: '13.0'
117
+ version: '0'
104
118
  type: :development
105
119
  prerelease: false
106
120
  version_requirements: !ruby/object:Gem::Requirement
107
121
  requirements:
108
- - - "~>"
122
+ - - ">="
109
123
  - !ruby/object:Gem::Version
110
- version: '13.0'
124
+ version: '0'
111
125
  - !ruby/object:Gem::Dependency
112
126
  name: rspec
113
127
  requirement: !ruby/object:Gem::Requirement
114
128
  requirements:
115
- - - "~>"
129
+ - - ">="
116
130
  - !ruby/object:Gem::Version
117
- version: '3.0'
131
+ version: '0'
118
132
  type: :development
119
133
  prerelease: false
120
134
  version_requirements: !ruby/object:Gem::Requirement
121
135
  requirements:
122
- - - "~>"
136
+ - - ">="
123
137
  - !ruby/object:Gem::Version
124
- version: '3.0'
138
+ version: '0'
125
139
  - !ruby/object:Gem::Dependency
126
140
  name: rubocop
127
141
  requirement: !ruby/object:Gem::Requirement
128
142
  requirements:
129
- - - "~>"
143
+ - - ">="
130
144
  - !ruby/object:Gem::Version
131
- version: '1.21'
145
+ version: '0'
132
146
  type: :development
133
147
  prerelease: false
134
148
  version_requirements: !ruby/object:Gem::Requirement
135
149
  requirements:
136
- - - "~>"
150
+ - - ">="
137
151
  - !ruby/object:Gem::Version
138
- version: '1.21'
152
+ version: '0'
139
153
  - !ruby/object:Gem::Dependency
140
154
  name: sidekiq
141
155
  requirement: !ruby/object:Gem::Requirement
142
156
  requirements:
143
- - - "~>"
157
+ - - ">="
144
158
  - !ruby/object:Gem::Version
145
- version: '6.5'
159
+ version: '0'
146
160
  type: :development
147
161
  prerelease: false
148
162
  version_requirements: !ruby/object:Gem::Requirement
149
163
  requirements:
150
- - - "~>"
164
+ - - ">="
151
165
  - !ruby/object:Gem::Version
152
- version: '6.5'
166
+ version: '0'
153
167
  - !ruby/object:Gem::Dependency
154
168
  name: simplecov
155
169
  requirement: !ruby/object:Gem::Requirement
156
170
  requirements:
157
- - - "~>"
171
+ - - ">="
158
172
  - !ruby/object:Gem::Version
159
- version: '0.21'
173
+ version: '0'
160
174
  type: :development
161
175
  prerelease: false
162
176
  version_requirements: !ruby/object:Gem::Requirement
163
177
  requirements:
164
- - - "~>"
178
+ - - ">="
165
179
  - !ruby/object:Gem::Version
166
- version: '0.21'
180
+ version: '0'
167
181
  - !ruby/object:Gem::Dependency
168
182
  name: simplecov-json
169
183
  requirement: !ruby/object:Gem::Requirement
170
184
  requirements:
171
- - - "~>"
185
+ - - ">="
172
186
  - !ruby/object:Gem::Version
173
- version: '0.2'
187
+ version: '0'
174
188
  type: :development
175
189
  prerelease: false
176
190
  version_requirements: !ruby/object:Gem::Requirement
177
191
  requirements:
178
- - - "~>"
192
+ - - ">="
179
193
  - !ruby/object:Gem::Version
180
- version: '0.2'
194
+ version: '0'
181
195
  - !ruby/object:Gem::Dependency
182
196
  name: webdrivers
183
197
  requirement: !ruby/object:Gem::Requirement
184
198
  requirements:
185
- - - "~>"
199
+ - - ">="
186
200
  - !ruby/object:Gem::Version
187
- version: 5.3.1
201
+ version: '0'
188
202
  type: :development
189
203
  prerelease: false
190
204
  version_requirements: !ruby/object:Gem::Requirement
191
205
  requirements:
192
- - - "~>"
206
+ - - ">="
193
207
  - !ruby/object:Gem::Version
194
- version: 5.3.1
208
+ version: '0'
195
209
  - !ruby/object:Gem::Dependency
196
210
  name: webrick
197
211
  requirement: !ruby/object:Gem::Requirement
198
212
  requirements:
199
- - - "~>"
213
+ - - ">="
200
214
  - !ruby/object:Gem::Version
201
- version: 1.9.1
215
+ version: '0'
202
216
  type: :development
203
217
  prerelease: false
204
218
  version_requirements: !ruby/object:Gem::Requirement
205
219
  requirements:
206
- - - "~>"
220
+ - - ">="
207
221
  - !ruby/object:Gem::Version
208
- version: 1.9.1
222
+ version: '0'
209
223
  description: |
210
224
  Sidekiq Tasks is an extension for Sidekiq that provides an interface for launching tasks.
211
225
  Natively supports rake tasks and can be easily extended to support other task execution systems.
@@ -245,15 +259,25 @@ files:
245
259
  - lib/sidekiq/tasks/web.rb
246
260
  - lib/sidekiq/tasks/web/extension.rb
247
261
  - lib/sidekiq/tasks/web/helpers/application_helper.rb
262
+ - lib/sidekiq/tasks/web/helpers/pagination_helper.rb
248
263
  - lib/sidekiq/tasks/web/helpers/task_helper.rb
249
- - lib/sidekiq/tasks/web/locales/en.yml
250
- - lib/sidekiq/tasks/web/locales/fr.yml
264
+ - lib/sidekiq/tasks/web/pagination.rb
251
265
  - lib/sidekiq/tasks/web/params.rb
252
266
  - lib/sidekiq/tasks/web/search.rb
253
- - lib/sidekiq/tasks/web/views/_pagination.html.erb
254
- - lib/sidekiq/tasks/web/views/_task.html.erb
255
- - lib/sidekiq/tasks/web/views/tasks.html.erb
256
267
  - sig/sidekiq/tasks.rbs
268
+ - web/assets/tasks/css/components/buttons.css
269
+ - web/assets/tasks/css/components/forms.css
270
+ - web/assets/tasks/css/components/pagination.css
271
+ - web/assets/tasks/css/components/tables.css
272
+ - web/assets/tasks/css/ext.css
273
+ - web/assets/tasks/css/layouts/header.css
274
+ - web/assets/tasks/css/utilities.css
275
+ - web/assets/tasks/js/env_confirmation.js
276
+ - web/locales/en.yml
277
+ - web/locales/fr.yml
278
+ - web/views/_pagination.erb
279
+ - web/views/task.erb
280
+ - web/views/tasks.erb
257
281
  homepage: https://github.com/victorauthiat/sidekiq-tasks
258
282
  licenses:
259
283
  - MIT
@@ -1,25 +0,0 @@
1
- <% if search.total_pages > 1 %>
2
- <ul class="pagination pull-right">
3
- <% if search.page > 1 %>
4
- <li>
5
- <a href="<%= "#{root_path}tasks?filter=#{ERB::Util.url_encode(search.filter)}&count=#{search.count}&page=#{search.page - 1}" %>">
6
- <%= t("prev") %>
7
- </a>
8
- </li>
9
- <% end %>
10
-
11
- <% (0..(search.total_pages - 1)).each.with_index(1) do |_page, index| %>
12
- <li class="<%= 'active' if index == search.page %>">
13
- <a href="<%= "#{root_path}tasks?filter=#{ERB::Util.url_encode(search.filter)}&count=#{search.count}&page=#{index}" %>"><%= index %></a>
14
- </li>
15
- <% end %>
16
-
17
- <% if search.tasks.any? && search.tasks.size == search.count %>
18
- <li>
19
- <a href="<%= "#{root_path}tasks?filter=#{ERB::Util.url_encode(search.filter)}&count=#{search.count}&page=#{search.page + 1}" %>">
20
- <%= t("next") %>
21
- </a>
22
- </li>
23
- <% end %>
24
- </ul>
25
- <% end %>
@@ -1,84 +0,0 @@
1
- <header class="row">
2
- <div class="span col-sm-5 pull-left">
3
- <h1><%= t("task") %></h1>
4
- </div>
5
- </header>
6
-
7
- <table class="table table-bordered table-striped">
8
- <tbody>
9
- <tr>
10
- <th><%= t("name") %></th>
11
- <td><%= task.name %></td>
12
- </tr>
13
- <tr>
14
- <th><%= t("desc") %></th>
15
- <td><%= task.desc %></td>
16
- </tr>
17
- <tr>
18
- <th><%= t("strategy") %></th>
19
- <td><%= task.strategy.name %></td>
20
- </tr>
21
- <tr>
22
- <th><%= t("last_enqueued") %></th>
23
- <td><%= task.last_enqueue_at ? relative_time(task.last_enqueue_at) : "-" %></td>
24
- </tr>
25
- </tbody>
26
- </table>
27
-
28
- <header class="row">
29
- <div class="col-sm-12">
30
- <h2><%= t("history") %></h2>
31
- </div>
32
- </header>
33
-
34
- <% if task.history.empty? %>
35
- <p><%= t("no_history") %></p>
36
- <% else %>
37
- <table class="table table-hover table-bordered table-striped">
38
- <thead>
39
- <tr>
40
- <th><%= t("jid") %></th>
41
- <th><%= t("args") %></th>
42
- <th><%= t("enqueued_at") %></th>
43
- <th><%= t("executed_at") %></th>
44
- </tr>
45
- </thead>
46
- <tbody>
47
- <% task.history.each do |jid_history| %>
48
- <tr>
49
- <td><%= jid_history["jid"] %></td>
50
- <td><%= jid_history["args"] %></td>
51
- <td><%= jid_history["enqueued_at"] ? relative_time(jid_history["enqueued_at"]) : "-" %></td>
52
- <td><%= jid_history["executed_at"] ? relative_time(jid_history["executed_at"]) : "-" %></td>
53
- </tr>
54
- <% end %>
55
- </tbody>
56
- </table>
57
- <% end %>
58
-
59
- <header class="row">
60
- <div class="col-sm-12">
61
- <h2><%= t("run_task") %></h2>
62
- </div>
63
- </header>
64
-
65
- <form action="<%= task_url(root_path, task) %>/enqueue" method="post">
66
- <%= csrf_tag %>
67
-
68
- <div class="container">
69
- <div class="row">
70
- <% task.args.each do |arg| %>
71
- <div class="col-md-6 mb-3">
72
- <div class="form-group">
73
- <label for="<%= arg %>" class="form-label"><%= arg %></label>
74
- <input type="text" class="form-control" name="args[<%= arg %>]" id="<%= arg %>" />
75
- </div>
76
- </div>
77
- <% end %>
78
- </div>
79
- </div>
80
-
81
- <button type="submit" class="btn btn-primary">
82
- <%= t("enqueue") %>
83
- </button>
84
- </form>