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.
Files changed (61) hide show
  1. checksums.yaml +7 -0
  2. data/CHANGES +504 -0
  3. data/COPYING +58 -0
  4. data/README +182 -0
  5. data/lib/atom.rb +728 -0
  6. data/lib/plist.rb +22 -0
  7. data/lib/vpim.rb +13 -0
  8. data/lib/vpim/address.rb +219 -0
  9. data/lib/vpim/attachment.rb +102 -0
  10. data/lib/vpim/date.rb +222 -0
  11. data/lib/vpim/dirinfo.rb +277 -0
  12. data/lib/vpim/duration.rb +119 -0
  13. data/lib/vpim/enumerator.rb +32 -0
  14. data/lib/vpim/field.rb +614 -0
  15. data/lib/vpim/icalendar.rb +381 -0
  16. data/lib/vpim/maker/vcard.rb +16 -0
  17. data/lib/vpim/property/base.rb +193 -0
  18. data/lib/vpim/property/common.rb +315 -0
  19. data/lib/vpim/property/location.rb +38 -0
  20. data/lib/vpim/property/priority.rb +43 -0
  21. data/lib/vpim/property/recurrence.rb +69 -0
  22. data/lib/vpim/property/resources.rb +24 -0
  23. data/lib/vpim/repo.rb +181 -0
  24. data/lib/vpim/rfc2425.rb +367 -0
  25. data/lib/vpim/rrule.rb +591 -0
  26. data/lib/vpim/vcard.rb +1430 -0
  27. data/lib/vpim/version.rb +18 -0
  28. data/lib/vpim/vevent.rb +187 -0
  29. data/lib/vpim/view.rb +90 -0
  30. data/lib/vpim/vjournal.rb +58 -0
  31. data/lib/vpim/vpim.rb +65 -0
  32. data/lib/vpim/vtodo.rb +103 -0
  33. data/samples/README.mutt +93 -0
  34. data/samples/ab-query.rb +57 -0
  35. data/samples/cmd-itip.rb +156 -0
  36. data/samples/ex_cpvcard.rb +55 -0
  37. data/samples/ex_get_vcard_photo.rb +22 -0
  38. data/samples/ex_mkv21vcard.rb +34 -0
  39. data/samples/ex_mkvcard.rb +64 -0
  40. data/samples/ex_mkyourown.rb +29 -0
  41. data/samples/ics-dump.rb +210 -0
  42. data/samples/ics-to-rss.rb +84 -0
  43. data/samples/mutt-aliases-to-vcf.rb +45 -0
  44. data/samples/osx-wrappers.rb +86 -0
  45. data/samples/reminder.rb +203 -0
  46. data/samples/rrule.rb +71 -0
  47. data/samples/tabbed-file-to-vcf.rb +390 -0
  48. data/samples/vcf-dump.rb +86 -0
  49. data/samples/vcf-lines.rb +61 -0
  50. data/samples/vcf-to-ics.rb +22 -0
  51. data/samples/vcf-to-mutt.rb +121 -0
  52. data/test/test_all.rb +17 -0
  53. data/test/test_date.rb +120 -0
  54. data/test/test_dur.rb +41 -0
  55. data/test/test_field.rb +156 -0
  56. data/test/test_ical.rb +415 -0
  57. data/test/test_repo.rb +158 -0
  58. data/test/test_rrule.rb +1030 -0
  59. data/test/test_vcard.rb +973 -0
  60. data/test/test_view.rb +79 -0
  61. metadata +117 -0
@@ -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
@@ -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
+