1

Is there a way to validate crontabs with ruby? I've seen some decent php implementations.

Crontabs are pretty common knowledge, so I will spare the details, though I don't need the keywords like reboot to work. basically just numbers with commas and dashes like so

0 4 25,31 12 2012-2020
  • the next 8 christmases and new years at 4 am
dansch
  • 6,059
  • 4
  • 43
  • 59

4 Answers4

1

Read Validating Crontab Entries with PHP regarding this issue. Same applies to ruby, as regex is a limitation.

Community
  • 1
  • 1
Ωmega
  • 42,614
  • 34
  • 134
  • 203
0

Well, it's not the prettiest, though I made something that will split it up and parse it out. This isn't going to be touched very often so I'm not worried about speed, though it should handle everything that whenever cannot handle, such as ranges and comma separated dates, as well as reboot.

how it works?

there is a method called cron_wrong which just adds a base error.

I am using 2 columns, interval and interval_type . interval_type can be cron, @reboot, minute, hour, day, month, year. if it is minute, hour, day, month, or year, I am just passing the interval and interval_type to whenever and doing interval.send interval_type or using @reboot

The tricky part is when the interval_type is cron -- I am validating that it is indeed a valid cron string

 def cron_intervals
    non_num_at_beg_or_end = /^[^\d]|[^\d]$/
    has_non_nums = /[^\d]/

    return if interval_type == '@reboot'

    if interval_type != 'cron'
      has_non_nums.match(interval) { |str| errors.add :interval, "#{str} must be number" }
      errors.add :interval, "can't be blank" if interval.length == 0
      return
    end


    cron = interval.split(' ')

    if cron.length != 5
      cron_wrong
      errors.add :interval, 'Wrong number of arguments supplied, (must be 5 -- minute hour day month year)'
      return
    end


    cron.each do |c|
      # return if star or only numbers
      if c == '*' || !has_non_nums.match(c)
        next
      end

      non_num_at_beg_or_end.match(c) do |str|
        cron_wrong
        errors.add :interval, "Non-number '#{str}' found"
        return
      end

      c.split(',').each do |spl_dash|
        non_num_at_beg_or_end.match(spl_dash) do |str|
          cron_wrong
          errors.add :interval, "Non-number '#{str}' found"
          return
        end

        range = spl_dash.split('-')

        if range.length < 1 || range.length > 2
          cron_wrong 
          errors.add :interval, "ranges must be between two numbers"
          return
        elsif range.length == 2 and not (range[0].to_i < range[1].to_i)
          cron_wrong 
          errors.add :interval, "range numbers must be lower-higher"
          return
        end

        range.each do |num|

          # should be only numbers at this point
          has_non_nums.match(num) do |str|
            cron_wrong
            errors.add :interval, "Non-number '#{str}' found"
            return
          end
          if num.length.blank?
            cron_wrong
            errors.add :interval, "No number supplied"
          end
        end
      end
    end
  end
dansch
  • 6,059
  • 4
  • 43
  • 59
0

I used Fugit, the library used by Sidekiq-cron

def valid?(cron_expression)
   Fugit::Cron.parse(cron_expression).present?
end
Bruno Degomme
  • 883
  • 10
  • 11
-2

Check out this ruby gem to handle your cron queries:

https://github.com/javan/whenever

Here is an associated railscast episode:

http://railscasts.com/episodes/164-cron-in-ruby

Here is an example of the syntax:

every :day, :at => '12:20am' do
  command "echo 'Hi! Its 12:20 am.'"
end
sunnyrjuneja
  • 6,033
  • 2
  • 32
  • 51
  • 1
    This answer doesn't have anything to do with the issue of validating crontabs. He's not trying to create them, but check them. – Colin R Oct 16 '12 at 17:31
  • This outputs crontabs. Since they are extremely easy to write with this gem, he can compare the output to see if it is what he expected. – sunnyrjuneja Oct 16 '12 at 20:12