Welcome to mirror list, hosted at ThFree Co, Russian Federation.

options.rb « gitlab_settings « lib - gitlab.com/gitlab-org/gitlab-foss.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
blob: 685557944364aa5e8d1a56b4c0f9ace65c9f80a5 (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
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
# frozen_string_literal: true

require 'forwardable'

module GitlabSettings
  class Options
    extend Forwardable

    def_delegators :@options,
      :count,
      :deep_stringify_keys,
      :deep_symbolize_keys,
      :default_proc,
      :dig,
      :each_key,
      :each_pair,
      :each_value,
      :each,
      :empty?,
      :fetch_values,
      :fetch,
      :filter,
      :keys,
      :length,
      :map,
      :member?,
      :merge,
      :reject,
      :select,
      :size,
      :slice,
      :stringify_keys,
      :symbolize_keys,
      :transform_keys,
      :transform_values,
      :value?,
      :values_at,
      :values

    # Recursively build GitlabSettings::Options
    def self.build(obj)
      case obj
      when Hash
        new(obj.transform_values { |value| build(value) })
      when Array
        obj.map { |value| build(value) }
      else
        obj
      end
    end

    def initialize(value)
      @options = value.deep_stringify_keys
    end

    def [](key)
      @options[key.to_s]
    end

    def []=(key, value)
      @options[key.to_s] = self.class.build(value)
    end

    def key?(key)
      @options.key?(key.to_s)
    end
    alias_method :has_key?, :key?

    # Some configurations use the 'default' key, like:
    # https://gitlab.com/gitlab-org/gitlab/-/blob/c4d5c77c87494bb320fa7fdf19b0e4d7d52af1d1/spec/support/helpers/stub_configuration.rb#L96
    # But since `default` is also a method in Hash, this can be confusing and
    # raise an exception instead of returning nil, as expected in some places.
    # To avoid that, we use #default always as a possible internal key
    def default
      @options['default']
    end

    # For backward compatibility, like:
    # https://gitlab.com/gitlab-org/gitlab/-/blob/adf67e90428670aaa955731f3bdeafb8b3a874cd/lib/gitlab/database/health_status/indicators/patroni_apdex.rb#L58
    def with_indifferent_access
      to_hash.with_indifferent_access
    end

    def dup
      self.class.build(to_hash)
    end

    def merge(other)
      self.class.build(to_hash.merge(other.deep_stringify_keys))
    end

    def merge!(other)
      @options = to_hash.merge(other.deep_stringify_keys)
    end

    def deep_merge(other)
      self.class.build(to_hash.deep_merge(other.deep_stringify_keys))
    end

    def deep_merge!(other)
      @options = to_hash.deep_merge(other.deep_stringify_keys)
    end

    def is_a?(klass)
      return true if klass == Hash

      super(klass)
    end

    def to_hash
      @options.deep_transform_values do |option|
        case option
        when self.class
          option.to_hash
        else
          option
        end
      end
    end
    alias_method :to_h, :to_hash

    # Don't alter the internal keys
    def stringify_keys!
      error_msg = "Warning: Do not mutate #{self.class} objects: `#{__method__}`"

      Gitlab::ErrorTracking.track_and_raise_for_dev_exception(RuntimeError.new(error_msg), method: __method__)

      to_hash.deep_stringify_keys
    end
    alias_method :deep_stringify_keys!, :stringify_keys!

    # Don't alter the internal keys
    def symbolize_keys!
      error_msg = "Warning: Do not mutate #{self.class} objects: `#{__method__}`"

      Gitlab::ErrorTracking.track_and_raise_for_dev_exception(RuntimeError.new(error_msg), method: __method__)

      to_hash.deep_symbolize_keys
    end
    alias_method :deep_symbolize_keys!, :symbolize_keys!

    def method_missing(name, *args, &block)
      name_string = +name.to_s

      if name_string.chomp!("=")
        return self[name_string] = args.first if key?(name_string)
      elsif key?(name_string)
        return self[name_string]
      end

      if @options.respond_to?(name)
        error_msg = "Calling a hash method on #{self.class}: `#{name}`"

        Gitlab::ErrorTracking.track_and_raise_for_dev_exception(RuntimeError.new(error_msg), method: name)

        return @options.public_send(name, *args, &block) # rubocop: disable GitlabSecurity/PublicSend
      end

      raise ::GitlabSettings::MissingSetting, "option '#{name}' not defined"
    end

    def respond_to_missing?(name, include_all = false)
      return true if key?(name)

      @options.respond_to?(name, include_all)
    end
  end
end