Class: Inquirex::Engine
- Inherits:
-
Object
- Object
- Inquirex::Engine
- Defined in:
- lib/inquirex/engine.rb,
lib/inquirex/engine/state_serializer.rb
Overview
Runtime session that drives flow navigation. Holds the definition, collected answers, and current position in the flow graph.
Collecting steps (ask, confirm): call engine.answer(value) Display steps (say, header, btw, warning): call engine.advance
Validates each answer via an optional Validation::Adapter, then advances using node transitions. Skips steps whose skip_if rule evaluates to true.
Defined Under Namespace
Modules: StateSerializer
Instance Attribute Summary collapse
-
#answers ⇒ Object
readonly
Returns the value of attribute answers.
-
#completion_hook_errors ⇒ Array<StandardError>
readonly
Exceptions raised by after_completion hooks, in the order they were raised.
-
#completion_metadata ⇒ CompletionMetadata?
Metadata describing how and where the flow was completed.
-
#current_step_id ⇒ Object
readonly
Returns the value of attribute current_step_id.
-
#definition ⇒ Object
readonly
Returns the value of attribute definition.
-
#history ⇒ Object
readonly
Returns the value of attribute history.
-
#totals ⇒ Object
readonly
Returns the value of attribute totals.
Class Method Summary collapse
-
.from_state(definition, state_hash, validator: Validation::NullAdapter.new) ⇒ Engine
Rebuilds an Engine from a previously saved state.
Instance Method Summary collapse
-
#advance ⇒ Object
Advances past the current non-collecting step (say/header/btw/warning).
-
#after_completion {|Engine| ... } ⇒ Engine
Registers a hook to run when the flow finishes.
-
#answer(value) ⇒ Object
Submits an answer for the current collecting step (ask/confirm).
-
#answers_with_metadata ⇒ Hash
The collected answers with the completion metadata (when a renderer attached one) merged in under the :completion_metadata key.
-
#current_step ⇒ Node?
Current step node, or nil if flow is finished.
-
#finished? ⇒ Boolean
True when there is no current step (flow ended).
-
#initialize(definition, validator: Validation::NullAdapter.new) ⇒ Engine
constructor
A new instance of Engine.
-
#prefill!(hash) ⇒ Hash
Merges a hash of { step_id => value } into the top-level answers without clobbering answers the user has already provided.
-
#to_state ⇒ Hash
Serializable state snapshot for persistence or resumption.
-
#total(name) ⇒ Numeric
Convenience accessor for a single accumulator's running total.
Constructor Details
#initialize(definition, validator: Validation::NullAdapter.new) ⇒ Engine
Returns a new instance of Engine.
34 35 36 37 38 39 40 41 42 43 44 45 46 |
# File 'lib/inquirex/engine.rb', line 34 def initialize(definition, validator: Validation::NullAdapter.new) @definition = definition @answers = {} @history = [] @current_step_id = definition.start_step_id @validator = validator @totals = init_totals @completion_metadata = nil @after_completion_hooks = [] @completion_hook_errors = [] @history << @current_step_id skip_display_steps_if_needed end |
Instance Attribute Details
#answers ⇒ Object (readonly)
Returns the value of attribute answers.
13 14 15 |
# File 'lib/inquirex/engine.rb', line 13 def answers @answers end |
#completion_hook_errors ⇒ Array<StandardError> (readonly)
Exceptions raised by after_completion hooks, in the order they were raised. Hooks are isolated from one another, so a raising hook is recorded here rather than propagated — callers that care can inspect this after the flow finishes. Empty when every hook succeeded.
30 31 32 |
# File 'lib/inquirex/engine.rb', line 30 def completion_hook_errors @completion_hook_errors end |
#completion_metadata ⇒ CompletionMetadata?
Metadata describing how and where the flow was completed. Rendering front-ends (TTY, web, chat widget) attach a rich version from an after_completion hook; when no hook provides one, the engine stamps a minimal core version the moment the flow finishes. Only :engine and :engine_version are required members; everything else is free-form.
22 23 24 |
# File 'lib/inquirex/engine.rb', line 22 def @completion_metadata end |
#current_step_id ⇒ Object (readonly)
Returns the value of attribute current_step_id.
13 14 15 |
# File 'lib/inquirex/engine.rb', line 13 def current_step_id @current_step_id end |
#definition ⇒ Object (readonly)
Returns the value of attribute definition.
13 14 15 |
# File 'lib/inquirex/engine.rb', line 13 def definition @definition end |
#history ⇒ Object (readonly)
Returns the value of attribute history.
13 14 15 |
# File 'lib/inquirex/engine.rb', line 13 def history @history end |
#totals ⇒ Object (readonly)
Returns the value of attribute totals.
13 14 15 |
# File 'lib/inquirex/engine.rb', line 13 def totals @totals end |
Class Method Details
.from_state(definition, state_hash, validator: Validation::NullAdapter.new) ⇒ Engine
Rebuilds an Engine from a previously saved state.
186 187 188 189 190 191 |
# File 'lib/inquirex/engine.rb', line 186 def self.from_state(definition, state_hash, validator: Validation::NullAdapter.new) state = StateSerializer.symbolize_state(state_hash) engine = allocate engine.send(:restore_state, definition, state, validator) engine end |
Instance Method Details
#advance ⇒ Object
Advances past the current non-collecting step (say/header/btw/warning).
91 92 93 94 95 |
# File 'lib/inquirex/engine.rb', line 91 def advance raise Errors::AlreadyFinishedError, "Flow is already finished" if finished? advance_step end |
#after_completion {|Engine| ... } ⇒ Engine
Registers a hook to run when the flow finishes. The block receives the engine; front-ends typically use it to attach a rich completion_metadata (host, user, ips, terminal, ...). Optional — after all hooks run, the engine fills in a minimal CompletionMetadata when none of them provided one. Registering on an already-finished engine invokes the block immediately.
Any number of hooks may be registered; they run in registration order. Each is isolated from the others — a hook that raises a StandardError has it recorded in #completion_hook_errors, and the remaining hooks still run. Non-StandardError exceptions (Interrupt, SignalException) propagate, as they should.
146 147 148 149 150 151 152 153 154 155 |
# File 'lib/inquirex/engine.rb', line 146 def after_completion(&block) raise ArgumentError, "after_completion requires a block" unless block @after_completion_hooks << block if finished? invoke_completion_hook(block) end self end |
#answer(value) ⇒ Object
Submits an answer for the current collecting step (ask/confirm). Validates, stores, and advances to the next step.
75 76 77 78 79 80 81 82 83 84 85 86 |
# File 'lib/inquirex/engine.rb', line 75 def answer(value) raise Errors::AlreadyFinishedError, "Flow is already finished" if finished? raise Errors::NonCollectingStepError, "Step #{@current_step_id} is a display step; use #advance instead" \ unless current_step.collecting? result = @validator.validate(current_step, value) raise Errors::ValidationError, "Validation failed: #{result.errors.join(", ")}" unless result.valid? @answers[@current_step_id] = value apply_accumulations(current_step, value) advance_step end |
#answers_with_metadata ⇒ Hash
The collected answers with the completion metadata (when a renderer attached one) merged in under the :completion_metadata key.
174 175 176 177 178 |
# File 'lib/inquirex/engine.rb', line 174 def return @answers if @completion_metadata.nil? @answers.merge(completion_metadata: @completion_metadata.to_h) end |
#current_step ⇒ Node?
Returns current step node, or nil if flow is finished.
57 58 59 60 61 |
# File 'lib/inquirex/engine.rb', line 57 def current_step return nil if finished? @definition.step(@current_step_id) end |
#finished? ⇒ Boolean
Returns true when there is no current step (flow ended).
64 65 66 |
# File 'lib/inquirex/engine.rb', line 64 def finished? @current_step_id.nil? end |
#prefill!(hash) ⇒ Hash
Merges a hash of { step_id => value } into the top-level answers without
clobbering answers the user has already provided. Used by LLM clarify
steps to populate downstream answers from free-text extraction so that
skip_if not_empty(:id) rules on later steps will fire.
Nil/empty values in the hash are ignored so that "unknown" LLM outputs
don't spuriously satisfy not_empty rules.
If the engine's current step becomes skippable as a result of the prefill, it auto-advances past it.
110 111 112 113 114 115 116 117 118 119 120 121 122 |
# File 'lib/inquirex/engine.rb', line 110 def prefill!(hash) return @answers unless hash.is_a?(Hash) hash.each do |key, value| next if value.nil? next if value.respond_to?(:empty?) && value.empty? sym = key.to_sym @answers[sym] = value unless @answers.key?(sym) end skip_if_needed unless finished? @answers end |
#to_state ⇒ Hash
Serializable state snapshot for persistence or resumption.
160 161 162 163 164 165 166 167 168 |
# File 'lib/inquirex/engine.rb', line 160 def to_state { current_step_id: @current_step_id, answers: @answers, history: @history, totals: @totals, completion_metadata: @completion_metadata&.to_h } end |
#total(name) ⇒ Numeric
Convenience accessor for a single accumulator's running total.
52 53 54 |
# File 'lib/inquirex/engine.rb', line 52 def total(name) @totals[name.to_sym] || 0 end |