diff --git a/core/lib/rom/plugins/relation/registry_reader.rb b/core/lib/rom/plugins/relation/registry_reader.rb index fec206387..5a0602bca 100644 --- a/core/lib/rom/plugins/relation/registry_reader.rb +++ b/core/lib/rom/plugins/relation/registry_reader.rb @@ -14,9 +14,9 @@ class RegistryReader < ::Module EMPTY_REGISTRY = RelationRegistry.build(EMPTY_HASH).freeze # @api private - def initialize(klass:, relation_readers_module:) + def initialize(readers:) super() - klass.include relation_readers_module + include readers end # @api private diff --git a/core/lib/rom/setup/finalize/finalize_relations.rb b/core/lib/rom/setup/finalize/finalize_relations.rb index 056d4ab34..7b8565f64 100644 --- a/core/lib/rom/setup/finalize/finalize_relations.rb +++ b/core/lib/rom/setup/finalize/finalize_relations.rb @@ -9,14 +9,12 @@ class Finalize class FinalizeRelations attr_reader :notifications - module BuildRelationReaders - def self.build(relations) - Module.new do - relations.each do |name| - define_method(name) do - __registry__[name] - end - end + class RegistryReaders < ::Module + def initialize(relations) + super() + + relations.each do |name| + define_method(name) { __registry__[name] } end end end @@ -44,7 +42,7 @@ def initialize(gateways, relation_classes, notifications:, mappers: nil, plugins # rubocop:disable Metrics/AbcSize, Metrics/MethodLength def run! relation_registry = RelationRegistry.new do |registry, relations| - relation_readers_module = BuildRelationReaders.build(relation_names) + registry_readers = RegistryReaders.new(relation_names) @relation_classes.each do |klass| unless klass.adapter raise MissingAdapterIdentifierError, @@ -58,7 +56,7 @@ def run! "Relation with name #{key.inspect} registered more than once" end - klass.use(:registry_reader, klass: klass, relation_readers_module: relation_readers_module) + klass.use(:registry_reader, readers: registry_readers) notifications.trigger( 'configuration.relations.class.ready', diff --git a/repository/lib/rom/repository/class_interface.rb b/repository/lib/rom/repository/class_interface.rb index 1bf549348..6bc6a6877 100644 --- a/repository/lib/rom/repository/class_interface.rb +++ b/repository/lib/rom/repository/class_interface.rb @@ -60,7 +60,7 @@ def new(container = nil, **options) container ||= options.fetch(:container) unless relation_reader - relation_reader(RelationReader.new(self, container.relations.elements.keys)) + relation_reader(RelationReader.new(container.relations.elements.keys)) include(relation_reader) end diff --git a/repository/lib/rom/repository/relation_reader.rb b/repository/lib/rom/repository/relation_reader.rb index d365471ea..4eb92912a 100644 --- a/repository/lib/rom/repository/relation_reader.rb +++ b/repository/lib/rom/repository/relation_reader.rb @@ -7,70 +7,84 @@ class RelationReader < ::Module extend ::Dry::Core::ClassAttributes # @api private - attr_reader :klass - - # @api private - attr_reader :relations - defines :relation_readers - defines :mutex - mutex(Mutex.new) - - defines :relation_cache - relation_cache(Concurrent::Hash.new) + # @api private + defines :lock + lock ::Mutex.new module InstanceMethods # @api private - def set_relation(name) # rubocop:disable Naming/AccessorMethodName + def prepare_relation(name) container .relations[name] - .with(auto_struct: auto_struct, struct_namespace: struct_namespace) + .with( + auto_struct: auto_struct, + struct_namespace: struct_namespace + ) end + # @api private def relation_reader(name, relation_cache) key = [name, auto_struct, struct_namespace] - relation_cache[key] ||= set_relation(name) + relation_cache[key] ||= prepare_relation(name) end end # @api private - def mutex - ROM::Repository::RelationReader.mutex - end + class Readers < ::Module + # @api private + attr_reader :cache - # @api private - def initialize(klass, relations) - super() - @relations = relations - mutex.synchronize do - unless self.class.relation_readers - self.class.relation_readers( - build_relation_readers(relations, self.class.relation_cache) - ) + def initialize(relations) + super() + cache = ::Concurrent::Hash.new + + relations.each do |name| + define_readers_for_relation(name, cache) + end + end + + # @api private + def define_readers_for_relation(name, cache) + define_method(name) do + relation_reader(name, cache) end end - klass.include self.class.relation_readers end # @api private - def included(klass) - super - klass.include(InstanceMethods) - end + def lock = self.class.lock - private + # @api private + def initialize(relations) + super() + + include relation_readers(relations) + end # @api private - def build_relation_readers(relations, relation_cache) - Module.new do - relations.each do |name| - define_method(name) do - relation_reader(name, relation_cache) + def relation_readers(relations) + existing = self.class.relation_readers + + if existing.nil? + lock.synchronize do + unless self.class.relation_readers + self.class.relation_readers Readers.new(relations) end end + + self.class.relation_readers + else + existing end end + + # @api private + def included(klass) + super + klass.include(InstanceMethods) + end end end end diff --git a/repository/lib/rom/repository/root.rb b/repository/lib/rom/repository/root.rb index be0c891be..17d0a57dc 100644 --- a/repository/lib/rom/repository/root.rb +++ b/repository/lib/rom/repository/root.rb @@ -58,7 +58,7 @@ def self.inherited(klass) # @see Repository#initialize def initialize(*, **) super - @root = set_relation(self.class.root) + @root = prepare_relation(self.class.root) end end end diff --git a/repository/spec/integration/plugin_spec.rb b/repository/spec/integration/plugin_spec.rb index 641b129be..a500e5f24 100644 --- a/repository/spec/integration/plugin_spec.rb +++ b/repository/spec/integration/plugin_spec.rb @@ -12,7 +12,7 @@ def self.apply(target, **) target.prepend(self) end - def set_relation(*) + def prepare_relation(*) super.where { `1 = 0` } end end diff --git a/repository/spec/spec_helper.rb b/repository/spec/spec_helper.rb index 98673fda1..561bdef14 100644 --- a/repository/spec/spec_helper.rb +++ b/repository/spec/spec_helper.rb @@ -59,7 +59,6 @@ def self.remove_constants config.after do Test.remove_constants ROM::Repository::RelationReader.relation_readers(nil) - ROM::Repository::RelationReader.relation_cache(Concurrent::Hash.new) end Dir[SPEC_ROOT.join('support/*.rb').to_s].each do |f|