From c963882a3cb0ed10bf96590f87c901893034b962 Mon Sep 17 00:00:00 2001 From: Tim Riley Date: Fri, 11 Nov 2022 07:06:58 +1100 Subject: [PATCH] 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. --- lib/hanami.rb | 29 +++++++++++++--- lib/hanami/app.rb | 5 --- .../code_loading/loading_from_lib_spec.rb | 34 +++++++++++++++++++ spec/integration/setup_spec.rb | 4 +-- 4 files changed, 60 insertions(+), 12 deletions(-) diff --git a/lib/hanami.rb b/lib/hanami.rb index 7d65e567..24b4537c 100644 --- a/lib/hanami.rb +++ b/lib/hanami.rb @@ -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 diff --git a/lib/hanami/app.rb b/lib/hanami/app.rb index f9d65a81..52c197a4 100644 --- a/lib/hanami/app.rb +++ b/lib/hanami/app.rb @@ -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 diff --git a/spec/integration/code_loading/loading_from_lib_spec.rb b/spec/integration/code_loading/loading_from_lib_spec.rb index 59ebb377..67f7bb9f 100644 --- a/spec/integration/code_loading/loading_from_lib_spec.rb +++ b/spec/integration/code_loading/loading_from_lib_spec.rb @@ -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 diff --git a/spec/integration/setup_spec.rb b/spec/integration/setup_spec.rb index dd70197d..16035a55 100644 --- a/spec/integration/setup_spec.rb +++ b/spec/integration/setup_spec.rb @@ -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