chore-core 1.5.2 → 1.5.10
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.
- 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
|