icalendar 2.9.0 → 2.10.0
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.
- checksums.yaml +4 -4
- data/.github/workflows/main.yml +3 -4
- data/History.txt +5 -0
- data/icalendar.gemspec +4 -0
- data/lib/icalendar/has_properties.rb +2 -2
- data/lib/icalendar/parser.rb +1 -1
- data/lib/icalendar/value.rb +1 -1
- data/lib/icalendar/values/date_time.rb +2 -2
- data/lib/icalendar/values/helpers/active_support_time_with_zone_adapter.rb +16 -0
- data/lib/icalendar/values/helpers/array.rb +62 -0
- data/lib/icalendar/values/helpers/time_with_zone.rb +61 -0
- data/lib/icalendar/values/time.rb +2 -2
- data/lib/icalendar/version.rb +1 -1
- data/spec/roundtrip_spec.rb +1 -1
- data/spec/values/date_time_spec.rb +16 -0
- metadata +8 -7
- data/lib/icalendar/values/active_support_time_with_zone_adapter.rb +0 -14
- data/lib/icalendar/values/array.rb +0 -60
- data/lib/icalendar/values/time_with_zone.rb +0 -55
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 4a4c654b36ffe5a09f521ad3c012686f12e582ba5d380242c646334f47153360
|
4
|
+
data.tar.gz: 184399a11e6e8a39b18ceeef59de8a9b9f7cd581f0c7169cce5c7dca63575e3b
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 61837b5d19193d80d72f37edff89290fc2e5ffa0c6b9ee500036de58b0fbd9aacb5e0e9bc9f1a0a4e1ce7a3ad494ed0147e87bf697b54523d8b799e9eaa275fa
|
7
|
+
data.tar.gz: 00d54cc0ab4b7fc6acf616a537b426d110ddbe65dda1d6ab8ec938ba2df64e87def99023916c6193ab25d1885a2a60e8a1706617ed5a6e0adb5cb34671dae1ec
|
data/.github/workflows/main.yml
CHANGED
@@ -17,13 +17,12 @@ jobs:
|
|
17
17
|
strategy:
|
18
18
|
matrix:
|
19
19
|
ruby:
|
20
|
-
-
|
21
|
-
- 3.
|
22
|
-
- 3.1.2
|
20
|
+
- 3.0.6
|
21
|
+
- 3.1.4
|
23
22
|
- 3.2.2
|
24
23
|
|
25
24
|
steps:
|
26
|
-
- uses: actions/checkout@
|
25
|
+
- uses: actions/checkout@v4
|
27
26
|
- name: Set up Ruby
|
28
27
|
uses: ruby/setup-ruby@v1
|
29
28
|
with:
|
data/History.txt
CHANGED
@@ -1,5 +1,10 @@
|
|
1
1
|
=== Unreleased
|
2
2
|
|
3
|
+
=== 2.10.0 2023-11-01
|
4
|
+
* Add changelog metadata to gemspec - Juri Hahn
|
5
|
+
* Attempt to rescue timezone info when given a nonstandard tzid with no VTIMEZONE
|
6
|
+
* Move Values classes that shouldn't be directly used into Helpers module
|
7
|
+
|
3
8
|
=== 2.9.0 2023-08-11
|
4
9
|
* Always include the VALUE of Event URLs for improved compatibility - Sean Kelley
|
5
10
|
* Improved parse performance - Thomas Cannon
|
data/icalendar.gemspec
CHANGED
@@ -20,6 +20,10 @@ variety of calendaring applications.
|
|
20
20
|
ActiveSupport is required for TimeWithZone support, but not required for general use.
|
21
21
|
EOM
|
22
22
|
|
23
|
+
s.metadata = {
|
24
|
+
'changelog_uri' => 'https://github.com/icalendar/icalendar/blob/main/History.txt'
|
25
|
+
}
|
26
|
+
|
23
27
|
s.files = `git ls-files`.split "\n"
|
24
28
|
s.test_files = `git ls-files -- {test,spec,features}/*`.split "\n"
|
25
29
|
s.executables = `git ls-files -- bin/*`.split("\n").map { |f| File.basename f }
|
@@ -148,7 +148,7 @@ module Icalendar
|
|
148
148
|
|
149
149
|
define_method "#{prop}=" do |value|
|
150
150
|
mapped = map_property_value value, klass, true, new_property
|
151
|
-
if mapped.is_a? Icalendar::Values::Array
|
151
|
+
if mapped.is_a? Icalendar::Values::Helpers::Array
|
152
152
|
instance_variable_set property_var, mapped.to_a.compact
|
153
153
|
else
|
154
154
|
instance_variable_set property_var, [mapped].compact
|
@@ -179,7 +179,7 @@ module Icalendar
|
|
179
179
|
if value.nil? || value.is_a?(Icalendar::Value)
|
180
180
|
value
|
181
181
|
elsif value.is_a? ::Array
|
182
|
-
Icalendar::Values::Array.new value, klass, params, {delimiter: (multi_valued ? ',' : ';')}
|
182
|
+
Icalendar::Values::Helpers::Array.new value, klass, params, {delimiter: (multi_valued ? ',' : ';')}
|
183
183
|
else
|
184
184
|
klass.new value, params
|
185
185
|
end
|
data/lib/icalendar/parser.rb
CHANGED
@@ -84,7 +84,7 @@ module Icalendar
|
|
84
84
|
klass = get_wrapper_class component, fields
|
85
85
|
if wrap_in_array? klass, fields[:value], multi_property
|
86
86
|
delimiter = fields[:value].match(WRAP_PROPERTY_VALUE_DELIMETER_REGEX)[1]
|
87
|
-
Icalendar::Values::Array.new fields[:value].split(WRAP_PROPERTY_VALUE_SPLIT_REGEX),
|
87
|
+
Icalendar::Values::Helpers::Array.new fields[:value].split(WRAP_PROPERTY_VALUE_SPLIT_REGEX),
|
88
88
|
klass,
|
89
89
|
fields[:params],
|
90
90
|
delimiter: delimiter
|
data/lib/icalendar/value.rb
CHANGED
@@ -1,13 +1,13 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
3
|
require 'date'
|
4
|
-
require_relative 'time_with_zone'
|
4
|
+
require_relative 'helpers/time_with_zone'
|
5
5
|
|
6
6
|
module Icalendar
|
7
7
|
module Values
|
8
8
|
|
9
9
|
class DateTime < Value
|
10
|
-
include TimeWithZone
|
10
|
+
include Helpers::TimeWithZone
|
11
11
|
|
12
12
|
FORMAT = '%Y%m%dT%H%M%S'
|
13
13
|
|
@@ -0,0 +1,16 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Icalendar
|
4
|
+
module Values
|
5
|
+
module Helpers
|
6
|
+
class ActiveSupportTimeWithZoneAdapter < ActiveSupport::TimeWithZone
|
7
|
+
# ActiveSupport::TimeWithZone implements a #to_a method that will cause
|
8
|
+
# unexpected behavior in components with multi_property DateTime
|
9
|
+
# properties when the setters for those properties are invoked with an
|
10
|
+
# Icalendar::Values::DateTime that is delegating for an
|
11
|
+
# ActiveSupport::TimeWithZone. To avoid this behavior, undefine #to_a.
|
12
|
+
undef_method :to_a
|
13
|
+
end
|
14
|
+
end
|
15
|
+
end
|
16
|
+
end
|
@@ -0,0 +1,62 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Icalendar
|
4
|
+
module Values
|
5
|
+
module Helpers
|
6
|
+
|
7
|
+
class Array < Value
|
8
|
+
|
9
|
+
attr_reader :value_delimiter
|
10
|
+
|
11
|
+
def initialize(value, klass, params = {}, options = {})
|
12
|
+
@value_delimiter = options[:delimiter] || ','
|
13
|
+
mapped = if value.is_a? ::Array
|
14
|
+
value.map do |v|
|
15
|
+
if v.is_a? Icalendar::Values::Helpers::Array
|
16
|
+
Icalendar::Values::Helpers::Array.new v.value, klass, v.ical_params, delimiter: v.value_delimiter
|
17
|
+
elsif v.is_a? ::Array
|
18
|
+
Icalendar::Values::Helpers::Array.new v, klass, params, delimiter: value_delimiter
|
19
|
+
elsif v.is_a? Icalendar::Value
|
20
|
+
v
|
21
|
+
else
|
22
|
+
klass.new v, params
|
23
|
+
end
|
24
|
+
end
|
25
|
+
else
|
26
|
+
[klass.new(value, params)]
|
27
|
+
end
|
28
|
+
super mapped
|
29
|
+
end
|
30
|
+
|
31
|
+
def params_ical
|
32
|
+
value.each do |v|
|
33
|
+
ical_params.merge! v.ical_params
|
34
|
+
end
|
35
|
+
super
|
36
|
+
end
|
37
|
+
|
38
|
+
def value_ical
|
39
|
+
value.map do |v|
|
40
|
+
v.value_ical
|
41
|
+
end.join value_delimiter
|
42
|
+
end
|
43
|
+
|
44
|
+
def valid?
|
45
|
+
klass = value.first.class
|
46
|
+
!value.all? { |v| v.class == klass }
|
47
|
+
end
|
48
|
+
|
49
|
+
def value_type
|
50
|
+
value.first.value_type
|
51
|
+
end
|
52
|
+
|
53
|
+
private
|
54
|
+
|
55
|
+
def needs_value_type?(default_type)
|
56
|
+
value.first.class != default_type
|
57
|
+
end
|
58
|
+
end
|
59
|
+
|
60
|
+
end
|
61
|
+
end
|
62
|
+
end
|
@@ -0,0 +1,61 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
begin
|
4
|
+
require 'active_support/time'
|
5
|
+
|
6
|
+
if defined?(ActiveSupport::TimeWithZone)
|
7
|
+
require_relative 'active_support_time_with_zone_adapter'
|
8
|
+
end
|
9
|
+
rescue NameError
|
10
|
+
# ActiveSupport v7+ needs the base require to be run first before loading
|
11
|
+
# specific parts of it.
|
12
|
+
# https://guides.rubyonrails.org/active_support_core_extensions.html#stand-alone-active-support
|
13
|
+
require 'active_support'
|
14
|
+
retry
|
15
|
+
rescue LoadError
|
16
|
+
# tis ok, just a bit less fancy
|
17
|
+
end
|
18
|
+
|
19
|
+
module Icalendar
|
20
|
+
module Values
|
21
|
+
module Helpers
|
22
|
+
module TimeWithZone
|
23
|
+
attr_reader :tz_utc
|
24
|
+
|
25
|
+
def initialize(value, params = {})
|
26
|
+
params = Icalendar::DowncasedHash(params)
|
27
|
+
@tz_utc = params['tzid'] == 'UTC'
|
28
|
+
x_tz_info = params.delete 'x-tz-info'
|
29
|
+
|
30
|
+
offset_value = unless params['tzid'].nil?
|
31
|
+
tzid = params['tzid'].is_a?(::Array) ? params['tzid'].first : params['tzid']
|
32
|
+
support_classes_defined = defined?(ActiveSupport::TimeZone) && defined?(ActiveSupportTimeWithZoneAdapter)
|
33
|
+
if support_classes_defined && (tz = ActiveSupport::TimeZone[tzid])
|
34
|
+
# plan a - use ActiveSupport::TimeWithZone
|
35
|
+
ActiveSupportTimeWithZoneAdapter.new(nil, tz, value)
|
36
|
+
elsif !x_tz_info.nil?
|
37
|
+
# plan b - use definition from provided `VTIMEZONE`
|
38
|
+
offset = x_tz_info.offset_for_local(value).to_s
|
39
|
+
if value.respond_to?(:change)
|
40
|
+
value.change offset: offset
|
41
|
+
else
|
42
|
+
::Time.new value.year, value.month, value.day, value.hour, value.min, value.sec, offset
|
43
|
+
end
|
44
|
+
elsif support_classes_defined && (tz = ActiveSupport::TimeZone[tzid.split.first])
|
45
|
+
# plan c - try to find an ActiveSupport::TimeWithZone based on the first word of the tzid
|
46
|
+
params['tzid'] = [tz.tzinfo.name]
|
47
|
+
ActiveSupportTimeWithZoneAdapter.new(nil, tz, value)
|
48
|
+
end
|
49
|
+
# plan d - just ignore the tzid
|
50
|
+
end
|
51
|
+
super((offset_value || value), params)
|
52
|
+
end
|
53
|
+
|
54
|
+
def params_ical
|
55
|
+
ical_params.delete 'tzid' if tz_utc
|
56
|
+
super
|
57
|
+
end
|
58
|
+
end
|
59
|
+
end
|
60
|
+
end
|
61
|
+
end
|
@@ -1,13 +1,13 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
3
|
require 'date'
|
4
|
-
require_relative 'time_with_zone'
|
4
|
+
require_relative 'helpers/time_with_zone'
|
5
5
|
|
6
6
|
module Icalendar
|
7
7
|
module Values
|
8
8
|
|
9
9
|
class Time < Value
|
10
|
-
include TimeWithZone
|
10
|
+
include Helpers::TimeWithZone
|
11
11
|
|
12
12
|
FORMAT = '%H%M%S'
|
13
13
|
|
data/lib/icalendar/version.rb
CHANGED
data/spec/roundtrip_spec.rb
CHANGED
@@ -14,7 +14,7 @@ describe Icalendar do
|
|
14
14
|
event = Icalendar::Event.new
|
15
15
|
parsed = Icalendar::Calendar.parse(source).first
|
16
16
|
event.rdate = parsed.events.first.rdate
|
17
|
-
expect(event.rdate.first).to be_kind_of Icalendar::Values::Array
|
17
|
+
expect(event.rdate.first).to be_kind_of Icalendar::Values::Helpers::Array
|
18
18
|
expect(event.rdate.first.params_ical).to eq ";TZID=US-Mountain"
|
19
19
|
end
|
20
20
|
end
|
@@ -27,6 +27,22 @@ describe Icalendar::Values::DateTime do
|
|
27
27
|
expect(subject.value.utc.hour).to eq 23
|
28
28
|
end
|
29
29
|
end
|
30
|
+
|
31
|
+
context 'nonstandard format tzid local time' do
|
32
|
+
let(:value) { '20230901T230404' }
|
33
|
+
let(:params) { {'tzid' => 'Singapore Standard Time'} }
|
34
|
+
|
35
|
+
it 'parses the value as local time' do
|
36
|
+
expect(subject.value.hour).to eq 23
|
37
|
+
expect(subject.value.utc_offset).to eq 28800
|
38
|
+
expect(subject.value.utc.hour).to eq 15
|
39
|
+
end
|
40
|
+
|
41
|
+
it 'updates the tzid' do
|
42
|
+
# use an array because that's how output from the parser will end up
|
43
|
+
expect(subject.ical_params['tzid']).to eq ['Asia/Singapore']
|
44
|
+
end
|
45
|
+
end
|
30
46
|
end
|
31
47
|
|
32
48
|
else
|
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.
|
4
|
+
version: 2.10.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: 2023-
|
11
|
+
date: 2023-11-01 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: ice_cube
|
@@ -187,8 +187,6 @@ files:
|
|
187
187
|
- lib/icalendar/todo.rb
|
188
188
|
- lib/icalendar/tzinfo.rb
|
189
189
|
- lib/icalendar/value.rb
|
190
|
-
- lib/icalendar/values/active_support_time_with_zone_adapter.rb
|
191
|
-
- lib/icalendar/values/array.rb
|
192
190
|
- lib/icalendar/values/binary.rb
|
193
191
|
- lib/icalendar/values/boolean.rb
|
194
192
|
- lib/icalendar/values/cal_address.rb
|
@@ -197,12 +195,14 @@ files:
|
|
197
195
|
- lib/icalendar/values/date_time.rb
|
198
196
|
- lib/icalendar/values/duration.rb
|
199
197
|
- lib/icalendar/values/float.rb
|
198
|
+
- lib/icalendar/values/helpers/active_support_time_with_zone_adapter.rb
|
199
|
+
- lib/icalendar/values/helpers/array.rb
|
200
|
+
- lib/icalendar/values/helpers/time_with_zone.rb
|
200
201
|
- lib/icalendar/values/integer.rb
|
201
202
|
- lib/icalendar/values/period.rb
|
202
203
|
- lib/icalendar/values/recur.rb
|
203
204
|
- lib/icalendar/values/text.rb
|
204
205
|
- lib/icalendar/values/time.rb
|
205
|
-
- lib/icalendar/values/time_with_zone.rb
|
206
206
|
- lib/icalendar/values/uri.rb
|
207
207
|
- lib/icalendar/values/utc_offset.rb
|
208
208
|
- lib/icalendar/version.rb
|
@@ -247,7 +247,8 @@ licenses:
|
|
247
247
|
- BSD-2-Clause
|
248
248
|
- GPL-3.0-only
|
249
249
|
- icalendar
|
250
|
-
metadata:
|
250
|
+
metadata:
|
251
|
+
changelog_uri: https://github.com/icalendar/icalendar/blob/main/History.txt
|
251
252
|
post_install_message: 'ActiveSupport is required for TimeWithZone support, but not
|
252
253
|
required for general use.
|
253
254
|
|
@@ -266,7 +267,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
266
267
|
- !ruby/object:Gem::Version
|
267
268
|
version: '0'
|
268
269
|
requirements: []
|
269
|
-
rubygems_version: 3.
|
270
|
+
rubygems_version: 3.4.10
|
270
271
|
signing_key:
|
271
272
|
specification_version: 4
|
272
273
|
summary: A ruby implementation of the iCalendar specification (RFC-5545).
|
@@ -1,14 +0,0 @@
|
|
1
|
-
# frozen_string_literal: true
|
2
|
-
|
3
|
-
module Icalendar
|
4
|
-
module Values
|
5
|
-
class ActiveSupportTimeWithZoneAdapter < ActiveSupport::TimeWithZone
|
6
|
-
# ActiveSupport::TimeWithZone implements a #to_a method that will cause
|
7
|
-
# unexpected behavior in components with multi_property DateTime
|
8
|
-
# properties when the setters for those properties are invoked with an
|
9
|
-
# Icalendar::Values::DateTime that is delegating for an
|
10
|
-
# ActiveSupport::TimeWithZone. To avoid this behavior, undefine #to_a.
|
11
|
-
undef_method :to_a
|
12
|
-
end
|
13
|
-
end
|
14
|
-
end
|
@@ -1,60 +0,0 @@
|
|
1
|
-
# frozen_string_literal: true
|
2
|
-
|
3
|
-
module Icalendar
|
4
|
-
module Values
|
5
|
-
|
6
|
-
class Array < Value
|
7
|
-
|
8
|
-
attr_reader :value_delimiter
|
9
|
-
|
10
|
-
def initialize(value, klass, params = {}, options = {})
|
11
|
-
@value_delimiter = options[:delimiter] || ','
|
12
|
-
mapped = if value.is_a? ::Array
|
13
|
-
value.map do |v|
|
14
|
-
if v.is_a? Icalendar::Values::Array
|
15
|
-
Icalendar::Values::Array.new v.value, klass, v.ical_params, delimiter: v.value_delimiter
|
16
|
-
elsif v.is_a? ::Array
|
17
|
-
Icalendar::Values::Array.new v, klass, params, delimiter: value_delimiter
|
18
|
-
elsif v.is_a? Icalendar::Value
|
19
|
-
v
|
20
|
-
else
|
21
|
-
klass.new v, params
|
22
|
-
end
|
23
|
-
end
|
24
|
-
else
|
25
|
-
[klass.new(value, params)]
|
26
|
-
end
|
27
|
-
super mapped
|
28
|
-
end
|
29
|
-
|
30
|
-
def params_ical
|
31
|
-
value.each do |v|
|
32
|
-
ical_params.merge! v.ical_params
|
33
|
-
end
|
34
|
-
super
|
35
|
-
end
|
36
|
-
|
37
|
-
def value_ical
|
38
|
-
value.map do |v|
|
39
|
-
v.value_ical
|
40
|
-
end.join value_delimiter
|
41
|
-
end
|
42
|
-
|
43
|
-
def valid?
|
44
|
-
klass = value.first.class
|
45
|
-
!value.all? { |v| v.class == klass }
|
46
|
-
end
|
47
|
-
|
48
|
-
def value_type
|
49
|
-
value.first.value_type
|
50
|
-
end
|
51
|
-
|
52
|
-
private
|
53
|
-
|
54
|
-
def needs_value_type?(default_type)
|
55
|
-
value.first.class != default_type
|
56
|
-
end
|
57
|
-
end
|
58
|
-
|
59
|
-
end
|
60
|
-
end
|
@@ -1,55 +0,0 @@
|
|
1
|
-
# frozen_string_literal: true
|
2
|
-
|
3
|
-
require 'icalendar/timezone_store'
|
4
|
-
|
5
|
-
begin
|
6
|
-
require 'active_support/time'
|
7
|
-
|
8
|
-
if defined?(ActiveSupport::TimeWithZone)
|
9
|
-
require 'icalendar/values/active_support_time_with_zone_adapter'
|
10
|
-
end
|
11
|
-
rescue NameError
|
12
|
-
# ActiveSupport v7+ needs the base require to be run first before loading
|
13
|
-
# specific parts of it.
|
14
|
-
# https://guides.rubyonrails.org/active_support_core_extensions.html#stand-alone-active-support
|
15
|
-
require 'active_support'
|
16
|
-
retry
|
17
|
-
rescue LoadError
|
18
|
-
# tis ok, just a bit less fancy
|
19
|
-
end
|
20
|
-
|
21
|
-
module Icalendar
|
22
|
-
module Values
|
23
|
-
module TimeWithZone
|
24
|
-
attr_reader :tz_utc
|
25
|
-
|
26
|
-
def initialize(value, params = {})
|
27
|
-
params = Icalendar::DowncasedHash(params)
|
28
|
-
@tz_utc = params['tzid'] == 'UTC'
|
29
|
-
x_tz_info = params.delete 'x-tz-info'
|
30
|
-
|
31
|
-
offset_value = unless params['tzid'].nil?
|
32
|
-
tzid = params['tzid'].is_a?(::Array) ? params['tzid'].first : params['tzid']
|
33
|
-
if defined?(ActiveSupport::TimeZone) &&
|
34
|
-
defined?(ActiveSupportTimeWithZoneAdapter) &&
|
35
|
-
(tz = ActiveSupport::TimeZone[tzid])
|
36
|
-
ActiveSupportTimeWithZoneAdapter.new(nil, tz, value)
|
37
|
-
elsif !x_tz_info.nil?
|
38
|
-
offset = x_tz_info.offset_for_local(value).to_s
|
39
|
-
if value.respond_to?(:change)
|
40
|
-
value.change offset: offset
|
41
|
-
else
|
42
|
-
::Time.new value.year, value.month, value.day, value.hour, value.min, value.sec, offset
|
43
|
-
end
|
44
|
-
end
|
45
|
-
end
|
46
|
-
super((offset_value || value), params)
|
47
|
-
end
|
48
|
-
|
49
|
-
def params_ical
|
50
|
-
ical_params.delete 'tzid' if tz_utc
|
51
|
-
super
|
52
|
-
end
|
53
|
-
end
|
54
|
-
end
|
55
|
-
end
|