rj 0.0.4.1

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,18 @@
1
+ *.gem
2
+ *.rbc
3
+ .bundle
4
+ .config
5
+ .yardoc
6
+ .DS_Store
7
+ Gemfile.lock
8
+ InstalledFiles
9
+ _yardoc
10
+ coverage
11
+ doc/
12
+ lib/bundler/man
13
+ pkg
14
+ rdoc
15
+ spec/reports
16
+ test/tmp
17
+ test/version_tmp
18
+ tmp
data/.rspec ADDED
@@ -0,0 +1,2 @@
1
+ --colour
2
+ --format documentation
data/Gemfile ADDED
@@ -0,0 +1,13 @@
1
+ source :rubygems
2
+
3
+ gemspec
4
+
5
+ gem 'rabbit_jobs', :path => './'
6
+
7
+ group :development do
8
+ gem 'rspec', '~> 2.8'
9
+ gem 'rr'
10
+ gem 'autotest'
11
+ # gem 'fs-event'
12
+ gem 'simplecov', require: false
13
+ end
data/LICENSE ADDED
@@ -0,0 +1,22 @@
1
+ Copyright (c) 2012 Pavel Lazureykis
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.
@@ -0,0 +1,29 @@
1
+ # RabbitJobs
2
+
3
+ TODO: Write a gem description
4
+
5
+ ## Installation
6
+
7
+ Add this line to your application's Gemfile:
8
+
9
+ gem 'rabbit_jobs'
10
+
11
+ And then execute:
12
+
13
+ $ bundle
14
+
15
+ Or install it yourself as:
16
+
17
+ $ gem install rabbit_jobs
18
+
19
+ ## Usage
20
+
21
+ TODO: Write usage instructions here
22
+
23
+ ## Contributing
24
+
25
+ 1. Fork it
26
+ 2. Create your feature branch (`git checkout -b my-new-feature`)
27
+ 3. Commit your changes (`git commit -am 'Added some feature'`)
28
+ 4. Push to the branch (`git push origin my-new-feature`)
29
+ 5. Create new Pull Request
@@ -0,0 +1,2 @@
1
+ #!/usr/bin/env rake
2
+ require "bundler/gem_tasks"
@@ -0,0 +1,29 @@
1
+ # -*- encoding : utf-8 -*-
2
+ require 'bundler/setup'
3
+ require 'rabbit_jobs'
4
+ require 'json'
5
+
6
+ RabbitJobs.configure do |c|
7
+ c.host "127.0.0.1"
8
+
9
+ c.exchange 'test_exchange', durable: true, auto_delete: false
10
+
11
+ c.queue 'rabbit_jobs_test1', durable: true, auto_delete: false, ack: true, arguments: {'x-ha-policy' => 'all'}
12
+ c.queue 'rabbit_jobs_test2', durable: true, auto_delete: false, ack: true, arguments: {'x-ha-policy' => 'all'}
13
+ c.queue 'rabbit_jobs_test3', durable: true, auto_delete: false, ack: true, arguments: {'x-ha-policy' => 'all'}
14
+ end
15
+
16
+ puts JSON.pretty_generate(RabbitJobs.config.to_hash)
17
+
18
+ puts JSON.pretty_generate(RabbitJobs.config.queues)
19
+
20
+ class MyJob < RabbitJobs::Job
21
+
22
+ expires_in 60 # dont perform this job after 60 seconds
23
+
24
+ def self.perform(time)
25
+ puts "This job was published at #{}"
26
+ end
27
+ end
28
+
29
+ RabbitJobs.publish_to('rabbit_jobs_test1', MyJob, Time.now)
@@ -0,0 +1,24 @@
1
+ # -*- encoding : utf-8 -*-
2
+
3
+ require 'rabbit_jobs/version'
4
+
5
+ require 'rabbit_jobs/helpers'
6
+ require 'rabbit_jobs/amqp_helpers'
7
+ require 'rabbit_jobs/configuration'
8
+ require 'rabbit_jobs/logger'
9
+
10
+ require 'rabbit_jobs/job'
11
+ require 'rabbit_jobs/publisher'
12
+ require 'rabbit_jobs/worker'
13
+
14
+ module RabbitJobs
15
+ extend self
16
+
17
+ def publish(klass, opts = {}, *params)
18
+ RabbitJobs::Publisher.publish(klass, opts, *params)
19
+ end
20
+
21
+ def publish_to(routing_key, klass, opts = {}, *params)
22
+ RabbitJobs::Publisher.publish_to(routing_key, klass, opts, *params)
23
+ end
24
+ end
@@ -0,0 +1,45 @@
1
+ # -*- encoding : utf-8 -*-
2
+
3
+ module RabbitJobs
4
+ module AmqpHelpers
5
+
6
+ # Calls given block with initialized amqp
7
+
8
+ def amqp_with_exchange(&block)
9
+ raise ArgumentError unless block
10
+
11
+ AMQP.start(host: RabbitJobs.config.host) do |connection|
12
+ channel = AMQP::Channel.new(connection)
13
+
14
+ channel.on_error do |ch, channel_close|
15
+ puts "Channel-level error: #{channel_close.reply_text}, shutting down..."
16
+ connection.close { EM.stop }
17
+ end
18
+
19
+ exchange = channel.direct(RabbitJobs.config[:exchange], RabbitJobs.config[:exchange_params])
20
+
21
+ # go work
22
+ block.call(connection, exchange)
23
+ end
24
+ end
25
+
26
+ def amqp_with_queue(routing_key, &block)
27
+
28
+ raise ArgumentError unless routing_key && block
29
+
30
+ amqp_with_exchange do |connection, exchange|
31
+ queue = exchange.channel.queue(RabbitJobs.config.queue_name(routing_key), RabbitJobs.config[:queues][routing_key])
32
+ queue.bind(exchange, :routing_key => routing_key)
33
+
34
+ # go work
35
+ block.call(connection, queue)
36
+ end
37
+ end
38
+
39
+ def make_queue(exchange, routing_key)
40
+ queue = exchange.channel.queue(RabbitJobs.config.queue_name(routing_key), RabbitJobs.config[:queues][routing_key])
41
+ queue.bind(exchange, :routing_key => routing_key)
42
+ queue
43
+ end
44
+ end
45
+ end
@@ -0,0 +1,124 @@
1
+ # -*- encoding : utf-8 -*-
2
+ require 'yaml'
3
+
4
+ module RabbitJobs
5
+
6
+ extend self
7
+
8
+ def configure(&block)
9
+ @@configuration ||= Configuration.new
10
+ block.call(@@configuration)
11
+ end
12
+
13
+ def config
14
+ @@configuration ||= load_config
15
+ end
16
+
17
+ def load_config
18
+ self.configure do |c|
19
+ c.host 'localhost'
20
+ c.exchange 'rabbit_jobs', auto_delete: false, durable: true
21
+ c.queue 'default', auto_delete: false, ack: true, durable: true
22
+ end
23
+ @@configuration
24
+ end
25
+
26
+ class Configuration
27
+ include Helpers
28
+
29
+ DEFAULT_QUEUE_PARAMS = {
30
+ auto_delete: false,
31
+ durable: true,
32
+ ack: true
33
+ }
34
+
35
+ DEFAULT_EXCHANGE_PARAMS = {
36
+ auto_delete: false,
37
+ durable: true
38
+ }
39
+
40
+ DEFAULT_MESSAGE_PARAMS = {
41
+ persistent: true,
42
+ nowait: false,
43
+ immediate: false
44
+ }
45
+
46
+ def to_hash
47
+ @data.dup
48
+ end
49
+
50
+ def initialize
51
+ @data = {
52
+ host: 'localhost',
53
+ exchange: 'rabbit_jobs',
54
+ exchange_params: DEFAULT_EXCHANGE_PARAMS,
55
+ queues: {}
56
+ }
57
+ end
58
+
59
+ def [](name)
60
+ @data[name]
61
+ end
62
+
63
+ def host(value = nil)
64
+ if value
65
+ raise ArgumentError unless value.is_a?(String) && value != ""
66
+ @data[:host] = value.to_s
67
+ else
68
+ @data[:host]
69
+ end
70
+ end
71
+
72
+ def exchange(value = nil, params = {})
73
+ if value
74
+ raise ArgumentError unless value.is_a?(String) && value != ""
75
+ @data[:exchange] = value.downcase
76
+ @data[:exchange_params] = DEFAULT_EXCHANGE_PARAMS.merge(params)
77
+ else
78
+ @data[:exchange]
79
+ end
80
+ end
81
+
82
+ def queue(name, params = {})
83
+ raise ArgumentError.new("name is #{name.inspect}") unless name && name.is_a?(String) && name != ""
84
+ raise ArgumentError.new("params is #{params.inspect}") unless params && params.is_a?(Hash)
85
+
86
+ name = name.downcase
87
+
88
+ if @data[:queues][name]
89
+ @data[:queues][name].merge!(params)
90
+ else
91
+ @data[:queues][name] = DEFAULT_QUEUE_PARAMS.merge(params)
92
+ end
93
+ end
94
+
95
+ def routing_keys
96
+ @data[:queues].keys
97
+ end
98
+
99
+ def queue_name(routing_key)
100
+ [@data[:exchange], routing_key].join('#')
101
+ end
102
+
103
+ def load_file(filename)
104
+ load_yaml(File.read(filename))
105
+ end
106
+
107
+ def load_yaml(text)
108
+ convert_yaml_config(YAML.load(text))
109
+ end
110
+
111
+ def convert_yaml_config(yaml)
112
+ if yaml['rabbit_jobs']
113
+ convert_yaml_config(yaml['rabbit_jobs'])
114
+ else
115
+ @data = {host: nil, exchange: nil, queues: {}}
116
+ host yaml['host']
117
+ exchange yaml['exchange'], symbolize_keys!(yaml['exchange_params'])
118
+ yaml['queues'].each do |name, params|
119
+ queue name, symbolize_keys!(params) || {}
120
+ end
121
+ end
122
+ end
123
+ end
124
+ end
@@ -0,0 +1,53 @@
1
+ # -*- encoding : utf-8 -*-
2
+
3
+ module RabbitJobs
4
+ module Helpers
5
+ def symbolize_keys!(hash)
6
+ hash.inject({}) do |options, (key, value)|
7
+ options[(key.to_sym rescue key) || key] = value
8
+ options
9
+ end
10
+ end
11
+
12
+
13
+ # Tries to find a constant with the name specified in the argument string:
14
+ #
15
+ # constantize("Module") # => Module
16
+ # constantize("Test::Unit") # => Test::Unit
17
+ #
18
+ # The name is assumed to be the one of a top-level constant, no matter
19
+ # whether it starts with "::" or not. No lexical context is taken into
20
+ # account:
21
+ #
22
+ # C = 'outside'
23
+ # module M
24
+ # C = 'inside'
25
+ # C # => 'inside'
26
+ # constantize("C") # => 'outside', same as ::C
27
+ # end
28
+ #
29
+ # NameError is raised when the constant is unknown.
30
+ def constantize(camel_cased_word)
31
+ camel_cased_word = camel_cased_word.to_s
32
+
33
+ if camel_cased_word.include?('-')
34
+ camel_cased_word = classify(camel_cased_word)
35
+ end
36
+
37
+ names = camel_cased_word.split('::')
38
+ names.shift if names.empty? || names.first.empty?
39
+
40
+ constant = Object
41
+ names.each do |name|
42
+ args = Module.method(:const_get).arity != 1 ? [false] : []
43
+
44
+ if constant.const_defined?(name, *args)
45
+ constant = constant.const_get(name)
46
+ else
47
+ constant = constant.const_missing(name)
48
+ end
49
+ end
50
+ constant
51
+ end
52
+ end
53
+ end
@@ -0,0 +1,88 @@
1
+ # -*- encoding : utf-8 -*-
2
+ require 'json'
3
+ require 'digest/md5'
4
+
5
+ module RabbitJobs::Job
6
+ extend RabbitJobs::Helpers
7
+ extend RabbitJobs::Logger
8
+ extend self
9
+
10
+ def self.included(base)
11
+ include RabbitJobs::Logger
12
+ base.extend (ClassMethods)
13
+
14
+ def initialize(*perform_params)
15
+ self.params = *perform_params
16
+ self.opts = {}
17
+ end
18
+
19
+ attr_accessor :params, :opts, :child_pid
20
+
21
+ def run_perform
22
+ if @child_pid = fork
23
+ srand # Reseeding
24
+ log "Forked #{@child_pid} at #{Time.now} to process #{self.class}.perform(#{ params.map(&:inspect).join(', ') })"
25
+ Process.wait(@child_pid)
26
+ yield if block_given?
27
+ else
28
+ begin
29
+ # log 'before perform'
30
+ self.class.perform(*params)
31
+ # log 'after perform'
32
+ rescue
33
+ puts $!.inspect
34
+ end
35
+ exit!
36
+ end
37
+ end
38
+
39
+ def payload
40
+ {'class' => self.class.to_s, 'opts' => (self.opts || {}), 'params' => params}.to_json
41
+ # ([self.class.to_s] + params).to_json
42
+ end
43
+
44
+ def expires_in
45
+ self.class.rj_expires_in
46
+ end
47
+
48
+ def expires?
49
+ !!self.expires_in
50
+ end
51
+
52
+ def expired?
53
+ if self.opts['expires_at']
54
+ Time.now > Time.new(opts['expires_at'])
55
+ elsif expires? && opts['created_at']
56
+ Time.now > (Time.new(opts['created_at']) + expires_in)
57
+ else
58
+ false
59
+ end
60
+ end
61
+ end
62
+
63
+ module ClassMethods
64
+ attr_accessor :rj_expires_in
65
+
66
+ # DSL method for jobs
67
+ def expires_in(seconds)
68
+ @rj_expires_in = seconds
69
+ end
70
+ end
71
+
72
+ def self.parse(payload)
73
+ begin
74
+ encoded = JSON.parse(payload)
75
+ job_klass = constantize(encoded['class'])
76
+ job = job_klass.new(*encoded['params'])
77
+ job.opts = encoded['opts']
78
+ job
79
+ rescue
80
+ log "JOB INIT ERROR at #{Time.now.to_s}:"
81
+ log $!.inspect
82
+ log $!.backtrace
83
+ log "message: #{payload.inspect}"
84
+ # Mailer.send(klass_name, params, $!)
85
+ # raise $!
86
+ end
87
+ end
88
+ end
@@ -0,0 +1,18 @@
1
+ module RabbitJobs
2
+ module Logger
3
+ extend self
4
+
5
+ def log(string)
6
+ puts string
7
+ end
8
+
9
+ def log!(string)
10
+ @@verbose ||= false
11
+ log(string) if RabbitJobs::Logger.verbose
12
+ end
13
+
14
+ class << self
15
+ attr_accessor :verbose
16
+ end
17
+ end
18
+ end
@@ -0,0 +1,56 @@
1
+ # -*- encoding : utf-8 -*-
2
+
3
+ require 'json'
4
+ require 'amqp'
5
+ require 'eventmachine'
6
+
7
+ module RabbitJobs
8
+ module Publisher
9
+ extend self
10
+ extend AmqpHelpers
11
+
12
+ def publish(klass, opts = {}, *params)
13
+ key = RabbitJobs.config.routing_keys.first
14
+ publish_to(key, klass, opts, *params)
15
+ end
16
+
17
+ def publish_to(routing_key, klass, opts = {}, *params)
18
+ raise ArgumentError unless klass && routing_key
19
+ opts ||= {}
20
+
21
+ job = klass.new(*params)
22
+ job.opts = opts
23
+
24
+ publish_job_to(routing_key, job)
25
+ end
26
+
27
+ def publish_job_to(routing_key, job)
28
+ amqp_with_exchange do |connection, exchange|
29
+
30
+ queue = make_queue(exchange, routing_key)
31
+
32
+ job.opts['created_at'] = Time.now.to_s
33
+
34
+ payload = job.payload
35
+ exchange.publish(job.payload, Configuration::DEFAULT_MESSAGE_PARAMS.merge({routing_key: routing_key})) {
36
+ connection.close { EM.stop }
37
+ }
38
+ end
39
+ end
40
+
41
+ def purge_queue(routing_key)
42
+ raise ArgumentError unless routing_key
43
+
44
+ amqp_with_queue(routing_key) do |connection, queue|
45
+ queue.status do |number_of_messages, number_of_consumers|
46
+ queue.purge {
47
+ connection.close {
48
+ EM.stop
49
+ return number_of_messages
50
+ }
51
+ }
52
+ end
53
+ end
54
+ end
55
+ end
56
+ end
@@ -0,0 +1,51 @@
1
+ # require 'resque/tasks'
2
+ # will give you the resque tasks
3
+
4
+ namespace :rj do
5
+ task :setup
6
+
7
+ desc "Start a Rabbit Jobs worker"
8
+ task :work => [ :preload, :setup ] do
9
+ require 'rabbit_jobs'
10
+
11
+ queues = (ENV['QUEUES'] || ENV['QUEUE']).to_s.split(',')
12
+
13
+ begin
14
+ worker = RabbitJobs::Worker.new(*queues)
15
+ worker.pidfile = ENV['PIDFILE']
16
+ worker.background = %w(yes true).include? ENV['BACKGROUND']
17
+ RabbitJobs::Logger.verbose = true if ENV['VERBOSE']
18
+ # worker.very_verbose = ENV['VVERBOSE']
19
+ end
20
+
21
+ # worker.log "Starting worker #{worker.pid}"
22
+ # worker.verbose = true
23
+ worker.work 10
24
+ # worker.work(ENV['INTERVAL'] || 5) # interval, will block
25
+ end
26
+
27
+ desc "Start multiple Resque workers. Should only be used in dev mode."
28
+ task :workers do
29
+ threads = []
30
+
31
+ ENV['COUNT'].to_i.times do
32
+ threads << Thread.new do
33
+ system "rake resque:work"
34
+ end
35
+ end
36
+
37
+ threads.each { |thread| thread.join }
38
+ end
39
+
40
+ # Preload app files if this is Rails
41
+ task :preload => :setup do
42
+ if defined?(Rails) && Rails.respond_to?(:application)
43
+ # Rails 3
44
+ Rails.application.eager_load!
45
+ elsif defined?(Rails::Initializer)
46
+ # Rails 2.3
47
+ $rails_rake_task = false
48
+ Rails::Initializer.run :load_application_classes
49
+ end
50
+ end
51
+ end
@@ -0,0 +1,3 @@
1
+ module RabbitJobs
2
+ VERSION = "0.0.4.1"
3
+ end
@@ -0,0 +1,119 @@
1
+ # -*- encoding : utf-8 -*-
2
+
3
+ module RabbitJobs
4
+ class Worker
5
+ include AmqpHelpers
6
+ include Logger
7
+
8
+ attr_accessor :pidfile, :background
9
+
10
+ # Workers should be initialized with an array of string queue
11
+ # names. The order is important: a Worker will check the first
12
+ # queue given for a job. If none is found, it will check the
13
+ # second queue name given. If a job is found, it will be
14
+ # processed. Upon completion, the Worker will again check the
15
+ # first queue given, and so forth. In this way the queue list
16
+ # passed to a Worker on startup defines the priorities of queues.
17
+ #
18
+ # If passed a single "*", this Worker will operate on all queues
19
+ # in alphabetical order. Queues can be dynamically added or
20
+ # removed without needing to restart workers using this method.
21
+ def initialize(*queues)
22
+ @queues = queues.map { |queue| queue.to_s.strip }.flatten.uniq
23
+ if @queues == ['*'] || @queues.empty?
24
+ @queues = RabbitJobs.config.routing_keys
25
+ end
26
+ end
27
+
28
+ def queues
29
+ @queues || ['default']
30
+ end
31
+
32
+ # Subscribes to channel and working on jobs
33
+ def work(time = 0)
34
+ startup
35
+
36
+ processed_count = 0
37
+ amqp_with_exchange do |connection, exchange|
38
+ exchange.channel.prefetch(1)
39
+
40
+ check_shutdown = Proc.new {
41
+ if @shutdown
42
+ log "Processed jobs: #{processed_count}"
43
+ log "Stopping worker..."
44
+
45
+ connection.close {
46
+ File.delete(self.pidfile) if self.pidfile
47
+ EM.stop { exit! }
48
+ }
49
+ end
50
+ }
51
+
52
+ queues.each do |routing_key|
53
+ queue = make_queue(exchange, routing_key)
54
+
55
+ log "Worker ##{Process.pid} <= #{exchange.name}##{routing_key}"
56
+
57
+ queue.subscribe(ack: true) do |metadata, payload|
58
+ @job = RabbitJobs::Job.parse(payload)
59
+ @job.run_perform unless @job.expired?
60
+ metadata.ack
61
+ processed_count += 1
62
+ check_shutdown.call
63
+ end
64
+ end
65
+
66
+ if time > 0
67
+ # for debugging
68
+ EM.add_timer(time) do
69
+ self.shutdown
70
+ end
71
+ end
72
+
73
+ EM.add_periodic_timer(1) do
74
+ check_shutdown.call
75
+ end
76
+ end
77
+ end
78
+
79
+ def shutdown
80
+ @shutdown = true
81
+ end
82
+
83
+ def startup
84
+ # prune_dead_workers
85
+
86
+ Process.daemon(true) if self.background
87
+
88
+ if self.pidfile
89
+ File.open(self.pidfile, 'w') { |f| f << Process.pid }
90
+ end
91
+
92
+ # Fix buffering so we can `rake rj:work > resque.log` and
93
+ # get output from the child in there.
94
+ $stdout.sync = true
95
+
96
+ @shutdown = false
97
+
98
+ Signal.trap('TERM') { shutdown }
99
+ Signal.trap('INT') { shutdown! }
100
+ end
101
+
102
+ def shutdown!
103
+ shutdown
104
+ kill_child
105
+ end
106
+
107
+ def kill_child
108
+ if @job && @job.child_pid
109
+ # log! "Killing child at #{@child}"
110
+ if Kernel.system("ps -o pid,state -p #{@job.child_pid}")
111
+ Process.kill("KILL", @job.child_pid) rescue nil
112
+ else
113
+ # log! "Child #{@child} not found, restarting."
114
+ # shutdown
115
+ end
116
+ end
117
+ end
118
+ end
119
+ end
@@ -0,0 +1,3 @@
1
+
2
+ $LOAD_PATH.unshift File.dirname(__FILE__) + '/../../lib'
3
+ require 'rabbit_jobs/tasks'
@@ -0,0 +1,22 @@
1
+ # -*- encoding: utf-8 -*-
2
+ $:.push File.expand_path("../lib", __FILE__)
3
+ require 'rabbit_jobs/version'
4
+
5
+ Gem::Specification.new do |gem|
6
+ gem.authors = ["Pavel Lazureykis"]
7
+ gem.email = ["lazureykis@gmail.com"]
8
+ gem.description = %q{Background jobs on RabbitMQ}
9
+ gem.summary = %q{Background jobs on RabbitMQ}
10
+ gem.homepage = ""
11
+ gem.date = Time.now.strftime('%Y-%m-%d')
12
+
13
+ gem.executables = `git ls-files -- bin/*`.split("\n").map{ |f| File.basename(f) }
14
+ gem.files = `git ls-files`.split("\n")
15
+ gem.test_files = `git ls-files -- {test,spec,features}/*`.split("\n")
16
+ gem.name = "rj"
17
+ gem.require_paths = ["lib"]
18
+ gem.version = RabbitJobs::VERSION
19
+
20
+ gem.add_dependency "amqp", "~> 0.9"
21
+ gem.add_dependency "rake"
22
+ end
@@ -0,0 +1,17 @@
1
+ rabbit_jobs:
2
+ host: example.com
3
+ exchange: 'my_exchange'
4
+ exchange_params:
5
+ durable: true
6
+ auto_delete: false
7
+ queues:
8
+ durable_queue:
9
+ durable: true
10
+ auto_delete: false
11
+ ack: true
12
+ arguments:
13
+ x-ha-policy: all
14
+ fast_queue:
15
+ durable: false
16
+ auto_delete: true
17
+ ack: false
@@ -0,0 +1,29 @@
1
+ # -*- encoding : utf-8 -*-
2
+
3
+ class TestJob
4
+ include RabbitJobs::Job
5
+ end
6
+
7
+ class PrintTimeJob
8
+ include RabbitJobs::Job
9
+
10
+ def self.perform(time)
11
+ puts "Running job queued at #{time}"
12
+ end
13
+ end
14
+
15
+ class JobWithExpire
16
+ include RabbitJobs::Job
17
+ expires_in 60*60 # expires in 1 hour
18
+ def self.perform
19
+
20
+ end
21
+ end
22
+
23
+ class ExpiredJob
24
+ include RabbitJobs::Job
25
+
26
+ def self.perform
27
+
28
+ end
29
+ end
@@ -0,0 +1,19 @@
1
+ # -*- encoding : utf-8 -*-
2
+ require 'spec_helper'
3
+ require 'json'
4
+
5
+ describe RabbitJobs::Publisher do
6
+
7
+ before(:each) do
8
+ queue_name = 'test'
9
+ RabbitJobs.configure do |c|
10
+ c.exchange 'test'
11
+ c.queue 'rspec_queue'
12
+ end
13
+ end
14
+
15
+ it 'should publish message to queue' do
16
+ RabbitJobs.publish(TestJob, nil, 'some', 'other', 'params')
17
+ RabbitJobs::Publisher.purge_queue('rspec_queue').should == 1
18
+ end
19
+ end
@@ -0,0 +1,18 @@
1
+ # -*- encoding : utf-8 -*-
2
+ require 'spec_helper'
3
+
4
+ require 'eventmachine'
5
+ describe RabbitJobs::Worker do
6
+ it 'should listen for messages' do
7
+ RabbitJobs.configure do |c|
8
+ c.exchange 'test_durable', auto_delete: false, durable: true
9
+ c.queue 'rspec_durable_queue', auto_delete: false, durable: true, ack: true
10
+ end
11
+
12
+ 5.times { RabbitJobs.publish(PrintTimeJob, nil, Time.now) }
13
+ 5.times { RabbitJobs.publish(ExpiredJob, { :expires_at => Time.now - 10 }) }
14
+ worker = RabbitJobs::Worker.new
15
+
16
+ worker.work(1) # work for 1 second
17
+ end
18
+ end
@@ -0,0 +1,22 @@
1
+ # -*- encoding : utf-8 -*-
2
+ require 'simplecov'
3
+ SimpleCov.start do
4
+ add_filter "spec" # ignore spec files
5
+ end
6
+
7
+ require 'rr'
8
+
9
+ require 'rabbit_jobs'
10
+
11
+ require 'fixtures/jobs'
12
+
13
+ RSpec.configure do |config|
14
+ config.mock_with :rr
15
+ # or if that doesn't work due to a version incompatibility
16
+ # config.mock_with RR::Adapters::Rspec
17
+
18
+ config.before(:each) do
19
+ # clear config options
20
+ RabbitJobs.class_variable_set '@@configuration', nil
21
+ end
22
+ end
@@ -0,0 +1,89 @@
1
+ # -*- encoding : utf-8 -*-
2
+ require 'spec_helper'
3
+
4
+ describe RabbitJobs::Configuration do
5
+ it 'builds configuration from configure block' do
6
+ RabbitJobs.configure do |c|
7
+ c.host "somehost.lan"
8
+
9
+ c.exchange 'my_exchange', durable: true, auto_delete: false
10
+
11
+ c.queue 'durable_queue', durable: true, auto_delete: false, ack: true, arguments: {'x-ha-policy' => 'all'}
12
+ c.queue 'fast_queue', durable: false, auto_delete: true, ack: false
13
+ end
14
+
15
+ RabbitJobs.config.to_hash.should == {
16
+ host: "somehost.lan",
17
+ exchange: "my_exchange",
18
+ exchange_params: {
19
+ durable: true,
20
+ auto_delete: false
21
+ },
22
+ queues: {
23
+ "durable_queue" => {
24
+ durable: true,
25
+ auto_delete: false,
26
+ ack: true,
27
+ arguments: {"x-ha-policy"=>"all"}
28
+ },
29
+ "fast_queue" => {
30
+ durable: false,
31
+ auto_delete: true,
32
+ ack: false
33
+ },
34
+ }
35
+ }
36
+ end
37
+
38
+ it 'builds configuration from yaml' do
39
+ RabbitJobs.config.load_file(File.expand_path('../../fixtures/config.yml', __FILE__))
40
+
41
+ RabbitJobs.config.to_hash.should == {
42
+ host: "example.com",
43
+ exchange: "my_exchange",
44
+ exchange_params: {
45
+ durable: true,
46
+ auto_delete: false
47
+ },
48
+ queues: {
49
+ "durable_queue" => {
50
+ durable: true,
51
+ auto_delete: false,
52
+ ack: true,
53
+ arguments: {"x-ha-policy"=>"all"}
54
+ },
55
+ "fast_queue" => {
56
+ durable: false,
57
+ auto_delete: true,
58
+ ack: false
59
+ }
60
+ }
61
+ }
62
+ end
63
+
64
+ it 'use default config' do
65
+ RabbitJobs.config.to_hash.should == {
66
+ host: "localhost",
67
+ exchange: "rabbit_jobs",
68
+ exchange_params: {
69
+ auto_delete: false,
70
+ durable: true
71
+ },
72
+ queues: {
73
+ "default" => {
74
+ auto_delete: false,
75
+ ack: true,
76
+ durable: true
77
+ }
78
+ }
79
+ }
80
+ end
81
+
82
+ it 'returns settings on some methods' do
83
+ RabbitJobs.config.host.should == 'localhost'
84
+ RabbitJobs.config[:host].should == 'localhost'
85
+ RabbitJobs.config.routing_keys.should == ['default']
86
+ RabbitJobs.config.exchange.should == 'rabbit_jobs'
87
+ RabbitJobs.config.queue_name('default').should == 'rabbit_jobs#default'
88
+ end
89
+ end
@@ -0,0 +1,29 @@
1
+ # -*- encoding : utf-8 -*-
2
+ require 'spec_helper'
3
+
4
+ describe RabbitJobs::Job do
5
+ it 'should parse class and params' do
6
+ job = RabbitJobs::Job.parse({class: 'TestJob', params: [1,2,3]}.to_json)
7
+ job.params.should == [1, 2, 3]
8
+ end
9
+
10
+ it 'should understand expires_in' do
11
+ job = JobWithExpire.new(1, 2, 3)
12
+ job.expires_in.should == 60*60
13
+ job.expires?.should == true
14
+ end
15
+
16
+ context 'job expiration' do
17
+ it 'should expire job by expires_in option' do
18
+ job = TestJob.new
19
+ job.opts['expires_at'] = (Time.now - 10).to_s
20
+ job.expired?.should == true
21
+ end
22
+
23
+ it 'should expire job by expires_in option in job class and current_time' do
24
+ job = JobWithExpire.new(1, 2, 3)
25
+ job.opts['created_at'] = (Time.now - job.expires_in - 10).to_s
26
+ job.expired?.should == true
27
+ end
28
+ end
29
+ end
@@ -0,0 +1,23 @@
1
+ # -*- encoding : utf-8 -*-
2
+ require 'spec_helper'
3
+
4
+ describe RabbitJobs::Logger do
5
+ it '#log should write messages to stdout' do
6
+ mock($stdout).puts "hello"
7
+ RabbitJobs::Logger.log("hello")
8
+ end
9
+
10
+ it '#log! should not write messages to stdout in normal mode' do
11
+ RabbitJobs::Logger.verbose = false
12
+
13
+ dont_allow(RabbitJobs::Logger).log("hello")
14
+ RabbitJobs::Logger.log!("hello")
15
+ end
16
+
17
+ it '#log! should write messages to stdout in verbose mode' do
18
+ RabbitJobs::Logger.verbose = true
19
+
20
+ mock(RabbitJobs::Logger).log("hello")
21
+ RabbitJobs::Logger.log!("hello")
22
+ end
23
+ end
@@ -0,0 +1,11 @@
1
+ # -*- encoding : utf-8 -*-
2
+
3
+ describe RabbitJobs do
4
+ it 'should pass publish methods to publisher' do
5
+ mock(RabbitJobs::Publisher).publish(TestJob, nil, 1, 2, "string")
6
+ RabbitJobs.publish(TestJob, nil, 1, 2, "string")
7
+
8
+ mock(RabbitJobs::Publisher).publish_to('default_queue', TestJob, nil, 1, 2, "string")
9
+ RabbitJobs.publish_to('default_queue', TestJob, nil, 1, 2, "string")
10
+ end
11
+ end
@@ -0,0 +1,60 @@
1
+ # -*- encoding : utf-8 -*-
2
+ require 'spec_helper'
3
+
4
+ describe RabbitJobs::Worker do
5
+ describe 'methods' do
6
+ before :each do
7
+ @worker = RabbitJobs::Worker.new
8
+ end
9
+
10
+ it '#initialize with default options' do
11
+ @worker.queues.should == ['default']
12
+ end
13
+
14
+ it '#startup should set @shutdown to false' do
15
+ @worker.instance_variable_get('@shutdown').should_not == true
16
+
17
+ mock(Signal).trap('TERM')
18
+ mock(Signal).trap('INT')
19
+
20
+ @worker.startup
21
+
22
+ @worker.instance_variable_get('@shutdown').should_not == true
23
+ end
24
+
25
+ it '#startup should write process id to file' do
26
+ mock(Signal).trap('TERM')
27
+ mock(Signal).trap('INT')
28
+
29
+ filename = 'test_worker.pid'
30
+ mock(File).open(filename, 'w') {}
31
+ @worker.pidfile = filename
32
+ @worker.startup
33
+ @worker.pidfile.should == filename
34
+ end
35
+
36
+ it '#shutdown should set @shutdown to true' do
37
+ @worker.instance_variable_get('@shutdown').should_not == true
38
+ @worker.shutdown
39
+ @worker.instance_variable_get('@shutdown').should == true
40
+ end
41
+
42
+ it '#shutdown! should kill child process' do
43
+ mock(@worker.kill_child)
44
+ mock(@worker.shutdown)
45
+
46
+ @worker.shutdown!
47
+ end
48
+
49
+ it '#kill_child' do
50
+ job = TestJob.new()
51
+ job.instance_variable_set '@child_pid', 123123
52
+ @worker.instance_variable_set('@job', job)
53
+
54
+ mock(Kernel).system("ps -o pid,state -p #{123123}") { true }
55
+ mock(Process).kill("KILL", 123123)
56
+
57
+ @worker.kill_child
58
+ end
59
+ end
60
+ end
metadata ADDED
@@ -0,0 +1,106 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: rj
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.0.4.1
5
+ prerelease:
6
+ platform: ruby
7
+ authors:
8
+ - Pavel Lazureykis
9
+ autorequire:
10
+ bindir: bin
11
+ cert_chain: []
12
+ date: 2012-01-31 00:00:00.000000000 Z
13
+ dependencies:
14
+ - !ruby/object:Gem::Dependency
15
+ name: amqp
16
+ requirement: &70272887573820 !ruby/object:Gem::Requirement
17
+ none: false
18
+ requirements:
19
+ - - ~>
20
+ - !ruby/object:Gem::Version
21
+ version: '0.9'
22
+ type: :runtime
23
+ prerelease: false
24
+ version_requirements: *70272887573820
25
+ - !ruby/object:Gem::Dependency
26
+ name: rake
27
+ requirement: &70272887573200 !ruby/object:Gem::Requirement
28
+ none: false
29
+ requirements:
30
+ - - ! '>='
31
+ - !ruby/object:Gem::Version
32
+ version: '0'
33
+ type: :runtime
34
+ prerelease: false
35
+ version_requirements: *70272887573200
36
+ description: Background jobs on RabbitMQ
37
+ email:
38
+ - lazureykis@gmail.com
39
+ executables: []
40
+ extensions: []
41
+ extra_rdoc_files: []
42
+ files:
43
+ - .gitignore
44
+ - .rspec
45
+ - Gemfile
46
+ - LICENSE
47
+ - README.md
48
+ - Rakefile
49
+ - examples/configuration.rb
50
+ - lib/rabbit_jobs.rb
51
+ - lib/rabbit_jobs/amqp_helpers.rb
52
+ - lib/rabbit_jobs/configuration.rb
53
+ - lib/rabbit_jobs/helpers.rb
54
+ - lib/rabbit_jobs/job.rb
55
+ - lib/rabbit_jobs/logger.rb
56
+ - lib/rabbit_jobs/publisher.rb
57
+ - lib/rabbit_jobs/tasks.rb
58
+ - lib/rabbit_jobs/version.rb
59
+ - lib/rabbit_jobs/worker.rb
60
+ - lib/tasks/rabbit_jobs.rake
61
+ - rabbit_jobs.gemspec
62
+ - spec/fixtures/config.yml
63
+ - spec/fixtures/jobs.rb
64
+ - spec/integration/publisher_spec.rb
65
+ - spec/integration/worker_spec.rb
66
+ - spec/spec_helper.rb
67
+ - spec/unit/configuration_spec.rb
68
+ - spec/unit/job_spec.rb
69
+ - spec/unit/logger_spec.rb
70
+ - spec/unit/rabbit_jobs_spec.rb
71
+ - spec/unit/worker_spec.rb
72
+ homepage: ''
73
+ licenses: []
74
+ post_install_message:
75
+ rdoc_options: []
76
+ require_paths:
77
+ - lib
78
+ required_ruby_version: !ruby/object:Gem::Requirement
79
+ none: false
80
+ requirements:
81
+ - - ! '>='
82
+ - !ruby/object:Gem::Version
83
+ version: '0'
84
+ required_rubygems_version: !ruby/object:Gem::Requirement
85
+ none: false
86
+ requirements:
87
+ - - ! '>='
88
+ - !ruby/object:Gem::Version
89
+ version: '0'
90
+ requirements: []
91
+ rubyforge_project:
92
+ rubygems_version: 1.8.15
93
+ signing_key:
94
+ specification_version: 3
95
+ summary: Background jobs on RabbitMQ
96
+ test_files:
97
+ - spec/fixtures/config.yml
98
+ - spec/fixtures/jobs.rb
99
+ - spec/integration/publisher_spec.rb
100
+ - spec/integration/worker_spec.rb
101
+ - spec/spec_helper.rb
102
+ - spec/unit/configuration_spec.rb
103
+ - spec/unit/job_spec.rb
104
+ - spec/unit/logger_spec.rb
105
+ - spec/unit/rabbit_jobs_spec.rb
106
+ - spec/unit/worker_spec.rb