scenic-jets 1.5.4

Sign up to get free protection for your applications and to get access to all the features.
Files changed (77) hide show
  1. checksums.yaml +7 -0
  2. data/.github/workflows/ci.yml +78 -0
  3. data/.gitignore +19 -0
  4. data/.hound.yml +2 -0
  5. data/.rubocop.yml +129 -0
  6. data/.yardopts +4 -0
  7. data/CHANGELOG.md +223 -0
  8. data/CODE_OF_CONDUCT.md +76 -0
  9. data/CONTRIBUTING.md +24 -0
  10. data/Gemfile +16 -0
  11. data/LICENSE.txt +22 -0
  12. data/README.md +5 -0
  13. data/Rakefile +29 -0
  14. data/SECURITY.md +14 -0
  15. data/bin/rake +17 -0
  16. data/bin/rspec +17 -0
  17. data/bin/setup +18 -0
  18. data/bin/yard +16 -0
  19. data/lib/generators/scenic/generators.rb +12 -0
  20. data/lib/generators/scenic/materializable.rb +31 -0
  21. data/lib/generators/scenic/model/USAGE +12 -0
  22. data/lib/generators/scenic/model/model_generator.rb +52 -0
  23. data/lib/generators/scenic/model/templates/model.erb +3 -0
  24. data/lib/generators/scenic/view/USAGE +20 -0
  25. data/lib/generators/scenic/view/templates/db/migrate/create_view.erb +5 -0
  26. data/lib/generators/scenic/view/templates/db/migrate/update_view.erb +12 -0
  27. data/lib/generators/scenic/view/view_generator.rb +127 -0
  28. data/lib/scenic.rb +31 -0
  29. data/lib/scenic/adapters/postgres.rb +256 -0
  30. data/lib/scenic/adapters/postgres/connection.rb +57 -0
  31. data/lib/scenic/adapters/postgres/errors.rb +26 -0
  32. data/lib/scenic/adapters/postgres/index_reapplication.rb +71 -0
  33. data/lib/scenic/adapters/postgres/indexes.rb +53 -0
  34. data/lib/scenic/adapters/postgres/refresh_dependencies.rb +116 -0
  35. data/lib/scenic/adapters/postgres/views.rb +74 -0
  36. data/lib/scenic/command_recorder.rb +52 -0
  37. data/lib/scenic/command_recorder/statement_arguments.rb +51 -0
  38. data/lib/scenic/configuration.rb +37 -0
  39. data/lib/scenic/definition.rb +35 -0
  40. data/lib/scenic/index.rb +36 -0
  41. data/lib/scenic/schema_dumper.rb +44 -0
  42. data/lib/scenic/statements.rb +163 -0
  43. data/lib/scenic/version.rb +3 -0
  44. data/lib/scenic/view.rb +54 -0
  45. data/scenic.gemspec +36 -0
  46. data/spec/acceptance/user_manages_views_spec.rb +88 -0
  47. data/spec/acceptance_helper.rb +33 -0
  48. data/spec/dummy/.gitignore +16 -0
  49. data/spec/dummy/Rakefile +13 -0
  50. data/spec/dummy/app/models/application_record.rb +5 -0
  51. data/spec/dummy/bin/bundle +3 -0
  52. data/spec/dummy/bin/rails +4 -0
  53. data/spec/dummy/bin/rake +4 -0
  54. data/spec/dummy/config.ru +4 -0
  55. data/spec/dummy/config/application.rb +15 -0
  56. data/spec/dummy/config/boot.rb +5 -0
  57. data/spec/dummy/config/database.yml +14 -0
  58. data/spec/dummy/config/environment.rb +5 -0
  59. data/spec/dummy/db/migrate/.keep +0 -0
  60. data/spec/dummy/db/views/.keep +0 -0
  61. data/spec/generators/scenic/model/model_generator_spec.rb +36 -0
  62. data/spec/generators/scenic/view/view_generator_spec.rb +57 -0
  63. data/spec/integration/revert_spec.rb +74 -0
  64. data/spec/scenic/adapters/postgres/connection_spec.rb +79 -0
  65. data/spec/scenic/adapters/postgres/refresh_dependencies_spec.rb +82 -0
  66. data/spec/scenic/adapters/postgres/views_spec.rb +37 -0
  67. data/spec/scenic/adapters/postgres_spec.rb +209 -0
  68. data/spec/scenic/command_recorder/statement_arguments_spec.rb +41 -0
  69. data/spec/scenic/command_recorder_spec.rb +111 -0
  70. data/spec/scenic/configuration_spec.rb +27 -0
  71. data/spec/scenic/definition_spec.rb +62 -0
  72. data/spec/scenic/schema_dumper_spec.rb +115 -0
  73. data/spec/scenic/statements_spec.rb +199 -0
  74. data/spec/spec_helper.rb +22 -0
  75. data/spec/support/generator_spec_setup.rb +14 -0
  76. data/spec/support/view_definition_helpers.rb +10 -0
  77. metadata +307 -0
@@ -0,0 +1,15 @@
1
+ require File.expand_path("../boot", __FILE__)
2
+
3
+ # Pick the frameworks you want:
4
+ require "active_record/railtie"
5
+
6
+ Bundler.require(*Rails.groups)
7
+ require "scenic"
8
+
9
+ module Dummy
10
+ class Application < Rails::Application
11
+ config.cache_classes = true
12
+ config.eager_load = false
13
+ config.active_support.deprecation = :stderr
14
+ end
15
+ end
@@ -0,0 +1,5 @@
1
+ # Set up gems listed in the Gemfile.
2
+ ENV["BUNDLE_GEMFILE"] ||= File.expand_path("../../../../Gemfile", __FILE__)
3
+
4
+ require "bundler/setup" if File.exist?(ENV["BUNDLE_GEMFILE"])
5
+ $LOAD_PATH.unshift File.expand_path("../../../../lib", __FILE__)
@@ -0,0 +1,14 @@
1
+ development: &default
2
+ adapter: postgresql
3
+ database: dummy_development
4
+ encoding: unicode
5
+ host: localhost
6
+ pool: 5
7
+ <% if ENV.fetch("GITHUB_ACTIONS", false) %>
8
+ username: <%= ENV.fetch("POSTGRES_USER") %>
9
+ password: <%= ENV.fetch("POSTGRES_PASSWORD") %>
10
+ <% end %>
11
+
12
+ test:
13
+ <<: *default
14
+ database: dummy_test
@@ -0,0 +1,5 @@
1
+ # Load the Rails application.
2
+ require File.expand_path("../application", __FILE__)
3
+
4
+ # Initialize the Rails application.
5
+ Rails.application.initialize!
File without changes
File without changes
@@ -0,0 +1,36 @@
1
+ require "spec_helper"
2
+ require "generators/scenic/model/model_generator"
3
+
4
+ module Scenic::Generators
5
+ describe ModelGenerator, :generator do
6
+ before do
7
+ allow(ViewGenerator).to receive(:new)
8
+ .and_return(
9
+ instance_double("Scenic::Generators::ViewGenerator").as_null_object,
10
+ )
11
+ end
12
+
13
+ it "invokes the view generator" do
14
+ run_generator ["current_customer"]
15
+
16
+ expect(ViewGenerator).to have_received(:new)
17
+ end
18
+
19
+ it "creates a migration to create the view" do
20
+ run_generator ["current_customer"]
21
+ model_definition = file("app/models/current_customer.rb")
22
+ expect(model_definition).to exist
23
+ expect(model_definition).to have_correct_syntax
24
+ expect(model_definition).not_to contain("self.refresh")
25
+ expect(model_definition).to have_correct_syntax
26
+ end
27
+
28
+ it "adds a refresh method to materialized models" do
29
+ run_generator ["active_user", "--materialized"]
30
+ model_definition = file("app/models/active_user.rb")
31
+
32
+ expect(model_definition).to contain("self.refresh")
33
+ expect(model_definition).to have_correct_syntax
34
+ end
35
+ end
36
+ end
@@ -0,0 +1,57 @@
1
+ require "spec_helper"
2
+ require "generators/scenic/view/view_generator"
3
+
4
+ describe Scenic::Generators::ViewGenerator, :generator do
5
+ it "creates view definition and migration files" do
6
+ migration = file("db/migrate/create_searches.rb")
7
+ view_definition = file("db/views/searches_v01.sql")
8
+
9
+ run_generator ["search"]
10
+
11
+ expect(migration).to be_a_migration
12
+ expect(view_definition).to exist
13
+ end
14
+
15
+ it "updates an existing view" do
16
+ with_view_definition("searches", 1, "hello") do
17
+ migration = file("db/migrate/update_searches_to_version_2.rb")
18
+ view_definition = file("db/views/searches_v02.sql")
19
+ allow(Dir).to receive(:entries).and_return(["searches_v01.sql"])
20
+
21
+ run_generator ["search"]
22
+
23
+ expect(migration).to be_a_migration
24
+ expect(view_definition).to exist
25
+ end
26
+ end
27
+
28
+ it "adds 'materialized: true' to the migration if view is materialized" do
29
+ with_view_definition("aired_episodes", 1, "hello") do
30
+ allow(Dir).to receive(:entries).and_return(["aired_episodes_v01.sql"])
31
+
32
+ run_generator ["aired_episode", "--materialized"]
33
+ migration = migration_file(
34
+ "db/migrate/update_aired_episodes_to_version_2.rb",
35
+ )
36
+ expect(migration).to contain "materialized: true"
37
+ end
38
+ end
39
+
40
+ context "for views created in a schema other than 'public'" do
41
+ it "creates a view definition" do
42
+ view_definition = file("db/views/non_public_searches_v01.sql")
43
+
44
+ run_generator ["non_public.search"]
45
+
46
+ expect(view_definition).to exist
47
+ end
48
+
49
+ it "creates a migration file" do
50
+ run_generator ["non_public.search"]
51
+
52
+ migration = migration_file("db/migrate/create_non_public_searches.rb")
53
+ expect(migration).to contain(/class CreateNonPublicSearches/)
54
+ expect(migration).to contain(/create_view "non_public.searches"/)
55
+ end
56
+ end
57
+ end
@@ -0,0 +1,74 @@
1
+ require "spec_helper"
2
+
3
+ describe "Reverting scenic schema statements", :db do
4
+ around do |example|
5
+ with_view_definition :greetings, 1, "SELECT text 'hola' AS greeting" do
6
+ example.run
7
+ end
8
+ end
9
+
10
+ it "reverts dropped view to specified version" do
11
+ run_migration(migration_for_create, :up)
12
+ run_migration(migration_for_drop, :up)
13
+ run_migration(migration_for_drop, :down)
14
+
15
+ expect { execute("SELECT * from greetings") }
16
+ .not_to raise_error
17
+ end
18
+
19
+ it "reverts updated view to specified version" do
20
+ with_view_definition :greetings, 2, "SELECT text 'good day' AS greeting" do
21
+ run_migration(migration_for_create, :up)
22
+ run_migration(migration_for_update, :up)
23
+ run_migration(migration_for_update, :down)
24
+
25
+ greeting = execute("SELECT * from greetings")[0]["greeting"]
26
+
27
+ expect(greeting).to eq "hola"
28
+ end
29
+ end
30
+
31
+ def migration_for_create
32
+ Class.new(migration_class) do
33
+ def change
34
+ create_view :greetings
35
+ end
36
+ end
37
+ end
38
+
39
+ def migration_for_drop
40
+ Class.new(migration_class) do
41
+ def change
42
+ drop_view :greetings, revert_to_version: 1
43
+ end
44
+ end
45
+ end
46
+
47
+ def migration_for_update
48
+ Class.new(migration_class) do
49
+ def change
50
+ update_view :greetings, version: 2, revert_to_version: 1
51
+ end
52
+ end
53
+ end
54
+
55
+ def migration_class
56
+ if Rails::VERSION::MAJOR >= 5
57
+ ::ActiveRecord::Migration[5.0]
58
+ else
59
+ ::ActiveRecord::Migration
60
+ end
61
+ end
62
+
63
+ def run_migration(migration, directions)
64
+ silence_stream(STDOUT) do
65
+ Array.wrap(directions).each do |direction|
66
+ migration.migrate(direction)
67
+ end
68
+ end
69
+ end
70
+
71
+ def execute(sql)
72
+ ActiveRecord::Base.connection.execute(sql)
73
+ end
74
+ end
@@ -0,0 +1,79 @@
1
+ require "spec_helper"
2
+
3
+ module Scenic
4
+ module Adapters
5
+ describe Postgres::Connection do
6
+ describe "supports_materialized_views?" do
7
+ context "supports_materialized_views? was defined on connection" do
8
+ it "uses the previously defined version" do
9
+ base_response = double("response from base connection")
10
+ base_connection = double(
11
+ "Connection",
12
+ supports_materialized_views?: base_response,
13
+ )
14
+
15
+ connection = Postgres::Connection.new(base_connection)
16
+
17
+ expect(connection.supports_materialized_views?).to be base_response
18
+ end
19
+ end
20
+
21
+ context "supports_materialized_views? is not already defined" do
22
+ it "is true if postgres version is at least than 9.3.0" do
23
+ base_connection = double("Connection", postgresql_version: 90300)
24
+
25
+ connection = Postgres::Connection.new(base_connection)
26
+
27
+ expect(connection.supports_materialized_views?).to be true
28
+ end
29
+
30
+ it "is false if postgres version is less than 9.3.0" do
31
+ base_connection = double("Connection", postgresql_version: 90299)
32
+
33
+ connection = Postgres::Connection.new(base_connection)
34
+
35
+ expect(connection.supports_materialized_views?).to be false
36
+ end
37
+ end
38
+ end
39
+
40
+ describe "#postgresql_version" do
41
+ it "uses the public method on the provided connection if defined" do
42
+ base_connection = Class.new do
43
+ def postgresql_version
44
+ 123
45
+ end
46
+ end
47
+
48
+ connection = Postgres::Connection.new(base_connection.new)
49
+
50
+ expect(connection.postgresql_version).to eq 123
51
+ end
52
+
53
+ it "uses the protected method if the underlying method is not public" do
54
+ base_connection = Class.new do
55
+ protected
56
+
57
+ def postgresql_version
58
+ 123
59
+ end
60
+ end
61
+
62
+ connection = Postgres::Connection.new(base_connection.new)
63
+
64
+ expect(connection.postgresql_version).to eq 123
65
+ end
66
+ end
67
+
68
+ describe "#supports_concurrent_refresh" do
69
+ it "is true if postgres version is at least 9.4.0" do
70
+ base_connection = double("Connection", postgresql_version: 90400)
71
+
72
+ connection = Postgres::Connection.new(base_connection)
73
+
74
+ expect(connection.supports_concurrent_refreshes?).to be true
75
+ end
76
+ end
77
+ end
78
+ end
79
+ end
@@ -0,0 +1,82 @@
1
+ require "spec_helper"
2
+
3
+ module Scenic
4
+ module Adapters
5
+ describe Postgres::RefreshDependencies, :db do
6
+ context "view has dependencies" do
7
+ let(:adapter) { Postgres.new }
8
+
9
+ before do
10
+ adapter.create_materialized_view(
11
+ "first",
12
+ "SELECT text 'hi' AS greeting",
13
+ )
14
+ adapter.create_materialized_view(
15
+ "second",
16
+ "SELECT * FROM first",
17
+ )
18
+ adapter.create_materialized_view(
19
+ "third",
20
+ "SELECT * FROM first UNION SELECT * FROM second",
21
+ )
22
+ adapter.create_materialized_view(
23
+ "fourth_1",
24
+ "SELECT * FROM third",
25
+ )
26
+ adapter.create_materialized_view(
27
+ "x_fourth",
28
+ "SELECT * FROM fourth_1",
29
+ )
30
+ adapter.create_materialized_view(
31
+ "fourth",
32
+ "SELECT * FROM fourth_1 UNION SELECT * FROM x_fourth",
33
+ )
34
+
35
+ expect(adapter).to receive(:refresh_materialized_view)
36
+ .with("public.first", concurrently: true).ordered
37
+ expect(adapter).to receive(:refresh_materialized_view)
38
+ .with("public.second", concurrently: true).ordered
39
+ expect(adapter).to receive(:refresh_materialized_view)
40
+ .with("public.third", concurrently: true).ordered
41
+ expect(adapter).to receive(:refresh_materialized_view)
42
+ .with("public.fourth_1", concurrently: true).ordered
43
+ expect(adapter).to receive(:refresh_materialized_view)
44
+ .with("public.x_fourth", concurrently: true).ordered
45
+ end
46
+
47
+ it "refreshes in the right order when called without namespace" do
48
+ described_class.call(
49
+ :fourth,
50
+ adapter,
51
+ ActiveRecord::Base.connection,
52
+ concurrently: true,
53
+ )
54
+ end
55
+
56
+ it "refreshes in the right order when called with namespace" do
57
+ described_class.call(
58
+ "public.fourth",
59
+ adapter,
60
+ ActiveRecord::Base.connection,
61
+ concurrently: true,
62
+ )
63
+ end
64
+ end
65
+
66
+ context "view has no dependencies" do
67
+ it "does not raise an error" do
68
+ adapter = Postgres.new
69
+
70
+ adapter.create_materialized_view(
71
+ "first",
72
+ "SELECT text 'hi' AS greeting",
73
+ )
74
+
75
+ expect {
76
+ described_class.call(:first, adapter, ActiveRecord::Base.connection)
77
+ }.not_to raise_error
78
+ end
79
+ end
80
+ end
81
+ end
82
+ end
@@ -0,0 +1,37 @@
1
+ require "spec_helper"
2
+
3
+ module Scenic
4
+ module Adapters
5
+ describe Postgres::Views, :db do
6
+ it "returns scenic view objects for plain old views" do
7
+ connection = ActiveRecord::Base.connection
8
+ connection.execute <<-SQL
9
+ CREATE VIEW children AS SELECT text 'Elliot' AS name
10
+ SQL
11
+
12
+ views = Postgres::Views.new(connection).all
13
+ first = views.first
14
+
15
+ expect(views.size).to eq 1
16
+ expect(first.name).to eq "children"
17
+ expect(first.materialized).to be false
18
+ expect(first.definition).to eq "SELECT 'Elliot'::text AS name;"
19
+ end
20
+
21
+ it "returns scenic view objects for materialized views" do
22
+ connection = ActiveRecord::Base.connection
23
+ connection.execute <<-SQL
24
+ CREATE MATERIALIZED VIEW children AS SELECT text 'Owen' AS name
25
+ SQL
26
+
27
+ views = Postgres::Views.new(connection).all
28
+ first = views.first
29
+
30
+ expect(views.size).to eq 1
31
+ expect(first.name).to eq "children"
32
+ expect(first.materialized).to be true
33
+ expect(first.definition).to eq "SELECT 'Owen'::text AS name;"
34
+ end
35
+ end
36
+ end
37
+ end
@@ -0,0 +1,209 @@
1
+ require "spec_helper"
2
+
3
+ module Scenic
4
+ module Adapters
5
+ describe Postgres, :db do
6
+ describe "#create_view" do
7
+ it "successfully creates a view" do
8
+ adapter = Postgres.new
9
+
10
+ adapter.create_view("greetings", "SELECT text 'hi' AS greeting")
11
+
12
+ expect(adapter.views.map(&:name)).to include("greetings")
13
+ end
14
+ end
15
+
16
+ describe "#create_materialized_view" do
17
+ it "successfully creates a materialized view" do
18
+ adapter = Postgres.new
19
+
20
+ adapter.create_materialized_view(
21
+ "greetings",
22
+ "SELECT text 'hi' AS greeting",
23
+ )
24
+
25
+ view = adapter.views.first
26
+ expect(view.name).to eq("greetings")
27
+ expect(view.materialized).to eq true
28
+ end
29
+
30
+ it "handles semicolon in definition when using `with no data`" do
31
+ adapter = Postgres.new
32
+
33
+ adapter.create_materialized_view(
34
+ "greetings",
35
+ "SELECT text 'hi' AS greeting; \n",
36
+ no_data: true,
37
+ )
38
+
39
+ view = adapter.views.first
40
+ expect(view.name).to eq("greetings")
41
+ expect(view.materialized).to eq true
42
+ end
43
+
44
+ it "raises an exception if the version of PostgreSQL is too old" do
45
+ connection = double("Connection", supports_materialized_views?: false)
46
+ connectable = double("Connectable", connection: connection)
47
+ adapter = Postgres.new(connectable)
48
+ err = Scenic::Adapters::Postgres::MaterializedViewsNotSupportedError
49
+
50
+ expect { adapter.create_materialized_view("greetings", "select 1") }
51
+ .to raise_error err
52
+ end
53
+ end
54
+
55
+ describe "#replace_view" do
56
+ it "successfully replaces a view" do
57
+ adapter = Postgres.new
58
+
59
+ adapter.create_view("greetings", "SELECT text 'hi' AS greeting")
60
+
61
+ view = adapter.views.first.definition
62
+ expect(view).to eql "SELECT 'hi'::text AS greeting;"
63
+
64
+ adapter.replace_view("greetings", "SELECT text 'hello' AS greeting")
65
+
66
+ view = adapter.views.first.definition
67
+ expect(view).to eql "SELECT 'hello'::text AS greeting;"
68
+ end
69
+ end
70
+
71
+ describe "#drop_view" do
72
+ it "successfully drops a view" do
73
+ adapter = Postgres.new
74
+
75
+ adapter.create_view("greetings", "SELECT text 'hi' AS greeting")
76
+ adapter.drop_view("greetings")
77
+
78
+ expect(adapter.views.map(&:name)).not_to include("greetings")
79
+ end
80
+ end
81
+
82
+ describe "#drop_materialized_view" do
83
+ it "successfully drops a materialized view" do
84
+ adapter = Postgres.new
85
+
86
+ adapter.create_materialized_view(
87
+ "greetings",
88
+ "SELECT text 'hi' AS greeting",
89
+ )
90
+ adapter.drop_materialized_view("greetings")
91
+
92
+ expect(adapter.views.map(&:name)).not_to include("greetings")
93
+ end
94
+
95
+ it "raises an exception if the version of PostgreSQL is too old" do
96
+ connection = double("Connection", supports_materialized_views?: false)
97
+ connectable = double("Connectable", connection: connection)
98
+ adapter = Postgres.new(connectable)
99
+ err = Scenic::Adapters::Postgres::MaterializedViewsNotSupportedError
100
+
101
+ expect { adapter.drop_materialized_view("greetings") }
102
+ .to raise_error err
103
+ end
104
+ end
105
+
106
+ describe "#refresh_materialized_view" do
107
+ it "raises an exception if the version of PostgreSQL is too old" do
108
+ connection = double("Connection", supports_materialized_views?: false)
109
+ connectable = double("Connectable", connection: connection)
110
+ adapter = Postgres.new(connectable)
111
+ err = Scenic::Adapters::Postgres::MaterializedViewsNotSupportedError
112
+
113
+ expect { adapter.refresh_materialized_view(:tests) }
114
+ .to raise_error err
115
+ end
116
+
117
+ it "can refresh the views dependencies first" do
118
+ connection = double("Connection").as_null_object
119
+ connectable = double("Connectable", connection: connection)
120
+ adapter = Postgres.new(connectable)
121
+ expect(Scenic::Adapters::Postgres::RefreshDependencies)
122
+ .to receive(:call)
123
+ .with(:tests, adapter, connection, concurrently: true)
124
+
125
+ adapter.refresh_materialized_view(
126
+ :tests,
127
+ cascade: true,
128
+ concurrently: true,
129
+ )
130
+ end
131
+
132
+ context "refreshing concurrently" do
133
+ it "raises descriptive error if concurrent refresh is not possible" do
134
+ adapter = Postgres.new
135
+ adapter.create_materialized_view(:tests, "SELECT text 'hi' as text")
136
+
137
+ expect {
138
+ adapter.refresh_materialized_view(:tests, concurrently: true)
139
+ }.to raise_error(/Create a unique index with no WHERE clause/)
140
+ end
141
+
142
+ it "raises an exception if the version of PostgreSQL is too old" do
143
+ connection = double("Connection", postgresql_version: 90300)
144
+ connectable = double("Connectable", connection: connection)
145
+ adapter = Postgres.new(connectable)
146
+ e = Scenic::Adapters::Postgres::ConcurrentRefreshesNotSupportedError
147
+
148
+ expect {
149
+ adapter.refresh_materialized_view(:tests, concurrently: true)
150
+ }.to raise_error e
151
+ end
152
+ end
153
+ end
154
+
155
+ describe "#views" do
156
+ it "returns the views defined on this connection" do
157
+ adapter = Postgres.new
158
+
159
+ ActiveRecord::Base.connection.execute <<-SQL
160
+ CREATE VIEW parents AS SELECT text 'Joe' AS name
161
+ SQL
162
+
163
+ ActiveRecord::Base.connection.execute <<-SQL
164
+ CREATE VIEW children AS SELECT text 'Owen' AS name
165
+ SQL
166
+
167
+ ActiveRecord::Base.connection.execute <<-SQL
168
+ CREATE MATERIALIZED VIEW people AS
169
+ SELECT name FROM parents UNION SELECT name FROM children
170
+ SQL
171
+
172
+ ActiveRecord::Base.connection.execute <<-SQL
173
+ CREATE VIEW people_with_names AS
174
+ SELECT name FROM people
175
+ WHERE name IS NOT NULL
176
+ SQL
177
+
178
+ expect(adapter.views.map(&:name)).to eq [
179
+ "parents",
180
+ "children",
181
+ "people",
182
+ "people_with_names",
183
+ ]
184
+ end
185
+
186
+ context "with views in non public schemas" do
187
+ it "returns also the non public views" do
188
+ adapter = Postgres.new
189
+
190
+ ActiveRecord::Base.connection.execute <<-SQL
191
+ CREATE VIEW parents AS SELECT text 'Joe' AS name
192
+ SQL
193
+
194
+ ActiveRecord::Base.connection.execute <<-SQL
195
+ CREATE SCHEMA scenic;
196
+ CREATE VIEW scenic.parents AS SELECT text 'Maarten' AS name;
197
+ SET search_path TO scenic, public;
198
+ SQL
199
+
200
+ expect(adapter.views.map(&:name)).to eq [
201
+ "parents",
202
+ "scenic.parents",
203
+ ]
204
+ end
205
+ end
206
+ end
207
+ end
208
+ end
209
+ end