-
-
Notifications
You must be signed in to change notification settings - Fork 20
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
Invoke commands via HTTP POST #127
Conversation
hopsoft
commented
Feb 17, 2024
•
edited
Loading
edited
@@ -113,6 +117,7 @@ def dom_id_selector(...) | |||
end | |||
|
|||
# Same method signature as ActionView::Rendering#render (i.e. controller.view_context.render) | |||
# Great for rendering partials with short-hand syntax sugar → `render "/path/to/partial"` | |||
def render(options = {}, locals = {}, &block) | |||
return controller.view_context.render(options, locals, &block) unless options.is_a?(Hash) |
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.
off topic, I might steal that for TBP
Mime::Type.register "text/vnd.turbo-boost.html", :turbo_boost | ||
app.middleware.insert 0, TurboBoost::Commands::Middleware |
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.
I trust that you know that this is the right spot in the middleware chain to insert it 😅
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.
Yep we want it in as early as possible before rails or its middleware gets the request.
@@ -241,6 +245,28 @@ def response_type | |||
:unknown | |||
end | |||
|
|||
# Indicates if a TurboStream template exists for the current action. | |||
# Any template with the format of :turbo_boost or :turbo_stream format is considered a match. |
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.
turbo boost templates are new, right?
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.
They've been supported for a while now given the Rails mechanics around mime types etc.
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.
thats 🤯 . needs an example or README entry, I guess. I wasn't aware of it.
# use the masked token for the client state | ||
command_state[:command_token] = message_verifier.generate(new_command_token) | ||
client_state = command_state.to_json | ||
|
||
# use the unmasked token for the signed (server) state | ||
command_state[:command_token] = new_command_token | ||
signed_state = command_state.to_sgid_param |
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.
hm, I think this would be cleaner if the State
object just had two methods to render state
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.
Will punt on this refactor for now.
@@ -14,4 +14,8 @@ class ApplicationCommand < TurboBoost::Commands::Command | |||
rescue_from TurboBoost::Commands::PerformError do |error| | |||
# do something... | |||
end | |||
|
|||
def resolve_state(changed_state) | |||
state.merge! changed_state |
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.
shouldn't that be command_state.merge?
?
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.
It's state
when inside a command, and command_state
when outside a command.
|
||
const invoke = (payload = {}) => { | ||
try { | ||
fetch(urls.commandInvocationURL.href, { |
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.
just wanted to mention the possibility to use https://github.com/rails/request.js here
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.
Apart from a potential spelling mistake, I see nothing but exciting possibilities and improvements with the code!
@@ -113,6 +117,7 @@ def dom_id_selector(...) | |||
end | |||
|
|||
# Same method signature as ActionView::Rendering#render (i.e. controller.view_context.render) | |||
# Great for rendering partials with short-hand syntax sugar → `render "/path/to/partial"` |
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 is where I'd like to hook into TurboBoost and allow both a ViewComponent and a Phlex::View or Component to be rendered.
Thank you for the additional comment, now I know where to look.
# Any template with the format of :turbo_boost or :turbo_stream format is considered a match. | ||
# @return [Boolean] true if a TurboStream template exists, false otherwise | ||
def turbo_stream_template_exists? | ||
controller.lookup_context.exists? controller.action_name, controller.lookup_context.prefixes, formats: [:turbo_boost, :turbo_stream] |
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.
I might have to hook in here, too.
append_response_header "TurboBoost-Command-Status", "HTTP #{controller.response.status} #{TurboBoost::Commands::HTTP_STATUS_CODES[controller.response.status]}" | ||
|
||
values = [ | ||
status || "#{controller.response.status} #{TurboBoost::Commands::HTTP_STATUS_CODES[controller.response.status]}".delete(","), |
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.
Curious question: why delete(",")
? Looks a bit like a smell at first glance.
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.
Just a safeguard as I use ,
as the value delimiter for multiple values stored under the single HTTP header now.
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.
Ensures we can properly tokenize those values.
@store.cleanup | ||
@provisional = provisional | ||
end | ||
|
||
delegate :to_json, to: :to_h | ||
delegate_missing_to :store | ||
|
||
def dig(*keys) |
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.
Phenomenal! Now that state is a little more complex this makes perfect sense.
append_error_to_response error | ||
else | ||
render_response status: :internal_server_error, headers: {"TurboBoost-Command-Status": error.message} | ||
render_response status: :internal_server_error, status_header: error.message |
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.
I was going to complain about this, but then I saw the status_header usage underneath. It's a shame you sometimes need more context in the diff here.
} | ||
|
||
function append(content) { | ||
document.body.insertAdjacentHTML('beforeend', content) | ||
// TODO: Revisit the "Replace" strategy after morph ships with Turbo 8 |
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.
It already did ship ;)
fetchOptions.headers['Accept'] = acceptHeaders | ||
} | ||
}) | ||
|
||
// fires after receiving a turbo HTTP response | ||
addEventListener('turbo:before-fetch-response', event => { |
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.
So this is the meat of how we hook into turbo. I'm glad you asked me to have a look at the code; I am learning a lot