當我們開始製作頁面的時候,很快會發現一件事情,沒有資料庫的網站終究只是個靜態頁面,但當我們想要結合資料庫時,接踵而來的問題就開始產生,例如怎麼連到資料庫?怎麼查詢資料?怎麼把資料丟到頁面?尤其是光想到要面對那些 SQL 語法就很頭痛
但也這就是 Rails Model 厲害的地方,你剛剛所想的問題都幫你處理好了,你只要有物件導向的觀念,會用所謂的 ORM(Object-relational mapping)
,就可以省下很多開發時間,讓你可以更專注在開發產品上面
也因為 Model 是如此重要,而且不容易了解,所以接下來會花比較多的篇幅來介紹 Model 的部分,為了循序漸進,我們先從透過操作 JSON 格式檔案來模擬資料庫,熟悉一下 Model 在做些什麼事情
在開始之前,要先在 Mavericks 安裝 multi_json
,我們將會用它做 JSON 轉成 Hash 的處理
# mavericks/mavericks.gemspec
.
.
(略)
spec.add_runtime_dependency "rack"
spec.add_runtime_dependency "erubi"
spec.add_runtime_dependency "multi_json"
這裡會建立一個叫 file_model
的 Class,之後會分段說明程式碼
# mavericks/lib/mavericks/file_model.rb
require "multi_json"
module Mavericks
module Model
class FileModel
def initialize(file)
@file = file
basename = File.split(file)[-1]
@id = File.basename(basename, ".json").to_i
obj = File.read(file)
@hash = MultiJson.load(obj)
end
def [](name)
@hash[name.to_s]
end
def []=(name, value)
@hash[name.to_s] = value
end
def self.find(id)
begin
FileModel.new("db/tasks/#{id}.json")
rescue
return nil
end
end
end
end
end
別忘了要記得 require
# mavericks/lib/mavericks.rb
# .
# .
# (略)
require "mavericks/file_Model"
module Mavericks
class Error < StandardError; end
class Application
# .
# .
# (略)
先看 initialize
,這段程式碼要做的事情是,從檔名中取得 ID
# mavericks/lib/mavericks/file_model.rb
def initialize(file)
@file = file
basename = File.split(file)[-1]
@id = File.basename(basename, ".json").to_i
obj = File.read(file)
@hash = MultiJson.load(obj)
end
File.split
會在 'just_do/db/tasks/1.json'
的字串中取得 1.json
,接著透過 File.basename
去掉 .json
的附檔名,就可以取得 id
接著再建立兩個 method,一個叫 []
,另一個是 []=
,用來對物件的 attribute
做 set 和 get
# mavericks/lib/mavericks/file_model.rb
def [](name)
@hash[name.to_s]
end
def []=(name, value)
@hash[name.to_s] = value
end
最後建立一個 class method
叫 find
,透過 id
來尋找單筆資料,就如同 ActiveRecord
的方式一樣
def self.find(id)
begin
FileModel.new("db/tasks/#{id}.json")
rescue
return nil
end
end
回到 just_do 的專案,來試試看 Mavericks 的 Model,前面提到,會用 JSON 格式檔案來模擬資料庫,就跟 Rails 一樣先建立一個 db/
資料夾,但不一樣的是我們運用 資料夾 + JSON 格式檔案
當作「資料庫」來使用,在 db/
底下再建立一個 tasks/
資料夾,這個 tasks/
可以看作是一個 table
,接著在 tasks/
裡面建立一個 1.json
可以看作是一筆資料,如下面所示
# just_do/db/tasks/1.json
{
"title": "完成30天鐵人賽",
"content": "每天寫一篇,連續30天"
}
然後在 Controller 的 show
裡面建立一個 Model 物件,用 #find
來尋找資料
# just_do/app/controllers/tasks_controller.rb
class TasksController < Mavericks::Controller
def index
end
def show
@task = Mavericks::Model::FileModel.find(1)
end
end
增加 show.html.erb
頁面
# just_do/app/views/tasks/show.html.erb
<div class="row">
<div class="col-6">
<div class="card">
<div class="card-header">
任務細節
</div>
<div class="card-body">
<div class="row">
<label class="col" for="task_name">任務名稱</label>
<div class="col">
<%= @task['title'] %>
</div>
</div>
<div class="row">
<label class="col" for="task_name">任務內容</label>
<div class="col">
<%= @task['content'] %>
</div>
</div>
</div>
</div>
</div>
</div>
重新整理一下畫面,如果取得檔案內容,代表我們成功了,恭喜完成第一個 Model!
眼尖的讀者會發現,Mavericks::Model::FileModel.find(1)
這段寫起來落落長,應該有更好的方式來處理才對,這裡我們可以用 include
來解決這個問題,還記得 include
和 extend
的差別嗎?前者是繼承 module 的 method 做為 instance method
,後者是繼承 module 做為 class method
# mavericks/lib/mavericks/controller.rb
require 'erubi'
# 記得要 require
require "mavericks/file_model"
module Mavericks
class Controller
# include method 進來
include Mavericks::Model
.
.
(略)
在 controller.rb
裡面 require file_model
,然後在 class Controller
裡面 include Mavericks::Model
所以在 jsut_do 那邊,就可以改寫成這樣
# just_do/app/controllers/tasks_controller.rb
class TasksController < Mavericks::Controller
def index
end
def show
@task = FileModel.find(1)
end
end
重新整理看看結果有沒有改變?正常來講應該會一樣,這樣是不是減少了很多冗長的程式碼?
我知道開發者想說什麼,光只有 #find
是不夠的,今天只是 Model 初體驗,明天會在補上其他的 method,讓我們的 file_model 越來越強大也越來越完整