solid_objects 0.14.0 → 0.14.2
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 +75 -0
- data/README.md +349 -1124
- data/Rakefile +5 -0
- data/docs/architecture.md +40 -1
- data/docs/correctness.md +9 -0
- data/docs/operations.md +169 -21
- data/docs/reminders.md +112 -0
- data/docs/roadmap.md +7 -1
- data/examples/at_least_once/actor.rb +14 -0
- data/examples/at_least_once/boot.rb +47 -0
- data/examples/at_least_once/demo.rb +92 -0
- data/examples/at_least_once/effect_worker.rb +40 -0
- data/examples/at_least_once/sink.rb +27 -0
- data/lib/solid_objects/actor_channel.rb +33 -3
- data/lib/solid_objects/engine.rb +4 -0
- data/lib/solid_objects/version.rb +1 -1
- data/sig/generated/lib/solid_objects/actor_channel.rbs +10 -0
- metadata +8 -2
|
@@ -4,6 +4,14 @@ require "action_cable"
|
|
|
4
4
|
|
|
5
5
|
module SolidObjects
|
|
6
6
|
class ActorChannel < ActionCable::Channel::Base
|
|
7
|
+
REJECT_REASONS = {
|
|
8
|
+
UnknownActorType => "unregistered_actor_type",
|
|
9
|
+
InvalidStreamToken => "invalid_stream_token",
|
|
10
|
+
InvalidComponentToken => "invalid_component_token",
|
|
11
|
+
JSON::ParserError => "malformed_component_registration",
|
|
12
|
+
KeyError => "missing_subscription_parameter"
|
|
13
|
+
}.freeze
|
|
14
|
+
|
|
7
15
|
# @rbs () -> void
|
|
8
16
|
def subscribed
|
|
9
17
|
identity = StreamToken.verify(params.fetch("token"))
|
|
@@ -15,7 +23,9 @@ module SolidObjects
|
|
|
15
23
|
actor_id:,
|
|
16
24
|
authorization_context: connection
|
|
17
25
|
)
|
|
18
|
-
|
|
26
|
+
unless authorized
|
|
27
|
+
return reject_and_report("unauthorized", actor_type:, actor_id:)
|
|
28
|
+
end
|
|
19
29
|
|
|
20
30
|
@reference = Reference.new(actor_type:, actor_id:)
|
|
21
31
|
@scalar_observables = identity["observables"]
|
|
@@ -43,8 +53,8 @@ module SolidObjects
|
|
|
43
53
|
JSON::ParserError,
|
|
44
54
|
InvalidStreamToken,
|
|
45
55
|
InvalidComponentToken,
|
|
46
|
-
UnknownActorType
|
|
47
|
-
|
|
56
|
+
UnknownActorType => error
|
|
57
|
+
reject_and_report(reject_reason(error), actor_type:, actor_id:, error:)
|
|
48
58
|
end
|
|
49
59
|
|
|
50
60
|
private
|
|
@@ -54,6 +64,26 @@ module SolidObjects
|
|
|
54
64
|
:scalar_observables,
|
|
55
65
|
:payload_names
|
|
56
66
|
|
|
67
|
+
# The exception message stays out of the payload, because a component or
|
|
68
|
+
# payload error can carry actor state into logs.
|
|
69
|
+
# @rbs (String, actor_type: String?, actor_id: String?, ?error: Exception?) -> void
|
|
70
|
+
def reject_and_report(reason, actor_type:, actor_id:, error: nil)
|
|
71
|
+
SolidObjects.instrument(
|
|
72
|
+
:"subscription.rejected",
|
|
73
|
+
reason:,
|
|
74
|
+
actor_type:,
|
|
75
|
+
actor_id:,
|
|
76
|
+
error_class: error&.class&.name
|
|
77
|
+
)
|
|
78
|
+
reject
|
|
79
|
+
end
|
|
80
|
+
|
|
81
|
+
# @rbs (Exception) -> String
|
|
82
|
+
def reject_reason(error)
|
|
83
|
+
match = REJECT_REASONS.find { |error_class, _| error.is_a?(error_class) }
|
|
84
|
+
match ? match.last : "invalid_subscription"
|
|
85
|
+
end
|
|
86
|
+
|
|
57
87
|
# @rbs (String) -> void
|
|
58
88
|
def receive_broadcast(stream)
|
|
59
89
|
invalidation = TurboStreamRenderer.invalidation(stream)
|
data/lib/solid_objects/engine.rb
CHANGED
|
@@ -15,6 +15,10 @@ module SolidObjects
|
|
|
15
15
|
SolidObjects::LogSubscriber.install
|
|
16
16
|
end
|
|
17
17
|
|
|
18
|
+
initializer "solid_objects.actors" do |application|
|
|
19
|
+
application.config.to_prepare { ApplicationActorLoader.new.call }
|
|
20
|
+
end
|
|
21
|
+
|
|
18
22
|
initializer "solid_objects.database", after: :load_config_initializers do
|
|
19
23
|
ActiveSupport.on_load(:active_record) do
|
|
20
24
|
require RECORD_PATH
|
|
@@ -2,6 +2,8 @@
|
|
|
2
2
|
|
|
3
3
|
module SolidObjects
|
|
4
4
|
class ActorChannel < ActionCable::Channel::Base
|
|
5
|
+
REJECT_REASONS: untyped
|
|
6
|
+
|
|
5
7
|
# @rbs () -> void
|
|
6
8
|
def subscribed: () -> void
|
|
7
9
|
|
|
@@ -15,6 +17,14 @@ module SolidObjects
|
|
|
15
17
|
|
|
16
18
|
attr_reader payload_names: untyped
|
|
17
19
|
|
|
20
|
+
# The exception message stays out of the payload, because a component or
|
|
21
|
+
# payload error can carry actor state into logs.
|
|
22
|
+
# @rbs (String, actor_type: String?, actor_id: String?, ?error: Exception?) -> void
|
|
23
|
+
def reject_and_report: (String, actor_type: String?, actor_id: String?, ?error: Exception?) -> void
|
|
24
|
+
|
|
25
|
+
# @rbs (Exception) -> String
|
|
26
|
+
def reject_reason: (Exception) -> String
|
|
27
|
+
|
|
18
28
|
# @rbs (String) -> void
|
|
19
29
|
def receive_broadcast: (String) -> void
|
|
20
30
|
|
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.2
|
|
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-08-
|
|
11
|
+
date: 2026-08-25 00:00:00.000000000 Z
|
|
12
12
|
dependencies:
|
|
13
13
|
- !ruby/object:Gem::Dependency
|
|
14
14
|
name: actioncable
|
|
@@ -355,6 +355,7 @@ files:
|
|
|
355
355
|
- docs/migrating-existing-state.md
|
|
356
356
|
- docs/operations.md
|
|
357
357
|
- docs/realtime.md
|
|
358
|
+
- docs/reminders.md
|
|
358
359
|
- docs/research/solid_queue.md
|
|
359
360
|
- docs/roadmap.md
|
|
360
361
|
- docs/security.md
|
|
@@ -371,6 +372,11 @@ files:
|
|
|
371
372
|
- examples/application/app/views/chat_rooms/show.html.erb
|
|
372
373
|
- examples/application/config/initializers/solid_objects.rb
|
|
373
374
|
- examples/application/config/routes.rb
|
|
375
|
+
- examples/at_least_once/actor.rb
|
|
376
|
+
- examples/at_least_once/boot.rb
|
|
377
|
+
- examples/at_least_once/demo.rb
|
|
378
|
+
- examples/at_least_once/effect_worker.rb
|
|
379
|
+
- examples/at_least_once/sink.rb
|
|
374
380
|
- exe/solid_objects
|
|
375
381
|
- lib/generators/solid_objects/install_generator.rb
|
|
376
382
|
- lib/generators/solid_objects/templates/solid_objects.rb
|