inst-jobs 0.11.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (86) hide show
  1. checksums.yaml +7 -0
  2. data/bin/inst_job +4 -0
  3. data/db/migrate/20101216224513_create_delayed_jobs.rb +40 -0
  4. data/db/migrate/20110208031356_add_delayed_jobs_tag.rb +14 -0
  5. data/db/migrate/20110426161613_add_delayed_jobs_max_attempts.rb +13 -0
  6. data/db/migrate/20110516225834_add_delayed_jobs_strand.rb +14 -0
  7. data/db/migrate/20110531144916_cleanup_delayed_jobs_indexes.rb +26 -0
  8. data/db/migrate/20110610213249_optimize_delayed_jobs.rb +40 -0
  9. data/db/migrate/20110831210257_add_delayed_jobs_next_in_strand.rb +52 -0
  10. data/db/migrate/20120510004759_delayed_jobs_delete_trigger_lock_for_update.rb +31 -0
  11. data/db/migrate/20120531150712_drop_psql_jobs_pop_fn.rb +15 -0
  12. data/db/migrate/20120607164022_delayed_jobs_use_advisory_locks.rb +80 -0
  13. data/db/migrate/20120607181141_index_jobs_on_locked_by.rb +15 -0
  14. data/db/migrate/20120608191051_add_jobs_run_at_index.rb +15 -0
  15. data/db/migrate/20120927184213_change_delayed_jobs_handler_to_text.rb +13 -0
  16. data/db/migrate/20140505215131_add_failed_jobs_original_job_id.rb +13 -0
  17. data/db/migrate/20140505215510_copy_failed_jobs_original_id.rb +13 -0
  18. data/db/migrate/20140505223637_drop_failed_jobs_original_id.rb +13 -0
  19. data/db/migrate/20140512213941_add_source_to_jobs.rb +15 -0
  20. data/db/migrate/20150807133223_add_max_concurrent_to_jobs.rb +70 -0
  21. data/db/migrate/20151123210429_add_expires_at_to_jobs.rb +15 -0
  22. data/db/migrate/20151210162949_improve_max_concurrent.rb +50 -0
  23. data/lib/delayed/backend/active_record.rb +340 -0
  24. data/lib/delayed/backend/base.rb +335 -0
  25. data/lib/delayed/backend/redis/bulk_update.lua +50 -0
  26. data/lib/delayed/backend/redis/destroy_job.lua +2 -0
  27. data/lib/delayed/backend/redis/enqueue.lua +29 -0
  28. data/lib/delayed/backend/redis/fail_job.lua +5 -0
  29. data/lib/delayed/backend/redis/find_available.lua +3 -0
  30. data/lib/delayed/backend/redis/functions.rb +57 -0
  31. data/lib/delayed/backend/redis/get_and_lock_next_available.lua +17 -0
  32. data/lib/delayed/backend/redis/includes/jobs_common.lua +203 -0
  33. data/lib/delayed/backend/redis/job.rb +497 -0
  34. data/lib/delayed/backend/redis/set_running.lua +5 -0
  35. data/lib/delayed/backend/redis/tickle_strand.lua +2 -0
  36. data/lib/delayed/batch.rb +56 -0
  37. data/lib/delayed/cli.rb +101 -0
  38. data/lib/delayed/daemon.rb +103 -0
  39. data/lib/delayed/engine.rb +4 -0
  40. data/lib/delayed/job_tracking.rb +31 -0
  41. data/lib/delayed/lifecycle.rb +90 -0
  42. data/lib/delayed/log_tailer.rb +22 -0
  43. data/lib/delayed/message_sending.rb +134 -0
  44. data/lib/delayed/performable_method.rb +52 -0
  45. data/lib/delayed/periodic.rb +85 -0
  46. data/lib/delayed/plugin.rb +22 -0
  47. data/lib/delayed/pool.rb +161 -0
  48. data/lib/delayed/server/helpers.rb +28 -0
  49. data/lib/delayed/server/public/css/app.css +12 -0
  50. data/lib/delayed/server/public/js/app.js +132 -0
  51. data/lib/delayed/server/views/index.erb +90 -0
  52. data/lib/delayed/server/views/layout.erb +47 -0
  53. data/lib/delayed/server.rb +120 -0
  54. data/lib/delayed/settings.rb +90 -0
  55. data/lib/delayed/testing.rb +32 -0
  56. data/lib/delayed/version.rb +3 -0
  57. data/lib/delayed/work_queue/in_process.rb +13 -0
  58. data/lib/delayed/work_queue/parent_process.rb +180 -0
  59. data/lib/delayed/worker.rb +234 -0
  60. data/lib/delayed/yaml_extensions.rb +109 -0
  61. data/lib/delayed_job.rb +46 -0
  62. data/lib/inst-jobs.rb +1 -0
  63. data/spec/active_record_job_spec.rb +246 -0
  64. data/spec/delayed/cli_spec.rb +23 -0
  65. data/spec/delayed/daemon_spec.rb +35 -0
  66. data/spec/delayed/server_spec.rb +63 -0
  67. data/spec/delayed/settings_spec.rb +32 -0
  68. data/spec/delayed/work_queue/in_process_spec.rb +31 -0
  69. data/spec/delayed/work_queue/parent_process_spec.rb +159 -0
  70. data/spec/delayed/worker_spec.rb +16 -0
  71. data/spec/gemfiles/32.gemfile +6 -0
  72. data/spec/gemfiles/40.gemfile +5 -0
  73. data/spec/gemfiles/41.gemfile +5 -0
  74. data/spec/gemfiles/42.gemfile +5 -0
  75. data/spec/migrate/20140924140513_add_story_table.rb +7 -0
  76. data/spec/redis_job_spec.rb +140 -0
  77. data/spec/sample_jobs.rb +28 -0
  78. data/spec/shared/delayed_batch.rb +85 -0
  79. data/spec/shared/delayed_method.rb +419 -0
  80. data/spec/shared/performable_method.rb +66 -0
  81. data/spec/shared/shared_backend.rb +819 -0
  82. data/spec/shared/testing.rb +48 -0
  83. data/spec/shared/worker.rb +378 -0
  84. data/spec/shared_jobs_specs.rb +15 -0
  85. data/spec/spec_helper.rb +97 -0
  86. metadata +390 -0
@@ -0,0 +1,28 @@
1
+ module Delayed
2
+ class Server
3
+ module Helpers
4
+ def h(text)
5
+ Rack::Utils.escape_html(text)
6
+ end
7
+
8
+ def url_path(*path_parts)
9
+ [path_prefix, path_parts].join('/').squeeze('/')
10
+ end
11
+
12
+ def path_prefix
13
+ request.env['SCRIPT_NAME']
14
+ end
15
+
16
+ def render_javascript_env
17
+ {
18
+ Routes: {
19
+ root: path_prefix,
20
+ running: url_path('running'),
21
+ tags: url_path('tags'),
22
+ jobs: url_path('jobs'),
23
+ }
24
+ }.to_json
25
+ end
26
+ end
27
+ end
28
+ end
@@ -0,0 +1,12 @@
1
+ table.dataTable td {
2
+ word-wrap: break-word;
3
+ }
4
+ table.dataTable td.strand {
5
+ max-width: 100px;
6
+ }
7
+ table.dataTable td.tag {
8
+ max-width: 100px;
9
+ }
10
+ div.job-list-row {
11
+ padding-top: 10px;
12
+ }
@@ -0,0 +1,132 @@
1
+ (function () {
2
+ var Delayed = {};
3
+
4
+ Delayed.Render = {};
5
+ Delayed.Render.attempts = function (previousAttempts, callType, jobData) {
6
+ var thisAttempt = parseInt(previousAttempts, 10) + 1;
7
+ switch(callType) {
8
+ case "display":
9
+ return thisAttempt + "/" + jobData.max_attempts;
10
+ default:
11
+ return thisAttempt;
12
+ }
13
+ };
14
+
15
+ Delayed.Render.runTime = function (lockedAt, callType) {
16
+ var elapsed, days, hours, minutes, seconds, remaining, formattedTime;
17
+ elapsed = Math.round((new Date().getTime() - Date.parse(lockedAt)) / 1000);
18
+ switch(callType) {
19
+ case "display": {
20
+ days = Math.floor(elapsed / 86400);
21
+ remaining = elapsed - (days * 86400);
22
+ hours = Math.floor(remaining / 3600);
23
+ remaining = remaining - (hours * 3600);
24
+ minutes = Math.floor(remaining / 60);
25
+ seconds = remaining - (minutes * 60);
26
+ formattedTime = ""
27
+ if(days > 0) {
28
+ formattedTime = days + "d "
29
+ }
30
+ formattedTime = formattedTime + ("0" + hours).slice(-2) + ":" + ("0" + minutes).slice(-2) + ":" + ("0" + seconds).slice(-2);
31
+ return formattedTime;
32
+ }
33
+ default:
34
+ return elapsed;
35
+ }
36
+ };
37
+
38
+ $(document).ready(function () {
39
+ var runningTable, runningInterval, tagsTable, tagsInterval, jobsTable, jobsInterval
40
+ runningTable = $('#running').DataTable({
41
+ "autoWidth": false,
42
+ "bSort": false,
43
+ "paging": false,
44
+ "processing": true,
45
+ "searching": false,
46
+ "scrollY": "200px",
47
+ "serverSide": true,
48
+ "order": [[5, "desc"]],
49
+ "ajax": ENV.Routes.running,
50
+ "columns": [
51
+ {"data": "id"},
52
+ {"data": "locked_by", "className": "worker"},
53
+ {"data": "tag", "className": "tag"},
54
+ {"data": "attempts", "render": Delayed.Render.attempts, "className": "attempts"},
55
+ {"data": "strand", "className": "strand"},
56
+ {"data": "locked_at", "render": Delayed.Render.runTime}
57
+ ]
58
+ });
59
+ runningInterval = setInterval(function () { runningTable.ajax.reload(); }, 2000);
60
+
61
+ tagsTable = $("#tags").DataTable({
62
+ "autoWidth": false,
63
+ "bSort": false,
64
+ "paging": false,
65
+ "processing": true,
66
+ "searching": false,
67
+ "scrollY": "200px",
68
+ "order": [[1, "desc"]],
69
+ "ajax": ENV.Routes.tags,
70
+ "columns": [
71
+ {"data": "tag", "className": "tag"},
72
+ {"data": "count"}
73
+ ]
74
+ });
75
+ tagsInterval = setInterval(function () { tagsTable.ajax.reload(); }, 10000);
76
+
77
+ jobsTable = $("#jobs").DataTable({
78
+ "autoWidth": false,
79
+ "bSort": false,
80
+ "paging": true,
81
+ "processing": true,
82
+ "searching": false,
83
+ "scrollY": "200px",
84
+ "serverSide": true,
85
+ "order": [[1, "desc"]],
86
+ "ajax": {
87
+ "url": ENV.Routes.jobs,
88
+ "data": function (data) {
89
+ data.flavor = $("select#current_jobs_flavor").val();
90
+ data.search_term = $("input#jobs_search_term").val();
91
+ }
92
+ },
93
+ "columns": [
94
+ {"data": "id"},
95
+ {"data": "tag", "className": "tag"},
96
+ {"data": "attempts", "render": Delayed.Render.attempts},
97
+ {"data": "priority"},
98
+ {"data": "strand"},
99
+ {"data": "run_at"}
100
+ ]
101
+ });
102
+
103
+ $('body').on('click', '.refresh_jobs_link', function(event) {
104
+ event.preventDefault();
105
+ jobsTable.ajax.reload();
106
+ return true
107
+ });
108
+
109
+ $('body').on('change', 'select#current_jobs_flavor', function (event) {
110
+ event.preventDefault();
111
+ $selectBox = $(this);
112
+ $selectedOption = $($selectBox.children(':selected'));
113
+ $searchDiv = $('div#job_search');
114
+
115
+ if ($selectedOption.data('requires-search')) {
116
+ $searchDiv.show();
117
+ } else {
118
+ $searchDiv.hide();
119
+ }
120
+
121
+ jobsTable.ajax.reload();
122
+ return true
123
+ });
124
+
125
+ $('body').on('keyup', '#jobs_search_term', function (event) {
126
+ if (event.keyCode == 13) {
127
+ jobsTable.ajax.reload();
128
+ }
129
+ return true
130
+ });
131
+ });
132
+ })();
@@ -0,0 +1,90 @@
1
+ <div class="row">
2
+ <div class="col-md-8">
3
+ <h2>Running</h2>
4
+ <table id="running" class="table table-striped table-bordered">
5
+ <thead>
6
+ <tr>
7
+ <th>ID</th>
8
+ <th>Worker</th>
9
+ <th>Tag</th>
10
+ <th>Attempt</th>
11
+ <th>Strand</th>
12
+ <th>Runtime</th>
13
+ </tr>
14
+ </thead>
15
+ <tbody>
16
+ </tbody>
17
+ </table>
18
+ </div>
19
+ <div class="col-md-4">
20
+ <h2>Tags</h2>
21
+ <table id="tags" class="table table-striped table-bordered">
22
+ <thead>
23
+ <tr>
24
+ <th>Tag</th>
25
+ <th>Count</th>
26
+ </tr>
27
+ </thead>
28
+ <tbody>
29
+ </tbody>
30
+ </table>
31
+ </div>
32
+ </div>
33
+
34
+ <div class="row">
35
+ <div class="col-md-12">
36
+ <h2>Jobs List</h2>
37
+ </div>
38
+ </div>
39
+
40
+ <div class="row">
41
+ <div class="form-group">
42
+ <div class="col-md-2">
43
+ <select id="current_jobs_flavor" class="form-control">
44
+ <option value="current">Current</option>
45
+ <option value="future">Future</option>
46
+ <option value="failed">Failed</option>
47
+ <option value="id" data-requires-search="true">ID</option>
48
+ <option value="strand" data-requires-search="true">Strand</option>
49
+ <option value="tag" data-requires-search="true">Tag</option>
50
+ </select>
51
+ </div>
52
+ <div class="col-md-3">
53
+ <!-- this inline style makes me sad but doing it in the stylesheet seems to break bootstrap -->
54
+ <div class="input-group" id="job_search" style="display: none;">
55
+ <input type="text" class="form-control" id="jobs_search_term" placeholder="">
56
+ <span class="input-group-btn">
57
+ <a href="" class="btn btn-default refresh_jobs_link" aria-label="Run Search">
58
+ Filter <span class="glyphicon glyphicon-filter"></span>
59
+ </a>
60
+ </span>
61
+ </div>
62
+ </div>
63
+ <div class="col-md-6">
64
+ </div>
65
+ <div class="col-md-1">
66
+ <a href="" class="btn btn-default pull-right refresh_jobs_link" aria-label="Refresh Jobs Table">
67
+ <span class="glyphicon glyphicon-refresh"></span> Refresh
68
+ </a>
69
+ </div>
70
+ </div>
71
+ </div>
72
+
73
+ <div class="row job-list-row">
74
+ <div class="col-md-12">
75
+ <table id="jobs" class="table table-striped table-bordered">
76
+ <thead>
77
+ <tr>
78
+ <th>ID</th>
79
+ <th>Tag</th>
80
+ <th>Attempt</th>
81
+ <th>Priority</th>
82
+ <th>Strand</th>
83
+ <th>Run At</th>
84
+ </tr>
85
+ </thead>
86
+ <tbody>
87
+ </tbody>
88
+ </table>
89
+ </div>
90
+ </div>
@@ -0,0 +1,47 @@
1
+ <!DOCTYPE html>
2
+ <html>
3
+ <head>
4
+ <meta charset="utf-8">
5
+ <meta http-equiv="X-UA-Compatible" content="IE=edge">
6
+ <meta name="viewport" content="width=device-width, initial-scale=1">
7
+ <script type="text/javascript" charset="utf8">
8
+ window.ENV = <%= render_javascript_env %>
9
+ </script>
10
+ <script type="text/javascript" charset="utf8 "src="//code.jquery.com/jquery-2.1.3.min.js" defer></script>
11
+ <script type="text/javascript" charset="utf8" src="//maxcdn.bootstrapcdn.com/bootstrap/3.3.4/js/bootstrap.min.js" defer></script>
12
+ <script type="text/javascript" charset="utf8" src="//cdn.datatables.net/1.10.6/js/jquery.dataTables.js" defer></script>
13
+ <script type="text/javascript" charset="utf8" src="//cdn.datatables.net/plug-ins/1.10.6/integration/bootstrap/3/dataTables.bootstrap.js" defer></script>
14
+ <script type="text/javascript" charset="utf8" src="<%= url_path 'js/app.js' %>" defer></script>
15
+
16
+ <link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.4/css/bootstrap.min.css">
17
+ <link rel="stylesheet" href="//cdn.datatables.net/plug-ins/1.10.6/integration/bootstrap/3/dataTables.bootstrap.css">
18
+ <link rel="stylesheet" href="<%= url_path 'css/app.css' %>">
19
+
20
+ <title>Delayed Jobs</title>
21
+ </head>
22
+ <body>
23
+ <nav class="navbar navbar-default">
24
+ <div class="container-fluid">
25
+ <!-- Brand and toggle get grouped for better mobile display -->
26
+ <div class="navbar-header">
27
+ <button type="button" class="navbar-toggle collapsed" data-toggle="collapse" data-target="#bs-example-navbar-collapse-1" aria-expanded="false">
28
+ <span class="sr-only">Toggle navigation</span>
29
+ <span class="icon-bar"></span>
30
+ <span class="icon-bar"></span>
31
+ <span class="icon-bar"></span>
32
+ </button>
33
+ <a class="navbar-brand" href="<%= url_path '/' %>">Delayed Jobs</a>
34
+ </div>
35
+
36
+ <!-- Collect the nav links, forms, and other content for toggling -->
37
+ <div class="collapse navbar-collapse" id="bs-example-navbar-collapse-1">
38
+ <ul class="nav navbar-nav">
39
+ </ul>
40
+ </div><!-- /.navbar-collapse -->
41
+ </div><!-- /.container-fluid -->
42
+ </nav>
43
+ <div class="container">
44
+ <%= yield %>
45
+ </div>
46
+ </body>
47
+ </html>
@@ -0,0 +1,120 @@
1
+ require 'sinatra/base'
2
+ require 'sinatra/json'
3
+ require 'json'
4
+ require 'delayed_job'
5
+
6
+ module Delayed
7
+ class Server < Sinatra::Base
8
+ APP_DIR = File.dirname(File.expand_path(__FILE__))
9
+ set :views, File.join(APP_DIR, 'server', 'views')
10
+ set :public_folder, File.join(APP_DIR, 'server', 'public')
11
+
12
+ def initialize(*args, &block)
13
+ super()
14
+ # Rails will take care of establishing the DB connection for us if there is
15
+ # an application present
16
+ if using_active_record? && !ActiveRecord::Base.connected?
17
+ ActiveRecord::Base.establish_connection(ENV['DATABASE_URL'])
18
+ end
19
+ end
20
+
21
+ def using_active_record?
22
+ Delayed::Job == Delayed::Backend::ActiveRecord::Job
23
+ end
24
+
25
+ # Ensure we're connected to the DB before processing the request
26
+ before do
27
+ if ActiveRecord::Base.respond_to?(:verify_active_connections!) && using_active_record?
28
+ ActiveRecord::Base.verify_active_connections!
29
+ end
30
+ end
31
+
32
+ # Release any used connections back to the pool
33
+ after do
34
+ ActiveRecord::Base.clear_active_connections! if using_active_record?
35
+ end
36
+
37
+ configure :development do
38
+ require 'sinatra/reloader'
39
+ register Sinatra::Reloader
40
+ end
41
+
42
+ helpers do
43
+ # this can't get required until the class has been opened for the first time
44
+ require 'delayed/server/helpers'
45
+ include Delayed::Server::Helpers
46
+ end
47
+
48
+ get '/' do
49
+ erb :index
50
+ end
51
+
52
+ get '/running' do
53
+ content_type :json
54
+ json({
55
+ draw: params['draw'].to_i,
56
+ recordsTotal: Delayed::Job.running.count,
57
+ recordsFiltered: Delayed::Job.running.count,
58
+ data: Delayed::Job.running_jobs.map{ |j|
59
+ j.as_json(include_root: false, except: [:handler, :last_error])
60
+ },
61
+ })
62
+ end
63
+
64
+ get '/tags' do
65
+ content_type :json
66
+ json({
67
+ draw: params['draw'].to_i,
68
+ data: Delayed::Job.tag_counts('current', 10)
69
+ })
70
+ end
71
+
72
+ DEFAULT_PAGE_SIZE = 10
73
+ MAX_PAGE_SIZE = 100
74
+ get '/jobs' do
75
+ content_type :json
76
+ flavor = params['flavor'] || 'current'
77
+ page_size = extract_page_size
78
+ offset = Integer(params['start'] || 0)
79
+ case flavor
80
+ when 'id'
81
+ jobs = Delayed::Job.where(id: params['search_term'])
82
+ total_records = 1
83
+ when 'future', 'current', 'failed'
84
+ jobs = Delayed::Job.list_jobs(flavor, page_size, offset)
85
+ total_records = Delayed::Job.jobs_count(flavor)
86
+ else
87
+ query = params['search_term']
88
+ if query.present?
89
+ jobs = Delayed::Job.list_jobs(flavor, page_size, offset, query)
90
+ else
91
+ jobs = []
92
+ end
93
+ total_records = Delayed::Job.jobs_count(flavor, query)
94
+ end
95
+ json({
96
+ draw: params['draw'].to_i,
97
+ recordsTotal: total_records,
98
+ recordsFiltered: jobs.size,
99
+ data: build_jobs_json(jobs),
100
+ })
101
+ end
102
+
103
+ private
104
+
105
+ def extract_page_size
106
+ page_size = Integer(params['length'] || DEFAULT_PAGE_SIZE)
107
+ # if dataTables wants all of the records it will send us -1 but we don't
108
+ # want the potential to kill our servers with this request so we'll limit it
109
+ page_size = DEFAULT_PAGE_SIZE if page_size == -1
110
+ [page_size, MAX_PAGE_SIZE].min
111
+ end
112
+
113
+
114
+ def build_jobs_json(jobs)
115
+ json = jobs.map{ |j|
116
+ j.as_json(root: false, except: [:handler, :last_error])
117
+ }
118
+ end
119
+ end
120
+ end
@@ -0,0 +1,90 @@
1
+ require 'yaml'
2
+ require 'erb'
3
+
4
+ module Delayed
5
+ module Settings
6
+ SETTINGS = [
7
+ :queue,
8
+ :max_attempts,
9
+ :sleep_delay,
10
+ :sleep_delay_stagger,
11
+ :fetch_batch_size,
12
+ :select_random_from_batch,
13
+ :worker_procname_prefix,
14
+ :pool_procname_suffix,
15
+ :default_job_options,
16
+ :silence_periodic_log,
17
+ :disable_periodic_jobs,
18
+ :disable_automatic_orphan_unlocking,
19
+ :last_ditch_logfile,
20
+ :parent_process_client_timeout,
21
+ ]
22
+ SETTINGS_WITH_ARGS = [ :num_strands ]
23
+
24
+ SETTINGS.each do |setting|
25
+ mattr_writer(setting)
26
+ self.send("#{setting}=", nil)
27
+ define_singleton_method(setting) do
28
+ val = class_variable_get(:"@@#{setting}")
29
+ val.respond_to?(:call) ? val.call() : val
30
+ end
31
+ end
32
+
33
+ mattr_accessor(*SETTINGS_WITH_ARGS)
34
+
35
+ def self.queue=(queue_name)
36
+ raise(ArgumentError, "queue_name must not be blank") if queue_name.blank?
37
+ @@queue = queue_name
38
+ end
39
+
40
+ self.queue = "queue"
41
+ self.max_attempts = 1
42
+ self.sleep_delay = 2.0
43
+ self.sleep_delay_stagger = 2.0
44
+ self.fetch_batch_size = 5
45
+ self.select_random_from_batch = false
46
+ self.silence_periodic_log = false
47
+ self.parent_process_client_timeout = 10.0
48
+
49
+ self.num_strands = ->(strand_name){ nil }
50
+ self.default_job_options = ->{ Hash.new }
51
+
52
+ def self.worker_config(config_filename = nil)
53
+ config_filename ||= default_worker_config_name
54
+ config = YAML.load(ERB.new(File.read(config_filename)).result)
55
+ env = defined?(RAILS_ENV) ? RAILS_ENV : ENV['RAILS_ENV'] || 'development'
56
+ config = config[env] || config['default']
57
+ # Backwards compatibility from when the config was just an array of queues
58
+ config = { :workers => config } if config.is_a?(Array)
59
+ unless config && config.is_a?(Hash)
60
+ raise ArgumentError,
61
+ "Invalid config file #{config_filename}"
62
+ end
63
+ config = config.with_indifferent_access
64
+ config[:workers].map! do |worker_config|
65
+ config.except(:workers).merge(worker_config.with_indifferent_access)
66
+ end
67
+ config
68
+ end
69
+
70
+ def self.apply_worker_config!(config)
71
+ SETTINGS.each do |setting|
72
+ self.send("#{setting}=", config[setting.to_s]) if config.key?(setting.to_s)
73
+ end
74
+ end
75
+
76
+ def self.default_worker_config_name
77
+ expand_rails_path("config/delayed_jobs.yml")
78
+ end
79
+
80
+ # Expands rails-relative paths, without depending on rails being loaded.
81
+ def self.expand_rails_path(path)
82
+ root = if defined?(Rails) && Rails.root
83
+ (Rails.root+"Gemfile").to_s
84
+ else
85
+ ENV.fetch('BUNDLE_GEMFILE')
86
+ end
87
+ File.expand_path("../#{path}", root)
88
+ end
89
+ end
90
+ end
@@ -0,0 +1,32 @@
1
+ module Delayed
2
+ module Testing
3
+ def self.run_job(job)
4
+ Delayed::Worker.new.perform(job)
5
+ end
6
+
7
+ def self.drain
8
+ while job = Delayed::Job.get_and_lock_next_available(
9
+ 'spec run_jobs',
10
+ Delayed::Settings.queue,
11
+ 0,
12
+ Delayed::MAX_PRIORITY)
13
+ run_job(job)
14
+ end
15
+ end
16
+
17
+ def self.track_created
18
+ job_tracking = JobTracking.track { yield }
19
+ job_tracking.created
20
+ end
21
+
22
+ def self.clear_all!
23
+ case Delayed::Job.name
24
+ when /Redis/
25
+ Delayed::Job.redis.flushdb
26
+ when /ActiveRecord/
27
+ Delayed::Job.delete_all
28
+ Delayed::Job::Failed.delete_all
29
+ end
30
+ end
31
+ end
32
+ end
@@ -0,0 +1,3 @@
1
+ module Delayed
2
+ VERSION = "0.11.0"
3
+ end
@@ -0,0 +1,13 @@
1
+ module Delayed
2
+ module WorkQueue
3
+ # The simplest possible implementation of a WorkQueue -- just turns around and
4
+ # queries the queue inline.
5
+ class InProcess
6
+ def get_and_lock_next_available(worker_name, queue_name, min_priority, max_priority)
7
+ Delayed::Worker.lifecycle.run_callbacks(:work_queue_pop, self) do
8
+ Delayed::Job.get_and_lock_next_available(worker_name, queue_name, min_priority, max_priority)
9
+ end
10
+ end
11
+ end
12
+ end
13
+ end