lib/hanami/app.rb in hanami-2.0.0.beta1.1 vs lib/hanami/app.rb in hanami-2.0.0.beta2

- old
+ new

@@ -1,8 +1,7 @@ # frozen_string_literal: true -require "zeitwerk" require_relative "configuration" require_relative "constants" require_relative "slice" require_relative "slice_name" @@ -29,33 +28,101 @@ subclass.extend(ClassMethods) @_mutex.synchronize do subclass.class_eval do @configuration = Hanami::Configuration.new(app_name: slice_name, env: Hanami.env) - @autoloader = Zeitwerk::Loader.new - prepare_base_load_path + # 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 end # App class interface module ClassMethods - attr_reader :autoloader, :configuration + attr_reader :configuration def app_name slice_name end + # Prepares the $LOAD_PATH based on the app's configured root, prepending the `lib/` + # directory if it exists. If the lib directory is already added, this will do + # nothing. + # + # In ordinary circumstances, you should never have to call this method: this method + # is called immediately upon subclassing {Hanami::App}, as a convenicence to put + # lib/ (under the default root of `Dir.pwd`) on the load path automatically. This is + # helpful if you need to require files inside the subclass body for performing + # certain app configuration steps. + # + # If you change your app's `config.root` and you need to require files from its + # `lib/` directory within your {App} subclass body, you should call + # {.prepare_load_path} explicitly after setting the new root. + # + # Otherwise, this method is called again as part of the app {.prepare} step, so if + # you've changed your app's root and do _not_ need to require files within your {App} + # subclass body, then you don't need to call this method. + # + # @example + # module MyApp + # class App < Hanami::App + # config.root = Pathname(__dir__).join("../src") + # prepare_load_path + # + # # You can make requires for your files here + # end + # end + # + # @return [self] + # + # @api public + # @since 2.0.0 + def prepare_load_path + if (lib_path = root.join(LIB_DIR)).directory? + path = lib_path.realpath.to_s + $LOAD_PATH.prepend(path) unless $LOAD_PATH.include?(path) + end + + self + end + private - def prepare_base_load_path - base_path = root.join(LIB_DIR) - $LOAD_PATH.unshift(base_path) unless $LOAD_PATH.include?(base_path) + # Uses [dotenv](https://github.com/bkeepers/dotenv) (if available) to populate `ENV` from + # various `.env` files. + # + # For a given `HANAMI_ENV` environment, the `.env` files are looked up in the following order: + # + # - .env.{environment}.local + # - .env.local (unless the environment is `test`) + # - .env.{environment} + # - .env + # + # If dotenv is unavailable, the method exits and does nothing. + def load_dotenv + return unless Hanami.bundled?("dotenv") + + hanami_env = Hanami.env + dotenv_files = [ + ".env.#{hanami_env}.local", + (".env.local" unless hanami_env == :test), + ".env.#{hanami_env}", + ".env" + ].compact + + require "dotenv" + Dotenv.load(*dotenv_files) end def prepare_all + prepare_load_path + # Make app-wide notifications available as early as possible container.use(:notifications) # Ensure all basic slice preparation is complete before we make adjustments below # (which rely on the basic prepare steps having already run) @@ -63,20 +130,13 @@ # Run specific prepare steps for the app slice. Note also that some # standard steps have been skipped via the empty method overrides below. prepare_app_component_dirs prepare_app_providers - - # The autoloader must be setup after the container is configured, which is the - # point at which any component dirs from other slices are added to the autoloader - app = self - container.after(:configure) do - app.send(:prepare_app_autoloader) - end end - # Skip standard slice prepare steps that do not apply to the app slice + # Skip standard slice prepare steps that do not apply to the app def prepare_container_component_dirs; end def prepare_container_imports; end # rubocop:disable Metrics/AbcSize @@ -120,10 +180,10 @@ require_relative "providers/rack" register_provider(:rack, source: Hanami::Providers::Rack, namespace: true) end - def prepare_app_autoloader + def prepare_autoloader # Component dirs are automatically pushed to the autoloader by dry-system's # zeitwerk plugin. This method adds other dirs that are not otherwise configured # as component dirs. # Autoload classes from `lib/[app_namespace]/`