cloudist 0.1.2 → 0.2.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.
data/Gemfile CHANGED
@@ -8,7 +8,7 @@ gem "activesupport", "~> 3.0.3"
8
8
  # Include everything needed to run rake, tests, features, etc.
9
9
  group :development do
10
10
  gem "rspec", "~> 2.3.0"
11
- gem "moqueue", :git => "git://github.com/customink/moqueue.git"
11
+ gem "moqueue", :git => "git://github.com/ivanvanderbyl/moqueue.git"
12
12
  gem "mocha"
13
13
  gem "bundler", "~> 1.0.0"
14
14
  gem "jeweler", "~> 1.5.2"
data/Gemfile.lock CHANGED
@@ -1,6 +1,6 @@
1
1
  GIT
2
- remote: git://github.com/customink/moqueue.git
3
- revision: 091a8f57e5c79b0b25e152b4d5230e4031797d62
2
+ remote: git://github.com/ivanvanderbyl/moqueue.git
3
+ revision: cf2108e3bee7730a6a57e0a95a4c17082c2b2603
4
4
  specs:
5
5
  moqueue (0.1.4)
6
6
  amqp
data/VERSION CHANGED
@@ -1 +1 @@
1
- 0.1.2
1
+ 0.2.0
data/cloudist.gemspec CHANGED
@@ -5,11 +5,11 @@
5
5
 
6
6
  Gem::Specification.new do |s|
7
7
  s.name = %q{cloudist}
8
- s.version = "0.1.2"
8
+ s.version = "0.2.0"
9
9
 
10
10
  s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version=
11
11
  s.authors = ["Ivan Vanderbyl"]
12
- s.date = %q{2011-01-18}
12
+ s.date = %q{2011-01-20}
13
13
  s.description = %q{Cloudist is a simple, highly scalable job queue for Ruby applications, it can run within Rails, DaemonKit or your own custom application. Refer to github page for examples}
14
14
  s.email = %q{ivanvanderbyl@me.com}
15
15
  s.extra_rdoc_files = [
@@ -26,21 +26,25 @@ Gem::Specification.new do |s|
26
26
  "Rakefile",
27
27
  "VERSION",
28
28
  "cloudist.gemspec",
29
+ "examples/extending_values.rb",
29
30
  "examples/queue_message.rb",
30
31
  "examples/sandwich_client.rb",
31
32
  "examples/sandwich_worker.rb",
33
+ "examples/sandwich_worker_with_class.rb",
32
34
  "lib/cloudist.rb",
33
- "lib/cloudist/basic_queue.rb",
34
35
  "lib/cloudist/callback.rb",
35
36
  "lib/cloudist/callback_methods.rb",
37
+ "lib/cloudist/callbacks/error_callback.rb",
38
+ "lib/cloudist/core_ext/object.rb",
36
39
  "lib/cloudist/core_ext/string.rb",
37
40
  "lib/cloudist/errors.rb",
38
41
  "lib/cloudist/job.rb",
39
- "lib/cloudist/job_queue.rb",
40
42
  "lib/cloudist/listener.rb",
41
43
  "lib/cloudist/payload.rb",
42
44
  "lib/cloudist/publisher.rb",
43
- "lib/cloudist/reply_queue.rb",
45
+ "lib/cloudist/queues/basic_queue.rb",
46
+ "lib/cloudist/queues/job_queue.rb",
47
+ "lib/cloudist/queues/reply_queue.rb",
44
48
  "lib/cloudist/request.rb",
45
49
  "lib/cloudist/utils.rb",
46
50
  "lib/cloudist/worker.rb",
@@ -48,6 +52,7 @@ Gem::Specification.new do |s|
48
52
  "spec/cloudist/job_spec.rb",
49
53
  "spec/cloudist/payload_spec.rb",
50
54
  "spec/cloudist/request_spec.rb",
55
+ "spec/cloudist/utils_spec.rb",
51
56
  "spec/cloudist_spec.rb",
52
57
  "spec/core_ext/string_spec.rb",
53
58
  "spec/spec_helper.rb"
@@ -58,13 +63,16 @@ Gem::Specification.new do |s|
58
63
  s.rubygems_version = %q{1.3.7}
59
64
  s.summary = %q{Super fast job queue using AMQP}
60
65
  s.test_files = [
66
+ "examples/extending_values.rb",
61
67
  "examples/queue_message.rb",
62
68
  "examples/sandwich_client.rb",
63
69
  "examples/sandwich_worker.rb",
70
+ "examples/sandwich_worker_with_class.rb",
64
71
  "spec/cloudist/basic_queue_spec.rb",
65
72
  "spec/cloudist/job_spec.rb",
66
73
  "spec/cloudist/payload_spec.rb",
67
74
  "spec/cloudist/request_spec.rb",
75
+ "spec/cloudist/utils_spec.rb",
68
76
  "spec/cloudist_spec.rb",
69
77
  "spec/core_ext/string_spec.rb",
70
78
  "spec/spec_helper.rb"
@@ -0,0 +1,44 @@
1
+ class SandwichMaker
2
+
3
+ end
4
+
5
+ class SandwichEater
6
+
7
+ end
8
+
9
+ module Cloudist
10
+ class << self
11
+ @@workers = {}
12
+
13
+ def handle(*queue_names)
14
+ class << queue_names
15
+ def with(handler)
16
+ self.each do |queue_name|
17
+ ((@@workers[queue_name.to_s] ||= []) << handler).uniq!
18
+ end
19
+ end
20
+ end
21
+ queue_names
22
+ end
23
+
24
+ def use(handler)
25
+ proxy = handler.new
26
+ class << proxy
27
+ def to(queue_name)
28
+ ((@@workers[queue_name.to_s] ||= []) << self.class).uniq!
29
+ end
30
+ end
31
+ proxy
32
+ end
33
+
34
+ def workers
35
+ @@workers
36
+ end
37
+ end
38
+ end
39
+
40
+ Cloudist.handle('make.sandwich', 'eat').with(SandwichMaker)
41
+ Cloudist.use(SandwichEater).to('eat.sandwich')
42
+
43
+ p Cloudist.workers
44
+ # >> {"eat"=>[SandwichMaker], "make.sandwich"=>[SandwichMaker], "eat.sandwich"=>[SandwichEater]}
@@ -18,13 +18,26 @@ Cloudist.signal_trap!
18
18
  Cloudist.start {
19
19
 
20
20
  log.info("Dispatching sandwich making job...")
21
- enqueue('make.sandwich', {:bread => 'white'})
22
- # enqueue('make.sandwich', {:bread => 'brown'})
23
21
 
22
+ unless ARGV.empty?
23
+ job_count = ARGV.pop.to_i
24
+ job_count.times { |i| enqueue('make.sandwich', {:bread => 'white', :sandwich_number => i})}
25
+ end
26
+
27
+
28
+ # enqueue('eat.sandwich', {:sandwich => job.id})
29
+ # enqueue('make.sandwich', {:bread => 'brown'})
30
+
24
31
  # Listen to all sandwich jobs
25
- listen('make.sandwich') {
32
+ listen('make.sandwich', 'eat.sandwich') {
26
33
  everything {
27
- Cloudist.log.info("Job ID: #{job_id}")
34
+ Cloudist.log.info("#{headers[:message_type]} - Job ID: #{job_id}")
35
+ }
36
+
37
+ error { |e|
38
+ Cloudist.log.error(e.inspect)
39
+ Cloudist.log.error(e.backtrace.inspect)
40
+ Cloudist.stop
28
41
  }
29
42
 
30
43
  progress {
@@ -37,6 +50,7 @@ Cloudist.start {
37
50
 
38
51
  event('finished'){
39
52
  Cloudist.log.info("Finished making sandwich at #{Time.now.to_s}")
53
+ Cloudist.stop
40
54
  }
41
55
  }
42
56
 
@@ -19,24 +19,18 @@ Cloudist.signal_trap!
19
19
  Cloudist.start {
20
20
  log.info("Started Worker")
21
21
 
22
- worker {
23
- job('make.sandwich') {
24
- # Fire the started event
22
+ job('make.sandwich') {
23
+ log.info("JOB (#{id}) Make sandwich with #{data[:bread]} bread")
24
+
25
+ job.started!
26
+
27
+ (1..20).each do |i|
28
+ job.progress(i * 5)
29
+ sleep(1)
25
30
 
26
- log.info("JOB (#{id}) Make sandwich with #{data[:bread]} bread")
27
- log.debug(data.inspect)
28
-
29
- EM.defer {
30
- progress(0)
31
- started!
32
- progress(10)
33
- sleep(1)
34
- progress(20)
35
- sleep(5)
36
- progress(90)
37
- finished!
38
- progress(100)
39
- }
40
- }
31
+ raise ArgumentError, "NOT GOOD!" if i == 4
32
+ end
33
+ job.finished!
41
34
  }
35
+
42
36
  }
@@ -0,0 +1,37 @@
1
+ # Cloudst Example: Sandwich Worker
2
+ #
3
+ # This example demonstrates receiving a job and sending back events to the client to let it know we've started and finsihed
4
+ # making a sandwich. From here you could dispatch an eat.sandwich event.
5
+ #
6
+ # Be sure to update the Cloudist connection settings if they differ from defaults:
7
+ # user: guest
8
+ # pass: guest
9
+ # port: 5672
10
+ # host: localhost
11
+ # vhost: /
12
+ #
13
+ $:.unshift File.dirname(__FILE__) + '/../lib'
14
+ require "rubygems"
15
+ require "cloudist"
16
+
17
+ class SandwichWorker < Cloudist::Worker
18
+ def process
19
+ log.info("Processing queue: #{queue.name}")
20
+ log.info(data.inspect)
21
+
22
+ job.started!
23
+ (1..20).each do |i|
24
+ job.progress(i * 5)
25
+ sleep(1)
26
+
27
+ raise ArgumentError, "NOT GOOD!" if i == 4
28
+ end
29
+ job.finished!
30
+ end
31
+ end
32
+
33
+ Cloudist.signal_trap!
34
+
35
+ Cloudist.start {
36
+ Cloudist.handle('make.sandwich', 'eat.sandwich').with(SandwichWorker)
37
+ }
data/lib/cloudist.rb CHANGED
@@ -8,22 +8,27 @@ require "digest/md5"
8
8
 
9
9
  $:.unshift File.dirname(__FILE__)
10
10
  require "cloudist/core_ext/string"
11
+ require "cloudist/core_ext/object"
11
12
  require "cloudist/errors"
12
13
  require "cloudist/utils"
13
- require "cloudist/basic_queue"
14
- require "cloudist/job_queue"
15
- require "cloudist/reply_queue"
14
+ require "cloudist/queues/basic_queue"
15
+ require "cloudist/queues/job_queue"
16
+ require "cloudist/queues/reply_queue"
16
17
  require "cloudist/publisher"
17
18
  require "cloudist/payload"
18
19
  require "cloudist/request"
19
- require "cloudist/worker"
20
20
  require "cloudist/callback_methods"
21
21
  require "cloudist/listener"
22
22
  require "cloudist/callback"
23
+ require "cloudist/callbacks/error_callback"
23
24
  require "cloudist/job"
25
+ require "cloudist/worker"
24
26
 
25
27
  module Cloudist
26
28
  class << self
29
+
30
+ @@workers = {}
31
+
27
32
  # Start the Cloudist loop
28
33
  #
29
34
  # Cloudist.start {
@@ -48,15 +53,82 @@ module Cloudist
48
53
 
49
54
  # Define a worker. Must be called inside start loop
50
55
  #
51
- # worker {
52
- # job('make.sandwich') {}
53
- # }
56
+ # worker {
57
+ # job('make.sandwich') {}
58
+ # }
59
+ #
60
+ # REMOVED
61
+ def worker(&block)
62
+ raise NotImplementedError, "This DSL format has been removed. Please use job('make.sandwich') {} instead."
63
+ end
64
+
65
+ # Defines a job handler (GenericWorker)
66
+ #
67
+ # job('make.sandwich') {
68
+ # job.started!
69
+ # # Work hard
70
+ # sleep(5)
71
+ # job.finished!
72
+ # }
73
+ #
74
+ # Refer to sandwich_worker.rb example
75
+ #
76
+ def job(queue_name, &block)
77
+ register_worker(queue_name, &block)
78
+ end
79
+
80
+ # Registers a worker class to handle a specific queue
81
+ #
82
+ # Cloudist.handle('make.sandwich', 'eat.sandwich').with(MyWorker)
83
+ #
84
+ # A standard worker would look like this:
85
+ #
86
+ # class MyWorker < Cloudist::Worker
87
+ # def process
88
+ # log.debug(data.inspect)
89
+ # end
90
+ # end
91
+ #
92
+ # A new instance of this worker will be created everytime a job arrives
54
93
  #
55
94
  # Refer to examples.
56
- def worker(options = {}, &block)
57
- _worker = Cloudist::Worker.new(options)
58
- _worker.instance_eval(&block)
59
- return _worker
95
+ def handle(*queue_names)
96
+ class << queue_names
97
+ def with(handler)
98
+ self.each do |queue_name|
99
+ Cloudist.register_worker(queue_name.to_s, handler)
100
+ end
101
+ end
102
+ end
103
+ queue_names
104
+ end
105
+
106
+ def register_worker(queue_name, klass = nil, &block)
107
+ job_queue = JobQueue.new(queue_name)
108
+ job_queue.subscribe do |request|
109
+ j = Job.new(request.payload.dup)
110
+ EM.defer do
111
+ begin
112
+ if block_given?
113
+ worker_instance = GenericWorker.new(j, job_queue.q)
114
+ worker_instance.process(&block)
115
+ elsif klass
116
+ worker_instance = klass.new(j, job_queue.q)
117
+ worker_instance.process
118
+ else
119
+ raise RuntimeError, "Failed to register worker, I need either a handler class or block."
120
+ end
121
+ finished = Time.now.utc.to_i
122
+ log.debug("Finished Job in #{finished - request.start} seconds")
123
+
124
+ rescue Exception => e
125
+ j.handle_error(e)
126
+ end
127
+ end
128
+ j.cleanup
129
+ end
130
+
131
+ ((@@workers[queue_name.to_s] ||= []) << job_queue).uniq!
60
132
  end
61
133
 
62
134
  # Accepts either a queue name or a job instance returned from enqueue.
@@ -64,10 +136,14 @@ module Cloudist
64
136
  # will return all responses regardless of job id so you can use the job
65
137
  # id to lookup a database record to update etc.
66
138
  # When given a job instance it will only return messages from that job.
67
- def listen(job_or_queue_name, &block)
68
- _listener = Cloudist::Listener.new(job_or_queue_name)
69
- _listener.subscribe(&block)
70
- return _listener
139
+ def listen(*queue_names, &block)
140
+ @@listeners ||= []
141
+ queue_names.each do |job_or_queue_name|
142
+ _listener = Cloudist::Listener.new(job_or_queue_name)
143
+ _listener.subscribe(&block)
144
+ @@listeners << _listener
145
+ end
146
+ return @@listeners
71
147
  end
72
148
 
73
149
  # Enqueues a job.
@@ -82,11 +158,11 @@ module Cloudist
82
158
 
83
159
  # Call this at anytime inside the loop to exit the app.
84
160
  def stop_safely
85
- ::EM.add_timer(0.2) {
161
+ # ::EM.add_timer(0.2) {
86
162
  ::AMQP.stop {
87
163
  ::EM.stop
88
164
  }
89
- }
165
+ # }
90
166
  end
91
167
 
92
168
  alias :stop :stop_safely
@@ -138,6 +214,14 @@ module Cloudist
138
214
  ::Signal.trap('TERM'){ Cloudist.stop }
139
215
  end
140
216
 
217
+ def workers
218
+ @@workers
219
+ end
220
+
221
+ def remove_workers
222
+ @@workers = {}
223
+ end
224
+
141
225
  end
142
226
 
143
227
  end
@@ -0,0 +1,14 @@
1
+ module Cloudist
2
+ class ErrorCallback < Callback
3
+ def call(payload)
4
+ @payload = payload
5
+
6
+ case source.arity
7
+ when 0
8
+ instance_exec(&source)
9
+ when 1
10
+ instance_exec(payload.exception, &source)
11
+ end
12
+ end
13
+ end
14
+ end
@@ -0,0 +1,81 @@
1
+ # Taken from Rails ActiveSupport
2
+ class Object
3
+ def remove_subclasses_of(*superclasses) #:nodoc:
4
+ Class.remove_class(*subclasses_of(*superclasses))
5
+ end
6
+
7
+ begin
8
+ ObjectSpace.each_object(Class.new) {}
9
+
10
+ # Exclude this class unless it's a subclass of our supers and is defined.
11
+ # We check defined? in case we find a removed class that has yet to be
12
+ # garbage collected. This also fails for anonymous classes -- please
13
+ # submit a patch if you have a workaround.
14
+ def subclasses_of(*superclasses) #:nodoc:
15
+ subclasses = []
16
+
17
+ superclasses.each do |sup|
18
+ ObjectSpace.each_object(class << sup; self; end) do |k|
19
+ if k != sup && (k.name.blank? || eval("defined?(::#{k}) && ::#{k}.object_id == k.object_id"))
20
+ subclasses << k
21
+ end
22
+ end
23
+ end
24
+
25
+ subclasses
26
+ end
27
+ rescue RuntimeError
28
+ # JRuby and any implementations which cannot handle the objectspace traversal
29
+ # above fall back to this implementation
30
+ def subclasses_of(*superclasses) #:nodoc:
31
+ subclasses = []
32
+
33
+ superclasses.each do |sup|
34
+ ObjectSpace.each_object(Class) do |k|
35
+ if superclasses.any? { |superclass| k < superclass } &&
36
+ (k.name.blank? || eval("defined?(::#{k}) && ::#{k}.object_id == k.object_id"))
37
+ subclasses << k
38
+ end
39
+ end
40
+ subclasses.uniq!
41
+ end
42
+ subclasses
43
+ end
44
+ end
45
+
46
+ def extended_by #:nodoc:
47
+ ancestors = class << self; ancestors end
48
+ ancestors.select { |mod| mod.class == Module } - [ Object, Kernel ]
49
+ end
50
+
51
+ def extend_with_included_modules_from(object) #:nodoc:
52
+ object.extended_by.each { |mod| extend mod }
53
+ end
54
+
55
+ unless defined? instance_exec # 1.9
56
+ module InstanceExecMethods #:nodoc:
57
+ end
58
+ include InstanceExecMethods
59
+
60
+ # Evaluate the block with the given arguments within the context of
61
+ # this object, so self is set to the method receiver.
62
+ #
63
+ # From Mauricio's http://eigenclass.org/hiki/bounded+space+instance_exec
64
+ def instance_exec(*args, &block)
65
+ begin
66
+ old_critical, Thread.critical = Thread.critical, true
67
+ n = 0
68
+ n += 1 while respond_to?(method_name = "__instance_exec#{n}")
69
+ InstanceExecMethods.module_eval { define_method(method_name, &block) }
70
+ ensure
71
+ Thread.critical = old_critical
72
+ end
73
+
74
+ begin
75
+ send(method_name, *args)
76
+ ensure
77
+ InstanceExecMethods.module_eval { remove_method(method_name) } rescue nil
78
+ end
79
+ end
80
+ end
81
+ end