blazer 1.0.0 → 1.0.1
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/app/controllers/blazer/base_controller.rb +7 -1
- data/app/controllers/blazer/dashboards_controller.rb +1 -1
- data/app/controllers/blazer/queries_controller.rb +27 -10
- data/app/views/blazer/checks/run.html.erb +1 -1
- data/app/views/blazer/dashboards/_form.html.erb +3 -5
- data/app/views/blazer/dashboards/show.html.erb +9 -1
- data/app/views/blazer/queries/_form.html.erb +1 -1
- data/app/views/blazer/queries/_index.html.erb +29 -0
- data/app/views/blazer/queries/home.html.erb +40 -0
- data/app/views/blazer/queries/run.html.erb +13 -1
- data/app/views/blazer/queries/show.html.erb +9 -1
- data/config/routes.rb +3 -2
- data/lib/blazer.rb +3 -1
- data/lib/blazer/data_source.rb +75 -16
- data/lib/blazer/engine.rb +7 -0
- data/lib/blazer/version.rb +1 -1
- data/lib/generators/blazer/templates/config.yml +17 -1
- metadata +4 -4
- data/app/views/blazer/queries/index.html.erb +0 -57
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 135fd38b9d7a75473a1ecf2afd1acf27ff9a6ffb
|
4
|
+
data.tar.gz: 83e0ec15e78191d00b6a81275f60c59dfaff17d8
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 11fd0afc742cde141f2019cc70de520a95b7222c302dba952807dd91945380db9aeb2334e89eaa7a8f60ebcbfb9ec56faf838a23e12f7a80b9507f1937942aa3
|
7
|
+
data.tar.gz: ac2b8e150d3791aa89d63be8562d6413c30cbd488d5b0dad7a60c813e66ece451adec26b6b2962708ad322495a52c02dd612933769bf14b54d4c4a87612b81de
|
data/CHANGELOG.md
CHANGED
@@ -30,6 +30,7 @@ module Blazer
|
|
30
30
|
if var.end_with?("_at")
|
31
31
|
value = Blazer.time_zone.parse(value) rescue nil
|
32
32
|
end
|
33
|
+
value.gsub!(" ", "+") if ["start_time", "end_time"].include?(var) # fix for Quip bug
|
33
34
|
statement.gsub!("{#{var}}", ActiveRecord::Base.connection.quote(value))
|
34
35
|
end
|
35
36
|
end
|
@@ -41,8 +42,13 @@ module Blazer
|
|
41
42
|
helper_method :extract_vars
|
42
43
|
|
43
44
|
def variable_params
|
44
|
-
params.except(:controller, :action, :id, :host, :query, :table_names, :authenticity_token, :utf8, :_method, :commit, :statement, :data_source)
|
45
|
+
params.except(:controller, :action, :id, :host, :query, :query_id, :table_names, :authenticity_token, :utf8, :_method, :commit, :statement, :data_source)
|
45
46
|
end
|
46
47
|
helper_method :variable_params
|
48
|
+
|
49
|
+
def blazer_user
|
50
|
+
send(Blazer.user_method) if Blazer.user_method && respond_to?(Blazer.user_method)
|
51
|
+
end
|
52
|
+
helper_method :blazer_user
|
47
53
|
end
|
48
54
|
end
|
@@ -34,7 +34,7 @@ module Blazer
|
|
34
34
|
data_sources.each do |data_source|
|
35
35
|
query = data_source.smart_variables[var]
|
36
36
|
if query
|
37
|
-
rows, error = data_source.run_statement(query)
|
37
|
+
rows, error, cached_at = data_source.run_statement(query)
|
38
38
|
(@smart_vars[var] ||= []).concat rows.map { |v| v.values.reverse }
|
39
39
|
@sql_errors << error if error
|
40
40
|
end
|
@@ -1,12 +1,14 @@
|
|
1
1
|
module Blazer
|
2
2
|
class QueriesController < BaseController
|
3
|
-
before_action :
|
3
|
+
before_action :set_queries, only: [:home, :index]
|
4
|
+
before_action :set_query, only: [:show, :edit, :update, :destroy, :refresh]
|
5
|
+
|
6
|
+
def home
|
7
|
+
@queries = @queries.limit(1000)
|
8
|
+
end
|
4
9
|
|
5
10
|
def index
|
6
|
-
|
7
|
-
@queries = @queries.includes(:creator) if Blazer.user_class
|
8
|
-
@trending_queries = Blazer::Audit.group(:query_id).where("created_at > ?", 2.days.ago).having("COUNT(DISTINCT user_id) >= 3").uniq.count(:user_id)
|
9
|
-
@checks = Blazer::Check.group(:query_id).count
|
11
|
+
render partial: "index", layout: false
|
10
12
|
end
|
11
13
|
|
12
14
|
def new
|
@@ -15,7 +17,7 @@ module Blazer
|
|
15
17
|
|
16
18
|
def create
|
17
19
|
@query = Blazer::Query.new(query_params)
|
18
|
-
@query.creator =
|
20
|
+
@query.creator = blazer_user
|
19
21
|
|
20
22
|
if @query.save
|
21
23
|
redirect_to query_path(@query, variable_params)
|
@@ -34,7 +36,7 @@ module Blazer
|
|
34
36
|
@bind_vars.each do |var|
|
35
37
|
query = data_source.smart_variables[var]
|
36
38
|
if query
|
37
|
-
rows, error = data_source.run_statement(query)
|
39
|
+
rows, error, cached_at = data_source.run_statement(query)
|
38
40
|
@smart_vars[var] = rows.map { |v| v.values.reverse }
|
39
41
|
@sql_errors << error if error
|
40
42
|
end
|
@@ -60,12 +62,12 @@ module Blazer
|
|
60
62
|
audit = Blazer::Audit.new(statement: @statement)
|
61
63
|
audit.query = @query
|
62
64
|
audit.data_source = data_source
|
63
|
-
audit.user =
|
65
|
+
audit.user = blazer_user
|
64
66
|
audit.save!
|
65
67
|
end
|
66
68
|
|
67
69
|
@data_source = Blazer.data_sources[data_source]
|
68
|
-
@rows, @error = @data_source.run_statement(@statement)
|
70
|
+
@rows, @error, @cached_at = @data_source.run_statement(@statement, user: blazer_user, query: @query, refresh_cache: params[:check])
|
69
71
|
|
70
72
|
if @query && !@error.to_s.include?("canceling statement due to statement timeout")
|
71
73
|
@query.checks.each do |check|
|
@@ -97,7 +99,7 @@ module Blazer
|
|
97
99
|
query = @data_source.smart_columns[key]
|
98
100
|
if query
|
99
101
|
values = @rows.map { |r| r[key] }.compact.uniq
|
100
|
-
rows, error = @data_source.run_statement(ActiveRecord::Base.send(:sanitize_sql_array, [query.sub("{value}", "(?)"), values]))
|
102
|
+
rows, error, cached_at = @data_source.run_statement(ActiveRecord::Base.send(:sanitize_sql_array, [query.sub("{value}", "(?)"), values]))
|
101
103
|
@boom[key] = Hash[rows.map(&:values)]
|
102
104
|
end
|
103
105
|
end
|
@@ -115,6 +117,14 @@ module Blazer
|
|
115
117
|
end
|
116
118
|
end
|
117
119
|
|
120
|
+
def refresh
|
121
|
+
data_source = Blazer.data_sources[@query.data_source]
|
122
|
+
@statement = @query.statement.dup
|
123
|
+
process_vars(@statement)
|
124
|
+
data_source.clear_cache(@statement)
|
125
|
+
redirect_to query_path(@query, variable_params)
|
126
|
+
end
|
127
|
+
|
118
128
|
def update
|
119
129
|
if @query.update(query_params)
|
120
130
|
redirect_to query_path(@query, variable_params)
|
@@ -135,6 +145,13 @@ module Blazer
|
|
135
145
|
|
136
146
|
private
|
137
147
|
|
148
|
+
def set_queries
|
149
|
+
@queries = Blazer::Query.order(:name)
|
150
|
+
@queries = @queries.includes(:creator) if Blazer.user_class
|
151
|
+
@trending_queries = Blazer::Audit.group(:query_id).where("created_at > ?", 2.days.ago).having("COUNT(DISTINCT user_id) >= 3").uniq.count(:user_id)
|
152
|
+
@checks = Blazer::Check.group(:query_id).count
|
153
|
+
end
|
154
|
+
|
138
155
|
def set_query
|
139
156
|
@query = Blazer::Query.find(params[:id].to_s.split("-").first)
|
140
157
|
end
|
@@ -1,7 +1,7 @@
|
|
1
1
|
<p style="text-muted">Running check...</p>
|
2
2
|
|
3
3
|
<script>
|
4
|
-
$.post("<%= run_queries_path %>", <%= json_escape({statement: @query.statement, query_id: @query.id}.to_json).html_safe %>, function (data) {
|
4
|
+
$.post("<%= run_queries_path %>", <%= json_escape({statement: @query.statement, query_id: @query.id, check: true}.to_json).html_safe %>, function (data) {
|
5
5
|
setTimeout( function () {
|
6
6
|
window.location.href = "<%= checks_path %>";
|
7
7
|
}, 200);
|
@@ -34,21 +34,19 @@ li:hover .glyphicon-remove {
|
|
34
34
|
<div class="form-group">
|
35
35
|
<%= f.label :query_id, "Add Chart" %>
|
36
36
|
<div class="hide">
|
37
|
-
<%= select_tag :query_id, options_for_select(Blazer::Query.order(:name).map { |q| [q.name, q.id] }, {include_blank: true}
|
37
|
+
<%= select_tag :query_id, options_for_select(Blazer::Query.order(:name).map { |q| [q.name, q.id] }), {include_blank: true, placeholder: "Select chart"} %>
|
38
38
|
</div>
|
39
39
|
<script>
|
40
|
-
$("#query_id").selectize(
|
40
|
+
$("#query_id").selectize().parents(".hide").removeClass("hide");
|
41
41
|
$("#query_id").change( function () {
|
42
42
|
var $option = $(this).find("option:selected");
|
43
43
|
if ($option.val() !== "") {
|
44
|
-
// console.log($option.val());
|
45
|
-
// console.log($option.text());
|
46
44
|
var $li = $("<li></li>");
|
47
45
|
$li.addClass("list-group-item");
|
48
46
|
$li.text($option.text());
|
49
47
|
$li.prepend('<span class="glyphicon glyphicon-remove" aria-hidden="true" style="float: right; margin-top: 3px;"></span><input type="hidden" name="query_ids[]" id="query_ids_" value="' + $option.val() + '">');
|
50
48
|
$(".list-group").append($li);
|
51
|
-
$(this).
|
49
|
+
$(this)[0].selectize.setValue("");
|
52
50
|
$(".form-group").removeClass("hide");
|
53
51
|
}
|
54
52
|
});
|
@@ -134,9 +134,17 @@
|
|
134
134
|
</div>
|
135
135
|
</div>
|
136
136
|
<script>
|
137
|
-
|
137
|
+
var request = $.ajax({
|
138
|
+
url: "<%= run_queries_path %>",
|
139
|
+
method: "POST",
|
140
|
+
data: <%= json_escape({statement: query.statement, query_id: query.id, only_chart: true}.to_json).html_safe %>,
|
141
|
+
dataType: "html"
|
142
|
+
}).done(function(data) {
|
138
143
|
$("#chart-<%= i %>").html(data);
|
139
144
|
$("#chart-<%= i %> table").stupidtable();
|
145
|
+
}).fail(function(jqXHR, textStatus, errorThrown) {
|
146
|
+
var message = (typeof errorThrown === "string") ? errorThrown : errorThrown.message;
|
147
|
+
$("#chart-<%= i %>").css("color", "red").html(message);
|
140
148
|
});
|
141
149
|
</script>
|
142
150
|
<% end %>
|
@@ -111,7 +111,7 @@
|
|
111
111
|
if (xhr) {
|
112
112
|
xhr.abort();
|
113
113
|
}
|
114
|
-
xhr = $.post("<%= run_queries_path %>", $.extend({}, params, {statement: editor.getValue(), data_source: $("#query_data_source").val()}), function (data) {
|
114
|
+
xhr = $.post("<%= run_queries_path %>", $.extend({}, params, {statement: editor.getValue().replace(/\n/g, "\r\n"), data_source: $("#query_data_source").val()}), function (data) {
|
115
115
|
$("#results").html(data);
|
116
116
|
|
117
117
|
error_line = /LINE (\d+)/g.exec($("#results").find('.alert-danger').text());
|
@@ -0,0 +1,29 @@
|
|
1
|
+
<% @queries.each do |query| %>
|
2
|
+
<tr>
|
3
|
+
<td class="query">
|
4
|
+
<%= link_to query.name, query %>
|
5
|
+
<span style="color: #ccc;"><%= extract_vars(query.statement).join(", ") %></span>
|
6
|
+
<% if @queries.size < 1000 && query.created_at > 2.days.ago %>
|
7
|
+
<small style="font-weight: bold; color: #5cb85c;">NEW</small>
|
8
|
+
<% end %>
|
9
|
+
<% if @trending_queries[query.id] %>
|
10
|
+
<small style="font-weight: bold; color: #f60;">TRENDING</small>
|
11
|
+
<% end %>
|
12
|
+
<% if @checks[query.id] %>
|
13
|
+
<small style="font-weight: bold; color: #f60;">CHECK</small>
|
14
|
+
<% end %>
|
15
|
+
<div class="hide"><%= query.name.gsub(/\s+/, "") %></div>
|
16
|
+
</td>
|
17
|
+
<td class="creator text-right text-muted">
|
18
|
+
<% if query.respond_to?(:creator) && (creator = query.creator) && creator.respond_to?(Blazer.user_name) %>
|
19
|
+
<% name = creator.send(Blazer.user_name) %>
|
20
|
+
<% if creator == blazer_user %>
|
21
|
+
You
|
22
|
+
<div class="hide">me <%= name %></div>
|
23
|
+
<% else %>
|
24
|
+
<%= name %>
|
25
|
+
<% end %>
|
26
|
+
<% end %>
|
27
|
+
</td>
|
28
|
+
</tr>
|
29
|
+
<% end %>
|
@@ -0,0 +1,40 @@
|
|
1
|
+
<div id="queries">
|
2
|
+
<div id="header" style="margin-bottom: 20px;">
|
3
|
+
<div class="pull-right">
|
4
|
+
<%= link_to "New Query", new_query_path, class: "btn btn-info" %>
|
5
|
+
<%= link_to "Dashboards", dashboards_path, class: "btn btn-primary" %>
|
6
|
+
<%= link_to "Checks", checks_path, class: "btn btn-primary" %>
|
7
|
+
</div>
|
8
|
+
<input type="text" placeholder="Start typing a query or person" style="width: 300px; display: inline-block;" autofocus=true class="search form-control" />
|
9
|
+
</div>
|
10
|
+
|
11
|
+
<table class="table">
|
12
|
+
<thead>
|
13
|
+
<tr>
|
14
|
+
<th>Query</th>
|
15
|
+
<th style="width: 20%; text-align: right;">Mastermind</th>
|
16
|
+
</tr>
|
17
|
+
</thead>
|
18
|
+
<tbody class="list">
|
19
|
+
<%= render partial: "index" %>
|
20
|
+
</tbody>
|
21
|
+
</table>
|
22
|
+
</div>
|
23
|
+
|
24
|
+
<script>
|
25
|
+
var options = {
|
26
|
+
valueNames: ['query', 'creator']
|
27
|
+
};
|
28
|
+
|
29
|
+
function updateList() {
|
30
|
+
var userList = new List('queries', options);
|
31
|
+
userList.search($(".search").val());
|
32
|
+
}
|
33
|
+
updateList();
|
34
|
+
</script>
|
35
|
+
|
36
|
+
<% if @queries.size == 1000 %>
|
37
|
+
<script>
|
38
|
+
$(".list").load("<%= queries_path %>", updateList);
|
39
|
+
</script>
|
40
|
+
<% end %>
|
@@ -8,6 +8,14 @@
|
|
8
8
|
<% end %>
|
9
9
|
<% else %>
|
10
10
|
<% unless @only_chart %>
|
11
|
+
<% if @cached_at %>
|
12
|
+
<p class="text-muted" style="float: right;">
|
13
|
+
Cached <%= time_ago_in_words(@cached_at, include_seconds: true) %> ago
|
14
|
+
<% if @query && !params[:data_source] %>
|
15
|
+
<%= link_to "Refresh", refresh_query_path(@query, variable_params), method: :post %>
|
16
|
+
<% end %>
|
17
|
+
</p>
|
18
|
+
<% end %>
|
11
19
|
<p class="text-muted"><%= pluralize(@rows.size, "row") %></p>
|
12
20
|
<% end %>
|
13
21
|
<% if @rows.any? %>
|
@@ -22,7 +30,11 @@
|
|
22
30
|
<% elsif values.size == 2 && values.first.is_a?(String) && values.last.is_a?(Numeric) %>
|
23
31
|
<%= pie_chart @rows.map(&:values), library: {sliceVisibilityThreshold: 1 / 40.0}, id: chart_id %>
|
24
32
|
<% elsif @only_chart %>
|
25
|
-
<% @
|
33
|
+
<% if @rows.size == 1 && @rows.first.size == 1 %>
|
34
|
+
<p style="font-size: 160px;"><%= format_value(@rows.first.keys.first, @rows.first.values.first) %></p>
|
35
|
+
<% else %>
|
36
|
+
<% @no_chart = true %>
|
37
|
+
<% end %>
|
26
38
|
<% end %>
|
27
39
|
|
28
40
|
<% unless @only_chart && !@no_chart %>
|
@@ -153,9 +153,17 @@
|
|
153
153
|
</div>
|
154
154
|
|
155
155
|
<script>
|
156
|
-
|
156
|
+
var request = $.ajax({
|
157
|
+
url: "<%= run_queries_path %>",
|
158
|
+
method: "POST",
|
159
|
+
data: <%= json_escape(variable_params.merge(statement: @statement, query_id: @query.id).to_json).html_safe %>,
|
160
|
+
dataType: "html"
|
161
|
+
}).done(function(data) {
|
157
162
|
$("#results").html(data);
|
158
163
|
$("#results table").stupidtable().stickyTableHeaders({fixedOffset: 60});
|
164
|
+
}).fail(function(jqXHR, textStatus, errorThrown) {
|
165
|
+
var message = (typeof errorThrown === "string") ? errorThrown : errorThrown.message;
|
166
|
+
$("#results").css("color", "red").html(message);
|
159
167
|
});
|
160
168
|
</script>
|
161
169
|
<% end %>
|
data/config/routes.rb
CHANGED
@@ -1,11 +1,12 @@
|
|
1
1
|
Blazer::Engine.routes.draw do
|
2
|
-
resources :queries
|
2
|
+
resources :queries do
|
3
3
|
post :run, on: :collection # err on the side of caution
|
4
|
+
post :refresh, on: :member
|
4
5
|
get :tables, on: :collection
|
5
6
|
end
|
6
7
|
resources :checks, except: [:show] do
|
7
8
|
get :run, on: :member
|
8
9
|
end
|
9
10
|
resources :dashboards
|
10
|
-
root to: "queries#
|
11
|
+
root to: "queries#home"
|
11
12
|
end
|
data/lib/blazer.rb
CHANGED
@@ -11,7 +11,9 @@ module Blazer
|
|
11
11
|
attr_reader :time_zone
|
12
12
|
attr_accessor :user_name
|
13
13
|
attr_accessor :user_class
|
14
|
+
attr_accessor :user_method
|
14
15
|
attr_accessor :from_email
|
16
|
+
attr_accessor :cache
|
15
17
|
end
|
16
18
|
self.audit = true
|
17
19
|
self.user_name = :name
|
@@ -43,7 +45,7 @@ module Blazer
|
|
43
45
|
tries = 0
|
44
46
|
# try 3 times on timeout errors
|
45
47
|
begin
|
46
|
-
rows, error = data_sources[check.query.data_source].run_statement(check.query.statement)
|
48
|
+
rows, error, cached_at = data_sources[check.query.data_source].run_statement(check.query.statement, refresh_cache: true)
|
47
49
|
tries += 1
|
48
50
|
end while error && error.include?("canceling statement due to statement timeout") && tries < 3
|
49
51
|
check.update_state(rows, error)
|
data/lib/blazer/data_source.rb
CHANGED
@@ -1,3 +1,5 @@
|
|
1
|
+
require "digest/md5"
|
2
|
+
|
1
3
|
module Blazer
|
2
4
|
class DataSource
|
3
5
|
attr_reader :id, :settings, :connection_model
|
@@ -34,37 +36,94 @@ module Blazer
|
|
34
36
|
settings["timeout"]
|
35
37
|
end
|
36
38
|
|
37
|
-
def
|
38
|
-
|
39
|
+
def cache
|
40
|
+
settings["cache"]
|
41
|
+
end
|
42
|
+
|
43
|
+
def use_transaction?
|
44
|
+
settings.key?("use_transaction") ? settings["use_transaction"] : true
|
45
|
+
end
|
46
|
+
|
47
|
+
def run_statement(statement, options = {})
|
48
|
+
rows = nil
|
39
49
|
error = nil
|
40
|
-
|
41
|
-
|
42
|
-
|
43
|
-
|
44
|
-
|
45
|
-
|
46
|
-
|
47
|
-
|
50
|
+
cached_at = nil
|
51
|
+
cache_key = self.cache_key(statement) if cache
|
52
|
+
if cache && !options[:refresh_cache]
|
53
|
+
value = Blazer.cache.read(cache_key)
|
54
|
+
rows, cached_at = Marshal.load(value) if value
|
55
|
+
end
|
56
|
+
|
57
|
+
unless rows
|
58
|
+
rows = []
|
59
|
+
|
60
|
+
comment = "blazer"
|
61
|
+
if options[:user].respond_to?(:id)
|
62
|
+
comment << ",user_id:#{options[:user].id}"
|
63
|
+
end
|
64
|
+
if options[:user].respond_to?(Blazer.user_name)
|
65
|
+
# only include letters, numbers, and spaces to prevent injection
|
66
|
+
comment << ",user_name:#{options[:user].send(Blazer.user_name).to_s.gsub(/[^a-zA-Z0-9 ]/, "")}"
|
67
|
+
end
|
68
|
+
if options[:query].respond_to?(:id)
|
69
|
+
comment << ",query_id:#{options[:query].id}"
|
70
|
+
end
|
71
|
+
|
72
|
+
in_transaction do
|
73
|
+
begin
|
74
|
+
connection_model.connection.execute("SET statement_timeout = #{timeout.to_i * 1000}") if timeout && postgresql?
|
75
|
+
result = connection_model.connection.select_all("#{statement} /*#{comment}*/")
|
76
|
+
result.each do |untyped_row|
|
77
|
+
row = {}
|
78
|
+
untyped_row.each do |k, v|
|
79
|
+
row[k] = result.column_types.empty? ? v : result.column_types[k].send(:type_cast, v)
|
80
|
+
end
|
81
|
+
rows << row
|
48
82
|
end
|
49
|
-
|
83
|
+
rescue ActiveRecord::StatementInvalid => e
|
84
|
+
error = e.message.sub(/.+ERROR: /, "")
|
50
85
|
end
|
51
|
-
raise ActiveRecord::Rollback
|
52
86
|
end
|
53
|
-
|
54
|
-
|
87
|
+
|
88
|
+
Blazer.cache.write(cache_key, Marshal.dump([rows, Time.now]), expires_in: cache.to_f * 60) if !error && cache
|
55
89
|
end
|
56
|
-
|
90
|
+
|
91
|
+
[rows, error, cached_at]
|
92
|
+
end
|
93
|
+
|
94
|
+
def clear_cache(statement)
|
95
|
+
Blazer.cache.delete(cache_key(statement))
|
96
|
+
end
|
97
|
+
|
98
|
+
def cache_key(statement)
|
99
|
+
["blazer", "v2", id, Digest::MD5.hexdigest(statement)].join("/")
|
57
100
|
end
|
58
101
|
|
59
102
|
def tables
|
60
103
|
default_schema = postgresql? ? "public" : connection_model.connection_config[:database]
|
61
104
|
schema = connection_model.connection_config[:schema] || default_schema
|
62
|
-
rows, error = run_statement(connection_model.send(:sanitize_sql_array, ["SELECT table_name, column_name, ordinal_position, data_type FROM information_schema.columns WHERE table_schema = ?", schema]))
|
105
|
+
rows, error, cached_at = run_statement(connection_model.send(:sanitize_sql_array, ["SELECT table_name, column_name, ordinal_position, data_type FROM information_schema.columns WHERE table_schema = ?", schema]))
|
63
106
|
Hash[rows.group_by { |r| r["table_name"] }.map { |t, f| [t, f.sort_by { |f| f["ordinal_position"] }.map { |f| f.slice("column_name", "data_type") }] }.sort_by { |t, _f| t }]
|
64
107
|
end
|
65
108
|
|
66
109
|
def postgresql?
|
67
110
|
["PostgreSQL", "Redshift"].include?(connection_model.connection.adapter_name)
|
68
111
|
end
|
112
|
+
|
113
|
+
protected
|
114
|
+
|
115
|
+
def in_transaction
|
116
|
+
if use_transaction?
|
117
|
+
connection_model.transaction do
|
118
|
+
begin
|
119
|
+
yield
|
120
|
+
ensure
|
121
|
+
raise ActiveRecord::Rollback
|
122
|
+
end
|
123
|
+
end
|
124
|
+
else
|
125
|
+
yield
|
126
|
+
end
|
127
|
+
end
|
69
128
|
end
|
70
129
|
end
|
data/lib/blazer/engine.rb
CHANGED
@@ -12,7 +12,14 @@ module Blazer
|
|
12
12
|
Blazer.from_email = Blazer.settings["from_email"] if Blazer.settings["from_email"]
|
13
13
|
|
14
14
|
Blazer.user_class ||= Blazer.settings["user_class"] || User rescue nil
|
15
|
+
Blazer.user_method = Blazer.settings["user_method"]
|
16
|
+
if Blazer.user_class
|
17
|
+
Blazer.user_method ||= "current_#{Blazer.user_class.to_s.downcase.singularize}"
|
18
|
+
end
|
19
|
+
|
15
20
|
Blazer::Query.belongs_to :creator, class_name: Blazer.user_class.to_s if Blazer.user_class
|
21
|
+
|
22
|
+
Blazer.cache ||= Rails.cache
|
16
23
|
end
|
17
24
|
end
|
18
25
|
end
|
data/lib/blazer/version.rb
CHANGED
@@ -4,7 +4,20 @@ data_sources:
|
|
4
4
|
main:
|
5
5
|
url: <%%= ENV["BLAZER_DATABASE_URL"] %>
|
6
6
|
|
7
|
-
# timeout
|
7
|
+
# statement timeout, in seconds
|
8
|
+
# applies to PostgreSQL only
|
9
|
+
# none by default
|
10
|
+
# timeout: 15
|
11
|
+
|
12
|
+
# time to cache results, in minutes
|
13
|
+
# can greatly improve speed
|
14
|
+
# none by default
|
15
|
+
# cache: 60
|
16
|
+
|
17
|
+
# wrap queries in a transaction for safety
|
18
|
+
# not necessary if you use a read-only user
|
19
|
+
# true by default
|
20
|
+
# use_transaction: false
|
8
21
|
|
9
22
|
smart_variables:
|
10
23
|
# zone_id: "SELECT id, name FROM zones ORDER BY name ASC"
|
@@ -26,3 +39,6 @@ audit: true
|
|
26
39
|
|
27
40
|
# method name for the user model
|
28
41
|
# user_name: name
|
42
|
+
|
43
|
+
# email to send checks from
|
44
|
+
# from_email: blazer@example.org
|
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.0.
|
4
|
+
version: 1.0.1
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Andrew Kane
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2015-10-
|
11
|
+
date: 2015-10-08 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: rails
|
@@ -128,9 +128,10 @@ files:
|
|
128
128
|
- app/views/blazer/dashboards/new.html.erb
|
129
129
|
- app/views/blazer/dashboards/show.html.erb
|
130
130
|
- app/views/blazer/queries/_form.html.erb
|
131
|
+
- app/views/blazer/queries/_index.html.erb
|
131
132
|
- app/views/blazer/queries/_tables.html
|
132
133
|
- app/views/blazer/queries/edit.html.erb
|
133
|
-
- app/views/blazer/queries/
|
134
|
+
- app/views/blazer/queries/home.html.erb
|
134
135
|
- app/views/blazer/queries/new.html.erb
|
135
136
|
- app/views/blazer/queries/run.html.erb
|
136
137
|
- app/views/blazer/queries/show.html.erb
|
@@ -170,4 +171,3 @@ signing_key:
|
|
170
171
|
specification_version: 4
|
171
172
|
summary: Share data effortlessly with your team
|
172
173
|
test_files: []
|
173
|
-
has_rdoc:
|
@@ -1,57 +0,0 @@
|
|
1
|
-
<div id="queries">
|
2
|
-
<div id="header" style="margin-bottom: 20px;">
|
3
|
-
<div class="pull-right">
|
4
|
-
<%= link_to "New Query", new_query_path, class: "btn btn-info" %>
|
5
|
-
<%= link_to "Dashboards", dashboards_path, class: "btn btn-primary" %>
|
6
|
-
<%= link_to "Checks", checks_path, class: "btn btn-primary" %>
|
7
|
-
</div>
|
8
|
-
<input type="text" placeholder="Start typing a query or person" style="width: 300px; display: inline-block;" autofocus=true class="search form-control" />
|
9
|
-
</div>
|
10
|
-
|
11
|
-
<table class="table">
|
12
|
-
<thead>
|
13
|
-
<tr>
|
14
|
-
<th>Query</th>
|
15
|
-
<th style="width: 20%; text-align: right;">Mastermind</th>
|
16
|
-
</tr>
|
17
|
-
</thead>
|
18
|
-
<tbody class="list">
|
19
|
-
<% @queries.each do |query| %>
|
20
|
-
<tr>
|
21
|
-
<td class="query">
|
22
|
-
<%= link_to query.name, query %>
|
23
|
-
<span style="color: #ccc;"><%= extract_vars(query.statement).join(", ") %></span>
|
24
|
-
<% if query.created_at > 2.days.ago %>
|
25
|
-
<small style="font-weight: bold; color: #5cb85c;">NEW</small>
|
26
|
-
<% end %>
|
27
|
-
<% if @trending_queries[query.id] %>
|
28
|
-
<small style="font-weight: bold; color: #f60;">TRENDING</small>
|
29
|
-
<% end %>
|
30
|
-
<% if @checks[query.id] %>
|
31
|
-
<small style="font-weight: bold; color: #f60;">CHECK</small>
|
32
|
-
<% end %>
|
33
|
-
<div class="hide"><%= query.name.gsub(/\s+/, "") %></div>
|
34
|
-
</td>
|
35
|
-
<td class="creator text-right text-muted">
|
36
|
-
<% if query.respond_to?(:creator) && (creator = query.creator) && creator.respond_to?(Blazer.user_name) %>
|
37
|
-
<% name = creator.send(Blazer.user_name) %>
|
38
|
-
<% if respond_to?(:current_user) and creator == current_user %>
|
39
|
-
You
|
40
|
-
<div class="hide">me <%= name %></div>
|
41
|
-
<% else %>
|
42
|
-
<%= name %>
|
43
|
-
<% end %>
|
44
|
-
<% end %>
|
45
|
-
</td>
|
46
|
-
</tr>
|
47
|
-
<% end %>
|
48
|
-
</tbody>
|
49
|
-
</table>
|
50
|
-
</div>
|
51
|
-
|
52
|
-
<script>
|
53
|
-
var options = {
|
54
|
-
valueNames: ['query', 'creator']
|
55
|
-
};
|
56
|
-
var userList = new List('queries', options);
|
57
|
-
</script>
|