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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 144e7b7601ab22b7de6b32db6da271ca7f2e487d
4
- data.tar.gz: 36fe6e7a0bc064ff6d989dbbc40b56a87c42508d
3
+ metadata.gz: 3ed623b569af5c7f68aa5917f19acedb5a11f7e8
4
+ data.tar.gz: 3293337f990892e2c07cc9c8d23f5bf4b86e5a37
5
5
  SHA512:
6
- metadata.gz: 053e06312ce27ff2b6e964396171c15b3808a4954f9dc42330d6febde5d0e5339144fe5521088f446db652fa6f12a61718f7585b1025f31f26ca21cd95f1cbef
7
- data.tar.gz: 4a22eaa229789f42b8584a1a8bc707846ee4f24b0ee34851583fc8c0f2c2fdcbf4aacf34c4fdab9dc82183a64ca8cd51ed9b899837bf26a2cae5e8f54b6a5fd4
6
+ metadata.gz: 4a95ab23132eeffa95b42b1c2da033aa46b92775caf43928458ffd048685f66095855235b1b951f1bb18a1a33b7fb817cc1e4fc3f1b4917a277a3f18306dc592
7
+ data.tar.gz: 8d77c499752d3f589b9dca961d61df24f9b67a89f4764c462e71bcedd313a0f8d3b1aace26849bbe183a415235e4c6b159b8ea4b194b37cf9b202a73f001c568
@@ -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
@@ -62,7 +62,7 @@ module Icalendar
62
62
  klass ||= Icalendar.const_get singular_name.capitalize
63
63
  klass.new
64
64
  rescue NameError => ne
65
- puts "WARN: #{ne.message}"
65
+ Icalendar.logger.warn ne.message
66
66
  Component.new singular_name
67
67
  end
68
68
  end
@@ -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
- klass = component.class.default_property_types[fields[:name]]
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
@@ -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.dup
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.to_s] = value
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.class.value_type if needs_value_type?(default_type)
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 { |v| klass.new v, params }
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 @value_delimiter
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 ArgumentError.new("Failed to parse \"#{value}\" - #{e.message}")
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
 
@@ -1,5 +1,5 @@
1
1
  module Icalendar
2
2
 
3
- VERSION = '2.2.2'
3
+ VERSION = '2.3.0'
4
4
 
5
5
  end
@@ -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
@@ -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
- let(:source) { File.read File.join(File.dirname(__FILE__), 'fixtures', 'single_event.ics') }
8
+ context 'single_event.ics' do
9
+ let(:fn) { 'single_event.ics' }
8
10
 
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
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
- 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
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
- it 'saves params' do
21
- event = subject.parse.first.events.first
22
- expect(event.dtstart.ical_params).to eq('tzid' => ['US-Mountain'])
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(:source) { File.read File.join(File.dirname(__FILE__), 'fixtures', 'single_event_bad_line.ics') }
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
@@ -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.2.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: 2014-12-27 00:00:00.000000000 Z
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.2.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