Rendering Responses
Controllers are responsible for creating responses to send back to the browser. Rails provides many ways to render content.
Render vs Redirect
ruby
1class ArticlesController < ApplicationController
2 def create
3 @article = Article.new(article_params)
4
5 if @article.save
6 # Redirect: sends 302, browser makes new request
7 redirect_to @article
8 else
9 # Render: returns response immediately, no new request
10 render :new, status: :unprocessable_entity
11 end
12 end
13endRendering Templates
Default Rendering
ruby
1class ArticlesController < ApplicationController
2 def show
3 @article = Article.find(params[:id])
4 # Automatically renders app/views/articles/show.html.erb
5 end
6endExplicit Rendering
ruby
1class ArticlesController < ApplicationController
2 def show
3 @article = Article.find(params[:id])
4
5 # Render same template explicitly
6 render :show
7
8 # Render different template
9 render :special_show
10
11 # Render template from another controller
12 render "products/show"
13
14 # Render with absolute path
15 render file: "/path/to/template"
16 end
17endRender Options
ruby
1# With status code
2render :new, status: :unprocessable_entity # 422
3render :error, status: :not_found # 404
4render :error, status: 500 # Numeric codes work too
5
6# Without layout
7render :show, layout: false
8
9# With different layout
10render :show, layout: "admin"
11
12# With locals
13render :show, locals: { article: @article, author: @author }
14
15# Set content type
16render :show, content_type: "text/plain"Rendering Non-HTML
JSON
ruby
1def show
2 @article = Article.find(params[:id])
3
4 # Simple JSON
5 render json: @article
6
7 # With options
8 render json: @article, only: [:id, :title, :body]
9
10 # With includes
11 render json: @article, include: :comments
12
13 # Custom structure
14 render json: {
15 article: @article,
16 meta: { generated_at: Time.current }
17 }
18
19 # With status
20 render json: { error: "Not found" }, status: :not_found
21endOther Formats
ruby
1def show
2 @article = Article.find(params[:id])
3
4 # Plain text
5 render plain: "Hello World"
6
7 # HTML string
8 render html: "<h1>Hello</h1>".html_safe
9
10 # XML
11 render xml: @article
12
13 # Raw body
14 render body: "Raw content"
15
16 # Nothing (empty response)
17 head :ok
18 head :no_content
19 head :created
20endRespond To Multiple Formats
ruby
1class ArticlesController < ApplicationController
2 def index
3 @articles = Article.all
4
5 respond_to do |format|
6 format.html # renders index.html.erb
7 format.json { render json: @articles }
8 format.xml { render xml: @articles }
9 format.csv { render plain: @articles.to_csv }
10 format.pdf { send_pdf }
11 end
12 end
13
14 private
15
16 def send_pdf
17 pdf = ArticlesPdf.new(@articles)
18 send_data pdf.render,
19 filename: "articles.pdf",
20 type: "application/pdf",
21 disposition: "inline"
22 end
23endRendering Partials
ruby
1def show
2 @article = Article.find(params[:id])
3
4 # Render partial as response
5 render partial: "article"
6
7 # With locals
8 render partial: "article", locals: { article: @article }
9
10 # Collection
11 render partial: "article", collection: @articles
12
13 # With layout for partial
14 render partial: "article", layout: "card"
15endStreaming Responses
ruby
1class ReportsController < ApplicationController
2 include ActionController::Live
3
4 def download
5 response.headers["Content-Type"] = "text/csv"
6 response.headers["Content-Disposition"] = 'attachment; filename="report.csv"'
7
8 response.stream.write "id,name,email\n"
9
10 User.find_each do |user|
11 response.stream.write "#{user.id},#{user.name},#{user.email}\n"
12 end
13 ensure
14 response.stream.close
15 end
16endSending Files
ruby
1class DownloadsController < ApplicationController
2 def show
3 # Send existing file
4 send_file Rails.root.join("files", "document.pdf"),
5 filename: "document.pdf",
6 type: "application/pdf",
7 disposition: "attachment" # or "inline"
8
9 # Send generated data
10 send_data generate_csv,
11 filename: "report.csv",
12 type: "text/csv"
13 end
14
15 private
16
17 def generate_csv
18 CSV.generate do |csv|
19 csv << ["ID", "Name", "Email"]
20 User.find_each do |user|
21 csv << [user.id, user.name, user.email]
22 end
23 end
24 end
25endRedirects
ruby
1class ArticlesController < ApplicationController
2 def create
3 @article = Article.new(article_params)
4
5 if @article.save
6 # Redirect to show action
7 redirect_to @article
8
9 # Or explicit path
10 redirect_to article_path(@article)
11
12 # With flash message
13 redirect_to @article, notice: "Created!"
14 redirect_to @article, alert: "Warning!"
15
16 # With custom flash
17 redirect_to @article, flash: { success: "Great job!" }
18
19 # External URL
20 redirect_to "https://example.com"
21
22 # Back to previous page
23 redirect_back(fallback_location: root_path)
24
25 # With status
26 redirect_to @article, status: :see_other # 303
27 end
28 end
29endFlash Messages
ruby
1class ArticlesController < ApplicationController
2 def create
3 @article = Article.new(article_params)
4
5 if @article.save
6 flash[:notice] = "Article created!"
7 redirect_to @article
8 else
9 flash.now[:alert] = "Please fix errors"
10 render :new
11 end
12 end
13
14 def update
15 # Keep flash for one more request
16 flash.keep(:notice)
17 redirect_to articles_path
18 end
19enderb
1<!-- In layout -->
2<% flash.each do |type, message| %>
3 <div class="alert alert-<%= type %>">
4 <%= message %>
5 </div>
6<% end %>Head (Status Only)
ruby
1class ArticlesController < ApplicationController
2 def check
3 if Article.exists?(params[:id])
4 head :ok
5 else
6 head :not_found
7 end
8 end
9
10 def destroy
11 Article.find(params[:id]).destroy
12 head :no_content # 204
13 end
14endProper rendering makes your controllers communicate effectively with clients!
