icalendar-rrule 0.1.5

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 ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA256:
3
+ metadata.gz: 5bc3ceb9790452476e8f32de6f5c5f6f9780fb13f7fb667d002a76d8a0438774
4
+ data.tar.gz: a910d639df4e221adb306571005e3eb23a4f68753de363946cb8a6befb906080
5
+ SHA512:
6
+ metadata.gz: 32f11de521b34a9caeda104a49cf428e9c9f3f23938e804c057a5f0a8371bffbb96eb8cbc9cbe5ed890c5813a0a3d5240994d72db175b86f2f18e2b5f8108896
7
+ data.tar.gz: 440e88786c1ed54ee3c090a709e0fd145d930147bfb00c62cd8d8b98fafda00a0680880727e68d0f2c13ba9845010d04718261eb924ac81e06c2efe6c96f5e75
data/.gitignore ADDED
@@ -0,0 +1,100 @@
1
+ *.gem
2
+ *.rbc
3
+ /.config
4
+ /coverage/
5
+ /InstalledFiles
6
+ /pkg/
7
+ /spec/reports/
8
+ /spec/examples.txt
9
+ /test/tmp/
10
+ /test/version_tmp/
11
+ /tmp/
12
+
13
+ # Used by dotenv library to load environment variables.
14
+ # .env
15
+
16
+ ## Specific to RubyMotion:
17
+ .dat*
18
+ .repl_history
19
+ build/
20
+ *.bridgesupport
21
+ build-iPhoneOS/
22
+ build-iPhoneSimulator/
23
+
24
+ ## Specific to RubyMotion (use of CocoaPods):
25
+ #
26
+ # We recommend against adding the Pods directory to your .gitignore. However
27
+ # you should judge for yourself, the pros and cons are mentioned at:
28
+ # https://guides.cocoapods.org/using/using-cocoapods.html#should-i-check-the-pods-directory-into-source-control
29
+ #
30
+ # vendor/Pods/
31
+
32
+ ## Documentation cache and generated files:
33
+ /.yardoc/
34
+ /_yardoc/
35
+ /doc/
36
+ /rdoc/
37
+
38
+ ## Environment normalization:
39
+ /.bundle/
40
+ /vendor/bundle
41
+ /lib/bundler/man/
42
+
43
+ # for a library or gem, you might want to ignore these files since the code is
44
+ # intended to run in multiple environments; otherwise, check them in:
45
+ .ruby-version
46
+ .ruby-gemset
47
+
48
+ # unless supporting rvm < 1.11.0 or doing something fancy, ignore this:
49
+ .rvmrc
50
+ ### JetBrains template
51
+ # Covers JetBrains IDEs: IntelliJ, RubyMine, PhpStorm, AppCode, PyCharm, CLion, Android Studio and WebStorm
52
+ # Reference: https://intellij-support.jetbrains.com/hc/en-us/articles/206544839
53
+
54
+ # User-specific stuff:
55
+ .idea/**/workspace.xml
56
+ .idea/**/tasks.xml
57
+ .idea/dictionaries
58
+
59
+ # Sensitive or high-churn files:
60
+ .idea/**/dataSources/
61
+ .idea/**/dataSources.ids
62
+ .idea/**/dataSources.local.xml
63
+ .idea/**/sqlDataSources.xml
64
+ .idea/**/dynamic.xml
65
+ .idea/**/uiDesigner.xml
66
+
67
+ # Gradle:
68
+ .idea/**/gradle.xml
69
+ .idea/**/libraries
70
+
71
+ # CMake
72
+ cmake-build-debug/
73
+ cmake-build-release/
74
+
75
+ # Mongo Explorer plugin:
76
+ .idea
77
+
78
+ ## File-based project format:
79
+ *.iws
80
+
81
+ ## Plugin-specific files:
82
+
83
+ # IntelliJ
84
+ out/
85
+
86
+ # mpeltonen/sbt-idea plugin
87
+ .idea_modules/
88
+
89
+ # JIRA plugin
90
+ atlassian-ide-plugin.xml
91
+
92
+ # Cursive Clojure plugin
93
+ .idea/replstate.xml
94
+
95
+ # Crashlytics plugin (for Android Studio and IntelliJ)
96
+ com_crashlytics_export_strings.xml
97
+ crashlytics.properties
98
+ crashlytics-build.properties
99
+ fabric.properties
100
+ /.rspec_status
data/.rspec ADDED
@@ -0,0 +1,3 @@
1
+ --format documentation
2
+ --color
3
+ --require spec_helper
data/.rubocop.yml ADDED
@@ -0,0 +1,45 @@
1
+ require: rubocop-rspec
2
+
3
+ AllCops:
4
+ TargetRubyVersion: 2.5.0
5
+
6
+ Style/Documentation:
7
+ Exclude:
8
+ - 'spec/**/*'
9
+ - 'test/**/*'
10
+
11
+ Metrics/BlockLength:
12
+ Exclude:
13
+ - 'spec/**/*'
14
+ - 'test/**/*'
15
+
16
+ Metrics/LineLength:
17
+ Max: 122
18
+
19
+ Metrics/MethodLength:
20
+ Max: 16
21
+
22
+ Naming/FileName:
23
+ Exclude:
24
+ - 'lib/icalendar-rrule.rb'
25
+ - 'lib/icalendar/schedulable-component.rb'
26
+ - 'lib/icalendar/scannable-calendar.rb'
27
+ - 'spec/icalendar/scannable-calendar_spec.rb'
28
+ - 'spec/icalendar/schedulable-component_spec.rb'
29
+
30
+
31
+
32
+ Metrics/ModuleLength:
33
+ Enabled: false
34
+
35
+ Style/DocumentationMethod:
36
+ Description: 'Public methods.'
37
+ Enabled: true
38
+ Exclude:
39
+ - 'spec/**/*'
40
+ - 'test/**/*'
41
+
42
+
43
+
44
+
45
+ # inherit_from: .rubocop_todo.yml
data/.travis.yml ADDED
@@ -0,0 +1,5 @@
1
+ sudo: false
2
+ language: ruby
3
+ rvm:
4
+ - 2.5.1
5
+ before_install: gem install bundler -v 1.16.1
data/.yardopts ADDED
@@ -0,0 +1 @@
1
+ --markup=markdown
data/Gemfile ADDED
@@ -0,0 +1,8 @@
1
+ # frozen_string_literal: true
2
+
3
+ source 'https://rubygems.org'
4
+
5
+ git_source(:github) { |repo_name| "https://github.com/#{repo_name}" }
6
+
7
+ # Specify your gem's dependencies in icalendar-rrule.gemspec
8
+ gemspec
data/Gemfile.lock ADDED
@@ -0,0 +1,79 @@
1
+ PATH
2
+ remote: .
3
+ specs:
4
+ icalendar-rrule (0.1.5)
5
+ activesupport (~> 5.1)
6
+ icalendar (~> 2.4)
7
+ ice_cube (~> 0.16)
8
+
9
+ GEM
10
+ remote: https://rubygems.org/
11
+ specs:
12
+ activesupport (5.2.0)
13
+ concurrent-ruby (~> 1.0, >= 1.0.2)
14
+ i18n (>= 0.7, < 2)
15
+ minitest (~> 5.1)
16
+ tzinfo (~> 1.1)
17
+ ast (2.4.0)
18
+ concurrent-ruby (1.0.5)
19
+ diff-lcs (1.3)
20
+ docile (1.3.0)
21
+ i18n (1.0.1)
22
+ concurrent-ruby (~> 1.0)
23
+ icalendar (2.4.1)
24
+ ice_cube (0.16.2)
25
+ json (2.1.0)
26
+ minitest (5.11.3)
27
+ parallel (1.12.1)
28
+ parser (2.5.1.0)
29
+ ast (~> 2.4.0)
30
+ powerpack (0.1.1)
31
+ rainbow (3.0.0)
32
+ rake (10.5.0)
33
+ rspec (3.7.0)
34
+ rspec-core (~> 3.7.0)
35
+ rspec-expectations (~> 3.7.0)
36
+ rspec-mocks (~> 3.7.0)
37
+ rspec-core (3.7.1)
38
+ rspec-support (~> 3.7.0)
39
+ rspec-expectations (3.7.0)
40
+ diff-lcs (>= 1.2.0, < 2.0)
41
+ rspec-support (~> 3.7.0)
42
+ rspec-mocks (3.7.0)
43
+ diff-lcs (>= 1.2.0, < 2.0)
44
+ rspec-support (~> 3.7.0)
45
+ rspec-support (3.7.1)
46
+ rubocop (0.55.0)
47
+ parallel (~> 1.10)
48
+ parser (>= 2.5)
49
+ powerpack (~> 0.1)
50
+ rainbow (>= 2.2.2, < 4.0)
51
+ ruby-progressbar (~> 1.7)
52
+ unicode-display_width (~> 1.0, >= 1.0.1)
53
+ rubocop-rspec (1.25.1)
54
+ rubocop (>= 0.53.0)
55
+ ruby-progressbar (1.9.0)
56
+ simplecov (0.16.1)
57
+ docile (~> 1.1)
58
+ json (>= 1.8, < 3)
59
+ simplecov-html (~> 0.10.0)
60
+ simplecov-html (0.10.2)
61
+ thread_safe (0.3.6)
62
+ tzinfo (1.2.5)
63
+ thread_safe (~> 0.1)
64
+ unicode-display_width (1.3.2)
65
+
66
+ PLATFORMS
67
+ ruby
68
+
69
+ DEPENDENCIES
70
+ bundler (~> 1.16)
71
+ icalendar-rrule!
72
+ rake (~> 10.0)
73
+ rspec (~> 3.7)
74
+ rubocop (~> 0.55.0)
75
+ rubocop-rspec (~> 1.24)
76
+ simplecov (~> 0.16)
77
+
78
+ BUNDLED WITH
79
+ 1.16.1
data/LICENSE.txt ADDED
@@ -0,0 +1,21 @@
1
+ The MIT License (MIT)
2
+
3
+ Copyright (c) 2018 Harald
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in
13
+ all copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
21
+ THE SOFTWARE.
data/README.md ADDED
@@ -0,0 +1,112 @@
1
+ # icalendar-rrule
2
+ This is an add-on to the [iCalendar Gem](https://github.com/icalendar/icalendar).
3
+ It helps to handle calendars in iCalendar format with __repeating events__.
4
+
5
+ According to the [RFC 5545](https://tools.ietf.org/html/rfc5545) specification,
6
+ repeating events are represented by one single entry, the repetitions being shown by
7
+ an attached _repeat rule_. Thus when we iterate through a calendar with, for example,
8
+ a daily repeating event,
9
+ we'll only see one single event where for a month there would be many more events in reality.
10
+
11
+ The _icalendar-rrule gem_ patches an additional function called `scan` into the _iCalendar Gem_.
12
+ The _scan_ shows all events by unrolling the _repeat rule_ for a
13
+ given time period.
14
+
15
+ ## Installation
16
+
17
+ Add this line to your application's Gemfile:
18
+
19
+ ```ruby
20
+ gem 'icalendar-rrule'
21
+ ```
22
+
23
+ and run `bundle install` from your shell.
24
+
25
+ ## Usage
26
+
27
+ For explanations on how to parse and process RFC 5545 compatible calendars, please
28
+ have a look at the [iCalendar gem](http://github.com/icalendar/icalendar).
29
+
30
+ To use this gem we'll first have to require it:
31
+
32
+ `require 'icalendar-rrule'`
33
+
34
+ Further we have to declare the use of the "Scannable" namespace.
35
+ This is called a "[Refinement](https://ruby-doc.org/core-2.5.0/doc/syntax/refinements_rdoc.html)"
36
+ which is a _new Ruby core feature_ since Ruby 2.0.
37
+
38
+ `using Icalendar::Scannable`
39
+
40
+ Now we can inquire a calendar for all events (or tasks) within in a time span.
41
+
42
+ `scan = calendar.scan(begin_time, closing_time)`
43
+
44
+ Here is a simple example:
45
+ ```ruby
46
+ require 'icalendar-rrule' # this will require all needed GEMS including the icalendar gem
47
+
48
+ using Icalendar::Scannable # this will make the function Icalendar::Calendar.scan available
49
+
50
+ # we create a calendar with one single event
51
+ calendar = Icalendar::Calendar.new
52
+ calendar.event do |e|
53
+ # the event starts on January first and lasts from half past eight to five o' clock
54
+ e.dtstart = DateTime.civil(2018, 1, 1, 8, 30)
55
+ e.dtend = DateTime.civil(2018, 1, 1, 17, 00)
56
+ e.summary = 'Working'
57
+ # the event repeats all working days
58
+ e.rrule = 'FREQ=DAILY;BYDAY=MO,TU,WE,TH,FR'
59
+ end
60
+
61
+ begin_time = Date.new(2018, 4, 22)
62
+ closing_time = Date.new(2018, 4, 29)
63
+
64
+ # we are interested in the calendar entries in the last week of April
65
+ scan = calendar.scan(begin_time, closing_time) # that's where the magic happens
66
+
67
+
68
+ scan.each do |occurrence|
69
+ puts "#{occurrence.start_time.strftime('%a. %b. %d. %k:%M')}-#{occurrence.end_time.strftime('%k:%M')}"
70
+ end
71
+ ```
72
+ This will produce:
73
+
74
+ ```
75
+ Mon. Apr. 23. 8:30-17:00
76
+ Tue. Apr. 24. 8:30-17:00
77
+ Wed. Apr. 25. 8:30-17:00
78
+ Thu. Apr. 26. 8:30-17:00
79
+ Fri. Apr. 27. 8:30-17:00
80
+ ```
81
+
82
+ For a more elaborate example, please have a look at
83
+ <https://github.com/free-creations/sk_calendar>
84
+
85
+ ## Used Libraries
86
+
87
+ - [iCalendar Gem](https://github.com/icalendar/icalendar).
88
+ - [Ice cube](https://github.com/seejohnrun/ice_cube)
89
+ - **Active Support:** see also
90
+ [How to Load Core Extensions](http://edgeguides.rubyonrails.org/active_support_core_extensions.html#how-to-load-core-extensions)
91
+
92
+ ## Links
93
+ - [Wikipedia](https://en.wikipedia.org/wiki/ICalendar) article explaining the _iCalendar_ format.
94
+ - [RFC 5545](https://tools.ietf.org/html/rfc5545) Internet
95
+ Calendaring and Scheduling Core Object Specification.
96
+ - The Ruby [iCalendar gem](http://github.com/icalendar/icalendar) is used here as a base for
97
+ handling ical data.
98
+ - [RI_CAL](https://github.com/rubyredrick/ri_cal) is a project similar
99
+ to this one that aims to
100
+ "support important things like enumerating occurrences of repeating events".
101
+ A newer fork is available here: [kdgm/ri_cal](https://github.com/kdgm/ri_cal)
102
+ - [The deceptively complex world of calendar events and RRULEs](https://www.nylas.com/blog/calendar-events-rrules/).
103
+ A Blog of Jennie Lees.
104
+
105
+
106
+ ## Contributing
107
+
108
+ Bug reports and pull requests are welcome on GitHub at https://github.com/free-creations/icalendar-rrule.
109
+
110
+ ## License
111
+
112
+ The gem is available as open source under the terms of the [MIT License](https://opensource.org/licenses/MIT).
data/Rakefile ADDED
@@ -0,0 +1,8 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'bundler/gem_tasks'
4
+ require 'rspec/core/rake_task'
5
+
6
+ RSpec::Core::RakeTask.new(:spec)
7
+
8
+ task default: :spec
@@ -0,0 +1,38 @@
1
+
2
+ # frozen_string_literal: true
3
+
4
+ lib = File.expand_path('lib', __dir__)
5
+ $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
6
+ # note: requiring the version file here, will make this file invisible to 'SimpleCov' the code coverage analysis tool.
7
+ require 'icalendar/rrule/version'
8
+
9
+ Gem::Specification.new do |gem_spec|
10
+ gem_spec.name = 'icalendar-rrule'
11
+ gem_spec.version = Icalendar::Rrule::VERSION
12
+ gem_spec.authors = ['Harald Postner']
13
+ gem_spec.email = ['harald@free-creations.de']
14
+
15
+ gem_spec.summary = 'Use this module if you want to iterate over an ICalendars with recurring events. '
16
+ gem_spec.description = 'This Gem adds a view to ICalendar class which expands ' \
17
+ 'all recurring events.'
18
+ gem_spec.homepage = 'https://github.com/free-creations/icalendar-rrule'
19
+ gem_spec.license = 'MIT'
20
+
21
+ gem_spec.files = `git ls-files -z`.split("\x0").reject do |f|
22
+ f.match(%r{^(test|spec|features)/})
23
+ end
24
+ gem_spec.bindir = 'exe'
25
+ gem_spec.executables = gem_spec.files.grep(%r{^exe/}) { |f| File.basename(f) }
26
+ gem_spec.require_paths = ['lib']
27
+
28
+ gem_spec.add_dependency 'activesupport', '~> 5.1'
29
+ gem_spec.add_dependency 'icalendar', '~> 2.4'
30
+ gem_spec.add_dependency 'ice_cube', '~> 0.16'
31
+
32
+ gem_spec.add_development_dependency 'bundler', '~> 1.16'
33
+ gem_spec.add_development_dependency 'rake', '~> 10.0'
34
+ gem_spec.add_development_dependency 'rspec', '~> 3.7'
35
+ gem_spec.add_development_dependency 'rubocop', '~> 0.55.0'
36
+ gem_spec.add_development_dependency 'rubocop-rspec', '~> 1.24'
37
+ gem_spec.add_development_dependency 'simplecov', '~> 0.16'
38
+ end
@@ -0,0 +1,3 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'icalendar/rrule'
@@ -0,0 +1,8 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'icalendar'
4
+ require 'icalendar/schedulable-component.rb'
5
+ require 'icalendar/scannable-calendar'
6
+
7
+ require 'icalendar/rrule/version'
8
+ require 'icalendar/rrule/occurrence'
@@ -0,0 +1,140 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Icalendar
4
+ module Rrule
5
+ ##
6
+ # An Occurrence represents the point of time where an event or any other component of an iCalendar happens.
7
+ #
8
+ # A component with a _repeat rule_ happens several times.
9
+ # Such a component is represented by a set of many __Occurrence instances__.
10
+ # All these occurrence instances refer to the same component called herein the __base_component__.
11
+ #
12
+ # The base_component can be one of the following:
13
+ #
14
+ # - Icalendar::Event
15
+ # - Icalendar::Todo
16
+ #
17
+ # furthermore it can also be one of the following:
18
+ #
19
+ # - Icalendar::Freebusy
20
+ # - Icalendar::Journal
21
+ #
22
+ # but these are not tested in the current version of this GEM.
23
+ #
24
+ # The Occurrence delegates reading of attributes to its underlying
25
+ # base_component.
26
+ # @example
27
+ # occurrence_of_event.description #=> "Meeting with Mr. X"
28
+ # occurrence_of_event.not_an_attribute #=> Error
29
+ # occurrence_of_event.description = 'new description' #=> Error
30
+ #
31
+ #
32
+ #
33
+ class Occurrence
34
+ include Comparable
35
+ using Icalendar::Schedulable
36
+
37
+ ##
38
+ # @return [Icalendar::Calendar] the calendar this occurrence is taken from.
39
+ attr_reader :base_calendar
40
+ ##
41
+ # @return [Icalendar::Component] the calendar-component (an event or a task) this occurrence refers to.
42
+ attr_reader :base_component
43
+ ##
44
+ # @return [ActiveSupport::TimeWithZone] the start of this occurrence.
45
+ attr_reader :start_time
46
+ ##
47
+ # @return [ActiveSupport::TimeWithZone] the end of this occurrence.
48
+ attr_reader :end_time
49
+ ##
50
+
51
+ ##
52
+ # Create a new Occurrence instance.
53
+ #
54
+ # @param [Icalendar::Calendar] base_calendar the calendar that holds the component.
55
+ # @param [Icalendar::Component] base_component the underlying calendar-component.
56
+ # @param [ActiveSupport::TimeWithZone] start_time the time when this occurrence starts.
57
+ # (might be different to the start time of the base_component)
58
+ # @param [ActiveSupport::TimeWithZone] end_time the time when this occurrence starts.
59
+ # (might be different to the end time of the base_component)
60
+ #
61
+ def initialize(base_calendar, base_component, start_time, end_time)
62
+ raise ArgumentError, "'base_calendar' not of class 'Icalendar::Calendar'" unless
63
+ base_calendar.nil? || base_calendar.is_a?(Icalendar::Calendar)
64
+ raise ArgumentError, "'base_component' not of class 'Icalendar::Component'" unless
65
+ base_component.is_a?(Icalendar::Component)
66
+ raise ArgumentError, "'start_time' not of class 'ActiveSupport::TimeWithZone'" unless
67
+ start_time.is_a?(ActiveSupport::TimeWithZone)
68
+ raise ArgumentError, "'end_time' not of class 'ActiveSupport::TimeWithZone'" unless
69
+ end_time.is_a?(ActiveSupport::TimeWithZone)
70
+
71
+ @base_calendar = base_calendar
72
+ @base_component = base_component
73
+ @start_time = start_time
74
+ @end_time = end_time
75
+
76
+ super()
77
+ end
78
+
79
+ ##
80
+ # Invoked by Ruby when the Occurrence-object is sent a message it cannot handle.
81
+ #
82
+ # Reading of attributes will be delegated all to the _base component_.
83
+ #
84
+ # An attempt to set an attribute will result in an error.
85
+ #
86
+ # @param [String] method_name the symbol for the method called
87
+ # @param [*object] arguments the arguments that were passed to the method.
88
+ # @param [] block the block that was passed to the method.
89
+ def method_missing(method_name, *arguments, &block)
90
+ if method_name.to_s[-1, 1] == '='
91
+ # do not allow for setter methods
92
+ super
93
+ else
94
+ # delegate all other requests to the base component
95
+ base_component.send(method_name, *arguments, &block)
96
+ end
97
+ end
98
+
99
+ ##
100
+ # @return [ActiveSupport::TimeZone] the timezone used in this component
101
+ def time_zone
102
+ base_component.component_timezone
103
+ end
104
+
105
+ ##
106
+ # Returns true if the Occurrence can respond to the given method, that is, the
107
+ # base component responds to the given method.
108
+ #
109
+ # @param [String] method_name the symbol for the method called
110
+ # @param include_private
111
+ def respond_to_missing?(method_name, include_private = false)
112
+ if method_name.to_s[-1, 1] == '='
113
+ # do not allow to set attributes
114
+ super ## throws no method error
115
+ else
116
+ # delegate all read requests to the base component
117
+ base_component.respond_to?(method_name, include_private)
118
+ end
119
+ end
120
+
121
+ ##
122
+ # Compares this occurrence to the other.
123
+ # Comparison is on:
124
+ #
125
+ # 1. @start_time
126
+ # 2. @end_time
127
+ #
128
+ def <=>(other)
129
+ return nil unless other.respond_to? :start_time
130
+ return nil unless other.start_time.is_a?(ActiveSupport::TimeWithZone)
131
+ start_compare = @start_time <=> other.start_time
132
+ return start_compare unless start_compare.zero?
133
+
134
+ return 0 unless other.respond_to? :end_time
135
+ return 0 unless other.end_time.is_a?(ActiveSupport::TimeWithZone)
136
+ @end_time <=> other.end_time
137
+ end
138
+ end
139
+ end
140
+ end
@@ -0,0 +1,7 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Icalendar
4
+ module Rrule
5
+ VERSION = '0.1.5'
6
+ end
7
+ end
@@ -0,0 +1,64 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Icalendar
4
+ ##
5
+ # Refines the Icalendar::Calendar class by adding
6
+ # the `scan` function to this class.
7
+ #
8
+ # @note [Refinement](https://ruby-doc.org/core-2.5.0/doc/syntax/refinements_rdoc.html)
9
+ # is a _Ruby core feature_ since Ruby 2.0
10
+ #
11
+ module Scannable
12
+ ##
13
+ # Provides _mixin_ methods for the
14
+ # Icalendar::Calendar class
15
+ refine Icalendar::Calendar do # rubocop:disable Metrics/BlockLength
16
+ using Icalendar::Schedulable
17
+ ##
18
+ # @param[date_time] begin_time
19
+ # @param[date_time] closing_time
20
+ # @param [Set] component_types a list of components that shall be retrieved these can be
21
+ # - :events
22
+ # - :todos
23
+ # Note: `:journals` and `:freebusys` are currently not tested.
24
+ #
25
+ # @return [Array] all occurrences between begin_time and closing_time
26
+ def scan(begin_time, closing_time, component_types = Set[:events])
27
+ component_types = component_types.to_set
28
+ result = []
29
+ component_types.each do |component_type|
30
+ result += _occurrences_between(_components(component_type), begin_time, closing_time)
31
+ end
32
+ result ||= [] # stop RubyMine to complain about uninitialized result.
33
+ result.sort!
34
+ end
35
+
36
+ private def _components(component_type)
37
+ # note: events(), todos(), journals(), freebusys() are attributes added
38
+ # to Icalendar::Calendar by meta-programming.
39
+ case component_type
40
+ when :events then events
41
+ when :todos then todos
42
+ # :nocov:
43
+ when :journals then journals
44
+ when :freebusys then freebusys
45
+ # :nocov:
46
+ else
47
+ raise ArgumentError, "Unknown Component type: `#{component_type}`."
48
+ end
49
+ end
50
+
51
+ private def _occurrences_between(components, begin_time, closing_time)
52
+ result = []
53
+ components.each do |comp|
54
+ occurrences = comp.schedule.occurrences_between(begin_time, closing_time)
55
+ occurrences.each do |oc|
56
+ new_oc = Icalendar::Rrule::Occurrence.new(self, comp, oc.start_time, oc.end_time)
57
+ result << new_oc
58
+ end
59
+ end
60
+ result
61
+ end
62
+ end
63
+ end
64
+ end
@@ -0,0 +1,437 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'ice_cube'
4
+ require 'active_support/time_with_zone'
5
+
6
+ module Icalendar
7
+ ##
8
+ # Refines the Icalendar::Component class by adding
9
+ # an interface between the IceCube Gem and the the Icalendar::Component-class.
10
+ #
11
+ # __Note:__ _Refinement_ is a Ruby core feature since Ruby 2.0.
12
+ # @see: https://ruby-doc.org/core-2.5.0/doc/syntax/refinements_rdoc.html
13
+ #
14
+ # There are some shortcomings, the
15
+ # [documentation](http://ruby-doc.org/core-2.2.2/doc/syntax/refinements_rdoc.html#label-Indirect+Method+Calls)
16
+ # says:
17
+ #
18
+ # >When using indirect method access such as Kernel#send, Kernel#method or Kernel#respond_to?
19
+ # >refinements are not honored for the caller context during method lookup.
20
+ # >
21
+ # >This behavior may be changed in the future.
22
+ #
23
+ # The purpose of this module is:
24
+ #
25
+ # - normalise the handling of date and time by using ActiveSupport::TimeWithZone everywhere.
26
+ # - provide the methods *start_time* and *end_time* that always return something sensible, no matter
27
+ # how these times were defined in the original component.
28
+ # - provide a schedule for repeating events created by the so called **rrule**.
29
+ #
30
+ #
31
+ module Schedulable
32
+ ##
33
+ # @!method start_time()
34
+ # The time when the event or task shall start.
35
+ # @return [ActiveSupport::TimeWithZone] a valid DateTime object
36
+ #
37
+ #
38
+ # @!method end_time()
39
+ # The time when the event or task shall end.
40
+ # @return [ActiveSupport::TimeWithZone] a valid DateTime object
41
+ #
42
+
43
+ # The start of the Unix Epoch (January 1, 1970 00:00 UTC).
44
+ NULL_TIME = 0
45
+
46
+ # the number of seconds in a minute
47
+ SEC_MIN = 60
48
+ # the number of seconds in an hour
49
+ SEC_HOUR = 60 * SEC_MIN
50
+ # the number of seconds in a day
51
+ SEC_DAY = 24 * SEC_HOUR
52
+ # the number of seconds in a week
53
+ SEC_WEEK = 7 * SEC_DAY
54
+ ##
55
+ # Provides _mixin_ methods for the
56
+ # Icalendar::Component class
57
+ refine Icalendar::Component do # rubocop:disable Metrics/BlockLength
58
+ ##
59
+ # Make sure, that we can always query for a _dtstart_ time.
60
+ # @return [Icalendar::Value, nil] a valid DateTime object or nil.
61
+ # @api private
62
+ private def _dtstart
63
+ dtstart
64
+ rescue StandardError
65
+ nil
66
+ end
67
+ ##
68
+ # Make sure, that we can always query for a _dtend_ time.
69
+ # @return [Icalendar::Value, nil] a valid DateTime object or nil.
70
+ # @api private
71
+ private def _dtend
72
+ dtend
73
+ rescue StandardError
74
+ nil
75
+ end
76
+ ##
77
+ # Make sure, that we can always query for a _due_ date.
78
+ # @return [Icalendar::Value, nil] a valid DateTime object or nil.
79
+ # @api private
80
+ private def _due
81
+ due
82
+ rescue StandardError
83
+ nil
84
+ end
85
+ ##
86
+ # Make sure, that we can always query for a _the duration value.
87
+ # @return [Icalendar::Values::Duration, nil] a valid Duration object or nil.
88
+ # @api private
89
+ def _duration
90
+ duration
91
+ rescue StandardError
92
+ nil
93
+ end
94
+
95
+ ##
96
+ # @return [Integer] the number of seconds this task will last.
97
+ # If no duration for this task is specified, this function returns zero.
98
+ # @api private
99
+ def _duration_seconds # rubocop:disable Metrics/AbcSize
100
+ return _guessed_duration unless _duration
101
+ d = _duration
102
+ return _guessed_duration unless d.is_a?(Icalendar::Values::Duration)
103
+ d.seconds + (d.minutes * SEC_MIN) + (d.hours * SEC_HOUR) + (d.days * SEC_DAY) + (d.weeks * SEC_WEEK)
104
+ end
105
+
106
+ ##
107
+ # Make an educated guess how long this event might last according to the following definition from RFC 5545:
108
+ #
109
+ # > For cases where a "VEVENT" calendar component
110
+ # > specifies a "DTSTART" property with a DATE value type but no
111
+ # > "DTEND" nor "DURATION" property, the event's duration is taken to
112
+ # > be one day.
113
+ #
114
+ # @return [Integer] the number of seconds this task might last.
115
+ # @api private
116
+ def _guessed_duration
117
+ if _dtstart.is_a?(Icalendar::Values::Date) && _dtend.nil? && _duration.nil? && _due.nil?
118
+ SEC_DAY
119
+ else
120
+ 0
121
+ end
122
+ end
123
+
124
+ ##
125
+ # The time when the event or task shall start.
126
+ # @return [ActiveSupport::TimeWithZone] a valid DateTime object
127
+ def start_time
128
+ if _dtstart
129
+ _to_time_with_zone(_dtstart)
130
+ elsif _due
131
+ _to_time_with_zone(_due.to_i - _duration_seconds)
132
+ else
133
+ _to_time_with_zone(NULL_TIME)
134
+ end
135
+ end
136
+
137
+ ##
138
+ # The time when the event or task shall end.
139
+ # @return [ActiveSupport::TimeWithZone] a valid DateTime object
140
+ def end_time # rubocop:disable Metrics/AbcSize
141
+ if _due
142
+ _to_time_with_zone(_due)
143
+ elsif _dtend
144
+ _to_time_with_zone(_dtend)
145
+ elsif _dtstart
146
+ _to_time_with_zone(_dtstart.to_i + _duration_seconds)
147
+ else
148
+ _to_time_with_zone(NULL_TIME + _duration_seconds)
149
+ end
150
+ end
151
+
152
+ ##
153
+ # Heuristic to determine whether the event is scheduled
154
+ # for a date without precising the exact time of day.
155
+ # @return [Boolean] true if the component is scheduled for a date, false otherwise.
156
+ def all_day?
157
+ _dtstart.is_a?(Icalendar::Values::Date) ||
158
+ (start_time == start_time.beginning_of_day && end_time == end_time.beginning_of_day)
159
+ end
160
+
161
+ ##
162
+ # @return [Boolean] true if the duration of the event spans more than one day.
163
+ def multi_day?
164
+ start_time.next_day.beginning_of_day < end_time
165
+ end
166
+
167
+ ##
168
+ # Make sure, that we can always query for a _rrule_ array.
169
+ # @return [array] an array of _ical repeat-rules_ (or an empty array
170
+ # if no repeat-rules are defined for this component).
171
+ # @api private
172
+ def _rrules
173
+ Array(rrule).flatten.map(&:value_ical)
174
+ rescue StandardError
175
+ []
176
+ end
177
+
178
+ ##
179
+ # Make sure, that we can always query for an _exdate_ array.
180
+ # @return [array<ActiveSupport::TimeWithZone>] an array of _ical exdates_ (or an empty array
181
+ # if no repeat-rules are defined for this component).
182
+ # @api private
183
+ def _exdates
184
+ Array(exdate).flatten
185
+ rescue StandardError
186
+ []
187
+ end
188
+
189
+ ##
190
+ # Make sure, that we can always query for the Recurrence ID.
191
+ #
192
+ # From RFC 5545:
193
+ #
194
+ # ```
195
+ # 3.8.4.4. Recurrence ID
196
+ #
197
+ # This property is used in conjunction with the "UID" and
198
+ # "SEQUENCE" properties to identify a specific instance of a
199
+ # recurring "VEVENT", "VTODO", or "VJOURNAL" calendar component.
200
+ #
201
+ # ```
202
+ #
203
+ # @return [ActiveSupport::TimeWithZone] the original value of the "DTSTART" property
204
+ # of the recurrence instance.
205
+ # @api private
206
+ def _recurrence_id
207
+ recurrence_id
208
+ rescue StandardError
209
+ nil
210
+ end
211
+
212
+ ##
213
+ # Is this component a replacement for a certain repeat- occurrence.
214
+ # @return [Boolean] true if this component replaces a repeat- occurrence.
215
+ # @api private
216
+ def _is_substitute?
217
+ !_recurrence_id.nil?
218
+ end
219
+
220
+ ##
221
+ # @return [Array<Icalendar::Component>] the container that holds this component.
222
+ # @api private
223
+ def _parent_set
224
+ return [] unless respond_to?(:parent)
225
+ return [] unless parent.is_a?(Icalendar::Calendar)
226
+
227
+ case self
228
+ when Icalendar::Event then parent.events
229
+ when Icalendar::Todo then parent.todos
230
+ when Icalendar::Journal then parent.journals
231
+ else
232
+ []
233
+ end
234
+ end
235
+
236
+ ##
237
+ # Like the for _exdates, also for these dates do not schedule recurrence items.
238
+ #
239
+ # @return [array<ActiveSupport::TimeWithZone>] an array of dates.
240
+ # @api private
241
+ def _overwritten_dates
242
+ result = []
243
+ _parent_set.each do |event|
244
+ next unless uid == event.uid
245
+ next unless event._is_substitute?
246
+ next if _recurrence_id == event._recurrence_id # do not add myself
247
+ result << event._recurrence_id
248
+ end
249
+ result
250
+ end
251
+
252
+ ##
253
+ # Make sure, that we can always query for an rdate(Recurrence Date) array.
254
+ # @return [array] an array of _ical rdates_ (or an empty array
255
+ # if no repeat-rules are defined for this component).
256
+ # @api private
257
+ def _rdates
258
+ Array(rdate).flatten
259
+ rescue StandardError
260
+ []
261
+ end
262
+
263
+ ##
264
+ # Creates a schedule for this event
265
+ # @return [IceCube::Schedule]
266
+ def schedule # rubocop:disable Metrics/AbcSize, Metrics/MethodLength
267
+ schedule = IceCube::Schedule.new
268
+ schedule.start_time = start_time
269
+ schedule.end_time = end_time
270
+ _rrules.each do |rrule|
271
+ ice_cube_recurrence_rule = IceCube::Rule.from_ical(rrule)
272
+ schedule.add_recurrence_rule(ice_cube_recurrence_rule)
273
+ end
274
+
275
+ _exdates.each do |time|
276
+ schedule.add_exception_time(_to_time_with_zone(time))
277
+ end
278
+
279
+ _overwritten_dates.each do |time|
280
+ schedule.add_exception_time(_to_time_with_zone(time))
281
+ end
282
+
283
+ rdates = _rdates
284
+ rdates.each do |time|
285
+ schedule.add_recurrence_time(_to_time_with_zone(time))
286
+ end
287
+ schedule
288
+ end
289
+
290
+ ##
291
+ # Transform the given object into an object of type `ActiveSupport::TimeWithZone`.
292
+ #
293
+ # Further, try to make sure, that all time-objects of this component are defined in the same timezone.
294
+ #
295
+ # @param [Object] date_time an object that represents a time.
296
+ # @param [ActiveSupport::TimeZone] timezone the timezone to be used. If nil, the timezone will be guessed.
297
+ # @return [ActiveSupport::TimeWithZone] if the given object satisfies all conditions it is returned unchanged.
298
+ # Otherwise the method attempts to "correct" the given Object.
299
+ #
300
+ # rubocop:disable Metrics/MethodLength,Metrics/AbcSize, Metrics/PerceivedComplexity, Metrics/CyclomaticComplexity
301
+ def _to_time_with_zone(date_time, timezone = nil)
302
+ timezone ||= component_timezone
303
+
304
+ # For Icalendar::Values::DateTime, we can extract the ical value. Which probably is already what we want.
305
+ date_time_value = if date_time.is_a?(Icalendar::Values::DateTime)
306
+ date_time.value
307
+ else
308
+ date_time
309
+ end
310
+
311
+ if date_time_value.is_a?(ActiveSupport::TimeWithZone)
312
+ # the class is correct
313
+ # if the timezone is also correct, we'll give back the input object.
314
+ return date_time_value if date_time_value.time_zone == timezone
315
+
316
+ # convert to the requested timezone and return it.
317
+ return date_time_value.in_time_zone(timezone)
318
+
319
+ elsif date_time_value.is_a?(DateTime)
320
+ return date_time_value.in_time_zone(timezone)
321
+
322
+ elsif date_time_value.is_a?(Icalendar::Values::Date)
323
+ return _date_to_time_with_zone(date_time_value, timezone)
324
+
325
+ elsif date_time_value.is_a?(Date)
326
+ return _date_to_time_with_zone(date_time_value, timezone)
327
+
328
+ elsif date_time_value.respond_to?(:to_time)
329
+ return timezone.at(date_time_value.to_time)
330
+
331
+ elsif date_time_value.respond_to?(:to_i)
332
+ # lets interpret the given value as the number of seconds since the Epoch (January 1, 1970 00:00 UTC).
333
+ return timezone.at(date_time_value.to_i)
334
+
335
+ end
336
+ # Oops, the given object is unusable, we'll give back the NULL_DATE
337
+ timezone.at(NULL_TIME)
338
+ end
339
+ # rubocop:enable Metrics/MethodLength,Metrics/AbcSize, Metrics/PerceivedComplexity, Metrics/CyclomaticComplexity
340
+
341
+ ##
342
+ # Convert a date into the corresponding TimeWithZone value.
343
+ # @param [#to_date] date a calendar date.
344
+ # @param [ActiveSupport::TimeZone] timezone the timezone to be used.
345
+ # @return [ActiveSupport::TimeWithZone] mid-night in the given timezone at the given date.
346
+ def _date_to_time_with_zone(date, timezone)
347
+ d = date.to_date
348
+ timezone.local(d.year, d.month, d.day)
349
+ end
350
+
351
+ ##
352
+ # Heuristic to determine the best timezone that shall be used in this component.
353
+ # @return [ActiveSupport::TimeZone] the unique timezone used in this component
354
+ def component_timezone
355
+ # let's try sequentially, the first non-nil wins.
356
+ timezone ||= _extract_timezone(_dtend)
357
+ timezone ||= _extract_timezone(_dtstart)
358
+ timezone ||= _extract_timezone(_due)
359
+ timezone ||= _extract_calendar_timezone
360
+
361
+ # as a last resort we'll use the Coordinated Universal Time (UTC).
362
+ timezone || ActiveSupport::TimeZone['UTC']
363
+ end
364
+
365
+ ##
366
+ # Try to determine this components time zone by inspecting the parents calendar.
367
+ # @return[ActiveSupport::TimeZone, nil] the first valid timezone found in the
368
+ # parent calender or nil if none could be found.
369
+ #
370
+ # rubocop:disable Metrics/CyclomaticComplexity
371
+ def _extract_calendar_timezone
372
+ return nil unless parent
373
+ return nil unless parent.is_a?(Icalendar::Calendar)
374
+ calendar_timezones = parent.timezones
375
+ calendar_timezones.each do |tz|
376
+ break unless tz.valid?(true)
377
+ ugly_tzid = tz.tzid
378
+ break unless ugly_tzid
379
+ tzid = Array(ugly_tzid).first.to_s.gsub(/^(["'])|(["'])$/, '')
380
+ tz_found = ActiveSupport::TimeZone[tzid]
381
+ return tz_found if tz_found
382
+ end
383
+ nil
384
+ rescue StandardError
385
+ nil
386
+ end
387
+ # rubocop:enable Metrics/CyclomaticComplexity
388
+
389
+ ##
390
+ # Get the timezone from the given object trying different methods to find an indication in the object.
391
+ # @param [Object] date_time an object from which we shall determine the time zone.
392
+ # @return [ActiveSupport::TimeZone, nil] the timezone used by the parameter or nil if no timezone has been set.
393
+ # @api private
394
+ def _extract_timezone(date_time)
395
+ timezone ||= _extract_ical_time_zone(date_time) # try with ical parameter
396
+ timezone ||= _extract_act_sup_timezone(date_time) # is the given value already ActiveSupport::TimeWithZone?
397
+ timezone || _extract_value_time_zone(date_time) # is the ical.value of type ActiveSupport::TimeWithZone?
398
+ end
399
+
400
+ ##
401
+ # Get the timezone from the given object, assuming it is an ActiveSupport::TimeWithZone.
402
+ # @param [Object] date_time an object from which we shall determine the time zone.
403
+ # @return [ActiveSupport::TimeZone, nil] the timezone or nil if the operation could not be performed.
404
+ # @api private
405
+ def _extract_act_sup_timezone(date_time)
406
+ return nil unless date_time.is_a?(ActiveSupport::TimeWithZone)
407
+ date_time.time_zone
408
+ end
409
+
410
+ ##
411
+ # Get the timezone from the given object, assuming it can be extracted from `ical_value.value.time_zone`
412
+ # @param [Object] ical_value an object from which we shall determine the time zone.
413
+ # @return [ActiveSupport::TimeZone, nil] the timezone used by the parameter
414
+ # or nil if the operation could not be performed.
415
+ # @api private
416
+ def _extract_value_time_zone(ical_value)
417
+ return nil unless ical_value.is_a?(Icalendar::Value)
418
+ return nil unless ical_value.value.is_a?(ActiveSupport::TimeWithZone)
419
+ ical_value.value.time_zone
420
+ end
421
+
422
+ ##
423
+ # Get the timezone from the given object, assuming it can be extracted from ical params.
424
+ # @param [Icalendar::Value] ical_value an ical value that (probably) supports a time zone identifier.
425
+ # @return [ActiveSupport::TimeZone, nil] the timezone referred to by the ical_value or nil.
426
+ # @api private
427
+ def _extract_ical_time_zone(ical_value)
428
+ return nil unless ical_value.is_a?(Icalendar::Value)
429
+ return nil unless ical_value.respond_to?(:ical_params)
430
+ ugly_tzid = ical_value.ical_params.fetch('tzid', nil)
431
+ return nil if ugly_tzid.nil?
432
+ tzid = Array(ugly_tzid).first.to_s.gsub(/^(["'])|(["'])$/, '')
433
+ ActiveSupport::TimeZone[tzid]
434
+ end
435
+ end
436
+ end
437
+ end
metadata ADDED
@@ -0,0 +1,188 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: icalendar-rrule
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.1.5
5
+ platform: ruby
6
+ authors:
7
+ - Harald Postner
8
+ autorequire:
9
+ bindir: exe
10
+ cert_chain: []
11
+ date: 2018-07-25 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: activesupport
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - "~>"
18
+ - !ruby/object:Gem::Version
19
+ version: '5.1'
20
+ type: :runtime
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - "~>"
25
+ - !ruby/object:Gem::Version
26
+ version: '5.1'
27
+ - !ruby/object:Gem::Dependency
28
+ name: icalendar
29
+ requirement: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - "~>"
32
+ - !ruby/object:Gem::Version
33
+ version: '2.4'
34
+ type: :runtime
35
+ prerelease: false
36
+ version_requirements: !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - "~>"
39
+ - !ruby/object:Gem::Version
40
+ version: '2.4'
41
+ - !ruby/object:Gem::Dependency
42
+ name: ice_cube
43
+ requirement: !ruby/object:Gem::Requirement
44
+ requirements:
45
+ - - "~>"
46
+ - !ruby/object:Gem::Version
47
+ version: '0.16'
48
+ type: :runtime
49
+ prerelease: false
50
+ version_requirements: !ruby/object:Gem::Requirement
51
+ requirements:
52
+ - - "~>"
53
+ - !ruby/object:Gem::Version
54
+ version: '0.16'
55
+ - !ruby/object:Gem::Dependency
56
+ name: bundler
57
+ requirement: !ruby/object:Gem::Requirement
58
+ requirements:
59
+ - - "~>"
60
+ - !ruby/object:Gem::Version
61
+ version: '1.16'
62
+ type: :development
63
+ prerelease: false
64
+ version_requirements: !ruby/object:Gem::Requirement
65
+ requirements:
66
+ - - "~>"
67
+ - !ruby/object:Gem::Version
68
+ version: '1.16'
69
+ - !ruby/object:Gem::Dependency
70
+ name: rake
71
+ requirement: !ruby/object:Gem::Requirement
72
+ requirements:
73
+ - - "~>"
74
+ - !ruby/object:Gem::Version
75
+ version: '10.0'
76
+ type: :development
77
+ prerelease: false
78
+ version_requirements: !ruby/object:Gem::Requirement
79
+ requirements:
80
+ - - "~>"
81
+ - !ruby/object:Gem::Version
82
+ version: '10.0'
83
+ - !ruby/object:Gem::Dependency
84
+ name: rspec
85
+ requirement: !ruby/object:Gem::Requirement
86
+ requirements:
87
+ - - "~>"
88
+ - !ruby/object:Gem::Version
89
+ version: '3.7'
90
+ type: :development
91
+ prerelease: false
92
+ version_requirements: !ruby/object:Gem::Requirement
93
+ requirements:
94
+ - - "~>"
95
+ - !ruby/object:Gem::Version
96
+ version: '3.7'
97
+ - !ruby/object:Gem::Dependency
98
+ name: rubocop
99
+ requirement: !ruby/object:Gem::Requirement
100
+ requirements:
101
+ - - "~>"
102
+ - !ruby/object:Gem::Version
103
+ version: 0.55.0
104
+ type: :development
105
+ prerelease: false
106
+ version_requirements: !ruby/object:Gem::Requirement
107
+ requirements:
108
+ - - "~>"
109
+ - !ruby/object:Gem::Version
110
+ version: 0.55.0
111
+ - !ruby/object:Gem::Dependency
112
+ name: rubocop-rspec
113
+ requirement: !ruby/object:Gem::Requirement
114
+ requirements:
115
+ - - "~>"
116
+ - !ruby/object:Gem::Version
117
+ version: '1.24'
118
+ type: :development
119
+ prerelease: false
120
+ version_requirements: !ruby/object:Gem::Requirement
121
+ requirements:
122
+ - - "~>"
123
+ - !ruby/object:Gem::Version
124
+ version: '1.24'
125
+ - !ruby/object:Gem::Dependency
126
+ name: simplecov
127
+ requirement: !ruby/object:Gem::Requirement
128
+ requirements:
129
+ - - "~>"
130
+ - !ruby/object:Gem::Version
131
+ version: '0.16'
132
+ type: :development
133
+ prerelease: false
134
+ version_requirements: !ruby/object:Gem::Requirement
135
+ requirements:
136
+ - - "~>"
137
+ - !ruby/object:Gem::Version
138
+ version: '0.16'
139
+ description: This Gem adds a view to ICalendar class which expands all recurring events.
140
+ email:
141
+ - harald@free-creations.de
142
+ executables: []
143
+ extensions: []
144
+ extra_rdoc_files: []
145
+ files:
146
+ - ".gitignore"
147
+ - ".rspec"
148
+ - ".rubocop.yml"
149
+ - ".travis.yml"
150
+ - ".yardopts"
151
+ - Gemfile
152
+ - Gemfile.lock
153
+ - LICENSE.txt
154
+ - README.md
155
+ - Rakefile
156
+ - icalendar-rrule.gemspec
157
+ - lib/icalendar-rrule.rb
158
+ - lib/icalendar/rrule.rb
159
+ - lib/icalendar/rrule/occurrence.rb
160
+ - lib/icalendar/rrule/version.rb
161
+ - lib/icalendar/scannable-calendar.rb
162
+ - lib/icalendar/schedulable-component.rb
163
+ homepage: https://github.com/free-creations/icalendar-rrule
164
+ licenses:
165
+ - MIT
166
+ metadata: {}
167
+ post_install_message:
168
+ rdoc_options: []
169
+ require_paths:
170
+ - lib
171
+ required_ruby_version: !ruby/object:Gem::Requirement
172
+ requirements:
173
+ - - ">="
174
+ - !ruby/object:Gem::Version
175
+ version: '0'
176
+ required_rubygems_version: !ruby/object:Gem::Requirement
177
+ requirements:
178
+ - - ">="
179
+ - !ruby/object:Gem::Version
180
+ version: '0'
181
+ requirements: []
182
+ rubyforge_project:
183
+ rubygems_version: 2.7.6
184
+ signing_key:
185
+ specification_version: 4
186
+ summary: Use this module if you want to iterate over an ICalendars with recurring
187
+ events.
188
+ test_files: []