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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: edcfdb878952330a2fcc9a78db4f4e7780e643e3
4
- data.tar.gz: 619002eee045e0d960de83d2bdf78078d7d74883
3
+ metadata.gz: a88cd103fc68290cf832c8e41ec2e8a1ca4d4a87
4
+ data.tar.gz: c74c51dad1844316155c1b138b0b34f548d27dfd
5
5
  SHA512:
6
- metadata.gz: 7be4df5c22b3677978134b446d30c7e3bac3e33309d35f66253aea93ed62d5e60a17bf9b1a8eb8a3745260ef38dee9f74d7f057a0b2fa181f752e7a3183336b1
7
- data.tar.gz: 5a1c3a19e2e5fc6a7b64c620be48926d9d257e33b3b2fde91b0588b3d1355052b0b016e011ec7cf396f2046a2e3005b10fab43de0cd648224b55b7b90383f1c4
6
+ metadata.gz: 3d186764038aaeb902fb809225a5e26779831e55a3ed0cc11998a6395821c9ab4e34af9d0152f43558f660c39d02ad9e2eff82630e24d56986e1958d0ff51de6
7
+ data.tar.gz: 4c939c9c4e6a416f20fb24b718d6fab5026b17650f72e5769b03abaa40ecc10d002a6a8210cd0ec6ae44c832d41020a222cf5e248597ede1754d830e8587d92b
@@ -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
@@ -1,6 +1,5 @@
1
1
  source "https://rubygems.org"
2
2
 
3
- # Specify your gem's dependencies in cure.gemspec
4
3
  gemspec
5
4
 
6
5
  group :development do
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
- [Introduction](https://rossta.net/blog/recurring-events-in-ruby.html)
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
 
@@ -30,7 +30,9 @@ module Montrose
30
30
  #
31
31
  # @return [Montrose::Recurrence]
32
32
  #
33
- alias recurrence branch
34
- alias r branch
33
+ def recurrence(options = {})
34
+ branch(options)
35
+ end
36
+ alias r recurrence
35
37
  end
36
38
  end
@@ -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, ...
@@ -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 @starts if @time.nil?
22
+ return @start_time if @time.nil?
22
23
 
23
- @time.advance(step)
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
@@ -45,7 +45,7 @@ module Montrose
45
45
  def initialize(opts = {})
46
46
  opts = Montrose::Options.merge(opts)
47
47
  @time = nil
48
- @starts = opts.fetch(:starts)
48
+ @starts = opts.fetch(:start_time)
49
49
  @interval = opts.fetch(:interval)
50
50
  end
51
51
 
@@ -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| Montrose::Utils.day_number!(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| Montrose::Utils.month_number!(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
- times = map_arg(time) { |t| as_time(t) }
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| Montrose::Utils.day_number!(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 = Montrose::Utils.month_number(key)
294
+ month = month_number(key)
279
295
  return [:month, month] if month
280
- day = Montrose::Utils.day_number(key)
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 as_time(time)
293
- return nil unless time
308
+ def as_time_parts(arg)
309
+ return arg if arg.is_a?(Array)
294
310
 
295
- case
296
- when time.is_a?(String)
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)
@@ -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
- times_of_day.include?(parts(time))
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
@@ -11,6 +11,7 @@ module Montrose
11
11
  Frequency,
12
12
  Rule::After,
13
13
  Rule::Before,
14
+ Rule::Except,
14
15
  Rule::Total,
15
16
  Rule::TimeOfDay,
16
17
  Rule::HourOfDay,
@@ -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
@@ -1,4 +1,4 @@
1
1
  # frozen_string_literal: true
2
2
  module Montrose
3
- VERSION = "0.2.2".freeze
3
+ VERSION = "0.3.0".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.2
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-09 00:00:00.000000000 Z
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