Port message manager that emulate a request-response style communication, a'la Http.send Response request
.
Run the example like so:
cd ./example
elm-reactor
Navigate to: http://localhost:8000/index.html
port module Example exposing (..)
import Porter
import Html exposing (Html, text)
import Json.Encode as Encode
import Json.Decode as Decode
-- Configure Porter
port outgoing : Encode.Value -> Cmd msg
port incoming : (Decode.Value -> msg) -> Sub msg
porterConfig : Porter.Config String String Msg
porterConfig =
{ outgoingPort = outgoing
, incomingPort = incoming
-- Porter works with a single Request and Response data types. They can both be anything, as long as you supply decoders :)
, encodeRequest = Encode.string
, decodeResponse = Decode.string
-- Porter uses a message added to your Msg type for its internal communications (See `type Msg` below)
, porterMsg = PorterMsg
}
-- Application model
type alias Model =
{ porter : Porter.Model String String Msg
, response : String
}
init : ( Model, Cmd Msg )
init =
( { porter = Porter.init
, response = ""
}
-- Send a request through porter, specifying the response handler directly
, Porter.send porterConfig Receive (Porter.simpleRequest "Reverse me!")
)
-- Message includes Porter's message
type Msg
= PorterMsg (Porter.Msg String String Msg)
| Receive String
-- Update porter accordingly
update : Msg -> Model -> ( Model, Cmd Msg )
update msg model =
case msg of
PorterMsg porterMsg ->
let
( porterModel, porterCmd ) =
Porter.update porterConfig porterMsg model.porter
in
( { model | porter = porterModel }, porterCmd )
Receive response ->
( { model | response = response }, Cmd.none )
-- Set up porter's subscriptions
subscriptions : Model -> Sub Msg
subscriptions model =
Porter.subscriptions porterConfig
-- Any view you like
view : Model -> Html Msg
view model =
text (toString model)
--
main : Program Never Model Msg
main =
Html.program
{ init = init
, update = update
, subscriptions = subscriptions
, view = view
}
Outgoing messages into JavaScript as JSON, with an id that Porter generates and uses to match up with response handlers.
const app = Elm.Example.fullscreen()
app.ports.outgoing.subscribe(msgWithId => {
const id = msgWithId.id
const request = msgWithId.msg
// Reverse response
const response = request.split("").reverse().join("")
// Simply include the same `id` and the response under the `msg` key.
app.ports.incoming.send({
id: id,
msg: response
})
})
If you want to perform multiple requests where some of these request depend on responses from other requests, you can use Porter.request
in combination with Porter.andThen
and Porter.sendRequest
.
Porter.simpleRequest ("Reverse me too!")
|> Porter.andThen (\reversedStr -> Porter.simpleRequest (reversedStr ++ " The Quick Brown Fox!"))
|> Porter.andThen (\reversedStr -> Porter.simpleRequest (reversedStr ++ " A man a plan a canal: panama"))
|> Porter.sendRequest porterConfig Receive
- Merge
Porter.Multi
intoPorter
so now there is a single way to send requests. - Introduce
simpleRequest
in addition torequest
to cover the simple use-case when the response is not converted. - Introduce
succeed
to expose short-circuit functionality. - Remove
andThenResult
- usesucceed
and work withResult
values directly instead.
- The
Porter.Config
type now has aporterMsg
-field. - Signature of
Porter.send
was changed:- It now takes the
porterConfig
as argument, meaning (in combination with the previous change) thatCmd.map
ping the result to your Msg type is no longer necessary because this is handled for you. - Requests are now constructed using
Porter.request
and can be chained usingPorter.andThen
before passing them off toPorter.send
.
- It now takes the
So: Where in Version 1 you'd use Porter.send responseHandler request
, you'd now use Porter.send porterConfig responseHandler (Porter.request request)
.
First stable release