• No results found

So, after sitting down with the customer and doing some preliminary anal- ysis, we’re ready to start using a computer for development! We’ll be work- ing from our original three diagrams, but the chances are pretty good that we’ll be throwing them away fairly quickly—they’ll become outdated as we gather feedback. Interestingly, that’s why we didn’t spend too long on them—it’s easier to throw something away if you didn’t spend a long time creating it.

In the chapters that follow, we’ll start developing the application based on our current understanding. However, before we turn that page, we have to answer just one more question. What should we do first?

I like to work with the customer so we can jointly agree on priorities. In this case, I’d point out to her that it’s hard to develop anything else until we have some basic products defined in the system, so I’d suggest spending a couple of hours getting the initial version of the product maintenance functionality up and running. And, of course, she’d agree.

Report erratum

Chapter 6

Task A: Product Maintenance

Our first development task is to create the web interface that lets us main- tain our product information—create new products, edit existing products, delete unwanted ones, and so on. We’ll develop this application in small

iterations, wheresmall means “measured in minutes.” Let’s get started....

6.1

Iteration A1: Get Something Running

Perhaps surprisingly, we should get the first iteration of this working in almost no time. We’ll start off by creating a new Rails application. This is where we’ll be doing all our work. Next, we’ll create a database to hold our information (in fact we’ll create three databases). Once that groundwork is in place, we’ll

• create the table to hold the product information,

• configure our Rails application to point to our database(s), and • have Rails generate the initial version of our product maintenance

application for us.

Create a Rails Application

Back on page 25 we saw how to create a new Rails application. Go to a

command prompt, and type rails followed by the name of our project. In

this case, our project is calleddepot, so type

work> rails depot

We see a bunch of output scroll by. When it has finished, we find that a

new directory, depot, has been created. That’s where we’ll be doing our

work.

ITERATIONA1: GETSOMETHINGRUNNING 50

work> cd depot

work> ls

CHANGELOG app db log test

README components doc public vendor

Rakefile config lib script

Create the Databases

For this application, we’ll use the open-source MySQL database server (which you’ll need too if you’re following along with the code). For reasons that will become clear later, we’re actually going to create three databases. • depot_developmentwill be our development database. All of our pro-

gramming work will be done here.

• depot_test is a test database. It is considered to be transient, so it’s perfectly acceptable for us to empty it out to give our tests a fresh place to start each time they run.

• depot_productionis the production database. Our application will use this when we put it online.

We’ll use the mysql command-line client to create our databases, but if

you’re more comfortable with tools such asphpmyadminorCocoaMySQL, go

for it. (In the session that follows, we’ve stripped out MySQL’s somewhat useless responses to each command.)

depot> mysql -u root -p

Enter password: *******

Welcome to the MySQL monitor. Commands end with ; or \g. mysql> create database depot_development;

mysql> create database depot_test;

mysql> create database depot_production;

mysql> grant all on depot_development.* to 'dave'@'localhost';

mysql> grant all on depot_test.* to 'dave'@'localhost';

mysql> grant all on depot_production.* to 'prod'@'localhost' identified by 'wibble';

mysql> exit

Create the Products Table

Back in Figure5.3, on page 47, we sketched out the basic content of the

products table. Now let’s turn that into reality. Here’s the Data Definition

Language (DDL) for creating theproductstable in MySQL.

File 22 drop table if exists products;

create table products (

id int not null auto_increment,

title varchar(100) not null,

description text not null,

image_url varchar(200) not null, price decimal(10,2) not null,

primary key (id) );

Report erratum

ITERATIONA1: GETSOMETHINGRUNNING 51

Our table includes the product title, description, image, and price, just as we sketched out. We’ve also added something new: a column called

id. This is used to give each row in the table a unique key, allowing

other tables to reference products. But there’s more to this id column.

By default, Rails assumes that every table it handles has as its primary

key an integer column called id.1 Internally, Rails uses the value in this

column to keep track of the data it has loaded from the database and to link between data in different tables. You can override this naming sys- tem, but unless you’re using Rails to work with legacy schemas that you

can’t change, we recommend you just stick with using the nameid.

It’s all very well coming up with the DDL for theproducts table, but where

should we store it? I’m a strong believer in keeping the DDL for my appli- cation databases under version control, so I always create it in a flat file.

For a Rails application, I call the file create.sql and put it in my applica-

tion’sdbsubdirectory. This lets me use themysqlclient to execute the DDL

and create the table in my development database. Again, you’re free to do this using GUI or web-based tools if you prefer.

depot> mysql depot_development <db/create.sql

Configure the Application

In many simple scripting-language web applications, the information on how to connect to the database is embedded directly into the code—you

might find a call to some connect( ) method, passing in host and database

names, along with a user name and password. This is dangerous, because password information sits in a file in a web-accessible directory. A small server configuration error could expose your password to the world. The approach of embedding connection information into code is also inflex- ible. One minute you might be using the development database as you hack away. Next you might need to run the same code against the test database. Eventually, you’ll want to deploy it into production. Every time you switch target databases, you have to edit the connection call. There’s a rule of programming that says you’ll mistype the password only when switching the application into production.

Smart developers keep the connection information out of the code. Some- times you might want to use some kind of repository to store it all (Java developers often use JNDI to look up connection parameters). That’s a bit

1Note that the case is significant. If you use a nannyish GUI tool that insists on changing

the column name toId, you might have problems.

Report erratum

ITERATIONA1: GETSOMETHINGRUNNING 52 development: adapter: mysql database: rails_development host: localhost username: root password: test: adapter: mysql database: rails_test host: localhost username: root password: production: adapter: mysql database: rails_production host: localhost username: root password: development: adapter: mysql database: depot_development host: localhost username: <blank> password: test: adapter: mysql database: depot_test host: localhost username: <blank> password: production: adapter: mysql database: depot_production host: localhost username: prod password: wibble config/database.yml

Edit the file

Original File New File

Figure 6.1: Configure thedatabase.ymlFile

heavy for the average web application that we’ll write, so Rails simply uses

a flat file. You’ll find it inconfig/database.yml.2

As Figure6.1 shows, database.yml contains three sections, one each for

the development, test, and production databases. Using your favorite edi- tor, change the fields in each to match the databases we created. Note

that in the diagram we’ve left the username fields blank for the develop-

ment and test environments in the new database.yml file. This is con-

venient, as it means that different developers will each use their own usernames when connecting. However, we’ve had reports that with some combinations of MySQL, database drivers, and operating systems, leav- ing these fields blank makes Rails attempt to connect to the database

as the root user. Should you get an error such asAccess denied for user

’root’@’localhost.localdomain’, put an explicit username in these two fields.

Create the Maintenance Application

OK. All the ground work has been done. We set up our Depot application

as a Rails project. We’ve created the databases and theproductstable. And

2The .ymlpart of the name stands for YAML, or YAML Ain’t a Markup Language. It’s

a simple way of storing structured information in flat files (and it isn’t XML). Recent Ruby releases include built-in YAML support.

Report erratum

ITERATIONA1: GETSOMETHINGRUNNING 53

we configured our application to be able to connect to the databases. Time to write the maintenance app.

depot> ruby script/generate scaffold Product Admin

dependency model exists app/models/ exists test/unit/ exists test/fixtures/ create app/models/product.rb create test/unit/product_test.rb : : create app/views/admin/show.rhtml create app/views/admin/new.rhtml create app/views/admin/edit.rhtml create app/views/admin/_form.rhtml

That wasn’t hard now, was it?3,4

That single command has written a basic maintenance application. The

Productparameter told the command the name of the model we want, and

theAdminparameter specifies the name of the controller. Before we worry about just what happened behind the scenes here, let’s try our shiny new application. First, we’ll start a local WEBrick-based web server, supplied with Rails.

depot> ruby script/server

=> Rails application started on http://0.0.0.0:3000 [2005-02-08 12:08:40] INFO WEBrick 1.3.1

[2005-02-08 12:08:40] INFO ruby 1.8.2 (2004-12-30) [powerpc-darwin7.7.0] [2005-02-08 12:08:40] INFO WEBrick::HTTPServer#start: pid=20261 port=3000

Just as it did with our demo application in Chapter4,Instant Gratification,

this command starts a web server on our local host, port 3000.5 Let’s

connect to it. Remember, the URL we give to our browser contains both the port number (3000) and the name of the controller in lowercase (admin).

3Unless, perhaps, you’re running OS X 10.4. It seems as if Tiger has broken Ruby’s

standard MySQL library. If you see the errorBefore updating scaffolding from new DB schema, try creating a table for your model (Product), it may well be because Ruby (and hence Rails) can’t get to the database. To fix Apple’s bad install, you’re going to need to reinstall Ruby’s MySQL library, which means going back to on page21, running the script to repair the Ruby installation, and then reinstalling themysqlgem.

4Some readers also report getting the errorClient does not support authentication protocol

requested by server; consider upgrading MySQL client. This incompatibility between the ver- sion of MySQL installed and the libraries used to access it can be resolved by following the instructions athttp://dev.mysql.com/doc/mysql/en/old-client.htmland issuing a MySQL command such asset password for ’some_user’@’some_host’ = OLD_PASSWORD(’newpwd’);.

5You might get an error sayingAddress already in usewhen you try to run WEBrick.

That simply means that you already have a Rails WEBrick server running on your machine. If you’ve been following along with the examples in the book, that might well be theHello World!application from Chapter 4. Find its console, and kill the server using control-C.

Report erratum

ITERATIONA1: GETSOMETHINGRUNNING 54

Port: 3000

Controller: admin

That’s pretty boring. It’s showing us a list of products, and there aren’t

any products. Let’s remedy that. Click the New product link, and a form

should appear. Figure 6.2, on the following page shows the form after it

is filled in. Click the Create button, and you should see the new product

in the list (Figure 6.3, on the next page). Perhaps it isn’t the prettiest

interface, but it works, and we can show it to our client for approval. They can play with the other links (showing details, editing existing products,

as shown in Figure 6.4, on page 56). We explain to them that this is

only a first step—we know it’s rough, but we wanted to get their feedback early. (And 25 minutes into the start of coding probably counts as early in anyone’s book.)

Rails Scaffolds

We covered a lot of ground in a very short initial implementation, so let’s take a minute to look at that last step in a bit more detail.

A Rails scaffold is an autogenerated framework for manipulating a model. When we run the generator, we tell it that we want a scaffold for a particu- lar model (which it creates) and that we want to access it through a given controller (which it also creates).

In Rails, a model is automatically mapped to a database table whose name name mapping

page180

is the plural form of the model’s class. In our case, we asked for a model

calledProduct, so Rails associated it with the table calledproducts. And how

did it find that table? We told it where to look when we set up the devel-

opmententry inconfig/database.yml. When we started the application, the model examined the table in the database, worked out what columns it had, and created mappings between the database data and Ruby objects.

Report erratum

ITERATIONA1: GETSOMETHINGRUNNING 55

Figure 6.2: Adding a New Product

Figure 6.3: We Just Added Our First Product

Report erratum

ITERATIONA1: GETSOMETHINGRUNNING 56

Figure 6.4: Showing Details and Editing

That’s why the New products form came up already knowing about the

title, description, image, and price fields—because they are in the database table, they are added to the model. The form generator used by the scaf- fold can ask the model for information on these fields and uses what it discovers to create an appropriate HTML form.

Controllers handle incoming requests from the browser. A single applica- tion can have multiple controllers. For our Depot application, it’s likely that we’ll end up with two of them, one handling the seller’s administra- tion of the site and the other handling the buyer’s experience. We created

the product maintenance scaffolding in theAdmincontroller, which is why

the URL that accesses it hasadminat the start of its path.

The utility that generates a Rails scaffold populates your application’s directory tree with working Ruby code. If you examine it, you’ll find that what you have is the bare bones of a full application—the Ruby code has been placed inline; it’s all in the source, rather than simply being a sin- gle call into some standard library. This is good news for us, because it

Report erratum

ITERATIONA2: ADD AMISSINGCOLUMN 57

David Says. . .

Won’t We End Up Replacing All the Scaffolds?

Most of the time, yes. Scaffolding is not intended to be the shake ’n’ bake of application development. It’s there as supportwhileyou build out the application. As you’re designing how the list of products should work, you rely on the scaffold-generated create, update, and delete actions. Then you replace the generated creation functionality while relying on the remaining actions. And so on and so forth.

Sometimes scaffolding will be enough, though. If you’re merely interested in getting a quick interface to a model online as part of a backend inter- face, you may not care that the looks are bare. But this is the exception. Don’t expect scaffolding to replace the need for you as a programmer just yet (or ever).

means that we can modify the code produced in the scaffold. The scaffold is the starting point of an application, not a finished application in its own right. And we’re about to make use of that fact as we move on to the next iteration in our project.