require 'lotus/utils/class_attribute' require 'lotus/frameworks' require 'lotus/configuration' require 'lotus/loader' require 'lotus/rendering_policy' require 'lotus/middleware' module Lotus # A full stack Lotus application # # @since 0.1.0 # # @example # require 'lotus' # # module Bookshelf # class Application < Lotus::Application # end # end class Application # Override Ruby's Class#inherited # # @since 0.2.0 # @api private # # @see http://www.ruby-doc.org/core/Class.html#method-i-inherited def self.inherited(base) super base.class_eval do include Lotus::Utils::ClassAttribute class_attribute :configuration self.configuration = Configuration.new end synchronize do applications.add(base) end end # Registry of Lotus applications in the current Ruby process # # @return [Set] a set of all the registered applications # # @since 0.2.0 # @api private def self.applications synchronize do @@applications ||= Set.new 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 Lotus::Configuration # # @example # require 'lotus' # # module Bookshelf # Application < Lotus::Application # configure do # # ... # end # end # end def self.configure(environment = nil, &blk) configuration.configure(environment, &blk) end # Return the routes for this application # # @return [Lotus::Router] a route set # # @since 0.1.0 # # @see Lotus::Configuration#routes attr_reader :routes # Set the routes for this application # # @param [Lotus::Router] # # @since 0.1.0 # @api private attr_writer :routes # Rendering policy # # @param [Lotus::RenderingPolicy] # # @since 0.2.0 # @api private attr_accessor :renderer # Initialize and load a new instance of the application # # @return [Lotus::Application] a new instance of the application # # @since 0.1.0 def initialize self.class.load!(self) end # Eager load the application configuration, by activating the framework # duplication mechanisms. # # @param application [Lotus::Application, Class<Lotus::Application>] # @return void # # @since 0.1.1 # # @example # require 'lotus' # # module OneFile # class Application < Lotus::Application # configure do # routes do # get '/', to: 'dashboard#index' # end # end # # load! # end # # module Controllers::Dashboard # include OneFile::Controller # # action 'Index' do # def call(params) # self.body = 'Hello!' # end # end # end # end def self.load!(application = self) Lotus::Loader.new(application).load! end # Preload all the registered applications # # @return [void] # # @since 0.2.0 def self.preload! synchronize do applications.each(&:load!) end end # Return the configuration for this application # # @since 0.1.0 # @api private # # @see Lotus::Application.configuration def configuration self.class.configuration end # Return the application name # # @since 0.2.0 # @api private def name self.class.name end # Process a request. # This method makes Lotus 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 Lotus::Application#middleware def call(env) renderer.render(env, middleware.call(env)) end # Rack middleware stack # # @return [Lotus::Middleware] the middleware stack # # @since 0.1.0 # @api private # # @see Lotus::Middleware def middleware @middleware ||= configuration.middleware end private # Yields the given block in a critical section # # @since 0.2.0 # @api private def self.synchronize Mutex.new.synchronize do yield end end end end