Sinatra   x   Street Fighter
Tags: ruby, rubysinatra, mvc, crud
Published: 2020-11-12 00:52:48

The time has come for my Module 2 Sinatra Project. Dun dun dun! 

While my previous case study on [Lakers < ActiveRecord::Base](https://dev.to/codinghall/lakers-activerecord-base-2nj9) solely focused on the Model from **MVC (Model-View-Controller)**, this Sinatra capstone project will encapsulate the whole MVC paradigm. One of the major concepts in MVC is to assign *separation of concerns* in Models, Views and Controllers. **Models** encapsulate the logic behind our application. **Views** are the user's interface, and implementation may utilize various platform from HTML, CSS, forms to ERB (Embedded Ruby). **Controllers** are the mediator in between our Models and Views. Each entity has its own *single responsibility*.

### **What is Sinatra?**

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

[Sinatra](http://sinatrarb.com/) is a Domain Specific Language, or DSL. 

Sinatra is a light-weight alternative to Ruby on Rails application framework, and of course, named after the infamous musician [Frank Sinatra](https://en.wikipedia.org/wiki/Frank_Sinatra). It allows the creation of a simple app development in Ruby with minimal effort. However, understanding Sinatra is a great catalyst to Rails' massive framework application.

### **Ideation**

Flatiron School curriculum requires our cohort to choose a domain we care about and are familiar with. I was a gamer back in the days. I remember religiously playing PS1 on mostly RPGs (*FF series, Chrono Cross, Parasite Eve, Xenogears, Suikoden.. I can go on.. and on.. and on...*) as well as fighting games. I was glad we were then moving on to PS2 and XBox One. Eventually, technology allows us to exercise social distancing from our monitor screen and fully decommissioned this old-school wired controller. *Ahhh.. so nostalgic!*

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

After a few days contemplating a domain that I would love to explore more in depth, I have decided to adopt Street Fighter as my domain modeling. 

### **Domain and MVP**

In my Street Fighter domain modeling universe, the Minimum Viable Product (MVP) I was aiming for this capstone project would be to allow a user to select a character and perform character's fighting moves on multiple stage platforms. 

The extended function I am aiming for would be to save a user's session when signing up or logging in, and user's ability to record multiple selections of characters with various fighting moves and stage platforms. This is my preliminary Entity Relationship Diagram (ERD), and overall model relationships.

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

`user` has_many `:characters`

`character` belongs_to `:user`
`character` has_many `:moves`
`character` has_many `:stages`, through: `:moves`

`stage` has_and_belongs_to_many `:moves`
`stage` has_many `:characters`, through: `:moves`

`move` belongs_to `:character`
`move` has_and_belongs_to_many `:stages`

### **Back End::ORMs and ActiveRecord Migration Unit**

Major thanks to [Ruby gem Corneal](https://github.com/thebrianemory/corneal) in scaffolding basic initial setup. It automatically populates my app folder with models, controllers and views. Upon instantiation with `corneal new APP-NAME` in the terminal and `bundle install`, I have my basic Sinatra file structure setup. 

The Object Relational Mapping (ORM) concept in *vanilla ActiveRecord model classes and associations* are defined as follows:

```ruby
class User < ActiveRecord::Base
    has_secure_password
    
    validates_presence_of :username, :email, :password 
    validates_uniqueness_of :username, :email

    has_many :characters
end
```
```ruby
class Character < ActiveRecord::Base 
    belongs_to :user 
    has_many :moves 
    has_many :stages, through: :moves 

    def slug 
        self.name.downcase.split.join("-")
    end 

    def self.find_by_slug(slug)
        self.all.find {|character| character.slug == slug}
    end 
end
```
```ruby
class Stage < ActiveRecord::Base 
		validates_uniqueness_of :name

    has_and_belongs_to_many :moves
    has_many :characters, through: :moves  
end
```
```ruby
class Move < ActiveRecord::Base 
    validates_uniqueness_of :name

    belongs_to :character 
    has_and_belongs_to_many :stages
end
```
The `ActiveRecord` gem allows us to create a mapping between our model and database. Each migration is recorded by ActiveRecord in the database, eliminating manual execution and any duplication errors. Classes inheriting from `ActiveRecord::Base` carry meta-programming methods to associate the models and their respective database tables. The AR macros of `has_many`, `belongs_to` and `has_and_belongs_to_many` further define model associations. 

When architecting a new application, it is a good practice to start bottom up; starting from setting up the database. Once `rake db:migrate` triggered, `schema.rb` file will be auto-generated and it displays the current state of database migration.
```ruby
ActiveRecord::Schema.define(version: 2020_10_31_224114) do

  create_table "users", force: :cascade do |t|
    t.string "username", default: "username"
    t.string "email", default: "email"
    t.string "password_digest", default: "password"
  end

  create_table "characters", force: :cascade do |t|
    t.string "name"
    t.string "quote"
    t.text "bio"
    t.string "image"
    t.string "video"
    t.integer "user_id"
  end

  create_table "moves", force: :cascade do |t|
    t.string "name"
    t.integer "character_id"
  end

  create_table "moves_stages", force: :cascade do |t|
    t.integer "stage_id"
    t.integer "move_id"
  end

  create_table "stages", force: :cascade do |t|
    t.string "name"
    t.string "image"
  end

end
```
The `user_id` and `character_id` are defined as **foreign keys** in relational database structure. It uses that **primary key** of another table in referring to a row of that table. `user_id` in `"characters"` table is equivalent to `user.id` in `"users"` table. `character_id` in `"moves"` table is equivalent to `character.id` in `"characters"` table.

Since a stage `has_and_belongs_to_many` fighting moves, and a move `has_and_belongs_to_many` stage platforms. The **join table** `moves_stages` fulfills the *many-to-many* relationships. It has two columns, `stage_id` and `move_id`. One for each of the table primary key reference.

### **Database Searching Saga**

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

At first, I thought it would not be difficult in finding an API or HTML to parse out and sanitize the data in order to feed my rudimentary database `seed.rb`. Most of the gaming webpages are buggy and not consistent. I required a solid database for my CRUD study. After searching of little or no avail, I decided to *hard-code* my seed data from multiple sources. *:silent-crying-mode*

### **Front End::Controllers**

**Routes** are part of the code application connecting HTTP to specific methods in my Controller Action. These specific URLs via HTTP mappings are defined as Routes. When the user makes a request, matching the route to the controller action, it triggers the code inside of the controller action block. As programmers, we should be able to define the specificity of our controllers, and navigate the corresponding users to our web application. 

Thanks to [Roy Fielding](https://en.wikipedia.org/wiki/Roy_Fielding) for **Re**presentational **S**tate **T**ransfer (REST) routes. This pattern-oriented URLs allow conventions for developers as they work unanimously in complex settings. CRUD (Create, Read, Update and Delete) actions are semantically followed by corresponding HTTP verbs. My project RESTful routes are defined as follows:

**Application Controller**

| Request | Route | CRUD action |
| ------------- |:-------------:| -----:|
| GET | '/' | read |

The `ApplicationController` is the mother of other ancillary controllers in handling all of the incoming requests. It has the application setups, routes and controller actions. 

My web application requires `UsersController` at its inception in managing users database. The challenge became more apparent when developing associative controllers, and creating *dynamic routes `:`*. `CharactersController`, `MovesController` and `StagesController` utilize more of these dynamic routes in passing data from users' inputs to database, and vice versa.

**Users Controller**

| Request | Route | CRUD action |
| ------------- |:-------------:| -----:|
| GET | '/signup' | create |
| POST | '/signup' | create |
| GET | '/login' | read |
| POST | '/login' | read |
| GET | '/index' | read |
| GET | '/edit' | update |
| PATCH | '/edit' | update |
| GET | '/delete' | read |
| DELETE | '/delete' | delete |
| GET | '/logout' | read |
| POST | '/logout' | delete |

**Characters Controller**

| Request | Route | CRUD action |
| ------------- |:-------------:| -----:|
| GET | '/characters' | create and read |
| POST | '/characters' | create |
| GET | '/characters/:slug' | read and update |
| DELETE | '/characters/:slug' | delete |
| GET | '/characters/:slug/:id' | create |
| GET | '/characters/:slug/:id/battle' | read |

**Stages Controller**

| Request | Route | CRUD action |
| ------------- |:-------------:| -----:|
| GET | '/:slug/stages/new' | create |
| POST | '/:slug/stages/new' | create |
| GET | '/:slug/stages/edit' | update |
| POST | '/:slug/stages/edit' | update |

**Moves Controller**

| Request | Route | CRUD action |
| ------------- |:-------------:| -----:|
| GET | '/:slug/moves/new' | create |
| POST | '/:slug/moves/new' | create |
| GET | '/:slug/moves/edit' | update and delete |
| POST | '/:slug/moves/edit' | update and delete |

During the development of my controllers and views, Ruby gem [shotgun](https://github.com/rtomayko/shotgun) and `binding.pry` became my best friends. `rackup` only allows one-time read of application code, while `shotgun` allows application code reload upon every request. It is prudent as programmers to check consistencies when building controllers and sending appropriate data to views simultaneously. I also learned the hard way not to forget `use Rack::MethodOverride` as `delete` and `patch` requires Sinatra Middleware to find requests with `name="_method"`.

Manipulating `params` in the controllers from users' inputs, and transversing `instance variables` from controllers to views are ways in which both controllers and views communicate, passing data back and forth. 

As my controllers overloaded with more HTTP routes, code repetitions became un-avoidable. **Helper methods** have been very useful. Aside from `logged_in?` and `current_user`, I added `finding_character_slug` in finding instance variable `@character`.

```ruby
def finding_character_slug
	@character = current_user.characters.find_by_slug(params[:slug])
end
```

### **Front End::Views and Forms**

ERB (or, Embedded Ruby) provides application for both HTML and Ruby code syntax. It is a great alternative to `.html` format where its lexical environment is restricted to HTML tags. ERB has two types: `<%= %>` substitution tag and `<% %>` scripting tag. `<%= %>` displays the results while `<% %>` only evaluates.

Defining form methods, actions and retrieving inputs is essential for users in passing data. These form fields have various data `type` inputs. Any user input data passed from the URL forms to the controllers is in Ruby hash data types, or `params`. As developers we need to provide directions on **where** and **how** to send the data from the user. After all, creating *dynamic* web application allows user interaction, it gives more value than simply having *static* pages.

When creating these views, my biggest challenge was to create a flow for the user. As the user navigates through the site and populates user-driven attributes, I had to break a couple of RESTful conventions.

### **Authentication, Authorization and Validations**

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

Open-source gem `bcrypt` allows encrypted password in hashing algorithm. This is to avoid saving a password in its raw form - [developer's nightmare](https://www.youtube.com/watch?v=O6cmuiTBZVs)! The User model needs to include `has_secure_password` in order to allow AR macro method `authenticate` when a user logs in.

HTTP (Hyper-Text Transfer Protocol) is a stateless protocol, meaning user's requests are treated as independent transactions. I need my web app to remember the user's identity from page to page. Welcome to **Session Cookies**! Session is an object (a hash datatype) storing user's data on the server when interacting with the web application, and passes the data to the client as a cookie. It persists user's information while the user navigates through pages. It typically expires when the user logs out or closes the browser. By checking concurrence with the user's `session` hash, we **authorize** the corresponding user to access the app.

I have only used two validations for this project. `validates_presence_of` is to confirm user's input (no empty data, or `!= ""`) on username, email, and password. Otherwise the system will not allow the user object to be saved in the database. `validates_uniqueness_of` is to confirm no duplication on username or email. They are both useful in managing users. Along with these validations, I included gem `rack-flash3` flash messages to display validation failures.

### **Execution**

When either `shotgun` or `rackup` is triggered, `config.ru` will be executed. My `config.ru` requires my `config/environment` where I have established `sqlite3` database connection and controllers. Short video of **back-end** demo below.

{% youtube 4IKJC1tK_KM %}

### **Difficult Lessons Learned**

At the inception of this project, I had both User and Character model relationships as `has_and_belongs_to_many`. The idea was to have every single user the ability to access all characters, and each character belongs to many users. As I progressed further, I was faced with a challenge of my character attributes. Let's assume User A selects attributes of fighting moves and stages for a specific character. When User B selects the same character, it inherits pre-determined attributes. A brand new user would want a fresh set of characters with no pre-existing attributes. 

My amazing cohort leader, Ally Kadel, helped me brainstorm a possible solution. Eventually, I revised the User and Character model relationships to `has_many` and `belongs_to`. User `has_many` characters, and each character `belongs_to` a user. In the `CharactersController`, I duplicated the user's selected character using `.dup` method, leaving the original character selections un-touched. User A will have duplicated sets of the original characters, and this allows User B to have another set of duplicate characters. The solution was rather simplistic, but I was stuck for the longest time - *multiple brain farts do exist!* 

### **CSS the Fun Way**

I got a chance to contribute a tiny bit of my design skills. I am an architect after all. Though, the project does not require any CSS requirements, but I figured why not. It's fun, and was my first CSS exposure. I decided to keep things simple and outsource from [Bootswatch](https://bootswatch.com/). I have learned `div`, `class`, `style`, `text-align`, `display` along with few other `css` attributes. I found using `css selectors` on web browser's `inspect` proved to be very useful in experimenting display results. I did not get a chance to do a deep dive into [CSS documentation](https://www.w3schools.com/Css/css_intro.asp), but surely I will be more exposed in future modules. It gets complicated to apply consistent style and layout across all pages. Short video of **front-end** demo below.

{% youtube pvurDvp8TZY %}

### **Conclusion**

Overall, I am satisfied with my rudimentary application of Content Management System (CMS) with Sinatra CRUD and MVC. I would love to eventually learn how to build integration testings `RSpec` (another Domain Specific Language) with *Capybara Ruby Library* when time permits. Sinatra is another milestone in my developer journey. Moving on to Rails!  

> "Don't fight for victory—fight to improve yourself. Victory will come." 
-Ryu, Super Street Fighter II Turbo HD Remix

{% github fentyhall/mod2_sinatra_streetfighter no-readme %}

---

Post Scriptum:
This is my Module 2 capstone project with Flatiron School. I believe one of the catalyst to becoming a good programmer is to welcome constructive criticism. Feel free to drop a message. 🙂

Keep Calm, and Code On. 

External Sources:
[Street Fighter V Wiki](http://wiki.shoryuken.com/Street_Fighter_V)
[Ruby Gem Corneal](https://github.com/thebrianemory/corneal)
[Free themes for Bootstrap](https://bootswatch.com/)
[Active Record Associations](https://guides.rubyonrails.org/association_basics.html)

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