dbhero 1.0.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (73) hide show
  1. checksums.yaml +7 -0
  2. data/MIT-LICENSE +20 -0
  3. data/Rakefile +21 -0
  4. data/app/assets/javascripts/dbhero/application.js +45 -0
  5. data/app/assets/javascripts/dbhero/dataclips.js +42 -0
  6. data/app/assets/stylesheets/dbhero/application.css.scss +88 -0
  7. data/app/controllers/dbhero/application_controller.rb +30 -0
  8. data/app/controllers/dbhero/dataclips_controller.rb +84 -0
  9. data/app/helpers/dbhero/application_helper.rb +4 -0
  10. data/app/helpers/dbhero/dataclips_helper.rb +4 -0
  11. data/app/models/dbhero/dataclip.rb +50 -0
  12. data/app/views/dbhero/dataclips/_clip_table.html.slim +38 -0
  13. data/app/views/dbhero/dataclips/_form.html.slim +32 -0
  14. data/app/views/dbhero/dataclips/edit.html.slim +6 -0
  15. data/app/views/dbhero/dataclips/index.html.slim +25 -0
  16. data/app/views/dbhero/dataclips/new.html.slim +2 -0
  17. data/app/views/dbhero/dataclips/show.html.slim +18 -0
  18. data/app/views/layouts/dbhero/application.html.slim +24 -0
  19. data/config/routes.rb +6 -0
  20. data/lib/dbhero.rb +7 -0
  21. data/lib/dbhero/configuration.rb +39 -0
  22. data/lib/dbhero/engine.rb +9 -0
  23. data/lib/dbhero/gdrive_exporter.rb +56 -0
  24. data/lib/dbhero/router_constraint.rb +43 -0
  25. data/lib/dbhero/version.rb +3 -0
  26. data/lib/generators/dbhero/install/install_generator.rb +29 -0
  27. data/lib/generators/dbhero/install/templates/dbhero.rb +26 -0
  28. data/lib/generators/dbhero/install/templates/migrations/create_dbhero_dataclips.rb +17 -0
  29. data/lib/tasks/dbhero_tasks.rake +4 -0
  30. data/spec/controllers/dbhero/dataclips_controller_spec.rb +293 -0
  31. data/spec/dummy/README.rdoc +28 -0
  32. data/spec/dummy/Rakefile +6 -0
  33. data/spec/dummy/app/assets/javascripts/application.js +13 -0
  34. data/spec/dummy/app/assets/stylesheets/application.css +15 -0
  35. data/spec/dummy/app/controllers/application_controller.rb +5 -0
  36. data/spec/dummy/app/helpers/application_helper.rb +2 -0
  37. data/spec/dummy/app/views/layouts/application.html.erb +14 -0
  38. data/spec/dummy/bin/bundle +3 -0
  39. data/spec/dummy/bin/rails +4 -0
  40. data/spec/dummy/bin/rake +4 -0
  41. data/spec/dummy/config.ru +4 -0
  42. data/spec/dummy/config/application.rb +27 -0
  43. data/spec/dummy/config/boot.rb +5 -0
  44. data/spec/dummy/config/database.yml +85 -0
  45. data/spec/dummy/config/environment.rb +5 -0
  46. data/spec/dummy/config/environments/development.rb +37 -0
  47. data/spec/dummy/config/environments/production.rb +78 -0
  48. data/spec/dummy/config/environments/test.rb +39 -0
  49. data/spec/dummy/config/initializers/assets.rb +8 -0
  50. data/spec/dummy/config/initializers/backtrace_silencers.rb +7 -0
  51. data/spec/dummy/config/initializers/cookies_serializer.rb +3 -0
  52. data/spec/dummy/config/initializers/dbhero.rb +26 -0
  53. data/spec/dummy/config/initializers/filter_parameter_logging.rb +4 -0
  54. data/spec/dummy/config/initializers/inflections.rb +16 -0
  55. data/spec/dummy/config/initializers/mime_types.rb +4 -0
  56. data/spec/dummy/config/initializers/session_store.rb +3 -0
  57. data/spec/dummy/config/initializers/wrap_parameters.rb +14 -0
  58. data/spec/dummy/config/locales/en.yml +23 -0
  59. data/spec/dummy/config/routes.rb +4 -0
  60. data/spec/dummy/config/secrets.yml +22 -0
  61. data/spec/dummy/db/migrate/20150323172444_create_dbhero_dataclips.rb +17 -0
  62. data/spec/dummy/db/schema.rb +32 -0
  63. data/spec/dummy/log/development.log +27 -0
  64. data/spec/dummy/log/test.log +3758 -0
  65. data/spec/dummy/public/404.html +67 -0
  66. data/spec/dummy/public/422.html +67 -0
  67. data/spec/dummy/public/500.html +66 -0
  68. data/spec/dummy/public/favicon.ico +0 -0
  69. data/spec/factories.rb +8 -0
  70. data/spec/models/dbhero/dataclip_spec.rb +119 -0
  71. data/spec/rails_helper.rb +57 -0
  72. data/spec/spec_helper.rb +90 -0
  73. metadata +312 -0
checksums.yaml ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: 04ea97a58981185948410f15d73215eb9caa4c7e
4
+ data.tar.gz: fb418837a8c175fec3dcd489427203da481b3a18
5
+ SHA512:
6
+ metadata.gz: 25d1e1f03d9fbf3af2457c4f2cf3d22e829737804703f010d9f2ca145f38266beea804810e8faf4c39b29ea9cba7ece97eef915c531e125d6fb1676436b76b58
7
+ data.tar.gz: 4d959ad723d1be89cd081f37a3ade030e80685f408afcc26b29680f9ffbbf611d6a809709350df240c0975325ae7522f032e5d5ed6c22ac80d97062b61630aa6
data/MIT-LICENSE ADDED
@@ -0,0 +1,20 @@
1
+ Copyright 2015 YOURNAME
2
+
3
+ Permission is hereby granted, free of charge, to any person obtaining
4
+ a copy of this software and associated documentation files (the
5
+ "Software"), to deal in the Software without restriction, including
6
+ without limitation the rights to use, copy, modify, merge, publish,
7
+ distribute, sublicense, and/or sell copies of the Software, and to
8
+ permit persons to whom the Software is furnished to do so, subject to
9
+ the following conditions:
10
+
11
+ The above copyright notice and this permission notice shall be
12
+ included in all copies or substantial portions of the Software.
13
+
14
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
15
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
16
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
17
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
18
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
19
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
20
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
data/Rakefile ADDED
@@ -0,0 +1,21 @@
1
+ #!/usr/bin/env rake
2
+ begin
3
+ require 'bundler/setup'
4
+ rescue LoadError
5
+ puts 'You must `gem install bundler` and `bundle install` to run rake tasks'
6
+ end
7
+
8
+ APP_RAKEFILE = File.expand_path("../spec/dummy/Rakefile", __FILE__)
9
+
10
+ load 'rails/tasks/engine.rake'
11
+
12
+ Bundler::GemHelper.install_tasks
13
+
14
+ Dir[File.join(File.dirname(__FILE__), 'tasks/**/*.rake')].each {|f| load f }
15
+
16
+ require 'rspec/core'
17
+ require 'rspec/core/rake_task'
18
+
19
+ desc "Run all specs in spec directory (excluding plugin specs)"
20
+ RSpec::Core::RakeTask.new(:spec => 'app:db:test:prepare')
21
+ task :default => :spec
@@ -0,0 +1,45 @@
1
+ // This is a manifest file that'll be compiled into application.js, which will include all the files
2
+ // listed below.
3
+ //
4
+ // Any JavaScript/Coffee file within this directory, lib/assets/javascripts, vendor/assets/javascripts,
5
+ // or vendor/assets/javascripts of plugins, if any, can be referenced here using a relative path.
6
+ //
7
+ // It's not advisable to add code directly here, but if you do, it'll appear at the bottom of the
8
+ // compiled file.
9
+ //
10
+ // Read Sprockets README (https://github.com/sstephenson/sprockets#sprockets-directives) for details
11
+ // about supported directives.
12
+ //
13
+ //= require jquery
14
+ //= require_tree .
15
+
16
+ $(function(){
17
+
18
+ $('.dropdown-button').dropdown({
19
+ inDuration: 300,
20
+ outDuration: 225,
21
+ constrain_width: false, // Does not change width of dropdown to that of the activator
22
+ hover: false, // Activate on click
23
+ alignment: 'left', // Aligns dropdown to left or right edge (works with constrain_width)
24
+ gutter: 0, // Spacing from edge
25
+ belowOrigin: false // Displays dropdown below the button
26
+ }
27
+ );
28
+
29
+ try {
30
+ var $table = $('table#clip_table')
31
+
32
+ $table.dataTable({
33
+ scrollX: true,
34
+ searching: false,
35
+ lengthChange: false,
36
+ pagingType: 'simple'
37
+ });
38
+
39
+ //$table.floatThead({
40
+ // scrollingTop: 'pageTop',
41
+ //});
42
+ } catch(e) {
43
+ console.log(e);
44
+ }
45
+ });
@@ -0,0 +1,42 @@
1
+ $(function(){
2
+
3
+ startAce = function() {
4
+ var textarea = $('textarea#dataclip_raw_query');
5
+ console.log(textarea.width());
6
+ console.log(textarea.height());
7
+
8
+ var editDiv = $('<div>', {
9
+ id: "ace_editor",
10
+ position: 'absolute',
11
+ width: textarea.width(),
12
+ height: textarea.height(),
13
+ 'class': textarea.attr('class')
14
+ }).insertBefore(textarea);
15
+
16
+ textarea.css('display', 'none');
17
+
18
+ var editor = ace.edit(editDiv[0]);
19
+ editor.renderer.setShowGutter(true);
20
+
21
+ editor.getSession().setUseWrapMode(true);
22
+ editor.getSession().setValue(textarea.val());
23
+ editor.getSession().setTabSize(2);
24
+ editor.getSession().setUseSoftTabs(true);
25
+ editor.getSession().setMode("ace/mode/pgsql");
26
+
27
+ textarea.closest('form').submit(function () {
28
+ textarea.val(editor.getSession().getValue());
29
+ });
30
+
31
+ $('.ace_editor').css({'padding':'0'});
32
+
33
+ editor.setTheme("ace/theme/xcode");
34
+ $(editDiv).css({ 'font-size': '15px' })
35
+ }
36
+
37
+ if($('textarea#dataclip_raw_query').length > 0){
38
+ startAce();
39
+ }
40
+
41
+ });
42
+
@@ -0,0 +1,88 @@
1
+ /*
2
+ * This is a manifest file that'll be compiled into application.css, which will include all the files
3
+ * listed below.
4
+ *
5
+ * Any CSS and SCSS file within this directory, lib/assets/stylesheets, vendor/assets/stylesheets,
6
+ * or vendor/assets/stylesheets of plugins, if any, can be referenced here using a relative path.
7
+ *
8
+ * You're free to add application-wide styles to this file and they'll appear at the bottom of the
9
+ * compiled file so the styles you add here take precedence over styles defined in any styles
10
+ * defined in the other CSS/SCSS files in this directory. It is generally better to create a new
11
+ * file per style scope.
12
+ *
13
+ *= require_tree .
14
+ *= require_self
15
+ */
16
+ .editor-style {
17
+ height: 300px;
18
+ border-radius: 1px;
19
+ }
20
+
21
+ header {
22
+ border-bottom: 1px solid #cfd8dc;
23
+ .row {
24
+ margin-bottom: 0;
25
+ }
26
+ }
27
+ .dataTables_scrollBody {
28
+ overflow-y: hidden !important;
29
+ }
30
+
31
+ .bottom-bord {
32
+ border-bottom: 1px solid #cfd8dc;
33
+ }
34
+ .clip-title {
35
+ p {
36
+ margin-bottom: 5px;
37
+ }
38
+ }
39
+
40
+ .end {
41
+ margin-bottom: 0 !important;
42
+ }
43
+ header .btn-group {
44
+ margin-top: 15px;
45
+ }
46
+
47
+ header h1 {
48
+ font-size: 22px;
49
+ margin: 0;
50
+ color: #455a64;
51
+ }
52
+
53
+ .style-text-field {
54
+ /* label color */
55
+ .input-field label {
56
+ color: #455a64;
57
+ font-size: 22px;
58
+ }
59
+ /* label focus color */
60
+ .input-field input[type=text]:focus + label,
61
+ .input-field textarea:focus + label,
62
+ {
63
+ color: #90a4ae;
64
+ font-size: 18px;
65
+ }
66
+ /* label underline focus color */
67
+ .input-field input[type=text]:focus,
68
+ .input-field textarea:focus {
69
+ border-bottom: 1px solid #455a64;
70
+ box-shadow: 0 1px 0 0 #455a64;
71
+ }
72
+ /* valid color */
73
+ .input-field input[type=text].valid,
74
+ .input-field textarea.valid {
75
+ border-bottom: 1px solid #455a64;
76
+ box-shadow: 0 1px 0 0 #455a64;
77
+ }
78
+ /* invalid color */
79
+ .input-field input[type=text].invalid,
80
+ .input-field textarea.invalid {
81
+ border-bottom: 1px solid #455a64;
82
+ box-shadow: 0 1px 0 0 #455a64;
83
+ }
84
+ /* icon prefix focus color */
85
+ .input-field .prefix.active {
86
+ color: #455a64;
87
+ }
88
+ }
@@ -0,0 +1,30 @@
1
+ module Dbhero
2
+ class ApplicationController < ActionController::Base
3
+ def check_auth
4
+ if Dbhero.authenticate
5
+ unless _current_user && call_custom_auth
6
+ raise ActionController::RoutingError.new('Forbidden')
7
+ end
8
+ end
9
+ end
10
+
11
+ def user_representation
12
+ _current_user.send(Dbhero.user_representation) if _current_user
13
+ end
14
+
15
+ def _current_user
16
+ if Dbhero.current_user_method.present?
17
+ send(Dbhero.current_user_method)
18
+ end
19
+ end
20
+
21
+ def call_custom_auth
22
+ cond = Dbhero.custom_user_auth_condition
23
+ if cond.present? && cond.is_a?(Proc)
24
+ cond.call(_current_user)
25
+ else
26
+ true
27
+ end
28
+ end
29
+ end
30
+ end
@@ -0,0 +1,84 @@
1
+ require_dependency "dbhero/application_controller"
2
+ require_dependency "responders"
3
+ require_dependency "google/api_client"
4
+ require_dependency "google_drive"
5
+
6
+ module Dbhero
7
+ class DataclipsController < ApplicationController
8
+ before_action :check_auth, except: [:show]
9
+ before_action :set_dataclip, only: [:show, :edit, :update, :destroy]
10
+ has_scope :search
11
+ respond_to :html, :csv
12
+
13
+ def index
14
+ @dataclips = apply_scopes(Dataclip.ordered)
15
+ end
16
+
17
+ def drive
18
+ token = session.delete(:clip_token)
19
+
20
+ google_client.fetch_access_token!(params[:code])
21
+ google_client.options[:import_data_url] = dataclip_url(id: token, format: 'csv')
22
+ google_client.export_clip_by_token token
23
+
24
+ redirect_to dataclip_path(google_client.dataclip, gdrive_file_url: google_client.exported_file_url)
25
+ end
26
+
27
+ def show
28
+ check_auth if @dataclip.private?
29
+
30
+ @dataclip.query_result
31
+
32
+ respond_to do |format|
33
+ format.html do
34
+ if params[:export_gdrive]
35
+ session[:clip_token] = @dataclip.token
36
+ redirect_to google_client.auth.authorization_uri.to_s
37
+ end
38
+ end
39
+
40
+ format.csv do
41
+ send_data @dataclip.csv_string, type: Mime::CSV, disposition: "attachment; filename=#{@dataclip.token}.csv"
42
+ end
43
+ end
44
+ end
45
+
46
+ def new
47
+ @dataclip = Dataclip.new
48
+ end
49
+
50
+ def edit
51
+ @dataclip.query_result
52
+ end
53
+
54
+ def create
55
+ @dataclip = Dataclip.create(dataclip_params.merge(user: user_representation))
56
+ respond_with @dataclip, location: edit_dataclip_path(@dataclip),notice: 'Dataclip was successfully created.'
57
+ end
58
+
59
+ def update
60
+ @dataclip.update(dataclip_params)
61
+ respond_with @dataclip, location: edit_dataclip_path(@dataclip), notice: 'Dataclip was successfully updated.'
62
+ end
63
+
64
+ def destroy
65
+ @dataclip.destroy
66
+ redirect_to dataclips_url, notice: 'Dataclip was successfully destroyed.'
67
+ end
68
+
69
+ private
70
+ # Use callbacks to share common setup or constraints between actions.
71
+ def set_dataclip
72
+ @dataclip = Dataclip.find_by_token(params[:id])
73
+ end
74
+
75
+ # Only allow a trusted parameter "white list" through.
76
+ def dataclip_params
77
+ params.require(:dataclip).permit(:description, :raw_query, :private)
78
+ end
79
+
80
+ def google_client
81
+ @g_client ||= Dbhero::GdriveExporter.new(redirect_uri: drive_dataclips_url)
82
+ end
83
+ end
84
+ end
@@ -0,0 +1,4 @@
1
+ module Dbhero
2
+ module ApplicationHelper
3
+ end
4
+ end
@@ -0,0 +1,4 @@
1
+ module Dbhero
2
+ module DataclipsHelper
3
+ end
4
+ end
@@ -0,0 +1,50 @@
1
+ module Dbhero
2
+ class Dataclip < ActiveRecord::Base
3
+ before_create :set_token
4
+
5
+ scope :ordered, -> { order(updated_at: :desc) }
6
+ scope :search, ->(term) { where(arel_table[:description].matches("%#{term}%")) }
7
+
8
+ validates :description, :raw_query, presence: true
9
+ attr_reader :q_result
10
+
11
+ def set_token
12
+ self.token = SecureRandom.uuid unless self.token
13
+ end
14
+
15
+ def to_param
16
+ self.token
17
+ end
18
+
19
+ def title
20
+ description.split("\n")[0]
21
+ end
22
+
23
+ def description_without_title
24
+ description.split("\n")[1..-1].join("\n")
25
+ end
26
+
27
+ def total_rows
28
+ @total_rows ||= @q_result.rows.length
29
+ end
30
+
31
+ def query_result
32
+ Dataclip.transaction do
33
+ begin
34
+ @q_result ||= ActiveRecord::Base.connection.select_all(self.raw_query)
35
+ rescue => e
36
+ self.errors.add(:base, e.message)
37
+ end
38
+ raise ActiveRecord::Rollback
39
+ end
40
+ end
41
+
42
+ def csv_string
43
+ query_result
44
+ csv = ''
45
+ csv << "#{@q_result.columns.join(',')}\n"
46
+ @q_result.rows.each { |row| csv << "#{row.join(',')}\n" }
47
+ csv
48
+ end
49
+ end
50
+ end
@@ -0,0 +1,38 @@
1
+ = stylesheet_link_tag "//cdn.datatables.net/1.10.5/css/jquery.dataTables.min.css"
2
+ = javascript_include_tag "//cdn.datatables.net/1.10.5/js/jquery.dataTables.min.js"
3
+ = javascript_include_tag "//cdnjs.cloudflare.com/ajax/libs/floatthead/1.2.10/jquery.floatThead.min.js"
4
+
5
+ - show_public_link = defined?(show_public_link) ? show_public_link : false
6
+ .section
7
+ .row
8
+ .col.s6.style-text-field
9
+ - if show_public_link
10
+ .input-field
11
+ i.tiny.mdi-social-share.prefix
12
+ input#icon_prefix.validate type="text" value=dataclip_url(@dataclip)
13
+ label for="icon_prefix"
14
+ strong PUBLIC URL
15
+ - else
16
+ p style="margin-top: 40px; margin-bottom: 0;"
17
+ | total rows returned&nbsp;
18
+ strong= @dataclip.total_rows
19
+ .col.s6.right-align style="margin-top: 25px;"
20
+ a class='dropdown-button btn' href='#' data-activates='export-drop' Export
21
+
22
+ ul id='export-drop' class='dropdown-content'
23
+ li
24
+ = link_to 'csv', dataclip_path(@dataclip, format: 'csv')
25
+ li
26
+ = link_to 'google drive', dataclip_path(@dataclip, export_gdrive: true)
27
+
28
+ .col.s12 style="overflow: auto;"
29
+ table#clip_table.cell-border.striped data-page-length='300'
30
+ thead.grey.lighten-2
31
+ tr
32
+ - @dataclip.q_result.columns.each do |column|
33
+ th= column
34
+ tbody
35
+ - @dataclip.q_result.rows.each do |row|
36
+ tr
37
+ - row.each do |value|
38
+ td style="min-width: 200px;"= value