ice_cube Ruby Library

repository·master·Indexed 25 days ago

https://github.com/ice-cube-ruby/ice_cube

A Ruby library for handling repeated events and complex recurrence patterns modeled after the iCalendar RFC. It allows developers to define schedules using Recurrence Rules, Recurrence Times, and Exception Times. Features include support for daily, weekly, monthly, yearly, hourly, minutely, and secondly rules, as well as serialization to YAML, Hash, and iCal formats. It integrates with ActiveSupport for multi-time zone and DST support.

Tokens
2.4K
Snippets
5
Records
17
Agent score
81%

What's inside ice_cube

  1. How ice_cube schedules work

    master

    ice_cube is a Ruby library for handling repeated events (schedules). It is modeled after the iCalendar RFC and allows you to define complex recurrence patterns.

    A Schedule is composed of three types of components, applied in increasing order of precedence:

    1. Recurrence Rules: Define how to include recurring times.
    2. Recurrence Times: Specific times to include.
    3. Exception Times: Specific times to exclude.

    To limit a schedule, use .count or .until on the recurrence rules. Setting end_time on the Schedule itself sets the duration (from the start time) for each occurrence.

  2. Configure time zones with ActiveSupport

    master

    By default, ice_cube only supports the environment's single "local" time zone (ENV['TZ']) or UTC. To support multiple time zones and handle Daylight Savings Time (DST) correctly, you should require active_support/time.

    Occurrences will be returned in the same class and time zone as the schedule's start_time. Supported start times include:

    • Time.local (default)
    • Time.utc
    • ActiveSupport::TimeWithZone (via Time.zone.now, etc.)
    • DateTime and Date (converted to Time.local)
  3. Migrate from `start_date` to `start_time` for serialized schedules

    master

    In version 0.12.0 and later, the hash key start_date was renamed to start_time for consistency.

    To upgrade your code:

    1. Update your downstream code to look for the start_time key instead of start_date when processing serialized schedules.
    2. By default, ice_cube will export serialized schedules containing both keys to maintain backward compatibility.
    3. Once your code is updated to use start_time, you can disable the duplication of keys by setting IceCube.compatibility = 12.

    Watch your logs for deprecation notices during this transition.

  4. Querying schedule occurrences

    master

    Once a schedule is defined, you can query it to find specific occurrences or check if a time falls within the schedule.

    Common Query Methods

    • occurrences(end_time): List occurrences until end_time (required for non-terminating rules).
    • all_occurrences: Returns all occurrences (only for terminating schedules).
    • occurs_at?(time): Checks if a specific time is part of the schedule.
    • occurs_on?(date): Checks if a specific day is part of the schedule.
    • occurs_between?(start, end): Checks if the schedule occurs within a date range.
    • first(n): Returns the first n occurrences (or just the first if n is omitted).
    • last(n): Returns the last n occurrences (if the schedule terminates).
    • next_occurrence(from_time): Returns the next occurrence after from_time (defaults to Time.now).
    • previous_occurrence(from_time): Returns the previous occurrence before from_time.
    • each_occurrence { |t| ... }: Iterates through occurrences.

    Handling Durations and Spans

    If you provide a duration to the Schedule, you can use occurring_at? and occurring_between? to check if the schedule's interval overlaps with a given time.

    To include prior occurrences that overlap a specific time, use the spans: true option:

    • next_occurrences(n, from_time, spans: true)
    • occurrences_between(from_time, to_time, spans: true)
    require 'ice_cube'
    require 'active_support/time'
    
    schedule = IceCube::Schedule.new(now = Time.now) do |s|
      s.add_recurrence_rule(IceCube::Rule.daily.count(4))
      s.add_exception_time(now + 1.day)
    end
    
    # Examples
    schedule.occurrences(now + 10.days)
    schedule.occurs_at?(now + 2.days)
    schedule.occurs_between?(now, now + 30.days)
    schedule.first(2)
    schedule.next_occurrence(now)
  5. Create recurrence rules (Daily, Weekly, Monthly, Yearly, etc.)

    master

    You can build complex schedules using IceCube::Rule. Below are the primary rule types and their common configurations:

    Daily

    • IceCube::Rule.daily: Every day.
    • IceCube::Rule.daily(n): Every n days.

    Weekly

    • IceCube::Rule.weekly: Every week.
    • IceCube::Rule.weekly(n).day(:monday, :tuesday): Every n weeks on Monday and Tuesday.
    • IceCube::Rule.weekly(1, :monday): Weekly, starting the week on Monday.

    Monthly

    • IceCube::Rule.monthly.day_of_month(1, -1): On the 1st and last day of the month.
    • IceCube::Rule.monthly(2).day_of_month(15): Every other month on the 15th.
    • IceCube::Rule.monthly.day_of_week(tuesday: [1, -1]): On the first and last Tuesday of the month.

    Yearly

    • IceCube::Rule.yearly.day_of_year(100, -100): On the 100th day from the start and end of the year.
    • IceCube::Rule.yearly.month_of_year(:january, :february): Every year in January and February.

    Hourly, Minutely, and Secondly

    • IceCube::Rule.hourly: Every hour.
    • IceCube::Rule.minutely(10): Every 10 minutes.
    • IceCube::Rule.secondly(15): Every 15 seconds.

    BYSETPOS (Select the Nth occurrence)

    Use .by_set_pos(n) to select the $n^{th}$ occurrence within an interval (positive for start, negative for end).

    # Last weekday of the month
    IceCube::Rule.monthly.day(:monday, :tuesday, :wednesday, :thursday, :friday).by_set_pos(-1)
    # Example: Every Friday the 13th in October
    schedule = IceCube::Schedule.new
    schedule.add_recurrence_rule(
      IceCube::Rule.yearly.day_of_month(13).day(:friday).month_of_year(:october)
    )
  6. Convert rules to iCal or String representations

    master

    You can get a human-readable string or a machine-readable iCal string from an individual rule.

    • .to_ical: Returns the iCal string (e.g., 'FREQ=DAILY;INTERVAL=2;...').
    • .to_s: Returns a natural language description (e.g., 'Every 2 days on...').
    rule = IceCube::Rule.daily(2).day_of_week(tuesday: [1, -1], wednesday: [2])
    
    rule.to_ical # 'FREQ=DAILY;INTERVAL=2;BYDAY=1TU,-1TU,2WE'
    rule.to_s   # 'Every 2 days on the last and 1st Tuesdays and the 2nd Wednesday'
  7. Serialize and persist schedules

    master

    ice_cube provides several ways to serialize schedules for storage in a data store:

    • YAML: Uses a built-in hash-based .to_yaml.
    • Hash: Converts the schedule to a standard Ruby Hash.
    • iCal: Converts the schedule to the iCalendar format.

    Note: Parsing datetimes with time zone information via iCal is not currently supported.

    # YAML
    yaml = schedule.to_yaml
    IceCube::Schedule.from_yaml(yaml)
    
    # Hash
    hash = schedule.to_hash
    IceCube::Schedule.from_hash(hash)
    
    # iCal
    ichal = schedule.to_ical
    IceCube::Schedule.from_ical(ichal)
  8. Configure the default time format for Schedule#to_s

    master
    You can globally set the date format used when calling Schedule#to_s. By default, IceCube uses the format defined in its i18n configuration (typically '%B %e, %Y'). Use IceCube.to_s_time_format= to change this globally.
  9. Set compatibility version for exported schedules

    master
    IceCube provides a compatibility setting to ensure backwards compatibility when working with schedules exported from older versions. The versioning logic maps older versions to integers (e.g., version 0.11 is represented as 11, and 1.0 as 100).
  10. Convert a Rule to and from YAML

    master

    Rules can be serialized to YAML format for storage or transmission and reconstructed later. Use to_yaml to generate a YAML string and Rule.from_yaml(yaml_string) to recreate the rule object.

    Note: from_yaml handles the differences in YAML.safe_load between Ruby versions (pre-3.1 and 3.1+) internally.

  11. Create Rules using convenience methods

    master

    You can create specific recurrence rules using class methods on IceCube::Rule. Each method accepts an interval (the frequency of the recurrence) and, in the case of weekly, an optional week_start symbol.

    Supported interval types are:

    • secondly(interval = 1)
    • minutely(interval = 1)
    • hourly(interval = 1)
    • daily(interval = 1)
    • weekly(interval = 1, week_start = :sunday)
    • monthly(interval = 1)
    • yearly(interval = 1)