-
Notifications
You must be signed in to change notification settings - Fork 21.7k
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
when a template is missing for the default render, do head no_content instead #19377
Conversation
@@ -8,6 +8,8 @@ def send_action(method, *args) | |||
|
|||
def default_render(*args) | |||
render(*args) | |||
rescue ActionView::MissingTemplate |
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.
Concerns around this being slow / an exceptional state when it doesn't need to be. It should be once-per-request code path, and it was exceptional but being handled.
I'm happy to check if the template exists or not, just need to find out how to turn a method into the correct template name.
Yes, this is the best solution here. Lets split Also it is missing a test for implicit render with non-defined actions. |
@rafaelfranca in send_action, how do you get access to the action_name from the method? I need this to do template_exists as the method/action_name may differ. |
It will hardly differ. |
@rafaelfranca I think that variants through off the def test_variant_with_implicit_rendering
@request.variant = :mobile
get :variant_with_implicit_rendering
assert_equal "text/html", @response.content_type
assert_equal "mobile", @response.body
end |
Looking into variant implicit rendering more. It looks like the default behavior is to render the variant name. I can not find where in actionpack or actionview this behavior is defined at. Should the default for variants be to render the variant name, or head :no_content? |
@rafaelfranca do you have any feedback on how to approach the implicit variant rendering? |
@sb8244 what does render the variant name mean? Can you show a code example? |
Sure @kaspth def test_variant_with_implicit_rendering
@request.variant = :mobile
get :variant_with_implicit_rendering
assert_equal "text/html", @response.content_type
assert_equal "mobile", @response.body
end This test is expecting that the behavior of an implicit rendering of a variant is the variant name. However, it seems like this behavior is going to change based on this PR, so that the default behavior is a |
ret | ||
return ret if performed? | ||
|
||
if template_exists?(method, _prefixes) |
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.
Because of this do we still need method_for_action
down below?
Alternatively, we might be able to implement this as:
def default_head(*)
logger.info "No template found for #{self.class.name}#{action_name}, rendering head :no_content"
head :no_content
end
def method_for_action(action_name)
if method = super
method
elsif template_exists?(action_name.to_s, _prefixes)
"default_render"
else
"default_head"
end
end
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.
method_for_action
is important when you are rendering a method that doesn't exist (super is nil in that case). Implementing it like you proposed is how it should be handled, although template_exists? is not picking up variants correctly here as well as in send_action
.
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 propose as leaving method_for_action
alone. The current method body is:
def method_for_action(action_name)
super || if template_exists?(action_name.to_s, _prefixes)
"default_render"
end
end
The reason I say this is that the default behavior of default_head
should apply when there is a valid method that doesn't call render but not in the case of there being no method at all. This could lead to more complex errors such as getting head :no_content
that doesn't seem to apply to a method.
Ah, I see where I went astray. The default render for variants just renders the correct template, say That's why you can't find any rendering of the variant name defined in Action Pack or Action View. The reason you're seeing To answer your question |
@@ -608,16 +608,20 @@ def test_invalid_format | |||
end | |||
|
|||
def test_invalid_variant | |||
logger = ActiveSupport::LogSubscriber::TestHelper::MockLogger.new | |||
ActionController::Base.logger = logger |
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 test overwrites the logger, which will leek into other tests. You have to capture the original value and then set it again after the tests:
def test_invalid_variant
logger = ActiveSupport::LogSubscriber::TestHelper::MockLogger.new
old_logger, ActionController::Base.logger = ActionController::Base.logger, logger
# tests...
ensure
ActionController::Base.logger = old_logger
end
@sb8244 Left a few notes. We also need an entry at the top of Action Pack's CHANGELOG.md file. Finally squash all your commits into one. Once those things have been addressed, we should be good to go, 👍. |
@rafaelfranca do we need a deprecation warning for this too or an upgrade flag (raise instead of no content)? We might even be able to remove the missing template error completely. |
@kaspth in the case of mobile template being rendered, wouldn't we want that in this specific test? There is a template that exists, so |
@sb8244 sorry, for not being clearer. What you're saying is what we want: render the variant template if it's there or |
Awesome, I'm digging through template_exists? now to figure out how to properly pass the variant. It's not getting used currently so |
I've determined these will work:
or set .variants
Any preference? |
@sb8244 do we need either of those? The way I see it variants are already handled. For instance the original tests would render the correct template if the correct variant was set and otherwise raise
|
The variants are not handled in the case of implicit rendering in the proposed change. It worked previously because the following code snippet always ret = super
default_render unless performed?
ret In the proposed change, it only default_renders if the template exists, so the implicit rendering of variant doesn't get handled. |
Ah, I see. Well, I don't like mutating the Can we change
This way we should be able to call it like: template_exists?(method, _prefixes, variants: @_request.variant) |
end | ||
|
||
def default_render(*args) | ||
render(*args) | ||
end | ||
|
||
def default_head(*) |
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.
We should extend method_for_action
to use this too, right? Otherwise we should just put this back in the else clause above.
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 made a comment about that on the original comment you did. I think we should not change method_for_action because it isn't in the use case and could very easily lead to silent confusion in certain cases. I'll pull it back into the send_action
method.
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.
Yeah, I thought about this some more and you're right, 👍.
when a template is missing for the default render, do head no_content instead
Woot first Rails commit! See yall at Rails conf. On Monday, April 6, 2015, David Heinemeier Hansson notifications@github.com
Steve Bussey •SalesLoft •Software Engineer e steve.bussey@salesloft.com w salesloft.com a 3423 Piedmont Rd NE, Atlanta, GA 30305 [image: Facebook] https://www.facebook.com/SalesLoft [image: Twitter] |
After merging #19377 ActionPack tests were missing a require for `ActiveSupport::LogSubscriber::TestHelper` and change didn't take into account that logger could be nil. Added the require and only log to info if logger exists. This wasn't caught earlier because these tests only run after a merge.
I'd love to find a way to bring back the "Missing template" exception for a regular browser request to fix #23077. As a start, I spent some time looking through this merge and how it compares to the behavior in Rails 4. The primary difference is that For browser requests, it would be ideal to just call def default_render(*args)
if request.format == Mime[:html]
render(*args)
end
# current implementation for all other formats
# which returns a 204 if the template is missing
end I realize I'm really late to this party and may be missing subtleties that were addressed in the original discussion. Is there general interest in a pull request along these lines that raises a Any help or guidance is appreciated. |
@clarkware yeah, do open a pull request. ❤️ Your suggestion also comports with @matthewd's here: #19036 (comment) and #19036 (comment) |
Yeah, I think the only potential challenge will be in finding the right way of defining an |
That is my concern as well. For instance, a potentially interesting side Steve Bussey • SalesLoft • Software Engineer e steve.bussey@salesloft.com w salesloft.com a 3423 Piedmont Rd NE, Atlanta, GA 30305 [image: Facebook] https://www.facebook.com/SalesLoft [image: Twitter] |
Thanks for the input! Here's a first cut at a pull request: #23564 |
1. Conceptually revert #20276 The feature was implemented for the `responders` gem. In the end, they did not need that feature, and have found a better fix (see heartcombo/responders#131). `ImplicitRender` is the place where Rails specifies our default policies for the case where the user did not explicitly tell us what to render, essentially describing a set of heuristics. If the gem (or the user) knows exactly what they want, they could just perform the correct `render` to avoid falling through to here, as `responders` did (the user called `respond_with`). Reverting the patch allows us to avoid exploding the complexity and defining “the fallback for a fallback” policies. 2. `respond_to` and templates are considered exhaustive enumerations If the user specified a list of formats/variants in a `respond_to` block, anything that is not explicitly included should result in an `UnknownFormat` error (which is then caught upstream to mean “406 Not Acceptable” by default). This is already how it works before this commit. Same goes for templates – if the user defined a set of templates (usually in the file system), that set is now considered exhaustive, which means that “missing” templates are considered `UnknownFormat` errors (406). 3. To keep API endpoints simple, the implicit render behavior for actions with no templates defined at all (regardless of formats, locales, variants, etc) are defaulted to “204 No Content”. This is a strictly narrower version of the feature landed in #19036 and #19377. 4. To avoid confusion when interacting in the browser, these actions will raise a `TemplateMissing` error for “interactive” requests instead. (The precise definition of “interactive” requests might change – the spirit here is to give helpful messages and avoid confusions.) Closes #20666, #22854, #23062, #23077, #23564 [Godfrey Chan, Jon Moss, Mike Clark, Matthew Draper]
1. Conceptually revert rails#20276 The feature was implemented for the `responders` gem. In the end, they did not need that feature, and have found a better fix (see heartcombo/responders#131). `ImplicitRender` is the place where Rails specifies our default policies for the case where the user did not explicitly tell us what to render, essentially describing a set of heuristics. If the gem (or the user) knows exactly what they want, they could just perform the correct `render` to avoid falling through to here, as `responders` did (the user called `respond_with`). Reverting the patch allows us to avoid exploding the complexity and defining “the fallback for a fallback” policies. 2. `respond_to` and templates are considered exhaustive enumerations If the user specified a list of formats/variants in a `respond_to` block, anything that is not explicitly included should result in an `UnknownFormat` error (which is then caught upstream to mean “406 Not Acceptable” by default). This is already how it works before this commit. Same goes for templates – if the user defined a set of templates (usually in the file system), that set is now considered exhaustive, which means that “missing” templates are considered `UnknownFormat` errors (406). 3. To keep API endpoints simple, the implicit render behavior for actions with no templates defined at all (regardless of formats, locales, variants, etc) are defaulted to “204 No Content”. This is a strictly narrower version of the feature landed in rails#19036 and rails#19377. 4. To avoid confusion when interacting in the browser, these actions will raise a `TemplateMissing` error for “interactive” requests instead. (The precise definition of “interactive” requests might change – the spirit here is to give helpful messages and avoid confusions.) Closes rails#20666, rails#22854, rails#23062, rails#23077, rails#23564 [Godfrey Chan, Jon Moss, Kasper Timm Hansen, Mike Clark, Matthew Draper]
1. Conceptually revert #20276 The feature was implemented for the `responders` gem. In the end, they did not need that feature, and have found a better fix (see heartcombo/responders#131). `ImplicitRender` is the place where Rails specifies our default policies for the case where the user did not explicitly tell us what to render, essentially describing a set of heuristics. If the gem (or the user) knows exactly what they want, they could just perform the correct `render` to avoid falling through to here, as `responders` did (the user called `respond_with`). Reverting the patch allows us to avoid exploding the complexity and defining “the fallback for a fallback” policies. 2. `respond_to` and templates are considered exhaustive enumerations If the user specified a list of formats/variants in a `respond_to` block, anything that is not explicitly included should result in an `UnknownFormat` error (which is then caught upstream to mean “406 Not Acceptable” by default). This is already how it works before this commit. Same goes for templates – if the user defined a set of templates (usually in the file system), that set is now considered exhaustive, which means that “missing” templates are considered `UnknownFormat` errors (406). 3. To keep API endpoints simple, the implicit render behavior for actions with no templates defined at all (regardless of formats, locales, variants, etc) are defaulted to “204 No Content”. This is a strictly narrower version of the feature landed in #19036 and #19377. 4. To avoid confusion when interacting in the browser, these actions will raise an `UnknownFormat` error for “interactive” requests instead. (The precise definition of “interactive” requests might change – the spirit here is to give helpful messages and avoid confusions.) Closes #20666, #22854, #23062, #23077, #23564 [Godfrey Chan, Jon Moss, Kasper Timm Hansen, Mike Clark, Matthew Draper]
1. Conceptually revert #20276 The feature was implemented for the `responders` gem. In the end, they did not need that feature, and have found a better fix (see heartcombo/responders#131). `ImplicitRender` is the place where Rails specifies our default policies for the case where the user did not explicitly tell us what to render, essentially describing a set of heuristics. If the gem (or the user) knows exactly what they want, they could just perform the correct `render` to avoid falling through to here, as `responders` did (the user called `respond_with`). Reverting the patch allows us to avoid exploding the complexity and defining “the fallback for a fallback” policies. 2. `respond_to` and templates are considered exhaustive enumerations If the user specified a list of formats/variants in a `respond_to` block, anything that is not explicitly included should result in an `UnknownFormat` error (which is then caught upstream to mean “406 Not Acceptable” by default). This is already how it works before this commit. Same goes for templates – if the user defined a set of templates (usually in the file system), that set is now considered exhaustive, which means that “missing” templates are considered `UnknownFormat` errors (406). 3. To keep API endpoints simple, the implicit render behavior for actions with no templates defined at all (regardless of formats, locales, variants, etc) are defaulted to “204 No Content”. This is a strictly narrower version of the feature landed in #19036 and #19377. 4. To avoid confusion when interacting in the browser, these actions will raise an `UnknownFormat` error for “interactive” requests instead. (The precise definition of “interactive” requests might change – the spirit here is to give helpful messages and avoid confusions.) Closes #20666, #23062, #23077, #23564 [Godfrey Chan, Jon Moss, Kasper Timm Hansen, Mike Clark, Matthew Draper]
A 204 No Content is returned if the lock request is successful. [Rails 5](rails/rails#19377) does this by default by automatically adding `:no_content` to the header if it can't find a template to respond with.
A 204 No Content is returned if the unlock request is successful. [Rails 5](rails/rails#19377) does this by default by automatically adding `:no_content` to the header if it can't find a template to respond with.
Fixes #19036
#19036 (comment)
I want to do a comparison like:
However, there is no way (that I know of) to access the action at this point, it is simply the method. I opted for rescuing
MissingTemplate
as an iteration 1 that I will look into refactoring. Ideally, the logic for renderingdefault_render
vshead :no_content
will happen inImplicitRender#send_action
and not in default_render.