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
@@ -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"
|