repeatable 1.0.0 → 1.1.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/.github/workflows/ci.yml +72 -0
- data/CHANGELOG.md +18 -1
- data/CODE_OF_CONDUCT.md +128 -0
- data/Gemfile +2 -0
- data/Gemfile.lock +24 -5
- data/README.md +27 -8
- data/Rakefile +5 -1
- data/lib/repeatable/conversions.rb +5 -1
- data/lib/repeatable/expression/base.rb +21 -11
- data/lib/repeatable/expression/biweekly.rb +14 -6
- data/lib/repeatable/expression/date.rb +10 -6
- data/lib/repeatable/expression/day_in_month.rb +4 -0
- data/lib/repeatable/expression/difference.rb +14 -5
- data/lib/repeatable/expression/exact_date.rb +5 -1
- data/lib/repeatable/expression/intersection.rb +6 -2
- data/lib/repeatable/expression/range_in_year.rb +33 -9
- data/lib/repeatable/expression/set.rb +15 -5
- data/lib/repeatable/expression/union.rb +5 -1
- data/lib/repeatable/expression/weekday.rb +4 -0
- data/lib/repeatable/expression/weekday_in_month.rb +14 -1
- data/lib/repeatable/expression.rb +1 -0
- data/lib/repeatable/last_date_of_month.rb +4 -0
- data/lib/repeatable/parse_error.rb +1 -0
- data/lib/repeatable/parser.rb +7 -1
- data/lib/repeatable/schedule.rb +18 -4
- data/lib/repeatable/types.rb +6 -0
- data/lib/repeatable/version.rb +2 -1
- data/lib/repeatable.rb +4 -0
- data/rbi/repeatable.rbi +310 -0
- data/repeatable.gemspec +3 -0
- 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 +61 -6
- data/.travis.yml +0 -18
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
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,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/CHANGELOG.md
CHANGED
@@ -2,7 +2,24 @@
|
|
2
2
|
|
3
3
|
### Unreleased
|
4
4
|
|
5
|
-
[Commits](https://github.com/molawson/repeatable/compare/v1.
|
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)
|
6
23
|
|
7
24
|
### 1.0.0 (2021-03-25)
|
8
25
|
|
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
data/Gemfile.lock
CHANGED
@@ -1,17 +1,26 @@
|
|
1
1
|
PATH
|
2
2
|
remote: .
|
3
3
|
specs:
|
4
|
-
repeatable (1.
|
4
|
+
repeatable (1.1.0)
|
5
|
+
sorbet-runtime
|
5
6
|
|
6
7
|
GEM
|
7
8
|
remote: https://rubygems.org/
|
8
9
|
specs:
|
9
10
|
ast (2.4.2)
|
10
11
|
coderay (1.1.3)
|
12
|
+
commander (4.6.0)
|
13
|
+
highline (~> 2.0.0)
|
11
14
|
diff-lcs (1.4.4)
|
12
|
-
docile (1.
|
15
|
+
docile (1.4.0)
|
16
|
+
highline (2.0.3)
|
13
17
|
method_source (1.0.0)
|
14
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)
|
15
24
|
parser (3.0.0.0)
|
16
25
|
ast (~> 2.4.1)
|
17
26
|
pry (0.14.0)
|
@@ -20,7 +29,7 @@ GEM
|
|
20
29
|
rainbow (3.0.0)
|
21
30
|
rake (13.0.3)
|
22
31
|
regexp_parser (2.1.1)
|
23
|
-
rexml (3.2.
|
32
|
+
rexml (3.2.5)
|
24
33
|
rspec (3.10.0)
|
25
34
|
rspec-core (~> 3.10.0)
|
26
35
|
rspec-expectations (~> 3.10.0)
|
@@ -54,23 +63,33 @@ GEM
|
|
54
63
|
simplecov-html (~> 0.11)
|
55
64
|
simplecov_json_formatter (~> 0.1)
|
56
65
|
simplecov-html (0.12.3)
|
57
|
-
simplecov_json_formatter (0.1.
|
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)
|
58
73
|
standard (1.0.4)
|
59
74
|
rubocop (= 1.11.0)
|
60
75
|
rubocop-performance (= 1.10.1)
|
61
76
|
unicode-display_width (2.0.0)
|
62
77
|
|
63
78
|
PLATFORMS
|
79
|
+
arm64-darwin-21
|
80
|
+
linux
|
64
81
|
x86_64-darwin-19
|
65
82
|
x86_64-linux
|
66
83
|
|
67
84
|
DEPENDENCIES
|
85
|
+
parlour
|
68
86
|
pry (~> 0.13)
|
69
87
|
rake (>= 12.3.3)
|
70
88
|
repeatable!
|
71
89
|
rspec (~> 3.0)
|
72
90
|
simplecov (~> 0.18)
|
91
|
+
sorbet
|
73
92
|
standard (~> 1.0)
|
74
93
|
|
75
94
|
BUNDLED WITH
|
76
|
-
2.
|
95
|
+
2.3.6
|
data/README.md
CHANGED
@@ -1,6 +1,6 @@
|
|
1
1
|
# Repeatable
|
2
2
|
|
3
|
-
[![
|
3
|
+
[![CI](https://github.com/molawson/repeatable/actions/workflows/ci.yml/badge.svg)](https://github.com/molawson/repeatable/actions/workflows/ci.yml)
|
4
4
|
[![Maintainability](https://api.codeclimate.com/v1/badges/73707efd5eeffd364c0d/maintainability)](https://codeclimate.com/github/molawson/repeatable/maintainability)
|
5
5
|
[![Test Coverage](https://api.codeclimate.com/v1/badges/73707efd5eeffd364c0d/test_coverage)](https://codeclimate.com/github/molawson/repeatable/test_coverage)
|
6
6
|
|
@@ -22,10 +22,6 @@ Or install it yourself as:
|
|
22
22
|
|
23
23
|
$ gem install repeatable
|
24
24
|
|
25
|
-
## Requirements
|
26
|
-
|
27
|
-
Because this gem relies heavily on required keyword arguments, especially to make dumping and parsing of schedules simpler, this code will only work on **Ruby 2.2** and higher.
|
28
|
-
|
29
25
|
## Usage
|
30
26
|
|
31
27
|
### Building a Schedule
|
@@ -98,8 +94,8 @@ Repeatable::Expression::WeekdayInMonth.new(weekday: 1, count: 3)
|
|
98
94
|
Repeatable::Expression::WeekdayInMonth.new(weekday: 4, count: -1)
|
99
95
|
|
100
96
|
# Every other Monday, starting from December 1, 2015
|
101
|
-
{ biweekly: { weekday: 1,
|
102
|
-
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))
|
103
99
|
|
104
100
|
# The 13th of every month
|
105
101
|
{ day_in_month: { day: 13 } }
|
@@ -186,16 +182,39 @@ Repeatable::Schedule.new(union) == Repeatable::Schedule.new(another_union)
|
|
186
182
|
|
187
183
|
```
|
188
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
|
+
|
189
198
|
## Development
|
190
199
|
|
191
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.
|
192
201
|
|
193
|
-
|
202
|
+
You can run the tests with `bundle exec rake`.
|
194
203
|
|
195
204
|
## Contributing
|
196
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
|
+
|
197
208
|
1. Fork it ( https://github.com/molawson/repeatable/fork )
|
198
209
|
2. Create your feature branch (`git checkout -b my-new-feature`)
|
199
210
|
3. Commit your changes (`git commit -am 'Add some feature'`)
|
200
211
|
4. Push to the branch (`git push origin my-new-feature`)
|
201
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,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,31 +17,29 @@ 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
|
-
NotImplementedError,
|
23
|
-
"Don't use Expression::Base directly. Subclasses must implement `#to_h`"
|
24
|
-
)
|
26
|
+
{hash_key => hash_value}
|
25
27
|
end
|
26
28
|
|
29
|
+
sig { params(other: Expression::Base).returns(Expression::Union) }
|
27
30
|
def union(other)
|
28
31
|
Union.new(self, other)
|
29
32
|
end
|
30
33
|
alias_method :+, :union
|
31
34
|
alias_method :|, :union
|
32
35
|
|
36
|
+
sig { params(other: Expression::Base).returns(Expression::Intersection) }
|
33
37
|
def intersection(other)
|
34
38
|
Intersection.new(self, other)
|
35
39
|
end
|
36
40
|
alias_method :&, :intersection
|
37
41
|
|
42
|
+
sig { params(other: T.untyped).returns(Expression::Difference) }
|
38
43
|
def difference(other)
|
39
44
|
Difference.new(included: self, excluded: other)
|
40
45
|
end
|
@@ -42,12 +47,17 @@ module Repeatable
|
|
42
47
|
|
43
48
|
private
|
44
49
|
|
50
|
+
sig { returns(Symbol) }
|
45
51
|
def hash_key
|
46
|
-
self.class.name.split("::").last
|
47
|
-
.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]}" }
|
48
54
|
.downcase
|
49
55
|
.to_sym
|
50
56
|
end
|
57
|
+
|
58
|
+
sig { abstract.returns(T.any(Types::SymbolHash, T::Array[Types::SymbolHash])) }
|
59
|
+
def hash_value
|
60
|
+
end
|
51
61
|
end
|
52
62
|
end
|
53
63
|
end
|
@@ -1,23 +1,31 @@
|
|
1
|
+
# typed: strict
|
1
2
|
module Repeatable
|
2
3
|
module Expression
|
3
4
|
class Biweekly < Date
|
5
|
+
sig { params(weekday: Integer, start_after: Object).void }
|
4
6
|
def initialize(weekday:, start_after: ::Date.today)
|
5
7
|
@weekday = weekday
|
6
|
-
@start_after = Conversions::Date(start_after)
|
8
|
+
@start_after = T.let(Conversions::Date(start_after), ::Date)
|
9
|
+
@_first_occurrence = T.let(find_first_occurrence, ::Date)
|
7
10
|
end
|
8
11
|
|
12
|
+
sig { override.params(date: ::Date).returns(T::Boolean) }
|
9
13
|
def include?(date)
|
10
|
-
date >= start_after && (date -
|
14
|
+
date >= start_after && (date - _first_occurrence) % 14 == 0
|
11
15
|
end
|
12
16
|
|
13
17
|
private
|
14
18
|
|
15
|
-
|
19
|
+
sig { returns(Integer) }
|
20
|
+
attr_reader :weekday
|
16
21
|
|
17
|
-
|
18
|
-
|
19
|
-
|
22
|
+
sig { returns(::Date) }
|
23
|
+
attr_reader :start_after
|
24
|
+
|
25
|
+
sig { returns(::Date) }
|
26
|
+
attr_reader :_first_occurrence
|
20
27
|
|
28
|
+
sig { returns(::Date) }
|
21
29
|
def find_first_occurrence
|
22
30
|
days_away = weekday - start_after.wday
|
23
31
|
days_away += 7 if days_away <= 0
|
@@ -1,29 +1,33 @@
|
|
1
|
+
# typed: strict
|
1
2
|
module Repeatable
|
2
3
|
module Expression
|
3
4
|
class Date < Base
|
4
|
-
|
5
|
-
Hash[hash_key, attributes]
|
6
|
-
end
|
5
|
+
abstract!
|
7
6
|
|
7
|
+
sig { params(other: Object).returns(T::Boolean) }
|
8
8
|
def ==(other)
|
9
9
|
other.is_a?(self.class) && attributes == other.attributes
|
10
10
|
end
|
11
|
-
|
12
11
|
alias_method :eql?, :==
|
13
12
|
|
13
|
+
sig { returns(Integer) }
|
14
14
|
def hash
|
15
15
|
[attributes.values, self.class.name].hash
|
16
16
|
end
|
17
17
|
|
18
18
|
protected
|
19
19
|
|
20
|
+
sig { returns(Types::SymbolHash) }
|
20
21
|
def attributes
|
21
22
|
instance_variables.each_with_object({}) do |name, hash|
|
22
|
-
key = name.to_s.gsub(/^@/, "")
|
23
|
-
|
23
|
+
key = name.to_s.gsub(/^@/, "")
|
24
|
+
next if key.start_with?("_")
|
25
|
+
hash[key.to_sym] = normalize_attribute_value(instance_variable_get(name))
|
24
26
|
end
|
25
27
|
end
|
28
|
+
alias_method :hash_value, :attributes
|
26
29
|
|
30
|
+
sig { params(value: BasicObject).returns(T.untyped) }
|
27
31
|
def normalize_attribute_value(value)
|
28
32
|
case value
|
29
33
|
when ::Date
|
@@ -1,12 +1,15 @@
|
|
1
|
+
# typed: strict
|
1
2
|
module Repeatable
|
2
3
|
module Expression
|
3
4
|
class DayInMonth < Date
|
4
5
|
include LastDateOfMonth
|
5
6
|
|
7
|
+
sig { params(day: Integer).void }
|
6
8
|
def initialize(day:)
|
7
9
|
@day = day
|
8
10
|
end
|
9
11
|
|
12
|
+
sig { override.params(date: ::Date).returns(T::Boolean) }
|
10
13
|
def include?(date)
|
11
14
|
if day < 0
|
12
15
|
date - last_date_of_month(date) - 1 == day
|
@@ -17,6 +20,7 @@ module Repeatable
|
|
17
20
|
|
18
21
|
private
|
19
22
|
|
23
|
+
sig { returns(Integer) }
|
20
24
|
attr_reader :day
|
21
25
|
end
|
22
26
|
end
|
@@ -1,19 +1,19 @@
|
|
1
|
+
# typed: strict
|
1
2
|
module Repeatable
|
2
3
|
module Expression
|
3
4
|
class Difference < Base
|
5
|
+
sig { params(included: Expression::Base, excluded: Expression::Base).void }
|
4
6
|
def initialize(included:, excluded:)
|
5
7
|
@included = included
|
6
8
|
@excluded = excluded
|
7
9
|
end
|
8
10
|
|
11
|
+
sig { override.params(date: ::Date).returns(T::Boolean) }
|
9
12
|
def include?(date)
|
10
13
|
included.include?(date) && !excluded.include?(date)
|
11
14
|
end
|
12
15
|
|
13
|
-
|
14
|
-
Hash[hash_key, {included: included.to_h, excluded: excluded.to_h}]
|
15
|
-
end
|
16
|
-
|
16
|
+
sig { params(other: Object).returns(T::Boolean) }
|
17
17
|
def ==(other)
|
18
18
|
other.is_a?(self.class) &&
|
19
19
|
included == other.included &&
|
@@ -22,7 +22,16 @@ module Repeatable
|
|
22
22
|
|
23
23
|
protected
|
24
24
|
|
25
|
-
|
25
|
+
sig { returns(Expression::Base) }
|
26
|
+
attr_reader :included
|
27
|
+
|
28
|
+
sig { returns(Expression::Base) }
|
29
|
+
attr_reader :excluded
|
30
|
+
|
31
|
+
sig { override.returns(Types::SymbolHash) }
|
32
|
+
def hash_value
|
33
|
+
{included: included.to_h, excluded: excluded.to_h}
|
34
|
+
end
|
26
35
|
end
|
27
36
|
end
|
28
37
|
end
|
@@ -1,16 +1,20 @@
|
|
1
|
+
# typed: strict
|
1
2
|
module Repeatable
|
2
3
|
module Expression
|
3
4
|
class ExactDate < Date
|
5
|
+
sig { params(date: Object).void }
|
4
6
|
def initialize(date:)
|
5
|
-
@date = Conversions::Date(date)
|
7
|
+
@date = T.let(Conversions::Date(date), ::Date)
|
6
8
|
end
|
7
9
|
|
10
|
+
sig { override.params(other_date: ::Date).returns(T::Boolean) }
|
8
11
|
def include?(other_date)
|
9
12
|
date == other_date
|
10
13
|
end
|
11
14
|
|
12
15
|
private
|
13
16
|
|
17
|
+
sig { returns(::Date) }
|
14
18
|
attr_reader :date
|
15
19
|
end
|
16
20
|
end
|