icalendar-rrule 0.1.5

Sign up to get free protection for your applications and to get access to all the features.
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: []