montrose 0.11.0 → 0.13.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (48) hide show
  1. checksums.yaml +4 -4
  2. data/.circleci/config.yml +66 -0
  3. data/Appraisals +8 -12
  4. data/CHANGELOG.md +24 -0
  5. data/Guardfile +2 -2
  6. data/README.md +27 -15
  7. data/Rakefile +2 -4
  8. data/bin/setup +1 -0
  9. data/bin/standardrb +29 -0
  10. data/gemfiles/activesupport_5.2.gemfile +5 -1
  11. data/gemfiles/activesupport_6.0.gemfile +5 -1
  12. data/gemfiles/{activesupport_4.2.gemfile → activesupport_6.1.gemfile} +5 -1
  13. data/gemfiles/{activesupport_5.0.gemfile → activesupport_7.0.gemfile} +5 -1
  14. data/lib/montrose/chainable.rb +26 -10
  15. data/lib/montrose/clock.rb +4 -4
  16. data/lib/montrose/day.rb +83 -0
  17. data/lib/montrose/frequency.rb +58 -25
  18. data/lib/montrose/hour.rb +22 -0
  19. data/lib/montrose/ical.rb +128 -0
  20. data/lib/montrose/minute.rb +22 -0
  21. data/lib/montrose/month.rb +47 -0
  22. data/lib/montrose/month_day.rb +25 -0
  23. data/lib/montrose/options.rb +73 -79
  24. data/lib/montrose/recurrence.rb +40 -13
  25. data/lib/montrose/rule/between.rb +1 -1
  26. data/lib/montrose/rule/covering.rb +40 -0
  27. data/lib/montrose/rule/during.rb +7 -15
  28. data/lib/montrose/rule/minute_of_hour.rb +25 -0
  29. data/lib/montrose/rule/nth_day_of_month.rb +0 -2
  30. data/lib/montrose/rule/nth_day_of_year.rb +0 -2
  31. data/lib/montrose/rule/time_of_day.rb +1 -1
  32. data/lib/montrose/rule/until.rb +1 -1
  33. data/lib/montrose/rule.rb +18 -16
  34. data/lib/montrose/schedule.rb +6 -8
  35. data/lib/montrose/stack.rb +3 -4
  36. data/lib/montrose/time_of_day.rb +48 -0
  37. data/lib/montrose/utils.rb +2 -40
  38. data/lib/montrose/version.rb +1 -1
  39. data/lib/montrose/week.rb +20 -0
  40. data/lib/montrose/year_day.rb +25 -0
  41. data/lib/montrose.rb +43 -11
  42. data/montrose.gemspec +17 -17
  43. metadata +43 -36
  44. data/.rubocop.yml +0 -136
  45. data/.travis.yml +0 -33
  46. data/bin/rubocop +0 -16
  47. data/gemfiles/activesupport_4.1.gemfile +0 -12
  48. data/gemfiles/activesupport_5.1.gemfile +0 -12
@@ -2,10 +2,6 @@
2
2
 
3
3
  require "json"
4
4
  require "yaml"
5
- require "montrose/chainable"
6
- require "montrose/errors"
7
- require "montrose/stack"
8
- require "montrose/clock"
9
5
 
10
6
  module Montrose
11
7
  # Represents the rules for a set of recurring events. Can be instantiated
@@ -238,7 +234,7 @@ module Montrose
238
234
  else
239
235
  fail SerializationError,
240
236
  "Object was supposed to be a #{self}, but was a #{obj.class}. -- #{obj.inspect}"
241
- end
237
+ end
242
238
 
243
239
  JSON.dump(hash)
244
240
  end
@@ -250,6 +246,16 @@ module Montrose
250
246
  rescue JSON::ParserError => e
251
247
  fail SerializationError, "Could not parse JSON: #{e}"
252
248
  end
249
+
250
+ alias_method :from_json, :load
251
+
252
+ def from_yaml(yaml)
253
+ new(YAML.safe_load(yaml))
254
+ end
255
+
256
+ def from_ical(ical)
257
+ new(Montrose::ICal.parse(ical))
258
+ end
253
259
  end
254
260
 
255
261
  def initialize(opts = {})
@@ -308,7 +314,7 @@ module Montrose
308
314
  def to_hash
309
315
  default_options.to_hash
310
316
  end
311
- alias to_h to_hash
317
+ alias_method :to_h, :to_hash
312
318
 
313
319
  # Returns json string of options used to create the recurrence
314
320
  #
@@ -331,7 +337,7 @@ module Montrose
331
337
  # @return [String] YAML-formatted recurrence options
332
338
  #
333
339
  def to_yaml(*args)
334
- YAML.dump(JSON.parse(to_json(*args)))
340
+ YAML.dump(as_json(*args))
335
341
  end
336
342
 
337
343
  def inspect
@@ -346,20 +352,41 @@ module Montrose
346
352
  def include?(timestamp)
347
353
  return false if earlier?(timestamp) || later?(timestamp)
348
354
 
349
- recurrence = finite? ? self : starts(timestamp)
355
+ recurrence = finite? ? self : fast_forward(timestamp)
350
356
 
351
357
  recurrence.events.lazy.each do |event|
352
358
  return true if event == timestamp
353
359
  return false if event > timestamp
354
- end or false
360
+ end || false
355
361
  end
356
362
 
357
- # Return true/false if recurrence will iterate infinitely
363
+ def fast_forward(timestamp)
364
+ return starts(timestamp) unless starts_at.present?
365
+
366
+ interval = default_options[:interval]
367
+ frequency = default_options[:every]
368
+ duration = interval.send(frequency)
369
+
370
+ # Calculate nearest earlier time ahead matching frequency * interval
371
+ jump = ((timestamp - starts_at) / duration).floor * duration
372
+
373
+ starts(starts_at + jump)
374
+ end
375
+
376
+ # Return true/false if recurrence will terminate
358
377
  #
359
- # @return [Boolean] whether or not recurrence is infinite
378
+ # @return [Boolean] returns true if recurrence has an end
360
379
  #
361
380
  def finite?
362
- ends_at || length
381
+ !infinite?
382
+ end
383
+
384
+ # Return true/false if recurrence will iterate infinitely
385
+ #
386
+ # @return [Boolean] returns true if recurrence has no end
387
+ #
388
+ def infinite?
389
+ !ends_at && !length
363
390
  end
364
391
 
365
392
  # Return true/false if given timestamp occurs before
@@ -391,7 +418,7 @@ module Montrose
391
418
  loop do
392
419
  stack.advance(clock.tick) do |time|
393
420
  yielder << time
394
- end or break
421
+ end || break
395
422
  end
396
423
  end
397
424
  end
@@ -22,7 +22,7 @@ module Montrose
22
22
  end
23
23
 
24
24
  def continue?(time)
25
- @between.max > time
25
+ time < @between.max
26
26
  end
27
27
  end
28
28
  end
@@ -0,0 +1,40 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Montrose
4
+ module Rule
5
+ class Covering
6
+ include Montrose::Rule
7
+
8
+ def self.apply_options(opts)
9
+ opts[:covering].is_a?(Range) && opts[:covering]
10
+ end
11
+
12
+ # Initializes rule
13
+ #
14
+ # @param [Range] covering - timestamp range
15
+ #
16
+ def initialize(covering)
17
+ @covering = case covering.first
18
+ when Date
19
+ DateRange.new(covering)
20
+ else
21
+ covering
22
+ end
23
+ end
24
+
25
+ def include?(time)
26
+ @covering.include?(time)
27
+ end
28
+
29
+ def continue?(time)
30
+ time < @covering.last
31
+ end
32
+
33
+ class DateRange < SimpleDelegator
34
+ def include?(time)
35
+ __getobj__.include?(time.to_date)
36
+ end
37
+ end
38
+ end
39
+ end
40
+ end
@@ -21,22 +21,10 @@ module Montrose
21
21
  @during.any? { |range| range.include?(time) }
22
22
  end
23
23
 
24
- class TimeOfDay
25
- def initialize(hour, min, sec)
26
- @hour = hour
27
- @min = min
28
- @sec = sec
29
- end
30
-
31
- def seconds_since_midnight
32
- @seconds_since_midnight ||= (@hour * 60 * 60) + (@min * 60) + @sec
33
- end
34
- end
35
-
36
24
  class TimeOfDayRange
37
25
  def initialize(first, last, exclude_end: false)
38
- @first = TimeOfDay.new(*first)
39
- @last = TimeOfDay.new(*last)
26
+ @first = ::Montrose::TimeOfDay.new(first)
27
+ @last = ::Montrose::TimeOfDay.new(last)
40
28
  @exclude_end = exclude_end
41
29
  end
42
30
 
@@ -47,7 +35,11 @@ module Montrose
47
35
  private
48
36
 
49
37
  def range
50
- @range ||= Range.new(@first.seconds_since_midnight, @last.seconds_since_midnight, @exclude_end)
38
+ @range ||= Range.new(
39
+ @first.seconds_since_midnight,
40
+ @last.seconds_since_midnight,
41
+ @exclude_end
42
+ )
51
43
  end
52
44
  end
53
45
  end
@@ -0,0 +1,25 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Montrose
4
+ module Rule
5
+ class MinuteOfHour
6
+ include Montrose::Rule
7
+
8
+ def self.apply_options(opts)
9
+ opts[:minute]
10
+ end
11
+
12
+ # Initializes rule
13
+ #
14
+ # @param minutes [Array<Fixnum>] valid minutes of hour, e.g. [0, 20, 59]
15
+ #
16
+ def initialize(minutes)
17
+ @minutes = minutes
18
+ end
19
+
20
+ def include?(time)
21
+ @minutes.include?(time.min)
22
+ end
23
+ end
24
+ end
25
+ end
@@ -1,7 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require "montrose/rule/nth_day_matcher"
4
-
5
3
  module Montrose
6
4
  module Rule
7
5
  class NthDayOfMonth
@@ -1,7 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require "montrose/rule/nth_day_matcher"
4
-
5
3
  module Montrose
6
4
  module Rule
7
5
  class NthDayOfYear
@@ -24,7 +24,7 @@ module Montrose
24
24
  private
25
25
 
26
26
  def parts(time)
27
- [time.hour, time.min, time.sec]
27
+ ::Montrose::TimeOfDay.to_parts(time)
28
28
  end
29
29
  end
30
30
  end
@@ -8,7 +8,7 @@ module Montrose
8
8
  def self.apply_options(opts)
9
9
  return false unless opts[:until]
10
10
 
11
- { until: opts[:until], exclude_end: opts.fetch(:exclude_end, false) }
11
+ {until: opts[:until], exclude_end: opts.fetch(:exclude_end, false)}
12
12
  end
13
13
 
14
14
  def initialize(opts)
data/lib/montrose/rule.rb CHANGED
@@ -3,6 +3,24 @@
3
3
  module Montrose
4
4
  # Defines the Rule duck type for recurrence rules
5
5
  module Rule
6
+ autoload :After, "montrose/rule/after"
7
+ autoload :Covering, "montrose/rule/covering"
8
+ autoload :DayOfMonth, "montrose/rule/day_of_month"
9
+ autoload :DayOfWeek, "montrose/rule/day_of_week"
10
+ autoload :DayOfYear, "montrose/rule/day_of_year"
11
+ autoload :During, "montrose/rule/during"
12
+ autoload :Except, "montrose/rule/except"
13
+ autoload :HourOfDay, "montrose/rule/hour_of_day"
14
+ autoload :MinuteOfHour, "montrose/rule/minute_of_hour"
15
+ autoload :MonthOfYear, "montrose/rule/month_of_year"
16
+ autoload :NthDayMatcher, "montrose/rule/nth_day_matcher"
17
+ autoload :NthDayOfMonth, "montrose/rule/nth_day_of_month"
18
+ autoload :NthDayOfYear, "montrose/rule/nth_day_of_year"
19
+ autoload :TimeOfDay, "montrose/rule/time_of_day"
20
+ autoload :Total, "montrose/rule/total"
21
+ autoload :Until, "montrose/rule/until"
22
+ autoload :WeekOfYear, "montrose/rule/week_of_year"
23
+
6
24
  def self.included(base)
7
25
  base.extend ClassMethods
8
26
  end
@@ -34,19 +52,3 @@ module Montrose
34
52
  end
35
53
  end
36
54
  end
37
-
38
- require "montrose/rule/after"
39
- require "montrose/rule/between"
40
- require "montrose/rule/day_of_month"
41
- require "montrose/rule/day_of_week"
42
- require "montrose/rule/day_of_year"
43
- require "montrose/rule/during"
44
- require "montrose/rule/except"
45
- require "montrose/rule/hour_of_day"
46
- require "montrose/rule/month_of_year"
47
- require "montrose/rule/nth_day_of_month"
48
- require "montrose/rule/nth_day_of_year"
49
- require "montrose/rule/time_of_day"
50
- require "montrose/rule/total"
51
- require "montrose/rule/until"
52
- require "montrose/rule/week_of_year"
@@ -42,7 +42,7 @@ module Montrose
42
42
  else
43
43
  fail SerializationError,
44
44
  "Object was supposed to be a #{self}, but was a #{obj.class}. -- #{obj.inspect}"
45
- end
45
+ end
46
46
 
47
47
  JSON.dump(array)
48
48
  end
@@ -77,7 +77,7 @@ module Montrose
77
77
 
78
78
  self
79
79
  end
80
- alias add <<
80
+ alias_method :add, :<<
81
81
 
82
82
  # Return true/false if given timestamp is included in any of the rules
83
83
  # found in the schedule
@@ -127,7 +127,7 @@ module Montrose
127
127
  enums = @rules.map { |r| r.merge(opts).events }
128
128
  Enumerator.new do |y|
129
129
  loop do
130
- enum = active_enums(enums).min_by(&:peek) or break
130
+ (enum = active_enums(enums).min_by(&:peek)) || break
131
131
  y << enum.next
132
132
  end
133
133
  end
@@ -173,11 +173,9 @@ module Montrose
173
173
 
174
174
  def active_enums(enums)
175
175
  enums.select do |e|
176
- begin
177
- e.peek
178
- rescue StopIteration
179
- false
180
- end
176
+ e.peek
177
+ rescue StopIteration
178
+ false
181
179
  end
182
180
  end
183
181
  end
@@ -1,7 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require "montrose/rule"
4
-
5
3
  module Montrose
6
4
  # Maintains stack of recurrences rules that apply to
7
5
  # an associated recurrence; manages advancing state
@@ -13,11 +11,12 @@ module Montrose
13
11
  Frequency,
14
12
  Rule::After,
15
13
  Rule::Until,
16
- Rule::Between,
14
+ Rule::Covering,
17
15
  Rule::During,
18
16
  Rule::Except,
19
17
  Rule::Total,
20
18
  Rule::TimeOfDay,
19
+ Rule::MinuteOfHour,
21
20
  Rule::HourOfDay,
22
21
  Rule::NthDayOfMonth,
23
22
  Rule::NthDayOfYear,
@@ -44,7 +43,7 @@ module Montrose
44
43
  yes, no = @stack.partition { |rule| rule.include?(time) }
45
44
 
46
45
  if no.empty?
47
- yes.all? { |rule| rule.advance!(time) } or return false
46
+ yes.all? { |rule| rule.advance!(time) } || (return false)
48
47
  puts time if ENV["DEBUG"]
49
48
  yield time if block_given?
50
49
  true
@@ -0,0 +1,48 @@
1
+ module Montrose
2
+ class TimeOfDay
3
+ include Comparable
4
+
5
+ attr_reader :parts, :hour, :min, :sec
6
+
7
+ def self.parse(arg)
8
+ return new(arg) if arg.is_a?(Array)
9
+
10
+ from_time(::Montrose::Utils.as_time(arg))
11
+ end
12
+
13
+ def self.from_time(time)
14
+ new(to_parts(time))
15
+ end
16
+
17
+ def self.to_parts(time)
18
+ [time.hour, time.min, time.sec]
19
+ end
20
+
21
+ def initialize(parts)
22
+ @parts = parts
23
+ @hour, @min, @sec = *parts
24
+ end
25
+
26
+ def seconds_since_midnight
27
+ @seconds_since_midnight ||= (@hour * 60 * 60) + (@min * 60) + @sec
28
+ end
29
+
30
+ def to_a
31
+ @parts
32
+ end
33
+
34
+ # def inspect
35
+ # "#<Montrose::TimeOfDay #{format_time(@hour)}:#{format_time(@min)}:#{format_time(@sec)}"
36
+ # end
37
+
38
+ def <=>(other)
39
+ to_a <=> other.to_a
40
+ end
41
+
42
+ private
43
+
44
+ def format_time(part)
45
+ format("%02d", part)
46
+ end
47
+ end
48
+ end
@@ -4,10 +4,6 @@ module Montrose
4
4
  module Utils
5
5
  module_function
6
6
 
7
- MONTHS = ::Date::MONTHNAMES
8
-
9
- DAYS = ::Date::DAYNAMES
10
-
11
7
  MAX_HOURS_IN_DAY = 24
12
8
  MAX_DAYS_IN_YEAR = 366
13
9
  MAX_WEEKS_IN_YEAR = 53
@@ -29,7 +25,7 @@ module Montrose
29
25
 
30
26
  # Recurrence at fractions of a second are not recognized
31
27
  def normalize_time(time)
32
- time && time.change(usec: 0)
28
+ time&.change(usec: 0)
33
29
  end
34
30
 
35
31
  def as_date(time)
@@ -44,40 +40,6 @@ module Montrose
44
40
  ::Time.current
45
41
  end
46
42
 
47
- def month_number(name)
48
- case name
49
- when Symbol, String
50
- string = name.to_s
51
- MONTHS.index(string.titleize) || month_number(to_index(string))
52
- when 1..12
53
- name
54
- end
55
- end
56
-
57
- def month_number!(name)
58
- month_numbers = MONTHS.map.with_index { |_n, i| i.to_s }.slice(1, 12)
59
- month_number(name) or fail ConfigurationError,
60
- "Did not recognize month #{name}, must be one of #{(MONTHS + month_numbers).inspect}"
61
- end
62
-
63
- def day_number(name)
64
- case name
65
- when 0..6
66
- name
67
- when Symbol, String
68
- string = name.to_s
69
- DAYS.index(string.titleize) || day_number(to_index(string))
70
- when Array
71
- day_number name.first
72
- end
73
- end
74
-
75
- def day_number!(name)
76
- day_numbers = DAYS.map.with_index { |_n, i| i.to_s }
77
- day_number(name) or fail ConfigurationError,
78
- "Did not recognize day #{name}, must be one of #{(DAYS + day_numbers).inspect}"
79
- end
80
-
81
43
  def days_in_month(month, year = current_time.year)
82
44
  date = ::Date.new(year, month, 1)
83
45
  ((date >> 1) - date).to_i
@@ -93,7 +55,7 @@ module Montrose
93
55
  # Returns string.to_i only if string fully matches an integer
94
56
  # otherwise ensures that return value won't match a valid index
95
57
  def to_index(string)
96
- string =~ %r{^\d+} ? string.to_i : -1
58
+ /^\d+/.match?(string) ? string.to_i : -1
97
59
  end
98
60
  end
99
61
  end
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Montrose
4
- VERSION = "0.11.0"
4
+ VERSION = "0.13.0"
5
5
  end
@@ -0,0 +1,20 @@
1
+ module Montrose
2
+ class Week
3
+ class << self
4
+ NUMBERS = (-53.upto(-1).to_a + 1.upto(53).to_a)
5
+
6
+ def parse(arg)
7
+ return nil unless arg.present?
8
+
9
+ Array(arg).map { |value| assert(value.to_i) }
10
+ end
11
+
12
+ def assert(number)
13
+ test = number.abs
14
+ raise ConfigurationError, "Out of range: #{NUMBERS.inspect} does not include #{test}" unless NUMBERS.include?(number.abs)
15
+
16
+ number
17
+ end
18
+ end
19
+ end
20
+ end
@@ -0,0 +1,25 @@
1
+ module Montrose
2
+ class YearDay
3
+ class << self
4
+ YDAYS = 1.upto(366).to_a
5
+
6
+ def parse(ydays)
7
+ return nil unless ydays.present?
8
+
9
+ case ydays
10
+ when String
11
+ parse(ydays.split(","))
12
+ else
13
+ Array(ydays).map { |d| assert(d.to_i) }
14
+ end
15
+ end
16
+
17
+ def assert(number)
18
+ test = number.abs
19
+ raise ConfigurationError, "Out of range: #{YDAYS.inspect} does not include #{test}" unless YDAYS.include?(number.abs)
20
+
21
+ number
22
+ end
23
+ end
24
+ end
25
+ end
data/lib/montrose.rb CHANGED
@@ -2,22 +2,37 @@
2
2
 
3
3
  require "active_support"
4
4
  require "active_support/core_ext/object"
5
- require "active_support/core_ext/numeric"
5
+
6
6
  require "active_support/core_ext/date"
7
- require "active_support/core_ext/time"
8
7
  require "active_support/core_ext/date_time"
8
+ require "active_support/core_ext/integer"
9
+ require "active_support/core_ext/numeric"
10
+ require "active_support/core_ext/string"
11
+ require "active_support/core_ext/time"
9
12
 
10
- require "montrose/utils"
11
- require "montrose/rule"
12
- require "montrose/clock"
13
- require "montrose/chainable"
14
- require "montrose/recurrence"
15
- require "montrose/frequency"
16
- require "montrose/schedule"
17
- require "montrose/stack"
18
13
  require "montrose/version"
14
+ require "montrose/errors"
19
15
 
20
16
  module Montrose
17
+ autoload :Chainable, "montrose/chainable"
18
+ autoload :Clock, "montrose/clock"
19
+ autoload :Day, "montrose/day"
20
+ autoload :Frequency, "montrose/frequency"
21
+ autoload :Hour, "montrose/hour"
22
+ autoload :ICal, "montrose/ical"
23
+ autoload :Minute, "montrose/minute"
24
+ autoload :Month, "montrose/month"
25
+ autoload :MonthDay, "montrose/month_day"
26
+ autoload :Options, "montrose/options"
27
+ autoload :Recurrence, "montrose/recurrence"
28
+ autoload :Rule, "montrose/rule"
29
+ autoload :TimeOfDay, "montrose/time_of_day"
30
+ autoload :Schedule, "montrose/schedule"
31
+ autoload :Stack, "montrose/stack"
32
+ autoload :Utils, "montrose/utils"
33
+ autoload :Week, "montrose/week"
34
+ autoload :YearDay, "montrose/year_day"
35
+
21
36
  extend Chainable
22
37
 
23
38
  class << self
@@ -35,6 +50,23 @@ module Montrose
35
50
  def recurrence(options = {})
36
51
  branch(options)
37
52
  end
38
- alias r recurrence
53
+ alias_method :r, :recurrence
54
+
55
+ # Create a new recurrence from given options
56
+ # An alias to {Montrose::Recurrence.new}
57
+ attr_reader :enable_deprecated_between_masking
58
+
59
+ def enable_deprecated_between_masking=(value)
60
+ warn "[DEPRECATION] Montrose.enable_deprecated_between_masking is deprecated and will be removed in a future version."
61
+ @enable_deprecated_between_masking = value
62
+ end
63
+
64
+ def enable_deprecated_between_masking?
65
+ result = !!enable_deprecated_between_masking
66
+ if result
67
+ warn "[DEPRECATION] Legacy Montrose.between masking behavior is deprecated. Please use Montrose.covering instead to retain this behavior."
68
+ end
69
+ result
70
+ end
39
71
  end
40
72
  end
data/montrose.gemspec CHANGED
@@ -5,29 +5,29 @@ $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
5
5
  require "montrose/version"
6
6
 
7
7
  Gem::Specification.new do |spec|
8
- spec.name = "montrose"
9
- spec.version = Montrose::VERSION
10
- spec.authors = ["Ross Kaffenberger"]
11
- spec.email = ["rosskaff@gmail.com"]
8
+ spec.name = "montrose"
9
+ spec.version = Montrose::VERSION
10
+ spec.authors = ["Ross Kaffenberger"]
11
+ spec.email = ["rosskaff@gmail.com"]
12
12
 
13
- spec.summary = "Recurring events in Ruby"
14
- spec.description = "A library for specifying, quering, and enumerating recurring events for calendars in Ruby."
15
- spec.homepage = "https://github.com/rossta/montrose"
16
- spec.license = "MIT"
13
+ spec.summary = "Recurring events in Ruby"
14
+ spec.description = "A library for specifying, quering, and enumerating recurring events for calendars in Ruby."
15
+ spec.homepage = "https://github.com/rossta/montrose"
16
+ spec.license = "MIT"
17
17
 
18
- spec.files = `git ls-files -z`.split("\x0").reject { |f| f.match(%r{^(test|spec|features)/}) }
19
- spec.bindir = "exe"
20
- spec.executables = spec.files.grep(%r{^exe/}) { |f| File.basename(f) }
18
+ spec.files = `git ls-files -z`.split("\x0").reject { |f| f.match(%r{^(test|spec|features)/}) }
19
+ spec.bindir = "exe"
20
+ spec.executables = spec.files.grep(%r{^exe/}) { |f| File.basename(f) }
21
21
  spec.require_paths = ["lib"]
22
22
 
23
- spec.required_ruby_version = ">= 2.3.0"
23
+ spec.required_ruby_version = ">= 2.6.0"
24
24
 
25
- spec.add_dependency "activesupport", ">= 4.1", "< 6.0"
25
+ spec.add_dependency "activesupport", ">= 5.2", "< 7.1"
26
26
 
27
- spec.add_development_dependency "appraisal", "~> 2.2.0"
28
- spec.add_development_dependency "m", "~> 1.5"
27
+ spec.add_development_dependency "appraisal"
28
+ spec.add_development_dependency "m"
29
29
  spec.add_development_dependency "minitest"
30
- spec.add_development_dependency "rake", "~> 11.0"
31
- spec.add_development_dependency "rubocop", "~> 0.64.0"
30
+ spec.add_development_dependency "rake", ">= 12.3.3"
31
+ spec.add_development_dependency "standard"
32
32
  spec.add_development_dependency "timecop"
33
33
  end