montrose 0.2.1 → 0.2.2

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: 53a2439841915011853eef7bb8fee926939b5335
4
- data.tar.gz: fad0d53e1187f6a077c6410e63016526973633c2
3
+ metadata.gz: edcfdb878952330a2fcc9a78db4f4e7780e643e3
4
+ data.tar.gz: 619002eee045e0d960de83d2bdf78078d7d74883
5
5
  SHA512:
6
- metadata.gz: 6e3b010f94e12c9705278b2fc860df7d0384476a097ab4f77ca3b503fa365272a6e5af2faa34d1ec4e4692f1c192f01179e63637e85d2164fa8bedaa8ad387ac
7
- data.tar.gz: 2466894b36681dc0a8bc723e72b8976dab0c8d7d340f2cf47ec15b7f52bc697b919434aae9e52e13e3e2ea27d1754d47615e05876188afb8a087bd19e0b884b7
6
+ metadata.gz: 7be4df5c22b3677978134b446d30c7e3bac3e33309d35f66253aea93ed62d5e60a17bf9b1a8eb8a3745260ef38dee9f74d7f057a0b2fa181f752e7a3183336b1
7
+ data.tar.gz: 5a1c3a19e2e5fc6a7b64c620be48926d9d257e33b3b2fde91b0588b3d1355052b0b016e011ec7cf396f2046a2e3005b10fab43de0cd648224b55b7b90383f1c4
@@ -1,3 +1,11 @@
1
+ ### 0.2.2 - 2016-02-08
2
+
3
+ * bug fixes
4
+ * Handle `Hash` in `Montrose::Chainable` methods that support varargs
5
+ * enhancements
6
+ * Adds `Montrose.r` method for starting a new recurrence
7
+ * Adds `Chainable` alias methods including `#starts`, `#until`, `#repeat`
8
+
1
9
  ### 0.2.1 - 2016-02-03
2
10
 
3
11
  * bug fixes
data/README.md CHANGED
@@ -7,6 +7,8 @@
7
7
 
8
8
  Montrose is an easy-to-use library for defining recurring events in Ruby. It uses a simple chaining system for building recurrences, inspired heavily by the design principles of [HTTP.rb](https://github.com/httprb/http) and rule definitions available in [Recurrence](https://github.com/fnando/recurrence).
9
9
 
10
+ [Introduction](https://rossta.net/blog/recurring-events-in-ruby.html)
11
+
10
12
  ## Installation
11
13
 
12
14
  Add this line to your application's Gemfile:
@@ -28,6 +30,11 @@ Or install it yourself as:
28
30
  ```ruby
29
31
  require "montrose"
30
32
 
33
+ # a new recurrence
34
+ Montrose.r
35
+ Montrose.recurrence
36
+ Montrose::Recurrence.new
37
+
31
38
  # daily for 10 occurrences
32
39
  Montrose.daily(total: 10)
33
40
 
@@ -144,36 +151,36 @@ Montrose.every(20.minutes, hour: 9..16)
144
151
 
145
152
  # Minutely
146
153
  Montrose.minutely
147
- Montrose::Recurrence.new(every: :minute)
154
+ Montrose.r(every: :minute)
148
155
 
149
156
  Montrose.every(10.minutes)
150
- Montrose::Recurrence.new(every: 10.minutes)
151
- Montrose::Recurrence.new(every: :minute, interval: 10) # every 10 minutes
157
+ Montrose.r(every: 10.minutes)
158
+ Montrose.r(every: :minute, interval: 10) # every 10 minutes
152
159
 
153
160
  Montrose.minutely(until: "9:00 PM")
154
- Montrose::Recurrence.new(every: :minute, until: "9:00 PM")
161
+ Montrose.r(every: :minute, until: "9:00 PM")
155
162
 
156
163
  # Daily
157
164
  Montrose.daily
158
165
  Montrose.every(:day)
159
- Montrose::Recurrence.new(every: :day)
166
+ Montrose.r(every: :day)
160
167
 
161
168
  Montrose.every(9.days)
162
- Montrose::Recurrence.new(every: 9.days)
163
- Montrose::Recurrence.new(every: :day, interval: 9)
169
+ Montrose.r(every: 9.days)
170
+ Montrose.r(every: :day, interval: 9)
164
171
 
165
172
  Montrose.daily(at: "9:00 AM")
166
173
  Montrose.every(:day, at: "9:00 AM")
167
- Montrose::Recurrence.new(every: :day, at: "9:00 AM")
174
+ Montrose.r(every: :day, at: "9:00 AM")
168
175
 
169
176
  Montrose.daily(total: 7)
170
177
  Montrose.every(:day, total: 7)
171
- Montrose::Recurrence.new(every: :day, total: 7)
178
+ Montrose.r(every: :day, total: 7)
172
179
 
173
180
  # Weekly
174
181
  Montrose.weekly
175
182
  Montrose.every(:week)
176
- Montrose::Recurrence.new(every: :week)
183
+ Montrose.r(every: :week)
177
184
 
178
185
  Montrose.every(:week, on: :monday)
179
186
  Montrose.every(:week, on: [:monday, :wednesday, :friday])
@@ -184,7 +191,7 @@ Montrose.weekly(on: :thursday)
184
191
  # Monthly by month day
185
192
  Montrose.monthly(mday: 1) # first of the month
186
193
  Montrose.every(:month, mday: 1)
187
- Montrose::Recurrence.new(every: :month, mday: 1)
194
+ Montrose.r(every: :month, mday: 1)
188
195
 
189
196
  Montrose.monthly(mday: [2, 15]) # 2nd and 15th of the month
190
197
  Montrose.monthly(mday: -3) # third-to-last day of the month
@@ -193,7 +200,7 @@ Montrose.monthly(mday: 10..15) # 10th through the 15th day of the month
193
200
  # Monthly by week day
194
201
  Montrose.monthly(day: :friday, interval: 2) # every Friday every other month
195
202
  Montrose.every(:month, day: :friday, interval: 2)
196
- Montrose::Recurrence.new(every: :month, day: :friday, interval: 2)
203
+ Montrose.r(every: :month, day: :friday, interval: 2)
197
204
 
198
205
  Montrose.monthly(day: { friday: [1] }) # 1st Friday of the month
199
206
  Montrose.monthly(day: { Sunday: [1, -1] }) # first and last Sunday of the month
@@ -203,14 +210,14 @@ Montrose.monthly(mday: 7..13, day: :saturday) # first Saturday that follow the f
203
210
  # Yearly
204
211
  Montrose.yearly
205
212
  Montrose.every(:year)
206
- Montrose::Recurrence.new(every: :year)
213
+ Montrose.r(every: :year)
207
214
 
208
215
  Montrose.yearly(month: [:june, :july]) # yearly in June and July
209
216
  Montrose.yearly(month: 6..8, day: :thursday) # yearly in June, July, August on Thursday
210
217
  Montrose.yearly(yday: [1, 100]) # yearly on the 1st and 100th day of year
211
218
 
212
- Montrose::Recurrence.yearly(on: { january: 31 })
213
- Montrose::Recurrence.new(every: :year, on: { 10 => 31 }, interval: 3)
219
+ Montrose.yearly(on: { january: 31 })
220
+ Montrose.r(every: :year, on: { 10 => 31 }, interval: 3)
214
221
 
215
222
  # TODO: Remove a date in the series with :except date(s)
216
223
  Montrose.daily(:day, except: "2017-01-31")
@@ -235,9 +242,11 @@ r.events.take(10).each { |date| puts date.to_s }
235
242
  r.events.lazy.select { |time| time > 1.month.from_now }.take(3).each { |date| puts date.to_s }
236
243
  ```
237
244
 
238
- ## Goals
245
+ ## Why?
246
+
247
+ `Montrose` aims to provide a simple interface for specifying and enumerating recurring events as `Time` objects.
239
248
 
240
- `Montrose` aims to provide a simple interface for specifying and enumerating recurring events as Time objects. To that end, the project intends to:
249
+ More specifically, this project intends to:
241
250
 
242
251
  * embrace Ruby idioms
243
252
  * support Ruby 2.1+
@@ -245,9 +254,112 @@ r.events.lazy.select { |time| time > 1.month.from_now }.take(3).each { |date| pu
245
254
  * serialize to yaml, hash, and [ical](http://www.kanzaki.com/docs/ical/rrule.html#basic) formats
246
255
  * be suitable for integration with persistence libraries
247
256
 
248
- What `Montrose` isn't:
257
+ What `Montrose` doesn't do:
249
258
 
250
259
  * support all calendaring use cases under the sun
260
+ * schedule recurring jobs for you. See instead [rufus-scheduler](https://github.com/jmettraux/rufus-scheduler), [sidetiq](https://github.com/tobiassvn/sidetiq), [whenever](https://github.com/javan/whenever)
261
+
262
+ ## Concepts
263
+
264
+ Montrose allows you to easily create "recurrence" objects through chaining:
265
+
266
+ ```ruby
267
+ # Every Monday at 10:30am
268
+ Montrose.weekly.on(:monday).at("10:30 am")
269
+ => #<Montrose::Recurrence...>
270
+ ```
271
+
272
+ Or the constructor hash-syntax:
273
+
274
+ ```ruby
275
+ Montrose.r(every: :week, on: :monday, at: "10:30 am")
276
+ => #<Montrose::Recurrence...>
277
+ ```
278
+
279
+ A Montrose recurrence responds to `#events`, which returns an [`Enumerator`](/blog/what-is-enumerator.html) that can generate timestamps:
280
+
281
+ ```ruby
282
+ r = Montrose.hourly
283
+ => #<Montrose::Recurrence...>
284
+
285
+ r.events
286
+ => #<Enumerator:...>
287
+
288
+ r.events.take(10)
289
+ => [2016-02-03 18:26:08 -0500,
290
+ 2016-02-03 19:26:08 -0500,
291
+ 2016-02-03 20:26:08 -0500,
292
+ 2016-02-03 21:26:08 -0500,
293
+ 2016-02-03 22:26:08 -0500,
294
+ 2016-02-03 23:26:08 -0500,
295
+ 2016-02-04 00:26:08 -0500,
296
+ 2016-02-04 01:26:08 -0500,
297
+ 2016-02-04 02:26:08 -0500,
298
+ 2016-02-04 03:26:08 -0500]
299
+ ```
300
+
301
+ Montrose recurrences are themselves enumerable:
302
+
303
+ ```ruby
304
+ # Every month starting a year from now on Friday the 13th for 5 occurrences
305
+ r = Montrose.monthly.starting(1.year.from_now).on(friday: 13).repeat(5)
306
+
307
+ r.map(&:to_date)
308
+ => [Fri, 13 Oct 2017,
309
+ Fri, 13 Apr 2018,
310
+ Fri, 13 Jul 2018,
311
+ Fri, 13 Sep 2019,
312
+ Fri, 13 Dec 2019]
313
+ ```
314
+
315
+ Each chained recurrence returns a new object so they can be composed and
316
+ merged:
317
+
318
+ ```ruby
319
+ # Every week
320
+ r1 = Montrose.every(:week)
321
+ r2 = Montrose.on([:tuesday, :thursday])
322
+ r3 = Montrose.at("12 pm")
323
+ r4 = Montrose.total(4)
324
+
325
+ r1.merge(r2).merge(r3).merge(r4).to_a
326
+ => [2016-02-04 12:00:00 -0500,
327
+ 2016-02-09 12:00:00 -0500,
328
+ 2016-02-11 12:00:00 -0500,
329
+ 2016-02-16 12:00:00 -0500]
330
+ ```
331
+
332
+ Conceptually, recurrences can represent an infinite sequence. When we say
333
+ simply "every day", there is no implied ending. It's therefore possible to
334
+ create a recurrence that can enumerate forever.
335
+
336
+ ```ruby
337
+ # Every day starting now
338
+ r = Montrose.daily
339
+
340
+ # this expression will never complete, Ctrl-c!
341
+ r.map(&:to_date)
342
+
343
+ # so use your `Enumerable` methods wisely
344
+ r.lazy.map(&:to_date).select { |d| d.mday > 25 }.take(5).to_a
345
+ => [Fri, 26 Feb 2016,
346
+ Sat, 27 Feb 2016,
347
+ Sun, 28 Feb 2016,
348
+ Mon, 29 Feb 2016,
349
+ Sat, 26 Mar 2016]
350
+ ```
351
+
352
+ It's straightforward to convert a recurrence to a hash and back.
353
+
354
+ ```ruby
355
+ opts = Montrose::Recurrence.new(every: 10.minutes).to_h
356
+ => {:every=>:minute, :interval=>10}
357
+
358
+ Montrose::Recurrence.new(opts).take(3)
359
+ => [2016-02-03 19:06:07 -0500,
360
+ 2016-02-03 19:16:07 -0500,
361
+ 2016-02-03 19:26:07 -0500]
362
+ ```
251
363
 
252
364
  ## Inspiration
253
365
 
@@ -17,4 +17,20 @@ require "montrose/version"
17
17
 
18
18
  module Montrose
19
19
  extend Chainable
20
+
21
+ class << self
22
+ # Create a new recurrence from given options
23
+ # An alias to {Montrose::Recurrence.new}
24
+ #
25
+ # @param options [Hash] recurrence options
26
+ #
27
+ # @example
28
+ # Montrose.recurrence(every: :day)
29
+ # Montrose.r(every: :day)
30
+ #
31
+ # @return [Montrose::Recurrence]
32
+ #
33
+ alias recurrence branch
34
+ alias r branch
35
+ end
20
36
  end
@@ -16,6 +16,8 @@ module Montrose
16
16
  # Montrose.every(3.days, starts: 2.days.from_now)
17
17
  # Montrose.every(1.year, until: 10.days.from_now)
18
18
  #
19
+ # @return [Montrose::Recurrence]
20
+ #
19
21
  def every(frequency, options = {})
20
22
  branch options.merge(every: frequency)
21
23
  end
@@ -32,6 +34,8 @@ module Montrose
32
34
  # Montrose.minutely(total: 5)
33
35
  # Montrose.minutely(except: Date.tomorrow)
34
36
  #
37
+ # @return [Montrose::Recurrence]
38
+ #
35
39
  def minutely(options = {})
36
40
  branch options.merge(every: :minute)
37
41
  end
@@ -48,6 +52,8 @@ module Montrose
48
52
  # Montrose.hourly(total: 5)
49
53
  # Montrose.hourly(except: Date.tomorrow)
50
54
  #
55
+ # @return [Montrose::Recurrence]
56
+ #
51
57
  def hourly(options = {})
52
58
  branch options.merge(every: :hour)
53
59
  end
@@ -64,6 +70,8 @@ module Montrose
64
70
  # Montrose.daily(total: 5)
65
71
  # Montrose.daily(except: Date.tomorrow)
66
72
  #
73
+ # @return [Montrose::Recurrence]
74
+ #
67
75
  def daily(options = {})
68
76
  branch options.merge(every: :day)
69
77
  end
@@ -79,6 +87,8 @@ module Montrose
79
87
  # Montrose.weekly(on: :saturday, interval: 2)
80
88
  # Montrose.weekly(on: :saturday, total: 5)
81
89
  #
90
+ # @return [Montrose::Recurrence]
91
+ #
82
92
  def weekly(options = {})
83
93
  branch options.merge(every: :week)
84
94
  end
@@ -92,6 +102,8 @@ module Montrose
92
102
  # Montrose.monthly(mday: -3) # third-to-last day of the month
93
103
  # Montrose.monthly(mday: 10..15) # 10th through the 15th day of the month
94
104
  #
105
+ # @return [Montrose::Recurrence]
106
+ #
95
107
  def monthly(options = {})
96
108
  branch options.merge(every: :month)
97
109
  end
@@ -107,6 +119,8 @@ module Montrose
107
119
  # Montrose.yearly(on: [:january, 14], interval: 2)
108
120
  # Montrose.yearly(on: [:january, 14], total: 5)
109
121
  #
122
+ # @return [Montrose::Recurrence]
123
+ #
110
124
  def yearly(options = {})
111
125
  branch options.merge(every: :year)
112
126
  end
@@ -118,9 +132,12 @@ module Montrose
118
132
  # @example
119
133
  # Montrose.daily.starting(Date.tomorrow)
120
134
  #
121
- def starting(starts_at)
135
+ # @return [Montrose::Recurrence]
136
+ #
137
+ def starts(starts_at)
122
138
  merge(starts: starts_at)
123
139
  end
140
+ alias starting starts
124
141
 
125
142
  # Create a recurrence ending at given timestamp.
126
143
  #
@@ -129,9 +146,12 @@ module Montrose
129
146
  # @example
130
147
  # Montrose.daily.ending(1.year.from_now)
131
148
  #
132
- def ending(ends_at)
149
+ # @return [Montrose::Recurrence]
150
+ #
151
+ def until(ends_at)
133
152
  merge(until: ends_at)
134
153
  end
154
+ alias ending until
135
155
 
136
156
  # Create a recurrence occurring during date range.
137
157
  #
@@ -140,6 +160,8 @@ module Montrose
140
160
  # @example
141
161
  # Montrose.weekly.between(Date.today..Date.new(2016, 3, 15))
142
162
  #
163
+ # @return [Montrose::Recurrence]
164
+ #
143
165
  def between(date_range)
144
166
  merge(between: date_range)
145
167
  end
@@ -152,6 +174,8 @@ module Montrose
152
174
  # Montrose.weekly.on(:friday)
153
175
  # Montrose.monthly.on(friday: 13)
154
176
  #
177
+ # @return [Montrose::Recurrence]
178
+ #
155
179
  def on(day)
156
180
  merge(on: day)
157
181
  end
@@ -164,6 +188,8 @@ module Montrose
164
188
  # Montrose.daily.at("12pm")
165
189
  # Montrose.daily.at("9:37am")
166
190
  #
191
+ # @return [Montrose::Recurrence]
192
+ #
167
193
  def at(time)
168
194
  merge(at: time)
169
195
  end
@@ -177,6 +203,8 @@ module Montrose
177
203
  # Montrose.daily.day_of_month([1, -1])
178
204
  # Montrose.daily.day_of_month(2..8)
179
205
  #
206
+ # @return [Montrose::Recurrence]
207
+ #
180
208
  def day_of_month(days, *extras)
181
209
  merge(mday: days.array_concat(extras))
182
210
  end
@@ -191,6 +219,8 @@ module Montrose
191
219
  # Montrose.daily.day_of_week(:monday, :tuesday)
192
220
  # Montrose.daily.day_of_week(2..5)
193
221
  #
222
+ # @return [Montrose::Recurrence]
223
+ #
194
224
  def day_of_week(weekdays, *extras)
195
225
  merge(day: weekdays.array_concat(extras))
196
226
  end
@@ -205,6 +235,8 @@ module Montrose
205
235
  # Montrose.daily.day_of_year([1, 10, 100])
206
236
  # Montrose.daily.day_of_year(20..50)
207
237
  #
238
+ # @return [Montrose::Recurrence]
239
+ #
208
240
  def day_of_year(days, *extras)
209
241
  merge(yday: days.array_concat(extras))
210
242
  end
@@ -219,6 +251,8 @@ module Montrose
219
251
  # Montrose.hourly.hour_of_day(15)
220
252
  # Montrose.hourly.hour_of_day(6..10)
221
253
  #
254
+ # @return [Montrose::Recurrence]
255
+ #
222
256
  def hour_of_day(hours, *extras)
223
257
  merge(hour: hours.array_concat(extras))
224
258
  end
@@ -233,6 +267,8 @@ module Montrose
233
267
  # Montrose.monthly.month_of_year([2, 5])
234
268
  # Montrose.monthly.month_of_year(2..5)
235
269
  #
270
+ # @return [Montrose::Recurrence]
271
+ #
236
272
  def month_of_year(months, *extras)
237
273
  merge(month: months.array_concat(extras))
238
274
  end
@@ -247,6 +283,8 @@ module Montrose
247
283
  # Montrose.weekly.week_of_year([2, 5])
248
284
  # Montrose.weekly.week_of_year(2..5)
249
285
  #
286
+ # @return [Montrose::Recurrence]
287
+ #
250
288
  def week_of_year(weeks, *extras)
251
289
  merge(week: weeks.array_concat(extras))
252
290
  end
@@ -259,31 +297,36 @@ module Montrose
259
297
  # @example
260
298
  # Montrose.daily.total(10)
261
299
  #
300
+ # @return [Montrose::Recurrence]
301
+ #
262
302
  def total(total)
263
303
  merge(total: total)
264
304
  end
305
+ alias repeat total
265
306
 
266
307
  # Create a new recurrence combining options of self
267
308
  # and other. The value of entries with duplicate
268
309
  # keys will be those of other
269
310
  #
270
- # @param opts [Hash,Montrose::Recurrence] other options or recurrence
311
+ # @param other [Hash,Montrose::Recurrence] other options or recurrence
271
312
  #
272
313
  # @example
273
314
  # Montrose.daily.total(10)
274
315
  #
316
+ # @return [Montrose::Recurrence]
317
+ #
275
318
  def merge(other = {})
276
319
  branch default_options.merge(other)
277
320
  end
278
321
 
279
322
  # @private
280
- def default_options
281
- @default_options ||= Montrose::Options.new
323
+ def branch(options)
324
+ Montrose::Recurrence.new(options)
282
325
  end
283
326
 
284
327
  # @private
285
- def branch(options)
286
- Montrose::Recurrence.new(options)
328
+ def default_options
329
+ @default_options ||= Montrose::Options.new
287
330
  end
288
331
  end
289
332
  end
@@ -36,6 +36,14 @@ module Montrose
36
36
  @default_options = Montrose::Options.new(opts)
37
37
  end
38
38
 
39
+ # Returns an enumerator for iterating over
40
+ # timestamps in the recurrence
41
+ #
42
+ # @example
43
+ # recurrence.events
44
+ #
45
+ # @return [Enumerator] a enumerator of recurrence timestamps
46
+ #
39
47
  def events
40
48
  event_enum
41
49
  end
@@ -44,6 +52,11 @@ module Montrose
44
52
  events.each(&block)
45
53
  end
46
54
 
55
+ # Returns a hash of the options used to create
56
+ # the recurrence
57
+ #
58
+ # @return [Hash] hash of recurrence options
59
+ #
47
60
  def to_hash
48
61
  default_options.to_hash
49
62
  end
@@ -6,6 +6,14 @@ module Montrose
6
6
  Array(self) + other
7
7
  end
8
8
  end
9
+
10
+ refine Hash do
11
+ # array concat for Hash not supported
12
+ # so we just return self
13
+ def array_concat(_other)
14
+ self
15
+ end
16
+ end
9
17
  end
10
18
  end
11
19
  end
@@ -1,4 +1,4 @@
1
1
  # frozen_string_literal: true
2
2
  module Montrose
3
- VERSION = "0.2.1".freeze
3
+ VERSION = "0.2.2".freeze
4
4
  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.2.1
4
+ version: 0.2.2
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-02-03 00:00:00.000000000 Z
11
+ date: 2016-02-09 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: activesupport
@@ -186,7 +186,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
186
186
  version: '0'
187
187
  requirements: []
188
188
  rubyforge_project:
189
- rubygems_version: 2.4.5.1
189
+ rubygems_version: 2.5.1
190
190
  signing_key:
191
191
  specification_version: 4
192
192
  summary: Recurring events in Ruby