activerecord_views 0.0.20 → 0.1.3
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- 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 +35 -6
- 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 +55 -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
|
@@ -228,6 +250,28 @@ describe ActiveRecordViews do
|
|
228
250
|
create_test_view 'select 1 as foo, 2 as bar, 3 as baz', materialized: false, unique_columns: [:foo, 'bar']
|
229
251
|
}.to raise_error ArgumentError, 'unique_columns option requires view to be materialized'
|
230
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
|
231
275
|
end
|
232
276
|
|
233
277
|
describe '.without_transaction' do
|
@@ -289,5 +333,16 @@ describe ActiveRecordViews do
|
|
289
333
|
original_connection.pool.checkin original_connection_2
|
290
334
|
end
|
291
335
|
end
|
336
|
+
|
337
|
+
it 'does not attempt to checkin when checkout fails' do
|
338
|
+
expect(original_connection.pool).to receive(:checkout).and_raise PG::ConnectionBad
|
339
|
+
expect(original_connection.pool).to_not receive(:checkin)
|
340
|
+
|
341
|
+
expect {
|
342
|
+
original_connection.transaction do
|
343
|
+
ActiveRecordViews.without_transaction(original_connection) { }
|
344
|
+
end
|
345
|
+
}.to raise_error PG::ConnectionBad
|
346
|
+
end
|
292
347
|
end
|
293
348
|
end
|
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
|