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,42 @@
1
+ module Montrose
2
+ class Schedule
3
+ attr_accessor :rules
4
+
5
+ def initialize
6
+ @rules = []
7
+ end
8
+
9
+ def <<(rule)
10
+ @rules << Montrose::Recurrence.new(rule)
11
+
12
+ self
13
+ end
14
+ alias add <<
15
+
16
+ def include?(time)
17
+ @rules.any? { |r| r.include?(time) }
18
+ end
19
+
20
+ def events(opts = {})
21
+ enums = @rules.map { |r| r.merge(opts).events }
22
+ Enumerator.new do |y|
23
+ loop do
24
+ enums = active_enums(enums)
25
+ enum = enums.min_by(&:peek) or break
26
+ y << enum.next
27
+ end
28
+ end
29
+ end
30
+
31
+ private
32
+
33
+ def active_enums(enums)
34
+ enums.each_with_object([]) do |enum, actives|
35
+ begin
36
+ actives << enum if enum.peek
37
+ rescue StopIteration
38
+ end
39
+ end
40
+ end
41
+ end
42
+ end
@@ -0,0 +1,51 @@
1
+ require "montrose/rule"
2
+
3
+ module Montrose
4
+ # Maintains stack of recurrences rules that apply to
5
+ # an associated recurrence; manages advancing state
6
+ # on each rule in stack as time instances are iterated.
7
+ #
8
+ class Stack
9
+ def self.build(opts = {})
10
+ [
11
+ Frequency,
12
+ Rule::After,
13
+ Rule::Before,
14
+ Rule::Total,
15
+ Rule::TimeOfDay,
16
+ Rule::HourOfDay,
17
+ Rule::NthDayOfMonth,
18
+ Rule::NthDayOfYear,
19
+ Rule::DayOfWeek,
20
+ Rule::DayOfMonth,
21
+ Rule::DayOfYear,
22
+ Rule::WeekOfYear,
23
+ Rule::MonthOfYear
24
+ ].map { |r| r.from_options(opts) }.compact
25
+ end
26
+
27
+ def initialize(opts = {})
28
+ @stack = self.class.build(opts)
29
+ end
30
+
31
+ # Given a time instance, advances state of when all
32
+ # recurrence rules on the stack match, and yielding
33
+ # time to the block, otherwise, invokes break? on
34
+ # non-matching rules.
35
+ #
36
+ # @param [Time] time - time instance candidate for recurrence
37
+ #
38
+ def advance(time)
39
+ yes, no = @stack.partition { |rule| rule.include?(time) }
40
+
41
+ if no.empty?
42
+ yes.all? { |rule| rule.advance!(time) } or return false
43
+ puts time if ENV["DEBUG"]
44
+ yield time if block_given?
45
+ true
46
+ else
47
+ no.any?(&:continue?)
48
+ end
49
+ end
50
+ end
51
+ end
@@ -0,0 +1,32 @@
1
+ module Montrose
2
+ module Utils
3
+ module_function
4
+
5
+ MONTHS = Date::MONTHNAMES
6
+ DAYS = Date::DAYNAMES
7
+
8
+ def month_number(name)
9
+ index = case name
10
+ when Symbol, String
11
+ MONTHS.index(name.to_s.titleize)
12
+ when 1..12
13
+ name
14
+ end
15
+
16
+ index or fail ConfigurationError, "Did not recognize month #{name}, must be one of #{MONTHS.inspect}"
17
+ end
18
+
19
+ def day_number(name)
20
+ index = case name
21
+ when 0..6
22
+ name
23
+ when Symbol, String
24
+ DAYS.index(name.to_s.titleize)
25
+ when Array
26
+ day_number name.first
27
+ end
28
+
29
+ index or fail ConfigurationError, "Did not recognize day #{name}, must be one of #{DAYS.inspect}"
30
+ end
31
+ end
32
+ end
@@ -0,0 +1,3 @@
1
+ module Montrose
2
+ VERSION = "0.1.0".freeze
3
+ end
@@ -0,0 +1,32 @@
1
+ # coding: utf-8
2
+ lib = File.expand_path("../lib", __FILE__)
3
+ $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
4
+ require "montrose/version"
5
+
6
+ Gem::Specification.new do |spec|
7
+ spec.name = "montrose"
8
+ spec.version = Montrose::VERSION
9
+ spec.authors = ["Ross Kaffenberger"]
10
+ spec.email = ["rosskaff@gmail.com"]
11
+
12
+ spec.summary = "Recurring events in Ruby"
13
+ spec.description = "A library for specifying, quering, and enumerating recurring events for calendars in Ruby."
14
+ spec.homepage = "https://github.com/rossta/montrose"
15
+ spec.license = "MIT"
16
+
17
+ spec.files = `git ls-files -z`.split("\x0").reject { |f| f.match(%r{^(test|spec|features)/}) }
18
+ spec.bindir = "exe"
19
+ spec.executables = spec.files.grep(%r{^exe/}) { |f| File.basename(f) }
20
+ spec.require_paths = ["lib"]
21
+
22
+ spec.required_ruby_version = ">= 2.1.0"
23
+
24
+ spec.add_dependency "activesupport"
25
+
26
+ spec.add_development_dependency "bundler", "~> 1.10"
27
+ spec.add_development_dependency "rake", "~> 10.0"
28
+ spec.add_development_dependency "minitest", "~> 5.8"
29
+ spec.add_development_dependency "m", "~> 1.4"
30
+ spec.add_development_dependency "rubocop", "~> 0.36"
31
+ spec.add_development_dependency "timecop"
32
+ end
metadata ADDED
@@ -0,0 +1,192 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: montrose
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.1.0
5
+ platform: ruby
6
+ authors:
7
+ - Ross Kaffenberger
8
+ autorequire:
9
+ bindir: exe
10
+ cert_chain: []
11
+ date: 2016-01-18 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: '0'
20
+ type: :runtime
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - ">="
25
+ - !ruby/object:Gem::Version
26
+ version: '0'
27
+ - !ruby/object:Gem::Dependency
28
+ name: bundler
29
+ requirement: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - "~>"
32
+ - !ruby/object:Gem::Version
33
+ version: '1.10'
34
+ type: :development
35
+ prerelease: false
36
+ version_requirements: !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - "~>"
39
+ - !ruby/object:Gem::Version
40
+ version: '1.10'
41
+ - !ruby/object:Gem::Dependency
42
+ name: rake
43
+ requirement: !ruby/object:Gem::Requirement
44
+ requirements:
45
+ - - "~>"
46
+ - !ruby/object:Gem::Version
47
+ version: '10.0'
48
+ type: :development
49
+ prerelease: false
50
+ version_requirements: !ruby/object:Gem::Requirement
51
+ requirements:
52
+ - - "~>"
53
+ - !ruby/object:Gem::Version
54
+ version: '10.0'
55
+ - !ruby/object:Gem::Dependency
56
+ name: minitest
57
+ requirement: !ruby/object:Gem::Requirement
58
+ requirements:
59
+ - - "~>"
60
+ - !ruby/object:Gem::Version
61
+ version: '5.8'
62
+ type: :development
63
+ prerelease: false
64
+ version_requirements: !ruby/object:Gem::Requirement
65
+ requirements:
66
+ - - "~>"
67
+ - !ruby/object:Gem::Version
68
+ version: '5.8'
69
+ - !ruby/object:Gem::Dependency
70
+ name: m
71
+ requirement: !ruby/object:Gem::Requirement
72
+ requirements:
73
+ - - "~>"
74
+ - !ruby/object:Gem::Version
75
+ version: '1.4'
76
+ type: :development
77
+ prerelease: false
78
+ version_requirements: !ruby/object:Gem::Requirement
79
+ requirements:
80
+ - - "~>"
81
+ - !ruby/object:Gem::Version
82
+ version: '1.4'
83
+ - !ruby/object:Gem::Dependency
84
+ name: rubocop
85
+ requirement: !ruby/object:Gem::Requirement
86
+ requirements:
87
+ - - "~>"
88
+ - !ruby/object:Gem::Version
89
+ version: '0.36'
90
+ type: :development
91
+ prerelease: false
92
+ version_requirements: !ruby/object:Gem::Requirement
93
+ requirements:
94
+ - - "~>"
95
+ - !ruby/object:Gem::Version
96
+ version: '0.36'
97
+ - !ruby/object:Gem::Dependency
98
+ name: timecop
99
+ requirement: !ruby/object:Gem::Requirement
100
+ requirements:
101
+ - - ">="
102
+ - !ruby/object:Gem::Version
103
+ version: '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'
111
+ description: A library for specifying, quering, and enumerating recurring events for
112
+ calendars in Ruby.
113
+ email:
114
+ - rosskaff@gmail.com
115
+ executables: []
116
+ extensions: []
117
+ extra_rdoc_files: []
118
+ files:
119
+ - ".gitignore"
120
+ - ".rubocop.yml"
121
+ - ".travis.yml"
122
+ - CODE_OF_CONDUCT.md
123
+ - Gemfile
124
+ - Guardfile
125
+ - LICENSE.txt
126
+ - README.md
127
+ - Rakefile
128
+ - bin/_guard-core
129
+ - bin/console
130
+ - bin/guard
131
+ - bin/m
132
+ - bin/rake
133
+ - bin/rubocop
134
+ - bin/setup
135
+ - lib/montrose.rb
136
+ - lib/montrose/chainable.rb
137
+ - lib/montrose/clock.rb
138
+ - lib/montrose/errors.rb
139
+ - lib/montrose/frequency.rb
140
+ - lib/montrose/frequency/daily.rb
141
+ - lib/montrose/frequency/hourly.rb
142
+ - lib/montrose/frequency/minutely.rb
143
+ - lib/montrose/frequency/monthly.rb
144
+ - lib/montrose/frequency/weekly.rb
145
+ - lib/montrose/frequency/yearly.rb
146
+ - lib/montrose/options.rb
147
+ - lib/montrose/recurrence.rb
148
+ - lib/montrose/rule.rb
149
+ - lib/montrose/rule/after.rb
150
+ - lib/montrose/rule/before.rb
151
+ - lib/montrose/rule/day_of_month.rb
152
+ - lib/montrose/rule/day_of_week.rb
153
+ - lib/montrose/rule/day_of_year.rb
154
+ - lib/montrose/rule/hour_of_day.rb
155
+ - lib/montrose/rule/month_of_year.rb
156
+ - lib/montrose/rule/nth_day_matcher.rb
157
+ - lib/montrose/rule/nth_day_of_month.rb
158
+ - lib/montrose/rule/nth_day_of_year.rb
159
+ - lib/montrose/rule/time_of_day.rb
160
+ - lib/montrose/rule/total.rb
161
+ - lib/montrose/rule/week_of_year.rb
162
+ - lib/montrose/schedule.rb
163
+ - lib/montrose/stack.rb
164
+ - lib/montrose/utils.rb
165
+ - lib/montrose/version.rb
166
+ - montrose.gemspec
167
+ homepage: https://github.com/rossta/montrose
168
+ licenses:
169
+ - MIT
170
+ metadata: {}
171
+ post_install_message:
172
+ rdoc_options: []
173
+ require_paths:
174
+ - lib
175
+ required_ruby_version: !ruby/object:Gem::Requirement
176
+ requirements:
177
+ - - ">="
178
+ - !ruby/object:Gem::Version
179
+ version: 2.1.0
180
+ required_rubygems_version: !ruby/object:Gem::Requirement
181
+ requirements:
182
+ - - ">="
183
+ - !ruby/object:Gem::Version
184
+ version: '0'
185
+ requirements: []
186
+ rubyforge_project:
187
+ rubygems_version: 2.4.5.1
188
+ signing_key:
189
+ specification_version: 4
190
+ summary: Recurring events in Ruby
191
+ test_files: []
192
+ has_rdoc: