sidekiq_admin_enquerer 0.0.0 → 0.1.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 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: