College Park Crime Reports
Building the Web Interface: the 'C' and 'V' in 'MCV'
Rails is built to implement the Model-Controller-View design pattern. When I used its built-in scripts to generate the file structure for my project, Rails produced directories called 'controllers' and 'views'. With just a few more commands, I can generate scaffolding files. I could also write classes myself for controllers and views. They extend base classes that come with Rails and contain the basic functionality I need.
Adding and Testing a Controller
There is a simple and straight-forward way to create a basic controller: use the generate script. Here's the command, and some of the output.
$ script/generate controller CrimeReports create app/controllers/crime_reports_controller.rb create test/functional/crime_reports_controller_test.rb
These two new files are, again, only stubs. To create files with default code already in place, and to produce files for default views automatically too, I'll use generate with the scaffold argument. Since I just used generate to create stubs, I will be prompted to overwrite those stub files. I could have just skipped the earlier generation and used the command below to create the files.
$ ruby script/generate scaffold CrimeReport ... overwrite app/controllers/crime_reports_controller.rb? [Ynaq] Y force app/controllers/crime_reports_controller.rb overwrite test/functional/crime_reports_controller_test.rb? [Ynaq] Y force test/functional/crime_reports_controller_test.rb ...
Now that I'm working with controllers, Rails has produced some functional tests for me. I can run them like this:
$ rake test:functionals ... 1) Failure: test_create(CrimeReportsControllerTest) [./test/functional/crime_reports_controller_test.rb:55]: Expected response to be a <:redirect>, but was <200> ...
Uh oh. Why don't the tests work? Well, there are two problems. First, the tests don't know about requirements of our CrimeReport model, that one must specify certain values for description, lat, and long. So this test within /test/functional/crime_reports_controller_test.rb fails:
def test_create num_crime_reports = CrimeReport.count post :create, :crime_report => {} ...
Second, there are no records in the test database, so this test, which assumes it can get a record out of that database, also fails:
def test_show get :show, :id => 1 ...
I can fix the tests. First of all, the test_create method should specify values for the CrimeReport object it's creating.
def test_create num_crime_reports = CrimeReport.count post :create, :crime_report => { :lat => 40.0, :long => 75.0, :description => 'Some description' }
To handle the issue of pre-existing data, I will set up a test fixture. This is just a YAML file that describes data to be used to generate objects/records. Now we come to why there are separate test and development databases: because when you use rake to run tests, not only does Rails switch your code over to using the test database, thus leaving development and production data alone, but it uses the files it finds in the test/fixtures folder to populate the test database before the tests are run.
$ vi test/fixtures/crime_reports.yml
first: id: 1 lat: 41.0 long: 81.0 description: First test description another: id: 2 lat: 42.0 long: 82.0 description: 2nd test description
$ rake test:functionals ........ Finished in 1.257341 seconds. 8 tests, 28 assertions, 0 failures, 0 errors
Working with Views
All this work and we haven't seen any web pages yet. But Rails has set them up for us, going, as always, by convention. Let's take a look. I fire up my browser and point it to my website, to the folder /projects/cpcr/public/crime_reports/list. Here's what I see:

This is a default Rails List View. All the data is presented in a table. It's ugly, but it's essentially complete. The code that puts all of the data on the page is written for me, in /app/views/crime_reports/list.rhtml. If I select the "show" link at the end of a record, I am taken to the Show View:

There's a lot of work to do here. I want a Google map on this page and on the List View as well. But before I get started, I'm going to make a little change that makes the Crime Report Listing the default page for this application. I'll do that by editing the routing configuration file:
$ vi config/routes.rb
In this file, I come across a commented-out line that begins with "map.connect".
# map.connect '', :controller => "welcome"
I'm going to change it thus:
map.connect '', :controller => "crime_reports", :action => 'list'
Now pointing a browser to /projects/cpcr/public/ brings up the List View.
Note that the controller for CrimeReport is 'crime_reports', even though the generate command we used was script/generate controller CrimeReports. Rails has naming conventions that switch between CamelCase and underscore_delimited schemes. It takes some getting used to.