= iCalendar -- Internet calendaring, Ruby style

This is a Ruby library for dealing with iCalendar files.  Rather than
explaining myself, here is the introduction from RFC-2445, which
defines the format:

The use of calendaring and scheduling has grown considerably in the
last decade. Enterprise and inter-enterprise business has become
dependent on rapid scheduling of events and actions using this
information technology. However, the longer term growth of calendaring
and scheduling, is currently limited by the lack of Internet standards
for the message content types that are central to these knowledgeware
applications. This memo is intended to progress the level of
interoperability possible between dissimilar calendaring and
scheduling applications. This memo defines a MIME content type for
exchanging electronic calendaring and scheduling information. The
Internet Calendaring and Scheduling Core Object Specification, or
iCalendar, allows for the capture and exchange of information normally
stored within a calendaring and scheduling application; such as a
Personal Information Manager (PIM) or a Group Scheduling product. 

The iCalendar format is suitable as an exchange format between
applications or systems. The format is defined in terms of a MIME
content type. This will enable the object to be exchanged using
several transports, including but not limited to SMTP, HTTP, a file
system, desktop interactive protocols such as the use of a memory-
based clipboard or drag/drop interactions, point-to-point asynchronous
communication, wired-network transport, or some form of unwired
transport such as infrared might also be used.

Now for some examples:

## Probably want to start with this

  require 'rubygems' # Unless you install from the tarball or zip.
  require 'icalendar'
  require 'date'

  include Icalendar # Probably do this in your class to limit namespace overlap

## Creating calendars and events is easy.

  # Create a calendar with an event (standard method)
  cal = Calendar.new
  cal.event do
    dtstart       Date.new(2005, 04, 29)
    dtend         Date.new(2005, 04, 28)
    summary     "Meeting with the man."
    description "Have a long lunch meeting and decide nothing..."
    klass       "PRIVATE"
  end

  cal.publish

## Or you can make events like this
  event = Event.new
  event.start = DateTime.civil(2006, 6, 23, 8, 30)
  event.summary = "A great event!"
  cal.add_event(event)

  event2 = cal.event  # This automatically adds the event to the calendar
  event2.start = DateTime.civil(2006, 6, 24, 8, 30)
  event2.summary = "Another great event!"

  # Now with support for property parameters
  params = {"ALTREP" =>['"http://my.language.net"'], "LANGUAGE" => ["SPANISH"]}

  cal.event do
    dtstart Date.new(2005, 04, 29)
    dtend   Date.new(2005, 04, 28)
    summary "This is a summary with params.", params
  end
  
  # We can output the calendar as a string to write to a file, 
  # network port, database etc.
  cal_string = cal.to_ical
  puts cal_string

## ALARMS

## Within an event, you can create e-mail notification alarms like this...

  cal.event.do
    # ...other event properties
    alarm do
      action        "EMAIL"
      description   "This is an event reminder" # email body (required)
      summary       "Alarm notification"        # email subject (required)
      attendees     %w(mailto:me@my-domain.com mailto:me-too@my-domain.com) # one or more email recipients (required)
      add_attendee  "mailto:me-three@my-domain.com"
      remove_attendee "mailto:me@my-domain.com"
      trigger       "-PT15M" # 15 minutes before
      add_attach    "ftp://host.com/novo-procs/felizano.exe", {"FMTTYPE" => "application/binary"} # email attachments (optional)
    end
    
    alarm do
      action        "DISPLAY" # This line isn't necessary, it's the default
      summary       "Alarm notification"
      trigger       "-P1DT0H0M0S" # 1 day before
    end

    alarm do
      action        "AUDIO"
      trigger       "-PT15M"
      add_attach    "Basso", {"VALUE" => ["URI"]}  # only one attach allowed (optional)
    end
  end

  # Output

  # BEGIN:VALARM
  # ACTION:EMAIL
  # ATTACH;FMTTYPE=application/binary:ftp://host.com/novo-procs/felizano.exe
  # TRIGGER:-PT15M
  # SUMMARY:Alarm notification
  # DESCRIPTION:This is an event reminder
  # ATTENDEE:mailto:me-too@my-domain.com
  # ATTENDEE:mailto:me-three@my-domain.com
  # END:VALARM
  #
  # BEGIN:VALARM
  # ACTION:DISPLAY
  # TRIGGER:-P1DT0H0M0S
  # SUMMARY:Alarm notification
  # END:VALARM
  # 
  # BEGIN:VALARM
  # ACTION:AUDIO
  # ATTACH;VALUE=URI:Basso
  # TRIGGER:-PT15M
  # END:VALARM

## Timezones
  
  # Create a timezone definition (previous convention)
  cal = Calendar.new
  timezone = Icalendar::Timezone.new
  daylight = Icalendar::Daylight.new
  standard = Icalendar::Standard.new

  timezone.timezone_id =            "America/Chicago"

  daylight.timezone_offset_from =   "-0600"
  daylight.timezone_offset_to =     "-0500"
  daylight.timezone_name =          "CDT"
  daylight.dtstart =                "19700308TO20000"
  daylight.recurrence_rules =       ["FREQ=YEARLY;BYMONTH=3;BYDAY=2SU"]

  standard.timezone_offset_from =   "-0500"
  standard.timezone_offset_to =     "-0600"
  standard.timezone_name =          "CST"
  standard.dtstart =                "19701101T020000"
  standard.recurrence_rules =       ["YEARLY;BYMONTH=11;BYDAY=1SU"]

  timezone.add(daylight)
  timezone.add(standard)
  cal.add(timezone)

  # Now, you can make timezones like this
  cal = Calendar.new
  cal.timezone do
    timezone_id             "America/Chicago"

    daylight do
      timezone_offset_from  "-0600"
      timezone_offset_to    "-0500"
      timezone_name         "CDT"
      dtstart               "19700308TO20000"
      add_recurrence_rule   "FREQ=YEARLY;BYMONTH=3;BYDAY=2SU"
    end

    standard do
      timezone_offset_from  "-0500"
      timezone_offset_to    "-0600"
      timezone_name         "CST"
      dtstart               "19701101T020000"
      add_recurrence_rule   "YEARLY;BYMONTH=11;BYDAY=1SU"
    end
  end
  
  # Both conventions output
  
  # BEGIN:VTIMEZONE
  # TZID:America/Chicago
  # BEGIN:DAYLIGHT
  # TZOFFSETFROM:-0600
  # TZOFFSETTO:-0500
  # TZNAME:CDT
  # DTSTART:19700308T020000
  # RRULE:FREQ=YEARLY;BYMONTH=3;BYDAY=2SU
  # END:DAYLIGHT
  # BEGIN:STANDARD
  # TZOFFSETFROM:-0500
  # TZOFFSETTO:-0600
  # TZNAME:CST
  # DTSTART:19701101T020000
  # RRULE:FREQ=YEARLY;BYMONTH=11;BYDAY=1SU
  # END:STANDARD
  # END:VTIMEZONE

== Unicode
Add `$KCODE = 'u'` to make icalender work correctly with Utf8 texts
  
== Parsing iCalendars:
  
  # Open a file or pass a string to the parser
  cal_file = File.open("single_event.ics")
  
  # Parser returns an array of calendars because a single file
  # can have multiple calendars.
  cals = Icalendar.parse(cal_file)
  cal = cals.first
  
  # Now you can access the cal object in just the same way I created it
  event = cal.events.first
  
  puts "start date-time: " + event.dtstart
  puts "summary: " + event.summary

== Finders:

Often times in web apps and other interactive applications you'll need to
lookup items in a calendar to make changes or get details.  Now you can find
everything by the unique id automatically associated with all components.
  
  cal = Calendar.new
  10.times { cal.event } # Create 10 events with only default data.
  some_event = cal.events[5] # Grab it from the array of events

  # Use the uid as the key in your app
  key = some_event.uid

  # so later you can find it.
  same_event = cal.find_event(key)

== Examples:

Check the unit tests for examples of most things you'll want to do, but please
send me example code or let me know what's missing.

== Download

The latest release version of this library can be found at

* http://rubyforge.org/projects/icalendar/

Documentation can be found at

* http://icalendar.rubyforge.org/

== Installation

It's all about rubygems:

$ sudo gem install icalendar

== License

This library is released under the same license as Ruby itself.

== Support & Contributions

The iCalendar library homepage is http://icalendar.rubyforge.org/ 

There is an icalendar-devel@rubyforge.org mailing list that can be
used for asking questions, making comments or submitting patches.
