This repository has been archived on 2023-02-02. You can view files and clone it, but cannot push or open issues or pull requests.
mfgames-nitride-cil/src/MfGames.Nitride.Temporal.Schedules/README.md
D. Moonfire 070cf2bfb8
All checks were successful
ci/woodpecker/push/woodpecker Pipeline was successful
ci/woodpecker/tag/woodpecker Pipeline was successful
fix(scheduler): added better error messages and regular expression for numerical paths
2023-01-18 23:51:37 -06:00

182 lines
6.2 KiB
Markdown

# Schedules
Scheduling posts and entries is fairly common process with blob posts but the
methods for creating those schedules can vary drastically because it requires
site- or project-specific elements such as which components or models to change
and how. This package provides one approach to scheduling that "applies"
changes (as described in JSON or YAML) to a given object based on the current
date (as provided by the `MfGames.Nitride.Temporal` package).
A schedule is not needed when the date is in the path or inside a component. Using
`MfGames.Nitride.Temporal.SetFromComponent` or `MfGames.Nitride.Temporal.SetFromPath`
would be more than sufficient. This is for dynamically changing entities based on
the date based on `Timekeeper`.
## Configuring
To use the modules, either the `NitrideTemporalSchedulesModule` can be added or
the builder extension method can be used.
```csharp
NitrideBuilder builder;
builder.UseTemporalSchedules();
```
## ISchedule
A schedule is a class that implements `ISchedule` which has the following methods:
- CanApply(Entity) ⟶ bool
- This returns true if the schedule can apply to the given entity.
- Apply(Entity) ⟶ Entity
- This makes the changes for the schedule on the entity and returns the results.
- If the entity doesn't apply, then it should return the entity without changes.
## ApplySchedules
The primary operation is `ApplySchedules` which take a sequence of schedule objects
and applies each one in turn against every entity given to the operation.
```csharp
IEnumerable<Entity> entities;
IList<ISchedule> schedules;
ApplySchedules op;
return op
.WithSchedules(schedules)
.Run(entities);
```
The following properties are available (along with their corresponding `With*` methods):
- `IList<ISchedule> Schedules`
- Contains the list of schedules to apply against entities.
- Defaults to an empty list.
- `Timekeeper Timekeeper`
- Used to determine when the site is being generated.
- Defaults to the one provided by `MfGames.Nitride.Temporal`.
### Numerical Path-Based Schedules
A common pattern for using schedules is to dole out a numerical series of posts over
a period of time. For example, a weekly posting of chapters or comic strips. In these
cases, there is usually a number in the path such as `chapter-01` or `comic-0244`. The
schedule starts at a certain point and then continues every day or week as needed.
The `NumericalPathSchedule` encapsulates this pattern. It uses the `UPath` component
of the entity and compares it against a regular expression that captures the numerical
part of the path. If it matches, then the schedule sets the date equal to the starting
point plus the "period" for every one past the first.
In this example:
```csharp
ApplySchedule op;
var entities = new List<Entity>
{
new Entity().Set((UPath) "chapter-01.md"),
new Entity().Set((UPath) "chapter-02.md"),
new Entity().Set((UPath) "chapter-03.md"),
};
var schedules = new List<ISchedule>
{
new NumericalPathSchedule
{
PathRegex = "chapter-(\d+),
ScheduleStart = DateTime.Parse("2023-01-01"),
SchedulePeriod = "1 week",
// Alternatively, SchedulePeriodTimeSpan = TimeSpan.FromDays(7),
},
}
return entities.Run(op.WithSchedules(schedules));
```
This will have chapter-01 have an `Instant` component set to 2023-01-01, the second
chapter will be set to 2023-01-08, and the third at 2023-01-15.
`NumericalPathSchedule` has the following properties:
- `Func<Entity, string> GetPath`
- An override to allow retrieving a different function.
- Defaults to `entity.Get<UPath>().ToString()`
- `Regex PathRegex`
- The regular expression that retrieves the number.
- Defaults to `^.*?(\d+)[^\d]*$` which grabs the last number found.
- `DateTime ScheduleStart`
- The date that the schedule starts.
- No default.
- `string SchedulePeriod`
- Parsed using https://github.com/pengowray/TimeSpanParser
- Supports any `TimeSpan` value, also "2 weeks" and Humanizer formatted values
- `TimeSpan SchedulePeriodTimeSpan`
- Parsed from `SchedulePeriod`
- `int CaptureGroup`
- The numerical index of the capture group.
- Defaults to `1` because the first match in Regex is 1.
- `int CaptureOffset`
- The offset to make the capture group a zero-based number.
- Defaults to `-1` which makes `chapter-01` the first entry.
There is also a virtual method for applying the schedule.
- `protected Entity ApplySchedule(Entity entity, int number, Instant instant)`
- The callback method for applying the schedule to the entity.
- This defaults to `return entity.Set(instant)`.
#### Overriding Logic
The default operation of a `NumericalPathSchedule` is to only set the `Instant`
component of the entity. The class can be extended to have more site-specific
entries including adding more properties to the schedule and applying them.
```csharp
/// <summary>A model for the YAML front matter on a page.</summary>
public class PageModel
{
/// <summary>Gets or sets the access key for the page.</summary>
public string? Access { get; set; }
/// <summary>Gets or sets the optional schedule for this page.</summary>
List<PageSchedule>? Schedules { get; set; }
}
/// <summary>A schedule specific to this project.</summary>
public class PageSchedule : NumericalPathSchedule
{
public PageSchedule()
{
// Set the default to weekly.
this.SchedulePeriod = SchedulePeriod.Week;
}
/// <summary>Gets or sets the access key for the page.</summary>
public Access { get; set; }
protected override Entity Apply(Entity entity, int number, Instant instant)
{
var model = entity.Get<PageModel>();
model.Access = this.Access;
return entity.Set(instant, model);
}
}
```
Commonly, this schedule will be put into a JSON or YAML file.
```yaml
schedules:
# Patron and Ko-Fi subscribers get it all at once
- pathRegex: chapters/chapter-\d+
scheduleDate: 2025-01-01
schedulePeriod: instant # Because we overrode the default to be weekly.
access: subscribers
# The first fifteen chapters (01-15) were released at the rate of one
# per week starting in 2024. This will replace all the schedules above
# it.
- path: chapters/chapter-(0\d|1[1-5])
scheduleDate: 2030-01-01
access: public
```