it works in fsharp

This commit is contained in:
tparnell
2019-01-30 08:36:38 -05:00
parent 60fe3d8c2a
commit ab9c9fdc90
14 changed files with 94 additions and 85 deletions

View File

@@ -1,71 +1,82 @@
module BlogFactory
namespace TerribleDev.Blog.Core.Factories
module BlogFactory =
open System.IO
open YamlDotNet.Serialization
open TerribleDev.Blog.Core.Models
open TerribleDev.Blog.Core
open Markdig
open TerribleDev.Blog.MarkdownPlugins
open Microsoft.AspNetCore.Html
open System.Linq
open System.Collections.Generic
open System.IO
open YamlDotNet.Serialization
open TerribleDev.Blog.Core.Models
open TerribleDev.Blog.Core
open Markdig
open TerribleDev.Blog.MarkdownPlugins
open Microsoft.AspNetCore.Html
open System.Linq
open System.Collections.Generic
let fixTagName (tag:string) = tag.Replace(' ', '-').WithoutSpecialCharacters().ToLower()
let mapImgUrl resolveUrl imgUrl = if imgUrl.StartsWith('/') then imgUrl else sprintf "/%s/%s" resolveUrl imgUrl
let fixTagName (tag:string) = tag.Replace(' ', '-').WithoutSpecialCharacters().ToLower()
let mapImgUrlResolver (resolveUrl:string) =
fun (imgUrl: string) -> if imgUrl.StartsWith('/') then imgUrl else sprintf "/%s/%s" resolveUrl imgUrl
let getPosts (path) = Directory.EnumerateFiles(path, "*.md", SearchOption.TopDirectoryOnly)
let parseYml (postText:string):PostSettings =
postText.Split("---").[0]
|> DeserializerBuilder().Build().Deserialize
let getPosts (path) = Directory.EnumerateFiles(path, "*.md", SearchOption.TopDirectoryOnly)
let parseYml (postText:string):PostSettings =
postText.Split("---").[0]
|> DeserializerBuilder().Build().Deserialize
let getFileInfo (filePath:string) =
let fileInfo = FileInfo(filePath)
async {
let! text = File.ReadAllTextAsync(fileInfo.FullName) |> Async.AwaitTask
return (text, fileInfo)
}
let getMarkdownBuilder (imgRef) =
MarkdownPipelineBuilder()
.Use<TargetLinkExtension>()
.Use<ImageRecorder>(new ImageRecorder(imgRef))
.UseMediaLinks()
.UseEmojiAndSmiley()
.Build()
let parsePost (postText:string, fileName:FileInfo, postSettings:PostSettings): Post =
let mutable images = System.Collections.Generic.List<string>()
let markdownBuilder = getMarkdownBuilder(images)
//todo this function is a bit gross
let markdownText = postText.Split("---") |> Seq.skip 1 |> String.concat ""
let postContent = Markdown.ToHtml(markdownText, markdownBuilder);
let postContentPlain = Markdown.ToPlainText(markdownText, markdownBuilder).Split("<!-- more -->") |> String.concat ""
//todo pattern match
let resolvedUrl = if System.String.IsNullOrWhiteSpace(postSettings.permalink) then fileName.Name.Split('.').[0].Replace(' ', '-').WithoutSpecialCharacters() else postSettings.permalink
let summary = postContent.Split("<!-- more -->").[0];
let postSummaryPlain = postContentPlain.Split("<!-- more -->").[0];
let tags = postSettings.tags |> Seq.map fixTagName |> Seq.toList
let summaryPlainShort = match postContentPlain with
| postContentPlain when postContentPlain.Length <= 147 -> postContentPlain
| postContentPlain -> postContentPlain.Substring(0, 146) + "..."
let mapImgUrlFromResolved = mapImgUrl resolvedUrl
let images = images |> Seq.distinct |> Seq.map mapImgUrlFromResolved |> Seq.toList
{
PublishDate = postSettings.date.ToUniversalTime();
tags = tags;
Title = postSettings.title;
Url = resolvedUrl;
Content = HtmlString(postContent);
Summary = HtmlString(summary);
SummaryPlain = postSummaryPlain;
SummaryPlainShort = summaryPlainShort;
ContentPlain = postContentPlain;
Images = images
}
let getAllPosts path =
getPosts path
|> Seq.map getFileInfo
|> Seq.map(Async.map(fun (text, fileInfo) -> (text, fileInfo, parseYml(text))))
|> Seq.map(Async.map(parsePost))
let getFileInfo (filePath:string) =
let fileInfo = FileInfo(filePath)
async {
let! text = File.ReadAllTextAsync(fileInfo.FullName) |> Async.AwaitTask
return (text, fileInfo)
}
let getMarkdownBuilder (imgRef) =
MarkdownPipelineBuilder()
.Use<TargetLinkExtension>()
.Use<ImageRecorder>(new ImageRecorder(imgRef))
.UseMediaLinks()
.UseEmojiAndSmiley()
.Build()
let parsePost (postText:string, fileName:FileInfo, postSettings:PostSettings): Post =
let mutable images = System.Collections.Generic.List<string>()
let markdownBuilder = getMarkdownBuilder(images)
//todo this function is a bit gross
let markdownText = postText.Split("---") |> Seq.skip 1 |> String.concat ""
let postContent = Markdown.ToHtml(markdownText, markdownBuilder);
let postContentPlain = Markdown.ToPlainText(markdownText, markdownBuilder).Split("<!-- more -->") |> String.concat ""
//todo pattern match
let resolvedUrl = if System.String.IsNullOrWhiteSpace(postSettings.permalink) then fileName.Name.Split('.').[0].Replace(' ', '-').WithoutSpecialCharacters() else postSettings.permalink
let summary = postContent.Split("<!-- more -->").[0];
let postSummaryPlain = postContentPlain.Split("<!-- more -->").[0];
let tags = match postSettings.tags with
| null -> Seq.empty
| x -> x |> Seq.map fixTagName
let summaryPlainShort = match postContentPlain with
| postContentPlain when postContentPlain.Length <= 147 -> postContentPlain
| postContentPlain -> postContentPlain.Substring(0, 146) + "..."
let mapImgUrlFromResolved = mapImgUrlResolver resolvedUrl
let images = images |> Seq.distinct |> Seq.map mapImgUrlFromResolved |> Seq.toList
{
PublishDate = postSettings.date.ToUniversalTime();
tags = tags |> System.Collections.Generic.List
Title = postSettings.title;
Url = resolvedUrl;
Content = HtmlString(postContent);
Summary = HtmlString(summary);
SummaryPlain = postSummaryPlain;
SummaryPlainShort = summaryPlainShort;
ContentPlain = postContentPlain;
Images = images |> System.Collections.Generic.List
}
let getAllPosts path =
getPosts path
|> Seq.map getFileInfo
|> Seq.map(Async.map(fun (text, fileInfo) -> (text, fileInfo, parseYml(text))))
|> Seq.map(Async.map(parsePost))
|> Async.Parallel
// todo stop this
|> Async.RunSynchronously
|> System.Collections.Generic.List

View File

@@ -12,6 +12,6 @@ type Post =
ContentPlain: string;
SummaryPlain: string;
SummaryPlainShort: string;
tags : List<string>
Images: List<string>
tags : System.Collections.Generic.List<string>
Images: System.Collections.Generic.List<string>
}

View File

@@ -2,7 +2,7 @@
open System;
open System.Collections.Generic
[<CLIMutable>]
type PostSettings =
{
tags: List<string>;

View File

@@ -3,7 +3,6 @@
<TargetFramework>netcoreapp2.2</TargetFramework>
</PropertyGroup>
<ItemGroup>
<Compile Include="Util\Seq.fs" />
<Compile Include="Util\Async.fs" />
<Compile Include="Models\PostSettings.fs" />
<Compile Include="Models\Post.fs" />

View File

@@ -1 +0,0 @@

View File

@@ -12,10 +12,10 @@ namespace TerribleDev.Blog.Web.Controllers
{
public class HomeController : Controller
{
public static List<IPost> postsAsList = new BlogFactory().GetAllPosts().OrderByDescending(a=>a.PublishDate).ToList();
public static Dictionary<string, List<IPost>> tagToPost = postsAsList.Where(a=>a.tags != null)
public static List<TerribleDev.Blog.Core.Models.Post> postsAsList = TerribleDev.Blog.Core.Factories.BlogFactory.getAllPosts("Posts").OrderByDescending(a => a.PublishDate).ToList();
public static Dictionary<string, List<TerribleDev.Blog.Core.Models.Post>> tagToPost = postsAsList.Where(a=>a.tags != null)
.Aggregate(
new Dictionary<string, List<IPost>>(),
new Dictionary<string, List<TerribleDev.Blog.Core.Models.Post>>(),
(accum, item) => {
foreach(var tag in item.tags)
{
@@ -25,19 +25,19 @@ namespace TerribleDev.Blog.Web.Controllers
}
else
{
accum[tag] = new List<IPost>() { item };
accum[tag] = new List<TerribleDev.Blog.Core.Models.Post>() { item };
}
}
return accum;
});
public static IDictionary<string, IPost> posts = postsAsList.ToDictionary(a => a.Url);
public static IDictionary<int, List<IPost>> postsByPage = postsAsList.Aggregate(new Dictionary<int, List<IPost>>() { [1] = new List<IPost>() }, (accum, item) =>
public static IDictionary<string, TerribleDev.Blog.Core.Models.Post> posts = postsAsList.ToDictionary(a => a.Url);
public static IDictionary<int, List<TerribleDev.Blog.Core.Models.Post>> postsByPage = postsAsList.Aggregate(new Dictionary<int, List<TerribleDev.Blog.Core.Models.Post>>() { [1] = new List<TerribleDev.Blog.Core.Models.Post>() }, (accum, item) =>
{
var highestPage = accum.Keys.Max();
var current = accum[highestPage].Count;
if (current >= 10)
{
accum[highestPage + 1] = new List<IPost>() { item };
accum[highestPage + 1] = new List<TerribleDev.Blog.Core.Models.Post>() { item };
return accum;
}
accum[highestPage].Add(item);

View File

@@ -16,7 +16,7 @@ namespace TerribleDev.Blog.Web.Controllers
public class SeoController : Controller
{
public static DateTimeOffset publishDate = DateTimeOffset.UtcNow; // keep publish date in memory so we just return when the server was kicked
public static IEnumerable<SyndicationItem> postsToSyndication = HomeController.postsAsList.Select(a => a.ToSyndicationItem()).ToList();
public static IEnumerable<SyndicationItem> postsToSyndication = HomeController.postsAsList.Select(Core.Models.Util.ToSyndicationItem).ToList();
[Route("/rss")]
[Route("/rss.xml")]
[ResponseCache(Duration = 7200)]

View File

@@ -45,7 +45,7 @@ namespace TerribleDev.Blog.Web
List<string> postImages = new List<string>();
var pipeline = new MarkdownPipelineBuilder()
.Use<TargetLinkExtension>()
.Use<ImageRecorder>(new ImageRecorder(ref postImages))
.Use<ImageRecorder>(new ImageRecorder(postImages))
.UseMediaLinks()
.UseEmojiAndSmiley()
.Build();

View File

@@ -7,7 +7,7 @@ namespace TerribleDev.Blog.Web.Models
{
public class GetTagViewModel
{
public IEnumerable<IPost> Posts { get; set; }
public IEnumerable<TerribleDev.Blog.Core.Models.Post> Posts { get; set; }
public string Tag { get; set; }
}
}

View File

@@ -4,7 +4,7 @@ namespace TerribleDev.Blog.Web.Models
{
public class HomeViewModel
{
public IEnumerable<IPost> Posts { get; set;}
public IEnumerable<TerribleDev.Blog.Core.Models.Post> Posts { get; set;}
public int Page { get; set; }
public string NextUrl { get; set; }
public string PreviousUrl { get; set; }

View File

@@ -1,4 +1,4 @@
@model IPost
@model TerribleDev.Blog.Core.Models.Post
@{
ViewData["Title"] = "Post";
ViewData["HideNav"] = true;
@@ -23,10 +23,10 @@
<meta property="og:image" content="https://www.gravatar.com/avatar/333e3cea32cd17ff2007d131df336061?s=640" />
@foreach(var image in Model.Images.Take(6))
{
<meta property="og:image" content="https://blog.terribledev.io@(image)">
<meta property="og:image" content="https://blog.terribledev.io@(image)">
}
@if(Model.Images.Count > 0)
{
<meta name="twitter:image" content="https://blog.terribledev.io@(Model.Images[0])">
<meta name="twitter:image" content="https://blog.terribledev.io@(Model.Images[0])">
}
}

View File

@@ -1,4 +1,4 @@
@model IPost
@model TerribleDev.Blog.Core.Models.Post
<article itemprop="blogPost">
<h1 itemprop="headline" class="headline">@Model.Title</h1>

View File

@@ -1,4 +1,4 @@
@model IPost
@model TerribleDev.Blog.Core.Models.Post
<article class="btmRule">
<h3 itemprop="headline" class="headline"><a href="/@Model.Url/" class="link-unstyled">@Model.Title</a></h3>