1. Trang chủ
  2. » Công Nghệ Thông Tin

Agile Web Development with Rails phần 2 docx

55 433 0

Đang tải... (xem toàn văn)

Tài liệu hạn chế xem trước, để xem đầy đủ mời bạn chọn Tải xuống

THÔNG TIN TÀI LIỆU

WHAT DEPOT DOES 46 During checkout we capture contact and payment details and then display a receipt page. We don’t yet know how we’re going to handle payment, so those details are fairly vague in the flow. The seller flow, shown in Figure 5.2 , is also fairly simple. After logging in, the seller sees a menu letting her create or view a product, or ship existing orders. Once viewing a product, the seller may optionally edit the product information or delete the product entirely. Figure 5.2: Flow of Seller Pages The shipping option is very simplistic. It displays each order that has not yet been shipped, one order per page. The seller may choose to skip to the next, or may ship the order, using the information from the page as appropriate. The shipping function is clearly not going to survive long in the real world, but shipping is also one of those areas where reality is often stranger than you might think. Overspecify it upfront, and we’re likely to get it wrong. For now let’s leave it as it is, confident that we can change it as the user gains experience using our application. Report erratum WHAT DEPOT DOES 47 Data The last thing we need to think about before plowing into the first round of coding is the data we’re going to be working with. Notice that we’re not using words such as schema or classes here. We’re also not talking about databases, tables, keys, and the like. We’re simply talking about data. At this stage in the development, we don’t know if we’ll even be using a database—sometimes a flat file beats a database table hands down. Based on the use cases and the flows, it seems likely that we’ll be working with the data shown in Figure 5.3 . Again, pencil and paper seems a whole lot easier than some fancy tool, but use whatever works for you. Figure 5.3: Initial Guess at Application Data Working on the data diagram raised a couple of questions. As the user builds their shopping cart, we’ll need somewhere to keep the list of prod- ucts she’s added to it, so I added a cart. But apart from its use as a tran- sient place to keep this list, the cart seems to be something of a ghost—I couldn’t find anything meaningful to store in it. To reflect this uncertainty, I put a question mark inside the cart’s box in the diagram. I’m assuming this uncertainty will get resolved as we implement Depot. Coming up with the high-level data also raised the question of what infor- mation should go into an order. Again, I chose to leave this fairly open for Report erratum LET’S CODE 48 now—we’ll refine this further as we start showing the customer our early iterations. Finally, you might have noticed that I’ve duplicated the product’s price in the line item data. Here I’m breaking the “initially, keep it simple” rule slightly, but it’s a transgression based on experience. If the price of a product changes, that price change should not be reflected in the line item price of currently open orders, so each line item needs to reflect the price of the product at the time the order was made. Again, at this point I’ll double check with my customer that we’re still on the right track. (Hopefully, my customer was sitting in the room with me while I drew these three diagrams.) 5.3 Let’s Code 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, 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,sotype 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. 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 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_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,onpage47, 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) ); 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 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 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 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’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 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 1 Note that the case is significant. If you use a nannyish GUI tool that insists on changing thecolumnnameto Id, you might have problems. Report erratum ITERATION A1: GET SOMETHING RUNNING 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 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 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 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 2 The .yml partofthenamestandsforYAML, 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 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). 3 Unless, 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. 4 Some readers also report getting the error Client 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 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’);. 5 You 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. 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,onpage56). 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 → page 180 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 devel- opment 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. Report erratum ITERATION A1: GET SOMETHING RUNNING 55 Figure 6.2: Adding a New Product Figure 6.3: We Just Added Our First Product Report erratum [...]... image along with the image URL? We’re faced with a dilemma here As developers, we’re trained to respond to these kinds of request with a sharp intake of breath, a knowing shake of the head, and a murmured “you want what?” At the same time, we also like to show off a bit In the end, the fact that it’s fun to make these kinds of changes using Rails wins out, and we fire up our trusty editor The Rails view... proper-looking web page here She needs at least a title at the top and a sidebar with links and news Report erratum 69 I TERATION B2: A DD P AGE D ECORATIONS Figure 7.1: Our First Catalog Page At this point in the real world we’d probably want to call in the design folks—we’ve all seen too many programmer-designed web sites to feel comfortable inflicting another on the world But the Pragmatic Web Designer... messy problem Fortunately, as a Rails programmer you don’t have to worry about all these low-level details (In fact, the only reason to go into them at all is to explain why users of Rails applications must have cookies enabled in their browsers.) Rather than have developers worry about protocols and cookies, Rails provides a simple abstraction Within the controller, Rails maintains a special hash-like... display, but instead we are faced with a somewhat brutal page What’s going on? At first, we might be tempted to think that we’d mispelled the name of the action method or the name of the view, but that’s not the case This isn’t a Rails error message—it comes straight from WEBrick To find out what’s going on, we need to look at the WEBrick console output Find the window where WEBrick is running, and you’ll... method, index( ) So why did we choose to call our first method index? Because, just like most web servers, if you invoke a Rails controller and don’t specify an explicit action, Rails automatically invokes the index action In fact, let’s try it Point a browser at http://localhost:3000/store and up pops our web page It might not make us rich, but at least we know things are all wired together correctly... method to verify that the price is a valid number File 65 validates_numericality_of :price Now, if we add a product with an invalid price, the appropriate message will appear.6 6 MySQL gives Rails enough metadata to know that price contains a number, so Rails converts it to a floating-point value With other databases, the value might come back as a string, so you’d need to convert it using Float(price) before... let’s put a placeholder in for now It’s time for an iteration 7 .2 Iteration B2: Add Page Decorations The pages in a particular web site typically share a similar layout—the designer will have created a standard template that is used when placing content Our job is to add this page decoration to each of the store pages Fortunately, in Rails we can define layouts A layout is a template into which we can... ways of specifying and using layouts in Rails We’ll choose the simplest for now If you create a template file in the app/views/layouts directory with the same name as a controller, all views rendered by that controller will use that layout by default So let’s create one now Our controller is called store, so we’ll name the layout store.rhtml File 72 Line 1 5 10 15 20 - Pragprog Books... the application uses cookies Rails uses the cookie-based approach A cookie is simply a chunk of named data that a web application passes to a web browser The browser stores the cookie locally on the user’s computer Subsequently, when the browser sends a request to the application, the cookie data tags along The application uses information in the cookie to match the request with session information stored... varchar(100) description text image_url varchar (20 0) price decimal(10 ,2) date_available datetime primary key (id) ); not not not not not not null auto_increment, null, null, null, null, null, When I first created this file, I added a drop table command at the top of it This now allows us to create a new (empty) schema instance with the commands depot> mysql depot _development . local WEBrick-based web server, supplied with Rails. depot> ruby script/server => Rails application started on http://0.0.0.0:3000 [20 05- 02- 08 12: 08:40] INFO WEBrick 1.3.1 [20 05- 02- 08 12: 08:40]. 1.3.1 [20 05- 02- 08 12: 08:40] INFO ruby 1.8 .2 (20 04- 12- 30) [powerpc-darwin7.7.0] [20 05- 02- 08 12: 08:40] INFO WEBrick::HTTPServer#start: pid =20 261 port=3000 Just as it did with our demo application in Chapter. A1: GET SOMETHING RUNNING 52 development: adapter: mysql database: rails _development host: localhost username: root password: test: adapter: mysql database: rails_ test host: localhost

Ngày đăng: 07/08/2014, 00:22

Xem thêm: Agile Web Development with Rails phần 2 docx

TỪ KHÓA LIÊN QUAN