There are three main data structures, called components in iCalender to represent various types of calendaring information. These are:
The datatype that is used to represent things like appointments, meetings, birthdays, schedules, etc.
This is used to describe the properties of assignments you have. Think of things like ‘write the minutes for last week's meeting’ or ‘buy anniverary present’.
Another component whose name makes it obvious what it is used for. You can log your activities into your calendar with a journal entry.
The iCalendar format is very straightforward. Here's a simple example.
BEGIN:VEVENT DTSTART:20010714T170000Z DTEND:20010715T035959Z SUMMARY:Bastille Day Party ATTACH;FMTTYPE=image/jpeg:http://domain.com/images/bastille.jpg END:VEVENT
This shows a simple event component that tells us there is a “Bastille Day” party somewhere starting at 5 p.m on July 14, and ending just before 4 a.m the next day. It also has a link to a jpeg image, which might be a flyer or something to promote the party.
As you can see, the format is a straightforward list of lines containing key/value pairs. The example shows the three granularities of data in iCalendar. The first is the component level---- that's everything between the BEGIN:VEVENT and END:VEVENT. Each component contains a list of properties; properties are the next level of granularity. Here the properties are dtstart (start date), dtend (end date), summary and attach (file attachment). The last level of granularity is the parameter; the attach property has a parameter, fmttype.
To see how this translates into code in Reefknot, and to show how you might want to use the RFCs, we'll dissect a simple component. The module that implements the handling of the iCalendar data format is Net::ICal. We will pick the Alarm component (describing reminders for the components above; i.e. a “you are having lunch with your boss in 30 minutes” popup at the appropriate time).
The alarm component is described in section 4.6.6 of the RFC. The basic format is:
alarmc = "BEGIN" ":" "VALARM" CRLF (audioprop / dispprop / emailprop / procprop) "END" ":" "VALARM" CRLF
This is ABNF, the standard language used to formally define things in RFCs. It defines that an alarm starts with a line that contains the literal strings BEGIN, : and VALARM concatenated together, and that the line ends with CRLF which is elsewhere defined as “\012\015”.
After that, you get to choose from four things, which will be defined shortly, and then it ends with a string much like the one we started with. An example alarm would be:
BEGIN:VALARM ACTION:DISPLAY DESCRIPTION:Breakfast meeting with executive team at 8:30 AM EST. TRIGGER:-PT30M REPEAT:2 DURATION:PT15M END:VALARM
This happens to be an alarm that displays a popup on your screen. This is defined by “dispprop” (the second option in the ABNF above). If you read a little more of section 4.6.6 in the RFC, you will see the following:
dispprop = 3*( ; the following are all REQUIRED, ; but MUST NOT occur more than once action / description / trigger / ; 'duration' and 'repeat' are both optional, ; and MUST NOT occur more than once each, ; but if one occurs, so MUST the other duration / repeat / ; the following is optional, ; and MAY occur more than once *x-prop )
This lists the possible properties of an alarm component that has a value of DISPLAY for its ACTION property. What the possible values of these properties are is described in section 4.8.x.x of RFC 2445.
If you wanted to create the example Alarm component from above with Net::ICal you'd need the following code.
my $alarm = Net::ICal::Alarm->new ( action => 'DISPLAY', description => 'Breakfast meeting with executive team at 8:30 AM EST.', trigger => '-PT30M', repeat => 2, duration => 'PT15M' );Now, let's take a look at how this is handled.
Net::ICal uses Class::MethodMapper, so you can describe property methods for a class in a hash. This looks like the following (only displaying the parts that are relevant to a display alarm)
my $map = { action => { type => 'parameter', doc => 'the action type of this alarm', domain => 'enum', options => [qw(AUDIO DISPLAY EMAIL PROCEDURE)], },
The ACTION property is described in section 4.8.6.1 of the RFC. If we look there, we see the value type of the property is TEXT, which means a string. The possible values of the property are AUDIO, DISPLAY, EMAIL and PROCEDURE. So basically it is an enumeration with four possible values. If you look at the method map above, you see that the domain is enum, and the options key has a listref with the possible values.
Class::MethodMapper wants you to specify a few more things. First the type of the method. This can be parameter as above, which means the value will be stored if the object gets serialized, or volatile, meaning it won't be. And secondly, you specify a short descriptive sting, documenting the purpose of this method.
The next method in the method map is the description. This property is defined in section 4.8.1.5. It too has a value type of TEXT, but it is a free-form string, not an enumeration. This property has two optional parameters, namely altrep and language. Hence it has the domain type param and the possible parameters listed in the list-value of the options key.
description => { type => 'parameter', doc => 'description of this alarm', domain => 'param', options => [qw(altrep language)], value => undef, },
The trigger property, as described in section 4.8.6.3 is even more complicated, so we've created a seperate module for it. You define that in the map by setting the domain to ref and putting the name of the module in the options key.
trigger => { type => 'parameter', doc => 'when the alarm will be triggered', domain => 'ref', options => 'Net::ICal::Trigger', value => undef, },
The repeat property is a simple integer value that defines the number of times the alarm is repeated. It is described in section 4.8.6.2 of the RFC.
repeat => { type => 'parameter', doc => 'the amount of times the alarm is repeated', value => 0, },
Because the properties were defined using Class::MethodMapper, you automatically have code to query and change them.
$repeat = $alarm->repeat; # retrieves value of repeat $repeat = $alarm->get('repeat'); # ditto $alarm->repeat (3); # changes the component to repeat 3 times $alarm->set (repeat => 3); # ditto