schema_plus_views 0.3.1 → 1.0.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (40) hide show
  1. checksums.yaml +5 -5
  2. data/.github/workflows/prs.yml +194 -0
  3. data/.gitignore +1 -0
  4. data/.simplecov +20 -0
  5. data/Gemfile +4 -1
  6. data/README.md +58 -31
  7. data/Rakefile +2 -0
  8. data/gemfiles/Gemfile.base +1 -1
  9. data/gemfiles/activerecord-5.2/Gemfile.base +4 -0
  10. data/gemfiles/activerecord-5.2/Gemfile.mysql2 +10 -0
  11. data/gemfiles/activerecord-5.2/Gemfile.postgresql +10 -0
  12. data/gemfiles/{activerecord-4.2 → activerecord-5.2}/Gemfile.sqlite3 +3 -3
  13. data/gemfiles/activerecord-6.0/Gemfile.base +4 -0
  14. data/gemfiles/activerecord-6.0/Gemfile.mysql2 +10 -0
  15. data/gemfiles/activerecord-6.0/Gemfile.postgresql +10 -0
  16. data/gemfiles/activerecord-6.0/Gemfile.sqlite3 +10 -0
  17. data/lib/schema_plus/views/active_record/connection_adapters/abstract_adapter.rb +21 -5
  18. data/lib/schema_plus/views/active_record/connection_adapters/mysql2_adapter.rb +7 -11
  19. data/lib/schema_plus/views/active_record/connection_adapters/postgresql_adapter.rb +76 -17
  20. data/lib/schema_plus/views/active_record/connection_adapters/sqlite3_adapter.rb +7 -9
  21. data/lib/schema_plus/views/active_record/migration/command_recorder.rb +5 -1
  22. data/lib/schema_plus/views/middleware.rb +35 -34
  23. data/lib/schema_plus/views/schema_dump.rb +28 -0
  24. data/lib/schema_plus/views/version.rb +3 -1
  25. data/lib/schema_plus/views.rb +3 -0
  26. data/lib/schema_plus_views.rb +2 -0
  27. data/schema_dev.yml +7 -2
  28. data/schema_plus_views.gemspec +10 -9
  29. data/spec/dumper_spec.rb +60 -10
  30. data/spec/introspection_spec.rb +54 -6
  31. data/spec/middleware_spec.rb +3 -18
  32. data/spec/migration_spec.rb +108 -58
  33. data/spec/named_schemas_spec.rb +10 -42
  34. data/spec/sanity_spec.rb +2 -0
  35. data/spec/spec_helper.rb +16 -3
  36. metadata +39 -56
  37. data/.travis.yml +0 -18
  38. data/gemfiles/activerecord-4.2/Gemfile.base +0 -3
  39. data/gemfiles/activerecord-4.2/Gemfile.mysql2 +0 -10
  40. data/gemfiles/activerecord-4.2/Gemfile.postgresql +0 -10
@@ -1,32 +1,91 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module SchemaPlus::Views
2
4
  module ActiveRecord
3
5
  module ConnectionAdapters
4
6
  module PostgresqlAdapter
7
+ POSTGIS_VIEWS = %W[
8
+ geography_columns
9
+ geometry_columns
10
+ raster_columns
11
+ raster_overviews
12
+ ].freeze
13
+
14
+ # Create a view given the SQL definition. Specify :force => true
15
+ # to first drop the view if it already exists.
16
+ def create_view(view_name, definition, options={})
17
+ SchemaMonkey::Middleware::Migration::CreateView.start(connection: self, view_name: view_name, definition: definition, options: options) do |env|
18
+ definition = env.definition
19
+ view_name = env.view_name
20
+ options = env.options
21
+ definition = definition.to_sql if definition.respond_to? :to_sql
22
+
23
+ if options[:materialized] && options[:allow_replace]
24
+ raise ArgumentError, 'allow_replace is not supported for materialized views'
25
+ end
26
+
27
+ if options[:force]
28
+ drop_view(view_name, {if_exists: true}.merge(options.slice(:materialized)))
29
+ end
30
+
31
+ command = if options[:materialized]
32
+ "CREATE MATERIALIZED"
33
+ elsif options[:allow_replace]
34
+ "CREATE OR REPLACE"
35
+ else
36
+ "CREATE"
37
+ end
38
+
39
+ execute "#{command} VIEW #{quote_table_name(view_name)} AS #{definition}"
40
+ end
41
+ end
42
+
43
+ # Drop the named view. Specify :if_exists => true
44
+ # to fail silently if the view doesn't exist.
45
+ def drop_view(view_name, options = {})
46
+ SchemaMonkey::Middleware::Migration::DropView.start(connection: self, view_name: view_name, options: options) do |env|
47
+ view_name = env.view_name
48
+ options = env.options
49
+ materialized = options[:materialized] ? 'MATERIALIZED' : ''
50
+ sql = "DROP #{materialized} VIEW"
51
+ sql += " IF EXISTS" if options[:if_exists]
52
+ sql += " #{quote_table_name(view_name)}"
53
+ execute sql
54
+ end
55
+ end
5
56
 
6
- def views(name = nil) #:nodoc:
7
- SchemaMonkey::Middleware::Schema::Views.start(connection: self, query_name: name, views: []) { |env|
8
- sql = <<-SQL
9
- SELECT viewname
10
- FROM pg_views
11
- WHERE schemaname = ANY (current_schemas(false))
12
- AND viewname NOT LIKE 'pg\_%'
13
- SQL
14
- sql += " AND schemaname != 'postgis'" if adapter_name == 'PostGIS'
15
- env.views += env.connection.query(sql, env.query_name).map { |row| row[0] }
16
- }.views
57
+ # Refresh a materialized view.
58
+ def refresh_view(view_name, options = {})
59
+ SchemaMonkey::Middleware::Migration::RefreshView.start(connection: self, view_name: view_name, options: options) do |env|
60
+ view_name = env.view_name
61
+ sql = "REFRESH MATERIALIZED VIEW #{quote_table_name(view_name)}"
62
+ execute sql
63
+ end
17
64
  end
18
65
 
19
- def view_definition(view_name, name = nil) #:nodoc:
20
- SchemaMonkey::Middleware::Schema::ViewDefinition.start(connection: self, view_name: view_name, query_name: name) { |env|
66
+ def views #:nodoc:
67
+ # Filter out any view that begins with "pg_"
68
+ super.reject do |c|
69
+ c.start_with?("pg_") || POSTGIS_VIEWS.include?(c)
70
+ end
71
+ end
72
+
73
+ def view_full_definition(view_name, name = nil) #:nodoc:
74
+ data = SchemaMonkey::Middleware::Schema::ViewDefinition.start(connection: self, view_name: view_name, query_name: name, view_type: :view) { |env|
21
75
  result = env.connection.query(<<-SQL, name)
22
- SELECT pg_get_viewdef(oid)
76
+ SELECT pg_get_viewdef(oid), relkind
23
77
  FROM pg_class
24
- WHERE relkind = 'v'
78
+ WHERE relkind in ('v', 'm')
25
79
  AND relname = '#{env.view_name}'
26
80
  SQL
27
81
  row = result.first
28
- env.definition = row.first.chomp(';').strip unless row.nil?
29
- }.definition
82
+ unless row.nil?
83
+ env.definition = row.first.chomp(';').strip
84
+ env.view_type = :materialized if row.second == 'm'
85
+ end
86
+ }
87
+
88
+ [data.definition, data.view_type]
30
89
  end
31
90
 
32
91
  end
@@ -1,20 +1,18 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module SchemaPlus::Views
2
4
  module ActiveRecord
3
5
  module ConnectionAdapters
4
6
  module Sqlite3Adapter
5
7
 
6
- def views(name = nil)
7
- SchemaMonkey::Middleware::Schema::Views.start(connection: self, query_name: name, views: []) { |env|
8
- env.views += env.connection.execute("SELECT name FROM sqlite_master WHERE type='view'", env.query_name).collect{|row| row["name"]}
9
- }.views
10
- end
11
-
12
- def view_definition(view_name, name = nil)
13
- SchemaMonkey::Middleware::Schema::ViewDefinition.start(connection: self, view_name: view_name, query_name: name) { |env|
8
+ def view_full_definition(view_name, name = nil)
9
+ data = SchemaMonkey::Middleware::Schema::ViewDefinition.start(connection: self, view_name: view_name, query_name: name, view_type: :view) { |env|
14
10
  sql = env.connection.execute("SELECT sql FROM sqlite_master WHERE type='view' AND name=#{quote(env.view_name)}", env.query_name).collect{|row| row["sql"]}.first
15
11
  sql.sub!(/^CREATE VIEW \S* AS\s+/im, '') unless sql.nil?
16
12
  env.definition = sql
17
- }.definition
13
+ }
14
+
15
+ [data.definition, data.view_type]
18
16
  end
19
17
 
20
18
  end
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module SchemaPlus::Views
2
4
  module ActiveRecord
3
5
  module Migration
@@ -11,7 +13,9 @@ module SchemaPlus::Views
11
13
  end
12
14
 
13
15
  def invert_create_view(args)
14
- [ :drop_view, [args.first] ]
16
+ options = {}
17
+ options[:materialized] = args[2][:materialized] if args[2].has_key?(:materialized)
18
+ [ :drop_view, [args.first, options] ]
15
19
  end
16
20
 
17
21
  end
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module SchemaPlus::Views
2
4
  module Middleware
3
5
 
@@ -9,44 +11,43 @@ module SchemaPlus::Views
9
11
  re_view_referent = %r{(?:(?i)FROM|JOIN) \S*\b(\S+)\b}
10
12
  env.connection.views.each do |view_name|
11
13
  next if env.dumper.ignored?(view_name)
12
- view = View.new(name: view_name, definition: env.connection.view_definition(view_name))
13
- env.dump.tables[view.name] = view
14
- env.dump.depends(view.name, view.definition.scan(re_view_referent).flatten)
15
- end
16
- end
14
+ definition, view_type = env.connection.view_full_definition(view_name)
17
15
 
18
- # quacks like a SchemaMonkey Dump::Table
19
- class View < KeyStruct[:name, :definition]
20
- def assemble(stream)
21
- heredelim = "END_VIEW_#{name.upcase}"
22
- stream.puts <<-ENDVIEW
23
- create_view "#{name}", <<-'#{heredelim}', :force => true
24
- #{definition}
25
- #{heredelim}
16
+ indexes = []
26
17
 
27
- ENDVIEW
28
- end
29
- end
30
- end
31
- end
18
+ if view_type == :materialized
19
+ env.connection.indexes(view_name).each do |index|
20
+ indexes << SchemaPlus::Core::SchemaDump::Table::Index.new(
21
+ name: index.name, columns: index.columns, options: view_index_options(index, env.connection)
22
+ )
23
+ end
24
+ end
32
25
 
33
- module Schema
34
- module Tables
26
+ view = ::SchemaPlus::Views::SchemaDump::View.new(
27
+ name: view_name,
28
+ definition: definition,
29
+ view_type: view_type,
30
+ indexes: indexes
31
+ )
35
32
 
36
- module Mysql
37
- def after(env)
38
- Tables.filter_out_views(env)
33
+ env.dump.tables[view.name] = view
34
+ env.dump.depends(view.name, view.definition.scan(re_view_referent).flatten)
39
35
  end
40
36
  end
41
37
 
42
- module Sqlite3
43
- def after(env)
44
- Tables.filter_out_views(env)
45
- end
46
- end
38
+ # Take from ActiveRecord::SchemaDumper#index_parts
39
+ def view_index_options(index, connection)
40
+ options = {}
41
+ options[:unique] = true if index.unique
42
+ options[:length] = index.lengths if index.lengths.present?
43
+ options[:order] = index.orders if index.orders.present?
44
+ options[:opclass] = index.opclasses if index.opclasses.present?
45
+ options[:where] = index.where if index.where
46
+ options[:using] = index.using if !connection.default_index_type?(index)
47
+ options[:type] = index.type if index.type
48
+ options[:comment] = index.comment if index.comment
47
49
 
48
- def self.filter_out_views(env)
49
- env.tables -= env.connection.views(env.query_name)
50
+ options
50
51
  end
51
52
  end
52
53
  end
@@ -56,11 +57,8 @@ module SchemaPlus::Views
56
57
  # for tables
57
58
 
58
59
  module Schema
59
- module Views
60
- ENV = [:connection, :query_name, :views]
61
- end
62
60
  module ViewDefinition
63
- ENV = [:connection, :view_name, :query_name, :definition]
61
+ ENV = [:connection, :view_name, :query_name, :definition, :view_type]
64
62
  end
65
63
  end
66
64
 
@@ -71,6 +69,9 @@ module SchemaPlus::Views
71
69
  module DropView
72
70
  ENV = [:connection, :view_name, :options]
73
71
  end
72
+ module RefreshView
73
+ ENV = [:connection, :view_name, :options]
74
+ end
74
75
  end
75
76
  end
76
77
 
@@ -0,0 +1,28 @@
1
+ # frozen_string_literal: true
2
+
3
+ module SchemaPlus
4
+ module Views
5
+ class SchemaDump
6
+ # quacks like a SchemaMonkey Dump::Table
7
+ class View < Struct.new(:name, :definition, :view_type, :indexes, keyword_init: true)
8
+ def assemble(stream)
9
+ extra_options = ", materialized: true" if view_type == :materialized
10
+ heredelim = "END_VIEW_#{name.upcase}"
11
+ stream.puts <<~ENDVIEW
12
+ create_view "#{name}", <<-'#{heredelim}', :force => true#{extra_options}
13
+ #{definition}
14
+ #{heredelim}
15
+ ENDVIEW
16
+ stream.puts
17
+
18
+ indexes.each do |index|
19
+ stream.write " add_index \"#{name}\", "
20
+ index.assemble(stream)
21
+ stream.puts ""
22
+ end
23
+ stream.puts ""
24
+ end
25
+ end
26
+ end
27
+ end
28
+ end
@@ -1,5 +1,7 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module SchemaPlus
2
4
  module Views
3
- VERSION = "0.3.1"
5
+ VERSION = '1.0.0'
4
6
  end
5
7
  end
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  require 'schema_plus/core'
2
4
 
3
5
  module SchemaPlus
@@ -9,6 +11,7 @@ require_relative 'views/version'
9
11
  require_relative 'views/active_record/connection_adapters/abstract_adapter'
10
12
  require_relative 'views/active_record/migration/command_recorder'
11
13
  require_relative 'views/middleware'
14
+ require_relative 'views/schema_dump'
12
15
 
13
16
  module SchemaPlus::Views
14
17
  module ActiveRecord
@@ -1 +1,3 @@
1
+ # frozen_string_literal: true
2
+
1
3
  require_relative 'schema_plus/views'
data/schema_dev.yml CHANGED
@@ -1,8 +1,13 @@
1
1
  ruby:
2
- - 2.1.5
2
+ - 2.5
3
+ - 2.7
4
+ - 3.0
3
5
  activerecord:
4
- - 4.2
6
+ - 5.2
7
+ - 6.0
5
8
  db:
6
9
  - mysql2
7
10
  - sqlite3
8
11
  - postgresql
12
+ dbversions:
13
+ postgresql: ['9.6', '10', '11', '12']
@@ -1,4 +1,5 @@
1
- # coding: utf-8
1
+ # frozen_string_literal: true
2
+
2
3
  lib = File.expand_path('../lib', __FILE__)
3
4
  $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
4
5
  require 'schema_plus/views/version'
@@ -17,13 +18,13 @@ Gem::Specification.new do |gem|
17
18
  gem.test_files = gem.files.grep(%r{^(test|spec|features)/})
18
19
  gem.require_paths = ["lib"]
19
20
 
20
- gem.add_dependency "activerecord", "~> 4.2"
21
- gem.add_dependency "schema_plus_core", "~> 1.0"
21
+ gem.required_ruby_version = '>= 2.5'
22
+
23
+ gem.add_dependency "activerecord", '>= 5.2', '< 6.1'
24
+ gem.add_dependency "schema_plus_core", '~> 3.0'
22
25
 
23
- gem.add_development_dependency "bundler", "~> 1.7"
24
- gem.add_development_dependency "rake", "~> 10.0"
25
- gem.add_development_dependency "rspec", "~> 3.0"
26
- gem.add_development_dependency "schema_dev", "~> 3.6"
27
- gem.add_development_dependency "simplecov"
28
- gem.add_development_dependency "simplecov-gem-profile"
26
+ gem.add_development_dependency "bundler"
27
+ gem.add_development_dependency "rake", '~> 13.0'
28
+ gem.add_development_dependency "rspec", '~> 3.0'
29
+ gem.add_development_dependency "schema_dev", '~> 4.1'
29
30
  end
data/spec/dumper_spec.rb CHANGED
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  require 'spec_helper'
2
4
 
3
5
  class Item < ActiveRecord::Base
@@ -5,8 +7,6 @@ end
5
7
 
6
8
  describe "Dumper" do
7
9
 
8
- let(:schema) { ActiveRecord::Schema }
9
-
10
10
  let(:migration) { ActiveRecord::Migration }
11
11
 
12
12
  let(:connection) { ActiveRecord::Base.connection }
@@ -39,27 +39,77 @@ describe "Dumper" do
39
39
  # when in the (say) development database, but then uses it to
40
40
  # initialize the test database when testing. this meant that the
41
41
  # test database had views into the development database.
42
- db = connection.respond_to?(:current_database)? connection.current_database : SchemaDev::Rspec.db_configuration[:database]
42
+ db = connection.respond_to?(:current_database) ? connection.current_database : SchemaDev::Rspec.db_configuration[:database]
43
43
  expect(dump).not_to match(%r{#{connection.quote_table_name(db)}[.]})
44
44
  end
45
45
 
46
+ context 'materialized views', postgresql: :only do
47
+ before do
48
+ apply_migration do
49
+ create_view :materialized, Item.select('b, s').where(:a => 1), materialized: true
50
+
51
+ add_index :items, :a, where: 'a = 1'
52
+
53
+ add_index :materialized, :s
54
+ add_index :materialized, :b, unique: true, name: 'index_materialized_unique'
55
+ end
56
+ end
57
+
58
+ it "include the view definitions" do
59
+ expect(dump).to match(view_re("materialized", /SELECT .*b.*,.*s.* FROM .*items.* WHERE \(?.*a.* = 1\)?/mi, ', materialized: true'))
60
+ end
61
+
62
+ it 'includes the index definitions' do
63
+ expect(dump).to match(/index_materialized_on_s/)
64
+ expect(dump).to match(/index_materialized_unique.+unique/)
65
+ end
66
+
67
+ context 'when indexes are function results', postgresql: :only do
68
+ before do
69
+ apply_migration do
70
+ add_index :materialized, 'length(s)', name: 'index_materialized_function'
71
+ end
72
+ end
73
+
74
+ it 'includes the index definition' do
75
+ expect(dump).to match(/length\(.+name.+index_materialized_function/)
76
+ end
77
+ end
78
+
79
+ context 'when index has a where clause', postgresql: :only do
80
+ before do
81
+ apply_migration do
82
+ add_index :materialized, :s, where: 'b = 1', name: 'index_materialized_conditional'
83
+ end
84
+ end
85
+
86
+ it 'includes the index definition' do
87
+ expect(dump).to match(/index_materialized_conditional.+where/)
88
+ end
89
+ end
90
+ end
91
+
46
92
  protected
47
93
 
48
- def view_re(name, re)
94
+ def view_re(name, re, extra_config = '')
49
95
  heredelim = "END_VIEW_#{name.upcase}"
50
- %r{create_view "#{name}", <<-'#{heredelim}', :force => true\n\s*#{re}\s*\n *#{heredelim}$}mi
96
+ %r{create_view "#{name}", <<-'#{heredelim}', :force => true#{extra_config}\n\s*#{re}\s*\n *#{heredelim}$}mi
51
97
  end
52
98
 
53
99
  def define_schema_and_data
54
- connection.views.each do |view| connection.drop_view view end
55
- connection.tables.each do |table| connection.drop_table table, cascade: true end
100
+ connection.views.each do |view|
101
+ connection.drop_view view
102
+ end
103
+ connection.tables.each do |table|
104
+ connection.drop_table table, cascade: true
105
+ end
56
106
 
57
- schema.define do
107
+ apply_migration do
58
108
 
59
109
  create_table :items, :force => true do |t|
60
110
  t.integer :a
61
111
  t.integer :b
62
- t.string :s
112
+ t.string :s
63
113
  end
64
114
 
65
115
  create_view :a_ones, Item.select('b, s').where(:a => 1)
@@ -71,7 +121,7 @@ describe "Dumper" do
71
121
  connection.execute "insert into items (a, b, s) values (2, 2, 'two_two')"
72
122
  end
73
123
 
74
- def dump(opts={})
124
+ def dump(opts = {})
75
125
  StringIO.open { |stream|
76
126
  ActiveRecord::SchemaDumper.ignore_tables = Array.wrap(opts[:ignore_tables])
77
127
  ActiveRecord::SchemaDumper.dump(ActiveRecord::Base.connection, stream)
@@ -1,12 +1,11 @@
1
+ # frozen_string_literal: true
2
+
1
3
  require 'spec_helper'
2
4
 
3
5
  class Item < ActiveRecord::Base
4
6
  end
5
7
 
6
8
  describe "Introspection" do
7
-
8
- let(:schema) { ActiveRecord::Schema }
9
-
10
9
  let(:migration) { ActiveRecord::Migration }
11
10
 
12
11
  let(:connection) { ActiveRecord::Base.connection }
@@ -17,8 +16,6 @@ describe "Introspection" do
17
16
 
18
17
  it "should list all views" do
19
18
  expect(connection.views.sort).to eq(%W[a_ones ab_ones])
20
- expect(connection.view_definition('a_ones')).to match(%r{^SELECT .*b.*,.*s.* FROM .*items.* WHERE .*a.* = 1}mi)
21
- expect(connection.view_definition('ab_ones')).to match(%r{^SELECT .*s.* FROM .*a_ones.* WHERE .*b.* = 1}mi)
22
19
  end
23
20
 
24
21
  it "should ignore views named pg_*", postgresql: :only do
@@ -30,6 +27,29 @@ describe "Introspection" do
30
27
  end
31
28
  end
32
29
 
30
+ context "when using PostGIS", postgresql: :only do
31
+ before do
32
+ apply_migration do
33
+ SchemaPlus::Views::ActiveRecord::ConnectionAdapters::PostgresqlAdapter::POSTGIS_VIEWS.each do |view|
34
+ create_view view, 'select 1'
35
+ end
36
+ end
37
+ end
38
+
39
+ after do
40
+ apply_migration do
41
+ SchemaPlus::Views::ActiveRecord::ConnectionAdapters::PostgresqlAdapter::POSTGIS_VIEWS.each do |view|
42
+ drop_view view
43
+ end
44
+ end
45
+ end
46
+
47
+ it "should hide views in postgis views" do
48
+ expect(connection.views.sort).to eq(%W[a_ones ab_ones])
49
+ end
50
+ end
51
+
52
+
33
53
  it "should not be listed as a table" do
34
54
  expect(connection.tables).not_to include('a_ones')
35
55
  expect(connection.tables).not_to include('ab_ones')
@@ -40,6 +60,11 @@ describe "Introspection" do
40
60
  expect(connection.view_definition('ab_ones')).to match(%r{^SELECT .*s.* FROM .*a_ones.* WHERE .*b.* = 1}mi)
41
61
  end
42
62
 
63
+ it 'returns them as view types' do
64
+ expect(connection.view_type('a_ones')).to eq(:view)
65
+ expect(connection.view_type('ab_ones')).to eq(:view)
66
+ end
67
+
43
68
  context "in mysql", :mysql => :only do
44
69
 
45
70
  around(:each) do |example|
@@ -67,13 +92,36 @@ describe "Introspection" do
67
92
  end
68
93
  end
69
94
 
95
+ context 'in postgresql', postgresql: :only do
96
+ context 'for materialized views' do
97
+ around(:each) do |example|
98
+ begin
99
+ migration.drop_view :materialized, materialized: true, if_exists: true
100
+ example.run
101
+ ensure
102
+ migration.drop_view :materialized, materialized: true, if_exists: true
103
+ end
104
+ end
105
+
106
+ it 'returns the definition' do
107
+ migration.create_view :materialized, 'SELECT * FROM items WHERE (a=2)', materialized: true
108
+ expect(connection.view_definition('materialized')).to match(%r{FROM items})
109
+ end
110
+
111
+ it 'returns the type as materialized' do
112
+ migration.create_view :materialized, 'SELECT * FROM items WHERE (a=2)', materialized: true
113
+ expect(connection.view_type('materialized')).to eq(:materialized)
114
+ end
115
+ end
116
+ end
117
+
70
118
  protected
71
119
 
72
120
  def define_schema_and_data
73
121
  connection.views.each do |view| connection.drop_view view end
74
122
  connection.tables.each do |table| connection.drop_table table, cascade: true end
75
123
 
76
- schema.define do
124
+ apply_migration do
77
125
 
78
126
  create_table :items, :force => true do |t|
79
127
  t.integer :a
@@ -1,15 +1,11 @@
1
+ # frozen_string_literal: true
2
+
1
3
  require 'spec_helper'
2
4
 
3
5
  module TestMiddleware
4
6
  module Middleware
5
7
 
6
8
  module Schema
7
- module Views
8
- SPY = []
9
- def after(env)
10
- SPY << env.to_hash.except(:connection)
11
- end
12
- end
13
9
  module ViewDefinition
14
10
  SPY = []
15
11
  def after(env)
@@ -40,12 +36,11 @@ SchemaMonkey.register TestMiddleware
40
36
 
41
37
  context SchemaPlus::Views::Middleware do
42
38
 
43
- let(:schema) { ActiveRecord::Schema }
44
39
  let(:migration) { ActiveRecord::Migration }
45
40
  let(:connection) { ActiveRecord::Base.connection }
46
41
 
47
42
  before(:each) do
48
- schema.define do
43
+ apply_migration do
49
44
  create_table :items, force: true do |t|
50
45
  t.integer :a
51
46
  end
@@ -53,16 +48,6 @@ context SchemaPlus::Views::Middleware do
53
48
  end
54
49
  end
55
50
 
56
- context TestMiddleware::Middleware::Schema::Views do
57
- it "calls middleware" do
58
- expect(spy_on {connection.views 'qn'}).to eq({
59
- #connection: connection,
60
- views: ['a_view'],
61
- query_name: 'qn'
62
- })
63
- end
64
- end
65
-
66
51
  context TestMiddleware::Middleware::Schema::ViewDefinition do
67
52
  it "calls middleware" do
68
53
  spied = spy_on {connection.view_definition('a_view', 'qn')}