rbcrontab 0.1 → 1.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.
data/Rakefile CHANGED
@@ -6,7 +6,7 @@ require 'rake/gempackagetask'
6
6
 
7
7
  spec = Gem::Specification.new do |s|
8
8
  s.name = 'rbcrontab'
9
- s.version = '0.1'
9
+ s.version = '1.0'
10
10
  s.summary = 'Generate crontabs using ruby'
11
11
  s.author = 'Rob Hurring'
12
12
  s.email = 'rob@ubrio.us'
@@ -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 #=> '* 5/*'
3
+ # 10.minutes #=> '*/10 *'
4
+ # 5.hours #=> '0 */5'
5
5
  class Fixnum
6
6
  def minutes
7
- '%s/* *' % self
7
+ '*/%s *' % self
8
8
  end
9
9
  alias_method :minute, :minutes
10
10
  def hours
11
- '* %s/*' % self
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
@@ -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 'extensions'
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(*args, &block)
75
- args.first.merge! :description => @last_description if @last_description
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(*args, &block)
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
- def to_crontab
82
- @schedules.inject(header){ |o, s| o << s.to_cron + "\n\n" }
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
- def initialize(*args, &block)
119
- args.first.each { |k,v| instance_variable_set "@#{k}", v }
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 << cron_description if @description
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
- def cron_description #:nodoc:
132
- "# #{@description}" if @description
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
- def command #:nodoc:
135
- all_commands = []
136
- all_commands << @setup if @setup
137
- all_commands << @do
138
- all_commands.join ' && '
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 print the usage
157
- # -d --describe describes the crontab
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
- def self.start!(the_binding)
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 crontab.describe
332
+ puts #{crontab_object}.to_s
179
333
  else
180
- puts crontab.to_crontab
334
+ puts #{crontab_object}.to_crontab(options[:cron_description])
181
335
  end
182
336
  end
183
- }, the_binding
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.1"
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-08-24 00:00:00 -04:00
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
- - lib/extensions.rb
26
+ - examples/full_example.rb
27
+ - lib/crontab_helper.rb
27
28
  - lib/rbcrontab.rb
28
29
  - README
29
30
  - Rakefile