# Ruby On Rails
ref: https://railsbook.tw/ , https://gorails.com/series/build-a-blog-with-rails-7
## Notify
以下指令僅在 Mac / Ubuntu (wsl & server) 測試過,如果想要在不同的地方(windows)使用,請自行查詢相關更動,大概不會差多少
## Wats RoR
RoR == Ruby on Rails == 紅寶石在鐵路上 ~~顯否~~
### Ruby
- 一個酷酷的程式語言
- ~~在我出生那年獲選年度程式語言~~
- 是否代表他很老~~過時~~了
- 一種物件導向、指令式、函數式、動態的通用程式語言
- 減少編程時候的不必要的瑣碎時間,令編寫程式的人高興,是設計Ruby語言的Matz的一個首要的考慮;其次是良好的介面設計。他強調系統設計必須強調人性化,而不是一味從機器的角度設想
- 記住這句話 "人性化" 你等下就會懂了
- 人們特別是電腦工程師們,常常從機器著想。他們認為:「這樣做,機器就能執行的更快;這樣做,機器執行效率更高;這樣做,機器就會怎樣怎樣怎樣。」實際上,我們需要從人的角度考慮問題,人們怎樣編寫程式或者怎樣使用機器上應用程式。我們是主人,他們是僕人。
- last two by the developer of ruby
- 小知識: 我有一次在cf的blog說ruby很噁然後我的social rating就變-1了
### Rails
- 是ruby的一個gem(套件)
- 依附ruby而生的一個動態網頁應用框架
- 比其他可以寫動態網頁的框架簡單多ㄌ
- 努力使自身保持簡單,使實際應用開發時的程式碼更少
- 慣例優於設定
- 有一套作者定義的程式碼撰寫與變數命名邏輯
- 不同人的rails可能類似 方便借鑑、理解
- 檔案有點多 但是架構很明瞭 熟悉的人很容易找到要修改的地方
- MVC架構
- Models, Views, Controllers
## Why RoR and What Can it Achieve
~~因為你們接幹後要維護一個叫iscoj的東西而且他是用RoR寫的~~
一個相對簡單的動態網頁框架
讓你不用為了寫SQL而煩惱
讓你的CAPSLOCK多休息幾天
雖然但是 動態網頁終歸是要學的 對吧
但 這就是下學期的事了 而且不關我的事
## installation for ruby
因為你可以要在不同的ruby專案用不同的版本
所以裝rvm
aka ruby version manager
好用
為了要裝rvm要先裝一個叫gpg的東西
### Linux & Mac
裝gpg 把這串複製到終端機上
等一下 終端機是什麼
- 在Mac上 cmd+space 輸入terminal然後按enter 就是那個
- 在Win上 Win+R 輸入cmd 就是那個
- 在Linux上 我不相信你不知道
Linux
```shell
wget ftp://ftp.gnupg.org/gcrypt/gnupg/gnupg-1.4.2.tar.bz2
tar jxf gnupg-1.4.2.tar.bz2
cd gnupg-1.4.2
./configure
make
sudo make install
```
Mac
```bash
brew install gnupg
```
裝rvm
```
gpg2 --keyserver keyserver.ubuntu.com --recv-keys 409B6B1796C275462A1703113804BB82D39DC0E3 7D2BAF1CF37B13E2069D6956105BD0E739499BDB
\curl -sSL https://get.rvm.io | bash -s stable --rails
```
用rvm安裝ruby
```shell
rvm install 3.2.2
rvm use 3.2.2
rvm list
```
此三依序為
裝3.2.2版本的Ruby
使用3.2.2版本的Ruby
列出已安裝過的Ruby 版本
### Windows
裝WSL
往上看
## installation for rails
```
gem install rails
```
沒了 ㄏ
gem是ruby的套件管理程式
可以想像成python的pip
而rails也是ruby的一個套件
上面的安裝可能挺慢的
而且不會跳任何正在安裝的提示
請耐心稍等不要把他終止
## ruby syntax
ruby是一個高度物件導向的程式語言,會把程式中各種東西包裝成物件,類似於c++中的struct
在寫初階的Rails的時候,其實不需要用到太多的Ruby語法,我自己跟Ruby語法其實也不是很熟悉,只會用到常見的邏輯運算與條件判斷
### variables
ruby變數沒有型別之分
但卻有不同的前綴
| 類別 |例子| 解釋 |
| --------|- | -------- |
| 區域變數 (無前綴) | name |只能在目前的區塊用的變數,不能從controller傳到view,也不能在view/view或controller/controller之間互傳
|全域變數 (global variable) |$name |不太會用到
|實體變數 (instance variable) |@name |超級常用,從controller傳變數給view就是要用實體變數才傳的過去(等下會解釋),開頭記得接個@
|類別變數 (class variable) |@@name |也不會用到
### conditions
```ruby
if
#do something
elsif
#do something
else
#do something
end
# 對了註解是 # 喔
```
相信大家對於程式語言都有一些基礎的了解就不多做解釋了
記得每個區塊結束都要end
只有if也要
### loops
```ruby
while boolean
do do something
end
#friends是一個陣列
for friend in friends
puts friend
end
for i in 1..5 do
puts i
end
```
### Others
```rb
#輸出是puts
puts "ruby is cool(?"
```
其他需要用到的時候再講,或是可以自己查
## How to RoR
### create new project
到你要的地方 在終端機打上
```
rails new hello_rails
```
在你要的地方,打上面那個指令後,會在目前的資料夾新增一個資料夾,叫做
```
hello_rails
```
裡面幫你創建好了一個rails application所需要用到的各種東西,之後都在裡面修改新增東西就可以了,如果你在一個空資料夾裡面,希望這個專案直接生成在這個資料夾而不是再開一層資料夾,可以加一個點 like:
```
rails new hello_rails .
```
相信你們經過早上的課都知道 `.` 代表現在的位置
接下來進到專案資料夾
```shell
cd hello_rails
bundle install
```
要更新一下rails的各種套件確定是新的
#### start server
既然是個動態網頁 肯定是要啟動伺服器讓別人連線的
```
rails server
```
之後到你的瀏覽器連上 `localhost:3000`
看到這個就代表你成功了

因為工程師很懶
所以可以縮寫成
```
rails s
```
真的很懶= =
### MVC 架構
rails 運作的核心

- Route: 存取一個網頁後先透過route決定要去哪個controller的哪個action
- Controller: 會有很多的函示定義哪個路徑要做甚麼事,同時串接view&model,將model的資料傳遞給view進行讀取與渲染
- View: 使用者看到的畫面,透過 .html.erb(嵌入式ruby)可實現迴圈式及各種渲染
- Model: 儲存各種資料庫的地方,可定義其資料庫的關聯性並互相存取
-
### ror architecture
::: spoiler 我們先來看檔案整個架構
```
.
├── Dockerfile
├── Gemfile
├── Gemfile.lock
├── Procfile.dev
├── README.md
├── Rakefile
├── app
│ ├── assets 一些你用到的插件 通常是css & js
│ │ ├── images
│ │ └── stylesheets
│ ├── channels
│ │ └── application_cable
│ │ ├── channel.rb
│ │ └── connection.rb
│ ├── controllers
│ │ ├── application_controller.rb
│ │ ├── blog_posts_controller.rb
│ │ └── concerns
│ ├── helpers
│ │ └── application_helper.rb
│ ├── javascript
│ │ ├── MaterialComponentList.js
│ │ ├── application.js
│ │ └── controllers
│ │ ├── application.js
│ │ ├── hello_controller.js
│ │ └── index.js
│ ├── jobs
│ │ └── application_job.rb
│ ├── mailers
│ │ └── application_mailer.rb
│ ├── models
│ │ ├── application_record.rb
│ │ ├── blog_post.rb
│ │ ├── concerns
│ │ └── user.rb
│ └── views 你網頁的外觀
│ ├── blog_posts
│ ├── devise
│ └── layouts
├── bin 存快捷指令的地方
├── config 各種設定黨
│ ├── application.rb
│ ├── boot.rb
│ ├── cable.yml
│ ├── environment.rb
│ ├── environments
│ │ ├── development.rb
│ │ ├── production.rb
│ │ └── test.rb
│ ├── importmap.rb
│ ├── initializers
│ ├── locales
│ │ ├── devise.en.yml
│ │ └── en.yml
│ ├── puma.rb
│ ├── routes.rb
│ ├── storage.yml
│ └── tailwind.config.js
├── config.ru
├── db
│ ├── migrate
│ │ ├── 20231212090538_create_blog_posts.rb
│ │ └── 20231213103700_devise_create_users.rb
│ ├── schema.rb
│ └── seeds.rb
├── lib
│ ├── assets
│ └── tasks
├── log
│ └── development.log
├── package.json
├── public 一些可以公開的檔案 會先load好
├── storage 資料庫的家
│ └── development.sqlite3
├── test
│ └── 不是很重要
├── tmp
│ └──暫存資料夾
└── vendor
└── javascript
```
:::
### routes.rb
pwd: rails-app/config/routes.rb
這是你存取網頁是ror會第一個看得地方
```ruby
Rails.application.routes.draw do
#get "/posts", to "posts#index"
#get "/posts/new", to: "posts#new", as: :new_post
#get "/posts/:id/edit", to: "posts#edit", as: :edit_post
#get "/posts/:id", to: "posts#show", as: :post
#post "/posts", to: "posts#create", as: :posts
#patch "/posts/:id", to: "posts#update"
#delete "/posts/:id", to: "posts#destroy"
resources :posts
# Defines the root path route ("/")
root "posts#index"
end
```
其實劃掉那七行等價下面的
```
resources :posts
```
但我們用上面的來講解,如果你進到你的網址的`/post/new`去看,他會請求`post_controller`裡面的`def new`這個方法,`get / post / patch /delete` 是網頁各種存取伺服器的方式,to則是要到哪個controller的哪個action
```
to: "controller#action"
```
最後的as則是為這條路命個名字,有了名字之後你想要在其他地方重新導向到這條路,就可以打
```
<%= link_to new_post_path, 'New Post' %>
```
人性化的方面開始出現了 方便吧,還有 root 也是可以用root_path去存取。此外有一個要注意的是rails去找尋存在的路徑時是依序尋找的,所以如果上面的有覆蓋到下面的會優先取用上面的
### models
pwd: rails-app/app/models
可以透過rails generate 來生出一個資料庫
```
rails generate model model_name column_name: column_type ...
```
在 Rails 專案中,Model 的命名是單數(而且必須大寫,因為在 Ruby 的類別名稱必須是大寫),而資料表則是預設使用複數並以小寫及底線分隔方式命名,如:
| Model 名稱 | 資料表名稱 |
| -------- | -------- |
| User | users |
|BlogPost| blog_posts |
|Category|categories|
至於column的名字也希望是用小寫加底線分隔,可以參考下面專案實作的
column_type則是那些常見的型別們
| 名字 | 內容 |
| -------- | -------- |
binary |二進位
boolean|布林值
date|year, month, day
datetime|date+time
decimal|十進位數字
float|小數
integer|整數
primary_key|資料庫存取用的主要key
string|短字串
text|長字串
time|hours, minutes, seconds
timestamp| 時間戳 同datetime
rails會自動幫你轉好在不同要用不同的呼叫方式喔
實際應用方式會在接下來的實作介紹
### Model的關聯性
有時候有些資料會有關連的問題,讓我們想要從一個資料庫去存取其他資料庫的項目,就一個blog來說,一個user他可能有很多post,同時每個post只會有一個author。
我們可以總結出三種關聯關係
#### 一對一的model關聯性
假設每個人有一台電腦,同時我們需要這台電腦的詳細資訊,且每台電腦也只屬於一個人,因為在user資料庫開不同欄位存電腦型號、大小、規格蠻不切實際的,同時為了貫徹OOP(物件導向)的精神,我們有兩個model分別存user跟computer
這時我們可以在 `app/model` 中定義他們的關聯性
app/models/user.rb
```ruby
class User < ApplicationRecord
has_one :computer
end
```
app/models/computer.rb
```ruby
class Computer < ApplicationRecord
belongs_to :computer
end
```
挺好懂的
#### 一對多的model關聯性
現在大家都變有錢了,可以買很多台電腦,但每台電腦還是都只屬於一個人,就會用到一對多的關聯性
app/models/user.rb
```ruby
class User < ApplicationRecord
has_many :computers
end
```
app/models/computer.rb
```ruby
class Computer < ApplicationRecord
belongs_to :computer
end
```
其實差不多,但你會發現在`user.rb`的地方,不只是has_one被改成has_many,computer也被改成複數了,畢竟有很多電腦對吧
#### 多對多的model關聯性
因為大家共有一台電腦太奇怪了,所以我們換種講法,現在電腦的資料庫裡面代表的是一種型號,這樣同型號的電腦可能被很多人持有,即使他們不是同一台,我們可以這樣寫讓每個人有很多種型號的電腦,一個型號的電腦也被很多人持有
這時候你會發現你需要一個第三方的資料表紀錄他們的關係
對於每台電腦紀錄他的持有者以及型號,還有自身的流水編號
即會有一個資料庫其column為 `id, user_id, computer_id`
至於這個第三方資料表則應該叫做`computers_stores`(按照字典序並把中間加一個底線)
app/models/user.rb
```ruby
class User < ApplicationRecord
has_and_belongs_to_many :computers
end
```
app/models/computer.rb
```ruby
class Computer < ApplicationRecord
has_and_belongs_to_many :users
end
```
這樣就完成model的關聯性了
models有關聯性有什麼用?
這邊一樣等實作的時候再講
### views
什麼是view? 就是你要變成html給使用者看的畫面,其實寫法跟靜態網頁差不多,只有幾個要注意的點,在app/view/layouts的資料夾下面有一個application.html.erb,那是整個app都會預先載好的背景,可能包括選單列之類,中間的一個`<%=yield%>`則是在不同的controller下的不同action時會載入的不同分頁
.html.erb中的.erb又稱嵌入式ruby,讓你可以在html中寫ruby,批次的渲染重複的東西並執行程式語言的條件判斷
嵌入方法共分兩種
在erb模版裡,Ruby程式碼會放在 ``<% %>`` 或是 ``<%= %>`` 標籤裡。``<% %>`` 標籤是用來執行不會回傳任何值的 Ruby 程式碼,例如條件判斷、迴圈或是區塊等等,而 ``<%= %>`` 標籤則是用來輸出結果。
### controllers
controller是一個承接route發來的各種請求的東西,他會去進行一些身分驗證、在資料庫挖東西或是運算的工作,將view渲染東西所需要的資料們透過@變數傳遞過去
### CRUD
Create, Read, Update, Delete,通常來說一個物件會經歷的各種過程,專案開發時可依循此為你的物件添加功能
# 專案實作
來做一個簡單的blog吧,這邊有個我做的css破掉的範例(從此立志當後端工程師),https://ruby.cjtsai.com,所有的code都存放在 https://github.com/ckeisc43rd-cjtsai/rails-blog
## 分析
- 這個專案包含了哪些model?
- user, post
- 關聯性?
- user has_many posts
- post has_one user
- 我們這邊先做到可以發文,有興趣的可以研究怎麼加comment,或是去參考我的github(https://github.com/ckeisc43rd-cjtsai/rails-blog)
- 這些model要包含哪些功能?
- user
- register
- login
- password hash
- change password/username
- post
- create
- read
- update
- delete
- 聽起來很耳熟 CRUD
## startup
確認要做什麼之後就可以開始了
```
rails new blog
```
先創一個專案,可以先在專案裡面逛逛
## create post model
```rb
rails g model posts title:string body:text
```
要創一個資料庫存要發的posts
有兩個欄位分別是title跟body
現在會有一個migrate檔案被存到`db/migrate`
可以透過編輯他更改你實際要的欄位
也可以透過手打migrate檔案來達到像上面`rails g`的功能
然後要跑
```
rails db:migrate
```
來實際執行剛剛的資料庫遷移
現在你有一個叫post的資料庫了
可以進到
```
rails console
```
或是簡寫成
```
rails c
```
進入後可以進行測試的操作
```ruby
3.1.2 :001 > a = Post.new(title: "test", body: "test")
=> #<Post:0x00007f06ecbe6f78 id: nil, title: "test", body: "test", created_at: nil, updated_at: nil>
3.1.2 :002 > a
=> #<Post:0x00007f06ecbe6f78 id: nil, title: "test", body: "test", created_at: nil, updated_at: nil>
3.1.2 :003 > a.save
TRANSACTION (0.1ms) begin transaction
Post Create (0.6ms) INSERT INTO "posts" ("title", "body", "created_at", "updated_at") VALUES (?, ?, ?, ?) RETURNING "id" [["title", "test"], ["body", "test"], ["created_at", "2024-01-06 11:47:26.819611"], ["updated_at", "2024-01-06 11:47:26.819611"]]
TRANSACTION (0.2ms) commit transaction
=> true
3.1.2 :004 > Post.all
Post Load (0.1ms) SELECT "posts".* FROM "posts" /* loading for pp */ LIMIT ? [["LIMIT", 11]]
=>
[#<Post:0x00007f06ec48efa0
id: 1,
title: "test",
body: "test",
created_at: Sat, 06 Jan 2024 11:47:26.819611000 UTC +00:00,
updated_at: Sat, 06 Jan 2024 11:47:26.819611000 UTC +00:00>]
```
第001行的時候透過Post.new新建了一個物件並存入a中(注意大寫)
第002行便可以透過打這個變數檢視他
第003行執行a.save會將a這個物件實際寫入Post的資料庫裡面
第004行的Post.all則是顯示Post資料庫的所有物件,可以看到就包刮了剛存進去的a
## create post controller
現在我們有一個能用的資料庫了
接下來就要有個能夠操控他並且負責應對各種網路存取的controller
```
rails g controller posts
```
會在`app/controllers`生出一個`post_controller.rb`
他也會順便幫你生好view畢竟有controller就有view
### index action
第一個要建立的便是首頁,英文稱index
還記得前面說的route嗎
rails是先透過route去知道特定網址要存取哪個controller的哪個action
所以我們要先設定posts#index到主頁
config/routes.rb
```ruby
Rails.application.routes.draw do
root "posts#index"
end
```
把根給過去就好ㄌ
其他之後再說
現在進入網址會取存取這個action了,那這個action要幹嘛?
要做的事情就是把所有post顯示出來對吧
這要透過view來實現,所以存取post#index時
其實會同時去看到`app/controllers/post_controller.rb`與`app/views/posts/index.html.erb`
controller主要做的事情是整理資料並且回傳給view,view則透過html&css將其實現出來
所以我們要在
app/controllers/post_controller.rb
```ruby
class PostsController < ApplicationController
def index
@posts=Post.all
end
end
```
喔對 如果你沒有這個檔案要自己加喔
app/views/posts/index.html.erb
```erb
<h1>Posts</h1>
<%@posts.each do |post|%>
<div>
<h2><%=post.title%></h2>
<%=post.body%>
</div>
<%end%>
```
如果剛剛有在rails console新增物件的話,現在打開`http://127.0.0.1:3000/`就會看到

這樣就成功了
接下來我們要為posts完善剛剛提到的CRUD,也就是Create(建立)、Read(讀取)、Update(更新)、Delete(刪除)
### create new post
我們照著route->controller->view的順序走
routes.rb加上這兩句
```ruby
get "/posts/new", to: "posts#new", as: "new_post"
post "/posts", to: "posts#create", as: :posts
```
有兩個是因為要在new有一個新建post的頁面
post(撞名了 這邊是指網頁操作的post)則是要把posts存起來時呼叫
app/controllers/post_controller.rb
```ruby
def new
@post = Post.new
end
```
因為要存新的post,我們就先開一個新的到時候再填欄位
這邊來到view的部分,因為讓使用者填資料的表格等一下還會用到
乾脆就開一個模板來存,在rails中開模板的方式為用底線開頭的.html.erb
例如`_form.html.erb`
app/views/posts/_form.html.erb
```ruby
<%= form_with model: @post do |form| %>
<div>
<%=form.label :title%>
<%=form.text_field :title %>
</div>
<div>
<%=form.label :body%>
<%=form.text_area :body %>
</div>
<%= form.button %>
<%end%>
```
app/views/posts/new.html.erb
```ruby
<h1>New Post</h1>
<%= render partial: "form", locals: {post: @post}%>
```
這裡的render partial 的功能可以讓我們透過將一部分交給模板來渲染
locals則是要把變數傳入,這邊的@post是在controller new出來的那一個
之後按下create之後rails才會知道要把什麼東西存進資料庫
如果你這時候開啟了網頁並進到`localhost:3000/posts/new`
這邊有個form.button,由於rails的慣例優於設定準則,他偷偷的發現你這裡叫做new
所以把這個button叫做了create post,同時他會在你按下他的時候對post controller發送create這個action的請求,所以我們需要寫一個action接受他並在這時候把他存到資料庫裡面
app/controllers/post_controller.rb
```ruby
def create
@post = Post.new(post_params)
if @post.save
redirect_to @post
else
render :new
end
end
```
這邊會發現有個沒看過的post_params,這是什麼意思呢
因為rails覺得直接傳所有的參數回來有點危險,因此要求你對你需要的參數們進行允許
被允許的東西才能從網頁回來,因為需要常常用到就定義了一個函數允許會用到的參數們
在app/controllers/post_controller.rb最下面加上
```ruby
private
def post_params
params.require(:post).permit(:title, :body)
end
```
這樣就可以新增postㄌ
在`localhost:3000/posts/new`
打完之後按下create,會發現
欸?報錯了
沒關係 這是因為寫了`redirect_to @post`
但是你還沒幫每個post寫自己的show,所以重新導向過去就爛了
但是如果回到主頁就會發現他已經被存下來囉
### show post
這個應該蠻簡單的ㄅ
要先加route
config/routes.rb
```ruby
get "/posts/:id", to: "posts#show", as: :post
```
這樣寫的話你在posts後面接東西的話他就會默認他是id然後變成參數傳給show這個controller
這樣就能透過id找post了
app/controllers/post_controller.rb
```ruby
def show
@post = Post.find(params[:id])
end
```
app/views/posts/show.html.erb
```ruby
<h1> <%=@post.title%> </h1>
<div>
<%=@post.body%>
</div>
```
就去資料庫裡面用id找一找你要的post在哪並透過controller傳給view就好
但這邊還可以再改一個東西,理論上要能從主頁點到每個post的show
所以可以把每個post他們的title都變成超連結,寫起來也很簡單
把index.html.erb中顯示title的那一行變成這樣
```ruby
<h2><%= link_to post.title, post %></h2>
```
語法是 `link_to 要顯示的文字, 要去的地方`
### update post
一樣先加route
config/routes.rb
```rb
get "/posts/:id/edit", to: "posts#edit", as: :edit_post
patch "/posts/:id", to: "posts#update"
```
上面那個是存取edit這個頁面的route
下面的則是要進行修改的動作時的route
然後加controller
我們的controller在傳給view的時候,應該要先找到你要改的post的資料,先作為預設資料放到欄位中,然後讓使用者直接進行修改,而不需要直接重打
app/controllers/post_controller.rb
```rb
def edit
@post = Post.find(params[:id])
end
```
這邊就跟show一樣透過網址的id找到Post
接下來傳給view
因為前面有做過form的格式了
可以再render partial一次
views/posts/show.html.erb
```htmlembedded
<h1> Edit Post</h1>
<%= render partial: "form", locals: {post: @post}%>
```
簡單
然後我們需要讓別人有辦法透過按按鈕找到這個edit的地方
所以我們就在show的時候下面加個button來導向到這個頁面吧
在最下面加上
views/posts/show.html.erb
```rb
<%=button_to "Edit", method: :get ,edit_post_path(@post)%>
```
因為button_to預設的是post,所以要切換成get才能是切換網頁
### delete post
一樣先加route(我絕對沒有複製貼上)
config/routes.rb
```rb
delete "/blog_posts/:id", to: "blog_posts#destroy"
```
然後加controller
根據現在的id去找到對應的資料庫物件並刪掉
app/controllers/post_controller.rb
```rb
def destroy
@post = Post.find(params[:id])
@post.destroy
redirect_to root_path
end
```
我們也需要把這個delete的button顯示出來,位置同edit
views/posts/show.html.erb
```rb
<%= button_to "Delete", @post, method: :delete, data: { turbo_confirm: 'Are you sure?' } %>
```
## create user model
因為要自己刻一個user實在太麻煩了
所以我們交給套件吧,得益於rails優秀的套件管理系統,加gem就跟python一樣簡單
在Gemfile加上
```ruby
gem "devise"
```
然後在終端機跑
```shell
bundle install
rails g devise:install
```
現在我們就有devise這個套件了
他支持各種的註冊、登入、更改密碼
並且有預設好的view可以用
```shell
rails g devise user
```
這樣就可以在`localhost:3000/user/new`新增user了
為了方便我們要在主頁加上登入登出以及修改個人資料的按鈕
這東西應該在我們在每個頁面的時候都能存取
這邊重新介紹一下rails view的架構
他是透過`views/layout/application.html.erb`當作底層
其中有一個`<%yield%>`則是在你定義的不同view時會把那一小部分render出來
所以如果我們在application.html.erb新增東西的話,不管在哪裡這些東西都會被顯示出來
views/layout/application.html.erb
```erb
<% if user_signed_in? %>
<div>
<h3><%=link_to "Logout", destroy_user_session_path %></h3>
</div>
<div>
<h3><%=link_to "Profile", edit_user_registration_path%></h3>
</div>
<% else %>
<div>
<h3><%=link_to "Login", new_user_session_path%></h3>
</div>
<div>
<h3><%=link_to "SignUp", new_user_registration_path%></h3>
</div>
<% end %>
```
這邊檢查了user是否已經登入
如果已經登入便給他logout跟profile的連結
否則給他login跟signup的連結
至於後面的path則是透過devise自動定義的
同時我們會發現每次要回到首頁有點麻煩
所以可以在這裡的最頂部加上root_path的連結
```erb
<h1><%=link_to "Blog", root_path %></h1>
```
還有要讓大家發文變得比較簡單
可以在主頁加上新post的連結
記得要登入才能發文喔
這邊要加在app/views/posts/index.html.erb
畢竟你不是希望大家在哪裡都看到這個按鈕
```erb
<%= button_to "New Post", new_post_path, method: :get if user_signed_in?%>
```
後面的if user_signed_in?是devise內建的函示,會回傳一個布林值讓你方便判斷
### authenticate
沒登入的人理論上只能看index跟每個post個別的show對吧
app/controllers/post_controller.rb
```rb
before_action :authenticate_user!, except: [:index, :show]
#請把上面這段加在post controller的最上面
#請把下面這段加在post controller的private區塊下面
def authenticate_user!
redirect_to new_user_session_path, alert: "YOU MUST SIGN IN TO CONTINUE" unless user_signed_in?
end
```
這邊用了一點進階的語法
except是除了哪些其他都要跑before_action
也可以用only指定只有那些要跑before action
至於下面的unless中文翻成除非就很好懂了ㄅ
## Tasks
就是成發啦哈哈
可以選擇下面幾個提議中的其中一個來做
或是自己想,但決定前可以先跟講師討論一下
一組只要做一份就好了喔,如果可以的話也可以試試用github分工?
做完之後可以架到我們提供的伺服器與網域上喔
- 讓別人新增留言
- 允許上傳檔案/文字使用markdown渲染
- 加上username並允許用username&email登入
- add css (bootstrap or tailwindcss)
- 讓使用者只能編輯自己發的文
痾如果到時候我上課決定講某些的話就不算