Child Objects

A user can create posts within a site. That user might be the creator of the blog, or it might be a different user. A Post fact refers to the Site parent, the author User, and also includes a timestamp to differentiate it from other posts.

[FactType("Blog.Post")]
public record Post(Site site, User author, DateTime createdAt) { }

Renderer.RenderTypes(typeof(Post))
%0 Blog.Post Blog.Post Blog.Site Blog.Site Blog.Post->Blog.Site site Jinaga.User Jinaga.User Blog.Post->Jinaga.User author Blog.Site->Jinaga.User creator
var post0 = await jinagaClient.Fact(new Post(site, user, DateTime.UtcNow));
var post1 = await jinagaClient.Fact(new Post(site, user, DateTime.UtcNow));

jinagaClient.RenderFacts(post0, post1)
%0 eLG0D2gojV+51Ix80JNo2Uh4EQTKvjbZUq5JNrf9K5gSXEqRmi7LBSleOl09F/bWdrlWUgDkalEH847v83U7fA== Jinaga.User publicKey --- FAKE USER --- fRStq94kRaFpm+n+5BCGNRP7e7NOyihLThCOo0vRo+inA0LbPUbVkJsNJwcWCKIVb9lUylMwc7Bt2vfHTbicPg== Blog.Site createdAt 2024-05-18T20:32:02.... fRStq94kRaFpm+n+5BCGNRP7e7NOyihLThCOo0vRo+inA0LbPUbVkJsNJwcWCKIVb9lUylMwc7Bt2vfHTbicPg==->eLG0D2gojV+51Ix80JNo2Uh4EQTKvjbZUq5JNrf9K5gSXEqRmi7LBSleOl09F/bWdrlWUgDkalEH847v83U7fA== creator BXJeL5YB/zBDnSIp09B7ZEa26C5LaOfc5RvoDPXDWUS0lrXoxJrvn9enpf10BY5vVIa4XwS0HWm6WGXEokldNA== Blog.Post createdAt 2024-05-18T20:32:03.... BXJeL5YB/zBDnSIp09B7ZEa26C5LaOfc5RvoDPXDWUS0lrXoxJrvn9enpf10BY5vVIa4XwS0HWm6WGXEokldNA==->eLG0D2gojV+51Ix80JNo2Uh4EQTKvjbZUq5JNrf9K5gSXEqRmi7LBSleOl09F/bWdrlWUgDkalEH847v83U7fA== author BXJeL5YB/zBDnSIp09B7ZEa26C5LaOfc5RvoDPXDWUS0lrXoxJrvn9enpf10BY5vVIa4XwS0HWm6WGXEokldNA==->fRStq94kRaFpm+n+5BCGNRP7e7NOyihLThCOo0vRo+inA0LbPUbVkJsNJwcWCKIVb9lUylMwc7Bt2vfHTbicPg== site 8Cl5MsQaiRie2EbqjNUhj5Jf8TjJ8e/0O1+Knfsw3pyIHKTaaJ2okiPxoDtGEztPS3MXQiDMV1G8jCC5G0mvbw== Blog.Post createdAt 2024-05-18T20:32:03.... 8Cl5MsQaiRie2EbqjNUhj5Jf8TjJ8e/0O1+Knfsw3pyIHKTaaJ2okiPxoDtGEztPS3MXQiDMV1G8jCC5G0mvbw==->eLG0D2gojV+51Ix80JNo2Uh4EQTKvjbZUq5JNrf9K5gSXEqRmi7LBSleOl09F/bWdrlWUgDkalEH847v83U7fA== author 8Cl5MsQaiRie2EbqjNUhj5Jf8TjJ8e/0O1+Knfsw3pyIHKTaaJ2okiPxoDtGEztPS3MXQiDMV1G8jCC5G0mvbw==->fRStq94kRaFpm+n+5BCGNRP7e7NOyihLThCOo0vRo+inA0LbPUbVkJsNJwcWCKIVb9lUylMwc7Bt2vfHTbicPg== site

When using the application, you will first select a site from the list. Then you will navigate to a page where you see the posts. The specification should therefore start from the site.

var postsInSite = Given<Site>.Match((site, facts) =>
    from post in facts.OfType<Post>()
    where post.site == site
    select post);

var posts = await jinagaClient.Query(postsInSite, site);

posts.Count()
2

Continue the pattern to define deletion, restoration, and mutable properties for the child objects.

[FactType("Blog.Post.Title")]
public record PostTitle(Post post, string value, PostTitle[] prior) { }

[FactType("Blog.Post.Content")]
public record PostContent(Post post, string markdown, PostContent[] prior) { }

[FactType("Blog.Post.Deleted")]
public record PostDeleted(Post post, DateTime deletedAt) { }

[FactType("Blog.Post.Restored")]
public record PostRestored(PostDeleted deleted) { }

Renderer.RenderTypes(
    typeof(PostTitle),
    typeof(PostContent),
    typeof(PostDeleted),
    typeof(PostRestored))
%0 Blog.Post.Title Blog.Post.Title Blog.Post.Title->Blog.Post.Title prior Blog.Post Blog.Post Blog.Post.Title->Blog.Post post Blog.Site Blog.Site Blog.Post->Blog.Site site Jinaga.User Jinaga.User Blog.Post->Jinaga.User author Blog.Post.Content Blog.Post.Content Blog.Post.Content->Blog.Post post Blog.Post.Content->Blog.Post.Content prior Blog.Post.Deleted Blog.Post.Deleted Blog.Post.Deleted->Blog.Post post Blog.Post.Restored Blog.Post.Restored Blog.Post.Restored->Blog.Post.Deleted deleted Blog.Site->Jinaga.User creator
var postTitle0 = await jinagaClient.Fact(new PostTitle(
    post0, "Welcome to My Blog", []));
var postContent0 = await jinagaClient.Fact(new PostContent(
    post0, "This is my first post.", []));

var postTitle1 = await jinagaClient.Fact(new PostTitle(
    post1, "Interesting Facts", []));
var postContent1 = await jinagaClient.Fact(new PostContent(
    post1, "Let me tell you about this thing that I love.", []));

var post0Deleted = await jinagaClient.Fact(new PostDeleted(
    post0, DateTime.UtcNow));

jinagaClient.RenderFacts(
    postTitle0,
    postContent0,
    postTitle1,
    postContent1,
    post0Deleted)
%0 eLG0D2gojV+51Ix80JNo2Uh4EQTKvjbZUq5JNrf9K5gSXEqRmi7LBSleOl09F/bWdrlWUgDkalEH847v83U7fA== Jinaga.User publicKey --- FAKE USER --- fRStq94kRaFpm+n+5BCGNRP7e7NOyihLThCOo0vRo+inA0LbPUbVkJsNJwcWCKIVb9lUylMwc7Bt2vfHTbicPg== Blog.Site createdAt 2024-05-18T20:32:02.... fRStq94kRaFpm+n+5BCGNRP7e7NOyihLThCOo0vRo+inA0LbPUbVkJsNJwcWCKIVb9lUylMwc7Bt2vfHTbicPg==->eLG0D2gojV+51Ix80JNo2Uh4EQTKvjbZUq5JNrf9K5gSXEqRmi7LBSleOl09F/bWdrlWUgDkalEH847v83U7fA== creator BXJeL5YB/zBDnSIp09B7ZEa26C5LaOfc5RvoDPXDWUS0lrXoxJrvn9enpf10BY5vVIa4XwS0HWm6WGXEokldNA== Blog.Post createdAt 2024-05-18T20:32:03.... BXJeL5YB/zBDnSIp09B7ZEa26C5LaOfc5RvoDPXDWUS0lrXoxJrvn9enpf10BY5vVIa4XwS0HWm6WGXEokldNA==->eLG0D2gojV+51Ix80JNo2Uh4EQTKvjbZUq5JNrf9K5gSXEqRmi7LBSleOl09F/bWdrlWUgDkalEH847v83U7fA== author BXJeL5YB/zBDnSIp09B7ZEa26C5LaOfc5RvoDPXDWUS0lrXoxJrvn9enpf10BY5vVIa4XwS0HWm6WGXEokldNA==->fRStq94kRaFpm+n+5BCGNRP7e7NOyihLThCOo0vRo+inA0LbPUbVkJsNJwcWCKIVb9lUylMwc7Bt2vfHTbicPg== site nO8A3zVvDLx8X6LkD4mea5GbFbWt+hkoJdqelWq6rIcrhmy4yA/lBzZkZuHnc8gqVkWEBrZ4Q9XQE7ZF6QJAYQ== Blog.Post.Title value Welcome to My Blog nO8A3zVvDLx8X6LkD4mea5GbFbWt+hkoJdqelWq6rIcrhmy4yA/lBzZkZuHnc8gqVkWEBrZ4Q9XQE7ZF6QJAYQ==->BXJeL5YB/zBDnSIp09B7ZEa26C5LaOfc5RvoDPXDWUS0lrXoxJrvn9enpf10BY5vVIa4XwS0HWm6WGXEokldNA== post vZg30BbPhmhY+EHd4hoAbWex80yeOJbSOXMRPpjTJhQaO/2hFIj+vypnyEI1oTOjcJECphz5emMuJ3jiYgtmhg== Blog.Post.Content markdown This is my first pos... vZg30BbPhmhY+EHd4hoAbWex80yeOJbSOXMRPpjTJhQaO/2hFIj+vypnyEI1oTOjcJECphz5emMuJ3jiYgtmhg==->BXJeL5YB/zBDnSIp09B7ZEa26C5LaOfc5RvoDPXDWUS0lrXoxJrvn9enpf10BY5vVIa4XwS0HWm6WGXEokldNA== post 8Cl5MsQaiRie2EbqjNUhj5Jf8TjJ8e/0O1+Knfsw3pyIHKTaaJ2okiPxoDtGEztPS3MXQiDMV1G8jCC5G0mvbw== Blog.Post createdAt 2024-05-18T20:32:03.... 8Cl5MsQaiRie2EbqjNUhj5Jf8TjJ8e/0O1+Knfsw3pyIHKTaaJ2okiPxoDtGEztPS3MXQiDMV1G8jCC5G0mvbw==->eLG0D2gojV+51Ix80JNo2Uh4EQTKvjbZUq5JNrf9K5gSXEqRmi7LBSleOl09F/bWdrlWUgDkalEH847v83U7fA== author 8Cl5MsQaiRie2EbqjNUhj5Jf8TjJ8e/0O1+Knfsw3pyIHKTaaJ2okiPxoDtGEztPS3MXQiDMV1G8jCC5G0mvbw==->fRStq94kRaFpm+n+5BCGNRP7e7NOyihLThCOo0vRo+inA0LbPUbVkJsNJwcWCKIVb9lUylMwc7Bt2vfHTbicPg== site AMe9xpS8GHaCTkyBmnvHESY48H7xtHshMrpi5kjUYaoJZTf9LavJdtFr3DR8pBzC1z3UJtBcxe+eCqd0qqG5Qw== Blog.Post.Title value Interesting Facts AMe9xpS8GHaCTkyBmnvHESY48H7xtHshMrpi5kjUYaoJZTf9LavJdtFr3DR8pBzC1z3UJtBcxe+eCqd0qqG5Qw==->8Cl5MsQaiRie2EbqjNUhj5Jf8TjJ8e/0O1+Knfsw3pyIHKTaaJ2okiPxoDtGEztPS3MXQiDMV1G8jCC5G0mvbw== post petnLBBgNIGTKz+tPUctalOq0LgKZC7zfsehdIYy1mG0tQZZKWACchVY/U5CKfogTLgQr19x5k7AN9DUqKU0GQ== Blog.Post.Content markdown Let me tell you abou... petnLBBgNIGTKz+tPUctalOq0LgKZC7zfsehdIYy1mG0tQZZKWACchVY/U5CKfogTLgQr19x5k7AN9DUqKU0GQ==->8Cl5MsQaiRie2EbqjNUhj5Jf8TjJ8e/0O1+Knfsw3pyIHKTaaJ2okiPxoDtGEztPS3MXQiDMV1G8jCC5G0mvbw== post Ols1d5hkAP55vmPmTHLtO4bHrkc4HayJ97SQe3OyhZIijRyL/5b79W4LRSKau60gi/lztl+zalDWfP96g8lndg== Blog.Post.Deleted deletedAt 2024-05-18T20:32:03.... Ols1d5hkAP55vmPmTHLtO4bHrkc4HayJ97SQe3OyhZIijRyL/5b79W4LRSKau60gi/lztl+zalDWfP96g8lndg==->BXJeL5YB/zBDnSIp09B7ZEa26C5LaOfc5RvoDPXDWUS0lrXoxJrvn9enpf10BY5vVIa4XwS0HWm6WGXEokldNA== post

Extend the specification to filter out deleted posts and project properties such as title.

var postsInSite = Given<Site>.Match((site, facts) =>
    from post in facts.OfType<Post>()
    where post.site == site
    where !facts.Any<PostDeleted>(pd => pd.post == post &&
        !facts.Any<PostRestored>(pr => pr.deleted == pd))
    select new
    {
        Post = post,
        Title =
            from title in facts.OfType<PostTitle>()
            where title.post == post
            where !facts.Any<PostTitle>(next => next.prior.Contains(title))
            select title.value,
        Content =
            from content in facts.OfType<PostContent>()
            where content.post == post
            where !facts.Any<PostContent>(next => next.prior.Contains(content))
            select content.markdown
    });

var posts = await jinagaClient.Query(postsInSite, site);

posts

Create a view model for the child screen in the same way you did for the parent.

var postsViewModel = posts.Select(p => new
{
    Title = p.Title.FirstOrDefault() ?? "New post",
    Content = p.Content.FirstOrDefault() ?? ""
});

postsViewModel

Continue With

Tree View

Jinaga is a product of Jinaga LLC.

Michael L Perry, President