reprise 0.1.0-arm64-darwin

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.
@@ -0,0 +1,299 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Reprise
4
+ class Error < StandardError; end
5
+ class InvalidRangeError < Error; end
6
+
7
+ # The +Reprise::Schedule+ class is the primary interface of the Reprise gem.
8
+ #
9
+ # It offers methods that enable you to:
10
+ # - Initialize a new schedule.
11
+ # - Add recurring series to the schedule via +#repeat_*+ methods.
12
+ # - Mark specific intervals of time as excluded from the schedule via {Reprise::Schedule#add_exclusion} and {Reprise::Schedule#add_exclusions}.
13
+ # - Query for the presence of occurrences within intervals of time via {Reprise::Schedule#occurs_between?}.
14
+ # - Generate an array of all of the schedule's occurrences via {Reprise::Schedule#occurrences}.
15
+ #
16
+ # @private On the interface of this class: all of the methods on +Reprise::Schedule+ could have been
17
+ # transparently delegated to +Reprise::Core::Schedule+, the internal schedule class that is
18
+ # implemented in Rust; instead, we define explicit proxy methods with obvious kwargs duplication,
19
+ # both to make it easier to generate YARD docs and to offer decent autocomplete support in IDEs.
20
+ # For any changes in the implementation of the interface, prefer DevX over DRY and save our
21
+ # sophistication budget for the underlying Rust extension.
22
+ class Schedule
23
+ # All schedules must be constructed with a valid +starts_at+ and +ends_at+ time.
24
+ # Reprise does not support infinitely-recurring schedules, or the bounding
25
+ # of schedules on the basis of a maximum occurrence count.
26
+ #
27
+ # @param starts_at [Time, ActiveSupport::TimeWithZone]
28
+ # The beginning of the schedule; the earliest possible moment for a valid occurrence.
29
+ # If no +time_zone+ is given, the schedule's time zone will be inferred from +starts_at+,
30
+ # defaulting to UTC if it lacks time zone information (e.g. it is a plain +Time+).
31
+ # @param ends_at [Time, ActiveSupport::TimeWithZone]
32
+ # The end of the schedule; the latest possible moment for a valid occurrence.
33
+ # @param time_zone [String]
34
+ # Must be an unambiguous, valid Rails time zone string or IANA time-zone identifier
35
+ # according to +ActiveSupport::TimeZone::find_tzinfo+.
36
+ # See https://github.com/tzinfo/tzinfo/issues/53
37
+ # @raise [Reprise::InvalidTimeZoneError] if the time zone is ambiguous or invalid.
38
+ def initialize(starts_at:, ends_at:, time_zone: nil)
39
+ raise InvalidRangeError, "The end time cannot precede the start time" if ends_at < starts_at
40
+
41
+ @starts_at = starts_at
42
+ @ends_at = ends_at
43
+ @time_zone = TimeZoneIdentifier.new(time_zone:, datetime_source: starts_at).to_s
44
+ @default_time_of_day = TimeOfDay.new(starts_at)
45
+ end
46
+
47
+ # Returns an array of occurrences sorted in order of ascending occurrence start time.
48
+ # This method is not cached; on every call, it will recompute all of the schedule's occurrences.
49
+ # @return [Array<Reprise::Core::Occurrence>]
50
+ def occurrences
51
+ internal_schedule.occurrences
52
+ end
53
+
54
+ # @!macro [new] weekday
55
+ # @param weekday [Symbol] Accepts +:monday+, +:tuesday+, +:wednesday+, +:thursday+, or +:friday+.
56
+
57
+ # @!macro [new] time_of_day
58
+ # @param time_of_day [Hash,Time,nil]
59
+ # Either a local time value from which the hour, minute, and second
60
+ # should be derived, or a hash containing at least one of +hour+, +minute+,
61
+ # or +second+. If +nil+, the time of day will be inferred from the schedule's
62
+ # +starts_at+ value.
63
+ # @option time_of_day [Integer] :hour, >= 0 && <= 23
64
+ # @option time_of_day [Integer] :minute, >= 0 && <= 59
65
+ # @option time_of_day [Integer] :second, >= 0 && <= 59
66
+ # @raise [UnsupportedTypeError] if +time_of_day+ is neither a +Hash+ nor a +Time+.
67
+ # @raise [InvalidHashError] if the hash representation of the time is invalid.
68
+ # @raise [RangeError] if either the hour, minute, or second is out-of-range.
69
+
70
+ # @!macro [new] interval
71
+ # @param interval [Integer]
72
+ # This determines whether or not occurrences should be skipped.
73
+ # A value of +1+ means that every occurrence for the series should be returned;
74
+ # +2+, every other occurrence should be returned, etc.
75
+
76
+ # @!macro [new] recurring_series_start_and_end_times
77
+ # @param starts_at [Time, nil] The time that the series should begin. If left blank,
78
+ # the series will start at the same time as the parent schedule.
79
+ # @param ends_at [Time, nil] The time that the series should end. If left blank,
80
+ # the series will end at the same time as the parent schedule.
81
+
82
+ # @!macro [new] duration_in_seconds
83
+ # @param duration_in_seconds [Integer]
84
+ # This determines the end time of each occurrence ({Reprise::Core::Occurrence#end_time}), and also
85
+ # influences occurrence queries, and whether any added exclusions conflict with any of the schedule's
86
+ # occurrences.
87
+
88
+ # @!macro [new] label
89
+ # @param label [String, nil] An optional label to apply to all of the occurrences
90
+ # that are generated from the series. See {Reprise::Core::Occurrence#label}.
91
+
92
+ # @!macro time_of_day
93
+ # @!macro duration_in_seconds
94
+ # @!macro interval
95
+ # @!macro recurring_series_start_and_end_times
96
+ # @!macro label
97
+ # @return [void]
98
+ def repeat_minutely(time_of_day: nil, duration_in_seconds:, interval: 1, starts_at: nil, ends_at: nil, label: nil)
99
+ internal_schedule.repeat_minutely(
100
+ time_of_day: TimeOfDay.new(time_of_day || self.starts_at).to_h,
101
+ duration_in_seconds:,
102
+ interval:,
103
+ starts_at_unix_timestamp: starts_at.presence&.to_i,
104
+ ends_at_unix_timestamp: ends_at.presence&.to_i,
105
+ label:
106
+ )
107
+ end
108
+
109
+ # @!macro time_of_day
110
+ # @!macro duration_in_seconds
111
+ # @!macro interval
112
+ # @!macro recurring_series_start_and_end_times
113
+ # @!macro label
114
+ # @return [void]
115
+ def repeat_hourly(time_of_day: nil, duration_in_seconds:, interval: 1, starts_at: nil, ends_at: nil, label: nil)
116
+ internal_schedule.repeat_hourly(
117
+ time_of_day: TimeOfDay.new(time_of_day || self.starts_at).to_h,
118
+ duration_in_seconds:,
119
+ interval:,
120
+ starts_at_unix_timestamp: starts_at.presence&.to_i,
121
+ ends_at_unix_timestamp: ends_at.presence&.to_i,
122
+ label:
123
+ )
124
+ end
125
+
126
+ # @!macro time_of_day
127
+ # @!macro duration_in_seconds
128
+ # @!macro interval
129
+ # @!macro recurring_series_start_and_end_times
130
+ # @!macro label
131
+ # @return [void]
132
+ def repeat_daily(time_of_day: nil, duration_in_seconds:, interval: 1, starts_at: nil, ends_at: nil, label: nil)
133
+ internal_schedule.repeat_daily(
134
+ time_of_day: TimeOfDay.new(time_of_day || self.starts_at).to_h,
135
+ duration_in_seconds:,
136
+ interval:,
137
+ starts_at_unix_timestamp: starts_at.presence&.to_i,
138
+ ends_at_unix_timestamp: ends_at.presence&.to_i,
139
+ label:
140
+ )
141
+ end
142
+
143
+ # @!macro weekday
144
+ # @!macro time_of_day
145
+ # @!macro duration_in_seconds
146
+ # @!macro interval
147
+ # @!macro recurring_series_start_and_end_times
148
+ # @!macro label
149
+ # @return [void]
150
+ # @example with a +time_of_day+ hash
151
+ # schedule.repeat_weekly(:monday, time_of_day: { hour: 6 }, duration_in_seconds: 30)
152
+ # @example with a local time for +time_of_day+
153
+ # local_time = Time.current.in_time_zone(my_current_time_zone)
154
+ # schedule.repeat_weekly(:monday, time_of_day: local_time, duration_in_seconds: 30)
155
+ def repeat_weekly(weekday, time_of_day: nil, duration_in_seconds:, interval: 1, starts_at: nil, ends_at: nil, label: nil)
156
+ internal_schedule.repeat_weekly(
157
+ weekday,
158
+ time_of_day: TimeOfDay.new(time_of_day || self.starts_at).to_h,
159
+ duration_in_seconds:,
160
+ interval:,
161
+ starts_at_unix_timestamp: starts_at.presence&.to_i,
162
+ ends_at_unix_timestamp: ends_at.presence&.to_i,
163
+ label:
164
+ )
165
+ end
166
+
167
+ # @param day_number [Integer] The number of the day in the month; >= 1 && <= 31
168
+ # @!macro time_of_day
169
+ # @!macro duration_in_seconds
170
+ # @!macro interval
171
+ # @!macro recurring_series_start_and_end_times
172
+ # @!macro label
173
+ # @return [void]
174
+ # @example
175
+ # schedule.repeat_monthly_by_day(15, time_of_day: { hour: 9 }, duration_in_seconds: 30)
176
+ def repeat_monthly_by_day(day_number, time_of_day:, duration_in_seconds:, interval: 1, starts_at: nil, ends_at: nil, label: nil)
177
+ internal_schedule.repeat_monthly_by_day(
178
+ day_number,
179
+ time_of_day: TimeOfDay.new(time_of_day || self.starts_at).to_h,
180
+ duration_in_seconds:,
181
+ interval:,
182
+ starts_at_unix_timestamp: starts_at.presence&.to_i,
183
+ ends_at_unix_timestamp: ends_at.presence&.to_i,
184
+ label:
185
+ )
186
+ end
187
+
188
+ # @!macro weekday
189
+ # @param nth_day [Integer] The nth weekday, 0-indexed; e.g. 0 might represent the first wednesday
190
+ # @!macro time_of_day
191
+ # @!macro duration_in_seconds
192
+ # @!macro interval
193
+ # @!macro recurring_series_start_and_end_times
194
+ # @!macro label
195
+ # @return [void]
196
+ def repeat_monthly_by_nth_weekday(weekday, nth_day, time_of_day:, duration_in_seconds:, interval: 1, starts_at: nil, ends_at: nil, label: nil)
197
+ internal_schedule.repeat_monthly_by_nth_weekday(
198
+ weekday,
199
+ nth_day,
200
+ time_of_day: TimeOfDay.new(time_of_day || self.starts_at).to_h,
201
+ duration_in_seconds:,
202
+ interval:,
203
+ starts_at_unix_timestamp: starts_at.presence&.to_i,
204
+ ends_at_unix_timestamp: ends_at.presence&.to_i,
205
+ label:
206
+ )
207
+ end
208
+
209
+ # @param day_number [Integer] The number of the day in the year; >= 1 && <= 366
210
+ # @!macro time_of_day
211
+ # @!macro duration_in_seconds
212
+ # @!macro interval
213
+ # @!macro recurring_series_start_and_end_times
214
+ # @!macro label
215
+ # @return [void]
216
+ # @example
217
+ # schedule.repeat_annually_by_day(200, duration_in_seconds: 30)
218
+ def repeat_annually_by_day(day_number, time_of_day:, duration_in_seconds:, interval: 1, starts_at: nil, ends_at: nil, label: nil)
219
+ internal_schedule.repeat_annually_by_day(
220
+ day_number,
221
+ time_of_day: TimeOfDay.new(time_of_day || self.starts_at).to_h,
222
+ duration_in_seconds:,
223
+ interval:,
224
+ starts_at_unix_timestamp: starts_at.presence&.to_i,
225
+ ends_at_unix_timestamp: ends_at.presence&.to_i,
226
+ label:
227
+ )
228
+ end
229
+
230
+ # Add a time interval between which no occurrences are valid.
231
+ # Any occurrences that overlap with an exclusion are removed from the schedule's occurrences.
232
+ # @param starts_at [Time] The time that the exclusion starts at
233
+ # @param ends_at [Time] The time that the exclusion ends at
234
+ def add_exclusion(starts_at:, ends_at:)
235
+ internal_schedule.add_exclusion(
236
+ starts_at_unix_timestamp: starts_at.to_i,
237
+ ends_at_unix_timestamp: ends_at.to_i
238
+ )
239
+ end
240
+
241
+ # Add time intervals between which no occurrences are valid.
242
+ # Any occurrences that overlap with an exclusion are removed from the schedule's occurrences.
243
+ # @param exclusions [Array<Array<Time,Time>>] An array of exclusion arrays, consisting of start
244
+ # and end +Time+ values.
245
+ # @return [void]
246
+ # @example
247
+ # schedule.add_exclusions([
248
+ # [exclusion_1_starts_at, exclusion_1_ends_at],
249
+ # [exclusion_2_starts_at, exclusion_2_ends_at],
250
+ # ])
251
+ def add_exclusions(exclusions)
252
+ internal_schedule.add_exclusions(
253
+ exclusions.map {|e| e.map(&:to_i) }
254
+ )
255
+ end
256
+
257
+ # @!macro [new] include_overlapping
258
+ # @param include_overlapping [Boolean] when true, the query will also consider
259
+ # occurrences that partially overlap with the given interval, not just the occurrences
260
+ # that are entirely contained within the interval.
261
+
262
+ # Indicates whether one or more of your schedule's occurrences fall within the given interval.
263
+ # @param starts_at [Time] The start of the interval to query
264
+ # @param ends_at [Time] The end of the interval to query
265
+ # @!macro include_overlapping
266
+ # @return [Boolean]
267
+ def occurs_between?(starts_at, ends_at, include_overlapping: false)
268
+ occurrences_between(starts_at, ends_at, include_overlapping:).any?
269
+ end
270
+
271
+ # This method efficiently queries your schedule for occurrences that fall within a given interval.
272
+ # @param starts_at [Time] The start of the interval to query
273
+ # @param ends_at [Time] The end of the interval to query
274
+ # @!macro include_overlapping
275
+ # @return [Array<Reprise::Core::Occurrence>] an array of occurrences that occur between
276
+ # the given +starts_at+ and +ends_at+ bookends.
277
+ def occurrences_between(starts_at, ends_at, include_overlapping: false)
278
+ if include_overlapping
279
+ internal_schedule.occurrences_overlapping_with_interval(starts_at.to_i, ends_at.to_i)
280
+ else
281
+ internal_schedule.occurrences_contained_within_interval(starts_at.to_i, ends_at.to_i)
282
+ end
283
+ end
284
+
285
+ private
286
+
287
+ attr_reader :starts_at, :ends_at, :time_zone, :default_time_of_day
288
+
289
+ def internal_schedule
290
+ return @_internal_schedule if defined?(@_internal_schedule)
291
+
292
+ @_internal_schedule = ::Reprise::Core::Schedule.new(
293
+ starts_at.to_i,
294
+ ends_at.to_i,
295
+ time_zone
296
+ )
297
+ end
298
+ end
299
+ end
@@ -0,0 +1,75 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "active_support"
4
+ require "active_support/core_ext"
5
+
6
+ module Reprise
7
+ # @api private
8
+ class TimeOfDay
9
+ class UnsupportedTypeError < Reprise::Error; end
10
+ class InvalidHashError < Reprise::Error; end
11
+ class RangeError < Reprise::Error; end
12
+
13
+ DEFAULT_TIME_OF_DAY = { hour: 0, minute: 0, second: 0 }.freeze
14
+ TIME_OF_DAY_ATTRIBUTES = DEFAULT_TIME_OF_DAY.keys.freeze
15
+ attr_accessor(*TIME_OF_DAY_ATTRIBUTES)
16
+
17
+ # @param [Time, Hash] time_of_day
18
+ # Accepts either a local time value from which the hour, minute, and second
19
+ # should be derived, or a hash containing at least one of +hour+, +minute+,
20
+ # or +second+.
21
+ # @option time_of_day [Integer] :hour, >= 0 && <= 23
22
+ # @option time_of_day [Integer] :minute, >= 0 && <= 59
23
+ # @option time_of_day [Integer] :second, >= 0 && <= 59
24
+ # @example
25
+ # { hour: 1, minute: 30, second: 15 }
26
+ # @raise [UnsupportedTypeError] if +time_of_day+ is neither a +Hash+ nor a +Time+.
27
+ # @raise [InvalidHashError] if the hash representation of the time is invalid.
28
+ # @raise [RangeError] if either the hour, minute, or second is out-of-range.
29
+ def initialize(time_of_day)
30
+ return initialize_from_hash(time_of_day) if time_of_day.is_a?(Hash)
31
+ return initialize_from_time(time_of_day) if time_of_day.is_a?(Time)
32
+
33
+ raise UnsupportedTypeError, "#{time_of_day.class} is not a supported type"
34
+ end
35
+
36
+ # @return [Hash]
37
+ # @example
38
+ # { hour: 1, minute: 30, second: 15 }
39
+ def to_h
40
+ {
41
+ hour:,
42
+ minute:,
43
+ second:
44
+ }
45
+ end
46
+
47
+ private
48
+
49
+ # @private
50
+ def initialize_from_time(time)
51
+ self.hour = time.hour
52
+ self.minute = time.min
53
+ self.second = time.sec
54
+ end
55
+
56
+ # @private
57
+ def initialize_from_hash(hms_opts)
58
+ raise InvalidHashError if (hms_opts.keys - TIME_OF_DAY_ATTRIBUTES).any?
59
+
60
+ DEFAULT_TIME_OF_DAY.merge(hms_opts).slice(*TIME_OF_DAY_ATTRIBUTES).each do |key, value|
61
+ public_send("#{key}=", value)
62
+ end
63
+
64
+ validate_time_of_day
65
+ end
66
+
67
+ def validate_time_of_day
68
+ return if (hour >= 0 && hour <= 23) &&
69
+ (minute >= 0 && minute <= 59) &&
70
+ (second >= 0 && second <= 59)
71
+
72
+ raise RangeError, "The time of day is out of range (#{to_h})"
73
+ end
74
+ end
75
+ end
@@ -0,0 +1,51 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "active_support"
4
+ require "active_support/core_ext"
5
+
6
+ module Reprise
7
+ class InvalidTimeZoneError < Reprise::Error; end
8
+
9
+ # @api private
10
+ class TimeZoneIdentifier
11
+ # https://github.com/rails/rails/blob/19eebf6d33dd15a0172e3ed2481bec57a89a2404/activesupport/lib/active_support/values/time_zone.rb#L76
12
+ UTC_TIME_ZONE_IDENTIFIER = "Etc/UTC"
13
+
14
+ # @param time_zone [String]
15
+ # Must be an unambiguous, valid Rails time zone string or IANA time-zone identifier
16
+ # according to +ActiveSupport::TimeZone::find_tzinfo+.
17
+ # See https://github.com/tzinfo/tzinfo/issues/53
18
+ # @param datetime_source [Time, ActiveSupport::TimeWithZone]
19
+ # A time value from which the time zone will be inferred.
20
+ # Only considered if no explicit +time_zone+ option is given.
21
+ def initialize(time_zone: nil, datetime_source:)
22
+ @time_zone = time_zone
23
+ @datetime_source = datetime_source
24
+ end
25
+
26
+ # Defaults to UTC if no time zone is passed and the datetime source lacks time zone information.
27
+ # @return [String] IANA Time Zone Database identifier
28
+ # @raise [Reprise::InvalidTimeZoneError] if the time zone is ambiguous or invalid.
29
+ def to_s
30
+ return ActiveSupport::TimeZone.find_tzinfo(time_zone).identifier if time_zone
31
+ return datetime_source.time_zone.tzinfo.identifier if datetime_source.is_a?(ActiveSupport::TimeWithZone)
32
+
33
+ UTC_TIME_ZONE_IDENTIFIER
34
+ rescue TZInfo::InvalidTimezoneIdentifier
35
+ raise InvalidTimeZoneError, invalid_time_zone_identifier_error
36
+ end
37
+
38
+ private
39
+
40
+ attr_reader :time_zone, :datetime_source
41
+
42
+ def invalid_time_zone_identifier_error
43
+ <<~ERROR
44
+ "#{time_zone}" is not a valid, unambiguous IANA Time Zone Database identifier.
45
+ For more information, see:
46
+ - https://github.com/tzinfo/tzinfo/issues/53
47
+ - https://github.com/rails/rails/blob/19eebf6d33dd15a0172e3ed2481bec57a89a2404/activesupport/lib/active_support/values/time_zone.rb#L33-L185
48
+ ERROR
49
+ end
50
+ end
51
+ end
@@ -0,0 +1,5 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Reprise
4
+ VERSION = "0.1.0"
5
+ end
data/lib/reprise.rb ADDED
@@ -0,0 +1,10 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "active_support"
4
+ require "active_support/core_ext/integer/time"
5
+ require "active_support/core_ext/time"
6
+ require "reprise/reprise"
7
+ require "reprise/schedule"
8
+ require "reprise/time_of_day"
9
+ require "reprise/time_zone_identifier"
10
+ require "reprise/version"
metadata ADDED
@@ -0,0 +1,251 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: reprise
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.1.0
5
+ platform: arm64-darwin
6
+ authors:
7
+ - Jordan Hiltunen
8
+ autorequire:
9
+ bindir: exe
10
+ cert_chain: []
11
+ date: 2024-07-20 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: benchmark-ips
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - ">="
18
+ - !ruby/object:Gem::Version
19
+ version: '0'
20
+ type: :development
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - ">="
25
+ - !ruby/object:Gem::Version
26
+ version: '0'
27
+ - !ruby/object:Gem::Dependency
28
+ name: benchmark-memory
29
+ requirement: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - ">="
32
+ - !ruby/object:Gem::Version
33
+ version: '0'
34
+ type: :development
35
+ prerelease: false
36
+ version_requirements: !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - ">="
39
+ - !ruby/object:Gem::Version
40
+ version: '0'
41
+ - !ruby/object:Gem::Dependency
42
+ name: bundler
43
+ requirement: !ruby/object:Gem::Requirement
44
+ requirements:
45
+ - - ">="
46
+ - !ruby/object:Gem::Version
47
+ version: '0'
48
+ type: :development
49
+ prerelease: false
50
+ version_requirements: !ruby/object:Gem::Requirement
51
+ requirements:
52
+ - - ">="
53
+ - !ruby/object:Gem::Version
54
+ version: '0'
55
+ - !ruby/object:Gem::Dependency
56
+ name: ice_cube
57
+ requirement: !ruby/object:Gem::Requirement
58
+ requirements:
59
+ - - ">="
60
+ - !ruby/object:Gem::Version
61
+ version: '0'
62
+ type: :development
63
+ prerelease: false
64
+ version_requirements: !ruby/object:Gem::Requirement
65
+ requirements:
66
+ - - ">="
67
+ - !ruby/object:Gem::Version
68
+ version: '0'
69
+ - !ruby/object:Gem::Dependency
70
+ name: memory_profiler
71
+ requirement: !ruby/object:Gem::Requirement
72
+ requirements:
73
+ - - ">="
74
+ - !ruby/object:Gem::Version
75
+ version: '0'
76
+ type: :development
77
+ prerelease: false
78
+ version_requirements: !ruby/object:Gem::Requirement
79
+ requirements:
80
+ - - ">="
81
+ - !ruby/object:Gem::Version
82
+ version: '0'
83
+ - !ruby/object:Gem::Dependency
84
+ name: montrose
85
+ requirement: !ruby/object:Gem::Requirement
86
+ requirements:
87
+ - - ">="
88
+ - !ruby/object:Gem::Version
89
+ version: '0'
90
+ type: :development
91
+ prerelease: false
92
+ version_requirements: !ruby/object:Gem::Requirement
93
+ requirements:
94
+ - - ">="
95
+ - !ruby/object:Gem::Version
96
+ version: '0'
97
+ - !ruby/object:Gem::Dependency
98
+ name: rake
99
+ requirement: !ruby/object:Gem::Requirement
100
+ requirements:
101
+ - - "~>"
102
+ - !ruby/object:Gem::Version
103
+ version: '13.2'
104
+ type: :development
105
+ prerelease: false
106
+ version_requirements: !ruby/object:Gem::Requirement
107
+ requirements:
108
+ - - "~>"
109
+ - !ruby/object:Gem::Version
110
+ version: '13.2'
111
+ - !ruby/object:Gem::Dependency
112
+ name: rake-compiler
113
+ requirement: !ruby/object:Gem::Requirement
114
+ requirements:
115
+ - - "~>"
116
+ - !ruby/object:Gem::Version
117
+ version: 1.2.0
118
+ type: :development
119
+ prerelease: false
120
+ version_requirements: !ruby/object:Gem::Requirement
121
+ requirements:
122
+ - - "~>"
123
+ - !ruby/object:Gem::Version
124
+ version: 1.2.0
125
+ - !ruby/object:Gem::Dependency
126
+ name: redcarpet
127
+ requirement: !ruby/object:Gem::Requirement
128
+ requirements:
129
+ - - ">="
130
+ - !ruby/object:Gem::Version
131
+ version: '0'
132
+ type: :development
133
+ prerelease: false
134
+ version_requirements: !ruby/object:Gem::Requirement
135
+ requirements:
136
+ - - ">="
137
+ - !ruby/object:Gem::Version
138
+ version: '0'
139
+ - !ruby/object:Gem::Dependency
140
+ name: rspec
141
+ requirement: !ruby/object:Gem::Requirement
142
+ requirements:
143
+ - - "~>"
144
+ - !ruby/object:Gem::Version
145
+ version: '3.0'
146
+ type: :development
147
+ prerelease: false
148
+ version_requirements: !ruby/object:Gem::Requirement
149
+ requirements:
150
+ - - "~>"
151
+ - !ruby/object:Gem::Version
152
+ version: '3.0'
153
+ - !ruby/object:Gem::Dependency
154
+ name: rubocop
155
+ requirement: !ruby/object:Gem::Requirement
156
+ requirements:
157
+ - - ">="
158
+ - !ruby/object:Gem::Version
159
+ version: '0'
160
+ type: :development
161
+ prerelease: false
162
+ version_requirements: !ruby/object:Gem::Requirement
163
+ requirements:
164
+ - - ">="
165
+ - !ruby/object:Gem::Version
166
+ version: '0'
167
+ - !ruby/object:Gem::Dependency
168
+ name: yard
169
+ requirement: !ruby/object:Gem::Requirement
170
+ requirements:
171
+ - - ">="
172
+ - !ruby/object:Gem::Version
173
+ version: '0'
174
+ type: :development
175
+ prerelease: false
176
+ version_requirements: !ruby/object:Gem::Requirement
177
+ requirements:
178
+ - - ">="
179
+ - !ruby/object:Gem::Version
180
+ version: '0'
181
+ - !ruby/object:Gem::Dependency
182
+ name: activesupport
183
+ requirement: !ruby/object:Gem::Requirement
184
+ requirements:
185
+ - - ">="
186
+ - !ruby/object:Gem::Version
187
+ version: 7.0.0
188
+ type: :runtime
189
+ prerelease: false
190
+ version_requirements: !ruby/object:Gem::Requirement
191
+ requirements:
192
+ - - ">="
193
+ - !ruby/object:Gem::Version
194
+ version: 7.0.0
195
+ force_ruby_platform: false
196
+ description: Generates date & time events from recurrence rules to support various
197
+ calendar and scheduling use cases, with an emphasis on speed; the core of the Reprise
198
+ gem is implemented as a Rust extension to enable its use for performance-sensitive
199
+ workloads.
200
+ email:
201
+ - oss@jordanhiltunen.com
202
+ executables: []
203
+ extensions: []
204
+ extra_rdoc_files: []
205
+ files:
206
+ - Cargo.lock
207
+ - Cargo.toml
208
+ - LICENSE.txt
209
+ - README.md
210
+ - Rakefile
211
+ - lib/reprise.rb
212
+ - lib/reprise/3.1/reprise.bundle
213
+ - lib/reprise/3.2/reprise.bundle
214
+ - lib/reprise/3.3/reprise.bundle
215
+ - lib/reprise/core/occurrence.rb
216
+ - lib/reprise/schedule.rb
217
+ - lib/reprise/time_of_day.rb
218
+ - lib/reprise/time_zone_identifier.rb
219
+ - lib/reprise/version.rb
220
+ homepage: https://github.com/jordanhiltunen/reprise
221
+ licenses:
222
+ - MIT
223
+ metadata:
224
+ allowed_push_host: https://rubygems.org
225
+ homepage_uri: https://github.com/jordanhiltunen/reprise
226
+ source_code_uri: https://github.com/jordanhiltunen/reprise
227
+ changelog_uri: https://github.com/jordanhiltunen/reprise/blob/main/CHANGELOG.md
228
+ rubygems_mfa_required: 'true'
229
+ post_install_message:
230
+ rdoc_options: []
231
+ require_paths:
232
+ - lib
233
+ required_ruby_version: !ruby/object:Gem::Requirement
234
+ requirements:
235
+ - - ">="
236
+ - !ruby/object:Gem::Version
237
+ version: '3.1'
238
+ - - "<"
239
+ - !ruby/object:Gem::Version
240
+ version: 3.4.dev
241
+ required_rubygems_version: !ruby/object:Gem::Requirement
242
+ requirements:
243
+ - - ">="
244
+ - !ruby/object:Gem::Version
245
+ version: '0'
246
+ requirements: []
247
+ rubygems_version: 3.4.4
248
+ signing_key:
249
+ specification_version: 4
250
+ summary: Fast recurring event generation for Ruby.
251
+ test_files: []