adhoq 0.3.0 → 1.0.1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (52) hide show
  1. checksums.yaml +5 -5
  2. data/README.md +1 -1
  3. data/app/assets/javascripts/adhoq/current_tables.js +23 -0
  4. data/app/assets/javascripts/adhoq/previewer.js +52 -0
  5. data/app/controllers/adhoq/queries_controller.rb +1 -1
  6. data/app/models/adhoq/execution.rb +5 -3
  7. data/app/models/adhoq/time_based_orders.rb +1 -1
  8. data/app/views/adhoq/application/_global_nav.html.erb +18 -0
  9. data/app/views/adhoq/current_tables/index.html.erb +53 -0
  10. data/app/views/adhoq/explains/create.html.erb +1 -0
  11. data/app/views/adhoq/explains/statement_invalid.html.erb +5 -0
  12. data/app/views/adhoq/previews/create.html.erb +21 -0
  13. data/app/views/adhoq/previews/statement_invalid.html.erb +4 -0
  14. data/app/views/adhoq/queries/_current_tables_leftbar.html.erb +14 -0
  15. data/app/views/adhoq/queries/_execution.html.erb +10 -0
  16. data/app/views/adhoq/queries/_form.html.erb +124 -0
  17. data/app/views/adhoq/queries/_queries.html.erb +16 -0
  18. data/app/views/adhoq/queries/_query.html.erb +67 -0
  19. data/app/views/adhoq/queries/edit.html.erb +13 -0
  20. data/app/views/adhoq/queries/index.html.erb +15 -0
  21. data/app/views/adhoq/queries/new.html.erb +12 -0
  22. data/app/views/adhoq/queries/show.html.erb +14 -0
  23. data/app/views/layouts/adhoq/application.html.erb +18 -0
  24. data/config/database.yml +25 -0
  25. data/lib/adhoq/configuration.rb +1 -0
  26. data/lib/adhoq/engine.rb +1 -1
  27. data/lib/adhoq/executor/connection_wrapper.rb +12 -8
  28. data/lib/adhoq/global_variable.rb +1 -0
  29. data/lib/adhoq/storage.rb +1 -0
  30. data/lib/adhoq/storage/cache.rb +27 -0
  31. data/lib/adhoq/version.rb +1 -1
  32. data/spec/factories/adhoq_queries.rb +9 -9
  33. data/spec/models/adhoq/execution_spec.rb +18 -0
  34. metadata +44 -71
  35. data/app/assets/javascripts/adhoq/current_tables.js.coffee +0 -18
  36. data/app/assets/javascripts/adhoq/previewer.js.coffee +0 -34
  37. data/app/views/adhoq/application/_global_nav.html.slim +0 -12
  38. data/app/views/adhoq/current_tables/index.html.slim +0 -34
  39. data/app/views/adhoq/explains/create.html.slim +0 -2
  40. data/app/views/adhoq/explains/statement_invalid.html.slim +0 -5
  41. data/app/views/adhoq/previews/create.html.slim +0 -12
  42. data/app/views/adhoq/previews/statement_invalid.html.slim +0 -5
  43. data/app/views/adhoq/queries/_current_tables_leftbar.html.slim +0 -9
  44. data/app/views/adhoq/queries/_execution.html.slim +0 -10
  45. data/app/views/adhoq/queries/_form.html.slim +0 -93
  46. data/app/views/adhoq/queries/_queries.html.slim +0 -14
  47. data/app/views/adhoq/queries/_query.html.slim +0 -48
  48. data/app/views/adhoq/queries/edit.html.slim +0 -11
  49. data/app/views/adhoq/queries/index.html.slim +0 -11
  50. data/app/views/adhoq/queries/new.html.slim +0 -10
  51. data/app/views/adhoq/queries/show.html.slim +0 -11
  52. data/app/views/layouts/adhoq/application.html.slim +0 -15
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
- SHA1:
3
- metadata.gz: 47ea5ea847453ee2fd5a5fbd5e8c8d09e579998f
4
- data.tar.gz: 40fe3bee5c8f7e289c52d5ef02e42cbce70f854e
2
+ SHA256:
3
+ metadata.gz: 3b8ab6cb531de6cad52c1ef81825f7f8ac9f8c0b8adcd09d260f87e1e104f8eb
4
+ data.tar.gz: 7865c607aa6dc3bc326facd0f256a99d7a650e3ce9c9e1ddc1d0d32ed284bcab
5
5
  SHA512:
6
- metadata.gz: 4e61736b80b3946ea4ade7f4014641dc96362ee7a945d1bd24074a5edfd0c7ef9e21646c5afe24f6f5f0731d7d19c2db35130f04e41ada1fdcee8a1a88f3b472
7
- data.tar.gz: 3d7131fee5b46181dec95018313b3284e99dac5ac029d864cef5eb87970d7337b57e7c46ca954f66c412402c5ce56a93a9627ad3fcdefe9ee10a3f44d88a988f
6
+ metadata.gz: b91806bc676493a5eb105250906ca669cc4a6f6cfd99dd948c4fd96eb3f84e5d8ae5cf5a38f0388cb8141d301598f24b8d8f0adeb2e88a73e3135239722dd1b1
7
+ data.tar.gz: b8315a176029673aca6445f235d14f77d181f71f905aa8eb081837c3791af500516e74d99f6f0433d3c64636776e4d82a46aa4f8d432688273740ba71d770198
data/README.md CHANGED
@@ -12,7 +12,7 @@ Rails engine to generate instant reports from adhoc SQL query.
12
12
  - .json
13
13
  - .xlsx
14
14
  - Persist generated report as local file or in AWS S3
15
- - Rails 4.x
15
+ - over Rails 5.1.X
16
16
  - Nice administration console with rails engine
17
17
 
18
18
  ### Future planning
@@ -0,0 +1,23 @@
1
+ (function($) {
2
+ var loadCurrentTableTabOnce = function($el) {
3
+ $el.load($el.find('a.loading').attr('href'));
4
+ };
5
+
6
+ Adhoq.toggleCurrentTables = function(action, elements){
7
+ loadCurrentTableTabOnce($('#current-tables'));
8
+
9
+ var $main = $(elements.main);
10
+ var $tables = $(elements.tables);
11
+
12
+ if (action === 'show') {
13
+ $main.addClass('col-md-6').removeClass('col-md-12');
14
+ $tables.addClass('col-md-6').show();
15
+ } else {
16
+ $main.addClass('col-md-12').removeClass('col-md-6');
17
+ $tables.addClass('col-md-6').hide();
18
+ }
19
+
20
+ return true;
21
+ };
22
+ })(jQuery);
23
+
@@ -0,0 +1,52 @@
1
+ (function($) {
2
+ var Previewer = function(el) {
3
+ this.el = el;
4
+ }
5
+
6
+ Previewer.prototype.init = function() {
7
+ var self = this;
8
+ this.el.on('adhoq:updatePreview', function() {
9
+ self.update()
10
+ });
11
+
12
+ return this.el.on('click', function() {
13
+ self.el.trigger('adhoq:updatePreview');
14
+ return false;
15
+ });
16
+ }
17
+
18
+ Previewer.prototype.update = function() {
19
+ var self = this;
20
+ return jQuery.ajax({
21
+ type: this.el.data('method'),
22
+ url: this.el.attr('href'),
23
+ data: {query: this.source()},
24
+ complete: function(xhr) {
25
+ return self.result().html(xhr.responseText);
26
+ }
27
+ });
28
+ }
29
+
30
+ Previewer.prototype.source = function() {
31
+ return $(this.el.data('source')).val();
32
+ }
33
+
34
+ Previewer.prototype.result = function() {
35
+ return $(this.el.data('result'));
36
+ }
37
+
38
+ Adhoq.enablePreview = function($el) {
39
+ (new Previewer($el)).init()
40
+ }
41
+
42
+ Adhoq.enablePreviewKeybordShortCut = function($textarea, previewSelector) {
43
+ $textarea.on('keyup', function(ev){
44
+ if(ev.ctrlKey && (ev.keyCode === 82)) {
45
+ $(previewSelector).trigger('adhoq:updatePreview');
46
+ }
47
+
48
+ return false;
49
+ })
50
+ }
51
+ })(jQuery);
52
+
@@ -24,7 +24,7 @@ module Adhoq
24
24
 
25
25
  def update
26
26
  @query = Adhoq::Query.find(params[:id])
27
- @query.update_attributes!(query_attributes)
27
+ @query.update!(query_attributes)
28
28
 
29
29
  redirect_to @query
30
30
  end
@@ -9,9 +9,11 @@ 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
+ update(status: :success)
13
+ rescue => e
14
+ Rails.logger.error(e)
15
+ self.report = nil
16
+ update(status: :failure)
15
17
  end
16
18
 
17
19
  def name
@@ -3,7 +3,7 @@ module Adhoq
3
3
  extend ActiveSupport::Concern
4
4
 
5
5
  included do
6
- scope :recent_first, -> { order("#{quoted_table_name}.updated_at DESC") }
6
+ scope :recent_first, -> { order(arel_table[:updated_at].desc) }
7
7
  end
8
8
  end
9
9
  end
@@ -0,0 +1,18 @@
1
+ <nav class="navbar navbar-default" id="global-nav" role="navigation">
2
+ <div class="container-fluid">
3
+ <div class="navbar-header">
4
+ <%= link_to 'Adhoq', root_path, class: 'navbar-brand' %>
5
+ </div>
6
+ <div class="navbar-collapse collapse">
7
+ <ul class="nav navbar-nav navbar-right">
8
+ <% if main_app.respond_to?(:root_path) %>
9
+ <li>
10
+ <%= link_to main_app.root_path do %>
11
+ <i class="fa fa-arrow-circle-right fa-pad-r"></i>Main app
12
+ <% end %>
13
+ </li>
14
+ <% end %>
15
+ </ul>
16
+ </div>
17
+ </div>
18
+ </nav>
@@ -0,0 +1,53 @@
1
+ <h3>
2
+ <i class="fa fa-database fa-pad-r" />Current tables
3
+ <small><%= "Version #{schema_version}" %></small>
4
+ <div class="pull-right">
5
+ <button class="close" data-trigger="toggleCurrentTables" role="close">
6
+ <span aria-hidden="">&times;</span>
7
+ </button>
8
+ </div>
9
+ </h3>
10
+
11
+ <ul class="list-unstyled tables">
12
+ <% @ar_classes.each do |ar_class| %>
13
+ <% first_record = ar_class.unscoped.order(table_order_key(ar_class)).first %>
14
+
15
+ <li class="ar_class" data-table-name="<%= ar_class.table_name %>">
16
+ <table class="table table-striped table-hover table-bordered">
17
+ <caption>
18
+ <span class="name"><%= ar_class.table_name %></span>
19
+ <% unless Adhoq.config.hide_rows_count %>
20
+ <small class="count"><%= ar_class.unscoped.count %> rows</small>
21
+ <% end %>
22
+ </caption>
23
+ <thead>
24
+ <tr>
25
+ <th class="col-sm-1 pk">PK</th>
26
+ <th class="col-sm-3 name"> Name</th>
27
+ <th class="col-sm-2 type"> Type</th>
28
+ <th class="col-sm-1 null"> Non-Null</th>
29
+ <th class="col-sm-2 limit">Limit</th>
30
+ <th class="col-sm-3 default"> Default</th>
31
+ </tr>
32
+ </thead>
33
+ <tbody>
34
+ <% ar_class.columns.each do |column| %>
35
+ <tr>
36
+ <td class="pk icon"><%= column.name == ar_class.primary_key ? icon_fa('check-circle') : '' %>
37
+ </td>
38
+ <td class="monospace"><%= column.name %>
39
+ </td>
40
+ <td><%= column.type %>
41
+ </td>
42
+ <td class="null icon"><%= column.null ? '' : icon_fa('check') %>
43
+ </td>
44
+ <td class="limit number"><%= column.limit %>
45
+ </td>
46
+ <td><%= column.default %></td>
47
+ </tr>
48
+ <% end %>
49
+ </tbody>
50
+ </table>
51
+ </li>
52
+ <% end %>
53
+ </ul>
@@ -0,0 +1 @@
1
+ <pre><%= @result %></pre>
@@ -0,0 +1,5 @@
1
+ <p class="statement-invalid alert alert-danger">
2
+ <strong><%= @statement_invalid.cause.class %>
3
+ </strong><br />
4
+ <%= @statement_invalid.cause.message %>
5
+ </p>
@@ -0,0 +1,21 @@
1
+ <p class="note">
2
+ <%= @result.rows.size %> rows
3
+ </p>
4
+ <table class="table table-striped table-focus">
5
+ <thead>
6
+ <tr>
7
+ <% @result.header.each do |column| %>
8
+ <th><%= column %></th>
9
+ <% end %>
10
+ </tr>
11
+ </thead>
12
+ <tbody>
13
+ <% @result.rows.take(100).each do |row| %>
14
+ <tr>
15
+ <% row.each do |val| %>
16
+ <td><%= val %></td>
17
+ <% end %>
18
+ </tr>
19
+ <% end %>
20
+ </tbody>
21
+ </table>
@@ -0,0 +1,4 @@
1
+ <p class="statement-invalid alert alert-danger">
2
+ <strong><%= @statement_invalid.cause.class %></strong><br />
3
+ <%= @statement_invalid.cause.message %>
4
+ </p>
@@ -0,0 +1,14 @@
1
+ <div id="current-tables">
2
+ <a class="loading" href="<%= current_tables_path %>"></a>
3
+ </div>
4
+ <script>
5
+ $(function() {
6
+
7
+ $(document).on('click', '[data-trigger="toggleCurrentTables"]', function($ev) {
8
+
9
+ Adhoq.toggleCurrentTables($($ev.target).attr('role'), {main: '#main', tables: '#current-tables'});
10
+
11
+ })
12
+
13
+ });
14
+ </script>
@@ -0,0 +1,10 @@
1
+ <tr exec="">
2
+ <td class="wip"></td>
3
+ <td class="created_at"><%= exec.created_at.localtime.iso8601 %></td>
4
+ <td class="status"><%= exec.status_label %></td>
5
+ <td class="report">
6
+ <% if exec.success? %>
7
+ <%= link_to(query_execution_path(query, exec, format: exec.report_format), class: 'btn btn-sm btn-default') do %>
8
+ <i class="fa fa-download fa-pad-r"></i><%= exec.report_format %>
9
+ <% end %>
10
+ <% end %>
@@ -0,0 +1,124 @@
1
+ <%= form_for query, html: {class: 'form query-form', role: 'form'} do |f| %>
2
+ <div class="page-header">
3
+ <h1>
4
+ <%= f.label :query, title, class: 'control-label' %>
5
+ <div class="pull-right">
6
+ <a href="#" class="btn btn-default btn-sm" data-trigger="toggleCurrentTables" role="show">
7
+ <i class="fa fa-database fa-pad-r"></i> Show tables
8
+ </a>
9
+ </div>
10
+ </h1>
11
+ </div>
12
+
13
+ <div class="form-group">
14
+ <%= f.text_area :query, class: 'form-control', rows: 15, required: true %>
15
+ </div>
16
+
17
+ <div class="modal fade" id="nameAndDesc" role="dialog">
18
+ <div class="modal-dialog">
19
+ <div class="modal-content">
20
+ <div class="modal-header">
21
+ <button class="close" data-dismiss="modal" aria-label="close" />
22
+ <button class="close" type="button" data-dismiss="modal" aria-label="close">
23
+ <span aria-hidden="true">&times;</span>
24
+ </button>
25
+
26
+ <h4>Add name and description to query</h4>
27
+ </div>
28
+ <div class="modal-body">
29
+ <div class="form-horizontal">
30
+ <div class="form-group">
31
+ <%= f.label :name, class: 'control-label col-sm-2' %>
32
+ <div class="col-sm-8">
33
+ <%= f.text_field :name, class: 'form-control', required: true %>
34
+ </div>
35
+ </div>
36
+ <div class="form-group">
37
+ <%= f.label :description, class: 'control-label col-sm-2' %>
38
+ <div class="col-sm-8">
39
+ <%= f.text_area :description, class: 'form-control' %>
40
+ </div>
41
+ </div>
42
+ </div>
43
+ </div>
44
+ <div class="modal-footer">
45
+ <button class="btn btn-primary center-block">
46
+ <i class="fa fa-floppy-o fa-pad-r"></i>Save
47
+ </button>
48
+ </div>
49
+ </div>
50
+ </div>
51
+ </div>
52
+
53
+ <div class="actions">
54
+ <% if query.persisted? %>
55
+ <%= link_to query do %>
56
+ <i class="fa fa-arrow-left fa-pad-r"></i>Cancel
57
+ <% end %>
58
+ <% else %>
59
+ <%= link_to :queries do %>
60
+ <i class="fa fa-arrow-left fa-pad-r"></i>Back to Index
61
+ <% end %>
62
+ <% end %>
63
+
64
+ <div class="pull-right">
65
+ <%= link_to '#nameAndDesc', class: 'btn btn-default btn-sm', data: {toggle: 'modal', target: '#nameAndDesc'} do %>
66
+ <i class="fa fa-floppy-o fa-pad-r"></i>Save as...
67
+ <% end %>
68
+ </div>
69
+ </div>
70
+
71
+ <% end %>
72
+
73
+ <ul class="nav nav-tabs" role="tablist">
74
+ <li class="active">
75
+ <a data-toggle="tab" href="#preview" role="tab">
76
+ <i class="fa fa-eye fa-pad-r"></i>Preview
77
+ </a>
78
+ </li>
79
+ <li>
80
+ <a data-toggle="tab" href="#explain" role="tab">
81
+ <i class="fa fa-info fa-pad-r"></i>Explain
82
+ </a>
83
+ </li>
84
+ </ul>
85
+
86
+ <div class="tab-content" id="previews">
87
+ <div class="tab-pane active" id="preview">
88
+ <h3>Query preview
89
+ <small>
90
+ <%= link_to preview_path, class: 'js-preview-button', data: {source: '#query_query', result: '.js-preview-result', remote: true, method: 'POST'} do %>
91
+ <i class="fa fa-refresh fa-pad-r" data-title="Refresh preview"></i> Refresh
92
+ <% end %>
93
+ </small>
94
+ </h3>
95
+ <div class="js-preview-result">
96
+ <div class="alert alert-info">Preview is shown here</div>
97
+ </div>
98
+ </div>
99
+ <div class="tab-pane" id="explain">
100
+ <h3>Query explain
101
+ <small>
102
+ <%= link_to explain_path, class: 'js-explain-button', data: {source: '#query_query', result: '.js-explain-result', remote: true, method: 'POST'} do %>
103
+ <i class="fa fa-refresh fa-pad-r" data-title="Refresh explain"></i> Refresh
104
+ <% end %>
105
+ </small>
106
+ </h3>
107
+ <div class="js-explain-result">
108
+ <div class="alert alert-info">Explain result is shown here</div>
109
+ </div>
110
+ </div>
111
+ </div>
112
+ <script>
113
+ $(function() {
114
+
115
+ Adhoq.enablePreview($('#preview a.js-preview-button'));
116
+
117
+ Adhoq.enablePreview($('#explain a.js-explain-button'));
118
+
119
+
120
+
121
+ Adhoq.enablePreviewKeybordShortCut($('#query_query'), '#previews .tab-pane.active a:has(".fa-refresh")')
122
+
123
+ });
124
+ </script>
@@ -0,0 +1,16 @@
1
+ <% highlight ||= nil %>
2
+ <section>
3
+ <%= link_to :root, class: 'btn btn-default new-query btn-sm center-block' do %>
4
+ <i class="fa fa-plus-square fa-pad-r"></i>New query
5
+ <% end %>
6
+ <ol class="queries-index list-unstyled">
7
+ <% queries.each do |query| %>
8
+ <li class="panel <%= query == highlight ? 'panel-success' : 'panel-default' %>">
9
+ <div class="panel-heading">
10
+ <h2><%= link_to query.name, query_path(query) %></h2>
11
+ </div>
12
+ <p class="panel-body description"><%= query.description %></p>
13
+ </li>
14
+ <% end %>
15
+ </ol>
16
+ </section>
@@ -0,0 +1,67 @@
1
+ <section class="query">
2
+ <div class="page-header">
3
+ <h1>
4
+ <%= query.name %>
5
+ <div class="pull-right">
6
+ <%= link_to [:edit, query], class: 'btn btn-default btn-sm' do %>
7
+ <i class="fa fa-pencil fa-pad-r" ></i> Edit
8
+ <% end %>
9
+ </div>
10
+ <div class="pull-right">
11
+ <%= link_to 'Delete', query, class: 'btn btn-default btn-sm', method: :delete, data: { confirm: 'Are you sure?' } %>
12
+ </div>
13
+ <div class="clearfix" />
14
+ <small><%= "Updated at #{l(query.updated_at, format: :short)}" %></small>
15
+ </h1>
16
+ </div>
17
+
18
+ <p class="description"><%= query.description %></p>
19
+ <style type="text/css">
20
+ <%= Rouge::Themes::Github.render(scope: '.highlight') %>
21
+ </style>
22
+
23
+ <%= raw query.query_with_highlight %>
24
+
25
+ <section class="new-execution">
26
+ <h2>Create report</h2>
27
+ <%= form_for [query, query.executions.build], html: {role: 'form'} do |f| %>
28
+ <div class="form-inline form-group report_format">
29
+ <%= f.label :report_format %>
30
+ <%= f.select :report_format, f.object.supported_formats, {}, class: 'form-control' %>
31
+ <% if query.parameters.present? %>
32
+ <h4>Query parameters</h4>
33
+ <div class="form-group query_parameters">
34
+ <% query.parameters.each do |param_name| %>
35
+ <div class="form-inline query_parameter">
36
+ <%= label_tag "parameters_#{param_name}", "#{param_name}" %>
37
+ <%= query_parameter_field(param_name) %>
38
+ </div>
39
+ <% end %>
40
+ </div>
41
+ <% end %>
42
+ <div class="form-group">
43
+ <%= f.submit 'Create report', class: 'btn btn-default' %>
44
+ </div>
45
+ </div>
46
+ <% end %>
47
+ </section>
48
+ <section class="past-executions">
49
+ <h2>Reports</h2>
50
+ <table class="executions table table-striped table-hover">
51
+ <thead>
52
+ <tr>
53
+ <th class="wip">&nbsp;</th>
54
+ <th class="created_at"><%= human(Adhoq::Execution, :created_at) %></th>
55
+ <th class="status"><%= human(Adhoq::Execution, :status) %></th>
56
+ <th class="report"></th>
57
+ </tr>
58
+ </thead>
59
+ <tbody>
60
+ <% query.executions.recent_first.preload(:report).each do |exec| %>
61
+ <% next if exec.report.try(:on_the_fly?) %>
62
+ <%= render 'execution', query: query, exec: exec %>
63
+ <% end %>
64
+ </tbody>
65
+ </table>
66
+ </section>
67
+ </section>