montrose 0.2.2 → 0.3.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.
- checksums.yaml +4 -4
- data/CHANGELOG.md +12 -0
- data/Gemfile +0 -1
- data/README.md +135 -123
- data/lib/montrose.rb +4 -2
- data/lib/montrose/chainable.rb +14 -0
- data/lib/montrose/clock.rb +16 -3
- data/lib/montrose/frequency.rb +1 -1
- data/lib/montrose/options.rb +31 -21
- data/lib/montrose/rule.rb +1 -0
- data/lib/montrose/rule/except.rb +23 -0
- data/lib/montrose/rule/time_of_day.rb +1 -5
- data/lib/montrose/stack.rb +1 -0
- data/lib/montrose/utils.rb +17 -0
- data/lib/montrose/version.rb +1 -1
- metadata +4 -3
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: a88cd103fc68290cf832c8e41ec2e8a1ca4d4a87
|
4
|
+
data.tar.gz: c74c51dad1844316155c1b138b0b34f548d27dfd
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 3d186764038aaeb902fb809225a5e26779831e55a3ed0cc11998a6395821c9ab4e34af9d0152f43558f660c39d02ad9e2eff82630e24d56986e1958d0ff51de6
|
7
|
+
data.tar.gz: 4c939c9c4e6a416f20fb24b718d6fab5026b17650f72e5769b03abaa40ecc10d002a6a8210cd0ec6ae44c832d41020a222cf5e248597ede1754d830e8587d92b
|
data/CHANGELOG.md
CHANGED
@@ -1,3 +1,14 @@
|
|
1
|
+
### 0.3.0 - (2016-02-19)
|
2
|
+
|
3
|
+
* enhancements
|
4
|
+
* Adds `:except` option and chainable method to filter timestamps by date (by
|
5
|
+
@thewatts)
|
6
|
+
* bug fixes
|
7
|
+
* Fix recurrences when specifying both `:starts` and `:at` by treating
|
8
|
+
`:starts` value like a date
|
9
|
+
* Respect recurrence rules using multiple `:at` values
|
10
|
+
* Using `Montrose.r` without any arguments no longer throws `ArgumentError`
|
11
|
+
|
1
12
|
### 0.2.2 - 2016-02-08
|
2
13
|
|
3
14
|
* bug fixes
|
@@ -5,6 +16,7 @@
|
|
5
16
|
* enhancements
|
6
17
|
* Adds `Montrose.r` method for starting a new recurrence
|
7
18
|
* Adds `Chainable` alias methods including `#starts`, `#until`, `#repeat`
|
19
|
+
* README updates (by @thegcat)
|
8
20
|
|
9
21
|
### 0.2.1 - 2016-02-03
|
10
22
|
|
data/Gemfile
CHANGED
data/README.md
CHANGED
@@ -7,7 +7,9 @@
|
|
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
|
-
[
|
10
|
+
* [Introductory blog post](http://bit.ly/1PA68Zb)
|
11
|
+
* [NYC.rb
|
12
|
+
presentation](https://speaderdeck.com/rossta/recurring-events-with-montrose)
|
11
13
|
|
12
14
|
## Installation
|
13
15
|
|
@@ -25,6 +27,135 @@ Or install it yourself as:
|
|
25
27
|
|
26
28
|
$ gem install montrose
|
27
29
|
|
30
|
+
## Why
|
31
|
+
|
32
|
+
Dealing with recurring events is hard. `Montrose` provides a simple interface for specifying and enumerating recurring events as `Time` objects.
|
33
|
+
|
34
|
+
More specifically, this project intends to:
|
35
|
+
|
36
|
+
* embrace Ruby idioms
|
37
|
+
* support Ruby 2.1+
|
38
|
+
* be reasonably performant
|
39
|
+
* serialize to yaml, hash, and [ical](http://www.kanzaki.com/docs/ical/rrule.html#basic) formats
|
40
|
+
* be suitable for integration with persistence libraries
|
41
|
+
|
42
|
+
What `Montrose` doesn't do:
|
43
|
+
|
44
|
+
* support all calendaring use cases under the sun
|
45
|
+
* schedule recurring jobs for you. See instead [rufus-scheduler](https://github.com/jmettraux/rufus-scheduler), [sidekiq-cron](https://github.com/ondrejbartas/sidekiq-cron), [sidetiq](https://github.com/tobiassvn/sidetiq), [whenever](https://github.com/javan/whenever)
|
46
|
+
|
47
|
+
## Concepts
|
48
|
+
|
49
|
+
Montrose allows you to easily create "recurrence" objects through chaining:
|
50
|
+
|
51
|
+
```ruby
|
52
|
+
# Every Monday at 10:30am
|
53
|
+
Montrose.weekly.on(:monday).at("10:30 am")
|
54
|
+
=> #<Montrose::Recurrence...>
|
55
|
+
```
|
56
|
+
|
57
|
+
Each chained recurrence returns a **new object** so they can be composed and merged. In both examples below, recurrence `r4` represents 'every week on Tuesday and Thursday at noon for four occurrences'.
|
58
|
+
|
59
|
+
```ruby
|
60
|
+
# Example 1 - building recurrence in succession
|
61
|
+
r1 = Montrose.every(:week)
|
62
|
+
r2 = r1.on([:tuesday, :thursday])
|
63
|
+
r3 = r2.at("12 pm")
|
64
|
+
r4 = r3.total(4)
|
65
|
+
|
66
|
+
# Example 2 - merging distinct recurrences
|
67
|
+
r1 = Montrose.every(:week)
|
68
|
+
r2 = Montrose.on([:tuesday, :thursday])
|
69
|
+
r3 = Montrose.at("12 pm")
|
70
|
+
r4 = r1.merge(r2).merge(r3).total(4)
|
71
|
+
```
|
72
|
+
|
73
|
+
Most recurrence methods accept additional options if you favor the hash-syntax:
|
74
|
+
|
75
|
+
```ruby
|
76
|
+
Montrose.r(every: :week, on: :monday, at: "10:30 am")
|
77
|
+
=> #<Montrose::Recurrence...>
|
78
|
+
```
|
79
|
+
|
80
|
+
See [the docs for `Montrose::Chainable`](https://rossta.net/montrose/Montrose/Chainable.html) for more info on recurrence creation methods.
|
81
|
+
|
82
|
+
A Montrose recurrence responds to `#events`, which returns an [`Enumerator`](/blog/what-is-enumerator.html) that can generate timestamps:
|
83
|
+
|
84
|
+
```ruby
|
85
|
+
r = Montrose.hourly
|
86
|
+
=> #<Montrose::Recurrence...>
|
87
|
+
|
88
|
+
r.events
|
89
|
+
=> #<Enumerator:...>
|
90
|
+
|
91
|
+
r.events.take(10)
|
92
|
+
=> [2016-02-03 18:26:08 -0500,
|
93
|
+
2016-02-03 19:26:08 -0500,
|
94
|
+
2016-02-03 20:26:08 -0500,
|
95
|
+
2016-02-03 21:26:08 -0500,
|
96
|
+
2016-02-03 22:26:08 -0500,
|
97
|
+
2016-02-03 23:26:08 -0500,
|
98
|
+
2016-02-04 00:26:08 -0500,
|
99
|
+
2016-02-04 01:26:08 -0500,
|
100
|
+
2016-02-04 02:26:08 -0500,
|
101
|
+
2016-02-04 03:26:08 -0500]
|
102
|
+
```
|
103
|
+
|
104
|
+
Montrose recurrences are themselves enumerable:
|
105
|
+
|
106
|
+
```ruby
|
107
|
+
# Every month starting a year from now on Friday the 13th for 5 occurrences
|
108
|
+
r = Montrose.monthly.starting(1.year.from_now).on(friday: 13).repeat(5)
|
109
|
+
|
110
|
+
r.map(&:to_date)
|
111
|
+
=> [Fri, 13 Oct 2017,
|
112
|
+
Fri, 13 Apr 2018,
|
113
|
+
Fri, 13 Jul 2018,
|
114
|
+
Fri, 13 Sep 2019,
|
115
|
+
Fri, 13 Dec 2019]
|
116
|
+
```
|
117
|
+
|
118
|
+
Conceptually, recurrences can represent an infinite sequence. When we say
|
119
|
+
simply "every day", there is no implied ending. It's therefore possible to
|
120
|
+
create a recurrence that can enumerate forever, so use your `Enumerable` methods wisely.
|
121
|
+
|
122
|
+
```ruby
|
123
|
+
# Every day starting now
|
124
|
+
r = Montrose.daily
|
125
|
+
|
126
|
+
# this expression will never complete, Ctrl-c!
|
127
|
+
r.map(&:to_date)
|
128
|
+
|
129
|
+
# use `lazy` enumerator to avoid eager enumeration
|
130
|
+
r.lazy.map(&:to_date).select { |d| d.mday > 25 }.take(5).to_a
|
131
|
+
=> [Fri, 26 Feb 2016,
|
132
|
+
Sat, 27 Feb 2016,
|
133
|
+
Sun, 28 Feb 2016,
|
134
|
+
Mon, 29 Feb 2016,
|
135
|
+
Sat, 26 Mar 2016]
|
136
|
+
```
|
137
|
+
|
138
|
+
It's straightforward to convert a recurrence to a hash and back.
|
139
|
+
|
140
|
+
```ruby
|
141
|
+
opts = Montrose::Recurrence.new(every: 10.minutes).to_h
|
142
|
+
=> {:every=>:minute, :interval=>10}
|
143
|
+
|
144
|
+
Montrose::Recurrence.new(opts).take(3)
|
145
|
+
=> [2016-02-03 19:06:07 -0500,
|
146
|
+
2016-02-03 19:16:07 -0500,
|
147
|
+
2016-02-03 19:26:07 -0500]
|
148
|
+
```
|
149
|
+
|
150
|
+
A recurrence object must minimally specify a frequency, e.g. `:minute`, `:hour`, `:day`, `:week`, `:month`, or, `:year`, to be viable. Otherwise, you'll see an informative error message when attempting to enumerate the recurrence.
|
151
|
+
|
152
|
+
```ruby
|
153
|
+
r = Montrose.at("12pm")
|
154
|
+
=> #<Montrose::Recurrence...>
|
155
|
+
r.each
|
156
|
+
Montrose::ConfigurationError: Please specify the :every option
|
157
|
+
```
|
158
|
+
|
28
159
|
## Usage
|
29
160
|
|
30
161
|
```ruby
|
@@ -60,7 +191,7 @@ Montrose.weekly(total: 10)
|
|
60
191
|
# weekly until December 23, 2015
|
61
192
|
ends_on = Date.new(2015, 12, 23)
|
62
193
|
starts_on = ends_on - 15.weeks
|
63
|
-
Montrose.every(:week, until: ends_on, starts: starts_on
|
194
|
+
Montrose.every(:week, until: ends_on, starts: starts_on)
|
64
195
|
|
65
196
|
# every other week forever
|
66
197
|
Montrose.every(2.weeks)
|
@@ -219,7 +350,6 @@ Montrose.yearly(yday: [1, 100]) # yearly on the 1st and 100th day of year
|
|
219
350
|
Montrose.yearly(on: { january: 31 })
|
220
351
|
Montrose.r(every: :year, on: { 10 => 31 }, interval: 3)
|
221
352
|
|
222
|
-
# TODO: Remove a date in the series with :except date(s)
|
223
353
|
Montrose.daily(:day, except: "2017-01-31")
|
224
354
|
Montrose.daily(except: [Date.today, "2017-01-31"])
|
225
355
|
|
@@ -242,125 +372,6 @@ r.events.take(10).each { |date| puts date.to_s }
|
|
242
372
|
r.events.lazy.select { |time| time > 1.month.from_now }.take(3).each { |date| puts date.to_s }
|
243
373
|
```
|
244
374
|
|
245
|
-
## Why?
|
246
|
-
|
247
|
-
`Montrose` aims to provide a simple interface for specifying and enumerating recurring events as `Time` objects.
|
248
|
-
|
249
|
-
More specifically, this project intends to:
|
250
|
-
|
251
|
-
* embrace Ruby idioms
|
252
|
-
* support Ruby 2.1+
|
253
|
-
* be reasonably performant
|
254
|
-
* serialize to yaml, hash, and [ical](http://www.kanzaki.com/docs/ical/rrule.html#basic) formats
|
255
|
-
* be suitable for integration with persistence libraries
|
256
|
-
|
257
|
-
What `Montrose` doesn't do:
|
258
|
-
|
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
|
-
```
|
363
|
-
|
364
375
|
## Inspiration
|
365
376
|
|
366
377
|
Montrose is named after the beautifully diverse and artistic [neighborhood in Houston, Texas](https://en.wikipedia.org/wiki/Montrose,_Houston).
|
@@ -369,9 +380,10 @@ Montrose is named after the beautifully diverse and artistic [neighborhood in Ho
|
|
369
380
|
|
370
381
|
Check out following related projects, all of which have provided inspiration for `Montrose`.
|
371
382
|
|
372
|
-
* [recurrence](https://github.com/fnando/recurrence)
|
373
383
|
* [ice_cube](https://github.com/seejohnrun/ice_cube)
|
384
|
+
* [recurrence](https://github.com/fnando/recurrence)
|
374
385
|
* [runt](https://github.com/mlipper/runt)
|
386
|
+
* [http.rb](https://github.com/httprb/http) - not a recurrence project, but inspirational to design, implementation, and interface of `Montrose`
|
375
387
|
|
376
388
|
## Development
|
377
389
|
|
data/lib/montrose.rb
CHANGED
data/lib/montrose/chainable.rb
CHANGED
@@ -194,6 +194,20 @@ module Montrose
|
|
194
194
|
merge(at: time)
|
195
195
|
end
|
196
196
|
|
197
|
+
# Create a recurrence with dates except dates given
|
198
|
+
#
|
199
|
+
# @param date [String, Date] represents date
|
200
|
+
#
|
201
|
+
# @example
|
202
|
+
# Montrose.daily.except("2016-03-01")
|
203
|
+
# Montrose.daily.except(Date.today)
|
204
|
+
#
|
205
|
+
# @return [Montrose::Recurrence]
|
206
|
+
#
|
207
|
+
def except(date)
|
208
|
+
merge(except: date)
|
209
|
+
end
|
210
|
+
|
197
211
|
# Create a recurrence for given days of month
|
198
212
|
#
|
199
213
|
# @param days [Fixnum] days of month, e.g. 1, 2, -1, ...
|
data/lib/montrose/clock.rb
CHANGED
@@ -6,8 +6,9 @@ module Montrose
|
|
6
6
|
@options = Montrose::Options.merge(opts)
|
7
7
|
@time = nil
|
8
8
|
@every = @options.fetch(:every) { fail ConfigurationError, "Required option :every not provided" }
|
9
|
-
@starts = @options.fetch(:starts)
|
10
9
|
@interval = @options.fetch(:interval)
|
10
|
+
@start_time = @options.fetch(:start_time)
|
11
|
+
@at = @options.fetch(:at, nil)
|
11
12
|
end
|
12
13
|
|
13
14
|
# Advances time to new unit by increment and sets
|
@@ -18,13 +19,25 @@ module Montrose
|
|
18
19
|
end
|
19
20
|
|
20
21
|
def peek
|
21
|
-
return @
|
22
|
+
return @start_time if @time.nil?
|
22
23
|
|
23
|
-
@
|
24
|
+
if @at
|
25
|
+
times = @at.map { |(hour, min)| @time.change(hour: hour, min: min) }
|
26
|
+
|
27
|
+
min_next = times.select { |t| t > @time }.min and return min_next
|
28
|
+
|
29
|
+
advance_step(times.min || @time)
|
30
|
+
else
|
31
|
+
advance_step(@time)
|
32
|
+
end
|
24
33
|
end
|
25
34
|
|
26
35
|
private
|
27
36
|
|
37
|
+
def advance_step(time)
|
38
|
+
time.advance(step)
|
39
|
+
end
|
40
|
+
|
28
41
|
def step
|
29
42
|
@step ||= smallest_step or fail ConfigurationError, "No step for #{@options.inspect}"
|
30
43
|
end
|
data/lib/montrose/frequency.rb
CHANGED
data/lib/montrose/options.rb
CHANGED
@@ -1,5 +1,7 @@
|
|
1
1
|
module Montrose
|
2
2
|
class Options
|
3
|
+
include Montrose::Utils
|
4
|
+
|
3
5
|
@default_starts = nil
|
4
6
|
@default_until = nil
|
5
7
|
@default_every = nil
|
@@ -65,7 +67,6 @@ module Montrose
|
|
65
67
|
|
66
68
|
def default_options
|
67
69
|
{
|
68
|
-
starts: default_starts,
|
69
70
|
until: default_until,
|
70
71
|
interval: 1
|
71
72
|
}
|
@@ -86,6 +87,7 @@ module Montrose
|
|
86
87
|
def_option :between
|
87
88
|
def_option :at
|
88
89
|
def_option :on
|
90
|
+
def_option :except
|
89
91
|
|
90
92
|
def initialize(opts = {})
|
91
93
|
defaults = {
|
@@ -163,7 +165,7 @@ module Montrose
|
|
163
165
|
end
|
164
166
|
|
165
167
|
def day=(days)
|
166
|
-
@day = nested_map_arg(days) { |d|
|
168
|
+
@day = nested_map_arg(days) { |d| day_number!(d) }
|
167
169
|
end
|
168
170
|
|
169
171
|
def mday=(mdays)
|
@@ -179,7 +181,7 @@ module Montrose
|
|
179
181
|
end
|
180
182
|
|
181
183
|
def month=(months)
|
182
|
-
@month = map_arg(months) { |d|
|
184
|
+
@month = map_arg(months) { |d| month_number!(d) }
|
183
185
|
end
|
184
186
|
|
185
187
|
def between=(range)
|
@@ -194,11 +196,7 @@ module Montrose
|
|
194
196
|
end
|
195
197
|
|
196
198
|
def at=(time)
|
197
|
-
|
198
|
-
now = Time.now
|
199
|
-
first = times.map { |t| t < now ? t + 24.hours : t }.min
|
200
|
-
self[:starts] = first if first
|
201
|
-
@at = times
|
199
|
+
@at = map_arg(time) { |t| as_time_parts(t) }
|
202
200
|
end
|
203
201
|
|
204
202
|
def on=(arg)
|
@@ -209,12 +207,30 @@ module Montrose
|
|
209
207
|
@on = arg
|
210
208
|
end
|
211
209
|
|
210
|
+
def except=(date)
|
211
|
+
@except = map_arg(date) { |d| as_date(d) }
|
212
|
+
end
|
213
|
+
|
212
214
|
def inspect
|
213
215
|
"#<#{self.class} #{to_h.inspect}>"
|
214
216
|
end
|
215
217
|
|
218
|
+
def start_time
|
219
|
+
time = starts || default_starts
|
220
|
+
|
221
|
+
if at
|
222
|
+
at.map { |(hour, min)| time.change(hour: hour, min: min) }.min || time
|
223
|
+
else
|
224
|
+
time
|
225
|
+
end
|
226
|
+
end
|
227
|
+
|
216
228
|
private
|
217
229
|
|
230
|
+
def default_starts
|
231
|
+
self.class.default_starts
|
232
|
+
end
|
233
|
+
|
218
234
|
def nested_map_arg(arg, &block)
|
219
235
|
case arg
|
220
236
|
when Hash
|
@@ -233,7 +249,7 @@ module Montrose
|
|
233
249
|
end
|
234
250
|
|
235
251
|
def map_days(arg)
|
236
|
-
map_arg(arg) { |d|
|
252
|
+
map_arg(arg) { |d| day_number!(d) }
|
237
253
|
end
|
238
254
|
|
239
255
|
def map_mdays(arg)
|
@@ -275,9 +291,9 @@ module Montrose
|
|
275
291
|
end
|
276
292
|
|
277
293
|
def month_or_day(key)
|
278
|
-
month =
|
294
|
+
month = month_number(key)
|
279
295
|
return [:month, month] if month
|
280
|
-
day =
|
296
|
+
day = day_number(key)
|
281
297
|
return [:day, day] if day
|
282
298
|
fail ConfigurationError, "Did not recognize #{key} as a month or day"
|
283
299
|
end
|
@@ -289,17 +305,11 @@ module Montrose
|
|
289
305
|
item
|
290
306
|
end
|
291
307
|
|
292
|
-
def
|
293
|
-
return
|
308
|
+
def as_time_parts(arg)
|
309
|
+
return arg if arg.is_a?(Array)
|
294
310
|
|
295
|
-
|
296
|
-
|
297
|
-
Time.parse(time)
|
298
|
-
when time.respond_to?(:to_time)
|
299
|
-
time.to_time
|
300
|
-
else
|
301
|
-
Array(time).flat_map { |d| as_time(d) }
|
302
|
-
end
|
311
|
+
time = as_time(arg)
|
312
|
+
[time.hour, time.min]
|
303
313
|
end
|
304
314
|
|
305
315
|
def parse_frequency(input)
|
data/lib/montrose/rule.rb
CHANGED
@@ -38,6 +38,7 @@ require "montrose/rule/before"
|
|
38
38
|
require "montrose/rule/day_of_month"
|
39
39
|
require "montrose/rule/day_of_week"
|
40
40
|
require "montrose/rule/day_of_year"
|
41
|
+
require "montrose/rule/except"
|
41
42
|
require "montrose/rule/hour_of_day"
|
42
43
|
require "montrose/rule/month_of_year"
|
43
44
|
require "montrose/rule/nth_day_of_month"
|
@@ -0,0 +1,23 @@
|
|
1
|
+
module Montrose
|
2
|
+
module Rule
|
3
|
+
class Except
|
4
|
+
include Montrose::Rule
|
5
|
+
|
6
|
+
def self.apply_options(opts)
|
7
|
+
opts[:except]
|
8
|
+
end
|
9
|
+
|
10
|
+
# Initializes rule
|
11
|
+
#
|
12
|
+
# @param [Date] dates - array of date objects
|
13
|
+
#
|
14
|
+
def initialize(dates)
|
15
|
+
@dates = dates
|
16
|
+
end
|
17
|
+
|
18
|
+
def include?(time)
|
19
|
+
!@dates.include?(time.to_date)
|
20
|
+
end
|
21
|
+
end
|
22
|
+
end
|
23
|
+
end
|
@@ -16,7 +16,7 @@ module Montrose
|
|
16
16
|
end
|
17
17
|
|
18
18
|
def include?(time)
|
19
|
-
|
19
|
+
@times.include?(parts(time))
|
20
20
|
end
|
21
21
|
|
22
22
|
private
|
@@ -24,10 +24,6 @@ module Montrose
|
|
24
24
|
def parts(time)
|
25
25
|
[time.hour, time.min]
|
26
26
|
end
|
27
|
-
|
28
|
-
def times_of_day
|
29
|
-
@times_of_day ||= @times.map { |t| parts(t) }
|
30
|
-
end
|
31
27
|
end
|
32
28
|
end
|
33
29
|
end
|
data/lib/montrose/stack.rb
CHANGED
data/lib/montrose/utils.rb
CHANGED
@@ -5,6 +5,23 @@ module Montrose
|
|
5
5
|
MONTHS = Date::MONTHNAMES
|
6
6
|
DAYS = Date::DAYNAMES
|
7
7
|
|
8
|
+
def as_time(time)
|
9
|
+
return nil unless time
|
10
|
+
|
11
|
+
case
|
12
|
+
when time.is_a?(String)
|
13
|
+
Time.parse(time)
|
14
|
+
when time.respond_to?(:to_time)
|
15
|
+
time.to_time
|
16
|
+
else
|
17
|
+
Array(time).flat_map { |d| as_time(d) }
|
18
|
+
end
|
19
|
+
end
|
20
|
+
|
21
|
+
def as_date(time)
|
22
|
+
as_time(time).to_date
|
23
|
+
end
|
24
|
+
|
8
25
|
def month_number(name)
|
9
26
|
case name
|
10
27
|
when Symbol, String
|
data/lib/montrose/version.rb
CHANGED
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
|
+
version: 0.3.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-02-
|
11
|
+
date: 2016-02-20 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: activesupport
|
@@ -153,6 +153,7 @@ files:
|
|
153
153
|
- lib/montrose/rule/day_of_month.rb
|
154
154
|
- lib/montrose/rule/day_of_week.rb
|
155
155
|
- lib/montrose/rule/day_of_year.rb
|
156
|
+
- lib/montrose/rule/except.rb
|
156
157
|
- lib/montrose/rule/hour_of_day.rb
|
157
158
|
- lib/montrose/rule/month_of_year.rb
|
158
159
|
- lib/montrose/rule/nth_day_matcher.rb
|
@@ -186,7 +187,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
186
187
|
version: '0'
|
187
188
|
requirements: []
|
188
189
|
rubyforge_project:
|
189
|
-
rubygems_version: 2.5.1
|
190
|
+
rubygems_version: 2.4.5.1
|
190
191
|
signing_key:
|
191
192
|
specification_version: 4
|
192
193
|
summary: Recurring events in Ruby
|