icalendar 2.5.3 → 2.6.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
  SHA256:
3
- metadata.gz: 5cc6bb6eba99ce8e25232bb417cb9f3c5e9438b20a56a4a136e9b0d2f9a71b7c
4
- data.tar.gz: f9022e04c0aad637791e0c964f6c27eef986f610bc2417c5f70b85ec65275445
3
+ metadata.gz: 165b0d4192335c7fb36354195666ea4d386d63029190d7c1248ab3d4fe1554ec
4
+ data.tar.gz: 3781a361f70f099aff7e891e7f3bc26eaaac059c615a40d377634d41d4905d3c
5
5
  SHA512:
6
- metadata.gz: 2bd684301596f90024af2a690f7cbc20bdba2d02a902105fddc298a4108ac23825d444c2658dace796146cc0581c88ef2c6e62e229d4fa89ff2e89d8ac41eadb
7
- data.tar.gz: e62e5f8f4fe3983d19cf213c505fe8f9f002cf7e0b1054a958af4874eddaf376b80660a3e3d1b6cac50cab96a0535169738adf443f5fcc982c318750f5f16200
6
+ metadata.gz: 682932f688906567e95fc58b4952b05664e9eeece75708bd83c1ebc42362e927a252f77a8124bf028500359e23aa1d7b071b9af5ccf1032d92074c29cecd6f3f
7
+ data.tar.gz: 38a5153c80c3b170c2d973104f565d3e0ff5a3c74b8573870331cc842e23e2918de712b6a0046cecb758a1aa3450c72f4aedaa2f22bb302a7343332f2c0a017a
data/.gitignore CHANGED
@@ -6,3 +6,4 @@ pkg/
6
6
  .bundle
7
7
  coverage/
8
8
  tags
9
+ .idea
@@ -6,15 +6,11 @@ rvm:
6
6
  - 2.6
7
7
  - 2.5
8
8
  - 2.4
9
- - 2.3
10
- - 2.2
11
9
  - jruby-19mode
12
- - rbx-3
13
10
  - ruby-head
14
11
  - jruby-head
15
12
  matrix:
16
13
  allow_failures:
17
- - rvm: 2.2
18
14
  - rvm: ruby-head
19
15
  - rvm: jruby-head
20
16
  script: bundle exec rake spec
@@ -1,3 +1,9 @@
1
+ === 2.6.0 2019-11-26
2
+ * Improve performance for calculating timezone offsets - Justin Howard
3
+ * Make it possible to de/serialize with Marshal - Pawel Niewiadomski
4
+ * Avoid FrozenError when running with frozen_string_literal
5
+ * Update minimum Ruby version to supported versions
6
+
1
7
  === 2.5.3 2019-03-04
2
8
  * Improve parsing performance - nehresma
3
9
  * Support tzinfo 2.0 - Misty De Meo
@@ -24,7 +24,7 @@ ActiveSupport is required for TimeWithZone support, but not required for general
24
24
  s.executables = `git ls-files -- bin/*`.split("\n").map { |f| File.basename f }
25
25
  s.require_paths = ['lib']
26
26
 
27
- s.required_ruby_version = '>= 2.2.0'
27
+ s.required_ruby_version = '>= 2.4.0'
28
28
 
29
29
  s.add_dependency 'ice_cube', '~> 0.16'
30
30
 
@@ -22,6 +22,7 @@ end
22
22
 
23
23
  require 'icalendar/has_properties'
24
24
  require 'icalendar/has_components'
25
+ require 'icalendar/marshable'
25
26
  require 'icalendar/component'
26
27
  require 'icalendar/value'
27
28
  require 'icalendar/alarm'
@@ -10,7 +10,7 @@ module Icalendar
10
10
  end
11
11
 
12
12
  def initialize(*args)
13
- @custom_components = Hash.new { |h, k| h[k] = [] }
13
+ @custom_components = Hash.new
14
14
  super
15
15
  end
16
16
 
@@ -26,7 +26,7 @@ module Icalendar
26
26
  if method_name =~ /^add_(x_\w+)$/
27
27
  component_name = $1
28
28
  custom = args.first || Component.new(component_name, component_name.upcase)
29
- custom_components[component_name] << custom
29
+ (custom_components[component_name] ||= []) << custom
30
30
  yield custom if block_given?
31
31
  custom
32
32
  else
@@ -10,7 +10,7 @@ module Icalendar
10
10
  end
11
11
 
12
12
  def initialize(*args)
13
- @custom_properties = Hash.new { |h, k| h[k] = [] }
13
+ @custom_properties = Hash.new
14
14
  super
15
15
  end
16
16
 
@@ -39,7 +39,7 @@ module Icalendar
39
39
  end
40
40
 
41
41
  def custom_property(property_name)
42
- custom_properties[property_name.downcase]
42
+ custom_properties[property_name.downcase] || []
43
43
  end
44
44
 
45
45
  def append_custom_property(property_name, value)
@@ -49,9 +49,9 @@ module Icalendar
49
49
  elsif self.class.multiple_properties.include? property_name
50
50
  send "append_#{property_name}", value
51
51
  elsif value.is_a? Icalendar::Value
52
- custom_properties[property_name] << value
52
+ (custom_properties[property_name] ||= []) << value
53
53
  else
54
- custom_properties[property_name] << Icalendar::Values::Text.new(value)
54
+ (custom_properties[property_name] ||= []) << Icalendar::Values::Text.new(value)
55
55
  end
56
56
  end
57
57
 
@@ -0,0 +1,34 @@
1
+ module Icalendar
2
+ module Marshable
3
+ def self.included(base)
4
+ base.extend ClassMethods
5
+ end
6
+
7
+ def marshal_dump
8
+ instance_variables
9
+ .reject { |ivar| self.class.transient_variables.include?(ivar) }
10
+ .each_with_object({}) do |ivar, serialized|
11
+
12
+ serialized[ivar] = instance_variable_get(ivar)
13
+ end
14
+ end
15
+
16
+ def marshal_load(serialized)
17
+ serialized.each do |ivar, value|
18
+ unless self.class.transient_variables.include?(ivar)
19
+ instance_variable_set(ivar, value)
20
+ end
21
+ end
22
+ end
23
+
24
+ module ClassMethods
25
+ def transient_variables
26
+ @transient_variables ||= [:@transient_variables]
27
+ end
28
+
29
+ def transient_variable(name)
30
+ transient_variables.push(name.to_sym)
31
+ end
32
+ end
33
+ end
34
+ end
@@ -14,10 +14,40 @@ module Icalendar
14
14
  optional_property :comment
15
15
  optional_property :rdate, Icalendar::Values::DateTime
16
16
  optional_property :tzname
17
+
18
+ transient_variable :@cached_occurrences
19
+ transient_variable :@occurrences
17
20
  end
18
21
  end
22
+
23
+ def occurrences
24
+ @occurrences ||= IceCube::Schedule.new(dtstart.to_time) do |s|
25
+ rrule.each do |rule|
26
+ s.add_recurrence_rule IceCube::Rule.from_ical(rule.value_ical)
27
+ end
28
+ rdate.each do |date|
29
+ s.add_recurrence_time date.to_time
30
+ end
31
+ end.all_occurrences_enumerator
32
+ end
33
+
34
+ def previous_occurrence(from)
35
+ from = IceCube::TimeUtil.match_zone(from, dtstart.to_time)
36
+
37
+ @cached_occurrences ||= []
38
+ while @cached_occurrences.empty? || @cached_occurrences.last <= from
39
+ begin
40
+ @cached_occurrences << occurrences.next
41
+ rescue StopIteration
42
+ break
43
+ end
44
+ end
45
+
46
+ @cached_occurrences.reverse_each.find { |occurrence| occurrence < from }
47
+ end
19
48
  end
20
49
  class Daylight < Component
50
+ include Marshable
21
51
  include TzProperties
22
52
 
23
53
  def initialize
@@ -25,6 +55,7 @@ module Icalendar
25
55
  end
26
56
  end
27
57
  class Standard < Component
58
+ include Marshable
28
59
  include TzProperties
29
60
 
30
61
  def initialize
@@ -75,30 +106,14 @@ module Icalendar
75
106
 
76
107
  def standard_for(local)
77
108
  possible = standards.map do |std|
78
- schedule = IceCube::Schedule.new(std.dtstart.to_time) do |s|
79
- std.rrule.each do |rule|
80
- s.add_recurrence_rule IceCube::Rule.from_ical(rule.value_ical)
81
- end
82
- std.rdate.each do |date|
83
- s.add_recurrence_time date.to_time
84
- end
85
- end
86
- [schedule.previous_occurrence(local.to_time), std]
109
+ [std.previous_occurrence(local.to_time), std]
87
110
  end
88
111
  possible.sort_by(&:first).last
89
112
  end
90
113
 
91
114
  def daylight_for(local)
92
115
  possible = daylights.map do |day|
93
- schedule = IceCube::Schedule.new(day.dtstart.to_time) do |s|
94
- day.rrule.each do |rule|
95
- s.add_recurrence_rule IceCube::Rule.from_ical(rule.value_ical)
96
- end
97
- day.rdate.each do |date|
98
- s.add_recurrence_time date.to_time
99
- end
100
- end
101
- [schedule.previous_occurrence(local.to_time), day]
116
+ [day.previous_occurrence(local.to_time), day]
102
117
  end
103
118
  possible.sort_by(&:first).last
104
119
  end
@@ -37,8 +37,7 @@ module Icalendar
37
37
  end
38
38
 
39
39
  def parse_fields(value)
40
- value.gsub!(/\s+/, '')
41
- md = /\A(?<behind>[+-])(?<hours>\d{2})(?<minutes>\d{2})(?<seconds>\d{2})?\z/.match value
40
+ md = /\A(?<behind>[+-])(?<hours>\d{2})(?<minutes>\d{2})(?<seconds>\d{2})?\z/.match value.gsub(/\s+/, '')
42
41
  {
43
42
  behind: (md[:behind] == '-'),
44
43
  hours: md[:hours].to_i,
@@ -1,5 +1,5 @@
1
1
  module Icalendar
2
2
 
3
- VERSION = '2.5.3'
3
+ VERSION = '2.6.0'
4
4
 
5
5
  end
@@ -2,6 +2,12 @@ require 'spec_helper'
2
2
 
3
3
  describe Icalendar::Calendar do
4
4
 
5
+ context 'marshalling' do
6
+ it 'can be de/serialized' do
7
+ Marshal.load(Marshal.dump(subject))
8
+ end
9
+ end
10
+
5
11
  context 'values' do
6
12
  let(:property) { 'my-value' }
7
13
 
@@ -113,6 +113,13 @@ describe Icalendar::Event do
113
113
  end
114
114
  end
115
115
 
116
+ describe "#custom_property" do
117
+ it "returns a default for missing properties" do
118
+ expect(subject.x_doesnt_exist).to eq([])
119
+ expect(subject.custom_property "x_doesnt_exist").to eq([])
120
+ end
121
+ end
122
+
116
123
  describe '.parse' do
117
124
  let(:source) { File.read File.join(File.dirname(__FILE__), 'fixtures', fn) }
118
125
  let(:fn) { 'event.ics' }
@@ -28,4 +28,52 @@ describe Icalendar::Timezone do
28
28
  it { should_not be_valid }
29
29
  end
30
30
  end
31
+
32
+ context 'marshalling' do
33
+ context 'with standard/daylight components' do
34
+ before do
35
+ subject.standard do |standard|
36
+ standard.rrule = Icalendar::Values::Recur.new("FREQ=YEARLY;INTERVAL=1;BYDAY=-1SU;BYMONTH=10")
37
+ standard.dtstart = Icalendar::Values::DateTime.new("16010101T030000")
38
+ standard.tzoffsetfrom = Icalendar::Values::UtcOffset.new("+0200")
39
+ standard.tzoffsetto = Icalendar::Values::UtcOffset.new("+0100")
40
+ end
41
+
42
+ subject.daylight do |daylight|
43
+ daylight.rrule = Icalendar::Values::Recur.new("FREQ=YEARLY;INTERVAL=1;BYDAY=-1SU;BYMONTH=3")
44
+ daylight.dtstart = Icalendar::Values::DateTime.new("16010101T020000")
45
+ daylight.tzoffsetfrom = Icalendar::Values::UtcOffset.new("+0100")
46
+ daylight.tzoffsetto = Icalendar::Values::UtcOffset.new("+0200")
47
+ end
48
+ end
49
+
50
+ it 'can be de/serialized' do
51
+ first_standard = subject.standards.first
52
+ first_daylight = subject.daylights.first
53
+
54
+ expect(first_standard.valid?).to be_truthy
55
+ expect(first_daylight.valid?).to be_truthy
56
+
57
+ # calling previous_occurrence intializes @cached_occurrences with a time that's not handled by ruby marshaller
58
+ first_occurence_for = Time.new(1601, 10, 31)
59
+
60
+ standard_previous_occurrence = first_standard.previous_occurrence(first_occurence_for)
61
+ expect(standard_previous_occurrence).not_to be_nil
62
+
63
+ daylight_previous_occurrence = first_daylight.previous_occurrence(first_occurence_for)
64
+ expect(daylight_previous_occurrence).not_to be_nil
65
+
66
+ deserialized = nil
67
+
68
+ expect { deserialized = Marshal.load(Marshal.dump(subject)) }.not_to raise_exception
69
+
70
+ expect(deserialized.standards.first.previous_occurrence(first_occurence_for)).to eq(standard_previous_occurrence)
71
+ expect(deserialized.daylights.first.previous_occurrence(first_occurence_for)).to eq(daylight_previous_occurrence)
72
+ end
73
+ end
74
+
75
+ it 'can be de/serialized' do
76
+ expect { Marshal.load(Marshal.dump(subject)) }.not_to raise_exception
77
+ end
78
+ end
31
79
  end
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.5.3
4
+ version: 2.6.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: 2019-03-05 00:00:00.000000000 Z
11
+ date: 2019-11-27 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: ice_cube
@@ -180,6 +180,7 @@ files:
180
180
  - lib/icalendar/has_properties.rb
181
181
  - lib/icalendar/journal.rb
182
182
  - lib/icalendar/logger.rb
183
+ - lib/icalendar/marshable.rb
183
184
  - lib/icalendar/parser.rb
184
185
  - lib/icalendar/timezone.rb
185
186
  - lib/icalendar/timezone_store.rb
@@ -253,14 +254,14 @@ required_ruby_version: !ruby/object:Gem::Requirement
253
254
  requirements:
254
255
  - - ">="
255
256
  - !ruby/object:Gem::Version
256
- version: 2.2.0
257
+ version: 2.4.0
257
258
  required_rubygems_version: !ruby/object:Gem::Requirement
258
259
  requirements:
259
260
  - - ">="
260
261
  - !ruby/object:Gem::Version
261
262
  version: '0'
262
263
  requirements: []
263
- rubygems_version: 3.0.1
264
+ rubygems_version: 3.0.3
264
265
  signing_key:
265
266
  specification_version: 4
266
267
  summary: A ruby implementation of the iCalendar specification (RFC-5545).