Webcal Links in Rails with the icalendar Gem

The icalendar gem is a useful way to generate iCalendar files for use with various calendar applications. One thing I couldn't find good documentation on, though, is how to use it to create a Webcal link in a Ruby on Rails app, so I figured I'd would go ahead and document that myself.

Before I get started, I'll just mention that all of the code examples in this post are from the demo app, which you can find on Github. Also, the demo app is live, so you can see everything in action.

Webcal

Webcal is what allows a user to click on a link in their browser to subscribe to a calendar in their calendar app.

So, you click on a link in your browser, then a dialog window pops up in your calendar app to subscribe to the calendar:

Then you click Subscribe and, voilà, now that power lunch you have scheduled is in your calendar:

In the future, your calendar app will periodically poll the calendar's URL to check for updates.

Controller

Here's what your controller should look like:

class CalendarsController < ApplicationController
  def show
    respond_to do |format|
      format.ics do
        cal = Icalendar::Calendar.new
        cal.x_wr_calname = 'Awesome Rails Calendar'
        cal.event do |e|
          e.dtstart     = DateTime.now + 2.hours
          e.dtend       = DateTime.now + 3.hours
          e.summary     = 'Power Lunch'
          e.description = 'Get together and do big things'
        end
        cal.publish
        render plain: cal.to_ical
      end
    end
  end
end

View code on Github

I'll highlight the important parts:

  1. Your controller needs to be unauthenticated, or else the user's calendar app won't be able to access the calendar. So, if your ApplicationController is authenticated, your CalendarsController will probably need to inherit directly from ActionController::Base to avoid authentication.

  2. If your calendar contains sensitive data that should only be available to a specific user, you will need to use a strategy similar to password reset links so that you aren't leaking sensitive data. So, your calendar should be available at a path like /calendar/u/:some_long_random_string, instead of /calendar/u/:user_id.

  3. You respond_to the ics format.

  4. You set cal.x_wr_calname to specify the default calendar name that will be displayed in the user's calendar app.

  5. You use render plain: cal.to_ical to actually render the calendar. In older versions of Rails you would specify text instead of plain.

You can learn about everything else going on in this controller by reading the icalendar gem documentation. It's all standard stuff.

Link

Here's what your subscription link should look like:

<%= link_to 'Subscribe', calendar_url(protocol: :webcal, format: :ics) %>

View code on Github

I'll highlight the important parts:

  1. You have to use a full URL, not just a path. The reason for this is the same as for Rails mailers: unlike the browser, the user's calendar app won't know the domain. So, in this example, we're using calendar_url instead of calendar_path.

  2. You specify the webcal protocol. This will create a link that starts with webcal://, instead of http://, which is what tells the browser to hand the link off to the user's calendar app.

  3. You specify the ics format. This will create a link that ends in /calendar.ics, instead of just /calendar, which is what tells Rails to handle the request with the format.ics block in the controller.


That's it, you're done!

Just a reminder that you can find the demo app on Github, or see it in action on Heroku.