resque-admin-scheduler 1.0.2 → 1.3.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/bin/migrate_to_timestamps_set.rb +16 -0
- data/exe/resque-scheduler +2 -2
- data/lib/resque/scheduler/cli.rb +6 -6
- data/lib/resque/scheduler/delaying_extensions.rb +14 -14
- data/lib/resque/scheduler/env.rb +7 -7
- data/lib/resque/scheduler/lock.rb +1 -1
- data/lib/resque/scheduler/locking.rb +7 -7
- data/lib/resque/scheduler/logger_builder.rb +4 -4
- data/lib/resque/scheduler/scheduling_extensions.rb +4 -4
- data/lib/resque/scheduler/server/views/delayed.erb +4 -4
- data/lib/resque/scheduler/server/views/search.erb +1 -1
- data/lib/resque/scheduler/server.rb +37 -37
- data/lib/resque/scheduler/signal_handling.rb +1 -1
- data/lib/resque/scheduler/tasks.rb +6 -6
- data/lib/resque/scheduler/util.rb +4 -4
- data/lib/resque/scheduler_admin/cli.rb +147 -0
- data/lib/resque/scheduler_admin/delaying_extensions.rb +324 -0
- data/lib/resque/scheduler_admin/env.rb +89 -0
- data/lib/resque/scheduler_admin/lock.rb +4 -0
- data/lib/resque/scheduler_admin/locking.rb +104 -0
- data/lib/resque/scheduler_admin/logger_builder.rb +72 -0
- data/lib/resque/scheduler_admin/scheduling_extensions.rb +141 -0
- data/lib/resque/scheduler_admin/server/views/delayed.erb +63 -0
- data/lib/resque/scheduler_admin/server/views/search.erb +72 -0
- data/lib/resque/scheduler_admin/signal_handling.rb +40 -0
- data/lib/resque/scheduler_admin/tasks.rb +25 -0
- data/lib/resque/scheduler_admin/util.rb +39 -0
- data/lib/resque-admin-scheduler.rb +4 -0
- data/lib/resque_admin/scheduler/cli.rb +147 -0
- data/lib/{resque → resque_admin}/scheduler/configuration.rb +1 -1
- data/lib/resque_admin/scheduler/delaying_extensions.rb +324 -0
- data/lib/resque_admin/scheduler/env.rb +89 -0
- data/lib/{resque → resque_admin}/scheduler/extension.rb +1 -1
- data/lib/{resque → resque_admin}/scheduler/failure_handler.rb +2 -2
- data/lib/{resque → resque_admin}/scheduler/lock/base.rb +3 -3
- data/lib/{resque → resque_admin}/scheduler/lock/basic.rb +4 -4
- data/lib/{resque → resque_admin}/scheduler/lock/resilient.rb +4 -4
- data/lib/resque_admin/scheduler/lock.rb +4 -0
- data/lib/resque_admin/scheduler/locking.rb +104 -0
- data/lib/resque_admin/scheduler/logger_builder.rb +72 -0
- data/lib/{resque → resque_admin}/scheduler/plugin.rb +1 -1
- data/lib/resque_admin/scheduler/scheduling_extensions.rb +141 -0
- data/lib/resque_admin/scheduler/server/views/delayed.erb +63 -0
- data/lib/{resque → resque_admin}/scheduler/server/views/delayed_schedules.erb +0 -0
- data/lib/{resque → resque_admin}/scheduler/server/views/delayed_timestamp.erb +2 -2
- data/lib/{resque → resque_admin}/scheduler/server/views/requeue-params.erb +0 -0
- data/lib/{resque → resque_admin}/scheduler/server/views/scheduler.erb +7 -7
- data/lib/resque_admin/scheduler/server/views/search.erb +72 -0
- data/lib/{resque → resque_admin}/scheduler/server/views/search_form.erb +0 -0
- data/lib/resque_admin/scheduler/server.rb +268 -0
- data/lib/resque_admin/scheduler/signal_handling.rb +40 -0
- data/lib/resque_admin/scheduler/tasks.rb +25 -0
- data/lib/resque_admin/scheduler/util.rb +39 -0
- data/lib/{resque → resque_admin}/scheduler/version.rb +2 -2
- data/lib/{resque → resque_admin}/scheduler.rb +44 -44
- data/tasks/resque_admin_scheduler.rake +2 -0
- metadata +47 -85
- data/AUTHORS.md +0 -81
- data/CHANGELOG.md +0 -456
- data/CODE_OF_CONDUCT.md +0 -74
- data/CONTRIBUTING.md +0 -6
- data/Gemfile +0 -4
- data/LICENSE +0 -23
- data/README.md +0 -691
- data/Rakefile +0 -21
- data/lib/resque-scheduler.rb +0 -4
- data/resque-scheduler.gemspec +0 -60
@@ -0,0 +1,72 @@
|
|
1
|
+
# vim:fileencoding=utf-8
|
2
|
+
|
3
|
+
require 'mono_logger'
|
4
|
+
|
5
|
+
module ResqueAdmin
|
6
|
+
module Scheduler
|
7
|
+
# Just builds a logger, with specified verbosity and destination.
|
8
|
+
# The simplest example:
|
9
|
+
#
|
10
|
+
# ResqueAdmin::Scheduler::LoggerBuilder.new.build
|
11
|
+
class LoggerBuilder
|
12
|
+
# Initializes new instance of the builder
|
13
|
+
#
|
14
|
+
# Pass :opts Hash with
|
15
|
+
# - :quiet if logger needs to be silent for all levels. Default - false
|
16
|
+
# - :verbose if there is a need in debug messages. Default - false
|
17
|
+
# - :log_dev to output logs into a desired file. Default - STDOUT
|
18
|
+
# - :format log format, either 'text' or 'json'. Default - 'text'
|
19
|
+
#
|
20
|
+
# Example:
|
21
|
+
#
|
22
|
+
# LoggerBuilder.new(
|
23
|
+
# :quiet => false, :verbose => true, :log_dev => 'log/scheduler_admin.log'
|
24
|
+
# )
|
25
|
+
def initialize(opts = {})
|
26
|
+
@quiet = !!opts[:quiet]
|
27
|
+
@verbose = !!opts[:verbose]
|
28
|
+
@log_dev = opts[:log_dev] || $stdout
|
29
|
+
@format = opts[:format] || 'text'
|
30
|
+
end
|
31
|
+
|
32
|
+
# Returns an instance of MonoLogger
|
33
|
+
def build
|
34
|
+
logger = MonoLogger.new(@log_dev)
|
35
|
+
logger.level = level
|
36
|
+
logger.formatter = send(:"#{@format}_formatter")
|
37
|
+
logger
|
38
|
+
end
|
39
|
+
|
40
|
+
private
|
41
|
+
|
42
|
+
def level
|
43
|
+
if @verbose && !@quiet
|
44
|
+
MonoLogger::DEBUG
|
45
|
+
elsif !@quiet
|
46
|
+
MonoLogger::INFO
|
47
|
+
else
|
48
|
+
MonoLogger::FATAL
|
49
|
+
end
|
50
|
+
end
|
51
|
+
|
52
|
+
def text_formatter
|
53
|
+
proc do |severity, datetime, _progname, msg|
|
54
|
+
"resque_admin-scheduler_admin: [#{severity}] #{datetime.iso8601}: #{msg}\n"
|
55
|
+
end
|
56
|
+
end
|
57
|
+
|
58
|
+
def json_formatter
|
59
|
+
proc do |severity, datetime, progname, msg|
|
60
|
+
require 'json'
|
61
|
+
JSON.dump(
|
62
|
+
name: 'resque_admin-scheduler_admin',
|
63
|
+
progname: progname,
|
64
|
+
level: severity,
|
65
|
+
timestamp: datetime.iso8601,
|
66
|
+
msg: msg
|
67
|
+
) + "\n"
|
68
|
+
end
|
69
|
+
end
|
70
|
+
end
|
71
|
+
end
|
72
|
+
end
|
@@ -0,0 +1,141 @@
|
|
1
|
+
# vim:fileencoding=utf-8
|
2
|
+
|
3
|
+
module ResqueAdmin
|
4
|
+
module Scheduler
|
5
|
+
module SchedulingExtensions
|
6
|
+
# Accepts a new schedule configuration of the form:
|
7
|
+
#
|
8
|
+
# {
|
9
|
+
# "MakeTea" => {
|
10
|
+
# "every" => "1m" },
|
11
|
+
# "some_name" => {
|
12
|
+
# "cron" => "5/* * * *",
|
13
|
+
# "class" => "DoSomeWork",
|
14
|
+
# "args" => "work on this string",
|
15
|
+
# "description" => "this thing works it"s butter off" },
|
16
|
+
# ...
|
17
|
+
# }
|
18
|
+
#
|
19
|
+
# Hash keys can be anything and are used to describe and reference
|
20
|
+
# the scheduled job. If the "class" argument is missing, the key
|
21
|
+
# is used implicitly as "class" argument - in the "MakeTea" example,
|
22
|
+
# "MakeTea" is used both as job name and resque_admin worker class.
|
23
|
+
#
|
24
|
+
# Any jobs that were in the old schedule, but are not
|
25
|
+
# present in the new schedule, will be removed.
|
26
|
+
#
|
27
|
+
# :cron can be any cron scheduling string
|
28
|
+
#
|
29
|
+
# :every can be used in lieu of :cron. see rufus-scheduler_admin's 'every'
|
30
|
+
# usage for valid syntax. If :cron is present it will take precedence
|
31
|
+
# over :every.
|
32
|
+
#
|
33
|
+
# :class must be a resque_admin worker class. If it is missing, the job name
|
34
|
+
# (hash key) will be used as :class.
|
35
|
+
#
|
36
|
+
# :args can be any yaml which will be converted to a ruby literal and
|
37
|
+
# passed in a params. (optional)
|
38
|
+
#
|
39
|
+
# :rails_envs is the list of envs where the job gets loaded. Envs are
|
40
|
+
# comma separated (optional)
|
41
|
+
#
|
42
|
+
# :description is just that, a description of the job (optional). If
|
43
|
+
# params is an array, each element in the array is passed as a separate
|
44
|
+
# param, otherwise params is passed in as the only parameter to
|
45
|
+
# perform.
|
46
|
+
def schedule=(schedule_hash)
|
47
|
+
@non_persistent_schedules = nil
|
48
|
+
prepared_schedules = prepare_schedules(schedule_hash)
|
49
|
+
|
50
|
+
prepared_schedules.each do |schedule, config|
|
51
|
+
set_schedule(schedule, config, false)
|
52
|
+
end
|
53
|
+
|
54
|
+
# ensure only return the successfully saved data!
|
55
|
+
reload_schedule!
|
56
|
+
end
|
57
|
+
|
58
|
+
# Returns the schedule hash
|
59
|
+
def schedule
|
60
|
+
@schedule ||= all_schedules
|
61
|
+
@schedule || {}
|
62
|
+
end
|
63
|
+
|
64
|
+
# reloads the schedule from redis and memory
|
65
|
+
def reload_schedule!
|
66
|
+
@schedule = all_schedules
|
67
|
+
end
|
68
|
+
|
69
|
+
# gets the schedules as it exists in redis
|
70
|
+
def all_schedules
|
71
|
+
non_persistent_schedules.merge(persistent_schedules)
|
72
|
+
end
|
73
|
+
|
74
|
+
# Create or update a schedule with the provided name and configuration.
|
75
|
+
#
|
76
|
+
# Note: values for class and custom_job_class need to be strings,
|
77
|
+
# not constants.
|
78
|
+
#
|
79
|
+
# ResqueAdmin.set_schedule('some_job', {:class => 'SomeJob',
|
80
|
+
# :every => '15mins',
|
81
|
+
# :queue => 'high',
|
82
|
+
# :args => '/tmp/poop'})
|
83
|
+
#
|
84
|
+
# Preventing a reload is optional and available to batch operations
|
85
|
+
def set_schedule(name, config, reload = true)
|
86
|
+
persist = config.delete(:persist) || config.delete('persist')
|
87
|
+
|
88
|
+
if persist
|
89
|
+
redis.hset(:persistent_schedules, name, encode(config))
|
90
|
+
else
|
91
|
+
non_persistent_schedules[name] = decode(encode(config))
|
92
|
+
end
|
93
|
+
|
94
|
+
redis.sadd(:schedules_changed, name)
|
95
|
+
reload_schedule! if reload
|
96
|
+
end
|
97
|
+
|
98
|
+
# retrive the schedule configuration for the given name
|
99
|
+
def fetch_schedule(name)
|
100
|
+
schedule[name]
|
101
|
+
end
|
102
|
+
|
103
|
+
# remove a given schedule by name
|
104
|
+
def remove_schedule(name)
|
105
|
+
non_persistent_schedules.delete(name)
|
106
|
+
redis.hdel(:persistent_schedules, name)
|
107
|
+
redis.sadd(:schedules_changed, name)
|
108
|
+
|
109
|
+
reload_schedule!
|
110
|
+
end
|
111
|
+
|
112
|
+
private
|
113
|
+
|
114
|
+
# we store our non-persistent schedules in this hash
|
115
|
+
def non_persistent_schedules
|
116
|
+
@non_persistent_schedules ||= {}
|
117
|
+
end
|
118
|
+
|
119
|
+
# reads the persistent schedules from redis
|
120
|
+
def persistent_schedules
|
121
|
+
redis.hgetall(:persistent_schedules).tap do |h|
|
122
|
+
h.each do |name, config|
|
123
|
+
h[name] = decode(config)
|
124
|
+
end
|
125
|
+
end
|
126
|
+
end
|
127
|
+
|
128
|
+
def prepare_schedules(schedule_hash)
|
129
|
+
prepared_hash = {}
|
130
|
+
schedule_hash.each do |name, job_spec|
|
131
|
+
job_spec = job_spec.dup
|
132
|
+
unless job_spec.key?('class') || job_spec.key?(:class)
|
133
|
+
job_spec['class'] = name
|
134
|
+
end
|
135
|
+
prepared_hash[name] = job_spec
|
136
|
+
end
|
137
|
+
prepared_hash
|
138
|
+
end
|
139
|
+
end
|
140
|
+
end
|
141
|
+
end
|
@@ -0,0 +1,63 @@
|
|
1
|
+
<h1>Delayed Jobs</h1>
|
2
|
+
<%- size = resque_admin.delayed_queue_schedule_size %>
|
3
|
+
|
4
|
+
<%= scheduler_admin_view :search_form, layout: false %>
|
5
|
+
|
6
|
+
<p style="font-color: red; font-weight: bold;">
|
7
|
+
<%= @error_message %>
|
8
|
+
</p>
|
9
|
+
|
10
|
+
<p class='intro'>
|
11
|
+
This list below contains the timestamps for scheduled delayed jobs.
|
12
|
+
Server local time: <%= Time.now %>
|
13
|
+
</p>
|
14
|
+
|
15
|
+
<p class='sub'>
|
16
|
+
Showing <%= start = params[:start].to_i %> to <%= start + 20 %> of <b><%= size %></b> timestamps
|
17
|
+
</p>
|
18
|
+
|
19
|
+
<table>
|
20
|
+
<tr>
|
21
|
+
<th></th>
|
22
|
+
<th>Timestamp</th>
|
23
|
+
<th>Job count</th>
|
24
|
+
<th>Class</th>
|
25
|
+
<th>Args</th>
|
26
|
+
<th>All schedules</th>
|
27
|
+
</tr>
|
28
|
+
<% resque_admin.delayed_queue_peek(start, 20).each do |timestamp| %>
|
29
|
+
<tr>
|
30
|
+
<td>
|
31
|
+
<form action="<%= u "/delayed/queue_now" %>" method="post">
|
32
|
+
<input type="hidden" name="timestamp" value="<%= timestamp.to_i %>">
|
33
|
+
<input type="submit" value="Queue now">
|
34
|
+
</form>
|
35
|
+
</td>
|
36
|
+
<td><a href="<%= u "delayed/#{timestamp}" %>"><%= format_time(Time.at(timestamp)) %></a></td>
|
37
|
+
<td><%= delayed_timestamp_size = resque_admin.delayed_timestamp_size(timestamp) %></td>
|
38
|
+
<% job = resque_admin.delayed_timestamp_peek(timestamp, 0, 1).first %>
|
39
|
+
<td>
|
40
|
+
<% if job && delayed_timestamp_size == 1 %>
|
41
|
+
<%= h(job['class']) %>
|
42
|
+
<% else %>
|
43
|
+
<a href="<%= u "delayed/#{timestamp}" %>">see details</a>
|
44
|
+
<% end %>
|
45
|
+
</td>
|
46
|
+
<td><%= h(show_job_arguments(job['args'])) if job && delayed_timestamp_size == 1 %></td>
|
47
|
+
<td>
|
48
|
+
<% if job %>
|
49
|
+
<a href="<%=u URI("/delayed/jobs/#{job['class']}?args=" + URI.encode(job['args'].to_json)) %>">All schedules</a>
|
50
|
+
<% end %>
|
51
|
+
</td>
|
52
|
+
</tr>
|
53
|
+
<% end %>
|
54
|
+
</table>
|
55
|
+
|
56
|
+
<% if size > 0 %>
|
57
|
+
<br>
|
58
|
+
<form method="POST" action="<%=u 'delayed/clear'%>" class='clear-delayed'>
|
59
|
+
<input type='submit' name='' value='Clear Delayed Jobs' />
|
60
|
+
</form>
|
61
|
+
<% end %>
|
62
|
+
|
63
|
+
<%= partial :next_more, :start => start, :size => size %>
|
@@ -0,0 +1,72 @@
|
|
1
|
+
<h1>Search Results</h1>
|
2
|
+
<%= scheduler_admin_view :search_form, layout: false %>
|
3
|
+
<hr>
|
4
|
+
<% delayed = @jobs.select { |j| j['where_at'] == 'delayed' } %>
|
5
|
+
<h1>Delayed jobs</h1>
|
6
|
+
<table class='jobs'>
|
7
|
+
<tr>
|
8
|
+
<th></th>
|
9
|
+
<th></th>
|
10
|
+
<th>Timestamp</th>
|
11
|
+
<th>Class</th>
|
12
|
+
<th>Args</th>
|
13
|
+
</tr>
|
14
|
+
<% delayed.each do |job| %>
|
15
|
+
<tr>
|
16
|
+
<td>
|
17
|
+
<form action="<%= u "/delayed/queue_now" %>" method="post">
|
18
|
+
<input type="hidden" name="timestamp" value="<%= job['timestamp'].to_i %>">
|
19
|
+
<input type="submit" value="Queue now">
|
20
|
+
</form>
|
21
|
+
</td>
|
22
|
+
<td>
|
23
|
+
<form action="<%= u "/delayed/cancel_now" %>" method="post">
|
24
|
+
<input type="hidden" name="timestamp" value="<%= job['timestamp'].to_i %>">
|
25
|
+
<input type="hidden" name="klass" value="<%= job['class'] %>">
|
26
|
+
<input type="hidden" name="args" value="<%= h(ResqueAdmin.encode job['args']) %>">
|
27
|
+
<input type="submit" value="Cancel Job">
|
28
|
+
</form>
|
29
|
+
</td>
|
30
|
+
<td class='args'><%= format_time(Time.at(job['timestamp'])) %></td>
|
31
|
+
<td class='class'><%= job['class'] %></td>
|
32
|
+
<td class='args'><%= h job['args'].inspect %></td>
|
33
|
+
</tr>
|
34
|
+
<% end %>
|
35
|
+
</table>
|
36
|
+
</h1>
|
37
|
+
|
38
|
+
<% queued = @jobs.select { |j| j['where_at'] == 'queued' } %>
|
39
|
+
<h1>Queued jobs</h1>
|
40
|
+
<table class='jobs'>
|
41
|
+
<tr>
|
42
|
+
<th>Queue</th>
|
43
|
+
<th>Class</th>
|
44
|
+
<th>Args</th>
|
45
|
+
</tr>
|
46
|
+
<% queued.each do |job| %>
|
47
|
+
<tr>
|
48
|
+
<td class='class'><%= job['queue'] %></td>
|
49
|
+
<td class='class'><%= job['class'] %></td>
|
50
|
+
<td class='args'><%= h job['args'].inspect %></td>
|
51
|
+
</tr>
|
52
|
+
<% end %>
|
53
|
+
</table>
|
54
|
+
|
55
|
+
<% working = @jobs.select { |j| j['where_at'] == 'working' } %>
|
56
|
+
<h1>Working jobs</h1>
|
57
|
+
<table class='jobs'>
|
58
|
+
<tr>
|
59
|
+
<th>Queue</th>
|
60
|
+
<th>Class</th>
|
61
|
+
<th>Args</th>
|
62
|
+
</tr>
|
63
|
+
<% working.each do |job| %>
|
64
|
+
<tr>
|
65
|
+
<td class='class'><%= job['queue'] %></td>
|
66
|
+
<td class='class'><%= job['class'] %></td>
|
67
|
+
<td class='args'><%= h job['args'].inspect %></td>
|
68
|
+
</tr>
|
69
|
+
<% end %>
|
70
|
+
</table>
|
71
|
+
|
72
|
+
|
@@ -0,0 +1,40 @@
|
|
1
|
+
# vim:fileencoding=utf-8
|
2
|
+
|
3
|
+
module ResqueAdmin
|
4
|
+
module Scheduler
|
5
|
+
module SignalHandling
|
6
|
+
attr_writer :signal_queue
|
7
|
+
|
8
|
+
def signal_queue
|
9
|
+
@signal_queue ||= []
|
10
|
+
end
|
11
|
+
|
12
|
+
# For all signals, set the shutdown flag and wait for current
|
13
|
+
# poll/enqueing to finish (should be almost instant). In the
|
14
|
+
# case of sleeping, exit immediately.
|
15
|
+
def register_signal_handlers
|
16
|
+
(Signal.list.keys & %w(INT TERM USR1 USR2 QUIT)).each do |sig|
|
17
|
+
trap(sig) do
|
18
|
+
signal_queue << sig
|
19
|
+
# break sleep in the primary scheduler_admin thread, alowing
|
20
|
+
# the signal queue to get processed as soon as possible.
|
21
|
+
@th.wakeup if @th && @th.alive?
|
22
|
+
end
|
23
|
+
end
|
24
|
+
end
|
25
|
+
|
26
|
+
def handle_signals
|
27
|
+
loop do
|
28
|
+
sig = signal_queue.shift
|
29
|
+
break unless sig
|
30
|
+
log! "Got #{sig} signal"
|
31
|
+
case sig
|
32
|
+
when 'INT', 'TERM', 'QUIT' then shutdown
|
33
|
+
when 'USR1' then print_schedule
|
34
|
+
when 'USR2' then reload_schedule!
|
35
|
+
end
|
36
|
+
end
|
37
|
+
end
|
38
|
+
end
|
39
|
+
end
|
40
|
+
end
|
@@ -0,0 +1,25 @@
|
|
1
|
+
# vim:fileencoding=utf-8
|
2
|
+
|
3
|
+
require 'resque_admin/tasks'
|
4
|
+
require 'resque_admin-scheduler_admin'
|
5
|
+
|
6
|
+
namespace :resque_admin do
|
7
|
+
task :setup
|
8
|
+
|
9
|
+
def scheduler_admin_cli
|
10
|
+
@scheduler_admin_cli ||= ResqueAdmin::Scheduler::Cli.new(
|
11
|
+
%W(#{ENV['RESQUE_SCHEDULER_OPTIONS']})
|
12
|
+
)
|
13
|
+
end
|
14
|
+
|
15
|
+
desc 'Start ResqueAdmin Scheduler'
|
16
|
+
task scheduler_admin: :scheduler_admin_setup do
|
17
|
+
scheduler_admin_cli.setup_env
|
18
|
+
scheduler_admin_cli.run_forever
|
19
|
+
end
|
20
|
+
|
21
|
+
task :scheduler_admin_setup do
|
22
|
+
scheduler_admin_cli.parse_options
|
23
|
+
Rake::Task['resque_admin:setup'].invoke unless scheduler_admin_cli.pre_setup
|
24
|
+
end
|
25
|
+
end
|
@@ -0,0 +1,39 @@
|
|
1
|
+
# vim:fileencoding=utf-8
|
2
|
+
|
3
|
+
module ResqueAdmin
|
4
|
+
module Scheduler
|
5
|
+
class Util
|
6
|
+
# In order to upgrade to resque_admin(1.25) which has deprecated following
|
7
|
+
# methods, we just added these usefull helpers back to use in ResqueAdmin
|
8
|
+
# Scheduler. refer to:
|
9
|
+
# https://github.com/resque_admin/resque_admin-scheduler_admin/pull/273
|
10
|
+
|
11
|
+
def self.constantize(camel_cased_word)
|
12
|
+
camel_cased_word = camel_cased_word.to_s
|
13
|
+
|
14
|
+
if camel_cased_word.include?('-')
|
15
|
+
camel_cased_word = classify(camel_cased_word)
|
16
|
+
end
|
17
|
+
|
18
|
+
names = camel_cased_word.split('::')
|
19
|
+
names.shift if names.empty? || names.first.empty?
|
20
|
+
|
21
|
+
constant = Object
|
22
|
+
names.each do |name|
|
23
|
+
args = Module.method(:const_get).arity != 1 ? [false] : []
|
24
|
+
|
25
|
+
constant = if constant.const_defined?(name, *args)
|
26
|
+
constant.const_get(name)
|
27
|
+
else
|
28
|
+
constant.const_missing(name)
|
29
|
+
end
|
30
|
+
end
|
31
|
+
constant
|
32
|
+
end
|
33
|
+
|
34
|
+
def self.classify(dashed_word)
|
35
|
+
dashed_word.split('-').map(&:capitalize).join
|
36
|
+
end
|
37
|
+
end
|
38
|
+
end
|
39
|
+
end
|
@@ -0,0 +1,147 @@
|
|
1
|
+
# vim:fileencoding=utf-8
|
2
|
+
|
3
|
+
require 'optparse'
|
4
|
+
|
5
|
+
module ResqueAdmin
|
6
|
+
module Scheduler
|
7
|
+
CLI_OPTIONS_ENV_MAPPING = {
|
8
|
+
app_name: 'APP_NAME',
|
9
|
+
background: 'BACKGROUND',
|
10
|
+
dynamic: 'DYNAMIC_SCHEDULE',
|
11
|
+
env: 'RAILS_ENV',
|
12
|
+
initializer_path: 'INITIALIZER_PATH',
|
13
|
+
logfile: 'LOGFILE',
|
14
|
+
logformat: 'LOGFORMAT',
|
15
|
+
quiet: 'QUIET',
|
16
|
+
pidfile: 'PIDFILE',
|
17
|
+
poll_sleep_amount: 'RESQUE_SCHEDULER_INTERVAL',
|
18
|
+
verbose: 'VERBOSE'
|
19
|
+
}.freeze
|
20
|
+
|
21
|
+
class Cli
|
22
|
+
BANNER = <<-EOF.gsub(/ {6}/, '')
|
23
|
+
Usage: resque_admin-scheduler [options]
|
24
|
+
|
25
|
+
Runs a resque_admin scheduler process directly (rather than via rake).
|
26
|
+
|
27
|
+
EOF
|
28
|
+
OPTIONS = [
|
29
|
+
{
|
30
|
+
args: ['-n', '--app-name [APP_NAME]',
|
31
|
+
'Application name for procline'],
|
32
|
+
callback: ->(options) { ->(n) { options[:app_name] = n } }
|
33
|
+
},
|
34
|
+
{
|
35
|
+
args: ['-B', '--background', 'Run in the background [BACKGROUND]'],
|
36
|
+
callback: ->(options) { ->(b) { options[:background] = b } }
|
37
|
+
},
|
38
|
+
{
|
39
|
+
args: ['-D', '--dynamic-schedule',
|
40
|
+
'Enable dynamic scheduling [DYNAMIC_SCHEDULE]'],
|
41
|
+
callback: ->(options) { ->(d) { options[:dynamic] = d } }
|
42
|
+
},
|
43
|
+
{
|
44
|
+
args: ['-E', '--environment [RAILS_ENV]', 'Environment name'],
|
45
|
+
callback: ->(options) { ->(e) { options[:env] = e } }
|
46
|
+
},
|
47
|
+
{
|
48
|
+
args: ['-I', '--initializer-path [INITIALIZER_PATH]',
|
49
|
+
'Path to optional initializer ruby file'],
|
50
|
+
callback: ->(options) { ->(i) { options[:initializer_path] = i } }
|
51
|
+
},
|
52
|
+
{
|
53
|
+
args: ['-i', '--interval [RESQUE_SCHEDULER_INTERVAL]',
|
54
|
+
'Interval for checking if a scheduled job must run'],
|
55
|
+
callback: ->(options) { ->(i) { options[:poll_sleep_amount] = i } }
|
56
|
+
},
|
57
|
+
{
|
58
|
+
args: ['-l', '--logfile [LOGFILE]', 'Log file name'],
|
59
|
+
callback: ->(options) { ->(l) { options[:logfile] = l } }
|
60
|
+
},
|
61
|
+
{
|
62
|
+
args: ['-F', '--logformat [LOGFORMAT]', 'Log output format'],
|
63
|
+
callback: ->(options) { ->(f) { options[:logformat] = f } }
|
64
|
+
},
|
65
|
+
{
|
66
|
+
args: ['-P', '--pidfile [PIDFILE]', 'PID file name'],
|
67
|
+
callback: ->(options) { ->(p) { options[:pidfile] = p } }
|
68
|
+
},
|
69
|
+
{
|
70
|
+
args: ['-q', '--quiet', 'Run with minimal output [QUIET]'],
|
71
|
+
callback: ->(options) { ->(q) { options[:quiet] = q } }
|
72
|
+
},
|
73
|
+
{
|
74
|
+
args: ['-v', '--verbose', 'Run with verbose output [VERBOSE]'],
|
75
|
+
callback: ->(options) { ->(v) { options[:verbose] = v } }
|
76
|
+
}
|
77
|
+
].freeze
|
78
|
+
|
79
|
+
def self.run!(argv = ARGV, env = ENV)
|
80
|
+
new(argv, env).run!
|
81
|
+
end
|
82
|
+
|
83
|
+
def initialize(argv = ARGV, env = ENV)
|
84
|
+
@argv = argv
|
85
|
+
@env = env
|
86
|
+
end
|
87
|
+
|
88
|
+
def run!
|
89
|
+
pre_run
|
90
|
+
run_forever
|
91
|
+
end
|
92
|
+
|
93
|
+
def pre_run
|
94
|
+
parse_options
|
95
|
+
pre_setup
|
96
|
+
setup_env
|
97
|
+
end
|
98
|
+
|
99
|
+
def parse_options
|
100
|
+
option_parser.parse!(argv.dup)
|
101
|
+
end
|
102
|
+
|
103
|
+
def pre_setup
|
104
|
+
if options[:initializer_path]
|
105
|
+
load options[:initializer_path].to_s.strip
|
106
|
+
else
|
107
|
+
false
|
108
|
+
end
|
109
|
+
end
|
110
|
+
|
111
|
+
def setup_env
|
112
|
+
require_relative 'env'
|
113
|
+
runtime_env.setup
|
114
|
+
end
|
115
|
+
|
116
|
+
def run_forever
|
117
|
+
ResqueAdmin::Scheduler.run
|
118
|
+
end
|
119
|
+
|
120
|
+
private
|
121
|
+
|
122
|
+
attr_reader :argv, :env
|
123
|
+
|
124
|
+
def runtime_env
|
125
|
+
@runtime_env ||= ResqueAdmin::Scheduler::Env.new(options)
|
126
|
+
end
|
127
|
+
|
128
|
+
def option_parser
|
129
|
+
OptionParser.new do |opts|
|
130
|
+
opts.banner = BANNER
|
131
|
+
opts.version = ResqueAdmin::Scheduler::VERSION
|
132
|
+
OPTIONS.each do |opt|
|
133
|
+
opts.on(*opt[:args], &opt[:callback].call(options))
|
134
|
+
end
|
135
|
+
end
|
136
|
+
end
|
137
|
+
|
138
|
+
def options
|
139
|
+
@options ||= {}.tap do |o|
|
140
|
+
CLI_OPTIONS_ENV_MAPPING.each do |key, envvar|
|
141
|
+
o[key] = env[envvar] if env.include?(envvar)
|
142
|
+
end
|
143
|
+
end
|
144
|
+
end
|
145
|
+
end
|
146
|
+
end
|
147
|
+
end
|