icalendar 1.5.4 → 2.0.0.beta.1

Sign up to get free protection for your applications and to get access to all the features.
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