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,25 @@
1
+ require 'simplecov'
2
+ SimpleCov.start do
3
+ add_filter '/spec/'
4
+ end
5
+
6
+ # This file was generated by the `rspec --init` command. Conventionally, all
7
+ # specs live under a `spec` directory, which RSpec adds to the `$LOAD_PATH`.
8
+ # Require this file using `require "spec_helper"` to ensure that it is only
9
+ # loaded once.
10
+ #
11
+ # See http://rubydoc.info/gems/rspec-core/RSpec/Core/Configuration
12
+ require 'timecop'
13
+ require 'icalendar'
14
+
15
+ RSpec.configure do |config|
16
+ config.treat_symbols_as_metadata_keys_with_true_values = true
17
+ config.run_all_when_everything_filtered = true
18
+ config.filter_run :focus
19
+
20
+ # Run specs in random order to surface order dependencies. If you find an
21
+ # order dependency and want to debug it, you can fix the order by providing
22
+ # the seed, which is printed after each run.
23
+ # --seed 1234
24
+ config.order = 'random'
25
+ end
@@ -0,0 +1,31 @@
1
+ require 'spec_helper'
2
+
3
+ describe Icalendar::Timezone do
4
+
5
+ describe "valid?" do
6
+ subject { described_class.new.tap { |t| t.tzid = 'Eastern' } }
7
+
8
+ context 'with both standard and daylight components' do
9
+ before(:each) do
10
+ subject.daylight { |d| d.should_receive(:valid?).and_return true }
11
+ subject.standard { |s| s.should_receive(:valid?).and_return true }
12
+ end
13
+
14
+ it { should be_valid }
15
+ end
16
+
17
+ context 'with only standard' do
18
+ before(:each) { subject.standard { |s| s.stub(:valid?).and_return true } }
19
+ it { should be_valid }
20
+ end
21
+
22
+ context 'with only daylight' do
23
+ before(:each) { subject.daylight { |d| d.stub(:valid?).and_return true } }
24
+ it { should be_valid }
25
+ end
26
+
27
+ context 'with neither standard or daylight' do
28
+ it { should_not be_valid }
29
+ end
30
+ end
31
+ end
data/spec/todo_spec.rb ADDED
@@ -0,0 +1,24 @@
1
+ require 'spec_helper'
2
+
3
+ describe Icalendar::Todo do
4
+
5
+ describe '#dtstart' do
6
+ it 'is not normally required' do
7
+ subject.dtstart = nil
8
+ subject.should be_valid
9
+ end
10
+
11
+ context 'with duration set' do
12
+ before(:each) { subject.duration = 'PT15M' }
13
+
14
+ it 'is invalid if not set' do
15
+ subject.should_not be_valid
16
+ end
17
+
18
+ it 'is valid when set' do
19
+ subject.dtstart = Date.today
20
+ subject.should be_valid
21
+ end
22
+ end
23
+ end
24
+ end
@@ -0,0 +1,85 @@
1
+ require 'spec_helper'
2
+ require_relative '../lib/icalendar/tzinfo'
3
+
4
+ describe 'TZInfo::Timezone' do
5
+
6
+ let(:tz) { TZInfo::Timezone.get 'Europe/Copenhagen' }
7
+ let(:date) { DateTime.new 1970 }
8
+ subject { tz.ical_timezone date }
9
+
10
+ describe 'daylight offset' do
11
+ specify { expect(subject.daylights.first.tzoffsetto.value_ical).to eq "+0200" }
12
+ specify { expect(subject.daylights.first.tzoffsetfrom.value_ical).to eq "+0100" }
13
+ end
14
+
15
+ describe 'standard offset' do
16
+ specify { expect(subject.standards.first.tzoffsetto.value_ical).to eq "+0100" }
17
+ specify { expect(subject.standards.first.tzoffsetfrom.value_ical).to eq "+0200" }
18
+ end
19
+
20
+ describe 'no end transition' do
21
+ let(:tz) { TZInfo::Timezone.get 'America/Cayman' }
22
+ let(:date) { DateTime.now }
23
+
24
+ it 'only creates a standard component' do
25
+ expect(subject.to_ical).to eq <<-EXPECTED.gsub "\n", "\r\n"
26
+ BEGIN:VTIMEZONE
27
+ TZID:America/Cayman
28
+ BEGIN:STANDARD
29
+ DTSTART:19120201T000711
30
+ TZOFFSETFROM:-0652
31
+ TZOFFSETTO:-0500
32
+ TZNAME:EST
33
+ END:STANDARD
34
+ END:VTIMEZONE
35
+ EXPECTED
36
+ end
37
+ end
38
+
39
+ describe 'no transition' do
40
+ let(:tz) { TZInfo::Timezone.get 'UTC' }
41
+ let(:date) { DateTime.now }
42
+
43
+ it 'creates a standard component with equal offsets' do
44
+ expect(subject.to_ical).to eq <<-EXPECTED.gsub "\n", "\r\n"
45
+ BEGIN:VTIMEZONE
46
+ TZID:UTC
47
+ BEGIN:STANDARD
48
+ DTSTART:19700101T000000
49
+ TZOFFSETFROM:+0000
50
+ TZOFFSETTO:+0000
51
+ TZNAME:UTC
52
+ END:STANDARD
53
+ END:VTIMEZONE
54
+ EXPECTED
55
+ end
56
+ end
57
+
58
+ describe 'dst transition' do
59
+ subject { TZInfo::Timezone.get 'America/Los_Angeles' }
60
+ let(:now) { subject.now }
61
+ # freeze in DST transition in America/Los_Angeles
62
+ before(:each) { Timecop.freeze DateTime.new(2013, 11, 03, 1, 30, 0, '-08:00') }
63
+ after(:each) { Timecop.return }
64
+
65
+ specify { expect { subject.ical_timezone now, nil }.to raise_error TZInfo::AmbiguousTime }
66
+ specify { expect { subject.ical_timezone now, true }.not_to raise_error }
67
+ specify { expect { subject.ical_timezone now, false }.not_to raise_error }
68
+
69
+ context 'TZInfo::Timezone.default_dst = nil' do
70
+ before(:each) { TZInfo::Timezone.default_dst = nil }
71
+ specify { expect { subject.ical_timezone now }.to raise_error TZInfo::AmbiguousTime }
72
+ end
73
+
74
+ context 'TZInfo::Timezone.default_dst = true' do
75
+ before(:each) { TZInfo::Timezone.default_dst = true }
76
+ specify { expect { subject.ical_timezone now }.not_to raise_error }
77
+ end
78
+
79
+ context 'TZInfo::Timezone.default_dst = false' do
80
+ before(:each) { TZInfo::Timezone.default_dst = false }
81
+ specify { expect { subject.ical_timezone now }.not_to raise_error }
82
+ end
83
+ end
84
+
85
+ end
@@ -0,0 +1,80 @@
1
+ require 'spec_helper'
2
+
3
+ describe Icalendar::Values::DateTime do
4
+
5
+ subject { described_class.new value, params }
6
+ let(:value) { '20140209T194355Z' }
7
+ let(:params) { {} }
8
+
9
+ # not sure how to automatically test both sides of this.
10
+ # For now - relying on commenting out dev dependency in gemspec
11
+ # AND uninstalling gem manually
12
+ if defined? ActiveSupport
13
+
14
+ context 'with ActiveSupport' do
15
+ it 'parses a string to a ActiveSupport::TimeWithZone instance' do
16
+ expect(subject.value).to be_a_kind_of ActiveSupport::TimeWithZone
17
+ expect(subject.value_ical).to eq value
18
+ end
19
+
20
+ context 'local time' do
21
+ let(:value) { '20140209T160652' }
22
+ let(:params) { {'tzid' => 'America/Denver'} }
23
+
24
+ it 'parses the value as local time' do
25
+ expect(subject.value.hour).to eq 16
26
+ expect(subject.value.utc_offset).to eq -25200
27
+ expect(subject.value.utc.hour).to eq 23
28
+ end
29
+ end
30
+ end
31
+
32
+ else
33
+
34
+ context 'without ActiveSupport' do
35
+ it 'parses a string to a DateTime instance' do
36
+ expect(subject.value).to be_a_kind_of ::DateTime
37
+ end
38
+
39
+ context 'local time' do
40
+ let(:value) { '20140209T160652' }
41
+ let(:params) { {'tzid' => 'America/Denver'} }
42
+
43
+ it 'parses the value as local time' do
44
+ expect(subject.value.hour).to eq 16
45
+ # TODO better offset support without ActiveSupport
46
+ #expect(subject.offset).to eq Rational(-7, 24)
47
+ end
48
+ end
49
+ end
50
+
51
+ end
52
+
53
+
54
+ context 'common tests' do
55
+ it 'does not add any tzid parameter to output' do
56
+ expect(subject.to_ical described_class).to eq ":#{value}"
57
+ end
58
+
59
+ context 'local time' do
60
+ let(:value) { '20140209T160652' }
61
+ let(:params) { {'tzid' => 'America/Denver'} }
62
+
63
+ it 'keeps value and tzid as localtime on output' do
64
+ expect(subject.to_ical described_class).to eq ";TZID=America/Denver:#{value}"
65
+ end
66
+ end
67
+
68
+ context 'floating local time' do
69
+ let(:value) { '20140209T162845' }
70
+
71
+ it 'keeps the value as a DateTime' do
72
+ expect(subject.value).to be_a_kind_of ::DateTime
73
+ end
74
+
75
+ it 'does not append a Z on output' do
76
+ expect(subject.to_ical described_class).to eq ":#{value}"
77
+ end
78
+ end
79
+ end
80
+ end
@@ -0,0 +1,67 @@
1
+ require 'spec_helper'
2
+
3
+ describe Icalendar::Values::Duration do
4
+
5
+ subject { described_class.new value }
6
+
7
+ describe '#past?' do
8
+ context 'positive explicit' do
9
+ let(:value) { '+P15M' }
10
+ specify { expect(subject.past?).to be_false }
11
+ end
12
+
13
+ context 'positive implicit' do
14
+ let(:value) { 'P15M' }
15
+ specify { expect(subject.past?).to be_false }
16
+ end
17
+
18
+ context 'negative' do
19
+ let(:value) { '-P15M' }
20
+ specify { expect(subject.past?).to be_true }
21
+ end
22
+ end
23
+
24
+ describe '#days' do
25
+ context 'days given' do
26
+ let(:value) { 'P5DT3H' }
27
+ specify { expect(subject.days).to eq 5 }
28
+ end
29
+ context 'no days given' do
30
+ let(:value) { 'P5W' }
31
+ specify { expect(subject.days).to eq 0 }
32
+ end
33
+ end
34
+
35
+ describe '#weeks' do
36
+ let(:value) { 'P3W' }
37
+ specify { expect(subject.weeks).to eq 3 }
38
+ end
39
+
40
+ describe '#hours' do
41
+ let(:value) { 'PT6H' }
42
+ specify { expect(subject.hours).to eq 6 }
43
+ end
44
+
45
+ describe '#minutes' do
46
+ let(:value) { 'P2DT5H45M12S' }
47
+ specify { expect(subject.minutes).to eq 45 }
48
+ end
49
+
50
+ describe '#seconds' do
51
+ let(:value) { '-PT5M30S' }
52
+ specify { expect(subject.seconds).to eq 30 }
53
+ end
54
+
55
+ describe '#value_ical' do
56
+ let(:value) { 'P2DT4H' }
57
+ specify { expect(subject.value_ical).to eq value }
58
+ end
59
+
60
+ describe '#days=' do
61
+ let(:value) { 'P3D' }
62
+ it 'can set the number of days' do
63
+ subject.days = 4
64
+ expect(subject.value_ical).to eq 'P4D'
65
+ end
66
+ end
67
+ end
@@ -0,0 +1,47 @@
1
+ require 'spec_helper'
2
+ require 'ostruct'
3
+
4
+ describe Icalendar::Values::Period do
5
+
6
+ subject { described_class.new value }
7
+
8
+ context 'date-time/date-time' do
9
+ let(:value) { '19830507T000600Z/20140128T201400Z' }
10
+
11
+ describe '#value_ical' do
12
+ specify { expect(subject.value_ical).to eq value }
13
+ end
14
+ describe '#period_start' do
15
+ specify { expect(subject.period_start).to eq DateTime.new(1983, 5, 7, 0, 6) }
16
+ end
17
+ describe '#duration' do
18
+ specify { expect(subject.duration).to be_nil }
19
+ end
20
+ describe '#explicit_end' do
21
+ specify { expect(subject.explicit_end).to eq DateTime.new(2014, 01, 28, 20, 14) }
22
+ end
23
+ end
24
+
25
+ context 'date-time/duration' do
26
+ let(:value) { '19830507T000600Z/P1604W' }
27
+ let(:expected_duration) { OpenStruct.new past: false, weeks: 1604, days: 0, hours: 0, minutes: 0, seconds: 0 }
28
+
29
+ describe '#value_ical' do
30
+ specify { expect(subject.value_ical).to eq value }
31
+
32
+ it 'allows updating duration' do
33
+ subject.duration = 'PT30M'
34
+ expect(subject.value_ical).to eq '19830507T000600Z/PT30M'
35
+ end
36
+ end
37
+ describe '#period_start' do
38
+ specify { expect(subject.period_start).to eq DateTime.new(1983, 5, 7, 0, 6) }
39
+ end
40
+ describe '#duration' do
41
+ specify { expect(subject.duration).to eq expected_duration }
42
+ end
43
+ describe '#explicit_end' do
44
+ specify { expect(subject.explicit_end).to eq nil }
45
+ end
46
+ end
47
+ end
@@ -0,0 +1,47 @@
1
+ require 'spec_helper'
2
+
3
+ describe Icalendar::Values::Recur do
4
+
5
+ subject { described_class.new value }
6
+ let(:value) { 'FREQ=DAILY' }
7
+
8
+ describe 'parsing' do
9
+ context 'multiple bydays' do
10
+ let(:value) { 'FREQ=WEEKLY;COUNT=4;BYDAY=MO,WE,FR' }
11
+
12
+ specify { expect(subject.frequency).to eq 'WEEKLY' }
13
+ specify { expect(subject.count).to eq 4 }
14
+ specify { expect(subject.by_day).to eq %w(MO WE FR) }
15
+ end
16
+
17
+ context 'single byday' do
18
+ let(:value) { 'FREQ=YEARLY;BYDAY=2SU;BYMONTH=3' }
19
+
20
+ specify { expect(subject.frequency).to eq 'YEARLY' }
21
+ specify { expect(subject.by_day).to eq %w(2SU) }
22
+ specify { expect(subject.by_month).to eq [3] }
23
+ end
24
+ end
25
+
26
+ describe '#valid?' do
27
+ it 'requires frequency' do
28
+ expect(subject.valid?).to be_true
29
+ subject.frequency = nil
30
+ expect(subject.valid?).to be_false
31
+ end
32
+
33
+ it 'cannot have both until and count' do
34
+ subject.until = '20140201'
35
+ subject.count = 4
36
+ expect(subject.valid?).to be_false
37
+ end
38
+ end
39
+
40
+ describe '#value_ical' do
41
+ let(:value) { 'FREQ=DAILY;BYYEARDAY=1,34,56,240;BYDAY=SU,SA' }
42
+
43
+ it 'outputs in spec order' do
44
+ expect(subject.value_ical).to eq 'FREQ=DAILY;BYDAY=SU,SA;BYYEARDAY=1,34,56,240'
45
+ end
46
+ end
47
+ end
@@ -0,0 +1,72 @@
1
+ require 'spec_helper'
2
+
3
+ describe Icalendar::Values::Text do
4
+
5
+ subject { described_class.new value }
6
+ let(:unescaped) { "This \\ that, semi; colons\r\nAnother line: \"why not?\"" }
7
+ let(:escaped) { 'This \\\\ that\, semi\; colons\nAnother line: "why not?"' }
8
+
9
+ describe '#value_ical' do
10
+ let(:value) { unescaped }
11
+ it 'escapes \ , ; NL' do
12
+ expect(subject.value_ical).to eq escaped
13
+ end
14
+ end
15
+
16
+ describe 'unescapes in initializer' do
17
+ context 'given escaped version' do
18
+ let(:unescaped_no_cr) { unescaped.gsub "\r", '' }
19
+ let(:value) { escaped }
20
+ it 'removes escaping' do
21
+ expect(subject.value).to eq unescaped_no_cr
22
+ end
23
+ end
24
+
25
+ context 'given unescaped version' do
26
+ let(:value) { unescaped }
27
+ it 'does not try to double unescape' do
28
+ expect(subject.value).to eq unescaped
29
+ end
30
+ end
31
+ end
32
+
33
+ describe 'escapes parameter text properly' do
34
+ subject { described_class.new escaped, {'param' => param_value} }
35
+ context 'single value, no special characters' do
36
+ let(:param_value) { 'HelloWorld' }
37
+ it 'does not wrap param in double quotes' do
38
+ expect(subject.params_ical).to eq %(;PARAM=HelloWorld)
39
+ end
40
+ end
41
+ context 'single value, special characters' do
42
+ let(:param_value) { 'Hello:World' }
43
+ it 'wraps param value in double quotes' do
44
+ expect(subject.params_ical).to eq %(;PARAM="Hello:World")
45
+ end
46
+ end
47
+ context 'single value, double quotes' do
48
+ let(:param_value) { 'Hello "World"' }
49
+ it 'replaces double quotes with single' do
50
+ expect(subject.params_ical).to eq %(;PARAM=Hello 'World')
51
+ end
52
+ end
53
+ context 'multiple values, no special characters' do
54
+ let(:param_value) { ['HelloWorld', 'GoodbyeMoon'] }
55
+ it 'joins with comma' do
56
+ expect(subject.params_ical).to eq %(;PARAM=HelloWorld,GoodbyeMoon)
57
+ end
58
+ end
59
+ context 'multiple values, with special characters' do
60
+ let(:param_value) { ['Hello, World', 'GoodbyeMoon'] }
61
+ it 'quotes values with special characters, joins with comma' do
62
+ expect(subject.params_ical).to eq %(;PARAM="Hello, World",GoodbyeMoon)
63
+ end
64
+ end
65
+ context 'multiple values, double quotes' do
66
+ let(:param_value) { ['Hello, "World"', 'GoodbyeMoon'] }
67
+ it 'replaces double quotes with single' do
68
+ expect(subject.params_ical).to eq %(;PARAM="Hello, 'World'",GoodbyeMoon)
69
+ end
70
+ end
71
+ end
72
+ end