module Pakyow
Pakyow
environment for running one or more rack apps. Multiple apps can be mounted in the environment, each one handling requests at some path.
Pakyow.configure do mount Pakyow::Application, at: "/" end
Configuration¶ ↑
The environment can be configured
Pakyow.configure do config.server.port = 2001 end
It's possible to configure environments differently.
Pakyow.configure :development do config.server.host = "pakyow.dev" end
@see Support::Configurable
Hooks¶ ↑
Hooks can be defined for the following events:
- load - configure - setup - boot - shutdown - run
Here's how to log a message after boot:
Pakyow.after "boot" do logger.info "booted" end
@see Support::Hookable
Logging¶ ↑
The environment contains a global general-purpose logger. It also provides a {RequestLogger} instance to each app for logging during a request.
Setup & Running¶ ↑
The environment can be setup and then run.
Pakyow.setup(env: :development).run
Constants
- VERSION
Pakyow's current version.
Attributes
Name of the environment
Any error encountered during the boot process
Public Class Methods
# File lib/pakyow/environment.rb, line 289 def app(app_name, path: "/", without: [], only: nil, mount: true, &block) app_name = app_name.to_sym if booted? @apps.find { |app| app.config.name == app_name } else local_frameworks = (only || frameworks.keys) - Array.ensure(without) Pakyow::Application.make(Support::ObjectName.namespace(app_name, "application")) { config.name = app_name include_frameworks(*local_frameworks) }.tap do |app| app.define(&block) if block_given? mount(app, at: path) if mount end end end
Boots the environment without running it.
# File lib/pakyow/environment.rb, line 248 def boot(unsafe: false) ensure_setup_succeeded performing :boot do # Tasks should only be available before boot. # @tasks = [] unless unsafe # Mount each app. # @apps = mounts.map { |mount| initialize_app_for_mount(mount) } # Create the callable pipeline. # @pipeline = Pakyow.__pipeline.callable(self) # Set the environment as booted ahead of telling each app that it is booted. This allows an # app's after boot hook to access the booted app through `Pakyow.app`. # @booted = true # Now tell each app that it has been booted. # @apps.select { |app| app.respond_to?(:booted) }.each(&:booted) end if config.freeze_on_boot deep_freeze unless unsafe end self rescue StandardError => error handle_boot_failure(error) end
Returns true if the environment has booted.
# File lib/pakyow/environment.rb, line 242 def booted? @booted == true end
# File lib/pakyow/environment.rb, line 313 def call(input) config.connection_class.new(input).yield_self { |connection| Async(logger: connection.logger) { # Set the request logger as a thread-local variable for when there's no other way to access # it. This originated when looking for a way to log queries with the request logger. By # setting the request logger for the current connection as thread-local we can create a # connection pointing to `Pakyow.logger`, an instance of `Pakyow::Logger::ThreadLocal`. The # thread local logger decides at the time of logging which logger to use based on an # available context, falling back to `Pakyow.global_logger`. This gets us around needing to # configure a connection per request, altering Sequel's internals, and other oddities. # # Pakyow is designed so that the connection object and its logger should always be available # anywhere you need it. If it isn't, reconsider the design before using the thread local. # Thread.current[:pakyow_logger] = connection.logger catch :halt do @pipeline.call(connection) end }.wait }.finalize rescue StandardError => error Pakyow.logger.houston(error) Async::HTTP::Protocol::Response.new( nil, 500, {}, Async::HTTP::Body::Buffered.wrap( StringIO.new("500 Low-Level Server Error") ) ) end
# File lib/pakyow/environment.rb, line 309 def env?(name) env == name.to_sym end
Global log output.
Builds and returns a default global output that's replaced in `setup`.
# File lib/pakyow/environment.rb, line 147 def global_logger unless defined?(@global_logger) require "pakyow/logger/formatters/human" @global_logger = Logger::Formatters::Human.new( Logger::Destination.new(:stdout, $stdout) ) end @global_logger end
Returns information about the environment.
# File lib/pakyow/info.rb, line 8 def self.info { versions: { ruby: "v#{RUBY_VERSION}-p#{RUBY_PATCHLEVEL} (#{RUBY_PLATFORM})", pakyow: "v#{VERSION}" }, apps: Pakyow.mounts.map { |mount| { mount_path: mount[:path], class: mount[:app].to_s, reference: mount[:app].config.name.inspect, frameworks: mount[:app].config.loaded_frameworks, app_root: File.expand_path(mount[:app].config.root) } } } end
@api private
# File lib/pakyow/environment.rb, line 357 def initialize_app_for_mount(mount) if mount[:app].ancestors.include?(Pakyow::Application) mount[:app].new(env, mount_path: mount[:path], &mount[:block]) else mount[:app].new end end
Loads the Pakyow
environment for the current project.
# File lib/pakyow/environment.rb, line 180 def load performing :load do if File.exist?(config.loader_path + ".rb") require config.loader_path else require "pakyow/integrations/bundler/setup" require "pakyow/integrations/bootsnap" require "pakyow/integrations/bundler/require" require "pakyow/integrations/dotenv" require config.environment_path load_apps end end end
Loads apps located in the current project.
# File lib/pakyow/environment.rb, line 200 def load_apps require File.join(config.root, "config/application") end
@api private
# File lib/pakyow/environment.rb, line 345 def load_tasks require "rake" require "pakyow/task" @tasks = config.tasks.paths.uniq.each_with_object([]) do |tasks_path, tasks| Dir.glob(File.join(File.expand_path(tasks_path), "**/*.rake")).each do |task_path| tasks.concat(Pakyow::Task::Loader.new(task_path).__tasks) end end end
Logger
instance for the environment.
Builds and returns a default logger that's replaced in `setup`.
# File lib/pakyow/environment.rb, line 162 def logger @logger ||= Logger.new("dflt", output: global_logger, level: :all) end
Mounts an app at a path.
The app can be any rack endpoint, but must implement an initializer like {Application#initialize}.
@param app the rack endpoint to mount @param at [String] where the endpoint should be mounted
# File lib/pakyow/environment.rb, line 174 def mount(app, at:, &block) mounts << { app: app, block: block, path: at } end
# File lib/pakyow/environment.rb, line 285 def register_framework(framework_name, framework_module) @frameworks[framework_name] = framework_module end
Prepares the environment for booting.
@param env [Symbol] the environment to prepare for
# File lib/pakyow/environment.rb, line 208 def setup(env: nil) @env = (env ||= config.default_env).to_sym load performing :configure do configure!(env) $LOAD_PATH.unshift(config.lib) end performing :setup do destinations = Logger::Multiplexed.new( *config.logger.destinations.map { |destination, io| io.sync = config.logger.sync Logger::Destination.new(destination, io) } ) @global_logger = config.logger.formatter.new(destinations) @logger = Logger::ThreadLocal.new( Logger.new("pkyw", output: @global_logger, level: config.logger.level) ) Console.logger = Logger.new("asnc", output: @global_logger, level: :warn) end self rescue => error @setup_error = error; self end
Private Class Methods
# File lib/pakyow/environment.rb, line 367 def ensure_setup_succeeded if @setup_error handle_boot_failure(@setup_error) end end
# File lib/pakyow/environment.rb, line 373 def handle_boot_failure(error) @error = error logger.houston(error) if config.exit_on_boot_failure exit(false) end end