Add lib/ to $LOAD_PATH before loading app file (#1251)

This makes it possible to require files from lib/ at conventional spot at the top of the file, rather than inside the body of the app class.
This commit is contained in:
Tim Riley 2022-11-11 07:06:58 +11:00 committed by GitHub
parent 70d86d047e
commit c963882a3c
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
4 changed files with 60 additions and 12 deletions

View File

@ -1,5 +1,6 @@
# frozen_string_literal: true
require "pathname"
require "zeitwerk"
require_relative "hanami/constants"
@ -36,7 +37,8 @@ module Hanami
app_path = self.app_path
if app_path
require(app_path)
prepare_load_path
require(app_path.to_s)
app
elsif raise_exception
raise(
@ -47,6 +49,23 @@ module Hanami
end
end
# Prepare the load path as early as possible (based on the default root inferred from the location
# of `config/app.rb`), so `require` can work at the top of `config/app.rb`. This may be useful
# when external classes are needed for configuring certain aspects of the app.
#
# @api private
# @since 2.0.0
private_class_method def self.prepare_load_path
lib_path = app_path&.join("..", "..", LIB_DIR)
if lib_path&.directory?
path = lib_path.realpath.to_s
$LOAD_PATH.prepend(path) unless $LOAD_PATH.include?(path)
end
lib_path
end
# Returns the Hamami app class.
#
# To ensure your Hanami app is loaded, run {.setup} (or `require "hanami/setup"`) first.
@ -98,10 +117,10 @@ module Hanami
# Searches within the given directory, then searches upwards through parent directories until the
# app file can be found.
#
# @param dir [String] The directory from which to start searching. Defaults to the current
# directory.
# @param dir [String, Pathname] The directory from which to start searching. Defaults to the
# current directory.
#
# @return [String, nil] the app file path, or nil if not found.
# @return [Pathname, nil] the app file path, or nil if not found.
#
# @api public
# @since 2.0.0
@ -110,7 +129,7 @@ module Hanami
path = dir.join(APP_PATH)
if path.file?
path.to_s
path
elsif !dir.root?
app_path(dir.parent)
end

View File

@ -29,11 +29,6 @@ module Hanami
subclass.class_eval do
@config = Hanami::Config.new(app_name: slice_name, env: Hanami.env)
# Prepare the load path (based on the default root of `Dir.pwd`) as early as possible, so
# you can make a `require` inside the body of an `App` subclass, which may be useful for
# certain kinds of app configuration.
prepare_load_path
load_dotenv
end
end

View File

@ -82,6 +82,40 @@ RSpec.describe "Code loading / Loading from lib directory", :app_integration do
end
end
describe "default root with requires at top of app file" do
before :context do
with_directory(@dir = make_tmp_directory.realpath) do
write "config/app.rb", <<~'RUBY'
require "hanami"
require "external_class"
module TestApp
class App < Hanami::App
@class_from_lib = ExternalClass
def self.class_from_lib
@class_from_lib
end
end
end
RUBY
write "lib/external_class.rb", <<~'RUBY'
class ExternalClass
end
RUBY
end
end
before do
with_directory(@dir) { require "hanami/setup" }
end
specify "classes in lib/ can be required directly from the top of the app file" do
expect(Hanami.app.class_from_lib).to be ExternalClass
end
end
context "app root reconfigured" do
before :context do
with_directory(@dir = make_tmp_directory.realpath) do

View File

@ -126,7 +126,7 @@ RSpec.describe "Hanami setup", :app_integration do
with_tmp_directory(Dir.mktmpdir) do
write "config/app.rb"
expect(app_path).to match(%r{^/.*/config/app.rb$})
expect(app_path.to_s).to match(%r{^/.*/config/app.rb$})
end
end
end
@ -138,7 +138,7 @@ RSpec.describe "Hanami setup", :app_integration do
write "lib/foo/bar/.keep"
Dir.chdir("lib/foo/bar") do
expect(app_path).to match(%r{^/.*/config/app.rb$})
expect(app_path.to_s).to match(%r{^/.*/config/app.rb$})
end
end
end