-
-
Notifications
You must be signed in to change notification settings - Fork 25
Add support for Rails::Engine #49
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Changes from 3 commits
2a0259b
11cae20
8df8ff5
d4614d1
54a5b80
35d2a5a
480d19f
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,44 @@ | ||
| # frozen_string_literal: true | ||
|
|
||
| module Dry | ||
| module Rails | ||
| module Engine | ||
| # Set container block that will be evaluated in the context of the container | ||
| # | ||
| # @param name [Symbol] | ||
| # @return [self] | ||
| # | ||
| # @api public | ||
| def self.container(name, &block) | ||
| _container_blocks[name] << block | ||
|
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. this is a bit puzzling tbh, when I do: would the same happen if container is used eg. in
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. @zlw setting up container should only be available during app's booting phase. Once it's done, we should literally freeze its config (which I believe already happens?) |
||
| self | ||
| end | ||
|
|
||
| # Create a new container class | ||
| # | ||
| # This is used during booting and reloading | ||
| # | ||
| # @param name [Symbol] | ||
| # @param options [Hash] Container configuration settings | ||
| # | ||
| # @return [Class] | ||
| # | ||
| # @api private | ||
| def self.create_container(options = {}) | ||
| Class.new(Container) { config.update(options) } | ||
| end | ||
|
|
||
| # @api private | ||
| def self.evaluate_initializer(name, container) | ||
| _container_blocks[name].each do |block| | ||
| container.class_eval(&block) | ||
| end | ||
| end | ||
|
|
||
| # @api private | ||
| def self._container_blocks | ||
| @_container_blocks ||= Hash.new { |h, k| h[k] = [] } | ||
| end | ||
| end | ||
| end | ||
| end | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,172 @@ | ||
| # frozen_string_literal: true | ||
|
|
||
| module Dry | ||
| module Rails | ||
| class Finalizer | ||
| def self.app_namespace_to_name(app_namespace) | ||
| app_namespace.name.underscore.to_sym | ||
| end | ||
|
|
||
| # rubocop:disable Metrics/ParameterLists | ||
| def initialize( | ||
| railtie:, | ||
| app_namespace:, | ||
| root_path:, | ||
| name: Dry::Rails.config.main_app_name, | ||
| container_const_name: Dry::Rails::Container.container_constant, | ||
| default_inflector: ActiveSupport::Inflector | ||
| ) | ||
| @railtie = railtie | ||
| @app_namespace = app_namespace | ||
| @root_path = root_path | ||
| @name = name | ||
| @container_const_name = container_const_name | ||
| @default_inflector = default_inflector | ||
| end | ||
| # rubocop:enable Metrics/ParameterLists | ||
|
|
||
| attr_reader :railtie, | ||
| :root_path, | ||
| :container_const_name | ||
|
|
||
| # Infer the default application namespace | ||
| # | ||
| # TODO: we had to rename namespace=>app_namespace because | ||
| # Rake::DSL's Kernel#namespace *sometimes* breaks things. | ||
| # Currently we are missing specs verifying that rake tasks work | ||
| # correctly and those must be added! | ||
| # | ||
| # @return [Module] | ||
| # | ||
| # @api public | ||
| attr_reader :app_namespace | ||
|
|
||
| # Code-reloading-aware finalization process | ||
| # | ||
| # This sets up `Container` and `Deps` constants, reloads them if this is in reloading mode, | ||
| # and registers default components like the railtie itself or the inflector | ||
| # | ||
| # @api public | ||
| # | ||
| # rubocop:disable Metrics/AbcSize | ||
| def finalize! | ||
| stop_features if reloading? | ||
|
|
||
| container = Dry::Rails::Engine.create_container( | ||
| root: root_path, | ||
| inflector: default_inflector, | ||
| system_dir: root_path.join("config/system"), | ||
| bootable_dirs: [root_path.join("config/system/boot")] | ||
| ) | ||
|
|
||
| # Enable :env plugin by default because it is a very common requirement | ||
| container.use :env, inferrer: -> { ::Rails.env } | ||
|
|
||
| container.register(:railtie, railtie) | ||
| container.register(:inflector, default_inflector) | ||
|
|
||
| # Remove previously defined constants, if any, so we don't end up with | ||
| # unsused constants in app's namespace when a name change happens. | ||
| remove_constant(container.auto_inject_constant) | ||
| remove_constant(container.container_constant) | ||
|
|
||
| Dry::Rails::Engine.evaluate_initializer(name, container) | ||
|
|
||
| @container_const_name = container.container_constant | ||
|
|
||
| set_or_reload(container.container_constant, container) | ||
| set_or_reload(container.auto_inject_constant, container.injector) | ||
|
|
||
| container.features.each do |feature| | ||
| container.boot(feature, from: :rails) | ||
| end | ||
|
|
||
| container.refresh_boot_files if reloading? | ||
|
|
||
| container.finalize!(freeze: !::Rails.env.test?) | ||
| end | ||
| # rubocop:enable Metrics/AbcSize | ||
|
|
||
| # Stops all configured features (bootable components) | ||
| # | ||
| # This is *crucial* when reloading code in development mode. Every bootable component | ||
| # should be able to clear the runtime from any constants that it created in its `stop` | ||
| # lifecycle step | ||
| # | ||
| # @api public | ||
| def stop_features | ||
| container.features.each do |feature| | ||
| container.stop(feature) if container.booted?(feature) | ||
| end | ||
| end | ||
|
|
||
| # Exposes the container constant | ||
| # | ||
| # @return [Dry::Rails::Container] | ||
| # | ||
| # @api public | ||
| def container | ||
| app_namespace.const_get(container_const_name, false) | ||
| end | ||
|
|
||
| # Return true if we're in code-reloading mode | ||
| # | ||
| # @api private | ||
| def reloading? | ||
| app_namespace.const_defined?(container_const_name, false) | ||
| end | ||
|
|
||
| # Return the default system name | ||
| # | ||
| # In the dry-system world containers are explicitly named using symbols, so that you can | ||
| # refer to them easily when ie importing one container into another | ||
| # | ||
| # @return [Symbol] | ||
| # | ||
| # @api private | ||
| attr_reader :name | ||
|
|
||
| # Sets or reloads a constant within the application namespace | ||
| # | ||
| # @api private | ||
| attr_reader :default_inflector | ||
|
|
||
| # @api private | ||
| def set_or_reload(const_name, const) | ||
| remove_constant(const_name) | ||
| app_namespace.const_set(const_name, const) | ||
| end | ||
|
|
||
| # @api private | ||
| def remove_constant(const_name) | ||
| if app_namespace.const_defined?(const_name, false) | ||
| app_namespace.__send__(:remove_const, const_name) | ||
| end | ||
| end | ||
| end | ||
|
|
||
| module Engine | ||
| class Finalizer | ||
| # rubocop:disable Metrics/ParameterLists | ||
| def self.new( | ||
| railtie:, | ||
| app_namespace:, | ||
| root_path:, | ||
| name: nil, | ||
| container_const_name: Dry::Rails::Container.container_constant, | ||
| default_inflector: ActiveSupport::Inflector | ||
| ) | ||
| Dry::Rails::Finalizer.new( | ||
| railtie: railtie, | ||
| app_namespace: app_namespace, | ||
| root_path: root_path, | ||
| name: name || ::Dry::Rails::Finalizer.app_namespace_to_name(app_namespace), | ||
| container_const_name: container_const_name, | ||
| default_inflector: default_inflector | ||
| ) | ||
| end | ||
| # rubocop:enable Metrics/ParameterLists | ||
| end | ||
| end | ||
| end | ||
| end |
Uh oh!
There was an error while loading. Please reload this page.