Skip to content

Instantly share code, notes, and snippets.

@snboisen
Created June 20, 2017 16:15
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save snboisen/fa933407a84a8273df98575fc07ff47d to your computer and use it in GitHub Desktop.
Save snboisen/fa933407a84a8273df98575fc07ff47d to your computer and use it in GitHub Desktop.
A template loader for Stubble based on ASP.NET Core abstractions
using System.Collections.Concurrent;
using System.IO;
using System.Text;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Hosting;
using Microsoft.Extensions.Options;
using Stubble.Core.Interfaces;
namespace TeamString.Admin.Email
{
/// <summary>
/// A template loader for Stubble that locates templates based on a base path relative to the
/// webapp content root. This loader integrates much better with ASP.NET Core apps than the
/// FileSystemLoader provided in Stubble extensions.
/// </summary>
public sealed class AspNetCoreTemplateLoader : IStubbleLoader
{
private readonly string templateBasePath;
private readonly ConcurrentDictionary<string, string> templateCache;
public AspNetCoreTemplateLoader(IOptions<StubbleOptions> options, IHostingEnvironment hostEnv)
{
this.templateBasePath = Path.Combine(hostEnv.ContentRootPath, options.Value.TemplateBasePath);
this.templateCache = new ConcurrentDictionary<string, string>();
}
private AspNetCoreTemplateLoader(string templateBasePath, ConcurrentDictionary<string, string> templateCache)
{
this.templateBasePath = templateBasePath;
this.templateCache = templateCache;
}
public IStubbleLoader Clone()
{
return new AspNetCoreTemplateLoader(templateBasePath, templateCache);
}
public string Load(string name)
{
string template = null;
if (!templateCache.TryGetValue(name, out template))
{
template = LoadTextFile(name);
templateCache.AddOrUpdate(name, template, (k, v) => template);
}
return template;
}
public ValueTask<string> LoadAsync(string name)
{
string template = null;
if (templateCache.TryGetValue(name, out template))
{
return new ValueTask<string>(template);
}
else
{
return new ValueTask<string>(LoadFromFileAsync(name));
}
}
// TODO: Move this method inside LoadAsync as a local function once we move to C# 7
// See: http://thebillwagner.com/Blog/Item/2016-03-02-C7FeatureProposalLocalFunctions
private async Task<string> LoadFromFileAsync(string name)
{
string template = await LoadTextFileAsync(name);
templateCache.AddOrUpdate(name, template, (k, v) => template);
return template;
}
private string GetFullPath(string templateName) => Path.Combine(templateBasePath, templateName);
private string LoadTextFile(string templateName) => File.ReadAllText(GetFullPath(templateName), Encoding.UTF8);
private async Task<string> LoadTextFileAsync(string templateName)
{
using (var file = new FileStream(GetFullPath(templateName), FileMode.Open, FileAccess.Read, FileShare.Read, bufferSize: 4096, useAsync: true))
using (var reader = new StreamReader(file, Encoding.UTF8, detectEncodingFromByteOrderMarks: false, bufferSize: 4096, leaveOpen: true))
{
return await reader.ReadToEndAsync();
}
}
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment