Chapter 5. Validating your Data: Preventing mistakes

image with no caption

Everyone makes mistakes... but many of them are preventable! Even with the very best of intentions, your users will still enter bad data into your web app, leaving you to deal with the consequences. But just imagine if there was some way of preventing mistakes from happening in the first place. That’s where validators come in. Keep reading, and we’ll show you how to add clever Rails validation to your web app so that you can take control of what data is allowed in—and what needs to be kept out.

Watch out—there’s bad data in the room

Everything seemed to be going so well with the personal trainers’ web app, at least until the body builders showed up. The body builders say they’ve paid their gym dues, and have the receipts to prove it, but their payments aren’t showing up on the system.

image with no caption

So what went wrong?

Clicking on the save button should have saved the data into the database, but something went wrong. Instead of saving the amount paid as $50, the amount got saved as 0.0. But how? Let’s take a look at the chain of events that occurred.

  1. A trainer enters “$50” into the paid field in the view, and then clicks on the save button. A value of “$50” is passed to the controller.

  2. The controller receives a value of “$50” for the amount paid, and it passes this along to the model.

  3. The model receives the value “$50,” but there’s a problem. Since the value contains a $ symbol, the model can’t convert it to a number. It saves the value 0.0 to the database instead.

image with no caption

The problem was caused by the trainer entering the wrong sort of data in the web page, and we need to prevent this happening again. We need to write code to validate the form data before it’s written to the database—but where should validation code go?

image with no caption

Mark: Why does it matter where the validation is?

Bob: We fix the problem where it happens. The error happens in the form, so that’s where it should be fixed.

Mark: No - look. Just put a check in the controller. The controller calls the save and update methods. We can just make the controller able to decide whether or not to save the object.

Bob: But the problem occurs in the form.

Laura: I’m not sure. Isn’t the problem really in the model?

Mark: The model’s just data. Most of the code we write is in the controller.

Laura: I thought the model was more than just data. Can’t we put code in there, too?

Mark: But the clever logic is all in the controller.

Bob: No - it’s easier than that. You just put a JavaScript check in the form’s markup.

Mark: What if the user’s switched JavaScript off in their browser?

Bob: Hey, come on, nobody switches JavaScript off anymore.

Laura: But how can you rely on that? Particularly when it’s obvious where the code should go.

Mark: The controller.

Laura: The model.

Validation code goes in the MODEL

The trouble with putting validation code in the view or the controller is that two separate bits of code might try to save values to the database. If we have insert and edit methods in the controller, for instance, both of these need validation. If the validation is centralized in the model, it doesn’t matter how data gets stored—validation on that data will still occur.

image with no caption

In general, it’s a good idea to validate in the model. And after all, that’s one of the reasons that we have a model layer. The model isn’t just data. The reason we wrap the database in a layer of code is so that we can add the kind of smarts—like validation—that a database on its own doesn’t provide. So how exactly do we add validation to the model?

Rails uses validators for simple validation

Every system needs to perform some kind of check on the data that gets entered into it, and sometimes the checking code can be long and complicated.

So what can Rails do to help? After all, the checks you need to make are pretty customized, aren’t they? Well - yes and no. The set of validation rules for your data will probably be unique to your system. But the individual rules themselves will probably be checking for a small set of typical errors, like missing data, or data in the wrong format, or data of the wrong type.

That’s why Rails comes with a set of built-in standard checks called validators. A validator is a Ruby object that looks at the data people have entered and performs a simple check on it. When does it do the check? Whenever someone tries to save or update the data in the database.

image with no caption

Validators are a quick and effective way of improving the quality of your data. They will help you filter what is and isn’t allowed into your database. And in the cases where the data is bad, they will even provide a set of error messages to help the user diagnose what went wrong.

But how do validators work?

So how do validators work?

Let’s follow a ClientWorkout as it goes through a validation sequence.

  1. The user submits the details of a ClientWorkout.

    The problem is, the paid_amount field contains “$50” rather than “50”, and “$50” can’t be converted to a numeric value.

    image with no caption
  2. The controller converts the form data into a ClientWorkout model object.

    The model object stores a copy of the form data, and it uses that to generate the values of its attributes. If you ask the object for the value of its paid_amount attribute, it tries to convert the “$50” to a number, but can’t, so the controller says the paid_amount is 0.0.

    image with no caption
  3. The controller tries to save the object.

    The controller asks the model object to save itself. Ordinarily the object would save a record of itself to the database with a paid_amount value of 0.0. But if there’s a validator on the model, then things are a little different...

    image with no caption
  4. The model object runs its validator(s).

    When the model object is asked to insert or update a record to the database, it first runs its validators. The validators check the values in the underlying form data stored inside the ClientWorkout object. Our paid_amount validator records an error because “$50” is not numeric.

    image with no caption
  5. The model object decides whether it’s OK to save the record.

    Only after the validator has run will the model object decide if it can save the record to the database. How does it decide? It looks to see if any errors have been created. The paid_amount validator failed so the model skips saving a record and tells the controller that something went wrong.

    image with no caption
  6. The controller returns the user to the form.

    The code in the controller knows that something went wrong, so it returns the user to the form page so that the errors can be corrected.

    image with no caption

Let’s check if something is a number

We’ll check the paid_amount field with a validator called validates_numericality_of. The validator will belong to a model object, so we need to add it to the model code in client_workout.rb:

image with no caption

This will create an instance of the validator for each ClientWorkout object.

The validator needs the name of the attribute it’s going to check. Like pretty much all names in Ruby, the attribute name is given as a symbol: :paid_amount.

Remember that a symbol is a little like a string. Symbols always begin with a colon (:) and are generally used to refer to the names of things, like the names of fields and attributes.

So how will this all actually work in our app? Let’s say the controller has a ClientWorkout model object called @client_workout.

Whenever the controller calls @client_workout.save or @client_workout.update_attributes, the model object will run the validates_numericality_of validator.

If the original form data inside the model object has “$50” recorded against the paid_amount field, the validator will generate an error. Rails will spot the error and abort the database update.

That’s the theory. Let’s see if this all works.

Users have been leaving out data on their workout forms

Some people are have been leaving fields blank. For example, one of the trainers has been forgetting to enter his own name on some of the workouts he’s entered. Later on, when he searches for all of his own workouts, he can’t find the ones where he left his name off.

image with no caption

And not only does the trainer’s name need to completed, but the client’s name as well. Lenny Goldberg had a couple of sessions where his name wasn’t recorded. Lenny normally gets billed at the end of the month, so when they searched for his training sessions to find which ones didn’t have payments, they couldn’t find them. The personal trainers just can’t afford for this to happen!

So can validators help?

So far, we’ve only used a validator to check and see if an input value is numeric. But there’s a whole family of validators that can do anything from checking that a value is in a list, to whether a value is unique in a particular column in a table.

How do we check for mandatory fields?

There’s a validator we can use to check for values in mandatory fields. The validator that does this is validates_presence_of:

image with no caption

Here’s the code with the validators in place:

image with no caption

Validators are simple and work well

The data quality is now much higher, and workout data has stopped mysteriously vanishing. The accountant is happy because she has a record of all the clients who haven’t paid and the body builders no longer have to worry about their payments going missing.

image with no caption

Everything was going so well until...

image with no caption

Something strange has happened at MeBay

The folks at MeBay heard about your work with validators so they tried adding them to the code you’d written for them.

But they didn’t get such a good result...

image with no caption

All MeBay did was add validators to check that all the fields on new adds were completed, and that the numeric and email fields were correctly formatted.

Let’s look at what’s happening in more detail...

The validators work, but they don’t display errors

Someone entered an ad with a blank price to see what’s wrong with the validators.

image with no caption

If you build your own pages, you need to write your own error message code

When you scaffold part of an application, Rails generates the code you need to handle errors. But if you are creating code manually, you’re pretty much on your own. So we need to change the MeBay code to handle errors.

The code will need to do two things:

  1. If an error occurs, the system needs to redisplay the page where the error occurred.

    image with no caption
  2. The form page will need to display all of the errors that were generated by the validators.

    image with no caption

So what’s the first thing the application needs to do?

The controller needs to know if there was an error

If the user enters bad data into a form, Rails needs to send the user back to the form with the error. Page flow like this is handled by the controller. Remember that the controller is in charge of what data is read and written and which pages are displayed.

What does the controller code need to do to handle errors in the MeBay application? Here’s what the app currently does when a new ad is submitted:

image with no caption

The code will always do the same thing—try to save the ad to the database and then go to a page to display the data. It doesn’t currently matter if the save fails... and that’s a big problem

But how do we tell if the save method has failed? Well, in Ruby every command has a return value. If there’s a problem saving an ad, the @ad.save command will return false. We can use the return value of @ad.save to determine whether we should redisplay the page... or display the saved ad.

image with no caption

But to do that we need to learn just a little more Ruby...

We still need to display error messages!

It’s good that the app redisplays the form—but then what?

To fix form problems, a user needs to know what went wrong. They need error messages that show them:

  1. Which fields had a problem

  2. Exactly what those problems were

Every time one of the validators fails, it stores an error message in the model. But we want to display the error messages in the view. That means the messages need to be transferred from the model to the view.

Which part of the interface is tightly bound to a model object?

The form! And the form object has a special method that can generate an error block. That method is called error_messages:

image with no caption

The MeBay system is looking pretty sweet

Now that the system is reporting errors correctly, the folks at MeBay are adding more and more validators. The controller checks for the errors and reports back any problems. Before long the data in the system has really great quality, and the number of errors drops dramatically.

image with no caption

There’s just one more thing to do. The validators prevent any major data problems, but the errors are only displayed when a new ad is posted.

But errors still aren’t reported when ads are edited ...

Tools for your Rails Toolbox

You’ve got Chapter 5 under your belt, and now you’ve added the ability to use validators.

Rails Tools

validates_length_of :field1, :maximum=>32 checks the field is no longer than 32 characters

validates_format_of :field1, :with=>/regular expression/ checks that the field matches the regular expression

validates_uniqueness_of :field1 checks that no other record in the table has the same value for field1

validates_inclusion_of :field1, :in=>[val1, val2, ..., valn] checks that the field has one of the given values

f.error_messages displays errors within a form

The save and update_attributes methods on model objects return true if they work, and false if they don’t

render :template=>“a/template” renders output using the app/views/a/template.html.erb file

render :action=>’new’ renders the template for the new action

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset
18.232.125.188