Class: InputManager

Inherits:
Object
  • Object
show all
Defined in:
lib/mangledotdev.rb

Overview

InputManager - Manages sending requests to other processes and handling responses.

This is an instance-based class - create one instance per request.

Attributes:

response (Hash): Complete response with status, data, errors, warnings

Methods:

request(): Send a request to another process
get_response(): Get the full response object
get_data(): Get the response data (returns nil on error)

Instance Attribute Summary collapse

Class Method Summary collapse

Instance Method Summary collapse

Constructor Details

#initializeInputManager

Initialize a new InputManager instance.



27
28
29
30
31
32
33
34
# File 'lib/mangledotdev.rb', line 27

def initialize
  @process = nil
  @raw_request = nil
  @request = nil
  @response = nil
  @data = nil
  @key = nil
end

Instance Attribute Details

#responseObject (readonly)

Returns the value of attribute response.



18
19
20
# File 'lib/mangledotdev.rb', line 18

def response
  @response
end

Class Method Details

.bundle(value) ⇒ Object

Bundle any value for use with request() - for API consistency with other languages. In Ruby, this just returns the value as-is since Ruby handles serialization automatically.

Parameters:

  • value (Object)

    Any value

Returns:

  • (Object)

    The same value (Ruby handles JSON serialization automatically)



291
292
293
# File 'lib/mangledotdev.rb', line 291

def self.bundle(value)
  value
end

.gen_keyString

Generate a unique key for request/response matching.

Returns:

  • (String)

    Unique key



22
23
24
# File 'lib/mangledotdev.rb', line 22

def self.gen_key
  SecureRandom.hex(16)
end

Instance Method Details

#get_dataObject?

Get the response data if request was successful.

Returns:

  • (Object, nil)

    The data from the response (any type), or nil if request failed. The return type matches the type sent by the target process.



279
280
281
282
283
284
# File 'lib/mangledotdev.rb', line 279

def get_data
  if @response
    return @response[:data] if @response[:request_status]
  end
  nil
end

#get_responseHash

Get the full response object.

Returns:

  • (Hash)

    The complete response with status, data, errors, warnings.



271
272
273
# File 'lib/mangledotdev.rb', line 271

def get_response
  @response
end

#request(is_unique: true, optional_output: true, data: nil, language:, file:) ⇒ Object

Send a request to another process.

Sets @response (Hash) with keys:

- request_status (Boolean|nil): Success status
- data: Response data (preserves type)
- optionalOutput (Boolean): Echo of parameter
- isUnique (Boolean): Echo of parameter
- warnings (Array<String>): Warning messages
- errors (Array<String>): Error messages

Parameters:

  • is_unique (Boolean) (defaults to: true)

    Expect single output (true) or multiple (false)

  • optional_output (Boolean) (defaults to: true)

    Output is optional (true) or required (false)

  • data (Object) (defaults to: nil)

    Data to send (any JSON-serializable type)

  • language (String)

    Target language/runtime

  • file (String)

    Path to target file



153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
# File 'lib/mangledotdev.rb', line 153

def request(is_unique: true, optional_output: true, data: nil, language:, file:)
  begin
    @key = InputManager.gen_key
    command = get_command(language, file)

    @raw_request = {
      key: @key,
      optionalOutput: optional_output,
      isUnique: is_unique,
      data: data
    }
    @request = JSON.generate(@raw_request)

    response = {
      request_status: nil,
      data: nil,
      optionalOutput: optional_output,
      isUnique: is_unique,
      warnings: [],
      errors: []
    }

    stdout, stderr, status = Open3.capture3(*command, stdin_data: @request)

    unless status.success?
      response[:request_status] = false
      response[:errors] << "Process exited with code #{status.exitstatus}"
      response[:errors] << "stderr: #{stderr.strip}" unless stderr.strip.empty?
      response[:warnings] << "Warning: these kind of errors result from an error in the targeted script."
      @response = response
      return
    end

    @response_data = []
    stdout.strip.split("\n").each do |line|
      next if line.strip.empty?

      begin
        parsed_data = JSON.parse(line)

        # Validate response has matching key or null key (for init errors)
        # This ensures we only process responses meant for this request
        if parsed_data.is_a?(Hash) && (parsed_data['key'] == @key || parsed_data['key'].nil?)
          @response_data << parsed_data
        end
      rescue JSON::ParserError
        # Ignore lines that aren't valid JSON (e.g., debug prints)
      end
    end

    if @response_data.length != 0
      failure = false
      @response_data.each do |resp|
        failure = true unless resp['request_status']
      end

      response[:request_status] = !failure

      response[:isUnique] = @response_data[0]['isUnique']

      @response_data.each do |resp|
        resp['errors'].each do |err|
          response[:errors] << err
        end
      end

      data_list = []
      @response_data.each do |resp|
        data_list << resp['data']
      end

      if response[:isUnique]
        if data_list.length == 1
          response[:data] = data_list[0]
        else
          response[:request_status] = false
          response[:data] = nil
          response[:errors] << "Error: Expected 1 output (isUnique=True) but received #{data_list.length}."
        end
      else
        response[:data] = data_list
      end
    elsif optional_output
      response[:request_status] = nil
      response[:data] = nil
      response[:warnings] << "Warning: the output setting is set to optional, and the targeted program didn't gave any output."
    else
      response[:request_status] = false
      response[:data] = nil
      response[:errors] << "Error: OutputManager might not be used or not correctly."
    end

    @response = response

  rescue ArgumentError => e
    @response = {
      request_status: false,
      data: nil,
      optionalOutput: optional_output,
      isUnique: is_unique,
      warnings: ["Warning: targeted file not found or can't be executed, consider checking file informations and language dependencies."],
      errors: ["Error: #{e.message}"]
    }
  rescue => e
    @response = {
      request_status: false,
      data: nil,
      optionalOutput: optional_output,
      isUnique: is_unique,
      warnings: [],
      errors: ["Unexpected error: #{e.message}"]
    }
  end
end