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,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