rbcrontab 0.1 → 1.0
Sign up to get free protection for your applications and to get access to all the features.
- data/Rakefile +1 -1
- data/examples/full_example.rb +72 -0
- data/lib/{extensions.rb → crontab_helper.rb} +12 -4
- data/lib/rbcrontab.rb +178 -24
- metadata +4 -3
data/Rakefile
CHANGED
@@ -0,0 +1,72 @@
|
|
1
|
+
#!/usr/bin/env ruby -rrubygems
|
2
|
+
require 'rbcrontab'
|
3
|
+
|
4
|
+
# setup your basic file paths
|
5
|
+
ROOT = File.dirname(__FILE__)
|
6
|
+
|
7
|
+
# global setup -- we want to source our profile before any cron command
|
8
|
+
source_bash = 'source $HOME/.bash_profile'
|
9
|
+
|
10
|
+
# simple rake helper method
|
11
|
+
def rake(app, command, silent = true)
|
12
|
+
"rake -f#{ROOT}/#{app}/Rakefile #{'-s' if silent} #{command}"
|
13
|
+
end
|
14
|
+
|
15
|
+
# simple script helper method
|
16
|
+
def script(name)
|
17
|
+
"#{ROOT}/scripts/name"
|
18
|
+
end
|
19
|
+
|
20
|
+
#
|
21
|
+
# Crontab Start
|
22
|
+
#
|
23
|
+
|
24
|
+
crontab = Crontab.new do |cron|
|
25
|
+
|
26
|
+
# mail it to me
|
27
|
+
cron.mail_to 'you@example.com'
|
28
|
+
# add my custom paths
|
29
|
+
cron.path %w{/usr/local/bin /usr/bin $HOME/bin}, false
|
30
|
+
# setup my home variable
|
31
|
+
cron.home ENV['HOME']
|
32
|
+
# make sure every schedule sources the bash profile and sets
|
33
|
+
# the RAILS_ENV to production
|
34
|
+
cron.global_setup source_bash, 'RAILS_ENV=production'
|
35
|
+
|
36
|
+
# handle some requests through rake on some app
|
37
|
+
cron.desc 'handle requests at 5 min intervals on weekdays'
|
38
|
+
cron.schedule :every => 5.minutes, :on => Days::WEEKDAYS do
|
39
|
+
rake 'app', 'requests:process'
|
40
|
+
end
|
41
|
+
|
42
|
+
# do some nightly database backups through a bash script we have
|
43
|
+
# in our scripts folder
|
44
|
+
cron.desc 'backup app database files at midnight on weekdays'
|
45
|
+
cron.schedule :at => Time::MIDNIGHT, :on => Days::WEEKDAYS do
|
46
|
+
script 'backup_database_script'
|
47
|
+
end
|
48
|
+
|
49
|
+
# clean up some old user searches if they are old
|
50
|
+
cron.desc 'clean old user searches in some app if > 1 week old on monday'
|
51
|
+
# set the DAYS_OLD environment variable so the rake task can use it
|
52
|
+
cron.schedule :at => Time::MIDNIGHT, :on => Days::MONDAY, :setup => 'DAYS_OLD=7' do
|
53
|
+
rake 'app', 'searches:clean'
|
54
|
+
end
|
55
|
+
|
56
|
+
# just a stupid 'hello world' task to show how to ignore global setups
|
57
|
+
cron.desc 'at 8:30am echo "I GOT A CASE OF THE MUNDAYS!" on mondays'
|
58
|
+
cron.schedule :at => '08:30', :on => Days::MONDAY, :ignore_global_setup => true do
|
59
|
+
"echo 'I GOT A CASE OF THE MUNDAYS!'"
|
60
|
+
end
|
61
|
+
|
62
|
+
# pointless, but just to show how to override the global setup and make your own for this one
|
63
|
+
# schedule
|
64
|
+
cron.desc 'using our OWN setup, not the global one'
|
65
|
+
cron.schedule :every => 1.hour, :on => Days::WEEKENDS, :ignore_global_setup => true, :setup => ['A=1', 'B=2'] do
|
66
|
+
"echo $A && echo $B"
|
67
|
+
end
|
68
|
+
|
69
|
+
end
|
70
|
+
|
71
|
+
# eval the CLI script so you can run it from the command line
|
72
|
+
Crontab::CLI.start! binding
|
@@ -1,14 +1,14 @@
|
|
1
1
|
# create the cron equiv of Fixnum.minutes
|
2
2
|
# Example:
|
3
|
-
# 10.minutes #=> '10
|
4
|
-
# 5.hours #=> '
|
3
|
+
# 10.minutes #=> '*/10 *'
|
4
|
+
# 5.hours #=> '0 */5'
|
5
5
|
class Fixnum
|
6
6
|
def minutes
|
7
|
-
'
|
7
|
+
'*/%s *' % self
|
8
8
|
end
|
9
9
|
alias_method :minute, :minutes
|
10
10
|
def hours
|
11
|
-
'
|
11
|
+
'0 */%s' % self
|
12
12
|
end
|
13
13
|
alias_method :hour, :hours
|
14
14
|
end
|
@@ -37,4 +37,12 @@ class Days
|
|
37
37
|
EVERYDAY = '*'
|
38
38
|
WEEKDAYS = '1-5'
|
39
39
|
WEEKENDS = '0,6'
|
40
|
+
SUNDAY = 0
|
41
|
+
MONDAY = 1
|
42
|
+
TUESDAY = 2
|
43
|
+
WEDNESDAY = 3
|
44
|
+
THURSDAY = 4
|
45
|
+
FRIDAY = 5
|
46
|
+
SATURDAY = 6
|
47
|
+
SUNDAY_ALT= 7
|
40
48
|
end
|
data/lib/rbcrontab.rb
CHANGED
@@ -33,7 +33,7 @@
|
|
33
33
|
# FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
|
34
34
|
# OTHER DEALINGS IN THE SOFTWARE.
|
35
35
|
require 'optparse'
|
36
|
-
require '
|
36
|
+
require 'crontab_helper'
|
37
37
|
# Example:
|
38
38
|
# crontab = Crontab.new do |cron|
|
39
39
|
# cron.desc 'this is my schedule description'
|
@@ -43,18 +43,23 @@ require 'extensions'
|
|
43
43
|
#
|
44
44
|
# see the examples directory for a more verbose example
|
45
45
|
class Crontab
|
46
|
+
|
46
47
|
# can either use as a block or regular object
|
47
48
|
# Crontab.new { |cron| ... }
|
48
49
|
# crontab = Crontab.new
|
49
|
-
def initialize
|
50
|
+
def initialize(options = {})
|
50
51
|
@schedules = []
|
52
|
+
@global_setup = nil
|
53
|
+
@header = options[:header] || false
|
51
54
|
yield self if block_given?
|
52
55
|
end
|
56
|
+
|
53
57
|
# set the MAILTO user for the _entire_ crontab
|
54
58
|
# to set it per-schedule view the schedule :mail_to arg
|
55
59
|
def mail_to(who)
|
56
60
|
@mail_to = who
|
57
61
|
end
|
62
|
+
|
58
63
|
# set the path for the _entire_ crontab
|
59
64
|
# include_path will keep the original $PATH in there
|
60
65
|
def path(paths, include_path = true)
|
@@ -63,28 +68,75 @@ class Crontab
|
|
63
68
|
@paths << paths
|
64
69
|
@paths = @paths.flatten.join(':')
|
65
70
|
end
|
71
|
+
|
72
|
+
# defines your setup for every schedule
|
73
|
+
def global_setup(*commands)
|
74
|
+
@global_setup = commands
|
75
|
+
end
|
76
|
+
|
66
77
|
# set your home path, probably not necessary, but why not
|
67
78
|
def home(home_path)
|
68
79
|
@home = home_path
|
69
80
|
end
|
81
|
+
|
70
82
|
# creates a schedule object from the args passes
|
71
83
|
# your command will go in the block
|
72
84
|
#
|
73
85
|
# crontab.schedule(:args => ...) { "command to run" }
|
74
|
-
def schedule(
|
75
|
-
|
86
|
+
def schedule(settings, &block)
|
87
|
+
settings.merge! :description => @last_description if @last_description
|
88
|
+
setup = []
|
89
|
+
setup << @global_setup if @global_setup and not settings[:ignore_global_setup]
|
90
|
+
setup << settings[:setup] if settings[:setup]
|
91
|
+
settings[:setup] = setup.flatten.uniq.join(' && ') unless setup.empty?
|
76
92
|
@last_description = nil
|
77
|
-
@schedules << Schedule.new(
|
93
|
+
@schedules << Schedule.new(settings, &block)
|
78
94
|
end
|
95
|
+
|
79
96
|
# runs the to_cron method on all schedules and dumps
|
80
97
|
# the crontab to be copied/pasted
|
81
|
-
|
82
|
-
|
98
|
+
# if display_cron_header is true, it will output the
|
99
|
+
# text block in cron_description_block
|
100
|
+
#
|
101
|
+
# FIXME: ugly as fuck
|
102
|
+
def to_crontab(display_cron_header = false)
|
103
|
+
crontab = ''
|
104
|
+
crontab << header
|
105
|
+
crontab << cron_description_block if display_cron_header
|
106
|
+
@schedules.each { |s| crontab << s.to_cron + "\n\n" }
|
107
|
+
crontab
|
108
|
+
end
|
109
|
+
|
110
|
+
# goofy cron description text to visually see whats going on if you aren't
|
111
|
+
# familiar with crontabs
|
112
|
+
# FIXME: ugly as fuck
|
113
|
+
def cron_description_block
|
114
|
+
%{
|
115
|
+
# +---------------- minute (0 - 59)
|
116
|
+
# | +------------- hour (0 - 23)
|
117
|
+
# | | +---------- day of month (1 - 31)
|
118
|
+
# | | | +------- month (1 - 12)
|
119
|
+
# | | | | +---- day of week (0 - 7) (Sunday=0 or 7)
|
120
|
+
# | | | | |
|
121
|
+
# * * * * * command to be executed
|
122
|
+
|
123
|
+
}
|
83
124
|
end
|
125
|
+
|
84
126
|
# sets the description for the schedule immediately following
|
85
127
|
def desc(description)
|
86
128
|
@last_description = description
|
87
129
|
end
|
130
|
+
|
131
|
+
# creates a human readable list of all crob schedules
|
132
|
+
def to_s(verbose = true)
|
133
|
+
@schedules.inject('') do |o,s|
|
134
|
+
o << s.to_s + "\n"
|
135
|
+
o << "`#{s.command}`\n" if verbose
|
136
|
+
o << "\n"
|
137
|
+
end
|
138
|
+
end
|
139
|
+
|
88
140
|
# lists the descriptions of all cron schedules for easy reviewing
|
89
141
|
def describe
|
90
142
|
output = "Crontabs\n" + ('-' * 40) + "\n"
|
@@ -94,6 +146,7 @@ class Crontab
|
|
94
146
|
end
|
95
147
|
output
|
96
148
|
end
|
149
|
+
|
97
150
|
def header #:nodoc:
|
98
151
|
headers = []
|
99
152
|
headers << "HOME=%s" % @home if @home
|
@@ -102,12 +155,14 @@ class Crontab
|
|
102
155
|
headers << "\n" unless headers.empty?
|
103
156
|
headers.join("\n")
|
104
157
|
end
|
158
|
+
|
105
159
|
# Example:
|
106
160
|
# crontab.schedule(:at => Time::MIDNIGHT, :on => Day::WEEKDAYS) { 'backup_database' }
|
107
161
|
# crontab.schedule(:every => 10.minutes, :on => Day::WEEKENDS) { 'check something' }
|
108
162
|
# crontab.schedule(:at => '18:00', :on_the => '5', :of => Month::MAY) { 'party!' }
|
163
|
+
# crontab.schedule(:every => 5.minutes, :on =>Days::WEEKDAYS, :setup => 'source $HOME/.bashrc') { 'command' }
|
164
|
+
# crontab.schedule(:every => 1.hour, :on =>Days::MONDAY, :setup => ['HI=1', 'WORLD=2']) { 'command' }
|
109
165
|
class Schedule
|
110
|
-
attr_reader :description
|
111
166
|
# args consist of
|
112
167
|
# * :every => 1.minute, 10.minutes, 1.hour, 5.hours -- minutes or hours
|
113
168
|
# * :at => '00:00' -- 24 hour time for when to run
|
@@ -115,37 +170,130 @@ class Crontab
|
|
115
170
|
# * :on_the => '1,2,3' -- days of the _month_
|
116
171
|
# * :in => Month::JANUARY -- month [0..12]
|
117
172
|
# * :of => Month::JANUARY -- month [0..12] -- alias for readability
|
118
|
-
|
119
|
-
|
173
|
+
# * :setup => 'command' (or ['command', 'command', ...]) -- anything to do _before_ running your comand (source bashrc or whatnot for example)
|
174
|
+
# * :ignore_global_setup => true|false -- if you would like to ignore the global setup command
|
175
|
+
def initialize(settings, &block)
|
176
|
+
settings.each { |k,v| instance_variable_set "@#{k}", v }
|
120
177
|
@do = yield
|
121
178
|
end
|
179
|
+
|
180
|
+
# the command the schedule should run
|
181
|
+
def command
|
182
|
+
all_commands = []
|
183
|
+
all_commands << @setup if @setup
|
184
|
+
all_commands << @do
|
185
|
+
all_commands.join ' && '
|
186
|
+
end
|
187
|
+
|
122
188
|
# builds the cron schedule line
|
123
189
|
def to_cron
|
124
190
|
cron_lines = []
|
125
|
-
cron_lines <<
|
191
|
+
cron_lines << "# #{@description}" if @description
|
126
192
|
cron_lines << ('MAILTO=%s' % [@mail_to]) if @mail_to
|
127
193
|
cron_lines << ("%s %s %s %s %s" % [time, days_of_month, months, weekdays, command])
|
128
194
|
cron_lines.join("\n")
|
129
195
|
end
|
196
|
+
|
197
|
+
# outputs a human readable format of the cron schedule
|
198
|
+
def to_s
|
199
|
+
output = ''
|
200
|
+
output << human_time if human_time
|
201
|
+
output << ' on ' + human_day_of_month if human_day_of_month
|
202
|
+
output << ' in ' + human_month if human_month
|
203
|
+
output << ' on ' + human_weekdays if human_weekdays
|
204
|
+
output << " \"#{@description}\"" if @description
|
205
|
+
output
|
206
|
+
end
|
207
|
+
|
130
208
|
private
|
131
|
-
|
132
|
-
|
209
|
+
|
210
|
+
# converts a cron weekday list into human readable form
|
211
|
+
def human_weekdays
|
212
|
+
case weekdays
|
213
|
+
when '*': nil
|
214
|
+
when Days::WEEKDAYS: 'Weekdays'
|
215
|
+
when Days::WEEKENDS: 'Weekends'
|
216
|
+
when Days::MONDAY: 'Monday'
|
217
|
+
when Days::TUESDAY: 'Tuesday'
|
218
|
+
when Days::WEDNESDAY: 'Wednesday'
|
219
|
+
when Days::THURSDAY: 'Thursday'
|
220
|
+
when Days::FRIDAY: 'Friday'
|
221
|
+
when Days::SATURDAY: 'Saturday'
|
222
|
+
when Days::SUNDAY, Days::SUNDAY_ALT: 'Sunday'
|
223
|
+
else
|
224
|
+
weekdays
|
225
|
+
end
|
133
226
|
end
|
134
|
-
|
135
|
-
|
136
|
-
|
137
|
-
|
138
|
-
|
227
|
+
|
228
|
+
# converts a cron day of month list into human readable form
|
229
|
+
def human_day_of_month
|
230
|
+
case days_of_month
|
231
|
+
when '*': nil
|
232
|
+
else
|
233
|
+
"the %s" % days_of_month
|
234
|
+
end
|
235
|
+
end
|
236
|
+
|
237
|
+
# converts a cron month into a human readable month
|
238
|
+
def human_month
|
239
|
+
case months
|
240
|
+
when '*': nil
|
241
|
+
when Month::JANUARY: 'January'
|
242
|
+
when Month::FEBRUARY: 'February'
|
243
|
+
when Month::MARCH: 'March'
|
244
|
+
when Month::APRIL: 'April'
|
245
|
+
when Month::MAY: 'May'
|
246
|
+
when Month::JUNE: 'June'
|
247
|
+
when Month::JULY: 'July'
|
248
|
+
when Month::AUGUST: 'August'
|
249
|
+
when Month::SEPTEMBER: 'September'
|
250
|
+
when Month::OCTOBER: 'October'
|
251
|
+
when Month::NOVEMBER: 'November'
|
252
|
+
else
|
253
|
+
months
|
254
|
+
end
|
255
|
+
end
|
256
|
+
|
257
|
+
# converts a cron minute/hour list into human readable format
|
258
|
+
def human_time
|
259
|
+
minutes, hours = time.split(' ')
|
260
|
+
|
261
|
+
minute_description = \
|
262
|
+
case minutes
|
263
|
+
when '*': 'Every minute'
|
264
|
+
when '0': nil
|
265
|
+
when /\*\/(\d*)/: "every #{$1} minutes"
|
266
|
+
end
|
267
|
+
|
268
|
+
hour_description = \
|
269
|
+
case hours
|
270
|
+
when '*': nil
|
271
|
+
when /\*\/(\d*)/: $1.to_i == 1 ? 'every hour' : "every #{$1} hours"
|
272
|
+
end
|
273
|
+
|
274
|
+
unless minute_description or hour_description
|
275
|
+
output = "at %02d:%02d" % [hours.to_i, minutes.to_i]
|
276
|
+
else
|
277
|
+
output = ''
|
278
|
+
output << minute_description if minute_description
|
279
|
+
output << ' in ' if hour_description and minute_description
|
280
|
+
output << hour_description if hour_description
|
281
|
+
end
|
282
|
+
output.capitalize
|
139
283
|
end
|
284
|
+
|
140
285
|
def time #:nodoc:
|
141
286
|
@at ? (@at || '*:*').split(':').reverse.join(' ') : (@every || '* *')
|
142
287
|
end
|
288
|
+
|
143
289
|
def days_of_month #:nodoc:
|
144
290
|
@on_the || '*'
|
145
291
|
end
|
292
|
+
|
146
293
|
def months #:nodoc:
|
147
294
|
@in || @of || '*'
|
148
295
|
end
|
296
|
+
|
149
297
|
def weekdays #:nodoc:
|
150
298
|
@on || '*'
|
151
299
|
end
|
@@ -153,14 +301,17 @@ class Crontab
|
|
153
301
|
# Simple CLI class which includes basic command line options
|
154
302
|
# to the cron script
|
155
303
|
# Options:
|
156
|
-
# -h --help
|
157
|
-
# -
|
304
|
+
# -h --help print the usage
|
305
|
+
# --cron-description shows the cron description block
|
306
|
+
# -d --describe describes the crontab
|
158
307
|
class CLI
|
159
308
|
# evaluates the necessary script to handle CLI option parsing
|
160
309
|
# it wants the current binding so inside your script you'd do
|
161
310
|
# Example:
|
162
311
|
# Crontab::CLI.start!(binding)
|
163
|
-
|
312
|
+
# if your crontab is _not_ named 'crontab' you must pass the object in using
|
313
|
+
# Crontab::CLI.start! binding, whatever_your_crontab_object_is_called
|
314
|
+
def self.start!(binding, crontab_object = 'crontab')
|
164
315
|
eval %{if $0 == __FILE__
|
165
316
|
options = {}
|
166
317
|
OptionParser.new do |opts|
|
@@ -168,6 +319,9 @@ class Crontab
|
|
168
319
|
opts.on('-d', '--describe', 'Describe current cron schedules') do |d|
|
169
320
|
options[:describe] = d
|
170
321
|
end
|
322
|
+
opts.on('--cron-description', 'Show the cron description block in the crontab') do |c|
|
323
|
+
options[:cron_description] = c
|
324
|
+
end
|
171
325
|
opts.on_tail("-h", "--help", "Show this message") do
|
172
326
|
puts opts
|
173
327
|
exit
|
@@ -175,12 +329,12 @@ class Crontab
|
|
175
329
|
end.parse!
|
176
330
|
|
177
331
|
if options[:describe]
|
178
|
-
puts
|
332
|
+
puts #{crontab_object}.to_s
|
179
333
|
else
|
180
|
-
puts
|
334
|
+
puts #{crontab_object}.to_crontab(options[:cron_description])
|
181
335
|
end
|
182
336
|
end
|
183
|
-
},
|
337
|
+
}, binding
|
184
338
|
end
|
185
339
|
end
|
186
340
|
end
|
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: rbcrontab
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: "0
|
4
|
+
version: "1.0"
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Rob Hurring
|
@@ -9,7 +9,7 @@ autorequire:
|
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
11
|
|
12
|
-
date: 2008-
|
12
|
+
date: 2008-09-22 00:00:00 -04:00
|
13
13
|
default_executable:
|
14
14
|
dependencies: []
|
15
15
|
|
@@ -23,7 +23,8 @@ extra_rdoc_files:
|
|
23
23
|
- README
|
24
24
|
files:
|
25
25
|
- examples/cli_crontab.rb
|
26
|
-
-
|
26
|
+
- examples/full_example.rb
|
27
|
+
- lib/crontab_helper.rb
|
27
28
|
- lib/rbcrontab.rb
|
28
29
|
- README
|
29
30
|
- Rakefile
|