---
# System prepended metadata

title: Rails
tags: [Back-end]

---

# Rails

###### tags: `Rails`

### ==專案記錄==
- ++test\test_01++
已上 github 測試
- ++test\cart_415a++
使用 postgresql 的購物車程式，已用 git 版控
- ++test\cart_416c++
使用 postgresql 的購物車程式，可加減數量，已用 git 版控
- ++ruby\task\book_v2++
測試 tailwindcss 和 daisyUI
- ++ruby\vote_me++
線上課程範例

---

4/21
優先把購物車的介面刻出來
用cartproduct（parent去撈saleinfo的id與資料）用find_by找
後續要接user_id

---

1. 建立新的 Rails 專案：
rails new 專案名稱 --database=postgresql
cd 專案名稱
2. 建立資料庫：
rails db:create

---

### 3/21

- `gem install rails -v 6.1.7.3`
`rails -v`
`rails new jobbank`
`rails _6.1.7.3_ new jobbank`
- `ubuntu 20.04`
`wsl --list --verbose`
`wsl --set-version Ubuntu-20.04 1`
`Ruby 3.2.2`
`Rails 6.1.7.3`
`Node 16`
`npm install -g yarn`
- 加 readme 資料
語意化版本
- `gem install XXXX`  <= Gemfile/Gemfile.lock
`gem uninstall XXXX`
- 全部安裝
`bundle install`
自動下載某個套件
`bundle add XXXX`
`yarn add / npm install`
- Ctrl+p 可以搜尋
- 連結資料庫(migrate)，這個指令不會重作
`rails db:migrate`
若想清除需要使用以下指令 (倒退一步)
`rails db:rollback` 

---

### Rails的規則，table會小寫+複數s
| Model     | Table     |
| --------  | --------  |
| Book      | books     |
| Student   | students  |
| Rusume    | resumes   |

- 慣例優於設定(COC)，不要重複code(DRY)
- 後續想要加新欄位，正確的做法應該開一個新的migrate
`rails g migration 修改說明文字` (g=generate)
`rails d migration 修改說明文字` (刪除)
- `rails -T`	可看Rails有哪些指令
- 在vscode內的 view > source control 可顯示GIT符號
- 看誰用了3000 port 並砍掉
`lsof -n -i:3000 | grep LISTEN`
`kill 754`

### 在rails內新增model
- `$ rails g model Resume name:string email:string tel:string skill:string intro:text city:string education:text experience:text portfolio:text`

上面為完整指令，若資料為 string 型別可以省略，以下為省略後指令

- `$ rails g model Resume name email tel skill intro:text city education:text experience:text portfolio:text`

---

### 換 node 16 執行流程

1. 換用 node 16
2. 刪除 node_modules 目錄
3. 執行 yarn install -> 產生新的 node_module
4. 重開 rails s

### 在 nvm 中安裝 node 16
- `nvm install 16.19.0`		
使用 node v16
`nvm use 16`
查詢 node 版本
`nvm ls`
設定預設 node 版本
`nvm alias default 16.19.0`	


### 3/21 rails 進度
* `$ rails new ____`
`$ rails g model ____`
`$ rails g migration ____`
`$ rails db:migrate`
`$ rails db:rollback`
`edit routes.rb`
`$ rails g controller ____`
`edit controller ____`
`add app/____/views/___.html.erb`

### 檔案位置
* `migrate：db > migrate`
`routes.rb：config > routes.rb`
`xxxx_controller.rb：app > controllers > xxxx_controller.rb`
`zzzz.html.erb：app > views > xxxx資料夾 > zzzz.html.erb`

### Rails Route
* 設定首頁路徑
`root "welcome#index"`
把"/users"轉址到"/accounts"
`get '/users', to: redirect('/accounts')`
- 在Route進行比對的時候，在前面先查到的就會先生效
- 在route內使用resources可一次製作8個路徑，如下
```ruby=
Rails.application.routes.draw do
  resources :users
end
```

    
| Prefix    | Verb   | URI Pattern              | Controller#Action |
| --------- | ------ | ------------------------ | ----------------- |
| users     | GET    | /users(.:format)         | user#index        |
|           | POST   | /users(.:format)         | user#create       |
| new_user  | GET    | /users/new(.:format)     | user#new          |
| edit_user | GET    | /users/:id/edit(.:format)| user#edit         |
| user      | GET    | /users/:id(.:format)     | user#show         | 
|           | PATCH  | /users/:id(.:format)     | user#update       |
|           | PUT    | /users/:id(.:format)     | user#update       |
|           | DELETE | /users/:id(.:format)     | user#desroy       |
  

* 若不需要8個路由，可使用以下指令修改

```ruby=
Rails.application.routes.draw do
  resources :products, only: [:index, :show]
```
* 或是反過來這樣寫也行
```ruby=
resources :products, except: [:new, :create, :edit, :update, :destroy]
end
```

### rails c (控制台模式)

* exit / ctrl + d
(離開)
Post.first
(顯示第一篇文章的資訊)
Post.all
(顯示全部文章的資訊)
p1 = Post.first
(取得第一篇文章)
p1.user
(找出該篇文章的作者)
u1 = User.first
(取得第一個會員)
u1
(列出第一個會員的資料)
u1.name
(列出第一個會員的name)
u1.email
(列出第一個會員的email)

### 下拉式選單
* 在veiw的html.erb內，可做下拉式選單列出所有user
```rbuy=
form.label :user_id
form.collection_select(:user_id, User.all, :id, :name)
```

### 解決 N + 1 問題 (重複撈資料造能效能浪費問題)
* 在controllers的posts_controller.rb內，可在撈文章時順便撈使用者名稱，改寫如下 (上→下)
`@posts = Post.all`
`@posts = Post.all.includes(:user)`

### 驗證器
* 希望名字 / email不能填空白
在 controllers 的 user_controller.rb 內加上
`validates :name, presence: true, length: { in: 6..20 }`
名字長度在 6~20 字之間
`validates :email, presence: true`
(驗證欄位必須要填資料，若空白則跳錯 "name / email can’t be blank")

---

### 3/23
- Route
  - xxxx_path vs xxxx_url 
  - xxxx_path 優缺點
  - link_to
- Form
  - <form action method>
  - GET vs POST
  - HTTP 狀態
  - <%= form_for %>
- 保護機制
  - AuthenticityToken
    - skip 驗證（小心用）
    - 給 token
    - 用 form_for
  - ForbiddenAttributeError
    - 用 permit

### 局部渲染
* `<%= render 'shared/navbar' %>`
`shared > _navbar.html.erb` (額外新增的模組檔名前要加底線) 用的時候不用加底線
- 文字判斷可用regex(101)
- 一般的搜尋網頁功能都用get做，例如google搜尋引擎
post 傳送資料不會顯示在網址列上
- http狀態
204	not connect
404	not found
- 串接金流時可能會skip authenticity_token(驗證)，其他情況不建議skip
- 打debugger可下中斷點
可打參數測試
`params`
`params[‘resume’]`

---

### 3/27

- `rails c --sandbox	`
控制台(沙盒模式)  離開會清除設定
`r1 = Resume.new`
`r1.save`		            =>false
`r1.errors.any?`	        =>true
`r1.errors.full_messages`	=>列出所有錯誤

* 用間接給參數 達到模組化 重複利用
局部渲染 erb 內不要放實體變數 (@xxxx)，可以用餵的方式給他，以下使用廣告為例
```ruby=
<%= render ‘shared/ads/square’, content: @ad1 %>
<%= render ‘shared/ads/banner_large’, content: @ad2 %>
<%= render ‘shared/ads/banner_small’, content: @ad3 %>
```
    
### clone github上的專案
- 在終端機下指令
`git clone https://XXXXXX.git`
專案連結在github綠色按鈕內的http下複製
- 在 github 下載完專案後
`bundle install`
`yarn install`
`rails db:migrate`
`rails s`

---

### 3/28
- rails 錯誤訊息頁面下方可以打字
例如 "params"
- 有動態id的router系統不會給名字  要自己取
有相同路徑時不用再給一次 as
- Query String
`/resume/123?a=1&b=2`
- "?"前面的是路徑，後面是id資料
- `.where`		    會撈出多筆資料，並放進一個陣列
`.find()`		    只能找出一筆資料
`.find_by(number)`	只能找出一筆資料
- `rescue_from`是rails提供的功能 可處理錯誤訊息
- `Turbolinks`是加速連結的rails功能，可能造成某些 javascript損壞
- 要有表單才能做POST
- 局部渲染其他檔案時，檔名前需要加底線
`<%=render 'resumes/data' %>`
若是要渲染有相關的檔案，可以不用寫資料夾名稱
- 作業 (已完成)
用 Rails new 一個專案
book 的 CRUD，書名(name)、作者(author)、簡介(introduction)
25分鐘完成

---

### 3/30

- resources 和 resouce 產出的路徑差別為，前者有id，後者沒有
 collection 和 menber 可建立路徑，前有無id後者有id
- annotate 套件，可在model備註資料
- ruby 使用 digest 函式庫需要 `require 'digest'`
- `self.password` 的 self 指的是自己本身，而非類別變數，self.password 指的是一個欄位，把左邊前面的 self 拿掉會變成區域變數，就不會生效了
- devise 套件 可快速做 會員登入/查詢密碼 功能
- hash 取值時要填 `keyword:`
- paranoia 套件 可快速設定軟刪除
    
---

### 3/31
- 增加 migrate 檔案需要使用指令
- Authentication   認證、憑證
Authorization    授權
- pundit 套件 可處理憑證/授權問題
- foreman 套件 可一次開啟多個server
`bundle add foreman`    安裝 foreman
`foreman s`    使用指令開啟 server
- 建立一個`Procfile.dev`檔案，內容如下
`web: rails server -p 3000`
`webpack: bin/webpack-dev-server`
`css: bin/rails tailwindcss:watch` (tailwindcss使用)
- 建立一個`.foreman`檔案，內容如下
`procfile: Procfile.dev`
    
---

### 4/6
    
- rake
`rake task` 可幫忙自動生產履歷
`rake -T` 可查看 rake 指令
- tailwind
回去測試 `tailwind` 的使用方式，可查看官網和上課影片安裝，可取代傳統 css
- 看 form_for 和 form_with 的用法與差異
- `rails routes -c xxxxx` 查詢跟 xxxxx 相關的路徑
- 在新增路徑時，加上 `shallow` 可以自動做出縮短路徑的巢狀結構
- comment 同時與 resume 和 user 產生關聯，給資料時兩個同時都要給
- js.erb
在 rails 中寫 js 時，render 時在 "" 中加 `j` 可跳脫 單引號和雙引號
- 設定變數時要使用`var`，不然重複宣告變數會出現錯誤
- `stimulus`  方便使用 js 的套件
- 用 `bin/dev` 開 server
    
---

### 4/7

- foreman 套件 可一次開啟多個server
`css: bin/rails tailwindcss:watch`
- action table    互動式聊天室窗套件  
- hotwire    可對 html 的取用進行優化  
- turbo-frame     可立即在目前的視窗顯示其他視窗的內容
trubo-stream    刪除可用，也是立即更新
- delete-at 刪除使用套件 (軟刪除)  
- thubmb 套件 可快速使用各種向量圖
要先裝 fortawesome  
- axios 套件 用來代替 fetch
- rubocop-rails 套件 整理程式碼
`rubocop -A` 自動修正程式   
  
   
---

### 4/10

- friendly_id 套件 優化網址路徑
`rails g magration add_liug_to_users slug:uniq`
- Rails Internationalization (I18n) 多國語系
- sortableJS 套件 各種拖曳物件效果
- acts_as_list 套件 拖拉排序用套件
- rails_request.js 套件 
- ngrok 套件 可產生一個臨時的網址，用來測試金流或其他功能
 
---

### 4/11
    
- braintree  串 paypal 金流
測試卡號：4111-1111-1111-1111 
- dotenv 可存放變數(token)
- aasm 狀態機套件  
- kaminari 分頁套件 
    
---

### 4/17
    
- carrierwave 套件 上傳圖片
- active storage
- Amazon S3 放照片的地方
- marked 套件

---

### Docker
    
- image (only read)
- `container = image.new`
- `docker run hello-world`
- `which docker`
- `docker image ls`
查看電腦裡有哪些image
- `docker container ls`
查看電腦裡有那些正在執行的
- `docker container ls -a`
- `docker run nginx`
- `docker run -p 777:80 nginx`
將localhost port 777 對到 外部的port 80
- 連到 `http://localhost:777/` 會有 nginx 的預設頁面
`docker run -v ${PWD}:/user/share/nginx/html -p 777:80 nginx`
- `docker run -it nginx /bin/sh`
可打字互動 不要執行預設行為
- `docker build -t`
    
---

### 4/18
    
- rails 7 import maps hotwire
- rails g stimulus 
- polymorphic 多型關聯
- STI single type ixxxx 單一表格繼承關聯
- 在 model 中不要亂用 type 當欄位
    
---

### 4/20

- 使用hotwire時，turbo預設會打開，若無法開啟網頁可關閉
`data: { turbo: false }`
   
- 到 `config/initializers/inflecions.rb` 檔案內
解開第6行 `ActiveSupport::Inflrctor.inflections(:en) do |inflect|`
解開第11行 `end`
改第9行的 `inflect.irregular 'aaa', 'bbb'`
設定 rails 專案內的 aaa 複數為 bbb
    
---
    
### vote_me 線上課程
    
* 建立候選人的 magration
`rails g model Candidate name party age:integer politics:text votes:integer`

* 建立選票的 magration
`rails g model VoteLog candidate_id:integer ip_address`
改寫為下面方法，效果與上面相同
`rails g model VoteLog candidate:references ip_address`
* `candidate:references` 
此寫法會產生一個 candidate 的 id 欄位，會參照指向候選人的 id (references為複數)

* `rails c` 開控制台
`VoteLog.all` 查詢所有投票記錄
`VoteLog.count` 查詢投票數量
    
    `c1 = Candidate.first`
`c1.vote_logs` 第一個候選人的投票記錄
`c1.vote_logs.count` 第一個候選人的票數
    
* Counter Cache 可在數據庫做一個簡單的計數器，減少資料庫的存取
- 在資料表中增加一個欄位
    `rails g migration add_counter_cache_to_candidate`
- 在 migration 表內增加欄位資料如下
```ruby=
class AddCounterCacheToCandidate < ActiveRecord::Migration[6.1]
  def change
    add_column :candidates, :vote_logs_count, :integer, default: 0
  end
end
```
                                        
- 執行 `rails db:migrate`
在 `model/vote_log.rb` 內增加資料 `counter_cache: true`   
會在寫入票數的同時，去 `vote_logs_count` 欄位更新票數                                       
- 在 `veiw/cansidates/index.html.erb` 內增加 `<td><%= candidate.vote_logs_count %></td>` 

    