瞭解Scaffold 的程式流程是怎麼跑後, 再來就由它的程式碼來學習囉~
先看下述幾個頁面:
- Controller: app/controllers/posts_controller.rb
- Model: app/models/post.rb
- View: app/views/posts/edit.html.erb # 編輯
- View Layout: app/views/layouts/posts.html.erb # 此檔案是 posts html layout 的 template 檔.
下述的內容可搭配此圖一起看:
Index Controller 流程
- 進入 Controller index function
- Post find 去將 Model 的 Post class 產生實體(new)回傳
- 決定要秀 HTML 頁面 或 XML 頁面
- View / Layout
進入 Controller index function
程式進入點是 /posts 或 /posts.xml 預設就是會跑 Controller index function.
先看 Controller: app/controllers/posts_controller.rb 的 index function.
class PostsController < ApplicationController
# GET /posts
# GET /posts.xml
def index
@posts = Post.find(:all)respond_to do |format|
format.html # index.html.erb
format.xml { render :xml => @posts }
end
end
end
由此可知 Controller 是繼承自 ApplicationController(app/controllers/application.rb), 而註解寫到的就是存取此 index 可以用 GET /posts 和 GET /posts.xml 兩種方式, 來分別看到 View 的 Template 結果和 XML 結果.
def index
def index 是 function, 到底是對應到哪個頁面, 可由下述步驟查詢:
bash$ rake routes 可以看到:
- posts GET /posts {:action=>"index", :controller=>"posts"}
- formatted_posts GET /posts.:format {:action=>"index", :controller=>"posts"}
此處的 def index
就是 routes 的 :action => "index"
Post find 去將 Model 的 Post class 產生實體(new)回傳
@posts = Post.find(:all)
到此處時, 這邊就是使用到 Model 的 Post class, 這段程式的流程會從 Post.find(:all) 直接進入 Model, 完成後再繼續下面的處理.
於 Post.find(:all) 可見: Model: app/models/post.rb, 程式碼只有下面2行:
class Post < ActiveRecord::Base
end
find() 這是於 ActiveRecord 的 function, 因為 class Post 是繼承自 ActiveRecord, 所以可直接使用, 也是於此做其它資料正確性的檢查.
決定要秀 HTML 頁面 或 XML 頁面
respond_to do |format|
再來看最後輸出, 要輸出前為何還要加 respond_to? 且同一個 function 是如何輸出兩種不同的結果?
於程式可以看到 respond_to 裡面有包兩個輸出檔: format.html / format.xml, 由此可知是由此產生 HTML/XML 檔輸出.
respond_to 判斷何時要送 HTML, 何時要送 XML, 主要是靠 "HTTP 的 Accept-Type", 若不用 respond_to 的話, 程式得要改寫成下述:(參考: REST on Rails指南5: respond_to)
def index(client_format)
@posts = Post.find(:all)if client_format == "text/html"
# TO DO: render the default template
elsif client_format == "application/javascript"
# TO DO: return some javascript
elsif client_format == "application/xml" || client_format == "text/xml"
# TO DO: return some XML back the client
# ... more elsif statements here for each MIME type you want to support
end
end
因為 respond_to 處理掉上述的事情, 所以就可以用下述存取看看, 就可以得到不同的結果:
- http://DOMAIN/posts/1
- http://DOMAIN/posts/1.xml
View / Layout
format.html # index.html.erb
於 format.html 這邊, 它就會去讀取 View 的 index.html.erb 檔, 而 View 裡面也有固定的 Layout file 做定義.
先看 Layout file: (只看 body 部份)
<body>
<p style="color: green"><%= flash[:notice] %></p>
<%= yield %>
</body>
<%= flash[:notice] %>
此段變數的訊息會從 Controller 取出, 於 Create / Update 的 Controller 完成後, 就會看到 Post was successfully created. 等訊息.
<%= yield %>
而此行, 就會把 View 的內容放進去(取代 yield), 例如 View 的編輯頁面:
View: app/views/posts/edit.html.erb, 內容大致如下:
<%= error_messages_for :post %>
<% form_for(@post) do |f| %>
<p>
<b>Id</b><br />
<%= f.text_field :id %>
</p>
<%= error_messages_for :post %>
此行就是 Model 有錯誤時, 錯誤訊息(:message)會於此印出. ex: 若於 Model 加入此段: validates_length_of :title, :minimum => 20, :message => "最少要 %d 字元", 那 title 若沒有輸入超過 20個字, 就會顯示錯誤訊息(若有任何錯誤, 資料就不會被新增/修改).