Data Integrity w/ Validations
Published: 2024-02-14 17:25:13

### **Invalid Records History** 

>Reminder: to run the rails console, you need to run either `rails c` or `rails console` in the terminal to prompt, even though it is `pry` you cannot just run `pry` in the terminal to get it started, you will need to run the other one to make sure that all you models and tables are being queried correctly.

- As soon as you start serving data from external sources (ex. users, CSVs, or APIs) you have to start worrying about whether that data is *valid*

- If you do encounter *invalid* records and they become apart of your database, you will have to begin writing your code defensively, utilizing `if/elsif/else/end`, just to guard against the invalid data. 

- It’s best not to allow invalid data that doesn’t meet our criteria to enter our database in the first place; then, we don’t need to worry about writing lots of defensive conditionals downstream in our code

- ActiveRecord provides the feature: **validations**

- Validations are a way that we can specify constraints on what we allow in each column in a model. It is crucial that we have the `.save` method, this will allow us not to place records into the database we don't want to. Thus, returning to us `false`

- Validations are a super-useful tool that ActiveRecord provides for maintaining the integrity of our data.

### **How to Insert Validations in Models** 
**_question to address_**
let’s say we don’t want to allow any rows to enter our movies table with a blank `director_id` attribute.

**_how to fix & add validations_**
to address this problem we want to move into the `Movie` model insert a Validation inside of it. 
```
class Movie < ApplicationRecord
  validates(:director_id, presence: true)

  def director
  # ...
end
```

- The first argument to validates is a Symbol; the name of the column we are declaring constraints for.

- The second argument to validates is a Hash.

- The keys in the Hash are the constraints we are applying. There is a fixed list of constraints built-in to ActiveRecord that we can choose from. By far, the three most common are :presence, :uniqueness, and :numericality.

- the value that we are adding to either `true`, which would allow you wanting the simplest use of validation. On the other hand, you can also have a hash in there clarifying the configuration options, like whether you want want the `:numerically` validation to have maximum or minimum 

### **Testing our New Validations with Tests** 

- type these commands inot the `rails console` termnial

-  `m = Movie.new` creating a new instance of a movie

- the return value should look something like this 
```
[1] pry(main)> m = Movie.new
   (0.4ms)  SELECT sqlite_version(*)
=> #<Movie:0x00007f93ab387430
 id: nil,
 title: nil,
 year: nil,
 duration: nil,
 description: nil,
 image: nil,
 director_id: nil,
 created_at: nil,
 updated_at: nil>
```
- next, we will type `m.save`, which if done correctly should return a `false`

- we can double check that its not in there by typing `m` into the console again to see that there was no `created_at:` or `updated_at:` values inserted so we know that it was never created. 
### **The Errors Collection** 
- we can even find out *why* the object didn’t save. each ActiveRecord object has an attribute called errors, which contains an instance of ActiveModel::Errors — a collection of validation failures

- we can then type `m.errors` into our terminal and ActiveModel::Errors will prompt an output like this 
```
[4] pry(main)> m.errors
=> #<ActiveModel::Errors [#<ActiveModel::Error attribute=director_id, type=blank, options={}>]>
```

- next we can try to get the message 
```
[5] pry(main)> m.errors.messages
=> {:director_id=>["can't be blank"]}
```

- there is also a very helpful method we can place onto the error which will return a list of arrays of detailed messages on how to fix our issue on why it is not saving. 
```
[6] pry(main)> m.errors.full_messages
=> ["Director can't be blank"]
```

- later on down the road we can actually take those error messages, say in like logging in to an account or making a password and actually provide the user the string from the error to realize their mistakes and fix the issue. this is a good alternative than creating a for loop and making it so you have a ton of code. you can really be utilizing the ActiveRecord::Errors to make it so you are personally doing less code and using more resources. 

### **Helpful Methods for Validations** 

- `validates(:something_id, presence: true)` checks to see if a a specific "something" in a table has the presents of the thing it needs before saving to the table

- `validates(:something, uniqueness: true)` checks to see if no other rows have the same value in that column, very helpful for usernames and stuff where each value has to be unique to the specific id so there is no confusion.