vpim2 0.0.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +7 -0
- data/CHANGES +504 -0
- data/COPYING +58 -0
- data/README +182 -0
- data/lib/atom.rb +728 -0
- data/lib/plist.rb +22 -0
- data/lib/vpim.rb +13 -0
- data/lib/vpim/address.rb +219 -0
- data/lib/vpim/attachment.rb +102 -0
- data/lib/vpim/date.rb +222 -0
- data/lib/vpim/dirinfo.rb +277 -0
- data/lib/vpim/duration.rb +119 -0
- data/lib/vpim/enumerator.rb +32 -0
- data/lib/vpim/field.rb +614 -0
- data/lib/vpim/icalendar.rb +381 -0
- data/lib/vpim/maker/vcard.rb +16 -0
- data/lib/vpim/property/base.rb +193 -0
- data/lib/vpim/property/common.rb +315 -0
- data/lib/vpim/property/location.rb +38 -0
- data/lib/vpim/property/priority.rb +43 -0
- data/lib/vpim/property/recurrence.rb +69 -0
- data/lib/vpim/property/resources.rb +24 -0
- data/lib/vpim/repo.rb +181 -0
- data/lib/vpim/rfc2425.rb +367 -0
- data/lib/vpim/rrule.rb +591 -0
- data/lib/vpim/vcard.rb +1430 -0
- data/lib/vpim/version.rb +18 -0
- data/lib/vpim/vevent.rb +187 -0
- data/lib/vpim/view.rb +90 -0
- data/lib/vpim/vjournal.rb +58 -0
- data/lib/vpim/vpim.rb +65 -0
- data/lib/vpim/vtodo.rb +103 -0
- data/samples/README.mutt +93 -0
- data/samples/ab-query.rb +57 -0
- data/samples/cmd-itip.rb +156 -0
- data/samples/ex_cpvcard.rb +55 -0
- data/samples/ex_get_vcard_photo.rb +22 -0
- data/samples/ex_mkv21vcard.rb +34 -0
- data/samples/ex_mkvcard.rb +64 -0
- data/samples/ex_mkyourown.rb +29 -0
- data/samples/ics-dump.rb +210 -0
- data/samples/ics-to-rss.rb +84 -0
- data/samples/mutt-aliases-to-vcf.rb +45 -0
- data/samples/osx-wrappers.rb +86 -0
- data/samples/reminder.rb +203 -0
- data/samples/rrule.rb +71 -0
- data/samples/tabbed-file-to-vcf.rb +390 -0
- data/samples/vcf-dump.rb +86 -0
- data/samples/vcf-lines.rb +61 -0
- data/samples/vcf-to-ics.rb +22 -0
- data/samples/vcf-to-mutt.rb +121 -0
- data/test/test_all.rb +17 -0
- data/test/test_date.rb +120 -0
- data/test/test_dur.rb +41 -0
- data/test/test_field.rb +156 -0
- data/test/test_ical.rb +415 -0
- data/test/test_repo.rb +158 -0
- data/test/test_rrule.rb +1030 -0
- data/test/test_vcard.rb +973 -0
- data/test/test_view.rb +79 -0
- metadata +117 -0
data/lib/vpim/version.rb
ADDED
@@ -0,0 +1,18 @@
|
|
1
|
+
=begin
|
2
|
+
Copyright (C) 2008 Sam Roberts
|
3
|
+
|
4
|
+
This library is free software; you can redistribute it and/or modify it
|
5
|
+
under the same terms as the ruby language itself, see the file COPYING for
|
6
|
+
details.
|
7
|
+
=end
|
8
|
+
|
9
|
+
module Vpim
|
10
|
+
PRODID = '-//Ensemble Independent//vPim 0.658//EN'
|
11
|
+
|
12
|
+
VERSION = '0.658'
|
13
|
+
|
14
|
+
# Return the API version as a string.
|
15
|
+
def Vpim.version
|
16
|
+
VERSION
|
17
|
+
end
|
18
|
+
end
|
data/lib/vpim/vevent.rb
ADDED
@@ -0,0 +1,187 @@
|
|
1
|
+
=begin
|
2
|
+
Copyright (C) 2008 Sam Roberts
|
3
|
+
|
4
|
+
This library is free software; you can redistribute it and/or modify it
|
5
|
+
under the same terms as the ruby language itself, see the file COPYING for
|
6
|
+
details.
|
7
|
+
=end
|
8
|
+
|
9
|
+
require 'vpim/dirinfo'
|
10
|
+
require 'vpim/field'
|
11
|
+
require 'vpim/rfc2425'
|
12
|
+
require 'vpim/rrule'
|
13
|
+
require 'vpim/vpim'
|
14
|
+
require 'vpim/property/base'
|
15
|
+
require 'vpim/property/common'
|
16
|
+
require 'vpim/property/priority'
|
17
|
+
require 'vpim/property/location'
|
18
|
+
require 'vpim/property/resources'
|
19
|
+
require 'vpim/property/recurrence'
|
20
|
+
|
21
|
+
module Vpim
|
22
|
+
class Icalendar
|
23
|
+
class Vevent
|
24
|
+
|
25
|
+
include Vpim::Icalendar::Property::Base
|
26
|
+
include Vpim::Icalendar::Property::Common
|
27
|
+
include Vpim::Icalendar::Property::Priority
|
28
|
+
include Vpim::Icalendar::Property::Location
|
29
|
+
include Vpim::Icalendar::Property::Resources
|
30
|
+
include Vpim::Icalendar::Property::Recurrence
|
31
|
+
|
32
|
+
def initialize(fields) #:nodoc:
|
33
|
+
outer, inner = Vpim.outer_inner(fields)
|
34
|
+
|
35
|
+
@properties = Vpim::DirectoryInfo.create(outer)
|
36
|
+
|
37
|
+
@elements = inner
|
38
|
+
|
39
|
+
# See "TODO - fields" in dirinfo.rb
|
40
|
+
end
|
41
|
+
|
42
|
+
# TODO - derive everything from Icalendar::Component to get rid of this kind of stuff?
|
43
|
+
def fields #:nodoc:
|
44
|
+
f = @properties.to_a
|
45
|
+
last = f.pop
|
46
|
+
f.push @elements
|
47
|
+
f.push last
|
48
|
+
end
|
49
|
+
|
50
|
+
def properties #:nodoc:
|
51
|
+
@properties
|
52
|
+
end
|
53
|
+
|
54
|
+
# Create a new Vevent object. All events must have a DTSTART field,
|
55
|
+
# specify it as either a Time or a Date in +start+, it defaults to "now"
|
56
|
+
#
|
57
|
+
# If specified, +fields+ must be either an array of Field objects to
|
58
|
+
# add, or a Hash of String names to values that will be used to build
|
59
|
+
# Field objects. The latter is a convenient short-cut allowing the Field
|
60
|
+
# objects to be created for you when called like:
|
61
|
+
#
|
62
|
+
# Vevent.create(Date.today, 'SUMMARY' => "today's event")
|
63
|
+
#
|
64
|
+
def Vevent.create(start = Time.now, fields=[])
|
65
|
+
# TODO
|
66
|
+
# - maybe events are usually created in a particular way? With a
|
67
|
+
# start/duration or a start/end? Maybe I can make it easier. Ideally, I
|
68
|
+
# would like to make it hard to encode an invalid Event.
|
69
|
+
# - I don't think its useful to have a default dtstart for events
|
70
|
+
# - also, I don't think dstart is mandatory
|
71
|
+
dtstart = DirectoryInfo::Field.create('DTSTART', start)
|
72
|
+
di = DirectoryInfo.create([ dtstart ], 'VEVENT')
|
73
|
+
|
74
|
+
Vpim::DirectoryInfo::Field.create_array(fields).each { |f| di.push_unique f }
|
75
|
+
|
76
|
+
new(di.to_a)
|
77
|
+
end
|
78
|
+
|
79
|
+
# Creates a yearly repeating event, such as for a birthday.
|
80
|
+
def Vevent.create_yearly(date, summary)
|
81
|
+
create(
|
82
|
+
date,
|
83
|
+
'SUMMARY' => summary.to_str,
|
84
|
+
'RRULE' => 'FREQ=YEARLY'
|
85
|
+
)
|
86
|
+
end
|
87
|
+
|
88
|
+
# Accept an event invitation. The +invitee+ is the Address that wishes
|
89
|
+
# to accept the event invitation as confirmed.
|
90
|
+
#
|
91
|
+
# The event created is identical to this one, but
|
92
|
+
# - without the attendees
|
93
|
+
# - with the invitee added with a PARTSTAT of ACCEPTED
|
94
|
+
def accept(invitee)
|
95
|
+
# FIXME - move to Vpim::Itip.
|
96
|
+
invitee = invitee.copy
|
97
|
+
invitee.partstat = 'ACCEPTED'
|
98
|
+
|
99
|
+
fields = []
|
100
|
+
|
101
|
+
@properties.each_with_index do
|
102
|
+
|f,i|
|
103
|
+
|
104
|
+
# put invitee in as field[1]
|
105
|
+
fields << invitee.encode('ATTENDEE') if i == 1
|
106
|
+
|
107
|
+
fields << f unless f.name? 'ATTENDEE'
|
108
|
+
end
|
109
|
+
|
110
|
+
Vevent.new(fields)
|
111
|
+
end
|
112
|
+
|
113
|
+
# In iTIP, whether this event is OPAQUE or TRANSPARENT to scheduling. If
|
114
|
+
# transparency is not explicitly set, it defaults to OPAQUE.
|
115
|
+
def transparency
|
116
|
+
proptoken 'TRANSP', ["OPAQUE", "TRANSPARENT"], "OPAQUE"
|
117
|
+
end
|
118
|
+
|
119
|
+
# The duration in seconds of an Event, or nil if unspecified. If the
|
120
|
+
# DURATION field is not present, but the DTEND field is, the duration is
|
121
|
+
# calculated from DTSTART and DTEND. Durations of zero seconds are
|
122
|
+
# possible.
|
123
|
+
def duration
|
124
|
+
propduration 'DTEND'
|
125
|
+
end
|
126
|
+
|
127
|
+
# The end time for this Event. If the DTEND field is not present, but the
|
128
|
+
# DURATION field is, the end will be calculated from DTSTART and
|
129
|
+
# DURATION.
|
130
|
+
def dtend
|
131
|
+
propend 'DTEND'
|
132
|
+
end
|
133
|
+
|
134
|
+
# Make a new Vevent, or make changes to an existing Vevent.
|
135
|
+
class Maker
|
136
|
+
include Vpim::Icalendar::Set::Util #:nodoc:
|
137
|
+
include Vpim::Icalendar::Set::Common
|
138
|
+
|
139
|
+
# The event that changes are being made to.
|
140
|
+
attr_reader :event
|
141
|
+
|
142
|
+
def initialize(event) #:nodoc:
|
143
|
+
@event = event
|
144
|
+
@comp = event
|
145
|
+
end
|
146
|
+
|
147
|
+
# Make changes to +event+. If +event+ is not specified, creates a new
|
148
|
+
# event. Yields a Vevent::Maker, and returns +event+.
|
149
|
+
def self.make(event = Vpim::Icalendar::Vevent.create) #:yield:maker
|
150
|
+
m = self.new(event)
|
151
|
+
yield m
|
152
|
+
m.event
|
153
|
+
end
|
154
|
+
|
155
|
+
# Set transparency to "OPAQUE" or "TRANSPARENT", see Vpim::Vevent#transparency.
|
156
|
+
def transparency(token)
|
157
|
+
set_token 'TRANSP', ["OPAQUE", "TRANSPARENT"], "OPAQUE", token
|
158
|
+
end
|
159
|
+
|
160
|
+
# Set end for events with fixed durations. +end+ can be a Date or Time
|
161
|
+
def dtend(dtend)
|
162
|
+
set_date_or_datetime 'DTEND', 'DATE-TIME', dtend
|
163
|
+
end
|
164
|
+
|
165
|
+
# Add a RRULE to this event. The rule can be provided as a pre-build
|
166
|
+
# RRULE value, or the RRULE maker can be used.
|
167
|
+
def add_rrule(rule = nil, &block) #:yield: Rrule::Maker
|
168
|
+
# TODO - should be in Property::Reccurrence::Set
|
169
|
+
unless rule
|
170
|
+
rule = Rrule::Maker.new(&block).encode
|
171
|
+
end
|
172
|
+
@comp.properties.push(Vpim::DirectoryInfo::Field.create("RRULE", rule))
|
173
|
+
self
|
174
|
+
end
|
175
|
+
|
176
|
+
# Set the RRULE for this event. See #add_rrule
|
177
|
+
def set_rrule(rule = nil, &block) #:yield: Rrule::Maker
|
178
|
+
rm_all("RRULE")
|
179
|
+
add_rrule(rule, &block)
|
180
|
+
end
|
181
|
+
|
182
|
+
end
|
183
|
+
|
184
|
+
end
|
185
|
+
end
|
186
|
+
end
|
187
|
+
|
data/lib/vpim/view.rb
ADDED
@@ -0,0 +1,90 @@
|
|
1
|
+
=begin
|
2
|
+
Copyright (C) 2008 Sam Roberts
|
3
|
+
|
4
|
+
This library is free software; you can redistribute it and/or modify it
|
5
|
+
under the same terms as the ruby language itself, see the file COPYING for
|
6
|
+
details.
|
7
|
+
=end
|
8
|
+
|
9
|
+
require "enumerator"
|
10
|
+
|
11
|
+
module Vpim
|
12
|
+
module View
|
13
|
+
|
14
|
+
SECSPERDAY = 24 * 60 * 60
|
15
|
+
|
16
|
+
# View only events occuring in the next week.
|
17
|
+
module Week
|
18
|
+
def each(klass = nil) #:nodoc:
|
19
|
+
unless block_given?
|
20
|
+
return Enumerable::Enumerator.new(self, :each, klass)
|
21
|
+
end
|
22
|
+
|
23
|
+
t0 = Time.new.to_a
|
24
|
+
t0[0] = t0[1] = t0[2] = 0 # sec,min,hour = 0
|
25
|
+
t0 = Time.local(*t0)
|
26
|
+
t1 = t0 + 7 * SECSPERDAY
|
27
|
+
|
28
|
+
# Need to filter occurrences, too. Create modules for this on the fly.
|
29
|
+
occurrences = Module.new
|
30
|
+
# I'm passing state into the module's instance methods by doing string
|
31
|
+
# evaluation... which sucks, but I don't think I can get this closure in
|
32
|
+
# there.
|
33
|
+
occurrences.module_eval(<<"__", __FILE__, __LINE__+1)
|
34
|
+
def occurrences(dountil=nil)
|
35
|
+
unless block_given?
|
36
|
+
return Enumerable::Enumerator.new(self, :occurrences, dountil)
|
37
|
+
end
|
38
|
+
super(dountil) do |t|
|
39
|
+
t0 = Time.at(#{t0.to_i})
|
40
|
+
t1 = Time.at(#{t1.to_i})
|
41
|
+
break if t >= t1
|
42
|
+
tend = t
|
43
|
+
if respond_to? :duration
|
44
|
+
tend += duration || 0
|
45
|
+
end
|
46
|
+
if tend >= t0
|
47
|
+
yield t
|
48
|
+
end
|
49
|
+
end
|
50
|
+
end
|
51
|
+
__
|
52
|
+
=begin
|
53
|
+
block = lambda do |dountil|
|
54
|
+
unless block_given?
|
55
|
+
return Enumerable::Enumerator.new(self, :occurrences, dountil)
|
56
|
+
end
|
57
|
+
super(dountil) do |t|
|
58
|
+
break if t >= t1
|
59
|
+
yield t
|
60
|
+
end
|
61
|
+
end
|
62
|
+
occurrences.send(:define_method, :occurrences, block)
|
63
|
+
=end
|
64
|
+
super do |ve|
|
65
|
+
if ve.occurs_in?(t0, t1)
|
66
|
+
if ve.respond_to? :occurrences
|
67
|
+
ve.extend occurrences
|
68
|
+
end
|
69
|
+
yield ve
|
70
|
+
end
|
71
|
+
end
|
72
|
+
end
|
73
|
+
end
|
74
|
+
|
75
|
+
# Return a calendar view for the next week.
|
76
|
+
def self.week(cal)
|
77
|
+
cal.clone.extend Week.dup
|
78
|
+
end
|
79
|
+
|
80
|
+
module Todo
|
81
|
+
end
|
82
|
+
|
83
|
+
# Return a calendar view of only todos (optionally, include todos that
|
84
|
+
# are done).
|
85
|
+
def self.todos(cal, withdone=false)
|
86
|
+
end
|
87
|
+
|
88
|
+
end
|
89
|
+
end
|
90
|
+
|
@@ -0,0 +1,58 @@
|
|
1
|
+
=begin
|
2
|
+
Copyright (C) 2008 Sam Roberts
|
3
|
+
|
4
|
+
This library is free software; you can redistribute it and/or modify it
|
5
|
+
under the same terms as the ruby language itself, see the file COPYING for
|
6
|
+
details.
|
7
|
+
=end
|
8
|
+
|
9
|
+
require 'vpim/dirinfo'
|
10
|
+
require 'vpim/field'
|
11
|
+
require 'vpim/rfc2425'
|
12
|
+
require 'vpim/vpim'
|
13
|
+
require 'vpim/property/base'
|
14
|
+
require 'vpim/property/common'
|
15
|
+
require 'vpim/property/recurrence'
|
16
|
+
|
17
|
+
module Vpim
|
18
|
+
class Icalendar
|
19
|
+
|
20
|
+
class Vjournal
|
21
|
+
include Vpim::Icalendar::Property::Base
|
22
|
+
include Vpim::Icalendar::Property::Common
|
23
|
+
include Vpim::Icalendar::Property::Recurrence
|
24
|
+
|
25
|
+
def initialize(fields) #:nodoc:
|
26
|
+
outer, inner = Vpim.outer_inner(fields)
|
27
|
+
|
28
|
+
@properties = Vpim::DirectoryInfo.create(outer)
|
29
|
+
|
30
|
+
@elements = inner
|
31
|
+
end
|
32
|
+
|
33
|
+
# TODO - derive everything from Icalendar::Component to get rid of this kind of stuff?
|
34
|
+
def fields #:nodoc:
|
35
|
+
f = properties.to_a
|
36
|
+
last = f.pop
|
37
|
+
f.push @elements
|
38
|
+
f.push last
|
39
|
+
end
|
40
|
+
|
41
|
+
def properties #:nodoc:
|
42
|
+
@properties
|
43
|
+
end
|
44
|
+
|
45
|
+
# Create a Vjournal component.
|
46
|
+
def self.create(fields=[])
|
47
|
+
di = DirectoryInfo.create([], 'VJOURNAL')
|
48
|
+
|
49
|
+
Vpim::DirectoryInfo::Field.create_array(fields).each { |f| di.push_unique f }
|
50
|
+
|
51
|
+
new(di.to_a)
|
52
|
+
end
|
53
|
+
|
54
|
+
end
|
55
|
+
|
56
|
+
end
|
57
|
+
end
|
58
|
+
|
data/lib/vpim/vpim.rb
ADDED
@@ -0,0 +1,65 @@
|
|
1
|
+
=begin
|
2
|
+
Copyright (C) 2008 Sam Roberts
|
3
|
+
|
4
|
+
This library is free software; you can redistribute it and/or modify it
|
5
|
+
under the same terms as the ruby language itself, see the file COPYING for
|
6
|
+
details.
|
7
|
+
=end
|
8
|
+
|
9
|
+
require 'vpim/version'
|
10
|
+
|
11
|
+
#:main:README
|
12
|
+
#:title:vPim - vCard and iCalendar support for Ruby
|
13
|
+
module Vpim
|
14
|
+
# Exception used to indicate that data being decoded is invalid, the message
|
15
|
+
# should describe what is invalid.
|
16
|
+
class InvalidEncodingError < StandardError; end
|
17
|
+
|
18
|
+
# Exception used to indicate that data being decoded is unsupported, the message
|
19
|
+
# should describe what is unsupported.
|
20
|
+
#
|
21
|
+
# If its unsupported, its likely because I didn't anticipate it being useful
|
22
|
+
# to support this, and it likely it could be supported on request.
|
23
|
+
class UnsupportedError < StandardError; end
|
24
|
+
|
25
|
+
# Exception used to indicate that encoding failed, probably because the
|
26
|
+
# object would not result in validly encoded data. The message should
|
27
|
+
# describe what is unsupported.
|
28
|
+
class Unencodeable < StandardError; end
|
29
|
+
end
|
30
|
+
|
31
|
+
module Vpim::Methods #:nodoc:
|
32
|
+
module_function
|
33
|
+
|
34
|
+
# Case-insensitive comparison of +str0+ to +str1+, returns true or false.
|
35
|
+
# Either argument can be nil, where nil compares not equal to anything other
|
36
|
+
# than nil.
|
37
|
+
#
|
38
|
+
# This is available both as a module function:
|
39
|
+
# Vpim::Methods.casecmp?("yes", "YES")
|
40
|
+
# and an instance method:
|
41
|
+
# include Vpim::Methods
|
42
|
+
# casecmp?("yes", "YES")
|
43
|
+
#
|
44
|
+
# Will work with ruby1.6 and ruby 1.8.
|
45
|
+
#
|
46
|
+
# TODO - could make this be more efficient, but I'm supporting 1.6, not
|
47
|
+
# optimizing for it.
|
48
|
+
def casecmp?(str0, str1)
|
49
|
+
if str0 == nil
|
50
|
+
if str1 == nil
|
51
|
+
return true
|
52
|
+
else
|
53
|
+
return false
|
54
|
+
end
|
55
|
+
end
|
56
|
+
|
57
|
+
begin
|
58
|
+
str0.casecmp(str1) == 0
|
59
|
+
rescue NoMethodError
|
60
|
+
str0.downcase == str1.downcase
|
61
|
+
end
|
62
|
+
end
|
63
|
+
|
64
|
+
end
|
65
|
+
|
data/lib/vpim/vtodo.rb
ADDED
@@ -0,0 +1,103 @@
|
|
1
|
+
=begin
|
2
|
+
Copyright (C) 2008 Sam Roberts
|
3
|
+
|
4
|
+
This library is free software; you can redistribute it and/or modify it
|
5
|
+
under the same terms as the ruby language itself, see the file COPYING for
|
6
|
+
details.
|
7
|
+
=end
|
8
|
+
|
9
|
+
require 'vpim/dirinfo'
|
10
|
+
require 'vpim/field'
|
11
|
+
require 'vpim/rfc2425'
|
12
|
+
require 'vpim/vpim'
|
13
|
+
require 'vpim/property/base'
|
14
|
+
require 'vpim/property/common'
|
15
|
+
require 'vpim/property/priority'
|
16
|
+
require 'vpim/property/location'
|
17
|
+
require 'vpim/property/resources'
|
18
|
+
require 'vpim/property/recurrence'
|
19
|
+
|
20
|
+
module Vpim
|
21
|
+
class Icalendar
|
22
|
+
|
23
|
+
class Vtodo
|
24
|
+
include Vpim::Icalendar::Property::Base
|
25
|
+
include Vpim::Icalendar::Property::Common
|
26
|
+
include Vpim::Icalendar::Property::Priority
|
27
|
+
include Vpim::Icalendar::Property::Location
|
28
|
+
include Vpim::Icalendar::Property::Resources
|
29
|
+
include Vpim::Icalendar::Property::Recurrence
|
30
|
+
|
31
|
+
def initialize(fields) #:nodoc:
|
32
|
+
outer, inner = Vpim.outer_inner(fields)
|
33
|
+
|
34
|
+
@properties = Vpim::DirectoryInfo.create(outer)
|
35
|
+
|
36
|
+
@elements = inner
|
37
|
+
end
|
38
|
+
|
39
|
+
# TODO - derive everything from Icalendar::Component to get this kind of stuff?
|
40
|
+
def fields #:nodoc:
|
41
|
+
f = @properties.to_a
|
42
|
+
last = f.pop
|
43
|
+
f.push @elements
|
44
|
+
f.push last
|
45
|
+
end
|
46
|
+
|
47
|
+
def properties #:nodoc:
|
48
|
+
@properties
|
49
|
+
end
|
50
|
+
|
51
|
+
# Create a new Vtodo object.
|
52
|
+
#
|
53
|
+
# If specified, +fields+ must be either an array of Field objects to
|
54
|
+
# add, or a Hash of String names to values that will be used to build
|
55
|
+
# Field objects. The latter is a convenient short-cut allowing the Field
|
56
|
+
# objects to be created for you when called like:
|
57
|
+
#
|
58
|
+
# Vtodo.create('SUMMARY' => "buy mangos")
|
59
|
+
#
|
60
|
+
# TODO - maybe todos are usually created in a particular way? I can
|
61
|
+
# make it easier. Ideally, I would like to make it hard to encode an invalid
|
62
|
+
# Event.
|
63
|
+
def Vtodo.create(fields=[])
|
64
|
+
di = DirectoryInfo.create([], 'VTODO')
|
65
|
+
|
66
|
+
Vpim::DirectoryInfo::Field.create_array(fields).each { |f| di.push_unique f }
|
67
|
+
|
68
|
+
new(di.to_a)
|
69
|
+
end
|
70
|
+
|
71
|
+
# The duration in seconds of a Todo, or nil if unspecified. If the
|
72
|
+
# DURATION field is not present, but the DUE field is, the duration is
|
73
|
+
# calculated from DTSTART and DUE. Durations of zero seconds are
|
74
|
+
# possible.
|
75
|
+
def duration
|
76
|
+
propduration 'DUE'
|
77
|
+
end
|
78
|
+
|
79
|
+
# The time at which this Todo is due to be completed. If the DUE field is not present,
|
80
|
+
# but the DURATION field is, due will be calculated from DTSTART and DURATION.
|
81
|
+
def due
|
82
|
+
propend 'DUE'
|
83
|
+
end
|
84
|
+
|
85
|
+
# The date and time that a to-do was actually completed, a Time.
|
86
|
+
def completed
|
87
|
+
proptime 'COMPLETED'
|
88
|
+
end
|
89
|
+
|
90
|
+
# The percentage completetion of the to-do, between 0 and 100. 0 means it hasn't
|
91
|
+
# started, 100 that it has been completed.
|
92
|
+
#
|
93
|
+
# TODO - the handling of this property isn't tied to either COMPLETED: or
|
94
|
+
# STATUS:, but perhaps it should be?
|
95
|
+
def percent_complete
|
96
|
+
propinteger 'PERCENT-COMPLETE'
|
97
|
+
end
|
98
|
+
|
99
|
+
end
|
100
|
+
|
101
|
+
end
|
102
|
+
end
|
103
|
+
|