Class: RubyReactor::Web::API

Inherits:
Roda
  • Object
show all
Defined in:
lib/ruby_reactor/web/api.rb

Overview

rubocop:disable Metrics/BlockLength

Class Method Summary collapse

Class Method Details

.build_structure(reactor_class) ⇒ Object



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
# File 'lib/ruby_reactor/web/api.rb', line 170

def self.build_structure(reactor_class)
  return {} unless reactor_class.respond_to?(:steps)

  steps_config = reactor_class.steps
  return {} unless steps_config.is_a?(Hash)

  # Use DependencyGraph to calculate dependencies effectively
  graph = RubyReactor::DependencyGraph.new
  steps_config.each_value { |config| graph.add_step(config) }

  steps_config.to_h do |name, config|
    type = determine_step_type(config)

    step_data = {
      name: name,
      type: type,
      depends_on: graph.dependencies[name],
      async: config.async?
    }

    if type == "compose"
      inner_class = extract_inner_class(config, :composed_reactor_class)
      step_data[:nested_structure] = build_structure(inner_class) if inner_class
    elsif type == "map"
      inner_class = extract_inner_class(config, :mapped_reactor_class)
      step_data[:nested_structure] = build_structure(inner_class) if inner_class
    end

    [name, step_data]
  end
end

.determine_step_type(config) ⇒ Object



202
203
204
205
206
207
208
209
210
211
212
213
214
# File 'lib/ruby_reactor/web/api.rb', line 202

def self.determine_step_type(config)
  if config.respond_to?(:interrupt?) && config.interrupt?
    "interrupt"
  elsif config.arguments&.key?(:composed_reactor_class)
    "compose"
  elsif config.arguments&.key?(:mapped_reactor_class)
    "map"
  elsif config.async?
    "async"
  else
    "step"
  end
end

.execution_evidence?(data) ⇒ Boolean

Returns:

  • (Boolean)


158
159
160
161
# File 'lib/ruby_reactor/web/api.rb', line 158

def self.execution_evidence?(data)
  (data[:execution_trace] || []).any? ||
    (data[:intermediate_results] || {}).any?
end

.extract_inner_class(config, param_name) ⇒ Object



216
217
218
219
220
221
# File 'lib/ruby_reactor/web/api.rb', line 216

def self.extract_inner_class(config, param_name)
  val = config.arguments.dig(param_name, :source)
  val.is_a?(RubyReactor::Template::Value) ? val.value : nil
rescue StandardError
  nil
end

.extract_retry_inputs(data) ⇒ Object



163
164
165
166
167
168
# File 'lib/ruby_reactor/web/api.rb', line 163

def self.extract_retry_inputs(data)
  inputs = data[:inputs] || {}
  return {} unless inputs.is_a?(Hash)

  inputs.transform_keys(&:to_sym)
end

.hydrate_composed_contexts(composed_contexts, reactor_class_name) ⇒ Object



223
224
225
226
227
228
229
230
231
232
233
234
# File 'lib/ruby_reactor/web/api.rb', line 223

def self.hydrate_composed_contexts(composed_contexts, reactor_class_name)
  return {} unless composed_contexts.is_a?(Hash)

  composed_contexts.transform_values do |value|
    type = value[:type] || value["type"]
    if ["map_ref", :map_ref].include?(type)
      hydrate_map_ref(value, reactor_class_name)
    else
      value
    end
  end
end

.hydrate_map_ref(ref_data, reactor_class_name) ⇒ Object



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
267
268
269
270
# File 'lib/ruby_reactor/web/api.rb', line 236

def self.hydrate_map_ref(ref_data, reactor_class_name)
  storage = RubyReactor.configuration.storage_adapter
  map_id = ref_data[:map_id] || ref_data["map_id"]

  # Use the specific element reactor class if available, otherwise fallback to parent
  target_reactor_class = ref_data[:element_reactor_class] ||
                         ref_data["element_reactor_class"] ||
                         reactor_class_name

  # 1. Check for specific failure (O(1))
  # Stored by ResultHandler when a map element fails
  failed_context_id = storage.retrieve_map_failed_context_id(map_id, reactor_class_name)

  target_context_id = if failed_context_id
                        failed_context_id
                      else
                        # 2. Fallback to representative sample (last element) (O(1))
                        # If no failure, the last element gives a good idea of progress/completion
                        target_id = storage.retrieve_map_element_context_id(map_id, reactor_class_name, index: -1)
                        target_id
                      end

  return ref_data unless target_context_id

  # Retrieve the actual context data for the target ID
  representative_data = storage.retrieve_context(target_context_id, target_reactor_class)

  return ref_data unless representative_data

  {
    "name" => ref_data["name"],
    "type" => "map_element",
    "context" => representative_data
  }
end

.reactor_status(data) ⇒ Object



148
149
150
151
152
153
154
155
156
# File 'lib/ruby_reactor/web/api.rb', line 148

def self.reactor_status(data)
  status = data[:status].to_s
  return status if %w[failed paused completed running skipped pending].include?(status)
  return "cancelled" if data[:cancelled]
  return "running" if data[:current_step]
  return "completed" if execution_evidence?(data)

  "pending"
end