-
-
Notifications
You must be signed in to change notification settings - Fork 72
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
cable_car aka 'Ajax mode' #108
Conversation
@leastbad Coming into this with very little context (looks like a killer API so far!) — if update: ah, I just noticed you did have |
Thanks! I'm just building on what was already here, and borrowing good ideas from @julianrubisch. The short answer is that the reason (I think) a terminating verb is necessary is that a render action is not the only place you might call this. In fact, the whole impetus behind doing it is to rework the backend so that there's a consistent queueing API and then loads of syntactic sugar (like the implicit render stuff) to keep up our BMW 7 Series vibe. The other wildcard is that @joshleblanc made a PR on this PR which I haven't had a chance to review, but my spidey sense tells me is going to have a major impact on what's already been done. |
This is exciting. I love the wordplay but would like to propose different semantics. operations = cable_car.inner_html(...).console_log(...).enqueued_operations
cable_car.dispatch # vs "ride", flushes enqueued_operations
cable_car.append(operations) # vs "apply", appends operations to enqueued_operations (alt option: enqueue) |
The new renderer also opens the door for ActionCable based responses. What do you think about this proposal? ActionController::Renderers.add :cable_car do
render json: cable_car.dispatch
end
ActionController::Renderers.add :cable_ready do
cable_ready.broadcast
head :ok
end class HomeController < ApplicationController
def index
cable_car.console_log(message: "hi")
render :cable_car
end
def show
cable_ready.console_log(message: "hi")
render :cable_ready
end
end That's not a complete example, but it communicates the idea. Let's go for the land grab and add support for a |
Exciting! I haven't watched closely but am thrilled to read all the docs afterwards 😜 |
You're not proposing that we make people put I prefer
I think that your I don't totally understand what you're proposing with |
Correct. This is already supported (but not required). I was just using it as an example of how to get a reference to some operations.
Good catch on cable_car.merge! operations Let me expand on the renderers API a bit and see if we can land on something that everyone likes. The CableReady renderers are more challenging because CR is so incredibly flexible. cable_car# multi line example
def index
cable_car.console_log(message: "hi")
cable_car.console_log(message: "hi again")
render :cable_car
end
# single line example
def index
render cable_car: cable_car.console_log(message: "hi")
end cable_readydef index
render broadcast: cable_ready['string-identifier'].console_log(message: "hello world ")
end
def index
render broadcast: [
cable_ready['one'].console_log(message: "hello world from one"),
cable_ready['two'].console_log(message: "hello world from two")
]
end
def index
render broadcast_to: Helen.find(30), cable_ready[HelensChannel].console_log(message: "hello helen")
end
def index
render broadcast_to: Helen.find(30), [
cable_ready[HelensChannel1].console_log(message: "hello helen from 1"),
cable_ready[HelensChannel2].console_log(message: "hello helen from 2")
]
end |
I don't love Anything jump out at you here? https://www.merriam-webster.com/thesaurus/ingest Naming is super hard. The While I'm ultimately fine with switching from I understand the "what" for a From an implementation perspective, I agree that introducing a I would advise putting the resource after the I like the single-line forms (1 and 3, above) but the array-based multiple channel approach makes me hate the way 2 and 4 look. I might be biased because I still don't understand what this is for, but it just seems like one too-many permutations of syntax for my train to track effectively. 🧠 I feel like the same naming lens critique applies to I don't want to sound negative! I do want to carefully weigh every new code path for benefits vs the perceived complexity and decision fatigue of the library. |
standardrb is automatically converting @operation_builder.merge!(foobar: [{name: "passed_option"}]) to @operation_builder[:foobar] = [{name: "passed_option"}] Which is definitely not what we want. Should we consider this a standardrb bug, or look at different names? |
Actually, maybe we should just get operation builder to respond to [] and []=. That would solve our problem and make it behave like one might expect? |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This looks great.
People have started to notice that you can call
CableReady.perform
anywhere, including outside of anActionCable#received
callback. @jonsgreen calls it inside of a custom CR operation, transforming it into a DSL for DOM manipulation. All you need is a viable JSON payload and you're good to go.As we prepare for a multi-transport future in SR, so too must CR adapt to non-ActionCable usage cases. This PR proposes
cable_car
: a simplified, transport-agnostic class used for generating CableReady-compliant JSON payloads.cable_car
has an almost identical usage pattern as string identifier channels inside Reflex classes, except that instead ofbroadcast
, chains are terminated by.dispatch
.cable_car
is a Singleton likecable_ready
, and chains the same operations list from theconfig
class. Callingdispatch
clears out the enqueued operations unless you call it withclear: false
. It should be completely fine to usecable_ready
andcable_car
alongside each other simultaneously.Thanks to @joshleblanc, the Channel and CableCar are both subclasses that inherit from a new common superclass called OperationBuilder. This means that there is no duplicated code and further changes to OperationBuilder will benefit both.
I also removed what I believe were vestigal references to
@operations
from theChannels
class.# {"innerHtml":[{"selector":"#users","html":"\u003cspan\u003ewinning\u003c/span\u003e"}],"setFocus":[{"selector":"#users"}],"playSound":[{"src":"song.mp3"}]}
Serialized operations via the
apply!
methodThere needs to be a simple way to turn serialized operations into enqueued operations.
The
apply!
concept was lifted from @julianrubisch'scable_ready_callbacks
gem, (where it was originallyapply
).Keep in mind that while
dispatch
returns a Hash,apply!
will accept both a Hash or a JSON string.I also made it so that both
channels
andcable_car
now have ato_json
that returns the JSON forenqueued_operations
instead of the whole object, and this, too, can be passed intoapply!
.Custom ActionController Renderer
Once again thanks to @joshleblanc, controllers can now respond with a CableReady JSON payload, ready to be passed into
CableReady.perform()
.