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. Using +render+ with +:update+ You can also render JavaScript-based page updates inline using the +:update+ option to +render+: render :update do |page| page.replace_html 'warning', "Invalid options supplied" end WARNING: Placing JavaScript updates in your controller may seem to streamline small updates, but it defeats the MVC orientation of Rails and will make it harder for other developers to follow the logic of your project. We recommend using a separate RJS template instead, no matter how small the update. 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 (as an alternative to using +update+ with an +.rjs+ file): 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 HTML 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 either numeric status codes or symbols for status codes. You can find its list of status codes in +actionpack/lib/action_controller/status_codes.rb+. You can also see there how Rails maps symbols to 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 on a per-Controller Basis You can override the automatic layout conventions in your controllers by using the +layout+ declaration in the controller. For example: class ProductsController < ApplicationController layout "inventory" #... end With this declaration, all 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 declaration in your +ApplicationController+ class: class ApplicationController < ActionController::Base layout "main" #... end With this declaration, all 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 to determine the layout: You can also decide the layout by passing a Proc object, the block you give the Proc will be given the +controller+ instance, so you can make decisions based on the current request. For example: class ProductsController < ApplicationController layout Proc.new { |controller| controller.request.xhr? ? 'popup' : 'application' } end h6. Conditional Layouts Layouts specified at the controller level support +:only+ and +:except+ options that take either a method name or an array of method names which correspond 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 Layouts are shared downwards in the hierarchy, and more specific layouts 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 you use +and return+ and not +&& return+ because while the former will work, the latter will not due to operator precedence in the Ruby Language. Note that the implicit render done by ActionController detects if +render+ has been called, and thus avoids this error. Therefore, 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_path You can use +redirect_to+ with any arguments that you could use with +link_to+ or +url_for+. In addition, there's 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 (temporary redirect) when you call +redirect_to+. If you'd like to use a different status code (perhaps 301, permanent redirect), you can do so by using 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 conceive 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 is presumably depending on. 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 app, this added latency might not be a problem, it is something to think about when speed of response is of the essence. One way to handle this double request (though a contrived example) could be: 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 Which would detect that there are no books, populate the +@books+ instance variable with all the books in the database and then directly render the +index.html.erb+ template returning it to the browser with a flash alert message telling the user what happened. h4. Using +head+ To Build Header-Only Responses The +head+ method exists to let you send back responses to the browser that have only headers. It provides a more obvious alternative to calling +render :nothing+. The +head+ method takes one response, which is interpreted as a hash of header names and values. For example, you can return only an error header: head :bad_request Which 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 %>