jobs_dashboard 0.3.4

Sign up to get free protection for your applications and to get access to all the features.
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