qs 0.6.1 → 0.7.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
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