sidekiq_admin_enquerer 0.0.0 → 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 6d94014f28e2cfa95c707791723fd3017c8e8f5f76c932bcd6790c506534b4d1
4
- data.tar.gz: d4b101bbe38bfd603e1f920ece7f3acb0390ff244283c3d3af97313a62871d33
3
+ metadata.gz: 90f5930895e062f99665e5740778c3e658a2b5f15f9c1831715383b8471ae098
4
+ data.tar.gz: d405c2ee9b8f34e89a2f059f32765dbe7592cd8bd7db75916b04cf915c47c0c2
5
5
  SHA512:
6
- metadata.gz: 84a0a8b2de2e53fa1bb448fe083f38cee28c3834d4dd812ee8784130143ffc576bf3ca520c543638b4673b51ae8e6a19af350d65bdf2da107082eb87448e995f
7
- data.tar.gz: 793eff5d1198b815b9dc29d4edcc1370bdf0e21f929a508ad48c52937891e2c5540eed0321956c201d29bc7beafabfafdb3ca26e9839ed316554ad8c14939c17
6
+ metadata.gz: 9c0d22153004c8e1133bd48e21f857d7fe085dbaad06557efea53acafed02caab4ae0066b6394f6b1c4dd2cdf2bbe8ff2dd400cdab9df1b1523853dd3d78b9bc
7
+ data.tar.gz: 63449fc65d48d4d5e83d9bbd82e87b5da352c920d592f3e3ef8ebbc1a37c9af0d4fa495e62a32449632452593b101f67283616df6ee9b580549f6482c7c942c6
data/CHANGELOG.md CHANGED
@@ -1,6 +1,10 @@
1
1
  # Changelog
2
2
  All notable changes to this project will be documented in this file.
3
3
 
4
- ## [0.0.0] - 2023-06-05
4
+ ## [0.1.0] - 2023-07-09
5
+
6
+ - Prototype;
7
+
8
+ ## [0.0.0] - 2023-07-05
5
9
 
6
10
  - RubyGems-placement release;
data/Gemfile.lock CHANGED
@@ -1,7 +1,7 @@
1
1
  PATH
2
2
  remote: .
3
3
  specs:
4
- sidekiq_admin_enquerer (0.0.0)
4
+ sidekiq_admin_enquerer (0.1.0)
5
5
 
6
6
  GEM
7
7
  remote: https://rubygems.org/
data/README.md CHANGED
@@ -8,6 +8,12 @@
8
8
  - [License](#license)
9
9
  - [Authors](#authors)
10
10
 
11
+ # TODO:
12
+
13
+ - interactive UI tabs for each performing method (sidekiq's UI has no jquery and bootstrap-js integrations);
14
+ - job invocation rework (support for kwarg invocation and analysis of passed attributes);
15
+ - specs;
16
+
11
17
  ## Installation
12
18
 
13
19
  ```ruby
@@ -26,6 +32,18 @@ require 'sidekiq_admin_enquerer'
26
32
 
27
33
  ## Usage
28
34
 
35
+ - it is assumed that you are using `Rails`;
36
+
37
+ ```ruby
38
+ # config/initializers/sidekiq.rb
39
+
40
+ # ...your sidekiq configs...
41
+
42
+ SidekiqAdminEnqueuer.load
43
+ ```
44
+
45
+ - navigate to `/your-sidekiq-path/enqueuer`
46
+
29
47
  ---
30
48
 
31
49
  ## Development
@@ -0,0 +1,34 @@
1
+ <h3>Enqueue Job</h3>
2
+
3
+ <!-- TODO: add interactive tabs (for each performing method) -->
4
+ <!-- NOTE: sidekiq's UI has no jquery and bootstrap-js integrations -->
5
+
6
+ <% @job.methods.each do |job_method| %>
7
+ <div class="form-group"><%= job_method.name %></div>
8
+ <form method="post" action="<%= side_enq__enqueue_job_path %>">
9
+ <%= csrf_tag if respond_to?(:csrf_tag) %>
10
+ <input class="form-control" name="perform_method" value="<%= job_method.name %>" readonly />
11
+ <input class="form-control" name="job_name" value="<%= @job.name %>" readonly />
12
+ <% job_method.params.each do |job_param| %>
13
+ <div class="form-group">
14
+ <label for="<%= job_method.name %>[<%= job_param.name %>]">
15
+ <b><%= side_enq__param_requirement(job_param) %></b>: <%= job_param.name %>
16
+ </label>
17
+ <input class="form-control" name="<%= job_method.name %>[<%= job_param.name %>]" />
18
+ </div>
19
+ <% end %>
20
+
21
+ <div class="form-group">
22
+ <input type="submit" class="btn btn-danger" name="submit" value="enqueue" />
23
+ </div>
24
+
25
+ <div class="form-group">
26
+ <label for="enqueue_in">Schedule</label>
27
+ <input class="form-control" name="enqueue_in" placeholder="in seconds" />
28
+ </div>
29
+
30
+ <input type="submit" class="btn btn-danger" name="submit" value="schedule" />
31
+ </form>
32
+
33
+ <hr />
34
+ <% end %>
@@ -0,0 +1,27 @@
1
+ <h3>Jobs</h3>
2
+ <table class="table table-hover table-bordered table-striped">
3
+ <thead>
4
+ <tr>
5
+ <th>Name</th>
6
+ <th>Method Parameters</th>
7
+ <th>Actions</th>
8
+ </tr>
9
+ </thead>
10
+ <tbody>
11
+ <% @jobs.each do |job| %>
12
+ <tr>
13
+ <td><%= job.name %></td>
14
+ <td>
15
+ <% job.methods.each do |job_method| %>
16
+ <div><b><%= job_method.name %></b>: <%= side_enq__method_params(job_method) %></div>
17
+ <% end %>
18
+ </td>
19
+ <td>
20
+ <a class="btn btn-danger btn-xs" href="<%= side_enq__enqueue_path(job) %>">
21
+ Enqueue
22
+ </a>
23
+ </td>
24
+ </tr>
25
+ <% end %>
26
+ </tbody>
27
+ </table>
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module SidekiqAdminEnquerer
4
- VERSION = '0.0.0'
4
+ VERSION = '0.1.0'
5
5
  end
@@ -0,0 +1,37 @@
1
+ # frozen_string_literal: true
2
+
3
+ module SidekiqAdminEnquerer::WebApp
4
+ # @api private
5
+ # @since 0.1.0
6
+ module AppControl
7
+ class << self
8
+ # @return [Array<SidekiqAdminEnquerer::WebApp::JobWrapper>]
9
+ #
10
+ # @api private
11
+ # @since 0.1.0
12
+ def jobs
13
+ SidekiqAdminEnquerer.jobs.map { JobWrapper.new(_1) }
14
+ end
15
+
16
+ # @param job_name [String]
17
+ # @return [SidekiqAdminEnquerer::WebApp::JobWrapper]
18
+ #
19
+ # @api private
20
+ # @since 0.1.0
21
+ def find_job(job_name)
22
+ SidekiqAdminEnquerer.find_job(job_name).yield_self { JobWrapper.new(_1) }
23
+ end
24
+
25
+ # @param job_run_params [Hash<Symbol,Any>]
26
+ # @return [void]
27
+ #
28
+ # @api private
29
+ # @since 0.1.0
30
+ def run_job(job_run_params)
31
+ job_name = job_run_params[:job_name]
32
+ job_klass = SidekiqAdminEnquerer.find_job(job_name)
33
+ SidekiqAdminEnquerer.run_job(job_klass, job_run_params)
34
+ end
35
+ end
36
+ end
37
+ end
@@ -0,0 +1,65 @@
1
+ # frozen_string_literal: true
2
+
3
+ # @api private
4
+ # @since 0.1.0
5
+ class SidekiqAdminEnquerer::WebApp::JobMethodWrapper
6
+ # @return [Array<Symbol>]
7
+ #
8
+ # @api private
9
+ # @since 0.1.0
10
+ JOB_INVOCATION_METHODS = %i[
11
+ perform
12
+ perform_in
13
+ perform_async
14
+ perform_at
15
+ ].freeze
16
+
17
+ class << self
18
+ # @param job [Class<ActiveJob::Base>, Class<Sidekiq::Worker]
19
+ # @return [Array<SidekiqAdminEnquerer::WebApp::JobMethodWrapper>]
20
+ #
21
+ # @api private
22
+ # @since 0.1.0
23
+ def extract_methods_from(job)
24
+ job.instance_methods.select do |method|
25
+ JOB_INVOCATION_METHODS.include?(method)
26
+ end.each_with_object([]) do |method, aggregate|
27
+ parameters = job.instance_method(method).parameters.map do |(req, name)|
28
+ SidekiqAdminEnquerer::WebApp::JobParamWrapper.new(job, method, name, req)
29
+ end # => Array<JobParamWrapper>
30
+ aggregate << new(job, method, parameters) # => JobMethodWrapper
31
+ end
32
+ end
33
+ end
34
+
35
+ # @return [Class<ActiveJob::Base>, Class<Sidekiq::Worker>]
36
+ #
37
+ # @api private
38
+ # @since 0.1.0
39
+ attr_reader :job
40
+
41
+ # @return [Symbol]
42
+ #
43
+ # @api private
44
+ # @since 0.1.0
45
+ attr_reader :name
46
+
47
+ # @return [Array<SidekiqAdminEnquerer::WebApp::JobParamWrapper>]
48
+ #
49
+ # @api private
50
+ # @since 0.1.0
51
+ attr_reader :params
52
+
53
+ # @param job [Class<ActiveJob::Base>, Class<Sidekiq::Worker>]
54
+ # @param name [Symbol]
55
+ # @param params [Array<SidekiqAdminEnquerer::WebApp::JobParamWrapper>]
56
+ # @return [void]
57
+ #
58
+ # @api private
59
+ # @since 0.1.0
60
+ def initialize(job, name, params)
61
+ @job = job
62
+ @name = name
63
+ @params = params
64
+ end
65
+ end
@@ -0,0 +1,69 @@
1
+ # frozen_string_literal: true
2
+
3
+ # @api private
4
+ # @since 0.1.0
5
+ class SidekiqAdminEnquerer::WebApp::JobParamWrapper
6
+ # @return [Symbol]
7
+ #
8
+ # @api private
9
+ # @since 0.1.0
10
+ attr_reader :name
11
+
12
+ # @return [Class<ActiveJob::Base>, Class<Sidekiq::Worker>]
13
+ #
14
+ # @api private
15
+ # @since 0.1.0
16
+ attr_reader :job
17
+
18
+ # @return [Symbol]
19
+ #
20
+ # @api private
21
+ # @since 0.1.0
22
+ attr_reader :req
23
+
24
+ # @param job [Class<ActiveJob::Base>, Class<Sidekiq::Worker>]
25
+ # @param method [Symbol]
26
+ # @param name [Symbol]
27
+ # @param req [Symbol]
28
+ # @return [void]
29
+ #
30
+ # @api private
31
+ # @since 0.1.0
32
+ def initialize(job, method, name, req)
33
+ @job = job
34
+ @name = name
35
+ @req = req
36
+ end
37
+
38
+ # @return [Boolean]
39
+ #
40
+ # @api private
41
+ # @since 0.1.0
42
+ def optional?
43
+ req == :key || req == :opt
44
+ end
45
+
46
+ # @return [Boolean]
47
+ #
48
+ # @api private
49
+ # @since 0.1.0
50
+ def required?
51
+ req == :keyreq || req == :req
52
+ end
53
+
54
+ # @return [Boolean]
55
+ #
56
+ # @api private
57
+ # @since 0.1.0
58
+ def kwarg_attr?
59
+ req == :key || req == :keyreq
60
+ end
61
+
62
+ # @return [Boolean]
63
+ #
64
+ # @api private
65
+ # @since 0.1.0
66
+ def named_attr?
67
+ req == :opt || req == :req
68
+ end
69
+ end
@@ -0,0 +1,37 @@
1
+ # frozen_string_literal: true
2
+
3
+ # @api private
4
+ # @since 0.1.0
5
+ class SidekiqAdminEnquerer::WebApp::JobWrapper
6
+ # @return [Array<SidekiqAdminEnquerer::WebApp::JobMethodWrapper>]
7
+ #
8
+ # @api private
9
+ # @since 0.1.0
10
+ attr_reader :methods
11
+
12
+ # @param job [Class<ActiveJob::Base>, Class<Sidekiq::Worker>]
13
+ # @return [void]
14
+ #
15
+ # @api private
16
+ # @since 0.1.0
17
+ def initialize(job)
18
+ @job = job
19
+ @methods = SidekiqAdminEnquerer::WebApp::JobMethodWrapper.extract_methods_from(job)
20
+ end
21
+
22
+ # @return [String]
23
+ #
24
+ # @api private
25
+ # @since 0.1.0
26
+ def name
27
+ job.name
28
+ end
29
+
30
+ private
31
+
32
+ # @return [Class<ActiveJob::Base>, Class<Sidekiq::Worker>]
33
+ #
34
+ # @api private
35
+ # @since 0.1.0
36
+ attr_reader :job
37
+ end
@@ -0,0 +1,40 @@
1
+ # frozen_string_literal: true
2
+
3
+ # @api private
4
+ # @since 0.1.0
5
+ module SidekiqAdminEnquerer::WebApp::WebHelpers
6
+ # @param job [SidekiqAdminEnquerer::WebApp::JobWrapper]
7
+ # @return [String]
8
+ #
9
+ # @api private
10
+ # @since 0.1.0
11
+ def side_enq__enqueue_path(job)
12
+ "#{root_path}enqueuer/#{job.name}"
13
+ end
14
+
15
+ # @return [String]
16
+ #
17
+ # @api private
18
+ # @since 0.1.0
19
+ def side_enq__enqueue_job_path
20
+ "#{root_path}enqueuer"
21
+ end
22
+
23
+ # @param job_method [SidekiqAdminEnquerer::WebApp::JobMethodWrapper]
24
+ # @return [String]
25
+ #
26
+ # @api private
27
+ # @since 0.1.0
28
+ def side_enq__method_params(job_method)
29
+ job_method.params.map(&:name).join(', ')
30
+ end
31
+
32
+ # @param job_param [SidekiqAdminEnquerer::WebApp::JobParamWrapper]
33
+ # @return [String]
34
+ #
35
+ # @api private
36
+ # @since 0.1.0
37
+ def side_enq__param_requirement(job_param)
38
+ job_param.required? ? 'required' : 'optional'
39
+ end
40
+ end
@@ -0,0 +1,46 @@
1
+ # frozen_string_literal: true
2
+
3
+ # @api private
4
+ # @since 0.1.0
5
+ module SidekiqAdminEnquerer::WebApp
6
+ require_relative 'web_app/web_helpers'
7
+ require_relative 'web_app/job_param_wrapper'
8
+ require_relative 'web_app/job_method_wrapper'
9
+ require_relative 'web_app/job_wrapper'
10
+ require_relative 'web_app/app_control'
11
+
12
+ # @return [Hash<Symbol,String>]
13
+ #
14
+ # @api private
15
+ # @since 0.1.0
16
+ TEMPLATES = {
17
+ jobs: File.read(File.join(File.expand_path(__dir__), 'pages', 'jobs.erb')),
18
+ job: File.read(File.join(File.expand_path(__dir__), 'pages', 'job.erb'))
19
+ }.freeze
20
+
21
+ class << self
22
+ # @param app [?]
23
+ # @return [void]
24
+ #
25
+ # @api private
26
+ # @since 0.1.0
27
+ def registered(app)
28
+ app.helpers(WebHelpers)
29
+
30
+ app.get('/enqueuer') do
31
+ @jobs = AppControl.jobs
32
+ render(:erb, TEMPLATES[:jobs])
33
+ end
34
+
35
+ app.get('/enqueuer/:job_name') do
36
+ @job = AppControl.find_job(params[:job_name])
37
+ render(:erb, TEMPLATES[:job])
38
+ end
39
+
40
+ app.post('/enqueuer') do
41
+ AppControl.run_job(params)
42
+ redirect File.join(root_path, 'enqueuer')
43
+ end
44
+ end
45
+ end
46
+ end
@@ -1,5 +1,89 @@
1
1
  # frozen_string_literal: true
2
2
 
3
+ # @api public
4
+ # @since 0.1.0
3
5
  module SidekiqAdminEnquerer
4
6
  require_relative 'sidekiq_admin_enquerer/version'
7
+ require_relative 'sidekiq_admin_enquerer/web_app'
8
+
9
+ # @return [Array<String>]
10
+ #
11
+ # @api private
12
+ # @since 0.1.0
13
+ JOB_SKIP_LIST = %w[
14
+ Sidekiq::Extensions::DelayedModel
15
+ Sidekiq::Extensions::DelayedMailer
16
+ Sidekiq::Extensions::DelayedClass
17
+ ApplicationJob
18
+ ActiveJob::QueueAdapters::SidekiqAdapter::JobWrapper
19
+ ].freeze
20
+
21
+ class << self
22
+ # @return [Array<Class<ActiveJob::Base>,Class<Sidekiq::Worker>>]
23
+ #
24
+ # @api private
25
+ # @since 0.1.0
26
+ def jobs
27
+ Rails.application.eager_load!
28
+
29
+ ObjectSpace.each_object(Class).select do |job_klass|
30
+ next if job_klass.singleton_class?
31
+ next if JOB_SKIP_LIST.any? { job_klass.to_s.include?(_1) }
32
+ job_klass < ActiveJob::Base || job_klass.included_modules.include?(Sidekiq::Worker)
33
+ end
34
+ end
35
+
36
+ # @param job_name [String]
37
+ # @return [Class<ActiveJob::Base>,Class<Sidekiq::Worker>]
38
+ #
39
+ # @api private
40
+ # @since 0.1.0
41
+ def find_job(job_name)
42
+ jobs.find { _1.name == job_name || _1.to_s == job_name }
43
+ end
44
+
45
+ # @param job [?]
46
+ # @param params [?]
47
+ # @return [void]
48
+ #
49
+ # @api private
50
+ # @since 0.1.0
51
+ # rubocop:disable Metrics/AbcSize
52
+ def run_job(job, params)
53
+ # TODO: rework with attribute analysis
54
+ job_perform_method = params['perform_method']
55
+ job_params = params[job_perform_method]
56
+ job.respond_to?(:sidekiq_options) ? job.sidekiq_options['queue'].to_s : 'default'
57
+
58
+ if params['submit'] == 'enqueue'
59
+ case
60
+ when job < ActiveJob::Base
61
+ job.perform_later(*job_params.values)
62
+ when job_klass.included_modules.include?(Sidekiq::Worker)
63
+ Sidekiq::Client.enqueue_to(queue, job, *job_params.values)
64
+ end
65
+ elsif params['submit'] == 'schedule'
66
+ case
67
+ when job < ActiveJob::Base
68
+ job.set(wait: params['enqueue_in'].to_i.seconds).perform_later(*job_params.values)
69
+ when job_klass.included_modules.include?(Sidekiq::Worker)
70
+ Sidekiq::Client.enqueue_to_in(
71
+ queue, params['enqueue_in'].to_i.seconds, job, *job_params.values
72
+ )
73
+ end
74
+ end
75
+ end
76
+ # rubocop:enable Metrics/AbcSize
77
+
78
+ # @return [void]
79
+ #
80
+ # @api public
81
+ # @since 0.1.0
82
+ def load
83
+ if defined?(Sidekiq::Web)
84
+ Sidekiq::Web.register(SidekiqAdminEnquerer::WebApp)
85
+ Sidekiq::Web.tabs['Enqueuer'] = 'enqueuer'
86
+ end
87
+ end
88
+ end
5
89
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: sidekiq_admin_enquerer
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.0.0
4
+ version: 0.1.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Rustam Ibragimov
8
8
  autorequire:
9
9
  bindir: exe
10
10
  cert_chain: []
11
- date: 2023-07-04 00:00:00.000000000 Z
11
+ date: 2023-07-09 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: bundler
@@ -114,7 +114,15 @@ files:
114
114
  - bin/console
115
115
  - bin/setup
116
116
  - lib/sidekiq_admin_enquerer.rb
117
+ - lib/sidekiq_admin_enquerer/pages/job.erb
118
+ - lib/sidekiq_admin_enquerer/pages/jobs.erb
117
119
  - lib/sidekiq_admin_enquerer/version.rb
120
+ - lib/sidekiq_admin_enquerer/web_app.rb
121
+ - lib/sidekiq_admin_enquerer/web_app/app_control.rb
122
+ - lib/sidekiq_admin_enquerer/web_app/job_method_wrapper.rb
123
+ - lib/sidekiq_admin_enquerer/web_app/job_param_wrapper.rb
124
+ - lib/sidekiq_admin_enquerer/web_app/job_wrapper.rb
125
+ - lib/sidekiq_admin_enquerer/web_app/web_helpers.rb
118
126
  - sidekiq_admin_enquerer.gemspec
119
127
  homepage: https://github.com/0exp/sidekiq_admin_enquerer
120
128
  licenses: