Apr 24 fetching Google Calendar events in Ruby

tags: rexml google data api google calendar | comments

You have a Google Calendar and you want to make some cool things by fetching your events, the Google Calendar API and the gdata ruby gem make that a charm. It’s all about login in to your calendar, fetching the events and parsing the REXML response.

Let’s see an easy example:

# gcal.rb

# to run standalone we require rubygems
# you surely don't need it to to use the GCal class inside your web app
require 'rubygems'
# the ruby magic gem that makes this all posible
require 'gdata'
require 'ostruct'

class GCal

  attr_accessor :events, :user

  def initialize(user = GOOGLE_USER_WITH_PUBLIC_CALENDAR, pwd = PASSWORD)
    @user = user
    @events = []
    @cal = GData::Client::Calendar.new
    # we are login to google
    @cal.clientlogin(user,pwd)
    # and fetching the public calendar of the user
    response = @cal.get("https://www.google.com/calendar/feeds/#{@user}/private/full")

    # entries are the events, we turn the response into an REXML object to iterate the XML elements
    response.to_xml.elements.each('entry') do |entry|
      # we fill the details of the events
      details = OpenStruct.new({ :title => entry.elements['title'].text })
      details.attendees = []

      entry.elements.each do |elem|
        case elem.name
        when "who"
          unless elem.attributes['valueString'] == "calendar calendar"
            details.attendees << elem.attributes['valueString']
          end
        when "when"
          details.starts_at = DateTime.parse elem.attributes['startTime']
          details.ends_at = DateTime.parse elem.attributes['endTime']
        when "where"
          details.where = elem.attributes['valueString']
        end
      end
      @events << details
    end

    # just as an example, printing the event details
    def list_events
      unless @events.nil?
        @events.each do |event|
          puts "event #{event.title}"
          puts "from #{event.starts_at} to #{event.ends_at}"
          puts "where #{event.where}"
          event.attendees.each do |attendee|
            puts "attendee: #{attendee}"
          end
        end
      end
    end
  end
end

# just a running example, remove the line to use inside your web app
GCal.new.list_events

You notice the calendar is public, well it turned out I could not get to fetch the non-public ones, and every Google example was about public ones.

And the Specs?

Yes, you are a cool kid and can live without the Specs, huh? Well we copy an example
of expected Google response and stub the network access.

# gcal_spec.rb
require File.dirname(__FILE__) + '/../spec_helper'
# gcal.rb lives in RAILS_ROOT/lib
require File.dirname(__FILE__) + '/../../lib/gcal'

describe GCal do
  it 'should retrieve events correctly' do
    user = "calendar@example.com"
    # we are stubbing all google access, if you are better with real google access comment it
    stub_gdata_google_calendar

    cl = GCal.new user, "fake"
    cl.user.should == user
    cl.events.length.should == 2
    first_event = cl.events.first
    first_event.title.should == "some event"
    first_event.starts_at.should be_instance_of(DateTime)
    first_event.ends_at.should be_instance_of(DateTime)
    first_event.attendees.size.should == 2
    first_event.where.should == "plaza"
  end

  private

  def stub_gdata_google_calendar
    # we stub the whole Calendar object
    GData::Client::Calendar.stub!(:new)
    # bypassing google login
    @cal.stub!(:clientlogin).and_return(true)
    # and stub the response also
    @cal.stub!(:get).and_return(my_stubbed_response)
  end

  def my_stubbed_response
    response = GData::HTTP::Response.new
    # we are not caring about headers
    response.headers = { }
    response.body = GCAL_BODY
    response.status_code = 200
    response
  end
   
   # hey, if google changes its response the specs still pass, but your gcal WILL NOT really work
   # you could problably set a public self calendar, remove the stub_gdata_google_calendar line 
   # and set login info for that user and check against real google response
   
   # google body response
   GCAL_BODY = grab the value from http://gist.github.com/101258
end

well, that’s it

blog comments powered by Disqus