![]() * WiP initial setup, ServerSentEventGenerator class * WiP working merge_fragments in Rails * #merge_fragments and #merge_signals * Handle SSE vs Data* options * Test that #merge_fragments works with a #call(view_context:) interface * Test Dispatcher#stream * #remove_fragments * #remove_signals * #execute_script * execute_script with attributes Hash * Connection: keep-alive * Use 2 line-breaks as message end, plus last line's 1 line break (3 total) * Connection callbacks. #on_connect, #on_disconnect, #on_error * Dispatcher#signals * Omit retry if using default value (1000) * Omit defaults * Multiline scripts * Test Rack endpoint * Document test Rack endpoint * Add missing defaults * Spawn multiple streams in threads, client_disconnect and server_disconnect handlers * Move ThreadSpawner to configuration * Configure a RailsThreadSpawner when Rails detected * Move Railtie one dir up * Global error callback Datastar.config.on_error { |err| Sentry.notify(err) } * Catch exception from stream threads in main thread * Linearlize exception handling * Refactor dispatcher to handle single stream in main thread, multi streams in separate threads * spawner => executor. Rails Async executor using fibers. * Support Async for fiber-based concurrency * Finalize response for Rack and Rails * test Rack app * Threaded demo * Test Dispatcher#sse? Also do not check for SSE Accept on stream. Leave it up to the user. * Do not check Accept header in test app. Test scripts don't send it properly. * Document code * Example progress bar Rack app * README * Link to D* SSE docs * See examples * Document callbacks * List Ruby SDK in SDKs.md * Ruby struct in consts.go * Document running tasks with arguments via Docker * Code-gen Ruby constants from shared data via template * Make test rely on constants * Datastar.from_rack_env(env) => Datastar::Dispatcher * Ruby example snippets * #redirect(location) * Ruby snippet using #redirect(new_path) * Add X-Accel-Buffering: no header To disable response buffering by NGinx and other proxies. * Clarify linearisation of updates in Readme * Tidy-up progress example * Move examples to /examples/ruby * Document Rails and Phlex * Version 1.0.0.beta.1 * Version 1.0.0.beta.1 * Do not set Connection header if not HTTP/1.1 * Don't touch BUILDING.md docs in this PR * Remove Changelog for now * Sort Ruby alphabetically (just "ruby", not the entire line) * Add hello world example, remove progress bar one. * Add hello-world example to code-gen * Typos |
||
---|---|---|
.. | ||
bin | ||
examples | ||
lib | ||
sig | ||
spec | ||
.gitignore | ||
.rspec | ||
Gemfile | ||
Gemfile.lock | ||
README.md | ||
Rakefile | ||
datastar.gemspec |
README.md
Datastar Ruby SDK
Implement the Datastart SSE procotocol in Ruby. It can be used in any Rack handler, and Rails controllers.
Installation
Install the gem and add to the application's Gemfile by executing:
bundle add datastar
Or point your Gemfile
to the source
gem 'datastar', git: 'https://github.com/starfederation/datastar', glob: 'sdk/ruby/*.gemspec'
Usage
Initialize the Datastar dispatcher
In your Rack handler or Rails controller:
# Rails controllers, as well as Sinatra and others,
# already have request and response objects
datastar = Datastar.new(request:, response:, view_context: self)
# In a Rack handler, you can instantiate from the Rack env
datastar = Datastar.from_rack_env(env)
Sending updates to the browser
There are two ways to use this gem in HTTP handlers:
- One-off responses, where you want to send a single update down to the browser.
- Streaming responses, where you want to send multiple updates down to the browser.
One-off update:
datastar.merge_fragments(%(<h1 id="title">Hello, World!</h1>))
In this mode, the response is closed after the fragment is sent.
Streaming updates
datastar.stream do |sse|
sse.merge_fragments(%(<h1 id="title">Hello, World!</h1>))
# Streaming multiple updates
100.times do |i|
sleep 1
sse.merge_fragments(%(<h1 id="title">Hello, World #{i}!</h1>))
end
end
In this mode, the response is kept open until stream
blocks have finished.
Concurrent streaming blocks
Multiple stream
blocks will be launched in threads/fibers, and will run concurrently.
Their updates are linearized and sent to the browser as they are produced.
# Stream to the browser from two concurrent threads
datastar.stream do |sse|
100.times do |i|
sleep 1
sse.merge_fragments(%(<h1 id="slow">#{i}!</h1>))
end
end
datastar.stream do |sse|
1000.times do |i|
sleep 0.1
sse.merge_fragments(%(<h1 id="fast">#{i}!</h1>))
end
end
See the examples directory.
Datastar methods
All these methods are available in both the one-off and the streaming modes.
merge_fragments
See https://data-star.dev/reference/sse_events#datastar-merge-fragments
sse.merge_fragments(%(<div id="foo">\n<span>hello</span>\n</div>))
# or a Phlex view object
sse.merge_fragments(UserComponet.new)
# Or pass options
sse.merge_fragments(
%(<div id="foo">\n<span>hello</span>\n</div>),
merge_mode: 'append'
)
remove_fragments
See https://data-star.dev/reference/sse_events#datastar-remove-fragments
sse.remove_fragments('#users')
merge_signals
See https://data-star.dev/reference/sse_events#datastar-merge-signals
sse.merge_signals(count: 4, user: { name: 'John' })
remove_signals
See https://data-star.dev/reference/sse_events#datastar-remove-signals
sse.remove_signals(['user.name', 'user.email'])
execute_script
See https://data-star.dev/reference/sse_events#datastar-execute-script
sse.execute_scriprt(%(alert('Hello World!'))
signals
See https://data-star.dev/guide/getting_started#data-signals
Returns signals sent by the browser.
sse.signals # => { user: { name: 'John' } }
redirect
This is just a helper to send a script to update the browser's location.
sse.redirect('/new_location')
Lifecycle callbacks
on_connect
Register server-side code to run when the connection is first handled.
datastar.on_connect do
puts 'A user has connected'
end
on_client_disconnect
Register server-side code to run when the connection is closed by the client
datastar.on_client_connect do
puts 'A user has disconnected connected'
end
on_server_disconnect
Register server-side code to run when the connection is closed by the server. Ie when the served is done streaming without errors.
datastar.on_server_connect do
puts 'Server is done streaming'
end
on_error
Ruby code to handle any exceptions raised by streaming blocks.
datastar.on_error do |exception|
Sentry.notify(exception)
end
Note that this callback can be registered globally, too.
Global configuration
Datastar.configure do |config|
config.on_error do |exception|
Sentry.notify(exception)
end
end
Rails
Rendering Rails templates
datastar.stream do |sse|
10.times do |i|
sleep 1
tpl = render_to_string('events/user', layout: false, locals: { name: "David #{i}" })
sse.merge_fragments tpl
end
end
Rendering Phlex components
#merge_fragments
supports Phlex component instances.
sse.merge_fragments(UserComponent.new(user: User.first))
Tests
bundle exec rspec
Running Datastar's SDK test suite
Install dependencies.
bundle install
From this library's root, run the bundled-in test Rack app:
bundle puma examples/test.ru
Now run the test bash scripts in the test
directory in this repo.
./test-all.sh http://localhost:9292
Development
After checking out the repo, run bin/setup
to install dependencies. Then, run rake spec
to run the tests. You can also run bin/console
for an interactive prompt that will allow you to experiment.
To install this gem onto your local machine, run bundle exec rake install
. To release a new version, update the version number in version.rb
, and then run bundle exec rake release
, which will create a git tag for the version, push git commits and the created tag, and push the .gem
file to rubygems.org.
Contributing
Bug reports and pull requests are welcome on GitHub at https://github.com/starfederation/datastar.