lib/hanami/application.rb in hanami-2.0.0.alpha6 vs lib/hanami/application.rb in hanami-2.0.0.alpha7
- old
+ new
@@ -1,14 +1,15 @@
# frozen_string_literal: true
require "dry/system/container"
-require "dry/system/loader/autoloading"
require "hanami/configuration"
require "pathname"
require "rack"
require "zeitwerk"
+require_relative "constants"
require_relative "slice"
+require_relative "application/slice_registrar"
module Hanami
# Hanami application class
#
# @since 2.0.0
@@ -18,12 +19,14 @@
class << self
def inherited(klass)
super
@_mutex.synchronize do
klass.class_eval do
- @_mutex = Mutex.new
+ @_mutex = Mutex.new
@_configuration = Hanami::Configuration.new(application_name: name, env: Hanami.env)
+ @autoloader = Zeitwerk::Loader.new
+ @container = Class.new(Dry::System::Container)
extend ClassMethods
end
klass.send :prepare_base_load_path
@@ -35,10 +38,12 @@
# Application class interface
#
# rubocop:disable Metrics/ModuleLength
module ClassMethods
+ attr_reader :autoloader, :container
+
def self.extended(klass)
klass.class_eval do
@prepared = @booted = false
end
end
@@ -48,31 +53,18 @@
end
alias_method :config, :configuration
def prepare(provider_name = nil)
- if provider_name
- container.prepare(provider_name)
- return self
- end
+ container.prepare(provider_name) and return self if provider_name
return self if prepared?
configuration.finalize!
- load_settings
+ prepare_all
- @autoloader = Zeitwerk::Loader.new
- @container = prepare_container
- @deps_module = prepare_deps_module
-
- load_slices
- slices.each_value(&:prepare)
- slices.freeze
-
- @autoloader.setup
-
@prepared = true
self
end
def boot(&block)
@@ -80,46 +72,30 @@
prepare
container.finalize!(&block)
- slices.values.each(&:boot)
+ slices.each(&:boot)
@booted = true
self
end
def shutdown
+ slices.each(&:shutdown)
container.shutdown!
+ self
end
def prepared?
- @prepared
+ !!@prepared
end
def booted?
- @booted
+ !!@booted
end
- def autoloader
- raise "Application not yet prepared" unless defined?(@autoloader)
-
- @autoloader
- end
-
- def container
- raise "Application not yet prepared" unless defined?(@container)
-
- @container
- end
-
- def deps
- raise "Application not yet prepared" unless defined?(@deps_module)
-
- @deps_module
- end
-
def router
raise "Application not yet prepared" unless prepared?
@_mutex.synchronize do
@_router ||= load_router
@@ -129,19 +105,15 @@
def rack_app
@rack_app ||= router.to_rack_app
end
def slices
- @slices ||= {}
+ @slices ||= SliceRegistrar.new(self)
end
- def register_slice(name, **slice_args)
- raise "Slice +#{name}+ already registered" if slices.key?(name.to_sym)
-
- slice = Slice.new(self, name: name, **slice_args)
- slice.namespace.const_set :Slice, slice if slice.namespace # rubocop:disable Style/SafeNavigation
- slices[name.to_sym] = slice
+ def register_slice(...)
+ slices.register(...)
end
def register(...)
container.register(...)
end
@@ -200,12 +172,12 @@
# @api private
def component_provider(component)
raise "Hanami.application must be prepared before detecting providers" unless prepared?
- # [Admin, Main, MyApp] or [MyApp::Admin, MyApp::Main, MyApp]
- providers = slices.values + [self]
+ # e.g. [Admin, Main, MyApp]
+ providers = slices.to_a + [self]
component_class = component.is_a?(Class) ? component : component.class
component_name = component_class.name
return unless component_name
@@ -218,88 +190,62 @@
def prepare_base_load_path
base_path = File.join(root, "lib")
$LOAD_PATH.unshift base_path unless $LOAD_PATH.include?(base_path)
end
- # rubocop:disable Metrics/AbcSize
- def prepare_container
- container =
- begin
- require "#{application_name}/container"
- namespace.const_get :Container
- rescue LoadError, NameError
- namespace.const_set :Container, Class.new(Dry::System::Container)
- end
+ def prepare_all
+ load_settings
+ prepare_container_plugins
+ prepare_container_base_config
+ prepare_container_consts
+ container.configured!
+ prepare_slices
+ # For the application, the autoloader must be prepared after the slices, since
+ # they'll be configuring the autoloader with their own dirs
+ prepare_autoloader
+ end
- container.use :env, inferrer: -> { Hanami.env }
- container.use :zeitwerk, loader: autoloader, run_setup: false, eager_load: false
- container.use :notifications
+ def prepare_container_plugins
+ container.use(:env, inferrer: -> { Hanami.env })
+ container.use(:zeitwerk, loader: autoloader, run_setup: false, eager_load: false)
+ container.use(:notifications)
+ end
+ def prepare_container_base_config
container.config.root = configuration.root
container.config.inflector = configuration.inflector
container.config.provider_dirs = [
"config/providers",
Pathname(__dir__).join("application/container/providers").realpath,
]
+ end
+ def prepare_autoload_paths
# Autoload classes defined in lib/[app_namespace]/
if root.join("lib", namespace_path).directory?
- container.autoloader.push_dir(root.join("lib", namespace_path), namespace: namespace)
+ autoloader.push_dir(root.join("lib", namespace_path), namespace: namespace)
end
-
- # Add lib/ to to the $LOAD_PATH so any files there (outside the app namespace) can
- # be required
- container.add_to_load_path!("lib") if root.join("lib").directory?
-
- container.configured!
-
- container
end
- # rubocop:enable Metrics/AbcSize
- def prepare_deps_module
- define_deps_module
- end
-
- def define_deps_module
- require "#{application_name}/deps"
- namespace.const_get :Deps
- rescue LoadError, NameError
+ def prepare_container_consts
+ namespace.const_set :Container, container
namespace.const_set :Deps, container.injector
end
- def load_slices
- Dir[File.join(slices_path, "*")]
- .select(&File.method(:directory?))
- .each(&method(:load_slice))
+ def prepare_slices
+ slices.load_slices.each(&:prepare)
+ slices.freeze
end
- def slices_path
- File.join(root, config.slices_dir)
- end
-
- def load_slice(slice_path)
- slice_path = Pathname(slice_path)
-
- slice_name = slice_path.relative_path_from(Pathname(slices_path)).to_s
- slice_const_name = inflector.camelize(slice_name)
-
- if config.slices_namespace.const_defined?(slice_const_name)
- slice_module = config.slices_namespace.const_get(slice_const_name)
-
- raise "Cannot use slice +#{slice_const_name}+ since it is not a module" unless slice_module.is_a?(Module)
- else
- slice_module = Module.new
- config.slices_namespace.const_set inflector.camelize(slice_name), slice_module
+ def prepare_autoloader
+ # Autoload classes defined in lib/[app_namespace]/
+ if root.join("lib", namespace_path).directory?
+ autoloader.push_dir(root.join("lib", namespace_path), namespace: namespace)
end
- register_slice(
- slice_name,
- namespace: slice_module,
- root: slice_path.realpath
- )
+ autoloader.setup
end
def load_settings
require_relative "application/settings"
@@ -308,12 +254,9 @@
settings_class = autodiscover_application_constant(configuration.settings_class_name)
settings_class.new(configuration.settings_store)
rescue LoadError
Settings.new
end
-
- MODULE_DELIMITER = "::"
- private_constant :MODULE_DELIMITER
def autodiscover_application_constant(constants)
inflector.constantize([namespace_name, *constants].join(MODULE_DELIMITER))
end