Paste blocks: injecting custom code into generated files

Table of Contents

Summary

Codegen overwrites every generated file on each run. Paste blocks are the mechanism for injecting hand-written C++ into those files in a way that survives regeneration. Each injection point in a template is identified by a UUID marker (<<paste:UUID>>); any Babel block in the entity org model that carries a matching :implements UUID header argument contributes its body to that marker. The pattern covers declarations (.hpp), implementations (.cpp), and extra .cpp includes.

Detail

How it works

During code generation core.py collects all Babel blocks in the entity org model that carry an :implements <UUID> header argument. After rendering the Mustache template it replaces each <<paste:UUID>> marker with the concatenated bodies of all matching blocks.

The Babel block :name tag is for human navigation only. The :implements tag is what codegen acts on.

Repository injection points

The following paste UUIDs are defined for the repository profile. Their canonical IDs live in org entity meta model.

UUID Where injected Template
DCA78C69-E508-48D9-9972-A9B8094D91FB Inside the repository class, before the closing } ores.cpp.repository.repository_header
F2EB1914-5E94-42CA-9C77-46BDB364BF9E Near the end of _repository.cpp, before } of namespace ores.cpp.repository.repository_impl
6141050C-0ED4-4680-B387-7DDDA3A69806 Top of _repository.cpp, after the generated includes ores.cpp.repository.repository_impl

Qt detail dialog injection points

The following paste UUIDs are defined for the Qt detail dialog profile, for entity-specific dialog logic that doesn't fit the generic is_locked_after_create=/=locked_fields-style declarative flags (e.g. a derived field computed live from two others, or a field whose availability depends on another field's value at runtime). Their canonical IDs live in org entity meta model.

UUID Where injected Template
3F8B6C1D-4E2A-4F9B-8C3D-1A5E7F2B9D4C Top of _DetailDialog.cpp, after the generated includes ores.cpp.qt.detail_dialog_impl
F55CB64E-165A-4E15-A50A-5723C0320E97 Inside the class, in private:, after the generated declarations ores.cpp.qt.detail_dialog_header
EC97923E-BC8D-4A50-9970-CBC5CBD4B732 Near the end of _DetailDialog.cpp, after the generated methods ores.cpp.qt.detail_dialog_impl
B6F2748A-9896-4B9C-BEAC-38436FD1CA79 End of setupConnections(), before its closing } ores.cpp.qt.detail_dialog_impl
9A719585-3AB1-47DE-B0B0-99AFDFE7180E End of setCreateMode(), after the generated locked_fields loop ores.cpp.qt.detail_dialog_impl
7A4AE8D5-A22D-4912-B4BF-D58215681CD2 End of setReadOnly(), after the generated is_locked_after_create loop ores.cpp.qt.detail_dialog_impl
69F22F14-6701-4E73-AFCC-78AC1B1FDDDF Inside validateInput()'s return chain, before the closing ; ores.cpp.qt.detail_dialog_impl
4E83FA7A-742A-4102-90AF-D337F6FB1269 Inside onSaveClicked(), after validateInput() passes, before the save dispatch ores.cpp.qt.detail_dialog_impl
F8EC96B9-04BA-46A1-A2D8-4E4B91DCB435 Inside onSaveClicked()'s async watcher, right after the entity save succeeds ores.cpp.qt.detail_dialog_impl
67D24D2F-2D98-49EB-9A1D-32F1D8BFA76A In signals:, after the generated signals ores.cpp.qt.mdi_window_header
18B9ED63-78C2-4259-93AC-CCDFBC88EFBD End of setupToolbar(), after the generated History action ores.cpp.qt.mdi_window_impl
66E078FB-D6A6-4DF6-814B-65995D948090 In signals:, after the generated signals ores.cpp.qt.controller_header
52E70F3B-5903-4359-91FB-B533054A18AC End of showListWindow(), after the generated signal connections ores.cpp.qt.controller_impl
EC19A4EE-363D-4D41-A33A-D5F85A139C80 In the dialog's signals:, after the generated signals ores.cpp.qt.detail_dialog_header
DF72DFCF-FD65-4FE5-98BA-CB8168CE0FDF End of wireDetailDialogCommon(), after the generated connections ores.cpp.qt.controller_impl

The create-mode and read-only seams run last, after the generated per-field lock logic — a paste block there can override what the generated code just did (e.g. replace a setEnabled(false)-based lock with WidgetUtils::set_combo_locked() for a field that should stay visually normal, flag icon included, while locked).

The mdi_window/controller header+impl pairs follow the same shape as the repository profile's declaration/implementation pairing above: an entity declares a signal via the header seam, then wires it via the matching impl seam — e.g. a cross-navigation button on one entity's list window that opens a related entity's list window.

Recipe: adding a custom repository method

Given an entity org model, add a heading under ** Repository with a unique :ID:, then attach three Babel blocks:

#+begin_src org

read_latest_all_tenants

Reads the latest records across all tenants (no tenant_id filter).

std::vector<domain::feed_binding> read_latest_all_tenants(context ctx);
#include <string>
std::vector<domain::feed_binding>
feed_binding_repository::read_latest_all_tenants(context ctx) {
    ...
}

#+end_src

After editing the org model run:

./compass.sh codegen entity generate <entity>

The generated .hpp will contain the declaration and the generated .cpp will contain the implementation — both survive all future regen runs.

Considerations

  • The impl_includes block can be omitted when no extra includes are needed; codegen silently skips absent markers.
  • Multiple paste blocks with the same :implements UUID are concatenated in document order.
  • read_latest_all_tenants is a good candidate for promotion to a generated optional controlled by a flag in the entity org model (e.g. #+has_cross_tenant_read: true), since the pattern is identical across entities. Tracked as a future codegen improvement.

See also

Emacs 29.3 (Org mode 9.6.15)