cjtsai
失蹤的c++殭屍
蔡嘉晉
cjtsai 失蹤了
因為AaW一直說要不要學Ruby on Rails所以來當RoR講師
在競程中迷失了自我跑來學網頁
會發現
喔好好玩喔
不用思考就能做出東西了欸
沒 上面純屬唬爛 不要打我
以下指令僅在 Mac 與 Ubuntu (wsl & server) 測試過
如果你用windows 請先自行設定好環境(wsl)
昨天應該已經講過了
Generated by stable diffusion with this model and this lora
我推的孩子 星野瑠美衣
因為你們接幹後要維護一個叫iscoj的東西而且他是用RoR寫的
一個相對簡單的動態網頁框架
讓你不用為了寫SQL而煩惱
讓你的CAPSLOCK多休息幾天
雖然但是 動態網頁終歸是要學的 對吧
但 這就是下學期的事了 而且不關我的事
因為你可以要在不同的ruby專案用不同的版本
所以裝rvm
aka ruby version manager
為了要裝rvm要先裝一個叫gpg的東西
在終端機複製上這些
sudo apt install gnupg2
等一下 終端機是什麼
brew install gnupg
Linux
Mac
如果你沒有brew
/bin/bash -c "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/HEAD/install.sh)"
接下來就可以裝rvm了
gpg2 --keyserver keyserver.ubuntu.com --recv-keys 409B6B1796C275462A1703113804BB82D39DC0E3 7D2BAF1CF37B13E2069D6956105BD0E739499BDB
\curl -sSL https://get.rvm.io | bash
有了rvm就可以下載特定版本的ruby
Linux要多做一個步驟
每次需要用到rvm / ruby / rails相關的都要打一次
/bin/bash --login
Linux
rvm install 3.2.0
此三依序為
裝某個版本的Ruby
列出已安裝過的Ruby 版本
使用某個版本的Ruby
Mac
可以先試試左邊的
如果不行的話 這個裝的是3.2.0
rvm reinstall 3.2.0 --with-openssl-dir=$(brew --prefix openssl) --with-readline-dir=$(brew --prefix readline) --with-libyaml-dir=$(brew --prefix libyaml) --disable-dtrace --disable-docs
rvm install <ruby_version>
rvm list
rvm use <ruby_version>
install的指令可能要跑有點久
先下了我們繼續講
這樣就可以了
gem install rails
ruby是一個高度物件導向的程式語言,會把程式中各種東西包裝成物件,類似於c++中的struct
在寫初階的Rails的時候,其實不需要用到太多的Ruby語法,我自己跟Ruby語法其實也不是很熟悉,只會用到常見的邏輯運算與條件判斷
ruby變數沒有型別之分
但卻有不同的前綴與類別之分
類別 | 例子 | 解釋 |
---|---|---|
區域變數 (無前綴) | name | 只能在目前的區塊用的變數,不能從controller傳到view,也不能在view/view或controller/controller之間互傳 |
全域變數 | $name | 不太會用到 |
實體變數 | @name | 超級常用,從controller傳變數給view就是要用實體變數才傳的過去(等下會解釋),開頭記得接個@ |
類別變數 | @@name | 也不會用到 |
嗯
if
#do something
elsif
#do something
else
#do something
end
# 對了註解是 # 喔
相信大家對於程式語言都有一些基礎的了解就不多做解釋了
記得每個區塊結束都要end
只有if也要
然後elsif 很噁心我知道
loop有各種進階操作可自行上網查詢或參閱 這個網站
while boolean
do do something
end
#friends是一個陣列
for friend in friends
puts friend
end
for i in 1..5 do
puts i
end
此外ruby使用puts進行基本輸出
#輸出是puts
puts "ruby is cool(?"
要開始寫Ruby on Rails最開始當然是建立一個專案了
rails new hello_rails
請記得先走到你要的路徑再打
這個指令會在 當前 的位置創一個資料夾以及生出所有的檔案
如果要指定專案位置的話
rails new hello_rails .
最後面的那點代表當前資料夾 也就是你已經開好一個空的資料夾了
要他生成所有檔案在目前位置
當然也可以是絕對位置
rails new hello_rails ~/webruby/winter
這樣便會生成在家目錄下的webruby下的winter中
這邊推薦使用vscode開啟整個專案
畢竟進去之後檔案有點太多了
而且常常也會需要同時更改多個檔案
使用終端機自帶的編輯器如vim會顯得略為不便
使用vscode開啟整個專案後
可以看到下面有個終端機
可先pwd確認目前路徑是否為整個專案之根目錄
接下來使用
bundle install
這會為你更新好所有需要使用的套件
先別被側邊欄那一大堆檔案嚇到了
接下來會慢慢介紹會使用到的部分
既然是個動態的網頁
肯定是要先嘗試把整個專案開起來
rails server
接下來到 localhost:3000 查看一下便會發現有東西囉
然後因為工程師很懶所以可以簡寫成 rails s
CTRL+C就可以把它終止了喔
如果你是用我們提供的伺服器的話
則可以直接到你們小隊選擇的網域名去看看真的會有東西喔
阿如果你們小有不只一個人使用的話port不能重複
可以用以下的指令開在不同的port
rails s -p <port>
port可以選擇3001或附近的 組內可以協調一下
如果希望3001 port之類的也可以連接到外網可以跟我講
port可以選擇3001或附近的 組內可以協調一下
如果希望3001 port之類的也可以連接到外網可以跟我講
既然是個網頁後端
肯定是要長時間放在伺服器執行的
rails s -d
這是run as daemon的意思 也就是背景執行
當然也可以搭配-p使用
然後你會發現 欸不是我沒有地方按CTRL+C了怎麼終止
重開機嗎
確實不是不行
但我們也可以偵測3000口正在跑甚麼東西並把他停掉
kill $(lsof -i :3000 -t)
把這些東西放到~/.bashrc中
記得把rails-directory換成你自己放的位置
可以用pwd查看
function railsdown(){
kill $(lsof -i :3000 -t)
}
function railsup(){
cd <rails-directory> && rails s -d
}
這一長串的有夠難記
我要用幾個字來執行
source ~/.bashrc
這樣之後就能用railsup跟railsdown來開關rails server了
喔對zsh指令寫法直接把function刪掉就是了
RoR採用了MVC架構 即
來進行整體的運行 可謂是Rails的核心
credit: railsbook.tw
雖然Route沒有被包含在MVC的命名之中
但從上頁之圖亦可見其重要性
Route是你存取一個網頁後先透過route決定要去哪個controller的哪個action
雖然Route沒有被包含在MVC的命名之中
但從上頁之圖亦可見其重要性
Controller則是會有很多的函示定義哪個路徑要做甚麼事,同時串接view&model,將model的資料傳遞給view進行讀取與渲染
Views是使用者看到的畫面,透過 .html.erb(嵌入式ruby)可實現迴圈式及各種渲染
Models則是儲存各種資料庫的地方,可定義其資料庫的關聯性並互相存取
新生成的Rails專案檔案太多了
知道了MVC架構之後我們來帶大家看看哪些東西分別在哪吧
pwd: hello_rails
hello_rails/Gemfile
我們先來看在最外層的東西
先前提過Gem是ruby新增套件的方式
就像python 的 pip
在這邊加上
gem "module-name"
之後終端機運行
bundle install
就可以用這個gem了
有些gem比如devise會要求額外的動作
請詳閱doc
README.md
Dockerfile
其他不是很重要
hello_rails/
hello_rails/app
放各種執行檔
或是bash script
可以用
bin/rails 之類的執行
沒提到不要亂執行以免把東西戳爛
hello_world/bin
hello_rails/config
hello_rails/db
可以預設model
例如在user中生一個admin出來
hello_rails/$$
pwd: ~/config/routes.rb
這是你存取網頁是ror會第一個看得地方
先附一個範例
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
接下來專案的時候會一一講解
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
用上面的來講解
如果你進到你的網址的 /post/new 去看
他會請求 post_controller 裡面的 def new 這個方法
get / post / patch /delete 則是網頁各種存取伺服器的方式
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"
第一行的 get "/posts", to "posts#index"
會把 localhost:3000(或你的網域)/posts
轉到posts#index這個action
也就是post_controller下的def index中
def index
#do something
end
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"
第二行的 get "/posts/new",to:"posts#new",as: :new_post
則是把localhost:3000/posts/new轉到posts#new這個action
如上頁所述最後會走到def new
但後面還有一個as: :new_post
這個會讓我們之後在做重新導向的時候很方便
你可以直接跟他說我要重新導向到new_post_path
他就會自動跑到這個網址來了
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"
第四行的 get "/posts/:id", to: "posts#show", as: :post
則用到了變數式的路徑
在進入到localhost:3000/posts/<something>後
他會把後面的那串東西當作變數pass給posts#show這個action
所以我們在寫def show的時候就可以用這個叫做id的變數去資料庫裡面找東西並將相對應的內容印在畫面上給使用者
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"
剩餘的則會在接下來專案慢慢介紹
這七條route會是接下來寫專案的時候最常用到的各種route
所以rails很貼心的幫我們把這七條東西包成了一個
常用的簡寫
resources :posts
routes的語法就差不多到這些
要注意的是rails找尋路徑的時候會從上往下找
找到一個符合的走進去了
所以如果像是posts/:id 會跟 posts/new 衝突的話
記得要把new擺在前面以免new被當作id傳了過去就會找不到
Models在rails中負責儲存各種資料庫物件必提供相對應的操作
可以透過rails generate 來生出一個資料庫
rails generate model model_name column_name: column_type ...
這會幫你生好一個model跟一個資料庫
model的東西被放在/app/models
資料庫的東西則是pending migration被存在了/db/migrations
前面提過migrations需要執行rails db:migrate
來真正的執行他並更動資料庫架構
所以
rails db:migrate
rails generate model model_name column_name: column_type ...
在 Rails 專案中,Model 的命名是單數(而且必須大寫,因為在 Ruby 的類別名稱必須是大寫),而資料表則是預設使用複數並以小寫及底線分隔方式命名,如:
Model 名稱 | 資料表名稱 |
---|---|
User | users |
BlogPost | blog_posts |
Category | categories |
rails generate model model_name column_name: column_type ...
至於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 |
有時候有些資料會有關連的問題,讓我們想要從一個資料庫去存取其他資料庫的項目,就一個blog來說,一個user他可能有很多post,同時每個post只會有一個author。
我們可以總結出三種關聯關係
假設每個人有一台電腦,同時我們需要這台電腦的詳細資訊,且每台電腦也只屬於一個人,因為在user資料庫開不同欄位存電腦型號、大小、規格蠻不切實際的,同時為了貫徹OOP(物件導向)的精神,我們有兩個model分別存user跟computer
這時我們可以在 /app/models
中定義他們的關聯性
class User < ApplicationRecord
has_one :computer
end
app/models/user.rb
class Computer < ApplicationRecord
belongs_to :user
end
app/models/computer.rb
現在大家都變有錢了,可以買很多台電腦,但每台電腦還是都只屬於一個人,就會用到一對多的關聯性
class User < ApplicationRecord
has_many :computers
end
app/models/user.rb
class Computer < ApplicationRecord
belongs_to :user
end
app/models/computer.rb
其實差不多,但你會發現在 user.rb 的地方,不只是has_one被改成has_many,computer也被改成複數了,畢竟有很多電腦對吧
現在大家都變有錢了,可以買很多台電腦,但每台電腦還是都只屬於一個人,就會用到一對多的關聯性
因為大家共有一台電腦太奇怪了,所以我們換種講法,現在電腦的資料庫裡面代表的是一種型號,這樣同型號的電腦可能被很多人持有,即使他們不是同一台,我們可以這樣寫讓每個人有很多種型號的電腦,一個型號的電腦也被很多人持有
class User < ApplicationRecord
has_and_belongs_to_many :computers
end
app/models/user.rb
class Computer < ApplicationRecord
has_and_belongs_to_many :users
end
app/models/computer.rb
這樣就完成model的關聯性了
models有關聯性有什麼用?
這邊一樣等實作的時候再講
這時候你會發現你需要一個第三方的資料表紀錄他們的關係
對於每台電腦紀錄他的持有者以及型號,還有自身的流水編號
即會有一個資料庫其column為
id, store_id, computer_id
至於這個第三方資料表則應該叫做 computers_stores
(按照字典序並把中間加一個底線)
這會幫你生出一個migration檔 可能有東西 可能是空的 要自己視情況調整
rails generate migration add_email_to_users email:string
什麼是view?
就是你要變成html給使用者看的畫面
其實寫法跟靜態網頁差不多 只有幾個要注意的點
在app/view/layouts的資料夾下面有一個application.html.erb
那是整個app都會預先載好的背景
可能包括選單列之類
中間的一個 <%=yield%>
則是在不同的controller下的不同action時會載入的不同分頁
你會發現所有檔案都是以 xxx.html.erb的方式呈現
.html.erb中的.erb又稱嵌入式ruby
讓你可以在html中寫ruby
批次的渲染重複的東西並執行程式語言的條件判斷
嵌入方法共分兩種
在erb模版裡
Ruby程式碼會放在 <% %>
或是 <%= %>
標籤裡
<% %>
<%= %>
controller是一個承接route發來的各種請求的東西,他會去進行一些身分驗證、在資料庫挖東西或是運算的工作,將view渲染東西所需要的資料們透過@變數傳遞過去
因為不知道要做什麼 又為了在實用性與難度間平衡
來做一個可以讓大家亂講話的blog吧
實例可以參考我做的這個css醜到哭的網站
如果傷到你們眼睛我很抱歉 後面那個顯然好看多了 但網址有點傻逼
https://ruby.cjtsai.com https://bclaiisdasabi.cjtsai.com
GitHub: https://github.com/ckeisc43rd-cjtsai/rails-blog
你們在上完之後也能做出一個類似的
但有一些功能可能要自己想想看怎麼加
我們也會透過伺服器與反向代理讓你們各組的網站可以在外網存取
相信你們已經決定好你們小隊要的網域名了
by bclai
確認要做什麼之後就可以開始了
先創一個專案,可以先在專案裡面逛逛
rails new blog
這個model要存兩個欄位,分別是title跟body,分別代表標題跟內文,title用的是string因為他比較短,body則是text因為是長篇文章
rails g model posts title:string body:text
可以透過編輯他更改你實際要的欄位
也可以透過手打migrate檔案來達到像上面 rails g 的功能
然後要跑
rails db:migrate
來實際執行剛剛的資料庫遷移
現在你有一個叫post的資料庫了
可以進到
rails console
或是簡寫成rails c
進入後可以進行測試的操作
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>]
現在我們有一個能用的資料庫了
接下來就要有個能夠操控他並且負責應對各種網路存取的controller
rails g controller posts
會在 app/controllers 生出一個 post_controller.rb
他也會順便幫你生好view畢竟有controller就有view
第一個要建立的便是首頁,英文稱index
還記得前面說的route嗎
rails是先透過route去知道特定網址要存取哪個controller的哪個action
所以我們要先設定posts#index到主頁
Rails.application.routes.draw do
root "posts#index"
end
config/routes.rb
第一個要建立的便是首頁,英文稱index
還記得前面說的route嗎
rails是先透過route去知道特定網址要存取哪個controller的哪個action
所以我們要先設定posts#index到主頁
把根給過去就好ㄌ
其他之後再說
現在進入網址會取存取這個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
class PostsController < ApplicationController
def index
@posts=Post.all
end
end
然後要在view的地方讓傳遞過去的@posts變數渲染出來
這邊相信大家對於基本的html tag有一些基本的了解
因為是index action所以檔名要長這樣
app/views/posts/index.html.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(刪除)
我們照著route->controller->view的順序走
routes.rb加上這兩句
get "/posts/new", to: "posts#new", as: "new_post"
post "/posts", to: "posts#create", as: :posts
有兩個是因為要在new有一個新建post的頁面
渲染出讓使用者輸入新post內容的頁面
post(撞名了 這邊是指網頁操作的post)
則是要把posts存起來時呼叫
也是使用者按下按鈕時要進行的動作
app/controllers/post_controller.rb
def new
@post = Post.new
end
因為要存新的post,我們就先開一個新的到時候再填欄位
這邊來到view的部分,因為讓使用者填資料的表格等一下還會用到
乾脆就開一個模板來存,在rails中開模板的方式為用底線開頭的.html.erb
例如 _form.html.erb 或 _show.html.erb
<%= 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/_form.html.erb
app/views/posts/new.html.erb 一樣沒有的話自己新增
<h1>New Post</h1>
<%= render partial: "form", locals: {post: @post}%>
如果你這時候開啟了網頁並進到 localhost:3000/posts/new
這邊有個form.button,由於rails的慣例優於設定準則,他偷偷的發現你這裡叫做new
所以把這個button叫做了create post,同時他會在你按下他的時候對post controller發送create這個action的請求,所以我們需要寫一個action接受他並在這時候把他存到資料庫裡面
這邊會發現有個沒看過的post_params,這是什麼意思呢
因為rails覺得直接傳所有的參數回來有點危險,因此要求你對你需要的參數們進行允許
被允許的東西才能從網頁回來,因為需要常常用到就定義了一個函數允許會用到的參數們
app/controllers/post_controller.rb
def create
@post = Post.new(post_params)
if @post.save
redirect_to @post
else
render :new
end
end
在app/controllers/post_controller.rb最下面加上
private
def post_params
params.require(:post).permit(:title, :body)
end
這樣就可以新增postㄌ
在 localhost:3000/posts/new
打完之後按下create,會發現
欸?報錯了
沒關係 這是因為寫了 redirect_to @post
但是你還沒幫每個post寫自己的show,所以重新導向過去就爛了
但是如果回到主頁就會發現他已經被存下來囉
現在來幫每個post寫自己的頁面吧
這個應該蠻簡單的ㄅ
要先加route
config/routes.rb
get "/posts/:id", to: "posts#show", as: :post
這樣寫的話你在posts後面接東西他就會默認他是id
然後變成參數傳給show這個controller
這樣就能透過id在資料庫裡面找post了
app/controllers/post_controller.rb
def show
@post = Post.find(params[:id])
end
他透過了id這個parameter在Post這個model的資料庫中找到了對應的@post並傳給view
app/views/posts/show.html.erb
<h1> <%=@post.title%> </h1>
<div>
<%=@post.body%>
</div>
這樣承接了controller傳過來的post並顯示出來就好
記得要用<%=%>而不是<%%>
這邊還可以再改一個東西,理論上要能從主頁點到每個post的show
所以可以把每個post他們的title都變成超連結,寫起來也很簡單
把index.html.erb中顯示title的那一行變成這樣
<h2><%= link_to post.title, post %></h2>
語法是 link_to 要顯示的文字, 要去的地方
一樣先加route
config/routes.rb
get "/posts/:id/edit", to: "posts#edit", as: :edit_post
patch "/posts/:id", to: "posts#update"
上面那個是存取edit這個頁面的route
下面的則是要進行修改的動作時的route
我們的controller在傳給view的時候,應該要先找到你要改的post的資料,先作為預設資料放到欄位中,然後讓使用者直接進行修改,而不需要直接重打
app/controllers/post_controller.rb
def edit
@post = Post.find(params[:id])
end
這邊就跟show一樣透過網址的id找到Post
接下來傳給view
因為前面有做過form的格式了
可以再render partial一次
views/posts/show.html.erb
<h1> Edit Post</h1>
<%= render partial: "form", locals: {post: @post}%>
因為前面有做過form的格式了
可以再render partial一次
然後我們需要讓別人有辦法透過按按鈕找到這個edit的地方
所以我們就在show的時候下面加個button來導向到這個頁面吧
在最下面加上
views/posts/show.html.erb
<%=button_to "Edit", edit_post_path(@post), method: :get%>
因為button_to預設的是post,所以要切換成get才能是切換網頁
app/controllers/posts_controller.rb
def update
@post = Post.find(params[:id])
if @post.update(post_params)
redirect_to @post
else
render :edit
end
end
這邊先透過id找到你要改的是哪一則post
然後從view傳回來的東西找到要改成怎樣
最後用model內建的update就可以修改了
一樣先加route(我絕對沒有複製貼上)
config/routes.rb
delete "/posts/:id", to: "posts#destroy"
然後加controller
根據現在的id去找到對應的資料庫物件並刪掉
app/controllers/post_controller.rb
def destroy
@post = Post.find(params[:id])
@post.destroy
redirect_to root_path
end
因為東西都被刪掉了所以我們最後重新導向到目錄
我們也需要把這個delete的button顯示出來,位置同edit
views/posts/show.html.erb
<%= button_to "Delete", @post, method: :delete, data: { turbo_confirm: 'Are you sure?' } %>
現在我們把一個blog該有的功能都做完了
也就是CRUD
但現在誰都能在上面亂講話
或許可以加個User讓大家要先登錄再發post(?
因為要自己刻一個user實在太麻煩了
尤其是password的各種hash不能讓人可以輕易回推
所以我們交給套件吧,得益於rails優秀的套件管理系統,加gem就跟python一樣簡單
在Gemfile加上
gem "devise"
Gemfile在專案的根目錄喔
然後在終端機跑
bundle install
rails g devise:install
因為devise是個挺大的套件所以我們裝完之後還要跑他自己的install指令
現在我們就有devise這個套件了
他支持各種的註冊、登入、更改密碼
並且有預設好的view可以用
這樣就可以在 localhost:3000/user/new 新增user了
為了方便我們要在主頁加上登入登出以及修改個人資料的按鈕
這東西應該在我們在每個頁面的時候都能存取
rails g devise user
這邊重新介紹一下rails view的架構
他是透過 views/layout/application.html.erb 當作底層
其中有一個 <%yield%> 則是在你定義的不同view時會把那一小部分render出來
所以如果我們在application.html.erb新增東西的話,不管在哪裡這些東西都會被顯示出來
這邊檢查了user是否已經登入
如果已經登入便給他logout跟profile的連結
否則給他login跟signup的連結
至於後面的path則是透過devise自動定義的
這邊登出的method記得換成delete
<% 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 %>
views/layout/application.html.erb
同時我們會發現每次要回到首頁有點麻煩
所以可以在這裡的最頂部加上root_path的連結
<h1><%=link_to "Blog", root_path %></h1>
views/layout/application.html.erb
要讓大家發文變得比較簡單
可以在主頁加上發新post的連結
記得要登入才能發文喔
這邊要加在app/views/posts/index.html.erb
畢竟你不是希望大家在哪裡都看到這個按鈕
<%= button_to "New Post", new_post_path, method: :get if user_signed_in?%>
後面的if user_signed_in?是devise內建的函示
會回傳一個布林值讓你方便判斷
沒登入的人理論上只能看index跟每個post個別的show對吧
app/controllers/post_controller.rb
before_action :authenticate_user!, except: [:index, :show]
#請把上面這段加在post controller的最上面
#請把下面這段加在post controller的private區塊下面
def authenticate_user!
redirect_to new_user_session_path, alert: "SIGN IN TO CONTINUE" unless user_signed_in?
end
這邊用了一點進階的語法
except是除了哪些其他都要跑before_action
也可以用only指定只有那些要跑before action
至於下面的unless中文翻成除非就很好懂了ㄅ
因為我的css實在是太醜啦哈哈
Bootstrap是一組用於網站和網路應用程式開發的開源前端框架,包括HTML、CSS及JavaScript的框架,提供字體排印、表單、按鈕、導航及其他各種元件及Javascript擴充套件,旨在使動態網頁和Web應用的開發更加容易。
要先裝node.js喔
sudo apt install nodejs
sudo apt install npm
cssbundling-rails 跟devise 一樣是一個套件
他可以幫你安裝Tailwind CSS, Bootstrap, Bulma, PostCSS, or Dart Sass
用套件去安裝Bootstrap
rails css:install:bootstrap
#Gemfile
gem 'cssbundling-rails'
#console
bundle install
./bin/importmap pin @rails/ujs
include ujs => include ujs 才能在<%= %> 內加css
#app/javascript/application.js
#add
import Rails from "@rails/ujs"
Rails.start()
#app/views/layouts/application.html.erb
<script src=”https://cdn.jsdelivr.net/npm/bootstrap@5.3.2/dist/js/bootstrap.bundle.min.js" integrity=”sha384-C6RzsynM9kWDrMNeT87bh95OGNyZPhcTNXj1NW7RuBCsyN/o0jlpcV8Qyq46cDfL” crossorigin=”anonymous”></script>
include Bootstrap
./bin/importmap pin bootstrap
在HTML 裡引用Bootstrap
這樣就可以在你的rails app 裡加css 啦
但 要怎麼在<%= %> 加css呢
問就是通通包一個<div class=""></div>
<%= post.body class: "text-secondary" %>
<%= link_to blog_post.title, blog_post, class: "link-light" %>
<%= button_to "Delete", :class => "btn btn-secondary btn-outline-dark fw-bold mt-2" %>
真心建議直接包<div></div>
再說一件事
前面的步驟其實都不太需要
只要
rails new blog -j esbuild --css bootstrap
就好囉 哈哈
By cjtsai