reprise 0.1.0-aarch64-linux

Sign up to get free protection for your applications and to get access to all the features.
data/Cargo.toml ADDED
@@ -0,0 +1,8 @@
1
+ [workspace]
2
+ resolver = "2"
3
+ members = ["ext/reprise"]
4
+
5
+ [profile.release]
6
+ codegen-units = 1 # more llvm optimizations
7
+ debug = 2 # make perfomance engineers happy
8
+ lto = "thin" # cross-crate inlining
data/LICENSE.txt ADDED
@@ -0,0 +1,21 @@
1
+ The MIT License (MIT)
2
+
3
+ Copyright (c) 2024 Jordan Hiltunen
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in
13
+ all copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
21
+ THE SOFTWARE.
data/README.md ADDED
@@ -0,0 +1,362 @@
1
+ # Reprise
2
+
3
+ [![Tests](https://github.com/jordanhiltunen/reprise/actions/workflows/test.yml/badge.svg)](https://github.com/jordanhiltunen/reprise/actions/workflows/test.yml) | [![Test CRuby Gem Build](https://github.com/jordanhiltunen/reprise/actions/workflows/cruby-build-and-install.yml/badge.svg)](https://github.com/jordanhiltunen/reprise/actions/workflows/cruby-build-and-install.yml)
4
+
5
+ Reprise is an experimental performance-first Ruby gem that provides support for defining event recurrence
6
+ rules and generating & querying their future occurrences. Depending on your use case,
7
+ you may benefit from a speedup of up to 1000x relative to other recurrence rule gems: because
8
+ Reprise is a thin Ruby wrapper around an extension written in Rust, we are able to offer a level
9
+ of speed and conservative memory use that we would otherwise be unable to accomplish in
10
+ pure Ruby alone.
11
+
12
+ For more on why and when you might want to use this gem, see [Why Reprise?](#why-reprise).
13
+
14
+ ## Installation
15
+
16
+ Add this line to your application's Gemfile:
17
+
18
+ ```ruby
19
+ gem "reprise"
20
+ ```
21
+
22
+ ## Usage
23
+
24
+ ### Initialize a new schedule
25
+
26
+ All schedules need to be initialized with a `start_time` and `end_time`:
27
+
28
+ ```ruby
29
+ may_26_2015_four_thirty_pm_in_rome = Time.parse("2015-05-26 10:30:45").in_time_zone("Rome")
30
+ # => Tue, 26 May 2015 16:30:45.000000000 CEST +02:00
31
+
32
+ schedule = Reprise::Schedule.new(
33
+ starts_at: may_26_2015_four_thirty_pm_in_rome,
34
+ ends_at: may_26_2015_four_thirty_pm_in_rome + 1.year
35
+ )
36
+ ````
37
+
38
+ If your `starts_at` is an `ActiveSupport::TimeWithZone`, your schedule will infer its time zone from
39
+ that value. You can also explicitly pass in a `time_zone` if your schedule bookends don't have time zone
40
+ information (e.g. if you are passing in simple `Time` instances):
41
+
42
+ ```ruby
43
+ schedule = Reprise::Schedule.new(
44
+ starts_at: may_26_2015_ten_thirty_pm_utc,
45
+ ends_at: may_26_2015_ten_thirty_pm_utc + 1.year,
46
+ time_zone: "Rome"
47
+ )
48
+ ```
49
+
50
+ ### Add recurring event series
51
+
52
+ You can add any number of recurring series to the schedule via `repeat_*` methods:
53
+
54
+ ```ruby
55
+ schedule.repeat_weekly(:sunday, duration_in_seconds: 15.minutes)
56
+ schedule.occurrences.size
57
+ # => 52
58
+ ```
59
+
60
+ #### Customizing the time of day for a recurring series' occurrences
61
+
62
+ By default, all series that advance in units of a day or greater will use the time that your schedule
63
+ started at as the local time for each future occurrence:
64
+
65
+ ```ruby
66
+ first_occurrence = schedule.occurrences.first
67
+ # => <Reprise::Core::Occurrence start_time="2015-05-31T14:30:45+00:00" end_time="2015-05-31T14:45:45+00:00" label="nil">
68
+ first_occurrence.start_time.in_time_zone("Rome")
69
+ # => Sun, 31 May 2015 16:30:45.000000000 CEST +02:00 # <- 4:30 PM
70
+ ```
71
+
72
+ You can override this behaviour, and set a custom time of day for each of your series,
73
+ either by passing an hour/minute/second hash to `time_of_day`:
74
+
75
+ ```ruby
76
+ schedule.repeat_weekly(:sunday, time_of_day: { hour: 9, minute: 30 }, duration_in_seconds: 60)
77
+ first_occurrence = schedule.occurrences.first
78
+ # => => <Reprise::Core::Occurrence start_time="2015-05-31T07:30:00+00:00" end_time="2015-05-31T07:31:00+00:00" label="nil">
79
+ first_occurrence.start_time.in_time_zone("Rome")
80
+ # => Sun, 31 May 2015 09:30:00.000000000 CEST +02:00
81
+ ```
82
+
83
+ Or, by passing a `Time` object instead:
84
+
85
+ ```ruby
86
+ ten_forty_five_pm_in_rome = Time.parse("2015-05-27 04:45:00").in_time_zone("Rome")
87
+ schedule.repeat_weekly(:tuesday, time_of_day: ten_forty_five_pm_in_rome, duration_in_seconds: 60)
88
+ # => <Reprise::Core::Occurrence start_time="2015-06-02T08:45:00+00:00" end_time="2015-06-02T08:46:00+00:00" label="nil">
89
+ first_occurrence.start_time.in_time_zone("Rome")
90
+ # => Tue, 02 Jun 2015 10:45:00.000000000 CEST +02:00
91
+ ```
92
+
93
+ #### Customizing the bookends of a recurring series
94
+
95
+ By default, all series will inherit the `starts_at` and `ends_at` values of their parent schedule:
96
+
97
+ ```ruby
98
+ schedule = Reprise::Schedule.new(
99
+ starts_at: may_26_2015_four_thirty_pm_in_rome,
100
+ ends_at: may_26_2015_four_thirty_pm_in_rome + 200.days
101
+ )
102
+
103
+ schedule.repeat_weekly(:wednesday, time_of_day: { hour: 9, minute: 30 }, duration_in_seconds: 10.minutes)
104
+ occurrences = schedule.occurrences
105
+ puts occurrences.size
106
+ # => 29
107
+ puts (occurrences.last.start_time.to_date - occurrences.first.start_time.to_date).to_i
108
+ # => 196 # days
109
+ ```
110
+
111
+ You can also specify the bookends of each recurring series:
112
+
113
+ ```ruby
114
+ schedule.repeat_weekly(
115
+ :friday,
116
+ time_of_day: { hour: 4, minute: 01 },
117
+ duration_in_seconds: 40.minutes,
118
+ starts_at: may_26_2015_four_thirty_pm_in_rome + 40.days,
119
+ ends_at: may_26_2015_four_thirty_pm_in_rome + 100.days
120
+ )
121
+ occurrences = schedule.occurrences
122
+ puts occurrences.size
123
+ # => 8
124
+ puts occurrences.first.start_time.in_time_zone("Rome")
125
+ # 2015-07-10 04:01:00 +0200
126
+ puts occurrences.last.start_time.in_time_zone("Rome")
127
+ # 2015-08-28 04:01:00 +0200
128
+ puts (occurrences.last.start_time.to_date - occurrences.first.start_time.to_date).to_i
129
+ # => 49 # days
130
+ ```
131
+
132
+ There are many recurring series that you can create; `#repeat_minutely`, `#repeat_hourly`,
133
+ `#repeat_daily`, `#repeat_weekly`, `#repeat_monthly_by_day`, and `#repeat_monthly_by_nth_weekday`.
134
+
135
+ For more information on each method, see the docs.
136
+
137
+ #### Adding labels to the occurrences of each series
138
+
139
+ If you need to disambiguate occurrences from different series in the same schedule,
140
+ you can add an optional label:
141
+
142
+ ```ruby
143
+ schedule.repeat_daily(label: "Coffee Time", time_of_day: { hour: 8 }, duration_in_seconds: 15.minutes)
144
+ schedule.repeat_daily(label: "Tea Time", interval: 3, time_of_day: { hour: 9 }, duration_in_seconds: 10.minutes)
145
+ schedule.occurrences.take(7).map { |o| puts o.inspect }
146
+ # => <Reprise::Core::Occurrence label="Coffee Time" start_time="2015-05-27T06:00:00+00:00" end_time="2015-05-27T06:15:00+00:00">
147
+ # => <Reprise::Core::Occurrence label="Tea Time" start_time="2015-05-27T07:00:00+00:00" end_time="2015-05-27T07:10:00+00:00">
148
+ # => <Reprise::Core::Occurrence label="Coffee Time" start_time="2015-05-28T06:00:00+00:00" end_time="2015-05-28T06:15:00+00:00">
149
+ # => <Reprise::Core::Occurrence label="Coffee Time" start_time="2015-05-29T06:00:00+00:00" end_time="2015-05-29T06:15:00+00:00">
150
+ # => <Reprise::Core::Occurrence label="Coffee Time" start_time="2015-05-30T06:00:00+00:00" end_time="2015-05-30T06:15:00+00:00">
151
+ # => <Reprise::Core::Occurrence label="Tea Time" start_time="2015-05-30T07:00:00+00:00" end_time="2015-05-30T07:10:00+00:00">
152
+ # => <Reprise::Core::Occurrence label="Coffee Time" start_time="2015-05-31T06:00:00+00:00" end_time="2015-05-31T06:15:00+00:00">
153
+ ```
154
+
155
+ #### Excluding time intervals from the schedule's occurrences
156
+
157
+ If you have other non-recurring "schedule entries" in your domain that can collide with your recurring series'
158
+ occurrences and need to be excluded, you can add exclusions to your schedule before generating occurrences:
159
+
160
+ ```ruby
161
+ schedule.repeat_daily(label: "Standing Meeting", ends_at: may_26_2015_four_thirty_pm_in_rome + 5.days, duration_in_seconds: 15.minutes)
162
+ schedule.occurrences.map { |o| puts o.inspect }
163
+ # => <Reprise::Core::Occurrence start_time="2015-05-26T14:30:45+00:00" end_time="2015-05-26T14:45:45+00:00" label="Standing Meeting">
164
+ # => <Reprise::Core::Occurrence start_time="2015-05-27T14:30:45+00:00" end_time="2015-05-27T14:45:45+00:00" label="Standing Meeting">
165
+ # => <Reprise::Core::Occurrence start_time="2015-05-28T14:30:45+00:00" end_time="2015-05-28T14:45:45+00:00" label="Standing Meeting">
166
+ # => <Reprise::Core::Occurrence start_time="2015-05-29T14:30:45+00:00" end_time="2015-05-29T14:45:45+00:00" label="Standing Meeting">
167
+ # => <Reprise::Core::Occurrence start_time="2015-05-30T14:30:45+00:00" end_time="2015-05-30T14:45:45+00:00" label="Standing Meeting">
168
+
169
+ # You don't need to specify entire days; you can pass time intervals as narrow or wide as you like.
170
+ schedule.add_exclusion(
171
+ starts_at: (may_26_2015_four_thirty_pm_in_rome + 2.days).beginning_of_day,
172
+ ends_at: (may_26_2015_four_thirty_pm_in_rome + 2.days).end_of_day
173
+ )
174
+
175
+ schedule.occurrences.map { |o| puts o.inspect }
176
+ # N.B. The occurrence on 2015-05-28 is now excluded.
177
+ # => <Reprise::Core::Occurrence start_time="2015-05-26T14:30:45+00:00" end_time="2015-05-26T14:45:45+00:00" label="Standing Meeting">
178
+ # => <Reprise::Core::Occurrence start_time="2015-05-27T14:30:45+00:00" end_time="2015-05-27T14:45:45+00:00" label="Standing Meeting">
179
+ # => <Reprise::Core::Occurrence start_time="2015-05-29T14:30:45+00:00" end_time="2015-05-29T14:45:45+00:00" label="Standing Meeting">
180
+ # => <Reprise::Core::Occurrence start_time="2015-05-30T14:30:45+00:00" end_time="2015-05-30T14:45:45+00:00" label="Standing Meeting">
181
+ ```
182
+
183
+ #### Querying for occurrences within a given time interval
184
+
185
+ After constructing your schedule, you can query for the occurrences within any interval
186
+ of time:
187
+
188
+ ```ruby
189
+ schedule.repeat_daily(label: "Standing Meeting", ends_at: may_26_2015_four_thirty_pm_in_rome + 5.days, duration_in_seconds: 15.minutes)
190
+
191
+ schedule.occurs_between?(
192
+ may_26_2015_four_thirty_pm_in_rome + 2.days,
193
+ may_26_2015_four_thirty_pm_in_rome + 3.days,
194
+ )
195
+ # => true
196
+
197
+ schedule.occurrences_between(
198
+ may_26_2015_four_thirty_pm_in_rome + 2.days,
199
+ may_26_2015_four_thirty_pm_in_rome + 3.days,
200
+ ).map { |o| puts o.inspect }
201
+ # => <Reprise::Core::Occurrence start_time="2015-05-28T14:30:45+00:00" end_time="2015-05-28T14:45:45+00:00" label="Standing Meeting">
202
+ ```
203
+
204
+ Both `#occurs_between?` and `#occurrences_between` also support an optional `include_overlapping`
205
+ argument, which allows you to search for occurrences that not only occur entirely within a given
206
+ interval, but also those that partially overlap.
207
+
208
+ ## Why Reprise?
209
+
210
+ ### First, consider the alternatives
211
+
212
+ Reprise is particularly indebted to [ice_cube](https://github.com/ice-cube-ruby/ice_cube) and [Montrose](https://github.com/rossta/montrose), projects that have served the Ruby community for years.
213
+ They are stable and battle-tested. If you have no actual business need for the kind of performance that Reprise aims for,
214
+ you would probably be much better served by choosing one of those two gems instead.
215
+
216
+ ### Tradeoffs
217
+
218
+ - **Flexibility.** Because Reprise calls into a strictly-typed extension, its current public interface is very much "one-size-fits-all";
219
+ the influence of Rust leaks into its Ruby API. Alternative gems offer much more flexible APIs that support a variety
220
+ of more idiomatic calling conventions: they have better, more forgiving ergonomics. Reprise may invest more efforts
221
+ here in the future, but not until we have landed on a feature-complete, performant core - our primary design goal.
222
+ Until then, out API will remain sparse but sufficient.
223
+ - **Stability.** Reprise is still experimental; we do not yet have a `1.0.0` release or a public roadmap. Breaking changes
224
+ may be frequent across releases. If you do not want to pin Reprise to a specific version and want a library that you can
225
+ upgrade without reviewing the changelog, you may want to consider an alternative for now.
226
+ - **Serialization.** We do not yet offer any form of persistence support (e.g. parsing from / serializing to yaml / hash
227
+ / ical / others).
228
+
229
+ ### Advantages
230
+
231
+ #### Performance
232
+
233
+ A truism in the Ruby community is that "Ruby is slow, but that doesn't matter for you":
234
+ > So, often it hardly matters that [Ruby] is slow, because your use-case does not need the scale,
235
+ > speed, or throughput that Ruby chokes on. Or because the trade-offs are worth it: Often the
236
+ > quicker development, cheaper development, faster time-to-market etc is worth the extra resources
237
+ > (servers, hardware, SAAS) you must throw at your app to keep it performing acceptable.
238
+ > https://berk.es/2022/08/09/ruby-slow-database-slow/
239
+
240
+ This is often delightfully true, until on the odd occasion Ruby's speed requires that a straightforward feature
241
+ be implemented in a contorted or meaningfully-constrained way in order to work.
242
+
243
+ Reprise aims to solve a niche problem: cutting the latency of recurring schedule generation when it is in
244
+ the critical path without imposing an additional complexity burden on clients. For most applications
245
+ that deal with recurring events, this is probably not a problem. But if it is, we want to buy you more
246
+ effectively-free per-request headroom that you can spend in simple Ruby to improve or ship a feature that
247
+ you otherwise couldn't.
248
+
249
+ ##### Benchmarks
250
+
251
+ You can run benchmarks locally via `bundle exec rake benchmark`; additionally,
252
+ to view our recent benchmarking results in CI, see [past runs of our Benchmark worfklow](https://github.com/jordanhiltunen/reprise/actions/workflows/benchmark.yml).
253
+
254
+ Below is a sample local benchmark run taken on the following development machine:
255
+
256
+ | System Detail | Value |
257
+ |---------------|------------------------------------------------------------|
258
+ | OS | macOS 14.5 (23F79) |
259
+ | CPU | 2.4 GHz 8-Core Intel i9 |
260
+ | Memory | 64GB 2667 MHz DDRr |
261
+ | Ruby Version | 3.3.2 (2024-05-30 revision e5a195edf6) \[x86_64-darwin23\] |
262
+ | Rust Version | rustc 1.79.0 (129f3b996 2024-06-10) |
263
+
264
+ `benchmark-ips`: (higher is better)
265
+ ```
266
+ ruby 3.3.2 (2024-05-30 revision e5a195edf6) [x86_64-darwin23]
267
+ Warming up --------------------------------------
268
+ IceCube 1.000 i/100ms
269
+ Montrose 1.000 i/100ms
270
+ Reprise 1.197k i/100ms
271
+ Calculating -------------------------------------
272
+ IceCube 10.259 (± 9.7%) i/s - 52.000 in 5.081337s
273
+ Montrose 14.986 (± 6.7%) i/s - 75.000 in 5.022293s
274
+ Reprise 13.127k (±19.9%) i/s - 63.441k in 5.047277s
275
+ ```
276
+
277
+ `benchmark-memory`: (lower is better)
278
+ ```
279
+ Calculating -------------------------------------
280
+ IceCube 10.986M memsize ( 1.040k retained)
281
+ 202.268k objects ( 14.000 retained)
282
+ 5.000 strings ( 1.000 retained)
283
+ Montrose 9.799M memsize ( 3.792k retained)
284
+ 157.675k objects ( 13.000 retained)
285
+ 34.000 strings ( 7.000 retained)
286
+ Reprise 14.872k memsize ( 0.000 retained)
287
+ 310.000 objects ( 0.000 retained)
288
+ 0.000 strings ( 0.000 retained)
289
+
290
+ Comparison:
291
+ Reprise: 14872 allocated
292
+ Montrose: 9799288 allocated - 658.91x more
293
+ IceCube: 10986192 allocated - 738.72x more
294
+ ```
295
+
296
+ #### Exclusion Handling
297
+
298
+ Beyond performance, one area where Reprise shines is in schedule exclusion handling:
299
+
300
+ Suppose you have a recurring series that occurs every Monday from 12:30 PM - 1:00 PM. You need to generate
301
+ future occurrences of this series, excluding those that do not conflict with pre-existing, non-recurring
302
+ schedule entries; e.g. on one particular Monday, you have schedule entries at 9:00 AM - 9:30 AM, 12:15 PM - 1:00 PM,
303
+ and 3:30 - 4:30 PM.
304
+
305
+ How do you filter out recurring series occurrences that conflict with other schedule entries that exist
306
+ in your application?
307
+
308
+ At time of writing, alternative gems' solutions to this problem are all unfortunately lacking:
309
+ - **None**: It is entirely the responsibility of the client application to handle occurrence exclusions,
310
+ despite this logic being core to the domain of recurring schedule management.
311
+ - **Date-based exclusion**: Client applications can pass specific dates when occurrences should be excluded.
312
+ This is not sufficient except for in the most simple of circumstances. Again, consider our hypothetical
313
+ Monday @ 12:30 PM recurring series: being able to exclude a specific _date_ from your recurrence rule still
314
+ requires you to implement your own overlap detection logic to determine whether an occurrence actually conflicts with
315
+ the start and end times of a schedule entry on a given date.
316
+
317
+ These limitations can push a significant amount of schedule recurrence logic onto client applications;
318
+ Reprise improves on this significantly by offering an API to define exclusions with start and end times; Reprise
319
+ then determines whether any given occurrence overlaps with an exclusion that you have defined, and filters
320
+ them out during occurrence generation accordingly.
321
+
322
+ ## Acknowledgements
323
+
324
+ Reprise, a Ruby gem with a Rust core, is only possible because of the foundation laid by the excellent [Magnus](https://github.com/matsadler/magnus) project.
325
+
326
+ ## Development
327
+
328
+ To get started after checking out the repo:
329
+
330
+ ```bash
331
+ $ bin/setup # install dependencies
332
+ $ rake compile:reprise # recompile the extension after making changes to Rust files
333
+ $ rake spec # run the test suite
334
+ $ rake benchmark # run the benchmarks
335
+ ```
336
+
337
+ ### Generating Documentation
338
+
339
+ Reprise' public Ruby API is documented using [YARD](https://yardoc.org/guides/).
340
+ To regenerate the documentation after changing any of the annotations, run `rake yard`
341
+ and commit the changes.
342
+
343
+ ## Contributing
344
+
345
+ This project is intended to be a safe, welcoming space for collaboration, and contributors are expected to adhere to the [Contributor Covenant](http://contributor-covenant.org) code of conduct.
346
+
347
+ - :white_check_mark: Report or fix bugs
348
+ - :white_check_mark: Suggest features
349
+ - :white_check_mark: Write or improve documentation
350
+ - :yellow_circle: Submit pull requests (please reach out first)
351
+
352
+ We plan on welcoming pull requests once we settle on an initial `1.0.0`; until then, we anticipate
353
+ a lot of early experimentation, and we will have more time to collaborate and welcome pull requests
354
+ once we've hit that milestone.
355
+
356
+ ## License
357
+
358
+ The gem is available as open source under the terms of the [MIT License](https://opensource.org/licenses/MIT).
359
+
360
+ ## Code of Conduct
361
+
362
+ Everyone interacting in the Reprise project’s codebases, issue trackers, chat rooms and mailing lists is expected to follow the [code of conduct](https://github.com/jordanhiltunen/reprise/blob/master/CODE_OF_CONDUCT.md).
data/Rakefile ADDED
@@ -0,0 +1,48 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "bundler/gem_tasks"
4
+ require "rake/extensiontask"
5
+ require "rake/testtask"
6
+ require "rspec/core/rake_task"
7
+ require "yard"
8
+
9
+ Rake.add_rakelib("lib/tasks")
10
+
11
+ RSpec::Core::RakeTask.new(:spec)
12
+ task default: :spec
13
+
14
+ task :dev do
15
+ ENV["RB_SYS_CARGO_PROFILE"] = "dev"
16
+ end
17
+
18
+ CROSS_PLATFORMS = %w[
19
+ x86_64-linux
20
+ x86_64-linux-musl
21
+ aarch64-linux
22
+ x86_64-darwin
23
+ arm64-darwin
24
+ x64-mingw-ucrt
25
+ x64-mingw32
26
+ ]
27
+
28
+ gemspec = Bundler.load_gemspec("reprise.gemspec")
29
+ Rake::ExtensionTask.new("reprise", gemspec) do |ext|
30
+ ext.lib_dir = "lib/reprise"
31
+ ext.cross_compile = true
32
+ ext.cross_platform = CROSS_PLATFORMS
33
+ ext.cross_compiling do |spec|
34
+ spec.dependencies.reject! { |dep| dep.name == "rb_sys" }
35
+ spec.files.reject! { |file| File.fnmatch?("ext/*", file, File::FNM_EXTGLOB) }
36
+ end
37
+ end
38
+
39
+ YARD::Rake::YardocTask.new do |t|
40
+ t.options = %w[--output-dir ./docs]
41
+ end
42
+
43
+ task :remove_ext do
44
+ path = "lib/reprise/reprise.bundle"
45
+ File.unlink(path) if File.exist?(path)
46
+ end
47
+
48
+ Rake::Task["build"].enhance([:remove_ext])
Binary file
Binary file
Binary file
@@ -0,0 +1,21 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Reprise
4
+ module Core
5
+ # An Occurrence represents a single instance of a recurring series belong to a schedule.
6
+ #
7
+ # @private This class definition is open-classed only for the purposes
8
+ # of adding documentation; it is defined dynamically within
9
+ # the Rust extension.
10
+ class Occurrence
11
+ # @!attribute [r] start_time
12
+ # @return [Time] The start time of the occurrence, given in the current system time zone.
13
+ # @!attribute [r] end_time
14
+ # @return [Time] The end time of the occurrence, given in the current system time zone.
15
+ # @!attribute [r] label
16
+ # @return [String, nil] The label given to the recurring series from which the
17
+ # occurrence was generated (if present). Can be used to disambiguate occurrences
18
+ # from different series after generating the schedule's occurrences.
19
+ end
20
+ end
21
+ end