Cómo usar Cloud Datastore con Ruby

Esta página del instructivo de Bookshelf muestra cómo la app de ejemplo almacena los datos persistentes en Google Cloud Datastore. El código de muestra para este paso proporciona ejemplos de cómo crear, leer, actualizar y borrar (CRUD) datos almacenados en Cloud Datastore.

Esta página forma parte de un instructivo de varias páginas. Para comenzar desde el principio y leer las instrucciones de configuración, ve a la app de Bookshelf para Ruby.

Cómo instalar dependencias

Ve al directorio getting-started-ruby/2-cloud-datastore y, luego, ingresa este comando:

bundle install

Configuraciones

  1. Copia el archivo database.example.yml:

    cp config/database.example.yml config/database.yml
    
  2. Edita config/database.yml a fin de configura la app para usar Cloud Datastore. Reemplaza [YOUR_PROJECT_ID] por el ID del proyecto:

    Por ejemplo, supongamos que el ID del proyecto es my-project. Entonces, las secciones development y production del archivo database.yml se verían como el siguiente ejemplo:

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

Cloud Datastore es un servicio completamente administrado que se inicializa y se conecta a la app de App Engine automáticamente. No es necesario que lo configures.

Cómo ejecutar la app en una máquina local

  1. Inicia un servidor web local:

    bundle exec rails server
    
  2. En el navegador web, ingresa la siguiente dirección:

    http://localhost:3000

Ahora puedes navegar por las páginas web de la app y agregar, editar y borrar libros.

Presiona Control + C para salir del servidor web local.

Implementación de la app en el entorno flexible de App Engine

  1. Compila recursos de JavaScript para producción:

    RAILS_ENV=production bundle exec rake assets:precompile
    
  2. Implementa la app de muestra:

    gcloud app deploy
    
  3. En el navegador web, ingresa la siguiente dirección. Reemplaza [YOUR_PROJECT_ID] por el ID del proyecto:

    https://[YOUR_PROJECT_ID].appspot.com
    

Si actualizas tu app, podrás implementar la versión actualizada mediante el mismo comando que usaste para implementar la app por primera vez. La implementación nueva crea una versión nueva de tu app y la convierte a la versión predeterminada. Las versiones anteriores de la app se conservan, al igual que sus instancias de VM asociadas. Ten en cuenta que todas estas instancias de VM y versiones de la app son recursos facturables.

Para reducir costos, borra las versiones no predeterminadas de la app.

Para borrar una versión de una app, haz lo siguiente:

  1. En GCP Console, dirígete a la página Versiones de App Engine.

    Ir a la página de Versiones

  2. Haz clic en la casilla de verificación junto a la versión de app no predeterminada que deseas borrar.
  3. Haz clic en el botón Borrar en la parte superior de la página para borrar la versión de la app.

Para obtener toda la información acerca de la limpieza de los recursos facturables, consulta la sección Limpieza en el paso final de este instructivo.

Estructura de la aplicación

El siguiente diagrama muestra los componentes de la aplicación y la manera en que se conectan entre sí.

Proceso y estructura de la implementación de la app de Bookshelf

Comprensión del código

Esta sección explica el código de muestra y el funcionamiento.

Cómo interactuar con Google Cloud Datastore

La app de Bookshelf de muestra usa la RubyGem google-cloud, una biblioteca cliente para acceder a los servicios de Google Cloud Platform.

Para interactuar con Datastore, la app configura un objeto Dataset según el ID del proyecto que ingresaste en config/database.yml.

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

Las siguientes secciones explican cómo la app usa Dataset para buscar listas de libros en Cloud Datastore.

Cómo mostrar una lista de libros

Cuando visitas la página principal de la app, te diriges a la acción index de la clase BooksController. Esto se configura en 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

La acción BookController#index recupera una lista de libros desde el almacén de datos. El método Book.query muestra un arreglo de objetos Book y un cursor de consultas que se puede usar para recuperar entidades adicionales. Obtén más información acerca de los cursores de consulta.

class BooksController < ApplicationController

  PER_PAGE = 10

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

El método Book.query consulta el almacén de datos para buscar entidades del tipo "Book" y las muestras como instancias de la clase 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

El método Book.query crea un objeto Google::Cloud::Datastore::Query y lo configura para solicitar entidades del tipo Book, con un límite configurado para la cantidad de resultados que se muestran. Si se proporciona un cursor de consulta, los resultados se muestran a partir de él.

Después de que Book.query recupera una lista de libros, el código de Ruby incorporado a books/index.html.erb procesa la lista:

<% @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 %>

Para consultar y recuperar results desde Cloud Datastore, Book.query llama a Dataset#run, que ejecuta la consulta y muestra una colección de objetos Entity.

Los objetos Entity que se muestran desde Cloud Datastore se representan con este formato:

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

La clase Book incluye métodos para traducir entre objetos Entity y objetos Book. Por ejemplo, Book.from_entity toma un objeto Entity y muestra una instancia de 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

Cómo mostrar detalles de los libros

Cuando haces clic en un libro en la página web, la acción BookController#show llama a Book.find para recuperar el libro desde Cloud Datastore.

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

El método Book.find recupera el libro desde el almacén de datos según el número entero de su clave id que se genera automáticamente:

# 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

Después de que Book.find recupera los libros, el código de Ruby incorporado a books/show.html.erb procesa la lista:

<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>

Cómo crear libros

Cuando haces clic en Agregar libro en la página web, la acción BooksController#new crea un Book nuevo. El código Ruby incorporado a new.html.erb apunta a _form.html.erb, que muestra el formulario para agregar un libro nuevo:

<%= 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 %>

Cuando envías el formulario, la acción BooksController#create guarda el libro en el almacén de datos. Si el libro nuevo se guarda correctamente, se muestra la página del libro. De lo contrario, se vuelve a mostrar el formulario junto con mensajes de error. El método book_params usa parámetros estrictos para especificar los campos del formulario que están permitidos. En este caso, solo están permitidos los campos title, author, published_on y description del libro:

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

El campo title es obligatorio para que un libro se considere válido. Solo se guardan los libros válidos en el almacén de datos.

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

validates :title, presence: true

El método Book.create llama a Book.save, que traduce el objeto Book a un objeto Entity y usa Datastore dataset para guardar el libro en 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

Cómo editar libros

Cuando haces clic en Editar libro en la página web, la acción BooksController#update recupera el libro del almacén de datos. El código Ruby incorporado a edit.html.erb apunta a _form.html.erb, que muestra el formulario para editar el libro:

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

La acción BooksController#update llama al método update de la instancia de Book:

# 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

Si se actualizan los detalles del libro correctamente, el usuario se dirige a la página del libro. De lo contrario, se vuelve a mostrar el formulario con mensajes de error.

Cómo borrar libros

Cuando haces clic en Borrar libro en la página web, la acción BooksController#destroy borra el libro del almacén de datos y muestra la lista de libros:

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

La acción BooksController#destroy llama al método destroy de la instancia de Book:

def destroy
  Book.dataset.delete Google::Cloud::Datastore::Key.new "Book", id
end
¿Te sirvió esta página? Envíanos tu opinión: