Rails Intro::Mandalorian
Tags: rails, ruby, activerecord, formbuilder
Published: 2020-12-12 21:42:29

Happy *belated* Thanksgiving 🦃 and Merry *almost* Christmas 🎄, fellow devs! 

I still can not believe that I am officially on Module 3! So glad that I passed my [Sinatra x Street Fighter](https://dev.to/codinghall/sinatra-x-street-fighter-1hgl) project assessment, and enjoyed some downtime over Thanksgiving weekend. 

The past few weeks of my first exposure to Rails just blew my mind... [David H. Hansson](https://en.wikipedia.org/wiki/David_Heinemeier_Hansson) created this magic of Rails, eliminating a bunch of repetitive Sinatra configuration. **Ruby on Rails** is a web framework with tremendous open source contributions, and provides developers building applications with strong base frame functionality. Rails' architecture still relies upon the **MVC (Model-View-Controller)** paradigm. The *separation of concerns* governs the overall Rails file directory structure. Once the command `rails new <application_name>` is prompted, new directories emerge including `app`, `config`, `db`, `lib`, `Gemfile` and so on. [You may review each directory and its functionality here](https://guides.rubyonrails.org/getting_started.html#creating-the-blog-application). `rails s` is a basic command line in order to start the *Rails server*, while `rails c` provides *Rails console* session (similar to `rake console`). One main characteristic from Rails I have learned thus far would be its ***convention over configuration***. This pattern is inherent in the Rails routing system, file naming conventions and/or data flow structure. 

My case study focuses mainly on **CRUD (Create, Read, Update and Delete)** Rails implementation, and it will be in reference to one of my favorite shows, **The Mandalorian**. *"This is The Way."* Season 2 is extraordinary! Kudos to [Jon Favreau](https://en.wikipedia.org/wiki/Jon_Favreau) and our beloved Grogu. 

![https://media1.tenor.com/images/30523f960d5d67c0ef26283f441585f5/tenor.gif?itemid=19358531](https://media1.tenor.com/images/30523f960d5d67c0ef26283f441585f5/tenor.gif?itemid=19358531)

### **Rails Generators**

In Sinatra, we manually build many standard features. Rails, on the other hand, provides a more efficient way when building core application functionality. This exercise alone prevents spelling and syntax errors following **RESTful (Representational State Transfer)** naming patterns. I adore the `rails generate` (or `rails g`) command line. It automatically populates our basic Rails app framework. There are a few [**Rails generators**](https://api.rubyonrails.org/classes/Rails/Generators.html) including *migration, model, controller, resource and scaffold*. Each generator is capable of creating a large number of files and codes. This case study will utilize my favorite one, **resource generator**. It creates some of the basic core functionality without code overflow. Let's create our first `resource`, **Mandalorian**, on the terminal.

```
> rails g resource Mandalorian name:string spaceship:string companion:string --no-test-framework
```
The `--no-test-framework flag` is being utilized as my case study will not dive in to any testing frameworks. The above command line gives us the following list:
```
> invoke  active_record
> create    db/migrate/20201201211632_create_mandalorians.rb
> create    app/models/mandalorian.rb
> invoke  controller
> create    app/controllers/mandalorians_controller.rb
> invoke    erb
> create      app/views/mandalorians
> invoke    helper
> create      app/helpers/mandalorians_helper.rb
> invoke    assets
> invoke      scss
> create        app/assets/stylesheets/mandalorians.scss
> invoke  resource_route
> route    resources :mandalorians
```
Isn't Rails magic?!
Let's take a peek at some of these files.
```ruby
# db/migrate/20201201211632_create_mandalorians.rb

class CreateMandalorians < ActiveRecord::Migration[6.0]
  def change
    create_table :mandalorians do |t|
      t.string :name
      t.string :spaceship
      t.string :companion

      t.timestamps
    end
  end
end
```
We have a new database table `mandalorians` created under folder `db/migrate` along with the assigned string attributes, `spaceship` and `companion`.
```ruby
# app/models/application_record.rb

class ApplicationRecord < ActiveRecord::Base
  self.abstract_class = true
end


# app/models/mandalorian.rb

class Mandalorian < ApplicationRecord
end


# app/controllers/mandalorians_controller.rb

class MandaloriansController < ApplicationController
end 
```
`Mandalorian` model directly inherits the Active Record values from `ApplicationRecord`. The  `MandaloriansController` currently does not have any single actions. *Resource generator* allows flexibility when adding *necessary features*.
```ruby
# config/routes.rb

Rails.application.routes.draw do
  resources :mandalorians
end
```
A full `resources` call currently resides in the `config/routes.rb` file. `resources :mandalorians` embodies the conventional RESTful routes in order to perform CRUD functions: `index`, `new`, `create`, `show`, `edit`, `update` and `destroy`. These routes will be adjusted as we progress on this case study.

I will create our second `resource`, **Armor** in order to establish basic object relationships, `has_many` and `belongs_to`. Similar Rails file structure will be created upon instantiation.
```
> rails g resource Armor name:string description:string --no-test-framework
```
### **Models**

The models are still inheriting from the `ActiveRecord::Base` class. **Active Record** is a strong tool when implementing logic. It provides **ORM (Object Relational Mapping)** meta-programming methods built into the models entity and associations. Model files may contain object relationships, validations, callbacks, custom scopes and/or others.  

![Alt Text](https://dev-to-uploads.s3.amazonaws.com/i/20lvgmimqz9sfaj4zig8.png)

Active Record associations come into play especially when creating more complex data relationships. I would have to adjust both models, `Mandalorian` and `Armor`, along with the database migration. In the universe of my case study, let's assume that a mandalorian `has_many` armors, and each armor `belongs_to` a mandalorian. We understand the `armors` table should include a `foreign key` column, a reference to a primary key of the associated mandalorian.

```ruby
# app/models/mandalorian.rb

class Mandalorian < ApplicationRecord
    has_many :armors
    validates :name, presence: true, uniqueness: true 
end


# app/models/armor.rb

class Armor < ApplicationRecord
    belongs_to :mandalorian
    validates :description, length: { maximum: 50 }
end


# db/schema.rb

ActiveRecord::Schema.define(version: 2020_12_01_221427) do
  create_table "mandalorians", force: :cascade do |t|
    t.string "name"
    t.string "spaceship"
    t.string "companion"
    t.datetime "created_at", precision: 6, null: false
    t.datetime "updated_at", precision: 6, null: false
  end
	create_table "armors", force: :cascade do |t|
    t.string "name"
    t.string "description"
    t.integer "mandalorian_id"
    t.datetime "created_at", precision: 6, null: false
    t.datetime "updated_at", precision: 6, null: false
  end
end
```
**Active Record validations** are useful in protecting the database from invalid data inputs. `validates` method takes two arguments, `:name` of the attribute requiring validation, and a hash of options to detail out how the validation works. `{ presence: true }` prevents the `:name` attribute from being empty. `{ uniqueness: true }` does not allow identical `:name` data input. 

It is always a good practice to test our models post migration (or, `rails db:migrate`). Let's use my second favorite Rails console command line, `rails c`.
```ruby
001 > mando = Mandalorian.create(name: "Din Djarin", spaceship: "Razor Crest", companion: "Grogu") 
=> #<Mandalorian id: 1, name: "Din Djarin", spaceship: "Razor Crest", companion: "Grogu", created_at: "2020-12-02 01:11:33.083593", updated_at: "2020-12-02 01:11:33.083593">

002 > flamethrowers = mando.armors.build(name: "Flamethrowers", description: "Fire streams or bursts of fire")
=> #<Armor id: nil, name: "Flamethrowers", description: "Fire streams or bursts of fire", mandalorian_id: 1, created_at: nil, updated_at: nil>

003 > flamethrowers.save 
=> [["name", "Flamethrowers"], ["description", "Fire streams or bursts of fire"], ["mandalorian_id", 1], ["created_at", "2020-12-02 01:20:57.923623"], ["updated_at", "2020-12-02 01:20:57.923623"]]
```
I am able to `create` mando from Mandalorian class, with its spaceship and companion attributes. `build` method is similar to `new` method. `mando` obviously has many `armors`, and `build` method is one of the association macros. Once `flamethrowers` saved, Active Record automatically assigns it to `mando`, or equivalently, `mandalorian_id = 1`. Terrific! Our object relationships work. 

There are a lot of more [class methods in Rails application](https://api.rubyonrails.org/classes/ActiveRecord/Associations/ClassMethods.html) aside from my rudimentary case study such as `has_many :through`, `has_one` and many more.

### **Controllers**

Controllers essentially connect the models, views and routes. They inherit a number of methods built into the Rails controller system through `ApplicationController`. 

![https://fastly.syfy.com/sites/syfy/files/styles/1400xauto/public/baby-yoda-carry.gif](https://fastly.syfy.com/sites/syfy/files/styles/1400xauto/public/baby-yoda-carry.gif)

Assuming following conventional mappings of full `resources`, the `MandaloriansController` has HTTP verbs, methods, paths and descriptions as follows:

**HTTP verb** | **Method** | **Path** | **Description**
--- | --- | --- | ---
`GET` | index | */mandalorians* | show all mandalorians
`POST` | create | */mandalorians* | create a new mandalorian
`GET` | new | */mandalorians/new* | render the form for new creation
`GET` | edit | */mandalorians/:id/edit* | render the form for editing
`GET` | show | */mandalorians/:id* | show a single mandalorian
`PATCH` | update | */mandalorians/:id* | update a new mandalorian
`DELETE` | destroy | */mandalorians/:id* | delete a mandalorian

The `ArmorsController` has similar mappings as above. 

These controller actions (or methods) will perform CRUD functions and render views to the end user. The `/:id` on the url paths accepts `params[:id]` passed to the route. This `dynamic routing system` allows our controller methods to receive params input.

```ruby
# config/routes.rb

Rails.application.routes.draw do
  resources :mandalorians, only: [:new, :show, :edit]
  # get '/mandalorians', to: 'mandalorians#new'
  # post '/mandalorians', to: 'mandalorians#create'
  # get '/mandalorians/:id', to: 'mandalorians#show'
  # get '/mandalorians/:id/edit', to: 'mandalorians#edit'
  # patch '/mandalorians/:id', to: 'mandalorians#update'
  resources :armors
end
```

In Sinatra application, the actions' routes reside in the controller. Rails assigns routes in the `config/routes.rb` by default, and elaborate the actions as methods in the controllers' files. `resources :mandalorians, only: [:new, :show, :edit]` strictly only allows new, show and edit routing system. Note: I revised previously full path `resources` down to three `methods`. On the other hand, `resources :armors` provides a full set of routing systems; `index`, `new`, `create`, `show`, `edit`, `update` and `destroy`. 

Since validations were implemented in the models, the controllers require slight modifications to the `create` and `update` actions. Ideally if validations were to fail, the form needs to be re-rendered. For example, the `:new` form params input fails the Active Record validation. `:new` template would have to be re-rendered. Similar to the `:edit` form.

```ruby
# app/controllers/mandalorians_controller.rb

class MandaloriansController < ApplicationController
    before_action :set_mandalorian, only: [:show, :edit, :update]

    def new 
        @mandalorian = Mandalorian.new 
    end 

    def create 
        @mandalorian = Mandalorian.new(mandalorian_params)
        if @mandalorian.save 
            redirect_to @mandalorian
        else 
            render :new 
        end 
    end

		def show
		end 

    def edit 
    end 

    def update 
        if @mandalorian.update(mandalorian_params)
            redirect_to @mandalorian 
        else  
            render :edit 
        end 
    end 

    private 

    def set_mandalorian 
        @mandalorian = Mandalorian.find(params[:id])
    end 

    def mandalorian_params
        params.require(:mandalorian).permit(:name, :spaceship, :companion)
    end 
end
```

`before_action :set_mandalorian` helps in keeping our code **DRY (Don't Repeat Yourself)**. It triggers the `set_mandalorian` function at the instantiation of `show`, `edit` and `update` actions. Each action requires an object instance of Mandalorian class through `.find(params[:id])`.

**[URL or Route Helpers](https://api.rubyonrails.org/classes/ActionView/Helpers/UrlHelper.html)** are another level of abstraction in Rails in order to avoid hard-coded paths. You can implement these route helpers on Controllers and Views files, but not in Models. It also provides more assistance to readability. For example, `"mandalorian_path(@mandalorian)"` would be the *route helper* version of `"/mandalorians/#{@mandalorian.id}"`. `ActionView`, a sub-gem of Rails, helps us in providing these numerous helper methods. In fact, let's implement another level of abstraction. `@mandalorian` is applicable and a more succinct version of `"mandalorian_path(@mandalorian)"`. 

The goal of **strong params** is to whitelist the parameters received. In the `mandalorian_params` function, the `require` method **must** require a key called `:mandalorian`. The `permit` method allows more flexibility in key attributes, and is especially useful when assigning mass attributes.  

On a side note, I found `raise params.inspect` as a great way to print `params` when working in between both controllers and views. 

### **Views**

View files should have the least amount of logic, and their solid purpose is to render views for the end user. In this exercise, we will only focus on **implicit rendering** following Rails convention. My application instinctively seeks out view files carrying the same name as the controller actions. Each action from the controller corresponds only to its respective view. For example, `new` action only communicates with `new.html.erb`. Any `instance variables` from `new` action only transcribes to `new.html.erb`.

![Alt Text](https://dev-to-uploads.s3.amazonaws.com/i/clozw3j514ld7d84ucc2.png)

Rails forms allow the end user to submit data into form fields. I will directly implement the forms using *Rails Route Helpers* in lieu of plain HTML format, and `form_for` in lieu of `form_tag`. [form_for](https://api.rubyonrails.org/classes/ActionView/Helpers/FormHelper.html) yields [FormBuilder](https://api.rubyonrails.org/classes/ActionView/Helpers/FormBuilder.html) object, and is full of convenient features. It is best implemented when forms are directly connected with the models, in performing CRUD functions. However, it is also crucial to understand the rudimentary [application of `form_tag` and its helper methods](https://api.rubyonrails.org/classes/ActionView/Helpers/FormTagHelper.html).

`form_authenticity_token` helper is a part of Rails to combat **CSRF (Cross-Site Request Forgery)**. CSRF general flow is one site implementing a request to another site without end user's consent. Fun fact, this `hidden_field_tag` is a redundancy as Rails forms by default generates a required authenticity token. It reminds me of Sinatra manual implementation of assigning `session[:user_id] = @user.id`.

```html
# app/views/mandalorians/_form.html.erb 

<%= form_for @armor do |f| %>
    <% if @armor.errors.any? %>
        <div id="error_explanation">
            <h2>
                <%= pluralize(@armor.errors.count, 'error') %> 
                prohibited this armor from being saved:
            </h2>
            <ul>
                <% @armor.errors.full_messages.each do |message| %>
                    <li><%= message %></li>
                <% end %>
            </ul>
        </div>
    <% end %>

    <%= f.label "Name:"%>
    <%= f.text_field :name %><br>

    <%= f.label "Description:" %>
    <%= f.text_field :description %><br>

    <%= hidden_field_tag :authenticity_token, form_authenticity_token %>

    <%= f.submit %>
<% end %>

# app/views/mandalorians/new.html.erb

<h3>New Form</h3>
<%= render 'form' %>
<%= link_to 'Back', armors_path %> 

# app/views/mandalorians/edit.html.erb

<h3>Edit Form</h3>
<%= render 'form' %>
<%= link_to 'Show', @armor %>
<%= link_to 'Back', armors_path %>
```

Since both `:new` and `:edit` are almost identical, I created `_form.html.erb` where `form_for` codes reside. `new.html.erb` and `edit.html.erb` render files to retrieve codes from `_form.html.erb`. `form_for` automatically sets up the path where the form will be sent.

`errors.full_messages` is optional, but [displaying validation errors in views](https://guides.rubyonrails.org/active_record_validations.html#displaying-validation-errors-in-views) has proved to enhance user experience. Being able to notify users of input errors creates stability in the database. It is most prudent for views to display errors to the user. When the form (either `:new` or `:edit`) is re-rendered, pre-filling forms with existing user inputs promotes better user experience. None of us wants the hassle of re-typing or re-filling pre-populated forms. `form_for` automatically **pre-populates** form reload with pre-existing values of `@mandalorian` data.

![Alt Text](https://dev-to-uploads.s3.amazonaws.com/i/c1c7pi2fs35zb0u0d3z2.png)

In the above example, the view renders an edit form and once reloaded, the model executes an error message that `Name has already been taken` from the Active Record validation, `validates :name, uniqueness: true`. `form_for` realizes that the current `@mandalorian` is *not a new instance* of the Mandalorian class.  

I would hate to delete any of our bounty hunters, let's try to implement the `delete` request on our Armor model. HTML5 forms officially do not support `delete` and `patch` methods. We place the `delete` request in the `armors_controller`, and render the `delete` button on `show.html.erb`. Once deleted, the end user will be routed to armors index view.

```ruby 
# app/controllers/armors_controller.rb

def destroy 
    @armor = Armor.find(params[:id])
    @armor.destroy 
    redirect_to armors_path
end
```
```html
# app/views/armors/show.html.erb 

<p>Name: <%= @armor.name %></p>
<p>Description: <%= @armor.description %></p>
<p>Mandalorian: <%= @armor.mandalorian.name %></p>

<%= button_to 'Delete', @armor, method: :delete %>
```

[button_to](https://api.rubyonrails.org/v5.1.7/classes/ActionView/Helpers/UrlHelper.html#method-i-button_to) is a better version of `link_to` when sending a `delete` request. 
  
In the previous Rails console, we assured our object relationships work. Similar model associations can be implemented in the view files. Since each armor `belongs_to` a mandalorian, we should be able to call out `@armor.mandalorian.name` method. I repeat. Isn't Rails magic?!

![https://media1.tenor.com/images/614de537ea516c274f9c95e722fbf9d5/tenor.gif?itemid=15630699](https://media1.tenor.com/images/614de537ea516c274f9c95e722fbf9d5/tenor.gif?itemid=15630699)

> This is The Way.
> The Mandalorian

***
<center>[**fentybit**](https://fentybit.me/) | [GitHub](https://github.com/fentybit) | [Twitter](https://twitter.com/fentybit) | [LinkedIn](https://www.linkedin.com/in/fentybit/)</center>