I picked up “Exercises for Programmers” by Brian P. Hogan a while back as a way to test my skills and keep them sharp. Earlier today it occurred to me that since I’m in the process of teaching myself Ruby on Rails, it might be fun to implement these exercises in a Rails app.
Exercise 1: Say Hello
The problem description for exercise 1 says to “create a program that prompts for your name and prints a greeting using your name”. Since I’m using Rails, it’s obvious to me that I want to create form with an text box to receive the name input, and redirect to a page that displays the greeting.
First I created the app.
rails new exercises_for_programmers_rails
Next I created a controller with ‘new’ and ‘show’ views to receive the input and produce the output respectively.
rails g controller say_hello new show
This automatically inserted the following routes into the routes file.
# app/config.routes.rb Rails.application.routes.draw do get 'say_hello/new' get 'say_hello/show' end
Next I updated the auto-generated view file
/app/views/say_hello/new.html.erb to include a form with a text field for receiving the ‘name’ input
<%# app/views/say_hello/new.html.erb %> <h1>Saying Hello</h1> <p>What is your name?</p> <%= form_for(:form) do |f| %> <%= f.text_field :name %> <%= f.submit %> <% end %>
Then I added a create action to
app/controllers/say_hello_controller.rb to capture the input, set it to an instance variable, and redirect to the show view.
# app/controllers/say_hello_controller.rb ... def create @name = params[:form][:name] redirect_to say_hello_show_path end ...
<%# app/views/say_hello/show.html.erb %> <p>Hello <%= @name %>, It's nice to meet you!</p> <%= link_to 'Ask Name', say_hello_new_path %>
After this, I booted the rails app up running
rails s tested it out:
After clicking ‘Save Form’, I was met with the following error:
The issue here was that in
form_for helper doesn’t specify a url to post to, so it defaults to the the path that points to the current page. Fixing this was a two step process:
First, I added a new route to
app/config/routes.rb to associate a post request with the
create action on the
# app/config/routes.rb Rails.application.routes.draw do ... post 'say_hello' => 'say_hello#create' end
Second, I updated
form_for helper in
app/views/say_hello/new.html.erb to point to the new route:
<%# app/views/say_hello/new.html.erb %> <h1>Saying Hello</h1> <p>What is your name?</p> <%= form_for(:form, url: say_hello_path) do |f| %> <%= f.text_field :name %> <%= f.submit %> <% end %>
Testing it again fixes the routing error:
But in the resulting page, the name isn’t displayed:
That’s when it occurs to me that every time the browser makes a request in to a Rails app, and the rails router maps that request to a controller action, a new instance of the controller is created. So all state set by a previous request (including the instance variable
@name set by the create method) disappears.
Typically Rails applications use ActiveRecord to persist state to a database which is queried for each requests. That seems like overkill for such a simple exercise, so instead I create a singleton object to cache the name in the controllers
create action, and retrieve it in the show action. Below shows the updated and complete source for the
# app/controllers/say_hello_controller.rb class SayHelloController < ApplicationController def new end def show # @name = SayHello.get_instance.name end def create @name = params[:form][:name] # SayHello.get_instance.name = @name redirect_to say_hello_show_path end end class SayHello @@instance = SayHello.new attr_accessor :name def self.get_instance @@instance end end
Testing it again:
You can find all the source code in this exercise here.
Thanks for reading!