rotemplate

package module
v0.0.0-...-a6ee939 Latest Latest
Warning

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

Go to latest
Published: Jan 10, 2026 License: Apache-2.0 Imports: 4 Imported by: 0

README

Template Plugin

The template plugin provides operators for processing data with Go's text and HTML templates.

Installation

go get github.com/samber/ro/plugins/template

Operators

TextTemplate

Processes data with text templates using Go's text/template package.

import (
    "github.com/samber/ro"
    rotemplate "github.com/samber/ro/plugins/template"
)

type User struct {
    Name string
    Age  int
    City string
}

observable := ro.Pipe1(
    ro.Just(
        User{Name: "Alice", Age: 30, City: "New York"},
        User{Name: "Bob", Age: 25, City: "Los Angeles"},
        User{Name: "Charlie", Age: 35, City: "Chicago"},
    ),
    rotemplate.TextTemplate[User]("Hello {{.Name}}, you are {{.Age}} years old from {{.City}}."),
)

subscription := observable.Subscribe(ro.PrintObserver[string]())
defer subscription.Unsubscribe()

// Output:
// Next: Hello Alice, you are 30 years old from New York.
// Next: Hello Bob, you are 25 years old from Los Angeles.
// Next: Hello Charlie, you are 35 years old from Chicago.
// Completed
HTMLTemplate

Processes data with HTML templates using Go's html/template package.

observable := ro.Pipe1(
    ro.Just(
        User{Name: "Alice", Age: 30, City: "New York"},
        User{Name: "Bob", Age: 25, City: "Los Angeles"},
        User{Name: "Charlie", Age: 35, City: "Chicago"},
    ),
    rotemplate.HTMLTemplate[User](`<div class="user">
  <h2>{{.Name}}</h2>
  <p>Age: {{.Age}}</p>
  <p>City: {{.City}}</p>
</div>`),
)

subscription := observable.Subscribe(ro.PrintObserver[string]())
defer subscription.Unsubscribe()

// Output:
// Next: <div class="user">
//   <h2>Alice</h2>
//   <p>Age: 30</p>
//   <p>City: New York</p>
// </div>
// Next: <div class="user">
//   <h2>Bob</h2>
//   <p>Age: 25</p>
//   <p>City: Los Angeles</p>
// </div>
// Next: <div class="user">
//   <h2>Charlie</h2>
//   <p>Age: 35</p>
//   <p>City: Chicago</p>
// </div>
// Completed

Supported Data Types

The template plugin supports various data types:

Structs
type Person struct {
    Name string
    Age  int
    Email string
}

observable := ro.Pipe1(
    ro.Just(Person{Name: "Alice", Age: 30, Email: "alice@example.com"}),
    rotemplate.TextTemplate[Person]("{{.Name}} ({{.Age}}) - {{.Email}}"),
)
Simple Types
// Strings
observable := ro.Pipe1(
    ro.Just("Alice", "Bob", "Charlie"),
    rotemplate.TextTemplate[string]("Hello {{.}}!"),
)

// Integers
observable := ro.Pipe1(
    ro.Just(1, 2, 3, 4, 5),
    rotemplate.TextTemplate[int]("Number: {{.}}"),
)
Maps
observable := ro.Pipe1(
    ro.Just(
        map[string]interface{}{"name": "Alice", "age": 30, "city": "New York"},
        map[string]interface{}{"name": "Bob", "age": 25, "city": "Los Angeles"},
    ),
    rotemplate.TextTemplate[map[string]interface{}]("User {{.name}} is {{.age}} years old from {{.city}}."),
)

Template Features

Conditionals
type Person struct {
    Name string
    Age  int
}

observable := ro.Pipe1(
    ro.Just(
        Person{Name: "Alice", Age: 30},
        Person{Name: "Bob", Age: 17},
        Person{Name: "Charlie", Age: 25},
    ),
    rotemplate.TextTemplate[Person](`{{.Name}} is {{if ge .Age 18}}an adult{{else}}a minor{{end}} ({{.Age}} years old).`),
)

// Output:
// Next: Alice is an adult (30 years old).
// Next: Bob is a minor (17 years old).
// Next: Charlie is an adult (25 years old).
Loops
type Team struct {
    Name    string
    Members []string
}

observable := ro.Pipe1(
    ro.Just(
        Team{Name: "Alpha", Members: []string{"Alice", "Bob", "Charlie"}},
        Team{Name: "Beta", Members: []string{"David", "Eve"}},
    ),
    rotemplate.TextTemplate[Team](`Team {{.Name}}:
{{range .Members}}- {{.}}
{{end}}`),
)
Functions
observable := ro.Pipe1(
    ro.Just("hello world", "GOLANG PROGRAMMING"),
    rotemplate.TextTemplate[string](`Original: {{.}}
Upper: {{. | upper}}
Lower: {{. | lower}}`),
)

Error Handling

Both TextTemplate and HTMLTemplate handle errors gracefully and will emit error notifications for template processing errors:

observable := ro.Pipe1(
    ro.Just(
        User{Name: "Alice", Age: 30, City: "New York"},
        User{Name: "Bob", Age: 25, City: "Los Angeles"},
    ),
    rotemplate.TextTemplate[User]("Hello {{.Name}}, you are {{.Age}} years old from {{.InvalidField}}."),
)

subscription := observable.Subscribe(
    ro.NewObserver(
        func(value string) {
            // Handle successful template processing
        },
        func(err error) {
            // Handle template processing error
            // This could be due to:
            // - Invalid template syntax
            // - Missing template fields
            // - Template execution errors
        },
        func() {
            // Handle completion
        },
    ),
)
defer subscription.Unsubscribe()

Real-world Example

Here's a practical example that generates HTML reports:

import (
    "github.com/samber/ro"
    rotemplate "github.com/samber/ro/plugins/template"
)

type Report struct {
    Title   string
    Date    string
    Items   []string
    Summary string
}

// Generate HTML reports
pipeline := ro.Pipe2(
    // Simulate report data
    ro.Just(
        Report{
            Title:   "Monthly Report",
            Date:    "2024-01-15",
            Items:   []string{"Item 1", "Item 2", "Item 3"},
            Summary: "All items completed successfully",
        },
        Report{
            Title:   "Weekly Summary",
            Date:    "2024-01-12",
            Items:   []string{"Task A", "Task B"},
            Summary: "Tasks in progress",
        },
    ),
    // Generate HTML
    rotemplate.HTMLTemplate[Report](`<!DOCTYPE html>
<html>
<head>
    <title>{{.Title}}</title>
</head>
<body>
    <h1>{{.Title}}</h1>
    <p>Date: {{.Date}}</p>
    <h2>Items:</h2>
    <ul>
    {{range .Items}}
        <li>{{.}}</li>
    {{end}}
    </ul>
    <p><strong>Summary:</strong> {{.Summary}}</p>
</body>
</html>`),
)

subscription := pipeline.Subscribe(ro.PrintObserver[string]())
defer subscription.Unsubscribe()

Performance Considerations

  • The plugin uses Go's standard text/template and html/template packages
  • Templates are compiled once and reused for all data items
  • Error handling is built into both template operators
  • HTML templates provide automatic escaping for security
  • Consider template complexity for performance with large datasets
  • Use appropriate template syntax for your use case
  • Templates support all Go template features including functions, pipelines, and nested templates

Documentation

Index

Examples

Constants

This section is empty.

Variables

This section is empty.

Functions

func HTMLTemplate

func HTMLTemplate[T any](template string) func(ro.Observable[T]) ro.Observable[string]

HTMLTemplate creates an HTML template operator that renders templates with input data. Play: https://go.dev/play/p/emlON8wyaXx

Example
// Process data with HTML template
observable := ro.Pipe1(
	ro.Just(
		User{Name: "Alice", Age: 30, City: "New York"},
		User{Name: "Bob", Age: 25, City: "Los Angeles"},
		User{Name: "Charlie", Age: 35, City: "Chicago"},
	),
	HTMLTemplate[User](`<div class="user">
  <h2>{{.Name}}</h2>
  <p>Age: {{.Age}}</p>
  <p>City: {{.City}}</p>
</div>`),
)

subscription := observable.Subscribe(ro.PrintObserver[string]())
defer subscription.Unsubscribe()
Output:

Next: <div class="user">
  <h2>Alice</h2>
  <p>Age: 30</p>
  <p>City: New York</p>
</div>
Next: <div class="user">
  <h2>Bob</h2>
  <p>Age: 25</p>
  <p>City: Los Angeles</p>
</div>
Next: <div class="user">
  <h2>Charlie</h2>
  <p>Age: 35</p>
  <p>City: Chicago</p>
</div>
Completed
Example (WithConditionals)
// Process data with conditional HTML templates
type Person struct {
	Name string
	Age  int
}

observable := ro.Pipe1(
	ro.Just(
		Person{Name: "Alice", Age: 30},
		Person{Name: "Bob", Age: 17},
		Person{Name: "Charlie", Age: 25},
	),
	HTMLTemplate[Person](`<div class="person {{if ge .Age 18}}adult{{else}}minor{{end}}">
  <h2>{{.Name}}</h2>
  <p>Age: {{.Age}}</p>
  <p>Status: {{if ge .Age 18}}Adult{{else}}Minor{{end}}</p>
</div>`),
)

subscription := observable.Subscribe(ro.PrintObserver[string]())
defer subscription.Unsubscribe()
Output:

Next: <div class="person adult">
  <h2>Alice</h2>
  <p>Age: 30</p>
  <p>Status: Adult</p>
</div>
Next: <div class="person minor">
  <h2>Bob</h2>
  <p>Age: 17</p>
  <p>Status: Minor</p>
</div>
Next: <div class="person adult">
  <h2>Charlie</h2>
  <p>Age: 25</p>
  <p>Status: Adult</p>
</div>
Completed
Example (WithMaps)
// Process maps with HTML template
observable := ro.Pipe1(
	ro.Just(
		map[string]interface{}{"name": "Alice", "age": 30, "city": "New York"},
		map[string]interface{}{"name": "Bob", "age": 25, "city": "Los Angeles"},
	),
	HTMLTemplate[map[string]interface{}](`<div class="user">
  <h2>{{.name}}</h2>
  <p>Age: {{.age}}</p>
  <p>City: {{.city}}</p>
</div>`),
)

subscription := observable.Subscribe(ro.PrintObserver[string]())
defer subscription.Unsubscribe()
Output:

Next: <div class="user">
  <h2>Alice</h2>
  <p>Age: 30</p>
  <p>City: New York</p>
</div>
Next: <div class="user">
  <h2>Bob</h2>
  <p>Age: 25</p>
  <p>City: Los Angeles</p>
</div>
Completed
Example (WithSimpleTypes)
// Process simple types with HTML template
observable := ro.Pipe1(
	ro.Just("Alice", "Bob", "Charlie"),
	HTMLTemplate[string](`<span class="name">{{.}}</span>`),
)

subscription := observable.Subscribe(ro.PrintObserver[string]())
defer subscription.Unsubscribe()
Output:

Next: <span class="name">Alice</span>
Next: <span class="name">Bob</span>
Next: <span class="name">Charlie</span>
Completed

func TextTemplate

func TextTemplate[T any](template string) func(ro.Observable[T]) ro.Observable[string]

TextTemplate creates a text template operator that renders templates with input data. Play: https://go.dev/play/p/06cCGj34vLo

Example
// Process data with text template
observable := ro.Pipe1(
	ro.Just(
		User{Name: "Alice", Age: 30, City: "New York"},
		User{Name: "Bob", Age: 25, City: "Los Angeles"},
		User{Name: "Charlie", Age: 35, City: "Chicago"},
	),
	TextTemplate[User]("Hello {{.Name}}, you are {{.Age}} years old from {{.City}}."),
)

subscription := observable.Subscribe(ro.PrintObserver[string]())
defer subscription.Unsubscribe()
Output:

Next: Hello Alice, you are 30 years old from New York.
Next: Hello Bob, you are 25 years old from Los Angeles.
Next: Hello Charlie, you are 35 years old from Chicago.
Completed
Example (WithConditionals)
// Process data with conditional templates
type Person struct {
	Name string
	Age  int
}

observable := ro.Pipe1(
	ro.Just(
		Person{Name: "Alice", Age: 30},
		Person{Name: "Bob", Age: 17},
		Person{Name: "Charlie", Age: 25},
	),
	TextTemplate[Person](`{{.Name}} is {{if ge .Age 18}}an adult{{else}}a minor{{end}} ({{.Age}} years old).`),
)

subscription := observable.Subscribe(ro.PrintObserver[string]())
defer subscription.Unsubscribe()
Output:

Next: Alice is an adult (30 years old).
Next: Bob is a minor (17 years old).
Next: Charlie is an adult (25 years old).
Completed
Example (WithError)
// Process data with potential template errors
observable := ro.Pipe1(
	ro.Just(
		User{Name: "Alice", Age: 30, City: "New York"},
		User{Name: "Bob", Age: 25, City: "Los Angeles"},
	),
	TextTemplate[User]("Hello {{.Name}}, you are {{.Age}} years old from {{.InvalidField}}."),
)

subscription := observable.Subscribe(ro.PrintObserver[string]())
defer subscription.Unsubscribe()
Output:

Error: template: Hello {{.Name}}, you are {{.Age}} years old from {{.InvalidField}}.:1:51: executing "Hello {{.Name}}, you are {{.Age}} years old from {{.InvalidField}}." at <.InvalidField>: can't evaluate field InvalidField in type rotemplate.User
Example (WithMaps)
// Process maps with text template
observable := ro.Pipe1(
	ro.Just(
		map[string]interface{}{"name": "Alice", "age": 30, "city": "New York"},
		map[string]interface{}{"name": "Bob", "age": 25, "city": "Los Angeles"},
	),
	TextTemplate[map[string]interface{}]("User {{.name}} is {{.age}} years old from {{.city}}."),
)

subscription := observable.Subscribe(ro.PrintObserver[string]())
defer subscription.Unsubscribe()
Output:

Next: User Alice is 30 years old from New York.
Next: User Bob is 25 years old from Los Angeles.
Completed
Example (WithSimpleTypes)
// Process simple types with text template
observable := ro.Pipe1(
	ro.Just("Alice", "Bob", "Charlie"),
	TextTemplate[string]("Hello {{.}}!"),
)

subscription := observable.Subscribe(ro.PrintObserver[string]())
defer subscription.Unsubscribe()
Output:

Next: Hello Alice!
Next: Hello Bob!
Next: Hello Charlie!
Completed

Types

This section is empty.

Jump to

Keyboard shortcuts

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