Skip to content

Commit

Permalink
Makes LinkGenerator an instance class and exposes it via an interface…
Browse files Browse the repository at this point in the history
… through IExecutionState
  • Loading branch information
daveaglick committed Oct 14, 2021
1 parent 0c192e4 commit e1ef957
Show file tree
Hide file tree
Showing 17 changed files with 299 additions and 114 deletions.
4 changes: 4 additions & 0 deletions RELEASE.md
Original file line number Diff line number Diff line change
@@ -1,7 +1,11 @@
# 1.0.0-beta.49

- **Breaking change:** The `LinkGenerator` class is no longer static and now needs to be accessed through a new `IExecutionState.LinkGenerator` or `IExecutionContext.LinkGenerator` property.
- Added the `cache` directory to the excluded list in `Statiq.App.props`.
- Fixed a bug with `DocumentFileProvider` and documents with a null `Destination`.
- Fixed a bug in the Razor engine when run under .NET 6 RC runtimes (#204, thanks @phil-scott-78).
- Updated several dependencies (#199, #201, #202, thanks @devlead).
- Added the ability to cache Razor partials using new `CachedPartial()` and `CachedPartialAsync()` HTML helpers. (#205)

# 1.0.0-beta.48

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,6 @@ public static string GetLink(this IDocument document, bool includeHost = false)
/// A string representation of the path suitable for a web link.
/// </returns>
public static string GetLink(this IDocument document, string queryAndFragment, bool includeHost = false) =>
IExecutionContext.Current.GetLink(LinkGenerator.AddQueryAndFragment(document.Destination, queryAndFragment), includeHost);
IExecutionContext.Current.GetLink(IExecutionContext.Current.LinkGenerator.AddQueryAndFragment(document.Destination, queryAndFragment), includeHost);
}
}
}
4 changes: 3 additions & 1 deletion src/core/Statiq.Common/Execution/EmptyExecutionContext.cs
Original file line number Diff line number Diff line change
Expand Up @@ -66,6 +66,8 @@ public EmptyExecutionContext(IExecutionState executionState)

public IReadOnlyPipelineCollection ExecutingPipelines => ExecutionState.ExecutingPipelines;

public ILinkGenerator LinkGenerator => ExecutionState.LinkGenerator;

public IDocument CreateDocument(NormalizedPath source, NormalizedPath destination, IEnumerable<KeyValuePair<string, object>> items, IContentProvider contentProvider = null) =>
ExecutionState.CreateDocument(source, destination, items, contentProvider);

Expand Down Expand Up @@ -104,4 +106,4 @@ public void Log<TState>(LogLevel logLevel, EventId eventId, TState state, Except

public IDisposable BeginScope<TState>(TState state) => Logger.BeginScope(state);
}
}
}
7 changes: 6 additions & 1 deletion src/core/Statiq.Common/Execution/IExecutionState.cs
Original file line number Diff line number Diff line change
Expand Up @@ -131,6 +131,11 @@ internal set
/// </summary>
IReadOnlyPipelineCollection ExecutingPipelines { get; }

/// <summary>
/// Helps generate normalized links.
/// </summary>
ILinkGenerator LinkGenerator { get; }

/// <summary>
/// Gets a <see cref="Stream"/> that can be used for document content. If <paramref name="content"/>
/// is not null, the stream is initialized with the specified content. It is preferred to use
Expand Down Expand Up @@ -194,4 +199,4 @@ IJavaScriptEnginePool GetJavaScriptEnginePool(
int maxUsagesPerEngine = 100,
TimeSpan? engineTimeout = null);
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -97,7 +97,9 @@ public static string GetLink(
{
// Return the actual URI if it's absolute
string path = metadata.GetString(key);
return path is null ? null : executionState.GetLink(LinkGenerator.AddQueryAndFragment(path, queryAndFragment), includeHost);
return path is null
? null
: executionState.GetLink(executionState.LinkGenerator.AddQueryAndFragment(path, queryAndFragment), includeHost);
}
return null;
}
Expand All @@ -122,7 +124,7 @@ public static string GetLink(
bool includeHost = false)
{
// Return the actual URI if it's absolute
if (path is object && LinkGenerator.TryGetAbsoluteHttpUri(path, out string absoluteUri))
if (path is object && executionState.LinkGenerator.TryGetAbsoluteHttpUri(path, out string absoluteUri))
{
return absoluteUri;
}
Expand Down Expand Up @@ -163,7 +165,7 @@ public static string GetLink(
bool hideExtensions)
{
// Return the actual URI if it's absolute
if (path is object && LinkGenerator.TryGetAbsoluteHttpUri(path, out string absoluteUri))
if (path is object && executionState.LinkGenerator.TryGetAbsoluteHttpUri(path, out string absoluteUri))
{
return absoluteUri;
}
Expand All @@ -178,10 +180,6 @@ public static string GetLink(
hideExtensions);
}

// This is a frequently used hot path so cache the results
private static readonly ConcurrentCache<(NormalizedPath, bool), string> _links =
new ConcurrentCache<(NormalizedPath, bool), string>(true);

/// <summary>
/// Converts the specified path into a string appropriate for use as a link using default settings from the
/// configuration. This version should be used inside modules to ensure
Expand All @@ -200,18 +198,14 @@ public static string GetLink(
this IExecutionState executionState,
in NormalizedPath path,
bool includeHost = false) =>
_links.GetOrAdd(
(path, includeHost),
(key, es) =>
es.GetLink(
key.Item1,
key.Item2 ? es.Settings.GetString(Keys.Host) : null,
es.Settings.GetPath(Keys.LinkRoot),
es.Settings.GetBool(Keys.LinksUseHttps),
es.Settings.GetBool(Keys.LinkHideIndexPages),
es.Settings.GetBool(Keys.LinkHideExtensions),
es.Settings.GetBool(Keys.LinkLowercase)),
executionState);
executionState.GetLink(
path,
includeHost ? executionState.Settings.GetString(Keys.Host) : null,
executionState.Settings.GetPath(Keys.LinkRoot),
executionState.Settings.GetBool(Keys.LinksUseHttps),
executionState.Settings.GetBool(Keys.LinkHideIndexPages),
executionState.Settings.GetBool(Keys.LinkHideExtensions),
executionState.Settings.GetBool(Keys.LinkLowercase));

/// <summary>
/// Converts the path into a string appropriate for use as a link, overriding one or more
Expand Down Expand Up @@ -270,7 +264,7 @@ public static string GetLink(
bool hideIndexPages,
bool hideExtensions,
bool lowercase) =>
LinkGenerator.GetLink(
executionState.LinkGenerator.GetLink(
path,
host,
root,
Expand Down Expand Up @@ -312,7 +306,7 @@ public static string GetLink(
bool hideExtensions,
bool lowercase,
bool makeAbsolute) =>
LinkGenerator.GetLink(
executionState.LinkGenerator.GetLink(
path,
host,
root,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@ public static string GetIndexFileName(this IReadOnlySettings settings)
/// </summary>
/// <param name="settings">The settings.</param>
/// <returns>The page file extensions.</returns>
public static IReadOnlyList<string> GetPageFileExtensions(this IReadOnlySettings settings)
public static string[] GetPageFileExtensions(this IReadOnlySettings settings)
{
settings.ThrowIfNull(nameof(settings));
IReadOnlyList<string> pageFileExtensions = settings.GetList<string>(Keys.PageFileExtensions);
Expand All @@ -37,4 +37,4 @@ public static IReadOnlyList<string> GetPageFileExtensions(this IReadOnlySettings
: pageFileExtensions.Select(x => x.StartsWith('.') ? x : "." + x).ToArray();
}
}
}
}
68 changes: 68 additions & 0 deletions src/core/Statiq.Common/Util/ILinkGenerator.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,68 @@
using System.Collections.Generic;

namespace Statiq.Common
{
/// <summary>
/// Helps generate normalized links.
/// </summary>
public interface ILinkGenerator
{
/// <summary>
/// Generates a normalized link given a path and other conditions.
/// </summary>
/// <param name="path">The path to get a link for.</param>
/// <param name="host">The host for the link (or <c>null</c> to omit the host).</param>
/// <param name="root">The root path for the link (or <c>null</c> for no root path).</param>
/// <param name="scheme">The scheme for the link (or <c>null</c> for "http").</param>
/// <param name="hidePages">An array of file names to hide (or <c>null</c> to not hide any files).</param>
/// <param name="hideExtensions">An array of file extensions to hide (or <c>null</c> to not hide extensions or an empty array to hide all file extensions).</param>
/// <param name="lowercase">Indicates that the link should be rendered in all lowercase.</param>
/// <param name="makeAbsolute">
/// If <paramref name="path"/> is relative, setting this to <c>true</c> (the default value) will assume the path relative from the root of the site
/// and make it absolute by prepending a slash and <paramref name="root"/> to the path. Otherwise, <c>false</c> will leave relative paths as relative
/// and won't prepend a slash (but <paramref name="host"/>, <paramref name="scheme"/>, and <paramref name="root"/> will have no effect).
/// If <paramref name="path"/> is absolute, this value has no effect and <paramref name="host"/>, <paramref name="scheme"/>, and <paramref name="root"/>
/// will be applied as appropriate.
/// </param>
/// <returns>A generated link.</returns>
string GetLink(
NormalizedPath path,
string host,
in NormalizedPath root,
string scheme,
string[] hidePages,
string[] hideExtensions,
bool lowercase,
bool makeAbsolute = true);

/// <summary>
/// Checks if a string contains an absolute URI with a "http" or "https" scheme and returns it if it does.
/// </summary>
/// <param name="str">The string to check.</param>
/// <param name="absoluteUri">The resulting absolute URI.</param>
/// <returns><c>true</c> if the string contains an absolute URI, <c>false</c> otherwise.</returns>
bool TryGetAbsoluteHttpUri(string str, out string absoluteUri);

/// <summary>
/// Adds a query and/or fragment to a URL or path.
/// </summary>
/// <param name="path">The path or URL.</param>
/// <param name="queryAndFragment">
/// The query and/or fragment to add. If a value is provided for this parameter
/// and it does not start with "?" or "#" then it will be assumed a query and a "?" will be prefixed.
/// </param>
/// <returns>The path or URL with an appended query and/or fragment.</returns>
string AddQueryAndFragment(string path, string queryAndFragment);

/// <summary>
/// Adds a query and/or fragment to a path.
/// </summary>
/// <param name="path">The path.</param>
/// <param name="queryAndFragment">
/// The query and/or fragment to add. If a value is provided for this parameter
/// and it does not start with "?" or "#" then it will be assumed a query and a "?" will be prefixed.
/// </param>
/// <returns>The path with an appended query and/or fragment.</returns>
NormalizedPath AddQueryAndFragment(NormalizedPath path, string queryAndFragment);
}
}
Loading

0 comments on commit e1ef957

Please sign in to comment.