Class: Vpim::Icalendar
- Inherits:
-
Object
- Object
- Vpim::Icalendar
- Includes:
- Enumerable, Vpim
- Defined in:
- lib/vpim/icalendar.rb,
lib/vpim/vtodo.rb,
lib/vpim/vevent.rb,
lib/vpim/address.rb,
lib/vpim/vjournal.rb,
lib/vpim/property/base.rb,
lib/vpim/property/common.rb,
lib/vpim/property/location.rb,
lib/vpim/property/priority.rb,
lib/vpim/property/resources.rb,
lib/vpim/property/recurrence.rb
Overview
An iCalendar.
A Calendar is some meta-information followed by a sequence of components.
Defined components are Event, Todo, Freebusy, Journal, and Timezone, each of which are represented by their own class, though they share many properties in common. For example, Event and Todo may both contain multiple Alarm components.
Reference
The iCalendar format is specified by a series of IETF documents:
-
RFC2445: Internet Calendaring and Scheduling Core Object Specification
-
RFC2446: iCalendar Transport-Independent Interoperability Protocol (iTIP) Scheduling Events, BusyTime, To-dos and Journal Entries
-
RFC2447: iCalendar Message-Based Interoperability Protocol
iCalendar and vCalendar
iCalendar files have VERSION:2.0 and vCalendar have VERSION:1.0. iCalendar (RFC 2445) is based on vCalendar, but is not very compatible. While much appears to be similar, the recurrence rule syntax is completely different.
iCalendars are usually transmitted in files with .ics
extensions.
Defined Under Namespace
Modules: Bnf, Property, Set Classes: Address, Vevent, Vjournal, Vtodo
Constant Summary
Constants included from Vpim
Class Method Summary collapse
-
.create(fields = []) ⇒ Object
Create a new Icalendar object with the minimal set of fields for a valid Calendar.
-
.create2(producer = Vpim::PRODID) ⇒ Object
The producer ID defaults to Vpim::PRODID but you can set it to something specific to your application.
-
.create_reply(fields = []) ⇒ Object
Create a new Icalendar object with a protocol method of REPLY.
-
.decode(cal, e = nil) ⇒ Object
Decode iCalendar data into an array of Icalendar objects.
-
.decode_duration(str) ⇒ Object
:nodoc:.
Instance Method Summary collapse
-
#add_event(&block) ⇒ Object
Add an event to this calendar.
-
#add_todo(&block) ⇒ Object
Add a task/todo to this calendar.
-
#calscale ⇒ Object
The value of the CALSCALE: property, or “GREGORIAN” if CALSCALE: is not present.
-
#components(klass = Object) ⇒ Object
The array of all supported calendar components.
-
#each(klass = nil, &block) ⇒ Object
Enumerate the top-level calendar components.
-
#encode(width = nil) ⇒ Object
(also: #to_s)
Encode the Calendar as a string.
-
#events(&block) ⇒ Object
Short-hand for #each(Icalendar::Vevent).
-
#fields ⇒ Object
Used during encoding.
-
#initialize(fields) ⇒ Icalendar
constructor
Create a new Icalendar object from
fields
, an array of DirectoryInfo::Field objects. -
#journals(&block) ⇒ Object
Short-hand for #each(Icalendar::Vjournal).
-
#producer ⇒ Object
The value of the PRODID field, an unstructured string meant to identify the software which encoded the Calendar data.
-
#protocol ⇒ Object
The value of the METHOD field.
-
#protocol?(method) ⇒ Boolean
Check if the protocol method is
method
. -
#push(component) ⇒ Object
(also: #<<)
Push a calendar component onto the calendar.
-
#todos(&block) ⇒ Object
Short-hand for #each(Icalendar::Vtodo).
-
#version ⇒ Object
The iCalendar version multiplied by 10 as an Integer.
Methods included from Vpim
array_datetime_to_time, decode_date, decode_date_list, decode_date_time, decode_date_time_list, decode_date_time_to_datetime, decode_date_to_date, decode_integer, decode_list, decode_text, decode_text_list, decode_time, decode_time_list, decode_time_to_time, encode_date, encode_date_time, encode_paramtext, encode_paramvalue, encode_text, encode_text_list, encode_time, expand, outer_inner, unfold, version
Constructor Details
#initialize(fields) ⇒ Icalendar
Create a new Icalendar object from fields
, an array of DirectoryInfo::Field objects.
When decoding Calendar data, you would usually use Icalendar.decode(), which decodes the data into the field arrays, and calls this method for each Calendar it finds.
69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 |
# File 'lib/vpim/icalendar.rb', line 69 def initialize(fields) #:nodoc: # seperate into the outer-level fields, and the arrays of component # fields outer, inner = Vpim.outer_inner(fields) # Make a dirinfo out of outer, and check its an iCalendar @properties = DirectoryInfo.create(outer) @properties.check_begin_end('VCALENDAR') @components = [] # could use #constants instead of this factory = { 'VEVENT' => Vevent, 'VTODO' => Vtodo, 'VJOURNAL' => Vjournal, # TODO - VTIMEZONE } inner.each do |component| name = component.first.value if klass = factory[name] @components << klass.new(component) end end end |
Class Method Details
.create(fields = []) ⇒ Object
Create a new Icalendar object with the minimal set of fields for a valid Calendar. If specified, fields
must be an array of DirectoryInfo::Field objects to add. They can override the the default Calendar fields, so, for example, this can be used to set a custom PRODID field.
147 148 149 150 151 152 153 154 155 156 |
# File 'lib/vpim/icalendar.rb', line 147 def Icalendar.create(fields=[]) di = DirectoryInfo.create( [ DirectoryInfo::Field.create('VERSION', '2.0') ], 'VCALENDAR' ) DirectoryInfo::Field.create_array(fields).each { |f| di.push_unique f } di.push_unique DirectoryInfo::Field.create('PRODID', Vpim::PRODID) di.push_unique DirectoryInfo::Field.create('CALSCALE', "Gregorian") new(di.to_a) end |
.create2(producer = Vpim::PRODID) ⇒ Object
The producer ID defaults to Vpim::PRODID but you can set it to something specific to your application.
127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 |
# File 'lib/vpim/icalendar.rb', line 127 def Icalendar.create2(producer = Vpim::PRODID) #:yield: self # FIXME - make the primary API di = DirectoryInfo.create( [ DirectoryInfo::Field.create('VERSION', '2.0') ], 'VCALENDAR' ) di.push_unique DirectoryInfo::Field.create('PRODID', producer.to_str) di.push_unique DirectoryInfo::Field.create('CALSCALE', "Gregorian") cal = new(di.to_a) if block_given? yield cal end cal end |
.create_reply(fields = []) ⇒ Object
Create a new Icalendar object with a protocol method of REPLY.
Meeting requests, and such, are Calendar containers with a protocol method of REQUEST, and contains some number of Events, Todos, etc., that may need replying to. In order to reply to any of these components of a request, you must first build a Calendar object to hold your reply components.
This method builds the reply Calendar, you then will add to it replies to the specific components of the request Calendar that you are replying to. If you have any particular fields that you want to be in the Calendar, other than the defaults, then can be supplied as fields
, an array of Field objects.
171 172 173 174 175 |
# File 'lib/vpim/icalendar.rb', line 171 def Icalendar.create_reply(fields=[]) fields << DirectoryInfo::Field.create('METHOD', 'REPLY') Icalendar.create(fields) end |
.decode(cal, e = nil) ⇒ Object
Decode iCalendar data into an array of Icalendar objects.
Since iCalendars are self-delimited (by a BEGIN:VCALENDAR and an END:VCALENDAR), multiple iCalendars can be concatenated into a single file.
cal must be String or IO, or implement #each by returning each line in the input as those classes do.
265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 |
# File 'lib/vpim/icalendar.rb', line 265 def Icalendar.decode(cal, e = nil) entities = Vpim.(Vpim.decode(cal)) # Since all iCalendars must have a begin/end, the top-level should # consist entirely of entities/arrays, even if its a single iCalendar. if entities.detect { |e| ! e.kind_of? Array } raise "Not a valid iCalendar" end calendars = [] entities.each do |e| calendars << new(e) end calendars end |
.decode_duration(str) ⇒ Object
:nodoc:
217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 |
# File 'lib/vpim/icalendar.rb', line 217 def Icalendar.decode_duration(str) #:nodoc: unless match = %r{\s*#{Bnf::DURATION}\s*}.match(str) raise InvalidEncodingError, "duration not valid (#{str})" end dur = 0 # Remember: match[0] is the whole match string, match[1] is $1, etc. # Week if match[2] dur = match[2].to_i end # Days dur *= 7 if match[3] dur += match[3].to_i end # Hours dur *= 24 if match[4] dur += match[4].to_i end # Minutes dur *= 60 if match[5] dur += match[5].to_i end # Seconds dur *= 60 if match[6] dur += match[6].to_i end if match[1] && match[1] == '-' dur = -dur end dur end |
Instance Method Details
#add_event(&block) ⇒ Object
Add an event to this calendar.
Yields an event maker, Icalendar::Vevent::Maker.
100 101 102 |
# File 'lib/vpim/icalendar.rb', line 100 def add_event(&block) #:yield:event push Vevent::Maker.make( &block ) end |
#add_todo(&block) ⇒ Object
Add a task/todo to this calendar.
Yields a todo maker, Icalendar::Vtodo::Maker.
109 110 111 |
# File 'lib/vpim/icalendar.rb', line 109 def add_todo(&block) #:yield:todo push Vtodo::Maker.make( &block ) end |
#calscale ⇒ Object
The value of the CALSCALE: property, or “GREGORIAN” if CALSCALE: is not present.
This is of academic interest only. There aren’t any other calendar scales defined, and given that its hard enough just dealing with Gregorian calendars, there probably won’t be.
323 324 325 |
# File 'lib/vpim/icalendar.rb', line 323 def calscale (@properties['CALSCALE'] || 'GREGORIAN').upcase end |
#components(klass = Object) ⇒ Object
The array of all supported calendar components. If a class is provided, return only the components of that class.
If a block is provided, yield the components instead of returning them.
Examples:
calendar.components(Vpim::Icalendar::Vevent)
=> array of all calendar components
calendar.components(Vpim::Icalendar::Vtodo) {|c| c... }
=> yield all todo components
calendar.components {|c| c... }
=> yield all components
Note - use of this is mildly deprecated in favour of #each, #events, #todos, #journals because those won’t return timezones, and will return Enumerators if called without a block.
345 346 347 348 349 350 351 352 353 354 355 356 357 358 |
# File 'lib/vpim/icalendar.rb', line 345 def components(klass=Object) #:yields:component klass ||= Object unless block_given? return @components.select{|c| klass === c}.freeze end @components.each do |c| if klass === c yield c end end self end |
#each(klass = nil, &block) ⇒ Object
Enumerate the top-level calendar components. Yields them if a block is provided, otherwise returns an Enumerator.
This skips components that are only internally meaningful to iCalendar, such as timezone definitions.
367 368 369 370 371 372 |
# File 'lib/vpim/icalendar.rb', line 367 def each(klass=nil, &block) # :yield: component unless block return Enumerable::Enumerator.new(self, :each, klass) end components(klass, &block) end |
#encode(width = nil) ⇒ Object Also known as: to_s
Encode the Calendar as a string. The width is the maximum width of the encoded lines, it can be specified, but is better left to the default.
190 191 192 193 194 195 |
# File 'lib/vpim/icalendar.rb', line 190 def encode(width=nil) # We concatenate the fields of all objects, create a DirInfo, then # encode it. di = DirectoryInfo.create(self.fields.flatten) di.encode(width) end |
#events(&block) ⇒ Object
Short-hand for #each(Icalendar::Vevent).
375 376 377 |
# File 'lib/vpim/icalendar.rb', line 375 def events(&block) #:yield: Vevent each(Icalendar::Vevent, &block) end |
#fields ⇒ Object
Used during encoding.
178 179 180 181 182 183 184 185 186 |
# File 'lib/vpim/icalendar.rb', line 178 def fields # :nodoc: f = @properties.to_a last = f.pop # Use of #each means we won't encode components in our View, but also # that we won't encode timezones... but we don't decode/support timezones # anyhow, so fix later. each { |c| f << c.fields } f.push last end |
#journals(&block) ⇒ Object
Short-hand for #each(Icalendar::Vjournal).
385 386 387 |
# File 'lib/vpim/icalendar.rb', line 385 def journals(&block) #:yield: Vjournal each(Icalendar::Vjournal, &block) end |
#producer ⇒ Object
The value of the PRODID field, an unstructured string meant to identify the software which encoded the Calendar data.
298 299 300 301 302 |
# File 'lib/vpim/icalendar.rb', line 298 def producer #f = @properties.field('PRODID') #f && f.to_text @properties.text('PRODID').first end |
#protocol ⇒ Object
The value of the METHOD field. Protocol methods are used when iCalendars are exchanged in a calendar messaging system, such as iTIP or iMIP. When METHOD is not specified, the Calendar object is merely being used to transport a snapshot of some calendar information; without the intention of conveying a scheduling semantic.
Note that this method can’t be called method
, thats already a method of Object.
312 313 314 315 |
# File 'lib/vpim/icalendar.rb', line 312 def protocol m = @properties['METHOD'] m ? m.upcase : m end |
#protocol?(method) ⇒ Boolean
Check if the protocol method is method
213 214 215 |
# File 'lib/vpim/icalendar.rb', line 213 def protocol?(method) Vpim::Methods.casecmp?(protocol, method) end |
#push(component) ⇒ Object Also known as: <<
Push a calendar component onto the calendar.
200 201 202 203 204 205 206 207 208 |
# File 'lib/vpim/icalendar.rb', line 200 def push(component) case component when Vevent, Vtodo, Vjournal @components << component else raise ArgumentError, "can't add a #{component.type} to a calendar" end self end |
#todos(&block) ⇒ Object
Short-hand for #each(Icalendar::Vtodo).
380 381 382 |
# File 'lib/vpim/icalendar.rb', line 380 def todos(&block) #:yield: Vtodo each(Icalendar::Vtodo, &block) end |
#version ⇒ Object
The iCalendar version multiplied by 10 as an Integer. iCalendar must have a version of 20, and vCalendar must have a version of 10.
285 286 287 288 289 290 291 292 293 294 |
# File 'lib/vpim/icalendar.rb', line 285 def version v = @properties['VERSION'] unless v raise InvalidEncodingError, "Invalid calendar, no version field!" end v = v.to_f * 10 v = v.to_i end |