Class: Kobako::Catalog::Snippets
- Inherits:
-
Object
- Object
- Kobako::Catalog::Snippets
- Defined in:
- lib/kobako/catalog/snippets.rb,
sig/kobako/catalog/snippets.rbs
Overview
Kobako::Catalog::Snippets — per-Sandbox insertion-ordered registry of preloaded snippets.
Entries replay against the fresh mrb_state before per-invocation
source / entrypoint resolution. Each Snippet::Source entry's name
is its canonical identity — the filename baked into the loaded IREP's
debug_info that surfaces in every backtrace frame originating from
the snippet as (snippet:Name):line. Duplicate names within the
code: form would produce ambiguous attribution and are rejected at
registration time.
Snippet::Binary entries carry no host-side name — their canonical
name lives in the bytecode's debug_info and is read by the guest at
load time; the host does not extract it.
Sealing is governed by the owning Sandbox — the registry itself
is append-only and exposes no mutation API beyond #register; the
Sandbox guards #register behind the seal check before delegating.
Constant Summary collapse
- NAME_PATTERN =
Ruby constant-name pattern enforced on snippet names.
/\A[A-Z]\w*\z/
Instance Method Summary collapse
-
#ensure_source_args!(code, name) ⇒ [String, Symbol | String]
Shape-only validation for the
code:+name:pair. -
#entries ⇒ Array[tuple]
The registered snippets in insertion order, each projected as the
[kind, name, body]tripleRuntime#eval/#runframe into Frame 3 —kindnames the form as a Symbol,nameisnilfor the bytecode form. -
#entry_tuple(entry) ⇒ tuple
Project one entry as its
[kind, name, body]triple. -
#initialize ⇒ Snippets
constructor
A new instance of Snippets.
- #normalize_name(name) ⇒ Symbol
-
#register(code: nil, name: nil, binary: nil) ⇒ Symbol?
Register one preloaded snippet in either of two forms.
-
#register_binary!(bytes) ⇒ nil
Binary-form register path.
-
#register_source!(code, name) ⇒ Symbol
Source-form register path.
Constructor Details
#initialize ⇒ Snippets
Returns a new instance of Snippets.
28 29 30 |
# File 'lib/kobako/catalog/snippets.rb', line 28 def initialize @entries = [] # : Array[Kobako::Snippet::Source | Kobako::Snippet::Binary] end |
Instance Method Details
#ensure_source_args!(code, name) ⇒ [String, Symbol | String]
Shape-only validation for the code: + name: pair. Returns
the pair with nil narrowed away so callers can treat both as
present. The code: type check runs before the name:
presence check so callers passing code: nil explicitly see
the type error rather than the "missing keyword" error.
92 93 94 95 96 97 98 |
# File 'lib/kobako/catalog/snippets.rb', line 92 def ensure_source_args!(code, name) raise ArgumentError, "missing keyword: code: + name:, or binary:" if code.nil? && name.nil? raise ArgumentError, "code must be a String, got #{code.class}" unless code.is_a?(String) raise ArgumentError, "missing keyword: name:" if name.nil? [code, name] end |
#entries ⇒ Array[tuple]
The registered snippets in insertion order, each projected as the
[kind, name, body] triple Runtime#eval / #run frame into
Frame 3 — kind names the form as a Symbol, name is nil for
the bytecode form. The entry value objects stay pure carriers, so
this collection-tier method reads their attributes externally
rather than asking each entry to project itself.
38 39 40 |
# File 'lib/kobako/catalog/snippets.rb', line 38 def entries @entries.map { |entry| entry_tuple(entry) } end |
#entry_tuple(entry) ⇒ tuple
Project one entry as its [kind, name, body] triple. Source
entries contribute their host-side name; Binary entries carry
nil because the canonical name lives in the bytecode's embedded
debug_info and is read by the guest at load time.
114 115 116 117 118 119 120 121 |
# File 'lib/kobako/catalog/snippets.rb', line 114 def entry_tuple(entry) case entry when Snippet::Source [Snippet::Source::KIND, entry.name.to_s, entry.body] else [Snippet::Binary::KIND, nil, entry.body] end end |
#normalize_name(name) ⇒ Symbol
123 124 125 126 127 128 129 130 131 132 133 134 135 |
# File 'lib/kobako/catalog/snippets.rb', line 123 def normalize_name(name) unless name.is_a?(Symbol) || name.is_a?(String) raise ArgumentError, "snippet name must be a Symbol or String, got #{name.class}" end name_str = name.to_s unless NAME_PATTERN.match?(name_str) raise ArgumentError, "snippet name must match #{NAME_PATTERN.inspect} (got #{name.inspect})" end name_str.to_sym end |
#register(code: nil, name: nil, binary: nil) ⇒ Symbol?
Register one preloaded snippet in either of two forms.
* Source form +register(code: src, name: Name)+ — +src+ is the
mruby source as a String; the bytes are re-encoded as UTF-8
and detached from the caller's reference. +name+ is a Symbol
or String matching +NAME_PATTERN+. Returns the Symbol form
of +name+.
* Binary form +register(binary: bytes)+ — +bytes+ is
precompiled RITE bytecode as a String, duplicated and forced
to ASCII-8BIT so msgpack-ruby ships it as +bin+. Returns
+nil+ — bytecode entries are anonymous on the host side; any
structural validation is deferred to the guest at first replay.
The two forms are mutually exclusive: shape validation lives
here so callers (chiefly Kobako::Sandbox#preload) collapse to
a single delegation. Raises ArgumentError on mixed forms,
missing keywords, wrong types, malformed name, or
duplicate code: name.
60 61 62 63 64 65 66 67 68 |
# File 'lib/kobako/catalog/snippets.rb', line 60 def register(code: nil, name: nil, binary: nil) if binary raise ArgumentError, "cannot combine binary: with code: / name:" if code || name register_binary!(binary) else register_source!(code, name) end end |
#register_binary!(bytes) ⇒ nil
Binary-form register path. Validates the binary: payload type
and appends the Binary entry. The bytes are duplicated and forced
to ASCII-8BIT so msgpack-ruby picks the bin family on the wire.
103 104 105 106 107 108 |
# File 'lib/kobako/catalog/snippets.rb', line 103 def register_binary!(bytes) raise ArgumentError, "binary must be a String, got #{bytes.class}" unless bytes.is_a?(String) @entries << Snippet::Binary.new(body: bytes.dup.force_encoding(Encoding::ASCII_8BIT)) nil end |
#register_source!(code, name) ⇒ Symbol
Source-form register path. Delegates argument-shape checks to
ensure_source_args! (which returns the narrowed [code, name]
pair), normalises name to a Symbol, rejects duplicates,
and appends the Source entry.
76 77 78 79 80 81 82 83 84 85 |
# File 'lib/kobako/catalog/snippets.rb', line 76 def register_source!(code, name) code, name = ensure_source_args!(code, name) name_sym = normalize_name(name) if @entries.any? { |e| e.is_a?(Snippet::Source) && e.name == name_sym } raise ArgumentError, "snippet #{name_sym.inspect} already preloaded" end @entries << Snippet::Source.new(name: name_sym, body: code.dup.force_encoding(Encoding::UTF_8)) name_sym end |