module ActiveModel::Datastore
Active Model Datastore
¶ ↑
Makes the google-cloud-datastore gem compliant with active_model conventions and compatible with your Rails 5+ applications.
Let's start by implementing the model:
class User include ActiveModel::Datastore attr_accessor :email, :enabled, :name, :role, :state before_validation :set_default_values after_validation :format_values before_save { puts '** something can happen before save **'} after_save { puts '** something can happen after save **'} validates :email, format: { with: /\A([^@\s]+)@((?:[-a-z0-9]+\.)+[a-z]{2,})\z/i } validates :name, presence: true, length: { maximum: 30 } validates :role, presence: true def entity_properties %w[email enabled name role] end def set_default_values default_property_value :enabled, true default_property_value :role, 1 end def format_values format_property_value :role, :integer end end
Using `attr_accessor` the attributes of the model are defined. Validations and Callbacks all work as you would expect. However, `entity_properties` is new. Data objects in Google Cloud Datastore
are known as entities. Entities are of a kind. An entity has one or more named properties, each of which can have one or more values. Think of them like this:
-
'Kind' (which is your table)
-
'Entity' (which is the record from the table)
-
'Property' (which is the attribute of the record)
The `entity_properties` method defines an Array of the properties that belong to the entity in cloud datastore. With this approach, Rails deals solely with ActiveModel
objects. The objects are converted to/from entities as needed during save/query operations.
We have also added the ability to set default property values and type cast the format of values for entities.
Now on to the controller! A scaffold generated controller works out of the box:
class UsersController < ApplicationController before_action :set_user, only: [:show, :edit, :update, :destroy] def index @users = User.all end def show end def new @user = User.new end def edit end def create @user = User.new(user_params) respond_to do |format| if @user.save format.html { redirect_to @user, notice: 'User was successfully created.' } else format.html { render :new } end end end def update respond_to do |format| if @user.update(user_params) format.html { redirect_to @user, notice: 'User was successfully updated.' } else format.html { render :edit } end end end def destroy @user.destroy respond_to do |format| format.html { redirect_to users_url, notice: 'User was successfully destroyed.' } end end private def set_user @user = User.find(params[:id]) end def user_params params.require(:user).permit(:email, :name) end end
Constants
- VERSION
Public Instance Methods
Builds the Cloud Datastore
entity with attributes from the Model object.
@param [Google::Cloud::Datastore::Key] parent An optional parent Key of the entity.
@return [Entity] The updated Google::Cloud::Datastore::Entity.
# File lib/active_model/datastore.rb, line 153 def build_entity(parent = nil) entity = CloudDatastore.dataset.entity self.class.name, id if parent.present? raise ArgumentError, 'Must be a Key' unless parent.is_a? Google::Cloud::Datastore::Key entity.key.parent = parent elsif parent? entity.key.parent = self.class.parent_key(parent_key_id) end entity_properties.each do |attr| entity[attr] = instance_variable_get("@#{attr}") entity.exclude_from_indexes!(attr, true) if no_index_attributes.include? attr end entity end
# File lib/active_model/datastore.rb, line 190 def destroy run_callbacks :destroy do key = CloudDatastore.dataset.key self.class.name, id key.parent = self.class.parent_key(parent_key_id) if parent? self.class.retry_on_exception? { CloudDatastore.dataset.delete key } end end
# File lib/active_model/datastore.rb, line 128 def entity_properties [] end
Used to determine if the ActiveModel
object belongs to an entity group.
# File lib/active_model/datastore.rb, line 135 def parent? parent_key_id.present? end
Used by ActiveModel
for determining polymorphic routing.
# File lib/active_model/datastore.rb, line 142 def persisted? id.present? end
# File lib/active_model/datastore.rb, line 169 def save(parent = nil) save_entity(parent) end
For compatibility with libraries that require the bang method version (example, factory_bot).
# File lib/active_model/datastore.rb, line 176 def save! save_entity || raise(EntityNotSavedError, 'Failed to save the entity') end
# File lib/active_model/datastore.rb, line 180 def update(params) assign_attributes(params) return unless valid? run_callbacks :update do entity = build_entity self.class.retry_on_exception? { CloudDatastore.dataset.save entity } end end
Private Instance Methods
# File lib/active_model/datastore.rb, line 200 def save_entity(parent = nil) return unless valid? run_callbacks :save do entity = build_entity(parent) success = self.class.retry_on_exception? { CloudDatastore.dataset.save entity } self.id = entity.key.id if success self.parent_key_id = entity.key.parent.id if entity.key.parent.present? success end end