sidekiq-tasks 0.1.8 → 1.0.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.
- checksums.yaml +4 -4
- data/CHANGELOG.md +7 -0
- data/README.md +27 -1
- data/docs/custom_storage.md +95 -0
- data/lib/sidekiq/tasks/config.rb +28 -1
- data/lib/sidekiq/tasks/storage/base.rb +73 -0
- data/lib/sidekiq/tasks/storage/redis.rb +89 -0
- data/lib/sidekiq/tasks/storage.rb +3 -96
- data/lib/sidekiq/tasks/task.rb +4 -4
- data/lib/sidekiq/tasks/validations.rb +8 -0
- data/lib/sidekiq/tasks/version.rb +1 -1
- data/lib/sidekiq/tasks/web/extension.rb +21 -3
- data/lib/sidekiq/tasks/web/helpers/pagination_helper.rb +15 -8
- data/lib/sidekiq/tasks/web/helpers/sort_helper.rb +26 -0
- data/lib/sidekiq/tasks/web/search.rb +44 -1
- data/lib/sidekiq/tasks.rb +1 -1
- data/web/assets/tasks/css/components/tables.css +34 -0
- data/web/locales/en.yml +1 -0
- data/web/locales/fr.yml +1 -0
- data/web/views/_pagination.erb +4 -4
- data/web/views/task.erb +33 -6
- data/web/views/tasks.erb +31 -4
- metadata +5 -1
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 8fee03d24fdcc9755f8fadb9915b39127fac89ba1f4447a7d822998b9a64c102
|
|
4
|
+
data.tar.gz: 8a6c5940eae17706522c7f584c392779a0b2c513848fa49cab8206c2a5af5473
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: b85eb95f150f99c0de282d0ac6fb1356a984f0cdc7d5a485baf1a9fdb7963346156a9e4c224d1cd22476002efd34bb8ed18bbf1252d762cbd25c8b41623557b4
|
|
7
|
+
data.tar.gz: 51ca1179d3232c84139c8ef380290aa5e9bd8f3b97b21e92a54165fcee0376a1a42b207ec9029510f086b35e2ec69aa091685f89d343a3eb38c31296d8600709
|
data/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,12 @@
|
|
|
1
1
|
## Changelog
|
|
2
2
|
|
|
3
|
+
### [1.0.0] - 2026-03-13
|
|
4
|
+
|
|
5
|
+
- Add configurable `storage` option with support for custom backends (default: Redis).
|
|
6
|
+
- Add configurable `current_user` option to track who enqueued each task.
|
|
7
|
+
- Add configurable `history_limit` option (default: 10).
|
|
8
|
+
- Add sortable columns (`name`, `last_enqueued`) in the tasks list.
|
|
9
|
+
|
|
3
10
|
### [0.1.8] - 2026-03-06
|
|
4
11
|
|
|
5
12
|
- Replace deprecated `webdrivers` gem with `selenium-webdriver`.
|
data/README.md
CHANGED
|
@@ -227,12 +227,38 @@ end
|
|
|
227
227
|
|
|
228
228
|
## Execution history
|
|
229
229
|
|
|
230
|
-
Each task keeps a history of its last 10
|
|
230
|
+
Each task keeps a history of its last executions (default: 10, stored in Redis). The task detail page displays for each execution:
|
|
231
231
|
|
|
232
232
|
- The enqueue, start, and finish timestamps
|
|
233
233
|
- The arguments passed
|
|
234
234
|
- The error message if the execution failed
|
|
235
235
|
|
|
236
|
+
You can configure the history limit:
|
|
237
|
+
|
|
238
|
+
```ruby
|
|
239
|
+
Sidekiq::Tasks.configure do |config|
|
|
240
|
+
config.history_limit = 25
|
|
241
|
+
end
|
|
242
|
+
```
|
|
243
|
+
|
|
244
|
+
You can also use a custom storage backend. See the [Custom Storage Guide](docs/custom_storage.md) for more details.
|
|
245
|
+
|
|
246
|
+
## Current user
|
|
247
|
+
|
|
248
|
+
You can configure a `current_user` proc to track who enqueued each task.
|
|
249
|
+
The proc receives the Rack `env` and should return a Hash identifying the user:
|
|
250
|
+
|
|
251
|
+
```ruby
|
|
252
|
+
Sidekiq::Tasks.configure do |config|
|
|
253
|
+
config.current_user = ->(env) do
|
|
254
|
+
user = env["warden"].user(:admin_user)
|
|
255
|
+
{id: user.id, email: user.email}
|
|
256
|
+
end
|
|
257
|
+
end
|
|
258
|
+
```
|
|
259
|
+
|
|
260
|
+
When configured, an "Enqueued by" column appears in the task history table.
|
|
261
|
+
|
|
236
262
|
## Development
|
|
237
263
|
|
|
238
264
|
After checking out the repo, run `bin/setup` to install dependencies. Then, run `rake spec` to run the tests. You can also run `bin/console` for an interactive prompt that will allow you to experiment.
|
|
@@ -0,0 +1,95 @@
|
|
|
1
|
+
# Custom Storage Guide
|
|
2
|
+
|
|
3
|
+
This guide walks you through implementing a custom storage backend using ActiveRecord in a Rails application.
|
|
4
|
+
|
|
5
|
+
## Step 1: Generate the model and run the migration
|
|
6
|
+
|
|
7
|
+
```bash
|
|
8
|
+
rails generate model TaskExecution \
|
|
9
|
+
task_name:string:index \
|
|
10
|
+
jid:string:index \
|
|
11
|
+
args:jsonb \
|
|
12
|
+
enqueued_at:datetime \
|
|
13
|
+
executed_at:datetime \
|
|
14
|
+
finished_at:datetime \
|
|
15
|
+
error:string \
|
|
16
|
+
user:jsonb
|
|
17
|
+
|
|
18
|
+
rails db:migrate
|
|
19
|
+
```
|
|
20
|
+
|
|
21
|
+
## Step 2: Configure the initializer
|
|
22
|
+
|
|
23
|
+
In `config/initializers/sidekiq_tasks.rb`:
|
|
24
|
+
|
|
25
|
+
```ruby
|
|
26
|
+
class ActiveRecordStorage < Sidekiq::Tasks::Storage::Base
|
|
27
|
+
def last_enqueue_at
|
|
28
|
+
TaskExecution.where(task_name: task_name).order(enqueued_at: :desc).pick(:enqueued_at)
|
|
29
|
+
end
|
|
30
|
+
|
|
31
|
+
def history
|
|
32
|
+
TaskExecution
|
|
33
|
+
.where(task_name: task_name)
|
|
34
|
+
.order(enqueued_at: :desc)
|
|
35
|
+
.limit(history_limit)
|
|
36
|
+
.select(:jid, :task_name, :args, :enqueued_at, :executed_at, :finished_at, :error, :user)
|
|
37
|
+
.map(&:attributes)
|
|
38
|
+
end
|
|
39
|
+
|
|
40
|
+
def store_enqueue(jid, args, user: nil)
|
|
41
|
+
TaskExecution.create!(
|
|
42
|
+
task_name: task_name,
|
|
43
|
+
jid: jid,
|
|
44
|
+
args: args,
|
|
45
|
+
enqueued_at: Time.now,
|
|
46
|
+
user: user
|
|
47
|
+
)
|
|
48
|
+
end
|
|
49
|
+
|
|
50
|
+
def store_execution(jid, time_key)
|
|
51
|
+
TaskExecution.find_by(jid: jid)&.update!(time_key => Time.now)
|
|
52
|
+
end
|
|
53
|
+
|
|
54
|
+
def store_execution_error(jid, error)
|
|
55
|
+
message = truncate_message("#{error.class}: #{error.message}", ERROR_MESSAGE_MAX_LENGTH)
|
|
56
|
+
TaskExecution.find_by(jid: jid)&.update!(error: message)
|
|
57
|
+
end
|
|
58
|
+
end
|
|
59
|
+
|
|
60
|
+
Sidekiq::Tasks.configure do |config|
|
|
61
|
+
config.storage = ActiveRecordStorage
|
|
62
|
+
end
|
|
63
|
+
```
|
|
64
|
+
|
|
65
|
+
> [!NOTE]
|
|
66
|
+
> The `history_limit` config is passed to each storage instance. The default Redis storage uses it to trim old entries. Custom storage implementations receive it via the `history_limit` accessor and can use it as needed (e.g. as a SQL `LIMIT`) or ignore it entirely.
|
|
67
|
+
|
|
68
|
+
## History entry format
|
|
69
|
+
|
|
70
|
+
The `history` method must return an array of hashes with the following keys:
|
|
71
|
+
|
|
72
|
+
| Key | Type | Description |
|
|
73
|
+
|-----------------|---------------|--------------------------------------|
|
|
74
|
+
| `"jid"` | String | The Sidekiq job ID |
|
|
75
|
+
| `"task_name"` | String | The task name |
|
|
76
|
+
| `"args"` | Hash | The arguments passed to the task |
|
|
77
|
+
| `"enqueued_at"` | Time | When the task was enqueued |
|
|
78
|
+
| `"executed_at"` | Time \| nil | When the task started executing |
|
|
79
|
+
| `"finished_at"` | Time \| nil | When the task finished executing |
|
|
80
|
+
| `"error"` | String \| nil | Error message if execution failed |
|
|
81
|
+
| `"user"` | Hash \| nil | User who enqueued the task |
|
|
82
|
+
|
|
83
|
+
## Migrating from Redis
|
|
84
|
+
|
|
85
|
+
After switching to a custom storage, you can clean up the old Redis history:
|
|
86
|
+
|
|
87
|
+
```bash
|
|
88
|
+
redis-cli --scan --pattern "task:*" | xargs redis-cli del
|
|
89
|
+
```
|
|
90
|
+
|
|
91
|
+
Or from a Rails console:
|
|
92
|
+
|
|
93
|
+
```ruby
|
|
94
|
+
Sidekiq.redis { |conn| conn.keys("task:*").each { |key| conn.del(key) } }
|
|
95
|
+
```
|
data/lib/sidekiq/tasks/config.rb
CHANGED
|
@@ -15,14 +15,21 @@ module Sidekiq
|
|
|
15
15
|
),
|
|
16
16
|
].freeze
|
|
17
17
|
|
|
18
|
+
DEFAULT_STORAGE = Sidekiq::Tasks::Storage::Redis
|
|
19
|
+
|
|
20
|
+
DEFAULT_HISTORY_LIMIT = 10
|
|
21
|
+
|
|
18
22
|
include Sidekiq::Tasks::Validations
|
|
19
23
|
|
|
20
|
-
attr_reader :strategies, :sidekiq_options, :authorization
|
|
24
|
+
attr_reader :strategies, :sidekiq_options, :authorization, :history_limit, :current_user, :storage
|
|
21
25
|
|
|
22
26
|
def initialize
|
|
23
27
|
@sidekiq_options = DEFAULT_SIDEKIQ_OPTIONS
|
|
24
28
|
@strategies = DEFAULT_STRATEGIES
|
|
29
|
+
@storage = DEFAULT_STORAGE
|
|
25
30
|
@authorization = ->(_env) { true }
|
|
31
|
+
@history_limit = DEFAULT_HISTORY_LIMIT
|
|
32
|
+
@current_user = nil
|
|
26
33
|
end
|
|
27
34
|
|
|
28
35
|
# @see https://github.com/sidekiq/sidekiq/wiki/Advanced-Options#jobs
|
|
@@ -45,11 +52,31 @@ module Sidekiq
|
|
|
45
52
|
@strategies = strategies
|
|
46
53
|
end
|
|
47
54
|
|
|
55
|
+
def storage=(storage_class)
|
|
56
|
+
validate_subclass!(storage_class, Sidekiq::Tasks::Storage::Base, "storage")
|
|
57
|
+
|
|
58
|
+
@storage = storage_class
|
|
59
|
+
end
|
|
60
|
+
|
|
61
|
+
def history_limit=(limit)
|
|
62
|
+
validate_class!(limit, [Integer], "history_limit")
|
|
63
|
+
|
|
64
|
+
raise Sidekiq::Tasks::ArgumentError, "'history_limit' must be greater than 0" if limit <= 0
|
|
65
|
+
|
|
66
|
+
@history_limit = limit
|
|
67
|
+
end
|
|
68
|
+
|
|
48
69
|
def authorization=(authorization_proc)
|
|
49
70
|
validate_class!(authorization_proc, [Proc], "authorization")
|
|
50
71
|
|
|
51
72
|
@authorization = authorization_proc
|
|
52
73
|
end
|
|
74
|
+
|
|
75
|
+
def current_user=(current_user_proc)
|
|
76
|
+
validate_class!(current_user_proc, [Proc], "current_user")
|
|
77
|
+
|
|
78
|
+
@current_user = current_user_proc
|
|
79
|
+
end
|
|
53
80
|
end
|
|
54
81
|
end
|
|
55
82
|
end
|
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
module Sidekiq
|
|
2
|
+
module Tasks
|
|
3
|
+
module Storage
|
|
4
|
+
class Base
|
|
5
|
+
ERROR_MESSAGE_MAX_LENGTH = 255
|
|
6
|
+
|
|
7
|
+
attr_reader :task_name, :history_limit
|
|
8
|
+
|
|
9
|
+
def initialize(task_name, history_limit: nil)
|
|
10
|
+
@task_name = task_name
|
|
11
|
+
@history_limit = history_limit
|
|
12
|
+
end
|
|
13
|
+
|
|
14
|
+
# Returns the last enqueue time for the task.
|
|
15
|
+
#
|
|
16
|
+
# @abstract Subclasses must implement this method.
|
|
17
|
+
# @return [Time, NilClass] The last enqueue time or nil.
|
|
18
|
+
# @raise [NotImplementedError] If the method is not implemented in a subclass.
|
|
19
|
+
def last_enqueue_at
|
|
20
|
+
raise NotImplementedError, "Storage must implement #last_enqueue_at"
|
|
21
|
+
end
|
|
22
|
+
|
|
23
|
+
# Returns the execution history for the task.
|
|
24
|
+
#
|
|
25
|
+
# @abstract Subclasses must implement this method.
|
|
26
|
+
# @return [Array<Hash>] The execution history entries.
|
|
27
|
+
# @raise [NotImplementedError] If the method is not implemented in a subclass.
|
|
28
|
+
def history
|
|
29
|
+
raise NotImplementedError, "Storage must implement #history"
|
|
30
|
+
end
|
|
31
|
+
|
|
32
|
+
# Stores enqueue information for the task.
|
|
33
|
+
#
|
|
34
|
+
# @abstract Subclasses must implement this method.
|
|
35
|
+
# @param jid [String] The Sidekiq job ID.
|
|
36
|
+
# @param args [Hash] The arguments passed to the task.
|
|
37
|
+
# @param user [Hash, NilClass] The user who enqueued the task.
|
|
38
|
+
# @raise [NotImplementedError] If the method is not implemented in a subclass.
|
|
39
|
+
def store_enqueue(_jid, _args, user: nil)
|
|
40
|
+
raise NotImplementedError, "Storage must implement #store_enqueue"
|
|
41
|
+
end
|
|
42
|
+
|
|
43
|
+
# Stores execution time for a specific history entry.
|
|
44
|
+
#
|
|
45
|
+
# @abstract Subclasses must implement this method.
|
|
46
|
+
# @param jid [String] The Sidekiq job ID.
|
|
47
|
+
# @param time_key [String] The time key to store (e.g. "executed_at", "finished_at").
|
|
48
|
+
# @raise [NotImplementedError] If the method is not implemented in a subclass.
|
|
49
|
+
def store_execution(_jid, _time_key)
|
|
50
|
+
raise NotImplementedError, "Storage must implement #store_execution"
|
|
51
|
+
end
|
|
52
|
+
|
|
53
|
+
# Stores an execution error for a specific history entry.
|
|
54
|
+
#
|
|
55
|
+
# @abstract Subclasses must implement this method.
|
|
56
|
+
# @param jid [String] The Sidekiq job ID.
|
|
57
|
+
# @param error [Exception] The error that occurred during execution.
|
|
58
|
+
# @raise [NotImplementedError] If the method is not implemented in a subclass.
|
|
59
|
+
def store_execution_error(_jid, _error)
|
|
60
|
+
raise NotImplementedError, "Storage must implement #store_execution_error"
|
|
61
|
+
end
|
|
62
|
+
|
|
63
|
+
protected
|
|
64
|
+
|
|
65
|
+
def truncate_message(message, max_length)
|
|
66
|
+
return message if message.length <= max_length
|
|
67
|
+
|
|
68
|
+
"#{message[0...(max_length - 3)]}..."
|
|
69
|
+
end
|
|
70
|
+
end
|
|
71
|
+
end
|
|
72
|
+
end
|
|
73
|
+
end
|
|
@@ -0,0 +1,89 @@
|
|
|
1
|
+
module Sidekiq
|
|
2
|
+
module Tasks
|
|
3
|
+
module Storage
|
|
4
|
+
class Redis < Base
|
|
5
|
+
JID_PREFIX = "task".freeze
|
|
6
|
+
def jid_key
|
|
7
|
+
"#{JID_PREFIX}:#{task_name}"
|
|
8
|
+
end
|
|
9
|
+
|
|
10
|
+
def history_key
|
|
11
|
+
"#{jid_key}:history"
|
|
12
|
+
end
|
|
13
|
+
|
|
14
|
+
def last_enqueue_at
|
|
15
|
+
stored_time("last_enqueue_at")
|
|
16
|
+
end
|
|
17
|
+
|
|
18
|
+
def history
|
|
19
|
+
raw_entries = Sidekiq.redis { |conn| conn.lrange(history_key, 0, -1) }
|
|
20
|
+
|
|
21
|
+
return [] unless raw_entries
|
|
22
|
+
|
|
23
|
+
raw_entries.map do |raw|
|
|
24
|
+
entry = Sidekiq.load_json(raw)
|
|
25
|
+
["enqueued_at", "executed_at", "finished_at"].each do |key|
|
|
26
|
+
entry[key] = Time.at(entry[key]) if entry[key]
|
|
27
|
+
end
|
|
28
|
+
entry
|
|
29
|
+
end
|
|
30
|
+
end
|
|
31
|
+
|
|
32
|
+
def store_history(jid, task_args, time, user: nil)
|
|
33
|
+
Sidekiq.redis do |conn|
|
|
34
|
+
task_trace = {jid: jid, task_name: task_name, args: task_args, enqueued_at: time.to_f}
|
|
35
|
+
task_trace[:user] = user if user
|
|
36
|
+
conn.lpush(history_key, Sidekiq.dump_json(task_trace))
|
|
37
|
+
conn.ltrim(history_key, 0, history_limit - 1)
|
|
38
|
+
end
|
|
39
|
+
end
|
|
40
|
+
|
|
41
|
+
def store_enqueue(jid, args, user: nil)
|
|
42
|
+
time = Time.now.to_f
|
|
43
|
+
store_time(time, "last_enqueue_at")
|
|
44
|
+
store_history(jid, args, time, user: user)
|
|
45
|
+
end
|
|
46
|
+
|
|
47
|
+
def store_execution(jid, time_key)
|
|
48
|
+
update_history_entry(jid) do |entry|
|
|
49
|
+
entry.merge(time_key => Time.now.to_f)
|
|
50
|
+
end
|
|
51
|
+
end
|
|
52
|
+
|
|
53
|
+
def store_execution_error(jid, error)
|
|
54
|
+
update_history_entry(jid) do |entry|
|
|
55
|
+
error_message = truncate_message("#{error.class}: #{error.message}", ERROR_MESSAGE_MAX_LENGTH)
|
|
56
|
+
entry.merge("error" => error_message)
|
|
57
|
+
end
|
|
58
|
+
end
|
|
59
|
+
|
|
60
|
+
private
|
|
61
|
+
|
|
62
|
+
def store_time(time, time_key)
|
|
63
|
+
Sidekiq.redis { |conn| conn.hset(jid_key, time_key, time.to_f) }
|
|
64
|
+
end
|
|
65
|
+
|
|
66
|
+
def stored_time(time_key)
|
|
67
|
+
timestamp = Sidekiq.redis { |conn| conn.hget(jid_key, time_key) }
|
|
68
|
+
|
|
69
|
+
[nil, ""].include?(timestamp) ? nil : Time.at(timestamp.to_f)
|
|
70
|
+
end
|
|
71
|
+
|
|
72
|
+
def update_history_entry(jid)
|
|
73
|
+
Sidekiq.redis do |conn|
|
|
74
|
+
entries = conn.lrange(history_key, 0, -1)
|
|
75
|
+
|
|
76
|
+
entries.each_with_index do |raw, index|
|
|
77
|
+
entry = Sidekiq.load_json(raw)
|
|
78
|
+
next unless entry["jid"] == jid
|
|
79
|
+
|
|
80
|
+
updated_entry = yield(entry)
|
|
81
|
+
conn.lset(history_key, index, Sidekiq.dump_json(updated_entry))
|
|
82
|
+
break
|
|
83
|
+
end
|
|
84
|
+
end
|
|
85
|
+
end
|
|
86
|
+
end
|
|
87
|
+
end
|
|
88
|
+
end
|
|
89
|
+
end
|
|
@@ -1,101 +1,8 @@
|
|
|
1
1
|
module Sidekiq
|
|
2
2
|
module Tasks
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
ERROR_MESSAGE_MAX_LENGTH = 255
|
|
7
|
-
|
|
8
|
-
attr_reader :task_name
|
|
9
|
-
|
|
10
|
-
def initialize(task_name)
|
|
11
|
-
@task_name = task_name
|
|
12
|
-
end
|
|
13
|
-
|
|
14
|
-
def jid_key
|
|
15
|
-
"#{JID_PREFIX}:#{task_name}"
|
|
16
|
-
end
|
|
17
|
-
|
|
18
|
-
def history_key
|
|
19
|
-
"#{jid_key}:history"
|
|
20
|
-
end
|
|
21
|
-
|
|
22
|
-
def last_enqueue_at
|
|
23
|
-
stored_time("last_enqueue_at")
|
|
24
|
-
end
|
|
25
|
-
|
|
26
|
-
def history
|
|
27
|
-
raw_entries = Sidekiq.redis { |conn| conn.lrange(history_key, 0, -1) }
|
|
28
|
-
|
|
29
|
-
return [] unless raw_entries
|
|
30
|
-
|
|
31
|
-
raw_entries.map do |raw|
|
|
32
|
-
entry = Sidekiq.load_json(raw)
|
|
33
|
-
%w[enqueued_at executed_at finished_at].each do |key|
|
|
34
|
-
entry[key] = Time.at(entry[key]) if entry[key]
|
|
35
|
-
end
|
|
36
|
-
entry
|
|
37
|
-
end
|
|
38
|
-
end
|
|
39
|
-
|
|
40
|
-
def store_history(jid, task_args, time)
|
|
41
|
-
Sidekiq.redis do |conn|
|
|
42
|
-
task_trace = {jid: jid, name: task_name, args: task_args, enqueued_at: time.to_f}
|
|
43
|
-
conn.lpush(history_key, Sidekiq.dump_json(task_trace))
|
|
44
|
-
conn.ltrim(history_key, 0, HISTORY_LIMIT - 1)
|
|
45
|
-
end
|
|
46
|
-
end
|
|
47
|
-
|
|
48
|
-
def store_enqueue(jid, args)
|
|
49
|
-
time = Time.now.to_f
|
|
50
|
-
store_time(time, "last_enqueue_at")
|
|
51
|
-
store_history(jid, args, time)
|
|
52
|
-
end
|
|
53
|
-
|
|
54
|
-
def store_execution(jid, time_key)
|
|
55
|
-
update_history_entry(jid) do |entry|
|
|
56
|
-
entry.merge(time_key => Time.now.to_f)
|
|
57
|
-
end
|
|
58
|
-
end
|
|
59
|
-
|
|
60
|
-
def store_execution_error(jid, error)
|
|
61
|
-
update_history_entry(jid) do |entry|
|
|
62
|
-
error_message = truncate_message("#{error.class}: #{error.message}", ERROR_MESSAGE_MAX_LENGTH)
|
|
63
|
-
entry.merge("error" => error_message)
|
|
64
|
-
end
|
|
65
|
-
end
|
|
66
|
-
|
|
67
|
-
private
|
|
68
|
-
|
|
69
|
-
def truncate_message(message, max_length)
|
|
70
|
-
return message if message.length <= max_length
|
|
71
|
-
|
|
72
|
-
"#{message[0...(max_length - 3)]}..."
|
|
73
|
-
end
|
|
74
|
-
|
|
75
|
-
def store_time(time, time_key)
|
|
76
|
-
Sidekiq.redis { |conn| conn.hset(jid_key, time_key, time.to_f) }
|
|
77
|
-
end
|
|
78
|
-
|
|
79
|
-
def stored_time(time_key)
|
|
80
|
-
timestamp = Sidekiq.redis { |conn| conn.hget(jid_key, time_key) }
|
|
81
|
-
|
|
82
|
-
[nil, ""].include?(timestamp) ? nil : Time.at(timestamp.to_f)
|
|
83
|
-
end
|
|
84
|
-
|
|
85
|
-
def update_history_entry(jid)
|
|
86
|
-
Sidekiq.redis do |conn|
|
|
87
|
-
entries = conn.lrange(history_key, 0, -1)
|
|
88
|
-
|
|
89
|
-
entries.each_with_index do |raw, index|
|
|
90
|
-
entry = Sidekiq.load_json(raw)
|
|
91
|
-
next unless entry["jid"] == jid
|
|
92
|
-
|
|
93
|
-
updated_entry = yield(entry)
|
|
94
|
-
conn.lset(history_key, index, Sidekiq.dump_json(updated_entry))
|
|
95
|
-
break
|
|
96
|
-
end
|
|
97
|
-
end
|
|
98
|
-
end
|
|
3
|
+
module Storage
|
|
4
|
+
autoload :Base, "sidekiq/tasks/storage/base"
|
|
5
|
+
autoload :Redis, "sidekiq/tasks/storage/redis"
|
|
99
6
|
end
|
|
100
7
|
end
|
|
101
8
|
end
|
data/lib/sidekiq/tasks/task.rb
CHANGED
|
@@ -22,10 +22,10 @@ module Sidekiq
|
|
|
22
22
|
validate_class!(strategy, [Sidekiq::Tasks::Strategies::Base], "strategy")
|
|
23
23
|
end
|
|
24
24
|
|
|
25
|
-
def enqueue(params = {})
|
|
25
|
+
def enqueue(params = {}, user: nil)
|
|
26
26
|
jid = strategy.enqueue_task(name, params)
|
|
27
27
|
|
|
28
|
-
storage.store_enqueue(jid, params)
|
|
28
|
+
storage.store_enqueue(jid, params, user: user)
|
|
29
29
|
end
|
|
30
30
|
|
|
31
31
|
def execute(params = {}, jid: nil)
|
|
@@ -33,7 +33,7 @@ module Sidekiq
|
|
|
33
33
|
|
|
34
34
|
begin
|
|
35
35
|
strategy.execute_task(name, params)
|
|
36
|
-
rescue => e
|
|
36
|
+
rescue StandardError, SystemExit => e
|
|
37
37
|
storage.store_execution(jid, "finished_at")
|
|
38
38
|
storage.store_execution_error(jid, e)
|
|
39
39
|
raise
|
|
@@ -43,7 +43,7 @@ module Sidekiq
|
|
|
43
43
|
end
|
|
44
44
|
|
|
45
45
|
def storage
|
|
46
|
-
@_storage ||= Sidekiq::Tasks
|
|
46
|
+
@_storage ||= Sidekiq::Tasks.config.storage.new(name, history_limit: Sidekiq::Tasks.config.history_limit)
|
|
47
47
|
end
|
|
48
48
|
end
|
|
49
49
|
end
|
|
@@ -32,6 +32,14 @@ module Sidekiq
|
|
|
32
32
|
"'#{name}' must be one of #{expected_values.map(&:inspect).join(" or ")} but received #{value.inspect}"
|
|
33
33
|
end
|
|
34
34
|
module_function :validate_expected_values!
|
|
35
|
+
|
|
36
|
+
def validate_subclass!(klass, base_class, name = nil)
|
|
37
|
+
return if klass.is_a?(Class) && klass <= base_class
|
|
38
|
+
|
|
39
|
+
raise Sidekiq::Tasks::ArgumentError,
|
|
40
|
+
"'#{name}' must be a class inheriting from #{base_class.name} but received #{klass.inspect}"
|
|
41
|
+
end
|
|
42
|
+
module_function :validate_subclass!
|
|
35
43
|
end
|
|
36
44
|
end
|
|
37
45
|
end
|
|
@@ -2,6 +2,7 @@ require "sidekiq/tasks/web/helpers/application_helper"
|
|
|
2
2
|
require "sidekiq/tasks/web/helpers/tag_helper"
|
|
3
3
|
require "sidekiq/tasks/web/helpers/task_helper"
|
|
4
4
|
require "sidekiq/tasks/web/helpers/pagination_helper"
|
|
5
|
+
require "sidekiq/tasks/web/helpers/sort_helper"
|
|
5
6
|
require "sidekiq/tasks/web/search"
|
|
6
7
|
require "sidekiq/tasks/web/pagination"
|
|
7
8
|
require "sidekiq/tasks/web/params"
|
|
@@ -15,11 +16,12 @@ module Sidekiq
|
|
|
15
16
|
app.helpers(Sidekiq::Tasks::Web::Helpers::TagHelper)
|
|
16
17
|
app.helpers(Sidekiq::Tasks::Web::Helpers::TaskHelper)
|
|
17
18
|
app.helpers(Sidekiq::Tasks::Web::Helpers::PaginationHelper)
|
|
19
|
+
app.helpers(Sidekiq::Tasks::Web::Helpers::SortHelper)
|
|
18
20
|
|
|
19
21
|
app.get "/tasks" do
|
|
20
22
|
authorize!
|
|
21
23
|
|
|
22
|
-
@search = Sidekiq::Tasks::Web::Search.new(fetch_params(:count, :page, :filter))
|
|
24
|
+
@search = Sidekiq::Tasks::Web::Search.new(fetch_params(:count, :page, :filter, :sort, :direction))
|
|
23
25
|
|
|
24
26
|
erb(read_view(:tasks), locals: {search: @search})
|
|
25
27
|
end
|
|
@@ -29,7 +31,22 @@ module Sidekiq
|
|
|
29
31
|
|
|
30
32
|
@task = find_task!(env["rack.route_params"][:name])
|
|
31
33
|
|
|
32
|
-
|
|
34
|
+
history = @task.history
|
|
35
|
+
per_page = 10
|
|
36
|
+
page = [fetch_param("page").to_i, 1].max
|
|
37
|
+
total_pages = [(history.size.to_f / per_page).ceil, 1].max
|
|
38
|
+
history_entries = history.slice((page - 1) * per_page, per_page) || []
|
|
39
|
+
|
|
40
|
+
erb(
|
|
41
|
+
read_view(:task),
|
|
42
|
+
locals: {
|
|
43
|
+
task: @task,
|
|
44
|
+
history_entries: history_entries,
|
|
45
|
+
history_page: page,
|
|
46
|
+
history_total_pages: total_pages,
|
|
47
|
+
history_total_count: history.size,
|
|
48
|
+
}
|
|
49
|
+
)
|
|
33
50
|
rescue Sidekiq::Tasks::NotFoundError
|
|
34
51
|
throw :halt, [404, {Rack::CONTENT_TYPE => "text/plain"}, ["Task not found"]]
|
|
35
52
|
end
|
|
@@ -44,7 +61,8 @@ module Sidekiq
|
|
|
44
61
|
task = find_task!(env["rack.route_params"][:name])
|
|
45
62
|
args = Sidekiq::Tasks::Web::Params.new(task, fetch_param("args")).permit!
|
|
46
63
|
|
|
47
|
-
|
|
64
|
+
current_user = Sidekiq::Tasks.config.current_user&.call(env)
|
|
65
|
+
task.enqueue(args, user: current_user)
|
|
48
66
|
|
|
49
67
|
redirect(task_url(root_path, task))
|
|
50
68
|
rescue Sidekiq::Tasks::ArgumentError => e
|
|
@@ -6,22 +6,29 @@ module Sidekiq
|
|
|
6
6
|
extend self
|
|
7
7
|
include Sidekiq::Tasks::Web::Helpers::TagHelper
|
|
8
8
|
|
|
9
|
-
def
|
|
9
|
+
def pagination_base_url(search, root_path)
|
|
10
|
+
query_params = {
|
|
11
|
+
filter: search.filter.to_s,
|
|
12
|
+
count: search.count,
|
|
13
|
+
sort: search.sort,
|
|
14
|
+
direction: search.direction,
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
"#{root_path}tasks?#{URI.encode_www_form(query_params)}"
|
|
18
|
+
end
|
|
19
|
+
|
|
20
|
+
def build_pagination_link(link, base_url)
|
|
21
|
+
separator = base_url.include?("?") ? "&" : "?"
|
|
22
|
+
|
|
10
23
|
build_tag(:li, class: "st-page-item") do
|
|
11
24
|
build_tag(
|
|
12
25
|
:a,
|
|
13
26
|
link[:text],
|
|
14
27
|
class: build_classes("st-page-link", disabled: link[:disabled], active: link[:active]),
|
|
15
|
-
href:
|
|
28
|
+
href: "#{base_url}#{separator}page=#{link[:page]}"
|
|
16
29
|
)
|
|
17
30
|
end
|
|
18
31
|
end
|
|
19
|
-
|
|
20
|
-
private
|
|
21
|
-
|
|
22
|
-
def pagination_url(root_path, search, page)
|
|
23
|
-
"#{root_path}tasks?filter=#{ERB::Util.url_encode(search.filter)}&count=#{search.count}&page=#{page}"
|
|
24
|
-
end
|
|
25
32
|
end
|
|
26
33
|
end
|
|
27
34
|
end
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
module Sidekiq
|
|
2
|
+
module Tasks
|
|
3
|
+
module Web
|
|
4
|
+
module Helpers
|
|
5
|
+
module SortHelper
|
|
6
|
+
extend self
|
|
7
|
+
|
|
8
|
+
def sort_header_url(search, root_path, column)
|
|
9
|
+
next_direction = search.toggle_direction(column)
|
|
10
|
+
|
|
11
|
+
query_params = {filter: search.filter.to_s, count: search.count}
|
|
12
|
+
query_params.merge!(sort: column, direction: next_direction) if next_direction
|
|
13
|
+
|
|
14
|
+
"#{root_path}tasks?#{URI.encode_www_form(query_params)}"
|
|
15
|
+
end
|
|
16
|
+
|
|
17
|
+
def sort_header_classes(search, column)
|
|
18
|
+
css = "st-sortable"
|
|
19
|
+
css += " st-sorted-#{search.direction}" if search.sorted_by?(column)
|
|
20
|
+
css
|
|
21
|
+
end
|
|
22
|
+
end
|
|
23
|
+
end
|
|
24
|
+
end
|
|
25
|
+
end
|
|
26
|
+
end
|
|
@@ -3,6 +3,8 @@ module Sidekiq
|
|
|
3
3
|
module Web
|
|
4
4
|
class Search
|
|
5
5
|
DEFAULT_COUNT = 15
|
|
6
|
+
SORT_COLUMNS = ["name", "last_enqueued"].freeze
|
|
7
|
+
SORT_DIRECTIONS = ["asc", "desc"].freeze
|
|
6
8
|
|
|
7
9
|
def self.count_options
|
|
8
10
|
(0..3).map { |index| DEFAULT_COUNT * (2**index) }
|
|
@@ -15,7 +17,7 @@ module Sidekiq
|
|
|
15
17
|
end
|
|
16
18
|
|
|
17
19
|
def tasks
|
|
18
|
-
@_tasks ||=
|
|
20
|
+
@_tasks ||= sorted_collection.slice(offset, count) || []
|
|
19
21
|
end
|
|
20
22
|
|
|
21
23
|
def filtered_collection
|
|
@@ -40,6 +42,25 @@ module Sidekiq
|
|
|
40
42
|
requested_page.positive? ? requested_page : 1
|
|
41
43
|
end
|
|
42
44
|
|
|
45
|
+
def sort
|
|
46
|
+
SORT_COLUMNS.include?(params[:sort]) ? params[:sort] : SORT_COLUMNS.first
|
|
47
|
+
end
|
|
48
|
+
|
|
49
|
+
def direction
|
|
50
|
+
SORT_DIRECTIONS.include?(params[:direction]) ? params[:direction] : SORT_DIRECTIONS.first
|
|
51
|
+
end
|
|
52
|
+
|
|
53
|
+
def toggle_direction(column)
|
|
54
|
+
return "asc" unless sort == column
|
|
55
|
+
return "desc" if direction == "asc"
|
|
56
|
+
|
|
57
|
+
nil
|
|
58
|
+
end
|
|
59
|
+
|
|
60
|
+
def sorted_by?(column)
|
|
61
|
+
sort == column
|
|
62
|
+
end
|
|
63
|
+
|
|
43
64
|
def total_pages
|
|
44
65
|
(filtered_collection.size.to_f / count).ceil
|
|
45
66
|
end
|
|
@@ -47,6 +68,28 @@ module Sidekiq
|
|
|
47
68
|
def offset
|
|
48
69
|
(page - 1) * count
|
|
49
70
|
end
|
|
71
|
+
|
|
72
|
+
private
|
|
73
|
+
|
|
74
|
+
def sorted_collection
|
|
75
|
+
sorted = filtered_collection.sort_by { |task| sort_value(task) }
|
|
76
|
+
direction == "desc" ? sorted.reverse : sorted
|
|
77
|
+
end
|
|
78
|
+
|
|
79
|
+
def sort_value(task)
|
|
80
|
+
case sort
|
|
81
|
+
when "name"
|
|
82
|
+
task.name.to_s.downcase
|
|
83
|
+
when "last_enqueued"
|
|
84
|
+
sort_value_for_time(task.last_enqueue_at)
|
|
85
|
+
end
|
|
86
|
+
end
|
|
87
|
+
|
|
88
|
+
def sort_value_for_time(time)
|
|
89
|
+
return time.to_f if time
|
|
90
|
+
|
|
91
|
+
direction == "asc" ? Float::INFINITY : -Float::INFINITY
|
|
92
|
+
end
|
|
50
93
|
end
|
|
51
94
|
end
|
|
52
95
|
end
|
data/lib/sidekiq/tasks.rb
CHANGED
|
@@ -3,6 +3,7 @@
|
|
|
3
3
|
require "rake"
|
|
4
4
|
|
|
5
5
|
require_relative "tasks/errors"
|
|
6
|
+
require_relative "tasks/storage"
|
|
6
7
|
require_relative "tasks/strategies"
|
|
7
8
|
require_relative "tasks/validations"
|
|
8
9
|
require_relative "tasks/version"
|
|
@@ -12,7 +13,6 @@ module Sidekiq
|
|
|
12
13
|
autoload :Config, "sidekiq/tasks/config"
|
|
13
14
|
autoload :Job, "sidekiq/tasks/job"
|
|
14
15
|
autoload :Set, "sidekiq/tasks/set"
|
|
15
|
-
autoload :Storage, "sidekiq/tasks/storage"
|
|
16
16
|
autoload :Task, "sidekiq/tasks/task"
|
|
17
17
|
autoload :TaskMetadata, "sidekiq/tasks/task_metadata"
|
|
18
18
|
|
|
@@ -22,6 +22,7 @@
|
|
|
22
22
|
|
|
23
23
|
.st-table-container {
|
|
24
24
|
overflow: auto;
|
|
25
|
+
max-width: 100%;
|
|
25
26
|
}
|
|
26
27
|
|
|
27
28
|
.st-table {
|
|
@@ -64,9 +65,42 @@
|
|
|
64
65
|
border-radius: 2px;
|
|
65
66
|
padding: 0.2em 0.4em;
|
|
66
67
|
border-radius: 0.2em;
|
|
68
|
+
display: block;
|
|
69
|
+
max-width: 200px;
|
|
70
|
+
overflow: auto;
|
|
67
71
|
}
|
|
68
72
|
|
|
69
73
|
.st-table .st-desc-cell {
|
|
70
74
|
white-space: pre-wrap;
|
|
71
75
|
word-wrap: break-word;
|
|
72
76
|
}
|
|
77
|
+
|
|
78
|
+
.st-sortable {
|
|
79
|
+
color: inherit;
|
|
80
|
+
text-decoration: none;
|
|
81
|
+
cursor: pointer;
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
.st-sortable:hover {
|
|
85
|
+
text-decoration: underline;
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
.st-sorted-asc::after,
|
|
89
|
+
.st-sorted-desc::after {
|
|
90
|
+
content: "";
|
|
91
|
+
display: inline-block;
|
|
92
|
+
margin-left: 5px;
|
|
93
|
+
width: 0;
|
|
94
|
+
height: 0;
|
|
95
|
+
vertical-align: middle;
|
|
96
|
+
border-left: 4px solid transparent;
|
|
97
|
+
border-right: 4px solid transparent;
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
.st-sorted-asc::after {
|
|
101
|
+
border-bottom: 4px solid currentColor;
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
.st-sorted-desc::after {
|
|
105
|
+
border-top: 4px solid currentColor;
|
|
106
|
+
}
|
data/web/locales/en.yml
CHANGED
data/web/locales/fr.yml
CHANGED
data/web/views/_pagination.erb
CHANGED
|
@@ -1,14 +1,14 @@
|
|
|
1
|
-
<% if
|
|
1
|
+
<% if total_pages > 1 %>
|
|
2
2
|
<div class="st-pagination-wrapper">
|
|
3
3
|
<div>
|
|
4
4
|
<small class="st-text-primary">
|
|
5
|
-
<%=
|
|
5
|
+
<%= displayed_count %> / <%= total_count %>
|
|
6
6
|
</small>
|
|
7
7
|
</div>
|
|
8
8
|
|
|
9
9
|
<ul class="st-pagination">
|
|
10
|
-
<% Sidekiq::Tasks::Web::Pagination.new(
|
|
11
|
-
<%=
|
|
10
|
+
<% Sidekiq::Tasks::Web::Pagination.new(page, total_pages).links.each do |link| %>
|
|
11
|
+
<%= build_pagination_link(link, base_url) %>
|
|
12
12
|
<% end %>
|
|
13
13
|
</ul>
|
|
14
14
|
</div>
|
data/web/views/task.erb
CHANGED
|
@@ -6,7 +6,7 @@
|
|
|
6
6
|
<% end %>
|
|
7
7
|
<% else %>
|
|
8
8
|
<% add_to_head do %>
|
|
9
|
-
<link href="<%= root_path %>tasks/css/ext.css" media="screen" rel="stylesheet" type="text/css"
|
|
9
|
+
<link href="<%= root_path %>tasks/css/ext.css" media="screen" rel="stylesheet" type="text/css">
|
|
10
10
|
<% end %>
|
|
11
11
|
<% end %>
|
|
12
12
|
|
|
@@ -42,7 +42,7 @@
|
|
|
42
42
|
</header>
|
|
43
43
|
|
|
44
44
|
<div class="st-table-container">
|
|
45
|
-
<% if
|
|
45
|
+
<% if history_entries.empty? %>
|
|
46
46
|
<p><%= t("no_history") %></p>
|
|
47
47
|
<% else %>
|
|
48
48
|
<table class="st-table">
|
|
@@ -50,6 +50,9 @@
|
|
|
50
50
|
<tr>
|
|
51
51
|
<th><%= t("jid") %></th>
|
|
52
52
|
<th><%= t("args") %></th>
|
|
53
|
+
<% if Sidekiq::Tasks.config.current_user %>
|
|
54
|
+
<th><%= t("enqueued_by") %></th>
|
|
55
|
+
<% end %>
|
|
53
56
|
<th><%= t("enqueued") %></th>
|
|
54
57
|
<th><%= t("executed") %></th>
|
|
55
58
|
<th><%= t("duration") %></th>
|
|
@@ -57,12 +60,17 @@
|
|
|
57
60
|
</tr>
|
|
58
61
|
</thead>
|
|
59
62
|
<tbody>
|
|
60
|
-
<%
|
|
63
|
+
<% history_entries.each do |jid_history| %>
|
|
61
64
|
<tr>
|
|
62
65
|
<td><%= jid_history["jid"] %></td>
|
|
63
66
|
<td>
|
|
64
67
|
<code><%= jid_history["args"] %></code>
|
|
65
68
|
</td>
|
|
69
|
+
<% if Sidekiq::Tasks.config.current_user %>
|
|
70
|
+
<td>
|
|
71
|
+
<code><%= jid_history["user"] || "-" %></code>
|
|
72
|
+
</td>
|
|
73
|
+
<% end %>
|
|
66
74
|
<td>
|
|
67
75
|
<%= jid_history["enqueued_at"] ? jid_history["enqueued_at"].strftime(t("task_time")) : "-" %>
|
|
68
76
|
</td>
|
|
@@ -77,13 +85,24 @@
|
|
|
77
85
|
:span,
|
|
78
86
|
t(task_status(jid_history).to_s).capitalize,
|
|
79
87
|
class: "st-status-badge #{task_status(jid_history)}",
|
|
80
|
-
"data-tooltip": jid_history["error"]
|
|
88
|
+
"data-tooltip": jid_history["error"]
|
|
81
89
|
) %>
|
|
82
90
|
</td>
|
|
83
91
|
</tr>
|
|
84
92
|
<% end %>
|
|
85
93
|
</tbody>
|
|
86
94
|
</table>
|
|
95
|
+
|
|
96
|
+
<%= erb(
|
|
97
|
+
read_view(:_pagination),
|
|
98
|
+
locals: {
|
|
99
|
+
displayed_count: history_entries.size,
|
|
100
|
+
total_count: history_total_count,
|
|
101
|
+
page: history_page,
|
|
102
|
+
total_pages: history_total_pages,
|
|
103
|
+
base_url: task_url(root_path, task),
|
|
104
|
+
}
|
|
105
|
+
) %>
|
|
87
106
|
<% end %>
|
|
88
107
|
</div>
|
|
89
108
|
|
|
@@ -100,7 +119,7 @@
|
|
|
100
119
|
<% task.args.each do |arg| %>
|
|
101
120
|
<div class="st-form-group">
|
|
102
121
|
<label for="<%= arg %>" class="st-label"><%= arg %></label>
|
|
103
|
-
<input type="text" id="<%= arg %>" class="st-input" name="args[<%= arg %>]"
|
|
122
|
+
<input type="text" id="<%= arg %>" class="st-input" name="args[<%= arg %>]" autocomplete="off">
|
|
104
123
|
</div>
|
|
105
124
|
<% end %>
|
|
106
125
|
</div>
|
|
@@ -109,7 +128,15 @@
|
|
|
109
128
|
<label for="envConfirmationInput" class="st-label">
|
|
110
129
|
<%= t("env_confirmation", current_env: current_env) %>
|
|
111
130
|
</label>
|
|
112
|
-
<input
|
|
131
|
+
<input
|
|
132
|
+
type="text"
|
|
133
|
+
id="envConfirmationInput"
|
|
134
|
+
class="st-input"
|
|
135
|
+
name="env_confirmation"
|
|
136
|
+
data-current-env="<%= current_env %>"
|
|
137
|
+
autocomplete="off"
|
|
138
|
+
required
|
|
139
|
+
>
|
|
113
140
|
</div>
|
|
114
141
|
|
|
115
142
|
<button type="submit" class="st-button" id="submitBtn" disabled>
|
data/web/views/tasks.erb
CHANGED
|
@@ -6,18 +6,22 @@
|
|
|
6
6
|
<% end %>
|
|
7
7
|
<% else %>
|
|
8
8
|
<% add_to_head do %>
|
|
9
|
-
<link href="<%= root_path %>tasks/css/ext.css" media="screen" rel="stylesheet" type="text/css"
|
|
9
|
+
<link href="<%= root_path %>tasks/css/ext.css" media="screen" rel="stylesheet" type="text/css">
|
|
10
10
|
<% end %>
|
|
11
11
|
<% end %>
|
|
12
12
|
|
|
13
13
|
<header class="st-header">
|
|
14
14
|
<form action="<%= root_path %>tasks" method="get" class="st-search-form">
|
|
15
|
+
<input type="hidden" name="sort" value="<%= @search.sort %>">
|
|
16
|
+
<input type="hidden" name="direction" value="<%= @search.direction %>">
|
|
17
|
+
|
|
15
18
|
<input
|
|
16
19
|
name="filter"
|
|
17
20
|
class="st-input"
|
|
18
21
|
type="text"
|
|
19
22
|
value="<%= Rack::Utils.escape_html(@search.filter.to_s) %>"
|
|
20
23
|
placeholder="<%= t("search") %>"
|
|
24
|
+
autocomplete="off"
|
|
21
25
|
>
|
|
22
26
|
|
|
23
27
|
<select name="count" class="st-select">
|
|
@@ -41,9 +45,23 @@
|
|
|
41
45
|
<table class="st-table">
|
|
42
46
|
<thead>
|
|
43
47
|
<tr>
|
|
44
|
-
<th
|
|
48
|
+
<th>
|
|
49
|
+
<a
|
|
50
|
+
href="<%= sort_header_url(@search, root_path, "name") %>"
|
|
51
|
+
class="<%= sort_header_classes(@search, "name") %>"
|
|
52
|
+
>
|
|
53
|
+
<%= t("name") %>
|
|
54
|
+
</a>
|
|
55
|
+
</th>
|
|
45
56
|
<th><%= t("description") %></th>
|
|
46
|
-
<th
|
|
57
|
+
<th>
|
|
58
|
+
<a
|
|
59
|
+
href="<%= sort_header_url(@search, root_path, "last_enqueued") %>"
|
|
60
|
+
class="<%= sort_header_classes(@search, "last_enqueued") %>"
|
|
61
|
+
>
|
|
62
|
+
<%= t("last_enqueued") %>
|
|
63
|
+
</a>
|
|
64
|
+
</th>
|
|
47
65
|
</tr>
|
|
48
66
|
</thead>
|
|
49
67
|
<tbody>
|
|
@@ -62,6 +80,15 @@
|
|
|
62
80
|
</tbody>
|
|
63
81
|
</table>
|
|
64
82
|
|
|
65
|
-
<%= erb(
|
|
83
|
+
<%= erb(
|
|
84
|
+
read_view(:_pagination),
|
|
85
|
+
locals: {
|
|
86
|
+
displayed_count: @search.tasks.size,
|
|
87
|
+
total_count: Sidekiq::Tasks.tasks.size,
|
|
88
|
+
page: @search.page,
|
|
89
|
+
total_pages: @search.total_pages,
|
|
90
|
+
base_url: pagination_base_url(@search, root_path),
|
|
91
|
+
}
|
|
92
|
+
) %>
|
|
66
93
|
<% end %>
|
|
67
94
|
</div>
|
metadata
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: sidekiq-tasks
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 0.
|
|
4
|
+
version: 1.0.0
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Victor
|
|
@@ -236,6 +236,7 @@ files:
|
|
|
236
236
|
- LICENSE.txt
|
|
237
237
|
- README.md
|
|
238
238
|
- Rakefile
|
|
239
|
+
- docs/custom_storage.md
|
|
239
240
|
- docs/task.png
|
|
240
241
|
- lib/sidekiq-tasks.rb
|
|
241
242
|
- lib/sidekiq/tasks.rb
|
|
@@ -244,6 +245,8 @@ files:
|
|
|
244
245
|
- lib/sidekiq/tasks/job.rb
|
|
245
246
|
- lib/sidekiq/tasks/set.rb
|
|
246
247
|
- lib/sidekiq/tasks/storage.rb
|
|
248
|
+
- lib/sidekiq/tasks/storage/base.rb
|
|
249
|
+
- lib/sidekiq/tasks/storage/redis.rb
|
|
247
250
|
- lib/sidekiq/tasks/strategies.rb
|
|
248
251
|
- lib/sidekiq/tasks/strategies/base.rb
|
|
249
252
|
- lib/sidekiq/tasks/strategies/rake_task.rb
|
|
@@ -260,6 +263,7 @@ files:
|
|
|
260
263
|
- lib/sidekiq/tasks/web/extension.rb
|
|
261
264
|
- lib/sidekiq/tasks/web/helpers/application_helper.rb
|
|
262
265
|
- lib/sidekiq/tasks/web/helpers/pagination_helper.rb
|
|
266
|
+
- lib/sidekiq/tasks/web/helpers/sort_helper.rb
|
|
263
267
|
- lib/sidekiq/tasks/web/helpers/tag_helper.rb
|
|
264
268
|
- lib/sidekiq/tasks/web/helpers/task_helper.rb
|
|
265
269
|
- lib/sidekiq/tasks/web/pagination.rb
|