montrose 0.11.0 → 0.13.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 +66 -0
- data/Appraisals +8 -12
- data/CHANGELOG.md +24 -0
- data/Guardfile +2 -2
- data/README.md +27 -15
- data/Rakefile +2 -4
- data/bin/setup +1 -0
- data/bin/standardrb +29 -0
- data/gemfiles/activesupport_5.2.gemfile +5 -1
- data/gemfiles/activesupport_6.0.gemfile +5 -1
- data/gemfiles/{activesupport_4.2.gemfile → activesupport_6.1.gemfile} +5 -1
- data/gemfiles/{activesupport_5.0.gemfile → activesupport_7.0.gemfile} +5 -1
- data/lib/montrose/chainable.rb +26 -10
- data/lib/montrose/clock.rb +4 -4
- data/lib/montrose/day.rb +83 -0
- data/lib/montrose/frequency.rb +58 -25
- data/lib/montrose/hour.rb +22 -0
- data/lib/montrose/ical.rb +128 -0
- data/lib/montrose/minute.rb +22 -0
- data/lib/montrose/month.rb +47 -0
- data/lib/montrose/month_day.rb +25 -0
- data/lib/montrose/options.rb +73 -79
- data/lib/montrose/recurrence.rb +40 -13
- data/lib/montrose/rule/between.rb +1 -1
- data/lib/montrose/rule/covering.rb +40 -0
- data/lib/montrose/rule/during.rb +7 -15
- data/lib/montrose/rule/minute_of_hour.rb +25 -0
- data/lib/montrose/rule/nth_day_of_month.rb +0 -2
- data/lib/montrose/rule/nth_day_of_year.rb +0 -2
- data/lib/montrose/rule/time_of_day.rb +1 -1
- data/lib/montrose/rule/until.rb +1 -1
- data/lib/montrose/rule.rb +18 -16
- data/lib/montrose/schedule.rb +6 -8
- data/lib/montrose/stack.rb +3 -4
- data/lib/montrose/time_of_day.rb +48 -0
- data/lib/montrose/utils.rb +2 -40
- data/lib/montrose/version.rb +1 -1
- data/lib/montrose/week.rb +20 -0
- data/lib/montrose/year_day.rb +25 -0
- data/lib/montrose.rb +43 -11
- data/montrose.gemspec +17 -17
- metadata +43 -36
- 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.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: c4b7914b70270912f8acf6757a8f130112d9137c50c34b933714f0edf97ac3c1
|
4
|
+
data.tar.gz: d70713d758bf62b45a7a74196f3d2c5bcc0a4bcf48b899164d7b5d4e2afe6b4a
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 5ea9a0877d09b1d360c3de7dc9b68a4fd9b6f64047728991d0398333777c18afa06f12fba1b2e178f53bd412f054314b125a174099a896c554cc53a337672fb5
|
7
|
+
data.tar.gz: f0b7431307846f0fa969352cfb98101ae2542ad37dd443e8324dd1480b01edfa81f8c06f92b6dc96f4278410f7cb221b7a1c1a487241a0faf8f5ee07fd763db3
|
@@ -0,0 +1,66 @@
|
|
1
|
+
version: 2.1
|
2
|
+
orbs:
|
3
|
+
ruby: circleci/ruby@1.3.0
|
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: 'cimg/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.6', '2.7', '3.0', '3.1']
|
57
|
+
gemfile:
|
58
|
+
[
|
59
|
+
'gemfiles/activesupport_5.2.gemfile',
|
60
|
+
'gemfiles/activesupport_6.0.gemfile',
|
61
|
+
'gemfiles/activesupport_6.1.gemfile',
|
62
|
+
'gemfiles/activesupport_7.0.gemfile',
|
63
|
+
]
|
64
|
+
exclude:
|
65
|
+
- ruby_version: '2.6'
|
66
|
+
gemfile: gemfiles/activesupport_7.0.gemfile
|
data/Appraisals
CHANGED
@@ -1,21 +1,17 @@
|
|
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-7.0" do
|
4
|
+
gem "activesupport", "~> 7.0"
|
9
5
|
end
|
10
6
|
|
11
|
-
appraise "activesupport-
|
12
|
-
gem "activesupport", "~>
|
7
|
+
appraise "activesupport-6.1" do
|
8
|
+
gem "activesupport", "~> 6.1"
|
13
9
|
end
|
14
10
|
|
15
|
-
appraise "activesupport-
|
16
|
-
gem "activesupport", "~>
|
11
|
+
appraise "activesupport-6.0" do
|
12
|
+
gem "activesupport", "~> 6.0"
|
17
13
|
end
|
18
14
|
|
19
|
-
appraise "activesupport-
|
20
|
-
gem "activesupport", "~>
|
15
|
+
appraise "activesupport-5.2" do
|
16
|
+
gem "activesupport", "~> 5.2"
|
21
17
|
end
|
data/CHANGELOG.md
CHANGED
@@ -1,3 +1,27 @@
|
|
1
|
+
### 0.12.0 - (2021-02-02)
|
2
|
+
|
3
|
+
* enhancements
|
4
|
+
* Adds `Montrose.covering` to disambiguate `Montrose.between` behavior
|
5
|
+
`#covering` provides recurrence masking behavior, i.e., only recurrences
|
6
|
+
within the given range will be emitted
|
7
|
+
* Added support for ActiveSupport 6 and Ruby 2.7
|
8
|
+
* Adds `Montrose#infinite?` and ensures `Montrose.finite?` returns a boolean
|
9
|
+
|
10
|
+
* bug fixes
|
11
|
+
* Fixes `Recurrence#include?` behavior for infinite recurrences with
|
12
|
+
intervals > 1
|
13
|
+
|
14
|
+
* breaking changes
|
15
|
+
* `Montrose.between` no longer provides masking behavior, which is now
|
16
|
+
provided by `Montrose.covering`. A global option can be used
|
17
|
+
`Montrose.enable_deprecated_between_masking = true` to retain the legacy
|
18
|
+
behavior for `Montrose.between`. This option will be removed in v1.0.
|
19
|
+
* Dropped official support for EOL'd rubies and ActiveSupport < 5.2
|
20
|
+
|
21
|
+
* miscellaneous
|
22
|
+
* switched from Travis to CircleCi for builds
|
23
|
+
* switched default branch to `main`
|
24
|
+
#
|
1
25
|
### 0.11.0 - (2019-08-16)
|
2
26
|
|
3
27
|
* enhancements
|
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
@@ -4,10 +4,10 @@
|
|
4
4
|
[](https://codeclimate.com/github/rossta/montrose)
|
5
5
|
[](https://coveralls.io/github/rossta/montrose?branch=master)
|
6
6
|
|
7
|
-
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).
|
7
|
+
Montrose is an easy-to-use library for defining recurring events in Ruby. It uses a simple chaining system for building enumerable 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).
|
8
8
|
|
9
|
-
|
10
|
-
|
9
|
+
- [Introductory blog post](http://bit.ly/1PA68Zb)
|
10
|
+
- [NYC.rb
|
11
11
|
presentation](https://speakerdeck.com/rossta/recurring-events-with-montrose)
|
12
12
|
|
13
13
|
## Installation
|
@@ -32,17 +32,17 @@ Dealing with recurring events is hard. `Montrose` provides a simple interface fo
|
|
32
32
|
|
33
33
|
More specifically, this project intends to:
|
34
34
|
|
35
|
-
|
36
|
-
|
37
|
-
|
38
|
-
|
39
|
-
|
40
|
-
|
35
|
+
- model recurring events in Ruby
|
36
|
+
- embrace Ruby idioms
|
37
|
+
- support recent Rubies
|
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
41
|
|
42
42
|
What `Montrose` doesn't do:
|
43
43
|
|
44
|
-
|
45
|
-
|
44
|
+
- support all calendaring use cases under the sun
|
45
|
+
- schedule recurring jobs for your Rails app. Use one of these instead: [cron](https://en.wikipedia.org/wiki/Cron), [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
46
|
|
47
47
|
## Concepts
|
48
48
|
|
@@ -392,13 +392,16 @@ end
|
|
392
392
|
# add after building
|
393
393
|
s << Montrose.yearly
|
394
394
|
```
|
395
|
+
|
395
396
|
The `Schedule#<<` method also accepts valid recurrence options as hashes:
|
397
|
+
|
396
398
|
```ruby
|
397
399
|
schedule = Montrose::Schedule.build do |s|
|
398
400
|
s << { day: { friday: [1] } }
|
399
401
|
s << { on: :tuesday }
|
400
402
|
end
|
401
403
|
```
|
404
|
+
|
402
405
|
A schedule acts like a collection of recurrence rules that also behaves as a single
|
403
406
|
stream of events:
|
404
407
|
|
@@ -419,7 +422,9 @@ class RecurringEvent < ApplicationRecord
|
|
419
422
|
|
420
423
|
end
|
421
424
|
```
|
425
|
+
|
422
426
|
`Montrose::Schedule` can also be serialized:
|
427
|
+
|
423
428
|
```ruby
|
424
429
|
class RecurringEvent < ApplicationRecord
|
425
430
|
serialize :recurrence, Montrose::Schedule
|
@@ -435,10 +440,10 @@ Montrose is named after the beautifully diverse and artistic [neighborhood in Ho
|
|
435
440
|
|
436
441
|
Check out following related projects, all of which have provided inspiration for `Montrose`.
|
437
442
|
|
438
|
-
|
439
|
-
|
440
|
-
|
441
|
-
|
443
|
+
- [ice_cube](https://github.com/seejohnrun/ice_cube)
|
444
|
+
- [recurrence](https://github.com/fnando/recurrence)
|
445
|
+
- [runt](https://github.com/mlipper/runt)
|
446
|
+
- [http.rb](https://github.com/httprb/http) - not a recurrence project, but inspirational to design, implementation, and interface of `Montrose`
|
442
447
|
|
443
448
|
## Development
|
444
449
|
|
@@ -446,6 +451,13 @@ After checking out the repo, run `bin/setup` to install dependencies. Then, run
|
|
446
451
|
|
447
452
|
You can also run `bin/console` for an interactive prompt that will allow you to experiment.
|
448
453
|
|
454
|
+
To run tests against multiple versions of activesupport, use the Appraisals:
|
455
|
+
|
456
|
+
```sh
|
457
|
+
bin/appraisal install
|
458
|
+
bin/appraisal rake test
|
459
|
+
```
|
460
|
+
|
449
461
|
## Contributing
|
450
462
|
|
451
463
|
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", "~> 5.2
|
5
|
+
gem "activesupport", "~> 5.2"
|
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", "~> 6.0
|
5
|
+
gem "activesupport", "~> 6.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"
|
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", "~> 7.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/chainable.rb
CHANGED
@@ -1,6 +1,5 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
-
require "montrose/options"
|
4
3
|
require "montrose/refinements/array_concat"
|
5
4
|
|
6
5
|
module Montrose
|
@@ -139,7 +138,7 @@ module Montrose
|
|
139
138
|
def starts(starts_at)
|
140
139
|
merge(starts: starts_at)
|
141
140
|
end
|
142
|
-
|
141
|
+
alias_method :starting, :starts
|
143
142
|
|
144
143
|
# Create a recurrence ending at given timestamp.
|
145
144
|
#
|
@@ -153,9 +152,12 @@ module Montrose
|
|
153
152
|
def until(ends_at)
|
154
153
|
merge(until: ends_at)
|
155
154
|
end
|
156
|
-
|
155
|
+
alias_method :ending, :until
|
157
156
|
|
158
|
-
# Create a recurrence occurring
|
157
|
+
# Create a recurrence occurring between the start and end
|
158
|
+
# of a given date range; :between is shorthand for separate
|
159
|
+
# :starts and :until options. When used with explicit :start
|
160
|
+
# and/or :until options, those will take precedence.
|
159
161
|
#
|
160
162
|
# @param [Range<Date>] date_range
|
161
163
|
#
|
@@ -168,6 +170,20 @@ module Montrose
|
|
168
170
|
merge(between: date_range)
|
169
171
|
end
|
170
172
|
|
173
|
+
# Create a recurrence which will only emit values within the
|
174
|
+
# date range, also called "masking."
|
175
|
+
#
|
176
|
+
# @param [Range<Date>] date_range
|
177
|
+
#
|
178
|
+
# @example
|
179
|
+
# Montrose.weekly.covering(Date.tomorrow..Date.new(2016, 3, 15))
|
180
|
+
#
|
181
|
+
# @return [Montrose::Recurrence]
|
182
|
+
#
|
183
|
+
def covering(date_range)
|
184
|
+
merge(covering: date_range)
|
185
|
+
end
|
186
|
+
|
171
187
|
# Create a recurrence occurring within a time-of-day range or ranges.
|
172
188
|
# Given time ranges will parse as times-of-day and ignore given dates.
|
173
189
|
#
|
@@ -241,7 +257,7 @@ module Montrose
|
|
241
257
|
def day_of_month(days, *extras)
|
242
258
|
merge(mday: days.array_concat(extras))
|
243
259
|
end
|
244
|
-
|
260
|
+
alias_method :mday, :day_of_month
|
245
261
|
|
246
262
|
# Create a recurrence for given days of week
|
247
263
|
#
|
@@ -257,7 +273,7 @@ module Montrose
|
|
257
273
|
def day_of_week(weekdays, *extras)
|
258
274
|
merge(day: weekdays.array_concat(extras))
|
259
275
|
end
|
260
|
-
|
276
|
+
alias_method :day, :day_of_week
|
261
277
|
|
262
278
|
# Create a recurrence for given days of year
|
263
279
|
#
|
@@ -273,7 +289,7 @@ module Montrose
|
|
273
289
|
def day_of_year(days, *extras)
|
274
290
|
merge(yday: days.array_concat(extras))
|
275
291
|
end
|
276
|
-
|
292
|
+
alias_method :yday, :day_of_year
|
277
293
|
|
278
294
|
# Create a recurrence for given hours of day
|
279
295
|
#
|
@@ -289,7 +305,7 @@ module Montrose
|
|
289
305
|
def hour_of_day(hours, *extras)
|
290
306
|
merge(hour: hours.array_concat(extras))
|
291
307
|
end
|
292
|
-
|
308
|
+
alias_method :hour, :hour_of_day
|
293
309
|
|
294
310
|
# Create a recurrence for given months of year
|
295
311
|
#
|
@@ -305,7 +321,7 @@ module Montrose
|
|
305
321
|
def month_of_year(months, *extras)
|
306
322
|
merge(month: months.array_concat(extras))
|
307
323
|
end
|
308
|
-
|
324
|
+
alias_method :month, :month_of_year
|
309
325
|
|
310
326
|
# Create a recurrence for given weeks of year
|
311
327
|
#
|
@@ -335,7 +351,7 @@ module Montrose
|
|
335
351
|
def total(total)
|
336
352
|
merge(total: total)
|
337
353
|
end
|
338
|
-
|
354
|
+
alias_method :repeat, :total
|
339
355
|
|
340
356
|
# Create a new recurrence combining options of self
|
341
357
|
# and other. The value of entries with duplicate
|
data/lib/montrose/clock.rb
CHANGED
@@ -26,7 +26,7 @@ module Montrose
|
|
26
26
|
if @at
|
27
27
|
times = @at.map { |hour, min, sec = 0| @time.change(hour: hour, min: min, sec: sec) }
|
28
28
|
|
29
|
-
min_next = times.select { |t| t > @time }.min
|
29
|
+
(min_next = times.select { |t| t > @time }.min) && (return min_next)
|
30
30
|
|
31
31
|
advance_step(times.min || @time)
|
32
32
|
else
|
@@ -41,7 +41,7 @@ module Montrose
|
|
41
41
|
end
|
42
42
|
|
43
43
|
def step
|
44
|
-
@step ||= smallest_step
|
44
|
+
(@step ||= smallest_step) || fail(ConfigurationError, "No step for #{@options.inspect}")
|
45
45
|
end
|
46
46
|
|
47
47
|
def smallest_step
|
@@ -76,9 +76,9 @@ module Montrose
|
|
76
76
|
is_frequency = @every == unit
|
77
77
|
if ([unit] + alternates).any? { |u| @options.key?(u) } && !is_frequency
|
78
78
|
# smallest unit, increment by 1
|
79
|
-
{
|
79
|
+
{step_key(unit) => 1}
|
80
80
|
elsif is_frequency
|
81
|
-
{
|
81
|
+
{step_key(unit) => @interval}
|
82
82
|
end
|
83
83
|
end
|
84
84
|
|
data/lib/montrose/day.rb
ADDED
@@ -0,0 +1,83 @@
|
|
1
|
+
module Montrose
|
2
|
+
class Day
|
3
|
+
extend Montrose::Utils
|
4
|
+
|
5
|
+
NAMES = ::Date::DAYNAMES
|
6
|
+
TWO_LETTER_ABBREVIATIONS = %w[SU MO TU WE TH FR SA].freeze
|
7
|
+
THREE_LETTER_ABBREVIATIONS = %w[SUN MON TUE WED THU FRI SAT]
|
8
|
+
NUMBERS = NAMES.map.with_index { |_n, i| i.to_s }
|
9
|
+
|
10
|
+
ICAL_MATCH = /(?<ordinal>[+-]?\d+)?(?<day>[A-Z]{2})/ # e.g. 1FR
|
11
|
+
|
12
|
+
class << self
|
13
|
+
def parse(arg)
|
14
|
+
case arg
|
15
|
+
when Hash
|
16
|
+
parse_entries(arg.entries)
|
17
|
+
when String
|
18
|
+
parse(arg.split(","))
|
19
|
+
else
|
20
|
+
parse_entries(map_arg(arg) { |value| parse_value(value) })
|
21
|
+
end
|
22
|
+
end
|
23
|
+
|
24
|
+
def parse_entries(entries)
|
25
|
+
hash = Hash.new { |h, k| h[k] = [] }
|
26
|
+
result = entries.each_with_object(hash) { |(k, v), hash|
|
27
|
+
index = number!(k)
|
28
|
+
hash[index] = hash[index] + [*v]
|
29
|
+
}
|
30
|
+
result.values.all?(&:empty?) ? result.keys : result
|
31
|
+
end
|
32
|
+
|
33
|
+
def parse_value(value)
|
34
|
+
parse_ical(value) || [number!(value), nil]
|
35
|
+
end
|
36
|
+
|
37
|
+
def parse_ical(value)
|
38
|
+
(match = ICAL_MATCH.match(value.to_s)) || (return nil)
|
39
|
+
index = number!(match[:day])
|
40
|
+
ordinal = match[:ordinal]&.to_i
|
41
|
+
[index, ordinal]
|
42
|
+
end
|
43
|
+
|
44
|
+
def map_arg(arg, &block)
|
45
|
+
return nil unless arg.present?
|
46
|
+
|
47
|
+
Array(arg).map(&block)
|
48
|
+
end
|
49
|
+
|
50
|
+
def names
|
51
|
+
NAMES
|
52
|
+
end
|
53
|
+
|
54
|
+
def number(name)
|
55
|
+
case name
|
56
|
+
when 0..6
|
57
|
+
name
|
58
|
+
when Symbol, String
|
59
|
+
string = name.to_s.downcase
|
60
|
+
NAMES.index(string.titleize) ||
|
61
|
+
TWO_LETTER_ABBREVIATIONS.index(string.upcase) ||
|
62
|
+
THREE_LETTER_ABBREVIATIONS.index(string.upcase) ||
|
63
|
+
number(to_index(string))
|
64
|
+
when Array
|
65
|
+
number name.first
|
66
|
+
end
|
67
|
+
end
|
68
|
+
|
69
|
+
def number!(name)
|
70
|
+
number(name) || raise(ConfigurationError,
|
71
|
+
"Did not recognize day #{name}, must be one of #{(names + abbreviations + numbers).inspect}")
|
72
|
+
end
|
73
|
+
|
74
|
+
def numbers
|
75
|
+
NUMBERS
|
76
|
+
end
|
77
|
+
|
78
|
+
def abbreviations
|
79
|
+
TWO_LETTER_ABBREVIATIONS + THREE_LETTER_ABBREVIATIONS
|
80
|
+
end
|
81
|
+
end
|
82
|
+
end
|
83
|
+
end
|