inquirex 0.4.1 → 0.6.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (44) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +18 -0
  3. data/README.md +203 -52
  4. data/docs/badges/coverage_badge.svg +2 -2
  5. data/examples/01_readme_example.rb +55 -0
  6. data/examples/02_readme_mermaid.png +0 -0
  7. data/examples/02_readme_mermaid.rb +36 -0
  8. data/examples/03_send_email_actions.rb +97 -0
  9. data/examples/README.md +33 -0
  10. data/justfile +3 -2
  11. data/lib/inquirex/accumulator.rb +29 -0
  12. data/lib/inquirex/actions/action.rb +68 -0
  13. data/lib/inquirex/actions/base.rb +41 -0
  14. data/lib/inquirex/actions/custom.rb +31 -0
  15. data/lib/inquirex/actions/outbox.rb +57 -0
  16. data/lib/inquirex/actions/runner.rb +52 -0
  17. data/lib/inquirex/actions/send_email.rb +174 -0
  18. data/lib/inquirex/actions/template.rb +95 -0
  19. data/lib/inquirex/actions/webhook.rb +139 -0
  20. data/lib/inquirex/actions.rb +57 -0
  21. data/lib/inquirex/answers.rb +13 -0
  22. data/lib/inquirex/completion_metadata.rb +82 -0
  23. data/lib/inquirex/definition.rb +67 -5
  24. data/lib/inquirex/dsl/action_builder.rb +53 -0
  25. data/lib/inquirex/dsl/flow_builder.rb +49 -6
  26. data/lib/inquirex/engine/state_serializer.rb +6 -4
  27. data/lib/inquirex/engine.rb +97 -5
  28. data/lib/inquirex/errors.rb +5 -0
  29. data/lib/inquirex/graph/mermaid_exporter.rb +2 -0
  30. data/lib/inquirex/node.rb +2 -0
  31. data/lib/inquirex/rules/all.rb +12 -0
  32. data/lib/inquirex/rules/any.rb +11 -0
  33. data/lib/inquirex/rules/base.rb +6 -0
  34. data/lib/inquirex/rules/contains.rb +12 -0
  35. data/lib/inquirex/rules/equals.rb +12 -0
  36. data/lib/inquirex/rules/greater_than.rb +12 -0
  37. data/lib/inquirex/rules/less_than.rb +12 -0
  38. data/lib/inquirex/rules/not_empty.rb +12 -0
  39. data/lib/inquirex/validation/adapter.rb +1 -0
  40. data/lib/inquirex/validation/null_adapter.rb +5 -0
  41. data/lib/inquirex/version.rb +1 -1
  42. data/lib/inquirex/widget_registry.rb +1 -0
  43. data/lib/inquirex.rb +13 -0
  44. metadata +35 -5
@@ -12,6 +12,23 @@ module Inquirex
12
12
  class Engine
13
13
  attr_reader :definition, :answers, :history, :current_step_id, :totals
14
14
 
15
+ # Metadata describing how and where the flow was completed. Rendering
16
+ # front-ends (TTY, web, chat widget) attach a rich version from an
17
+ # after_completion hook; when no hook provides one, the engine stamps a
18
+ # minimal core version the moment the flow finishes. Only :engine and
19
+ # :engine_version are required members; everything else is free-form.
20
+ #
21
+ # @return [CompletionMetadata, nil] nil until the flow finishes
22
+ attr_accessor :completion_metadata
23
+
24
+ # Exceptions raised by after_completion hooks, in the order they were
25
+ # raised. Hooks are isolated from one another, so a raising hook is
26
+ # recorded here rather than propagated — callers that care can inspect
27
+ # this after the flow finishes. Empty when every hook succeeded.
28
+ #
29
+ # @return [Array<StandardError>]
30
+ attr_reader :completion_hook_errors
31
+
15
32
  # @param definition [Definition] the flow to run
16
33
  # @param validator [Validation::Adapter] optional (default: NullAdapter)
17
34
  def initialize(definition, validator: Validation::NullAdapter.new)
@@ -21,6 +38,9 @@ module Inquirex
21
38
  @current_step_id = definition.start_step_id
22
39
  @validator = validator
23
40
  @totals = init_totals
41
+ @completion_metadata = nil
42
+ @after_completion_hooks = []
43
+ @completion_hook_errors = []
24
44
  @history << @current_step_id
25
45
  skip_display_steps_if_needed
26
46
  end
@@ -101,18 +121,62 @@ module Inquirex
101
121
  @answers
102
122
  end
103
123
 
124
+ # Registers a hook to run when the flow finishes. The block receives the
125
+ # engine; front-ends typically use it to attach a rich
126
+ # completion_metadata (host, user, ips, terminal, ...). Optional — after
127
+ # all hooks run, the engine fills in a minimal CompletionMetadata when
128
+ # none of them provided one. Registering on an already-finished engine
129
+ # invokes the block immediately.
130
+ #
131
+ # Any number of hooks may be registered; they run in registration order.
132
+ # Each is isolated from the others — a hook that raises a StandardError
133
+ # has it recorded in #completion_hook_errors, and the remaining hooks
134
+ # still run. Non-StandardError exceptions (Interrupt, SignalException)
135
+ # propagate, as they should.
136
+ #
137
+ # @example Stamp renderer-specific completion metadata
138
+ # engine.after_completion do |eng|
139
+ # eng.completion_metadata = Inquirex::CompletionMetadata.new(
140
+ # engine: "inquirex-tty", engine_version: "0.5.0", hostname: Socket.gethostname
141
+ # )
142
+ # end
143
+ #
144
+ # @yield [Engine] the engine, at completion time
145
+ # @return [Engine] self
146
+ def after_completion(&block)
147
+ raise ArgumentError, "after_completion requires a block" unless block
148
+
149
+ @after_completion_hooks << block
150
+ if finished?
151
+ invoke_completion_hook(block)
152
+ ensure_completion_metadata
153
+ end
154
+ self
155
+ end
156
+
104
157
  # Serializable state snapshot for persistence or resumption.
105
158
  #
106
159
  # @return [Hash]
107
160
  def to_state
108
161
  {
109
- current_step_id: @current_step_id,
110
- answers: @answers,
111
- history: @history,
112
- totals: @totals
162
+ current_step_id: @current_step_id,
163
+ answers: @answers,
164
+ history: @history,
165
+ totals: @totals,
166
+ completion_metadata: @completion_metadata&.to_h
113
167
  }
114
168
  end
115
169
 
170
+ # The collected answers with the completion metadata (when a renderer
171
+ # attached one) merged in under the :completion_metadata key.
172
+ #
173
+ # @return [Hash]
174
+ def answers_with_metadata
175
+ return @answers if @completion_metadata.nil?
176
+
177
+ @answers.merge(completion_metadata: @completion_metadata.to_h)
178
+ end
179
+
116
180
  # Rebuilds an Engine from a previously saved state.
117
181
  #
118
182
  # @param definition [Definition] same definition used when state was captured
@@ -135,6 +199,9 @@ module Inquirex
135
199
  @answers = state[:answers] || {}
136
200
  @history = state[:history] || []
137
201
  @totals = state[:totals] || init_totals
202
+ @completion_metadata = CompletionMetadata.from_h(state[:completion_metadata])
203
+ @after_completion_hooks = []
204
+ @completion_hook_errors = []
138
205
  end
139
206
 
140
207
  def init_totals
@@ -154,12 +221,37 @@ module Inquirex
154
221
  node = @definition.step(@current_step_id)
155
222
  next_id = node.next_step_id(@answers)
156
223
  @current_step_id = next_id
157
- return unless next_id
224
+ return run_after_completion_hooks unless next_id
158
225
 
159
226
  @history << next_id
160
227
  skip_if_needed
161
228
  end
162
229
 
230
+ # Fires the moment the flow finishes: runs registered after_completion
231
+ # hooks in registration order, then guarantees completion_metadata exists
232
+ # — hooks may attach a rich version; absent that (including when the hook
233
+ # meant to supply it raised), a minimal core-stamped one is used.
234
+ def run_after_completion_hooks
235
+ @after_completion_hooks.each { |hook| invoke_completion_hook(hook) }
236
+ ensure_completion_metadata
237
+ nil
238
+ end
239
+
240
+ # Runs one hook in isolation. A StandardError is recorded rather than
241
+ # propagated so that one misbehaving hook cannot silently cancel the
242
+ # hooks registered after it, nor abort the completion of the flow itself.
243
+ def invoke_completion_hook(hook)
244
+ hook.call(self)
245
+ rescue StandardError => e
246
+ @completion_hook_errors << e
247
+ end
248
+
249
+ def ensure_completion_metadata
250
+ return if @completion_metadata
251
+
252
+ @completion_metadata = CompletionMetadata.new(engine: "inquirex", engine_version: VERSION)
253
+ end
254
+
163
255
  # Auto-skips the current step if its skip_if rule is satisfied.
164
256
  def skip_if_needed
165
257
  return if finished?
@@ -1,6 +1,7 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Inquirex
4
+ # Namespace for all Inquirex exception classes.
4
5
  module Errors
5
6
  # Base exception for all Inquirex errors.
6
7
  class Error < StandardError; end
@@ -26,5 +27,9 @@ module Inquirex
26
27
 
27
28
  # Raised when serializing or deserializing a Definition to/from JSON fails.
28
29
  class SerializationError < Error; end
30
+
31
+ # Raised when a post-completion action cannot execute structurally,
32
+ # e.g. send_email is used without the mail gem installed.
33
+ class ActionError < Error; end
29
34
  end
30
35
  end
@@ -1,10 +1,12 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Inquirex
4
+ # Namespace for flow graph export utilities (Mermaid, ...).
4
5
  module Graph
5
6
  # Exports a flow Definition to Mermaid flowchart syntax for visualization.
6
7
  # Node labels show verb + truncated question/text; edges show condition labels.
7
8
  class MermaidExporter
9
+ # Maximum characters of a node label before it is truncated with "...".
8
10
  MAX_LABEL_LENGTH = 50
9
11
 
10
12
  attr_reader :definition
data/lib/inquirex/node.rb CHANGED
@@ -27,7 +27,9 @@ module Inquirex
27
27
  class Node
28
28
  # Valid DSL verbs and which ones collect input from the user.
29
29
  VERBS = %i[ask say header btw warning confirm].freeze
30
+ # Verbs that collect input from the user.
30
31
  COLLECTING_VERBS = %i[ask confirm].freeze
32
+ # Verbs that only display content and auto-advance.
31
33
  DISPLAY_VERBS = %i[say header btw warning].freeze
32
34
 
33
35
  # Valid data types for :ask steps.
@@ -1,29 +1,41 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Inquirex
4
+ # Namespace for rule AST nodes used in transitions and visibility conditions.
4
5
  module Rules
5
6
  # Composite rule: logical AND of multiple sub-rules. All must be true.
6
7
  class All < Base
7
8
  attr_reader :rules
8
9
 
10
+ # @param rules [Array<Rules::Base>] sub-rules that must all hold
9
11
  def initialize(*rules)
10
12
  super()
11
13
  @rules = rules.flatten.freeze
12
14
  freeze
13
15
  end
14
16
 
17
+ # True when every sub-rule evaluates true (vacuously true when empty).
18
+ #
19
+ # @param answers [Hash{Symbol => Object}] answer context, step_id => value
20
+ # @return [Boolean]
15
21
  def evaluate(answers)
16
22
  @rules.all? { |rule| rule.evaluate(answers) }
17
23
  end
18
24
 
25
+ # @return [Hash{String => Object}] wire format with nested rule hashes
19
26
  def to_h
20
27
  { "op" => "all", "rules" => @rules.map(&:to_h) }
21
28
  end
22
29
 
30
+ # @return [String] human-readable form, sub-rules joined with AND
23
31
  def to_s
24
32
  "(#{@rules.join(" AND ")})"
25
33
  end
26
34
 
35
+ # Deserializes an All rule, recursively rehydrating its sub-rules.
36
+ #
37
+ # @param hash [Hash] rule hash with string or symbol keys
38
+ # @return [All]
27
39
  def self.from_h(hash)
28
40
  raw_rules = hash["rules"] || hash[:rules] || []
29
41
  new(*raw_rules.map { |r| Base.from_h(r) })
@@ -6,24 +6,35 @@ module Inquirex
6
6
  class Any < Base
7
7
  attr_reader :rules
8
8
 
9
+ # @param rules [Array<Rules::Base>] sub-rules, at least one of which must hold
9
10
  def initialize(*rules)
10
11
  super()
11
12
  @rules = rules.flatten.freeze
12
13
  freeze
13
14
  end
14
15
 
16
+ # True when at least one sub-rule evaluates true (false when empty).
17
+ #
18
+ # @param answers [Hash{Symbol => Object}] answer context, step_id => value
19
+ # @return [Boolean]
15
20
  def evaluate(answers)
16
21
  @rules.any? { |rule| rule.evaluate(answers) }
17
22
  end
18
23
 
24
+ # @return [Hash{String => Object}] wire format with nested rule hashes
19
25
  def to_h
20
26
  { "op" => "any", "rules" => @rules.map(&:to_h) }
21
27
  end
22
28
 
29
+ # @return [String] human-readable form, sub-rules joined with OR
23
30
  def to_s
24
31
  "(#{@rules.join(" OR ")})"
25
32
  end
26
33
 
34
+ # Deserializes an Any rule, recursively rehydrating its sub-rules.
35
+ #
36
+ # @param hash [Hash] rule hash with string or symbol keys
37
+ # @return [Any]
27
38
  def self.from_h(hash)
28
39
  raw_rules = hash["rules"] || hash[:rules] || []
29
40
  new(*raw_rules.map { |r| Base.from_h(r) })
@@ -30,6 +30,12 @@ module Inquirex
30
30
 
31
31
  # Deserializes a rule from a plain Hash (e.g. parsed from JSON).
32
32
  #
33
+ # @example Rehydrate a rule from its wire format
34
+ # rule = Inquirex::Rules::Base.from_h(
35
+ # "op" => "equals", "field" => "filing_status", "value" => "single"
36
+ # )
37
+ # rule.evaluate(filing_status: "single") # => true
38
+ #
33
39
  # @param hash [Hash] rule hash with string or symbol keys
34
40
  # @return [Rules::Base] the deserialized rule
35
41
  # @raise [Inquirex::Errors::SerializationError] on unknown operator
@@ -7,6 +7,8 @@ module Inquirex
7
7
  class Contains < Base
8
8
  attr_reader :field, :value
9
9
 
10
+ # @param field [Symbol, String] step id whose answer is inspected
11
+ # @param value [Object] value the answer must include
10
12
  def initialize(field, value)
11
13
  super()
12
14
  @field = field.to_sym
@@ -14,18 +16,28 @@ module Inquirex
14
16
  freeze
15
17
  end
16
18
 
19
+ # True when the field's answer, coerced to an Array, includes the value.
20
+ #
21
+ # @param answers [Hash{Symbol => Object}] answer context, step_id => value
22
+ # @return [Boolean]
17
23
  def evaluate(answers)
18
24
  Array(answers[@field]).include?(@value)
19
25
  end
20
26
 
27
+ # @return [Hash{String => Object}] wire format, same shape .from_h accepts
21
28
  def to_h
22
29
  { "op" => "contains", "field" => @field.to_s, "value" => @value }
23
30
  end
24
31
 
32
+ # @return [String] human-readable form, e.g. "Business in income_types"
25
33
  def to_s
26
34
  "#{@value} in #{@field}"
27
35
  end
28
36
 
37
+ # Deserializes a Contains rule from a plain Hash.
38
+ #
39
+ # @param hash [Hash] rule hash with string or symbol keys
40
+ # @return [Contains]
29
41
  def self.from_h(hash)
30
42
  field = hash["field"] || hash[:field]
31
43
  value = hash["value"] || hash[:value]
@@ -6,6 +6,8 @@ module Inquirex
6
6
  class Equals < Base
7
7
  attr_reader :field, :value
8
8
 
9
+ # @param field [Symbol, String] step id whose answer is compared
10
+ # @param value [Object] value the answer must equal
9
11
  def initialize(field, value)
10
12
  super()
11
13
  @field = field.to_sym
@@ -13,18 +15,28 @@ module Inquirex
13
15
  freeze
14
16
  end
15
17
 
18
+ # True when the field's answer equals the value (Ruby ==).
19
+ #
20
+ # @param answers [Hash{Symbol => Object}] answer context, step_id => value
21
+ # @return [Boolean]
16
22
  def evaluate(answers)
17
23
  answers[@field] == @value
18
24
  end
19
25
 
26
+ # @return [Hash{String => Object}] wire format, same shape .from_h accepts
20
27
  def to_h
21
28
  { "op" => "equals", "field" => @field.to_s, "value" => @value }
22
29
  end
23
30
 
31
+ # @return [String] human-readable form, e.g. "filing_status == single"
24
32
  def to_s
25
33
  "#{@field} == #{@value}"
26
34
  end
27
35
 
36
+ # Deserializes an Equals rule from a plain Hash.
37
+ #
38
+ # @param hash [Hash] rule hash with string or symbol keys
39
+ # @return [Equals]
28
40
  def self.from_h(hash)
29
41
  field = hash["field"] || hash[:field]
30
42
  value = hash["value"] || hash[:value]
@@ -6,6 +6,8 @@ module Inquirex
6
6
  class GreaterThan < Base
7
7
  attr_reader :field, :value
8
8
 
9
+ # @param field [Symbol, String] step id whose answer is compared
10
+ # @param value [Integer] threshold the answer must exceed
9
11
  def initialize(field, value)
10
12
  super()
11
13
  @field = field.to_sym
@@ -13,18 +15,28 @@ module Inquirex
13
15
  freeze
14
16
  end
15
17
 
18
+ # True when the field's answer, coerced to an Integer, exceeds the threshold.
19
+ #
20
+ # @param answers [Hash{Symbol => Object}] answer context, step_id => value
21
+ # @return [Boolean]
16
22
  def evaluate(answers)
17
23
  answers[@field].to_i > @value
18
24
  end
19
25
 
26
+ # @return [Hash{String => Object}] wire format, same shape .from_h accepts
20
27
  def to_h
21
28
  { "op" => "greater_than", "field" => @field.to_s, "value" => @value }
22
29
  end
23
30
 
31
+ # @return [String] human-readable form, e.g. "dependents > 2"
24
32
  def to_s
25
33
  "#{@field} > #{@value}"
26
34
  end
27
35
 
36
+ # Deserializes a GreaterThan rule from a plain Hash.
37
+ #
38
+ # @param hash [Hash] rule hash with string or symbol keys
39
+ # @return [GreaterThan]
28
40
  def self.from_h(hash)
29
41
  field = hash["field"] || hash[:field]
30
42
  value = (hash["value"] || hash[:value]).to_i
@@ -6,6 +6,8 @@ module Inquirex
6
6
  class LessThan < Base
7
7
  attr_reader :field, :value
8
8
 
9
+ # @param field [Symbol, String] step id whose answer is compared
10
+ # @param value [Integer] threshold the answer must stay below
9
11
  def initialize(field, value)
10
12
  super()
11
13
  @field = field.to_sym
@@ -13,18 +15,28 @@ module Inquirex
13
15
  freeze
14
16
  end
15
17
 
18
+ # True when the field's answer, coerced to an Integer, is below the threshold.
19
+ #
20
+ # @param answers [Hash{Symbol => Object}] answer context, step_id => value
21
+ # @return [Boolean]
16
22
  def evaluate(answers)
17
23
  answers[@field].to_i < @value
18
24
  end
19
25
 
26
+ # @return [Hash{String => Object}] wire format, same shape .from_h accepts
20
27
  def to_h
21
28
  { "op" => "less_than", "field" => @field.to_s, "value" => @value }
22
29
  end
23
30
 
31
+ # @return [String] human-readable form, e.g. "dependents < 2"
24
32
  def to_s
25
33
  "#{@field} < #{@value}"
26
34
  end
27
35
 
36
+ # Deserializes a LessThan rule from a plain Hash.
37
+ #
38
+ # @param hash [Hash] rule hash with string or symbol keys
39
+ # @return [LessThan]
28
40
  def self.from_h(hash)
29
41
  field = hash["field"] || hash[:field]
30
42
  value = (hash["value"] || hash[:value]).to_i
@@ -6,12 +6,18 @@ module Inquirex
6
6
  class NotEmpty < Base
7
7
  attr_reader :field
8
8
 
9
+ # @param field [Symbol, String] step id whose answer is checked for presence
9
10
  def initialize(field)
10
11
  super()
11
12
  @field = field.to_sym
12
13
  freeze
13
14
  end
14
15
 
16
+ # True when the field's answer is neither nil nor empty
17
+ # (empty String, Array, Hash, etc. all evaluate false).
18
+ #
19
+ # @param answers [Hash{Symbol => Object}] answer context, step_id => value
20
+ # @return [Boolean]
15
21
  def evaluate(answers)
16
22
  val = answers[@field]
17
23
  return false if val.nil?
@@ -20,14 +26,20 @@ module Inquirex
20
26
  true
21
27
  end
22
28
 
29
+ # @return [Hash{String => Object}] wire format, same shape .from_h accepts
23
30
  def to_h
24
31
  { "op" => "not_empty", "field" => @field.to_s }
25
32
  end
26
33
 
34
+ # @return [String] human-readable form, e.g. "income_types is not empty"
27
35
  def to_s
28
36
  "#{@field} is not empty"
29
37
  end
30
38
 
39
+ # Deserializes a NotEmpty rule from a plain Hash.
40
+ #
41
+ # @param hash [Hash] rule hash with string or symbol keys
42
+ # @return [NotEmpty]
31
43
  def self.from_h(hash)
32
44
  field = hash["field"] || hash[:field]
33
45
  new(field)
@@ -1,6 +1,7 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Inquirex
4
+ # Namespace for pluggable step-answer validation adapters.
4
5
  module Validation
5
6
  # Result of validating a step answer.
6
7
  #
@@ -5,6 +5,11 @@ module Inquirex
5
5
  # No-op validator: always accepts any input.
6
6
  # Used by default when no validation adapter is configured.
7
7
  class NullAdapter < Adapter
8
+ # Accepts any input unconditionally.
9
+ #
10
+ # @param _node [Node] the current step (ignored)
11
+ # @param _input [Object] value submitted by the user (ignored)
12
+ # @return [Result] always valid, with no errors
8
13
  def validate(_node, _input)
9
14
  Result.new(valid: true)
10
15
  end
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Inquirex
4
- VERSION = "0.4.1"
4
+ VERSION = "0.6.0"
5
5
  end
@@ -10,6 +10,7 @@ module Inquirex
10
10
  # Adapters (TTY, JS, Rails) use this to pick an appropriate renderer when
11
11
  # the DSL author has not specified an explicit `widget` hint.
12
12
  module WidgetRegistry
13
+ # Every widget type an adapter may be asked to render, across all contexts.
13
14
  WIDGET_TYPES = %i[
14
15
  text_input
15
16
  textarea
data/lib/inquirex.rb CHANGED
@@ -24,16 +24,29 @@ require_relative "inquirex/accumulator"
24
24
  require_relative "inquirex/node"
25
25
  require_relative "inquirex/definition"
26
26
  require_relative "inquirex/answers"
27
+ require_relative "inquirex/completion_metadata"
27
28
 
28
29
  # Validation
29
30
  require_relative "inquirex/validation/adapter"
30
31
  require_relative "inquirex/validation/null_adapter"
31
32
 
33
+ # Post-completion actions
34
+ require_relative "inquirex/actions"
35
+ require_relative "inquirex/actions/template"
36
+ require_relative "inquirex/actions/outbox"
37
+ require_relative "inquirex/actions/base"
38
+ require_relative "inquirex/actions/send_email"
39
+ require_relative "inquirex/actions/webhook"
40
+ require_relative "inquirex/actions/custom"
41
+ require_relative "inquirex/actions/action"
42
+ require_relative "inquirex/actions/runner"
43
+
32
44
  # DSL
33
45
  require_relative "inquirex/dsl"
34
46
  require_relative "inquirex/dsl/rule_helpers"
35
47
  require_relative "inquirex/dsl/step_builder"
36
48
  require_relative "inquirex/dsl/flow_builder"
49
+ require_relative "inquirex/dsl/action_builder"
37
50
 
38
51
  # Engine
39
52
  require_relative "inquirex/engine/state_serializer"
metadata CHANGED
@@ -1,21 +1,35 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: inquirex
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.4.1
4
+ version: 0.6.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Konstantin Gredeskoul
8
8
  bindir: bin
9
9
  cert_chain: []
10
10
  date: 1980-01-02 00:00:00.000000000 Z
11
- dependencies: []
11
+ dependencies:
12
+ - !ruby/object:Gem::Dependency
13
+ name: ostruct
14
+ requirement: !ruby/object:Gem::Requirement
15
+ requirements:
16
+ - - "~>"
17
+ - !ruby/object:Gem::Version
18
+ version: '0.6'
19
+ type: :runtime
20
+ prerelease: false
21
+ version_requirements: !ruby/object:Gem::Requirement
22
+ requirements:
23
+ - - "~>"
24
+ - !ruby/object:Gem::Version
25
+ version: '0.6'
12
26
  description: Inquirex lets you define multi-step questionnaires as directed graphs
13
27
  with conditional branching, using a conversational DSL (ask, say, mention) and an
14
28
  AST-based rule system (contains, equals, greater_than, all, any). The engine walks
15
29
  the graph, collects structured answers, and serializes everything to JSON — making
16
30
  it the ideal backbone for cross-platform intake forms where the frontend is a chat
17
- widget, a terminal, or a mobile app. Framework-agnostic, zero dependencies, thread-safe
18
- immutable definitions.
31
+ widget, a terminal, or a mobile app. Framework-agnostic, no third-party dependencies,
32
+ thread-safe immutable definitions.
19
33
  email:
20
34
  - kigster@gmail.com
21
35
  executables: []
@@ -32,13 +46,29 @@ files:
32
46
  - README.md
33
47
  - Rakefile
34
48
  - docs/badges/coverage_badge.svg
49
+ - examples/01_readme_example.rb
50
+ - examples/02_readme_mermaid.png
51
+ - examples/02_readme_mermaid.rb
52
+ - examples/03_send_email_actions.rb
53
+ - examples/README.md
35
54
  - justfile
36
55
  - lefthook.yml
37
56
  - lib/inquirex.rb
38
57
  - lib/inquirex/accumulator.rb
58
+ - lib/inquirex/actions.rb
59
+ - lib/inquirex/actions/action.rb
60
+ - lib/inquirex/actions/base.rb
61
+ - lib/inquirex/actions/custom.rb
62
+ - lib/inquirex/actions/outbox.rb
63
+ - lib/inquirex/actions/runner.rb
64
+ - lib/inquirex/actions/send_email.rb
65
+ - lib/inquirex/actions/template.rb
66
+ - lib/inquirex/actions/webhook.rb
39
67
  - lib/inquirex/answers.rb
68
+ - lib/inquirex/completion_metadata.rb
40
69
  - lib/inquirex/definition.rb
41
70
  - lib/inquirex/dsl.rb
71
+ - lib/inquirex/dsl/action_builder.rb
42
72
  - lib/inquirex/dsl/flow_builder.rb
43
73
  - lib/inquirex/dsl/rule_helpers.rb
44
74
  - lib/inquirex/dsl/step_builder.rb
@@ -68,7 +98,7 @@ licenses:
68
98
  - MIT
69
99
  metadata:
70
100
  allowed_push_host: https://rubygems.org
71
- homepage_uri: https://github.com/inquirex/inquirex
101
+ homepage_uri: https://qualified.at
72
102
  source_code_uri: https://github.com/inquirex/inquirex
73
103
  changelog_uri: https://github.com/inquirex/inquirex/blob/main/CHANGELOG.md
74
104
  rubygems_mfa_required: 'true'