models

package
v2.0.5 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Jan 19, 2026 License: GPL-3.0 Imports: 5 Imported by: 0

Documentation

Overview

Package models provides data structures for representing blog posts and collections.

The primary types in this package are Post and PostList, which are designed to work with the parser package to create a complete blog generation system.

Basic Usage

Post objects are typically created by the parser package, but can also be constructed manually:

post := &models.Post{
    Title:       "My First Blog Post",
    Date:        time.Now(),
    Description: "An introduction to my blog",
    Tags:        []string{"intro", "meta"},
    Content:     "<p>Hello, world!</p>",
}

if err := post.Validate(); err != nil {
    log.Fatal(err)
}

post.GenerateSlug()
fmt.Println(post.Slug) // Output: my-first-blog-post

Working with PostList

PostList provides methods for filtering, sorting, and working with collections of posts:

var posts models.PostList = []*models.Post{post1, post2, post3}

// Sort by date (newest first)
posts.SortByDate()

// Filter by tag
goPosts := posts.FilterByTag("go")

// Get all unique tags
allTags := posts.GetAllTags()

Concurrency Safety

Post and PostList types are not safe for concurrent modification. If you need to access posts from multiple goroutines, you must provide your own synchronization. However, read-only operations on Post objects (such as calling HasTag or FormattedDate) are safe for concurrent use.

Example
package main

import (
	"fmt"
	"time"

	"github.com/harrydayexe/GoBlog/v2/pkg/models"
)

func main() {
	// Create a new blog post
	post := &models.Post{
		Title:       "Getting Started with Go",
		Date:        time.Date(2024, 3, 15, 0, 0, 0, 0, time.UTC),
		Description: "An introduction to Go programming",
		Tags:        []string{"go", "tutorial"},
		Content:     []byte("<p>Welcome to Go programming!</p>"),
	}

	// Generate a URL-friendly slug
	post.GenerateSlug()

	fmt.Println(post.Title)
	fmt.Println(post.Slug)
	fmt.Println(post.FormattedDate())
}
Output:

Getting Started with Go
getting-started-with-go
March 15, 2024

Index

Examples

Constants

This section is empty.

Variables

This section is empty.

Functions

This section is empty.

Types

type BaseData added in v2.0.5

type BaseData struct {
	// SiteTitle is the name of the blog site.
	// Example: "My Awesome Blog"
	SiteTitle string

	// PageTitle is the title for this specific page.
	// Used in <title> tag and may be shown in header.
	// Example: "How to Use Go Templates"
	PageTitle string

	// Description is the meta description for SEO.
	// Should be 150-160 characters.
	Description string

	// Year is the current year, useful for copyright notices.
	// Example: 2026
	Year int
}

BaseData contains common data available to all templates. This data is included in all page renders.

type IndexPageData added in v2.0.5

type IndexPageData struct {
	BaseData

	// Posts is the list of posts to display, typically sorted by date descending.
	// Use range to iterate: {{range .Posts}}...{{end}}
	Posts []*Post

	// TotalPosts is the total number of posts in the blog.
	TotalPosts int
}

IndexPageData is passed to the index page template. It shows a list of recent blog posts.

type Post

type Post struct {
	// Frontmatter fields
	Title       string    `yaml:"title"`
	Date        time.Time `yaml:"date"`
	Description string    `yaml:"description"`
	Tags        []string  `yaml:"tags"`

	// Generated fields
	Slug        string        // URL-friendly identifier
	Content     []byte        // Rendered HTML content
	HTMLContent template.HTML // HTML content for templates (not escaped)
	RawContent  string        // Original markdown content
	SourcePath  string        // Path to source markdown file
	PublishDate time.Time     // Formatted publish date
}

Post represents a blog post with metadata and content

func (*Post) FormattedDate

func (p *Post) FormattedDate() string

FormattedDate returns the date in a human-readable format. The format used is "January 2, 2006" (e.g., "March 15, 2024").

Example
package main

import (
	"fmt"
	"time"

	"github.com/harrydayexe/GoBlog/v2/pkg/models"
)

func main() {
	post := &models.Post{
		Date: time.Date(2024, 12, 25, 10, 30, 0, 0, time.UTC),
	}

	fmt.Println(post.FormattedDate())
}
Output:

December 25, 2024

func (*Post) GenerateSlug

func (p *Post) GenerateSlug()

GenerateSlug creates a URL-friendly slug from the title or filename. If the Slug field is already set, this method does nothing.

The slug generation process:

  1. Attempts to use the post's Title if available
  2. Falls back to the filename (without extension) if no Title exists
  3. Converts to lowercase
  4. Replaces spaces and underscores with hyphens
  5. Removes all non-alphanumeric characters except hyphens
  6. Removes consecutive and leading/trailing hyphens

Example: "Hello World!" becomes "hello-world"

Example
package main

import (
	"fmt"

	"github.com/harrydayexe/GoBlog/v2/pkg/models"
)

func main() {
	post := &models.Post{
		Title: "Hello World! This is My First Post",
	}

	post.GenerateSlug()
	fmt.Println(post.Slug)

	// Slug generation is idempotent
	post.GenerateSlug()
	fmt.Println(post.Slug)

}
Output:

hello-world-this-is-my-first-post
hello-world-this-is-my-first-post

func (*Post) HasTag

func (p *Post) HasTag(tag string) bool

HasTag checks if the post has a specific tag. The comparison is case-insensitive, so "Go", "go", and "GO" are all considered equal. Returns true if the tag is found, false otherwise.

Example
package main

import (
	"fmt"

	"github.com/harrydayexe/GoBlog/v2/pkg/models"
)

func main() {
	post := &models.Post{
		Title: "Go Tutorial",
		Tags:  []string{"Go", "Programming", "Tutorial"},
	}

	// Tag comparison is case-insensitive
	fmt.Println(post.HasTag("go"))
	fmt.Println(post.HasTag("GO"))
	fmt.Println(post.HasTag("programming"))
	fmt.Println(post.HasTag("javascript"))

}
Output:

true
true
true
false

func (*Post) ShortDate

func (p *Post) ShortDate() string

ShortDate returns the date in YYYY-MM-DD format. This format is suitable for ISO 8601 compatibility and sorting (e.g., "2024-03-15").

Example
package main

import (
	"fmt"
	"time"

	"github.com/harrydayexe/GoBlog/v2/pkg/models"
)

func main() {
	post := &models.Post{
		Date: time.Date(2024, 3, 5, 10, 30, 0, 0, time.UTC),
	}

	fmt.Println(post.ShortDate())
}
Output:

2024-03-05

func (*Post) Validate

func (p *Post) Validate() error

Validate checks if the post has all required fields. It returns an error if any of the following fields are missing or invalid:

  • Title: must be non-empty
  • Date: must be non-zero
  • Description: must be non-empty

The returned error includes the source file path for debugging purposes.

Example
package main

import (
	"fmt"
	"time"

	"github.com/harrydayexe/GoBlog/v2/pkg/models"
)

func main() {
	// Valid post
	validPost := &models.Post{
		Title:       "My Post",
		Date:        time.Now(),
		Description: "A description",
	}

	if err := validPost.Validate(); err != nil {
		fmt.Println("Error:", err)
	} else {
		fmt.Println("Valid post")
	}

	// Invalid post (missing title)
	invalidPost := &models.Post{
		Date:        time.Now(),
		Description: "A description",
		SourcePath:  "posts/invalid.md",
	}

	if err := invalidPost.Validate(); err != nil {
		fmt.Println("Invalid: missing title")
	}

}
Output:

Valid post
Invalid: missing title

type PostList

type PostList []*Post

PostList is a collection of posts with helper methods

func (PostList) FilterByTag

func (pl PostList) FilterByTag(tag string) PostList

FilterByTag returns a new PostList containing only posts that have the specified tag. The tag comparison is case-insensitive. The original PostList is not modified. If no posts match the tag, an empty PostList is returned.

Example
package main

import (
	"fmt"

	"github.com/harrydayexe/GoBlog/v2/pkg/models"
)

func main() {
	posts := models.PostList{
		{Title: "Go Basics", Tags: []string{"go", "tutorial"}},
		{Title: "Python Guide", Tags: []string{"python", "tutorial"}},
		{Title: "Advanced Go", Tags: []string{"go", "advanced"}},
	}

	// Filter posts by tag
	goPosts := posts.FilterByTag("go")
	fmt.Printf("Found %d posts about Go\n", len(goPosts))

	for _, post := range goPosts {
		fmt.Println(post.Title)
	}

}
Output:

Found 2 posts about Go
Go Basics
Advanced Go

func (PostList) GetAllTags

func (pl PostList) GetAllTags() []string

GetAllTags returns a unique list of all tags across all posts in the collection. Tags are deduplicated but not sorted. The order of tags in the returned slice is non-deterministic due to map iteration. If the PostList is empty, an empty slice is returned.

Example
package main

import (
	"fmt"

	"github.com/harrydayexe/GoBlog/v2/pkg/models"
)

func main() {
	posts := models.PostList{
		{Tags: []string{"go", "tutorial"}},
		{Tags: []string{"python", "tutorial"}},
		{Tags: []string{"go", "advanced"}},
	}

	allTags := posts.GetAllTags()

	// Note: order is non-deterministic, so we just check the count
	fmt.Printf("Found %d unique tags\n", len(allTags))

}
Output:

Found 4 unique tags

func (PostList) SortByDate

func (pl PostList) SortByDate()

SortByDate sorts the posts in-place by date in descending order (newest first). This method modifies the PostList directly rather than returning a new one. Posts with equal dates maintain their relative order (stable sort).

Example
package main

import (
	"fmt"
	"time"

	"github.com/harrydayexe/GoBlog/v2/pkg/models"
)

func main() {
	posts := models.PostList{
		{Title: "Oldest Post", Date: time.Date(2024, 1, 1, 0, 0, 0, 0, time.UTC)},
		{Title: "Newest Post", Date: time.Date(2024, 3, 15, 0, 0, 0, 0, time.UTC)},
		{Title: "Middle Post", Date: time.Date(2024, 2, 10, 0, 0, 0, 0, time.UTC)},
	}

	// Sort by date (newest first)
	posts.SortByDate()

	for _, post := range posts {
		fmt.Printf("%s: %s\n", post.Title, post.ShortDate())
	}

}
Output:

Newest Post: 2024-03-15
Middle Post: 2024-02-10
Oldest Post: 2024-01-01

type PostPageData added in v2.0.5

type PostPageData struct {
	BaseData

	// Post is the blog post to display.
	// See models.Post for available fields.
	Post *Post
}

PostPageData is passed to the post page template. It renders a single blog post with all its metadata.

type TagInfo added in v2.0.5

type TagInfo struct {
	// Name is the tag name.
	Name string
	// PostCount is the number of posts with this tag.
	PostCount int
}

TagInfo represents information about a single tag.

type TagPageData added in v2.0.5

type TagPageData struct {
	BaseData

	// Tag is the name of the tag being displayed.
	// Example: "golang", "tutorial", "web-development"
	Tag string

	// Posts is the list of posts with this tag.
	Posts []*Post

	// PostCount is the number of posts with this tag.
	PostCount int
}

TagPageData is passed to tag page templates. It shows all posts with a specific tag.

type TagsIndexPageData added in v2.0.5

type TagsIndexPageData struct {
	BaseData
	// Tags is the list of all tags with their post counts.
	Tags []TagInfo
	// TotalTags is the total number of tags.
	TotalTags int
}

TagsIndexPageData contains data for rendering the tags index page.

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL