Understanding Ruby Syntax [duplicate] - ruby-on-rails

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

Related

Rails, respond_to blocks and |format|

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.

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.

How to redirect after a successful AJAX form submission (Rails 3.2)

Suppose you have an edit form with :remote => true. Your controller method looks like
def update
#article = Article.find(params[:id])
respond_to do |format|
if #article.update_attributes(params[:article])
format.html { redirect_to #article}
format.js { render :js => "window.location.replace('#{article_path(#article)}');"}
else
format.html { render :action => "edit" }
# Render update.js.erb which replaces the body of the form
format.js {}
end
end
end
What's the best way to do the redirect on successful article update in Rails 3.2.1? The raw JS solution seems a little sleazy, but I do like the fact that it's obvious that it's performing the same function as the format.html version.
I would prefer a solution that does not use RJS (if that even works in Rails 3.2?)
How about adding the below line of code on the view file itself
#some_file.js.erb
`window.location = redirect_path
As you mentioned in the question you do not prefer RJS, but I think it's better to follow this pattern better than writing the js code in the controller.
Does your ajax interact with a model (.php,.asp?). My preferred method in this instance is to create a success/fail criteria within the model after submission and redirect directly from there. Not sure if that makes sense in this application though?

Why does the Rails "new" action have an XML format directive?

I'm a newcomer to Rails, and have been following the tutorial on the rails webpage.
Using the scaffold instruction to create a "post" model, I found that the new action in the controller has a special directive for XML format:
def new
#post = Post.new
respond_to do |format|
format.html # new.html.erb
format.xml { render :xml => #post }
end
end
I can't see the reasoning for supporting an XML request when creating a new post. Browsing to /posts/new.xml returns nothing. What is the purpose of this?
The reasoning for it behind the new action is simply to provide a xml client with the default data (or something else if you want).
The format directive is being used by all routes, and you don't need to support a format unless you want to.
The above code could might as well have looked like:
respond_to do |format|
format.html # renders new.html.erb
format.xml { render :xml => {:message => "XML is not supported"} }
format.json { render :text => #post.to_json }
format.js # renders new.js.erb
end
Also, this is not limited to the new action, but is available in all your actions. The format to use is either taken from the url (if the route is set up to use it), or from the HTTP-Accept header that the browser sends.

Always use responds_to?

Until now I have always specified the format of the response for actions using a responds_to block, like so:
responds_to do |format|
format.js { render :json => #record }
end
Recently I realized that if you only support one format (as in the above example), you don't really need that block. Is it best practice to leave it in, or remove it?
I'm going to differ with existing answers--I like to have a responds_to block for all of my actions. I find that, while slightly more verbose, it more clearly self-documents the action. It also makes it easy to support additional formats in the future. Edit: another advantage is it acts as a gatekeeper. Any format not declared in the block is automatically served a "406 Not Acceptable"
I'm not really sure if this is best practice or not, but usually what I like to do is to leave the routes open to respond_to (i.e. by appending .:format to the end), but only use it in the controllers when it's necessary.
Example:
routes.rb
map.connect :controller/:action/:id.:format
model_controller.rb
# Return a collection of model objects
def action_with_multiple_responses
#models = Model.all
respond_to do |format|
format.html #=> action_with_multiple_responses.html
format.xml { render :xml => #models }
end
end
# Return the first model object
def action_with_one_response
#model = Model.first
end
That way, you aren't cluttering up your action_with_one_response method with an unnecessary block, but you also have set yourself up quite nicely if you want to someday return your object in xml, json, etc.
I would say not to use respond_to unless you have multiple response types.
It is simply extra code to understand and for your app to process and handle:
render :json => #record
Is much more concise than:
responds_to do |format|
format.js { render :json => #record }
end

Resources