Dynamoid/dynamoid

View on GitHub
lib/dynamoid/transaction_write.rb

Summary

Maintainability
A
45 mins
Test Coverage
# frozen_string_literal: true

require 'dynamoid/transaction_write/action'
require 'dynamoid/transaction_write/create'
require 'dynamoid/transaction_write/delete'
require 'dynamoid/transaction_write/destroy'
require 'dynamoid/transaction_write/update_upsert'
require 'dynamoid/transaction_write/update'
require 'dynamoid/transaction_write/upsert'

module Dynamoid
  class TransactionWrite
    attr_accessor :action_inputs, :models

    def initialize(_options = {})
      @action_inputs = []
      @models = []
    end

    def self.execute(options = {})
      transaction = new(options)
      yield(transaction)
      transaction.commit
    end

    def commit
      return unless @action_inputs.present? # nothing to commit

      Dynamoid.adapter.transact_write_items(@action_inputs)
      models.each { |model| model.new_record = false }
    end

    def save!(model, options = {})
      save(model, options.reverse_merge(raise_validation_error: true))
    end

    def save(model, options = {})
      model.new_record? ? create(model, {}, options) : update(model, {}, options)
    end

    def create!(model_or_model_class, attributes = {}, options = {}, &block)
      create(model_or_model_class, attributes, options.reverse_merge(raise_validation_error: true), &block)
    end

    def create(model_or_model_class, attributes = {}, options = {}, &block)
      add_action_and_validate Dynamoid::TransactionWrite::Create.new(model_or_model_class, attributes, options, &block)
    end

    # upsert! does not exist because upserting instances that can raise validation errors is not officially supported

    def upsert(model_or_model_class, attributes = {}, options = {}, &block)
      add_action_and_validate Dynamoid::TransactionWrite::Upsert.new(model_or_model_class, attributes, options, &block)
    end

    def update!(model_or_model_class, attributes = {}, options = {}, &block)
      update(model_or_model_class, attributes, options.reverse_merge(raise_validation_error: true), &block)
    end

    def update(model_or_model_class, attributes = {}, options = {}, &block)
      add_action_and_validate Dynamoid::TransactionWrite::Update.new(model_or_model_class, attributes, options, &block)
    end

    def delete(model_or_model_class, key_or_attributes = {}, options = {})
      add_action_and_validate Dynamoid::TransactionWrite::Delete.new(model_or_model_class, key_or_attributes, options)
    end

    def destroy!(model_or_model_class, key_or_attributes = {}, options = {})
      destroy(model_or_model_class, key_or_attributes, options.reverse_merge(raise_validation_error: true))
    end

    def destroy(model_or_model_class, key_or_attributes = {}, options = {})
      add_action_and_validate Dynamoid::TransactionWrite::Destroy.new(model_or_model_class, key_or_attributes, options)
    end

    private

    # validates unless validations are skipped
    # runs callbacks unless callbacks are skipped
    # raise validation error or returns false if not valid
    # otherwise adds hash of action to list in preparation for committing
    def add_action_and_validate(action)
      if !action.skip_validation? && !action.valid?
        raise Dynamoid::Errors::DocumentNotValid, action.model if action.raise_validation_error?

        return false
      end

      if action.skip_callbacks?
        @action_inputs << action.to_h
      else
        action.run_callbacks do
          @action_inputs << action.to_h
        end
      end
      action.changes_applied # action has been processed and added to queue so mark as applied
      models << action.model if action.model

      action.model || true # return model if it exists
    end
  end
end