Class: Rem2ics::Recurrence
- Inherits:
-
Object
- Object
- Rem2ics::Recurrence
- Defined in:
- lib/rem2ics/recurrence.rb
Overview
How a reminder repeats, expressed the way iCalendar expresses it -- when iCalendar can express it at all.
A reminder's trigger and an RRULE overlap, but neither contains the other.
REM 15 and FREQ=MONTHLY;BYMONTHDAY=15 are the same thing. REM Mon 13 SKIP OMIT Sat Sun is not any RRULE: it means "the Monday of the week the
13th falls in, unless that lands on an omitted day, in which case do not
trigger at all", and BYDAY has no way to say the second half.
So the rule is a guess, and the guess is checked. Remind::Reminder can
produce the dates the reminder actually fires on -- they come from
ComputeTrigger, which is what remind itself runs -- so the candidate
RRULE is expanded with ice_cube and compared against them. If they agree
for the whole horizon, the event carries the rule, and every calendar that
reads it recurs forever, correctly. If they disagree anywhere, the event
carries Remind's dates instead: fewer of them, but none of them wrong.
That check is the reason this converter is built on bindings rather than on a parser of its own. Without Remind there is nothing to check against, and every previous converter has had to guess and hope.
Defined Under Namespace
Constant Summary collapse
- DEFAULT_HORIZON =
How many occurrences the guess has to get right. Ten years of a monthly reminder, or two of a weekly one -- far enough out for the leap years, the month lengths and the moving holidays that break a rule to have shown up.
120
Instance Attribute Summary collapse
-
#horizon ⇒ Object
readonly
Returns the value of attribute horizon.
-
#reminder ⇒ Object
readonly
Returns the value of attribute reminder.
Instance Method Summary collapse
- #call ⇒ Object
-
#candidate ⇒ Object
The RRULE this reminder looks like it means, before anyone checks.
-
#initialize(reminder, horizon: DEFAULT_HORIZON) ⇒ Recurrence
constructor
A new instance of Recurrence.
-
#remind_dates ⇒ Object
The dates Remind says the reminder fires on, bounded by the horizon.
-
#single? ⇒ Boolean
A reminder Remind only ever triggers once needs no rule to describe it, whatever its trigger looks like.
Constructor Details
#initialize(reminder, horizon: DEFAULT_HORIZON) ⇒ Recurrence
Returns a new instance of Recurrence.
53 54 55 56 |
# File 'lib/rem2ics/recurrence.rb', line 53 def initialize(reminder, horizon: DEFAULT_HORIZON) @reminder = reminder @horizon = horizon end |
Instance Attribute Details
#horizon ⇒ Object (readonly)
Returns the value of attribute horizon.
51 52 53 |
# File 'lib/rem2ics/recurrence.rb', line 51 def horizon @horizon end |
#reminder ⇒ Object (readonly)
Returns the value of attribute reminder.
51 52 53 |
# File 'lib/rem2ics/recurrence.rb', line 51 def reminder @reminder end |
Instance Method Details
#call ⇒ Object
58 59 60 61 62 63 64 65 66 |
# File 'lib/rem2ics/recurrence.rb', line 58 def call if single? Dates.new(remind_dates) elsif agrees? Rule.new(candidate, remind_dates) else Dates.new(remind_dates) end end |
#candidate ⇒ Object
The RRULE this reminder looks like it means, before anyone checks.
The shape is decided by which parts of the date the trigger left out,
because that is how Remind's trigger language works: REM 15 says
nothing about the month, so it happens every month.
86 87 88 |
# File 'lib/rem2ics/recurrence.rb', line 86 def candidate @candidate ||= bounded(frequency) end |
#remind_dates ⇒ Object
The dates Remind says the reminder fires on, bounded by the horizon.
77 78 79 |
# File 'lib/rem2ics/recurrence.rb', line 77 def remind_dates @remind_dates ||= reminder.occurrences(limit: horizon).to_a end |
#single? ⇒ Boolean
A reminder Remind only ever triggers once needs no rule to describe it,
whatever its trigger looks like. Asking Remind is more reliable than
reading the trigger: REM 25 Dec 2027 and REM 1 Jan 2027 *14 UNTIL 14 Jan 2027 both happen once, and only one of them looks like it.
72 73 74 |
# File 'lib/rem2ics/recurrence.rb', line 72 def single? remind_dates.length <= 1 end |