CableReady is zero-config, meaning that there is no elaborate setup ritual. Still, there are several ways to tweak how things work, especially when it comes to integrations with other libraries.
You can add your own operations to CableReady by creating an initializer:
config/initializers/cable_ready.rbCableReady.configure do |config|config.add_operation_name :jazz_handsend
Then you need to add your operation's implementation to the CableReady client, ideally before you import your Stimulus controllers and/or ActionCable channel subscribers. Note that while the Ruby
Now you can call your custom operation like any other. Any options passed to the operation when it is queued will be sent as part of the broadcast to the client.
You can find inspiration for your own operations by checking out how the "factory default" operations were implemented. The
innerHtml methods are an excellent starting point.
If you need for your custom operation to support multi-element selectors, you will need to import the
You can now call your custom operation with
select_all: true and you will see a console log message for every matching element.
cable_ready["visitors"].jazz_hands(selector: ".hand", select_all: true, thumbs: 2).broadcast
Would you like your custom operations to raise
after events? Would you like your custom operations to be
cancelable and able to be
utils class provides all of the helper functions you might need:
This looks more complex than it is. In reality, we did the work so you don't have to.
callee as the second argument to our custom operation.
Inside of the
processElements closure, we use the
after functions to handle emitting our standardized events. In between, we pass our custom logic to the
operate function. Someday soon, developers will be able to add processing stages to the
operate function, but today, it's responsible for making operations able to be cancelled on the client, as well as making it possible for operations to
delay their own execution.
Finally, you can pull any options that developers might have set when enqueueing the operation using object destructuring. In the example above, we accessed the
action from the
cable_ready["visitors"].jazz_hands(name: "Helen", dance_move: "Moonwalk").broadcast
In addition to defining custom operations in your
application.js, you can now load them from npm packages. The first example of this is the play_sound operation, which was removed from CableReady core in v5.0.0; some people didn't love our implementation, and we wanted them to be able to write their own - or skip it entirely.
play_sound now lives in the cableready/audio_operations repo on GitHub, which you can both import and clone to use as the basis for your own custom operations packages.
A package could be a great solution for you if you're trying to keep your application pack skinny, if you're trying to group/organize a large number of operations, or if you're sharing operations across multiple projects. You might even publish and promote your best custom operations for other CableReady developers to use.
First, import the package:
yarn add @cable_ready/audio_operations
play_sound as an operation to your CableReady initializer:
config/initializers/cable_ready.rbCableReady.configure do |config|config.add_operation_name :play_soundend
Now, add the lines required to import the
AudioOperations to your pack file, and you can continue to use
play_sound exactly as you did before:
The morphdom library supports providing a callback function to decide whether a DOM element should be morphed; if this callback returns false for an element, neither that element nor any of that element's children will be morphed.
There is also a corresponding callback function that will run only if an element did get morphed. So this is not "before and after", but "should I and did I?" By adding our own callback functions, we can add support for interacting with other libraries which would otherwise be difficult or impossible.
shouldMorph, sequentially executes an array of functions called
shouldMorphCallbacks. It comes factory installed with two callbacks that you can probably leave alone:
verifyNotPermanent. If you're not using StimulusReflex, you could experiment with
slice to remove
verifyNotPermanent for a small performance boost. 🤷
These callbacks need to return true if the element should be morphed, or else return false to skip it. All callbacks must return a boolean value, even if the purpose of of the callback is to perform some kind of meta-transformation on the elements, as you'll see with the Alpine example in a moment.
Your shouldMorph callback function will receive three parameters: the
options passed to the
fromEl, which is the element before it is (potentially) morphed, and
toEl, which is the element
fromEl will (potentially) be morphed into.
The primary use case of a shouldMorph callback is to skip elements that meet a certain criteria - or don't. This could be handy if you need to protect part of the DOM:
A sneaky second use of shouldMorph callbacks is to assume the function will return true, but use it as an opportunity to prepare the incoming element by copying something from the outgoing element. This is important for AlpineJS users, who need to clone Alpine's internal state machine so that components are properly initialized:
didMorph, sequentially executes an array of functions called
didMorphCallbacks. These callbacks will only fire for elements which were successfully morphed.
Your didMorph callback function will receive two parameters: the
options passed to the
morph method, and
el, which is the element after it has been morphed.
In the following example, users of the Shoelace web component UI toolkit can add the
hydrated CSS class to every morphed element with a tagname that starts with "SL-".
Do you love CableReady's
perform method, but wish that it returned a Promise? You are in luck!
By default, CableReady will generate a warning in your Console Log if you attempt to execute an operation on a selector that does not exist. Sometimes, this isn't desirable and you just want to silently ignore missing elements. This can be achieved by passing an object with
emitMissingElementWarnings: false as the second parameter to
An example of this behavior at work is in the Optimism channel subscriber class, which can occasionally generate validation warnings for fields that don't exist in your View: