FEATURE: modifier API for plugins (#20887)

Introduces a new API for plugin data modification without class-based extension overhead.

This commit introduces a new API that allows plugins to modify data in cases where they return different data rather than additional data, as is common with filtered_registers in DiscoursePluginRegistry. This API removes the need for defining class-based extension points.

When a plugin registers a modifier, it will automatically be called if the plugin is enabled. The core will then modify the parameter sent to it using the block registered by the plugin:
 
```ruby
DiscoursePluginRegistry.register_modifier(plugin_instance, :magic_sum_modifier) { |a, b| a + b }
sum = DiscoursePluginRegistry.apply_modifier(:magic_sum_filter, 1, 2)
expect(sum).to eq(3)
```

Key features of these modifiers:

- Operate in a stack (first registered, first called)
- Automatically disabled when the plugin is disabled
- Pass the cumulative result of all block invocations to the caller
This commit is contained in:
Sam
2023-03-30 14:39:55 +11:00
committed by GitHub
parent 4e11014693
commit 795e6d72a4
4 changed files with 99 additions and 0 deletions

View File

@ -246,8 +246,39 @@ class DiscoursePluginRegistry
asset
end
def self.clear_modifiers!
@modifiers = nil
end
def self.register_modifier(plugin_instance, name, &blk)
@modifiers ||= {}
modifiers = @modifiers[name] ||= []
modifiers << [plugin_instance, blk]
end
def self.apply_modifier(name, arg, *more_args)
return arg if !@modifiers
registered_modifiers = @modifiers[name]
return arg if !registered_modifiers
# iterate as fast as possible to minimize cost (avoiding each)
# also erases one stack frame
length = registered_modifiers.length
index = 0
while index < length
plugin_instance, block = registered_modifiers[index]
arg = block.call(arg, *more_args) if plugin_instance.enabled?
index += 1
end
arg
end
def self.reset!
@@register_names.each { |name| instance_variable_set(:"@#{name}", nil) }
clear_modifiers!
end
def self.reset_register!(register_name)