1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 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 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115
|
require 'securerandom'
module Icalendar
class Component
include HasProperties
include HasComponents
attr_reader :name
attr_reader :ical_name
attr_accessor :parent
def self.parse(source)
_parse source
rescue ArgumentError
source.rewind if source.respond_to?(:rewind)
_parse Parser.clean_bad_wrapping(source)
end
def initialize(name, ical_name = nil)
@name = name
@ical_name = ical_name || "V#{name.upcase}"
super()
end
def new_uid
SecureRandom.uuid
end
def to_ical
[
"BEGIN:#{ical_name}",
ical_properties,
ical_components,
"END:#{ical_name}\r\n"
].compact.join "\r\n"
end
private
def ical_properties
(self.class.properties + custom_properties.keys).map do |prop|
value = property prop
unless value.nil?
if value.is_a? ::Array
value.map do |part|
ical_fold "#{ical_prop_name prop}#{part.to_ical self.class.default_property_types[prop]}"
end.join "\r\n" unless value.empty?
else
ical_fold "#{ical_prop_name prop}#{value.to_ical self.class.default_property_types[prop]}"
end
end
end.compact.join "\r\n"
end
def ical_prop_name(prop_name)
prop_name.gsub(/\Aip_/, '').gsub('_', '-').upcase
end
def ical_fold(long_line, indent = "\x20")
# rfc2445 says:
# Lines of text SHOULD NOT be longer than 75 octets, excluding the line
# break. Long content lines SHOULD be split into a multiple line
# representations using a line "folding" technique. That is, a long
# line can be split between any two characters by inserting a CRLF
# immediately followed by a single linear white space character (i.e.,
# SPACE, US-ASCII decimal 32 or HTAB, US-ASCII decimal 9). Any sequence
# of CRLF followed immediately by a single linear white space character
# is ignored (i.e., removed) when processing the content type.
#
# Note the useage of "octets" and "characters": a line should not be longer
# than 75 octets, but you need to split between characters, not bytes.
# This is challanging with Unicode composing accents, for example.
return long_line if long_line.bytesize <= Icalendar::MAX_LINE_LENGTH
chars = long_line.scan(/\P{M}\p{M}*/u) # split in graphenes
folded = ['']
bytes = 0
while chars.count > 0
c = chars.shift
cb = c.bytes.count
if bytes + cb > Icalendar::MAX_LINE_LENGTH
# Split here
folded.push "#{indent}"
bytes = indent.bytes.count
end
folded[-1] += c
bytes += cb
end
folded.join("\r\n")
end
def ical_components
collection = []
(self.class.components + custom_components.keys).each do |component_name|
components = send component_name
components.each do |component|
collection << component.to_ical
end
end
collection.empty? ? nil : collection.join.chomp("\r\n")
end
class << self
private def _parse(source)
parser = Parser.new(source)
parser.component_class = self
parser.parse
end
end
end
end
|