1 | var stream = require("obj-stream");
|
2 | var request = require("./request");
|
3 | var extend = require("xtend/mutable");
|
4 | var qs = require("querystring");
|
5 |
|
6 |
|
7 |
|
8 |
|
9 | var HTTPDatabase = function(options) {
|
10 | if (!options) options = {};
|
11 |
|
12 | this.options = options;
|
13 | this.request = options.request || request;
|
14 | this.idProperty = options.idProperty = options.idProperty || "uid";
|
15 | this.prefix = options.prefix || "";
|
16 |
|
17 | this.method = options.method || function(operation) {
|
18 | return {
|
19 | "insert" : "post",
|
20 | "update" : "put",
|
21 | "upsert" : operation.data && operation.data[operation.idProperty] ? "put" : "post",
|
22 | "load" : "get",
|
23 | "remove" : "del"
|
24 | }[operation.name];
|
25 | };
|
26 |
|
27 | this.path = function(operation) {
|
28 |
|
29 | var cpath = "/" + operation.collection;
|
30 | var mpath = operation.data ? cpath + "/" + operation.data[operation.idProperty] : "";
|
31 |
|
32 | return {
|
33 | "insert" : cpath,
|
34 | "update" : mpath,
|
35 | "upsert" : operation.data && operation.data[operation.idProperty] ? mpath : cpath,
|
36 | "load" : operation.multi ? cpath : mpath,
|
37 | "remove" : mpath
|
38 | }[operation.name];
|
39 | };
|
40 | };
|
41 |
|
42 |
|
43 |
|
44 |
|
45 | var _getters = {};
|
46 |
|
47 |
|
48 |
|
49 |
|
50 | function _get(target, keypath) {
|
51 | var getter = _getters[keypath];
|
52 | if (!getter) {
|
53 | getter = _getters[keypath] = new Function("target", "return target." + keypath);
|
54 | }
|
55 | try {
|
56 | return getter(target);
|
57 | } catch (e) { }
|
58 | }
|
59 |
|
60 |
|
61 |
|
62 |
|
63 | extend(HTTPDatabase.prototype, {
|
64 | run: function(operation, next) {
|
65 |
|
66 | var options = operation = extend({}, this.options, operation.http, operation);
|
67 |
|
68 | var method = options.method || this.method(operation);
|
69 |
|
70 | if (typeof method === "function") {
|
71 | method = method(options);
|
72 | }
|
73 |
|
74 | if (!method) return next();
|
75 |
|
76 | var path = options.path || this.path;
|
77 |
|
78 | if (typeof path === "function") {
|
79 | path = path.call(this, options);
|
80 | }
|
81 |
|
82 | path = path.replace(/:([^\/]+)/g, function(keypath) {
|
83 | return _get(options, keypath.substr(1));
|
84 | });
|
85 |
|
86 | var transform = options.transform || function(data) {
|
87 | return data;
|
88 | };
|
89 |
|
90 | var headers = options.headers;
|
91 | if (typeof headers === "function") {
|
92 | headers = headers.call(this, operation);
|
93 | }
|
94 |
|
95 | var query = options.query;
|
96 | if (typeof query === "function") {
|
97 | query = query(operation);
|
98 | }
|
99 |
|
100 | path = (options.prefix || this.prefix) + path;
|
101 |
|
102 | if (!~path.indexOf("://") && process.browser) {
|
103 | path = location.protocol + "//" + location.host + path;
|
104 | }
|
105 |
|
106 | var ops = {
|
107 | uri: path,
|
108 | query: query,
|
109 | method: method,
|
110 | headers: headers,
|
111 | form: options.form,
|
112 | data: options.data
|
113 | };
|
114 |
|
115 | this.request(ops, function(err, body) {
|
116 | if (err) return next(err);
|
117 | next(null, transform(body));
|
118 | });
|
119 | }
|
120 | });
|
121 |
|
122 |
|
123 |
|
124 |
|
125 | function _toArray(data) {
|
126 | if (data == void 0) return [];
|
127 | return Object.prototype.toString.call(data) === "[object Array]" ? data : [data];
|
128 | }
|
129 |
|
130 |
|
131 |
|
132 |
|
133 | module.exports = function(options) {
|
134 |
|
135 | var db = new HTTPDatabase(options);
|
136 |
|
137 | return function(operation) {
|
138 | var writable = stream.writable();
|
139 |
|
140 | process.nextTick(function() {
|
141 | var self = this;
|
142 | db.run(operation, function(err, data) {
|
143 | if (err) return writable.reader.emit("error");
|
144 | _toArray(data).forEach(writable.write.bind(writable));
|
145 | writable.end();
|
146 | });
|
147 | });
|
148 |
|
149 | return writable.reader;
|
150 | };
|
151 | };
|