jobs_dashboard 0.3.4

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.
Files changed (62) hide show
  1. checksums.yaml +7 -0
  2. data/.github/workflows/gem-push.yml +34 -0
  3. data/.gitignore +12 -0
  4. data/.rspec +3 -0
  5. data/CHANGELOG.md +60 -0
  6. data/Gemfile +10 -0
  7. data/Gemfile.lock +129 -0
  8. data/LICENSE.txt +21 -0
  9. data/README.md +133 -0
  10. data/Rakefile +8 -0
  11. data/app/assets/stylesheets/jobs_dashboard/application.scss +214 -0
  12. data/app/assets/stylesheets/jobs_dashboard/modules/buttons.sass +15 -0
  13. data/app/assets/stylesheets/jobs_dashboard/modules/card.sass +22 -0
  14. data/app/assets/stylesheets/jobs_dashboard/modules/description.sass +17 -0
  15. data/app/assets/stylesheets/jobs_dashboard/modules/grid.sass +17 -0
  16. data/app/assets/stylesheets/jobs_dashboard/modules/inputs.sass +31 -0
  17. data/app/assets/stylesheets/jobs_dashboard/modules/item-list.sass +10 -0
  18. data/app/assets/stylesheets/jobs_dashboard/modules/labels.sass +12 -0
  19. data/app/assets/stylesheets/jobs_dashboard/modules/statistics.sass +15 -0
  20. data/app/assets/stylesheets/jobs_dashboard/modules/table.sass +38 -0
  21. data/app/assets/stylesheets/jobs_dashboard/pages/jobs_logs/show.sass +36 -0
  22. data/app/assets/stylesheets/jobs_dashboard/reset.scss +135 -0
  23. data/app/assets/stylesheets/jobs_dashboard/variables.sass +6 -0
  24. data/app/controllers/jobs_dashboard/application_controller.rb +20 -0
  25. data/app/controllers/jobs_dashboard/dashboard_controller.rb +12 -0
  26. data/app/controllers/jobs_dashboard/job_logs_controller.rb +17 -0
  27. data/app/helpers/jobs_dashboard/application_helper.rb +31 -0
  28. data/app/models/jobs_dashboard/application_record.rb +5 -0
  29. data/app/models/jobs_dashboard/job_log.rb +32 -0
  30. data/app/views/jobs_dashboard/dashboard/_job_row.slim +8 -0
  31. data/app/views/jobs_dashboard/dashboard/index.slim +40 -0
  32. data/app/views/jobs_dashboard/job_logs/_stats.html.erb +0 -0
  33. data/app/views/jobs_dashboard/job_logs/index.slim +84 -0
  34. data/app/views/jobs_dashboard/job_logs/show.slim +89 -0
  35. data/app/views/kaminari/jobs_dashboard/_first_page.html.erb +11 -0
  36. data/app/views/kaminari/jobs_dashboard/_gap.html.erb +8 -0
  37. data/app/views/kaminari/jobs_dashboard/_last_page.html.erb +11 -0
  38. data/app/views/kaminari/jobs_dashboard/_next_page.html.erb +11 -0
  39. data/app/views/kaminari/jobs_dashboard/_page.html.erb +12 -0
  40. data/app/views/kaminari/jobs_dashboard/_paginator.html.erb +25 -0
  41. data/app/views/kaminari/jobs_dashboard/_prev_page.html.erb +11 -0
  42. data/app/views/layouts/jobs_dashboard/application.html.erb +15 -0
  43. data/app/views/shared/_logo.slim +4 -0
  44. data/app/views/shared/_navigation.slim +12 -0
  45. data/bin/console +15 -0
  46. data/bin/setup +8 -0
  47. data/config/initializers/ransack.rb +11 -0
  48. data/config/locales/default.fr.yml +219 -0
  49. data/config/locales/jobs_dashboard.en.yml +33 -0
  50. data/config/locales/jobs_dashboard.fr.yml +37 -0
  51. data/config/routes.rb +5 -0
  52. data/jobs_dashboard.gemspec +38 -0
  53. data/lib/generators/active_record/job_log_migration_generator.rb +24 -0
  54. data/lib/generators/active_record/templates/migration.rb +20 -0
  55. data/lib/jobs_dashboard/client_middleware.rb +56 -0
  56. data/lib/jobs_dashboard/engine.rb +18 -0
  57. data/lib/jobs_dashboard/server_middleware.rb +62 -0
  58. data/lib/jobs_dashboard/storage.rb +54 -0
  59. data/lib/jobs_dashboard/version.rb +5 -0
  60. data/lib/jobs_dashboard/worker.rb +25 -0
  61. data/lib/jobs_dashboard.rb +12 -0
  62. metadata +161 -0
@@ -0,0 +1,11 @@
1
+ <%# Link to the "Last" page
2
+ - available local variables
3
+ url: url to the last page
4
+ current_page: a page object for the currently displayed page
5
+ total_pages: total number of pages
6
+ per_page: number of items to fetch per page
7
+ remote: data-remote
8
+ -%>
9
+ <span class="last">
10
+ <%= link_to_unless current_page.last?, t('views.pagination.last').html_safe, url, remote: remote %>
11
+ </span>
@@ -0,0 +1,11 @@
1
+ <%# Link to the "Next" page
2
+ - available local variables
3
+ url: url to the next page
4
+ current_page: a page object for the currently displayed page
5
+ total_pages: total number of pages
6
+ per_page: number of items to fetch per page
7
+ remote: data-remote
8
+ -%>
9
+ <span class="next">
10
+ <%= link_to_unless current_page.last?, t('views.pagination.next').html_safe, url, rel: 'next', remote: remote %>
11
+ </span>
@@ -0,0 +1,12 @@
1
+ <%# Link showing page number
2
+ - available local variables
3
+ page: a page object for "this" page
4
+ url: url to this page
5
+ current_page: a page object for the currently displayed page
6
+ total_pages: total number of pages
7
+ per_page: number of items to fetch per page
8
+ remote: data-remote
9
+ -%>
10
+ <span class="page<%= ' current' if page.current? %>">
11
+ <%= link_to_unless page.current?, page, url, {remote: remote, rel: page.rel} %>
12
+ </span>
@@ -0,0 +1,25 @@
1
+ <%# The container tag
2
+ - available local variables
3
+ current_page: a page object for the currently displayed page
4
+ total_pages: total number of pages
5
+ per_page: number of items to fetch per page
6
+ remote: data-remote
7
+ paginator: the paginator that renders the pagination tags inside
8
+ -%>
9
+ <%= paginator.render do -%>
10
+ <nav class="pagination" role="navigation" aria-label="pager">
11
+ <%= first_page_tag unless current_page.first? %>
12
+ <%= prev_page_tag unless current_page.first? %>
13
+ <% each_page do |page| -%>
14
+ <% if page.display_tag? -%>
15
+ <%= page_tag page %>
16
+ <% elsif !page.was_truncated? -%>
17
+ <%= gap_tag %>
18
+ <% end -%>
19
+ <% end -%>
20
+ <% unless current_page.out_of_range? %>
21
+ <%= next_page_tag unless current_page.last? %>
22
+ <%= last_page_tag unless current_page.last? %>
23
+ <% end %>
24
+ </nav>
25
+ <% end -%>
@@ -0,0 +1,11 @@
1
+ <%# Link to the "Previous" page
2
+ - available local variables
3
+ url: url to the previous page
4
+ current_page: a page object for the currently displayed page
5
+ total_pages: total number of pages
6
+ per_page: number of items to fetch per page
7
+ remote: data-remote
8
+ -%>
9
+ <span class="prev">
10
+ <%= link_to_unless current_page.first?, t('views.pagination.previous').html_safe, url, rel: 'prev', remote: remote %>
11
+ </span>
@@ -0,0 +1,15 @@
1
+ <!DOCTYPE html>
2
+ <html>
3
+ <head>
4
+ <title>Jobs dashboard</title>
5
+ <%= csrf_meta_tags %>
6
+ <%= csp_meta_tag %>
7
+ <%= stylesheet_link_tag "jobs_dashboard/application", media: "all" %>
8
+ </head>
9
+ <body class="<%= controller_name %>-<%= action_name %>">
10
+ <%= render partial: 'shared/navigation' %>
11
+ <div class="layout-content">
12
+ <%= yield %>
13
+ </div>
14
+ </body>
15
+ </html>
@@ -0,0 +1,4 @@
1
+ <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512">
2
+ <!--! Font Awesome Pro 6.4.2 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license (Commercial License) Copyright 2023 Fonticons, Inc. --><
3
+ defs><style>.fa-secondary{opacity:.4}</style></>
4
+ <path class="fa-primary" d="M112 192c0-35.3 28.7-64 64-64H336c35.3 0 64 28.7 64 64V384c0 35.3-28.7 64-64 64H176c-35.3 0-64-28.7-64-64V192zm48 24v80c0 13.3 10.7 24 24 24H328c13.3 0 24-10.7 24-24V216c0-13.3-10.7-24-24-24H184c-13.3 0-24 10.7-24 24zm96 200a32 32 0 1 0 0-64 32 32 0 1 0 0 64z"/><path class="fa-secondary" d="M256 0C114.6 0 0 114.6 0 256V448c0 35.3 28.7 64 64 64h41.4l64.3-64.3C137.3 444.5 112 417.2 112 384V192c0-35.3 28.7-64 64-64H336c35.3 0 64 28.7 64 64V384c0 33.2-25.3 60.5-57.7 63.7L406.6 512H448c35.3 0 64-28.7 64-64V256C512 114.6 397.4 0 256 0zM361.4 512l-64-64H214.6l-64 64H361.4zM160 216v80c0 13.3 10.7 24 24 24H328c13.3 0 24-10.7 24-24V216c0-13.3-10.7-24-24-24H184c-13.3 0-24 10.7-24 24z"/></svg>
@@ -0,0 +1,12 @@
1
+ .navbar
2
+ .nav-wrapper
3
+ .logo
4
+ = render 'shared/logo'
5
+ ul.nav
6
+ li.nav-item class="#{'active' if current_page?(root_path)}"
7
+ = link_to t(:'jobs_dashboard.menu.dashboard'), root_path
8
+ li.nav-item class="#{'active' if params[:controller] == 'jobs_dashboard/job_logs'}"
9
+ = link_to t(:'jobs_dashboard.menu.logs'), job_logs_path
10
+ header.title-wrapper
11
+ h1
12
+ = @title || t(:"jobs_dashboard.#{controller.controller_name}.#{controller.action_name}.title")
data/bin/console ADDED
@@ -0,0 +1,15 @@
1
+ #!/usr/bin/env ruby
2
+ # frozen_string_literal: true
3
+
4
+ require "bundler/setup"
5
+ require "jobs_dashboard"
6
+
7
+ # You can add fixtures and/or initialization code here to make experimenting
8
+ # with your gem easier. You can also use a different console, if you like.
9
+
10
+ # (If you use this, don't forget to add pry to your Gemfile!)
11
+ # require "pry"
12
+ # Pry.start
13
+
14
+ require "irb"
15
+ IRB.start(__FILE__)
data/bin/setup ADDED
@@ -0,0 +1,8 @@
1
+ #!/usr/bin/env bash
2
+ set -euo pipefail
3
+ IFS=$'\n\t'
4
+ set -vx
5
+
6
+ bundle install
7
+
8
+ # Do any other automated setup that you need to do here
@@ -0,0 +1,11 @@
1
+ Ransack.configure do |config|
2
+ config.add_predicate 'dategteq',
3
+ arel_predicate: 'gteq',
4
+ formatter: proc { |v| v.beginning_of_day },
5
+ type: :date
6
+
7
+ config.add_predicate 'datelteq',
8
+ arel_predicate: 'lteq',
9
+ formatter: proc { |v| v.end_of_day },
10
+ type: :date
11
+ end
@@ -0,0 +1,219 @@
1
+ ---
2
+ fr:
3
+ activerecord:
4
+ errors:
5
+ messages:
6
+ record_invalid: 'La validation a échoué : %{errors}'
7
+ restrict_dependent_destroy:
8
+ has_one: Vous ne pouvez pas supprimer l'enregistrement car un(e) %{record}
9
+ dépendant(e) existe
10
+ has_many: Vous ne pouvez pas supprimer l'enregistrement parce que les %{record}
11
+ dépendants existent
12
+ date:
13
+ abbr_day_names:
14
+ - dim
15
+ - lun
16
+ - mar
17
+ - mer
18
+ - jeu
19
+ - ven
20
+ - sam
21
+ abbr_month_names:
22
+ -
23
+ - jan.
24
+ - fév.
25
+ - mar.
26
+ - avr.
27
+ - mai
28
+ - juin
29
+ - juil.
30
+ - août
31
+ - sept.
32
+ - oct.
33
+ - nov.
34
+ - déc.
35
+ day_names:
36
+ - dimanche
37
+ - lundi
38
+ - mardi
39
+ - mercredi
40
+ - jeudi
41
+ - vendredi
42
+ - samedi
43
+ formats:
44
+ default: "%d/%m/%Y"
45
+ long: "%e %B %Y"
46
+ short: "%e %b"
47
+ month_names:
48
+ -
49
+ - janvier
50
+ - février
51
+ - mars
52
+ - avril
53
+ - mai
54
+ - juin
55
+ - juillet
56
+ - août
57
+ - septembre
58
+ - octobre
59
+ - novembre
60
+ - décembre
61
+ order:
62
+ - :day
63
+ - :month
64
+ - :year
65
+ datetime:
66
+ distance_in_words:
67
+ about_x_hours:
68
+ one: environ une heure
69
+ other: environ %{count} heures
70
+ about_x_months:
71
+ one: environ un mois
72
+ other: environ %{count} mois
73
+ about_x_years:
74
+ one: environ un an
75
+ other: environ %{count} ans
76
+ almost_x_years:
77
+ one: presqu'un an
78
+ other: presque %{count} ans
79
+ half_a_minute: une demi‑minute
80
+ less_than_x_seconds:
81
+ zero: moins d'une seconde
82
+ one: moins d'une seconde
83
+ other: moins de %{count} secondes
84
+ less_than_x_minutes:
85
+ zero: moins d'une minute
86
+ one: moins d'une minute
87
+ other: moins de %{count} minutes
88
+ over_x_years:
89
+ one: plus d'un an
90
+ other: plus de %{count} ans
91
+ x_seconds:
92
+ one: 1 seconde
93
+ other: "%{count} secondes"
94
+ x_minutes:
95
+ one: 1 minute
96
+ other: "%{count} minutes"
97
+ x_days:
98
+ one: 1 jour
99
+ other: "%{count} jours"
100
+ x_months:
101
+ one: 1 mois
102
+ other: "%{count} mois"
103
+ x_years:
104
+ one: 1 an
105
+ other: "%{count} ans"
106
+ prompts:
107
+ second: Seconde
108
+ minute: Minute
109
+ hour: Heure
110
+ day: Jour
111
+ month: Mois
112
+ year: Année
113
+ errors:
114
+ format: "%{attribute} %{message}"
115
+ messages:
116
+ accepted: doit être accepté(e)
117
+ blank: doit être rempli(e)
118
+ confirmation: ne concorde pas avec %{attribute}
119
+ empty: doit être rempli(e)
120
+ equal_to: doit être égal à %{count}
121
+ even: doit être pair
122
+ exclusion: n'est pas disponible
123
+ greater_than: doit être supérieur à %{count}
124
+ greater_than_or_equal_to: doit être supérieur ou égal à %{count}
125
+ inclusion: n'est pas inclus(e) dans la liste
126
+ invalid: n'est pas valide
127
+ less_than: doit être inférieur à %{count}
128
+ less_than_or_equal_to: doit être inférieur ou égal à %{count}
129
+ model_invalid: 'Validation échouée : %{errors}'
130
+ not_a_number: n'est pas un nombre
131
+ not_an_integer: doit être un nombre entier
132
+ odd: doit être impair
133
+ other_than: doit être différent de %{count}
134
+ present: doit être vide
135
+ required: doit exister
136
+ taken: est déjà utilisé(e)
137
+ too_long:
138
+ one: est trop long (pas plus d'un caractère)
139
+ other: est trop long (pas plus de %{count} caractères)
140
+ too_short:
141
+ one: est trop court (au moins un caractère)
142
+ other: est trop court (au moins %{count} caractères)
143
+ wrong_length:
144
+ one: ne fait pas la bonne longueur (doit comporter un seul caractère)
145
+ other: ne fait pas la bonne longueur (doit comporter %{count} caractères)
146
+ template:
147
+ body: 'Veuillez vérifier les champs suivants : '
148
+ header:
149
+ one: 'Impossible d''enregistrer ce(tte) %{model} : 1 erreur'
150
+ other: 'Impossible d''enregistrer ce(tte) %{model} : %{count} erreurs'
151
+ helpers:
152
+ select:
153
+ prompt: Veuillez sélectionner
154
+ submit:
155
+ create: Créer un(e) %{model}
156
+ submit: Enregistrer ce(tte) %{model}
157
+ update: Modifier ce(tte) %{model}
158
+ number:
159
+ currency:
160
+ format:
161
+ delimiter: " "
162
+ format: "%n %u"
163
+ precision: 2
164
+ separator: ","
165
+ significant: false
166
+ strip_insignificant_zeros: false
167
+ unit: "€"
168
+ format:
169
+ delimiter: " "
170
+ precision: 3
171
+ separator: ","
172
+ significant: false
173
+ strip_insignificant_zeros: false
174
+ human:
175
+ decimal_units:
176
+ format: "%n %u"
177
+ units:
178
+ billion: milliard
179
+ million: million
180
+ quadrillion: million de milliards
181
+ thousand: millier
182
+ trillion: billion
183
+ unit: ''
184
+ format:
185
+ delimiter: ''
186
+ precision: 3
187
+ significant: true
188
+ strip_insignificant_zeros: true
189
+ storage_units:
190
+ format: "%n %u"
191
+ units:
192
+ byte:
193
+ one: octet
194
+ other: octets
195
+ eb: Eo
196
+ gb: Go
197
+ kb: ko
198
+ mb: Mo
199
+ pb: Po
200
+ tb: To
201
+ percentage:
202
+ format:
203
+ delimiter: ''
204
+ format: "%n%"
205
+ precision:
206
+ format:
207
+ delimiter: ''
208
+ support:
209
+ array:
210
+ last_word_connector: " et "
211
+ two_words_connector: " et "
212
+ words_connector: ", "
213
+ time:
214
+ am: am
215
+ formats:
216
+ default: "%d %B %Y %Hh %Mmin %Ss"
217
+ long: "%A %d %B %Y %Hh%M"
218
+ short: "%d %b %Hh%M"
219
+ pm: pm
@@ -0,0 +1,33 @@
1
+ en:
2
+ form:
3
+ select:
4
+ all: All
5
+ jobs_dashboard:
6
+ dashboard:
7
+ index:
8
+ title: Dashboard
9
+ statuses:
10
+ complete: Complete
11
+ queued: Queued
12
+ failed: Failed
13
+ interrupted: Interrupted
14
+ retrying: Retrying
15
+ working: Working
16
+ menu:
17
+ dashboard: Dashboard
18
+ logs: Logs
19
+ table:
20
+ headers:
21
+ finished_at: Finished at
22
+ created_at: Created at
23
+ job: Job
24
+ sidekiq_jid: Sidekiq ID
25
+ queue: Queue
26
+ status: Status
27
+ sentences:
28
+ clear_filters: Clear filters
29
+ no_ongoing_jobs: No ongoing jobs
30
+ no_awaiting_jobs: No awaiting jobs
31
+ words:
32
+ to: to
33
+ submit: 'Apply filters'
@@ -0,0 +1,37 @@
1
+ fr:
2
+ form:
3
+ select:
4
+ all: Tout
5
+ jobs_dashboard:
6
+ job_logs:
7
+ index:
8
+ title: Job logs
9
+ dashboard:
10
+ index:
11
+ title: Dashboard
12
+ statuses:
13
+ complete: Terminé
14
+ queued: En attente
15
+ failed: Échoué
16
+ interrupted: Interrompu
17
+ retrying: Nouvelle tentative
18
+ working: En cours
19
+ menu:
20
+ dashboard: Dashboard
21
+ logs: Job logs
22
+ table:
23
+ headers:
24
+ finished_at: Terminé le
25
+ created_at: Créé le
26
+ item_type: Job
27
+ job_id: Sidekiq ID
28
+ sidekiq_jid: Sidekiq ID
29
+ queue: File
30
+ status: Statut
31
+ sentences:
32
+ clear_filters: Supprimer les filtres
33
+ no_ongoing_jobs: Aucun job en cours
34
+ no_awaiting_jobs: Aucun job en attente
35
+ words:
36
+ to: au
37
+ submit: 'Filtrer'
data/config/routes.rb ADDED
@@ -0,0 +1,5 @@
1
+ JobsDashboard::Engine.routes.draw do
2
+ root 'dashboard#index'
3
+
4
+ resources :job_logs
5
+ end
@@ -0,0 +1,38 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative "lib/jobs_dashboard/version"
4
+
5
+ Gem::Specification.new do |spec|
6
+ spec.name = "jobs_dashboard"
7
+ spec.version = JobsDashboard::VERSION
8
+ spec.authors = ["Patryk"]
9
+ spec.email = ["patryk@9troisquarts.com"]
10
+
11
+ spec.summary = "Sidekiq jobs dashboard"
12
+ spec.homepage = "https://github.com/9troisquarts/jobs_dashboard"
13
+ spec.license = "MIT"
14
+
15
+ # spec.metadata["allowed_push_host"] = "TODO: Set to 'http://mygemserver.com'"
16
+
17
+ spec.metadata["homepage_uri"] = spec.homepage
18
+ spec.metadata["source_code_uri"] = "https://github.com/9troisquarts/jobs_dashboard"
19
+ # spec.metadata["changelog_uri"] = "TODO: Put your gem's CHANGELOG.md URL here."
20
+
21
+ # Specify which files should be added to the gem when it is released.
22
+ # The `git ls-files -z` loads the files in the RubyGem that have been added into git.
23
+ spec.files = Dir.chdir(File.expand_path(__dir__)) do
24
+ `git ls-files -z`.split("\x0").reject { |f| f.match(%r{\A(?:test|spec|features)/}) }
25
+ end
26
+ spec.bindir = "exe"
27
+ spec.executables = spec.files.grep(%r{\Aexe/}) { |f| File.basename(f) }
28
+ spec.require_paths = ["lib"]
29
+
30
+ # Uncomment to register a new dependency of your gem
31
+ spec.add_dependency 'sidekiq', '>= 6.0'
32
+ spec.add_dependency 'kaminari'
33
+ spec.add_dependency 'slim-rails'
34
+ spec.add_dependency 'ransack'
35
+
36
+ # For more information and examples about making a new gem, checkout our
37
+ # guide at: https://bundler.io/guides/creating_gem.html
38
+ end
@@ -0,0 +1,24 @@
1
+ require 'rails/generators/active_record'
2
+
3
+ module ActiveRecord
4
+ module Generators
5
+ class JobLogMigrationGenerator < Base
6
+ source_root File.expand_path("../templates", __FILE__)
7
+ argument :name, :type => :string, :default => "add_job_logs_table"
8
+
9
+ def create_migration_file
10
+ migration_template "migration.rb", "db/migrate/#{file_name}.rb"
11
+ end
12
+
13
+ protected
14
+
15
+ def job_log_table_name
16
+ 'jobs_dashboard_job_logs'
17
+ end
18
+
19
+ def migration_version
20
+ "[#{ActiveRecord::Migration.current_version}]"
21
+ end
22
+ end
23
+ end
24
+ end
@@ -0,0 +1,20 @@
1
+ class <%= migration_class_name %> < ActiveRecord::Migration<%= migration_version %>
2
+ def change
3
+ create_table :<%= job_log_table_name %> do |t|
4
+ t.string :sidekiq_jid, :null => false
5
+ t.string :status
6
+ t.string :item_type
7
+ t.text :args, limit: 4294967295
8
+ t.text :logs, limit: 4294967295
9
+ t.boolean :retry, null: false, default: false
10
+ t.string :queue
11
+ t.text :error_message, limit: 4294967295
12
+ t.text :backtrace, limit: 4294967295
13
+ t.datetime :finished_at
14
+ t.timestamps
15
+ end
16
+
17
+ add_index :<%= job_log_table_name %>, :sidekiq_jid, :unique => true
18
+ add_index :<%= job_log_table_name %>, :updated_at
19
+ end
20
+ end
@@ -0,0 +1,56 @@
1
+ require 'sidekiq/api'
2
+ require 'jobs_dashboard/storage'
3
+
4
+ module JobsDashboard
5
+ # Should be in the client middleware chain
6
+ class ClientMiddleware
7
+ include Storage
8
+
9
+ # Parameterized initialization, use it when adding middleware to client chain
10
+ # chain.add JobsDashboard::ClientMiddleware
11
+ # @param [Hash] opts middleware initialization options
12
+ # @option opts [Fixnum] :expiration ttl for complete jobs
13
+ def initialize(opts = {})
14
+ @expiration = opts[:expiration]
15
+ end
16
+
17
+ # Uses msg['jid'] id and puts :queued status in the job's Redis hash
18
+ # @param [Class] worker_class if includes JobsDashboard::Worker, the job gets processed with the plugin
19
+ # @param [Array] msg job arguments
20
+ # @param [String] queue the queue's name
21
+ # @param [ConnectionPool] redis_pool optional redis connection pool
22
+ def call(worker, msg, queue, redis_pool=nil)
23
+ unless get_jobs_dashboard_options(msg)[:skip]
24
+ store_for_id(msg['jid'], {
25
+ item_type: msg['class'],
26
+ retry: msg['retry'],
27
+ queue: msg['queue'],
28
+ args: msg['args'],
29
+ status: 'queued'
30
+ })
31
+ end
32
+
33
+ yield
34
+
35
+ end
36
+
37
+ private
38
+ def get_jobs_dashboard_options(msg)
39
+ klass = msg["args"][0]["job_class"] || msg["class"] rescue msg["class"]
40
+ job_class = klass.is_a?(Class) ? klass : Module.const_get(klass)
41
+ job_class.get_sidekiq_options['jobs_dashboard'] || {}
42
+ end
43
+
44
+ end
45
+
46
+ # Helper method to easily configure sidekiq-status client middleware
47
+ # whatever the Sidekiq version is.
48
+ # @param [Sidekiq] sidekiq_config the Sidekiq config
49
+ # @param [Hash] client_middleware_options client middleware initialization options
50
+ # @option client_middleware_options [Fixnum] :expiration ttl for complete jobs
51
+ def self.configure_client_middleware(sidekiq_config, client_middleware_options = {})
52
+ sidekiq_config.client_middleware do |chain|
53
+ chain.add JobsDashboard::ClientMiddleware, client_middleware_options
54
+ end
55
+ end
56
+ end
@@ -0,0 +1,18 @@
1
+ module JobsDashboard
2
+ class Engine < ::Rails::Engine
3
+ isolate_namespace JobsDashboard
4
+ initializer "jobs_dashboard.assets.precompile" do |app|
5
+ app.config.assets.precompile += %w( jobs_dashboard/application.css )
6
+ end
7
+
8
+ require 'slim-rails'
9
+ end
10
+
11
+ self.mattr_accessor :additional_params
12
+ self.additional_params = []
13
+
14
+ def self.setup(&block)
15
+ yield self
16
+ end
17
+ end
18
+
@@ -0,0 +1,62 @@
1
+ require 'jobs_dashboard/storage'
2
+ module JobsDashboard
3
+ class ServerMiddleware
4
+
5
+ include JobsDashboard::Storage
6
+
7
+ # Options can be send in sidekiq.rb initializer
8
+ # @param [Hash] opts middleware initialization options
9
+ # @option opts
10
+ def initialize(opts = {})
11
+ end
12
+
13
+ # Uses sidekiq's internal jid as id
14
+ # Store worker status in DB
15
+ # @param [Worker] worker worker instance
16
+ # @param [Array] msg job args, should have jid format
17
+ # @param [String] queue name
18
+ def call(worker, msg, queue)
19
+ # Determine the actual job class
20
+ # Bypass if attributes skip is set
21
+ if get_jobs_dashboard_options(worker)[:skip]
22
+ yield
23
+ return
24
+ end
25
+
26
+ update_job_status worker.jid, 'working'
27
+ yield
28
+ update_job_status worker.jid, 'complete'
29
+ #rescue Worker::Stopped
30
+ # update_job_status worker.jid, 'stopped'
31
+ rescue SystemExit, Interrupt
32
+ update_job_status worker.jid, 'interrupted'
33
+ raise
34
+ rescue Exception => e
35
+ status = :failed
36
+ update_job_status worker.jid, 'failed'
37
+ store_for_id(worker.jid, {
38
+ error_message: e.message,
39
+ backtrace: e.backtrace
40
+ })
41
+ raise
42
+ end
43
+
44
+ private
45
+ def get_jobs_dashboard_options(worker)
46
+ worker.class.get_sidekiq_options['jobs_dashboard'] || {}
47
+ end
48
+
49
+ end
50
+
51
+ # Helper method to easily configure sidekiq-status server middleware
52
+ # whatever the Sidekiq version is.
53
+ # @param [Sidekiq] sidekiq_config the Sidekiq config
54
+ # @param [Hash] server_middleware_options server middleware initialization options
55
+ # @option server_middleware_options
56
+ def self.configure_server_middleware(sidekiq_config, server_middleware_options = {})
57
+ sidekiq_config.server_middleware do |chain|
58
+ chain.add JobsDashboard::ServerMiddleware, server_middleware_options
59
+ end
60
+
61
+ end
62
+ end