8.5. Rendering

An action's main goal is to enable the formulation of a response for each user request. To be exact, that's one response per request. An action typically sets up one or more instance variables and renders a template that uses these variables to present them in a meaningful way. This is not always the case though; when appropriate, a request can send only headers back and not the actual body of the content. This is fairly common for some Ajax requests.

NOTE

Because only one response is sent back to the client, at most an action should invoke the rendering of one template.

Whether a template is involved or you're sending a string of text or just headers back to the template, the method of choice is the fittingly named render.

8.5.1. render

Invoked without parameters, render will render the default template for the given action. For example, the following snippet renders the hello.html.erb template back to the client:

def hello
    render
end

Notice that this is the default behavior, and the explicit render is not required. As mentioned in the previous section, for such a basic case, not even the action needs to be declared because Rails automatically looks for the template.

If you are working with a legacy Rails application, the template that is rendered could be called hello.rhtml. This is the old-fashioned extension for HTML ERb templates in Rails.

By passing a :text argument, you can render a string back to the browser thus bypassing any template rendering:

def hello
  render(:text => "Hello, Rails!")
end

Even though the parentheses are recommended in general, in many code bases this is found without parentheses.

Passing :action allows you to render the default template for another action:

def hello
  render(:action => "index")
end

The index.html.erb template is rendered, but the code of the index action is not. For this reason, if that template expects to use instance variables that have been set up by index, these have to be set up in hello as well. But if that's the case, chances are that you should use redirect_to (as explained further later on in this chapter).

NOTE

render :action doesn't execute the code within the specified action; only its default template is rendered.

A template name can be rendered through the :template option:

def hello
  render(:template => "example/my_template")
end

Notice that both a controller and a template name must be specified.

If a :locals hash is passed to the method as well, the values assigned to its key will be available as local variables within the template.

Likewise, a template can be specified as an absolute file path through the option :file. Aside from :locals, in this case render also accepts a :user_full_path Boolean option, to switch to a relative path.

It's also possible to provide the source for a template inline as a parameter. Use :inline and assign a string representing the template to it. Within this string you can embed variable evaluations (for example, #{my_var}) as well as accessing values passed within the :locals hash. The template engine defaults to ERb, but you can specify differently through the :type key. This accepts "erb", "builder", and "rjs."

Other common options are render(:nothing => true) to send an empty body back to the client, :partial to render a partial template, and :xml and :json for rendering XML and JSON data and to set the content type appropriately (for example, application/xml). Finally, an :update option is available if you want to pass the current page object to a block of RJS code.

Consult the more than extensive online documentation for the ActionController::Base#render method, to see further examples and details about the many extra options (for example, :layout) available for each of these rendering types.

Running the API Documentation Locally

Rails has become a fairly large and flexible framework. For each method or concept that's introduced, many more options are available to customize its behavior to your liking. Due to the limited size of the book you are holding, it's neither possible nor wise to reproduce the whole API documentation, which already exists online. You should become well acquainted with it, because it's going to become your main tool when working with Rails.

Aside from accessing the documentation online, you can also download a local copy from sites like http://www.railsbrain.com. Another option, perhaps less fancy in terms of presentation, is to run a documentation server locally. This makes the documentation available not just for Rails, but for all of the gems for which RDoc documentation has been installed on your system. To do so run: gem server from anywhere in your system. The documentation for this is available at http://localhost:8808.

Aside from a more Spartan presentation, the downside to this is losing both the Ajax search facility and the insightful comments and notes that you can find on sites like http://apidock.com/rails.


8.5.2. send_data and send_file

So far you saw that it's possible to send "nothing" (well, just the headers), a string, or a template-based response. But what about files or more "generally arbitrary" data streams? Rails provides two methods for this: send_data and send_file.

The following is a fictitious example, inspired by the awesome movie Office Space, of how to send a PDF report in response through send_data:

def report
  tps_report = QualityAssurance.generate_report(Date.today, :format => :pdf)
  send_data(tps_report, :type => "application/pdf", :filename => "tps_report.pdf"
end

Notice how it is passing three arguments to the method: the data that constitutes the document, image, or binary file; an HTTP content type (which defaults to application/octet-stream); and the file name suggested to the browser. Two other two available options are :disposition and :status. :disposition defaults to "attachment" (save and download) but can also be set to "inline" (often used for images). :status is used to indicate an HTTP status code (defaults to "200 OK" of course).

NOTE

For working with reports, I highly recommend the Ruby gem Ruport. You can read more about it on its official website at http://rubyreports.org.

Similarly, it's possible to send a file that's available on the server, to the client. The following example shows how to use send_file to do just that:

def download
  send_file '/path/to/ebook.zip'
end

This method also accepts several options, including :filename, :disposition, :type, and :status, as seen with send_data. Other available options are :stream, :buffer_size, :url_based_filename, and x_send_file:

  • :stream determines if the file should be sent to the user agent as soon as it's read (if set to true, which is the default behavior if the option is omitted) or if it should only be sent after the entire file has been read (if set to false).

  • :buffer_size specifies the buffer size in bytes for streaming (defaults to 4096 bytes).

  • :url_based_filename can be set to true if you want the browser to determine the file name based on the URL. This is necessary for i18n file names on certain browsers. Specifying a file name overrides this option.

  • :x_send_file is an option that, when set to true, enables support for the X-Sendfile HTTP header in the Apache Web server. The file will be served directly from the disk and not by streaming through a Rails process. The end result is a faster, less memory-intensive operation. This option was incorporated into Rails 2.1 (previously it was available as a plugin), but not all Web servers support it.

NGINX and x_send_file => :true

Nginx — an excellent Russian Web server that's popular within the Rails community — can take advantage of the x_send_file feature as long as you set the header to X-Accel-Redirect (which is similar to the X-Sendfile of Apache). To do so, add the following line to your environment.rb or to an initializer:


8.5.3. redirect_to

When the code generated through scaffolding was analyzed, it contained several redirect_to methods. This method allows an action to hand control over to another action. This is very useful and it's a technique that's used in pretty much all Rails applications. Think about the destroy action in your RESTful articles controller:

def destroy
  @article = Article.find(params[:id])
  @article.destroy

  respond_to do |format|
    format.html { redirect_to(:back) }
    format.xml  { head :ok }
  end
end

This action destroys an article and then, if the requested format is in HTML, it redirects to the articles_url (or the unpublished page, whichever triggered the deletion), which will be handled by the same controller's index action (or again, unpublished). This shows a list of articles to the user, who will see that the article was just deleted.

NOTE

As mentioned before for render, only one redirect_to should be ever executed within an action.

Aside from providing :back or a path, it's also possible to redirect directly to an action:

redirect_to(:action => :thanks)

The options provided (for example, :action, :controller, :id, and so on) will be passed to url_ to generate the actual URL that Rails needs to redirect to. This means that all the nice options discussed in the routing section are available to redirect_to as well.

By default these redirects are performed as "302 Found," but it's possible to specify otherwise through the :status option by setting it to, for example, :301 (or the equivalent :moved_permanently):

redirect_to(articles_url, :status => :301)

Less elegantly, it's also possible to set the Status key (in the headers hash) within the action before performing a redirect:

headers["Status"] = "301 Moved Permanently"
redirect_to(articles_url)

Finally, remember that redirect_to(:back) is just a syntax sugar for redirecting back to the page that issued the request, without the need to write your own logic (that is, redirect_to(request.env["HTTP_REFERER"])).

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset
3.149.236.27