Ruby-on-Rails

Validating Author Data in an Online Bookstore

In this third part of a four-part article series on building an online bookstore application with Ruby on Rails, you'll learn how to actually create authors in the system, validate data, and more. This article is excerpted from chapter two of the book Practical Rails Projects, written by Eldon Alameda (Apress; ISBN: 1590597818).

The next thing to do is to actually create the author. For this, we use thecreate action, where the form on thenewpage is pointing. What we want from the create functionality is that when we post a form to thecreateaction, a new author is created according to the parameters we specify. Let’s extend our tests to test for this, too, by adding a new test case method.

Flash is a hash-like structure stored on the server for the time of one sequential action. It is available inboth controllers and views. So when we set flash[:notice] in the create action and then redirect to the index action, the index page has access to the notice. If the user then clicks a link and goes on to another page, the notice has disappeared from the flash and the notice is not shown anymore.

If you sometimes want to show a flash message in the current action so that it will not be available in the next action (for example, when a form input has been invalid and you want to rerender the form instead of redirecting to the next action), you should use flash.now instead of flash. It will cause the message to becleaned up immediately after the current action has been processed.

Here, we first test that there are no authors in the test database. Then we try to create a new author by simulating the HTTPPOST method sending a form to thecreate action. We expect that Rails responds to thePOSTrequest by redirecting us to the index page. We also want to make sure that a newAuthor object is created and that Rails sets theflash[:notice]variable correctly.

However, the test code is not really beautiful. Instead of checking the exact amount of authors before and after running thecreateaction, we only want to know that the amount was incremented by one. Fortunately, the chaps at projectionist (http://project.ioni.st/) have created a helper assertion method for testing just that (http://project.ioni.st/post/218#post-218). Opentest/test_helper.rbin a text editor and add the following code to it:

Tip test/test_helper.rbis a file where you can define helper methods that are available to all tests. A helper method can be a custom assertion like above, or any other method that makes writing actual tests easier and cleaner.

assert_differencetakes as its parameters an object, a method of that object, and a code block. It first stores the initial value returned byobject.method, then runs the code block, and after that fetches the return value ofobject.methodagain. If the difference between those two method calls is not exactly the value of thedifference parameter (1by default), the test will fail.assert_no_differenceis a convenience method that callsassert_differencewith thedifferencevalue of zero.

Now, instead of explicitly checking the number of authors before and after the request, we enclose it inside anassert_differencecall. And because the default value of thedifferenceparameter is1,assert_differencepasses if, and only if, the code inside its code block increments the count of authors by one. It doesn’t matter what the value was before, so we’re not relying on theauthorstable being empty at the start anymore.

Running the test obviously fails, so let’s move on to implement the author creation. We have an emptycreateaction inauthor_controller.rb, so we can go ahead and fill it in.

Thecreateaction is a typical example of how actions that modify data work in Rails. First, we create a newAuthorobject from the request parameters sent from the form on thenewpage. Then we try to save the object. If the object was valid and could thus be saved, we create a flash message to be shown to the user and redirect to a page that lists all the authors. If@authorcouldn’t be saved—probably because it didn’t pass the validations (we’ll talk more about validations in the next section)—we render thenew.rhtmltemplate with the form again, so that the user can fill in the required fields she forgot to fill in the first time.

Running the test again, we see that it passes. But George has also advised us that every author needs to have both a first and a last name. We thus want to test that no new author is created if either of those form fields is left empty. We create another test method for this intest/functional/admin/author_controller_test.rb:

Here, we do the same thing as in the previous test, except that this time, we leave the last name out of the form post. Now, instead of a redirect, we want Rails to show us the form template again, with the fields with errors marked accordingly. We also useassert_no_differenceto make sure that the new author is not created.

When we run the test, we can see it failing. Instead of rendering the form again on an invalid form input, we are still redirected to the index page. We need a way to make the form validate only if both the first and the last name are present. In Rails, validations are done on the ActiveRecord object level.