Keyboard shortcuts

Press or to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

Property Ownership – A Clean Routing Pattern

In the previous chapter, we formalized the HostAPI contract.

We saw that:

  • Plugins speak in shared keys.
  • Hosts decide where those keys live.
  • Routing happens through get() and draw().

Earlier, we implemented routing using:

if key == "project.audio_path":
    ...

That works for one key.

It does not scale.

This chapter introduces a clean, declarative routing pattern.


The Problem with if Chains

Suppose audio_encode defines three shared keys:

shared={
    "audio_path": "project.audio_path",
    "bpm": "project.bpm",
    "output_dir": "project.output_dir",
}

Writing:

if key == ...
elif key == ...
elif key == ...

Becomes repetitive and fragile.

As the number of shared keys grows, branching logic becomes harder to reason about and maintain.

We want:

  • Explicit mapping
  • Easy extension
  • No branching logic explosion
  • Clear ownership declaration

Step 1 – Define a KEYMAP

Inside audiodeck/__init__.py:

KEYMAP = {
    "project.audio_path": ("audiodeck_props", "audio_path"),
    "project.bpm": ("audiodeck_props", "bpm"),
    "project.output_dir": ("audiodeck_props", "output_dir"),
}

Each entry maps:

shared_key → (property_group_name, attribute_name)

This makes ownership explicit and declarative.

The host now clearly states:

“These shared keys belong to these properties.”


Step 2 – Refactor HostAPI to Use KEYMAP

Replace the earlier implementation with:

class AudioDeckHostAPI:

    def get(self, context, key, fallback_prop):
        if key in KEYMAP:
            group_name, attr = KEYMAP[key]
            group = getattr(context.scene, group_name)
            return getattr(group, attr)

        return getattr(context.scene.qa_shared, fallback_prop)

    def draw(self, layout, context, key, fallback_prop, label=None):
        if key in KEYMAP:
            group_name, attr = KEYMAP[key]
            group = getattr(context.scene, group_name)
            layout.prop(group, attr, text=label)
        else:
            layout.prop(
                context.scene.qa_shared,
                fallback_prop,
                text=label
            )

Routing is now centralized.

No condition chains. No duplication. No hidden logic.


Adding a New Shared Key

To route a new key, modify only the KEYMAP:

KEYMAP["project.sample_rate"] = ("audiodeck_props", "sample_rate")

No other code changes are required.

Growth becomes mechanical.


Fallback Still Works

If a shared key is not listed in KEYMAP:

  • It automatically uses fallback storage.
  • The plugin continues to function.
  • Ownership transfer can be incremental.

This makes adoption safe and predictable.


Discipline Guidelines

To keep routing reliable:

  • Shared keys must match exactly.
  • Host property types must match plugin expectations.
  • Avoid transforming values inside HostAPI.
  • Keep KEYMAP static and explicit.

HostAPI is a routing layer. It should remain simple.


What You Now Have

At this point:

  • UI ownership belongs to audiodeck.
  • Data ownership belongs to audiodeck.
  • Routing is scalable.
  • Fallback behavior is preserved.
  • Plugins remain reusable.

In the next chapter, we extend this model to multiple embedded plugins.