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
.