resque-approve 0.0.4 → 0.0.12

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
- SHA1:
3
- metadata.gz: e8ca59f3ecba62eae0dc3399c1ca5f82df1fe7d9
4
- data.tar.gz: 062fa0951dd4bbe28b25663561c507806a23288f
2
+ SHA256:
3
+ metadata.gz: 4964a615615665834296d65c85866378409435eb0eba93bc5932978334d7ac3b
4
+ data.tar.gz: 900f8341e08a2da8fa893016a8f36c4f8bd47996c6848a314ba47586df8cb47a
5
5
  SHA512:
6
- metadata.gz: b6cf2c253fd889aa77e5d0a21a40e5b4235020221282d43fe31233695fc50d78b149d65b89f7be10de74bbbcc31e04f55e708432faf2dcac62ae870f32fea197
7
- data.tar.gz: 721007d47b2b5bc52434b2cbdf65eaae21df7043f2d7e2dc2f831f6677b5f827bded14b663f3d93757b395fedafff28b9080c1d1f8590c8fb7ff697b07cb1077
6
+ metadata.gz: '0942156347f37b046f148de6e941a7c6ffa2fada7659201f56889f04cf0bcf3ddbb03d3e469aa07c2fbb6f27f0486cee2aebb4d9b91d86ecc349c8ee7686ba74'
7
+ data.tar.gz: 53575d650f15b4c51477e85b321c420edf783fbc6a45380a47b91149a11b365ce068b33098435c0c73b25464bd770dd784e606dece377c718daaced99ff0bada
@@ -6,5 +6,6 @@ require File.expand_path(File.join("resque", "plugins", "approve", "pending_job"
6
6
  require File.expand_path(File.join("resque", "plugins", "approve", "pending_job_queue"), File.dirname(__FILE__))
7
7
  require File.expand_path(File.join("resque", "plugins", "approve", "approval_key_list"), File.dirname(__FILE__))
8
8
  require File.expand_path(File.join("resque", "plugins", "approve", "cleaner"), File.dirname(__FILE__))
9
+ require File.expand_path(File.join("resque", "plugins", "approve", "auto_approve_next"), File.dirname(__FILE__))
9
10
 
10
11
  require File.expand_path(File.join("resque", "plugins", "approve"), File.dirname(__FILE__))
@@ -95,7 +95,7 @@ module Resque
95
95
 
96
96
  def add_static_files(base)
97
97
  base.class_eval do
98
- get %r{approve/public/([a-z_]+\.[a-z]+)} do
98
+ get %r{/approve/public/([a-z_]+\.[a-z]+)} do
99
99
  send_file Resque::ApproveServer.public_path(params[:captures]&.first)
100
100
  end
101
101
  end
@@ -107,6 +107,7 @@ module Resque
107
107
  delete_all_queues(base)
108
108
  approve_all_queues(base)
109
109
  delete_queue(base)
110
+ reset_running(base)
110
111
  delete_one_queue(base)
111
112
  approve_queue(base)
112
113
  approve_one_queue(base)
@@ -167,6 +168,16 @@ module Resque
167
168
  end
168
169
  end
169
170
 
171
+ def reset_running(base)
172
+ base.class_eval do
173
+ post "/approve/reset_running" do
174
+ Resque::Plugins::Approve::PendingJobQueue.new(params[:approval_key]).reset_running
175
+
176
+ redirect u("approve/job_list?#{{ approval_key: params[:approval_key] }.to_param}")
177
+ end
178
+ end
179
+ end
180
+
170
181
  def delete_one_queue(base)
171
182
  base.class_eval do
172
183
  post "/approve/delete_one_queue" do
@@ -11,6 +11,8 @@ module Resque
11
11
 
12
12
  included do
13
13
  self.auto_delete_approval_key = false
14
+ self.max_active_jobs = -1
15
+ self.default_queue_name = nil
14
16
  end
15
17
 
16
18
  class << self
@@ -46,20 +48,60 @@ module Resque
46
48
  @auto_delete_approval_key
47
49
  end
48
50
 
51
+ def max_active_jobs=(value)
52
+ @max_active_jobs = value
53
+ end
54
+
55
+ def max_active_jobs
56
+ @max_active_jobs
57
+ end
58
+
59
+ def default_queue_name=(value)
60
+ @default_queue_name = value
61
+ end
62
+
63
+ def default_queue_name
64
+ @default_queue_name || Resque.queue_from_class(self)
65
+ end
66
+
67
+ def approve
68
+ Resque::Plugins::Approve.approve(default_queue_name)
69
+ end
70
+
71
+ def approve_one
72
+ Resque::Plugins::Approve.approve_one(default_queue_name)
73
+ end
74
+
75
+ def approve_num(num_approve)
76
+ Resque::Plugins::Approve.approve_num(num_approve, default_queue_name)
77
+ end
78
+
79
+ def remove
80
+ Resque::Plugins::Approve.remove(default_queue_name)
81
+ end
82
+
83
+ def remove_one
84
+ Resque::Plugins::Approve.remove_one(default_queue_name)
85
+ end
86
+
49
87
  # It is possible to run a job immediately using `Resque.push`. This will bypass the queue and run
50
88
  # the job immediately. This will prevent such a job from enqueuing, and instead pause it for approval
51
89
  #
52
90
  # The primary reason for this is to prevent the job from receiving the approval parameters it is not
53
91
  # supposed to have when actually run/enqueued.
92
+ #
93
+ # NOTE: This does not validate running counts.
54
94
  def before_perform_approve(*args)
55
95
  # Check if the job needs to be approved, and if so, do not enqueue it.
56
96
  job = PendingJob.new(SecureRandom.uuid, class_name: name, args: args)
57
97
 
58
- if job.requires_approval?
98
+ if job.approval_keys? && !job.max_active_jobs?
59
99
  ApprovalKeyList.new.add_job(job)
60
100
 
61
101
  raise Resque::Job::DontPerform, "The job has not been approved yet."
62
102
  else
103
+ job.max_jobs_perform_args(args)
104
+
63
105
  true
64
106
  end
65
107
  end
@@ -72,6 +114,8 @@ module Resque
72
114
  ApprovalKeyList.new.add_job(job)
73
115
  false
74
116
  else
117
+ job.max_jobs_perform_args(args)
118
+
75
119
  true
76
120
  end
77
121
  end
@@ -20,7 +20,7 @@ module Resque
20
20
  end
21
21
 
22
22
  def remove_key(key)
23
- Resque::Plugins::Approve::PendingJobQueue.new(job.approval_key).resume
23
+ Resque::Plugins::Approve::PendingJobQueue.new(key).resume
24
24
 
25
25
  redis.srem(list_key, key)
26
26
  end
@@ -0,0 +1,38 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Resque
4
+ module Plugins
5
+ module Approve
6
+ # This module adds a new class level perform method that overrides the jobs classes
7
+ # class method perform that is used by Resque.
8
+ #
9
+ # The new overloaded method uses super to perform the original perform functionality
10
+ # but ensures that when the job is complete that it approves the next job in the queue.
11
+ #
12
+ # The reason for this class is to manage the maximum number of jobs that are allowed to
13
+ # run at the same time for a particular job. When the maximum number of jobs is reached
14
+ # when a job completes, it will approve the next job in the queue automatically.
15
+ module AutoApproveNext
16
+ extend ActiveSupport::Concern
17
+
18
+ class_methods do
19
+ def perform(*args)
20
+ job = Resque::Plugins::Approve::PendingJob.new(SecureRandom.uuid, class_name: name, args: args)
21
+ dup_args = job.args
22
+ del_options = dup_args.extract_options!.with_indifferent_access
23
+ approval_key = del_options.delete(:approval_key) || job.approve_options[:approval_key]
24
+
25
+ dup_args << del_options if del_options.present?
26
+
27
+ begin
28
+ super(*dup_args)
29
+ ensure
30
+ Resque::Plugins::Approve::PendingJobQueue.new(approval_key).decrement_running
31
+ Resque::Plugins::Approve.approve_one approval_key
32
+ end
33
+ end
34
+ end
35
+ end
36
+ end
37
+ end
38
+ end
@@ -12,31 +12,35 @@ module Resque
12
12
  # class_name - The name of the job class to be enqueued
13
13
  # args - The arguments for the job to be enqueued
14
14
  #
15
- # approval_key - The approval key for the pending job that will be used to release/approve the job.
16
- # This will default to nil.
17
- # approval_queue - The queue that the pending job will be enqueued into.
18
- # This will default to the queue for the job.
19
- # approval_at - The time when the job is to be enqueued.
20
- # This will call `enqueue_at` to enqueue the job, so this option will only work
21
- # properly if you use the `resque-scheduler` gem.
15
+ # requires_approval - If a class defines a default approval queue (similar to defining
16
+ # the queue for Resque), then instead of passing `approval_key: "key name"` in the
17
+ # parameters, you would pass `requires_approval: true`. This way the key name
18
+ # appears only in the class and not scattered through the code.
19
+ # approval_key - The approval key for the pending job that will be used to release/approve the job.
20
+ # This will default to nil.
21
+ # approval_queue - The queue that the pending job will be enqueued into.
22
+ # This will default to the queue for the job.
23
+ # approval_at - The time when the job is to be enqueued.
24
+ # This will call `enqueue_at` to enqueue the job, so this option will only work
25
+ # properly if you use the `resque-scheduler` gem.
22
26
  #
23
- # When using resque-scheduler, there are two ways to delay enqueue a job
24
- # and how you do this depends on your use case.
27
+ # When using resque-scheduler, there are two ways to delay enqueue a job
28
+ # and how you do this depends on your use case.
25
29
  #
26
- # Resque.enqueue YourJob, params, approval_key: "your key", approval_at: time
27
- # This will enqueue the job for approval, which will pause the job until it is approved.
28
- # Once approved, the job will delay enqueue for time, and will execute immediately or at
29
- # that time depending on if the time has passed.
30
+ # Resque.enqueue YourJob, params, approval_key: "your key", approval_at: time
31
+ # This will enqueue the job for approval, which will pause the job until it is approved.
32
+ # Once approved, the job will delay enqueue for time, and will execute immediately or at
33
+ # that time depending on if the time has passed.
30
34
  #
31
- # This is the recommended method to use as it will not run the job early, and it will allow
32
- # you to release it without knowing if it is still delayed or not.
35
+ # This is the recommended method to use as it will not run the job early, and it will allow
36
+ # you to release it without knowing if it is still delayed or not.
33
37
  #
34
- # You can also do:
35
- # Resque.enqueue_at time, YourJob, params, approval_key: "your key"
36
- # This will delay enqueue the job - because it has not been enqueued yet, the job
37
- # cannot be releaed until the time has passed and the job is actually enqueued.
38
- # Any time after that point, it can be released. Releasing the key before this
39
- # time has no effect on this job.
38
+ # You can also do:
39
+ # Resque.enqueue_at time, YourJob, params, approval_key: "your key"
40
+ # This will delay enqueue the job - because it has not been enqueued yet, the job
41
+ # cannot be releaed until the time has passed and the job is actually enqueued.
42
+ # Any time after that point, it can be released. Releasing the key before this
43
+ # time has no effect on this job.
40
44
  class PendingJob
41
45
  include Resque::Plugins::Approve::RedisAccess
42
46
  include Comparable
@@ -86,8 +90,12 @@ module Resque
86
90
  end
87
91
  end
88
92
 
93
+ def approval_keys?
94
+ @approval_keys ||= (approve_options.key?(:approval_key) || approve_options[:requires_approval])
95
+ end
96
+
89
97
  def requires_approval?
90
- @requires_approval ||= approve_options.key?(:approval_key) || approve_options[:requires_approval]
98
+ @requires_approval ||= approval_keys? && too_many_running?
91
99
  end
92
100
 
93
101
  def approval_key
@@ -98,6 +106,21 @@ module Resque
98
106
  @approval_queue ||= approve_options[:approval_queue] || Resque.queue_from_class(klass)
99
107
  end
100
108
 
109
+ def max_active_jobs?
110
+ return @max_active_jobs if defined?(@max_active_jobs)
111
+
112
+ @max_active_jobs = max_active_jobs.positive?
113
+ end
114
+
115
+ def max_active_jobs
116
+ max_val = ((klass.respond_to?(:max_active_jobs) && klass.max_active_jobs) ||
117
+ (klass.instance_variable_defined?(:@max_active_jobs) && klass.instance_variable_get(:@max_active_jobs)))
118
+
119
+ return -1 if max_val.blank?
120
+
121
+ max_val.to_i
122
+ end
123
+
101
124
  def approval_at
102
125
  @approval_at ||= approve_options[:approval_at]&.to_time
103
126
  end
@@ -132,10 +155,10 @@ module Resque
132
155
  # Make sure the job is loaded into memory so we can use it even though we are going to delete it.
133
156
  stored_values
134
157
 
135
- return if class_name.blank?
136
-
137
158
  redis.del(job_key)
138
159
 
160
+ return if class_name.blank?
161
+
139
162
  queue.remove_job(self)
140
163
  end
141
164
 
@@ -143,6 +166,16 @@ module Resque
143
166
  @queue ||= Resque::Plugins::Approve::PendingJobQueue.new(approval_key)
144
167
  end
145
168
 
169
+ def max_jobs_perform_args(args)
170
+ remove_options = args.extract_options!.with_indifferent_access
171
+
172
+ return if remove_options.blank?
173
+
174
+ options = remove_options.slice!(:approval_queue, :approval_at, :requires_approval)
175
+
176
+ args << options if options.present?
177
+ end
178
+
146
179
  private
147
180
 
148
181
  def klass
@@ -158,15 +191,27 @@ module Resque
158
191
  end
159
192
 
160
193
  def extract_approve_options
161
- return if args.blank? || !args[-1].is_a?(Hash)
194
+ return if args.blank? || !@args[-1].is_a?(Hash)
195
+
196
+ self.approve_options = @args.pop
197
+
198
+ options = slice_approval_options
162
199
 
163
- self.approve_options = args.pop
200
+ @args << options.to_hash if options.present?
201
+ end
202
+
203
+ # rubocop:disable Layout/SpaceAroundOperators
204
+ def slice_approval_options
205
+ options = approve_options.slice!(:approval_key, :approval_queue, :approval_at, :requires_approval)
206
+ approve_options[:approval_key] ||= klass.default_queue_name if approve_options[:requires_approval]
164
207
 
165
- options = approve_options.slice!(:approval_key, :approval_queue, :approval_at)
208
+ options_approval_key(options)
166
209
 
167
- args << options.to_hash if options.present?
210
+ options
168
211
  end
169
212
 
213
+ # rubocop:enable Layout/SpaceAroundOperators
214
+
170
215
  def encode_args(*args)
171
216
  Resque.encode(args)
172
217
  end
@@ -180,6 +225,27 @@ module Resque
180
225
  def approve_options=(value)
181
226
  @approve_options = (value&.dup || {}).with_indifferent_access
182
227
  end
228
+
229
+ def too_many_running?
230
+ return true if queue.paused?
231
+ return true unless max_active_jobs?
232
+ return @too_many_running if defined?(@too_many_running)
233
+
234
+ num_running = queue.increment_running
235
+ max_jobs = max_active_jobs
236
+
237
+ queue.decrement_running if num_running > max_jobs
238
+ @too_many_running = num_running > max_jobs
239
+ end
240
+
241
+ def options_approval_key(options)
242
+ return unless max_active_jobs?
243
+ return if options.key?(:approval_key)
244
+
245
+ klass.include Resque::Plugins::Approve::AutoApproveNext unless klass.included_modules.include?(Resque::Plugins::Approve::AutoApproveNext)
246
+
247
+ options[:approval_key] = approve_options.fetch(:approval_key) { klass.default_queue_name }
248
+ end
183
249
  end
184
250
  # rubocop:enable Metrics/ClassLength
185
251
  end
@@ -1,5 +1,7 @@
1
1
  # frozen_string_literal: true
2
2
 
3
+ # rubocop:disable Metrics/ClassLength
4
+
3
5
  module Resque
4
6
  module Plugins
5
7
  module Approve
@@ -15,6 +17,24 @@ module Resque
15
17
  @approval_key = approval_key
16
18
  end
17
19
 
20
+ def num_running
21
+ redis.get(running_key)
22
+ end
23
+
24
+ def reset_running
25
+ redis.set(running_key, 0)
26
+ end
27
+
28
+ def increment_running
29
+ Resque::Plugins::Approve::ApprovalKeyList.new.add_key approval_key
30
+ redis.incr(running_key)
31
+ end
32
+
33
+ def decrement_running
34
+ Resque::Plugins::Approve::ApprovalKeyList.new.add_key approval_key
35
+ redis.decr(running_key)
36
+ end
37
+
18
38
  def pause
19
39
  redis.set(pause_key, true)
20
40
  redis.set(paused_count_key, 0)
@@ -89,6 +109,10 @@ module Resque
89
109
  delete_job(id)
90
110
  end
91
111
 
112
+ def remove_num(num_approve)
113
+ num_approve.times { remove_one }
114
+ end
115
+
92
116
  def remove_all
93
117
  true while remove_one
94
118
  end
@@ -161,10 +185,16 @@ module Resque
161
185
  @pause_key ||= "approve.job_queue.#{approval_key}.paused"
162
186
  end
163
187
 
188
+ def running_key
189
+ @running_key ||= "approve.job_queue.#{approval_key}.running"
190
+ end
191
+
164
192
  def paused_count_key
165
- @pause_key ||= "approve.job_queue.#{approval_key}.paused.count"
193
+ @paused_count_key ||= "approve.job_queue.#{approval_key}.paused.count"
166
194
  end
167
195
  end
168
196
  end
169
197
  end
170
198
  end
199
+
200
+ # rubocop:enable Metrics/ClassLength
@@ -3,7 +3,7 @@
3
3
  module Resque
4
4
  module Plugins
5
5
  module Approve
6
- VERSION = "0.0.4"
6
+ VERSION = "0.0.12"
7
7
  end
8
8
  end
9
9
  end
@@ -2,12 +2,15 @@
2
2
  <tr>
3
3
  <td>
4
4
  <a href="<%= u("approve") %>/job_list?<%= { approval_key: pending_job.approval_key }.to_param %>">
5
- <%= pending_job.approval_key %>
5
+ <%= pending_job.approval_key.presence || "&nbsp;&nbsp;&nbsp;".html_safe %>
6
6
  </a>
7
7
  </td>
8
8
  <td>
9
9
  <%= pending_job.num_jobs.to_i %>
10
10
  </td>
11
+ <td>
12
+ <%= pending_job.num_running.to_i %>
13
+ </td>
11
14
  <td>
12
15
  <% if pending_job.first_enqueued.present? %>
13
16
  <%= time_ago_in_words(pending_job.first_enqueued) %> ago