resque-approval 1.0.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.
data/.gitignore ADDED
@@ -0,0 +1,4 @@
1
+ *.gem
2
+ Gemfile.lock
3
+ .rvmrc
4
+ spec/tmp
data/Gemfile ADDED
@@ -0,0 +1,4 @@
1
+ source 'https://rubygems.org'
2
+
3
+ # Specify your gem's dependencies in resque_approval.gemspec
4
+ gemspec
data/Guardfile ADDED
@@ -0,0 +1,5 @@
1
+ guard 'rspec', :version => 2 do
2
+ watch(%r{^spec/.+_spec\.rb$})
3
+ watch(%r{^lib/(.+)\.rb$}) { |m| "spec/lib/#{m[1]}_spec.rb" }
4
+ watch('spec/spec_helper.rb') { "spec" }
5
+ end
data/LICENSE ADDED
@@ -0,0 +1,22 @@
1
+ Copyright (c) 2012 Earle Clubb
2
+
3
+ MIT License
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining
6
+ a copy of this software and associated documentation files (the
7
+ "Software"), to deal in the Software without restriction, including
8
+ without limitation the rights to use, copy, modify, merge, publish,
9
+ distribute, sublicense, and/or sell copies of the Software, and to
10
+ permit persons to whom the Software is furnished to do so, subject to
11
+ the following conditions:
12
+
13
+ The above copyright notice and this permission notice shall be
14
+ included in all copies or substantial portions of the Software.
15
+
16
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
17
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
18
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
19
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
20
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
21
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
22
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
data/README.md ADDED
@@ -0,0 +1,100 @@
1
+ # resque-approval
2
+
3
+ A Resque plugin allowing jobs to be sent to a temporary queue to await approval.
4
+ Once the job is approved, it is placed on its normal queue.
5
+
6
+ ## Installation
7
+
8
+ Add this line to your application's Gemfile:
9
+
10
+ gem 'resque-approval'
11
+
12
+ And then execute:
13
+
14
+ $ bundle
15
+
16
+ Or install it yourself as:
17
+
18
+ $ gem install resque-approval
19
+
20
+ ## Usage
21
+
22
+ To enable approval holding for a job class, simply extend that class with
23
+ Resque::Plugins::Approval, like so:
24
+
25
+ ```ruby
26
+ class Job
27
+ extend Resque::Plugins::Approval
28
+
29
+ @queue = 'dummy'
30
+
31
+ def self.perform
32
+ # ...
33
+ end
34
+ end
35
+ ```
36
+
37
+ Then, when you want to queue up a job of that class, add a couple special
38
+ arguments to your job:
39
+
40
+ ```ruby
41
+ Resque.enqueue(Job, :requires_approval => true, :approval_message => 'Test')
42
+ ```
43
+
44
+ `:requires_approval` tells resque-approval to put your job in a special queue to
45
+ wait for approval. If its missing or false, your job is placed in its default
46
+ queue.
47
+
48
+ `:approval_message` is an optional message you can reference later.
49
+
50
+ To get a list of pending jobs:
51
+ ```ruby
52
+ Resque::Plugins::Approval.pending_job_keys
53
+ ```
54
+ This will return a list in first-in, first-out order. Each entry contains an id
55
+ and any message you may have specified when enqueueing the job.
56
+
57
+ To approve a job:
58
+ ```ruby
59
+ Job.approve(key)
60
+ ```
61
+ where key is an entry from the list returned by pending_job_keys. This will
62
+ move the job from the approval_required queue to the default queue for that job.
63
+
64
+ To reject a job:
65
+ ```ruby
66
+ Job.reject(key)
67
+ ```
68
+ This will delete the job from the approval_required queue, but will not move it
69
+ to the job's default queue. This effectively drops the job from the system.
70
+
71
+ ## Example
72
+
73
+ A sample session using the Job class above might look like this:
74
+
75
+ ```ruby
76
+ # Queue up a job with a message.
77
+ Resque.enqueue(Job, :requires_approval => true, :approval_message => 'Just a test')
78
+
79
+ # Queue up a job without a message.
80
+ Resque.enqueue(Job, :requires_approval => true)
81
+
82
+ # List the ids and messages.
83
+ pending_actions = Resque::Plugins::Approval.pending_job_keys
84
+ pending_actions.each do |action|
85
+ puts "id: #{action['id'], message: #{action['approval_message']}"
86
+ end
87
+
88
+ # Approve the first job...
89
+ Job.approve(pending_actions[0])
90
+
91
+ # But reject the second
92
+ Job.reject(pending_actions[1])
93
+ ```
94
+ ## Contributing
95
+
96
+ 1. Fork it
97
+ 2. Create your feature branch (`git checkout -b my-new-feature`)
98
+ 3. Commit your changes (`git commit -am 'Added some feature'`)
99
+ 4. Push to the branch (`git push origin my-new-feature`)
100
+ 5. Create new Pull Request
data/Rakefile ADDED
@@ -0,0 +1,13 @@
1
+ #!/usr/bin/env rake
2
+
3
+ require 'bundler/gem_tasks'
4
+ require 'rspec/core/rake_task'
5
+
6
+ RSpec::Core::RakeTask.new
7
+
8
+ task :default => :spec
9
+
10
+ desc 'Run guard'
11
+ task :guard do
12
+ sh %{ bundle exec guard --notify false}
13
+ end
@@ -0,0 +1,72 @@
1
+ require 'resque-approval/version'
2
+ require 'resque'
3
+
4
+ module Resque
5
+ module Plugins
6
+ module Approval
7
+ def self.pending_job_keys
8
+ keys = Resque.redis.hkeys('pending_jobs')
9
+ keys.map! { |key| JSON.parse(key) }
10
+ keys.sort! { |a, b| a['id'] <=> b['id'] }
11
+ end
12
+
13
+ def before_enqueue_approval(*args)
14
+ args = args[0] || {}
15
+
16
+ requires_approval = args.delete(:requires_approval) || args.delete('requires_approval')
17
+ if requires_approval
18
+ enqueue_for_approval(args)
19
+ allow_enqueue = false
20
+ else
21
+ allow_enqueue = true
22
+ end
23
+
24
+ allow_enqueue
25
+ end
26
+
27
+ def enqueue_for_approval(*args)
28
+ args = args[0] || {}
29
+
30
+ message = args.delete(:approval_message) || args.delete('approval_message')
31
+
32
+ Resque.enqueue_to(:approval_required, self, args)
33
+
34
+ id = Resque.size(:approval_required) - 1
35
+
36
+ if message
37
+ key = {:id => id, :approval_message => message}.to_json
38
+ else
39
+ key = { :id => id }.to_json
40
+ end
41
+
42
+ job = Resque.peek(:approval_required, id)
43
+ value = job.to_json
44
+
45
+ Resque.redis.hset('pending_jobs', key, value)
46
+ end
47
+
48
+ def approve(key)
49
+ value = Resque.redis.hget('pending_jobs', key)
50
+
51
+ return false if value.nil?
52
+
53
+ job = JSON.parse(value)
54
+
55
+ Resque.redis.hdel('pending_jobs', key)
56
+ Resque.redis.lrem('queue:approval_required', 1, job.to_json)
57
+ Resque.push(Resque.queue_from_class(self), job)
58
+ end
59
+
60
+ def reject(key)
61
+ value = Resque.redis.hget('pending_jobs', key)
62
+
63
+ return false if value.nil?
64
+
65
+ job = JSON.parse(value)
66
+
67
+ Resque.redis.hdel('pending_jobs', key)
68
+ Resque.redis.lrem('queue:approval_required', 1, job.to_json)
69
+ end
70
+ end
71
+ end
72
+ end
@@ -0,0 +1,7 @@
1
+ module Resque
2
+ module Plugins
3
+ module Approval
4
+ VERSION = '1.0.0'
5
+ end
6
+ end
7
+ end
@@ -0,0 +1,28 @@
1
+ # -*- encoding: utf-8 -*-
2
+ require File.expand_path('../lib/resque-approval/version', __FILE__)
3
+
4
+ Gem::Specification.new do |gem|
5
+ gem.name = 'resque-approval'
6
+ gem.version = Resque::Plugins::Approval::VERSION
7
+ gem.date = Time.now.strftime('%Y-%m-%d')
8
+ gem.description = %q{A Resque plugin allowing jobs to be sent to a temporary
9
+ queue to await approval. Once the job is approved, it
10
+ is placed on its normal queue.}
11
+ gem.summary = %q{A Resque plugin allowing jobs to be sent to a temporary
12
+ queue to await approval.}
13
+ gem.homepage = 'https://github.com/eclubb/resque-approval'
14
+ gem.authors = ['Earle Clubb']
15
+ gem.email = ['eclubb@valcom.com']
16
+
17
+ gem.files = `git ls-files`.split($\)
18
+ gem.executables = gem.files.grep(%r{^bin/}).map{ |f| File.basename(f) }
19
+ gem.test_files = gem.files.grep(%r{^(test|spec|features)/})
20
+ gem.require_paths = ['lib']
21
+
22
+ gem.add_runtime_dependency 'resque'
23
+
24
+ gem.add_development_dependency 'rake'
25
+ gem.add_development_dependency 'rspec'
26
+ gem.add_development_dependency 'guard'
27
+ gem.add_development_dependency 'guard-rspec'
28
+ end
@@ -0,0 +1,153 @@
1
+ require 'spec_helper'
2
+
3
+ class Job
4
+ extend Resque::Plugins::Approval
5
+
6
+ @queue = 'dummy'
7
+
8
+ def self.perform
9
+ end
10
+ end
11
+
12
+ describe "Resque::Plugins::Approval" do
13
+ before do
14
+ Resque.remove_queue(:dummy)
15
+ Resque.remove_queue(:approval_required)
16
+ Resque.redis.del('pending_jobs')
17
+ end
18
+
19
+ it "is a valid Resque plugin" do
20
+ lambda { Resque::Plugin.lint(Resque::Plugins::Approval) }.should_not raise_error
21
+ end
22
+
23
+ describe "#pending_job_keys" do
24
+ 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
+ Job.enqueue_for_approval
27
+ Job.enqueue_for_approval(:approval_message => 'test message 2')
28
+
29
+ keys = [{'id' => 0, 'approval_message' => 'test message 1'},
30
+ {'id' => 1},
31
+ {'id' => 2, 'approval_message' => 'test message 2'}]
32
+ Resque::Plugins::Approval.pending_job_keys.should == keys
33
+ end
34
+ end
35
+
36
+ describe ".before_enqueue_approval" do
37
+ context "when a job requires approval (via symbol or string)" do
38
+ it "calls enqueue_for_approval" do
39
+ Job.should_receive(:enqueue_for_approval).twice.with({})
40
+ Resque.enqueue(Job, :requires_approval => true)
41
+ Resque.enqueue(Job, 'requires_approval' => true)
42
+ end
43
+ end
44
+
45
+ context "when a job does not require approval" do
46
+ it "does not call enqueue_for_approval" do
47
+ Job.should_not_receive(:enqueue_for_approval)
48
+ Resque.enqueue(Job)
49
+ end
50
+ end
51
+ end
52
+
53
+ describe ".enqueue_for_approval" do
54
+ it "adds the job to the appoval queue" do
55
+ Job.enqueue_for_approval
56
+ Resque.size(:approval_required).should == 1
57
+ end
58
+
59
+ it "does not add the job to the dummy queue" do
60
+ Job.enqueue_for_approval
61
+ Resque.size(:dummy).should == 0
62
+ end
63
+
64
+ it "adds an entry to the 'pending_jobs' hash" do
65
+ Job.enqueue_for_approval()
66
+
67
+ key = '{"id":0}'
68
+ value = '{"class":"Job","args":[{}]}'
69
+
70
+ Resque.redis.hget('pending_jobs', key).should == value
71
+ end
72
+
73
+ context "with an approval message (via symbol)" do
74
+ it "includes the message in the 'pending_jobs' hash entry" do
75
+ Job.enqueue_for_approval(:approval_message => 'symbol test message')
76
+
77
+ key = '{"id":0,"approval_message":"symbol test message"}'
78
+ value = '{"class":"Job","args":[{}]}'
79
+
80
+ Resque.redis.hget('pending_jobs', key).should == value
81
+ end
82
+ end
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')
87
+
88
+ key = '{"id":0,"approval_message":"string test message"}'
89
+ value = '{"class":"Job","args":[{}]}'
90
+
91
+ Resque.redis.hget('pending_jobs', key).should == value
92
+ end
93
+ end
94
+ end
95
+
96
+ describe ".approve" do
97
+ it "moves the job from the approval queue to its normal queue" do
98
+ key = '{"id":0}'
99
+
100
+ Resque.enqueue(Job, :requires_approval => true)
101
+ Job.approve(key)
102
+
103
+ Resque.size(:approval_required).should == 0
104
+ Resque.size(:dummy).should == 1
105
+ end
106
+
107
+ it "deletes the entry in the 'pending_jobs' hash" do
108
+ key = '{"id":0}'
109
+
110
+ Resque.enqueue(Job, :requires_approval => true)
111
+ Job.approve(key)
112
+
113
+ Resque.redis.hget('pending_jobs', key).should be_nil
114
+ end
115
+
116
+ it "returns false when key can not be found" do
117
+ Job.approve('bad key').should == false
118
+ end
119
+ end
120
+
121
+ describe ".reject" do
122
+ it "deletes the job from the approval queue" do
123
+ key = '{"id":0}'
124
+
125
+ Resque.enqueue(Job, :requires_approval => true)
126
+ Job.reject(key)
127
+
128
+ Resque.size(:approval_required).should == 0
129
+ end
130
+
131
+ it "does not add the job to its normal queue" do
132
+ key = '{"id":0}'
133
+
134
+ Resque.enqueue(Job, :requires_approval => true)
135
+ Job.reject(key)
136
+
137
+ Resque.size(:dummy).should == 0
138
+ end
139
+
140
+ it "deletes the entry in the 'pending_jobs' hash" do
141
+ key = '{"id":0}'
142
+
143
+ Resque.enqueue(Job, :requires_approval => true)
144
+ Job.reject(key)
145
+
146
+ Resque.redis.hget('pending_jobs', key).should be_nil
147
+ end
148
+
149
+ it "returns false when key can not be found" do
150
+ Job.reject('bad key').should == false
151
+ end
152
+ end
153
+ end
@@ -0,0 +1,7 @@
1
+ daemonize yes
2
+ pidfile /tmp/redis-test.pid
3
+ port 9736
4
+ dbfilename redis-test-dump.rdb
5
+ dir /tmp
6
+ loglevel debug
7
+ logfile stdout
@@ -0,0 +1,29 @@
1
+ require 'resque-approval'
2
+
3
+ ROOT_PATH = File.expand_path('..', __FILE__)
4
+ TMP_PATH = '/tmp'
5
+
6
+ def start_redis
7
+ puts 'Starting redis for testing at localhost:9736...'
8
+ `redis-server #{ROOT_PATH}/redis-test.conf`
9
+ Resque.redis = 'localhost:9736'
10
+ sleep 0.1
11
+ end
12
+
13
+ def stop_redis
14
+ puts "\nKilling test redis server..."
15
+ %x{
16
+ cat #{TMP_PATH}/redis-test.pid | xargs kill -QUIT
17
+ rm -f #{TMP_PATH}/redis-test-dump.rdb
18
+ }
19
+ end
20
+
21
+ RSpec.configure do |config|
22
+ config.before(:suite) do
23
+ start_redis
24
+ end
25
+
26
+ config.after(:suite) do
27
+ stop_redis
28
+ end
29
+ end
metadata ADDED
@@ -0,0 +1,117 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: resque-approval
3
+ version: !ruby/object:Gem::Version
4
+ version: 1.0.0
5
+ prerelease:
6
+ platform: ruby
7
+ authors:
8
+ - Earle Clubb
9
+ autorequire:
10
+ bindir: bin
11
+ cert_chain: []
12
+ date: 2012-06-06 00:00:00.000000000 Z
13
+ dependencies:
14
+ - !ruby/object:Gem::Dependency
15
+ name: resque
16
+ requirement: &12740880 !ruby/object:Gem::Requirement
17
+ none: false
18
+ requirements:
19
+ - - ! '>='
20
+ - !ruby/object:Gem::Version
21
+ version: '0'
22
+ type: :runtime
23
+ prerelease: false
24
+ version_requirements: *12740880
25
+ - !ruby/object:Gem::Dependency
26
+ name: rake
27
+ requirement: &12740240 !ruby/object:Gem::Requirement
28
+ none: false
29
+ requirements:
30
+ - - ! '>='
31
+ - !ruby/object:Gem::Version
32
+ version: '0'
33
+ type: :development
34
+ prerelease: false
35
+ version_requirements: *12740240
36
+ - !ruby/object:Gem::Dependency
37
+ name: rspec
38
+ requirement: &12739480 !ruby/object:Gem::Requirement
39
+ none: false
40
+ requirements:
41
+ - - ! '>='
42
+ - !ruby/object:Gem::Version
43
+ version: '0'
44
+ type: :development
45
+ prerelease: false
46
+ version_requirements: *12739480
47
+ - !ruby/object:Gem::Dependency
48
+ name: guard
49
+ requirement: &12737500 !ruby/object:Gem::Requirement
50
+ none: false
51
+ requirements:
52
+ - - ! '>='
53
+ - !ruby/object:Gem::Version
54
+ version: '0'
55
+ type: :development
56
+ prerelease: false
57
+ version_requirements: *12737500
58
+ - !ruby/object:Gem::Dependency
59
+ name: guard-rspec
60
+ requirement: &12732800 !ruby/object:Gem::Requirement
61
+ none: false
62
+ requirements:
63
+ - - ! '>='
64
+ - !ruby/object:Gem::Version
65
+ version: '0'
66
+ type: :development
67
+ prerelease: false
68
+ version_requirements: *12732800
69
+ description: ! "A Resque plugin allowing jobs to be sent to a temporary\n queue
70
+ to await approval. Once the job is approved, it\n is placed
71
+ on its normal queue."
72
+ email:
73
+ - eclubb@valcom.com
74
+ executables: []
75
+ extensions: []
76
+ extra_rdoc_files: []
77
+ files:
78
+ - .gitignore
79
+ - Gemfile
80
+ - Guardfile
81
+ - LICENSE
82
+ - README.md
83
+ - Rakefile
84
+ - lib/resque-approval.rb
85
+ - lib/resque-approval/version.rb
86
+ - resque-approval.gemspec
87
+ - spec/lib/resque-approval_spec.rb
88
+ - spec/redis-test.conf
89
+ - spec/spec_helper.rb
90
+ homepage: https://github.com/eclubb/resque-approval
91
+ licenses: []
92
+ post_install_message:
93
+ rdoc_options: []
94
+ require_paths:
95
+ - lib
96
+ required_ruby_version: !ruby/object:Gem::Requirement
97
+ none: false
98
+ requirements:
99
+ - - ! '>='
100
+ - !ruby/object:Gem::Version
101
+ version: '0'
102
+ required_rubygems_version: !ruby/object:Gem::Requirement
103
+ none: false
104
+ requirements:
105
+ - - ! '>='
106
+ - !ruby/object:Gem::Version
107
+ version: '0'
108
+ requirements: []
109
+ rubyforge_project:
110
+ rubygems_version: 1.8.10
111
+ signing_key:
112
+ specification_version: 3
113
+ summary: A Resque plugin allowing jobs to be sent to a temporary queue to await approval.
114
+ test_files:
115
+ - spec/lib/resque-approval_spec.rb
116
+ - spec/redis-test.conf
117
+ - spec/spec_helper.rb