Class: Datastar::Dispatcher
- Inherits:
-
Object
- Object
- Datastar::Dispatcher
- Defined in:
- lib/datastar/dispatcher.rb
Overview
The Dispatcher encapsulates the logic of handling a request and building a response with streaming datastar messages. You’ll normally instantiate a Dispatcher in your controller action of Rack handler via Datastar.new.
Constant Summary collapse
- BLANK_BODY =
[].freeze
- SSE_CONTENT_TYPE =
'text/event-stream'- SSE_ACCEPT_EXP =
/text\/event-stream/- HTTP_ACCEPT =
'HTTP_ACCEPT'- HTTP1 =
'HTTP/1.1'
Instance Attribute Summary collapse
-
#heartbeat ⇒ Object
readonly
Returns the value of attribute heartbeat.
-
#request ⇒ Object
readonly
Returns the value of attribute request.
-
#response ⇒ Object
readonly
Returns the value of attribute response.
Instance Method Summary collapse
-
#execute_script(script, options = BLANK_OPTIONS) ⇒ Object
One-off execute script in the UI See data-star.dev/reference/sse_events#datastar-execute-script.
-
#initialize(request:, response: nil, view_context: nil, executor: Datastar.config.executor, error_callback: Datastar.config.error_callback, finalize: Datastar.config.finalize, heartbeat: Datastar.config.heartbeat, compression: Datastar.config.compression) ⇒ Dispatcher
constructor
A new instance of Dispatcher.
-
#on_client_disconnect(callable = nil, &block) ⇒ self
Register a callback for client disconnection Ex.
-
#on_connect(callable = nil) {|sse| ... } ⇒ self
Register an on-connect callback Triggered when the request is handled.
-
#on_error(callable = nil, &block) ⇒ self
Register a callback server-side exceptions Ex.
-
#on_server_disconnect(callable = nil, &block) ⇒ self
Register a callback for server disconnection Ex.
-
#patch_elements(elements, options = BLANK_OPTIONS) ⇒ Object
Send one-off elements to the UI See data-star.dev/reference/sse_events#datastar-patch-elements.
-
#patch_signals(signals, options = BLANK_OPTIONS) ⇒ Object
One-off patch signals in the UI See data-star.dev/reference/sse_events#datastar-patch-signals.
-
#redirect(url) ⇒ Object
Send an execute_script event to change window.location.
-
#remove_elements(selector, options = BLANK_OPTIONS) ⇒ Object
One-off remove elements from the UI Sugar on top of patch-elements with mode: ‘remove’ See data-star.dev/reference/sse_events#datastar-patch-elements.
-
#remove_signals(paths, options = BLANK_OPTIONS) ⇒ Object
One-off remove signals from the UI See data-star.dev/reference/sse_events#datastar-remove-signals.
-
#signals ⇒ Hash
Parse and returns Datastar signals sent by the client.
-
#sse? ⇒ Boolean
Check if the request accepts SSE responses.
-
#stream(streamer = nil, heartbeat: @heartbeat) {|sse| ... } ⇒ Object
Start a streaming response A generator object is passed to the block The generator supports all the Datastar methods listed above (it’s the same type) But you can call them multiple times to send multiple messages down an open SSE connection.
Constructor Details
#initialize(request:, response: nil, view_context: nil, executor: Datastar.config.executor, error_callback: Datastar.config.error_callback, finalize: Datastar.config.finalize, heartbeat: Datastar.config.heartbeat, compression: Datastar.config.compression) ⇒ Dispatcher
Returns a new instance of Dispatcher.
40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 |
# File 'lib/datastar/dispatcher.rb', line 40 def initialize( request:, response: nil, view_context: nil, executor: Datastar.config.executor, error_callback: Datastar.config.error_callback, finalize: Datastar.config.finalize, heartbeat: Datastar.config.heartbeat, compression: Datastar.config.compression ) @on_connect = [] @on_client_disconnect = [] @on_server_disconnect = [] @on_error = [error_callback] @finalize = finalize @streamers = [] @queue = nil @executor = executor @view_context = view_context # Dup the env so that Rack middleware restores (e.g. Rack::URLMap's # ensure block resetting SCRIPT_NAME/PATH_INFO after app.call returns) # don't affect async stream fibers that run after the handler returns. @request = Rack::Request.new(request.env.dup) @response = Rack::Response.new(BLANK_BODY, 200, response&.headers || {}) @response.content_type = SSE_CONTENT_TYPE @response.headers['Cache-Control'] = 'no-cache' @response.headers['Connection'] = 'keep-alive' if @request.env['SERVER_PROTOCOL'] == HTTP1 # Disable response buffering in NGinx and other proxies @response.headers['X-Accel-Buffering'] = 'no' @response.delete_header 'Content-Length' @executor.prepare(@response) raise ArgumentError, ':heartbeat must be a number' if heartbeat && !heartbeat.is_a?(Numeric) @heartbeat = heartbeat @heartbeat_on = false # Negotiate compression compression = CompressionConfig.build(compression) unless compression.is_a?(CompressionConfig) @compressor = compression.negotiate(request) @compressor.prepare_response(@response) end |
Instance Attribute Details
#heartbeat ⇒ Object (readonly)
Returns the value of attribute heartbeat.
31 32 33 |
# File 'lib/datastar/dispatcher.rb', line 31 def heartbeat @heartbeat end |
#request ⇒ Object (readonly)
Returns the value of attribute request.
31 32 33 |
# File 'lib/datastar/dispatcher.rb', line 31 def request @request end |
#response ⇒ Object (readonly)
Returns the value of attribute response.
31 32 33 |
# File 'lib/datastar/dispatcher.rb', line 31 def response @response end |
Instance Method Details
#execute_script(script, options = BLANK_OPTIONS) ⇒ Object
One-off execute script in the UI See data-star.dev/reference/sse_events#datastar-execute-script
199 200 201 202 203 |
# File 'lib/datastar/dispatcher.rb', line 199 def execute_script(script, = BLANK_OPTIONS) stream(heartbeat: false) do |sse| sse.execute_script(script, ) end end |
#on_client_disconnect(callable = nil, &block) ⇒ self
Register a callback for client disconnection Ex. when the browser is closed mid-stream
102 103 104 105 |
# File 'lib/datastar/dispatcher.rb', line 102 def on_client_disconnect(callable = nil, &block) @on_client_disconnect << (callable || block) self end |
#on_connect(callable = nil) {|sse| ... } ⇒ self
Register an on-connect callback Triggered when the request is handled
93 94 95 96 |
# File 'lib/datastar/dispatcher.rb', line 93 def on_connect(callable = nil, &block) @on_connect << (callable || block) self end |
#on_error(callable = nil, &block) ⇒ self
Register a callback server-side exceptions Ex. when one of the server threads raises an exception
120 121 122 123 |
# File 'lib/datastar/dispatcher.rb', line 120 def on_error(callable = nil, &block) @on_error << (callable || block) self end |
#on_server_disconnect(callable = nil, &block) ⇒ self
Register a callback for server disconnection Ex. when the server finishes serving the request
111 112 113 114 |
# File 'lib/datastar/dispatcher.rb', line 111 def on_server_disconnect(callable = nil, &block) @on_server_disconnect << (callable || block) self end |
#patch_elements(elements, options = BLANK_OPTIONS) ⇒ Object
Send one-off elements to the UI See data-star.dev/reference/sse_events#datastar-patch-elements
142 143 144 145 146 |
# File 'lib/datastar/dispatcher.rb', line 142 def patch_elements(elements, = BLANK_OPTIONS) stream(heartbeat: false) do |sse| sse.patch_elements(elements, ) end end |
#patch_signals(signals, options = BLANK_OPTIONS) ⇒ Object
One-off patch signals in the UI See data-star.dev/reference/sse_events#datastar-patch-signals
171 172 173 174 175 |
# File 'lib/datastar/dispatcher.rb', line 171 def patch_signals(signals, = BLANK_OPTIONS) stream(heartbeat: false) do |sse| sse.patch_signals(signals, ) end end |
#redirect(url) ⇒ Object
Send an execute_script event to change window.location
209 210 211 212 213 |
# File 'lib/datastar/dispatcher.rb', line 209 def redirect(url) stream(heartbeat: false) do |sse| sse.redirect(url) end end |
#remove_elements(selector, options = BLANK_OPTIONS) ⇒ Object
One-off remove elements from the UI Sugar on top of patch-elements with mode: ‘remove’ See data-star.dev/reference/sse_events#datastar-patch-elements
157 158 159 160 161 |
# File 'lib/datastar/dispatcher.rb', line 157 def remove_elements(selector, = BLANK_OPTIONS) stream(heartbeat: false) do |sse| sse.remove_elements(selector, ) end end |
#remove_signals(paths, options = BLANK_OPTIONS) ⇒ Object
One-off remove signals from the UI See data-star.dev/reference/sse_events#datastar-remove-signals
185 186 187 188 189 |
# File 'lib/datastar/dispatcher.rb', line 185 def remove_signals(paths, = BLANK_OPTIONS) stream(heartbeat: false) do |sse| sse.remove_signals(paths, ) end end |
#signals ⇒ Hash
Parse and returns Datastar signals sent by the client. See data-star.dev/guide/getting_started#data-signals
128 129 130 |
# File 'lib/datastar/dispatcher.rb', line 128 def signals @signals ||= parse_signals(request).freeze end |
#sse? ⇒ Boolean
Check if the request accepts SSE responses
84 85 86 |
# File 'lib/datastar/dispatcher.rb', line 84 def sse? !!(@request.get_header(HTTP_ACCEPT).to_s =~ SSE_ACCEPT_EXP) end |
#stream(streamer = nil, heartbeat: @heartbeat) {|sse| ... } ⇒ Object
Start a streaming response A generator object is passed to the block The generator supports all the Datastar methods listed above (it’s the same type) But you can call them multiple times to send multiple messages down an open SSE connection. This methods also captures exceptions raised in the block and triggers any error callbacks. Client disconnection errors trigger the @on_client_disconnect callbacks. Finally, when the block is done streaming, the @on_server_disconnect callbacks are triggered.
When multiple streams are scheduled this way, this SDK will spawn each block in separate threads (or fibers, depending on executor) and linearize their writes to the connection socket As a last step, the finalize callback is called with the view context and the response This is so that different frameworks can setup their responses correctly. By default, the built-in Rack finalzer just returns the resposne Array which can be used by any Rack handler. On Rails, the Rails controller response is set to this objects streaming response.
A per-call heartbeat: keyword overrides the constructor-level heartbeat for the duration of this call. Pass false to disable heartbeat for a one-shot message (e.g. a single patch_elements), or a Numeric interval to enable it. The previous value is restored once the call returns.
265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 |
# File 'lib/datastar/dispatcher.rb', line 265 def stream(streamer = nil, heartbeat: @heartbeat, &block) heartbeat_was = @heartbeat @heartbeat = heartbeat streamer ||= block @streamers << streamer if @heartbeat && !@heartbeat_on @heartbeat_on = true @streamers << proc do |sse| while true sleep @heartbeat sse.check_connection! end end end body = if @streamers.size == 1 stream_one(streamer) else stream_many(streamer) end @response.body = body @finalize.call(@view_context, @response) ensure @heartbeat = heartbeat_was end |