MVC Rails - Views

How to Implement Rails-like Dynamic Views in Ruby

In this article, we will learn 'one' way to implement the controller-to-view data handoff using instance variables, just like Rails, and following the same conventions as Rails. Although it's a highly simplified implementation to keep things simple, I hope you'll find it fun and learn something new.

8 min read

When I first started learning Rails, one of the patterns that I thought was pure 'magic' was how you could access a controller's instance variables in the views.

# controllers/posts_controller.rb
class PostsController
  def show
    @post = Post.find(1)
  end
end

# views/posts/show.html.erb
<%= @post.title %>

For a long time, I didn't understand how this would work behind the scenes. I tried reading the source code a few times without success. I just wasn't familiar enough with Ruby's advanced metaprogramming techniques.

If you're feeling the same, worry not. In this lesson, we'll learn one way to implement the Rails views pattern, where a controller's instance variables are accessible in the view, in pure Ruby.

This post won't show how Rails actually implements the views behind the scenes. I'm only trying to mimic the external Rails API, i.e. making controller instance variables available in views.

As things stand now, our controllers are returning a plain text response from the index action method. The router calls the controller's action to get this plain text response and returns it to the browser.

require_relative 'application_controller'

class ArticlesController < ApplicationController
  def index
    index_file = File.join(Dir.pwd, "views", "index.html")
    File.read(index_file)
  end
end

To make it behave like Rails, we'll want to assign the data required by a view to instance variables:

require_relative 'application_controller'

class ArticlesController < ApplicationController
  def index
    @title = "Write Software, Well"
    @tagline = "Learn to program Ruby and build webapps with Rails"
  end
end

Then, the instance variables @title and @tagline will be used by the corresponding view, just like Rails.

<%# views/articles/index.html.erb %> 

<header>
  <h1>
    <%= @title %>
  </h1>
  <p>
    <%= @tagline %>
  </p>
</header>

We'll learn how to can implement this later. But let's solve a simpler problem first.

Understanding the Concept of Binding in Ruby

Before trying to implement the above feature, let's try to solve a different, simpler problem. How can we access instance variables in a string template?

Binding is an elegant way to access the current scope (variables, methods, and self) in Ruby. Typically, you use it for building view templates and executing strings of Ruby code. The Ruby REPL also makes abundant use of binding.

The basic idea behind binding is to store the current context in an object for later use. Later, you can execute some code in the context of that binding, using eval.