montrose 0.2.2 → 0.3.0
Sign up to get free protection for your applications and to get access to all the features.
- 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
|