From 25abc3881291ddd5de6ce88d95dff0d291c44d4c Mon Sep 17 00:00:00 2001 From: Gary Burgess Date: Mon, 23 Mar 2015 00:35:59 +0000 Subject: [PATCH 01/10] `Options` approach --- .gitignore | 6 ++ bower.json | 29 ++++++ gulpfile.js | 35 +++++++ package.json | 9 ++ src/Data/Proxy.purs | 3 + src/Network/HTTP/Affjax.purs | 119 ++++++++++++++++++++++ src/Network/HTTP/Affjax/Request.purs | 84 +++++++++++++++ src/Network/HTTP/Affjax/Response.purs | 91 +++++++++++++++++ src/Network/HTTP/Affjax/ResponseType.purs | 38 +++++++ src/Network/HTTP/Method.purs | 41 ++++++++ src/Network/HTTP/MimeType.purs | 13 +++ src/Network/HTTP/MimeType/Common.purs | 42 ++++++++ src/Network/HTTP/RequestHeader.purs | 34 +++++++ src/Network/HTTP/ResponseHeader.purs | 17 ++++ src/Network/HTTP/StatusCode.purs | 12 +++ test/Main.purs | 56 ++++++++++ 16 files changed, 629 insertions(+) create mode 100644 .gitignore create mode 100644 bower.json create mode 100644 gulpfile.js create mode 100644 package.json create mode 100644 src/Data/Proxy.purs create mode 100644 src/Network/HTTP/Affjax.purs create mode 100644 src/Network/HTTP/Affjax/Request.purs create mode 100644 src/Network/HTTP/Affjax/Response.purs create mode 100644 src/Network/HTTP/Affjax/ResponseType.purs create mode 100644 src/Network/HTTP/Method.purs create mode 100644 src/Network/HTTP/MimeType.purs create mode 100644 src/Network/HTTP/MimeType/Common.purs create mode 100644 src/Network/HTTP/RequestHeader.purs create mode 100644 src/Network/HTTP/ResponseHeader.purs create mode 100644 src/Network/HTTP/StatusCode.purs create mode 100644 test/Main.purs diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..dc070b8 --- /dev/null +++ b/.gitignore @@ -0,0 +1,6 @@ +/.* +!/.gitignore +/bower_components/ +/node_modules/ +/output/ +/tmp/ diff --git a/bower.json b/bower.json new file mode 100644 index 0000000..7aa92e8 --- /dev/null +++ b/bower.json @@ -0,0 +1,29 @@ +{ + "name": "purescript-affjax", + "homepage": "https://github.com/slamdata/purescript-affjax", + "description": "An asynchronous AJAX library built using Aff.", + "keywords": [ + "purescript", + "ajax" + ], + "license": "MIT", + "ignore": [ + "**/.*", + "bower_components", + "node_modules", + "output", + "test", + "tmp", + "bower.json", + "gulpfile.js", + "package.json" + ], + "dependencies": { + "purescript-aff": "~0.7.0", + "purescript-arraybuffer-types": "~0.1.1", + "purescript-dom": "~0.1.2", + "purescript-foreign": "~0.4.1", + "purescript-integers": "~0.0.1", + "purescript-options": "~0.2.1" + } +} diff --git a/gulpfile.js b/gulpfile.js new file mode 100644 index 0000000..b5f865c --- /dev/null +++ b/gulpfile.js @@ -0,0 +1,35 @@ +"use strict"; + +var gulp = require("gulp"); +var plumber = require("gulp-plumber"); +var purescript = require("gulp-purescript"); +var jsvalidate = require("gulp-jsvalidate"); + +gulp.task("make", function() { + return gulp.src(["src/**/*.purs", "bower_components/purescript-*/src/**/*.purs"]) + .pipe(plumber()) + .pipe(purescript.pscMake()); +}); + +gulp.task("make-test", function() { + return gulp.src(["src/**/*.purs", "test/**/*.purs", "bower_components/purescript-*/src/**/*.purs"]) + .pipe(plumber()) + .pipe(purescript.psc({ main: "Test.Main", output: "test.js" })) + .pipe(gulp.dest("tmp/")); +}); + +gulp.task("jsvalidate", ["make"], function () { + return gulp.src("output/**/*.js") + .pipe(plumber()) + .pipe(jsvalidate()); +}); + +gulp.task("docs", function () { + return gulp.src("src/**/*.purs") + .pipe(plumber()) + .pipe(purescript.pscDocs()) + .pipe(gulp.dest("README.md")); +}); + +// gulp.task("default", ["jsvalidate", "docs"]); +gulp.task("default", ["make-test"]); diff --git a/package.json b/package.json new file mode 100644 index 0000000..327f797 --- /dev/null +++ b/package.json @@ -0,0 +1,9 @@ +{ + "private": true, + "devDependencies": { + "gulp": "^3.8.11", + "gulp-jsvalidate": "^1.0.1", + "gulp-plumber": "^1.0.0", + "gulp-purescript": "^0.1.2" + } +} diff --git a/src/Data/Proxy.purs b/src/Data/Proxy.purs new file mode 100644 index 0000000..3418acd --- /dev/null +++ b/src/Data/Proxy.purs @@ -0,0 +1,3 @@ +module Data.Proxy where + +data Proxy a = Proxy diff --git a/src/Network/HTTP/Affjax.purs b/src/Network/HTTP/Affjax.purs new file mode 100644 index 0000000..f0a191c --- /dev/null +++ b/src/Network/HTTP/Affjax.purs @@ -0,0 +1,119 @@ +module Network.HTTP.Affjax + ( Ajax() + , AffjaxOptions() + , AffjaxResponse() + , url, method, content, headers, username, password + , affjax + , affjax' + ) where + +import Control.Monad.Aff (Aff(), makeAff) +import Control.Monad.Eff (Eff()) +import Control.Monad.Eff.Exception (Error()) +import Data.Foreign (Foreign(..)) +import Data.Function (Fn4(), runFn4) +import Data.Options (Option(), Options(), IsOption, options, (:=), opt) +import Data.Proxy (Proxy(..)) +import Network.HTTP.Affjax.Request +import Network.HTTP.Affjax.Response +import Network.HTTP.Affjax.ResponseType +import Network.HTTP.Method (Method()) +import Network.HTTP.RequestHeader (RequestHeader()) +import Network.HTTP.ResponseHeader (ResponseHeader(), responseHeader) +import Network.HTTP.StatusCode (StatusCode()) + +-- | The effect type for AJAX requests made with Affjax. +foreign import data Ajax :: ! + +-- | Options type for Affjax requests. +foreign import data AffjaxOptions :: * -> * + +-- | The type of records that will be received as an Affjax response. +type AffjaxResponse a = + { status :: StatusCode + , headers :: [ResponseHeader] + , response :: a + } + +-- | Sets the URL for a request. +url :: forall a. Option (AffjaxOptions a) String +url = opt "url" + +-- | Sets the HTTP method for a request. +method :: forall a. Option (AffjaxOptions a) Method +method = opt "method" + +-- | Sets the content to send in a request. +content :: forall a. (Requestable a, IsOption a) => Option (AffjaxOptions a) a +content = opt "content" + +-- | Sets the headers to send with a request. +headers :: forall a. Option (AffjaxOptions a) [RequestHeader] +headers = opt "headers" + +-- | Sets the HTTP auth username to send with a request. +username :: forall a. Option (AffjaxOptions a) String +username = opt "username" + +-- | Sets the HTTP auth password to send with a request. +password :: forall a. Option (AffjaxOptions a) String +password = opt "password" + +-- | Sets the expected response type for a request. This is not exposed outside +-- | of the module as the `ResponseType` is set based on the `Responsable` +-- | instance for the expected result content type. +responseType = opt "responseType" :: forall a. Option (AffjaxOptions a) ResponseType + +-- | Runs a request. +affjax :: forall e a b. (Requestable a, Responsable b) => + Options (AffjaxOptions a) -> + Aff (ajax :: Ajax | e) (AffjaxResponse b) +affjax = makeAff <<< affjax' + +-- | Runs a request directly in Eff. +affjax' :: forall e a b. (Requestable a, Responsable b) => + Options (AffjaxOptions a) -> + (Error -> Eff (ajax :: Ajax | e) Unit) -> + (AffjaxResponse b -> Eff (ajax :: Ajax | e) Unit) -> + Eff (ajax :: Ajax | e) Unit +affjax' opts eb cb = + let opts' = opts <> responseType := toResponseType (Proxy :: Proxy b) + in runFn4 unsafeAjax responseHeader (options opts') eb cb + +foreign import unsafeAjax + """ + function unsafeAjax (mkHeader, options, errback, callback) { + return function () { + var xhr = new XMLHttpRequest(); + xhr.open(options.method || "GET", options.url || "/", true, options.username, options.password); + if (options.headers) { + for (var i = 0, header; header = options.headers[i]; i++) { + xhr.setRequestHeader(header.field, header.value); + } + } + xhr.onerror = function (err) { + errback(err)(); + }; + xhr.onload = function () { + callback({ + status: xhr.status, + headers: xhr.getAllResponseHeaders().split("\n") + .filter(function (header) { + return header.length > 0; + }) + .map(function (header) { + var i = header.indexOf(":"); + return mkHeader(header.substring(0, i))(header.substring(i + 2)); + }), + response: xhr.response + })(); + }; + if (options.responseType) xhr.responseType = options.responseType; + xhr.send(options.content); + }; + } + """ :: forall e a b. Fn4 (String -> String -> ResponseHeader) + Foreign + (Error -> Eff (ajax :: Ajax | e) Unit) + (AffjaxResponse b -> Eff (ajax :: Ajax | e) Unit) + (Eff (ajax :: Ajax | e) Unit) diff --git a/src/Network/HTTP/Affjax/Request.purs b/src/Network/HTTP/Affjax/Request.purs new file mode 100644 index 0000000..b082dd1 --- /dev/null +++ b/src/Network/HTTP/Affjax/Request.purs @@ -0,0 +1,84 @@ +module Network.HTTP.Affjax.Request + ( RequestContent() + , Requestable, toContent + ) where + +import Data.Options (Option(), Options(), IsOption, optionFn, (:=)) +import DOM (Document()) +import DOM.File (Blob()) +import DOM.XHR (FormData()) +import Network.HTTP.MimeType (MimeType()) +import qualified Data.ArrayBuffer.Types as A + +-- | Type representing all content types that be sent via XHR (ArrayBufferView, +-- | Blob, Document, String, FormData). +foreign import data RequestContent :: * + +instance isOptionRequestContent :: IsOption RequestContent where + (:=) = unsafeIsOption + +-- | A class for types that can be converted to values that can be sent with +-- | XHR requests. +class Requestable a where + toContent :: a -> RequestContent + +instance requestableAjaxRequestContent :: Requestable RequestContent where + toContent = id + +instance requestableInt8Array :: Requestable (A.ArrayView A.Int8) where + toContent = unsafeConversion + +instance requestableInt16Array :: Requestable (A.ArrayView A.Int16) where + toContent = unsafeConversion + +instance requestableInt32Array :: Requestable (A.ArrayView A.Int32) where + toContent = unsafeConversion + +instance requestableUint8Array :: Requestable (A.ArrayView A.Uint8) where + toContent = unsafeConversion + +instance requestableUint16Array :: Requestable (A.ArrayView A.Uint16) where + toContent = unsafeConversion + +instance requestableUint32Array :: Requestable (A.ArrayView A.Uint32) where + toContent = unsafeConversion + +instance requestableUint8ClampedArray :: Requestable (A.ArrayView A.Uint8Clamped) where + toContent = unsafeConversion + +instance requestableFloat32Array :: Requestable (A.ArrayView A.Float32) where + toContent = unsafeConversion + +instance requestableFloat64Array :: Requestable (A.ArrayView A.Float64) where + toContent = unsafeConversion + +instance requestableBlob :: Requestable Blob where + toContent = unsafeConversion + +instance requestableDocument :: Requestable Document where + toContent = unsafeConversion + +instance requestableString :: Requestable String where + toContent = unsafeConversion + +instance requestableFormData :: Requestable FormData where + toContent = unsafeConversion + +instance requestableUnit :: Requestable Unit where + toContent = unsafeConversion + +foreign import unsafeIsOption + """ + function unsafeIsOption(k) { + return function (v) { + return [[k, v]]; + }; + } + """ :: forall b a. (Option b a) -> a -> (Options b) + +foreign import unsafeConversion + """ + function unsafeConversion (x) { + return x; + } + """ :: forall a b. a -> b diff --git a/src/Network/HTTP/Affjax/Response.purs b/src/Network/HTTP/Affjax/Response.purs new file mode 100644 index 0000000..0d3bc58 --- /dev/null +++ b/src/Network/HTTP/Affjax/Response.purs @@ -0,0 +1,91 @@ +module Network.HTTP.Affjax.Response + ( ResponseContent() + , Responsable, toResponseType, fromContent + ) where + +import Data.Either (Either(..)) +import Data.Foreign (Foreign(), ForeignError()) +import Data.Options (IsOption, optionFn, (:=)) +import Data.Proxy (Proxy()) +import DOM (Document()) +import DOM.File (Blob()) +import DOM.XHR (FormData()) +import Network.HTTP.Affjax.ResponseType +import qualified Data.ArrayBuffer.Types as A + +-- | Type representing content types that be received from an XHR request +-- | (ArrayBuffer, Blob, Document, JSON, String). +type ResponseContent = Foreign + +-- | Class for types that converted from values returned from an XHR request. +class Responsable a where + toResponseType :: Proxy a -> ResponseType + fromContent :: ResponseContent -> Either ForeignError a + +instance responsableUnit :: Responsable Unit where + toResponseType _ = StringResponse + fromContent _ = Right unit + +instance responsableInt8Array :: Responsable (A.ArrayView A.Int8) where + toResponseType _ = ArrayBufferResponse + fromContent = arrayBufferConversion + +instance responsableInt16Array :: Responsable (A.ArrayView A.Int16) where + toResponseType _ = ArrayBufferResponse + fromContent = arrayBufferConversion + +instance responsableInt32Array :: Responsable (A.ArrayView A.Int32) where + toResponseType _ = ArrayBufferResponse + fromContent = arrayBufferConversion + +instance responsableUint8Array :: Responsable (A.ArrayView A.Uint8) where + toResponseType _ = ArrayBufferResponse + fromContent = arrayBufferConversion + +instance responsableUint16Array :: Responsable (A.ArrayView A.Uint16) where + toResponseType _ = ArrayBufferResponse + fromContent = arrayBufferConversion + +instance responsableUint32Array :: Responsable (A.ArrayView A.Uint32) where + toResponseType _ = ArrayBufferResponse + fromContent = arrayBufferConversion + +instance responsableUint8ClampedArray :: Responsable (A.ArrayView A.Uint8Clamped) where + toResponseType _ = ArrayBufferResponse + fromContent = arrayBufferConversion + +instance responsableFloat32Array :: Responsable (A.ArrayView A.Float32) where + toResponseType _ = ArrayBufferResponse + fromContent = arrayBufferConversion + +instance responsableFloat64Array :: Responsable (A.ArrayView A.Float64) where + toResponseType _ = ArrayBufferResponse + fromContent = arrayBufferConversion + +instance responsableBlob :: Responsable Blob where + toResponseType _ = BlobResponse + fromContent = unsafeConversion + +instance responsableDocument :: Responsable Document where + toResponseType _ = DocumentResponse + fromContent = unsafeConversion + +instance responsableString :: Responsable String where + toResponseType _ = StringResponse + fromContent = Right <<< unsafeConversion + +-- TODO: this, properly +foreign import arrayBufferConversion + """ + function arrayBufferConversion (x) { + return x; + } + """ :: forall a b. a -> b + +-- TODO: not this either, at least use foreign to check the tag of returned values to ensure they are not null, etc. +foreign import unsafeConversion + """ + function unsafeConversion (x) { + return x; + } + """ :: forall a b. a -> b diff --git a/src/Network/HTTP/Affjax/ResponseType.purs b/src/Network/HTTP/Affjax/ResponseType.purs new file mode 100644 index 0000000..7288c21 --- /dev/null +++ b/src/Network/HTTP/Affjax/ResponseType.purs @@ -0,0 +1,38 @@ +module Network.HTTP.Affjax.ResponseType where + +import Data.Options (IsOption, optionFn, (:=)) + +-- | Valid response types for an AJAX request. This is used to determine the +-- | `ResponseContent` type for a request. +data ResponseType + = ArrayBufferResponse + | BlobResponse + | DocumentResponse + | JSONResponse + | StringResponse + +instance eqResponseType :: Eq ResponseType where + (==) ArrayBufferResponse ArrayBufferResponse = true + (==) BlobResponse BlobResponse = true + (==) DocumentResponse DocumentResponse = true + (==) JSONResponse JSONResponse = true + (==) StringResponse StringResponse = true + (==) _ _ = false + (/=) x y = not (x == y) + +instance showResponseType :: Show ResponseType where + show ArrayBufferResponse = "ArrayBufferResponse" + show BlobResponse = "BlobResponse" + show DocumentResponse = "DocumentResponse" + show JSONResponse = "JSONResponse" + show StringResponse = "StringResponse" + +instance isOptionResponseType :: IsOption ResponseType where + (:=) k a = (optionFn k) := ajaxResponseTypeToString a + +ajaxResponseTypeToString :: ResponseType -> String +ajaxResponseTypeToString ArrayBufferResponse = "arraybuffer" +ajaxResponseTypeToString BlobResponse = "blob" +ajaxResponseTypeToString DocumentResponse = "document" +ajaxResponseTypeToString JSONResponse = "json" +ajaxResponseTypeToString StringResponse = "text" diff --git a/src/Network/HTTP/Method.purs b/src/Network/HTTP/Method.purs new file mode 100644 index 0000000..c6b2b54 --- /dev/null +++ b/src/Network/HTTP/Method.purs @@ -0,0 +1,41 @@ +module Network.HTTP.Method where + +import Data.Options (IsOption, optionFn, (:=)) + +data Method + = DELETE + | GET + | HEAD + | OPTIONS + | PATCH + | POST + | PUT + | CustomMethod String + +instance eqMethod :: Eq Method where + (==) DELETE DELETE = true + (==) GET GET = true + (==) HEAD HEAD = true + (==) OPTIONS OPTIONS = true + (==) PATCH PATCH = true + (==) POST POST = true + (==) PUT PUT = true + (==) _ _ = false + (/=) x y = not (x == y) + +instance showMethod :: Show Method where + show DELETE = "DELETE" + show GET = "GET" + show HEAD = "HEAD" + show OPTIONS = "OPTIONS" + show PATCH = "PATCH" + show POST = "POST" + show PUT = "PUT" + show (CustomMethod m) = "(CustomMethod " ++ show m ++ ")" + +instance isOptionMethod :: IsOption Method where + (:=) k a = (optionFn k) := methodToString a + +methodToString :: Method -> String +methodToString (CustomMethod m) = m +methodToString other = show other diff --git a/src/Network/HTTP/MimeType.purs b/src/Network/HTTP/MimeType.purs new file mode 100644 index 0000000..5e8acc2 --- /dev/null +++ b/src/Network/HTTP/MimeType.purs @@ -0,0 +1,13 @@ +module Network.HTTP.MimeType where + +newtype MimeType = MimeType String + +instance eqMimeType :: Eq MimeType where + (==) (MimeType x) (MimeType y) = x == y + (/=) (MimeType x) (MimeType y) = x /= y + +instance showMimeType :: Show MimeType where + show (MimeType h) = "(MimeType " ++ show h ++ ")" + +mimeTypeToString :: MimeType -> String +mimeTypeToString (MimeType s) = s diff --git a/src/Network/HTTP/MimeType/Common.purs b/src/Network/HTTP/MimeType/Common.purs new file mode 100644 index 0000000..976f328 --- /dev/null +++ b/src/Network/HTTP/MimeType/Common.purs @@ -0,0 +1,42 @@ +module Network.HTTP.MimeType.Common where + +import Network.HTTP.MimeType + +applicationFormURLEncoded :: MimeType +applicationFormURLEncoded = MimeType "application/x-www-form-urlencoded" + +applicationJSON :: MimeType +applicationJSON = MimeType "application/json" + +applicationJavascript :: MimeType +applicationJavascript = MimeType "application/javascript" + +applicationOctetStream :: MimeType +applicationOctetStream = MimeType "application/octet-stream" + +applicationXML :: MimeType +applicationXML = MimeType "application/xml" + +imageGIF :: MimeType +imageGIF = MimeType "image/gif" + +imageJPEG :: MimeType +imageJPEG = MimeType "image/jpeg" + +imagePNG :: MimeType +imagePNG = MimeType "image/png" + +multipartFormData :: MimeType +multipartFormData = MimeType "multipart/form-data" + +textCSV :: MimeType +textCSV = MimeType "text/csv" + +textHTML :: MimeType +textHTML = MimeType "text/html" + +textPlain :: MimeType +textPlain = MimeType "text/plain" + +textXML :: MimeType +textXML = MimeType "text/xml" diff --git a/src/Network/HTTP/RequestHeader.purs b/src/Network/HTTP/RequestHeader.purs new file mode 100644 index 0000000..d224a56 --- /dev/null +++ b/src/Network/HTTP/RequestHeader.purs @@ -0,0 +1,34 @@ +module Network.HTTP.RequestHeader where + +import Data.Options (IsOption, optionFn, (:=)) +import Network.HTTP.MimeType + +data RequestHeader + = Accept MimeType + | ContentType MimeType + | RequestHeader String String + +instance eqRequestHeader :: Eq RequestHeader where + (==) (Accept m1) (Accept m2) = m1 == m2 + (==) (ContentType m1) (ContentType m2) = m1 == m2 + (==) (RequestHeader h1 v1) (RequestHeader h2 v2) = h1 == h2 && v1 == v2 + (==) _ _ = false + (/=) x y = not (x == y) + +instance showRequestHeader :: Show RequestHeader where + show (Accept m) = "(Accept " ++ show m ++ ")" + show (ContentType m) = "(ContentType " ++ show m ++ ")" + show (RequestHeader h v) = "(RequestHeader " ++ show h ++ " " ++ show v ++ ")" + +instance isOptionRequestHeader :: IsOption RequestHeader where + (:=) k a = (optionFn k) := { field: requestHeaderName a, value: requestHeaderValue a } + +requestHeaderName :: RequestHeader -> String +requestHeaderName (Accept _) = "Accept" +requestHeaderName (ContentType _) = "Content-Type" +requestHeaderName (RequestHeader h _) = h + +requestHeaderValue :: RequestHeader -> String +requestHeaderValue (Accept m) = mimeTypeToString m +requestHeaderValue (ContentType m) = mimeTypeToString m +requestHeaderValue (RequestHeader _ v) = v diff --git a/src/Network/HTTP/ResponseHeader.purs b/src/Network/HTTP/ResponseHeader.purs new file mode 100644 index 0000000..80af915 --- /dev/null +++ b/src/Network/HTTP/ResponseHeader.purs @@ -0,0 +1,17 @@ +module Network.HTTP.ResponseHeader + ( ResponseHeader() + , responseHeader + ) where + +data ResponseHeader = ResponseHeader String String + +responseHeader :: String -> String -> ResponseHeader +responseHeader field value = ResponseHeader field value + +instance eqResponseHeader :: Eq ResponseHeader where + (==) (ResponseHeader h1 v1) (ResponseHeader h2 v2) = h1 == h2 && v1 == v2 + (==) _ _ = false + (/=) x y = not (x == y) + +instance showResponseHeader :: Show ResponseHeader where + show (ResponseHeader h v) = "(ResponseHeader " ++ show h ++ " " ++ show v ++ ")" diff --git a/src/Network/HTTP/StatusCode.purs b/src/Network/HTTP/StatusCode.purs new file mode 100644 index 0000000..93b67c0 --- /dev/null +++ b/src/Network/HTTP/StatusCode.purs @@ -0,0 +1,12 @@ +module Network.HTTP.StatusCode where + +import Data.Int + +newtype StatusCode = StatusCode Int + +instance eqStatusCode :: Eq StatusCode where + (==) (StatusCode x) (StatusCode y) = x == y + (/=) x y = not (x == y) + +instance showStatusCode :: Show StatusCode where + show (StatusCode code) = "(StatusCode " ++ show code ++ ")" diff --git a/test/Main.purs b/test/Main.purs new file mode 100644 index 0000000..66eb69d --- /dev/null +++ b/test/Main.purs @@ -0,0 +1,56 @@ +module Test.Main where + +import Control.Monad.Aff +import Control.Monad.Eff +import Control.Monad.Eff.Class +import Control.Monad.Eff.Exception +import Data.Either +import Data.Foreign +import Data.Options +import Debug.Trace +import Network.HTTP.Affjax +import Network.HTTP.Affjax.Response +import Network.HTTP.Affjax.Request +import Network.HTTP.Method +import Network.HTTP.MimeType.Common +import Network.HTTP.RequestHeader + +foreign import traceAny + """ + function traceAny(a){ + return function () { + console.log(a); + return {}; + }; + } + """ :: forall e a. a -> Eff (trace :: Trace | e) Unit + +traceAny' :: forall e. AffjaxResponse Unit -> Eff (trace :: Trace | e) Unit +traceAny' = traceAny + +foreign import noContent "var noContent = new FormData();" :: RequestContent + +-- TODO: make PR for options +instance isOptionUnit :: IsOption Unit where + (:=) k a = (optionFn k) := toContent a + +main = do + + go $ url := "/api" + <> headers := [ContentType applicationOctetStream] + <> content := noContent + + go $ url := "/api" + <> method := POST + <> content := unit + + launchAff $ do + res <- attempt $ affjax $ url := "/api" + <> method := POST + <> content := unit + liftEff $ case res of + (Left err) -> traceAny err + (Right res') -> traceAny (res' :: AffjaxResponse String) + +go :: forall e a. (Requestable a) => Options (AffjaxOptions a) -> Eff (ajax :: Ajax, trace :: Trace | e) Unit +go opts = affjax' opts traceAny traceAny' From 8e0e848f349dbbc666b8e0831bcd7fdd72990507 Mon Sep 17 00:00:00 2001 From: Gary Burgess Date: Fri, 27 Mar 2015 00:56:59 +0000 Subject: [PATCH 02/10] Rework Requestable and Responsable --- bower.json | 4 +- src/Network/HTTP/Affjax.purs | 97 ++++++++++++------ src/Network/HTTP/Affjax/Request.purs | 2 +- src/Network/HTTP/Affjax/Response.purs | 135 +++++++++++--------------- test/Main.purs | 29 +++--- 5 files changed, 141 insertions(+), 126 deletions(-) diff --git a/bower.json b/bower.json index 7aa92e8..0fd58e5 100644 --- a/bower.json +++ b/bower.json @@ -22,8 +22,8 @@ "purescript-aff": "~0.7.0", "purescript-arraybuffer-types": "~0.1.1", "purescript-dom": "~0.1.2", - "purescript-foreign": "~0.4.1", + "purescript-foreign": "~0.4.2", "purescript-integers": "~0.0.1", - "purescript-options": "~0.2.1" + "purescript-options": "~0.3.0" } } diff --git a/src/Network/HTTP/Affjax.purs b/src/Network/HTTP/Affjax.purs index f0a191c..8185194 100644 --- a/src/Network/HTTP/Affjax.purs +++ b/src/Network/HTTP/Affjax.purs @@ -1,23 +1,28 @@ module Network.HTTP.Affjax ( Ajax() + , Affjax() , AffjaxOptions() , AffjaxResponse() , url, method, content, headers, username, password , affjax , affjax' + , get + , post, post_ + , put, put_ + , delete, delete_ ) where import Control.Monad.Aff (Aff(), makeAff) import Control.Monad.Eff (Eff()) -import Control.Monad.Eff.Exception (Error()) -import Data.Foreign (Foreign(..)) +import Control.Monad.Eff.Exception (Error(), error) +import Data.Either (Either(..)) +import Data.Foreign (Foreign(..), F()) import Data.Function (Fn4(), runFn4) import Data.Options (Option(), Options(), IsOption, options, (:=), opt) -import Data.Proxy (Proxy(..)) import Network.HTTP.Affjax.Request import Network.HTTP.Affjax.Response import Network.HTTP.Affjax.ResponseType -import Network.HTTP.Method (Method()) +import Network.HTTP.Method (Method(..)) import Network.HTTP.RequestHeader (RequestHeader()) import Network.HTTP.ResponseHeader (ResponseHeader(), responseHeader) import Network.HTTP.StatusCode (StatusCode()) @@ -25,8 +30,11 @@ import Network.HTTP.StatusCode (StatusCode()) -- | The effect type for AJAX requests made with Affjax. foreign import data Ajax :: ! +-- | The type for Affjax requests. +type Affjax e a = Aff (ajax :: Ajax | e) (AffjaxResponse a) + -- | Options type for Affjax requests. -foreign import data AffjaxOptions :: * -> * +foreign import data AffjaxOptions :: * -- | The type of records that will be received as an Affjax response. type AffjaxResponse a = @@ -36,49 +44,80 @@ type AffjaxResponse a = } -- | Sets the URL for a request. -url :: forall a. Option (AffjaxOptions a) String +url :: Option AffjaxOptions String url = opt "url" -- | Sets the HTTP method for a request. -method :: forall a. Option (AffjaxOptions a) Method +method :: Option AffjaxOptions Method method = opt "method" -- | Sets the content to send in a request. -content :: forall a. (Requestable a, IsOption a) => Option (AffjaxOptions a) a +content :: Option AffjaxOptions RequestContent content = opt "content" -- | Sets the headers to send with a request. -headers :: forall a. Option (AffjaxOptions a) [RequestHeader] +headers :: Option AffjaxOptions [RequestHeader] headers = opt "headers" -- | Sets the HTTP auth username to send with a request. -username :: forall a. Option (AffjaxOptions a) String +username :: Option AffjaxOptions String username = opt "username" -- | Sets the HTTP auth password to send with a request. -password :: forall a. Option (AffjaxOptions a) String +password :: Option AffjaxOptions String password = opt "password" -- | Sets the expected response type for a request. This is not exposed outside -- | of the module as the `ResponseType` is set based on the `Responsable` -- | instance for the expected result content type. -responseType = opt "responseType" :: forall a. Option (AffjaxOptions a) ResponseType +responseType = opt "responseType" :: Option AffjaxOptions ResponseType -- | Runs a request. -affjax :: forall e a b. (Requestable a, Responsable b) => - Options (AffjaxOptions a) -> - Aff (ajax :: Ajax | e) (AffjaxResponse b) -affjax = makeAff <<< affjax' +affjax :: forall e a. Responsable a -> + Options AffjaxOptions -> + Affjax e a +affjax r = makeAff <<< affjax' r -- | Runs a request directly in Eff. -affjax' :: forall e a b. (Requestable a, Responsable b) => - Options (AffjaxOptions a) -> - (Error -> Eff (ajax :: Ajax | e) Unit) -> - (AffjaxResponse b -> Eff (ajax :: Ajax | e) Unit) -> - Eff (ajax :: Ajax | e) Unit -affjax' opts eb cb = - let opts' = opts <> responseType := toResponseType (Proxy :: Proxy b) - in runFn4 unsafeAjax responseHeader (options opts') eb cb +affjax' :: forall e a. Responsable a -> + Options AffjaxOptions -> + (Error -> Eff (ajax :: Ajax | e) Unit) -> + (AffjaxResponse a -> Eff (ajax :: Ajax | e) Unit) -> + Eff (ajax :: Ajax | e) Unit +affjax' (Responsable read ty) opts eb cb = + runFn4 unsafeAjax responseHeader (options $ opts <> responseType := ty) eb cb' + where + cb' :: AffjaxResponse Foreign -> Eff (ajax :: Ajax | e) Unit + cb' res = case res { response = _ } <$> read res.response of + Left err -> eb $ error (show err) + Right res' -> cb res' + +get :: forall e a. Responsable a -> String -> Affjax e a +get r addr = affjax r $ method := GET + <> url := addr + +post :: forall e a. Responsable a -> String -> RequestContent -> Affjax e a +post r u c = affjax r $ method := POST + <> url := u + <> content := c + +post_ :: forall e. String -> RequestContent -> Affjax e Unit +post_ = post rUnit + +put :: forall e a. Responsable a -> String -> RequestContent -> Affjax e a +put r u c = affjax r $ method := PUT + <> url := u + <> content := c + +put_ :: forall e. String -> RequestContent -> Affjax e Unit +put_ = put rUnit + +delete :: forall e a. Responsable a -> String -> Affjax e a +delete r u = affjax r $ method := DELETE + <> url := u + +delete_ :: forall e. String -> Affjax e Unit +delete_ = delete rUnit foreign import unsafeAjax """ @@ -112,8 +151,8 @@ foreign import unsafeAjax xhr.send(options.content); }; } - """ :: forall e a b. Fn4 (String -> String -> ResponseHeader) - Foreign - (Error -> Eff (ajax :: Ajax | e) Unit) - (AffjaxResponse b -> Eff (ajax :: Ajax | e) Unit) - (Eff (ajax :: Ajax | e) Unit) + """ :: forall e a. Fn4 (String -> String -> ResponseHeader) + Foreign + (Error -> Eff (ajax :: Ajax | e) Unit) + (AffjaxResponse Foreign -> Eff (ajax :: Ajax | e) Unit) + (Eff (ajax :: Ajax | e) Unit) diff --git a/src/Network/HTTP/Affjax/Request.purs b/src/Network/HTTP/Affjax/Request.purs index b082dd1..bbcdd6f 100644 --- a/src/Network/HTTP/Affjax/Request.purs +++ b/src/Network/HTTP/Affjax/Request.purs @@ -22,7 +22,7 @@ instance isOptionRequestContent :: IsOption RequestContent where class Requestable a where toContent :: a -> RequestContent -instance requestableAjaxRequestContent :: Requestable RequestContent where +instance requestableRequestContent :: Requestable RequestContent where toContent = id instance requestableInt8Array :: Requestable (A.ArrayView A.Int8) where diff --git a/src/Network/HTTP/Affjax/Response.purs b/src/Network/HTTP/Affjax/Response.purs index 0d3bc58..74a4394 100644 --- a/src/Network/HTTP/Affjax/Response.purs +++ b/src/Network/HTTP/Affjax/Response.purs @@ -1,12 +1,24 @@ module Network.HTTP.Affjax.Response ( ResponseContent() - , Responsable, toResponseType, fromContent + , Responsable(..) + , rInt8Array + , rInt16Array + , rInt32Array + , rUint8Array + , rUint16Array + , rUint32Array + , rUint8ClampedArray + , rFloat32Array + , rFloat64Array + , rBlob + , rDocument + , rJSON + , rString + , rUnit ) where import Data.Either (Either(..)) -import Data.Foreign (Foreign(), ForeignError()) -import Data.Options (IsOption, optionFn, (:=)) -import Data.Proxy (Proxy()) +import Data.Foreign (Foreign(), F(), readString, unsafeReadTagged) import DOM (Document()) import DOM.File (Blob()) import DOM.XHR (FormData()) @@ -17,75 +29,46 @@ import qualified Data.ArrayBuffer.Types as A -- | (ArrayBuffer, Blob, Document, JSON, String). type ResponseContent = Foreign --- | Class for types that converted from values returned from an XHR request. -class Responsable a where - toResponseType :: Proxy a -> ResponseType - fromContent :: ResponseContent -> Either ForeignError a - -instance responsableUnit :: Responsable Unit where - toResponseType _ = StringResponse - fromContent _ = Right unit - -instance responsableInt8Array :: Responsable (A.ArrayView A.Int8) where - toResponseType _ = ArrayBufferResponse - fromContent = arrayBufferConversion - -instance responsableInt16Array :: Responsable (A.ArrayView A.Int16) where - toResponseType _ = ArrayBufferResponse - fromContent = arrayBufferConversion - -instance responsableInt32Array :: Responsable (A.ArrayView A.Int32) where - toResponseType _ = ArrayBufferResponse - fromContent = arrayBufferConversion - -instance responsableUint8Array :: Responsable (A.ArrayView A.Uint8) where - toResponseType _ = ArrayBufferResponse - fromContent = arrayBufferConversion - -instance responsableUint16Array :: Responsable (A.ArrayView A.Uint16) where - toResponseType _ = ArrayBufferResponse - fromContent = arrayBufferConversion - -instance responsableUint32Array :: Responsable (A.ArrayView A.Uint32) where - toResponseType _ = ArrayBufferResponse - fromContent = arrayBufferConversion - -instance responsableUint8ClampedArray :: Responsable (A.ArrayView A.Uint8Clamped) where - toResponseType _ = ArrayBufferResponse - fromContent = arrayBufferConversion - -instance responsableFloat32Array :: Responsable (A.ArrayView A.Float32) where - toResponseType _ = ArrayBufferResponse - fromContent = arrayBufferConversion - -instance responsableFloat64Array :: Responsable (A.ArrayView A.Float64) where - toResponseType _ = ArrayBufferResponse - fromContent = arrayBufferConversion - -instance responsableBlob :: Responsable Blob where - toResponseType _ = BlobResponse - fromContent = unsafeConversion - -instance responsableDocument :: Responsable Document where - toResponseType _ = DocumentResponse - fromContent = unsafeConversion - -instance responsableString :: Responsable String where - toResponseType _ = StringResponse - fromContent = Right <<< unsafeConversion - --- TODO: this, properly -foreign import arrayBufferConversion - """ - function arrayBufferConversion (x) { - return x; - } - """ :: forall a b. a -> b - --- TODO: not this either, at least use foreign to check the tag of returned values to ensure they are not null, etc. -foreign import unsafeConversion - """ - function unsafeConversion (x) { - return x; - } - """ :: forall a b. a -> b +data Responsable a = Responsable (ResponseContent -> F a) ResponseType + +rInt8Array :: Responsable A.Int8Array +rInt8Array = Responsable (unsafeReadTagged "ArrayBuffer") ArrayBufferResponse + +rInt16Array :: Responsable A.Int16Array +rInt16Array = Responsable (unsafeReadTagged "ArrayBuffer") ArrayBufferResponse + +rInt32Array :: Responsable A.Int32Array +rInt32Array = Responsable (unsafeReadTagged "ArrayBuffer") ArrayBufferResponse + +rUint8Array :: Responsable A.Uint8Array +rUint8Array = Responsable (unsafeReadTagged "ArrayBuffer") ArrayBufferResponse + +rUint16Array :: Responsable A.Uint16Array +rUint16Array = Responsable (unsafeReadTagged "ArrayBuffer") ArrayBufferResponse + +rUint32Array :: Responsable A.Uint32Array +rUint32Array = Responsable (unsafeReadTagged "ArrayBuffer") ArrayBufferResponse + +rUint8ClampedArray :: Responsable A.Uint8ClampedArray +rUint8ClampedArray = Responsable (unsafeReadTagged "ArrayBuffer") ArrayBufferResponse + +rFloat32Array :: Responsable A.Float32Array +rFloat32Array = Responsable (unsafeReadTagged "ArrayBuffer") ArrayBufferResponse + +rFloat64Array :: Responsable A.Float64Array +rFloat64Array = Responsable (unsafeReadTagged "ArrayBuffer") ArrayBufferResponse + +rBlob :: Responsable Blob +rBlob = Responsable (unsafeReadTagged "Blob") BlobResponse + +rDocument :: Responsable Document +rDocument = Responsable (unsafeReadTagged "Document") DocumentResponse + +rJSON :: Responsable Foreign +rJSON = Responsable Right JSONResponse + +rString :: Responsable String +rString = Responsable readString StringResponse + +rUnit :: Responsable Unit +rUnit = Responsable (const $ Right unit) StringResponse diff --git a/test/Main.purs b/test/Main.purs index 66eb69d..f5f328d 100644 --- a/test/Main.purs +++ b/test/Main.purs @@ -25,32 +25,25 @@ foreign import traceAny } """ :: forall e a. a -> Eff (trace :: Trace | e) Unit -traceAny' :: forall e. AffjaxResponse Unit -> Eff (trace :: Trace | e) Unit -traceAny' = traceAny - foreign import noContent "var noContent = new FormData();" :: RequestContent --- TODO: make PR for options -instance isOptionUnit :: IsOption Unit where - (:=) k a = (optionFn k) := toContent a - main = do go $ url := "/api" <> headers := [ContentType applicationOctetStream] - <> content := noContent + <> content := (toContent "test") go $ url := "/api" <> method := POST - <> content := unit + <> content := (toContent unit) launchAff $ do - res <- attempt $ affjax $ url := "/api" - <> method := POST - <> content := unit - liftEff $ case res of - (Left err) -> traceAny err - (Right res') -> traceAny (res' :: AffjaxResponse String) - -go :: forall e a. (Requestable a) => Options (AffjaxOptions a) -> Eff (ajax :: Ajax, trace :: Trace | e) Unit -go opts = affjax' opts traceAny traceAny' + res <- attempt $ affjax rString $ url := "/api" <> method := POST + liftEff $ either traceAny traceAny res + + launchAff $ do + res <- attempt $ get rInt8Array "/arrayview" + liftEff $ either traceAny traceAny res + +go :: forall e. Options AffjaxOptions -> Eff (ajax :: Ajax, trace :: Trace | e) Unit +go opts = affjax' rUnit opts traceAny traceAny From 07aa956508382ae4c05d21ae0bc33708812f1297 Mon Sep 17 00:00:00 2001 From: Gary Burgess Date: Fri, 27 Mar 2015 01:05:51 +0000 Subject: [PATCH 03/10] Tidy up --- src/Data/Proxy.purs | 3 --- src/Network/HTTP/Affjax.purs | 2 +- src/Network/HTTP/Affjax/Request.purs | 3 +-- 3 files changed, 2 insertions(+), 6 deletions(-) delete mode 100644 src/Data/Proxy.purs diff --git a/src/Data/Proxy.purs b/src/Data/Proxy.purs deleted file mode 100644 index 3418acd..0000000 --- a/src/Data/Proxy.purs +++ /dev/null @@ -1,3 +0,0 @@ -module Data.Proxy where - -data Proxy a = Proxy diff --git a/src/Network/HTTP/Affjax.purs b/src/Network/HTTP/Affjax.purs index 8185194..781466b 100644 --- a/src/Network/HTTP/Affjax.purs +++ b/src/Network/HTTP/Affjax.purs @@ -18,7 +18,7 @@ import Control.Monad.Eff.Exception (Error(), error) import Data.Either (Either(..)) import Data.Foreign (Foreign(..), F()) import Data.Function (Fn4(), runFn4) -import Data.Options (Option(), Options(), IsOption, options, (:=), opt) +import Data.Options (Option(), Options(), options, (:=), opt) import Network.HTTP.Affjax.Request import Network.HTTP.Affjax.Response import Network.HTTP.Affjax.ResponseType diff --git a/src/Network/HTTP/Affjax/Request.purs b/src/Network/HTTP/Affjax/Request.purs index bbcdd6f..8a60961 100644 --- a/src/Network/HTTP/Affjax/Request.purs +++ b/src/Network/HTTP/Affjax/Request.purs @@ -3,11 +3,10 @@ module Network.HTTP.Affjax.Request , Requestable, toContent ) where -import Data.Options (Option(), Options(), IsOption, optionFn, (:=)) +import Data.Options (Option(), Options(), IsOption, (:=)) import DOM (Document()) import DOM.File (Blob()) import DOM.XHR (FormData()) -import Network.HTTP.MimeType (MimeType()) import qualified Data.ArrayBuffer.Types as A -- | Type representing all content types that be sent via XHR (ArrayBufferView, From 18c06084ea7591ea904d67482c45c5597b47a9d3 Mon Sep 17 00:00:00 2001 From: Gary Burgess Date: Fri, 27 Mar 2015 01:11:27 +0000 Subject: [PATCH 04/10] Update generated docs --- README.md | 726 +++++++++++++++++++++++++++++++++++++++++++++++++++- gulpfile.js | 3 +- 2 files changed, 725 insertions(+), 4 deletions(-) diff --git a/README.md b/README.md index 374919b..2ce6405 100644 --- a/README.md +++ b/README.md @@ -1,2 +1,724 @@ -# purescript-affjax -An asynchronous AJAX library built using Aff. +# Module Documentation + +## Module Network.HTTP.Affjax + +#### `Ajax` + +``` purescript +data Ajax :: ! +``` + +The effect type for AJAX requests made with Affjax. + +#### `Affjax` + +``` purescript +type Affjax e a = Aff (ajax :: Ajax | e) (AffjaxResponse a) +``` + +The type for Affjax requests. + +#### `AffjaxOptions` + +``` purescript +data AffjaxOptions :: * +``` + +Options type for Affjax requests. + +#### `AffjaxResponse` + +``` purescript +type AffjaxResponse a = { response :: a, headers :: [ResponseHeader], status :: StatusCode } +``` + +The type of records that will be received as an Affjax response. + +#### `url` + +``` purescript +url :: Option AffjaxOptions String +``` + +Sets the URL for a request. + +#### `method` + +``` purescript +method :: Option AffjaxOptions Method +``` + +Sets the HTTP method for a request. + +#### `content` + +``` purescript +content :: Option AffjaxOptions RequestContent +``` + +Sets the content to send in a request. + +#### `headers` + +``` purescript +headers :: Option AffjaxOptions [RequestHeader] +``` + +Sets the headers to send with a request. + +#### `username` + +``` purescript +username :: Option AffjaxOptions String +``` + +Sets the HTTP auth username to send with a request. + +#### `password` + +``` purescript +password :: Option AffjaxOptions String +``` + +Sets the HTTP auth password to send with a request. + +#### `affjax` + +``` purescript +affjax :: forall e a. Responsable a -> Options AffjaxOptions -> Affjax e a +``` + +Runs a request. + +#### `affjax'` + +``` purescript +affjax' :: forall e a. Responsable a -> Options AffjaxOptions -> (Error -> Eff (ajax :: Ajax | e) Unit) -> (AffjaxResponse a -> Eff (ajax :: Ajax | e) Unit) -> Eff (ajax :: Ajax | e) Unit +``` + +Runs a request directly in Eff. + +#### `get` + +``` purescript +get :: forall e a. Responsable a -> String -> Affjax e a +``` + + +#### `post` + +``` purescript +post :: forall e a. Responsable a -> String -> RequestContent -> Affjax e a +``` + + +#### `post_` + +``` purescript +post_ :: forall e. String -> RequestContent -> Affjax e Unit +``` + + +#### `put` + +``` purescript +put :: forall e a. Responsable a -> String -> RequestContent -> Affjax e a +``` + + +#### `put_` + +``` purescript +put_ :: forall e. String -> RequestContent -> Affjax e Unit +``` + + +#### `delete` + +``` purescript +delete :: forall e a. Responsable a -> String -> Affjax e a +``` + + +#### `delete_` + +``` purescript +delete_ :: forall e. String -> Affjax e Unit +``` + + + +## Module Network.HTTP.Method + +#### `Method` + +``` purescript +data Method + = DELETE + | GET + | HEAD + | OPTIONS + | PATCH + | POST + | PUT + | CustomMethod String +``` + + +#### `eqMethod` + +``` purescript +instance eqMethod :: Eq Method +``` + + +#### `showMethod` + +``` purescript +instance showMethod :: Show Method +``` + + +#### `isOptionMethod` + +``` purescript +instance isOptionMethod :: IsOption Method +``` + + +#### `methodToString` + +``` purescript +methodToString :: Method -> String +``` + + + +## Module Network.HTTP.MimeType + +#### `MimeType` + +``` purescript +newtype MimeType + = MimeType String +``` + + +#### `eqMimeType` + +``` purescript +instance eqMimeType :: Eq MimeType +``` + + +#### `showMimeType` + +``` purescript +instance showMimeType :: Show MimeType +``` + + +#### `mimeTypeToString` + +``` purescript +mimeTypeToString :: MimeType -> String +``` + + + +## Module Network.HTTP.RequestHeader + +#### `RequestHeader` + +``` purescript +data RequestHeader + = Accept MimeType + | ContentType MimeType + | RequestHeader String String +``` + + +#### `eqRequestHeader` + +``` purescript +instance eqRequestHeader :: Eq RequestHeader +``` + + +#### `showRequestHeader` + +``` purescript +instance showRequestHeader :: Show RequestHeader +``` + + +#### `isOptionRequestHeader` + +``` purescript +instance isOptionRequestHeader :: IsOption RequestHeader +``` + + +#### `requestHeaderName` + +``` purescript +requestHeaderName :: RequestHeader -> String +``` + + +#### `requestHeaderValue` + +``` purescript +requestHeaderValue :: RequestHeader -> String +``` + + + +## Module Network.HTTP.ResponseHeader + +#### `ResponseHeader` + +``` purescript +data ResponseHeader +``` + + +#### `responseHeader` + +``` purescript +responseHeader :: String -> String -> ResponseHeader +``` + + +#### `eqResponseHeader` + +``` purescript +instance eqResponseHeader :: Eq ResponseHeader +``` + + +#### `showResponseHeader` + +``` purescript +instance showResponseHeader :: Show ResponseHeader +``` + + + +## Module Network.HTTP.StatusCode + +#### `StatusCode` + +``` purescript +newtype StatusCode + = StatusCode Int +``` + + +#### `eqStatusCode` + +``` purescript +instance eqStatusCode :: Eq StatusCode +``` + + +#### `showStatusCode` + +``` purescript +instance showStatusCode :: Show StatusCode +``` + + + +## Module Network.HTTP.Affjax.Request + +#### `RequestContent` + +``` purescript +data RequestContent :: * +``` + +Type representing all content types that be sent via XHR (ArrayBufferView, +Blob, Document, String, FormData). + +#### `isOptionRequestContent` + +``` purescript +instance isOptionRequestContent :: IsOption RequestContent +``` + + +#### `Requestable` + +``` purescript +class Requestable a where + toContent :: a -> RequestContent +``` + +A class for types that can be converted to values that can be sent with +XHR requests. + +#### `requestableRequestContent` + +``` purescript +instance requestableRequestContent :: Requestable RequestContent +``` + + +#### `requestableInt8Array` + +``` purescript +instance requestableInt8Array :: Requestable (A.ArrayView A.Int8) +``` + + +#### `requestableInt16Array` + +``` purescript +instance requestableInt16Array :: Requestable (A.ArrayView A.Int16) +``` + + +#### `requestableInt32Array` + +``` purescript +instance requestableInt32Array :: Requestable (A.ArrayView A.Int32) +``` + + +#### `requestableUint8Array` + +``` purescript +instance requestableUint8Array :: Requestable (A.ArrayView A.Uint8) +``` + + +#### `requestableUint16Array` + +``` purescript +instance requestableUint16Array :: Requestable (A.ArrayView A.Uint16) +``` + + +#### `requestableUint32Array` + +``` purescript +instance requestableUint32Array :: Requestable (A.ArrayView A.Uint32) +``` + + +#### `requestableUint8ClampedArray` + +``` purescript +instance requestableUint8ClampedArray :: Requestable (A.ArrayView A.Uint8Clamped) +``` + + +#### `requestableFloat32Array` + +``` purescript +instance requestableFloat32Array :: Requestable (A.ArrayView A.Float32) +``` + + +#### `requestableFloat64Array` + +``` purescript +instance requestableFloat64Array :: Requestable (A.ArrayView A.Float64) +``` + + +#### `requestableBlob` + +``` purescript +instance requestableBlob :: Requestable Blob +``` + + +#### `requestableDocument` + +``` purescript +instance requestableDocument :: Requestable Document +``` + + +#### `requestableString` + +``` purescript +instance requestableString :: Requestable String +``` + + +#### `requestableFormData` + +``` purescript +instance requestableFormData :: Requestable FormData +``` + + +#### `requestableUnit` + +``` purescript +instance requestableUnit :: Requestable Unit +``` + + + +## Module Network.HTTP.Affjax.Response + +#### `ResponseContent` + +``` purescript +type ResponseContent = Foreign +``` + +Type representing content types that be received from an XHR request +(ArrayBuffer, Blob, Document, JSON, String). + +#### `Responsable` + +``` purescript +data Responsable a + = Responsable (ResponseContent -> F a) ResponseType +``` + + +#### `rInt8Array` + +``` purescript +rInt8Array :: Responsable A.Int8Array +``` + + +#### `rInt16Array` + +``` purescript +rInt16Array :: Responsable A.Int16Array +``` + + +#### `rInt32Array` + +``` purescript +rInt32Array :: Responsable A.Int32Array +``` + + +#### `rUint8Array` + +``` purescript +rUint8Array :: Responsable A.Uint8Array +``` + + +#### `rUint16Array` + +``` purescript +rUint16Array :: Responsable A.Uint16Array +``` + + +#### `rUint32Array` + +``` purescript +rUint32Array :: Responsable A.Uint32Array +``` + + +#### `rUint8ClampedArray` + +``` purescript +rUint8ClampedArray :: Responsable A.Uint8ClampedArray +``` + + +#### `rFloat32Array` + +``` purescript +rFloat32Array :: Responsable A.Float32Array +``` + + +#### `rFloat64Array` + +``` purescript +rFloat64Array :: Responsable A.Float64Array +``` + + +#### `rBlob` + +``` purescript +rBlob :: Responsable Blob +``` + + +#### `rDocument` + +``` purescript +rDocument :: Responsable Document +``` + + +#### `rJSON` + +``` purescript +rJSON :: Responsable Foreign +``` + + +#### `rString` + +``` purescript +rString :: Responsable String +``` + + +#### `rUnit` + +``` purescript +rUnit :: Responsable Unit +``` + + + +## Module Network.HTTP.Affjax.ResponseType + +#### `ResponseType` + +``` purescript +data ResponseType + = ArrayBufferResponse + | BlobResponse + | DocumentResponse + | JSONResponse + | StringResponse +``` + +Valid response types for an AJAX request. This is used to determine the +`ResponseContent` type for a request. + +#### `eqResponseType` + +``` purescript +instance eqResponseType :: Eq ResponseType +``` + + +#### `showResponseType` + +``` purescript +instance showResponseType :: Show ResponseType +``` + + +#### `isOptionResponseType` + +``` purescript +instance isOptionResponseType :: IsOption ResponseType +``` + + +#### `ajaxResponseTypeToString` + +``` purescript +ajaxResponseTypeToString :: ResponseType -> String +``` + + + +## Module Network.HTTP.MimeType.Common + +#### `applicationFormURLEncoded` + +``` purescript +applicationFormURLEncoded :: MimeType +``` + + +#### `applicationJSON` + +``` purescript +applicationJSON :: MimeType +``` + + +#### `applicationJavascript` + +``` purescript +applicationJavascript :: MimeType +``` + + +#### `applicationOctetStream` + +``` purescript +applicationOctetStream :: MimeType +``` + + +#### `applicationXML` + +``` purescript +applicationXML :: MimeType +``` + + +#### `imageGIF` + +``` purescript +imageGIF :: MimeType +``` + + +#### `imageJPEG` + +``` purescript +imageJPEG :: MimeType +``` + + +#### `imagePNG` + +``` purescript +imagePNG :: MimeType +``` + + +#### `multipartFormData` + +``` purescript +multipartFormData :: MimeType +``` + + +#### `textCSV` + +``` purescript +textCSV :: MimeType +``` + + +#### `textHTML` + +``` purescript +textHTML :: MimeType +``` + + +#### `textPlain` + +``` purescript +textPlain :: MimeType +``` + + +#### `textXML` + +``` purescript +textXML :: MimeType +``` + + + + diff --git a/gulpfile.js b/gulpfile.js index b5f865c..5a883bd 100644 --- a/gulpfile.js +++ b/gulpfile.js @@ -31,5 +31,4 @@ gulp.task("docs", function () { .pipe(gulp.dest("README.md")); }); -// gulp.task("default", ["jsvalidate", "docs"]); -gulp.task("default", ["make-test"]); +gulp.task("default", ["jsvalidate", "docs", "make-test"]); From 5bb40412edbbca1b8154cee22e1e5483c73116db Mon Sep 17 00:00:00 2001 From: Gary Burgess Date: Fri, 27 Mar 2015 21:56:42 +0000 Subject: [PATCH 05/10] Fix license type --- bower.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/bower.json b/bower.json index 0fd58e5..0f372ce 100644 --- a/bower.json +++ b/bower.json @@ -6,7 +6,7 @@ "purescript", "ajax" ], - "license": "MIT", + "license": "Apache 2.0", "ignore": [ "**/.*", "bower_components", From 2117fcc622b92ae34e4c43465fda887cf1df5831 Mon Sep 17 00:00:00 2001 From: Gary Burgess Date: Fri, 27 Mar 2015 22:00:34 +0000 Subject: [PATCH 06/10] Move URL parameter to front of functions --- README.md | 24 ++++++++++++++++-------- src/Network/HTTP/Affjax.purs | 36 ++++++++++++++++++++---------------- test/Main.purs | 2 +- 3 files changed, 37 insertions(+), 25 deletions(-) diff --git a/README.md b/README.md index 2ce6405..0e79fe4 100644 --- a/README.md +++ b/README.md @@ -34,10 +34,18 @@ type AffjaxResponse a = { response :: a, headers :: [ResponseHeader], status :: The type of records that will be received as an Affjax response. +#### `URL` + +``` purescript +type URL = String +``` + +Type alias for URL strings to aid readability of types. + #### `url` ``` purescript -url :: Option AffjaxOptions String +url :: Option AffjaxOptions URL ``` Sets the URL for a request. @@ -101,49 +109,49 @@ Runs a request directly in Eff. #### `get` ``` purescript -get :: forall e a. Responsable a -> String -> Affjax e a +get :: forall e a. URL -> Responsable a -> Affjax e a ``` #### `post` ``` purescript -post :: forall e a. Responsable a -> String -> RequestContent -> Affjax e a +post :: forall e a. URL -> Responsable a -> RequestContent -> Affjax e a ``` #### `post_` ``` purescript -post_ :: forall e. String -> RequestContent -> Affjax e Unit +post_ :: forall e. URL -> RequestContent -> Affjax e Unit ``` #### `put` ``` purescript -put :: forall e a. Responsable a -> String -> RequestContent -> Affjax e a +put :: forall e a. URL -> Responsable a -> RequestContent -> Affjax e a ``` #### `put_` ``` purescript -put_ :: forall e. String -> RequestContent -> Affjax e Unit +put_ :: forall e. URL -> RequestContent -> Affjax e Unit ``` #### `delete` ``` purescript -delete :: forall e a. Responsable a -> String -> Affjax e a +delete :: forall e a. URL -> Responsable a -> Affjax e a ``` #### `delete_` ``` purescript -delete_ :: forall e. String -> Affjax e Unit +delete_ :: forall e. URL -> Affjax e Unit ``` diff --git a/src/Network/HTTP/Affjax.purs b/src/Network/HTTP/Affjax.purs index 781466b..cc19fb0 100644 --- a/src/Network/HTTP/Affjax.purs +++ b/src/Network/HTTP/Affjax.purs @@ -3,6 +3,7 @@ module Network.HTTP.Affjax , Affjax() , AffjaxOptions() , AffjaxResponse() + , URL() , url, method, content, headers, username, password , affjax , affjax' @@ -43,8 +44,11 @@ type AffjaxResponse a = , response :: a } +-- | Type alias for URL strings to aid readability of types. +type URL = String + -- | Sets the URL for a request. -url :: Option AffjaxOptions String +url :: Option AffjaxOptions URL url = opt "url" -- | Sets the HTTP method for a request. @@ -92,32 +96,32 @@ affjax' (Responsable read ty) opts eb cb = Left err -> eb $ error (show err) Right res' -> cb res' -get :: forall e a. Responsable a -> String -> Affjax e a -get r addr = affjax r $ method := GET - <> url := addr +get :: forall e a. URL -> Responsable a -> Affjax e a +get u r = affjax r $ method := GET + <> url := u -post :: forall e a. Responsable a -> String -> RequestContent -> Affjax e a -post r u c = affjax r $ method := POST +post :: forall e a. URL -> Responsable a -> RequestContent -> Affjax e a +post u r c = affjax r $ method := POST <> url := u <> content := c -post_ :: forall e. String -> RequestContent -> Affjax e Unit -post_ = post rUnit +post_ :: forall e. URL -> RequestContent -> Affjax e Unit +post_ = flip post rUnit -put :: forall e a. Responsable a -> String -> RequestContent -> Affjax e a -put r u c = affjax r $ method := PUT +put :: forall e a. URL -> Responsable a -> RequestContent -> Affjax e a +put u r c = affjax r $ method := PUT <> url := u <> content := c -put_ :: forall e. String -> RequestContent -> Affjax e Unit -put_ = put rUnit +put_ :: forall e. URL -> RequestContent -> Affjax e Unit +put_ = flip put rUnit -delete :: forall e a. Responsable a -> String -> Affjax e a -delete r u = affjax r $ method := DELETE +delete :: forall e a. URL -> Responsable a -> Affjax e a +delete u r = affjax r $ method := DELETE <> url := u -delete_ :: forall e. String -> Affjax e Unit -delete_ = delete rUnit +delete_ :: forall e. URL -> Affjax e Unit +delete_ = flip delete rUnit foreign import unsafeAjax """ diff --git a/test/Main.purs b/test/Main.purs index f5f328d..92deb05 100644 --- a/test/Main.purs +++ b/test/Main.purs @@ -42,7 +42,7 @@ main = do liftEff $ either traceAny traceAny res launchAff $ do - res <- attempt $ get rInt8Array "/arrayview" + res <- attempt $ get "/arrayview" rInt8Array liftEff $ either traceAny traceAny res go :: forall e. Options AffjaxOptions -> Eff (ajax :: Ajax, trace :: Trace | e) Unit From 1d3472f9e3c80d53cc96f0e3c7ee42a29ebecdc6 Mon Sep 17 00:00:00 2001 From: Gary Burgess Date: Fri, 27 Mar 2015 22:16:16 +0000 Subject: [PATCH 07/10] Add MOVE and COPY verbs --- README.md | 2 ++ src/Network/HTTP/Method.purs | 6 ++++++ 2 files changed, 8 insertions(+) diff --git a/README.md b/README.md index 0e79fe4..9a91117 100644 --- a/README.md +++ b/README.md @@ -169,6 +169,8 @@ data Method | PATCH | POST | PUT + | MOVE + | COPY | CustomMethod String ``` diff --git a/src/Network/HTTP/Method.purs b/src/Network/HTTP/Method.purs index c6b2b54..13c2fa2 100644 --- a/src/Network/HTTP/Method.purs +++ b/src/Network/HTTP/Method.purs @@ -10,6 +10,8 @@ data Method | PATCH | POST | PUT + | MOVE + | COPY | CustomMethod String instance eqMethod :: Eq Method where @@ -20,6 +22,8 @@ instance eqMethod :: Eq Method where (==) PATCH PATCH = true (==) POST POST = true (==) PUT PUT = true + (==) MOVE MOVE = true + (==) COPY COPY = true (==) _ _ = false (/=) x y = not (x == y) @@ -31,6 +35,8 @@ instance showMethod :: Show Method where show PATCH = "PATCH" show POST = "POST" show PUT = "PUT" + show MOVE = "MOVE" + show COPY = "COPY" show (CustomMethod m) = "(CustomMethod " ++ show m ++ ")" instance isOptionMethod :: IsOption Method where From e3dc584b5fab03ad10123b2159bb38822ed258af Mon Sep 17 00:00:00 2001 From: Gary Burgess Date: Sun, 29 Mar 2015 18:00:54 +0100 Subject: [PATCH 08/10] Drop options, use classes --- README.md | 222 +++++----------------- bower.json | 2 +- src/Network/HTTP/Affjax.purs | 146 +++++++------- src/Network/HTTP/Affjax/Request.purs | 47 ++--- src/Network/HTTP/Affjax/Response.purs | 85 ++++----- src/Network/HTTP/Affjax/ResponseType.purs | 5 - src/Network/HTTP/Method.purs | 5 - src/Network/HTTP/RequestHeader.purs | 4 - src/Type/Proxy.purs | 3 + test/Main.purs | 27 +-- 10 files changed, 189 insertions(+), 357 deletions(-) create mode 100644 src/Type/Proxy.purs diff --git a/README.md b/README.md index 9a91117..1435cfe 100644 --- a/README.md +++ b/README.md @@ -1,5 +1,16 @@ # Module Documentation +## Module Type.Proxy + +#### `Proxy` + +``` purescript +data Proxy a + = Proxy +``` + + + ## Module Network.HTTP.Affjax #### `Ajax` @@ -18,13 +29,19 @@ type Affjax e a = Aff (ajax :: Ajax | e) (AffjaxResponse a) The type for Affjax requests. -#### `AffjaxOptions` +#### `AffjaxRequest` ``` purescript -data AffjaxOptions :: * +type AffjaxRequest a = { password :: Maybe String, username :: Maybe String, content :: Maybe a, headers :: [RequestHeader], url :: URL, method :: Method } +``` + + +#### `defaultRequest` + +``` purescript +defaultRequest :: AffjaxRequest Unit ``` -Options type for Affjax requests. #### `AffjaxResponse` @@ -42,109 +59,53 @@ type URL = String Type alias for URL strings to aid readability of types. -#### `url` - -``` purescript -url :: Option AffjaxOptions URL -``` - -Sets the URL for a request. - -#### `method` - -``` purescript -method :: Option AffjaxOptions Method -``` - -Sets the HTTP method for a request. - -#### `content` - -``` purescript -content :: Option AffjaxOptions RequestContent -``` - -Sets the content to send in a request. - -#### `headers` - -``` purescript -headers :: Option AffjaxOptions [RequestHeader] -``` - -Sets the headers to send with a request. - -#### `username` - -``` purescript -username :: Option AffjaxOptions String -``` - -Sets the HTTP auth username to send with a request. - -#### `password` - -``` purescript -password :: Option AffjaxOptions String -``` - -Sets the HTTP auth password to send with a request. - #### `affjax` ``` purescript -affjax :: forall e a. Responsable a -> Options AffjaxOptions -> Affjax e a -``` - -Runs a request. - -#### `affjax'` - -``` purescript -affjax' :: forall e a. Responsable a -> Options AffjaxOptions -> (Error -> Eff (ajax :: Ajax | e) Unit) -> (AffjaxResponse a -> Eff (ajax :: Ajax | e) Unit) -> Eff (ajax :: Ajax | e) Unit +affjax :: forall e a b. (Requestable a, Responsable b) => AffjaxRequest a -> Affjax e b ``` -Runs a request directly in Eff. +Makes an `Affjax` request. #### `get` ``` purescript -get :: forall e a. URL -> Responsable a -> Affjax e a +get :: forall e a. (Responsable a) => URL -> Affjax e a ``` #### `post` ``` purescript -post :: forall e a. URL -> Responsable a -> RequestContent -> Affjax e a +post :: forall e a b. (Requestable a, Responsable b) => URL -> a -> Affjax e b ``` #### `post_` ``` purescript -post_ :: forall e. URL -> RequestContent -> Affjax e Unit +post_ :: forall e a. (Requestable a) => URL -> a -> Affjax e Unit ``` #### `put` ``` purescript -put :: forall e a. URL -> Responsable a -> RequestContent -> Affjax e a +put :: forall e a b. (Requestable a, Responsable b) => URL -> a -> Affjax e b ``` #### `put_` ``` purescript -put_ :: forall e. URL -> RequestContent -> Affjax e Unit +put_ :: forall e a. (Requestable a) => URL -> a -> Affjax e Unit ``` #### `delete` ``` purescript -delete :: forall e a. URL -> Responsable a -> Affjax e a +delete :: forall e a. (Responsable a) => URL -> Affjax e a ``` @@ -155,6 +116,14 @@ delete_ :: forall e. URL -> Affjax e Unit ``` +#### `affjax'` + +``` purescript +affjax' :: forall e a b. (Requestable a, Responsable b) => AffjaxRequest a -> (Error -> Eff (ajax :: Ajax | e) Unit) -> (AffjaxResponse b -> Eff (ajax :: Ajax | e) Unit) -> Eff (ajax :: Ajax | e) Unit +``` + +Run a request directly without using `Aff`. + ## Module Network.HTTP.Method @@ -189,13 +158,6 @@ instance showMethod :: Show Method ``` -#### `isOptionMethod` - -``` purescript -instance isOptionMethod :: IsOption Method -``` - - #### `methodToString` ``` purescript @@ -262,13 +224,6 @@ instance showRequestHeader :: Show RequestHeader ``` -#### `isOptionRequestHeader` - -``` purescript -instance isOptionRequestHeader :: IsOption RequestHeader -``` - - #### `requestHeaderName` ``` purescript @@ -351,18 +306,11 @@ data RequestContent :: * Type representing all content types that be sent via XHR (ArrayBufferView, Blob, Document, String, FormData). -#### `isOptionRequestContent` - -``` purescript -instance isOptionRequestContent :: IsOption RequestContent -``` - - #### `Requestable` ``` purescript class Requestable a where - toContent :: a -> RequestContent + toRequest :: a -> RequestContent ``` A class for types that can be converted to values that can be sent with @@ -488,106 +436,43 @@ Type representing content types that be received from an XHR request #### `Responsable` ``` purescript -data Responsable a - = Responsable (ResponseContent -> F a) ResponseType -``` - - -#### `rInt8Array` - -``` purescript -rInt8Array :: Responsable A.Int8Array -``` - - -#### `rInt16Array` - -``` purescript -rInt16Array :: Responsable A.Int16Array -``` - - -#### `rInt32Array` - -``` purescript -rInt32Array :: Responsable A.Int32Array -``` - - -#### `rUint8Array` - -``` purescript -rUint8Array :: Responsable A.Uint8Array -``` - - -#### `rUint16Array` - -``` purescript -rUint16Array :: Responsable A.Uint16Array -``` - - -#### `rUint32Array` - -``` purescript -rUint32Array :: Responsable A.Uint32Array -``` - - -#### `rUint8ClampedArray` - -``` purescript -rUint8ClampedArray :: Responsable A.Uint8ClampedArray -``` - - -#### `rFloat32Array` - -``` purescript -rFloat32Array :: Responsable A.Float32Array +class Responsable a where + responseType :: Proxy a -> ResponseType + fromResponse :: ResponseContent -> F a ``` -#### `rFloat64Array` +#### `responsableBlob` ``` purescript -rFloat64Array :: Responsable A.Float64Array +instance responsableBlob :: Responsable Blob ``` - -#### `rBlob` +#### `responsableDocument` ``` purescript -rBlob :: Responsable Blob +instance responsableDocument :: Responsable Document ``` -#### `rDocument` +#### `responsableJSON` ``` purescript -rDocument :: Responsable Document +instance responsableJSON :: Responsable Foreign ``` -#### `rJSON` +#### `responsableString` ``` purescript -rJSON :: Responsable Foreign +instance responsableString :: Responsable String ``` -#### `rString` +#### `responsableUnit` ``` purescript -rString :: Responsable String -``` - - -#### `rUnit` - -``` purescript -rUnit :: Responsable Unit +instance responsableUnit :: Responsable Unit ``` @@ -622,13 +507,6 @@ instance showResponseType :: Show ResponseType ``` -#### `isOptionResponseType` - -``` purescript -instance isOptionResponseType :: IsOption ResponseType -``` - - #### `ajaxResponseTypeToString` ``` purescript diff --git a/bower.json b/bower.json index 0f372ce..9fbc019 100644 --- a/bower.json +++ b/bower.json @@ -24,6 +24,6 @@ "purescript-dom": "~0.1.2", "purescript-foreign": "~0.4.2", "purescript-integers": "~0.0.1", - "purescript-options": "~0.3.0" + "purescript-nullable": "~0.1.1" } } diff --git a/src/Network/HTTP/Affjax.purs b/src/Network/HTTP/Affjax.purs index cc19fb0..4bb978d 100644 --- a/src/Network/HTTP/Affjax.purs +++ b/src/Network/HTTP/Affjax.purs @@ -1,10 +1,9 @@ module Network.HTTP.Affjax ( Ajax() , Affjax() - , AffjaxOptions() + , AffjaxRequest(), defaultRequest , AffjaxResponse() , URL() - , url, method, content, headers, username, password , affjax , affjax' , get @@ -16,15 +15,16 @@ module Network.HTTP.Affjax import Control.Monad.Aff (Aff(), makeAff) import Control.Monad.Eff (Eff()) import Control.Monad.Eff.Exception (Error(), error) +import Data.Maybe (Maybe(..)) import Data.Either (Either(..)) import Data.Foreign (Foreign(..), F()) import Data.Function (Fn4(), runFn4) -import Data.Options (Option(), Options(), options, (:=), opt) +import Data.Nullable (Nullable(), toNullable) import Network.HTTP.Affjax.Request import Network.HTTP.Affjax.Response import Network.HTTP.Affjax.ResponseType -import Network.HTTP.Method (Method(..)) -import Network.HTTP.RequestHeader (RequestHeader()) +import Network.HTTP.Method (Method(..), methodToString) +import Network.HTTP.RequestHeader (RequestHeader(), requestHeaderName, requestHeaderValue) import Network.HTTP.ResponseHeader (ResponseHeader(), responseHeader) import Network.HTTP.StatusCode (StatusCode()) @@ -34,8 +34,24 @@ foreign import data Ajax :: ! -- | The type for Affjax requests. type Affjax e a = Aff (ajax :: Ajax | e) (AffjaxResponse a) --- | Options type for Affjax requests. -foreign import data AffjaxOptions :: * +type AffjaxRequest a = + { method :: Method + , url :: URL + , headers :: [RequestHeader] + , content :: Maybe a + , username :: Maybe String + , password :: Maybe String + } + +defaultRequest :: AffjaxRequest Unit +defaultRequest = + { method: GET + , url: "/" + , headers: [] + , content: Nothing + , username: Nothing + , password: Nothing + } -- | The type of records that will be received as an Affjax response. type AffjaxResponse a = @@ -47,81 +63,61 @@ type AffjaxResponse a = -- | Type alias for URL strings to aid readability of types. type URL = String --- | Sets the URL for a request. -url :: Option AffjaxOptions URL -url = opt "url" - --- | Sets the HTTP method for a request. -method :: Option AffjaxOptions Method -method = opt "method" - --- | Sets the content to send in a request. -content :: Option AffjaxOptions RequestContent -content = opt "content" - --- | Sets the headers to send with a request. -headers :: Option AffjaxOptions [RequestHeader] -headers = opt "headers" - --- | Sets the HTTP auth username to send with a request. -username :: Option AffjaxOptions String -username = opt "username" - --- | Sets the HTTP auth password to send with a request. -password :: Option AffjaxOptions String -password = opt "password" - --- | Sets the expected response type for a request. This is not exposed outside --- | of the module as the `ResponseType` is set based on the `Responsable` --- | instance for the expected result content type. -responseType = opt "responseType" :: Option AffjaxOptions ResponseType - --- | Runs a request. -affjax :: forall e a. Responsable a -> - Options AffjaxOptions -> - Affjax e a -affjax r = makeAff <<< affjax' r - --- | Runs a request directly in Eff. -affjax' :: forall e a. Responsable a -> - Options AffjaxOptions -> - (Error -> Eff (ajax :: Ajax | e) Unit) -> - (AffjaxResponse a -> Eff (ajax :: Ajax | e) Unit) -> - Eff (ajax :: Ajax | e) Unit -affjax' (Responsable read ty) opts eb cb = - runFn4 unsafeAjax responseHeader (options $ opts <> responseType := ty) eb cb' - where - cb' :: AffjaxResponse Foreign -> Eff (ajax :: Ajax | e) Unit - cb' res = case res { response = _ } <$> read res.response of - Left err -> eb $ error (show err) - Right res' -> cb res' +-- | Makes an `Affjax` request. +affjax :: forall e a b. (Requestable a, Responsable b) => AffjaxRequest a -> Affjax e b +affjax = makeAff <<< affjax' -get :: forall e a. URL -> Responsable a -> Affjax e a -get u r = affjax r $ method := GET - <> url := u +get :: forall e a. (Responsable a) => URL -> Affjax e a +get u = affjax $ defaultRequest { url = u } -post :: forall e a. URL -> Responsable a -> RequestContent -> Affjax e a -post u r c = affjax r $ method := POST - <> url := u - <> content := c +post :: forall e a b. (Requestable a, Responsable b) => URL -> a -> Affjax e b +post u c = affjax $ defaultRequest { method = POST, url = u, content = Just c } -post_ :: forall e. URL -> RequestContent -> Affjax e Unit -post_ = flip post rUnit +post_ :: forall e a. (Requestable a) => URL -> a -> Affjax e Unit +post_ = post -put :: forall e a. URL -> Responsable a -> RequestContent -> Affjax e a -put u r c = affjax r $ method := PUT - <> url := u - <> content := c +put :: forall e a b. (Requestable a, Responsable b) => URL -> a -> Affjax e b +put u c = affjax $ defaultRequest { method = PUT, url = u, content = Just c } -put_ :: forall e. URL -> RequestContent -> Affjax e Unit -put_ = flip put rUnit +put_ :: forall e a. (Requestable a) => URL -> a -> Affjax e Unit +put_ = put -delete :: forall e a. URL -> Responsable a -> Affjax e a -delete u r = affjax r $ method := DELETE - <> url := u +delete :: forall e a. (Responsable a) => URL -> Affjax e a +delete u = affjax $ defaultRequest { method = DELETE, url = u } delete_ :: forall e. URL -> Affjax e Unit -delete_ = flip delete rUnit +delete_ = delete + +-- | Run a request directly without using `Aff`. +affjax' :: forall e a b. (Requestable a, Responsable b) => + AffjaxRequest a -> + (Error -> Eff (ajax :: Ajax | e) Unit) -> + (AffjaxResponse b -> Eff (ajax :: Ajax | e) Unit) -> + Eff (ajax :: Ajax | e) Unit +affjax' req eb cb = + runFn4 unsafeAjax responseHeader req' eb cb' + where + req' :: AjaxRequest + req' = { method: methodToString req.method + , url: req.url + , headers: (\h -> { field: requestHeaderName h, value: requestHeaderValue h }) <$> req.headers + , content: toNullable (toRequest <$> req.content) + , username: toNullable req.username + , password: toNullable req.password + } + cb' :: AffjaxResponse ResponseContent -> Eff (ajax :: Ajax | e) Unit + cb' res = case res { response = _ } <$> fromResponse res.response of + Left err -> eb $ error (show err) + Right res' -> cb res' + +type AjaxRequest = + { method :: String + , url :: URL + , headers :: [{ field :: String, value :: String }] + , content :: Nullable RequestContent + , username :: Nullable String + , password :: Nullable String + } foreign import unsafeAjax """ @@ -156,7 +152,7 @@ foreign import unsafeAjax }; } """ :: forall e a. Fn4 (String -> String -> ResponseHeader) - Foreign + AjaxRequest (Error -> Eff (ajax :: Ajax | e) Unit) (AffjaxResponse Foreign -> Eff (ajax :: Ajax | e) Unit) (Eff (ajax :: Ajax | e) Unit) diff --git a/src/Network/HTTP/Affjax/Request.purs b/src/Network/HTTP/Affjax/Request.purs index 8a60961..49cdc66 100644 --- a/src/Network/HTTP/Affjax/Request.purs +++ b/src/Network/HTTP/Affjax/Request.purs @@ -1,9 +1,8 @@ module Network.HTTP.Affjax.Request ( RequestContent() - , Requestable, toContent + , Requestable, toRequest ) where -import Data.Options (Option(), Options(), IsOption, (:=)) import DOM (Document()) import DOM.File (Blob()) import DOM.XHR (FormData()) @@ -13,67 +12,55 @@ import qualified Data.ArrayBuffer.Types as A -- | Blob, Document, String, FormData). foreign import data RequestContent :: * -instance isOptionRequestContent :: IsOption RequestContent where - (:=) = unsafeIsOption - -- | A class for types that can be converted to values that can be sent with -- | XHR requests. class Requestable a where - toContent :: a -> RequestContent + toRequest :: a -> RequestContent instance requestableRequestContent :: Requestable RequestContent where - toContent = id + toRequest = id instance requestableInt8Array :: Requestable (A.ArrayView A.Int8) where - toContent = unsafeConversion + toRequest = unsafeConversion instance requestableInt16Array :: Requestable (A.ArrayView A.Int16) where - toContent = unsafeConversion + toRequest = unsafeConversion instance requestableInt32Array :: Requestable (A.ArrayView A.Int32) where - toContent = unsafeConversion + toRequest = unsafeConversion instance requestableUint8Array :: Requestable (A.ArrayView A.Uint8) where - toContent = unsafeConversion + toRequest = unsafeConversion instance requestableUint16Array :: Requestable (A.ArrayView A.Uint16) where - toContent = unsafeConversion + toRequest = unsafeConversion instance requestableUint32Array :: Requestable (A.ArrayView A.Uint32) where - toContent = unsafeConversion + toRequest = unsafeConversion instance requestableUint8ClampedArray :: Requestable (A.ArrayView A.Uint8Clamped) where - toContent = unsafeConversion + toRequest = unsafeConversion instance requestableFloat32Array :: Requestable (A.ArrayView A.Float32) where - toContent = unsafeConversion + toRequest = unsafeConversion instance requestableFloat64Array :: Requestable (A.ArrayView A.Float64) where - toContent = unsafeConversion + toRequest = unsafeConversion instance requestableBlob :: Requestable Blob where - toContent = unsafeConversion + toRequest = unsafeConversion instance requestableDocument :: Requestable Document where - toContent = unsafeConversion + toRequest = unsafeConversion instance requestableString :: Requestable String where - toContent = unsafeConversion + toRequest = unsafeConversion instance requestableFormData :: Requestable FormData where - toContent = unsafeConversion + toRequest = unsafeConversion instance requestableUnit :: Requestable Unit where - toContent = unsafeConversion - -foreign import unsafeIsOption - """ - function unsafeIsOption(k) { - return function (v) { - return [[k, v]]; - }; - } - """ :: forall b a. (Option b a) -> a -> (Options b) + toRequest = unsafeConversion foreign import unsafeConversion """ diff --git a/src/Network/HTTP/Affjax/Response.purs b/src/Network/HTTP/Affjax/Response.purs index 74a4394..e5c13e9 100644 --- a/src/Network/HTTP/Affjax/Response.purs +++ b/src/Network/HTTP/Affjax/Response.purs @@ -1,74 +1,69 @@ module Network.HTTP.Affjax.Response ( ResponseContent() - , Responsable(..) - , rInt8Array - , rInt16Array - , rInt32Array - , rUint8Array - , rUint16Array - , rUint32Array - , rUint8ClampedArray - , rFloat32Array - , rFloat64Array - , rBlob - , rDocument - , rJSON - , rString - , rUnit + , Responsable, responseType, fromResponse ) where +import Control.Bind ((>=>)) import Data.Either (Either(..)) -import Data.Foreign (Foreign(), F(), readString, unsafeReadTagged) +import Data.Foreign (Foreign(), F(), readString, parseJSON, unsafeReadTagged) import DOM (Document()) import DOM.File (Blob()) import DOM.XHR (FormData()) import Network.HTTP.Affjax.ResponseType +import Type.Proxy (Proxy()) import qualified Data.ArrayBuffer.Types as A -- | Type representing content types that be received from an XHR request -- | (ArrayBuffer, Blob, Document, JSON, String). type ResponseContent = Foreign -data Responsable a = Responsable (ResponseContent -> F a) ResponseType +class Responsable a where + responseType :: Proxy a -> ResponseType + fromResponse :: ResponseContent -> F a -rInt8Array :: Responsable A.Int8Array -rInt8Array = Responsable (unsafeReadTagged "ArrayBuffer") ArrayBufferResponse +-- rInt8Array :: Responsable A.Int8Array +-- rInt8Array = Responsable (unsafeReadTagged "ArrayBuffer") ArrayBufferResponse -rInt16Array :: Responsable A.Int16Array -rInt16Array = Responsable (unsafeReadTagged "ArrayBuffer") ArrayBufferResponse +-- rInt16Array :: Responsable A.Int16Array +-- rInt16Array = Responsable (unsafeReadTagged "ArrayBuffer") ArrayBufferResponse -rInt32Array :: Responsable A.Int32Array -rInt32Array = Responsable (unsafeReadTagged "ArrayBuffer") ArrayBufferResponse +-- rInt32Array :: Responsable A.Int32Array +-- rInt32Array = Responsable (unsafeReadTagged "ArrayBuffer") ArrayBufferResponse -rUint8Array :: Responsable A.Uint8Array -rUint8Array = Responsable (unsafeReadTagged "ArrayBuffer") ArrayBufferResponse +-- rUint8Array :: Responsable A.Uint8Array +-- rUint8Array = Responsable (unsafeReadTagged "ArrayBuffer") ArrayBufferResponse -rUint16Array :: Responsable A.Uint16Array -rUint16Array = Responsable (unsafeReadTagged "ArrayBuffer") ArrayBufferResponse +-- rUint16Array :: Responsable A.Uint16Array +-- rUint16Array = Responsable (unsafeReadTagged "ArrayBuffer") ArrayBufferResponse -rUint32Array :: Responsable A.Uint32Array -rUint32Array = Responsable (unsafeReadTagged "ArrayBuffer") ArrayBufferResponse +-- rUint32Array :: Responsable A.Uint32Array +-- rUint32Array = Responsable (unsafeReadTagged "ArrayBuffer") ArrayBufferResponse -rUint8ClampedArray :: Responsable A.Uint8ClampedArray -rUint8ClampedArray = Responsable (unsafeReadTagged "ArrayBuffer") ArrayBufferResponse +-- rUint8ClampedArray :: Responsable A.Uint8ClampedArray +-- rUint8ClampedArray = Responsable (unsafeReadTagged "ArrayBuffer") ArrayBufferResponse -rFloat32Array :: Responsable A.Float32Array -rFloat32Array = Responsable (unsafeReadTagged "ArrayBuffer") ArrayBufferResponse +-- rFloat32Array :: Responsable A.Float32Array +-- rFloat32Array = Responsable (unsafeReadTagged "ArrayBuffer") ArrayBufferResponse -rFloat64Array :: Responsable A.Float64Array -rFloat64Array = Responsable (unsafeReadTagged "ArrayBuffer") ArrayBufferResponse +-- rFloat64Array :: Responsable A.Float64Array +-- rFloat64Array = Responsable (unsafeReadTagged "ArrayBuffer") ArrayBufferResponse -rBlob :: Responsable Blob -rBlob = Responsable (unsafeReadTagged "Blob") BlobResponse +instance responsableBlob :: Responsable Blob where + responseType _ = BlobResponse + fromResponse = unsafeReadTagged "Blob" -rDocument :: Responsable Document -rDocument = Responsable (unsafeReadTagged "Document") DocumentResponse +instance responsableDocument :: Responsable Document where + responseType _ = DocumentResponse + fromResponse = unsafeReadTagged "Document" -rJSON :: Responsable Foreign -rJSON = Responsable Right JSONResponse +instance responsableJSON :: Responsable Foreign where + responseType _ = JSONResponse + fromResponse = readString >=> parseJSON -rString :: Responsable String -rString = Responsable readString StringResponse +instance responsableString :: Responsable String where + responseType _ = StringResponse + fromResponse = readString -rUnit :: Responsable Unit -rUnit = Responsable (const $ Right unit) StringResponse +instance responsableUnit :: Responsable Unit where + responseType _ = StringResponse + fromResponse = const (Right unit) diff --git a/src/Network/HTTP/Affjax/ResponseType.purs b/src/Network/HTTP/Affjax/ResponseType.purs index 7288c21..90cd357 100644 --- a/src/Network/HTTP/Affjax/ResponseType.purs +++ b/src/Network/HTTP/Affjax/ResponseType.purs @@ -1,7 +1,5 @@ module Network.HTTP.Affjax.ResponseType where -import Data.Options (IsOption, optionFn, (:=)) - -- | Valid response types for an AJAX request. This is used to determine the -- | `ResponseContent` type for a request. data ResponseType @@ -27,9 +25,6 @@ instance showResponseType :: Show ResponseType where show JSONResponse = "JSONResponse" show StringResponse = "StringResponse" -instance isOptionResponseType :: IsOption ResponseType where - (:=) k a = (optionFn k) := ajaxResponseTypeToString a - ajaxResponseTypeToString :: ResponseType -> String ajaxResponseTypeToString ArrayBufferResponse = "arraybuffer" ajaxResponseTypeToString BlobResponse = "blob" diff --git a/src/Network/HTTP/Method.purs b/src/Network/HTTP/Method.purs index 13c2fa2..6f6a469 100644 --- a/src/Network/HTTP/Method.purs +++ b/src/Network/HTTP/Method.purs @@ -1,7 +1,5 @@ module Network.HTTP.Method where -import Data.Options (IsOption, optionFn, (:=)) - data Method = DELETE | GET @@ -39,9 +37,6 @@ instance showMethod :: Show Method where show COPY = "COPY" show (CustomMethod m) = "(CustomMethod " ++ show m ++ ")" -instance isOptionMethod :: IsOption Method where - (:=) k a = (optionFn k) := methodToString a - methodToString :: Method -> String methodToString (CustomMethod m) = m methodToString other = show other diff --git a/src/Network/HTTP/RequestHeader.purs b/src/Network/HTTP/RequestHeader.purs index d224a56..871d7d4 100644 --- a/src/Network/HTTP/RequestHeader.purs +++ b/src/Network/HTTP/RequestHeader.purs @@ -1,6 +1,5 @@ module Network.HTTP.RequestHeader where -import Data.Options (IsOption, optionFn, (:=)) import Network.HTTP.MimeType data RequestHeader @@ -20,9 +19,6 @@ instance showRequestHeader :: Show RequestHeader where show (ContentType m) = "(ContentType " ++ show m ++ ")" show (RequestHeader h v) = "(RequestHeader " ++ show h ++ " " ++ show v ++ ")" -instance isOptionRequestHeader :: IsOption RequestHeader where - (:=) k a = (optionFn k) := { field: requestHeaderName a, value: requestHeaderValue a } - requestHeaderName :: RequestHeader -> String requestHeaderName (Accept _) = "Accept" requestHeaderName (ContentType _) = "Content-Type" diff --git a/src/Type/Proxy.purs b/src/Type/Proxy.purs new file mode 100644 index 0000000..ee3ccda --- /dev/null +++ b/src/Type/Proxy.purs @@ -0,0 +1,3 @@ +module Type.Proxy where + +data Proxy a = Proxy diff --git a/test/Main.purs b/test/Main.purs index 92deb05..9873f68 100644 --- a/test/Main.purs +++ b/test/Main.purs @@ -6,7 +6,6 @@ import Control.Monad.Eff.Class import Control.Monad.Eff.Exception import Data.Either import Data.Foreign -import Data.Options import Debug.Trace import Network.HTTP.Affjax import Network.HTTP.Affjax.Response @@ -25,25 +24,13 @@ foreign import traceAny } """ :: forall e a. a -> Eff (trace :: Trace | e) Unit -foreign import noContent "var noContent = new FormData();" :: RequestContent +main = launchAff $ do -main = do + res <- attempt $ affjax $ defaultRequest { url = "/api", method = POST } + liftEff $ either traceAny (traceAny :: AffjaxResponse String -> _) res - go $ url := "/api" - <> headers := [ContentType applicationOctetStream] - <> content := (toContent "test") + res <- attempt $ post_ "/api" "test" + liftEff $ either traceAny traceAny res - go $ url := "/api" - <> method := POST - <> content := (toContent unit) - - launchAff $ do - res <- attempt $ affjax rString $ url := "/api" <> method := POST - liftEff $ either traceAny traceAny res - - launchAff $ do - res <- attempt $ get "/arrayview" rInt8Array - liftEff $ either traceAny traceAny res - -go :: forall e. Options AffjaxOptions -> Eff (ajax :: Ajax, trace :: Trace | e) Unit -go opts = affjax' rUnit opts traceAny traceAny + res <- attempt $ get "/arrayview" + liftEff $ either traceAny (traceAny :: AffjaxResponse Foreign -> _) res From 7ac8f648c1d279071f2c075dc7664c6341b234eb Mon Sep 17 00:00:00 2001 From: Gary Burgess Date: Sun, 29 Mar 2015 18:14:20 +0100 Subject: [PATCH 09/10] Set responseType in request --- README.md | 4 ++-- src/Network/HTTP/Affjax.purs | 7 +++++-- src/Network/HTTP/Affjax/Response.purs | 2 +- src/Network/HTTP/Affjax/ResponseType.purs | 12 ++++++------ 4 files changed, 14 insertions(+), 11 deletions(-) diff --git a/README.md b/README.md index 1435cfe..19f08d3 100644 --- a/README.md +++ b/README.md @@ -507,10 +507,10 @@ instance showResponseType :: Show ResponseType ``` -#### `ajaxResponseTypeToString` +#### `responseTypeToString` ``` purescript -ajaxResponseTypeToString :: ResponseType -> String +responseTypeToString :: ResponseType -> String ``` diff --git a/src/Network/HTTP/Affjax.purs b/src/Network/HTTP/Affjax.purs index 4bb978d..3a1bfe7 100644 --- a/src/Network/HTTP/Affjax.purs +++ b/src/Network/HTTP/Affjax.purs @@ -15,10 +15,10 @@ module Network.HTTP.Affjax import Control.Monad.Aff (Aff(), makeAff) import Control.Monad.Eff (Eff()) import Control.Monad.Eff.Exception (Error(), error) -import Data.Maybe (Maybe(..)) import Data.Either (Either(..)) import Data.Foreign (Foreign(..), F()) import Data.Function (Fn4(), runFn4) +import Data.Maybe (Maybe(..), maybe) import Data.Nullable (Nullable(), toNullable) import Network.HTTP.Affjax.Request import Network.HTTP.Affjax.Response @@ -27,6 +27,7 @@ import Network.HTTP.Method (Method(..), methodToString) import Network.HTTP.RequestHeader (RequestHeader(), requestHeaderName, requestHeaderValue) import Network.HTTP.ResponseHeader (ResponseHeader(), responseHeader) import Network.HTTP.StatusCode (StatusCode()) +import Type.Proxy (Proxy(..)) -- | The effect type for AJAX requests made with Affjax. foreign import data Ajax :: ! @@ -102,6 +103,7 @@ affjax' req eb cb = , url: req.url , headers: (\h -> { field: requestHeaderName h, value: requestHeaderValue h }) <$> req.headers , content: toNullable (toRequest <$> req.content) + , responseType: responseTypeToString $ responseType (Proxy :: Proxy b) , username: toNullable req.username , password: toNullable req.password } @@ -115,6 +117,7 @@ type AjaxRequest = , url :: URL , headers :: [{ field :: String, value :: String }] , content :: Nullable RequestContent + , responseType :: String , username :: Nullable String , password :: Nullable String } @@ -147,7 +150,7 @@ foreign import unsafeAjax response: xhr.response })(); }; - if (options.responseType) xhr.responseType = options.responseType; + xhr.responseType = options.responseType; xhr.send(options.content); }; } diff --git a/src/Network/HTTP/Affjax/Response.purs b/src/Network/HTTP/Affjax/Response.purs index e5c13e9..3fd80c2 100644 --- a/src/Network/HTTP/Affjax/Response.purs +++ b/src/Network/HTTP/Affjax/Response.purs @@ -58,7 +58,7 @@ instance responsableDocument :: Responsable Document where instance responsableJSON :: Responsable Foreign where responseType _ = JSONResponse - fromResponse = readString >=> parseJSON + fromResponse = Right instance responsableString :: Responsable String where responseType _ = StringResponse diff --git a/src/Network/HTTP/Affjax/ResponseType.purs b/src/Network/HTTP/Affjax/ResponseType.purs index 90cd357..71f7373 100644 --- a/src/Network/HTTP/Affjax/ResponseType.purs +++ b/src/Network/HTTP/Affjax/ResponseType.purs @@ -25,9 +25,9 @@ instance showResponseType :: Show ResponseType where show JSONResponse = "JSONResponse" show StringResponse = "StringResponse" -ajaxResponseTypeToString :: ResponseType -> String -ajaxResponseTypeToString ArrayBufferResponse = "arraybuffer" -ajaxResponseTypeToString BlobResponse = "blob" -ajaxResponseTypeToString DocumentResponse = "document" -ajaxResponseTypeToString JSONResponse = "json" -ajaxResponseTypeToString StringResponse = "text" +responseTypeToString :: ResponseType -> String +responseTypeToString ArrayBufferResponse = "arraybuffer" +responseTypeToString BlobResponse = "blob" +responseTypeToString DocumentResponse = "document" +responseTypeToString JSONResponse = "json" +responseTypeToString StringResponse = "text" From c8e805330dc4f3aea749764b4ec0ee2bd6322ae6 Mon Sep 17 00:00:00 2001 From: Gary Burgess Date: Sun, 29 Mar 2015 18:21:21 +0100 Subject: [PATCH 10/10] Add more POST and PUT variations --- README.md | 43 ++++++++++++++++++++++++++++++++++++ src/Network/HTTP/Affjax.purs | 31 ++++++++++++++++++++++++-- 2 files changed, 72 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index 19f08d3..8726bf6 100644 --- a/README.md +++ b/README.md @@ -73,6 +73,7 @@ Makes an `Affjax` request. get :: forall e a. (Responsable a) => URL -> Affjax e a ``` +Makes a `GET` request to the specified URL. #### `post` @@ -80,6 +81,15 @@ get :: forall e a. (Responsable a) => URL -> Affjax e a post :: forall e a b. (Requestable a, Responsable b) => URL -> a -> Affjax e b ``` +Makes a `POST` request to the specified URL, sending data. + +#### `post'` + +``` purescript +post' :: forall e a b. (Requestable a, Responsable b) => URL -> Maybe a -> Affjax e b +``` + +Makes a `POST` request to the specified URL with the option to send data. #### `post_` @@ -87,6 +97,17 @@ post :: forall e a b. (Requestable a, Responsable b) => URL -> a -> Affjax e b post_ :: forall e a. (Requestable a) => URL -> a -> Affjax e Unit ``` +Makes a `POST` request to the specified URL, sending data and ignoring the +response. + +#### `post_'` + +``` purescript +post_' :: forall e a. (Requestable a) => URL -> Maybe a -> Affjax e Unit +``` + +Makes a `POST` request to the specified URL with the option to send data, +and ignores the response. #### `put` @@ -94,6 +115,15 @@ post_ :: forall e a. (Requestable a) => URL -> a -> Affjax e Unit put :: forall e a b. (Requestable a, Responsable b) => URL -> a -> Affjax e b ``` +Makes a `PUT` request to the specified URL, sending data. + +#### `put'` + +``` purescript +put' :: forall e a b. (Requestable a, Responsable b) => URL -> Maybe a -> Affjax e b +``` + +Makes a `PUT` request to the specified URL with the option to send data. #### `put_` @@ -101,6 +131,17 @@ put :: forall e a b. (Requestable a, Responsable b) => URL -> a -> Affjax e b put_ :: forall e a. (Requestable a) => URL -> a -> Affjax e Unit ``` +Makes a `PUT` request to the specified URL, sending data and ignoring the +response. + +#### `put_'` + +``` purescript +put_' :: forall e a. (Requestable a) => URL -> Maybe a -> Affjax e Unit +``` + +Makes a `PUT` request to the specified URL with the option to send data, +and ignores the response. #### `delete` @@ -108,6 +149,7 @@ put_ :: forall e a. (Requestable a) => URL -> a -> Affjax e Unit delete :: forall e a. (Responsable a) => URL -> Affjax e a ``` +Makes a `DELETE` request to the specified URL. #### `delete_` @@ -115,6 +157,7 @@ delete :: forall e a. (Responsable a) => URL -> Affjax e a delete_ :: forall e. URL -> Affjax e Unit ``` +Makes a `DELETE` request to the specified URL and ignores the response. #### `affjax'` diff --git a/src/Network/HTTP/Affjax.purs b/src/Network/HTTP/Affjax.purs index 3a1bfe7..0558753 100644 --- a/src/Network/HTTP/Affjax.purs +++ b/src/Network/HTTP/Affjax.purs @@ -7,8 +7,8 @@ module Network.HTTP.Affjax , affjax , affjax' , get - , post, post_ - , put, put_ + , post, post_, post', post_' + , put, put_, put', put_' , delete, delete_ ) where @@ -68,24 +68,51 @@ type URL = String affjax :: forall e a b. (Requestable a, Responsable b) => AffjaxRequest a -> Affjax e b affjax = makeAff <<< affjax' +-- | Makes a `GET` request to the specified URL. get :: forall e a. (Responsable a) => URL -> Affjax e a get u = affjax $ defaultRequest { url = u } +-- | Makes a `POST` request to the specified URL, sending data. post :: forall e a b. (Requestable a, Responsable b) => URL -> a -> Affjax e b post u c = affjax $ defaultRequest { method = POST, url = u, content = Just c } +-- | Makes a `POST` request to the specified URL with the option to send data. +post' :: forall e a b. (Requestable a, Responsable b) => URL -> Maybe a -> Affjax e b +post' u c = affjax $ defaultRequest { method = POST, url = u, content = c } + +-- | Makes a `POST` request to the specified URL, sending data and ignoring the +-- | response. post_ :: forall e a. (Requestable a) => URL -> a -> Affjax e Unit post_ = post +-- | Makes a `POST` request to the specified URL with the option to send data, +-- | and ignores the response. +post_' :: forall e a. (Requestable a) => URL -> Maybe a -> Affjax e Unit +post_' = post' + +-- | Makes a `PUT` request to the specified URL, sending data. put :: forall e a b. (Requestable a, Responsable b) => URL -> a -> Affjax e b put u c = affjax $ defaultRequest { method = PUT, url = u, content = Just c } +-- | Makes a `PUT` request to the specified URL with the option to send data. +put' :: forall e a b. (Requestable a, Responsable b) => URL -> Maybe a -> Affjax e b +put' u c = affjax $ defaultRequest { method = PUT, url = u, content = c } + +-- | Makes a `PUT` request to the specified URL, sending data and ignoring the +-- | response. put_ :: forall e a. (Requestable a) => URL -> a -> Affjax e Unit put_ = put +-- | Makes a `PUT` request to the specified URL with the option to send data, +-- | and ignores the response. +put_' :: forall e a. (Requestable a) => URL -> Maybe a -> Affjax e Unit +put_' = put' + +-- | Makes a `DELETE` request to the specified URL. delete :: forall e a. (Responsable a) => URL -> Affjax e a delete u = affjax $ defaultRequest { method = DELETE, url = u } +-- | Makes a `DELETE` request to the specified URL and ignores the response. delete_ :: forall e. URL -> Affjax e Unit delete_ = delete