activerecord_views 0.1.5 → 0.1.7

Sign up to get free protection for your applications and to get access to all the features.
Files changed (40) hide show
  1. checksums.yaml +4 -4
  2. data/lib/active_record_views/database_tasks.rb +91 -0
  3. data/lib/active_record_views/railtie.rb +0 -4
  4. data/lib/active_record_views/version.rb +1 -1
  5. data/lib/active_record_views.rb +2 -0
  6. metadata +11 -65
  7. data/.gitignore +0 -8
  8. data/.rspec +0 -2
  9. data/.tool-versions +0 -1
  10. data/Appraisals +0 -15
  11. data/Gemfile +0 -3
  12. data/LICENSE.txt +0 -22
  13. data/Rakefile +0 -14
  14. data/activerecord_views.gemspec +0 -27
  15. data/gemfiles/rails5_2.gemfile +0 -7
  16. data/gemfiles/rails6_0.gemfile +0 -7
  17. data/gemfiles/rails6_1.gemfile +0 -7
  18. data/gemfiles/rails7_0.gemfile +0 -7
  19. data/lib/tasks/active_record_views.rake +0 -106
  20. data/spec/active_record_views_checksum_cache_spec.rb +0 -112
  21. data/spec/active_record_views_extension_spec.rb +0 -459
  22. data/spec/active_record_views_spec.rb +0 -344
  23. data/spec/internal/Rakefile +0 -24
  24. data/spec/internal/app/models/dependency_a.rb +0 -3
  25. data/spec/internal/app/models/dependency_b.rb +0 -3
  26. data/spec/internal/app/models/dependency_c.rb +0 -3
  27. data/spec/internal/app/models/erb_test_model.rb +0 -7
  28. data/spec/internal/app/models/erb_test_model.sql.erb +0 -1
  29. data/spec/internal/app/models/external_file_test_model.rb +0 -3
  30. data/spec/internal/app/models/external_file_test_model.sql +0 -1
  31. data/spec/internal/app/models/heredoc_test_model.rb +0 -5
  32. data/spec/internal/app/models/modified_a.rb +0 -3
  33. data/spec/internal/app/models/modified_b.rb +0 -3
  34. data/spec/internal/app/models/namespace/test_model.rb +0 -3
  35. data/spec/internal/app/models/namespace/test_model.sql +0 -1
  36. data/spec/internal/config/database.yml +0 -9
  37. data/spec/internal/config/routes.rb +0 -3
  38. data/spec/spec_helper.rb +0 -130
  39. data/spec/support/silence_warnings.rb +0 -6
  40. data/spec/tasks_spec.rb +0 -113
@@ -1,459 +0,0 @@
1
- require 'spec_helper'
2
-
3
- describe ActiveRecordViews::Extension do
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
- connection.view_exists?(name)
12
- end
13
-
14
- it 'creates database views from heredocs' do
15
- expect(ActiveRecordViews).to receive(:create_view).once.and_call_original
16
- expect(HeredocTestModel.first.name).to eq 'Here document'
17
- end
18
-
19
- it 'creates database views from external SQL files' do
20
- expect(ActiveRecordViews).to receive(:create_view).once.and_call_original
21
- expect(ExternalFileTestModel.first.name).to eq 'External SQL file'
22
- end
23
-
24
- it 'creates database views from namespaced external SQL files' do
25
- expect(ActiveRecordViews).to receive(:create_view).once.and_call_original
26
- expect(Namespace::TestModel.first.name).to eq 'Namespaced SQL file'
27
- end
28
-
29
- it 'creates database views from external ERB files' do
30
- expect(ActiveRecordViews).to receive(:create_view).once.and_call_original
31
- expect(ErbTestModel.first.name).to eq 'ERB method file'
32
- end
33
-
34
- it 'errors if external SQL file is missing' do
35
- expect {
36
- class MissingFileTestModel < ActiveRecord::Base
37
- is_view
38
- end
39
- }.to raise_error RuntimeError, /could not find missing_file_test_model.sql/
40
- end
41
-
42
- it 'reloads the database view when external SQL file is modified' do
43
- sql_file = File.join(TEST_TEMP_MODEL_DIR, 'modified_file_test_model.sql')
44
- update_file sql_file, "SELECT 'foo'::text AS test"
45
-
46
- expect {
47
- expect(ActiveRecordViews).to receive(:create_view).once.and_call_original
48
- class ModifiedFileTestModel < ActiveRecord::Base
49
- is_view
50
- end
51
- }.to change { begin; ModifiedFileTestModel.take!.test; rescue NameError; end }.from(nil).to('foo')
52
- .and change { registered_model_class_names.include?('ModifiedFileTestModel') }.from(false).to(true)
53
-
54
- expect {
55
- update_file sql_file, "SELECT 'bar'::text AS test, 42::integer AS test2"
56
- }.to_not change { ModifiedFileTestModel.take!.test }
57
-
58
- expect {
59
- expect(ActiveRecordViews).to receive(:create_view).once.and_call_original
60
- test_request
61
- test_request # second request does not `create_view` again
62
- }.to change { ModifiedFileTestModel.take!.test }.to('bar')
63
- .and change { ModifiedFileTestModel.column_names }.from(%w[test]).to(%w[test test2])
64
-
65
- expect {
66
- update_file sql_file, "SELECT 'baz'::text AS test"
67
- }.to_not change { ModifiedFileTestModel.take!.test }
68
-
69
- expect {
70
- expect(ActiveRecordViews).to receive(:create_view).once.and_call_original
71
- test_request
72
- }.to change { ModifiedFileTestModel.take!.test }.to('baz')
73
-
74
- File.unlink sql_file
75
- test_request # trigger cleanup
76
- end
77
-
78
- it 'reloads the database view when external ERB SQL file is modified' do
79
- ['foo 42', 'bar 42'].each do |sql|
80
- expect(ActiveRecordViews).to receive(:create_view).with(
81
- anything,
82
- 'modified_erb_file_test_models',
83
- 'ModifiedErbFileTestModel',
84
- sql,
85
- {}
86
- ).once.ordered
87
- end
88
-
89
- sql_file = File.join(TEST_TEMP_MODEL_DIR, 'modified_erb_file_test_model.sql.erb')
90
- update_file sql_file, 'foo <%= test_erb_method %>'
91
-
92
- class ModifiedErbFileTestModel < ActiveRecord::Base
93
- def self.test_erb_method
94
- 2 * 3 * 7
95
- end
96
-
97
- is_view
98
- end
99
-
100
- update_file sql_file, 'bar <%= test_erb_method %>'
101
- test_request
102
-
103
- File.unlink sql_file
104
- test_request # trigger cleanup
105
- end
106
-
107
- it 'drops the view if the external SQL file is deleted' do
108
- sql_file = File.join(TEST_TEMP_MODEL_DIR, 'deleted_file_test_model.sql')
109
- File.write sql_file, "SELECT 1 AS id, 'delete test'::text AS name"
110
-
111
- rb_file = 'spec/internal/app/models_temp/deleted_file_test_model.rb'
112
- File.write rb_file, <<~RB
113
- class DeletedFileTestModel < ActiveRecord::Base
114
- is_view
115
- end
116
- RB
117
-
118
- Rails.application.reloader.wrap do
119
- expect(DeletedFileTestModel.first.name).to eq 'delete test'
120
- end
121
-
122
- File.unlink sql_file
123
- File.unlink rb_file
124
-
125
- expect(ActiveRecord::Base.connection).to receive(:execute).with(/\ADROP/).once.and_call_original
126
- expect {
127
- test_request
128
- }.to change { registered_model_class_names.include?('DeletedFileTestModel') }.from(true).to(false)
129
- .and change { view_exists?('deleted_file_test_models') }.from(true).to(false)
130
- test_request # second request does not `drop_view` again
131
-
132
- expect {
133
- DeletedFileTestModel.first.name
134
- }.to raise_error NameError, 'uninitialized constant DeletedFileTestModel'
135
- end
136
-
137
- it 'does not create if database view is initially up to date' do
138
- ActiveRecordViews.create_view ActiveRecord::Base.connection, 'initial_create_test_models', 'InitialCreateTestModel', 'SELECT 42 as id'
139
- expect(ActiveRecord::Base.connection).to receive(:execute).with(/\ACREATE (?:OR REPLACE )?VIEW/).never
140
- class InitialCreateTestModel < ActiveRecord::Base
141
- is_view 'SELECT 42 as id'
142
- end
143
- end
144
-
145
- it 'successfully recreates modified paired views with incompatible changes' do
146
- without_dependency_checks do
147
- ActiveRecordViews.create_view ActiveRecord::Base.connection, 'modified_as', 'ModifiedA', 'SELECT 11 AS old_name;'
148
- ActiveRecordViews.create_view ActiveRecord::Base.connection, 'modified_bs', 'ModifiedB', 'SELECT old_name FROM modified_as;'
149
- end
150
-
151
- expect(ModifiedB.first.attributes.except(nil)).to eq('new_name' => 22)
152
- end
153
-
154
- it 'successfully restores dependant view when temporarily dropping dependency' do
155
- without_dependency_checks do
156
- ActiveRecordViews.create_view ActiveRecord::Base.connection, 'dependency_as', 'DependencyA', 'SELECT 42 AS foo, 1 AS id;'
157
- ActiveRecordViews.create_view ActiveRecord::Base.connection, 'dependency_bs', 'DependencyB', 'SELECT id FROM dependency_as;'
158
- end
159
-
160
- expect(DependencyA.first.id).to eq 2
161
- expect(DependencyB.first.id).to eq 2
162
- end
163
-
164
- it 'sucessfully restore dependant view and dependency when loading from middle outwards' do
165
- without_dependency_checks do
166
- ActiveRecordViews.create_view ActiveRecord::Base.connection, 'dependency_as', 'DependencyA', 'SELECT 42 AS foo, 1 AS id;'
167
- ActiveRecordViews.create_view ActiveRecord::Base.connection, 'dependency_bs', 'DependencyB', 'SELECT id FROM dependency_as;'
168
- ActiveRecordViews.create_view ActiveRecord::Base.connection, 'dependency_cs', 'DependencyC', 'SELECT id FROM dependency_bs;'
169
- end
170
-
171
- expect(DependencyB.first.id).to eq 2
172
- end
173
-
174
- it 'errors if more than one argument is specified' do
175
- expect {
176
- class TooManyArguments < ActiveRecord::Base
177
- is_view 'SELECT 1 AS ID;', 'SELECT 2 AS ID;'
178
- end
179
- }.to raise_error ArgumentError, 'wrong number of arguments (2 for 0..1)'
180
- end
181
-
182
- it 'errors if an invalid option is specified' do
183
- expect {
184
- class InvalidOption < ActiveRecord::Base
185
- is_view 'SELECT 1 AS ID;', blargh: 123
186
- end
187
- }.to raise_error ArgumentError, /^Unknown key: :?blargh/
188
- end
189
-
190
- it 'creates/refreshes/drops materialized views' do
191
- sql_file = File.join(TEST_TEMP_MODEL_DIR, 'materialized_view_test_model.sql')
192
- File.write sql_file, 'SELECT 123 AS id;'
193
-
194
- class MaterializedViewTestModel < ActiveRecord::Base
195
- is_view materialized: true
196
- end
197
-
198
- expect {
199
- MaterializedViewTestModel.first!
200
- }.to raise_error ActiveRecord::StatementInvalid, /materialized view "materialized_view_test_models" has not been populated/
201
-
202
- expect(MaterializedViewTestModel.view_populated?).to eq false
203
- expect(MaterializedViewTestModel.refreshed_at).to eq nil
204
-
205
- MaterializedViewTestModel.refresh_view!
206
-
207
- expect(MaterializedViewTestModel.view_populated?).to eq true
208
- expect(MaterializedViewTestModel.refreshed_at).to be_a Time
209
- expect(MaterializedViewTestModel.refreshed_at.zone).to eq 'UTC'
210
- expect(MaterializedViewTestModel.refreshed_at).to be_within(1.second).of Time.now
211
-
212
- expect(MaterializedViewTestModel.first!.id).to eq 123
213
-
214
- File.unlink sql_file
215
-
216
- expect {
217
- test_request
218
- }.to change { view_exists?('materialized_view_test_models') }.from(true).to(false)
219
- end
220
-
221
- it 'raises an error for `view_populated?` if view is not materialized' do
222
- class NonMaterializedViewPopulatedTestModel < ActiveRecord::Base
223
- is_view 'SELECT 1 AS id;'
224
- end
225
-
226
- expect {
227
- NonMaterializedViewPopulatedTestModel.view_populated?
228
- }.to raise_error ArgumentError, 'not a materialized view'
229
- end
230
-
231
- it 'supports ensuring a view hierarchy has been populated' do
232
- class EnsurePopulatedFoo < ActiveRecord::Base
233
- is_view 'SELECT 1 AS id;', materialized: true
234
- end
235
-
236
- class EnsurePopulatedBar < ActiveRecord::Base
237
- is_view "SELECT * FROM #{EnsurePopulatedFoo.table_name}", dependencies: [EnsurePopulatedFoo]
238
- end
239
-
240
- class EnsurePopulatedBaz < ActiveRecord::Base
241
- is_view "SELECT * FROM #{EnsurePopulatedBar.table_name}", dependencies: [EnsurePopulatedBar]
242
- end
243
-
244
- expect(ActiveRecord::Base.connection).to receive(:execute).with('REFRESH MATERIALIZED VIEW "ensure_populated_foos";').once.and_call_original
245
- allow(ActiveRecord::Base.connection).to receive(:execute).and_call_original
246
-
247
- expect(EnsurePopulatedFoo.view_populated?).to eq false
248
- EnsurePopulatedBaz.ensure_populated!
249
- expect(EnsurePopulatedFoo.view_populated?).to eq true
250
- EnsurePopulatedBaz.ensure_populated!
251
- expect(EnsurePopulatedFoo.view_populated?).to eq true
252
- end
253
-
254
- it 'invalidates ActiveRecord query cache after populating' do
255
- class EnsurePopulatedCache < ActiveRecord::Base
256
- is_view 'SELECT 1 AS id;', materialized: true
257
- end
258
-
259
- expect(ActiveRecord::Base.connection).to receive(:execute).with('REFRESH MATERIALIZED VIEW "ensure_populated_caches";').once.and_call_original
260
- allow(ActiveRecord::Base.connection).to receive(:execute).and_call_original
261
-
262
- ActiveRecord::Base.connection.cache do
263
- expect(EnsurePopulatedCache.view_populated?).to eq false
264
- EnsurePopulatedCache.ensure_populated!
265
- expect(EnsurePopulatedCache.view_populated?).to eq true
266
- EnsurePopulatedCache.ensure_populated!
267
- expect(EnsurePopulatedCache.view_populated?).to eq true
268
- end
269
- end
270
-
271
- it 'supports refreshing materialized views concurrently' do
272
- class MaterializedViewRefreshTestModel < ActiveRecord::Base
273
- is_view 'SELECT 1 AS id;', materialized: true
274
- end
275
- class MaterializedViewConcurrentRefreshTestModel < ActiveRecord::Base
276
- is_view 'SELECT 1 AS id;', materialized: true, unique_columns: [:id]
277
- end
278
- MaterializedViewRefreshTestModel.refresh_view!
279
- MaterializedViewConcurrentRefreshTestModel.refresh_view!
280
-
281
- sql_statements.clear
282
-
283
- MaterializedViewRefreshTestModel.refresh_view!
284
- MaterializedViewRefreshTestModel.refresh_view! concurrent: false
285
- expect {
286
- MaterializedViewRefreshTestModel.refresh_view! concurrent: true
287
- }.to raise_error ActiveRecord::StatementInvalid, /^PG::ObjectNotInPrerequisiteState: ERROR: +cannot refresh/
288
- MaterializedViewConcurrentRefreshTestModel.refresh_view!
289
- MaterializedViewConcurrentRefreshTestModel.refresh_view! concurrent: false
290
- MaterializedViewConcurrentRefreshTestModel.refresh_view! concurrent: true
291
-
292
- expect(sql_statements.grep_v(/^SELECT/)).to eq [
293
- 'BEGIN',
294
- 'REFRESH MATERIALIZED VIEW "materialized_view_refresh_test_models";',
295
- "UPDATE active_record_views SET refreshed_at = current_timestamp AT TIME ZONE 'UTC' WHERE name = 'materialized_view_refresh_test_models';",
296
- 'COMMIT',
297
-
298
- 'BEGIN',
299
- 'REFRESH MATERIALIZED VIEW "materialized_view_refresh_test_models";',
300
- "UPDATE active_record_views SET refreshed_at = current_timestamp AT TIME ZONE 'UTC' WHERE name = 'materialized_view_refresh_test_models';",
301
- 'COMMIT',
302
-
303
- 'BEGIN',
304
- 'REFRESH MATERIALIZED VIEW CONCURRENTLY "materialized_view_refresh_test_models";',
305
- 'ROLLBACK',
306
-
307
- 'BEGIN',
308
- 'REFRESH MATERIALIZED VIEW CONCURRENTLY "materialized_view_concurrent_refresh_test_models";',
309
- "UPDATE active_record_views SET refreshed_at = current_timestamp AT TIME ZONE 'UTC' WHERE name = 'materialized_view_concurrent_refresh_test_models';",
310
- 'COMMIT',
311
-
312
- 'BEGIN',
313
- 'REFRESH MATERIALIZED VIEW "materialized_view_concurrent_refresh_test_models";',
314
- "UPDATE active_record_views SET refreshed_at = current_timestamp AT TIME ZONE 'UTC' WHERE name = 'materialized_view_concurrent_refresh_test_models';",
315
- 'COMMIT',
316
-
317
- 'BEGIN',
318
- 'REFRESH MATERIALIZED VIEW CONCURRENTLY "materialized_view_concurrent_refresh_test_models";',
319
- "UPDATE active_record_views SET refreshed_at = current_timestamp AT TIME ZONE 'UTC' WHERE name = 'materialized_view_concurrent_refresh_test_models';",
320
- 'COMMIT',
321
- ]
322
- end
323
-
324
- it 'supports opportunistically refreshing materialized views concurrently' do
325
- class MaterializedViewAutoRefreshTestModel < ActiveRecord::Base
326
- is_view 'SELECT 1 AS id;', materialized: true, unique_columns: [:id]
327
- end
328
-
329
- sql_statements.clear
330
-
331
- MaterializedViewAutoRefreshTestModel.refresh_view! concurrent: :auto
332
- MaterializedViewAutoRefreshTestModel.refresh_view! concurrent: :auto
333
- MaterializedViewAutoRefreshTestModel.refresh_view! concurrent: :auto
334
-
335
- expect(sql_statements.grep_v(/^SELECT/)).to eq [
336
- 'BEGIN',
337
- 'REFRESH MATERIALIZED VIEW "materialized_view_auto_refresh_test_models";',
338
- "UPDATE active_record_views SET refreshed_at = current_timestamp AT TIME ZONE 'UTC' WHERE name = 'materialized_view_auto_refresh_test_models';",
339
- 'COMMIT',
340
-
341
- 'BEGIN',
342
- 'REFRESH MATERIALIZED VIEW CONCURRENTLY "materialized_view_auto_refresh_test_models";',
343
- "UPDATE active_record_views SET refreshed_at = current_timestamp AT TIME ZONE 'UTC' WHERE name = 'materialized_view_auto_refresh_test_models';",
344
- 'COMMIT',
345
-
346
- 'BEGIN',
347
- 'REFRESH MATERIALIZED VIEW CONCURRENTLY "materialized_view_auto_refresh_test_models";',
348
- "UPDATE active_record_views SET refreshed_at = current_timestamp AT TIME ZONE 'UTC' WHERE name = 'materialized_view_auto_refresh_test_models';",
349
- 'COMMIT',
350
- ]
351
- end
352
-
353
- it 'raises an error when refreshing materialized views with invalid concurrent option' do
354
- class MaterializedViewInvalidRefreshTestModel < ActiveRecord::Base
355
- is_view 'SELECT 1 AS id;', materialized: true, unique_columns: [:id]
356
- end
357
-
358
- expect {
359
- MaterializedViewAutoRefreshTestModel.refresh_view! concurrent: :blah
360
- }.to raise_error ArgumentError, 'invalid concurrent option'
361
- end
362
-
363
- it 'errors if dependencies are not specified' do
364
- class DependencyCheckBase1 < ActiveRecord::Base
365
- self.table_name = 'dependency_check_base1'
366
- is_view 'SELECT 1 AS ID;'
367
- end
368
- class DependencyCheckBase2 < ActiveRecord::Base
369
- self.table_name = 'dependency_check_base2'
370
- is_view 'SELECT 1 AS ID;'
371
- end
372
- ActiveRecord::Base.connection.execute 'CREATE VIEW dependency_check_base_unmanaged AS SELECT 1 AS ID;'
373
-
374
- expect {
375
- class DependencyCheckGood < ActiveRecord::Base
376
- is_view 'SELECT * FROM dependency_check_base1;', dependencies: [DependencyCheckBase1]
377
- end
378
- }.to_not raise_error
379
-
380
- expect {
381
- class DependencyCheckGoodUnmanaged < ActiveRecord::Base
382
- is_view 'SELECT * FROM dependency_check_base_unmanaged;'
383
- end
384
- }.to_not raise_error
385
-
386
- expect {
387
- class DependencyCheckMissing1 < ActiveRecord::Base
388
- is_view 'SELECT * FROM dependency_check_base1 UNION ALL SELECT * FROM dependency_check_base2;', dependencies: [DependencyCheckBase1]
389
- end
390
- }.to raise_error ArgumentError, 'DependencyCheckBase2 must be specified as a dependency of DependencyCheckMissing1: `is_view dependencies: [DependencyCheckBase1, DependencyCheckBase2]`'
391
-
392
- expect {
393
- class DependencyCheckMissing2 < ActiveRecord::Base
394
- is_view 'SELECT * FROM dependency_check_base1 UNION ALL SELECT * FROM dependency_check_base2;', dependencies: []
395
- end
396
- }.to raise_error ArgumentError, 'DependencyCheckBase1 and DependencyCheckBase2 must be specified as dependencies of DependencyCheckMissing2: `is_view dependencies: [DependencyCheckBase1, DependencyCheckBase2]`'
397
-
398
- expect {
399
- class DependencyCheckNested < ActiveRecord::Base
400
- is_view 'SELECT 1 FROM dependency_check_goods'
401
- end
402
- }.to raise_error ArgumentError, 'DependencyCheckGood must be specified as a dependency of DependencyCheckNested: `is_view dependencies: [DependencyCheckGood]`'
403
-
404
- expect {
405
- class DependencyCheckExtra1 < ActiveRecord::Base
406
- is_view 'SELECT * FROM dependency_check_base1;', dependencies: [DependencyCheckBase1, DependencyCheckBase2]
407
- end
408
- }.to raise_error ArgumentError, 'DependencyCheckBase2 is not a dependency of DependencyCheckExtra1'
409
-
410
- expect {
411
- class DependencyCheckExtra2 < ActiveRecord::Base
412
- is_view 'SELECT 1 AS id;', dependencies: [DependencyCheckBase1, DependencyCheckBase2]
413
- end
414
- }.to raise_error ArgumentError, 'DependencyCheckBase1 and DependencyCheckBase2 are not dependencies of DependencyCheckExtra2'
415
-
416
- expect {
417
- class DependencyCheckWrongType < ActiveRecord::Base
418
- is_view 'SELECT 1;', dependencies: %w[DependencyCheckBase1]
419
- end
420
- }.to raise_error ArgumentError, 'dependencies must be ActiveRecord classes'
421
-
422
- expect(view_names).to match_array %w[
423
- dependency_check_base1
424
- dependency_check_base2
425
- dependency_check_base_unmanaged
426
-
427
- dependency_check_goods
428
- dependency_check_good_unmanageds
429
- ]
430
- end
431
-
432
- context 'without create_enabled' do
433
- around do |example|
434
- without_create_enabled(&example)
435
- end
436
-
437
- it 'delays create_view until process_create_queue! is called' do
438
- allow(ActiveRecordViews).to receive(:create_view).and_call_original
439
-
440
- expect(ActiveRecordViews::Extension.create_queue.size).to eq 0
441
- expect(ActiveRecordViews).to_not have_received(:create_view)
442
-
443
- expect {
444
- expect(HeredocTestModel.first.name).to eq 'Here document'
445
- }.to raise_error ActiveRecord::StatementInvalid
446
-
447
- expect(ActiveRecordViews::Extension.create_queue.size).to eq 1
448
- expect(ActiveRecordViews).to_not have_received(:create_view)
449
-
450
- ActiveRecordViews::Extension.process_create_queue!
451
-
452
- expect(ActiveRecordViews::Extension.create_queue.size).to eq 0
453
- expect(ActiveRecordViews).to have_received(:create_view)
454
-
455
- expect(HeredocTestModel.first.name).to eq 'Here document'
456
- end
457
- end
458
- end
459
- end