Skip to content

Instantly share code, notes, and snippets.

@alebian
Created February 21, 2020 12:53
Show Gist options
  • Save alebian/d378d4d254af5ee8167439b8cfbc90fe to your computer and use it in GitHub Desktop.
Save alebian/d378d4d254af5ee8167439b8cfbc90fe to your computer and use it in GitHub Desktop.
class BaseCommand
def initialize(params: {}, context: {})
@params = params
@context = context
end
def call; end
def undo; end
end
class CommandComposer
attr_reader :error, :intermediate_results
def initialize
@commands = []
@instances = []
@intermediate_results = []
@results_as_context = {}
@current_command_idx = -1
@success = true
@error = nil
@logger = Rails.logger
end
def call # rubocop:disable Metrics/MethodLength
@commands.each_with_index do |command, idx|
@current_command_idx = idx
@logger.info(
"About to execute step #{idx + 1}: #{command[:klass]} with params: #{command[:params]}"
)
instance = create_instance(command)
begin
calculate_instance_result(instance, command)
rescue StandardError => e
handle_exception(e, command)
return self
end
end
self
end
def add_command(command_klass, options = {})
if command_klass
hash = {
klass: command_klass,
params: (options[:params] || {}),
result_as: options[:result_as]
}
@commands << hash
end
self
end
def success?
@success
end
def result
@intermediate_results[-1]
end
private
def create_instance(command)
instance = command[:klass].new(
params: command[:params], context: @results_as_context
)
@instances << instance
instance
end
def calculate_instance_result(instance, command)
result = instance.call
@intermediate_results << result
@results_as_context[command[:result_as]] = result if command[:result_as]
result
end
def handle_exception(exception, command)
@logger.error(
"#{exception.class} in step #{@current_command_idx + 1}: #{command[:klass]}. Rolling back."
)
@logger.error("Error: #{exception.message}")
@error = exception
@success = false
rollback
end
def rollback
(0..@current_command_idx).reverse_each do |idx|
instance = @instances[idx]
@logger.info("Undoing step #{idx + 1}: #{instance.class}")
instance.undo
end
end
end
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment