Sap v0.0.3 Sap View Source
Sap is a toolkit for Plug applications to accept and respond to HTTP requests by using a decision tree built with combinators.
Making a decision with combinators
In most Plug applications, there is some sort of match and/or dispatch process that takes place for an individual request. A request will come in, a set of pattern matching statements created will be compared against the requested path, and if there’s a match, a function will be called to send the client a response. Here, there is a clear separation between matching/filtering a request and creating a response.
With Sap, the basic idea remains the same, but the way a request is accepted and a response is created is slightly different. Combinators, a fancy name for simple functions meant to be composed with one another, are tasked with one of two goals:
- filter requests based on some criteria
- build up a response
The power of these functions comes from the ability to compose them in any order. There is no clear separation of each type of combinators, so a filtering combinator can be used both before and after a building combinator and vice versa.
Example
To help demonstrate this, let’s walk through a small example.
def app do
choose [
get ~>> resp_json ~>> choose [
path("/body2") ~>> ok("{\"data\": \"body 2\"}"),
path("/body1") ~>> ok("{\"data\": \"body 1\"}")
]
]
end
Sap.serve(app: app)
# or Plug.Adapters.Cowboy.http Sap, [app: app], []
A few combinators are used, both filtering and building, to define our application, with all of them working together to create a decision tree for dealing with requests.
choose/1
allows for multiple options at a decision tree node. get/0
only permits HTTP GET requests in the rest of a node’s definition.
resp_json/0
sets the response header for the rest of a node to
application/json
. path/1
filters out requests that do not match a
given path. ok/1
sets the response status code of 200 Ok
and the given
response body.
With the above example, all POST, PUT, PATCH, etc. requests would not be
handled. There was an explicit declaration that only GET requests are
allowed since there is only one option in the first choose/1
list. At
the second choose/1
list, the application has two options, /body1
and
/body2
.
These combinators work together through a Sap.Context
struct that is
passed between them that holds a Plug.Conn
and a status for the current
decision path. As long as the status is :ok
, further combinators can
affect the decision, but once the status is :error
, the decision path
effectively ends, giving way to the next option path.
Link to this section Summary
Functions
Callback implementation for Plug.call/2
Callback implementation for Plug.init/1
Convenience function to start a Plug adapter with Sap as the plug
Called when an application is started
Link to this section Functions
call(Plug.Conn.t(), Keyword.t()) :: Plug.Conn.t()
Callback implementation for Plug.call/2
.
Callback implementation for Plug.init/1
.
Convenience function to start a Plug adapter with Sap as the plug.
Options
:app
- (required) - the Sap app decision tree. AnArgumentError
will be raised if this option is not passed.:scheme
- desired scheme on which the Plug adapter should attach (defaults to:http
)
Adapter Options
:adapter
- desired Plug adapter (defaults toPlug.Adapters.Cowboy
)
See the Plug docs for a detailed list of possible adapter options.
Called when an application is started.
This function is called when an application is started using
Application.start/2
(and functions on top of that, such as
Application.ensure_started/2
). This function should start the top-level
process of the application (which should be the top supervisor of the
application’s supervision tree if the application follows the OTP design
principles around supervision).
start_type
defines how the application is started:
:normal
- used if the startup is a normal startup or if the application is distributed and is started on the current node because of a failover from another node and the application specification key:start_phases
is:undefined
.{:takeover, node}
- used if the application is distributed and is started on the current node because of a failover on the nodenode
.{:failover, node}
- used if the application is distributed and is started on the current node because of a failover on nodenode
, and the application specification key:start_phases
is not:undefined
.
start_args
are the arguments passed to the application in the :mod
specification key (e.g., mod: {MyApp, [:my_args]}
).
This function should either return {:ok, pid}
or {:ok, pid, state}
if
startup is successful. pid
should be the PID of the top supervisor. state
can be an arbitrary term, and if omitted will default to []
; if the
application is later stopped, state
is passed to the stop/1
callback (see
the documentation for the c:stop/1
callback for more information).
use Application
provides no default implementation for the start/2
callback.
Callback implementation for Application.start/2
.