scenic 1.0.0 → 1.1.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 (42) hide show
  1. checksums.yaml +4 -4
  2. data/.gitignore +1 -0
  3. data/.travis.yml +14 -4
  4. data/.yardopts +0 -1
  5. data/Appraisals +25 -0
  6. data/CONTRIBUTING.md +5 -4
  7. data/LICENSE.txt +1 -1
  8. data/NEWS.md +20 -0
  9. data/README.md +68 -28
  10. data/bin/appraisal +16 -0
  11. data/bin/setup +5 -0
  12. data/bin/yard +16 -0
  13. data/gemfiles/rails40.gemfile +8 -0
  14. data/gemfiles/rails41.gemfile +8 -0
  15. data/gemfiles/rails42.gemfile +8 -0
  16. data/gemfiles/rails50.gemfile +14 -0
  17. data/lib/generators/scenic/generators.rb +1 -0
  18. data/lib/generators/scenic/model/templates/model.erb +1 -1
  19. data/lib/scenic.rb +1 -0
  20. data/lib/scenic/adapters/postgres.rb +140 -33
  21. data/lib/scenic/adapters/postgres/connection.rb +57 -0
  22. data/lib/scenic/adapters/postgres/errors.rb +26 -0
  23. data/lib/scenic/adapters/postgres/index_reapplication.rb +71 -0
  24. data/lib/scenic/adapters/postgres/indexes.rb +53 -0
  25. data/lib/scenic/adapters/postgres/views.rb +51 -0
  26. data/lib/scenic/configuration.rb +2 -2
  27. data/lib/scenic/index.rb +36 -0
  28. data/lib/scenic/schema_dumper.rb +1 -1
  29. data/lib/scenic/statements.rb +7 -13
  30. data/lib/scenic/version.rb +1 -1
  31. data/lib/scenic/view.rb +0 -3
  32. data/scenic.gemspec +4 -1
  33. data/spec/dummy/config/application.rb +3 -0
  34. data/spec/scenic/adapters/postgres/connection_spec.rb +79 -0
  35. data/spec/scenic/adapters/postgres/views_spec.rb +37 -0
  36. data/spec/scenic/adapters/postgres_spec.rb +84 -26
  37. data/spec/scenic/statements_spec.rb +14 -15
  38. data/spec/smoke +16 -3
  39. data/spec/spec_helper.rb +4 -0
  40. metadata +64 -8
  41. data/spec/dummy/config/environments/development.rb +0 -6
  42. data/spec/dummy/config/environments/test.rb +0 -5
@@ -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
@@ -3,7 +3,7 @@ require "spec_helper"
3
3
  module Scenic
4
4
  module Adapters
5
5
  describe Postgres, :db do
6
- describe "create_view" do
6
+ describe "#create_view" do
7
7
  it "successfully creates a view" do
8
8
  adapter = Postgres.new
9
9
 
@@ -13,7 +13,7 @@ module Scenic
13
13
  end
14
14
  end
15
15
 
16
- describe "create_materialized_view" do
16
+ describe "#create_materialized_view" do
17
17
  it "successfully creates a materialized view" do
18
18
  adapter = Postgres.new
19
19
 
@@ -26,9 +26,18 @@ module Scenic
26
26
  expect(view.name).to eq("greetings")
27
27
  expect(view.materialized).to eq true
28
28
  end
29
+
30
+ it "raises an exception if the version of PostgreSQL is too old" do
31
+ connection = double("Connection", supports_materialized_views?: false)
32
+ adapter = Postgres.new(connection)
33
+ err = Scenic::Adapters::Postgres::MaterializedViewsNotSupportedError
34
+
35
+ expect { adapter.create_materialized_view("greetings", "select 1") }
36
+ .to raise_error err
37
+ end
29
38
  end
30
39
 
31
- describe "drop_view" do
40
+ describe "#drop_view" do
32
41
  it "successfully drops a view" do
33
42
  adapter = Postgres.new
34
43
 
@@ -39,7 +48,7 @@ module Scenic
39
48
  end
40
49
  end
41
50
 
42
- describe "drop_materialized_view" do
51
+ describe "#drop_materialized_view" do
43
52
  it "successfully drops a materialized view" do
44
53
  adapter = Postgres.new
45
54
 
@@ -51,30 +60,79 @@ module Scenic
51
60
 
52
61
  expect(adapter.views.map(&:name)).not_to include("greetings")
53
62
  end
63
+
64
+ it "raises an exception if the version of PostgreSQL is too old" do
65
+ connection = double("Connection", supports_materialized_views?: false)
66
+ adapter = Postgres.new(connection)
67
+ err = Scenic::Adapters::Postgres::MaterializedViewsNotSupportedError
68
+
69
+ expect { adapter.drop_materialized_view("greetings") }
70
+ .to raise_error err
71
+ end
72
+ end
73
+
74
+ describe "#refresh_materialized_view" do
75
+ it "raises an exception if the version of PostgreSQL is too old" do
76
+ connection = double("Connection", supports_materialized_views?: false)
77
+ adapter = Postgres.new(connection)
78
+ err = Scenic::Adapters::Postgres::MaterializedViewsNotSupportedError
79
+
80
+ expect { adapter.refresh_materialized_view(:tests) }
81
+ .to raise_error err
82
+ end
83
+
84
+ context "refreshing concurrently" do
85
+ it "raises descriptive error if concurrent refresh is not possible" do
86
+ adapter = Postgres.new
87
+ adapter.create_materialized_view(:tests, "SELECT text 'hi' as text")
88
+
89
+ expect {
90
+ adapter.refresh_materialized_view(:tests, concurrently: true)
91
+ }.to raise_error(/Create a unique index with no WHERE clause/)
92
+ end
93
+
94
+ it "raises an exception if the version of PostgreSQL is too old" do
95
+ connection = double("Connection", postgresql_version: 90300)
96
+ adapter = Postgres.new(connection)
97
+ e = Scenic::Adapters::Postgres::ConcurrentRefreshesNotSupportedError
98
+
99
+ expect {
100
+ adapter.refresh_materialized_view(:tests, concurrently: true)
101
+ }.to raise_error e
102
+ end
103
+ end
54
104
  end
55
105
 
56
- it "finds views and builds Scenic::View objects" do
57
- adapter = Postgres.new
58
-
59
- ActiveRecord::Base.connection.execute(
60
- "CREATE VIEW greetings AS SELECT text 'hi' AS greeting"
61
- )
62
- ActiveRecord::Base.connection.execute(
63
- "CREATE MATERIALIZED VIEW farewells AS SELECT text 'bye' AS farewell"
64
- )
65
-
66
- expect(adapter.views).to eq([
67
- Scenic::View.new(
68
- name: "farewells",
69
- definition: "SELECT 'bye'::text AS farewell;",
70
- materialized: true,
71
- ),
72
- Scenic::View.new(
73
- name: "greetings",
74
- definition: "SELECT 'hi'::text AS greeting;",
75
- materialized: false,
76
- ),
77
- ])
106
+ describe "#views" do
107
+ it "returns the views defined on this connection" do
108
+ adapter = Postgres.new
109
+
110
+ ActiveRecord::Base.connection.execute <<-SQL
111
+ CREATE VIEW parents AS SELECT text 'Joe' AS name
112
+ SQL
113
+
114
+ ActiveRecord::Base.connection.execute <<-SQL
115
+ CREATE VIEW children AS SELECT text 'Owen' AS name
116
+ SQL
117
+
118
+ ActiveRecord::Base.connection.execute <<-SQL
119
+ CREATE MATERIALIZED VIEW people AS
120
+ SELECT name FROM parents UNION SELECT name FROM children
121
+ SQL
122
+
123
+ ActiveRecord::Base.connection.execute <<-SQL
124
+ CREATE VIEW people_with_names AS
125
+ SELECT name FROM people
126
+ WHERE name IS NOT NULL
127
+ SQL
128
+
129
+ expect(adapter.views.map(&:name)).to eq [
130
+ "parents",
131
+ "children",
132
+ "people",
133
+ "people_with_names",
134
+ ]
135
+ end
78
136
  end
79
137
  end
80
138
  end
@@ -65,7 +65,7 @@ module Scenic
65
65
  end
66
66
 
67
67
  describe "update_view" do
68
- it "drops the existing version and creates the new" do
68
+ it "updates the view in the database" do
69
69
  definition = instance_double("Definition", to_sql: "definition")
70
70
  allow(Definition).to receive(:new)
71
71
  .with(:name, 3)
@@ -73,8 +73,19 @@ module Scenic
73
73
 
74
74
  connection.update_view(:name, version: 3)
75
75
 
76
- expect(Scenic.database).to have_received(:drop_view).with(:name)
77
- expect(Scenic.database).to have_received(:create_view)
76
+ expect(Scenic.database).to have_received(:update_view)
77
+ .with(:name, definition.to_sql)
78
+ end
79
+
80
+ it "updates the materialized view in the database" do
81
+ definition = instance_double("Definition", to_sql: "definition")
82
+ allow(Definition).to receive(:new)
83
+ .with(:name, 3)
84
+ .and_return(definition)
85
+
86
+ connection.update_view(:name, version: 3, materialized: true)
87
+
88
+ expect(Scenic.database).to have_received(:update_materialized_view)
78
89
  .with(:name, definition.to_sql)
79
90
  end
80
91
 
@@ -84,18 +95,6 @@ module Scenic
84
95
  end
85
96
  end
86
97
 
87
- describe "update_view :materialized" do
88
- it "raises an error because this is not supported" do
89
- definition = instance_double("Definition").as_null_object
90
- allow(Definition).to receive(:new).and_return(definition)
91
-
92
- expect { connection.update_view(:name, version: 3, materialized: true) }.
93
- to raise_error(/not supported/)
94
- expect(Scenic.database).not_to have_received(:drop_materialized_view)
95
- expect(Scenic.database).not_to have_received(:create_materialized_view)
96
- end
97
- end
98
-
99
98
  def connection
100
99
  Class.new { extend Statements }
101
100
  end
data/spec/smoke CHANGED
@@ -21,7 +21,7 @@ trap teardown EXIT
21
21
  verifySearchResults() {
22
22
  echo "verify search results"
23
23
  local expectedResult=$1
24
- local actualResult=$(rails runner "puts Search.first.results")
24
+ local actualResult=$(rails runner "puts Search.take.results")
25
25
  [[ "$actualResult" == "$expectedResult" ]] || exit 1
26
26
  echo "[success]"
27
27
  }
@@ -32,7 +32,7 @@ writeToFileAndMigrateAndVerifySearchResults() {
32
32
  local expectedResult=$2
33
33
  local filePath=db/views/searches_v$version\.sql
34
34
  cp /dev/null $filePath
35
- echo "SELECT '$expectedResult'::text AS results" >> $filePath
35
+ echo "SELECT '$expectedResult'::text AS results, 1 AS user_id" >> $filePath
36
36
  rake db:migrate
37
37
  echo "[success]"
38
38
  verifySearchResults $expectedResult
@@ -97,11 +97,24 @@ main() {
97
97
  rails runner "Search.refresh" || exit 1
98
98
  echo "[success]"
99
99
 
100
+ echo "add indexes to materialized view"
101
+ rails runner "ActiveRecord::Base.connection.execute 'CREATE INDEX searches_test_1 ON searches USING btree (results);'" || exit 1
102
+ rails runner "ActiveRecord::Base.connection.execute 'CREATE INDEX searches_test_2 ON searches USING btree (user_id);'" || exit 1
103
+ echo "[success]"
104
+
105
+ echo "update materialized view"
106
+ rails generate scenic:view search --materialized
107
+ echo "SELECT 'test'::text AS results" > db/views/searches_v02.sql
108
+ rake db:migrate
109
+ verifySearchResults 'test'
110
+
100
111
  echo "rake db:rollback"
101
112
  rake db:rollback
113
+ rake db:rollback
102
114
  echo "[success]"
103
115
 
104
- echo "rails destroy scenic:view search --materialized"
116
+ echo "rails destroy scenic:model search --materialized"
117
+ rails destroy scenic:view search --materialized
105
118
  rails destroy scenic:model search --materialized
106
119
  [[ ! -f app/models/search.rb ]] || exit 1
107
120
  [[ ! -f db/views/searches_v01.sql ]] || exit 1
@@ -15,4 +15,8 @@ RSpec.configure do |config|
15
15
  example.run
16
16
  DatabaseCleaner.clean
17
17
  end
18
+
19
+ if defined? ActiveSupport::Testing::Stream
20
+ config.include ActiveSupport::Testing::Stream
21
+ end
18
22
  end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: scenic
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.0.0
4
+ version: 1.1.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Derek Prior
@@ -9,8 +9,22 @@ authors:
9
9
  autorequire:
10
10
  bindir: bin
11
11
  cert_chain: []
12
- date: 2015-12-03 00:00:00.000000000 Z
12
+ date: 2016-01-20 00:00:00.000000000 Z
13
13
  dependencies:
14
+ - !ruby/object:Gem::Dependency
15
+ name: appraisal
16
+ requirement: !ruby/object:Gem::Requirement
17
+ requirements:
18
+ - - ">="
19
+ - !ruby/object:Gem::Version
20
+ version: '0'
21
+ type: :development
22
+ prerelease: false
23
+ version_requirements: !ruby/object:Gem::Requirement
24
+ requirements:
25
+ - - ">="
26
+ - !ruby/object:Gem::Version
27
+ version: '0'
14
28
  - !ruby/object:Gem::Dependency
15
29
  name: bundler
16
30
  requirement: !ruby/object:Gem::Requirement
@@ -109,6 +123,34 @@ dependencies:
109
123
  - - ">="
110
124
  - !ruby/object:Gem::Version
111
125
  version: 1.1.3
126
+ - !ruby/object:Gem::Dependency
127
+ name: yard
128
+ requirement: !ruby/object:Gem::Requirement
129
+ requirements:
130
+ - - ">="
131
+ - !ruby/object:Gem::Version
132
+ version: '0'
133
+ type: :development
134
+ prerelease: false
135
+ version_requirements: !ruby/object:Gem::Requirement
136
+ requirements:
137
+ - - ">="
138
+ - !ruby/object:Gem::Version
139
+ version: '0'
140
+ - !ruby/object:Gem::Dependency
141
+ name: redcarpet
142
+ requirement: !ruby/object:Gem::Requirement
143
+ requirements:
144
+ - - ">="
145
+ - !ruby/object:Gem::Version
146
+ version: '0'
147
+ type: :development
148
+ prerelease: false
149
+ version_requirements: !ruby/object:Gem::Requirement
150
+ requirements:
151
+ - - ">="
152
+ - !ruby/object:Gem::Version
153
+ version: '0'
112
154
  - !ruby/object:Gem::Dependency
113
155
  name: activerecord
114
156
  requirement: !ruby/object:Gem::Requirement
@@ -151,13 +193,20 @@ files:
151
193
  - ".hound.yml"
152
194
  - ".travis.yml"
153
195
  - ".yardopts"
196
+ - Appraisals
154
197
  - CONTRIBUTING.md
155
198
  - Gemfile
156
199
  - LICENSE.txt
157
200
  - NEWS.md
158
201
  - README.md
159
202
  - Rakefile
203
+ - bin/appraisal
160
204
  - bin/setup
205
+ - bin/yard
206
+ - gemfiles/rails40.gemfile
207
+ - gemfiles/rails41.gemfile
208
+ - gemfiles/rails42.gemfile
209
+ - gemfiles/rails50.gemfile
161
210
  - lib/generators/scenic/generators.rb
162
211
  - lib/generators/scenic/materializable.rb
163
212
  - lib/generators/scenic/model/USAGE
@@ -169,10 +218,16 @@ files:
169
218
  - lib/generators/scenic/view/view_generator.rb
170
219
  - lib/scenic.rb
171
220
  - lib/scenic/adapters/postgres.rb
221
+ - lib/scenic/adapters/postgres/connection.rb
222
+ - lib/scenic/adapters/postgres/errors.rb
223
+ - lib/scenic/adapters/postgres/index_reapplication.rb
224
+ - lib/scenic/adapters/postgres/indexes.rb
225
+ - lib/scenic/adapters/postgres/views.rb
172
226
  - lib/scenic/command_recorder.rb
173
227
  - lib/scenic/command_recorder/statement_arguments.rb
174
228
  - lib/scenic/configuration.rb
175
229
  - lib/scenic/definition.rb
230
+ - lib/scenic/index.rb
176
231
  - lib/scenic/railtie.rb
177
232
  - lib/scenic/schema_dumper.rb
178
233
  - lib/scenic/statements.rb
@@ -189,13 +244,13 @@ files:
189
244
  - spec/dummy/config/boot.rb
190
245
  - spec/dummy/config/database.yml
191
246
  - spec/dummy/config/environment.rb
192
- - spec/dummy/config/environments/development.rb
193
- - spec/dummy/config/environments/test.rb
194
247
  - spec/dummy/db/migrate/.keep
195
248
  - spec/dummy/db/views/.keep
196
249
  - spec/generators/scenic/model/model_generator_spec.rb
197
250
  - spec/generators/scenic/view/view_generator_spec.rb
198
251
  - spec/integration/revert_spec.rb
252
+ - spec/scenic/adapters/postgres/connection_spec.rb
253
+ - spec/scenic/adapters/postgres/views_spec.rb
199
254
  - spec/scenic/adapters/postgres_spec.rb
200
255
  - spec/scenic/command_recorder/statement_arguments_spec.rb
201
256
  - spec/scenic/command_recorder_spec.rb
@@ -219,7 +274,7 @@ required_ruby_version: !ruby/object:Gem::Requirement
219
274
  requirements:
220
275
  - - "~>"
221
276
  - !ruby/object:Gem::Version
222
- version: '2.0'
277
+ version: '2.1'
223
278
  required_rubygems_version: !ruby/object:Gem::Requirement
224
279
  requirements:
225
280
  - - ">="
@@ -227,7 +282,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
227
282
  version: '0'
228
283
  requirements: []
229
284
  rubyforge_project:
230
- rubygems_version: 2.4.8
285
+ rubygems_version: 2.5.1
231
286
  signing_key:
232
287
  specification_version: 4
233
288
  summary: Support for database views in Rails migrations
@@ -242,13 +297,13 @@ test_files:
242
297
  - spec/dummy/config/boot.rb
243
298
  - spec/dummy/config/database.yml
244
299
  - spec/dummy/config/environment.rb
245
- - spec/dummy/config/environments/development.rb
246
- - spec/dummy/config/environments/test.rb
247
300
  - spec/dummy/db/migrate/.keep
248
301
  - spec/dummy/db/views/.keep
249
302
  - spec/generators/scenic/model/model_generator_spec.rb
250
303
  - spec/generators/scenic/view/view_generator_spec.rb
251
304
  - spec/integration/revert_spec.rb
305
+ - spec/scenic/adapters/postgres/connection_spec.rb
306
+ - spec/scenic/adapters/postgres/views_spec.rb
252
307
  - spec/scenic/adapters/postgres_spec.rb
253
308
  - spec/scenic/command_recorder/statement_arguments_spec.rb
254
309
  - spec/scenic/command_recorder_spec.rb
@@ -260,3 +315,4 @@ test_files:
260
315
  - spec/spec_helper.rb
261
316
  - spec/support/generator_spec_setup.rb
262
317
  - spec/support/view_definition_helpers.rb
318
+ has_rdoc:
@@ -1,6 +0,0 @@
1
- Rails.application.configure do
2
- config.cache_classes = false
3
-
4
- # Do not eager load code on boot.
5
- config.eager_load = false
6
- end