Class: Reprise::Schedule

Inherits:
Object
  • Object
show all
Defined in:
lib/reprise/schedule.rb

Overview

The Reprise::Schedule class is the primary interface of the Reprise gem.

It offers methods that enable you to:

  • Initialize a new schedule.

  • Add recurring series to the schedule via #repeat_* methods.

  • Mark specific intervals of time as excluded from the schedule via #add_exclusion and #add_exclusions.

  • Query for the presence of occurrences within intervals of time via #occurs_between?.

  • Generate an array of all of the schedule’s occurrences via #occurrences.

Instance Method Summary collapse

Constructor Details

#initialize(starts_at:, ends_at:, time_zone: nil) ⇒ Schedule

All schedules must be constructed with a valid starts_at and ends_at time. Reprise does not support infinitely-recurring schedules, or the bounding of schedules on the basis of a maximum occurrence count.

Parameters:

  • starts_at (Time, ActiveSupport::TimeWithZone)

    The beginning of the schedule; the earliest possible moment for a valid occurrence. If no time_zone is given, the schedule’s time zone will be inferred from starts_at, defaulting to UTC if it lacks time zone information (e.g. it is a plain Time).

  • ends_at (Time, ActiveSupport::TimeWithZone)

    The end of the schedule; the latest possible moment for a valid occurrence.

  • time_zone (String) (defaults to: nil)

    Must be an unambiguous, valid Rails time zone string or IANA time-zone identifier according to ActiveSupport::TimeZone::find_tzinfo. See github.com/tzinfo/tzinfo/issues/53

Raises:



38
39
40
41
42
43
44
45
# File 'lib/reprise/schedule.rb', line 38

def initialize(starts_at:, ends_at:, time_zone: nil)
  raise InvalidRangeError, "The end time cannot precede the start time" if ends_at < starts_at

  @starts_at = starts_at
  @ends_at = ends_at
  @time_zone = TimeZoneIdentifier.new(time_zone:, datetime_source: starts_at).to_s
  @default_time_of_day = TimeOfDay.new(starts_at)
end

Instance Method Details

#add_exclusion(starts_at:, ends_at:) ⇒ Object

Add a time interval between which no occurrences are valid. Any occurrences that overlap with an exclusion are removed from the schedule’s occurrences.

Parameters:

  • starts_at (Time)

    The time that the exclusion starts at

  • ends_at (Time)

    The time that the exclusion ends at



254
255
256
257
258
259
# File 'lib/reprise/schedule.rb', line 254

def add_exclusion(starts_at:, ends_at:)
  internal_schedule.add_exclusion(
    starts_at_unix_timestamp: starts_at.to_i,
    ends_at_unix_timestamp: ends_at.to_i
  )
end

#add_exclusions(exclusions) ⇒ void

This method returns an undefined value.

Add time intervals between which no occurrences are valid. Any occurrences that overlap with an exclusion are removed from the schedule’s occurrences.

Examples:

schedule.add_exclusions([
  [exclusion_1_starts_at, exclusion_1_ends_at],
  [exclusion_2_starts_at, exclusion_2_ends_at],
])

Parameters:

  • exclusions (Array<Array<Time,Time>>)

    An array of exclusion arrays, consisting of start and end Time values.



271
272
273
274
275
# File 'lib/reprise/schedule.rb', line 271

def add_exclusions(exclusions)
  internal_schedule.add_exclusions(
    exclusions.map {|e| e.map(&:to_i) }
  )
end

#occurrencesArray<Reprise::Core::Occurrence>

Returns an array of occurrences sorted in order of ascending occurrence start time. This method is not cached; on every call, it will recompute all of the schedule’s occurrences.

Returns:



50
51
52
# File 'lib/reprise/schedule.rb', line 50

def occurrences
  internal_schedule.occurrences
end

#occurrences_between(starts_at, ends_at, include_overlapping: false) ⇒ Array<Reprise::Core::Occurrence>

This method efficiently queries your schedule for occurrences that fall within a given interval.

Parameters:

  • starts_at (Time)

    The start of the interval to query

  • ends_at (Time)

    The end of the interval to query

  • include_overlapping (Boolean) (defaults to: false)

    when true, the query will also consider occurrences that partially overlap with the given interval, not just the occurrences that are entirely contained within the interval.

Returns:

  • (Array<Reprise::Core::Occurrence>)

    an array of occurrences that occur between the given starts_at and ends_at bookends.



297
298
299
300
301
302
303
# File 'lib/reprise/schedule.rb', line 297

def occurrences_between(starts_at, ends_at, include_overlapping: false)
  if include_overlapping
    internal_schedule.occurrences_overlapping_with_interval(starts_at.to_i, ends_at.to_i)
  else
    internal_schedule.occurrences_contained_within_interval(starts_at.to_i, ends_at.to_i)
  end
end

#occurs_between?(starts_at, ends_at, include_overlapping: false) ⇒ Boolean

Indicates whether one or more of your schedule’s occurrences fall within the given interval.

Parameters:

  • starts_at (Time)

    The start of the interval to query

  • ends_at (Time)

    The end of the interval to query

  • include_overlapping (Boolean) (defaults to: false)

    when true, the query will also consider occurrences that partially overlap with the given interval, not just the occurrences that are entirely contained within the interval.

Returns:

  • (Boolean)


287
288
289
# File 'lib/reprise/schedule.rb', line 287

def occurs_between?(starts_at, ends_at, include_overlapping: false)
  occurrences_between(starts_at, ends_at, include_overlapping:).any?
end

#repeat_annually_by_day(day_number, time_of_day:, duration_in_seconds:, interval: 1, starts_at: nil, ends_at: nil, count: nil, label: nil) ⇒ void

This method returns an undefined value.

Examples:

schedule.repeat_annually_by_day(200, duration_in_seconds: 30)

Parameters:

  • day_number (Integer)

    The number of the day in the year; >= 1 && <= 366

  • time_of_day (Hash, Time, nil)

    Either a local time value from which the hour, minute, and second should be derived, or a hash containing at least one of hour, minute, or second. If nil, the time of day will be inferred from the schedule’s starts_at value.

  • duration_in_seconds (Integer)

    This determines the end time of each occurrence (Core::Occurrence#ends_at), and also influences occurrence queries, and whether any added exclusions conflict with any of the schedule’s occurrences.

  • interval (Integer) (defaults to: 1)

    This determines whether or not occurrences should be skipped. A value of 1 means that every occurrence for the series should be returned; 2, every other occurrence should be returned, etc.

  • starts_at (Time, nil) (defaults to: nil)

    The time that the series should begin. If left blank, the series will start at the same time as the parent schedule.

  • ends_at (Time, nil) (defaults to: nil)

    The time that the series should end. If left blank, the series will end at the same time as the parent schedule.

  • count (Integer, nil) (defaults to: nil)

    An optional count limit to apply to the occurrences of a series; once the schedule has generated the requested number of occurrences, it will halt further expansion of that specific series. The count takes precedence over the optional ends_at param.

  • label (String, nil) (defaults to: nil)

    An optional label to apply to all of the occurrences that are generated from the series. See Core::Occurrence#label.

Options Hash (time_of_day:):

  • :hour, (Integer)

    >= 0 && <= 23

  • :minute, (Integer)

    >= 0 && <= 59

  • :second, (Integer)

    >= 0 && <= 59

Raises:

  • (UnsupportedTypeError)

    if time_of_day is neither a Hash nor a Time.

  • (InvalidHashError)

    if the hash representation of the time is invalid.

  • (RangeError)

    if either the hour, minute, or second is out-of-range.



237
238
239
240
241
242
243
244
245
246
247
248
# File 'lib/reprise/schedule.rb', line 237

def repeat_annually_by_day(day_number, time_of_day:, duration_in_seconds:, interval: 1, starts_at: nil, ends_at: nil, count: nil, label: nil)
  internal_schedule.repeat_annually_by_day(
    day_number,
    time_of_day: TimeOfDay.new(time_of_day || self.starts_at).to_h,
    duration_in_seconds:,
    interval:,
    starts_at_unix_timestamp: starts_at.presence&.to_i,
    ends_at_unix_timestamp: ends_at.presence&.to_i,
    count:,
    label:
  )
end

#repeat_daily(time_of_day: nil, duration_in_seconds:, interval: 1, starts_at: nil, ends_at: nil, count: nil, label: nil) ⇒ void

This method returns an undefined value.

Parameters:

  • time_of_day (Hash, Time, nil) (defaults to: nil)

    Either a local time value from which the hour, minute, and second should be derived, or a hash containing at least one of hour, minute, or second. If nil, the time of day will be inferred from the schedule’s starts_at value.

  • duration_in_seconds (Integer)

    This determines the end time of each occurrence (Core::Occurrence#ends_at), and also influences occurrence queries, and whether any added exclusions conflict with any of the schedule’s occurrences.

  • interval (Integer) (defaults to: 1)

    This determines whether or not occurrences should be skipped. A value of 1 means that every occurrence for the series should be returned; 2, every other occurrence should be returned, etc.

  • starts_at (Time, nil) (defaults to: nil)

    The time that the series should begin. If left blank, the series will start at the same time as the parent schedule.

  • ends_at (Time, nil) (defaults to: nil)

    The time that the series should end. If left blank, the series will end at the same time as the parent schedule.

  • count (Integer, nil) (defaults to: nil)

    An optional count limit to apply to the occurrences of a series; once the schedule has generated the requested number of occurrences, it will halt further expansion of that specific series. The count takes precedence over the optional ends_at param.

  • label (String, nil) (defaults to: nil)

    An optional label to apply to all of the occurrences that are generated from the series. See Core::Occurrence#label.

Options Hash (time_of_day:):

  • :hour, (Integer)

    >= 0 && <= 23

  • :minute, (Integer)

    >= 0 && <= 59

  • :second, (Integer)

    >= 0 && <= 59

Raises:

  • (UnsupportedTypeError)

    if time_of_day is neither a Hash nor a Time.

  • (InvalidHashError)

    if the hash representation of the time is invalid.

  • (RangeError)

    if either the hour, minute, or second is out-of-range.



143
144
145
146
147
148
149
150
151
152
153
# File 'lib/reprise/schedule.rb', line 143

def repeat_daily(time_of_day: nil, duration_in_seconds:, interval: 1, starts_at: nil, ends_at: nil, count: nil, label: nil)
  internal_schedule.repeat_daily(
    time_of_day: TimeOfDay.new(time_of_day || self.starts_at).to_h,
    duration_in_seconds:,
    interval:,
    starts_at_unix_timestamp: starts_at.presence&.to_i,
    ends_at_unix_timestamp: ends_at.presence&.to_i,
    count:,
    label:
  )
end

#repeat_hourly(time_of_day: nil, duration_in_seconds:, interval: 1, starts_at: nil, ends_at: nil, count: nil, label: nil) ⇒ void

This method returns an undefined value.

Parameters:

  • time_of_day (Hash, Time, nil) (defaults to: nil)

    Either a local time value from which the hour, minute, and second should be derived, or a hash containing at least one of hour, minute, or second. If nil, the time of day will be inferred from the schedule’s starts_at value.

  • duration_in_seconds (Integer)

    This determines the end time of each occurrence (Core::Occurrence#ends_at), and also influences occurrence queries, and whether any added exclusions conflict with any of the schedule’s occurrences.

  • interval (Integer) (defaults to: 1)

    This determines whether or not occurrences should be skipped. A value of 1 means that every occurrence for the series should be returned; 2, every other occurrence should be returned, etc.

  • starts_at (Time, nil) (defaults to: nil)

    The time that the series should begin. If left blank, the series will start at the same time as the parent schedule.

  • ends_at (Time, nil) (defaults to: nil)

    The time that the series should end. If left blank, the series will end at the same time as the parent schedule.

  • count (Integer, nil) (defaults to: nil)

    An optional count limit to apply to the occurrences of a series; once the schedule has generated the requested number of occurrences, it will halt further expansion of that specific series. The count takes precedence over the optional ends_at param.

  • label (String, nil) (defaults to: nil)

    An optional label to apply to all of the occurrences that are generated from the series. See Core::Occurrence#label.

Options Hash (time_of_day:):

  • :hour, (Integer)

    >= 0 && <= 23

  • :minute, (Integer)

    >= 0 && <= 59

  • :second, (Integer)

    >= 0 && <= 59

Raises:

  • (UnsupportedTypeError)

    if time_of_day is neither a Hash nor a Time.

  • (InvalidHashError)

    if the hash representation of the time is invalid.

  • (RangeError)

    if either the hour, minute, or second is out-of-range.



124
125
126
127
128
129
130
131
132
133
134
# File 'lib/reprise/schedule.rb', line 124

def repeat_hourly(time_of_day: nil, duration_in_seconds:, interval: 1, starts_at: nil, ends_at: nil, count: nil, label: nil)
  internal_schedule.repeat_hourly(
    time_of_day: TimeOfDay.new(time_of_day || self.starts_at).to_h,
    duration_in_seconds:,
    interval:,
    starts_at_unix_timestamp: starts_at.presence&.to_i,
    ends_at_unix_timestamp: ends_at.presence&.to_i,
    count:,
    label:
  )
end

#repeat_minutely(time_of_day: nil, duration_in_seconds:, interval: 1, starts_at: nil, ends_at: nil, count: nil, label: nil) ⇒ void

This method returns an undefined value.

Parameters:

  • time_of_day (Hash, Time, nil) (defaults to: nil)

    Either a local time value from which the hour, minute, and second should be derived, or a hash containing at least one of hour, minute, or second. If nil, the time of day will be inferred from the schedule’s starts_at value.

  • duration_in_seconds (Integer)

    This determines the end time of each occurrence (Core::Occurrence#ends_at), and also influences occurrence queries, and whether any added exclusions conflict with any of the schedule’s occurrences.

  • interval (Integer) (defaults to: 1)

    This determines whether or not occurrences should be skipped. A value of 1 means that every occurrence for the series should be returned; 2, every other occurrence should be returned, etc.

  • starts_at (Time, nil) (defaults to: nil)

    The time that the series should begin. If left blank, the series will start at the same time as the parent schedule.

  • ends_at (Time, nil) (defaults to: nil)

    The time that the series should end. If left blank, the series will end at the same time as the parent schedule.

  • count (Integer, nil) (defaults to: nil)

    An optional count limit to apply to the occurrences of a series; once the schedule has generated the requested number of occurrences, it will halt further expansion of that specific series. The count takes precedence over the optional ends_at param.

  • label (String, nil) (defaults to: nil)

    An optional label to apply to all of the occurrences that are generated from the series. See Core::Occurrence#label.

Options Hash (time_of_day:):

  • :hour, (Integer)

    >= 0 && <= 23

  • :minute, (Integer)

    >= 0 && <= 59

  • :second, (Integer)

    >= 0 && <= 59

Raises:

  • (UnsupportedTypeError)

    if time_of_day is neither a Hash nor a Time.

  • (InvalidHashError)

    if the hash representation of the time is invalid.

  • (RangeError)

    if either the hour, minute, or second is out-of-range.



105
106
107
108
109
110
111
112
113
114
115
# File 'lib/reprise/schedule.rb', line 105

def repeat_minutely(time_of_day: nil, duration_in_seconds:, interval: 1, starts_at: nil, ends_at: nil, count: nil, label: nil)
  internal_schedule.repeat_minutely(
    time_of_day: TimeOfDay.new(time_of_day || self.starts_at).to_h,
    duration_in_seconds:,
    interval:,
    starts_at_unix_timestamp: starts_at.presence&.to_i,
    ends_at_unix_timestamp: ends_at.presence&.to_i,
    count:,
    label:
  )
end

#repeat_monthly_by_day(day_number, time_of_day:, duration_in_seconds:, interval: 1, starts_at: nil, ends_at: nil, count: nil, label: nil) ⇒ void

This method returns an undefined value.

Examples:

schedule.repeat_monthly_by_day(15, time_of_day: { hour: 9 }, duration_in_seconds: 30)

Parameters:

  • day_number (Integer)

    The number of the day in the month; >= 1 && <= 31

  • time_of_day (Hash, Time, nil)

    Either a local time value from which the hour, minute, and second should be derived, or a hash containing at least one of hour, minute, or second. If nil, the time of day will be inferred from the schedule’s starts_at value.

  • duration_in_seconds (Integer)

    This determines the end time of each occurrence (Core::Occurrence#ends_at), and also influences occurrence queries, and whether any added exclusions conflict with any of the schedule’s occurrences.

  • interval (Integer) (defaults to: 1)

    This determines whether or not occurrences should be skipped. A value of 1 means that every occurrence for the series should be returned; 2, every other occurrence should be returned, etc.

  • starts_at (Time, nil) (defaults to: nil)

    The time that the series should begin. If left blank, the series will start at the same time as the parent schedule.

  • ends_at (Time, nil) (defaults to: nil)

    The time that the series should end. If left blank, the series will end at the same time as the parent schedule.

  • count (Integer, nil) (defaults to: nil)

    An optional count limit to apply to the occurrences of a series; once the schedule has generated the requested number of occurrences, it will halt further expansion of that specific series. The count takes precedence over the optional ends_at param.

  • label (String, nil) (defaults to: nil)

    An optional label to apply to all of the occurrences that are generated from the series. See Core::Occurrence#label.

Options Hash (time_of_day:):

  • :hour, (Integer)

    >= 0 && <= 23

  • :minute, (Integer)

    >= 0 && <= 59

  • :second, (Integer)

    >= 0 && <= 59

Raises:

  • (UnsupportedTypeError)

    if time_of_day is neither a Hash nor a Time.

  • (InvalidHashError)

    if the hash representation of the time is invalid.

  • (RangeError)

    if either the hour, minute, or second is out-of-range.



191
192
193
194
195
196
197
198
199
200
201
202
# File 'lib/reprise/schedule.rb', line 191

def repeat_monthly_by_day(day_number, time_of_day:, duration_in_seconds:, interval: 1, starts_at: nil, ends_at: nil, count: nil, label: nil)
  internal_schedule.repeat_monthly_by_day(
    day_number,
    time_of_day: TimeOfDay.new(time_of_day || self.starts_at).to_h,
    duration_in_seconds:,
    interval:,
    starts_at_unix_timestamp: starts_at.presence&.to_i,
    ends_at_unix_timestamp: ends_at.presence&.to_i,
    count:,
    label:
  )
end

#repeat_monthly_by_nth_weekday(weekday, nth_day, time_of_day:, duration_in_seconds:, interval: 1, starts_at: nil, ends_at: nil, count: nil, label: nil) ⇒ void

This method returns an undefined value.

Parameters:

  • weekday (Symbol)

    Accepts :monday, :tuesday, :wednesday, :thursday, or :friday.

  • nth_day (Integer)

    The nth weekday, 0-indexed; e.g. 0 might represent the first wednesday

  • time_of_day (Hash, Time, nil)

    Either a local time value from which the hour, minute, and second should be derived, or a hash containing at least one of hour, minute, or second. If nil, the time of day will be inferred from the schedule’s starts_at value.

  • duration_in_seconds (Integer)

    This determines the end time of each occurrence (Core::Occurrence#ends_at), and also influences occurrence queries, and whether any added exclusions conflict with any of the schedule’s occurrences.

  • interval (Integer) (defaults to: 1)

    This determines whether or not occurrences should be skipped. A value of 1 means that every occurrence for the series should be returned; 2, every other occurrence should be returned, etc.

  • starts_at (Time, nil) (defaults to: nil)

    The time that the series should begin. If left blank, the series will start at the same time as the parent schedule.

  • ends_at (Time, nil) (defaults to: nil)

    The time that the series should end. If left blank, the series will end at the same time as the parent schedule.

  • count (Integer, nil) (defaults to: nil)

    An optional count limit to apply to the occurrences of a series; once the schedule has generated the requested number of occurrences, it will halt further expansion of that specific series. The count takes precedence over the optional ends_at param.

  • label (String, nil) (defaults to: nil)

    An optional label to apply to all of the occurrences that are generated from the series. See Core::Occurrence#label.

Options Hash (time_of_day:):

  • :hour, (Integer)

    >= 0 && <= 23

  • :minute, (Integer)

    >= 0 && <= 59

  • :second, (Integer)

    >= 0 && <= 59

Raises:

  • (UnsupportedTypeError)

    if time_of_day is neither a Hash nor a Time.

  • (InvalidHashError)

    if the hash representation of the time is invalid.

  • (RangeError)

    if either the hour, minute, or second is out-of-range.



213
214
215
216
217
218
219
220
221
222
223
224
225
# File 'lib/reprise/schedule.rb', line 213

def repeat_monthly_by_nth_weekday(weekday, nth_day, time_of_day:, duration_in_seconds:, interval: 1, starts_at: nil, ends_at: nil, count: nil, label: nil)
  internal_schedule.repeat_monthly_by_nth_weekday(
    weekday,
    nth_day,
    time_of_day: TimeOfDay.new(time_of_day || self.starts_at).to_h,
    duration_in_seconds:,
    interval:,
    starts_at_unix_timestamp: starts_at.presence&.to_i,
    ends_at_unix_timestamp: ends_at.presence&.to_i,
    count:,
    label:
  )
end

#repeat_weekly(weekday, time_of_day: nil, duration_in_seconds:, interval: 1, starts_at: nil, ends_at: nil, count: nil, label: nil) ⇒ void

This method returns an undefined value.

Examples:

with a time_of_day hash

schedule.repeat_weekly(:monday, time_of_day: { hour: 6 }, duration_in_seconds: 30)

with a local time for time_of_day

local_time = Time.current.in_time_zone(my_current_time_zone)
schedule.repeat_weekly(:monday, time_of_day: local_time, duration_in_seconds: 30)

Parameters:

  • weekday (Symbol)

    Accepts :monday, :tuesday, :wednesday, :thursday, or :friday.

  • time_of_day (Hash, Time, nil) (defaults to: nil)

    Either a local time value from which the hour, minute, and second should be derived, or a hash containing at least one of hour, minute, or second. If nil, the time of day will be inferred from the schedule’s starts_at value.

  • duration_in_seconds (Integer)

    This determines the end time of each occurrence (Core::Occurrence#ends_at), and also influences occurrence queries, and whether any added exclusions conflict with any of the schedule’s occurrences.

  • interval (Integer) (defaults to: 1)

    This determines whether or not occurrences should be skipped. A value of 1 means that every occurrence for the series should be returned; 2, every other occurrence should be returned, etc.

  • starts_at (Time, nil) (defaults to: nil)

    The time that the series should begin. If left blank, the series will start at the same time as the parent schedule.

  • ends_at (Time, nil) (defaults to: nil)

    The time that the series should end. If left blank, the series will end at the same time as the parent schedule.

  • count (Integer, nil) (defaults to: nil)

    An optional count limit to apply to the occurrences of a series; once the schedule has generated the requested number of occurrences, it will halt further expansion of that specific series. The count takes precedence over the optional ends_at param.

  • label (String, nil) (defaults to: nil)

    An optional label to apply to all of the occurrences that are generated from the series. See Core::Occurrence#label.

Options Hash (time_of_day:):

  • :hour, (Integer)

    >= 0 && <= 23

  • :minute, (Integer)

    >= 0 && <= 59

  • :second, (Integer)

    >= 0 && <= 59

Raises:

  • (UnsupportedTypeError)

    if time_of_day is neither a Hash nor a Time.

  • (InvalidHashError)

    if the hash representation of the time is invalid.

  • (RangeError)

    if either the hour, minute, or second is out-of-range.



168
169
170
171
172
173
174
175
176
177
178
179
# File 'lib/reprise/schedule.rb', line 168

def repeat_weekly(weekday, time_of_day: nil, duration_in_seconds:, interval: 1, starts_at: nil, ends_at: nil, count: nil, label: nil)
  internal_schedule.repeat_weekly(
    weekday,
    time_of_day: TimeOfDay.new(time_of_day || self.starts_at).to_h,
    duration_in_seconds:,
    interval:,
    starts_at_unix_timestamp: starts_at.presence&.to_i,
    ends_at_unix_timestamp: ends_at.presence&.to_i,
    count:,
    label:
  )
end