Ruby gem to handle recurring calendar events [closed]
Asked Answered
G

4

14

I want to find a Ruby gem that can work with recurring events that satisfies the following requirements:

  • can process patterns like "Weekly on Tuesday and Wednesday", or "Monthly on the last Tuesday"
  • can compute the next occurrence
  • can serialize/deserialize a pattern into a string to store in the database
  • serialization has a stable format (i.e. it will be able to load even after upgrading)
  • work at least with following pattern components: Time, Day of Week, Date of Month, nth day of week in month;
  • can repeat daily, weekly, monthly or with interval of n days, weeks or months
  • can represent a pattern in natural English language
  • can parse a pattern from English (optional)
  • can export to some popular format like iCal (optional)
  • can integrate with other gems/systems for calendar and task management (optional)
  • support of Active Record - parameter parsing, validation (optional)
  • has enough tests, and few bugs, more then 1 user :)
  • has reasonable performance

I found two relevant candidates:

  • Tickle - this can parse English temporal expressions
  • Ice_Cube(+Schedule-Attributes) - this is the most popular and can export to iCal

Could you suggest a gem (or set of gems) and describe how well it (or they) meet the criteria listed?

(And if I've missed some important criteria, please mention them in your answer.)

Galer answered 14/3, 2011 at 16:13 Comment(3)
github.com/rubyredrick/ri_cal and github.com/mojombo/chronicCetane
@Greg, could you give more details? Your impression and with criteria are met/unmet. ThanksGaler
chronic is pretty good, you won't find anything better for Ruby. I've used it without any problems. No direct experience with ri_cal but I am going to use that gem in my next project.Cetane
G
9

I end up using Ice_Cube for the following reasons:

  • Most popular
  • can compute next occurence
  • can serialize/deserialize a pattern into string to store in database
  • serialization has stable format (e.i. it will be able to load even after upgrading)
  • work at least with following pattern components: Time, Day Of Week, Date of Month, nth day of week in month;
  • can repeat daily, weekly, monthly or with interval of n days, weeks or months
  • can parse a pattern from English (optional)
  • can export to some popular format like iCal (optional)

These on my criteria are not fullfilled by it:

  • can represent a pattern in natural English language
  • support of Active Record - parameter parsing, validation (optional)

This one is not verified:

  • has reasonable performance

Creating Ice_Cube::Schedule from user input in Rails is not very convinient, but doable:

class EntryForm < FormModel

  include IceCube
  class_eval &ValidatesTimelinessSupport[{:start_date => :datetime}]

  Units = [Day = 'day', Week = 'week']
  Intervals = %w[0 1 2 3 4 5 6 7 8 9]
  Week_Days = [:sunday, :monday, :tuesday, :wednesday, :thursday, :friday, :saturday]

  Days_With_Letters = Week_Days.zip(%w[S M T W T F S])

  attr_accessible_accessors :interval, :unit, :start_date
  attr_accessible_accessors *Week_Days

  def_delegators :@model, :display_title, :schedule_yaml, :schedule_yaml=

  validates_date :start_date, :allow_blank => true
  validates_inclusion_of :unit, :in => Units
  validates_inclusion_of :interval, :in => Intervals
  validates_inclusion_of :complete, :in => %w[0 1], :allow_blank => true
  Week_Days.each { |day| validates_inclusion_of day, :in => %w[0 1], :allow_blank => true }

  before_edit {
    if not schedule_yaml.blank? and hash = YAML::load(schedule_yaml)
      schedule = Schedule.from_hash(hash)
    end

    if schedule and rule = schedule.rrules.first
      @start_date = schedule.start_date

      rule_hash = rule.to_hash
      @interval = rule_hash[:interval]

      case rule
      when DailyRule
        @unit = Day
      when WeeklyRule
        @unit = Week
        rule_hash[:validations][:day].try :each do |day_index|
          send "#{Week_Days[day_index]}=", 1
        end
      end

    else
      @start_date = Date.today
      @interval = 1
      @unit = Day
    end
  }

  before_save {
      sd = @start_date.blank? ?
          Date.today.to_all_day :
          @start_date.parse_date_in_timezone
      i = @interval.to_i
      schedule = Schedule.new(sd)


      rule = case @unit
        when Day
          Rule.daily i
        when Week
          Rule.weekly(i).day(
            *Week_Days.
            select { |day| send(day).to_i == 1 } )
      end

      schedule.add_recurrence_rule(rule)

      self.schedule_yaml = schedule.to_yaml
    end
  }
end
Galer answered 19/4, 2011 at 19:39 Comment(0)
O
8

As lovely as Ice_Cube is, it doesn't seem suitable for a large scale scheduling applications where you might have 100,000 + Events that you need to filter through to see what appears on a current day. Think meetup.com

Because it's all serialized into a rule string the only way to filter a major list seems to be something like this:

def self.entries_on(date)
    entries = TimetableEntry.all
    entries.reject{|te|!te.schedule.occurs_on?(date)}
end

Not very efficient. Someone PLEASE CORRECT ME!! hopefully I'm missing a trick?

Also try the above method with a Date that's 100 years in the future... it appears as though ice_cube slows down the further you query away from the current date.

Outrange answered 9/8, 2011 at 9:36 Comment(4)
I opened an issue (with a benchmark) for a performance issue here, in case someone follows along: github.com/seejohnrun/ice_cube/issues/33Blowzy
A little late to the party, but here goes. You shouldn't be retrieving everything and then calling this on Ruby. I would cache the occurrences in the database itself (postgres has support for arrays, so perhaps this may come in handy). As such, leave it up to the database to do any filtering you might need. Might be a better strategy than this.Thong
So does anyone know of a gem which will handle this better, or a better way to deal with this? Ie a gem which makes it easier to handle large schedules where the checks for whether something occurs on a date, etc are figured out on the database level rather than having to deserialize every row and check in ruby.Intelligent
I have not seen a gem or other tech to quickly produce "all the events occuring this week" given formulas such as those in Ice Cube. Generally, some number of occurrences are cached and more are generated as one peruses the calendar.Fibroblast
S
3

I'm not aware of a single plugin that handles all of your requirements, but the combination of rufus-scheduler and chronic should get you pretty far.

Rufus scheduler handles scheduling using a cron-like format that also accommodates local timezones. Chronic does the natural language parsing. Putting them together should solve most of your needs.

The Rufus documentation has some notes on pairing the two solutions.

Stutz answered 15/3, 2011 at 16:10 Comment(0)
D
2

I recommend taking a look RubyToolbox's Recurring Events gems. It should be up to date and includes ice_cube, mentioned previously.

Dwain answered 27/3, 2012 at 3:51 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.