I've run into a bizarre issue running Rails 4.2.7: When I look at the HTML index view for a particular model, everything seems fine. When I request the same data via JSON, I only get a subset of the attributes.
My index action is just:
def index
#coverages = Coverage.all
end
If I call it with
http://localhost:3000/coverages
everything is there. If I call it with
http://localhost:3000/coverages.json
I only get 7 of the attributes returned, in addition to the :id, :created_at, :updated_at, and a :url to the instance (e.g. http://localhost:3005/coverages/1.json).
Is the JSON method somehow truncating the attributes? I never recall running into this before.
POSTSCRIPT:
I modified the index action to:
def index
#coverages = Coverage.all
respond_to do |format|
format.html {render :index}
format.json {render json: #coverages}
end
end
and now I get all the attributes. Probably I was violating Rails best practices by not including the explicit format statements, but I am still curious about my original result.
I'm posting the solution I found (in the Postscript of the question) here in case it is useful. Still an open question as to why the original configuration didn't work.
Modifying the index action to:
def index
#coverages = Coverage.all
respond_to do |format|
format.html {render :index}
format.json {render json: #coverages}
end
end
solves the issue.
Related
I am learning how to write a restful API using RoR and have a question related to it. So, I will explain what I did along with the code.
This is how my controller looks like:
class EmployeesController < ApplicationController
require 'rest_client'
def index
uri = "localhost:3000/employees"
rest_resource = RestClient::Resource.new(uri)
users = rest_resource.get # will get back you all the detail in json format, but it will we wraped as string, so we will parse it in the next step.
#users = JSON.parse(users, :symbolize_names => true) # convert the return data into array
#users.each do |u|
logger.info(u.id)
end
# return based on the format type
respond_to do |format|
format.json {render json: #users}
format.xml {render xml: #users}
format.html
end
end
end
In my Gemfile, I have included rest-client as well.
gem 'rest-client'
My routes are :
root 'employees#index'
resources 'employees'
Hope everything is fine till now.
Now, when I send:
-> Curl request to 'http://localhost:3000/employees', it gets stuck.
-> Get request(by typing in the browser) to 'http://localhost:3000/', it get stuck here as well.
What is that which I am missing?
You don't need RestClient as you're writing a server here, not a client. The browser acts as the client. Remove the call to localhost as it's creating a loop.
The URL for this should already be set in your routes.rb, maybe using:
resources :users
Assuming this is a typical app, the show function should be reading from the database using ActiveRecord.
class EmployeesController < ApplicationController
def index
#users = User.all
respond_to do |format|
format.json {render json: #users}
format.xml {render xml: #users}
format.html
end
end
end
Do you have some other application running on localhost:3000? Because if not, then what your server does is calling himself again and again, causing a loop.
If you do have some other application, which fetches users from database, then be sure its running on some other port, other than this your rails app.
If you have only 1 app, then you don't need rest client.
You actually can do this without any additional gem. You just need to declare your routes according to what you want to expose to your API users and return the type (xml, json, ...) accordingly.
Rails scaffold generated the following:
respond_to do |format|
if #student.save
format.html { redirect_to #student, notice => 'Student was successfully created.' }
format.json { render :show, status: :created, location: #student }
else
format.html { render :new }
format.json { render json: #student.errors, status: :unprocessable_entity }
end
end
After reading this I understand how the respond_to is working (sort of), but I don't get what format is doing. Shouldn't it be either format.html or format.json and not both? What are these two lines actually doing?
format.html { render :new }
format.json { render json: #student.errors, status: :unprocessable_entity }
Is there an implied if in there? Is it something like
if (format == html) {}
if (format == json) {}
Side note: Why does update require the respond_to block while show will handle /students/1.json or /students/1 without any logic at all?
format is a local variable that respond_to yields. When you do format.html {} you are actually registering a callback block for a format.
Rails goes through the registered formats and tries to find a compatible format to the MIME type in the request. If there is no handler it will raise an error.
This could be explained as something like using syntactic sugar on top of a case statement (the Ruby equivalent of a switch statement). But the analogy is not completely accurate since Rails does a bit of work in matching the request type.
Also the code inside your block is not executed when the format.html block is registered (as it would be if it was just a conditional statement) but rather when respond_to finishes or not at all if you are using for example E-Tag caching.
Why does update require the respond_to block while show will handle
/students/1.json or /students/1 without any logic at all?
Rails handles many actions by using a convention over configuration approach and guessing the intent of the action.
def PostsController < ApplicationController
def index
# rails auto-magically fills in the controller with something
# like this
#posts = Post.all
respond_to do |format|
format.html { render :index }
format.json { render json: #posts }
end
end
def show
# convention over configuration is awesome!
#post = Post.find(params[:id])
respond_to do |format|
format.html { render :show }
format.json { render json: #post }
end
end
def new
#post = Post.new
render :new
end
def edit
#post = Post.find(params[:id])
render :edit
end
end
Rails assumes that there is a resource with the same name as the controller and auto-magically fills in the controller action. It also assumes there is a view in app/views/posts/(:action).html.[erb|haml|slim|jbuilder]. This is known as implicit rendering.
The comments show roughly what action rails attempts.
It does not fill in actions which operate on data (create, update, destroy) since the actual implementation can vary greatly and it's hard to make useful guesses.
Well, it depends on the format of the request. If a request demands HTML from the server, format.html block will be executed, and in the same way, if a request demands JSON format, format.json will be executed.
Rails will automatically(read: magically) handle the if (format == html) part for you. All you have to do is fill in the blanks. Same way, you can write a block for XML starting with format.xml.
And for the side note, I think you have said it otherwise. update method doesn't require respond_to block, while show requires. And the reason is very simple: update method is there to update the Model, and then, redirect you to somewhere, while show will always return you something. In your case, /students/1 will return you the first student created in the database, and the response will be HTML, while /students/1.json will return you the same result, but response will be JSON this time.
Well you could very well replace 'format' with 'foo' or 'banana' or whatever you want. It is just the variable name in this case because the variable that is sent to your block by respond_to is passing along the format as requested by the incoming http request's Accept header.
Sometimes you'll see 422 "Unacceptable" errors in your logs because you are receiving a request with an Accept header that does not request a mime type your app knows about.
As it is, your callers should be using a browser or be a JSON consumer sending the proper headers to receive responses from the boilerplate.
I have seen some people using code like this
respond_to do |format|
format.html
format.js
end
What is the purpose of this if we have template.html and template.js. Either can be rendered without specifying respond_to
Your snippet doesn't do anything special, but the formatting options allow you to provide additional custom behavior if it is necessary.
For example, if you want to render your #products as a JSON:
respond_to do |format|
format.html
format.js { render :json => #products }
end
This is just one of the many things you can do with the format blocks. For more information, see Ruby on Rails Guides: Layouts and Rendering
The format options can take a block so that you can do some custom rendering such as rendering a file or :head response. Have a look at some of the examples here
If you don't specify different behavior for the different formats there is no reason to use respond_to. If you have templates they will automatically be picked up by rails. The respond_to method is useful if you need different behavior per format:
respond_to do |format|
format.html { render :edit }
format.json { render :json => '{}' }
end
In my post_index action, I generate different kinds of "#posts" like..
def index
case params[:listing_type]
when "all"
#posts = get_all_post_from_memcached
when "most_popular"
#posts = get_all_most_popular_from_memcached
respond_to do |format|
format.html
format.js #for ajax reqeusts
format.xml #for rss etc
end
end
end
##updated
def index
case params[:listing_type]
when "all"
#the key here is teh same key I used for memcached
if stale?(:etag => 'all_posts_key')
#posts = get_all_post_from_memcached
else
head :not_modified and return
end
when "most_popular"
if stale?(:etag => 'most_popular_key')
#posts = get_all_most_popular_from_memcached
else
head :notified and return
end
respond_to do |format|
format.html
format.js #for ajax reqeusts
format.xml #for rss etc
end
end
end
From what I understand fresh_when takes a etag, and it is to be used if there is no difference in different kinds of rendered (in my case the rendering is different based on html or ajax)
and
stale? also takes an etag and surrounds the respond_to block.
In this case the etag will be different based on the different listing types. But it seems there isn't much flexibility in the way fresh_when or stale? can be used here?
Thanks
update. I changed the original block a little and now get a double render error what am I doing wrong, should "head :notified and return" just return the header and not touch the respond_to block?
If you have special response processing like the one in show method, then use stale? helper. If you don’t have any special response processing like the one in edit method and using default-rendering mechanism (i.e., you’re not using respond_to or calling render yourself) then use fresh_when.
This question already has answers here:
Closed 12 years ago.
Possible Duplicates:
What is the best way to learn Ruby?
Explain Iterator Syntax on Ruby on Rails
I'm still learning ruby, ruby on rails and such. I'm getting better at understanding all the ruby and rails syntax but this one has me a little stumped.
respond_to do |format|
format.html # index.html.erb
format.xml { render :xml => #contact_lists }
end
respond_to is a method that takes a proceedure, I think. The two formats look like they may be method calls too, but I don't know.
respond_to is a method which takes block. The block takes one argument, which here is called format.
Now you call two methods on format. html which you call without arguments. And xml which you call with a block.
This block takes no arguments and contains a call to the render method with a hash as an argument. The hash contains the key :xml and the value #contact_lists.
Yeap, you're right.
Ruby method calls are a bit puzzling at first, because you can ommit the parethesis, and they may receive code blocks.
So, this is the explaination:
respond_to do |format|
Invoke the method respond_to and pass it a block on what to do with the format it will receive.
format.html # index.html.erb
With that object called format invoke the method html
format.xml { render :xml => #contact_lists }
And the method xml which in turns receive another block ( do / en and { } , are different syntax to pass block. )
end
Finish the first block
See this other , other answers.
I think this post can help you.
Also, take a minute to read the respond_to documentation.
It is worth to know that this method has changed in Rails 3.
Without web-service support, an action
which collects the data for displaying
a list of people might look something
like this:
def index
#people = Person.find(:all)
end
Here’s the same action, with
web-service support baked in:
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.)
Supposing you have an action that adds
a new person, optionally creating
their company (by name) if it does not
already exist, without web-services,
it might look like this:
def create
#company = Company.find_or_create_by_name(params[:company][:name])
#person = #company.people.create(params[:person])
redirect_to(person_list_url)
end
Here’s the same action, with
web-service support baked in:
def create
company = params[:person].delete(:company)
#company = Company.find_or_create_by_name(company[:name])
#person = #company.people.create(params[:person])
respond_to do |format|
format.html { redirect_to(person_list_url) }
format.js
format.xml { render :xml => #person.to_xml(:include => #company) }
end
end