182 lines
6.2 KiB
Markdown
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
|
|
```
|