BigW Consortium Gitlab

json_hash_builder.rb 4.54 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
      # 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)
86 87 88
        @attributes_finder.parse(value) do |hash|
          value = { value => hash } unless value.is_a?(Hash)
        end
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 114 115

        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