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 |
# 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 @request = request @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
196 197 198 199 200 |
# File 'lib/datastar/dispatcher.rb', line 196 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
99 100 101 102 |
# File 'lib/datastar/dispatcher.rb', line 99 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
90 91 92 93 |
# File 'lib/datastar/dispatcher.rb', line 90 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
117 118 119 120 |
# File 'lib/datastar/dispatcher.rb', line 117 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
108 109 110 111 |
# File 'lib/datastar/dispatcher.rb', line 108 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
139 140 141 142 143 |
# File 'lib/datastar/dispatcher.rb', line 139 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
168 169 170 171 172 |
# File 'lib/datastar/dispatcher.rb', line 168 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
206 207 208 209 210 |
# File 'lib/datastar/dispatcher.rb', line 206 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
154 155 156 157 158 |
# File 'lib/datastar/dispatcher.rb', line 154 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
182 183 184 185 186 |
# File 'lib/datastar/dispatcher.rb', line 182 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
125 126 127 |
# File 'lib/datastar/dispatcher.rb', line 125 def signals @signals ||= parse_signals(request).freeze end |
#sse? ⇒ Boolean
Check if the request accepts SSE responses
81 82 83 |
# File 'lib/datastar/dispatcher.rb', line 81 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.
262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 |
# File 'lib/datastar/dispatcher.rb', line 262 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 |