resque-mock 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (4) hide show
  1. data/Rakefile +10 -0
  2. data/lib/resque/mock.rb +84 -0
  3. data/spec/mock_spec.rb +111 -0
  4. metadata +85 -0
@@ -0,0 +1,10 @@
1
+ begin
2
+ require 'rspec/core/rake_task'
3
+ RSpec::Core::RakeTask.new do |t|
4
+ t.rspec_opts = %w[ -c ]
5
+ t.pattern = 'spec/**/*_spec.rb'
6
+ end
7
+
8
+ task :default => :spec
9
+ rescue LoadError
10
+ end
@@ -0,0 +1,84 @@
1
+ require 'resque'
2
+
3
+ module Resque
4
+ def self.mock!
5
+ extend MockExt
6
+ end
7
+
8
+ module MockExt
9
+ def async
10
+ @async = true
11
+ create_worker_manager
12
+ yield
13
+ ensure
14
+ wait_for_worker_manager
15
+ @async = false
16
+ end
17
+
18
+ def enqueue(klass, *args)
19
+ puts "Mock enqueue: async=#{!!@async}, stack_depth=#{caller.size}, #{klass}, #{args.inspect}" if ENV['VERBOSE']
20
+ defer(klass, args)
21
+ end
22
+
23
+ def enqueue_in(delay, klass, *args)
24
+ puts "Mock enqueue in #{delay}: async=#{!!@async}, stack_depth=#{caller.size}, #{klass}, #{args.inspect}" if ENV['VERBOSE']
25
+ defer(klass, args, delay)
26
+ end
27
+
28
+ def defer(klass, args, delay = nil)
29
+ if @async
30
+ add_job('payload' => { 'class' => klass, 'args' => args }, 'delay' => delay)
31
+ else
32
+ sleep delay if delay
33
+ klass.perform(*roundtrip(args))
34
+ end
35
+ end
36
+
37
+ def create_worker_manager
38
+ @worker_manager = Thread.new do
39
+ Thread.current.abort_on_exception = true
40
+ worker_threads = []
41
+
42
+ while true
43
+ break if Thread.current[:exit] && worker_threads.empty? && Thread.current[:jobs].empty?
44
+
45
+ worker_threads.reject! {|t| !t.alive? }
46
+
47
+ while Thread.current[:jobs] && job_data = Thread.current[:jobs].shift
48
+ worker_threads << create_worker_thread_for(job_data)
49
+ end
50
+
51
+ sleep 0.5
52
+ end
53
+ end.tap {|t| t[:jobs] = [] }
54
+ end
55
+
56
+ def wait_for_worker_manager
57
+ @worker_manager[:exit] = true
58
+ @worker_manager.join
59
+ @worker_manager = nil
60
+ end
61
+
62
+ def create_worker_thread_for(data)
63
+ Thread.new(data) do |data|
64
+ Thread.current.abort_on_exception = true
65
+ if delay = data['delay']
66
+ sleep delay
67
+ end
68
+
69
+ klass = data['payload']['class']
70
+ puts "Mock perform: #{klass}.perform(*#{data['payload']['args'].inspect})" if ENV['VERBOSE']
71
+ klass.perform(*roundtrip(data['payload']['args']))
72
+ puts "Mock exit: #{klass}.perform(*#{data['payload']['args'].inspect})" if ENV['VERBOSE']
73
+ end
74
+ end
75
+
76
+ def roundtrip(args)
77
+ decode(encode(args))
78
+ end
79
+
80
+ def add_job(data)
81
+ @worker_manager[:jobs] << data
82
+ end
83
+ end
84
+ end
@@ -0,0 +1,111 @@
1
+ #require 'spec_helper'
2
+ require 'resque/mock'
3
+
4
+ Resque.mock!
5
+
6
+ class Performer
7
+ def self.run?
8
+ !!@args
9
+ end
10
+
11
+ def self.args
12
+ @args
13
+ end
14
+
15
+ def self.runs
16
+ @runs || 0
17
+ end
18
+
19
+ def self.perform(*args)
20
+ @runs += 1
21
+ if Hash === (options = args.first)
22
+ if runs_left = options['runs']
23
+ runs_left -= 1
24
+ if runs_left > 0
25
+ Resque.enqueue(self, 'runs' => runs_left)
26
+ end
27
+ end
28
+ end
29
+ @args = args
30
+ end
31
+
32
+ def self.reset!
33
+ @args = nil
34
+ @runs = 0
35
+ end
36
+ end
37
+
38
+ class BadPerformer < Performer
39
+ def self.perform(*args)
40
+ raise 'hello'
41
+ end
42
+ end
43
+
44
+ describe Resque do
45
+ before { Performer.reset! }
46
+
47
+ describe "synchronously" do
48
+ it "performs jobs without delay" do
49
+ Resque.enqueue(Performer, 'hello', 'there')
50
+ Performer.should be_run
51
+ Performer.args.should == ['hello', 'there']
52
+ end
53
+
54
+ it "performs jobs with a delay" do
55
+ Resque.should_receive(:sleep).with(5)
56
+ Resque.enqueue_in(5, Performer, 'hello', 'there')
57
+ Performer.should be_run
58
+ Performer.args.should == ['hello', 'there']
59
+ end
60
+
61
+ it "can perform more jobs that are queued" do
62
+ Resque.enqueue(Performer, 'runs' => 3)
63
+ Performer.runs.should == 3
64
+ end
65
+
66
+ it "roundtrips arguments" do
67
+ Resque.enqueue(Performer, :hello => :there)
68
+ Performer.args.should == [{ 'hello' => 'there' }]
69
+ end
70
+ end
71
+
72
+ describe "asynchronously" do
73
+ it "performs jobs without delay" do
74
+ Resque.async do
75
+ Resque.enqueue(Performer, 'hello', 'there')
76
+ end
77
+
78
+ Performer.should be_run
79
+ Performer.args.should == ['hello', 'there']
80
+ end
81
+
82
+ it "performs jobs with delay" do
83
+ # not immediately sure how to mock this
84
+
85
+ Resque.async do
86
+ Resque.enqueue_in(5, Performer, 'hello', 'there')
87
+ end
88
+
89
+ Performer.should be_run
90
+ Performer.args.should == ['hello', 'there']
91
+ end
92
+
93
+ it "can perform more jobs that are queued" do
94
+ Resque.async { Resque.enqueue(Performer, 'runs' => 3) }
95
+ Performer.runs.should == 3
96
+ end
97
+
98
+ it "roundtrips arguments" do
99
+ Resque.async { Resque.enqueue(Performer, :hello => :there) }
100
+ Performer.args.should == [{ 'hello' => 'there' }]
101
+ end
102
+
103
+ it "raises errors encountered inside the block" do
104
+ expect { Resque.async { raise 'hello' } }.to raise_error
105
+ end
106
+
107
+ it "raises errors encountered by jobs" do
108
+ expect { Resque.async { Resque.enqueue(BadPerformer, 5) } }.to raise_error
109
+ end
110
+ end
111
+ end
metadata ADDED
@@ -0,0 +1,85 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: resque-mock
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.1.0
5
+ prerelease:
6
+ platform: ruby
7
+ authors:
8
+ - Dan Peterson
9
+ autorequire:
10
+ bindir: bin
11
+ cert_chain: []
12
+ date: 2011-06-16 00:00:00.000000000 -07:00
13
+ default_executable:
14
+ dependencies:
15
+ - !ruby/object:Gem::Dependency
16
+ name: resque
17
+ requirement: &2169208800 !ruby/object:Gem::Requirement
18
+ none: false
19
+ requirements:
20
+ - - ! '>='
21
+ - !ruby/object:Gem::Version
22
+ version: '0'
23
+ type: :runtime
24
+ prerelease: false
25
+ version_requirements: *2169208800
26
+ - !ruby/object:Gem::Dependency
27
+ name: rake
28
+ requirement: &2169208360 !ruby/object:Gem::Requirement
29
+ none: false
30
+ requirements:
31
+ - - ! '>='
32
+ - !ruby/object:Gem::Version
33
+ version: '0'
34
+ type: :development
35
+ prerelease: false
36
+ version_requirements: *2169208360
37
+ - !ruby/object:Gem::Dependency
38
+ name: rspec
39
+ requirement: &2169207940 !ruby/object:Gem::Requirement
40
+ none: false
41
+ requirements:
42
+ - - ! '>='
43
+ - !ruby/object:Gem::Version
44
+ version: '0'
45
+ type: :development
46
+ prerelease: false
47
+ version_requirements: *2169207940
48
+ description: Mock resque with threads
49
+ email:
50
+ - dpiddy@gmail.com
51
+ executables: []
52
+ extensions: []
53
+ extra_rdoc_files: []
54
+ files:
55
+ - lib/resque/mock.rb
56
+ - Rakefile
57
+ - spec/mock_spec.rb
58
+ has_rdoc: true
59
+ homepage: https://github.com/dpiddy/resque-mock
60
+ licenses: []
61
+ post_install_message:
62
+ rdoc_options: []
63
+ require_paths:
64
+ - lib
65
+ required_ruby_version: !ruby/object:Gem::Requirement
66
+ none: false
67
+ requirements:
68
+ - - ! '>='
69
+ - !ruby/object:Gem::Version
70
+ version: '0'
71
+ required_rubygems_version: !ruby/object:Gem::Requirement
72
+ none: false
73
+ requirements:
74
+ - - ! '>='
75
+ - !ruby/object:Gem::Version
76
+ version: '0'
77
+ requirements: []
78
+ rubyforge_project:
79
+ rubygems_version: 1.5.2
80
+ signing_key:
81
+ specification_version: 3
82
+ summary: Mock resque with threads
83
+ test_files:
84
+ - Rakefile
85
+ - spec/mock_spec.rb