sql-jarvis 2.0.1 → 2.0.2

Sign up to get free protection for your applications and to get access to all the features.
Files changed (83) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +25 -1
  3. data/LICENSE.txt +1 -1
  4. data/README.md +153 -52
  5. data/app/assets/fonts/blazer/glyphicons-halflings-regular.eot +0 -0
  6. data/app/assets/fonts/blazer/glyphicons-halflings-regular.svg +0 -0
  7. data/app/assets/fonts/blazer/glyphicons-halflings-regular.ttf +0 -0
  8. data/app/assets/fonts/blazer/glyphicons-halflings-regular.woff +0 -0
  9. data/app/assets/fonts/blazer/glyphicons-halflings-regular.woff2 +0 -0
  10. data/app/assets/images/blazer/favicon.png +0 -0
  11. data/app/assets/javascripts/blazer/Chart.js +4195 -3884
  12. data/app/assets/javascripts/blazer/Sortable.js +1493 -1097
  13. data/app/assets/javascripts/blazer/ace/ace.js +21294 -4
  14. data/app/assets/javascripts/blazer/ace/ext-language_tools.js +1991 -3
  15. data/app/assets/javascripts/blazer/ace/mode-sql.js +110 -1
  16. data/app/assets/javascripts/blazer/ace/snippets/sql.js +40 -1
  17. data/app/assets/javascripts/blazer/ace/snippets/text.js +14 -1
  18. data/app/assets/javascripts/blazer/ace/theme-twilight.js +116 -1
  19. data/app/assets/javascripts/blazer/application.js +4 -3
  20. data/app/assets/javascripts/blazer/bootstrap.js +623 -612
  21. data/app/assets/javascripts/blazer/chartkick.js +1769 -1248
  22. data/app/assets/javascripts/blazer/daterangepicker.js +263 -115
  23. data/app/assets/javascripts/blazer/highlight.min.js +3 -0
  24. data/app/assets/javascripts/blazer/{jquery_ujs.js → jquery-ujs.js} +161 -75
  25. data/app/assets/javascripts/blazer/jquery.js +9506 -9450
  26. data/app/assets/javascripts/blazer/jquery.stickytableheaders.js +321 -259
  27. data/app/assets/javascripts/blazer/moment-timezone-with-data.js +1212 -0
  28. data/app/assets/javascripts/blazer/queries.js +1 -1
  29. data/app/assets/javascripts/blazer/routes.js +3 -0
  30. data/app/assets/javascripts/blazer/selectize.js +3828 -3604
  31. data/app/assets/javascripts/blazer/stupidtable.js +255 -88
  32. data/app/assets/javascripts/blazer/vue.js +8015 -4583
  33. data/app/assets/stylesheets/blazer/application.css +41 -5
  34. data/app/assets/stylesheets/blazer/bootstrap.css.erb +879 -325
  35. data/app/assets/stylesheets/blazer/daterangepicker.css +269 -0
  36. data/app/assets/stylesheets/blazer/selectize.default.css +26 -10
  37. data/app/controllers/blazer/base_controller.rb +7 -0
  38. data/app/controllers/blazer/checks_controller.rb +1 -1
  39. data/app/controllers/blazer/dashboards_controller.rb +0 -4
  40. data/app/controllers/blazer/queries_controller.rb +20 -12
  41. data/app/helpers/blazer/base_helper.rb +1 -1
  42. data/app/mailers/blazer/slack_notifier.rb +76 -0
  43. data/app/models/blazer/check.rb +9 -0
  44. data/app/views/blazer/_nav.html.erb +0 -1
  45. data/app/views/blazer/_variables.html.erb +41 -19
  46. data/app/views/blazer/checks/_form.html.erb +16 -8
  47. data/app/views/blazer/checks/edit.html.erb +2 -0
  48. data/app/views/blazer/checks/index.html.erb +33 -4
  49. data/app/views/blazer/checks/new.html.erb +2 -0
  50. data/app/views/blazer/dashboards/_form.html.erb +4 -4
  51. data/app/views/blazer/dashboards/edit.html.erb +2 -0
  52. data/app/views/blazer/dashboards/new.html.erb +2 -0
  53. data/app/views/blazer/dashboards/show.html.erb +7 -3
  54. data/app/views/blazer/queries/_form.html.erb +11 -6
  55. data/app/views/blazer/queries/docs.html.erb +131 -0
  56. data/app/views/blazer/queries/home.html.erb +12 -3
  57. data/app/views/blazer/queries/run.html.erb +36 -6
  58. data/app/views/blazer/queries/schema.html.erb +46 -8
  59. data/app/views/blazer/queries/show.html.erb +4 -4
  60. data/app/views/layouts/blazer/application.html.erb +3 -3
  61. data/config/routes.rb +5 -1
  62. data/lib/blazer.rb +32 -13
  63. data/lib/blazer/adapters/athena_adapter.rb +1 -1
  64. data/lib/blazer/adapters/elasticsearch_adapter.rb +14 -17
  65. data/lib/blazer/adapters/mongodb_adapter.rb +1 -1
  66. data/lib/blazer/adapters/sql_adapter.rb +7 -1
  67. data/lib/blazer/engine.rb +4 -0
  68. data/lib/blazer/result.rb +62 -29
  69. data/lib/blazer/run_statement_job.rb +6 -9
  70. data/lib/blazer/version.rb +1 -1
  71. data/lib/generators/blazer/templates/config.yml.tt +13 -2
  72. data/lib/generators/blazer/templates/install.rb.tt +1 -0
  73. data/lib/tasks/blazer.rake +1 -0
  74. metadata +33 -37
  75. data/.gitattributes +0 -1
  76. data/.github/ISSUE_TEMPLATE.md +0 -7
  77. data/.gitignore +0 -14
  78. data/Gemfile +0 -7
  79. data/Rakefile +0 -1
  80. data/app/assets/javascripts/blazer/highlight.pack.js +0 -1
  81. data/app/assets/javascripts/blazer/moment-timezone.js +0 -1007
  82. data/app/assets/stylesheets/blazer/daterangepicker-bs3.css +0 -375
  83. data/blazer.gemspec +0 -30
@@ -0,0 +1,269 @@
1
+ .daterangepicker {
2
+ position: absolute;
3
+ color: inherit;
4
+ background-color: #fff;
5
+ border-radius: 4px;
6
+ width: 278px;
7
+ padding: 4px;
8
+ margin-top: 1px;
9
+ top: 100px;
10
+ left: 20px;
11
+ /* Calendars */ }
12
+ .daterangepicker:before, .daterangepicker:after {
13
+ position: absolute;
14
+ display: inline-block;
15
+ border-bottom-color: rgba(0, 0, 0, 0.2);
16
+ content: ''; }
17
+ .daterangepicker:before {
18
+ top: -7px;
19
+ border-right: 7px solid transparent;
20
+ border-left: 7px solid transparent;
21
+ border-bottom: 7px solid #ccc; }
22
+ .daterangepicker:after {
23
+ top: -6px;
24
+ border-right: 6px solid transparent;
25
+ border-bottom: 6px solid #fff;
26
+ border-left: 6px solid transparent; }
27
+ .daterangepicker.opensleft:before {
28
+ right: 9px; }
29
+ .daterangepicker.opensleft:after {
30
+ right: 10px; }
31
+ .daterangepicker.openscenter:before {
32
+ left: 0;
33
+ right: 0;
34
+ width: 0;
35
+ margin-left: auto;
36
+ margin-right: auto; }
37
+ .daterangepicker.openscenter:after {
38
+ left: 0;
39
+ right: 0;
40
+ width: 0;
41
+ margin-left: auto;
42
+ margin-right: auto; }
43
+ .daterangepicker.opensright:before {
44
+ left: 9px; }
45
+ .daterangepicker.opensright:after {
46
+ left: 10px; }
47
+ .daterangepicker.dropup {
48
+ margin-top: -5px; }
49
+ .daterangepicker.dropup:before {
50
+ top: initial;
51
+ bottom: -7px;
52
+ border-bottom: initial;
53
+ border-top: 7px solid #ccc; }
54
+ .daterangepicker.dropup:after {
55
+ top: initial;
56
+ bottom: -6px;
57
+ border-bottom: initial;
58
+ border-top: 6px solid #fff; }
59
+ .daterangepicker.dropdown-menu {
60
+ max-width: none;
61
+ z-index: 3001; }
62
+ .daterangepicker.single .ranges, .daterangepicker.single .calendar {
63
+ float: none; }
64
+ .daterangepicker.show-calendar .calendar {
65
+ display: block; }
66
+ .daterangepicker .calendar {
67
+ display: none;
68
+ max-width: 270px;
69
+ margin: 4px; }
70
+ .daterangepicker .calendar.single .calendar-table {
71
+ border: none; }
72
+ .daterangepicker .calendar th, .daterangepicker .calendar td {
73
+ white-space: nowrap;
74
+ text-align: center;
75
+ min-width: 32px; }
76
+ .daterangepicker .calendar-table {
77
+ border: 1px solid #fff;
78
+ padding: 4px;
79
+ border-radius: 4px;
80
+ background-color: #fff; }
81
+ .daterangepicker table {
82
+ width: 100%;
83
+ margin: 0; }
84
+ .daterangepicker td, .daterangepicker th {
85
+ text-align: center;
86
+ width: 20px;
87
+ height: 20px;
88
+ border-radius: 4px;
89
+ border: 1px solid transparent;
90
+ white-space: nowrap;
91
+ cursor: pointer; }
92
+ .daterangepicker td.available:hover, .daterangepicker th.available:hover {
93
+ background-color: #eee;
94
+ border-color: transparent;
95
+ color: inherit; }
96
+ .daterangepicker td.week, .daterangepicker th.week {
97
+ font-size: 80%;
98
+ color: #ccc; }
99
+ .daterangepicker td.off, .daterangepicker td.off.in-range, .daterangepicker td.off.start-date, .daterangepicker td.off.end-date {
100
+ background-color: #fff;
101
+ border-color: transparent;
102
+ color: #999; }
103
+ .daterangepicker td.in-range {
104
+ background-color: #ebf4f8;
105
+ border-color: transparent;
106
+ color: #000;
107
+ border-radius: 0; }
108
+ .daterangepicker td.start-date {
109
+ border-radius: 4px 0 0 4px; }
110
+ .daterangepicker td.end-date {
111
+ border-radius: 0 4px 4px 0; }
112
+ .daterangepicker td.start-date.end-date {
113
+ border-radius: 4px; }
114
+ .daterangepicker td.active, .daterangepicker td.active:hover {
115
+ background-color: #357ebd;
116
+ border-color: transparent;
117
+ color: #fff; }
118
+ .daterangepicker th.month {
119
+ width: auto; }
120
+ .daterangepicker td.disabled, .daterangepicker option.disabled {
121
+ color: #999;
122
+ cursor: not-allowed;
123
+ text-decoration: line-through; }
124
+ .daterangepicker select.monthselect, .daterangepicker select.yearselect {
125
+ font-size: 12px;
126
+ padding: 1px;
127
+ height: auto;
128
+ margin: 0;
129
+ cursor: default; }
130
+ .daterangepicker select.monthselect {
131
+ margin-right: 2%;
132
+ width: 56%; }
133
+ .daterangepicker select.yearselect {
134
+ width: 40%; }
135
+ .daterangepicker select.hourselect, .daterangepicker select.minuteselect, .daterangepicker select.secondselect, .daterangepicker select.ampmselect {
136
+ width: 50px;
137
+ margin-bottom: 0; }
138
+ .daterangepicker .input-mini {
139
+ border: 1px solid #ccc;
140
+ border-radius: 4px;
141
+ color: #555;
142
+ height: 30px;
143
+ line-height: 30px;
144
+ display: block;
145
+ vertical-align: middle;
146
+ margin: 0 0 5px 0;
147
+ padding: 0 6px 0 28px;
148
+ width: 100%; }
149
+ .daterangepicker .input-mini.active {
150
+ border: 1px solid #08c;
151
+ border-radius: 4px; }
152
+ .daterangepicker .daterangepicker_input {
153
+ position: relative; }
154
+ .daterangepicker .daterangepicker_input i {
155
+ position: absolute;
156
+ left: 8px;
157
+ top: 8px; }
158
+ .daterangepicker.rtl .input-mini {
159
+ padding-right: 28px;
160
+ padding-left: 6px; }
161
+ .daterangepicker.rtl .daterangepicker_input i {
162
+ left: auto;
163
+ right: 8px; }
164
+ .daterangepicker .calendar-time {
165
+ text-align: center;
166
+ margin: 5px auto;
167
+ line-height: 30px;
168
+ position: relative;
169
+ padding-left: 28px; }
170
+ .daterangepicker .calendar-time select.disabled {
171
+ color: #ccc;
172
+ cursor: not-allowed; }
173
+
174
+ .ranges {
175
+ font-size: 11px;
176
+ float: none;
177
+ margin: 4px;
178
+ text-align: left; }
179
+ .ranges ul {
180
+ list-style: none;
181
+ margin: 0 auto;
182
+ padding: 0;
183
+ width: 100%; }
184
+ .ranges li {
185
+ font-size: 13px;
186
+ background-color: #f5f5f5;
187
+ border: 1px solid #f5f5f5;
188
+ border-radius: 4px;
189
+ color: #08c;
190
+ padding: 3px 12px;
191
+ margin-bottom: 8px;
192
+ cursor: pointer; }
193
+ .ranges li:hover {
194
+ background-color: #08c;
195
+ border: 1px solid #08c;
196
+ color: #fff; }
197
+ .ranges li.active {
198
+ background-color: #08c;
199
+ border: 1px solid #08c;
200
+ color: #fff; }
201
+
202
+ /* Larger Screen Styling */
203
+ @media (min-width: 564px) {
204
+ .daterangepicker {
205
+ width: auto; }
206
+ .daterangepicker .ranges ul {
207
+ width: 160px; }
208
+ .daterangepicker.single .ranges ul {
209
+ width: 100%; }
210
+ .daterangepicker.single .calendar.left {
211
+ clear: none; }
212
+ .daterangepicker.single.ltr .ranges, .daterangepicker.single.ltr .calendar {
213
+ float: left; }
214
+ .daterangepicker.single.rtl .ranges, .daterangepicker.single.rtl .calendar {
215
+ float: right; }
216
+ .daterangepicker.ltr {
217
+ direction: ltr;
218
+ text-align: left; }
219
+ .daterangepicker.ltr .calendar.left {
220
+ clear: left;
221
+ margin-right: 0; }
222
+ .daterangepicker.ltr .calendar.left .calendar-table {
223
+ border-right: none;
224
+ border-top-right-radius: 0;
225
+ border-bottom-right-radius: 0; }
226
+ .daterangepicker.ltr .calendar.right {
227
+ margin-left: 0; }
228
+ .daterangepicker.ltr .calendar.right .calendar-table {
229
+ border-left: none;
230
+ border-top-left-radius: 0;
231
+ border-bottom-left-radius: 0; }
232
+ .daterangepicker.ltr .left .daterangepicker_input {
233
+ padding-right: 12px; }
234
+ .daterangepicker.ltr .calendar.left .calendar-table {
235
+ padding-right: 12px; }
236
+ .daterangepicker.ltr .ranges, .daterangepicker.ltr .calendar {
237
+ float: left; }
238
+ .daterangepicker.rtl {
239
+ direction: rtl;
240
+ text-align: right; }
241
+ .daterangepicker.rtl .calendar.left {
242
+ clear: right;
243
+ margin-left: 0; }
244
+ .daterangepicker.rtl .calendar.left .calendar-table {
245
+ border-left: none;
246
+ border-top-left-radius: 0;
247
+ border-bottom-left-radius: 0; }
248
+ .daterangepicker.rtl .calendar.right {
249
+ margin-right: 0; }
250
+ .daterangepicker.rtl .calendar.right .calendar-table {
251
+ border-right: none;
252
+ border-top-right-radius: 0;
253
+ border-bottom-right-radius: 0; }
254
+ .daterangepicker.rtl .left .daterangepicker_input {
255
+ padding-left: 12px; }
256
+ .daterangepicker.rtl .calendar.left .calendar-table {
257
+ padding-left: 12px; }
258
+ .daterangepicker.rtl .ranges, .daterangepicker.rtl .calendar {
259
+ text-align: right;
260
+ float: right; } }
261
+ @media (min-width: 730px) {
262
+ .daterangepicker .ranges {
263
+ width: auto; }
264
+ .daterangepicker.ltr .ranges {
265
+ float: left; }
266
+ .daterangepicker.rtl .ranges {
267
+ float: right; }
268
+ .daterangepicker .calendar.left {
269
+ clear: none !important; } }
@@ -1,5 +1,5 @@
1
1
  /**
2
- * selectize.default.css (v0.12.1) - Default Theme
2
+ * selectize.default.css (v0.12.6) - Default Theme
3
3
  * Copyright (c) 2013–2015 Brian Reavis & contributors
4
4
  *
5
5
  * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this
@@ -18,8 +18,8 @@
18
18
  background: #f2f2f2 !important;
19
19
  background: rgba(0, 0, 0, 0.06) !important;
20
20
  border: 0 none !important;
21
- -webkit-box-shadow: inset 0 0 12px 4px #ffffff;
22
- box-shadow: inset 0 0 12px 4px #ffffff;
21
+ -webkit-box-shadow: inset 0 0 12px 4px #fff;
22
+ box-shadow: inset 0 0 12px 4px #fff;
23
23
  }
24
24
  .selectize-control.plugin-drag_drop .ui-sortable-placeholder::after {
25
25
  content: '!';
@@ -108,6 +108,12 @@
108
108
  .selectize-control.plugin-remove_button .disabled [data-value] .remove {
109
109
  border-left-color: #aaaaaa;
110
110
  }
111
+ .selectize-control.plugin-remove_button .remove-single {
112
+ position: absolute;
113
+ right: 0;
114
+ top: 0;
115
+ font-size: 23px;
116
+ }
111
117
  .selectize-control {
112
118
  position: relative;
113
119
  }
@@ -122,7 +128,7 @@
122
128
  }
123
129
  .selectize-input,
124
130
  .selectize-control.single .selectize-input.input-active {
125
- background: #ffffff;
131
+ background: #fff;
126
132
  cursor: text;
127
133
  display: inline-block;
128
134
  }
@@ -147,7 +153,7 @@
147
153
  padding: 5px 8px 2px;
148
154
  }
149
155
  .selectize-input.full {
150
- background-color: #ffffff;
156
+ background-color: #fff;
151
157
  }
152
158
  .selectize-input.disabled,
153
159
  .selectize-input.disabled * {
@@ -174,12 +180,12 @@
174
180
  margin: 0 3px 3px 0;
175
181
  padding: 2px 6px;
176
182
  background: #1da7ee;
177
- color: #ffffff;
183
+ color: #fff;
178
184
  border: 1px solid #0073bb;
179
185
  }
180
186
  .selectize-control.multi .selectize-input > div.active {
181
187
  background: #92c836;
182
- color: #ffffff;
188
+ color: #fff;
183
189
  border: 1px solid #00578d;
184
190
  }
185
191
  .selectize-control.multi .selectize-input.disabled > div,
@@ -228,7 +234,7 @@
228
234
  position: absolute;
229
235
  z-index: 10;
230
236
  border: 1px solid #d0d0d0;
231
- background: #ffffff;
237
+ background: #fff;
232
238
  margin: -1px 0 0 0;
233
239
  border-top: 0 none;
234
240
  -webkit-box-sizing: border-box;
@@ -250,16 +256,25 @@
250
256
  -moz-border-radius: 1px;
251
257
  border-radius: 1px;
252
258
  }
253
- .selectize-dropdown [data-selectable],
259
+ .selectize-dropdown .option,
254
260
  .selectize-dropdown .optgroup-header {
255
261
  padding: 5px 8px;
256
262
  }
263
+ .selectize-dropdown .option,
264
+ .selectize-dropdown [data-disabled],
265
+ .selectize-dropdown [data-disabled] [data-selectable].option {
266
+ cursor: inherit;
267
+ opacity: 0.5;
268
+ }
269
+ .selectize-dropdown [data-selectable].option {
270
+ opacity: 1;
271
+ }
257
272
  .selectize-dropdown .optgroup:first-child .optgroup-header {
258
273
  border-top: 0 none;
259
274
  }
260
275
  .selectize-dropdown .optgroup-header {
261
276
  color: #303030;
262
- background: #ffffff;
277
+ background: #fff;
263
278
  cursor: default;
264
279
  }
265
280
  .selectize-dropdown .active {
@@ -276,6 +291,7 @@
276
291
  overflow-y: auto;
277
292
  overflow-x: hidden;
278
293
  max-height: 200px;
294
+ -webkit-overflow-scrolling: touch;
279
295
  }
280
296
  .selectize-control.single .selectize-input,
281
297
  .selectize-control.single .selectize-input input {
@@ -24,6 +24,12 @@ module Blazer
24
24
  before_action Blazer.before_action.to_sym
25
25
  end
26
26
 
27
+ if Blazer.override_csp
28
+ after_action do
29
+ response.headers['Content-Security-Policy'] = "default-src 'self' https: 'unsafe-inline' 'unsafe-eval' data:"
30
+ end
31
+ end
32
+
27
33
  layout "blazer/application"
28
34
 
29
35
  private
@@ -57,6 +63,7 @@ module Blazer
57
63
  value = value.to_f
58
64
  end
59
65
  end
66
+ value = Blazer.transform_variable.call(var, value) if Blazer.transform_variable
60
67
  if value.is_a?(Array)
61
68
  var_value = value.map{|v| ActiveRecord::Base.connection.quote(v)}.join(', ')
62
69
  else
@@ -46,7 +46,7 @@ module Blazer
46
46
  private
47
47
 
48
48
  def check_params
49
- params.require(:check).permit(:query_id, :emails, :invert, :check_type, :schedule)
49
+ params.require(:check).permit(:query_id, :emails, :slack_channels, :invert, :check_type, :schedule)
50
50
  end
51
51
 
52
52
  def set_check
@@ -2,10 +2,6 @@ module Blazer
2
2
  class DashboardsController < BaseController
3
3
  before_action :set_dashboard, only: [:show, :edit, :update, :destroy, :refresh]
4
4
 
5
- def index
6
- redirect_to root_path(filter: "dashboards")
7
- end
8
-
9
5
  def new
10
6
  @dashboard = Blazer::Dashboard.new
11
7
  end
@@ -1,17 +1,13 @@
1
1
  module Blazer
2
2
  class QueriesController < BaseController
3
3
  before_action :set_query, only: [:show, :edit, :update, :destroy, :refresh]
4
- before_action :set_data_source, only: [:tables, :schema, :cancel, :columns]
4
+ before_action :set_data_source, only: [:tables, :docs, :schema, :cancel, :columns]
5
5
  before_action :set_assignees, only: [:new, :create, :show, :edit, :update, :destroy, :refresh]
6
6
 
7
7
  def home
8
- if params[:filter] == "dashboards"
9
- @queries = []
10
- else
11
- set_queries(1000)
12
- end
8
+ set_queries(1000)
13
9
 
14
- if params[:filter] && params[:filter] != "dashboards"
10
+ if params[:filter]
15
11
  @dashboards = [] # TODO show my dashboards
16
12
  else
17
13
  @dashboards = Blazer::Dashboard.order(:name)
@@ -120,14 +116,13 @@ module Blazer
120
116
 
121
117
  options = {user: blazer_user, query: @query, refresh_cache: params[:check], run_id: @run_id, async: Blazer.async}
122
118
  if Blazer.async && request.format.symbol != :csv
123
- result = []
124
- Blazer::RunStatementJob.perform_async(result, @data_source, @statement, options)
119
+ Blazer::RunStatementJob.perform_later(@data_source.id, @statement, options)
125
120
  wait_start = Time.now
126
121
  loop do
127
- sleep(0.02)
128
- break if result.any? || Time.now - wait_start > 3
122
+ sleep(0.1)
123
+ @result = @data_source.run_results(@run_id)
124
+ break if @result || Time.now - wait_start > 3
129
125
  end
130
- @result = result.first
131
126
  else
132
127
  @result = Blazer::RunStatement.new.perform(@data_source, @statement, options)
133
128
  end
@@ -141,6 +136,13 @@ module Blazer
141
136
  @cached_at = @result.cached_at
142
137
  @just_cached = @result.just_cached
143
138
 
139
+ @forecast = @query && @result.forecastable? && params[:forecast]
140
+ if @forecast
141
+ @result.forecast
142
+ @forecast_error = @result.forecast_error
143
+ @forecast = @forecast_error.nil?
144
+ end
145
+
144
146
  render_run
145
147
  else
146
148
  @timestamp = Time.now.to_i
@@ -184,6 +186,12 @@ module Blazer
184
186
  render json: @data_source.tables
185
187
  end
186
188
 
189
+ def docs
190
+ @smart_variables = @data_source.smart_variables
191
+ @linked_columns = @data_source.linked_columns
192
+ @smart_columns = @data_source.smart_columns
193
+ end
194
+
187
195
  def columns
188
196
  column_names = []
189
197
  @data_source.schema.map do |t|