resque-approve 0.0.7 → 0.0.11

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: 346c8fcef056427d3dd16e9f6c3075d66ba2a209
4
- data.tar.gz: 961923c6b5fa71e78763dc9e3b29791ad737cba8
2
+ SHA256:
3
+ metadata.gz: 016bb046286c17b61fd13b252761500bf002430f1a0690d2cb6c49ec55353136
4
+ data.tar.gz: 81ee22490809b2822f22f8eb72a21c68a51b8782f1ba495145042fc050f7cd47
5
5
  SHA512:
6
- metadata.gz: 7c0b34d32004e25dee56d9e71d1bb6d681e308847267e2fbca0d8f7c098f75c48d094d03862430bd31a7b67606941741ef9160e83986752301793135951cd9d2
7
- data.tar.gz: e18cfc1e00817eaaf2b45adaed87640022818076e1fbef703cbe0efc308b5efc0755d2174f382185871d2ee47567c6272aad5ee8f8113a84273451e4ff0e307a
6
+ metadata.gz: d7c05c7dc064dfd6f4d7fbc99982ca91164cacbbe5effaa0a71537ff2387e19059dbc23e47cf794262e3f20be6a5e76257bb0024f5bdf96b91f339623c9e27ce
7
+ data.tar.gz: b05e8c34e6278dea52070dec7aeb782b8697dd85b6160477a02b680785f6ae9d2d3751f8a4cea30ff49e86b8a0d73ca80d0314b95c0002869a18661ede3d5371
@@ -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__))
@@ -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
@@ -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
@@ -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)
162
195
 
163
- self.approve_options = args.pop
196
+ self.approve_options = @args.pop
164
197
 
165
- options = approve_options.slice!(:approval_key, :approval_queue, :approval_at)
198
+ options = slice_approval_options
166
199
 
167
- args << options.to_hash if options.present?
200
+ @args << options.to_hash if options.present?
168
201
  end
169
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]
207
+
208
+ options_approval_key(options)
209
+
210
+ options
211
+ end
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,22 @@ 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
+ redis.incr(running_key)
30
+ end
31
+
32
+ def decrement_running
33
+ redis.decr(running_key)
34
+ end
35
+
18
36
  def pause
19
37
  redis.set(pause_key, true)
20
38
  redis.set(paused_count_key, 0)
@@ -165,6 +183,10 @@ module Resque
165
183
  @pause_key ||= "approve.job_queue.#{approval_key}.paused"
166
184
  end
167
185
 
186
+ def running_key
187
+ @running_key ||= "approve.job_queue.#{approval_key}.running"
188
+ end
189
+
168
190
  def paused_count_key
169
191
  @paused_count_key ||= "approve.job_queue.#{approval_key}.paused.count"
170
192
  end
@@ -172,3 +194,5 @@ module Resque
172
194
  end
173
195
  end
174
196
  end
197
+
198
+ # rubocop:enable Metrics/ClassLength
@@ -3,7 +3,7 @@
3
3
  module Resque
4
4
  module Plugins
5
5
  module Approve
6
- VERSION = "0.0.7"
6
+ VERSION = "0.0.11"
7
7
  end
8
8
  end
9
9
  end
@@ -8,6 +8,9 @@
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
@@ -28,6 +28,9 @@
28
28
  order_param("num_jobs", @sort_by, @sort_order) }.to_param %>">
29
29
  Pending Jobs
30
30
  </a></th>
31
+ <th>
32
+ Num Running
33
+ </th>
31
34
  <th><a href="<%= u("approve") %>?<%=
32
35
  { sort: "first_enqueued",
33
36
  page_size: @page_size,
@@ -64,3 +67,6 @@
64
67
  </div>
65
68
  <div class="approve_reset"/>
66
69
  <br/>
70
+ <div>
71
+ Approval v<%= VERSION %>
72
+ </div>
@@ -80,3 +80,6 @@
80
80
  <div class="approve_reset"/>
81
81
 
82
82
  <br/>
83
+ <div>
84
+ Approval v<%= VERSION %>
85
+ </div>
@@ -12,6 +12,10 @@
12
12
  </a>
13
13
  </p>
14
14
 
15
+ <p>
16
+ Num active jobs in this queue = <%= pending_queue.num_running %>
17
+ </p>
18
+
15
19
  <% if pending_queue.paused? %>
16
20
  <p>
17
21
  Paused. Skipped <%= pending_queue.num_ignored %> resume actions.
@@ -39,6 +43,11 @@
39
43
 
40
44
  <br/>
41
45
 
46
+ <form method="POST" action="<%= u("approve/reset_running") %>?<%=
47
+ { approval_key: pending_queue.approval_key }.to_param %>">
48
+ <input type="submit" name="" value="Reset Running"/>
49
+ </form>
50
+
42
51
  <form method="POST" action="<%= u("approve/delete_queue") %>?<%=
43
52
  { approval_key: pending_queue.approval_key }.to_param %>">
44
53
  <input type="submit" name="" value="Delete All"/>
@@ -74,3 +83,8 @@
74
83
 
75
84
  <div class="approve_reset"/>
76
85
  <br/>
86
+ <div>
87
+ <p>
88
+ Approval v<%= VERSION %>
89
+ </p>
90
+ </div>