├── .circleci └── config.yml ├── .gitignore ├── Appraisals ├── CODE_OF_CONDUCT.md ├── Gemfile ├── LICENSE.md ├── README.md ├── Rakefile ├── bin ├── console └── setup ├── gemfiles ├── .bundle │ └── config ├── rails_42.gemfile ├── rails_50.gemfile ├── rails_51.gemfile └── rails_52.gemfile ├── lib └── scenic │ ├── adapters │ ├── my_sql.rb │ └── railtie.rb │ ├── mysql_adapter.rb │ └── mysql_adapter │ ├── schema_dumper.rb │ ├── version.rb │ └── views.rb ├── scenic-mysql_adapter.gemspec └── test ├── scenic └── mysql_adapter_test.rb └── test_helper.rb /.circleci/config.yml: -------------------------------------------------------------------------------- 1 | version: 2 2 | 3 | jobs: 4 | build: 5 | working_directory: ~/mysql_point 6 | docker: 7 | - image: circleci/ruby:2.4 8 | environment: 9 | DATABASE_URL: mysql2://root@127.0.0.1/circle_test 10 | - image: circleci/mysql:5.7 11 | 12 | steps: 13 | - checkout 14 | - run: bin/setup 15 | - run: appraisal install 16 | - run: appraisal rake test 17 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | /.bundle/ 2 | /.yardoc 3 | /Gemfile.lock 4 | /_yardoc/ 5 | /coverage/ 6 | /doc/ 7 | /pkg/ 8 | /spec/reports/ 9 | /tmp/ 10 | /gemfiles/*.lock 11 | -------------------------------------------------------------------------------- /Appraisals: -------------------------------------------------------------------------------- 1 | appraise "rails-42" do 2 | gem "activerecord", "~> 4.2", "< 5.0" 3 | gem "activemodel", "~> 4.2", "< 5.0" 4 | gem "actionpack", "~> 4.2", "< 5.0" 5 | gem "activesupport", "~> 4.2", "< 5.0" 6 | end 7 | 8 | appraise "rails-50" do 9 | gem "activerecord", "~> 5.0", "< 5.1" 10 | gem "activemodel", "~> 5.0", "< 5.1" 11 | gem "actionpack", "~> 5.0", "< 5.1" 12 | gem "activesupport", "~> 5.0", "< 5.1" 13 | end 14 | 15 | appraise "rails-51" do 16 | gem "activerecord", "~> 5.1", "< 5.2" 17 | gem "activemodel", "~> 5.1", "< 5.2" 18 | gem "actionpack", "~> 5.1", "< 5.2" 19 | gem "activesupport", "~> 5.1", "< 5.2" 20 | end 21 | 22 | appraise "rails-52" do 23 | gem "activerecord", "~> 5.2" 24 | gem "activemodel", "~> 5.2" 25 | gem "actionpack", "~> 5.2" 26 | gem "activesupport", "~> 5.2" 27 | end 28 | -------------------------------------------------------------------------------- /CODE_OF_CONDUCT.md: -------------------------------------------------------------------------------- 1 | # Contributor Covenant Code of Conduct 2 | 3 | ## Our Pledge 4 | 5 | In the interest of fostering an open and welcoming environment, we as 6 | contributors and maintainers pledge to making participation in our project and 7 | our community a harassment-free experience for everyone, regardless of age, body 8 | size, disability, ethnicity, gender identity and expression, level of experience, 9 | nationality, personal appearance, race, religion, or sexual identity and 10 | orientation. 11 | 12 | ## Our Standards 13 | 14 | Examples of behavior that contributes to creating a positive environment 15 | include: 16 | 17 | * Using welcoming and inclusive language 18 | * Being respectful of differing viewpoints and experiences 19 | * Gracefully accepting constructive criticism 20 | * Focusing on what is best for the community 21 | * Showing empathy towards other community members 22 | 23 | Examples of unacceptable behavior by participants include: 24 | 25 | * The use of sexualized language or imagery and unwelcome sexual attention or 26 | advances 27 | * Trolling, insulting/derogatory comments, and personal or political attacks 28 | * Public or private harassment 29 | * Publishing others' private information, such as a physical or electronic 30 | address, without explicit permission 31 | * Other conduct which could reasonably be considered inappropriate in a 32 | professional setting 33 | 34 | ## Our Responsibilities 35 | 36 | Project maintainers are responsible for clarifying the standards of acceptable 37 | behavior and are expected to take appropriate and fair corrective action in 38 | response to any instances of unacceptable behavior. 39 | 40 | Project maintainers have the right and responsibility to remove, edit, or 41 | reject comments, commits, code, wiki edits, issues, and other contributions 42 | that are not aligned to this Code of Conduct, or to ban temporarily or 43 | permanently any contributor for other behaviors that they deem inappropriate, 44 | threatening, offensive, or harmful. 45 | 46 | ## Scope 47 | 48 | This Code of Conduct applies both within project spaces and in public spaces 49 | when an individual is representing the project or its community. Examples of 50 | representing a project or community include using an official project e-mail 51 | address, posting via an official social media account, or acting as an appointed 52 | representative at an online or offline event. Representation of a project may be 53 | further defined and clarified by project maintainers. 54 | 55 | ## Enforcement 56 | 57 | Instances of abusive, harassing, or otherwise unacceptable behavior may be 58 | reported by contacting the project team at lance@cainlevy.net. All 59 | complaints will be reviewed and investigated and will result in a response that 60 | is deemed necessary and appropriate to the circumstances. The project team is 61 | obligated to maintain confidentiality with regard to the reporter of an incident. 62 | Further details of specific enforcement policies may be posted separately. 63 | 64 | Project maintainers who do not follow or enforce the Code of Conduct in good 65 | faith may face temporary or permanent repercussions as determined by other 66 | members of the project's leadership. 67 | 68 | ## Attribution 69 | 70 | This Code of Conduct is adapted from the [Contributor Covenant][homepage], version 1.4, 71 | available at [http://contributor-covenant.org/version/1/4][version] 72 | 73 | [homepage]: http://contributor-covenant.org 74 | [version]: http://contributor-covenant.org/version/1/4/ 75 | -------------------------------------------------------------------------------- /Gemfile: -------------------------------------------------------------------------------- 1 | source "https://rubygems.org" 2 | 3 | git_source(:github) {|repo_name| "https://github.com/#{repo_name}" } 4 | 5 | # Specify your gem's dependencies in scenic-mysql_adapter.gemspec 6 | gemspec 7 | -------------------------------------------------------------------------------- /LICENSE.md: -------------------------------------------------------------------------------- 1 | Copyright (c) 2019 2 | 3 | Permission is hereby granted, free of charge, to any person obtaining a copy of 4 | this software and associated documentation files (the "Software"), to deal in 5 | the Software without restriction, including without limitation the rights to 6 | use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies 7 | of the Software, and to permit persons to whom the Software is furnished to do 8 | so, subject to the following conditions: 9 | 10 | The above copyright notice and this permission notice shall be included in all 11 | copies or substantial portions of the Software. 12 | 13 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 14 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 15 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 16 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 17 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 18 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 19 | SOFTWARE. 20 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # ScenicMysqlAdapter 2 | 3 | MySQL adapter for [scenic](https://github.com/thoughtbot/scenic). 4 | 5 | [![Gem](https://img.shields.io/gem/v/scenic-mysql_adapter.svg)](https://rubygems.org/gems/scenic-mysql_adapter)[![master branch](https://img.shields.io/circleci/project/github/EmpaticoOrg/scenic-mysql_adapter.svg)](https://circleci.com/gh/EmpaticoOrg/scenic-mysql_adapter/tree/master) 6 | 7 | ## Installation 8 | 9 | Add this line to your application's Gemfile: 10 | 11 | ```ruby 12 | gem 'scenic-mysql_adapter' 13 | ``` 14 | 15 | And then execute: 16 | 17 | $ bundle 18 | 19 | Then create the following file: 20 | 21 | ```ruby 22 | # config/initializers/scenic.rb 23 | 24 | require 'scenic/mysql_adapter' 25 | 26 | Scenic.configure do |config| 27 | config.database = Scenic::Adapters::MySQL.new 28 | end 29 | ``` 30 | 31 | ## Limitations 32 | 33 | MySQL does not support materialized views. 34 | 35 | ## Development 36 | 37 | After checking out the repo, run `bin/setup` to install dependencies. Then, run `rake test` to run the tests. You can also run `bin/console` for an interactive prompt that will allow you to experiment. 38 | 39 | 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). 40 | 41 | ## Contributing 42 | 43 | Bug reports and pull requests are welcome on GitHub at https://github.com/EmpaticoOrg/scenic-mysql_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. 44 | 45 | ## Code of Conduct 46 | 47 | Everyone interacting in the ScenicMysqlAdapter project’s codebases, issue trackers, chat rooms and mailing lists is expected to follow the [code of conduct](https://github.com/EmpaticoOrg/scenic-mysql_adapter/blob/master/CODE_OF_CONDUCT.md). 48 | -------------------------------------------------------------------------------- /Rakefile: -------------------------------------------------------------------------------- 1 | require "bundler/gem_tasks" 2 | require "rake/testtask" 3 | 4 | Rake::TestTask.new(:test) do |t| 5 | t.libs << "test" 6 | t.libs << "lib" 7 | t.test_files = FileList["test/**/*_test.rb"] 8 | end 9 | 10 | task :default => :test 11 | -------------------------------------------------------------------------------- /bin/console: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env ruby 2 | 3 | require "bundler/setup" 4 | require "scenic/mysql_adapter" 5 | require "irb" 6 | 7 | IRB.start(__FILE__) 8 | -------------------------------------------------------------------------------- /bin/setup: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | set -euo pipefail 3 | IFS=$'\n\t' 4 | set -vx 5 | 6 | bundle install 7 | -------------------------------------------------------------------------------- /gemfiles/.bundle/config: -------------------------------------------------------------------------------- 1 | --- 2 | BUNDLE_RETRY: "1" 3 | -------------------------------------------------------------------------------- /gemfiles/rails_42.gemfile: -------------------------------------------------------------------------------- 1 | # This file was generated by Appraisal 2 | 3 | source "https://rubygems.org" 4 | 5 | gem "mysql2", "0.4.4" 6 | gem "activerecord", "~> 4.2", "< 5.0" 7 | gem "activemodel", "~> 4.2", "< 5.0" 8 | gem "actionpack", "~> 4.2", "< 5.0" 9 | gem "activesupport", "~> 4.2", "< 5.0" 10 | 11 | gemspec path: "../" 12 | -------------------------------------------------------------------------------- /gemfiles/rails_50.gemfile: -------------------------------------------------------------------------------- 1 | # This file was generated by Appraisal 2 | 3 | source "https://rubygems.org" 4 | 5 | gem "activerecord", "~> 5.0", "< 5.1" 6 | gem "activemodel", "~> 5.0", "< 5.1" 7 | gem "actionpack", "~> 5.0", "< 5.1" 8 | gem "activesupport", "~> 5.0", "< 5.1" 9 | 10 | gemspec path: "../" 11 | -------------------------------------------------------------------------------- /gemfiles/rails_51.gemfile: -------------------------------------------------------------------------------- 1 | # This file was generated by Appraisal 2 | 3 | source "https://rubygems.org" 4 | 5 | gem "mysql2", "0.4.4" 6 | gem "activerecord", "~> 5.1", "< 5.2" 7 | gem "activemodel", "~> 5.1", "< 5.2" 8 | gem "actionpack", "~> 5.1", "< 5.2" 9 | gem "activesupport", "~> 5.1", "< 5.2" 10 | 11 | gemspec path: "../" 12 | -------------------------------------------------------------------------------- /gemfiles/rails_52.gemfile: -------------------------------------------------------------------------------- 1 | # This file was generated by Appraisal 2 | 3 | source "https://rubygems.org" 4 | 5 | gem "activerecord", "~> 5.2" 6 | gem "activemodel", "~> 5.2" 7 | gem "actionpack", "~> 5.2" 8 | gem "activesupport", "~> 5.2" 9 | 10 | gemspec path: "../" 11 | -------------------------------------------------------------------------------- /lib/scenic/adapters/my_sql.rb: -------------------------------------------------------------------------------- 1 | require_relative '../mysql_adapter/views' 2 | 3 | module Scenic 4 | module Adapters 5 | class MySQL 6 | class FeatureNotSupportedError < StandardError 7 | def initialize 8 | super 'MySQL does not support this feature' 9 | end 10 | end 11 | 12 | def initialize(connectable = ActiveRecord::Base) 13 | @connectable = connectable 14 | end 15 | 16 | def views 17 | Scenic::MySQLAdapter::Views.new( 18 | view_names.map do |name| 19 | Scenic::View.new( 20 | name: name, 21 | definition: view_definition(name), 22 | materialized: false 23 | ) 24 | end, 25 | dependencies 26 | ).tsort 27 | end 28 | 29 | def create_view(name, sql_definition) 30 | execute "CREATE VIEW #{quote_table_name(name)} AS #{sql_definition}" 31 | end 32 | 33 | def replace_view(name, sql_definition) 34 | execute "CREATE OR REPLACE VIEW #{quote_table_name(name)} AS #{sql_definition}" 35 | end 36 | 37 | def drop_view(name) 38 | execute "DROP VIEW #{quote_table_name(name)}" 39 | end 40 | 41 | def update_view(name, sql_definition) 42 | drop_view(name) 43 | create_view(name, sql_definition) 44 | end 45 | 46 | def create_materialized_view(*_) 47 | raise FeatureNotSupportedError 48 | end 49 | 50 | def update_materialized_view(*_) 51 | raise FeatureNotSupportedError 52 | end 53 | 54 | def drop_materialized_view(*_) 55 | raise FeatureNotSupportedError 56 | end 57 | 58 | delegate :connection, to: :@connectable 59 | delegate :execute, :quote_table_name, to: :connection 60 | 61 | private def dependencies 62 | result = execute(<<-SQL) 63 | SELECT 64 | views.table_name As `View`, 65 | other_views.table_name AS `Dependency` 66 | FROM information_schema.views AS views 67 | INNER JOIN information_schema.views AS other_views 68 | ON views.table_schema = other_views.table_schema 69 | AND views.view_definition LIKE CONCAT('%`', other_views.table_name,'`%') 70 | WHERE views.table_schema = '#{@connectable.connection.current_database}'; 71 | SQL 72 | 73 | graph = Hash.new { |h, k| h[k] = [] } 74 | result.each_with_object(graph) do |row, memo| 75 | memo[row[result.fields.index('View')]] << row[result.fields.index('Dependency')] 76 | end 77 | end 78 | 79 | # returns a list of views in the current database 80 | private def view_names 81 | execute('SHOW FULL TABLES WHERE table_type = "VIEW"') 82 | .map(&:first) 83 | end 84 | 85 | # returns the SELECT used to create the view 86 | private def view_definition(name) 87 | execute("SHOW CREATE VIEW #{quote_table_name(name)}") 88 | .first[1] 89 | .sub(/\A.*#{quote_table_name(name)} AS /i, '') 90 | .gsub(/#{quote_table_name(@connectable.connection.current_database)}\./, '') 91 | end 92 | end 93 | end 94 | end 95 | -------------------------------------------------------------------------------- /lib/scenic/adapters/railtie.rb: -------------------------------------------------------------------------------- 1 | require "rails/railtie" 2 | require 'active_record/connection_adapters/abstract_mysql_adapter' 3 | require_relative '../mysql_adapter/schema_dumper' 4 | 5 | module Scenic 6 | module Adapters 7 | class MySqlRailtie < Rails::Railtie 8 | ActiveSupport.on_load :active_record do 9 | unless ActiveRecord::ConnectionAdapters::AbstractMysqlAdapter.respond_to?(:views) 10 | # Versions of Rails < 5 need this patch to avoid listing views as tables in schema.rb 11 | ActiveRecord::SchemaDumper.prepend Scenic::Adapters::MySql::SchemaDumper 12 | end 13 | end 14 | end 15 | end 16 | end 17 | -------------------------------------------------------------------------------- /lib/scenic/mysql_adapter.rb: -------------------------------------------------------------------------------- 1 | require 'active_record' 2 | require 'scenic' 3 | require_relative 'adapters/my_sql' 4 | require_relative 'mysql_adapter/version' 5 | require_relative 'adapters/railtie' if defined?(Rails) 6 | -------------------------------------------------------------------------------- /lib/scenic/mysql_adapter/schema_dumper.rb: -------------------------------------------------------------------------------- 1 | # This code is needed to support SQL views in MySQL in Rails < 5.1 2 | # if you want to continue using 'db/schema.rb' instead of migrating to 3 | # the SQL version 'db/structure.sql'. 4 | # 5 | # The problem is that, when you execute "SHOW TABLES" in MySQL, any 6 | # view that you hava created also shows in that list. 7 | # 8 | # So when you create a view, when the 'db/schema.rb' file is updated, 9 | # the view appears twice: first as a table, then as a view. 10 | # 11 | # As of this writing, the latest 5.x versions of Rails filter out 12 | # views when providing the list of tables, so this problem doesn't 13 | # appear. But for previous versions it's still there. 14 | # 15 | # This code patches the method SchemaDumper#table which is in charge 16 | # of dumping a table to the 'db/schema.rb' file when it's being updated 17 | # and does nothing when the supplied table is actually a view. 18 | # 19 | # This module needs to be prepended to ActiveRecord::SchemaDumper for 20 | # the patch to work. See 'adapters/railtie.rb' 21 | 22 | require "rails" 23 | require_relative '../adapters/my_sql' 24 | 25 | module Scenic 26 | module Adapters 27 | module MySql 28 | module SchemaDumper 29 | def table(table, stream) 30 | return if defined_views.any? { |v| v.name == table } # Ignore tables that are views 31 | super 32 | end 33 | 34 | private 35 | 36 | def defined_views 37 | @defined_views ||= Scenic.database.views 38 | end 39 | end 40 | end 41 | end 42 | end 43 | -------------------------------------------------------------------------------- /lib/scenic/mysql_adapter/version.rb: -------------------------------------------------------------------------------- 1 | module Scenic 2 | module MysqlAdapter 3 | VERSION = "1.0.1" 4 | end 5 | end 6 | -------------------------------------------------------------------------------- /lib/scenic/mysql_adapter/views.rb: -------------------------------------------------------------------------------- 1 | require 'tsort' 2 | 3 | module Scenic 4 | module MySQLAdapter 5 | class Views 6 | include TSort 7 | include Enumerable 8 | 9 | def initialize(views, graph) 10 | @views = views 11 | @graph = graph 12 | end 13 | 14 | def each(&block) 15 | @views.each(&block) 16 | end 17 | 18 | alias :tsort_each_node each 19 | 20 | private def tsort_each_child(node) 21 | @graph[node.name].each do |child_name| 22 | yield(@views.find { |v| v.name == child_name }) 23 | end 24 | end 25 | end 26 | end 27 | end 28 | -------------------------------------------------------------------------------- /scenic-mysql_adapter.gemspec: -------------------------------------------------------------------------------- 1 | # coding: utf-8 2 | lib = File.expand_path("../lib", __FILE__) 3 | $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib) 4 | require "scenic/mysql_adapter/version" 5 | 6 | Gem::Specification.new do |spec| 7 | spec.name = "scenic-mysql_adapter" 8 | spec.version = Scenic::MysqlAdapter::VERSION 9 | spec.authors = ["Lance Ivy"] 10 | spec.email = ["lance@cainlevy.net"] 11 | 12 | spec.summary = %q{MySQL adapter for thoughtbot/scenic} 13 | spec.homepage = "https://github.com/EmpaticoOrg/scenic-mysql_adapter" 14 | 15 | # Prevent pushing this gem to RubyGems.org. To allow pushes either set the 'allowed_push_host' 16 | # to allow pushing to a single host or delete this section to allow pushing to any host. 17 | if spec.respond_to?(:metadata) 18 | spec.metadata["allowed_push_host"] = "https://rubygems.org" 19 | else 20 | raise "RubyGems 2.0 or newer is required to protect against " \ 21 | "public gem pushes." 22 | end 23 | 24 | spec.files = `git ls-files -z`.split("\x0").reject do |f| 25 | f.match(%r{^(test|spec|features)/}) 26 | end 27 | spec.bindir = "exe" 28 | spec.executables = spec.files.grep(%r{^exe/}) { |f| File.basename(f) } 29 | spec.require_paths = ["lib"] 30 | 31 | spec.add_dependency "scenic", ">= 1.4.0" 32 | spec.add_dependency "mysql2" 33 | 34 | spec.add_development_dependency "bundler" 35 | spec.add_development_dependency "rake" 36 | spec.add_development_dependency "minitest", "~> 5.0" 37 | spec.add_development_dependency "appraisal" 38 | end 39 | -------------------------------------------------------------------------------- /test/scenic/mysql_adapter_test.rb: -------------------------------------------------------------------------------- 1 | require "test_helper" 2 | 3 | class Scenic::MysqlAdapterTest < Minitest::Test 4 | def test_that_it_has_a_version_number 5 | refute_nil ::Scenic::MysqlAdapter::VERSION 6 | end 7 | 8 | def setup 9 | @adapter = Scenic::Adapters::MySQL.new 10 | end 11 | 12 | def teardown 13 | ActiveRecord::Base.connection.execute 'DROP VIEW IF EXISTS numbers' 14 | end 15 | 16 | def test_create_view 17 | @adapter.create_view('numbers', 'SELECT 1 AS num') 18 | 19 | view = @adapter.views.find(&named('numbers')) 20 | assert_equal 'numbers', view.name 21 | assert_equal 'select 1 as `num`', view.definition.downcase 22 | end 23 | 24 | def test_replace_view 25 | @adapter.create_view('numbers', 'SELECT 1 AS num') 26 | @adapter.replace_view('numbers', 'SELECT 2 AS num') 27 | 28 | view = @adapter.views.find(&named('numbers')) 29 | assert_equal 'numbers', view.name 30 | assert_equal 'select 2 as `num`', view.definition.downcase 31 | end 32 | 33 | def test_drop_view 34 | @adapter.create_view('numbers', 'SELECT 1 AS num') 35 | @adapter.drop_view('numbers') 36 | 37 | refute @adapter.views.find(&named('numbers')) 38 | end 39 | 40 | def test_update_view 41 | @adapter.create_view('numbers', 'SELECT 1 AS num') 42 | @adapter.update_view('numbers', 'SELECT 2 AS num') 43 | 44 | view = @adapter.views.find(&named('numbers')) 45 | assert_equal 'numbers', view.name 46 | assert_equal 'select 2 as `num`', view.definition.downcase 47 | end 48 | 49 | def test_create_materialized_view 50 | @adapter.create_materialized_view('numbers') 51 | assert false 52 | rescue Scenic::Adapters::MySQL::FeatureNotSupportedError 53 | assert true 54 | else 55 | assert false 56 | end 57 | 58 | def test_update_materialized_view 59 | @adapter.update_materialized_view('numbers') 60 | assert false 61 | rescue Scenic::Adapters::MySQL::FeatureNotSupportedError 62 | assert true 63 | else 64 | assert false 65 | end 66 | 67 | def test_drop_materialized_view 68 | @adapter.drop_materialized_view('numbers') 69 | assert false 70 | rescue Scenic::Adapters::MySQL::FeatureNotSupportedError 71 | assert true 72 | else 73 | assert false 74 | end 75 | 76 | def test_views 77 | @adapter.create_view('b', 'select 1 as num') 78 | @adapter.create_view('a', 'select * from b') 79 | @adapter.create_view('c', 'select * from a') 80 | 81 | assert_equal ['b', 'a', 'c'], @adapter.views.map(&:name) 82 | ensure 83 | ActiveRecord::Base.connection.execute 'DROP VIEW IF EXISTS a' 84 | ActiveRecord::Base.connection.execute 'DROP VIEW IF EXISTS b' 85 | ActiveRecord::Base.connection.execute 'DROP VIEW IF EXISTS c' 86 | end 87 | 88 | def test_views_dont_show_as_tables 89 | @adapter.create_view('v', 'select 1 as num') 90 | output = ActiveRecord::SchemaDumper.dump(ActiveRecord::Base.connection, StringIO.new) 91 | refute_match(/create_table\s+\"v\"/i, output.string, "View is dumped as a table in schema.rb") 92 | ensure 93 | ActiveRecord::Base.connection.execute 'DROP VIEW IF EXISTS v' 94 | end 95 | 96 | private def named(name) 97 | proc { |v| v.name == name } 98 | end 99 | end 100 | -------------------------------------------------------------------------------- /test/test_helper.rb: -------------------------------------------------------------------------------- 1 | $LOAD_PATH.unshift File.expand_path("../../lib", __FILE__) 2 | require "scenic/mysql_adapter" 3 | 4 | require "minitest/autorun" 5 | 6 | Scenic.configure do |config| 7 | config.database = Scenic::Adapters::MySQL.new 8 | end 9 | 10 | # establish the database connection 11 | ActiveRecord::Base.establish_connection( 12 | ENV['DATABASE_URL'] || 'mysql2://root@localhost/scenic_mysql_adapter_test' 13 | ) 14 | ActiveRecord::Migration.verbose = false 15 | --------------------------------------------------------------------------------