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.
Files changed (74) hide show
  1. checksums.yaml +5 -5
  2. data/.git-blame-ignore-revs +13 -0
  3. data/.github/workflows/ci.yml +72 -0
  4. data/.gitignore +0 -1
  5. data/.rspec +0 -1
  6. data/.standard.yml +3 -0
  7. data/CHANGELOG.md +58 -3
  8. data/CODE_OF_CONDUCT.md +128 -0
  9. data/Gemfile +11 -3
  10. data/Gemfile.lock +95 -0
  11. data/README.md +49 -9
  12. data/Rakefile +9 -3
  13. data/lib/repeatable/conversions.rb +5 -1
  14. data/lib/repeatable/expression/base.rb +37 -11
  15. data/lib/repeatable/expression/biweekly.rb +14 -6
  16. data/lib/repeatable/expression/date.rb +11 -8
  17. data/lib/repeatable/expression/day_in_month.rb +11 -1
  18. data/lib/repeatable/expression/difference.rb +37 -0
  19. data/lib/repeatable/expression/exact_date.rb +21 -0
  20. data/lib/repeatable/expression/intersection.rb +9 -0
  21. data/lib/repeatable/expression/range_in_year.rb +39 -9
  22. data/lib/repeatable/expression/set.rb +15 -7
  23. data/lib/repeatable/expression/union.rb +9 -0
  24. data/lib/repeatable/expression/weekday.rb +4 -0
  25. data/lib/repeatable/expression/weekday_in_month.rb +35 -4
  26. data/lib/repeatable/expression.rb +1 -0
  27. data/lib/repeatable/last_date_of_month.rb +11 -0
  28. data/lib/repeatable/parse_error.rb +1 -0
  29. data/lib/repeatable/parser.rb +14 -2
  30. data/lib/repeatable/schedule.rb +23 -9
  31. data/lib/repeatable/types.rb +6 -0
  32. data/lib/repeatable/version.rb +2 -1
  33. data/lib/repeatable.rb +25 -19
  34. data/rbi/repeatable.rbi +310 -0
  35. data/repeatable.gemspec +15 -15
  36. data/sorbet/config +3 -0
  37. data/sorbet/rbi/gems/ast.rbi +49 -0
  38. data/sorbet/rbi/gems/coderay.rbi +285 -0
  39. data/sorbet/rbi/gems/commander.rbi +197 -0
  40. data/sorbet/rbi/gems/docile.rbi +36 -0
  41. data/sorbet/rbi/gems/highline.rbi +577 -0
  42. data/sorbet/rbi/gems/method_source.rbi +64 -0
  43. data/sorbet/rbi/gems/parallel.rbi +83 -0
  44. data/sorbet/rbi/gems/parlour.rbi +840 -0
  45. data/sorbet/rbi/gems/parser.rbi +1950 -0
  46. data/sorbet/rbi/gems/pry.rbi +1898 -0
  47. data/sorbet/rbi/gems/rainbow.rbi +118 -0
  48. data/sorbet/rbi/gems/rake.rbi +646 -0
  49. data/sorbet/rbi/gems/regexp_parser.rbi +926 -0
  50. data/sorbet/rbi/gems/repeatable.rbi +13 -0
  51. data/sorbet/rbi/gems/rexml.rbi +583 -0
  52. data/sorbet/rbi/gems/rspec-core.rbi +1919 -0
  53. data/sorbet/rbi/gems/rspec-expectations.rbi +1150 -0
  54. data/sorbet/rbi/gems/rspec-mocks.rbi +1100 -0
  55. data/sorbet/rbi/gems/rspec-support.rbi +280 -0
  56. data/sorbet/rbi/gems/rspec.rbi +15 -0
  57. data/sorbet/rbi/gems/rubocop-ast.rbi +1356 -0
  58. data/sorbet/rbi/gems/rubocop-performance.rbi +487 -0
  59. data/sorbet/rbi/gems/rubocop.rbi +7923 -0
  60. data/sorbet/rbi/gems/ruby-progressbar.rbi +304 -0
  61. data/sorbet/rbi/gems/simplecov-html.rbi +35 -0
  62. data/sorbet/rbi/gems/simplecov.rbi +419 -0
  63. data/sorbet/rbi/gems/simplecov_json_formatter.rbi +47 -0
  64. data/sorbet/rbi/gems/standard.rbi +130 -0
  65. data/sorbet/rbi/gems/unicode-display_width.rbi +20 -0
  66. data/sorbet/rbi/hidden-definitions/errors.txt +4273 -0
  67. data/sorbet/rbi/hidden-definitions/hidden.rbi +9013 -0
  68. data/sorbet/rbi/sorbet-typed/lib/rainbow/all/rainbow.rbi +276 -0
  69. data/sorbet/rbi/sorbet-typed/lib/rake/all/rake.rbi +645 -0
  70. data/sorbet/rbi/sorbet-typed/lib/rspec-core/all/rspec-core.rbi +24 -0
  71. data/sorbet/rbi/sorbet-typed/lib/rubocop/>=1.8/rubocop.rbi +12 -0
  72. data/sorbet/rbi/sorbet-typed/lib/rubocop-performance/~>1.6/rubocop-performance.rbi +149 -0
  73. metadata +62 -45
  74. 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 - first_occurrence) % 14 == 0
14
+ date >= start_after && (date - _first_occurrence) % 14 == 0
11
15
  end
12
16
 
13
17
  private
14
18
 
15
- attr_reader :weekday, :start_after
19
+ sig { returns(Integer) }
20
+ attr_reader :weekday
16
21
 
17
- def first_occurrence
18
- @first_occurrence ||= find_first_occurrence
19
- end
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
- def to_h
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
- alias eql? ==
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(/^@/, '').to_sym
24
- hash[key] = normalize_attribute_value(instance_variable_get(name))
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
- date.day == day
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
- months_include?(date) || start_month_include?(date) || end_month_include?(date)
13
- end
22
+ return true if months_include?(date)
14
23
 
15
- def to_h
16
- args = { start_month: start_month }
17
- args[:end_month] = end_month unless end_month == start_month
18
- args[:start_day] = start_day unless start_day.zero?
19
- args[:end_day] = end_day unless end_day.zero?
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
- attr_reader :start_month, :end_month, :start_day, :end_day
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
- def to_h
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
- protected
28
+ private
24
29
 
25
- attr_reader :elements
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
- attr_reader :weekday, :count
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
- week_in_month(date.day) == count
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
- def week_in_month(day)
26
- ((day - 1) / 7) + 1
55
+ sig { returns(T::Boolean) }
56
+ def negative_count?
57
+ count < 0
27
58
  end
28
59
  end
29
60
  end
@@ -1,3 +1,4 @@
1
+ # typed: strict
1
2
  module Repeatable
2
3
  module Expression
3
4
  end
@@ -0,0 +1,11 @@
1
+ # typed: strict
2
+ module Repeatable
3
+ module LastDateOfMonth
4
+ extend T::Sig
5
+
6
+ sig { params(date: ::Date).returns(::Date) }
7
+ def last_date_of_month(date)
8
+ ::Date.new(date.next_month.year, date.next_month.month, 1).prev_day
9
+ end
10
+ end
11
+ end
@@ -1,3 +1,4 @@
1
+ # typed: strict
1
2
  module Repeatable
2
3
  class ParseError < StandardError
3
4
  end
@@ -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].capitalize }
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
@@ -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, 'end_date must be equal to or after start_date') if end_date < start_date
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
- 1.step do |i|
34
+ result = T.let(nil, T.nilable(Date))
35
+ 1.step(limit) do |i|
29
36
  date = date.next_day
30
37
 
31
- break date if include?(date)
32
- break if i == limit.to_i
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
@@ -0,0 +1,6 @@
1
+ # typed: strict
2
+ module Repeatable
3
+ module Types
4
+ SymbolHash = T.type_alias { T::Hash[Symbol, T.untyped] }
5
+ end
6
+ end
@@ -1,3 +1,4 @@
1
+ # typed: strict
1
2
  module Repeatable
2
- VERSION = '0.5.0'
3
+ VERSION = "1.1.0"
3
4
  end
data/lib/repeatable.rb CHANGED
@@ -1,28 +1,34 @@
1
- require 'date'
1
+ # typed: strict
2
+ require "date"
3
+ require "sorbet-runtime"
2
4
 
3
- require 'repeatable/version'
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 'repeatable/parse_error'
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 'repeatable/expression'
14
- require 'repeatable/expression/base'
17
+ require "repeatable/expression"
18
+ require "repeatable/expression/base"
15
19
 
16
- require 'repeatable/expression/date'
17
- require 'repeatable/expression/weekday'
18
- require 'repeatable/expression/biweekly'
19
- require 'repeatable/expression/weekday_in_month'
20
- require 'repeatable/expression/day_in_month'
21
- require 'repeatable/expression/range_in_year'
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 'repeatable/expression/set'
24
- require 'repeatable/expression/union'
25
- require 'repeatable/expression/intersection'
28
+ require "repeatable/expression/set"
29
+ require "repeatable/expression/union"
30
+ require "repeatable/expression/intersection"
31
+ require "repeatable/expression/difference"
26
32
 
27
- require 'repeatable/schedule'
28
- require 'repeatable/parser'
33
+ require "repeatable/schedule"
34
+ require "repeatable/parser"