vpim 0.604 → 0.619
Sign up to get free protection for your applications and to get access to all the features.
- data/CHANGES +16 -0
- data/README +3 -3
- data/bin/reminder +4 -4
- data/lib/vpim/date.rb +2 -2
- data/lib/vpim/property/recurrence.rb +5 -3
- data/lib/vpim/rfc2425.rb +10 -3
- data/lib/vpim/rrule.rb +6 -6
- data/lib/vpim/vcard.rb +21 -9
- data/lib/vpim/version.rb +2 -2
- data/samples/cmd-itip.rb +1 -1
- data/samples/ics-dump.rb +2 -2
- data/samples/reminder.rb +4 -4
- data/test/test_ical.rb +19 -0
- data/test/test_rrule.rb +1 -1
- data/test/test_vcard.rb +60 -1
- metadata +3 -3
data/CHANGES
CHANGED
@@ -1,3 +1,19 @@
|
|
1
|
+
0.619 - 2008-03-30
|
2
|
+
|
3
|
+
- Fixed some problems with rescue statements not being specific enough.
|
4
|
+
|
5
|
+
- Vcard#birthday may now return a DateTime.
|
6
|
+
|
7
|
+
- Added Vcard#urls
|
8
|
+
|
9
|
+
- Fixed a mispelled Uri in Vcard#url
|
10
|
+
|
11
|
+
- Global fix of misspellings of "occurrence"
|
12
|
+
|
13
|
+
- KAddressBook compatibility fix - allow / in field names, even though it is
|
14
|
+
illegal in vCard 3.0.
|
15
|
+
|
16
|
+
|
1
17
|
0.604 - 2008-03-13
|
2
18
|
|
3
19
|
- Fixed a bug with lower-case UTF-16 encoded cards not being detected properly.
|
data/README
CHANGED
@@ -36,9 +36,9 @@ subscribe to "vpim-talk":
|
|
36
36
|
|
37
37
|
= Thanks
|
38
38
|
|
39
|
-
-
|
40
|
-
- ZipDX.com: for sponsoring
|
41
|
-
rules.
|
39
|
+
- http://RubyForge.org: for their generous hosting of this project.
|
40
|
+
- http://ZipDX.com: for sponsoring development of FREQ=weekly and BYSETPOS in
|
41
|
+
recurrence rules.
|
42
42
|
|
43
43
|
= Examples
|
44
44
|
|
data/bin/reminder
CHANGED
@@ -117,8 +117,8 @@ end
|
|
117
117
|
|
118
118
|
puts
|
119
119
|
|
120
|
-
def
|
121
|
-
e.
|
120
|
+
def start_of_first_occurrence(t0, t1, e)
|
121
|
+
e.occurrences.each_until(t1).each do |t|
|
122
122
|
# An event might start before t0, but end after it..., in which case
|
123
123
|
# we are still interested.
|
124
124
|
if (t + (e.duration || 0)) >= t0
|
@@ -129,7 +129,7 @@ def start_of_first_occurence(t0, t1, e)
|
|
129
129
|
end
|
130
130
|
|
131
131
|
all_events.sort! do |lhs, rhs|
|
132
|
-
|
132
|
+
start_of_first_occurrence(t0, t1, lhs) <=> start_of_first_occurrence(t0, t1, rhs)
|
133
133
|
end
|
134
134
|
|
135
135
|
all_events.each do |e|
|
@@ -145,7 +145,7 @@ all_events.each do |e|
|
|
145
145
|
end
|
146
146
|
|
147
147
|
i = 1
|
148
|
-
e.
|
148
|
+
e.occurrences.each_until(t1).each do |t|
|
149
149
|
# An event might start before t0, but end after it..., in which case
|
150
150
|
# we are still interested.
|
151
151
|
dstr = ''
|
data/lib/vpim/date.rb
CHANGED
@@ -61,7 +61,7 @@ class Date
|
|
61
61
|
# +mon+, and day-of-the-week +wday+.
|
62
62
|
#
|
63
63
|
# The nth, +n+, occurrence of +wday+ within the period will be generated
|
64
|
-
# (+n+ defaults to 1). If +n+ is positive, the nth
|
64
|
+
# (+n+ defaults to 1). If +n+ is positive, the nth occurrence from the
|
65
65
|
# beginning of the period will be returned, if negative, the nth occurrence
|
66
66
|
# from the end of the period will be returned.
|
67
67
|
#
|
@@ -154,7 +154,7 @@ class DateGen
|
|
154
154
|
# 0-6, where 0 is Sunday).
|
155
155
|
#
|
156
156
|
# If +n+ is specified, only the nth occurrence of +wday+ within the period
|
157
|
-
# will be generated. If +n+ is positive, the nth
|
157
|
+
# will be generated. If +n+ is positive, the nth occurrence from the
|
158
158
|
# beginning of the period will be returned, if negative, the nth occurrence
|
159
159
|
# from the end of the period will be returned.
|
160
160
|
#
|
@@ -25,18 +25,20 @@ module Vpim
|
|
25
25
|
# need it, contact me and implementation will get on the schedule.
|
26
26
|
module Recurrence
|
27
27
|
# The times this event occurs, as a Vpim::Rrule.
|
28
|
-
def
|
28
|
+
def occurrences
|
29
29
|
start = dtstart
|
30
30
|
unless start
|
31
|
-
raise ArgumentError, "Components with no DTSTART: don't have
|
31
|
+
raise ArgumentError, "Components with no DTSTART: don't have occurrences!"
|
32
32
|
end
|
33
33
|
Vpim::Rrule.new(start, propvalue('RRULE'))
|
34
34
|
end
|
35
35
|
|
36
|
+
alias occurences occurrences #:nodoc: backwards compatibility
|
37
|
+
|
36
38
|
# Check if this event overlaps with the time period later than or equal to +t0+, but
|
37
39
|
# earlier than +t1+.
|
38
40
|
def occurs_in?(t0, t1)
|
39
|
-
|
41
|
+
occurrences.each_until(t1).detect { |t| tend = t + (duration || 0); tend > t0 }
|
40
42
|
end
|
41
43
|
|
42
44
|
def rdates
|
data/lib/vpim/rfc2425.rb
CHANGED
@@ -15,7 +15,8 @@ module Vpim
|
|
15
15
|
# 1*(ALPHA / DIGIT / "-")
|
16
16
|
# Note: I think I can add A-Z here, and get rid of the "i" matches elsewhere.
|
17
17
|
# Note: added '_' to allowed because its produced by Notes - X-LOTUS-CHILD_UID
|
18
|
-
|
18
|
+
# Note: added '/' to allowed because its produced by KAddressBook - X-messaging/xmpp-All:
|
19
|
+
NAME = '[-a-z0-9_/]+'
|
19
20
|
|
20
21
|
# <"> <Any character except CTLs, DQUOTE> <">
|
21
22
|
QSTR = '"([^"]*)"'
|
@@ -51,7 +52,7 @@ module Vpim
|
|
51
52
|
# time-second = 2 DIGIT
|
52
53
|
# time-secfrac = "," 1*DIGIT
|
53
54
|
# time-zone = "Z" / time-numzone
|
54
|
-
# time-
|
55
|
+
# time-numzone = sign time-hour [":"] time-minute
|
55
56
|
TIME = '(\d\d):?(\d\d):?(\d\d)(\.\d+)?(Z|[-+]\d\d:?\d\d)?'
|
56
57
|
|
57
58
|
# integer = (["+"] / "-") 1*DIGIT
|
@@ -111,7 +112,7 @@ module Vpim
|
|
111
112
|
[$1.to_i, $2.to_i, $3.to_i]
|
112
113
|
end
|
113
114
|
|
114
|
-
# Convert a RFC 2425 date into
|
115
|
+
# Convert a RFC 2425 date into a Date object.
|
115
116
|
def self.decode_date_to_date(v)
|
116
117
|
Date.new(*decode_date(v))
|
117
118
|
end
|
@@ -174,6 +175,12 @@ module Vpim
|
|
174
175
|
]
|
175
176
|
end
|
176
177
|
|
178
|
+
def Vpim.decode_date_time_to_datetime(v) #:nodoc:
|
179
|
+
year, month, day, hour, min, sec, secfrac, tz = Vpim.decode_date_time(v)
|
180
|
+
# TODO - DateTime understands timezones, so we could decode tz and use it.
|
181
|
+
DateTime.civil(year, month, day, hour, min, sec, 0)
|
182
|
+
end
|
183
|
+
|
177
184
|
# Vpim.decode_boolean
|
178
185
|
#
|
179
186
|
# float
|
data/lib/vpim/rrule.rb
CHANGED
@@ -32,7 +32,7 @@ end
|
|
32
32
|
|
33
33
|
module Vpim
|
34
34
|
|
35
|
-
# Implements the iCalendar
|
35
|
+
# Implements the iCalendar recurrence rule syntax. See etc/rrule.txt for the
|
36
36
|
# syntax description and examples from RFC 2445. The description is pretty
|
37
37
|
# hard to understand, but the examples are more helpful.
|
38
38
|
#
|
@@ -124,7 +124,7 @@ module Vpim
|
|
124
124
|
end
|
125
125
|
end
|
126
126
|
|
127
|
-
# Return an Enumerable, it's #each() will yield over all
|
127
|
+
# Return an Enumerable, it's #each() will yield over all occurrences up to
|
128
128
|
# (and not including) time +dountil+.
|
129
129
|
def each_until(dountil)
|
130
130
|
Vpim::Enumerator.new(self, dountil)
|
@@ -133,7 +133,7 @@ module Vpim
|
|
133
133
|
# Yields for each +ytime+ in the recurring set of events.
|
134
134
|
#
|
135
135
|
# Warning: the set may be infinite! If you need an upper bound on the
|
136
|
-
# number of
|
136
|
+
# number of occurrences, you need to implement a count, or pass a time,
|
137
137
|
# +dountil+, which will not be iterated past (i.e. all times yielded will be
|
138
138
|
# less than +dountil+).
|
139
139
|
#
|
@@ -152,7 +152,7 @@ module Vpim
|
|
152
152
|
end
|
153
153
|
count = 1
|
154
154
|
|
155
|
-
# With no recurrence, DTSTART is the only
|
155
|
+
# With no recurrence, DTSTART is the only occurrence.
|
156
156
|
if !@rrule
|
157
157
|
return self
|
158
158
|
end
|
@@ -171,7 +171,7 @@ module Vpim
|
|
171
171
|
|
172
172
|
case @freq
|
173
173
|
#when 'YEARLY' then
|
174
|
-
# Don't need to keep track of year, all
|
174
|
+
# Don't need to keep track of year, all occurrences are within t's
|
175
175
|
# year.
|
176
176
|
when 'MONTHLY' then days.month = t.month
|
177
177
|
when 'WEEKLY' then #days.month = t.month
|
@@ -298,7 +298,7 @@ module Vpim
|
|
298
298
|
end.compact # set positions out of scope will be nil, RFC says ignore them
|
299
299
|
end
|
300
300
|
|
301
|
-
# Yield the
|
301
|
+
# Yield the occurrence, if we haven't gone over COUNT, or past UNTIL, or
|
302
302
|
# past the end of representable time.
|
303
303
|
|
304
304
|
yset.each do |y|
|
data/lib/vpim/vcard.rb
CHANGED
@@ -446,11 +446,9 @@ module Vpim
|
|
446
446
|
def decode_date_or_datetime(field) #:nodoc:
|
447
447
|
date = nil
|
448
448
|
begin
|
449
|
-
date = Vpim.
|
450
|
-
date = Date.new(*date)
|
449
|
+
date = Vpim.decode_date_to_date(field.value_raw)
|
451
450
|
rescue Vpim::InvalidEncodingError
|
452
|
-
|
453
|
-
raise
|
451
|
+
date = Vpim.decode_date_time_to_datetime(field.value_raw)
|
454
452
|
end
|
455
453
|
Line.new( field.group, field.name, date )
|
456
454
|
end
|
@@ -460,7 +458,9 @@ module Vpim
|
|
460
458
|
return decode_date_or_datetime(field)
|
461
459
|
|
462
460
|
rescue Vpim::InvalidEncodingError
|
463
|
-
|
461
|
+
# Hack around BDAY dates hat are correct in the month and day, but have
|
462
|
+
# some kind of garbage in the year.
|
463
|
+
if field.value =~ /^\s*(\d+)-(\d+)-(\d+)\s*$/
|
464
464
|
y = $1.to_i
|
465
465
|
m = $2.to_i
|
466
466
|
d = $3.to_i
|
@@ -502,7 +502,7 @@ module Vpim
|
|
502
502
|
end
|
503
503
|
|
504
504
|
def decode_uri(field) #:nodoc:
|
505
|
-
Line.new( field.group, field.name, Uri.new(field.value) )
|
505
|
+
Line.new( field.group, field.name, Attachment::Uri.new(field.value, nil) )
|
506
506
|
end
|
507
507
|
|
508
508
|
def decode_agent(field) #:nodoc:
|
@@ -586,7 +586,11 @@ module Vpim
|
|
586
586
|
|
587
587
|
# Return line for a field
|
588
588
|
def f2l(field) #:nodoc:
|
589
|
-
|
589
|
+
begin
|
590
|
+
Line.decode(@@decode, self, field)
|
591
|
+
rescue InvalidEncodingError
|
592
|
+
# Skip invalidly encoded fields.
|
593
|
+
end
|
590
594
|
end
|
591
595
|
|
592
596
|
# With no block, returns an Array of Line. If +name+ is specified, the
|
@@ -769,7 +773,10 @@ module Vpim
|
|
769
773
|
end
|
770
774
|
|
771
775
|
if fields.first
|
772
|
-
line =
|
776
|
+
line = begin
|
777
|
+
Line.decode(@@decode, self, fields.first)
|
778
|
+
rescue Vpim::InvalidEncodingError
|
779
|
+
end
|
773
780
|
|
774
781
|
if line
|
775
782
|
return line.value
|
@@ -955,11 +962,16 @@ module Vpim
|
|
955
962
|
|
956
963
|
## UID
|
957
964
|
|
958
|
-
# The URL value, a Uri. A wrapper around #value('
|
965
|
+
# The URL value, a Attachment::Uri. A wrapper around #value('URL').
|
959
966
|
def url
|
960
967
|
value('URL')
|
961
968
|
end
|
962
969
|
|
970
|
+
# The URL values, an Attachment::Uri. A wrapper around #values('URL').
|
971
|
+
def urls
|
972
|
+
values('URL')
|
973
|
+
end
|
974
|
+
|
963
975
|
# The VERSION multiplied by 10 as an Integer. For example, a VERSION:2.1
|
964
976
|
# vCard would have a version of 21, and a VERSION:3.0 vCard would have a
|
965
977
|
# version of 30.
|
data/lib/vpim/version.rb
CHANGED
data/samples/cmd-itip.rb
CHANGED
@@ -94,7 +94,7 @@ ARGV.each do |file|
|
|
94
94
|
puts "Organized by: #{e.organizer.to_s}"
|
95
95
|
|
96
96
|
# TODO - spec as hours/mins/secs
|
97
|
-
e.
|
97
|
+
e.occurrences.each_with_index do |t, i|
|
98
98
|
if(i < 1)
|
99
99
|
puts "At time: #{t}" +( e.duration ? " for #{Duration.secs(e.duration).to_s}" : '' )
|
100
100
|
else
|
data/samples/ics-dump.rb
CHANGED
@@ -155,7 +155,7 @@ def puts_properties(c)
|
|
155
155
|
end
|
156
156
|
|
157
157
|
begin
|
158
|
-
c.
|
158
|
+
c.occurrences.each_with_index do |t, i|
|
159
159
|
if(i < 10)
|
160
160
|
puts " #{i+1} -> #{t}"
|
161
161
|
else
|
@@ -164,7 +164,7 @@ def puts_properties(c)
|
|
164
164
|
end
|
165
165
|
end
|
166
166
|
rescue ArgumentError
|
167
|
-
# No
|
167
|
+
# No occurrences.
|
168
168
|
end
|
169
169
|
|
170
170
|
end
|
data/samples/reminder.rb
CHANGED
@@ -117,8 +117,8 @@ end
|
|
117
117
|
|
118
118
|
puts
|
119
119
|
|
120
|
-
def
|
121
|
-
e.
|
120
|
+
def start_of_first_occurrence(t0, t1, e)
|
121
|
+
e.occurrences.each_until(t1).each do |t|
|
122
122
|
# An event might start before t0, but end after it..., in which case
|
123
123
|
# we are still interested.
|
124
124
|
if (t + (e.duration || 0)) >= t0
|
@@ -129,7 +129,7 @@ def start_of_first_occurence(t0, t1, e)
|
|
129
129
|
end
|
130
130
|
|
131
131
|
all_events.sort! do |lhs, rhs|
|
132
|
-
|
132
|
+
start_of_first_occurrence(t0, t1, lhs) <=> start_of_first_occurrence(t0, t1, rhs)
|
133
133
|
end
|
134
134
|
|
135
135
|
all_events.each do |e|
|
@@ -145,7 +145,7 @@ all_events.each do |e|
|
|
145
145
|
end
|
146
146
|
|
147
147
|
i = 1
|
148
|
-
e.
|
148
|
+
e.occurrences.each_until(t1).each do |t|
|
149
149
|
# An event might start before t0, but end after it..., in which case
|
150
150
|
# we are still interested.
|
151
151
|
dstr = ''
|
data/test/test_ical.rb
CHANGED
@@ -100,6 +100,25 @@ ___
|
|
100
100
|
assert_equal(icstodo, cal.to_s)
|
101
101
|
end
|
102
102
|
|
103
|
+
# Tracker #18920
|
104
|
+
def test_recurring_todos
|
105
|
+
icstodo =<<___
|
106
|
+
BEGIN:VCALENDAR
|
107
|
+
VERSION:2.0
|
108
|
+
BEGIN:VTODO
|
109
|
+
SUMMARY:todo
|
110
|
+
DTSTART:20040415T120000
|
111
|
+
RRULE:FREQ=WEEKLY;COUNT=2
|
112
|
+
END:VTODO
|
113
|
+
END:VCALENDAR
|
114
|
+
___
|
115
|
+
|
116
|
+
cal = Icalendar.decode(icstodo).first
|
117
|
+
todo = cal.todos.first
|
118
|
+
assert(todo)
|
119
|
+
assert_equal(todo.occurrences.to_a.size, 2)
|
120
|
+
end
|
121
|
+
|
103
122
|
def test_1
|
104
123
|
req = Icalendar.decode(Req_1).first
|
105
124
|
|
data/test/test_rrule.rb
CHANGED
data/test/test_vcard.rb
CHANGED
@@ -16,7 +16,7 @@ def assert_equal_nospace(expected, got)
|
|
16
16
|
end
|
17
17
|
|
18
18
|
|
19
|
-
# Test cases: multiple
|
19
|
+
# Test cases: multiple occurrences of type
|
20
20
|
=begin
|
21
21
|
begin:VCARD
|
22
22
|
version:2.1
|
@@ -743,6 +743,65 @@ ___
|
|
743
743
|
|
744
744
|
end
|
745
745
|
|
746
|
+
def test_slash_in_field_name
|
747
|
+
cin = <<___
|
748
|
+
BEGIN:VCARD
|
749
|
+
X-messaging/xmpp-All:some@jabber.id
|
750
|
+
END:VCARD
|
751
|
+
___
|
752
|
+
|
753
|
+
card = Vpim::Vcard.decode(cin).first
|
754
|
+
assert_equal(card.value("X-messaging/xmpp-All"), "some@jabber.id")
|
755
|
+
assert_equal(card["X-messaging/xmpp-All"], "some@jabber.id")
|
756
|
+
end
|
757
|
+
|
758
|
+
def test_url_decode
|
759
|
+
cin=<<'---'
|
760
|
+
BEGIN:VCARD
|
761
|
+
URL:www.email.com
|
762
|
+
URL:www.work.com
|
763
|
+
END:VCARD
|
764
|
+
---
|
765
|
+
card = Vpim::Vcard.decode(cin).first
|
766
|
+
|
767
|
+
assert_equal("www.email.com", card.url.uri)
|
768
|
+
assert_equal("www.email.com", card.url.uri.to_s)
|
769
|
+
assert_equal("www.email.com", card.urls.first.uri)
|
770
|
+
assert_equal("www.work.com", card.urls.last.uri)
|
771
|
+
end
|
772
|
+
|
773
|
+
def test_bday_decode
|
774
|
+
cin=<<'---'
|
775
|
+
BEGIN:VCARD
|
776
|
+
BDAY:1970-07-14
|
777
|
+
END:VCARD
|
778
|
+
---
|
779
|
+
card = Vpim::Vcard.decode(cin).first
|
780
|
+
|
781
|
+
card.birthday
|
782
|
+
|
783
|
+
assert_equal(Date.new(1970, 7, 14), card.birthday)
|
784
|
+
assert_equal(1, card.values("bday").size)
|
785
|
+
|
786
|
+
# Nobody should have multiple bdays, I hope, but its allowed syntactically,
|
787
|
+
# so test it, along with some variant forms of BDAY
|
788
|
+
cin=<<'---'
|
789
|
+
BEGIN:VCARD
|
790
|
+
BDAY:1970-07-14
|
791
|
+
BDAY:70-7-14
|
792
|
+
BDAY:1970-07-15T03:45:12
|
793
|
+
BDAY:1970-07-15T03:45:12Z
|
794
|
+
END:VCARD
|
795
|
+
---
|
796
|
+
card = Vpim::Vcard.decode(cin).first
|
797
|
+
|
798
|
+
assert_equal(Date.new(1970, 7, 14), card.birthday)
|
799
|
+
assert_equal(4, card.values("bday").size)
|
800
|
+
assert_equal(Date.new(1970, 7, 14), card.values("bday").first)
|
801
|
+
assert_equal(Date.new(Time.now.year, 7, 14), card.values("bday")[1])
|
802
|
+
assert_equal(DateTime.new(1970, 7, 15, 3, 45, 12).to_s, card.values("bday")[2].to_s)
|
803
|
+
assert_equal(DateTime.new(1970, 7, 15, 3, 45, 12).to_s, card.values("bday").last.to_s)
|
804
|
+
end
|
746
805
|
|
747
806
|
def utf_name_test(c)
|
748
807
|
|
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: vpim
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: "0.
|
4
|
+
version: "0.619"
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Sam Roberts
|
@@ -9,7 +9,7 @@ autorequire:
|
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
11
|
|
12
|
-
date: 2008-03-
|
12
|
+
date: 2008-03-30 00:00:00 -07:00
|
13
13
|
default_executable:
|
14
14
|
dependencies:
|
15
15
|
- !ruby/object:Gem::Dependency
|
@@ -22,7 +22,7 @@ dependencies:
|
|
22
22
|
version: "0"
|
23
23
|
version:
|
24
24
|
description: This is a pure-ruby library for decoding and encoding vCard and iCalendar data ("personal information") called vPim.
|
25
|
-
email:
|
25
|
+
email: vieuxtech@gmail.com
|
26
26
|
executables:
|
27
27
|
- reminder
|
28
28
|
- rrule
|