nogates/brujula

View on GitHub
lib/brujula/mergers/map_object_merger.rb

Summary

Maintainability
A
35 mins
Test Coverage
module Brujula
  module Mergers
    class MapObjectMerger
      attr_reader :superinstance, :instance

      def initialize(instance:, superinstance:)
        @superinstance = superinstance
        @instance      = instance
      end

      def call
        return superinstance.dup if instance.nil?

        instance.dup.tap do |extended_instance|
          each_inheritable_items do |inherit_item|
            extended_instance.merge(inherit_item.name, inherit_item)
          end
        end
      end

      def each_inheritable_items
        superinstance.each do |inherit_item|

          merge_item = case item_reference(inherit_item.name)
          when :optional_to_optional
            instance.fetch(inherit_item.name)
          when :optional
            instance.fetch(inherit_item.name.gsub(/\?$/, ''))
          when :direct
            instance.key?(inherit_item.name) ?
              instance.fetch(inherit_item.name) : nil
          else
            next
          end

          yield merger(merge_item, inherit_item)
        end
      end

      def merger(merge_item, inherit_item)
        Merger.new(instance: merge_item, superinstance: inherit_item).call
      end

      def item_reference(key)
        case
        when optional_key?(key) && instance_has_optional_item?(key)
          :optional_to_optional
        when optional_key?(key) && instance_has_item?(key)
          :optional
        when !optional_key?(key)
          :direct
        end
      end

      def merge_item?(key)
        return true unless optional_key?(key)

        instance_has_item?(key) || instance_has_optional_item?(key)
      end

      def optional_key?(key)
        key.to_s.end_with?('?')
      end

      def instance_has_item?(key)
        item_names.include?(key.gsub(/\?$/, ''))
      end

      def instance_has_optional_item?(key)
        item_names.include?(key)
      end

      def item_names
        @item_names ||= instance.to_a.map(&:name)
      end
    end
  end
end