disc 0.0.27 → 0.0.28

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 176f77e62f7e6d5d3fb12160f104e6f9920829f4
4
- data.tar.gz: 3f081fa1f1835c21d92c6b52d20650d09b697e5a
3
+ metadata.gz: 5afabb650aa27d3b020674c300b2c2f198da7562
4
+ data.tar.gz: 309002ddceb18dbbae790371d6761330044591ef
5
5
  SHA512:
6
- metadata.gz: 285379b94730d7a5e1eec6a5d551dfd42403452f91289a1fbce88c49d7f26457b59d9e32be25eb16c8a137888756f6ed851666ff64fc07532e0494e8deee8ef2
7
- data.tar.gz: ad8a5da1ea7ce655c093796186abd7eb292fd22b2f68e0e7f13c9473643e93c28345893283966196578058fa121bbce70a8971df7f57d2e20b0cb298eeff41f8
6
+ metadata.gz: 73c4688d1d3363c68f4927bfc9096efc1ac353371d1a4f0a67bcf22c48e5bd1f7e9d57f2771d2dbb831340f939531757d160797d0915753a41ef1e11ed9ab2cb
7
+ data.tar.gz: c4fa2f21bb5d56b00c658aa72c3e66f307b5c86a77af9f9e9ef917794fd63faa14a2edb16d60a7bd792a78ecdc8cfc29d450a58dea8040ac9c5debd1aade71be
data/README.md CHANGED
@@ -23,7 +23,7 @@ Disc fills the gap between your Ruby service objects and [antirez](http://antire
23
23
  include Disc::Job
24
24
  disc queue: 'urgent'
25
25
 
26
- def perform(type)
26
+ def self.perform(type)
27
27
  # perform rather lengthy operations here.
28
28
  end
29
29
  end
@@ -40,7 +40,8 @@ Disc fills the gap between your Ruby service objects and [antirez](http://antire
40
40
 
41
41
  ```ruby
42
42
  # disc_init.rb
43
- require 'disc/worker'
43
+ # Require here anything that your application needs to run,
44
+ # like ORMs and your models, database configuration, etc.
44
45
  Dir['./jobs/**/*.rb'].each { |job| require job }
45
46
  ```
46
47
 
@@ -79,7 +80,7 @@ Signature documentation follows:
79
80
  ## Parameters:
80
81
  #
81
82
  ## `arguments` - an optional array of arguments with which to execute
82
- # the job's #perform method.
83
+ # the job's `self.perform` method.
83
84
  #
84
85
  # `at` - an optional named parameter specifying a moment in the
85
86
  # future in which to run the job, must respond to
@@ -117,7 +118,7 @@ Signature documentation follows:
117
118
  ### CAVEATS
118
119
  #
119
120
  ## For convenience, any object can be passed as the `arguments` parameter,
120
- # `Array.wrap` will be used internally to preserve the array structure.
121
+ # `Array()` will be used internally to preserve the array structure.
121
122
  #
122
123
  ## The `arguments` parameter is serialized for storage using `Disc.serialize`
123
124
  # and Disc workers picking it up use `Disc.deserialize` on it, both methods
@@ -127,7 +128,7 @@ Signature documentation follows:
127
128
 
128
129
  You can see [Disque's ADDJOB documentation](https://github.com/antirez/disque#addjob-queue_name-job-ms-timeout-replicate-count-delay-sec-retry-sec-ttl-sec-maxlen-count-async) for more details
129
130
 
130
- When a Disc worker process is assigned a job, it will create a new intance of the job's class and execute the `#perform` method with whatever arguments were previously passed to `#enqueue`.
131
+ When a Disc worker process is assigned a job, it will create a new intance of the job's class and execute the `self.perform` method with whatever arguments were previously passed to `#enqueue`.
131
132
 
132
133
  Example:
133
134
 
@@ -136,7 +137,7 @@ class ComplexJob
136
137
  include Disc::Job
137
138
  disc queue: 'urgent'
138
139
 
139
- def perform(first_parameter, second_parameter)
140
+ def self.perform(first_parameter, second_parameter)
140
141
  # do things...
141
142
  end
142
143
  end
@@ -145,6 +146,18 @@ end
145
146
  ComplexJob.enqueue(['first argument', { second: 'argument' }])
146
147
  ```
147
148
 
149
+ ### Job Serialization
150
+
151
+ Job information (their arguments, and class) need to be serialized in order to be stored
152
+ in Disque, to this end Disc uses the `Disc.serialize` and `Disc.deserialize` methods.
153
+
154
+ By default, these methods use by default the Ruby standard library json implementation in order to serialize and deserialize job data, this has a few implications:
155
+
156
+ 1. Arguments passed to a job's `#enqueue` method need to be serializable by `Disc.serialize` and parsed back by `Disc.deserialize`, so by default you can't pass complex Ruby objects like a `user` model, instead, pass `user.id`, and use that from your job code.
157
+
158
+ 2. You can override `Disc.serialize` and `Disc.deserialize` to use a different JSON implementation, or MessagePack, or whatever else you wish.
159
+
160
+
148
161
  ## Settings
149
162
 
150
163
  Disc takes its configuration from environment variables.
@@ -173,7 +186,7 @@ end
173
186
  Dir["./jobs/**/*.rb"].each { |job| require job }
174
187
  ```
175
188
 
176
- ## Job Definition
189
+ ### Job Definition
177
190
 
178
191
  The error handler function gets the data of the current job as a Hash, that has the following schema.
179
192
 
@@ -182,7 +195,60 @@ The error handler function gets the data of the current job as a Hash, that has
182
195
  | `'class'` | (String) The Job class. |
183
196
  | `'arguments'` | (Array) The arguments passed to perform. |
184
197
  | `'queue'` | (String) The queue from which this job was picked up. |
185
- | `'id'` | (String) Disque's job ID. |
198
+ | `'disque_id'` | (String) Disque's job ID. |
199
+
200
+
201
+ ## Testing modes
202
+
203
+ Disc includes a testing mode, so you can run your test suite without a need to run a Disque server.
204
+
205
+ ### Enqueue mode
206
+
207
+ By default, Disc places your jobs in an in-memory hash, with each queue being a key in the hash and values being an array.
208
+
209
+ ```ruby
210
+ require 'disc'
211
+ require 'disc/testing'
212
+
213
+ require_relative 'examples/returner'
214
+ Disc.enqueue! #=> This is the default mode for disc/testing so you don't need to specify it,
215
+ # you can use this method to go back to the enqueue mode if you switch it.
216
+
217
+
218
+ Returner.enqueue('test argument')
219
+
220
+ Disc.queues
221
+ #=> {"default"=>[{:arguments=>["test argument"], :class=>"Returner", :options=>{}}]}
222
+
223
+ Returner.enqueue('another test')
224
+ #=> => {"default"=>[{:arguments=>["test argument"], :class=>"Returner", :options=>{}}, {:arguments=>["another test"], :class=>"Returner", :options=>{}}]}
225
+
226
+
227
+ ```
228
+
229
+ You can still flush the queues just as you would running on regular mode.
230
+
231
+ ```ruby
232
+ Disc.flush
233
+
234
+ Disc.queues
235
+ #=> {}
236
+ ```
237
+
238
+ ### Inline mode
239
+
240
+ You also have the option for Disc to execute jobs immediately when `#enqueue` is called.
241
+
242
+ ```ruby
243
+ require 'disc'
244
+ require 'disc/testing'
245
+
246
+ require_relative 'examples/returner'
247
+ Disc.inline!
248
+
249
+ Returner.enqueue('test argument')
250
+ #=> 'test argument'
251
+ ```
186
252
 
187
253
  ## [Optional] Celluloid integration
188
254
 
@@ -220,7 +286,7 @@ end
220
286
  class CluJob < ActiveJob::Base
221
287
  queue_as :urgent
222
288
 
223
- def perform(*args)
289
+ def self.perform(*args)
224
290
  # Try to take over The Grid here...
225
291
  end
226
292
  end
@@ -238,10 +304,6 @@ Disc is run in the exact same way, for this example it'd be:
238
304
  $ QUEUES=urgent disc -r ./disc_init.rb
239
305
  ```
240
306
 
241
- ## A note on stability
242
-
243
- The version of Disque at the time of this writing is `0.0.1`. It is a wonderful project, I know of people running it in production and I expect it will only get better and better with time, but please do not expect Disc to be more production ready than Disque is.
244
-
245
307
  ## Similar Projects
246
308
 
247
309
  If you want to use Disque but Disc isn't cutting it for you then you should take a look at [Havanna](https://github.com/djanowski/havanna), a project by my friend [@djanowski](https://twitter.com/djanowski).
data/bin/disc CHANGED
@@ -19,6 +19,7 @@ trap(:TERM, &stop)
19
19
 
20
20
  require 'clap'
21
21
  require_relative '../lib/disc'
22
+ require_relative '../lib/disc/worker'
22
23
 
23
24
  Clap.run ARGV,
24
25
  "-r" => lambda { |file| require file }
data/examples/echoer.rb CHANGED
@@ -4,7 +4,7 @@ class Echoer
4
4
  include Disc::Job
5
5
  disc queue: 'test'
6
6
 
7
- def perform(first, second, third)
7
+ def self.perform(first, second, third)
8
8
  puts "First: #{ first }, Second: #{ second }, Third: #{ third }"
9
9
  end
10
10
  end
data/examples/failer.rb CHANGED
@@ -10,7 +10,7 @@ class Failer
10
10
  include Disc::Job
11
11
  disc queue: 'test'
12
12
 
13
- def perform(string)
13
+ def self.perform(string)
14
14
  raise string
15
15
  end
16
16
  end
data/examples/greeter.rb CHANGED
@@ -4,7 +4,7 @@ class Greeter
4
4
  include Disc::Job
5
5
  disc queue: 'test_medium'
6
6
 
7
- def perform(string)
7
+ def self.perform(string)
8
8
  $stdout.puts(string)
9
9
  end
10
10
  end
@@ -0,0 +1,7 @@
1
+ class Returner
2
+ include Disc::Job
3
+
4
+ def self.perform(argument)
5
+ return argument
6
+ end
7
+ end
@@ -0,0 +1,6 @@
1
+ class Disc
2
+ class Error < StandardError; end
3
+
4
+ class UnknownJobClassError < Error; end
5
+ class NonParsableJobError < Error; end
6
+ end
data/lib/disc/job.rb CHANGED
@@ -1,35 +1,10 @@
1
1
  module Disc::Job
2
- attr_accessor :disque_id,
3
- :arguments
4
2
 
5
3
  def self.included(base)
6
4
  base.extend(ClassMethods)
7
5
  end
8
6
 
9
- def info
10
- return nil if disque_id.nil?
11
-
12
- Hash[*self.class.disque.call("SHOW", disque_id)]
13
- end
14
-
15
- def state
16
- info.fetch('state')
17
- end
18
-
19
7
  module ClassMethods
20
- def [](disque_id)
21
- job_data = disque.call("SHOW", disque_id)
22
- return nil if job_data.nil?
23
-
24
- job = self.new
25
- job_data = Hash[*job_data]
26
-
27
- job.disque_id = disque_id
28
- job.arguments = Disc.deserialize(job_data.fetch('body')).fetch('arguments')
29
-
30
- return job
31
- end
32
-
33
8
  def disque
34
9
  defined?(@disque) ? @disque : Disc.disque
35
10
  end
@@ -51,10 +26,6 @@ module Disc::Job
51
26
  @queue || Disc.default_queue
52
27
  end
53
28
 
54
- def perform(arguments)
55
- self.new.perform(*arguments)
56
- end
57
-
58
29
  ## Disc's `#enqueue` is the main user-facing method of a Disc job, it
59
30
  # enqueues a job with a given set of arguments in Disque, so it can be
60
31
  # picked up by a Disc worker process.
@@ -100,7 +71,7 @@ module Disc::Job
100
71
  ### CAVEATS
101
72
  #
102
73
  ## For convenience, any object can be passed as the `arguments` parameter,
103
- # `Array.wrap` will be used internally to preserve the array structure.
74
+ # `Array()` will be used internally to preserve the array structure.
104
75
  #
105
76
  ## The `arguments` parameter is serialized for storage using `Disc.serialize`
106
77
  # and Disc workers picking it up use `Disc.deserialize` on it, both methods
@@ -111,7 +82,7 @@ module Disc::Job
111
82
  opt[:delay] = at.to_time.to_i - DateTime.now.to_time.to_i unless at.nil?
112
83
  end
113
84
 
114
- disque_id = disque.push(
85
+ disque.push(
115
86
  queue || self.queue,
116
87
  Disc.serialize({
117
88
  class: self.name,
@@ -120,8 +91,6 @@ module Disc::Job
120
91
  Disc.disque_timeout,
121
92
  options
122
93
  )
123
-
124
- return self[disque_id]
125
94
  end
126
95
  end
127
96
  end
data/lib/disc/testing.rb CHANGED
@@ -1,9 +1,56 @@
1
- module Disc::Job::ClassMethods
2
- def mocked_queue
3
- @_mocked_queue ||= []
1
+ class Disc
2
+ def self.queues
3
+ @queues ||= {}
4
+ end
5
+
6
+ def self.testing_mode
7
+ @testing_mode ||= 'enqueue'
8
+ end
9
+
10
+ def self.enqueue!
11
+ @testing_mode = 'enqueue'
12
+ end
13
+
14
+ def self.inline!
15
+ @testing_mode = 'inline'
16
+ end
17
+
18
+ def self.flush
19
+ @queues = {}
4
20
  end
5
21
 
22
+ def self.qlen(queue)
23
+ return 0 if Disc.queues[queue].nil?
24
+
25
+ Disc.queues[queue].length
26
+ end
27
+
28
+ def self.enqueue(klass, arguments, at: nil, queue: nil, **options)
29
+ job_attrs = { arguments: arguments, class: klass, options: options }
30
+ if queues[queue].nil?
31
+ queues[queue] = [job_attrs]
32
+ else
33
+ queues[queue] << job_attrs
34
+ end
35
+
36
+ job_attrs
37
+ end
38
+ end
39
+
40
+ module Disc::Job::ClassMethods
6
41
  def enqueue(args = [], at: nil, queue: nil, **options)
7
- mocked_queue << { args: args, at: at, options: options }
42
+ case Disc.testing_mode
43
+ when 'enqueue'
44
+ Disc.enqueue(
45
+ self.name,
46
+ Array(args),
47
+ queue: queue || self.queue,
48
+ at: at,
49
+ **options)
50
+ when 'inline'
51
+ self.perform(*args)
52
+ else
53
+ raise "Unknown Disc testing mode, this shouldn't happen"
54
+ end
8
55
  end
9
56
  end
data/lib/disc/version.rb CHANGED
@@ -1,3 +1,3 @@
1
1
  class Disc
2
- VERSION = "0.0.27"
2
+ VERSION = "0.0.28"
3
3
  end
data/lib/disc/worker.rb CHANGED
@@ -52,13 +52,17 @@ class Disc::Worker
52
52
  jobs = disque.fetch(from: queues, timeout: timeout, count: count)
53
53
  Array(jobs).each do |queue, msgid, serialized_job|
54
54
  begin
55
- job = Disc.deserialize(serialized_job)
56
- instance = Object.const_get(job['class']).new
57
- instance.perform(*job['arguments'])
55
+ job_class, arguments = Disc.load_job(serialized_job)
56
+ job_class.perform(*arguments)
58
57
  disque.call('ACKJOB', msgid)
59
- $stdout.puts("Completed #{job['class']} id #{msgid}")
58
+ $stdout.puts("Completed #{ job_class.name } id #{ msgid }")
60
59
  rescue => err
61
- Disc.on_error(err, job.update('id' => msgid, 'queue' => queue))
60
+ Disc.on_error(err, {
61
+ disque_id: msgid,
62
+ queue: queue,
63
+ class: defined?(job_class) ? job_class.name : '',
64
+ arguments: defined?(arguments) ? arguments : []
65
+ })
62
66
  end
63
67
  end
64
68
 
data/lib/disc.rb CHANGED
@@ -16,7 +16,7 @@ class Disc
16
16
  end
17
17
 
18
18
  def self.disque_timeout
19
- @disque_timeout ||= ENV.fetch('DISQUE_TIMEOUT', 100)
19
+ @disque_timeout ||= 100
20
20
  end
21
21
 
22
22
  def self.disque_timeout=(timeout)
@@ -50,8 +50,46 @@ class Disc
50
50
  def self.deserialize(data)
51
51
  JSON.parse(data)
52
52
  end
53
+
54
+ def self.[](disque_id)
55
+ job_data = disque.call("SHOW", disque_id)
56
+ return nil if job_data.nil?
57
+
58
+ job_data = Hash[*job_data]
59
+ job_data['arguments'] = Disc.deserialize(job_data['body'])['arguments']
60
+ job_data['class'] = Disc.deserialize(job_data['body'])['class']
61
+
62
+ job_data
63
+ end
64
+
65
+ ## Receives:
66
+ #
67
+ # A string containing data serialized by `Disc.serialize`
68
+ #
69
+ ## Returns:
70
+ #
71
+ # An array containing:
72
+ #
73
+ # * An instance of the given job class
74
+ # * An array of arguments to pass to the job's `#perorm` class.
75
+ #
76
+ def self.load_job(serialized_job)
77
+ begin
78
+ job_data = Disc.deserialize(serialized_job)
79
+ rescue => err
80
+ raise Disc::NonParsableJobError.new(err)
81
+ end
82
+
83
+ begin
84
+ job_class = Object.const_get(job_data['class'])
85
+ rescue => err
86
+ raise Disc::UnknownJobClassError.new(err)
87
+ end
88
+
89
+ return [job_class, job_data['arguments']]
90
+ end
53
91
  end
54
92
 
93
+ require_relative 'disc/errors'
55
94
  require_relative 'disc/job'
56
95
  require_relative 'disc/version'
57
-
data/test/disc_test.rb CHANGED
@@ -1,33 +1,7 @@
1
1
  require 'cutest'
2
2
  require 'disc'
3
- require 'pty'
4
- require 'timeout'
5
3
 
6
4
  require_relative '../examples/echoer'
7
- # class Echoer
8
- # include Disc::Job
9
- # disc queue: 'test'
10
- #
11
- # def perform(first, second, third)
12
- # puts "First: #{ first }, Second: #{ second }, Third: #{ third }"
13
- # end
14
- # end
15
-
16
- require_relative '../examples/failer'
17
- # def Disc.on_error(exception, job)
18
- # $stdout.puts('<insert error reporting here>')
19
- # $stdout.puts(exception.message)
20
- # $stdout.puts(job)
21
- # end
22
- #
23
- # class Failer
24
- # include Disc::Job
25
- # disc queue: 'test'
26
- #
27
- # def perform(string)
28
- # raise string
29
- # end
30
- # end
31
5
 
32
6
  prepare do
33
7
  Disc.disque_timeout = 1 # 1ms so we don't wait at all.
@@ -35,180 +9,67 @@ prepare do
35
9
  end
36
10
 
37
11
  scope do
38
- # Runs a given command, yielding the stdout (as an IO) and the PID (a String).
39
- # Makes sure the process finishes after the block runs.
40
- def run(command)
41
- out, _, pid = PTY.spawn(command)
42
- yield out, pid
43
- ensure
44
- Process.kill("KILL", pid)
45
- sleep 0.1 # Make sure we give it time to finish.
46
- end
47
-
48
- # Checks whether a process is running.
49
- def is_running?(pid)
50
- Process.getpgid(pid)
51
- true
52
- rescue Errno::ESRCH
53
- false
54
- end
55
-
56
- test 'jobs are enqueued to the correct Disque queue with appropriate parameters and class' do
57
- job_instance = Echoer.enqueue(['one argument', { random: 'data' }, 3])
58
-
59
- jobs = Array(Disc.disque.fetch(from: ['test'], timeout: Disc.disque_timeout, count: 1))
60
- assert jobs.any?
61
- assert_equal 1, jobs.count
12
+ test 'Disc should be able to communicate with Disque' do
13
+ assert !Disc.disque.nil?
62
14
 
63
- jobs.first.tap do |queue, id, serialized_job|
64
- job = Disc.deserialize(serialized_job)
65
-
66
- assert job.has_key?('class')
67
- assert job.has_key?('arguments')
68
-
69
- assert_equal 'Echoer', job['class']
70
- assert_equal job_instance.disque_id, id
71
-
72
- args = job['arguments']
73
- assert_equal 3, args.size
74
- assert_equal 'one argument', args[0]
75
- assert_equal({ 'random' => 'data' }, args[1])
76
- assert_equal(3, args[2])
77
- end
15
+ assert_equal 'PONG', Disc.disque.call('PING')
78
16
  end
79
17
 
80
- test 'we get easy access to the job via the job id' do
81
- job_instance = Echoer.enqueue(['one argument', { random: 'data' }, 3])
82
-
83
- assert job_instance.is_a?(Echoer)
84
- assert !job_instance.disque_id.nil?
85
- assert !job_instance.info.nil?
18
+ test 'we get easy access to the job via the job id with Disc[job_id]' do
19
+ job_id = Echoer.enqueue(['one argument', { random: 'data' }, 3])
86
20
 
87
- job = Echoer[job_instance.disque_id]
21
+ job_data = Disc[job_id]
88
22
 
89
- assert job.is_a?(Echoer)
90
- assert_equal 'queued', job.state
91
- assert_equal 3, job.arguments.count
92
- assert_equal 'one argument', job.arguments.first
23
+ assert_equal 'Echoer', job_data['class']
24
+ assert_equal 'queued', job_data['state']
25
+ assert_equal 3, job_data['arguments'].count
26
+ assert_equal 'one argument', job_data['arguments'].first
93
27
  end
94
28
 
95
- test 'we can query the lenght of a given queue' do
96
- job_instance = Echoer.enqueue(['one argument', { random: 'data' }, 3])
29
+ test 'we can query the length of a given queue with Disc.qlen' do
30
+ Echoer.enqueue(['one argument', { random: 'data' }, 3])
97
31
 
98
32
  assert_equal 1, Disc.qlen(Echoer.queue)
99
33
  end
100
34
 
101
- test 'enqueue at timestamp behaves properly' do
102
- job_instance = Echoer.enqueue(['one argument', { random: 'data' }, 3], at: Time.now + 1)
103
-
104
- jobs = Array(Disc.disque.fetch(from: ['test'], timeout: Disc.disque_timeout, count: 1))
105
- assert jobs.empty?
106
-
107
- sleep 0.5
108
- jobs = Array(Disc.disque.fetch(from: ['test'], timeout: Disc.disque_timeout, count: 1))
109
- assert jobs.empty?
110
-
111
- sleep 0.5
112
- jobs = Array(Disc.disque.fetch(from: ['test'], timeout: Disc.disque_timeout, count: 1))
113
- assert jobs.any?
114
- assert_equal 1, jobs.size
115
-
116
- jobs.first.tap do |queue, id, serialized_job|
117
- assert_equal 'test', queue
118
- assert_equal job_instance.disque_id, id
119
- job = Disc.deserialize(serialized_job)
120
- assert job.has_key?('class')
121
- assert job.has_key?('arguments')
122
- assert_equal 'Echoer', job['class']
123
- assert_equal 3, job['arguments'].size
124
- end
125
- end
126
-
127
- test 'jobs are executed' do
35
+ test 'Disc.flush deletes everything in the queue' do
128
36
  Echoer.enqueue(['one argument', { random: 'data' }, 3])
37
+ Disc.flush
129
38
 
130
- run('QUEUES=test ruby -Ilib bin/disc -r disc/worker -r ./examples/echoer') do |cout, pid|
131
- output = Timeout.timeout(1) { cout.take(3) }
132
- jobs = Disc.disque.fetch(from: ['test'], timeout: Disc.disque_timeout, count: 1)
133
- assert jobs.nil?
134
- assert output.grep(/First: one argument, Second: {"random"=>"data"}, Third: 3/).any?
135
- end
39
+ assert_equal 0, Disc.qlen(Echoer.queue)
136
40
  end
137
41
 
138
- test 'Disc.on_error will catch unhandled exceptions and keep disc alive' do
139
- failer = Failer.enqueue('this can only end positively')
42
+ test 'Disc.load_job returns a job instance and arguments' do
43
+ serialized_job = Disc.serialize(
44
+ { class: 'Echoer', arguments: ['one argument', { random: 'data' }, 3] }
45
+ )
140
46
 
141
- run('QUEUES=test ruby -Ilib bin/disc -r disc/worker -r ./examples/failer') do |cout, pid|
142
- output = Timeout.timeout(1) { cout.take(5) }
143
- jobs = Disc.disque.fetch(from: ['test'], timeout: Disc.disque_timeout, count: 1)
144
- assert jobs.nil?
47
+ job_class, arguments = Disc.load_job(serialized_job)
145
48
 
146
- assert output.grep(/<insert error reporting here>/).any?
147
- assert output.grep(/this can only end positively/).any?
148
- assert output.grep(/Failer/).any?
149
-
150
- assert is_running?(pid)
151
- end
49
+ assert_equal Echoer, job_class
50
+ assert arguments.is_a?(Array)
51
+ assert_equal 3, arguments.count
52
+ assert_equal 'one argument', arguments.first
152
53
  end
153
54
 
154
- test 'enqueue supports replicate' do
155
- error = Echoer.enqueue(['one argument', { random: 'data' }, 3], replicate: 100) rescue $!
156
-
157
- assert_equal RuntimeError, error.class
158
- assert_equal "NOREPL Not enough reachable nodes for the requested replication level", error.message
159
- end
160
-
161
- test 'enqueue supports delay' do
162
- job_instance = Echoer.enqueue(['one argument', { random: 'data' }, 3], delay: 2)
163
-
164
- jobs = Array(Disc.disque.fetch(from: ['test'], timeout: Disc.disque_timeout, count: 1))
165
- assert jobs.empty?
166
-
167
- sleep 1
168
- jobs = Array(Disc.disque.fetch(from: ['test'], timeout: Disc.disque_timeout, count: 1))
169
- assert jobs.empty?
170
-
171
- sleep 2
172
- jobs = Array(Disc.disque.fetch(from: ['test'], timeout: Disc.disque_timeout, count: 1))
173
- assert jobs.any?
174
- assert_equal 1, jobs.size
175
- end
176
-
177
- test 'enqueue supports retry' do
178
- job_instance = Echoer.enqueue(['one argument', { random: 'data' }, 3], retry: 1)
179
-
180
- jobs = Array(Disc.disque.fetch(from: ['test'], timeout: Disc.disque_timeout, count: 1))
181
- assert jobs.any?
182
- assert_equal 1, jobs.size
183
-
184
- sleep 1.5
185
- jobs = Array(Disc.disque.fetch(from: ['test'], timeout: Disc.disque_timeout, count: 1))
186
- assert jobs.any?
187
- assert_equal 1, jobs.size
188
- end
189
-
190
- test 'enqueue supports ttl' do
191
- job_instance = Echoer.enqueue(['one argument', { random: 'data' }, 3], ttl: 1)
192
-
193
- sleep 1.5
194
- jobs = Array(Disc.disque.fetch(from: ['test'], timeout: Disc.disque_timeout, count: 1))
195
- assert jobs.empty?
196
- end
197
-
198
- test 'enqueue supports maxlen' do
199
- Echoer.enqueue(['one argument', { random: 'data' }, 3], maxlen: 1)
200
- error = Echoer.enqueue(['one argument', { random: 'data' }, 3], maxlen: 1) rescue $!
201
-
202
- assert_equal RuntimeError, error.class
203
- assert_equal "MAXLEN Queue is already longer than the specified MAXLEN count", error.message
204
- end
205
-
206
- test 'enqueue supports async' do
207
- job_instance = Echoer.enqueue(['one argument', { random: 'data' }, 3], async: true)
55
+ test 'Disc.load_job raises appropriate errors ' do
56
+ begin
57
+ job_instance, arguments = Disc.load_job('gibberish')
58
+ assert_equal 'Should not reach this point', false
59
+ rescue => err
60
+ assert err.is_a?(Disc::Error)
61
+ assert err.is_a?(Disc::NonParsableJobError)
62
+ end
208
63
 
209
- sleep 1 # async is too fast to reliably assert an empty queue, let's wait instead
210
- jobs = Array(Disc.disque.fetch(from: ['test'], timeout: Disc.disque_timeout, count: 1))
211
- assert jobs.any?
212
- assert_equal 1, jobs.size
64
+ serialized_job = Disc.serialize(
65
+ { class: 'NonExistantDiscJobClass', arguments: [] }
66
+ )
67
+ begin
68
+ job_instance, arguments = Disc.load_job(serialized_job)
69
+ assert_equal 'Should not reach this point', false
70
+ rescue => err
71
+ assert err.is_a?(Disc::Error)
72
+ assert err.is_a?(Disc::UnknownJobClassError)
73
+ end
213
74
  end
214
75
  end
data/test/job_test.rb ADDED
@@ -0,0 +1,122 @@
1
+ require 'cutest'
2
+ require 'disc'
3
+
4
+ require_relative '../examples/echoer'
5
+
6
+ prepare do
7
+ Disc.disque_timeout = 1 # 1ms so we don't wait at all.
8
+ Disc.flush
9
+ end
10
+
11
+ scope do
12
+ test 'jobs are enqueued to the correct Disque queue with appropriate parameters and class' do
13
+ job_id = Echoer.enqueue(['one argument', { random: 'data' }, 3])
14
+
15
+ jobs = Array(Disc.disque.fetch(from: ['test'], timeout: Disc.disque_timeout, count: 1))
16
+ assert jobs.any?
17
+ assert_equal 1, jobs.count
18
+
19
+ jobs.first.tap do |queue, id, serialized_job|
20
+ job = Disc.deserialize(serialized_job)
21
+
22
+ assert job.has_key?('class')
23
+ assert job.has_key?('arguments')
24
+
25
+ assert_equal 'Echoer', job['class']
26
+ assert_equal job_id, id
27
+
28
+ args = job['arguments']
29
+ assert_equal 3, args.size
30
+ assert_equal 'one argument', args[0]
31
+ assert_equal({ 'random' => 'data' }, args[1])
32
+ assert_equal(3, args[2])
33
+ end
34
+ end
35
+
36
+ test 'enqueue at timestamp behaves properly' do
37
+ job_id = Echoer.enqueue(['one argument', { random: 'data' }, 3], at: Time.now + 1)
38
+
39
+ jobs = Array(Disc.disque.fetch(from: ['test'], timeout: Disc.disque_timeout, count: 1))
40
+ assert jobs.empty?
41
+
42
+ sleep 0.5
43
+ jobs = Array(Disc.disque.fetch(from: ['test'], timeout: Disc.disque_timeout, count: 1))
44
+ assert jobs.empty?
45
+
46
+ sleep 0.5
47
+ jobs = Array(Disc.disque.fetch(from: ['test'], timeout: Disc.disque_timeout, count: 1))
48
+ assert jobs.any?
49
+ assert_equal 1, jobs.size
50
+
51
+ jobs.first.tap do |queue, id, serialized_job|
52
+ assert_equal 'test', queue
53
+ assert_equal job_id, id
54
+ job = Disc.deserialize(serialized_job)
55
+ assert job.has_key?('class')
56
+ assert job.has_key?('arguments')
57
+ assert_equal 'Echoer', job['class']
58
+ assert_equal 3, job['arguments'].size
59
+ end
60
+ end
61
+
62
+ test 'enqueue supports replicate' do
63
+ error = Echoer.enqueue(['one argument', { random: 'data' }, 3], replicate: 100) rescue $!
64
+
65
+ assert_equal RuntimeError, error.class
66
+ assert_equal "NOREPL Not enough reachable nodes for the requested replication level", error.message
67
+ end
68
+
69
+ test 'enqueue supports delay' do
70
+ job_instance = Echoer.enqueue(['one argument', { random: 'data' }, 3], delay: 2)
71
+
72
+ jobs = Array(Disc.disque.fetch(from: ['test'], timeout: Disc.disque_timeout, count: 1))
73
+ assert jobs.empty?
74
+
75
+ sleep 1
76
+ jobs = Array(Disc.disque.fetch(from: ['test'], timeout: Disc.disque_timeout, count: 1))
77
+ assert jobs.empty?
78
+
79
+ sleep 2
80
+ jobs = Array(Disc.disque.fetch(from: ['test'], timeout: Disc.disque_timeout, count: 1))
81
+ assert jobs.any?
82
+ assert_equal 1, jobs.size
83
+ end
84
+
85
+ test 'enqueue supports retry' do
86
+ job_instance = Echoer.enqueue(['one argument', { random: 'data' }, 3], retry: 1)
87
+
88
+ jobs = Array(Disc.disque.fetch(from: ['test'], timeout: Disc.disque_timeout, count: 1))
89
+ assert jobs.any?
90
+ assert_equal 1, jobs.size
91
+
92
+ sleep 1.5
93
+ jobs = Array(Disc.disque.fetch(from: ['test'], timeout: Disc.disque_timeout, count: 1))
94
+ assert jobs.any?
95
+ assert_equal 1, jobs.size
96
+ end
97
+
98
+ test 'enqueue supports ttl' do
99
+ job_instance = Echoer.enqueue(['one argument', { random: 'data' }, 3], ttl: 1)
100
+
101
+ sleep 1.5
102
+ jobs = Array(Disc.disque.fetch(from: ['test'], timeout: Disc.disque_timeout, count: 1))
103
+ assert jobs.empty?
104
+ end
105
+
106
+ test 'enqueue supports maxlen' do
107
+ Echoer.enqueue(['one argument', { random: 'data' }, 3], maxlen: 1)
108
+ error = Echoer.enqueue(['one argument', { random: 'data' }, 3], maxlen: 1) rescue $!
109
+
110
+ assert_equal RuntimeError, error.class
111
+ assert_equal "MAXLEN Queue is already longer than the specified MAXLEN count", error.message
112
+ end
113
+
114
+ test 'enqueue supports async' do
115
+ job_instance = Echoer.enqueue(['one argument', { random: 'data' }, 3], async: true)
116
+
117
+ sleep 1 # async is too fast to reliably assert an empty queue, let's wait instead
118
+ jobs = Array(Disc.disque.fetch(from: ['test'], timeout: Disc.disque_timeout, count: 1))
119
+ assert jobs.any?
120
+ assert_equal 1, jobs.size
121
+ end
122
+ end
@@ -0,0 +1,59 @@
1
+ require 'cutest'
2
+ require 'disc'
3
+ require 'pty'
4
+ require 'timeout'
5
+
6
+ require_relative '../examples/echoer'
7
+ require_relative '../examples/failer'
8
+
9
+ prepare do
10
+ Disc.disque_timeout = 1 # 1ms so we don't wait at all.
11
+ Disc.flush
12
+ end
13
+
14
+ scope do
15
+ # Runs a given command, yielding the stdout (as an IO) and the PID (a String).
16
+ # Makes sure the process finishes after the block runs.
17
+ def run(command)
18
+ out, _, pid = PTY.spawn(command)
19
+ yield out, pid
20
+ ensure
21
+ Process.kill("KILL", pid)
22
+ sleep 0.1 # Make sure we give it time to finish.
23
+ end
24
+
25
+ # Checks whether a process is running.
26
+ def is_running?(pid)
27
+ Process.getpgid(pid)
28
+ true
29
+ rescue Errno::ESRCH
30
+ false
31
+ end
32
+
33
+ test 'Disc.on_error will catch unhandled exceptions and keep disc alive' do
34
+ failer = Failer.enqueue('this can only end positively')
35
+
36
+ run('QUEUES=test ruby -Ilib bin/disc -r ./examples/failer') do |cout, pid|
37
+ output = Timeout.timeout(1) { cout.take(5) }
38
+ jobs = Disc.disque.fetch(from: ['test'], timeout: Disc.disque_timeout, count: 1)
39
+ assert jobs.nil?
40
+
41
+ assert output.grep(/<insert error reporting here>/).any?
42
+ assert output.grep(/this can only end positively/).any?
43
+ assert output.grep(/Failer/).any?
44
+
45
+ assert is_running?(pid)
46
+ end
47
+ end
48
+
49
+ test 'jobs are executed' do
50
+ Echoer.enqueue(['one argument', { random: 'data' }, 3])
51
+
52
+ run('QUEUES=test ruby -Ilib bin/disc -r ./examples/echoer') do |cout, pid|
53
+ output = Timeout.timeout(1) { cout.take(3) }
54
+ jobs = Disc.disque.fetch(from: ['test'], timeout: Disc.disque_timeout, count: 1)
55
+ assert jobs.nil?
56
+ assert output.grep(/First: one argument, Second: {"random"=>"data"}, Third: 3/).any?
57
+ end
58
+ end
59
+ end
@@ -0,0 +1,45 @@
1
+ # Yo dawg I put some testing in your testing so you can test while you test.
2
+
3
+ require 'disc'
4
+ require 'disc/testing'
5
+
6
+ require_relative '../examples/echoer'
7
+ require_relative '../examples/returner'
8
+
9
+ prepare do
10
+ Disc.disque_timeout = 1 # 1ms so we don't wait at all.
11
+ Disc.enqueue!
12
+ Disc.flush
13
+ end
14
+
15
+ scope do
16
+ test "testing mode should not enqueue jobs into Disque" do
17
+ Echoer.enqueue(['one argument', { random: 'data' }, 3])
18
+ assert_equal 0, Disc.disque.call('QLEN', 'test')
19
+ assert_equal 1, Disc.qlen('test')
20
+
21
+ # Flush should still work though
22
+ Disc.flush
23
+ assert_equal 0, Disc.qlen('test')
24
+ end
25
+
26
+ test "testing mode enqueue jobs into an in-memory list by default" do
27
+ Echoer.enqueue(['one argument', { random: 'data' }, 3])
28
+
29
+ assert_equal 1, Disc.queues['test'].count
30
+ assert Disc.queues['test'].first.has_key?(:arguments)
31
+ assert_equal 3, Disc.queues['test'].first[:arguments].count
32
+ assert_equal 'one argument', Disc.queues['test'].first[:arguments].first
33
+ assert_equal 'Echoer', Disc.queues['test'].first[:class]
34
+ end
35
+
36
+ test "testing mode enqueue jobs into an in-memory list by default" do
37
+ Echoer.enqueue(['one argument', { random: 'data' }, 3])
38
+ assert_equal 'one argument', Disc.queues['test'].first[:arguments].first
39
+ end
40
+
41
+ test "ability to run jobs inline" do
42
+ Disc.inline!
43
+ assert_equal 'this is an argument', Returner.enqueue('this is an argument')
44
+ end
45
+ end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: disc
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.0.27
4
+ version: 0.0.28
5
5
  platform: ruby
6
6
  authors:
7
7
  - pote
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2016-05-01 00:00:00.000000000 Z
11
+ date: 2016-05-04 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: disque
@@ -61,13 +61,18 @@ files:
61
61
  - examples/echoer.rb
62
62
  - examples/failer.rb
63
63
  - examples/greeter.rb
64
+ - examples/returner.rb
64
65
  - lib/active_job/queue_adapters/disc_adapter.rb
65
66
  - lib/disc.rb
67
+ - lib/disc/errors.rb
66
68
  - lib/disc/job.rb
67
69
  - lib/disc/testing.rb
68
70
  - lib/disc/version.rb
69
71
  - lib/disc/worker.rb
70
72
  - test/disc_test.rb
73
+ - test/job_test.rb
74
+ - test/process_test.rb
75
+ - test/testing_test.rb
71
76
  homepage: https://github.com/pote/disc
72
77
  licenses:
73
78
  - MIT