class Whenever::CommandLine

Public Class Methods

execute(options={}) click to toggle source
# File lib/whenever/command_line.rb, line 5
def self.execute(options={})
  new(options).run
end
new(options={}) click to toggle source
# File lib/whenever/command_line.rb, line 9
def initialize(options={})
  @options = options

  @options[:crontab_command] ||= 'crontab'
  @options[:file]            ||= 'config/schedule.rb'
  @options[:cut]             ||= 0
  @options[:identifier]      ||= default_identifier

  if !File.exist?(@options[:file]) && @options[:clear].nil?
    warn("[fail] Can't find file: #{@options[:file]}")
    exit(1)
  end

  if [@options[:update], @options[:write], @options[:clear]].compact.length > 1
    warn("[fail] Can only update, write or clear. Choose one.")
    exit(1)
  end

  unless @options[:cut].to_s =~ /[0-9]*/
    warn("[fail] Can't cut negative lines from the crontab #{options[:cut]}")
    exit(1)
  end
  @options[:cut] = @options[:cut].to_i

  @timestamp = Time.now.to_s
end

Public Instance Methods

run() click to toggle source
# File lib/whenever/command_line.rb, line 36
def run
  if @options[:update] || @options[:clear]
    write_crontab(updated_crontab)
  elsif @options[:write]
    write_crontab(whenever_cron)
  else
    puts Whenever.cron(@options)
    puts "## [message] Above is your schedule file converted to cron syntax; your crontab file was not updated."
    puts "## [message] Run `whenever --help' for more options."
    exit(0)
  end
end

Protected Instance Methods

comment_base(include_timestamp = true) click to toggle source
# File lib/whenever/command_line.rb, line 127
def comment_base(include_timestamp = true)
  if include_timestamp
    "Whenever generated tasks for: #{@options[:identifier]} at: #{@timestamp}"
  else
    "Whenever generated tasks for: #{@options[:identifier]}"
  end
end
comment_close(include_timestamp = true) click to toggle source
# File lib/whenever/command_line.rb, line 139
def comment_close(include_timestamp = true)
  "# End #{comment_base(include_timestamp)}"
end
comment_close_regex() click to toggle source
# File lib/whenever/command_line.rb, line 147
def comment_close_regex
  "#{comment_close(false)}(#{timestamp_regex}|)"
end
comment_open(include_timestamp = true) click to toggle source
# File lib/whenever/command_line.rb, line 135
def comment_open(include_timestamp = true)
  "# Begin #{comment_base(include_timestamp)}"
end
comment_open_regex() click to toggle source
# File lib/whenever/command_line.rb, line 143
def comment_open_regex
  "#{comment_open(false)}(#{timestamp_regex}|)"
end
default_identifier() click to toggle source
# File lib/whenever/command_line.rb, line 51
def default_identifier
  File.expand_path(@options[:file])
end
prepare(contents) click to toggle source
# File lib/whenever/command_line.rb, line 115
def prepare(contents)
  # Strip n lines from the top of the file as specified by the :cut option.
  # Use split with a -1 limit option to ensure the join is able to rebuild
  # the file with all of the original seperators in-tact.
  stripped_contents = contents.split($/,-1)[@options[:cut]..-1].join($/)

  # Some cron implementations require all non-comment lines to be newline-
  # terminated. (issue #95) Strip all newlines and replace with the default
  # platform record seperator ($/)
  stripped_contents.gsub!(/\s+$/, $/)
end
read_crontab() click to toggle source
# File lib/whenever/command_line.rb, line 60
def read_crontab
  return @current_crontab if instance_variable_defined?(:@current_crontab)

  command = [@options[:crontab_command]]
  command << '-l'
  command << "-u #{@options[:user]}" if @options[:user]

  command_results  = %x[#{command.join(' ')} 2> /dev/null]
  @current_crontab = $?.exitstatus.zero? ? prepare(command_results) : ''
end
timestamp_regex() click to toggle source
# File lib/whenever/command_line.rb, line 151
def timestamp_regex
  " at: \\d{4}-\\d{2}-\\d{2} \\d{2}:\\d{2}:\\d{2} ([+-]\\d{4}|UTC)"
end
updated_crontab() click to toggle source
# File lib/whenever/command_line.rb, line 95
def updated_crontab
  # Check for unopened or unclosed identifier blocks
  if read_crontab =~ Regexp.new("^#{comment_open_regex}\s*$") && (read_crontab =~ Regexp.new("^#{comment_close_regex}\s*$")).nil?
    warn "[fail] Unclosed indentifier; Your crontab file contains '#{comment_open(false)}', but no '#{comment_close(false)}'"
    exit(1)
  elsif (read_crontab =~ Regexp.new("^#{comment_open_regex}\s*$")).nil? && read_crontab =~ Regexp.new("^#{comment_close_regex}\s*$")
    warn "[fail] Unopened indentifier; Your crontab file contains '#{comment_close(false)}', but no '#{comment_open(false)}'"
    exit(1)
  end

  # If an existing identifier block is found, replace it with the new cron entries
  if read_crontab =~ Regexp.new("^#{comment_open_regex}\s*$") && read_crontab =~ Regexp.new("^#{comment_close_regex}\s*$")
    # If the existing crontab file contains backslashes they get lost going through gsub.
    # .gsub('\\', '\\\\\\') preserves them. Go figure.
    read_crontab.gsub(Regexp.new("^#{comment_open_regex}\s*$.+^#{comment_close_regex}\s*$", Regexp::MULTILINE), whenever_cron.chomp.gsub('\\', '\\\\\\'))
  else # Otherwise, append the new cron entries after any existing ones
    [read_crontab, whenever_cron].join("\n\n")
  end.gsub(/\n{3,}/, "\n\n") # More than two newlines becomes just two.
end
whenever_cron() click to toggle source
# File lib/whenever/command_line.rb, line 55
def whenever_cron
  return '' if @options[:clear]
  @whenever_cron ||= [comment_open, Whenever.cron(@options), comment_close].compact.join("\n") + "\n"
end
write_crontab(contents) click to toggle source
# File lib/whenever/command_line.rb, line 71
def write_crontab(contents)
  command = [@options[:crontab_command]]
  command << "-u #{@options[:user]}" if @options[:user]
  # Solaris/SmartOS cron does not support the - option to read from stdin.
  command << "-" unless OS.solaris?

  IO.popen(command.join(' '), 'r+') do |crontab|
    crontab.write(contents)
    crontab.close_write
  end

  success = $?.exitstatus.zero?

  if success
    action = 'written' if @options[:write]
    action = 'updated' if @options[:update]
    puts "[write] crontab file #{action}"
    exit(0)
  else
    warn "[fail] Couldn't write crontab; try running `whenever' with no options to ensure your schedule file is valid."
    exit(1)
  end
end