repeatable 1.0.0 → 1.2.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 +77 -0
- data/CHANGELOG.md +27 -1
- data/CODE_OF_CONDUCT.md +128 -0
- data/Gemfile +3 -0
- data/Gemfile.lock +89 -24
- data/README.md +44 -8
- data/Rakefile +5 -1
- data/bin/tapioca +29 -0
- data/lib/repeatable/conversions.rb +7 -2
- data/lib/repeatable/expression/base.rb +27 -11
- data/lib/repeatable/expression/biweekly.rb +15 -6
- data/lib/repeatable/expression/date.rb +11 -6
- data/lib/repeatable/expression/day_in_month.rb +5 -0
- data/lib/repeatable/expression/difference.rb +15 -5
- data/lib/repeatable/expression/exact_date.rb +6 -1
- data/lib/repeatable/expression/intersection.rb +8 -2
- data/lib/repeatable/expression/range_in_year.rb +34 -9
- data/lib/repeatable/expression/set.rb +17 -6
- data/lib/repeatable/expression/union.rb +7 -1
- data/lib/repeatable/expression/weekday.rb +5 -0
- data/lib/repeatable/expression/weekday_in_month.rb +15 -1
- data/lib/repeatable/expression.rb +2 -0
- data/lib/repeatable/last_date_of_month.rb +5 -0
- data/lib/repeatable/parse_error.rb +2 -0
- data/lib/repeatable/parser.rb +8 -1
- data/lib/repeatable/schedule.rb +24 -4
- data/lib/repeatable/types.rb +7 -0
- data/lib/repeatable/version.rb +3 -1
- data/lib/repeatable.rb +5 -0
- data/rbi/repeatable.rbi +316 -0
- data/repeatable.gemspec +3 -0
- data/sorbet/config +4 -0
- data/sorbet/rbi/annotations/.gitattributes +1 -0
- data/sorbet/rbi/annotations/rainbow.rbi +269 -0
- data/sorbet/rbi/gems/.gitattributes +1 -0
- data/sorbet/rbi/gems/ast@2.4.2.rbi +584 -0
- data/sorbet/rbi/gems/coderay@1.1.3.rbi +3426 -0
- data/sorbet/rbi/gems/commander@4.6.0.rbi +8 -0
- data/sorbet/rbi/gems/diff-lcs@1.4.4.rbi +1082 -0
- data/sorbet/rbi/gems/docile@1.4.0.rbi +376 -0
- data/sorbet/rbi/gems/erubi@1.12.0.rbi +145 -0
- data/sorbet/rbi/gems/highline@2.0.3.rbi +8 -0
- data/sorbet/rbi/gems/json@2.7.2.rbi +1561 -0
- data/sorbet/rbi/gems/language_server-protocol@3.17.0.3.rbi +14237 -0
- data/sorbet/rbi/gems/lint_roller@1.1.0.rbi +239 -0
- data/sorbet/rbi/gems/method_source@1.0.0.rbi +272 -0
- data/sorbet/rbi/gems/netrc@0.11.0.rbi +158 -0
- data/sorbet/rbi/gems/parallel@1.24.0.rbi +280 -0
- data/sorbet/rbi/gems/parlour@8.1.0.rbi +3053 -0
- data/sorbet/rbi/gems/parser@3.3.1.0.rbi +7320 -0
- data/sorbet/rbi/gems/prism@0.28.0.rbi +37903 -0
- data/sorbet/rbi/gems/pry@0.14.0.rbi +10072 -0
- data/sorbet/rbi/gems/racc@1.7.3.rbi +161 -0
- data/sorbet/rbi/gems/rainbow@3.1.1.rbi +402 -0
- data/sorbet/rbi/gems/rake@13.0.3.rbi +3024 -0
- data/sorbet/rbi/gems/rbi@0.1.13.rbi +3071 -0
- data/sorbet/rbi/gems/regexp_parser@2.9.0.rbi +3771 -0
- data/sorbet/rbi/gems/rexml@3.2.6.rbi +4781 -0
- data/sorbet/rbi/gems/rspec-core@3.10.1.rbi +10837 -0
- data/sorbet/rbi/gems/rspec-expectations@3.10.1.rbi +7930 -0
- data/sorbet/rbi/gems/rspec-mocks@3.10.2.rbi +5247 -0
- data/sorbet/rbi/gems/rspec-support@3.10.2.rbi +1594 -0
- data/sorbet/rbi/gems/rspec@3.10.0.rbi +76 -0
- data/sorbet/rbi/gems/rubocop-ast@1.31.3.rbi +7014 -0
- data/sorbet/rbi/gems/rubocop-performance@1.20.2.rbi +8 -0
- data/sorbet/rbi/gems/rubocop@1.62.1.rbi +57542 -0
- data/sorbet/rbi/gems/ruby-progressbar@1.13.0.rbi +1317 -0
- data/sorbet/rbi/gems/simplecov-html@0.12.3.rbi +216 -0
- data/sorbet/rbi/gems/simplecov@0.21.2.rbi +2135 -0
- data/sorbet/rbi/gems/simplecov_json_formatter@0.1.4.rbi +8 -0
- data/sorbet/rbi/gems/spoom@1.3.2.rbi +4420 -0
- data/sorbet/rbi/gems/standard-custom@1.0.2.rbi +8 -0
- data/sorbet/rbi/gems/standard-performance@1.3.1.rbi +8 -0
- data/sorbet/rbi/gems/standard@1.35.1.rbi +850 -0
- data/sorbet/rbi/gems/tapioca@0.13.3.rbi +3527 -0
- data/sorbet/rbi/gems/thor@1.3.1.rbi +4351 -0
- data/sorbet/rbi/gems/unicode-display_width@2.5.0.rbi +65 -0
- data/sorbet/rbi/gems/yard-sorbet@0.8.1.rbi +428 -0
- data/sorbet/rbi/gems/yard@0.9.36.rbi +18220 -0
- data/sorbet/tapioca/config.yml +13 -0
- data/sorbet/tapioca/require.rb +4 -0
- metadata +75 -6
- data/.travis.yml +0 -18
@@ -1,23 +1,32 @@
|
|
1
|
+
# typed: strict
|
2
|
+
|
1
3
|
module Repeatable
|
2
4
|
module Expression
|
3
5
|
class Biweekly < Date
|
6
|
+
sig { params(weekday: Integer, start_after: Object).void }
|
4
7
|
def initialize(weekday:, start_after: ::Date.today)
|
5
8
|
@weekday = weekday
|
6
|
-
@start_after = Conversions::Date(start_after)
|
9
|
+
@start_after = T.let(Conversions::Date(start_after), ::Date)
|
10
|
+
@_first_occurrence = T.let(find_first_occurrence, ::Date)
|
7
11
|
end
|
8
12
|
|
13
|
+
sig { override.params(date: ::Date).returns(T::Boolean) }
|
9
14
|
def include?(date)
|
10
|
-
date >= start_after && (date -
|
15
|
+
date >= start_after && (date - _first_occurrence) % 14 == 0
|
11
16
|
end
|
12
17
|
|
13
18
|
private
|
14
19
|
|
15
|
-
|
20
|
+
sig { returns(Integer) }
|
21
|
+
attr_reader :weekday
|
16
22
|
|
17
|
-
|
18
|
-
|
19
|
-
|
23
|
+
sig { returns(::Date) }
|
24
|
+
attr_reader :start_after
|
25
|
+
|
26
|
+
sig { returns(::Date) }
|
27
|
+
attr_reader :_first_occurrence
|
20
28
|
|
29
|
+
sig { returns(::Date) }
|
21
30
|
def find_first_occurrence
|
22
31
|
days_away = weekday - start_after.wday
|
23
32
|
days_away += 7 if days_away <= 0
|
@@ -1,29 +1,34 @@
|
|
1
|
+
# typed: strict
|
2
|
+
|
1
3
|
module Repeatable
|
2
4
|
module Expression
|
3
5
|
class Date < Base
|
4
|
-
|
5
|
-
Hash[hash_key, attributes]
|
6
|
-
end
|
6
|
+
abstract!
|
7
7
|
|
8
|
+
sig { params(other: Object).returns(T::Boolean) }
|
8
9
|
def ==(other)
|
9
10
|
other.is_a?(self.class) && attributes == other.attributes
|
10
11
|
end
|
11
|
-
|
12
12
|
alias_method :eql?, :==
|
13
13
|
|
14
|
+
sig { returns(Integer) }
|
14
15
|
def hash
|
15
16
|
[attributes.values, self.class.name].hash
|
16
17
|
end
|
17
18
|
|
18
19
|
protected
|
19
20
|
|
21
|
+
sig { returns(Types::SymbolHash) }
|
20
22
|
def attributes
|
21
23
|
instance_variables.each_with_object({}) do |name, hash|
|
22
|
-
key = name.to_s.gsub(/^@/, "")
|
23
|
-
|
24
|
+
key = name.to_s.gsub(/^@/, "")
|
25
|
+
next if key.start_with?("_")
|
26
|
+
hash[key.to_sym] = normalize_attribute_value(instance_variable_get(name))
|
24
27
|
end
|
25
28
|
end
|
29
|
+
alias_method :hash_value, :attributes
|
26
30
|
|
31
|
+
sig { params(value: BasicObject).returns(T.untyped) }
|
27
32
|
def normalize_attribute_value(value)
|
28
33
|
case value
|
29
34
|
when ::Date
|
@@ -1,12 +1,16 @@
|
|
1
|
+
# typed: strict
|
2
|
+
|
1
3
|
module Repeatable
|
2
4
|
module Expression
|
3
5
|
class DayInMonth < Date
|
4
6
|
include LastDateOfMonth
|
5
7
|
|
8
|
+
sig { params(day: Integer).void }
|
6
9
|
def initialize(day:)
|
7
10
|
@day = day
|
8
11
|
end
|
9
12
|
|
13
|
+
sig { override.params(date: ::Date).returns(T::Boolean) }
|
10
14
|
def include?(date)
|
11
15
|
if day < 0
|
12
16
|
date - last_date_of_month(date) - 1 == day
|
@@ -17,6 +21,7 @@ module Repeatable
|
|
17
21
|
|
18
22
|
private
|
19
23
|
|
24
|
+
sig { returns(Integer) }
|
20
25
|
attr_reader :day
|
21
26
|
end
|
22
27
|
end
|
@@ -1,19 +1,20 @@
|
|
1
|
+
# typed: strict
|
2
|
+
|
1
3
|
module Repeatable
|
2
4
|
module Expression
|
3
5
|
class Difference < Base
|
6
|
+
sig { params(included: Expression::Base, excluded: Expression::Base).void }
|
4
7
|
def initialize(included:, excluded:)
|
5
8
|
@included = included
|
6
9
|
@excluded = excluded
|
7
10
|
end
|
8
11
|
|
12
|
+
sig { override.params(date: ::Date).returns(T::Boolean) }
|
9
13
|
def include?(date)
|
10
14
|
included.include?(date) && !excluded.include?(date)
|
11
15
|
end
|
12
16
|
|
13
|
-
|
14
|
-
Hash[hash_key, {included: included.to_h, excluded: excluded.to_h}]
|
15
|
-
end
|
16
|
-
|
17
|
+
sig { params(other: Object).returns(T::Boolean) }
|
17
18
|
def ==(other)
|
18
19
|
other.is_a?(self.class) &&
|
19
20
|
included == other.included &&
|
@@ -22,7 +23,16 @@ module Repeatable
|
|
22
23
|
|
23
24
|
protected
|
24
25
|
|
25
|
-
|
26
|
+
sig { returns(Expression::Base) }
|
27
|
+
attr_reader :included
|
28
|
+
|
29
|
+
sig { returns(Expression::Base) }
|
30
|
+
attr_reader :excluded
|
31
|
+
|
32
|
+
sig { override.returns(Types::SymbolHash) }
|
33
|
+
def hash_value
|
34
|
+
{included: included.to_h, excluded: excluded.to_h}
|
35
|
+
end
|
26
36
|
end
|
27
37
|
end
|
28
38
|
end
|
@@ -1,16 +1,21 @@
|
|
1
|
+
# typed: strict
|
2
|
+
|
1
3
|
module Repeatable
|
2
4
|
module Expression
|
3
5
|
class ExactDate < Date
|
6
|
+
sig { params(date: Object).void }
|
4
7
|
def initialize(date:)
|
5
|
-
@date = Conversions::Date(date)
|
8
|
+
@date = T.let(Conversions::Date(date), ::Date)
|
6
9
|
end
|
7
10
|
|
11
|
+
sig { override.params(other_date: ::Date).returns(T::Boolean) }
|
8
12
|
def include?(other_date)
|
9
13
|
date == other_date
|
10
14
|
end
|
11
15
|
|
12
16
|
private
|
13
17
|
|
18
|
+
sig { returns(::Date) }
|
14
19
|
attr_reader :date
|
15
20
|
end
|
16
21
|
end
|
@@ -1,11 +1,17 @@
|
|
1
|
+
# typed: strict
|
2
|
+
|
1
3
|
module Repeatable
|
2
4
|
module Expression
|
3
5
|
class Intersection < Set
|
6
|
+
sig { params(elements: T.any(Expression::Base, T::Array[Expression::Base])).void }
|
4
7
|
def initialize(*elements)
|
5
|
-
|
6
|
-
|
8
|
+
elements = Array(elements).flatten
|
9
|
+
other_intersections, not_intersections = elements.partition { |e| e.is_a?(self.class) }
|
10
|
+
other_intersections = T.cast(other_intersections, T::Array[Expression::Intersection])
|
11
|
+
super(other_intersections.flat_map(&:elements).concat(not_intersections))
|
7
12
|
end
|
8
13
|
|
14
|
+
sig { override.params(date: ::Date).returns(T::Boolean) }
|
9
15
|
def include?(date)
|
10
16
|
elements.all? { |e| e.include?(date) }
|
11
17
|
end
|
@@ -1,6 +1,16 @@
|
|
1
|
+
# typed: strict
|
2
|
+
|
1
3
|
module Repeatable
|
2
4
|
module Expression
|
3
5
|
class RangeInYear < Date
|
6
|
+
sig do
|
7
|
+
params(
|
8
|
+
start_month: Integer,
|
9
|
+
end_month: Integer,
|
10
|
+
start_day: Integer,
|
11
|
+
end_day: Integer
|
12
|
+
).void
|
13
|
+
end
|
4
14
|
def initialize(start_month:, end_month: start_month, start_day: 0, end_day: 0)
|
5
15
|
@start_month = start_month
|
6
16
|
@end_month = end_month
|
@@ -8,6 +18,7 @@ module Repeatable
|
|
8
18
|
@end_day = end_day
|
9
19
|
end
|
10
20
|
|
21
|
+
sig { override.params(date: ::Date).returns(T::Boolean) }
|
11
22
|
def include?(date)
|
12
23
|
return true if months_include?(date)
|
13
24
|
|
@@ -18,29 +29,43 @@ module Repeatable
|
|
18
29
|
end
|
19
30
|
end
|
20
31
|
|
21
|
-
def to_h
|
22
|
-
args = {start_month: start_month}
|
23
|
-
args[:end_month] = end_month unless end_month == start_month
|
24
|
-
args[:start_day] = start_day unless start_day.zero?
|
25
|
-
args[:end_day] = end_day unless end_day.zero?
|
26
|
-
{range_in_year: args}
|
27
|
-
end
|
28
|
-
|
29
32
|
private
|
30
33
|
|
31
|
-
|
34
|
+
sig { returns(Integer) }
|
35
|
+
attr_reader :start_month
|
36
|
+
|
37
|
+
sig { returns(Integer) }
|
38
|
+
attr_reader :end_month
|
32
39
|
|
40
|
+
sig { returns(Integer) }
|
41
|
+
attr_reader :start_day
|
42
|
+
|
43
|
+
sig { returns(Integer) }
|
44
|
+
attr_reader :end_day
|
45
|
+
|
46
|
+
sig { params(date: ::Date).returns(T::Boolean) }
|
33
47
|
def months_include?(date)
|
34
48
|
date.month > start_month && date.month < end_month
|
35
49
|
end
|
36
50
|
|
51
|
+
sig { params(date: ::Date).returns(T::Boolean) }
|
37
52
|
def start_month_include?(date)
|
38
53
|
date.month == start_month && (start_day == 0 || date.day >= start_day)
|
39
54
|
end
|
40
55
|
|
56
|
+
sig { params(date: ::Date).returns(T::Boolean) }
|
41
57
|
def end_month_include?(date)
|
42
58
|
date.month == end_month && (end_day == 0 || date.day <= end_day)
|
43
59
|
end
|
60
|
+
|
61
|
+
sig { override.returns(T::Hash[Symbol, Integer]) }
|
62
|
+
def hash_value
|
63
|
+
args = {start_month: start_month}
|
64
|
+
args[:end_month] = end_month unless end_month == start_month
|
65
|
+
args[:start_day] = start_day unless start_day.zero?
|
66
|
+
args[:end_day] = end_day unless end_day.zero?
|
67
|
+
args
|
68
|
+
end
|
44
69
|
end
|
45
70
|
end
|
46
71
|
end
|
@@ -1,26 +1,37 @@
|
|
1
|
+
# typed: strict
|
2
|
+
|
1
3
|
module Repeatable
|
2
4
|
module Expression
|
3
5
|
class Set < Base
|
6
|
+
abstract!
|
7
|
+
|
8
|
+
sig { returns(T::Array[Expression::Base]) }
|
4
9
|
attr_reader :elements
|
5
10
|
|
6
|
-
|
7
|
-
|
11
|
+
sig { params(elements: T::Array[Expression::Base]).void }
|
12
|
+
def initialize(elements)
|
13
|
+
@elements = T.let(elements.flatten.uniq, T::Array[Expression::Base])
|
8
14
|
end
|
9
15
|
|
16
|
+
sig { params(element: T.untyped).returns(Repeatable::Expression::Set) }
|
10
17
|
def <<(element)
|
11
18
|
elements << element unless elements.include?(element)
|
12
19
|
self
|
13
20
|
end
|
14
21
|
|
15
|
-
|
16
|
-
Hash[hash_key, elements.map(&:to_h)]
|
17
|
-
end
|
18
|
-
|
22
|
+
sig { params(other: Object).returns(T::Boolean) }
|
19
23
|
def ==(other)
|
20
24
|
other.is_a?(self.class) &&
|
21
25
|
elements.size == other.elements.size &&
|
22
26
|
other.elements.all? { |e| elements.include?(e) }
|
23
27
|
end
|
28
|
+
|
29
|
+
private
|
30
|
+
|
31
|
+
sig { override.returns(T::Array[Types::SymbolHash]) }
|
32
|
+
def hash_value
|
33
|
+
elements.map(&:to_h)
|
34
|
+
end
|
24
35
|
end
|
25
36
|
end
|
26
37
|
end
|
@@ -1,11 +1,17 @@
|
|
1
|
+
# typed: strict
|
2
|
+
|
1
3
|
module Repeatable
|
2
4
|
module Expression
|
3
5
|
class Union < Set
|
6
|
+
sig { params(elements: T.any(Expression::Base, T::Array[Expression::Base])).void }
|
4
7
|
def initialize(*elements)
|
8
|
+
elements = Array(elements).flatten
|
5
9
|
other_unions, not_unions = elements.partition { |e| e.is_a?(self.class) }
|
6
|
-
|
10
|
+
other_unions = T.cast(other_unions, T::Array[Expression::Union])
|
11
|
+
super(other_unions.flat_map(&:elements).concat(not_unions))
|
7
12
|
end
|
8
13
|
|
14
|
+
sig { override.params(date: ::Date).returns(T::Boolean) }
|
9
15
|
def include?(date)
|
10
16
|
elements.any? { |e| e.include?(date) }
|
11
17
|
end
|
@@ -1,16 +1,21 @@
|
|
1
|
+
# typed: strict
|
2
|
+
|
1
3
|
module Repeatable
|
2
4
|
module Expression
|
3
5
|
class Weekday < Date
|
6
|
+
sig { params(weekday: Integer).void }
|
4
7
|
def initialize(weekday:)
|
5
8
|
@weekday = weekday
|
6
9
|
end
|
7
10
|
|
11
|
+
sig { override.params(date: ::Date).returns(T::Boolean) }
|
8
12
|
def include?(date)
|
9
13
|
date.wday == weekday
|
10
14
|
end
|
11
15
|
|
12
16
|
private
|
13
17
|
|
18
|
+
sig { returns(Integer) }
|
14
19
|
attr_reader :weekday
|
15
20
|
end
|
16
21
|
end
|
@@ -1,25 +1,35 @@
|
|
1
|
+
# typed: strict
|
2
|
+
|
1
3
|
module Repeatable
|
2
4
|
module Expression
|
3
5
|
class WeekdayInMonth < Date
|
4
6
|
include LastDateOfMonth
|
5
7
|
|
8
|
+
sig { params(weekday: Integer, count: Integer).void }
|
6
9
|
def initialize(weekday:, count:)
|
7
10
|
@weekday = weekday
|
8
11
|
@count = count
|
9
12
|
end
|
10
13
|
|
14
|
+
sig { override.params(date: ::Date).returns(T::Boolean) }
|
11
15
|
def include?(date)
|
12
16
|
day_matches?(date) && week_matches?(date)
|
13
17
|
end
|
14
18
|
|
15
19
|
private
|
16
20
|
|
17
|
-
|
21
|
+
sig { returns(Integer) }
|
22
|
+
attr_reader :weekday
|
23
|
+
|
24
|
+
sig { returns(Integer) }
|
25
|
+
attr_reader :count
|
18
26
|
|
27
|
+
sig { params(date: ::Date).returns(T::Boolean) }
|
19
28
|
def day_matches?(date)
|
20
29
|
date.wday == weekday
|
21
30
|
end
|
22
31
|
|
32
|
+
sig { params(date: ::Date).returns(T::Boolean) }
|
23
33
|
def week_matches?(date)
|
24
34
|
if negative_count?
|
25
35
|
week_from_end(date) == count
|
@@ -28,18 +38,22 @@ module Repeatable
|
|
28
38
|
end
|
29
39
|
end
|
30
40
|
|
41
|
+
sig { params(date: ::Date).returns(Integer) }
|
31
42
|
def week_from_beginning(date)
|
32
43
|
week_in_month(date.day - 1)
|
33
44
|
end
|
34
45
|
|
46
|
+
sig { params(date: ::Date).returns(Integer) }
|
35
47
|
def week_from_end(date)
|
36
48
|
-week_in_month(last_date_of_month(date).day - date.day)
|
37
49
|
end
|
38
50
|
|
51
|
+
sig { params(zero_indexed_day: Integer).returns(Integer) }
|
39
52
|
def week_in_month(zero_indexed_day)
|
40
53
|
(zero_indexed_day / 7) + 1
|
41
54
|
end
|
42
55
|
|
56
|
+
sig { returns(T::Boolean) }
|
43
57
|
def negative_count?
|
44
58
|
count < 0
|
45
59
|
end
|
data/lib/repeatable/parser.rb
CHANGED
@@ -1,13 +1,20 @@
|
|
1
|
+
# typed: false
|
2
|
+
|
1
3
|
module Repeatable
|
2
4
|
class Parser
|
5
|
+
extend T::Sig
|
6
|
+
|
7
|
+
sig { params(hash: T::Hash[T.any(String, Symbol), T.untyped]).void }
|
3
8
|
def initialize(hash)
|
4
9
|
@hash = hash
|
5
10
|
end
|
6
11
|
|
12
|
+
sig { params(hash: T::Hash[T.any(String, Symbol), T.untyped]).returns(Expression::Base) }
|
7
13
|
def self.call(hash)
|
8
14
|
new(hash).call
|
9
15
|
end
|
10
16
|
|
17
|
+
sig { returns(Expression::Base) }
|
11
18
|
def call
|
12
19
|
build_expression(hash)
|
13
20
|
end
|
@@ -50,7 +57,7 @@ module Repeatable
|
|
50
57
|
def expression_klass(string)
|
51
58
|
camel_cased_string = string
|
52
59
|
.capitalize
|
53
|
-
.gsub(/(?:_)(?<word>[a-z\d]+)/i) { Regexp.last_match[:word]
|
60
|
+
.gsub(/(?:_)(?<word>[a-z\d]+)/i) { T.must(Regexp.last_match)[:word]&.capitalize }
|
54
61
|
Repeatable::Expression.const_get(camel_cased_string)
|
55
62
|
rescue NameError => e
|
56
63
|
raise if e.name && e.name.to_s != camel_cased_string
|
data/lib/repeatable/schedule.rb
CHANGED
@@ -1,9 +1,14 @@
|
|
1
|
+
# typed: strict
|
2
|
+
|
1
3
|
module Repeatable
|
2
4
|
class Schedule
|
5
|
+
extend T::Sig
|
6
|
+
|
7
|
+
sig { params(arg: Object).void }
|
3
8
|
def initialize(arg)
|
4
9
|
case arg
|
5
10
|
when Repeatable::Expression::Base
|
6
|
-
@expression = arg
|
11
|
+
@expression = T.let(arg, Expression::Base)
|
7
12
|
when Hash
|
8
13
|
@expression = Parser.call(arg)
|
9
14
|
else
|
@@ -11,6 +16,7 @@ module Repeatable
|
|
11
16
|
end
|
12
17
|
end
|
13
18
|
|
19
|
+
sig { params(start_date: Object, end_date: Object).returns(T::Array[::Date]) }
|
14
20
|
def occurrences(start_date, end_date)
|
15
21
|
start_date = Conversions::Date(start_date)
|
16
22
|
end_date = Conversions::Date(end_date)
|
@@ -20,34 +26,48 @@ module Repeatable
|
|
20
26
|
(start_date..end_date).select { |date| include?(date) }
|
21
27
|
end
|
22
28
|
|
29
|
+
sig { params(start_date: Object, include_start: T::Boolean, limit: Integer).returns(T.nilable(::Date)) }
|
23
30
|
def next_occurrence(start_date = Date.today, include_start: false, limit: 36525)
|
24
31
|
date = Conversions::Date(start_date)
|
25
32
|
|
26
33
|
return date if include_start && include?(date)
|
27
34
|
|
28
|
-
|
35
|
+
result = T.let(nil, T.nilable(Date))
|
36
|
+
1.step(limit) do |i|
|
29
37
|
date = date.next_day
|
30
38
|
|
31
|
-
|
32
|
-
|
39
|
+
if include?(date)
|
40
|
+
result = date
|
41
|
+
break
|
42
|
+
end
|
33
43
|
end
|
44
|
+
result
|
34
45
|
end
|
35
46
|
|
47
|
+
sig { params(date: Object).returns(T::Boolean) }
|
36
48
|
def include?(date = Date.today)
|
37
49
|
date = Conversions::Date(date)
|
38
50
|
expression.include?(date)
|
39
51
|
end
|
40
52
|
|
53
|
+
sig { returns(T::Hash[Symbol, T.any(Types::SymbolHash, T::Array[Types::SymbolHash])]) }
|
41
54
|
def to_h
|
42
55
|
expression.to_h
|
43
56
|
end
|
44
57
|
|
58
|
+
sig { params(_keys: T.nilable(T::Array[Symbol])).returns(T::Hash[Symbol, T.any(Types::SymbolHash, T::Array[Types::SymbolHash])]) }
|
59
|
+
def deconstruct_keys(_keys)
|
60
|
+
to_h
|
61
|
+
end
|
62
|
+
|
63
|
+
sig { params(other: Object).returns(T::Boolean) }
|
45
64
|
def ==(other)
|
46
65
|
other.is_a?(self.class) && expression == other.expression
|
47
66
|
end
|
48
67
|
|
49
68
|
protected
|
50
69
|
|
70
|
+
sig { returns(Expression::Base) }
|
51
71
|
attr_reader :expression
|
52
72
|
end
|
53
73
|
end
|
data/lib/repeatable/version.rb
CHANGED
data/lib/repeatable.rb
CHANGED
@@ -1,4 +1,7 @@
|
|
1
|
+
# typed: strict
|
2
|
+
|
1
3
|
require "date"
|
4
|
+
require "sorbet-runtime"
|
2
5
|
|
3
6
|
require "repeatable/version"
|
4
7
|
|
@@ -7,6 +10,8 @@ end
|
|
7
10
|
|
8
11
|
require "repeatable/parse_error"
|
9
12
|
|
13
|
+
require "repeatable/types"
|
14
|
+
|
10
15
|
require "repeatable/conversions"
|
11
16
|
require "repeatable/last_date_of_month"
|
12
17
|
|