big_query_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
+ SHA1:
3
+ metadata.gz: ff8793c82931ac9adc5f02f8ebf19020aa93d35a
4
+ data.tar.gz: ec244155405cc0d2c4f5c5f58a8698ebe8e4b97b
5
+ SHA512:
6
+ metadata.gz: 5ce19466cf43782c07a92da4661159236e97e8ed6834aa45095a4c8ded1c1778b7b155871fa7c2b5d5681bba3569fd21113205a8ba35e609b6655abbe9277e7a
7
+ data.tar.gz: ef3f534b1b345a10763a319f0f30a6653cc412ffc605b5497aaedd37b351da6f9985cc9432ad12f2130c755762b7c7b47ddbdf080eedccf765ec42c784b0f209
@@ -0,0 +1,12 @@
1
+ /.bundle/
2
+ /.yardoc
3
+ /Gemfile.lock
4
+ /_yardoc/
5
+ /coverage/
6
+ /doc/
7
+ /pkg/
8
+ /spec/reports/
9
+ /tmp/
10
+ /bundle/
11
+ /etc/
12
+ .DS_Store
@@ -0,0 +1,5 @@
1
+ sudo: false
2
+ language: ruby
3
+ rvm:
4
+ - 2.3.1
5
+ before_install: gem install bundler -v 1.13.6
data/Gemfile ADDED
@@ -0,0 +1,6 @@
1
+ source 'https://rubygems.org'
2
+
3
+ gemspec
4
+
5
+ gem 'activerecord', '5.0.1'
6
+ gem 'pry', '0.10.4'
data/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ The MIT License
2
+
3
+ Copyright (c) 2017 Localytics http://www.localytics.com
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,51 @@
1
+ # BigQueryAdapter
2
+
3
+ EXPERIMENTAL VERSION:
4
+
5
+ Features so far:
6
+ - perform select queries in a bigquery table, using
7
+ - possibility to generate schema
8
+
9
+ Inspiration and fork of the project:
10
+
11
+ [localytics odbc activerecord adapter](https://github.com/localytics/odbc_adapter)
12
+ [blog post](http://www.blrice.net/blog/2016/04/09/one-rails-app-with-many-databases/)
13
+
14
+ ## Usage
15
+
16
+ Add this line to your application's Gemfile:
17
+
18
+ ```ruby
19
+ gem 'big_query_adapter'
20
+ ```
21
+
22
+
23
+ ```ruby
24
+ options = { adapter: 'big_query' }
25
+ options[:keyfile] = "path-to-key-file"
26
+ options[:project] = "your-project-id"
27
+ options[:datasets] = [] # specify dataset for faster schema generation
28
+ ActiveRecord::Base.establish_connection(options)
29
+ ```
30
+
31
+ TODO: add example
32
+
33
+ In the meanwhile check this post:
34
+
35
+ [One rails app with many databases](http://www.blrice.net/blog/2016/04/09/one-rails-app-with-many-databases/)
36
+
37
+ ## Development
38
+
39
+ After checking out the repo, run `bin/setup` to install dependencies. Then, run `rake spec` to run the tests. You can also run `bin/console` for an interactive prompt that will allow you to experiment.
40
+
41
+ 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).
42
+
43
+ ## Contributing
44
+
45
+ Bug reports and pull requests are welcome on GitHub at https://github.com/[USERNAME]/big_query_adapter. This project is intended to be a safe, welcoming space for collaboration, and contributors are expected to adhere to the [Contributor Covenant](http://contributor-covenant.org) code of conduct.
46
+
47
+
48
+ ## License
49
+
50
+ The gem is available as open source under the terms of the [MIT License](http://opensource.org/licenses/MIT).
51
+
@@ -0,0 +1,10 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'bundler/gem_tasks'
4
+ require 'rspec/core/rake_task'
5
+ require 'rubocop/rake_task'
6
+
7
+ RSpec::Core::RakeTask.new(:spec)
8
+ RuboCop::RakeTask.new(:rubocop)
9
+
10
+ task default: %i[spec rubocop]
@@ -0,0 +1,31 @@
1
+ # coding: utf-8
2
+
3
+ lib = File.expand_path('../lib', __FILE__)
4
+ $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
5
+ require 'big_query_adapter/version'
6
+
7
+ Gem::Specification.new do |spec|
8
+ spec.name = 'big_query_adapter'
9
+ spec.version = BigQueryAdapter::VERSION
10
+ spec.authors = ['pedrocarmona']
11
+ spec.email = ['pcarmona1990@gmail.com']
12
+
13
+ spec.summary = 'An ActiveRecord Google BigQuery adapter'
14
+ spec.homepage = 'https://github.com/pedrocarmona/big_query_adapter'
15
+ spec.license = 'MIT'
16
+
17
+ spec.files = `git ls-files -z`.split("\x0").reject do |f|
18
+ f.match(%r{^(test|spec|features)/})
19
+ end
20
+ spec.bindir = 'exe'
21
+ spec.executables = spec.files.grep(%r{^exe/}) { |f| File.basename(f) }
22
+ spec.require_paths = ['lib']
23
+
24
+ spec.add_dependency 'google-cloud-bigquery', '~> 0.27.1'
25
+
26
+ spec.add_development_dependency 'bundler', '~> 1.13'
27
+ spec.add_development_dependency 'rake', '~> 12.0'
28
+ spec.add_development_dependency 'rspec', '~> 3.0'
29
+ spec.add_development_dependency 'rubocop', '~> 0.48'
30
+ spec.add_development_dependency 'simplecov', '~> 0.14'
31
+ end
@@ -0,0 +1,13 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ require 'bundler/setup'
4
+ require 'big_query_adapter'
5
+
6
+ options = { adapter: 'big_query' }
7
+ options[:keyfile] = ENV.fetch('BIGQUERY_KEYFILE') if ENV['BIGQUERY_KEYFILE']
8
+ options[:project] = ENV.fetch('BIGQUERY_PROJECT') if ENV['BIGQUERY_PROJECT']
9
+ options[:datasets] = []
10
+ ActiveRecord::Base.establish_connection(options)
11
+
12
+ require 'pry'
13
+ Pry.start
@@ -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,17 @@
1
+ version: '2'
2
+ services:
3
+ app:
4
+ image: ruby:2.3.1
5
+ working_dir: /usr/src/app
6
+ environment:
7
+ TMPDIR: /usr/src/app/tmp
8
+ BUNDLE_PATH: /usr/src/app/.bundle
9
+ BUNDLE_CONFIG: /usr/src/app/bundle/config
10
+ BIGQUERY_KEYFILE: "/etc/app/keyfile.json"
11
+ BIGQUERY_PROJECT: "--replace-me--"
12
+ volumes:
13
+ - .:/usr/src/app
14
+ - ./etc/:/etc/app/
15
+
16
+
17
+
@@ -0,0 +1,141 @@
1
+ require 'active_record'
2
+ require 'arel/visitors/bind_visitor'
3
+
4
+ require 'big_query_adapter/connection'
5
+ require 'big_query_adapter/database_statements'
6
+ require 'big_query_adapter/error'
7
+ require 'big_query_adapter/quoting'
8
+ require 'big_query_adapter/schema_statements'
9
+
10
+ require 'big_query_adapter/version'
11
+
12
+ module ActiveRecord
13
+ # no-doc
14
+ class Base
15
+ class << self
16
+ # Build a new BigQuery connection with the given configuration.
17
+ def big_query_connection(config)
18
+ config = config.symbolize_keys
19
+ params = {
20
+ keyfile: config[:keyfile],
21
+ project: config[:project],
22
+ datasets: config[:datasets]
23
+ }
24
+ connection = ::BigQueryAdapter::Connection.new(params)
25
+ ConnectionAdapters::BigQueryAdapter
26
+ .new(connection, logger, config, {})
27
+ end
28
+ end
29
+ end
30
+
31
+ module ConnectionAdapters
32
+ # Adapter in the active record namespace
33
+ class BigQueryAdapter < AbstractAdapter
34
+ include ::BigQueryAdapter::DatabaseStatements
35
+ include ::BigQueryAdapter::Quoting
36
+ include ::BigQueryAdapter::SchemaStatements
37
+
38
+ ADAPTER_NAME = 'BigQuery'.freeze
39
+
40
+ ERR_DUPLICATE_KEY_VALUE = 23_505
41
+ ERR_QUERY_TIMED_OUT = 57_014
42
+ ERR_QUERY_TIMED_OUT_MESSAGE = /Query has timed out/
43
+
44
+ # The object that stores the information that is fetched from the DBMS
45
+ # when a connection is first established.
46
+ attr_reader :database_metadata
47
+
48
+ def initialize(connection, logger, config, database_metadata)
49
+ super(connection, logger, config)
50
+ @database_metadata = database_metadata
51
+ end
52
+
53
+ # Returns the human-readable name of the adapter.
54
+ def adapter_name
55
+ ADAPTER_NAME
56
+ end
57
+
58
+ # CONNECTION MANAGEMENT ====================================
59
+
60
+ # Checks whether the connection to the database is still active. This
61
+ # includes checking whether the database is actually capable of
62
+ # responding, i.e. whether the connection isn't stale.
63
+ def active?
64
+ @connection.run('SELECT TRUE AS active')
65
+ end
66
+
67
+ # Disconnects from the database if already connected, and establishes a
68
+ # new connection with the database.
69
+ def reconnect!
70
+ disconnect!
71
+ @connection = Base.big_query_connection(@config)
72
+ super
73
+ end
74
+ alias reset! reconnect!
75
+
76
+ # Disconnects from the database if already connected. Otherwise, this
77
+ # method does nothing.
78
+ def disconnect!
79
+ false
80
+ end
81
+
82
+ protected
83
+
84
+ # Build the type map for ActiveRecord
85
+ def initialize_type_map(map)
86
+ super
87
+ end
88
+
89
+ # Translate an exception from the native DBMS to something usable by
90
+ # ActiveRecord.
91
+ def translate_exception(exception, message)
92
+ error_number = exception.message[/^\d+/].to_i
93
+
94
+ if error_number == ERR_DUPLICATE_KEY_VALUE
95
+ ActiveRecord::RecordNotUnique.new(message, exception)
96
+ # rubocop:disable Metrics/LineLength
97
+ elsif error_number == ERR_QUERY_TIMED_OUT || exception.message =~ ERR_QUERY_TIMED_OUT_MESSAGE
98
+ ::BigQueryAdapter::QueryTimeoutError.new(message, exception)
99
+ # rubocop:enable Metrics/LineLength
100
+ else
101
+ super
102
+ end
103
+ end
104
+
105
+ # no-doc
106
+ class BindSubstitution < Arel::Visitors::ToSql
107
+ include Arel::Visitors::BindVisitor
108
+ end
109
+
110
+ # Using a BindVisitor so that the SQL string gets substituted before it is
111
+ # sent to the DBMS (to attempt to get as much coverage as possible for
112
+ # DBMSs we don't support).
113
+ def arel_visitor
114
+ BindSubstitution.new(self)
115
+ end
116
+
117
+ # Explicitly turning off prepared_statements in the null adapter because
118
+ # there isn't really a standard on which substitution character to use.
119
+ def prepared_statements
120
+ false
121
+ end
122
+
123
+ # Turning off support for migrations because there is no information to
124
+ # go off of for what syntax the DBMS will expect.
125
+ def supports_migrations?
126
+ false
127
+ end
128
+
129
+ private
130
+
131
+ # Can't use the built-in ActiveRecord map#alias_type because it doesn't
132
+ # work with non-string keys, and in our case the keys are (almost) all
133
+ # numeric
134
+ def alias_type(map, new_type, old_type)
135
+ map.register_type(new_type) do |_, *args|
136
+ map.lookup(old_type, *args)
137
+ end
138
+ end
139
+ end
140
+ end
141
+ end
@@ -0,0 +1,2 @@
1
+ # Requiring with this pattern to mirror ActiveRecord
2
+ require 'active_record/connection_adapters/big_query_adapter'
@@ -0,0 +1,106 @@
1
+ require 'google/cloud/bigquery'
2
+ require 'date'
3
+
4
+ module BigQueryAdapter
5
+ # Driver for bigquery connection
6
+ class Connection
7
+ Result = Struct.new(:columns, :rows)
8
+
9
+ attr_reader :project
10
+
11
+ def initialize(project:, keyfile:, timeout: nil, datasets: [])
12
+ @project = project
13
+ @bigquery = Google::Cloud::Bigquery.new(
14
+ project: project,
15
+ keyfile: keyfile
16
+ )
17
+ @dataset_ids = datasets
18
+ @timeout = timeout.to_i if timeout
19
+ end
20
+
21
+ def run(statement)
22
+ columns = []
23
+ rows = []
24
+
25
+ options = {}
26
+ options[:timeout] = @timeout if @timeout
27
+ results = @bigquery.query(statement, options) # ms
28
+ if results.complete?
29
+ columns = results.first.keys.map(&:to_s) unless results.empty?
30
+ rows = results.map(&:values)
31
+ end
32
+
33
+ Result.new(columns, rows)
34
+ end
35
+
36
+ def tables
37
+ table_refs
38
+ .map { |table_ref| table_ref_name(table_ref) }
39
+ .group_by { |table_ref_name| table_ref_wildcard_name(table_ref_name) }
40
+ .keys
41
+ end
42
+
43
+ def columns(table_name)
44
+ table_schema = table_schema(table_name)
45
+ return [] if table_schema.fields.nil?
46
+ table_schema.fields
47
+ end
48
+
49
+ private
50
+
51
+ def table_ref_name(table_ref)
52
+ "#{table_ref.project_id}.#{table_ref.dataset_id}.#{table_ref.table_id}"
53
+ end
54
+
55
+ def table_ref_wildcard_name(table_ref_name)
56
+ if partitioned_table?(table_ref_name)
57
+ base_name = table_ref_name.split('_')
58
+ base_name.pop
59
+ base_name.join('_') << '_*'
60
+ else
61
+ table_ref_name
62
+ end
63
+ end
64
+
65
+ def partitioned_table?(table_ref_name)
66
+ return false if table_ref_name.split('_').size < 2
67
+ date_str = table_ref_name.split('_').last
68
+ date = Date.strptime(date_str, '%Y%m%d')
69
+ return !date.nil?
70
+ rescue StandardError => _error
71
+ return nil
72
+ end
73
+
74
+ def datasets
75
+ return @bigquery.datasets if @dataset_ids.empty?
76
+ @bigquery.datasets.select do |dataset|
77
+ @dataset_ids.include?(dataset.dataset_id)
78
+ end
79
+ end
80
+
81
+ def table_refs
82
+ datasets
83
+ .map(&:tables)
84
+ .flat_map { |table_list| table_list.map(&:table_ref) }
85
+ end
86
+
87
+ def table_ref(table_name)
88
+ if table_name.ends_with?('_*')
89
+ table_name = table_name[0...-1]
90
+ table_refs.find do |table_ref|
91
+ table_ref_name(table_ref) =~ /#{table_name}[0-9]{8}/
92
+ end
93
+ else
94
+ table_refs.find { |table_ref| table_ref_name(table_ref) == table_name }
95
+ end
96
+ end
97
+
98
+ def table_schema(table_name)
99
+ table_ref = table_ref(table_name)
100
+ @bigquery.service.get_table(
101
+ table_ref.dataset_id,
102
+ table_ref.table_id
103
+ ).schema
104
+ end
105
+ end
106
+ end
@@ -0,0 +1,47 @@
1
+ module BigQueryAdapter
2
+ # Includes helper methods
3
+ module DatabaseStatements
4
+ NATIVE_DATABASE_TYPES = {
5
+ boolean: { name: 'BOOL' },
6
+ integer: { name: 'INTEGER' },
7
+ float: { name: 'FLOAT' },
8
+ string: { name: 'STRING' },
9
+ datetime: { name: 'DATETIME' },
10
+ date: { name: 'DATE' },
11
+ timestamp: { name: 'TIMESTAMP' },
12
+ time: { name: 'TIME' }
13
+ }.freeze
14
+
15
+ def native_database_types
16
+ NATIVE_DATABASE_TYPES
17
+ end
18
+
19
+ def valid_type?(type) # :nodoc:
20
+ !native_database_types[type].nil?
21
+ end
22
+
23
+ # Executes the SQL statement in the context of this connection.
24
+ # Returns the number of rows affected.
25
+ def execute(sql, name = nil, _binds = [])
26
+ log(sql, name) do
27
+ @connection.do(sql)
28
+ end
29
+ end
30
+
31
+ # Executes +sql+ statement in the context of this connection using
32
+ # +binds+ as the bind substitutes. +name+ is logged along with
33
+ # the executed +sql+ statement.
34
+ # rubocop:disable Lint/UnusedMethodArgument
35
+ def exec_query(sql, name = 'SQL', _binds = [], prepare: false)
36
+ log(sql, name) do
37
+ result = @connection.run(sql)
38
+ ActiveRecord::Result.new(result.columns, result.rows)
39
+ end
40
+ end
41
+ # rubocop:enable Lint/UnusedMethodArgument
42
+
43
+ def supports_ddl_transactions
44
+ false
45
+ end
46
+ end
47
+ end
@@ -0,0 +1,4 @@
1
+ module BigQueryAdapter
2
+ class QueryTimeoutError < ActiveRecord::StatementInvalid
3
+ end
4
+ end
@@ -0,0 +1,14 @@
1
+ module BigQueryAdapter
2
+ # Includes helper methods
3
+ module Quoting
4
+ # Quotes a string, escaping any ' (single quote) characters.
5
+ def quote_string(string)
6
+ string.gsub(/\'/, "''")
7
+ end
8
+
9
+ # Quotes the table name. Defaults to column name quoting.
10
+ def quote_table_name(table_name)
11
+ "`#{table_name}`"
12
+ end
13
+ end
14
+ end
@@ -0,0 +1,69 @@
1
+ module BigQueryAdapter
2
+ # Includes helper methods
3
+ module SchemaStatements
4
+ # Returns an array of table names, for database tables visible on the
5
+ # current connection.
6
+ def tables(_name = nil)
7
+ raw_connection.tables
8
+ end
9
+
10
+ # Returns an array of view names defined in the database.
11
+ def views
12
+ []
13
+ end
14
+
15
+ # Returns an array of indexes for the given table.
16
+ def indexes(_table_name, _name = nil)
17
+ []
18
+ end
19
+
20
+ # Returns an array of Column objects for the table specified by
21
+ # +table_name+.
22
+ # rubocop:disable Metrics/MethodLength, Metrics/AbcSize
23
+ def columns(table_name, _name = nil)
24
+ result = @connection.columns(table_name.to_s)
25
+
26
+ result.each_with_object([]) do |field, cols|
27
+ col_name = field.name
28
+ col_sql_type = native_database_types.invert[name: field.type]
29
+ col_nullable = (field.mode == 'NULLABLE')
30
+
31
+ args = { sql_type: col_sql_type, type: col_sql_type, limit: nil }
32
+ args[:scale] = nil
33
+ args[:precision] = nil
34
+
35
+ sql_type_metadata =
36
+ ActiveRecord::ConnectionAdapters::SqlTypeMetadata.new(**args)
37
+
38
+ cols << ActiveRecord::ConnectionAdapters::Column.new(
39
+ col_name,
40
+ nil,
41
+ sql_type_metadata,
42
+ col_nullable,
43
+ table_name
44
+ )
45
+ end
46
+ end
47
+ # rubocop:enable Metrics/MethodLength, Metrics/AbcSize
48
+
49
+ # Returns just a table's primary key
50
+ def primary_key(_table_name)
51
+ []
52
+ end
53
+
54
+ def foreign_keys(_table_name)
55
+ []
56
+ end
57
+
58
+ # Ensure it's shorter than the maximum identifier length for the current
59
+ # dbms
60
+ def index_name(table_name, options)
61
+ maximum = database_metadata.max_identifier_len || 255
62
+ super(table_name, options)[0...maximum]
63
+ end
64
+
65
+ def current_database
66
+ database_metadata.database_name.strip
67
+ end
68
+ end
69
+ end
@@ -0,0 +1,3 @@
1
+ module BigQueryAdapter
2
+ VERSION = '0.1.0'.freeze
3
+ end
metadata ADDED
@@ -0,0 +1,146 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: big_query_adapter
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.1.0
5
+ platform: ruby
6
+ authors:
7
+ - pedrocarmona
8
+ autorequire:
9
+ bindir: exe
10
+ cert_chain: []
11
+ date: 2017-08-16 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: google-cloud-bigquery
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - "~>"
18
+ - !ruby/object:Gem::Version
19
+ version: 0.27.1
20
+ type: :runtime
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - "~>"
25
+ - !ruby/object:Gem::Version
26
+ version: 0.27.1
27
+ - !ruby/object:Gem::Dependency
28
+ name: bundler
29
+ requirement: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - "~>"
32
+ - !ruby/object:Gem::Version
33
+ version: '1.13'
34
+ type: :development
35
+ prerelease: false
36
+ version_requirements: !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - "~>"
39
+ - !ruby/object:Gem::Version
40
+ version: '1.13'
41
+ - !ruby/object:Gem::Dependency
42
+ name: rake
43
+ requirement: !ruby/object:Gem::Requirement
44
+ requirements:
45
+ - - "~>"
46
+ - !ruby/object:Gem::Version
47
+ version: '12.0'
48
+ type: :development
49
+ prerelease: false
50
+ version_requirements: !ruby/object:Gem::Requirement
51
+ requirements:
52
+ - - "~>"
53
+ - !ruby/object:Gem::Version
54
+ version: '12.0'
55
+ - !ruby/object:Gem::Dependency
56
+ name: rspec
57
+ requirement: !ruby/object:Gem::Requirement
58
+ requirements:
59
+ - - "~>"
60
+ - !ruby/object:Gem::Version
61
+ version: '3.0'
62
+ type: :development
63
+ prerelease: false
64
+ version_requirements: !ruby/object:Gem::Requirement
65
+ requirements:
66
+ - - "~>"
67
+ - !ruby/object:Gem::Version
68
+ version: '3.0'
69
+ - !ruby/object:Gem::Dependency
70
+ name: rubocop
71
+ requirement: !ruby/object:Gem::Requirement
72
+ requirements:
73
+ - - "~>"
74
+ - !ruby/object:Gem::Version
75
+ version: '0.48'
76
+ type: :development
77
+ prerelease: false
78
+ version_requirements: !ruby/object:Gem::Requirement
79
+ requirements:
80
+ - - "~>"
81
+ - !ruby/object:Gem::Version
82
+ version: '0.48'
83
+ - !ruby/object:Gem::Dependency
84
+ name: simplecov
85
+ requirement: !ruby/object:Gem::Requirement
86
+ requirements:
87
+ - - "~>"
88
+ - !ruby/object:Gem::Version
89
+ version: '0.14'
90
+ type: :development
91
+ prerelease: false
92
+ version_requirements: !ruby/object:Gem::Requirement
93
+ requirements:
94
+ - - "~>"
95
+ - !ruby/object:Gem::Version
96
+ version: '0.14'
97
+ description:
98
+ email:
99
+ - pcarmona1990@gmail.com
100
+ executables: []
101
+ extensions: []
102
+ extra_rdoc_files: []
103
+ files:
104
+ - ".gitignore"
105
+ - ".travis.yml"
106
+ - Gemfile
107
+ - LICENSE
108
+ - README.md
109
+ - Rakefile
110
+ - big_query_adapter.gemspec
111
+ - bin/console
112
+ - bin/setup
113
+ - docker-compose.yml
114
+ - lib/active_record/connection_adapters/big_query_adapter.rb
115
+ - lib/big_query_adapter.rb
116
+ - lib/big_query_adapter/connection.rb
117
+ - lib/big_query_adapter/database_statements.rb
118
+ - lib/big_query_adapter/error.rb
119
+ - lib/big_query_adapter/quoting.rb
120
+ - lib/big_query_adapter/schema_statements.rb
121
+ - lib/big_query_adapter/version.rb
122
+ homepage: https://github.com/pedrocarmona/big_query_adapter
123
+ licenses:
124
+ - MIT
125
+ metadata: {}
126
+ post_install_message:
127
+ rdoc_options: []
128
+ require_paths:
129
+ - lib
130
+ required_ruby_version: !ruby/object:Gem::Requirement
131
+ requirements:
132
+ - - ">="
133
+ - !ruby/object:Gem::Version
134
+ version: '0'
135
+ required_rubygems_version: !ruby/object:Gem::Requirement
136
+ requirements:
137
+ - - ">="
138
+ - !ruby/object:Gem::Version
139
+ version: '0'
140
+ requirements: []
141
+ rubyforge_project:
142
+ rubygems_version: 2.6.8
143
+ signing_key:
144
+ specification_version: 4
145
+ summary: An ActiveRecord Google BigQuery adapter
146
+ test_files: []