Class: Kobako::Catalog::Namespaces
- Inherits:
-
Object
- Object
- Kobako::Catalog::Namespaces
- Defined in:
- lib/kobako/catalog/namespaces.rb,
sig/kobako/catalog/namespaces.rbs
Overview
Kobako::Catalog::Namespaces — per-Sandbox registry of
Kobako::Namespace entities. Holds the Namespace / Member bindings
and the preamble emitted on Frame 1.
Public API:
namespaces = Kobako::Catalog::Namespaces.new
namespace = namespaces.define(:MyService) # => Kobako::Namespace
namespace.bind(:KV, kv_object) # => namespace (chainable)
namespaces.encode # => msgpack bytes for Frame 1
namespaces.lookup("MyService::KV") # => kv_object
Namespaces live at Kobako::Namespace. Per-dispatch routing is
+Kobako::Transport::Dispatcher+'s responsibility — the Dispatcher
receives this registry and the Catalog::Handles as arguments from
the Runtime#on_dispatch Proc that Kobako::Sandbox#initialize
installs. The registry holds an injected Catalog::Handles reference so
dispatch target resolution and host→guest auto-wrap share the same
Sandbox-owned allocator.
Instance Method Summary collapse
-
#define(name) ⇒ Kobako::Namespace
Declare or retrieve the Namespace named
name(idempotent). -
#encode ⇒ String
Encode the preamble as msgpack bytes for stdin Frame 1 delivery.
-
#initialize(handler: Catalog::Handles.new) ⇒ Namespaces
constructor
Build a fresh registry.
-
#lookup(target) ⇒ Object
Resolve a
targetpath of the form"<Namespace>::<Member>"(e.g."MyService::KV") to the bound Host object. -
#seal! ⇒ self
Mark the registry as sealed and propagate the seal to every declared
Kobako::Namespace. -
#sealed? ⇒ Boolean
Returns
truewhen #seal! has been called,falseotherwise.
Constructor Details
#initialize(handler: Catalog::Handles.new) ⇒ Namespaces
Build a fresh registry. handler is an internal seam that injects
a pre-configured Catalog::Handles; tests pass one whose next_id
is pinned near MAX_ID to exercise the cap-exhaustion path
without 2³¹ allocations. Production callers leave it at the default.
34 35 36 37 38 39 |
# File 'lib/kobako/catalog/namespaces.rb', line 34 def initialize(handler: Catalog::Handles.new) @namespaces = {} # : Hash[String, Kobako::Namespace] @handler = handler @sealed = false @encoded = nil # : String? end |
Instance Method Details
#define(name) ⇒ Kobako::Namespace
Declare or retrieve the Namespace named name (idempotent).
name is a constant-form name as a Symbol or String (must satisfy
Namespace::NAME_PATTERN). Returns the Kobako::Namespace for that
name, creating it if it does not exist. Raises ArgumentError when
name is malformed, or when called after the owning Sandbox has been
sealed by its first invocation.
47 48 49 50 51 52 53 54 55 56 57 |
# File 'lib/kobako/catalog/namespaces.rb', line 47 def define(name) raise ArgumentError, "cannot define after first Sandbox invocation" if @sealed name_str = name.to_s unless Namespace::NAME_PATTERN.match?(name_str) raise ArgumentError, "Namespace name must match #{Namespace::NAME_PATTERN.inspect} (got #{name.inspect})" end @namespaces[name_str] ||= Namespace.new(name_str) end |
#encode ⇒ String
Encode the preamble as msgpack bytes for stdin Frame 1 delivery.
Routes through Kobako::Codec::Encoder like every other host-side
wire encode so there is a single codec path; the preamble carries
only Strings and Arrays, so none of the kobako ext types actually
fire. Structure: [["Namespace", ["MemberA", "MemberB"]], ...].
Returns a binary String of msgpack bytes.
Once sealed, the bytes are computed once and reused for every
subsequent invocation: sealing freezes Service registration at the
first invocation, so the preamble is exactly the bindings that
existed at that moment — a bind reaching a Kobako::Namespace
after the seal raises ArgumentError and never alters Frame 1.
The memo is gated on the seal rather than dropped per mutation (the
Catalog::Snippets#encode approach) because a Member bind lands
on a child Kobako::Namespace, invisible to this collection; only
the seal guarantees nothing further can change.
89 90 91 92 93 94 95 |
# File 'lib/kobako/catalog/namespaces.rb', line 89 def encode return @encoded if @encoded bytes = Codec::Encoder.encode(@namespaces.values.map(&:to_preamble)).freeze @encoded = bytes if @sealed bytes end |
#lookup(target) ⇒ Object
Resolve a target path of the form "<Namespace>::<Member>"
(e.g. "MyService::KV") to the bound Host object. target is a
two-level path using the :: separator. Returns the bound Host
object. Raises KeyError when the namespace or the member is not
bound.
64 65 66 67 68 69 70 71 |
# File 'lib/kobako/catalog/namespaces.rb', line 64 def lookup(target) namespace_name, member_name = target.to_s.split("::", 2) namespace = @namespaces[namespace_name] raise KeyError, "no namespace named #{namespace_name.inspect}" if namespace.nil? raise KeyError, "no member in target #{target.inspect}" unless member_name namespace.fetch(member_name) end |
#seal! ⇒ self
Mark the registry as sealed and propagate the seal to every
declared Kobako::Namespace. Called by Sandbox on the first
invocation. After sealing, both #define and Namespace#bind
raise ArgumentError. Idempotent.
101 102 103 104 105 106 107 |
# File 'lib/kobako/catalog/namespaces.rb', line 101 def seal! return self if @sealed @sealed = true @namespaces.each_value(&:seal!) self end |
#sealed? ⇒ Boolean
Returns true when #seal! has been called, false otherwise.
110 111 112 |
# File 'lib/kobako/catalog/namespaces.rb', line 110 def sealed? @sealed end |