Allow settings to be overridden for writable and values from plugins

This commit is contained in:
Oliver Günther
2026-04-02 11:30:19 +02:00
parent 18b2bdee4c
commit d875d31b86
2 changed files with 195 additions and 0 deletions
+50
View File
@@ -1428,6 +1428,10 @@ module Settings
end
def value
unless (override = resolve_value_override).nil?
return cast(override)
end
cast(@value)
end
@@ -1444,6 +1448,8 @@ module Settings
end
def writable?
return false if value_override?
if writable.respond_to?(:call)
writable.call
else
@@ -1567,6 +1573,38 @@ module Settings
@all ||= {}
end
# Registers a value override block for a setting. The block is called
# whenever the setting's value or writability is evaluated.
#
# If the block returns a non-nil value, that value is used as the setting's
# value and the setting becomes non-writable. If the block returns nil,
# no override is applied.
#
# To override a setting with nil, return a callable: +-> { nil }+
#
# @param name [Symbol] The setting name to override.
# @yield A block that returns the override value, or nil to skip.
#
# @example Force a setting to true when a condition is met
# Settings::Definition.add_value_override(:capture_external_links) do
# true if MyPlugin.active?
# end
def add_value_override(name, &block)
(value_overrides[name.to_sym] ||= []) << block
end
def value_overrides
@value_overrides ||= {}
end
def clear_value_overrides(name = nil)
if name
value_overrides.delete(name.to_sym)
else
@value_overrides = {}
end
end
private
def file_config
@@ -1760,6 +1798,18 @@ module Settings
attr_accessor :serialized,
:writable
def value_override?
!resolve_value_override.nil?
end
def resolve_value_override
self.class.value_overrides[name.to_sym]&.each do |block|
result = block.call
return result unless result.nil?
end
nil
end
def cast(value)
return nil if value.nil?
+145
View File
@@ -1053,4 +1053,149 @@ RSpec.describe Settings::Definition, :settings_reset do
end
end
end
describe ".add_value_override" do
before do
described_class.add "bogus_override_test",
default: false,
format: :boolean
end
after do
described_class.clear_value_overrides(:bogus_override_test)
end
context "when the override block returns a non-nil value" do
before do
described_class.add_value_override(:bogus_override_test) do
true
end
end
it "uses the returned value as the setting value" do
expect(described_class[:bogus_override_test].value).to be true
end
it "marks the setting as non-writable" do
expect(described_class[:bogus_override_test]).not_to be_writable
end
end
context "when the override block returns nil" do
before do
described_class.add_value_override(:bogus_override_test) do
nil
end
end
it "uses the original default value" do
expect(described_class[:bogus_override_test].value).to be false
end
it "keeps the setting writable" do
expect(described_class[:bogus_override_test]).to be_writable
end
end
context "when the override block returns a callable" do
before do
described_class.add_value_override(:bogus_override_test) do
-> { nil }
end
end
it "calls it to obtain the value, allowing override with nil" do
expect(described_class[:bogus_override_test].value).to be_nil
end
it "marks the setting as non-writable" do
expect(described_class[:bogus_override_test]).not_to be_writable
end
end
context "when the override is conditional" do
let(:condition) { true }
before do
cond = condition
described_class.add_value_override(:bogus_override_test) do
true if cond
end
end
context "when condition is met" do
let(:condition) { true }
it "overrides the value" do
expect(described_class[:bogus_override_test].value).to be true
end
it "is non-writable" do
expect(described_class[:bogus_override_test]).not_to be_writable
end
end
context "when condition is not met" do
let(:condition) { false }
it "uses the original value" do
expect(described_class[:bogus_override_test].value).to be false
end
it "remains writable" do
expect(described_class[:bogus_override_test]).to be_writable
end
end
end
context "with multiple override blocks" do
before do
described_class.add "bogus_multi_override_test",
default: "original",
format: :string
described_class.add_value_override(:bogus_multi_override_test) do
nil
end
described_class.add_value_override(:bogus_multi_override_test) do
"from_second"
end
described_class.add_value_override(:bogus_multi_override_test) do
"from_third"
end
end
after do
described_class.clear_value_overrides(:bogus_multi_override_test)
end
it "uses the first block that returns a non-nil value" do
expect(described_class[:bogus_multi_override_test].value).to eq "from_second"
end
end
context "when the setting is already non-writable" do
before do
described_class.add "bogus_non_writable_test",
default: "original",
format: :string,
writable: false
described_class.add_value_override(:bogus_non_writable_test) do
"overridden"
end
end
after do
described_class.clear_value_overrides(:bogus_non_writable_test)
end
it "overrides the value" do
expect(described_class[:bogus_non_writable_test].value).to eq "overridden"
end
it "remains non-writable" do
expect(described_class[:bogus_non_writable_test]).not_to be_writable
end
end
end
end