bunnyque 0.0.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.
checksums.yaml ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: 4344171813cdc86827f387a1530652112fbe4c62
4
+ data.tar.gz: 0cea0b49cfd1c43410715b363b85d4a5ee33264c
5
+ SHA512:
6
+ metadata.gz: 8c0ae010f53ffc7848bad4fcf67eba77a7d738c5220ad864924c57b1fa7f59d8278bf812a10a173f9444975b3eb20b80ec23ec946b28f34745e65a5a5cb3fddd
7
+ data.tar.gz: 5a584b73c76ed9616f719a6ca5c1309228f16dba49eb07122601338cdf41dac41c67672d18d032bbaaf4a5c88195df8a36a1e646c8a648ffb25334cbd2ab25a0
data/.gitignore ADDED
@@ -0,0 +1,14 @@
1
+ /.bundle/
2
+ /.yardoc
3
+ /Gemfile.lock
4
+ /_yardoc/
5
+ /coverage/
6
+ /doc/
7
+ /pkg/
8
+ /spec/reports/
9
+ /tmp/
10
+ *.bundle
11
+ *.so
12
+ *.o
13
+ *.a
14
+ mkmf.log
data/Gemfile ADDED
@@ -0,0 +1,4 @@
1
+ source 'https://rubygems.org'
2
+
3
+ # Specify your gem's dependencies in bunnyque.gemspec
4
+ gemspec
data/LICENSE.txt ADDED
@@ -0,0 +1,22 @@
1
+ Copyright (c) 2014 Wouter de Vos
2
+
3
+ MIT License
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining
6
+ a copy of this software and associated documentation files (the
7
+ "Software"), to deal in the Software without restriction, including
8
+ without limitation the rights to use, copy, modify, merge, publish,
9
+ distribute, sublicense, and/or sell copies of the Software, and to
10
+ permit persons to whom the Software is furnished to do so, subject to
11
+ the following conditions:
12
+
13
+ The above copyright notice and this permission notice shall be
14
+ included in all copies or substantial portions of the Software.
15
+
16
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
17
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
18
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
19
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
20
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
21
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
22
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
data/README.md ADDED
@@ -0,0 +1,31 @@
1
+ # Bunnyque
2
+
3
+ TODO: Write a gem description
4
+
5
+ ## Installation
6
+
7
+ Add this line to your application's Gemfile:
8
+
9
+ ```ruby
10
+ gem 'bunnyque'
11
+ ```
12
+
13
+ And then execute:
14
+
15
+ $ bundle
16
+
17
+ Or install it yourself as:
18
+
19
+ $ gem install bunnyque
20
+
21
+ ## Usage
22
+
23
+ TODO: Write usage instructions here
24
+
25
+ ## Contributing
26
+
27
+ 1. Fork it ( https://github.com/[my-github-username]/bunnyque/fork )
28
+ 2. Create your feature branch (`git checkout -b my-new-feature`)
29
+ 3. Commit your changes (`git commit -am 'Add some feature'`)
30
+ 4. Push to the branch (`git push origin my-new-feature`)
31
+ 5. Create a new Pull Request
data/Rakefile ADDED
@@ -0,0 +1,2 @@
1
+ require "bundler/gem_tasks"
2
+
data/bunnyque.gemspec ADDED
@@ -0,0 +1,23 @@
1
+ # coding: utf-8
2
+ lib = File.expand_path('../lib', __FILE__)
3
+ $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
4
+ require 'bunnyque/version'
5
+
6
+ Gem::Specification.new do |spec|
7
+ spec.name = "bunnyque"
8
+ spec.version = Bunnyque::VERSION
9
+ spec.authors = ["Wouter de Vos"]
10
+ spec.email = ["wouter@springest.com"]
11
+ spec.summary = %q{Jobs in RabbitMQ}
12
+ spec.description = %q{Jobs in RabbitMQ that support fanout.}
13
+ spec.homepage = "https://github.com/foxycoder/bunnyque"
14
+ spec.license = "MIT"
15
+
16
+ spec.files = `git ls-files -z`.split("\x0")
17
+ spec.executables = spec.files.grep(%r{^bin/}) { |f| File.basename(f) }
18
+ spec.test_files = spec.files.grep(%r{^(test|spec|features)/})
19
+ spec.require_paths = ["lib"]
20
+
21
+ spec.add_development_dependency "bundler", "~> 1.7"
22
+ spec.add_development_dependency "rake", "~> 10.0"
23
+ end
data/lib/bunnyque.rb ADDED
@@ -0,0 +1,40 @@
1
+ %w(
2
+ base
3
+ direct
4
+ broadcast
5
+ master
6
+ worker
7
+ masters/broadcast_server
8
+ masters/server
9
+ workers/broadcast_worker
10
+ workers/client
11
+ version
12
+ ).each do |lib|
13
+ require File.expand_path("../bunnyque/#{lib}.rb", __FILE__)
14
+ end
15
+
16
+ module Bunnyque
17
+
18
+ class <<self
19
+ def broadcast object, params
20
+ Bunnyque::Masters::BroadcastServer.new(host: config.host, port: config.port, queue: config.queue).publish object.to_s, params
21
+ end
22
+
23
+ def send node_name, object, params
24
+ Bunnyque::Masters::Server.new(host: config.host, port: config.port, queue: node_name).publish object.to_s, params
25
+ end
26
+
27
+ def config
28
+ @config ||
29
+ begin
30
+ Utilities::HashAsObject.new(
31
+ Base::DEFAULT_CONFIG.merge(
32
+ YAML.load(
33
+ ERB.new(
34
+ File.read(
35
+ File.join(File.expand_path('../../config/bunnyque.yml', __FILE__)))).result
36
+ )[defined?(Rails) ? Rails.env : (ENV['RAILS_ENV'] || 'development')]))
37
+ end
38
+ end
39
+ end
40
+ end
@@ -0,0 +1,71 @@
1
+ module Bunnyque
2
+ # Public: Base class for Bunnyque Masters and Workers
3
+ class Base
4
+ define_const_unless_defined :DEFAULT_CONFIG, {
5
+ host: "rabbitmq.springest.io",
6
+ port: 5672,
7
+ queue: "springest.messages"
8
+ }
9
+
10
+ # Public: Base class for Bunnyque Masters and Workers.
11
+ #
12
+ # options - optional hash with options for the Bunnyque
13
+ # host - the hostname to bind to / listen on.
14
+ # Default: rabbitmq.springest.io
15
+ # port - the port to bind to / listen on
16
+ # Default: 5672
17
+ # queue - the queue to use
18
+ # Default: springest.messages
19
+ def initialize options={}
20
+ @config = DEFAULT_CONFIG.merge options
21
+ end
22
+
23
+ def connection
24
+ @connection ||= AMQP.connect bind_addr
25
+ end
26
+
27
+ def channel
28
+ @channel ||=
29
+ begin
30
+ channel = AMQP::Channel.new(connection)
31
+ channel.on_error do |ch, close|
32
+ puts "Handling a channel-level exception on channel1: #{close.reply_text}, #{close.inspect}"
33
+ end
34
+ channel
35
+ end
36
+ end
37
+
38
+ def exchange
39
+ raise NotImplementedError.new
40
+ end
41
+
42
+ # This should be implemented in a Bunnyque Master.
43
+ def publish
44
+ raise NotImplementedError.new
45
+ end
46
+
47
+ # This should be implemented in a Bunnyque Worker.
48
+ def listen
49
+ raise NotImplementedError.new
50
+ end
51
+
52
+ protected
53
+
54
+ def bind_addr
55
+ "amqp://#{config[:host]}:#{config[:port]}"
56
+ end
57
+
58
+ def config
59
+ @config || DEFAULT_CONFIG
60
+ end
61
+
62
+ def log message
63
+ STDOUT.puts Time.now.strftime "[%c]: #{message}"
64
+ end
65
+
66
+ def log_file
67
+ File.expand_path('../../../log/bunnyque.log', __FILE__)
68
+ end
69
+ end
70
+ end
71
+
@@ -0,0 +1,15 @@
1
+ module Bunnyque
2
+ module Broadcast
3
+ extend ActiveSupport::Concern
4
+
5
+ included do
6
+ def exchange
7
+ @exchange ||= channel.fanout(queue_name)
8
+ end
9
+
10
+ def queue_name
11
+ "#{config[:queue]}.fanout"
12
+ end
13
+ end
14
+ end
15
+ end
@@ -0,0 +1,8 @@
1
+ module Bunnyque
2
+ module Masters
3
+ class BroadcastServer < Bunnyque::Master
4
+ include Bunnyque::Broadcast
5
+ end
6
+ end
7
+ end
8
+
@@ -0,0 +1,8 @@
1
+ module Bunnyque
2
+ module Workers
3
+ class BroadcastWorker < Bunnyque::Worker
4
+ include Bunnyque::Broadcast
5
+ end
6
+ end
7
+ end
8
+
@@ -0,0 +1,8 @@
1
+ module Bunnyque
2
+ module Workers
3
+ class Client < Bunnyque::Worker
4
+ include Bunnyque::Direct
5
+ end
6
+ end
7
+ end
8
+
@@ -0,0 +1,15 @@
1
+ module Bunnyque
2
+ module Direct
3
+ extend ActiveSupport::Concern
4
+
5
+ included do
6
+ def exchange
7
+ @exchange ||= channel.direct(queue_name)
8
+ end
9
+
10
+ def queue_name
11
+ "#{`hostname`.chomp}.direct"
12
+ end
13
+ end
14
+ end
15
+ end
@@ -0,0 +1,24 @@
1
+ module Bunnyque
2
+ # Public: Base class for Bunnyque Masters and Workers
3
+ class Master < Base
4
+ def publish klass, *params
5
+ EventMachine.run do
6
+ payload = prepare_payload klass, params
7
+ exchange.publish(payload) do
8
+ connection.disconnect { EventMachine.stop }
9
+ end
10
+ log "Published payload #{payload} to #{queue_name}"
11
+ end
12
+ end
13
+
14
+ protected
15
+
16
+ def prepare_payload klass, params
17
+ {
18
+ class: klass,
19
+ params: params
20
+ }.to_json
21
+ end
22
+ end
23
+ end
24
+
@@ -0,0 +1,8 @@
1
+ module Bunnyque
2
+ module Masters
3
+ class BroadcastServer < Bunnyque::Master
4
+ include Bunnyque::Broadcast
5
+ end
6
+ end
7
+ end
8
+
@@ -0,0 +1,8 @@
1
+ module Bunnyque
2
+ module Masters
3
+ class Server < Bunnyque::Master
4
+ include Bunnyque::Direct
5
+ end
6
+ end
7
+ end
8
+
@@ -0,0 +1,8 @@
1
+ module Bunnyque
2
+ module Masters
3
+ class Server < Bunnyque::Master
4
+ include Bunnyque::Direct
5
+ end
6
+ end
7
+ end
8
+
@@ -0,0 +1,3 @@
1
+ module Bunnyque
2
+ VERSION = "0.0.1"
3
+ end
@@ -0,0 +1,103 @@
1
+ module Bunnyque
2
+ # Public: Base class for Bunnyque Workers
3
+ class Worker < Base
4
+ def initialize name, options={}
5
+ @name = name.chomp
6
+ super(options)
7
+ end
8
+
9
+ def start options={}, &blk
10
+ if File.exist?(pid_path)
11
+ existing_pid = IO.read(pid_path).to_i
12
+ begin
13
+ Process.kill(0, existing_pid)
14
+ raise "Worker is already running with PID #{existing_pid}"
15
+ rescue Errno::ESRCH
16
+ log "Removing stale PID file at #{pid_path}"
17
+ FileUtils.rm(pid_path)
18
+ end
19
+ end
20
+ pid = fork do
21
+ Process.setsid
22
+ STDIN.reopen('/dev/null')
23
+ STDOUT.reopen(log_file, 'a')
24
+ STDOUT.sync = true
25
+ STDERR.reopen(STDOUT)
26
+ listen &blk
27
+ end
28
+ FileUtils.mkdir_p(pid_dir)
29
+ File.open(pid_path, 'w') do |file|
30
+ file << pid
31
+ end
32
+ log "Worker '#{name}' started with PID #{pid}"
33
+ end
34
+
35
+ def restart &blk
36
+ begin
37
+ stop
38
+ rescue
39
+ ensure
40
+ start &blk
41
+ end
42
+ end
43
+
44
+ def stop
45
+ if File.exist?(pid_path)
46
+ pid = IO.read(pid_path).to_i
47
+ begin
48
+ Process.kill('TERM', pid)
49
+ rescue Errno::ESRCH
50
+ raise "Process with PID #{pid} is no longer running"
51
+ ensure
52
+ FileUtils.rm(pid_path)
53
+ end
54
+ else
55
+ raise "No PID file at #{pid_path}"
56
+ end
57
+ end
58
+
59
+ def listen(&blk)
60
+ EventMachine.run do
61
+ queue.bind(exchange).subscribe do |payload|
62
+ log "#{name} received #{payload}"
63
+ if block_given?
64
+ blk.call(payload)
65
+ else
66
+ work payload
67
+ end
68
+ end
69
+ log "#{name} is now listening on #{queue_name}"
70
+ end
71
+ end
72
+
73
+ def name
74
+ @name || ""
75
+ end
76
+
77
+ def queue
78
+ @queue ||= channel.queue!(name, durable: true)
79
+ end
80
+
81
+ protected
82
+
83
+ def work payload
84
+ log "#{name} is working on #{payload}"
85
+ parcel = JSON.parse payload
86
+ begin
87
+ parcel["class"].constantize.send :perform, parcel["params"]
88
+ rescue => e
89
+ log "#{name} choked on #{payload} with error #{e}"
90
+ end
91
+ log "#{name} is idle after working on #{payload}"
92
+ end
93
+
94
+ def pid_dir
95
+ File.expand_path('../../../tmp/pids', __FILE__)
96
+ end
97
+
98
+ def pid_path
99
+ File.join(pid_dir, "rabbit-worker-#{name}.pid")
100
+ end
101
+ end
102
+ end
103
+
@@ -0,0 +1,8 @@
1
+ module Bunnyque
2
+ module Workers
3
+ class BroadcastWorker < Bunnyque::Worker
4
+ include Bunnyque::Broadcast
5
+ end
6
+ end
7
+ end
8
+
@@ -0,0 +1,8 @@
1
+ module Bunnyque
2
+ module Workers
3
+ class Client < Bunnyque::Worker
4
+ include Bunnyque::Direct
5
+ end
6
+ end
7
+ end
8
+
metadata ADDED
@@ -0,0 +1,93 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: bunnyque
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.0.1
5
+ platform: ruby
6
+ authors:
7
+ - Wouter de Vos
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+ date: 2014-10-21 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: bundler
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - ~>
18
+ - !ruby/object:Gem::Version
19
+ version: '1.7'
20
+ type: :development
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - ~>
25
+ - !ruby/object:Gem::Version
26
+ version: '1.7'
27
+ - !ruby/object:Gem::Dependency
28
+ name: rake
29
+ requirement: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - ~>
32
+ - !ruby/object:Gem::Version
33
+ version: '10.0'
34
+ type: :development
35
+ prerelease: false
36
+ version_requirements: !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - ~>
39
+ - !ruby/object:Gem::Version
40
+ version: '10.0'
41
+ description: Jobs in RabbitMQ that support fanout.
42
+ email:
43
+ - wouter@springest.com
44
+ executables: []
45
+ extensions: []
46
+ extra_rdoc_files: []
47
+ files:
48
+ - .gitignore
49
+ - Gemfile
50
+ - LICENSE.txt
51
+ - README.md
52
+ - Rakefile
53
+ - bunnyque.gemspec
54
+ - lib/bunnyque.rb
55
+ - lib/bunnyque/base.rb
56
+ - lib/bunnyque/broadcast.rb
57
+ - lib/bunnyque/broadcast_server.rb
58
+ - lib/bunnyque/broadcast_worker.rb
59
+ - lib/bunnyque/client.rb
60
+ - lib/bunnyque/direct.rb
61
+ - lib/bunnyque/master.rb
62
+ - lib/bunnyque/masters/broadcast_server.rb
63
+ - lib/bunnyque/masters/server.rb
64
+ - lib/bunnyque/server.rb
65
+ - lib/bunnyque/version.rb
66
+ - lib/bunnyque/worker.rb
67
+ - lib/bunnyque/workers/broadcast_worker.rb
68
+ - lib/bunnyque/workers/client.rb
69
+ homepage: https://github.com/foxycoder/bunnyque
70
+ licenses:
71
+ - MIT
72
+ metadata: {}
73
+ post_install_message:
74
+ rdoc_options: []
75
+ require_paths:
76
+ - lib
77
+ required_ruby_version: !ruby/object:Gem::Requirement
78
+ requirements:
79
+ - - '>='
80
+ - !ruby/object:Gem::Version
81
+ version: '0'
82
+ required_rubygems_version: !ruby/object:Gem::Requirement
83
+ requirements:
84
+ - - '>='
85
+ - !ruby/object:Gem::Version
86
+ version: '0'
87
+ requirements: []
88
+ rubyforge_project:
89
+ rubygems_version: 2.4.2
90
+ signing_key:
91
+ specification_version: 4
92
+ summary: Jobs in RabbitMQ
93
+ test_files: []