Добавил:
Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз: Предмет: Файл:
Agile Web Development With Rails, 1st Edition (2005).pdf
Скачиваний:
28
Добавлен:
17.08.2013
Размер:
7.99 Mб
Скачать

Chapter 6

Task A: Product Maintenance

Our first development task is to create the web interface that lets us maintain our product information—create new products, edit existing products, delete unwanted ones, and so on. We’ll develop this application in small iterations, where small 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 called depot, 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.

Prepared exclusively for Rida Al Barazi

 

 

 

ITERATION A1: GET SOMETHING RUNNING

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_development will be our development database. All of our programming 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_production is 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 as phpmyadmin or CocoaMySQL, 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 Figure 5.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 the products table 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) );

Prepared exclusively for Rida Al Barazi

Report erratum

ITERATION A1: GET SOMETHING RUNNING

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 system, but unless you’re using Rails to work with legacy schemas that you can’t change, we recommend you just stick with using the name id.

It’s all very well coming up with the DDL for the products table, but where should we store it? I’m a strong believer in keeping the DDL for my application 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 application’s db subdirectory. This lets me use the mysql client 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 inflexible. 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. Sometimes 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 to Id, you might have problems.

Prepared exclusively for Rida Al Barazi

Report erratum

 

 

 

 

ITERATION A1: GET SOMETHING RUNNING

52

development:

 

 

 

 

development:

 

 

adapter:

mysql

 

 

 

adapter:

mysql

 

database:

rails_development

 

 

 

database:

depot_development

 

host:

localhost

 

 

 

host:

localhost

 

username: root

 

 

 

username: <blank>

 

password:

 

 

 

 

password:

 

 

test:

 

 

 

 

test:

 

 

adapter:

mysql

 

 

 

adapter:

mysql

 

database:

rails_test

 

 

 

database:

depot_test

 

host:

localhost

 

Edit the file

host:

localhost

 

username: root

 

 

 

username: <blank>

 

config/database.yml

 

password:

 

 

 

 

password:

 

 

production:

 

 

 

 

production:

 

 

adapter:

mysql

 

 

 

adapter:

mysql

 

database:

rails_production

 

 

 

database:

depot_production

 

host:

localhost

 

 

 

host:

localhost

 

username: root

username: prod

password:

password: wibble

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 in config/database.yml.2

As Figure 6.1 shows, database.yml contains three sections, one each for the development, test, and production databases. Using your favorite editor, 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 development and test environments in the new database.yml file. This is convenient, 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, leaving these fields blank makes Rails attempt to connect to the database as the root user. Should you get an error such as Access 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 the products table. And

2The .yml part 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.

Prepared exclusively for Rida Al Barazi

Report erratum

ITERATION A1: GET SOMETHING RUNNING

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 Product parameter told the command the name of the model we want, and the Admin parameter 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 Chapter 4, 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 error Before 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 page 21, running the script to repair the Ruby installation, and then reinstalling the mysql gem.

4Some readers also report getting the error Client does not support authentication protocol requested by server; consider upgrading MySQL client. This incompatibility between the version of MySQL installed and the libraries used to access it can be resolved by following the instructions at http://dev.mysql.com/doc/mysql/en/old-client.html and issuing a MySQL command such as set password for ’some_user’@’some_host’ = OLD_PASSWORD(’newpwd’);.

5You might get an error saying Address already in use when 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 the Hello World! application from Chapter 4. Find its console, and kill the server using control-C.

Prepared exclusively for Rida Al Barazi

Report erratum

ITERATION A1: GET SOMETHING RUNNING

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 particular 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 is the plural form of the model’s class. In our case, we asked for a model called Product, so Rails associated it with the table called products. And how did it find that table? We told it where to look when we set up the development entry in config/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.

name mapping

page 180

Prepared exclusively for Rida Al Barazi

Report erratum

ITERATION A1: GET SOMETHING RUNNING

55

Figure 6.2: Adding a New Product

Figure 6.3: We Just Added Our First Product

Prepared exclusively for Rida Al Barazi

Report erratum

ITERATION A1: GET SOMETHING RUNNING

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 scaffold 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 application 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 administration of the site and the other handling the buyer’s experience. We created the product maintenance scaffolding in the Admin controller, which is why the URL that accesses it has admin at 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 single call into some standard library. This is good news for us, because it

Prepared exclusively for Rida Al Barazi

Report erratum