rabbit_jobs 0.0.3 → 0.0.4

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.
@@ -17,6 +17,13 @@ puts JSON.pretty_generate(RabbitJobs.config.to_hash)
17
17
 
18
18
  puts JSON.pretty_generate(RabbitJobs.config.queues)
19
19
 
20
- # 10.times {
21
- RabbitJobs.enqueue_to('rabbit_jobs_test1', Integer, 'to_s')
22
- # }
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)
@@ -72,7 +72,7 @@ module RabbitJobs
72
72
  def exchange(value = nil, params = {})
73
73
  if value
74
74
  raise ArgumentError unless value.is_a?(String) && value != ""
75
- @data[:exchange] = value
75
+ @data[:exchange] = value.downcase
76
76
  @data[:exchange_params] = DEFAULT_EXCHANGE_PARAMS.merge(params)
77
77
  else
78
78
  @data[:exchange]
@@ -83,6 +83,8 @@ module RabbitJobs
83
83
  raise ArgumentError.new("name is #{name.inspect}") unless name && name.is_a?(String) && name != ""
84
84
  raise ArgumentError.new("params is #{params.inspect}") unless params && params.is_a?(Hash)
85
85
 
86
+ name = name.downcase
87
+
86
88
  if @data[:queues][name]
87
89
  @data[:queues][name].merge!(params)
88
90
  else
@@ -1,37 +1,33 @@
1
1
  # -*- encoding : utf-8 -*-
2
2
  require 'json'
3
+ require 'digest/md5'
3
4
 
4
- module RabbitJobs
5
- class Job
6
- include RabbitJobs::Helpers
7
- include Logger
8
-
9
- attr_accessor :params, :klass, :child
10
-
11
- def initialize(payload)
12
- begin
13
- self.params = JSON.parse(payload)
14
- klass_name = params.delete_at(0)
15
- self.klass = constantize(klass_name)
16
- rescue
17
- log "JOB INIT ERROR at #{Time.now.to_s}:"
18
- log $!.inspect
19
- log $!.backtrace
20
- log "message: #{payload.inspect}"
21
- # Mailer.send(klass_name, params, $!)
22
- end
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 = {}
23
17
  end
24
18
 
25
- def perform
26
- if @child = fork
19
+ attr_accessor :params, :opts, :child_pid
20
+
21
+ def run_perform
22
+ if @child_pid = fork
27
23
  srand # Reseeding
28
- log! "Forked #{@child} at #{Time.now} to process #{klass}.perform(#{ params.map(&:inspect).join(', ') })"
29
- Process.wait(@child)
24
+ log "Forked #{@child_pid} at #{Time.now} to process #{self.class}.perform(#{ params.map(&:inspect).join(', ') })"
25
+ Process.wait(@child_pid)
30
26
  yield if block_given?
31
27
  else
32
28
  begin
33
29
  # log 'before perform'
34
- klass.perform(*params)
30
+ self.class.perform(*params)
35
31
  # log 'after perform'
36
32
  rescue
37
33
  puts $!.inspect
@@ -39,5 +35,54 @@ module RabbitJobs
39
35
  exit!
40
36
  end
41
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
42
87
  end
43
88
  end
@@ -9,37 +9,35 @@ module RabbitJobs
9
9
  extend self
10
10
  extend AmqpHelpers
11
11
 
12
- def enqueue(klass, *params)
12
+ def publish(klass, opts = {}, *params)
13
13
  key = RabbitJobs.config.routing_keys.first
14
- enqueue_to(key, klass, *params)
14
+ publish_to(key, klass, opts, *params)
15
15
  end
16
16
 
17
- def enqueue_to(routing_key, klass, *params)
17
+ def publish_to(routing_key, klass, opts = {}, *params)
18
18
  raise ArgumentError unless klass && routing_key
19
+ opts ||= {}
19
20
 
20
- payload = ([klass.to_s] + params).to_json
21
+ job = klass.new(*params)
22
+ job.opts = opts
21
23
 
24
+ publish_job_to(routing_key, job)
25
+ end
26
+
27
+ def publish_job_to(routing_key, job)
22
28
  amqp_with_exchange do |connection, exchange|
23
29
 
24
30
  queue = make_queue(exchange, routing_key)
25
31
 
26
- exchange.publish(payload, Configuration::DEFAULT_MESSAGE_PARAMS.merge({routing_key: routing_key})) {
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})) {
27
36
  connection.close { EM.stop }
28
37
  }
29
38
  end
30
39
  end
31
40
 
32
- # def spam
33
- # payload = ([klass.to_s] + params).to_json
34
-
35
- # amqp_with_exchange do |connection, exchange|
36
- # 10000.times { |i| RabbitJobs.enqueue(RabbitJobs::TestJob, i) }
37
- # exchange.publish(payload, RabbitJobs.config.publish_params.merge({routing_key: routing_key})) {
38
- # connection.close { EM.stop }
39
- # }
40
- # end
41
- # end
42
-
43
41
  def purge_queue(routing_key)
44
42
  raise ArgumentError unless routing_key
45
43
 
@@ -12,21 +12,15 @@ namespace :rj do
12
12
 
13
13
  begin
14
14
  worker = RabbitJobs::Worker.new(*queues)
15
+ worker.pidfile = ENV['PIDFILE']
16
+ worker.background = %w(yes true).include? ENV['BACKGROUND']
15
17
  RabbitJobs::Logger.verbose = true if ENV['VERBOSE']
16
18
  # worker.very_verbose = ENV['VVERBOSE']
17
19
  end
18
20
 
19
- if ENV['BACKGROUND']
20
- Process.daemon(true)
21
- end
22
-
23
- if ENV['PIDFILE']
24
- File.open(ENV['PIDFILE'], 'w') { |f| f << worker.pid }
25
- end
26
-
27
21
  # worker.log "Starting worker #{worker.pid}"
28
22
  # worker.verbose = true
29
- worker.work 1
23
+ worker.work 10
30
24
  # worker.work(ENV['INTERVAL'] || 5) # interval, will block
31
25
  end
32
26
 
@@ -1,3 +1,3 @@
1
1
  module RabbitJobs
2
- VERSION = "0.0.3"
2
+ VERSION = "0.0.4"
3
3
  end
@@ -5,6 +5,8 @@ module RabbitJobs
5
5
  include AmqpHelpers
6
6
  include Logger
7
7
 
8
+ attr_accessor :pidfile, :background
9
+
8
10
  # Workers should be initialized with an array of string queue
9
11
  # names. The order is important: a Worker will check the first
10
12
  # queue given for a job. If none is found, it will check the
@@ -28,7 +30,7 @@ module RabbitJobs
28
30
  end
29
31
 
30
32
  # Subscribes to channel and working on jobs
31
- def work(time = 10)
33
+ def work(time = 0)
32
34
  startup
33
35
 
34
36
  processed_count = 0
@@ -39,7 +41,11 @@ module RabbitJobs
39
41
  if @shutdown
40
42
  log "Processed jobs: #{processed_count}"
41
43
  log "Stopping worker..."
42
- connection.close { EM.stop { exit! } }
44
+
45
+ connection.close {
46
+ File.delete(self.pidfile) if self.pidfile
47
+ EM.stop { exit! }
48
+ }
43
49
  end
44
50
  }
45
51
 
@@ -49,16 +55,19 @@ module RabbitJobs
49
55
  log "Worker ##{Process.pid} <= #{exchange.name}##{routing_key}"
50
56
 
51
57
  queue.subscribe(ack: true) do |metadata, payload|
52
- @job = Job.new(payload)
53
- @job.perform
58
+ @job = RabbitJobs::Job.parse(payload)
59
+ @job.run_perform unless @job.expired?
54
60
  metadata.ack
55
61
  processed_count += 1
56
62
  check_shutdown.call
57
63
  end
58
64
  end
59
65
 
60
- EM.add_timer(time) do
61
- self.shutdown
66
+ if time > 0
67
+ # for debugging
68
+ EM.add_timer(time) do
69
+ self.shutdown
70
+ end
62
71
  end
63
72
 
64
73
  EM.add_periodic_timer(1) do
@@ -74,6 +83,12 @@ module RabbitJobs
74
83
  def startup
75
84
  # prune_dead_workers
76
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
+
77
92
  # Fix buffering so we can `rake rj:work > resque.log` and
78
93
  # get output from the child in there.
79
94
  $stdout.sync = true
@@ -90,10 +105,10 @@ module RabbitJobs
90
105
  end
91
106
 
92
107
  def kill_child
93
- if @job && @job.child
108
+ if @job && @job.child_pid
94
109
  # log! "Killing child at #{@child}"
95
- if Kernel.system("ps -o pid,state -p #{@job.child}")
96
- Process.kill("KILL", @job.child) rescue nil
110
+ if Kernel.system("ps -o pid,state -p #{@job.child_pid}")
111
+ Process.kill("KILL", @job.child_pid) rescue nil
97
112
  else
98
113
  # log! "Child #{@child} not found, restarting."
99
114
  # shutdown
data/lib/rabbit_jobs.rb CHANGED
@@ -14,23 +14,11 @@ require 'rabbit_jobs/worker'
14
14
  module RabbitJobs
15
15
  extend self
16
16
 
17
- def enqueue(klass, *params)
18
- RabbitJobs::Publisher.enqueue(klass, *params)
17
+ def publish(klass, opts = {}, *params)
18
+ RabbitJobs::Publisher.publish(klass, opts, *params)
19
19
  end
20
20
 
21
- def enqueue_to(routing_key, klass, *params)
22
- RabbitJobs::Publisher.enqueue_to(routing_key, klass, *params)
23
- end
24
-
25
- class TestJob < RabbitJobs::Job
26
- def self.perform(*params)
27
- # puts "processing in job: " + params.inspect
28
- # sleep 0.1
29
-
30
- # if rand(3) == 0
31
- # puts "ERROR TEXT"
32
- # raise "ERROR TEXT"
33
- # end
34
- end
21
+ def publish_to(routing_key, klass, opts = {}, *params)
22
+ RabbitJobs::Publisher.publish_to(routing_key, klass, opts, *params)
35
23
  end
36
24
  end
data/rabbit_jobs.gemspec CHANGED
@@ -18,4 +18,5 @@ Gem::Specification.new do |gem|
18
18
  gem.version = RabbitJobs::VERSION
19
19
 
20
20
  gem.add_dependency "amqp", "~> 0.9"
21
+ gem.add_dependency "rake"
21
22
  end
@@ -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
@@ -3,15 +3,17 @@ require 'spec_helper'
3
3
  require 'json'
4
4
 
5
5
  describe RabbitJobs::Publisher do
6
- it 'should publish message to queue' do
7
6
 
7
+ before(:each) do
8
+ queue_name = 'test'
8
9
  RabbitJobs.configure do |c|
9
- # c.host "somehost.lan"
10
- c.exchange 'test', auto_delete: true
11
- c.queue 'rspec_queue', auto_delete: true
10
+ c.exchange 'test'
11
+ c.queue 'rspec_queue'
12
12
  end
13
+ end
13
14
 
14
- RabbitJobs.enqueue(Integer, 'some', 'other', 'params')
15
+ it 'should publish message to queue' do
16
+ RabbitJobs.publish(TestJob, nil, 'some', 'other', 'params')
15
17
  RabbitJobs::Publisher.purge_queue('rspec_queue').should == 1
16
18
  end
17
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
data/spec/spec_helper.rb CHANGED
@@ -1,13 +1,15 @@
1
1
  # -*- encoding : utf-8 -*-
2
2
  require 'simplecov'
3
- SimpleCov.start {
4
- add_filter "spec" # ignore gems
5
- }
3
+ SimpleCov.start do
4
+ add_filter "spec" # ignore spec files
5
+ end
6
6
 
7
7
  require 'rr'
8
8
 
9
9
  require 'rabbit_jobs'
10
10
 
11
+ require 'fixtures/jobs'
12
+
11
13
  RSpec.configure do |config|
12
14
  config.mock_with :rr
13
15
  # or if that doesn't work due to a version incompatibility
@@ -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
@@ -1,11 +1,11 @@
1
1
  # -*- encoding : utf-8 -*-
2
2
 
3
3
  describe RabbitJobs do
4
- it 'should pass enqueue methods to publisher' do
5
- mock(RabbitJobs::Publisher).enqueue(Integer, 1, 2, "string")
6
- RabbitJobs.enqueue(Integer, 1, 2, "string")
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
7
 
8
- mock(RabbitJobs::Publisher).enqueue_to('default_queue', Integer, 1, 2, "string")
9
- RabbitJobs.enqueue_to('default_queue', Integer, 1, 2, "string")
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
10
  end
11
11
  end
@@ -11,7 +11,7 @@ describe RabbitJobs::Worker do
11
11
  @worker.queues.should == ['default']
12
12
  end
13
13
 
14
- it '#startup' do
14
+ it '#startup should set @shutdown to false' do
15
15
  @worker.instance_variable_get('@shutdown').should_not == true
16
16
 
17
17
  mock(Signal).trap('TERM')
@@ -22,6 +22,17 @@ describe RabbitJobs::Worker do
22
22
  @worker.instance_variable_get('@shutdown').should_not == true
23
23
  end
24
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
+
25
36
  it '#shutdown should set @shutdown to true' do
26
37
  @worker.instance_variable_get('@shutdown').should_not == true
27
38
  @worker.shutdown
@@ -36,8 +47,8 @@ describe RabbitJobs::Worker do
36
47
  end
37
48
 
38
49
  it '#kill_child' do
39
- job = RabbitJobs::Job.new(['RabbitJobs'].to_json)
40
- job.instance_variable_set '@child', 123123
50
+ job = TestJob.new()
51
+ job.instance_variable_set '@child_pid', 123123
41
52
  @worker.instance_variable_set('@job', job)
42
53
 
43
54
  mock(Kernel).system("ps -o pid,state -p #{123123}") { true }
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: rabbit_jobs
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.0.3
4
+ version: 0.0.4
5
5
  prerelease:
6
6
  platform: ruby
7
7
  authors:
@@ -9,11 +9,11 @@ authors:
9
9
  autorequire:
10
10
  bindir: bin
11
11
  cert_chain: []
12
- date: 2012-01-27 00:00:00.000000000 Z
12
+ date: 2012-01-31 00:00:00.000000000 Z
13
13
  dependencies:
14
14
  - !ruby/object:Gem::Dependency
15
15
  name: amqp
16
- requirement: &70147046160300 !ruby/object:Gem::Requirement
16
+ requirement: &70340051105620 !ruby/object:Gem::Requirement
17
17
  none: false
18
18
  requirements:
19
19
  - - ~>
@@ -21,7 +21,18 @@ dependencies:
21
21
  version: '0.9'
22
22
  type: :runtime
23
23
  prerelease: false
24
- version_requirements: *70147046160300
24
+ version_requirements: *70340051105620
25
+ - !ruby/object:Gem::Dependency
26
+ name: rake
27
+ requirement: &70340051105000 !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: *70340051105000
25
36
  description: Background jobs on RabbitMQ
26
37
  email:
27
38
  - lazureykis@gmail.com
@@ -49,9 +60,12 @@ files:
49
60
  - lib/tasks/rabbit_jobs.rake
50
61
  - rabbit_jobs.gemspec
51
62
  - spec/fixtures/config.yml
63
+ - spec/fixtures/jobs.rb
52
64
  - spec/integration/publisher_spec.rb
65
+ - spec/integration/worker_spec.rb
53
66
  - spec/spec_helper.rb
54
67
  - spec/unit/configuration_spec.rb
68
+ - spec/unit/job_spec.rb
55
69
  - spec/unit/logger_spec.rb
56
70
  - spec/unit/rabbit_jobs_spec.rb
57
71
  - spec/unit/worker_spec.rb
@@ -81,9 +95,12 @@ specification_version: 3
81
95
  summary: Background jobs on RabbitMQ
82
96
  test_files:
83
97
  - spec/fixtures/config.yml
98
+ - spec/fixtures/jobs.rb
84
99
  - spec/integration/publisher_spec.rb
100
+ - spec/integration/worker_spec.rb
85
101
  - spec/spec_helper.rb
86
102
  - spec/unit/configuration_spec.rb
103
+ - spec/unit/job_spec.rb
87
104
  - spec/unit/logger_spec.rb
88
105
  - spec/unit/rabbit_jobs_spec.rb
89
106
  - spec/unit/worker_spec.rb