Skip to content

Instantly share code, notes, and snippets.

@mattbarackman
Last active December 18, 2015 22:09
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 1 You must be signed in to fork a gist
  • Save mattbarackman/5852729 to your computer and use it in GitHub Desktop.
Save mattbarackman/5852729 to your computer and use it in GitHub Desktop.

Default Rendering

By default, controllers in Rails automatically render views with names that correspond to valid routes.

For example, if you have this code in your BooksController class:

class BooksController < ApplicationController
end

And the following in your routes file:

resources :books

And you have a view file app/views/books/index.html.erb:

<h1>Books are coming soon!</h1>

Rails will automatically render app/views/books/index.html.erb when you navigate to /books and you will see “Books are coming soon!” on your screen.

However a coming soon screen is only minimally useful, so you will soon create your Book model and add the index action to BooksController:

class BooksController < ApplicationController
  def index
    @books = Book.all
  end
end

If you do not explicitly render something at the end of a controller action, Rails will automatically look for the action_name.html.erb template in the controller’s view path and render it. So in this case, Rails will render the app/views/books/index.html.erb file.

Rendering other actions

If you want to render the view that corresponds to a different action within the same controller, you can use render with the name of the view:

def update
  @book = Book.find(params[:id])
  if @book.update_attributes(params[:book])
    redirect_to(@book) 
  else
    render "edit"
    # render :edit (an alternative using the action's symbol) 
    # render :action => "edit" (deprecated)
  end
end

Using render with :action is a frequent source of confusion for Rails newcomers. The specified action is used to determine which view to render, but Rails does not run any of the code for that action in the controller. Any instance variables that you require in the view must be set up in the current action before calling render.

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

You can also render an action's view from within a different controller by specifying the controller first.

render 'products/show'
# render :template => 'products/show' (deprecated)

Potential Pitfalls

Double Render

Sooner or later, most Rails developers will see the error message “Can only render or redirect once per action”. It occurs when you tell Rails to render two views from the same method.

The following code will throw this error if @book.special? is true because it will begin to render the "special_show" action in the true condition, but also try and render the "regular_show" action.

def show
  @book = Book.find(params[:id])
  if @book.special?
    render :action => "special_show"
  end
  render :action => "regular_show"
end

To fix this, you can include an and return after the first render to force the method to return immediately and not evalute the rest of the method code.

def show
  @book = Book.find(params[:id])
  if @book.special?
    render :action => "special_show" and return # note, using && instead of and will not work.
  end
  render :action => "regular_show"
end

#Rendering with layout

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. If there is no such controller-specific layout, Rails will use app/views/layouts/application.html.erb.

Depending on the context of where you are calling render or what you are calling it on it may or may not render with its associated layout (similar to Sinatra).

You can invoke the layout or turn it off manually by putting a boolean value for layout in the options hash.

render 'products/show' :layout => true #with the layout
render 'products/show' :layout => false #without the layout

You can also render with an alternative layout by specifying the filename instead of a boolean.

render 'products/show' :layout => 'special_layout'

Alternatively, you can override the default layout conventions in your whole controller by using the layout declaration.

class ProductsController < ApplicationController
  layout "special_layout"
  
  def show
  
  end
  #...
end

Rendering Alternative Formats (Not HTML)

Rendering pure 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"

Rendering pure text is most useful when you’re responding to AJAX or web service requests that are expecting something other than proper HTML.

Rendering a status

Similarly, you can render a status code by passing its numerical value or associated symbol. You can see a list of all the symbols [here] (http://apidock.com/rails/ActionController/StatusCodes).

render :status => 500
render :status => :forbidden

Rendering JavaScript

render :js => "alert('Hello Rails');"

This will send the supplied string to the browser with a type of text/javascript. This may be valuable for AJAX request responses.

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

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.

###Rendering XML

render :xml => @product

###Testing Tip

If you want to see the exact results of a call to render without needing to inspect it in a browser, you can call render_to_string. This method takes exactly the same options as render, but it returns a string instead of sending a response back to the browser.

#Redirects the Rails Way

Redirects in Rails use redirect_to and then the Rails url helper.

redirect_to photos_url

There’s also a special redirect that sends the user back to the page they just came from:

redirect_to :back

By default, Rails offers the status code 302 (temporary redirect) for redirect_to. If you would like to pass in an alternative status code, like 301 (permanent redirect) you can do so with the :status option.

redirect_to photos_path, :status => 301

Asset Tag Helpers

Asset tag helpers provide methods for generating HTML that link views to feeds, JavaScript, stylesheets, images, videos and audios. There are six asset tag helpers available in Rails:

  • auto_discovery_link_tag
  • javascript_include_tag
  • stylesheet_link_tag
  • image_tag
  • video_tag
  • audio_tag

Javascript

<%= javascript_include_tag "main" %>
<script src='/assets/main.js' type="text/javascript"></script>

You can also include multiple assets of the same type by including multiple files in your asset tag (with their relative file paths where necessary).

<%= javascript_include_tag "main", "/photos/columns" %>
#Pitfalls

You can also include web-based assets by including the full url.

<%= javascript_include_tag "http://example.com/main.js" %>

CSS

CSS tags work exactly the same way, they just ouput different html.

<%= stylesheet_link_tag "main", "/photos/columns" %>

<%= stylesheet_link_tag "http://example.com/main.css" %>

By default css asset tags with the media type as "screen", you can override this by passing in an optional :media parameter.

<%= stylesheet_link_tag "main_print", :media => "print" %>

Images

Image tags works much the same way, but you can only provide one image and you can add in additional parameters.

<%= image_tag "icons/delete.gif", {:height => 45} %> # Height
<%= image_tag "home.gif", :size => "50x20" %>        # Size

<%= image_tag "home.gif", :alt => "Go Home",         # alt text
                          :id => "HomeImage",        # id
                          :class => 'nav_bar' %>     #class

Audio and Video

<%= video_tag "movie.ogg" %>

The video tag also supports all of the

:poster => 'image_name.png', provides an image to put in place of the video before it starts playing. :autoplay => true, starts playing the video on page load. :loop => true, loops the video once it gets to the end. :controls => true, provides browser supplied controls for the user to interact with the video. :autobuffer => true, the video will pre load the file for the user on page load.

<%= audio_tag "music.mp3" %>

Like the video_tag, the audio_tag has special options:

:autoplay => true, starts playing the audio on page load :controls => true, provides browser supplied controls for the user to interact with the audio. :autobuffer => true, the audio will pre load the file for the user on page load.

Partials

Much like Sinatra you can render partials within your views or controllers. These partials by convention are named with a leading underscore (i.e. _menu.html.erb).

Unlike Sinatra, instead of using erb to render a partial, you use render.

In a view you might see

<%= render "menu" %>
 
<h1>Products</h1>
 
<p>Here are a few of our fine products:</p>
... 

You can also reference a partial from outside of your controller (like a shared partial) by calling its relative path.

<%= render "shared/footer" %>

Every partial also has a local variable with the same name as the partial (minus the underscore). You can pass an object in to this local variable via the :object option:

<%= render :partial => "customer", :object => @new_customer %>

Within the customer partial, the customer variable will refer to @new_customer from the parent view.

Partials are very useful in rendering collections. When you pass a collection to a partial via the :collection option, the partial will be inserted once for each member in the collection:

<h1>Products</h1>
<%= render :partial => "product", :collection => @products %>
<p>Product Name: <%= product.name %></p>

content_for

Content_for allows you to specify different content types based on partial which partial is being passed. Include a content_fortag in the partial file and call the content through an additional yeild tag on the layout.

*Great for sidebars and headers if the styling changes based on content.

Example

FILE => applicaiton.html.erb

<html>
  <head>
        <title></title>
        <%= stylesheet_link_tag 'applicaiton %>
        <%= yield :head %>
    </head>
...

FILE => index.html.erb

    <% content_for :head do %>
        <%= stylesheet_link_tag 'projects' %> #this will load the projects.css stylesheet in the assest folder
        <% end %>
    <h2>There rest of the partial</h2>
...

Additional Resources

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment