adamwiggins-minion 0.1.2

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.
@@ -0,0 +1,83 @@
1
+
2
+ = Minion: super simple job queue over amqp
3
+
4
+ Minion makes processing jobs over AMQP simple and easy.
5
+
6
+ == Setup
7
+
8
+ Minion pulls the AMQP credentials out the environment via AMQP_URI.
9
+
10
+ $ export AMQP_URI="amqp://johndoe:abc123@localhost/my_vhost"
11
+
12
+ If no URI is supplied, Minion defaults to "amqp://guest:guest@localhost/" which
13
+ is the default credentials for Rabbitmq running locally.
14
+
15
+ == Principles
16
+
17
+ Minion treats your jobs with respect. The queues are durable and not
18
+ autodelete. When popping jobs off the queue, they will not receive an ack
19
+ until the job is done. You can rest assured that once queued, the job will not
20
+ be lost.
21
+
22
+ Sends are done synchronously and receives are done asynchronously. This allows
23
+ you to Minion.enqueue() from the console, or in a mongrel and you don't need to
24
+ worry about eventmachine. It also means that when enqueue returns, the AMQP
25
+ server has received your message. Daemons set to receive messages however use
26
+ eventmachine.
27
+
28
+ Message processing is done one at a time (prefetch 1). If you want tasks done
29
+ in parallel, run two minions.
30
+
31
+ == Push a job onto the queue
32
+
33
+ Its easy to push a job onto the queue.
34
+
35
+ Minion.enqueue("make.sandwich", { "for" => "me", "with" => "bread" })
36
+
37
+ Minion expects a queue name (and will create it if needed). The second argument
38
+ needs to be a hash.
39
+
40
+ == Processing a job
41
+
42
+ require 'minion'
43
+
44
+ include Minion
45
+
46
+ job "make.sandwich" do |args|
47
+ Sandwich.make(args["for"],args["with"])
48
+ end
49
+
50
+ == Chaining multiple steps
51
+
52
+ If you have a task that requires more than one step just pass an array of
53
+ queues when you enqueue.
54
+
55
+ Minion.enqueue([ "make.sandwich", "eat.sandwich" ], "for" => "me")
56
+
57
+ job "make.sandwich" do
58
+ ## this return value is merged with for => me and sent to the next queue
59
+ { "type" => "ham on rye" }
60
+ end
61
+
62
+ job "eat.sandwich" do |args|
63
+ puts "I have #{args["type"]} sandwich for #{args["me"]}"
64
+ end
65
+
66
+ == Error handling
67
+
68
+ When an error is thrown in a job handler, the job is requeued to be done later
69
+ and the minion process exits. If you define an error handler, however, the
70
+ error handler is run and the job is removed from the queue.
71
+
72
+ on_error do |e|
73
+ puts "got an error! #{e}"
74
+ end
75
+
76
+ == Logging
77
+
78
+ Minion logs to stdout via "puts". You can specify a custom logger like this:
79
+
80
+ logger do |msg|
81
+ puts msg
82
+ end
83
+
data/VERSION ADDED
@@ -0,0 +1 @@
1
+ 0.1.2
@@ -0,0 +1,33 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ require 'rubygems'
4
+ require 'minion'
5
+
6
+ include Minion
7
+
8
+ on_error do |e|
9
+ puts "got an error!"
10
+ end
11
+
12
+ logger do |msg|
13
+ puts "--> #{msg}"
14
+ end
15
+
16
+ job "math.incr" do |args|
17
+ { "number" => (1 + args["number"].to_i) }
18
+ end
19
+
20
+ job "math.double" do |args|
21
+ { "number" => (2 * args["number"].to_i) }
22
+ end
23
+
24
+ job "math.square" do |args|
25
+ { "number" => (args["number"].to_i * args["number"].to_i) }
26
+ end
27
+
28
+ job "math.print" do |args|
29
+ puts "NUMBER -----> #{args["number"]}"
30
+ end
31
+
32
+ enqueue([ "math.incr", "math.double", "math.square", "math.incr", "math.double", "math.print" ], { :number => 3 })
33
+
@@ -0,0 +1,25 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ require 'rubygems'
4
+ require 'minion'
5
+
6
+ include Minion
7
+
8
+ job "add.bread" do |args|
9
+ { "bread" => "sourdough" }
10
+ end
11
+
12
+ job "add.meat" do |args|
13
+ { "meat" => "turkey" }
14
+ end
15
+
16
+ job "add.condiments" do |args|
17
+ { "condiments" => "mayo" }
18
+ end
19
+
20
+ job "eat.sandwich" do |args|
21
+ puts "YUM! A #{args['meat']} on #{args['bread']} sandwich with #{args['condiments']}"
22
+ end
23
+
24
+ enqueue(["add.bread", "add.meat", "add.condiments", "eat.sandwich" ])
25
+
@@ -0,0 +1,114 @@
1
+ require 'uri'
2
+ require 'json'
3
+ require 'mq'
4
+ require 'bunny'
5
+
6
+ module Minion
7
+ extend self
8
+
9
+ def enqueue(jobs, data = {})
10
+ ## jobs can be one or more jobs
11
+ if jobs.respond_to? :shift
12
+ queue = jobs.shift
13
+ data["next_job"] = jobs unless jobs.empty?
14
+ else
15
+ queue = jobs
16
+ end
17
+
18
+ log "send: #{queue}:#{data.to_json}"
19
+ bunny.queue(queue, :durable => true, :auto_delete => false).publish(data.to_json)
20
+ end
21
+
22
+ def on_error(&blk)
23
+ @@error_handler = blk
24
+ end
25
+
26
+ def logger(&blk)
27
+ @@logger = blk
28
+ end
29
+
30
+ def job(queue, &blk)
31
+ handler do
32
+ MQ.queue(queue).subscribe(:ack => true) do |h,m|
33
+ return if AMQP.closing?
34
+ begin
35
+ log "recv: #{queue}:#{m}"
36
+
37
+ args = JSON.load(m)
38
+
39
+ result = yield(args)
40
+
41
+ next_job(args, result)
42
+ rescue Object => e
43
+ raise unless error_handler
44
+ error_handler.call(e)
45
+ end
46
+ h.ack
47
+ end
48
+ end
49
+ end
50
+
51
+ def run
52
+ log "Starting minion"
53
+
54
+ Signal.trap('INT') { AMQP.stop{ EM.stop } }
55
+ Signal.trap('TERM'){ AMQP.stop{ EM.stop } }
56
+
57
+ EM.run do
58
+ AMQP.start(amqp_config) do
59
+ MQ.prefetch(1)
60
+ @@handlers.each { |h| h.call }
61
+ end
62
+ end
63
+ end
64
+
65
+ private
66
+
67
+ def amqp_url
68
+ ENV["AMQP_URL"] || "amqp://guest:guest@localhost/"
69
+ end
70
+
71
+ def amqp_config
72
+ uri = URI.parse(amqp_url)
73
+ {
74
+ :vhost => uri.path,
75
+ :host => uri.host,
76
+ :user => uri.user,
77
+ :port => (uri.port || 5672),
78
+ :pass => uri.password
79
+ }
80
+ rescue
81
+ raise "invalid AMQP_URL: #{uri.inspect} (#{e})"
82
+ end
83
+
84
+ def new_bunny
85
+ b = Bunny.new(amqp_config)
86
+ b.start
87
+ b
88
+ end
89
+
90
+ def bunny
91
+ @@bunny ||= new_bunny
92
+ end
93
+
94
+ def log(msg)
95
+ @@logger ||= proc { |m| puts "#{Time.now} :minion: #{m}" }
96
+ @@logger.call(msg)
97
+ end
98
+
99
+ def handler(&blk)
100
+ @@handlers ||= []
101
+ at_exit { Minion.run } if @@handlers.size == 0
102
+ @@handlers << blk
103
+ end
104
+
105
+ def next_job(args, response)
106
+ queue = args.delete("next_job")
107
+ enqueue(queue,args.merge(response)) if queue
108
+ end
109
+
110
+ def error_handler
111
+ @@error_handler ||= nil
112
+ end
113
+ end
114
+
metadata ADDED
@@ -0,0 +1,88 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: adamwiggins-minion
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.1.2
5
+ platform: ruby
6
+ authors:
7
+ - Orion Henry
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+
12
+ date: 2009-08-19 00:00:00 -07:00
13
+ default_executable:
14
+ dependencies:
15
+ - !ruby/object:Gem::Dependency
16
+ name: tmm1-amqp
17
+ type: :runtime
18
+ version_requirement:
19
+ version_requirements: !ruby/object:Gem::Requirement
20
+ requirements:
21
+ - - ">="
22
+ - !ruby/object:Gem::Version
23
+ version: 0.6.4
24
+ version:
25
+ - !ruby/object:Gem::Dependency
26
+ name: json
27
+ type: :runtime
28
+ version_requirement:
29
+ version_requirements: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - ">="
32
+ - !ruby/object:Gem::Version
33
+ version: 1.1.7
34
+ version:
35
+ - !ruby/object:Gem::Dependency
36
+ name: bunny
37
+ type: :runtime
38
+ version_requirement:
39
+ version_requirements: !ruby/object:Gem::Requirement
40
+ requirements:
41
+ - - ">="
42
+ - !ruby/object:Gem::Version
43
+ version: 0.5.2
44
+ version:
45
+ description: Super simple job queue over AMQP
46
+ email: orion@heroku.com
47
+ executables: []
48
+
49
+ extensions: []
50
+
51
+ extra_rdoc_files:
52
+ - README.rdoc
53
+ files:
54
+ - README.rdoc
55
+ - VERSION
56
+ - examples/math.rb
57
+ - examples/sandwich.rb
58
+ - lib/minion.rb
59
+ has_rdoc: true
60
+ homepage: http://github.com/orionz/minion
61
+ licenses:
62
+ post_install_message:
63
+ rdoc_options:
64
+ - --charset=UTF-8
65
+ require_paths:
66
+ - lib
67
+ required_ruby_version: !ruby/object:Gem::Requirement
68
+ requirements:
69
+ - - ">="
70
+ - !ruby/object:Gem::Version
71
+ version: "0"
72
+ version:
73
+ required_rubygems_version: !ruby/object:Gem::Requirement
74
+ requirements:
75
+ - - ">="
76
+ - !ruby/object:Gem::Version
77
+ version: "0"
78
+ version:
79
+ requirements: []
80
+
81
+ rubyforge_project: minion
82
+ rubygems_version: 1.3.5
83
+ signing_key:
84
+ specification_version: 2
85
+ summary: Super simple job queue over AMQP
86
+ test_files:
87
+ - examples/math.rb
88
+ - examples/sandwich.rb