From b07dd256cac2a7714924c445badfad74c9970eac Mon Sep 17 00:00:00 2001 From: Mike Christensen Date: Fri, 28 Aug 2026 11:05:02 -0700 Subject: [PATCH] Update README --- README.md | 308 +++++++++--------------------------------------------- 1 file changed, 50 insertions(+), 258 deletions(-) diff --git a/README.md b/README.md index 7d4a60f..f12e152 100644 --- a/README.md +++ b/README.md @@ -1,114 +1,13 @@ # Imp [![Build](https://github.com/KitchenPC/Imp/actions/workflows/build.yml/badge.svg)](https://github.com/KitchenPC/Imp/actions/workflows/build.yml) +[![NuGet](https://img.shields.io/nuget/v/KitchenPC.Imp.svg)](https://www.nuget.org/packages/KitchenPC.Imp) -Imp (In Memory Pages) is a lightweight page framework built on ASP.NET Core middleware. It maps request paths to .NET classes, creates those classes through ASP.NET Core dependency injection, binds query-string values to page properties, and renders the response. Pages can render HTML directly or use HTML templates embedded in the application's assembly. Embedded templates are compiled once and cached in memory, so no page-template files need to be read from disk while the application is running. +Imp (In Memory Pages) is a lightweight page framework built on ASP.NET Core middleware. It maps request paths to .NET classes, creates pages through dependency injection, binds query-string values to page properties, and renders responses directly or through embedded XML templates. -## Repository layout +## The idea in one example -- `src/Imp` contains the `KitchenPC.Imp` library. -- `src/Imp.Tests` contains the framework unit tests. -- `Samples/TodoApp` is a runnable ASP.NET Core To Do website. -- `Samples/TodoApp.Tests` tests the sample's application behavior. - -## Building and testing - -Restore, build, and run the fast unit-test suite from the repository root: - -```bash -dotnet test -``` - -The tests use in-memory request objects and do not start a web server or make network calls. - -Create a local NuGet package and symbol package with: - -```bash -dotnet pack src/Imp/Imp.csproj --configuration Release --output artifacts -``` - -## Releasing - -Every push and pull request builds, tests, and packs `KitchenPC.Imp` for validation. NuGet publication is intentionally separate and only runs for version tags. To publish a configured release, create and push a tag matching the package version: - -```bash -git tag v0.1.0 -git push origin v0.1.0 -``` - -Package versions on NuGet are immutable. Never reuse a release tag or version; increment the version for each subsequent release. - -## Getting started - -Install Imp in an ASP.NET Core application: - -```xml - - - -``` - -For a complete project-reference example, run the [To Do sample](Samples/TodoApp): - -```bash -dotnet run --project Samples/TodoApp/TodoApp.csproj -``` - -Register Imp near the end of the ASP.NET Core pipeline in `Startup.cs`: - -```csharp -using Imp; - -public void Configure(IApplicationBuilder app, IWebHostEnvironment env) -{ - app.UseStaticFiles(); - app.UseRouting(); - - app.UseImp(config => - config.PageAssembly(typeof(Startup).Assembly) - .RootPageNamespace("HelloWorld.Pages") - .RootTemplateNamespace("HelloWorld.Templates") - .NotFoundPageType() - ); -} -``` - -With the root page namespace set to `HelloWorld.Pages`, Imp uses the URL path to find a page class: - -| Request path | Page type | -| --- | --- | -| `/` | `HelloWorld.Pages.Default` | -| `/hello` | `HelloWorld.Pages.Hello` | -| `/account/settings` | `HelloWorld.Pages.Account.Settings` | - -Page type lookup is case-insensitive. The root path is represented by a class named `Default`. - -## Hello World - -Every page derives from `BasePage`. The smallest page can override `Render` and write directly to the ASP.NET Core response: - -```csharp -using System.Threading.Tasks; -using Imp; -using Microsoft.AspNetCore.Http; - -namespace HelloWorld.Pages -{ - public sealed class Hello : BasePage - { - public override Task Render(HttpResponse response) - { - return response.WriteAsync("

Hello, World!

"); - } - } -} -``` - -This page is available at `/hello`. - -### Query-string parameters - -Imp binds query-string values to matching public writable properties before rendering the page. Property names are case-sensitive during binding. +Given this page class: ```csharp using System.Text.Encodings.Web; @@ -116,187 +15,80 @@ using System.Threading.Tasks; using Imp; using Microsoft.AspNetCore.Http; -namespace HelloWorld.Pages +namespace MySite.Pages; + +public sealed class Hello : BasePage { - public sealed class Hello : BasePage - { - public string Name { get; set; } = "World"; + public string Name { get; set; } = "World"; - public override Task Render(HttpResponse response) - { - string name = HtmlEncoder.Default.Encode(Name); - return response.WriteAsync($"

Hello, {name}!

"); - } + public override Task Render(HttpResponse response) + { + string name = HtmlEncoder.Default.Encode(Name); + return response.WriteAsync($"

Hello, {name}!

"); } } ``` -`/hello?Name=Mike` renders `Hello, Mike!`. Imp supports strings, GUIDs, dates, Boolean and numeric values, and enums. Nullable forms are supported for GUIDs, dates, Boolean values, and numeric values. Values should still be HTML-encoded before being placed in a response. - -## Embedded page templates +Imp maps the request: -A page can keep its HTML in an embedded resource instead of implementing `Render`. First, include the template files as embedded resources in the website project: - -```xml - - - - +```text +/hello?Name=Mike ``` -Create `PageTemplates/Hello.htm`: +to `MySite.Pages.Hello`, binds `Mike` to its `Name` property, and renders: -```xml - - - - Imp example - - -

- - -
+```html +

Hello, Mike!

``` -Associate the embedded resource with a page class and implement the dynamic element as a method: - -```csharp -using System.IO; -using System.Text.Encodings.Web; -using System.Threading.Tasks; -using Imp; -using Imp.TemplateManagers; - -namespace HelloWorld.Pages -{ - [PageTemplate("HelloWorld.PageTemplates.Hello.htm")] - public sealed class Hello : BasePage - { - public string Name { get; set; } = "World"; +That is Imp's central model: **URL paths map to page classes, and query-string parameters map to public writable properties.** Query-property names are case-sensitive, so this example uses `Name`, not `name`. - public Task Greeting(TextWriter output, DynamicContentArgs args) - { - string name = HtmlEncoder.Default.Encode(Name); - return output.WriteAsync($"Hello, {name}!"); - } - } -} -``` +## Get started -`` calls the page's `Greeting` method while the compiled template is being rendered. Dynamic methods accept a `TextWriter` and `DynamicContentArgs` and return a `Task`. Attributes on a dynamic element are available through `args`, for example `` can read `args["Text"]`. +Install the package: -Templates are XML-based and must be well-formed. The outer element for a page is ``. - -### Reusable templates - -Reusable layout templates are embedded beneath the namespace configured by `RootTemplateNamespace`. A layout named `Simple.htm` starts with `