#to_json throws "show.json.jbuilder missing" - ruby-on-rails

In #show I have this code:
def show
respond_to do |format|
format.html { render :show }
format.json { #my_item.to_json }
end
end
private
def set_trip
#my_item = MyModel.find(params[:id])
end
When I'm requesting "/my_models/1.json", it throws an exception:
Showing app/views/my_models/show.json.jbuilder where line #1 raised:
Missing partial my_models/_my_model with {:locale=>[:en], :formats=>[:json], :variants=>[], :handlers=>[:jbuilder]}. Searched in:
* "app/views"
* "/home/fdsafds/.gem/ruby/2.4.2/gems/apitome-0.1.0/app/views"
Why is that? Do I have to have jbuilder? Why can't it simply convert an object to json?

You don't have to use jbuilder.
You need to call render in your json format block. See the documentation on rendering json.
2.2.8 Rendering JSON
JSON is a JavaScript data format used by many Ajax libraries. Rails
has built-in support for converting objects to JSON and rendering that
JSON back to the browser: render json: #product
You don't need to call to_json on the object that you want to render.
If you use the :json option, render will automatically call to_json
for you.

Related

Ruby on Rails: setting content-type in `responds_to { |f| f.any`

Using Rails API I had a /events endpoint that could be called without specifying the format; no /events.json needed. The route:
resources :events, defaults: { format: :json }
The controller:
def index
#events = get_events
render json: #events
end
Now I want to respond to /events.csv with a CSV file, but otherwise keep the behavior unchanged. Before getting to the CSV, I add a respond_to block:
def index
#events = get_events
respond_to do |format|
format.any { render json: #events }
end
end
This has almost the same behavior as the previous approach, except that requests to /events now have a header Content-Type: application/html rather than application/json.
I find this surprising, given that I specify json in that block! But looking at the Rails source code, I see that they indeed skip setting the Content-Type header for any responses. This makes it always set Content-Type to whatever the user requested, rather than matching Content-Type to the actual response. If someone requests XML, it returns JSON but calls it XML. If someone requests HTML, as I am in my tests without format: :json and when I test without the .json in my browser, it returns JSON but calls it HTML.
Why? And how to I override this and force the Content-Type/mime type to match the actual response?
Rails 6
This issue seems to be changed in Rails 6 so that format.any { render json: #events } will return the Content-Type as application/json as expected
Rails 5
For Rails 5 a workaround can be to force change the Content-Type header:
def index
#events = get_events
respond_to do |format|
format.any {
response.headers['Content-Type'] = 'application/json; charset=utf-8'
render json: #events
}
end
end

Rails looking for template for JSON requests

In my routes.rb I have:
resources :workouts
In my workouts controller I have:
def show
respond_to do |format|
format.html
format.json { render :json => "Success" }
end
end
But when I go to /workouts/1.json, I receive the following:
Template is missing
Missing template workouts/show, application/show with {:locale=>[:en], :formats=>[:json], :handlers=>[:erb, :builder, :coffee]}. Searched in: * "/home/rails/app/views"
Which appears to show that the format is what it should be but it's still searching for a view. This same code functions in other controllers with identical setups just fine. Also, going to /workouts/1 for the html view seems to work just fine, though it also renders the html view properly when the format.html is removed.
Looks at the source code of render
elsif options.include?(:json)
json = options[:json]
json = ActiveSupport::JSON.encode(json) unless json.is_a?(String)
json = "#{options[:callback]}(#{json})" unless options[:callback].blank?
response.content_type ||= Mime::JSON
render_for_text(json, options[:status])
Pay attention to the third line. If the value of :json is a string, render won't call to_json automatically for this value.
So the value remains as string and render will go on to search template.
To fix, supply a valid hash even for trying purpose.
format.json { render :json => {:message => "Success"} }
You would try with /workouts.json

Rails Controller - What is the purpose of "respond to do format" html and json?

Ruby on Rails Question:
Inside the controller you have seven REST actions. Almost all of them have respond to do format xml/html or json. I have no idea what this means. Can you please explain it's purpose.
For example:
def index
#tweets = Tweet.all
respond_to do |format|
format.html
format.json { render json: #tweets }
end
end
What is the purpose of the "respond to" part that contains the html and json? What do these formats do?
Also, what is the difference between xml and html? Sometimes I see xml and other times html.
Thank you
Its just a way of telling your controller how to respond to different request types. For example your client could want html or xml information from you:
def index
#people = Person.find(:all)
respond_to do |format|
format.html
format.xml { render :xml => #people.to_xml }
end
end
What that says is, "if the client wants HTML in response to this action, just respond as we would have before, but if the client wants XML, return them the list of people in XML format." (Rails determines the desired response format from the HTTP Accept header submitted by the client.)
http://apidock.com/rails/ActionController/MimeResponds/InstanceMethods/respond_to

What is actually returned by respond_to?

respond_to do |format|
format.html { render :html => #something }
format.json { render :json => #something }
format.xml { render :xml => #something }
end
Here we have three different formats: html, json, xml. So which one is actually returned? Do we have three different files ending with .html, .xml, .json? Or in other words, does respond_to render all three html, json, xml files?
respond_to is a Rails helper method that is attached to the Controller class (or rather, its super class). It is referencing the response that will be sent to the View (which is going to the browser).
The block in your example is formatting data - by passing in a 'format' paramater in the block - to be sent from the controller to the view whenever a browser makes a request for html or json data.
in rails you can write this also
class PostsController < ApplicationController
respond_to :html, :xml, :js
def index
#posts = Post.all
respond_with(#posts)
end
end
respond_to can render each of the three, according to the current request. The right response is not what's returned from respond_to but what's actually rendered.
You can find the full explanation here

What does respond_to do?

#some instance variables
respond_to do |format|
format.html
format.xml {...}
format.json {...}
end
Does respond_to simply send all instance variables to the next webpage or it does something more?
I'm wondering how much data would be sent by respond_to. For example, if I have many instance variables #one #two #three etc. Are they all sent by respond_to? Any other data would be bundled and sent as well? ?
You instance variables will not be sent anywhere without your direction.
You probably have a .html.erb template that renders the instance variables when an HTML request is received (format.html).
For the xml and json responses, you need to tell Rails what to do. For instance, you could supply a template .xml.builder.
Rails can also render certain structures (arrays etc.) for you automatically, simply by calling render json: #one
Rails goes through the registered formats and tries to find a compatible format, otherwise, it will raise an error.
Example:
def index
#stories = Story.all
end
index action does not have a respond_to block. If a client asks to get the page in TEXT format, it will lead to the following exception:
ActionView::MissingTemplate (Missing template blogs/index ... with { ... :formats=>[:text], ...})
We can easily fix this by adding a respond_to block:
def index
#stories = Story.all
respond_to do |format|
format.html
format.js
end
end
After the change, the client will get 406 error when the format is not supported. Also, your index action will respond to two new formats: js and HTML.
This article explains all the ways you can use respond_to blocks.

Resources