diff --git a/Gemfile b/Gemfile index 5e46b7728..aed350593 100644 --- a/Gemfile +++ b/Gemfile @@ -19,6 +19,8 @@ gem "rom-sql", github: "rom-rb/rom-sql", branch: "master" gem "dry-monitor", github: "dry-rb/dry-monitor", branch: "master" gem "dry-events", github: "dry-rb/dry-events", branch: "master" +gem "zeitwerk", github: "fxn/zeitwerk", branch: "main" + group :sql do gem "jdbc-postgres", platforms: :jruby gem "jdbc-sqlite3", platforms: :jruby diff --git a/Rakefile b/Rakefile index 2cfddf2bd..705e07aab 100644 --- a/Rakefile +++ b/Rakefile @@ -5,7 +5,15 @@ require "bundler/gem_tasks" require "rspec/core" require "rspec/core/rake_task" -RSpec::Core::RakeTask.new(:spec) +RSpec::Core::RakeTask.new("spec:all") do |t| + t.pattern = ["spec/unit/**/*_spec.rb", "spec/integration/**/*_spec.rb"] +end + +RSpec::Core::RakeTask.new("spec:compat") do |t| + t.pattern = ["spec/compat/**/*_spec.rb"] +end + +task "spec" => ["spec:all", "spec:compat"] task default: :spec @@ -25,8 +33,10 @@ namespace :benchmark do end end +# rubocop:disable Lint/SuppressedException begin require "yard-junk/rake" YardJunk::Rake.define_task(:text) rescue LoadError end +# rubocop:enable Lint/SuppressedException diff --git a/docsite/core/source/framework-setup.html.md b/docsite/core/source/framework-setup.html.md index 2bd180003..631a77727 100644 --- a/docsite/core/source/framework-setup.html.md +++ b/docsite/core/source/framework-setup.html.md @@ -65,7 +65,7 @@ module Persistence end configuration = ROM::Configuration.new(:memory) -configuration.auto_registration('root_dir/lib/persistence/') +configuration.auto_register('root_dir/lib/persistence/') container = ROM.container(configuration) ``` @@ -92,7 +92,7 @@ Notice that the directory structure is different from our module structure. Sinc ```ruby configuration = ROM::Configuration.new(:memory) -configuration.auto_registration('/path/to/lib', namespace: 'Persistence') +configuration.auto_register('/path/to/lib', namespace: 'Persistence') container = ROM.container(configuration) ``` @@ -146,7 +146,7 @@ end Then, auto-registration can be achieved with ```ruby -configuration.auto_registration('/path/to/lib', namespace: 'MyApp::Persistence') +configuration.auto_register('/path/to/lib', namespace: 'MyApp::Persistence') ``` #### Turning namespace off @@ -162,7 +162,7 @@ end ```ruby configuration = ROM::Configuration.new(:memory) -configuration.auto_registration('/path/to/lib', namespace: false) +configuration.auto_register('/path/to/lib', namespace: false) container = ROM.container(configuration) ``` diff --git a/lib/rom/compat.rb b/lib/rom/compat.rb new file mode 100644 index 000000000..00e715e2f --- /dev/null +++ b/lib/rom/compat.rb @@ -0,0 +1,3 @@ +# frozen_string_literal: true + +require_relative "compat/setup" diff --git a/lib/rom/setup/auto_registration.rb b/lib/rom/compat/auto_registration.rb similarity index 93% rename from lib/rom/setup/auto_registration.rb rename to lib/rom/compat/auto_registration.rb index afbb82752..64b94b32f 100644 --- a/lib/rom/setup/auto_registration.rb +++ b/lib/rom/compat/auto_registration.rb @@ -6,9 +6,10 @@ require "rom/types" require "rom/initializer" -require "rom/setup/auto_registration_strategies/no_namespace" -require "rom/setup/auto_registration_strategies/with_namespace" -require "rom/setup/auto_registration_strategies/custom_namespace" + +require_relative "auto_registration_strategies/no_namespace" +require_relative "auto_registration_strategies/with_namespace" +require_relative "auto_registration_strategies/custom_namespace" module ROM # AutoRegistration is used to load component files automatically from the provided directory path diff --git a/lib/rom/setup/auto_registration_strategies/base.rb b/lib/rom/compat/auto_registration_strategies/base.rb similarity index 100% rename from lib/rom/setup/auto_registration_strategies/base.rb rename to lib/rom/compat/auto_registration_strategies/base.rb diff --git a/lib/rom/setup/auto_registration_strategies/custom_namespace.rb b/lib/rom/compat/auto_registration_strategies/custom_namespace.rb similarity index 97% rename from lib/rom/setup/auto_registration_strategies/custom_namespace.rb rename to lib/rom/compat/auto_registration_strategies/custom_namespace.rb index 58fea65d0..8882fd9fc 100644 --- a/lib/rom/setup/auto_registration_strategies/custom_namespace.rb +++ b/lib/rom/compat/auto_registration_strategies/custom_namespace.rb @@ -4,7 +4,8 @@ require "rom/support/inflector" require "rom/types" -require "rom/setup/auto_registration_strategies/base" + +require_relative "base" module ROM module AutoRegistrationStrategies diff --git a/lib/rom/setup/auto_registration_strategies/no_namespace.rb b/lib/rom/compat/auto_registration_strategies/no_namespace.rb similarity index 93% rename from lib/rom/setup/auto_registration_strategies/no_namespace.rb rename to lib/rom/compat/auto_registration_strategies/no_namespace.rb index a535b42f8..849f95d85 100644 --- a/lib/rom/setup/auto_registration_strategies/no_namespace.rb +++ b/lib/rom/compat/auto_registration_strategies/no_namespace.rb @@ -4,7 +4,7 @@ require "rom/support/inflector" require "rom/types" -require "rom/setup/auto_registration_strategies/base" +require_relative "base" module ROM module AutoRegistrationStrategies diff --git a/lib/rom/setup/auto_registration_strategies/with_namespace.rb b/lib/rom/compat/auto_registration_strategies/with_namespace.rb similarity index 92% rename from lib/rom/setup/auto_registration_strategies/with_namespace.rb rename to lib/rom/compat/auto_registration_strategies/with_namespace.rb index 1b113fffa..d5cad81a1 100644 --- a/lib/rom/setup/auto_registration_strategies/with_namespace.rb +++ b/lib/rom/compat/auto_registration_strategies/with_namespace.rb @@ -3,7 +3,7 @@ require "pathname" require "rom/support/inflector" -require "rom/setup/auto_registration_strategies/base" +require_relative "base" module ROM module AutoRegistrationStrategies diff --git a/lib/rom/compat/setup.rb b/lib/rom/compat/setup.rb new file mode 100644 index 000000000..eb754747b --- /dev/null +++ b/lib/rom/compat/setup.rb @@ -0,0 +1,30 @@ +# frozen_string_literal: true + +require_relative "auto_registration" + +module ROM + # Setup objects collect component classes during setup/finalization process + # + # @api public + class Setup + # Enable auto-registration for a given setup object + # + # @param [String, Pathname] directory The root path to components + # @param [Hash] options + # @option options [Boolean, String] :namespace Toggle root namespace + # or provide a custom namespace name + # + # @return [Setup] + # + # @deprecated + # + # @api public + def auto_registration(directory, **options) + auto_registration = AutoRegistration.new(directory, inflector: inflector, **options) + auto_registration.relations.map { |r| register_relation(r) } + auto_registration.commands.map { |r| register_command(r) } + auto_registration.mappers.map { |r| register_mapper(r) } + self + end + end +end diff --git a/lib/rom/container.rb b/lib/rom/container.rb index dd1d37c6a..bc4eff800 100644 --- a/lib/rom/container.rb +++ b/lib/rom/container.rb @@ -74,7 +74,7 @@ module ROM # # @example multi-step setup with auto-registration # config = ROM::Configuration.new(:sql, 'sqlite::memory') - # config.auto_registration('./persistence', namespace: false) + # config.auto_register('./persistence', namespace: false) # # config.default.create_table :users do # primary_key :id diff --git a/lib/rom/loader.rb b/lib/rom/loader.rb new file mode 100644 index 000000000..bf1f22889 --- /dev/null +++ b/lib/rom/loader.rb @@ -0,0 +1,151 @@ +# frozen_string_literal: true + +require "pathname" +require "zeitwerk" + +require "rom/support/inflector" + +require "rom/types" +require "rom/initializer" +require "rom/loader" + +module ROM + # AutoRegistration is used to load component files automatically from the provided directory path + # + # @api public + class Loader + extend Initializer + + NamespaceType = Types::Strict::Bool | Types.Instance(Module) + + PathnameType = Types.Constructor(Pathname, &Kernel.method(:Pathname)) + + InflectorType = Types.Interface(:camelize) + + ComponentDirs = Types::Strict::Hash.constructor { |hash| hash.transform_values(&:to_s) } + + DEFAULT_MAPPING = { + relations: "relations", + mappers: "mappers", + commands: "commands" + }.freeze + + # @!attribute [r] directory + # @return [Pathname] The root path + param :root_directory, type: PathnameType + + # @!attribute [r] namespace + # @return [Boolean,String] + # The name of the top level namespace or true/false which + # enables/disables default top level namespace inferred from the dir name + option :namespace, type: NamespaceType, default: -> { true } + + # @!attribute [r] component_dirs + # @return [Hash] component => dir-name map + option :component_dirs, type: ComponentDirs, default: -> { DEFAULT_MAPPING } + + # @!attribute [r] inflector + # @return [Dry::Inflector] String inflector + # @api private + option :inflector, type: InflectorType, default: -> { Inflector } + + # @!attribute [r] loaded_constants + # @return [Dry::Inflector] String inflector + # @api private + option :inflector, type: InflectorType, default: -> { Inflector } + + option :loaded_constants, default: -> { {relations: [], commands: [], mappers: []} } + + # Load components + # + # @api private + def constants(component_type) + unless @loaded + setup and backend.eager_load + @loaded = true + end + + loaded_constants.fetch(component_type) + end + + # Load relation files + # + # @api private + def relations + constants(__method__) + end + + # Load command files + # + # @api private + def commands + constants(__method__) + end + + # Load mapper files + # + # @api private + def mappers + constants(__method__) + end + + private + + # @api private + def backend + @backend ||= Zeitwerk::Loader.new + end + + # @api private + # rubocop:disable Metrics/AbcSize + def setup + backend.inflector = inflector + + case namespace + when true + backend.push_dir(root_directory.join("..").realpath) + + component_dirs.each_value do |dir| + backend.collapse(root_directory.join(dir).join("**/*")) + end + when false + backend.push_dir(root_directory) + else + backend.push_dir(root_directory, namespace: namespace) + end + + excluded_dirs.each do |dir| + backend.ignore(dir) + end + + backend.on_load do |_, const, path| + if (type = path_to_component_type(path)) + loaded_constants[type] << const + end + end + + backend.setup + end + # rubocop:enable Metrics/AbcSize + + # @api private + def path_to_component_type(path) + return unless File.file?(path) + + component_dirs + .detect { |_, dir| + path.start_with?(root_directory.join(dir).to_s) + } + &.first + end + + # @api private + def excluded_dirs + root_directory + .children + .select(&:directory?) + .reject { |dir| component_dirs.values.include?(dir.basename.to_s) } + .map { |name| root_directory.join(name) } + end + end +end diff --git a/lib/rom/setup.rb b/lib/rom/setup.rb index 5d2507979..ec9ff7ea8 100644 --- a/lib/rom/setup.rb +++ b/lib/rom/setup.rb @@ -1,27 +1,12 @@ # frozen_string_literal: true -require "rom/setup/auto_registration" +require "rom/loader" module ROM # Setup objects collect component classes during setup/finalization process # # @api public class Setup - # @return [Array] registered relation subclasses - # - # @api private - attr_reader :relation_classes - - # @return [Array] registered mapper subclasses - # - # @api private - attr_reader :mapper_classes - - # @return [Array] registered command subclasses - # - # @api private - attr_reader :command_classes - # @api private attr_reader :plugins @@ -31,57 +16,76 @@ class Setup # @api private attr_accessor :inflector + # @api private + attr_reader :components + # @api private def initialize(notifications) - @relation_classes = [] - @command_classes = [] - @mapper_classes = [] @plugins = [] @notifications = notifications @inflector = Inflector + @components = {relations: [], commands: [], mappers: []} end # Enable auto-registration for a given setup object # # @param [String, Pathname] directory The root path to components # @param [Hash] options - # @option options [Boolean, String] :namespace Enable/disable namespace or provide a custom namespace name + # @option options [Boolean] :namespace Toggle root namespace # # @return [Setup] # # @api public - def auto_registration(directory, **options) - auto_registration = AutoRegistration.new(directory, inflector: inflector, **options) - auto_registration.relations.map { |r| register_relation(r) } - auto_registration.commands.map { |r| register_command(r) } - auto_registration.mappers.map { |r| register_mapper(r) } - self + def auto_register(directory, **options) + @auto_register ||= [directory, {**options}] end # Relation sub-classes are being registered with this method during setup # # @api private def register_relation(*klasses) - @relation_classes.concat(klasses) + components[:relations].concat(klasses) end # Mapper sub-classes are being registered with this method during setup # # @api private def register_mapper(*klasses) - @mapper_classes.concat(klasses) + components[:mappers].concat(klasses) end # Command sub-classes are being registered with this method during setup # # @api private def register_command(*klasses) - @command_classes.concat(klasses) + components[:commands].concat(klasses) + end + + # @api private + def relation_classes + @relation_classes ||= components[:relations].concat(loader&.relations || []) + end + + # @api private + def command_classes + @command_classes ||= components[:commands].concat(loader&.commands || []) + end + + # @api private + def mapper_classes + @mapper_classes ||= components[:mappers].concat(loader&.mappers || []) end # @api private def register_plugin(plugin) plugins << plugin end + + private + + # @api private + def loader + @loader ||= Loader.new(@auto_register[0], **(@auto_register[1] || {})) if @auto_register + end end end diff --git a/lib/rom/support/inflector.rb b/lib/rom/support/inflector.rb index 84576219f..6fa93e74e 100644 --- a/lib/rom/support/inflector.rb +++ b/lib/rom/support/inflector.rb @@ -3,7 +3,15 @@ require "dry/inflector" module ROM + module ZeitwerkCompatibility + def camelize(name, *) + super(name) + end + end + Inflector = Dry::Inflector.new do |i| i.plural(/people\z/i, "people") end + + Inflector.extend(ZeitwerkCompatibility) end diff --git a/rom.gemspec b/rom.gemspec index 5b05a2962..24394cd3a 100644 --- a/rom.gemspec +++ b/rom.gemspec @@ -20,6 +20,7 @@ Gem::Specification.new do |gem| gem.required_ruby_version = '>= 2.4.0' gem.add_runtime_dependency 'concurrent-ruby', '~> 1.1' + gem.add_runtime_dependency 'zeitwerk' gem.add_runtime_dependency 'dry-core', '~> 0.6', '>= 0.6' gem.add_runtime_dependency 'dry-inflector', '~> 0.1' gem.add_runtime_dependency 'dry-container', '~> 0.7' diff --git a/spec/fixtures/app/commands/create_user.rb b/spec/compat/fixtures/app/commands/create_user.rb similarity index 100% rename from spec/fixtures/app/commands/create_user.rb rename to spec/compat/fixtures/app/commands/create_user.rb diff --git a/spec/fixtures/app/mappers/user_list.rb b/spec/compat/fixtures/app/mappers/user_list.rb similarity index 100% rename from spec/fixtures/app/mappers/user_list.rb rename to spec/compat/fixtures/app/mappers/user_list.rb diff --git a/spec/fixtures/app/my_commands/create_user.rb b/spec/compat/fixtures/app/my_commands/create_user.rb similarity index 100% rename from spec/fixtures/app/my_commands/create_user.rb rename to spec/compat/fixtures/app/my_commands/create_user.rb diff --git a/spec/fixtures/app/my_mappers/user_list.rb b/spec/compat/fixtures/app/my_mappers/user_list.rb similarity index 100% rename from spec/fixtures/app/my_mappers/user_list.rb rename to spec/compat/fixtures/app/my_mappers/user_list.rb diff --git a/spec/fixtures/app/my_relations/users.rb b/spec/compat/fixtures/app/my_relations/users.rb similarity index 100% rename from spec/fixtures/app/my_relations/users.rb rename to spec/compat/fixtures/app/my_relations/users.rb diff --git a/spec/fixtures/app/relations/users.rb b/spec/compat/fixtures/app/relations/users.rb similarity index 100% rename from spec/fixtures/app/relations/users.rb rename to spec/compat/fixtures/app/relations/users.rb diff --git a/spec/fixtures/custom/commands/create_user.rb b/spec/compat/fixtures/custom/commands/create_user.rb similarity index 100% rename from spec/fixtures/custom/commands/create_user.rb rename to spec/compat/fixtures/custom/commands/create_user.rb diff --git a/spec/fixtures/custom/mappers/user_list.rb b/spec/compat/fixtures/custom/mappers/user_list.rb similarity index 100% rename from spec/fixtures/custom/mappers/user_list.rb rename to spec/compat/fixtures/custom/mappers/user_list.rb diff --git a/spec/fixtures/custom/relations/users.rb b/spec/compat/fixtures/custom/relations/users.rb similarity index 100% rename from spec/fixtures/custom/relations/users.rb rename to spec/compat/fixtures/custom/relations/users.rb diff --git a/spec/fixtures/custom_namespace/commands/create_customer.rb b/spec/compat/fixtures/custom_namespace/commands/create_customer.rb similarity index 100% rename from spec/fixtures/custom_namespace/commands/create_customer.rb rename to spec/compat/fixtures/custom_namespace/commands/create_customer.rb diff --git a/spec/fixtures/custom_namespace/mappers/customer_list.rb b/spec/compat/fixtures/custom_namespace/mappers/customer_list.rb similarity index 100% rename from spec/fixtures/custom_namespace/mappers/customer_list.rb rename to spec/compat/fixtures/custom_namespace/mappers/customer_list.rb diff --git a/spec/fixtures/custom_namespace/relations/customers.rb b/spec/compat/fixtures/custom_namespace/relations/customers.rb similarity index 100% rename from spec/fixtures/custom_namespace/relations/customers.rb rename to spec/compat/fixtures/custom_namespace/relations/customers.rb diff --git a/spec/compat/fixtures/lib/persistence/commands/create_user.rb b/spec/compat/fixtures/lib/persistence/commands/create_user.rb new file mode 100644 index 000000000..e4032edf0 --- /dev/null +++ b/spec/compat/fixtures/lib/persistence/commands/create_user.rb @@ -0,0 +1,8 @@ +# frozen_string_literal: true + +module Persistence + module Commands + class CreateUser + end + end +end diff --git a/spec/compat/fixtures/lib/persistence/mappers/user_list.rb b/spec/compat/fixtures/lib/persistence/mappers/user_list.rb new file mode 100644 index 000000000..19f231b4d --- /dev/null +++ b/spec/compat/fixtures/lib/persistence/mappers/user_list.rb @@ -0,0 +1,8 @@ +# frozen_string_literal: true + +module Persistence + module Mappers + class UserList + end + end +end diff --git a/spec/compat/fixtures/lib/persistence/my_commands/create_user.rb b/spec/compat/fixtures/lib/persistence/my_commands/create_user.rb new file mode 100644 index 000000000..01d8f3395 --- /dev/null +++ b/spec/compat/fixtures/lib/persistence/my_commands/create_user.rb @@ -0,0 +1,8 @@ +# frozen_string_literal: true + +module Persistence + module MyCommands + class CreateUser + end + end +end diff --git a/spec/compat/fixtures/lib/persistence/my_mappers/user_list.rb b/spec/compat/fixtures/lib/persistence/my_mappers/user_list.rb new file mode 100644 index 000000000..cea9458a1 --- /dev/null +++ b/spec/compat/fixtures/lib/persistence/my_mappers/user_list.rb @@ -0,0 +1,8 @@ +# frozen_string_literal: true + +module Persistence + module MyMappers + class UserList + end + end +end diff --git a/spec/compat/fixtures/lib/persistence/my_relations/users.rb b/spec/compat/fixtures/lib/persistence/my_relations/users.rb new file mode 100644 index 000000000..871e8a295 --- /dev/null +++ b/spec/compat/fixtures/lib/persistence/my_relations/users.rb @@ -0,0 +1,8 @@ +# frozen_string_literal: true + +module Persistence + module MyRelations + class Users + end + end +end diff --git a/spec/compat/fixtures/lib/persistence/relations/users.rb b/spec/compat/fixtures/lib/persistence/relations/users.rb new file mode 100644 index 000000000..9b03279d9 --- /dev/null +++ b/spec/compat/fixtures/lib/persistence/relations/users.rb @@ -0,0 +1,8 @@ +# frozen_string_literal: true + +module Persistence + module Relations + class Users + end + end +end diff --git a/spec/compat/fixtures/wrong/commands/create_customer.rb b/spec/compat/fixtures/wrong/commands/create_customer.rb new file mode 100644 index 000000000..f4c4fe23d --- /dev/null +++ b/spec/compat/fixtures/wrong/commands/create_customer.rb @@ -0,0 +1,10 @@ +# frozen_string_literal: true + +module My + module NewNamespace + module Foo + class CreateCustomer + end + end + end +end diff --git a/spec/compat/fixtures/wrong/mappers/customer_list.rb b/spec/compat/fixtures/wrong/mappers/customer_list.rb new file mode 100644 index 000000000..2f7aa5e15 --- /dev/null +++ b/spec/compat/fixtures/wrong/mappers/customer_list.rb @@ -0,0 +1,10 @@ +# frozen_string_literal: true + +module My + module NewNamespace + module Foo + class CustomerList + end + end + end +end diff --git a/spec/compat/fixtures/wrong/relations/customers.rb b/spec/compat/fixtures/wrong/relations/customers.rb new file mode 100644 index 000000000..a91b85cb8 --- /dev/null +++ b/spec/compat/fixtures/wrong/relations/customers.rb @@ -0,0 +1,10 @@ +# frozen_string_literal: true + +module My + module NewNamespace + module Foo + class Customers + end + end + end +end diff --git a/spec/fixtures/xml_space/xml_commands/create_customer.rb b/spec/compat/fixtures/xml_space/xml_commands/create_customer.rb similarity index 100% rename from spec/fixtures/xml_space/xml_commands/create_customer.rb rename to spec/compat/fixtures/xml_space/xml_commands/create_customer.rb diff --git a/spec/fixtures/xml_space/xml_mappers/customer_list.rb b/spec/compat/fixtures/xml_space/xml_mappers/customer_list.rb similarity index 100% rename from spec/fixtures/xml_space/xml_mappers/customer_list.rb rename to spec/compat/fixtures/xml_space/xml_mappers/customer_list.rb diff --git a/spec/fixtures/xml_space/xml_relations/customers.rb b/spec/compat/fixtures/xml_space/xml_relations/customers.rb similarity index 100% rename from spec/fixtures/xml_space/xml_relations/customers.rb rename to spec/compat/fixtures/xml_space/xml_relations/customers.rb diff --git a/spec/unit/rom/setup/auto_registration_spec.rb b/spec/compat/unit/setup/auto_registration_spec.rb similarity index 93% rename from spec/unit/rom/setup/auto_registration_spec.rb rename to spec/compat/unit/setup/auto_registration_spec.rb index 00fb85fe9..cad579889 100644 --- a/spec/unit/rom/setup/auto_registration_spec.rb +++ b/spec/compat/unit/setup/auto_registration_spec.rb @@ -1,7 +1,6 @@ # frozen_string_literal: true -require "rom/setup" -require "rom/support/notifications" +require "rom/compat" RSpec.describe ROM::Setup, "#auto_registration" do let!(:loaded_features) { $LOADED_FEATURES.dup } @@ -21,7 +20,7 @@ context "with default component_dirs" do context "with namespace turned on" do before do - setup.auto_registration(SPEC_ROOT.join("fixtures/lib/persistence").to_s) + setup.auto_registration(SPEC_ROOT.join("compat/fixtures/lib/persistence").to_s) end describe "#relations" do @@ -45,7 +44,7 @@ context "with namespace turned off" do before do - setup.auto_registration(SPEC_ROOT.join("fixtures/app"), namespace: false) + setup.auto_registration(SPEC_ROOT.join("compat/fixtures/app"), namespace: false) end describe "#relations" do @@ -72,7 +71,7 @@ context "with namespace turned on" do before do setup.auto_registration( - SPEC_ROOT.join("fixtures/lib/persistence").to_s, + SPEC_ROOT.join("compat/fixtures/lib/persistence").to_s, component_dirs: { relations: :my_relations, mappers: :my_mappers, @@ -103,7 +102,7 @@ context "with namespace turned off" do before do setup.auto_registration( - SPEC_ROOT.join("fixtures/app"), + SPEC_ROOT.join("compat/fixtures/app"), component_dirs: { relations: :my_relations, mappers: :my_mappers, @@ -136,7 +135,7 @@ context "when namespace has subnamespace" do before do setup.auto_registration( - SPEC_ROOT.join("fixtures/custom_namespace"), + SPEC_ROOT.join("compat/fixtures/custom_namespace"), component_dirs: { relations: :relations, mappers: :mappers, @@ -183,7 +182,7 @@ module Customers context "when namespace has wrong subnamespace" do subject(:registration) do setup.auto_registration( - SPEC_ROOT.join("fixtures/wrong"), + SPEC_ROOT.join("compat/fixtures/wrong"), component_dirs: { relations: :relations, mappers: :mappers, @@ -209,7 +208,7 @@ module Customers context "when namespace does not implement subnamespace" do before do setup.auto_registration( - SPEC_ROOT.join("fixtures/custom"), + SPEC_ROOT.join("compat/fixtures/custom"), component_dirs: { relations: :relations, mappers: :mappers, @@ -248,7 +247,7 @@ module Customers before do setup.inflector = inflector setup.auto_registration( - SPEC_ROOT.join("fixtures/xml_space"), + SPEC_ROOT.join("compat/fixtures/xml_space"), component_dirs: { relations: :xml_relations, mappers: :xml_mappers, diff --git a/spec/fixtures/app/persistence/commands/create_user.rb b/spec/fixtures/app/persistence/commands/create_user.rb new file mode 100644 index 000000000..6eb0abc6c --- /dev/null +++ b/spec/fixtures/app/persistence/commands/create_user.rb @@ -0,0 +1,6 @@ +# frozen_string_literal: true + +module Commands + class CreateUser + end +end diff --git a/spec/fixtures/app/persistence/mappers/user_list.rb b/spec/fixtures/app/persistence/mappers/user_list.rb new file mode 100644 index 000000000..f6fc2b677 --- /dev/null +++ b/spec/fixtures/app/persistence/mappers/user_list.rb @@ -0,0 +1,6 @@ +# frozen_string_literal: true + +module Mappers + class UserList + end +end diff --git a/spec/fixtures/app/persistence/my_commands/create_user.rb b/spec/fixtures/app/persistence/my_commands/create_user.rb new file mode 100644 index 000000000..a324a7271 --- /dev/null +++ b/spec/fixtures/app/persistence/my_commands/create_user.rb @@ -0,0 +1,6 @@ +# frozen_string_literal: true + +module MyCommands + class CreateUser + end +end diff --git a/spec/fixtures/app/persistence/my_mappers/user_list.rb b/spec/fixtures/app/persistence/my_mappers/user_list.rb new file mode 100644 index 000000000..8795e40a9 --- /dev/null +++ b/spec/fixtures/app/persistence/my_mappers/user_list.rb @@ -0,0 +1,6 @@ +# frozen_string_literal: true + +module MyMappers + class UserList + end +end diff --git a/spec/fixtures/app/persistence/my_relations/users.rb b/spec/fixtures/app/persistence/my_relations/users.rb new file mode 100644 index 000000000..247d3dc84 --- /dev/null +++ b/spec/fixtures/app/persistence/my_relations/users.rb @@ -0,0 +1,6 @@ +# frozen_string_literal: true + +module MyRelations + class Users + end +end diff --git a/spec/fixtures/app/persistence/relations/users.rb b/spec/fixtures/app/persistence/relations/users.rb new file mode 100644 index 000000000..6a0fbf2d3 --- /dev/null +++ b/spec/fixtures/app/persistence/relations/users.rb @@ -0,0 +1,6 @@ +# frozen_string_literal: true + +module Relations + class Users + end +end diff --git a/spec/fixtures/custom/xml_space/xml_commands/create_customer.rb b/spec/fixtures/custom/xml_space/xml_commands/create_customer.rb new file mode 100644 index 000000000..c5d2cd8b7 --- /dev/null +++ b/spec/fixtures/custom/xml_space/xml_commands/create_customer.rb @@ -0,0 +1,8 @@ +# frozen_string_literal: true + +module XMLSpace + module XMLCommands + class CreateCustomer + end + end +end diff --git a/spec/fixtures/custom/xml_space/xml_mappers/customer_list.rb b/spec/fixtures/custom/xml_space/xml_mappers/customer_list.rb new file mode 100644 index 000000000..097965922 --- /dev/null +++ b/spec/fixtures/custom/xml_space/xml_mappers/customer_list.rb @@ -0,0 +1,8 @@ +# frozen_string_literal: true + +module XMLSpace + module XMLMappers + class CustomerList + end + end +end diff --git a/spec/fixtures/custom/xml_space/xml_relations/customers.rb b/spec/fixtures/custom/xml_space/xml_relations/customers.rb new file mode 100644 index 000000000..29c01893a --- /dev/null +++ b/spec/fixtures/custom/xml_space/xml_relations/customers.rb @@ -0,0 +1,8 @@ +# frozen_string_literal: true + +module XMLSpace + module XMLRelations + class Customers + end + end +end diff --git a/spec/fixtures/explicit/commands/create_user.rb b/spec/fixtures/explicit/commands/create_user.rb new file mode 100644 index 000000000..e8c720a83 --- /dev/null +++ b/spec/fixtures/explicit/commands/create_user.rb @@ -0,0 +1,8 @@ +# frozen_string_literal: true + +module Test + module Commands + class CreateUser + end + end +end diff --git a/spec/fixtures/explicit/mappers/user_list.rb b/spec/fixtures/explicit/mappers/user_list.rb new file mode 100644 index 000000000..732ae296e --- /dev/null +++ b/spec/fixtures/explicit/mappers/user_list.rb @@ -0,0 +1,8 @@ +# frozen_string_literal: true + +module Test + module Mappers + class UserList + end + end +end diff --git a/spec/fixtures/explicit/relations/users.rb b/spec/fixtures/explicit/relations/users.rb new file mode 100644 index 000000000..4d82cb2c7 --- /dev/null +++ b/spec/fixtures/explicit/relations/users.rb @@ -0,0 +1,8 @@ +# frozen_string_literal: true + +module Test + module Relations + class Users + end + end +end diff --git a/spec/fixtures/explicit_custom/my_commands/create_user.rb b/spec/fixtures/explicit_custom/my_commands/create_user.rb new file mode 100644 index 000000000..1eec6974e --- /dev/null +++ b/spec/fixtures/explicit_custom/my_commands/create_user.rb @@ -0,0 +1,8 @@ +# frozen_string_literal: true + +module Test + module MyCommands + class CreateUser + end + end +end diff --git a/spec/fixtures/explicit_custom/my_mappers/user_list.rb b/spec/fixtures/explicit_custom/my_mappers/user_list.rb new file mode 100644 index 000000000..7bf348a84 --- /dev/null +++ b/spec/fixtures/explicit_custom/my_mappers/user_list.rb @@ -0,0 +1,8 @@ +# frozen_string_literal: true + +module Test + module MyMappers + class UserList + end + end +end diff --git a/spec/fixtures/explicit_custom/my_relations/users.rb b/spec/fixtures/explicit_custom/my_relations/users.rb new file mode 100644 index 000000000..9f186b451 --- /dev/null +++ b/spec/fixtures/explicit_custom/my_relations/users.rb @@ -0,0 +1,8 @@ +# frozen_string_literal: true + +module Test + module MyRelations + class Users + end + end +end diff --git a/spec/spec_helper.rb b/spec/spec_helper.rb index 8d1245ed8..f5d7f89f3 100644 --- a/spec/spec_helper.rb +++ b/spec/spec_helper.rb @@ -8,6 +8,8 @@ require "warning" +Warning.ignore(/zeitwerk/) +Warning.ignore(/pry-byebug/) Warning.ignore(/sequel/) Warning.ignore(/mysql2/) Warning.ignore(/rspec-core/) @@ -21,18 +23,20 @@ require "rom/core" require "rom-changeset" -Dir[root.join("support/**/*.rb").to_s].each do |f| +Dir[root.join("support/**/*.rb").to_s].sort.each do |f| require f unless f.include?("coverage") end -Dir[root.join("shared/**/*.rb").to_s].each do |f| +Dir[root.join("shared/**/*.rb").to_s].sort.each do |f| require f end +# rubocop:disable Lint/SuppressedException begin require "pry-byebug" rescue LoadError end +# rubocop:enable Lint/SuppressedException module SpecProfiler def report(*) diff --git a/spec/unit/rom/setup/auto_register_spec.rb b/spec/unit/rom/setup/auto_register_spec.rb new file mode 100644 index 000000000..740c4bea5 --- /dev/null +++ b/spec/unit/rom/setup/auto_register_spec.rb @@ -0,0 +1,133 @@ +# frozen_string_literal: true + +require "rom/setup" +require "rom/support/notifications" + +RSpec.describe ROM::Setup, "#auto_register" do + subject(:setup) { ROM::Setup.new(notifications) } + + let(:notifications) { instance_double(ROM::Notifications::EventBus) } + + around do |example| + example.run + ensure + zeitwerk_teardown + end + + def zeitwerk_teardown + Zeitwerk::Registry.loaders.each(&:unload) + Zeitwerk::Registry.loaders.clear + Zeitwerk::Registry.loaders_managing_gems.clear + Zeitwerk::ExplicitNamespace.cpaths.clear + Zeitwerk::ExplicitNamespace.tracer.disable + end + + context "with default component dirs and namespace turned on" do + it "loads files and returns constants" do + setup.auto_register(SPEC_ROOT.join("fixtures/lib/persistence")) + + expect(setup.relation_classes).to eql([Persistence::Relations::Users]) + expect(setup.command_classes).to eql([Persistence::Commands::CreateUser]) + expect(setup.mapper_classes).to eql([Persistence::Mappers::UserList]) + end + end + + context "with custom component dirs and namespace turned on" do + it "loads files and returns constants" do + setup.auto_register( + SPEC_ROOT.join("fixtures/lib/persistence"), + component_dirs: { + relations: :my_relations, + mappers: :my_mappers, + commands: :my_commands + } + ) + + expect(setup.relation_classes).to eql([Persistence::MyRelations::Users]) + expect(setup.command_classes).to eql([Persistence::MyCommands::CreateUser]) + expect(setup.mapper_classes).to eql([Persistence::MyMappers::UserList]) + end + end + + context "with default component dirs and namespace set explicitly" do + it "loads files and returns constants" do + setup.auto_register(SPEC_ROOT.join("fixtures/explicit"), namespace: Test) + + expect(setup.relation_classes).to eql([Test::Relations::Users]) + expect(setup.command_classes).to eql([Test::Commands::CreateUser]) + expect(setup.mapper_classes).to eql([Test::Mappers::UserList]) + end + end + + context "with custom component dirs and namespace set explicitly" do + it "loads files and returns constants" do + setup.auto_register( + SPEC_ROOT.join("fixtures/explicit_custom"), + component_dirs: { + relations: :my_relations, + mappers: :my_mappers, + commands: :my_commands + }, + namespace: Test + ) + + expect(setup.relation_classes).to eql([Test::MyRelations::Users]) + expect(setup.command_classes).to eql([Test::MyCommands::CreateUser]) + expect(setup.mapper_classes).to eql([Test::MyMappers::UserList]) + end + end + + context "with default component dirs and namespace turned off" do + it "loads files and returns constants" do + setup.auto_register(SPEC_ROOT.join("fixtures/app/persistence"), namespace: false) + + expect(setup.relation_classes).to eql([Relations::Users]) + expect(setup.command_classes).to eql([Commands::CreateUser]) + expect(setup.mapper_classes).to eql([Mappers::UserList]) + end + end + + context "with custom component dirs and namespace turned off" do + it "loads files and returns constants" do + setup.auto_register( + SPEC_ROOT.join("fixtures/app/persistence"), + component_dirs: { + relations: :my_relations, + mappers: :my_mappers, + commands: :my_commands + }, + namespace: false + ) + + expect(setup.relation_classes).to eql([MyRelations::Users]) + expect(setup.command_classes).to eql([MyCommands::CreateUser]) + expect(setup.mapper_classes).to eql([MyMappers::UserList]) + end + end + + context "with custom component dirs and a customized inflector" do + let(:inflector) do + Dry::Inflector.new do |i| + i.acronym("XML") + end + end + + it "loads files and returns constants" do + inflector.extend(ROM::ZeitwerkCompatibility) + + setup.auto_register( + SPEC_ROOT.join("fixtures/custom/xml_space"), + inflector: inflector, + component_dirs: { + relations: :xml_relations, + mappers: :xml_mappers, + commands: :xml_commands + } + ) + + expect(setup.relation_classes).to eql([XMLSpace::XMLRelations::Customers]) + expect(setup.command_classes).to eql([XMLSpace::XMLCommands::CreateCustomer]) + expect(setup.mapper_classes).to eql([XMLSpace::XMLMappers::CustomerList]) + end + end +end