added some clarification docs
This commit is contained in:
parent
6566c2af5c
commit
3f7bde8d30
|
@ -0,0 +1,129 @@
|
||||||
|
# Landline library and application structure
|
||||||
|
|
||||||
|
## File layout
|
||||||
|
|
||||||
|
- `/landline/lib/*.rb` - core of the library
|
||||||
|
- `/landline/lib/dsl/` - module namespaces for use by executable contexts
|
||||||
|
- `/landline/lib/path/` - nodes that extend the Path class (traversable
|
||||||
|
node/setup execution context)
|
||||||
|
- `/landline/lib/probe/` - nodes that extend the Probe class (executable
|
||||||
|
nodes/per-request execution context)
|
||||||
|
- `/landline/lib/template/` - template engine adapters
|
||||||
|
- `/landline/lib/util/` - utility classes and modules
|
||||||
|
- `/landline/lib/pattern_matching/` - classes that implement path pattern
|
||||||
|
matching (i.e. globs, regex and static traversal paths)
|
||||||
|
|
||||||
|
## Architecture overview
|
||||||
|
|
||||||
|
The following chapter defines information that pertains to the overall
|
||||||
|
structure and interaction of landline's core abstractions - Nodes, Paths
|
||||||
|
and Probes.
|
||||||
|
|
||||||
|
For context: Node is an element of the path tree, and is another name for a
|
||||||
|
Probe (which is a leaf of the tree) or a path (which is an internal vertex
|
||||||
|
of the tree)
|
||||||
|
|
||||||
|
```plaintext
|
||||||
|
Path +-> Path +-> Probe
|
||||||
|
| |
|
||||||
|
| +-> Probe
|
||||||
|
+-> Probe
|
||||||
|
|
|
||||||
|
|-> Path -> Probe
|
||||||
|
```
|
||||||
|
|
||||||
|
### Execution contexts
|
||||||
|
|
||||||
|
In Sinatra, which this library takes most of its influence from, every
|
||||||
|
request creates its own instance of the Sinatra application. This seemed
|
||||||
|
like an excessive measure to solve a trivial problem of race conditions on a
|
||||||
|
shared execution context, so to solve that, Landline introduces per-request
|
||||||
|
execution contexts, which have a life time that spans the duration of
|
||||||
|
request processing.
|
||||||
|
|
||||||
|
In short, there are two distinct execution contexts:
|
||||||
|
|
||||||
|
- Per-request execution context, which is used by blocks that process the
|
||||||
|
request and which is bound to that given request
|
||||||
|
- Setup execution context, in which the Landline node tree is constructed
|
||||||
|
|
||||||
|
For every Path-like node, the supplied block is executed in the setup
|
||||||
|
context bound to that path.
|
||||||
|
|
||||||
|
For every Probe-like node, the supplied block is executed in the per-request
|
||||||
|
context of the request that is passed to the probe.
|
||||||
|
|
||||||
|
These execution contexts also affect the receivers of the instance variable
|
||||||
|
write and read operations. Additionally, the two types have different
|
||||||
|
sets of available methods, and every DSL method has a description attached
|
||||||
|
which describes in which context that method can be used (this is due to the
|
||||||
|
limitations of the YARD parser and due to the need of showing method
|
||||||
|
descriptions in the IDE)
|
||||||
|
|
||||||
|
### Request traversal
|
||||||
|
|
||||||
|
Requests in Landline traverse through a series of paths that determine
|
||||||
|
which route the request would take. All nodes are organized in a tree
|
||||||
|
structure, where the root of the tree is a Server object that handles
|
||||||
|
things likes localized jumps, request/response conversion and response
|
||||||
|
finalization, and root-level error/`die()` handling.
|
||||||
|
|
||||||
|
Every path-like node can be supplied with callbacks that perform
|
||||||
|
the following functions:
|
||||||
|
|
||||||
|
- filters - callbacks that filter incoming request objects
|
||||||
|
- preprocessors - callbacks that don't usually affect the routing decision
|
||||||
|
but can modify requests before they come through
|
||||||
|
- postprocessors - callbacks that execute either when a request exits a
|
||||||
|
given path after not finding any suitable probe-like node or when a
|
||||||
|
response to a request is being finalized
|
||||||
|
- pipeline - single callback that is injected in the execution stack that
|
||||||
|
can be used to catch throws and exceptions
|
||||||
|
|
||||||
|
(see `/examples/logging.ru`, `/examples/norxondor_gorgonax/config.ru`)
|
||||||
|
|
||||||
|
The execution contexts for every callback (except pipeline) are shared with
|
||||||
|
those of the probe-like execution contexts, meaning that an instance
|
||||||
|
variable written to in a filter or a preprocessor can be accessed from the
|
||||||
|
probe node or a postprocessor callback.
|
||||||
|
|
||||||
|
Once all preprocessors and filters are finished successfully, the request
|
||||||
|
is passed to all subsequent graph elements (either other Paths or Probes)
|
||||||
|
to check whether a given Node accepts that element.
|
||||||
|
|
||||||
|
If the subtree of the path contains at least one probe that accepts the
|
||||||
|
request, that probe finishes processing the request and either exits
|
||||||
|
the tree immediately by throwing the `:finish` signal with a response
|
||||||
|
or by returning a valid (not nil) response from the `process` method,
|
||||||
|
in which case the response ascends from the probe back to the root.
|
||||||
|
|
||||||
|
If none of the subsequent elements of a path accepted the request, the
|
||||||
|
path executes `die(404)` if `bounce` is not enabled, and if `bounce` is
|
||||||
|
enabled, the request exits from the path and proceeds to sibling
|
||||||
|
(adjacent) paths. (see `/examples/dirbounce.ru`)
|
||||||
|
|
||||||
|
A probe can finish the request by either throwing a response, by executing a
|
||||||
|
jump to another path, by explicitly dying with a status code or by throwing
|
||||||
|
an exception (which effectively causes `die(500)`)
|
||||||
|
(see `/examples/errorpages.ru`, `/examples/jumps.ru`)
|
||||||
|
|
||||||
|
If a jump is executed, the path of the request is rewritten locally, and the
|
||||||
|
request is fed through the server again for processing. This is effectively
|
||||||
|
a localized redirect which retains accumulated request processing
|
||||||
|
information (i.e. instance variables)
|
||||||
|
(see `/examples/jumps.ru`)
|
||||||
|
|
||||||
|
The tree can be changed at runtime, but it's generally not advisable to
|
||||||
|
depend on that fact. If, eventually, some solution will be found to
|
||||||
|
optimizing trees into linear routing paths, and if that solution is proven
|
||||||
|
to improve performance, this statement might become false.
|
||||||
|
|
||||||
|
```plaintext
|
||||||
|
+--------------------------------------+
|
||||||
|
v |
|
||||||
|
run (obj) -> App#call -> Server#call +-> Path#go -> ... -> Probe#go +
|
||||||
|
| | (if app acts as a wrapper for another
|
||||||
|
<-----------------------+ | Rack server and if response is 404
|
||||||
|
(returns back to Rack server | with x-cascade header set)
|
||||||
|
if no jumps occured) +-> passthrough#call
|
||||||
|
```
|
Loading…
Reference in New Issue