inst-jobs 0.11.0

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.
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