• Stars
    star
    268
  • Rank 153,144 (Top 4 %)
  • Language
    HTML
  • License
    MIT License
  • Created about 2 years ago
  • Updated 9 months ago

Reviews

There are no reviews yet. Be the first to send feedback to the community and the maintainers!

Repository Details

Take full control of the DOM with Turbo Streams

Welcome to TurboBoost Streams 👋

Lines of Code GEM Version GEM Downloads Ruby Style NPM Version NPM Downloads NPM Bundle Size JavaScript Style Tests GitHub Discussions Discord Community Sponsors
Ruby.Social Follow Twitter Follow

TurboBoost Streams extends Turbo Streams to give you full control of the browser's Document Object Model (DOM).

turbo_stream.invoke "console.log", args: ["Hello World!"]

Thats right! You can invoke any DOM method on the client with Turbo Streams.

Table of Contents

Why boosted Streams?

Turbo Streams intentionally restrict official actions to CRUD related activity. These official actions work well for a considerable number of use cases. We recommend that you push Turbo Streams as far as possible before reaching for boosted streams.

If you find that CRUD isn't enough, boosted streams are there to handle pretty much everything else.

Sponsors

Proudly sponsored by

Community

Come join the party with over 2200+ like-minded friendly Rails/Hotwire enthusiasts on our Discord server.

Dependencies

Installation

Be sure to install the same version for each libary.

bundle add "turbo_boost-streams --version VERSION"
yarn add "@turbo-boost/streams@VERSION --exact"

Setup

Import and intialize Turbo Boost Streams in your application.

# Gemfile
gem "turbo-rails", ">= 1.1", "< 2"
+gem "turbo_boost-streams", "~> VERSION"
# package.json
"dependencies": {
  "@hotwired/turbo-rails": ">=7.2",
+  "@turbo-boost/streams": "^VERSION"
# app/javascript/application.js
import '@hotwired/turbo-rails'
+import '@turbo-boost/streams'

Usage

Manipulate the DOM from anywhere you use official Turbo Streams. The possibilities are endless. Learn more about the DOM at MDN.

turbo_stream.invoke "console.log", args: ["Hello World!"]

Method Chaining

You can use dot notation or selectors and even combine them!

turbo_stream
  .invoke("document.body.insertAdjacentHTML", args: ["afterbegin", "<h1>Hello World!</h1>"]) # dot notation
  .invoke("setAttribute", args: ["data-turbo-ready", true], selector: ".button") # selector
  .invoke("classList.add", args: ["turbo-ready"], selector: "a") # dot notation + selector

Event Dispatch

It's possible to fire events on window, document, and element(s).

turbo_stream
  .invoke(:dispatch_event, args: ["turbo-ready:demo"]) # fires on window
  .invoke("document.dispatchEvent", args: ["turbo-ready:demo"]) # fires on document
  .invoke(:dispatch_event, args: ["turbo-ready:demo"], selector: "#my-element") # fires on matching element(s)
  .invoke(:dispatch_event, args: ["turbo-ready:demo", {bubbles: true, detail: {...}}]) # set event options

Syntax Styles

You can use snake_case when invoking DOM functionality. It will implicitly convert to camelCase.

turbo_stream.invoke :event,
  args: ["turbo-ready:demo", {detail: {converts_to_camel_case: true}}]

Need to opt-out? No problem... just disable it.

turbo_stream.invoke :contrived_demo, camelize: false

Extending Behavior

If you add new capabilities to the browser, you can control them from the server.

// JavaScript on the client
import morphdom from 'morphdom'

window.MyNamespace = {
  morph: (from, to, options = {}) => {
    morphdom(document.querySelector(from), to, options)
  }
}
# Ruby on the server
turbo_stream.invoke "MyNamespace.morph",
  args: [
    "#demo",
    "<div id='demo'><p>You've changed...</p></div>",
    {children_only: true}
  ]

Implementation Details

There's basically one method to learn... invoke

# Ruby
turbo_stream
  .invoke(method, args: [], selector: nil, camelize: true, id: nil)
#         |       |         |              |               |
#         |       |         |              |               |- Identifies this invocation (optional)
#         |       |         |              |
#         |       |         |              |- Should we camelize the JavaScript stuff? (optional)
#         |       |         |                 (allows us to write snake_case in Ruby)
#         |       |         |
#         |       |         |- A CSS selector for the element(s) to target (optional)
#         |       |
#         |       |- The arguments to pass to the JavaScript method (optional)
#         |
#         |- The JavaScript method to invoke (can use dot notation)

📘 NOTE: The method will be invoked on all matching elements if a selector is present.

The following Ruby code,

turbo_stream.invoke "console.log", args: ["Hello World!"], id: "123ABC"

emits this HTML markup.

<turbo-stream action="invoke" target="DOM">
  <template>{"id":"123ABC","receiver":"console","method":"log","args":["Hello World!"]}</template>
</turbo-stream>

When this element enters the DOM, Turbo Streams automatically executes invoke on the client with the template's JSON payload and then removes the element from the DOM.

Broadcasting

You can also broadcast DOM invocations to subscribed users.

  1. First, setup the stream subscription.

    <!-- app/views/posts/show.html.erb -->
    <%= turbo_stream_from @post %>
    <!--                  |
                          |- *streamables - model(s), string(s), etc...
    -->
  2. Then, broadcast to the subscription.

    # app/models/post.rb
    class Post < ApplicationRecord
      after_save do
        # emit a message in the browser conosle for anyone subscribed to this post
        broadcast_invoke "console.log", args: ["Post was saved! #{to_gid.to_s}"]
    
        # broadcast with a background job
        broadcast_invoke_later "console.log", args: ["Post was saved! #{to_gid.to_s}"]
      end
    end
    # app/controllers/posts_controller.rb
    class PostsController < ApplicationController
      def create
        @post = Post.find params[:id]
    
        if @post.update post_params
          # emit a message in the browser conosle for anyone subscribed to this post
          @post.broadcast_invoke "console.log", args: ["Post was saved! #{to_gid.to_s}"]
    
          # broadcast with a background job
          @post.broadcast_invoke_later "console.log", args: ["Post was saved! #{to_gid.to_s}"]
    
          # you can also broadcast directly from the channel
          Turbo::StreamsChannel.broadcast_invoke_to @post, "console.log",
            args: ["Post was saved! #{@post.to_gid.to_s}"]
    
          # broadcast with a background job
          Turbo::StreamsChannel.broadcast_invoke_later_to @post, "console.log",
            args: ["Post was saved! #{@post.to_gid.to_s}"]
        end
      end
    end

📘 NOTE: Method Chaining is not currently supported when broadcasting.

Background Job Queues

You may want to change the queue name for Turbo Stream background jobs in order to isolate, prioritize, and scale the workers independently.

# config/initializers/turbo_streams.rb
Turbo::Streams::BroadcastJob.queue_name = :turbo_streams
TurboBoost::Streams::BroadcastInvokeJob.queue_name = :turbo_streams

FAQ

  • Isn't this just RJS?

    No. But, perhaps it could be considered RJS's "modern" spirtual successor. 🤷‍♂️ Though it embraces JavaScript instead of trying to avoid it.

  • Does it use eval?

    No. The invoke stream can only execute existing functions on the client. It's not a carte blanche invitation to emit free-form JavaScript to be evaluated on the client.

A Word of Warning

TurboBoost Streams is a foundational tool designed to help you build modern, maintainable, and scalable reactive web apps with Hotwire. It allows you to break free from the strict CRUD/REST conventions that Rails and Hotwire wisely encourage. You should consider boosted streams a substrate for building additional libraries and abstractions.

Please don't use TurboBoost Streams to manually orchestrate micro DOM updates (from the server). Such techniques are what gave rise to Full Stack Frontend and sent the industry on a decade long journey of complexity and frustration.

Developing

This project supports a fully Dockerized development experience.

  1. Simply run the following commands to get started.

    git clone -o github https://github.com/hopsoft/turbo_boost-streams.git
    cd turbo_boost-streams
    docker compose up -d # start the envionment (will take a few minutes on 1st run)
    docker exec -it turbo_boost-streams-web rake # run the test suite
    open http://localhost:3000 # open the `test/dummy` app in a browser

    And, if you're using the containers gem (WIP).

    containers up # start the envionment (will take a few minutes on 1st run)
    containers rake # run the test suite
    open http://localhost:3000 # open the `test/dummy` app in a browser
  2. Edit files using your preferred tools on the host machine.

  3. That's it!

Notable Files

Deploying

This project supports Dockerized deployment via the same configurtation used for development, and... it actually runs the test/dummy application in "production". 🤯

The test/dummy app serves the following purposes.

  • Test app for the Rails engine
  • Documentation and marketing site with interactive demos

You can see it in action here. How's that for innovative simplicity?

Notable Files

How to Deploy

fly deploy

Releasing

  1. Run yarn and bundle to pick up the latest
  2. Bump version number at lib/turbo_boost-streams/version.rb. Pre-release versions use .preN
  3. Run rake build and yarn build
  4. Run bin/standardize
  5. Commit and push changes to GitHub
  6. Run rake release
  7. Run yarn publish --no-git-tag-version --access public
  8. Yarn will prompt you for the new version. Pre-release versions use -preN
  9. Commit and push changes to GitHub
  10. Create a new release on GitHub (here) and generate the changelog for the stable release for it

About TurboBoost

TurboBoost is a suite of libraries that enhance Rails, Hotwire, and Turbo... making them even more powerful and boosing your productivity. Be sure to check out all of the various the libraries.

License

These libraries are available as open source under the terms of the MIT License.

More Repositories

1

docker-graphite-statsd

Docker image for Graphite & Statsd
Python
869
star
2

rails_standards

A developer's guide of practices to follow when building Rails applications.
354
star
3

universalid

Fast, recursive, optimized, URL-Safe serialization for any Ruby object
Ruby
350
star
4

turbo_boost-commands

Commands to help you build robust reactive applications with Rails & Hotwire.
HTML
292
star
5

model_probe

ActiveRecord schema visualization and model organization made easy
Ruby
166
star
6

relay

140
star
7

sr_mini

A single file Rails app that will have you running a StimulusReflex and CableReady demo in just 2 steps.
Ruby
139
star
8

turbo_boost-elements

Pre-built easy to use reactive TurboBoost behaviors for Rails/Hotwire apps.
JavaScript
100
star
9

stimulus_reflex_expo

StimulusReflex demos
Ruby
90
star
10

debounced

Debounced versions of standard DOM events
JavaScript
80
star
11

composite_cache_store

A composite cache store comprised of layered ActiveSupport::Cache::Store instances
Ruby
75
star
12

goldmine

Extract a wealth of information from lists
Ruby
75
star
13

stimulus_controllers

[WIP] Stimulus controller library common enough to span multiple projects
JavaScript
58
star
14

chatter

Build a twitter clone in 10 mins with Rails, CableReady, and StimulusReflex
Ruby
53
star
15

tag_columns

Fast & simple Rails ActiveRecord model tagging using PostgreSQL's Array datatype
Ruby
52
star
16

stimulus_reflex_todomvc

An implementation of TodoMVC using Ruby on Rails, StimulusJS, and StimulusReflex
Ruby
49
star
17

bg

Non-blocking ActiveRecord method invocation
Ruby
48
star
18

pipe_envy

Elixir style pipe operator for Ruby
Ruby
46
star
19

hero

Business process modeling for the Rubyist
Ruby
39
star
20

polysearch

Simplified polymorphic full text + similarity search based on postgres
Ruby
38
star
21

coast

RESTful behavior for Rails controllers
Ruby
28
star
22

field_mapper

Data mapping & transformation
Ruby
25
star
23

pry-test

A small test framework that supports debugging test failures & errors when they happen
Ruby
25
star
24

stimulus_toolbox

Stimulus Toolbox is a catalog of Stimulus projects tracked by popularity and other metrics to help you find the libraries you need to build better applications.
Ruby
22
star
25

credentials_demo

Demo of environment aware Rails encrypted credentials with environment variable override
Ruby
21
star
26

active_storage_svg_sanitizer

Sanitize ActiveStorage SVG uploads
Ruby
20
star
27

stimulus_todomvc

[WIP] An implementation of TodoMVC using Ruby on Rails and StimulusJS
Ruby
14
star
28

perm

Simple authorization/permission management in Ruby
Ruby
14
star
29

coin

An absurdly simple DRb based in-memory cache
Ruby
13
star
30

grumpy_old_man

Asserts for RSpec
Ruby
13
star
31

render_later

Improve the user perceived performance of your Rails app
Ruby
13
star
32

trix_embed

Take control over what external links and embedded media is permitted in the Trix editor via copy/paste
JavaScript
11
star
33

docker-ruby-rbx

Trusted Docker Image for Rubinius Ruby
Shell
11
star
34

ellington

A different take on flow based programming.
Ruby
10
star
35

dotfiles

Lua
9
star
36

todomvc

TodoMVC with Rails 7, StimulusReflex, & Import Maps
Ruby
9
star
37

state_jacket

A simple & intuitive state machine
Ruby
9
star
38

turbo_boost-devtools

Devtools for the Hotwire/Turbo ecosystem (TurboBoost, CableReady, StimulusReflex, etc.)
JavaScript
7
star
39

containers

Ruby
7
star
40

roleup

Simple role management
Ruby
6
star
41

legion

Parallel processing made easy
Ruby
6
star
42

kvn

KVN (Key/Value Notation) converter & parser
Ruby
4
star
43

apex-doc

Oracle Application Express (APEX) Quick Reference
4
star
44

bighorn

A standardized interface for event tracking
JavaScript
4
star
45

docker-images

Shell
4
star
46

private-docker-registry

A project to help you build a private docker registry image.
Shell
4
star
47

todo

WIP: CableReady + StimulusReflex + Web Components todo app demo
Ruby
4
star
48

foreign_key_migrations

Plugin that supports adding and removing foreign key constraints in your migrations.
Ruby
3
star
49

github_search

Ruby
3
star
50

docker-nodejs

Docker image for NodeJS
Shell
2
star
51

footing

An ActiveSupport style utility library that employs delegation instead of monkey patching
Ruby
2
star
52

coin_rack

A REST API for Coin
Ruby
2
star
53

fig

The smart way to manage config settings for Rails and Ruby applications using YAML configuration files.
Ruby
2
star
54

self_renderer

Rails model & object rendering outside the context of web requests
Ruby
1
star
55

docker-ruby-mri

Trusted Docker Image for MRI Ruby
Shell
1
star
56

model_definition_tester

Powerful schema and validations testing.
Ruby
1
star
57

string_extensions

Extensions and monkey patches for String.
Ruby
1
star
58

looker

Hash based enumerated types (ENUMS)
Ruby
1
star
59

cable_ready_todomvc

Rails implementation of TodoMVC using the CableReady GEM instead of a heavy SPA framework
Ruby
1
star
60

safe_migrations

Rails plugin that gracefully handles errors in data migrations and prevents you from getting stuck in between.
Ruby
1
star
61

hopsoft.github.io

Web pages for Hopsoft on Github.
JavaScript
1
star
62

docker

Docker images for development and other environments
Dockerfile
1
star
63

active_record_addons

A small library of helper methods for ActiveRecord
Ruby
1
star
64

hustle

Offload CPU heavy computing to separate processes with Ruby blocks.
Ruby
1
star
65

raml_doc

Generate API documentation from RAML files
HTML
1
star
66

stimulus_reflex_client

WIP: Will eventually replace the JavaScript in the StimulusReflex project
JavaScript
1
star
67

han

Hypermedia API Navigation spec
1
star
68

docker-postgres

Trusted Docker Image for PostgreSQL
Shell
1
star