diff options
Diffstat (limited to 'lib/gitlab_settings/options.rb')
-rw-r--r-- | lib/gitlab_settings/options.rb | 110 |
1 files changed, 97 insertions, 13 deletions
diff --git a/lib/gitlab_settings/options.rb b/lib/gitlab_settings/options.rb index 077c1aa944a..68555794436 100644 --- a/lib/gitlab_settings/options.rb +++ b/lib/gitlab_settings/options.rb @@ -1,7 +1,42 @@ # 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 @@ -26,22 +61,25 @@ module GitlabSettings @options[key.to_s] = self.class.build(value) end - def key?(name) - @options.key?(name.to_s) || @options.key?(name.to_sym) + def key?(key) + @options.key?(key.to_s) end alias_method :has_key?, :key? - def to_hash - @options.deep_transform_values do |option| - case option - when self.class - option.to_hash - else - option - end - end + # 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 - alias_method :to_h, :to_hash def dup self.class.build(to_hash) @@ -51,16 +89,56 @@ module GitlabSettings 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 @@ -70,7 +148,13 @@ module GitlabSettings return self[name_string] end - return @options.public_send(name, *args, &block) if @options.respond_to?(name) # rubocop: disable GitlabSecurity/PublicSend + 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 |