delayed_job 4.0.2 → 4.0.3

Sign up to get free protection for your applications and to get access to all the features.
@@ -1,15 +1,17 @@
1
- begin
2
- require 'daemons'
3
- rescue LoadError
4
- raise "You need to add gem 'daemons' to your Gemfile if you wish to use it."
1
+ unless ENV['RAILS_ENV'] == 'test'
2
+ begin
3
+ require 'daemons'
4
+ rescue LoadError
5
+ raise "You need to add gem 'daemons' to your Gemfile if you wish to use it."
6
+ end
5
7
  end
6
8
  require 'optparse'
7
9
 
8
10
  module Delayed
9
- class Command
10
- attr_accessor :worker_count
11
+ class Command # rubocop:disable ClassLength
12
+ attr_accessor :worker_count, :worker_pools
11
13
 
12
- def initialize(args)
14
+ def initialize(args) # rubocop:disable MethodLength
13
15
  @options = {
14
16
  :quiet => true,
15
17
  :pid_dir => "#{Rails.root}/tmp/pids"
@@ -18,88 +20,106 @@ module Delayed
18
20
  @worker_count = 1
19
21
  @monitor = false
20
22
 
21
- opts = OptionParser.new do |opts|
22
- opts.banner = "Usage: #{File.basename($0)} [options] start|stop|restart|run"
23
+ opts = OptionParser.new do |opt|
24
+ opt.banner = "Usage: #{File.basename($PROGRAM_NAME)} [options] start|stop|restart|run"
23
25
 
24
- opts.on('-h', '--help', 'Show this message') do
25
- puts opts
26
+ opt.on('-h', '--help', 'Show this message') do
27
+ puts opt
26
28
  exit 1
27
29
  end
28
- opts.on('-e', '--environment=NAME', 'Specifies the environment to run this delayed jobs under (test/development/production).') do |e|
29
- STDERR.puts "The -e/--environment option has been deprecated and has no effect. Use RAILS_ENV and see http://github.com/collectiveidea/delayed_job/issues/#issue/7"
30
+ opt.on('-e', '--environment=NAME', 'Specifies the environment to run this delayed jobs under (test/development/production).') do |_e|
31
+ STDERR.puts 'The -e/--environment option has been deprecated and has no effect. Use RAILS_ENV and see http://github.com/collectiveidea/delayed_job/issues/#issue/7'
30
32
  end
31
- opts.on('--min-priority N', 'Minimum priority of jobs to run.') do |n|
33
+ opt.on('--min-priority N', 'Minimum priority of jobs to run.') do |n|
32
34
  @options[:min_priority] = n
33
35
  end
34
- opts.on('--max-priority N', 'Maximum priority of jobs to run.') do |n|
36
+ opt.on('--max-priority N', 'Maximum priority of jobs to run.') do |n|
35
37
  @options[:max_priority] = n
36
38
  end
37
- opts.on('-n', '--number_of_workers=workers', "Number of unique workers to spawn") do |worker_count|
38
- @worker_count = worker_count.to_i rescue 1
39
+ opt.on('-n', '--number_of_workers=workers', 'Number of unique workers to spawn') do |worker_count|
40
+ @worker_count = worker_count.to_i rescue 1 # rubocop:disable RescueModifier
39
41
  end
40
- opts.on('--pid-dir=DIR', 'Specifies an alternate directory in which to store the process ids.') do |dir|
42
+ opt.on('--pid-dir=DIR', 'Specifies an alternate directory in which to store the process ids.') do |dir|
41
43
  @options[:pid_dir] = dir
42
44
  end
43
- opts.on('-i', '--identifier=n', 'A numeric identifier for the worker.') do |n|
45
+ opt.on('-i', '--identifier=n', 'A numeric identifier for the worker.') do |n|
44
46
  @options[:identifier] = n
45
47
  end
46
- opts.on('-m', '--monitor', 'Start monitor process.') do
48
+ opt.on('-m', '--monitor', 'Start monitor process.') do
47
49
  @monitor = true
48
50
  end
49
- opts.on('--sleep-delay N', "Amount of time to sleep when no jobs are found") do |n|
51
+ opt.on('--sleep-delay N', 'Amount of time to sleep when no jobs are found') do |n|
50
52
  @options[:sleep_delay] = n.to_i
51
53
  end
52
- opts.on('--read-ahead N', "Number of jobs from the queue to consider") do |n|
54
+ opt.on('--read-ahead N', 'Number of jobs from the queue to consider') do |n|
53
55
  @options[:read_ahead] = n
54
56
  end
55
- opts.on('-p', '--prefix NAME', "String to be prefixed to worker process names") do |prefix|
57
+ opt.on('-p', '--prefix NAME', 'String to be prefixed to worker process names') do |prefix|
56
58
  @options[:prefix] = prefix
57
59
  end
58
- opts.on('--queues=queues', "Specify which queue DJ must look up for jobs") do |queues|
60
+ opt.on('--queues=queues', 'Specify which queue DJ must look up for jobs') do |queues|
59
61
  @options[:queues] = queues.split(',')
60
62
  end
61
- opts.on('--queue=queue', "Specify which queue DJ must look up for jobs") do |queue|
63
+ opt.on('--queue=queue', 'Specify which queue DJ must look up for jobs') do |queue|
62
64
  @options[:queues] = queue.split(',')
63
65
  end
64
- opts.on('--exit-on-complete', "Exit when no more jobs are available to run. This will exit if all jobs are scheduled to run in the future.") do
66
+ opt.on('--pool=queue1[,queue2][:worker_count]', 'Specify queues and number of workers for a worker pool') do |pool|
67
+ parse_worker_pool(pool)
68
+ end
69
+ opt.on('--exit-on-complete', 'Exit when no more jobs are available to run. This will exit if all jobs are scheduled to run in the future.') do
65
70
  @options[:exit_on_complete] = true
66
71
  end
67
72
  end
68
73
  @args = opts.parse!(args)
69
74
  end
70
75
 
71
- def daemonize
76
+ def daemonize # rubocop:disable PerceivedComplexity
72
77
  dir = @options[:pid_dir]
73
- Dir.mkdir(dir) unless File.exists?(dir)
78
+ Dir.mkdir(dir) unless File.exist?(dir)
74
79
 
75
- if @worker_count > 1 && @options[:identifier]
76
- raise ArgumentError, 'Cannot specify both --number-of-workers and --identifier'
77
- elsif @worker_count == 1 && @options[:identifier]
78
- process_name = "delayed_job.#{@options[:identifier]}"
79
- run_process(process_name, dir)
80
+ if worker_pools
81
+ setup_pools
82
+ elsif @options[:identifier]
83
+ if worker_count > 1
84
+ raise ArgumentError, 'Cannot specify both --number-of-workers and --identifier'
85
+ else
86
+ run_process("delayed_job.#{@options[:identifier]}", @options)
87
+ end
80
88
  else
81
89
  worker_count.times do |worker_index|
82
- process_name = worker_count == 1 ? "delayed_job" : "delayed_job.#{worker_index}"
83
- run_process(process_name, dir)
90
+ process_name = worker_count == 1 ? 'delayed_job' : "delayed_job.#{worker_index}"
91
+ run_process(process_name, @options)
92
+ end
93
+ end
94
+ end
95
+
96
+ def setup_pools
97
+ worker_index = 0
98
+ @worker_pools.each do |queues, worker_count|
99
+ options = @options.merge(:queues => queues)
100
+ worker_count.times do
101
+ process_name = "delayed_job.#{worker_index}"
102
+ run_process(process_name, options)
103
+ worker_index += 1
84
104
  end
85
105
  end
86
106
  end
87
107
 
88
- def run_process(process_name, dir)
108
+ def run_process(process_name, options = {})
89
109
  Delayed::Worker.before_fork
90
- Daemons.run_proc(process_name, :dir => dir, :dir_mode => :normal, :monitor => @monitor, :ARGV => @args) do |*args|
91
- $0 = File.join(@options[:prefix], process_name) if @options[:prefix]
110
+ Daemons.run_proc(process_name, :dir => options[:pid_dir], :dir_mode => :normal, :monitor => @monitor, :ARGV => @args) do |*_args|
111
+ $0 = File.join(options[:prefix], process_name) if @options[:prefix]
92
112
  run process_name
93
113
  end
94
114
  end
95
115
 
96
- def run(worker_name = nil)
116
+ def run(worker_name = nil, options = {})
97
117
  Dir.chdir(Rails.root)
98
118
 
99
119
  Delayed::Worker.after_fork
100
120
  Delayed::Worker.logger ||= Logger.new(File.join(Rails.root, 'log', 'delayed_job.log'))
101
121
 
102
- worker = Delayed::Worker.new(@options)
122
+ worker = Delayed::Worker.new(options)
103
123
  worker.name_prefix = "#{worker_name} "
104
124
  worker.start
105
125
  rescue => e
@@ -107,5 +127,20 @@ module Delayed
107
127
  STDERR.puts e.message
108
128
  exit 1
109
129
  end
130
+
131
+ private
132
+
133
+ def parse_worker_pool(pool)
134
+ @worker_pools ||= []
135
+
136
+ queues, worker_count = pool.split(':')
137
+ if ['*', '', nil].include?(queues)
138
+ queues = []
139
+ else
140
+ queues = queues.split(',')
141
+ end
142
+ worker_count = (worker_count || 1).to_i rescue 1
143
+ @worker_pools << [queues, worker_count]
144
+ end
110
145
  end
111
146
  end
@@ -3,7 +3,8 @@ require 'timeout'
3
3
  module Delayed
4
4
  class WorkerTimeout < Timeout::Error
5
5
  def message
6
- "#{super} (Delayed::Worker.max_run_time is only #{Delayed::Worker.max_run_time.to_i} seconds)"
6
+ seconds = Delayed::Worker.max_run_time.to_i
7
+ "#{super} (Delayed::Worker.max_run_time is only #{seconds} second#{seconds == 1 ? '' : 's'})"
7
8
  end
8
9
  end
9
10
 
@@ -13,7 +13,9 @@ module Delayed
13
13
  }
14
14
 
15
15
  def initialize
16
- @callbacks = EVENTS.keys.inject({}) { |hash, e| hash[e] = Callback.new; hash }
16
+ @callbacks = EVENTS.keys.each_with_object({}) do |e, hash|
17
+ hash[e] = Callback.new
18
+ end
17
19
  end
18
20
 
19
21
  def before(event, &block)
@@ -29,7 +31,7 @@ module Delayed
29
31
  end
30
32
 
31
33
  def run_callbacks(event, *args, &block)
32
- missing_callback(event) unless @callbacks.has_key?(event)
34
+ missing_callback(event) unless @callbacks.key?(event)
33
35
 
34
36
  unless EVENTS[event].size == args.size
35
37
  raise ArgumentError, "Callback #{event} expects #{EVENTS[event].size} parameter(s): #{EVENTS[event].join(', ')}"
@@ -38,17 +40,16 @@ module Delayed
38
40
  @callbacks[event].execute(*args, &block)
39
41
  end
40
42
 
41
- private
42
-
43
- def add(type, event, &block)
44
- missing_callback(event) unless @callbacks.has_key?(event)
43
+ private
45
44
 
46
- @callbacks[event].add(type, &block)
47
- end
45
+ def add(type, event, &block)
46
+ missing_callback(event) unless @callbacks.key?(event)
47
+ @callbacks[event].add(type, &block)
48
+ end
48
49
 
49
- def missing_callback(event)
50
- raise InvalidCallback, "Unknown callback event: #{event}"
51
- end
50
+ def missing_callback(event)
51
+ raise InvalidCallback, "Unknown callback event: #{event}"
52
+ end
52
53
  end
53
54
 
54
55
  class Callback
@@ -17,31 +17,30 @@ module Delayed
17
17
  def delay(options = {})
18
18
  DelayProxy.new(PerformableMethod, self, options)
19
19
  end
20
- alias __delay__ delay
20
+ alias_method :__delay__, :delay
21
21
 
22
22
  def send_later(method, *args)
23
- warn "[DEPRECATION] `object.send_later(:method)` is deprecated. Use `object.delay.method"
23
+ warn '[DEPRECATION] `object.send_later(:method)` is deprecated. Use `object.delay.method'
24
24
  __delay__.__send__(method, *args)
25
25
  end
26
26
 
27
27
  def send_at(time, method, *args)
28
- warn "[DEPRECATION] `object.send_at(time, :method)` is deprecated. Use `object.delay(:run_at => time).method"
28
+ warn '[DEPRECATION] `object.send_at(time, :method)` is deprecated. Use `object.delay(:run_at => time).method'
29
29
  __delay__(:run_at => time).__send__(method, *args)
30
30
  end
31
31
 
32
32
  module ClassMethods
33
33
  def handle_asynchronously(method, opts = {})
34
- aliased_method, punctuation = method.to_s.sub(/([?!=])$/, ''), $1
34
+ aliased_method, punctuation = method.to_s.sub(/([?!=])$/, ''), $1 # rubocop:disable PerlBackrefs
35
35
  with_method, without_method = "#{aliased_method}_with_delay#{punctuation}", "#{aliased_method}_without_delay#{punctuation}"
36
36
  define_method(with_method) do |*args|
37
37
  curr_opts = opts.clone
38
38
  curr_opts.each_key do |key|
39
- if (val = curr_opts[key]).is_a?(Proc)
40
- curr_opts[key] = if val.arity == 1
41
- val.call(self)
42
- else
43
- val.call
44
- end
39
+ next unless (val = curr_opts[key]).is_a?(Proc)
40
+ curr_opts[key] = if val.arity == 1
41
+ val.call(self)
42
+ else
43
+ val.call
45
44
  end
46
45
  end
47
46
  delay(curr_opts).__send__(without_method, *args)
@@ -15,7 +15,7 @@ module Delayed
15
15
  end
16
16
 
17
17
  Mail::Message.class_eval do
18
- def delay(*args)
19
- raise RuntimeError, "Use MyMailer.delay.mailer_action(args) to delay sending of emails."
18
+ def delay(*_args)
19
+ raise 'Use MyMailer.delay.mailer_action(args) to delay sending of emails.'
20
20
  end
21
21
  end
@@ -10,7 +10,7 @@ module Delayed
10
10
  raise NoMethodError, "undefined method `#{method_name}' for #{object.inspect}" unless object.respond_to?(method_name, true)
11
11
 
12
12
  if object.respond_to?(:persisted?) && !object.persisted?
13
- raise(ArgumentError, 'Jobs cannot be created for non-persisted records')
13
+ raise ArgumentError, 'Jobs cannot be created for non-persisted records'
14
14
  end
15
15
 
16
16
  self.object = object
@@ -30,7 +30,7 @@ module Delayed
30
30
  object.send(symbol, *args)
31
31
  end
32
32
 
33
- def respond_to?(symbol, include_private=false)
33
+ def respond_to?(symbol, include_private = false)
34
34
  super || object.respond_to?(symbol, include_private)
35
35
  end
36
36
  end
@@ -1,133 +1,69 @@
1
1
  if defined?(ActiveRecord)
2
2
  ActiveRecord::Base.class_eval do
3
+ # rubocop:disable BlockNesting
3
4
  if instance_methods.include?(:encode_with)
4
5
  def encode_with_override(coder)
5
6
  encode_with_without_override(coder)
6
- coder.tag = "!ruby/ActiveRecord:#{self.class.name}"
7
+ coder.tag = "!ruby/ActiveRecord:#{self.class.name}" if coder.respond_to?(:tag=)
7
8
  end
8
9
  alias_method :encode_with_without_override, :encode_with
9
10
  alias_method :encode_with, :encode_with_override
10
11
  else
11
12
  def encode_with(coder)
12
- coder["attributes"] = attributes
13
- coder.tag = "!ruby/ActiveRecord:#{self.class.name}"
13
+ coder['attributes'] = attributes
14
+ coder.tag = "!ruby/ActiveRecord:#{self.class.name}" if coder.respond_to?(:tag=)
14
15
  end
15
16
  end
16
17
  end
17
18
  end
18
19
 
19
- class Delayed::PerformableMethod
20
- # serialize to YAML
21
- def encode_with(coder)
22
- coder.map = {
23
- "object" => object,
24
- "method_name" => method_name,
25
- "args" => args
26
- }
20
+ module Delayed
21
+ class PerformableMethod
22
+ # serialize to YAML
23
+ def encode_with(coder)
24
+ coder.map = {
25
+ 'object' => object,
26
+ 'method_name' => method_name,
27
+ 'args' => args
28
+ }
29
+ end
27
30
  end
28
31
  end
29
32
 
30
33
  module Psych
31
34
  module Visitors
32
- class YAMLTree
33
- def visit_Class(klass)
34
- @emitter.scalar klass.name, nil, '!ruby/class', false, false, Nodes::Scalar::SINGLE_QUOTED
35
- end
36
- end
37
-
38
35
  class ToRuby
39
- def visit_Psych_Nodes_Scalar(o)
40
- @st[o.anchor] = o.value if o.anchor
41
-
42
- if klass = Psych.load_tags[o.tag]
43
- instance = klass.allocate
44
-
45
- if instance.respond_to?(:init_with)
46
- coder = Psych::Coder.new(o.tag)
47
- coder.scalar = o.value
48
- instance.init_with coder
49
- end
50
-
51
- return instance
52
- end
53
-
54
- return o.value if o.quoted
55
- return @ss.tokenize(o.value) unless o.tag
56
-
57
- case o.tag
58
- when '!binary', 'tag:yaml.org,2002:binary'
59
- o.value.unpack('m').first
60
- when '!str', 'tag:yaml.org,2002:str'
61
- o.value
62
- when "!ruby/object:DateTime"
63
- require 'date'
64
- @ss.parse_time(o.value).to_datetime
65
- when "!ruby/object:Complex"
66
- Complex(o.value)
67
- when "!ruby/object:Rational"
68
- Rational(o.value)
69
- when "!ruby/class", "!ruby/module"
70
- resolve_class o.value
71
- when "tag:yaml.org,2002:float", "!float"
72
- Float(@ss.tokenize(o.value))
73
- when "!ruby/regexp"
74
- o.value =~ /^\/(.*)\/([mixn]*)$/
75
- source = $1
76
- options = 0
77
- lang = nil
78
- ($2 || '').split('').each do |option|
79
- case option
80
- when 'x' then options |= Regexp::EXTENDED
81
- when 'i' then options |= Regexp::IGNORECASE
82
- when 'm' then options |= Regexp::MULTILINE
83
- when 'n' then options |= Regexp::NOENCODING
84
- else lang = option
85
- end
86
- end
87
- Regexp.new(*[source, options, lang].compact)
88
- when "!ruby/range"
89
- args = o.value.split(/([.]{2,3})/, 2).map { |s|
90
- accept Nodes::Scalar.new(s)
91
- }
92
- args.push(args.delete_at(1) == '...')
93
- Range.new(*args)
94
- when /^!ruby\/sym(bol)?:?(.*)?$/
95
- o.value.to_sym
96
- else
97
- @ss.tokenize o.value
98
- end
99
- end
100
-
101
- def visit_Psych_Nodes_Mapping_with_class(object)
36
+ def visit_Psych_Nodes_Mapping_with_class(object) # rubocop:disable CyclomaticComplexity, MethodName
102
37
  return revive(Psych.load_tags[object.tag], object) if Psych.load_tags[object.tag]
103
38
 
104
39
  case object.tag
105
40
  when /^!ruby\/ActiveRecord:(.+)$/
106
- klass = resolve_class($1)
41
+ klass = resolve_class(Regexp.last_match[1])
107
42
  payload = Hash[*object.children.map { |c| accept c }]
108
- id = payload["attributes"][klass.primary_key]
43
+ id = payload['attributes'][klass.primary_key]
109
44
  begin
110
45
  klass.unscoped.find(id)
111
- rescue ActiveRecord::RecordNotFound
112
- raise Delayed::DeserializationError
46
+ rescue ActiveRecord::RecordNotFound => error
47
+ raise Delayed::DeserializationError, "ActiveRecord::RecordNotFound, class: #{klass}, primary key: #{id} (#{error.message})"
113
48
  end
114
49
  when /^!ruby\/Mongoid:(.+)$/
115
- klass = resolve_class($1)
50
+ klass = resolve_class(Regexp.last_match[1])
116
51
  payload = Hash[*object.children.map { |c| accept c }]
52
+ id = payload['attributes']['_id']
117
53
  begin
118
- klass.find(payload["attributes"]["_id"])
119
- rescue Mongoid::Errors::DocumentNotFound
120
- raise Delayed::DeserializationError
54
+ klass.find(id)
55
+ rescue Mongoid::Errors::DocumentNotFound => error
56
+ raise Delayed::DeserializationError, "Mongoid::Errors::DocumentNotFound, class: #{klass}, primary key: #{id} (#{error.message})"
121
57
  end
122
58
  when /^!ruby\/DataMapper:(.+)$/
123
- klass = resolve_class($1)
59
+ klass = resolve_class(Regexp.last_match[1])
124
60
  payload = Hash[*object.children.map { |c| accept c }]
125
61
  begin
126
- primary_keys = klass.properties.select { |p| p.key? }
62
+ primary_keys = klass.properties.select(&:key?)
127
63
  key_names = primary_keys.map { |p| p.name.to_s }
128
- klass.get!(*key_names.map { |k| payload["attributes"][k] })
129
- rescue DataMapper::ObjectNotFoundError
130
- raise Delayed::DeserializationError
64
+ klass.get!(*key_names.map { |k| payload['attributes'][k] })
65
+ rescue DataMapper::ObjectNotFoundError => error
66
+ raise Delayed::DeserializationError, "DataMapper::ObjectNotFoundError, class: #{klass} (#{error.message})"
131
67
  end
132
68
  else
133
69
  visit_Psych_Nodes_Mapping_without_class(object)