icalendar 2.4.1 → 2.5.0
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 +5 -5
- data/.travis.yml +5 -4
- data/History.txt +5 -0
- data/{COPYING → LICENSE} +0 -0
- data/README.md +3 -20
- data/icalendar.gemspec +14 -17
- data/lib/icalendar/has_properties.rb +5 -1
- data/lib/icalendar/parser.rb +4 -2
- data/lib/icalendar/timezone.rb +53 -0
- data/lib/icalendar/timezone_store.rb +33 -0
- data/lib/icalendar/values/date_or_date_time.rb +23 -11
- data/lib/icalendar/values/time_with_zone.rb +11 -6
- data/lib/icalendar/values/uri.rb +2 -2
- data/lib/icalendar/values/utc_offset.rb +9 -0
- data/lib/icalendar/version.rb +1 -1
- data/spec/event_spec.rb +17 -0
- data/spec/fixtures/single_event.ics +1 -1
- data/spec/fixtures/single_event_bad_organizer.ics +22 -0
- data/spec/fixtures/single_event_organizer_parsed.ics +22 -0
- data/spec/fixtures/tzid_search.ics +31 -0
- data/spec/parser_spec.rb +21 -9
- data/spec/roundtrip_spec.rb +14 -0
- data/spec/values/date_or_date_time_spec.rb +10 -0
- metadata +53 -35
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
|
-
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
2
|
+
SHA256:
|
3
|
+
metadata.gz: 3f6d533875d40bbe7fe5f4ceedffb05f31207bf5e54fa9680f395c206c0e9a88
|
4
|
+
data.tar.gz: 85a0ddde7183fe00d00b3d71cb92ed2678ac7b34f63d9ffbbad20f03cc4adf6e
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: f645517ee3cb6960432f420c76edf0592c98b066d38ca893f66d04eb97d38e72853cdafe31835609750011dc0939dafbeaa0dc96337396418edbe9a830462ccf
|
7
|
+
data.tar.gz: b99f6e61e505a27b275c3da7555cf2017e891be6debcff89d045c5852d9e7ae0e88192c66b8134b221db6e8d3bb0c502f3b0c4cfa1186bdb0e3e280a11f56383
|
data/.travis.yml
CHANGED
@@ -3,16 +3,17 @@ before_install:
|
|
3
3
|
- gem install bundler
|
4
4
|
language: ruby
|
5
5
|
rvm:
|
6
|
-
- 2.
|
6
|
+
- 2.5
|
7
|
+
- 2.4
|
8
|
+
- 2.3
|
7
9
|
- 2.2
|
8
|
-
- 2.1
|
9
|
-
- 2.0
|
10
10
|
- jruby-19mode
|
11
|
-
- rbx-
|
11
|
+
- rbx-3
|
12
12
|
- ruby-head
|
13
13
|
- jruby-head
|
14
14
|
matrix:
|
15
15
|
allow_failures:
|
16
|
+
- rvm: 2.2
|
16
17
|
- rvm: ruby-head
|
17
18
|
- rvm: jruby-head
|
18
19
|
script: bundle exec rake spec
|
data/History.txt
CHANGED
@@ -1,3 +1,8 @@
|
|
1
|
+
=== 2.5.0 2018-09-10
|
2
|
+
* Set timezone information from VTIMEZONE components in cases that ActiveSupport can't figure it out (or isn't installed)
|
3
|
+
* Prevent rewinding the Parser IO input during parsing - Niels Laukens
|
4
|
+
* Update tested/supported ruby versions & documentation updates.
|
5
|
+
|
1
6
|
=== 2.4.1 2016-09-03
|
2
7
|
* Fix parsing multiple calendars or components in same file - Patrick Schnetger
|
3
8
|
* Fix multi-byte folding bug - Niels Laukens
|
data/{COPYING → LICENSE}
RENAMED
File without changes
|
data/README.md
CHANGED
@@ -6,23 +6,6 @@ iCalendar -- Internet calendaring, Ruby style
|
|
6
6
|
|
7
7
|
<http://github.com/icalendar/icalendar>
|
8
8
|
|
9
|
-
2.x Status
|
10
|
-
---
|
11
|
-
|
12
|
-
iCalendar 2.0 is under active development, and can be followed in the
|
13
|
-
[master branch](https://github.com/icalendar/icalendar/tree/master).
|
14
|
-
|
15
|
-
iCalendar 1.x (currently the 1.x branch) will still survive for a
|
16
|
-
while, but will only be accepting bug fixes from this point forward
|
17
|
-
unless someone else wants to take over more active maintainership of
|
18
|
-
the 1.x series.
|
19
|
-
|
20
|
-
### 2.0 Goals ###
|
21
|
-
|
22
|
-
* Implements [RFC 5545](http://tools.ietf.org/html/rfc5545)
|
23
|
-
* More obvious access to parameters and values
|
24
|
-
* Cleaner & easier timezone support
|
25
|
-
|
26
9
|
### Upgrade from 1.x ###
|
27
10
|
|
28
11
|
Better documentation is still to come, but in the meantime the changes needed to move from 1.x to 2.0 are summarized by the [diff needed to update the README](https://github.com/icalendar/icalendar/commit/bc3701e004c915a250054030a9375d1e7618857f)
|
@@ -92,11 +75,11 @@ event.summary.ical_params #=> {'altrep' => 'http://my.language.net', 'language'
|
|
92
75
|
|
93
76
|
#### Support for Dates or DateTimes
|
94
77
|
|
95
|
-
Sometimes we don't care if an event's start or end are `Date` or `DateTime` objects. For this, we can use `DateOrDateTime.new(value)
|
78
|
+
Sometimes we don't care if an event's start or end are `Date` or `DateTime` objects. For this, we can use `DateOrDateTime.new(value)`. Calling `.call` on the returned `DateOrDateTime` will immediately return the underlying `Date` or `DateTime` object.
|
96
79
|
|
97
80
|
```ruby
|
98
81
|
event = cal.event do |e|
|
99
|
-
e.dtstart = Icalendar::Values::DateOrDateTime.new('20140924')
|
82
|
+
e.dtstart = Icalendar::Values::DateOrDateTime.new('20140924')
|
100
83
|
e.dtend = Icalendar::Values::DateOrDateTime.new('20140925').call
|
101
84
|
e.summary = 'This is an all-day event, because DateOrDateTime will return Dates'
|
102
85
|
end
|
@@ -237,7 +220,7 @@ end
|
|
237
220
|
iCalendar has some basic support for creating VTIMEZONE blocks from timezone information pulled from `tzinfo`.
|
238
221
|
You must require `tzinfo` support manually to take advantage.
|
239
222
|
|
240
|
-
iCalendar has been tested and works with `tzinfo` versions 0.3 and 1.
|
223
|
+
iCalendar has been tested and works with `tzinfo` versions 0.3 and 1.x
|
241
224
|
|
242
225
|
#### Example ####
|
243
226
|
|
data/icalendar.gemspec
CHANGED
@@ -16,11 +16,6 @@ for the generation and parsing of .ics files, which are used by a
|
|
16
16
|
variety of calendaring applications.
|
17
17
|
EOD
|
18
18
|
s.post_install_message = <<-EOM
|
19
|
-
HEADS UP! iCalendar 2.0 is not backwards-compatible with 1.x. Please see the README for the new syntax
|
20
|
-
|
21
|
-
HEADS UP! icalendar 2.2.0 switches to non-strict parsing as default. Please see the README if you
|
22
|
-
rely on strict parsing for information on how to enable it.
|
23
|
-
|
24
19
|
ActiveSupport is required for TimeWithZone support, but not required for general use.
|
25
20
|
EOM
|
26
21
|
|
@@ -29,24 +24,26 @@ ActiveSupport is required for TimeWithZone support, but not required for general
|
|
29
24
|
s.executables = `git ls-files -- bin/*`.split("\n").map { |f| File.basename f }
|
30
25
|
s.require_paths = ['lib']
|
31
26
|
|
32
|
-
s.required_ruby_version = '>=
|
27
|
+
s.required_ruby_version = '>= 2.2.0'
|
33
28
|
|
34
|
-
s.
|
35
|
-
|
29
|
+
s.add_dependency 'ice_cube', '~> 0.16'
|
30
|
+
|
31
|
+
s.add_development_dependency 'rake', '~> 12.0'
|
32
|
+
s.add_development_dependency 'bundler', '~> 1.16'
|
36
33
|
|
37
34
|
# test with both groups of tzinfo dependencies
|
38
35
|
# tzinfo 1.x
|
39
|
-
s.add_development_dependency '
|
40
|
-
s.add_development_dependency '
|
36
|
+
s.add_development_dependency 'activesupport', '~> 5.2'
|
37
|
+
s.add_development_dependency 'i18n', '~> 1.1'
|
38
|
+
s.add_development_dependency 'tzinfo', '~> 1.2'
|
39
|
+
s.add_development_dependency 'tzinfo-data', '~> 1.2018'
|
41
40
|
# tzinfo 0.x
|
41
|
+
# s.add_development_dependency 'activesupport', '~> 4.0'
|
42
|
+
# s.add_development_dependency 'i18n', '~> 0.7'
|
42
43
|
# s.add_development_dependency 'tzinfo', '~> 0.3'
|
43
44
|
# end tzinfo
|
44
45
|
|
45
|
-
s.add_development_dependency '
|
46
|
-
|
47
|
-
s.add_development_dependency '
|
48
|
-
|
49
|
-
s.add_development_dependency 'timecop', '~> 0.7.0'
|
50
|
-
s.add_development_dependency 'rspec', '~> 3.0'
|
51
|
-
s.add_development_dependency 'simplecov', '~> 0.8'
|
46
|
+
s.add_development_dependency 'timecop', '~> 0.9'
|
47
|
+
s.add_development_dependency 'rspec', '~> 3.8'
|
48
|
+
s.add_development_dependency 'simplecov', '~> 0.16'
|
52
49
|
end
|
@@ -44,7 +44,11 @@ module Icalendar
|
|
44
44
|
|
45
45
|
def append_custom_property(property_name, value)
|
46
46
|
property_name = property_name.downcase
|
47
|
-
if
|
47
|
+
if self.class.single_properties.include? property_name
|
48
|
+
send "#{property_name}=", value
|
49
|
+
elsif self.class.multiple_properties.include? property_name
|
50
|
+
send "append_#{property_name}", value
|
51
|
+
elsif value.is_a? Icalendar::Value
|
48
52
|
custom_properties[property_name] << value
|
49
53
|
else
|
50
54
|
custom_properties[property_name] << Icalendar::Values::Text.new(value)
|
data/lib/icalendar/parser.rb
CHANGED
@@ -1,3 +1,5 @@
|
|
1
|
+
require 'icalendar/timezone_store'
|
2
|
+
|
1
3
|
module Icalendar
|
2
4
|
|
3
5
|
class Parser
|
@@ -19,8 +21,6 @@ module Icalendar
|
|
19
21
|
end
|
20
22
|
|
21
23
|
def parse
|
22
|
-
source.rewind
|
23
|
-
read_in_data
|
24
24
|
components = []
|
25
25
|
while (fields = next_fields)
|
26
26
|
component = component_class.new
|
@@ -101,6 +101,8 @@ module Icalendar
|
|
101
101
|
def parse_component(component)
|
102
102
|
while (fields = next_fields)
|
103
103
|
if fields[:name] == 'end'
|
104
|
+
klass_name = fields[:value].gsub(/\AV/, '').downcase.capitalize
|
105
|
+
TimezoneStore.store(component) if klass_name == 'Timezone'
|
104
106
|
break
|
105
107
|
elsif fields[:name] == 'begin'
|
106
108
|
klass_name = fields[:value].gsub(/\AV/, '').downcase.capitalize
|
data/lib/icalendar/timezone.rb
CHANGED
@@ -1,3 +1,5 @@
|
|
1
|
+
require 'ice_cube'
|
2
|
+
|
1
3
|
module Icalendar
|
2
4
|
|
3
5
|
class Timezone < Component
|
@@ -49,5 +51,56 @@ module Icalendar
|
|
49
51
|
standards.all? { |s| s.valid? strict } or return false
|
50
52
|
super
|
51
53
|
end
|
54
|
+
|
55
|
+
def offset_for_local(local)
|
56
|
+
standard = standard_for local
|
57
|
+
daylight = daylight_for local
|
58
|
+
|
59
|
+
if standard.nil? && daylight.nil?
|
60
|
+
"+00:00"
|
61
|
+
elsif daylight.nil?
|
62
|
+
standard.last.tzoffsetto
|
63
|
+
elsif standard.nil?
|
64
|
+
daylight.last.tzoffsetto
|
65
|
+
else
|
66
|
+
sdst = standard.first
|
67
|
+
ddst = daylight.first
|
68
|
+
if sdst > ddst
|
69
|
+
standard.last.tzoffsetto
|
70
|
+
else
|
71
|
+
daylight.last.tzoffsetto
|
72
|
+
end
|
73
|
+
end
|
74
|
+
end
|
75
|
+
|
76
|
+
def standard_for(local)
|
77
|
+
possible = standards.map do |std|
|
78
|
+
schedule = IceCube::Schedule.new(std.dtstart) do |s|
|
79
|
+
std.rrule.each do |rule|
|
80
|
+
s.add_recurrence_rule IceCube::Rule.from_ical(rule.value_ical)
|
81
|
+
end
|
82
|
+
std.rdate.each do |date|
|
83
|
+
s.add_recurrence_date date
|
84
|
+
end
|
85
|
+
end
|
86
|
+
[schedule.previous_occurrence(local.to_time), std]
|
87
|
+
end
|
88
|
+
possible.sort_by(&:first).last
|
89
|
+
end
|
90
|
+
|
91
|
+
def daylight_for(local)
|
92
|
+
possible = daylights.map do |day|
|
93
|
+
schedule = IceCube::Schedule.new(day.dtstart) do |s|
|
94
|
+
day.rrule.each do |rule|
|
95
|
+
s.add_recurrence_rule IceCube::Rule.from_ical(rule.value_ical)
|
96
|
+
end
|
97
|
+
day.rdate.each do |date|
|
98
|
+
s.add_recurrence_date date
|
99
|
+
end
|
100
|
+
end
|
101
|
+
[schedule.previous_occurrence(local.to_time), day]
|
102
|
+
end
|
103
|
+
possible.sort_by(&:first).last
|
104
|
+
end
|
52
105
|
end
|
53
106
|
end
|
@@ -0,0 +1,33 @@
|
|
1
|
+
require 'delegate'
|
2
|
+
require 'icalendar/downcased_hash'
|
3
|
+
|
4
|
+
module Icalendar
|
5
|
+
class TimezoneStore < ::SimpleDelegator
|
6
|
+
|
7
|
+
def initialize
|
8
|
+
super DowncasedHash.new({})
|
9
|
+
end
|
10
|
+
|
11
|
+
def self.instance
|
12
|
+
@instance ||= new
|
13
|
+
end
|
14
|
+
|
15
|
+
def self.store(timezone)
|
16
|
+
instance.store timezone
|
17
|
+
end
|
18
|
+
|
19
|
+
def self.retrieve(tzid)
|
20
|
+
instance.retrieve tzid
|
21
|
+
end
|
22
|
+
|
23
|
+
def store(timezone)
|
24
|
+
self[timezone.tzid] = timezone
|
25
|
+
end
|
26
|
+
|
27
|
+
def retrieve(tzid)
|
28
|
+
self[tzid]
|
29
|
+
end
|
30
|
+
|
31
|
+
end
|
32
|
+
|
33
|
+
end
|
@@ -2,25 +2,37 @@ module Icalendar
|
|
2
2
|
module Values
|
3
3
|
|
4
4
|
# DateOrDateTime can be used to set an attribute to either a Date or a DateTime value.
|
5
|
-
# It should not be used
|
6
|
-
class DateOrDateTime
|
5
|
+
# It should not be used without also invoking the `call` method.
|
6
|
+
class DateOrDateTime < Value
|
7
7
|
|
8
|
-
|
9
|
-
|
10
|
-
@value = value
|
11
|
-
@params = params
|
8
|
+
def call
|
9
|
+
parsed
|
12
10
|
end
|
13
11
|
|
14
|
-
def
|
12
|
+
def value_ical
|
13
|
+
parsed.value_ical
|
14
|
+
end
|
15
|
+
|
16
|
+
def params_ical
|
17
|
+
parsed.params_ical
|
18
|
+
end
|
19
|
+
|
20
|
+
private
|
21
|
+
|
22
|
+
def parsed
|
15
23
|
@parsed ||= begin
|
16
|
-
Icalendar::Values::DateTime.new value,
|
24
|
+
Icalendar::Values::DateTime.new value, ical_params
|
17
25
|
rescue Icalendar::Values::DateTime::FormatError
|
18
|
-
Icalendar::Values::Date.new value,
|
26
|
+
Icalendar::Values::Date.new value, ical_params
|
19
27
|
end
|
20
28
|
end
|
21
29
|
|
22
|
-
def
|
23
|
-
|
30
|
+
def needs_value_type?(default_type)
|
31
|
+
parsed.class != default_type
|
32
|
+
end
|
33
|
+
|
34
|
+
def value_type
|
35
|
+
parsed.class.value_type
|
24
36
|
end
|
25
37
|
|
26
38
|
end
|
@@ -1,3 +1,5 @@
|
|
1
|
+
require 'icalendar/timezone_store'
|
2
|
+
|
1
3
|
begin
|
2
4
|
require 'active_support/time'
|
3
5
|
|
@@ -17,14 +19,17 @@ module Icalendar
|
|
17
19
|
params = Icalendar::DowncasedHash(params)
|
18
20
|
@tz_utc = params['tzid'] == 'UTC'
|
19
21
|
|
20
|
-
|
22
|
+
offset_value = if params['tzid'].present?
|
21
23
|
tzid = params['tzid'].is_a?(::Array) ? params['tzid'].first : params['tzid']
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
24
|
+
if defined?(ActiveSupport::TimeZone) &&
|
25
|
+
defined?(ActiveSupportTimeWithZoneAdapter) &&
|
26
|
+
(tz = ActiveSupport::TimeZone[tzid])
|
27
|
+
ActiveSupportTimeWithZoneAdapter.new(nil, tz, value)
|
28
|
+
elsif (tz = TimezoneStore.retrieve(tzid))
|
29
|
+
value.change offset: tz.offset_for_local(value).to_s
|
30
|
+
end
|
27
31
|
end
|
32
|
+
super((offset_value || value), params)
|
28
33
|
end
|
29
34
|
|
30
35
|
def params_ical
|
data/lib/icalendar/values/uri.rb
CHANGED
@@ -6,7 +6,7 @@ module Icalendar
|
|
6
6
|
class Uri < Value
|
7
7
|
|
8
8
|
def initialize(value, params = {})
|
9
|
-
parsed = URI.parse
|
9
|
+
parsed = URI.parse(value) rescue value
|
10
10
|
super parsed, params
|
11
11
|
end
|
12
12
|
|
@@ -16,4 +16,4 @@ module Icalendar
|
|
16
16
|
end
|
17
17
|
|
18
18
|
end
|
19
|
-
end
|
19
|
+
end
|
@@ -21,6 +21,15 @@ module Icalendar
|
|
21
21
|
"#{behind? ? '-' : '+'}#{'%02d' % hours}#{'%02d' % minutes}#{'%02d' % seconds if seconds > 0}"
|
22
22
|
end
|
23
23
|
|
24
|
+
def to_s
|
25
|
+
str = "#{behind? ? '-' : '+'}#{'%02d' % hours}:#{'%02d' % minutes}"
|
26
|
+
if seconds > 0
|
27
|
+
"#{str}:#{'%02d' % seconds}"
|
28
|
+
else
|
29
|
+
str
|
30
|
+
end
|
31
|
+
end
|
32
|
+
|
24
33
|
private
|
25
34
|
|
26
35
|
def zero_offset?
|
data/lib/icalendar/version.rb
CHANGED
data/spec/event_spec.rb
CHANGED
@@ -96,6 +96,23 @@ describe Icalendar::Event do
|
|
96
96
|
end
|
97
97
|
end
|
98
98
|
|
99
|
+
describe "#append_custom_property" do
|
100
|
+
context "with custom property" do
|
101
|
+
it "appends to the custom properties hash" do
|
102
|
+
subject.append_custom_property "x_my_property", "test value"
|
103
|
+
expect(subject.custom_properties).to eq({"x_my_property" => ["test value"]})
|
104
|
+
end
|
105
|
+
end
|
106
|
+
|
107
|
+
context "with a defined property" do
|
108
|
+
it "sets the proper setter" do
|
109
|
+
subject.append_custom_property "summary", "event"
|
110
|
+
expect(subject.summary).to eq "event"
|
111
|
+
expect(subject.custom_properties).to eq({})
|
112
|
+
end
|
113
|
+
end
|
114
|
+
end
|
115
|
+
|
99
116
|
describe '.parse' do
|
100
117
|
let(:source) { File.read File.join(File.dirname(__FILE__), 'fixtures', fn) }
|
101
118
|
let(:fn) { 'event.ics' }
|
@@ -9,7 +9,7 @@ DTSTART;TZID=US-Mountain:20050120T170000
|
|
9
9
|
DTEND;TZID=US-Mountain:20050120T184500
|
10
10
|
CLASS:PRIVATE
|
11
11
|
GEO:37.386013;-122.0829322
|
12
|
-
ORGANIZER:mailto:joebob@random.net
|
12
|
+
ORGANIZER;CN="Joe Bob: Magician":mailto:joebob@random.net
|
13
13
|
PRIORITY:2
|
14
14
|
SUMMARY:This is a really long summary to test the method of unfolding lines
|
15
15
|
\, so I'm just going to make it a whole bunch of lines. With a twist: a "
|
@@ -0,0 +1,22 @@
|
|
1
|
+
BEGIN:VCALENDAR
|
2
|
+
VERSION:2.0
|
3
|
+
PRODID:bsprodidfortestabc123
|
4
|
+
CALSCALE:GREGORIAN
|
5
|
+
BEGIN:VEVENT
|
6
|
+
DTSTAMP:20050118T211523Z
|
7
|
+
UID:bsuidfortestabc123
|
8
|
+
DTSTART;TZID=US-Mountain:20050120T170000
|
9
|
+
DTEND;TZID=US-Mountain:20050120T184500
|
10
|
+
CLASS:PRIVATE
|
11
|
+
GEO:37.386013;-122.0829322
|
12
|
+
ORGANIZER;CN=Joe Bob\: Magician:mailto:joebob@random.net
|
13
|
+
PRIORITY:2
|
14
|
+
SUMMARY:This is a really long summary to test the method of unfolding lines
|
15
|
+
\, so I'm just going to make it a whole bunch of lines. With a twist: a "
|
16
|
+
ö" takes up multiple bytes\, and should be wrapped to the next line.
|
17
|
+
ATTACH:http://bush.sucks.org/impeach/him.rhtml
|
18
|
+
ATTACH:http://corporations-dominate.existence.net/why.rhtml
|
19
|
+
RDATE;TZID=US-Mountain:20050121T170000,20050122T170000
|
20
|
+
X-TEST-COMPONENT;QTEST="Hello, World":Shouldn't double double quotes
|
21
|
+
END:VEVENT
|
22
|
+
END:VCALENDAR
|
@@ -0,0 +1,22 @@
|
|
1
|
+
BEGIN:VCALENDAR
|
2
|
+
VERSION:2.0
|
3
|
+
PRODID:bsprodidfortestabc123
|
4
|
+
CALSCALE:GREGORIAN
|
5
|
+
BEGIN:VEVENT
|
6
|
+
DTSTAMP:20050118T211523Z
|
7
|
+
UID:bsuidfortestabc123
|
8
|
+
DTSTART;TZID=US-Mountain:20050120T170000
|
9
|
+
DTEND;TZID=US-Mountain:20050120T184500
|
10
|
+
CLASS:PRIVATE
|
11
|
+
GEO:37.386013;-122.0829322
|
12
|
+
ORGANIZER;CN=Joe Bob\:magician:mailto:joebob@random.net
|
13
|
+
PRIORITY:2
|
14
|
+
SUMMARY:This is a really long summary to test the method of unfolding lines
|
15
|
+
\, so I'm just going to make it a whole bunch of lines. With a twist: a "
|
16
|
+
ö" takes up multiple bytes\, and should be wrapped to the next line.
|
17
|
+
ATTACH:http://bush.sucks.org/impeach/him.rhtml
|
18
|
+
ATTACH:http://corporations-dominate.existence.net/why.rhtml
|
19
|
+
RDATE;TZID=US-Mountain:20050121T170000,20050122T170000
|
20
|
+
X-TEST-COMPONENT;QTEST="Hello, World":Shouldn't double double quotes
|
21
|
+
END:VEVENT
|
22
|
+
END:VCALENDAR
|
@@ -0,0 +1,31 @@
|
|
1
|
+
BEGIN:VCALENDAR
|
2
|
+
METHOD:PUBLISH
|
3
|
+
VERSION:2.0
|
4
|
+
CALSCALE:GREGORIAN
|
5
|
+
BEGIN:VTIMEZONE
|
6
|
+
TZID:(GMT-05:00) Eastern Time (US & Canada)
|
7
|
+
BEGIN:STANDARD
|
8
|
+
DTSTART:16011104T010000
|
9
|
+
RRULE:FREQ=YEARLY;BYDAY=1SU;BYMONTH=11
|
10
|
+
TZOFFSETFROM:-0400
|
11
|
+
TZOFFSETTO:-0500
|
12
|
+
END:STANDARD
|
13
|
+
BEGIN:DAYLIGHT
|
14
|
+
DTSTART:16010311T030000
|
15
|
+
RRULE:FREQ=YEARLY;BYDAY=2SU;BYMONTH=3
|
16
|
+
TZOFFSETFROM:-0500
|
17
|
+
TZOFFSETTO:-0400
|
18
|
+
END:DAYLIGHT
|
19
|
+
END:VTIMEZONE
|
20
|
+
BEGIN:VEVENT
|
21
|
+
CREATED:20120104T225934Z
|
22
|
+
UID:23D50AF6-23D4-4CB5-85C6-4826F3F19999
|
23
|
+
DTEND;TZID="(GMT-05:00) Eastern Time (US & Canada)":20180104T130000
|
24
|
+
RRULE:FREQ=WEEKLY;INTERVAL=1
|
25
|
+
TRANSP:OPAQUE
|
26
|
+
SUMMARY:Recurring on Wed
|
27
|
+
DTSTART;TZID="(GMT-05:00) Eastern Time (US & Canada)":20180104T100000
|
28
|
+
DTSTAMP:20120104T231637Z
|
29
|
+
SEQUENCE:6
|
30
|
+
END:VEVENT
|
31
|
+
END:VCALENDAR
|
data/spec/parser_spec.rb
CHANGED
@@ -9,9 +9,10 @@ describe Icalendar::Parser do
|
|
9
9
|
let(:fn) { 'single_event.ics' }
|
10
10
|
|
11
11
|
it 'returns an array of calendars' do
|
12
|
-
|
13
|
-
expect(
|
14
|
-
expect(
|
12
|
+
parsed = subject.parse
|
13
|
+
expect(parsed).to be_instance_of Array
|
14
|
+
expect(parsed.count).to eq 1
|
15
|
+
expect(parsed[0]).to be_instance_of Icalendar::Calendar
|
15
16
|
end
|
16
17
|
|
17
18
|
it 'properly splits multi-valued lines' do
|
@@ -38,9 +39,10 @@ describe Icalendar::Parser do
|
|
38
39
|
before { subject.component_class = Icalendar::Event }
|
39
40
|
|
40
41
|
it 'returns an array of events' do
|
41
|
-
|
42
|
-
expect(
|
43
|
-
expect(
|
42
|
+
parsed = subject.parse
|
43
|
+
expect(parsed).to be_instance_of Array
|
44
|
+
expect(parsed.count).to be 1
|
45
|
+
expect(parsed[0]).to be_instance_of Icalendar::Event
|
44
46
|
end
|
45
47
|
end
|
46
48
|
context 'events.ics' do
|
@@ -55,15 +57,25 @@ describe Icalendar::Parser do
|
|
55
57
|
expect(events.last.uid).to eq("uid-1234-uid-4321")
|
56
58
|
end
|
57
59
|
end
|
60
|
+
context 'tzid_search.ics' do
|
61
|
+
let(:fn) { 'tzid_search.ics' }
|
62
|
+
|
63
|
+
it 'correctly sets the weird tzid' do
|
64
|
+
parsed = subject.parse
|
65
|
+
event = parsed.first.events.first
|
66
|
+
expect(event.dtstart.utc).to eq Time.parse("20180104T150000Z")
|
67
|
+
end
|
68
|
+
end
|
58
69
|
end
|
59
70
|
|
60
71
|
describe '#parse with bad line' do
|
61
72
|
let(:fn) { 'single_event_bad_line.ics' }
|
62
73
|
|
63
74
|
it 'returns an array of calendars' do
|
64
|
-
|
65
|
-
expect(
|
66
|
-
expect(
|
75
|
+
parsed = subject.parse
|
76
|
+
expect(parsed).to be_instance_of Array
|
77
|
+
expect(parsed.count).to eq 1
|
78
|
+
expect(parsed[0]).to be_instance_of Icalendar::Calendar
|
67
79
|
end
|
68
80
|
|
69
81
|
it 'properly splits multi-valued lines' do
|
data/spec/roundtrip_spec.rb
CHANGED
@@ -19,6 +19,20 @@ describe Icalendar do
|
|
19
19
|
end
|
20
20
|
end
|
21
21
|
|
22
|
+
describe 'cleanly handle facebook organizers' do
|
23
|
+
let(:source) { File.read File.join(File.dirname(__FILE__), 'fixtures', 'single_event_bad_organizer.ics') }
|
24
|
+
let(:source_lowered_uri) { File.read File.join(File.dirname(__FILE__), 'fixtures', 'single_event_organizer_parsed.ics') }
|
25
|
+
it 'will generate the same file as it parsed' do
|
26
|
+
ical = Icalendar::Calendar.parse(source).first.to_ical
|
27
|
+
source_equal = ical == source
|
28
|
+
# rbx-3 parses the organizer as a URI, which strips the space and lowercases everything after the first :
|
29
|
+
# this is correct behavior, according to the icalendar spec, so we're not fudging the parser to accomodate
|
30
|
+
# facebook not properly wrapping the CN param in dquotes
|
31
|
+
source_lowered_equal = ical == source_lowered_uri
|
32
|
+
expect(source_equal || source_lowered_equal).to be true
|
33
|
+
end
|
34
|
+
end
|
35
|
+
|
22
36
|
describe 'timezone round trip' do
|
23
37
|
let(:source) { File.read File.join(File.dirname(__FILE__), 'fixtures', 'timezone.ics') }
|
24
38
|
it 'will generate the same file as it parsed' do
|
@@ -38,4 +38,14 @@ describe Icalendar::Values::DateOrDateTime do
|
|
38
38
|
end
|
39
39
|
end
|
40
40
|
end
|
41
|
+
|
42
|
+
describe "#to_ical" do
|
43
|
+
let(:event) { Icalendar::Event.new }
|
44
|
+
let(:time_stamp) { Time.now.strftime Icalendar::Values::DateTime::FORMAT }
|
45
|
+
|
46
|
+
it "should call parse behind the scenes" do
|
47
|
+
event.dtstart = described_class.new time_stamp, "tzid" => "UTC"
|
48
|
+
expect(event.to_ical).to include "DTSTART:#{time_stamp}Z"
|
49
|
+
end
|
50
|
+
end
|
41
51
|
end
|
metadata
CHANGED
@@ -1,141 +1,155 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: icalendar
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 2.
|
4
|
+
version: 2.5.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Ryan Ahearn
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date:
|
11
|
+
date: 2018-09-10 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
|
+
- !ruby/object:Gem::Dependency
|
14
|
+
name: ice_cube
|
15
|
+
requirement: !ruby/object:Gem::Requirement
|
16
|
+
requirements:
|
17
|
+
- - "~>"
|
18
|
+
- !ruby/object:Gem::Version
|
19
|
+
version: '0.16'
|
20
|
+
type: :runtime
|
21
|
+
prerelease: false
|
22
|
+
version_requirements: !ruby/object:Gem::Requirement
|
23
|
+
requirements:
|
24
|
+
- - "~>"
|
25
|
+
- !ruby/object:Gem::Version
|
26
|
+
version: '0.16'
|
13
27
|
- !ruby/object:Gem::Dependency
|
14
28
|
name: rake
|
15
29
|
requirement: !ruby/object:Gem::Requirement
|
16
30
|
requirements:
|
17
31
|
- - "~>"
|
18
32
|
- !ruby/object:Gem::Version
|
19
|
-
version: '
|
33
|
+
version: '12.0'
|
20
34
|
type: :development
|
21
35
|
prerelease: false
|
22
36
|
version_requirements: !ruby/object:Gem::Requirement
|
23
37
|
requirements:
|
24
38
|
- - "~>"
|
25
39
|
- !ruby/object:Gem::Version
|
26
|
-
version: '
|
40
|
+
version: '12.0'
|
27
41
|
- !ruby/object:Gem::Dependency
|
28
42
|
name: bundler
|
29
43
|
requirement: !ruby/object:Gem::Requirement
|
30
44
|
requirements:
|
31
45
|
- - "~>"
|
32
46
|
- !ruby/object:Gem::Version
|
33
|
-
version: '1.
|
47
|
+
version: '1.16'
|
34
48
|
type: :development
|
35
49
|
prerelease: false
|
36
50
|
version_requirements: !ruby/object:Gem::Requirement
|
37
51
|
requirements:
|
38
52
|
- - "~>"
|
39
53
|
- !ruby/object:Gem::Version
|
40
|
-
version: '1.
|
54
|
+
version: '1.16'
|
41
55
|
- !ruby/object:Gem::Dependency
|
42
|
-
name:
|
56
|
+
name: activesupport
|
43
57
|
requirement: !ruby/object:Gem::Requirement
|
44
58
|
requirements:
|
45
59
|
- - "~>"
|
46
60
|
- !ruby/object:Gem::Version
|
47
|
-
version: '
|
61
|
+
version: '5.2'
|
48
62
|
type: :development
|
49
63
|
prerelease: false
|
50
64
|
version_requirements: !ruby/object:Gem::Requirement
|
51
65
|
requirements:
|
52
66
|
- - "~>"
|
53
67
|
- !ruby/object:Gem::Version
|
54
|
-
version: '
|
68
|
+
version: '5.2'
|
55
69
|
- !ruby/object:Gem::Dependency
|
56
|
-
name:
|
70
|
+
name: i18n
|
57
71
|
requirement: !ruby/object:Gem::Requirement
|
58
72
|
requirements:
|
59
73
|
- - "~>"
|
60
74
|
- !ruby/object:Gem::Version
|
61
|
-
version: '1.
|
75
|
+
version: '1.1'
|
62
76
|
type: :development
|
63
77
|
prerelease: false
|
64
78
|
version_requirements: !ruby/object:Gem::Requirement
|
65
79
|
requirements:
|
66
80
|
- - "~>"
|
67
81
|
- !ruby/object:Gem::Version
|
68
|
-
version: '1.
|
82
|
+
version: '1.1'
|
69
83
|
- !ruby/object:Gem::Dependency
|
70
|
-
name:
|
84
|
+
name: tzinfo
|
71
85
|
requirement: !ruby/object:Gem::Requirement
|
72
86
|
requirements:
|
73
87
|
- - "~>"
|
74
88
|
- !ruby/object:Gem::Version
|
75
|
-
version: '
|
89
|
+
version: '1.2'
|
76
90
|
type: :development
|
77
91
|
prerelease: false
|
78
92
|
version_requirements: !ruby/object:Gem::Requirement
|
79
93
|
requirements:
|
80
94
|
- - "~>"
|
81
95
|
- !ruby/object:Gem::Version
|
82
|
-
version: '
|
96
|
+
version: '1.2'
|
83
97
|
- !ruby/object:Gem::Dependency
|
84
|
-
name:
|
98
|
+
name: tzinfo-data
|
85
99
|
requirement: !ruby/object:Gem::Requirement
|
86
100
|
requirements:
|
87
|
-
- - "
|
101
|
+
- - "~>"
|
88
102
|
- !ruby/object:Gem::Version
|
89
|
-
version:
|
103
|
+
version: '1.2018'
|
90
104
|
type: :development
|
91
105
|
prerelease: false
|
92
106
|
version_requirements: !ruby/object:Gem::Requirement
|
93
107
|
requirements:
|
94
|
-
- - "
|
108
|
+
- - "~>"
|
95
109
|
- !ruby/object:Gem::Version
|
96
|
-
version:
|
110
|
+
version: '1.2018'
|
97
111
|
- !ruby/object:Gem::Dependency
|
98
112
|
name: timecop
|
99
113
|
requirement: !ruby/object:Gem::Requirement
|
100
114
|
requirements:
|
101
115
|
- - "~>"
|
102
116
|
- !ruby/object:Gem::Version
|
103
|
-
version: 0.
|
117
|
+
version: '0.9'
|
104
118
|
type: :development
|
105
119
|
prerelease: false
|
106
120
|
version_requirements: !ruby/object:Gem::Requirement
|
107
121
|
requirements:
|
108
122
|
- - "~>"
|
109
123
|
- !ruby/object:Gem::Version
|
110
|
-
version: 0.
|
124
|
+
version: '0.9'
|
111
125
|
- !ruby/object:Gem::Dependency
|
112
126
|
name: rspec
|
113
127
|
requirement: !ruby/object:Gem::Requirement
|
114
128
|
requirements:
|
115
129
|
- - "~>"
|
116
130
|
- !ruby/object:Gem::Version
|
117
|
-
version: '3.
|
131
|
+
version: '3.8'
|
118
132
|
type: :development
|
119
133
|
prerelease: false
|
120
134
|
version_requirements: !ruby/object:Gem::Requirement
|
121
135
|
requirements:
|
122
136
|
- - "~>"
|
123
137
|
- !ruby/object:Gem::Version
|
124
|
-
version: '3.
|
138
|
+
version: '3.8'
|
125
139
|
- !ruby/object:Gem::Dependency
|
126
140
|
name: simplecov
|
127
141
|
requirement: !ruby/object:Gem::Requirement
|
128
142
|
requirements:
|
129
143
|
- - "~>"
|
130
144
|
- !ruby/object:Gem::Version
|
131
|
-
version: '0.
|
145
|
+
version: '0.16'
|
132
146
|
type: :development
|
133
147
|
prerelease: false
|
134
148
|
version_requirements: !ruby/object:Gem::Requirement
|
135
149
|
requirements:
|
136
150
|
- - "~>"
|
137
151
|
- !ruby/object:Gem::Version
|
138
|
-
version: '0.
|
152
|
+
version: '0.16'
|
139
153
|
description: |
|
140
154
|
Implements the iCalendar specification (RFC-5545) in Ruby. This allows
|
141
155
|
for the generation and parsing of .ics files, which are used by a
|
@@ -149,9 +163,9 @@ files:
|
|
149
163
|
- ".gitignore"
|
150
164
|
- ".rspec"
|
151
165
|
- ".travis.yml"
|
152
|
-
- COPYING
|
153
166
|
- Gemfile
|
154
167
|
- History.txt
|
168
|
+
- LICENSE
|
155
169
|
- README.md
|
156
170
|
- Rakefile
|
157
171
|
- icalendar.gemspec
|
@@ -168,6 +182,7 @@ files:
|
|
168
182
|
- lib/icalendar/logger.rb
|
169
183
|
- lib/icalendar/parser.rb
|
170
184
|
- lib/icalendar/timezone.rb
|
185
|
+
- lib/icalendar/timezone_store.rb
|
171
186
|
- lib/icalendar/todo.rb
|
172
187
|
- lib/icalendar/tzinfo.rb
|
173
188
|
- lib/icalendar/value.rb
|
@@ -201,11 +216,14 @@ files:
|
|
201
216
|
- spec/fixtures/single_event.ics
|
202
217
|
- spec/fixtures/single_event_bad_dtstart.ics
|
203
218
|
- spec/fixtures/single_event_bad_line.ics
|
219
|
+
- spec/fixtures/single_event_bad_organizer.ics
|
220
|
+
- spec/fixtures/single_event_organizer_parsed.ics
|
204
221
|
- spec/fixtures/timezone.ics
|
205
222
|
- spec/fixtures/two_date_time_events.ics
|
206
223
|
- spec/fixtures/two_day_events.ics
|
207
224
|
- spec/fixtures/two_events.ics
|
208
225
|
- spec/fixtures/two_time_events.ics
|
226
|
+
- spec/fixtures/tzid_search.ics
|
209
227
|
- spec/freebusy_spec.rb
|
210
228
|
- spec/journal_spec.rb
|
211
229
|
- spec/parser_spec.rb
|
@@ -224,13 +242,10 @@ files:
|
|
224
242
|
homepage: https://github.com/icalendar/icalendar
|
225
243
|
licenses: []
|
226
244
|
metadata: {}
|
227
|
-
post_install_message:
|
228
|
-
|
229
|
-
|
230
|
-
HEADS UP! icalendar 2.2.0 switches to non-strict parsing as default. Please see the README if you
|
231
|
-
rely on strict parsing for information on how to enable it.
|
245
|
+
post_install_message: 'ActiveSupport is required for TimeWithZone support, but not
|
246
|
+
required for general use.
|
232
247
|
|
233
|
-
|
248
|
+
'
|
234
249
|
rdoc_options: []
|
235
250
|
require_paths:
|
236
251
|
- lib
|
@@ -238,7 +253,7 @@ required_ruby_version: !ruby/object:Gem::Requirement
|
|
238
253
|
requirements:
|
239
254
|
- - ">="
|
240
255
|
- !ruby/object:Gem::Version
|
241
|
-
version:
|
256
|
+
version: 2.2.0
|
242
257
|
required_rubygems_version: !ruby/object:Gem::Requirement
|
243
258
|
requirements:
|
244
259
|
- - ">="
|
@@ -246,7 +261,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
246
261
|
version: '0'
|
247
262
|
requirements: []
|
248
263
|
rubyforge_project:
|
249
|
-
rubygems_version: 2.
|
264
|
+
rubygems_version: 2.7.6
|
250
265
|
signing_key:
|
251
266
|
specification_version: 4
|
252
267
|
summary: A ruby implementation of the iCalendar specification (RFC-5545).
|
@@ -262,11 +277,14 @@ test_files:
|
|
262
277
|
- spec/fixtures/single_event.ics
|
263
278
|
- spec/fixtures/single_event_bad_dtstart.ics
|
264
279
|
- spec/fixtures/single_event_bad_line.ics
|
280
|
+
- spec/fixtures/single_event_bad_organizer.ics
|
281
|
+
- spec/fixtures/single_event_organizer_parsed.ics
|
265
282
|
- spec/fixtures/timezone.ics
|
266
283
|
- spec/fixtures/two_date_time_events.ics
|
267
284
|
- spec/fixtures/two_day_events.ics
|
268
285
|
- spec/fixtures/two_events.ics
|
269
286
|
- spec/fixtures/two_time_events.ics
|
287
|
+
- spec/fixtures/tzid_search.ics
|
270
288
|
- spec/freebusy_spec.rb
|
271
289
|
- spec/journal_spec.rb
|
272
290
|
- spec/parser_spec.rb
|