que-view 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA256:
3
+ metadata.gz: 3a3458af823c9bb66e334138e63455e4c0dc94392d6f09b959276733bcb30ef3
4
+ data.tar.gz: c4b4be700ff4456f882488ccdd0adf34af14fa98bd9ab94380cc21a835ac1d49
5
+ SHA512:
6
+ metadata.gz: dd7d0ce017b6c4e2becf99b429dd0450aee7b250761ee3d9772d1dd5dc1e9f3bbfaf55508ab0e120ec3a30513e62a0857b5b3c375d2d44835c75f34b16b0c1aa
7
+ data.tar.gz: f3aeb0ced2fc4b59bf5e783073505a51f507ea5114a79df6034c9764a8e12637a18f912581399492fef18fb33973c53144493a25dffa86c57a1aaf6a6c2c64d4
data/MIT-LICENSE ADDED
@@ -0,0 +1,20 @@
1
+ Copyright Bogdanov Anton
2
+
3
+ Permission is hereby granted, free of charge, to any person obtaining
4
+ a copy of this software and associated documentation files (the
5
+ "Software"), to deal in the Software without restriction, including
6
+ without limitation the rights to use, copy, modify, merge, publish,
7
+ distribute, sublicense, and/or sell copies of the Software, and to
8
+ permit persons to whom the Software is furnished to do so, subject to
9
+ the following conditions:
10
+
11
+ The above copyright notice and this permission notice shall be
12
+ included in all copies or substantial portions of the Software.
13
+
14
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
15
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
16
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
17
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
18
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
19
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
20
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
data/README.md ADDED
@@ -0,0 +1,29 @@
1
+ # Que::View
2
+ Short description and motivation.
3
+
4
+ ## Usage
5
+ How to use my plugin.
6
+
7
+ ## Installation
8
+ Add this line to your application's Gemfile:
9
+
10
+ ```ruby
11
+ gem 'que'
12
+ gem 'que-view'
13
+ ```
14
+
15
+ And then execute:
16
+ ```bash
17
+ $ bundle install
18
+ ```
19
+
20
+ Or install it yourself as:
21
+ ```bash
22
+ $ gem install que-view
23
+ ```
24
+
25
+ ## Contributing
26
+ Contribution directions go here.
27
+
28
+ ## License
29
+ The gem is available as open source under the terms of the [MIT License](https://opensource.org/licenses/MIT).
data/Rakefile ADDED
@@ -0,0 +1,10 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'bundler/setup'
4
+
5
+ APP_RAKEFILE = File.expand_path('spec/dummy/Rakefile', __dir__)
6
+ load 'rails/tasks/engine.rake'
7
+
8
+ load 'rails/tasks/statistics.rake'
9
+
10
+ require 'bundler/gem_tasks'
@@ -0,0 +1 @@
1
+ //= link_directory ../stylesheets/que/view .css
@@ -0,0 +1,90 @@
1
+ /*
2
+ * This is a manifest file that'll be compiled into application.css, which will include all the files
3
+ * listed below.
4
+ *
5
+ * Any CSS and SCSS file within this directory, lib/assets/stylesheets, vendor/assets/stylesheets,
6
+ * or any plugin's vendor/assets/stylesheets directory can be referenced here using a relative path.
7
+ *
8
+ * You're free to add application-wide styles to this file and they'll appear at the bottom of the
9
+ * compiled file so the styles you add here take precedence over styles defined in any other CSS/SCSS
10
+ * files in this directory. Styles in this file should be added after the last require_* statement.
11
+ * It is generally better to create a new file per style scope.
12
+ *
13
+ *= require_tree .
14
+ *= require_self
15
+ */
16
+
17
+ @import url('https://fonts.googleapis.com/css?family=Source+Sans+Pro:300,400,600&display=swap&subset=cyrillic');
18
+
19
+ * {
20
+ font-family: 'Source Sans Pro';
21
+ }
22
+
23
+ .row {
24
+ display: flex;
25
+ flex-direction: row;
26
+ margin: 0 auto;
27
+ max-width: 62.5rem;
28
+ width: 100%;
29
+ }
30
+
31
+ .row .column, .row table {
32
+ flex: 1;
33
+ padding: 1rem;
34
+ }
35
+
36
+ table th, table td {
37
+ padding: .25rem .75rem;
38
+ border-bottom: 1px solid #999;
39
+ }
40
+
41
+ table tbody tr:nth-of-type(2n) {
42
+ background: #DDD;
43
+ }
44
+
45
+ .dashboard-stat {
46
+ text-align: center;
47
+ display: table;
48
+ width: 100%;
49
+ }
50
+
51
+ .dashboard-stat a {
52
+ text-decoration: none;
53
+ }
54
+
55
+ .dashboard-stat .cell {
56
+ display: flex;
57
+ flex-direction: column;
58
+ justify-content: center;
59
+ height: 200px;
60
+ }
61
+
62
+ .dashboard-stat h2 {
63
+ color: #222;
64
+ font-size: 1rem;
65
+ font-weight: normal;
66
+ text-transform: uppercase;
67
+ margin: 0;
68
+ }
69
+
70
+ .dashboard-stat.total {
71
+ background: #828E8C;
72
+ }
73
+
74
+ .dashboard-stat.scheduled {
75
+ background: #828E8C;
76
+ }
77
+
78
+ .dashboard-stat.running {
79
+ background: #CFD0C1;
80
+ }
81
+
82
+ .dashboard-stat.failing {
83
+ background: #E8866C;
84
+ }
85
+
86
+ .dashboard-value {
87
+ font-size: 5rem;
88
+ line-height: 5rem;
89
+ color: black;
90
+ }
@@ -0,0 +1,8 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Que
4
+ module View
5
+ class ApplicationController < ActionController::Base
6
+ end
7
+ end
8
+ end
@@ -0,0 +1,57 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Que
4
+ module View
5
+ class JobsController < Que::View::ApplicationController
6
+ PER_PAGE = 20
7
+
8
+ def index
9
+ @jobs = find_jobs(params[:status])
10
+ @jobs_amount = find_jobs_amount(params[:status])
11
+ end
12
+
13
+ def destroy
14
+ redirect_to(
15
+ jobs_path(status: params[:status]),
16
+ notice: 'Removing single job is not ready yet'
17
+ )
18
+ end
19
+
20
+ def destroy_all
21
+ updated_rows = destroy_all_jobs(params[:status])
22
+ redirect_to(
23
+ jobs_path(status: params[:status]),
24
+ notice: updated_rows.empty? ? 'No jobs deleted' : "#{updated_rows.count} jobs deleted"
25
+ )
26
+ end
27
+
28
+ private
29
+
30
+ def find_jobs(status)
31
+ case status&.to_sym
32
+ when :failing then ::Que::View.failing_jobs(PER_PAGE, offset, '%')
33
+ else []
34
+ end
35
+ end
36
+
37
+ def find_jobs_amount(status)
38
+ ::Que::View.dashboard_stats('%')[0][status&.to_sym]
39
+ end
40
+
41
+ def destroy_all_jobs(status)
42
+ case status&.to_sym
43
+ when :failing then ::Que::View.delete_all_failing_jobs
44
+ else 0
45
+ end
46
+ end
47
+
48
+ def page
49
+ (params[:page] || 1).to_i
50
+ end
51
+
52
+ def offset
53
+ (page - 1) * PER_PAGE
54
+ end
55
+ end
56
+ end
57
+ end
@@ -0,0 +1,11 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Que
4
+ module View
5
+ class WelcomeController < Que::View::ApplicationController
6
+ def index
7
+ @dashboard_stats = ::Que::View.dashboard_stats('%')[0]
8
+ end
9
+ end
10
+ end
11
+ end
@@ -0,0 +1,22 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Que
4
+ module View
5
+ module ApplicationHelper
6
+ include ::Pagy::Frontend
7
+
8
+ def humanized_job_class(job)
9
+ case job[:job_class]
10
+ when 'ActiveJob::QueueAdapters::QueAdapter::JobWrapper' then job.dig(:args, 0, :job_class)
11
+ else job[:job_class]
12
+ end
13
+ end
14
+
15
+ def format_error(job)
16
+ return unless job[:last_error_message]
17
+
18
+ job[:last_error_message].lines.first || ''
19
+ end
20
+ end
21
+ end
22
+ end
@@ -0,0 +1,12 @@
1
+ <!DOCTYPE html>
2
+ <html>
3
+ <head>
4
+ <title>Que View</title>
5
+ <%= csrf_meta_tags %>
6
+ <%= csp_meta_tag %>
7
+ <%= stylesheet_link_tag 'que/view/application', media: 'all' %>
8
+ </head>
9
+ <body>
10
+ <%= yield %>
11
+ </body>
12
+ </html>
@@ -0,0 +1,35 @@
1
+ <div class="row">
2
+ <% if @jobs.size.positive? %>
3
+ <table cellspacing="0">
4
+ <thead>
5
+ <tr>
6
+ <th>ID</th>
7
+ <th>Failures</th>
8
+ <th>Job</th>
9
+ <th>Queue</th>
10
+ <th>Error</th>
11
+ <th></th>
12
+ </tr>
13
+ </thead>
14
+ <tbody>
15
+ <% @jobs.each do |job| %>
16
+ <tr>
17
+ <td><%= job[:id] %></td>
18
+ <td><%= job[:error_count] %></td>
19
+ <td><%= humanized_job_class(job) %></td>
20
+ <td><%= job[:queue] %></td>
21
+ <td><%= format_error(job) %></td>
22
+ <td>
23
+ <%= button_to 'Delete', job_path(job[:id]), class: 'btn-danger', method: :delete, onclick: "return confirm('Are you sure you wish to delete job?')" %>
24
+ </td>
25
+ </tr>
26
+ <% end %>
27
+ </tbody>
28
+ </table>
29
+ <% else %>
30
+ <p>No jobs found</p>
31
+ <% end %>
32
+ </div>
33
+ <% if @jobs_amount.positive? %>
34
+ <%= button_to 'Delete All', destroy_all_jobs_path(status: params[:status]), class: 'btn-danger', method: :delete, onclick: "return confirm('Are you sure you wish to delete all jobs?')" %>
35
+ <% end %>
@@ -0,0 +1,34 @@
1
+ <div class="row">
2
+ <div class="column">
3
+ <div class="dashboard-stat running">
4
+ <div class="cell">
5
+ <h2>Running</h2>
6
+ <span class="dashboard-value">
7
+ <%= @dashboard_stats[:running] %>
8
+ </span>
9
+ </div>
10
+ </div>
11
+ </div>
12
+ <div class="column">
13
+ <div class="dashboard-stat scheduled">
14
+ <div class="cell">
15
+ <h2>Scheduled</h2>
16
+ <span class="dashboard-value">
17
+ <%= @dashboard_stats[:scheduled] %>
18
+ </span>
19
+ </div>
20
+ </div>
21
+ </div>
22
+ <div class="column">
23
+ <div class="dashboard-stat failing">
24
+ <%= link_to jobs_path(status: 'failing') do %>
25
+ <div class="cell">
26
+ <h2>Failing</h2>
27
+ <span class="dashboard-value">
28
+ <%= @dashboard_stats[:failing] %>
29
+ </span>
30
+ </div>
31
+ <% end %>
32
+ </div>
33
+ </div>
34
+ </div>
data/config/routes.rb ADDED
@@ -0,0 +1,9 @@
1
+ # frozen_string_literal: true
2
+
3
+ Que::View::Engine.routes.draw do
4
+ resources :jobs, only: %i[index destroy] do
5
+ delete :destroy_all, on: :collection
6
+ end
7
+
8
+ root 'welcome#index'
9
+ end
@@ -0,0 +1,17 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Que
4
+ module View
5
+ class Configuration
6
+ attr_accessor :ui_username, :ui_password, :ui_secured_environments
7
+
8
+ def initialize
9
+ # It's required to specify these 3 variables to enable basic auth to UI
10
+ @ui_username = ''
11
+ @ui_password = ''
12
+ # Secured environments variable must directly contains environment names
13
+ @ui_secured_environments = []
14
+ end
15
+ end
16
+ end
17
+ end
@@ -0,0 +1,85 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Que
4
+ module View
5
+ class DSL
6
+ def dashboard_stats(...)
7
+ execute(dashboard_stats_sql(...))
8
+ end
9
+
10
+ def failing_jobs(...)
11
+ execute(failing_jobs_sql(...))
12
+ end
13
+
14
+ def delete_all_failing_jobs
15
+ execute(delete_jobs_query(lock_all_failing_jobs_sql))
16
+ end
17
+
18
+ private
19
+
20
+ # rubocop: disable Metrics/MethodLength
21
+ def dashboard_stats_sql(search)
22
+ <<-SQL.squish
23
+ SELECT count(*) AS total,
24
+ count(locks.job_id) AS running,
25
+ coalesce(sum((error_count > 0 AND locks.job_id IS NULL)::int), 0) AS failing,
26
+ coalesce(sum((error_count = 0 AND locks.job_id IS NULL)::int), 0) AS scheduled
27
+ FROM que_jobs
28
+ LEFT JOIN (
29
+ SELECT (classid::bigint << 32) + objid::bigint AS job_id
30
+ FROM pg_locks
31
+ WHERE locktype = 'advisory'
32
+ ) locks ON (que_jobs.id=locks.job_id)
33
+ WHERE
34
+ job_class ILIKE ('#{search}')
35
+ OR que_jobs.args #>> '{0, job_class}' ILIKE ('#{search}')
36
+ SQL
37
+ end
38
+
39
+ def failing_jobs_sql(per_page, offset, search)
40
+ <<-SQL.squish
41
+ SELECT que_jobs.*
42
+ FROM que_jobs
43
+ LEFT JOIN (
44
+ SELECT (classid::bigint << 32) + objid::bigint AS job_id
45
+ FROM pg_locks
46
+ WHERE locktype = 'advisory'
47
+ ) locks ON (que_jobs.id=locks.job_id)
48
+ WHERE locks.job_id IS NULL
49
+ AND error_count > 0
50
+ AND (
51
+ job_class ILIKE ('#{search}')
52
+ OR que_jobs.args #>> '{0, job_class}' ILIKE ('#{search}')
53
+ )
54
+ ORDER BY run_at, id
55
+ LIMIT #{per_page}::int
56
+ OFFSET #{offset}::int
57
+ SQL
58
+ end
59
+
60
+ def delete_jobs_sql(scope)
61
+ <<-SQL.squish
62
+ WITH target AS (#{scope})
63
+ DELETE FROM que_jobs
64
+ USING target
65
+ WHERE target.locked
66
+ AND target.id = que_jobs.id
67
+ RETURNING pg_advisory_unlock(target.id)
68
+ SQL
69
+ end
70
+
71
+ def lock_all_failing_jobs_sql
72
+ <<-SQL.squish
73
+ SELECT id, pg_try_advisory_lock(id) AS locked
74
+ FROM que_jobs
75
+ WHERE error_count > 0
76
+ SQL
77
+ end
78
+ # rubocop: enable Metrics/MethodLength
79
+
80
+ def execute(sql)
81
+ Que.execute(sql)
82
+ end
83
+ end
84
+ end
85
+ end
@@ -0,0 +1,9 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Que
4
+ module View
5
+ class Engine < ::Rails::Engine
6
+ isolate_namespace Que::View
7
+ end
8
+ end
9
+ end
@@ -0,0 +1,7 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Que
4
+ module View
5
+ VERSION = '0.1.0'
6
+ end
7
+ end
data/lib/que/view.rb ADDED
@@ -0,0 +1,39 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'forwardable'
4
+
5
+ require 'que/view/version'
6
+ require 'que/view/engine'
7
+ require 'que/view/configuration'
8
+ require 'que/view/dsl'
9
+
10
+ module Que
11
+ module View
12
+ extend self
13
+ extend Forwardable
14
+
15
+ # Public: Configure emailbutler.
16
+ #
17
+ # Que::View.configure do |config|
18
+ # end
19
+ #
20
+ def configure
21
+ yield configuration
22
+ end
23
+
24
+ # Public: Returns Que::View::Configuration instance.
25
+ def configuration
26
+ @configuration ||= Configuration.new
27
+ end
28
+
29
+ # Public: Default per thread emailbutler instance if configured.
30
+ # Returns Que::View::DSL instance.
31
+ def instance
32
+ Thread.current[:que_view_instance] ||= DSL.new
33
+ end
34
+
35
+ # Public: All the methods delegated to instance. These should match the interface of Que::View::DSL.
36
+ def_delegators :instance,
37
+ :dashboard_stats, :failing_jobs, :delete_all_failing_jobs
38
+ end
39
+ end
metadata ADDED
@@ -0,0 +1,204 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: que-view
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.1.0
5
+ platform: ruby
6
+ authors:
7
+ - Bogdanov Anton
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+ date: 2023-11-14 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: que
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - ">"
18
+ - !ruby/object:Gem::Version
19
+ version: '2.0'
20
+ type: :runtime
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - ">"
25
+ - !ruby/object:Gem::Version
26
+ version: '2.0'
27
+ - !ruby/object:Gem::Dependency
28
+ name: rails
29
+ requirement: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - ">"
32
+ - !ruby/object:Gem::Version
33
+ version: 6.0.0
34
+ type: :runtime
35
+ prerelease: false
36
+ version_requirements: !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - ">"
39
+ - !ruby/object:Gem::Version
40
+ version: 6.0.0
41
+ - !ruby/object:Gem::Dependency
42
+ name: sassc-rails
43
+ requirement: !ruby/object:Gem::Requirement
44
+ requirements:
45
+ - - ">="
46
+ - !ruby/object:Gem::Version
47
+ version: '0'
48
+ type: :runtime
49
+ prerelease: false
50
+ version_requirements: !ruby/object:Gem::Requirement
51
+ requirements:
52
+ - - ">="
53
+ - !ruby/object:Gem::Version
54
+ version: '0'
55
+ - !ruby/object:Gem::Dependency
56
+ name: bundler
57
+ requirement: !ruby/object:Gem::Requirement
58
+ requirements:
59
+ - - ">="
60
+ - !ruby/object:Gem::Version
61
+ version: '0'
62
+ type: :development
63
+ prerelease: false
64
+ version_requirements: !ruby/object:Gem::Requirement
65
+ requirements:
66
+ - - ">="
67
+ - !ruby/object:Gem::Version
68
+ version: '0'
69
+ - !ruby/object:Gem::Dependency
70
+ name: pg
71
+ requirement: !ruby/object:Gem::Requirement
72
+ requirements:
73
+ - - ">"
74
+ - !ruby/object:Gem::Version
75
+ version: '1.0'
76
+ type: :development
77
+ prerelease: false
78
+ version_requirements: !ruby/object:Gem::Requirement
79
+ requirements:
80
+ - - ">"
81
+ - !ruby/object:Gem::Version
82
+ version: '1.0'
83
+ - !ruby/object:Gem::Dependency
84
+ name: puma
85
+ requirement: !ruby/object:Gem::Requirement
86
+ requirements:
87
+ - - '='
88
+ - !ruby/object:Gem::Version
89
+ version: 6.3.0
90
+ type: :development
91
+ prerelease: false
92
+ version_requirements: !ruby/object:Gem::Requirement
93
+ requirements:
94
+ - - '='
95
+ - !ruby/object:Gem::Version
96
+ version: 6.3.0
97
+ - !ruby/object:Gem::Dependency
98
+ name: rake
99
+ requirement: !ruby/object:Gem::Requirement
100
+ requirements:
101
+ - - "~>"
102
+ - !ruby/object:Gem::Version
103
+ version: '13.0'
104
+ type: :development
105
+ prerelease: false
106
+ version_requirements: !ruby/object:Gem::Requirement
107
+ requirements:
108
+ - - "~>"
109
+ - !ruby/object:Gem::Version
110
+ version: '13.0'
111
+ - !ruby/object:Gem::Dependency
112
+ name: rubocop
113
+ requirement: !ruby/object:Gem::Requirement
114
+ requirements:
115
+ - - "~>"
116
+ - !ruby/object:Gem::Version
117
+ version: '1.39'
118
+ type: :development
119
+ prerelease: false
120
+ version_requirements: !ruby/object:Gem::Requirement
121
+ requirements:
122
+ - - "~>"
123
+ - !ruby/object:Gem::Version
124
+ version: '1.39'
125
+ - !ruby/object:Gem::Dependency
126
+ name: rubocop-performance
127
+ requirement: !ruby/object:Gem::Requirement
128
+ requirements:
129
+ - - "~>"
130
+ - !ruby/object:Gem::Version
131
+ version: '1.8'
132
+ type: :development
133
+ prerelease: false
134
+ version_requirements: !ruby/object:Gem::Requirement
135
+ requirements:
136
+ - - "~>"
137
+ - !ruby/object:Gem::Version
138
+ version: '1.8'
139
+ - !ruby/object:Gem::Dependency
140
+ name: rubocop-rake
141
+ requirement: !ruby/object:Gem::Requirement
142
+ requirements:
143
+ - - "~>"
144
+ - !ruby/object:Gem::Version
145
+ version: '0.6'
146
+ type: :development
147
+ prerelease: false
148
+ version_requirements: !ruby/object:Gem::Requirement
149
+ requirements:
150
+ - - "~>"
151
+ - !ruby/object:Gem::Version
152
+ version: '0.6'
153
+ description: Rails engine as web interface for Que for inspecting jobs.
154
+ email:
155
+ - kortirso@gmail.com
156
+ executables: []
157
+ extensions: []
158
+ extra_rdoc_files: []
159
+ files:
160
+ - MIT-LICENSE
161
+ - README.md
162
+ - Rakefile
163
+ - app/assets/config/que_view_manifest.js
164
+ - app/assets/stylesheets/que/view/application.css
165
+ - app/controllers/que/view/application_controller.rb
166
+ - app/controllers/que/view/jobs_controller.rb
167
+ - app/controllers/que/view/welcome_controller.rb
168
+ - app/helpers/que/view/application_helper.rb
169
+ - app/views/layouts/que/view/application.html.erb
170
+ - app/views/que/view/jobs/index.html.erb
171
+ - app/views/que/view/welcome/index.html.erb
172
+ - config/routes.rb
173
+ - lib/que/view.rb
174
+ - lib/que/view/configuration.rb
175
+ - lib/que/view/dsl.rb
176
+ - lib/que/view/engine.rb
177
+ - lib/que/view/version.rb
178
+ homepage: https://github.com/kortirso/que-view
179
+ licenses:
180
+ - MIT
181
+ metadata:
182
+ homepage_uri: https://github.com/kortirso/que-view
183
+ source_code_uri: https://github.com/kortirso/que-view
184
+ changelog_uri: https://github.com/kortirso/que-view/blob/master/CHANGELOG.md
185
+ post_install_message:
186
+ rdoc_options: []
187
+ require_paths:
188
+ - lib
189
+ required_ruby_version: !ruby/object:Gem::Requirement
190
+ requirements:
191
+ - - ">="
192
+ - !ruby/object:Gem::Version
193
+ version: '2.7'
194
+ required_rubygems_version: !ruby/object:Gem::Requirement
195
+ requirements:
196
+ - - ">="
197
+ - !ruby/object:Gem::Version
198
+ version: '0'
199
+ requirements: []
200
+ rubygems_version: 3.4.1
201
+ signing_key:
202
+ specification_version: 4
203
+ summary: Web interface for Que.
204
+ test_files: []