Class: LittleGhost::Invocation

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

Overview

Carry one application request into an agent run. An invocation keeps framework fields and application-specific context in one indifferent-key environment.

invocation = LittleGhost::Invocation.new(
message: "Why is transfer 481 pending?",
account_id: "account-1",
metadata: {channel: "customer_support"}
)

invocation.message.text  # => "Why is transfer 481 pending?"
invocation[:account_id]  # => "account-1"
invocation.    # => "account-1"

String and symbol keys address the same field. Known fields have named accessors, while unknown application fields remain available through hash access and dynamic readers or writers. message and every history entry are normalized to Message objects; deadline_at lazily parses ISO 8601 text.

Missing run, invocation, and session identifiers are generated when the object is built. Actor identity is never inferred: applications that use it for persistence or tenant isolation must pass a value established by their trusted authentication boundary. Invalid payloads, messages, keys, or deadlines raise InvocationError.

Constant Summary collapse

DEFAULTS =
{
  "history" => -> { [] },
  "settings" => -> { {} },
  "context" => -> { {} },
  "metadata" => -> { {} }
}.freeze
ACCESSORS =

:nodoc:

%i[
  message history settings context metadata
  run_id invocation_id session_id actor_id
].freeze

Instance Attribute Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(env = {}) ⇒ Invocation

Copies env, normalizes known framework fields, and fills missing identifiers.

The payload must be a Hash and must contain a non-blank message.



49
50
51
52
53
54
55
56
57
# File 'lib/little_ghost/invocation.rb', line 49

def initialize(env = {})
  invalid!("Invocation payload must be an object") unless env.is_a?(Hash)

  @env = env.to_h { |key, value| [normalize_key(key), duplicate_value(value)] }
  DEFAULTS.each { |key, default| @env[key] = default.call unless @env.key?(key) }
  self.history = history
  self.message = message
  initialize_identifiers!
end

Dynamic Method Handling

This class handles dynamic methods through the method_missing method

#method_missing(name, *arguments) ⇒ Object

:nodoc:



212
213
214
215
216
217
218
219
220
# File 'lib/little_ghost/invocation.rb', line 212

def method_missing(name, *arguments) # :nodoc:
  value = name.to_s
  if value.end_with?("=") && arguments.length == 1
    return self[value.delete_suffix("=")] = arguments.first
  end
  return self[value] if arguments.empty? && key?(value)

  super
end

Instance Attribute Details

#envObject (readonly)

:nodoc:



44
45
46
# File 'lib/little_ghost/invocation.rb', line 44

def env
  @env
end

Instance Method Details

#[](key) ⇒ Object

Looks up key after normalizing it to a String.



161
# File 'lib/little_ghost/invocation.rb', line 161

def [](key) = env[normalize_key(key)]

#[]=(key, value) ⇒ Object

Stores value under a normalized String key.

The message and history fields are normalized before storage.



166
167
168
169
170
171
# File 'lib/little_ghost/invocation.rb', line 166

def []=(key, value)
  normalized = normalize_key(key)
  value = normalize_message(value) if normalized == "message"
  value = Array(value).map { |message| Message.coerce(message) }.freeze if normalized == "history"
  env[normalized] = value
end

#deadline_atObject

The request deadline as a Time, parsing ISO 8601 text on first access.



188
189
190
191
192
193
194
195
# File 'lib/little_ghost/invocation.rb', line 188

def deadline_at
  value = self[:deadline_at]
  return value if value.nil? || value.is_a?(Time)

  self[:deadline_at] = Time.iso8601(String(value))
rescue ArgumentError, TypeError
  invalid!("deadline_at must be a valid time")
end

#deadline_at=(value) ⇒ Object

Replaces the deadline; parsing is deferred until deadline_at is read.



198
199
200
# File 'lib/little_ghost/invocation.rb', line 198

def deadline_at=(value)
  self[:deadline_at] = value
end

#dig(key, *names) ⇒ Object

Traverses the environment from normalized key through names.



177
# File 'lib/little_ghost/invocation.rb', line 177

def dig(key, *names) = env.dig(normalize_key(key), *names)

#fetch(key, *defaults, &block) ⇒ Object

Fetches key with the same default and block behavior as Hash#fetch.



174
# File 'lib/little_ghost/invocation.rb', line 174

def fetch(key, *defaults, &block) = env.fetch(normalize_key(key), *defaults, &block)

#history=(value) ⇒ Object

Replaces and freezes the normalized message history.



208
209
210
# File 'lib/little_ghost/invocation.rb', line 208

def history=(value)
  self[:history] = value
end

#key?(key) ⇒ Boolean

Whether the environment contains key after normalization.

Returns:

  • (Boolean)


180
# File 'lib/little_ghost/invocation.rb', line 180

def key?(key) = env.key?(normalize_key(key))

#message=(value) ⇒ Object

Replaces and normalizes the current message.



203
204
205
# File 'lib/little_ghost/invocation.rb', line 203

def message=(value)
  self[:message] = value
end

#nameObject

Replaces the application-established actor identifier. :method: actor_id= :call-seq:

actor_id=(value) -> value


155
156
157
158
# File 'lib/little_ghost/invocation.rb', line 155

ACCESSORS.each do |name|
  define_method(name) { self[name] }
  define_method(:"#{name}=") { |value| self[name] = value } unless %i[message history].include?(name)
end

#respond_to_missing?(name, include_private = false) ⇒ Boolean

:nodoc:

Returns:

  • (Boolean)


222
223
224
225
# File 'lib/little_ghost/invocation.rb', line 222

def respond_to_missing?(name, include_private = false) # :nodoc:
  value = name.to_s
  value.end_with?("=") || key?(value) || super
end

#to_hObject

Produces a mutable copy of the invocation environment.

Nested hashes, arrays, strings, and other duplicable values are copied.



185
# File 'lib/little_ghost/invocation.rb', line 185

def to_h = duplicate_value(env)