blazer 1.7.1 → 1.7.2
Sign up to get free protection for your applications and to get access to all the features.
Potentially problematic release.
This version of blazer might be problematic. Click here for more details.
- checksums.yaml +4 -4
- data/CHANGELOG.md +7 -0
- data/README.md +14 -1
- data/app/assets/javascripts/blazer/application.js +83 -192
- data/app/assets/javascripts/blazer/queries.js +107 -0
- data/app/controllers/blazer/base_controller.rb +17 -10
- data/app/controllers/blazer/queries_controller.rb +1 -0
- data/app/models/blazer/query.rb +3 -1
- data/app/views/blazer/queries/_form.html.erb +1 -1
- data/app/views/blazer/queries/home.html.erb +3 -3
- data/app/views/blazer/queries/run.html.erb +3 -3
- data/blazer.gemspec +1 -2
- data/lib/blazer.rb +1 -0
- data/lib/blazer/adapters/base_adapter.rb +4 -0
- data/lib/blazer/adapters/sql_adapter.rb +5 -1
- data/lib/blazer/data_source.rb +1 -1
- data/lib/blazer/result.rb +15 -5
- data/lib/blazer/version.rb +1 -1
- metadata +6 -4
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 9d8af782cbd8374eb05295ae7fbd31766198adb1
|
4
|
+
data.tar.gz: f825ed76228ddf5eee748c9f48f252a358b206d3
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 8010c7ef1b87607b5d1c19980358c615fec43e711c1d50fd621b052efaa64c5bba165032f006a4a47b299680431d91ae84b48bd7bdb873de1519ca5827a7b32f
|
7
|
+
data.tar.gz: 84fffc2f2ea73319dc98df13ee7798b8d5411f1b44427a46db58d4a5cba2bf1896f759ef804ee925e253231cc8c1038f71759d16e2db62301bbb8159422d573b
|
data/CHANGELOG.md
CHANGED
@@ -1,3 +1,10 @@
|
|
1
|
+
## 1.7.2
|
2
|
+
|
3
|
+
- Cancel all queries on page nav
|
4
|
+
- Prevent Ace from taking over find command
|
5
|
+
- Added ability to use hashes for smart columns
|
6
|
+
- Added ability to inherit smart variables and columns from other data sources
|
7
|
+
|
1
8
|
## 1.7.1
|
2
9
|
|
3
10
|
- Do not fork when enter key pressed
|
data/README.md
CHANGED
@@ -228,6 +228,13 @@ smart_columns:
|
|
228
228
|
city_id: "SELECT id, name FROM cities WHERE id IN {value}"
|
229
229
|
```
|
230
230
|
|
231
|
+
You can also use a hash for static data and enums.
|
232
|
+
|
233
|
+
```yml
|
234
|
+
smart_columns:
|
235
|
+
status: {0: "Active", 1: "Archived"}
|
236
|
+
```
|
237
|
+
|
231
238
|
### Caching
|
232
239
|
|
233
240
|
Blazer can automatically cache results to improve speed. It can cache slow queries:
|
@@ -421,7 +428,13 @@ Use [ibm_db](https://github.com/ibmdb/ruby-ibmdb).
|
|
421
428
|
|
422
429
|
### SQLite
|
423
430
|
|
424
|
-
|
431
|
+
Add [sqlite3](https://github.com/sparklemotion/sqlite3-ruby) to your Gemfile and set:
|
432
|
+
|
433
|
+
```yml
|
434
|
+
data_sources:
|
435
|
+
my_source:
|
436
|
+
url: sqlite3:path/to/database.sqlite3
|
437
|
+
```
|
425
438
|
|
426
439
|
### Redshift
|
427
440
|
|
@@ -14,131 +14,31 @@
|
|
14
14
|
//= require ./bootstrap
|
15
15
|
//= require ./vue
|
16
16
|
//= require ./routes
|
17
|
+
//= require ./queries
|
18
|
+
|
19
|
+
Vue.config.devtools = false
|
17
20
|
|
18
21
|
$(document).on('mouseenter', '.dropdown-toggle', function () {
|
19
|
-
$(this).parent().addClass('open')
|
20
|
-
})
|
22
|
+
$(this).parent().addClass('open')
|
23
|
+
})
|
21
24
|
|
22
25
|
$(document).on("change", "#bind input, #bind select", function () {
|
23
|
-
submitIfCompleted($(this).closest("form"))
|
24
|
-
})
|
26
|
+
submitIfCompleted($(this).closest("form"))
|
27
|
+
})
|
25
28
|
|
26
29
|
$(document).on("click", "#code", function () {
|
27
|
-
$(this).addClass("expanded")
|
28
|
-
})
|
29
|
-
|
30
|
-
function uuid() {
|
31
|
-
return 'xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx'.replace(/[xy]/g, function(c) {
|
32
|
-
var r = Math.random()*16|0, v = c == 'x' ? r : (r&0x3|0x8);
|
33
|
-
return v.toString(16);
|
34
|
-
});
|
35
|
-
}
|
36
|
-
|
37
|
-
function cancelQuery(runningQuery) {
|
38
|
-
runningQuery.canceled = true;
|
39
|
-
var xhr = runningQuery.xhr;
|
40
|
-
if (xhr) {
|
41
|
-
xhr.abort();
|
42
|
-
}
|
43
|
-
remoteCancelQuery(runningQuery);
|
44
|
-
queryComplete();
|
45
|
-
}
|
46
|
-
|
47
|
-
function csrfProtect(payload) {
|
48
|
-
var param = $("meta[name=csrf-param]").attr("content");
|
49
|
-
var token = $("meta[name=csrf-token]").attr("content");
|
50
|
-
if (param && token) payload[param] = token;
|
51
|
-
return new Blob([JSON.stringify(payload)], {type : "application/json; charset=utf-8"});
|
52
|
-
}
|
53
|
-
|
54
|
-
function remoteCancelQuery(runningQuery) {
|
55
|
-
var path = Routes.blazer_cancel_queries_path();
|
56
|
-
var data = {run_id: runningQuery.run_id, data_source: runningQuery.data_source};
|
57
|
-
if (navigator.sendBeacon) {
|
58
|
-
navigator.sendBeacon(path, csrfProtect(data));
|
59
|
-
} else {
|
60
|
-
// TODO make sync
|
61
|
-
$.post(path, data);
|
62
|
-
}
|
63
|
-
}
|
64
|
-
|
65
|
-
var queriesQueue = [];
|
66
|
-
var runningQueries = 0;
|
67
|
-
var maxQueries = 3;
|
68
|
-
|
69
|
-
function queueQuery(callback) {
|
70
|
-
queriesQueue.push(callback);
|
71
|
-
runNext();
|
72
|
-
}
|
73
|
-
|
74
|
-
function runNext() {
|
75
|
-
if (runningQueries < maxQueries) {
|
76
|
-
var callback = queriesQueue.shift();
|
77
|
-
if (callback) {
|
78
|
-
runningQueries++;
|
79
|
-
callback();
|
80
|
-
runNext();
|
81
|
-
}
|
82
|
-
}
|
83
|
-
}
|
84
|
-
|
85
|
-
function queryComplete() {
|
86
|
-
runningQueries--;
|
87
|
-
runNext();
|
88
|
-
}
|
89
|
-
|
90
|
-
function runQuery(data, success, error, runningQuery) {
|
91
|
-
queueQuery( function () {
|
92
|
-
runningQuery = runningQuery || {};
|
93
|
-
runningQuery.run_id = data.run_id = uuid();
|
94
|
-
runningQuery.data_source = data.data_source;
|
95
|
-
return runQueryHelper(data, success, error, runningQuery);
|
96
|
-
});
|
97
|
-
}
|
98
|
-
|
99
|
-
function runQueryHelper(data, success, error, runningQuery) {
|
100
|
-
var xhr = $.ajax({
|
101
|
-
url: Routes.blazer_run_queries_path(),
|
102
|
-
method: "POST",
|
103
|
-
data: data,
|
104
|
-
dataType: "html"
|
105
|
-
}).done( function (d) {
|
106
|
-
if (d[0] == "{") {
|
107
|
-
var response = $.parseJSON(d);
|
108
|
-
data.blazer = response;
|
109
|
-
setTimeout( function () {
|
110
|
-
if (!(runningQuery && runningQuery.canceled)) {
|
111
|
-
runQueryHelper(data, success, error, runningQuery);
|
112
|
-
}
|
113
|
-
}, 1000);
|
114
|
-
} else {
|
115
|
-
if (!(runningQuery && runningQuery.canceled)) {
|
116
|
-
success(d);
|
117
|
-
}
|
118
|
-
queryComplete();
|
119
|
-
}
|
120
|
-
}).fail( function(jqXHR, textStatus, errorThrown) {
|
121
|
-
if (!(runningQuery && runningQuery.canceled)) {
|
122
|
-
var message = (typeof errorThrown === "string") ? errorThrown : errorThrown.message;
|
123
|
-
error(message);
|
124
|
-
}
|
125
|
-
queryComplete();
|
126
|
-
});
|
127
|
-
if (runningQuery) {
|
128
|
-
runningQuery.xhr = xhr;
|
129
|
-
}
|
130
|
-
return xhr;
|
131
|
-
}
|
30
|
+
$(this).addClass("expanded")
|
31
|
+
})
|
132
32
|
|
133
33
|
function submitIfCompleted($form) {
|
134
|
-
var completed = true
|
34
|
+
var completed = true
|
135
35
|
$form.find("input[name], select").each( function () {
|
136
36
|
if ($(this).val() == "") {
|
137
|
-
completed = false
|
37
|
+
completed = false
|
138
38
|
}
|
139
|
-
})
|
39
|
+
})
|
140
40
|
if (completed) {
|
141
|
-
$form.submit()
|
41
|
+
$form.submit()
|
142
42
|
}
|
143
43
|
}
|
144
44
|
|
@@ -146,140 +46,131 @@ function submitIfCompleted($form) {
|
|
146
46
|
// Adapted from Biff MaGriff: http://stackoverflow.com/a/7895814/1196499
|
147
47
|
function preventBackspaceNav() {
|
148
48
|
$(document).keydown(function (e) {
|
149
|
-
var preventKeyPress
|
49
|
+
var preventKeyPress
|
150
50
|
if (e.keyCode == 8) {
|
151
|
-
var d = e.srcElement || e.target
|
51
|
+
var d = e.srcElement || e.target
|
152
52
|
switch (d.tagName.toUpperCase()) {
|
153
53
|
case 'TEXTAREA':
|
154
|
-
preventKeyPress = d.readOnly || d.disabled
|
155
|
-
break
|
54
|
+
preventKeyPress = d.readOnly || d.disabled
|
55
|
+
break
|
156
56
|
case 'INPUT':
|
157
|
-
preventKeyPress = d.readOnly || d.disabled || (d.attributes["type"] && $.inArray(d.attributes["type"].value.toLowerCase(), ["radio", "reset", "checkbox", "submit", "button"]) >= 0)
|
158
|
-
break
|
57
|
+
preventKeyPress = d.readOnly || d.disabled || (d.attributes["type"] && $.inArray(d.attributes["type"].value.toLowerCase(), ["radio", "reset", "checkbox", "submit", "button"]) >= 0)
|
58
|
+
break
|
159
59
|
case 'DIV':
|
160
|
-
preventKeyPress = d.readOnly || d.disabled || !(d.attributes["contentEditable"] && d.attributes["contentEditable"].value == "true")
|
161
|
-
break
|
60
|
+
preventKeyPress = d.readOnly || d.disabled || !(d.attributes["contentEditable"] && d.attributes["contentEditable"].value == "true")
|
61
|
+
break
|
162
62
|
default:
|
163
|
-
preventKeyPress = true
|
164
|
-
break
|
63
|
+
preventKeyPress = true
|
64
|
+
break
|
165
65
|
}
|
166
66
|
}
|
167
67
|
else {
|
168
|
-
preventKeyPress = false
|
68
|
+
preventKeyPress = false
|
169
69
|
}
|
170
70
|
|
171
71
|
if (preventKeyPress) {
|
172
|
-
e.preventDefault()
|
72
|
+
e.preventDefault()
|
173
73
|
}
|
174
|
-
})
|
74
|
+
})
|
175
75
|
}
|
176
76
|
|
177
|
-
var editor
|
77
|
+
var editor
|
178
78
|
|
179
79
|
// http://stackoverflow.com/questions/11584061/
|
180
80
|
function adjustHeight() {
|
181
|
-
var lines = editor.getSession().getScreenLength()
|
81
|
+
var lines = editor.getSession().getScreenLength()
|
182
82
|
if (lines < 9) {
|
183
|
-
lines = 9
|
83
|
+
lines = 9
|
184
84
|
}
|
185
85
|
|
186
|
-
var newHeight = (lines + 1) * 16
|
187
|
-
$("#editor").height(newHeight.toString() + "px")
|
188
|
-
editor.resize()
|
189
|
-
}
|
86
|
+
var newHeight = (lines + 1) * 16
|
87
|
+
$("#editor").height(newHeight.toString() + "px")
|
88
|
+
editor.resize()
|
89
|
+
}
|
190
90
|
|
191
91
|
function getSQL() {
|
192
|
-
var selectedText = editor.getSelectedText()
|
193
|
-
var text = selectedText.length < 10 ? editor.getValue() : selectedText
|
194
|
-
return text.replace(/\n/g, "\r\n")
|
92
|
+
var selectedText = editor.getSelectedText()
|
93
|
+
var text = selectedText.length < 10 ? editor.getValue() : selectedText
|
94
|
+
return text.replace(/\n/g, "\r\n")
|
195
95
|
}
|
196
96
|
|
197
97
|
function getErrorLine() {
|
198
|
-
var error_line = /LINE (\d+)/g.exec($("#results").find('.alert-danger').text())
|
98
|
+
var error_line = /LINE (\d+)/g.exec($("#results").find('.alert-danger').text())
|
199
99
|
|
200
100
|
if (error_line) {
|
201
|
-
error_line = parseInt(error_line[1], 10)
|
101
|
+
error_line = parseInt(error_line[1], 10)
|
202
102
|
if (editor.getSelectedText().length >= 10) {
|
203
|
-
error_line += editor.getSelectionRange().start.row
|
103
|
+
error_line += editor.getSelectionRange().start.row
|
204
104
|
}
|
205
|
-
return error_line
|
105
|
+
return error_line
|
206
106
|
}
|
207
107
|
}
|
208
108
|
|
209
|
-
var error_line = null
|
210
|
-
var runningQuery;
|
211
|
-
|
212
|
-
function queryDone() {
|
213
|
-
runningQuery = null
|
214
|
-
$("#run").removeClass("hide")
|
215
|
-
$("#cancel").addClass("hide")
|
216
|
-
}
|
109
|
+
var error_line = null
|
217
110
|
|
218
111
|
$(document).on("click", "#cancel", function (e) {
|
219
112
|
e.preventDefault()
|
220
113
|
|
221
|
-
|
114
|
+
cancelAllQueries()
|
115
|
+
|
222
116
|
queryDone()
|
223
117
|
|
224
118
|
$("#results").html("")
|
225
119
|
})
|
226
120
|
|
227
|
-
function
|
228
|
-
|
229
|
-
|
230
|
-
}
|
121
|
+
function queryDone() {
|
122
|
+
$("#run").removeClass("hide")
|
123
|
+
$("#cancel").addClass("hide")
|
231
124
|
}
|
232
125
|
|
233
|
-
$(window).unload(cancelQuery2)
|
234
|
-
|
235
126
|
$(document).on("click", "#run", function (e) {
|
236
|
-
e.preventDefault()
|
127
|
+
e.preventDefault()
|
237
128
|
|
238
129
|
$(this).addClass("hide")
|
239
130
|
$("#cancel").removeClass("hide")
|
240
131
|
|
241
132
|
if (error_line) {
|
242
|
-
editor.getSession().removeGutterDecoration(error_line - 1, "error")
|
243
|
-
error_line = null
|
133
|
+
editor.getSession().removeGutterDecoration(error_line - 1, "error")
|
134
|
+
error_line = null
|
244
135
|
}
|
245
136
|
|
246
|
-
$("#results").html('<p class="text-muted">Loading...</p>')
|
137
|
+
$("#results").html('<p class="text-muted">Loading...</p>')
|
247
138
|
|
248
|
-
var data = $.extend({}, params, {statement: getSQL(), data_source: $("#query_data_source").val()})
|
139
|
+
var data = $.extend({}, params, {statement: getSQL(), data_source: $("#query_data_source").val()})
|
249
140
|
|
250
|
-
|
141
|
+
cancelAllQueries()
|
251
142
|
|
252
143
|
runQuery(data, function (data) {
|
253
144
|
queryDone()
|
254
145
|
|
255
|
-
$("#results").html(data)
|
146
|
+
$("#results").html(data)
|
256
147
|
|
257
|
-
error_line = getErrorLine()
|
148
|
+
error_line = getErrorLine()
|
258
149
|
if (error_line) {
|
259
|
-
editor.getSession().addGutterDecoration(error_line - 1, "error")
|
260
|
-
editor.scrollToLine(error_line, true, true, function () {})
|
261
|
-
editor.gotoLine(error_line, 0, true)
|
262
|
-
editor.focus()
|
150
|
+
editor.getSession().addGutterDecoration(error_line - 1, "error")
|
151
|
+
editor.scrollToLine(error_line, true, true, function () {})
|
152
|
+
editor.gotoLine(error_line, 0, true)
|
153
|
+
editor.focus()
|
263
154
|
}
|
264
155
|
}, function (data) {
|
265
156
|
// TODO show error
|
266
157
|
queryDone()
|
267
|
-
}
|
268
|
-
})
|
158
|
+
})
|
159
|
+
})
|
269
160
|
|
270
161
|
$(document).on("change", "#table_names", function () {
|
271
|
-
var val = $(this).val()
|
162
|
+
var val = $(this).val()
|
272
163
|
if (val.length > 0) {
|
273
|
-
var dataSource = $("#query_data_source").val()
|
274
|
-
editor.setValue(previewStatement[dataSource].replace("{table}", val), 1)
|
275
|
-
$("#run").click()
|
164
|
+
var dataSource = $("#query_data_source").val()
|
165
|
+
editor.setValue(previewStatement[dataSource].replace("{table}", val), 1)
|
166
|
+
$("#run").click()
|
276
167
|
}
|
277
|
-
})
|
168
|
+
})
|
278
169
|
|
279
170
|
function showEditor() {
|
280
|
-
editor = ace.edit("editor")
|
281
|
-
editor.setTheme("ace/theme/twilight")
|
282
|
-
editor.getSession().setMode("ace/mode/sql")
|
171
|
+
editor = ace.edit("editor")
|
172
|
+
editor.setTheme("ace/theme/twilight")
|
173
|
+
editor.getSession().setMode("ace/mode/sql")
|
283
174
|
editor.setOptions({
|
284
175
|
enableBasicAutocompletion: false,
|
285
176
|
enableSnippets: false,
|
@@ -287,32 +178,32 @@ function showEditor() {
|
|
287
178
|
highlightActiveLine: false,
|
288
179
|
fontSize: 12,
|
289
180
|
minLines: 10
|
290
|
-
})
|
291
|
-
editor.renderer.setShowGutter(true)
|
292
|
-
editor.renderer.setPrintMarginColumn(false)
|
293
|
-
editor.renderer.setPadding(10)
|
294
|
-
editor.getSession().setUseWrapMode(true)
|
181
|
+
})
|
182
|
+
editor.renderer.setShowGutter(true)
|
183
|
+
editor.renderer.setPrintMarginColumn(false)
|
184
|
+
editor.renderer.setPadding(10)
|
185
|
+
editor.getSession().setUseWrapMode(true)
|
295
186
|
editor.commands.addCommand({
|
296
187
|
name: 'run',
|
297
188
|
bindKey: {win: 'Ctrl-Enter', mac: 'Command-Enter'},
|
298
189
|
exec: function(editor) {
|
299
|
-
$("#run").click()
|
190
|
+
$("#run").click()
|
300
191
|
},
|
301
192
|
readOnly: false // false if this command should not apply in readOnly mode
|
302
|
-
})
|
193
|
+
})
|
303
194
|
// fix command+L
|
304
|
-
editor.commands.removeCommands(["gotoline"])
|
195
|
+
editor.commands.removeCommands(["gotoline", "find"])
|
305
196
|
|
306
197
|
editor.getSession().on("change", function () {
|
307
|
-
$("#query_statement").val(editor.getValue())
|
308
|
-
adjustHeight()
|
309
|
-
})
|
310
|
-
adjustHeight()
|
311
|
-
$("#editor").show()
|
312
|
-
editor.focus()
|
198
|
+
$("#query_statement").val(editor.getValue())
|
199
|
+
adjustHeight()
|
200
|
+
})
|
201
|
+
adjustHeight()
|
202
|
+
$("#editor").show()
|
203
|
+
editor.focus()
|
313
204
|
}
|
314
205
|
|
315
|
-
preventBackspaceNav()
|
206
|
+
preventBackspaceNav()
|
316
207
|
|
317
208
|
function updatePreviewSelect() {
|
318
209
|
var dataSource = $("#query_data_source").val()
|
@@ -0,0 +1,107 @@
|
|
1
|
+
var pendingQueries = []
|
2
|
+
var runningQueries = []
|
3
|
+
var maxQueries = 3
|
4
|
+
|
5
|
+
function runQuery(data, success, error) {
|
6
|
+
data.run_id = uuid()
|
7
|
+
var query = {
|
8
|
+
data: data,
|
9
|
+
success: success,
|
10
|
+
error: error,
|
11
|
+
run_id: data.run_id,
|
12
|
+
data_source: data.data_source,
|
13
|
+
canceled: false
|
14
|
+
}
|
15
|
+
pendingQueries.push(query)
|
16
|
+
runNext()
|
17
|
+
return query
|
18
|
+
}
|
19
|
+
|
20
|
+
function runNext() {
|
21
|
+
if (runningQueries.length < maxQueries) {
|
22
|
+
var query = pendingQueries.shift()
|
23
|
+
if (query) {
|
24
|
+
runningQueries.push(query)
|
25
|
+
runQueryHelper(query)
|
26
|
+
runNext()
|
27
|
+
}
|
28
|
+
}
|
29
|
+
}
|
30
|
+
|
31
|
+
function runQueryHelper(query) {
|
32
|
+
var xhr = $.ajax({
|
33
|
+
url: Routes.blazer_run_queries_path(),
|
34
|
+
method: "POST",
|
35
|
+
data: query.data,
|
36
|
+
dataType: "html"
|
37
|
+
}).done( function (d) {
|
38
|
+
if (d[0] == "{") {
|
39
|
+
var response = $.parseJSON(d)
|
40
|
+
query.data.blazer = response
|
41
|
+
setTimeout( function () {
|
42
|
+
if (!query.canceled) {
|
43
|
+
runQueryHelper(query)
|
44
|
+
}
|
45
|
+
}, 1000)
|
46
|
+
} else {
|
47
|
+
if (!query.canceled) {
|
48
|
+
query.success(d)
|
49
|
+
}
|
50
|
+
queryComplete(query)
|
51
|
+
}
|
52
|
+
}).fail( function(jqXHR, textStatus, errorThrown) {
|
53
|
+
if (!query.canceled) {
|
54
|
+
var message = (typeof errorThrown === "string") ? errorThrown : errorThrown.message
|
55
|
+
query.error(message)
|
56
|
+
}
|
57
|
+
queryComplete(query)
|
58
|
+
})
|
59
|
+
query.xhr = xhr
|
60
|
+
return xhr
|
61
|
+
}
|
62
|
+
|
63
|
+
function queryComplete(query) {
|
64
|
+
var index = runningQueries.indexOf(query)
|
65
|
+
runningQueries.splice(index, 1)
|
66
|
+
runNext()
|
67
|
+
}
|
68
|
+
|
69
|
+
function uuid() {
|
70
|
+
return 'xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx'.replace(/[xy]/g, function(c) {
|
71
|
+
var r = Math.random()*16|0, v = c == 'x' ? r : (r&0x3|0x8)
|
72
|
+
return v.toString(16)
|
73
|
+
})
|
74
|
+
}
|
75
|
+
|
76
|
+
function cancelAllQueries() {
|
77
|
+
pendingQueries = []
|
78
|
+
for (var i = 0; i < runningQueries.length; i++) {
|
79
|
+
cancelQuery(runningQueries[i])
|
80
|
+
}
|
81
|
+
}
|
82
|
+
|
83
|
+
$(window).unload(cancelAllQueries)
|
84
|
+
|
85
|
+
function cancelQuery(query) {
|
86
|
+
query.canceled = true
|
87
|
+
if (query.xhr) {
|
88
|
+
query.xhr.abort()
|
89
|
+
}
|
90
|
+
|
91
|
+
// tell server
|
92
|
+
var path = Routes.blazer_cancel_queries_path()
|
93
|
+
var data = {run_id: query.run_id, data_source: query.data_source}
|
94
|
+
if (navigator.sendBeacon) {
|
95
|
+
navigator.sendBeacon(path, csrfProtect(data))
|
96
|
+
} else {
|
97
|
+
// TODO make sync
|
98
|
+
$.post(path, data)
|
99
|
+
}
|
100
|
+
}
|
101
|
+
|
102
|
+
function csrfProtect(payload) {
|
103
|
+
var param = $("meta[name=csrf-param]").attr("content")
|
104
|
+
var token = $("meta[name=csrf-token]").attr("content")
|
105
|
+
if (param && token) payload[param] = token
|
106
|
+
return new Blob([JSON.stringify(payload)], {type : "application/json charset=utf-8"})
|
107
|
+
}
|
@@ -59,17 +59,24 @@ module Blazer
|
|
59
59
|
end
|
60
60
|
|
61
61
|
def parse_smart_variables(var, data_source)
|
62
|
-
|
63
|
-
|
64
|
-
|
65
|
-
|
66
|
-
|
67
|
-
|
68
|
-
|
69
|
-
|
70
|
-
|
62
|
+
smart_var_data_source =
|
63
|
+
([data_source] + Array(data_source.settings["inherit_smart_settings"]).map { |ds| Blazer.data_sources[ds] }).find { |ds| ds.smart_variables[var] }
|
64
|
+
|
65
|
+
if smart_var_data_source
|
66
|
+
query = smart_var_data_source.smart_variables[var]
|
67
|
+
|
68
|
+
if query.is_a? Hash
|
69
|
+
smart_var = query.map { |k,v| [v, k] }
|
70
|
+
elsif query.is_a? Array
|
71
|
+
smart_var = query.map { |v| [v, v] }
|
72
|
+
elsif query
|
73
|
+
result = smart_var_data_source.run_statement(query)
|
74
|
+
smart_var = result.rows.map { |v| v.reverse }
|
75
|
+
error = result.error if result.error
|
76
|
+
end
|
71
77
|
end
|
72
|
-
|
78
|
+
|
79
|
+
[smart_var, error]
|
73
80
|
end
|
74
81
|
|
75
82
|
def extract_vars(statement)
|
data/app/models/blazer/query.rb
CHANGED
@@ -19,7 +19,9 @@ module Blazer
|
|
19
19
|
end
|
20
20
|
|
21
21
|
def editable?(user)
|
22
|
-
!persisted? || (name.present? && name.first != "*" && name.first != "#") || user == creator
|
22
|
+
editable = !persisted? || (name.present? && name.first != "*" && name.first != "#") || user == creator
|
23
|
+
editable &&= Blazer.query_editable.call(self, user) if Blazer.query_editable
|
24
|
+
editable
|
23
25
|
end
|
24
26
|
end
|
25
27
|
end
|
@@ -16,7 +16,7 @@
|
|
16
16
|
<%= link_to "Back", :back %>
|
17
17
|
</div>
|
18
18
|
<%= link_to "Schema", "#", target: "_blank", id: "view-schema", style: "margin-right: 10px;" %>
|
19
|
-
<%= f.select :data_source, Blazer.data_sources.values.map { |ds| [ds.name, ds.id] }, {}, class: ("hide" if Blazer.data_sources.size == 1), style: "width: 140px;" %>
|
19
|
+
<%= f.select :data_source, Blazer.data_sources.values.select { |ds| q = @query.dup; q.data_source = ds.id; q.editable?(blazer_user) }.map { |ds| [ds.name, ds.id] }, {}, class: ("hide" if Blazer.data_sources.size == 1), style: "width: 140px;" %>
|
20
20
|
<div id="tables" style="display: inline-block; width: 250px; margin-right: 10px;" class="hide">
|
21
21
|
<%= render partial: "tables" %>
|
22
22
|
</div>
|
@@ -38,10 +38,10 @@
|
|
38
38
|
<tbody class="list" v-cloak>
|
39
39
|
<tr v-for="query in visibleItems">
|
40
40
|
<td>
|
41
|
-
<span
|
42
|
-
<span class="vars">{{query.vars}}</span>
|
41
|
+
<span :class="{dashboard: query.dashboard}"><a :href="itemPath(query)">{{ query.name }}</a></span>
|
42
|
+
<span class="vars">{{ query.vars }}</span>
|
43
43
|
</td>
|
44
|
-
<td class="creator">{{query.creator}}</td>
|
44
|
+
<td class="creator">{{ query.creator }}</td>
|
45
45
|
</tr>
|
46
46
|
</tbody>
|
47
47
|
</table>
|
@@ -141,10 +141,10 @@
|
|
141
141
|
<% else %>
|
142
142
|
<%= blazer_format_value(k, v) %>
|
143
143
|
<% end %>
|
144
|
+
<% end %>
|
144
145
|
|
145
|
-
|
146
|
-
|
147
|
-
<% end %>
|
146
|
+
<% if v2 = (@boom[k] || {})[v.nil? ? v : v.to_s] %>
|
147
|
+
<div class="text-muted"><%= v2 %></div>
|
148
148
|
<% end %>
|
149
149
|
</td>
|
150
150
|
<% end %>
|
data/blazer.gemspec
CHANGED
@@ -8,8 +8,7 @@ Gem::Specification.new do |spec|
|
|
8
8
|
spec.version = Blazer::VERSION
|
9
9
|
spec.authors = ["Andrew Kane"]
|
10
10
|
spec.email = ["andrew@chartkick.com"]
|
11
|
-
spec.summary = "
|
12
|
-
spec.description = "Share data effortlessly with your team"
|
11
|
+
spec.summary = "Explore your data with SQL. Easily create charts and dashboards, and share them with your team."
|
13
12
|
spec.homepage = "https://github.com/ankane/blazer"
|
14
13
|
spec.license = "MIT"
|
15
14
|
|
data/lib/blazer.rb
CHANGED
@@ -41,7 +41,7 @@ module Blazer
|
|
41
41
|
end
|
42
42
|
|
43
43
|
def tables
|
44
|
-
result = data_source.run_statement(connection_model.send(:sanitize_sql_array, ["SELECT table_name FROM information_schema.tables WHERE table_schema IN (?) ORDER BY table_name", schemas]))
|
44
|
+
result = data_source.run_statement(connection_model.send(:sanitize_sql_array, ["SELECT table_name FROM information_schema.tables WHERE table_schema IN (?) ORDER BY table_name", schemas]), refresh_cache: true)
|
45
45
|
result.rows.map(&:first)
|
46
46
|
end
|
47
47
|
|
@@ -83,6 +83,10 @@ module Blazer
|
|
83
83
|
end
|
84
84
|
end
|
85
85
|
|
86
|
+
def cachable?(statement)
|
87
|
+
!%w[CREATE ALTER UPDATE INSERT DELETE].include?(statement.split.first.to_s.upcase)
|
88
|
+
end
|
89
|
+
|
86
90
|
protected
|
87
91
|
|
88
92
|
def select_all(statement)
|
data/lib/blazer/data_source.rb
CHANGED
@@ -172,7 +172,7 @@ module Blazer
|
|
172
172
|
cache_data = Marshal.dump([columns, rows, error, cache ? Time.now : nil]) rescue nil
|
173
173
|
end
|
174
174
|
|
175
|
-
if cache && cache_data
|
175
|
+
if cache && cache_data && @adapter_instance.cachable?(statement)
|
176
176
|
Blazer.cache.write(statement_cache_key(statement), cache_data, expires_in: cache_expires_in.to_f * 60)
|
177
177
|
end
|
178
178
|
|
data/lib/blazer/result.rb
CHANGED
@@ -23,11 +23,21 @@ module Blazer
|
|
23
23
|
@boom ||= begin
|
24
24
|
boom = {}
|
25
25
|
columns.each_with_index do |key, i|
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
|
26
|
+
smart_columns_data_source =
|
27
|
+
([data_source] + Array(data_source.settings["inherit_smart_settings"]).map { |ds| Blazer.data_sources[ds] }).find { |ds| ds.smart_columns[key] }
|
28
|
+
|
29
|
+
if smart_columns_data_source
|
30
|
+
query = smart_columns_data_source.smart_columns[key]
|
31
|
+
res =
|
32
|
+
if query.is_a?(Hash)
|
33
|
+
query
|
34
|
+
else
|
35
|
+
values = rows.map { |r| r[i] }.compact.uniq
|
36
|
+
result = smart_columns_data_source.run_statement(ActiveRecord::Base.send(:sanitize_sql_array, [query.sub("{value}", "(?)"), values]))
|
37
|
+
result.rows
|
38
|
+
end
|
39
|
+
|
40
|
+
boom[key] = Hash[res.map { |k, v| [k.nil? ? k : k.to_s, v] }]
|
31
41
|
end
|
32
42
|
end
|
33
43
|
boom
|
data/lib/blazer/version.rb
CHANGED
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: blazer
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 1.7.
|
4
|
+
version: 1.7.2
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Andrew Kane
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2016-10-
|
11
|
+
date: 2016-10-31 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: rails
|
@@ -108,7 +108,7 @@ dependencies:
|
|
108
108
|
- - "~>"
|
109
109
|
- !ruby/object:Gem::Version
|
110
110
|
version: '10.0'
|
111
|
-
description:
|
111
|
+
description:
|
112
112
|
email:
|
113
113
|
- andrew@chartkick.com
|
114
114
|
executables: []
|
@@ -145,6 +145,7 @@ files:
|
|
145
145
|
- app/assets/javascripts/blazer/jquery_ujs.js
|
146
146
|
- app/assets/javascripts/blazer/moment-timezone.js
|
147
147
|
- app/assets/javascripts/blazer/moment.js
|
148
|
+
- app/assets/javascripts/blazer/queries.js
|
148
149
|
- app/assets/javascripts/blazer/routes.js.erb
|
149
150
|
- app/assets/javascripts/blazer/selectize.js
|
150
151
|
- app/assets/javascripts/blazer/stupidtable.js
|
@@ -230,5 +231,6 @@ rubyforge_project:
|
|
230
231
|
rubygems_version: 2.5.1
|
231
232
|
signing_key:
|
232
233
|
specification_version: 4
|
233
|
-
summary:
|
234
|
+
summary: Explore your data with SQL. Easily create charts and dashboards, and share
|
235
|
+
them with your team.
|
234
236
|
test_files: []
|