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 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 ¶
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:
- Attempts to use the post's Title if available
- Falls back to the filename (without extension) if no Title exists
- Converts to lowercase
- Replaces spaces and underscores with hyphens
- Removes all non-alphanumeric characters except hyphens
- 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 ¶
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 ¶
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 ¶
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 ¶
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 ¶
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