lib/ledger_sync/domains/operation.rb in ledger_sync-domains-1.0.0.rc4 vs lib/ledger_sync/domains/operation.rb in ledger_sync-domains-1.0.0.rc6

- old
+ new

@@ -1,9 +1,18 @@ # frozen_string_literal: true +unless Object.const_defined?('ActiveRecord') + # reopen AR module for non-rails apps + module ActiveRecord + module Base; end + end +end + module LedgerSync module Domains + class InternalOperationError < LedgerSync::Error::OperationError; end + class Operation class OperationResult module ResultTypeBase attr_reader :meta @@ -20,31 +29,21 @@ end include LedgerSync::ResultBase end - module Mixin + module Mixin # rubocop:disable Metrics/ModuleLength module ClassMethods - def inferred_resource_class - name = to_s.split('::') - name.pop # remove serializer/operation class from name - resource = name.pop.singularize # pluralized resource module name + @internal = false - const_get((name + [resource]).join('::')) + def internal + @internal = true end - def inferred_serializer_class - const_get("#{inferred_resource_class}Serializer") + def internal? + !!@internal end - - def inferred_deserializer_class - const_get("#{inferred_resource_class}Deserializer") - end - - def inferred_validation_contract_class - const_get('Contract') - end end def self.included(base) base.include SimplySerializable::Mixin base.include Fingerprintable::Mixin @@ -59,18 +58,26 @@ end end attr_reader :params, :result - def initialize(serializer: nil, deserializer: nil, **params) - @serializer = serializer - @deserializer = deserializer + def initialize(domain:, **params) + @domain = domain @params = params @result = nil end def perform # rubocop:disable Metrics/MethodLength + unless allowed? + return failure( + LedgerSync::Domains::InternalOperationError.new( + operation: self, + message: 'Cross-domain operation execution is not allowed' + ) + ) + end + if performed? return failure( LedgerSync::Error::OperationError::PerformedOperationError.new( operation: self ) @@ -91,36 +98,51 @@ ensure @performed = true end end + def allowed? + return true unless self.class.internal? + + local_domain == @domain + end + def performed? @performed == true end def serialize(resource:) - serializer.serialize(resource: resource) + serializer_for(resource: resource).serialize(resource: resource) end - def deserializer - @deserializer ||= deserializer_class.new + def serializer_for(resource:) + serializer_class_for(resource: resource).new end - def deserializer_class - @deserializer_class ||= self.class.inferred_deserializer_class + def serializer_class_for(resource:) + Object.const_get( + [ + serializer_module_for(resource: resource), + "#{domain}Serializer" + ].join('::') + ) end - def serializer - @serializer ||= serializer_class.new + def serializer_module_for(resource:) + ( + resource.class.try(:serializer_module) || resource.class + ).to_s.pluralize end - def serializer_class - @serializer_class ||= self.class.inferred_serializer_class + def domain + LedgerSync::Domains.domains.module_for(domain: @domain) end - def resource_class - @resource_class ||= self.class.inferred_resource_class + def local_domain + LedgerSync::Domains.domains.domain_for( + base_module: self.class.to_s.split('::').first.constantize + ) end # Results def failure(error) @@ -130,29 +152,46 @@ def failure? result.failure? end def success(value, meta: nil) - @result = OperationResult.Success(value, meta: meta) + @result = OperationResult.Success(deep_serialize(value), meta: meta) end + def deep_serialize(value) + case value + when ActiveRecord::Base, LedgerSync::Resource + serialize(resource: value) + when Hash + value.transform_values { deep_serialize(_1) } + when Array + value.map { deep_serialize(_1) } + else + value + end + end + def success? result.success? end def valid? validate.success? end def validate LedgerSync::Util::Validator.new( - contract: validation_contract, + contract: validation_contract_class, data: params ).validate end - def validation_contract - @validation_contract ||= self.class.inferred_validation_contract_class + def validation_contract_class + @validation_contract_class ||= inferred_validation_contract_class + end + + def inferred_validation_contract_class + self.class.const_get('Contract') end def errors validate.validator.errors end