activerecord_views 0.0.19 → 0.1.2
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.
- checksums.yaml +5 -5
- data/.gitignore +2 -0
- data/.tool-versions +1 -0
- data/Appraisals +10 -0
- data/README.markdown +28 -14
- data/activerecord_views.gemspec +3 -1
- data/gemfiles/rails4_2.gemfile +1 -0
- data/gemfiles/rails5_0.gemfile +1 -0
- data/gemfiles/rails6_0.gemfile +7 -0
- data/gemfiles/rails6_1.gemfile +7 -0
- data/lib/active_record_views/checksum_cache.rb +9 -5
- data/lib/active_record_views/extension.rb +10 -13
- data/lib/active_record_views/railtie.rb +13 -4
- data/lib/active_record_views/registered_view.rb +7 -2
- data/lib/active_record_views/version.rb +1 -1
- data/lib/active_record_views.rb +45 -14
- data/lib/tasks/active_record_views.rake +70 -16
- data/spec/active_record_views_checksum_cache_spec.rb +10 -6
- data/spec/active_record_views_extension_spec.rb +149 -68
- data/spec/active_record_views_spec.rb +59 -0
- data/spec/internal/Rakefile +5 -0
- data/spec/internal/app/models/erb_test_model.rb +4 -0
- data/spec/internal/app/models/erb_test_model.sql.erb +1 -1
- data/spec/internal/config/database.yml +1 -0
- data/spec/internal/config/routes.rb +3 -0
- data/spec/spec_helper.rb +47 -13
- data/spec/support/silence_warnings.rb +23 -0
- data/spec/tasks_spec.rb +53 -11
- metadata +44 -12
- data/spec/internal/app/models/missing_file_test_model.rb +0 -5
@@ -2,6 +2,19 @@ require 'spec_helper'
|
|
2
2
|
|
3
3
|
describe ActiveRecordViews::Extension do
|
4
4
|
describe '.is_view' do
|
5
|
+
def registered_model_class_names
|
6
|
+
ActiveRecordViews.registered_views.map(&:model_class_name)
|
7
|
+
end
|
8
|
+
|
9
|
+
def view_exists?(name)
|
10
|
+
connection = ActiveRecord::Base.connection
|
11
|
+
if connection.respond_to?(:view_exists?)
|
12
|
+
connection.view_exists?(name)
|
13
|
+
else
|
14
|
+
connection.table_exists?(name)
|
15
|
+
end
|
16
|
+
end
|
17
|
+
|
5
18
|
it 'creates database views from heredocs' do
|
6
19
|
expect(ActiveRecordViews).to receive(:create_view).once.and_call_original
|
7
20
|
expect(HeredocTestModel.first.name).to eq 'Here document'
|
@@ -19,43 +32,50 @@ describe ActiveRecordViews::Extension do
|
|
19
32
|
|
20
33
|
it 'creates database views from external ERB files' do
|
21
34
|
expect(ActiveRecordViews).to receive(:create_view).once.and_call_original
|
22
|
-
expect(ErbTestModel.first.name).to eq 'ERB file'
|
35
|
+
expect(ErbTestModel.first.name).to eq 'ERB method file'
|
23
36
|
end
|
24
37
|
|
25
38
|
it 'errors if external SQL file is missing' do
|
26
39
|
expect {
|
27
|
-
MissingFileTestModel
|
40
|
+
class MissingFileTestModel < ActiveRecord::Base
|
41
|
+
is_view
|
42
|
+
end
|
28
43
|
}.to raise_error RuntimeError, /could not find missing_file_test_model.sql/
|
29
44
|
end
|
30
45
|
|
31
46
|
it 'reloads the database view when external SQL file is modified' do
|
32
|
-
|
33
|
-
|
34
|
-
anything,
|
35
|
-
'modified_file_test_models',
|
36
|
-
'ModifiedFileTestModel',
|
37
|
-
sql,
|
38
|
-
{}
|
39
|
-
).once.ordered
|
40
|
-
end
|
41
|
-
|
42
|
-
with_temp_sql_dir do |temp_dir|
|
43
|
-
sql_file = File.join(temp_dir, 'modified_file_test_model.sql')
|
44
|
-
update_file sql_file, 'foo'
|
47
|
+
sql_file = File.join(TEST_TEMP_MODEL_DIR, 'modified_file_test_model.sql')
|
48
|
+
update_file sql_file, "SELECT 'foo'::text AS test"
|
45
49
|
|
50
|
+
expect {
|
51
|
+
expect(ActiveRecordViews).to receive(:create_view).once.and_call_original
|
46
52
|
class ModifiedFileTestModel < ActiveRecord::Base
|
47
53
|
is_view
|
48
54
|
end
|
55
|
+
}.to change { begin; ModifiedFileTestModel.take!.test; rescue NameError; end }.from(nil).to('foo')
|
56
|
+
.and change { registered_model_class_names.include?('ModifiedFileTestModel') }.from(false).to(true)
|
49
57
|
|
50
|
-
|
58
|
+
expect {
|
59
|
+
update_file sql_file, "SELECT 'bar'::text AS test, 42::integer AS test2"
|
60
|
+
}.to_not change { ModifiedFileTestModel.take!.test }
|
51
61
|
|
62
|
+
expect {
|
63
|
+
expect(ActiveRecordViews).to receive(:create_view).once.and_call_original
|
52
64
|
test_request
|
53
65
|
test_request # second request does not `create_view` again
|
66
|
+
}.to change { ModifiedFileTestModel.take!.test }.to('bar')
|
67
|
+
.and change { ModifiedFileTestModel.column_names }.from(%w[test]).to(%w[test test2])
|
54
68
|
|
55
|
-
|
69
|
+
expect {
|
70
|
+
update_file sql_file, "SELECT 'baz'::text AS test"
|
71
|
+
}.to_not change { ModifiedFileTestModel.take!.test }
|
56
72
|
|
73
|
+
expect {
|
74
|
+
expect(ActiveRecordViews).to receive(:create_view).once.and_call_original
|
57
75
|
test_request
|
58
|
-
|
76
|
+
}.to change { ModifiedFileTestModel.take!.test }.to('baz')
|
77
|
+
|
78
|
+
File.unlink sql_file
|
59
79
|
test_request # trigger cleanup
|
60
80
|
end
|
61
81
|
|
@@ -70,40 +90,53 @@ describe ActiveRecordViews::Extension do
|
|
70
90
|
).once.ordered
|
71
91
|
end
|
72
92
|
|
73
|
-
|
74
|
-
|
75
|
-
update_file sql_file, 'foo <%= 2*3*7 %>'
|
93
|
+
sql_file = File.join(TEST_TEMP_MODEL_DIR, 'modified_erb_file_test_model.sql.erb')
|
94
|
+
update_file sql_file, 'foo <%= test_erb_method %>'
|
76
95
|
|
77
|
-
|
78
|
-
|
96
|
+
class ModifiedErbFileTestModel < ActiveRecord::Base
|
97
|
+
def self.test_erb_method
|
98
|
+
2 * 3 * 7
|
79
99
|
end
|
80
100
|
|
81
|
-
|
82
|
-
test_request
|
101
|
+
is_view
|
83
102
|
end
|
103
|
+
|
104
|
+
update_file sql_file, 'bar <%= test_erb_method %>'
|
105
|
+
test_request
|
106
|
+
|
107
|
+
File.unlink sql_file
|
84
108
|
test_request # trigger cleanup
|
85
109
|
end
|
86
110
|
|
87
111
|
it 'drops the view if the external SQL file is deleted' do
|
88
|
-
|
89
|
-
|
90
|
-
File.write sql_file, "SELECT 1 AS id, 'delete test'::text AS name"
|
112
|
+
sql_file = File.join(TEST_TEMP_MODEL_DIR, 'deleted_file_test_model.sql')
|
113
|
+
File.write sql_file, "SELECT 1 AS id, 'delete test'::text AS name"
|
91
114
|
|
115
|
+
rb_file = 'spec/internal/app/models_temp/deleted_file_test_model.rb'
|
116
|
+
File.write rb_file, <<~RB
|
92
117
|
class DeletedFileTestModel < ActiveRecord::Base
|
93
118
|
is_view
|
94
119
|
end
|
120
|
+
RB
|
95
121
|
|
122
|
+
with_reloader do
|
96
123
|
expect(DeletedFileTestModel.first.name).to eq 'delete test'
|
124
|
+
end
|
97
125
|
|
98
|
-
|
126
|
+
File.unlink sql_file
|
127
|
+
File.unlink rb_file
|
99
128
|
|
100
|
-
|
129
|
+
expect(ActiveRecord::Base.connection).to receive(:execute).with(/\ADROP/).once.and_call_original
|
130
|
+
expect {
|
101
131
|
test_request
|
102
|
-
|
132
|
+
}.to change { registered_model_class_names.include?('DeletedFileTestModel') }.from(true).to(false)
|
133
|
+
.and change { view_exists?('deleted_file_test_models') }.from(true).to(false)
|
134
|
+
test_request # second request does not `drop_view` again
|
103
135
|
|
136
|
+
if Rails::VERSION::MAJOR >= 5
|
104
137
|
expect {
|
105
138
|
DeletedFileTestModel.first.name
|
106
|
-
}.to raise_error
|
139
|
+
}.to raise_error NameError, 'uninitialized constant DeletedFileTestModel'
|
107
140
|
end
|
108
141
|
end
|
109
142
|
|
@@ -161,37 +194,34 @@ describe ActiveRecordViews::Extension do
|
|
161
194
|
end
|
162
195
|
|
163
196
|
it 'creates/refreshes/drops materialized views' do
|
164
|
-
|
165
|
-
|
166
|
-
File.write sql_file, 'SELECT 123 AS id;'
|
197
|
+
sql_file = File.join(TEST_TEMP_MODEL_DIR, 'materialized_view_test_model.sql')
|
198
|
+
File.write sql_file, 'SELECT 123 AS id;'
|
167
199
|
|
168
|
-
|
169
|
-
|
170
|
-
|
200
|
+
class MaterializedViewTestModel < ActiveRecord::Base
|
201
|
+
is_view materialized: true
|
202
|
+
end
|
171
203
|
|
172
|
-
|
173
|
-
|
174
|
-
|
204
|
+
expect {
|
205
|
+
MaterializedViewTestModel.first!
|
206
|
+
}.to raise_error ActiveRecord::StatementInvalid, /materialized view "materialized_view_test_models" has not been populated/
|
175
207
|
|
176
|
-
|
177
|
-
|
208
|
+
expect(MaterializedViewTestModel.view_populated?).to eq false
|
209
|
+
expect(MaterializedViewTestModel.refreshed_at).to eq nil
|
178
210
|
|
179
|
-
|
211
|
+
MaterializedViewTestModel.refresh_view!
|
180
212
|
|
181
|
-
|
182
|
-
|
183
|
-
|
184
|
-
|
213
|
+
expect(MaterializedViewTestModel.view_populated?).to eq true
|
214
|
+
expect(MaterializedViewTestModel.refreshed_at).to be_a Time
|
215
|
+
expect(MaterializedViewTestModel.refreshed_at.zone).to eq 'UTC'
|
216
|
+
expect(MaterializedViewTestModel.refreshed_at).to be_within(1.second).of Time.now
|
185
217
|
|
186
|
-
|
218
|
+
expect(MaterializedViewTestModel.first!.id).to eq 123
|
187
219
|
|
188
|
-
|
189
|
-
test_request
|
220
|
+
File.unlink sql_file
|
190
221
|
|
191
|
-
|
192
|
-
|
193
|
-
|
194
|
-
end
|
222
|
+
expect {
|
223
|
+
test_request
|
224
|
+
}.to change { view_exists?('materialized_view_test_models') }.from(true).to(false)
|
195
225
|
end
|
196
226
|
|
197
227
|
it 'raises an error for `view_populated?` if view is not materialized' do
|
@@ -217,7 +247,7 @@ describe ActiveRecordViews::Extension do
|
|
217
247
|
is_view "SELECT * FROM #{EnsurePopulatedBar.table_name}", dependencies: [EnsurePopulatedBar]
|
218
248
|
end
|
219
249
|
|
220
|
-
expect(ActiveRecord::Base.connection).to receive(:execute).with(
|
250
|
+
expect(ActiveRecord::Base.connection).to receive(:execute).with('REFRESH MATERIALIZED VIEW "ensure_populated_foos";').once.and_call_original
|
221
251
|
allow(ActiveRecord::Base.connection).to receive(:execute).and_call_original
|
222
252
|
|
223
253
|
expect(EnsurePopulatedFoo.view_populated?).to eq false
|
@@ -227,6 +257,23 @@ describe ActiveRecordViews::Extension do
|
|
227
257
|
expect(EnsurePopulatedFoo.view_populated?).to eq true
|
228
258
|
end
|
229
259
|
|
260
|
+
it 'invalidates ActiveRecord query cache after populating' do
|
261
|
+
class EnsurePopulatedCache < ActiveRecord::Base
|
262
|
+
is_view 'SELECT 1 AS id;', materialized: true
|
263
|
+
end
|
264
|
+
|
265
|
+
expect(ActiveRecord::Base.connection).to receive(:execute).with('REFRESH MATERIALIZED VIEW "ensure_populated_caches";').once.and_call_original
|
266
|
+
allow(ActiveRecord::Base.connection).to receive(:execute).and_call_original
|
267
|
+
|
268
|
+
ActiveRecord::Base.connection.cache do
|
269
|
+
expect(EnsurePopulatedCache.view_populated?).to eq false
|
270
|
+
EnsurePopulatedCache.ensure_populated!
|
271
|
+
expect(EnsurePopulatedCache.view_populated?).to eq true
|
272
|
+
EnsurePopulatedCache.ensure_populated!
|
273
|
+
expect(EnsurePopulatedCache.view_populated?).to eq true
|
274
|
+
end
|
275
|
+
end
|
276
|
+
|
230
277
|
it 'supports refreshing materialized views concurrently' do
|
231
278
|
class MaterializedViewRefreshTestModel < ActiveRecord::Base
|
232
279
|
is_view 'SELECT 1 AS id;', materialized: true
|
@@ -234,23 +281,50 @@ describe ActiveRecordViews::Extension do
|
|
234
281
|
class MaterializedViewConcurrentRefreshTestModel < ActiveRecord::Base
|
235
282
|
is_view 'SELECT 1 AS id;', materialized: true, unique_columns: [:id]
|
236
283
|
end
|
284
|
+
MaterializedViewRefreshTestModel.refresh_view!
|
237
285
|
MaterializedViewConcurrentRefreshTestModel.refresh_view!
|
238
286
|
|
239
|
-
|
287
|
+
sql_statements.clear
|
288
|
+
|
289
|
+
MaterializedViewRefreshTestModel.refresh_view!
|
290
|
+
MaterializedViewRefreshTestModel.refresh_view! concurrent: false
|
291
|
+
expect {
|
292
|
+
MaterializedViewRefreshTestModel.refresh_view! concurrent: true
|
293
|
+
}.to raise_error ActiveRecord::StatementInvalid, /^PG::ObjectNotInPrerequisiteState: ERROR: +cannot refresh/
|
294
|
+
MaterializedViewConcurrentRefreshTestModel.refresh_view!
|
295
|
+
MaterializedViewConcurrentRefreshTestModel.refresh_view! concurrent: false
|
296
|
+
MaterializedViewConcurrentRefreshTestModel.refresh_view! concurrent: true
|
297
|
+
|
298
|
+
expect(sql_statements.grep_v(/^SELECT/)).to eq [
|
240
299
|
'BEGIN',
|
241
300
|
'REFRESH MATERIALIZED VIEW "materialized_view_refresh_test_models";',
|
242
301
|
"UPDATE active_record_views SET refreshed_at = current_timestamp AT TIME ZONE 'UTC' WHERE name = 'materialized_view_refresh_test_models';",
|
243
302
|
'COMMIT',
|
303
|
+
|
304
|
+
'BEGIN',
|
305
|
+
'REFRESH MATERIALIZED VIEW "materialized_view_refresh_test_models";',
|
306
|
+
"UPDATE active_record_views SET refreshed_at = current_timestamp AT TIME ZONE 'UTC' WHERE name = 'materialized_view_refresh_test_models';",
|
307
|
+
'COMMIT',
|
308
|
+
|
309
|
+
'BEGIN',
|
310
|
+
'REFRESH MATERIALIZED VIEW CONCURRENTLY "materialized_view_refresh_test_models";',
|
311
|
+
'ROLLBACK',
|
312
|
+
|
244
313
|
'BEGIN',
|
245
314
|
'REFRESH MATERIALIZED VIEW CONCURRENTLY "materialized_view_concurrent_refresh_test_models";',
|
246
315
|
"UPDATE active_record_views SET refreshed_at = current_timestamp AT TIME ZONE 'UTC' WHERE name = 'materialized_view_concurrent_refresh_test_models';",
|
247
316
|
'COMMIT',
|
248
|
-
].each do |sql|
|
249
|
-
expect(ActiveRecord::Base.connection).to receive(:execute).with(sql).once.and_call_original
|
250
|
-
end
|
251
317
|
|
252
|
-
|
253
|
-
|
318
|
+
'BEGIN',
|
319
|
+
'REFRESH MATERIALIZED VIEW "materialized_view_concurrent_refresh_test_models";',
|
320
|
+
"UPDATE active_record_views SET refreshed_at = current_timestamp AT TIME ZONE 'UTC' WHERE name = 'materialized_view_concurrent_refresh_test_models';",
|
321
|
+
'COMMIT',
|
322
|
+
|
323
|
+
'BEGIN',
|
324
|
+
'REFRESH MATERIALIZED VIEW CONCURRENTLY "materialized_view_concurrent_refresh_test_models";',
|
325
|
+
"UPDATE active_record_views SET refreshed_at = current_timestamp AT TIME ZONE 'UTC' WHERE name = 'materialized_view_concurrent_refresh_test_models';",
|
326
|
+
'COMMIT',
|
327
|
+
]
|
254
328
|
end
|
255
329
|
|
256
330
|
it 'supports opportunistically refreshing materialized views concurrently' do
|
@@ -258,21 +332,28 @@ describe ActiveRecordViews::Extension do
|
|
258
332
|
is_view 'SELECT 1 AS id;', materialized: true, unique_columns: [:id]
|
259
333
|
end
|
260
334
|
|
261
|
-
|
335
|
+
sql_statements.clear
|
336
|
+
|
337
|
+
MaterializedViewAutoRefreshTestModel.refresh_view! concurrent: :auto
|
338
|
+
MaterializedViewAutoRefreshTestModel.refresh_view! concurrent: :auto
|
339
|
+
MaterializedViewAutoRefreshTestModel.refresh_view! concurrent: :auto
|
340
|
+
|
341
|
+
expect(sql_statements.grep_v(/^SELECT/)).to eq [
|
262
342
|
'BEGIN',
|
263
343
|
'REFRESH MATERIALIZED VIEW "materialized_view_auto_refresh_test_models";',
|
264
344
|
"UPDATE active_record_views SET refreshed_at = current_timestamp AT TIME ZONE 'UTC' WHERE name = 'materialized_view_auto_refresh_test_models';",
|
265
345
|
'COMMIT',
|
346
|
+
|
266
347
|
'BEGIN',
|
267
348
|
'REFRESH MATERIALIZED VIEW CONCURRENTLY "materialized_view_auto_refresh_test_models";',
|
268
349
|
"UPDATE active_record_views SET refreshed_at = current_timestamp AT TIME ZONE 'UTC' WHERE name = 'materialized_view_auto_refresh_test_models';",
|
269
350
|
'COMMIT',
|
270
|
-
].each do |sql|
|
271
|
-
expect(ActiveRecord::Base.connection).to receive(:execute).with(sql).once.and_call_original
|
272
|
-
end
|
273
351
|
|
274
|
-
|
275
|
-
|
352
|
+
'BEGIN',
|
353
|
+
'REFRESH MATERIALIZED VIEW CONCURRENTLY "materialized_view_auto_refresh_test_models";',
|
354
|
+
"UPDATE active_record_views SET refreshed_at = current_timestamp AT TIME ZONE 'UTC' WHERE name = 'materialized_view_auto_refresh_test_models';",
|
355
|
+
'COMMIT',
|
356
|
+
]
|
276
357
|
end
|
277
358
|
|
278
359
|
it 'raises an error when refreshing materialized views with invalid concurrent option' do
|
@@ -20,6 +20,28 @@ describe ActiveRecordViews do
|
|
20
20
|
SQL
|
21
21
|
end
|
22
22
|
|
23
|
+
def test_view_populated?
|
24
|
+
value = connection.select_value(<<~SQL)
|
25
|
+
SELECT ispopulated
|
26
|
+
FROM pg_matviews
|
27
|
+
WHERE schemaname = 'public' AND matviewname = 'test'
|
28
|
+
SQL
|
29
|
+
|
30
|
+
if Rails::VERSION::MAJOR < 5
|
31
|
+
value = ActiveRecord::ConnectionAdapters::Column::TRUE_VALUES.include?(value)
|
32
|
+
end
|
33
|
+
|
34
|
+
value
|
35
|
+
end
|
36
|
+
|
37
|
+
def test_view_refreshed_at
|
38
|
+
connection.select_value(<<~SQL)
|
39
|
+
SELECT refreshed_at
|
40
|
+
FROM active_record_views
|
41
|
+
WHERE name = 'test'
|
42
|
+
SQL
|
43
|
+
end
|
44
|
+
|
23
45
|
def test_materialized_view_sql
|
24
46
|
connection.select_value(<<-SQL.squish).try(&:squish)
|
25
47
|
SELECT definition
|
@@ -120,6 +142,21 @@ describe ActiveRecordViews do
|
|
120
142
|
expect(view_names).to match_array %w[unmanaged]
|
121
143
|
end
|
122
144
|
|
145
|
+
it 'support being ran inside a transaction' do
|
146
|
+
expect(ActiveRecordViews).to receive(:without_transaction).at_least(:once).and_wrap_original do |original, *args, &block|
|
147
|
+
original.call(*args) do |new_connection|
|
148
|
+
new_connection.execute 'SET statement_timeout = 1000'
|
149
|
+
block.call(new_connection)
|
150
|
+
end
|
151
|
+
end
|
152
|
+
|
153
|
+
connection.transaction requires_new: true do
|
154
|
+
expect {
|
155
|
+
ActiveRecordViews.drop_all_views connection
|
156
|
+
}.to change { view_names }
|
157
|
+
end
|
158
|
+
end
|
159
|
+
|
123
160
|
it 'errors if an unmanaged view depends on a managed view' do
|
124
161
|
connection.execute 'CREATE VIEW unmanaged AS SELECT * from dependant2a'
|
125
162
|
|
@@ -213,6 +250,28 @@ describe ActiveRecordViews do
|
|
213
250
|
create_test_view 'select 1 as foo, 2 as bar, 3 as baz', materialized: false, unique_columns: [:foo, 'bar']
|
214
251
|
}.to raise_error ArgumentError, 'unique_columns option requires view to be materialized'
|
215
252
|
end
|
253
|
+
|
254
|
+
it 'supports resetting all materialised views' do
|
255
|
+
class ResetMaterializeViewTestModel < ActiveRecord::Base
|
256
|
+
self.table_name = 'test'
|
257
|
+
is_view 'select 123 as id', materialized: true
|
258
|
+
end
|
259
|
+
ResetMaterializeViewTestModel.refresh_view!
|
260
|
+
|
261
|
+
expect {
|
262
|
+
ActiveRecordViews.reset_materialized_views
|
263
|
+
}.to change { test_view_populated? }.to(false)
|
264
|
+
.and change { test_view_refreshed_at }.to(nil)
|
265
|
+
end
|
266
|
+
end
|
267
|
+
|
268
|
+
describe '.drop_all_views' do
|
269
|
+
let(:connection) { ActiveRecord::Base.connection }
|
270
|
+
|
271
|
+
it 'does nothing when no views have been defined' do
|
272
|
+
ActiveRecordViews.drop_all_views connection
|
273
|
+
expect(view_names).to match_array %w[]
|
274
|
+
end
|
216
275
|
end
|
217
276
|
|
218
277
|
describe '.without_transaction' do
|
data/spec/internal/Rakefile
CHANGED
@@ -1,4 +1,5 @@
|
|
1
1
|
require 'bundler/setup'
|
2
|
+
require './spec/support/silence_warnings.rb'
|
2
3
|
require 'combustion'
|
3
4
|
|
4
5
|
Combustion::Database.instance_eval do
|
@@ -10,6 +11,10 @@ end
|
|
10
11
|
Combustion.initialize! :active_record, :action_controller do
|
11
12
|
config.cache_classes = false
|
12
13
|
config.log_level = :debug
|
14
|
+
config.active_record.schema_format = ENV.fetch('SCHEMA_FORMAT', 'sql').to_sym
|
15
|
+
if ENV['SKIP_MODEL_EAGER_LOAD']
|
16
|
+
config.eager_load_paths -= Rails.application.config.paths['app/models'].to_a
|
17
|
+
end
|
13
18
|
end
|
14
19
|
|
15
20
|
load 'active_record/railties/databases.rake'
|
@@ -1 +1 @@
|
|
1
|
-
SELECT 1 AS id, '<%=
|
1
|
+
SELECT 1 AS id, '<%= test_erb_method %> file'::text AS name;
|
data/spec/spec_helper.rb
CHANGED
@@ -4,21 +4,52 @@ Bundler.setup
|
|
4
4
|
require 'rails/version'
|
5
5
|
$VERBOSE = true
|
6
6
|
|
7
|
+
require './spec/support/silence_warnings.rb'
|
8
|
+
|
7
9
|
require 'combustion'
|
8
10
|
require 'active_record_views'
|
9
11
|
|
10
12
|
FileUtils.mkdir_p 'spec/internal/db'
|
11
13
|
File.write 'spec/internal/db/schema.rb', ''
|
12
14
|
|
15
|
+
TEST_TEMP_MODEL_DIR = Rails.root + 'spec/internal/app/models_temp'
|
16
|
+
FileUtils.mkdir_p TEST_TEMP_MODEL_DIR
|
17
|
+
Rails.application.config.paths['app/models'] << 'app/models_temp'
|
18
|
+
|
13
19
|
Combustion.initialize! :active_record, :action_controller do
|
14
20
|
config.cache_classes = false
|
15
21
|
end
|
16
22
|
require 'rspec/rails'
|
23
|
+
require 'super_diff/rspec-rails'
|
24
|
+
|
25
|
+
RSpec.shared_context 'sql_statements' do
|
26
|
+
let(:sql_statements) { [] }
|
27
|
+
|
28
|
+
let!(:sql_statements_subscription) do
|
29
|
+
ActiveSupport::Notifications.subscribe('sql.active_record') do |_, _, _, _, details|
|
30
|
+
sql_statements << details.fetch(:sql)
|
31
|
+
end
|
32
|
+
end
|
33
|
+
|
34
|
+
after do
|
35
|
+
ActiveSupport::Notifications.unsubscribe sql_statements_subscription
|
36
|
+
end
|
37
|
+
end
|
17
38
|
|
18
39
|
RSpec.configure do |config|
|
19
40
|
config.use_transactional_fixtures = false
|
41
|
+
config.filter_run_when_matching focus: true
|
42
|
+
config.filter_run_excluding skip: true
|
43
|
+
config.example_status_persistence_file_path = 'tmp/examples.txt'
|
44
|
+
|
45
|
+
config.mock_with :rspec do |mocks|
|
46
|
+
mocks.verify_doubled_constant_names = true
|
47
|
+
mocks.verify_partial_doubles = true
|
48
|
+
end
|
20
49
|
|
21
50
|
config.before do
|
51
|
+
FileUtils.rm_rf Dir["spec/internal/app/models_temp/*"]
|
52
|
+
|
22
53
|
if Rails::VERSION::MAJOR >= 5
|
23
54
|
Rails.application.reloader.reload!
|
24
55
|
else
|
@@ -48,24 +79,27 @@ RSpec.configure do |config|
|
|
48
79
|
connection.execute "DROP MATERIALIZED VIEW IF EXISTS #{connection.quote_table_name view_name} CASCADE"
|
49
80
|
end
|
50
81
|
end
|
82
|
+
|
83
|
+
config.include_context 'sql_statements'
|
51
84
|
end
|
52
85
|
|
53
|
-
def
|
54
|
-
|
55
|
-
Rails.application.
|
56
|
-
|
86
|
+
def with_reloader(&block)
|
87
|
+
if Rails.application.respond_to?(:reloader)
|
88
|
+
Rails.application.reloader.wrap(&block)
|
89
|
+
else
|
90
|
+
block.call
|
57
91
|
end
|
58
92
|
end
|
59
93
|
|
60
|
-
def
|
61
|
-
|
62
|
-
|
63
|
-
|
64
|
-
|
65
|
-
|
66
|
-
|
67
|
-
|
68
|
-
|
94
|
+
def test_request
|
95
|
+
with_reloader do
|
96
|
+
status, headers, body = Rails.application.call(
|
97
|
+
'REQUEST_METHOD' => 'GET',
|
98
|
+
'PATH_INFO' => '/',
|
99
|
+
'rack.input' => StringIO.new,
|
100
|
+
)
|
101
|
+
expect(status).to eq 204
|
102
|
+
body.close
|
69
103
|
end
|
70
104
|
end
|
71
105
|
|
@@ -0,0 +1,23 @@
|
|
1
|
+
require 'warning'
|
2
|
+
require 'rails/version'
|
3
|
+
|
4
|
+
case Rails::VERSION::STRING
|
5
|
+
when /^4\.2\./
|
6
|
+
Warning.ignore(%r{lib/(active_support/core_ext|action_dispatch/middleware)/.+: warning: (method redefined|previous definition)})
|
7
|
+
Warning.ignore(%r{lib/active_support/core_ext/.+: warning: BigDecimal.new is deprecated})
|
8
|
+
Warning.ignore(%r{lib/arel/visitors/informix.rb:\d+: warning: assigned but unused variable})
|
9
|
+
Warning.ignore(%r{lib/active_record/connection_adapters/.+: warning: deprecated Object#=~ is called on Integer})
|
10
|
+
Warning.ignore(%r{Inheriting from Rack::Session::Abstract::ID is deprecated})
|
11
|
+
when /^5\.0\./
|
12
|
+
Warning.ignore(%r{lib/(active_support/core_ext|action_view)/.+: warning: (method redefined|previous definition)})
|
13
|
+
Warning.ignore(%r{lib/arel/visitors/informix.rb:\d+: warning: assigned but unused variable})
|
14
|
+
Warning.ignore(%r{lib/action_view/.+: warning: `\*' interpreted as argument prefix})
|
15
|
+
when /^5\.1\./
|
16
|
+
Warning.ignore(%r{lib/(active_support/core_ext)/.+: warning: (method redefined|previous definition)})
|
17
|
+
Warning.ignore(%r{lib/arel/visitors/informix.rb:\d+: warning: assigned but unused variable})
|
18
|
+
Warning.ignore(%r{lib/active_record/.+/schema_statements.rb:\d+: (warning: in `drop_table': the last argument was passed as a single Hash|warning: although a splat keyword arguments here)})
|
19
|
+
end
|
20
|
+
|
21
|
+
Warning.process do |_warning|
|
22
|
+
:raise
|
23
|
+
end
|