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.
-
#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.
68 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 |
# File 'lib/vpim/icalendar.rb', line 68 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.
139 140 141 142 143 144 145 146 147 148 |
# File 'lib/vpim/icalendar.rb', line 139 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.
119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 |
# File 'lib/vpim/icalendar.rb', line 119 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.
163 164 165 166 167 |
# File 'lib/vpim/icalendar.rb', line 163 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.
257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 |
# File 'lib/vpim/icalendar.rb', line 257 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:
209 210 211 212 213 214 215 216 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 |
# File 'lib/vpim/icalendar.rb', line 209 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.
99 100 101 |
# File 'lib/vpim/icalendar.rb', line 99 def add_event(&block) #:yield:event push Vevent::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.
315 316 317 |
# File 'lib/vpim/icalendar.rb', line 315 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.
337 338 339 340 341 342 343 344 345 346 347 348 349 350 |
# File 'lib/vpim/icalendar.rb', line 337 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.
359 360 361 362 363 364 |
# File 'lib/vpim/icalendar.rb', line 359 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.
182 183 184 185 186 187 |
# File 'lib/vpim/icalendar.rb', line 182 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).
367 368 369 |
# File 'lib/vpim/icalendar.rb', line 367 def events(&block) #:yield: Vevent each(Icalendar::Vevent, &block) end |
#fields ⇒ Object
Used during encoding.
170 171 172 173 174 175 176 177 178 |
# File 'lib/vpim/icalendar.rb', line 170 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).
377 378 379 |
# File 'lib/vpim/icalendar.rb', line 377 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.
290 291 292 293 294 |
# File 'lib/vpim/icalendar.rb', line 290 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.
304 305 306 307 |
# File 'lib/vpim/icalendar.rb', line 304 def protocol m = @properties['METHOD'] m ? m.upcase : m end |
#protocol?(method) ⇒ Boolean
Check if the protocol method is method
205 206 207 |
# File 'lib/vpim/icalendar.rb', line 205 def protocol?(method) Vpim::Methods.casecmp?(protocol, method) end |
#push(component) ⇒ Object Also known as: <<
Push a calendar component onto the calendar.
192 193 194 195 196 197 198 199 200 |
# File 'lib/vpim/icalendar.rb', line 192 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).
372 373 374 |
# File 'lib/vpim/icalendar.rb', line 372 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.
277 278 279 280 281 282 283 284 285 286 |
# File 'lib/vpim/icalendar.rb', line 277 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 |