montrose 0.10.0 → 0.12.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/.circleci/config.yml +65 -0
- data/Appraisals +6 -14
- data/CHANGELOG.md +13 -1
- data/Guardfile +2 -2
- data/README.md +57 -0
- data/Rakefile +2 -4
- data/bin/setup +1 -0
- data/bin/standardrb +29 -0
- data/gemfiles/activesupport_5.2.gemfile +4 -0
- data/gemfiles/activesupport_6.0.gemfile +5 -1
- data/gemfiles/{activesupport_4.2.gemfile → activesupport_6.1.gemfile} +5 -1
- data/lib/montrose.rb +23 -3
- data/lib/montrose/chainable.rb +43 -9
- data/lib/montrose/clock.rb +4 -4
- data/lib/montrose/frequency.rb +4 -4
- data/lib/montrose/options.rb +40 -16
- data/lib/montrose/recurrence.rb +29 -8
- data/lib/montrose/rule.rb +2 -1
- data/lib/montrose/rule/between.rb +1 -1
- data/lib/montrose/rule/covering.rb +40 -0
- data/lib/montrose/rule/during.rb +55 -0
- data/lib/montrose/rule/until.rb +1 -1
- data/lib/montrose/schedule.rb +112 -24
- data/lib/montrose/stack.rb +3 -2
- data/lib/montrose/utils.rb +6 -6
- data/lib/montrose/version.rb +1 -1
- data/montrose.gemspec +17 -17
- metadata +35 -37
- data/.rubocop.yml +0 -136
- data/.travis.yml +0 -33
- data/bin/rubocop +0 -16
- data/gemfiles/activesupport_4.1.gemfile +0 -12
- data/gemfiles/activesupport_5.0.gemfile +0 -12
- data/gemfiles/activesupport_5.1.gemfile +0 -12
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: d44082eaa05a068714f05fe6116f8442b0507b3c7612174d93716ce6fd4af4ba
|
4
|
+
data.tar.gz: 691d88452db689b1555772b6749ae12e72bf675fbc55d4082b2d7f4cd41dd795
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 389adad3b13244fdb642204fa158bde6a204de4c752cab7a11dc1fe2a237a98f7b73c905477fd335d47164b795921a92090b68a528327b115ff2721b8c2945f7
|
7
|
+
data.tar.gz: 9ce4b11ae2d6c0fd06b06ca9fe56aa05767bca879be8c5cd3a24dae1c7fed442ca50d4195cf5225ff42f1bb2f2704a9ab8fb308b0a9167865317a245dd782441
|
@@ -0,0 +1,65 @@
|
|
1
|
+
version: 2.1
|
2
|
+
orbs:
|
3
|
+
ruby: circleci/ruby@1.1.2
|
4
|
+
commands:
|
5
|
+
run-tests:
|
6
|
+
description: Run tests
|
7
|
+
steps:
|
8
|
+
- run: bundle exec rake test
|
9
|
+
restore:
|
10
|
+
description: Restore cache
|
11
|
+
steps:
|
12
|
+
- restore_cache:
|
13
|
+
keys:
|
14
|
+
- v1_bundler_deps-
|
15
|
+
save:
|
16
|
+
description: Save cache
|
17
|
+
steps:
|
18
|
+
- save_cache:
|
19
|
+
paths:
|
20
|
+
- ./vendor/bundle
|
21
|
+
key: v1-bundler-deps-{{ .Environment.CIRCLE_JOB }}
|
22
|
+
bundle:
|
23
|
+
description: Install dependencies
|
24
|
+
steps:
|
25
|
+
- run:
|
26
|
+
echo "export BUNDLE_JOBS=4" >> $BASH_ENV
|
27
|
+
echo "export BUNDLE_RETRY=3" >> $BASH_ENV
|
28
|
+
echo "export BUNDLE_PATH=$(pwd)/vendor/bundle" >> $BASH_ENV
|
29
|
+
echo "export BUNDLE_GEMFILE=$(pwd)/${GEMFILE_NAME}" >> $BASH_ENV
|
30
|
+
- run: bundle install
|
31
|
+
|
32
|
+
jobs:
|
33
|
+
test:
|
34
|
+
parameters:
|
35
|
+
ruby_version:
|
36
|
+
type: string
|
37
|
+
gemfile:
|
38
|
+
type: string
|
39
|
+
docker:
|
40
|
+
- image: 'circleci/ruby:<< parameters.ruby_version >>'
|
41
|
+
environment:
|
42
|
+
GEMFILE_NAME: <<parameters.gemfile>>
|
43
|
+
steps:
|
44
|
+
- checkout
|
45
|
+
- restore
|
46
|
+
- bundle
|
47
|
+
- save
|
48
|
+
- run-tests
|
49
|
+
|
50
|
+
workflows:
|
51
|
+
all-tests:
|
52
|
+
jobs:
|
53
|
+
- test:
|
54
|
+
matrix:
|
55
|
+
parameters:
|
56
|
+
ruby_version: ['2.5', '2.6', '2.7']
|
57
|
+
gemfile:
|
58
|
+
[
|
59
|
+
'gemfiles/activesupport_5.2.gemfile',
|
60
|
+
'gemfiles/activesupport_6.0.gemfile',
|
61
|
+
'gemfiles/activesupport_6.1.gemfile',
|
62
|
+
]
|
63
|
+
# exclude:
|
64
|
+
# - ruby_version: '3.0'
|
65
|
+
# - gemfile: rails_5_2.gemfile
|
data/Appraisals
CHANGED
@@ -1,21 +1,13 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
-
appraise "activesupport-
|
4
|
-
gem "activesupport", "~>
|
5
|
-
end
|
6
|
-
|
7
|
-
appraise "activesupport-5.1" do
|
8
|
-
gem "activesupport", "~> 5.1.0"
|
3
|
+
appraise "activesupport-6.1" do
|
4
|
+
gem "activesupport", "~> 6.1.0"
|
9
5
|
end
|
10
6
|
|
11
|
-
appraise "activesupport-
|
12
|
-
gem "activesupport", "~>
|
7
|
+
appraise "activesupport-6.0" do
|
8
|
+
gem "activesupport", "~> 6.0.0"
|
13
9
|
end
|
14
10
|
|
15
|
-
appraise "activesupport-
|
16
|
-
gem "activesupport", "~>
|
17
|
-
end
|
18
|
-
|
19
|
-
appraise "activesupport-4.1" do
|
20
|
-
gem "activesupport", "~> 4.1.0"
|
11
|
+
appraise "activesupport-5.2" do
|
12
|
+
gem "activesupport", "~> 5.2.0"
|
21
13
|
end
|
data/CHANGELOG.md
CHANGED
@@ -1,3 +1,15 @@
|
|
1
|
+
### 0.11.0 - (2019-08-16)
|
2
|
+
|
3
|
+
* enhancements
|
4
|
+
* Adds `Recurrence#during` to support recurrences within time-of-day ranges,
|
5
|
+
e.g. `Montrose.every(20.minutes).during("9am-5pm")`
|
6
|
+
|
7
|
+
### 0.10.1 - (2019-07-22)
|
8
|
+
|
9
|
+
* enhancements
|
10
|
+
* Adds `Schedule.dump` and `Schedule.load` to support ActiveRecord column
|
11
|
+
serialization
|
12
|
+
|
1
13
|
### 0.10.0 - (2019-07-17)
|
2
14
|
|
3
15
|
* enhancements
|
@@ -61,7 +73,7 @@
|
|
61
73
|
* Previously, the :between option served as a shorthand for :starts to :until.
|
62
74
|
Now, when both :starts and :between are provided, the recurrence will behave
|
63
75
|
as if anchored by the given :starts option, but filtered through the given
|
64
|
-
:
|
76
|
+
:between option.
|
65
77
|
* The :exclude_end option changes the default behavior of :until--when the
|
66
78
|
timestamp of the interval matches the :until timestamp, it will be included
|
67
79
|
by default unless the :exclude_end option is set to true.
|
data/Guardfile
CHANGED
@@ -25,8 +25,8 @@ guard :minitest do
|
|
25
25
|
|
26
26
|
# with Minitest::Spec
|
27
27
|
watch(%r{^spec/(.*)_spec\.rb$})
|
28
|
-
watch(%r{^lib/(.+)\.rb$})
|
29
|
-
watch(%r{^lib/(.+)\.rb$})
|
28
|
+
watch(%r{^lib/(.+)\.rb$}) { |m| "spec/#{m[1]}_spec.rb" }
|
29
|
+
watch(%r{^lib/(.+)\.rb$}) { "spec/rfc_spec.rb" }
|
30
30
|
watch(%r{^spec/spec_helper\.rb$}) { "spec" }
|
31
31
|
end
|
32
32
|
|
data/README.md
CHANGED
@@ -277,6 +277,15 @@ Montrose.every(90.minutes, total: 4)
|
|
277
277
|
# every 20 minutes from 9:00 AM to 4:40 PM every day
|
278
278
|
Montrose.every(20.minutes, hour: 9..16)
|
279
279
|
|
280
|
+
# every 20 minutes from 9:00 AM to 4:40 PM every day with time-of-day precision
|
281
|
+
r = Montrose.every(20.minutes)
|
282
|
+
r.during("9am-4:40pm") # as semantic time-of-day range OR
|
283
|
+
r.during(time.change(hour: 9)..time.change(hour: 4: min: 40)) # as ruby time range OR
|
284
|
+
r.during([9, 0, 0], [16, 40, 0]) # as hour, min, sec tuple pairs for start, end
|
285
|
+
|
286
|
+
# every 20 minutes during multiple time-of-day ranges
|
287
|
+
Montrose.every(20.minutes).during("9am-12pm", "1pm-5pm")
|
288
|
+
|
280
289
|
# Minutely
|
281
290
|
Montrose.minutely
|
282
291
|
Montrose.r(every: :minute)
|
@@ -366,6 +375,40 @@ r.events.take(10).each { |date| puts date.to_s }
|
|
366
375
|
r.events.lazy.select { |time| time > 1.month.from_now }.take(3).each { |date| puts date.to_s }
|
367
376
|
```
|
368
377
|
|
378
|
+
### Combining recurrences
|
379
|
+
|
380
|
+
It may be necessary to combine several recurrence rules into a single
|
381
|
+
enumeration of events. For this purpose, there is `Montrose::Schedule`. To create a schedule of multiple recurrences:
|
382
|
+
|
383
|
+
```ruby
|
384
|
+
recurrence_1 = Montrose.monthly(day: { friday: [1] })
|
385
|
+
recurrence_2 = Montrose.weekly(on: :tuesday)
|
386
|
+
|
387
|
+
schedule = Montrose::Schedule.build do |s|
|
388
|
+
s << recurrence_1
|
389
|
+
s << recurrence_2
|
390
|
+
end
|
391
|
+
|
392
|
+
# add after building
|
393
|
+
s << Montrose.yearly
|
394
|
+
```
|
395
|
+
The `Schedule#<<` method also accepts valid recurrence options as hashes:
|
396
|
+
```ruby
|
397
|
+
schedule = Montrose::Schedule.build do |s|
|
398
|
+
s << { day: { friday: [1] } }
|
399
|
+
s << { on: :tuesday }
|
400
|
+
end
|
401
|
+
```
|
402
|
+
A schedule acts like a collection of recurrence rules that also behaves as a single
|
403
|
+
stream of events:
|
404
|
+
|
405
|
+
```ruby
|
406
|
+
schedule.events # => #<Enumerator: ...>
|
407
|
+
schedule.each do |event|
|
408
|
+
puts event
|
409
|
+
end
|
410
|
+
```
|
411
|
+
|
369
412
|
### Ruby on Rails
|
370
413
|
|
371
414
|
Instances of `Montrose::Recurrence` support the ActiveRecord serialization API so recurrence objects can be marshalled to and from a single database column:
|
@@ -374,6 +417,13 @@ Instances of `Montrose::Recurrence` support the ActiveRecord serialization API s
|
|
374
417
|
class RecurringEvent < ApplicationRecord
|
375
418
|
serialize :recurrence, Montrose::Recurrence
|
376
419
|
|
420
|
+
end
|
421
|
+
```
|
422
|
+
`Montrose::Schedule` can also be serialized:
|
423
|
+
```ruby
|
424
|
+
class RecurringEvent < ApplicationRecord
|
425
|
+
serialize :recurrence, Montrose::Schedule
|
426
|
+
|
377
427
|
end
|
378
428
|
```
|
379
429
|
|
@@ -396,6 +446,13 @@ After checking out the repo, run `bin/setup` to install dependencies. Then, run
|
|
396
446
|
|
397
447
|
You can also run `bin/console` for an interactive prompt that will allow you to experiment.
|
398
448
|
|
449
|
+
To run tests against multiple versions of activesupport, use the Appraisals:
|
450
|
+
|
451
|
+
```sh
|
452
|
+
bin/appraisal install
|
453
|
+
bin/appraisal rake test
|
454
|
+
```
|
455
|
+
|
399
456
|
## Contributing
|
400
457
|
|
401
458
|
Bug reports and pull requests are welcome on GitHub at https://github.com/rossta/montrose. This project is intended to be a safe, welcoming space for collaboration, and contributors are expected to adhere to the [Contributor Covenant](contributor-covenant.org) code of conduct.
|
data/Rakefile
CHANGED
@@ -3,7 +3,7 @@
|
|
3
3
|
require "bundler/setup"
|
4
4
|
require "bundler/gem_tasks"
|
5
5
|
require "rake/testtask"
|
6
|
-
require "
|
6
|
+
require "standard/rake"
|
7
7
|
require "yard"
|
8
8
|
|
9
9
|
Rake::TestTask.new(:spec) do |t|
|
@@ -15,9 +15,7 @@ end
|
|
15
15
|
|
16
16
|
task test: :spec
|
17
17
|
|
18
|
-
|
19
|
-
|
20
|
-
task default: %i[spec rubocop]
|
18
|
+
task default: %i[spec standard]
|
21
19
|
|
22
20
|
namespace :doc do
|
23
21
|
desc "Generate docs and publish to gh-pages"
|
data/bin/setup
CHANGED
data/bin/standardrb
ADDED
@@ -0,0 +1,29 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
# frozen_string_literal: true
|
3
|
+
|
4
|
+
#
|
5
|
+
# This file was generated by Bundler.
|
6
|
+
#
|
7
|
+
# The application 'standardrb' is installed as part of a gem, and
|
8
|
+
# this file is here to facilitate running it.
|
9
|
+
#
|
10
|
+
|
11
|
+
require "pathname"
|
12
|
+
ENV["BUNDLE_GEMFILE"] ||= File.expand_path("../../Gemfile",
|
13
|
+
Pathname.new(__FILE__).realpath)
|
14
|
+
|
15
|
+
bundle_binstub = File.expand_path("../bundle", __FILE__)
|
16
|
+
|
17
|
+
if File.file?(bundle_binstub)
|
18
|
+
if File.read(bundle_binstub, 300) =~ /This file was generated by Bundler/
|
19
|
+
load(bundle_binstub)
|
20
|
+
else
|
21
|
+
abort("Your `bin/bundle` was not generated by Bundler, so this binstub cannot run.
|
22
|
+
Replace `bin/bundle` by running `bundle binstubs bundler --force`, then run this command again.")
|
23
|
+
end
|
24
|
+
end
|
25
|
+
|
26
|
+
require "rubygems"
|
27
|
+
require "bundler/setup"
|
28
|
+
|
29
|
+
load Gem.bin_path("standard", "standardrb")
|
@@ -2,11 +2,15 @@
|
|
2
2
|
|
3
3
|
source "https://rubygems.org"
|
4
4
|
|
5
|
-
gem "activesupport", "~> 6.0.0
|
5
|
+
gem "activesupport", "~> 6.0.0"
|
6
6
|
|
7
7
|
group :development do
|
8
8
|
gem "coveralls"
|
9
9
|
gem "yard"
|
10
|
+
gem "guard"
|
11
|
+
gem "guard-minitest"
|
12
|
+
gem "guard-rubocop"
|
13
|
+
gem "pry-byebug"
|
10
14
|
end
|
11
15
|
|
12
16
|
gemspec path: "../"
|
@@ -2,11 +2,15 @@
|
|
2
2
|
|
3
3
|
source "https://rubygems.org"
|
4
4
|
|
5
|
-
gem "activesupport", "~>
|
5
|
+
gem "activesupport", "~> 6.1.0"
|
6
6
|
|
7
7
|
group :development do
|
8
8
|
gem "coveralls"
|
9
9
|
gem "yard"
|
10
|
+
gem "guard"
|
11
|
+
gem "guard-minitest"
|
12
|
+
gem "guard-rubocop"
|
13
|
+
gem "pry-byebug"
|
10
14
|
end
|
11
15
|
|
12
16
|
gemspec path: "../"
|
data/lib/montrose.rb
CHANGED
@@ -2,10 +2,13 @@
|
|
2
2
|
|
3
3
|
require "active_support"
|
4
4
|
require "active_support/core_ext/object"
|
5
|
-
|
5
|
+
|
6
6
|
require "active_support/core_ext/date"
|
7
|
-
require "active_support/core_ext/time"
|
8
7
|
require "active_support/core_ext/date_time"
|
8
|
+
require "active_support/core_ext/integer"
|
9
|
+
require "active_support/core_ext/numeric"
|
10
|
+
require "active_support/core_ext/string"
|
11
|
+
require "active_support/core_ext/time"
|
9
12
|
|
10
13
|
require "montrose/utils"
|
11
14
|
require "montrose/rule"
|
@@ -35,6 +38,23 @@ module Montrose
|
|
35
38
|
def recurrence(options = {})
|
36
39
|
branch(options)
|
37
40
|
end
|
38
|
-
|
41
|
+
alias_method :r, :recurrence
|
42
|
+
|
43
|
+
# Create a new recurrence from given options
|
44
|
+
# An alias to {Montrose::Recurrence.new}
|
45
|
+
attr_reader :enable_deprecated_between_masking
|
46
|
+
|
47
|
+
def enable_deprecated_between_masking=(value)
|
48
|
+
warn "[DEPRECATION] Montrose.enable_deprecated_between_masking is deprecated and will be removed in a future version."
|
49
|
+
@enable_deprecated_between_masking = value
|
50
|
+
end
|
51
|
+
|
52
|
+
def enable_deprecated_between_masking?
|
53
|
+
result = !!enable_deprecated_between_masking
|
54
|
+
if result
|
55
|
+
warn "[DEPRECATION] Legacy Montrose.between masking behavior is deprecated. Please use Montrose.covering instead to retain this behavior."
|
56
|
+
end
|
57
|
+
result
|
58
|
+
end
|
39
59
|
end
|
40
60
|
end
|
data/lib/montrose/chainable.rb
CHANGED
@@ -139,7 +139,7 @@ module Montrose
|
|
139
139
|
def starts(starts_at)
|
140
140
|
merge(starts: starts_at)
|
141
141
|
end
|
142
|
-
|
142
|
+
alias_method :starting, :starts
|
143
143
|
|
144
144
|
# Create a recurrence ending at given timestamp.
|
145
145
|
#
|
@@ -153,9 +153,12 @@ module Montrose
|
|
153
153
|
def until(ends_at)
|
154
154
|
merge(until: ends_at)
|
155
155
|
end
|
156
|
-
|
156
|
+
alias_method :ending, :until
|
157
157
|
|
158
|
-
# Create a recurrence occurring
|
158
|
+
# Create a recurrence occurring between the start and end
|
159
|
+
# of a given date range; :between is shorthand for separate
|
160
|
+
# :starts and :until options. When used with explicit :start
|
161
|
+
# and/or :until options, those will take precedence.
|
159
162
|
#
|
160
163
|
# @param [Range<Date>] date_range
|
161
164
|
#
|
@@ -168,6 +171,37 @@ module Montrose
|
|
168
171
|
merge(between: date_range)
|
169
172
|
end
|
170
173
|
|
174
|
+
# Create a recurrence which will only emit values within the
|
175
|
+
# date range, also called "masking."
|
176
|
+
#
|
177
|
+
# @param [Range<Date>] date_range
|
178
|
+
#
|
179
|
+
# @example
|
180
|
+
# Montrose.weekly.covering(Date.tomorrow..Date.new(2016, 3, 15))
|
181
|
+
#
|
182
|
+
# @return [Montrose::Recurrence]
|
183
|
+
#
|
184
|
+
def covering(date_range)
|
185
|
+
merge(covering: date_range)
|
186
|
+
end
|
187
|
+
|
188
|
+
# Create a recurrence occurring within a time-of-day range or ranges.
|
189
|
+
# Given time ranges will parse as times-of-day and ignore given dates.
|
190
|
+
#
|
191
|
+
# @param [Range<Time>,String,Array<Array>] time-of-day range(s)
|
192
|
+
#
|
193
|
+
# @example
|
194
|
+
# Montrose.every(20.minutes).during("9am-5pm")
|
195
|
+
# Montrose.every(20.minutes).during(time.change(hour: 9)..time.change(hour: 5))
|
196
|
+
# Montrose.every(20.minutes).during([9, 0, 0], [17, 0, 0])
|
197
|
+
# Montrose.every(20.minutes).during("9am-12pm", "1pm-5pm")
|
198
|
+
#
|
199
|
+
# @return [Montrose::Recurrence]
|
200
|
+
#
|
201
|
+
def during(time_of_day, *extras)
|
202
|
+
merge(during: time_of_day.array_concat(extras))
|
203
|
+
end
|
204
|
+
|
171
205
|
# Create a recurrence through :on option
|
172
206
|
#
|
173
207
|
# @param day [Hash,Symbol] weekday or day of month as hash, e.g. { friday: 13 }
|
@@ -224,7 +258,7 @@ module Montrose
|
|
224
258
|
def day_of_month(days, *extras)
|
225
259
|
merge(mday: days.array_concat(extras))
|
226
260
|
end
|
227
|
-
|
261
|
+
alias_method :mday, :day_of_month
|
228
262
|
|
229
263
|
# Create a recurrence for given days of week
|
230
264
|
#
|
@@ -240,7 +274,7 @@ module Montrose
|
|
240
274
|
def day_of_week(weekdays, *extras)
|
241
275
|
merge(day: weekdays.array_concat(extras))
|
242
276
|
end
|
243
|
-
|
277
|
+
alias_method :day, :day_of_week
|
244
278
|
|
245
279
|
# Create a recurrence for given days of year
|
246
280
|
#
|
@@ -256,7 +290,7 @@ module Montrose
|
|
256
290
|
def day_of_year(days, *extras)
|
257
291
|
merge(yday: days.array_concat(extras))
|
258
292
|
end
|
259
|
-
|
293
|
+
alias_method :yday, :day_of_year
|
260
294
|
|
261
295
|
# Create a recurrence for given hours of day
|
262
296
|
#
|
@@ -272,7 +306,7 @@ module Montrose
|
|
272
306
|
def hour_of_day(hours, *extras)
|
273
307
|
merge(hour: hours.array_concat(extras))
|
274
308
|
end
|
275
|
-
|
309
|
+
alias_method :hour, :hour_of_day
|
276
310
|
|
277
311
|
# Create a recurrence for given months of year
|
278
312
|
#
|
@@ -288,7 +322,7 @@ module Montrose
|
|
288
322
|
def month_of_year(months, *extras)
|
289
323
|
merge(month: months.array_concat(extras))
|
290
324
|
end
|
291
|
-
|
325
|
+
alias_method :month, :month_of_year
|
292
326
|
|
293
327
|
# Create a recurrence for given weeks of year
|
294
328
|
#
|
@@ -318,7 +352,7 @@ module Montrose
|
|
318
352
|
def total(total)
|
319
353
|
merge(total: total)
|
320
354
|
end
|
321
|
-
|
355
|
+
alias_method :repeat, :total
|
322
356
|
|
323
357
|
# Create a new recurrence combining options of self
|
324
358
|
# and other. The value of entries with duplicate
|