Codegen meta-model — paste blocks

Table of Contents

This page is the concept note for paste blocks, applicable to any metaclass's segments, not only Entity's. It is ORE Studio's extension to the MASD logical model: Physical Space already has a notion of protected regions (see § "Protected regions" below), but as a mechanism belonging to physical space, scanned from generated output. ORE Studio instead models a paste block as a field on the logical metaentity itself — a segment composes zero or more PasteBlock instances, each with its own kind ID, the same way it composes any other typed field:

codegen_meta_model_paste_blocks.png

This page explains the mechanism once; every segment page that has concrete block kinds (SQL, Repository, Service, Protocol, NATS, Qt, Generator) links back here instead of re-explaining it, and hosts its own block kinds inline rather than in one central registry — a Repository paste block belongs on the Repository page, not in a separate index, because you need it while reading about the repository facet, not some other time.

Physical model mapping

Paste blocks are not a segment themselves — this page is a concept note, not a hub-and-spokes segment page — so there is no facet of its own to map to here. A paste block exists within a segment (see each segment's own structural diagram, e.g. SQL's SqlSchemaExtraDropStatements), and it is that owning segment's facet the content ultimately projects into. See "Finding a block kind" below for which segment page (and therefore which facet) hosts each concrete block kind.

Terminology: this is a protected region

"Paste block" is not a MASD concept — it is ORE Studio's project-specific name for an instance of the formally-named MDE mechanism, the Physical Space § "Protected regions", the same naming mismatch already flagged for stereotype metatype (see Applied MASD's note on that). Read that section first for the general mechanism, the tool-naming survey across XPand/Acceleo/EGL/JET MOFScript, and the formal comparison this is drawn from (Greifenberg et al. 2015).

One thing worth being precise about: ORE Studio's paste blocks are not the classic protected-region implementation described there. The classic mechanism is a round-trip one — the generator scans the previous generated output for marker comments, extracts whatever handwritten content it finds inside them, and reinserts it into the newly generated file; this is exactly the mechanism whose known drawback is an unstable/non-unique marker ID silently losing content, because the source of truth for the handwritten content is the generated file itself. ORE Studio's paste blocks invert this: the handwritten content lives in the entity model (the .org file), never in generated output, and codegen does a one-directional, stateless substitution — render the template, then regex-replace <<paste:UUID>> markers with content read straight from the model. There is no "scan the old output" step and nothing to lose on regeneration, because generated output is never the source of truth for anything. Whether this project-specific variant deserves its own name distinct from "protected region," or whether "paste block" should just be retired in favour of a MASD-aligned term for it, is an open question — not decided on this page.

Why the mechanism exists

Most of a generated file is fully templated: given the entity's fields and knobs, the output is fully determined. But some code is genuinely entity-specific and cannot be derived from the model — a bespoke query method, a custom NATS message type, a hand-written constructor initialiser. Rather than making that entity's file partially hand-written (losing regeneration safety for the whole file), codegen defines fixed slots inside its templates that an entity model may fill with verbatim code. The generated file stays 100% regenerable; only the slot's content is entity-authored.

The two levels of identity

A block kind is the slot itself — a concept, like an interface. It has a stable UUID, declared once by whichever segment page owns it, and referenced by that facet's templates via a <<paste:UUID>> marker.

A block instance is one entity's concrete filling of that slot — like a class implementing an interface. Its own :ID: is per-entity, per-block; many entities may implement the same kind, each with a different instance UUID for their own copy.

This split means templates and entity files never need to coordinate UUID changes: a template references a fixed kind UUID forever; each entity file independently declares what it implements.

How an entity attaches a block

Inside the entity model, a sub-heading under the relevant section (e.g. ** Custom service methods under * C++) carries a babel block whose :implements: header argument names the kind UUID:

** Custom service methods

#+begin_src cpp :name declaration :implements 2D4EA0F9-6AF6-45A5-A959-F672BF866C6A
,    std::vector<domain::country> list_countries_for_party(
,        const boost::uuids::uuid& party_id,
,        std::uint32_t offset, std::uint32_t limit);
#+end_src

The babel block's :name (declaration above) is for human/Emacs navigation only — :implements is what codegen actually reads.

How the marker resolves

Codegen is a post-rendering substitution pass: mustache renders the whole template first, using the entity's ordinary fields and knobs; then a regex pass replaces every <<paste:UUID>> marker with the concatenated bodies of every sub-heading in the current entity whose :implements: matches that UUID. If no sub-heading implements a given kind, the marker is replaced with an empty string (and a single trailing newline collapses), so an entity that needs none of a facet's paste blocks produces exactly the same output as if the markers weren't there.

The well-known babel block name inside an implementing sub-heading is body for a single free-form insertion. Some kinds instead expect declaration=/=implementation pairs (a header slot and a matching .cpp slot for the same custom method) — each segment page states which name(s) its kinds expect.

Finding a block kind

Concrete block kinds are documented on the segment page for the facet whose generated file they inject into:

  • SQL — extra DROP statements.
  • C++ Repository — extra member declarations, out-of-class implementations, .cpp includes.
  • C++ Service — extra includes, custom public methods, extra private members, constructor init, .cpp implementations.
  • C++ Protocol — custom message includes and types.
  • C++ NATS — handler early-exit override, extra includes, custom methods, extra private members.
  • C++ Qt — detail-dialog hierarchy tree seam, private members, header includes, composite child-entity tables seam, entity-set load hook.
  • C++ Generator — custom declarations and implementations.

See also

Emacs 29.3 (Org mode 9.6.15)