Add ViewEngine for easy server integration
- ViewEngine manages views directory with path resolution - Auto-loads mixins from views/mixins/ directory - Simplifies template paths (relative to views dir, auto-adds extension) - Updated example app to use ViewEngine - Added example mixins (buttons.pug, cards.pug) - Updated CLAUDE.md with ViewEngine documentation
This commit is contained in:
@@ -1,7 +1,7 @@
|
||||
//! Pugz Template Inheritance Demo
|
||||
//!
|
||||
//! A web application demonstrating Pug-style template inheritance
|
||||
//! using the Pugz template engine with http.zig server.
|
||||
//! using the Pugz ViewEngine with http.zig server.
|
||||
//!
|
||||
//! Routes:
|
||||
//! GET / - Home page (layout.pug)
|
||||
@@ -19,58 +19,25 @@ const Allocator = std.mem.Allocator;
|
||||
/// Application state shared across all requests
|
||||
const App = struct {
|
||||
allocator: Allocator,
|
||||
views_dir: []const u8,
|
||||
engine: pugz.ViewEngine,
|
||||
|
||||
/// File resolver for loading templates from disk
|
||||
pub fn fileResolver(allocator: Allocator, path: []const u8) ?[]const u8 {
|
||||
const file = std.fs.cwd().openFile(path, .{}) catch return null;
|
||||
defer file.close();
|
||||
return file.readToEndAlloc(allocator, 1024 * 1024) catch null;
|
||||
pub fn init(allocator: Allocator) !App {
|
||||
return .{
|
||||
.allocator = allocator,
|
||||
.engine = try pugz.ViewEngine.init(allocator, .{
|
||||
.views_dir = "src/examples/app_01/views",
|
||||
}),
|
||||
};
|
||||
}
|
||||
|
||||
/// Render a template with data
|
||||
pub fn render(self: *App, template_name: []const u8, data: anytype) ![]u8 {
|
||||
// Build full path
|
||||
const template_path = try std.fs.path.join(self.allocator, &.{ self.views_dir, template_name });
|
||||
defer self.allocator.free(template_path);
|
||||
|
||||
// Load template source
|
||||
const source = fileResolver(self.allocator, template_path) orelse {
|
||||
return error.TemplateNotFound;
|
||||
};
|
||||
defer self.allocator.free(source);
|
||||
|
||||
// Parse template
|
||||
var lexer = pugz.Lexer.init(self.allocator, source);
|
||||
const tokens = try lexer.tokenize();
|
||||
|
||||
var parser = pugz.Parser.init(self.allocator, tokens);
|
||||
const doc = try parser.parse();
|
||||
|
||||
// Setup context with data
|
||||
var ctx = pugz.runtime.Context.init(self.allocator);
|
||||
defer ctx.deinit();
|
||||
|
||||
try ctx.pushScope();
|
||||
inline for (std.meta.fields(@TypeOf(data))) |field| {
|
||||
const value = @field(data, field.name);
|
||||
try ctx.set(field.name, pugz.runtime.toValue(self.allocator, value));
|
||||
}
|
||||
|
||||
// Render with file resolver for includes/extends
|
||||
var runtime = pugz.runtime.Runtime.init(self.allocator, &ctx, .{
|
||||
.file_resolver = fileResolver,
|
||||
.base_dir = self.views_dir,
|
||||
});
|
||||
defer runtime.deinit();
|
||||
|
||||
return runtime.renderOwned(doc);
|
||||
pub fn deinit(self: *App) void {
|
||||
self.engine.deinit();
|
||||
}
|
||||
};
|
||||
|
||||
/// Handler for GET /
|
||||
fn index(app: *App, _: *httpz.Request, res: *httpz.Response) !void {
|
||||
const html = app.render("layout.pug", .{
|
||||
const html = app.engine.render(app.allocator, "layout", .{
|
||||
.title = "Home",
|
||||
}) catch |err| {
|
||||
res.status = 500;
|
||||
@@ -84,7 +51,7 @@ fn index(app: *App, _: *httpz.Request, res: *httpz.Response) !void {
|
||||
|
||||
/// Handler for GET /page-a - demonstrates extends and block override
|
||||
fn pageA(app: *App, _: *httpz.Request, res: *httpz.Response) !void {
|
||||
const html = app.render("page-a.pug", .{
|
||||
const html = app.engine.render(app.allocator, "page-a", .{
|
||||
.title = "Page A - Pets",
|
||||
.items = &[_][]const u8{ "A", "B", "C" },
|
||||
.n = 0,
|
||||
@@ -100,7 +67,7 @@ fn pageA(app: *App, _: *httpz.Request, res: *httpz.Response) !void {
|
||||
|
||||
/// Handler for GET /page-b - demonstrates sub-layout inheritance
|
||||
fn pageB(app: *App, _: *httpz.Request, res: *httpz.Response) !void {
|
||||
const html = app.render("page-b.pug", .{
|
||||
const html = app.engine.render(app.allocator, "page-b", .{
|
||||
.title = "Page B - Sub Layout",
|
||||
}) catch |err| {
|
||||
res.status = 500;
|
||||
@@ -114,7 +81,7 @@ fn pageB(app: *App, _: *httpz.Request, res: *httpz.Response) !void {
|
||||
|
||||
/// Handler for GET /append - demonstrates block append
|
||||
fn pageAppend(app: *App, _: *httpz.Request, res: *httpz.Response) !void {
|
||||
const html = app.render("page-append.pug", .{
|
||||
const html = app.engine.render(app.allocator, "page-append", .{
|
||||
.title = "Page Append",
|
||||
}) catch |err| {
|
||||
res.status = 500;
|
||||
@@ -128,7 +95,7 @@ fn pageAppend(app: *App, _: *httpz.Request, res: *httpz.Response) !void {
|
||||
|
||||
/// Handler for GET /append-opt - demonstrates optional block keyword
|
||||
fn pageAppendOptional(app: *App, _: *httpz.Request, res: *httpz.Response) !void {
|
||||
const html = app.render("page-appen-optional-blk.pug", .{
|
||||
const html = app.engine.render(app.allocator, "page-appen-optional-blk", .{
|
||||
.title = "Page Append Optional",
|
||||
}) catch |err| {
|
||||
res.status = 500;
|
||||
@@ -145,13 +112,9 @@ pub fn main() !void {
|
||||
defer _ = gpa.deinit();
|
||||
const allocator = gpa.allocator();
|
||||
|
||||
// Views directory - relative to current working directory
|
||||
const views_dir = "src/examples/app_01/views";
|
||||
|
||||
var app = App{
|
||||
.allocator = allocator,
|
||||
.views_dir = views_dir,
|
||||
};
|
||||
// Initialize view engine once at startup
|
||||
var app = try App.init(allocator);
|
||||
defer app.deinit();
|
||||
|
||||
var server = try httpz.Server(*App).init(allocator, .{ .port = 8080 }, &app);
|
||||
defer server.deinit();
|
||||
|
||||
5
src/examples/app_01/views/mixins/buttons.pug
Normal file
5
src/examples/app_01/views/mixins/buttons.pug
Normal file
@@ -0,0 +1,5 @@
|
||||
mixin btn(text, type="primary")
|
||||
button(class="btn btn-" + type)= text
|
||||
|
||||
mixin btn-link(href, text)
|
||||
a.btn.btn-link(href=href)= text
|
||||
11
src/examples/app_01/views/mixins/cards.pug
Normal file
11
src/examples/app_01/views/mixins/cards.pug
Normal file
@@ -0,0 +1,11 @@
|
||||
mixin card(title)
|
||||
.card
|
||||
.card-header
|
||||
h3= title
|
||||
.card-body
|
||||
block
|
||||
|
||||
mixin card-simple(title, body)
|
||||
.card
|
||||
h3= title
|
||||
p= body
|
||||
Reference in New Issue
Block a user