Title | Summary | |||
---|---|---|---|---|
<%= book.title %> | <%= book.content %> | <%= link_to 'Show', book %> | <%= link_to 'Edit', edit_book_path(book) %> | <%= link_to 'Remove', book, :confirm => 'Are you sure?', :method => :delete %> |
<%= p.name %>
<% end %>" WARNING: There is seldom any good reason to use this option. Mixing ERB into your controllers defeats the MVC orientation of Rails and will make it harder for other developers to follow the logic of your project. Use a separate erb view instead. By default, inline rendering uses ERB. You can force it to use Builder instead with the +:type+ option: render :inline => "xml.p {'Horrid coding practice!'}", :type => :builder h5. Rendering Text You can send plain text - with no markup at all - back to the browser by using the +:text+ option to +render+: render :text => "OK" TIP: Rendering pure text is most useful when you're responding to AJAX or web service requests that are expecting something other than proper HTML. NOTE: By default, if you use the +:text+ option, the text is rendered without using the current layout. If you want Rails to put the text into the current layout, you need to add the +:layout => true+ option. h5. 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 TIP: 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. h5. Rendering XML Rails also has built-in support for converting objects to XML and rendering that XML back to the caller: render :xml => @product TIP: You don't need to call +to_xml+ on the object that you want to render. If you use the +:xml+ option, +render+ will automatically call +to_xml+ for you. h5. Rendering Vanilla JavaScript Rails can render vanilla JavaScript: render :js => "alert('Hello Rails');" This will send the supplied string to the browser with a MIME type of +text/javascript+. h5. Options for +render+ Calls to the +render+ method generally accept four options: * +:content_type+ * +:layout+ * +:status+ * +:location+ h6. The +:content_type+ Option By default, Rails will serve the results of a rendering operation with the MIME content-type of +text/html+ (or +application/json+ if you use the +:json+ option, or +application/xml+ for the +:xml+ option.). There are times when you might like to change this, and you can do so by setting the +:content_type+ option: render :file => filename, :content_type => 'application/rss' h6. The +:layout+ Option With most of the options to +render+, the rendered content is displayed as part of the current layout. You'll learn more about layouts and how to use them later in this guide. You can use the +:layout+ option to tell Rails to use a specific file as the layout for the current action: render :layout => 'special_layout' You can also tell Rails to render with no layout at all: render :layout => false h6. The +:status+ Option Rails will automatically generate a response with the correct HTTP status code (in most cases, this is +200 OK+). You can use the +:status+ option to change this: render :status => 500 render :status => :forbidden Rails understands both numeric and symbolic status codes. h6. The +:location+ Option You can use the +:location+ option to set the HTTP +Location+ header: render :xml => photo, :location => photo_url(photo) h5. Finding Layouts To find the current layout, Rails first looks for a file in +app/views/layouts+ with the same base name as the controller. For example, rendering actions from the +PhotosController+ class will use +app/views/layouts/photos.html.erb+ (or +app/views/layouts/photos.builder+). If there is no such controller-specific layout, Rails will use +app/views/layouts/application.html.erb+ or +app/views/layouts/application.builder+. If there is no +.erb+ layout, Rails will use a +.builder+ layout if one exists. Rails also provides several ways to more precisely assign specific layouts to individual controllers and actions. h6. Specifying Layouts for Controllers You can override the default layout conventions in your controllers by using the +layout+ declaration. For example: class ProductsController < ApplicationController layout "inventory" #... end With this declaration, all of the methods within +ProductsController+ will use +app/views/layouts/inventory.html.erb+ for their layout. To assign a specific layout for the entire application, use a +layout+ declaration in your +ApplicationController+ class: class ApplicationController < ActionController::Base layout "main" #... end With this declaration, all of the views in the entire application will use +app/views/layouts/main.html.erb+ for their layout. h6. Choosing Layouts at Runtime You can use a symbol to defer the choice of layout until a request is processed: class ProductsController < ApplicationController layout :products_layout def show @product = Product.find(params[:id]) end private def products_layout @current_user.special? ? "special" : "products" end end Now, if the current user is a special user, they'll get a special layout when viewing a product. You can even use an inline method, such as a Proc, to determine the layout. For example, if you pass a Proc object, the block you give the Proc will be given the +controller+ instance, so the layout can be determined based on the current request: class ProductsController < ApplicationController layout Proc.new { |controller| controller.request.xhr? ? 'popup' : 'application' } end h6. Conditional Layouts Layouts specified at the controller level support the +:only+ and +:except+ options. These options take either a method name, or an array of method names, corresponding to method names within the controller: class ProductsController < ApplicationController layout "product", :except => [:index, :rss] end With this declaration, the +product+ layout would be used for everything but the +rss+ and +index+ methods. h6. Layout Inheritance Layout declarations cascade downward in the hierarchy, and more specific layout declarations always override more general ones. For example: * +application_controller.rb+ class ApplicationController < ActionController::Base layout "main" end * +posts_controller.rb+ class PostsController < ApplicationController end * +special_posts_controller.rb+ class SpecialPostsController < PostsController layout "special" end * +old_posts_controller.rb+ class OldPostsController < SpecialPostsController layout nil def show @post = Post.find(params[:id]) end def index @old_posts = Post.older render :layout => "old" end # ... end In this application: * In general, views will be rendered in the +main+ layout * +PostsController#index+ will use the +main+ layout * +SpecialPostsController#index+ will use the +special+ layout * +OldPostsController#show+ will use no layout at all * +OldPostsController#index+ will use the +old+ layout h5. Avoiding Double Render Errors Sooner or later, most Rails developers will see the error message "Can only render or redirect once per action". While this is annoying, it's relatively easy to fix. Usually it happens because of a fundamental misunderstanding of the way that +render+ works. For example, here's some code that will trigger this error: def show @book = Book.find(params[:id]) if @book.special? render :action => "special_show" end render :action => "regular_show" end If +@book.special?+ evaluates to +true+, Rails will start the rendering process to dump the +@book+ variable into the +special_show+ view. But this will _not_ stop the rest of the code in the +show+ action from running, and when Rails hits the end of the action, it will start to render the +regular_show+ view - and throw an error. The solution is simple: make sure that you have only one call to +render+ or +redirect+ in a single code path. One thing that can help is +and return+. Here's a patched version of the method: def show @book = Book.find(params[:id]) if @book.special? render :action => "special_show" and return end render :action => "regular_show" end Make sure to use +and return+ instead of +&& return+ because +&& return+ will not work due to the operator precedence in the Ruby Language. Note that the implicit render done by ActionController detects if +render+ has been called, so the following will work without errors: def show @book = Book.find(params[:id]) if @book.special? render :action => "special_show" end end This will render a book with +special?+ set with the +special_show+ template, while other books will render with the default +show+ template. h4. Using +redirect_to+ Another way to handle returning responses to an HTTP request is with +redirect_to+. As you've seen, +render+ tells Rails which view (or other asset) to use in constructing a response. The +redirect_to+ method does something completely different: it tells the browser to send a new request for a different URL. For example, you could redirect from wherever you are in your code to the index of photos in your application with this call: redirect_to photos_url You can use +redirect_to+ with any arguments that you could use with +link_to+ or +url_for+. There's also a special redirect that sends the user back to the page they just came from: redirect_to :back h5. Getting a Different Redirect Status Code Rails uses HTTP status code 302, a temporary redirect, when you call +redirect_to+. If you'd like to use a different status code, perhaps 301, a permanent redirect, you can use the +:status+ option: redirect_to photos_path, :status => 301 Just like the +:status+ option for +render+, +:status+ for +redirect_to+ accepts both numeric and symbolic header designations. h5. The Difference Between +render+ and +redirect_to+ Sometimes inexperienced developers think of +redirect_to+ as a sort of +goto+ command, moving execution from one place to another in your Rails code. This is _not_ correct. Your code stops running and waits for a new request for the browser. It just happens that you've told the browser what request it should make next, by sending back an HTTP 302 status code. Consider these actions to see the difference: def index @books = Book.all end def show @book = Book.find_by_id(params[:id]) if @book.nil? render :action => "index" end end With the code in this form, there will likely be a problem if the +@book+ variable is +nil+. Remember, a +render :action+ doesn't run any code in the target action, so nothing will set up the +@books+ variable that the +index+ view will probably require. One way to fix this is to redirect instead of rendering: def index @books = Book.all end def show @book = Book.find_by_id(params[:id]) if @book.nil? redirect_to :action => :index end end With this code, the browser will make a fresh request for the index page, the code in the +index+ method will run, and all will be well. The only downside to this code is that it requires a round trip to the browser: the browser requested the show action with +/books/1+ and the controller finds that there are no books, so the controller sends out a 302 redirect response to the browser telling it to go to +/books/+, the browser complies and sends a new request back to the controller asking now for the +index+ action, the controller then gets all the books in the database and renders the index template, sending it back down to the browser which then shows it on your screen. While in a small application, this added latency might not be a problem, it is something to think about if response time is a concern. We can demonstrate one way to handle this with a contrived example: def index @books = Book.all end def show @book = Book.find_by_id(params[:id]) if @book.nil? @books = Book.all render "index", :alert => 'Your book was not found!' end end This would detect that there are no books with the specified ID, populate the +@books+ instance variable with all the books in the model, and then directly render the +index.html.erb+ template, returning it to the browser with a flash alert message to tell the user what happened. h4. Using +head+ To Build Header-Only Responses The +head+ method can be used to send responses with only headers to the browser. It provides a more obvious alternative to calling +render :nothing+. The +head+ method takes one parameter, which is interpreted as a hash of header names and values. For example, you can return only an error header: head :bad_request This would produce the following header:Hello, Rails!
Hello, Rails!
Here are a few of our fine products:
... <%= render "shared/footer" %>
Zone name
<%= f.text_field :name %>
<%= f.submit %>
<% end %>Product Name: <%= product.name %>
Customer: <%= customer.name %>
Employee: <%= employee.name %>