with_model 2.3.0 → 2.4.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.
- checksums.yaml +4 -4
- data/.github/workflows/ci.yml +0 -12
- data/CHANGELOG.md +17 -0
- data/CONTRIBUTING.md +20 -3
- data/Gemfile +1 -0
- data/README.md +47 -10
- data/Rakefile +7 -1
- data/docs/minitest.md +45 -0
- data/docs/rspec.md +43 -0
- data/docs/test-unit.md +73 -0
- data/lib/with_model/descendants_tracker.rb +3 -11
- data/lib/with_model/minitest.rb +7 -0
- data/lib/with_model/model.rb +0 -13
- data/lib/with_model/null_table.rb +11 -11
- data/lib/with_model/rspec.rb +10 -0
- data/lib/with_model/table.rb +2 -8
- data/lib/with_model/test_unit.rb +7 -0
- data/lib/with_model/version.rb +1 -1
- data/lib/with_model.rb +6 -1
- data/spec/descendants_tracking_spec.rb +20 -2
- data/spec/runner_entrypoints_spec.rb +153 -0
- data/spec/table_false_spec.rb +500 -348
- data/spec/table_spec.rb +37 -0
- data/spec/with_model_spec.rb +2 -2
- data/test_unit/lifecycle_test.rb +186 -0
- data/test_unit/sti_test.rb +71 -0
- data/test_unit/test_unit_helper.rb +15 -0
- data/test_unit/with_model_test.rb +58 -0
- data/with_model.gemspec +3 -2
- metadata +20 -6
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: f788d32a4cd252a5254b82928cab9b62b66cfd80e00dd010bdb901b60a6ddc6c
|
|
4
|
+
data.tar.gz: 2c44d64d8f3225e809c113894ec77df2e64c6dae513a12065e171bd892c80c7d
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: bbf514f03d362582bf15cd38301a9f2929c559ea702c283bae2c01720f48e3f613e93ce1580906983d567048d7241d70af92b94f54f4d992da06f3497c9afc8e
|
|
7
|
+
data.tar.gz: 66200b79760795f6d3e622b4b29c13285fb042e4e168f1fea34ac38161bca41e4330a451add6f9a0db777dd20cbde97567cd687c811dff1675a1090a24dc7704
|
data/.github/workflows/ci.yml
CHANGED
|
@@ -29,8 +29,6 @@ jobs:
|
|
|
29
29
|
matrix:
|
|
30
30
|
ruby-version: ["3.3", "3.4", "4.0"]
|
|
31
31
|
active-record:
|
|
32
|
-
- label: AR 7.2
|
|
33
|
-
env: ACTIVE_RECORD_VERSION=~> 7.2.0
|
|
34
32
|
- label: AR 8.0
|
|
35
33
|
env: ACTIVE_RECORD_VERSION=~> 8.0.0
|
|
36
34
|
- label: AR 8.1
|
|
@@ -52,11 +50,6 @@ jobs:
|
|
|
52
50
|
label: AR 8-0-stable
|
|
53
51
|
env: ACTIVE_RECORD_BRANCH=8-0-stable
|
|
54
52
|
allow-failure: true
|
|
55
|
-
- ruby-version: "4.0"
|
|
56
|
-
active-record:
|
|
57
|
-
label: AR 7-2-stable
|
|
58
|
-
env: ACTIVE_RECORD_BRANCH=7-2-stable
|
|
59
|
-
allow-failure: true
|
|
60
53
|
# A canary for the next Ruby, which cannot fail the build.
|
|
61
54
|
- ruby-version: ruby-head
|
|
62
55
|
active-record:
|
|
@@ -78,11 +71,6 @@ jobs:
|
|
|
78
71
|
ruby-version: ${{ matrix.ruby-version }}
|
|
79
72
|
bundler-cache: true
|
|
80
73
|
cache-version: ${{ matrix.active-record.env }}
|
|
81
|
-
# Gemfile.lock is not checked in, so this resolves the newest gems that
|
|
82
|
-
# satisfy the Gemfile on each run rather than a recorded set. `--all` is
|
|
83
|
-
# what Bundler 4, shipped with Ruby 4.0, wants for that.
|
|
84
|
-
- name: Update bundle
|
|
85
|
-
run: bundle update --all
|
|
86
74
|
# Not bin/rake: bin/ holds generated binstubs and is not checked in, and
|
|
87
75
|
# whether bundling writes them there varies by Bundler version - Bundler 4
|
|
88
76
|
# does not, which left Ruby 4.0 with no binstub to run.
|
data/CHANGELOG.md
CHANGED
|
@@ -1,3 +1,20 @@
|
|
|
1
|
+
### Unreleased
|
|
2
|
+
|
|
3
|
+
### 2.4.0
|
|
4
|
+
|
|
5
|
+
- Add Test::Unit support, including lifecycle integration for dynamic models and
|
|
6
|
+
tables.
|
|
7
|
+
- Add `with_model/rspec`, `with_model/minitest`, and `with_model/test_unit`
|
|
8
|
+
convenience entrypoints.
|
|
9
|
+
- Add runner integration guides for RSpec, Minitest, and Test::Unit.
|
|
10
|
+
- Skip temporary-table teardown when the table has already disappeared.
|
|
11
|
+
- Require Active Record 8.0 or later.
|
|
12
|
+
|
|
13
|
+
### 2.3.1
|
|
14
|
+
|
|
15
|
+
- Honor Active Record destruction callbacks and dependent associations when
|
|
16
|
+
cleaning up rows for a `table(false)` model.
|
|
17
|
+
|
|
1
18
|
### 2.3.0
|
|
2
19
|
|
|
3
20
|
- Add `table(false)` to create no table, so that a model whose superclass is a
|
data/CONTRIBUTING.md
CHANGED
|
@@ -24,12 +24,22 @@ avoid anything that only works on the newest of either.
|
|
|
24
24
|
|
|
25
25
|
## Running the tests
|
|
26
26
|
|
|
27
|
-
The suite runs against
|
|
28
|
-
under each:
|
|
27
|
+
The suite runs against all three supported test harnesses, so `with_model` has
|
|
28
|
+
to work under each:
|
|
29
29
|
|
|
30
30
|
- `spec/` covers behavior under RSpec.
|
|
31
|
-
- `test/` covers the
|
|
31
|
+
- `test/` covers the Minitest life cycle — the setup and teardown hooks, and
|
|
32
32
|
their ordering — rather than repeating the specs.
|
|
33
|
+
- `test_unit/` covers Test::Unit integration in a dedicated process, including
|
|
34
|
+
native fixture callback ordering.
|
|
35
|
+
|
|
36
|
+
Run one harness with its scoped Rake task:
|
|
37
|
+
|
|
38
|
+
```sh
|
|
39
|
+
bundle exec rake spec
|
|
40
|
+
bundle exec rake test
|
|
41
|
+
bundle exec rake test_unit
|
|
42
|
+
```
|
|
33
43
|
|
|
34
44
|
Run everything, including the linter, with:
|
|
35
45
|
|
|
@@ -37,6 +47,13 @@ Run everything, including the linter, with:
|
|
|
37
47
|
bundle exec rake
|
|
38
48
|
```
|
|
39
49
|
|
|
50
|
+
Validate the README and runner guides with RDoc before submitting documentation
|
|
51
|
+
changes:
|
|
52
|
+
|
|
53
|
+
```sh
|
|
54
|
+
rm -rf tmp/rdoc && bundle exec rdoc --markup markdown --op tmp/rdoc README.md docs/rspec.md docs/minitest.md docs/test-unit.md lib/**/*.rb
|
|
55
|
+
```
|
|
56
|
+
|
|
40
57
|
`bin/` holds generated binstubs and is not checked in. Whether bundling writes
|
|
41
58
|
them for you depends on your Bundler version, so create them once if you would
|
|
42
59
|
rather type `bin/rake`:
|
data/Gemfile
CHANGED
data/README.md
CHANGED
|
@@ -14,32 +14,63 @@
|
|
|
14
14
|
|
|
15
15
|
Install as usual: `gem install with_model` or add `gem 'with_model'` to your Gemfile. See [`.github/workflows/ci.yml`](./.github/workflows/ci.yml) for supported (tested) Ruby versions.
|
|
16
16
|
|
|
17
|
+
## Test runner setup
|
|
18
|
+
|
|
19
|
+
Choose the entrypoint for the test runner that owns the declarations. Each
|
|
20
|
+
performs complete global setup for one runner; see its guide for lifecycle and
|
|
21
|
+
manual-setup details.
|
|
22
|
+
|
|
17
23
|
### RSpec
|
|
18
24
|
|
|
19
|
-
|
|
25
|
+
```ruby
|
|
26
|
+
require "with_model/rspec"
|
|
27
|
+
```
|
|
28
|
+
|
|
29
|
+
See the [RSpec guide](docs/rspec.md).
|
|
30
|
+
|
|
31
|
+
### Minitest
|
|
20
32
|
|
|
21
33
|
```ruby
|
|
22
|
-
require "with_model"
|
|
34
|
+
require "with_model/minitest"
|
|
35
|
+
```
|
|
23
36
|
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
37
|
+
See the [Minitest guide](docs/minitest.md).
|
|
38
|
+
|
|
39
|
+
### Test::Unit
|
|
40
|
+
|
|
41
|
+
```ruby
|
|
42
|
+
require "with_model/test_unit"
|
|
27
43
|
```
|
|
28
44
|
|
|
29
|
-
|
|
45
|
+
See the [Test::Unit guide](docs/test-unit.md).
|
|
30
46
|
|
|
31
|
-
|
|
47
|
+
### Advanced and mixed-runner setup
|
|
48
|
+
|
|
49
|
+
Plain `require "with_model"` remains framework-neutral. For manual setup,
|
|
50
|
+
select a runner and extend its test context directly:
|
|
32
51
|
|
|
33
52
|
```ruby
|
|
53
|
+
require "minitest"
|
|
34
54
|
require "with_model"
|
|
35
55
|
|
|
36
56
|
WithModel.runner = :minitest
|
|
57
|
+
Minitest::Test.extend WithModel
|
|
58
|
+
```
|
|
37
59
|
|
|
38
|
-
|
|
39
|
-
|
|
60
|
+
RSpec uses `RSpec.configure { |config| config.extend WithModel }`; Test::Unit
|
|
61
|
+
uses `Test::Unit::TestCase.extend WithModel`. A declaration can override the
|
|
62
|
+
global runner when needed:
|
|
63
|
+
|
|
64
|
+
```ruby
|
|
65
|
+
with_model :Post, runner: :test_unit do
|
|
66
|
+
table
|
|
40
67
|
end
|
|
41
68
|
```
|
|
42
69
|
|
|
70
|
+
Loading multiple convenience entrypoints in one process is unsupported because
|
|
71
|
+
the last one changes the global runner. For mixed runners, use plain
|
|
72
|
+
`with_model`, direct extension, and explicit `runner:` options.
|
|
73
|
+
|
|
43
74
|
## Usage
|
|
44
75
|
|
|
45
76
|
After setting up as above, call `with_model` and inside its block pass it a `table` block and a `model` block.
|
|
@@ -252,7 +283,13 @@ the order they are declared, and destroyed in the reverse order.
|
|
|
252
283
|
|
|
253
284
|
## Requirements
|
|
254
285
|
|
|
255
|
-
See the [gemspec metadata](https://rubygems.org/gems/with_model) for
|
|
286
|
+
See the [gemspec metadata](https://rubygems.org/gems/with_model) for Ruby and
|
|
287
|
+
Active Record requirements. RSpec, Minitest, and Test::Unit are optional
|
|
288
|
+
consumer framework dependencies; install the framework used by its selected
|
|
289
|
+
entrypoint. Requiring a selected entrypoint without its framework installed
|
|
290
|
+
raises the framework's ordinary `LoadError`.
|
|
291
|
+
|
|
292
|
+
`scope:` is RSpec-only and is silently ignored by Minitest and Test::Unit.
|
|
256
293
|
|
|
257
294
|
## Thread-safety
|
|
258
295
|
|
data/Rakefile
CHANGED
|
@@ -8,7 +8,13 @@ RSpec::Core::RakeTask.new
|
|
|
8
8
|
require "minitest/test_task"
|
|
9
9
|
Minitest::TestTask.create
|
|
10
10
|
|
|
11
|
+
require "rake/testtask"
|
|
12
|
+
Rake::TestTask.new(:test_unit) do |task|
|
|
13
|
+
task.libs << "lib" << "test_unit" << "test"
|
|
14
|
+
task.pattern = "test_unit/**/*_test.rb"
|
|
15
|
+
end
|
|
16
|
+
|
|
11
17
|
# standard rake task
|
|
12
18
|
require "standard/rake"
|
|
13
19
|
|
|
14
|
-
task default: %i[spec test standard]
|
|
20
|
+
task default: %i[spec test test_unit standard]
|
data/docs/minitest.md
ADDED
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
# Minitest integration
|
|
2
|
+
|
|
3
|
+
Load the Minitest entrypoint before defining tests:
|
|
4
|
+
|
|
5
|
+
```ruby
|
|
6
|
+
require "with_model/minitest"
|
|
7
|
+
```
|
|
8
|
+
|
|
9
|
+
This requires `minitest`, then `with_model`, selects the
|
|
10
|
+
`:minitest` runner, and extends `Minitest::Test`. Its class-level DSL is
|
|
11
|
+
inherited by `Minitest::Test` subclasses, including `Minitest::Spec` and
|
|
12
|
+
`ActiveSupport::TestCase`. The entrypoint does not require or start Minitest's
|
|
13
|
+
autorun — your process or test framework owns Minitest startup.
|
|
14
|
+
|
|
15
|
+
## Lifecycle
|
|
16
|
+
|
|
17
|
+
The Minitest runner creates declared models and tables in `before_setup` and
|
|
18
|
+
destroys them in `after_teardown`. This makes them available to ordinary setup
|
|
19
|
+
methods and keeps them available through teardown. `scope:` is silently ignored
|
|
20
|
+
by Minitest because it is an RSpec-only option.
|
|
21
|
+
|
|
22
|
+
## Manual setup
|
|
23
|
+
|
|
24
|
+
If another process or tool owns Minitest configuration, configure manually:
|
|
25
|
+
|
|
26
|
+
```ruby
|
|
27
|
+
require "minitest"
|
|
28
|
+
require "with_model"
|
|
29
|
+
|
|
30
|
+
WithModel.runner = :minitest
|
|
31
|
+
Minitest::Test.extend WithModel
|
|
32
|
+
```
|
|
33
|
+
|
|
34
|
+
Minitest is an optional consumer dependency. Requiring `with_model/minitest`
|
|
35
|
+
without Minitest installed raises Minitest's ordinary `LoadError`.
|
|
36
|
+
|
|
37
|
+
## Mixed runners
|
|
38
|
+
|
|
39
|
+
Do not load multiple convenience entrypoints in one process: each changes the
|
|
40
|
+
global runner, and the last one wins. For mixed runners, load plain
|
|
41
|
+
`with_model`, extend each framework boundary directly, and select a runner per
|
|
42
|
+
declaration with `runner:`.
|
|
43
|
+
|
|
44
|
+
See the [general DSL reference](../README.md#usage) for declaring models and
|
|
45
|
+
tables.
|
data/docs/rspec.md
ADDED
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
# RSpec integration
|
|
2
|
+
|
|
3
|
+
Load the RSpec entrypoint before defining example groups:
|
|
4
|
+
|
|
5
|
+
```ruby
|
|
6
|
+
require "with_model/rspec"
|
|
7
|
+
```
|
|
8
|
+
|
|
9
|
+
The entrypoint requires `rspec/core` and `with_model`, selects the `:rspec`
|
|
10
|
+
runner, and extends RSpec example groups through `RSpec.configure`. It does not
|
|
11
|
+
start RSpec; the RSpec command-line runner remains in control.
|
|
12
|
+
|
|
13
|
+
## Lifecycle
|
|
14
|
+
|
|
15
|
+
`with_model` and `with_table` register RSpec `before` and `after` hooks. Models
|
|
16
|
+
and tables are available to examples after setup and are removed after cleanup.
|
|
17
|
+
Use `scope:` when RSpec hook scope is needed, including `scope: :all` for a
|
|
18
|
+
model needed by `before(:all)`. `scope:` is an RSpec-only option.
|
|
19
|
+
|
|
20
|
+
## Manual setup
|
|
21
|
+
|
|
22
|
+
For runner-owned setup, require the framework and `with_model` separately:
|
|
23
|
+
|
|
24
|
+
```ruby
|
|
25
|
+
require "rspec/core"
|
|
26
|
+
require "with_model"
|
|
27
|
+
|
|
28
|
+
WithModel.runner = :rspec
|
|
29
|
+
RSpec.configure { |config| config.extend WithModel }
|
|
30
|
+
```
|
|
31
|
+
|
|
32
|
+
RSpec is an optional consumer dependency. Requiring `with_model/rspec` without
|
|
33
|
+
RSpec installed raises RSpec's ordinary `LoadError`.
|
|
34
|
+
|
|
35
|
+
## Mixed runners
|
|
36
|
+
|
|
37
|
+
Do not load multiple convenience entrypoints in one process: each changes the
|
|
38
|
+
global runner, and the last one wins. For mixed runners, load plain
|
|
39
|
+
`with_model`, extend each framework boundary directly, and select a runner per
|
|
40
|
+
declaration with `runner:`.
|
|
41
|
+
|
|
42
|
+
See the [general DSL reference](../README.md#usage) for declaring models and
|
|
43
|
+
tables.
|
data/docs/test-unit.md
ADDED
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
# Test::Unit integration
|
|
2
|
+
|
|
3
|
+
Load the Test::Unit entrypoint before defining tests:
|
|
4
|
+
|
|
5
|
+
```ruby
|
|
6
|
+
require "with_model/test_unit"
|
|
7
|
+
```
|
|
8
|
+
|
|
9
|
+
The entrypoint requires `test/unit`, then `with_model`, selects the
|
|
10
|
+
`:test_unit` runner, and extends `Test::Unit::TestCase`. Test::Unit retains
|
|
11
|
+
ownership of autorun and fixture execution.
|
|
12
|
+
|
|
13
|
+
## Lifecycle
|
|
14
|
+
|
|
15
|
+
`with_model` and `with_table` register Test::Unit fixture callbacks with exact
|
|
16
|
+
placement: setup uses `before: :append`, while teardown uses `after: :prepend`.
|
|
17
|
+
Declared models and tables therefore create in declaration order before user or
|
|
18
|
+
inherited setup methods and callbacks. They destroy in reverse order after user
|
|
19
|
+
or inherited teardown methods and callbacks.
|
|
20
|
+
|
|
21
|
+
Test::Unit runs teardown when setup raises. `WithModel::Model#destroy`
|
|
22
|
+
defensively handles creation that never assigned a model. `Table#destroy` checks
|
|
23
|
+
whether its table exists before dropping it, so teardown does not raise solely
|
|
24
|
+
because a reconnect replaced an in-memory database or the table otherwise
|
|
25
|
+
already disappeared. It does not rescue other database, connection, or drop
|
|
26
|
+
errors; those still propagate. This conditional drop check does not promise
|
|
27
|
+
generic partial-creation recovery for bare `with_table`. `scope:` is silently
|
|
28
|
+
ignored by Test::Unit because it is an RSpec-only option.
|
|
29
|
+
|
|
30
|
+
## Errata and concerns
|
|
31
|
+
|
|
32
|
+
Establish the Active Record connection before `with_model`'s setup callback
|
|
33
|
+
creates tables and models. An ordinary `def setup` runs too late. Connect in
|
|
34
|
+
your test helper before test classes load, or register a connection callback
|
|
35
|
+
with `setup(before: :prepend)`.
|
|
36
|
+
|
|
37
|
+
```ruby
|
|
38
|
+
class PostTest < Test::Unit::TestCase
|
|
39
|
+
setup(before: :prepend) do
|
|
40
|
+
ActiveRecord::Base.establish_connection(adapter: "sqlite3", database: ":memory:")
|
|
41
|
+
end
|
|
42
|
+
|
|
43
|
+
with_model :Post do
|
|
44
|
+
table
|
|
45
|
+
end
|
|
46
|
+
end
|
|
47
|
+
```
|
|
48
|
+
|
|
49
|
+
## Manual setup
|
|
50
|
+
|
|
51
|
+
For runner-owned setup, require Test::Unit and `with_model` separately:
|
|
52
|
+
|
|
53
|
+
```ruby
|
|
54
|
+
require "test/unit"
|
|
55
|
+
require "with_model"
|
|
56
|
+
|
|
57
|
+
WithModel.runner = :test_unit
|
|
58
|
+
Test::Unit::TestCase.extend WithModel
|
|
59
|
+
```
|
|
60
|
+
|
|
61
|
+
Test::Unit is an optional consumer dependency. Requiring
|
|
62
|
+
`with_model/test_unit` without Test::Unit installed raises Test::Unit's ordinary
|
|
63
|
+
`LoadError`.
|
|
64
|
+
|
|
65
|
+
## Mixed runners
|
|
66
|
+
|
|
67
|
+
Do not load multiple convenience entrypoints in one process: each changes the
|
|
68
|
+
global runner, and the last one wins. For mixed runners, load plain
|
|
69
|
+
`with_model`, extend each framework boundary directly, and select a runner per
|
|
70
|
+
declaration with `runner:`.
|
|
71
|
+
|
|
72
|
+
See the [general DSL reference](../README.md#usage) for declaring models and
|
|
73
|
+
tables.
|
|
@@ -74,14 +74,6 @@ module WithModel
|
|
|
74
74
|
end
|
|
75
75
|
end
|
|
76
76
|
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
module ActiveSupport
|
|
82
|
-
module DescendantsTracker
|
|
83
|
-
class << self
|
|
84
|
-
attr_reader :clear_disabled
|
|
85
|
-
end
|
|
86
|
-
end
|
|
87
|
-
end
|
|
77
|
+
ActiveRecord::Base.singleton_class.prepend(
|
|
78
|
+
WithModel::DescendantsTracker::DestroyedClassesFiltering
|
|
79
|
+
)
|
data/lib/with_model/model.rb
CHANGED
|
@@ -74,8 +74,6 @@ module WithModel
|
|
|
74
74
|
# can only do so while the class still has its name.
|
|
75
75
|
table.teardown(@model)
|
|
76
76
|
stubber.unstub_const
|
|
77
|
-
cleanup_descendants_tracking
|
|
78
|
-
reset_dependencies_cache
|
|
79
77
|
WithModel::DescendantsTracker.clear([@model])
|
|
80
78
|
@model = nil
|
|
81
79
|
end
|
|
@@ -132,17 +130,6 @@ module WithModel
|
|
|
132
130
|
@model.reset_column_information
|
|
133
131
|
end
|
|
134
132
|
|
|
135
|
-
def cleanup_descendants_tracking
|
|
136
|
-
ActiveSupport::DescendantsTracker.clear([@model]) \
|
|
137
|
-
unless ActiveSupport::DescendantsTracker.clear_disabled
|
|
138
|
-
end
|
|
139
|
-
|
|
140
|
-
def reset_dependencies_cache
|
|
141
|
-
return unless defined?(ActiveSupport::Dependencies::Reference)
|
|
142
|
-
|
|
143
|
-
ActiveSupport::Dependencies::Reference.clear!
|
|
144
|
-
end
|
|
145
|
-
|
|
146
133
|
def stubber
|
|
147
134
|
@stubber ||= ConstantStubber.new const_name
|
|
148
135
|
end
|
|
@@ -13,17 +13,17 @@ module WithModel
|
|
|
13
13
|
class NullTable
|
|
14
14
|
# @param [Class] superclass The resolved superclass whose table will be
|
|
15
15
|
# inherited.
|
|
16
|
-
# @param [Symbol, String] name The model's name, so that
|
|
16
|
+
# @param [Symbol, String] name The model's name, so that an error can say
|
|
17
17
|
# which model it is talking about.
|
|
18
18
|
def initialize(superclass, name)
|
|
19
19
|
@superclass = superclass
|
|
20
20
|
@name = name
|
|
21
21
|
end
|
|
22
22
|
|
|
23
|
-
# Creates nothing, but
|
|
24
|
-
# table inheritance.
|
|
23
|
+
# Creates nothing, but raises {WithModel::InvalidSuperclass} when the
|
|
24
|
+
# superclass cannot support single table inheritance.
|
|
25
25
|
#
|
|
26
|
-
#
|
|
26
|
+
# Errors describe the model's situation rather than the call that produced
|
|
27
27
|
# it. Any expression that evaluates to false selects a NullTable, so there
|
|
28
28
|
# is no call spelling to quote - and in with_model 3.0, omitting `table`
|
|
29
29
|
# will arrive here too.
|
|
@@ -31,20 +31,20 @@ module WithModel
|
|
|
31
31
|
# A superclass whose table does not exist *yet* is deliberately allowed: the
|
|
32
32
|
# table may be created later in the example, and a test may legitimately
|
|
33
33
|
# want a model whose table is missing. Active Record raises a clear
|
|
34
|
-
# `StatementInvalid` naming the table if it never appears, so
|
|
34
|
+
# `StatementInvalid` naming the table if it never appears, so raising here
|
|
35
35
|
# would only forbid working setups.
|
|
36
36
|
#
|
|
37
|
-
# What is left is {WithModel::InvalidSuperclass}: both
|
|
37
|
+
# What is left is {WithModel::InvalidSuperclass}: both errors are permanent
|
|
38
38
|
# facts about the superclass passed in, not about when it was looked at, so no
|
|
39
39
|
# amount of waiting makes them work.
|
|
40
40
|
def create
|
|
41
|
-
|
|
41
|
+
raise_invalid_superclass "#{@superclass} has none to inherit" unless table_name?
|
|
42
42
|
|
|
43
43
|
# Nothing more can be checked until there is a table to look at.
|
|
44
44
|
return unless table_exists?
|
|
45
45
|
return if inheritance_column?
|
|
46
46
|
|
|
47
|
-
|
|
47
|
+
raise_invalid_superclass "#{@superclass}'s table #{@superclass.table_name.inspect} has no " \
|
|
48
48
|
"#{inheritance_column.inspect} column, so Active Record cannot tell its rows " \
|
|
49
49
|
"apart from a subclass's"
|
|
50
50
|
end
|
|
@@ -59,7 +59,7 @@ module WithModel
|
|
|
59
59
|
# (`ActiveRecord::SubclassNotFound`).
|
|
60
60
|
#
|
|
61
61
|
# `unscoped` is required because a `default_scope` on the superclass would
|
|
62
|
-
# otherwise hide rows from
|
|
62
|
+
# otherwise hide rows from cleanup; the inheritance-column condition is
|
|
63
63
|
# then reapplied explicitly, since `unscoped` also discards the type
|
|
64
64
|
# condition that keeps this from touching the superclass's own rows.
|
|
65
65
|
#
|
|
@@ -68,7 +68,7 @@ module WithModel
|
|
|
68
68
|
def teardown(klass)
|
|
69
69
|
return unless klass.table_exists?
|
|
70
70
|
|
|
71
|
-
klass.unscoped.where(klass.inheritance_column => klass.sti_name).
|
|
71
|
+
klass.unscoped.where(klass.inheritance_column => klass.sti_name).each(&:destroy!)
|
|
72
72
|
end
|
|
73
73
|
|
|
74
74
|
# Drops nothing; there is no table of our own to drop.
|
|
@@ -90,7 +90,7 @@ module WithModel
|
|
|
90
90
|
inheritance_column.present? && @superclass.columns_hash.key?(inheritance_column)
|
|
91
91
|
end
|
|
92
92
|
|
|
93
|
-
def
|
|
93
|
+
def raise_invalid_superclass(problem)
|
|
94
94
|
raise InvalidSuperclass,
|
|
95
95
|
"with_model #{@name.inspect} has no table of its own, but #{problem}"
|
|
96
96
|
end
|
data/lib/with_model/table.rb
CHANGED
|
@@ -38,19 +38,13 @@ module WithModel
|
|
|
38
38
|
end
|
|
39
39
|
|
|
40
40
|
def destroy
|
|
41
|
-
connection.drop_table(@name)
|
|
41
|
+
connection.drop_table(@name) if exists?
|
|
42
42
|
end
|
|
43
43
|
|
|
44
44
|
private
|
|
45
45
|
|
|
46
46
|
attr_reader :connection
|
|
47
47
|
|
|
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
|
|
48
|
+
def exists? = connection.data_source_exists?(@name)
|
|
55
49
|
end
|
|
56
50
|
end
|
data/lib/with_model/version.rb
CHANGED
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 :
|
|
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
|