搭配 Ruby 使用 Cloud Datastore

本頁的 Bookshelf 教學課程說明範例應用程式如何將永久資料儲存在 Cloud Datastore 中。範例程式碼提供如何對儲存在 Cloud Datastore 中的資料執行建立、讀取、更新及刪除 (CRUD) 作業的範例。

完整的教學課程內容涵蓋了數個頁面的篇幅,本頁面屬於其中一個單元。如要從頭開始並閱讀設定操作說明,請前往 Ruby Bookshelf 應用程式頁面。

安裝依附元件並進行設定

  1. 前往 getting-started-ruby/2-cloud-datastore 目錄,並輸入下列指令:

    bundle install
    
  2. 複製 database.example.yml 檔案。

    cp config/database.example.yml config/database.yml
    
  3. 如要設定應用程式使用您的 Cloud Datastore,請編輯 config/database.yml 檔案。請將 [YOUR_PROJECT_ID] 替換為您的專案 ID。

    例如,假設您的專案 ID 是 my-project。那麼您的 database.yml 檔案的 developmentproduction 部分看起來會像這樣:

    development:
      dataset_id: my-project
    
    production:
      dataset_id: my-project
    

Cloud Datastore 是一套全代管服務,會自動初始化及連線至您的 App Engine 應用程式。您不需要採取進一步的設定。

在本機電腦執行應用程式

  1. 啟動本機網路伺服器。

    bundle exec rails server
    
  2. 在網路瀏覽器中,輸入下列網址:

    http://localhost:3000

您現在可以瀏覽應用程式的網頁,並新增、編輯及刪除書籍。

如要離開本機網路伺服器,請按下 Control+C 鍵。

將應用程式部署至 App Engine 彈性環境

  1. 編譯 JavaScript 資產以用於實際工作環境。

    RAILS_ENV=production bundle exec rake assets:precompile
    
  2. 部署範例應用程式。

    gcloud app deploy
    
  3. 在網路瀏覽器中,輸入下列網址。

    https://[YOUR_PROJECT_ID].appspot.com
    

若您更新了應用程式,您可以輸入第一次部署應用程式時使用的指令來部署更新版本。新的部署作業會為您的應用程式建立新版本,並將這個新版本晉升為預設版本。應用程式的較舊版本和相關聯的 VM 執行個體都會保留下來。請注意,這些應用程式版本和 VM 執行個體全部都是計費資源。

您可以刪除應用程式的非預設版本來降低費用。

如何刪除應用程式版本:

  1. 前往 GCP 主控台的「App Engine Versions」(App Engine 版本) 頁面。

    前往版本頁面

  2. 找到您要刪除的非預設應用程式版本,然後點選旁邊的核取方塊。
  3. 按一下頁面頂部的 [刪除] 按鈕, 刪除應用程式版本。

如需有關清除計費資源的完整資訊,請參閱本教學課程最後一個步驟的清除一節。

應用程式結構

下圖顯示應用程式的元件,以及這些元件彼此之間的連結方式。

Bookshelf 應用程式部署程序與結構

瞭解程式碼

這部分內容會逐步引導您瞭解範例程式碼,並說明其運作方式。

與 Cloud Datastore 進行互動

Bookshelf 範例應用程式使用 google-cloud RubyGem。RubyGem 是用來存取 Google Cloud Platform (GCP) 服務的用戶端程式庫。

為了與 Cloud Datastore 互動,該應用程式會根據您在 config/database.yml 檔案中輸入的專案 ID 設定 Dataset 物件。

以下各節說明該應用程式如何使用 Dataset 向 Cloud Datastore 查詢書籍清單。

require "google/cloud/datastore"

class Book

  attr_accessor :id, :title, :author, :published_on, :description

  # Return a Google::Cloud::Datastore::Dataset for the configured dataset.
  # The dataset is used to create, read, update, and delete entity objects.
  def self.dataset
    @dataset ||= Google::Cloud::Datastore.new(
      project_id: Rails.application.config.
                        database_configuration[Rails.env]["dataset_id"]
    )
  end

列出書籍

當您造訪應用程式的首頁時,系統會將您轉送到 BooksController 類別的 index 動作。這個動作是在 config/routes.rb 中設定的。

Rails.application.routes.draw do

  # Route root of application to BooksController#index action
  root "books#index"

  # Restful routes for BooksController
  resources :books

end

BookController#index 動作會從 Cloud Datastore 擷取書籍清單。Book.query 方法會傳回 Book 物件陣列和查詢游標,您可以使用這個游標擷取額外的實體。 進一步瞭解查詢游標

class BooksController < ApplicationController

  PER_PAGE = 10

  def index
    @books, @cursor = Book.query limit: PER_PAGE, cursor: params[:cursor]
  end

Book.query 方法向 Cloud Datastore 查詢 Book 種類的實體,並將這些實體做為 Book 類別的例項傳回:

# Query Book entities from Cloud Datastore.
#
# returns an array of Book query results and a cursor
# that can be used to query for additional results.
def self.query options = {}
  query = Google::Cloud::Datastore::Query.new
  query.kind "Book"
  query.limit options[:limit]   if options[:limit]
  query.cursor options[:cursor] if options[:cursor]

  results = dataset.run query
  books   = results.map {|entity| Book.from_entity entity }

  if options[:limit] && results.size == options[:limit]
    next_cursor = results.cursor
  end

  return books, next_cursor
end

Book.query 方法會建立 Google::Cloud::Datastore::Query 物件,並設定這個物件以要求種類為 Book 的實體,同時設定傳回的結果數量上限。如有提供查詢游標,查詢會傳回從游標開始的結果。

Book.query 擷取書籍清單後,books/index.html.erb 中嵌入的 Ruby 程式碼會轉譯清單。

<% @books.each do |book| %>
  <div class="media book">
    <%= link_to book_path(book) do %>
      <div class="media-body">
        <h4><%= book.title %></h4>
        <p><%= book.author %></p>
      </div>
    <% end %>
  </div>
<% end %>

<% if @cursor %>
  <nav>
    <ul class="pager">
      <li><%= link_to "More", books_path(cursor: @cursor) %></li>
    </ul>
  </nav>
<% end %>

為了從 Cloud Datastore 查詢並擷取 resultsBook.query 會呼叫 Dataset#run,後者會執行查詢,並傳回一個 Entity 物件集合。

從 Cloud Datastore 傳回的 Entity 物件會使用下列格式表示:

{
  key: {
    dataset_id: "your-project-id",
    kind: "Book",
    id: "5726256557457408"
  },
  properties: {
    title: "A Tale of Two Cities",
    author: "Charles Dickens"
  }
}

Book 類別包含在 Entity 物件和 Book 物件之間進行轉譯的方法。例如,Book.from_entity 收到 Entity 物件後會傳回 Book 的例項。

def self.from_entity entity
  book = Book.new
  book.id = entity.key.id
  entity.properties.to_hash.each do |name, value|
    book.send "#{name}=", value if book.respond_to? "#{name}="
  end
  book
end

顯示書籍詳細資料

當您在網頁上按一下個別書籍時,BookController#show 動作會呼叫 Book.find,從 Cloud Datastore 擷取書籍。

def show
  @book = Book.find params[:id]
end

Book.find 方法會依據自動產生的整數 id 鍵,從 Cloud Datastore 擷取書籍。

# Lookup Book by ID.  Returns Book or nil.
def self.find id
  query    = Google::Cloud::Datastore::Key.new "Book", id.to_i
  entities = dataset.lookup query

  from_entity entities.first if entities.any?
end

Book.find 擷取書籍後,books/show.html.erb 中嵌入的 Ruby 程式碼會轉譯清單。

<div class="media">
  <div class="media-body">
    <h4><%= @book.title %> | &nbsp; <small><%= @book.published_on %></small></h4>
    <h5>By <%= @book.author || "unknown" %></h5>
    <p><%= @book.description %></p>
  </div>
</div>

建立書籍

當您在網頁上按一下 [Add book] (新增書籍) 時,BooksController#new 動作會建立新的 Booknew.html.erb 中嵌入的 Ruby 程式碼會指向 _form.html.erb,後者會顯示用來新增書籍的表單。

<%= form_for @book do |f| %>
  <div class="form-group">
    <%= f.label :title %>
    <%= f.text_field :title %>
  </div>
  <div class="form-group">
    <%= f.label :author %>
    <%= f.text_field :author %>
  </div>
  <div class="form-group">
    <%= f.label :published_on, "Date Published" %>
    <%= f.date_field :published_on %>
  </div>
  <div class="form-group">
    <%= f.label :description %>
    <%= f.text_area :description %>
  </div>
  <button class="btn btn-success" type="submit">Save</button>
<% end %>

當您提交表單時,BooksController#create 動作會將書籍儲存在 Cloud Datastore 中。如果新書籍儲存成功,就會顯示該書籍的網頁。如果儲存失敗,表單會再次顯示,且您會看到錯誤訊息。book_params 方法使用 Strong Parameters 來指定允許的表單欄位。在這個範例中,只有書籍的 titleauthorpublished_ondescription 是允許的欄位:

def create
  @book = Book.new book_params

  if @book.save
    flash[:success] = "Added Book"
    redirect_to book_path(@book)
  else
    render :new
  end
end

private

def book_params
  params.require(:book).permit(:title, :author, :published_on, :description)
end

系統只會將具有 title 欄位的書籍視為有效。Cloud Datastore 中只會儲存有效的書籍。

# Add Active Model validation support to Book class.
include ActiveModel::Validations

validates :title, presence: true

Book.create 方法會呼叫 Book.save,後者會將 Book 物件轉譯為 Entity 物件,並使用 Datastore dataset 將書籍儲存在 Cloud Datastore 中。

# Save the book to Datastore.
# @return true if valid and saved successfully, otherwise false.
def save
  if valid?
    entity = to_entity
    Book.dataset.save entity
    self.id = entity.key.id
    true
  else
    false
  end
end

編輯書籍

當您在網頁上按一下 [Edit book] (編輯書籍) 時,BooksController#update 動作會從 Cloud Datastore 擷取書籍。edit.html.erb 中嵌入的 Ruby 程式碼會指向 _form.html.erb,後者會顯示用來編輯書籍的表單。

def update
  @book = Book.find params[:id]

  if @book.update book_params
    flash[:success] = "Updated Book"
    redirect_to book_path(@book)
  else
    render :edit
  end
end

BooksController#update 動作會呼叫 Book 例項的 update 方法。

# Set attribute values from provided Hash and save to Datastore.
def update attributes
  attributes.each do |name, value|
    send "#{name}=", value if respond_to? "#{name}="
  end
  save
end

如果書籍詳細資料更新成功,系統會將使用者導向到書籍的頁面。如果更新失敗,表單會再次顯示,且使用者會看到錯誤訊息。

刪除書籍

當您在網頁上按一下 [Delete Book] (刪除書籍) 時,BooksController#destroy 動作會從 Cloud Datastore 刪除書籍,然後顯示書籍清單。

def destroy
  @book = Book.find params[:id]
  @book.destroy
  redirect_to books_path
end

BooksController#destroy 動作會呼叫 Book 例項的 destroy 方法。

def destroy
  Book.dataset.delete Google::Cloud::Datastore::Key.new "Book", id
end
本頁內容對您是否有任何幫助?請提供意見:

傳送您對下列選項的寶貴意見...

這個網頁