repeatable 0.5.0 → 1.1.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +5 -5
- data/.git-blame-ignore-revs +13 -0
- data/.github/workflows/ci.yml +72 -0
- data/.gitignore +0 -1
- data/.rspec +0 -1
- data/.standard.yml +3 -0
- data/CHANGELOG.md +58 -3
- data/CODE_OF_CONDUCT.md +128 -0
- data/Gemfile +11 -3
- data/Gemfile.lock +95 -0
- data/README.md +49 -9
- data/Rakefile +9 -3
- data/lib/repeatable/conversions.rb +5 -1
- data/lib/repeatable/expression/base.rb +37 -11
- data/lib/repeatable/expression/biweekly.rb +14 -6
- data/lib/repeatable/expression/date.rb +11 -8
- data/lib/repeatable/expression/day_in_month.rb +11 -1
- data/lib/repeatable/expression/difference.rb +37 -0
- data/lib/repeatable/expression/exact_date.rb +21 -0
- data/lib/repeatable/expression/intersection.rb +9 -0
- data/lib/repeatable/expression/range_in_year.rb +39 -9
- data/lib/repeatable/expression/set.rb +15 -7
- data/lib/repeatable/expression/union.rb +9 -0
- data/lib/repeatable/expression/weekday.rb +4 -0
- data/lib/repeatable/expression/weekday_in_month.rb +35 -4
- data/lib/repeatable/expression.rb +1 -0
- data/lib/repeatable/last_date_of_month.rb +11 -0
- data/lib/repeatable/parse_error.rb +1 -0
- data/lib/repeatable/parser.rb +14 -2
- data/lib/repeatable/schedule.rb +23 -9
- data/lib/repeatable/types.rb +6 -0
- data/lib/repeatable/version.rb +2 -1
- data/lib/repeatable.rb +25 -19
- data/rbi/repeatable.rbi +310 -0
- data/repeatable.gemspec +15 -15
- data/sorbet/config +3 -0
- data/sorbet/rbi/gems/ast.rbi +49 -0
- data/sorbet/rbi/gems/coderay.rbi +285 -0
- data/sorbet/rbi/gems/commander.rbi +197 -0
- data/sorbet/rbi/gems/docile.rbi +36 -0
- data/sorbet/rbi/gems/highline.rbi +577 -0
- data/sorbet/rbi/gems/method_source.rbi +64 -0
- data/sorbet/rbi/gems/parallel.rbi +83 -0
- data/sorbet/rbi/gems/parlour.rbi +840 -0
- data/sorbet/rbi/gems/parser.rbi +1950 -0
- data/sorbet/rbi/gems/pry.rbi +1898 -0
- data/sorbet/rbi/gems/rainbow.rbi +118 -0
- data/sorbet/rbi/gems/rake.rbi +646 -0
- data/sorbet/rbi/gems/regexp_parser.rbi +926 -0
- data/sorbet/rbi/gems/repeatable.rbi +13 -0
- data/sorbet/rbi/gems/rexml.rbi +583 -0
- data/sorbet/rbi/gems/rspec-core.rbi +1919 -0
- data/sorbet/rbi/gems/rspec-expectations.rbi +1150 -0
- data/sorbet/rbi/gems/rspec-mocks.rbi +1100 -0
- data/sorbet/rbi/gems/rspec-support.rbi +280 -0
- data/sorbet/rbi/gems/rspec.rbi +15 -0
- data/sorbet/rbi/gems/rubocop-ast.rbi +1356 -0
- data/sorbet/rbi/gems/rubocop-performance.rbi +487 -0
- data/sorbet/rbi/gems/rubocop.rbi +7923 -0
- data/sorbet/rbi/gems/ruby-progressbar.rbi +304 -0
- data/sorbet/rbi/gems/simplecov-html.rbi +35 -0
- data/sorbet/rbi/gems/simplecov.rbi +419 -0
- data/sorbet/rbi/gems/simplecov_json_formatter.rbi +47 -0
- data/sorbet/rbi/gems/standard.rbi +130 -0
- data/sorbet/rbi/gems/unicode-display_width.rbi +20 -0
- data/sorbet/rbi/hidden-definitions/errors.txt +4273 -0
- data/sorbet/rbi/hidden-definitions/hidden.rbi +9013 -0
- data/sorbet/rbi/sorbet-typed/lib/rainbow/all/rainbow.rbi +276 -0
- data/sorbet/rbi/sorbet-typed/lib/rake/all/rake.rbi +645 -0
- data/sorbet/rbi/sorbet-typed/lib/rspec-core/all/rspec-core.rbi +24 -0
- data/sorbet/rbi/sorbet-typed/lib/rubocop/>=1.8/rubocop.rbi +12 -0
- data/sorbet/rbi/sorbet-typed/lib/rubocop-performance/~>1.6/rubocop-performance.rbi +149 -0
- metadata +62 -45
- data/.travis.yml +0 -10
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
|
-
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
2
|
+
SHA256:
|
3
|
+
metadata.gz: 71a9ecf64d973e0e8298628e5ab19ab4025c9c3281a357efaae330f42c9767a0
|
4
|
+
data.tar.gz: 190a230b24d32f0df2f6565d35c2da1867d3ae87c82d0e00d98ab16b53baa46c
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 8335d2132cd0fc2cdf5d991431bafcec688819b9b2df45090afcdb05886f74632ebb262ae6f014911aa300589a5f1a776d2a5325adb39569824af306cd94bbfc
|
7
|
+
data.tar.gz: 26a637424b6a64a3a1a6b6db0ed8fb6c0e1ece5040b15499182fd3e45cdb3bf7298634620d1f3284af1a47f00655f7bac7212bea369fbc5f287e154fc3ce45cf
|
@@ -0,0 +1,13 @@
|
|
1
|
+
# These commits should be ignored by git-blame, making it much easier to step through the history
|
2
|
+
# of a line without hitting formatting commits (esp. large, update-all-the-files sorts of commits).
|
3
|
+
#
|
4
|
+
# For git-blame to ignore these revisions, you'll need to tell it about this file.
|
5
|
+
#
|
6
|
+
# You can either pass it to the command each time:
|
7
|
+
# git blame --ignore-rev-file=.git-blame-ignore-revs -L 12,14 lib/repeatable/conversions.rb
|
8
|
+
#
|
9
|
+
# Or you can set it as a config value, which will be used by all git-blame commands henceforth:
|
10
|
+
# git config blame.ignoreRevsFile .git-blame-ignore-revs
|
11
|
+
|
12
|
+
# Initial style fix after introducing Standard
|
13
|
+
80611e9be4fe12e6b9bdb937d37902c12ac3a53b
|
@@ -0,0 +1,72 @@
|
|
1
|
+
name: CI
|
2
|
+
|
3
|
+
on:
|
4
|
+
push:
|
5
|
+
branches:
|
6
|
+
- main
|
7
|
+
- 'ml/**'
|
8
|
+
pull_request:
|
9
|
+
branches:
|
10
|
+
- main
|
11
|
+
|
12
|
+
jobs:
|
13
|
+
RSpec:
|
14
|
+
runs-on: ubuntu-latest
|
15
|
+
strategy:
|
16
|
+
fail-fast: false
|
17
|
+
matrix:
|
18
|
+
ruby-version:
|
19
|
+
- '2.5' # deprecated
|
20
|
+
- '2.6'
|
21
|
+
- '2.7'
|
22
|
+
- '3.0'
|
23
|
+
- '3.1'
|
24
|
+
include:
|
25
|
+
- ruby-version: '3.0'
|
26
|
+
coverage: 'true'
|
27
|
+
steps:
|
28
|
+
- uses: actions/checkout@v2
|
29
|
+
- name: Set up Ruby ${{ matrix.ruby-version }}
|
30
|
+
uses: ruby/setup-ruby@v1
|
31
|
+
with:
|
32
|
+
ruby-version: ${{ matrix.ruby-version }}
|
33
|
+
bundler-cache: true
|
34
|
+
- uses: amancevice/setup-code-climate@v0
|
35
|
+
name: CodeClimate setup
|
36
|
+
if: ${{ matrix.coverage == 'true' }}
|
37
|
+
with:
|
38
|
+
cc_test_reporter_id: ${{ secrets.CC_TEST_REPORTER_ID }}
|
39
|
+
- name: CodeClimate before-build
|
40
|
+
run: cc-test-reporter before-build
|
41
|
+
if: ${{ matrix.coverage == 'true' }}
|
42
|
+
continue-on-error: true
|
43
|
+
- name: Run tests
|
44
|
+
env:
|
45
|
+
COVERAGE: ${{ matrix.coverage }}
|
46
|
+
run: bundle exec rake spec
|
47
|
+
- name: CodeClimate after-build
|
48
|
+
run: cc-test-reporter after-build
|
49
|
+
if: ${{ matrix.coverage == 'true' }}
|
50
|
+
continue-on-error: true
|
51
|
+
Standard:
|
52
|
+
runs-on: ubuntu-latest
|
53
|
+
steps:
|
54
|
+
- uses: actions/checkout@v2
|
55
|
+
- name: Set up Ruby
|
56
|
+
uses: ruby/setup-ruby@v1
|
57
|
+
with:
|
58
|
+
ruby-version: '3.0'
|
59
|
+
bundler-cache: true
|
60
|
+
- name: Run Standard
|
61
|
+
run: bundle exec rake standard
|
62
|
+
Sorbet:
|
63
|
+
runs-on: ubuntu-latest
|
64
|
+
steps:
|
65
|
+
- uses: actions/checkout@v2
|
66
|
+
- name: Set up Ruby
|
67
|
+
uses: ruby/setup-ruby@v1
|
68
|
+
with:
|
69
|
+
ruby-version: '3.0'
|
70
|
+
bundler-cache: true
|
71
|
+
- name: Run Sorbet
|
72
|
+
run: bundle exec rake sorbet
|
data/.gitignore
CHANGED
data/.rspec
CHANGED
data/.standard.yml
ADDED
data/CHANGELOG.md
CHANGED
@@ -2,7 +2,59 @@
|
|
2
2
|
|
3
3
|
### Unreleased
|
4
4
|
|
5
|
-
[Commits](https://github.com/molawson/repeatable/compare/
|
5
|
+
[Commits](https://github.com/molawson/repeatable/compare/v1.1.0...main)
|
6
|
+
|
7
|
+
### 1.1.0 (2022-02-25)
|
8
|
+
|
9
|
+
Changes:
|
10
|
+
|
11
|
+
* Add [sorbet](https://sorbet.org) for type checking
|
12
|
+
|
13
|
+
Bug Fixes:
|
14
|
+
|
15
|
+
* Ensure `first_occurrence` is never part of `#to_h` output for `Expression::Biweekly`
|
16
|
+
|
17
|
+
Chores:
|
18
|
+
|
19
|
+
* Add support for Ruby 3.1
|
20
|
+
* Deprecate support for Ruby 2.5
|
21
|
+
|
22
|
+
[Commits](https://github.com/molawson/repeatable/compare/v1.0.0...v1.1.0)
|
23
|
+
|
24
|
+
### 1.0.0 (2021-03-25)
|
25
|
+
|
26
|
+
Breaking Changes:
|
27
|
+
|
28
|
+
* Change `Expression::DayInMonth` to take negative numbers instead of special `:last` symbol (or string)
|
29
|
+
* Stop including `Conversions` in top level namespace (must now be accessed via `Repeatable::Conversions::Date()` or by calling `include Repeatable::Conversions` in whatever namespace(s) it's needed)
|
30
|
+
* Require Ruby 2.5.0 or greater
|
31
|
+
|
32
|
+
Changes:
|
33
|
+
|
34
|
+
* Flatten nested `Expression::Union` elements during initialization ([@cmoel][])
|
35
|
+
* Flatten nested `Expression::Intersection` elements during initialization
|
36
|
+
|
37
|
+
Chores:
|
38
|
+
|
39
|
+
* Add support for Ruby 2.5, 2.6, 2.7, 3.0
|
40
|
+
* Introduce [standard](https://github.com/testdouble/standard) for code formatting
|
41
|
+
|
42
|
+
[Commits](https://github.com/molawson/repeatable/compare/v0.6.0...v1.0.0)
|
43
|
+
|
44
|
+
|
45
|
+
### 0.6.0 (2017-05-04)
|
46
|
+
|
47
|
+
Features:
|
48
|
+
|
49
|
+
* Add `Expression::Difference` for set differences between 2 schedules ([@danott][])
|
50
|
+
* Allow `Expression::DayInMonth` to take `:last` (or `'last'`) for its `day:` argument ([@PatrickLerner][])
|
51
|
+
* Allow `Expression::WeekdayInMonth` to take negative `count` argument for last, second-to-last, etc. of a given weekday ([@danielma][])
|
52
|
+
|
53
|
+
Bug Fixes:
|
54
|
+
|
55
|
+
* Fix `Expression::RangeInYear` to properly handle using `start_day` and `end_day` when `start_month == end_month` ([@danielma][])
|
56
|
+
|
57
|
+
[Commits](https://github.com/molawson/repeatable/compare/v0.5.0...v0.6.0)
|
6
58
|
|
7
59
|
### 0.5.0 (2016-01-27)
|
8
60
|
|
@@ -38,7 +90,7 @@ Features:
|
|
38
90
|
* Add `ParseError` class for better error handling
|
39
91
|
* Extract `Parser` class from `Schedule`
|
40
92
|
|
41
|
-
|
93
|
+
Bug Fixes:
|
42
94
|
|
43
95
|
* Enable `Schedule` to take a hash with string keys
|
44
96
|
|
@@ -51,7 +103,7 @@ Features:
|
|
51
103
|
* Add `Schedule#to_h` and `Expression#to_h` methods
|
52
104
|
* Enable building a `Schedule` from composed `Expression` objects
|
53
105
|
|
54
|
-
|
106
|
+
Bug Fixes:
|
55
107
|
|
56
108
|
* Fix default case equality for `Expression::Base` to work with classes and instances
|
57
109
|
|
@@ -65,3 +117,6 @@ Initial Release
|
|
65
117
|
|
66
118
|
|
67
119
|
[@danott]: https://github.com/danott
|
120
|
+
[@PatrickLerner]: https://github.com/PatrickLerner
|
121
|
+
[@danielma]: https://github.com/danielma
|
122
|
+
[@cmoel]: https://github.com/cmoel
|
data/CODE_OF_CONDUCT.md
ADDED
@@ -0,0 +1,128 @@
|
|
1
|
+
# Contributor Covenant Code of Conduct
|
2
|
+
|
3
|
+
## Our Pledge
|
4
|
+
|
5
|
+
We as members, contributors, and leaders pledge to make participation in our
|
6
|
+
community a harassment-free experience for everyone, regardless of age, body
|
7
|
+
size, visible or invisible disability, ethnicity, sex characteristics, gender
|
8
|
+
identity and expression, level of experience, education, socio-economic status,
|
9
|
+
nationality, personal appearance, race, religion, or sexual identity
|
10
|
+
and orientation.
|
11
|
+
|
12
|
+
We pledge to act and interact in ways that contribute to an open, welcoming,
|
13
|
+
diverse, inclusive, and healthy community.
|
14
|
+
|
15
|
+
## Our Standards
|
16
|
+
|
17
|
+
Examples of behavior that contributes to a positive environment for our
|
18
|
+
community include:
|
19
|
+
|
20
|
+
* Demonstrating empathy and kindness toward other people
|
21
|
+
* Being respectful of differing opinions, viewpoints, and experiences
|
22
|
+
* Giving and gracefully accepting constructive feedback
|
23
|
+
* Accepting responsibility and apologizing to those affected by our mistakes,
|
24
|
+
and learning from the experience
|
25
|
+
* Focusing on what is best not just for us as individuals, but for the
|
26
|
+
overall community
|
27
|
+
|
28
|
+
Examples of unacceptable behavior include:
|
29
|
+
|
30
|
+
* The use of sexualized language or imagery, and sexual attention or
|
31
|
+
advances of any kind
|
32
|
+
* Trolling, insulting or derogatory comments, and personal or political attacks
|
33
|
+
* Public or private harassment
|
34
|
+
* Publishing others' private information, such as a physical or email
|
35
|
+
address, without their explicit permission
|
36
|
+
* Other conduct which could reasonably be considered inappropriate in a
|
37
|
+
professional setting
|
38
|
+
|
39
|
+
## Enforcement Responsibilities
|
40
|
+
|
41
|
+
Community leaders are responsible for clarifying and enforcing our standards of
|
42
|
+
acceptable behavior and will take appropriate and fair corrective action in
|
43
|
+
response to any behavior that they deem inappropriate, threatening, offensive,
|
44
|
+
or harmful.
|
45
|
+
|
46
|
+
Community leaders have the right and responsibility to remove, edit, or reject
|
47
|
+
comments, commits, code, wiki edits, issues, and other contributions that are
|
48
|
+
not aligned to this Code of Conduct, and will communicate reasons for moderation
|
49
|
+
decisions when appropriate.
|
50
|
+
|
51
|
+
## Scope
|
52
|
+
|
53
|
+
This Code of Conduct applies within all community spaces, and also applies when
|
54
|
+
an individual is officially representing the community in public spaces.
|
55
|
+
Examples of representing our community include using an official e-mail address,
|
56
|
+
posting via an official social media account, or acting as an appointed
|
57
|
+
representative at an online or offline event.
|
58
|
+
|
59
|
+
## Enforcement
|
60
|
+
|
61
|
+
Instances of abusive, harassing, or otherwise unacceptable behavior may be
|
62
|
+
reported to the community leaders responsible for enforcement at
|
63
|
+
mo@molawson.com.
|
64
|
+
All complaints will be reviewed and investigated promptly and fairly.
|
65
|
+
|
66
|
+
All community leaders are obligated to respect the privacy and security of the
|
67
|
+
reporter of any incident.
|
68
|
+
|
69
|
+
## Enforcement Guidelines
|
70
|
+
|
71
|
+
Community leaders will follow these Community Impact Guidelines in determining
|
72
|
+
the consequences for any action they deem in violation of this Code of Conduct:
|
73
|
+
|
74
|
+
### 1. Correction
|
75
|
+
|
76
|
+
**Community Impact**: Use of inappropriate language or other behavior deemed
|
77
|
+
unprofessional or unwelcome in the community.
|
78
|
+
|
79
|
+
**Consequence**: A private, written warning from community leaders, providing
|
80
|
+
clarity around the nature of the violation and an explanation of why the
|
81
|
+
behavior was inappropriate. A public apology may be requested.
|
82
|
+
|
83
|
+
### 2. Warning
|
84
|
+
|
85
|
+
**Community Impact**: A violation through a single incident or series
|
86
|
+
of actions.
|
87
|
+
|
88
|
+
**Consequence**: A warning with consequences for continued behavior. No
|
89
|
+
interaction with the people involved, including unsolicited interaction with
|
90
|
+
those enforcing the Code of Conduct, for a specified period of time. This
|
91
|
+
includes avoiding interactions in community spaces as well as external channels
|
92
|
+
like social media. Violating these terms may lead to a temporary or
|
93
|
+
permanent ban.
|
94
|
+
|
95
|
+
### 3. Temporary Ban
|
96
|
+
|
97
|
+
**Community Impact**: A serious violation of community standards, including
|
98
|
+
sustained inappropriate behavior.
|
99
|
+
|
100
|
+
**Consequence**: A temporary ban from any sort of interaction or public
|
101
|
+
communication with the community for a specified period of time. No public or
|
102
|
+
private interaction with the people involved, including unsolicited interaction
|
103
|
+
with those enforcing the Code of Conduct, is allowed during this period.
|
104
|
+
Violating these terms may lead to a permanent ban.
|
105
|
+
|
106
|
+
### 4. Permanent Ban
|
107
|
+
|
108
|
+
**Community Impact**: Demonstrating a pattern of violation of community
|
109
|
+
standards, including sustained inappropriate behavior, harassment of an
|
110
|
+
individual, or aggression toward or disparagement of classes of individuals.
|
111
|
+
|
112
|
+
**Consequence**: A permanent ban from any sort of public interaction within
|
113
|
+
the community.
|
114
|
+
|
115
|
+
## Attribution
|
116
|
+
|
117
|
+
This Code of Conduct is adapted from the [Contributor Covenant][homepage],
|
118
|
+
version 2.0, available at
|
119
|
+
https://www.contributor-covenant.org/version/2/0/code_of_conduct.html.
|
120
|
+
|
121
|
+
Community Impact Guidelines were inspired by [Mozilla's code of conduct
|
122
|
+
enforcement ladder](https://github.com/mozilla/diversity).
|
123
|
+
|
124
|
+
[homepage]: https://www.contributor-covenant.org
|
125
|
+
|
126
|
+
For answers to common questions about this code of conduct, see the FAQ at
|
127
|
+
https://www.contributor-covenant.org/faq. Translations are available at
|
128
|
+
https://www.contributor-covenant.org/translations.
|
data/Gemfile
CHANGED
@@ -1,7 +1,15 @@
|
|
1
|
-
source
|
1
|
+
source "https://rubygems.org"
|
2
2
|
|
3
3
|
# Specify your gem's dependencies in repeatable.gemspec
|
4
4
|
gemspec
|
5
5
|
|
6
|
-
gem
|
7
|
-
gem
|
6
|
+
gem "parlour"
|
7
|
+
gem "pry", "~> 0.13"
|
8
|
+
gem "rake", ">= 12.3.3"
|
9
|
+
gem "sorbet"
|
10
|
+
gem "standard", "~> 1.0"
|
11
|
+
|
12
|
+
group :test do
|
13
|
+
gem "rspec", "~> 3.0"
|
14
|
+
gem "simplecov", "~> 0.18"
|
15
|
+
end
|
data/Gemfile.lock
ADDED
@@ -0,0 +1,95 @@
|
|
1
|
+
PATH
|
2
|
+
remote: .
|
3
|
+
specs:
|
4
|
+
repeatable (1.1.0)
|
5
|
+
sorbet-runtime
|
6
|
+
|
7
|
+
GEM
|
8
|
+
remote: https://rubygems.org/
|
9
|
+
specs:
|
10
|
+
ast (2.4.2)
|
11
|
+
coderay (1.1.3)
|
12
|
+
commander (4.6.0)
|
13
|
+
highline (~> 2.0.0)
|
14
|
+
diff-lcs (1.4.4)
|
15
|
+
docile (1.4.0)
|
16
|
+
highline (2.0.3)
|
17
|
+
method_source (1.0.0)
|
18
|
+
parallel (1.20.1)
|
19
|
+
parlour (6.0.1)
|
20
|
+
commander (~> 4.5)
|
21
|
+
parser
|
22
|
+
rainbow (~> 3.0)
|
23
|
+
sorbet-runtime (>= 0.5)
|
24
|
+
parser (3.0.0.0)
|
25
|
+
ast (~> 2.4.1)
|
26
|
+
pry (0.14.0)
|
27
|
+
coderay (~> 1.1)
|
28
|
+
method_source (~> 1.0)
|
29
|
+
rainbow (3.0.0)
|
30
|
+
rake (13.0.3)
|
31
|
+
regexp_parser (2.1.1)
|
32
|
+
rexml (3.2.5)
|
33
|
+
rspec (3.10.0)
|
34
|
+
rspec-core (~> 3.10.0)
|
35
|
+
rspec-expectations (~> 3.10.0)
|
36
|
+
rspec-mocks (~> 3.10.0)
|
37
|
+
rspec-core (3.10.1)
|
38
|
+
rspec-support (~> 3.10.0)
|
39
|
+
rspec-expectations (3.10.1)
|
40
|
+
diff-lcs (>= 1.2.0, < 2.0)
|
41
|
+
rspec-support (~> 3.10.0)
|
42
|
+
rspec-mocks (3.10.2)
|
43
|
+
diff-lcs (>= 1.2.0, < 2.0)
|
44
|
+
rspec-support (~> 3.10.0)
|
45
|
+
rspec-support (3.10.2)
|
46
|
+
rubocop (1.11.0)
|
47
|
+
parallel (~> 1.10)
|
48
|
+
parser (>= 3.0.0.0)
|
49
|
+
rainbow (>= 2.2.2, < 4.0)
|
50
|
+
regexp_parser (>= 1.8, < 3.0)
|
51
|
+
rexml
|
52
|
+
rubocop-ast (>= 1.2.0, < 2.0)
|
53
|
+
ruby-progressbar (~> 1.7)
|
54
|
+
unicode-display_width (>= 1.4.0, < 3.0)
|
55
|
+
rubocop-ast (1.4.1)
|
56
|
+
parser (>= 2.7.1.5)
|
57
|
+
rubocop-performance (1.10.1)
|
58
|
+
rubocop (>= 0.90.0, < 2.0)
|
59
|
+
rubocop-ast (>= 0.4.0)
|
60
|
+
ruby-progressbar (1.11.0)
|
61
|
+
simplecov (0.21.2)
|
62
|
+
docile (~> 1.1)
|
63
|
+
simplecov-html (~> 0.11)
|
64
|
+
simplecov_json_formatter (~> 0.1)
|
65
|
+
simplecov-html (0.12.3)
|
66
|
+
simplecov_json_formatter (0.1.4)
|
67
|
+
sorbet (0.5.9672)
|
68
|
+
sorbet-static (= 0.5.9672)
|
69
|
+
sorbet-runtime (0.5.6357)
|
70
|
+
sorbet-static (0.5.9672-universal-darwin-19)
|
71
|
+
sorbet-static (0.5.9672-universal-darwin-21)
|
72
|
+
sorbet-static (0.5.9672-x86_64-linux)
|
73
|
+
standard (1.0.4)
|
74
|
+
rubocop (= 1.11.0)
|
75
|
+
rubocop-performance (= 1.10.1)
|
76
|
+
unicode-display_width (2.0.0)
|
77
|
+
|
78
|
+
PLATFORMS
|
79
|
+
arm64-darwin-21
|
80
|
+
linux
|
81
|
+
x86_64-darwin-19
|
82
|
+
x86_64-linux
|
83
|
+
|
84
|
+
DEPENDENCIES
|
85
|
+
parlour
|
86
|
+
pry (~> 0.13)
|
87
|
+
rake (>= 12.3.3)
|
88
|
+
repeatable!
|
89
|
+
rspec (~> 3.0)
|
90
|
+
simplecov (~> 0.18)
|
91
|
+
sorbet
|
92
|
+
standard (~> 1.0)
|
93
|
+
|
94
|
+
BUNDLED WITH
|
95
|
+
2.3.6
|
data/README.md
CHANGED
@@ -1,8 +1,8 @@
|
|
1
1
|
# Repeatable
|
2
2
|
|
3
|
-
[![
|
4
|
-
[![
|
5
|
-
[![
|
3
|
+
[![CI](https://github.com/molawson/repeatable/actions/workflows/ci.yml/badge.svg)](https://github.com/molawson/repeatable/actions/workflows/ci.yml)
|
4
|
+
[![Maintainability](https://api.codeclimate.com/v1/badges/73707efd5eeffd364c0d/maintainability)](https://codeclimate.com/github/molawson/repeatable/maintainability)
|
5
|
+
[![Test Coverage](https://api.codeclimate.com/v1/badges/73707efd5eeffd364c0d/test_coverage)](https://codeclimate.com/github/molawson/repeatable/test_coverage)
|
6
6
|
|
7
7
|
Ruby implementation of Martin Fowler's [Recurring Events for Calendars](http://martinfowler.com/apsupp/recurring.pdf) paper.
|
8
8
|
|
@@ -33,9 +33,9 @@ You can create a schedule in one of two ways.
|
|
33
33
|
Instantiate and compose each of the `Repeatable::Expression` objects manually.
|
34
34
|
|
35
35
|
```ruby
|
36
|
-
second_monday =
|
36
|
+
second_monday = Repeatable::Expression::WeekdayInMonth.new(weekday: 1, count: 2)
|
37
37
|
oct_thru_dec = Repeatable::Expression::RangeInYear.new(start_month: 10, end_month: 12)
|
38
|
-
intersection = Repeatable::
|
38
|
+
intersection = Repeatable::Expression::Intersection.new(second_monday, oct_thru_dec)
|
39
39
|
|
40
40
|
schedule = Repeatable::Schedule.new(intersection)
|
41
41
|
```
|
@@ -49,7 +49,8 @@ Or describe the same structure with a `Hash`, and the gem will handle instantiat
|
|
49
49
|
arg = {
|
50
50
|
intersection: [
|
51
51
|
{ weekday_in_month: { weekday: 1, count: 2 } },
|
52
|
-
{ range_in_year: { start_month: 10, end_month: 12 } }
|
52
|
+
{ range_in_year: { start_month: 10, end_month: 12 } },
|
53
|
+
{ exact_date: { date: "2015-08-01" } }
|
53
54
|
]
|
54
55
|
}
|
55
56
|
|
@@ -73,6 +74,10 @@ Repeatable::Expression::Union.new(expressions)
|
|
73
74
|
{ intersection: [] }
|
74
75
|
Repeatable::Expression::Intersection.new(expressions)
|
75
76
|
|
77
|
+
# Date is part of the first set (`included`) but not part of the second set (`excluded`)
|
78
|
+
{ difference: { included: expression, excluded: another_expression } }
|
79
|
+
Repeatable::Expression::Difference.new(included: expression, excluded: another_expression)
|
80
|
+
|
76
81
|
|
77
82
|
# DATES
|
78
83
|
|
@@ -84,14 +89,22 @@ Repeatable::Expression::Weekday.new(weekday: 0)
|
|
84
89
|
{ weekday_in_month: { weekday: 1, count: 3 } }
|
85
90
|
Repeatable::Expression::WeekdayInMonth.new(weekday: 1, count: 3)
|
86
91
|
|
92
|
+
# The last Thursday of every month
|
93
|
+
{ weekday_in_month: { weekday: 4, count: -1 } }
|
94
|
+
Repeatable::Expression::WeekdayInMonth.new(weekday: 4, count: -1)
|
95
|
+
|
87
96
|
# Every other Monday, starting from December 1, 2015
|
88
|
-
{ biweekly: { weekday: 1,
|
89
|
-
Repeatable::Expression::Biweekly.new(weekday: 1,
|
97
|
+
{ biweekly: { weekday: 1, start_after: '2015-12-01' } }
|
98
|
+
Repeatable::Expression::Biweekly.new(weekday: 1, start_after: Date.new(2015, 12, 1))
|
90
99
|
|
91
100
|
# The 13th of every month
|
92
101
|
{ day_in_month: { day: 13 } }
|
93
102
|
Repeatable::Expression::DayInMonth.new(day: 13)
|
94
103
|
|
104
|
+
# The last day of every month
|
105
|
+
{ day_in_month: { day: -1 } }
|
106
|
+
Repeatable::Expression::DayInMonth.new(day: -1)
|
107
|
+
|
95
108
|
# All days in October
|
96
109
|
{ range_in_year: { start_month: 10 } }
|
97
110
|
Repeatable::Expression::RangeInYear.new(start_month: 10)
|
@@ -103,6 +116,10 @@ Repeatable::Expression::RangeInYear.new(start_month: 10, end_month: 12)
|
|
103
116
|
# All days from October 1 through December 20
|
104
117
|
{ range_in_year: { start_month: 10, end_month: 12, start_day: 1, end_day: 20 } }
|
105
118
|
Repeatable::Expression::RangeInYear.new(start_month: 10, end_month: 12, start_day: 1, end_day: 20)
|
119
|
+
|
120
|
+
# only December 21, 2012
|
121
|
+
{ exact_date: { date: '2012-12-21' } }
|
122
|
+
Repeatable::Expression::ExactDate.new(date: Date.new(2012, 12, 21)
|
106
123
|
```
|
107
124
|
|
108
125
|
#### Schedule Errors
|
@@ -165,16 +182,39 @@ Repeatable::Schedule.new(union) == Repeatable::Schedule.new(another_union)
|
|
165
182
|
|
166
183
|
```
|
167
184
|
|
185
|
+
## Ruby version support
|
186
|
+
|
187
|
+
Currently tested and supported:
|
188
|
+
- 2.6
|
189
|
+
- 2.7
|
190
|
+
- 3.0
|
191
|
+
- 3.1
|
192
|
+
|
193
|
+
Deprecated (currently tested but has reached EOL and will be unsupported in the next major version):
|
194
|
+
- 2.5
|
195
|
+
|
196
|
+
The supported versions will roughly track with versions that are currently maintained by the Ruby core team.
|
197
|
+
|
168
198
|
## Development
|
169
199
|
|
170
200
|
After checking out the repo, run `bin/setup` to install dependencies. Then, run `bin/console` for an interactive prompt that will allow you to experiment.
|
171
201
|
|
172
|
-
|
202
|
+
You can run the tests with `bundle exec rake`.
|
173
203
|
|
174
204
|
## Contributing
|
175
205
|
|
206
|
+
Bug reports and pull requests are welcome on GitHub at https://github.com/molawson/repeatable. This project is intended to be a safe, welcoming space for collaboration, and contributors are expected to adhere to the [Contributor Covenant](http://contributor-covenant.org) code of conduct.
|
207
|
+
|
176
208
|
1. Fork it ( https://github.com/molawson/repeatable/fork )
|
177
209
|
2. Create your feature branch (`git checkout -b my-new-feature`)
|
178
210
|
3. Commit your changes (`git commit -am 'Add some feature'`)
|
179
211
|
4. Push to the branch (`git push origin my-new-feature`)
|
180
212
|
5. Create a new Pull Request
|
213
|
+
|
214
|
+
## License
|
215
|
+
|
216
|
+
The gem is available as open source under the terms of the [MIT License](https://opensource.org/licenses/MIT).
|
217
|
+
|
218
|
+
## Code of Conduct
|
219
|
+
|
220
|
+
Everyone interacting in the Repeatable project’s codebases, issue trackers, chat rooms and mailing lists is expected to follow the [code of conduct](https://github.com/molawson/repeatable/blob/main/CODE_OF_CONDUCT.md).
|
data/Rakefile
CHANGED
@@ -1,6 +1,12 @@
|
|
1
|
-
require
|
2
|
-
require
|
1
|
+
require "bundler/gem_tasks"
|
2
|
+
require "rspec/core/rake_task"
|
3
3
|
|
4
4
|
RSpec::Core::RakeTask.new(:spec)
|
5
5
|
|
6
|
-
|
6
|
+
require "standard/rake"
|
7
|
+
|
8
|
+
task :sorbet do
|
9
|
+
sh %(bundle exec srb tc)
|
10
|
+
end
|
11
|
+
|
12
|
+
task default: %i[spec sorbet standard]
|
@@ -1,7 +1,11 @@
|
|
1
|
+
# typed: strict
|
1
2
|
module Repeatable
|
2
3
|
module Conversions
|
4
|
+
extend T::Sig
|
5
|
+
|
3
6
|
module_function
|
4
7
|
|
8
|
+
sig { params(arg: Object).returns(::Date) }
|
5
9
|
def Date(arg)
|
6
10
|
case arg
|
7
11
|
when Date, Time
|
@@ -10,7 +14,7 @@ module Repeatable
|
|
10
14
|
Date.parse(arg)
|
11
15
|
end
|
12
16
|
rescue ArgumentError
|
13
|
-
raise TypeError, "Cannot convert #{arg.inspect} to Date"
|
17
|
+
Kernel.raise TypeError, "Cannot convert #{arg.inspect} to Date"
|
14
18
|
end
|
15
19
|
end
|
16
20
|
end
|
@@ -1,6 +1,13 @@
|
|
1
|
+
# typed: strict
|
1
2
|
module Repeatable
|
2
3
|
module Expression
|
3
4
|
class Base
|
5
|
+
extend T::Sig
|
6
|
+
extend T::Helpers
|
7
|
+
|
8
|
+
abstract!
|
9
|
+
|
10
|
+
sig { params(other: Object).returns(T::Boolean) }
|
4
11
|
def self.===(other)
|
5
12
|
case other
|
6
13
|
when Class
|
@@ -10,28 +17,47 @@ module Repeatable
|
|
10
17
|
end
|
11
18
|
end
|
12
19
|
|
13
|
-
|
14
|
-
|
15
|
-
NotImplementedError,
|
16
|
-
"Don't use Expression::Base directly. Subclasses must implement `#include?`"
|
17
|
-
)
|
20
|
+
sig { abstract.params(date: ::Date).returns(T::Boolean) }
|
21
|
+
def include?(date)
|
18
22
|
end
|
19
23
|
|
24
|
+
sig { returns(T::Hash[Symbol, T.any(Types::SymbolHash, T::Array[Types::SymbolHash])]) }
|
20
25
|
def to_h
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
26
|
+
{hash_key => hash_value}
|
27
|
+
end
|
28
|
+
|
29
|
+
sig { params(other: Expression::Base).returns(Expression::Union) }
|
30
|
+
def union(other)
|
31
|
+
Union.new(self, other)
|
32
|
+
end
|
33
|
+
alias_method :+, :union
|
34
|
+
alias_method :|, :union
|
35
|
+
|
36
|
+
sig { params(other: Expression::Base).returns(Expression::Intersection) }
|
37
|
+
def intersection(other)
|
38
|
+
Intersection.new(self, other)
|
39
|
+
end
|
40
|
+
alias_method :&, :intersection
|
41
|
+
|
42
|
+
sig { params(other: T.untyped).returns(Expression::Difference) }
|
43
|
+
def difference(other)
|
44
|
+
Difference.new(included: self, excluded: other)
|
25
45
|
end
|
46
|
+
alias_method :-, :difference
|
26
47
|
|
27
48
|
private
|
28
49
|
|
50
|
+
sig { returns(Symbol) }
|
29
51
|
def hash_key
|
30
|
-
self.class.name.split(
|
31
|
-
.gsub(/(?<!\b)[A-Z]/) { "_#{Regexp.last_match[0]}" }
|
52
|
+
T.must(T.must(self.class.name).split("::").last)
|
53
|
+
.gsub(/(?<!\b)[A-Z]/) { "_#{T.must(Regexp.last_match)[0]}" }
|
32
54
|
.downcase
|
33
55
|
.to_sym
|
34
56
|
end
|
57
|
+
|
58
|
+
sig { abstract.returns(T.any(Types::SymbolHash, T::Array[Types::SymbolHash])) }
|
59
|
+
def hash_value
|
60
|
+
end
|
35
61
|
end
|
36
62
|
end
|
37
63
|
end
|