montrose 0.4.3 → 0.5.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 2624845de532b2888ed205eeb7e92be0e44475c2
4
- data.tar.gz: 0be802540d67f55b0b930a57202ee44683718bf1
3
+ metadata.gz: e2af0b4abedbea2421c73f457fcdbd66390870f1
4
+ data.tar.gz: dafba1cbbd0f14757bc95ffe81764a065947e2fa
5
5
  SHA512:
6
- metadata.gz: ac9b40468a9a9f00775f6d2041e1e98d6df31df6512a66f6870f43776f7dbbebc1f17c3901bcdfbae3dafd30761131601cbe42d9d709a042ac22db52dd57612f
7
- data.tar.gz: 6c49f3532b8a7b49654fa2a7f4ca217394d9e03d3b315ce015f23ff7652d28f180172103b3e75dd079bb47ba064946811ee8b0c9d086b5411a0a7b7c85bd5049
6
+ metadata.gz: f3c9e25c8f3e30cf8df3037e653f3c515ea25c3bf9d144da520a4e4a5e043a8a51c8b8eefe449d4c83ac0b87ab47d5a0c0e14a58e875846a1d9995a9472501c0
7
+ data.tar.gz: fb805c48e5e6d9880e6c3c091ea738bf2b6f4e21a59afa78cf3c467ad062cdf12f5802f46078138fbde9e74030f6d3893a83fee5877fc527b1c66da38c609312
@@ -112,3 +112,9 @@ Style/RegexpLiteral:
112
112
 
113
113
  Style/DotPosition:
114
114
  EnforcedStyle: trailing
115
+
116
+ Style/VariableNumber:
117
+ Enabled: false
118
+
119
+ Style/FormatString:
120
+ Enabled: false
@@ -12,6 +12,6 @@ matrix:
12
12
  fast_finish: true
13
13
  rvm:
14
14
  - 2.1.10
15
- - 2.2.5
16
- - 2.3.1
15
+ - 2.2.6
16
+ - 2.3.3
17
17
  sudo: false
@@ -1,4 +1,10 @@
1
- ### 0.4.3 - (2016-07-27)
1
+ ### 0.5.0 - (2016-11-23)
2
+
3
+ * enhancements
4
+ * Adds `Recurrence#include?`
5
+ * Improved documentation
6
+
7
+ ### 0.4.3 - (2016-11-20)
2
8
 
3
9
  * enhancements
4
10
  * Add CI support for ActiveSupport 4.1, 4.2, 5.0 (by @phlipper)
data/README.md CHANGED
@@ -351,9 +351,6 @@ Montrose.yearly(yday: [1, 100]) # yearly on the 1st and 100th day of year
351
351
  Montrose.yearly(on: { january: 31 })
352
352
  Montrose.r(every: :year, on: { 10 => 31 }, interval: 3)
353
353
 
354
- Montrose.daily(:day, except: "2017-01-31")
355
- Montrose.daily(except: [Date.today, "2017-01-31"])
356
-
357
354
  # Chaining
358
355
  Montrose.weekly.starting(3.weeks.from_now).on(:friday)
359
356
  Montrose.every(:day).at("4:05pm")
data/Rakefile CHANGED
@@ -20,6 +20,7 @@ task default: [:spec, :rubocop]
20
20
  namespace :doc do
21
21
  desc "Generate docs and publish to gh-pages"
22
22
  task :publish do
23
+ puts "Generating docs"
23
24
  require "fileutils"
24
25
  sh "yard doc"
25
26
  sh "git checkout gh-pages"
@@ -5,11 +5,216 @@ require "montrose/stack"
5
5
  require "montrose/clock"
6
6
 
7
7
  module Montrose
8
+ # Represents the rules for a set of recurring events. Can be instantiated
9
+ # and extended in a variety of ways as show in the examples below.
10
+ #
11
+ # @author Ross Kaffenberger
12
+ # @since 0.0.1
13
+ # @attr_reader [Montrose::Options] default_options contains the recurrence rules in hash-like format
14
+ #
15
+ # @example a new recurrence
16
+ # Montrose.r
17
+ # Montrose.recurrence
18
+ # Montrose::Recurrence.new
19
+ #
20
+ # @example daily for 10 occurrences
21
+ # Montrose.daily(total: 10)
22
+ #
23
+ # @example daily until December 23, 2015
24
+ # starts = Date.new(2015, 1, 1)
25
+ # ends = Date.new(2015, 12, 23)
26
+ # Montrose.daily(starts: starts, until: ends)
27
+ #
28
+ # @example every other day forever
29
+ # Montrose.daily(interval: 2)
30
+ #
31
+ # @example every 10 days 5 occurrences
32
+ # Montrose.every(10.days, total: 5)
33
+ #
34
+ # @example everyday in January for 3 years
35
+ # starts = Time.current.beginning_of_year
36
+ # ends = Time.current.end_of_year + 2.years
37
+ # Montrose.daily(month: :january, between: starts...ends)
38
+ #
39
+ # @example weekly for 10 occurrences
40
+ # Montrose.weekly(total: 10)
41
+ #
42
+ # @example weekly until December 23, 2015
43
+ # ends_on = Date.new(2015, 12, 23)
44
+ # starts_on = ends_on - 15.weeks
45
+ # Montrose.every(:week, until: ends_on, starts: starts_on)
46
+ #
47
+ # @example every other week forever
48
+ # Montrose.every(2.weeks)
49
+ #
50
+ # @example weekly on Tuesday and Thursday for five weeks from September 1, 2015 until October 5, 2015
51
+ # Montrose.weekly(on: [:tuesday, :thursday],
52
+ # between: Date.new(2015, 9, 1)..Date.new(2015, 10, 5))
53
+ #
54
+ # @example every other week on Mon, Wed and Fri until December 23 2015, but starting on Tuesday, September 1, 2015
55
+ # Montrose.every(2.weeks,
56
+ # on: [:monday, :wednesday, :friday],
57
+ # starts: Date.new(2015, 9, 1))
58
+ #
59
+ # @example every other week on Tuesday and Thursday, for 8 occurrences
60
+ # Montrose.weekly(on: [:tuesday, :thursday], total: 8, interval: 2)
61
+ #
62
+ # @example monthly on the first Friday for ten occurrences
63
+ # Montrose.monthly(day: { friday: [1] }, total: 10)
64
+ #
65
+ # @example monthly on the first Friday until December 23, 2015
66
+ # Montrose.every(:month, day: { friday: [1] }, until: Date.new(2016, 12, 23))
67
+ #
68
+ # @example every other month on the first and last Sunday of the month for 10 occurrences
69
+ # Montrose.every(:month, day: { sunday: [1, -1] }, interval: 2, total: 10)
70
+ #
71
+ # @example monthly on the second-to-last Monday of the month for 6 months
72
+ # Montrose.every(:month, day: { monday: [-2] }, total: 6)
73
+ #
74
+ # @example monthly on the third-to-the-last day of the month, forever
75
+ # Montrose.every(:month, mday: [-3])
76
+ #
77
+ # @example monthly on the 2nd and 15th of the month for 10 occurrences
78
+ # Montrose.every(:month, mday: [2, 15], total: 10)
79
+ #
80
+ # @example monthly on the first and last day of the month for 10 occurrences
81
+ # Montrose.monthly(mday: [1, -1], total: 10)
82
+ #
83
+ # @example every 18 months on the 10th thru 15th of the month for 10 occurrences
84
+ # Montrose.every(18.months, total: 10, mday: 10..15)
85
+ #
86
+ # @example every Tuesday, every other month
87
+ # Montrose.every(2.months, on: :tuesday)
88
+ #
89
+ # @example yearly in June and July for 10 occurrences
90
+ # Montrose.yearly(month: [:june, :july], total: 10)
91
+ #
92
+ # @example every other year on January, February, and March for 10 occurrences
93
+ # Montrose.every(2.years, month: [:january, :february, :march], total: 10)
94
+ #
95
+ # @example every third year on the 1st, 100th and 200th day for 10 occurrences
96
+ # Montrose.yearly(yday: [1, 100, 200], total: 10)
97
+ #
98
+ # @example every 20th Monday of the year, forever
99
+ # Montrose.yearly(day: { monday: [20] })
100
+ #
101
+ # @example Monday of week number 20 forever
102
+ # Montrose.yearly(week: [20], on: :monday)
103
+ #
104
+ # @example every Thursday in March, forever
105
+ # Montrose.monthly(month: :march, on: :thursday, at: "12 pm")
106
+ #
107
+ # @example every Thursday, but only during June, July, and August, forever" do
108
+ # Montrose.monthly(month: 6..8, on: :thursday)
109
+ #
110
+ # @example every Friday 13th, forever
111
+ # Montrose.monthly(on: { friday: 13 })
112
+ #
113
+ # @example first Saturday that follows the first Sunday of the month, forever
114
+ # Montrose.monthly(on: { saturday: 7..13 })
115
+ #
116
+ # @example every four years, the first Tuesday after a Monday in November, forever (U.S. Presidential Election day)
117
+ # Montrose.every(4.years, month: :november, on: { tuesday: 2..8 })
118
+ #
119
+ # @example every 3 hours from 9:00 AM to 5:00 PM on a specific day
120
+ # date = Date.new(2016, 9, 1)
121
+ # Montrose.hourly(between: date..(date+1), hour: 9..17, interval: 3)
122
+ #
123
+ # @example every 15 minutes for 6 occurrences
124
+ # Montrose.every(90.minutes, total: 6)
125
+ #
126
+ # @example every hour and a half for four occurrences
127
+ # Montrose.every(90.minutes, total: 4)
128
+ #
129
+ # @example every 20 minutes from 9:00 AM to 4:40 PM every day
130
+ # Montrose.every(20.minutes, hour: 9..16)
131
+ #
132
+ # @example Minutely
133
+ # Montrose.minutely
134
+ # Montrose.r(every: :minute)
135
+ # Montrose.every(10.minutes)
136
+ # Montrose.r(every: 10.minutes)
137
+ # Montrose.r(every: :minute, interval: 10) # every 10 minutes
138
+ # Montrose.minutely(until: "9:00 PM")
139
+ # Montrose.r(every: :minute, until: "9:00 PM")
140
+ #
141
+ # @example Daily
142
+ # Montrose.daily
143
+ # Montrose.every(:day)
144
+ # Montrose.r(every: :day)
145
+ # Montrose.every(9.days)
146
+ # Montrose.r(every: 9.days)
147
+ # Montrose.r(every: :day, interval: 9)
148
+ # Montrose.daily(at: "9:00 AM")
149
+ # Montrose.every(:day, at: "9:00 AM")
150
+ # Montrose.r(every: :day, at: "9:00 AM")
151
+ # Montrose.daily(total: 7)
152
+ # Montrose.every(:day, total: 7)
153
+ # Montrose.r(every: :day, total: 7)
154
+ #
155
+ # @example Weekly
156
+ # Montrose.weekly
157
+ # Montrose.every(:week)
158
+ # Montrose.r(every: :week)
159
+ # Montrose.every(:week, on: :monday)
160
+ # Montrose.every(:week, on: [:monday, :wednesday, :friday])
161
+ # Montrose.every(2.weeks, on: :friday)
162
+ # Montrose.every(:week, on: :friday, at: "3:41 PM")
163
+ # Montrose.weekly(on: :thursday)
164
+ #
165
+ # @example Monthly by month day
166
+ # Montrose.monthly(mday: 1) # first of the month
167
+ # Montrose.every(:month, mday: 1)
168
+ # Montrose.r(every: :month, mday: 1)
169
+ # Montrose.monthly(mday: [2, 15]) # 2nd and 15th of the month
170
+ # Montrose.monthly(mday: -3) # third-to-last day of the month
171
+ # Montrose.monthly(mday: 10..15) # 10th through the 15th day of the month
172
+ #
173
+ # @example Monthly by week day
174
+ # Montrose.monthly(day: :friday, interval: 2) # every Friday every other month
175
+ # Montrose.every(:month, day: :friday, interval: 2)
176
+ # Montrose.r(every: :month, day: :friday, interval: 2)
177
+ # Montrose.monthly(day: { friday: [1] }) # 1st Friday of the month
178
+ # Montrose.monthly(day: { Sunday: [1, -1] }) # first and last Sunday of the month
179
+ # Montrose.monthly(mday: 7..13, day: :saturday) # first Saturday that follow the first Sunday of the month
180
+ #
181
+ # @example Yearly
182
+ # Montrose.yearly
183
+ # Montrose.every(:year)
184
+ # Montrose.r(every: :year)
185
+ # Montrose.yearly(month: [:june, :july]) # yearly in June and July
186
+ # Montrose.yearly(month: 6..8, day: :thursday) # yearly in June, July, August on Thursday
187
+ # Montrose.yearly(yday: [1, 100]) # yearly on the 1st and 100th day of year
188
+ # Montrose.yearly(on: { january: 31 })
189
+ # Montrose.r(every: :year, on: { 10 => 31 }, interval: 3)
190
+ #
191
+ # @example Chaining
192
+ # Montrose.weekly.starting(3.weeks.from_now).on(:friday)
193
+ # Montrose.every(:day).at("4:05pm")
194
+ # Montrose.yearly.between(Time.current..10.years.from_now)
195
+ #
196
+ # @example Enumerating events
197
+ # r = Montrose.every(:month, mday: 31, until: "January 1, 2017")
198
+ # r.each { |time| puts time.to_s }
199
+ # r.take(10).to_a
200
+ #
201
+ # @example Merging rules
202
+ # r.merge(starts: "2017-01-01").each { |time| puts time.to_s }
203
+ #
204
+ # @example Using #events Enumerator
205
+ # r.events # => #<Enumerator: ...>
206
+ # r.events.take(10).each { |date| puts date.to_s }
207
+ # r.events.lazy.select { |time| time > 1.month.from_now }.take(3).each { |date| puts date.to_s }
208
+ #
8
209
  class Recurrence
210
+ extend Forwardable
9
211
  include Chainable
10
212
  include Enumerable
11
213
 
12
214
  attr_reader :default_options
215
+ def_delegator :@default_options, :total, :length
216
+ def_delegator :@default_options, :starts, :starts_at
217
+ def_delegator :@default_options, :until, :ends_at
13
218
 
14
219
  class << self
15
220
  def new(options = {})
@@ -28,7 +233,7 @@ module Montrose
28
233
  end
29
234
 
30
235
  def load(json)
31
- new JSON.load(json)
236
+ new JSON.parse(json)
32
237
  end
33
238
  end
34
239
 
@@ -36,24 +241,52 @@ module Montrose
36
241
  @default_options = Montrose::Options.new(opts)
37
242
  end
38
243
 
39
- # Returns an enumerator for iterating over
40
- # timestamps in the recurrence
244
+ # Returns an enumerator for iterating over timestamps in the recurrence
41
245
  #
42
- # @example
246
+ # @example Return the events
247
+ # recurrence = Montrose.recurrence(every: :day)
43
248
  # recurrence.events
44
249
  #
45
- # @return [Enumerator] a enumerator of recurrence timestamps
250
+ # @example Iterate over a finite recurrence
251
+ # recurrence = Montrose.recurrence(every: :day, until: 1.year.from_now)
252
+ # recurrence.events.each do |event|
253
+ # puts event
254
+ # end
255
+ #
256
+ # @example Iterate over an infinite recurrence
257
+ # recurrence = Montrose.recurrence(every: :day)
258
+ # recurrence.events.lazy.each do |event|
259
+ # puts event
260
+ # end
261
+ #
262
+ # @return [Enumerator] an enumerator of recurrence timestamps
46
263
  #
47
264
  def events
48
265
  event_enum
49
266
  end
50
267
 
268
+ # Iterate over the events of a recurrence. Along with the Enumerable
269
+ # module, this makes Montrose occurrences enumerable like other Ruby
270
+ # collections
271
+ #
272
+ # @example Iterate over a finite recurrence
273
+ # recurrence = Montrose.recurrence(every: :day, until: 1.year.from_now)
274
+ # recurrence.each do |event|
275
+ # puts event
276
+ # end
277
+ #
278
+ # @example Iterate over an infinite recurrence
279
+ # recurrence = Montrose.recurrence(every: :day)
280
+ # recurrence.lazy.each do |event|
281
+ # puts event
282
+ # end
283
+ #
284
+ # @return [Enumerator] an enumerator of recurrence timestamps
51
285
  def each(&block)
52
286
  events.each(&block)
53
287
  end
54
288
 
55
- # Returns a hash of the options used to create
56
- # the recurrence
289
+ # Returns a hash of the options used to create the recurrence
57
290
  #
58
291
  # @return [Hash] hash of recurrence options
59
292
  #
@@ -62,8 +295,7 @@ module Montrose
62
295
  end
63
296
  alias to_h to_hash
64
297
 
65
- # Returns json string of options used to create
66
- # the recurrence
298
+ # Returns json string of options used to create the recurrence
67
299
  #
68
300
  # @return [String] json of recurrence options
69
301
  #
@@ -75,6 +307,48 @@ module Montrose
75
307
  "#<#{self.class}:#{object_id.to_s(16)} #{to_h.inspect}>"
76
308
  end
77
309
 
310
+ # Return true/false if given timestamp equals a timestamp given
311
+ # by the recurrence
312
+ #
313
+ # @return [Boolean] whether or not timestamp is included in recurrence
314
+ #
315
+ def include?(timestamp)
316
+ return false if earlier?(timestamp) || later?(timestamp)
317
+
318
+ recurrence = finite? ? self : starts(timestamp)
319
+
320
+ recurrence.events.lazy.each do |event|
321
+ return true if event == timestamp
322
+ return false if event > timestamp
323
+ end or false
324
+ end
325
+
326
+ # Return true/false if recurrence will iterate infinitely
327
+ #
328
+ # @return [Boolean] whether or not recurrence is infinite
329
+ #
330
+ def finite?
331
+ ends_at || length
332
+ end
333
+
334
+ # Return true/false if given timestamp occurs before
335
+ # the recurrence
336
+ #
337
+ # @return [Boolean] whether or not timestamp is earlier
338
+ #
339
+ def earlier?(timestamp)
340
+ starts_at && timestamp < starts_at
341
+ end
342
+
343
+ # Return true/false if given timestamp occurs after
344
+ # the recurrence
345
+ #
346
+ # @return [Boolean] whether or not timestamp is later
347
+ #
348
+ def later?(timestamp)
349
+ ends_at && timestamp > ends_at
350
+ end
351
+
78
352
  private
79
353
 
80
354
  def event_enum
@@ -1,7 +1,24 @@
1
1
  module Montrose
2
+ # A schedule represents a group of recurrences
3
+ #
4
+ # @author Ross Kaffenberger
5
+ # @since 0.0.1
6
+ # @attr_reader [Array] rules the list of recurrences
7
+ #
2
8
  class Schedule
3
9
  attr_accessor :rules
4
10
 
11
+ # Instantiates a schedule and yields the instance to an optional
12
+ # block for building recurrences inline
13
+ #
14
+ # @example Build a schedule with multiple rules added in the given block
15
+ # schedule = Montrose::Schedule.build do |s|
16
+ # s << { every: :day }
17
+ # s << { every: :year }
18
+ # end
19
+ #
20
+ # @return [Montrose::Schedule]
21
+ #
5
22
  def self.build
6
23
  schedule = new
7
24
  yield schedule if block_given?
@@ -12,6 +29,18 @@ module Montrose
12
29
  @rules = []
13
30
  end
14
31
 
32
+ # Add a recurrence rule to the schedule, either by hash or recurrence
33
+ # instance
34
+ #
35
+ # @example Add a recurrence by hash
36
+ # schedule = Montrose::Schedule.new
37
+ # schedule << { every: :day }
38
+ #
39
+ # @example Add a recurrence by instance
40
+ # schedule = Montrose::Schedule.new
41
+ # recurrence = Montrose.recurrence(every: :day)
42
+ # schedule << recurrence
43
+ #
15
44
  def <<(rule)
16
45
  @rules << Montrose::Recurrence.new(rule)
17
46
 
@@ -19,10 +48,25 @@ module Montrose
19
48
  end
20
49
  alias add <<
21
50
 
22
- def include?(time)
23
- @rules.any? { |r| r.include?(time) }
51
+ # Return true/false if given timestamp is included in any of the rules
52
+ # found in the schedule
53
+ #
54
+ # @return [Boolean] whether or not timestamp is included in schedule
55
+ #
56
+ def include?(timestamp)
57
+ @rules.any? { |r| r.include?(timestamp) }
24
58
  end
25
59
 
60
+ # Returns an enumerator for iterating over timestamps in the schedule
61
+ #
62
+ # @example Return the events
63
+ # schedule = Montrose::Schedule.build do |s|
64
+ # s << { every: :day }
65
+ # end
66
+ # schedule.events
67
+ #
68
+ # @return [Enumerator] an enumerator of recurrence timestamps
69
+ #
26
70
  def events(opts = {})
27
71
  enums = @rules.map { |r| r.merge(opts).events }
28
72
  Enumerator.new do |y|
@@ -1,4 +1,4 @@
1
1
  # frozen_string_literal: true
2
2
  module Montrose
3
- VERSION = "0.4.3".freeze
3
+ VERSION = "0.5.0".freeze
4
4
  end
@@ -28,6 +28,6 @@ Gem::Specification.new do |spec|
28
28
  spec.add_development_dependency "rake", "~> 11.0"
29
29
  spec.add_development_dependency "minitest", "~> 5.9"
30
30
  spec.add_development_dependency "m", "~> 1.4"
31
- spec.add_development_dependency "rubocop", "0.41.0"
31
+ spec.add_development_dependency "rubocop", "0.45.0"
32
32
  spec.add_development_dependency "timecop"
33
33
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: montrose
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.4.3
4
+ version: 0.5.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Ross Kaffenberger
8
8
  autorequire:
9
9
  bindir: exe
10
10
  cert_chain: []
11
- date: 2016-11-20 00:00:00.000000000 Z
11
+ date: 2016-11-23 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: activesupport
@@ -106,14 +106,14 @@ dependencies:
106
106
  requirements:
107
107
  - - '='
108
108
  - !ruby/object:Gem::Version
109
- version: 0.41.0
109
+ version: 0.45.0
110
110
  type: :development
111
111
  prerelease: false
112
112
  version_requirements: !ruby/object:Gem::Requirement
113
113
  requirements:
114
114
  - - '='
115
115
  - !ruby/object:Gem::Version
116
- version: 0.41.0
116
+ version: 0.45.0
117
117
  - !ruby/object:Gem::Dependency
118
118
  name: timecop
119
119
  requirement: !ruby/object:Gem::Requirement
@@ -212,7 +212,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
212
212
  version: '0'
213
213
  requirements: []
214
214
  rubyforge_project:
215
- rubygems_version: 2.4.5.1
215
+ rubygems_version: 2.5.1
216
216
  signing_key:
217
217
  specification_version: 4
218
218
  summary: Recurring events in Ruby