Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions Gemfile
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
12 changes: 11 additions & 1 deletion Rakefile
Original file line number Diff line number Diff line change
Expand Up @@ -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

Expand All @@ -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
8 changes: 4 additions & 4 deletions docsite/core/source/framework-setup.html.md
Original file line number Diff line number Diff line change
Expand Up @@ -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)
```

Expand All @@ -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)
```

Expand Down Expand Up @@ -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
Expand All @@ -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)
```

Expand Down
3 changes: 3 additions & 0 deletions lib/rom/compat.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
# frozen_string_literal: true

require_relative "compat/setup"
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
require "pathname"

require "rom/support/inflector"
require "rom/setup/auto_registration_strategies/base"
require_relative "base"

module ROM
module AutoRegistrationStrategies
Expand Down
30 changes: 30 additions & 0 deletions lib/rom/compat/setup.rb
Original file line number Diff line number Diff line change
@@ -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
2 changes: 1 addition & 1 deletion lib/rom/container.rb
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
151 changes: 151 additions & 0 deletions lib/rom/loader.rb
Original file line number Diff line number Diff line change
@@ -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
Loading