resque-approve 0.0.4 → 0.0.12

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
- 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