icalendar 2.2.2 → 2.3.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/History.txt +6 -0
- data/lib/icalendar/downcased_hash.rb +40 -0
- data/lib/icalendar/has_components.rb +1 -1
- data/lib/icalendar/parser.rb +30 -17
- data/lib/icalendar/value.rb +9 -4
- data/lib/icalendar/values/array.rb +15 -3
- data/lib/icalendar/values/date_time.rb +3 -1
- data/lib/icalendar/values/time_with_zone.rb +2 -1
- data/lib/icalendar/version.rb +1 -1
- data/spec/downcased_hash_spec.rb +52 -0
- data/spec/fixtures/recurrence.ics +18 -0
- data/spec/fixtures/single_event_bad_dtstart.ics +21 -0
- data/spec/parser_spec.rb +34 -14
- data/spec/roundtrip_spec.rb +8 -0
- data/spec/values/date_time_spec.rb +9 -0
- metadata +10 -3
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 3ed623b569af5c7f68aa5917f19acedb5a11f7e8
|
4
|
+
data.tar.gz: 3293337f990892e2c07cc9c8d23f5bf4b86e5a37
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 4a95ab23132eeffa95b42b1c2da033aa46b92775caf43928458ffd048685f66095855235b1b951f1bb18a1a33b7fb817cc1e4fc3f1b4917a277a3f18306dc592
|
7
|
+
data.tar.gz: 8d77c499752d3f589b9dca961d61df24f9b67a89f4764c462e71bcedd313a0f8d3b1aace26849bbe183a415235e4c6b159b8ea4b194b37cf9b202a73f001c568
|
data/History.txt
CHANGED
@@ -1,3 +1,9 @@
|
|
1
|
+
=== 2.3.0 2015-04-26
|
2
|
+
* fix value parameter for properties with multiple values
|
3
|
+
* fix error when assigning Icalendar::Values::Array to a component
|
4
|
+
* Fall back to Icalendar::Values::Date if Icalendar::Values::DateTime is not given a properly formatted value
|
5
|
+
* Downcase the keys in ical_params to ensure we aren't assigning both tzid and TZID
|
6
|
+
|
1
7
|
=== 2.2.2 2014-12-27
|
2
8
|
* add a `has_#{component}?` method for testing if component exists - John Hope
|
3
9
|
* add documentation & tests for organizer attribute - Ben Walding
|
@@ -0,0 +1,40 @@
|
|
1
|
+
require 'delegate'
|
2
|
+
|
3
|
+
module Icalendar
|
4
|
+
class DowncasedHash < ::SimpleDelegator
|
5
|
+
|
6
|
+
def initialize(base)
|
7
|
+
super Hash.new
|
8
|
+
base.each do |key, value|
|
9
|
+
self[key] = value
|
10
|
+
end
|
11
|
+
end
|
12
|
+
|
13
|
+
def []=(key, value)
|
14
|
+
__getobj__[key.to_s.downcase] = value
|
15
|
+
end
|
16
|
+
|
17
|
+
def [](key)
|
18
|
+
__getobj__[key.to_s.downcase]
|
19
|
+
end
|
20
|
+
|
21
|
+
def has_key?(key)
|
22
|
+
__getobj__.has_key? key.to_s.downcase
|
23
|
+
end
|
24
|
+
alias_method :include?, :has_key?
|
25
|
+
alias_method :member?, :has_key?
|
26
|
+
|
27
|
+
def delete(key, &block)
|
28
|
+
__getobj__.delete key.to_s.downcase, &block
|
29
|
+
end
|
30
|
+
end
|
31
|
+
|
32
|
+
def self.DowncasedHash(base)
|
33
|
+
case base
|
34
|
+
when Icalendar::DowncasedHash then base
|
35
|
+
when Hash then Icalendar::DowncasedHash.new(base)
|
36
|
+
else
|
37
|
+
fail ArgumentError
|
38
|
+
end
|
39
|
+
end
|
40
|
+
end
|
data/lib/icalendar/parser.rb
CHANGED
@@ -32,23 +32,7 @@ module Icalendar
|
|
32
32
|
|
33
33
|
def parse_property(component, fields = nil)
|
34
34
|
fields = next_fields if fields.nil?
|
35
|
-
|
36
|
-
if !fields[:params]['value'].nil?
|
37
|
-
klass_name = fields[:params].delete('value').first
|
38
|
-
unless klass_name.upcase == klass.value_type
|
39
|
-
klass_name = klass_name.downcase.gsub(/(?:\A|-)(.)/) { |m| m[-1].upcase }
|
40
|
-
klass = Icalendar::Values.const_get klass_name if Icalendar::Values.const_defined?(klass_name)
|
41
|
-
end
|
42
|
-
end
|
43
|
-
if klass.value_type != 'RECUR' && fields[:value] =~ /(?<!\\)([,;])/
|
44
|
-
delimiter = $1
|
45
|
-
prop_value = Icalendar::Values::Array.new fields[:value].split(/(?<!\\)[;,]/),
|
46
|
-
klass,
|
47
|
-
fields[:params],
|
48
|
-
delimiter: delimiter
|
49
|
-
else
|
50
|
-
prop_value = klass.new fields[:value], fields[:params]
|
51
|
-
end
|
35
|
+
prop_value = wrap_property_value component, fields
|
52
36
|
prop_name = %w(class method).include?(fields[:name]) ? "ip_#{fields[:name]}" : fields[:name]
|
53
37
|
begin
|
54
38
|
method_name = if component.class.multiple_properties.include? prop_name
|
@@ -68,6 +52,35 @@ module Icalendar
|
|
68
52
|
end
|
69
53
|
end
|
70
54
|
|
55
|
+
def wrap_property_value(component, fields)
|
56
|
+
klass = get_wrapper_class component, fields
|
57
|
+
if klass.value_type != 'RECUR' && fields[:value] =~ /(?<!\\)([,;])/
|
58
|
+
delimiter = $1
|
59
|
+
Icalendar::Values::Array.new fields[:value].split(/(?<!\\)[;,]/),
|
60
|
+
klass,
|
61
|
+
fields[:params],
|
62
|
+
delimiter: delimiter
|
63
|
+
else
|
64
|
+
klass.new fields[:value], fields[:params]
|
65
|
+
end
|
66
|
+
rescue Icalendar::Values::DateTime::FormatError => fe
|
67
|
+
raise fe if strict?
|
68
|
+
fields[:params]['value'] = ['DATE']
|
69
|
+
retry
|
70
|
+
end
|
71
|
+
|
72
|
+
def get_wrapper_class(component, fields)
|
73
|
+
klass = component.class.default_property_types[fields[:name]]
|
74
|
+
if !fields[:params]['value'].nil?
|
75
|
+
klass_name = fields[:params].delete('value').first
|
76
|
+
unless klass_name.upcase == klass.value_type
|
77
|
+
klass_name = klass_name.downcase.gsub(/(?:\A|-)(.)/) { |m| m[-1].upcase }
|
78
|
+
klass = Icalendar::Values.const_get klass_name if Icalendar::Values.const_defined?(klass_name)
|
79
|
+
end
|
80
|
+
end
|
81
|
+
klass
|
82
|
+
end
|
83
|
+
|
71
84
|
def strict?
|
72
85
|
!!@strict
|
73
86
|
end
|
data/lib/icalendar/value.rb
CHANGED
@@ -1,4 +1,5 @@
|
|
1
1
|
require 'delegate'
|
2
|
+
require 'icalendar/downcased_hash'
|
2
3
|
|
3
4
|
module Icalendar
|
4
5
|
|
@@ -7,12 +8,12 @@ module Icalendar
|
|
7
8
|
attr_accessor :ical_params
|
8
9
|
|
9
10
|
def initialize(value, params = {})
|
10
|
-
@ical_params = params
|
11
|
+
@ical_params = Icalendar::DowncasedHash(params)
|
11
12
|
super value
|
12
13
|
end
|
13
14
|
|
14
15
|
def ical_param(key, value)
|
15
|
-
@ical_params[key
|
16
|
+
@ical_params[key] = value
|
16
17
|
end
|
17
18
|
|
18
19
|
def value
|
@@ -20,7 +21,7 @@ module Icalendar
|
|
20
21
|
end
|
21
22
|
|
22
23
|
def to_ical(default_type)
|
23
|
-
ical_param 'value', self.
|
24
|
+
ical_param 'value', self.value_type if needs_value_type?(default_type)
|
24
25
|
"#{params_ical}:#{value_ical}"
|
25
26
|
end
|
26
27
|
|
@@ -34,6 +35,10 @@ module Icalendar
|
|
34
35
|
name.gsub(/\A.*::/, '').gsub(/(?<!\A)[A-Z]/, '-\0').upcase
|
35
36
|
end
|
36
37
|
|
38
|
+
def value_type
|
39
|
+
self.class.value_type
|
40
|
+
end
|
41
|
+
|
37
42
|
private
|
38
43
|
|
39
44
|
def needs_value_type?(default_type)
|
@@ -77,4 +82,4 @@ require_relative 'values/uri'
|
|
77
82
|
require_relative 'values/utc_offset'
|
78
83
|
|
79
84
|
# further refine above classes
|
80
|
-
require_relative 'values/cal_address'
|
85
|
+
require_relative 'values/cal_address'
|
@@ -3,10 +3,18 @@ module Icalendar
|
|
3
3
|
|
4
4
|
class Array < Value
|
5
5
|
|
6
|
+
attr_reader :value_delimiter
|
7
|
+
|
6
8
|
def initialize(value, klass, params = {}, options = {})
|
7
9
|
@value_delimiter = options[:delimiter] || ','
|
8
10
|
mapped = if value.is_a? ::Array
|
9
|
-
value.map
|
11
|
+
value.map do |v|
|
12
|
+
if v.is_a? Icalendar::Values::Array
|
13
|
+
Icalendar::Values::Array.new v.value, klass, v.ical_params, delimiter: v.value_delimiter
|
14
|
+
else
|
15
|
+
klass.new v, params
|
16
|
+
end
|
17
|
+
end
|
10
18
|
else
|
11
19
|
[klass.new(value, params)]
|
12
20
|
end
|
@@ -23,7 +31,7 @@ module Icalendar
|
|
23
31
|
def value_ical
|
24
32
|
value.map do |v|
|
25
33
|
v.value_ical
|
26
|
-
end.join
|
34
|
+
end.join value_delimiter
|
27
35
|
end
|
28
36
|
|
29
37
|
def valid?
|
@@ -31,6 +39,10 @@ module Icalendar
|
|
31
39
|
!value.all? { |v| v.class == klass }
|
32
40
|
end
|
33
41
|
|
42
|
+
def value_type
|
43
|
+
value.first.value_type
|
44
|
+
end
|
45
|
+
|
34
46
|
private
|
35
47
|
|
36
48
|
def needs_value_type?(default_type)
|
@@ -40,4 +52,4 @@ module Icalendar
|
|
40
52
|
end
|
41
53
|
|
42
54
|
end
|
43
|
-
end
|
55
|
+
end
|
@@ -16,7 +16,7 @@ module Icalendar
|
|
16
16
|
begin
|
17
17
|
parsed_date = ::DateTime.strptime(value, FORMAT)
|
18
18
|
rescue ArgumentError => e
|
19
|
-
raise
|
19
|
+
raise FormatError.new("Failed to parse \"#{value}\" - #{e.message}")
|
20
20
|
end
|
21
21
|
|
22
22
|
super parsed_date, params
|
@@ -43,6 +43,8 @@ module Icalendar
|
|
43
43
|
end
|
44
44
|
end
|
45
45
|
|
46
|
+
class FormatError < ArgumentError
|
47
|
+
end
|
46
48
|
end
|
47
49
|
|
48
50
|
end
|
@@ -14,6 +14,7 @@ module Icalendar
|
|
14
14
|
attr_reader :tz_utc
|
15
15
|
|
16
16
|
def initialize(value, params = {})
|
17
|
+
params = Icalendar::DowncasedHash(params)
|
17
18
|
@tz_utc = params['tzid'] == 'UTC'
|
18
19
|
|
19
20
|
if defined?(ActiveSupport::TimeZone) && defined?(ActiveSupportTimeWithZoneAdapter) && !params['tzid'].nil?
|
@@ -22,7 +23,7 @@ module Icalendar
|
|
22
23
|
value = ActiveSupportTimeWithZoneAdapter.new nil, zone, value unless zone.nil?
|
23
24
|
super value, params
|
24
25
|
else
|
25
|
-
super
|
26
|
+
super value, params
|
26
27
|
end
|
27
28
|
end
|
28
29
|
|
data/lib/icalendar/version.rb
CHANGED
@@ -0,0 +1,52 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe Icalendar::DowncasedHash do
|
4
|
+
|
5
|
+
subject { described_class.new base }
|
6
|
+
let(:base) { {'hello' => 'world'} }
|
7
|
+
|
8
|
+
describe '#[]=' do
|
9
|
+
it 'sets a new value' do
|
10
|
+
subject['FOO'] = 'bar'
|
11
|
+
expect(subject['foo']).to eq 'bar'
|
12
|
+
end
|
13
|
+
end
|
14
|
+
|
15
|
+
describe '#[]' do
|
16
|
+
it 'gets an already set value' do
|
17
|
+
subject['foo'] = 'bar'
|
18
|
+
expect(subject['FOO']).to eq 'bar'
|
19
|
+
end
|
20
|
+
end
|
21
|
+
|
22
|
+
describe '#has_key?' do
|
23
|
+
it 'correctly identifies keys in the hash' do
|
24
|
+
expect(subject.has_key? 'hello').to be true
|
25
|
+
expect(subject.has_key? 'HELLO').to be true
|
26
|
+
end
|
27
|
+
end
|
28
|
+
|
29
|
+
describe '#delete' do
|
30
|
+
context 'no block' do
|
31
|
+
it 'removes the key' do
|
32
|
+
subject.delete 'HELLO'
|
33
|
+
expect(subject.has_key? 'hello').to be false
|
34
|
+
end
|
35
|
+
end
|
36
|
+
context 'with a block' do
|
37
|
+
it 'calls the block when the key is not found' do
|
38
|
+
expect { |b| subject.delete 'nokey', &b }.to yield_with_args('nokey')
|
39
|
+
end
|
40
|
+
end
|
41
|
+
end
|
42
|
+
|
43
|
+
describe 'DowncasedHash()' do
|
44
|
+
it 'returns self when passed an DowncasedHash' do
|
45
|
+
expect(Icalendar::DowncasedHash(subject)).to be subject
|
46
|
+
end
|
47
|
+
|
48
|
+
it 'wraps a hash in an downcased hash' do
|
49
|
+
expect(Icalendar::DowncasedHash(base)).to be_kind_of Icalendar::DowncasedHash
|
50
|
+
end
|
51
|
+
end
|
52
|
+
end
|
@@ -0,0 +1,18 @@
|
|
1
|
+
BEGIN:VCALENDAR
|
2
|
+
VERSION:2.0
|
3
|
+
PRODID:bsprodidfortestabc123
|
4
|
+
CALSCALE:GREGORIAN
|
5
|
+
BEGIN:VEVENT
|
6
|
+
DTSTAMP:20050118T211523Z
|
7
|
+
UID:bsuidfortestabc123
|
8
|
+
DTSTART;VALUE=DATE:20050323
|
9
|
+
DTEND;VALUE=DATE:20050323
|
10
|
+
CLASS:PRIVATE
|
11
|
+
ORGANIZER:mailto:joebob@random.net
|
12
|
+
PRIORITY:2
|
13
|
+
SUMMARY:This is a really long summary to test the method of unfolding lines
|
14
|
+
\, so I'm just going to make it a whole bunch of lines.
|
15
|
+
EXDATE;VALUE=DATE:20120323,20130323
|
16
|
+
RRULE:FREQ=YEARLY
|
17
|
+
END:VEVENT
|
18
|
+
END:VCALENDAR
|
@@ -0,0 +1,21 @@
|
|
1
|
+
BEGIN:VCALENDAR
|
2
|
+
VERSION:2.0
|
3
|
+
PRODID:bsprodidfortestabc123
|
4
|
+
CALSCALE:GREGORIAN
|
5
|
+
BEGIN:VEVENT
|
6
|
+
DTSTAMP:20050118T211523Z
|
7
|
+
UID:bsuidfortestabc123
|
8
|
+
DTSTART:20050120
|
9
|
+
DTEND;TZID=US-Mountain:20050120T184500
|
10
|
+
CLASS:PRIVATE
|
11
|
+
GEO:37.386013;-122.0829322
|
12
|
+
ORGANIZER: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.
|
16
|
+
ATTACH:http://bush.sucks.org/impeach/him.rhtml
|
17
|
+
ATTACH:http://corporations-dominate.existence.net/why.rhtml
|
18
|
+
RDATE;TZID=US-Mountain:20050121T170000,20050122T170000
|
19
|
+
X-TEST-COMPONENT;QTEST="Hello, World":Shouldn't double double quotes
|
20
|
+
END:VEVENT
|
21
|
+
END:VCALENDAR
|
data/spec/parser_spec.rb
CHANGED
@@ -2,29 +2,40 @@ require 'spec_helper'
|
|
2
2
|
|
3
3
|
describe Icalendar::Parser do
|
4
4
|
subject { described_class.new source, false }
|
5
|
+
let(:source) { File.read File.join(File.dirname(__FILE__), 'fixtures', fn) }
|
5
6
|
|
6
7
|
describe '#parse' do
|
7
|
-
|
8
|
+
context 'single_event.ics' do
|
9
|
+
let(:fn) { 'single_event.ics' }
|
8
10
|
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
11
|
+
it 'returns an array of calendars' do
|
12
|
+
expect(subject.parse).to be_instance_of Array
|
13
|
+
expect(subject.parse.count).to eq 1
|
14
|
+
expect(subject.parse[0]).to be_instance_of Icalendar::Calendar
|
15
|
+
end
|
14
16
|
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
17
|
+
it 'properly splits multi-valued lines' do
|
18
|
+
event = subject.parse.first.events.first
|
19
|
+
expect(event.geo).to eq [37.386013,-122.0829322]
|
20
|
+
end
|
19
21
|
|
20
|
-
|
21
|
-
|
22
|
-
|
22
|
+
it 'saves params' do
|
23
|
+
event = subject.parse.first.events.first
|
24
|
+
expect(event.dtstart.ical_params).to eq('tzid' => ['US-Mountain'])
|
25
|
+
end
|
26
|
+
end
|
27
|
+
context 'recurrence.ics' do
|
28
|
+
let(:fn) { 'recurrence.ics' }
|
29
|
+
it 'correctly parses the exdate array' do
|
30
|
+
event = subject.parse.first.events.first
|
31
|
+
ics = event.to_ical
|
32
|
+
expect(ics).to match 'EXDATE;VALUE=DATE:20120323,20130323'
|
33
|
+
end
|
23
34
|
end
|
24
35
|
end
|
25
36
|
|
26
37
|
describe '#parse with bad line' do
|
27
|
-
let(:
|
38
|
+
let(:fn) { 'single_event_bad_line.ics' }
|
28
39
|
|
29
40
|
it 'returns an array of calendars' do
|
30
41
|
expect(subject.parse).to be_instance_of Array
|
@@ -42,4 +53,13 @@ describe Icalendar::Parser do
|
|
42
53
|
expect(event.dtstart.ical_params).to eq('tzid' => ['US-Mountain'])
|
43
54
|
end
|
44
55
|
end
|
56
|
+
|
57
|
+
describe 'missing date value parameter' do
|
58
|
+
let(:fn) { 'single_event_bad_dtstart.ics' }
|
59
|
+
|
60
|
+
it 'falls back to date type for dtstart' do
|
61
|
+
event = subject.parse.first.events.first
|
62
|
+
expect(event.dtstart).to be_kind_of Icalendar::Values::Date
|
63
|
+
end
|
64
|
+
end
|
45
65
|
end
|
data/spec/roundtrip_spec.rb
CHANGED
@@ -8,6 +8,14 @@ describe Icalendar do
|
|
8
8
|
it 'will generate the same file as is parsed' do
|
9
9
|
expect(Icalendar.parse(source, true).to_ical).to eq source
|
10
10
|
end
|
11
|
+
|
12
|
+
it 'array properties can be assigned to a new event' do
|
13
|
+
event = Icalendar::Event.new
|
14
|
+
parsed = Icalendar.parse source, true
|
15
|
+
event.rdate = parsed.events.first.rdate
|
16
|
+
expect(event.rdate.first).to be_kind_of Icalendar::Values::Array
|
17
|
+
expect(event.rdate.first.ical_params).to eq 'tzid' => ['US-Mountain']
|
18
|
+
end
|
11
19
|
end
|
12
20
|
|
13
21
|
describe 'timezone round trip' do
|
@@ -56,6 +56,15 @@ describe Icalendar::Values::DateTime do
|
|
56
56
|
expect(subject.to_ical described_class).to eq ":#{value}"
|
57
57
|
end
|
58
58
|
|
59
|
+
context 'manually set UTC' do
|
60
|
+
let(:value) { '20140209T194355' }
|
61
|
+
let(:params) { {'TZID' => 'UTC'} }
|
62
|
+
|
63
|
+
it 'does not add a tzid parameter, but does add a Z' do
|
64
|
+
expect(subject.to_ical described_class).to eq ":#{value}Z"
|
65
|
+
end
|
66
|
+
end
|
67
|
+
|
59
68
|
context 'local time' do
|
60
69
|
let(:value) { '20140209T160652' }
|
61
70
|
let(:params) { {'tzid' => 'America/Denver'} }
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: icalendar
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 2.
|
4
|
+
version: 2.3.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: 2015-04-26 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: rake
|
@@ -159,6 +159,7 @@ files:
|
|
159
159
|
- lib/icalendar/alarm.rb
|
160
160
|
- lib/icalendar/calendar.rb
|
161
161
|
- lib/icalendar/component.rb
|
162
|
+
- lib/icalendar/downcased_hash.rb
|
162
163
|
- lib/icalendar/event.rb
|
163
164
|
- lib/icalendar/freebusy.rb
|
164
165
|
- lib/icalendar/has_components.rb
|
@@ -190,10 +191,13 @@ files:
|
|
190
191
|
- lib/icalendar/version.rb
|
191
192
|
- spec/alarm_spec.rb
|
192
193
|
- spec/calendar_spec.rb
|
194
|
+
- spec/downcased_hash_spec.rb
|
193
195
|
- spec/event_spec.rb
|
194
196
|
- spec/fixtures/nondefault_values.ics
|
195
197
|
- spec/fixtures/nonstandard.ics
|
198
|
+
- spec/fixtures/recurrence.ics
|
196
199
|
- spec/fixtures/single_event.ics
|
200
|
+
- spec/fixtures/single_event_bad_dtstart.ics
|
197
201
|
- spec/fixtures/single_event_bad_line.ics
|
198
202
|
- spec/fixtures/timezone.ics
|
199
203
|
- spec/fixtures/two_date_time_events.ics
|
@@ -238,17 +242,20 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
238
242
|
version: '0'
|
239
243
|
requirements: []
|
240
244
|
rubyforge_project:
|
241
|
-
rubygems_version: 2.
|
245
|
+
rubygems_version: 2.4.5
|
242
246
|
signing_key:
|
243
247
|
specification_version: 4
|
244
248
|
summary: A ruby implementation of the iCalendar specification (RFC-5545).
|
245
249
|
test_files:
|
246
250
|
- spec/alarm_spec.rb
|
247
251
|
- spec/calendar_spec.rb
|
252
|
+
- spec/downcased_hash_spec.rb
|
248
253
|
- spec/event_spec.rb
|
249
254
|
- spec/fixtures/nondefault_values.ics
|
250
255
|
- spec/fixtures/nonstandard.ics
|
256
|
+
- spec/fixtures/recurrence.ics
|
251
257
|
- spec/fixtures/single_event.ics
|
258
|
+
- spec/fixtures/single_event_bad_dtstart.ics
|
252
259
|
- spec/fixtures/single_event_bad_line.ics
|
253
260
|
- spec/fixtures/timezone.ics
|
254
261
|
- spec/fixtures/two_date_time_events.ics
|