data-world-activerecord-adapter 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA256:
3
+ metadata.gz: 0dd97f1bb7c5a2538a603d4350e774d4a1df645d52a6826bc7cc736d69098429
4
+ data.tar.gz: aefddbc5f90890db9cd22c0b12f549e1c7ccd2e13dec5ffb3d5172d757922450
5
+ SHA512:
6
+ metadata.gz: 37ea52d1963dd7954af4dd365a21d604006494d5063c6602632f7cec79b423c3ce963ca2f677755c355e0f90b4bff1bea7184026ab7d95a0a9a938b3506f373d
7
+ data.tar.gz: 1e80c2001038c400c26cbe001c358a02c8957464b66f42f55a6833c1941161f92152f913efd9ac413a0dc79ceb549ac0ee45d5b013b077a8d87c1020bf946759
@@ -0,0 +1,8 @@
1
+ /.bundle/
2
+ /.yardoc
3
+ /_yardoc/
4
+ /coverage/
5
+ /doc/
6
+ /pkg/
7
+ /spec/reports/
8
+ /tmp/
data/Gemfile ADDED
@@ -0,0 +1,6 @@
1
+ source "https://rubygems.org"
2
+
3
+ # Specify your gem's dependencies in data-world-activerecord-adapter.gemspec
4
+ gemspec
5
+
6
+ gem "rake", "~> 12.0"
@@ -0,0 +1,142 @@
1
+ PATH
2
+ remote: .
3
+ specs:
4
+ data-world-activerecord-adapter (0.1.0)
5
+ rails (~> 6.0.0)
6
+
7
+ GEM
8
+ remote: https://rubygems.org/
9
+ specs:
10
+ actioncable (6.0.3.3)
11
+ actionpack (= 6.0.3.3)
12
+ nio4r (~> 2.0)
13
+ websocket-driver (>= 0.6.1)
14
+ actionmailbox (6.0.3.3)
15
+ actionpack (= 6.0.3.3)
16
+ activejob (= 6.0.3.3)
17
+ activerecord (= 6.0.3.3)
18
+ activestorage (= 6.0.3.3)
19
+ activesupport (= 6.0.3.3)
20
+ mail (>= 2.7.1)
21
+ actionmailer (6.0.3.3)
22
+ actionpack (= 6.0.3.3)
23
+ actionview (= 6.0.3.3)
24
+ activejob (= 6.0.3.3)
25
+ mail (~> 2.5, >= 2.5.4)
26
+ rails-dom-testing (~> 2.0)
27
+ actionpack (6.0.3.3)
28
+ actionview (= 6.0.3.3)
29
+ activesupport (= 6.0.3.3)
30
+ rack (~> 2.0, >= 2.0.8)
31
+ rack-test (>= 0.6.3)
32
+ rails-dom-testing (~> 2.0)
33
+ rails-html-sanitizer (~> 1.0, >= 1.2.0)
34
+ actiontext (6.0.3.3)
35
+ actionpack (= 6.0.3.3)
36
+ activerecord (= 6.0.3.3)
37
+ activestorage (= 6.0.3.3)
38
+ activesupport (= 6.0.3.3)
39
+ nokogiri (>= 1.8.5)
40
+ actionview (6.0.3.3)
41
+ activesupport (= 6.0.3.3)
42
+ builder (~> 3.1)
43
+ erubi (~> 1.4)
44
+ rails-dom-testing (~> 2.0)
45
+ rails-html-sanitizer (~> 1.1, >= 1.2.0)
46
+ activejob (6.0.3.3)
47
+ activesupport (= 6.0.3.3)
48
+ globalid (>= 0.3.6)
49
+ activemodel (6.0.3.3)
50
+ activesupport (= 6.0.3.3)
51
+ activerecord (6.0.3.3)
52
+ activemodel (= 6.0.3.3)
53
+ activesupport (= 6.0.3.3)
54
+ activestorage (6.0.3.3)
55
+ actionpack (= 6.0.3.3)
56
+ activejob (= 6.0.3.3)
57
+ activerecord (= 6.0.3.3)
58
+ marcel (~> 0.3.1)
59
+ activesupport (6.0.3.3)
60
+ concurrent-ruby (~> 1.0, >= 1.0.2)
61
+ i18n (>= 0.7, < 2)
62
+ minitest (~> 5.1)
63
+ tzinfo (~> 1.1)
64
+ zeitwerk (~> 2.2, >= 2.2.2)
65
+ builder (3.2.4)
66
+ concurrent-ruby (1.1.7)
67
+ crass (1.0.6)
68
+ erubi (1.9.0)
69
+ globalid (0.4.2)
70
+ activesupport (>= 4.2.0)
71
+ i18n (1.8.5)
72
+ concurrent-ruby (~> 1.0)
73
+ loofah (2.7.0)
74
+ crass (~> 1.0.2)
75
+ nokogiri (>= 1.5.9)
76
+ mail (2.7.1)
77
+ mini_mime (>= 0.1.1)
78
+ marcel (0.3.3)
79
+ mimemagic (~> 0.3.2)
80
+ method_source (1.0.0)
81
+ mimemagic (0.3.5)
82
+ mini_mime (1.0.2)
83
+ mini_portile2 (2.4.0)
84
+ minitest (5.14.2)
85
+ nio4r (2.5.4)
86
+ nokogiri (1.10.10)
87
+ mini_portile2 (~> 2.4.0)
88
+ rack (2.2.3)
89
+ rack-test (1.1.0)
90
+ rack (>= 1.0, < 3)
91
+ rails (6.0.3.3)
92
+ actioncable (= 6.0.3.3)
93
+ actionmailbox (= 6.0.3.3)
94
+ actionmailer (= 6.0.3.3)
95
+ actionpack (= 6.0.3.3)
96
+ actiontext (= 6.0.3.3)
97
+ actionview (= 6.0.3.3)
98
+ activejob (= 6.0.3.3)
99
+ activemodel (= 6.0.3.3)
100
+ activerecord (= 6.0.3.3)
101
+ activestorage (= 6.0.3.3)
102
+ activesupport (= 6.0.3.3)
103
+ bundler (>= 1.3.0)
104
+ railties (= 6.0.3.3)
105
+ sprockets-rails (>= 2.0.0)
106
+ rails-dom-testing (2.0.3)
107
+ activesupport (>= 4.2.0)
108
+ nokogiri (>= 1.6)
109
+ rails-html-sanitizer (1.3.0)
110
+ loofah (~> 2.3)
111
+ railties (6.0.3.3)
112
+ actionpack (= 6.0.3.3)
113
+ activesupport (= 6.0.3.3)
114
+ method_source
115
+ rake (>= 0.8.7)
116
+ thor (>= 0.20.3, < 2.0)
117
+ rake (12.3.3)
118
+ sprockets (4.0.2)
119
+ concurrent-ruby (~> 1.0)
120
+ rack (> 1, < 3)
121
+ sprockets-rails (3.2.2)
122
+ actionpack (>= 4.0)
123
+ activesupport (>= 4.0)
124
+ sprockets (>= 3.0.0)
125
+ thor (1.0.1)
126
+ thread_safe (0.3.6)
127
+ tzinfo (1.2.7)
128
+ thread_safe (~> 0.1)
129
+ websocket-driver (0.7.3)
130
+ websocket-extensions (>= 0.1.0)
131
+ websocket-extensions (0.1.5)
132
+ zeitwerk (2.4.0)
133
+
134
+ PLATFORMS
135
+ ruby
136
+
137
+ DEPENDENCIES
138
+ data-world-activerecord-adapter!
139
+ rake (~> 12.0)
140
+
141
+ BUNDLED WITH
142
+ 2.1.4
@@ -0,0 +1,21 @@
1
+ The MIT License (MIT)
2
+
3
+ Copyright (c) 2020 Spencer Oberstadt
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in
13
+ all copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
21
+ THE SOFTWARE.
@@ -0,0 +1,68 @@
1
+ # DataWorld::Activerecord::Adapter
2
+
3
+ This gem was written in one day. It works, but I wouldn't trust it further than I could throw it.
4
+
5
+ ## Installation
6
+
7
+ Add this line to your application's Gemfile:
8
+
9
+ ```ruby
10
+ gem 'data-world-activerecord-adapter'
11
+ ```
12
+
13
+ And then execute:
14
+
15
+ $ bundle install
16
+
17
+ Or install it yourself as:
18
+
19
+ $ gem install data-world-activerecord-adapter
20
+
21
+ ## Usage
22
+
23
+ Add something like this to your database config:
24
+ ```ruby
25
+ default: &default
26
+ adapter: 'data_world'
27
+ owner: 'marvel'
28
+ id: 'avengers-dataset'
29
+ auth_token: <%= ENV['DATA_WORLD_TOKEN'] %>
30
+ ```
31
+
32
+ or if you want one model to do this, you can do so with:
33
+ ```ruby
34
+ class AssembledModel < ApplicationRecord
35
+ establish_connection({
36
+ adapter: 'data_world',
37
+ owner: 'marvel',
38
+ id: 'avengers-dataset',
39
+ auth_token: <%= ENV['DATA_WORLD_TOKEN'] %>
40
+ })
41
+ end
42
+ ```
43
+
44
+ You will probably want to manually set your table names and primary keys:
45
+ ```ruby
46
+ class AssembledModel < ApplicationRecord
47
+ self.table_name = 'siu_donation'
48
+
49
+ def self.primary_key
50
+ 'row_id'
51
+ end
52
+ end
53
+ ```
54
+
55
+ ## Development
56
+
57
+ After checking out the repo, run `bin/setup` to install dependencies.
58
+
59
+ To install this gem onto your local machine, run `bundle exec rake install`. To release a new version, update the version number in `version.rb`, and then run `bundle exec rake release`, which will create a git tag for the version, push git commits and tags, and push the `.gem` file to [rubygems.org](https://rubygems.org).
60
+
61
+ ## Contributing
62
+
63
+ Bug reports and pull requests are welcome on GitHub at https://github.com/soberstadt/data-world-activerecord-adapter.
64
+
65
+
66
+ ## License
67
+
68
+ The gem is available as open source under the terms of the [MIT License](https://opensource.org/licenses/MIT).
@@ -0,0 +1,2 @@
1
+ require "bundler/gem_tasks"
2
+ task :default => :spec
@@ -0,0 +1,8 @@
1
+ #!/usr/bin/env bash
2
+ set -euo pipefail
3
+ IFS=$'\n\t'
4
+ set -vx
5
+
6
+ bundle install
7
+
8
+ # Do any other automated setup that you need to do here
@@ -0,0 +1,28 @@
1
+ require_relative 'lib/version'
2
+
3
+ Gem::Specification.new do |spec|
4
+ spec.name = "data-world-activerecord-adapter"
5
+ spec.version = DataWorldAdapter::VERSION
6
+ spec.authors = ["Spencer Oberstadt"]
7
+ spec.email = ["soberstadt@gmail.com"]
8
+
9
+ spec.summary = "Use ActiveRecord to connect with Data.World"
10
+ spec.homepage = 'https://github.com/soberstadt/data-world-activerecord-adapter'
11
+ spec.license = "MIT"
12
+ spec.required_ruby_version = Gem::Requirement.new(">= 2.3.0")
13
+
14
+ spec.metadata["homepage_uri"] = spec.homepage
15
+ spec.metadata["source_code_uri"] = spec.homepage
16
+
17
+ # Specify which files should be added to the gem when it is released.
18
+ # The `git ls-files -z` loads the files in the RubyGem that have been added into git.
19
+ spec.files = Dir.chdir(File.expand_path('..', __FILE__)) do
20
+ `git ls-files -z`.split("\x0").reject { |f| f.match(%r{^(test|spec|features)/}) }
21
+ end
22
+ spec.bindir = "exe"
23
+ spec.executables = spec.files.grep(%r{^exe/}) { |f| File.basename(f) }
24
+ spec.require_paths = ["lib"]
25
+
26
+ # this likely works with other and older versions of rails, but I haven't tested it
27
+ spec.add_dependency 'rails', '~> 6.0.0'
28
+ end
@@ -0,0 +1,118 @@
1
+ require 'active_record/connection_adapters/abstract_adapter'
2
+ require 'uri'
3
+ require 'net/http'
4
+ require 'openssl'
5
+
6
+ module ActiveRecord
7
+ class Base
8
+ class << self
9
+ def data_world_connection(config)
10
+ ConnectionAdapters::DataWorldAdapter.new({}, logger, config)
11
+ end
12
+ end
13
+ end
14
+
15
+ module ConnectionAdapters #:nodoc:
16
+
17
+ class DataWorldAdapter < AbstractAdapter
18
+ def adapter_name #:nodoc:
19
+ 'Data.World'
20
+ end
21
+
22
+ def requires_reloading?
23
+ true
24
+ end
25
+
26
+ def supports_count_distinct? #:nodoc:
27
+ true
28
+ end
29
+
30
+
31
+ # DATABASE STATEMENTS ======================================
32
+
33
+ def execute(sql, name = nil) #:nodoc:
34
+ url = URI("https://api.data.world/v0/sql/#{@config[:owner]}/#{@config[:id]}")
35
+
36
+ http = Net::HTTP.new(url.host, url.port)
37
+ http.use_ssl = true
38
+ http.verify_mode = OpenSSL::SSL::VERIFY_NONE
39
+
40
+ request = Net::HTTP::Post.new(url)
41
+ request["content-type"] = 'application/json'
42
+ request["authorization"] = "Bearer #{@config[:auth_token]}"
43
+ request.body = {
44
+ query: sql,
45
+ includeTableSchema: true
46
+ }.to_json
47
+
48
+ response = http.request(request)
49
+
50
+ try_json response.read_body
51
+ end
52
+
53
+ def exec_query(sql, name = nil, binds = [], prepare: false)
54
+ if preventing_writes? && write_query?(sql)
55
+ raise ActiveRecord::ReadOnlyError, "Data.World is read-only, query not accepted: #{sql}"
56
+ end
57
+
58
+ type_casted_binds = type_casted_binds(binds)
59
+
60
+ log(sql, name, binds, type_casted_binds) do
61
+ raw = execute(sql_bind(sql, binds))
62
+ cols = raw.dig(0, 'fields')&.map { |h| h['name'] }
63
+ records = raw[1..-1].map(&:values)
64
+
65
+ ActiveRecord::Result.new(cols, records)
66
+ end
67
+ end
68
+
69
+ # do our best job binding the variables into the query
70
+ def sql_bind(sql, binds)
71
+ new_sql = sql.clone
72
+ binds.each do |attribute|
73
+ value = if attribute.value.is_a?(String)
74
+ "'#{attribute.value}'"
75
+ else
76
+ attribute.value.to_s
77
+ end
78
+
79
+ new_sql = new_sql.sub('?', value)
80
+ end
81
+ new_sql
82
+ end
83
+
84
+ def try_json(string)
85
+ JSON.parse(string)
86
+ rescue
87
+ raise string
88
+ end
89
+
90
+
91
+ # SCHEMA STATEMENTS ========================================
92
+
93
+ def tables(name = nil) #:nodoc:
94
+ sql = "SELECT tableName FROM Tables"
95
+ execute(sql, name).map { |row| row[0] }
96
+ end
97
+
98
+ def views
99
+ []
100
+ end
101
+
102
+ def columns(table_name, name = nil) #:nodoc:
103
+ table_structure(table_name).map do |field|
104
+ type_metadata = SqlTypeMetadata.new(sql_type: field['columnDatatype'])
105
+ ActiveRecord::ConnectionAdapters::Column.new(field['columnName'], nil, type_metadata)
106
+ end
107
+ end
108
+
109
+ protected
110
+
111
+ def table_structure(table_name)
112
+ columns = execute("SELECT * FROM TableColumns WHERE tableName = \"#{table_name}\"")[1..-1]
113
+ raise(ActiveRecord::StatementInvalid, "Could not find table '#{table_name}'") if columns.empty?
114
+ columns
115
+ end
116
+ end
117
+ end
118
+ end
@@ -0,0 +1,3 @@
1
+ module DataWorldAdapter
2
+ VERSION = "0.1.0"
3
+ end
metadata ADDED
@@ -0,0 +1,69 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: data-world-activerecord-adapter
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.1.0
5
+ platform: ruby
6
+ authors:
7
+ - Spencer Oberstadt
8
+ autorequire:
9
+ bindir: exe
10
+ cert_chain: []
11
+ date: 2020-10-02 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: rails
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - "~>"
18
+ - !ruby/object:Gem::Version
19
+ version: 6.0.0
20
+ type: :runtime
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - "~>"
25
+ - !ruby/object:Gem::Version
26
+ version: 6.0.0
27
+ description:
28
+ email:
29
+ - soberstadt@gmail.com
30
+ executables: []
31
+ extensions: []
32
+ extra_rdoc_files: []
33
+ files:
34
+ - ".gitignore"
35
+ - Gemfile
36
+ - Gemfile.lock
37
+ - LICENSE.txt
38
+ - README.md
39
+ - Rakefile
40
+ - bin/setup
41
+ - data-world-activerecord-adapter.gemspec
42
+ - lib/active_record/connection_adapters/data_world_adapter.rb
43
+ - lib/version.rb
44
+ homepage: https://github.com/soberstadt/data-world-activerecord-adapter
45
+ licenses:
46
+ - MIT
47
+ metadata:
48
+ homepage_uri: https://github.com/soberstadt/data-world-activerecord-adapter
49
+ source_code_uri: https://github.com/soberstadt/data-world-activerecord-adapter
50
+ post_install_message:
51
+ rdoc_options: []
52
+ require_paths:
53
+ - lib
54
+ required_ruby_version: !ruby/object:Gem::Requirement
55
+ requirements:
56
+ - - ">="
57
+ - !ruby/object:Gem::Version
58
+ version: 2.3.0
59
+ required_rubygems_version: !ruby/object:Gem::Requirement
60
+ requirements:
61
+ - - ">="
62
+ - !ruby/object:Gem::Version
63
+ version: '0'
64
+ requirements: []
65
+ rubygems_version: 3.1.2
66
+ signing_key:
67
+ specification_version: 4
68
+ summary: Use ActiveRecord to connect with Data.World
69
+ test_files: []