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.
Files changed (100) hide show
  1. checksums.yaml +7 -0
  2. data/.gitignore +1 -1
  3. data/.rspec +2 -0
  4. data/.travis.yml +1 -2
  5. data/History.txt +2 -7
  6. data/README.md +82 -107
  7. data/Rakefile +6 -7
  8. data/icalendar.gemspec +10 -9
  9. data/lib/icalendar.rb +17 -33
  10. data/lib/icalendar/alarm.rb +35 -0
  11. data/lib/icalendar/calendar.rb +17 -100
  12. data/lib/icalendar/component.rb +41 -403
  13. data/lib/icalendar/event.rb +51 -0
  14. data/lib/icalendar/freebusy.rb +27 -0
  15. data/lib/icalendar/has_components.rb +83 -0
  16. data/lib/icalendar/has_properties.rb +156 -0
  17. data/lib/icalendar/journal.rb +39 -0
  18. data/lib/icalendar/parser.rb +75 -403
  19. data/lib/icalendar/timezone.rb +53 -0
  20. data/lib/icalendar/todo.rb +52 -0
  21. data/lib/icalendar/tzinfo.rb +30 -30
  22. data/lib/icalendar/value.rb +80 -0
  23. data/lib/icalendar/values/array.rb +43 -0
  24. data/lib/icalendar/values/binary.rb +31 -0
  25. data/lib/icalendar/values/boolean.rb +17 -0
  26. data/lib/icalendar/values/cal_address.rb +8 -0
  27. data/lib/icalendar/values/date.rb +26 -0
  28. data/lib/icalendar/values/date_time.rb +34 -0
  29. data/lib/icalendar/values/duration.rb +48 -0
  30. data/lib/icalendar/values/float.rb +17 -0
  31. data/lib/icalendar/values/integer.rb +17 -0
  32. data/lib/icalendar/values/period.rb +46 -0
  33. data/lib/icalendar/values/recur.rb +63 -0
  34. data/lib/icalendar/values/text.rb +26 -0
  35. data/lib/icalendar/values/time.rb +34 -0
  36. data/lib/icalendar/values/time_with_zone.rb +31 -0
  37. data/lib/icalendar/values/uri.rb +19 -0
  38. data/lib/icalendar/values/utc_offset.rb +39 -0
  39. data/lib/icalendar/version.rb +5 -0
  40. data/spec/alarm_spec.rb +108 -0
  41. data/spec/calendar_spec.rb +167 -0
  42. data/spec/event_spec.rb +108 -0
  43. data/{test/fixtures/folding.ics → spec/fixtures/nondefault_values.ics} +2 -2
  44. data/{test → spec}/fixtures/single_event.ics +11 -14
  45. data/spec/fixtures/timezone.ics +35 -0
  46. data/spec/freebusy_spec.rb +7 -0
  47. data/spec/journal_spec.rb +7 -0
  48. data/spec/parser_spec.rb +26 -0
  49. data/spec/roundtrip_spec.rb +40 -0
  50. data/spec/spec_helper.rb +25 -0
  51. data/spec/timezone_spec.rb +31 -0
  52. data/spec/todo_spec.rb +24 -0
  53. data/spec/tzinfo_spec.rb +85 -0
  54. data/spec/values/date_time_spec.rb +80 -0
  55. data/spec/values/duration_spec.rb +67 -0
  56. data/spec/values/period_spec.rb +47 -0
  57. data/spec/values/recur_spec.rb +47 -0
  58. data/spec/values/text_spec.rb +72 -0
  59. data/spec/values/utc_offset_spec.rb +41 -0
  60. metadata +129 -88
  61. data/GPL +0 -340
  62. data/examples/create_cal.rb +0 -45
  63. data/examples/parse_cal.rb +0 -20
  64. data/examples/single_event.ics +0 -18
  65. data/lib/hash_attrs.rb +0 -34
  66. data/lib/icalendar/base.rb +0 -47
  67. data/lib/icalendar/component/alarm.rb +0 -47
  68. data/lib/icalendar/component/event.rb +0 -131
  69. data/lib/icalendar/component/freebusy.rb +0 -38
  70. data/lib/icalendar/component/journal.rb +0 -60
  71. data/lib/icalendar/component/timezone.rb +0 -91
  72. data/lib/icalendar/component/todo.rb +0 -64
  73. data/lib/icalendar/conversions.rb +0 -107
  74. data/lib/icalendar/helpers.rb +0 -109
  75. data/lib/icalendar/parameter.rb +0 -33
  76. data/lib/icalendar/rrule.rb +0 -133
  77. data/lib/meta.rb +0 -32
  78. data/script/console +0 -10
  79. data/script/recur1.ics +0 -38
  80. data/script/tryit.rb +0 -13
  81. data/test/component/test_event.rb +0 -253
  82. data/test/component/test_timezone.rb +0 -74
  83. data/test/component/test_todo.rb +0 -31
  84. data/test/fixtures/life.ics +0 -46
  85. data/test/fixtures/nonstandard.ics +0 -25
  86. data/test/fixtures/simplecal.ics +0 -119
  87. data/test/interactive.rb +0 -17
  88. data/test/read_write.rb +0 -23
  89. data/test/test_calendar.rb +0 -167
  90. data/test/test_component.rb +0 -102
  91. data/test/test_conversions.rb +0 -104
  92. data/test/test_helper.rb +0 -7
  93. data/test/test_parameter.rb +0 -91
  94. data/test/test_parser.rb +0 -100
  95. data/test/test_tzinfo.rb +0 -83
  96. data/website/index.html +0 -70
  97. data/website/index.txt +0 -38
  98. data/website/javascripts/rounded_corners_lite.inc.js +0 -285
  99. data/website/stylesheets/screen.css +0 -159
  100. data/website/template.html.erb +0 -50
@@ -0,0 +1,167 @@
1
+ require 'spec_helper'
2
+
3
+ describe Icalendar::Calendar do
4
+
5
+ context 'values' do
6
+ let(:property) { 'my-value' }
7
+
8
+ %w(prodid version calscale ip_method).each do |prop|
9
+ it "##{prop} sets and gets" do
10
+ subject.send("#{prop}=", property)
11
+ subject.send(prop).should == property
12
+ end
13
+ end
14
+
15
+ it "sets and gets custom properties" do
16
+ subject.x_custom_prop = property
17
+ expect(subject.x_custom_prop).to eq [property]
18
+ end
19
+
20
+ it 'can set params on a property' do
21
+ subject.prodid.ical_params = {'hello' => 'world'}
22
+ subject.prodid.value.should == 'icalendar-ruby'
23
+ subject.prodid.ical_params.should == {'hello' => 'world'}
24
+ end
25
+
26
+ context "required values" do
27
+ it 'is not valid when prodid is not set' do
28
+ subject.prodid = nil
29
+ subject.should_not be_valid
30
+ end
31
+
32
+ it 'is not valid when version is not set' do
33
+ subject.version = nil
34
+ subject.should_not be_valid
35
+ end
36
+
37
+ it 'is valid when both prodid and version are set' do
38
+ subject.version = '2.0'
39
+ subject.prodid = 'my-product'
40
+ subject.should be_valid
41
+ end
42
+
43
+ it 'is valid by default' do
44
+ subject.should be_valid
45
+ end
46
+ end
47
+ end
48
+
49
+ context 'components' do
50
+ let(:ical_component) { double 'Component', name: 'event', 'parent=' => nil }
51
+
52
+ %w(event todo journal freebusy timezone).each do |component|
53
+ it "##{component} adds a new component" do
54
+ subject.send("#{component}").should be_a_kind_of Icalendar::Component
55
+ end
56
+
57
+ it "##{component} passes a component to a block to build parts" do
58
+ expect { |b| subject.send("#{component}", &b) }.to yield_with_args Icalendar::Component
59
+ end
60
+
61
+ it "##{component} can be passed in" do
62
+ expect { |b| subject.send("#{component}", ical_component, &b) }.to yield_with_args ical_component
63
+ subject.send("#{component}", ical_component).should == ical_component
64
+ end
65
+ end
66
+
67
+ it "adds event to events list" do
68
+ subject.event ical_component
69
+ subject.events.should == [ical_component]
70
+ end
71
+
72
+ describe '#add_event' do
73
+ it 'delegates to non add_ version' do
74
+ subject.should_receive(:event).with ical_component
75
+ subject.add_event ical_component
76
+ end
77
+ end
78
+
79
+ describe '#find_event' do
80
+ let(:ical_component) { double 'Component', uid: 'uid' }
81
+ let(:other_component) { double 'Component', uid: 'other' }
82
+ before(:each) do
83
+ subject.events << other_component
84
+ subject.events << ical_component
85
+ end
86
+
87
+ it 'finds by uid' do
88
+ subject.find_event('uid').should == ical_component
89
+ end
90
+ end
91
+
92
+ describe '#find_timezone' do
93
+ let(:ical_timezone) { double 'Timezone', tzid: 'Eastern' }
94
+ let(:other_timezone) { double 'Timezone', tzid: 'Pacific' }
95
+ before(:each) do
96
+ subject.timezones << other_timezone
97
+ subject.timezones << ical_timezone
98
+ end
99
+
100
+ it 'finds by tzid' do
101
+ subject.find_timezone('Eastern').should == ical_timezone
102
+ end
103
+ end
104
+
105
+ it "adds reference to parent" do
106
+ e = subject.event
107
+ e.parent.should == subject
108
+ end
109
+
110
+ it 'can be added with add_x_ for custom components' do
111
+ subject.add_x_custom_component.should be_a_kind_of Icalendar::Component
112
+ expect { |b| subject.add_x_custom_component(&b) }.to yield_with_args Icalendar::Component
113
+ subject.add_x_custom_component(ical_component).should == ical_component
114
+ end
115
+ end
116
+
117
+ describe '#to_ical' do
118
+ before(:each) do
119
+ Timecop.freeze DateTime.new(2013, 12, 26, 5, 0, 0, '+0000')
120
+ subject.event do |e|
121
+ e.summary = 'An event'
122
+ e.dtstart = "20140101T000000Z"
123
+ e.dtend = "20140101T050000Z"
124
+ e.geo = [-1.2, -2.1]
125
+ end
126
+ subject.freebusy do |f|
127
+ f.dtstart = "20140102T080000Z"
128
+ f.dtend = "20140102T100000Z"
129
+ f.comment = 'Busy'
130
+ end
131
+ end
132
+ after(:each) do
133
+ Timecop.return
134
+ end
135
+
136
+ it 'outputs properties and components' do
137
+ expected_no_uid = <<-EOICAL.gsub("\n", "\r\n")
138
+ BEGIN:VCALENDAR
139
+ VERSION:2.0
140
+ PRODID:icalendar-ruby
141
+ CALSCALE:GREGORIAN
142
+ BEGIN:VEVENT
143
+ DTSTAMP:20131226T050000Z
144
+ DTSTART:20140101T000000Z
145
+ DTEND:20140101T050000Z
146
+ GEO:-1.2;-2.1
147
+ SUMMARY:An event
148
+ END:VEVENT
149
+ BEGIN:VFREEBUSY
150
+ DTSTAMP:20131226T050000Z
151
+ DTSTART:20140102T080000Z
152
+ DTEND:20140102T100000Z
153
+ COMMENT:Busy
154
+ END:VFREEBUSY
155
+ END:VCALENDAR
156
+ EOICAL
157
+ expect(subject.to_ical.gsub(/^UID:.*\r\n(?: .*\r\n)*/, '')).to eq expected_no_uid
158
+ end
159
+ end
160
+
161
+ describe '#publish' do
162
+ it 'sets ip_method to "PUBLISH"' do
163
+ subject.publish
164
+ expect(subject.ip_method).to eq 'PUBLISH'
165
+ end
166
+ end
167
+ end
@@ -0,0 +1,108 @@
1
+ require 'spec_helper'
2
+
3
+ describe Icalendar::Event do
4
+
5
+ describe '#dtstart' do
6
+ context 'no parent' do
7
+ it 'is invalid if not set' do
8
+ subject.should_not be_valid
9
+ end
10
+
11
+ it 'is valid if set' do
12
+ subject.dtstart = DateTime.now
13
+ subject.should be_valid
14
+ end
15
+ end
16
+
17
+ context 'with parent' do
18
+ before(:each) { subject.parent = Icalendar::Calendar.new }
19
+
20
+ it 'is invalid without method set' do
21
+ subject.should_not be_valid
22
+ end
23
+
24
+ it 'is valid with parent method set' do
25
+ subject.parent.ip_method = 'UPDATE'
26
+ subject.should be_valid
27
+ end
28
+ end
29
+ end
30
+
31
+ context 'mutually exclusive values' do
32
+ before(:each) { subject.dtstart = DateTime.now }
33
+
34
+ it 'is invalid if both dtend and duration are set' do
35
+ subject.dtend = Date.today + 1;
36
+ subject.duration = 'PT15M'
37
+ subject.should_not be_valid
38
+ end
39
+
40
+ it 'is valid if dtend is set' do
41
+ subject.dtend = Date.today + 1;
42
+ subject.should be_valid
43
+ end
44
+
45
+ it 'is valid if duration is set' do
46
+ subject.duration = 'PT15M'
47
+ subject.should be_valid
48
+ end
49
+ end
50
+
51
+ context 'suggested single values' do
52
+ before(:each) do
53
+ subject.dtstart = DateTime.now
54
+ subject.append_rrule double('RRule')
55
+ subject.append_rrule double('RRule')
56
+ end
57
+
58
+ it 'is valid by default' do
59
+ subject.should be_valid
60
+ end
61
+
62
+ it 'is invalid with strict checking' do
63
+ expect(subject.valid?(true)).to be_false
64
+ end
65
+ end
66
+
67
+ context 'multi values' do
68
+ describe '#comment' do
69
+ it 'will return an array when set singly' do
70
+ subject.comment = 'a comment'
71
+ subject.comment.should == ['a comment']
72
+ end
73
+
74
+ it 'can be appended' do
75
+ subject.comment << 'a comment'
76
+ subject.comment << 'b comment'
77
+ subject.comment.should == ['a comment', 'b comment']
78
+ end
79
+
80
+ it 'can be added' do
81
+ subject.append_comment 'a comment'
82
+ subject.comment.should == ['a comment']
83
+ end
84
+ end
85
+ end
86
+
87
+ describe '#find_alarm' do
88
+ it 'should not respond_to find_alarm' do
89
+ expect(subject.respond_to?(:find_alarm)).to be_false
90
+ end
91
+ end
92
+
93
+ describe '#to_ical' do
94
+ before(:each) do
95
+ subject.dtstart = "20131227T013000Z"
96
+ subject.dtend = "20131227T033000Z"
97
+ subject.summary = 'My event, my ical, my test'
98
+ subject.geo = [41.230896,-74.411774]
99
+ subject.x_custom_property = 'customize'
100
+ end
101
+
102
+ it { expect(subject.to_ical).to include 'DTSTART:20131227T013000Z' }
103
+ it { expect(subject.to_ical).to include 'DTEND:20131227T033000Z' }
104
+ it { expect(subject.to_ical).to include 'SUMMARY:My event\, my ical\, my test' }
105
+ it { expect(subject.to_ical).to include 'X-CUSTOM-PROPERTY:customize' }
106
+ it { expect(subject.to_ical).to include 'GEO:41.230896;-74.411774' }
107
+ end
108
+ end
@@ -12,9 +12,9 @@ DESCRIPTION:FULL DETAILS:\nhttp://test.com/events/570\n\nCary Brothers walk
12
12
  lyrical beauty rare in a young songwriter\,&quot\; and &quot\;this soulful
13
13
  blend of everything that feels good.&quot\; Rising up around him is music\;
14
14
  transcending genres\, genders and generations.
15
- DTEND:20061215T180000
15
+ DTEND;VALUE=DATE:20061215
16
16
  DTSTAMP:20061215T114034
17
- DTSTART:20061215T160000
17
+ DTSTART;VALUE=DATE:20061215
18
18
  SEQUENCE:1001
19
19
  SUMMARY:DigiWorld 2006
20
20
  UID:foobar
@@ -1,24 +1,21 @@
1
1
  BEGIN:VCALENDAR
2
2
  VERSION:2.0
3
3
  PRODID:bsprodidfortestabc123
4
+ CALSCALE:GREGORIAN
4
5
  BEGIN:VEVENT
6
+ DTSTAMP:20050118T211523Z
5
7
  UID:bsuidfortestabc123
8
+ DTSTART;TZID=US-Mountain:20050120T170000
9
+ DTEND;TZID=US-Mountain:20050120T184500
10
+ CLASS:PRIVATE
11
+ GEO:37.386013;-122.0829322
6
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.
7
16
  ATTACH:http://bush.sucks.org/impeach/him.rhtml
8
17
  ATTACH:http://corporations-dominate.existence.net/why.rhtml
9
- SUMMARY:This is a really long summary
10
- to test the method of unfolding lines\,
11
- so I'm just going to ma
12
- ke it
13
- a whol
14
- e
15
- bunch of lines.
16
- CLASS:PRIVATE
17
- PRIORITY:2
18
- GEO:37.386013;-122.0829322
19
- DTSTART;TZID=US-Mountain:20050120T170000
20
- DTEND:20050120T184500
21
- DTSTAMP:20050118T211523Z
22
- NAME:SomeName
18
+ RDATE;TZID=US-Mountain:20050121T170000,20050122T170000
19
+ X-TEST-COMPONENT;QTEST="Hello, World":Shouldn't double double quotes
23
20
  END:VEVENT
24
21
  END:VCALENDAR
@@ -0,0 +1,35 @@
1
+ BEGIN:VCALENDAR
2
+ VERSION:2.0
3
+ PRODID:icalendar-ruby
4
+ CALSCALE:GREGORIAN
5
+ BEGIN:VTIMEZONE
6
+ TZID:America/New_York
7
+ BEGIN:DAYLIGHT
8
+ DTSTART:20070311T020000
9
+ TZOFFSETFROM:-0500
10
+ TZOFFSETTO:-0400
11
+ RRULE:FREQ=YEARLY;BYDAY=2SU;BYMONTH=3
12
+ TZNAME:EDT
13
+ END:DAYLIGHT
14
+ BEGIN:STANDARD
15
+ DTSTART:20071104T020000
16
+ TZOFFSETFROM:-0400
17
+ TZOFFSETTO:-0500
18
+ RRULE:FREQ=YEARLY;BYDAY=1SU;BYMONTH=11
19
+ TZNAME:EST
20
+ END:STANDARD
21
+ END:VTIMEZONE
22
+ BEGIN:VEVENT
23
+ DTSTAMP:20120320T014112Z
24
+ UID:9B9CB907-F25F-4D27-B6F3-6DE3A3397BC2
25
+ DTSTART;TZID=America/New_York:20120323T190000
26
+ DTEND;TZID=America/New_York:20120323T230000
27
+ CREATED:20120312T191609Z
28
+ LAST-MODIFIED:20120320T014105Z
29
+ LOCATION:Baltimore
30
+ SEQUENCE:0
31
+ STATUS:CONFIRMED
32
+ SUMMARY:Eastern Event
33
+ TRANSP:OPAQUE
34
+ END:VEVENT
35
+ END:VCALENDAR
@@ -0,0 +1,7 @@
1
+ require 'spec_helper'
2
+
3
+ describe Icalendar::Freebusy do
4
+
5
+ # currently no behavior in Journal not tested other places
6
+
7
+ end
@@ -0,0 +1,7 @@
1
+ require 'spec_helper'
2
+
3
+ describe Icalendar::Journal do
4
+
5
+ # currently no behavior in Journal not tested other places
6
+
7
+ end
@@ -0,0 +1,26 @@
1
+ require 'spec_helper'
2
+
3
+ describe Icalendar::Parser do
4
+
5
+ let(:source) { File.read File.join(File.dirname(__FILE__), 'fixtures', 'single_event.ics') }
6
+ subject { described_class.new source, false }
7
+
8
+ describe '#parse' do
9
+ it 'returns an array of calendars' do
10
+ expect(subject.parse).to be_instance_of Array
11
+ expect(subject.parse.count).to eq 1
12
+ expect(subject.parse[0]).to be_instance_of Icalendar::Calendar
13
+ end
14
+
15
+ it 'properly splits multi-valued lines' do
16
+ event = subject.parse.first.events.first
17
+ expect(event.geo).to eq [37.386013,-122.0829322]
18
+ end
19
+
20
+ it 'saves params' do
21
+ event = subject.parse.first.events.first
22
+ expect(event.dtstart.ical_params).to eq('tzid' => ['US-Mountain'])
23
+ end
24
+ end
25
+
26
+ end
@@ -0,0 +1,40 @@
1
+ require 'spec_helper'
2
+
3
+ describe Icalendar do
4
+
5
+ describe 'single event round trip' do
6
+ let(:source) { File.read File.join(File.dirname(__FILE__), 'fixtures', 'single_event.ics') }
7
+
8
+ it 'will generate the same file as is parsed' do
9
+ Icalendar.parse(source, true).to_ical.should == source
10
+ end
11
+ end
12
+
13
+ describe 'timezone round trip' do
14
+ let(:source) { File.read File.join(File.dirname(__FILE__), 'fixtures', 'timezone.ics') }
15
+ it 'will generate the same file as it parsed' do
16
+ Icalendar.parse(source, true).to_ical.should == source
17
+ end
18
+ end
19
+
20
+ describe 'non-default values' do
21
+ let(:source) { File.read File.join(File.dirname(__FILE__), 'fixtures', 'nondefault_values.ics') }
22
+ subject { Icalendar.parse(source, true).events.first }
23
+
24
+ it 'will set dtstart to Date' do
25
+ expect(subject.dtstart.value).to eq ::Date.new(2006, 12, 15)
26
+ end
27
+
28
+ it 'will set dtend to Date' do
29
+ expect(subject.dtend.value).to eq ::Date.new(2006, 12, 15)
30
+ end
31
+
32
+ it 'will output value param on dtstart' do
33
+ expect(subject.dtstart.to_ical(subject.class.default_property_types['dtstart'])).to match /^;VALUE=DATE:20061215$/
34
+ end
35
+
36
+ it 'will output value param on dtend' do
37
+ expect(subject.dtend.to_ical(subject.class.default_property_types['dtend'])).to match /^;VALUE=DATE:20061215$/
38
+ end
39
+ end
40
+ end