Module: TRMNLP::TransformBackend::Wrapper

Defined in:
lib/trmnlp/transform_backend/wrapper.rb

Overview

Code wrappers shared by Subprocess and Http backends. Each method emits the canonical dispatch harness — read stdin → run user code → find run/transform/result/passthrough → serialize output — and delegates the final write to a language-appropriate output_sink snippet supplied by the caller. Subprocess writes to a tempfile path, Http writes to FD 3 for the production daemon to capture.

Mirrors the hosted serverless runtime's code-wrapping behavior, parameterized on the per-backend output sink. PHP additionally carries a leading <?php tag the file-based Subprocess path needs; the daemon tolerates it, so parity holds.

NOTE: output_sink is spliced verbatim into the generated script as executable code. It MUST be trmnlp-generated (see Subprocess#sink_for and Http#sink_for) and never derived from user input or config — an attacker-influenced sink is arbitrary code execution in the transform process. Only code is untrusted; the sink is part of the harness.

Class Method Summary collapse

Class Method Details

.for(language, code, output_sink) ⇒ Object



105
106
107
108
109
110
111
112
# File 'lib/trmnlp/transform_backend/wrapper.rb', line 105

def for(language, code, output_sink)
  case language.to_s
  when 'python' then python(code, output_sink)
  when 'ruby'   then ruby(code, output_sink)
  when 'node'   then node(code, output_sink)
  when 'php'    then php(code, output_sink)
  end
end

.node(code, output_sink) ⇒ Object

NOTE: node also accepts function transform(input) for production parity. Plugins authored against the hosted service using transform would otherwise silently pass input through.



63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
# File 'lib/trmnlp/transform_backend/wrapper.rb', line 63

def node(code, output_sink)
  <<~JS
    const input = JSON.parse(require('fs').readFileSync(0, 'utf8'));

    #{code}

    let output;
    if (typeof run === "function") {
      output = run(input);
    } else if (typeof transform === "function") {
      output = transform(input);
    } else if (typeof result !== "undefined") {
      output = result;
    } else {
      output = input;
    }
    #{output_sink}
  JS
end

.php(code, output_sink) ⇒ Object

NOTE: strips a leading <?php tag from user code so plugin authors can write the file as a standalone .php script. The hosted service does the same.



86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
# File 'lib/trmnlp/transform_backend/wrapper.rb', line 86

def php(code, output_sink)
  cleaned = code.sub(/\A\s*<\?php\s*/, '')
  <<~PHP
    <?php
    $input = json_decode(file_get_contents('php://stdin'), true);

    #{cleaned}

    if (function_exists('run')) {
        $output = run($input);
    } elseif (isset($result)) {
        $output = $result;
    } else {
        $output = $input;
    }
    #{output_sink}
  PHP
end

.python(code, output_sink) ⇒ Object



25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
# File 'lib/trmnlp/transform_backend/wrapper.rb', line 25

def python(code, output_sink)
  <<~PYTHON
    import sys, json, os
    input = json.loads(sys.stdin.read())

    #{code}

    if callable(locals().get('run', None)):
        output = run(input)
    elif 'result' in dir():
        output = result
    else:
        output = input
    #{output_sink}
  PYTHON
end

.ruby(code, output_sink) ⇒ Object



42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
# File 'lib/trmnlp/transform_backend/wrapper.rb', line 42

def ruby(code, output_sink)
  <<~RUBY
    require 'json'
    input = JSON.parse($stdin.read)

    #{code}

    output = if defined?(run) == 'method'
               run(input)
             elsif defined?(result)
               result
             else
               input
             end
    #{output_sink}
  RUBY
end