I am searching for the best way to model recurring events. I am using fullcalendar to display events. But I guess recurring events are best handled on the rails backend.
I already looked at other questions and existing example code but I didn’t find anything which fits.
It should behave similar like google calendar. So it should be possible to delete/modify single events of the recurring event series. But saving all events of the event series in the database seems inefficient. Also it should be possible to create single events without any recurrence.
What would be a good model architecture?
My event model right now looks like that (without additional attributes):
# Table name: events
#
# id :integer not null, primary key
# employee_id :integer
# created_at :datetime
# updated_at :datetime
# starts_at :datetime
# ends_at :datetime
#
class Event < ActiveRecord::Base
attr_accessible :starts_at, :ends_at
end
Here is how I would model this. I haven’t used Google Calendar much, so I’m basing the functionality off of iCal‘s recurring events.
All models should have the usual id, created_at, updated_at properties. Listed are the custom properties. If the property is another model, you will implement it an association such as
has_oneorbelongs_to.RecurrencePeriodEventbase_event #has_one :base_event, :class_name'Event'Timeend_date # may be nil, if it recurs foreverWeeklyRecurrencerecurrence #has_one :recurrence, :as=>:recurrenceArray[OccurrenceOverride]overrides #has_many :overrides, :class_name=>'OccurrenceOverride'The
RecurrencePeriodstarts on the date that its base_event starts. Also, I assume that anEvent‘s employee_id refers to the employee that created that event. ARecurrencePeriodwill also belong to the employee that created the base_event.The model depends on how flexibly you want to be able to specify recurrences. Are you going to support "Tuesday and Thursday every two weeks from 10 AM to 11 AM and from 2 PM to 3 PM" or just "repeats weekly"? Here’s a model that supports just "repeats weekly", "repeats every two weeks", etc.; you can expand it if you need to.
WeeklyRecurrenceIntegerweeks_between_recurrencesRecurrencePeriodrecurrence_period #belongs_to :recurrence, :polymorphic=>trueI use polymorphic associations here, because I think they might be useful if you want more than one type of recurrence, such both
WeeklyRecurrenceandDailyRecurrence. But I’m not sure that they’re the correct way to model that, so if they turn out not to be, just usehas_one :weekly_recurrenceandbelongs_to :recurrence_periodinstead.The Ice cube library seems like it might be useful for calculating recurrences. If
WeeklyRecurrenceabove isn’t powerful enough, you might just want to store an Ice cubeScheduleobject in a model, replacingWeeklyRecurrence. To store aScheduleobject in a model, save it as an attribute "schedule", putserialize :schedulein the model definition, and generate a text column "schedule" in the database.OccurrenceOverridehandles the case of a single instance of a recurring event being edited.OccurrenceOverrideRecurrencePeriodrecurrence_period_to_override #belongs_to :recurrence_period_to_override, :class_name=>'RecurrencePeriod'Timeoriginal_start_time # uniquely identifies which recurrence within that RecurrencePeriod to replaceEventreplacement_event #has_one :replacement_event, :class_name=>'Event'; may be nil, if that recurrence was deleted instead of editedInstead of storing each occurrence of an event individually, generate them temporarily when you need to show them in the view. In
RecurrencePeriod, create a methodgenerate_events_in_range(start_date, end_date)that generatesEvents, not to save in the database, but just to pass to the view so it can show them.When a user edits a recurrence, they should have the option to modify all occurrences, all future occurrences, or just that event. If they modify all occurrences, modify the
RecurrencePeriod‘s base_event. If they modify all future occurrences, use a method you should implement onRecurrencePeriodthat splits itself into twoRecurrencePeriods on either side of a certain date, and then save the changes to just the second period. If they modify only that event, create anOccurrenceOverridefor the time that they are overriding, and save the changes to the override’s replacement_event.When a user says a certain event should now recur every two weeks for the foreseeable future, you should create a new
RecurrencePeriodwith that event as base_event and a nil end_date. Its recurrence should be a newWeeklyRecurrencewith weeks_between_recurrence=2, and it should have noOccurrenceOverrides.