vpim 0.357 → 0.360

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.
@@ -47,77 +47,31 @@ module Vpim
47
47
  # a field.
48
48
  #
49
49
  # Example:
50
+ #
50
51
  # ORGANIZER;CN="A. Person":mailto:a_person@example.com
52
+ #
51
53
  # ATTENDEE;ROLE=REQ-PARTICIPANT;PARTSTAT=NEEDS-ACTION
52
54
  # ;CN="Sam Roberts";RSVP=TRUE:mailto:SRoberts@example.com
53
55
  #
54
56
  class Address
55
57
 
56
- # Create an Address from a DirectoryInfo::Field, +field+.
57
- #
58
- # TODO - make private, and split into the encode/decode/create trinity.
59
- def initialize(field)
60
- unless field.value
61
- raise ArgumentError
62
- end
63
-
64
- @field = field
65
- end
66
-
67
- # Return a representation of this Address as a DirectoryInfo::Field.
68
- def field
69
- @field.copy
70
- end
71
-
72
58
  # Create a copy of Address. If the original Address was frozen, this one
73
59
  # won't be.
74
60
  def copy
75
- Marshal.load(Marshal.dump(self))
61
+ #Marshal.load(Marshal.dump(self))
62
+ self.dup.dirty
76
63
  end
77
64
 
78
- # Addresses in a CAL-ADDRESS are represented as a URI, usually a mailto URI.
79
- def uri
80
- @field.value
81
- end
82
-
83
- # Return true if the +uri+ is == to this address' URI. The comparison
84
- # is case-insensitive.
85
- #
86
- # FIXME - why case insensitive? Email addresses. Should use a URI library
87
- # if I can find one and it knows how to do URI comparisons.
88
- def ==(uri)
89
- Vpim::Methods.casecmp?(self.uri.to_str, uri.to_str)
90
- end
91
-
92
- # The common or displayable name associated with the calendar address,
93
- # or nil if there is none.
94
- def cn
95
- return nil unless n = @field.param('CN')
96
-
97
- # FIXME = the CN param may have no value, which is an error, but don't try
98
- # to decode it, return either nil, or InvalidEncoding
99
- Vpim.decode_text(n.first)
100
- end
101
-
102
- # A string representation of an address, using the common name, and the
103
- # URI. The URI protocol is stripped if it's "mailto:".
104
- #
105
- # TODO - this needs to properly escape the cn string!
106
- def to_s
107
- u = uri
108
- u.gsub!(/^mailto: */i, '')
109
-
110
- if cn
111
- "\"#{cn}\" <#{uri}>"
112
- else
113
- uri
114
- end
115
- end
116
-
117
- def inspect
118
- "#<Vpim::Icalendar::Address:cn=#{cn.inspect} status=#{partstat} rsvp?=#{rsvp} #{uri.inspect}>"
65
+ def dirty #:nodoc:
66
+ @field = nil
67
+ self
119
68
  end
120
69
 
70
+ # Addresses in a CAL-ADDRESS are represented as a URI, usually a mailto URI.
71
+ attr_accessor :uri
72
+ # The common or displayable name associated with the calendar address, or
73
+ # nil if there is none.
74
+ attr_accessor :cn
121
75
  # The participation role for the calendar user specified by the address.
122
76
  #
123
77
  # The standard roles are:
@@ -128,11 +82,7 @@ module Vpim
128
82
  #
129
83
  # The default role is REQ-PARTICIPANT, returned if no ROLE parameter was
130
84
  # specified.
131
- def role
132
- return 'REQ-PARTICIPANT' unless r = @field.param('ROLE')
133
- r.first.upcase
134
- end
135
-
85
+ attr_accessor :role
136
86
  # The participation status for the calendar user specified by the
137
87
  # property PARTSTAT, a String.
138
88
  #
@@ -145,36 +95,124 @@ module Vpim
145
95
  #
146
96
  # Default is NEEDS-ACTION.
147
97
  #
148
- # TODO - make the default depend on the component type.
149
- def partstat
150
- return 'NEEDS-ACTION' unless r = @field.param('PARTSTAT')
151
- r.first.upcase
98
+ # FIXME - make the default depend on the component type.
99
+ attr_accessor :partstat
100
+ # The value of the RSVP field, either +true+ or +false+. It is used to
101
+ # specify whether there is an expectation of a reply from the calendar
102
+ # user specified by the property value.
103
+ attr_accessor :rsvp
104
+
105
+ def initialize(field=nil) #:nodoc:
106
+ @field = field
107
+ @uri = ''
108
+ @cn = ''
109
+ @role = "REQ-PARTICIPANT"
110
+ @partstat = "NEEDS-ACTION"
111
+ @rsvp = false
152
112
  end
153
113
 
154
- # Set or change the participation status of the address, the PARTSTAT,
155
- # to +status+.
156
- #
157
- # See #partstat.
158
- def partstat=(status)
159
- @field['PARTSTAT'] = status.to_str
160
- status
114
+ # Create a new Address. It will encode as a +name+ property.
115
+ def self.create(uri='')
116
+ adr = new
117
+ adr.uri = uri.to_str
118
+ adr
161
119
  end
162
120
 
163
- # The value of the RSVP field, either +true+ or +false+. It is used to
164
- # specify whether there is an expectation of a favor of a reply from the
165
- # calendar user specified by the property value.
166
- #
167
- # TODO - should be #rsvp?
168
- def rsvp
169
- return false unless r = @field.param('RSVP')
170
- r = r.first
171
- return false unless r
172
- case r
173
- when /TRUE/i then true
174
- when /FALSE/i then false
175
- else raise InvalidEncodingError, "RSVP param value not TRUE/FALSE: #{r}"
121
+ def self.decode(field)
122
+ adr = new(field)
123
+ adr.uri = field.value
124
+
125
+ cn = field.param('CN')
126
+
127
+ if cn
128
+ adr.cn = cn.first
129
+ end
130
+
131
+ role = field.param('ROLE')
132
+
133
+ if role
134
+ adr.role = role.first.strip.upcase
135
+ end
136
+
137
+ partstat = field.param('PARTSTAT')
138
+
139
+ if partstat
140
+ adr.partstat = partstat.first.strip.upcase
141
+ end
142
+
143
+ rsvp = field.param('RSVP')
144
+
145
+ if rsvp
146
+ adr.rsvp = case rsvp.first
147
+ when /TRUE/i then true
148
+ when /FALSE/i then false
149
+ else raise InvalidEncodingError, "RSVP param value not TRUE/FALSE: #{rsvp}"
150
+ end
151
+ end
152
+
153
+ adr.freeze
154
+ end
155
+
156
+ # Return a representation of this Address as a DirectoryInfo::Field.
157
+ def encode(name) #:nodoc:
158
+ if @field
159
+ # FIXME - set the field name, it could be different from cached
160
+ return @field
161
+ end
162
+
163
+ value = uri.to_str.strip
164
+
165
+ if value.empty?
166
+ raise Uencodeable, "Address#uri is zero-length"
167
+ end
168
+
169
+ params = {}
170
+
171
+ if cn.length > 0
172
+ params['CN'] = Vpim::encode_paramvalue(cn)
173
+ end
174
+
175
+ # FIXME - default value is different for non-vEvent
176
+ if role.length > 0 && role != 'REQ-PARTICIPANT'
177
+ params['ROLE'] = Vpim::encode_paramtext(role)
178
+ end
179
+
180
+ # FIXME - default value is different for non-vEvent
181
+ if partstat.length > 0 && partstat != 'NEEDS-ACTION'
182
+ params['PARTSTAT'] = Vpim::encode_paramtext(partstat)
183
+ end
184
+
185
+ if rsvp
186
+ params['RSVP'] = 'true'
187
+ end
188
+
189
+ Vpim::DirectoryInfo::Field.create(name, value, params)
190
+ end
191
+
192
+ # Return true if the +uri+ is == to this address' URI. The comparison
193
+ # is case-insensitive (because email addresses and domain names are).
194
+ def ==(uri)
195
+ # TODO - could I use a URI library?
196
+ Vpim::Methods.casecmp?(self.uri.to_str, uri.to_str)
197
+ end
198
+
199
+ # A string representation of an address, using the common name, and the
200
+ # URI. The URI protocol is stripped if it's "mailto:".
201
+ def to_s
202
+ u = uri
203
+ u = u.gsub(/^mailto: */i, '')
204
+
205
+ if cn.length > 0
206
+ "#{cn.inspect} <#{uri}>"
207
+ else
208
+ uri
176
209
  end
177
210
  end
211
+
212
+ def inspect #:nodoc:
213
+ "#<Vpim::Icalendar::Address:cn=#{cn.inspect} status=#{partstat} rsvp=#{rsvp} #{uri.inspect}>"
214
+ end
215
+
178
216
  end
179
217
  end
180
218
  end
@@ -33,10 +33,12 @@ module Vpim
33
33
  # (iTIP) Scheduling Events, BusyTime, To-dos and Journal Entries
34
34
  # - link:rfc2447.txt: iCalendar Message-Based Interoperability Protocol
35
35
  #
36
- # iCalendar (RFC 2445) is based on vCalendar, but does not appear to be
37
- # altogether compatible. iCalendar files have VERSION:2.0 and vCalendar have
38
- # VERSION:1.0. While much appears to be similar, the recurrence rule syntax,
39
- # at least, is completely different.
36
+ # = iCalendar and vCalendar
37
+ #
38
+ # iCalendar files have VERSION:2.0 and vCalendar have VERSION:1.0. iCalendar
39
+ # (RFC 2445) is based on vCalendar, but but is not very compatible. While
40
+ # much appears to be similar, the recurrence rule syntax is completely
41
+ # different.
40
42
  #
41
43
  # iCalendars are usually transmitted in files with <code>.ics</code>
42
44
  # extensions.
@@ -88,18 +90,37 @@ module Vpim
88
90
  end
89
91
  end
90
92
 
93
+ # Add and event to this calendar.
94
+ #
95
+ # Yields an event maker, Icalendar::Vevent::Maker.
96
+ def add_event(&block) #:yield:event
97
+ push Vevent::Maker.make( &block )
98
+ end
99
+
100
+ # FIXME - could take mandatory fields as an arguments
101
+ # FIXME - args: support PRODID
102
+ # FIXME - yield an Icalendar::Maker if block provided
103
+ # FIXME - maker#prodid=
104
+ def Icalendar.create2(args = nil)
105
+ # FIXME - make the primary API
106
+ di = DirectoryInfo.create( [ DirectoryInfo::Field.create('VERSION', '2.0') ], 'VCALENDAR' )
107
+
108
+ di.push_unique DirectoryInfo::Field.create('PRODID', Vpim::PRODID)
109
+ di.push_unique DirectoryInfo::Field.create('CALSCALE', "Gregorian")
110
+
111
+ new(di.to_a)
112
+ end
113
+
91
114
  # Create a new Icalendar object with the minimal set of fields for a valid
92
115
  # Calendar. If specified, +fields+ must be an array of
93
116
  # DirectoryInfo::Field objects to add. They can override the the default
94
117
  # Calendar fields, so, for example, this can be used to set a custom PRODID field.
95
- #
96
- # TODO - allow hash args like Vevent.create
97
118
  def Icalendar.create(fields=[])
98
119
  di = DirectoryInfo.create( [ DirectoryInfo::Field.create('VERSION', '2.0') ], 'VCALENDAR' )
99
120
 
100
121
  DirectoryInfo::Field.create_array(fields).each { |f| di.push_unique f }
101
122
 
102
- di.push_unique DirectoryInfo::Field.create('PRODID', "-//Ensemble Independant//vPim #{Vpim.version}//EN")
123
+ di.push_unique DirectoryInfo::Field.create('PRODID', Vpim::PRODID)
103
124
  di.push_unique DirectoryInfo::Field.create('CALSCALE', "Gregorian")
104
125
 
105
126
  new(di.to_a)
@@ -124,10 +145,16 @@ module Vpim
124
145
  Icalendar.create(fields)
125
146
  end
126
147
 
148
+ # Used during encoding.
149
+ def fields # :nodoc:
150
+ f = @properties.to_a
151
+ last = f.pop
152
+ @components.each { |c| f << c.fields }
153
+ f.push last
154
+ end
155
+
127
156
  # Encode the Calendar as a string. The width is the maximum width of the
128
157
  # encoded lines, it can be specified, but is better left to the default.
129
- #
130
- # TODO - only does top-level now, needs to add the events/todos/etc.
131
158
  def encode(width=nil)
132
159
  # We concatenate the fields of all objects, create a DirInfo, then
133
160
  # encode it.
@@ -135,17 +162,6 @@ module Vpim
135
162
  di.encode(width)
136
163
  end
137
164
 
138
- # Used during encoding.
139
- def fields # :nodoc:
140
- fields = @properties.to_a
141
-
142
- last = fields.pop
143
-
144
- @components.each { |c| fields << c.fields }
145
-
146
- fields << last
147
- end
148
-
149
165
  alias to_s encode
150
166
 
151
167
  # Push a calendar component onto the calendar.
@@ -156,6 +172,7 @@ module Vpim
156
172
  else
157
173
  raise ArgumentError, "can't add a #{component.type} to a calendar"
158
174
  end
175
+ self
159
176
  end
160
177
 
161
178
  # Check if the protocol method is +method+
@@ -8,10 +8,88 @@
8
8
 
9
9
  module Vpim
10
10
  class Icalendar
11
+ module Set #:nodoc:
12
+ module Util #:nodoc:
13
+
14
+ def rm_all(name)
15
+ rm = @comp.properties.select { |f| f.name? name }
16
+ rm.each { |f| @comp.properties.delete(f) }
17
+ end
18
+
19
+ def set_token(name, allowed, default, value) #:nodoc:
20
+ value = value.to_str
21
+ unless allowed.include?(value)
22
+ raise Vpim::Unencodeable, "Invalid #{name} value '#{value}'"
23
+ end
24
+ rm_all(name)
25
+ unless value == default
26
+ @comp.properties.push Vpim::DirectoryInfo::Field.create(name, value)
27
+ end
28
+ end
29
+
30
+ def field_create(name, value, default_value_type = nil, value_type = nil, params = {})
31
+ if value_type && value_type != default_value_type
32
+ params['VALUE'] = value_type
33
+ end
34
+ Vpim::DirectoryInfo::Field.create(name, value, params)
35
+ end
36
+
37
+ def set_date_or_datetime(name, default, value)
38
+ f = nil
39
+ case value
40
+ when Date
41
+ f = field_create(name, Vpim.encode_date(value), default, 'DATE')
42
+ when Time
43
+ f = field_create(name, Vpim.encode_date_time(value), default, 'DATE-TIME')
44
+ else
45
+ raise Vpim::Unencodeable, "Invalid #{name} value #{value.inspect}"
46
+ end
47
+ rm_all(name)
48
+ @comp.properties.push(f)
49
+ end
50
+
51
+ def set_datetime(name, value)
52
+ f = field_create(name, Vpim.encode_date_time(value))
53
+ rm_all(name)
54
+ @comp.properties.push(f)
55
+ end
56
+
57
+ def set_text(name, value)
58
+ f = field_create(name, Vpim.encode_text(value))
59
+ rm_all(name)
60
+ @comp.properties.push(f)
61
+ end
62
+
63
+ def set_text_list(name, value)
64
+ f = field_create(name, Vpim.encode_text_list(value))
65
+ rm_all(name)
66
+ @comp.properties.push(f)
67
+ end
68
+
69
+ def set_integer(name, value)
70
+ value = value.to_int.to_s
71
+ f = field_create(name, value)
72
+ rm_all(name)
73
+ @comp.properties.push(f)
74
+ end
75
+
76
+ def add_address(name, value)
77
+ f = value.encode(name)
78
+ @comp.properties.push(f)
79
+ end
80
+
81
+ def set_address(name, value)
82
+ rm_all(name)
83
+ add_address(name, value)
84
+ end
85
+
86
+ end
87
+ end
88
+
11
89
  module Property #:nodoc:
12
90
 
13
- # FIXME - these should be part of Dirinfo
14
- module Base
91
+ # FIXME - rename Base to Util
92
+ module Base #:nodoc:
15
93
  # Value of first property with name +name+
16
94
  def propvalue(name) #:nodoc:
17
95
  prop = @properties.detect { |f| f.name? name }
@@ -78,7 +78,7 @@ module Vpim
78
78
  organizer = @properties.field('ORGANIZER')
79
79
 
80
80
  if organizer
81
- organizer = Icalendar::Address.new(organizer)
81
+ organizer = Icalendar::Address.decode(organizer)
82
82
  end
83
83
 
84
84
  organizer.freeze
@@ -128,7 +128,7 @@ seq
128
128
  # attendees are objects of Icalendar::Address. If +uri+ is specified
129
129
  # only the return the attendees with this +uri+.
130
130
  def attendees(uri = nil)
131
- attendees = @properties.enum_by_name('ATTENDEE').map { |a| Icalendar::Address.new(a).freeze }
131
+ attendees = @properties.enum_by_name('ATTENDEE').map { |a| Icalendar::Address.decode(a) }
132
132
  attendees.freeze
133
133
  if uri
134
134
  attendees.select { |a| a == uri }
@@ -174,6 +174,141 @@ seq
174
174
  end
175
175
 
176
176
  end
177
+
178
+ module Set
179
+
180
+ # Properties common to Vevent, Vtodo, and Vjournal.
181
+ module Common
182
+
183
+ # Set the access class of the component, see Icalendar::Get::Common#access_class.
184
+ def access_class(token)
185
+ set_token 'CLASS', ["PUBLIC", "PRIVATE", "CONFIDENTIAL"], "PUBLIC", token
186
+ end
187
+
188
+ # Set the creation time, see Icalendar::Get::Common#created
189
+ def created(time)
190
+ set_datetime 'CREATED', time
191
+ end
192
+
193
+ # Set the description, see Icalendar::Get::Common#description.
194
+ def description(text)
195
+ set_text 'DESCRIPTION', text
196
+ end
197
+
198
+ # Set the sequence number, see Icalendar::Get::Common#sequence.
199
+ # is no SEQUENCE; property.
200
+ def sequence(int)
201
+ set_integer 'SEQUENCE', int
202
+ end
203
+
204
+ # Set the timestamp, see Icalendar::Get::Common#timestamp.
205
+ def dtstamp(time)
206
+ set_datetime 'DTSTAMP', time
207
+ self
208
+ end
209
+
210
+ # The start time or date, see Icalendar::Get::Common#dtstart.
211
+ def dtstart(start)
212
+ set_date_or_datetime 'DTSTART', 'DATE-TIME', start
213
+ self
214
+ end
215
+
216
+ # Set the last modification time, see Icalendar::Get::Common#lastmod.
217
+ def lastmod(time)
218
+ set_datetime 'LAST-MODIFIED', time
219
+ self
220
+ end
221
+
222
+ # Set the event organizer, an Icalendar::Address, see Icalendar::Get::Common#organizer.
223
+ #
224
+ # Without an +adr+ it yields an Icalendar::Address that is a copy of
225
+ # the current organizer (if any), allowing it to be modified.
226
+ def organizer(adr=nil) #:yield: organizer
227
+ unless adr
228
+ adr = @comp.organizer
229
+ if adr
230
+ adr = adr.copy
231
+ else
232
+ adr = Icalendar::Address.create
233
+ end
234
+ yield adr
235
+ end
236
+ set_address('ORGANIZER', adr)
237
+ self
238
+ end
239
+
240
+ =begin
241
+ # Status values are not rejected during decoding. However, if the
242
+ # status is requested, and it's value is not one of the defined
243
+ # allowable values, an exception is raised.
244
+ def status
245
+ case self
246
+ when Vpim::Icalendar::Vevent
247
+ proptoken 'STATUS', ['TENTATIVE', 'CONFIRMED', 'CANCELLED']
248
+
249
+ when Vpim::Icalendar::Vtodo
250
+ proptoken 'STATUS', ['NEEDS-ACTION', 'COMPLETED', 'IN-PROCESS', 'CANCELLED']
251
+
252
+ when Vpim::Icalendar::Vevent
253
+ proptoken 'STATUS', ['DRAFT', 'FINAL', 'CANCELLED']
254
+ end
255
+ end
256
+ =end
257
+
258
+ # Set summary description of component, see Icalendar::Get::Common#summary.
259
+ def summary(text)
260
+ set_text 'SUMMARY', text
261
+ end
262
+
263
+ # Set the unique identifier of this calendar component, see Icalendar::Get::Common#uid.
264
+ def uid(uid)
265
+ set_text 'UID', uid
266
+ end
267
+
268
+ def url(url)
269
+ set_text 'URL', url
270
+ end
271
+
272
+ # Add an attendee Address, see Icalendar::Get::Common#attendees.
273
+ def add_attendee(adr)
274
+ add_address('ATTENDEE', adr)
275
+ end
276
+
277
+ # Set the categories, see Icalendar::Get::Common#attendees.
278
+ #
279
+ # If +cats+ is provided, the categories are set to cats, either a
280
+ # String or an Array of String. Otherwise, and array of the existing
281
+ # category strings is yielded, and it can be modified.
282
+ def categories(cats = nil) #:yield: categories
283
+ unless cats
284
+ cats = @comp.categories
285
+ yield cats
286
+ end
287
+ # TODO - strip the strings
288
+ set_text_list('CATEGORIES', cats)
289
+ end
290
+
291
+ # Set the comment, see Icalendar::Get::Common#comment.
292
+ def comment(value)
293
+ set_text 'COMMENT', value
294
+ end
295
+
296
+ =begin
297
+ def contacts
298
+ proptextarray 'CONTACT'
299
+ end
300
+
301
+ # An Array of attachments, see Attachment for more information.
302
+ def attachments
303
+ @properties.enum_by_name('ATTACH').map do |f|
304
+ attachment = Attachment.decode(f, 'uri', 'FMTTYPE')
305
+ end
306
+ end
307
+ =end
308
+
309
+ end
310
+
311
+ end
177
312
  end
178
313
  end
179
314
 
@@ -56,6 +56,14 @@ module Vpim
56
56
 
57
57
  # integer = (["+"] / "-") 1*DIGIT
58
58
  INTEGER = '[-+]?\d+'
59
+
60
+ # QSAFE-CHAR = WSP / %x21 / %x23-7E / NON-US-ASCII
61
+ # ; Any character except CTLs and DQUOTE
62
+ QSAFECHAR = '[ \t\x21\x23-\x7e\x80-\xff]'
63
+
64
+ # SAFE-CHAR = WSP / %x21 / %x23-2B / %x2D-39 / %x3C-7E / NON-US-ASCII
65
+ # ; Any character except CTLs, DQUOTE, ";", ":", ","
66
+ SAFECHAR = '[ \t\x21\x23-\x2b\x2d-\x39\x3c-\x7e\x80-\xff]'
59
67
  end
60
68
  end
61
69
 
@@ -232,8 +240,13 @@ module Vpim
232
240
  end
233
241
  end
234
242
 
243
+ # v is an Array of String, or just a single String
235
244
  def Vpim.encode_text_list(v, sep = ",") #:nodoc:
236
- v.to_ary.map{ |t| Vpim.encode_text(t) }.join(sep)
245
+ begin
246
+ v.to_ary.map{ |t| Vpim.encode_text(t) }.join(sep)
247
+ rescue
248
+ Vpim.encode_text(v)
249
+ end
237
250
  end
238
251
 
239
252
  # Convert a +sep+-seperated list of TEXT values into an array of values.
@@ -248,6 +261,29 @@ module Vpim
248
261
  list
249
262
  end
250
263
 
264
+ # param-value = paramtext / quoted-string
265
+ # paramtext = *SAFE-CHAR
266
+ # quoted-string = DQUOTE *QSAFE-CHAR DQUOTE
267
+ def Vpim.encode_paramtext(value)
268
+ case value
269
+ when %r{\A#{Bnf::SAFECHAR}*\z}
270
+ value
271
+ else
272
+ raise Vpim::Unencodable, "paramtext #{value.inspect}"
273
+ end
274
+ end
275
+
276
+ def Vpim.encode_paramvalue(value)
277
+ case value
278
+ when %r{\A#{Bnf::SAFECHAR}*\z}
279
+ value
280
+ when %r{\A#{Bnf::QSAFECHAR}*\z}
281
+ '"' + value + '"'
282
+ else
283
+ raise Vpim::Unencodable, "param-value #{value.inspect}"
284
+ end
285
+ end
286
+
251
287
 
252
288
  # Unfold the lines in +card+, then return an array of one Field object per
253
289
  # line.
@@ -1078,19 +1078,12 @@ module Vpim
1078
1078
 
1079
1079
  public
1080
1080
 
1081
- # def add_name # :yield: n
1082
- # # FIXME: Each attribute can currently only have a single String value.
1083
- # # FIXME: Need to escape specials in the String.
1084
- # x = Struct.new(:family, :given, :additional, :prefix, :suffix).new
1085
- # yield x
1086
- # @card << Vpim::DirectoryInfo::Field.create(
1087
- # 'N',
1088
- # x.map { |s| s ? s.to_str : '' }
1089
- # )
1090
- # self
1091
- # end
1092
- # Set with #name now.
1093
- # Use m.name do |n| n.fullname = foo end
1081
+ # Deprecated, see #name.
1082
+ #
1083
+ # Use
1084
+ # maker.name do |n| n.fullname = "foo" end
1085
+ # to set just fullname, or set the other fields to set fullname and the
1086
+ # name.
1094
1087
  def fullname=(fullname) #:nodoc: bacwards compat
1095
1088
  if @card.field('FN')
1096
1089
  raise Vpim::InvalidEncodingError, "Not allowed to add more than one FN field to a vCard."
@@ -7,9 +7,9 @@
7
7
  =end
8
8
 
9
9
  module Vpim
10
- PRODID = '-//Ensemble Independent//vPim 0.357//EN'
10
+ PRODID = '-//Ensemble Independent//vPim 0.360//EN'
11
11
 
12
- VERSION = '0.357'
12
+ VERSION = '0.360'
13
13
 
14
14
  # Return the API version as a string.
15
15
  def Vpim.version
@@ -20,6 +20,7 @@ require 'vpim/property/recurrence'
20
20
  module Vpim
21
21
  class Icalendar
22
22
  class Vevent
23
+
23
24
  include Vpim::Icalendar::Property::Base
24
25
  include Vpim::Icalendar::Property::Common
25
26
  include Vpim::Icalendar::Property::Priority
@@ -37,6 +38,19 @@ module Vpim
37
38
  # See "TODO - fields" in dirinfo.rb
38
39
  end
39
40
 
41
+ # TODO - derive everything from Icalendar::Component to get this kind of stuff?
42
+ def fields #:nodoc:
43
+ f = @properties.to_a
44
+ last = f.pop
45
+ f.push @elements
46
+ f.push last
47
+ end
48
+
49
+ def properties #:nodoc:
50
+ @properties
51
+ end
52
+
53
+
40
54
  # Create a new Vevent object. All events must have a DTSTART field,
41
55
  # specify it as either a Time or a Date in +start+, it defaults to "now"
42
56
  # (is this useful?).
@@ -71,10 +85,12 @@ module Vpim
71
85
 
72
86
  # Accept an event invitation. The +invitee+ is the Address that wishes
73
87
  # to accept the event invitation as confirmed.
88
+ #
89
+ # The event created is identical to this one, but
90
+ # - without the attendees
91
+ # - with the invitee added with a PARTSTAT of ACCEPTED
74
92
  def accept(invitee)
75
- # The event created is identical to this one, but
76
- # - without the attendees
77
- # - with the invitee added with a PARTSTAT of ACCEPTED
93
+ # FIXME - move to Vpim::Itip.
78
94
  invitee = invitee.copy
79
95
  invitee.partstat = 'ACCEPTED'
80
96
 
@@ -84,7 +100,7 @@ module Vpim
84
100
  |f,i|
85
101
 
86
102
  # put invitee in as field[1]
87
- fields << invitee.field if i == 1
103
+ fields << invitee.encode('ATTENDEE') if i == 1
88
104
 
89
105
  fields << f unless f.name? 'ATTENDEE'
90
106
  end
@@ -92,11 +108,8 @@ module Vpim
92
108
  Vevent.new(fields)
93
109
  end
94
110
 
95
- =begin
96
- # Set the start time for the event to +start+, a Time object.
97
- # TODO - def dtstart=(start) ... start should be allowed to be Time/Date/DateTime
98
- =end
99
-
111
+ # In iTIP, whether this event is OPAQUE or TRANSPARENT to scheduling. If
112
+ # transparency is not explicitly set, it defaults to OPAQUE.
100
113
  def transparency
101
114
  proptoken 'TRANSP', ["OPAQUE", "TRANSPARENT"], "OPAQUE"
102
115
  end
@@ -135,6 +148,57 @@ module Vpim
135
148
  end
136
149
  end
137
150
 
151
+ # Make a new Vevent, or make changes to an existing Vevent.
152
+ class Maker
153
+ # TODO - should I automatically set
154
+ # #created
155
+ # #dtstamp
156
+ # #sequence
157
+ # ...?
158
+ #
159
+ # Many have pretty specific meanings in iTIP, perhaps I should leave
160
+ # them alone.
161
+ include Vpim::Icalendar::Set::Util #:nodoc:
162
+ include Vpim::Icalendar::Set::Common
163
+
164
+ # The event that changes are being made to.
165
+ attr_reader :event
166
+
167
+ def initialize(event) #:nodoc:
168
+ @event = event
169
+ @comp = event
170
+ end
171
+
172
+ # Make changes to +event+. If +event+ is not specified, creates a new
173
+ # event. Yields a Vevent::Maker, and returns +event+.
174
+ def self.make(event = Vpim::Icalendar::Vevent.create) #:yield:maker
175
+ m = self.new(event)
176
+ yield m
177
+ m.event
178
+ end
179
+
180
+ # Set transparency to "OPAQUE" or "TRANSPARENT", see Vpim::Vevent#transparency.
181
+ def transparency(token)
182
+ set_token 'TRANSP', ["OPAQUE", "TRANSPARENT"], "OPAQUE", token
183
+ end
184
+
185
+ # Set end for events with fixed durations. +end+ can be a Date or Time
186
+ def dtend(dtend)
187
+ set_date_or_datetime 'DTEND', 'DATE-TIME', dtend
188
+ end
189
+
190
+ # Yields a selector that allows the duration to be set.
191
+ #
192
+ # TODO - syntax is:
193
+ # dur-value = (["+"] / "-") "P" (dur-date / dur-time / dur-week)
194
+ # dur-date = dur-day [ "T" (dur-hour / dur-minute / dur-second) ]
195
+ # dur-time = "T" (dur-hour / dur-minute / dur-second)
196
+ # dur-week = 1*DIGIT "W"
197
+ def duration(dur) #:yield:selector
198
+ raise Vpim::Unsupported
199
+ end
200
+ end
201
+
138
202
  end
139
203
  end
140
204
  end
metadata CHANGED
@@ -3,15 +3,15 @@ rubygems_version: 0.8.11
3
3
  specification_version: 1
4
4
  name: vpim
5
5
  version: !ruby/object:Gem::Version
6
- version: "0.357"
7
- date: 2006-03-31 00:00:00 -08:00
8
- summary: a library to manipulate vCards and iCalendars
6
+ version: "0.360"
7
+ date: 2006-04-02 00:00:00 -08:00
8
+ summary: iCalendar and vCard support for ruby
9
9
  require_paths:
10
10
  - lib
11
11
  email: sroberts@uniserve.com
12
12
  homepage: http://vpim.rubyforge.org
13
- rubyforge_project:
14
- description:
13
+ rubyforge_project: vpim
14
+ description: This is a pure-ruby library for decoding and encoding vCard and iCalendar data ("personal information") called vPim.
15
15
  autorequire: vpim
16
16
  default_executable:
17
17
  bindir: bin