schema_plus_views 0.3.1 → 1.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
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')}