I have an action show
that has a correspondent show.html.erb
file as well. What I'm trying to do is add an "early return" so to say in case the record doesn't exist:
class MessagesController < ApplicationController
def show
unless Message.exists?(mid: params[:id])
return render partial: 'messages/expired'
end
# rendered by views/messages/show.html.erb
end
...
In case the message exists, this works fine and I can see rails rendering all the files/layouts:
19:51:19 web.1 | Message Exists? (0.3ms) SELECT 1 AS one FROM "messages" WHERE "messages"."mid" = $1 LIMIT $2 [["mid", "01G7FD3BBGE2EJH3ABVC5RRWAG"], ["LIMIT", 1]]
19:51:19 web.1 | ↳ app/controllers/messages_controller.rb:3:in `show'
19:51:19 web.1 | Rendering layout layouts/application.html.erb
19:51:19 web.1 | Rendering messages/show.html.erb within layouts/application
19:51:19 web.1 | Rendered messages/show.html.erb within layouts/application (Duration: 1.9ms | Allocations: 1159)
19:51:19 web.1 | Rendered layouts/_nav.html.erb (Duration: 0.4ms | Allocations: 129)
19:51:19 web.1 | Rendered layout layouts/application.html.erb (Duration: 82.3ms | Allocations: 26506)
19:51:19 web.1 | Completed 200 OK in 94ms (Views: 88.6ms | ActiveRecord: 2.6ms | Allocations: 29578)
but here is the weird thing: in case the record doesn't exist, Rails does render the partial, but only the partial:
19:52:56 web.1 | Message Exists? (0.6ms) SELECT 1 AS one FROM "messages" WHERE "messages"."mid" = $1 LIMIT $2 [["mid", "01G7FD3BBGE2EJH3ABVC5RRWA"], ["LIMIT", 1]]
19:52:56 web.1 | ↳ app/controllers/messages_controller.rb:3:in `show'
19:52:56 web.1 | Rendered messages/_expired.html.erb (Duration: 0.1ms | Allocations: 22)
19:52:56 web.1 | Completed 200 OK in 10ms (Views: 1.3ms | ActiveRecord: 0.6ms | Allocations: 885)
19:52:56 web.1 |
I don't remember seeing this behavior before so I'm really confused...what am I doing wrong? Honestly, I'm a little burned out so it could be I can't see something that is right in my face but I simply can't understand why is this happening...
Interestingly, if do render 'messages/expired'
, without the partial:
and rename the file to remove the underscore so it is not a partial anymore, it works:
19:56:04 web.1 | ↳ app/controllers/messages_controller.rb:3:in `show'
19:56:04 web.1 | Rendering layout layouts/application.html.erb
19:56:04 web.1 | Rendering messages/expired.html.erb within layouts/application
19:56:04 web.1 | Rendered messages/expired.html.erb within layouts/application (Duration: 0.1ms | Allocations: 141)
19:56:04 web.1 | Rendered layouts/_nav.html.erb (Duration: 0.4ms | Allocations: 124)
19:56:04 web.1 | Rendered layout layouts/application.html.erb (Duration: 2.6ms | Allocations: 2627)
19:56:04 web.1 | Completed 200 OK in 7ms (Views: 3.4ms | ActiveRecord: 2.1ms | Allocations: 5494)
wild guess: it has something to do with the fact that all of that is happening on show
so some Rails' default kicks in.
CodePudding user response:
render
method in controllers processes its arguments a bit differently than in views.
# NOTE: :body, :plain, :html, :inline, :partial
# will render without a layout
# unless :layout option is specified
# https://github.com/rails/rails/blob/v7.0.3/actionview/lib/action_view/layouts.rb#L431
def show
# Without layout
render partial: "expired" # renders "messages/_expired"
# With layout
render "expired" # renders "messages/expired"
render template: "messages/expired" # renders "messages/expired"
render "_expired" # renders "messages/_expired"
# Doesn't work
render partial: "expired", # renders "messages/_expired"
layout: "application" # in "layouts/_application" # <= doesn't exist
end
In the view:
# Without layout
<%= render "expired" %> # renders "messages/_expired"
<%= render partial: "expired" %> # renders "messages/_expired"
<%= render template: "messages/_expired" %> # renders "messages/_expired"
<%= render template: "messages/expired" %> # renders "messages/expired"
# With layout
<%= render template: "messages/expired", layout: "layouts/application" %>
# renders "messages/expired"
# in "application" layout (again)
<%= render partial: "expired", layout: "application" %>
# renders "messages/_expired"
# in "messages/_application" partial layout
You can keep _expired partial or rename it to expired and just use the appropriate render call.
https://api.rubyonrails.org/classes/AbstractController/Rendering.html#method-i-render
https://api.rubyonrails.org/classes/ActionView/Helpers/RenderingHelper.html#method-i-render