actn-db 0.0.1

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,136 @@
1
+ class Builder
2
+
3
+ constructor: (@schema_name, @table_name, @search_path, @query) ->
4
+ @query.select ?= "*"
5
+ @params = []
6
+ @i = 0
7
+
8
+ qm: -> "$#{@i += 1}"
9
+
10
+ make_select: ->
11
+ if @query?.select?.indexOf('COUNT') > -1
12
+ @query.select
13
+ else
14
+ if @query.select is "*"
15
+ "data"
16
+ else
17
+ @params.push _.flatten([@query.select]).join(".")
18
+ "__select(data, #{@qm()}) as data" #::text
19
+
20
+
21
+ make_where: (q, join_by = 'AND') ->
22
+ sql = []
23
+ for k, subquery of q
24
+ switch k
25
+ when 'and', 'AND', '&', '&&'
26
+ sql.push "(#{make_where(subquery, 'AND')})"
27
+ when 'or', 'OR', '|', '||'
28
+ sql.push "(#{make_where(subquery, 'OR')})"
29
+ when 'not', 'NOT', '!'
30
+ sql.push "NOT (#{make_where(subquery, 'AND')})"
31
+ else
32
+ if _.isArray(subquery)
33
+ @params.push k
34
+ @params.push subquery[1]
35
+ sql.push "#{@plv8_key(subquery[1])} #{subquery[0]} #{@plv8_qm(subquery[1])}"
36
+ else if _.isObject(subquery)
37
+ comparisons = []
38
+ for symbol, value in subquery
39
+ comparisons.push "#{symbol} #{@plv8_qm(value)}"
40
+ @params.push k
41
+ @params.push value
42
+ sql.push _.map(comparisons, (comparison) -> "#{@plv8_key(value)} #{comparison}").join(" AND ")
43
+ else
44
+ @params.push k
45
+ @params.push subquery
46
+ sql.push "#{@plv8_key(subquery)} = #{@plv8_qm(subquery)}"
47
+
48
+ sql.join "\n#{join_by} "
49
+
50
+
51
+ make_order_by: () ->
52
+ ord = @query.order_by
53
+ str = []
54
+ if _.isArray(ord)
55
+ @params.push ord[0]
56
+ str.push "#{@plv8_key(ord[1])} #{ord[1].toUpperCase()}"
57
+ else if _.isObject(ord)
58
+ for k,v in ord
59
+ @params.push v
60
+ str.push "#{@plv8_key(k)} #{k.toUpperCase()}"
61
+ else
62
+ @params.push ord
63
+ str.push @qm()
64
+ str.join(",")
65
+
66
+
67
+ make_limit: ->
68
+ @params.push @query.limit
69
+ @qm()
70
+
71
+
72
+ make_offset: ->
73
+ @params.push @query.offset
74
+ @qm()
75
+
76
+
77
+ build_select: ->
78
+ sql = []
79
+ sql.push "SET search_path TO #{@search_path};"
80
+ sql.push "SELECT #{@make_select()} FROM #{@schema_name}.#{@table_name}"
81
+ sql.push "WHERE #{@make_where(@query.where)}" unless _.isEmpty(@query.where)
82
+ sql.push "ORDER BY #{@make_order()}" if @query.order_by?
83
+ sql.push "LIMIT #{@make_limit()}" if @query.limit?
84
+ sql.push "OFFSET #{@make_offset()}" if @query.offset?
85
+ [sql.join("\n"), @params]
86
+
87
+ build_delete: ->
88
+ sql = []
89
+ sql.push "SET search_path TO #{@search_path};"
90
+ sql.push "DELETE FROM #{@schema_name}.#{@table_name}"
91
+ sql.push "WHERE #{@make_where(@query.where)}" unless _.isEmpty(@query.where)
92
+ sql.push "RETURNING data::json;"
93
+ [sql.join("\n"), @params]
94
+
95
+ build_update: (data, merge = true) ->
96
+
97
+ @params.push data
98
+ @params.push merge
99
+ sql = []
100
+ sql.push "SET search_path TO #{@search_path};"
101
+ sql.push "UPDATE #{@schema_name}.#{@table_name} SET data = __patch(data,#{@qm()},#{@qm()})"
102
+ sql.push "WHERE #{@make_where(@query.where)}" unless _.isEmpty(@query.where)
103
+ sql.push "RETURNING data::json;"
104
+ [sql.join("\n"), @params]
105
+
106
+ build_insert: (data, merge = true) ->
107
+ @params.push data
108
+ @params.push merge
109
+ sql = []
110
+ sql.push "SET search_path TO #{@search_path};"
111
+ sql.push "INSERT INTO #{@schema_name}.#{@table_name} (data) VALUES (__patch(__defaults(),#{@qm()},#{@qm()}))"
112
+ sql.push "RETURNING data::json;"
113
+ [sql.join("\n"), @params]
114
+
115
+ plv8_key: (value) -> "#{@typecast(value,true)}(data, #{@qm()}::text)" #::text
116
+
117
+ plv8_qm: (value) -> "#{@qm()}::#{@typecast(value)}"
118
+
119
+ typecast: (value, is_func = false) ->
120
+ type = if is_func then "__" else ""
121
+ if _.isBoolean(value)
122
+ type += "bool"
123
+ else if _.isDate(value)
124
+ type += "timestamp"
125
+ else if _.isNumber(value)
126
+ type += "integer"
127
+ else if _.isObject(value)
128
+ type += (if is_func then "text" else "json")
129
+ else if _.isArray(value)
130
+ type += (if is_func then "text" else "array")
131
+ else
132
+ type += (if is_func then "string" else "text")
133
+ type
134
+
135
+
136
+ global.actn.Builder = Builder
@@ -0,0 +1,218 @@
1
+ // Generated by CoffeeScript 1.6.3
2
+ (function() {
3
+ var Builder;
4
+
5
+ Builder = (function() {
6
+ function Builder(schema_name, table_name, search_path, query) {
7
+ var _base;
8
+ this.schema_name = schema_name;
9
+ this.table_name = table_name;
10
+ this.search_path = search_path;
11
+ this.query = query;
12
+ if ((_base = this.query).select == null) {
13
+ _base.select = "*";
14
+ }
15
+ this.params = [];
16
+ this.i = 0;
17
+ }
18
+
19
+ Builder.prototype.qm = function() {
20
+ return "$" + (this.i += 1);
21
+ };
22
+
23
+ Builder.prototype.make_select = function() {
24
+ var _ref, _ref1;
25
+ if (((_ref = this.query) != null ? (_ref1 = _ref.select) != null ? _ref1.indexOf('COUNT') : void 0 : void 0) > -1) {
26
+ return this.query.select;
27
+ } else {
28
+ if (this.query.select === "*") {
29
+ return "data";
30
+ } else {
31
+ this.params.push(_.flatten([this.query.select]).join("."));
32
+ return "__select(data, " + (this.qm()) + ") as data";
33
+ }
34
+ }
35
+ };
36
+
37
+ Builder.prototype.make_where = function(q, join_by) {
38
+ var comparisons, k, sql, subquery, symbol, value, _i, _len;
39
+ if (join_by == null) {
40
+ join_by = 'AND';
41
+ }
42
+ sql = [];
43
+ for (k in q) {
44
+ subquery = q[k];
45
+ switch (k) {
46
+ case 'and':
47
+ case 'AND':
48
+ case '&':
49
+ case '&&':
50
+ sql.push("(" + (make_where(subquery, 'AND')) + ")");
51
+ break;
52
+ case 'or':
53
+ case 'OR':
54
+ case '|':
55
+ case '||':
56
+ sql.push("(" + (make_where(subquery, 'OR')) + ")");
57
+ break;
58
+ case 'not':
59
+ case 'NOT':
60
+ case '!':
61
+ sql.push("NOT (" + (make_where(subquery, 'AND')) + ")");
62
+ break;
63
+ default:
64
+ if (_.isArray(subquery)) {
65
+ this.params.push(k);
66
+ this.params.push(subquery[1]);
67
+ sql.push("" + (this.plv8_key(subquery[1])) + " " + subquery[0] + " " + (this.plv8_qm(subquery[1])));
68
+ } else if (_.isObject(subquery)) {
69
+ comparisons = [];
70
+ for (value = _i = 0, _len = subquery.length; _i < _len; value = ++_i) {
71
+ symbol = subquery[value];
72
+ comparisons.push("" + symbol + " " + (this.plv8_qm(value)));
73
+ this.params.push(k);
74
+ this.params.push(value);
75
+ }
76
+ sql.push(_.map(comparisons, function(comparison) {
77
+ return "" + (this.plv8_key(value)) + " " + comparison;
78
+ }).join(" AND "));
79
+ } else {
80
+ this.params.push(k);
81
+ this.params.push(subquery);
82
+ sql.push("" + (this.plv8_key(subquery)) + " = " + (this.plv8_qm(subquery)));
83
+ }
84
+ }
85
+ }
86
+ return sql.join("\n" + join_by + " ");
87
+ };
88
+
89
+ Builder.prototype.make_order_by = function() {
90
+ var k, ord, str, v, _i, _len;
91
+ ord = this.query.order_by;
92
+ str = [];
93
+ if (_.isArray(ord)) {
94
+ this.params.push(ord[0]);
95
+ str.push("" + (this.plv8_key(ord[1])) + " " + (ord[1].toUpperCase()));
96
+ } else if (_.isObject(ord)) {
97
+ for (v = _i = 0, _len = ord.length; _i < _len; v = ++_i) {
98
+ k = ord[v];
99
+ this.params.push(v);
100
+ str.push("" + (this.plv8_key(k)) + " " + (k.toUpperCase()));
101
+ }
102
+ } else {
103
+ this.params.push(ord);
104
+ str.push(this.qm());
105
+ }
106
+ return str.join(",");
107
+ };
108
+
109
+ Builder.prototype.make_limit = function() {
110
+ this.params.push(this.query.limit);
111
+ return this.qm();
112
+ };
113
+
114
+ Builder.prototype.make_offset = function() {
115
+ this.params.push(this.query.offset);
116
+ return this.qm();
117
+ };
118
+
119
+ Builder.prototype.build_select = function() {
120
+ var sql;
121
+ sql = [];
122
+ sql.push("SET search_path TO " + this.search_path + ";");
123
+ sql.push("SELECT " + (this.make_select()) + " FROM " + this.schema_name + "." + this.table_name);
124
+ if (!_.isEmpty(this.query.where)) {
125
+ sql.push("WHERE " + (this.make_where(this.query.where)));
126
+ }
127
+ if (this.query.order_by != null) {
128
+ sql.push("ORDER BY " + (this.make_order()));
129
+ }
130
+ if (this.query.limit != null) {
131
+ sql.push("LIMIT " + (this.make_limit()));
132
+ }
133
+ if (this.query.offset != null) {
134
+ sql.push("OFFSET " + (this.make_offset()));
135
+ }
136
+ return [sql.join("\n"), this.params];
137
+ };
138
+
139
+ Builder.prototype.build_delete = function() {
140
+ var sql;
141
+ sql = [];
142
+ sql.push("SET search_path TO " + this.search_path + ";");
143
+ sql.push("DELETE FROM " + this.schema_name + "." + this.table_name);
144
+ if (!_.isEmpty(this.query.where)) {
145
+ sql.push("WHERE " + (this.make_where(this.query.where)));
146
+ }
147
+ sql.push("RETURNING data::json;");
148
+ return [sql.join("\n"), this.params];
149
+ };
150
+
151
+ Builder.prototype.build_update = function(data, merge) {
152
+ var sql;
153
+ if (merge == null) {
154
+ merge = true;
155
+ }
156
+ this.params.push(data);
157
+ this.params.push(merge);
158
+ sql = [];
159
+ sql.push("SET search_path TO " + this.search_path + ";");
160
+ sql.push("UPDATE " + this.schema_name + "." + this.table_name + " SET data = __patch(data," + (this.qm()) + "," + (this.qm()) + ")");
161
+ if (!_.isEmpty(this.query.where)) {
162
+ sql.push("WHERE " + (this.make_where(this.query.where)));
163
+ }
164
+ sql.push("RETURNING data::json;");
165
+ return [sql.join("\n"), this.params];
166
+ };
167
+
168
+ Builder.prototype.build_insert = function(data, merge) {
169
+ var sql;
170
+ if (merge == null) {
171
+ merge = true;
172
+ }
173
+ this.params.push(data);
174
+ this.params.push(merge);
175
+ sql = [];
176
+ sql.push("SET search_path TO " + this.search_path + ";");
177
+ sql.push("INSERT INTO " + this.schema_name + "." + this.table_name + " (data) VALUES (__patch(__defaults()," + (this.qm()) + "," + (this.qm()) + "))");
178
+ sql.push("RETURNING data::json;");
179
+ return [sql.join("\n"), this.params];
180
+ };
181
+
182
+ Builder.prototype.plv8_key = function(value) {
183
+ return "" + (this.typecast(value, true)) + "(data, " + (this.qm()) + "::text)";
184
+ };
185
+
186
+ Builder.prototype.plv8_qm = function(value) {
187
+ return "" + (this.qm()) + "::" + (this.typecast(value));
188
+ };
189
+
190
+ Builder.prototype.typecast = function(value, is_func) {
191
+ var type;
192
+ if (is_func == null) {
193
+ is_func = false;
194
+ }
195
+ type = is_func ? "__" : "";
196
+ if (_.isBoolean(value)) {
197
+ type += "bool";
198
+ } else if (_.isDate(value)) {
199
+ type += "timestamp";
200
+ } else if (_.isNumber(value)) {
201
+ type += "integer";
202
+ } else if (_.isObject(value)) {
203
+ type += (is_func ? "text" : "json");
204
+ } else if (_.isArray(value)) {
205
+ type += (is_func ? "text" : "array");
206
+ } else {
207
+ type += (is_func ? "string" : "text");
208
+ }
209
+ return type;
210
+ };
211
+
212
+ return Builder;
213
+
214
+ })();
215
+
216
+ global.actn.Builder = Builder;
217
+
218
+ }).call(this);
@@ -0,0 +1,76 @@
1
+ {
2
+ "$schema": "http://json-schema.org/draft-04/schema",
3
+ "type": "object",
4
+ "properties": {
5
+ "uuid": {
6
+ "id": "#uuid",
7
+ "description": "Universal accessible name of the model, cannot be change after created"
8
+ },
9
+ "name": {
10
+ "id": "#name",
11
+ "description": "Universal accessible name of the model, cannot be change after created",
12
+ "type": "string"
13
+ },
14
+ "schema": {
15
+ "id": "#schema"
16
+ },
17
+ "indexes" : {
18
+ "id": "#indexes",
19
+ "description": "Database index definitions",
20
+ "type": "array",
21
+ "items": {
22
+ "type": "object",
23
+ "properties": {
24
+ "cols": {
25
+ "type": "object",
26
+ "patternProperties": {
27
+ ".{1,}": {
28
+ "type": "string"
29
+ }
30
+ }
31
+ },
32
+ "unique": { "type": "boolean" },
33
+ "concurrently": { "type": "boolean" }
34
+ },
35
+ "required": ["cols"]
36
+ },
37
+ "minItems": 1,
38
+ "maxItems": 10
39
+ },
40
+ "hooks": {
41
+ "type": "object",
42
+ "properties": {
43
+ "$ref": "#/definitions/hook"
44
+ }
45
+ }
46
+ },
47
+ "required": ["name"],
48
+ "readonly_attributes": ["name"],
49
+ "unique_attributes": ["name"],
50
+ "definitions": {
51
+ "hook":{
52
+ "id": "#hook",
53
+ "description": "Async jobs for after callbacks",
54
+ "type": "object",
55
+ "patternProperties": {
56
+ "after_[create|update|destroy]" : {
57
+ "type": "array",
58
+ "items": {
59
+ "type" : "object",
60
+ "properties": {
61
+ "name": { "type": "string" },
62
+ "run_at": { "type": "date" },
63
+ "conditions": {
64
+ "type": "array",
65
+ "items": { "type": "string" }
66
+ }
67
+ },
68
+ "required": ["name"]
69
+ },
70
+ "minItems": 1,
71
+ "maxItems": 20
72
+ }
73
+ }
74
+ }
75
+ }
76
+ }
@@ -0,0 +1,10 @@
1
+ require 'oj'
2
+
3
+ class ::Hash
4
+ def to_json
5
+ Oj.dump(self)
6
+ end
7
+ def as_json
8
+ self
9
+ end
10
+ end
@@ -0,0 +1,8 @@
1
+ module ::Kernel
2
+
3
+ def called_from(level=1)
4
+ arrs = caller((level||1)+1) or return
5
+ arrs[0] =~ /:(\d+)(?::in `(.*)')?/ ? [$`, $1.to_i, $2] : nil
6
+ end
7
+
8
+ end