roda-turbo
Warning
I've migrated away from using the Hotwire stack (see this blog post for the reasons why I now avoid 37signals-owned codebases), so I don't intend to develop this plugin any further. I'll be happy to accept PRs and cut a new release, but beyond that, consider this plugin "done".
This plugin adds Turbo Streams support for the Roda web toolkit. It works in a standard Roda context as well as in Bridgetown (v1.2 or higher).
NOTE: This does not add support for async streaming (aka via Websockets, etc.). It simply adds support for stream tags in HTML responses, such as when forms are submitted via Turbo. See issue #2 to track a future implementation. Turbo Frame tag helpers are also planned.
Installation
This README assumes you know how to install Turbo's JavaScript library in your Roda project. The easiest way might be to use the Skypack CDN in your main JavaScript file.
import * as Turbo from "https://cdn.skypack.dev/@hotwired/turbo"
If you're using Bridgetown, you can run the Turbo bundled configuration.
Once the Turbo frontend is installed, you can run this command to add the roda-turbo gem to your Gemfile
:
$ bundle add roda-turbo
Installation in Roda
Add the plugin to your Roda app, e.g.:
class App < Roda
plugin :turbo
end
Installation in Bridgetown
Add the initializer to your configure block:
# config/initializers.rb
Bridgetown.configure do
# configurations…
init :"roda-turbo"
# configurations…
end
Usage
Now you can use the turbo_stream
helper in a route, and the turbo_stream?
request method to determine if the incoming request was triggered by a Turbo form submission:
r.post "stream_this" do
if r.turbo_stream?
turbo_stream.append "element-id", "<p>Hello from Turbo!</p>"
else
"<p>Just a regular HTML request.</p>"
end
end
If you'd like to return multiple stream actions, just define them within an array:
r.post do
[
turbo_stream.append_all(".content", "<p>Content goes here.</p>"),
turbo_stream.replace_all("header", "<h1>Header Title</h1>")
]
end
The turbo_stream
Helper
You can also use the turbo_stream
helper in Roda views, along with render
parameters.
<%= turbo_stream.update "#el", template: "content_partial", locals: { foo: "bar" } %>
If for some reason you need to set the response content type to Turbo Streams programmatically (text/vnd.turbo-stream.html
), you can call the r.respond_with_turbo_stream
method.
In the Bridgetown context, the turbo_stream
helper will available within routes inside your Bridgetown project as well as Ruby-based templates.
Render parameters (such as in the above example) will be passed to the underlying template engine's partial
method. You can also inline render components and pass the output to the initial string argument.
turbo_stream.update "#navbar", render(Public::Navbar.new metadata: bridgetown_site.metadata, resource: resource)
Custom Actions
Turbo 7.2+ supports the ability to add custom actions so that you're no longer restricted only to append
, replace
, etc. There's a two-step process to adding custom actions. First, you'll want to add the action as a new method available through the turbo_stream
helper. Next, you'll want to provide the action function to Turbo's JS on the frontend.
Let's add a redirect_to
action so that it's easy to use Turbo's visit
feature from a stream. We'll start first with the pure Roda example, then show an alternate approach for Bridgetown.
Define this above your Roda application, or in a separate file:
require "json"
module CustomActions
def redirect_to(url, delay: nil)
action "redirect_to", "", { url: url, delay: delay }.to_json
end
end
Next, right below the plugin :turbo
statement, add the following:
Turbo::Streams::TagBuilder.include CustomActions
Then, below where you import * from Turbo
, add the following:
const redirectTo = function() {
const payload = JSON.parse(this.templateContent.textContent)
setTimeout(() => {
Turbo.visit(payload.url)
}, payload.delay || 0)
}
Turbo.StreamActions.redirect_to = redirectTo
If you have a lot of custom actions, you could relocate them all to a separate file.
And that's it! Now you can call turbo_stream.redirect_to("/my-url", delay: 2500)
in a response and it will use this custom action.
For Bridgetown users, configuration is easy. Just define a config/roda-turbo.rb
file and include the following:
module CustomActions
def redirect_to(url, delay: nil)
action "redirect_to", "", { url:, delay: }.to_json
end
end
Bridgetown.initializer :"roda-turbo" do
Turbo::Streams::TagBuilder.include CustomActions
end
And there you go!
Note: third-party gem makers can build their own custom actions which could be used by Rails, Roda, and/or Bridgetown. As long as they provide a module that's easily included in Turbo::Streams::TagBuilder
and doesn't require Rails as a hard dependency, then that will allow Turbo to flourish as a cross-Ruby-platform framework.
Development
After checking out this repo, run bin/setup
to install dependencies. Then, run bin/rake test
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 bin/rake install
. To release a new version, update the version number in roda-turbo.gemspec
, and then run bin/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/bridgetownrb/roda-turbo. This project is intended to be a safe, welcoming space for collaboration, and contributors are expected to adhere to the code of conduct.
License
The gem is available as open source under the terms of the MIT License.
Code of Conduct
Everyone interacting in the roda-turbo project's codebases, issue trackers, chat rooms and mailing lists is expected to follow the code of conduct.