Ensure prepare_container works as expected in App (#1185)

We want to ensure that the `prepare_container` block is run at the _last moment_ before the container is marked as `configured!`. With this change, this is now true for both `App` and `Slice`.

Before this change, the `prepare_container` block for `App` was being run before the app-specific configuration was applied.
This commit is contained in:
Tim Riley 2022-07-17 19:59:23 +10:00 committed by GitHub
parent f554c474ac
commit 1705bb19c8
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
4 changed files with 129 additions and 20 deletions

View File

@ -219,6 +219,7 @@ module Hanami
prepare_all
instance_exec(container, &@prepare_container_block) if @prepare_container_block
container.configured!
@prepared = true
@ -258,7 +259,6 @@ module Hanami
prepare_container_imports
prepare_container_providers
prepare_autoloader
instance_exec(container, &@prepare_container_block) if @prepare_container_block
prepare_slices
end

View File

@ -0,0 +1,123 @@
# frozen_string_literal: true
RSpec.describe "Container / prepare_container", :app_integration do
# (Most of) the examples below make their expectations on a `container_to_prepare`,
# which is the container yielded to the `Slice.prepare_container` block _at the moment
# it is called_.
#
# This around hook ensures the examples are run at the right time and container is
# available to each.
around(in_prepare_container: true) do |example|
# Eagerly capture @loaded_features here (see spec/support/app_integration.rb) since
# around hooks are run before any before hooks (where we ordinarily capture
# @loaded_features), and by invoking `example_group_instance.subject` below, we're
# making requires that we want to ensure are properly cleaned between examples.
@loaded_features = $LOADED_FEATURES.dup
slice = example.example_group_instance.subject
slice.prepare_container do |container|
example.example.example_group.let(:container_to_prepare) { container }
example.run
end
# The prepare_container block is called when the slice is prepared
slice.prepare
end
describe "in app", :in_prepare_container do
before :context do
with_directory(@dir = make_tmp_directory) do
write "config/app.rb", <<~'RUBY'
module TestApp
class App < Hanami::App
end
end
RUBY
write "app/.keep", ""
end
end
subject {
with_directory(@dir) { require "hanami/setup" }
Hanami.app
}
it "receives the container for the app" do
expect(container_to_prepare).to be TestApp::Container
end
specify "the container has been already configured by the app" do
expect(container_to_prepare.config.component_dirs.dir("app")).to be
end
specify "the container is not yet marked as configured" do
expect(container_to_prepare).not_to be_configured
end
describe "after app is prepared", in_prepare_container: false do
before do
subject.prepare_container do |container|
container.config.name = :custom_name
end
end
it "preserves any container configuration changes made via the block" do
expect { subject.prepare }
.to change { subject.container.config.name }
.to :custom_name
expect(subject.container).to be_configured
end
end
end
describe "in slice", :in_prepare_container do
before :context do
with_directory(@dir = make_tmp_directory) do
write "config/app.rb", <<~'RUBY'
module TestApp
class App < Hanami::App
end
end
RUBY
write "slices/main/.keep", ""
end
end
subject {
with_directory(@dir) { require "hanami/setup" }
Hanami.app.register_slice(:main)
}
it "receives the container for the slice" do
expect(container_to_prepare).to be Main::Container
end
specify "the container has been already configured by the slice" do
expect(container_to_prepare.config.component_dirs.dir("")).to be
end
specify "the container is not yet marked as configured" do
expect(container_to_prepare).not_to be_configured
end
describe "after slice is prepared", in_prepare_container: false do
before do
subject.prepare_container do |container|
container.config.name = :custom_name
end
end
it "preserves any container configuration changes made via the block" do
expect { subject.prepare }
.to change { subject.container.config.name }
.to :custom_name
expect(subject.container).to be_configured
end
end
end
end

View File

@ -29,8 +29,10 @@ RSpec.configure do |config|
config.include_context "Application integration", :app_integration
config.before :each, :app_integration do
@load_paths = $LOAD_PATH.dup
@loaded_features = $LOADED_FEATURES.dup
# Conditionally assign these in case they have been assigned earlier for specific
# example groups (e.g. container/prepare_container_spec.rb)
@load_paths ||= $LOAD_PATH.dup
@loaded_features ||= $LOADED_FEATURES.dup
end
config.after :each, :app_integration do
@ -56,7 +58,7 @@ RSpec.configure do |config|
$LOAD_PATH.replace(@load_paths)
# Remove any specific LOADED_FEATURES added over the course of running each example
# Remove example-specific LOADED_FEATURES added when running each example
new_features_to_keep = ($LOADED_FEATURES - @loaded_features).tap { |feats|
feats.delete_if do |path|
path =~ %r{hanami/(setup|prepare|boot|application/container/providers)} ||

View File

@ -14,20 +14,4 @@ RSpec.describe Hanami::Slice, :app_integration do
.to raise_error Hanami::SliceLoadError, /Slice must have a class name/
end
end
describe ".prepare_container" do
let(:app_modules) { %i[TestApp Slice1 Slice2] }
it "allows the user to configure the container after defaults settings have been applied" do
slice = Hanami.app.register_slice(:slice1).prepare
expect(slice.container.config.name).to eq :slice1
slice = Hanami.app.register_slice(:slice2) {
prepare_container do |container|
container.config.name = :my_slice
end
}.prepare
expect(slice.container.config.name).to eq :my_slice
end
end
end