Learning Journal
Published: 2023-01-04 00:38:28

**Class 1**

**Creating an app with draft:generator**
1. Create workspace in Gitpod (Canvas link or create template in Github)
2. Run `bin/setup` to run all the setup stuff
3. Run `bin/server` to generate web browser view
4. Add `gem "draft_generators", :github => "firstdraft/draft_generators"` in Gemfile. 
 - gems are libraries which allow any developer to add new functionalities without writing code
 - Gemfile is a format for describing gem dependencies for Ruby programs. Or you can say it contains the gems you need in this project
5. Run `bundle install`: install the dependencies specified in Gemfile
5. Run `rails generate draft:resource movie title:string description:text released:boolean` in the console
6. Run `rails db:migrate`: migrate database schema according to your `db/migrate` files

------Web app created, but with security issues------

**Reminder about routes and controllers**
When we visit /movies URL in the web browser, the app goes to our `config/routes.rb` file and checks where this route is defined. When it finds the line with `get("/movies", ...)` it then reads the following Hash `{ :controller => "movies", :action => "index"}`. From that, it understands it has to go to our file `movie_controller.rb` in the `/app/controllers` folder for the `Movies` class and find the method `index` defined in it. The job of an action is to send back a response to the user. Types of actions are:
- HTTP methods:
 - `get`: display a page with some info. If we have sensitive info we don't want to use this because it will show the info right in the query string in the URL (ex. READ movies list)
 - `post`: trigger the storing of some info, like when we want to create data. Puts all the inputs in different parts of the request so they are not visible (ex. CREATE a movie)
 - `delete`: trigger the deleting of some info (ex. DELETE a movie). Be careful that in HTML `delete` method doesn't exist. You have to include `data-method` instead
 - `patch`: trigger the updating of some info (ex. UPDATE). Considering `patch` method doesn't exist, we must include a line that says `<input type="text" name="_method" value="patch">`
In the controller, there are two main types of actions:
- `redirect_to`: forward the user to another URL, maybe somewhere on the internet that's public
- `render`: rendering some data, in any one of many formats. Here we can send a user to a internal file in our codebase, like a template in our `app/views/` folder

**Fixing security issues**
Ruby expects that when we call a `post` method to modify our database to come along with a password that guarantees that the form is created by a user and not by a malicious third party (cross site request forgery). Solution: in the form we will create a hidden input with a password. We generate in a way that we are the only ones allowed to create the password. To dos this, add `<input type="hidden" name="authenticity_token" value="<%= form_authenticity_token %>">`. The key here is the helper method we input in value argument
RESTful routes: use conventional naming for our routes. URLs should be used to uniquely identify resources (ex. `/movies`), not actions upon them. HTTP request should be used to indicate which CRUD function is performed (ex. `patch`)

**Scaffold generator instead of model**
- Use `rails generate scaffold book title:string description:text released:boolean` to generate the app. If I will need routes and controller/actions for users to be able to CRUD records in the table, then I probably want to generate scaffold instead of just model
- Routes: adding just one line in routes with resources:books creates RESTful CRUD routes. Can customize with a `Hash` like `{ :except => [:delete]}` or `:only`
- Controller: if everything is using named according to conventions, methods in Controller can be almost empty
 - Includes responses for multiple formats beside just HTML for `create` method. For ex. if we want to create iPhone app, we split front and back end and have a JSON API and the iPhone will consume the API
- Validations: go to the /app/models folder an in the `class.rb` file and include validation methods (ex. `validates :title, presence: true`)
- Notices and alerts: go to `app/views/layouts` folder and in the `application.html.erb` file, below the `<body>` include notices and alerts messages (ex. `<p><%= notice %></p>`)
- Web design: see resources [here](https://chapters.firstdraft.com/chapters/788)
- Cookies: to store values in a cookie, add a line with cookies[:var_name] = params.fetch("var_name_to_fetch")

**Before class 2**

**Testing in Rails** ([documentation](https://guides.rubyonrails.org/testing.html))

Tests info is held in the `test` directory. Main sub-directories:

1. `Helpers`, `mailers`, `models` and `channels`: self explanatory
2. `Controllers`: for controllers, routes, and views
3. `Integration`: interactions between controllers 
4. `System`: full browser testing of your application the way your users experience it and your JavaScript

- Tests are run on the Testing environment. It can be modified in `config/environments/test.rb`
- Tests key lines of code are assertions. They are the methods responsible for checking if two values are equal, if something is nil, etc.
- To run tests, use `bin/rails test` command (or any modification)
- Testing environment have its own database. It is defined in the `config/database.yml` file. Fixtures in `test/fixtures`directory allow you to populate your testing database with predefined data before your tests run

**Clean ups:**

**Route helper methods:**
- Drop curly brackets `{}` in case it is the last argument to method
- Eliminate Hash rockets (`=>`) in arguments by putting colons (:) at the end of the argument
- Drop parenthesis around arguments to a method if you are calling just one method in a line and there is no order of operation concern
- Instead of a `Hash` for a route and use `controller:` and `action:`, you can input arguments as a string like `"argument_type1_value#argument_type2_value"`
- Use `root` method for root route (no rocket hash needed)
- Define prefixes for routes by including `, as: :details` in the routes.rb file and replace all URLs with newly created ruby methods like `details_path(:id)` in the views files
- Omit `.id` from the movie paths. Route helpers expect conjunction with Active Record objects. Instance of Active Record uses id as default
- Eliminate .html.rb extension for templates as it is the assumed extension (if you want to use other like json you need to include it). If you are rendering a template, you could delete `template:` if you are not passing more options. 
- If the folder name where templates are located matches the Controller name and if the action name matches the name of the template, you could delete the render line altogether
- Shortcut for RESTful CRUD routes is resources :class

**Forms helper methods**
- Replace `<a href="<%= "path"%>">` in templates for links with Link_to helpers, i.e. include `<%= link_to "Description", "URL or Active Record" %>` instead 
- Replace `<form action="<%= path %>" method="">` and `<authenticity token>` with `<%= form_with(model: @class) do |form| %>` `<% end %>`. For edit forms, include `, method: :patch` inside the `form_with()`. Then, inside the form:
 - For labels, include `<%= form.label :label_name %>`
 - For inputs, include `<%= form.text_field :query_name %>`
 - For buttons, include `<%= form.button %>`
- When we are getting the movie id from the params Hash, we can change to: Movie.find(params.fetch(:id))
- For convention, match variable names with Classes name (ex. @movie)
- When defining methods in our Controllers (ex. `create`), we can store all the attributes in the params Hash by including `var_to_store = params.require(:class).permit(:att1, :att2,...)` and then creating a new variable `@class = Movie.new(var_to_store)`

**Add a new column after the database is already created**
- Add a new column: Run `rails g migration AddImageUrlToMovies image_url:string` in the console. Then `db:migrate`
 - If the new column is for an association, creating it is enough. The `has_many` that we can add in the `models` just help us work with the data enabling some methods
  - To create a drop down with the existing directors, add  `<% form.collection_select :director_id, Director.order(:name), :id, :name %>` in the template for creating a moive
 - Never touch schema.rb file. Always create new migration files or edit existing migration files directly and then use `rails db:migrate`. If you need to drop the db, use `rails db:drop` and then `rails db:create`, or `rails db:rollback` for one step back. Remember when you add a column, you need to whitelist it

**Class 2**

- Pull in Bootstrap CSS and Font Awesome: go to this [link](https://chapters.firstdraft.com/chapters/788#quick-links-to-assets) and copy the quick-and-dirty CDN links code at the bottom of the `<head>` piece of the `application.html.erb` file in the `app/views/layouts` folder
- Add a Bootstrap navbar: go to this [link](https://getbootstrap.com/docs/5.1/components/navbar/) and then copy the navbar code in the `application.html.erb` file at the top of the `<body>` section and refactor with links_to and desired structure
- Switch notice and alert messages to Bootstrap alerts: go to this [link](https://getbootstrap.com/docs/5.1/components/alerts/) and copy the desired format. In this case we chose success and danger formats. Add an if at the beginning with `<% if notice.present? %>`
- Add `<div class="container"><div>` in the application.html file around the yield so that all of our templates are rendered within one
- In case you want to avoid repeating the same line of code every time, you can create a new method. Ex. for defining movie_params with `def movie_params` / `params.require(:movie).permit(whitelist of attributes)` / `end`
- Partial view templates: in /view directory, create a file named _template with the template code. Then `render` the template from a view file with `<%= render partial: "partial_view_template" %>` (no need to include the _, it is implicit)
- Partials with inputs: if the partial view template needs to include variables with dynamic values, use the render method when you want to call the partial view template but now adding: `, locals: { var1: "var_value" , var2: "var2_value" ...}`
- Don't use instance variables (`@class`) within partials. Instead, prefer to use the :locals option and pass in any data that the partial requires, updating the form partial to use an arbitrary local variable name, e.g. foo, rather than @class. For ex. `<%= form_with model: foo do |form| %>`. Then, update class/new and edit with `<%= render template: "movies/form", locals: { foo: @new_movie } %>`
 - If you create a partial with the same name of the class, you can just use <% = render @class %>
- `before_action :method_name` runs the same method before any action. You can use `, only:[ :action1, ...]` or `, except:[ :action1, ]`
- For sign ins and outs: add `devise` gem to `Gemfile` and run it  with `rails g devise:install`. Then we can use `rails g devise user first_name last_name`. Finally run db:migrate and bin/server. In the routes file, the `devise_for :users` have 50 different routes related to accounts that are very useful 
 - The `users_controller` is inside the gem, is not visible. You shouldn't have to modify it (but you can)
 - What we probably want to modify is the view templates. To make them visible, run `rails g devise:views`
 - If you want to force sign in before every action, use `before_action :authenticate_user!` in the `application_controller`

**Before class 3**

Git ([link](https://chapters.firstdraft.com/chapters/859))
- `git init`: initialize a new repo
- `git add -A`/ `git commit -m "Title of your snapshot"` (`shortcut g acm ""`): copy all folders and subfolder in the project to commit (optional). Then take a snapshot (commitment) in the current branch
- `git status`: see if there are any changed files since the last commit
- `git diff`: see the line-by-line
- `git branch BranchName`: create a new branch
- `git branch`: list all branches
- `git checkout branch-name` (shortcut `g co name`): switch to another existing branch
- `git checkout -b fancy-new-version-name` (shortcut `g cob name`): create a new branch and change branches
- `git log` (shortcut with better UI `g sla`): history of current branch 
- `git merge BranchName`: merge specified branch with the main branch
- `git rebase BranchName`: copy the branch you are in over BranchName  
- `git push` (shortcut `g p`): send the work you’ve done on a branch back to GitHub.com
- `git pull`: retrieve the freshest version of the branch you’re on from GitHub.com

**Photogram industrial**
- Add default values for columns: in the migration file, add `t.integer :likes_count, default: 0`
- Database indexes: to speed up looking up a record by a column and not only primary key, add `add_index :users, :email, unique: true`
- Case insensitive text column: for username to simplify lookups. Add `enable_extension("citext")` at the top of the migration file and then change string for citext in the required variables with `t.citext :username`
- Association shortcut: when creating a model or scaffold, you can use `var1:references` in the variable that is a meant for association with another table. This adds a few lines in the migration file to prevent the value to be null and defines it as a foreign key 
- If you want to see what's included in your models, add `gem 'annotate'` in Gemfile and run `rails g annotate:install`. With this, every time you run `db:migrate` you will have the details in your models files
- To keep track of how many associated records you have for a specific record, you can add `, counter_cache: true` after the `belongs_to` and they are generated automatically
- Many to many associations: for this kind of relationship add `has_many :fans, through: :likes` in models. In the example, one photo can have many users that likes it but also a user can like many photos
- Indirect associations: for this kind of relationship add `has_many :feed, through: :leaders, source: :own_phothos` in models. In the example, we want to be able to see all the photos from the users that we follow (`leaders`)
- Add a default value when you already have your table: follow this [link](https://blog.arkency.com/how-to-add-a-default-value-to-an-existing-column-in-a-rails-migration/)
- Scopes: for frequently used .where(), we can add `scope :past_week, -> { where(created_at: 1.week.ago...) }` in the models file to encapsulate this logic and then call them by `current_user.discover.by_likes`
- Enum column type: when we have a limited amount of possibilities of values for a clomun, we can add `enum status: { pending: "pending", rejected: "rejected", accepted: "accepted" }` in model and we get a lot of handy methods that we can use afterwards
- Create sample data: Create a file in lib/tasks called `dev.rake` and stub out a rake task `task sample_data: :environment do` / `end`. You can use Faker gem to generate values

Javascript basics: ([link](https://chapters.firstdraft.com/chapters/892))

- To work with JS, you can either use the JS console in the web browser or create a `<script>` in the `HTML` file
- Some key differences between JS and Ruby: always end lines of code with `;`, print sth: `console.log("howdy!");`, string interpolation: `Your lucky number is ${7 * 6}`, create variable: `let var = ...` or `const` if its a constant, create a function: `function greet() { }`, loops: `for (let i = 2; i < 6; i++) { }`, conditionals: `if (computer_move === "rock") { } else if (computer_move === "paper") { } else { }`
- Purely manipulating HTML elements: mainly through the `document` object. Ex: `let items = document.getElementsByTagName("li")`
- Manipulating elements with jQuery: to use jQuery, add `<script src="https://code.jquery.com/jquery-3.6.1.min.js"></script>` in the  `application.html.erb` document to make sure it is loaded before you re trying to use any `JS`. To get elements: `$("li"); $(".west"); $("#best");`, get text: `chi.text();`, manipulate DOM: `chi.css("background-color", "green");`
- Ajax, i.e. Asynchronous `JS` and `XML` ([link](https://chapters.firstdraft.com/chapters/863)):