TCP Speed Tuning

I’m working on a project that has required me to do some Java socket programming.  All was going well until I uploaded my code to the VPS I’m using, and suddenly the upload speeds I was able to attain posting data to the server sucked.  I mean really sucked, like 4 KB/Sec bad.  My first thought was the connection my hosting company uses, but I was able to upload files via FTP at more than 100 KB/sec, so it must have been something to do with my application.  After a little Googling, I came across this great article that describes the problem I ran into and potential fixes.

http://onlamp.com/pub/a/onlamp/2005/11/17/tcp_tuning.html

The short of it was that the toolkit I was using had a ridiculously low default buffer size set on the socket, so I was basically limited to about 32000 bits per second coming in.  The solution was simple: specify a higher buffer size when setting up the socket, and I was back in business at over 100KB/sec!

Dynamically Generating Embedded Google Calendars with GCal4Ruby

One of the great features of Google Calendar is the ability to embed a calendar view into any webpage using the <iframe> tag.  Google makes this especially simple by providing a great tool that allows you to choose formatting options, which calendar you want to display, color, etc.  This is great, however it only generates the static tag, and there is no easy way to dynamically produce the correct <iframe> tag.

With the latest release of GCal4Ruby, I’ve included three helper methods for producing a dynamically generated <iframe> tag on demand.  This is great for AJAX applications where you want to create your own user interface for the calendar, say to select different calendars to show in one combined calendar view.  GCal4Ruby makes this impressively easy:

service = Service.new()
service.authenticate('account', 'password')
cal = service.calendars.first
cal.to_iframe

Which outputs the following<iframe> tag:

<iframe src='http://www.google.com/calendar/embed?&amp;height=600&amp;bgcolor=%23FFFFFF&amp;width=600&amp;color=%232952A3&amp;src=iowacity1%40seabourneconsulting.com&amp;' style='0 px solid;' width='600' height='600' frameborder='0' scrolling='no'></iframe>

You also have the ability to specify all the standard formatting features using GCal4Ruby.  The following code produces a calendar with no navigation controls in the agenda view:

cal.to_iframe({:showTitle => false,
               :showNav => false,
               :showPrint => false,
               :showTabs => false,
               :showCalendars => false,
               :showTimezone => false,
               :viewMode => 'AGENDA'})

You can also include multiple calendars in one embedded view by using the Service#to_iframe method.  You can display all calendars in the service using the :all token, or you can pass an array of calendar ids:

cals = ['calendarid@gmail.com', '23209384lkjdf9lakdjd@gmail.com']
service.to_iframe(cals)

You can use the same options to choose which calendar elements to display.

Lastly, if you already know the calendar id, and don’t want to authenticate, you can use the to_iframe class method, which looks like this:

Calendar.to_iframe('calendar_id@gmail.com')

Using the class method means you don’t have to authenticate the service and create the calendar instance, which reduces latency by bypassing all communications with the Google Calendar API, as long as you know the Calendar ID ahead of time.

For more information, and all the options you can pass, check out the documentation.

Chicken Waterzooi

One of the things I remember most about my time in Belgium, aside from the beer, was the chicken stew they call Waterzooi.  The national dish of Belgium, chicken waterzooi is popular and widely available, and was the perfect meal on a rainy day.  It has been rainy in my neck of the woods recently, perfect stew weather, so I decided to give it a try.  Here’s the recipe:

Ingredients

  • 1 Whole chicken (3-4 pounds)
  • 2 leeks
  • 2 onions
  • 4 large carrots
  • 1 pound (16 oz) mushrooms
  • 2 bay leaves
  • 2 thyme sprigs
  • 1 lemon
  • 4 tablespoons butter
  • 1 cup heavy cream
  • 1 tablespoon olive oil
  • 1 tablespoon salt
  • 2 tablespoon fresh ground pepper

Instructions

  1. Slice 1 leek, 2 carrots and 1 onion into large pieces.  Rinse off the chicken and place in a large stock pot with the leeks and the carrots and onion.  Add the bay leaves and thyme and 1 tablespoon of the pepper.  Turn on medium and bring to a boil.
  2. Cook the chicken for about 20 minutes, or until poached and not pink can be seen under the skin.  Remove the chicken and set aside to cool.
  3. Strain the remaining vegetables from the pot, reserving the stock.
  4. Slice the remaining carrots, leek, mushrooms and onion into small pieces.
  5. Heat the olive oil in the stock pot on medium-high heat.  Add the onions and leeks and saute for 5 minutes, or until soft.
  6. Add the carrots and mushrooms and the remaining salt, pepper and reserved chicken stock.
  7. Reduce to a simmer, and cook until the vegetables are soft, about 30 minutes.
  8. Meanwhile, once the chicken has cooled, remove the skin and pick the cooked meat off the bones.  For an authentic Belgian feel, remove the chicken in long strips by peeling along the bias.
  9. Once the vegetables have cooked, add the chicken to the pot and cook for 5 minutes more.
  10. Add the butter and cream, stirring to incorporate.
  11. To serve, ladle the soup in wide bowls, and squeeze a wedge of lemon over the top.

Powering Event Management Systems with Google Calendar

A frequent need for many community and groupware websites is an event calendar.  Though there are many calendar solutions available in commercial offerings, custom built and smaller sites often find it difficult to find a solution that is both cost-effective and feature rich.  This white paper examines how to use the existing Google Calendar infrastructure, by way of the published API, as the foundation for a fully featured calendar and event management system.

Background

Google Calendar was introduced by Google in 2006 as a web based, standards compliant calendar application.  In addition to the client interface, Google also published the full Application Programming Interface (API) so that third party developers could interact with the underlying data stored in the Google Calendar system.  The API is based on Atom RSS, an XML based publishing protocol operating over HTTP.  Because it is based on XML and Atom, the API is easily used by almost any programming language available, as long as there is a generic HTTP and XML parsing library available.

In addition to the API, Google also provides an easy way to embed Google Calendars in third party websites using HTML <iframe> tags.  This allows websites to use the display capabilities of the Google Calendar client on their own sites.  The embedded calendars can be customized to match the look and feel of a website while retaining the classic Google branding and style.

This document assumes basic familiarity with the Google Calendar service, as well as knowledge of basic programming concepts, including XML and HTTP.

Advantages of Integration with Google Calendar
It is possible to use the Google Calendar service as both the data provider (via the API) and the display system (through the embedded <iframe>) for a calendar, or as either option individually.

Using the Google Calendar system as a basis for a Calendar/Event Management system can provide many benefits:

  • Reduced Development Time: Google has implemented a robust, tested data model that can be leveraged for event storage and processing.  The well document API decreases development time by providing an existing data model and framework from which to build from.
  • Rich Feature Set: the Google Calendar service implements a range of useful features, from RSS syndication of calendars to Event attendee support.  These features are all standards compliant, ensuring interoperability with major calendaring applications like Microsoft Outlook and Apple iCal.
  • Improved Reliability: the Google services are enterprise applications that have an uptime > %99.9.
  • Cost Effectiveness: the Google services are free – using the API requires nothing more than a standard Google account, but you gain an enterprise level tool for your website.
  • Customization: Google provides many options for customizing the look and feel as well as the functionality available through the Google Calendar API.  You can choose to use only those features that you need, and a display that matches the look-and-feel of your site.

Taken together, the benefits and features of using the Google Calendar system to power an event management/calendar system can be compelling for the small to medium sized website.

Requirements
In our experience, small to medium sized community based sites have a common set of requirements for a calendar/event management system:

  • Display a publicly available calendar of events
  • Display private calendar events restricted to members/registered users
  • Categorize events by type and display in discrete sub-calendars
  • Allow users to submit events
  • Accept/Reject submitted events
  • Manage events, including updating and deletion

System Design

Building a system that interacts with the Google Calendar system has three main components:

  • The application server
  • The display subsystem
  • The event management staging subsystem

Figure 1 details the structure of the system.  The application server is the underlying structure and code upon which the website is based.  This may be any content management system, Ruby on Rails, etc.

The display subsystem includes all of the forms and ‘views’ that provide an interface to the user.  Within the display subsystem, and the main interface to the Google Calendar data is the embedded Google Calendar.  The calendar is embedded using <iframe> tags pointing to a Google hosted calendar resource.  Options are passed in the <iframe> source parameter, instructing Google on how to format the returned HTML calendar.  The display subsystem manages the display of the embedded calendar by dynamically updating the <iframe> and source parameters.

The event management staging subsystem handles events before they are committed to the Google Calendar service.  This is essential if a requirement for the system is to approve/reject event submissions before they are displayed publicly.  The staging subsystem can also provide a mechanism for storing and displaying additional information other than what Google provides in the default event data model (examples of this may be contact information, admission costs, website, etc.)

Figure 1: Diagram of System Design Utilizing Google Calendar

Figure 1: Diagram of System Design Utilizing Google Calendar

Workflow
The workflow for the system is relatively simple.  First, a user views a form with fields for entering all of the event information, then submits that information to the application server.  Assuming everything is validated, the event information is saved into the Event Staging System.  Before the event is saved to the Google Calendar service, it is reviewed and approved by a site administrator or other designated user.  Once the information is approved, the event staging system saves the event to the Google Calendar system using the Google Calendar API.  At this point, the information is ‘live’ and viewable on the calendar.

Any changes to the event go through the Event Staging System rather than the Google Calendar interface – this way, all changes are tracked and approved in the staging system before being made public.

Figure 2: Event Submission Workflow

Figure 2: Event Submission Workflow

Staging System Data Model
The key to synchronizing information between the Staging system and the Google Calendar system are unique IDs.  Google assigns unique IDs to all calendars and events within its system, providing the Staging system with a way to select and track data within the Google Calendar system.  By saving this ID locally with the event information, a representation of the Google event is made. Any change then made to the local representation of the event can be transparently saved to the Google representation via the unique ID.

Interacting with the Google Calendar API
Assuming the Application server is built using a MVC framework, the obvious place for interacting with the Google Calendar service is through the model.  The model will already contain the methods to save and update data, so it is a simple task to add the equivalent code to save the same information to the Google Calendar service.

Data Duplication vs Latency
A key thing to note about the system design described above is that data exists in two places simultaneously; in the staging database and the Google calendar service.  This is a design decision that weighs the latency cost of communicating with the Google Calendar service against the technical hassle of maintaining two separate data sets for the same event.

Depending on location, a typical API request to the Google servers will take anywhere from 200 to 600 ms to complete.  The most basic ’save’ POST operation to the Google calendar service requires two requests, one to authenticate and one to send the data.  Combined, these calls can take anywhere from 1/2 second to 1 second to complete, a very noticeable number for the end user.  And 2 requests to the Google service is the absolute minimum.  Therefore, using the Google service as the sole repository of the data, while possible, brings a sacrifice in perceived usability because of the overhead cost of the API requests.

The alternative, as outlined above, is a hybrid approach.  Latency is reduced by only communicating with the Google Server when necessary, i.e. when updating the displayed data on the Google servers.  For all other operations, such as searching for a particular event or populating a form with event data, the local staging table can be used, providing a faster end user experience.

However, this approach requires special care to reduce potential uses where data can become out of sync.  A simple set of design rules can help eliminate these situations:

  1. Information should flow in one direction – i.e. only one data set should be the ‘master’, and all others are updated from that set.
  2. If a remote save operation fails and data can not be updated to the remote service, the local operation should fail and no local data should be saved.
  3. If in doubt as to whether the remote copy is in sync with the master copy, do a full replace of the record from the master set.

Conclusions

Implemented correctly, integrating Google Calendars on your site can save time, lower development costs and give you cutting edge and standards compliant features, all for free.  The architecture outlined here will allow you to retain the look and feel of your own site, while making use of the features provided by Google through the Calendar API.

Have questions or want to discuss a project?  Feel free to contact me.  Comments and thoughts are always welcome.

Tip for Using Skype with GoToMeeting

I had a call with clients today via Go To Meeting and Skype in which I was unable to enter the Access Code for the conference call. GoToMeeting kept inaccurately recognizing the code I was typing in. After about 10 minutes of re-entering the access, I was finally able to get in. The trick? Enter the code very slowly, with a couple of seconds in between each number. Since GoToMeeting figures out what you are typing based on the tones for each number, there must be some problem with the way skype transmits the tones.

Hidden Embedded Google Calendar Parameters

One of the more powerful features of the Google Calendar system is the ability to embed calendars in websites using an <iframe> tag.  In the iframe’s ’source’ parameter you can pass an encoded string of parameters to Google that changes the default appearance of the returned embedded calendar.  Using the Customize Embedded Calendar tool from Google, developers have reverse engineered what the parameters are that you can pass to Google, but through some newsgroup surfing, I’ve come across a few parameters that are undocumented, or unimplemented in the Customize tool.

  1. Dates: one of the frequent uses of the embedded calendar is to publicly display upcoming events.  However, the default behavior of the embedded calendar is to display the current date.  To get around this, you can specify a date range using the ‘dates’ parameter appended in the source string.  The parameter takes a string of two dates, the start and end date, in the format of ‘yyyymmdd/yyyymmdd’.  You’ll want to make sure and encode the ‘/’ as ‘%2F’ for Google to parse this parameter.  One thing to note: specifying a default date range doesn’t affect the Agenda view.
  2. Private Key: ever tried to embed a calendar and received an error about not having adequate permissions to view it?  One way around this is to make your calendar public by default, but this means the events will also turn up in public searches – not an ideal solution for many who want to share a calendar on a password protected site, for example.  Also undocumented is the ability to specify a private key that will allow you to embed a private calendar with no warnings.  Simply append ‘pvttk=’ to the source string using the private key from the calendar settings page.  Simply grab the URL from the ‘Private Address’ HTML button towards the bottom, and parse the ‘pvttk’ parameter from the end.  Unfortunately, there is no programmatic way to do this – you’ll need to grab the private key manually from the website for each calendar you want to embed.

Review: Ford Fusion with Microsoft SYNC

I just got back from a week-long road trip to Montana.  We rented a car, and got a nice change from the usual Chevy that Enterprise seems to rent non-stop: a Ford Fusion.  We had asked for a car with an auxiliary input to the stereo so that we could listen to our iPhones while driving, and so they gave us the Fusion.  However, we were in for a surprise as the fusion has Microsoft’s new foray into car stereo equipment: Microsoft Sync.

Sync is essentially a microcomputer running a custom version of Windows CE.  The idea is that you can play almost any portable music device through a built-in USB port, and Auxiliary line-in port or via Bluetooth Audio.  There is no display, rather the Sync system uses the built-in standard 1 line stereo LCD display.

The upside of using the Sync system versus a standard auxiliary port is that you can control the playback functions from the controls on the steering wheel, as well see the track names and artist information on the display.  The USB port is also powered, so you can charge your device while its plugged in.  Our iPhones were on the list of approved devices that work well with the system (surprising for Microsoft), so we were in business.

I was also excited because new to the iPhone OS 3.0 supports the AD2P stereo bluetooth protocol for streaming sound, as well as a partial implementation of the AVRCP protocol for controlling playback.  So, I paired the phone with the stereo using the fairly straightforward instructions.  Note: the system will only let you do this if the car isn’t moving, which is really frustrating if you are a passenger and trying to set up the system.  Soon enough, I had my iPhone playing music on the car stereo via bluetooth.  There were a few pretty sweet features that showed some forethought into the implementation.  First, whenever the key was removed from the ignition and the phone was playing music, the system would pause the music.  Then, when you turned the car on again, it would automatically resume the song where you left off when you exited the car.  Pretty slick!  And the whole time I didn’t have to take the iPhone out of my pocket.

There were also a couple of drawbacks to the bluetooth side of things.  First, no song information was transfered or displayed on the dashboard display.  Second, Apple only partially implemented the AVRCP protocol, so the Sync system can only send play/pause signals – no skipping forward or back between songs.  Given that this is a pretty big disadvantage, especially on a road trip when you are listening to music for hours on end, we decided to try the USB route.

Plugging the iPhone into the USB port (which is hidden inside the center console) brings up the Sync system.  It starts by trying to index all the songs on the device – not sure why really.  After about 5 minutes of a progress bar, the car announces (via a built in voice synthesizer) that indexing could not retrieve all the songs, and that I’d have to enter track metadata manually into the system.  Yeah right!  Nice idea, but no one in their right mind would try to do this without a keyboard, and especially not with a one line display and turn nobs to select all the letters.  Anyway, we hit cancel and were good to go.  We selected the playlist we wanted, and it the Sync system started playing the song and displaying the track information on the stereo’s display.  Great!

We were cruising along for about 5 hours this way, then stopped at a gas station.  We had to turn off the car, but when we turned it back on to get going again, the stereo froze.  No sound, but it was displaying the track information for the last song we played.  After a few minutes, the display unlocked and gave us an error ‘USB Empty’.  Hmmm.  The iPhone was definitely plugged in, but was no longer getting any power from the port.  We tried restarting the car, unplugging the cable – basically everything we could think of, but we kept getting the same error.

After a little sluething online – thank god for iPhone’s internet connection – we discovered that this was a fairly common problem with the Sync system, and exposed a major design flaw.  There was no way to do a hard reset of the system!  Apparently, the Windows version used in the system had a buggy USB driver which would shut down under certain circumstances.  The only solution was to reboot everything, however there was no way to reboot the Sync system from the controls.  We tried to do a soft reset through the menu system, but no such luck.  The computer even kept drawing power when the car was off.  After more sleuthing, we found a forum post that suggested removing the fuse from under the steering wheel that controlled the power to the dash (For those of you wondering, it was fuse #13 – use the provided tool on the inside of the cover, and remove the fuse for about 30 seconds.  Your trunk may pop open.)  Once we reinserted the fuse and started the car up again, everything worked fine!

That turned out to be the only major hiccup we ran into.  Overall, I think the system works great, and it is definitely nice to see some serious integration between cars and the devices we use all the time.  The system would be improved by a better display, and of course by a hard reset option that is more accessible than popping out a fuse.

Getting Notes to Sync in Mail from iPhone OS 3.0

One feature I am really excited about in the new iPhone 3.0 OS released on Wednesday is the ability to sync your notes with the Mail application in Leopard.  However, it took me a few tries to figure out how to get this to work exactly.  I realized that I ran into this problem because I had never created a note on my Mac in Mail, so this walkthrough assumes you are in the same position.  I’ve outlined the steps below:

  1. Enable Notes syncing in iTunes.  You can do this by going to the ‘Info’ tab when you have your iPhone/iPod attached to your computer.  Check the ‘Sync Notes’ checkbox about halfway down the page.notes-itunes
  2. If you go ahead sync your iPhone now, and you’ll notice that none of the Notes show up in mail.  If you have multiple mailboxes, they’ll be listed under the ‘Reminders’ heading in the Mail sidebar, with little note icons, but you won’t see any from your iPhone.  What you have to do is create a note in Mail now, by going to File > New Note.  Type something in and save it, and you’ll see a new note icon under ‘Reminders/Notes’ labeled ‘On My Mac’.  mailboxes
  3. Sync your iPhone one more time.  After its completed, you should see all your notes under the ‘On My Mac’.

2009 MacBook Pro Review

Got home yesterday and decided to buy a new computer – just quit my job on Friday and signed a new contract for Seabourne so I felt like I could use a newmacbook pro toy.  For the last three years I’ve been using a Core 2 Duo MacBook and a Lenovo Thinkpad T60 with Kubuntu.  I liked both computers, but have been feeling hindered by the lack of screen real estate, especially when coding.  About three months ago I almost bought a 15 inch MBP, but decided to wait until I had more money.  And boy am I glad I did!  The whole line of MacBook Pros were updated last week at WWDC 09, and they all just got a lot better, and a lot cheaper.  Here’s my quick review.

Unboxing
As usual, Apple has done a terrific job on packaging.  Even though the computer is a solid 2 inches wider than my MacBook, there is probably 1/3 less packaging total.  Rather than a soft sleeve, the MBP comes wrapped in a clear plastic, similar to what the iPhones now come in.  As usual, a small manual and restore CDs come in a box below the computer, along with the power supply.  However, Apple no longer includes a free Apple Remote with laptops.

Look and Feel
The computer is gorgeous.  No other way to put it.  If I could pick one adjective for the whole thing, it would be ’smooth’.  The aluminum has a beautiful satin finish that feels silky to the touch, but definitely has enough texture to make it grippable.  The screen has a glossy finish – I don’t mind, but I can see how this would bug some people as you really have to be careful not to touch it otherwise the fingerprints are glaringly visible.  The increased screen color gamut is obviously apparent when comparing this version of the MBP with any previous Apple laptop – the colors are much sharper.  I’ve noticed that the screen hinge seems much smoother than the MacBook.  It is easy to move the screen with one finger and have it stay in that position.

The keyboard is great, though the same chiclet style as all the Apple laptops are now (some people hate it).  The keys are much more firm than the MacBook, a big improvement.  I really enjoy the backlit keyboard, and there are some very handy function buttons to dim or brighten the lighting behind the keys.

Size & Screen
One of the major reasons I bought a new computer was to get more screen space.  I’ve enjoyed the size of the 13 inch MacBook, but just felt that I’d be more productive with a 15 inch notebook.  Given the width and weight are about the same for both models now, it turns out to be a gain without much of a loss.
Screenshot w terminals

The screen is vibrant and has what seems like twice the brightness of my old MacBook.  Honestly, I feel about twice as productive with the new setup (as you can see in the picture above, that’s probably because I can have two terminal windows open at once!)

This thing is thin!  Though significantly wider than a MacBook (obviously), it is probably 10-20% thinner.  The LCD has benefited from the LED backlights by dropping a few millimeters of width.

Trackpad and Gestures
One of my favorite things about the new laptop is the support for multi-finger gestures.  Three fingers side to side goes back and forward in the browser or the finder, four fingers up and down triggers exposé or shows the desktop.  This took a little getting used to, but now that it’s an ingrained habit, I find that it is really hard to go back to computers without these features.  I had to download the new Release Candidate of Firefox 3.5 to get the three finger gesture support.  The track pad itself is much larger than almost any other laptop, which is really great for scrolling both horizontally and vertically through large code blocks.  I have had no problem with the new integrated button.  Overall I’m pretty impressed with the selective sensitivity of the track pad; I can keep my thumb on the button part of the pad and use my finger for pointing, and everything works smoothly as it would with a dedicated button.

Battery Life
The biggest selling point for me on this laptop was the new lithium-polymer battery.  The downside is that it’s integrated tightly into the unibody enclosure so you can’t remove it yourself or swap out another battery for longer uses.  The upside is that the battery now gets about 5-6 hours of use per charge, more than twice what my old MacBook used to get.  I’ve tested it out for about a week now, and with standard use (wireless on, screen brightness at 50%, bunches of programs open) I can get 5.5 hours pretty consistently.  The battery is also now supposed to last up to 1000 charges, or three years of standard use, and is replaceable at an apple store for $129, the same price as a new battery for older laptops.

Summary
Overall, this is the best MacBook Pro ever, and I think the best Apple Laptop ever.  The 15 inch MBP is a great middle ground between portability and usability.  The new features make this a compelling computer to use, has increased my productivity, but most importantly, is a pure joy to use.

New Ruby Google Calendar API Gem: GCal4Ruby

I’ve written a few previous posts about using the Google Calendar API with Ruby and Ruby on Rails, but I’ve wanted to create my own library that includes the functionality I’ve had to monkey patch into others’.  So, I’ve gone ahead and created GCal4Ruby, a full featured Google Calendar API wrapper for Ruby available as a Gem.  You can download the source here or with ’sudo gem install gcal4ruby’.  Some of the features:

  • Clean, ActiveRecord style syntax and code structure
  • Full querying of calendars and events
  • Full calendar creation, editing and deletion
  • Setting public/private permissions for calendars
  • Event creation/editing/deletion
  • Support for full event recurrence
  • Support for event reminders
  • Support for event attendees

All of the major features have been implemented, and over the next couple of days I hope to complete the smaller outstanding todos.  Any help testing is much appreciated.  Below are some example uses.  For more information, check out the documentation.

Get all calendars for an account

       service = Service.new
       service.authenticate("user@gmail.com", "password")
       calendars = service.calendars

Create a new Calendar

       cal = Calendar.new(service)

Find an existing Calendar

       cal = Calendar.find(service, "New Calendar", {:scope => :first})

Find all calendars containing the search term

       cal = Calendar.find(service, "Soccer Team")

Find a calendar by ID

       cal = Calendar.find(service, id)

Create a new Event

       event = Event.new(calendar)
       event.title = "Soccer Game"
       event.start = Time.parse("12-06-2009 at 12:30 PM")
       event.end = Time.parse("12-06-2009 at 1:30 PM")
       event.where = "Merry Playfields"
       event.save

Find an existing Event

       event = Event.find(cal, "Soccer Game", {:scope => :first})

Find all events containing the search term

       event = Event.find(cal, "Soccer Game")

Create a recurring event for every Saturday

       event = Event.new(calendar)
       event.title = "Baseball Game"
       event.where = "Municipal Stadium"
       event.recurrence = Recurrence.new
       event.recurrence.start = Time.parse("13-06-2009 at 4:30 PM")
       event.recurrence.end = Time.parse("13-06-2009 at 6:30 PM")
       event.recurrence.frequency = {"weekly" => ["SA"]}
       event.save

Create an event with a 15 minute email reminder

       event = Event.new(calendar)
       event.title = "Dinner with Kate"
       event.start = Time.parse("20-06-2009 at 5 pm")
       event.end = Time.parse("20-06-209 at 8 pm")
       event.where = "Luigi's"
       event.reminder = {:minutes => 15, :method => 'email'}
       event.save

Create an event with attendees

       event = Event.new(calendar)
       event.title = "Dinner with Kate"
       event.start = Time.parse("20-06-2009 at 5 pm")
       event.end = Time.parse("20-06-209 at 8 pm")
       event.attendees => {:name => "Kate", :email => "kate@gmail.com"}
       event.save

Next Page »