summaryrefslogtreecommitdiff
path: root/lib/gitlab/config/entry/node.rb
blob: 32912cb1046c21071879dd9e7aaf2b28711e4970 (plain)
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
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
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
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
# frozen_string_literal: true

module Gitlab
  module Config
    module Entry
      ##
      # Base abstract class for each configuration entry node.
      #
      class Node
        InvalidError = Class.new(StandardError)

        attr_reader :config, :metadata
        attr_accessor :key, :parent, :default, :description

        def initialize(config, **metadata)
          @config = config
          @metadata = metadata
          @entries = {}
          @warnings = []

          yield(self) if block_given?

          self.class.aspects.to_a.each do |aspect|
            instance_exec(&aspect)
          end
        end

        def [](key)
          @entries[key] || Entry::Undefined.new
        end

        def compose!(deps = nil)
          return unless valid?

          yield if block_given?
        end

        def leaf?
          @entries.none?
        end

        def descendants
          @entries.values
        end

        def ancestors
          @parent ? @parent.ancestors + [@parent] : []
        end

        def opt(key)
          opt = metadata[key]
          opt = @parent.opt(key) if opt.nil? && @parent
          opt
        end

        def valid?
          errors.none?
        end

        def errors
          []
        end

        def warnings
          @warnings + descendants.flat_map(&:warnings)
        end

        def add_warning(message)
          @warnings << "#{location} #{message}"
        end

        def value
          if leaf?
            @config
          else
            meaningful = @entries.select do |_key, value|
              value.specified? && value.relevant?
            end

            meaningful.transform_values { |entry| entry.value }
          end
        end

        def specified?
          true
        end

        def relevant?
          true
        end

        def location
          name = @key.presence || self.class.name.to_s.demodulize
                                      .underscore.humanize.downcase

          ancestors.map(&:key).append(name).compact.join(':')
        end

        def inspect
          val = leaf? ? config : descendants
          unspecified = specified? ? '' : '(unspecified) '
          "#<#{self.class.name} #{unspecified}{#{key}: #{val.inspect}}>"
        end

        def hash?
          @config.is_a?(Hash)
        end

        def string?
          @config.is_a?(String)
        end

        def integer?
          @config.is_a?(Integer)
        end

        def self.default(**)
        end

        def self.aspects
          @aspects ||= []
        end

        def self.with_aspect(blk)
          self.aspects.append(blk)
        end

        private

        attr_reader :entries
      end
    end
  end
end