\( RUBY\)
\(ON\)
\(RAILS\)

\(INDEX\)

Lecturer 

蔡嘉晉

cjtsai 失蹤了

因為AaW一直說要不要學Ruby on Rails所以來當RoR講師

 

在競程中迷失了自我跑來學網頁

會發現

喔好好玩喔

不用思考就能做出東西了欸

沒 上面純屬唬爛 不要打我

\( NOTIFY \)

以下指令僅在 Mac 與 Ubuntu (wsl & server) 測試過

如果你用windows 請先自行設定好環境(wsl)

昨天應該已經講過了

\(INTRODUCTION\)

Wats Ruby On Rails

RUBY

RAILS

好像沒毛病(?

Generated by stable diffusion with this model and this lora

我推的孩子 星野瑠美衣

Wats Ruby

  • 一種物件導向、指令式、函數式、動態的通用程式語言

 

  • 減少編程時候不必要的瑣碎時間,令編寫程式的人高興,是設計Ruby語言的Matz的一個首要的考慮;其次是良好的介面設計。他強調系統設計必須強調人性化,而不是一味從機器的角度設想

 

  • 人們特別是電腦工程師們,常常從機器著想。他們認為:「這樣做,機器就能執行的更快;這樣做,機器執行效率更高;這樣做,機器就會怎樣怎樣怎樣。」實際上,我們需要從人的角度考慮問題,人們怎樣編寫程式或者怎樣使用機器上應用程式。我們是主人,他們是僕人。

Wats Rails

  • 是ruby的一個gem(套件)
  • 依附ruby而生的一個動態網頁應用框架
    • 比其他可以寫動態網頁的框架簡單多ㄌ
  • 努力使自身保持簡單,使實際應用開發時的程式碼更少
  • 慣例優於設定
    • 有一套作者定義的程式碼撰寫與變數命名邏輯
    • 不同人的rails可能類似 方便借鑑、理解
  • 檔案有點多 但是架構很明瞭 熟悉的人很容易找到要修改的地方
  • MVC架構
    • Models, Views, Controllers

Why Ruby On Rails

Why

因為你們接幹後要維護一個叫iscoj的東西而且他是用RoR寫的
一個相對簡單的動態網頁框架
讓你不用為了寫SQL而煩惱
讓你的CAPSLOCK多休息幾天

 

雖然但是 動態網頁終歸是要學的 對吧
但 這就是下學期的事了 而且不關我的事

INSTALLATION

FOR MAC AND LINUX

Installation For Ruby

因為你可以要在不同的ruby專案用不同的版本
所以裝rvm
aka ruby version manager

為了要裝rvm要先裝一個叫gpg的東西

在終端機複製上這些

sudo apt install gnupg2

等一下 終端機是什麼

  • 在Mac上 cmd+space 輸入terminal然後按enter 就是那個
  • 在Linux上 我不相信你不知道
brew install gnupg

Linux

Mac

如果你沒有brew

/bin/bash -c "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/HEAD/install.sh)"

Installation For Ruby

接下來就可以裝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

RVM INSTALL

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的指令可能要跑有點久

先下了我們繼續講

Installation For Rails

這樣就可以了

gem install rails

INSTALLATION

FOR WINDOWS

並沒有

裝WSL

回到上一頁

 

或是參考這個

https://hotframeworks.com/railsinstaller-org/

RUBY SYNTAX

Intro

ruby是一個高度物件導向的程式語言,會把程式中各種東西包裝成物件,類似於c++中的struct


在寫初階的Rails的時候,其實不需要用到太多的Ruby語法,我自己跟Ruby語法其實也不是很熟悉,只會用到常見的邏輯運算與條件判斷

Variables

ruby變數沒有型別之分
但卻有不同的前綴與類別之分

類別 例子 解釋
區域變數 (無前綴) name 只能在目前的區塊用的變數,不能從controller傳到view,也不能在view/view或controller/controller之間互傳
全域變數 $name 不太會用到
實體變數 @name 超級常用,從controller傳變數給view就是要用實體變數才傳的過去(等下會解釋),開頭記得接個@
類別變數 @@name 也不會用到

Conditions

if
    #do something
elsif
    #do something
else
    #do something
end
# 對了註解是 # 喔

相信大家對於程式語言都有一些基礎的了解就不多做解釋了
記得每個區塊結束都要end
只有if也要

 

然後elsif 很噁心我知道

Loops

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(?"

\(ROR \ BASIC\)

NEW PROJECT

新建專案

要開始寫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 ARCHITECTURE

Ruby on Rails架構

RoR採用了MVC架構 即

  • Models
  • Views
  • Controllers

來進行整體的運行 可謂是Rails的核心

credit: railsbook.tw

MVC-Routes

雖然Route沒有被包含在MVC的命名之中

但從上頁之圖亦可見其重要性

Route是你存取一個網頁後先透過route決定要去哪個controller的哪個action 

雖然Route沒有被包含在MVC的命名之中

但從上頁之圖亦可見其重要性

🌰

MVC-Controllers

Controller則是會有很多的函示定義哪個路徑要做甚麼事,同時串接view&model,將model的資料傳遞給view進行讀取與渲染

🌰

MVC-Views

Views是使用者看到的畫面,透過 .html.erb(嵌入式ruby)可實現迴圈式及各種渲染

🌰

MVC-Models

Models則是儲存各種資料庫的地方,可定義其資料庫的關聯性並互相存取

🌰

檔案導覽

新生成的Rails專案檔案太多了

知道了MVC架構之後我們來帶大家看看哪些東西分別在哪吧

pwd: hello_rails

Gemfile

hello_rails/Gemfile

我們先來看在最外層的東西

先前提過Gem是ruby新增套件的方式

就像python 的 pip 

在這邊加上

gem "module-name"

之後終端機運行

bundle install

就可以用這個gem了

有些gem比如devise會要求額外的動作

請詳閱doc

其他根目錄的東西

README.md

  • 你push上github後會顯示在主頁的東西

Dockerfile

  • 可以創立一個獨立的虛擬機器環境並在其中執行rails server
  • 目前不會用到
  • 我們提供的伺服器裝不太了docker所以不要亂試
  • 一組只有一台伺服器喔

其他不是很重要

hello_rails/

app/

hello_rails/app

  • app
    • assets 用不太到
    • channels 用不太到
    • controllers
      • 所有的MVC架構中的controllers都會放在這裡
    • helpers 可以放一些好用的快捷指令
    • models
      • 所有的MVC架構中的models都會放在這裡
    • views
      • 所有的MVC架構中的views都會在這裡有自己的資料夾並存放index.html.erb之類的檔案

bin/

放各種執行檔

或是bash script

可以用

bin/rails 之類的執行

沒提到不要亂執行以免把東西戳爛

hello_world/bin

config/

hello_rails/config

  • config
    • environments
      • development.rb and others
        • 用來設定各種環境 像是網域名
    • application.rb
      • 有各種rails-application的預設參數
    • routes.rb
      • MVC架構第一個進到的地方
      • 透過他分派東西出去給各個controller執行

db/

hello_rails/db

  • db
    • migrate
      • 各種migration發生時的紀錄檔
      • rails db:migrate就是吃這裡還沒被跑過的檔案
    • schema.rb
      • 告訴你目前資料庫的架構以及各個欄位的資料型態
    • seeds.rb
      • 可以預設model

      • 例如在user中生一個admin出來

Others

hello_rails/$$

  • lib
    • 各種引用的library
  • log
    • 執行&debug的日誌
    • 可能有error code在裡面
  • public
    • 在網頁載入時預先load好的各種公開檔案
    • 包括404/500時的錯誤畫面或一些常見圖片如icon
  • test
    • 撰寫各種測試的地方

ROUTES

Routes.rb

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

接下來專案的時候會一一講解

Routes寫法

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 則是網頁各種存取伺服器的方式

Routes寫法

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

Routes寫法

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

他就會自動跑到這個網址來了

Routes寫法

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的變數去資料庫裡面找東西並將相對應的內容印在畫面上給使用者

Routes寫法

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寫法

routes的語法就差不多到這些

要注意的是rails找尋路徑的時候會從上往下找

找到一個符合的走進去了

所以如果像是posts/:id 會跟 posts/new 衝突的話

記得要把new擺在前面以免new被當作id傳了過去就會找不到

MODELS

Models

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

Models

rails generate model model_name column_name: column_type ...

在 Rails 專案中,Model 的命名是單數(而且必須大寫,因為在 Ruby 的類別名稱必須是大寫),而資料表則是預設使用複數並以小寫及底線分隔方式命名,如:

​Model 名稱 資料表名稱
User users
BlogPost blog_posts
Category categories

Models

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

MODELS ASSOSSIATIONS

Models關聯性

有時候有些資料會有關連的問題,讓我們想要從一個資料庫去存取其他資料庫的項目,就一個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 (按照字典序並把中間加一個底線)

models add column

這會幫你生出一個migration檔 可能有東西 可能是空的 要自己視情況調整

rails generate migration add_email_to_users email:string

VIEWS&CONTROLLERS

Views

什麼是view?

就是你要變成html給使用者看的畫面

其實寫法跟靜態網頁差不多 只有幾個要注意的點

在app/view/layouts的資料夾下面有一個application.html.erb

那是整個app都會預先載好的背景

可能包括選單列之類

中間的一個 <%=yield%>

則是在不同的controller下的不同action時會載入的不同分頁
 

Views

你會發現所有檔案都是以 xxx.html.erb的方式呈現

.html.erb中的.erb又稱嵌入式ruby

讓你可以在html中寫ruby

批次的渲染重複的東西並執行程式語言的條件判斷
嵌入方法共分兩種
在erb模版裡

Ruby程式碼會放在 <% %> 或是 <%= %> 標籤裡

  • <% %> 
    • 用來執行不會回傳任何值的 Ruby 程式碼
    • 例如條件判斷、迴圈或是區塊等等
  • <%= %>
    • 用來輸出結果 像是裡面放一個變數就會輸出變數的值

Controllers

controller是一個承接route發來的各種請求的東西,他會去進行一些身分驗證、在資料庫挖東西或是運算的工作,將view渲染東西所需要的資料們透過@變數傳遞過去

\(PROJECT\)

BLOG

專案實作

因為不知道要做什麼 又為了在實用性與難度間平衡

來做一個可以讓大家亂講話的blog吧

 

實例可以參考我做的這個css醜到哭的網站

如果傷到你們眼睛我很抱歉  後面那個顯然好看多了 但網址有點傻逼

https://ruby.cjtsai.com       https://bclaiisdasabi.cjtsai.com

GitHub: https://github.com/ckeisc43rd-cjtsai/rails-blog

 

你們在上完之後也能做出一個類似的

但有一些功能可能要自己想想看怎麼加

我們也會透過伺服器與反向代理讓你們各組的網站可以在外網存取

相信你們已經決定好你們小隊要的網域名了

by bclai

ANALYSIS & STARTUP

分析

  • 這個專案包含了哪些model?
    • user, post?
    • 關聯性?
      • user has_many posts
      • post has_one user
      • 我們這邊先做到可以發文,想幫他加關聯性的可以參見附錄或我的github
    • 這些model要包含哪些關聯?
      • user
        • register, login, password hash, change password, username
      • post
        • create, read, update, delete

先來創專案

確認要做什麼之後就可以開始了

先創一個專案,可以先在專案裡面逛逛

rails new blog

POST MODEL

Post Model

這個model要存兩個欄位,分別是title跟body,分別代表標題跟內文,title用的是string因為他比較短,body則是text因為是長篇文章

rails g model posts title:string body:text

可以透過編輯他更改你實際要的欄位
也可以透過手打migrate檔案來達到像上面 rails g 的功能
然後要跑

rails db:migrate

來實際執行剛剛的資料庫遷移

Console

現在你有一個叫post的資料庫了
可以進到

rails console

或是簡寫成rails c

進入後可以進行測試的操作

Console

  • 第001行的時候透過Post.new新建了一個物件並存入a中(注意大寫)
  • 第002行便可以透過打這個變數檢視他
  • 第003行執行a.save會將a這個物件實際寫入Post的資料庫裡面
  • 第004行的Post.all則是顯示Post資料庫的所有物件,可以看到就包刮了剛存進去的a
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>]

POST

CONTROLLER & VIEW

INDEX NEW 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到主頁

Rails.application.routes.draw do
  root "posts#index"
end

config/routes.rb

第一個要建立的便是首頁,英文稱index
還記得前面說的route嗎
rails是先透過route去知道特定網址要存取哪個controller的哪個action
所以我們要先設定posts#index到主頁

把根給過去就好ㄌ
其他之後再說

Index Action

現在進入網址會取存取這個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

Index Action

然後要在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%>

如果沒有這個檔案要自己新增喔

Index Action

如果剛剛有在rails console新增物件的話,現在打開http://127.0.0.1:3000/ 就會看到

這樣就成功ㄌ

接下來我們要為posts完善剛剛提到的CRUD,也就是Create(建立)、Read(讀取)、Update(更新)、Delete(刪除)

New Post Route

我們照著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存起來時呼叫

也是使用者按下按鈕時要進行的動作

New Post Controller

app/controllers/post_controller.rb

def new
    @post = Post.new
end

因為要存新的post,我們就先開一個新的到時候再填欄位

View Template

這邊來到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

New Post View

app/views/posts/new.html.erb 一樣沒有的話自己新增

<h1>New Post</h1>

<%= render partial: "form", locals: {post: @post}%>
  • 這裡的render partial 的功能可以讓我們將一部分交給模板來渲染

 

  • locals則是把變數傳入 這邊的@post是在controller new出來的那個

 

  • 之後按下create之後rails才會知道要把什麼東西存進資料庫

New Post View

如果你這時候開啟了網頁並進到 localhost:3000/posts/new
這邊有個form.button,由於rails的慣例優於設定準則,他偷偷的發現你這裡叫做new
所以把這個button叫做了create post,同時他會在你按下他的時候對post controller發送create這個action的請求,所以我們需要寫一個action接受他並在這時候把他存到資料庫裡面

Create Post Controller

這邊會發現有個沒看過的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

Create Post Controller

這樣就可以新增postㄌ
localhost:3000/posts/new
打完之後按下create,會發現
欸?報錯了
沒關係 這是因為寫了 redirect_to @post
但是你還沒幫每個post寫自己的show,所以重新導向過去就爛了


但是如果回到主頁就會發現他已經被存下來囉

 

現在來幫每個post寫自己的頁面吧

POST

CONTROLLER & VIEW

SHOW UPDATE DELETE

Show Post Route

這個應該蠻簡單的ㄅ
要先加route
config/routes.rb

get "/posts/:id", to: "posts#show", as: :post

這樣寫的話你在posts後面接東西他就會默認他是id

然後變成參數傳給show這個controller
這樣就能透過id在資料庫裡面找post了

Show Post Controller

app/controllers/post_controller.rb

 

def show
    @post = Post.find(params[:id]) 
end

他透過了id這個parameter在Post這個model的資料庫中找到了對應的@post並傳給view

Show 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 要顯示的文字, 要去的地方

Update Post Route

一樣先加route
config/routes.rb

get "/posts/:id/edit", to: "posts#edit", as: :edit_post
patch "/posts/:id", to: "posts#update"

上面那個是存取edit這個頁面的route
下面的則是要進行修改的動作時的route

Update Post Controller

我們的controller在傳給view的時候,應該要先找到你要改的post的資料,先作為預設資料放到欄位中,然後讓使用者直接進行修改,而不需要直接重打

app/controllers/post_controller.rb

def edit
    @post = Post.find(params[:id]) 
end

這邊就跟show一樣透過網址的id找到Post
接下來傳給view

Update Post View

因為前面有做過form的格式了
可以再render partial一次

views/posts/show.html.erb

 

<h1> Edit Post</h1>
<%= render partial: "form", locals: {post: @post}%>

你會發現rails又再一次偷看你的東西了

進到 localhost:3000/posts/1/edit 的話

下面的按鈕會自己叫update post

因為前面有做過form的格式了
可以再render partial一次

Update Post View

然後我們需要讓別人有辦法透過按按鈕找到這個edit的地方
所以我們就在show的時候下面加個button來導向到這個頁面吧
在最下面加上

views/posts/show.html.erb

<%=button_to "Edit", edit_post_path(@post), method: :get%>

因為button_to預設的是post,所以要切換成get才能是切換網頁

Update Post Controller

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就可以修改了

Delete Post Route

一樣先加route(我絕對沒有複製貼上)
config/routes.rb

delete "/posts/:id", to: "posts#destroy"

Delete Post Controller

然後加controller
根據現在的id去找到對應的資料庫物件並刪掉

app/controllers/post_controller.rb

def destroy
    @post = Post.find(params[:id]) 
    @post.destroy
    redirect_to root_path
end

因為東西都被刪掉了所以我們最後重新導向到目錄

Delete Post View

我們也需要把這個delete的button顯示出來,位置同edit

views/posts/show.html.erb

 

<%= button_to "Delete", @post, method: :delete, data: { turbo_confirm: 'Are you sure?' } %>

Conclusion

現在我們把一個blog該有的功能都做完了

也就是CRUD

但現在誰都能在上面亂講話

或許可以加個User讓大家要先登錄再發post(?

USER MODEL

INTRO

因為要自己刻一個user實在太麻煩了

尤其是password的各種hash不能讓人可以輕易回推
所以我們交給套件吧,得益於rails優秀的套件管理系統,加gem就跟python一樣簡單
在Gemfile加上

gem "devise"

Gemfile在專案的根目錄喔

然後在終端機跑

bundle install
rails g devise:install

因為devise是個挺大的套件所以我們裝完之後還要跑他自己的install指令

現在我們就有devise這個套件了
他支持各種的註冊、登入、更改密碼
並且有預設好的view可以用

DEVISE USER MODEL

這樣就可以在 localhost:3000/user/new 新增user了

為了方便我們要在主頁加上登入登出以及修改個人資料的按鈕
這東西應該在我們在每個頁面的時候都能存取

rails g devise user

RAILS VIEW LAYOUT

這邊重新介紹一下rails view的架構
他是透過 views/layout/application.html.erb 當作底層
其中有一個 <%yield%> 則是在你定義的不同view時會把那一小部分render出來
所以如果我們在application.html.erb新增東西的話,不管在哪裡這些東西都會被顯示出來

View Layout

這邊檢查了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

Redirect to Root

同時我們會發現每次要回到首頁有點麻煩
所以可以在這裡的最頂部加上root_path的連結

<h1><%=link_to "Blog", root_path %></h1>

views/layout/application.html.erb

Post Button

要讓大家發文變得比較簡單
可以在主頁加上發新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內建的函示

會回傳一個布林值讓你方便判斷

Authenticate

沒登入的人理論上只能看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中文翻成除非就很好懂了ㄅ

\(ADDITIONAL\)

How to add bootstrap to your project

 by BCLAI 

因為我的css實在是太醜啦哈哈

What is Bootstrap

Bootstrap是一組用於網站和網路應用程式開發的開源前端框架,包括HTML、CSS及JavaScript的框架,提供字體排印、表單、按鈕、導航及其他各種元件及Javascript擴充套件,旨在使動態網頁和Web應用的開發更加容易。 

ADD BOOTSTRAP

要先裝node.js喔

sudo apt install nodejs
sudo apt install npm

ADD BOOTSTRAP

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()

ADD BOOTSTRAP

#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 啦

ADD BOOTSTRAP

但 要怎麼在<%=  %> 加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>

ADD BOOTSTRAP

再說一件事

前面的步驟其實都不太需要

只要

rails new blog -j esbuild --css bootstrap

就好囉 哈哈

Made with Slides.com