# frozen_string_literal: true require "dry-transformer" require "json" module DjiMqttConnect module Sys::Product class StatusMarshal < MessageMarshal # Rename pesky `method` argument to `_method` and makes a copy of the raw data class AttributeTransformer < Dry::Transformer::Pipe import Dry::Transformer::HashTransformations define! do symbolize_keys copy_keys data: :_data rename_keys method: :_method end end # Attempts to look a the method attribute, and builds a specific Message class for the message def load(raw_message) # Parse the message from JSON parsed_message = JSON.parse(raw_message) # Transform the message transformed_message = attribute_transformer.call(parsed_message) # Build an instance of the class, or a generic message from the current class message_class = message_class_for_parsed_message(parsed_message) message_class.new transformed_message rescue JSON::ParserError => e raise ParseError.new(e, "Unable to parse message as JSON") rescue Dry::Struct::Error => e raise ParseError.new(e, "Unexpected #{message_class} payload") end private def attribute_transformer @attribute_transformer ||= AttributeTransformer.new end def message_class_for_parsed_message(parsed_message) # update_topo => UpdateTopo classified_method = ActiveSupport::Inflector.classify(parsed_message["method"]) # UpdateTopo => DjiMqttConnect::Sys::Product::UpdateTopoStatusMessage module_prefix, joiner, class_suffix = StatusMessage.name.rpartition("::") message_class_name = [module_prefix, joiner, classified_method, class_suffix].join # Constantize an instance of the message-specific class, or fallback to a generic message ActiveSupport::Inflector.safe_constantize(message_class_name) || StatusMessage end end end end