Forms and post methods

In rare case will the user visiting a page hardcore input the url.There many ways to get inputs for user and one of them is form.Since it the common method will take look into it.

Let us assume when calculating sum of user input on url /2/2 your route was /sum/:first_number/:second with that assumption in mind(if you used another routes it ok just change content to fit your app).

Change the content of route like

Note the removable of variables of the route

myapp.rb

1
2
3
4
5
6
7
8
9
#some code above this comment
get '/sum' do

    @first_number =params[:first_number]
    @second_number =params[:second_number]
    erb:sum # use whatever template used for this

end
## some routes below this comment

Remove the content on the template you created to display sum result and replace with this form and other content.In my case I will use sum.erb.

My assumption here is your created an erb file inside view directory sum.erb

1
2
3
4
5
6
7
8
<h3>Enter Your Values</h3>
<form>
     <input name="first_number" type="text" placeholder="First Number" ><br>
     <input name="second_number" type="text" placeholder="Second Number"><br>
     <input type="submit" value="calculate">     

</form>
 <p>The sum of <%[email protected]_number%> and <%[email protected]_number%> is <%= @first_number.to_f + @second_number.to_f%></p>

This is template allow user to enter their data via form and see result after calculation

Conditional Rendering

It is awkward that our form show us results even without entering.Let us deal with that.When you display only what you want be seen by user on browser when certain condition is met is known as conditional rendering.We can you control flow statement for this.

sum.erb

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
<h3>Enter Your Values</h3>
<form>
     <input name="first_number" type="text" placeholder="First Number" ><br>
     <input name="second_number" type="text" placeholder="Second Number"><br>
     <input type="submit" value="calculate">    
</form>
 <% if @first_number != nil and @second_number != nil %>
         <%if @first_number.strip == "" or @second_number.strip==""%>
             <p> Kindly fill the form to calculate </p>
              <%else%>
              <p>The sum of <%[email protected]_number%> and <%[email protected]_number%> is <%= @first_number.to_f + @second_number.to_f%></p>

         <%end%>

     <%end%>

When the form is first rendered first_name and second_name have values of nil since we have not yet passed any data.Therefore result will not be render unless we hit calculate.But when we hit calculate with our form blank or if its content are spacebar or tab created param, we display Kindly fill the form to calculate since strip method remove all leading or trailing on a string the we compare value to empty string if true we request user to fill form.

If we enter value and click calculate we get the sum but there is catch when user enter no numeric value is give us result logically that's absurd but for now bare with me I wanna take deteour.We shall revisit soon.

Rendering of different template

When we calculate the sum you realize that we the result on the same page/template.Well that's not the only way out and to be honest it was rather hectic when render because we had to check whether our params were empty string. Form are ussually give action which the routes the are to render at if not given like we did not give our on the previous example it renders on the same page.Let us add an action which will hit /calculation_results in our application.Add it like so:

sum.erb

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
<h3>Enter Your Values</h3>
<form action="/calculation_result">
     <input name="first_number" type="text" placeholder="First Number" ><br>
     <input name="second_number" type="text" placeholder="Second Number"><br>
     <input type="submit" value="calculate">

</form>
<% if @first_number != nil and @second_number != nil %>
         <%if @first_number.strip == "" or @second_number.strip==""%>
             <p> Kindly fill the form to calculate </p>
              <%else%>
              <p>The sum of <%[email protected]_number%> and <%[email protected]_number%> is <%= @first_number.to_f + @second_number.to_f%></p>

         <%end%>

     <%end%>

When we enter our values and try calculating error pops on our face.Route /calculation_results is not there.Let us create in our application like so:

myapp.rb

1
2
3
4
5
6
# other routes
# /sum route
get "/calculation_result"
erb: results
end
#other routes

This demands we create results template kindly do so.

This new addition also demands refactor from in /sum route block.We longer receive params there it's prime function is just now to render our form therefore:

Edit content of /sum routes like so.

myapp.rb

1
2
3
4
5
#other routes
get '/sum' do
    erb:sum
end
# /calculate_results route

We also have to factor sum.erb file but remove all content outside the form tag.Cut them and copy them in result.erb you created.One more refactor is required.We are not handling params coming to calculate_results let us to that.

1
2
3
4
5
6
7
8
9
# other routes
# /sum route

get "/calculation_result" do
  @first_number =params[:first_number]
  @second_number =params[:second_number] 
  erb: results 
end
 # other routes

This if <% if @first_number != nil and @second_number != nil %> statement in result.erb do serve us anymore There no way user will send nil value anymore, we have seperate the forms. Remove it

Saving and accessing results in a file

Saving

As you can see after calculation our result get lost and we can't get it back well http requests are stateless and our application just temporarily hold the information.What if we permanently want to store our information.File is one of storage we can use.Will go file class in our docs and see how it can help us.

myapp.rb

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
# other routes
# /sum route

get "/calculation_result" do
  @first_number =params[:first_number].strip
  @second_number =params[:second_number].strip 
  @sum = @first_number.to_f + @second_number.to_f 
  File.open("results.txt", "a+") do |file|
    file.puts(@sum) 
end
erb:results
end
 # other routes

Here we append results of our calculations into and a file results.txt if it does not exist we create the file before appending.

Reading from our store

It would be of no use to us if save our result and not be able to refer to them later.So let do just that, let first create a route all_results.

myapp.rb

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
# other routes
#/calculation_result route

get '/all_results' do
  def all_results
  return [] unless File.exist?("results.txt")
  File.read("results.txt").split("\n")
  end
  @all_results= all_results
  erb:all_results
end
# other routes

all_results.erb

1
2
3
4
5
6
<h1> All Results</h1>
<ul>
  <%@all_results.each do |result| %>
    <li><%= result%></li>
  <% end %>
<ul>

Using conditional rendering ensure that user see No results at given mome nt if there in no result in our file.

post method

What if I told you we have been doing all wrong honestly.The get method as http protocols dictates it should be used to read data but we just used it to save our which very wrong.Let create a post method for /sum url .Add this content myapp.rb file. Tranfer content of /calculation_result into our new route like so.Delete /calculation_result content.

myapp.rb

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
 get '/sum' do
    erb:sum
  end
  post '/sum' do
    @first_number =params[:first_number].strip
    @second_number =params[:second_number].strip 
    @sum = @first_number.to_f + @second_number.to_f 
    File.open("results.txt", "a+") do |file|
      file.puts(@sum) 
      erb:calculation_results
    end

  end

/sum route has both get and post methods.One is for read i.e getting the form other is for creating calcution results.It make sense now

Validations

What if user enter wrong input for instance in sum route we expect only integer,the user enter non-numeric input for now application just to convert whatever the input to float and since non-numeric converted to interger or float returns 0.This should not be the case we shoud be case and I promised will visit well now this is the time.

myapp.rb

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
get "/calculation_results" do
  @first_number =params[:first_number].strip
  @second_number =params[:second_number].strip
  @empty_error = nil
  @invalid_values = nil

  unless @first_number.empty? or @second_number.empty? 
    @empty_error = "Kindly enter value "
  end

 regular_expression = /^[+-]?((\d+(\.\d*)?)|(\.\d+))$/ 
  if regular_expression.match?(@first_number) and  regular_expression(@second_number)
    @invalid_values = "Kindly Enter Numeric Values Only"
  else
    @sum = @first_number + @second_number
  end 

erb:
end