Module: Midnight

Defined in:
lib/midnight.rb,
lib/midnight/handler.rb,
lib/midnight/version.rb,
lib/midnight/midnight.rb

Defined Under Namespace

Classes: Converter, CronExpression, InvalidArgumentException, Repeater, Token

Constant Summary collapse

VERSION =
'1.0.0'

Class Method Summary collapse

Class Method Details

.base_tokenize(text) ⇒ Object

Split the text on spaces and convert each word into a Token



55
56
57
# File 'lib/midnight/midnight.rb', line 55

def base_tokenize(text) #:nodoc:
  text.split(' ').map { |word| Token.new(word) }
end

.debugObject



23
# File 'lib/midnight.rb', line 23

def self.debug; false; end

.dwrite(msg) ⇒ Object



25
26
27
# File 'lib/midnight.rb', line 25

def self.dwrite(msg)
  puts msg if Midnight.debug
end

.guessObject



4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
# File 'lib/midnight/handler.rb', line 4

def guess()
  # Not sure we'll need interval for cron output
  interval = guess_unit_types
  interval ||= guess_weekday
  interval ||= guess_weekday
  interval ||= guess_month_names
  interval ||= guess_number_and_unit
  interval ||= guess_special

  # defines the next occurrence of this midnight if not set in a guess routine
  @next ||= @start + (interval * 60 * 60 * 24) if interval
  
  # # check to see if the start date is > NOW and, if so, set the next occurrence = start
  @next = @start if @start.to_time > Time.now
  
  # # return the next occurrence
  return @next.to_time if interval
end

.guess_month_namesObject



39
40
41
42
43
44
45
# File 'lib/midnight/handler.rb', line 39

def guess_month_names
  if token_types.same?([:month_name]) then
    @start = Chronic.parse("#{token_of_type(:month_name).start.to_s} 1")
    interval = 30
  end
  interval
end

.guess_number_and_unitObject



47
48
49
50
51
52
53
# File 'lib/midnight/handler.rb', line 47

def guess_number_and_unit
  interval = token_of_type(:number).interval if token_types.same?([:number, :day])
  interval = (token_of_type(:number).interval * 7) if token_types.same?([:number, :week])
  interval = (token_of_type(:number).interval * 30) if token_types.same?([:number, :month])
  interval = (token_of_type(:number).interval * 365) if token_types.same?([:number, :year])
  interval
end

.guess_specialObject



55
56
57
58
59
60
# File 'lib/midnight/handler.rb', line 55

def guess_special
  interval = guess_special_other
  interval ||= guess_special_beginning
  interval ||= guess_special_middle
  interval ||= guess_special_end
end

.guess_unit_typesObject



23
24
25
26
27
28
29
# File 'lib/midnight/handler.rb', line 23

def guess_unit_types
  interval = 1 if token_types.same?([:day])
  interval = 7 if token_types.same?([:week])
  interval = 30 if token_types.same?([:month])
  interval = 365 if token_types.same?([:year])
  interval
end

.guess_weekdayObject



31
32
33
34
35
36
37
# File 'lib/midnight/handler.rb', line 31

def guess_weekday
  if token_types.same?([:weekday]) then
    @start = Chronic.parse(token_of_type(:weekday).start.to_s)
    interval = 7
  end
  interval
end

.numericize_ordinals(text) ⇒ Object

Convert ordinal words to numeric ordinals (third => 3rd)



60
61
62
# File 'lib/midnight/midnight.rb', line 60

def numericize_ordinals(text) #:nodoc:
  text = text.gsub(/\b(\d*)(st|nd|rd|th)\b/, '\1')
end

.parse(text, specified_options = {}) ⇒ Object



6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
# File 'lib/midnight/midnight.rb', line 6

def parse(text, specified_options = {})
  # get options and set defaults if necessary
  default_options = {:start => Time.now}
  options = default_options.merge specified_options

  # ensure the specified options are valid
  specified_options.keys.each do |key|
    default_options.keys.include?(key) || raise(InvalidArgumentException, "#{key} is not a valid option key.")
  end
  Chronic.parse(specified_options[:start]) || raise(InvalidArgumentException, ':start specified is not a valid datetime.') if specified_options[:start]

  # remove every is specified
  text = text.gsub(/^every\s\b/, '')

  # put the text into a normal format to ease scanning using Chronic
  text = pre_normalize(text)
  text = Chronic::Parser.new.pre_normalize(text)
  text = numericize_ordinals(text)

  # check to see if this event starts some other time and reset now
  event, starting = text.split('starting')
  @start = (Chronic.parse(starting) || options[:start])
  @next = nil

  # split into tokens
  @tokens = base_tokenize(event)

  # scan the tokens with each token scanner
  @tokens = Repeater.scan(@tokens)

  # remove all tokens without a type
  @tokens.reject! {|token| token.type.nil? }

  converter = Converter.new
  cron_expression = converter.convert_tokens_to_cron_expression(@tokens)

  return cron_expression
end

.pre_normalize(text) ⇒ Object

Normalize natural string removing prefix language



46
47
48
49
50
51
# File 'lib/midnight/midnight.rb', line 46

def pre_normalize(text)
  normalized_text = text.gsub(/^every\s\b/, '')
  normalized_text = text.gsub(/^each\s\b/, '')
  normalized_text = text.gsub(/^on the\s\b/, '')
  normalized_text
end

.token_typesObject

Returns an array of types for all tokens



65
66
67
# File 'lib/midnight/midnight.rb', line 65

def token_types
  @tokens.map(&:type)
end