rabbit_jobs 0.7.8 → 0.7.9

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.
@@ -1,6 +1,4 @@
1
1
  # -*- encoding : utf-8 -*-
2
- require 'bunny'
3
- require 'uri'
4
2
 
5
3
  module RabbitJobs
6
4
  class AmqpHelper
@@ -8,7 +6,7 @@ module RabbitJobs
8
6
  class << self
9
7
 
10
8
  def prepare_connection
11
- conn = Bunny.new(RJ.config.server, :heartbeat_interval => 5)
9
+ conn = Bunny.new(RabbitJobs.config.server, :heartbeat_interval => 5)
12
10
  conn.start unless conn.connected? || conn.connecting?
13
11
  conn
14
12
  end
@@ -1,7 +1,4 @@
1
1
  # -*- encoding : utf-8 -*-
2
- require 'yaml'
3
- require 'uri'
4
-
5
2
  module RabbitJobs
6
3
 
7
4
  extend self
@@ -28,7 +25,7 @@ module RabbitJobs
28
25
 
29
26
  unless @@configuration
30
27
  self.configure do |c|
31
- c.prefix 'rabbit_jobs'
28
+ c.server "amqp://localhost"
32
29
  end
33
30
  end
34
31
 
@@ -63,8 +60,7 @@ module RabbitJobs
63
60
  def initialize
64
61
  @data = {
65
62
  error_log: true,
66
- server: 'amqp://localhost/',
67
- prefix: 'rabbit_jobs',
63
+ server: 'amqp://localhost',
68
64
  queues: {}
69
65
  }
70
66
  end
@@ -102,15 +98,6 @@ module RabbitJobs
102
98
  @data[:server]
103
99
  end
104
100
 
105
- def prefix(value = nil)
106
- if value
107
- raise ArgumentError unless value.is_a?(String) && value != ""
108
- @data[:prefix] = value.downcase
109
- else
110
- @data[:prefix]
111
- end
112
- end
113
-
114
101
  def queue(name, params = {})
115
102
  raise ArgumentError.new("name is #{name.inspect}") unless name && name.is_a?(String) && name != ""
116
103
  raise ArgumentError.new("params is #{params.inspect}") unless params && params.is_a?(Hash)
@@ -128,11 +115,6 @@ module RabbitJobs
128
115
  @data[:queues].keys
129
116
  end
130
117
 
131
- def queue_name(routing_key)
132
- routing_key = routing_key.to_sym
133
- @data[:queues][routing_key][:ignore_prefix] ? routing_key : [@data[:prefix], routing_key].compact.join('#')
134
- end
135
-
136
118
  def load_file(filename)
137
119
  load_yaml(File.read(filename))
138
120
  end
@@ -142,19 +124,23 @@ module RabbitJobs
142
124
  end
143
125
 
144
126
  def convert_yaml_config(yaml)
145
- if yaml['rabbit_jobs']
146
- convert_yaml_config(yaml['rabbit_jobs'])
147
- elsif defined?(Rails) && yaml[Rails.env.to_s]
148
- convert_yaml_config(yaml[Rails.env.to_s])
149
- else
150
- @data = {prefix: nil, queues: {}}
151
- %w(server prefix mail_errors_to mail_errors_from).each do |m|
152
- self.send(m, yaml[m])
153
- end
154
- yaml['queues'].each do |name, params|
155
- queue name, symbolize_keys!(params) || {}
156
- end
127
+ yaml = parse_environment(yaml)
128
+
129
+ @data = {queues: {}}
130
+ %w(server mail_errors_to mail_errors_from).each do |m|
131
+ self.send(m, yaml[m])
132
+ end
133
+ yaml['queues'].each do |name, params|
134
+ queue name, symbolize_keys!(params) || {}
157
135
  end
158
136
  end
137
+
138
+ private
139
+
140
+ def parse_environment(yaml)
141
+ yaml['rabbit_jobs'] ||
142
+ (defined?(Rails) && yaml[Rails.env.to_s]) ||
143
+ yaml
144
+ end
159
145
  end
160
146
  end
@@ -3,9 +3,10 @@ module RabbitJobs
3
3
  module Consumer
4
4
  class JobConsumer
5
5
  def process_message(delivery_info, properties, payload)
6
- job = RJ::Job.parse(payload)
6
+ job, *error_args = RJ::Job.parse(payload)
7
7
 
8
8
  if job.is_a?(Symbol)
9
+ report_error(job, *error_args)
9
10
  # case @job
10
11
  # when :not_found
11
12
  # when :parsing_error
@@ -23,5 +24,25 @@ module RabbitJobs
23
24
  true
24
25
  end
25
26
  end
27
+
28
+ def log_error(msg)
29
+ RJ.logger.error msg
30
+ end
31
+
32
+ def report_error(error_type, *args)
33
+ case error_type
34
+ when :not_found
35
+ log_error "Cannot find job class '#{args.first}'"
36
+ when :parsing_error
37
+ log_error "Cannot initialize job. Json parsing error."
38
+ log_error "Data received: #{args.first.inspect}"
39
+ when :error
40
+ ex, payload = args
41
+ log_error "Cannot initialize job."
42
+ log_error ex.message
43
+ log_error _cleanup_backtrace(ex.backtrace).join("\n")
44
+ log_error "Data received: #{payload.inspect}"
45
+ end
46
+ end
26
47
  end
27
48
  end
@@ -1,35 +1,47 @@
1
1
  # -*- encoding : utf-8 -*-
2
2
  module RabbitJobs
3
3
  class ErrorMailer
4
- def self.enabled?
5
- !!(
6
- defined?(ActionMailer) && \
7
- RabbitJobs.config.mail_errors_from && !RabbitJobs.config.mail_errors_from.empty? && \
8
- RabbitJobs.config.mail_errors_to && !RabbitJobs.config.mail_errors_to.empty?
9
- )
10
- end
4
+ class << self
5
+ def enabled?
6
+ defined?(ActionMailer) && config_present?
7
+ end
8
+
9
+ def send_letter(subject, body)
10
+ letter = ActionMailer::Base.mail
11
+ letter.from = RabbitJobs.config.mail_errors_from
12
+ letter.to = RabbitJobs.config.mail_errors_to
13
+ letter.subject = subject
14
+ letter.body = body
15
+
16
+ letter.deliver
17
+ end
11
18
 
12
- def self.report_error(job, error = $!)
13
- return unless enabled?
19
+ def report_error(job, error = $!)
20
+ return unless enabled?
14
21
 
15
- params ||= []
22
+ begin
23
+ subject, text = build_error_message(job, error)
24
+ send_letter(subject, text)
25
+ rescue
26
+ RabbitJobs.logger.error [$!.message, $!.backtrace].flatten.join("\n")
27
+ end
28
+ end
16
29
 
17
- params_str = job.params == [] ? '' : job.params.map { |p| p.inspect }.join(', ')
18
- subject = "RJ:Worker: #{error.class} on #{job.class}"
19
- text = "\n#{job.class}.perform(#{params_str})\n"
20
- text += "\n#{error.inspect}\n"
21
- text += "\nBacktrace:\n#{error.backtrace.join("\n")}" if error.backtrace
30
+ private
22
31
 
23
- letter = ActionMailer::Base.mail
24
- letter.from = RabbitJobs.config.mail_errors_from
25
- letter.to = RabbitJobs.config.mail_errors_to
26
- letter.subject = subject
27
- letter.body = text
32
+ def build_error_message(job, error)
33
+ params = job.params || []
28
34
 
29
- begin
30
- letter.deliver
31
- rescue
32
- RJ.logger.error [$!.message, $!.backtrace].flatten.join("\n")
35
+ params_str = params.map { |p| p.inspect }.join(', ')
36
+ subject = "RJ:Worker: #{error.class} on #{job.class}"
37
+ text = "\n#{job.class}.perform(#{params_str})\n"
38
+ text += "\n#{error.inspect}\n"
39
+ text += "\nBacktrace:\n#{error.backtrace.join("\n")}" if error.backtrace
40
+ end
41
+
42
+ def config_present?
43
+ config = RabbitJobs.config
44
+ config.mail_errors_from.present? && config.mail_errors_to.present?
33
45
  end
34
46
  end
35
47
  end
@@ -1,130 +1,138 @@
1
1
  # -*- encoding : utf-8 -*-
2
- require 'json'
3
- require 'digest/md5'
2
+ module RabbitJobs
3
+ module Job
4
+ include RabbitJobs::Helpers
5
+ extend self
4
6
 
5
- module RabbitJobs::Job
6
- extend RabbitJobs::Helpers
7
- extend self
7
+ def self.included(base)
8
+ base.extend (ClassMethods)
8
9
 
9
- def self.included(base)
10
- base.extend (ClassMethods)
10
+ def initialize(*perform_params)
11
+ self.params = perform_params
12
+ self.opts = {}
13
+ end
11
14
 
12
- def initialize(*perform_params)
13
- self.params = perform_params
14
- self.opts = {}
15
- end
15
+ attr_accessor :params, :opts
16
+
17
+ def run_perform
18
+ begin
19
+ start_time = Time.now
20
+ RabbitJobs.logger.info "Started to perform #{self.to_ruby_string}"
21
+ self.class.perform(*params)
22
+ execution_time = Time.now - start_time
23
+ RabbitJobs.logger.info " Job completed #{self.to_ruby_string} in #{execution_time} seconds."
24
+ rescue
25
+ log_job_error($!)
26
+ run_on_error_hooks($!)
27
+ end
28
+ end
16
29
 
17
- attr_accessor :params, :opts
30
+ def run_on_error_hooks(error)
31
+ if self.class.rj_on_error_hooks
32
+ self.class.rj_on_error_hooks.each do |proc_or_symbol|
33
+ proc = proc_or_symbol
34
+ if proc_or_symbol.is_a?(Symbol)
35
+ proc = self.method(proc_or_symbol)
36
+ end
37
+
38
+ case proc.arity
39
+ when 0
40
+ proc.call()
41
+ when 1
42
+ proc.call(error)
43
+ else
44
+ proc.call(error, *params)
45
+ end
46
+ end
47
+ end
48
+ end
18
49
 
19
- def run_perform
20
- begin
21
- start_time = Time.now
22
- RJ.logger.info "Started to perform #{self.to_ruby_string}"
23
- self.class.perform(*params)
24
- execution_time = Time.now - start_time
25
- RJ.logger.info " Job completed #{self.to_ruby_string} in #{execution_time} seconds."
26
- rescue
27
- RJ.logger.warn $!.message
28
- RJ.logger.warn(self.to_ruby_string)
29
- RJ.logger.warn _cleanup_backtrace($!.backtrace).join("\n")
30
- run_on_error_hooks($!)
31
- RabbitJobs::ErrorMailer.report_error(self, $!)
50
+ def payload
51
+ {'class' => self.class.to_s, 'opts' => (self.opts || {}), 'params' => params}.to_json
32
52
  end
33
- end
34
53
 
35
- def run_on_error_hooks(error)
36
- if self.class.rj_on_error_hooks
37
- self.class.rj_on_error_hooks.each do |proc_or_symbol|
38
- proc = proc_or_symbol
39
- if proc_or_symbol.is_a?(Symbol)
40
- proc = self.method(proc_or_symbol)
41
- end
54
+ def expires_in
55
+ self.class.rj_expires_in
56
+ end
42
57
 
43
- case proc.arity
44
- when 0
45
- proc.call()
46
- when 1
47
- proc.call(error)
48
- else
49
- proc.call(error, *params)
50
- end
51
- end
58
+ def expires?
59
+ self.expires_in && self.expires_in > 0
52
60
  end
53
- end
54
61
 
55
- def payload
56
- {'class' => self.class.to_s, 'opts' => (self.opts || {}), 'params' => params}.to_json
57
- end
62
+ def expires_at_expired?
63
+ expires_at = opts['expires_at'].to_i
64
+ return false if expires_at == 0
65
+ Time.now.to_i > expires_at
66
+ end
58
67
 
59
- def expires_in
60
- self.class.rj_expires_in
61
- end
68
+ def expires_in_expired?
69
+ exp_in = self.expires_in.to_i
70
+ created_at = opts['created_at'].to_i
71
+ return false if exp_in == 0 || created_at == 0
62
72
 
63
- def expires?
64
- self.expires_in && self.expires_in > 0
65
- end
73
+ Time.now.to_i > created_at + exp_in
74
+ end
66
75
 
67
- def expired?
68
- if self.opts['expires_at']
69
- Time.now.to_i > opts['expires_at'].to_i
70
- elsif expires? && opts['created_at']
71
- Time.now.to_i > (opts['created_at'].to_i + expires_in.to_i)
72
- else
73
- false
76
+ def expired?
77
+ expires_at_expired? || expires_in_expired?
74
78
  end
75
- end
76
79
 
77
- def to_ruby_string
78
- rs = self.class.name
79
- if params.count > 0
80
- rs << "("
81
- rs << params.map(&:to_s).join(", ")
82
- rs << ")"
80
+ def to_ruby_string
81
+ rs = self.class.name
82
+ rs << params_string
83
+ if opts.count > 0
84
+ rs << ", opts: "
85
+ rs << opts.inspect
86
+ end
83
87
  end
84
- if opts.count > 0
85
- rs << ", opts: "
86
- rs << opts.inspect
88
+
89
+ def params_string
90
+ if params.count > 0
91
+ "(#{params.map(&:to_s).join(", ")})"
92
+ else
93
+ ""
94
+ end
95
+ end
96
+
97
+ def log_job_error(error)
98
+ RabbitJobs.logger.warn error.message
99
+ RabbitJobs.logger.warn(self.to_ruby_string)
100
+ RabbitJobs.logger.warn _cleanup_backtrace(error.backtrace).join("\n")
101
+ RabbitJobs::ErrorMailer.report_error(self, error) rescue nil
87
102
  end
88
103
  end
89
- end
90
104
 
91
- module ClassMethods
92
- attr_accessor :rj_expires_in, :rj_on_error_hooks
105
+ module ClassMethods
106
+ attr_accessor :rj_expires_in, :rj_on_error_hooks
93
107
 
94
- # DSL method for jobs
95
- def expires_in(seconds)
96
- @rj_expires_in = seconds.to_i
97
- end
108
+ # DSL method for jobs
109
+ def expires_in(seconds)
110
+ @rj_expires_in = seconds.to_i
111
+ end
98
112
 
99
- def on_error(*hooks)
100
- hooks.each do |proc_or_symbol|
101
- raise ArgumentError unless proc_or_symbol && ( proc_or_symbol.is_a?(Proc) || proc_or_symbol.is_a?(Symbol) )
102
- @rj_on_error_hooks ||= []
103
- @rj_on_error_hooks << proc_or_symbol
113
+ def on_error(*hooks)
114
+ hooks.each do |proc_or_symbol|
115
+ raise ArgumentError unless proc_or_symbol && ( proc_or_symbol.is_a?(Proc) || proc_or_symbol.is_a?(Symbol) )
116
+ @rj_on_error_hooks ||= []
117
+ @rj_on_error_hooks << proc_or_symbol
118
+ end
104
119
  end
105
120
  end
106
- end
107
121
 
108
- def self.parse(payload)
109
- begin
110
- encoded = JSON.parse(payload)
111
- job_klass = constantize(encoded['class'])
112
- job = job_klass.new(*encoded['params'])
113
- job.opts = encoded['opts']
114
- job
115
- rescue NameError
116
- RJ.logger.error "Cannot find job class '#{encoded['class']}'"
117
- :not_found
118
- rescue JSON::ParserError
119
- RJ.logger.error "Cannot initialize job. Json parsing error."
120
- RJ.logger.error "Data received: #{payload.inspect}"
121
- :parsing_error
122
- rescue
123
- RJ.logger.warn "Cannot initialize job."
124
- RJ.logger.warn $!.message
125
- RJ.logger.warn _cleanup_backtrace($!.backtrace).join("\n")
126
- RJ.logger.warn "Data received: #{payload.inspect}"
127
- :error
122
+ def self.parse(payload)
123
+ begin
124
+ encoded = JSON.parse(payload)
125
+ job_klass = constantize(encoded['class'])
126
+ job = job_klass.new(*encoded['params'])
127
+ job.opts = encoded['opts']
128
+ job
129
+ rescue NameError
130
+ [:not_found, encoded['class']]
131
+ rescue JSON::ParserError
132
+ [:parsing_error, payload]
133
+ rescue
134
+ [:error, $!, payload]
135
+ end
128
136
  end
129
137
  end
130
- end
138
+ end
@@ -0,0 +1,40 @@
1
+ # -*- encoding : utf-8 -*-
2
+ module RabbitJobs
3
+ module MainLoop
4
+ def shutdown
5
+ @shutdown = true
6
+ end
7
+
8
+ def shutdown!
9
+ shutdown
10
+ end
11
+
12
+ def main_loop(time)
13
+ while true
14
+ sleep 1
15
+ if time > 0
16
+ time -= 1
17
+ if time == 0
18
+ shutdown
19
+ end
20
+ end
21
+
22
+ if @shutdown
23
+ RabbitJobs.logger.info "Stopping."
24
+ yield if block_given?
25
+ return true
26
+ end
27
+ end
28
+ end
29
+
30
+ def log_daemon_error(error)
31
+ if RabbitJobs.logger
32
+ begin
33
+ RabbitJobs.logger.error [error.message, error.backtrace].flatten.join("\n")
34
+ ensure
35
+ abort(error.message)
36
+ end
37
+ end
38
+ end
39
+ end
40
+ end
@@ -1,11 +1,5 @@
1
1
  # -*- encoding : utf-8 -*-
2
2
 
3
- require 'json'
4
- require 'bunny'
5
- require 'uri'
6
- require 'active_support'
7
- require 'active_support/core_ext/module'
8
-
9
3
  module RabbitJobs
10
4
  module Publisher
11
5
  extend self
@@ -17,8 +11,9 @@ module RabbitJobs
17
11
  end
18
12
 
19
13
  def publish_to(routing_key, klass, *params)
20
- raise ArgumentError.new("klass=#{klass.inspect}") unless klass && (klass.is_a?(Class) || klass.is_a?(String))
21
- raise ArgumentError.new("routing_key=#{routing_key}") unless routing_key && (routing_key.is_a?(Symbol) || routing_key.is_a?(String)) && !!RJ.config[:queues][routing_key.to_sym]
14
+ raise ArgumentError.new("klass=#{klass.inspect}") unless klass.is_a?(Class) || klass.is_a?(String)
15
+ routing_key = routing_key.to_sym unless routing_key.is_a?(Symbol)
16
+ raise ArgumentError.new("routing_key=#{routing_key}") unless RabbitJobs.config[:queues][routing_key]
22
17
 
23
18
  payload = {
24
19
  'class' => klass.to_s,
@@ -26,35 +21,43 @@ module RabbitJobs
26
21
  'params' => params
27
22
  }.to_json
28
23
 
29
- direct_publish_to(RJ.config.queue_name(routing_key.to_sym), payload)
24
+ direct_publish_to(routing_key, payload)
30
25
  end
31
26
 
32
27
  def direct_publish_to(routing_key, payload, ex = {})
33
28
  check_connection
34
29
  begin
35
- exchange = get_exchange(ex)
36
- exchange.publish(payload, Configuration::DEFAULT_MESSAGE_PARAMS.merge({key: routing_key.to_sym}))
37
- # channel.wait_for_confirms
30
+ exchange_name = ex.delete(:name).to_s
31
+ exchange_opts = Configuration::DEFAULT_MESSAGE_PARAMS.merge(ex || {})
32
+ connection.default_channel.basic_publish(payload, exchange_name, routing_key, exchange_opts)
38
33
  rescue
39
- RJ.logger.warn $!.message
40
- RJ.logger.warn $!.backtrace.join("\n")
34
+ RabbitJobs.logger.warn $!.message
35
+ RabbitJobs.logger.warn $!.backtrace.join("\n")
41
36
  raise $!
42
37
  end
43
38
 
44
39
  true
45
40
  end
46
41
 
42
+ def queue_status(routing_key)
43
+ raise ArgumentError unless routing_key.present?
44
+
45
+ routing_key = routing_key.to_sym
46
+ queue_declare_ok = connection.default_channel.queue_declare(routing_key, RabbitJobs.config[:queues][routing_key].merge(passive: true))
47
+ {
48
+ message_count: queue_declare_ok.message_count,
49
+ consumer_count: queue_declare_ok.consumer_count
50
+ }
51
+ end
52
+
47
53
  def purge_queue(*routing_keys)
48
- raise ArgumentError unless routing_keys && routing_keys.count > 0
54
+ raise ArgumentError unless routing_keys.present?
49
55
 
50
56
  messages_count = 0
51
- count = routing_keys.count
52
57
 
53
58
  routing_keys.map(&:to_sym).each do |routing_key|
54
- queue_name = RJ.config.queue_name(routing_key)
55
- queue = connection.queue(queue_name, RJ.config[:queues][routing_key])
56
- messages_count += queue.status[:message_count]
57
- queue.purge
59
+ messages_count += queue_status(routing_key)[:message_count].to_i
60
+ connection.default_channel.queue_purge(routing_key)
58
61
  end
59
62
 
60
63
  messages_count
@@ -68,29 +71,13 @@ module RabbitJobs
68
71
 
69
72
  def connection
70
73
  unless settings[:connection]
71
- settings[:connection] = Bunny.new(RJ.config.server, :heartbeat_interval => 5)
74
+ settings[:connection] = Bunny.new(RabbitJobs.config.server, :heartbeat_interval => 5)
72
75
  settings[:connection].start
73
76
  # settings[:channel].confirm_select
74
77
  end
75
78
  settings[:connection]
76
79
  end
77
80
 
78
- def exchanges
79
- settings[:exchanges] ||= {}
80
- end
81
-
82
- def get_exchange(ex = {})
83
- ex = {name: ex} if ex.is_a?(String)
84
- raise ArgumentError.new("Need to pass exchange name") if ex.size > 0 && ex[:name].to_s.empty?
85
-
86
- if ex.size > 0
87
- exchange_opts = Configuration::DEFAULT_EXCHANGE_PARAMS.merge(ex[:params] || {}).merge({type: (ex[:type] || :direct)})
88
- exchanges[ex[:name]] ||= connection.default_channel.exchange(ex[:name].to_s, exchange_opts)
89
- else
90
- exchanges[ex[:name]] ||= connection.default_channel.default_exchange
91
- end
92
- end
93
-
94
81
  def check_connection
95
82
  raise unless connection.connected?
96
83
  end
@@ -1,19 +1,20 @@
1
1
  # -*- encoding : utf-8 -*-
2
-
3
- require 'rufus/scheduler'
4
- require 'thwait'
5
- require 'yaml'
6
-
7
2
  module RabbitJobs
8
3
  class Scheduler
4
+ include MainLoop
9
5
 
10
- attr_accessor :schedule, :process_name
6
+ attr_reader :schedule, :process_name
7
+ attr_writer :process_name
8
+
9
+ def schedule=(value)
10
+ @schedule = HashWithIndifferentAccess.new(value)
11
+ end
11
12
 
12
13
  def load_default_schedule
13
14
  if defined?(Rails)
14
15
  file = Rails.root.join('config/schedule.yml')
15
16
  if file.file?
16
- @schedule = YAML.load_file(file)
17
+ @schedule = HashWithIndifferentAccess.new(YAML.load_file(file))
17
18
  end
18
19
  end
19
20
  end
@@ -31,21 +32,7 @@ module RabbitJobs
31
32
  # job should be scheduled regardless of what ENV['RAILS_ENV'] is set
32
33
  # to.
33
34
  if config['rails_env'].nil? || rails_env_matches?(config)
34
- interval_defined = false
35
- interval_types = %w{cron every}
36
- interval_types.each do |interval_type|
37
- if !config[interval_type].nil? && config[interval_type].length > 0
38
- RJ.logger.info "queueing #{config['class']} (#{name})"
39
- rufus_scheduler.send(interval_type, config[interval_type], blocking: true) do
40
- publish_from_config(config)
41
- end
42
- interval_defined = true
43
- break
44
- end
45
- end
46
- unless interval_defined
47
- RJ.logger.warn "no #{interval_types.join(' / ')} found for #{config['class']} (#{name}) - skipping"
48
- end
35
+ setup_job_schedule(name, config)
49
36
  end
50
37
  end
51
38
  end
@@ -57,16 +44,15 @@ module RabbitJobs
57
44
 
58
45
  # Publish a job based on a config hash
59
46
  def publish_from_config(config)
60
- args = config['args'] || config[:args] || []
61
- klass_name = config['class'] || config[:class]
62
- params = args.is_a?(Hash) ? [args] : Array(args)
63
- queue = config['queue'] || config[:queue] || RJ.config.routing_keys.first
47
+ args = config[:args] || []
48
+ klass_name = config[:class]
49
+ params = [args].flatten
64
50
 
65
- RJ.logger.info "publishing #{config} at #{Time.now}"
66
- RJ.publish_to(queue, klass_name, *params)
51
+ RabbitJobs.logger.info "publishing #{config} at #{Time.now}"
52
+ RabbitJobs.publish_to(config[:queue], klass_name, *params)
67
53
  rescue
68
- RJ.logger.warn "Failed to publish #{klass_name}:\n #{$!}\n params = #{params.inspect}"
69
- RJ.logger.warn $!.inspect
54
+ RabbitJobs.logger.warn "Failed to publish #{klass_name}:\n #{$!}\n params = #{params.inspect}"
55
+ RabbitJobs.logger.warn $!.inspect
70
56
  end
71
57
 
72
58
  def rufus_scheduler
@@ -88,46 +74,18 @@ module RabbitJobs
88
74
 
89
75
  $0 = self.process_name || "rj_scheduler"
90
76
 
91
- RJ.logger.info "Started."
77
+ RabbitJobs.logger.info "Started."
92
78
 
93
- processed_count = 0
94
79
  load_schedule!
95
80
 
96
- while true
97
- sleep 1
98
- if time > 0
99
- time -= 1
100
- if time == 0
101
- shutdown
102
- end
103
- end
104
-
105
- if @shutdown
106
- RJ.logger.info "Processed jobs: #{processed_count}."
107
- RJ.logger.info "Stopped."
108
-
109
- return true
110
- end
111
- end
112
- rescue => e
113
- error = $!
114
- if RJ.logger
115
- begin
116
- RJ.logger.error [error.message, error.backtrace].flatten.join("\n")
117
- ensure
118
- abort(error.message)
119
- end
120
- end
81
+ return main_loop(time)
82
+ rescue
83
+ log_daemon_error($!)
121
84
  end
122
85
 
123
86
  true
124
87
  end
125
88
 
126
- def shutdown
127
- RJ.logger.info "Stopping..."
128
- @shutdown = true
129
- end
130
-
131
89
  def startup
132
90
  # Fix buffering so we can `rake rj:work > resque.log` and
133
91
  # get output from the child in there.
@@ -141,8 +99,20 @@ module RabbitJobs
141
99
  true
142
100
  end
143
101
 
144
- def shutdown!
145
- shutdown
102
+ def setup_job_schedule(name, config)
103
+ interval_defined = false
104
+ %w(cron every).each do |interval_type|
105
+ if config[interval_type].present?
106
+ RabbitJobs.logger.info "queueing #{config['class']} (#{name})"
107
+ rufus_scheduler.send(interval_type, config[interval_type], blocking: true) do
108
+ publish_from_config(config)
109
+ end
110
+ interval_defined = true
111
+ end
112
+ end
113
+ unless interval_defined
114
+ RabbitJobs.logger.warn "no #{interval_types.join(' / ')} found for #{config['class']} (#{name}) - skipping"
115
+ end
146
116
  end
147
117
  end
148
118
  end
@@ -1,6 +1,4 @@
1
1
  require 'rabbit_jobs'
2
- require 'logger'
3
- require 'rake'
4
2
 
5
3
  def rails_env
6
4
  $my_rails_env ||= defined?(Rails) ? Rails.env : (ENV['RAILS_ENV'] || 'development')
@@ -1,5 +1,5 @@
1
1
  # -*- encoding : utf-8 -*-
2
2
 
3
3
  module RabbitJobs
4
- VERSION = "0.7.8"
4
+ VERSION = "0.7.9"
5
5
  end
@@ -2,12 +2,14 @@
2
2
 
3
3
  module RabbitJobs
4
4
  class Worker
5
+ include MainLoop
6
+
5
7
  attr_accessor :process_name
6
8
  attr_reader :consumer
7
9
 
8
- def consumer=(consumer)
9
- raise ArgumentError.new("consumer=#{consumer.inspect}") unless consumer.respond_to?(:process_message)
10
- @consumer = consumer
10
+ def consumer=(value)
11
+ raise ArgumentError.new("value=#{value.inspect}") unless value.respond_to?(:process_message)
12
+ @consumer = value
11
13
  end
12
14
 
13
15
  def amqp_connection
@@ -20,12 +22,8 @@ module RabbitJobs
20
22
  Thread.current[:rj_worker_connection] = nil
21
23
  end
22
24
 
23
- def queue_name(routing_key)
24
- RJ.config.queue_name(routing_key)
25
- end
26
-
27
25
  def queue_params(routing_key)
28
- RJ.config[:queues][routing_key]
26
+ RJ.config[:queues][routing_key.to_sym]
29
27
  end
30
28
 
31
29
  # Workers should be initialized with an array of string queue
@@ -58,79 +56,28 @@ module RabbitJobs
58
56
 
59
57
  $0 = self.process_name || "rj_worker (#{queues.join(', ')})"
60
58
 
61
- processed_count = 0
59
+ @processed_count = 0
62
60
 
63
61
  begin
64
- # amqp_channel.prefetch(1)
65
-
66
62
  amqp_channel = amqp_connection.create_channel
63
+ amqp_channel.prefetch(1)
67
64
 
68
- queue_objects = []
69
65
  queues.each do |routing_key|
70
- RJ.logger.info "Subscribing to #{queue_name(routing_key)}"
71
-
72
- routing_key = routing_key.to_sym
73
- queue = amqp_channel.queue(queue_name(routing_key), queue_params(routing_key))
74
- queue_objects << queue
75
- explicit_ack = !!queue_params(routing_key)[:ack]
76
-
77
- queue.subscribe(ack: explicit_ack) do |delivery_info, properties, payload|
78
- if RJ.run_before_process_message_callbacks
79
- begin
80
- @consumer.process_message(delivery_info, properties, payload)
81
- processed_count += 1
82
- rescue
83
- RJ.logger.warn "process_message failed. payload: #{payload.inspect}"
84
- RJ.logger.warn $!.inspect
85
- $!.backtrace.each {|l| RJ.logger.warn l}
86
- end
87
- amqp_channel.ack(delivery_info.delivery_tag, false) if explicit_ack
88
- else
89
- RJ.logger.warn "before_process_message hook failed, requeuing payload: #{payload.inspect}"
90
- amqp_channel.nack(delivery_info.delivery_tag, true) if explicit_ack
91
- end
92
-
93
- if @shutdown
94
- queue_objects.each {|q| q.unsubscribe}
95
- end
96
- end
66
+ consume_queue(amqp_channel, routing_key)
97
67
  end
98
68
 
99
69
  RJ.logger.info "Started."
100
70
 
101
- while true
102
- sleep 1
103
- if time > 0
104
- time -= 1
105
- if time == 0
106
- shutdown
107
- end
108
- end
109
-
110
- if @shutdown
111
- RJ.logger.info "Processed jobs: #{processed_count}."
112
- RJ.logger.info "Stopped."
113
- return true
114
- end
115
- end
71
+ return main_loop(time) {
72
+ RJ.logger.info "Processed jobs: #{@processed_count}."
73
+ }
116
74
  rescue
117
- error = $!
118
- if RJ.logger
119
- begin
120
- RJ.logger.error [error.message, error.backtrace].flatten.join("\n")
121
- ensure
122
- abort(error.message)
123
- end
124
- end
75
+ log_daemon_error($!)
125
76
  end
126
77
 
127
78
  true
128
79
  end
129
80
 
130
- def shutdown
131
- @shutdown = true
132
- end
133
-
134
81
  def startup
135
82
  count = RJ._run_after_fork_callbacks
136
83
 
@@ -144,8 +91,38 @@ module RabbitJobs
144
91
  true
145
92
  end
146
93
 
147
- def shutdown!
148
- shutdown
94
+ private
95
+
96
+ def consume_message(delivery_info, properties, payload)
97
+ if RJ.run_before_process_message_callbacks
98
+ begin
99
+ @consumer.process_message(delivery_info, properties, payload)
100
+ @processed_count += 1
101
+ rescue
102
+ RJ.logger.warn "process_message failed. payload: #{payload.inspect}"
103
+ RJ.logger.warn $!.inspect
104
+ $!.backtrace.each {|l| RJ.logger.warn l}
105
+ end
106
+ true
107
+ else
108
+ RJ.logger.warn "before_process_message hook failed, requeuing payload: #{payload.inspect}"
109
+ false
110
+ end
111
+ end
112
+
113
+ def consume_queue(amqp_channel, routing_key)
114
+ RJ.logger.info "Subscribing to #{routing_key}"
115
+ routing_key = routing_key.to_sym
116
+ queue = amqp_channel.queue(routing_key, queue_params(routing_key))
117
+ explicit_ack = !!queue_params(routing_key)[:ack]
118
+
119
+ queue.subscribe(ack: explicit_ack) do |delivery_info, properties, payload|
120
+ if consume_message(delivery_info, properties, payload)
121
+ amqp_channel.ack(delivery_info.delivery_tag, false) if explicit_ack
122
+ else
123
+ amqp_channel.nack(delivery_info.delivery_tag, true) if explicit_ack
124
+ end
125
+ end
149
126
  end
150
127
  end
151
128
  end
data/lib/rabbit_jobs.rb CHANGED
@@ -1,6 +1,15 @@
1
1
  # -*- encoding : utf-8 -*-
2
2
  require 'logger'
3
3
  require 'rake'
4
+ require 'json'
5
+ require 'digest/md5'
6
+ require 'bunny'
7
+ require 'uri'
8
+ require 'rufus/scheduler'
9
+ require 'thwait'
10
+ require 'yaml'
11
+ require 'active_support'
12
+ require 'active_support/core_ext'
4
13
 
5
14
  require 'rabbit_jobs/version'
6
15
 
@@ -12,6 +21,7 @@ require 'rabbit_jobs/error_mailer'
12
21
  require 'rabbit_jobs/consumer/job_consumer'
13
22
  require 'rabbit_jobs/job'
14
23
  require 'rabbit_jobs/publisher'
24
+ require 'rabbit_jobs/main_loop'
15
25
  require 'rabbit_jobs/worker'
16
26
  require 'rabbit_jobs/scheduler'
17
27
  require 'rabbit_jobs/tasks'
@@ -20,15 +30,15 @@ module RabbitJobs
20
30
  extend self
21
31
 
22
32
  def publish_to(routing_key, klass, *params)
23
- RJ::Publisher.publish_to(routing_key, klass, *params)
33
+ Publisher.publish_to(routing_key, klass, *params)
24
34
  end
25
35
 
26
36
  def direct_publish_to(routing_key, payload, ex = {})
27
- RJ::Publisher.direct_publish_to(routing_key, payload, ex)
37
+ Publisher.direct_publish_to(routing_key, payload, ex)
28
38
  end
29
39
 
30
40
  def purge_queue(*routing_keys)
31
- RJ::Publisher.purge_queue(*routing_keys)
41
+ Publisher.purge_queue(*routing_keys)
32
42
  end
33
43
 
34
44
  attr_writer :logger
@@ -1,6 +1,5 @@
1
1
  rabbit_jobs:
2
2
  server: amqp://example.com/vhost
3
- prefix: 'rabbit_jobs'
4
3
  queues:
5
4
  durable_queue:
6
5
  durable: true
@@ -34,7 +34,7 @@ class JobWithPublish
34
34
  def self.perform(param = 0)
35
35
  if param < 5
36
36
  puts "publishing job #{param}"
37
- RJ.publish JobWithPublish, param + 1
37
+ RJ.publish_to :rspec_durable_queue, JobWithPublish, param + 1
38
38
  else
39
39
  puts "processing job #{param}"
40
40
  end
@@ -64,4 +64,9 @@ class JobWithArgsArray
64
64
  def perform(first_param, *other_args)
65
65
  puts "#{self.class.name}.perform called with first_param: #{first_param.inspect} and other_args: #{other_args.inspect}"
66
66
  end
67
+ end
68
+
69
+ class TestConsumer
70
+ def process_message *args
71
+ end
67
72
  end
@@ -43,7 +43,7 @@ describe RabbitJobs::Publisher do
43
43
  end
44
44
 
45
45
  it 'should publish 1000 messages in one second' do
46
- count = 100
46
+ count = 1000
47
47
  published = 0
48
48
  time = Benchmark.measure {
49
49
  count.times {
@@ -51,7 +51,7 @@ describe RabbitJobs::Publisher do
51
51
  }
52
52
  # sleep 0.1
53
53
  removed = RJ.purge_queue(:rspec_queue, :rspec_queue2, :rspec_queue3)
54
- removed.should == 100
54
+ removed.should == 1000
55
55
  }
56
56
  puts time
57
57
  end
@@ -4,39 +4,43 @@ require 'spec_helper'
4
4
  describe RabbitJobs::Worker do
5
5
  it 'should listen for messages' do
6
6
  RabbitJobs.configure do |c|
7
- c.prefix 'test_durable'
7
+ c.server 'amqp://localhost/rj'
8
8
  c.queue 'rspec_durable_queue', auto_delete: false, durable: true, ack: true
9
9
  end
10
10
 
11
11
  RJ::Publisher.purge_queue('rspec_durable_queue')
12
12
  count = 5
13
13
  5.times {
14
- RabbitJobs.publish(PrintTimeJob, Time.now)
14
+ RabbitJobs.publish_to(:rspec_durable_queue, PrintTimeJob, Time.now)
15
15
  }
16
16
 
17
17
  Timecop.freeze(Time.now - 4600) {
18
- 5.times { RabbitJobs.publish(JobWithExpire) }
18
+ 5.times { RabbitJobs.publish_to(:rspec_durable_queue, JobWithExpire) }
19
19
  }
20
20
 
21
- RabbitJobs.publish(JobWithErrorHook)
21
+ RabbitJobs.publish_to(:rspec_durable_queue, JobWithErrorHook)
22
22
 
23
23
  worker = RabbitJobs::Worker.new
24
+ RJ.logger.level = Logger::FATAL
24
25
 
26
+ mock(PrintTimeJob).perform(anything).times(5)
27
+ mock(JobWithErrorHook).perform
28
+ dont_allow(JobWithExpire).perform
25
29
  worker.work(1) # work for 1 second
26
30
  RJ::Publisher.purge_queue('rspec_durable_queue')
27
31
  end
28
32
 
29
33
  it 'should allow to publish jobs from worker' do
30
34
  RabbitJobs.configure do |c|
31
- c.prefix 'test_durable'
35
+ c.server 'amqp://localhost/rj'
32
36
  c.queue 'rspec_durable_queue', auto_delete: false, durable: true, ack: true
33
37
  end
34
38
 
35
39
  RJ::Publisher.purge_queue('rspec_durable_queue')
36
- RabbitJobs.publish(JobWithPublish)
40
+ RabbitJobs.publish_to(:rspec_durable_queue, JobWithPublish, 1)
37
41
 
38
42
  worker = RabbitJobs::Worker.new
39
-
40
- worker.work(1) # work for 1 second
43
+ # mock(RJ).publish_to(:rspec_durable_queue, JobWithPublish, 5)
44
+ worker.work(3) # work for 1 second
41
45
  end
42
46
  end
data/spec/spec_helper.rb CHANGED
@@ -20,4 +20,12 @@ RSpec.configure do |config|
20
20
  # clear config options
21
21
  RabbitJobs.class_variable_set '@@configuration', nil
22
22
  end
23
+
24
+ if ENV['CC_BUILD_ARTIFACTS']
25
+ # "-c -f p -f h -o #{ENV['CC_BUILD_ARTIFACTS']}/rspec_report.html"
26
+ config.out = File.open "#{ENV['CC_BUILD_ARTIFACTS']}/rspec_report.html", 'w'
27
+ # config.color_enabled = true
28
+ # config.formatter = :progress
29
+ config.formatter = :html
30
+ end
23
31
  end
@@ -8,8 +8,6 @@ describe RabbitJobs::Configuration do
8
8
 
9
9
  c.server "amqp://somehost.lan"
10
10
 
11
- c.prefix 'my_prefix'
12
-
13
11
  c.queue 'durable_queue', durable: true, auto_delete: false, ack: true, arguments: {'x-ha-policy' => 'all'}
14
12
  c.queue 'fast_queue', durable: false, auto_delete: true, ack: false
15
13
  end
@@ -17,7 +15,6 @@ describe RabbitJobs::Configuration do
17
15
  RabbitJobs.config.to_hash.should == {
18
16
  error_log: false,
19
17
  server: "amqp://somehost.lan",
20
- prefix: "my_prefix",
21
18
  queues: {
22
19
  durable_queue: {
23
20
  auto_delete: false,
@@ -40,7 +37,6 @@ describe RabbitJobs::Configuration do
40
37
  RabbitJobs.config.load_file(File.expand_path('../../fixtures/config.yml', __FILE__))
41
38
 
42
39
  RabbitJobs.config.to_hash.should == {
43
- prefix: "rabbit_jobs",
44
40
  server: "amqp://example.com/vhost",
45
41
  queues: {
46
42
  durable_queue: {
@@ -60,19 +56,17 @@ describe RabbitJobs::Configuration do
60
56
  }
61
57
  end
62
58
 
63
- it 'use default values for #server and #prefix' do
59
+ it 'use default value for #server' do
64
60
  RabbitJobs.config.to_hash.should == {
65
61
  error_log: true,
66
- server: "amqp://localhost/",
67
- prefix: "rabbit_jobs",
62
+ server: "amqp://localhost",
68
63
  queues: {}
69
64
  }
70
65
  end
71
66
 
72
67
  it 'returns settings on some methods' do
73
68
  RabbitJobs.config.error_log == true
74
- RabbitJobs.config.server.should == 'amqp://localhost/'
69
+ RabbitJobs.config.server.should == 'amqp://localhost'
75
70
  RabbitJobs.config.routing_keys.should == []
76
- RabbitJobs.config.prefix.should == 'rabbit_jobs'
77
71
  end
78
72
  end
@@ -2,30 +2,25 @@
2
2
  require 'spec_helper'
3
3
 
4
4
  describe RabbitJobs::Worker do
5
- describe 'methods' do
6
- before :each do
7
- @worker = RabbitJobs::Worker.new
8
- end
9
5
 
10
- it '#initialize with default options' do
11
- @worker.queues.should == [:default]
6
+ before(:each) do
7
+ RJ.configure do |c|
8
+ c.server 'amqp://localhost'
9
+ c.queue "default"
12
10
  end
11
+ end
13
12
 
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
13
+ let(:worker) { RJ::Worker.new(:default) }
21
14
 
22
- @worker.instance_variable_get('@shutdown').should_not == true
23
- end
15
+ describe '#consumer' do
16
+ it 'validates consumer type' do
17
+ old_consumer = worker.consumer
18
+ lambda { worker.consumer = 123 }.should raise_error
19
+ worker.consumer.should == old_consumer
24
20
 
25
- it '#shutdown should set @shutdown to true' do
26
- @worker.instance_variable_get('@shutdown').should_not == true
27
- @worker.shutdown
28
- @worker.instance_variable_get('@shutdown').should == true
21
+ new_consumer = TestConsumer.new
22
+ lambda { worker.consumer = new_consumer }.should_not raise_error
23
+ worker.consumer.should == new_consumer
29
24
  end
30
25
  end
31
26
  end
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.7.8
4
+ version: 0.7.9
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: 2013-03-22 00:00:00.000000000 Z
12
+ date: 2013-03-25 00:00:00.000000000 Z
13
13
  dependencies:
14
14
  - !ruby/object:Gem::Dependency
15
15
  name: bunny
16
- requirement: &9230940 !ruby/object:Gem::Requirement
16
+ requirement: &17412060 !ruby/object:Gem::Requirement
17
17
  none: false
18
18
  requirements:
19
19
  - - =
@@ -21,10 +21,10 @@ dependencies:
21
21
  version: 0.9.0.pre8
22
22
  type: :runtime
23
23
  prerelease: false
24
- version_requirements: *9230940
24
+ version_requirements: *17412060
25
25
  - !ruby/object:Gem::Dependency
26
26
  name: rake
27
- requirement: &9229700 !ruby/object:Gem::Requirement
27
+ requirement: &17409580 !ruby/object:Gem::Requirement
28
28
  none: false
29
29
  requirements:
30
30
  - - ! '>='
@@ -32,10 +32,10 @@ dependencies:
32
32
  version: '0'
33
33
  type: :runtime
34
34
  prerelease: false
35
- version_requirements: *9229700
35
+ version_requirements: *17409580
36
36
  - !ruby/object:Gem::Dependency
37
37
  name: rufus-scheduler
38
- requirement: &9349960 !ruby/object:Gem::Requirement
38
+ requirement: &17800900 !ruby/object:Gem::Requirement
39
39
  none: false
40
40
  requirements:
41
41
  - - ~>
@@ -43,10 +43,10 @@ dependencies:
43
43
  version: '2.0'
44
44
  type: :runtime
45
45
  prerelease: false
46
- version_requirements: *9349960
46
+ version_requirements: *17800900
47
47
  - !ruby/object:Gem::Dependency
48
48
  name: rails
49
- requirement: &9697740 !ruby/object:Gem::Requirement
49
+ requirement: &17799580 !ruby/object:Gem::Requirement
50
50
  none: false
51
51
  requirements:
52
52
  - - ! '>='
@@ -54,7 +54,7 @@ dependencies:
54
54
  version: '3.0'
55
55
  type: :runtime
56
56
  prerelease: false
57
- version_requirements: *9697740
57
+ version_requirements: *17799580
58
58
  description: Background jobs on RabbitMQ
59
59
  email:
60
60
  - lazureykis@gmail.com
@@ -81,6 +81,7 @@ files:
81
81
  - lib/rabbit_jobs/error_mailer.rb
82
82
  - lib/rabbit_jobs/helpers.rb
83
83
  - lib/rabbit_jobs/job.rb
84
+ - lib/rabbit_jobs/main_loop.rb
84
85
  - lib/rabbit_jobs/publisher.rb
85
86
  - lib/rabbit_jobs/scheduler.rb
86
87
  - lib/rabbit_jobs/tasks.rb
@@ -114,7 +115,7 @@ required_ruby_version: !ruby/object:Gem::Requirement
114
115
  version: '0'
115
116
  segments:
116
117
  - 0
117
- hash: -1729098954943910884
118
+ hash: -1527066833663791632
118
119
  required_rubygems_version: !ruby/object:Gem::Requirement
119
120
  none: false
120
121
  requirements:
@@ -123,7 +124,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
123
124
  version: '0'
124
125
  segments:
125
126
  - 0
126
- hash: -1729098954943910884
127
+ hash: -1527066833663791632
127
128
  requirements: []
128
129
  rubyforge_project:
129
130
  rubygems_version: 1.8.11