Class: SolidObjects::Actor

Inherits:
Object
  • Object
show all
Defined in:
lib/solid_objects/actor.rb,
sig/generated/lib/solid_objects/actor.rbs

Direct Known Subclasses

Doctor::ProbeActor

Defined Under Namespace

Classes: CommitActionIntent, EffectIntent, OutboundMessageIntent, ReminderIntent

Instance Attribute Summary collapse

Class Method Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(actor_id:, state:) ⇒ Actor

Returns a new instance of Actor.

RBS:

  • (actor_id: String, state: State) -> void

Parameters:

  • actor_id: (String)
  • state: (State)


142
143
144
145
146
147
148
149
# File 'lib/solid_objects/actor.rb', line 142

def initialize(actor_id:, state:)
  @actor_id = actor_id
  @state = state
  @effect_intents = []
  @commit_action_intents = []
  @reminder_intents = []
  @outbound_message_intents = []
end

Instance Attribute Details

#actor_idObject (readonly)

RBS:

  • @actor_id: String

  • @state: State

  • @effect_intents: Array[EffectIntent]

  • @commit_action_intents: Array[CommitActionIntent]

  • @reminder_intents: Array[ReminderIntent]

  • @outbound_message_intents: Array[OutboundMessageIntent]

Returns:

  • (Object)


139
140
141
# File 'lib/solid_objects/actor.rb', line 139

def actor_id
  @actor_id
end

#commit_action_intentsObject (readonly)

Returns the value of attribute commit_action_intents.

Returns:

  • (Object)


308
309
310
# File 'lib/solid_objects/actor.rb', line 308

def commit_action_intents
  @commit_action_intents
end

#effect_intentsObject (readonly)

Returns the value of attribute effect_intents.

Returns:

  • (Object)


308
309
310
# File 'lib/solid_objects/actor.rb', line 308

def effect_intents
  @effect_intents
end

#outbound_message_intentsObject (readonly)

Returns the value of attribute outbound_message_intents.

Returns:

  • (Object)


308
309
310
# File 'lib/solid_objects/actor.rb', line 308

def outbound_message_intents
  @outbound_message_intents
end

#reminder_intentsObject (readonly)

Returns the value of attribute reminder_intents.

Returns:

  • (Object)


308
309
310
# File 'lib/solid_objects/actor.rb', line 308

def reminder_intents
  @reminder_intents
end

#stateObject (readonly)

RBS:

  • @actor_id: String

  • @state: State

  • @effect_intents: Array[EffectIntent]

  • @commit_action_intents: Array[CommitActionIntent]

  • @reminder_intents: Array[ReminderIntent]

  • @outbound_message_intents: Array[OutboundMessageIntent]

Returns:

  • (Object)


139
140
141
# File 'lib/solid_objects/actor.rb', line 139

def state
  @state
end

Class Method Details

.actor_type(value = nil) ⇒ String

RBS:

  • (?String | Symbol) -> String

Parameters:

  • (String, Symbol)

Returns:

  • (String)


19
20
21
22
23
24
25
26
# File 'lib/solid_objects/actor.rb', line 19

def actor_type(value = nil)
  if value
    @actor_type = value.to_s
    SolidObjects.register_actor(@actor_type, self)
  end

  @actor_type ||= default_actor_type
end

.attribute(name, default: nil) ⇒ StateDefinition::Attribute

RBS:

  • (Symbol | String, ?default: untyped) -> StateDefinition::Attribute

Parameters:

  • (Symbol, String)
  • default: (Object) (defaults to: nil)

Returns:



29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
# File 'lib/solid_objects/actor.rb', line 29

def attribute(name, default: nil)
  attribute_name = name.to_sym
  ensure_attribute_methods_available!(attribute_name)
  attribute = definition.add_attribute(attribute_name, default:)
  @defining_attribute_methods = true
  begin
    define_method(attribute_name) { state.fetch(attribute_name) }
    define_method(:"#{attribute_name}=") { |value| state.write(attribute_name, value) }
    generated_attribute_methods[attribute_name] = true
    generated_attribute_methods[:"#{attribute_name}="] = true
  ensure
    @defining_attribute_methods = false
  end
  attribute
end

.definitionActorDefinition

RBS:

  • () -> ActorDefinition

Returns:



90
91
92
# File 'lib/solid_objects/actor.rb', line 90

def definition
  (@definition ||= ActorDefinition.new).synchronize_instance_messages(self)
end

.ensure_registered!Class

RBS:

  • () -> Class

Returns:

  • (Class)


95
96
97
# File 'lib/solid_objects/actor.rb', line 95

def ensure_registered!
  SolidObjects.register_actor(actor_type, self)
end

.inherited(subclass) ⇒ void

This method returns an undefined value.

RBS:

  • (Class) -> void

Parameters:

  • (Class)


12
13
14
15
16
# File 'lib/solid_objects/actor.rb', line 12

def inherited(subclass)
  super
  subclass.instance_variable_set(:@definition, definition.duplicate)
  subclass.instance_variable_set(:@generated_attribute_methods, generated_attribute_methods.dup)
end

.message(name) {|arg0, arg1| ... } ⇒ ActorDefinition::Handler

RBS:

  • (Symbol | String) { (*untyped, **untyped) -> untyped } -> ActorDefinition::Handler

Parameters:

  • (Symbol, String)

Yields:

Yield Parameters:

  • arg0 (Object)
  • arg1 (Object)

Yield Returns:

  • (Object)

Returns:



46
47
48
# File 'lib/solid_objects/actor.rb', line 46

def message(name, &block)
  definition.add_message(name, block)
end

.migrate_state(from:, to:) {|arg0| ... } ⇒ ActorDefinition::StateMigration

RBS:

  • (from: Integer, to: Integer) { (Hash[String, untyped]) -> Hash[String, untyped] } -> ActorDefinition::StateMigration

Parameters:

  • from: (Integer)
  • to: (Integer)

Yields:

Yield Parameters:

  • arg0 (Hash[String, untyped])

Yield Returns:

  • (Hash[String, untyped])

Returns:



67
68
69
# File 'lib/solid_objects/actor.rb', line 67

def migrate_state(from:, to:, &block)
  definition.add_state_migration(from, to, block)
end

.observable(name, &block) ⇒ void

This method returns an undefined value.

RBS:

  • (Symbol | String) ?{ () -> untyped } -> ActorDefinition::Handler

Parameters:

  • (Symbol, String)


56
57
58
# File 'lib/solid_objects/actor.rb', line 56

def observable(name, &block)
  definition.add_observable(name, block)
end

.on_activate { ... } ⇒ Proc

RBS:

  • () { () -> untyped } -> Proc

Yields:

Yield Returns:

  • (Object)

Returns:

  • (Proc)


72
73
74
75
# File 'lib/solid_objects/actor.rb', line 72

def on_activate(&block)
  definition.activation_hooks << block
  block
end

.on_deactivate { ... } ⇒ Proc

RBS:

  • () { () -> untyped } -> Proc

Yields:

Yield Returns:

  • (Object)

Returns:

  • (Proc)


78
79
80
81
# File 'lib/solid_objects/actor.rb', line 78

def on_deactivate(&block)
  definition.deactivation_hooks << block
  block
end

.query(name) {|arg0, arg1| ... } ⇒ ActorDefinition::Handler

RBS:

  • (Symbol | String) { (*untyped, **untyped) -> untyped } -> ActorDefinition::Handler

Parameters:

  • (Symbol, String)

Yields:

Yield Parameters:

  • arg0 (Object)
  • arg1 (Object)

Yield Returns:

  • (Object)

Returns:



51
52
53
# File 'lib/solid_objects/actor.rb', line 51

def query(name, &block)
  definition.add_query(name, block)
end

.ref(actor_id) ⇒ Reference

RBS:

  • (String | Integer) -> Reference

Parameters:

  • (String, Integer)

Returns:



84
85
86
87
# File 'lib/solid_objects/actor.rb', line 84

def ref(actor_id)
  ensure_registered!
  Reference.new(actor_type:, actor_id: actor_id.to_s)
end

.state_version(version = nil) ⇒ Integer

RBS:

  • (?Integer) -> Integer

Parameters:

  • (Integer)

Returns:

  • (Integer)


61
62
63
64
# File 'lib/solid_objects/actor.rb', line 61

def state_version(version = nil)
  definition.set_state_version(version) if version
  definition.state_version
end

Instance Method Details

#activatevoid

This method returns an undefined value.

RBS:

  • () -> void



253
254
255
256
257
# File 'lib/solid_objects/actor.rb', line 253

def activate
  guard_application_writes("on_activate") do
    self.class.definition.activation_hooks.each { |hook| instance_exec(&hook) }
  end
end

#commit_action(name, **arguments) ⇒ nil

RBS:

  • (Symbol | String, **untyped) -> nil

Parameters:

  • (Symbol, String)
  • (Object)

Returns:

  • (nil)


182
183
184
185
186
187
188
189
190
# File 'lib/solid_objects/actor.rb', line 182

def commit_action(name, **arguments)
  CommitActionIntent.new(
    name: name.to_s,
    arguments: Serialization.dump(arguments)
  ).tap do |intent|
    commit_action_intents << intent
  end
  nil
end

#current_messageMessageContext?

RBS:

  • () -> MessageContext?

Returns:



152
153
154
# File 'lib/solid_objects/actor.rb', line 152

def current_message
  Context.current_message
end

#deactivatevoid

This method returns an undefined value.

RBS:

  • () -> void



260
261
262
263
264
# File 'lib/solid_objects/actor.rb', line 260

def deactivate
  guard_application_writes("on_deactivate") do
    self.class.definition.deactivation_hooks.each { |hook| instance_exec(&hook) }
  end
end

#discard_intentsvoid

This method returns an undefined value.

RBS:

  • () -> void



299
300
301
302
303
304
# File 'lib/solid_objects/actor.rb', line 299

def discard_intents
  effect_intents.clear
  commit_action_intents.clear
  reminder_intents.clear
  outbound_message_intents.clear
end

#drain_commit_action_intentsArray[CommitActionIntent]

RBS:

  • () -> Array[CommitActionIntent]

Returns:



284
285
286
# File 'lib/solid_objects/actor.rb', line 284

def drain_commit_action_intents
  commit_action_intents.shift(commit_action_intents.length)
end

#drain_effect_intentsArray[EffectIntent]

RBS:

  • () -> Array[EffectIntent]

Returns:



279
280
281
# File 'lib/solid_objects/actor.rb', line 279

def drain_effect_intents
  effect_intents.shift(effect_intents.length)
end

#drain_outbound_message_intentsArray[OutboundMessageIntent]

RBS:

  • () -> Array[OutboundMessageIntent]

Returns:



294
295
296
# File 'lib/solid_objects/actor.rb', line 294

def drain_outbound_message_intents
  outbound_message_intents.shift(outbound_message_intents.length)
end

#drain_reminder_intentsArray[ReminderIntent]

RBS:

  • () -> Array[ReminderIntent]

Returns:



289
290
291
# File 'lib/solid_objects/actor.rb', line 289

def drain_reminder_intents
  reminder_intents.shift(reminder_intents.length)
end

#emit(name, on_success: nil, on_failure: nil, **arguments) ⇒ nil

RBS:

  • (Symbol | String, ?on_success: Symbol | String?, ?on_failure: Symbol | String?, **untyped) -> nil

Parameters:

  • (Symbol, String)
  • on_success: (Symbol, String, nil) (defaults to: nil)
  • on_failure: (Symbol, String, nil) (defaults to: nil)
  • (Object)

Returns:

  • (nil)


167
168
169
170
171
172
173
174
175
176
177
178
179
# File 'lib/solid_objects/actor.rb', line 167

def emit(name, on_success: nil, on_failure: nil, **arguments)
  validate_effect_callback!(on_success)
  validate_effect_callback!(on_failure)
  EffectIntent.new(
    name: name.to_s,
    arguments: Serialization.dump(arguments),
    success_message_name: on_success&.to_s,
    failure_message_name: on_failure&.to_s
  ).tap do |intent|
    effect_intents << intent
  end
  nil
end

#guard_application_writes(operation) { ... } ⇒ Object

RBS:

  • (String) { () -> untyped } -> untyped

Parameters:

  • (String)

Yields:

Yield Returns:

  • (Object)

Returns:

  • (Object)


314
315
316
317
318
319
320
321
# File 'lib/solid_objects/actor.rb', line 314

def guard_application_writes(operation, &block)
  ApplicationWriteGuard.call(
    actor_type: self.class.actor_type,
    actor_id:,
    operation:,
    &block
  )
end

#invoke(message_name, arguments) ⇒ Object

RBS:

  • (Symbol | String, Hash[String, untyped]) -> untyped

Parameters:

  • (Symbol, String)
  • (Hash[String, untyped])

Returns:

  • (Object)


222
223
224
225
226
227
228
229
230
# File 'lib/solid_objects/actor.rb', line 222

def invoke(message_name, arguments)
  handler = self.class.definition.messages[message_name.to_sym] ||
    self.class.definition.queries[message_name.to_sym]
  raise UnknownMessage, "unknown message #{message_name.inspect} for #{self.class.actor_type}" unless handler

  guard_application_writes(message_name.to_s) do
    instance_exec(**keyword_arguments(arguments), &handler.block)
  end
end

#keyword_arguments(arguments) ⇒ Hash[Symbol, untyped]

RBS:

  • (Hash[String, untyped]) -> Hash[Symbol, untyped]

Parameters:

  • (Hash[String, untyped])

Returns:

  • (Hash[Symbol, untyped])


324
325
326
# File 'lib/solid_objects/actor.rb', line 324

def keyword_arguments(arguments)
  arguments.each_with_object({}) { |(key, value), converted| converted[key.to_sym] = value }
end

#observable_value(name) ⇒ Object

RBS:

  • (Symbol | String) -> untyped

Parameters:

  • (Symbol, String)

Returns:

  • (Object)


242
243
244
245
246
247
248
249
250
# File 'lib/solid_objects/actor.rb', line 242

def observable_value(name)
  observable_name = name.to_sym
  handler = self.class.definition.observables[observable_name]
  raise UnknownMessage, "unknown observable #{name.inspect}" unless handler

  guard_application_writes("observable.#{observable_name}") do
    Serialization.dump(instance_exec(&handler.block))
  end
end

#observable_valuesHash[String, untyped]

RBS:

  • () -> Hash[String, untyped]

Returns:

  • (Hash[String, untyped])


233
234
235
236
237
238
239
# File 'lib/solid_objects/actor.rb', line 233

def observable_values
  guard_application_writes("observables") do
    self.class.definition.observables.each_with_object({}) do |(name, handler), values|
      values[name.to_s] = Serialization.dump(instance_exec(&handler.block))
    end
  end
end

#reject(code, message, details: {}) ⇒ bot

RBS:

  • (Symbol | String, String, ?details: Hash[String | Symbol, untyped]) -> bot

Parameters:

  • (Symbol, String)
  • (String)
  • details: (Hash[String | Symbol, untyped]) (defaults to: {})

Returns:

  • (bot)


157
158
159
160
161
162
163
164
# File 'lib/solid_objects/actor.rb', line 157

def reject(code, message, details: {})
  rejection_code = code.to_s
  unless rejection_code.match?(/\A[a-z][a-z0-9_]*\z/)
    raise ArgumentError, "rejection code must contain lowercase letters, digits, and underscores"
  end

  raise Rejected.new(code: rejection_code, message:, details:)
end

#schedule(name, at:, every: nil, missed: :latest, arguments: {}) ⇒ nil

RBS:

  • (Symbol | String, at: Time, ?every: Numeric?, ?missed: Symbol | String, arguments: Hash[Symbol | String, untyped]) -> nil

Parameters:

  • (Symbol, String)
  • at: (Time)
  • arguments: (Hash[Symbol | String, untyped]) (defaults to: {})
  • every: (Numeric, nil) (defaults to: nil)
  • missed: (Symbol, String) (defaults to: :latest)

Returns:

  • (nil)


193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
# File 'lib/solid_objects/actor.rb', line 193

def schedule(name, at:, every: nil, missed: :latest, arguments: {})
  interval_seconds = every&.to_f
  if interval_seconds && !interval_seconds.positive?
    raise ArgumentError, "reminder interval must be positive"
  end
  missed_policy = missed.to_s
  unless %w[all latest].include?(missed_policy)
    raise ArgumentError, "missed reminder policy must be all or latest"
  end

  ReminderIntent.new(
    name: name.to_s,
    at:,
    arguments: Serialization.dump(arguments),
    interval_seconds:,
    missed_policy:
  ).tap do |intent|
    reminder_intents << intent
  end
  nil
end

#send_to(reference, message_name, available_at: nil, idempotency_key: nil, **arguments) ⇒ nil

RBS:

  • (Reference, Symbol | String, ?available_at: Time?, ?idempotency_key: String?, **untyped) -> nil

Parameters:

  • (Reference)
  • (Symbol, String)
  • available_at: (Time, nil) (defaults to: nil)
  • idempotency_key: (String, nil) (defaults to: nil)
  • (Object)

Returns:

  • (nil)


216
217
218
219
# File 'lib/solid_objects/actor.rb', line 216

def send_to(reference, message_name, available_at: nil, idempotency_key: nil, **arguments)
  stage_outbound_message(reference, message_name, arguments, available_at:, idempotency_key:)
  nil
end

#stage_outbound_message(reference, message_name, arguments, available_at: nil, idempotency_key: nil) ⇒ OutboundMessageIntent

RBS:

  • (Reference, Symbol | String, Hash[Symbol | String, untyped], ?available_at: Time?, idempotency_key: String?) -> OutboundMessageIntent

Parameters:

  • (Reference)
  • (Symbol, String)
  • (Hash[Symbol | String, untyped])
  • idempotency_key: (String, nil) (defaults to: nil)
  • available_at: (Time, nil) (defaults to: nil)

Returns:



267
268
269
270
271
272
273
274
275
276
# File 'lib/solid_objects/actor.rb', line 267

def stage_outbound_message(reference, message_name, arguments, available_at: nil, idempotency_key: nil)
  OutboundMessageIntent.new(
    actor_type: reference.actor_type,
    actor_id: reference.actor_id,
    message_name: message_name.to_s,
    arguments: Serialization.dump(arguments),
    available_at:,
    idempotency_key:
  ).tap { |intent| outbound_message_intents << intent }
end

#validate_effect_callback!(message_name) ⇒ void

This method returns an undefined value.

RBS:

  • (Symbol | String?) -> void

Parameters:

  • (Symbol, String, nil)


329
330
331
332
333
334
# File 'lib/solid_objects/actor.rb', line 329

def validate_effect_callback!(message_name)
  return unless message_name
  return if self.class.definition.messages.key?(message_name.to_sym)

  raise UnknownMessage, "unknown effect callback message #{message_name.inspect}"
end