fn nodecontext_to_rb_hash(
    ctx: &{{ context_type_path }},
) -> magnus::RHash {
    let ruby = unsafe { magnus::Ruby::get_unchecked() };
    let h = ruby.hash_new();
{{ context_field_lines }}
    h
}

pub struct {{ struct_name }} {
    rb_obj: magnus::Value,
}

impl std::fmt::Debug for {{ struct_name }} {
    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
        write!(f, "{{ struct_name }}")
    }
}

impl {{ struct_name }} {
    pub fn new(rb_obj: magnus::Value) -> Self {
        Self { rb_obj }
    }
}

// SAFETY: `magnus::Value` wraps a `VALUE` (a GC-managed Ruby object pointer).
// Ruby is effectively single-threaded (GIL/GVL); the bridge is only invoked
// synchronously from the Ruby thread that constructed it. The `Arc<Mutex>`
// outer wrapper ensures exclusive access. Sending the bridge across threads
// would cause undefined behaviour in the Ruby runtime — callers must not do so.
unsafe impl Send for {{ struct_name }} {}
// SAFETY: see Send impl above.
unsafe impl Sync for {{ struct_name }} {}

impl {{ trait_path }} for {{ struct_name }} {
{%- for method in methods %}
{{ method }}
{%- endfor %}
}
