json_hash_builder.rb 4.49 KB
Newer Older
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59
module Gitlab
  module ImportExport
    # Generates a hash that conforms with http://apidock.com/rails/Hash/to_json
    # and its peculiar options.
    class JsonHashBuilder
      def self.build(model_objects, attributes_finder)
        new(model_objects, attributes_finder).build
      end

      def initialize(model_objects, attributes_finder)
        @model_objects = model_objects
        @attributes_finder = attributes_finder
      end

      def build
        process_model_objects(@model_objects)
      end

      private

      # Called when the model is actually a hash containing other relations (more models)
      # Returns the config in the right format for calling +to_json+
      #
      # +model_object_hash+ - A model relationship such as:
      #   {:merge_requests=>[:merge_request_diff, :notes]}
      def process_model_objects(model_object_hash)
        json_config_hash = {}
        current_key = model_object_hash.keys.first

        model_object_hash.values.flatten.each do |model_object|
          @attributes_finder.parse(current_key) { |hash| json_config_hash[current_key] ||= hash }
          handle_model_object(current_key, model_object, json_config_hash)
        end

        json_config_hash
      end

      # Creates or adds to an existing hash an individual model or list
      #
      # +current_key+ main model that will be a key in the hash
      # +model_object+ model or list of models to include in the hash
      # +json_config_hash+ the original hash containing the root model
      def handle_model_object(current_key, model_object, json_config_hash)
        model_or_sub_model = model_object.is_a?(Hash) ? process_model_objects(model_object) : model_object

        if json_config_hash[current_key]
          add_model_value(current_key, model_or_sub_model, json_config_hash)
        else
          create_model_value(current_key, model_or_sub_model, json_config_hash)
        end
      end

      # Constructs a new hash that will hold the configuration for that particular object
      # It may include exceptions or other attribute detail configuration, parsed by +@attributes_finder+
      #
      # +current_key+ main model that will be a key in the hash
      # +value+ existing model to be included in the hash
      # +json_config_hash+ the original hash containing the root model
      def create_model_value(current_key, value, json_config_hash)
60
        json_config_hash[current_key] = parse_hash(value) || { include: value }
61 62 63 64 65 66
      end

      # Calls attributes finder to parse the hash and add any attributes to it
      #
      # +value+ existing model to be included in the hash
      # +parsed_hash+ the original hash
67
      def parse_hash(value)
68 69
        return nil if already_contains_methods?(value)

70
        @attributes_finder.parse(value) do |hash|
71
          { include: hash_or_merge(value, hash) }
72
        end
73 74
      end

75 76 77 78
      def already_contains_methods?(value)
        value.is_a?(Hash) && value.values.detect { |val| val[:methods]}
      end

79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113
      # Adds new model configuration to an existing hash with key +current_key+
      # It may include exceptions or other attribute detail configuration, parsed by +@attributes_finder+
      #
      # +current_key+ main model that will be a key in the hash
      # +value+ existing model to be included in the hash
      # +json_config_hash+ the original hash containing the root model
      def add_model_value(current_key, value, json_config_hash)
        @attributes_finder.parse(value) { |hash| value = { value => hash } }

        add_to_array(current_key, json_config_hash, value)
      end

      # Adds new model configuration to an existing hash with key +current_key+
      # it creates a new array if it was previously a single value
      #
      # +current_key+ main model that will be a key in the hash
      # +value+ existing model to be included in the hash
      # +json_config_hash+ the original hash containing the root model
      def add_to_array(current_key, json_config_hash, value)
        old_values = json_config_hash[current_key][:include]

        json_config_hash[current_key][:include] = ([old_values] + [value]).compact.flatten
      end

      # Construct a new hash or merge with an existing one a model configuration
      # This is to fulfil +to_json+ requirements.
      #
      # +hash+ hash containing configuration generated mainly from +@attributes_finder+
      # +value+ existing model to be included in the hash
      def hash_or_merge(value, hash)
        value.is_a?(Hash) ? value.merge(hash) : { value => hash }
      end
    end
  end
end