solid_objects 0.14.5 → 0.14.7
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.
- checksums.yaml +4 -4
- data/CHANGELOG.md +18 -0
- data/README.md +6 -12
- data/Rakefile +1 -1
- data/docs/architecture.md +77 -2
- data/docs/correctness.md +24 -1
- data/docs/development.md +110 -0
- data/docs/operations.md +6 -0
- data/docs/reminders.md +8 -0
- data/docs/roadmap.md +6 -0
- data/lib/solid_objects/actor_signatures.rb +80 -0
- data/lib/solid_objects/database_adapter.rb +4 -0
- data/lib/solid_objects/database_adapters/sqlite.rb +4 -0
- data/lib/solid_objects/effect_executor.rb +11 -15
- data/lib/solid_objects/effect_payload.rb +27 -0
- data/lib/solid_objects/errors.rb +17 -0
- data/lib/solid_objects/executor.rb +9 -1
- data/lib/solid_objects/synchronous_invocation.rb +2 -0
- data/lib/solid_objects/version.rb +1 -1
- data/lib/solid_objects/worker.rb +2 -0
- data/lib/solid_objects.rb +1 -0
- data/sig/generated/lib/solid_objects/actor_signatures.rbs +24 -0
- data/sig/generated/lib/solid_objects/effect_payload.rbs +14 -0
- data/sig/generated/lib/solid_objects/errors.rbs +12 -0
- data/sig/generated/lib/solid_objects/executor.rbs +2 -0
- data/sig/public/effect_payload.rbs +19 -0
- metadata +7 -2
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: d36e77947a8de94216c9174cc3a34a878803bdf242efa0b10f6067d48b4e7cda
|
|
4
|
+
data.tar.gz: 178955e77093552df5569ee5c0badfb6035a51a479c7bcfbd1025465ac5947a4
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 491698862debab8266650dbdc21a86561d3fa7dd51764edd469a7b5173a1b2a24a2542d39a584796d3433a2bc66794d7d5251ebfe73d7c991d539f2a4c92d287
|
|
7
|
+
data.tar.gz: 705ce841595e49cbc892535afd15fb96b8ac4134bb01a1c92de8d12061a6fe5717b745d989decf82fb1c25a19966b9e50a88afb30a1426c23055e1333c00f2cc
|
data/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,23 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## 0.14.7 - 2026-09-14
|
|
4
|
+
|
|
5
|
+
- Publish RBS contracts for effect callback envelopes and Ruby error summaries.
|
|
6
|
+
Check the runtime constructors and packaged consumer signatures strictly,
|
|
7
|
+
preserving ordinary hashes, keyword callbacks, serialization, and retries.
|
|
8
|
+
- Add optional actor-specific RBS generation for staged schedule/transmit calls
|
|
9
|
+
and effect callback names. Reuse application-declared operation argument types
|
|
10
|
+
without changing Ruby dispatch, runtime validation, or global effect registries.
|
|
11
|
+
|
|
12
|
+
## 0.14.6 - 2026-09-12
|
|
13
|
+
|
|
14
|
+
- Preserve committed turns when an Active Record after-commit callback raises.
|
|
15
|
+
Caller assistance and workers expose the original callback exception without
|
|
16
|
+
restoring obsolete state, retrying/rejecting the completed message, or
|
|
17
|
+
masking the error as a missing claim. Track actual SQL commitment before
|
|
18
|
+
SQLite retries and deadline translation; retain pre-commit rollback, retry,
|
|
19
|
+
and domain-rejection behavior.
|
|
20
|
+
|
|
3
21
|
## 0.14.5 - 2026-09-03
|
|
4
22
|
|
|
5
23
|
- Split broadcast claiming into separate pending and stale-processing probes,
|
data/README.md
CHANGED
|
@@ -110,15 +110,13 @@ bundle exec solid_objects start
|
|
|
110
110
|
```
|
|
111
111
|
|
|
112
112
|
Stop that process before the deadline and restart it afterwards. The reminder
|
|
113
|
-
is still in the Rails database and runs when the process returns.
|
|
114
|
-
`self.available += 1` a supervisor and excellent posture.
|
|
113
|
+
is still in the Rails database and runs when the process returns. The reminder survived the restart.
|
|
115
114
|
|
|
116
115
|
## Why this exists
|
|
117
116
|
|
|
118
117
|
The handwritten Rails version usually starts with `with_lock`. Then it gains an
|
|
119
118
|
`expires_at` column, a cron job, an Active Job retry policy, and an Action Cable
|
|
120
|
-
broadcast that must agree with the write. A small invariant has
|
|
121
|
-
tapestry of callbacks and scheduled cleanup.
|
|
119
|
+
broadcast that must agree with the write. A small invariant has spread across callbacks and scheduled cleanup.
|
|
122
120
|
|
|
123
121
|
This is complicated, hard to test, fragile and unnecessary.
|
|
124
122
|
|
|
@@ -133,14 +131,12 @@ PostgreSQL, or MySQL. Redis and a separate actor service are not required.
|
|
|
133
131
|
- Account, device, assessment, and approval workflows that survive deploys.
|
|
134
132
|
- Reactive ERB views that must follow committed actor revisions.
|
|
135
133
|
|
|
136
|
-
Different identities can run concurrently.
|
|
137
|
-
one actor ID and Rails will faithfully operate your new bottleneck.
|
|
134
|
+
Different identities can run concurrently. One actor ID for the whole application is a bottleneck.
|
|
138
135
|
|
|
139
136
|
## When a transaction is better
|
|
140
137
|
|
|
141
138
|
Often. If the entire invariant fits inside one request, use `with_lock`, a
|
|
142
|
-
database constraint, or a short transaction. A row lock
|
|
143
|
-
personal brand, and it is usually the clearest answer.
|
|
139
|
+
database constraint, or a short transaction. A row lock is usually the clearest answer.
|
|
144
140
|
|
|
145
141
|
Use Solid Objects when work must happen later, survive a restart, or stay
|
|
146
142
|
ordered across several requests or jobs. A plain counter remains one line of
|
|
@@ -171,15 +167,13 @@ Exactly once is not hiding in a more advanced configuration. Read the
|
|
|
171
167
|
- [Detailed documentation](docs/)
|
|
172
168
|
|
|
173
169
|
The dashboard, benchmarks, migration cookbook, schema, and exhaustive API
|
|
174
|
-
explanations remain in `docs/`. The README
|
|
175
|
-
robust interplay with its own table of contents.
|
|
170
|
+
explanations remain in `docs/`. The README stops here.
|
|
176
171
|
|
|
177
172
|
## Status and license
|
|
178
173
|
|
|
179
174
|
Solid Objects Ruby is a pre-1.0 early release. Its correctness core is tested
|
|
180
175
|
against SQLite, PostgreSQL, and MySQL, but the project makes no production-ready
|
|
181
|
-
claim. That requires more hardening and operational soak evidence.
|
|
182
|
-
not decorative punctuation.
|
|
176
|
+
claim. That requires more hardening and operational soak evidence. Expect breaking changes.
|
|
183
177
|
|
|
184
178
|
Solid Objects is released under the [MIT License](MIT-LICENSE). It is an
|
|
185
179
|
independent project and is not affiliated with, sponsored by, or endorsed by
|
data/Rakefile
CHANGED
|
@@ -14,7 +14,7 @@ task :rbs do
|
|
|
14
14
|
FileUtils.rm_rf(File.expand_path("sig/generated", __dir__))
|
|
15
15
|
sh "bundle exec rbs-inline --base lib --base app --output sig/generated lib app"
|
|
16
16
|
FileUtils.rm_f(File.expand_path("sig/generated/lib/generators/solid_objects/templates/solid_objects.rbs", __dir__))
|
|
17
|
-
sh "bundle exec rbs -I sig/generated -I sig/support validate"
|
|
17
|
+
sh "bundle exec rbs -I sig/generated -I sig/support -I sig/public validate"
|
|
18
18
|
end
|
|
19
19
|
|
|
20
20
|
desc "Run Standard Ruby"
|
data/docs/architecture.md
CHANGED
|
@@ -314,9 +314,30 @@ Any lease or message predicate failure raises `LostActivation` and rolls back ev
|
|
|
314
314
|
|
|
315
315
|
The state version can advance because of state migration even when the message itself makes no state change.
|
|
316
316
|
|
|
317
|
+
Active Record may raise from an application `after_commit` callback while the
|
|
318
|
+
transaction call unwinds, after SQL has committed. The adapter retains the
|
|
319
|
+
actual transaction object and checks its fully committed state before any
|
|
320
|
+
deadline conversion or SQLite retry. Reaching the end of the block, releasing
|
|
321
|
+
a savepoint, or observing an in-memory message update is not proof of SQL
|
|
322
|
+
commitment. A failing `before_commit` callback follows the rollback path.
|
|
323
|
+
|
|
324
|
+
An internal `CommittedTransactionError` carries the original error through
|
|
325
|
+
executor, SQLite retry, and coordination rescue handlers. The synchronous
|
|
326
|
+
invocation and worker boundaries re-raise the original exception with its
|
|
327
|
+
original backtrace and cause. The wrapper prevents a callback's `Rejected`,
|
|
328
|
+
`LostActivation`, or database error from being mistaken for a pre-commit
|
|
329
|
+
outcome. It is not a durable message error or a new delivery mechanism.
|
|
330
|
+
|
|
331
|
+
After commitment, actor state, application writes, message completion/result,
|
|
332
|
+
and outboxes remain durable. The executor preserves the committed activation
|
|
333
|
+
state and does not retry, reject, or dead-letter that turn. Caller assistance
|
|
334
|
+
still deactivates and releases its lease during cleanup; cached worker state
|
|
335
|
+
can process later messages. A worker running its continuous loop exposes the
|
|
336
|
+
error and runs its existing shutdown cleanup.
|
|
337
|
+
|
|
317
338
|
## Failure path
|
|
318
339
|
|
|
319
|
-
|
|
340
|
+
Before commitment, actor and commit-action exceptions roll back all in-memory changes by restoring the pre-turn state. A separate short transaction conditionally owned by the current generation:
|
|
320
341
|
|
|
321
342
|
- Stores a sanitized error
|
|
322
343
|
- Deletes claimed membership
|
|
@@ -403,9 +424,15 @@ outer commit, and callers timing out on work they indirectly block.
|
|
|
403
424
|
waiting and immediately returns a `MessageReference`. Runtime workers process
|
|
404
425
|
it normally.
|
|
405
426
|
|
|
427
|
+
An executing caller receives an inline after-commit callback error even though
|
|
428
|
+
the turn committed. An independently waiting caller observes the durable
|
|
429
|
+
result and may return before that callback raises in the worker. Completed
|
|
430
|
+
history is not changed retroactively; callback failures must be observed in
|
|
431
|
+
the executing process. Later Rails callbacks may not run after one raises.
|
|
432
|
+
|
|
406
433
|
## Domain rejection
|
|
407
434
|
|
|
408
|
-
|
|
435
|
+
Before commitment, actor code can call `reject` for a validation or business-rule outcome that
|
|
409
436
|
must not retry. The executor restores pre-turn state, discards staged intents,
|
|
410
437
|
stores the structured rejection, completes the claimed membership, and
|
|
411
438
|
continues with the next sequence in one fenced transaction. Synchronous callers
|
|
@@ -416,6 +443,10 @@ a dead letter.
|
|
|
416
443
|
|
|
417
444
|
`emit` creates a staged effect:
|
|
418
445
|
|
|
446
|
+
Typed applications can generate [actor-specific RBS signatures](development.md#actor-specific-dispatch-signatures)
|
|
447
|
+
to check callback names and staged `schedule`/`transmit` calls without changing
|
|
448
|
+
their Ruby syntax. Effect and commit-action registry names remain independent.
|
|
449
|
+
|
|
419
450
|
```ruby
|
|
420
451
|
emit(
|
|
421
452
|
:charge_payment,
|
|
@@ -456,6 +487,50 @@ and `result:`. A failure callback receives `effect_id:`, `arguments:`, and
|
|
|
456
487
|
`error:`, so an actor can correlate concurrent effects without storing a
|
|
457
488
|
separate callback ledger.
|
|
458
489
|
|
|
490
|
+
### Typing your on_failure handler
|
|
491
|
+
|
|
492
|
+
The gem ships `SolidObjects::effect_error`,
|
|
493
|
+
`SolidObjects::effect_failure_payload[Arguments]`, and
|
|
494
|
+
`SolidObjects::effect_success_payload[Arguments, Result]` as public RBS aliases.
|
|
495
|
+
They describe the existing string-keyed hashes; they are not Ruby wrapper classes.
|
|
496
|
+
See [signature loading](development.md#public-effect-payload-signatures) for Steep setup.
|
|
497
|
+
|
|
498
|
+
For an actor with `generation` and `status` attributes, declare the callback keywords
|
|
499
|
+
in the application's RBS:
|
|
500
|
+
|
|
501
|
+
```rbs
|
|
502
|
+
class ChatRun < SolidObjects::Actor
|
|
503
|
+
type run_arguments = { "generation" => Integer }
|
|
504
|
+
def fail_turn: (effect_id: String, arguments: run_arguments, error: SolidObjects::effect_error) -> void
|
|
505
|
+
end
|
|
506
|
+
```
|
|
507
|
+
|
|
508
|
+
```ruby
|
|
509
|
+
def fail_turn(effect_id:, arguments:, error:)
|
|
510
|
+
return unless arguments["generation"] == generation
|
|
511
|
+
|
|
512
|
+
self.status = "failed"
|
|
513
|
+
end
|
|
514
|
+
```
|
|
515
|
+
|
|
516
|
+
Record access with `arguments["generation"]` retains its declared `Integer` type.
|
|
517
|
+
The callback receives top-level keywords, while nested arguments and error keys
|
|
518
|
+
remain strings. The failure envelope requires `"effect_id"`, `"arguments"`, and
|
|
519
|
+
`"error"`; the success envelope replaces `"error"` with `"result"`. Empty original
|
|
520
|
+
arguments remain `{}`, and a success result may be `nil` or any supported JSON value.
|
|
521
|
+
Ruby errors contain `"class"` (`String?`, including anonymous exception classes),
|
|
522
|
+
`"message"` (`String`, limited to 8,192 bytes), and `"backtrace"` (`Array[String]`,
|
|
523
|
+
limited to 50 entries and possibly empty).
|
|
524
|
+
|
|
525
|
+
Applications supply the generic argument/result types to describe their serialized
|
|
526
|
+
JSON values. These aliases do not infer or validate independently registered effect
|
|
527
|
+
handlers. Their type parameters are deliberately unconstrained: Ruby serialization
|
|
528
|
+
accepts and normalizes values such as symbols, and RBS cannot express that conversion
|
|
529
|
+
as a generic bound. The constructors and consumer fixtures are checked with strict
|
|
530
|
+
Steep diagnostics. JavaScript exposes equivalent contracts with its existing
|
|
531
|
+
camelCase ID and `{ name, message }` error shape in
|
|
532
|
+
[solid-objects-js#47](https://github.com/cardmagic/solid-objects-js/issues/47).
|
|
533
|
+
|
|
459
534
|
A commit action is registered the same way and runs inside the short fenced
|
|
460
535
|
transaction:
|
|
461
536
|
|
data/docs/correctness.md
CHANGED
|
@@ -125,6 +125,29 @@ it is available only when Solid Objects and `ActiveRecord::Base` share one
|
|
|
125
125
|
connection pool. Commit actions must contain only bounded database work.
|
|
126
126
|
External I/O belongs in the effect outbox.
|
|
127
127
|
|
|
128
|
+
### Errors after SQL commit
|
|
129
|
+
|
|
130
|
+
Active Record `after_commit` callbacks registered by commit actions run after
|
|
131
|
+
SQL commitment. If one raises, the application writes, actor state, message
|
|
132
|
+
result/completion, and claimed-membership deletion remain committed. The
|
|
133
|
+
executor does not restore the pre-turn snapshot, retry the business action,
|
|
134
|
+
reject the message, or create a dead letter. A `before_commit` callback can
|
|
135
|
+
still roll back everything even after the fenced transaction block finishes.
|
|
136
|
+
|
|
137
|
+
The executing synchronous caller or `Worker#run_once` receives the original
|
|
138
|
+
callback exception, including its identity, backtrace, and cause. This also
|
|
139
|
+
applies when the callback raises `Rejected`, `LostActivation`, or a database
|
|
140
|
+
deadline/lock error: its class does not change a committed turn into a failed
|
|
141
|
+
one. Synchronous cleanup releases the activation; a worker retains the
|
|
142
|
+
committed state until normal deactivation or shutdown, so later messages can
|
|
143
|
+
continue from that state.
|
|
144
|
+
|
|
145
|
+
A separate waiting caller can observe the durable result before the callback
|
|
146
|
+
finishes. There is no retroactive failure delivery or durable callback-error
|
|
147
|
+
result. Inspect and report errors in the executing process. Rails may skip
|
|
148
|
+
later callbacks when one raises; these callbacks are not a durable delivery
|
|
149
|
+
mechanism. Use an idempotent effect for work that needs independent retries.
|
|
150
|
+
|
|
128
151
|
## Reactive components
|
|
129
152
|
|
|
130
153
|
A successful fenced turn advances `instances.state_revision` to that message's
|
|
@@ -205,7 +228,7 @@ wakes the caller, and raises `SolidObjects::ActorDestroyed`.
|
|
|
205
228
|
|
|
206
229
|
## Domain rejection
|
|
207
230
|
|
|
208
|
-
`reject` is a terminal domain outcome, not an infrastructure failure. It rolls
|
|
231
|
+
`reject` before commitment is a terminal domain outcome, not an infrastructure failure. It rolls
|
|
209
232
|
back in-memory state and staged intents, stores a structured rejection on the
|
|
210
233
|
message, removes claimed membership, and lets the next sequence run. It is
|
|
211
234
|
never retried or dead-lettered. The synchronous caller receives
|
data/docs/development.md
CHANGED
|
@@ -103,6 +103,116 @@ bundle exec rake rbs
|
|
|
103
103
|
|
|
104
104
|
This follows the inline convention used by `cardmagic/classifier`.
|
|
105
105
|
|
|
106
|
+
### Public effect payload signatures
|
|
107
|
+
|
|
108
|
+
The packaged `sig/public` directory owns the reusable effect payload aliases and
|
|
109
|
+
survives `rake rbs` regeneration. A host application's Steep target can load the
|
|
110
|
+
installed gem's complete signature tree alongside its own signatures:
|
|
111
|
+
|
|
112
|
+
```ruby
|
|
113
|
+
target :app do
|
|
114
|
+
library "solid_objects"
|
|
115
|
+
signature "sig"
|
|
116
|
+
check "app/actors"
|
|
117
|
+
configure_code_diagnostics(Diagnostic::Ruby.strict)
|
|
118
|
+
end
|
|
119
|
+
```
|
|
120
|
+
|
|
121
|
+
This requires no internal runtime imports. See the
|
|
122
|
+
[typed callback example](architecture.md#typing-your-on_failure-handler).
|
|
123
|
+
The gem's strict payload target checks the actual constructors; its packaged
|
|
124
|
+
consumer test also verifies that missing keys and incorrect field types fail.
|
|
125
|
+
|
|
126
|
+
### Actor-specific dispatch signatures
|
|
127
|
+
|
|
128
|
+
Typed applications can opt into `SolidObjects::ActorSignatures` to check ordinary
|
|
129
|
+
`schedule`, `transmit`, and effect callback names inside actor methods. Add `rbs`
|
|
130
|
+
and `steep` to the application's development dependencies. This tool is loaded
|
|
131
|
+
explicitly and is not required by workers or ordinary Ruby applications.
|
|
132
|
+
|
|
133
|
+
Declare application operation types first, including inherited operations and
|
|
134
|
+
block-defined `message` operations. Inline RBS can generate this input, or keep
|
|
135
|
+
handwritten declarations in a separate directory such as `sig/actors`:
|
|
136
|
+
|
|
137
|
+
```rbs
|
|
138
|
+
class ChatRun < SolidObjects::Actor
|
|
139
|
+
def recover_if_stuck: (generation: Integer) -> nil
|
|
140
|
+
def fail_turn: (effect_id: String, arguments: Hash[String, untyped], error: Hash[String, untyped]) -> nil
|
|
141
|
+
def start: () -> nil
|
|
142
|
+
end
|
|
143
|
+
```
|
|
144
|
+
|
|
145
|
+
After loading the application's actor classes, generate a separate output file:
|
|
146
|
+
|
|
147
|
+
```ruby
|
|
148
|
+
require "solid_objects/actor_signatures"
|
|
149
|
+
|
|
150
|
+
Rails.application.reloader.wrap do
|
|
151
|
+
signatures = SolidObjects::ActorSignatures.generate(
|
|
152
|
+
actors: [ChatRun],
|
|
153
|
+
signatures: [Rails.root.join("sig/actors").to_s]
|
|
154
|
+
)
|
|
155
|
+
FileUtils.mkdir_p(Rails.root.join("sig/generated"))
|
|
156
|
+
File.write(Rails.root.join("sig/generated/solid_objects.rbs"), signatures)
|
|
157
|
+
end
|
|
158
|
+
```
|
|
159
|
+
|
|
160
|
+
Run that script with `bin/rails runner` during development or CI. Outside Rails,
|
|
161
|
+
require the actor definitions and call `generate` directly. The generator returns
|
|
162
|
+
a string and does not write files, execute actor operations, start workers, or run
|
|
163
|
+
migrations. Rails boot follows the application's normal loading configuration;
|
|
164
|
+
the explicit actor list resolves its autoloaded classes within the reloader boundary.
|
|
165
|
+
Keep generated output out of the input signature paths, and regenerate after a
|
|
166
|
+
message is renamed or removed. Output is deterministic; handwritten signatures
|
|
167
|
+
remain separate.
|
|
168
|
+
|
|
169
|
+
Load the gem and both application signature directories in `Steepfile`:
|
|
170
|
+
|
|
171
|
+
```ruby
|
|
172
|
+
target :actors do
|
|
173
|
+
library "solid_objects"
|
|
174
|
+
signature "sig/actors"
|
|
175
|
+
signature "sig/generated"
|
|
176
|
+
check "app/actors"
|
|
177
|
+
configure_code_diagnostics(Diagnostic::Ruby.strict)
|
|
178
|
+
end
|
|
179
|
+
```
|
|
180
|
+
|
|
181
|
+
The usual Ruby code now passes Steep without a dispatcher cast:
|
|
182
|
+
|
|
183
|
+
```ruby
|
|
184
|
+
schedule(at: Time.now, key: "watchdog").recover_if_stuck(generation: 1)
|
|
185
|
+
transmit.recover_if_stuck(generation: 1)
|
|
186
|
+
emit :run_model, generation: 1, on_failure: :fail_turn
|
|
187
|
+
```
|
|
188
|
+
|
|
189
|
+
Misspelled operations/callbacks, queries, attributes, private methods, infrastructure
|
|
190
|
+
methods, and incorrect keyword arguments fail the strict check. Both string and
|
|
191
|
+
symbol callback literals work. Staging returns `nil` even when an operation's own
|
|
192
|
+
return type differs. Effect and commit-action names remain global registry names;
|
|
193
|
+
registry contract inference is separate work.
|
|
194
|
+
|
|
195
|
+
Reflection supplies message names only. Values come from declared RBS signatures;
|
|
196
|
+
missing signatures and positional/block arguments are rejected. Block-defined
|
|
197
|
+
messages require explicit method declarations in RBS; annotate their block-local
|
|
198
|
+
values separately when checking the block body. Generic actor classes currently
|
|
199
|
+
require application-owned dispatcher signatures. The generator preserves method
|
|
200
|
+
overloads and method type parameters.
|
|
201
|
+
|
|
202
|
+
Deliberately dynamic names can use Ruby's explicit dynamic dispatch:
|
|
203
|
+
|
|
204
|
+
```ruby
|
|
205
|
+
schedule(at: Time.now).public_send(operation_name, generation: generation)
|
|
206
|
+
public_send(:emit, :run_model, on_failure: callback_name, generation: generation)
|
|
207
|
+
```
|
|
208
|
+
|
|
209
|
+
Those calls opt out of name/argument checking and retain the existing runtime
|
|
210
|
+
validation. Ordinary calls on the generated dispatcher have no string-name fallback.
|
|
211
|
+
`send_to`, `Reference#async`, direct calls, and queries retain their existing
|
|
212
|
+
signatures and runtime behavior; this generator does not provide complete reference
|
|
213
|
+
typing or Sorbet/Tapioca actor-specific RBI generation. The corresponding TypeScript
|
|
214
|
+
work is tracked in [solid-objects-js#46](https://github.com/cardmagic/solid-objects-js/issues/46).
|
|
215
|
+
|
|
106
216
|
## Formatting and security
|
|
107
217
|
|
|
108
218
|
```bash
|
data/docs/operations.md
CHANGED
|
@@ -69,6 +69,12 @@ Rails schema migrations and actor state migrations are separate concerns.
|
|
|
69
69
|
|
|
70
70
|
### Host application tooling
|
|
71
71
|
|
|
72
|
+
RBS/Steep applications can generate
|
|
73
|
+
[actor-specific dispatch signatures](development.md#actor-specific-dispatch-signatures)
|
|
74
|
+
for reminders, transmit calls, and effect callback names. This is optional development
|
|
75
|
+
tooling; generated signatures do not change runtime dispatch. The generator does
|
|
76
|
+
not supply equivalent Sorbet/Tapioca actor-specific types.
|
|
77
|
+
|
|
72
78
|
Installed engine migrations are copied as
|
|
73
79
|
`db/migrate/*_create_solid_objects_tables.solid_objects.rb`. If the host enables
|
|
74
80
|
`Rails/CreateTableWithTimestamps`, exclude engine-owned migrations rather than
|
data/docs/reminders.md
CHANGED
|
@@ -39,6 +39,14 @@ raises, and nothing is logged except a `solid_objects.reminder.replaced` event.
|
|
|
39
39
|
|
|
40
40
|
## An alarm per item, with `key:`
|
|
41
41
|
|
|
42
|
+
For watchdogs paired with an effect's give-up callback, see
|
|
43
|
+
[typing your `on_failure` handler](architecture.md#typing-your-on_failure-handler)
|
|
44
|
+
to retain the original argument types without repeating the error hash contract.
|
|
45
|
+
|
|
46
|
+
For static checking of watchdog operation names and keyword arguments, opt into
|
|
47
|
+
[actor-specific RBS signatures](development.md#actor-specific-dispatch-signatures).
|
|
48
|
+
The Ruby `schedule(...).recover_if_stuck(generation: ...)` syntax stays the same.
|
|
49
|
+
|
|
42
50
|
Pass `key:` when an actor is waiting on several things at once. The key is your
|
|
43
51
|
own identifier for the item, and it names that item's alarm, so each item gets
|
|
44
52
|
one:
|
data/docs/roadmap.md
CHANGED
|
@@ -14,6 +14,8 @@
|
|
|
14
14
|
dead letters, and tail retry
|
|
15
15
|
- Transactional effects with success/failure actor messages carrying the
|
|
16
16
|
originally staged arguments for callback correlation
|
|
17
|
+
- Public RBS effect success/failure envelopes and error records, checked against
|
|
18
|
+
the runtime constructors and a packaged consumer with strict Steep diagnostics
|
|
17
19
|
- Actor-to-actor asynchronous outbox delivery. Effects and broadcasts use
|
|
18
20
|
portable status rows with polling indexes and database check constraints on
|
|
19
21
|
status, which works on all three adapters; a future version may add narrow
|
|
@@ -65,6 +67,10 @@
|
|
|
65
67
|
gem's dependencies
|
|
66
68
|
- Inline RBS generation/validation, Steep, Standard Ruby, Solid Queue's exact
|
|
67
69
|
RuboCop policy, and a warning-free Brakeman scan
|
|
70
|
+
- Opt-in actor-specific RBS generation for schedule/transmit keyword arguments
|
|
71
|
+
and effect callback names, using application declarations and checked consumer
|
|
72
|
+
fixtures. Dynamic names remain an explicit escape hatch; complete reference
|
|
73
|
+
typing and actor-specific RBI generation are separate work
|
|
68
74
|
- Compatibility CI across the supported span: Ruby 3.3, 3.4, and 4.0 against
|
|
69
75
|
Rails 7.1, 7.2, 8.0, and 8.1, pinned through `RAILS_VERSION` so the advertised
|
|
70
76
|
range is verified rather than assumed. The compatibility job runs SQLite only;
|
|
@@ -0,0 +1,80 @@
|
|
|
1
|
+
# rbs_inline: enabled
|
|
2
|
+
|
|
3
|
+
require "solid_objects"
|
|
4
|
+
require "rbs"
|
|
5
|
+
require "pathname"
|
|
6
|
+
|
|
7
|
+
module SolidObjects
|
|
8
|
+
class ActorSignatures
|
|
9
|
+
# @rbs (actors: Array[Class], signatures: Array[String]) -> String
|
|
10
|
+
def self.generate(actors:, signatures:)
|
|
11
|
+
new(signatures:).generate(actors:)
|
|
12
|
+
end
|
|
13
|
+
|
|
14
|
+
# @rbs @builder: untyped
|
|
15
|
+
|
|
16
|
+
# @rbs (signatures: Array[String]) -> void
|
|
17
|
+
def initialize(signatures:)
|
|
18
|
+
loader = RBS::EnvironmentLoader.new
|
|
19
|
+
loader.add(path: Pathname.new(File.expand_path("../../sig", __dir__)))
|
|
20
|
+
signatures.sort.each { |path| loader.add(path: Pathname.new(path)) }
|
|
21
|
+
environment = RBS::Environment.from_loader(loader).resolve_type_names
|
|
22
|
+
@builder = RBS::DefinitionBuilder.new(env: environment)
|
|
23
|
+
end
|
|
24
|
+
|
|
25
|
+
# @rbs (actors: Array[Class]) -> String
|
|
26
|
+
def generate(actors:)
|
|
27
|
+
actors.uniq.sort_by { |actor| actor.name.to_s }.map { |actor| actor_signature(actor) }.join("\n")
|
|
28
|
+
end
|
|
29
|
+
|
|
30
|
+
private
|
|
31
|
+
|
|
32
|
+
# @rbs (untyped) -> String
|
|
33
|
+
def actor_signature(actor)
|
|
34
|
+
unless actor < Actor && actor.name
|
|
35
|
+
raise ArgumentError, "actor signatures require named SolidObjects::Actor subclasses"
|
|
36
|
+
end
|
|
37
|
+
|
|
38
|
+
name = RBS::TypeName.parse("::#{actor.name}")
|
|
39
|
+
definition = @builder.build_instance(name)
|
|
40
|
+
if definition.type_params.any?
|
|
41
|
+
raise ArgumentError, "generic actor classes require application-owned dispatcher signatures"
|
|
42
|
+
end
|
|
43
|
+
messages = actor.definition.messages.keys.sort
|
|
44
|
+
methods = messages.map do |operation|
|
|
45
|
+
method = definition.methods[operation]
|
|
46
|
+
unless method && method.accessibility == :public
|
|
47
|
+
raise ArgumentError, "declare a public RBS signature for #{actor.name}##{operation}"
|
|
48
|
+
end
|
|
49
|
+
types = method.method_types.map { |type| staged_type(type, actor.name, operation).to_s }
|
|
50
|
+
" def #{operation}: #{types.join("\n | ")}"
|
|
51
|
+
end
|
|
52
|
+
callback_names = messages.flat_map { |operation| [ operation.inspect, operation.to_s.inspect ] }
|
|
53
|
+
callbacks = (callback_names + [ "nil" ]).join(" | ")
|
|
54
|
+
<<~RBS
|
|
55
|
+
class #{name}
|
|
56
|
+
interface _SolidObjectsOperations
|
|
57
|
+
#{methods.join("\n")}
|
|
58
|
+
def public_send: (Symbol | String, **untyped) -> nil
|
|
59
|
+
end
|
|
60
|
+
|
|
61
|
+
def schedule: (at: Time, ?every: Numeric?, ?missed: Symbol | String, ?key: (String | Symbol | Integer)?) -> #{name}::_SolidObjectsOperations
|
|
62
|
+
def transmit: () -> #{name}::_SolidObjectsOperations
|
|
63
|
+
def emit: (Symbol | String, ?on_success: (#{callbacks}), ?on_failure: (#{callbacks}), **untyped) -> nil
|
|
64
|
+
end
|
|
65
|
+
RBS
|
|
66
|
+
end
|
|
67
|
+
|
|
68
|
+
# @rbs (untyped, String, Symbol) -> untyped
|
|
69
|
+
def staged_type(method_type, actor_name, operation)
|
|
70
|
+
function = method_type.type
|
|
71
|
+
if !function.is_a?(RBS::Types::Function) || method_type.block ||
|
|
72
|
+
function.required_positionals.any? || function.optional_positionals.any? ||
|
|
73
|
+
function.rest_positionals || function.trailing_positionals.any?
|
|
74
|
+
raise ArgumentError, "#{actor_name}##{operation} must declare keyword-only arguments without a block"
|
|
75
|
+
end
|
|
76
|
+
|
|
77
|
+
method_type.update(type: function.update(return_type: RBS::Types::Bases::Nil.new(location: nil)))
|
|
78
|
+
end
|
|
79
|
+
end
|
|
80
|
+
end
|
|
@@ -123,17 +123,21 @@ module SolidObjects
|
|
|
123
123
|
|
|
124
124
|
# @rbs () { () -> untyped } -> untyped
|
|
125
125
|
def transaction(&block)
|
|
126
|
+
active_transaction = nil
|
|
126
127
|
raise DatabaseDeadlineExceeded, "synchronous invocation deadline expired" if SyncDeadline.expired?
|
|
127
128
|
|
|
128
129
|
with_connection do |connection|
|
|
129
130
|
with_transaction_deadline(connection) do
|
|
130
131
|
connection.transaction(requires_new: true) do
|
|
132
|
+
active_transaction = connection.current_transaction
|
|
131
133
|
configure_transaction_deadline(connection)
|
|
132
134
|
with_transaction_clock { block.call }
|
|
133
135
|
end
|
|
134
136
|
end
|
|
135
137
|
end
|
|
136
138
|
rescue => error
|
|
139
|
+
raise CommittedTransactionError.new(error) if active_transaction&.state&.fully_committed?
|
|
140
|
+
|
|
137
141
|
raise unless deadline_error?(error)
|
|
138
142
|
|
|
139
143
|
raise DatabaseDeadlineExceeded,
|
|
@@ -33,6 +33,8 @@ module SolidObjects
|
|
|
33
33
|
attempts = 0
|
|
34
34
|
begin
|
|
35
35
|
yield
|
|
36
|
+
rescue CommittedTransactionError
|
|
37
|
+
raise
|
|
36
38
|
rescue => error
|
|
37
39
|
raise unless busy_error?(error)
|
|
38
40
|
|
|
@@ -53,6 +55,8 @@ module SolidObjects
|
|
|
53
55
|
with_connection do |connection|
|
|
54
56
|
with_transaction_deadline(connection) { yield }
|
|
55
57
|
end
|
|
58
|
+
rescue CommittedTransactionError
|
|
59
|
+
raise
|
|
56
60
|
rescue DatabaseDeadlineExceeded
|
|
57
61
|
raise if SyncDeadline.expired?
|
|
58
62
|
|
|
@@ -184,11 +184,11 @@ module SolidObjects
|
|
|
184
184
|
effect: locked_effect,
|
|
185
185
|
operation: locked_effect.success_operation,
|
|
186
186
|
outcome: "success",
|
|
187
|
-
arguments:
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
187
|
+
arguments: EffectPayload.success(
|
|
188
|
+
effect_id: locked_effect.effect_id,
|
|
189
|
+
arguments: locked_effect.arguments,
|
|
190
|
+
result: serialized_result
|
|
191
|
+
)
|
|
192
192
|
)
|
|
193
193
|
locked_effect.update!(
|
|
194
194
|
status: "completed",
|
|
@@ -216,21 +216,17 @@ module SolidObjects
|
|
|
216
216
|
locked_effect = Effect.lock.find(effect.id)
|
|
217
217
|
verify_claim!(locked_effect)
|
|
218
218
|
dead = locked_effect.attempt_count >= locked_effect.max_attempts
|
|
219
|
-
error_details =
|
|
220
|
-
"class" => error.class.name,
|
|
221
|
-
"message" => error.message.to_s.byteslice(0, 8_192),
|
|
222
|
-
"backtrace" => Array(error.backtrace).first(50)
|
|
223
|
-
}
|
|
219
|
+
error_details = EffectPayload.error(error)
|
|
224
220
|
if dead
|
|
225
221
|
result_message = enqueue_result_message(
|
|
226
222
|
effect: locked_effect,
|
|
227
223
|
operation: locked_effect.failure_operation,
|
|
228
224
|
outcome: "failure",
|
|
229
|
-
arguments:
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
225
|
+
arguments: EffectPayload.failure(
|
|
226
|
+
effect_id: locked_effect.effect_id,
|
|
227
|
+
arguments: locked_effect.arguments,
|
|
228
|
+
error: error_details
|
|
229
|
+
)
|
|
234
230
|
)
|
|
235
231
|
end
|
|
236
232
|
locked_effect.update!(
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
# rbs_inline: enabled
|
|
2
|
+
|
|
3
|
+
module SolidObjects
|
|
4
|
+
module EffectPayload
|
|
5
|
+
class << self
|
|
6
|
+
# @rbs [Arguments, Result] (effect_id: String, arguments: Arguments, result: Result) -> effect_success_payload[Arguments, Result]
|
|
7
|
+
def success(effect_id:, arguments:, result:)
|
|
8
|
+
{ "effect_id" => effect_id, "arguments" => arguments, "result" => result }
|
|
9
|
+
end
|
|
10
|
+
|
|
11
|
+
# @rbs [Arguments] (effect_id: String, arguments: Arguments, error: effect_error) -> effect_failure_payload[Arguments]
|
|
12
|
+
def failure(effect_id:, arguments:, error:)
|
|
13
|
+
{ "effect_id" => effect_id, "arguments" => arguments, "error" => error }
|
|
14
|
+
end
|
|
15
|
+
|
|
16
|
+
# @rbs (Exception) -> effect_error
|
|
17
|
+
def error(exception)
|
|
18
|
+
message = exception.message.to_s.byteslice(0, 8_192) # : String
|
|
19
|
+
{
|
|
20
|
+
"class" => exception.class.name,
|
|
21
|
+
"message" => message,
|
|
22
|
+
"backtrace" => Array(exception.backtrace).first(50)
|
|
23
|
+
}
|
|
24
|
+
end
|
|
25
|
+
end
|
|
26
|
+
end
|
|
27
|
+
end
|
data/lib/solid_objects/errors.rb
CHANGED
|
@@ -67,6 +67,23 @@ module SolidObjects
|
|
|
67
67
|
class DatabaseDeadlineExceeded < Error
|
|
68
68
|
end
|
|
69
69
|
|
|
70
|
+
class CommittedTransactionError < Error
|
|
71
|
+
# @rbs @original_error: StandardError
|
|
72
|
+
|
|
73
|
+
attr_reader :original_error
|
|
74
|
+
|
|
75
|
+
# @rbs (StandardError) -> void
|
|
76
|
+
def initialize(original_error)
|
|
77
|
+
@original_error = original_error
|
|
78
|
+
super(original_error.message)
|
|
79
|
+
end
|
|
80
|
+
|
|
81
|
+
# @rbs () -> bot
|
|
82
|
+
def reraise
|
|
83
|
+
raise original_error, cause: original_error.cause
|
|
84
|
+
end
|
|
85
|
+
end
|
|
86
|
+
|
|
70
87
|
class SyncEnqueueTimeout < Error
|
|
71
88
|
# @rbs @timeout: Numeric
|
|
72
89
|
# @rbs @actor_type: String
|
|
@@ -4,6 +4,7 @@ module SolidObjects
|
|
|
4
4
|
class Executor
|
|
5
5
|
# @rbs @activation: Activation
|
|
6
6
|
# @rbs @message: Message
|
|
7
|
+
# @rbs @completion_transaction: untyped
|
|
7
8
|
|
|
8
9
|
# @rbs (activation: Activation, message: Message) -> void
|
|
9
10
|
def initialize(activation:, message:)
|
|
@@ -35,7 +36,7 @@ module SolidObjects
|
|
|
35
36
|
state_changed: state_after.value != state_before
|
|
36
37
|
)
|
|
37
38
|
true
|
|
38
|
-
rescue LostActivation
|
|
39
|
+
rescue CommittedTransactionError, LostActivation
|
|
39
40
|
raise
|
|
40
41
|
rescue Rejected => rejection
|
|
41
42
|
activation.restore_state(state_before) if state_before
|
|
@@ -96,6 +97,7 @@ module SolidObjects
|
|
|
96
97
|
moved_reminders = []
|
|
97
98
|
|
|
98
99
|
activation.lease.fenced_transaction do |instance|
|
|
100
|
+
@completion_transaction = Record.connection.current_transaction
|
|
99
101
|
# A busy database makes the adapter retry this whole block, so an
|
|
100
102
|
# attempt that was rolled back must not leave its work in the lists the
|
|
101
103
|
# reporting below reads. Each attempt starts from empty.
|
|
@@ -154,6 +156,12 @@ module SolidObjects
|
|
|
154
156
|
report_large_state(state_after.byte_size)
|
|
155
157
|
SolidObjects.instrument_after_commit(:"message.completed", **instrumentation_payload)
|
|
156
158
|
SolidObjects.wake_up.signal
|
|
159
|
+
rescue CommittedTransactionError
|
|
160
|
+
raise
|
|
161
|
+
rescue => error
|
|
162
|
+
raise unless @completion_transaction&.state&.fully_committed?
|
|
163
|
+
|
|
164
|
+
raise CommittedTransactionError.new(error)
|
|
157
165
|
end
|
|
158
166
|
|
|
159
167
|
# @rbs (Integer) -> void
|
|
@@ -20,6 +20,8 @@ module SolidObjects
|
|
|
20
20
|
SyncDeadline.with(timeout:) do
|
|
21
21
|
call_before_deadline(message_reference, timeout:)
|
|
22
22
|
end
|
|
23
|
+
rescue CommittedTransactionError => error
|
|
24
|
+
error.reraise
|
|
23
25
|
rescue ActiveRecord::RecordNotFound
|
|
24
26
|
raise ActorDestroyed, "actor was destroyed while waiting for its result"
|
|
25
27
|
end
|
data/lib/solid_objects/worker.rb
CHANGED
|
@@ -59,6 +59,8 @@ module SolidObjects
|
|
|
59
59
|
).around { activation.drain }
|
|
60
60
|
release_activation(activation) if activation.pass_exhausted?
|
|
61
61
|
processed
|
|
62
|
+
rescue CommittedTransactionError => error
|
|
63
|
+
error.reraise
|
|
62
64
|
rescue ActorDestroyed
|
|
63
65
|
release_activation(activation) if activation
|
|
64
66
|
0
|
data/lib/solid_objects.rb
CHANGED
|
@@ -55,6 +55,7 @@ require "solid_objects/wake_up_adapters/redis"
|
|
|
55
55
|
require "solid_objects/wake_up_adapters"
|
|
56
56
|
require "solid_objects/polling_backoff"
|
|
57
57
|
require "solid_objects/effect_registry"
|
|
58
|
+
require "solid_objects/effect_payload"
|
|
58
59
|
require "solid_objects/commit_action_registry"
|
|
59
60
|
require "solid_objects/lease"
|
|
60
61
|
require "solid_objects/lease_renewer"
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
# Generated from lib/solid_objects/actor_signatures.rb with RBS::Inline
|
|
2
|
+
|
|
3
|
+
module SolidObjects
|
|
4
|
+
class ActorSignatures
|
|
5
|
+
# @rbs (actors: Array[Class], signatures: Array[String]) -> String
|
|
6
|
+
def self.generate: (actors: Array[Class], signatures: Array[String]) -> String
|
|
7
|
+
|
|
8
|
+
@builder: untyped
|
|
9
|
+
|
|
10
|
+
# @rbs (signatures: Array[String]) -> void
|
|
11
|
+
def initialize: (signatures: Array[String]) -> void
|
|
12
|
+
|
|
13
|
+
# @rbs (actors: Array[Class]) -> String
|
|
14
|
+
def generate: (actors: Array[Class]) -> String
|
|
15
|
+
|
|
16
|
+
private
|
|
17
|
+
|
|
18
|
+
# @rbs (untyped) -> String
|
|
19
|
+
def actor_signature: (untyped) -> String
|
|
20
|
+
|
|
21
|
+
# @rbs (untyped, String, Symbol) -> untyped
|
|
22
|
+
def staged_type: (untyped, String, Symbol) -> untyped
|
|
23
|
+
end
|
|
24
|
+
end
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
# Generated from lib/solid_objects/effect_payload.rb with RBS::Inline
|
|
2
|
+
|
|
3
|
+
module SolidObjects
|
|
4
|
+
module EffectPayload
|
|
5
|
+
# @rbs [Arguments, Result] (effect_id: String, arguments: Arguments, result: Result) -> effect_success_payload[Arguments, Result]
|
|
6
|
+
def self.success: [Arguments, Result] (effect_id: String, arguments: Arguments, result: Result) -> effect_success_payload[Arguments, Result]
|
|
7
|
+
|
|
8
|
+
# @rbs [Arguments] (effect_id: String, arguments: Arguments, error: effect_error) -> effect_failure_payload[Arguments]
|
|
9
|
+
def self.failure: [Arguments] (effect_id: String, arguments: Arguments, error: effect_error) -> effect_failure_payload[Arguments]
|
|
10
|
+
|
|
11
|
+
# @rbs (Exception) -> effect_error
|
|
12
|
+
def self.error: (Exception) -> effect_error
|
|
13
|
+
end
|
|
14
|
+
end
|
|
@@ -67,6 +67,18 @@ module SolidObjects
|
|
|
67
67
|
class DatabaseDeadlineExceeded < Error
|
|
68
68
|
end
|
|
69
69
|
|
|
70
|
+
class CommittedTransactionError < Error
|
|
71
|
+
@original_error: StandardError
|
|
72
|
+
|
|
73
|
+
attr_reader original_error: untyped
|
|
74
|
+
|
|
75
|
+
# @rbs (StandardError) -> void
|
|
76
|
+
def initialize: (StandardError) -> void
|
|
77
|
+
|
|
78
|
+
# @rbs () -> bot
|
|
79
|
+
def reraise: () -> bot
|
|
80
|
+
end
|
|
81
|
+
|
|
70
82
|
class SyncEnqueueTimeout < Error
|
|
71
83
|
@timeout: Numeric
|
|
72
84
|
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
module SolidObjects
|
|
2
|
+
type effect_error = {
|
|
3
|
+
"class" => String?,
|
|
4
|
+
"message" => String,
|
|
5
|
+
"backtrace" => Array[String]
|
|
6
|
+
}
|
|
7
|
+
|
|
8
|
+
type effect_failure_payload[Arguments] = {
|
|
9
|
+
"effect_id" => String,
|
|
10
|
+
"arguments" => Arguments,
|
|
11
|
+
"error" => effect_error
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
type effect_success_payload[Arguments, Result] = {
|
|
15
|
+
"effect_id" => String,
|
|
16
|
+
"arguments" => Arguments,
|
|
17
|
+
"result" => Result
|
|
18
|
+
}
|
|
19
|
+
end
|
metadata
CHANGED
|
@@ -1,14 +1,14 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: solid_objects
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 0.14.
|
|
4
|
+
version: 0.14.7
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Lucas Carlson
|
|
8
8
|
autorequire:
|
|
9
9
|
bindir: exe
|
|
10
10
|
cert_chain: []
|
|
11
|
-
date: 2026-09-
|
|
11
|
+
date: 2026-09-15 00:00:00.000000000 Z
|
|
12
12
|
dependencies:
|
|
13
13
|
- !ruby/object:Gem::Dependency
|
|
14
14
|
name: actioncable
|
|
@@ -389,6 +389,7 @@ files:
|
|
|
389
389
|
- lib/solid_objects/actor_channel.rb
|
|
390
390
|
- lib/solid_objects/actor_definition.rb
|
|
391
391
|
- lib/solid_objects/actor_registry.rb
|
|
392
|
+
- lib/solid_objects/actor_signatures.rb
|
|
392
393
|
- lib/solid_objects/actor_snapshot.rb
|
|
393
394
|
- lib/solid_objects/actor_view.rb
|
|
394
395
|
- lib/solid_objects/administration.rb
|
|
@@ -416,6 +417,7 @@ files:
|
|
|
416
417
|
- lib/solid_objects/doctor.rb
|
|
417
418
|
- lib/solid_objects/dom_identity.rb
|
|
418
419
|
- lib/solid_objects/effect_executor.rb
|
|
420
|
+
- lib/solid_objects/effect_payload.rb
|
|
419
421
|
- lib/solid_objects/effect_registry.rb
|
|
420
422
|
- lib/solid_objects/engine.rb
|
|
421
423
|
- lib/solid_objects/errors.rb
|
|
@@ -478,6 +480,7 @@ files:
|
|
|
478
480
|
- sig/generated/lib/solid_objects/actor_channel.rbs
|
|
479
481
|
- sig/generated/lib/solid_objects/actor_definition.rbs
|
|
480
482
|
- sig/generated/lib/solid_objects/actor_registry.rbs
|
|
483
|
+
- sig/generated/lib/solid_objects/actor_signatures.rbs
|
|
481
484
|
- sig/generated/lib/solid_objects/actor_snapshot.rbs
|
|
482
485
|
- sig/generated/lib/solid_objects/actor_view.rbs
|
|
483
486
|
- sig/generated/lib/solid_objects/administration.rbs
|
|
@@ -505,6 +508,7 @@ files:
|
|
|
505
508
|
- sig/generated/lib/solid_objects/doctor.rbs
|
|
506
509
|
- sig/generated/lib/solid_objects/dom_identity.rbs
|
|
507
510
|
- sig/generated/lib/solid_objects/effect_executor.rbs
|
|
511
|
+
- sig/generated/lib/solid_objects/effect_payload.rbs
|
|
508
512
|
- sig/generated/lib/solid_objects/effect_registry.rbs
|
|
509
513
|
- sig/generated/lib/solid_objects/engine.rbs
|
|
510
514
|
- sig/generated/lib/solid_objects/errors.rbs
|
|
@@ -561,6 +565,7 @@ files:
|
|
|
561
565
|
- sig/generated/models/solid_objects/ready_message.rbs
|
|
562
566
|
- sig/generated/models/solid_objects/record.rbs
|
|
563
567
|
- sig/generated/models/solid_objects/reminder.rbs
|
|
568
|
+
- sig/public/effect_payload.rbs
|
|
564
569
|
- sig/support/framework.rbs
|
|
565
570
|
- web/assets/javascripts/application.js
|
|
566
571
|
- web/assets/javascripts/charts.js
|