qs 0.6.1 → 0.7.0

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
- data.tar.gz: 8f69f568a5b9a346be6a45f7f439fe835263240b
4
- metadata.gz: b11ca0b892b3fc054d3c350c529c6933d415ac01
3
+ data.tar.gz: 110c7cbf5c19120501d4dfae9f332e428363aff3
4
+ metadata.gz: 053fda8d651bb4509a99537ef8b8edb906a80acd
5
5
  SHA512:
6
- data.tar.gz: 18e0f1388fc35d85e8e324433be89bdccba9ee658b10797ed85c756646f51552109cf5c8fca4276e300cb53dda81cf78be772939d9fdc100e0146b0be64d37e3
7
- metadata.gz: e83ff088d753074d81c84d686d25b977e8a875ef6b0174e8455785d23fc25057fc5e1af4259801c97d0234c7ddc848f170e6009ee00cdac7c06cdb94edb9f7e8
6
+ data.tar.gz: 6c145c40e72aecad5c87b33709a3a3aa097a07a4bd08a17819d23965efd36fdbe46312a01fd638f346e4e34a2c7cc4c2ab85af7a9951083e97aae68f93527624
7
+ metadata.gz: 56bff595f01f50dba796e2b436b842376188d3c031ab6cb88395bb7cc41e04250217c04ef5eedc78ba221586795045ba8e6b739816cbe65d89cb01dacd9607ab
@@ -1,3 +1,4 @@
1
+ $LOAD_PATH.push(File.expand_path('../..', __FILE__))
1
2
  require 'qs'
2
3
 
3
4
  ENV['LOG_NAME'] = 'log/bench_daemon.log'
@@ -1,3 +1,4 @@
1
+ $LOAD_PATH.push(File.expand_path('../..', __FILE__))
1
2
  require 'qs'
2
3
 
3
4
  ENV['LOG_NAME'] = 'log/bench_dispatcher_daemon.log'
@@ -1,3 +1,5 @@
1
+ $LOAD_PATH.push(File.expand_path('../..', __FILE__))
2
+
1
3
  require 'benchmark'
2
4
  require 'scmd'
3
5
  require 'bench/setup'
@@ -16,7 +16,7 @@ else
16
16
  File.open('/dev/null', 'w')
17
17
  end
18
18
 
19
- Qs.config.dispatcher.queue_name = 'bench-dispatcher'
19
+ Qs.config.dispatcher_queue_name = 'bench-dispatcher'
20
20
  Qs.config.event_publisher = 'Bench Script'
21
21
  Qs.init
22
22
 
data/lib/qs.rb CHANGED
@@ -1,4 +1,3 @@
1
- require 'ns-options'
2
1
  require 'qs/version'
3
2
  require 'qs/client'
4
3
  require 'qs/daemon'
@@ -15,28 +14,24 @@ module Qs
15
14
  end
16
15
 
17
16
  def self.init
18
- self.config.redis.url ||= RedisUrl.new(
19
- self.config.redis.ip,
20
- self.config.redis.port,
21
- self.config.redis.db
22
- )
17
+ self.config.validate!
23
18
 
24
19
  @dispatcher_queue ||= DispatcherQueue.new({
25
20
  :queue_class => self.config.dispatcher_queue_class,
26
- :queue_name => self.config.dispatcher.queue_name,
27
- :job_name => self.config.dispatcher.job_name,
28
- :job_handler_class_name => self.config.dispatcher.job_handler_class_name
21
+ :queue_name => self.config.dispatcher_queue_name,
22
+ :job_name => self.config.dispatcher_job_name,
23
+ :job_handler_class_name => self.config.dispatcher_job_handler_class_name
29
24
  })
30
25
 
31
26
  @encoder ||= self.config.encoder
32
27
  @decoder ||= self.config.decoder
33
- @client ||= Client.new(self.redis_config)
28
+ @client ||= Client.new(self.redis_connect_hash)
34
29
  @redis ||= @client.redis
35
30
  true
36
31
  end
37
32
 
38
33
  def self.reset!
39
- self.config.reset
34
+ @config = nil
40
35
  @dispatcher_queue = nil
41
36
  @encoder = nil
42
37
  @decoder = nil
@@ -46,19 +41,19 @@ module Qs
46
41
  end
47
42
 
48
43
  def self.enqueue(queue, job_name, params = nil)
49
- @client.enqueue(queue, job_name, params)
44
+ self.client.enqueue(queue, job_name, params)
50
45
  end
51
46
 
52
47
  def self.publish(channel, name, params = nil)
53
- @client.publish(channel, name, params)
48
+ self.client.publish(channel, name, params)
54
49
  end
55
50
 
56
51
  def self.publish_as(publisher, channel, name, params = nil)
57
- @client.publish_as(publisher, channel, name, params)
52
+ self.client.publish_as(publisher, channel, name, params)
58
53
  end
59
54
 
60
55
  def self.push(queue_name, payload)
61
- @client.push(queue_name, payload)
56
+ self.client.push(queue_name, payload)
62
57
  end
63
58
 
64
59
  def self.encode(payload)
@@ -82,15 +77,15 @@ module Qs
82
77
  end
83
78
 
84
79
  def self.client
85
- @client
80
+ @client || NullClient.new
86
81
  end
87
82
 
88
83
  def self.redis
89
84
  @redis
90
85
  end
91
86
 
92
- def self.redis_config
93
- self.config.redis.to_hash
87
+ def self.redis_connect_hash
88
+ self.config.redis_connect_hash
94
89
  end
95
90
 
96
91
  def self.dispatcher_queue
@@ -98,7 +93,7 @@ module Qs
98
93
  end
99
94
 
100
95
  def self.dispatcher_job_name
101
- self.config.dispatcher.job_name
96
+ self.config.dispatcher_job_name
102
97
  end
103
98
 
104
99
  def self.event_publisher
@@ -110,46 +105,105 @@ module Qs
110
105
  end
111
106
 
112
107
  class Config
113
- include NsOptions::Proxy
114
108
 
115
- option :encoder, Proc, :default => proc{ |p| ::JSON.dump(p) }
116
- option :decoder, Proc, :default => proc{ |p| ::JSON.load(p) }
109
+ DEFAULT_ENCODER = proc{ |payload| ::JSON.dump(payload) }
110
+ DEFAULT_DECODER = proc{ |encoded_payload| ::JSON.load(encoded_payload) }
117
111
 
118
- option :timeout, Float
112
+ DEFAULT_DISPATCHER_QUEUE_CLASS = Queue
113
+ DEFAULT_DISPATCHER_QUEUE_NAME = 'dispatcher'.freeze
114
+ DEFAULT_DISPATCHER_JOB_NAME = 'run_dispatch_job'.freeze
115
+ DEFAULT_DISPATCHER_JOB_HANDLER_CLASS_NAME = DispatcherQueue::RunDispatchJob.to_s.freeze
119
116
 
120
- option :event_publisher, String
117
+ DEFAULT_REDIS_IP = '127.0.0.1'.freeze
118
+ DEFAULT_REDIS_PORT = 6379.freeze
119
+ DEFAULT_REDIS_DB = 0.freeze
120
+ DEFAULT_REDIS_NS = 'qs'.freeze
121
+ DEFAULT_REDIS_DRIVER = 'ruby'.freeze
122
+ DEFAULT_REDIS_TIMEOUT = 1.freeze
123
+ DEFAULT_REDIS_SIZE = 4.freeze
121
124
 
122
- namespace :dispatcher do
123
- option :queue_name, String, :default => 'dispatcher'
124
- option :job_name, String, :default => 'run_dispatch_job'
125
- option :job_handler_class_name, String, :default => DispatcherQueue::RunDispatchJob.to_s
126
- end
125
+ attr_accessor :encoder, :decoder, :timeout, :event_publisher
126
+ attr_accessor :dispatcher_queue_class, :dispatcher_queue_name
127
+ attr_accessor :dispatcher_job_name, :dispatcher_job_handler_class_name
128
+ attr_accessor :redis_ip, :redis_port, :redis_db, :redis_ns
129
+ attr_accessor :redis_driver, :redis_timeout, :redis_size, :redis_url
127
130
 
128
- namespace :redis do
129
- option :ip, :default => '127.0.0.1'
130
- option :port, :default => 6379
131
- option :db, :default => 0
131
+ def initialize
132
+ @encoder = DEFAULT_ENCODER
133
+ @decoder = DEFAULT_DECODER
134
+ @timeout = nil
135
+ @event_publisher = nil
136
+
137
+ @dispatcher_queue_class = DEFAULT_DISPATCHER_QUEUE_CLASS
138
+ @dispatcher_queue_name = DEFAULT_DISPATCHER_QUEUE_NAME
139
+ @dispatcher_job_name = DEFAULT_DISPATCHER_JOB_NAME
140
+ @dispatcher_job_handler_class_name = DEFAULT_DISPATCHER_JOB_HANDLER_CLASS_NAME
141
+
142
+ @redis_ip = DEFAULT_REDIS_IP
143
+ @redis_port = DEFAULT_REDIS_PORT
144
+ @redis_db = DEFAULT_REDIS_DB
145
+ @redis_ns = DEFAULT_REDIS_NS
146
+ @redis_driver = DEFAULT_REDIS_DRIVER
147
+ @redis_timeout = DEFAULT_REDIS_TIMEOUT
148
+ @redis_size = DEFAULT_REDIS_SIZE
149
+ @redis_url = nil
150
+
151
+ @valid = nil
152
+ end
132
153
 
133
- option :url
154
+ # the keys here should be compatible with HellaRedis connection configs
155
+ # https://github.com/redding/hella-redis#connection
156
+ def redis_connect_hash
157
+ { :ip => self.redis_ip,
158
+ :port => self.redis_port,
159
+ :db => self.redis_db,
160
+ :redis_ns => self.redis_ns,
161
+ :driver => self.redis_driver,
162
+ :timeout => self.redis_timeout,
163
+ :size => self.redis_size,
164
+ :url => self.redis_url
165
+ }
166
+ end
134
167
 
135
- option :redis_ns, String, :default => 'qs'
136
- option :driver, String, :default => 'ruby'
137
- option :timeout, Integer, :default => 1
138
- option :size, Integer, :default => 4
168
+ def valid?
169
+ !!@valid
139
170
  end
140
171
 
141
- attr_accessor :dispatcher_queue_class
172
+ def validate!
173
+ return @valid if !@valid.nil? # only need to run this once per config
142
174
 
143
- def initialize
144
- self.dispatcher_queue_class = Queue
175
+ # set the `redis_url`
176
+ self.redis_url ||= RedisUrl.new(self.redis_ip, self.redis_port, self.redis_db)
177
+
178
+ @valid = true
145
179
  end
180
+
146
181
  end
147
182
 
148
183
  module RedisUrl
184
+
149
185
  def self.new(ip, port, db)
150
186
  return if ip.to_s.empty? || port.to_s.empty? || db.to_s.empty?
151
187
  "redis://#{ip}:#{port}/#{db}"
152
188
  end
189
+
153
190
  end
154
191
 
192
+ class NullClient
193
+ def initialize
194
+ @error = UninitializedError.new("Qs hasn't been initialized")
195
+ end
196
+
197
+ def enqueue(*args); raise @error; end
198
+ def publish(*args); raise @error; end
199
+ def publish_as(*args); raise @error; end
200
+ def push(*args); raise @error; end
201
+ def sync_subscriptions(*args); raise @error; end
202
+ def clear_subscriptions(*args); raise @error; end
203
+ def event_subscribers(*args); raise @error; end
204
+ end
205
+
206
+ UninitializedError = Class.new(RuntimeError)
207
+ TimeoutError = Class.new(RuntimeError)
208
+
155
209
  end
@@ -25,10 +25,10 @@ module Qs
25
25
 
26
26
  module InstanceMethods
27
27
 
28
- attr_reader :redis_config, :redis
28
+ attr_reader :redis_connect_hash, :redis
29
29
 
30
- def initialize(redis_config)
31
- @redis_config = redis_config
30
+ def initialize(redis_connect_hash)
31
+ @redis_connect_hash = redis_connect_hash
32
32
  end
33
33
 
34
34
  def enqueue(queue, job_name, job_params = nil)
@@ -119,7 +119,7 @@ module Qs
119
119
 
120
120
  def initialize(*args)
121
121
  super
122
- @redis = HellaRedis::Connection.new(self.redis_config)
122
+ @redis = HellaRedis::Connection.new(self.redis_connect_hash)
123
123
  end
124
124
 
125
125
  def push(queue_name, payload_hash)
@@ -145,7 +145,7 @@ module Qs
145
145
  def initialize(*args)
146
146
  super
147
147
  require 'hella-redis/connection_spy'
148
- @redis = HellaRedis::ConnectionSpy.new(self.redis_config)
148
+ @redis = HellaRedis::ConnectionSpy.new(self.redis_connect_hash)
149
149
  @pushed_items = []
150
150
  end
151
151
 
@@ -35,7 +35,7 @@ module Qs
35
35
  full_path_with_sanford
36
36
  end
37
37
 
38
- # This evaluates the file and creates a proc using it's contents. This is
38
+ # This evaluates the file and creates a proc using its contents. This is
39
39
  # a trick borrowed from Rack. It is essentially converting a file into a
40
40
  # proc and then instance eval'ing it. This has a couple benefits:
41
41
  # * The obvious benefit is the file is evaluated in the context of this
@@ -1,8 +1,5 @@
1
1
  require 'dat-worker-pool'
2
2
  require 'much-plugin'
3
- require 'ns-options'
4
- require 'pathname'
5
- require 'system_timer'
6
3
  require 'thread'
7
4
  require 'qs'
8
5
  require 'qs/client'
@@ -16,9 +13,8 @@ module Qs
16
13
  module Daemon
17
14
  include MuchPlugin
18
15
 
19
- InvalidError = Class.new(ArgumentError)
20
-
21
- SIGNAL = '.'.freeze
16
+ SIGNAL = '.'.freeze
17
+ FETCH_ERR_SLEEP_TIME = 1.0.freeze
22
18
 
23
19
  plugin_included do
24
20
  extend ClassMethods
@@ -27,28 +23,46 @@ module Qs
27
23
 
28
24
  module InstanceMethods
29
25
 
30
- attr_reader :daemon_data, :logger
31
- attr_reader :signals_redis_key, :queue_redis_keys
26
+ attr_reader :daemon_data, :signals_redis_key
32
27
 
33
- # set the size of the client to the num workers + 1, this ensures we have
34
- # 1 connection for fetching work from redis and at least 1 connection for
35
- # each worker to requeue its message when hard-shutdown
36
28
  def initialize
37
- self.class.configuration.validate!
29
+ config = self.class.config
30
+ begin
31
+ config.validate!
32
+ rescue InvalidError => exception
33
+ exception.set_backtrace(caller)
34
+ raise exception
35
+ end
38
36
  Qs.init
39
- @daemon_data = DaemonData.new(self.class.configuration.to_hash)
40
- @logger = @daemon_data.logger
41
37
 
42
- @client = QsClient.new(Qs.redis_config.merge({
43
- :timeout => 1,
44
- :size => self.daemon_data.num_workers + 1
45
- }))
46
- @queue_redis_keys = self.daemon_data.queue_redis_keys
38
+ @daemon_data = DaemonData.new({
39
+ :name => config.name,
40
+ :pid_file => config.pid_file,
41
+ :shutdown_timeout => config.shutdown_timeout,
42
+ :worker_class => config.worker_class,
43
+ :worker_params => config.worker_params,
44
+ :num_workers => config.num_workers,
45
+ :error_procs => config.error_procs,
46
+ :logger => config.logger,
47
+ :queues => config.queues,
48
+ :verbose_logging => config.verbose_logging,
49
+ :routes => config.routes
50
+ })
47
51
 
48
- @signals_redis_key = "signals:#{@daemon_data.name}-" \
52
+ @signals_redis_key = "signals:#{self.daemon_data.name}-" \
49
53
  "#{Socket.gethostname}-#{::Process.pid}"
50
54
 
55
+ @thread = nil
51
56
  @worker_available = WorkerAvailable.new
57
+ @state = State.new(:stop)
58
+
59
+ # set the size of the client to the num workers + 1, this ensures we
60
+ # have 1 connection for fetching work from redis and at least 1
61
+ # connection for each worker to requeue its message when hard-shutdown
62
+ @client = QsClient.new(Qs.redis_connect_hash.merge({
63
+ :timeout => 1,
64
+ :size => self.daemon_data.num_workers + 1
65
+ }))
52
66
 
53
67
  @worker_pool = DatWorkerPool.new(self.daemon_data.worker_class, {
54
68
  :num_workers => self.daemon_data.num_workers,
@@ -57,15 +71,9 @@ module Qs
57
71
  :qs_daemon_data => self.daemon_data,
58
72
  :qs_client => @client,
59
73
  :qs_worker_available => @worker_available,
60
- :qs_logger => @logger
74
+ :qs_logger => self.logger
61
75
  })
62
76
  })
63
-
64
- @thread = nil
65
- @state = State.new(:stop)
66
- rescue InvalidError => exception
67
- exception.set_backtrace(caller)
68
- raise exception
69
77
  end
70
78
 
71
79
  def name
@@ -80,14 +88,22 @@ module Qs
80
88
  @daemon_data.pid_file
81
89
  end
82
90
 
91
+ def logger
92
+ @daemon_data.logger
93
+ end
94
+
95
+ def queue_redis_keys
96
+ @daemon_data.queue_redis_keys
97
+ end
98
+
83
99
  def running?
84
100
  !!(@thread && @thread.alive?)
85
101
  end
86
102
 
87
- # ping to check that it can communicate with redis before running, this is
88
- # friendlier than starting and continously erroring because it can't
89
- # dequeue
90
103
  def start
104
+ # ping to check that it can communicate with redis before running,
105
+ # this is friendlier than starting and continously erroring because
106
+ # it can't dequeue
91
107
  @client.ping
92
108
  @state.set :run
93
109
  @thread ||= Thread.new{ work_loop }
@@ -121,25 +137,25 @@ module Qs
121
137
  teardown
122
138
  end
123
139
 
124
- # clear any signals that are already on the signals list in redis
125
140
  def setup
126
- @client.clear(self.signals_redis_key)
141
+ # clear any signals that are already on the signals list in redis
142
+ @client.clear(self.signals_redis_key)
127
143
  @worker_pool.start
128
144
  end
129
145
 
130
- # shuffle the queue redis keys to avoid queue starvation, redis will pull
131
- # messages off queues in the order they are passed to the command, by
132
- # shuffling we ensure they are randomly ordered so every queue should get
133
- # a chance; use 0 for the brpop timeout which means block indefinitely;
134
- # rescue runtime errors so the daemon thread doesn't fail if redis is
135
- # temporarily down, sleep for a second to keep the thread from thrashing
136
- # by repeatedly erroring if redis is down
137
146
  def fetch_messages
138
147
  if !@worker_pool.worker_available? && @state.run?
139
148
  @worker_available.wait
140
149
  end
141
150
  return unless @worker_pool.worker_available? && @state.run?
142
151
 
152
+ # shuffle the queue redis keys to avoid queue starvation, redis will
153
+ # pull messages off queues in the order they are passed to the command,
154
+ # by shuffling we ensure they are randomly ordered so every queue
155
+ # should get a chance; use 0 for the brpop timeout which means block
156
+ # indefinitely; rescue runtime errors so the daemon thread doesn't fail
157
+ # if redis is temporarily down, sleep for a second to keep the thread
158
+ # from thrashing by repeatedly erroring if redis is down
143
159
  begin
144
160
  args = [self.signals_redis_key, self.queue_redis_keys.shuffle, 0].flatten
145
161
  redis_key, encoded_payload = @client.block_dequeue(*args)
@@ -150,7 +166,7 @@ module Qs
150
166
  log "Error occurred while dequeueing", :error
151
167
  log "#{exception.class}: #{exception.message}", :error
152
168
  (exception.backtrace || []).each{ |l| log(l, :error) }
153
- sleep 1
169
+ sleep FETCH_ERR_SLEEP_TIME
154
170
  end
155
171
  end
156
172
 
@@ -183,86 +199,102 @@ module Qs
183
199
 
184
200
  module ClassMethods
185
201
 
186
- def configuration
187
- @configuration ||= Configuration.new
202
+ def config
203
+ @config ||= Config.new
204
+ end
205
+
206
+ def name(value = nil)
207
+ self.config.name = value if !value.nil?
208
+ self.config.name
188
209
  end
189
210
 
190
- def name(*args)
191
- self.configuration.name(*args)
211
+ def pid_file(value = nil)
212
+ self.config.pid_file = value if !value.nil?
213
+ self.config.pid_file
192
214
  end
193
215
 
194
- def pid_file(*args)
195
- self.configuration.pid_file(*args)
216
+ def shutdown_timeout(value = nil)
217
+ self.config.shutdown_timeout = value if !value.nil?
218
+ self.config.shutdown_timeout
196
219
  end
197
220
 
198
- def worker_class(new_worker_class = nil)
199
- self.configuration.worker_class = new_worker_class if !new_worker_class.nil?
200
- self.configuration.worker_class
221
+ def worker_class(value = nil)
222
+ self.config.worker_class = value if !value.nil?
223
+ self.config.worker_class
201
224
  end
202
225
 
203
- def worker_params(new_worker_params = nil)
204
- self.configuration.worker_params = new_worker_params if !new_worker_params.nil?
205
- self.configuration.worker_params
226
+ def worker_params(value = nil)
227
+ self.config.worker_params = value if !value.nil?
228
+ self.config.worker_params
206
229
  end
207
230
 
208
- def num_workers(*args)
209
- self.configuration.num_workers(*args)
231
+ def num_workers(new_num_workers = nil)
232
+ self.config.num_workers = new_num_workers if new_num_workers
233
+ self.config.num_workers
210
234
  end
211
235
  alias :workers :num_workers
212
236
 
213
- def verbose_logging(*args)
214
- self.configuration.verbose_logging(*args)
237
+ def init(&block)
238
+ self.config.init_procs << block
215
239
  end
216
240
 
217
- def logger(*args)
218
- self.configuration.logger(*args)
241
+ def init_procs
242
+ self.config.init_procs
219
243
  end
220
244
 
221
- def shutdown_timeout(*args)
222
- self.configuration.shutdown_timeout(*args)
245
+ def error(&block)
246
+ self.config.error_procs << block
223
247
  end
224
248
 
225
- def init(&block)
226
- self.configuration.init_procs << block
249
+ def error_procs
250
+ self.config.error_procs
227
251
  end
228
252
 
229
- def error(&block)
230
- self.configuration.error_procs << block
253
+ def logger(value = nil)
254
+ self.config.logger = value if !value.nil?
255
+ self.config.logger
231
256
  end
232
257
 
233
- def queue(queue)
234
- self.configuration.queues << queue
258
+ def queue(value)
259
+ self.config.queues << value
235
260
  end
236
261
 
237
262
  def queues
238
- self.configuration.queues
263
+ self.config.queues
239
264
  end
240
265
 
241
- end
266
+ # flags
242
267
 
243
- class Configuration
244
- include NsOptions::Proxy
268
+ def verbose_logging(value = nil)
269
+ self.config.verbose_logging = value if !value.nil?
270
+ self.config.verbose_logging
271
+ end
245
272
 
246
- option :name, String, :required => true
247
- option :pid_file, Pathname
273
+ end
248
274
 
249
- option :num_workers, Integer, :default => 4
275
+ class Config
250
276
 
251
- option :verbose_logging, :default => true
252
- option :logger, :default => proc{ Qs::NullLogger.new }
277
+ DEFAULT_NUM_WORKERS = 4.freeze
253
278
 
254
- option :shutdown_timeout
279
+ attr_accessor :name, :pid_file, :shutdown_timeout
280
+ attr_accessor :worker_class, :worker_params, :num_workers
281
+ attr_accessor :init_procs, :error_procs, :logger, :queues
282
+ attr_accessor :verbose_logging
255
283
 
256
- attr_accessor :init_procs, :error_procs
257
- attr_accessor :worker_class, :worker_params
258
- attr_accessor :queues
284
+ def initialize
285
+ @name = nil
286
+ @pid_file = nil
287
+ @shutdown_timeout = nil
288
+ @worker_class = DefaultWorker
289
+ @worker_params = nil
290
+ @num_workers = DEFAULT_NUM_WORKERS
291
+ @init_procs = []
292
+ @error_procs = []
293
+ @logger = Qs::NullLogger.new
294
+ @queues = []
295
+
296
+ @verbose_logging = true
259
297
 
260
- def initialize(values = nil)
261
- super(values)
262
- @init_procs, @error_procs = [], []
263
- @worker_class = DefaultWorker
264
- @worker_params = nil
265
- @queues = []
266
298
  @valid = nil
267
299
  end
268
300
 
@@ -270,35 +302,37 @@ module Qs
270
302
  @queues.map(&:routes).flatten
271
303
  end
272
304
 
273
- def to_hash
274
- super.merge({
275
- :error_procs => self.error_procs,
276
- :worker_class => self.worker_class,
277
- :worker_params => self.worker_params,
278
- :routes => self.routes,
279
- :queue_redis_keys => self.queues.map(&:redis_key)
280
- })
281
- end
282
-
283
305
  def valid?
284
306
  !!@valid
285
307
  end
286
308
 
309
+ # for the config to be considered "valid", a few things need to happen.
310
+ # The key here is that this only needs to be done _once_ for each config.
311
+
287
312
  def validate!
288
- return @valid if !@valid.nil?
313
+ return @valid if !@valid.nil? # only need to run this once per config
314
+
315
+ # ensure all user and plugin configs/settings are applied
289
316
  self.init_procs.each(&:call)
290
- if self.queues.empty? || !self.required_set?
291
- raise InvalidError, "a name and queue must be configured"
317
+ if self.queues.empty? || self.name.nil?
318
+ raise InvalidError, "a name and at least 1 queue must be configured"
292
319
  end
320
+
321
+ # validate the worker class
293
322
  if !self.worker_class.kind_of?(Class) || !self.worker_class.include?(Qs::Worker)
294
323
  raise InvalidError, "worker class must include `#{Qs::Worker}`"
295
324
  end
325
+
326
+ # validate the routes
296
327
  self.routes.each(&:validate!)
297
- @valid = true
328
+
329
+ @valid = true # if it made it this far, it's valid!
298
330
  end
331
+
299
332
  end
300
333
 
301
334
  DefaultWorker = Class.new{ include Qs::Worker }
335
+ InvalidError = Class.new(ArgumentError)
302
336
 
303
337
  class WorkerAvailable
304
338
  def initialize