resque-approval 1.0.1 → 1.2.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -6,14 +6,14 @@ module Resque
6
6
  module Approval
7
7
  def self.pending_job_keys
8
8
  keys = Resque.redis.hkeys('pending_jobs')
9
- keys.map! { |key| JSON.parse(key) }
9
+ keys.map! { |key| Resque.decode(key) }
10
10
  keys.sort! { |a, b| a['id'] <=> b['id'] }
11
11
  end
12
12
 
13
13
  def before_enqueue_approval(*args)
14
14
  args = args[0] || {}
15
15
 
16
- requires_approval = args.delete(:requires_approval) || args.delete('requires_approval')
16
+ requires_approval = extract_value(args, :requires_approval)
17
17
  if requires_approval
18
18
  enqueue_for_approval(args)
19
19
  allow_enqueue = false
@@ -24,52 +24,93 @@ module Resque
24
24
  allow_enqueue
25
25
  end
26
26
 
27
+ def before_delayed_enqueue(args)
28
+ key = extract_value(args, :approval_key)
29
+ Resque.redis.hdel('pending_jobs', key)
30
+ end
31
+
27
32
  def enqueue_for_approval(*args)
28
33
  args = args[0] || {}
29
34
 
30
- message = args.delete(:approval_message) || args.delete('approval_message')
35
+ message = extract_value(args, :approval_message)
36
+ timeout = extract_value(args, :approval_timeout)
31
37
 
32
- Resque.enqueue_to(:approval_required, self, args)
38
+ id = Resque.redis.hlen('pending_jobs')
39
+ key = build_key(id, message, timeout)
33
40
 
34
- id = Resque.size(:approval_required) - 1
41
+ if timeout && Resque.respond_to?(:enqueue_in)
42
+ approval_args = args.merge(:approval_key => key)
43
+ Resque.enqueue_in(timeout, self, approval_args)
35
44
 
36
- if message
37
- key = {:id => id, :approval_message => message}.to_json
45
+ queue = Resque.queue_from_class(self)
46
+ value = build_value(queue, approval_args)
38
47
  else
39
- key = { :id => id }.to_json
40
- end
48
+ Resque.enqueue_to(:approval_required, self, args)
41
49
 
42
- job = Resque.peek(:approval_required, id)
43
- value = job.to_json
50
+ value = build_value(nil, args)
51
+ end
44
52
 
45
53
  Resque.redis.hset('pending_jobs', key, value)
46
54
  end
47
55
 
48
56
  def approve(key)
49
- value = Resque.redis.hget('pending_jobs', key)
50
-
51
- return false if value.nil?
57
+ job = remove_from_pending(key)
52
58
 
53
- job = JSON.parse(value)
59
+ return false if job.nil?
54
60
 
55
- Resque.redis.hdel('pending_jobs', key)
56
- Resque.redis.lrem('queue:approval_required', 1, job.to_json)
57
61
  Resque.push(Resque.queue_from_class(self), job)
58
62
 
59
63
  true
60
64
  end
61
65
 
62
66
  def reject(key)
67
+ !!remove_from_pending(key)
68
+ end
69
+
70
+ def remove_from_pending(key)
63
71
  value = Resque.redis.hget('pending_jobs', key)
64
72
 
65
- return false if value.nil?
73
+ return if value.nil?
66
74
 
67
- job = JSON.parse(value)
75
+ encoded_job = value
76
+ job = Resque.decode(value)
68
77
 
69
78
  Resque.redis.hdel('pending_jobs', key)
70
- Resque.redis.lrem('queue:approval_required', 1, job.to_json)
71
79
 
72
- true
80
+ decoded_key = Resque.decode(key)
81
+ if decoded_key.has_key? 'approval_timeout'
82
+ Array(Resque.redis.keys("delayed:*")).each do |key|
83
+ destroyed = Resque.redis.lrem(key, 1, encoded_job)
84
+ break if destroyed > 0
85
+ end
86
+ else
87
+ Resque.redis.lrem('queue:approval_required', 1, encoded_job)
88
+ end
89
+
90
+ job
91
+ end
92
+
93
+ private
94
+
95
+ def extract_value(args, key)
96
+ args.delete(key.to_sym) || args.delete(key.to_s)
97
+ end
98
+
99
+ def build_key(id, message, timeout = nil)
100
+ key = { :id => id }
101
+
102
+ key.merge!(:approval_message => message) if message
103
+ key.merge!(:approval_timeout => timeout) if timeout
104
+
105
+ Resque.encode(key)
106
+ end
107
+
108
+ def build_value(queue = nil, *args)
109
+ value = { :class => self.to_s, :args => args }
110
+
111
+ value.merge!(:queue => queue) if queue
112
+
113
+ Resque.encode(value)
73
114
  end
74
115
  end
75
116
  end
@@ -1,7 +1,7 @@
1
1
  module Resque
2
2
  module Plugins
3
3
  module Approval
4
- VERSION = '1.0.1'
4
+ VERSION = '1.2.0'
5
5
  end
6
6
  end
7
7
  end
@@ -25,4 +25,5 @@ Gem::Specification.new do |gem|
25
25
  gem.add_development_dependency 'rspec'
26
26
  gem.add_development_dependency 'guard'
27
27
  gem.add_development_dependency 'guard-rspec'
28
+ gem.add_development_dependency 'resque-scheduler'
28
29
  end
@@ -14,6 +14,7 @@ describe "Resque::Plugins::Approval" do
14
14
  Resque.remove_queue(:dummy)
15
15
  Resque.remove_queue(:approval_required)
16
16
  Resque.redis.del('pending_jobs')
17
+ Resque.reset_delayed_queue
17
18
  end
18
19
 
19
20
  it "is a valid Resque plugin" do
@@ -22,23 +23,22 @@ describe "Resque::Plugins::Approval" do
22
23
 
23
24
  describe "#pending_job_keys" do
24
25
  it "lists keys (ordered by id) for all jobs that are waiting for approval" do
25
- Job.enqueue_for_approval(:approval_message => 'test message 1')
26
26
  Job.enqueue_for_approval
27
- Job.enqueue_for_approval(:approval_message => 'test message 2')
27
+ Job.enqueue_for_approval(:approval_timeout => 10)
28
+ Job.enqueue_for_approval(:approval_message => 'test message')
28
29
 
29
- keys = [{'id' => 0, 'approval_message' => 'test message 1'},
30
- {'id' => 1},
31
- {'id' => 2, 'approval_message' => 'test message 2'}]
30
+ keys = [{ 'id' => 0 },
31
+ { 'id' => 1, 'approval_timeout' => 10 },
32
+ { 'id' => 2, 'approval_message' => 'test message' }]
32
33
  Resque::Plugins::Approval.pending_job_keys.should == keys
33
34
  end
34
35
  end
35
36
 
36
37
  describe ".before_enqueue_approval" do
37
- context "when a job requires approval (via symbol or string)" do
38
+ context "when a job requires approval" do
38
39
  it "calls enqueue_for_approval" do
39
- Job.should_receive(:enqueue_for_approval).twice.with({})
40
+ Job.should_receive(:enqueue_for_approval).with({})
40
41
  Resque.enqueue(Job, :requires_approval => true)
41
- Resque.enqueue(Job, 'requires_approval' => true)
42
42
  end
43
43
  end
44
44
 
@@ -62,7 +62,7 @@ describe "Resque::Plugins::Approval" do
62
62
  end
63
63
 
64
64
  it "adds an entry to the 'pending_jobs' hash" do
65
- Job.enqueue_for_approval()
65
+ Job.enqueue_for_approval
66
66
 
67
67
  key = '{"id":0}'
68
68
  value = '{"class":"Job","args":[{}]}'
@@ -70,47 +70,72 @@ describe "Resque::Plugins::Approval" do
70
70
  Resque.redis.hget('pending_jobs', key).should == value
71
71
  end
72
72
 
73
- context "with an approval message (via symbol)" do
73
+ context "with an approval message" do
74
74
  it "includes the message in the 'pending_jobs' hash entry" do
75
- Job.enqueue_for_approval(:approval_message => 'symbol test message')
75
+ Job.enqueue_for_approval(:approval_message => 'test message')
76
76
 
77
- key = '{"id":0,"approval_message":"symbol test message"}'
77
+ key = '{"id":0,"approval_message":"test message"}'
78
78
  value = '{"class":"Job","args":[{}]}'
79
79
 
80
80
  Resque.redis.hget('pending_jobs', key).should == value
81
81
  end
82
82
  end
83
83
 
84
- context "with an approval message (via string)" do
85
- it "includes the message in the 'pending_jobs' hash entry" do
86
- Job.enqueue_for_approval('approval_message' => 'string test message')
84
+ context "with a timeout" do
85
+ it "includes the timeout in the 'pending_jobs' hash entry" do
86
+ Job.enqueue_for_approval(:approval_timeout => 10)
87
87
 
88
- key = '{"id":0,"approval_message":"string test message"}'
89
- value = '{"class":"Job","args":[{}]}'
88
+ key = '{"id":0,"approval_timeout":10}'
89
+ args = { :approval_key => key }
90
+ value = { :class => Job, :args => [args], :queue => :dummy }
91
+ value = Resque.encode(value)
90
92
 
91
93
  Resque.redis.hget('pending_jobs', key).should == value
92
94
  end
95
+
96
+ it "schedules the job" do
97
+ Resque.count_all_scheduled_jobs.should == 0
98
+
99
+ Job.enqueue_for_approval(:approval_timeout => 10)
100
+
101
+ Resque.count_all_scheduled_jobs.should == 1
102
+ end
103
+
104
+ it "does not add the job to the appoval queue" do
105
+ Job.enqueue_for_approval(:approval_timeout => 10)
106
+ Resque.size(:approval_required).should == 0
107
+ end
93
108
  end
94
109
  end
95
110
 
96
111
  describe ".approve" do
97
- it "moves the job from the approval queue to its normal queue" do
112
+ it "calls .remove_from_pending" do
98
113
  key = '{"id":0}'
99
114
 
100
- Resque.enqueue(Job, :requires_approval => true)
115
+ Job.should_receive(:remove_from_pending).with(key)
101
116
  Job.approve(key)
117
+ end
118
+
119
+ context "without a timeout" do
120
+ it "adds the job to its normal queue" do
121
+ key = '{"id":0}'
122
+
123
+ Job.enqueue_for_approval
124
+ Job.approve(key)
102
125
 
103
- Resque.size(:approval_required).should == 0
104
- Resque.size(:dummy).should == 1
126
+ Resque.size(:dummy).should == 1
127
+ end
105
128
  end
106
129
 
107
- it "deletes the entry in the 'pending_jobs' hash" do
108
- key = '{"id":0}'
130
+ context "with a timeout" do
131
+ it "adds the job to its normal queue" do
132
+ key = '{"id":0,"approval_timeout":10}'
109
133
 
110
- Resque.enqueue(Job, :requires_approval => true)
111
- Job.approve(key)
134
+ Job.enqueue_for_approval(:approval_timeout => 10)
135
+ Job.approve(key)
112
136
 
113
- Resque.redis.hget('pending_jobs', key).should be_nil
137
+ Resque.size(:dummy).should == 1
138
+ end
114
139
  end
115
140
 
116
141
  it "returns false when key can not be found" do
@@ -119,35 +144,106 @@ describe "Resque::Plugins::Approval" do
119
144
  end
120
145
 
121
146
  describe ".reject" do
122
- it "deletes the job from the approval queue" do
147
+ it "calls .remove_from_pending" do
123
148
  key = '{"id":0}'
124
149
 
125
- Resque.enqueue(Job, :requires_approval => true)
150
+ Job.should_receive(:remove_from_pending).with(key)
126
151
  Job.reject(key)
127
-
128
- Resque.size(:approval_required).should == 0
129
152
  end
130
153
 
131
154
  it "does not add the job to its normal queue" do
132
155
  key = '{"id":0}'
133
156
 
134
- Resque.enqueue(Job, :requires_approval => true)
157
+ Job.enqueue_for_approval
135
158
  Job.reject(key)
136
159
 
137
160
  Resque.size(:dummy).should == 0
138
161
  end
139
162
 
163
+ it "returns false when key can not be found" do
164
+ Job.reject('bad key').should == false
165
+ end
166
+ end
167
+
168
+ describe ".remove_from_pending" do
169
+ context "without a timeout" do
170
+ it "deletes the job from the approval queue" do
171
+ key = '{"id":0}'
172
+
173
+ Job.enqueue_for_approval
174
+ Job.remove_from_pending(key)
175
+
176
+ Resque.size(:approval_required).should == 0
177
+ end
178
+ end
179
+
180
+ context "with a timeout" do
181
+ it "unschedules the job" do
182
+ key = '{"id":0,"approval_timeout":10}'
183
+
184
+ Job.enqueue_for_approval(:approval_timeout => 10)
185
+
186
+ Resque.count_all_scheduled_jobs.should == 1
187
+
188
+ Job.remove_from_pending(key)
189
+
190
+ Resque.count_all_scheduled_jobs.should == 0
191
+ end
192
+ end
193
+
140
194
  it "deletes the entry in the 'pending_jobs' hash" do
141
195
  key = '{"id":0}'
142
196
 
143
- Resque.enqueue(Job, :requires_approval => true)
144
- Job.reject(key)
197
+ Job.enqueue_for_approval
198
+ Job.remove_from_pending(key)
145
199
 
146
200
  Resque.redis.hget('pending_jobs', key).should be_nil
147
201
  end
148
202
 
149
- it "returns false when key can not be found" do
150
- Job.reject('bad key').should == false
203
+ it "returns job when key can be found" do
204
+ key = '{"id":0}'
205
+ job = { 'class' => 'Job', 'args' => [{}] }
206
+
207
+ Job.enqueue_for_approval
208
+ job = Job.remove_from_pending(key).should == job
209
+ end
210
+
211
+ it "returns nil when key can not be found" do
212
+ Job.remove_from_pending('bad key').should == nil
213
+ end
214
+ end
215
+
216
+ describe ".extract_value" do
217
+ context "when key is a symbol" do
218
+ it "deletes and returns value by symbol-referenced key" do
219
+ hash = { :key => 1 }
220
+
221
+ Job.send(:extract_value, hash, :key).should == 1
222
+ hash.should == {}
223
+ end
224
+
225
+ it "deletes and returns value by string-referenced key" do
226
+ hash = { :key => 1 }
227
+
228
+ Job.send(:extract_value, hash, 'key').should == 1
229
+ hash.should == {}
230
+ end
231
+ end
232
+
233
+ context "when key is a string" do
234
+ it "deletes and returns value by symbol-referenced key" do
235
+ hash = { 'key' => 1 }
236
+
237
+ Job.send(:extract_value, hash, :key).should == 1
238
+ hash.should == {}
239
+ end
240
+
241
+ it "deletes and returns value by string-referenced key" do
242
+ hash = { 'key' => 1 }
243
+
244
+ Job.send(:extract_value, hash, 'key').should == 1
245
+ hash.should == {}
246
+ end
151
247
  end
152
248
  end
153
249
  end
data/spec/spec_helper.rb CHANGED
@@ -1,4 +1,6 @@
1
+ require 'rspec'
1
2
  require 'resque-approval'
3
+ require 'resque_scheduler'
2
4
 
3
5
  def start_redis
4
6
  redis_config = <<END
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: resque-approval
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.0.1
4
+ version: 1.2.0
5
5
  prerelease:
6
6
  platform: ruby
7
7
  authors:
@@ -9,11 +9,11 @@ authors:
9
9
  autorequire:
10
10
  bindir: bin
11
11
  cert_chain: []
12
- date: 2012-06-06 00:00:00.000000000 Z
12
+ date: 2012-06-14 00:00:00.000000000 Z
13
13
  dependencies:
14
14
  - !ruby/object:Gem::Dependency
15
15
  name: resque
16
- requirement: &12083540 !ruby/object:Gem::Requirement
16
+ requirement: &11915740 !ruby/object:Gem::Requirement
17
17
  none: false
18
18
  requirements:
19
19
  - - ! '>='
@@ -21,10 +21,10 @@ dependencies:
21
21
  version: '0'
22
22
  type: :runtime
23
23
  prerelease: false
24
- version_requirements: *12083540
24
+ version_requirements: *11915740
25
25
  - !ruby/object:Gem::Dependency
26
26
  name: rake
27
- requirement: &12082920 !ruby/object:Gem::Requirement
27
+ requirement: &11910380 !ruby/object:Gem::Requirement
28
28
  none: false
29
29
  requirements:
30
30
  - - ! '>='
@@ -32,10 +32,10 @@ dependencies:
32
32
  version: '0'
33
33
  type: :development
34
34
  prerelease: false
35
- version_requirements: *12082920
35
+ version_requirements: *11910380
36
36
  - !ruby/object:Gem::Dependency
37
37
  name: rspec
38
- requirement: &12082180 !ruby/object:Gem::Requirement
38
+ requirement: &11908520 !ruby/object:Gem::Requirement
39
39
  none: false
40
40
  requirements:
41
41
  - - ! '>='
@@ -43,10 +43,10 @@ dependencies:
43
43
  version: '0'
44
44
  type: :development
45
45
  prerelease: false
46
- version_requirements: *12082180
46
+ version_requirements: *11908520
47
47
  - !ruby/object:Gem::Dependency
48
48
  name: guard
49
- requirement: &12080700 !ruby/object:Gem::Requirement
49
+ requirement: &11907760 !ruby/object:Gem::Requirement
50
50
  none: false
51
51
  requirements:
52
52
  - - ! '>='
@@ -54,10 +54,10 @@ dependencies:
54
54
  version: '0'
55
55
  type: :development
56
56
  prerelease: false
57
- version_requirements: *12080700
57
+ version_requirements: *11907760
58
58
  - !ruby/object:Gem::Dependency
59
59
  name: guard-rspec
60
- requirement: &12078080 !ruby/object:Gem::Requirement
60
+ requirement: &11907160 !ruby/object:Gem::Requirement
61
61
  none: false
62
62
  requirements:
63
63
  - - ! '>='
@@ -65,7 +65,18 @@ dependencies:
65
65
  version: '0'
66
66
  type: :development
67
67
  prerelease: false
68
- version_requirements: *12078080
68
+ version_requirements: *11907160
69
+ - !ruby/object:Gem::Dependency
70
+ name: resque-scheduler
71
+ requirement: &11906580 !ruby/object:Gem::Requirement
72
+ none: false
73
+ requirements:
74
+ - - ! '>='
75
+ - !ruby/object:Gem::Version
76
+ version: '0'
77
+ type: :development
78
+ prerelease: false
79
+ version_requirements: *11906580
69
80
  description: ! "A Resque plugin allowing jobs to be sent to a temporary\n queue
70
81
  to await approval. Once the job is approved, it\n is placed
71
82
  on its normal queue."