Software developer blog

Web app crash course (Part 2)

In the previous part we dipped our toe into git, bundler, sinatra and mustache. In this part we will first explore how to add some interesting logic to our web application, while keeping that logic unaware of the very existence of the web. In fact we will go as far as to create a web app and a command line application that will do the same thing through different user interfaces. In the process of creating these applications we will also explore the realm of test driven development, a practice that can lead us to better software.

If you haven't read the previous part, click here.

Writing tests

The way most developers write code today, is through incremental changes: we do the simplest useful thing, and try it, and if it's good, we build upon it. These incremental changes have the drawback of having to change existing code over and over again.

One natural reaction to this is to design the application before starting to code, which used to be a popular approach from the late 1970s till the turn of the century. The problem with it was that it quickly turned out that software needs to change a lot more, than what would make it practical to follow such process. By 2001 the number of failing software projects was so high, that it was apparent that something needs to be done. That year bunch of software developers gathered in a cottage near Snowbird, Utah, and came up with the Agile manifesto. It became the foundation of how most software companies work today. One of the most important corollaries of that document, is that software development needs to be iterative.

Now that we came to the conclusion that code needs to be changed regularly, comes the question of how should we avoid introducing bugs to a previously working software system. The answer is: by testing the application. However testing the same things over and over again is boring, and error prone, so we want to avoid that. This is how many developers came to the conclusion that the best way to preserve the integrity of a software is to create automated tests.

We will use a framework called rspec. To use it we first need to install it, like before. Let's add rspec to our gem file:

Gemfile
source 'https://rubygems.org'
 
gem 'sinatra'
gem 'mustache'
gem 'rspec'

And run bundle to get rspec installed.

We will create a spec directory, that is where we will put our tests. Create a file called fizzbuzz_spec.rb here. First we will implement a very simple algorithm that will take an integer, and return the same integer as string, except when the number is dividable by 3 or 5. When the number is dividable by 3, we will return "Fizz" and if it is dividable by 5, we will return "Buzz". When the number is dividable by both 3 and 5 (i.e. it is a multiple of 15) we will return "FizzBuzz".

To begin with we will start by writing a test. This sounds odd to some people, but there is a very simple explanation: if we don't see the test fail, there is nothing that guaranties that it's actually testing something. Our first test will ensure that the FizzBuzz class exists:

spec/fizzbuzz_spec.rb
require 'rspec'
 
describe FizzBuzz do
 
end

Now if you run rspec spec you will get an error message like this:

/home/devill/git/sinatra-tutorial/spec/fizzbuzz_spec.rb:3:in `<top (required)>': uninitialized constant FizzBuzz (NameError)
	from /var/lib/gems/1.9.1/gems/rspec-core-2.14.7/lib/rspec/core/configuration.rb:896:in `load'
	from /var/lib/gems/1.9.1/gems/rspec-core-2.14.7/lib/rspec/core/configuration.rb:896:in `block in load_spec_files'
	from /var/lib/gems/1.9.1/gems/rspec-core-2.14.7/lib/rspec/core/configuration.rb:896:in `each'
	from /var/lib/gems/1.9.1/gems/rspec-core-2.14.7/lib/rspec/core/configuration.rb:896:in `load_spec_files'
	from /var/lib/gems/1.9.1/gems/rspec-core-2.14.7/lib/rspec/core/command_line.rb:22:in `run'
	from /var/lib/gems/1.9.1/gems/rspec-core-2.14.7/lib/rspec/core/runner.rb:80:in `run'
	from /var/lib/gems/1.9.1/gems/rspec-core-2.14.7/lib/rspec/core/runner.rb:17:in `block in autorun'

We can fix this by creating the class. For now I will keep this in the same file:

spec/fizzbuzz_spec.rb
require 'rspec'
 
class FizzBuzz
 
end
 
describe FizzBuzz do
 
end

Now if you rerun rspec spec the test will pass. Our next test case will be a bit more useful:

spec/fizzbuzz_spec.rb
require 'rspec'
 
class FizzBuzz
 
end
 
describe FizzBuzz do
  it 'should return "1" for 1' do
    expect(subject.fizz_buzz(1)).to eq('1')
  end
end

Now the test fails with undefined method. But before we implement the fizz_buzz function, let me explain that one liner in the test. The variable subject always points to an instance of the class specified in the call of the describe function, in this case an instance of FizzBuzz. So essentially subject.fizz_buzz(1) is equivalent to FizzBuzz.new.fizz_buzz(1). We would like to check that the return value is 1, so we set up an expectation. The way to do that with rspec, is to wrap the return value into expect(...).to and pass an expectation into the to function. There are lots of marchers, but most of the time, we use eq that sets up the expectation, that the result should be equal to the value passed to it. In this case we expect the result to be the string '1'.

Now let's get to implementing the logic that will make this test pass. First we create an empty function with the name fizz_buzz. The test will now fail with incorrect return value. We can fix that by always returning '1'.

spec/fizzbuzz_spec.rb
require 'rspec'
 
class FizzBuzz
  def fizz_buzz(number)
    '1'
  end
end
 
describe FizzBuzz do
  it 'should return "1" for 1' do
    expect(subject.fizz_buzz(1)).to eq('1')
  end
end

And now the test passes again. From this point on I will not show you the entire code at each step, since that would take up to much space. Instead here is a video of me completing this function:

Test Driven Development

Note that while implementing the function I tried to satisfy the test with the smallest possible production code. Similarly I always tried to write as little of the test as possible to make it fail. Both of these are fairly typical, and serve to decompose the process of development into the tiniest possible steps. This can result in ugly repetitive code, so when I had my tests passing, I refactored it (i.e. changed it's structure without changing the behavior) so that it becomes more generic, and easier to extend.

The practice I described above is called Test Driven Development. If you would like to know more about it check out Uncle Bobs Clean Coders videos on the topic, or read Kent Becks book, "Test Driven Development: By Example".

This is the final code that was written in the video:

spec/fizzbuzz_spec.rb
require 'rspec'
 
class FizzBuzz
  def fizz_buzz(number)
    result = ''
    result += 'Fizz' if number % 3 == 0
    result += 'Buzz' if number % 5 == 0
    result = number.to_s if result == ''
    result
  end
end
 
describe FizzBuzz do
  [
      [1, '1'],
      [2, '2'],
      [3, 'Fizz'],
      [6, 'Fizz'],
      [5, 'Buzz'],
      [10, 'Buzz'],
      [15, 'FizzBuzz'],
  ].each do |input, result|
    it "should return #{input} for #{result}" do
      expect(subject.fizz_buzz(input)).to eq(result)
    end
  end
end

Now before I committed again, I created a new lib directory, and copied the FizzBuzz class (without the tests) to a new file in the lib directory called fizzbuzz.rb. In the test I than replaced the class with a simple require:

lib/fizzbuzz.rb
class FizzBuzz
  def fizz_buzz(number)
    result = ''
    result += 'Fizz' if number % 3 == 0
    result += 'Buzz' if number % 5 == 0
    result = number.to_s if result == ''
    result
  end
end
spec/fizzbuzz_spec.rb
require 'rspec'
require_relative '../lib/fizzbuzz'
 
describe FizzBuzz do
  [
      [1, '1'],
      [2, '2'],
      [3, 'Fizz'],
      [6, 'Fizz'],
      [5, 'Buzz'],
      [10, 'Buzz'],
      [15, 'FizzBuzz'],
  ].each do |input, result|
    it "should return #{input} for #{result}" do
      expect(subject.fizz_buzz(input)).to eq(result)
    end
  end
end

Now we are ready to commit. First we add the new directories git add lib spec, then commit git commit -a -m 'FizzBuzz implementation' and publish our code to GitHub git push. You can download this commit from GitHub here.

Web front end for FizzBuzz

Now all that is left is to connect what we have done in the previous part to the new FizzBuzz class. First let's create a template, fizz_buzz_view.mustache

fizz_buzz_view.mustache
<!DOCTYPE html>
<html>
    <head>
        <title>FizzBuzz</title>
    </head>
<body>
    <h1>The fizz buzz for {{{number}}} is {{{fizz_buzz}}}</h1>
</body>
</html>

And we also need a mustache view. It will need to have two functions: number and fizz_buzz. The number will be passed in through the initializer as before, and made available with attr_reader. The fizz_buzz function is a proper function that creates an instance of FizzBuzz and calls its fizz_buzz function with the number passed to the view in the initializer. Here is the code for fizz_buzz_view.rb:

fizz_buzz_view.rb
require_relative 'lib/fizzbuzz'
 
class FizzBuzzView < Mustache
  attr_reader :number
 
  def initialize(number)
    @number = number
  end
 
  def fizz_buzz
    FizzBuzz.new.fizz_buzz number.to_i
  end
end

Note that the fizz_buzz function in the FizzBuzzView is just delegating to the fizz_buzz function in the FizzBuzz class. Functions on the view class tend to be like that, since we would like to separate out the business logic from the presentation code.

Also note, that I haven't written any tests for the view: it's really hard to meaning fully test this logic. One way would be to test the FizzBuzzView function, but that would only check the delegation logic, which is really simple and unlikely to change or break. So I call bullocks on automated testing of a one liner like that, instead I tried it out, and took a look at the result.

On the other hand we still have something that is untested and has a high chance of breaking. The template expects the FizzBuzzView to have certain functions, and those functions should return data with a certain structure. This is a kind of integration problem, and there are tools for testing them, however I won't go into details at this point. Maybe later.

Finally we need to add a new route to app.rb

app.rb
require 'sinatra'
 
require_relative 'hello_view'
require_relative 'fizz_buzz_view'
 
get '/' do
  HelloView.new.render
end
 
get '/hello/:name' do
  HelloView.new(params[:name]).render
end
 
get '/fizzbuzz/:number' do
  FizzBuzzView.new(params[:number]).render
end

Now if you start the app with bundle exec ruby app.rb, and visit http://localhost:4567/fizzbuzz/12315 you should find out what the fizz buzz for 12315 is.

When you finished playing around with the app, you can stop Sinatra by going back to the terminal and pressing CTRL + C.

Let's commit our latest changes again: git add fizz_buzz_view.* and git commit -a -m 'FizzBuzz web app'. Download here.

Command line FizzBuzz

At the beginning I promised that we will also write a command line version of our application. I wanted to show you this, so that you understand why did we write the logic in a separate class instead of just implementing it into the FizzBuzzView. Most of the time we develop something useful it needs to be ported to other platforms, like desktop applications, command line tools, web APIs and so on. For that reason it's worth prepare for such situations, especially since it makes testing easier.

Creating a command line app is even simpler than the web app was. All we need, is to read the first command line argument, pass it to our business object, and print the result. Here is my fizz_buzz_cli.rb:

fizz_buzz_cli.rb
require_relative 'lib/fizzbuzz'
 
puts FizzBuzz.new.fizz_buzz ARGV.first.to_i

Apart from the require_relative at the beginning, this is actually a one liner. You can try the command line version by typing this into your terminal: bundle exec ruby fizz_buzz_cli.rb 12315. This will print the fizz-buzz of 12315 into the terminal.

Let's commit this one as well: git add fizz_buzz_cli.rb and git commit -a -m 'FizzBuzz command line interface'. This is a nice time to also publish the code to GitHub: git push. Download here.

Clean up

While finishing up this part of the tutorial I spotted some problems. As always, it's time to do a little tidy up.

First of all, I was inconsistent with the naming of the fizz_buzz files. Some of them are written in one word, some of them are written as two words. Since the convention is to have it as two words, let's follow that. I will first rename the files. It's important to do this with git, since otherwise git will loose track of those files. Here is how I do it: git mv lib/fizzbuzz.rb lib/fizz_buzz.rb and git mv spec/fizzbuzz_spec.rb spec/fizz_buzz_spec.rb. I also need to update the require_relative '../lib/fizbuzz' to require_relative '../lib/fiz_buzz' in the spec file spec/fizz_buzz_spec.rb. Similarly we need to make a similar change in fizz_buzz_cli.rb and fizz_buzz_view.rb.

Let's make a mark in our commit history: git commit -a -m 'Fix fizz buzz file names'

Another problem I spotted at this point was the proliferation of view files in our main directory. If we don't move them to a sub directory, we will soon have trouble finding files we need. So let's move them to a new directory by the name views. Here is the list of commands I issued to achieve that:

mkdir views
git mv hello_view.* fizz_buzz_view.* views

Now I also need to change the require paths in app.rb:

app.rb
require 'sinatra'
 
require_relative 'views/hello_view'
require_relative 'views/fizz_buzz_view'
 
get '/' do
  HelloView.new.render
end
 
get '/hello/:name' do
  HelloView.new(params[:name]).render
end
 
get '/fizzbuzz/:number' do
  FizzBuzzView.new(params[:number]).render
end

And also, since we moved the file fizz_buzz_view.rb we need to change the line require_relative 'lib/fizz_buzz' to require_relative '../lib/fizz_buzz'. Finally since Mustache looks for the templates in the root directory, we need to specify that it should look in the views directory. We need to add self.template_path = File.dirname(__FILE__) at the begining of both view classes. Here is how the hello_view.rb:

views/hello_view.rb
require 'mustache'
 
class HelloView < Mustache
  self.template_path = File.dirname(__FILE__)
 
  attr_reader :user_name
 
  def initialize(user_name = 'World')
    @user_name = user_name
  end
end

Don't forget to add the same line to fizz_buzz_view.rb as well. Finally we commit and push: git commit -a -m 'Move templates to separate directory' and git push. Download here.

Summary

Last time we managed to set up a very simple page that could greet people. This time we added some very simple logic to our web application. One way to advance would be to add a more sophisticated logic, but I will leave that as an exercise. Watch the Roman numerals kata by Corey Haines, and based on that try to create a web page that will return the roman numeral for each number that is passed to it.

Instead next time we will create a very simple forum page, where people can send messages to each other.