Class: SolidObjects::ActorDefinition
- Inherits:
-
Object
- Object
- SolidObjects::ActorDefinition
show all
- Defined in:
- lib/solid_objects/actor_definition.rb,
sig/generated/lib/solid_objects/actor_definition.rbs
Defined Under Namespace
Classes: Handler, StateMigration
Instance Attribute Summary collapse
Instance Method Summary
collapse
-
#actor_message_method_names(actor_class) ⇒ Array[Symbol]
-
#add_attribute(name, default:) ⇒ StateDefinition::Attribute
-
#add_handler(collection:, name:, block:) ⇒ Handler
-
#add_message(name, block) ⇒ Handler
-
#add_method_message(name) ⇒ Handler
-
#add_observable(name, block = nil) ⇒ Handler
-
#add_payload_broadcast(name, block) ⇒ Handler
-
#add_query(name, block) ⇒ Handler
-
#add_state_migration(from:, to:, block:) ⇒ StateMigration
-
#duplicate ⇒ ActorDefinition
-
#initialize ⇒ ActorDefinition
constructor
A new instance of ActorDefinition.
-
#migrate_state(version, data) ⇒ Hash[String, untyped]
-
#set_state_version(version) ⇒ Integer
-
#synchronize_instance_messages(actor_class) ⇒ ActorDefinition
-
#validate_attribute_methods!(attribute_method_names) ⇒ void
Constructor Details
Returns a new instance of ActorDefinition.
31
32
33
34
35
36
37
38
39
40
41
42
43
|
# File 'lib/solid_objects/actor_definition.rb', line 31
def initialize
@state_definition = StateDefinition.new
@messages = {}
@queries = {}
@observables = {}
@payload_broadcasts = {}
@state_version = 1
@state_migrations = []
@activation_hooks = []
@deactivation_hooks = []
@attribute_queries = {}
@method_messages = {}
end
|
Instance Attribute Details
#activation_hooks ⇒ Object
20
21
22
|
# File 'lib/solid_objects/actor_definition.rb', line 20
def activation_hooks
@activation_hooks
end
|
#attribute_queries ⇒ Object
Returns the value of attribute attribute_queries.
183
184
185
|
# File 'lib/solid_objects/actor_definition.rb', line 183
def attribute_queries
@attribute_queries
end
|
#deactivation_hooks ⇒ Object
20
21
22
|
# File 'lib/solid_objects/actor_definition.rb', line 20
def deactivation_hooks
@deactivation_hooks
end
|
#messages ⇒ Object
20
21
22
|
# File 'lib/solid_objects/actor_definition.rb', line 20
def messages
@messages
end
|
#method_messages ⇒ Object
Returns the value of attribute method_messages.
183
184
185
|
# File 'lib/solid_objects/actor_definition.rb', line 183
def method_messages
@method_messages
end
|
#observables ⇒ Object
20
21
22
|
# File 'lib/solid_objects/actor_definition.rb', line 20
def observables
@observables
end
|
#payload_broadcasts ⇒ Object
20
21
22
|
# File 'lib/solid_objects/actor_definition.rb', line 20
def payload_broadcasts
@payload_broadcasts
end
|
#queries ⇒ Object
20
21
22
|
# File 'lib/solid_objects/actor_definition.rb', line 20
def queries
@queries
end
|
#state_definition ⇒ Object
20
21
22
|
# File 'lib/solid_objects/actor_definition.rb', line 20
def state_definition
@state_definition
end
|
#state_migrations ⇒ Object
20
21
22
|
# File 'lib/solid_objects/actor_definition.rb', line 20
def state_migrations
@state_migrations
end
|
#state_version ⇒ Object
20
21
22
|
# File 'lib/solid_objects/actor_definition.rb', line 20
def state_version
@state_version
end
|
Instance Method Details
#actor_message_method_names(actor_class) ⇒ Array[Symbol]
202
203
204
205
206
207
208
209
210
211
212
213
214
|
# File 'lib/solid_objects/actor_definition.rb', line 202
def actor_message_method_names(actor_class)
actor_ancestors = actor_class.ancestors.take_while { |ancestor| ancestor != Actor }
attribute_method_names = actor_class.send(:generated_attribute_methods).keys
validate_attribute_methods!(attribute_method_names)
actor_class.public_instance_methods.filter_map do |name|
method_name = name.to_sym
next if attribute_method_names.include?(method_name)
next unless actor_ancestors.include?(actor_class.instance_method(method_name).owner)
method_name
end.uniq
end
|
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
|
# File 'lib/solid_objects/actor_definition.rb', line 46
def add_attribute(name, default:)
attribute_name = name.to_sym
OperationDispatcher.validate_operation_name!(attribute_name)
if messages.key?(attribute_name) || queries.key?(attribute_name)
raise InvalidActor, "#{attribute_name.inspect} is already defined"
end
state_definition.attribute(attribute_name, default:).tap do
queries[attribute_name] = Handler.new(
name: attribute_name,
block: -> { state.fetch(attribute_name) }
)
attribute_queries[attribute_name] = true
end
end
|
#add_handler(collection:, name:, block:) ⇒ Handler
228
229
230
231
232
233
234
|
# File 'lib/solid_objects/actor_definition.rb', line 228
def add_handler(collection:, name:, block:)
handler_name = name.to_sym
OperationDispatcher.validate_operation_name!(handler_name)
raise InvalidActor, "#{handler_name.inspect} is already defined" if messages.key?(handler_name) || queries.key?(handler_name)
Handler.new(name: handler_name, block:).tap { |handler| collection[handler_name] = handler }
end
|
#add_message(name, block) ⇒ Handler
63
64
65
|
# File 'lib/solid_objects/actor_definition.rb', line 63
def add_message(name, block)
add_handler(collection: messages, name:, block:)
end
|
#add_method_message(name) ⇒ Handler
186
187
188
189
190
191
192
193
194
195
196
197
198
199
|
# File 'lib/solid_objects/actor_definition.rb', line 186
def add_method_message(name)
OperationDispatcher.validate_operation_name!(name)
if messages.key?(name) || queries.key?(name)
raise InvalidActor, "#{name.inspect} is already defined"
end
Handler.new(
name:,
block: ->(**arguments) { public_send(name, **arguments) }
).tap do |handler|
messages[name] = handler
method_messages[name] = true
end
end
|
#add_observable(name, block = nil) ⇒ Handler
80
81
82
83
84
85
86
87
88
|
# File 'lib/solid_objects/actor_definition.rb', line 80
def add_observable(name, block = nil)
observable_name = name.to_sym
raise InvalidActor, "#{observable_name.inspect} observable is already defined" if observables.key?(observable_name)
observable_block = block || -> { state.fetch(observable_name) }
Handler.new(name: observable_name, block: observable_block).tap do |handler|
observables[observable_name] = handler
end
end
|
#add_payload_broadcast(name, block) ⇒ Handler
91
92
93
94
95
96
97
98
99
100
101
102
103
|
# File 'lib/solid_objects/actor_definition.rb', line 91
def add_payload_broadcast(name, block)
payload_name = name.to_sym
if payload_broadcasts.key?(payload_name)
raise InvalidActor, "#{payload_name.inspect} payload broadcast is already defined"
end
unless payload_name.to_s.match?(/\A[a-zA-Z0-9_]+\z/)
raise InvalidActor, "payload broadcast names may contain only letters, digits, and underscores"
end
Handler.new(name: payload_name, block:).tap do |handler|
payload_broadcasts[payload_name] = handler
end
end
|
#add_query(name, block) ⇒ Handler
68
69
70
71
72
73
74
75
76
77
|
# File 'lib/solid_objects/actor_definition.rb', line 68
def add_query(name, block)
query_name = name.to_sym
if attribute_queries.delete(query_name)
return Handler.new(name: query_name, block:).tap do |handler|
queries[query_name] = handler
end
end
add_handler(collection: queries, name:, block:)
end
|
#add_state_migration(from:, to:, block:) ⇒ StateMigration
126
127
128
129
130
131
132
133
134
135
136
|
# File 'lib/solid_objects/actor_definition.rb', line 126
def add_state_migration(from:, to:, block:)
unless to == from + 1
raise InvalidActor, "state migrations must advance exactly one version"
end
if state_migrations.any? { |migration| migration.from == from }
raise InvalidActor, "state migration from version #{from} is already defined"
end
StateMigration.new(from:, to:, block:).tap { |migration| state_migrations << migration }
end
|
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
|
# File 'lib/solid_objects/actor_definition.rb', line 165
def duplicate
self.class.new.tap do |copy|
copy.instance_variable_set(:@state_definition, state_definition.duplicate)
copy.instance_variable_set(:@messages, messages.dup)
copy.instance_variable_set(:@queries, queries.dup)
copy.instance_variable_set(:@observables, observables.dup)
copy.instance_variable_set(:@payload_broadcasts, payload_broadcasts.dup)
copy.instance_variable_set(:@state_version, state_version)
copy.instance_variable_set(:@state_migrations, state_migrations.dup)
copy.instance_variable_set(:@activation_hooks, activation_hooks.dup)
copy.instance_variable_set(:@deactivation_hooks, deactivation_hooks.dup)
copy.instance_variable_set(:@attribute_queries, attribute_queries.dup)
copy.instance_variable_set(:@method_messages, method_messages.dup)
end
end
|
#migrate_state(version, data) ⇒ Hash[String, untyped]
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
|
# File 'lib/solid_objects/actor_definition.rb', line 139
def migrate_state(version, data)
migrated = Serialization.deep_copy(data)
current_version = version
while current_version < state_version
migration = state_migrations.find { |candidate| candidate.from == current_version }
raise StateMigrationError, "missing state migration from version #{current_version}" unless migration
migrated = Serialization.dump(migration.block.call(migrated))
current_version = migration.to
end
if current_version > state_version
raise StateMigrationError, "stored state version #{current_version} is newer than code version #{state_version}"
end
migrated
rescue StateMigrationError
raise
rescue ActiveRecord::ReadOnlyError
raise
rescue => error
raise StateMigrationError, "state migration failed: #{error.message}"
end
|
#set_state_version(version) ⇒ Integer
119
120
121
122
123
|
# File 'lib/solid_objects/actor_definition.rb', line 119
def set_state_version(version)
raise InvalidActor, "state version must be positive" unless version.positive?
@state_version = version
end
|
#synchronize_instance_messages(actor_class) ⇒ ActorDefinition
106
107
108
109
110
111
112
113
114
115
116
|
# File 'lib/solid_objects/actor_definition.rb', line 106
def synchronize_instance_messages(actor_class)
names = actor_message_method_names(actor_class)
(method_messages.keys - names).each do |name|
messages.delete(name)
method_messages.delete(name)
end
names.each { |name| add_method_message(name) unless method_messages.key?(name) }
self
end
|
#validate_attribute_methods!(attribute_method_names) ⇒ void
This method returns an undefined value.
217
218
219
220
221
222
223
224
225
|
# File 'lib/solid_objects/actor_definition.rb', line 217
def validate_attribute_methods!(attribute_method_names)
state_definition.to_h.each_key do |name|
[ name, :"#{name}=" ].each do |method_name|
next if attribute_method_names.include?(method_name)
raise InvalidActor, "actor method #{method_name} overrides attribute #{name.inspect}"
end
end
end
|