class Cyclical::Occurrence

Holds an occurence of a recurrence rule, can compute next and previous and list occurrences

Attributes

duration[RW]
rule[R]
start_time[R]

Public Class Methods

new(rule, start_time) click to toggle source
# File lib/cyclical/occurrence.rb, line 10
def initialize(rule, start_time)
  @rule = rule
  @start_time = @rule.match?(start_time, start_time) ? start_time : @rule.next(start_time, start_time)
end

Public Instance Methods

all() click to toggle source
# File lib/cyclical/occurrence.rb, line 54
def all
  if @rule.stop
    list_occurrences(@start_time) { |t| t < @rule.stop }
  else
    n = @rule.count
    list_occurrences(@start_time) { (n -= 1) >= 0 }
  end
end
next_occurrence(after) click to toggle source
# File lib/cyclical/occurrence.rb, line 15
def next_occurrence(after)
  next_occurrences(1, after).first
end
next_occurrences(n, after) click to toggle source
# File lib/cyclical/occurrence.rb, line 19
def next_occurrences(n, after)
  return [] if @rule.stop && after > @rule.stop
  time = (after <= @start_time ? @start_time : after)
  time = @rule.next(time, @start_time) unless @rule.match?(time, @start_time)

  list_occurrences(time) { (n -= 1) >= 0 }
end
occurrences_between(t1, t2) click to toggle source
# File lib/cyclical/occurrence.rb, line 39
def occurrences_between(t1, t2)
  raise ArgumentError, "Empty time interval" unless t2 > t1
  return [] if t2 <= @start_time || @rule.stop && t1 >= @rule.stop

  time = (t1 <= @start_time ? @start_time : t1)
  time = @rule.next(time, @start_time) unless @rule.match?(time, @start_time)

  list_occurrences(time) { |t| t < t2 }
end
previous_occurrence(before) click to toggle source
# File lib/cyclical/occurrence.rb, line 27
def previous_occurrence(before)
  previous_occurrences(1, before).first
end
previous_occurrences(n, before) click to toggle source
# File lib/cyclical/occurrence.rb, line 31
def previous_occurrences(n, before)
  return [] if before <= @start_time
  time = (@rule.stop.nil? || before < @rule.stop ? before : @rule.stop)
  time = @rule.previous(time, @start_time) # go back even if before matches the rule (half-open time intervals, remember?)

  list_occurrences(time, :back) { (n -= 1) >= 0 }.reverse
end
suboccurrences_between(t1, t2) click to toggle source
# File lib/cyclical/occurrence.rb, line 49
def suboccurrences_between(t1, t2)
  occurrences = occurrences_between(t1 - duration, t2)
  occurrences.map { |occ| Suboccurrence.find(:occurrence => (occ)..(occ + duration), :interval => t1..t2) }
end
to_hash() click to toggle source
# File lib/cyclical/occurrence.rb, line 63
def to_hash
  @rule.to_hash
end

Private Instance Methods

init_loop(from, direction) click to toggle source
# File lib/cyclical/occurrence.rb, line 95
def init_loop(from, direction)
  return 0, from unless @rule.count # without count limit, life is easy

  # with it, it's... well...
  if direction == :forward
    n = 0
    current = @start_time
    while current < from
      n += 1
      current = @rule.next(current, @start_time)
    end

    # return the n remaining events
    return (@rule.count - n), current
  else
    n = 0
    current = @start_time
    while current < from && (n += 1) < @rule.count
      current = @rule.next(current, @start_time)
    end

    # return all events (downloop - yaay, I invented a word - will stop on start time)
    return @rule.count, current
  end
end
list_occurrences(from, direction = :forward) { |current| ... } click to toggle source

yields valid occurrences, return false from the block to stop

# File lib/cyclical/occurrence.rb, line 70
def list_occurrences(from, direction = :forward, &block)
  raise ArgumentError, "From #{from} not matching the rule #{@rule} and start time #{@start_time}" unless @rule.match?(from, @start_time)

  results = []

  n, current = init_loop(from, direction)
  loop do
    # Rails.logger.debug("Listing occurrences of #{@rule}, going #{direction.to_s}, current: #{current}")
    # break on schedule span limits
    return results unless (current >= @start_time) && (@rule.stop.nil? || current < @rule.stop) && (@rule.count.nil? || (n -= 1) >= 0)

    # break on block condition
    return results unless yield current

    results << current

    # step
    if direction == :forward
      current = @rule.next(current, @start_time)
    else
      current = @rule.previous(current, @start_time)
    end
  end
end