Module: Pakyow

Extended by:
Support::ClassState, Support::DeepFreeze
Includes:
Behavior::Initializers, Behavior::InputParsing, Behavior::Plugins, Behavior::Restarting, Behavior::Running, Behavior::Silencing, Behavior::Timezone, Behavior::Verifier, Behavior::Watching, Config, Support::Configurable, Support::Hookable, Support::Pipeline
Defined in:
lib/pakyow/environment.rb,
lib/pakyow/cli.rb,
lib/pakyow/info.rb,
lib/pakyow/task.rb,
lib/pakyow/error.rb,
lib/pakyow/types.rb,
lib/pakyow/config.rb,
lib/pakyow/errors.rb,
lib/pakyow/helper.rb,
lib/pakyow/loader.rb,
lib/pakyow/logger.rb,
lib/pakyow/plugin.rb,
lib/pakyow/version.rb,
lib/pakyow/verifier.rb,
lib/pakyow/endpoints.rb,
lib/pakyow/framework.rb,
lib/pakyow/generator.rb,
lib/pakyow/operation.rb,
lib/pakyow/validator.rb,
lib/pakyow/connection.rb,
lib/pakyow/application.rb,
lib/pakyow/plugin/state.rb,
lib/pakyow/plugin/lookup.rb,
lib/pakyow/actions/logger.rb,
lib/pakyow/process_manager.rb,
lib/pakyow/processes/proxy.rb,
lib/pakyow/actions/dispatch.rb,
lib/pakyow/behavior/plugins.rb,
lib/pakyow/behavior/running.rb,
lib/pakyow/logger/colorizer.rb,
lib/pakyow/logger/formatter.rb,
lib/pakyow/processes/server.rb,
lib/pakyow/behavior/timezone.rb,
lib/pakyow/behavior/verifier.rb,
lib/pakyow/behavior/watching.rb,
lib/pakyow/connection/params.rb,
lib/pakyow/logger/timekeeper.rb,
lib/pakyow/validations/email.rb,
lib/pakyow/actions/normalizer.rb,
lib/pakyow/application/config.rb,
lib/pakyow/behavior/silencing.rb,
lib/pakyow/generators/project.rb,
lib/pakyow/logger/destination.rb,
lib/pakyow/logger/multiplexed.rb,
lib/pakyow/rack/compatibility.rb,
lib/pakyow/validations/inline.rb,
lib/pakyow/validations/length.rb,
lib/pakyow/behavior/restarting.rb,
lib/pakyow/connection/statuses.rb,
lib/pakyow/logger/thread_local.rb,
lib/pakyow/actions/input_parser.rb,
lib/pakyow/plugin/helper_caller.rb,
lib/pakyow/validations/presence.rb,
lib/pakyow/behavior/initializers.rb,
lib/pakyow/behavior/verification.rb,
lib/pakyow/application/connection.rb,
lib/pakyow/behavior/input_parsing.rb,
lib/pakyow/logger/formatters/json.rb,
lib/pakyow/validations/acceptance.rb,
lib/pakyow/application/helpers/app.rb,
lib/pakyow/connection/query_parser.rb,
lib/pakyow/logger/formatters/human.rb,
lib/pakyow/logger/formatters/logfmt.rb,
lib/pakyow/connection/multipart_input.rb,
lib/pakyow/connection/multipart_parser.rb,
lib/pakyow/application/behavior/aspects.rb,
lib/pakyow/application/behavior/helpers.rb,
lib/pakyow/application/behavior/plugins.rb,
lib/pakyow/application/behavior/pipeline.rb,
lib/pakyow/application/behavior/rescuing.rb,
lib/pakyow/application/behavior/sessions.rb,
lib/pakyow/application/behavior/endpoints.rb,
lib/pakyow/application/behavior/isolating.rb,
lib/pakyow/application/helpers/connection.rb,
lib/pakyow/application/behavior/frameworks.rb,
lib/pakyow/application/behavior/operations.rb,
lib/pakyow/application/behavior/restarting.rb,
lib/pakyow/application/behavior/initializers.rb,
lib/pakyow/application/connection/session/base.rb,
lib/pakyow/application/connection/session/cookie.rb,
lib/pakyow/application/connection/behavior/values.rb,
lib/pakyow/application/connection/behavior/session.rb,
lib/pakyow/application/connection/behavior/verifier.rb

Overview

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

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

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

See Also:

  • Support::Configurable
  • Support::Hookable

Defined Under Namespace

Modules: Actions, Behavior, Config, Generators, Helper, Processes, Rack, Types, Validations Classes: Application, CLI, Connection, Endpoint, Endpoints, Error, Framework, Generator, InvalidData, Loader, Logger, Operation, Plugin, ProcessManager, Task, UnknownCommand, UnknownFramework, UnknownHelperContext, UnknownPlugin, UnknownType, UnknownValidationError, Validator, Verifier

Constant Summary collapse

VERSION =

Pakyow’s current version.

"1.0.6"

Class Attribute Summary collapse

Class Method Summary collapse

Class Attribute Details

.envObject (readonly)

Name of the environment



137
138
139
# File 'lib/pakyow/environment.rb', line 137

def env
  @env
end

.errorObject (readonly)

Any error encountered during the boot process



141
142
143
# File 'lib/pakyow/environment.rb', line 141

def error
  @error
end

Class Method Details

.app(app_name, path: "/", without: [], only: nil, mount: true, &block) ⇒ Object



289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
# 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

.boot(unsafe: false) ⇒ Object

Boots the environment without running it.



248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
# 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

.booted?Boolean

Returns true if the environment has booted.

Returns:

  • (Boolean)


242
243
244
# File 'lib/pakyow/environment.rb', line 242

def booted?
  @booted == true
end

.call(input) ⇒ Object



313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
# 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

.env?(name) ⇒ Boolean

Returns:

  • (Boolean)


309
310
311
# File 'lib/pakyow/environment.rb', line 309

def env?(name)
  env == name.to_sym
end

.global_loggerObject

Global log output.

Builds and returns a default global output that’s replaced in ‘setup`.



147
148
149
150
151
152
153
154
155
156
# 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

.infoObject

Returns information about the environment.



8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
# 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

.initialize_app_for_mount(mount) ⇒ Object

This method is part of a private API. You should avoid using this method if possible, as it may be removed or be changed in the future.



357
358
359
360
361
362
363
# 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

.loadObject

Loads the Pakyow environment for the current project.



180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
# 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

.load_appsObject

Loads apps located in the current project.



200
201
202
# File 'lib/pakyow/environment.rb', line 200

def load_apps
  require File.join(config.root, "config/application")
end

.load_tasksObject

This method is part of a private API. You should avoid using this method if possible, as it may be removed or be changed in the future.



345
346
347
348
349
350
351
352
353
354
# 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

.loggerObject

Logger instance for the environment.

Builds and returns a default logger that’s replaced in ‘setup`.



162
163
164
# File 'lib/pakyow/environment.rb', line 162

def logger
  @logger ||= Logger.new("dflt", output: global_logger, level: :all)
end

.mount(app, at:, &block) ⇒ Object

Mounts an app at a path.

The app can be any rack endpoint, but must implement an initializer like Pakyow::Application#initialize.

Parameters:

  • app

    the rack endpoint to mount

  • at (String)

    where the endpoint should be mounted



174
175
176
# File 'lib/pakyow/environment.rb', line 174

def mount(app, at:, &block)
  mounts << { app: app, block: block, path: at }
end

.register_framework(framework_name, framework_module) ⇒ Object



285
286
287
# File 'lib/pakyow/environment.rb', line 285

def register_framework(framework_name, framework_module)
  @frameworks[framework_name] = framework_module
end

.setup(env: nil) ⇒ Object

Prepares the environment for booting.

Parameters:

  • env (Symbol) (defaults to: nil)

    the environment to prepare for



208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
# 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