eternal 0.0.1

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.
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA256:
3
+ metadata.gz: a8a2cd0f6db16d5069aebdc6389b42a17fb14392e9e4d7d2943b04894f6ce48c
4
+ data.tar.gz: faf42fe5c77f76263af0042120f7688b7c42a76baabf33326c09e36af065b6e5
5
+ SHA512:
6
+ metadata.gz: 54e9347e2d34d1ab8ce3a3d5020bf9de4f436196f6ef237eb99684dd6f70521601d5fafcbe6fd4e75d77689318160ae14464a878fc784ddb15e25fb9a196793f
7
+ data.tar.gz: 5b1e4449cf31d12f1bcb63c1740f58ab441a008f035cb5062a5b6fc52ffde5196315351cb750416d8ebdd035a8c85344b79e27e28c874a90000283aed96e1286
@@ -0,0 +1 @@
1
+ service_name: travis-ci
@@ -0,0 +1,52 @@
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
+ .swp
13
+
14
+ # Used by dotenv library to load environment variables.
15
+ # .env
16
+
17
+ ## Specific to RubyMotion:
18
+ .dat*
19
+ .repl_history
20
+ build/
21
+ *.bridgesupport
22
+ build-iPhoneOS/
23
+ build-iPhoneSimulator/
24
+
25
+ ## Specific to RubyMotion (use of CocoaPods):
26
+ #
27
+ # We recommend against adding the Pods directory to your .gitignore. However
28
+ # you should judge for yourself, the pros and cons are mentioned at:
29
+ # https://guides.cocoapods.org/using/using-cocoapods.html#should-i-check-the-pods-directory-into-source-control
30
+ #
31
+ # vendor/Pods/
32
+
33
+ ## Documentation cache and generated files:
34
+ /.yardoc/
35
+ /_yardoc/
36
+ /doc/
37
+ /rdoc/
38
+
39
+ ## Environment normalization:
40
+ /.bundle/
41
+ /vendor/bundle
42
+ /lib/bundler/man/
43
+
44
+ # for a library or gem, you might want to ignore these files since the code is
45
+ # intended to run in multiple environments; otherwise, check them in:
46
+ Gemfile.lock
47
+ .ruby-version
48
+ .ruby-gemset
49
+
50
+ # unless supporting rvm < 1.11.0 or doing something fancy, ignore this:
51
+ .rvmrc
52
+
@@ -0,0 +1,6 @@
1
+ language: ruby
2
+ rvm:
3
+ - 2.0.0
4
+ - 2.2.0
5
+ - 2.4.0
6
+ - ruby-head
data/Gemfile ADDED
@@ -0,0 +1,6 @@
1
+ source "http://rubygems.org"
2
+
3
+ gem 'chronic_duration', :git => 'https://github.com/kritr/chronic_duration.git'
4
+ gem 'chronic', :git => 'https://github.com/davispuh/chronic.git', :branch => 'rewrite'
5
+ gem 'numerizer', :git => 'https://github.com/kritr/numerizer.git', :branch => 'ambiguous-cases'
6
+ gemspec
data/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2018 Krithik Rao
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 all
13
+ 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 THE
21
+ SOFTWARE.
@@ -0,0 +1,14 @@
1
+ # [eternal](https://github.com/kritr/eternal)
2
+
3
+ [![Build Status](https://travis-ci.org/KritR/eternal.svg?branch=master)](https://travis-ci.org/KritR/eternal)
4
+ [![Coverage Status](https://coveralls.io/repos/github/KritR/eternal/badge.svg?branch=master)](https://coveralls.io/github/KritR/eternal?branch=master)
5
+ [![Maintainability](https://api.codeclimate.com/v1/badges/7a13b8b52ff3263a2626/maintainability)](https://codeclimate.com/github/KritR/eternal/maintainability)
6
+
7
+ A parser for event timings and occurrences
8
+
9
+ ## Usage
10
+
11
+ ````Eternal.parse('I have dance practice every friday at 5 pm')````
12
+ will return an ````ice_cube```` object with the rule to repeat weeekly on fridays at 5pm.
13
+
14
+ Additional context can then be inputted into this object to complete the rule.
@@ -0,0 +1,10 @@
1
+ require "bundler/gem_tasks"
2
+ require "rake/testtask"
3
+
4
+ Rake::TestTask.new do |t|
5
+ t.libs << "test"
6
+ t.test_files = FileList['test/**/test_*.rb']
7
+ end
8
+ desc "run tests"
9
+
10
+ task default: :test
@@ -0,0 +1,26 @@
1
+ Gem::Specification.new do |s|
2
+ s.name = 'eternal'
3
+ s.version = '0.0.1'
4
+ s.date = '2018-04-04'
5
+ s.summary = %{Eternal parses events, dates, and everything involved in
6
+ schedules using chronic and chronic duration. These events are returned as
7
+ ice_cube objects which can be converted to ical or yaml, or simply used to
8
+ calculate the next occurrence of the schedule specified.}
9
+ s.description = "A parser for event timings and schedules"
10
+ s.authors = ["Krithik Rao"]
11
+ s.email = 'krdevmail@gmail.com'
12
+ s.files = `git ls-files`.split($/)
13
+ s.homepage = 'https://github.com/KritR/eternal'
14
+ s.license = 'MIT'
15
+
16
+ s.add_dependency "chronic_duration", "~>0.10", ">= 0.10.8"
17
+ s.add_dependency "numerizer", "~>0.2", ">= 0.2.0"
18
+ s.add_dependency "chronic", "~>0.10", ">= 0.10.2"
19
+ s.add_dependency "ice_cube", "~>0.16", ">= 0.16.2"
20
+ #s.add_dependency "textoken", "~> 1.1.2"
21
+
22
+ s.add_development_dependency "rake", "~>12.3", ">= 12.3.1"
23
+ s.add_development_dependency "simplecov", "~>0.14", ">= 0.14.1"
24
+ s.add_development_dependency "coveralls", "~>0.8", ">= 0.8.21"
25
+ s.add_development_dependency "minitest", "~>5.11", ">= 5.11.3"
26
+ end
@@ -0,0 +1,229 @@
1
+ require 'ice_cube'
2
+ require 'chronic'
3
+ require 'chronic_duration'
4
+ require 'patterns'
5
+ require 'time'
6
+
7
+ class Eternal
8
+ def self.parse(str)
9
+ str = str.dup
10
+ cleanup!(str)
11
+ tokens = tokenize(str)
12
+ #delete_unknown!(tokens)
13
+ recognize_keywords!(tokens)
14
+ nested_tokens = nest_keywords(tokens)
15
+ rrule = Evaluator.new(nested_tokens)
16
+ rrule.evaluate
17
+ end
18
+
19
+ def self.cleanup!(str)
20
+ str.gsub!(/\b(?:(last|spann?)(?:ing|s)?(?: for)?|for)(?: a| the)?(?: period| time| duration| span)?(?: of)?\b/i, 'duration')
21
+ str.gsub!(/\b(start|end)(s|ing)?(\sin|\son)?\b/i, '\1')
22
+ str.gsub!(/\b(end)(\sat)\b/i, 'endtime')
23
+ str.gsub!(/\beach\b/i,'every')
24
+ str.gsub!(/\b(start)(\sat)\b/i, 'at')
25
+ str.gsub!(/\b(?:every\s)?(#{DAYS_REGEX})\b/i, 'every 1 week on \1')
26
+ str.gsub!(/\b(?:on\s?)?(#{DAYS_REGEX})s\b/i, 'every 1 week on \1')
27
+ str.gsub!(/\bevery (#{TIME_UNITS_REGEX})\b/i, 'every 1 \1')
28
+ str.gsub!(/\bevery other\b/i, 'every 2')
29
+ str.gsub!(/\b(#{REPEATERS_REGEX})\b/i) { 'every 1 ' + REPEATERS[$1]}
30
+ str.gsub!(/\b(#{TIME_UNITS_REGEX})(?:s)\b/i, '\1')
31
+ # I HAVE NO IDEA IF THIS WORKS | DEFINITELY A HACK
32
+ str.gsub!(/\b(\w*)\sto\b/i) do |match|
33
+ previous_word = $1
34
+ if ($1 =~ /#{TIME_UNITS_REGEX}s?/i) == nil
35
+ match = previous_word + ' ' + 'end'
36
+ else
37
+ match = previous_word + ' ' + 'before'
38
+ end
39
+ match
40
+ end
41
+ str.gsub!(/\b(\w*)\sfrom\b/i) do |match|
42
+ previous_word = $1
43
+ if ($1 =~ /#{TIME_UNITS_REGEX}s?/i) == nil
44
+ match = previous_word + ' ' + 'start'
45
+ else
46
+ match = previous_word + ' ' + 'after'
47
+ end
48
+ match
49
+ end
50
+ end
51
+
52
+ def self.tokenize(str)
53
+ str.gsub!(/\s+/m, ' ')
54
+ str.strip!
55
+ str.split(" ")
56
+ end
57
+
58
+ def self.delete_unknown!(tokens)
59
+ tokens.select! {|token| (token =~ REGEX_ALL) != nil}
60
+ end
61
+
62
+ def self.recognize_keywords!(tokens)
63
+ tokens.map! do |token|
64
+ token = token.to_sym if (token =~ KEYWORDS_REGEX) != nil
65
+ token
66
+ end
67
+ end
68
+
69
+
70
+ def self.nest_keywords(tokens)
71
+ nested = [[]]
72
+ tokens.each do |token|
73
+ if (token.is_a? Symbol)
74
+ nested << []
75
+ end
76
+ nested.last << token
77
+ end
78
+ nested
79
+ end
80
+
81
+ end
82
+
83
+ class Evaluator
84
+
85
+ REPEAT_SPANS = {
86
+ :year => :yearly,
87
+ :week => :weekly,
88
+ :day => :daily,
89
+ :hour => :hourly,
90
+ :minute => :minutely,
91
+ :second => :secondly,
92
+ }
93
+ EVAL_ORDER = [:duration, :at_time, :every, :at_date, :on, :end_time, :end_date]
94
+ def initialize(rule_list)
95
+ @rule_list = rule_list
96
+ @schedule = IceCube::Schedule.new
97
+ @rules = []
98
+ @options = []
99
+ end
100
+
101
+ def evaluate
102
+ p @rule_list
103
+ @rule_list.each do |rule|
104
+ operation = rule.shift
105
+ p operation
106
+ case operation
107
+ when :every
108
+ amount = rule[0]
109
+ duration = rule[1]
110
+ @options << {every: {duration: REPEAT_SPANS[duration.to_sym], amount: amount}}
111
+ when :at
112
+ time = Chronic.parse(rule.reduce(:concat))
113
+ @options << {at_time: time}
114
+ when :on
115
+ if rule.all? {|x| (x =~ /#{DAYS_REGEX}|and/i) != nil}
116
+ rule.select! {|x| (x =~ /#{DAYS_REGEX}/i) != nil}
117
+ rule.map!(&:to_sym)
118
+ @options << {on: rule}
119
+ else
120
+ days = Chronic.parse(rule.reduce(:concat))
121
+ @options << {at_date: days}
122
+ end
123
+ when :start
124
+ time = rule.reduce(:concat)
125
+ if ChronicDuration.parse(time) < 24 * 60 * 60
126
+ @options << {at_time: Chronic.parse(time, :guess => :begin)}
127
+ else
128
+ @options << {at_date: Chronic.parse(time, :guess => :begin)}
129
+ end
130
+ when :endtime
131
+ time = rule.reduce(:concat)
132
+ @options << {end_time: time}
133
+ when :end
134
+ time = rule.reduce(:concat)
135
+ @options << {end_date: time}
136
+ when :duration
137
+ time = rule.reduce(:concat)
138
+ @options << {duration: ChronicDuration.parse(time)}
139
+ #when :until
140
+ end
141
+ end
142
+ p @options
143
+ option = @options.reduce(:merge)
144
+ sorted = option.sort_by { |a|EVAL_ORDER.index(a) }
145
+ sorted.each {|opt| operation = opt[0]; args = opt[1]; self.send(operation, args) }
146
+ @rules.each {|rule| @schedule.add_recurrence_rule(rule) }
147
+ @schedule
148
+ end
149
+
150
+ def every(opts)
151
+ p opts
152
+ @rules << IceCube::Rule.send(opts[:duration], opts[:amount])
153
+ end
154
+
155
+ def on(day)
156
+ if(@rules.last)
157
+ @rules.last.day(day)
158
+ else
159
+ at_date(Chronic.parse(day.last.to_s))
160
+ end
161
+ end
162
+
163
+ def at_time(time)
164
+ new_time = time
165
+ old_time = @schedule.start_time
166
+ yr = new_time.year
167
+ mon = new_time.mon
168
+ day = new_time.day
169
+ hr = new_time.hour
170
+ min = new_time.min
171
+ sec = new_time.sec
172
+ @schedule.start_time = Time.new(yr, mon, day, hr, min, sec)
173
+ end
174
+ def at_date(time)
175
+ new_time = time
176
+ old_time = @schedule.start_time
177
+ yr = new_time.year
178
+ mon = new_time.mon
179
+ day = new_time.day
180
+ hr = old_time.hour
181
+ min = old_time.min
182
+ sec = old_time.sec
183
+ @schedule.start_time = Time.new(yr, mon, day, hr, min, sec)
184
+ end
185
+
186
+ # needs to be deferred till existence of rule
187
+ def until(string)
188
+ @rules.last.until(Chronic.parse(string, :guess => :begin))
189
+ end
190
+
191
+ def in(span)
192
+ @rules.last.month_of_year(span) if MONTHS.values.include? span
193
+ end
194
+
195
+ def end_time(string)
196
+ now = @schedule.start_time || Time.now
197
+ new_time = Chronic.parse(string, :now => :now, :guess => :begin)
198
+ old_time = @schedule.start_time
199
+ yr = new_time.year
200
+ mon = new_time.mon
201
+ day = new_time.day
202
+ hr = new_time.hour
203
+ min = new_time.min
204
+ sec = new_time.sec
205
+ @schedule.end_time = Time.new(yr, mon, day, hr, min, sec)
206
+ end
207
+
208
+ def end_date(string)
209
+ now = @schedule.start_time || Time.now
210
+ new_time = Chronic.parse(string, :now => :now, :guess => :begin)
211
+ old_time = @schedule.start_time
212
+ yr = new_time.year
213
+ mon = new_time.mon
214
+ day = new_time.day
215
+ hr = old_time.hour
216
+ min = old_time.min
217
+ sec = old_time.sec
218
+ @schedule.end_time = Time.new(yr, mon, day, hr, min, sec)
219
+ end
220
+
221
+ def duration(time)
222
+ @schedule.duration = time
223
+ end
224
+
225
+ def except(time)
226
+
227
+ end
228
+ end
229
+
@@ -0,0 +1,35 @@
1
+ DAYS = ['saturday', 'monday', 'tuesday', 'wednesday', 'thursday', 'friday', 'sunday']
2
+ ABBREVIATED_DAYS = ['sat', 'mon', 'tu(e(s)?)?', 'wed', 'th(u(r(s)?)?)?', 'fri', 'sun']
3
+ ALL_DAYS = DAYS + ABBREVIATED_DAYS
4
+ MONTHS = ['january', 'february', 'march', 'april', 'may', 'june', 'july', 'august', 'september', 'october', 'november', 'december']
5
+ ABBREVIATED_MONTHS = MONTHS.map { |x| x[0..2] }
6
+ TIME_UNITS = ['second', 'hour', 'minute', 'week', 'month', 'year', 'decade', 'century']
7
+ ABBREVIATED_TIME_UNITS = ['sec', 'hr', 'min', 'wk', 'mo', 'yr', 'dec', 'cent']
8
+ ALL_TIME_UNITS = TIME_UNITS + ABBREVIATED_TIME_UNITS
9
+ SEASONS = ['summer','spring','autumn|fall', 'winter']
10
+ PREP_PHRASES = ['on','in','at', 'from', 'to', 'duration']
11
+ DETERMINERS = ['each','every']
12
+ REPEATERS = {'minutely' => 'minute', 'hourly' => 'hour' ,'daily' => 'day', 'weekly' => 'week', 'monthly' => 'month', 'yearly' => 'year'}
13
+ KEYWORDS = ['every', 'at', 'on', 'start', 'end', 'duration', 'until', 'endtime'] # 'except', 'and',]
14
+
15
+
16
+ # Combines array options into single regex
17
+ def regexify_strings(array)
18
+ Regexp.union(array)
19
+ end
20
+
21
+ DAYS_REGEX = regexify_strings(DAYS)
22
+ ABBREVIATED_DAYS_REGEX = regexify_strings(ABBREVIATED_DAYS)
23
+ ALL_DAY_REGEX = regexify_strings(ALL_DAYS)
24
+ MONTHS_REGEX = regexify_strings(MONTHS)
25
+ ABBREVIATED_MONTHS_REGEX = regexify_strings(ABBREVIATED_MONTHS)
26
+ TIME_UNITS_REGEX = regexify_strings(TIME_UNITS)
27
+ ABBREVIATED_TIME_UNITS_REGEX = regexify_strings(ABBREVIATED_TIME_UNITS)
28
+ ALL_TIME_UNITS_REGEX = regexify_strings(ALL_TIME_UNITS)
29
+ ORDINAL_REGEX = /(\d+)(st|nd|rd|th)/i
30
+ SEASONS_REGEX = regexify_strings(SEASONS)
31
+ PREP_PHRASES_REGEX = regexify_strings(PREP_PHRASES)
32
+ DETERMINERS_REGEX = regexify_strings(DETERMINERS)
33
+ REPEATERS_REGEX = regexify_strings(REPEATERS.keys)
34
+ KEYWORDS_REGEX = regexify_strings(KEYWORDS)
35
+
@@ -0,0 +1,12 @@
1
+ require 'test_helper'
2
+ require 'ice_cube'
3
+
4
+ describe 'Eternal' do
5
+ it 'can parse repeats occuring weekly' do
6
+ schedule = IceCube::Schedule.new() do |s|
7
+ s.add_recurrence_rule(IceCube::Rule.weekly.day(:sunday))
8
+ end
9
+ eternal_output = Eternal.parse('every sunday')
10
+ value(eternal_output).must_equal schedule
11
+ end
12
+ end
@@ -0,0 +1,7 @@
1
+ require 'coveralls'
2
+ Coveralls.wear!
3
+ require 'simplecov'
4
+ SimpleCov.start
5
+ require 'minitest/autorun'
6
+ require 'minitest/spec'
7
+ require 'eternal'
metadata ADDED
@@ -0,0 +1,218 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: eternal
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.0.1
5
+ platform: ruby
6
+ authors:
7
+ - Krithik Rao
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+ date: 2018-04-04 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: chronic_duration
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - "~>"
18
+ - !ruby/object:Gem::Version
19
+ version: '0.10'
20
+ - - ">="
21
+ - !ruby/object:Gem::Version
22
+ version: 0.10.8
23
+ type: :runtime
24
+ prerelease: false
25
+ version_requirements: !ruby/object:Gem::Requirement
26
+ requirements:
27
+ - - "~>"
28
+ - !ruby/object:Gem::Version
29
+ version: '0.10'
30
+ - - ">="
31
+ - !ruby/object:Gem::Version
32
+ version: 0.10.8
33
+ - !ruby/object:Gem::Dependency
34
+ name: numerizer
35
+ requirement: !ruby/object:Gem::Requirement
36
+ requirements:
37
+ - - "~>"
38
+ - !ruby/object:Gem::Version
39
+ version: '0.2'
40
+ - - ">="
41
+ - !ruby/object:Gem::Version
42
+ version: 0.2.0
43
+ type: :runtime
44
+ prerelease: false
45
+ version_requirements: !ruby/object:Gem::Requirement
46
+ requirements:
47
+ - - "~>"
48
+ - !ruby/object:Gem::Version
49
+ version: '0.2'
50
+ - - ">="
51
+ - !ruby/object:Gem::Version
52
+ version: 0.2.0
53
+ - !ruby/object:Gem::Dependency
54
+ name: chronic
55
+ requirement: !ruby/object:Gem::Requirement
56
+ requirements:
57
+ - - "~>"
58
+ - !ruby/object:Gem::Version
59
+ version: '0.10'
60
+ - - ">="
61
+ - !ruby/object:Gem::Version
62
+ version: 0.10.2
63
+ type: :runtime
64
+ prerelease: false
65
+ version_requirements: !ruby/object:Gem::Requirement
66
+ requirements:
67
+ - - "~>"
68
+ - !ruby/object:Gem::Version
69
+ version: '0.10'
70
+ - - ">="
71
+ - !ruby/object:Gem::Version
72
+ version: 0.10.2
73
+ - !ruby/object:Gem::Dependency
74
+ name: ice_cube
75
+ requirement: !ruby/object:Gem::Requirement
76
+ requirements:
77
+ - - "~>"
78
+ - !ruby/object:Gem::Version
79
+ version: '0.16'
80
+ - - ">="
81
+ - !ruby/object:Gem::Version
82
+ version: 0.16.2
83
+ type: :runtime
84
+ prerelease: false
85
+ version_requirements: !ruby/object:Gem::Requirement
86
+ requirements:
87
+ - - "~>"
88
+ - !ruby/object:Gem::Version
89
+ version: '0.16'
90
+ - - ">="
91
+ - !ruby/object:Gem::Version
92
+ version: 0.16.2
93
+ - !ruby/object:Gem::Dependency
94
+ name: rake
95
+ requirement: !ruby/object:Gem::Requirement
96
+ requirements:
97
+ - - "~>"
98
+ - !ruby/object:Gem::Version
99
+ version: '12.3'
100
+ - - ">="
101
+ - !ruby/object:Gem::Version
102
+ version: 12.3.1
103
+ type: :development
104
+ prerelease: false
105
+ version_requirements: !ruby/object:Gem::Requirement
106
+ requirements:
107
+ - - "~>"
108
+ - !ruby/object:Gem::Version
109
+ version: '12.3'
110
+ - - ">="
111
+ - !ruby/object:Gem::Version
112
+ version: 12.3.1
113
+ - !ruby/object:Gem::Dependency
114
+ name: simplecov
115
+ requirement: !ruby/object:Gem::Requirement
116
+ requirements:
117
+ - - "~>"
118
+ - !ruby/object:Gem::Version
119
+ version: '0.14'
120
+ - - ">="
121
+ - !ruby/object:Gem::Version
122
+ version: 0.14.1
123
+ type: :development
124
+ prerelease: false
125
+ version_requirements: !ruby/object:Gem::Requirement
126
+ requirements:
127
+ - - "~>"
128
+ - !ruby/object:Gem::Version
129
+ version: '0.14'
130
+ - - ">="
131
+ - !ruby/object:Gem::Version
132
+ version: 0.14.1
133
+ - !ruby/object:Gem::Dependency
134
+ name: coveralls
135
+ requirement: !ruby/object:Gem::Requirement
136
+ requirements:
137
+ - - "~>"
138
+ - !ruby/object:Gem::Version
139
+ version: '0.8'
140
+ - - ">="
141
+ - !ruby/object:Gem::Version
142
+ version: 0.8.21
143
+ type: :development
144
+ prerelease: false
145
+ version_requirements: !ruby/object:Gem::Requirement
146
+ requirements:
147
+ - - "~>"
148
+ - !ruby/object:Gem::Version
149
+ version: '0.8'
150
+ - - ">="
151
+ - !ruby/object:Gem::Version
152
+ version: 0.8.21
153
+ - !ruby/object:Gem::Dependency
154
+ name: minitest
155
+ requirement: !ruby/object:Gem::Requirement
156
+ requirements:
157
+ - - "~>"
158
+ - !ruby/object:Gem::Version
159
+ version: '5.11'
160
+ - - ">="
161
+ - !ruby/object:Gem::Version
162
+ version: 5.11.3
163
+ type: :development
164
+ prerelease: false
165
+ version_requirements: !ruby/object:Gem::Requirement
166
+ requirements:
167
+ - - "~>"
168
+ - !ruby/object:Gem::Version
169
+ version: '5.11'
170
+ - - ">="
171
+ - !ruby/object:Gem::Version
172
+ version: 5.11.3
173
+ description: A parser for event timings and schedules
174
+ email: krdevmail@gmail.com
175
+ executables: []
176
+ extensions: []
177
+ extra_rdoc_files: []
178
+ files:
179
+ - ".coveralls.yml"
180
+ - ".gitignore"
181
+ - ".travis.yml"
182
+ - Gemfile
183
+ - LICENSE
184
+ - README.md
185
+ - Rakefile
186
+ - eternal.gemspec
187
+ - lib/eternal.rb
188
+ - lib/patterns.rb
189
+ - test/test_eternal.rb
190
+ - test/test_helper.rb
191
+ homepage: https://github.com/KritR/eternal
192
+ licenses:
193
+ - MIT
194
+ metadata: {}
195
+ post_install_message:
196
+ rdoc_options: []
197
+ require_paths:
198
+ - lib
199
+ required_ruby_version: !ruby/object:Gem::Requirement
200
+ requirements:
201
+ - - ">="
202
+ - !ruby/object:Gem::Version
203
+ version: '0'
204
+ required_rubygems_version: !ruby/object:Gem::Requirement
205
+ requirements:
206
+ - - ">="
207
+ - !ruby/object:Gem::Version
208
+ version: '0'
209
+ requirements: []
210
+ rubyforge_project:
211
+ rubygems_version: 2.7.3
212
+ signing_key:
213
+ specification_version: 4
214
+ summary: Eternal parses events, dates, and everything involved in schedules using
215
+ chronic and chronic duration. These events are returned as ice_cube objects which
216
+ can be converted to ical or yaml, or simply used to calculate the next occurrence
217
+ of the schedule specified.
218
+ test_files: []