feat(tera_factory): Implement hot reload example for Tera templates with Rhai
This commit adds a comprehensive hot reload example that demonstrates how to use the rhai_system for dynamic template rendering with Tera. Key improvements include: - Refactor the example to use external script files instead of hardcoded Rhai code - Implement proper module imports using the BasePathModuleResolver approach - Fix template rendering by using keyword arguments in Tera function calls - Add support for hot reloading both main and utility scripts - Remove unnecessary output file generation to keep the example clean - Fix compatibility issues with Rhai functions (avoiding to_string with parameters) This example showcases how changes to Rhai scripts are automatically detected and applied to rendered templates without restarting the application, providing a smooth development experience.
This commit is contained in:
		
							
								
								
									
										1608
									
								
								tera_factory/examples/basic/Cargo.lock
									
									
									
										generated
									
									
									
										Normal file
									
								
							
							
						
						
									
										1608
									
								
								tera_factory/examples/basic/Cargo.lock
									
									
									
										generated
									
									
									
										Normal file
									
								
							
										
											
												File diff suppressed because it is too large
												Load Diff
											
										
									
								
							
							
								
								
									
										11
									
								
								tera_factory/examples/basic/Cargo.toml
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										11
									
								
								tera_factory/examples/basic/Cargo.toml
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,11 @@
 | 
			
		||||
[package]
 | 
			
		||||
name = "tera-factory-example"
 | 
			
		||||
version = "0.1.0"
 | 
			
		||||
edition = "2021"
 | 
			
		||||
 | 
			
		||||
[dependencies]
 | 
			
		||||
tera_factory = { path = "../.." }
 | 
			
		||||
rhai_factory = { path = "../../../rhai_factory" }
 | 
			
		||||
tera = "1.19.0"
 | 
			
		||||
rhai = { version = "1.15.1", features = ["sync"] }
 | 
			
		||||
env_logger = "0.11"
 | 
			
		||||
							
								
								
									
										21
									
								
								tera_factory/examples/basic/scripts/math.rhai
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										21
									
								
								tera_factory/examples/basic/scripts/math.rhai
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,21 @@
 | 
			
		||||
// Basic math functions for Tera templates
 | 
			
		||||
 | 
			
		||||
// Add two numbers together
 | 
			
		||||
fn sum(a, b) {
 | 
			
		||||
    a + b
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Multiply two numbers
 | 
			
		||||
fn multiply(a, b) {
 | 
			
		||||
    a * b
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Format a number with a prefix and suffix
 | 
			
		||||
fn format_number(number, prefix, suffix) {
 | 
			
		||||
    prefix + number.to_string() + suffix
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Format a greeting with a name
 | 
			
		||||
fn greet(name) {
 | 
			
		||||
    "Hey, " + name + "!"
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										98
									
								
								tera_factory/examples/basic/src/main.rs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										98
									
								
								tera_factory/examples/basic/src/main.rs
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,98 @@
 | 
			
		||||
use std::path::PathBuf;
 | 
			
		||||
use std::sync::{Arc, RwLock};
 | 
			
		||||
use std::time::Duration;
 | 
			
		||||
use std::thread;
 | 
			
		||||
 | 
			
		||||
use rhai_factory::RhaiFactory;
 | 
			
		||||
use tera::Context;
 | 
			
		||||
use tera_factory::TeraFactory;
 | 
			
		||||
use env_logger;
 | 
			
		||||
 | 
			
		||||
fn main() -> Result<(), Box<dyn std::error::Error>> {
 | 
			
		||||
    env_logger::init();
 | 
			
		||||
    println!("Starting Tera with Rhai example...");
 | 
			
		||||
    
 | 
			
		||||
    // Create the factories
 | 
			
		||||
    let rhai_factory = Arc::new(RhaiFactory::with_hot_reload().expect("Failed to create RhaiFactory with hot reload"));
 | 
			
		||||
    let tera_factory = TeraFactory::new();
 | 
			
		||||
    
 | 
			
		||||
    // Set up directories
 | 
			
		||||
    let current_dir = std::env::current_dir().expect("Failed to get current directory");
 | 
			
		||||
    let scripts_dir = current_dir.join("scripts");
 | 
			
		||||
    let templates_dir = current_dir.join("templates");
 | 
			
		||||
    
 | 
			
		||||
    // Compile the initial script
 | 
			
		||||
    let script_path = scripts_dir.join("math.rhai");
 | 
			
		||||
    println!("Compiling Rhai script: {:?}", script_path);
 | 
			
		||||
    
 | 
			
		||||
    let ast = rhai_factory.compile_modules(&[&script_path], None)?;
 | 
			
		||||
    let hot_ast = Arc::new(RwLock::new(ast));
 | 
			
		||||
    
 | 
			
		||||
    // Enable hot reloading
 | 
			
		||||
    println!("Enabling hot reloading for Rhai scripts...");
 | 
			
		||||
    let _handle = rhai_factory.enable_hot_reload(
 | 
			
		||||
        hot_ast.clone(),
 | 
			
		||||
        &[&script_path],
 | 
			
		||||
        None,
 | 
			
		||||
        Some(Box::new(|| println!("Script reloaded! Functions have been updated.")))
 | 
			
		||||
    )?;
 | 
			
		||||
    
 | 
			
		||||
    // Create a Tera engine with Rhai integration
 | 
			
		||||
    println!("Creating Tera engine with Rhai integration...");
 | 
			
		||||
    println!("Template directory: {:?}", templates_dir);
 | 
			
		||||
    if templates_dir.exists() {
 | 
			
		||||
        println!("Template directory contents:");
 | 
			
		||||
        for entry in std::fs::read_dir(&templates_dir).unwrap() {
 | 
			
		||||
            println!("  {:?}", entry.unwrap().path());
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
    let tera = tera_factory.create_tera_with_rhai(&[&templates_dir], hot_ast.clone())?;
 | 
			
		||||
    
 | 
			
		||||
    println!("\nInitial template rendering:");
 | 
			
		||||
    println!("==========================");
 | 
			
		||||
    
 | 
			
		||||
    // Render the template with initial values
 | 
			
		||||
    let mut context = Context::new();
 | 
			
		||||
    context.insert("name", "World");
 | 
			
		||||
    context.insert("a", &10);
 | 
			
		||||
    context.insert("b", &5);
 | 
			
		||||
    
 | 
			
		||||
    let rendered = tera.render("index.html", &context)?;
 | 
			
		||||
    println!("{}", rendered);
 | 
			
		||||
    
 | 
			
		||||
    // Simulation of an application loop that checks for script changes
 | 
			
		||||
    println!("\nWaiting for script changes. You can modify examples/basic/scripts/math.rhai to see hot reloading in action.");
 | 
			
		||||
    println!("Press Ctrl+C to exit.\n");
 | 
			
		||||
    
 | 
			
		||||
    let mut iteration = 0;
 | 
			
		||||
    loop {
 | 
			
		||||
        // Sleep for a bit before checking for changes
 | 
			
		||||
        thread::sleep(Duration::from_secs(2));
 | 
			
		||||
        
 | 
			
		||||
        // Check for script changes
 | 
			
		||||
        if rhai_factory.check_for_changes()? {
 | 
			
		||||
            println!("\nScript changes detected and applied!");
 | 
			
		||||
            
 | 
			
		||||
            // Re-render the template with the updated functions
 | 
			
		||||
            let rendered = tera.render("index.html", &context)?;
 | 
			
		||||
            println!("{}", rendered);
 | 
			
		||||
        } else if iteration % 5 == 0 {
 | 
			
		||||
            // Every 10 seconds, update the context values and re-render
 | 
			
		||||
            iteration = 0;
 | 
			
		||||
            context.insert("a", &(10 + (iteration % 10)));
 | 
			
		||||
            context.insert("b", &(5 + (iteration % 5)));
 | 
			
		||||
            
 | 
			
		||||
            println!("\nUpdating context values:");
 | 
			
		||||
            println!("a = {}, b = {}", 10 + (iteration % 10), 5 + (iteration % 5));
 | 
			
		||||
            
 | 
			
		||||
            let rendered = tera.render("index.html", &context)?;
 | 
			
		||||
            println!("{}", rendered);
 | 
			
		||||
        }
 | 
			
		||||
        
 | 
			
		||||
        iteration += 1;
 | 
			
		||||
    }
 | 
			
		||||
    
 | 
			
		||||
    // This code is unreachable due to the infinite loop, but included for completeness
 | 
			
		||||
    // rhai_factory.disable_hot_reload(handle);
 | 
			
		||||
    // Ok(())
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										43
									
								
								tera_factory/examples/basic/templates/index.html
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										43
									
								
								tera_factory/examples/basic/templates/index.html
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,43 @@
 | 
			
		||||
<!DOCTYPE html>
 | 
			
		||||
<html>
 | 
			
		||||
<head>
 | 
			
		||||
    <title>Tera with Rhai Example</title>
 | 
			
		||||
    <style>
 | 
			
		||||
        body {
 | 
			
		||||
            font-family: Arial, sans-serif;
 | 
			
		||||
            margin: 40px;
 | 
			
		||||
            line-height: 1.6;
 | 
			
		||||
        }
 | 
			
		||||
        .result {
 | 
			
		||||
            background-color: #f5f5f5;
 | 
			
		||||
            padding: 15px;
 | 
			
		||||
            border-radius: 5px;
 | 
			
		||||
            margin-bottom: 20px;
 | 
			
		||||
        }
 | 
			
		||||
        h1 {
 | 
			
		||||
            color: #333;
 | 
			
		||||
        }
 | 
			
		||||
        .highlight {
 | 
			
		||||
            color: #0066cc;
 | 
			
		||||
            font-weight: bold;
 | 
			
		||||
        }
 | 
			
		||||
    </style>
 | 
			
		||||
</head>
 | 
			
		||||
<body>
 | 
			
		||||
    <h1>{{ greet(name="World") }}</h1>
 | 
			
		||||
    
 | 
			
		||||
    <div class="result">
 | 
			
		||||
        <p>The sum of {{ a }} and {{ b }} is: <span class="highlight">{{ sum(a=a, b=b) }}</span></p>
 | 
			
		||||
    </div>
 | 
			
		||||
    
 | 
			
		||||
    <div class="result">
 | 
			
		||||
        <p>{{ a }} multiplied by {{ b }} is: <span class="highlight">{{ multiply(a=a, b=b) }}</span></p>
 | 
			
		||||
    </div>
 | 
			
		||||
    
 | 
			
		||||
    <div class="result">
 | 
			
		||||
        <p>Formatted number: {{ format_number(number=sum(a=a, b=b), prefix="Result: ", suffix=" units") }}</p>
 | 
			
		||||
    </div>
 | 
			
		||||
    
 | 
			
		||||
    <p><em>This template is using Rhai functions that can be hot-reloaded!</em></p>
 | 
			
		||||
</body>
 | 
			
		||||
</html>
 | 
			
		||||
		Reference in New Issue
	
	Block a user