with_model 2.3.1 → 2.5.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.
@@ -4,9 +4,11 @@ require "active_record"
4
4
  require "with_model/invalid_superclass"
5
5
 
6
6
  module WithModel
7
- # Stands in for a {WithModel::Table} when a model should inherit its
8
- # superclass's table instead of getting one of its own, as Rails Single Table
9
- # Inheritance requires. Selected by `table(false)`.
7
+ # Stands in for a {WithModel::Table} when a model should not get a table of its
8
+ # own: either it inherits its superclass's table, as Rails Single Table
9
+ # Inheritance requires, or it uses an existing table: one its model block
10
+ # names with `self.table_name`, or the one Active Record names after the
11
+ # model. An abstract model needs neither. Selected by `table(false)`.
10
12
  #
11
13
  # In general, direct use of this class should be avoided. Instead use
12
14
  # either the {WithModel high-level API} or {WithModel::Model::DSL low-level API}.
@@ -21,7 +23,9 @@ module WithModel
21
23
  end
22
24
 
23
25
  # Creates nothing, but raises {WithModel::InvalidSuperclass} when the
24
- # superclass cannot support single table inheritance.
26
+ # superclass has a table that cannot support single table inheritance.
27
+ # Whether a superclass with nothing to inherit is a problem depends on the
28
+ # model block, so that waits for {#verify}.
25
29
  #
26
30
  # Errors describe the model's situation rather than the call that produced
27
31
  # it. Any expression that evaluates to false selects a NullTable, so there
@@ -34,14 +38,13 @@ module WithModel
34
38
  # `StatementInvalid` naming the table if it never appears, so raising here
35
39
  # would only forbid working setups.
36
40
  #
37
- # What is left is {WithModel::InvalidSuperclass}: both errors are permanent
38
- # facts about the superclass passed in, not about when it was looked at, so no
39
- # amount of waiting makes them work.
41
+ # What is left is {WithModel::InvalidSuperclass}: a missing inheritance
42
+ # column, like a missing table to inherit, is a permanent fact about the
43
+ # superclass passed in, not about when it was looked at, so no amount of
44
+ # waiting makes it work.
40
45
  def create
41
- raise_invalid_superclass "#{@superclass} has none to inherit" unless table_name?
42
-
43
- # Nothing more can be checked until there is a table to look at.
44
- return unless table_exists?
46
+ # Nothing can be checked until there is a table to look at.
47
+ return unless inheritable_table? && table_exists?
45
48
  return if inheritance_column?
46
49
 
47
50
  raise_invalid_superclass "#{@superclass}'s table #{@superclass.table_name.inspect} has no " \
@@ -54,9 +57,33 @@ module WithModel
54
57
  def configure(klass)
55
58
  end
56
59
 
57
- # Removes the rows this model wrote, which would otherwise outlive the
58
- # constant that names them and make the superclass unloadable
59
- # (`ActiveRecord::SubclassNotFound`).
60
+ # Raises {WithModel::InvalidSuperclass} when the superclass has no table to
61
+ # inherit (`ActiveRecord::Base`, or an abstract class) and the model's own
62
+ # table - the one its block named, or else the one Active Record names after
63
+ # the model - does not exist once its block has run. A model its block made
64
+ # abstract needs no table, so it always passes.
65
+ #
66
+ # Unlike a superclass's missing table, which {#create} lets through, this
67
+ # table must already exist. A model with nothing to inherit is far more
68
+ # likely a mistaken attempt at single table inheritance than a deliberate
69
+ # reference to a table that is yet to be created, and with nothing to point
70
+ # at, Active Record's own error would leave that mistake unexplained.
71
+ def verify(klass)
72
+ return if klass.abstract_class? || inheritable_table? || own_table_exists?(klass)
73
+
74
+ raise_invalid_superclass "#{@superclass} has none to inherit and there is no " \
75
+ "#{klass.table_name.inspect} table. To use an existing table, create it before " \
76
+ "the model or set `self.table_name` in its model block."
77
+ end
78
+
79
+ # Removes the rows a single table inheritance subclass wrote, which would
80
+ # otherwise outlive the constant that names them and make the superclass
81
+ # unloadable (`ActiveRecord::SubclassNotFound`).
82
+ #
83
+ # A model that is its own base class names a table that belongs to the
84
+ # suite, and its rows name no class, so they are left for the suite to clean
85
+ # up like the rest of that table - its table need not have an inheritance
86
+ # column to delete by, either.
60
87
  #
61
88
  # `unscoped` is required because a `default_scope` on the superclass would
62
89
  # otherwise hide rows from cleanup; the inheritance-column condition is
@@ -66,7 +93,7 @@ module WithModel
66
93
  # A table that does not exist holds no rows to remove, and failing here
67
94
  # would fail an example whose body had already passed.
68
95
  def teardown(klass)
69
- return unless klass.table_exists?
96
+ return if klass.base_class? || !klass.table_exists?
70
97
 
71
98
  klass.unscoped.where(klass.inheritance_column => klass.sti_name).each(&:destroy!)
72
99
  end
@@ -79,10 +106,13 @@ module WithModel
79
106
 
80
107
  def inheritance_column = @superclass.inheritance_column
81
108
 
82
- # `ActiveRecord::Base` and abstract classes alike have no `table_name`, so
83
- # this is what "nothing to inherit" actually looks like - `abstract_class?`
84
- # is false for `ActiveRecord::Base` and so does not describe both.
85
- def table_name? = @superclass.table_name.present?
109
+ # `ActiveRecord::Base` has no `table_name`, and an abstract class has none to
110
+ # pass on through single table inheritance even when it has one: Active
111
+ # Record makes each of its subclasses a base class of its own. Neither test
112
+ # covers both, since `abstract_class?` is false for `ActiveRecord::Base`.
113
+ def inheritable_table? = !@superclass.abstract_class? && @superclass.table_name.present?
114
+
115
+ def own_table_exists?(klass) = klass.connection.data_source_exists?(klass.table_name)
86
116
 
87
117
  def table_exists? = @superclass.table_exists?
88
118
 
@@ -0,0 +1,10 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "rspec/core"
4
+ require "with_model"
5
+
6
+ WithModel.runner = :rspec
7
+
8
+ RSpec.configure do |config|
9
+ config.extend WithModel
10
+ end
@@ -30,6 +30,12 @@ module WithModel
30
30
  klass.table_name = @name
31
31
  end
32
32
 
33
+ # Accepts any model block: the table is the model's own. Present so that
34
+ # {WithModel::NullTable} - which checks the model once its block has run -
35
+ # can stand in here.
36
+ def verify(_klass)
37
+ end
38
+
33
39
  # Removes everything this table holds by dropping it. The model is not
34
40
  # needed, but is accepted so that {WithModel::NullTable} - which does need
35
41
  # it - can stand in here.
@@ -38,19 +44,13 @@ module WithModel
38
44
  end
39
45
 
40
46
  def destroy
41
- connection.drop_table(@name)
47
+ connection.drop_table(@name) if exists?
42
48
  end
43
49
 
44
50
  private
45
51
 
46
52
  attr_reader :connection
47
53
 
48
- def exists?
49
- if connection.respond_to?(:data_source_exists?)
50
- connection.data_source_exists?(@name)
51
- else
52
- connection.table_exists?(@name)
53
- end
54
- end
54
+ def exists? = connection.data_source_exists?(@name)
55
55
  end
56
56
  end
@@ -0,0 +1,7 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "test/unit"
4
+ require "with_model"
5
+
6
+ WithModel.runner = :test_unit
7
+ Test::Unit::TestCase.extend WithModel
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module WithModel
4
- VERSION = "2.3.1"
4
+ VERSION = "2.5.0"
5
5
  end
data/lib/with_model.rb CHANGED
@@ -113,8 +113,13 @@ module WithModel
113
113
  class_eval do
114
114
  include MiniTestLifeCycle.call(object)
115
115
  end
116
+ when :test_unit
117
+ # These options place creation before and destruction after user callbacks,
118
+ # preserving declaration and reverse order across inheritance.
119
+ setup(before: :append) { object.create }
120
+ teardown(after: :prepend) { object.destroy }
116
121
  else
117
- raise ArgumentError, "Unsupported test runner set, expected :rspec or :minitest"
122
+ raise ArgumentError, "Unsupported test runner set, expected :rspec, :minitest, or :test_unit"
118
123
  end
119
124
  end
120
125
  end
@@ -30,7 +30,6 @@ describe "Descendants tracking" do
30
30
 
31
31
  context "with ActiveSupport::DescendantsTracker (cache_classes: true)" do
32
32
  before do
33
- expect(ActiveSupport::DescendantsTracker.clear_disabled).to be_falsey
34
33
  expect { ActiveSupport::DescendantsTracker.clear([]) }.not_to raise_exception
35
34
  end
36
35
 
@@ -40,10 +39,29 @@ describe "Descendants tracking" do
40
39
  context "without ActiveSupport::DescendantsTracker (cache_classes: false)" do
41
40
  before do
42
41
  ActiveSupport::DescendantsTracker.disable_clear!
43
- expect(ActiveSupport::DescendantsTracker.clear_disabled).to be_truthy
44
42
  expect { ActiveSupport::DescendantsTracker.clear([]) }.to raise_exception(RuntimeError)
45
43
  end
46
44
 
47
45
  include_examples "clearing descendants between test runs"
48
46
  end
47
+
48
+ it "keeps filtering ahead of later class method extensions" do
49
+ raw_subclasses = Class.instance_method(:subclasses).super_method
50
+ raw_descendants = Class.instance_method(:descendants).super_method
51
+ override = Module.new do
52
+ define_method(:subclasses) { raw_subclasses.bind_call(self) }
53
+ define_method(:descendants) { raw_descendants.bind_call(self) }
54
+ end
55
+ destroyed_model = stub_const("DestroyedModel", Class.new(ActiveRecord::Base))
56
+ WithModel::DescendantsTracker.clear([destroyed_model])
57
+
58
+ ActiveRecord::Base.extend override
59
+
60
+ expect(ActiveRecord::Base.descendants).not_to include(destroyed_model)
61
+ ensure
62
+ override&.module_eval do
63
+ remove_method :subclasses
64
+ remove_method :descendants
65
+ end
66
+ end
49
67
  end
data/spec/readme_spec.rb CHANGED
@@ -145,6 +145,26 @@ describe "with_model supports Single Table Inheritance" do
145
145
  end
146
146
  end
147
147
 
148
+ describe "with_model supports existing tables" do
149
+ with_table :legendary_creatures do |t|
150
+ t.string "name"
151
+ end
152
+
153
+ with_model :Phoenix do
154
+ table(false)
155
+
156
+ model do
157
+ self.table_name = "legendary_creatures"
158
+ end
159
+ end
160
+
161
+ it "uses the table it names" do
162
+ Phoenix.create!(name: "Fawkes")
163
+
164
+ expect(Phoenix.table_name).to eq "legendary_creatures"
165
+ end
166
+ end
167
+
148
168
  describe "with_model supports foreign keys" do
149
169
  with_model :Author do
150
170
  table
@@ -0,0 +1,153 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "open3"
4
+ require "rbconfig"
5
+ require "tmpdir"
6
+
7
+ RSpec.describe "runner integration entrypoints" do
8
+ def run_ruby(source, load_paths: ["lib"])
9
+ Open3.capture3(
10
+ RbConfig.ruby,
11
+ *load_paths.map { |path| "-I#{path}" },
12
+ "-e",
13
+ source
14
+ )
15
+ end
16
+
17
+ def expect_native_runner_to_pass(source, load_paths: ["lib"])
18
+ stdout, stderr, status = run_ruby(source, load_paths:)
19
+
20
+ expect(status).to be_success, <<~MESSAGE
21
+ subprocess failed with status #{status.exitstatus}
22
+ stdout:
23
+ #{stdout}
24
+ stderr:
25
+ #{stderr}
26
+ MESSAGE
27
+ end
28
+
29
+ it "configures RSpec and extends new example groups" do
30
+ expect_native_runner_to_pass(<<~'RUBY')
31
+ require "with_model/rspec"
32
+ abort "runner: #{WithModel.runner.inspect}" unless WithModel.runner == :rspec
33
+
34
+ RSpec.describe "entrypoint" do
35
+ it "installs the DSL" do
36
+ expect(self.class).to respond_to(:with_model)
37
+ end
38
+ end
39
+
40
+ exit RSpec::Core::Runner.run([])
41
+ RUBY
42
+ end
43
+
44
+ it "configures Minitest and extends new test subclasses" do
45
+ expect_native_runner_to_pass(<<~'RUBY')
46
+ require "with_model/minitest"
47
+ abort "runner: #{WithModel.runner.inspect}" unless WithModel.runner == :minitest
48
+
49
+ # Assert that minitest/autorun is not loaded
50
+ autorun_loaded = $LOADED_FEATURES.any? { |f| f.end_with?("/minitest/autorun.rb") }
51
+ abort "autorun should not be loaded; loaded features: #{$LOADED_FEATURES.inspect}" if autorun_loaded
52
+
53
+ class MinitestEntrypointTest < Minitest::Test
54
+ def test_installs_the_dsl
55
+ assert_respond_to self.class, :with_model
56
+ end
57
+ end
58
+
59
+ exit(Minitest.run ? 0 : 1)
60
+ RUBY
61
+ end
62
+
63
+ it "configures Test::Unit and extends new test subclasses" do
64
+ expect_native_runner_to_pass(<<~'RUBY')
65
+ require "with_model/test_unit"
66
+ abort "runner: #{WithModel.runner.inspect}" unless WithModel.runner == :test_unit
67
+
68
+ class TestUnitEntrypointTest < Test::Unit::TestCase
69
+ def test_installs_the_dsl
70
+ assert_respond_to self.class, :with_model
71
+ end
72
+ end
73
+ RUBY
74
+ end
75
+
76
+ it "propagates Test::Unit's LoadError when the framework is unavailable" do
77
+ Dir.mktmpdir do |directory|
78
+ Dir.mkdir(File.join(directory, "test"))
79
+ File.write(
80
+ File.join(directory, "test/unit.rb"),
81
+ 'raise LoadError, "distinctive test/unit LoadError"'
82
+ )
83
+
84
+ expect_native_runner_to_pass(<<~'RUBY', load_paths: [directory, "lib"])
85
+ begin
86
+ require "with_model/test_unit"
87
+ rescue LoadError => error
88
+ abort "unexpected LoadError: #{error.message.inspect}" unless
89
+ error.message == "distinctive test/unit LoadError"
90
+ else
91
+ abort "expected a LoadError"
92
+ end
93
+ RUBY
94
+ end
95
+ end
96
+
97
+ it "retains manual Test::Unit setup with plain with_model" do
98
+ expect_native_runner_to_pass(<<~'RUBY')
99
+ require "test/unit"
100
+ require "with_model"
101
+
102
+ WithModel.runner = :test_unit
103
+ Test::Unit::TestCase.extend WithModel
104
+ abort "runner: #{WithModel.runner.inspect}" unless WithModel.runner == :test_unit
105
+
106
+ class PlainWithModelTestUnitTest < Test::Unit::TestCase
107
+ def test_installs_the_dsl
108
+ assert_respond_to self.class, :with_model
109
+ end
110
+ end
111
+ RUBY
112
+ end
113
+
114
+ it "retains manual RSpec setup with plain with_model" do
115
+ expect_native_runner_to_pass(<<~'RUBY')
116
+ require "rspec"
117
+ require "with_model"
118
+
119
+ WithModel.runner = :rspec
120
+ RSpec.configure do |config|
121
+ config.extend WithModel
122
+ end
123
+ abort "runner: #{WithModel.runner.inspect}" unless WithModel.runner == :rspec
124
+
125
+ RSpec.describe "entrypoint" do
126
+ it "installs the DSL" do
127
+ expect(self.class).to respond_to(:with_model)
128
+ end
129
+ end
130
+
131
+ exit RSpec::Core::Runner.run([])
132
+ RUBY
133
+ end
134
+
135
+ it "retains manual Minitest setup with plain with_model" do
136
+ expect_native_runner_to_pass(<<~'RUBY')
137
+ require "minitest"
138
+ require "with_model"
139
+
140
+ WithModel.runner = :minitest
141
+ Minitest::Test.extend WithModel
142
+ abort "runner: #{WithModel.runner.inspect}" unless WithModel.runner == :minitest
143
+
144
+ class PlainWithModelMinitestTest < Minitest::Test
145
+ def test_installs_the_dsl
146
+ assert_respond_to self.class, :with_model
147
+ end
148
+ end
149
+
150
+ exit(Minitest.run ? 0 : 1)
151
+ RUBY
152
+ end
153
+ end
@@ -21,10 +21,6 @@ RSpec.describe "table(false)" do
21
21
  classes = @stubbed_active_record_classes
22
22
  next unless classes
23
23
 
24
- ActiveSupport::DescendantsTracker.clear(classes) \
25
- unless ActiveSupport::DescendantsTracker.clear_disabled
26
- ActiveSupport::Dependencies::Reference.clear! \
27
- if defined?(ActiveSupport::Dependencies::Reference)
28
24
  WithModel::DescendantsTracker.clear(classes)
29
25
  end
30
26
 
@@ -437,6 +433,135 @@ RSpec.describe "table(false)" do
437
433
  end
438
434
  end
439
435
 
436
+ # Not single table inheritance at all: the model is its own base class and
437
+ # names an existing table, such as a legacy table or one several models share.
438
+ describe "with a model that sets its own table_name" do
439
+ with_table :legendary_creatures do |t|
440
+ t.string :name
441
+ end
442
+
443
+ # Declared between the two so that it runs after the model is torn down but
444
+ # before the table is dropped.
445
+ after do
446
+ expect(ActiveRecord::Base.connection.select_value("SELECT COUNT(*) FROM legendary_creatures"))
447
+ .to eq @rows_written.to_i
448
+ end
449
+
450
+ with_model :Phoenix do
451
+ table(false)
452
+ model do
453
+ self.table_name = "legendary_creatures"
454
+ end
455
+ end
456
+
457
+ it "uses the table it names" do
458
+ Phoenix.create!(name: "Fawkes")
459
+ @rows_written = 1
460
+
461
+ expect(Phoenix.table_name).to eq "legendary_creatures"
462
+ expect(Phoenix.count).to eq 1
463
+ end
464
+
465
+ it "creates no table of its own" do
466
+ expect(ActiveRecord::Base.connection.tables.grep(/with_model_phoenix/)).to be_empty
467
+ end
468
+
469
+ it "is its own base class" do
470
+ expect(Phoenix.base_class).to eq Phoenix
471
+ end
472
+
473
+ # The table belongs to the suite, which is left to clean it up - with
474
+ # transactional tests, or however it cleans up its other tables. Deleting by
475
+ # type would fail outright here, since there is no type column.
476
+ it "leaves its rows alone when torn down" do
477
+ Phoenix.create!(name: "Fawkes")
478
+ Phoenix.create!(name: "Buckbeak")
479
+ @rows_written = 2
480
+ end
481
+
482
+ # An abstract model needs no table, so `table(false)` suits it too.
483
+ context "with an abstract superclass that has no table" do
484
+ with_model :ApplicationRecord do
485
+ table(false)
486
+ model do
487
+ self.abstract_class = true
488
+ end
489
+ end
490
+
491
+ with_model :Griffin, superclass: :ApplicationRecord do
492
+ table(false)
493
+ model do
494
+ self.table_name = "legendary_creatures"
495
+ end
496
+ end
497
+
498
+ it "uses the table it names" do
499
+ Griffin.create!(name: "Buckbeak")
500
+ @rows_written = 1
501
+
502
+ expect(Griffin.table_name).to eq "legendary_creatures"
503
+ expect(Griffin.count).to eq 1
504
+ end
505
+
506
+ it "leaves the abstract superclass without a table" do
507
+ expect(ApplicationRecord.table_name).to be_nil
508
+ end
509
+ end
510
+
511
+ # Active Record never applies single table inheritance below an abstract
512
+ # class, so its table needs no inheritance column.
513
+ context "with an abstract superclass that has a table" do
514
+ with_model :LegendaryCreature do
515
+ table
516
+ model do
517
+ self.abstract_class = true
518
+ end
519
+ end
520
+
521
+ with_model :Sphinx, superclass: :LegendaryCreature do
522
+ table(false)
523
+ model do
524
+ self.table_name = "legendary_creatures"
525
+ end
526
+ end
527
+
528
+ it "uses the table it names" do
529
+ Sphinx.create!(name: "Phix")
530
+ @rows_written = 1
531
+
532
+ expect(Sphinx.table_name).to eq "legendary_creatures"
533
+ expect(Sphinx.count).to eq 1
534
+ end
535
+
536
+ it "is its own base class" do
537
+ expect(Sphinx.base_class).to eq Sphinx
538
+ end
539
+ end
540
+ end
541
+
542
+ # Active Record's naming convention picks the table, so the model block need
543
+ # not name it.
544
+ describe "with a model named after an existing table" do
545
+ with_table :dragons do |t|
546
+ t.string :name
547
+ end
548
+
549
+ with_model :Dragon do
550
+ table(false)
551
+ end
552
+
553
+ it "uses that table" do
554
+ Dragon.create!(name: "Norbert")
555
+
556
+ expect(Dragon.table_name).to eq "dragons"
557
+ expect(Dragon.count).to eq 1
558
+ end
559
+
560
+ it "creates no table of its own" do
561
+ expect(ActiveRecord::Base.connection.tables.grep(/with_model_dragon/)).to be_empty
562
+ end
563
+ end
564
+
440
565
  describe "invalid configurations" do
441
566
  # Creates immediately the same tableless subclass normally generated by:
442
567
  #
@@ -465,6 +590,46 @@ RSpec.describe "table(false)" do
465
590
  .to raise_error(WithModel::InvalidSuperclass, /ActiveRecord::Base has none to inherit/)
466
591
  end
467
592
 
593
+ it "names the table it looked for" do
594
+ expect { create_tableless_subclass(superclass: ActiveRecord::Base) }
595
+ .to raise_error(WithModel::InvalidSuperclass, /there is no "tableless_subclasses" table/)
596
+ end
597
+
598
+ it "suggests creating that table first or naming another" do
599
+ expect { create_tableless_subclass(superclass: ActiveRecord::Base) }
600
+ .to raise_error(
601
+ WithModel::InvalidSuperclass,
602
+ /create it before the model or set `self.table_name` in its model block/
603
+ )
604
+ end
605
+
606
+ # Builds the model by hand so that, should the check let it through, the
607
+ # example can still destroy it rather than leak its constant.
608
+ it "looks for the table the model block names" do
609
+ subclass = WithModel::Model.new(:TablelessSubclass, superclass: ActiveRecord::Base)
610
+ WithModel::Model::DSL.new(subclass).instance_eval do
611
+ table(false)
612
+ model { self.table_name = "missing_furniture" }
613
+ end
614
+
615
+ begin
616
+ expect { subclass.create }
617
+ .to raise_error(WithModel::InvalidSuperclass, /there is no "missing_furniture" table/)
618
+ ensure
619
+ subclass.destroy if Object.const_defined?(:TablelessSubclass, false)
620
+ end
621
+ end
622
+
623
+ # The check needs the model block to have run, so the constant already
624
+ # exists when it fails. A test runner tears down after a failed setup, but
625
+ # nothing else does, and a leftover constant would leak into later tests.
626
+ it "leaves no constant behind" do
627
+ expect { create_tableless_subclass(superclass: ActiveRecord::Base) }
628
+ .to raise_error(WithModel::InvalidSuperclass)
629
+
630
+ expect(Object.const_defined?(:TablelessSubclass, false)).to be false
631
+ end
632
+
468
633
  context "with an abstract superclass" do
469
634
  with_model :TablelessSuperclass do
470
635
  table
@@ -0,0 +1,37 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "spec_helper"
4
+
5
+ RSpec.describe WithModel::Table do
6
+ let(:connection_owner) do
7
+ klass = Class.new(ActiveRecord::Base)
8
+ stub_const("TableSpecConnection", klass)
9
+ klass.abstract_class = true
10
+ klass.establish_connection(adapter: "sqlite3", database: ":memory:")
11
+ klass
12
+ end
13
+ let(:connection) { connection_owner.connection }
14
+ let(:table) { described_class.new(:temporary_records, connection:) }
15
+
16
+ after { connection_owner.connection_pool.disconnect! }
17
+
18
+ describe "#destroy" do
19
+ it "does not raise after its connection reconnects before teardown" do
20
+ table.create
21
+
22
+ connection.disconnect!
23
+ connection.reconnect!
24
+
25
+ expect(connection.data_source_exists?(:temporary_records)).to be false
26
+ expect { table.destroy }.not_to raise_error
27
+ end
28
+
29
+ it "drops an existing table" do
30
+ table.create
31
+
32
+ table.destroy
33
+
34
+ expect(connection.data_source_exists?(:temporary_records)).to be false
35
+ end
36
+ end
37
+ end