# 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` 看到這個就代表你成功了 ![截圖 2023-11-22 上午10.36.05](https://hackmd.io/_uploads/By2HtJsNa.png) 因為工程師很懶 所以可以縮寫成 ``` rails s ``` 真的很懶= = ### MVC 架構 rails 運作的核心 ![image](https://hackmd.io/_uploads/r1vaM-wDa.png) - 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/`就會看到 ![截圖 2024-01-06 下午8.59.35](https://hackmd.io/_uploads/S1NT0TLd6.png) 這樣就成功了 接下來我們要為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) - 讓使用者只能編輯自己發的文 痾如果到時候我上課決定講某些的話就不算