mongo-resque 1.17.1

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.
Files changed (46) hide show
  1. data/HISTORY.md +278 -0
  2. data/LICENSE +20 -0
  3. data/README.markdown +76 -0
  4. data/bin/resque +67 -0
  5. data/bin/resque-web +23 -0
  6. data/docs/HOOKS.md +132 -0
  7. data/docs/PLUGINS.md +93 -0
  8. data/lib/resque/errors.rb +13 -0
  9. data/lib/resque/failure/base.rb +64 -0
  10. data/lib/resque/failure/hoptoad.rb +133 -0
  11. data/lib/resque/failure/mongo.rb +45 -0
  12. data/lib/resque/failure/multiple.rb +54 -0
  13. data/lib/resque/failure/redis.rb +46 -0
  14. data/lib/resque/failure.rb +70 -0
  15. data/lib/resque/helpers.rb +75 -0
  16. data/lib/resque/job.rb +215 -0
  17. data/lib/resque/plugin.rb +51 -0
  18. data/lib/resque/server/public/favicon.ico +0 -0
  19. data/lib/resque/server/public/idle.png +0 -0
  20. data/lib/resque/server/public/jquery-1.3.2.min.js +19 -0
  21. data/lib/resque/server/public/jquery.relatize_date.js +95 -0
  22. data/lib/resque/server/public/poll.png +0 -0
  23. data/lib/resque/server/public/ranger.js +73 -0
  24. data/lib/resque/server/public/reset.css +48 -0
  25. data/lib/resque/server/public/style.css +92 -0
  26. data/lib/resque/server/public/working.png +0 -0
  27. data/lib/resque/server/test_helper.rb +19 -0
  28. data/lib/resque/server/views/error.erb +1 -0
  29. data/lib/resque/server/views/failed.erb +65 -0
  30. data/lib/resque/server/views/key_sets.erb +19 -0
  31. data/lib/resque/server/views/key_string.erb +11 -0
  32. data/lib/resque/server/views/layout.erb +42 -0
  33. data/lib/resque/server/views/next_more.erb +10 -0
  34. data/lib/resque/server/views/overview.erb +4 -0
  35. data/lib/resque/server/views/queues.erb +69 -0
  36. data/lib/resque/server/views/stats.erb +73 -0
  37. data/lib/resque/server/views/workers.erb +109 -0
  38. data/lib/resque/server/views/working.erb +72 -0
  39. data/lib/resque/server.rb +268 -0
  40. data/lib/resque/stat.rb +54 -0
  41. data/lib/resque/tasks.rb +42 -0
  42. data/lib/resque/version.rb +3 -0
  43. data/lib/resque/worker.rb +497 -0
  44. data/lib/resque.rb +417 -0
  45. data/lib/tasks/resque.rake +2 -0
  46. metadata +148 -0
data/lib/resque.rb ADDED
@@ -0,0 +1,417 @@
1
+
2
+ begin
3
+ require 'yajl'
4
+ rescue LoadError
5
+ require 'json'
6
+ end
7
+
8
+ require 'mongo'
9
+
10
+ require 'resque/version'
11
+
12
+ require 'resque/errors'
13
+
14
+ require 'resque/failure'
15
+ require 'resque/failure/base'
16
+
17
+ require 'resque/helpers'
18
+ require 'resque/stat'
19
+ require 'resque/job'
20
+ require 'resque/worker'
21
+ require 'resque/plugin'
22
+
23
+ module Resque
24
+ include Helpers
25
+ extend self
26
+ @delay_allowed = []
27
+
28
+ # Set the queue database. Expects a Mongo::DB object.
29
+ def mongo=(database)
30
+ if database.is_a?(Mongo::DB)
31
+ @mongo = database
32
+ initialize_mongo
33
+ else
34
+ raise ArgumentError, "Resque.mongo= expects a Mongo::DB database, not a #{database.class}."
35
+ end
36
+ end
37
+
38
+ # Returns the current Mongo::DB. If none has been created, it will
39
+ # create a new one called 'resque'.
40
+ def mongo
41
+ return @mongo if @mongo
42
+ self.mongo = Mongo::Connection.new.db("resque")
43
+ @mongo
44
+ end
45
+
46
+ def initialize_mongo
47
+ mongo_workers.create_index :worker
48
+ mongo_stats.create_index :stat
49
+ delay_allowed = mongo_stats.find_one({ :stat => 'Delayable Queues'}, { :fields => ['value']})
50
+ @delay_allowed = delay_allowed['value'].map{ |queue| queue.to_sym} if delay_allowed
51
+ end
52
+
53
+ def mongo_workers
54
+ mongo['resque.workers']
55
+ end
56
+
57
+ def mongo_stats
58
+ mongo['resque.metrics']
59
+ end
60
+
61
+ def mongo_failures
62
+ mongo['resque.failures']
63
+ end
64
+
65
+ # The `before_first_fork` hook will be run in the **parent** process
66
+ # only once, before forking to run the first job. Be careful- any
67
+ # changes you make will be permanent for the lifespan of the
68
+ # worker.
69
+ #
70
+ # Call with a block to set the hook.
71
+ # Call with no arguments to return the hook.
72
+ def before_first_fork(&block)
73
+ block ? (@before_first_fork = block) : @before_first_fork
74
+ end
75
+
76
+ # Set a proc that will be called in the parent process before the
77
+ # worker forks for the first time.
78
+ def before_first_fork=(before_first_fork)
79
+ @before_first_fork = before_first_fork
80
+ end
81
+
82
+ # The `before_fork` hook will be run in the **parent** process
83
+ # before every job, so be careful- any changes you make will be
84
+ # permanent for the lifespan of the worker.
85
+ #
86
+ # Call with a block to set the hook.
87
+ # Call with no arguments to return the hook.
88
+ def before_fork(&block)
89
+ block ? (@before_fork = block) : @before_fork
90
+ end
91
+
92
+ # Set the before_fork proc.
93
+ def before_fork=(before_fork)
94
+ @before_fork = before_fork
95
+ end
96
+
97
+ # The `after_fork` hook will be run in the child process and is passed
98
+ # the current job. Any changes you make, therefore, will only live as
99
+ # long as the job currently being processed.
100
+ #
101
+ # Call with a block to set the hook.
102
+ # Call with no arguments to return the hook.
103
+ def after_fork(&block)
104
+ block ? (@after_fork = block) : @after_fork
105
+ end
106
+
107
+ # Set the after_fork proc.
108
+ def after_fork=(after_fork)
109
+ @after_fork = after_fork
110
+ end
111
+
112
+ def to_s
113
+ connection_info = mongo.connection.primary_pool
114
+ "Resque Client connected to #{connection_info.host}:#{connection_info.port}/#{mongo.name}"
115
+ end
116
+
117
+ def allows_delayed_jobs(klass)
118
+ klass.instance_variable_get(:@delayed_jobs) ||
119
+ (klass.respond_to?(:delayed_jobs) and klass.delayed_jobs)
120
+ end
121
+
122
+ def queue_allows_delayed(queue)
123
+ queue = namespace_queue(queue)
124
+ @delay_allowed.include?(queue.to_sym) || @delay_allowed.include?(queue.to_s)
125
+ end
126
+
127
+ def enable_delay(queue)
128
+ queue = namespace_queue(queue)
129
+ unless queue_allows_delayed queue
130
+ @delay_allowed << queue
131
+ mongo_stats.update({:stat => 'Delayable Queues'}, { '$addToSet' => { 'value' => queue}}, { :upsert => true})
132
+ end
133
+ end
134
+
135
+ # If 'inline' is true Resque will call #perform method inline
136
+ # without queuing it into Redis and without any Resque callbacks.
137
+ # The 'inline' is false Resque jobs will be put in queue regularly.
138
+ def inline?
139
+ @inline
140
+ end
141
+ alias_method :inline, :inline?
142
+
143
+ def inline=(inline)
144
+ @inline = inline
145
+ end
146
+
147
+ #
148
+ # queue manipulation
149
+ #
150
+
151
+ # Pushes a job onto a queue. Queue name should be a string and the
152
+ # item should be any JSON-able Ruby object.
153
+ #
154
+ # Resque works generally expect the `item` to be a hash with the following
155
+ # keys:
156
+ #
157
+ # class - The String name of the job to run.
158
+ # args - An Array of arguments to pass the job. Usually passed
159
+ # via `class.to_class.perform(*args)`.
160
+ #
161
+ # Example
162
+ #
163
+ # Resque.push('archive', :class => 'Archive', :args => [ 35, 'tar' ])
164
+ #
165
+ # Returns nothing
166
+ def push(queue, item)
167
+ queue = namespace_queue(queue)
168
+ item[:resque_enqueue_timestamp] = Time.now
169
+ mongo[queue] << item
170
+ end
171
+
172
+ # Pops a job off a queue. Queue name should be a string.
173
+ #
174
+ # Returns a Ruby object.
175
+ def pop(queue)
176
+ queue = namespace_queue(queue)
177
+ query = {}
178
+ if queue_allows_delayed queue
179
+ query['delay_until'] = { '$lt' => Time.now }
180
+ end
181
+ #sorting will result in significant performance penalties for large queues, you have been warned.
182
+ item = mongo[queue].find_and_modify(:query => query, :remove => true, :sort => [[:_id, :asc]] )
183
+ rescue Mongo::OperationFailure => e
184
+ return nil if e.message =~ /No matching object/
185
+ raise e
186
+ end
187
+
188
+ # Returns an integer representing the size of a queue.
189
+ # Queue name should be a string.
190
+ def size(queue)
191
+ queue = namespace_queue(queue)
192
+ mongo[queue].count
193
+ end
194
+
195
+ def delayed_size(queue)
196
+ queue = namespace_queue(queue)
197
+ if queue_allows_delayed queue
198
+ mongo[queue].find({'delay_until' => { '$gt' => Time.now }}).count
199
+ else
200
+ mongo[queue].count
201
+ end
202
+ end
203
+
204
+ def ready_size(queue)
205
+ queue = namespace_queue(queue)
206
+ if queue_allows_delayed queue
207
+ mongo[queue].find({'delay_until' => { '$lt' => Time.now }}).count
208
+ else
209
+ mongo[queue].count
210
+ end
211
+ end
212
+
213
+
214
+ # Returns an array of items currently queued. Queue name should be
215
+ # a string.
216
+ #
217
+ # start and count should be integer and can be used for pagination.
218
+ # start is the item to begin, count is how many items to return.
219
+ #
220
+ # To get the 3rd page of a 30 item, paginatied list one would use:
221
+ # Resque.peek('my_list', 59, 30)
222
+ def peek(queue, start = 0, count = 1, mode = :ready)
223
+ list_range(queue, start, count, mode)
224
+ end
225
+
226
+ # Does the dirty work of fetching a range of items from a Redis list
227
+ # and converting them into Ruby objects.
228
+ def list_range(key, start = 0, count = 1, mode = :ready)
229
+ query = { }
230
+ sort = []
231
+ if queue_allows_delayed(key)
232
+ if mode == :ready
233
+ query['delay_until'] = { '$not' => { '$gt' => Time.new}}
234
+ elsif mode == :delayed
235
+ query['delay_until'] = { '$gt' => Time.new}
236
+ elsif mode == :delayed_sorted
237
+ query['delay_until'] = { '$gt' => Time.new}
238
+ sort << ['delay_until', 1]
239
+ elsif mode == :all_sorted
240
+ query = {}
241
+ sort << ['delay_until', 1]
242
+ end
243
+ end
244
+ queue = namespace_queue(key)
245
+ items = mongo[queue].find(query, { :limit => count, :skip => start, :sort => sort}).to_a.map{ |i| i}
246
+ count > 1 ? items : items.first
247
+ end
248
+
249
+ # Returns an array of all known Resque queues as strings.
250
+ def queues
251
+ mongo.collection_names.
252
+ select { |name| name =~ /resque\.queues\./ }.
253
+ collect { |name| name.split(".")[2..-1].join('.') }
254
+ end
255
+
256
+ # Returns the mongo collection for a given queue
257
+ def collection_for_queue(queue)
258
+ queue = namespace_queue(queue)
259
+ mongo[queue]
260
+ end
261
+
262
+ # Given a queue name, completely deletes the queue.
263
+ def remove_queue(queue)
264
+ queue = namespace_queue(queue)
265
+ mongo[queue].drop
266
+ end
267
+
268
+ #
269
+ # job shortcuts
270
+ #
271
+
272
+ # This method can be used to conveniently add a job to a queue.
273
+ # It assumes the class you're passing it is a real Ruby class (not
274
+ # a string or reference) which either:
275
+ #
276
+ # a) has a @queue ivar set
277
+ # b) responds to `queue`
278
+ #
279
+ # If either of those conditions are met, it will use the value obtained
280
+ # from performing one of the above operations to determine the queue.
281
+ #
282
+ # If no queue can be inferred this method will raise a `Resque::NoQueueError`
283
+ #
284
+ # This method is considered part of the `stable` API.
285
+ def enqueue(klass, *args)
286
+ Job.create(queue_from_class(klass), klass, *args)
287
+
288
+ Plugin.after_enqueue_hooks(klass).each do |hook|
289
+ klass.send(hook, *args)
290
+ end
291
+ end
292
+
293
+ # This method can be used to conveniently remove a job from a queue.
294
+ # It assumes the class you're passing it is a real Ruby class (not
295
+ # a string or reference) which either:
296
+ #
297
+ # a) has a @queue ivar set
298
+ # b) responds to `queue`
299
+ #
300
+ # If either of those conditions are met, it will use the value obtained
301
+ # from performing one of the above operations to determine the queue.
302
+ #
303
+ # If no queue can be inferred this method will raise a `Resque::NoQueueError`
304
+ #
305
+ # If no args are given, this method will dequeue *all* jobs matching
306
+ # the provided class. See `Resque::Job.destroy` for more
307
+ # information.
308
+ #
309
+ # Returns the number of jobs destroyed.
310
+ #
311
+ # Example:
312
+ #
313
+ # # Removes all jobs of class `UpdateNetworkGraph`
314
+ # Resque.dequeue(GitHub::Jobs::UpdateNetworkGraph)
315
+ #
316
+ # # Removes all jobs of class `UpdateNetworkGraph` with matching args.
317
+ # Resque.dequeue(GitHub::Jobs::UpdateNetworkGraph, 'repo:135325')
318
+ #
319
+ # This method is considered part of the `stable` API.
320
+ def dequeue(klass, *args)
321
+ Job.destroy(queue_from_class(klass), klass, *args)
322
+ end
323
+
324
+ # Given a class, try to extrapolate an appropriate queue based on a
325
+ # class instance variable or `queue` method.
326
+ def queue_from_class(klass)
327
+ klass.instance_variable_get(:@queue) ||
328
+ (klass.respond_to?(:queue) and klass.queue)
329
+ end
330
+
331
+ # This method will return a `Resque::Job` object or a non-true value
332
+ # depending on whether a job can be obtained. You should pass it the
333
+ # precise name of a queue: case matters.
334
+ #
335
+ # This method is considered part of the `stable` API.
336
+ def reserve(queue)
337
+ Job.reserve(queue)
338
+ end
339
+
340
+ # Validates if the given klass could be a valid Resque job
341
+ #
342
+ # If no queue can be inferred this method will raise a `Resque::NoQueueError`
343
+ #
344
+ # If given klass is nil this method will raise a `Resque::NoClassError`
345
+ def validate(klass, queue = nil)
346
+ queue ||= queue_from_class(klass)
347
+
348
+ if !queue
349
+ raise NoQueueError.new("Jobs must be placed onto a queue.")
350
+ end
351
+
352
+ if klass.to_s.empty?
353
+ raise NoClassError.new("Jobs must be given a class.")
354
+ end
355
+ end
356
+
357
+
358
+ #
359
+ # worker shortcuts
360
+ #
361
+
362
+ # A shortcut to Worker.all
363
+ def workers
364
+ Worker.all
365
+ end
366
+
367
+ # A shortcut to Worker.working
368
+ def working
369
+ Worker.working
370
+ end
371
+
372
+ # A shortcut to unregister_worker
373
+ # useful for command line tool
374
+ def remove_worker(worker_id)
375
+ worker = Resque::Worker.find(worker_id)
376
+ worker.unregister_worker
377
+ end
378
+
379
+ #
380
+ # stats
381
+ #
382
+
383
+ # Returns a hash, similar to redis-rb's #info, of interesting stats.
384
+ def info
385
+ return {
386
+ :pending => queues.inject(0) { |m,k| m + size(k) },
387
+ :processed => Stat[:processed],
388
+ :queues => queues.size,
389
+ :workers => workers.size.to_i,
390
+ :working => working.count,
391
+ :failed => Stat[:failed],
392
+ :servers => to_s,
393
+ :environment => ENV['RAILS_ENV'] || ENV['RACK_ENV'] || 'development'
394
+ }
395
+ end
396
+
397
+ # Returns an array of all known Resque keys in Redis. Redis' KEYS operation
398
+ # is O(N) for the keyspace, so be careful - this can be slow for big databases.
399
+ def keys
400
+ names = mongo.collection_names
401
+ end
402
+
403
+ def drop
404
+ mongo.collections.each{ |collection| collection.drop unless collection.name =~ /^system./ }
405
+ @mongo = nil
406
+ end
407
+
408
+ private
409
+ def namespace_queue(queue)
410
+ queue = queue.to_s
411
+ if queue.start_with?('resque.queues.')
412
+ queue
413
+ else
414
+ "resque.queues.#{queue}"
415
+ end
416
+ end
417
+ end
@@ -0,0 +1,2 @@
1
+ $LOAD_PATH.unshift File.expand_path("../..", __FILE__)
2
+ require 'resque/tasks'
metadata ADDED
@@ -0,0 +1,148 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: mongo-resque
3
+ version: !ruby/object:Gem::Version
4
+ prerelease:
5
+ version: 1.17.1
6
+ platform: ruby
7
+ authors:
8
+ - David Backeus
9
+ autorequire:
10
+ bindir: bin
11
+ cert_chain: []
12
+
13
+ date: 2011-07-19 00:00:00 Z
14
+ dependencies:
15
+ - !ruby/object:Gem::Dependency
16
+ name: vegas
17
+ prerelease: false
18
+ requirement: &id001 !ruby/object:Gem::Requirement
19
+ none: false
20
+ requirements:
21
+ - - ~>
22
+ - !ruby/object:Gem::Version
23
+ version: 0.1.2
24
+ type: :runtime
25
+ version_requirements: *id001
26
+ - !ruby/object:Gem::Dependency
27
+ name: sinatra
28
+ prerelease: false
29
+ requirement: &id002 !ruby/object:Gem::Requirement
30
+ none: false
31
+ requirements:
32
+ - - ">="
33
+ - !ruby/object:Gem::Version
34
+ version: 0.9.2
35
+ type: :runtime
36
+ version_requirements: *id002
37
+ - !ruby/object:Gem::Dependency
38
+ name: json
39
+ prerelease: false
40
+ requirement: &id003 !ruby/object:Gem::Requirement
41
+ none: false
42
+ requirements:
43
+ - - ">="
44
+ - !ruby/object:Gem::Version
45
+ version: 1.4.6
46
+ - - <
47
+ - !ruby/object:Gem::Version
48
+ version: 1.6.0
49
+ type: :runtime
50
+ version_requirements: *id003
51
+ - !ruby/object:Gem::Dependency
52
+ name: mongo
53
+ prerelease: false
54
+ requirement: &id004 !ruby/object:Gem::Requirement
55
+ none: false
56
+ requirements:
57
+ - - ~>
58
+ - !ruby/object:Gem::Version
59
+ version: 1.3.0
60
+ type: :runtime
61
+ version_requirements: *id004
62
+ description: " Resque is a Redis-backed Ruby library for creating background jobs,\n placing those jobs on multiple queues, and processing them later.\n\n Mongo-Resque is the same thing, but for mongo. It would not exist\n without the work of defunkt and ctrochalakis on github.\n"
63
+ email:
64
+ - david@streamio.se
65
+ executables:
66
+ - resque
67
+ - resque-web
68
+ extensions: []
69
+
70
+ extra_rdoc_files:
71
+ - LICENSE
72
+ - README.markdown
73
+ files:
74
+ - lib/resque/errors.rb
75
+ - lib/resque/failure/base.rb
76
+ - lib/resque/failure/hoptoad.rb
77
+ - lib/resque/failure/mongo.rb
78
+ - lib/resque/failure/multiple.rb
79
+ - lib/resque/failure/redis.rb
80
+ - lib/resque/failure.rb
81
+ - lib/resque/helpers.rb
82
+ - lib/resque/job.rb
83
+ - lib/resque/plugin.rb
84
+ - lib/resque/server/public/favicon.ico
85
+ - lib/resque/server/public/idle.png
86
+ - lib/resque/server/public/jquery-1.3.2.min.js
87
+ - lib/resque/server/public/jquery.relatize_date.js
88
+ - lib/resque/server/public/poll.png
89
+ - lib/resque/server/public/ranger.js
90
+ - lib/resque/server/public/reset.css
91
+ - lib/resque/server/public/style.css
92
+ - lib/resque/server/public/working.png
93
+ - lib/resque/server/test_helper.rb
94
+ - lib/resque/server/views/error.erb
95
+ - lib/resque/server/views/failed.erb
96
+ - lib/resque/server/views/key_sets.erb
97
+ - lib/resque/server/views/key_string.erb
98
+ - lib/resque/server/views/layout.erb
99
+ - lib/resque/server/views/next_more.erb
100
+ - lib/resque/server/views/overview.erb
101
+ - lib/resque/server/views/queues.erb
102
+ - lib/resque/server/views/stats.erb
103
+ - lib/resque/server/views/workers.erb
104
+ - lib/resque/server/views/working.erb
105
+ - lib/resque/server.rb
106
+ - lib/resque/stat.rb
107
+ - lib/resque/tasks.rb
108
+ - lib/resque/version.rb
109
+ - lib/resque/worker.rb
110
+ - lib/resque.rb
111
+ - lib/tasks/resque.rake
112
+ - bin/resque
113
+ - bin/resque-web
114
+ - docs/HOOKS.md
115
+ - docs/PLUGINS.md
116
+ - README.markdown
117
+ - LICENSE
118
+ - HISTORY.md
119
+ homepage: http://github.com/dbackeus/mongo-resque
120
+ licenses: []
121
+
122
+ post_install_message:
123
+ rdoc_options: []
124
+
125
+ require_paths:
126
+ - lib
127
+ required_ruby_version: !ruby/object:Gem::Requirement
128
+ none: false
129
+ requirements:
130
+ - - ">="
131
+ - !ruby/object:Gem::Version
132
+ version: "0"
133
+ required_rubygems_version: !ruby/object:Gem::Requirement
134
+ none: false
135
+ requirements:
136
+ - - ">="
137
+ - !ruby/object:Gem::Version
138
+ version: "0"
139
+ requirements: []
140
+
141
+ rubyforge_project:
142
+ rubygems_version: 1.7.2
143
+ signing_key:
144
+ specification_version: 3
145
+ summary: Mongo-Resque is a mongo-backed queueing system
146
+ test_files: []
147
+
148
+ has_rdoc: