rabbit_jobs 0.0.3 → 0.0.4

Sign up to get free protection for your applications and to get access to all the features.
@@ -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