delayed_job 4.0.2 → 4.0.3

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,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)