├── .gitignore ├── .npmignore ├── LICENSE-Apache2 ├── .travis.yml ├── lib ├── hoax.js ├── hoax-browserify.js └── hoax-core.js ├── grunt.js ├── README.md ├── package.json └── test └── hoax_test.js /.gitignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | -------------------------------------------------------------------------------- /.npmignore: -------------------------------------------------------------------------------- 1 | /node_modules/ 2 | -------------------------------------------------------------------------------- /LICENSE-Apache2: -------------------------------------------------------------------------------- 1 | What is this I don’t even -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: node_js 2 | node_js: 3 | - 0.8 4 | - 0.6 5 | -------------------------------------------------------------------------------- /lib/hoax.js: -------------------------------------------------------------------------------- 1 | var core = require("./hoax-core"), 2 | request = require("request"); 3 | 4 | module.exports = core(request); 5 | -------------------------------------------------------------------------------- /lib/hoax-browserify.js: -------------------------------------------------------------------------------- 1 | var core = require("./hoax-core"), 2 | request = require("browser-request"); 3 | 4 | request.log.debug = function() {}; 5 | 6 | module.exports = core(request); 7 | -------------------------------------------------------------------------------- /grunt.js: -------------------------------------------------------------------------------- 1 | module.exports = function(grunt) { 2 | 3 | // Project configuration. 4 | grunt.initConfig({ 5 | pkg: '', 6 | test: { 7 | files: ['test/**/*.js'] 8 | }, 9 | lint: { 10 | files: ['grunt.js', 'lib/**/*.js', 'test/**/*.js'] 11 | }, 12 | watch: { 13 | files: '', 14 | tasks: 'default' 15 | }, 16 | jshint: { 17 | options: { 18 | curly: true, 19 | eqeqeq: true, 20 | immed: true, 21 | latedef: true, 22 | newcap: true, 23 | noarg: true, 24 | sub: true, 25 | undef: true, 26 | boss: true, 27 | eqnull: true, 28 | node: true 29 | }, 30 | globals: { 31 | exports: true 32 | } 33 | } 34 | }); 35 | 36 | // Default task. 37 | grunt.registerTask('default', 'lint test'); 38 | 39 | }; -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # hoax 2 | 3 | JSON HTTP client using [pax](https://npmjs.org/package/pax) for path currying and request for HTTP. 4 | 5 | Used by [coax](https://npmjs.org/package/coax) 6 | 7 | [![Build Status](https://travis-ci.org/jchris/hoax.png?branch=master)](https://travis-ci.org/jchris/hoax) 8 | 9 | ## Getting Started 10 | Install the module with: `npm install hoax` 11 | 12 | ```javascript 13 | var hoax = require('hoax'); 14 | 15 | hoax(["http://localhost:3001/","very","awesome"], function(err, json){ 16 | console.log("fetched", json) 17 | }); 18 | 19 | // currying 20 | var server = hoax("http://localhost:3001/"), 21 | path = server("very", {query : "params"}); 22 | 23 | // put JSON to "http://localhost:3001/very/awesome?query=params" 24 | path.put("awesome", {some:"data"}, function(err, json){ 25 | console.log("put json", json); 26 | }); 27 | 28 | ``` 29 | 30 | ## License 31 | Copyright (c) 2013 Chris Anderson 32 | Licensed under the Apache license. 33 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "hoax", 3 | "description": "Restful JSON client using pax for path currying and request for HTTP.", 4 | "version": "0.2.11", 5 | "homepage": "https://github.com/jchris/hoax", 6 | "author": { 7 | "name": "Chris Anderson", 8 | "email": "jchris@couchbase.com", 9 | "url": "http://www.couchbase.com" 10 | }, 11 | "repository": { 12 | "type": "git", 13 | "url": "git://github.com/jchris/hoax.git" 14 | }, 15 | "bugs": { 16 | "url": "https://github.com/jchris/hoax/issues" 17 | }, 18 | "licenses": [ 19 | { 20 | "type": "Apache", 21 | "url": "https://github.com/jchris/hoax/blob/master/LICENSE-Apache" 22 | } 23 | ], 24 | "main": "lib/hoax", 25 | "browserify": "lib/hoax-browserify", 26 | "engines": { 27 | "node": "~0.*" 28 | }, 29 | "scripts": { 30 | "test": "grunt test" 31 | }, 32 | "dependencies": { 33 | "pax" : "0.2", 34 | "request" : "2.12", 35 | "browser-request" : "0.3" 36 | }, 37 | "devDependencies": { 38 | "grunt": "~0.3.6" 39 | }, 40 | "keywords": [] 41 | } 42 | -------------------------------------------------------------------------------- /lib/hoax-core.js: -------------------------------------------------------------------------------- 1 | /* 2 | * hoax 3 | * https://github.com/jchris/hoax 4 | * 5 | * Copyright (c) 2013 Chris Anderson 6 | * Licensed under the Apache license. 7 | */ 8 | 9 | module.exports = function(request) { 10 | var pax = require("pax"); 11 | 12 | function makeHoaxCallback(cb, verb) { 13 | return function(err, res, body){ 14 | // console.log("hoax cb", verb||"get", err, res.statusCode, body); 15 | if (err && err !== "error") { 16 | cb(err, res, body); 17 | } else { 18 | if (res.statusCode >= 400 || err === "error") { 19 | cb(body || res.statusCode, res); 20 | } else { 21 | cb(null, body); 22 | } 23 | } 24 | }; 25 | } 26 | 27 | function processArguments(myPax, urlOrOpts, data, cb, verb) { 28 | var opts = {}, newPax = myPax; 29 | if (typeof urlOrOpts === 'function') { 30 | cb = urlOrOpts; 31 | data = null; 32 | urlOrOpts = null; 33 | } else { 34 | if (urlOrOpts.uri || urlOrOpts.url) { 35 | newPax = myPax(urlOrOpts.uri || urlOrOpts.url); 36 | } else { 37 | if (typeof data === 'function') { 38 | // we have only 2 args 39 | // the first is data if it is not an array 40 | // and the verb is put or post 41 | cb = data; 42 | data = null; 43 | if ((verb === "put" || verb === "post") && 44 | (typeof urlOrOpts !== "string" && 45 | Object.prototype.toString.call(urlOrOpts) !== '[object Array]')) { 46 | data = urlOrOpts; 47 | } else { 48 | newPax = myPax(urlOrOpts); 49 | } 50 | } else { 51 | newPax = myPax(urlOrOpts); 52 | } 53 | } 54 | } 55 | opts.headers = {'content-type': 'application/json'}; 56 | opts.json = true; 57 | opts.uri = newPax.toString(); 58 | if (data) { 59 | opts.body = JSON.stringify(data); 60 | } 61 | return [opts, cb, newPax]; 62 | } 63 | 64 | function extenderizer(oldHoax) { 65 | return function(name, fun) { 66 | this.methods = this.methods || {}; 67 | this.methods[name] = fun; 68 | this[name] = fun; 69 | }; 70 | } 71 | 72 | function addExtensions(newHoax, oldHoax) { 73 | if (oldHoax && oldHoax.methods) { 74 | var k; 75 | for (k in oldHoax.methods) { 76 | newHoax[k] = oldHoax.methods[k]; 77 | } 78 | } 79 | } 80 | 81 | function makeHoax(myPax, verb, oldHoax) { 82 | var newHoax = function(opts, data, xcb) { 83 | var args = processArguments(myPax, opts, data, xcb, verb), 84 | reqOpts = args[0], // includes uri, body 85 | cb = args[1], 86 | newPax = args[2]; 87 | if (cb) { 88 | // console.log(["hoax", verb||"get", reqOpts]); 89 | if (verb) { 90 | if (verb == "del") { 91 | reqOpts.method = "DELETE"; 92 | } else { 93 | reqOpts.method = verb.toUpperCase(); 94 | } 95 | return request(reqOpts, makeHoaxCallback(cb, verb)); 96 | } else { 97 | return request(reqOpts, makeHoaxCallback(cb)); 98 | } 99 | } else { 100 | // console.log("new hoax", newPax); 101 | return makeHoax(newPax, verb, newHoax); 102 | } 103 | }; 104 | if (!verb) { 105 | "get put post head del".split(" ").forEach(function(v){ 106 | newHoax[v] = makeHoax(myPax, v, newHoax); 107 | }); 108 | } 109 | addExtensions(newHoax, oldHoax); 110 | // should this be extenderizer(newHoax) ? 111 | newHoax.extend = extenderizer(oldHoax); 112 | newHoax.pax = myPax; // deprecated 113 | newHoax.url = myPax; 114 | return newHoax; 115 | } 116 | 117 | var Hoax = makeHoax(pax()); 118 | Hoax.makeHoax = makeHoax; 119 | 120 | return Hoax; 121 | }; 122 | -------------------------------------------------------------------------------- /test/hoax_test.js: -------------------------------------------------------------------------------- 1 | var hoax = require('../lib/hoax.js'), 2 | request = require("request"); 3 | 4 | var http = require("http"), url = require("url"); 5 | 6 | var handlers = {}; 7 | 8 | var testServer = function() { 9 | http.createServer(function(req, res){ 10 | // your custom error-handling logic: 11 | function error(status, err) { 12 | res.statusCode = status || 500; 13 | res.end(err.toString()); 14 | } 15 | var path = url.parse(req.url).pathname; 16 | console.log("test server", req.method, req.url); 17 | if (handlers[path]) { 18 | handlers[path](req, res); 19 | } else { 20 | error(404, "no handler for "+path); 21 | } 22 | }).listen(3001); 23 | }; 24 | 25 | testServer(); 26 | 27 | exports['/awesome'] = { 28 | setUp: function(done) { 29 | // setup here 30 | handlers['/awesome'] = function(req, res) { 31 | res.statusCode = 200; 32 | res.end(JSON.stringify({awesome:true})); 33 | }; 34 | handlers['/very/awesome'] = function(req, res) { 35 | var body = ""; 36 | req.on("data", function(data) {body = body + data;}); 37 | req.on("end", function(){ 38 | res.statusCode = 200; 39 | res.end(JSON.stringify({url:req.url,awesome:true, method : req.method, body:body})); 40 | }); 41 | }; 42 | handlers['/very/awesome/coat'] = function(req, res) { 43 | res.statusCode = 200; 44 | res.end(JSON.stringify({coat:true, method : req.method})); 45 | }; 46 | done(); 47 | }, 48 | '200 get': function(test) { 49 | test.expect(2); 50 | // tests here 51 | hoax("http://localhost:3001/awesome", function(err, json){ 52 | // console.log(ok.statusCode, body); 53 | test.equal(err, null); 54 | test.equal(json.awesome, true, 'should be awesome.'); 55 | test.done(); 56 | }); 57 | }, 58 | '200 array' : function(test) { 59 | // test.expect() 60 | hoax(["http://localhost:3001/","very","awesome"], function(err, json){ 61 | test.equal(err, null); 62 | test.equal(json.awesome, true, 'should be awesome.'); 63 | test.equal(json.method, 'GET', 'should be get.'); 64 | test.done(); 65 | }); 66 | }, 67 | '200 put JSON' : function(test) { 68 | // test.expect() 69 | hoax.put("http://localhost:3001/very/awesome", {my:"data"}, function(err, json){ 70 | test.equal(err, null); 71 | test.equal(json.body, '{"my":"data"}', 'should be json.'); 72 | test.equal(json.awesome, true, 'should be awesome.'); 73 | test.equal(json.method, 'PUT', 'should be put.'); 74 | test.done(); 75 | }); 76 | }, 77 | '200 array put' : function(test) { 78 | // test.expect() 79 | hoax.put(["http://localhost:3001/","very","awesome"], {my:"data"}, function(err, json){ 80 | test.equal(err, null); 81 | test.equal(json.body, '{"my":"data"}', 'should be json.'); 82 | test.equal(json.awesome, true, 'should be awesome.'); 83 | test.equal(json.method, 'PUT', 'should be put.'); 84 | test.done(); 85 | }); 86 | }, 87 | '200 post' : function(test) { 88 | test.expect(3); 89 | hoax.post("http://localhost:3001/very/awesome", function(err, json){ 90 | test.equal(err, null); 91 | test.equal(json.awesome, true, 'should be awesome.'); 92 | test.equal(json.method, 'POST', 'should be put.'); 93 | test.done(); 94 | }); 95 | }, 96 | '200 array curry' : function(test) { 97 | // test.expect() 98 | var host = hoax("http://localhost:3001/"), 99 | resource = host(["very","awesome"]); 100 | resource("coat", function(err, json){ 101 | test.equal(err, null); 102 | test.equal(json.coat, true, 'should be coat.'); 103 | test.equal(json.method, 'GET', 'should be get.'); 104 | test.done(); 105 | }); 106 | }, 107 | '200 array no path' : function(test) { 108 | // test.expect() 109 | var host = hoax("http://localhost:3001/"), 110 | resource = host(["very","awesome"]); 111 | resource(function(err, json){ 112 | test.equal(err, null); 113 | test.equal(json.awesome, true, 'should be awesome.'); 114 | test.equal(json.method, 'GET', 'should be get.'); 115 | test.done(); 116 | }); 117 | }, 118 | '200 array curry put' : function(test) { 119 | // test.expect() 120 | var host = hoax("http://localhost:3001/"), 121 | resource = host(["very","awesome"]); 122 | resource.put(function(err, json){ 123 | test.equal(err, null); 124 | test.equal(json.awesome, true, 'should be awesome.'); 125 | test.equal(json.method, 'PUT', 'should be put.'); 126 | test.done(); 127 | }); 128 | }, 129 | '200 array curry post' : function(test) { 130 | // test.expect() 131 | var host = hoax("http://localhost:3001/"), 132 | resource = host(["very","awesome"]); 133 | resource.post("", {myjson:"safe"}, function(err, json){ 134 | test.equal(err, null); 135 | test.equal(json.body, '{"myjson":"safe"}', 'should be json.'); 136 | test.equal(json.awesome, true, 'should be awesome.'); 137 | test.equal(json.method, 'POST', 'should be put.'); 138 | test.done(); 139 | }); 140 | }, 141 | 'put curry' : function(test) { 142 | // test.expect() 143 | var host = hoax("http://localhost:3001/"), 144 | resource = host.put(["very","awesome"]); 145 | resource("coat",function(err, json){ 146 | test.equal(err, null); 147 | test.equal(json.coat, true, 'should be awesome.'); 148 | test.equal(json.method, 'PUT', 'should be put.'); 149 | test.done(); 150 | }); 151 | } 152 | }; 153 | 154 | var query = hoax("http://localhost:3001/very/awesome"); 155 | exports['/query'] = { 156 | setUp: function(done) { 157 | // setup here 158 | done(); 159 | }, 160 | 'get': function(test) { 161 | // test.expect(2); 162 | // tests here 163 | query({myquery:"exciting"}, function(err, json){ 164 | // console.log(ok.statusCode, body); 165 | test.equal(err, null); 166 | test.ok(json.url, 'should have query'); 167 | // test.ok(json.query.myquery, 'should have query'); 168 | test.deepEqual(json.url, "/very/awesome?myquery=exciting", 'should be "exciting".'); 169 | test.done(); 170 | }); 171 | }, 172 | 'put body': function(test) { 173 | // test.expect(2); 174 | // tests here 175 | query.put({myquery:"exciting"}, function(err, json){ 176 | // console.log(ok.statusCode, body); 177 | test.equal(err, null); 178 | test.ok(json.url, 'should have query'); 179 | // test.ok(json.query.myquery, 'should have query'); 180 | test.deepEqual(json.url, "/very/awesome", 'should be boring.'); 181 | test.deepEqual(json.body, '{"myquery":"exciting"}', 'should be in the body.'); 182 | test.done(); 183 | }); 184 | }, 185 | 'put array query w/ body': function(test) { 186 | // test.expect(2); 187 | // tests here 188 | query.put([{myquery:"exciting"}], {"lo":"el"}, function(err, json){ 189 | // console.log(ok.statusCode, body); 190 | test.equal(err, null); 191 | test.ok(json.url, 'should have query'); 192 | test.deepEqual(json.body, '{"lo":"el"}', 'should be in the body.'); 193 | test.deepEqual(json.url, "/very/awesome?myquery=exciting", 'should be "exciting".'); 194 | test.done(); 195 | }); 196 | }, 197 | 'put query w/ body': function(test) { 198 | // test.expect(2); 199 | // tests here 200 | query.put({myquery:"exciting"}, {"lo":"el"}, function(err, json){ 201 | // console.log(ok.statusCode, body); 202 | test.equal(err, null); 203 | test.ok(json.url, 'should have query'); 204 | test.deepEqual(json.body, '{"lo":"el"}', 'should be in the body.'); 205 | test.deepEqual(json.url, "/very/awesome?myquery=exciting", 'should be "exciting".'); 206 | test.done(); 207 | }); 208 | }, 209 | 'put query no body': function(test) { 210 | // test.expect(2); 211 | // tests here 212 | query.put([{myquery:"exciting"}], function(err, json){ 213 | // console.log(ok.statusCode, body); 214 | test.equal(err, null); 215 | test.ok(json.url, 'should have query'); 216 | // test.ok(json.query.myquery, 'should have query'); 217 | test.deepEqual(json.url, "/very/awesome?myquery=exciting", 'should be "exciting".'); 218 | test.done(); 219 | }); 220 | } 221 | }; 222 | 223 | var base = hoax(["http://localhost:3001/very/awesome", {param : "yeah"}]); 224 | exports['can curry params'] = { 225 | 'merges right': function(test) { 226 | test.expect(1); 227 | base({more : "yep"}, function(err, ok) { 228 | test.equal(ok.url, '/very/awesome?param=yeah&more=yep'); 229 | test.done(); 230 | }); 231 | } 232 | }; 233 | 234 | exports['/error'] = { 235 | setUp: function(done) { 236 | // setup here 237 | handlers['/error'] = function(req, res) { 238 | res.statusCode = 404; 239 | res.end(JSON.stringify({error:true})); 240 | }; 241 | done(); 242 | }, 243 | '404': function(test) { 244 | // test.expect(2); 245 | // tests here 246 | hoax("http://localhost:3001/error?status=404", 247 | function(errJSON, res){ 248 | // console.log(ok.statusCode, body); 249 | test.equal(res.statusCode, 404, 'response is second argument on error'); 250 | test.equal(errJSON.error, true, 'error should be body when parsed json and error code'); 251 | test.done(); 252 | }); 253 | } 254 | }; 255 | 256 | var db; 257 | exports['extensions'] = { 258 | setUp: function(done) { 259 | // setup here 260 | handlers['/extensions'] = function(req, res) { 261 | var body = ""; 262 | req.on("data", function(data) {body = body + data;}); 263 | req.on("end", function(){ 264 | res.statusCode = 200; 265 | res.end(JSON.stringify({url:req.url,awesome:true, method : req.method, body:body})); 266 | }); 267 | }; 268 | 269 | db = hoax(["http://localhost:3001/extensions", {myKey : "valuable"}]); 270 | db.extend('mymethod', function(){return 'ok';}); 271 | db.extend('usethis', function(cb){this([{ice:"cream"}],cb);}); 272 | done(); 273 | }, 274 | 'is callable': function(test) { 275 | test.expect(1); 276 | // tests here 277 | test.equal(db.mymethod(), 'ok'); 278 | test.done(); 279 | }, 280 | 'has working this': function(test) { 281 | test.expect(1); 282 | // tests here 283 | db.usethis(function(err, ok){ 284 | test.equal(ok.url, '/extensions?myKey=valuable&ice=cream'); 285 | test.done(); 286 | }); 287 | }, 288 | 'is inherited': function(test) { 289 | test.expect(1); 290 | // tests here 291 | var doc = db("deeper"); 292 | test.equal(doc.mymethod(), 'ok'); 293 | test.done(); 294 | } 295 | }; 296 | 297 | 298 | --------------------------------------------------------------------------------