We have been working in a project which deals with date events and we needed a recurrence feature in the application. An initial implementation could simply work with Rails ActiveSupport and use its Date helper methods, in order to shift by day, week, month and others.

Very easy, right? But what if we now want events that occur every monday or sunday? Or events that happen at each two weeks? For each new case, we will need to add and deal with more and more logic.

Instead we have been using the Recurrence gem, created by Nando Vieira with some contributions by José Valim (yeah, our good fellow ;)). Recurrence works with ActiveSupport as its base for date calculation and provides a simple DSL to work with recurring events. Let me show a simple example:

If no :until is determined, it runs until year 2037 (maybe the end of world? No, it’s when the unix time 32-bit overflow…). You can check the Github repository and this blog post (in brazilian portuguese) to learn different use cases and examples for Recurrence.

Now I want to show you how we integrated it with our application. We have built an EventRecurrence class which deals internally with the recurrence logic.

Notice we need to setup the options hash for different cases (day, week, month and year) because the configuration options changes for each. Now, accessing all the recurring dates for one model is quite easy:

Since EventRecurrence is a model, these options are saved in database and we can update or use them for querying freely. For instance, if we want to look for all dates in a given period, we just search for all EventRecurrences that are in the range and collect the dates returned by recurrence. Here you can see a raw implementation of this idea:

class EventRecurrence < ActiveRecord::Base
# Retrieves a list of all dates for a period
def self.dates_between(start_date, end_date)
# Filtering EventRecurrence on the period using a between named scope
recurrences = EventRecurrence.between(start_date, end_date)
recurrences.inject([]) do |dates, recurrence|
# Use the given dates instead of the ones in the DB
dates.concat(recurrence.dates(:starts => start_date, :until => end_date))
end
end
end

This is just an initial post showing some ideas to get you going with recurring events. If you are interested, there is a lot of information related to this topic, like Recurring Event in Calendars, an article by Martin Fowler, and the iCal RFC2445 which includes calendar specifications.

And you, have some different solutions about recurring events to share with us?

Cool! Personally, I like to use the whenever gem, which provides a nice DSL for defining cron jobs. This can also be combined with delayed_job or resque, so you might have a cron job that creates a bunch of background tasks. I’ve used this technique for sending out weekly emails, etc.

Cool! Personally, I like to use the whenever gem, which provides a nice DSL for defining cron jobs. This can also be combined with delayed_job or resque, so you might have a cron job that creates a bunch of background tasks. I’ve used this technique for sending out weekly emails, etc.