adhoq 0.0.5 → 0.0.6
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/README.md +6 -6
- data/app/assets/javascripts/adhoq/current_tables.js.coffee +17 -3
- data/app/assets/javascripts/adhoq/previewer.js.coffee +10 -1
- data/app/assets/stylesheets/adhoq/adhoq.css.sass +38 -8
- data/app/controllers/adhoq/application_controller.rb +2 -0
- data/app/controllers/adhoq/current_tables_controller.rb +4 -1
- data/app/controllers/adhoq/executions_controller.rb +24 -3
- data/app/controllers/adhoq/explains_controller.rb +1 -1
- data/app/controllers/adhoq/previews_controller.rb +1 -1
- data/app/controllers/adhoq/queries_controller.rb +1 -1
- data/app/decorators/adhoq/execution_decorator.rb +20 -0
- data/app/decorators/adhoq/query_decorator.rb +9 -0
- data/app/helpers/adhoq/application_helper.rb +12 -2
- data/app/jobs/adhoq/execute_job.rb +11 -0
- data/app/models/adhoq/execution.rb +4 -6
- data/app/models/adhoq/report.rb +8 -0
- data/app/views/adhoq/current_tables/index.html.slim +10 -8
- data/app/views/adhoq/queries/_current_tables_leftbar.html.slim +9 -0
- data/app/views/adhoq/queries/_execution.html.slim +10 -0
- data/app/views/adhoq/queries/_form.html.slim +49 -30
- data/app/views/adhoq/queries/_queries.html.slim +14 -0
- data/app/views/adhoq/queries/_query.html.slim +20 -24
- data/app/views/adhoq/queries/edit.html.slim +11 -2
- data/app/views/adhoq/queries/index.html.slim +11 -1
- data/app/views/adhoq/queries/new.html.slim +10 -2
- data/app/views/adhoq/queries/show.html.slim +11 -1
- data/app/views/layouts/adhoq/application.html.slim +1 -4
- data/db/migrate/20141003095645_create_adhoq_queries.rb +1 -1
- data/db/migrate/20141006014750_create_adhoq_executions.rb +1 -1
- data/db/migrate/20141007052308_create_adhoq_reports.rb +1 -1
- data/lib/adhoq.rb +1 -0
- data/lib/adhoq/configuration.rb +10 -0
- data/lib/adhoq/engine.rb +1 -0
- data/lib/adhoq/executor.rb +4 -28
- data/lib/adhoq/executor/connection_wrapper.rb +32 -0
- data/lib/adhoq/global_variable.rb +1 -0
- data/lib/adhoq/storage.rb +1 -0
- data/lib/adhoq/storage/fog_storage.rb +4 -0
- data/lib/adhoq/storage/on_the_fly.rb +32 -0
- data/lib/adhoq/storage/s3.rb +26 -2
- data/lib/adhoq/version.rb +1 -1
- data/spec/adhoq/executor/connection_wrapper_spec.rb +16 -0
- data/spec/adhoq/executor_spec.rb +0 -13
- data/spec/adhoq/storage_spec.rb +14 -0
- data/spec/models/adhoq/execution_spec.rb +21 -0
- data/spec/support/activejob_helper.rb +26 -0
- metadata +43 -4
- data/app/views/adhoq/application/_sidebar_queries_index.html.slim +0 -10
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA1:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: e44fcb3a79e299b2a337652f8439d58cd4babe71
|
|
4
|
+
data.tar.gz: 5312feb5fee5890cae4b27f48c541a36b937ef0e
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 19546d4c89ab4cbb7da38b46e02a7cc4bfe091d1d00bbd3110d186a26586f929cdccdab8bf2bf12f322cfaf052297d102eefcfe3660c47012da952a0464baae9
|
|
7
|
+
data.tar.gz: 914e24aabd3a6a896c62e3c276642f8a6ba37d85d8d6f285363e5c1dbf6b4a3025f714f065989475b4042edfb8ab513ef2462974fb45214fa754c5f640ef345e
|
data/README.md
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
Adhoq [](https://travis-ci.org/esminc/adhoq) [](https://codeclimate.com/github/esminc/adhoq)
|
|
1
|
+
Adhoq [](https://travis-ci.org/esminc/adhoq) [](https://codeclimate.com/github/esminc/adhoq) [](https://codeclimate.com/github/esminc/adhoq/coverage)
|
|
2
2
|
====
|
|
3
3
|
|
|
4
4
|
Rails engine to generate instant reports from adhoc SQL query.
|
|
@@ -7,16 +7,16 @@ Rails engine to generate instant reports from adhoc SQL query.
|
|
|
7
7
|
|
|
8
8
|
## Features
|
|
9
9
|
|
|
10
|
-
- Export ad-hoc SQL
|
|
10
|
+
- Export ad-hoc SQL reports in some formats:
|
|
11
|
+
- .csv
|
|
12
|
+
- .json
|
|
13
|
+
- .xlsx
|
|
11
14
|
- Persist generated report as local file or in AWS S3
|
|
12
15
|
- Rails 4.x & 3.2 support
|
|
13
16
|
- Nice administration console with rails engine
|
|
14
17
|
|
|
15
18
|
### Future planning
|
|
16
19
|
|
|
17
|
-
- Export reports in some formats:
|
|
18
|
-
- [ ] .csv
|
|
19
|
-
- [ ] .json
|
|
20
20
|
- [ ] Label data substitution
|
|
21
21
|
|
|
22
22
|
## Installation
|
|
@@ -56,7 +56,7 @@ Rails.application.routes.draw do
|
|
|
56
56
|
end
|
|
57
57
|
```
|
|
58
58
|
|
|
59
|
-
Edit initialization file in `config/
|
|
59
|
+
Edit initialization file in `config/initializers/adhoq.rb`
|
|
60
60
|
|
|
61
61
|
```ruby
|
|
62
62
|
Adhoq.configure do |config|
|
|
@@ -1,4 +1,18 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
1
|
+
loadCurrentTableTabOnce = ($el)->
|
|
2
|
+
$el.load($el.find('a.loading').attr('href'))
|
|
3
|
+
|
|
4
|
+
Adhoq.toggleCurrentTables = (action, elements)->
|
|
5
|
+
loadCurrentTableTabOnce($('#current-tables'))
|
|
6
|
+
|
|
7
|
+
$main = $(elements.main)
|
|
8
|
+
$tables = $(elements.tables)
|
|
9
|
+
|
|
10
|
+
if action is 'show'
|
|
11
|
+
$main.addClass('col-md-6').removeClass('col-md-12')
|
|
12
|
+
$tables.addClass('col-md-6').show()
|
|
13
|
+
else
|
|
14
|
+
$main.addClass('col-md-12').removeClass('col-md-6')
|
|
15
|
+
$tables.addClass('col-md-6').hide()
|
|
16
|
+
|
|
17
|
+
true
|
|
3
18
|
|
|
4
|
-
pane.load(pane.find('a.loading').attr('href'))
|
|
@@ -2,8 +2,10 @@ class Previewer
|
|
|
2
2
|
constructor: (@el)->
|
|
3
3
|
|
|
4
4
|
init: ->
|
|
5
|
+
@el.on 'adhoq:updatePreview', => @update()
|
|
6
|
+
|
|
5
7
|
@el.on 'click', =>
|
|
6
|
-
@
|
|
8
|
+
@el.trigger 'adhoq:updatePreview'
|
|
7
9
|
false
|
|
8
10
|
|
|
9
11
|
update: ->
|
|
@@ -23,3 +25,10 @@ class Previewer
|
|
|
23
25
|
|
|
24
26
|
Adhoq.enablePreview = ($el)->
|
|
25
27
|
(new Previewer($el)).init()
|
|
28
|
+
|
|
29
|
+
Adhoq.enablePreviewKeybordShortCut= ($textarea, previewSelector)->
|
|
30
|
+
$textarea.on 'keyup', (ev)->
|
|
31
|
+
if(ev.ctrlKey && ev.keyCode is 82)
|
|
32
|
+
$(previewSelector).trigger('adhoq:updatePreview')
|
|
33
|
+
|
|
34
|
+
false
|
|
@@ -21,22 +21,41 @@ $short-span: $font-size-base / 2
|
|
|
21
21
|
margin: 0 10px
|
|
22
22
|
font-size: $font-size-base
|
|
23
23
|
|
|
24
|
-
#
|
|
25
|
-
|
|
26
|
-
|
|
24
|
+
#queries
|
|
25
|
+
a.new-query
|
|
26
|
+
margin-bottom: $short-span
|
|
27
27
|
|
|
28
|
-
|
|
28
|
+
ol.queries-index
|
|
29
29
|
li.panel
|
|
30
30
|
margin-bottom: $short-span
|
|
31
31
|
|
|
32
|
-
.panel-heading
|
|
33
|
-
|
|
34
|
-
|
|
32
|
+
.panel-heading
|
|
33
|
+
padding: $short-span $short-span * 2
|
|
34
|
+
h2
|
|
35
|
+
margin: 0
|
|
36
|
+
font-size: $font-size-base
|
|
35
37
|
|
|
36
38
|
p.panel-body.description
|
|
37
39
|
margin-bottom: 0
|
|
40
|
+
font-size: $font-size-base * 0.9
|
|
41
|
+
|
|
42
|
+
#the-query
|
|
43
|
+
.page-header
|
|
44
|
+
margin-top: 0
|
|
45
|
+
h1
|
|
46
|
+
margin-top: 0
|
|
47
|
+
|
|
48
|
+
small
|
|
49
|
+
font-size: $font-size-base
|
|
38
50
|
|
|
39
51
|
form.query-form
|
|
52
|
+
margin-bottom: 30px
|
|
53
|
+
|
|
54
|
+
h1
|
|
55
|
+
font-size: $font-size-base
|
|
56
|
+
margin-top: $font-size-base / 2
|
|
57
|
+
margin-bottom: $font-size-base / 4
|
|
58
|
+
|
|
40
59
|
textarea#query_query
|
|
41
60
|
font-family: monospace
|
|
42
61
|
|
|
@@ -57,10 +76,17 @@ form.query-form
|
|
|
57
76
|
padding-left: $short-span / 2
|
|
58
77
|
|
|
59
78
|
#current-tables
|
|
79
|
+
display: none
|
|
60
80
|
font-size: $font-size-base * 0.9
|
|
61
81
|
|
|
82
|
+
h3
|
|
83
|
+
font-size: $font-size-base
|
|
84
|
+
margin-top: $font-size-base / 2
|
|
85
|
+
margin-bottom: $font-size-base / 4
|
|
86
|
+
|
|
87
|
+
small
|
|
88
|
+
margin-left: $font-size-base
|
|
62
89
|
table
|
|
63
|
-
width: 800px
|
|
64
90
|
|
|
65
91
|
caption
|
|
66
92
|
text-align: left
|
|
@@ -95,7 +121,11 @@ form.query-form
|
|
|
95
121
|
|
|
96
122
|
.tab-pane > h3
|
|
97
123
|
font-size: $font-size-base * 1.5
|
|
124
|
+
margin-top: $font-size-base / 2
|
|
98
125
|
|
|
99
126
|
small
|
|
100
127
|
font-size: $font-size-base
|
|
101
128
|
margin-left: $font-size-base
|
|
129
|
+
|
|
130
|
+
.js-preview-result table, .js-explain-result pre
|
|
131
|
+
font-family: monospace
|
|
@@ -3,8 +3,11 @@ module Adhoq
|
|
|
3
3
|
before_filter :eager_load_models
|
|
4
4
|
|
|
5
5
|
def index
|
|
6
|
+
hidden_model_names = Array(Adhoq.config.hidden_model_names)
|
|
7
|
+
hidden_model_names << 'ActiveRecord::SchemaMigration'
|
|
8
|
+
|
|
6
9
|
@ar_classes = ActiveRecord::Base.subclasses.
|
|
7
|
-
reject {|klass| klass.name
|
|
10
|
+
reject {|klass| hidden_model_names.include?(klass.name) }.
|
|
8
11
|
sort_by(&:name)
|
|
9
12
|
|
|
10
13
|
render layout: false
|
|
@@ -7,19 +7,40 @@ module Adhoq
|
|
|
7
7
|
end
|
|
8
8
|
|
|
9
9
|
def create
|
|
10
|
+
async_execution? ? asynced_create : synced_create
|
|
11
|
+
end
|
|
12
|
+
|
|
13
|
+
private
|
|
14
|
+
|
|
15
|
+
def synced_create
|
|
10
16
|
@execution = current_query.execute!(params[:execution][:report_format])
|
|
11
17
|
|
|
12
|
-
|
|
18
|
+
if @execution.report.on_the_fly?
|
|
19
|
+
respond_report(@execution.report)
|
|
20
|
+
else
|
|
21
|
+
redirect_to current_query
|
|
22
|
+
end
|
|
13
23
|
end
|
|
14
24
|
|
|
15
|
-
|
|
25
|
+
def asynced_create
|
|
26
|
+
Adhoq::ExecuteJob.perform_later(current_query, params[:execution][:report_format])
|
|
27
|
+
redirect_to current_query
|
|
28
|
+
end
|
|
16
29
|
|
|
17
30
|
def current_query
|
|
18
31
|
@query ||= Adhoq::Query.find(params[:query_id])
|
|
19
32
|
end
|
|
20
33
|
|
|
21
34
|
def respond_report(report)
|
|
22
|
-
|
|
35
|
+
if Adhoq.current_storage.direct_download?
|
|
36
|
+
redirect_to report.data_url
|
|
37
|
+
else
|
|
38
|
+
send_data report.data, type: report.mime_type, filename: report.name, disposition: 'attachment'
|
|
39
|
+
end
|
|
40
|
+
end
|
|
41
|
+
|
|
42
|
+
def async_execution?
|
|
43
|
+
Adhoq.config.async_execution? && !Adhoq.current_storage.is_a?(Adhoq::Storage::OnTheFly)
|
|
23
44
|
end
|
|
24
45
|
end
|
|
25
46
|
end
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
module Adhoq::ExecutionDecorator
|
|
2
|
+
def status_label
|
|
3
|
+
content_tag :span, class: ["label", status_label_class] do
|
|
4
|
+
status
|
|
5
|
+
end
|
|
6
|
+
end
|
|
7
|
+
|
|
8
|
+
def status_label_class
|
|
9
|
+
case status
|
|
10
|
+
when "success"
|
|
11
|
+
"label-success"
|
|
12
|
+
when "failure"
|
|
13
|
+
"label-danger"
|
|
14
|
+
when "requested"
|
|
15
|
+
"label-default"
|
|
16
|
+
else
|
|
17
|
+
"label-default"
|
|
18
|
+
end
|
|
19
|
+
end
|
|
20
|
+
end
|
|
@@ -16,9 +16,19 @@ module Adhoq
|
|
|
16
16
|
if defined?(ActiveRecord::SchemaMigration)
|
|
17
17
|
ActiveRecord::SchemaMigration.maximum(:version)
|
|
18
18
|
else
|
|
19
|
-
|
|
20
|
-
result.
|
|
19
|
+
connection = Adhoq::Executor::ConnectionWrapper.new
|
|
20
|
+
result = connection.select("SELECT MAX(version) AS current_version FROM #{ActiveRecord::Migrator.schema_migrations_table_name}")
|
|
21
|
+
result.rows.first.first
|
|
21
22
|
end
|
|
22
23
|
end
|
|
24
|
+
|
|
25
|
+
# TODO extract into presenter
|
|
26
|
+
def query_friendly_name(query)
|
|
27
|
+
"Query: #{query.name}"
|
|
28
|
+
end
|
|
29
|
+
|
|
30
|
+
def table_order_key(ar_class)
|
|
31
|
+
ar_class.primary_key || ar_class.columns.first.name
|
|
32
|
+
end
|
|
23
33
|
end
|
|
24
34
|
end
|
|
@@ -9,6 +9,9 @@ module Adhoq
|
|
|
9
9
|
|
|
10
10
|
def generate_report!
|
|
11
11
|
build_report.generate!
|
|
12
|
+
update_attributes(status: :success)
|
|
13
|
+
rescue
|
|
14
|
+
update_attributes(status: :failure)
|
|
12
15
|
end
|
|
13
16
|
|
|
14
17
|
def name
|
|
@@ -16,12 +19,7 @@ module Adhoq
|
|
|
16
19
|
end
|
|
17
20
|
|
|
18
21
|
def success?
|
|
19
|
-
report.try(:available?)
|
|
20
|
-
end
|
|
21
|
-
|
|
22
|
-
# TODO go decorator or view model or so
|
|
23
|
-
def status_label
|
|
24
|
-
success? ? :success : :failure
|
|
22
|
+
report.try(:available?) || status == "success"
|
|
25
23
|
end
|
|
26
24
|
end
|
|
27
25
|
end
|
data/app/models/adhoq/report.rb
CHANGED
|
@@ -12,6 +12,10 @@ module Adhoq
|
|
|
12
12
|
save!
|
|
13
13
|
end
|
|
14
14
|
|
|
15
|
+
def on_the_fly?
|
|
16
|
+
storage.start_with?(Adhoq::Storage::OnTheFly::PREFIX)
|
|
17
|
+
end
|
|
18
|
+
|
|
15
19
|
def available?
|
|
16
20
|
identifier.present? && (storage == Adhoq.current_storage.identifier)
|
|
17
21
|
end
|
|
@@ -20,6 +24,10 @@ module Adhoq
|
|
|
20
24
|
storage.get(identifier)
|
|
21
25
|
end
|
|
22
26
|
|
|
27
|
+
def data_url(storage = Adhoq.current_storage)
|
|
28
|
+
storage.get_url(self)
|
|
29
|
+
end
|
|
30
|
+
|
|
23
31
|
def mime_type
|
|
24
32
|
Adhoq::Reporter.lookup(execution.report_format).mime_type
|
|
25
33
|
end
|
|
@@ -1,10 +1,14 @@
|
|
|
1
1
|
h3
|
|
2
|
-
|
|
2
|
+
i.fa.fa-database.fa-pad-r
|
|
3
|
+
| Current tables
|
|
3
4
|
small= "Version #{schema_version}"
|
|
5
|
+
.pull-right
|
|
6
|
+
button.close[data-trigger="toggleCurrentTables" role='close']
|
|
7
|
+
span[aria-hidden=true] ×
|
|
4
8
|
|
|
5
9
|
ul.list-unstyled.tables
|
|
6
10
|
- @ar_classes.each do |ar_class|
|
|
7
|
-
- first_record = ar_class.unscoped.order(
|
|
11
|
+
- first_record = ar_class.unscoped.order(table_order_key(ar_class)).first
|
|
8
12
|
|
|
9
13
|
li.ar_class data-table-name=ar_class.table_name
|
|
10
14
|
table.table.table-striped.table-hover.table-bordered
|
|
@@ -14,12 +18,11 @@ ul.list-unstyled.tables
|
|
|
14
18
|
thead
|
|
15
19
|
tr
|
|
16
20
|
th.col-sm-1.pk PK
|
|
17
|
-
th.col-sm-
|
|
18
|
-
th.col-sm-
|
|
21
|
+
th.col-sm-3.name Name
|
|
22
|
+
th.col-sm-2.type Type
|
|
19
23
|
th.col-sm-1.null Non-Null
|
|
20
|
-
th.col-sm-
|
|
21
|
-
th.col-sm-
|
|
22
|
-
th.col-sm-4.data unscoped.first
|
|
24
|
+
th.col-sm-2.limit Limit
|
|
25
|
+
th.col-sm-3.default Default
|
|
23
26
|
tbody
|
|
24
27
|
- ar_class.columns.each do |column|
|
|
25
28
|
tr
|
|
@@ -29,4 +32,3 @@ ul.list-unstyled.tables
|
|
|
29
32
|
td.null.icon= column.null ? '' : icon_fa('check')
|
|
30
33
|
td.limit.number= column.limit
|
|
31
34
|
td= column.default
|
|
32
|
-
td.monospace= first_record.try {|r| truncate(r.read_attribute_before_type_cast(column.name).to_s, length: 50) }
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
#current-tables
|
|
2
|
+
a.loading[href=current_tables_path]
|
|
3
|
+
|
|
4
|
+
javascript:
|
|
5
|
+
$(function() {
|
|
6
|
+
$(document).on('click', '[data-trigger="toggleCurrentTables"]', function($ev) {
|
|
7
|
+
Adhoq.toggleCurrentTables($($ev.target).attr('role'), {main: '#main', tables: '#current-tables'});
|
|
8
|
+
})
|
|
9
|
+
});
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
tr[exec]
|
|
2
|
+
td.wip
|
|
3
|
+
td.created_at= exec.created_at.localtime.iso8601
|
|
4
|
+
td.status
|
|
5
|
+
= exec.status_label
|
|
6
|
+
td.report
|
|
7
|
+
- if exec.success?
|
|
8
|
+
= link_to(query_execution_path(query, exec, format: exec.report_format), class: 'btn btn-sm btn-default') do
|
|
9
|
+
i.fa.fa-download.fa-pad-r
|
|
10
|
+
= exec.report_format
|
|
@@ -1,28 +1,54 @@
|
|
|
1
|
-
= form_for query, html: {class: 'form
|
|
1
|
+
= form_for query, html: {class: 'form query-form', role: 'form'} do |f|
|
|
2
2
|
.page-header
|
|
3
3
|
h1
|
|
4
|
-
= title
|
|
5
|
-
.
|
|
6
|
-
|
|
7
|
-
i.fa.fa-
|
|
8
|
-
|
|
|
9
|
-
- if f.object.persisted?
|
|
10
|
-
= link_to 'Cancel', f.object, class: 'btn btn-default'
|
|
4
|
+
= f.label :query, title, class: 'control-label'
|
|
5
|
+
.pull-right
|
|
6
|
+
a.btn.btn-default.btn-sm[href='#' data-trigger='toggleCurrentTables' role='show']
|
|
7
|
+
i.fa.fa-database.fa-pad-r
|
|
8
|
+
| Show tables
|
|
11
9
|
|
|
12
10
|
.form-group
|
|
13
|
-
= f.
|
|
14
|
-
.col-sm-8
|
|
15
|
-
= f.text_field :name, class: 'form-control', required: true
|
|
11
|
+
= f.text_area :query, class: 'form-control', rows: 15, required: true
|
|
16
12
|
|
|
17
|
-
.
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
13
|
+
.modal.fade#nameAndDesc[role='dialog']
|
|
14
|
+
.modal-dialog
|
|
15
|
+
.modal-content
|
|
16
|
+
.modal-header
|
|
17
|
+
button.close[data-dismiss='modal' aria-label='Close']
|
|
18
|
+
button.close[type='button' data-dismiss='modal' aria-label='Close']
|
|
19
|
+
span[aria-hidden='true'] ×
|
|
21
20
|
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
21
|
+
h4 Add name and description to query
|
|
22
|
+
.modal-body
|
|
23
|
+
.form-horizontal
|
|
24
|
+
.form-group
|
|
25
|
+
= f.label :name, class: 'control-label col-sm-2'
|
|
26
|
+
.col-sm-8
|
|
27
|
+
= f.text_field :name, class: 'form-control', required: true
|
|
28
|
+
|
|
29
|
+
.form-group
|
|
30
|
+
= f.label :description, class: 'control-label col-sm-2'
|
|
31
|
+
.col-sm-8
|
|
32
|
+
= f.text_area :description, class: 'form-control', required: true
|
|
33
|
+
.modal-footer
|
|
34
|
+
button.btn.btn-primary.center-block
|
|
35
|
+
i.fa.fa-floppy-o.fa-pad-r
|
|
36
|
+
| Save
|
|
37
|
+
|
|
38
|
+
.actions
|
|
39
|
+
- if query.persisted?
|
|
40
|
+
= link_to query do
|
|
41
|
+
i.fa.fa-arrow-left.fa-pad-r
|
|
42
|
+
| Cancel
|
|
43
|
+
- else
|
|
44
|
+
= link_to :queries do
|
|
45
|
+
i.fa.fa-arrow-left.fa-pad-r
|
|
46
|
+
| Back to Index
|
|
47
|
+
|
|
48
|
+
.pull-right
|
|
49
|
+
= link_to '#nameAndDesc', class: 'btn btn-default btn-sm', data: {toggle: 'modal', target: '#nameAndDesc'} do
|
|
50
|
+
i.fa.fa-floppy-o.fa-pad-r
|
|
51
|
+
| Save as...
|
|
26
52
|
|
|
27
53
|
ul.nav.nav-tabs[role='tablist']
|
|
28
54
|
li.active
|
|
@@ -33,19 +59,15 @@ ul.nav.nav-tabs[role='tablist']
|
|
|
33
59
|
a[role='tab' data-toggle='tab' href='#explain' ]
|
|
34
60
|
i.fa.fa-info.fa-pad-r
|
|
35
61
|
| Explain
|
|
36
|
-
li
|
|
37
|
-
a[role='tab' data-toggle='tab' href='#current-tables']
|
|
38
|
-
i.fa.fa-database.fa-pad-r
|
|
39
|
-
| Tables
|
|
40
62
|
|
|
41
|
-
.tab-content
|
|
63
|
+
#previews.tab-content
|
|
42
64
|
#preview.tab-pane.active
|
|
43
65
|
h3
|
|
44
66
|
| Query preview
|
|
45
67
|
small
|
|
46
68
|
= link_to preview_path, class: 'js-preview-button', data: {source: '#query_query', result: '.js-preview-result', remote: true, method: 'POST'} do
|
|
47
69
|
i.fa.fa-refresh.fa-pad-r[data-title='Refresh preview']
|
|
48
|
-
|
|
|
70
|
+
| Refresh
|
|
49
71
|
|
|
50
72
|
.js-preview-result
|
|
51
73
|
.alert.alert-info Preview is shown here
|
|
@@ -56,19 +78,16 @@ ul.nav.nav-tabs[role='tablist']
|
|
|
56
78
|
small
|
|
57
79
|
= link_to explain_path, class: 'js-explain-button', data: {source: '#query_query', result: '.js-explain-result', remote: true, method: 'POST'} do
|
|
58
80
|
i.fa.fa-refresh.fa-pad-r[data-title='Refresh explain']
|
|
59
|
-
|
|
|
81
|
+
| Refresh
|
|
60
82
|
|
|
61
83
|
.js-explain-result
|
|
62
84
|
.alert.alert-info Explain result is shown here
|
|
63
85
|
|
|
64
|
-
#current-tables.tab-pane
|
|
65
|
-
a.loading[href=current_tables_path]
|
|
66
|
-
|
|
67
86
|
javascript:
|
|
68
87
|
$(function() {
|
|
69
88
|
Adhoq.enablePreview($('#preview a.js-preview-button'));
|
|
70
89
|
Adhoq.enablePreview($('#explain a.js-explain-button'));
|
|
71
90
|
|
|
72
|
-
$('
|
|
91
|
+
Adhoq.enablePreviewKeybordShortCut($('#query_query'), '#previews .tab-pane.active a:has(".fa-refresh")')
|
|
73
92
|
});
|
|
74
93
|
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
- highlight ||= nil
|
|
2
|
+
section
|
|
3
|
+
= link_to :root, class: 'btn btn-default new-query btn-sm center-block' do
|
|
4
|
+
i.fa.fa-plus-square.fa-pad-r
|
|
5
|
+
| New query
|
|
6
|
+
|
|
7
|
+
ol.queries-index.list-unstyled
|
|
8
|
+
- queries.each do |query|
|
|
9
|
+
- css = (query == highlight) ? 'panel-success' : 'panel-default'
|
|
10
|
+
li.panel[query, class= css]
|
|
11
|
+
.panel-heading
|
|
12
|
+
h2= link_to query.name, query_path(query)
|
|
13
|
+
p.panel-body.description= query.description
|
|
14
|
+
|
|
@@ -2,13 +2,18 @@ section.query
|
|
|
2
2
|
.page-header
|
|
3
3
|
h1
|
|
4
4
|
= query.name
|
|
5
|
+
.pull-right
|
|
6
|
+
= link_to [:edit, query], class: 'btn btn-default btn-sm' do
|
|
7
|
+
i.fa.fa-pencil.fa-pad-r
|
|
8
|
+
| Edit
|
|
9
|
+
.clearfix
|
|
5
10
|
small= "Updated at #{l(query.updated_at, format: :short)}"
|
|
6
|
-
= link_to [:edit, query], class: 'btn btn-default' do
|
|
7
|
-
i.fa.fa-pencil.fa-pad-r
|
|
8
|
-
| Edit
|
|
9
11
|
p.description= query.description
|
|
10
12
|
|
|
11
|
-
|
|
13
|
+
css:
|
|
14
|
+
#{Rouge::Themes::Github.render(scope: '.highlight')}
|
|
15
|
+
|
|
16
|
+
= raw query.query_with_highlight
|
|
12
17
|
|
|
13
18
|
section.new-execution
|
|
14
19
|
h2 Create report
|
|
@@ -21,23 +26,14 @@ section.query
|
|
|
21
26
|
|
|
22
27
|
section.past-executions
|
|
23
28
|
h2 Reports
|
|
24
|
-
.
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
-
|
|
34
|
-
|
|
35
|
-
td.wip
|
|
36
|
-
td.created_at= exec.created_at.localtime.iso8601
|
|
37
|
-
td.status
|
|
38
|
-
span.label[class=(exec.success? ? 'label-success' : 'label-danger')]= exec.status_label
|
|
39
|
-
td.report
|
|
40
|
-
- if exec.success?
|
|
41
|
-
= link_to(adhoq.query_execution_path(query, exec, format: exec.report_format), class: 'btn btn-sm btn-default') do
|
|
42
|
-
i.fa.fa-download.fa-pad-r
|
|
43
|
-
= exec.report_format
|
|
29
|
+
table.executions.table.table-striped.table-hover
|
|
30
|
+
thead
|
|
31
|
+
tr
|
|
32
|
+
th.wip
|
|
33
|
+
th.created_at= human(Adhoq::Execution, :created_at)
|
|
34
|
+
th.status= human(Adhoq::Execution, :status)
|
|
35
|
+
th.report
|
|
36
|
+
tbody
|
|
37
|
+
- query.executions.recent_first.each do |exec|
|
|
38
|
+
- next if exec.report.try(:on_the_fly?)
|
|
39
|
+
= render 'execution', query: query, exec: exec
|
|
@@ -1,2 +1,11 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
1
|
+
.col-md-12
|
|
2
|
+
ol.breadcrumb
|
|
3
|
+
li= link_to 'Index', :queries
|
|
4
|
+
li= link_to query_friendly_name(@query), @query
|
|
5
|
+
li.active Edit
|
|
6
|
+
|
|
7
|
+
#main.col-md-12
|
|
8
|
+
section.edit-query
|
|
9
|
+
= render 'form', query: @query, title: "Edit query > #{query_friendly_name(@query)}"
|
|
10
|
+
|
|
11
|
+
= render 'current_tables_leftbar'
|
|
@@ -1 +1,11 @@
|
|
|
1
|
-
|
|
1
|
+
.col-md-12
|
|
2
|
+
ol.breadcrumb
|
|
3
|
+
li.active Index
|
|
4
|
+
|
|
5
|
+
#queries.col-md-3
|
|
6
|
+
section.queries
|
|
7
|
+
= render 'queries', queries: @queries
|
|
8
|
+
|
|
9
|
+
#the-query.col-md-9
|
|
10
|
+
- if first_query = @queries.first
|
|
11
|
+
= render 'query', query: first_query
|
|
@@ -1,2 +1,10 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
1
|
+
.col-md-12
|
|
2
|
+
ol.breadcrumb
|
|
3
|
+
li= link_to 'Index', :queries
|
|
4
|
+
li.active New query
|
|
5
|
+
|
|
6
|
+
#main.col-md-12
|
|
7
|
+
section.new-query
|
|
8
|
+
= render 'form', query: @query, title: 'New query'
|
|
9
|
+
|
|
10
|
+
= render 'current_tables_leftbar'
|
|
@@ -1 +1,11 @@
|
|
|
1
|
-
|
|
1
|
+
.col-md-12
|
|
2
|
+
ol.breadcrumb
|
|
3
|
+
li= link_to 'Index', :queries
|
|
4
|
+
li.active= query_friendly_name(@query)
|
|
5
|
+
|
|
6
|
+
#queries.col-md-3
|
|
7
|
+
section.queries
|
|
8
|
+
= render 'queries', queries: Adhoq::Query.recent_first, highlight: @query
|
|
9
|
+
|
|
10
|
+
#the-query.col-md-9
|
|
11
|
+
= render 'query', query: @query
|
data/lib/adhoq.rb
CHANGED
data/lib/adhoq/configuration.rb
CHANGED
|
@@ -10,6 +10,12 @@ module Adhoq
|
|
|
10
10
|
|
|
11
11
|
config_accessor :current_user
|
|
12
12
|
|
|
13
|
+
config_accessor :database_connection
|
|
14
|
+
config_accessor :hidden_model_names
|
|
15
|
+
|
|
16
|
+
config_accessor :async_execution
|
|
17
|
+
config_accessor :job_queue_name
|
|
18
|
+
|
|
13
19
|
def callablize(name)
|
|
14
20
|
if (c = config[name]).respond_to?(:call)
|
|
15
21
|
c
|
|
@@ -17,5 +23,9 @@ module Adhoq
|
|
|
17
23
|
c.to_proc
|
|
18
24
|
end
|
|
19
25
|
end
|
|
26
|
+
|
|
27
|
+
def async_execution?
|
|
28
|
+
defined?(ActiveJob) && Adhoq.config.async_execution
|
|
29
|
+
end
|
|
20
30
|
end
|
|
21
31
|
end
|
data/lib/adhoq/engine.rb
CHANGED
data/lib/adhoq/executor.rb
CHANGED
|
@@ -1,42 +1,18 @@
|
|
|
1
1
|
module Adhoq
|
|
2
2
|
class Executor
|
|
3
|
-
|
|
4
|
-
def select(query)
|
|
5
|
-
with_sandbox do
|
|
6
|
-
current_connection.exec_query(query)
|
|
7
|
-
end
|
|
8
|
-
end
|
|
9
|
-
|
|
10
|
-
def explain(query)
|
|
11
|
-
with_sandbox do
|
|
12
|
-
current_connection.explain(query)
|
|
13
|
-
end
|
|
14
|
-
end
|
|
15
|
-
|
|
16
|
-
def current_connection
|
|
17
|
-
ActiveRecord::Base.connection
|
|
18
|
-
end
|
|
19
|
-
|
|
20
|
-
def with_sandbox
|
|
21
|
-
result = nil
|
|
22
|
-
ActiveRecord::Base.transaction do
|
|
23
|
-
result = yield
|
|
24
|
-
raise ActiveRecord::Rollback
|
|
25
|
-
end
|
|
26
|
-
result
|
|
27
|
-
end
|
|
28
|
-
end
|
|
3
|
+
autoload 'ConnectionWrapper', 'adhoq/executor/connection_wrapper'
|
|
29
4
|
|
|
30
5
|
def initialize(query)
|
|
6
|
+
@connection = ConnectionWrapper.new
|
|
31
7
|
@query = query
|
|
32
8
|
end
|
|
33
9
|
|
|
34
10
|
def execute
|
|
35
|
-
wrap_result(
|
|
11
|
+
wrap_result(@connection.select(@query))
|
|
36
12
|
end
|
|
37
13
|
|
|
38
14
|
def explain
|
|
39
|
-
|
|
15
|
+
@connection.explain(@query)
|
|
40
16
|
end
|
|
41
17
|
|
|
42
18
|
private
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
module Adhoq
|
|
2
|
+
class Executor
|
|
3
|
+
class ConnectionWrapper
|
|
4
|
+
attr_reader :connection
|
|
5
|
+
|
|
6
|
+
def initialize
|
|
7
|
+
@connection = Adhoq.config.callablize(:database_connection).call
|
|
8
|
+
end
|
|
9
|
+
|
|
10
|
+
def select(query)
|
|
11
|
+
with_sandbox do
|
|
12
|
+
connection.exec_query(query)
|
|
13
|
+
end
|
|
14
|
+
end
|
|
15
|
+
|
|
16
|
+
def explain(query)
|
|
17
|
+
with_sandbox do
|
|
18
|
+
connection.explain(query)
|
|
19
|
+
end
|
|
20
|
+
end
|
|
21
|
+
|
|
22
|
+
def with_sandbox
|
|
23
|
+
result = nil
|
|
24
|
+
connection.transaction do
|
|
25
|
+
result = yield
|
|
26
|
+
raise ActiveRecord::Rollback
|
|
27
|
+
end
|
|
28
|
+
result
|
|
29
|
+
end
|
|
30
|
+
end
|
|
31
|
+
end
|
|
32
|
+
end
|
data/lib/adhoq/storage.rb
CHANGED
|
@@ -3,6 +3,7 @@ module Adhoq
|
|
|
3
3
|
autoload 'FogStorage', 'adhoq/storage/fog_storage'
|
|
4
4
|
autoload 'LocalFile', 'adhoq/storage/local_file'
|
|
5
5
|
autoload 'S3', 'adhoq/storage/s3'
|
|
6
|
+
autoload 'OnTheFly', 'adhoq/storage/on_the_fly'
|
|
6
7
|
|
|
7
8
|
def with_new_identifier(suffix = nil, seed = Time.now)
|
|
8
9
|
dirname, fname_seed = ['%Y-%m-%d', '%H%M%S.%L'].map {|f| seed.strftime(f) }
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
module Adhoq
|
|
2
|
+
module Storage
|
|
3
|
+
class OnTheFly
|
|
4
|
+
PREFIX = 'memory://adhoq-on-the-fly'
|
|
5
|
+
|
|
6
|
+
attr_reader :identifier, :reports
|
|
7
|
+
|
|
8
|
+
def initialize(id_base = Process.pid)
|
|
9
|
+
@identifier = "#{PREFIX}-#{id_base}"
|
|
10
|
+
@reports = {}
|
|
11
|
+
end
|
|
12
|
+
|
|
13
|
+
def store(suffix = nil, seed = Time.now, &block)
|
|
14
|
+
Adhoq::Storage.with_new_identifier(suffix, seed) do |identifier|
|
|
15
|
+
@reports[identifier] = yield.tap(&:rewind)
|
|
16
|
+
end
|
|
17
|
+
end
|
|
18
|
+
|
|
19
|
+
def direct_download?
|
|
20
|
+
false
|
|
21
|
+
end
|
|
22
|
+
|
|
23
|
+
def get(identifier)
|
|
24
|
+
if item = @reports.delete(identifier)
|
|
25
|
+
item.read.tap { item.close }
|
|
26
|
+
else
|
|
27
|
+
nil
|
|
28
|
+
end
|
|
29
|
+
end
|
|
30
|
+
end
|
|
31
|
+
end
|
|
32
|
+
end
|
data/lib/adhoq/storage/s3.rb
CHANGED
|
@@ -4,14 +4,27 @@ module Adhoq
|
|
|
4
4
|
module Storage
|
|
5
5
|
class S3 < FogStorage
|
|
6
6
|
def initialize(bucket, s3_options = {})
|
|
7
|
-
@bucket
|
|
8
|
-
@
|
|
7
|
+
@bucket = bucket
|
|
8
|
+
@direct_download = s3_options.delete(:direct_download)
|
|
9
|
+
@direct_download_options = s3_options.delete(:direct_download_options) || default_direct_download_options
|
|
10
|
+
@s3 = Fog::Storage.new({provider: 'AWS'}.merge(s3_options))
|
|
11
|
+
end
|
|
12
|
+
|
|
13
|
+
def direct_download?
|
|
14
|
+
@direct_download
|
|
9
15
|
end
|
|
10
16
|
|
|
11
17
|
def identifier
|
|
12
18
|
"s3://#{@bucket}"
|
|
13
19
|
end
|
|
14
20
|
|
|
21
|
+
def get_url(report)
|
|
22
|
+
get_raw(report.identifier).url(
|
|
23
|
+
1.minutes.from_now.to_i,
|
|
24
|
+
@direct_download_options.call(report)
|
|
25
|
+
)
|
|
26
|
+
end
|
|
27
|
+
|
|
15
28
|
private
|
|
16
29
|
|
|
17
30
|
def directory
|
|
@@ -19,6 +32,17 @@ module Adhoq
|
|
|
19
32
|
|
|
20
33
|
@directory = @s3.directories.get(@bucket) || @s3.directories.create(key: @bucket, public: false)
|
|
21
34
|
end
|
|
35
|
+
|
|
36
|
+
def default_direct_download_options
|
|
37
|
+
proc do |report|
|
|
38
|
+
{
|
|
39
|
+
query: {
|
|
40
|
+
'response-content-disposition' => "attachment; filename*=UTF-8''#{URI.encode_www_form_component(report.name)}",
|
|
41
|
+
'response-content-type' => report.mime_type,
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
end
|
|
45
|
+
end
|
|
22
46
|
end
|
|
23
47
|
end
|
|
24
48
|
end
|
data/lib/adhoq/version.rb
CHANGED
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
module Adhoq
|
|
2
|
+
RSpec.describe Executor::ConnectionWrapper, type: :model do
|
|
3
|
+
describe '.select' do
|
|
4
|
+
specify 'Do not reflect write access' do
|
|
5
|
+
expect {
|
|
6
|
+
Executor::ConnectionWrapper.new.select(<<-INSERT_SQL.strip_heredoc)
|
|
7
|
+
INSERT INTO "adhoq_queries"
|
|
8
|
+
("description", "name", "query", "updated_at", "created_at")
|
|
9
|
+
VALUES
|
|
10
|
+
("description", "name", "SELECT 1", "NOW", "NOW")
|
|
11
|
+
INSERT_SQL
|
|
12
|
+
}.not_to change(Adhoq::Query, :count)
|
|
13
|
+
end
|
|
14
|
+
end
|
|
15
|
+
end
|
|
16
|
+
end
|
data/spec/adhoq/executor_spec.rb
CHANGED
|
@@ -7,18 +7,5 @@ module Adhoq
|
|
|
7
7
|
|
|
8
8
|
specify { expect(executor.execute).to eq Adhoq::Result.new(%w[answer], [[42]]) }
|
|
9
9
|
end
|
|
10
|
-
|
|
11
|
-
describe '.select' do
|
|
12
|
-
specify 'Do not reflect write access' do
|
|
13
|
-
expect {
|
|
14
|
-
Executor.select(<<-INSERT_SQL.strip_heredoc)
|
|
15
|
-
INSERT INTO "adhoq_queries"
|
|
16
|
-
("description", "name", "query", "updated_at", "created_at")
|
|
17
|
-
VALUES
|
|
18
|
-
("description", "name", "SELECT 1", "NOW", "NOW")
|
|
19
|
-
INSERT_SQL
|
|
20
|
-
}.not_to change(Adhoq::Query, :count)
|
|
21
|
-
end
|
|
22
|
-
end
|
|
23
10
|
end
|
|
24
11
|
end
|
data/spec/adhoq/storage_spec.rb
CHANGED
|
@@ -25,5 +25,19 @@ module Adhoq
|
|
|
25
25
|
|
|
26
26
|
specify { expect(storage.get(identifier)).to eq "Hello adhoq!\n" }
|
|
27
27
|
end
|
|
28
|
+
|
|
29
|
+
describe Storage::OnTheFly do
|
|
30
|
+
let(:storage) { Storage::OnTheFly.new }
|
|
31
|
+
|
|
32
|
+
let!(:identifier) do
|
|
33
|
+
storage.store('.txt') { StringIO.new("Hello adhoq!\n") }
|
|
34
|
+
end
|
|
35
|
+
|
|
36
|
+
specify { expect(storage.get(identifier)).to eq "Hello adhoq!\n" }
|
|
37
|
+
|
|
38
|
+
specify do
|
|
39
|
+
expect { storage.get(identifier) }.to change { storage.reports.size }.by(-1)
|
|
40
|
+
end
|
|
41
|
+
end
|
|
28
42
|
end
|
|
29
43
|
end
|
|
@@ -1,4 +1,25 @@
|
|
|
1
1
|
module Adhoq
|
|
2
2
|
RSpec.describe Execution, :type => :model do
|
|
3
|
+
before do
|
|
4
|
+
storage = Adhoq::Storage::OnTheFly.new
|
|
5
|
+
allow(Adhoq).to receive(:current_storage) { storage }
|
|
6
|
+
end
|
|
7
|
+
|
|
8
|
+
let(:execution) do
|
|
9
|
+
query = create(:adhoq_query, query: 'SELECT name, description FROM adhoq_queries')
|
|
10
|
+
query.execute!('xlsx')
|
|
11
|
+
end
|
|
12
|
+
|
|
13
|
+
specify { expect(execution.report).to be_on_the_fly }
|
|
14
|
+
|
|
15
|
+
specify 'can get report only on execution' do
|
|
16
|
+
expect(execution.report.data).to have_values_in_xlsx_sheet([
|
|
17
|
+
["name", "description"],
|
|
18
|
+
["A query", "Simple simple SELECT"]
|
|
19
|
+
])
|
|
20
|
+
|
|
21
|
+
# Accessable only once
|
|
22
|
+
expect(execution.report.data).to be_nil
|
|
23
|
+
end
|
|
3
24
|
end
|
|
4
25
|
end
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
RSpec.configure do |config|
|
|
2
|
+
config.around(:each, async_execution: true) do |ex|
|
|
3
|
+
current_async_execution = Adhoq.config.async_execution
|
|
4
|
+
|
|
5
|
+
Adhoq.config.async_execution = true
|
|
6
|
+
|
|
7
|
+
ex.call
|
|
8
|
+
|
|
9
|
+
Adhoq.config.async_execution = current_async_execution
|
|
10
|
+
end
|
|
11
|
+
|
|
12
|
+
config.around(:each, active_job_test_adapter: true) do |ex|
|
|
13
|
+
current_active_job_queue_adapter = Adhoq::Engine.config.active_job.queue_adapter
|
|
14
|
+
current_execute_job_queue_adapter = Adhoq::ExecuteJob.queue_adapter
|
|
15
|
+
|
|
16
|
+
Adhoq::Engine.config.active_job.queue_adapter = :test
|
|
17
|
+
Adhoq::ExecuteJob.queue_adapter = ActiveJob::QueueAdapters::TestAdapter.new
|
|
18
|
+
Adhoq::ExecuteJob.queue_adapter.perform_enqueued_jobs = true
|
|
19
|
+
|
|
20
|
+
ex.call
|
|
21
|
+
|
|
22
|
+
Adhoq::ExecuteJob.queue_adapter.performed_jobs.clear
|
|
23
|
+
Adhoq::Engine.config.active_job.queue_adapter = current_active_job_queue_adapter
|
|
24
|
+
Adhoq::ExecuteJob.queue_adapter = current_execute_job_queue_adapter
|
|
25
|
+
end
|
|
26
|
+
end
|
metadata
CHANGED
|
@@ -1,14 +1,14 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: adhoq
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 0.0.
|
|
4
|
+
version: 0.0.6
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Kyosuke MOROHASHI
|
|
8
8
|
autorequire:
|
|
9
9
|
bindir: bin
|
|
10
10
|
cert_chain: []
|
|
11
|
-
date: 2015-
|
|
11
|
+
date: 2015-10-16 00:00:00.000000000 Z
|
|
12
12
|
dependencies:
|
|
13
13
|
- !ruby/object:Gem::Dependency
|
|
14
14
|
name: rails
|
|
@@ -122,6 +122,34 @@ dependencies:
|
|
|
122
122
|
- - ">="
|
|
123
123
|
- !ruby/object:Gem::Version
|
|
124
124
|
version: '0'
|
|
125
|
+
- !ruby/object:Gem::Dependency
|
|
126
|
+
name: active_decorator
|
|
127
|
+
requirement: !ruby/object:Gem::Requirement
|
|
128
|
+
requirements:
|
|
129
|
+
- - ">="
|
|
130
|
+
- !ruby/object:Gem::Version
|
|
131
|
+
version: '0'
|
|
132
|
+
type: :runtime
|
|
133
|
+
prerelease: false
|
|
134
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
135
|
+
requirements:
|
|
136
|
+
- - ">="
|
|
137
|
+
- !ruby/object:Gem::Version
|
|
138
|
+
version: '0'
|
|
139
|
+
- !ruby/object:Gem::Dependency
|
|
140
|
+
name: rouge
|
|
141
|
+
requirement: !ruby/object:Gem::Requirement
|
|
142
|
+
requirements:
|
|
143
|
+
- - ">="
|
|
144
|
+
- !ruby/object:Gem::Version
|
|
145
|
+
version: '0'
|
|
146
|
+
type: :runtime
|
|
147
|
+
prerelease: false
|
|
148
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
149
|
+
requirements:
|
|
150
|
+
- - ">="
|
|
151
|
+
- !ruby/object:Gem::Version
|
|
152
|
+
version: '0'
|
|
125
153
|
- !ruby/object:Gem::Dependency
|
|
126
154
|
name: capybara
|
|
127
155
|
requirement: !ruby/object:Gem::Requirement
|
|
@@ -372,19 +400,24 @@ files:
|
|
|
372
400
|
- app/controllers/adhoq/explains_controller.rb
|
|
373
401
|
- app/controllers/adhoq/previews_controller.rb
|
|
374
402
|
- app/controllers/adhoq/queries_controller.rb
|
|
403
|
+
- app/decorators/adhoq/execution_decorator.rb
|
|
404
|
+
- app/decorators/adhoq/query_decorator.rb
|
|
375
405
|
- app/helpers/adhoq/application_helper.rb
|
|
406
|
+
- app/jobs/adhoq/execute_job.rb
|
|
376
407
|
- app/models/adhoq/execution.rb
|
|
377
408
|
- app/models/adhoq/query.rb
|
|
378
409
|
- app/models/adhoq/report.rb
|
|
379
410
|
- app/models/adhoq/time_based_orders.rb
|
|
380
411
|
- app/views/adhoq/application/_global_nav.html.slim
|
|
381
|
-
- app/views/adhoq/application/_sidebar_queries_index.html.slim
|
|
382
412
|
- app/views/adhoq/current_tables/index.html.slim
|
|
383
413
|
- app/views/adhoq/explains/create.html.slim
|
|
384
414
|
- app/views/adhoq/explains/statement_invalid.html.slim
|
|
385
415
|
- app/views/adhoq/previews/create.html.slim
|
|
386
416
|
- app/views/adhoq/previews/statement_invalid.html.slim
|
|
417
|
+
- app/views/adhoq/queries/_current_tables_leftbar.html.slim
|
|
418
|
+
- app/views/adhoq/queries/_execution.html.slim
|
|
387
419
|
- app/views/adhoq/queries/_form.html.slim
|
|
420
|
+
- app/views/adhoq/queries/_queries.html.slim
|
|
388
421
|
- app/views/adhoq/queries/_query.html.slim
|
|
389
422
|
- app/views/adhoq/queries/edit.html.slim
|
|
390
423
|
- app/views/adhoq/queries/index.html.slim
|
|
@@ -401,6 +434,7 @@ files:
|
|
|
401
434
|
- lib/adhoq/engine.rb
|
|
402
435
|
- lib/adhoq/error.rb
|
|
403
436
|
- lib/adhoq/executor.rb
|
|
437
|
+
- lib/adhoq/executor/connection_wrapper.rb
|
|
404
438
|
- lib/adhoq/global_variable.rb
|
|
405
439
|
- lib/adhoq/reporter.rb
|
|
406
440
|
- lib/adhoq/reporter/csv.rb
|
|
@@ -410,9 +444,11 @@ files:
|
|
|
410
444
|
- lib/adhoq/storage.rb
|
|
411
445
|
- lib/adhoq/storage/fog_storage.rb
|
|
412
446
|
- lib/adhoq/storage/local_file.rb
|
|
447
|
+
- lib/adhoq/storage/on_the_fly.rb
|
|
413
448
|
- lib/adhoq/storage/s3.rb
|
|
414
449
|
- lib/adhoq/version.rb
|
|
415
450
|
- lib/tasks/adhoq_tasks.rake
|
|
451
|
+
- spec/adhoq/executor/connection_wrapper_spec.rb
|
|
416
452
|
- spec/adhoq/executor_spec.rb
|
|
417
453
|
- spec/adhoq/global_variable_spec.rb
|
|
418
454
|
- spec/adhoq/reporter/csv_spec.rb
|
|
@@ -424,6 +460,7 @@ files:
|
|
|
424
460
|
- spec/models/adhoq/query_spec.rb
|
|
425
461
|
- spec/models/adhoq/report_spec.rb
|
|
426
462
|
- spec/spec_helper.rb
|
|
463
|
+
- spec/support/activejob_helper.rb
|
|
427
464
|
- spec/support/codeclimate_reporter.rb
|
|
428
465
|
- spec/support/feature_spec_helper.rb
|
|
429
466
|
- spec/support/have_values_in_xlsx_sheet_matcher.rb
|
|
@@ -447,11 +484,12 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
|
447
484
|
version: '0'
|
|
448
485
|
requirements: []
|
|
449
486
|
rubyforge_project:
|
|
450
|
-
rubygems_version: 2.
|
|
487
|
+
rubygems_version: 2.4.5
|
|
451
488
|
signing_key:
|
|
452
489
|
specification_version: 4
|
|
453
490
|
summary: DB management console in the wild.
|
|
454
491
|
test_files:
|
|
492
|
+
- spec/adhoq/executor/connection_wrapper_spec.rb
|
|
455
493
|
- spec/adhoq/executor_spec.rb
|
|
456
494
|
- spec/adhoq/global_variable_spec.rb
|
|
457
495
|
- spec/adhoq/reporter/csv_spec.rb
|
|
@@ -462,6 +500,7 @@ test_files:
|
|
|
462
500
|
- spec/models/adhoq/execution_spec.rb
|
|
463
501
|
- spec/models/adhoq/query_spec.rb
|
|
464
502
|
- spec/models/adhoq/report_spec.rb
|
|
503
|
+
- spec/support/activejob_helper.rb
|
|
465
504
|
- spec/support/codeclimate_reporter.rb
|
|
466
505
|
- spec/support/feature_spec_helper.rb
|
|
467
506
|
- spec/support/have_values_in_xlsx_sheet_matcher.rb
|
|
@@ -1,10 +0,0 @@
|
|
|
1
|
-
h2
|
|
2
|
-
| Queries
|
|
3
|
-
= link_to 'Create new', :root, class: 'btn btn-primary new-query'
|
|
4
|
-
ul.queries.list-unstyled
|
|
5
|
-
- Adhoq::Query.recent_first.each do |query|
|
|
6
|
-
li.panel.panel-default[query]
|
|
7
|
-
.panel-heading
|
|
8
|
-
h2= link_to query.name, query_path(query)
|
|
9
|
-
p.panel-body.description= query.description
|
|
10
|
-
|