Initial commit with basic functionality and library structure.

This commit is contained in:
Connor Prussin 2017-05-25 12:12:29 -07:00
commit 17d39fcdc2
20 changed files with 477 additions and 0 deletions

3
.bowerrc Normal file
View File

@ -0,0 +1,3 @@
{
"directory": "output/components"
}

5
.gitignore vendored Normal file
View File

@ -0,0 +1,5 @@
/output
/.pulp-cache/
/.psc*
/.purs*
/.psa*

58
CONTRIBUTING.md Normal file
View File

@ -0,0 +1,58 @@
# HTTPure Contributing Guide
Welcome to HTTPure! We would love contributions from the community. Please
follow this guide when creating contributions to help make the project better!
## Logging Issues
If you find a bug or a place where documentation needs to be improved, or if you
have a feature request,
please
[submit an issue](https://github.com/cprussin/purescript-httpure/issues/new)! In
issues you submit, please be clear, and preferably have code examples indicating
what is broken, needs improvement, or what your requested API should look like.
## Contributions
All contributions to this repository should come in the form of pull requests.
All pull requests must be reviewed before being merged. Please follow these
steps for creating a successful PR:
1. [Create an issue](https://github.com/cprussin/purescript-httpure/issues/new)
for your contribution.
2. [Create a fork](https://github.com/cprussin/purescript-httpure) on github.
3. Create a branch in your fork for your contribution.
4. Add your contribution to the source tree.
5. Run the test suite. All tests MUST pass for a PR to be accepted.
6. Push your code and create a PR on github. Please make sure to reference your
issue number in your PR description.
Branch all work off the `master` branch. In the future, we will create branches
for specific release series, and `master` will be used for the current stable
release series.
### Documentation
For the most part, HTTPure's documentation is intended to be consumed
through [Pursuit](http://pursuit.purescript.org/packages/purescript-httpure). To
this end, documentation should mostly be provided inline in the codebase, and
should follow the same PR process as other commits.
We also welcome documentation in the form of guides and examples. These should
live in the [docs](docs) directory. Please ensure all guides are written in
markdown format, and all examples are fully-functional and implemented as
self-contained subdirectories under [docs/examples](docs/examples).
We try to ensure most examples have corresponding integration tests, both to add
additional testing and to ensure that examples we promote remain functional. If
you plan to contribute examples, please take a look
at [IntegrationSpec.purs](test/HTTPure/IntegrationSpec.purs).
### Code
Code should follow existing styles and all code should be accompanied with
relevant unit/integration tests. If you fix a bug, write a test. If you write a
new feature, write a test.
All tests MUST pass for your PR to be accepted. If you break a test, either fix
the test or fix the code.

20
LICENSE Normal file
View File

@ -0,0 +1,20 @@
The MIT License (MIT)
Copyright (c) 2017 Connor Prussin
Permission is hereby granted, free of charge, to any person obtaining a copy of
this software and associated documentation files (the "Software"), to deal in
the Software without restriction, including without limitation the rights to
use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
the Software, and to permit persons to whom the Software is furnished to do so,
subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.

34
Makefile Normal file
View File

@ -0,0 +1,34 @@
PULP := pulp
BOWER := bower
BOWERJSON := bower.json
SRCPATH := ./lib
TESTPATH := ./test
OUTPUT := ./output
BUILD := $(OUTPUT)/build
COMPONENTS := $(OUTPUT)/components
DOCS := $(OUTPUT)/docs
TESTMAIN := HTTPure.HTTPureSpec
SOURCES := $(SRCPATH)/**/*
TESTSOURCES := $(TESTPATH)/**/*
.PHONY: clean test
test: $(BUILD) $(TESTSOURCES)
$(PULP) test --src-path $(SRCPATH) --build-path $(BUILD) --main $(TESTMAIN)
$(BUILD): $(COMPONENTS) $(SOURCES)
$(PULP) build --src-path $(SRCPATH) --build-path $(BUILD)
touch $(BUILD)
$(COMPONENTS): $(BOWERJSON)
$(BOWER) install
clean:
rm -rf $(OUTPUT)
$(DOCS): $(COMPONENTS) $(SOURCES)
$(PULP) docs --src-path $(SRCPATH)
rm -rf $(DOCS)
mv generated-docs $(DOCS)
docs: $(DOCS)
build: $(BUILD)

52
README.md Normal file
View File

@ -0,0 +1,52 @@
# HTTPure
A purescript HTTP server framework.
## Status
This project is currently an early-stage work in progress. It is not
production-ready yet. You can track what's left before it gets production-ready
by looking at our [roadmap](TODO.md). If you'd like to help us get there
quicker, please contribute! To get started, check
our [contributing guide](CONTRIBUTING.md).
## Installation
```bash
bower install purescript-httpure
```
## Documentation
Module documentation is published
on [Pursuit](http://pursuit.purescript.org/packages/purescript-httpure).
## Quick Start
TODO
## Testing
We have a Makefile that wraps all commands for development. To run the test
suite, in the project root run:
```bash
make test
```
## Contributing
We are open to accepting contributions! Please see
the [contributing guide](CONTRIBUTING.md).
## People
HTTPure is written and maintained
by [Connor Prussin](https://connor.prussin.net).
We are open to accepting contributions! Please see
the [contributing guide](CONTRIBUTING.md).
## License
[MIT](LICENSE)

24
bower.json Normal file
View File

@ -0,0 +1,24 @@
{
"name": "purescript-httpure",
"homepage": "",
"description": "",
"license": "MIT",
"repository": {
"type": "git",
"url": ""
},
"ignore": [
"**/.*",
"output",
"test",
"bower.json"
],
"dependencies": {
"purescript-prelude": "^3.0.0",
"purescript-node-http": "^4.0.0"
},
"devDependencies": {
"purescript-psci-support": "^3.0.0",
"purescript-spec": "^1.0.0"
}
}

13
lib/HTTPure.purs Normal file
View File

@ -0,0 +1,13 @@
module HTTPure (
module HTTPure.HTTPureM,
module HTTPure.Server,
module HTTPure.Request,
module HTTPure.Response,
module HTTPure.Route
) where
import HTTPure.HTTPureM (HTTPureM)
import HTTPure.Server (serve)
import HTTPure.Request (Request, getURL)
import HTTPure.Response (Response, write)
import HTTPure.Route (Method(..), Route)

13
lib/HTTPure/HTTPureM.purs Normal file
View File

@ -0,0 +1,13 @@
module HTTPure.HTTPureM
( HTTPureM
) where
import Control.Monad.Eff (Eff)
import Prelude (Unit)
import Node.HTTP (HTTP)
-- | The `HTTPureM` monad represents actions acting over an HTTPure server
-- | lifecycle. It is the return type of all route handlers and of the `serve`
-- | function. It takes an effects row parameter which enumerates all other
-- | side-effects performed while carrying out the server actions.
type HTTPureM e = Eff (http :: HTTP | e) Unit

25
lib/HTTPure/Request.purs Normal file
View File

@ -0,0 +1,25 @@
module HTTPure.Request
( Request
, fromHTTPRequest
, getURL
) where
import Node.HTTP (HTTP, Request, requestAsStream, requestURL) as HTTP
import Node.Stream (Readable)
-- | TODO write me
type Request e = {
httpRequest :: HTTP.Request,
stream :: Readable () (http :: HTTP.HTTP | e)
}
-- | TODO write me
fromHTTPRequest :: forall e. HTTP.Request -> Request e
fromHTTPRequest request = {
httpRequest: request,
stream: HTTP.requestAsStream request
}
-- | TODO write me
getURL :: forall e. Request e -> String
getURL request = HTTP.requestURL request.httpRequest

28
lib/HTTPure/Response.purs Normal file
View File

@ -0,0 +1,28 @@
module HTTPure.Response
( Response
, fromHTTPResponse
, write
) where
import Control.Monad.Eff (Eff)
import Node.Encoding (Encoding(UTF8))
import Node.HTTP (HTTP, Response, responseAsStream) as HTTP
import Node.Stream (Writable, writeString)
import Prelude (Unit, bind, pure, unit)
-- | TODO write me
-- | TODO wrap me in a Record so that the HTTP response is accessible
type Response e = Writable () (http :: HTTP.HTTP | e)
-- | TODO write me
fromHTTPResponse :: forall e. HTTP.Response -> Response e
fromHTTPResponse = HTTP.responseAsStream
-- | TODO write me
--setStatusCode ::
-- | TODO write me
write :: forall e. Response e -> String -> Eff (http :: HTTP.HTTP | e) Unit
write response str = do
_ <- writeString response UTF8 str (pure unit)
pure unit

46
lib/HTTPure/Route.purs Normal file
View File

@ -0,0 +1,46 @@
module HTTPure.Route
( Method(..)
, Route
, RouteHandler
) where
import HTTPure.Request (Request)
import HTTPure.Response (Response)
import HTTPure.HTTPureM (HTTPureM)
-- | The available HTTP methods that a Route can service.
data Method = All | Get | Post | Put | Delete
-- | All route handler methods - that is, methods for before hooks, after hooks,
-- | or route handlers themselves - have this type signature.
type RouteHandler e = Request e -> Response e -> HTTPureM e
-- | A Route matches a given HTTP Method against a given URL string. The route
-- | string's format is inspired by express. When a request comes in that
-- | matches the route, the handler is executed against the request and the
-- | response.
type Route e = {
method :: Method,
route :: String,
handler :: RouteHandler e
}
-- The internal representation of a route. The route is converted from a String
-- to a RouteMatcher, which can cheaply match routes and extract params.
--type LoadedRoute e = {
-- method :: Method,
-- route :: RouteMatcher,
-- handler :: Request e -> Response e -> HTTPure e
--}
-- The main request handler.
-- Convert the passed in routes to their internal representation.
--loadRoutes :: forall e.
-- Array (Route e) ->
-- Array (LoadedRoute e)
--loadRoutes = map \route -> {
-- method: route.method,
-- handler: route.handler,
-- route: toRouteMatcher(route.route)
--}

40
lib/HTTPure/Server.purs Normal file
View File

@ -0,0 +1,40 @@
module HTTPure.Server (
serve
) where
import Data.Maybe (Maybe(Nothing))
import Data.Traversable (traverse_)
import Node.HTTP (Request, Response, ListenOptions, createServer, listen) as HTTP
import Node.Stream (end)
import Prelude (bind, discard, pure, unit, ($), (==))
import Data.Array (filter)
import HTTPure.Route (Route)
import HTTPure.Response (fromHTTPResponse)
import HTTPure.Request (fromHTTPRequest, getURL)
import HTTPure.HTTPureM (HTTPureM)
-- | TODO write me
handleRequest :: forall e. Array (Route e) -> HTTP.Request -> HTTP.Response -> HTTPureM e
handleRequest routes request response = do
traverse_ (\route -> route.handler req resp) (filter matching routes)
end resp (pure unit)
pure unit
where
req = fromHTTPRequest request
resp = fromHTTPResponse response
matching = \route -> route.route == getURL req
-- | TODO write me
getOptions :: Int -> HTTP.ListenOptions
getOptions port = {
hostname: "localhost",
port: port,
backlog: Nothing
}
-- | TODO write me
serve :: forall e. Array (Route e) -> Int -> HTTPureM e -> HTTPureM e
serve routes port onStarted = do
server <- HTTP.createServer $ handleRequest routes -- $ loadRoutes routes
HTTP.listen server (getOptions port) onStarted

View File

@ -0,0 +1,8 @@
module HTTPure.HTTPureMSpec where
import Prelude (Unit, pure, unit)
import Test.Spec (Spec)
import Test.Spec.Runner (RunnerEffects)
httpureMSpec :: Spec (RunnerEffects ()) Unit
httpureMSpec = pure unit

View File

@ -0,0 +1,13 @@
module HTTPure.IntegrationSpec where
import Prelude (Unit, ($))
import Test.Spec (Spec, describe, pending)
import Test.Spec.Runner (RunnerEffects)
startsServerSpec :: Spec (RunnerEffects ()) Unit
startsServerSpec =
pending "starts a server"
integrationSpec :: Spec (RunnerEffects ()) Unit
integrationSpec = describe "integration" $
startsServerSpec

View File

@ -0,0 +1,18 @@
module HTTPure.RequestSpec where
import Prelude (Unit, discard, ($))
import Test.Spec (Spec, describe, pending)
import Test.Spec.Runner (RunnerEffects)
fromHTTPRequestSpec :: Spec (RunnerEffects ()) Unit
fromHTTPRequestSpec = describe "fromHTTPRequest" $
pending "wraps an HTTP request"
getURLSpec :: Spec (RunnerEffects ()) Unit
getURLSpec = describe "getURL" $
pending "returns the URL of the request"
requestSpec :: Spec (RunnerEffects ()) Unit
requestSpec = describe "Request" do
fromHTTPRequestSpec
getURLSpec

View File

@ -0,0 +1,23 @@
module HTTPure.ResponseSpec where
import Prelude (Unit, discard, ($))
import Test.Spec (Spec, describe, pending)
import Test.Spec.Runner (RunnerEffects)
fromHTTPResponseSpec :: Spec (RunnerEffects ()) Unit
fromHTTPResponseSpec = describe "fromHTTPResponse" $
pending "wraps an HTTP response"
setStatusCodeSpec :: Spec (RunnerEffects ()) Unit
setStatusCodeSpec = describe "setStatusCode" $
pending "sets the status code"
writeSpec :: Spec (RunnerEffects ()) Unit
writeSpec = describe "write" $
pending "adds the string to the response output"
responseSpec :: Spec (RunnerEffects ()) Unit
responseSpec = describe "Response" do
fromHTTPResponseSpec
setStatusCodeSpec
writeSpec

View File

@ -0,0 +1,8 @@
module HTTPure.RouteSpec where
import Prelude (Unit, pure, unit)
import Test.Spec (Spec)
import Test.Spec.Runner (RunnerEffects)
routeSpec :: Spec (RunnerEffects ()) Unit
routeSpec = pure unit

View File

@ -0,0 +1,23 @@
module HTTPure.ServerSpec where
import Prelude (Unit, discard, ($))
import Test.Spec (Spec, describe, pending)
import Test.Spec.Runner (RunnerEffects)
handleRequestSpec :: Spec (RunnerEffects ()) Unit
handleRequestSpec = describe "handleRequest" $
pending "handles the request"
getOptionsSpec :: Spec (RunnerEffects ()) Unit
getOptionsSpec = describe "getOptions" $
pending "returns an options object"
serveSpec :: Spec (RunnerEffects ()) Unit
serveSpec = describe "serve" $
pending "starts the server"
serverSpec :: Spec (RunnerEffects ()) Unit
serverSpec = describe "Server" do
handleRequestSpec
getOptionsSpec
serveSpec

23
test/HTTPureSpec.purs Normal file
View File

@ -0,0 +1,23 @@
module HTTPure.HTTPureSpec where
import Prelude (Unit, discard, ($))
import Control.Monad.Eff (Eff)
import Test.Spec (describe)
import Test.Spec.Reporter (specReporter)
import Test.Spec.Runner (RunnerEffects, run)
import HTTPure.HTTPureMSpec (httpureMSpec)
import HTTPure.RequestSpec (requestSpec)
import HTTPure.ResponseSpec (responseSpec)
import HTTPure.RouteSpec (routeSpec)
import HTTPure.ServerSpec (serverSpec)
import HTTPure.IntegrationSpec (integrationSpec)
main :: Eff (RunnerEffects ()) Unit
main = run [ specReporter ] $ describe "HTTPure" do
httpureMSpec
requestSpec
responseSpec
routeSpec
serverSpec
integrationSpec