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:
Timur Gordon
2025-05-02 21:34:28 +02:00
parent c23de6871b
commit 22032f329a
25 changed files with 5635 additions and 0 deletions

View 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(())
}