rails-queue 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.
@@ -0,0 +1,5 @@
1
+ module Rails
2
+ module Queue
3
+ VERSION = "1.0.0"
4
+ end
5
+ end
@@ -0,0 +1,4 @@
1
+ # clone Rails 3.2 one directory up from our library
2
+ git clone git://github.com/rails/rails.git ../rails
3
+ cd ../rails
4
+ git checkout v$RAILS_VERSION
@@ -0,0 +1 @@
1
+ Welcome
@@ -0,0 +1,7 @@
1
+ require File.expand_path("../../lib/rails-queue", __FILE__)
2
+
3
+ class Rails::QueueTest < ActiveSupport::TestCase
4
+ test "module definition" do
5
+ assert_kind_of Module, Rails::Queue
6
+ end
7
+ end
@@ -0,0 +1,35 @@
1
+ require "support/isolation_abstract_unit"
2
+ require 'set'
3
+
4
+ module ApplicationTests
5
+ class FrameworksTest < ActiveSupport::TestCase
6
+ include ActiveSupport::Testing::Isolation
7
+
8
+ def setup
9
+ build_app
10
+ boot_rails
11
+ FileUtils.rm_rf "#{app_path}/config/environments"
12
+ end
13
+
14
+ def teardown
15
+ teardown_app
16
+ end
17
+
18
+ test "uses the default queue for ActionMailer" do
19
+ require "#{app_path}/config/environment"
20
+ assert_kind_of Rails::Queue::Queue, ActionMailer::Base.queue
21
+ end
22
+
23
+ test "allows me to configure queue for ActionMailer" do
24
+ app_file "config/environments/development.rb", <<-RUBY
25
+ AppTemplate::Application.configure do
26
+ config.action_mailer.queue = Rails::Queue::TestQueue.new
27
+ end
28
+ RUBY
29
+
30
+ require "#{app_path}/config/environment"
31
+ assert_kind_of Rails::Queue::TestQueue, ActionMailer::Base.queue
32
+ end
33
+
34
+ end
35
+ end
@@ -0,0 +1,22 @@
1
+ # Original File: RAILS_ROOT/actionmailer/test/base_test.rb
2
+ require "support/action_mailer_abstract_unit"
3
+ require "rails-queue"
4
+ require "support/mailers"
5
+
6
+ class BaseTest < ActiveSupport::TestCase
7
+ def teardown
8
+ ActionMailer::Base.asset_host = nil
9
+ ActionMailer::Base.assets_dir = nil
10
+ end
11
+
12
+ test "delivering message asynchronously" do
13
+ AsyncMailer.delivery_method = :test
14
+ AsyncMailer.deliveries.clear
15
+
16
+ AsyncMailer.welcome.deliver
17
+ assert_equal 0, AsyncMailer.deliveries.length
18
+
19
+ AsyncMailer.queue.drain
20
+ assert_equal 1, AsyncMailer.deliveries.length
21
+ end
22
+ end
@@ -0,0 +1,28 @@
1
+ # Original File: RAILS_ROOT/activesupport/test/queueing/synchronous_queue_test.rb
2
+ require "support/active_support_abstract_unit"
3
+ require "rails-queue"
4
+
5
+ class SynchronousQueueTest < ActiveSupport::TestCase
6
+ class Job
7
+ attr_reader :ran
8
+ def run; @ran = true end
9
+ end
10
+
11
+ class ExceptionRaisingJob
12
+ def run; raise end
13
+ end
14
+
15
+ def setup
16
+ @queue = Rails::Queue::SynchronousQueue.new
17
+ end
18
+
19
+ def test_runs_jobs_immediately
20
+ job = Job.new
21
+ @queue.push job
22
+ assert job.ran
23
+
24
+ assert_raises RuntimeError do
25
+ @queue.push ExceptionRaisingJob.new
26
+ end
27
+ end
28
+ end
@@ -0,0 +1,147 @@
1
+ # Original File: RAILS_ROOT/activesupport/test/queueing/test_queue_test.rb
2
+ require "support/active_support_abstract_unit"
3
+ require "rails-queue"
4
+
5
+ class TestQueueTest < ActiveSupport::TestCase
6
+ def setup
7
+ @queue = Rails::Queue::TestQueue.new
8
+ end
9
+
10
+ class ExceptionRaisingJob
11
+ def run
12
+ raise
13
+ end
14
+ end
15
+
16
+ def test_drain_raises_exceptions_from_running_jobs
17
+ @queue.push ExceptionRaisingJob.new
18
+ assert_raises(RuntimeError) { @queue.drain }
19
+ end
20
+
21
+ def test_jobs
22
+ @queue.push 1
23
+ @queue.push 2
24
+ assert_equal [1,2], @queue.jobs
25
+ end
26
+
27
+ class EquivalentJob
28
+ def initialize
29
+ @initial_id = self.object_id
30
+ end
31
+
32
+ def run
33
+ end
34
+
35
+ def ==(other)
36
+ other.same_initial_id?(@initial_id)
37
+ end
38
+
39
+ def same_initial_id?(other_id)
40
+ other_id == @initial_id
41
+ end
42
+ end
43
+
44
+ def test_contents
45
+ job = EquivalentJob.new
46
+ assert @queue.empty?
47
+ @queue.push job
48
+ refute @queue.empty?
49
+ assert_equal job, @queue.pop
50
+ end
51
+
52
+ class ProcessingJob
53
+ def self.clear_processed
54
+ @processed = []
55
+ end
56
+
57
+ def self.processed
58
+ @processed
59
+ end
60
+
61
+ def initialize(object)
62
+ @object = object
63
+ end
64
+
65
+ def run
66
+ self.class.processed << @object
67
+ end
68
+ end
69
+
70
+ def test_order
71
+ ProcessingJob.clear_processed
72
+ job1 = ProcessingJob.new(1)
73
+ job2 = ProcessingJob.new(2)
74
+
75
+ @queue.push job1
76
+ @queue.push job2
77
+ @queue.drain
78
+
79
+ assert_equal [1,2], ProcessingJob.processed
80
+ end
81
+
82
+ class ThreadTrackingJob
83
+ attr_reader :thread_id
84
+
85
+ def run
86
+ @thread_id = Thread.current.object_id
87
+ end
88
+
89
+ def ran?
90
+ @thread_id
91
+ end
92
+ end
93
+
94
+ def test_drain
95
+ @queue.push ThreadTrackingJob.new
96
+ job = @queue.jobs.last
97
+ @queue.drain
98
+
99
+ assert @queue.empty?
100
+ assert job.ran?, "The job runs synchronously when the queue is drained"
101
+ assert_equal job.thread_id, Thread.current.object_id
102
+ end
103
+
104
+ class IdentifiableJob
105
+ def initialize(id)
106
+ @id = id
107
+ end
108
+
109
+ def ==(other)
110
+ other.same_id?(@id)
111
+ end
112
+
113
+ def same_id?(other_id)
114
+ other_id == @id
115
+ end
116
+
117
+ def run
118
+ end
119
+ end
120
+
121
+ def test_queue_can_be_observed
122
+ jobs = (1..10).map do |id|
123
+ IdentifiableJob.new(id)
124
+ end
125
+
126
+ jobs.each do |job|
127
+ @queue.push job
128
+ end
129
+
130
+ assert_equal jobs, @queue.jobs
131
+ end
132
+
133
+ def test_adding_an_unmarshallable_job
134
+ anonymous_class_instance = Struct.new(:run).new
135
+
136
+ assert_raises TypeError do
137
+ @queue.push anonymous_class_instance
138
+ end
139
+ end
140
+
141
+ def test_attempting_to_add_a_reference_to_itself
142
+ job = {reference: @queue}
143
+ assert_raises TypeError do
144
+ @queue.push job
145
+ end
146
+ end
147
+ end
@@ -0,0 +1,111 @@
1
+ # Original File: RAILS_ROOT/activesupport/test/queueing/threaded_consumer_test.rb
2
+ require "support/active_support_abstract_unit"
3
+ require "rails-queue"
4
+ require "active_support/log_subscriber/test_helper"
5
+
6
+ class TestThreadConsumer < ActiveSupport::TestCase
7
+ class Job
8
+ attr_reader :id
9
+ def initialize(id = 1, &block)
10
+ @id = id
11
+ @block = block
12
+ end
13
+
14
+ def run
15
+ @block.call if @block
16
+ end
17
+ end
18
+
19
+ def setup
20
+ @logger = ActiveSupport::LogSubscriber::TestHelper::MockLogger.new
21
+ @queue = Rails::Queue::Queue.new(logger: @logger)
22
+ end
23
+
24
+ def teardown
25
+ @queue.drain
26
+ end
27
+
28
+ test "the jobs are executed" do
29
+ ran = false
30
+ job = Job.new { ran = true }
31
+
32
+ @queue.push job
33
+ @queue.drain
34
+
35
+ assert_equal true, ran
36
+ end
37
+
38
+ test "the jobs are not executed synchronously" do
39
+ run, ran = Queue.new, Queue.new
40
+ job = Job.new { ran.push run.pop }
41
+
42
+ @queue.consumer.start
43
+ @queue.push job
44
+ assert ran.empty?
45
+
46
+ run.push true
47
+ assert_equal true, ran.pop
48
+ end
49
+
50
+ test "shutting down the queue synchronously drains the jobs" do
51
+ ran = false
52
+ job = Job.new do
53
+ sleep 0.1
54
+ ran = true
55
+ end
56
+
57
+ @queue.consumer.start
58
+ @queue.push job
59
+ assert_equal false, ran
60
+
61
+ @queue.consumer.shutdown
62
+ assert_equal true, ran
63
+ end
64
+
65
+ test "log job that raises an exception" do
66
+ job = Job.new { raise "RuntimeError: Error!" }
67
+
68
+ @queue.push job
69
+ consume_queue @queue
70
+
71
+ assert_equal 1, @logger.logged(:error).size
72
+ assert_match "Job Error: #{job.inspect}\nRuntimeError: Error!", @logger.logged(:error).last
73
+ end
74
+
75
+ test "logger defaults to stderr" do
76
+ begin
77
+ $stderr, old_stderr = StringIO.new, $stderr
78
+ queue = Rails::Queue::Queue.new
79
+ queue.push Job.new { raise "RuntimeError: Error!" }
80
+ consume_queue queue
81
+ assert_match 'Job Error', $stderr.string
82
+ ensure
83
+ $stderr = old_stderr
84
+ end
85
+ end
86
+
87
+ test "test overriding exception handling" do
88
+ @queue.consumer.instance_eval do
89
+ def handle_exception(job, exception)
90
+ @last_error = exception.message
91
+ end
92
+
93
+ def last_error
94
+ @last_error
95
+ end
96
+ end
97
+
98
+ job = Job.new { raise "RuntimeError: Error!" }
99
+
100
+ @queue.push job
101
+ consume_queue @queue
102
+
103
+ assert_equal "RuntimeError: Error!", @queue.consumer.last_error
104
+ end
105
+
106
+ private
107
+ def consume_queue(queue)
108
+ queue.push nil
109
+ queue.consumer.consume
110
+ end
111
+ end
@@ -0,0 +1,152 @@
1
+ require "support/isolation_abstract_unit"
2
+
3
+ class QueueTest < ActiveSupport::TestCase
4
+ include ActiveSupport::Testing::Isolation
5
+
6
+ def setup
7
+ build_app
8
+ boot_rails
9
+ end
10
+
11
+ def teardown
12
+ teardown_app
13
+ end
14
+
15
+ def app_const
16
+ @app_const ||= Class.new(Rails::Application)
17
+ end
18
+
19
+ test "the queue is a SynchronousQueue in test mode" do
20
+ app("test")
21
+ assert_kind_of Rails::Queue::SynchronousQueue, Rails.application.queue
22
+ assert_kind_of Rails::Queue::SynchronousQueue, Rails.queue
23
+ end
24
+
25
+ test "the queue is a SynchronousQueue in development mode" do
26
+ app("development")
27
+ assert_kind_of Rails::Queue::SynchronousQueue, Rails.application.queue
28
+ assert_kind_of Rails::Queue::SynchronousQueue, Rails.queue
29
+ end
30
+
31
+ class ThreadTrackingJob
32
+ def initialize
33
+ @origin = Thread.current.object_id
34
+ end
35
+
36
+ def run
37
+ @target = Thread.current.object_id
38
+ end
39
+
40
+ def ran_in_different_thread?
41
+ @origin != @target
42
+ end
43
+
44
+ def ran?
45
+ @target
46
+ end
47
+ end
48
+
49
+ test "in development mode, an enqueued job will be processed in the same thread" do
50
+ app("development")
51
+
52
+ job = ThreadTrackingJob.new
53
+ Rails.queue.push job
54
+ sleep 0.1
55
+
56
+ assert job.ran?, "Expected job to be run"
57
+ refute job.ran_in_different_thread?, "Expected job to run in the same thread"
58
+ end
59
+
60
+ test "in test mode, an enqueued job will be processed in the same thread" do
61
+ app("test")
62
+
63
+ job = ThreadTrackingJob.new
64
+ Rails.queue.push job
65
+ sleep 0.1
66
+
67
+ assert job.ran?, "Expected job to be run"
68
+ refute job.ran_in_different_thread?, "Expected job to run in the same thread"
69
+ end
70
+
71
+ test "in production, automatically spawn a queue consumer in a background thread" do
72
+ add_to_env_config "production", <<-RUBY
73
+ config.queue = Rails::Queue::Queue.new
74
+ RUBY
75
+
76
+ app("production")
77
+
78
+ assert_nil Rails.application.config.queue_consumer
79
+ assert_kind_of Rails::Queue::ThreadedQueueConsumer, Rails.application.queue_consumer
80
+ assert_equal Rails.logger, Rails.application.queue_consumer.logger
81
+ end
82
+
83
+ test "attempting to marshal a queue will raise an exception" do
84
+ app("test")
85
+ assert_raises TypeError do
86
+ Marshal.dump Rails.queue
87
+ end
88
+ end
89
+
90
+ def setup_custom_queue
91
+ add_to_env_config "production", <<-RUBY
92
+ require "my_queue"
93
+ config.queue = MyQueue.new
94
+ RUBY
95
+
96
+ app_file "lib/my_queue.rb", <<-RUBY
97
+ class MyQueue
98
+ def push(job)
99
+ job.run
100
+ end
101
+ end
102
+ RUBY
103
+
104
+ app("production")
105
+ end
106
+
107
+ test "a custom queue implementation can be provided" do
108
+ setup_custom_queue
109
+
110
+ assert_kind_of MyQueue, Rails.queue
111
+
112
+ job = Struct.new(:id, :ran) do
113
+ def run
114
+ self.ran = true
115
+ end
116
+ end
117
+
118
+ job1 = job.new(1)
119
+ Rails.queue.push job1
120
+
121
+ assert_equal true, job1.ran
122
+ end
123
+
124
+ test "a custom consumer implementation can be provided" do
125
+ add_to_env_config "production", <<-RUBY
126
+ require "my_queue_consumer"
127
+ config.queue = Rails::Queue::Queue.new
128
+ config.queue_consumer = MyQueueConsumer.new
129
+ RUBY
130
+
131
+ app_file "lib/my_queue_consumer.rb", <<-RUBY
132
+ class MyQueueConsumer
133
+ attr_reader :started
134
+
135
+ def start
136
+ @started = true
137
+ end
138
+ end
139
+ RUBY
140
+
141
+ app("production")
142
+
143
+ assert_kind_of MyQueueConsumer, Rails.application.queue_consumer
144
+ assert Rails.application.queue_consumer.started
145
+ end
146
+
147
+ test "default consumer is not used with custom queue implementation" do
148
+ setup_custom_queue
149
+
150
+ assert_nil Rails.application.queue_consumer
151
+ end
152
+ end