Class: IceCube::AnchoredMonthlyRule

Inherits:
Rule
  • Object
show all
Defined in:
lib/ice_cube/anchored_monthly_rule.rb

Overview

A monthly recurrence whose occurrences are fixed day-offsets from the Nth of each month. Unlike IceCube's ordinal monthly rules, which count each weekday from day 1 of the month, this counts from a single anchor date, so a pattern like "Sun/Tue/Thu and the following Sun, starting after the 1st Saturday" lands on the same relative days every month.

Instance Attribute Summary collapse

Class Method Summary collapse

Instance Method Summary collapse

Methods inherited from Rule

from_hash_without_anchored

Constructor Details

#initialize(anchor_weekday:, anchor_ordinal:, day_offsets:, interval: 1) ⇒ AnchoredMonthlyRule

Returns a new instance of AnchoredMonthlyRule.



13
14
15
16
17
18
19
20
21
22
23
24
25
# File 'lib/ice_cube/anchored_monthly_rule.rb', line 13

def initialize(anchor_weekday:, anchor_ordinal:, day_offsets:, interval: 1)
  @anchor_weekday = anchor_weekday.to_i
  # The occurrence search walks day by day to the anchor weekday, so a
  # weekday outside 0 (Sunday)-6 (Saturday) would never match and loop
  # forever. Reject it up front.
  unless (0..6).cover?(@anchor_weekday)
    raise ArgumentError, "anchor_weekday must be 0 (Sunday) through 6 (Saturday), got #{anchor_weekday.inspect}"
  end
  @anchor_ordinal = anchor_ordinal.to_i
  @day_offsets = Array(day_offsets).map(&:to_i).sort
  @interval = interval.to_i
  @until_time = nil
end

Instance Attribute Details

#anchor_ordinalObject (readonly)

Returns the value of attribute anchor_ordinal.



11
12
13
# File 'lib/ice_cube/anchored_monthly_rule.rb', line 11

def anchor_ordinal
  @anchor_ordinal
end

#anchor_weekdayObject (readonly)

Returns the value of attribute anchor_weekday.



11
12
13
# File 'lib/ice_cube/anchored_monthly_rule.rb', line 11

def anchor_weekday
  @anchor_weekday
end

#day_offsetsObject (readonly)

Returns the value of attribute day_offsets.



11
12
13
# File 'lib/ice_cube/anchored_monthly_rule.rb', line 11

def day_offsets
  @day_offsets
end

#intervalObject (readonly)

Returns the value of attribute interval.



27
28
29
# File 'lib/ice_cube/anchored_monthly_rule.rb', line 27

def interval
  @interval
end

#until_timeObject (readonly)

Returns the value of attribute until_time.



27
28
29
# File 'lib/ice_cube/anchored_monthly_rule.rb', line 27

def until_time
  @until_time
end

Class Method Details

.from_hash(original_hash) ⇒ Object



82
83
84
85
86
87
88
89
90
91
92
# File 'lib/ice_cube/anchored_monthly_rule.rb', line 82

def self.from_hash(original_hash)
  hash = IceCube::FlexibleHash.new(original_hash)
  rule = new(
    anchor_weekday: hash[:anchor_weekday],
    anchor_ordinal: hash[:anchor_ordinal],
    day_offsets: hash[:day_offsets],
    interval: hash[:interval] || 1
  )
  rule.until(TimeUtil.deserialize_time(hash[:until])) if hash[:until]
  rule
end

Instance Method Details

#full_required?Boolean

Counts are not supported for this rule (YAGNI); never force a full-history walk.

Returns:

  • (Boolean)


36
37
38
# File 'lib/ice_cube/anchored_monthly_rule.rb', line 36

def full_required?
  false
end

#next_time(time, start_time, closing_time) ⇒ Object

IceCube enumeration contract: the earliest occurrence at or after time (and at or after the schedule's start_time), or nil when the rule has terminated. Computed arithmetically rather than through the monthly interval spinner.



54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
# File 'lib/ice_cube/anchored_monthly_rule.rb', line 54

def next_time(time, start_time, closing_time)
  from = (time < start_time) ? start_time : time
  month = Date.new(from.year, from.month, 1)
  # A match always exists within a couple of months; 24 is a safe bound.
  24.times do
    occurrence_dates(month).each do |date|
      occ = at_wall_clock(start_time, date)
      next if occ < from || occ < start_time
      return nil if @until_time && occ > @until_time
      return occ
    end
    month = month.next_month
  end
  nil
end

#occurrence_countObject



40
41
42
# File 'lib/ice_cube/anchored_monthly_rule.rb', line 40

def occurrence_count
  nil
end

#to_hashObject



70
71
72
73
74
75
76
77
78
79
80
# File 'lib/ice_cube/anchored_monthly_rule.rb', line 70

def to_hash
  hash = {
    rule_type: self.class.name,
    interval: @interval,
    anchor_weekday: @anchor_weekday,
    anchor_ordinal: @anchor_ordinal,
    day_offsets: @day_offsets
  }
  hash[:until] = TimeUtil.serialize_time(@until_time) if @until_time
  hash
end

#to_sObject



94
95
96
97
# File 'lib/ice_cube/anchored_monthly_rule.rb', line 94

def to_s
  "Monthly, after the #{ordinalize(@anchor_ordinal)} #{Date::DAYNAMES[@anchor_weekday]}: " \
    "day offsets #{@day_offsets.join(", ")}"
end

#until(time) ⇒ Object



29
30
31
32
# File 'lib/ice_cube/anchored_monthly_rule.rb', line 29

def until(time)
  @until_time = time
  self
end

#validationsObject

This rule uses no IceCube validations; return an empty set so callers that introspect validations (e.g. selected_days) stay safe.



46
47
48
# File 'lib/ice_cube/anchored_monthly_rule.rb', line 46

def validations
  {}
end