icalendar2 0.1.0
Sign up to get free protection for your applications and to get access to all the features.
- data/CHANGELOG.md +2 -0
- data/README.md +89 -0
- data/lib/icalendar2/calendar.rb +85 -0
- data/lib/icalendar2/calendar_property/calscale.rb +9 -0
- data/lib/icalendar2/calendar_property/method.rb +9 -0
- data/lib/icalendar2/calendar_property/prodid.rb +9 -0
- data/lib/icalendar2/calendar_property/version.rb +9 -0
- data/lib/icalendar2/calendar_property.rb +12 -0
- data/lib/icalendar2/component/base.rb +90 -0
- data/lib/icalendar2/component/event.rb +32 -0
- data/lib/icalendar2/component.rb +8 -0
- data/lib/icalendar2/parameter/altrep.rb +8 -0
- data/lib/icalendar2/parameter/base.rb +34 -0
- data/lib/icalendar2/parameter/cn.rb +8 -0
- data/lib/icalendar2/parameter/cutype.rb +8 -0
- data/lib/icalendar2/parameter/delegated_from.rb +8 -0
- data/lib/icalendar2/parameter/delegated_to.rb +8 -0
- data/lib/icalendar2/parameter/dir.rb +8 -0
- data/lib/icalendar2/parameter/encoding.rb +8 -0
- data/lib/icalendar2/parameter/fmttype.rb +10 -0
- data/lib/icalendar2/parameter.rb +4 -0
- data/lib/icalendar2/parser.rb +136 -0
- data/lib/icalendar2/property/attach.rb +10 -0
- data/lib/icalendar2/property/attendee.rb +8 -0
- data/lib/icalendar2/property/base.rb +80 -0
- data/lib/icalendar2/property/categories.rb +10 -0
- data/lib/icalendar2/property/class.rb +9 -0
- data/lib/icalendar2/property/comment.rb +8 -0
- data/lib/icalendar2/property/contact.rb +8 -0
- data/lib/icalendar2/property/description.rb +9 -0
- data/lib/icalendar2/property/dtend.rb +9 -0
- data/lib/icalendar2/property/dtstamp.rb +9 -0
- data/lib/icalendar2/property/dtstart.rb +9 -0
- data/lib/icalendar2/property/exdate.rb +10 -0
- data/lib/icalendar2/property/geo.rb +9 -0
- data/lib/icalendar2/property/last_mod.rb +9 -0
- data/lib/icalendar2/property/location.rb +9 -0
- data/lib/icalendar2/property/nil.rb +13 -0
- data/lib/icalendar2/property/organizer.rb +9 -0
- data/lib/icalendar2/property/priority.rb +9 -0
- data/lib/icalendar2/property/rdate.rb +8 -0
- data/lib/icalendar2/property/related_to.rb +8 -0
- data/lib/icalendar2/property/resources.rb +8 -0
- data/lib/icalendar2/property/rrule.rb +10 -0
- data/lib/icalendar2/property/rstatus.rb +8 -0
- data/lib/icalendar2/property/sequence.rb +9 -0
- data/lib/icalendar2/property/summary.rb +9 -0
- data/lib/icalendar2/property/uid.rb +9 -0
- data/lib/icalendar2/property.rb +9 -0
- data/lib/icalendar2/tokens.rb +32 -0
- data/lib/icalendar2/value/binary_value.rb +8 -0
- data/lib/icalendar2/value/boolean_value.rb +6 -0
- data/lib/icalendar2/value/cal_address_value.rb +6 -0
- data/lib/icalendar2/value/date_time_value.rb +14 -0
- data/lib/icalendar2/value/date_value.rb +14 -0
- data/lib/icalendar2/value/duration_value.rb +14 -0
- data/lib/icalendar2/value/float_value.rb +7 -0
- data/lib/icalendar2/value/integer_value.rb +6 -0
- data/lib/icalendar2/value/lat_lon_value.rb +6 -0
- data/lib/icalendar2/value/period_value.rb +8 -0
- data/lib/icalendar2/value/recur_value.rb +39 -0
- data/lib/icalendar2/value/text_value.rb +17 -0
- data/lib/icalendar2/value/time_value.rb +6 -0
- data/lib/icalendar2/value/uri_value.rb +6 -0
- data/lib/icalendar2/value.rb +45 -0
- data/lib/icalendar2/version.rb +3 -0
- data/lib/icalendar2.rb +74 -0
- metadata +131 -0
@@ -0,0 +1,80 @@
|
|
1
|
+
module Icalendar2
|
2
|
+
module Property
|
3
|
+
class Base
|
4
|
+
MAX_LINE_LENGTH = 75
|
5
|
+
LIST_SEPARATOR = /(?<!\\),/
|
6
|
+
|
7
|
+
attr_reader :value, :parameters
|
8
|
+
|
9
|
+
class << self; attr_reader :value_types end
|
10
|
+
class << self; attr_reader :value_factories end
|
11
|
+
|
12
|
+
def self.value(options = {})
|
13
|
+
if !options[:types].respond_to?(:map)
|
14
|
+
raise ":types option must be enumberable, was #{options[:types]}"
|
15
|
+
end
|
16
|
+
@value_types = options[:types]
|
17
|
+
@value_factories = options[:types].map { |t| Value.get_factory(t) }
|
18
|
+
define_method(:list?) do
|
19
|
+
options[:list]
|
20
|
+
end
|
21
|
+
end
|
22
|
+
|
23
|
+
def self.name(str)
|
24
|
+
define_method(:name) { str }
|
25
|
+
end
|
26
|
+
|
27
|
+
def initialize(value, parameters = {})
|
28
|
+
@value = if list?
|
29
|
+
value_list = value.respond_to?(:map) ? value : value.split(LIST_SEPARATOR)
|
30
|
+
value_list.map { |v| value_object(v) }
|
31
|
+
else
|
32
|
+
value_object(value)
|
33
|
+
end
|
34
|
+
if @value.nil?
|
35
|
+
raise "Invalid value for #{self.class}: '#{value}'. Must be one of these types: #{self.class.value_types}"
|
36
|
+
end
|
37
|
+
@parameters = parameters || {}
|
38
|
+
validate
|
39
|
+
end
|
40
|
+
|
41
|
+
def name
|
42
|
+
raise "Must define name attribute in Property sublcass #{self.class}."
|
43
|
+
end
|
44
|
+
|
45
|
+
def valid?
|
46
|
+
@valid
|
47
|
+
end
|
48
|
+
|
49
|
+
def to_ical
|
50
|
+
parameters_str = @parameters.map { |k, v| "#{k}=#{v}" }.join(";")
|
51
|
+
parameters_str = ";#{parameters_str}" if parameters_str != ""
|
52
|
+
str = fold("#{name}#{parameters_str}:#{value}")
|
53
|
+
str << Tokens::CRLF
|
54
|
+
end
|
55
|
+
|
56
|
+
private
|
57
|
+
|
58
|
+
def validate
|
59
|
+
@valid = if @value.is_a? Array
|
60
|
+
@value.all?(&:valid?)
|
61
|
+
else
|
62
|
+
@value.valid?
|
63
|
+
end
|
64
|
+
end
|
65
|
+
|
66
|
+
def value_object(value)
|
67
|
+
self.class.value_factories.each do |f|
|
68
|
+
obj = f.new(value)
|
69
|
+
return obj if obj.valid?
|
70
|
+
end
|
71
|
+
|
72
|
+
nil
|
73
|
+
end
|
74
|
+
|
75
|
+
def fold(str)
|
76
|
+
str.scan(/.{1,#{MAX_LINE_LENGTH}}/).join("#{Tokens::CRLF} ")
|
77
|
+
end
|
78
|
+
end
|
79
|
+
end
|
80
|
+
end
|
@@ -0,0 +1,32 @@
|
|
1
|
+
module Icalendar2
|
2
|
+
module Tokens
|
3
|
+
COMPONENT_BEGIN = "BEGIN"
|
4
|
+
COMPONENT_END = "END"
|
5
|
+
CRLF = "\r\n"
|
6
|
+
HTAB = /\t/
|
7
|
+
DQUOTE = /"/
|
8
|
+
SOLIDUS = /\//
|
9
|
+
ALPHA = /[a-zA-Z]/
|
10
|
+
DIGIT = /[0-9]/
|
11
|
+
CONTROL_CHARS = "\x00-\x08\x0A-\x1F\x7F"
|
12
|
+
CONTROL = /[#{CONTROL_CHARS}]/
|
13
|
+
QSAFE_CHAR = /[^"#{CONTROL_CHARS}]/
|
14
|
+
SAFE_CHAR = /[^"#{CONTROL_CHARS};:,]/
|
15
|
+
VALUE_CHAR = /[^#{CONTROL_CHARS}]/
|
16
|
+
NON_US_ASCII_CHARS = "\x80-\xFF".force_encoding("UTF-8")
|
17
|
+
QUOTED_STRING = /#{DQUOTE}(?:#{SAFE_CHAR})*#{DQUOTE}/
|
18
|
+
PARAM_VALUE = "(?:#{SAFE_CHAR})*|#{QUOTED_STRING}"
|
19
|
+
IANA_TOKEN = "[-a-zA-Z0-9]+"
|
20
|
+
VENDORID = "[a-zA-Z0-9]{3}"
|
21
|
+
X_NAME = "X-(?:#{VENDORID}-)?#{IANA_TOKEN}"
|
22
|
+
|
23
|
+
DATE = /\d{4}(?:0[1-9]|1[012])(?:0[1-9]|[12][0-9]|3[01])/
|
24
|
+
TIME = /(?:[01][0-9]|2[0-3])(?:[0-5][0-9])(?:[0-5][0-9]|60)Z?/
|
25
|
+
DATE_TIME = /#{DATE}T#{TIME}/
|
26
|
+
|
27
|
+
# URI components
|
28
|
+
SCHEME = "#{ALPHA}(?:#{ALPHA}|#{DIGIT}|[-+.])*"
|
29
|
+
HIER_PART = '[^"<>]+'
|
30
|
+
URI = "#{SCHEME}:#{HIER_PART}"
|
31
|
+
end
|
32
|
+
end
|
@@ -0,0 +1,8 @@
|
|
1
|
+
module Icalendar2
|
2
|
+
# See http://tools.ietf.org/html/rfc5545#section-3.3.1
|
3
|
+
class BinaryValue < Value
|
4
|
+
B_CHAR = "[a-z0-9+/]"
|
5
|
+
B_END = "#{B_CHAR}#{B_CHAR}==|#{B_CHAR}#{B_CHAR}#{B_CHAR}="
|
6
|
+
matches /^(?:#{B_CHAR}#{B_CHAR}#{B_CHAR}#{B_CHAR})*(?:#{B_END})?$/i
|
7
|
+
end
|
8
|
+
end
|
@@ -0,0 +1,14 @@
|
|
1
|
+
module Icalendar2
|
2
|
+
# See http://tools.ietf.org/html/rfc5545#section-3.3.5
|
3
|
+
class DateTimeValue < Value
|
4
|
+
matches /^#{Tokens::DATE_TIME}$/
|
5
|
+
|
6
|
+
def initialize(value)
|
7
|
+
if value.is_a?(DateTime)
|
8
|
+
super(value.strftime('%Y%m%dT%H%M%S'))
|
9
|
+
else
|
10
|
+
super(value)
|
11
|
+
end
|
12
|
+
end
|
13
|
+
end
|
14
|
+
end
|
@@ -0,0 +1,14 @@
|
|
1
|
+
module Icalendar2
|
2
|
+
# See http://tools.ietf.org/html/rfc5545#section-3.3.4
|
3
|
+
class DateValue < Value
|
4
|
+
matches /^#{Tokens::DATE}$/
|
5
|
+
|
6
|
+
def initialize(value)
|
7
|
+
if value.is_a?(Date)
|
8
|
+
super(value.strftime('%Y%m%d'))
|
9
|
+
else
|
10
|
+
super(value)
|
11
|
+
end
|
12
|
+
end
|
13
|
+
end
|
14
|
+
end
|
@@ -0,0 +1,14 @@
|
|
1
|
+
module Icalendar2
|
2
|
+
# See http://tools.ietf.org/html/rfc5545#section-3.3.6
|
3
|
+
class DurationValue < Value
|
4
|
+
DUR_DAY = "\\d+D"
|
5
|
+
DUR_SECOND = "\\d{1,2}S"
|
6
|
+
DUR_MINUTE = "\\d{1,2}M(?:#{DUR_SECOND})?"
|
7
|
+
DUR_HOUR = "\\d{1,2}H(?:#{DUR_MINUTE})?"
|
8
|
+
DUR_TIME = "T(?:#{DUR_HOUR}|#{DUR_MINUTE}|#{DUR_SECOND})"
|
9
|
+
DUR_DATE = "#{DUR_DAY}(?:#{DUR_TIME})?"
|
10
|
+
DUR_WEEK = "\\d+W"
|
11
|
+
BASE_FORMAT = "P(?:#{DUR_DATE}|#{DUR_TIME}|#{DUR_WEEK})"
|
12
|
+
matches /^(?:\+?|-)#{BASE_FORMAT}$/
|
13
|
+
end
|
14
|
+
end
|
@@ -0,0 +1,8 @@
|
|
1
|
+
module Icalendar2
|
2
|
+
# See http://tools.ietf.org/html/rfc5545#section-3.3.9
|
3
|
+
class PeriodValue < Value
|
4
|
+
PERIOD_EXPLICIT = "(?:#{Tokens::DATE_TIME}/#{Tokens::DATE_TIME})"
|
5
|
+
PERIOD_START = "(?:#{Tokens::DATE_TIME}/#{DurationValue::BASE_FORMAT})"
|
6
|
+
matches /^#{PERIOD_EXPLICIT}|#{PERIOD_START}$/
|
7
|
+
end
|
8
|
+
end
|
@@ -0,0 +1,39 @@
|
|
1
|
+
module Icalendar2
|
2
|
+
# See http://tools.ietf.org/html/rfc5545#section-3.3.10
|
3
|
+
class RecurValue < Value
|
4
|
+
FREQ = "(?:SECONDLY|MINUTELY|HOURLY|DAILY|WEEKLY|MONTHLY|YEARLY)"
|
5
|
+
ENDDATE = "(?:#{Tokens::DATE}|#{Tokens::DATE_TIME})"
|
6
|
+
|
7
|
+
SECONDS = "(?:\d{1,2})"
|
8
|
+
BYSECLIST = "(?:#{SECONDS}(?:,#{SECONDS})*)"
|
9
|
+
|
10
|
+
MINUTES = "(?:\d{1,2})"
|
11
|
+
BYMINLIST = "(?:#{MINUTES}(?:,#{MINUTES})*)"
|
12
|
+
|
13
|
+
HOURS = "(?:\d{1,2})"
|
14
|
+
BYHRLIST = "(?:#{HOURS}(?:,#{HOURS})*)"
|
15
|
+
|
16
|
+
ORDWK = "(?:\d{1,2})"
|
17
|
+
WEEKDAY = "(?:SU|MO|TU|WE|TH|FR|SA)"
|
18
|
+
WEEKDAYNUM = "(?:[-+]?#{ORDWK}?#{WEEKDAY})"
|
19
|
+
BYWDAYLIST = "(?:#{WEEKDAYNUM}(?:,#{WEEKDAYNUM})*)"
|
20
|
+
|
21
|
+
MONTHDAYNUM = "(?:[-+]?\d{1,2})"
|
22
|
+
BYMODAYLIST = "(?:#{MONTHDAYNUM}(?:,#{MONTHDAYNUM})*)"
|
23
|
+
|
24
|
+
YEARDAYNUM = "(?:[-+]?\d{1,2})"
|
25
|
+
BYYRDAYLIST = "(?:#{YEARDAYNUM}(?:,#{YEARDAYNUM})*)"
|
26
|
+
|
27
|
+
WEEKNUM = "(?:[-+]?\d{1,2})"
|
28
|
+
BYWKNOLIST = "(?:#{WEEKNUM}(?:,#{WEEKNUM})*)"
|
29
|
+
|
30
|
+
MONTHNUM = "(?:\d{1,2})"
|
31
|
+
BYMOLIST = "(?:#{MONTHNUM}(?:,#{MONTHNUM})*)"
|
32
|
+
|
33
|
+
BYSPLIST = "(?:#{YEARDAYNUM}(?:,#{YEARDAYNUM})*)"
|
34
|
+
|
35
|
+
RECUR_RULE_PART = "FREQ=#{FREQ}|UNTIL=#{ENDDATE}|COUNT=\d+|INTERVAL=\d+|BYSECOND=#{BYSECLIST}|BYMINUTE=#{BYMINLIST}|BYHOUR=#{BYHRLIST}|BYDAY=#{BYWDAYLIST}|BYMONTHDAY=#{BYMODAYLIST}|BYYEARDAY=#{BYYRDAYLIST}|BYWEEKNO=#{BYWKNOLIST}|BYMONTH=#{BYMOLIST}|BYSETPOS=#{BYSPLIST}|WKST=#{WEEKDAY}"
|
36
|
+
|
37
|
+
matches /^#{RECUR_RULE_PART}(?:;#{RECUR_RULE_PART})*$/
|
38
|
+
end
|
39
|
+
end
|
@@ -0,0 +1,17 @@
|
|
1
|
+
module Icalendar2
|
2
|
+
# See http://tools.ietf.org/html/rfc5545#section-3.3.11
|
3
|
+
# This will escape text passed in, so do not escape it first!
|
4
|
+
class TextValue < Value
|
5
|
+
|
6
|
+
def initialize(value)
|
7
|
+
replacements = {
|
8
|
+
'\\' => '\\\\',
|
9
|
+
';' => '\;',
|
10
|
+
',' => '\,',
|
11
|
+
"\r\n" => "\n",
|
12
|
+
"\r" => "\n"
|
13
|
+
}
|
14
|
+
super(value.respond_to?(:gsub) ? value.gsub(/\\|;|,|\r\n|\r/, replacements) : value)
|
15
|
+
end
|
16
|
+
end
|
17
|
+
end
|
@@ -0,0 +1,45 @@
|
|
1
|
+
module Icalendar2
|
2
|
+
class Value
|
3
|
+
|
4
|
+
class << self; attr_reader :format_matcher end
|
5
|
+
@format_matcher = nil
|
6
|
+
|
7
|
+
attr_reader :value
|
8
|
+
|
9
|
+
def self.matches(matcher)
|
10
|
+
@format_matcher = matcher
|
11
|
+
end
|
12
|
+
|
13
|
+
def self.get_factory(value_sym)
|
14
|
+
# :foo => "Foo", :bar_baz => "BarBaz", etc.
|
15
|
+
value_name = value_sym.to_s.split(/_/).map(&:capitalize).join('')
|
16
|
+
Icalendar2.const_get("#{value_name}Value", false)
|
17
|
+
end
|
18
|
+
|
19
|
+
def initialize(value)
|
20
|
+
@valid = true
|
21
|
+
@value = value
|
22
|
+
validate
|
23
|
+
end
|
24
|
+
|
25
|
+
def valid?
|
26
|
+
@valid
|
27
|
+
end
|
28
|
+
|
29
|
+
def to_s
|
30
|
+
value.to_s
|
31
|
+
end
|
32
|
+
|
33
|
+
private
|
34
|
+
|
35
|
+
def validate
|
36
|
+
unless self.class.format_matcher.nil?
|
37
|
+
if value.respond_to?(:to_s) && (val = value.to_s).respond_to?(:match)
|
38
|
+
@valid = !!val.match(self.class.format_matcher)
|
39
|
+
else
|
40
|
+
@valid = false
|
41
|
+
end
|
42
|
+
end
|
43
|
+
end
|
44
|
+
end
|
45
|
+
end
|