scenic_mysql 0.1.2

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: 4803974ec78680b939418294f691a3c6bf5d340f
4
+ data.tar.gz: 921ff1ef7337d24c7d1adf7727553d72d00bbc2a
5
+ SHA512:
6
+ metadata.gz: 1b072cd90d226465543254d2feb30e9a8624d21ca8f43d5844a3dc8d6a275a7a15132406cca0b60bd2f5ed769ea1940b48823e81409a114edfccd49492a55f97
7
+ data.tar.gz: 7907d25b17e8495d22ceb1972f389b17af3bbd061440365e603d64fd41052fbb6bb60ee1053d6cbc9b0082a807eb2c959772f0bbb124d575dd05709fb084ee8c
data/.gitignore ADDED
@@ -0,0 +1,9 @@
1
+ /.bundle/
2
+ /.yardoc
3
+ /Gemfile.lock
4
+ /_yardoc/
5
+ /coverage/
6
+ /doc/
7
+ /pkg/
8
+ /spec/reports/
9
+ /tmp/
data/.rspec ADDED
@@ -0,0 +1,2 @@
1
+ --format documentation
2
+ --color
data/.travis.yml ADDED
@@ -0,0 +1,4 @@
1
+ language: ruby
2
+ rvm:
3
+ - 2.0.0
4
+ before_install: gem install bundler -v 1.10.4
data/Gemfile ADDED
@@ -0,0 +1,4 @@
1
+ source 'https://rubygems.org'
2
+
3
+ # Specify your gem's dependencies in scenic_mysql.gemspec
4
+ gemspec
data/LICENSE.txt ADDED
@@ -0,0 +1,21 @@
1
+ The MIT License (MIT)
2
+
3
+ Copyright (c) 2016 Elpizo Choi
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.
data/README.md ADDED
@@ -0,0 +1,41 @@
1
+ # ScenicMysql
2
+
3
+ Welcome to your new gem! In this directory, you'll find the files you need to be able to package up your Ruby library into a gem. Put your Ruby code in the file `lib/scenic_mysql`. To experiment with that code, run `bin/console` for an interactive prompt.
4
+
5
+ TODO: Delete this and the text above, and describe your gem
6
+
7
+ ## Installation
8
+
9
+ Add this line to your application's Gemfile:
10
+
11
+ ```ruby
12
+ gem 'scenic_mysql'
13
+ ```
14
+
15
+ And then execute:
16
+
17
+ $ bundle
18
+
19
+ Or install it yourself as:
20
+
21
+ $ gem install scenic_mysql
22
+
23
+ ## Usage
24
+
25
+ TODO: Write usage instructions here
26
+
27
+ ## Development
28
+
29
+ After checking out the repo, run `bin/setup` to install dependencies. Then, run `rake rspec` to run the tests. You can also run `bin/console` for an interactive prompt that will allow you to experiment.
30
+
31
+ 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).
32
+
33
+ ## Contributing
34
+
35
+ Bug reports and pull requests are welcome on GitHub at https://github.com/[USERNAME]/scenic_mysql.
36
+
37
+
38
+ ## License
39
+
40
+ The gem is available as open source under the terms of the [MIT License](http://opensource.org/licenses/MIT).
41
+
data/Rakefile ADDED
@@ -0,0 +1,6 @@
1
+ require "bundler/gem_tasks"
2
+ require "rspec/core/rake_task"
3
+
4
+ RSpec::Core::RakeTask.new(:spec)
5
+
6
+ task :default => :spec
data/bin/console ADDED
@@ -0,0 +1,14 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ require "bundler/setup"
4
+ require "scenic_mysql"
5
+
6
+ # You can add fixtures and/or initialization code here to make experimenting
7
+ # with your gem easier. You can also use a different console, if you like.
8
+
9
+ # (If you use this, don't forget to add pry to your Gemfile!)
10
+ # require "pry"
11
+ # Pry.start
12
+
13
+ require "irb"
14
+ IRB.start
data/bin/setup ADDED
@@ -0,0 +1,7 @@
1
+ #!/bin/bash
2
+ set -euo pipefail
3
+ IFS=$'\n\t'
4
+
5
+ bundle install
6
+
7
+ # Do any other automated setup that you need to do here
@@ -0,0 +1,192 @@
1
+ require_relative "mysql/connection"
2
+ require_relative "mysql/errors"
3
+ require_relative "mysql/views"
4
+ require_relative "mysql/tables_definition"
5
+ require_relative "mysql/version"
6
+
7
+ module Scenic
8
+ # Scenic database adapters.
9
+ #
10
+ # Scenic ships with a Postgres adapter only but can be extended with
11
+ # additional adapters. The {Adapters::Postgres} adapter provides the
12
+ # interface.
13
+ module Adapters
14
+ # An adapter for managing MySQL views.
15
+ #
16
+ # These methods are used interally by Scenic and are not intended for direct
17
+ # use. Methods that alter database schema are intended to be called via
18
+ # {Statements}, while {#refresh_materialized_view} is called via
19
+ # {Scenic.database}.
20
+ #
21
+ # The methods are documented here for insight into specifics of how Scenic
22
+ # integrates with Postgres and the responsibilities of {Adapters}.
23
+ class Mysql
24
+ # Creates an instance of the Scenic Postgres adapter.
25
+ #
26
+ # This is the default adapter for Scenic. Configuring it via
27
+ # {Scenic.configure} is not required, but the example below shows how one
28
+ # would explicitly set it.
29
+ #
30
+ # @param [#connection] connectable An object that returns the connection
31
+ # for Scenic to use. Defaults to `ActiveRecord::Base`.
32
+ #
33
+ # @example
34
+ # Scenic.configure do |config|
35
+ # config.adapter = Scenic::Adapters::Postgres.new
36
+ # end
37
+ def initialize(connectable = ActiveRecord::Base)
38
+ @connectable = connectable
39
+ end
40
+
41
+ # Returns an array of views in the database.
42
+ #
43
+ # This collection of views is used by the [Scenic::SchemaDumper] to
44
+ # populate the `schema.rb` file.
45
+ #
46
+ # @return [Array<Scenic::View>]
47
+ def views
48
+ Views.new(connection).all
49
+ end
50
+
51
+ # Creates a view in the database.
52
+ #
53
+ # This is typically called in a migration via {Statements#create_view}.
54
+ #
55
+ # @param name The name of the view to create
56
+ # @param sql_definition The SQL schema for the view.
57
+ #
58
+ # @return [void]
59
+ def create_view(name, sql_definition)
60
+ execute "CREATE VIEW #{quote_table_name(name)} AS #{sql_definition};"
61
+ end
62
+
63
+ # Updates a view in the database.
64
+ #
65
+ # This results in a {#drop_view} followed by a {#create_view}. The
66
+ # explicitness of that two step process is preferred to `CREATE OR
67
+ # REPLACE VIEW` because the former ensures that the view you are trying to
68
+ # update did, in fact, already exist. Additionally, `CREATE OR REPLACE
69
+ # VIEW` is allowed only to add new columns to the end of an existing
70
+ # view schema. Existing columns cannot be re-ordered, removed, or have
71
+ # their types changed. Drop and create overcomes this limitation as well.
72
+ #
73
+ # This is typically called in a migration via {Statements#update_view}.
74
+ #
75
+ # @param name The name of the view to update
76
+ # @param sql_definition The SQL schema for the updated view.
77
+ #
78
+ # @return [void]
79
+ def update_view(name, sql_definition)
80
+ drop_view(name)
81
+ create_view(name, sql_definition)
82
+ end
83
+
84
+ # Drops the named view from the database
85
+ #
86
+ # This is typically called in a migration via {Statements#drop_view}.
87
+ #
88
+ # @param name The name of the view to drop
89
+ #
90
+ # @return [void]
91
+ def drop_view(name)
92
+ execute "DROP VIEW #{quote_table_name(name)};"
93
+ end
94
+
95
+ # Creates a materialized view in the database
96
+ #
97
+ # @param name The name of the materialized view to create
98
+ # @param sql_definition The SQL schema that defines the materialized view.
99
+ #
100
+ # This is typically called in a migration via {Statements#create_view}.
101
+ #
102
+ # @raise [MaterializedViewsNotSupportedError] if the version of Postgres
103
+ # in use does not support materialized views.
104
+ #
105
+ # @return [void]
106
+ def create_materialized_view(name, sql_definition)
107
+ raise_unless_materialized_views_supported
108
+ end
109
+
110
+ # Updates a materialized view in the database.
111
+ #
112
+ # Drops and recreates the materialized view. Attempts to maintain all
113
+ # previously existing and still applicable indexes on the materialized
114
+ # view after the view is recreated.
115
+ #
116
+ # This is typically called in a migration via {Statements#update_view}.
117
+ #
118
+ # @param name The name of the view to update
119
+ # @param sql_definition The SQL schema for the updated view.
120
+ #
121
+ # @raise [MaterializedViewsNotSupportedError] if the version of Postgres
122
+ # in use does not support materialized views.
123
+ #
124
+ # @return [void]
125
+ def update_materialized_view(name, sql_definition)
126
+ raise_unless_materialized_views_supported
127
+ end
128
+
129
+ # Drops a materialized view in the database
130
+ #
131
+ # This is typically called in a migration via {Statements#update_view}.
132
+ #
133
+ # @param name The name of the materialized view to drop.
134
+ # @raise [MaterializedViewsNotSupportedError] if the version of Postgres
135
+ # in use does not support materialized views.
136
+ #
137
+ # @return [void]
138
+ def drop_materialized_view(name)
139
+ raise_unless_materialized_views_supported
140
+ end
141
+
142
+ # Refreshes a materialized view from its SQL schema.
143
+ #
144
+ # This is typically called from application code via {Scenic.database}.
145
+ #
146
+ # @param name The name of the materialized view to refresh.
147
+ # @param concurrently [Boolean] Whether the refreshs hould happen
148
+ # concurrently or not. A concurrent refresh allows the view to be
149
+ # refreshed without locking the view for select but requires that the
150
+ # table have at least one unique index that covers all rows. Attempts to
151
+ # refresh concurrently without a unique index will raise a descriptive
152
+ # error.
153
+ #
154
+ # @raise [MaterializedViewsNotSupportedError] if the version of Postgres
155
+ # in use does not support materialized views.
156
+ # @raise [ConcurrentRefreshesNotSupportedError] when attempting a
157
+ # concurrent refresh on version of Postgres that does not support
158
+ # concurrent materialized view refreshes.
159
+ #
160
+ # @example Non-concurrent refresh
161
+ # Scenic.database.refresh_materialized_view(:search_results)
162
+ # @example Concurrent refresh
163
+ # Scenic.database.refresh_materialized_view(:posts, concurrent: true)
164
+ #
165
+ # @return [void]
166
+ def refresh_materialized_view(name, concurrently: false)
167
+ raise_unless_materialized_views_supported
168
+ end
169
+
170
+ private
171
+
172
+ attr_reader :connectable
173
+ delegate :execute, :quote_table_name, to: :connection
174
+
175
+ def connection
176
+ Connection.new(connectable.connection)
177
+ end
178
+
179
+ def raise_unless_materialized_views_supported
180
+ unless connection.supports_materialized_views?
181
+ raise MaterializedViewsNotSupportedError
182
+ end
183
+ end
184
+
185
+ def raise_unless_concurrent_refresh_supported
186
+ unless connection.supports_concurrent_refreshes?
187
+ raise ConcurrentRefreshesNotSupportedError
188
+ end
189
+ end
190
+ end
191
+ end
192
+ end
@@ -0,0 +1,39 @@
1
+ module Scenic
2
+ module Adapters
3
+ class Mysql
4
+ # Decorates an ActiveRecord connection with methods that help determine
5
+ # the connections capabilities.
6
+ #
7
+ # Every attempt is made to use the versions of these methods defined by
8
+ # Rails where they are available and public before falling back to our own
9
+ # implementations for older Rails versions.
10
+ #
11
+ # @api private
12
+ class Connection < SimpleDelegator
13
+ # True if the connection supports materialized views.
14
+ #
15
+ # Delegates to the method of the same name if it is already defined on
16
+ # the connection. This is the case for Rails 4.2 or higher.
17
+ #
18
+ # @return [Boolean]
19
+ def supports_materialized_views?
20
+ false
21
+ end
22
+
23
+ # True if the connection supports concurrent refreshes of materialized
24
+ # views.
25
+ #
26
+ # @return [Boolean]
27
+ def supports_concurrent_refreshes?
28
+ false
29
+ end
30
+
31
+ private
32
+
33
+ def undecorated_connection
34
+ __getobj__
35
+ end
36
+ end
37
+ end
38
+ end
39
+ end
@@ -0,0 +1,25 @@
1
+ module Scenic
2
+ module Adapters
3
+ class Mysql
4
+ # Raised when a materialized view operation is attempted on a database
5
+ # version that does not support materialized views.
6
+ #
7
+ class MaterializedViewsNotSupportedError < StandardError
8
+ def initialize
9
+ super("Materialized views not yet supported for MySQL")
10
+ end
11
+ end
12
+
13
+ # Raised when attempting a concurrent materialized view refresh on a
14
+ # database version that does not support that.
15
+ #
16
+ # Concurrent materialized view refreshes are supported on Postgres 9.4 or
17
+ # newer.
18
+ class ConcurrentRefreshesNotSupportedError < StandardError
19
+ def initialize
20
+ super("Concurrent refreshes not yet supported for MySQL")
21
+ end
22
+ end
23
+ end
24
+ end
25
+ end
@@ -0,0 +1,28 @@
1
+ module Scenic
2
+ module Adapters
3
+ class Mysql
4
+
5
+ module TablesDefinition
6
+
7
+ def tables(name = nil, database = nil, like = nil)
8
+ sql = "SELECT table_name FROM information_schema.tables"
9
+
10
+ wheres = ["table_type = 'BASE TABLE'"]
11
+
12
+ if database
13
+ wheres << "table_schema = #{quote_table_name(database)}"
14
+ else
15
+ wheres << "table_schema = SCHEMA()"
16
+ end
17
+ wheres << "table_name LIKE #{quote(like)}" if like
18
+
19
+ sql << " WHERE #{wheres.join(' AND ')}" if wheres.present?
20
+
21
+ execute_and_free(sql, 'SCHEMA') do |result|
22
+ result.collect { |field| field.first }
23
+ end
24
+ end
25
+ end
26
+ end
27
+ end
28
+ end
@@ -0,0 +1,7 @@
1
+ module Scenic
2
+ module Adapters
3
+ class Mysql
4
+ VERSION = "0.1.2"
5
+ end
6
+ end
7
+ end
@@ -0,0 +1,56 @@
1
+ module Scenic
2
+ module Adapters
3
+ class Mysql
4
+
5
+ # Fetches defined views from the mysql connection.
6
+ # @api private
7
+ class Views
8
+ def initialize(connection)
9
+ @connection = connection
10
+ end
11
+
12
+ def current_database
13
+ @connection.current_database
14
+ end
15
+
16
+ # All of the views that this connection has defined.
17
+ #
18
+ # This will include materialized views if those are supported by the
19
+ # connection.
20
+ #
21
+ # @return [Array<Scenic::View>]
22
+ def all
23
+ views_from_mysql.map(&method(:to_scenic_view))
24
+ end
25
+
26
+ private
27
+
28
+ attr_reader :connection
29
+
30
+ def views_from_mysql
31
+ connection.select_rows(<<-SQL)
32
+ SELECT table_name, view_definition
33
+ FROM information_schema.views
34
+ WHERE table_schema = SCHEMA()
35
+ SQL
36
+ end
37
+
38
+ def to_scenic_view(result)
39
+ table_name, view_def = result
40
+
41
+ Scenic::View.new(
42
+ name: table_name,
43
+ definition: scrub_view_def(view_def)
44
+ )
45
+ end
46
+
47
+ # Sometimes MySQL will include the database name
48
+ # in view definitions. This scrubs it out.
49
+ #
50
+ def scrub_view_def(view_def)
51
+ view_def.strip.gsub(/([\`]*#{current_database}[\`]*\.)/i, '')
52
+ end
53
+ end
54
+ end
55
+ end
56
+ end
@@ -0,0 +1 @@
1
+ require "scenic/adapters/mysql"
@@ -0,0 +1,27 @@
1
+ # coding: utf-8
2
+ lib = File.expand_path('../lib', __FILE__)
3
+ $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
4
+ require 'scenic/adapters/mysql/version'
5
+
6
+ Gem::Specification.new do |spec|
7
+ spec.name = "scenic_mysql"
8
+ spec.version = Scenic::Adapters::Mysql::VERSION
9
+ spec.authors = ["Elpizo Choi"]
10
+ spec.email = ["fu7iin@gmail.com"]
11
+
12
+ spec.summary = %q{Adds MySQL support for Scenic gem}
13
+ spec.description = %q{Adds support for MySQL views in ActiveRecord via the Scenic gem}
14
+ spec.homepage = "http://github.com/startweaving/scenic_mysql"
15
+ spec.license = "MIT"
16
+
17
+ spec.files = `git ls-files -z`.split("\x0").reject { |f| f.match(%r{^(test|spec|features)/}) }
18
+ spec.bindir = "exe"
19
+ spec.executables = spec.files.grep(%r{^exe/}) { |f| File.basename(f) }
20
+ spec.require_paths = ["lib"]
21
+
22
+ spec.add_dependency "scenic", "~> 1.1.1"
23
+
24
+ spec.add_development_dependency "bundler", "~> 1.10"
25
+ spec.add_development_dependency "rake", "~> 10.0"
26
+ spec.add_development_dependency "rspec"
27
+ end
metadata ADDED
@@ -0,0 +1,117 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: scenic_mysql
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.1.2
5
+ platform: ruby
6
+ authors:
7
+ - Elpizo Choi
8
+ autorequire:
9
+ bindir: exe
10
+ cert_chain: []
11
+ date: 2016-02-04 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: scenic
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - ~>
18
+ - !ruby/object:Gem::Version
19
+ version: 1.1.1
20
+ type: :runtime
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - ~>
25
+ - !ruby/object:Gem::Version
26
+ version: 1.1.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.10'
34
+ type: :development
35
+ prerelease: false
36
+ version_requirements: !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - ~>
39
+ - !ruby/object:Gem::Version
40
+ version: '1.10'
41
+ - !ruby/object:Gem::Dependency
42
+ name: rake
43
+ requirement: !ruby/object:Gem::Requirement
44
+ requirements:
45
+ - - ~>
46
+ - !ruby/object:Gem::Version
47
+ version: '10.0'
48
+ type: :development
49
+ prerelease: false
50
+ version_requirements: !ruby/object:Gem::Requirement
51
+ requirements:
52
+ - - ~>
53
+ - !ruby/object:Gem::Version
54
+ version: '10.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: '0'
62
+ type: :development
63
+ prerelease: false
64
+ version_requirements: !ruby/object:Gem::Requirement
65
+ requirements:
66
+ - - '>='
67
+ - !ruby/object:Gem::Version
68
+ version: '0'
69
+ description: Adds support for MySQL views in ActiveRecord via the Scenic gem
70
+ email:
71
+ - fu7iin@gmail.com
72
+ executables: []
73
+ extensions: []
74
+ extra_rdoc_files: []
75
+ files:
76
+ - .gitignore
77
+ - .rspec
78
+ - .travis.yml
79
+ - Gemfile
80
+ - LICENSE.txt
81
+ - README.md
82
+ - Rakefile
83
+ - bin/console
84
+ - bin/setup
85
+ - lib/scenic/adapters/mysql.rb
86
+ - lib/scenic/adapters/mysql/connection.rb
87
+ - lib/scenic/adapters/mysql/errors.rb
88
+ - lib/scenic/adapters/mysql/tables_definition.rb
89
+ - lib/scenic/adapters/mysql/version.rb
90
+ - lib/scenic/adapters/mysql/views.rb
91
+ - lib/scenic_mysql.rb
92
+ - scenic_mysql.gemspec
93
+ homepage: http://github.com/startweaving/scenic_mysql
94
+ licenses:
95
+ - MIT
96
+ metadata: {}
97
+ post_install_message:
98
+ rdoc_options: []
99
+ require_paths:
100
+ - lib
101
+ required_ruby_version: !ruby/object:Gem::Requirement
102
+ requirements:
103
+ - - '>='
104
+ - !ruby/object:Gem::Version
105
+ version: '0'
106
+ required_rubygems_version: !ruby/object:Gem::Requirement
107
+ requirements:
108
+ - - '>='
109
+ - !ruby/object:Gem::Version
110
+ version: '0'
111
+ requirements: []
112
+ rubyforge_project:
113
+ rubygems_version: 2.2.2
114
+ signing_key:
115
+ specification_version: 4
116
+ summary: Adds MySQL support for Scenic gem
117
+ test_files: []