[Rt-commit] r3538 - in Data-ICal: . lib/Data lib/Data/ICal lib/Data/ICal/Entry lib/Data/ICal/Entry/Alarm t

glasser at bestpractical.com glasser at bestpractical.com
Tue Jul 26 22:54:00 EDT 2005


Author: glasser
Date: Tue Jul 26 22:53:59 2005
New Revision: 3538

Added:
   Data-ICal/lib/Data/ICal/Entry/Alarm/
   Data-ICal/lib/Data/ICal/Entry/Alarm/Audio.pm
   Data-ICal/lib/Data/ICal/Entry/Alarm/Display.pm
   Data-ICal/lib/Data/ICal/Entry/Alarm/Email.pm
   Data-ICal/lib/Data/ICal/Entry/Alarm/Procedure.pm
   Data-ICal/lib/Data/ICal/Entry/Event.pm
   Data-ICal/lib/Data/ICal/Entry/FreeBusy.pm
   Data-ICal/lib/Data/ICal/Entry/Journal.pm
   Data-ICal/lib/Data/ICal/Entry/TimeZone.pm
   Data-ICal/lib/Data/ICal/Entry/Todo.pm
   Data-ICal/t/02.linewrap.t
   Data-ICal/t/03.unknown-props.t
   Data-ICal/t/04.mandatory-props.t
   Data-ICal/t/05.prop-params.t
   Data-ICal/t/06.prop-bad-quote.t
Removed:
   Data-ICal/lib/Data/ICal/Todo.pm
Modified:
   Data-ICal/   (props changed)
   Data-ICal/Makefile.PL
   Data-ICal/README
   Data-ICal/lib/Data/ICal.pm
   Data-ICal/lib/Data/ICal/Entry.pm
   Data-ICal/lib/Data/ICal/Property.pm
   Data-ICal/t/00.load.t
   Data-ICal/t/01.simplegen.t
Log:
 r37849 at tin-foil:  glasser | 2005-07-25 22:02:44 -0400
 Local branch of Data::ICal
 r37850 at tin-foil:  glasser | 2005-07-25 22:04:34 -0400
 Data::ICal uses Class::Accessor.
 r37851 at tin-foil:  glasser | 2005-07-25 22:06:05 -0400
 Make 00.load.t reference the right package.
 r37852 at tin-foil:  glasser | 2005-07-25 23:44:17 -0400
 Big refactoring/cleanup:
   * Todo moved into the Data::ICal::Entry directory (which was sitting there
     empty just waiting for some love).  (This breaks backwards compatibility.)
   * Data::ICal (the entire calendar) made a subclass of Data::ICal::Entry, which
     allows a lot of repeated code to be deleted (and will make us happy once we
     want to allows alarms to be nested in todos/events), although it's a little
     strange naming-wise
   * be very explicit about which routines that return strings return trailing
     newlines
   * lots more pod (now passing pod coverage), and a more explicit test
   * Change version from 0.001 to 0.01.
 
 I am tempted to s/Entry/Component/ to be consistent with the standard, although
 Entry is a much friendlier name.
 r37853 at tin-foil:  glasser | 2005-07-26 00:03:15 -0400
 Add TODO test cases for the fact that we allow repetitions of required-unique
 properties, and the fact that we don't wrap text at 75 characters
 r37854 at tin-foil:  glasser | 2005-07-26 00:10:09 -0400
 Line folding implemented.
 r37855 at tin-foil:  glasser | 2005-07-26 00:26:30 -0400
 Honor *_unique_properties
 r37856 at tin-foil:  glasser | 2005-07-26 00:30:40 -0400
 Make sure that the overriding thing happens even if you use different cases for the keys.
 r37857 at tin-foil:  glasser | 2005-07-26 00:48:06 -0400
 Add Event class.
 
 Also document the relied-on-by-tests fact that add_event returns true (presumably
 later we'll add a check to make sure you're only adding things to VCALENDARs, except
 for VALARMs which can be added only to VEVENTs and VTODOs)
 r37858 at tin-foil:  glasser | 2005-07-26 01:16:43 -0400
 Add is_property, is_unique, is_mandatory, is_repeatable, and is_optional.
 
 Make add_property warn on unknown properties (and test this).
 
 Add property lists to the main calendar class.
 r37859 at tin-foil:  glasser | 2005-07-26 01:27:07 -0400
 Entry's add_string warns if you're missing any mandatory properties.
 r37860 at tin-foil:  glasser | 2005-07-26 01:34:04 -0400
 Update POD to reflect improvements.
 r37863 at tin-foil:  glasser | 2005-07-26 11:43:24 -0400
 Properties can have parameters.
 
 Allow X- properties
 r37864 at tin-foil:  glasser | 2005-07-26 11:49:01 -0400
 perltidy a la jesse
 r37865 at tin-foil:  glasser | 2005-07-26 13:23:50 -0400
 FreeBusy, Journal, and TimeZones added
 r37866 at tin-foil:  glasser | 2005-07-26 13:26:58 -0400
 Oops, X-arguments snuck into some of the property lists.  (and another perltidy run)
 r37867 at tin-foil:  glasser | 2005-07-26 13:57:50 -0400
 Add alarms.
 r37868 at tin-foil:  glasser | 2005-07-26 14:05:25 -0400
 The BNF names of properties were not always exactly the same as the actual names.  Eit.
 r37869 at tin-foil:  glasser | 2005-07-26 14:15:27 -0400
 README.


Modified: Data-ICal/Makefile.PL
==============================================================================
--- Data-ICal/Makefile.PL	(original)
+++ Data-ICal/Makefile.PL	Tue Jul 26 22:53:59 2005
@@ -6,5 +6,6 @@
 abstract_from('lib/Data/ICal.pm');
 license('perl');
 requires('Test::More');
+requires('Class::Accessor');
 
 &WriteAll;

Modified: Data-ICal/README
==============================================================================
--- Data-ICal/README	(original)
+++ Data-ICal/README	Tue Jul 26 22:53:59 2005
@@ -1,18 +1,7 @@
-Data-ICal- version 0.0.1
+Data-ICal version 0.0.1
 
-[ REPLACE THIS...
-
-  The README is used to introduce the module and provide instructions on
-  how to install the module, any machine dependencies it may have (for
-  example C compilers and installed libraries) and any other information
-  that should be understood before the module is installed.
-
-  A README file is required for CPAN modules since CPAN extracts the
-  README file from a module distribution so that people browsing the
-  archive can use it get an idea of the modules uses. It is usually a
-  good idea to provide version information here so that people can
-  decide whether fixes for the module are worth downloading.
-]
+Data::ICal is a straightforward generator for iCalendar (RFC 2445) files, such
+as those used in Apple's iCal.
 
 
 INSTALLATION

Modified: Data-ICal/lib/Data/ICal.pm
==============================================================================
--- Data-ICal/lib/Data/ICal.pm	(original)
+++ Data-ICal/lib/Data/ICal.pm	Tue Jul 26 22:53:59 2005
@@ -2,187 +2,153 @@
 use strict;
 
 package Data::ICal;
-
-our $VERSION = '0.001';
+use base qw/Data::ICal::Entry/;
+our $VERSION = '0.01';
 
 use Carp;
 
-# Module implementation here
+=head1 NAME
 
+Data::ICal - Generates iCalendar (RFC 2445) calendar files
 
-sub new {
-    my $class = shift;
-    my $self = {};
-    bless $self, $class;
-    return $self;
-}
 
+=head1 SYNOPSIS
 
+    use Data::ICal;
 
-=head2 add_entry
+    my $calendar = Data::ICal->new();
 
-=cut
+    my $vtodo = Data::ICal::Entry::Todo->new();
+    $vtodo->add_properties(
+	# ... see Data::ICal::Entry::Todo documentation
+    );
 
-sub add_entry {
-    my $self = shift;
-    my $entry = shift;
-    push @{$self->{entries}}, $entry;
-}
+    $calendar->add_entry($vtodo);
 
-=head2 entries
+    print $calendar->as_string;
+  
+=head1 DESCRIPTION
 
-Returns a ref to the array of entries in this Data::ICal object
+A L<Data::ICal> object represents a C<VCALENDAR> object as defined in the
+iCalendar protocol (RFC 2445, MIME type "text/calendar"), as implemented in many
+popular calendaring programs such as Apple's iCal.  L<Data::ICal> only provides
+the ability to generate ICal files, not to parse them.
+
+Each L<Data::ICal> object is a collection of "entries", which are objects of a
+subclass of L<Data::ICal::Entry>.  The types of entries defined by iCalendar
+(which refers to them as "components") include events, to-do items, journal
+entries, free/busy time indicators, and time zone descriptors; in addition,
+events and to-do items can contain alarm entries.  (Currently, L<Data::ICal>
+only implements to-do items and events.)
 
-=cut
+L<Data::ICal> is a subclass of L<Data::ICal::Entry>; see its manpage for more
+methods applicable to L<Data::ICal>.
 
-sub entries {
-    my $self = shift;
-    return $self->{'entries'} || [];
-}
+=head1 METHODS
 
+=cut
 
-=head2 as_string
+=head2 new
 
+Creates a new L<Data::ICal> object; sets its C<VERSION> and C<PRODID> properties
+to "2.0" and the value of the C<product_id> method respectively.
 
 =cut
 
+sub new {
+    my $class = shift;
+    my $self  = $class->SUPER::new(@_);
+    $self->add_properties(
+        version => '2.0',
+        prodid  => $self->product_id,
+    );
+    return $self;
+}
 
-sub as_string {
-    my $self = shift;
-  
-    my $content;
-    $content = $self->header();
-
+=head2 ical_entry_type
 
-    foreach my $entry (@{$self->entries}) { 
-        $content .= $entry->as_string();
-    }
+Returns C<VCALENDAR>, its iCalendar entry name.
 
-    $content .= $self->footer() ."\n";
-    return($content);
-}
+=cut
 
+sub ical_entry_type {'VCALENDAR'}
 
-sub header {
-    my $self = shift;
-    return join("\n", "BEGIN:VCALENDAR", "VERSION:2.0", "PRODID:".$self->product_id) ."\n";
+=head2 product_id
 
-}
+Returns the product ID used in the calendar's C<PRODID> property; you may
+wish to override this in a subclass for your own application.
 
-sub footer {
-    my $self = shift;
-    return "END:VCALENDAR";
-}
+=cut
 
 sub product_id {
     my $self = shift;
     return "Data::ICal $VERSION";
 }
 
+=head2 mandatory_unique_properties
 
+According to the iCalendar standard, the following properties must be specified
+exactly one time for a calendar:
 
-1; # Magic true value required at end of module
-__END__
-
-=head1 NAME
-
-Data::ICal - [One line description of module's purpose here]
-
-
-=head1 SYNOPSIS
-
-    use Data::ICal;
-
-=for author to fill in:
-    Brief code example(s) here showing commonest usage(s).
-    This section will be as far as many users bother reading
-    so make it as educational and exeplary as possible.
-  
-  
-=head1 DESCRIPTION
-
-=for author to fill in:
-    Write a full description of the module and its features here.
-    Use subsections (=head2, =head3) as appropriate.
-
-
-=head1 INTERFACE 
-
-=for author to fill in:
-    Write a separate section listing the public components of the modules
-    interface. These normally consist of either subroutines that may be
-    exported, or methods that may be called on objects belonging to the
-    classes provided by the module.
-
-
-=head1 DIAGNOSTICS
-
-=for author to fill in:
-    List every single error and warning message that the module can
-    generate (even the ones that will "never happen"), with a full
-    explanation of each problem, one or more likely causes, and any
-    suggested remedies.
+      prodid version
 
-=over
-
-=item C<< Error message here, perhaps with %s placeholders >>
+=cut
 
-[Description of error here]
+sub mandatory_unique_properties {
+    qw(
+        prodid version
+    );
+}
 
-=item C<< Another error message here >>
+=head2 optional_unique_properties
 
-[Description of error here]
+According to the iCalendar standard, the following properties may be specified
+at most one time for a calendar:
 
-[Et cetera, et cetera]
+      calscale method
 
-=back
+=cut
 
+sub optional_unique_properties {
+    qw(
+        calscale method
+    );
+}
 
 =head1 CONFIGURATION AND ENVIRONMENT
 
-=for author to fill in:
-    A full explanation of any configuration system(s) used by the
-    module, including the names and locations of any configuration
-    files, and the meaning of any environment variables or properties
-    that can be set. These descriptions must also include details of any
-    configuration language used.
-  
-Data::ICal requires no configuration files or environment variables.
+L<Data::ICal> requires no configuration files or environment variables.
 
 
 =head1 DEPENDENCIES
 
-=for author to fill in:
-    A list of all the other modules that this module relies upon,
-    including any restrictions on versions, and an indication whether
-    the module is part of the standard Perl distribution, part of the
-    module's distribution, or must be installed separately. ]
-
-None.
+L<Data::ICal> requires L<Class::Accessor>.
 
 
 =head1 INCOMPATIBILITIES
 
-=for author to fill in:
-    A list of any modules that this module cannot be used in conjunction
-    with. This may be due to name conflicts in the interface, or
-    competition for system or program resources, or due to internal
-    limitations of Perl (for example, many modules that use source code
-    filters are mutually incompatible).
-
 None reported.
 
 
 =head1 BUGS AND LIMITATIONS
 
-=for author to fill in:
-    A list of known problems with the module, together with some
-    indication Whether they are likely to be fixed in an upcoming
-    release. Also a list of restrictions on the features the module
-    does provide: data types that cannot be handled, performance issues
-    and the circumstances in which they may arise, practical
-    limitations on the size of data sets, special cases that are not
-    (yet) handled, etc.
+L<Data::ICal> does not support time zone daylight or standard entries, so time zone
+components are basically useless.
+
+While L<Data::ICal> tries to check which properties are required and repeatable, this
+only works in simple cases; it does not check for properties that must either both exist
+or both not exist, or for mutually exclusive properties.
+
+L<Data::ICal> does not check to see if property parameter names are known in
+general or allowed on the particular property.
+
+L<Data::ICal> does not check to see if nested entries are nested properly (alarms in
+todos and events only, everything else in calendars only).
+
+L<Data::ICal> has no automatic support for converting binary data to the appropriate
+encoding and setting the corresponding parameters.
+
+There is no L<Data::ICal::Entry::Alarm> base class.
 
 No bugs have been reported.
 
@@ -226,3 +192,7 @@
 FAILURE OF THE SOFTWARE TO OPERATE WITH ANY OTHER SOFTWARE), EVEN IF
 SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF
 SUCH DAMAGES.
+
+=cut
+
+1;

Modified: Data-ICal/lib/Data/ICal/Entry.pm
==============================================================================
--- Data-ICal/lib/Data/ICal/Entry.pm	(original)
+++ Data-ICal/lib/Data/ICal/Entry.pm	Tue Jul 26 22:53:59 2005
@@ -3,71 +3,389 @@
 
 package Data::ICal::Entry;
 use Data::ICal::Property;
+use Carp;
 
+=head1 NAME
+
+Data::ICal::Entry - Represents an entry in an iCalendar file
+
+
+=head1 SYNOPSIS
+
+    my $vtodo = Data::ICal::Entry::Todo->new();
+    $vtodo->add_properties(
+	# ... see Data::ICal::Entry::Todo documentation
+    );
+
+    $calendar->add_entry($vtodo);
+
+    $event->add_entry($alarm); 
+  
+=head1 DESCRIPTION
+
+A L<Data::ICal::Entry> object represents a single entry in an iCalendar file.
+(Note that the iCalendar RFC refers to entries as "components".)  iCalendar
+defines several types of entries, such as events and to-do lists; each of
+these corresponds to a subclass of L<Data::ICal::Entry> (though only to-do
+lists and events are currently implemented).  L<Data::ICal::Entry> should be treated
+as an abstract base class -- all objects created should be of its subclasses.
+The entire calendar itself (the L<Data::ICal> object) is also represented
+as a L<Data::ICal::Entry> object.
+
+Each entry has an entry type (such as C<VCALENDAR> or C<VEVENT>), a series
+of "properties", and possibly some sub-entries.  (Only the root L<Data::ICal>
+object can have sub-entries, except for alarm entries contained in events and
+to-dos (not yet implemented).)
+
+=head1 METHODS
+
+=cut
+
+=head2 new
+
+Creates a new entry object with no properties or sub-entries.
+
+=cut
 
 sub new {
     my $class = shift;
-    my $self = {};
+    my $self  = {
+        properties => {},
+        entries    => [],
+    };
     bless $self, $class;
     return $self;
 }
 
+=head2 as_string
+
+Returns the entry as an appropriately formatted string (with trailing newline).
+
+Properties are returned in alphabetical order, with multiple properties of the same name
+returned in the order added.  (Property order is unimportant
+in iCalendar, and this makes testing easier.)
 
+If any mandatory property is missing, issues a warning.
+
+=cut
 
 sub as_string {
     my $self = shift;
 
-    return join( "\n",
-        $self->header, ( map { $_->as_string } @{ $self->properties } ),
-        $self->footer )
-      . "\n";
+    my $output = $self->header;
+
+    for my $name (
+        $self->mandatory_unique_properties,
+        $self->mandatory_repeatable_properties
+        )
+    {
+        carp "Mandatory property for " . ( ref $self ) . " missing: $name"
+            unless $self->properties->{$name}
+            and @{ $self->properties->{$name} };
+    }
+
+    for my $name ( sort keys %{ $self->properties } ) {
+        $output .= $_
+            for map { $_->as_string } @{ $self->properties->{$name} };
+    }
+
+    for my $entry ( @{ $self->entries } ) {
+        $output .= $entry->as_string;
+    }
+    $output .= $self->footer;
 
+    return $output;
 }
 
+=head2 add_entry $entry
+
+Adds an entry to this entry.  (According to the standard, this should only be called
+on either a to-do or event entry with an alarm entry, or on a calendar entry (L<Data::ICal>)
+with a to-do, event, journal, timezone, or free/busy entry.)
+
+Returns true if the entry was successfully added, and false otherwise (perhaps because you
+tried to add an entry of an invalid type, but this check hasn't been implemented yet). 
+
+=cut
+
+sub add_entry {
+    my $self  = shift;
+    my $entry = shift;
+    push @{ $self->{entries} }, $entry;
+
+    return 1;
+}
+
+=head2 entries
+
+Returns a reference to the array of subentries of this entry.
+
+=cut
+
+sub entries {
+    my $self = shift;
+    return $self->{'entries'};
+}
+
+=head2 properties
+
+Returns a reference to the hash of properties of this entry.  The keys are property names and
+the values are array references containing L<Data::ICal::Property> objects.
+
+=cut
+
 sub properties {
     my $self = shift;
-    return $self->{'properties'} || [];
+    return $self->{'properties'};
 }
 
+=head2 add_property $propname => $propval
+
+Creates a new L<Data::ICal::Property> object with name C<$propname> and value
+C<$propval> and adds it to the event.
+
+If the property is not known to exist for that object type and does not begin
+with C<X->, issues a warning.
+
+If the property is known to be unique, replaces the original property.
+
+To specify parameters for the property, let C<$propval> be a two-element array reference
+where the first element is the property value and the second element is a hash reference.
+The keys of the hash are parameter names; the values should be either strings or array references
+of strings, depending on whether the parameter should have one or multiple (to be comma-separated)
+values.
+
+=cut
+
 sub add_property {
     my $self = shift;
-    my $prop = shift;
-    my $val = shift;
-    push @{$self->{'properties'}}, Data::ICal::Property->new($prop => $val);
+    my $prop = lc shift;
+    my $val  = shift;
+
+    unless ( $self->is_property($prop) or $prop =~ /^x-/i ) {
+        carp "Unknown property for " . ( ref $self ) . ": $prop";
+    }
+
+    if ( $self->is_unique($prop) ) {
+
+        # It should be unique, so clear out anything we might have first
+        $self->properties->{$prop} = [];
+    }
+
+    $val = [ $val, {} ] unless ref $val eq 'ARRAY';
+
+    my ( $prop_value, $param_hash ) = @$val;
+
+    push @{ $self->properties->{$prop} },
+        Data::ICal::Property->new( $prop => $prop_value, $param_hash );
 }
 
+=head2 add_properties $propname1 => $propval1, [$propname2 => $propname2, ...]
+
+Convenience function to call C<add_property> several times with a list of properties.
+
+This method is guaranteed to call add C<add_property> on them in the order given, so that
+unique properties given later in the call will take precedence over those given earlier.
+(This is unrelated to the order of properties when the entry is rendered as a string, though.)
+
+Parameters for the properties are specified in the same way as in C<add_property>.
+
+=cut
+
 sub add_properties {
     my $self = shift;
-    my %args = @_;
-    while (my ( $prop, $val) = each %args) {
-        $self->add_property( $prop => $val);
+
+    if ( @_ % 2 ) {
+        carp "Odd number of elements in add_properties call";
+        return;
+    }
+
+    while (@_) {
+        my $prop = shift;
+        my $val  = shift;
+        $self->add_property( $prop => $val );
     }
 }
 
+=head2 mandatory_unique_properties 
+
+Subclasses should override this method (which returns an empty list by default)
+to provide a list of lower case strings identifying the properties which must appear exactly
+once in the subclass's entry type.
+
+=cut
+
+sub mandatory_unique_properties { () }
+
+=head2 mandatory_repeatable_properties 
+
+Subclasses should override this method (which returns an empty list by default)
+to provide a list of lower case strings identifying the properties which must appear at least
+once in the subclass's entry type.
+
+=cut
+
+sub mandatory_repeatable_properties { () }
+
+=head2 optional_unique_properties 
+
+Subclasses should override this method (which returns an empty list by default)
+to provide a list of lower case strings identifying the properties which must appear at most
+once in the subclass's entry type.
+
+=cut
+
+sub optional_unique_properties { () }
+
+=head2 optional_repeatable_properties
+
+Subclasses should override this method (which returns an empty list by default)
+to provide a list of lower case strings identifying the properties which may appear zero,
+one, or more times in the subclass's entry type.
+
+=cut
 
-sub mandatory_unique_properties {
+sub optional_repeatable_properties { () }
+
+=head2 is_property $name
+
+Returns a boolean value indicating whether or not the property C<$name> is known to the class
+(that is, if it's listed in C<(mandatory/optional)_(unique/repeatable)_properties>).
+
+=cut
+
+sub is_property {
+    my $self = shift;
+    my $name = shift;
+    return scalar grep { $_ eq $name } $self->mandatory_unique_properties,
+        $self->mandatory_repeatable_properties,
+        $self->optional_unique_properties,
+        $self->optional_repeatable_properties;
 }
 
-sub mandatory_repeatable_properties {
+=head2 is_mandatory $name
+
+Returns a boolean value indicating whether or not the property C<$name> is known to the class as mandatory
+(that is, if it's listed in C<mandatory_(unique/repeatable)_properties>).
+
+=cut
+
+sub is_mandatory {
+    my $self = shift;
+    my $name = shift;
+    return scalar grep { $_ eq $name } $self->mandatory_unique_properties,
+        $self->mandatory_repeatable_properties;
 }
 
-sub optional_unique_properties {
+=head2 is_optional $name
+
+Returns a boolean value indicating whether or not the property C<$name> is known to the class as optional
+(that is, if it's listed in C<optional_(unique/repeatable)_properties>).
+
+=cut
+
+sub is_optional {
+    my $self = shift;
+    my $name = shift;
+    return scalar grep { $_ eq $name } $self->optional_unique_properties,
+        $self->optional_repeatable_properties;
 }
 
-sub optional_repeatable_properties {
+=head2 is_unique $name
+
+Returns a boolean value indicating whether or not the property C<$name> is known to the class as unique
+(that is, if it's listed in C<(mandatory/optional)_unique_properties>).
+
+=cut
+
+sub is_unique {
+    my $self = shift;
+    my $name = shift;
+    return scalar grep { $_ eq $name } $self->mandatory_unique_properties,
+        $self->optional_unique_properties;
 }
 
-sub ical_entry_type {
-    return 'UNDEFINED';
+=head2 is_repeatable $name
+
+Returns a boolean value indicating whether or not the property C<$name> is known to the class as repeatable
+(that is, if it's listed in C<(mandatory/optional)_repeatable_properties>).
+
+=cut
+
+sub is_repeatable {
+    my $self = shift;
+    my $name = shift;
+    return scalar grep { $_ eq $name } $self->mandatory_repeatable_properties,
+        $self->optional_repeatable_properties;
 }
 
+=head2 ical_entry_type
+
+Subclasses should override this method to provide the identifying type name of the entry
+(such as C<VCALENDAR> or C<VTODO>).
+
+=cut
+
+sub ical_entry_type {'UNDEFINED'}
+
+=head2 header
+
+Returns the header line for the entry (including trailing newline).
+
+=cut
+
 sub header {
     my $self = shift;
-    return 'BEGIN:'.$self->ical_entry_type;
+    return 'BEGIN:' . $self->ical_entry_type . "\n";
 }
 
+=head2 footer
+
+Returns the footer line for the entry (including trailing newline).
+
+=cut
+
 sub footer {
     my $self = shift;
-    return 'END:'.$self->ical_entry_type;
+    return 'END:' . $self->ical_entry_type . "\n";
 }
+
+=head1 AUTHOR
+
+Jesse Vincent  C<< <jesse at bestpractical.com> >>
+
+
+=head1 LICENCE AND COPYRIGHT
+
+Copyright (c) 2005, Best Practical Solutions, LLC.  All rights reserved.
+
+This module is free software; you can redistribute it and/or
+modify it under the same terms as Perl itself. See L<perlartistic>.
+
+
+=head1 DISCLAIMER OF WARRANTY
+
+BECAUSE THIS SOFTWARE IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY
+FOR THE SOFTWARE, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN
+OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES
+PROVIDE THE SOFTWARE "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER
+EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE
+ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE SOFTWARE IS WITH
+YOU. SHOULD THE SOFTWARE PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL
+NECESSARY SERVICING, REPAIR, OR CORRECTION.
+
+IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
+WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR
+REDISTRIBUTE THE SOFTWARE AS PERMITTED BY THE ABOVE LICENCE, BE
+LIABLE TO YOU FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL,
+OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE
+THE SOFTWARE (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING
+RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A
+FAILURE OF THE SOFTWARE TO OPERATE WITH ANY OTHER SOFTWARE), EVEN IF
+SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF
+SUCH DAMAGES.
+
+=cut
+
 1;

Added: Data-ICal/lib/Data/ICal/Entry/Alarm/Audio.pm
==============================================================================
--- (empty file)
+++ Data-ICal/lib/Data/ICal/Entry/Alarm/Audio.pm	Tue Jul 26 22:53:59 2005
@@ -0,0 +1,127 @@
+use warnings;
+use strict;
+
+package Data::ICal::Entry::Alarm::Audio;
+
+use base qw/Data::ICal::Entry/;
+
+=head1 NAME
+
+Data::ICal::Entry::Alarm::Audio - Represents an audio alarm in an iCalendar file
+
+
+=head1 SYNOPSIS
+
+    my $valarm = Data::ICal::Entry::Alarm::Audio->new();
+    $valarm->add_properties(
+        attach => [ "ftp://host.com/pub/sounds/bell-01.aud", { fmttype => "audio/basic" } ],
+	# Dat*e*::ICal is not a typo here
+        trigger   => [ Date::ICal->new( epoch => ... )->ical, { value => 'DATE-TIME' } ],
+    );
+
+    $vevent->add_entry($valarm);
+  
+=head1 DESCRIPTION
+
+A L<Data::ICal::Entry::Alarm::Audio> object represents an audio alarm attached
+to a todo item or event in an iCalendar file.  (Note that the iCalendar RFC
+refers to entries as "components".)  It is a subclass of L<Data::ICal::Entry>
+and accepts all of its methods.
+
+=head1 METHODS
+
+=cut
+
+=head2 new
+
+Creates a new L<Data::ICal::Entry::Alarm::Audio> object; sets its C<ACTION> property
+to C<AUDIO>.
+
+=cut
+
+sub new {
+    my $class = shift;
+    my $self  = $class->SUPER::new(@_);
+    $self->add_property( action => "AUDIO" );
+    return $self;
+}
+
+=head2 ical_entry_type
+
+Returns C<VALARM>, its iCalendar entry name.
+
+=cut
+
+sub ical_entry_type {'VALARM'}
+
+=head2 optional_unique_properties
+
+According to the iCalendar standard, the following properties may be specified
+at most one time for an audio alarm:
+
+	duration repeat attach
+
+Note that if one of C<duration> or C<repeat> is specified, the other one must be also,
+though this module does not enforce that restriction.
+
+=cut
+
+sub optional_unique_properties {
+    qw(
+        duration repeat attach
+    );
+}
+
+=head2 mandatory_unique_properties
+
+According to the iCalendar standard, the C<trigger> property must be specified
+exactly once for an audio alarm.  (In addition, the C<action> property must be
+specified exactly once, but the module automatically sets it for you.)
+
+=cut
+
+sub mandatory_unique_properties {
+    qw(
+        action trigger
+    );
+}
+
+=head1 AUTHOR
+
+Jesse Vincent  C<< <jesse at bestpractical.com> >>
+
+
+=head1 LICENCE AND COPYRIGHT
+
+Copyright (c) 2005, Best Practical Solutions, LLC.  All rights reserved.
+
+This module is free software; you can redistribute it and/or
+modify it under the same terms as Perl itself. See L<perlartistic>.
+
+
+=head1 DISCLAIMER OF WARRANTY
+
+BECAUSE THIS SOFTWARE IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY
+FOR THE SOFTWARE, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN
+OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES
+PROVIDE THE SOFTWARE "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER
+EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE
+ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE SOFTWARE IS WITH
+YOU. SHOULD THE SOFTWARE PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL
+NECESSARY SERVICING, REPAIR, OR CORRECTION.
+
+IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
+WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR
+REDISTRIBUTE THE SOFTWARE AS PERMITTED BY THE ABOVE LICENCE, BE
+LIABLE TO YOU FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL,
+OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE
+THE SOFTWARE (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING
+RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A
+FAILURE OF THE SOFTWARE TO OPERATE WITH ANY OTHER SOFTWARE), EVEN IF
+SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF
+SUCH DAMAGES.
+
+=cut
+
+1;

Added: Data-ICal/lib/Data/ICal/Entry/Alarm/Display.pm
==============================================================================
--- (empty file)
+++ Data-ICal/lib/Data/ICal/Entry/Alarm/Display.pm	Tue Jul 26 22:53:59 2005
@@ -0,0 +1,124 @@
+use warnings;
+use strict;
+
+package Data::ICal::Entry::Alarm::Display;
+
+use base qw/Data::ICal::Entry/;
+
+=head1 NAME
+
+Data::ICal::Entry::Alarm::Display - Represents a displayed alarm in an iCalendar file
+
+
+=head1 SYNOPSIS
+
+    my $valarm = Data::ICal::Entry::Alarm::Display->new();
+    $valarm->add_properties(
+        description => "Wake up!",
+	# Dat*e*::ICal is not a typo here
+        trigger   => [ Date::ICal->new( epoch => ... )->ical, { value => 'DATE-TIME' } ],
+    );
+
+    $vevent->add_entry($valarm);
+  
+=head1 DESCRIPTION
+
+A L<Data::ICal::Entry::Alarm::Display> object represents a alarm that displays a
+message which is attached to a todo item or event in an iCalendar file.  (Note
+that the iCalendar RFC refers to entries as "components".)  It is a subclass of
+L<Data::ICal::Entry> and accepts all of its methods.
+
+=head1 METHODS
+
+=cut
+
+=head2 new
+
+Creates a new L<Data::ICal::Entry::Alarm::Display> object; sets its C<ACTION> property
+to C<DISPLAY>.
+
+=cut
+
+sub new {
+    my $class = shift;
+    my $self  = $class->SUPER::new(@_);
+    $self->add_property( action => "DISPLAY" );
+    return $self;
+}
+
+=head2 ical_entry_type
+
+Returns C<VALARM>, its iCalendar entry name.
+
+=cut
+
+sub ical_entry_type {'VALARM'}
+
+=head2 optional_unique_properties
+
+According to the iCalendar standard, the C<duration> and C<retreat> properties may be specified
+at most one time for an displayed alarm, and if one is specified, the other one must be also,
+though this module does not enforce that restriction.
+
+=cut
+
+sub optional_unique_properties {
+    qw(
+        duration repeat
+    );
+}
+
+=head2 mandatory_unique_properties
+
+According to the iCalendar standard, the C<trigger> and C<description>
+properties must be specified exactly once for an displayed alarm.  (In addition, the
+C<action> property must be specified exactly once, but the module automatically
+sets it for you.)
+
+=cut
+
+sub mandatory_unique_properties {
+    qw(
+        action trigger description
+    );
+}
+
+=head1 AUTHOR
+
+Jesse Vincent  C<< <jesse at bestpractical.com> >>
+
+
+=head1 LICENCE AND COPYRIGHT
+
+Copyright (c) 2005, Best Practical Solutions, LLC.  All rights reserved.
+
+This module is free software; you can redistribute it and/or
+modify it under the same terms as Perl itself. See L<perlartistic>.
+
+
+=head1 DISCLAIMER OF WARRANTY
+
+BECAUSE THIS SOFTWARE IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY
+FOR THE SOFTWARE, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN
+OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES
+PROVIDE THE SOFTWARE "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER
+EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE
+ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE SOFTWARE IS WITH
+YOU. SHOULD THE SOFTWARE PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL
+NECESSARY SERVICING, REPAIR, OR CORRECTION.
+
+IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
+WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR
+REDISTRIBUTE THE SOFTWARE AS PERMITTED BY THE ABOVE LICENCE, BE
+LIABLE TO YOU FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL,
+OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE
+THE SOFTWARE (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING
+RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A
+FAILURE OF THE SOFTWARE TO OPERATE WITH ANY OTHER SOFTWARE), EVEN IF
+SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF
+SUCH DAMAGES.
+
+=cut
+
+1;

Added: Data-ICal/lib/Data/ICal/Entry/Alarm/Email.pm
==============================================================================
--- (empty file)
+++ Data-ICal/lib/Data/ICal/Entry/Alarm/Email.pm	Tue Jul 26 22:53:59 2005
@@ -0,0 +1,156 @@
+use warnings;
+use strict;
+
+package Data::ICal::Entry::Alarm::Email;
+
+use base qw/Data::ICal::Entry/;
+
+=head1 NAME
+
+Data::ICal::Entry::Alarm::Email - Represents an emailed alarm in an iCalendar file
+
+
+=head1 SYNOPSIS
+
+    my $valarm = Data::ICal::Entry::Alarm::Audio->new();
+    $valarm->add_properties(
+        attach => [ "basic:ftp://host.com/pub/sounds/bell-01.aud", { fmttype => "audio/basic" } ],
+	# Dat*e*::ICal is not a typo here
+        trigger   => [ Date::ICal->new( epoch => ... )->ical, { value => 'DATE-TIME' } ],
+    );
+
+    $vevent->add_entry($valarm);
+  
+=head1 DESCRIPTION
+
+A L<Data::ICal::Entry::Alarm::Email> object represents an emailed alarm attached
+to a todo item or event in an iCalendar file.  (Note that the iCalendar RFC
+refers to entries as "components".)  It is a subclass of L<Data::ICal::Entry>
+and accepts all of its methods.
+
+The C<attendee> properties are intended as the recipient list of the email; the C<summary>
+as its subject; the C<description> as its body; and the C<attach>  as its attachments.
+
+=head1 METHODS
+
+=cut
+
+=head2 new
+
+Creates a new L<Data::ICal::Entry::Alarm::Email> object; sets its C<ACTION> property
+to C<EMAIL>.
+
+=cut
+
+sub new {
+    my $class = shift;
+    my $self  = $class->SUPER::new(@_);
+    $self->add_property( action => "EMAIL" );
+    return $self;
+}
+
+=head2 ical_entry_type
+
+Returns C<VALARM>, its iCalendar entry name.
+
+=cut
+
+sub ical_entry_type {'VALARM'}
+
+=head2 optional_unique_properties
+
+According to the iCalendar standard, the C<duration> and C<retreat> properties may be specified
+at most one time for an emailed alarm, and if one is specified, the other one must be also,
+though this module does not enforce that restriction.
+
+=cut
+
+sub optional_unique_properties {
+    qw(
+        duration repeat
+    );
+}
+
+=head2 mandatory_unique_properties
+
+According to the iCalendar standard, the following properties must be specified
+exactly once for an emailed alarm:
+
+  description summary trigger
+
+In addition, the C<action> property must be specified exactly once, but the
+module automatically sets it for you.
+
+=cut
+
+sub mandatory_unique_properties {
+    qw(
+        action description summary trigger
+    );
+}
+
+=head2 mandatory_repeatable_properties
+
+According to the iCalendar standard, the C<attendee> property must be specified
+at least once for an emailed alarm.
+
+=cut
+
+sub mandatory_repeatable_properties {
+    qw(
+        attendee
+    );
+}
+
+=head2 optional_repeatable_properties
+
+According to the iCalendar standard, the C<attach> property may be specified
+any number of times for an emailed alarm.
+
+=cut
+
+sub optional_repeatable_properties {
+    qw(
+        attach
+    );
+}
+
+=head1 AUTHOR
+
+Jesse Vincent  C<< <jesse at bestpractical.com> >>
+
+
+=head1 LICENCE AND COPYRIGHT
+
+Copyright (c) 2005, Best Practical Solutions, LLC.  All rights reserved.
+
+This module is free software; you can redistribute it and/or
+modify it under the same terms as Perl itself. See L<perlartistic>.
+
+
+=head1 DISCLAIMER OF WARRANTY
+
+BECAUSE THIS SOFTWARE IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY
+FOR THE SOFTWARE, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN
+OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES
+PROVIDE THE SOFTWARE "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER
+EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE
+ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE SOFTWARE IS WITH
+YOU. SHOULD THE SOFTWARE PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL
+NECESSARY SERVICING, REPAIR, OR CORRECTION.
+
+IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
+WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR
+REDISTRIBUTE THE SOFTWARE AS PERMITTED BY THE ABOVE LICENCE, BE
+LIABLE TO YOU FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL,
+OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE
+THE SOFTWARE (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING
+RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A
+FAILURE OF THE SOFTWARE TO OPERATE WITH ANY OTHER SOFTWARE), EVEN IF
+SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF
+SUCH DAMAGES.
+
+=cut
+
+1;

Added: Data-ICal/lib/Data/ICal/Entry/Alarm/Procedure.pm
==============================================================================
--- (empty file)
+++ Data-ICal/lib/Data/ICal/Entry/Alarm/Procedure.pm	Tue Jul 26 22:53:59 2005
@@ -0,0 +1,129 @@
+use warnings;
+use strict;
+
+package Data::ICal::Entry::Alarm::Procedure;
+
+use base qw/Data::ICal::Entry/;
+
+=head1 NAME
+
+Data::ICal::Entry::Alarm::Procedure - Represents a procedure-call alarm in an iCalendar file
+
+
+=head1 SYNOPSIS
+
+    my $valarm = Data::ICal::Entry::Alarm::Procedure->new();
+    $valarm->add_properties(
+        attach => [ "ftp://host.com/novo-procs/felizano.exe", { fmttype => "application/binary" } ],
+	# Dat*e*::ICal is not a typo here
+        trigger   => [ Date::ICal->new( epoch => ... )->ical, { value => 'DATE-TIME' } ],
+    );
+
+    $vevent->add_entry($valarm);
+  
+=head1 DESCRIPTION
+
+A L<Data::ICal::Entry::Alarm::Procedure> object represents an alarm that calls a
+procedure (in some application-defined way), which is attached to a todo item or
+event in an iCalendar file.  (Note that the iCalendar RFC refers to entries as
+"components".)  It is a subclass of L<Data::ICal::Entry> and accepts all of its
+methods.
+
+=head1 METHODS
+
+=cut
+
+=head2 new
+
+Creates a new L<Data::ICal::Entry::Alarm::Procedure> object; sets its C<ACTION> property
+to C<PROCEDURE>.
+
+=cut
+
+sub new {
+    my $class = shift;
+    my $self  = $class->SUPER::new(@_);
+    $self->add_property( action => "PROCEDURE" );
+    return $self;
+}
+
+=head2 ical_entry_type
+
+Returns C<VALARM>, its iCalendar entry name.
+
+=cut
+
+sub ical_entry_type {'VALARM'}
+
+=head2 optional_unique_properties
+
+According to the iCalendar standard, the following properties may be specified
+at most one time for a procedure-call alarm:
+
+	duration repeat description
+
+Note that if one of C<duration> or C<repeat> is specified, the other one must be also,
+though this module does not enforce that restriction.
+
+=cut
+
+sub optional_unique_properties {
+    qw(
+        duration repeat description
+    );
+}
+
+=head2 mandatory_unique_properties
+
+According to the iCalendar standard, the C<trigger> and C<attach> properties
+must be specified exactly once for a procedure-call alarm.  (In addition, the C<action>
+property must be specified exactly once, but the module automatically sets it
+for you.)
+
+=cut
+
+sub mandatory_unique_properties {
+    qw(
+        action trigger attach
+    );
+}
+
+=head1 AUTHOR
+
+Jesse Vincent  C<< <jesse at bestpractical.com> >>
+
+
+=head1 LICENCE AND COPYRIGHT
+
+Copyright (c) 2005, Best Practical Solutions, LLC.  All rights reserved.
+
+This module is free software; you can redistribute it and/or
+modify it under the same terms as Perl itself. See L<perlartistic>.
+
+
+=head1 DISCLAIMER OF WARRANTY
+
+BECAUSE THIS SOFTWARE IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY
+FOR THE SOFTWARE, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN
+OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES
+PROVIDE THE SOFTWARE "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER
+EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE
+ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE SOFTWARE IS WITH
+YOU. SHOULD THE SOFTWARE PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL
+NECESSARY SERVICING, REPAIR, OR CORRECTION.
+
+IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
+WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR
+REDISTRIBUTE THE SOFTWARE AS PERMITTED BY THE ABOVE LICENCE, BE
+LIABLE TO YOU FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL,
+OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE
+THE SOFTWARE (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING
+RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A
+FAILURE OF THE SOFTWARE TO OPERATE WITH ANY OTHER SOFTWARE), EVEN IF
+SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF
+SUCH DAMAGES.
+
+=cut
+
+1;

Added: Data-ICal/lib/Data/ICal/Entry/Event.pm
==============================================================================
--- (empty file)
+++ Data-ICal/lib/Data/ICal/Entry/Event.pm	Tue Jul 26 22:53:59 2005
@@ -0,0 +1,128 @@
+use warnings;
+use strict;
+
+package Data::ICal::Entry::Event;
+
+use base qw/Data::ICal::Entry/;
+
+=head1 NAME
+
+Data::ICal::Entry::Event - Represents an event in an iCalendar file
+
+
+=head1 SYNOPSIS
+
+    my $vevent = Data::ICal::Entry::Event->new();
+    $vevent->add_properties(
+        summary => "my party",
+        description => "I'll cry if I want to",
+	# Dat*e*::ICal is not a typo here
+        dtstart   => Date::ICal->new( epoch => time )->ical,
+    );
+
+    $calendar->add_entry($vevent);
+
+    $vevent->add_entry($alarm); 
+  
+=head1 DESCRIPTION
+
+A L<Data::ICal::Entry::Event> object represents a single event in an iCalendar file.
+(Note that the iCalendar RFC refers to entries as "components".)  It is a subclass
+of L<Data::ICal::Entry> and accepts all of its methods.
+
+=head1 METHODS
+
+=cut
+
+=head2 ical_entry_type
+
+Returns C<VEVENT>, its iCalendar entry name.
+
+=cut
+
+sub ical_entry_type {'VEVENT'}
+
+=head2 optional_unique_properties
+
+According to the iCalendar standard, the following properties may be specified
+at most one time for an event:
+
+	class  created  description  dtstart  geo 
+	last-modified  location  organizer  priority 
+	dtstamp  sequence  status  summary  transp 
+	uid  url  recurrence-id 
+
+In addition, C<dtend> and C<duration> may be specified at most once each, but not both
+in the same entry (though this restriction is not enforced).
+
+=cut
+
+sub optional_unique_properties {
+    qw(
+        class  created  description  dtstart  geo
+        last-modified  location  organizer  priority
+        dtstamp  sequence  status  summary  transp
+        uid  url  recurrence-id
+
+        dtend duration
+    );
+}
+
+=head2 optional_repeatable_properties
+
+According to the iCalendar standard, the following properties may be specified
+any number of times for an event:
+
+	attach  attendee  categories  comment 
+	contact  exdate  exrule  request-status  related-to 
+	resources  rdate  rrule  
+
+=cut
+
+sub optional_repeatable_properties {
+    qw(
+        attach  attendee  categories  comment
+        contact  exdate  exrule  request-status  related-to
+        resources  rdate  rrule
+    );
+}
+
+=head1 AUTHOR
+
+Jesse Vincent  C<< <jesse at bestpractical.com> >>
+
+
+=head1 LICENCE AND COPYRIGHT
+
+Copyright (c) 2005, Best Practical Solutions, LLC.  All rights reserved.
+
+This module is free software; you can redistribute it and/or
+modify it under the same terms as Perl itself. See L<perlartistic>.
+
+
+=head1 DISCLAIMER OF WARRANTY
+
+BECAUSE THIS SOFTWARE IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY
+FOR THE SOFTWARE, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN
+OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES
+PROVIDE THE SOFTWARE "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER
+EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE
+ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE SOFTWARE IS WITH
+YOU. SHOULD THE SOFTWARE PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL
+NECESSARY SERVICING, REPAIR, OR CORRECTION.
+
+IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
+WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR
+REDISTRIBUTE THE SOFTWARE AS PERMITTED BY THE ABOVE LICENCE, BE
+LIABLE TO YOU FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL,
+OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE
+THE SOFTWARE (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING
+RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A
+FAILURE OF THE SOFTWARE TO OPERATE WITH ANY OTHER SOFTWARE), EVEN IF
+SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF
+SUCH DAMAGES.
+
+=cut
+
+1;

Added: Data-ICal/lib/Data/ICal/Entry/FreeBusy.pm
==============================================================================
--- (empty file)
+++ Data-ICal/lib/Data/ICal/Entry/FreeBusy.pm	Tue Jul 26 22:53:59 2005
@@ -0,0 +1,113 @@
+use warnings;
+use strict;
+
+package Data::ICal::Entry::FreeBusy;
+
+use base qw/Data::ICal::Entry/;
+
+=head1 NAME
+
+Data::ICal::Entry::FreeBusy - Represents blocks of free and busy time in an iCalendar file
+
+
+=head1 SYNOPSIS
+
+    my $vfreebusy = Data::ICal::Entry::FreeBusy->new();
+    $vfreebusy->add_properties(
+        organizer => 'MAILTO:jsmith at host.com',
+	# Dat*e*::ICal is not a typo here
+        freebusy   => Date::ICal->new( epoch => ... )->ical . '/' . Date::ICal->new( epoch => ... )->ical,
+    );
+
+    $calendar->add_entry($vfreebusy);
+  
+=head1 DESCRIPTION
+
+A L<Data::ICal::Entry::FreeBusy> object represents a request for information about free and
+busy time or a reponse to such a request, in an iCalendar file.
+(Note that the iCalendar RFC refers to entries as "components".)  It is a subclass
+of L<Data::ICal::Entry> and accepts all of its methods.
+
+=head1 METHODS
+
+=cut
+
+=head2 ical_entry_type
+
+Returns C<VFREEBUSY>, its iCalendar entry name.
+
+=cut
+
+sub ical_entry_type {'VFREEBUSY'}
+
+=head2 optional_unique_properties
+
+According to the iCalendar standard, the following properties may be specified
+at most one time for a free/busy entry:
+
+	contact  dtstart  dtend  duration  dtstamp 
+	organizer  uid  url 
+
+=cut
+
+sub optional_unique_properties {
+    qw(
+        contact  dtstart  dtend  duration  dtstamp
+        organizer  uid  url
+    );
+}
+
+=head2 optional_repeatable_properties
+
+According to the iCalendar standard, the following properties may be specified
+any number of times for free/busy entry:
+
+        attendee comment freebusy request-status
+
+=cut
+
+sub optional_repeatable_properties {
+    qw(
+        attendee comment freebusy request-status
+    );
+}
+
+=head1 AUTHOR
+
+Jesse Vincent  C<< <jesse at bestpractical.com> >>
+
+
+=head1 LICENCE AND COPYRIGHT
+
+Copyright (c) 2005, Best Practical Solutions, LLC.  All rights reserved.
+
+This module is free software; you can redistribute it and/or
+modify it under the same terms as Perl itself. See L<perlartistic>.
+
+
+=head1 DISCLAIMER OF WARRANTY
+
+BECAUSE THIS SOFTWARE IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY
+FOR THE SOFTWARE, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN
+OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES
+PROVIDE THE SOFTWARE "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER
+EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE
+ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE SOFTWARE IS WITH
+YOU. SHOULD THE SOFTWARE PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL
+NECESSARY SERVICING, REPAIR, OR CORRECTION.
+
+IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
+WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR
+REDISTRIBUTE THE SOFTWARE AS PERMITTED BY THE ABOVE LICENCE, BE
+LIABLE TO YOU FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL,
+OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE
+THE SOFTWARE (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING
+RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A
+FAILURE OF THE SOFTWARE TO OPERATE WITH ANY OTHER SOFTWARE), EVEN IF
+SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF
+SUCH DAMAGES.
+
+=cut
+
+1;

Added: Data-ICal/lib/Data/ICal/Entry/Journal.pm
==============================================================================
--- (empty file)
+++ Data-ICal/lib/Data/ICal/Entry/Journal.pm	Tue Jul 26 22:53:59 2005
@@ -0,0 +1,119 @@
+use warnings;
+use strict;
+
+package Data::ICal::Entry::Journal;
+
+use base qw/Data::ICal::Entry/;
+
+=head1 NAME
+
+Data::ICal::Entry::Journal - Represents a journal entry in an iCalendar file
+
+
+=head1 SYNOPSIS
+
+    my $vjournal = Data::ICal::Entry::Journal->new();
+    $vjournal->add_properties(
+        summary => "Minutes of my party",
+        description => "I cried because I wanted to.",
+	# Dat*e*::ICal is not a typo here
+        dtstart   => Date::ICal->new( epoch => time )->ical,
+    );
+
+    $calendar->add_entry($vjournal);
+  
+=head1 DESCRIPTION
+
+A L<Data::ICal::Entry::Journal> object represents a single journal entry in an iCalendar file.
+(Note that the iCalendar RFC refers to entries as "components".)  It is a subclass
+of L<Data::ICal::Entry> and accepts all of its methods.
+
+=head1 METHODS
+
+=cut
+
+=head2 ical_entry_type
+
+Returns C<VJOURNAL>, its iCalendar entry name.
+
+=cut
+
+sub ical_entry_type {'VJOURNAL'}
+
+=head2 optional_unique_properties
+
+According to the iCalendar standard, the following properties may be specified
+at most one time for a journal entry:
+
+    class  created  description  dtstart  dtstamp 
+    last-modified  organizer  recurrence-id  sequence  status 
+    summary  uid  url 
+
+=cut
+
+sub optional_unique_properties {
+    qw(
+        class  created  description  dtstart  dtstamp
+        last-modified  organizer  recurrence-id  sequence  status
+        summary  uid  url
+    );
+}
+
+=head2 optional_repeatable_properties
+
+According to the iCalendar standard, the following properties may be specified
+any number of times for a journal entry:
+
+	attach  attendee  categories  comment 
+	contact  exdate  exrule  related-to  rdate 
+	rrule  request-status  
+
+=cut
+
+sub optional_repeatable_properties {
+    qw(
+        attach  attendee  categories  comment
+        contact  exdate  exrule  related-to  rdate
+        rrule  request-status
+    );
+}
+
+=head1 AUTHOR
+
+Jesse Vincent  C<< <jesse at bestpractical.com> >>
+
+
+=head1 LICENCE AND COPYRIGHT
+
+Copyright (c) 2005, Best Practical Solutions, LLC.  All rights reserved.
+
+This module is free software; you can redistribute it and/or
+modify it under the same terms as Perl itself. See L<perlartistic>.
+
+
+=head1 DISCLAIMER OF WARRANTY
+
+BECAUSE THIS SOFTWARE IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY
+FOR THE SOFTWARE, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN
+OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES
+PROVIDE THE SOFTWARE "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER
+EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE
+ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE SOFTWARE IS WITH
+YOU. SHOULD THE SOFTWARE PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL
+NECESSARY SERVICING, REPAIR, OR CORRECTION.
+
+IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
+WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR
+REDISTRIBUTE THE SOFTWARE AS PERMITTED BY THE ABOVE LICENCE, BE
+LIABLE TO YOU FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL,
+OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE
+THE SOFTWARE (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING
+RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A
+FAILURE OF THE SOFTWARE TO OPERATE WITH ANY OTHER SOFTWARE), EVEN IF
+SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF
+SUCH DAMAGES.
+
+=cut
+
+1;

Added: Data-ICal/lib/Data/ICal/Entry/TimeZone.pm
==============================================================================
--- (empty file)
+++ Data-ICal/lib/Data/ICal/Entry/TimeZone.pm	Tue Jul 26 22:53:59 2005
@@ -0,0 +1,115 @@
+use warnings;
+use strict;
+
+package Data::ICal::Entry::TimeZone;
+
+use base qw/Data::ICal::Entry/;
+
+=head1 NAME
+
+Data::ICal::Entry::TimeZone - Represents a time zone definition in an iCalendar file
+
+
+=head1 SYNOPSIS
+
+    my $vtimezone = Data::ICal::Entry::TimeZone->new();
+    $vtimezone->add_properties(
+        tzid => "US-Eastern",
+	tzurl => "http://zones.stds_r_us.net/tz/US-Eastern"
+    );
+
+    $vtimezone->add_entry($daylight); # daylight/ standard not yet implemented
+    $vtimezone->add_entry($standard); # :-(
+
+    $calendar->add_entry($vtimezone);
+
+  
+=head1 DESCRIPTION
+
+A L<Data::ICal::Entry::TimeZone> object represents the declaration of a time
+zone in an iCalendar file.  (Note that the iCalendar RFC refers to entries as
+"components".)  It is a subclass of L<Data::ICal::Entry> and accepts all of its
+methods.
+
+This module is not yet useful, because every time zone declaration needs to contain
+at least one C<STANDARD> or C<DAYLIGHT> component, and these have not yet been implemented.
+
+=head1 METHODS
+
+=cut
+
+=head2 ical_entry_type
+
+Returns C<VTIMEZONE>, its iCalendar entry name.
+
+=cut
+
+sub ical_entry_type {'VTIMEZONE'}
+
+=head2 optional_unique_properties
+
+According to the iCalendar standard, the following properties may be specified
+at most one time for a time zone declaration:
+
+	last-modified tzurl
+
+=cut
+
+sub optional_unique_properties {
+    qw(
+        last-modified tzurl
+    );
+}
+
+=head2 mandatory_unique_properties
+
+According to the iCalendar standard, the C<tzid> property must be specified
+exact one time in a time zone declaration.
+
+=cut
+
+sub mandatory_unique_properties {
+    qw(
+        tzid
+    );
+}
+
+=head1 AUTHOR
+
+Jesse Vincent  C<< <jesse at bestpractical.com> >>
+
+
+=head1 LICENCE AND COPYRIGHT
+
+Copyright (c) 2005, Best Practical Solutions, LLC.  All rights reserved.
+
+This module is free software; you can redistribute it and/or
+modify it under the same terms as Perl itself. See L<perlartistic>.
+
+
+=head1 DISCLAIMER OF WARRANTY
+
+BECAUSE THIS SOFTWARE IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY
+FOR THE SOFTWARE, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN
+OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES
+PROVIDE THE SOFTWARE "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER
+EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE
+ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE SOFTWARE IS WITH
+YOU. SHOULD THE SOFTWARE PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL
+NECESSARY SERVICING, REPAIR, OR CORRECTION.
+
+IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
+WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR
+REDISTRIBUTE THE SOFTWARE AS PERMITTED BY THE ABOVE LICENCE, BE
+LIABLE TO YOU FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL,
+OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE
+THE SOFTWARE (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING
+RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A
+FAILURE OF THE SOFTWARE TO OPERATE WITH ANY OTHER SOFTWARE), EVEN IF
+SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF
+SUCH DAMAGES.
+
+=cut
+
+1;

Added: Data-ICal/lib/Data/ICal/Entry/Todo.pm
==============================================================================
--- (empty file)
+++ Data-ICal/lib/Data/ICal/Entry/Todo.pm	Tue Jul 26 22:53:59 2005
@@ -0,0 +1,128 @@
+use warnings;
+use strict;
+
+package Data::ICal::Entry::Todo;
+
+use base qw/Data::ICal::Entry/;
+
+=head1 NAME
+
+Data::ICal::Entry::Todo - Represents a to-do entry in an iCalendar file
+
+
+=head1 SYNOPSIS
+
+    my $vtodo = Data::ICal::Entry::Todo->new();
+    $vtodo->add_properties(
+        summary   => "go to sleep",
+        status    => 'INCOMPLETE',
+	# Dat*e*::ICal is not a typo here
+        dtstart   => Date::ICal->new( epoch => time )->ical,
+    );
+
+    $calendar->add_entry($vtodo);
+
+    $vtodo->add_entry($alarm); 
+  
+=head1 DESCRIPTION
+
+A L<Data::ICal::Entry::Todo> object represents a single to-do entry in an iCalendar file.
+(Note that the iCalendar RFC refers to entries as "components".)  It is a subclass
+of L<Data::ICal::Entry> and accepts all of its methods.
+
+=head1 METHODS
+
+=cut
+
+=head2 ical_entry_type
+
+Returns C<VTODO>, its iCalendar entry name.
+
+=cut
+
+sub ical_entry_type {'VTODO'}
+
+=head2 optional_unique_properties
+
+According to the iCalendar standard, the following properties may be specified
+at most one time for a to-do item:
+
+      class  completed  created  description  dtstamp
+      dtstart  geo  last-modified  location  organizer
+      percent-complete  priority  recurrence-id  sequence  status
+      summary  uid  url
+
+In addition, C<due> and C<duration> may be specified at most once each, but not both
+in the same entry (though this restriction is not enforced).
+
+=cut
+
+sub optional_unique_properties {
+    qw(
+        class  completed  created  description  dtstamp
+        dtstart  geo  last-modified  location  organizer
+        percent-complete  priority  recurrence-id  sequence  status
+        summary  uid  url
+
+        due duration
+    );
+}
+
+=head2 optional_repeatable_properties
+
+According to the iCalendar standard, the following properties may be specified
+any number of times for a to-do item:
+
+      attach  attendee  categories  comment  contact
+      exdate  exrule  request-status  related-to  resources
+      rdate  rrule  
+
+=cut
+
+sub optional_repeatable_properties {
+    qw(
+        attach  attendee  categories  comment  contact
+        exdate  exrule  request-status  related-to  resources
+        rdate  rrule
+    );
+}
+
+=head1 AUTHOR
+
+Jesse Vincent  C<< <jesse at bestpractical.com> >>
+
+
+=head1 LICENCE AND COPYRIGHT
+
+Copyright (c) 2005, Best Practical Solutions, LLC.  All rights reserved.
+
+This module is free software; you can redistribute it and/or
+modify it under the same terms as Perl itself. See L<perlartistic>.
+
+
+=head1 DISCLAIMER OF WARRANTY
+
+BECAUSE THIS SOFTWARE IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY
+FOR THE SOFTWARE, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN
+OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES
+PROVIDE THE SOFTWARE "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER
+EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE
+ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE SOFTWARE IS WITH
+YOU. SHOULD THE SOFTWARE PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL
+NECESSARY SERVICING, REPAIR, OR CORRECTION.
+
+IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
+WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR
+REDISTRIBUTE THE SOFTWARE AS PERMITTED BY THE ABOVE LICENCE, BE
+LIABLE TO YOU FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL,
+OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE
+THE SOFTWARE (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING
+RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A
+FAILURE OF THE SOFTWARE TO OPERATE WITH ANY OTHER SOFTWARE), EVEN IF
+SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF
+SUCH DAMAGES.
+
+=cut
+
+1;

Modified: Data-ICal/lib/Data/ICal/Property.pm
==============================================================================
--- Data-ICal/lib/Data/ICal/Property.pm	(original)
+++ Data-ICal/lib/Data/ICal/Property.pm	Tue Jul 26 22:53:59 2005
@@ -5,22 +5,160 @@
 
 use base qw/Class::Accessor/;
 
-__PACKAGE__->mk_accessors(qw(key value));
+use Carp;
+
+=head1 NAME
+
+Data::ICal::Property - Represents a property on an entry in an iCalendar file
+
+  
+=head1 DESCRIPTION
+
+A L<Data::ICal::Property> object represents a single property on an
+entry in an iCalendar file.
+
+You shouldn't need to access L<Data::ICal::Property> values directly -- just use
+C<add_property> in L<Data::ICal::Entry>.
+
+=head1 METHODS
+
+=cut
+
+=head2 new $key, $value, [$parameter_hash]
+
+Creates a new L<Data::ICal::Property> with key C<$key> and value C<$value>.
+
+If C<$parameter_hash> is provided, sets the property's parameters to it.
+The parameter hash should have keys equal to the names of the parameters (case 
+insensitive; parameter hashes should not contain two different keys which are
+the same when converted to upper case); the values should either be a string
+if the parameter has a single value or an array reference of strings if
+the parameter has multiple values.
+
+=cut
 
 sub new {
     my $class = shift;
-    my $self = {};
-    
+    my $self  = {};
+
     bless $self, $class;
 
     $self->key(shift);
     $self->value(shift);
+    $self->parameters( shift || {} );
     return ($self);
 }
 
+=head2 key [$key]
+
+Gets or sets the key name of this property.
+
+=head2 value [$value]
+
+Gets or sets the value of this property.
+
+=head2 parameters [$param_hash]
+
+Gets or sets the parameter hash reference of this property.
+
+=cut
+
+__PACKAGE__->mk_accessors(qw(key value parameters));
+
+=head2 as_string
+
+Returns the property formatted as a string (including trailing newline).
+
+=cut
+
 sub as_string {
+    my $self   = shift;
+    my $string = uc( $self->key )
+        . $self->_parameters_as_string . ":"
+        . $self->value . "\n";
+
+  # Assumption: the only place in an iCalendar that needs folding are property
+  # lines
+    return $self->_fold($string);
+}
+
+=begin private
+
+=head2 _parameters_as_string
+
+Returns the property's parameters as a string.  Properties are sorted alphabetically
+to aid testing.
+
+=end private
+
+=cut
+
+sub _parameters_as_string {
     my $self = shift;
-    return uc($self->key).":".$self->value;
+    my $out  = '';
+    for my $name ( sort keys %{ $self->parameters } ) {
+        my $value = $self->parameters->{$name};
+        $out .= ';'
+            . uc($name) . '='
+            . $self->_quoted_parameter_values(
+            ref $value ? @$value : $value );
+    }
+    return $out;
+}
+
+=begin private
+
+=head2 _quoted_parameter_values @values
+
+Quotes any of the values in C<@values> that need to be quoted and returns the quoted values
+joined by commas.
+
+If any of the values contains a double-quote, erases it and emits a warning.
+
+=end private
+
+=cut
+
+sub _quoted_parameter_values {
+    my $self   = shift;
+    my @values = @_;
+
+    for my $val (@values) {
+        if ( $val =~ /"/ ) {
+
+            # Get all the way back to the user's code
+            local $Carp::CarpLevel = $Carp::CarpLevel + 1;
+            carp "Invalid parameter value (contains double quote): $val";
+            $val =~ tr/"//d;
+        }
+    }
+
+    return join ',', map { /[;,:]/ ? qq("$_") : $_ } @values;
+}
+
+=begin private
+
+=head2 _fold $string
+
+Returns C<$string> folded with newlines and leading whitespace so that each
+line is at most 75 characters.
+
+(Note that it folds at 75 characters, not 75 bytes as specified in the standard.)
+
+=end private
+
+=cut
+
+sub _fold {
+    my $self   = shift;
+    my $string = shift;
+
+    # We can't just use a s//g, because we need to include the added space and
+    # first character of the next line in the count for the next line.
+    while ( $string =~ /(.{76})/ ) {
+        $string =~ s/(.{75})(.)/$1\n $2/;
+    }
+    return $string;
 }
 
 1;

Modified: Data-ICal/t/00.load.t
==============================================================================
--- Data-ICal/t/00.load.t	(original)
+++ Data-ICal/t/00.load.t	Tue Jul 26 22:53:59 2005
@@ -4,4 +4,4 @@
 use_ok( 'Data::ICal' );
 }
 
-diag( "Testing Data::ICal $Data::ICal::Generator::VERSION" );
+diag( "Testing Data::ICal $Data::ICal::VERSION" );

Modified: Data-ICal/t/01.simplegen.t
==============================================================================
--- Data-ICal/t/01.simplegen.t	(original)
+++ Data-ICal/t/01.simplegen.t	Tue Jul 26 22:53:59 2005
@@ -3,28 +3,26 @@
 use warnings;
 use strict;
 
-use Test::More tests => 15;
+use Test::More tests => 20;
+use Test::LongString;
+use Test::NoWarnings; # this catches our warnings like setting unknown properties
 
-use_ok('Data::ICal');
+BEGIN { use_ok('Data::ICal') }
 
 my $s = Data::ICal->new();
 
 isa_ok($s, 'Data::ICal');
 
-can_ok($s, 'as_string');
+can_ok($s, qw/as_string add_entry entries/);
 
-can_ok($s, 'add_entry');
+BEGIN { use_ok('Data::ICal::Entry::Todo') }
 
-
-use_ok('Data::ICal::Todo');
-
-my $todo = Data::ICal::Todo->new();
+my $todo = Data::ICal::Entry::Todo->new();
+isa_ok($todo, 'Data::ICal::Entry::Todo');
 isa_ok($todo, 'Data::ICal::Entry');
 
 
-can_ok($todo, 'add_property');
-can_ok($todo, 'add_properties');
-can_ok($todo, 'properties');
+can_ok($todo, qw/add_property add_properties properties/);
 
 
 $todo->add_properties( url => 'http://example.com/todo1',
@@ -41,9 +39,66 @@
 is(scalar @{ $s->entries},1);
 
 
-my $output = $s->as_string;
 
-like( $output, qr/^BEGIN:VCALENDAR/, "Starts ok");
-like( $output, qr/END:VCALENDAR/, "Ends ok");
-like($output, qr/BEGIN:VTODO/, "has a single vtodo");
+is_string($s->as_string, <<END_VCAL, "Got the right output");
+BEGIN:VCALENDAR
+PRODID:Data::ICal $Data::ICal::VERSION
+VERSION:2.0
+BEGIN:VTODO
+COMMENT:a first comment
+COMMENT:a second comment
+SUMMARY:This summary trumps the first summary
+URL:http://example.com/todo1
+END:VTODO
+END:VCALENDAR
+END_VCAL
+
+$todo->add_property( suMMaRy => "This one trumps number two, even though weird capitalization!");
+
+is_string($s->as_string, <<END_VCAL, "add_property is case insensitive");
+BEGIN:VCALENDAR
+PRODID:Data::ICal $Data::ICal::VERSION
+VERSION:2.0
+BEGIN:VTODO
+COMMENT:a first comment
+COMMENT:a second comment
+SUMMARY:This one trumps number two, even though weird capitalization!
+URL:http://example.com/todo1
+END:VTODO
+END:VCALENDAR
+END_VCAL
+
+BEGIN { use_ok('Data::ICal::Entry::Event') }
+
+my $event = Data::ICal::Entry::Event->new();
+isa_ok($event, 'Data::ICal::Entry::Event');
+isa_ok($event, 'Data::ICal::Entry');
+
+
+can_ok($event, qw/add_property add_properties properties/);
+
+
+$event->add_properties( 
+                        summary => 'Awesome party',
+                        description => 'at my place!',
+                    );
+
+ok($s->add_entry($event));
+is(scalar @{ $s->entries},2);
 
+is_string($s->as_string, <<END_VCAL, "got the right output");
+BEGIN:VCALENDAR
+PRODID:Data::ICal $Data::ICal::VERSION
+VERSION:2.0
+BEGIN:VTODO
+COMMENT:a first comment
+COMMENT:a second comment
+SUMMARY:This one trumps number two, even though weird capitalization!
+URL:http://example.com/todo1
+END:VTODO
+BEGIN:VEVENT
+DESCRIPTION:at my place!
+SUMMARY:Awesome party
+END:VEVENT
+END:VCALENDAR
+END_VCAL

Added: Data-ICal/t/02.linewrap.t
==============================================================================
--- (empty file)
+++ Data-ICal/t/02.linewrap.t	Tue Jul 26 22:53:59 2005
@@ -0,0 +1,22 @@
+#!/usr/bin/perl -w
+
+use warnings;
+use strict;
+
+use Test::More tests => 6;
+use Test::LongString;
+
+BEGIN { use_ok('Data::ICal::Entry::Todo') }
+
+my $todo = Data::ICal::Entry::Todo->new;
+isa_ok($todo, 'Data::ICal::Entry::Todo');
+
+my $hundreds_of_characters = "X" x 300;
+
+is(length $hundreds_of_characters, 300);
+cmp_ok(length $hundreds_of_characters, '>', 75, "the summary is bigger than the suggested line-wrap");
+
+$todo->add_property(summary => $hundreds_of_characters);
+
+lacks_string($todo->as_string, $hundreds_of_characters, "the long string isn't there");
+unlike_string($todo->as_string, qr/.{76}/, "no lines are too long");

Added: Data-ICal/t/03.unknown-props.t
==============================================================================
--- (empty file)
+++ Data-ICal/t/03.unknown-props.t	Tue Jul 26 22:53:59 2005
@@ -0,0 +1,31 @@
+#!/usr/bin/perl -w
+
+use warnings;
+use strict;
+
+use Test::More tests => 6;
+use Test::LongString;
+use Test::Warn;
+
+BEGIN { use_ok('Data::ICal::Entry::Todo') }
+
+my $todo = Data::ICal::Entry::Todo->new();
+isa_ok($todo, 'Data::ICal::Entry::Todo');
+
+warnings_are { $todo->add_property( summary => 'Sum it up.' ) } 
+             [], "No warning on real property set";
+
+warnings_are { $todo->add_property( "x-summary" => 'Experimentally sum it up.' ) } 
+             [], "No warning on experimental property set";
+
+warning_is { $todo->add_property( summmmary => 'Summmm it up.' ) }
+    {carped => "Unknown property for Data::ICal::Entry::Todo: summmmary"}, 
+    "Got a warning for fake property set";
+
+is_string($todo->as_string, <<END_VCAL, "Got the right output");
+BEGIN:VTODO
+SUMMARY:Sum it up.
+SUMMMMARY:Summmm it up.
+X-SUMMARY:Experimentally sum it up.
+END:VTODO
+END_VCAL

Added: Data-ICal/t/04.mandatory-props.t
==============================================================================
--- (empty file)
+++ Data-ICal/t/04.mandatory-props.t	Tue Jul 26 22:53:59 2005
@@ -0,0 +1,36 @@
+#!/usr/bin/perl -w
+
+use warnings;
+use strict;
+
+use Test::More tests => 6;
+use Test::LongString;
+use Test::Warn;
+
+BEGIN { use_ok('Data::ICal') }
+BEGIN { use_ok('Data::ICal::Entry::Todo') }
+
+my $todo = Data::ICal::Entry::Todo->new;
+isa_ok($todo, 'Data::ICal::Entry::Todo');
+
+my $cal = Data::ICal->new;
+isa_ok($cal, 'Data::ICal');
+
+$cal->add_entry($todo);
+
+# breaking the abstraction, ah well
+$cal->properties->{'prodid'} = [];
+
+my $str;
+
+warning_is { $str = $cal->as_string }
+    {carped => "Mandatory property for Data::ICal missing: prodid"}, 
+    "Got a warning for missing mandatory property";
+
+is_string($str, <<END_VCAL, "Got the 'right' output anyway");
+BEGIN:VCALENDAR
+VERSION:2.0
+BEGIN:VTODO
+END:VTODO
+END:VCALENDAR
+END_VCAL

Added: Data-ICal/t/05.prop-params.t
==============================================================================
--- (empty file)
+++ Data-ICal/t/05.prop-params.t	Tue Jul 26 22:53:59 2005
@@ -0,0 +1,26 @@
+#!/usr/bin/perl -w
+
+use warnings;
+use strict;
+
+use Test::More tests => 4;
+use Test::LongString;
+use Test::NoWarnings;
+
+BEGIN { use_ok('Data::ICal::Entry::Todo') }
+
+my $todo = Data::ICal::Entry::Todo->new();
+isa_ok($todo, 'Data::ICal::Entry::Todo');
+
+$todo->add_property( summary => [ 'Sum it up.', { language => "en-US", value => "TEXT" } ] ); 
+# example from RFC 2445 4.2.11
+$todo->add_properties( attendee => [ 'MAILTO:janedoe at host.com', 
+    { member => [ 'MAILTO:projectA at host.com', 'MAILTO:projectB at host.com' ] } ]);
+
+is_string($todo->as_string, <<'END_VCAL', "Got the right output");
+BEGIN:VTODO
+ATTENDEE;MEMBER="MAILTO:projectA at host.com","MAILTO:projectB at host.com":MAILT
+ O:janedoe at host.com
+SUMMARY;LANGUAGE=en-US;VALUE=TEXT:Sum it up.
+END:VTODO
+END_VCAL

Added: Data-ICal/t/06.prop-bad-quote.t
==============================================================================
--- (empty file)
+++ Data-ICal/t/06.prop-bad-quote.t	Tue Jul 26 22:53:59 2005
@@ -0,0 +1,27 @@
+#!/usr/bin/perl -w
+
+use warnings;
+use strict;
+
+use Test::More tests => 4;
+use Test::LongString;
+use Test::Warn;
+
+BEGIN { use_ok('Data::ICal::Entry::Todo') }
+
+my $todo = Data::ICal::Entry::Todo->new();
+isa_ok($todo, 'Data::ICal::Entry::Todo');
+
+$todo->add_property( summary => [ 'Sum it up.', { language => 'bla"bla'} ] );
+
+my $str;
+
+warning_like { $str = $todo->as_string }
+    {carped => qr(Invalid parameter value)}, 
+    "Got a warning for fake property set";
+
+is_string($str, <<END_VCAL, "Got the right output");
+BEGIN:VTODO
+SUMMARY;LANGUAGE=blabla:Sum it up.
+END:VTODO
+END_VCAL


More information about the Rt-commit mailing list