Module: MaquinaStream::Streamable::Generated
- Defined in:
- lib/maquina_stream/streamable.rb
Overview
The host contract, generated by the maquina_stream macro. Included, so
a host method defined in the class body overrides it.
These are the methods the engine calls on a record — Broadcaster, Manifest, Export and MaquinaStream.render between them use nothing else. A host that cannot use the macro implements this module's public methods itself and never includes it.
Constant Summary collapse
- REQUIRED_COLUMNS =
{ maquina_stream_buffer: :buffer, maquina_stream_append: :buffer, maquina_stream_sequence: SEQUENCE_COLUMN, maquina_stream_open?: STATUS_COLUMN, maquina_stream_seal!: STATUS_COLUMN }.freeze
Instance Method Summary collapse
-
#maquina_stream_advance ⇒ Object
Increments the sequence and returns the new value.
-
#maquina_stream_append(text) ⇒ Object
Appends
textto the buffer, persists it, and returns the whole buffer. -
#maquina_stream_buffer ⇒ Object
The raw markdown written so far, as a String.
-
#maquina_stream_id ⇒ Object
A stable String, unique per message, used to build every DOM id in the message and to look the record back up in a repair request.
-
#maquina_stream_open? ⇒ Boolean
Whether the stream is still being written.
-
#maquina_stream_seal!(status: :complete) ⇒ Object
Closes the stream and returns the status Symbol it sealed with.
-
#maquina_stream_sequence ⇒ Object
The current frame sequence number, as an Integer.
-
#maquina_stream_status ⇒ Object
The recorded end state as a Symbol — one of SEAL_STATUSES — for replay and export.
-
#maquina_stream_target ⇒ Object
The Turbo broadcast target, from the
stream_for:callable the macro was given.
Instance Method Details
#maquina_stream_advance ⇒ Object
Increments the sequence and returns the new value.
Added in Phase 3. The sequence is documented as "incremented per frame", but the Broadcaster cannot write host state directly without contradicting "host owns persistence" - so it asks, through the contract, and the host's database does the incrementing atomically. That is also what keeps it monotonic under concurrent appends.
195 196 197 198 199 |
# File 'lib/maquina_stream/streamable.rb', line 195 def maquina_stream_advance maquina_stream_require_column!(SEQUENCE_COLUMN, :maquina_stream_advance) self.class.where(id: id).update_all("#{SEQUENCE_COLUMN} = #{SEQUENCE_COLUMN} + 1") reload.maquina_stream_sequence end |
#maquina_stream_append(text) ⇒ Object
Appends text to the buffer, persists it, and returns the whole
buffer.
The host owns persistence, which is why this and not the broadcaster writes. Broadcaster#append calls it first and only then has something to broadcast.
Raises ContractError when the buffer column does not exist.
174 175 176 177 178 179 |
# File 'lib/maquina_stream/streamable.rb', line 174 def maquina_stream_append(text) column = buffer_column maquina_stream_require_column!(column, :maquina_stream_append) update!(column => "#{public_send(column)}#{text}") maquina_stream_buffer end |
#maquina_stream_buffer ⇒ Object
The raw markdown written so far, as a String. Never HTML: the buffer is what the model wrote, and rendering it is the engine's job.
Raises ContractError when the column named by buffer: does not exist.
162 163 164 |
# File 'lib/maquina_stream/streamable.rb', line 162 def maquina_stream_buffer maquina_stream_read(buffer_column, :maquina_stream_buffer).to_s end |
#maquina_stream_id ⇒ Object
A stable String, unique per message, used to build every DOM id in the
message and to look the record back up in a repair request. Defaults to
to_param.
154 155 156 |
# File 'lib/maquina_stream/streamable.rb', line 154 def maquina_stream_id to_param.to_s end |
#maquina_stream_open? ⇒ Boolean
Whether the stream is still being written.
This is what decides render mode and what decides cacheability: an open message is never cached, because it is about to change.
217 218 219 |
# File 'lib/maquina_stream/streamable.rb', line 217 def maquina_stream_open? maquina_stream_read(STATUS_COLUMN, :maquina_stream_open?).to_s == OPEN_STATUS end |
#maquina_stream_seal!(status: :complete) ⇒ Object
Closes the stream and returns the status Symbol it sealed with.
status: must be one of SEAL_STATUSES: :complete, :cancelled,
:errored or :timed_out. Anything else raises ArgumentError.
Sealing only records the status. Broadcaster#seal! is what also emits the final frame, and that frame is never coalesced and never skipped — so a host seals through the broadcaster, not through this directly, unless it means to close a stream silently.
230 231 232 233 234 235 236 237 238 |
# File 'lib/maquina_stream/streamable.rb', line 230 def maquina_stream_seal!(status: :complete) unless SEAL_STATUSES.include?(status.to_sym) raise ArgumentError, "status must be one of #{SEAL_STATUSES.join(", ")}, got #{status.inspect}" end maquina_stream_require_column!(STATUS_COLUMN, :maquina_stream_seal!) update!(STATUS_COLUMN => status.to_s) status.to_sym end |
#maquina_stream_sequence ⇒ Object
The current frame sequence number, as an Integer. Monotonic. It moves once per frame that actually goes out, not once per append — the client uses it to notice that it missed one.
184 185 186 |
# File 'lib/maquina_stream/streamable.rb', line 184 def maquina_stream_sequence maquina_stream_read(SEQUENCE_COLUMN, :maquina_stream_sequence).to_i end |
#maquina_stream_status ⇒ Object
The recorded end state as a Symbol — one of SEAL_STATUSES — for replay and export. Nil while still open.
Export reads this to decide whether to append a status footer: a cancelled message that exports as though it were complete is a lie in a file somebody keeps.
207 208 209 210 211 |
# File 'lib/maquina_stream/streamable.rb', line 207 def maquina_stream_status return nil if maquina_stream_open? maquina_stream_read(STATUS_COLUMN, :maquina_stream_status)&.to_sym end |
#maquina_stream_target ⇒ Object
The Turbo broadcast target, from the stream_for: callable the macro
was given. Whatever that callable returns is passed straight to
Turbo::StreamsChannel.
Raises ContractError when stream_for: is not callable: the broadcast
target belongs to the host, and the engine never guesses one.
246 247 248 249 250 251 252 253 254 255 256 257 |
# File 'lib/maquina_stream/streamable.rb', line 246 def maquina_stream_target resolver = self.class.maquina_stream_target_resolver unless resolver.respond_to?(:call) raise ContractError, <<~MESSAGE #{self.class.name} does not supply #maquina_stream_target: `stream_for:` is not callable. The broadcast target belongs to the host — the engine never guesses one. See docs/streaming.md. MESSAGE end resolver.call(self) end |