montrose 0.1.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 (50) hide show
  1. checksums.yaml +7 -0
  2. data/.gitignore +11 -0
  3. data/.rubocop.yml +113 -0
  4. data/.travis.yml +5 -0
  5. data/CODE_OF_CONDUCT.md +13 -0
  6. data/Gemfile +13 -0
  7. data/Guardfile +34 -0
  8. data/LICENSE.txt +21 -0
  9. data/README.md +191 -0
  10. data/Rakefile +21 -0
  11. data/bin/_guard-core +16 -0
  12. data/bin/console +7 -0
  13. data/bin/guard +16 -0
  14. data/bin/m +16 -0
  15. data/bin/rake +16 -0
  16. data/bin/rubocop +16 -0
  17. data/bin/setup +7 -0
  18. data/lib/montrose.rb +20 -0
  19. data/lib/montrose/chainable.rb +210 -0
  20. data/lib/montrose/clock.rb +77 -0
  21. data/lib/montrose/errors.rb +5 -0
  22. data/lib/montrose/frequency.rb +63 -0
  23. data/lib/montrose/frequency/daily.rb +9 -0
  24. data/lib/montrose/frequency/hourly.rb +9 -0
  25. data/lib/montrose/frequency/minutely.rb +9 -0
  26. data/lib/montrose/frequency/monthly.rb +9 -0
  27. data/lib/montrose/frequency/weekly.rb +19 -0
  28. data/lib/montrose/frequency/yearly.rb +9 -0
  29. data/lib/montrose/options.rb +293 -0
  30. data/lib/montrose/recurrence.rb +67 -0
  31. data/lib/montrose/rule.rb +47 -0
  32. data/lib/montrose/rule/after.rb +27 -0
  33. data/lib/montrose/rule/before.rb +23 -0
  34. data/lib/montrose/rule/day_of_month.rb +31 -0
  35. data/lib/montrose/rule/day_of_week.rb +23 -0
  36. data/lib/montrose/rule/day_of_year.rb +37 -0
  37. data/lib/montrose/rule/hour_of_day.rb +23 -0
  38. data/lib/montrose/rule/month_of_year.rb +23 -0
  39. data/lib/montrose/rule/nth_day_matcher.rb +32 -0
  40. data/lib/montrose/rule/nth_day_of_month.rb +63 -0
  41. data/lib/montrose/rule/nth_day_of_year.rb +63 -0
  42. data/lib/montrose/rule/time_of_day.rb +33 -0
  43. data/lib/montrose/rule/total.rb +29 -0
  44. data/lib/montrose/rule/week_of_year.rb +23 -0
  45. data/lib/montrose/schedule.rb +42 -0
  46. data/lib/montrose/stack.rb +51 -0
  47. data/lib/montrose/utils.rb +32 -0
  48. data/lib/montrose/version.rb +3 -0
  49. data/montrose.gemspec +32 -0
  50. metadata +192 -0
@@ -0,0 +1,27 @@
1
+ module Montrose
2
+ module Rule
3
+ class After
4
+ include Montrose::Rule
5
+
6
+ def self.apply_options(opts)
7
+ opts[:starts]
8
+ end
9
+
10
+ # Initializes rule
11
+ #
12
+ # @param [Time] start_time - lower bound timestamp
13
+ #
14
+ def initialize(start_time)
15
+ @start_time = start_time
16
+ end
17
+
18
+ def include?(time)
19
+ time >= @start_time
20
+ end
21
+
22
+ def continue?
23
+ false
24
+ end
25
+ end
26
+ end
27
+ end
@@ -0,0 +1,23 @@
1
+ module Montrose
2
+ module Rule
3
+ class Before
4
+ include Montrose::Rule
5
+
6
+ def self.apply_options(opts)
7
+ opts[:until]
8
+ end
9
+
10
+ def initialize(end_time)
11
+ @end_time = end_time
12
+ end
13
+
14
+ def include?(time)
15
+ time < @end_time
16
+ end
17
+
18
+ def continue?
19
+ false
20
+ end
21
+ end
22
+ end
23
+ end
@@ -0,0 +1,31 @@
1
+ module Montrose
2
+ module Rule
3
+ class DayOfMonth
4
+ include Montrose::Rule
5
+
6
+ def self.apply_options(opts)
7
+ opts[:mday]
8
+ end
9
+
10
+ # Initializes rule
11
+ #
12
+ # @param [Array<Fixnum>] days - valid days of month, i.e. [1, 2, -1]
13
+ #
14
+ def initialize(days)
15
+ @days = days
16
+ end
17
+
18
+ def include?(time)
19
+ @days.include?(time.mday) || included_from_end_of_month?(time)
20
+ end
21
+
22
+ private
23
+
24
+ # matches days specified at negative numbers
25
+ def included_from_end_of_month?(time)
26
+ month_days = Time.days_in_month(time.month, time.year) # given by activesupport
27
+ @days.any? { |d| month_days + d + 1 == time.mday }
28
+ end
29
+ end
30
+ end
31
+ end
@@ -0,0 +1,23 @@
1
+ module Montrose
2
+ module Rule
3
+ class DayOfWeek
4
+ include Montrose::Rule
5
+
6
+ def self.apply_options(opts)
7
+ opts[:day]
8
+ end
9
+
10
+ # Initializes rule
11
+ #
12
+ # @param [Array<Fixnum>] days - valid days of week, e.g. [1, 2, 7]
13
+ #
14
+ def initialize(days)
15
+ @days = days
16
+ end
17
+
18
+ def include?(time)
19
+ @days.include?(time.wday)
20
+ end
21
+ end
22
+ end
23
+ end
@@ -0,0 +1,37 @@
1
+ module Montrose
2
+ module Rule
3
+ class DayOfYear
4
+ include Montrose::Rule
5
+
6
+ def self.apply_options(opts)
7
+ opts[:yday]
8
+ end
9
+
10
+ # Initializes rule
11
+ #
12
+ # @param [Array<Fixnum>] days - valid days of year, e.g. [1, 2, -1]
13
+ #
14
+ def initialize(days)
15
+ @days = days
16
+ end
17
+
18
+ def include?(time)
19
+ @days.include?(time.yday) || included_from_end_of_month?(time)
20
+ end
21
+
22
+ private
23
+
24
+ def included_from_end_of_month?(time)
25
+ year_days = days_in_year(time.year) # given by activesupport
26
+ @days.any? { |d| year_days + d + 1 == time.yday }
27
+ end
28
+
29
+ # Returns the number of days in the given year.
30
+ # If no year is specified, it will use the current year.
31
+ # https://github.com/rails/rails/pull/22244
32
+ def days_in_year(year)
33
+ Time.days_in_month(2, year) + 337
34
+ end
35
+ end
36
+ end
37
+ end
@@ -0,0 +1,23 @@
1
+ module Montrose
2
+ module Rule
3
+ class HourOfDay
4
+ include Montrose::Rule
5
+
6
+ def self.apply_options(opts)
7
+ opts[:hour]
8
+ end
9
+
10
+ # Initializes rule
11
+ #
12
+ # @param [Array<Fixnum>] hour - valid hours of days, e.g. [1, 2, 24]
13
+ #
14
+ def initialize(hours)
15
+ @hours = hours
16
+ end
17
+
18
+ def include?(time)
19
+ @hours.include?(time.hour)
20
+ end
21
+ end
22
+ end
23
+ end
@@ -0,0 +1,23 @@
1
+ module Montrose
2
+ module Rule
3
+ class MonthOfYear
4
+ include Montrose::Rule
5
+
6
+ def self.apply_options(opts)
7
+ opts[:month]
8
+ end
9
+
10
+ # Initializes rule
11
+ #
12
+ # @param [Array] months - valid month numbers
13
+ #
14
+ def initialize(months)
15
+ @months = months
16
+ end
17
+
18
+ def include?(time)
19
+ @months.include?(time.month)
20
+ end
21
+ end
22
+ end
23
+ end
@@ -0,0 +1,32 @@
1
+ module Montrose
2
+ module Rule
3
+ class NthDayMatcher
4
+ extend Forwardable
5
+
6
+ def_delegators :@period_day, :nth_day, :first_wday, :total_days
7
+
8
+ def initialize(wday, period_day)
9
+ @wday = wday
10
+ @period_day = period_day
11
+ end
12
+
13
+ def matches?(nth_occ)
14
+ nth_occ == current_occ || (nth_occ < 0 && (total_occ + nth_occ + 1) == current_occ)
15
+ end
16
+
17
+ private
18
+
19
+ def current_occ
20
+ @current_occ ||= (nth_day - first_occ) / 7 + 1
21
+ end
22
+
23
+ def total_occ
24
+ @total_occ ||= ((total_days - first_occ + 1) / 7.0).ceil
25
+ end
26
+
27
+ def first_occ
28
+ @first_occ ||= ((7 - first_wday) + @wday) % 7 + 1
29
+ end
30
+ end
31
+ end
32
+ end
@@ -0,0 +1,63 @@
1
+ require "montrose/rule/nth_day_matcher"
2
+
3
+ module Montrose
4
+ module Rule
5
+ class NthDayOfMonth
6
+ include Montrose::Rule
7
+
8
+ def self.apply_options?(opts)
9
+ opts[:every] == :month && opts[:day].is_a?(Hash)
10
+ end
11
+
12
+ def self.apply_options(opts)
13
+ opts[:day]
14
+ end
15
+
16
+ # Initializes rule
17
+ #
18
+ # @param [Hash] days - valid days of week to month occurrence pairs
19
+ #
20
+ def initialize(days)
21
+ @days = days
22
+ end
23
+
24
+ def include?(time)
25
+ @days.key?(time.wday) && nth_day?(time)
26
+ end
27
+
28
+ private
29
+
30
+ def nth_day?(time)
31
+ expected_occurrences = @days[time.wday]
32
+ nth_day = NthDayMatcher.new(time.wday, MonthDay.new(time))
33
+ expected_occurrences.any? { |n| nth_day.matches?(n) }
34
+ end
35
+
36
+ class MonthDay
37
+ def initialize(time)
38
+ @time = time
39
+ end
40
+
41
+ def nth_day
42
+ @time.mday
43
+ end
44
+
45
+ def first_wday
46
+ @time.beginning_of_month.wday
47
+ end
48
+
49
+ def total_days
50
+ days_in_month(@time)
51
+ end
52
+
53
+ private
54
+
55
+ # Get the days in the month for +time
56
+ def days_in_month(time)
57
+ date = Date.new(time.year, time.month, 1)
58
+ ((date >> 1) - date).to_i
59
+ end
60
+ end
61
+ end
62
+ end
63
+ end
@@ -0,0 +1,63 @@
1
+ require "montrose/rule/nth_day_matcher"
2
+
3
+ module Montrose
4
+ module Rule
5
+ class NthDayOfYear
6
+ include Montrose::Rule
7
+
8
+ def self.apply_options?(opts)
9
+ opts[:every] == :year && opts[:day].is_a?(Hash)
10
+ end
11
+
12
+ def self.apply_options(opts)
13
+ opts[:day]
14
+ end
15
+
16
+ # Initializes rule
17
+ #
18
+ # @param [Hash] days - valid days of week to year occurrence pairs
19
+ #
20
+ def initialize(days)
21
+ @days = days
22
+ end
23
+
24
+ def include?(time)
25
+ @days.key?(time.wday) && nth_day?(time)
26
+ end
27
+
28
+ private
29
+
30
+ def nth_day?(time)
31
+ expected_occurrences = @days[time.wday]
32
+ nth_day = NthDayMatcher.new(time.wday, YearDay.new(time))
33
+ expected_occurrences.any? { |n| nth_day.matches?(n) }
34
+ end
35
+
36
+ class YearDay
37
+ def initialize(time)
38
+ @time = time
39
+ end
40
+
41
+ def nth_day
42
+ @time.yday
43
+ end
44
+
45
+ def first_wday
46
+ @time.beginning_of_year.wday
47
+ end
48
+
49
+ def total_days
50
+ days_in_year(@time)
51
+ end
52
+
53
+ private
54
+
55
+ # Get the days in the month for +time
56
+ def days_in_year(time)
57
+ date = time.to_date
58
+ ((date + 1.year) - date).to_i
59
+ end
60
+ end
61
+ end
62
+ end
63
+ end
@@ -0,0 +1,33 @@
1
+ module Montrose
2
+ module Rule
3
+ class TimeOfDay
4
+ include Montrose::Rule
5
+
6
+ def self.apply_options(opts)
7
+ opts[:at]
8
+ end
9
+
10
+ # Initializes rule
11
+ #
12
+ # @param [Array<Time>] times - valid hours of days
13
+ #
14
+ def initialize(times)
15
+ @times = times
16
+ end
17
+
18
+ def include?(time)
19
+ times_of_day.include?(parts(time))
20
+ end
21
+
22
+ private
23
+
24
+ def parts(time)
25
+ [time.hour, time.min]
26
+ end
27
+
28
+ def times_of_day
29
+ @times_of_day ||= @times.map { |t| parts(t) }
30
+ end
31
+ end
32
+ end
33
+ end
@@ -0,0 +1,29 @@
1
+ module Montrose
2
+ module Rule
3
+ class Total
4
+ include Montrose::Rule
5
+
6
+ def self.apply_options(opts)
7
+ opts[:total]
8
+ end
9
+
10
+ def initialize(max)
11
+ @max = max
12
+ @count = 0
13
+ end
14
+
15
+ def include?(_time)
16
+ continue?
17
+ end
18
+
19
+ def advance!(_time)
20
+ @count += 1
21
+ continue?
22
+ end
23
+
24
+ def continue?
25
+ @count <= @max
26
+ end
27
+ end
28
+ end
29
+ end
@@ -0,0 +1,23 @@
1
+ module Montrose
2
+ module Rule
3
+ class WeekOfYear
4
+ include Montrose::Rule
5
+
6
+ def self.apply_options(opts)
7
+ opts[:week]
8
+ end
9
+
10
+ # Initializes rule
11
+ #
12
+ # @param [Array[Fixnum]] weeks - valid weeks of year
13
+ #
14
+ def initialize(weeks)
15
+ @weeks = weeks
16
+ end
17
+
18
+ def include?(time)
19
+ @weeks.include?(time.to_date.cweek)
20
+ end
21
+ end
22
+ end
23
+ end