29
.
04
.
2024
11
.
08
.
2022
Ruby on Rails
Frontend
Backend
Tutorial

Should I use instance variables in Rails views?

Mateusz Woźniczka
Ruby Developer

Should I use instance variables in Rails views?

In Ruby, an instance variable has a name starting with the @ character and is available only within the object's scope. In other words, it is accessible by all object's methods, without the need to pass it explicitly, but it is not shared between the objects.

Instance variables in Rails

Rails extends this idea and makes it look like instance variables are 'available' between some objects.

In the code below there are three instance variables initialized in the controller: @post, @comments, and @author, that are called in the show view template, and two partials rendered in that template.

# controllers/post_controller.rb
class PostsController < ApplicationController

  def show
    @post = Post.find(params[:id])
    @author = 'John Doe'
    @comments = @post.comments
  end
end
<!-- views/posts/show.html.erb -->
<%= render "shared/author" %>

<p>
  <strong>Title:</strong>
  <%= @post.title %>
</p>

<p>
  <strong>Body:</strong>
  <%= @post.body %>
</p>

<%= render "shared/comments" %>
<!-- views/shared/author.html.erb -->
<h4>Written by: <%= @author %></h4>
<!-- views/shared/comments.html.erb -->
<h3>Comments</h3>

<% @comments.each do |comment| %>
 <p>
   <%= comment.body %>
 </p>
<% end %>

Controller’s instance variables are copied to the view initializer, so they are available as the Action View's object's own instance variables. When the view is rendered, the instance variables used in the template are resolved and we are getting an HTML with all the data we wanted to present in it.

The view is not accessing controllers instance variables - it is using its own. That means, that no Ruby rules are bent or broken, and there are simply two objects sharing an access to the same objects via their instance variables.

Making instance variables accessible inside the whole view templates (including partials) is making the development process easier (because you do not have to worry about passing them around), but also can lead to some troubles and confusion in further development.

Potential issues

Global access

As you can see in the code above, an instance variable can be accessed anywhere in the view - @author is called inside the _author.html.erb partial, even though it is not used in the show.html.erb.

It is also possible to create such a variable in the partial (or any other part of the view) or even modify the existing one, which is obviously not a good practice. The following code is valid from a semantic point of view but can cause quite a lot of confusion. It is changing the value of @post_title, so in the show page instead of John Doe we will get Foo.

<!-- views/shared/author.html.erb -->
<h4>Written by: <%= @author %></h4>
<% @post.title = 'Foo' %>

Instance variables in views behave just like the global variables - they can be accessed and modified in multiple places, so it is easy to lose a track of what is going on in the view when the project grows.

Unusable partials

Let's assume, that we want to add a new feature that will be displaying the comment's author. We already have the partial with author’s details, so we can use it.

<!-- views/shared/comments.html.erb -->
<h3>Comments</h3>

<% @comments.each do |comment| %>
 <p>
   <%= comment.body %>
 </p>
 <%= render "shared/author" %>
<% end %>

But using it in the current form will not get us the result we want, because it will render details of the article's author.

Fixing partials

To use partials in multiple places, we should use local variables, that are passed to the partial explicitly. Our example will look like this:

<!-- views/posts/show.html.erb -->
<%= render "shared/author", locals: {author: @author} %>

<p>
  <strong>Title:</strong>
  <%= @post.title %>
</p>

<p>
  <strong>Body:</strong>
  <%= @post.body %>
</p>

<%= render "shared/comments", locals: {comments: @comments} %>
<!-- views/shared/author.html.erb -->
<h4>Written by: <%= author %></h4>
<!-- views/shared/comments.html.erb -->
<h3>Comments</h3>

<% comments.each do |comment| %>
 <p>
   <%= comment.body %>
   <%= render "shared/author", locals: {author: comment.author} %>
 </p>
<% end %>

Now the same partial is rendered in multiple places: on the top of the page - where it displays the name of the article's author, and after each comment displaying that comment's author.

This approach resolves also the issue with the global scope of instance variables because now they are still available everywhere, but they are used only on the show view page.

Taking it further

Some developers apply this approach not only to partials, but also to the whole views. In such a case controllers use explicit render method with local variables, so our example would look like this:

# controllers/post_controller.rb
class PostsController < ApplicationController

  def show
    @post = Post.find(params[:id])
    @author = 'John Doe'
    @comments = @post.comments

    render :show, locals: { post: @post, author: @author, comments: @comments }
  end
end
<!-- views/posts/show.html.erb -->
<%= render "shared/author", locals: {author: author} %>

<p>
  <strong>Title:</strong>
  <%= post.title %>
</p>

<p>
  <strong>Body:</strong>
  <%= post.body %>
</p>

<%= render "shared/comments", locals: {comments: comments} %>
<!-- views/shared/author.html.erb -->
<h4>Written by: <%= author %></h4>
<!-- views/shared/comments.html.erb -->
<h3>Comments</h3>

<% comments.each do |comment| %>
 <p>
   <%= comment.body %>
   <%= render "shared/author", locals: {author: comment.author} %>
 </p>
<% end %>

As we already know all instance variables from the controller are passed to the view. Sometimes it may be hard to track all of them because they can be initialized not only in the controller action itself but also in multiple other places like filters, controller methods or even helpers. Using local variables in such cases may help to understand what is passed to the view, especially when the application grows.

Wrap up

Instance variables inside view templates behave like global variables. Using local variables for partials rendering solves most of the issues caused by it, and makes partials reusable. Some developers prefer not to use instance variables at all, and render views with locals from controllers.

Mateusz Woźniczka
Ruby Developer

Check my Twitter

Check my Linkedin

Did you like it? 

Sign up To VIsuality newsletter

READ ALSO

Is Go Language the Right Choice for Your Next Project?

14
.
11
.
2023
Maciej Zdunek
Backend
Business

SXSW Tradeshow 2020: Get Your FREE Tickets and Meet Us

02
.
10
.
2024
Michał Krochecki
Ruby on Rails
Conferences
Frontend
Backend
Business

How to build effective website: simplicity & McDonald's

14
.
11
.
2023
Lukasz Jackiewicz
Ruby on Rails
Frontend
Design

Thermal Printer Protocols for Image and Text

14
.
11
.
2023
Burak Aybar
Backend
Tutorial
Software

WebUSB - Print Image and Text in Thermal Printers

14
.
11
.
2023
Burak Aybar
Backend
Tutorial
Software

What happened in Visuality in 2019

14
.
11
.
2023
Maciej Zdunek
Visuality
HR

Three strategies that work in board games and in real life

14
.
11
.
2023
Michał Łęcicki
Ruby on Rails

HR Wave - No Bullshit HR Conference 2019

14
.
11
.
2023
Alicja Gruszczyk
HR
Conferences

Lightning Talks in your company

14
.
11
.
2023
Jarosław Kowalewski
Ruby on Rails
Visuality

Stress in Project Management

02
.
10
.
2024
Wiktor De Witte
HR
Project Management

How to find good developers and keep them happy - Part 1

02
.
10
.
2024
Michał Krochecki
HR
Visuality

PKP Intercity - Redesign and case study of polish national carrier

14
.
11
.
2023
Katarzyna Szewc
Design
Business
Frontend

Let’s prepare for GITEX Dubai together!

14
.
11
.
2023
Michał Piórkowski
Conferences
Business

Ruby Quirks

14
.
11
.
2023
Jan Matusz
Ruby on Rails
Ruby

Visuality recognized as one of the Best Ruby on Rails Devs

14
.
11
.
2023
Maciej Zdunek
Ruby on Rails
Visuality
Business

Is the culture of the organization important?

14
.
11
.
2023
Alicja Gruszczyk
Conferences
Visuality

Between the devil and the deep blue sea

04
.
12
.
2023
Mateusz Wodyk
Project Management
Backend
HR

Let’s prototype!

14
.
11
.
2023
Michał Łęcicki
Ruby on Rails
Backend

5 marketing hacks which will make your life easier

14
.
11
.
2023
Maciej Zdunek
Marketing
Design