icalendar 1.5.4 → 2.0.0.beta.1
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.
- checksums.yaml +7 -0
- data/.gitignore +1 -1
- data/.rspec +2 -0
- data/.travis.yml +1 -2
- data/History.txt +2 -7
- data/README.md +82 -107
- data/Rakefile +6 -7
- data/icalendar.gemspec +10 -9
- data/lib/icalendar.rb +17 -33
- data/lib/icalendar/alarm.rb +35 -0
- data/lib/icalendar/calendar.rb +17 -100
- data/lib/icalendar/component.rb +41 -403
- data/lib/icalendar/event.rb +51 -0
- data/lib/icalendar/freebusy.rb +27 -0
- data/lib/icalendar/has_components.rb +83 -0
- data/lib/icalendar/has_properties.rb +156 -0
- data/lib/icalendar/journal.rb +39 -0
- data/lib/icalendar/parser.rb +75 -403
- data/lib/icalendar/timezone.rb +53 -0
- data/lib/icalendar/todo.rb +52 -0
- data/lib/icalendar/tzinfo.rb +30 -30
- data/lib/icalendar/value.rb +80 -0
- data/lib/icalendar/values/array.rb +43 -0
- data/lib/icalendar/values/binary.rb +31 -0
- data/lib/icalendar/values/boolean.rb +17 -0
- data/lib/icalendar/values/cal_address.rb +8 -0
- data/lib/icalendar/values/date.rb +26 -0
- data/lib/icalendar/values/date_time.rb +34 -0
- data/lib/icalendar/values/duration.rb +48 -0
- data/lib/icalendar/values/float.rb +17 -0
- data/lib/icalendar/values/integer.rb +17 -0
- data/lib/icalendar/values/period.rb +46 -0
- data/lib/icalendar/values/recur.rb +63 -0
- data/lib/icalendar/values/text.rb +26 -0
- data/lib/icalendar/values/time.rb +34 -0
- data/lib/icalendar/values/time_with_zone.rb +31 -0
- data/lib/icalendar/values/uri.rb +19 -0
- data/lib/icalendar/values/utc_offset.rb +39 -0
- data/lib/icalendar/version.rb +5 -0
- data/spec/alarm_spec.rb +108 -0
- data/spec/calendar_spec.rb +167 -0
- data/spec/event_spec.rb +108 -0
- data/{test/fixtures/folding.ics → spec/fixtures/nondefault_values.ics} +2 -2
- data/{test → spec}/fixtures/single_event.ics +11 -14
- data/spec/fixtures/timezone.ics +35 -0
- data/spec/freebusy_spec.rb +7 -0
- data/spec/journal_spec.rb +7 -0
- data/spec/parser_spec.rb +26 -0
- data/spec/roundtrip_spec.rb +40 -0
- data/spec/spec_helper.rb +25 -0
- data/spec/timezone_spec.rb +31 -0
- data/spec/todo_spec.rb +24 -0
- data/spec/tzinfo_spec.rb +85 -0
- data/spec/values/date_time_spec.rb +80 -0
- data/spec/values/duration_spec.rb +67 -0
- data/spec/values/period_spec.rb +47 -0
- data/spec/values/recur_spec.rb +47 -0
- data/spec/values/text_spec.rb +72 -0
- data/spec/values/utc_offset_spec.rb +41 -0
- metadata +129 -88
- data/GPL +0 -340
- data/examples/create_cal.rb +0 -45
- data/examples/parse_cal.rb +0 -20
- data/examples/single_event.ics +0 -18
- data/lib/hash_attrs.rb +0 -34
- data/lib/icalendar/base.rb +0 -47
- data/lib/icalendar/component/alarm.rb +0 -47
- data/lib/icalendar/component/event.rb +0 -131
- data/lib/icalendar/component/freebusy.rb +0 -38
- data/lib/icalendar/component/journal.rb +0 -60
- data/lib/icalendar/component/timezone.rb +0 -91
- data/lib/icalendar/component/todo.rb +0 -64
- data/lib/icalendar/conversions.rb +0 -107
- data/lib/icalendar/helpers.rb +0 -109
- data/lib/icalendar/parameter.rb +0 -33
- data/lib/icalendar/rrule.rb +0 -133
- data/lib/meta.rb +0 -32
- data/script/console +0 -10
- data/script/recur1.ics +0 -38
- data/script/tryit.rb +0 -13
- data/test/component/test_event.rb +0 -253
- data/test/component/test_timezone.rb +0 -74
- data/test/component/test_todo.rb +0 -31
- data/test/fixtures/life.ics +0 -46
- data/test/fixtures/nonstandard.ics +0 -25
- data/test/fixtures/simplecal.ics +0 -119
- data/test/interactive.rb +0 -17
- data/test/read_write.rb +0 -23
- data/test/test_calendar.rb +0 -167
- data/test/test_component.rb +0 -102
- data/test/test_conversions.rb +0 -104
- data/test/test_helper.rb +0 -7
- data/test/test_parameter.rb +0 -91
- data/test/test_parser.rb +0 -100
- data/test/test_tzinfo.rb +0 -83
- data/website/index.html +0 -70
- data/website/index.txt +0 -38
- data/website/javascripts/rounded_corners_lite.inc.js +0 -285
- data/website/stylesheets/screen.css +0 -159
- data/website/template.html.erb +0 -50
@@ -0,0 +1,53 @@
|
|
1
|
+
module Icalendar
|
2
|
+
|
3
|
+
class Timezone < Component
|
4
|
+
module TzProperties
|
5
|
+
def self.included(base)
|
6
|
+
base.class_eval do
|
7
|
+
required_property :dtstart, Icalendar::Values::DateTime
|
8
|
+
required_property :tzoffsetfrom, Icalendar::Values::UtcOffset
|
9
|
+
required_property :tzoffsetto, Icalendar::Values::UtcOffset
|
10
|
+
|
11
|
+
optional_property :rrule, Icalendar::Values::Recur, true
|
12
|
+
optional_property :comment
|
13
|
+
optional_property :rdate, Icalendar::Values::DateTime
|
14
|
+
optional_property :tzname
|
15
|
+
end
|
16
|
+
end
|
17
|
+
end
|
18
|
+
class Daylight < Component
|
19
|
+
include TzProperties
|
20
|
+
|
21
|
+
def initialize
|
22
|
+
super 'daylight', 'DAYLIGHT'
|
23
|
+
end
|
24
|
+
end
|
25
|
+
class Standard < Component
|
26
|
+
include TzProperties
|
27
|
+
|
28
|
+
def initialize
|
29
|
+
super 'standard', 'STANDARD'
|
30
|
+
end
|
31
|
+
end
|
32
|
+
|
33
|
+
|
34
|
+
required_property :tzid
|
35
|
+
|
36
|
+
optional_single_property :last_modified, Icalendar::Values::DateTime
|
37
|
+
optional_single_property :tzurl, Icalendar::Values::Uri
|
38
|
+
|
39
|
+
component :daylight, false, Icalendar::Timezone::Daylight
|
40
|
+
component :standard, false, Icalendar::Timezone::Standard
|
41
|
+
|
42
|
+
def initialize
|
43
|
+
super 'timezone'
|
44
|
+
end
|
45
|
+
|
46
|
+
def valid?(strict = false)
|
47
|
+
daylights.empty? && standards.empty? and return false
|
48
|
+
daylights.all? { |d| d.valid? strict } or return false
|
49
|
+
standards.all? { |s| s.valid? strict } or return false
|
50
|
+
super
|
51
|
+
end
|
52
|
+
end
|
53
|
+
end
|
@@ -0,0 +1,52 @@
|
|
1
|
+
module Icalendar
|
2
|
+
|
3
|
+
class Todo < Component
|
4
|
+
required_property :dtstamp, Icalendar::Values::DateTime
|
5
|
+
required_property :uid
|
6
|
+
# dtstart only required if duration is specified
|
7
|
+
required_property :dtstart, Icalendar::Values::DateTime,
|
8
|
+
->(todo, dtstart) { !(!todo.duration.nil? && dtstart.nil?) }
|
9
|
+
|
10
|
+
optional_single_property :due, Icalendar::Values::DateTime
|
11
|
+
optional_single_property :duration, Icalendar::Values::Duration
|
12
|
+
mutually_exclusive_properties :due, :duration
|
13
|
+
|
14
|
+
optional_single_property :ip_class
|
15
|
+
optional_single_property :completed, Icalendar::Values::DateTime
|
16
|
+
optional_single_property :created, Icalendar::Values::DateTime
|
17
|
+
optional_single_property :description
|
18
|
+
optional_single_property :geo, Icalendar::Values::Float
|
19
|
+
optional_single_property :last_modified, Icalendar::Values::DateTime
|
20
|
+
optional_single_property :location
|
21
|
+
optional_single_property :organizer, Icalendar::Values::CalAddress
|
22
|
+
optional_single_property :percent_complete, Icalendar::Values::Integer
|
23
|
+
optional_single_property :priority, Icalendar::Values::Integer
|
24
|
+
optional_single_property :recurrence_id, Icalendar::Values::DateTime
|
25
|
+
optional_single_property :sequence, Icalendar::Values::Integer
|
26
|
+
optional_single_property :status
|
27
|
+
optional_single_property :summary
|
28
|
+
optional_single_property :url, Icalendar::Values::Uri
|
29
|
+
|
30
|
+
optional_property :rrule, Icalendar::Values::Recur, true
|
31
|
+
optional_property :attach, Icalendar::Values::Uri
|
32
|
+
optional_property :attendee, Icalendar::Values::CalAddress
|
33
|
+
optional_property :categories
|
34
|
+
optional_property :comment
|
35
|
+
optional_property :contact
|
36
|
+
optional_property :exdate, Icalendar::Values::DateTime
|
37
|
+
optional_property :request_status
|
38
|
+
optional_property :related_to
|
39
|
+
optional_property :resources
|
40
|
+
optional_property :rdate, Icalendar::Values::DateTime
|
41
|
+
|
42
|
+
component :alarm, false
|
43
|
+
|
44
|
+
def initialize
|
45
|
+
super 'todo'
|
46
|
+
self.dtstamp = Icalendar::Values::DateTime.new Time.now.utc, 'tzid' => 'UTC'
|
47
|
+
self.uid = new_uid
|
48
|
+
end
|
49
|
+
|
50
|
+
end
|
51
|
+
|
52
|
+
end
|
data/lib/icalendar/tzinfo.rb
CHANGED
@@ -38,22 +38,22 @@
|
|
38
38
|
# seems to work fine (tested in Mozilla Thunderbird + Lightning).
|
39
39
|
# Future goal would be making this better.
|
40
40
|
|
41
|
-
|
42
|
-
|
41
|
+
|
42
|
+
require 'tzinfo'
|
43
43
|
|
44
44
|
module TZInfo
|
45
45
|
class Timezone
|
46
46
|
def ical_timezone(date, dst = Timezone.default_dst)
|
47
47
|
period = period_for_local(date, dst)
|
48
48
|
timezone = Icalendar::Timezone.new
|
49
|
-
timezone.
|
49
|
+
timezone.tzid = identifier
|
50
50
|
if period.start_transition.nil?
|
51
|
-
timezone.
|
51
|
+
timezone.add_component period.single
|
52
52
|
elsif period.end_transition.nil?
|
53
|
-
timezone.
|
53
|
+
timezone.add_component period.dst? ? period.daylight : period.standard
|
54
54
|
else
|
55
|
-
timezone.
|
56
|
-
timezone.
|
55
|
+
timezone.add_component period.daylight
|
56
|
+
timezone.add_component period.standard
|
57
57
|
end
|
58
58
|
timezone
|
59
59
|
end
|
@@ -73,7 +73,7 @@ module TZInfo
|
|
73
73
|
# this is somewhat of a hack, but seems to work ok
|
74
74
|
[sprintf(
|
75
75
|
'FREQ=YEARLY;BYMONTH=%d;BYDAY=%d%s',
|
76
|
-
start.month,
|
76
|
+
start.month,
|
77
77
|
((start.day - 1)/ 7).to_i + 1,
|
78
78
|
start.strftime('%a').upcase[0,2]
|
79
79
|
)]
|
@@ -93,46 +93,46 @@ module TZInfo
|
|
93
93
|
|
94
94
|
class TimezonePeriod
|
95
95
|
def daylight
|
96
|
-
Icalendar::Daylight.new.tap do |day|
|
96
|
+
Icalendar::Timezone::Daylight.new.tap do |day|
|
97
97
|
if dst?
|
98
|
-
day.
|
99
|
-
day.
|
100
|
-
day.
|
98
|
+
day.tzname = abbreviation.to_s
|
99
|
+
day.tzoffsetfrom = start_transition.offset_from
|
100
|
+
day.tzoffsetto = start_transition.offset_to
|
101
101
|
day.dtstart = start_transition.dtstart
|
102
|
-
day.
|
102
|
+
day.rrule = start_transition.rrule unless end_transition.nil?
|
103
103
|
else
|
104
|
-
day.
|
105
|
-
day.
|
106
|
-
day.
|
104
|
+
day.tzname = abbreviation.to_s.sub("ST","DT")
|
105
|
+
day.tzoffsetfrom = end_transition.offset_from
|
106
|
+
day.tzoffsetto = end_transition.offset_to
|
107
107
|
day.dtstart = end_transition.dtstart
|
108
|
-
day.
|
108
|
+
day.rrule = end_transition.rrule
|
109
109
|
end
|
110
110
|
end
|
111
111
|
end
|
112
112
|
|
113
113
|
def standard
|
114
|
-
Icalendar::Standard.new.tap do |std|
|
114
|
+
Icalendar::Timezone::Standard.new.tap do |std|
|
115
115
|
if dst?
|
116
|
-
std.
|
117
|
-
std.
|
118
|
-
std.
|
116
|
+
std.tzname = abbreviation.to_s.sub('DT', 'ST')
|
117
|
+
std.tzoffsetfrom = end_transition.offset_from
|
118
|
+
std.tzoffsetto = end_transition.offset_to
|
119
119
|
std.dtstart = end_transition.dtstart
|
120
|
-
std.
|
120
|
+
std.rrule = end_transition.rrule
|
121
121
|
else
|
122
|
-
std.
|
123
|
-
std.
|
124
|
-
std.
|
122
|
+
std.tzname = abbreviation.to_s
|
123
|
+
std.tzoffsetfrom = start_transition.offset_from
|
124
|
+
std.tzoffsetto = start_transition.offset_to
|
125
125
|
std.dtstart = start_transition.dtstart
|
126
|
-
std.
|
126
|
+
std.rrule = start_transition.rrule unless end_transition.nil?
|
127
127
|
end
|
128
128
|
end
|
129
129
|
end
|
130
130
|
|
131
131
|
def single
|
132
|
-
Icalendar::Standard.new.tap do |std|
|
133
|
-
std.
|
134
|
-
std.
|
135
|
-
std.
|
132
|
+
Icalendar::Timezone::Standard.new.tap do |std|
|
133
|
+
std.tzname = abbreviation.to_s
|
134
|
+
std.tzoffsetfrom = offset.ical_offset
|
135
|
+
std.tzoffsetto = offset.ical_offset
|
136
136
|
std.dtstart = DateTime.new(1970).strftime '%Y%m%dT%H%M%S'
|
137
137
|
end
|
138
138
|
end
|
@@ -0,0 +1,80 @@
|
|
1
|
+
require 'delegate'
|
2
|
+
|
3
|
+
module Icalendar
|
4
|
+
|
5
|
+
class Value < ::SimpleDelegator
|
6
|
+
|
7
|
+
attr_accessor :ical_params
|
8
|
+
|
9
|
+
def initialize(value, params = {})
|
10
|
+
@ical_params = params.dup
|
11
|
+
super value
|
12
|
+
end
|
13
|
+
|
14
|
+
def ical_param(key, value)
|
15
|
+
@ical_params[key.to_s] = value
|
16
|
+
end
|
17
|
+
|
18
|
+
def value
|
19
|
+
__getobj__
|
20
|
+
end
|
21
|
+
|
22
|
+
def to_ical(default_type)
|
23
|
+
ical_param 'value', self.class.value_type if needs_value_type?(default_type)
|
24
|
+
"#{params_ical}:#{value_ical}"
|
25
|
+
end
|
26
|
+
|
27
|
+
def params_ical
|
28
|
+
unless ical_params.empty?
|
29
|
+
";#{ical_params.map { |name, value| param_ical name, value }.join ';'}"
|
30
|
+
end
|
31
|
+
end
|
32
|
+
|
33
|
+
def self.value_type
|
34
|
+
name.gsub(/\A.*::/, '').gsub(/(?<!\A)[A-Z]/, '-\0').upcase
|
35
|
+
end
|
36
|
+
|
37
|
+
private
|
38
|
+
|
39
|
+
def needs_value_type?(default_type)
|
40
|
+
self.class != default_type
|
41
|
+
end
|
42
|
+
|
43
|
+
def param_ical(name, param_value)
|
44
|
+
if param_value.is_a? Array
|
45
|
+
param_value = param_value.map { |v| escape_param_value v }.join ','
|
46
|
+
else
|
47
|
+
param_value = escape_param_value param_value
|
48
|
+
end
|
49
|
+
"#{name.to_s.gsub('_', '-').upcase}=#{param_value}"
|
50
|
+
end
|
51
|
+
|
52
|
+
def escape_param_value(value)
|
53
|
+
v = value.gsub '"', "'"
|
54
|
+
v =~ /[;:,]/ ? %("#{v}") : v
|
55
|
+
end
|
56
|
+
|
57
|
+
end
|
58
|
+
|
59
|
+
end
|
60
|
+
|
61
|
+
# helper; not actual iCalendar value type
|
62
|
+
require_relative 'values/array'
|
63
|
+
|
64
|
+
# iCalendar value types
|
65
|
+
require_relative 'values/binary'
|
66
|
+
require_relative 'values/boolean'
|
67
|
+
require_relative 'values/date'
|
68
|
+
require_relative 'values/date_time'
|
69
|
+
require_relative 'values/duration'
|
70
|
+
require_relative 'values/float'
|
71
|
+
require_relative 'values/integer'
|
72
|
+
require_relative 'values/period'
|
73
|
+
require_relative 'values/recur'
|
74
|
+
require_relative 'values/text'
|
75
|
+
require_relative 'values/time'
|
76
|
+
require_relative 'values/uri'
|
77
|
+
require_relative 'values/utc_offset'
|
78
|
+
|
79
|
+
# further refine above classes
|
80
|
+
require_relative 'values/cal_address'
|
@@ -0,0 +1,43 @@
|
|
1
|
+
module Icalendar
|
2
|
+
module Values
|
3
|
+
|
4
|
+
class Array < Value
|
5
|
+
|
6
|
+
def initialize(value, klass, params = {}, options = {})
|
7
|
+
@value_delimiter = options[:delimiter] || ','
|
8
|
+
mapped = if value.is_a? ::Array
|
9
|
+
value.map { |v| klass.new v, params }
|
10
|
+
else
|
11
|
+
[klass.new(value, params)]
|
12
|
+
end
|
13
|
+
super mapped, params
|
14
|
+
end
|
15
|
+
|
16
|
+
def params_ical
|
17
|
+
value.each do |v|
|
18
|
+
ical_params.merge! v.ical_params
|
19
|
+
end
|
20
|
+
super
|
21
|
+
end
|
22
|
+
|
23
|
+
def value_ical
|
24
|
+
value.map do |v|
|
25
|
+
v.value_ical
|
26
|
+
end.join @value_delimiter
|
27
|
+
end
|
28
|
+
|
29
|
+
def valid?
|
30
|
+
klass = value.first.class
|
31
|
+
!value.all? { |v| v.class == klass }
|
32
|
+
end
|
33
|
+
|
34
|
+
private
|
35
|
+
|
36
|
+
def needs_value_type?(default_type)
|
37
|
+
value.first.class != default_type
|
38
|
+
end
|
39
|
+
|
40
|
+
end
|
41
|
+
|
42
|
+
end
|
43
|
+
end
|
@@ -0,0 +1,31 @@
|
|
1
|
+
require 'base64'
|
2
|
+
|
3
|
+
module Icalendar
|
4
|
+
module Values
|
5
|
+
|
6
|
+
class Binary < Value
|
7
|
+
|
8
|
+
def params_ical
|
9
|
+
ical_param :value, 'BINARY'
|
10
|
+
ical_param :encoding, 'BASE64'
|
11
|
+
super
|
12
|
+
end
|
13
|
+
|
14
|
+
def value_ical
|
15
|
+
if base64?
|
16
|
+
value
|
17
|
+
else
|
18
|
+
Base64.strict_encode64 value
|
19
|
+
end
|
20
|
+
end
|
21
|
+
|
22
|
+
private
|
23
|
+
|
24
|
+
def base64?
|
25
|
+
value.is_a?(String) &&
|
26
|
+
value =~ /\A(?:[A-Za-z0-9+\/]{4})*(?:[A-Za-z0-9+\/]{4}|[A-Za-z0-9+\/]{3}=|[A-Za-z0-9+\/]{2}==)\z/
|
27
|
+
end
|
28
|
+
end
|
29
|
+
|
30
|
+
end
|
31
|
+
end
|
@@ -0,0 +1,26 @@
|
|
1
|
+
require 'date'
|
2
|
+
|
3
|
+
module Icalendar
|
4
|
+
module Values
|
5
|
+
|
6
|
+
class Date < Value
|
7
|
+
FORMAT = '%Y%m%d'
|
8
|
+
|
9
|
+
def initialize(value, params = {})
|
10
|
+
if value.is_a? String
|
11
|
+
super ::Date.strptime(value, FORMAT), params
|
12
|
+
elsif value.respond_to? :to_date
|
13
|
+
super value.to_date, params
|
14
|
+
else
|
15
|
+
super
|
16
|
+
end
|
17
|
+
end
|
18
|
+
|
19
|
+
def value_ical
|
20
|
+
value.strftime FORMAT
|
21
|
+
end
|
22
|
+
|
23
|
+
end
|
24
|
+
|
25
|
+
end
|
26
|
+
end
|
@@ -0,0 +1,34 @@
|
|
1
|
+
require 'date'
|
2
|
+
require_relative 'time_with_zone'
|
3
|
+
|
4
|
+
module Icalendar
|
5
|
+
module Values
|
6
|
+
|
7
|
+
class DateTime < Value
|
8
|
+
include TimeWithZone
|
9
|
+
|
10
|
+
FORMAT = '%Y%m%dT%H%M%S'
|
11
|
+
|
12
|
+
def initialize(value, params = {})
|
13
|
+
if value.is_a? String
|
14
|
+
params['tzid'] = 'UTC' if value.end_with? 'Z'
|
15
|
+
super ::DateTime.strptime(value, FORMAT), params
|
16
|
+
elsif value.respond_to? :to_datetime
|
17
|
+
super value.to_datetime, params
|
18
|
+
else
|
19
|
+
super
|
20
|
+
end
|
21
|
+
end
|
22
|
+
|
23
|
+
def value_ical
|
24
|
+
if tz_utc
|
25
|
+
"#{strftime FORMAT}Z"
|
26
|
+
else
|
27
|
+
strftime FORMAT
|
28
|
+
end
|
29
|
+
end
|
30
|
+
|
31
|
+
end
|
32
|
+
|
33
|
+
end
|
34
|
+
end
|