Skip to content

Commit

Permalink
Trigger listeners only for enabled plugin
Browse files Browse the repository at this point in the history
Previously any plugin that would subscribe to notifications would be
always triggered, which made it impossible to isolate plugin configs
  • Loading branch information
solnic committed Jun 27, 2021
1 parent c61af46 commit 48020b9
Show file tree
Hide file tree
Showing 6 changed files with 74 additions and 26 deletions.
2 changes: 1 addition & 1 deletion lib/rom/components/core.rb
Original file line number Diff line number Diff line change
Expand Up @@ -72,7 +72,7 @@ def adapter
)
end

# @api public
# @api private
def apply_plugins
plugins.each do |plugin|
plugin.apply_to(constant)
Expand Down
66 changes: 49 additions & 17 deletions lib/rom/configuration.rb
Original file line number Diff line number Diff line change
Expand Up @@ -53,29 +53,20 @@ def initialize(*args, &block)
configure(*args, &block)
end

# @api public
# This is called internally when you pass a block to ROM.container
#
# @api private
def configure(*args)
unless args.empty?
gateways_config = args.first.is_a?(Hash) ? args.first : {default: args}

gateways_config.each do |name, value|
args = Array(value)

adapter, *rest = args

if rest.size > 1 && rest.last.is_a?(Hash)
load_config(config.gateways[name], {adapter: adapter, args: rest[0..-1], **rest.last})
else
options = rest.first.is_a?(Hash) ? rest.first : {args: rest.flatten(1)}
load_config(config.gateways[name], {adapter: adapter, **options})
end
end
end
infer_config(*args) unless args.empty?

# Load adapters explicitly here to ensure their plugins are present already
# while setup loads components and then triggers finalization
setup.load_adapters

# TODO: this is tricky because if you want to tweak the config in the block
# you end up doing `config.config.foo = "bar"` so it would be good
# to yield configuration object + its config or rename this whole class
# because it's a configuration *DSL* that builds up a config object too
yield(self) if block_given?

# No more changes allowed
Expand All @@ -89,10 +80,30 @@ def configure(*args)

# @api private
def finalize
attach_listeners
setup.finalize
self
end

# @api private
def attach_listeners
# Anything can attach globally to certain events, including plugins, so here
# we're making sure that only plugins that are enabled in thi configuration
# will be
global_listeners = Notifications.listeners.to_a
.reject { |(src, *)| plugin_registry.mods.include?(src) }.to_h

plugin_listeners = Notifications.listeners.to_a
.select { |(src, *)| plugins.map(&:mod).include?(src) }.to_h

listeners.update(global_listeners).update(plugin_listeners)
end

# @api private
def listeners
notifications.listeners
end

# Apply a plugin to the configuration
#
# @param [Mixed] plugin The plugin identifier, usually a Symbol
Expand Down Expand Up @@ -156,6 +167,27 @@ def respond_to_missing?(name, include_all = false)

private

# This infers config based on arguments passed to either ROM.container
# or Configuration.ne
#
# @api private
def infer_config(*args)
gateways_config = args.first.is_a?(Hash) ? args.first : {default: args}

gateways_config.each do |name, value|
args = Array(value)

adapter, *rest = args

if rest.size > 1 && rest.last.is_a?(Hash)
load_config(config.gateways[name], {adapter: adapter, args: rest[0..-1], **rest.last})
else
options = rest.first.is_a?(Hash) ? rest.first : {args: rest.flatten(1)}
load_config(config.gateways[name], {adapter: adapter, **options})
end
end
end

# @api private
def load_config(config, hash)
hash.each do |key, value|
Expand Down
5 changes: 5 additions & 0 deletions lib/rom/plugin_registry.rb
Original file line number Diff line number Diff line change
Expand Up @@ -12,9 +12,13 @@ class PluginRegistry
# @api private
attr_reader :types

# @api private
attr_reader :mods

# @api private
def initialize
@types = ::Concurrent::Map.new
@mods = []
end

# Register a plugin for future use
Expand All @@ -27,6 +31,7 @@ def initialize
# @option options [Symbol] :adapter (:default) which adapter this plugin
# applies to. Leave blank for all adapters
def register(name, mod, options = EMPTY_HASH)
mods << mod
type(options.fetch(:type)).register(name, mod, options)
end

Expand Down
16 changes: 10 additions & 6 deletions lib/rom/support/notifications.rb
Original file line number Diff line number Diff line change
Expand Up @@ -54,7 +54,7 @@ module Publisher
#
# @api public
def subscribe(event_id, query = EMPTY_HASH, &block)
listeners[event_id] << [block, query]
listeners[self] << [event_id, block, query]
self
end

Expand All @@ -67,7 +67,11 @@ def subscribe(event_id, query = EMPTY_HASH, &block)
def trigger(event_id, payload = EMPTY_HASH)
event = events[event_id]

listeners[event.id].each do |(listener, query)|
event_listeners = listeners.values.flatten(1).group_by(&:first)

return unless event_listeners.key?(event_id)

event_listeners[event_id].each do |(_, listener, query)|
event.payload(payload).trigger(listener, query)
end
end
Expand Down Expand Up @@ -178,7 +182,7 @@ def self.listeners
#
# @api public
def self.event_bus(id)
EventBus.new(id, events: events.dup, listeners: listeners.dup)
EventBus.new(id, events: events.dup)
end

# Extension for objects that can listen to events
Expand All @@ -193,7 +197,7 @@ module Listener
#
# @api public
def subscribe(event_id, query = EMPTY_HASH, &block)
Notifications.listeners[event_id] << [block, query]
Notifications.listeners[self] << [event_id, block, query]
end
end

Expand Down Expand Up @@ -224,9 +228,9 @@ class EventBus
# @param [Hash] listeners A hash with listeners
#
# @api public
def initialize(id, events: EMPTY_HASH, listeners: LISTENERS_HASH.dup)
def initialize(id, events: EMPTY_HASH)
@id = id
@listeners = listeners
@listeners = LISTENERS_HASH.dup
@events = events
end
end
Expand Down
6 changes: 5 additions & 1 deletion spec/shared/changeset/database.rb
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,11 @@
RSpec.shared_context "changeset / database setup" do
include_context "changeset / db_uri"

let(:configuration) { ROM::Configuration.new(:sql, db_uri) }
let(:configuration) do
ROM::Configuration.new(:sql, db_uri) do |config|
config.plugin(:sql, relation: :auto_restrictions)
end
end

let(:conn) { configuration.gateways[:default].connection }

Expand Down
5 changes: 4 additions & 1 deletion spec/shared/repository/database.rb
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
# frozen_string_literal: true

require "rom/sql"
RSpec.shared_context "repository / db_uri" do
let(:base_db_uri) do
ENV.fetch("BASE_DB_URI", "postgres@localhost/rom")
Expand All @@ -14,7 +15,9 @@
include_context "repository / db_uri"

let(:configuration) do
ROM::Configuration.new(:sql, db_uri)
ROM::Configuration.new(:sql, db_uri) do |config|
config.plugin(:sql, relation: :auto_restrictions)
end
end

let(:conn) do
Expand Down

0 comments on commit 48020b9

Please sign in to comment.