Module: Protocol::Caldav::Ical::Expand
- Defined in:
- lib/protocol/caldav/ical/expand.rb
Class Method Summary collapse
-
.expand(component, range_start:, range_end:, max_occurrences: 10000) ⇒ Object
Expand a recurring VCALENDAR into individual instances.
Class Method Details
.expand(component, range_start:, range_end:, max_occurrences: 10000) ⇒ Object
Expand a recurring VCALENDAR into individual instances. Returns a serialized VCALENDAR string with RRULE removed and each occurrence as a separate VEVENT with RECURRENCE-ID.
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 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 |
# File 'lib/protocol/caldav/ical/expand.rb', line 17 def (component, range_start:, range_end:, max_occurrences: 10000) return serialize_component(component) unless component.name.casecmp?('VCALENDAR') # Find the base VEVENT with RRULE base = component.find_components('VEVENT').find { |v| v.find_property('RRULE') } return serialize_component(component) unless base # Collect override VEVENTs (same UID, has RECURRENCE-ID) uid = base.find_property('UID')&.value overrides = {} component.find_components('VEVENT').each do |v| rid = v.find_property('RECURRENCE-ID') if rid && v.find_property('UID')&.value == uid rid_time = Filter::Match.send(:parse_datetime_string, rid.value.strip) overrides[rid_time] = v if rid_time end end dtstart_prop = base.find_property('DTSTART') dtstart = Filter::Match.send(:parse_datetime_string, dtstart_prop.value.strip) return serialize_component(component) unless dtstart dtend = Filter::Match.send(:parse_ical_datetime, base, 'DTEND') duration = dtend ? (dtend - dtstart).to_i : 3600 rrule = base.find_property('RRULE').value.strip exdates = base.find_all_properties('EXDATE').filter_map do |ex| Filter::Match.send(:parse_datetime_string, ex.value.strip) end occurrences = Rrule.( dtstart: dtstart, rrule_value: rrule, range_start: range_start, range_end: range_end, exdates: exdates, max_count: max_occurrences ) instances = occurrences.map do |occ_start| override = overrides[occ_start] if override serialize_vevent_instance(override, occ_start) else (base, occ_start, duration) end end # Also include overrides that aren't in the base RRULE expansion overrides.each do |rid_time, override_vevent| next if occurrences.any? { |o| Rrule.send(:times_equal?, o, rid_time) } odt = Filter::Match.send(:parse_ical_datetime, override_vevent, 'DTSTART') next unless odt && odt >= range_start && odt < range_end instances << serialize_vevent_instance(override_vevent, rid_time) end "BEGIN:VCALENDAR\r\n#{instances.join}END:VCALENDAR\r\n" end |