lib/hanami/application.rb in hanami-0.8.0 vs lib/hanami/application.rb in hanami-0.9.0

- old
+ new

@@ -1,12 +1,12 @@ -require 'hanami/utils/class_attribute' -require 'hanami/frameworks' -require 'hanami/configuration' -require 'hanami/loader' -require 'hanami/logger' +require 'thread' +require 'concurrent' +require 'hanami/application_name' +require 'hanami/application_namespace' +require 'hanami/application_configuration' +require 'hanami/environment_application_configurations' require 'hanami/rendering_policy' -require 'hanami/middleware' module Hanami # A full stack Hanami application # # @since 0.1.0 @@ -26,131 +26,120 @@ # # @see http://www.ruby-doc.org/core/Class.html#method-i-inherited def self.inherited(base) super - base.class_eval do - include Hanami::Utils::ClassAttribute + base.extend(ClassMethods) + base.namespace.module_eval do + class << self + # Logger for this application + # + # @return [Hanami::Logger] the logger for this Hanami application + # + # @since 0.9.0 + # @api public + # + # @example + # + # Web.logger + # Admin.logger + attr_accessor :logger - class_attribute :configuration - self.configuration = Configuration.new + # Routes for this application + # + # @return [Hanami::Routes] the routes for this Hanami application + # + # @since 0.9.0 + # @api public + # + # @example + # + # Web.routes + # Admin.routes + attr_accessor :routes + end end - - synchronize do - applications.add(base) - end end - # @since 0.8.0 - # @api private - LOCK = Mutex.new - - # Return the routes for this application + # Class interface for Hanami applications # - # @return [Hanami::Router] a route set - # - # @since 0.1.0 - # - # @see Hanami::Configuration#routes - attr_reader :routes + # @since 0.9.0 + module ClassMethods + # Override Ruby's Class#extended + # + # @since 0.9.0 + # @api private + # + # @see http://www.ruby-doc.org/core/Class.html#method-i-extended + def self.extended(base) # rubocop:disable Metrics/MethodLength + super - # Set the routes for this application - # - # @param [Hanami::Router] - # - # @since 0.1.0 - # @api private - attr_writer :routes + base.class_eval do + @namespace = ApplicationNamespace.resolve(name) + @configurations = EnvironmentApplicationConfigurations.new + @_lock = Mutex.new - # Rendering policy - # - # @param [Hanami::RenderingPolicy] - # - # @since 0.2.0 - # @api private - attr_accessor :renderer + class << self + # @since 0.9.0 + # @api private + attr_reader :namespace - # Initialize and load a new instance of the application - # - # @return [Hanami::Application] a new instance of the application - # - # @since 0.1.0 - def initialize(options = {}) - self.class.configuration.path_prefix options[:path_prefix] - self.class.load!(self) - end + # @since 0.9.0 + # @api private + attr_reader :configurations - # Return the configuration for this application - # - # @since 0.1.0 - # @api private - # - # @see Hanami::Application.configuration - def configuration - self.class.configuration - end + # @since 0.9.0 + # @api private + attr_reader :configuration + end + end + end - # Return the application name - # - # @since 0.2.0 - # @api private - def name - self.class.name - end - - # Process a request. - # This method makes Hanami applications compatible with the Rack protocol. - # - # @param env [Hash] a Rack env - # - # @return [Array] a serialized Rack response - # - # @since 0.1.0 - # - # @see http://rack.github.io - # @see Hanami::RenderingPolicy#render - # @see Hanami::Application#middleware - def call(env) - renderer.render(env, middleware.call(env)) - end - - # Rack middleware stack - # - # @return [Hanami::Middleware] the middleware stack - # - # @since 0.1.0 - # @api private - # - # @see Hanami::Middleware - def middleware - @middleware ||= configuration.middleware - end - - class << self - # @since 0.2.0 + # Hanami application name + # + # @return [String] the Hanami application name + # + # @since 0.9.0 # @api private - @@applications = Set.new + # + # @example + # require 'hanami' + # + # module Web + # class Application < Hanami::Application + # end + # end + # + # Web::Application.app_name # => "web" + def app_name + ApplicationName.new(name).to_s + end - # Registry of Hanami applications in the current Ruby process + # Set configuration # - # @return [Set] a set of all the registered applications + # @param configuration [Hanami::ApplicationConfiguration] the application configuration # - # @since 0.2.0 + # @raise [RuntimeError] if the configuration is assigned more than once + # + # @since 0.1.0 # @api private - def applications - @@applications + def configuration=(configuration) + @_lock.synchronize do + raise "Can't assign configuration more than once (#{app_name})" unless @configuration.nil? + @configuration = configuration + end end # Configure the application. # It yields the given block in the context of the configuration # # @param environment [Symbol,nil] the configuration environment name # @param blk [Proc] the configuration block # # @since 0.1.0 # - # @see Hanami::Configuration + # @see Hanami::ApplicationConfiguration # # @example # require 'hanami' # # module Bookshelf @@ -159,94 +148,66 @@ # # ... # end # end # end def configure(environment = nil, &blk) - configuration.configure(environment, &blk) + configurations.add(environment, &blk) end + end - # Eager load the application configuration, by activating the framework - # duplication mechanisms. - # - # @param application [Hanami::Application, Class<Hanami::Application>] - # @return void - # - # @since 0.1.1 - # - # @example - # require 'hanami' - # - # module OneFile - # class Application < Hanami::Application - # configure do - # routes do - # get '/', to: 'dashboard#index' - # end - # end - # - # load! - # end - # - # module Controllers::Dashboard - # class Index - # include OneFile::Action - # - # def call(params) - # self.body = 'Hello!' - # end - # end - # end - # end - def load!(application = self) - Hanami::Loader.new(application).load! - end + # Initialize and load a new instance of the application + # + # @return [Hanami::Application] a new instance of the application + # + # @since 0.1.0 + def initialize + @renderer = RenderingPolicy.new(configuration) + @middleware = configuration.middleware + end - # Preload all the registered applications, by yielding their configurations - # and preparing the frameworks. - # - # This is useful for testing suites, where we want to make Hanami frameworks - # ready, but not preload applications code. - # - # This allows to test components such as views or actions in isolation and - # to have faster boot times. - # - # @return [void] - # - # @since 0.2.0 - def preload! - synchronize do - applications.each(&:load!) - end + # Process a request. + # This method makes Hanami applications compatible with the Rack protocol. + # + # @param env [Hash] a Rack env + # + # @return [Array] a serialized Rack response + # + # @since 0.1.0 + # + # @see http://rack.github.io + # @see Hanami::RenderingPolicy#render + # @see Hanami::Application#middleware + def call(env) + renderer.render(env, middleware.call(env)) + end - nil - end + private - # Full preload for all the registered applications. - # - # This is useful in console where we want all the application code available. - # - # @return [void] - # - # @since 0.2.1 - # @api private - def preload_applications! - synchronize do - applications.each { |app| app.new } - end + # Return the configuration for this application + # + # @since 0.1.0 + # @api private + # + # @see Hanami::Application.configuration + def configuration + self.class.configuration + end - nil - end + # Rendering policy + # + # @since 0.2.0 + # @api private + # + # @see Hanami::RenderingPolicy + attr_reader :renderer - private - - # Yields the given block in a critical section - # - # @since 0.2.0 - # @api private - def synchronize - LOCK.synchronize do - yield - end - end - end + # Rack middleware stack + # + # @return [Hanami::Middleware] the middleware stack + # + # @since 0.1.0 + # @api private + # + # @see Hanami::Middleware + attr_reader :middleware end end