Rails Run-Through
Tags: ruby, rails
Published: 2022-10-18 21:50:06

## **VIEW Methods**

*soon*

## **Relationships**

*soon*

## **Validations**
Validations live in the model, checks if one or many certain parameters are met, and returns the expected result if correct. Error handling is done in the case of a validation failing.

Here are some simple validations, including some ugly regex:
```ruby
class Post < ApplicationRecord
  validates :title, presence: true
  validates :content, presence: true, length: { minimum: 50 }
  validates :category, inclusion: { in: ['Ruby', 'Rails'] }
  validates :capitalized?

  def capitalized?
    if title.split.first.match(/\A[A-Z]/)
      errors.add(:title, "first letter must be capitalized")
    end
  end
end
```

These validations do the following, in order:

1. Checks to see if your post has a title
2. Validates that the content exists, as well as it has at least a length of 50
3. Checks the post's category, making sure the value isn't empty, and includes either Ruby or Rails
4. CUSTOM VALIDATION: after defining the method below the validations, this now checks to see if the title begins with a capitol letter

If any one of these validations is not met, an error will be thrown by `ActiveRecord`, and the attempted task has failed.

## **Serializers**
MAKE SURE SERIALIZER GEM IS INSTALLED
Customizes the `json` returned for a single model. This functions like an `includes` statement, where data associated with the object you're acting on gets returned along with it.

### Built-in Serializers:
In this example for `Students`, I will walk through some reasonable use-cases for serializers:

<br>

With this, whenever we send json for any instance of `Student`, this serializer will innately apply to it. The key difference between this and custom options is passing a serializer in a fetch path; Built-in doesn't need to be written elsewhere besides the model, but custom serializers must. (example later for custom)

<br>

First, create the serializer for `Students`:

```
rails g serializer students
```

**Examples:**

1) A student object contains these eight values:

```ruby
create_table "students", force: :cascade do |t|
    t.string "name"
    t.integer "age"
    t.string "race"
    t.integer "grade"
    t.string "best-friend"
    t.integer "teacher_id"
end
```
Now, say we don't want all of those values, and instead only want the `student`'s name and age (we definitely don't need an id if this is a `SHOW` path, unless we're searching by a value other than id). With a serializer, we can pass those two values as attributes
```ruby
class StudentSerializer < ActiveModel::Serializer
  attributes :name, :age

  has_many :books
  belongs_to :teacher
end
```
I also went a step further to include the relationships, which is another aspect of serializers, where they automatically, in pair with `ActiveRecord`, find associated values with foreign keys. Here, in a `SHOW` request for a specific `student`, we are grabbing all books that have the `student`'s foreign key (owned by the `student`), as well as the one `teacher` the `student` belongs to, and returning them in the `JSON` response. A request as such would return something like this:

```json
{
  "name": "Frederick",
  "age": 21,
  "books": [
    {
      "title": "East of Eden",
      "author": "John Steinbeck"
    },
    {
      "title": "The House Of Mirth",
      "author": "Edith Wharton"
    }
  ],
  "teacher": {
    "name": "Mr. Teacher",
    "class": "Math"
  }
}
```
Here, though, the returned `json` for `Teacher` and `Books` holds their entire object, what if we want to serialize it like we did with `Student`? We can! Adding a serializer to either class, the same as we did for `student`, will innately apply it to any instance of `Teacher` or `Books`, even when pulled with `Student` again. Here is an example serializer for books:

```ruby
class BookSerializer < ActiveModel::Serializer
  attributes :title

  belongs_to :student
end
```

Now, if we do that same request for a single `student`, this will be returned:

```json
{
  "name": "Frederick",
  "age": 21,
  "books": [
    {
      "title": "East of Eden"
    },
    {
      "title": "The House Of Mirth"
    }
  ],
  "teacher": {
    "name": "Mr. Teacher",
    "class": "Math"
  }
}
```
### Custom Serializers:

Say we want the same return from a `student`, but we want the `teacher` to only include their name. We could technically do it this way:

```ruby
class TeacherSerializer < ActiveModel::Serializer
  attributes :name

  has_many :students
end
```
But this isn't what we want. When we call an `SHOW` method on `teacher` itself now, it will only return this:

```json
{
  "name": "Mr. Teacher"
}
```
But what if we wanted to return the whole `teacher` object? What if we want to only have the `teacher` return their name when we fetch a `student`, but continue returning the whole object when we just want to fetch a `teacher`? We can use a custom serializer, then pass is IN `student`, where the relationship lives.

First we generate the serializer:

```
rails g serializer student_teacher
```
Then, we can populate it with some code:

```ruby
class StudentTeacherSerializer < ActiveModel::Serializer
  attributes :name

  has_many :students
end
```
Then, if we go to the SHOW method for `Student` we can add it:
```ruby
def show
  render json: find_student, serializer: StudentTeacherSerializer
end
```

Now, without a serializer for `teacher` (no more `TeacherSerializer`), when we call a `SHOW` request on `student` we get this:

```json
{
  "name": "Frederick",
  "age": 21,
  "books": [
    {
      "title": "East of Eden"
    },
    {
      "title": "The House Of Mirth"
    }
  ],
  "teacher": {
    "name": "Mr. Teacher"
  }
}
```
WOW. Only the `teacher`'s name! Nice.

## **Error handling**

Here, we will be using `ActiveRecord`'s error-catching, and setting two general errors to handle, along with object-specific `json` returns, using the error object. There'll also be a custom error at the end.

First, we can look at what we need to error handle in the routes we've made for a model:

For `SHOW` or `UPDATE`, both of which are looking for instance of an object, we want to return an error when the search parameter doesn't match any existing instance. We can use `ActiveRecord`'s `rescue`, or `rescue_from` method to access the "record" value within the error object when it's thrown. Starting with the `RecordNotFound` error, we can add handling at the top of the controller, and then define what's returned in our private methods:

```ruby
class StudentsController < ApplicationController
  rescue_from ActiveRecord::RecordNotFound, with: :render_not_found_response
  #code...
  def show
    student = find_student
    render json: student
  end

  private

  def render_not_found_response
    render json: { error: "Student not found" }, status: :not_found
  end
  #code...
end
```
Now

Then, there is handling a `RecordInvalid`, which can be thrown when you are trying to pass an updated or new instance during a `CREATE` or `UPDATE`. Here is how that works:

```ruby


def update
  post = post.create!(post_params)
  render json: post
end
```

##Minimizing Code

*soon*