- Sponsor
-
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
Open
zlw
wants to merge
7
commits into
dry-rb:main
Choose a base branch
from
zlw:zlw/engine-support-latest-dry-system
base: main
Could not load branches
Branch not found: {{ refName }}
Loading
Could not load tags
Nothing to show
Loading
Are you sure you want to change the base?
Some commits from the old base branch may be removed from the timeline,
and old review comments may become outdated.
+1,534
−138
Open
Changes from all commits
Commits
Show all changes
7 commits
Select commit
Hold shift + click to select a range
2a0259b
Add support for Rails::Engine
zlw 11cae20
Apply rubocop corrections
zlw 8df8ff5
Fix Codacy issue :scream:
zlw d4614d1
Run specs in CI with/without the engine
zlw 54a5b80
Fix deprecation warnings
zlw 35d2a5a
Fix rubocop
zlw 480d19f
Add guards in boot#start
zlw File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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 | ||
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 |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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 |
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
this is a bit puzzling tbh, when I do:
Dry::Rails::Engine.container(...) { do something with container }
it's not available untilreload!
callFinalizer
and evaluates the config again 🤔 ("worked like that before")would the same happen if container is used eg. in
before_filter
- changes done to container would "leak" into next request? this doesn't sound right... 😅There was a problem hiding this comment.
Choose a reason for hiding this comment
The 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?)