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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: d9f1befb584a82255a0b18c923fed929e19b7d45
4
- data.tar.gz: a29b5d3ad69071040cabe3d55292ea90a424590f
3
+ metadata.gz: 9d8af782cbd8374eb05295ae7fbd31766198adb1
4
+ data.tar.gz: f825ed76228ddf5eee748c9f48f252a358b206d3
5
5
  SHA512:
6
- metadata.gz: bc7d9b22deda363e630490afacfa29f6bc3ca72eb9dc961bc6bcc19a5c73390791f0affd86a356f355da64abde234a8c3dab9bef5dbf5bb6668e816f111cd280
7
- data.tar.gz: fee705155f102fce4460a5f92d848ec7ddd2b9146f8888d9673ec1f7aca43feebf50835cf7707a22d35362ff0fec6db4ba7cd55c27dd8e9a5f3243270bf4b65e
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
- Use [sqlite3](https://github.com/sparklemotion/sqlite3-ruby).
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
- cancelQuery(runningQuery)
114
+ cancelAllQueries()
115
+
222
116
  queryDone()
223
117
 
224
118
  $("#results").html("")
225
119
  })
226
120
 
227
- function cancelQuery2() {
228
- if (runningQuery) {
229
- remoteCancelQuery(runningQuery)
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
- runningQuery = {};
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
- }, runningQuery);
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
- query = data_source.smart_variables[var]
63
- if query.is_a? Hash
64
- smart_var = query.map { |k,v| [v, k] }
65
- elsif query.is_a? Array
66
- smart_var = query.map { |v| [v, v] }
67
- elsif query
68
- result = data_source.run_statement(query)
69
- smart_var = result.rows.map { |v| v.reverse }
70
- error = result.error if result.error
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
- return smart_var, error
78
+
79
+ [smart_var, error]
73
80
  end
74
81
 
75
82
  def extract_vars(statement)
@@ -102,6 +102,7 @@ module Blazer
102
102
  render_run
103
103
  elsif Time.now > Time.at(@timestamp + (@data_source.timeout || 600).to_i + 5)
104
104
  # query lost
105
+ Rails.logger.info "[blazer lost query] #{@run_id}"
105
106
  @error = "We lost your query :("
106
107
  @rows = []
107
108
  @columns = []
@@ -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 v-bind:class="{dashboard: query.dashboard}"><a :href="itemPath(query)">{{query.name}}</a></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
- <% if v2 = (@boom[k] || {})[v.to_s] %>
146
- <div class="text-muted"><%= v2 %></div>
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 = "Share data effortlessly with your team"
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
@@ -33,6 +33,7 @@ module Blazer
33
33
  attr_accessor :anomaly_checks
34
34
  attr_accessor :async
35
35
  attr_accessor :images
36
+ attr_accessor :query_editable
36
37
  end
37
38
  self.audit = true
38
39
  self.user_name = :name
@@ -39,6 +39,10 @@ module Blazer
39
39
  # optional
40
40
  end
41
41
 
42
+ def cachable?(statement)
43
+ true # optional
44
+ end
45
+
42
46
  protected
43
47
 
44
48
  def settings
@@ -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)
@@ -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
- query = data_source.smart_columns[key]
27
- if query
28
- values = rows.map { |r| r[i] }.compact.uniq
29
- result = data_source.run_statement(ActiveRecord::Base.send(:sanitize_sql_array, [query.sub("{value}", "(?)"), values]))
30
- boom[key] = Hash[result.rows.map { |k, v| [k.to_s, v] }]
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
@@ -1,3 +1,3 @@
1
1
  module Blazer
2
- VERSION = "1.7.1"
2
+ VERSION = "1.7.2"
3
3
  end
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.1
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-30 00:00:00.000000000 Z
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: Share data effortlessly with your team
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: Share data effortlessly with your team
234
+ summary: Explore your data with SQL. Easily create charts and dashboards, and share
235
+ them with your team.
234
236
  test_files: []