repeatable 0.5.0 → 1.1.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 +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
@@ -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 = 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,30 +1,33 @@
|
|
1
|
+
# typed: strict
|
1
2
|
module Repeatable
|
2
3
|
module Expression
|
3
4
|
class Date < Base
|
5
|
+
abstract!
|
4
6
|
|
5
|
-
|
6
|
-
Hash[hash_key, attributes]
|
7
|
-
end
|
8
|
-
|
7
|
+
sig { params(other: Object).returns(T::Boolean) }
|
9
8
|
def ==(other)
|
10
9
|
other.is_a?(self.class) && attributes == other.attributes
|
11
10
|
end
|
11
|
+
alias_method :eql?, :==
|
12
12
|
|
13
|
-
|
14
|
-
|
13
|
+
sig { returns(Integer) }
|
15
14
|
def hash
|
16
15
|
[attributes.values, self.class.name].hash
|
17
16
|
end
|
18
17
|
|
19
18
|
protected
|
20
19
|
|
20
|
+
sig { returns(Types::SymbolHash) }
|
21
21
|
def attributes
|
22
22
|
instance_variables.each_with_object({}) do |name, hash|
|
23
|
-
key = name.to_s.gsub(/^@/,
|
24
|
-
|
23
|
+
key = name.to_s.gsub(/^@/, "")
|
24
|
+
next if key.start_with?("_")
|
25
|
+
hash[key.to_sym] = normalize_attribute_value(instance_variable_get(name))
|
25
26
|
end
|
26
27
|
end
|
28
|
+
alias_method :hash_value, :attributes
|
27
29
|
|
30
|
+
sig { params(value: BasicObject).returns(T.untyped) }
|
28
31
|
def normalize_attribute_value(value)
|
29
32
|
case value
|
30
33
|
when ::Date
|
@@ -1,16 +1,26 @@
|
|
1
|
+
# typed: strict
|
1
2
|
module Repeatable
|
2
3
|
module Expression
|
3
4
|
class DayInMonth < Date
|
5
|
+
include LastDateOfMonth
|
6
|
+
|
7
|
+
sig { params(day: Integer).void }
|
4
8
|
def initialize(day:)
|
5
9
|
@day = day
|
6
10
|
end
|
7
11
|
|
12
|
+
sig { override.params(date: ::Date).returns(T::Boolean) }
|
8
13
|
def include?(date)
|
9
|
-
|
14
|
+
if day < 0
|
15
|
+
date - last_date_of_month(date) - 1 == day
|
16
|
+
else
|
17
|
+
date.day == day
|
18
|
+
end
|
10
19
|
end
|
11
20
|
|
12
21
|
private
|
13
22
|
|
23
|
+
sig { returns(Integer) }
|
14
24
|
attr_reader :day
|
15
25
|
end
|
16
26
|
end
|
@@ -0,0 +1,37 @@
|
|
1
|
+
# typed: strict
|
2
|
+
module Repeatable
|
3
|
+
module Expression
|
4
|
+
class Difference < Base
|
5
|
+
sig { params(included: Expression::Base, excluded: Expression::Base).void }
|
6
|
+
def initialize(included:, excluded:)
|
7
|
+
@included = included
|
8
|
+
@excluded = excluded
|
9
|
+
end
|
10
|
+
|
11
|
+
sig { override.params(date: ::Date).returns(T::Boolean) }
|
12
|
+
def include?(date)
|
13
|
+
included.include?(date) && !excluded.include?(date)
|
14
|
+
end
|
15
|
+
|
16
|
+
sig { params(other: Object).returns(T::Boolean) }
|
17
|
+
def ==(other)
|
18
|
+
other.is_a?(self.class) &&
|
19
|
+
included == other.included &&
|
20
|
+
excluded == other.excluded
|
21
|
+
end
|
22
|
+
|
23
|
+
protected
|
24
|
+
|
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
|
35
|
+
end
|
36
|
+
end
|
37
|
+
end
|
@@ -0,0 +1,21 @@
|
|
1
|
+
# typed: strict
|
2
|
+
module Repeatable
|
3
|
+
module Expression
|
4
|
+
class ExactDate < Date
|
5
|
+
sig { params(date: Object).void }
|
6
|
+
def initialize(date:)
|
7
|
+
@date = T.let(Conversions::Date(date), ::Date)
|
8
|
+
end
|
9
|
+
|
10
|
+
sig { override.params(other_date: ::Date).returns(T::Boolean) }
|
11
|
+
def include?(other_date)
|
12
|
+
date == other_date
|
13
|
+
end
|
14
|
+
|
15
|
+
private
|
16
|
+
|
17
|
+
sig { returns(::Date) }
|
18
|
+
attr_reader :date
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end
|
@@ -1,6 +1,15 @@
|
|
1
|
+
# typed: strict
|
1
2
|
module Repeatable
|
2
3
|
module Expression
|
3
4
|
class Intersection < Set
|
5
|
+
sig { params(elements: T.any(Expression::Base, T::Array[Expression::Base])).void }
|
6
|
+
def initialize(*elements)
|
7
|
+
other_intersections, not_intersections = elements.partition { |e| e.is_a?(self.class) }
|
8
|
+
other_intersections = T.cast(other_intersections, T::Array[Expression::Intersection])
|
9
|
+
super(other_intersections.flat_map(&:elements).concat(not_intersections))
|
10
|
+
end
|
11
|
+
|
12
|
+
sig { override.params(date: ::Date).returns(T::Boolean) }
|
4
13
|
def include?(date)
|
5
14
|
elements.all? { |e| e.include?(date) }
|
6
15
|
end
|
@@ -1,6 +1,15 @@
|
|
1
|
+
# typed: strict
|
1
2
|
module Repeatable
|
2
3
|
module Expression
|
3
4
|
class RangeInYear < Date
|
5
|
+
sig do
|
6
|
+
params(
|
7
|
+
start_month: Integer,
|
8
|
+
end_month: Integer,
|
9
|
+
start_day: Integer,
|
10
|
+
end_day: Integer
|
11
|
+
).void
|
12
|
+
end
|
4
13
|
def initialize(start_month:, end_month: start_month, start_day: 0, end_day: 0)
|
5
14
|
@start_month = start_month
|
6
15
|
@end_month = end_month
|
@@ -8,33 +17,54 @@ module Repeatable
|
|
8
17
|
@end_day = end_day
|
9
18
|
end
|
10
19
|
|
20
|
+
sig { override.params(date: ::Date).returns(T::Boolean) }
|
11
21
|
def include?(date)
|
12
|
-
|
13
|
-
end
|
22
|
+
return true if months_include?(date)
|
14
23
|
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
{ range_in_year: args }
|
24
|
+
if start_month == end_month
|
25
|
+
start_month_include?(date) && end_month_include?(date)
|
26
|
+
else
|
27
|
+
start_month_include?(date) || end_month_include?(date)
|
28
|
+
end
|
21
29
|
end
|
22
30
|
|
23
31
|
private
|
24
32
|
|
25
|
-
|
33
|
+
sig { returns(Integer) }
|
34
|
+
attr_reader :start_month
|
35
|
+
|
36
|
+
sig { returns(Integer) }
|
37
|
+
attr_reader :end_month
|
38
|
+
|
39
|
+
sig { returns(Integer) }
|
40
|
+
attr_reader :start_day
|
26
41
|
|
42
|
+
sig { returns(Integer) }
|
43
|
+
attr_reader :end_day
|
44
|
+
|
45
|
+
sig { params(date: ::Date).returns(T::Boolean) }
|
27
46
|
def months_include?(date)
|
28
47
|
date.month > start_month && date.month < end_month
|
29
48
|
end
|
30
49
|
|
50
|
+
sig { params(date: ::Date).returns(T::Boolean) }
|
31
51
|
def start_month_include?(date)
|
32
52
|
date.month == start_month && (start_day == 0 || date.day >= start_day)
|
33
53
|
end
|
34
54
|
|
55
|
+
sig { params(date: ::Date).returns(T::Boolean) }
|
35
56
|
def end_month_include?(date)
|
36
57
|
date.month == end_month && (end_day == 0 || date.day <= end_day)
|
37
58
|
end
|
59
|
+
|
60
|
+
sig { override.returns(T::Hash[Symbol, Integer]) }
|
61
|
+
def hash_value
|
62
|
+
args = {start_month: start_month}
|
63
|
+
args[:end_month] = end_month unless end_month == start_month
|
64
|
+
args[:start_day] = start_day unless start_day.zero?
|
65
|
+
args[:end_day] = end_day unless end_day.zero?
|
66
|
+
args
|
67
|
+
end
|
38
68
|
end
|
39
69
|
end
|
40
70
|
end
|
@@ -1,28 +1,36 @@
|
|
1
|
+
# typed: strict
|
1
2
|
module Repeatable
|
2
3
|
module Expression
|
3
4
|
class Set < Base
|
5
|
+
abstract!
|
6
|
+
|
7
|
+
sig { returns(T::Array[Expression::Base]) }
|
8
|
+
attr_reader :elements
|
9
|
+
|
10
|
+
sig { params(elements: T.any(Expression::Base, T::Array[Expression::Base])).void }
|
4
11
|
def initialize(*elements)
|
5
|
-
@elements = elements.flatten.uniq
|
12
|
+
@elements = T.let(elements.flatten.uniq, T::Array[Expression::Base])
|
6
13
|
end
|
7
14
|
|
15
|
+
sig { params(element: T.untyped).returns(Repeatable::Expression::Set) }
|
8
16
|
def <<(element)
|
9
17
|
elements << element unless elements.include?(element)
|
10
18
|
self
|
11
19
|
end
|
12
20
|
|
13
|
-
|
14
|
-
Hash[hash_key, elements.map(&:to_h)]
|
15
|
-
end
|
16
|
-
|
21
|
+
sig { params(other: Object).returns(T::Boolean) }
|
17
22
|
def ==(other)
|
18
23
|
other.is_a?(self.class) &&
|
19
24
|
elements.size == other.elements.size &&
|
20
25
|
other.elements.all? { |e| elements.include?(e) }
|
21
26
|
end
|
22
27
|
|
23
|
-
|
28
|
+
private
|
24
29
|
|
25
|
-
|
30
|
+
sig { override.returns(T::Array[Types::SymbolHash]) }
|
31
|
+
def hash_value
|
32
|
+
elements.map(&:to_h)
|
33
|
+
end
|
26
34
|
end
|
27
35
|
end
|
28
36
|
end
|
@@ -1,6 +1,15 @@
|
|
1
|
+
# typed: strict
|
1
2
|
module Repeatable
|
2
3
|
module Expression
|
3
4
|
class Union < Set
|
5
|
+
sig { params(elements: T.any(Expression::Base, T::Array[Expression::Base])).void }
|
6
|
+
def initialize(*elements)
|
7
|
+
other_unions, not_unions = elements.partition { |e| e.is_a?(self.class) }
|
8
|
+
other_unions = T.cast(other_unions, T::Array[Expression::Union])
|
9
|
+
super(other_unions.flat_map(&:elements).concat(not_unions))
|
10
|
+
end
|
11
|
+
|
12
|
+
sig { override.params(date: ::Date).returns(T::Boolean) }
|
4
13
|
def include?(date)
|
5
14
|
elements.any? { |e| e.include?(date) }
|
6
15
|
end
|
@@ -1,16 +1,20 @@
|
|
1
|
+
# typed: strict
|
1
2
|
module Repeatable
|
2
3
|
module Expression
|
3
4
|
class Weekday < Date
|
5
|
+
sig { params(weekday: Integer).void }
|
4
6
|
def initialize(weekday:)
|
5
7
|
@weekday = weekday
|
6
8
|
end
|
7
9
|
|
10
|
+
sig { override.params(date: ::Date).returns(T::Boolean) }
|
8
11
|
def include?(date)
|
9
12
|
date.wday == weekday
|
10
13
|
end
|
11
14
|
|
12
15
|
private
|
13
16
|
|
17
|
+
sig { returns(Integer) }
|
14
18
|
attr_reader :weekday
|
15
19
|
end
|
16
20
|
end
|
@@ -1,29 +1,60 @@
|
|
1
|
+
# typed: strict
|
1
2
|
module Repeatable
|
2
3
|
module Expression
|
3
4
|
class WeekdayInMonth < Date
|
5
|
+
include LastDateOfMonth
|
6
|
+
|
7
|
+
sig { params(weekday: Integer, count: Integer).void }
|
4
8
|
def initialize(weekday:, count:)
|
5
9
|
@weekday = weekday
|
6
10
|
@count = count
|
7
11
|
end
|
8
12
|
|
13
|
+
sig { override.params(date: ::Date).returns(T::Boolean) }
|
9
14
|
def include?(date)
|
10
15
|
day_matches?(date) && week_matches?(date)
|
11
16
|
end
|
12
17
|
|
13
18
|
private
|
14
19
|
|
15
|
-
|
20
|
+
sig { returns(Integer) }
|
21
|
+
attr_reader :weekday
|
22
|
+
|
23
|
+
sig { returns(Integer) }
|
24
|
+
attr_reader :count
|
16
25
|
|
26
|
+
sig { params(date: ::Date).returns(T::Boolean) }
|
17
27
|
def day_matches?(date)
|
18
28
|
date.wday == weekday
|
19
29
|
end
|
20
30
|
|
31
|
+
sig { params(date: ::Date).returns(T::Boolean) }
|
21
32
|
def week_matches?(date)
|
22
|
-
|
33
|
+
if negative_count?
|
34
|
+
week_from_end(date) == count
|
35
|
+
else
|
36
|
+
week_from_beginning(date) == count
|
37
|
+
end
|
38
|
+
end
|
39
|
+
|
40
|
+
sig { params(date: ::Date).returns(Integer) }
|
41
|
+
def week_from_beginning(date)
|
42
|
+
week_in_month(date.day - 1)
|
43
|
+
end
|
44
|
+
|
45
|
+
sig { params(date: ::Date).returns(Integer) }
|
46
|
+
def week_from_end(date)
|
47
|
+
-week_in_month(last_date_of_month(date).day - date.day)
|
48
|
+
end
|
49
|
+
|
50
|
+
sig { params(zero_indexed_day: Integer).returns(Integer) }
|
51
|
+
def week_in_month(zero_indexed_day)
|
52
|
+
(zero_indexed_day / 7) + 1
|
23
53
|
end
|
24
54
|
|
25
|
-
|
26
|
-
|
55
|
+
sig { returns(T::Boolean) }
|
56
|
+
def negative_count?
|
57
|
+
count < 0
|
27
58
|
end
|
28
59
|
end
|
29
60
|
end
|
data/lib/repeatable/parser.rb
CHANGED
@@ -1,13 +1,19 @@
|
|
1
|
+
# typed: false
|
1
2
|
module Repeatable
|
2
3
|
class Parser
|
4
|
+
extend T::Sig
|
5
|
+
|
6
|
+
sig { params(hash: T::Hash[T.any(String, Symbol), T.untyped]).void }
|
3
7
|
def initialize(hash)
|
4
8
|
@hash = hash
|
5
9
|
end
|
6
10
|
|
11
|
+
sig { params(hash: T::Hash[T.any(String, Symbol), T.untyped]).returns(Expression::Base) }
|
7
12
|
def self.call(hash)
|
8
13
|
new(hash).call
|
9
14
|
end
|
10
15
|
|
16
|
+
sig { returns(Expression::Base) }
|
11
17
|
def call
|
12
18
|
build_expression(hash)
|
13
19
|
end
|
@@ -32,8 +38,14 @@ module Repeatable
|
|
32
38
|
when Repeatable::Expression::Set
|
33
39
|
args = value.map { |hash| build_expression(hash) }
|
34
40
|
klass.new(*args)
|
41
|
+
when Repeatable::Expression::Difference
|
42
|
+
value = symbolize_keys(value)
|
43
|
+
klass.new(
|
44
|
+
included: build_expression(value[:included]),
|
45
|
+
excluded: build_expression(value[:excluded])
|
46
|
+
)
|
35
47
|
else
|
36
|
-
klass.new(symbolize_keys(value))
|
48
|
+
klass.new(**symbolize_keys(value))
|
37
49
|
end
|
38
50
|
end
|
39
51
|
|
@@ -44,7 +56,7 @@ module Repeatable
|
|
44
56
|
def expression_klass(string)
|
45
57
|
camel_cased_string = string
|
46
58
|
.capitalize
|
47
|
-
.gsub(/(?:_)(?<word>[a-z\d]+)/i) { Regexp.last_match[:word]
|
59
|
+
.gsub(/(?:_)(?<word>[a-z\d]+)/i) { T.must(Regexp.last_match)[:word]&.capitalize }
|
48
60
|
Repeatable::Expression.const_get(camel_cased_string)
|
49
61
|
rescue NameError => e
|
50
62
|
raise if e.name && e.name.to_s != camel_cased_string
|
data/lib/repeatable/schedule.rb
CHANGED
@@ -1,9 +1,13 @@
|
|
1
|
+
# typed: strict
|
1
2
|
module Repeatable
|
2
3
|
class Schedule
|
4
|
+
extend T::Sig
|
5
|
+
|
6
|
+
sig { params(arg: Object).void }
|
3
7
|
def initialize(arg)
|
4
8
|
case arg
|
5
9
|
when Repeatable::Expression::Base
|
6
|
-
@expression = arg
|
10
|
+
@expression = T.let(arg, Expression::Base)
|
7
11
|
when Hash
|
8
12
|
@expression = Parser.call(arg)
|
9
13
|
else
|
@@ -11,43 +15,53 @@ module Repeatable
|
|
11
15
|
end
|
12
16
|
end
|
13
17
|
|
18
|
+
sig { params(start_date: Object, end_date: Object).returns(T::Array[::Date]) }
|
14
19
|
def occurrences(start_date, end_date)
|
15
|
-
start_date = Date(start_date)
|
16
|
-
end_date = Date(end_date)
|
20
|
+
start_date = Conversions::Date(start_date)
|
21
|
+
end_date = Conversions::Date(end_date)
|
17
22
|
|
18
|
-
fail(ArgumentError,
|
23
|
+
fail(ArgumentError, "end_date must be equal to or after start_date") if end_date < start_date
|
19
24
|
|
20
25
|
(start_date..end_date).select { |date| include?(date) }
|
21
26
|
end
|
22
27
|
|
28
|
+
sig { params(start_date: Object, include_start: T::Boolean, limit: Integer).returns(T.nilable(::Date)) }
|
23
29
|
def next_occurrence(start_date = Date.today, include_start: false, limit: 36525)
|
24
|
-
date = Date(start_date)
|
30
|
+
date = Conversions::Date(start_date)
|
25
31
|
|
26
32
|
return date if include_start && include?(date)
|
27
33
|
|
28
|
-
|
34
|
+
result = T.let(nil, T.nilable(Date))
|
35
|
+
1.step(limit) do |i|
|
29
36
|
date = date.next_day
|
30
37
|
|
31
|
-
|
32
|
-
|
38
|
+
if include?(date)
|
39
|
+
result = date
|
40
|
+
break
|
41
|
+
end
|
33
42
|
end
|
43
|
+
result
|
34
44
|
end
|
35
45
|
|
46
|
+
sig { params(date: Object).returns(T::Boolean) }
|
36
47
|
def include?(date = Date.today)
|
37
|
-
date = Date(date)
|
48
|
+
date = Conversions::Date(date)
|
38
49
|
expression.include?(date)
|
39
50
|
end
|
40
51
|
|
52
|
+
sig { returns(T::Hash[Symbol, T.any(Types::SymbolHash, T::Array[Types::SymbolHash])]) }
|
41
53
|
def to_h
|
42
54
|
expression.to_h
|
43
55
|
end
|
44
56
|
|
57
|
+
sig { params(other: Object).returns(T::Boolean) }
|
45
58
|
def ==(other)
|
46
59
|
other.is_a?(self.class) && expression == other.expression
|
47
60
|
end
|
48
61
|
|
49
62
|
protected
|
50
63
|
|
64
|
+
sig { returns(Expression::Base) }
|
51
65
|
attr_reader :expression
|
52
66
|
end
|
53
67
|
end
|
data/lib/repeatable/version.rb
CHANGED
data/lib/repeatable.rb
CHANGED
@@ -1,28 +1,34 @@
|
|
1
|
-
|
1
|
+
# typed: strict
|
2
|
+
require "date"
|
3
|
+
require "sorbet-runtime"
|
2
4
|
|
3
|
-
require
|
4
|
-
|
5
|
-
require 'repeatable/conversions'
|
6
|
-
include Repeatable::Conversions
|
5
|
+
require "repeatable/version"
|
7
6
|
|
8
7
|
module Repeatable
|
9
8
|
end
|
10
9
|
|
11
|
-
require
|
10
|
+
require "repeatable/parse_error"
|
11
|
+
|
12
|
+
require "repeatable/types"
|
13
|
+
|
14
|
+
require "repeatable/conversions"
|
15
|
+
require "repeatable/last_date_of_month"
|
12
16
|
|
13
|
-
require
|
14
|
-
require
|
17
|
+
require "repeatable/expression"
|
18
|
+
require "repeatable/expression/base"
|
15
19
|
|
16
|
-
require
|
17
|
-
require
|
18
|
-
require
|
19
|
-
require
|
20
|
-
require
|
21
|
-
require
|
20
|
+
require "repeatable/expression/date"
|
21
|
+
require "repeatable/expression/exact_date"
|
22
|
+
require "repeatable/expression/weekday"
|
23
|
+
require "repeatable/expression/biweekly"
|
24
|
+
require "repeatable/expression/weekday_in_month"
|
25
|
+
require "repeatable/expression/day_in_month"
|
26
|
+
require "repeatable/expression/range_in_year"
|
22
27
|
|
23
|
-
require
|
24
|
-
require
|
25
|
-
require
|
28
|
+
require "repeatable/expression/set"
|
29
|
+
require "repeatable/expression/union"
|
30
|
+
require "repeatable/expression/intersection"
|
31
|
+
require "repeatable/expression/difference"
|
26
32
|
|
27
|
-
require
|
28
|
-
require
|
33
|
+
require "repeatable/schedule"
|
34
|
+
require "repeatable/parser"
|