clanker il y a 2 semaines
Parent
commit
c8ca7dadaf
5 fichiers modifiés avec 55 ajouts et 62 suppressions
  1. 5 4
      example/Main.vala
  2. 36 21
      src/Statum.vala
  3. 1 1
      src/StatumHandlers.vala
  4. 7 0
      src/StatumPage.vala
  5. 6 36
      tools/statum-mkpstm/statum-mkpstm.vala

+ 5 - 4
example/Main.vala

@@ -13,10 +13,11 @@ void main(string[] args) {
 
         var statum = application.configure_with<StatumConfigurator>();
         // HomePage is generated from home.html; HomeEntrypoint hydrates it and
-        // embeds the login/bump action references. Actions are auto-mapped to
-        // GUID endpoints; the Statum client scripts are embedded by default.
-        statum.add_entrypoint_page<HomePage, HomeEntrypoint>(new EndpointRoute("/"));
-        statum.add_entrypoint_page<DashboardPage, DashboardEntrypoint>(new EndpointRoute("/dashboard"));
+        // embeds the login/bump action references. Routes come from each page's
+        // <pstm-uri>; actions are auto-mapped to GUID endpoints; the Statum
+        // client scripts are embedded by default.
+        statum.add_page<HomePage, HomeEntrypoint>();
+        statum.add_page<DashboardPage, DashboardEntrypoint>();
         statum.action<LoginAction>();
         statum.action<LogoutAction>();
         statum.action<BumpCounterAction>();

+ 36 - 21
src/Statum.vala

@@ -3,6 +3,11 @@ using Astralis;
 
 namespace Statum {
 
+    /** Configuration-time errors (e.g. a page registered without a route). */
+    public errordomain StatumError {
+        CONFIGURATION,
+    }
+
     /**
      * Inversion module that registers the Statum runtime services and framework
      * endpoints.
@@ -66,19 +71,16 @@ namespace Statum {
     /**
      * Binds application pages, entrypoints and resources into the container.
      *
-     * Usage mirrors Spry's {@link Spry.SpryConfigurator}:
-     *
      * ```
      * var statum = application.configure_with<StatumConfigurator>();
-     * statum.add_entrypoint_page<HomePage, HomeEntrypoint>(new EndpointRoute("/"));
-     * statum.add_page<AboutPage>(new EndpointRoute("/about"));
+     * statum.add_page<HomePage, HomeEntrypoint>();
+     * statum.add_static_page<AboutPage>();
      * statum.add_resource<LogoResource>();
      * ```
      *
-     * {@link add_entrypoint_page} registers the generated {@link StatumPage}
-     * (served at its URI) and adds the entrypoint to the {@link EntrypointRouteTable}
-     * so `/_statum/entrypoint?uri=…` dispatches to it. The page route is supplied
-     * explicitly (mirroring Spry) rather than read reflectively from the class.
+     * Each page's route is read from its generated {@link StatumPage.route}
+     * (set at build time from its `<pstm-uri>`); no route is passed by hand. A
+     * page without a `<pstm-uri>` (empty route) fails at startup.
      */
     public class StatumConfigurator : Object {
 
@@ -87,28 +89,41 @@ namespace Statum {
         private ActionRegistry action_registry = inject<ActionRegistry>();
 
         /**
-         * Registers a static page (no entrypoint) served at `route`.
+         * Resolves a page's route from its {@link StatumPage.route} property,
+         * throwing at startup when the page has no `<pstm-uri>`.
          */
-        public void add_page<TPage>(EndpointRoute route) {
-            container.register_scoped<TPage>()
-                .as<Endpoint>()
-                .with_metadata<EndpointRoute>(route);
+        private static EndpointRoute route_for<TPage>() throws Error {
+            var page = (StatumPage) Object.new(typeof(TPage));
+            var path = page.route;
+            if (path == null || path.length == 0) {
+                throw new StatumError.CONFIGURATION(@"Page %s has no <pstm-uri> (route is empty)", typeof(TPage).name());
+            }
+            return new EndpointRoute(path);
         }
 
         /**
-         * Registers a page served at `route` AND binds `TEntrypoint` to that route
-         * in the entrypoint table (so `/_statum/entrypoint?uri=route` dispatches to
-         * it).
-         *
-         * (Named distinctly from {@link add_page} because Vala does not permit
-         * methods that overload on generic arity.)
+         * Registers a page AND its entrypoint. The page is served at its
+         * `<pstm-uri>` route, and the entrypoint is bound to that route in the
+         * {@link EntrypointRouteTable} so `/_statum/entrypoint?uri=…` dispatches
+         * to it.
          */
-        public void add_entrypoint_page<TPage, TEntrypoint>(EndpointRoute route) {
-            add_page<TPage>(route);
+        public void add_page<TPage, TEntrypoint>() throws Error {
+            var route = route_for<TPage>();
+            container.register_scoped<TPage>()
+                .as<Endpoint>()
+                .with_metadata<EndpointRoute>(route);
             container.register_transient<TEntrypoint>();
             route_table.register(route, typeof(TEntrypoint));
         }
 
+        /** Registers a static page (no entrypoint) served at its `<pstm-uri>` route. */
+        public void add_static_page<TPage>() throws Error {
+            var route = route_for<TPage>();
+            container.register_scoped<TPage>()
+                .as<Endpoint>()
+                .with_metadata<EndpointRoute>(route);
+        }
+
         /** Registers a {@link StatumResource} for serving from `/_statum/resource/{name}`. */
         public void add_resource<T>() {
             container.register_startup<T>()

+ 1 - 1
src/StatumHandlers.vala

@@ -10,7 +10,7 @@ namespace Statum {
      *
      * After a page is served its HTML boots the Statum JS, which GETs
      * `/_statum/entrypoint?uri=…`. The framework resolves the entrypoint bound
-     * to that URI (see {@link StatumConfigurator.add_entrypoint_page}) and calls
+     * to that URI (see {@link StatumConfigurator.add_page}) and calls
      * {@link handle}, whose returned {@link DirectiveBuilder} directives hydrate
      * the page.
      *

+ 7 - 0
src/StatumPage.vala

@@ -22,6 +22,13 @@ namespace Statum {
         /** Content type of the served page (normally `text/html`). */
         public abstract string content_type { get; }
 
+        /**
+         * The page's route (from its `<pstm-uri>`), used by
+         * {@link StatumConfigurator} to register the page's endpoint. Must be
+         * non-empty; a page without a `<pstm-uri>` fails at startup.
+         */
+        public abstract string route { get; }
+
         /** Best supported encoding from the encodings this page carries. */
         public abstract string get_best_encoding(Set<string> supported);
 

+ 6 - 36
tools/statum-mkpstm/statum-mkpstm.vala

@@ -10,12 +10,10 @@ namespace Statum.Tools {
      * `statum-mkpstm` — the build-time Statum pre-rendering pipeline.
      *
      * Turns an app HTML page (plus its templates/fragments) into a Vala
-     * {@link Statum.StatumPage} subclass carrying precompressed variant byte
-     * arrays and a compiled {@link Statum.StatumPage.select_variant} that
-     * evaluates `pstm-guard`s and the `pstm-if` matrix against the request's held
-     * slots using the Invercargill runtime expression evaluator.
-     *
-     * See `phase-1-plan.md` Subsystem B for the supported `pstm-*` directives.
+     * {@link Statum.StatumPage} subclass: a static, precompressed document whose
+     * route comes from its `<pstm-uri>`. Composition (templates, fragments,
+     * `pstm-res` rewriting) is applied, then the result is precompressed with
+     * identity/gzip/zstd/br.
      */
     public class Mkpstm : Object {
 
@@ -88,11 +86,10 @@ namespace Statum.Tools {
             doc = compose_templates(doc);
             inline_fragments(doc);
             rewrite_resources(doc);
-            validate_no_conditionals(doc, page_file);
 
             var variant = build_variant(doc);
             var class_name = class_name_override ?? (make_pascal_case(page_file) + "Page");
-            var route = route_override ?? (routes.length > 0 ? routes[0] : "/");
+            var route = route_override ?? (routes.length > 0 ? routes[0] : "");
 
             return emit(class_name, route, variant);
         }
@@ -499,33 +496,6 @@ namespace Statum.Tools {
             }
         }
 
-        /**
-         * Errors if any removed server-side conditional (`pstm-if`/`pstm-else-if`/
-         * `pstm-else`) is still present; pages are static now, so conditionals
-         * are client-side `stm-if` only.
-         */
-        private void validate_no_conditionals(Xml.Doc* doc, string source) throws GLib.Error {
-            var root = doc->get_root_element();
-            if (root != null) {
-                validate_no_conditionals_from(root, source);
-            }
-        }
-
-        private void validate_no_conditionals_from(Xml.Node* node, string source) throws GLib.Error {
-            if (!is_element(node)) {
-                return;
-            }
-            string[] names = { "pstm-if", "pstm-else-if", "pstm-else" };
-            foreach (var name in names) {
-                if (node->get_prop(name) != null) {
-                    throw new IOError.FAILED(@"\"$source\": the \"$name\" directive is no longer supported; use the client-side stm-if instead");
-                }
-            }
-            for (var child = node->children; child != null; child = child->next) {
-                validate_no_conditionals_from(child, source);
-            }
-        }
-
         // ------------------------------------------------------------------
         // Code generation
         // ------------------------------------------------------------------
@@ -550,7 +520,7 @@ namespace Statum.Tools {
             b.append_printf("%s// Generated by statum-mkpstm\n", i1);
             b.append_printf("%spublic class %s : StatumPage {\n\n", i1, class_name);
 
-            b.append_printf("%spublic const string ROUTE = \"%s\";\n", i2, escape(route));
+            b.append_printf("%spublic override string route { get { return \"%s\"; } }\n", i2, escape(route));
             b.append_printf("%spublic override string content_type { get { return \"text/html; charset=utf-8\"; } }\n\n", i2);
 
             // get_best_encoding: smallest available supported encoding.