I'm using Rails 3.2.21 with JBuilder.
I have an example where I'm using an a JBuilder partial inside of a js.erb file to pre populate some fields:
var orderData = <%= raw render :partial => 'orders/orders', formats: [:json], handlers: [:jbuilder], locals: {orders: #orders} %>;
I have a weird problem where if an error is thrown in the jbuilder template, it renders a missing template error. so
If _orders.json.jbuilder looks like this
json.array! orders do |order|
json.someProperty order.a_missing_property
end
I get this:
ActionView::Template::Error (Missing partial orders/orders with {:locale=>[:en], :formats=>[:js, :html], :handlers=>[:erb, :builder, :coffee, :haml, :jbuilder, :riif]}
But if there is no error, this renders properly.
Any idea how my error is getting swallowed?
Update
I've created a demo app here: https://github.com/earnold/error-demo
If you load home/index you get a missing template error. If you comment out the bad line in the template, you render the template normally. What I am trying to do is make sure that errors aren't swallowed, but instead are shown on the page.
I was able to get an answer on Github here: https://github.com/rails/jbuilder/issues/40
Short version: this can be remedied by monkey patching Rails. Put this in an initializer and you're good to go.
if Rails.env.development? || Rails.env.staging?
module ActionView
class Template
protected
def handle_render_error(view, e) #:nodoc:
if e.is_a?(Template::Error)
e.sub_template_of(self)
raise e
else
assigns = view.respond_to?(:assigns) ? view.assigns : {}
template = self
unless template.source
# If an error occurs while the Jbuilder template is being rendered in
# in a nested context, you have a mismatch between the template format
# and the view context. Therefore, this block of code would raise
# a false exception (ActionView::MissingTemplate) and swallow the original
# error. This monkey patch tricks rails into thinking it was a json request
# so that refreshing the source hits the right partial
if template.formats == [:json]
view.lookup_context.formats = [:json]
end
template = refresh(view)
template.encode!
end
raise Template::Error.new(template, assigns, e)
end
end
end
end
end
Related
I'm receiving a deprecation warning when running rails test. That warning being below. Any help is appreciated in identifying what I'm doing incorrectly.
(Edit: Side note, the render MUST break and return from the current controller call. I attempted to use ApplicationController.render(...) in place of the current render call, but that did not return from the controller call and I was receiving errors/warnings of :no_content rendered.)
Warning:
DEPRECATION WARNING: Rendering actions with '.' in the name is deprecated: actions/action_success.json (called from update at /<path>/app/controllers/table_base_controller.rb:39)
The code throwing the warning is specifically this call to render within a controller:
render('/actions/action_success.json', locals: {
view: action.lookup_view('default'),
action: action,
area: current_area,
account: current_account
})
I've tried taking off the .json as directed (also tried adding template: <path>, tried file: <path>), however, I receive this error in the test console:
Error:
TableControllerTest#test_Admin_should_update_via_loan_table:
ActionView::MissingTemplate: Missing template actions/action_success with {:locale=>[:en], :formats=>[:html], :variants=>[], :handlers=>[:raw, :erb, :html, :builder, :ruby, :jbuilder]}. Searched in:
* "/<path>/app/views"
app/controllers/table_base_controller.rb:39:in `update'
app/controllers/application_controller.rb:79:in `with_account'
test/controllers/table_controller_test.rb:14:in `block in <class:TableControllerTest>'
The file in question (path: app/views/actions/action_success.json.jbuilder):
# frozen_string_literal: true
json.status 'success'
json.status_code 200
json.messages action.messages
if view
json.result do
json.partial! view.to_s, result: action.result, locals: { area: area }
end
else
json.result action.result
end
Deprecating partial names that include a . was done to prevent ambiguity in parsing the partial name. We should explicitly state formats instead of including them in the partial name we pass to render.
Without the format in the string you pass in, you need to ensure the formats the render is expecting includes the format you're using, in this case json, which is not one of the default formats.
You can send it in as an option (and make the partial name an option as well) like this:
render(
partial: '/actions/action_success',
formats: [:json],
locals: {
view: action.lookup_view('default'),
action: action,
area: current_area,
account: current_account
}
)
I'm attempting to use render html: to render raw html from a controller action:
class SomeController < ApplicationController
def raw_html
render html: '<html><body>Some body text</body></html>'
end
end
However, when I run this controller action, I get a "Template is missing" error
I don't want to use a template, just render raw html.
The error I get is:
Processing by SomeController#raw_html as HTML
Parameters: {}
ActionView::MissingTemplate (Missing template some_controller/raw_html
with {:locale=>[:en], :formats=>[:html], :handlers=>[:erb, :builder,
:raw, :ruby]}. Searched in: *
"/Users/doved/source/sample_app/app/views" *
"/Users/doved/.rvm/gems/ruby-2.0.0-p353#syp/gems/chameleon-0.2.4/app/views"
* "/Users/doved/.rvm/gems/ruby-2.0.0-p353#syp/gems/kaminari-0.15.1/app/views"):
app/controllers/some_controller.rb:14:in raw_html'
lib/middleware/cors_middleware.rb:8:incall'
I'm using Rails 4.0.2
What am I doing wrong?
html option was added to render method in Rails 4.1 version.
Checkout the discussion on this topic on Github
If you upgrade the Rails version to Rails 4.1 then you would be able to render html as
def raw_html
render html: '<html><body>Some body text</body></html>'.html_safe ## Add html_safe
end
With the current version of Rails 4.0.2, you would need to use
def raw_html
render text: '<html><body>Some body text</body></html>'
end
You are getting error as: ActionView::MissingTemplate
Because currently html option is not supported by render so the value passed with html option is ignored and Rails starts to look for a template some_controller/raw_html in views directory.
Possible duplicate of
How to return HTML directly from a Rails controller?
This should work for you:
render text: '<html><body>Some body text</body></html>'
I'm not asking how to use them, I'm asking how do they work. My controller (just used one of the scaffold generators for my controller) has a method:
def index
#users = User.all
end
From that method, I can either get the html response (index.html.erb), even without specifying "html" in the request, or I can get a json response (index.json.jbuilder).
In other methods there's format.html and format.json. In the above example method index, there's no reference to either "type" of response. Why/how does it work?!?
Request for /users -- expected default html
Request for /users.html -- expected html response
Request for /users.js -- I get back the html content of <body> (maybe that's the partial?)
I do not have a template specified for .js anywhere.
Request for /users.txt -- I get the expected error message:
Missing template users/index, application/index with {:locale=>[:en], :formats=>[:text], :handlers=>[:erb, :builder, :raw, :ruby, :jbuilder]}.
Request for /users.xml -- I get the expected error message, even though xml seems more practical than a .js handler of a main resource:
Missing template users/index, application/index with {:locale=>[:en], :formats=>[:xml], :handlers=>[:erb, :builder, :raw, :ruby, :jbuilder]}
Why is a request for .abc even being processed at all? Even though it's not indicating that it's html, that's how it's processing it.
Started GET "/users.abc" ...
Processing by UsersController#index as
I see that there are :handlers specified, but none specify how/what should be handling the .js request. Why isn't the .js request spitting out an error? Hell, how is this all working without a respond_to block? I guess I expect the html handler by default, but I do not expect the .json response by default. There are often cases where I'd like all kinds of formats to be able to be returned, sometimes I'd prefer only to have a .json response over an html one. How/where do I find the documentation for dealing with this? It's not really related to the respond_to block, since even in the absence of respond_to, I'm getting multi format output.
In a controller, when no mime types are defined, rails will simply render the default one:
def index
#users = User.all
end
is equivalent to
respond_to :html
def index
#users = User.all
respond_with #users
end
This being said, let's take a look at other mime types:
Request for /users.js
This one is interesting, looking at the rails code base here we can see that it will render a template using default_render:
# to_js simply tries to render a template. If no template is found, raises the error.
def to_js
default_render
end
This is the default case for the html mime type also:
# HTML format does not render the resource, it always attempt to render a
# template.
#
def to_html
default_render
rescue ActionView::MissingTemplate => e
# ...
end
One does it means ? -- If a js template is defined, rails will render it. Otherwise, it will fallback to render the default html template. This is why you get the html content of <body>. Look your app/views/layouts/application.html.erb file:
<body>
<%= yield %>
</body>
Request for /users.txt and Request for /users.xml
Basically all "undefined by default" mime types will require you define some logic to not throw an exception. From rails source:
def respond
method = "to_#{format}"
respond_to?(method) ? send(method) : to_format
end
Rails define by default to_html and to_js. For other mime types, you will need to satisfy the conditions of the following method. For json and xml, it simply means calling to_json, to_xml or using respond_to.
For more information, take a look at the responder.rb file in rails source here. It's always good to look at rails source to really understand how things work. Of course sometimes it requires to spare some time and jumping around methods and files.
I'm not sure what would be the reaction of rails if a controller's method get request of, let's say, JS format while there's no "reposnd_to" block, but I think that it'd answer with error "unacceptible format". So I think that by default it assumes that request is of html format and to answer for other than html you have to point out with respond_to format block.
What about how the mechanism work, "rails determines the desired response format from the HTTP Accept header submitted by the client" (https://github.com/rails/rails/blob/master/actionpack/lib/action_controller/metal/mime_responds.rb#L85) rails sources says. So accepted formats is stated explicitly while generating request in mime-type
I'm trying to render my pagination links inside an ajax request with kaminari and im getting a server error. I'm using the render_to_string method to render the pagination links to a string then parse it via json. I'm using rails 3.1.0.
ActionView::Template::Error (Missing partial kaminari/paginator with {:handlers=>[:erb, :builder, :haml], :formats=>[:json], :locale=>[:en, :en]}. Searched in:
Basically it's looking for the partials in all my load paths and can't seem to find the files, and they're there for sure.
Has anyone experienced similar behavior and know of a possible reason?
I just ran into this as well. I was able to work around it by moving render_to_string into a respond_to block -
respond_to do |format|
format.js do
foo = render_to_string(:partial => 'some_kaminari_view').to_json
render :js => "$('#foo').html(#{foo})"
end
end
See here: http://whowish-programming.blogspot.com/2011/07/stupid-rails3-with-missing-template-and.html
Just append .html to your view name.
For a plugin I want to hack the following feature into Rails:
When a (partial) template does not exist (regardless of the format) I want to render a default template.
So say I call an action 'users/index' if users/index.html.erb does not (or other format) exist, 'default/index.html.erb' should be rendered.
Similarly, If I call an action 'locations/edit' and 'locations/edit.html.erb' does not exist, 'default/edit.html.erb' should be rendered
For partials, If I call an action 'locations/index' and the template 'locations/index.html.erb' calls the partial 'locations/_location' which does not exist, it should render 'default/_object'
The solution is seek gives me access to the templates variables (e.g. #users, #locations) and information on the requested path (e.g. users/index, locations/edit). And it should also work with partials.
I have thought of some options which I'll post below. None of them are completely satisfactory.
Solution 2:
Use 'rescue_from' in ApplicationController
class ApplicationController > ActionController::Base
rescue_from ActionView::MissingTemplate do |exception|
# use exception.path to extract the path information
# This does not work for partials
end
end
Drawback: does not work for partials.
Rails 3.1 automatically looks for files in application/template.html.erb after looking in controller/template.html.erb you can see this in the Exception like so:
Missing template [controller name]/index, application/index with {:locale=>[:en, :en], :formats=>[:html], :handlers=>[:erb, :coffee, :builder]}. Searched in: * "/path/to/rails_project/app/views"
so, just put your default templates in app/views/application
I found a patch that is relatively clean, it only patches the lookup of the template which is exactly what was required in the question.
module ActionView
class PathSet
def find_template_with_exception_handling(original_template_path, format = nil, html_fallback = true)
begin
find_template_without_exception_handling(original_template_path, format, html_fallback)
rescue ActionView::MissingTemplate => e
# Do something with original_template_path, format, html_fallback
raise e
end
end
alias_method_chain :find_template, :exception_handling
end
end
Solution 1:
Monkey patch ActionView::Base#render
module ActionView
class Base
def render_with_template_missing(*args, &block)
# do something if template does not exist
render_without_template_missing(*args, &block)
end
alias_method_chain :render, :template_missing
end
end
This monkey patch requires to look into the (changing) internals of rails and results in ugly code, but probably works.