vpim 0.604 → 0.619
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.
- 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
|