chore-core 1.5.2 → 1.5.10
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +5 -13
- data/README.md +1 -0
- data/chore-core.gemspec +1 -1
- data/lib/chore/cli.rb +35 -15
- data/lib/chore/duplicate_detector.rb +6 -6
- data/lib/chore/encoders/json_encoder.rb +20 -0
- data/lib/chore/job.rb +15 -0
- data/lib/chore/publisher.rb +1 -1
- data/lib/chore/queues/sqs/consumer.rb +6 -7
- data/lib/chore/queues/sqs.rb +8 -3
- data/lib/chore/strategies/worker/forked_worker_strategy.rb +3 -2
- data/lib/chore/strategies/worker/single_worker_strategy.rb +3 -2
- data/lib/chore/version.rb +1 -1
- data/lib/chore/worker.rb +11 -21
- data/lib/chore.rb +5 -3
- data/spec/chore/cli_spec.rb +52 -3
- data/spec/chore/duplicate_detector_spec.rb +10 -14
- data/spec/chore/{json_encoder_spec.rb → encoders/json_encoder_spec.rb} +2 -2
- data/spec/chore/manager_spec.rb +10 -5
- data/spec/chore/queues/sqs/consumer_spec.rb +5 -5
- data/spec/chore/strategies/worker/forked_worker_strategy_spec.rb +2 -2
- data/spec/chore/worker_spec.rb +27 -18
- data/spec/spec_helper.rb +10 -1
- metadata +25 -25
- data/lib/chore/json_encoder.rb +0 -18
checksums.yaml
CHANGED
@@ -1,15 +1,7 @@
|
|
1
1
|
---
|
2
|
-
|
3
|
-
metadata.gz:
|
4
|
-
|
5
|
-
data.tar.gz: !binary |-
|
6
|
-
YmU5ZjI5YTFlNWUyODE3NzZlYzlkNjQ5NTFlZDViOWI4ZTZiYzU3Yg==
|
2
|
+
SHA1:
|
3
|
+
metadata.gz: b1aae723bbfe580eb5d31876ca91f32dd28baae7
|
4
|
+
data.tar.gz: 014aae64a27e2a6eb6210b76561e6cc1765056b6
|
7
5
|
SHA512:
|
8
|
-
metadata.gz:
|
9
|
-
|
10
|
-
Nzg1NTRkNGViZWI4NjgxNzkwYWQ4ZDM1YWU4MmQyMTgwNzRhMmYwMDRlYmMx
|
11
|
-
OTM3MDY5NWY5YjllZmZiM2I2NDJjZGE4NWRlZmY3MDcyYTI3Njg=
|
12
|
-
data.tar.gz: !binary |-
|
13
|
-
ODk0ZjIxYTI2YTU1MWQ0M2RiYzIyOGE4MGJjNTJkOTI1MmNhYjk4ZDFhODZl
|
14
|
-
YTQ5ZDIwMjAzOTEyY2JjYzBjNTZlMmFhYmEyZjYwYTU3Y2ZiMWUwYjZiYmRh
|
15
|
-
ZDg1YTg4MjBlZTkyNjQyMmRkNTZmZDkwMDBmYjFiNWJmYThiYjE=
|
6
|
+
metadata.gz: 110b80593989c4ddd8bf1502705d1ce88fd26ba0e458dcffdd9072ddcbf2d3dd9b7b4760b3a7ab37bbd150f66b4acea02e280e7396c2e203330820e5dceb6939
|
7
|
+
data.tar.gz: ef5522be2d88a900151437d3a127b5a692a7e71a3a77169a3fe12e25089157b363f7a0f890d1f48d4fd84aca305eaacfbb5f1553026a02a92ce99ef159ccf4c9
|
data/README.md
CHANGED
@@ -29,6 +29,7 @@ Other options include:
|
|
29
29
|
--worker-strategy Chore::Strategy::ForkedWorkerStrategy # which worker strategy class to use
|
30
30
|
--consumer Chore::Queues::SQS::Consumer # which consumer class to use Options are SQS::Consumer and Filesystem::Consumer. Filesystem is recommended for local and testing purposes only.
|
31
31
|
--consumer-strategy Chore::Queues::Strategies::Consumer::ThreadedConsumerStrategy # which consuming strategy to use. Options are SingleConsumerStrategy and ThreadedConsumerStrategy. Threaded is recommended for better tuning your consuming profile
|
32
|
+
--consumer-sleep-interval 1.0 # The amount of time in seconds to sleep when a consumer doesn't receive any messages. Sub-second values are accepted. The default varies by consumer implementation. This is a weak form of backoff for when there is no work to do.
|
32
33
|
--threads-per-queue 4 # number of threads per queue for consuming from a given queue.
|
33
34
|
--dedupe-servers # if using SQS or similiar queue with at-least once delivery and your memcache is running on something other than localhost
|
34
35
|
--batch-size 50 # how many messages are batched together before handing them to a worker
|
data/chore-core.gemspec
CHANGED
@@ -37,7 +37,7 @@ Gem::Specification.new do |s|
|
|
37
37
|
s.summary = "Job processing... for the future!"
|
38
38
|
|
39
39
|
s.add_runtime_dependency(%q<json>, [">= 0"])
|
40
|
-
s.add_runtime_dependency(%q<aws-sdk>, ["~> 1.
|
40
|
+
s.add_runtime_dependency(%q<aws-sdk>, ["~> 1.56", ">= 1.56.0"])
|
41
41
|
s.add_runtime_dependency(%q<thread>, ["~> 0.1.3"])
|
42
42
|
s.add_development_dependency(%q<rspec>, ["~> 2.12.0"])
|
43
43
|
s.add_development_dependency(%q<rdoc>, ["~> 3.12"])
|
data/lib/chore/cli.rb
CHANGED
@@ -92,7 +92,8 @@ module Chore #:nodoc:
|
|
92
92
|
private
|
93
93
|
def setup_options #:nodoc:
|
94
94
|
register_option "queues", "-q", "--queues QUEUE1,QUEUE2", "Names of queues to process (default: all known)" do |arg|
|
95
|
-
|
95
|
+
# This will remove duplicates. We ultimately force this to be a Set further below
|
96
|
+
options[:queues] = Set.new(arg.split(","))
|
96
97
|
end
|
97
98
|
|
98
99
|
register_option "except_queues", "-x", "--except QUEUE1,QUEUE2", "Process all queues (cannot specify --queues), except for the ones listed here" do |arg|
|
@@ -130,6 +131,12 @@ module Chore #:nodoc:
|
|
130
131
|
options[:consumer_strategy] = constantize(arg)
|
131
132
|
end
|
132
133
|
|
134
|
+
register_option 'consumer_sleep_interval', '--consumer-sleep-interval INTERVAL', Float, 'Length of time in seconds to sleep when the consumer does not find any messages. Defaults vary depending on consumer implementation'
|
135
|
+
|
136
|
+
register_option 'payload_handler', '--payload_handler CLASS_NAME', 'Name of a class to use as the payload handler (default: Chore::Job)' do |arg|
|
137
|
+
options[:payload_handler] = constantize(arg)
|
138
|
+
end
|
139
|
+
|
133
140
|
register_option 'shutdown_timeout', '--shutdown-timeout SECONDS', Float, "Upon shutdown, the number of seconds to wait before force killing worker strategies (default: #{Chore::DEFAULT_OPTIONS[:shutdown_timeout]})"
|
134
141
|
|
135
142
|
register_option 'dupe_on_cache_failure', '--dupe-on-cache-failure BOOLEAN', 'Determines the deduping behavior when a cache connection error occurs. When set to false, the message is assumed not to be a duplicate. (default: false)'
|
@@ -187,23 +194,36 @@ module Chore #:nodoc:
|
|
187
194
|
raise ArgumentError, "Cannot specify both --except and --queues"
|
188
195
|
end
|
189
196
|
|
190
|
-
|
191
|
-
|
192
|
-
|
193
|
-
|
194
|
-
|
195
|
-
|
196
|
-
|
197
|
+
### Ensure after loading the app, that we have the prefix set (quirk of using Chore::Job#prefixed_queue_name to do the prefixing)
|
198
|
+
Chore.config.queue_prefix ||= options[:queue_prefix]
|
199
|
+
|
200
|
+
### For ease, make sure except_queues is an array
|
201
|
+
options[:except_queues] ||= []
|
202
|
+
|
203
|
+
### Generate a hash of all possible queues and their prefixed_names
|
204
|
+
queue_map = Chore::Job.job_classes.inject({}) do |hsh,j|
|
205
|
+
klazz = constantize(j)
|
206
|
+
hsh[klazz.options[:name]] = klazz.prefixed_queue_name if klazz.options[:name]
|
207
|
+
hsh
|
197
208
|
end
|
198
209
|
|
199
|
-
|
200
|
-
|
201
|
-
|
202
|
-
|
203
|
-
|
204
|
-
options[:queues] = Set.new.tap do |queue_set|
|
205
|
-
original_queues.each {|oq_name| queue_set << "#{prefix}#{oq_name}"}
|
210
|
+
### If we passed in a queues option, use it as our working set, otherwise use all the queues
|
211
|
+
if options[:queues]
|
212
|
+
queues_to_use = options[:queues]
|
213
|
+
else
|
214
|
+
queues_to_use = queue_map.keys
|
206
215
|
end
|
216
|
+
|
217
|
+
### Remove the excepted queues from our working set
|
218
|
+
queues_to_use = queues_to_use - options[:except_queues]
|
219
|
+
|
220
|
+
### Set options[:queues] to the prefixed value of the current working set
|
221
|
+
options[:queues] = queues_to_use.inject([]) do |queues,k|
|
222
|
+
raise ArgumentError, "You have specified a queue #{k} for which you have no corresponding Job class" unless queue_map.has_key?(k)
|
223
|
+
queues << queue_map[k]
|
224
|
+
queues
|
225
|
+
end
|
226
|
+
|
207
227
|
raise ArgumentError, "No queues specified. Either include classes that include Chore::Job, or specify the --queues option" if options[:queues].empty?
|
208
228
|
end
|
209
229
|
|
@@ -29,11 +29,11 @@ module Chore
|
|
29
29
|
# Checks the message against the configured dedupe server to see if the message is unique or not
|
30
30
|
# Unique messages will return false
|
31
31
|
# Duplicated messages will return true
|
32
|
-
def found_duplicate?(
|
33
|
-
return false unless
|
34
|
-
timeout = self.queue_timeout(
|
32
|
+
def found_duplicate?(msg_data)
|
33
|
+
return false unless msg_data && msg_data[:queue]
|
34
|
+
timeout = self.queue_timeout(msg_data)
|
35
35
|
begin
|
36
|
-
!@memcached_client.add(
|
36
|
+
!@memcached_client.add(msg_data[:id], "1",timeout)
|
37
37
|
rescue StandardError => e
|
38
38
|
if @dupe_on_cache_failure
|
39
39
|
Chore.logger.error "Error accessing duplicate cache server. Assuming message is a duplicate. #{e}\n#{e.backtrace * "\n"}"
|
@@ -48,8 +48,8 @@ module Chore
|
|
48
48
|
# Retrieves the timeout for the given queue. The timeout is the window of time in seconds that
|
49
49
|
# we would consider the message to be non-unique, before we consider it dead in the water
|
50
50
|
# After that timeout, we would consider the next copy of the message received to be unique, and process it.
|
51
|
-
def queue_timeout(
|
52
|
-
@timeouts[queue
|
51
|
+
def queue_timeout(msg_data)
|
52
|
+
@timeouts[msg_data[:queue]] ||= msg_data[:visibility_timeout] || @timeout
|
53
53
|
end
|
54
54
|
|
55
55
|
end
|
@@ -0,0 +1,20 @@
|
|
1
|
+
require 'json'
|
2
|
+
|
3
|
+
module Chore
|
4
|
+
module Encoder
|
5
|
+
# Json encoding for serializing jobs.
|
6
|
+
module JsonEncoder
|
7
|
+
class << self
|
8
|
+
# Encodes the +job+ into JSON using the standard ruby JSON parsing library
|
9
|
+
def encode(job)
|
10
|
+
JSON.generate(job.to_hash)
|
11
|
+
end
|
12
|
+
|
13
|
+
# Decodes the +job+ from JSON into a ruby Hash using the standard ruby JSON parsing library
|
14
|
+
def decode(job)
|
15
|
+
JSON.parse(job)
|
16
|
+
end
|
17
|
+
end
|
18
|
+
end
|
19
|
+
end
|
20
|
+
end
|
data/lib/chore/job.rb
CHANGED
@@ -1,10 +1,13 @@
|
|
1
1
|
require 'chore/hooks'
|
2
|
+
require 'chore/util'
|
3
|
+
require 'chore/encoders/json_encoder'
|
2
4
|
|
3
5
|
module Chore
|
4
6
|
|
5
7
|
# <tt>Chore::Job</tt> is the module which gives your job classes the methods they need to be published
|
6
8
|
# and run within Chore. You cannot have a Job in Chore that does not include this module
|
7
9
|
module Job
|
10
|
+
extend Util
|
8
11
|
|
9
12
|
# An exception to represent a job choosing to forcibly reject a given instance of itself.
|
10
13
|
# The reasoning behind rejecting the job and the message that spawned it are left to
|
@@ -25,6 +28,18 @@ module Chore
|
|
25
28
|
base.extend(Hooks)
|
26
29
|
end
|
27
30
|
|
31
|
+
def self.payload_class(message)
|
32
|
+
constantize(message['class'])
|
33
|
+
end
|
34
|
+
|
35
|
+
def self.decode(data)
|
36
|
+
Encoder::JsonEncoder.decode(data)
|
37
|
+
end
|
38
|
+
|
39
|
+
def self.payload(message)
|
40
|
+
message['args']
|
41
|
+
end
|
42
|
+
|
28
43
|
module ClassMethods
|
29
44
|
DEFAULT_OPTIONS = { }
|
30
45
|
|
data/lib/chore/publisher.rb
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
module Chore
|
2
2
|
# Base class for Chore Publishers. Provides the bare interface one needs to adhere to when writing custom publishers
|
3
3
|
class Publisher
|
4
|
-
DEFAULT_OPTIONS = { :encoder => JsonEncoder }
|
4
|
+
DEFAULT_OPTIONS = { :encoder => Encoder::JsonEncoder }
|
5
5
|
|
6
6
|
attr_accessor :options
|
7
7
|
|
@@ -34,10 +34,10 @@ module Chore
|
|
34
34
|
while running?
|
35
35
|
begin
|
36
36
|
messages = handle_messages(&handler)
|
37
|
-
sleep 1 if messages.empty?
|
37
|
+
sleep (Chore.config.consumer_sleep_interval || 1) if messages.empty?
|
38
38
|
rescue AWS::SQS::Errors::NonExistentQueue => e
|
39
|
-
Chore.logger.error "You specified a queue that does not exist. You must create the queue before starting Chore. Shutting down..."
|
40
|
-
raise Chore::TerribleMistake
|
39
|
+
Chore.logger.error "You specified a queue '#{queue_name}' that does not exist. You must create the queue before starting Chore. Shutting down..."
|
40
|
+
raise Chore::TerribleMistake
|
41
41
|
rescue => e
|
42
42
|
Chore.logger.error { "SQSConsumer#Consume: #{e.inspect} #{e.backtrace * "\n"}" }
|
43
43
|
end
|
@@ -61,18 +61,17 @@ module Chore
|
|
61
61
|
# hook will be invoked, per message
|
62
62
|
def handle_messages(&block)
|
63
63
|
msg = queue.receive_messages(:limit => sqs_polling_amount, :attributes => [:receive_count])
|
64
|
-
|
65
64
|
messages = *msg
|
66
65
|
messages.each do |message|
|
67
|
-
block.call(message.handle, queue_name, queue_timeout, message.body, message.receive_count - 1) unless duplicate_message?(message)
|
66
|
+
block.call(message.handle, queue_name, queue_timeout, message.body, message.receive_count - 1) unless duplicate_message?({:id=>message.id, :queue=>message.queue.url, :visibility_timeout=>message.queue.visibility_timeout})
|
68
67
|
Chore.run_hooks_for(:on_fetch, message.handle, message.body)
|
69
68
|
end
|
70
69
|
messages
|
71
70
|
end
|
72
71
|
|
73
72
|
# Checks if the given message has already been received within the timeout window for this queue
|
74
|
-
def duplicate_message?(
|
75
|
-
dupe_detector.found_duplicate?(
|
73
|
+
def duplicate_message?(msg_data)
|
74
|
+
dupe_detector.found_duplicate?(msg_data)
|
76
75
|
end
|
77
76
|
|
78
77
|
# Returns the instance of the DuplicateDetector used to ensure unique messages.
|
data/lib/chore/queues/sqs.rb
CHANGED
@@ -27,9 +27,14 @@ module Chore
|
|
27
27
|
#This will raise an exception if AWS has not been configured by the project making use of Chore
|
28
28
|
sqs_queues = AWS::SQS.new.queues
|
29
29
|
Chore.prefixed_queue_names.each do |queue_name|
|
30
|
-
|
31
|
-
|
32
|
-
|
30
|
+
begin
|
31
|
+
Chore.logger.info "Chore Deleting Queue: #{queue_name}"
|
32
|
+
url = sqs_queues.url_for(queue_name)
|
33
|
+
sqs_queues[url].delete
|
34
|
+
rescue => e
|
35
|
+
# This could fail for a few reasons - log out why it failed, then continue on
|
36
|
+
Chore.logger.error "Deleting Queue: #{queue_name} failed because #{e}"
|
37
|
+
end
|
33
38
|
end
|
34
39
|
Chore.prefixed_queue_names
|
35
40
|
end
|
@@ -5,7 +5,8 @@ module Chore
|
|
5
5
|
class ForkedWorkerStrategy #:nodoc:
|
6
6
|
attr_accessor :workers
|
7
7
|
|
8
|
-
def initialize(manager)
|
8
|
+
def initialize(manager, opts={})
|
9
|
+
@options = opts
|
9
10
|
@manager = manager
|
10
11
|
@stopped = false
|
11
12
|
@workers = {}
|
@@ -57,7 +58,7 @@ module Chore
|
|
57
58
|
return unless acquire_worker
|
58
59
|
|
59
60
|
begin
|
60
|
-
w = Worker.new(work)
|
61
|
+
w = Worker.new(work, @options)
|
61
62
|
Chore.run_hooks_for(:before_fork,w)
|
62
63
|
pid = nil
|
63
64
|
Chore.run_hooks_for(:around_fork,w) do
|
@@ -7,7 +7,8 @@ module Chore
|
|
7
7
|
|
8
8
|
attr_reader :worker
|
9
9
|
|
10
|
-
def initialize(manager)
|
10
|
+
def initialize(manager, opts={})
|
11
|
+
@options = opts
|
11
12
|
@manager = manager
|
12
13
|
@worker = nil
|
13
14
|
end
|
@@ -23,7 +24,7 @@ module Chore
|
|
23
24
|
# Assigns work if there isn't already a worker in progress. Otherwise, is a noop
|
24
25
|
def assign(work)
|
25
26
|
if workers_available?
|
26
|
-
@worker = Worker.new(work)
|
27
|
+
@worker = Worker.new(work, @options)
|
27
28
|
@worker.start
|
28
29
|
@worker = nil
|
29
30
|
true
|
data/lib/chore/version.rb
CHANGED
data/lib/chore/worker.rb
CHANGED
@@ -1,5 +1,5 @@
|
|
1
1
|
require 'chore/util'
|
2
|
-
require 'chore/
|
2
|
+
require 'chore/job'
|
3
3
|
|
4
4
|
module Chore
|
5
5
|
class TimeoutError < StandardError
|
@@ -12,24 +12,23 @@ module Chore
|
|
12
12
|
class Worker
|
13
13
|
include Util
|
14
14
|
|
15
|
-
DEFAULT_OPTIONS = { :encoder => JsonEncoder }
|
16
15
|
attr_accessor :options
|
17
16
|
attr_reader :work
|
18
17
|
attr_reader :started_at
|
19
18
|
|
20
|
-
def self.start(work) #:nodoc:
|
21
|
-
self.new(work).start
|
19
|
+
def self.start(work, opts={}) #:nodoc:
|
20
|
+
self.new(work, opts).start
|
22
21
|
end
|
23
22
|
|
24
23
|
# Create a Worker. Give it an array of work (or single item), and +opts+.
|
25
|
-
# Currently, the only option supported by Worker is +:
|
26
|
-
# the
|
24
|
+
# Currently, the only option supported by Worker is +:payload_handler+ which contains helpers
|
25
|
+
# for decoding the item and finding the correct payload class
|
27
26
|
def initialize(work=[],opts={})
|
28
27
|
@stopping = false
|
29
28
|
@started_at = Time.now
|
30
29
|
@work = work
|
31
30
|
@work = [work] unless work.kind_of?(Array)
|
32
|
-
self.options =
|
31
|
+
self.options = {:payload_handler => Chore.config.payload_handler}.merge(opts)
|
33
32
|
end
|
34
33
|
|
35
34
|
# Whether this worker has existed for longer than it's allowed to
|
@@ -56,7 +55,6 @@ module Chore
|
|
56
55
|
def start
|
57
56
|
@work.each do |item|
|
58
57
|
return if @stopping
|
59
|
-
Chore.logger.debug { "Doing: #{item.queue_name} with #{item.message}" }
|
60
58
|
begin
|
61
59
|
start_item(item)
|
62
60
|
rescue => e
|
@@ -76,20 +74,16 @@ module Chore
|
|
76
74
|
@stopping = true
|
77
75
|
end
|
78
76
|
|
79
|
-
protected
|
80
|
-
def payload_class(message)
|
81
|
-
constantize(message['class'])
|
82
|
-
end
|
83
|
-
|
84
77
|
private
|
85
78
|
def start_item(item)
|
86
|
-
message =
|
87
|
-
klass = payload_class(message)
|
79
|
+
message = options[:payload_handler].decode(item.message)
|
80
|
+
klass = options[:payload_handler].payload_class(message)
|
88
81
|
return unless klass.run_hooks_for(:before_perform,message)
|
89
|
-
|
90
82
|
begin
|
83
|
+
Chore.logger.info { "Running job #{klass} with params #{message}"}
|
91
84
|
perform_job(klass,message)
|
92
85
|
item.consumer.complete(item.id)
|
86
|
+
Chore.logger.info { "Finished job #{klass} with params #{message}"}
|
93
87
|
klass.run_hooks_for(:after_perform,message)
|
94
88
|
rescue Job::RejectMessageException
|
95
89
|
item.consumer.reject(item.id)
|
@@ -107,11 +101,7 @@ module Chore
|
|
107
101
|
end
|
108
102
|
|
109
103
|
def perform_job(klass, message)
|
110
|
-
klass.perform(*
|
111
|
-
end
|
112
|
-
|
113
|
-
def decode_job(data)
|
114
|
-
options[:encoder].decode(data)
|
104
|
+
klass.perform(*options[:payload_handler].payload(message))
|
115
105
|
end
|
116
106
|
end
|
117
107
|
end
|
data/lib/chore.rb
CHANGED
@@ -8,7 +8,7 @@ require 'chore/configuration'
|
|
8
8
|
require 'chore/cli'
|
9
9
|
require 'chore/consumer'
|
10
10
|
require 'chore/job'
|
11
|
-
require 'chore/json_encoder'
|
11
|
+
require 'chore/encoders/json_encoder'
|
12
12
|
require 'chore/manager'
|
13
13
|
require 'chore/publisher'
|
14
14
|
require 'chore/util'
|
@@ -21,6 +21,7 @@ require 'chore/publisher'
|
|
21
21
|
end
|
22
22
|
|
23
23
|
module Chore #:nodoc:
|
24
|
+
extend Util
|
24
25
|
VERSION = Chore::Version::STRING #:nodoc:
|
25
26
|
|
26
27
|
# The default configuration options for Chore.
|
@@ -38,7 +39,8 @@ module Chore #:nodoc:
|
|
38
39
|
:default_queue_timeout => (12 * 60 * 60), # 12 hours
|
39
40
|
:shutdown_timeout => (2 * 60),
|
40
41
|
:max_attempts => 1.0 / 0.0, # Infinity
|
41
|
-
:dupe_on_cache_failure => false
|
42
|
+
:dupe_on_cache_failure => false,
|
43
|
+
:payload_handler => Chore::Job
|
42
44
|
}
|
43
45
|
|
44
46
|
class << self
|
@@ -211,7 +213,7 @@ module Chore #:nodoc:
|
|
211
213
|
|
212
214
|
# List of queue_names as configured via Chore::Job including their prefix, if set.
|
213
215
|
def self.prefixed_queue_names
|
214
|
-
Chore::Job.job_classes.collect {|klass| c = klass
|
216
|
+
Chore::Job.job_classes.collect {|klass| c = constantize(klass); c.prefixed_queue_name}
|
215
217
|
end
|
216
218
|
end
|
217
219
|
|
data/spec/chore/cli_spec.rb
CHANGED
@@ -79,6 +79,14 @@ describe Chore::CLI do
|
|
79
79
|
end
|
80
80
|
end
|
81
81
|
|
82
|
+
context 'when provided a queue for which there is no job class' do
|
83
|
+
let(:queue_options) {['--queues=test2,test3']}
|
84
|
+
|
85
|
+
it 'should raise an error' do
|
86
|
+
expect {cli.parse(queue_options)}.to raise_error
|
87
|
+
end
|
88
|
+
end
|
89
|
+
|
82
90
|
context 'when both --queue_prefix and --queues have been provided' do
|
83
91
|
let(:queue_options) {['--queue-prefix=prefixy', '--queues=test2']}
|
84
92
|
before :each do
|
@@ -112,9 +120,14 @@ describe Chore::CLI do
|
|
112
120
|
expect { cli.parse(['--except=something','--queues=something,else']) }.to raise_error(ArgumentError)
|
113
121
|
end
|
114
122
|
|
115
|
-
|
116
|
-
|
117
|
-
|
123
|
+
context "when no queues are found" do
|
124
|
+
before :each do
|
125
|
+
Chore::Job.stub(:job_classes).and_return([])
|
126
|
+
end
|
127
|
+
|
128
|
+
it 'should raise an exception' do
|
129
|
+
expect { cli.parse([]) }.to raise_error(ArgumentError)
|
130
|
+
end
|
118
131
|
end
|
119
132
|
end
|
120
133
|
|
@@ -138,6 +151,14 @@ describe Chore::CLI do
|
|
138
151
|
end
|
139
152
|
end
|
140
153
|
|
154
|
+
context "--payload_handler" do
|
155
|
+
let(:command) {["--payload_handler=Chore::Job"]}
|
156
|
+
|
157
|
+
it "should set the payload handler class" do
|
158
|
+
config.payload_handler.should == Chore::Job
|
159
|
+
end
|
160
|
+
end
|
161
|
+
|
141
162
|
describe '--shutdown-timeout' do
|
142
163
|
let(:command) { ["--shutdown-timeout=#{amount}"] }
|
143
164
|
subject { config.shutdown_timeout }
|
@@ -158,6 +179,34 @@ describe Chore::CLI do
|
|
158
179
|
end
|
159
180
|
end
|
160
181
|
|
182
|
+
describe '--consumer_sleep_interval' do
|
183
|
+
let(:command) {["--consumer-sleep-interval=#{amount}"]}
|
184
|
+
subject {config.consumer_sleep_interval}
|
185
|
+
|
186
|
+
context 'given an integer value' do
|
187
|
+
let(:amount) { '10' }
|
188
|
+
|
189
|
+
it 'is that amount' do
|
190
|
+
subject.should == amount.to_i
|
191
|
+
end
|
192
|
+
end
|
193
|
+
|
194
|
+
context 'given a float value' do
|
195
|
+
let(:amount) { '0.5' }
|
196
|
+
|
197
|
+
it 'is that amount' do
|
198
|
+
subject.should == amount.to_f
|
199
|
+
end
|
200
|
+
end
|
201
|
+
|
202
|
+
context 'given no value' do
|
203
|
+
let(:command) { [] }
|
204
|
+
it 'is the default value, nil' do
|
205
|
+
subject.should == nil
|
206
|
+
end
|
207
|
+
end
|
208
|
+
end
|
209
|
+
|
161
210
|
describe '--max-attempts' do
|
162
211
|
let(:command) { ["--max-attempts=#{amount}"] }
|
163
212
|
subject { config.max_attempts }
|
@@ -6,46 +6,42 @@ describe Chore::DuplicateDetector do
|
|
6
6
|
let(:dupe_on_cache_failure) { false }
|
7
7
|
let(:dedupe_params) { { :memcached_client => memcache, :dupe_on_cache_failure => dupe_on_cache_failure } }
|
8
8
|
let(:dedupe) { Chore::DuplicateDetector.new(dedupe_params)}
|
9
|
-
let(:message) { double('message') }
|
10
9
|
let(:timeout) { 2 }
|
11
10
|
let(:queue_url) {"queue://bogus/url"}
|
12
|
-
let(:queue) {
|
11
|
+
let(:queue) { double('queue', :visibility_timeout=>timeout, :url=>queue_url) }
|
13
12
|
let(:id) { SecureRandom.uuid }
|
14
|
-
|
15
|
-
|
16
|
-
message.stub(:id).and_return(id)
|
17
|
-
message.stub(:queue).and_return(queue)
|
18
|
-
end
|
13
|
+
let(:message) { double('message', :id=>id, :queue=>queue) }
|
14
|
+
let(:message_data) {{:id=>message.id, :visibility_timeout=>queue.visibility_timeout, :queue=>queue.url}}
|
19
15
|
|
20
16
|
describe "#found_duplicate" do
|
21
17
|
it 'should not return true if the message has not already been seen' do
|
22
18
|
memcache.should_receive(:add).and_return(true)
|
23
|
-
dedupe.found_duplicate?(
|
19
|
+
dedupe.found_duplicate?(message_data).should_not be_true
|
24
20
|
end
|
25
21
|
|
26
22
|
it 'should return true if the message has already been seen' do
|
27
23
|
memcache.should_receive(:add).and_return(false)
|
28
|
-
dedupe.found_duplicate?(
|
24
|
+
dedupe.found_duplicate?(message_data).should be_true
|
29
25
|
end
|
30
26
|
|
31
27
|
it 'should return false if given an invalid message' do
|
32
|
-
dedupe.found_duplicate?(
|
28
|
+
dedupe.found_duplicate?({}).should be_false
|
33
29
|
end
|
34
30
|
|
35
31
|
it "should return false when identity store errors" do
|
36
32
|
memcache.should_receive(:add).and_raise("no")
|
37
|
-
dedupe.found_duplicate?(
|
33
|
+
dedupe.found_duplicate?(message_data).should be_false
|
38
34
|
end
|
39
35
|
|
40
36
|
it "should set the timeout to be the queue's " do
|
41
37
|
memcache.should_receive(:add).with(id,"1",timeout).and_return(true)
|
42
|
-
dedupe.found_duplicate?(
|
38
|
+
dedupe.found_duplicate?(message_data).should be_false
|
43
39
|
end
|
44
40
|
|
45
41
|
it "should call #visibility_timeout once and only once" do
|
46
42
|
queue.should_receive(:visibility_timeout).once
|
47
43
|
memcache.should_receive(:add).at_least(3).times.and_return(true)
|
48
|
-
3.times { dedupe.found_duplicate?(
|
44
|
+
3.times { dedupe.found_duplicate?(message_data) }
|
49
45
|
end
|
50
46
|
|
51
47
|
context 'when a memecached connection error occurs' do
|
@@ -54,7 +50,7 @@ describe Chore::DuplicateDetector do
|
|
54
50
|
|
55
51
|
it "returns true" do
|
56
52
|
memcache.should_receive(:add).and_raise
|
57
|
-
dedupe.found_duplicate?(
|
53
|
+
dedupe.found_duplicate?(message_data).should be_true
|
58
54
|
end
|
59
55
|
end
|
60
56
|
end
|
@@ -1,6 +1,6 @@
|
|
1
|
-
require File.expand_path(File.dirname(__FILE__) + '
|
1
|
+
require File.expand_path(File.dirname(__FILE__) + '/../../spec_helper')
|
2
2
|
|
3
|
-
describe Chore::JsonEncoder do
|
3
|
+
describe Chore::Encoder::JsonEncoder do
|
4
4
|
it 'should have an encode method' do
|
5
5
|
subject.should respond_to :encode
|
6
6
|
end
|
data/spec/chore/manager_spec.rb
CHANGED
@@ -15,10 +15,12 @@ describe Chore::Manager do
|
|
15
15
|
manager = Chore::Manager.new
|
16
16
|
end
|
17
17
|
|
18
|
+
|
19
|
+
|
18
20
|
describe 'running the manager' do
|
19
21
|
|
20
22
|
let(:manager) { Chore::Manager.new}
|
21
|
-
let(:work) { Chore::UnitOfWork.new(Chore::JsonEncoder.encode({:class => 'MyClass',:args => []}),mock()) }
|
23
|
+
let(:work) { Chore::UnitOfWork.new(Chore::Encoder::JsonEncoder.encode({:class => 'MyClass',:args => []}),mock()) }
|
22
24
|
|
23
25
|
it 'should start the fetcher when starting the manager' do
|
24
26
|
fetcher.should_receive(:start)
|
@@ -26,14 +28,17 @@ describe Chore::Manager do
|
|
26
28
|
end
|
27
29
|
|
28
30
|
describe 'assigning messages' do
|
29
|
-
|
30
|
-
|
31
|
-
|
31
|
+
let(:worker) { mock() }
|
32
|
+
|
33
|
+
before(:each) do
|
32
34
|
worker.should_receive(:start).with()
|
35
|
+
end
|
36
|
+
|
37
|
+
it 'should create a worker if one is available' do
|
38
|
+
Chore::Worker.should_receive(:new).with(work,{}).and_return(worker)
|
33
39
|
manager.assign(work)
|
34
40
|
end
|
35
41
|
end
|
36
|
-
|
37
42
|
end
|
38
43
|
|
39
44
|
end
|
@@ -4,10 +4,11 @@ describe Chore::Queues::SQS::Consumer do
|
|
4
4
|
let(:queue_name) { "test" }
|
5
5
|
let(:queue_url) { "test_url" }
|
6
6
|
let(:queues) { double("queues") }
|
7
|
-
let(:queue) { double("test_queue") }
|
7
|
+
let(:queue) { double("test_queue", :visibility_timeout=>10, :url=>"test_queue", :name=>"test_queue") }
|
8
8
|
let(:options) { {} }
|
9
9
|
let(:consumer) { Chore::Queues::SQS::Consumer.new(queue_name) }
|
10
|
-
let(:message) { TestMessage.new("handle",
|
10
|
+
let(:message) { TestMessage.new("handle",queue, "message body", 1) }
|
11
|
+
let(:message_data) {{:id=>message.id, :queue=>message.queue.url, :visibility_timeout=>message.queue.visibility_timeout}}
|
11
12
|
let(:pool) { double("pool") }
|
12
13
|
let(:sqs) { double('AWS::SQS') }
|
13
14
|
|
@@ -18,7 +19,6 @@ describe Chore::Queues::SQS::Consumer do
|
|
18
19
|
queues.stub(:url_for) { queue_url }
|
19
20
|
queues.stub(:[]) { queue }
|
20
21
|
queue.stub(:receive_message) { message }
|
21
|
-
queue.stub(:visibility_timeout) { 10 }
|
22
22
|
pool.stub(:empty!) { nil }
|
23
23
|
end
|
24
24
|
|
@@ -72,7 +72,7 @@ describe Chore::Queues::SQS::Consumer do
|
|
72
72
|
end
|
73
73
|
|
74
74
|
it "should check the uniqueness of the message" do
|
75
|
-
Chore::DuplicateDetector.any_instance.should_receive(:found_duplicate?).with(
|
75
|
+
Chore::DuplicateDetector.any_instance.should_receive(:found_duplicate?).with(message_data).and_return(false)
|
76
76
|
consumer.consume
|
77
77
|
end
|
78
78
|
|
@@ -81,7 +81,7 @@ describe Chore::Queues::SQS::Consumer do
|
|
81
81
|
end
|
82
82
|
|
83
83
|
it 'should not yield for a dupe message' do
|
84
|
-
Chore::DuplicateDetector.any_instance.should_receive(:found_duplicate?).with(
|
84
|
+
Chore::DuplicateDetector.any_instance.should_receive(:found_duplicate?).with(message_data).and_return(true)
|
85
85
|
expect {|b| consumer.consume(&b) }.not_to yield_control
|
86
86
|
end
|
87
87
|
|
@@ -9,7 +9,7 @@ describe Chore::Strategy::ForkedWorkerStrategy do
|
|
9
9
|
strategy
|
10
10
|
end
|
11
11
|
let(:job_timeout) { 60 }
|
12
|
-
let(:job) { Chore::UnitOfWork.new(SecureRandom.uuid, 'test', job_timeout, Chore::JsonEncoder.encode(TestJob.job_hash([1,2,"3"])), 0) }
|
12
|
+
let(:job) { Chore::UnitOfWork.new(SecureRandom.uuid, 'test', job_timeout, Chore::Encoder::JsonEncoder.encode(TestJob.job_hash([1,2,"3"])), 0) }
|
13
13
|
let!(:worker) { Chore::Worker.new(job) }
|
14
14
|
let(:pid) { Random.rand(2048) }
|
15
15
|
|
@@ -41,7 +41,7 @@ describe Chore::Strategy::ForkedWorkerStrategy do
|
|
41
41
|
end
|
42
42
|
|
43
43
|
it 'should assign a job to a new worker' do
|
44
|
-
Chore::Worker.should_receive(:new).with(job).and_return(worker)
|
44
|
+
Chore::Worker.should_receive(:new).with(job, {}).and_return(worker)
|
45
45
|
worker.should_receive(:start)
|
46
46
|
forker.assign(job)
|
47
47
|
end
|
data/spec/chore/worker_spec.rb
CHANGED
@@ -15,27 +15,36 @@ describe Chore::Worker do
|
|
15
15
|
let(:job_args) { [1,2,'3'] }
|
16
16
|
let(:job) { SimpleJob.job_hash(job_args) }
|
17
17
|
|
18
|
-
it 'should use a default
|
18
|
+
it 'should use a default payload handler' do
|
19
19
|
worker = Chore::Worker.new
|
20
|
-
worker.options[:
|
20
|
+
worker.options[:payload_handler].should == Chore::Job
|
21
21
|
end
|
22
22
|
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
|
23
|
+
shared_examples_for "a worker" do
|
24
|
+
it 'processing a single job' do
|
25
|
+
work = Chore::UnitOfWork.new('1', 'test', 60, encoded_job, 0, consumer)
|
26
|
+
SimpleJob.should_receive(:perform).with(*payload)
|
27
|
+
consumer.should_receive(:complete).with('1')
|
28
|
+
w = Chore::Worker.new(work, {:payload_handler => payload_handler})
|
29
|
+
w.start
|
30
|
+
end
|
30
31
|
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
|
32
|
+
it 'processing multiple jobs' do
|
33
|
+
work = []
|
34
|
+
10.times do |i|
|
35
|
+
work << Chore::UnitOfWork.new(i, 'test', 60, encoded_job, 0, consumer)
|
36
|
+
end
|
37
|
+
SimpleJob.should_receive(:perform).exactly(10).times
|
38
|
+
consumer.should_receive(:complete).exactly(10).times
|
39
|
+
Chore::Worker.start(work, {:payload_handler => payload_handler})
|
35
40
|
end
|
36
|
-
|
37
|
-
|
38
|
-
|
41
|
+
end
|
42
|
+
|
43
|
+
describe "with default payload handler" do
|
44
|
+
let(:encoded_job) { Chore::Encoder::JsonEncoder.encode(job) }
|
45
|
+
let(:payload_handler) { Chore::Job }
|
46
|
+
let(:payload) {job_args}
|
47
|
+
it_behaves_like "a worker"
|
39
48
|
end
|
40
49
|
|
41
50
|
describe 'expired?' do
|
@@ -43,7 +52,7 @@ describe Chore::Worker do
|
|
43
52
|
let(:queue_timeouts) { [10, 20, 30] }
|
44
53
|
let(:work) do
|
45
54
|
queue_timeouts.map do |queue_timeout|
|
46
|
-
Chore::UnitOfWork.new('1', 'test', queue_timeout, Chore::JsonEncoder.encode(job), 0, consumer)
|
55
|
+
Chore::UnitOfWork.new('1', 'test', queue_timeout, Chore::Encoder::JsonEncoder.encode(job), 0, consumer)
|
47
56
|
end
|
48
57
|
end
|
49
58
|
let(:worker) do
|
@@ -100,7 +109,7 @@ describe Chore::Worker do
|
|
100
109
|
end
|
101
110
|
|
102
111
|
context 'on perform' do
|
103
|
-
let(:encoded_job) { Chore::JsonEncoder.encode(job) }
|
112
|
+
let(:encoded_job) { Chore::Encoder::JsonEncoder.encode(job) }
|
104
113
|
let(:parsed_job) { JSON.parse(encoded_job) }
|
105
114
|
|
106
115
|
before(:each) do
|
data/spec/spec_helper.rb
CHANGED
@@ -26,11 +26,19 @@ class FakePublisher < Chore::Publisher
|
|
26
26
|
end
|
27
27
|
end
|
28
28
|
|
29
|
-
TestMessage = Struct.new(:handle,:
|
29
|
+
TestMessage = Struct.new(:handle,:queue,:body,:receive_count) do
|
30
30
|
def empty?
|
31
31
|
false
|
32
32
|
end
|
33
33
|
|
34
|
+
def queue_name
|
35
|
+
self.queue.name
|
36
|
+
end
|
37
|
+
|
38
|
+
def id
|
39
|
+
self.handle
|
40
|
+
end
|
41
|
+
|
34
42
|
# Structs define a to_a behavior that is not compatible with array splatting. Remove it so that
|
35
43
|
# [*message] on a struct will behave the same as on a string.
|
36
44
|
undef_method :to_a
|
@@ -38,6 +46,7 @@ end
|
|
38
46
|
|
39
47
|
|
40
48
|
RSpec.configure do |config|
|
49
|
+
config.include Chore::Util
|
41
50
|
config.before do
|
42
51
|
Chore.configure do |c|
|
43
52
|
c.aws_access_key = ""
|
metadata
CHANGED
@@ -1,103 +1,103 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: chore-core
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 1.5.
|
4
|
+
version: 1.5.10
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Tapjoy
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date:
|
11
|
+
date: 2015-07-08 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: json
|
15
15
|
requirement: !ruby/object:Gem::Requirement
|
16
16
|
requirements:
|
17
|
-
- -
|
17
|
+
- - ">="
|
18
18
|
- !ruby/object:Gem::Version
|
19
19
|
version: '0'
|
20
20
|
type: :runtime
|
21
21
|
prerelease: false
|
22
22
|
version_requirements: !ruby/object:Gem::Requirement
|
23
23
|
requirements:
|
24
|
-
- -
|
24
|
+
- - ">="
|
25
25
|
- !ruby/object:Gem::Version
|
26
26
|
version: '0'
|
27
27
|
- !ruby/object:Gem::Dependency
|
28
28
|
name: aws-sdk
|
29
29
|
requirement: !ruby/object:Gem::Requirement
|
30
30
|
requirements:
|
31
|
-
- - ~>
|
31
|
+
- - "~>"
|
32
32
|
- !ruby/object:Gem::Version
|
33
|
-
version: '1.
|
34
|
-
- -
|
33
|
+
version: '1.56'
|
34
|
+
- - ">="
|
35
35
|
- !ruby/object:Gem::Version
|
36
|
-
version: 1.
|
36
|
+
version: 1.56.0
|
37
37
|
type: :runtime
|
38
38
|
prerelease: false
|
39
39
|
version_requirements: !ruby/object:Gem::Requirement
|
40
40
|
requirements:
|
41
|
-
- - ~>
|
41
|
+
- - "~>"
|
42
42
|
- !ruby/object:Gem::Version
|
43
|
-
version: '1.
|
44
|
-
- -
|
43
|
+
version: '1.56'
|
44
|
+
- - ">="
|
45
45
|
- !ruby/object:Gem::Version
|
46
|
-
version: 1.
|
46
|
+
version: 1.56.0
|
47
47
|
- !ruby/object:Gem::Dependency
|
48
48
|
name: thread
|
49
49
|
requirement: !ruby/object:Gem::Requirement
|
50
50
|
requirements:
|
51
|
-
- - ~>
|
51
|
+
- - "~>"
|
52
52
|
- !ruby/object:Gem::Version
|
53
53
|
version: 0.1.3
|
54
54
|
type: :runtime
|
55
55
|
prerelease: false
|
56
56
|
version_requirements: !ruby/object:Gem::Requirement
|
57
57
|
requirements:
|
58
|
-
- - ~>
|
58
|
+
- - "~>"
|
59
59
|
- !ruby/object:Gem::Version
|
60
60
|
version: 0.1.3
|
61
61
|
- !ruby/object:Gem::Dependency
|
62
62
|
name: rspec
|
63
63
|
requirement: !ruby/object:Gem::Requirement
|
64
64
|
requirements:
|
65
|
-
- - ~>
|
65
|
+
- - "~>"
|
66
66
|
- !ruby/object:Gem::Version
|
67
67
|
version: 2.12.0
|
68
68
|
type: :development
|
69
69
|
prerelease: false
|
70
70
|
version_requirements: !ruby/object:Gem::Requirement
|
71
71
|
requirements:
|
72
|
-
- - ~>
|
72
|
+
- - "~>"
|
73
73
|
- !ruby/object:Gem::Version
|
74
74
|
version: 2.12.0
|
75
75
|
- !ruby/object:Gem::Dependency
|
76
76
|
name: rdoc
|
77
77
|
requirement: !ruby/object:Gem::Requirement
|
78
78
|
requirements:
|
79
|
-
- - ~>
|
79
|
+
- - "~>"
|
80
80
|
- !ruby/object:Gem::Version
|
81
81
|
version: '3.12'
|
82
82
|
type: :development
|
83
83
|
prerelease: false
|
84
84
|
version_requirements: !ruby/object:Gem::Requirement
|
85
85
|
requirements:
|
86
|
-
- - ~>
|
86
|
+
- - "~>"
|
87
87
|
- !ruby/object:Gem::Version
|
88
88
|
version: '3.12'
|
89
89
|
- !ruby/object:Gem::Dependency
|
90
90
|
name: bundler
|
91
91
|
requirement: !ruby/object:Gem::Requirement
|
92
92
|
requirements:
|
93
|
-
- -
|
93
|
+
- - ">="
|
94
94
|
- !ruby/object:Gem::Version
|
95
95
|
version: '0'
|
96
96
|
type: :development
|
97
97
|
prerelease: false
|
98
98
|
version_requirements: !ruby/object:Gem::Requirement
|
99
99
|
requirements:
|
100
|
-
- -
|
100
|
+
- - ">="
|
101
101
|
- !ruby/object:Gem::Version
|
102
102
|
version: '0'
|
103
103
|
description: Job processing with pluggable backends and strategies
|
@@ -120,10 +120,10 @@ files:
|
|
120
120
|
- lib/chore/configuration.rb
|
121
121
|
- lib/chore/consumer.rb
|
122
122
|
- lib/chore/duplicate_detector.rb
|
123
|
+
- lib/chore/encoders/json_encoder.rb
|
123
124
|
- lib/chore/fetcher.rb
|
124
125
|
- lib/chore/hooks.rb
|
125
126
|
- lib/chore/job.rb
|
126
|
-
- lib/chore/json_encoder.rb
|
127
127
|
- lib/chore/manager.rb
|
128
128
|
- lib/chore/publisher.rb
|
129
129
|
- lib/chore/queues/filesystem/consumer.rb
|
@@ -147,10 +147,10 @@ files:
|
|
147
147
|
- spec/chore/cli_spec.rb
|
148
148
|
- spec/chore/consumer_spec.rb
|
149
149
|
- spec/chore/duplicate_detector_spec.rb
|
150
|
+
- spec/chore/encoders/json_encoder_spec.rb
|
150
151
|
- spec/chore/fetcher_spec.rb
|
151
152
|
- spec/chore/hooks_spec.rb
|
152
153
|
- spec/chore/job_spec.rb
|
153
|
-
- spec/chore/json_encoder_spec.rb
|
154
154
|
- spec/chore/manager_spec.rb
|
155
155
|
- spec/chore/queues/filesystem/filesystem_consumer_spec.rb
|
156
156
|
- spec/chore/queues/sqs/consumer_spec.rb
|
@@ -176,17 +176,17 @@ require_paths:
|
|
176
176
|
- lib
|
177
177
|
required_ruby_version: !ruby/object:Gem::Requirement
|
178
178
|
requirements:
|
179
|
-
- -
|
179
|
+
- - ">="
|
180
180
|
- !ruby/object:Gem::Version
|
181
181
|
version: '0'
|
182
182
|
required_rubygems_version: !ruby/object:Gem::Requirement
|
183
183
|
requirements:
|
184
|
-
- -
|
184
|
+
- - ">="
|
185
185
|
- !ruby/object:Gem::Version
|
186
186
|
version: '0'
|
187
187
|
requirements: []
|
188
188
|
rubyforge_project:
|
189
|
-
rubygems_version: 2.
|
189
|
+
rubygems_version: 2.4.5
|
190
190
|
signing_key:
|
191
191
|
specification_version: 4
|
192
192
|
summary: Job processing... for the future!
|
data/lib/chore/json_encoder.rb
DELETED
@@ -1,18 +0,0 @@
|
|
1
|
-
require 'json'
|
2
|
-
|
3
|
-
module Chore
|
4
|
-
# Json encoding for serializing jobs.
|
5
|
-
module JsonEncoder
|
6
|
-
class << self
|
7
|
-
# Encodes the +job+ into JSON using the standard ruby JSON parsing library
|
8
|
-
def encode(job)
|
9
|
-
JSON.generate(job.to_hash)
|
10
|
-
end
|
11
|
-
|
12
|
-
# Decodes the +job+ from JSON into a ruby Hash using the standard ruby JSON parsing library
|
13
|
-
def decode(job)
|
14
|
-
JSON.parse(job)
|
15
|
-
end
|
16
|
-
end
|
17
|
-
end
|
18
|
-
end
|