actn-db 0.0.1
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/.gitignore +17 -0
- data/.travis.yml +3 -0
- data/Gemfile +4 -0
- data/LICENSE.txt +22 -0
- data/README.md +29 -0
- data/Rakefile +12 -0
- data/actn-db.gemspec +30 -0
- data/db/1_db.sql +54 -0
- data/db/__functions.sql +472 -0
- data/db/__setup.sql +37 -0
- data/db/lib/_0_actn.js +23 -0
- data/db/lib/_1_underscore.js +1 -0
- data/db/lib/_2_jjv.js +739 -0
- data/db/lib/_3_inflections.js +634 -0
- data/db/lib/_4_builder.coffee +136 -0
- data/db/lib/_4_builder.js +218 -0
- data/db/schemas/model.json +76 -0
- data/lib/actn/core_ext/hash.rb +10 -0
- data/lib/actn/core_ext/kernel.rb +8 -0
- data/lib/actn/core_ext/string.rb +14 -0
- data/lib/actn/db.rb +24 -0
- data/lib/actn/db/mod.rb +175 -0
- data/lib/actn/db/model.rb +25 -0
- data/lib/actn/db/pg.rb +88 -0
- data/lib/actn/db/set.rb +67 -0
- data/lib/actn/db/tasks/db.rake +96 -0
- data/lib/actn/db/version.rb +5 -0
- data/lib/actn/paths.rb +38 -0
- data/test/actn/test_mod.rb +54 -0
- data/test/actn/test_model.rb +49 -0
- data/test/actn/test_pg_funcs.rb +71 -0
- data/test/actn/test_set.rb +57 -0
- data/test/minitest_helper.rb +18 -0
- metadata +208 -0
@@ -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
|
+
}
|