我把資料庫放到 Google Sheets 上了
大家好,我是卡米哥。今天要教大家怎麼用 Google Sheets 存放資料並且在 rails 當中使用。
在本次的教學中,我們主要的需求是想要直接以 Google Sheets 作為後台,來取代 rails 原本的資料庫以及需要自己寫的管理介面。
使用 Google Sheets 作為後台,比起自己做的管理介面,有許多好處,像是可以省下製作後台的時間,編輯速度快,支援共同編輯,支援版本管理等。
注意:在本次的教學中我們不保證資料的安全性。
建立 Google Sheet 並且發布成 CSV 格式
首先先新增一個 Google Sheet,我這邊做好了一個示範:
https://docs.google.com/spreadsheets/d/1kDEbaStqDG-g6HTTwbUx_MOWTtmHtISZzMtcinAKQqI/edit?usp=sharing
然後將這個 sheet 做發布:
點選 File
點選 Publish to the web...
點選 Entire Document
改選為 Sheet1
點選 Web page
改選為 Comma-separated values (.csv)
都選好的時候看起來像這樣:
點選 Publish
並且按下確定後:
把中間的網址複製起來,我的 csv 做好之後的連結如下:
使用瀏覽器開啟這個網址時,會下載一個 csv 檔。若使用文字編輯器開啟這個檔案,會看到以下內容:
keyword,message
喔~,氣氣氣氣氣
請注意,任何人只要擁有這個網址,就能夠讀取到完整的資料內容。
在 Rails 讀取 CSV 並且解析為 Model
首先,先建立一個新的專案:
rails new google_sheets_demo
在 ./app
資料夾下建立 sheets
資料夾,在 sheets
資料夾中新增一個 keyword.rb
檔案,並且輸入以下內容:
require 'net/http'
require 'csv'
class Keyword
SHEETS_URL = 'https://docs.google.com/spreadsheets/d/e/2PACX-1vROPREvtOJD2nRGeRjXAzFgG2Qx4Zdx0DM1bLl7VsPtI8ftp7juEW7g0IPhGsXI5g-fNWdfl1erwV7M/pub?gid=0&single=true&output=csv'
attr_accessor :keyword
attr_accessor :message
def initialize(keyword: nil, message: nil)
self.keyword = keyword
self.message = message
end
def self.all
body = Net::HTTP.get(URI(SHEETS_URL)).force_encoding('UTF-8')
sheet = CSV.parse(body)
title = sheet.shift.map(&:to_sym)
sheet.map do |row|
Keyword.new(**title.zip(row).to_h)
end
end
end
以上的程式碼就是在做一個類似 Model 的 all 方法,用來取得所有的資料,我大概解釋一下原理:
- 使用 Net::HTTP.get 取得 csv 資料
- 使用 CSV 解析 csv 資料
- 去除標題列
- 將資料轉換為物件模型
好的,那麼馬上來試驗一下,先進入 rails console
後輸入以下程式碼:
Keyword.all
其結果為:
=> [#<Keyword:0x00007faf28560e58 @keyword="喔~", @message="氣氣氣氣氣">]
取得了一個陣列,雖然沒辦法使用 Active Record 提供的 where 功能,但 Array 以及 Enumerable 還是有類似的內建方法可以用。
雖然取得了資料,但每次都會去抓取是不好的,畢竟後台的資料不會一直更新,而且 google sheets 的抓取時間其實蠻久的,大約要2~3秒左右。
實作簡易的快取機制
我們可以增加一個快取機制,讓他每分鐘重抓一次就好,我們對 keyword.rb
進行一些調整,以下是調整後的程式碼:
require 'net/http'
require 'csv'
class Keyword
SHEETS_URL = 'https://docs.google.com/spreadsheets/d/e/2PACX-1vROPREvtOJD2nRGeRjXAzFgG2Qx4Zdx0DM1bLl7VsPtI8ftp7juEW7g0IPhGsXI5g-fNWdfl1erwV7M/pub?gid=0&single=true&output=csv'
$cached_keywords = nil
$cached_keywords_expired_at = nil
attr_accessor :keyword
attr_accessor :message
def initialize(keyword: nil, message: nil)
self.keyword = keyword
self.message = message
end
def self.all
if $cached_keywords_expired_at.nil? || $cached_keywords_expired_at < Time.current
body = Net::HTTP.get(URI(SHEETS_URL)).force_encoding('UTF-8')
sheet = CSV.parse(body)
title = sheet.shift.map(&:to_sym)
$cached_keywords = sheet.map do |row|
Keyword.new(**title.zip(row).to_h)
end
$cached_keywords_expired_at = 1.minutes.since
end
$cached_keywords
end
end
我們將快取儲存在整個網站的全域變數上,也就是錢字號 $
開頭的變數上,如此一來就能夠加快查詢速度。
在每次取得 Keyword.all
的時候先檢查是否存在快取,如果不存在的話,才去抓取 CSV 以及解析成 Model 物件。
實際應用的話,我會建議將快取功能以及 Sheets 功能拆解成獨立的檔案,最理想的情況則是做成 gem,在這裡為求簡單理解,就不將問題複雜化了。
試用做好的 Model
那麼我們做個簡單的網頁介面來試用看看這個 Model。
先修改 config/routes.rb
建立一個 routes 規則指向 chatbot#talk
:
Rails.application.routes.draw do
get 'talk/:say', to: 'chatbot#talk'
end
然後在 app/controllers
資料夾下建立一個 chatbot_controller.rb
,內容如下:
class ChatbotController < ApplicationController
def talk
reply_message = Keyword.all.find{ |keyword| keyword.keyword == params[:say] }&.message
render plain: reply_message || "蛤?"
end
end
接著開啟 rails server 做測試,在瀏覽器輸入網址 http://localhost:3000/talk/喔~ 後,就會看到:
那麼今天的教學就到這邊,我是卡米哥,謝謝收看。