hanami/hanami

View on GitHub
lib/hanami/components/components.rb

Summary

Maintainability
A
2 hrs
Test Coverage
require 'hanami/utils'

module Hanami
  # Registered components
  #
  # @since 0.9.0
  # @api private
  #
  # @see Hanami::Components
  module Components # rubocop:disable Metrics/ModuleLength
    # Require the entire project
    #
    # @since 0.9.0
    # @api private
    register 'all' do
      requires 'logger', 'code', 'mailer', 'model', 'apps', 'finalizers'

      resolve { true }
    end

    # Setup project's logger
    #
    # @since 1.0.0
    # @api private
    register 'logger' do
      prepare do
        require 'hanami/logger'
      end

      resolve do |configuration|
        if configuration.logger.is_a?(Array)
          if logger_interface?(configuration.logger.first)
            configuration.logger.first
          else
            Hanami::Logger.new(Hanami.environment.project_name, *configuration.logger)
          end
        end
      end
    end

    # Check if code reloading is enabled
    #
    # @since 0.9.0
    # @api private
    register 'code_reloading' do
      prepare do
        begin
          require 'shotgun'
        rescue LoadError # rubocop:disable Lint/HandleExceptions
        end
      end

      resolve do
        !!(defined?(Shotgun) && # rubocop:disable Style/DoubleNegation
           Components['environment'].code_reloading?)
      end
    end

    # @api private
    register 'code' do
      run do
        directory = Hanami.root.join('lib')

        if Hanami.code_reloading?
          Utils.reload!(directory)
        else
          Utils.require!(directory)
        end
      end
    end

    # Tries to load hanami-model, if available for the project
    #
    # @since 0.9.0
    # @api private
    #
    # @example With hanami-model
    #   Hanami::Components.resolve('model')
    #   Hanami::Components['model'] # => true
    #
    # @example Without hanami-model
    #   Hanami::Components.resolve('model')
    #   Hanami::Components['model'] # => nil
    register 'model' do
      requires 'logger', 'model.configuration', 'model.sql'

      resolve do
        if Components['model.configuration']
          Hanami::Model.load!
          Hanami::Model.configuration.logger = Components['logger']
          true
        end
      end
    end

    # Tries to evaluate hanami-model configuration, if available for the project
    #
    # @since 0.9.0
    # @api private
    #
    # @example With hanami-model
    #   Hanami::Components.resolve('model.configuration')
    #   Hanami::Components['model.configuration'].class # => Hanami::Model::Configuration
    #
    # @example Without hanami-model
    #   Hanami::Components.resolve('model.configuration')
    #   Hanami::Components['model.configuration'].class # => NilClass
    register 'model.configuration' do
      requires 'model.bundled'

      resolve do |configuration|
        if Components['model.bundled']
          Hanami::Model.instance_variable_set(:@configuration, nil) if Hanami.code_reloading?
          Hanami::Model.configure(&configuration.model)
          Hanami::Model.configuration
        end
      end
    end

    # Tries to evaluate hanami-model configuration, if available for the project
    # and it doesn't activate migrations logger
    #
    # @since 1.1.0
    # @api private
    #
    # @example With hanami-model
    #   Hanami::Components.resolve('model.configuration.no_logger')
    #   Hanami::Components['model.configuration.no_logger'].class # => Hanami::Model::Configuration
    #
    # @example Without hanami-model
    #   Hanami::Components.resolve('model.configuration.no_logger')
    #   Hanami::Components['model.configuration.no_logger'].class # => NilClass
    register 'model.configuration.no_logger' do
      requires 'model.bundled'

      prepare do
        require 'stringio'
      end

      resolve do |configuration|
        if Components['model.bundled']
          Hanami::Model.configure(&configuration.model)
          Hanami::Model.config.migrations_logger(StringIO.new)
          Hanami::Model.configuration
        end
      end
    end

    # Tries to load SQL support for hanami, if available for the project
    #
    # @since 0.9.0
    # @api private
    #
    # @example With hanami-model
    #   Hanami::Components.resolve('model.sql')
    #   Hanami::Components['model.sql'] # => true
    #
    # @example Without hanami-model
    #   Hanami::Components.resolve('model.sql')
    #   Hanami::Components['model.sql'] # => nil
    register 'model.sql' do
      requires 'model.configuration'

      prepare do
        begin
          require 'hanami/model/sql'
        rescue LoadError # rubocop:disable Lint/HandleExceptions
        end
      end

      resolve do
        true if defined?(Hanami::Model::Sql)
      end
    end

    # Check if hanami-model is bundled
    #
    # @since 0.9.0
    # @api private
    #
    # @example With hanami-model
    #   Hanami::Components.resolve('model.bundled')
    #   Hanami::Components['model.bundled'] # => true
    #
    # @example Without hanami-model
    #   Hanami::Components.resolve('model.bundled')
    #   Hanami::Components['model.bundled'] # => nil
    register 'model.bundled' do
      prepare do
        begin
          require 'hanami/model'
        rescue LoadError # rubocop:disable Lint/HandleExceptions
        end
      end

      resolve do
        true if defined?(Hanami::Model)
      end
    end

    # Tries to evaluate hanami-mailer configuration
    #
    # @since 1.0.0
    # @api private
    #
    # @example With hanami-mailer
    #   Hanami::Components.resolve('mailer.configuration')
    #   Hanami::Components['mailer.configuration'].class # => Hanami::Mailer::Configuration
    register 'mailer.configuration' do
      prepare do
        require 'hanami/mailer'
        require 'hanami/mailer/glue'
      end

      resolve do |configuration|
        unless configuration.mailer_settings.empty?
          if Hanami.code_reloading? && !Hanami::Mailer.configuration.nil?
            Hanami::Mailer.configuration = Hanami::Mailer.configuration.dup
          end

          configuration.mailer_settings.each do |settings|
            Hanami::Mailer.configure(&settings)
            Hanami::Mailer.configuration.mailers.each do |mailer|
              mailer.configuration = Hanami::Mailer.configuration.duplicate
              Hanami::Mailer.configuration.copy!(mailer)
            end
          end

          Hanami::Mailer.configuration
        end
      end
    end

    # Tries to load hanami-mailer
    #
    # @since 1.0.0
    # @api private
    #
    # @example
    #   Hanami::Components.resolve('mailer')
    #   Hanami::Components['mailer'] # => true
    register 'mailer' do
      requires 'mailer.configuration'

      resolve do
        if Components['mailer.configuration']
          Hanami::Mailer.load!
          true
        end
      end
    end

    # Loads the routes for all the mounted Hanami/Rack applications
    #
    # This is used only by `hanami routes` command.
    #
    # @since 0.9.0
    # @api private
    register 'routes.inspector' do
      requires 'apps.configurations'

      prepare do
        require 'hanami/components/routes_inspector'
      end

      resolve do |configuration|
        RoutesInspector.new(configuration)
      end
    end

    # Loads all the Hanami applications in the project
    #
    # @since 0.9.0
    # @api private
    register 'apps' do
      resolve do |configuration|
        configuration.apps do |app|
          component('app').call(app)
        end

        true
      end
    end

    # Evaluates all the Hanami applications' configurations in the project
    #
    # @since 0.9.0
    # @api private
    register 'apps.configurations' do
      resolve do |configuration|
        configuration.apps do |app|
          component('app.configuration').call(app)
        end

        true
      end
    end

    # Evaluates all the Hanami assets configurations for each application in the project
    #
    # This is used only by `hanami assets precompile` command.
    #
    # @since 0.9.0
    # @api private
    register 'apps.assets.configurations' do
      requires 'apps.configurations'

      prepare do
        require 'hanami/components/app/assets'
      end

      resolve do |configuration|
        [].tap do |result|
          configuration.apps do |app|
            result << Components::App::Assets.resolve(app)
          end
        end
      end
    end

    # Finalizers for the project
    #
    # @since 0.9.0
    # @api private
    register 'finalizers' do
      requires 'plugins', 'finalizers.initializers'

      resolve { true }
    end

    # Load project initializers
    #
    # @since 0.9.0
    # @api private
    register 'finalizers.initializers' do
      run do
        Hanami::Utils.require!(
          Hanami.root.join('config', 'initializers')
        )
      end
    end

    # Load plugins
    #
    # @since 1.2.0
    # @api private
    register 'plugins' do
      resolve do |configuration|
        Hanami.plugins.each do |plugin_configuration|
          configuration.instance_eval(&plugin_configuration)
        end
      end
    end

    # Configure, load and finalize a Hanami application in the project
    #
    # @since 0.9.0
    # @api private
    register 'app' do
      run do |app|
        ['app.configuration', 'app.frameworks', 'app.code', 'app.routes', 'app.finalizer'].each do |c|
          component(c).call(app)
        end

        resolved(app.app_name, true)
      end
    end

    # Evaluate the configuration of a single Hanami application in the project
    #
    # @since 0.9.0
    # @api private
    register 'app.configuration' do
      run do |app|
        resolved("#{app.app_name}.configuration") do
          ApplicationConfiguration.new(app.namespace, app.configurations, app.path_prefix).tap do |config|
            app.configuration = config
          end
        end
      end
    end

    # Evaluate Hanami frameworks configurations of a single Hanami application in the project
    #
    # @since 0.9.0
    # @api private
    register 'app.frameworks' do
      run do |app|
        ['app.controller', 'app.view', 'app.assets'].each do |c|
          component(c).call(app)
        end
      end
    end

    # Evaluate hanami-controller configuration of a single Hanami application in the project
    #
    # @since 0.9.0
    # @api private
    register 'app.controller' do
      prepare do
        require 'hanami/components/app/controller'
      end

      run do |app|
        Components::App::Controller.resolve(app)
      end
    end

    # Evaluate hanami-view configuration of a single Hanami application in the project
    #
    # @since 0.9.0
    # @api private
    register 'app.view' do
      prepare do
        require 'hanami/components/app/view'
      end

      run do |app|
        Components::App::View.resolve(app)
      end
    end

    # Evaluate hanami-assets configuration of a single Hanami application in the project
    #
    # @since 0.9.0
    # @api private
    register 'app.assets' do
      prepare do
        require 'hanami/components/app/assets'
      end

      run do |app|
        Components::App::Assets.resolve(app)
      end
    end

    # Load the code for a single Hanami application in the project
    #
    # @since 0.9.0
    # @api private
    register 'app.code' do
      run do |app|
        config = app.configuration
        config.load_paths.load!
      end
    end

    # Load the routes for a single Hanami application in the project
    #
    # @since 0.9.0
    # @api private
    register 'app.routes' do
      prepare do
        require 'hanami/components/app/routes'
      end

      run do |app|
        Components::App::Routes.resolve(app)
      end
    end

    # Finalize a single Hanami application in the project
    #
    # @since 0.9.0
    # @api private
    register 'app.finalizer' do
      run do |app|
        config    = app.configuration
        namespace = app.namespace

        config.middleware.load!

        namespace.module_eval %(#{namespace}::Controller.load!)
        namespace.module_eval %(#{namespace}::View.load!)
        namespace.module_eval %(#{namespace}::Assets.load!)
      end
    end
  end
end