resque-mock 0.1.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.
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