Rails 3.1: Persistent "Template is missing" with JSON format - ruby-on-rails

I have a problem very similar to the one mentioned in this question. However, none of the solutions described in the answers are working.
When trying to access my route, with or without .json extension, as well as with jQuery $.ajax and $.getJSON (with the correct Accept headers), I am getting the following exception:
Missing template users/events/index, application/index with {:handlers=>[:erb, :builder, :coffee, :haml], :formats=>[:json], :locale=>[:en, :en]}.
Since I only need JSON responses for this controller, I don't have nor want any templates for this controller.
My controller is defined like so:
class Users::EventsController < ApplicationController
respond_to(:json)
def index
# Some extra code here...
respond_with(#data)
end
end
With these routes in place (under a "user" scope):
resources(:events, :only => [:index, :show]) do
collection do
get ':year/:month', :to => 'events#index', :format => :json
end
end
The problem persists when adding the do |format| block and explicitly making it render as JSON.
Tried this on both Rails 3.1.1 and on 3.2 and got the same issue.
It looks like a server-side issue since accessing with ".json" doesn't work. What am I missing?

After a bit more tinkering I found out what was the actual cause of this problem.
At some point in the controller I had the following code block:
#array.map { |a| return a['id'] }
The return keyword was incorrectly used and caused the entire action to return ahead of time, causing the issue.

We encountered a similar issue with a custom Devise controller when issues a password reset request via JSON (be sure to use CSRF correctly also).
Problematic code
responds_to :json => [:create, :our_custom_method1, :our_custom_method2]
Working code
responds_to :json
Not happy with the security aspect of allowing more routes to accept JSON, but we'll lock it down further later.

Related

Can someone explain to me how ActionView templates work?

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

Is it necessary to build Rails views when using Ember.js?

I'm updating a Rails app to utilize Ember.js. Those views that existed within the app prior to integrating ember still work fine, but I've also added several new views. These views have all the necessary ember parts (template, controller, etc), as well as all the Rails parts, excluding the view files.
These views work fine if the user accesses them by clicking on an internal link. However, if the user reloads the page or manually enters the URL, then I get this error:
ActionView::MissingTemplate at /contribute
Missing template pages/contribute, application/contribute with {:locale=>[:en], :formats=>[:html], :handlers=>[:erb, :builder, :raw, :ruby, :slim, :coffee]}. Searched in:
* "/home/sites/whistlr/app/views"
* "/home/.rvm/gems/ruby-2.0.0-p0#whistlr/gems/devise-3.0.0/app/views"
This is clearly happening because I do not have view files. The question is, is this strictly necessary? Is there some way to tell Rails to just load up the Ember views? Ideally, I'd just delete all the old Rails view files once the conversation is complete.
It isn't necessary, but you need to setup the rails routes.rb to have a catch all route that also renders just like your index page which displays the ember app and its html.
namespace :api do
# resources go here
end
root :to 'home#index'
match "/*path" => 'home#index'
Note: You want to customize this path pattern to your project, else 404s would also be send here.
It's not necessary to create individual views. The trick is to catch the exception in the application controller and then force it render the layout:
class ApplicationController < ActionController::Base
rescue_from ActionView::MissingTemplate do |exception|
render "/layouts/application"
end
end

ActionView::MissingTemplate: Missing template (Trying to render nonexistent :mobile format )

I have an index.js in place which is fired ok from desktop but gives the following error when I try to open from Mobile Safari.
ActionView::MissingTemplate: Missing template ads/thumbs/index, application/index with {:locale=>[:es, :en], :formats=>[:mobile], :handlers=>[:erb, :builder, :haml]}. Searched in: * "/app/app/views" * "/app/vendor/bundle/ruby/1.9.1/gems/devise-1.5.2/app/views" * "/app/app/views"
I'm not sure why it is looking for a :formats=>[:mobile] when there's no such a file in the folder.
Same thing happens after trying to sign in with devise from mobile phone. I tries to render a nonexistent create.mobile.haml file.
P.S. Is there a way to make :mobile views to fallback default :html views when not found? That would make the trick.
You should in general respond with content-type specific views. In this case, a simple way to get past this short-term issue is to rename index.js to index.mobile.js.
Rails attempts to render views that are specific to the content-type requested -- for example, index.html.haml when html is requested or show.json.haml if you request json. In this case the content-type requested is :mobile.
In the long run you should develop views that will be sent back when different content types are requested.
Here's a simple solution.
class ApplicationController
...
def formats=(values)
values << :html if values == [:mobile]
super(values)
end
...
end
It turns out Rails (3.2.11) already adds an :html fallback for requests with the :js format. Here's ActionView::LookupContext#formats=
# Override formats= to expand ["*/*"] values and automatically
# add :html as fallback to :js.
def formats=(values)
if values
values.concat(default_formats) if values.delete "*/*"
values << :html if values == [:js]
end
super(values)
end
So you can override #formats= yourself and it will be conceivably no more gross and hacky than the existing Rails implementation.

Rails "Template is missing" error, though it exists (3.2.1)

I just started using Rails and am not sure what I'm not doing correctly.
In routes.rb I have
resources :pages
In app/controllers/pages_controller.rb I have
class PagesController < ApplicationController
def index
end
end
I have a layout in app/views/layouts/application.html.erb and a template in app/views/home/pages/index.html.erb which I want rendered when I request "/pages". However, I get the error
Template is missing
Missing template pages/index, application/index with {:locale=>[:en],
:formats=>[:html], :handlers=>[:erb, :builder, :coffee]}. Searched in:
* "/###/app/views"
I've been using stackoverflow for ages without posting, but so many different things seem to trigger this error that it's hard to find answers for my particular case. Also I'm a noob :3 Please help!
You say you have app/views/home/pages/index.html.erb to represent the index view for your pages resource. I think the home/ directory is not required.
In other words, your view file should be app/views/pages/index.html.erb.
It's looking to find it in app/views/pages/index but you have it in app/views/home/pages/index. That slight difference makes it so that the Rails convention is lost.
If you must keep your new directory hierarchy, do this on your controller:
class PagesController < ApplicationController
def index
render :partial => "home/pages/index"
end
end
But, by default, if you have a resource, like :pages, it will automatically look in app/views/pages.
I had this problem and I resolved it by just changing the folder name from car to cars. I had to change the folder name from singular to plural.

render default template when requested template is missing in Rails

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.

Resources